summaryrefslogtreecommitdiff
path: root/adig.c
diff options
context:
space:
mode:
Diffstat (limited to 'adig.c')
-rw-r--r--adig.c623
1 files changed, 623 insertions, 0 deletions
diff --git a/adig.c b/adig.c
new file mode 100644
index 0000000..db7f32d
--- /dev/null
+++ b/adig.c
@@ -0,0 +1,623 @@
+/* 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);
+}