summaryrefslogtreecommitdiff
path: root/tests/querytcp.c
diff options
context:
space:
mode:
Diffstat (limited to 'tests/querytcp.c')
-rw-r--r--tests/querytcp.c797
1 files changed, 797 insertions, 0 deletions
diff --git a/tests/querytcp.c b/tests/querytcp.c
new file mode 100644
index 0000000..7e1418f
--- /dev/null
+++ b/tests/querytcp.c
@@ -0,0 +1,797 @@
+/*
+TCP query version of queryperf
+querytcp.c
+ fujiwara@jprs.co.jp
+ 2009.08.12
+ version 0.4
+
+queryperf for tcp query
+
+This program measures DNS server performance of TCP query.
+
+o Running environment:
+ Development environment:
+ Linux
+ FreeBSD
+ MacOS X 10.3.4
+
+o How to make:
+ Linux: gcc -D_LINUX -Wall -O2 -g -lm -o querytcp querytcp.c
+ FreeBSD: gcc -Wall -O2 -g -lm -o querytcp querytcp.c
+ MacOS X: gcc -Wall -O2 -g -lm -lresolv -o querytcp querytcp.c
+
+o changes
+
+ 2010/6/7: Linux compatibility
+ 2009/8/12: Remove use of res_mkquery
+*/
+
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <netdb.h>
+#include <errno.h>
+#include <math.h>
+#include <err.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <arpa/nameser.h>
+#include <arpa/inet.h>
+#include <resolv.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <fcntl.h>
+#include <math.h>
+#ifndef NO_SYS_SELECT_H
+#include <sys/select.h>
+#endif
+
+#ifdef __APPLE__
+#include <nameser8_compat.h>
+#endif
+
+#ifndef ns_t_soa
+#define ns_t_soa T_SOA
+#endif
+#ifndef ns_t_ns
+#define ns_t_ns T_NS
+#endif
+#ifndef ns_c_in
+#define ns_c_in C_IN
+#endif
+
+#ifdef NOINET6
+#undef AF_INET6
+#endif
+
+#define Global
+
+#ifndef PACKETSZ
+#define PACKETSZ 512
+#endif
+
+/* debug.c */
+void hexdump(char *title, unsigned char *memory, int len)
+{
+ printf("[ %s ", title);
+ while (len-- > 0)
+ printf("%02x ", *memory++);
+ printf("]\n");
+}
+
+#define Xmalloc(size) Xrealloc(NULL, size)
+
+void *Xrealloc(void *p, int size)
+{
+ int sz;
+
+ sz = (size > 0) ? size : -size;
+ if (p == NULL) {
+ p = malloc(sz);
+ } else {
+ p = realloc(p, sz);
+ }
+ if (p == NULL) {
+ char buf[100];
+ snprintf(buf, sizeof buf, "size=%d", size);
+ perror(buf);
+ exit(1);
+ }
+ if (size < 0)
+ memset(p, 0, sz);
+ return p;
+}
+
+/* strlcpy() emulation for Linux. */
+#ifdef _LINUX
+static inline size_t strlcpy(char *destination, const char *source, size_t size)
+{
+ if(strncpy(destination, source, size) == NULL)
+ return 0;
+
+ return size;
+}
+#endif
+
+/*
+ NULL ... returns NULL
+ */
+char *Xstrdup(char *p)
+{
+ char *q;
+ int len;
+
+ if (p == NULL)
+ return NULL;
+ len = strlen(p) + 1;
+ q = Xmalloc(len);
+ strlcpy(q, p, len);
+ return q;
+}
+
+
+typedef int64_t timediff_t;
+
+/* packet buffer */
+static struct timeval current;
+static struct timeval start, send_finished;;
+static fd_set fdset0r, fdset0w;
+static int nfds;
+static struct sockaddr_storage remote;
+static int remote_len = 0;
+static int finished = 0;
+static timediff_t Timeout = 10*1000000LL;
+unsigned short counter = 0;
+
+#define UpdateCurrentTime gettimeofday(&current, NULL)
+
+#define RECVBUFSIZ 65537
+#define SENDBUFSIZ 512
+
+struct dnsheader {
+ unsigned short id; // 2
+ unsigned char flag1, flag2; // 2
+ unsigned short qdcount, ancount, nscount, arcount; // 8
+};
+
+/*
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ |QR| Opcode |AA|TC|RD|RA| Z | RCODE |
+ +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+*/
+
+struct queries {
+ struct tcpdns {
+ unsigned short len;
+ union {
+ struct dnsheader h;
+ unsigned char dnsdata[SENDBUFSIZ];
+ } u;
+ } send;
+ unsigned char recvbuf[RECVBUFSIZ];
+ int sendlen;
+ int sent_flag:1;
+ int tcpstate:2;
+ int fd;
+ int rpos;
+ int wpos;
+ int no;
+ struct timeval sent; /* long tv_sec, long tv_usec */
+};
+
+struct queries *Queries;
+
+#define NQUERY 100
+
+#define TCP_NONE 0
+#define TCP_WRITABLE 1
+#define TCP_READABLE 2
+
+/* input */
+char *ServerName = "127.0.0.1";
+char *ServerPort = "53";
+int family = PF_UNSPEC;
+char *datafile = NULL;
+int TimeLimit = 20;
+int EDNS0 = 0;
+int DNSSEC = 0;
+int recursion = 0;
+FILE *fp = NULL;
+int datafileloop = 0;
+int verbose = 0;
+int nQueries = 120;
+int printrcode = 0;
+char *rcodestr[]= {
+ "NOERROR", "FormatError", "ServerFailure", "NameError",
+ "NotImplemented", "Reused", "RCODE06", "RCODE07",
+ "RCODE08", "RCODE09", "RCODE10", "RCODE11",
+ "RCODE12", "RCODE13", "RCODE14", "RCODE15",
+};
+
+timediff_t timediff(struct timeval *a, struct timeval *b) /* u sec */
+{
+ return (a->tv_sec - b->tv_sec) * 1000000 + (a->tv_usec - b->tv_usec);
+}
+
+#define TIMEOUTERROR -10000
+#define ERROROFFSET -20000
+#define ERRZEROREAD -30000
+
+uint64_t countrcode[16];
+uint64_t response_size_sum = 0;
+uint64_t response_size_sum2 = 0;
+uint64_t countanswers = 0;
+uint64_t countqueries = 0;
+uint64_t countzeroread = 0;
+uint64_t counttimeout = 0;
+uint64_t counterror = 0;
+
+int response_size_min = 0;
+int response_size_max = 0;
+
+
+
+void register_response(struct queries *q, int timeout, char *note)
+{
+ u_char *p;
+ int size;
+ int rcode;
+ int id;
+
+ id = ntohs(q->send.u.h.id);
+ if (note == NULL)
+ note = "";
+ countqueries++;
+ if (timeout >= 0) {
+ p = q->recvbuf;
+ NS_GET16(size, p);
+ response_size_sum += size;
+ response_size_sum2 += size * size;
+ if (response_size_min == 0 || response_size_min > size)
+ response_size_min = size;
+ if (response_size_max == 0 || response_size_max < size)
+ response_size_max = size;
+ rcode = p[3] & 0x0f;
+ countrcode[rcode]++;
+ countanswers++;
+ if (verbose)
+ printf("recv response id=%d rcode=%d size=%d rtt=%d\n", id, rcode, size, timeout);
+ } else if (timeout == ERRZEROREAD) {
+ countzeroread++;
+ if (verbose)
+ printf("recv response id=%d zeroread\n", id);
+ } else if (timeout == TIMEOUTERROR) {
+ counttimeout++;
+ if (verbose)
+ printf("recv timeout id=%d %lld usec\n", id, timediff(&current, &q->sent));
+ } else {
+ counterror++;
+ if (verbose) {
+ printf("recv error id=%d errno=%d at %s (%s)\n", id, ERROROFFSET - timeout, note, strerror(errno));
+ }
+ }
+#ifdef DEBUG
+ printf("%ld.%03ld no=%d fd=%d %d %s\n", q->sent.tv_sec, q->sent.tv_usec/1000, q->no, q->fd, timeout, note);
+ fflush(stdout);
+#endif
+}
+
+void output()
+{
+ double response_size_average, response_size_variance, et;
+
+ et = ((double)timediff(&current, &start))/1000000.0;
+
+ printf("elapsed time: %.3f\n", et);
+ printf("tcp qps: %.3f\n", (double)countanswers/et);
+ printf("sent: %lld\n", countqueries);
+ printf("answer: %lld %3.1f%%\n", countanswers,
+ (double)((double)countanswers/(double)countqueries*100.0));
+ printf("error: %lld %3.1f%%\n", counterror,
+ (double)((double)counterror/(double)countqueries*100.0));
+ printf("zeroread: %lld %3.1f%%\n", countzeroread,
+ (double)((double)countzeroread/(double)countqueries*100.0));
+ printf("timeout: %lld %3.1f%%\n", counttimeout,
+ (double)((double)counttimeout/(double)countqueries*100.0));
+ response_size_average = (double)response_size_sum/countanswers;
+ response_size_variance = (double)response_size_sum2 / countanswers
+ - response_size_average * response_size_average;
+ printf("response size: %d/%.3f/%d/%.3f bytes\n", response_size_min, response_size_average, response_size_max, sqrt(response_size_variance));
+ if (printrcode) {
+ int i;
+ for (i = 0; i < 16; i++) {
+ if (countrcode[i] != 0) {
+ printf("%s %lld %5.1f\n", rcodestr[i], countrcode[i], ((double)countrcode[i])/((double)countanswers)*100.0);
+ }
+ }
+ }
+}
+
+void tcp_close(struct queries *q)
+{
+
+#ifdef DEBUG
+printf("tcp_close no=%d fd=%d\n", q->no, q->fd);
+#endif
+ if (q->fd >= 0) {
+ close(q->fd);
+ FD_CLR(q->fd, &fdset0r);
+ FD_CLR(q->fd, &fdset0w);
+ }
+ q->sent_flag = 0;
+ q->tcpstate = TCP_NONE;
+ q->fd = -1;
+}
+
+void tcp_send(struct queries *q)
+{
+ int len;
+
+ len = send(q->fd, &q->send, q->sendlen, MSG_NOSIGNAL);
+#ifdef DEBUG
+printf("tcp_send no=%d fd=%d %d:%d:%d\n", q->no, q->fd, len, q->wpos, q->sendlen);
+#endif
+ if (len < 0) {
+ if (errno == ENOTCONN) {
+printf("tcp_send no=%d fd=%d ENOTCONN return\n", q->no, q->fd);
+ return;
+ }
+ register_response(q, ERROROFFSET - errno, "tcp_send");
+ tcp_close(q);
+ return;
+ }
+ if (len != q->sendlen) {
+ register_response(q, ERROROFFSET - errno, "tcp_send:sendto");
+ tcp_close(q);
+ return;
+ }
+ FD_CLR(q->fd, &fdset0w);
+ FD_SET(q->fd, &fdset0r);
+}
+
+struct typecodes {
+ char *name;
+ int code;
+} typecodes[] = {
+ { "A", ns_t_a },
+ { "NS", ns_t_ns },
+ { "SOA", ns_t_soa },
+ { "PTR", ns_t_ptr },
+ { "HINFO", ns_t_hinfo },
+ { "MX", ns_t_mx },
+ { "TXT", ns_t_txt },
+ { "SIG", ns_t_sig },
+ { "KEY", ns_t_key },
+ { "AAAA", ns_t_aaaa },
+ { "NXT", ns_t_nxt },
+ { "SRV", ns_t_srv },
+ { "NAPTR", ns_t_naptr },
+ { NULL, -1 },
+};
+
+int stringtodname(unsigned char *qname, unsigned char *buff, unsigned char *lim)
+{
+ unsigned char *p, *s, *t;
+ int count, total;
+
+ t = qname;
+ p = buff;
+ total = 0;
+ for ( ;; ) {
+ s = p++;
+ count = 0;
+ if (p >= lim) return -1;
+ while (*t != 0 && *t != '.')
+ if (p < lim) {
+ *p++ = *t++;
+ count++;
+ } else
+ return -1;
+ *s = count;
+ if (count == 0)
+ break;
+ if (count > 63)
+ return -1;
+ total += count + 1;
+ if (*t == '.') t++;
+ }
+ if (total > 250 || !(*t == 0 || (*t == '.' && t[1] == 0)))
+ return -1;
+ return p - buff;
+}
+
+void send_query_error(char *mesg)
+{
+ err(1, "Packet size exceed: %s", mesg);
+}
+
+void send_query(struct queries *q)
+{
+ u_char *p, *lim;
+ char *qname;
+ int qclass;
+ int qtype;
+ int tmp;
+ struct typecodes *t = typecodes;
+ u_char buff[512];
+ static char sep[] = "\n\t ";
+ static int lineno = 0;
+
+ /*
+ SEND E[send_packet_pos]
+ */
+ if (q->sent_flag) {
+ register_response(q, TIMEOUTERROR, "send_query");
+ tcp_close(q);
+ }
+ if (fp == NULL) {
+ qname = "version.bind";
+ qclass = ns_c_chaos;
+ qtype = ns_t_txt;
+ } else {
+ do {
+ if (fgets((char*)buff, sizeof(char)*512, fp) == NULL) {
+ if (datafileloop == 1) {
+ finished = 1;
+ fclose(fp);
+ fp = NULL;
+ return;
+ }
+ if (datafileloop > 0)
+ datafileloop--;
+ rewind(fp);
+ lineno = 0;
+ if (fgets((char*)buff, sizeof(char)*512, fp) == NULL)
+ err(1, "cannot rewind input file");
+ }
+ lineno++;
+ } while(buff[0] == '#');
+ qname = strtok((char*)buff, sep);
+ p = (u_char*) strtok(NULL, sep);
+ if (p != NULL) {
+ while(t->name != NULL) {
+ if (!strcasecmp(t->name, (char*)p))
+ break;
+ t++;
+ }
+ qtype = t->code;
+ } else {
+ qtype = ns_t_a;
+ }
+ if (qname == NULL || qtype < 0)
+ err(1, "datafile format error at line %d, qname=%s qtype=%d", lineno, qname, qtype);
+ qclass = ns_c_in;
+ }
+ q->send.u.h.id = counter++;
+ q->send.u.h.flag1 = recursion ? 1 : 0; /* Query,OP=0,AA=0,TC=0,RD=0/1 */
+ q->send.u.h.flag2 = 0;
+ q->send.u.h.qdcount = htons(1);
+ q->send.u.h.ancount = 0;
+ q->send.u.h.nscount = 0;
+ q->send.u.h.arcount = 0;
+ p = q->send.u.dnsdata + sizeof(q->send.u.h);
+ lim = p + sizeof(q->send.u.dnsdata);
+ if ((tmp = stringtodname((u_char*) qname, p, lim)) < 0)
+ send_query_error(qname);
+ p += tmp;
+ *(unsigned short *)p = htons(qtype);
+ p += sizeof(unsigned short);
+ *(unsigned short *)p = htons(qclass);
+ p += sizeof(unsigned short);
+ q->sendlen = p - q->send.u.dnsdata;
+ if (EDNS0) {
+#define EDNS0size 11
+ if (q->sendlen + EDNS0size >= sizeof(q->send.u.dnsdata))
+ send_query_error("ENDS0");
+ *p++ = 0; /* . */
+ *(unsigned short *)p = htons(ns_t_opt);
+ p += 2;
+ *(unsigned short *)p = htons(4096);
+ p += 2;
+ *p++ = 0;
+ *p++ = 0;
+ *p++ = (DNSSEC == 0) ? 0 : 0x80; /* eflag: DO bit */
+ *p++ = 0;
+ *p++ = 0;
+ *p++ = 0;
+ q->sendlen += EDNS0size;
+ p = (u_char*) &q->send.u.dnsdata;
+ q->send.u.h.ancount = htons(1);
+ }
+ q->send.len = htons(q->sendlen);
+ q->sendlen += sizeof(q->send.len);
+ q->wpos = 0;
+ q->rpos = 0;
+ q->sent = current;
+ if (verbose > 0) {
+ int id = ntohs(*(unsigned short *)&q->send.u.dnsdata);
+ printf("sending query(%s,%d,%d) id=%d %d bytes to %s\n", qname, qclass, qtype, id, q->sendlen, ServerName);
+ hexdump("sending packet header:", (unsigned char*) &q->send.u.h, 12);
+ }
+ if (q->fd > 0)
+ err(1, "q->fd > 0 but ignored\n");
+
+ q->fd = socket(remote.ss_family, SOCK_STREAM, 0);
+ tmp = fcntl(q->fd, F_GETFL, 0);
+ fcntl(q->fd, F_SETFL, O_NONBLOCK | tmp);
+ int conn_ret = connect(q->fd, (struct sockaddr *)&remote, remote_len);
+ if(conn_ret < 0 && errno != EINPROGRESS) {
+ register_response(q, ERROROFFSET - errno, "send_query:socket+fcntl+connect");
+ tcp_close(q);
+ return;
+ }
+#ifdef DEBUG
+printf("send_query no=%d fd=%d socket|connect\n", q->no, q->fd);
+#endif
+ q->tcpstate = TCP_WRITABLE;
+ FD_SET(q->fd, &fdset0w);
+ FD_CLR(q->fd, &fdset0r);
+ if (nfds <= q->fd) {
+ nfds = q->fd + 1;
+ }
+ q->sent = current;
+ q->sent_flag = 1;
+}
+
+int UpdateQuery()
+{
+ int i;
+ timediff_t t, min = Timeout;
+ struct queries *q;
+ int free = 0;
+
+ if (!finished && TimeLimit > 0) {
+ if ((t = timediff(&current, &start)) > TimeLimit * 1000000LL) {
+ finished = 1;
+ send_finished = current;
+ }
+ }
+ for(i = 0; i < nQueries; i++) {
+ q = &Queries[i];
+ if (q->sent_flag) {
+ if ((t = timediff(&current, &q->sent)) > Timeout) {
+ /* timeouted */
+ register_response(q, TIMEOUTERROR, "UpdateQuery");
+ tcp_close(q);
+ } else
+ if (t < min)
+ min = t;
+ }
+ if (!q->sent_flag) {
+ if (!finished)
+ send_query(q);
+ else
+ free++;
+ }
+ }
+ if (free == nQueries)
+ min = -1; /* finished */
+ return min;
+}
+
+char *skipname(char *p)
+{
+ while(*p > 0 && *p < 0x40) p += *p + 1;
+ if (*p == 0)
+ return p+1;
+ return p+2;
+}
+
+#define Hexdump(A,B,C)
+
+void tcp_receive(struct queries *q)
+{
+ int len, len2;
+ timediff_t tmp;
+ unsigned char *recvp;
+
+/*printf("tcp_receive %s\n", q->nameserverlabel);*/
+
+ len = read(q->fd, q->recvbuf + q->rpos, len2 = RECVBUFSIZ - q->rpos);
+ if (len < 0) {
+ if (errno == EAGAIN)
+ return;
+ register_response(q, ERROROFFSET - errno, "tcp_receive:read");
+ tcp_close(q);
+ return;
+ }
+ if (len == 0) {
+ register_response(q, ERRZEROREAD, "tcp_receive:read");
+ tcp_close(q);
+ return;
+ }
+ q->rpos += len;
+ if (q->rpos < 2)
+ return;
+ len2 = ntohs(*(unsigned short *)(q->recvbuf));
+ if (q->rpos >= len2 + 2) {
+ /* finished */
+ recvp = q->recvbuf + 2;
+ if (memcmp(recvp, q->send.u.dnsdata, 2) == 0) {
+ if ((recvp[2] & 1) == 0 /* RA bit */
+ || (recvp[3] & 15) != 0 /* RCODE must be 0 */
+ ) {
+/*
+ fprintf(stderr, "WRONG AA=%d RCODE=%d\n",
+ ((recvp[2]>>2) & 1), recvp[3]&15);
+*/
+ }
+ tmp = timediff(&current, &q->sent);
+ register_response(q, tmp, "tcp_receive");
+ tcp_close(q);
+ return;
+ } else {
+printf("no=%d fd=%d unknown recv %d bytes, len=%d\n", q->no, q->fd, q->rpos, ntohs(*(unsigned short *)(q->recvbuf)));
+ hexdump("", q->recvbuf, len);
+ /*
+ fprintf(stderr, "unknown recv from %s, %d bytes %02x %02x\n", q->nameserverlabel, q->rpos, recvp[0], recvp[1]);
+ */
+ tcp_close(q);
+ }
+ }
+}
+
+void query()
+{
+ fd_set fdsetr, fdsetw;
+ struct timeval timeout;
+ int min;
+ struct queries *q;
+ int i, n;
+ struct addrinfo hints, *res0;
+ int error;
+
+ Queries = Xmalloc(sizeof(Queries[0]) * nQueries);
+ memset(&remote, 0, sizeof(remote));
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = family;
+ hints.ai_socktype = SOCK_STREAM;
+ printf("resolving: %s:%s\n", ServerName, ServerPort);
+ error = getaddrinfo(ServerName, 0, &hints, &res0);
+ if (error) {
+ errx(1, "%s", gai_strerror(error));
+ }
+
+ /* Update server port. */
+ int port = atoi(ServerPort);
+ if (res0->ai_family == AF_INET6) {
+ struct sockaddr_in6 *ipv6 = (struct sockaddr_in6*)res0->ai_addr;
+ ipv6->sin6_port = htons(port);
+ } else {
+ struct sockaddr_in *ipv4 = (struct sockaddr_in*)res0->ai_addr;
+ ipv4->sin_port = htons(port);
+ }
+
+ remote_len = res0->ai_addrlen;
+ memcpy(&remote, res0->ai_addr, res0->ai_addrlen);
+ memset(&countrcode, 0, sizeof(countrcode));
+
+ res_init();
+ _res.options ^= ~RES_RECURSE;
+ _res.options |= RES_AAONLY;
+
+ for (i = 0; i < nQueries; i++) {
+ Queries[i].sent_flag = 0;
+ Queries[i].no = i;
+ }
+
+ FD_ZERO(&fdset0r);
+ FD_ZERO(&fdset0w);
+ nfds = 0;
+ UpdateCurrentTime;
+ start = current;
+ finished = 0;
+
+ for (;;) {
+ UpdateCurrentTime;
+ if ((min = UpdateQuery()) < 0)
+ break;
+ timeout.tv_sec = min / 1000000;
+ timeout.tv_usec = min % 1000000;
+ fdsetr = fdset0r;
+ fdsetw = fdset0w;
+ n = select(nfds, &fdsetr, &fdsetw, NULL, &timeout);
+ UpdateCurrentTime;
+ for(i = 0; i < nQueries; i++) {
+ q = &Queries[i];
+ if (q->fd < 0 || !q->sent_flag)
+ continue;
+ if (FD_ISSET(q->fd, &fdsetw)) {
+ tcp_send(q);
+ } else if (FD_ISSET(q->fd, &fdsetr)) {
+ tcp_receive(q);
+ }
+ }
+ }
+}
+
+void usage()
+{
+ fprintf(stderr,
+"querytcp [-d datafile] [-s server_addr] [-p port] [-q num_queries] [-t timeout] [l limit] [-4] [-6] [-h]\n"
+" -d specifies the input data file (default: stdin)\n"
+" -s sets the server to query (default: 127.0.0.1)\n"
+" -p sets the port on which to query the server (default: 53)\n"
+" -q specifies the maximum number of queries outstanding (default: 120)\n"
+" -t specifies the timeout for query completion in seconds (default: 10)\n"
+" -l specifies how a limit for how long to run tests in seconds (no default)\n"
+" -e enable EDNS0\n"
+" -D set DO bit\n"
+" -r set RD bit\n"
+"\n"
+"\n"
+"\n"
+" -c print the number of packets with each rcode\n"
+" -v verbose: report the RCODE of each response on stdout\n"
+" -h print this usage\n"
+);
+ exit(1);
+}
+
+int main(int argc, char *argv[])
+{
+ int ch, i;
+ printf("dnsheader size: %d\n", sizeof(struct dnsheader));
+ while ((ch = getopt(argc, argv, "d:s:p:q:t:l:46eDrvh")) != -1) {
+ switch (ch) {
+ case 'q':
+ nQueries = atoi(optarg);
+ if (nQueries < 1)
+ err(1, "-q requires natural number");
+ break;
+ case 'p':
+ ServerPort = Xstrdup(optarg);
+ break;
+ case 's':
+ ServerName = Xstrdup(optarg);
+ break;
+ case 'd':
+ datafile = Xstrdup(optarg);
+ if ((fp = fopen(datafile, "r")) == NULL)
+ err(1, "cannot open %s", optarg);
+ break;
+ case 't':
+ i = atoi(optarg);
+ if (i < 1)
+ err(1, "-t timeout > 0");
+ Timeout = (int64_t)i * 1000000LL;
+ break;
+ case 'l':
+ TimeLimit = atoi(optarg);
+ break;
+ case '4':
+ family = AF_INET;
+ break;
+ case '6':
+ family = AF_INET6;
+ break;
+ case 'e':
+ EDNS0 = 1;
+ break;
+ case 'D':
+ DNSSEC = 1;
+ break;
+ case 'r':
+ recursion = 1;
+ break;
+ case 'v':
+ verbose = 1;
+ break;
+ case 'c':
+ printrcode = 1;
+ break;
+ case 'h':
+ default:
+ usage();
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ query();
+ output();
+
+ return 0;
+}