1/*	$NetBSD$	*/
2
3/*
4 * Copyright (C) 2009  Internet Systems Consortium, Inc. ("ISC")
5 *
6 * Permission to use, copy, modify, and/or distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
11 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
12 * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
13 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
14 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
15 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
16 * PERFORMANCE OF THIS SOFTWARE.
17 */
18
19#ifndef lint
20static const char rcsid[] = "Id: dig8.c,v 1.4 2009/03/03 23:49:07 tbox Exp ";
21#endif
22
23/*
24 * Copyright (c) 1989
25 *    The Regents of the University of California.  All rights reserved.
26 *
27 * Redistribution and use in source and binary forms, with or without
28 * modification, are permitted provided that the following conditions
29 * are met:
30 * 1. Redistributions of source code must retain the above copyright
31 *    notice, this list of conditions and the following disclaimer.
32 * 2. Redistributions in binary form must reproduce the above copyright
33 *    notice, this list of conditions and the following disclaimer in the
34 *    documentation and/or other materials provided with the distribution.
35 * 3. All advertising materials mentioning features or use of this software
36 *    must display the following acknowledgement:
37 * 	This product includes software developed by the University of
38 * 	California, Berkeley and its contributors.
39 * 4. Neither the name of the University nor the names of its contributors
40 *    may be used to endorse or promote products derived from this software
41 *    without specific prior written permission.
42 *
43 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
44 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
45 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
46 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
47 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
48 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
49 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
50 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
51 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
52 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
53 * SUCH DAMAGE.
54 */
55
56/*
57 * Portions Copyright (c) 1993 by Digital Equipment Corporation.
58 *
59 * Permission to use, copy, modify, and distribute this software for any
60 * purpose with or without fee is hereby granted, provided that the above
61 * copyright notice and this permission notice appear in all copies, and that
62 * the name of Digital Equipment Corporation not be used in advertising or
63 * publicity pertaining to distribution of the document or software without
64 * specific, written prior permission.
65 *
66 * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL
67 * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES
68 * OF MERCHANTABILITY AND FITNESS.   IN NO EVENT SHALL DIGITAL EQUIPMENT
69 * CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
70 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
71 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
72 * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
73 * SOFTWARE.
74 */
75
76/*
77 * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
78 * Portions Copyright (c) 1996-1999 by Internet Software Consortium
79 *
80 * Permission to use, copy, modify, and distribute this software for any
81 * purpose with or without fee is hereby granted, provided that the above
82 * copyright notice and this permission notice appear in all copies.
83 *
84 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
85 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
86 * MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR
87 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
88 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
89 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
90 * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
91 */
92
93/*********************** Notes for the BIND 4.9 release (Paul Vixie, DEC)
94 *	dig 2.0 was written by copying sections of libresolv.a and nslookup
95 *	and modifying them to be more useful for a general lookup utility.
96 *	as of BIND 4.9, the changes needed to support dig have mostly been
97 *	incorporated into libresolv.a and nslookup; dig now links against
98 *	some of nslookup's .o files rather than #including them or maintaining
99 *	local copies of them.
100 *
101 *	while merging dig back into the BIND release, i made a number of
102 *	structural changes.  for one thing, i put all of dig's private
103 *	library routines into this file rather than maintaining them in
104 *	separate, #included, files.  i don't like to #include ".c" files.
105 *	i removed all calls to "bcopy", replacing them with structure
106 *	assignments.  i removed all "extern"'s of standard functions,
107 *	replacing them with #include's of standard header files.  this
108 *	version of dig is probably as portable as the rest of BIND.
109 *
110 *	i had to remove the query-time and packet-count statistics since
111 *	the current libresolv.a is a lot harder to modify to maintain these
112 *	than the 4.8 one (used in the original dig) was.  for consolation,
113 *	i added a "usage" message with extensive help text.
114 *
115 *	to save my (limited, albeit) sanity, i ran "indent" over the source.
116 *	i also added the standard berkeley/DEC copyrights, since this file now
117 *	contains a fair amount of non-USC code.  note that the berkeley and
118 *	DEC copyrights do not prohibit redistribution, with or without fee;
119 *	we add them only to protect ourselves (you have to claim copyright
120 *	in order to disclaim liability and warranty).
121 *
122 *	Paul Vixie, Palo Alto, CA, April 1993
123 ****************************************************************************
124
125 ******************************************************************
126 *      DiG -- Domain Information Groper                          *
127 *                                                                *
128 *        dig.c - Version 2.1 (7/12/94) ("BIND takeover")         *
129 *                                                                *
130 *        Developed by: Steve Hotz & Paul Mockapetris             *
131 *        USC Information Sciences Institute (USC-ISI)            *
132 *        Marina del Rey, California                              *
133 *        1989                                                    *
134 *                                                                *
135 *        dig.c -                                                 *
136 *           Version 2.0 (9/1/90)                                 *
137 *               o renamed difftime() difftv() to avoid           *
138 *                 clash with ANSI C                              *
139 *               o fixed incorrect # args to strcmp,gettimeofday  *
140 *               o incorrect length specified to strncmp          *
141 *               o fixed broken -sticky -envsa -envset functions  *
142 *               o print options/flags redefined & modified       *
143 *                                                                *
144 *           Version 2.0.beta (5/9/90)                            *
145 *               o output format - helpful to `doc`               *
146 *               o minor cleanup                                  *
147 *               o release to beta testers                        *
148 *                                                                *
149 *           Version 1.1.beta (10/26/89)                          *
150 *               o hanging zone transer (when REFUSED) fixed      *
151 *               o trailing dot added to domain names in RDATA    *
152 *               o ISI internal                                   *
153 *                                                                *
154 *           Version 1.0.tmp  (8/27/89)                           *
155 *               o Error in prnttime() fixed                      *
156 *               o no longer dumps core on large pkts             *
157 *               o zone transfer (axfr) added                     *
158 *               o -x added for inverse queries                   *
159 *                               (i.e. "dig -x 128.9.0.32")       *
160 *               o give address of default server                 *
161 *               o accept broadcast to server @255.255.255.255    *
162 *                                                                *
163 *           Version 1.0  (3/27/89)                               *
164 *               o original release                               *
165 *                                                                *
166 *     DiG is Public Domain, and may be used for any purpose as   *
167 *     long as this notice is not removed.                        *
168 ******************************************************************/
169
170/* Import. */
171
172#include "port_before.h"
173
174#include <sys/types.h>
175#include <sys/param.h>
176#include <sys/file.h>
177#include <sys/stat.h>
178#include <sys/socket.h>
179#include <sys/time.h>
180#include <sys/wait.h>
181
182#include <netinet/in.h>
183#include <arpa/inet.h>
184#include <arpa/nameser.h>
185
186#include <isc/dst.h>
187
188#include <assert.h>
189#include <ctype.h>
190#include <errno.h>
191#include <fcntl.h>
192#include <netdb.h>
193#include <setjmp.h>
194#include <stdio.h>
195#include <stdlib.h>
196#include <string.h>
197#include <unistd.h>
198#include <time.h>	/* time(2), ctime(3) */
199
200#include "port_after.h"
201
202#include <resolv.h>
203
204#include "res.h"
205
206/* Global. */
207
208#define VERSION 84
209#define VSTRING "8.4"
210
211#define PRF_DEF		(RES_PRF_STATS | RES_PRF_CMD | RES_PRF_QUES | \
212			 RES_PRF_ANS | RES_PRF_AUTH | RES_PRF_ADD | \
213			 RES_PRF_HEAD1 | RES_PRF_HEAD2 | RES_PRF_TTLID | \
214			 RES_PRF_HEADX | RES_PRF_REPLY | RES_PRF_TRUNC)
215#define PRF_MIN		(RES_PRF_QUES | RES_PRF_ANS | RES_PRF_HEAD1 | \
216			 RES_PRF_HEADX | RES_PRF_REPLY | RES_PRF_TRUNC)
217#define PRF_ZONE        (RES_PRF_STATS | RES_PRF_CMD | RES_PRF_QUES | \
218			 RES_PRF_ANS | RES_PRF_AUTH | RES_PRF_ADD | \
219			 RES_PRF_TTLID | RES_PRF_REPLY | RES_PRF_TRUNC)
220
221#ifndef MAXHOSTNAMELEN
222#define MAXHOSTNAMELEN 256
223#endif
224
225#define SAVEENV "DiG.env"
226#define DIG_MAXARGS 30
227
228#ifndef DIG_PING
229#define DIG_PING "ping"
230#endif
231#ifndef DIG_TAIL
232#define DIG_TAIL "tail"
233#endif
234#ifndef DIG_PINGFMT
235#define DIG_PINGFMT "%s -s %s 56 3 | %s -3"
236#endif
237
238static int		eecode = 0;
239static FILE *		qfp;
240static char		myhostname[MAXHOSTNAMELEN];
241static struct sockaddr_in myaddress;
242static struct sockaddr_in6 myaddress6;
243static u_int32_t	ixfr_serial;
244static char		ubuf[sizeof("ffff:ffff:ffff:ffff:ffff:ffff:123.123.123.123")];
245
246/* stuff for nslookup modules */
247struct __res_state  res;
248FILE		*filePtr;
249jmp_buf		env;
250HostInfo	*defaultPtr = NULL;
251HostInfo	curHostInfo, defaultRec;
252int		curHostValid = FALSE;
253int		queryType, queryClass;
254extern int	StringToClass(), StringToType();	/* subr.c */
255#if defined(BSD) && BSD >= 199006 && !defined(RISCOS_BSD)
256FILE		*yyin = NULL;
257void		yyrestart(FILE *f);
258void		yyrestart(FILE *f) { UNUSED(f); }
259#endif
260char		*pager = NULL;
261/* end of nslookup stuff */
262
263/* Forward. */
264
265static void		Usage(void);
266static int		setopt(const char *);
267static void		res_re_init(void);
268static int		xstrtonum(char *);
269static int		printZone(ns_type, const char *,
270				  const struct sockaddr_in *, ns_tsig_key *);
271static int		print_axfr(FILE *output, const u_char *msg,
272				   size_t msglen);
273static struct timeval	difftv(struct timeval, struct timeval);
274static void		prnttime(struct timeval);
275static void		stackarg(char *, char **);
276static void		reverse6(char *, struct in6_addr *);
277
278/* Public. */
279
280int
281main(int argc, char **argv) {
282	short port = htons(NAMESERVER_PORT);
283	short lport;
284	/* Wierd stuff for SPARC alignment, hurts nothing else. */
285	union {
286		HEADER header_;
287		u_char packet_[PACKETSZ];
288	} packet_;
289#define header (packet_.header_)
290#define	packet (packet_.packet_)
291	union {
292		HEADER u;
293		u_char b[NS_MAXMSG];
294	} answer;
295	int n;
296	char doping[90];
297	char pingstr[50];
298	char *afile;
299	char *addrc, *addrend, *addrbegin;
300
301	time_t exectime;
302	struct timeval tv1, tv2, start_time, end_time, query_time;
303
304	char *srv;
305	int anyflag = 0;
306	int sticky = 0;
307	int tmp;
308	int qtypeSet;
309	ns_type xfr = ns_t_invalid;
310        int bytes_out, bytes_in;
311
312	char cmd[512];
313	char domain[MAXDNAME];
314        char msg[120], **vtmp;
315	char *args[DIG_MAXARGS];
316	char **ax;
317	int once = 1, dofile = 0; /* batch -vs- interactive control */
318	char fileq[384];
319	int  fp;
320	int wait=0, delay;
321	int envset=0, envsave=0;
322	struct __res_state res_x, res_t;
323	int r;
324	struct in6_addr in6;
325
326	ns_tsig_key key;
327	char *keyfile = NULL, *keyname = NULL;
328	const char *pingfmt = NULL;
329
330	UNUSED(argc);
331
332	res_ninit(&res);
333	res.pfcode = PRF_DEF;
334	qtypeSet = 0;
335	memset(domain, 0, sizeof domain);
336	gethostname(myhostname, (sizeof myhostname));
337#ifdef HAVE_SA_LEN
338	myaddress.sin_len = sizeof(struct sockaddr_in);
339#endif
340	myaddress.sin_family = AF_INET;
341	myaddress.sin_addr.s_addr = INADDR_ANY;
342	myaddress.sin_port = 0; /*INPORT_ANY*/;
343
344#ifdef HAVE_SA_LEN
345	myaddress6.sin6_len = sizeof(struct sockaddr_in6);
346#endif
347	myaddress6.sin6_family = AF_INET6;
348	myaddress6.sin6_addr = in6addr_any;
349	myaddress6.sin6_port = 0; /*INPORT_ANY*/;
350
351	res_x = res;
352
353/*
354 * If LOCALDEF in environment, should point to file
355 * containing local favourite defaults.  Also look for file
356 * DiG.env (i.e. SAVEENV) in local directory.
357 */
358
359	if ((((afile = (char *) getenv("LOCALDEF")) != (char *) NULL) &&
360	     ((fp = open(afile, O_RDONLY)) > 0)) ||
361	    ((fp = open(SAVEENV, O_RDONLY)) > 0)) {
362		read(fp, (char *)&res_x, (sizeof res_x));
363		close(fp);
364		res = res_x;
365	}
366/*
367 * Check for batch-mode DiG; also pre-scan for 'help'.
368 */
369	vtmp = argv;
370	ax = args;
371	while (*vtmp != NULL) {
372		if (strcmp(*vtmp, "-h") == 0 ||
373		    strcmp(*vtmp, "-help") == 0 ||
374		    strcmp(*vtmp, "-usage") == 0 ||
375		    strcmp(*vtmp, "help") == 0) {
376			Usage();
377			exit(0);
378		}
379
380		if (strcmp(*vtmp, "-f") == 0) {
381			dofile++; once=0;
382			if ((qfp = fopen(*++vtmp, "r")) == NULL) {
383				fflush(stdout);
384				perror("file open");
385				fflush(stderr);
386				exit(10);
387			}
388		} else {
389			if (ax - args == DIG_MAXARGS) {
390				fprintf(stderr, "dig: too many arguments\n");
391				exit(10);
392			}
393			*ax++ = *vtmp;
394		}
395		vtmp++;
396	}
397
398	gettimeofday(&tv1, NULL);
399
400/*
401 * Main section: once if cmd-line query
402 *               while !EOF if batch mode
403 */
404	*fileq = '\0';
405	while ((dofile && fgets(fileq, sizeof fileq, qfp) != NULL) ||
406	       (!dofile && once--))
407	{
408		if (*fileq == '\n' || *fileq == '#' || *fileq==';') {
409			printf("%s", fileq);	/* echo but otherwise ignore */
410			continue;		/* blank lines and comments  */
411		}
412
413/*
414 * "Sticky" requests that before current parsing args
415 * return to current "working" environment (X******).
416 */
417		if (sticky) {
418			printf(";; (using sticky settings)\n");
419			res = res_x;
420		}
421
422/*
423 * Concat cmd-line and file args.
424 */
425		stackarg(fileq, ax);
426
427		/* defaults */
428		queryType = ns_t_ns;
429		queryClass = ns_c_in;
430		xfr = ns_t_invalid;
431		*pingstr = 0;
432		srv = NULL;
433
434		sprintf(cmd, "\n; <<>> DiG %s (libbind %d) <<>> ",
435			VSTRING, __RES);
436		argv = args;
437		/* argc = ax - args; */
438/*
439 * More cmd-line options than anyone should ever have to
440 * deal with ....
441 */
442		while (*(++argv) != NULL && **argv != '\0') {
443			if (strlen(cmd) + strlen(*argv) + 2 > sizeof (cmd)) {
444				fprintf(stderr,
445				   "Argument too large for input buffer\n");
446				exit(1);
447			}
448			strcat(cmd, *argv);
449			strcat(cmd, " ");
450			if (**argv == '@') {
451				srv = (*argv+1);
452				continue;
453			}
454			if (**argv == '%')
455				continue;
456			if (**argv == '+') {
457				setopt(*argv+1);
458				continue;
459			}
460			if (**argv == '=') {
461				ixfr_serial = strtoul(*argv+1, NULL, 0);
462				continue;
463			}
464			if (strncmp(*argv, "-nost", 5) == 0) {
465				sticky = 0;
466				continue;
467			} else if (strncmp(*argv, "-st", 3) == 0) {
468				sticky++;
469				continue;
470			} else if (strncmp(*argv, "-envsa", 6) == 0) {
471				envsave++;
472				continue;
473			} else if (strncmp(*argv, "-envse", 6) == 0) {
474				envset++;
475				continue;
476			}
477
478			if (**argv == '-') {
479				switch (argv[0][1]) {
480				case 'T':
481					if (*++argv == NULL)
482						printf("; no arg for -T?\n");
483					else
484						wait = atoi(*argv);
485					break;
486				case 'c':
487					if(*++argv == NULL)
488						printf("; no arg for -c?\n");
489					else if ((tmp = atoi(*argv))
490						  || *argv[0] == '0') {
491						queryClass = tmp;
492					} else if ((tmp = StringToClass(*argv,
493								       0, NULL)
494						   ) != 0) {
495						queryClass = tmp;
496					} else {
497						printf(
498						  "; invalid class specified\n"
499						       );
500					}
501					break;
502				case 't':
503					if (*++argv == NULL)
504						printf("; no arg for -t?\n");
505					else if ((tmp = atoi(*argv))
506					    || *argv[0]=='0') {
507						if (ns_t_xfr_p(tmp)) {
508							xfr = tmp;
509						} else {
510							queryType = tmp;
511							qtypeSet++;
512						}
513					} else if ((tmp = StringToType(*argv,
514								      0, NULL)
515						   ) != 0) {
516						if (ns_t_xfr_p(tmp)) {
517							xfr = tmp;
518						} else {
519							queryType = tmp;
520							qtypeSet++;
521						}
522					} else {
523						printf(
524						   "; invalid type specified\n"
525						       );
526					}
527					break;
528				case 'x':
529					if (!qtypeSet) {
530						queryType = T_ANY;
531						qtypeSet++;
532					}
533					if ((addrc = *++argv) == NULL) {
534						printf("; no arg for -x?\n");
535						break;
536					}
537					r = inet_pton(AF_INET6, addrc, &in6);
538					if (r > 0) {
539						reverse6(domain, &in6);
540						break;
541					}
542					addrend = addrc + strlen(addrc);
543					if (*addrend == '.')
544						*addrend = '\0';
545					*domain = '\0';
546					while ((addrbegin = strrchr(addrc,'.'))) {
547						strcat(domain, addrbegin+1);
548						strcat(domain, ".");
549						*addrbegin = '\0';
550					}
551					strcat(domain, addrc);
552					strcat(domain, ".in-addr.arpa.");
553					break;
554				case 'p':
555					if (argv[0][2] != '\0')
556						port = htons(atoi(argv[0]+2));
557					else if (*++argv == NULL)
558						printf("; no arg for -p?\n");
559					else
560						port = htons(atoi(*argv));
561					break;
562				case 'P':
563					if (argv[0][2] != '\0') {
564						strcpy(pingstr, argv[0]+2);
565						pingfmt =
566							"%s %s 56 3 | %s -3";
567					} else {
568						strcpy(pingstr, DIG_PING);
569						pingfmt = DIG_PINGFMT;
570					}
571					break;
572				case 'n':
573					if (argv[0][2] != '\0')
574						res.ndots = atoi(argv[0]+2);
575					else if (*++argv == NULL)
576						printf("; no arg for -n?\n");
577					else
578						res.ndots = atoi(*argv);
579					break;
580				case 'b': {
581					char *a, *p;
582
583					if (argv[0][2] != '\0')
584						a = argv[0]+2;
585					else if (*++argv == NULL) {
586						printf("; no arg for -b?\n");
587						break;
588					} else
589						a = *argv;
590					if ((p = strchr(a, ':')) != NULL) {
591						*p++ = '\0';
592						lport = htons(atoi(p));
593					} else
594						lport = htons(0);
595					if (inet_pton(AF_INET6, a,
596					      &myaddress6.sin6_addr) == 1) {
597					      myaddress6.sin6_port = lport;
598					} else if (!inet_aton(a,
599						   &myaddress.sin_addr)) {
600						fprintf(stderr,
601							";; bad -b addr\n");
602						exit(1);
603					} else
604						myaddress.sin_port = lport;
605				    }
606				    break;
607				case 'k':
608					/* -k keydir:keyname */
609
610					if (argv[0][2] != '\0')
611						keyfile = argv[0]+2;
612					else if (*++argv == NULL) {
613						printf("; no arg for -k?\n");
614						break;
615					} else
616						keyfile = *argv;
617
618					keyname = strchr(keyfile, ':');
619					if (keyname == NULL) {
620						fprintf(stderr,
621			     "key option argument should be keydir:keyname\n");
622						exit(1);
623					}
624					*keyname++='\0';
625					break;
626				} /* switch - */
627				continue;
628			} /* if '-'   */
629
630			if ((tmp = StringToType(*argv, -1, NULL)) != -1) {
631				if ((T_ANY == tmp) && anyflag++) {
632					queryClass = C_ANY;
633					continue;
634				}
635				if (ns_t_xfr_p(tmp) &&
636				    (tmp == ns_t_axfr ||
637				     (res.options & RES_USEVC) != 0)
638				     ) {
639					res.pfcode = PRF_ZONE;
640					xfr = (ns_type)tmp;
641				} else {
642					queryType = tmp;
643					qtypeSet++;
644				}
645			} else if ((tmp = StringToClass(*argv, -1, NULL))
646				   != -1) {
647				queryClass = tmp;
648			} else {
649				memset(domain, 0, sizeof domain);
650				sprintf(domain,"%s",*argv);
651			}
652		} /* while argv remains */
653
654		/* process key options */
655		if (keyfile) {
656#ifdef PARSE_KEYFILE
657			int i, n1;
658			char buf[BUFSIZ], *p;
659			FILE *fp = NULL;
660			int file_major, file_minor, alg;
661
662			fp = fopen(keyfile, "r");
663			if (fp == NULL) {
664				perror(keyfile);
665				exit(1);
666			}
667			/* Now read the header info from the file. */
668			i = fread(buf, 1, BUFSIZ, fp);
669			if (i < 5) {
670				fclose(fp);
671	                	exit(1);
672	        	}
673			fclose(fp);
674
675			p = buf;
676
677			n=strlen(p);		/* get length of strings */
678			n1=strlen("Private-key-format: v");
679			if (n1 > n ||
680			    strncmp(buf, "Private-key-format: v", n1)) {
681				fprintf(stderr, "Invalid key file format\n");
682				exit(1);	/* not a match */
683			}
684			p+=n1;		/* advance pointer */
685			sscanf((char *)p, "%d.%d", &file_major, &file_minor);
686			/* should do some error checking with these someday */
687			while (*p++!='\n');	/* skip to end of line */
688
689	        	n=strlen(p);		/* get length of strings */
690	        	n1=strlen("Algorithm: ");
691	        	if (n1 > n || strncmp(p, "Algorithm: ", n1)) {
692				fprintf(stderr, "Invalid key file format\n");
693	                	exit(1);	/* not a match */
694			}
695			p+=n1;		/* advance pointer */
696			if (sscanf((char *)p, "%d", &alg)!=1) {
697				fprintf(stderr, "Invalid key file format\n");
698				exit(1);
699			}
700			while (*p++!='\n');	/* skip to end of line */
701
702	        	n=strlen(p);		/* get length of strings */
703	        	n1=strlen("Key: ");
704	        	if (n1 > n || strncmp(p, "Key: ", n1)) {
705				fprintf(stderr, "Invalid key file format\n");
706				exit(1);	/* not a match */
707			}
708			p+=n1;		/* advance pointer */
709			pp=p;
710			while (*pp++!='\n');	/* skip to end of line,
711						 * terminate it */
712			*--pp='\0';
713
714			key.data=malloc(1024*sizeof(char));
715			key.len=b64_pton(p, key.data, 1024);
716
717			strcpy(key.name, keyname);
718			strcpy(key.alg, "HMAC-MD5.SIG-ALG.REG.INT");
719#else
720			/* use the dst* routines to parse the key files
721			 *
722			 * This requires that both the .key and the .private
723			 * files exist in your cwd, so the keyfile parmeter
724			 * here is assumed to be a path in which the
725			 * K*.{key,private} files exist.
726			 */
727			DST_KEY *dst_key;
728			char cwd[PATH_MAX+1];
729
730			if (getcwd(cwd, PATH_MAX)==NULL) {
731				perror("unable to get current directory");
732				exit(1);
733			}
734			if (chdir(keyfile)<0) {
735				fprintf(stderr,
736					"unable to chdir to %s: %s\n", keyfile,
737					strerror(errno));
738				exit(1);
739			}
740
741			dst_init();
742			dst_key = dst_read_key(keyname,
743					       0 /* not used for priv keys */,
744					       KEY_HMAC_MD5, DST_PRIVATE);
745			if (!dst_key) {
746				fprintf(stderr,
747					"dst_read_key: error reading key\n");
748				exit(1);
749			}
750			key.data=malloc(1024*sizeof(char));
751			dst_key_to_buffer(dst_key, key.data, 1024);
752			key.len=dst_key->dk_key_size;
753
754			strcpy(key.name, keyname);
755			strcpy(key.alg, "HMAC-MD5.SIG-ALG.REG.INT");
756
757			if (chdir(cwd)<0) {
758				fprintf(stderr, "unable to chdir to %s: %s\n",
759					cwd, strerror(errno));
760				exit(1);
761			}
762#endif
763		}
764
765		if (res.pfcode & 0x80000)
766			printf("; pfcode: %08lx, options: %08lx\n",
767			       (unsigned long)res.pfcode,
768			       (unsigned long)res.options);
769
770/*
771 * Current env. (after this parse) is to become the
772 * new "working" environmnet. Used in conj. with sticky.
773 */
774		if (envset) {
775			res_x = res;
776			envset = 0;
777		}
778
779/*
780 * Current env. (after this parse) is to become the
781 * new default saved environmnet. Save in user specified
782 * file if exists else is SAVEENV (== "DiG.env").
783 */
784		if (envsave) {
785			afile = (char *) getenv("LOCALDEF");
786			if ((afile &&
787			     ((fp = open(afile,
788					 O_WRONLY|O_CREAT|O_TRUNC,
789					 S_IREAD|S_IWRITE)) > 0))
790			    ||
791			    ((fp = open(SAVEENV,
792					O_WRONLY|O_CREAT|O_TRUNC,
793					S_IREAD|S_IWRITE)) > 0)) {
794				write(fp, (char *)&res, (sizeof res));
795				close(fp);
796			}
797			envsave = 0;
798		}
799
800		if (res.pfcode & RES_PRF_CMD)
801			printf("%s\n", cmd);
802
803		anyflag = 0;
804
805/*
806 * Find address of server to query. If not dot-notation, then
807 * try to resolve domain-name (if so, save and turn off print
808 * options, this domain-query is not the one we want. Restore
809 * user options when done.
810 * Things get a bit wierd since we need to use resolver to be
811 * able to "put the resolver to work".
812 */
813
814		if (srv != NULL) {
815			int nscount = 0;
816			union res_sockaddr_union u[MAXNS];
817			struct addrinfo *answer = NULL;
818			struct addrinfo *cur = NULL;
819			struct addrinfo hint;
820
821			memset(u, 0, sizeof(u));
822			res_t = res;
823			res_ninit(&res);
824			res.pfcode = 0;
825			res.options = RES_DEFAULT;
826			memset(&hint, 0, sizeof(hint));
827			hint.ai_socktype = SOCK_DGRAM;
828			if (!getaddrinfo(srv, NULL, &hint, &answer)) {
829				res = res_t;
830				cur = answer;
831				for (cur = answer;
832				     cur != NULL;
833				     cur = cur->ai_next) {
834					if (nscount == MAXNS)
835						break;
836					switch (cur->ai_addr->sa_family) {
837					case AF_INET6:
838						u[nscount].sin6 =
839					  *(struct sockaddr_in6*)cur->ai_addr;
840						u[nscount++].sin6.sin6_port =
841							port;
842						break;
843					case AF_INET:
844						u[nscount].sin =
845					   *(struct sockaddr_in*)cur->ai_addr;
846						u[nscount++].sin.sin_port =
847							port;
848						break;
849					}
850				}
851				if (nscount != 0)
852					res_setservers(&res, u, nscount);
853				freeaddrinfo(answer);
854			} else {
855				res = res_t;
856				fflush(stdout);
857				fprintf(stderr,
858		"; Bad server: %s -- using default server and timer opts\n",
859						srv);
860				fflush(stderr);
861				srv = NULL;
862			}
863			printf("; (%d server%s found)\n",
864			       res.nscount, (res.nscount==1)?"":"s");
865			res.id += res.retry;
866		}
867
868		if (ns_t_xfr_p(xfr)) {
869			int i;
870			int nscount;
871			union res_sockaddr_union u[MAXNS];
872			nscount = res_getservers(&res, u, MAXNS);
873			for (i = 0; i < nscount; i++) {
874				int x;
875
876				if (keyfile)
877					x = printZone(xfr, domain,
878						      &u[i].sin,
879						      &key);
880				else
881					x = printZone(xfr, domain,
882						      &u[i].sin,
883						      NULL);
884				if (res.pfcode & RES_PRF_STATS) {
885					exectime = time(NULL);
886					printf(";; FROM: %s to SERVER: %s\n",
887					       myhostname,
888					       p_sockun(u[i], ubuf,
889							sizeof(ubuf)));
890					printf(";; WHEN: %s", ctime(&exectime));
891				}
892				if (!x)
893					break;	/* success */
894			}
895			fflush(stdout);
896			continue;
897		}
898
899		if (*domain && !qtypeSet) {
900			queryType = T_A;
901			qtypeSet++;
902		}
903
904		bytes_out = n = res_nmkquery(&res, QUERY, domain,
905					     queryClass, queryType,
906					     NULL, 0, NULL,
907					     packet, sizeof packet);
908		if (n < 0) {
909			fflush(stderr);
910			printf(";; res_nmkquery: buffer too small\n\n");
911			fflush(stdout);
912			continue;
913		}
914		if (queryType == T_IXFR) {
915			HEADER *hp = (HEADER *) packet;
916			u_char *cpp = packet + bytes_out;
917
918			hp->nscount = htons(1+ntohs(hp->nscount));
919			n = dn_comp(domain, cpp,
920				    (sizeof packet) - (cpp - packet),
921				    NULL, NULL);
922			cpp += n;
923			PUTSHORT(T_SOA, cpp); /* type */
924			PUTSHORT(C_IN, cpp);  /* class */
925			PUTLONG(0, cpp);      /* ttl */
926			PUTSHORT(22, cpp);    /* dlen */
927			*cpp++ = 0;           /* mname */
928			*cpp++ = 0;           /* rname */
929			PUTLONG(ixfr_serial, cpp);
930			PUTLONG(0xDEAD, cpp); /* Refresh */
931			PUTLONG(0xBEEF, cpp); /* Retry */
932			PUTLONG(0xABCD, cpp); /* Expire */
933			PUTLONG(0x1776, cpp); /* Min TTL */
934			bytes_out = n = cpp - packet;
935		};
936
937#if defined(RES_USE_EDNS0) && defined(RES_USE_DNSSEC)
938		if (n > 0 &&
939		    (res.options & (RES_USE_EDNS0|RES_USE_DNSSEC)) != 0)
940			bytes_out = n = res_nopt(&res, n, packet,
941						 sizeof(packet), 4096);
942#endif
943
944		eecode = 0;
945		if (res.pfcode & RES_PRF_HEAD1)
946			fp_resstat(&res, stdout);
947		(void) gettimeofday(&start_time, NULL);
948		if (keyfile)
949			n = res_nsendsigned(&res, packet, n, &key,
950					    answer.b, sizeof(answer.b));
951		else
952			n = res_nsend(&res, packet, n,
953				      answer.b, sizeof(answer.b));
954		if ((bytes_in = n) < 0) {
955			fflush(stdout);
956			n = 0 - n;
957			if (keyfile)
958				strcpy(msg, ";; res_nsendsigned");
959			else
960				strcpy(msg, ";; res_nsend");
961			perror(msg);
962			fflush(stderr);
963
964			if (!dofile) {
965				if (eecode)
966					exit(eecode);
967				else
968					exit(9);
969			}
970		}
971		(void) gettimeofday(&end_time, NULL);
972
973		if (res.pfcode & RES_PRF_STATS) {
974			union res_sockaddr_union u[MAXNS];
975
976			(void) res_getservers(&res, u, MAXNS);
977			query_time = difftv(start_time, end_time);
978			printf(";; Total query time: ");
979			prnttime(query_time);
980			putchar('\n');
981			exectime = time(NULL);
982			printf(";; FROM: %s to SERVER: %s\n", myhostname,
983			       p_sockun(u[RES_GETLAST(res)],
984					ubuf, sizeof(ubuf)));
985			printf(";; WHEN: %s", ctime(&exectime));
986			printf(";; MSG SIZE  sent: %d  rcvd: %d\n",
987			       bytes_out, bytes_in);
988		}
989
990		fflush(stdout);
991/*
992 *   Argh ... not particularly elegant. Should put in *real* ping code.
993 *   Would necessitate root priviledges for icmp port though!
994 */
995		if (*pingstr && srv != NULL) {
996			sprintf(doping, pingfmt, pingstr, srv, DIG_TAIL);
997			system(doping);
998		}
999		putchar('\n');
1000
1001/*
1002 * Fairly crude method and low overhead method of keeping two
1003 * batches started at different sites somewhat synchronized.
1004 */
1005		gettimeofday(&tv2, NULL);
1006		delay = (int)(tv2.tv_sec - tv1.tv_sec);
1007		if (delay < wait) {
1008			sleep(wait - delay);
1009		}
1010		tv1 = tv2;
1011	}
1012	return (eecode);
1013}
1014
1015/* Private. */
1016
1017static void
1018Usage() {
1019	fputs("\
1020usage:  dig [@server] [domain] [q-type] [q-class] {q-opt} {d-opt} [%comment]\n\
1021where:	server,\n\
1022	domain	are names in the Domain Name System\n\
1023	q-class	is one of (in,any,...) [default: in]\n\
1024	q-type	is one of (a,any,mx,ns,soa,hinfo,axfr,txt,...) [default: a]\n\
1025", stderr);
1026	fputs("\
1027	q-opt	is one of:\n\
1028		-x dot-notation-address	(shortcut to in-addr.arpa lookups)\n\
1029		-f file			(batch mode input file name)\n\
1030		-T time			(batch mode time delay, per query)\n\
1031		-p port			(nameserver is on this port) [53]\n\
1032		-b addr[:port]		(bind AXFR to this tcp address) [*]\n\
1033		-P[ping-string]		(see man page)\n\
1034		-t query-type		(synonym for q-type)\n\
1035		-c query-class		(synonym for q-class)\n\
1036		-k keydir:keyname	(sign the query with this TSIG key)\n\
1037		-envsav,-envset		(see man page)\n\
1038		-[no]stick		(see man page)\n\
1039", stderr);
1040	fputs("\
1041	d-opt	is of the form ``+keyword=value'' where keyword is one of:\n\
1042		[no]debug [no]d2 [no]recurse retry=# time=# [no]ko [no]vc\n\
1043		[no]defname [no]search domain=NAME [no]ignore [no]primary\n\
1044		[no]aaonly [no]cmd [no]stats [no]Header [no]header [no]trunc\n\
1045		[no]ttlid [no]cl [no]qr [no]reply [no]ques [no]answer\n\
1046		[no]author [no]addit [no]dnssec pfdef pfmin\n\
1047		pfset=# pfand=# pfor=#\n\
1048", stderr);
1049	fprintf(stderr, "\
1050notes:	defname and search don't work; use fully-qualified names.\n\
1051	this is DiG version %s (libbind %d)\n\
1052	Id: dig8.c,v 1.4 2009/03/03 23:49:07 tbox Exp \n", VSTRING, __RES);
1053}
1054
1055static int
1056setopt(const char *string) {
1057	char option[NAME_LEN], *ptr;
1058	int i;
1059
1060	i = pickString(string, option, sizeof option);
1061	if (i == 0) {
1062		fprintf(stderr, ";*** Invalid option: %s\n",  string);
1063
1064		/* this is ugly, but fixing the caller to behave
1065		   properly with an error return value would require a major
1066		   cleanup. */
1067		exit(9);
1068	}
1069
1070	if (strncmp(option, "aa", 2) == 0) {	/* aaonly */
1071		res.options |= RES_AAONLY;
1072	} else if (strncmp(option, "noaa", 4) == 0) {
1073		res.options &= ~RES_AAONLY;
1074	} else if (strncmp(option, "deb", 3) == 0) {	/* debug */
1075		res.options |= RES_DEBUG;
1076	} else if (strncmp(option, "nodeb", 5) == 0) {
1077		res.options &= ~(RES_DEBUG | RES_DEBUG2);
1078	} else if (strncmp(option, "ko", 2) == 0) {	/* keepopen */
1079		res.options |= (RES_STAYOPEN | RES_USEVC);
1080	} else if (strncmp(option, "noko", 4) == 0) {
1081		res.options &= ~RES_STAYOPEN;
1082	} else if (strncmp(option, "d2", 2) == 0) {	/* d2 (more debug) */
1083		res.options |= (RES_DEBUG | RES_DEBUG2);
1084	} else if (strncmp(option, "nod2", 4) == 0) {
1085		res.options &= ~RES_DEBUG2;
1086	} else if (strncmp(option, "def", 3) == 0) {	/* defname */
1087		res.options |= RES_DEFNAMES;
1088	} else if (strncmp(option, "nodef", 5) == 0) {
1089		res.options &= ~RES_DEFNAMES;
1090	} else if (strncmp(option, "dn", 2) == 0) {	/* dnssec */
1091		res.options |= RES_USE_DNSSEC;
1092	} else if (strncmp(option, "nodn", 4) == 0) {
1093		res.options &= ~RES_USE_DNSSEC;
1094	} else if (strncmp(option, "sea", 3) == 0) {	/* search list */
1095		res.options |= RES_DNSRCH;
1096	} else if (strncmp(option, "nosea", 5) == 0) {
1097		res.options &= ~RES_DNSRCH;
1098	} else if (strncmp(option, "do", 2) == 0) {	/* domain */
1099		ptr = strchr(option, '=');
1100		if (ptr != NULL) {
1101			i = pickString(++ptr, res.defdname, sizeof res.defdname);
1102			if (i == 0) { /* value's too long or non-existant. This actually
1103					 shouldn't happen due to pickString()
1104					 above */
1105				fprintf(stderr, "*** Invalid domain: %s\n", ptr) ;
1106				exit(9); /* see comment at previous call to exit()*/
1107			}
1108		}
1109	} else if (strncmp(option, "ti", 2) == 0) {      /* timeout */
1110		ptr = strchr(option, '=');
1111		if (ptr != NULL)
1112			sscanf(++ptr, "%d", &res.retrans);
1113	} else if (strncmp(option, "ret", 3) == 0) {    /* retry */
1114		ptr = strchr(option, '=');
1115		if (ptr != NULL)
1116			sscanf(++ptr, "%d", &res.retry);
1117	} else if (strncmp(option, "i", 1) == 0) {	/* ignore */
1118		res.options |= RES_IGNTC;
1119	} else if (strncmp(option, "noi", 3) == 0) {
1120		res.options &= ~RES_IGNTC;
1121	} else if (strncmp(option, "pr", 2) == 0) {	/* primary */
1122		res.options |= RES_PRIMARY;
1123	} else if (strncmp(option, "nop", 3) == 0) {
1124		res.options &= ~RES_PRIMARY;
1125	} else if (strncmp(option, "rec", 3) == 0) {	/* recurse */
1126		res.options |= RES_RECURSE;
1127	} else if (strncmp(option, "norec", 5) == 0) {
1128		res.options &= ~RES_RECURSE;
1129	} else if (strncmp(option, "v", 1) == 0) {	/* vc */
1130		res.options |= RES_USEVC;
1131	} else if (strncmp(option, "nov", 3) == 0) {
1132		res.options &= ~RES_USEVC;
1133	} else if (strncmp(option, "pfset", 5) == 0) {
1134		ptr = strchr(option, '=');
1135		if (ptr != NULL)
1136			res.pfcode = xstrtonum(++ptr);
1137	} else if (strncmp(option, "pfand", 5) == 0) {
1138		ptr = strchr(option, '=');
1139		if (ptr != NULL)
1140			res.pfcode = res.pfcode & xstrtonum(++ptr);
1141	} else if (strncmp(option, "pfor", 4) == 0) {
1142		ptr = strchr(option, '=');
1143		if (ptr != NULL)
1144			res.pfcode |= xstrtonum(++ptr);
1145	} else if (strncmp(option, "pfmin", 5) == 0) {
1146		res.pfcode = PRF_MIN;
1147	} else if (strncmp(option, "pfdef", 5) == 0) {
1148		res.pfcode = PRF_DEF;
1149	} else if (strncmp(option, "an", 2) == 0) {  /* answer section */
1150		res.pfcode |= RES_PRF_ANS;
1151	} else if (strncmp(option, "noan", 4) == 0) {
1152		res.pfcode &= ~RES_PRF_ANS;
1153	} else if (strncmp(option, "qu", 2) == 0) {  /* question section */
1154		res.pfcode |= RES_PRF_QUES;
1155	} else if (strncmp(option, "noqu", 4) == 0) {
1156		res.pfcode &= ~RES_PRF_QUES;
1157	} else if (strncmp(option, "au", 2) == 0) {  /* authority section */
1158		res.pfcode |= RES_PRF_AUTH;
1159	} else if (strncmp(option, "noau", 4) == 0) {
1160		res.pfcode &= ~RES_PRF_AUTH;
1161	} else if (strncmp(option, "ad", 2) == 0) {  /* addition section */
1162		res.pfcode |= RES_PRF_ADD;
1163	} else if (strncmp(option, "noad", 4) == 0) {
1164		res.pfcode &= ~RES_PRF_ADD;
1165	} else if (strncmp(option, "tt", 2) == 0) {  /* TTL & ID */
1166		res.pfcode |= RES_PRF_TTLID;
1167	} else if (strncmp(option, "nott", 4) == 0) {
1168		res.pfcode &= ~RES_PRF_TTLID;
1169	} else if (strncmp(option, "tr", 2) == 0) {  /* TTL & ID */
1170		res.pfcode |= RES_PRF_TRUNC;
1171	} else if (strncmp(option, "notr", 4) == 0) {
1172		res.pfcode &= ~RES_PRF_TRUNC;
1173	} else if (strncmp(option, "he", 2) == 0) {  /* head flags stats */
1174		res.pfcode |= RES_PRF_HEAD2;
1175	} else if (strncmp(option, "nohe", 4) == 0) {
1176		res.pfcode &= ~RES_PRF_HEAD2;
1177	} else if (strncmp(option, "H", 1) == 0) {  /* header all */
1178		res.pfcode |= RES_PRF_HEADX;
1179	} else if (strncmp(option, "noH", 3) == 0) {
1180		res.pfcode &= ~(RES_PRF_HEADX);
1181	} else if (strncmp(option, "qr", 2) == 0) {  /* query */
1182		res.pfcode |= RES_PRF_QUERY;
1183	} else if (strncmp(option, "noqr", 4) == 0) {
1184		res.pfcode &= ~RES_PRF_QUERY;
1185	} else if (strncmp(option, "rep", 3) == 0) {  /* reply */
1186		res.pfcode |= RES_PRF_REPLY;
1187	} else if (strncmp(option, "norep", 5) == 0) {
1188		res.pfcode &= ~RES_PRF_REPLY;
1189	} else if (strncmp(option, "cm", 2) == 0) {  /* command line */
1190		res.pfcode |= RES_PRF_CMD;
1191	} else if (strncmp(option, "nocm", 4) == 0) {
1192		res.pfcode &= ~RES_PRF_CMD;
1193	} else if (strncmp(option, "cl", 2) == 0) {  /* class mnemonic */
1194		res.pfcode |= RES_PRF_CLASS;
1195	} else if (strncmp(option, "nocl", 4) == 0) {
1196		res.pfcode &= ~RES_PRF_CLASS;
1197	} else if (strncmp(option, "st", 2) == 0) {  /* stats*/
1198		res.pfcode |= RES_PRF_STATS;
1199	} else if (strncmp(option, "nost", 4) == 0) {
1200		res.pfcode &= ~RES_PRF_STATS;
1201	} else {
1202		fprintf(stderr, "; *** Invalid option: %s\n",  option);
1203		return (ERROR);
1204	}
1205	res_re_init();
1206	return (SUCCESS);
1207}
1208
1209/*
1210 * Force a reinitialization when the domain is changed.
1211 */
1212static void
1213res_re_init() {
1214	static char localdomain[] = "LOCALDOMAIN";
1215	u_long pfcode = res.pfcode, options = res.options;
1216	unsigned ndots = res.ndots;
1217	int retrans = res.retrans, retry = res.retry;
1218	char *buf;
1219
1220	/*
1221	 * This is ugly but putenv() is more portable than setenv().
1222	 */
1223	buf = malloc((sizeof localdomain) + strlen(res.defdname) +10/*fuzz*/);
1224	sprintf(buf, "%s=%s", localdomain, res.defdname);
1225	putenv(buf);	/* keeps the argument, so we won't free it */
1226	res_ninit(&res);
1227	res.pfcode = pfcode;
1228	res.options = options;
1229	res.ndots = ndots;
1230	res.retrans = retrans;
1231	res.retry = retry;
1232}
1233
1234/*
1235 * convert char string (decimal, octal, or hex) to integer
1236 */
1237static int
1238xstrtonum(char *p) {
1239	int v = 0;
1240	int i;
1241	int b = 10;
1242	int flag = 0;
1243	while (*p != 0) {
1244		if (!flag++)
1245			if (*p == '0') {
1246				b = 8; p++;
1247				continue;
1248			}
1249		if (isupper((unsigned char)*p))
1250			*p = tolower(*p);
1251		if (*p == 'x') {
1252			b = 16; p++;
1253			continue;
1254		}
1255		if (isdigit((unsigned char)*p)) {
1256			i = *p - '0';
1257		} else if (isxdigit((unsigned char)*p)) {
1258			i = *p - 'a' + 10;
1259		} else {
1260			fprintf(stderr,
1261				"; *** Bad char in numeric string..ignored\n");
1262			i = -1;
1263		}
1264		if (i >= b) {
1265			fprintf(stderr,
1266				"; *** Bad char in numeric string..ignored\n");
1267			i = -1;
1268		}
1269		if (i >= 0)
1270			v = v * b + i;
1271		p++;
1272	}
1273	return (v);
1274}
1275
1276typedef union {
1277	HEADER qb1;
1278	u_char qb2[PACKETSZ];
1279} querybuf;
1280
1281static int
1282printZone(ns_type xfr, const char *zone, const struct sockaddr_in *sin,
1283	  ns_tsig_key *key)
1284{
1285	static u_char *answer = NULL;
1286	static int answerLen = 0;
1287
1288	querybuf buf;
1289	int msglen, amtToRead, numRead, result, sockFD, len;
1290	int count, type, rlen, done, n;
1291	int numAnswers, numRecords, soacnt;
1292	u_char *cp, tmp[NS_INT16SZ];
1293	char dname[2][NS_MAXDNAME];
1294	enum { NO_ERRORS, ERR_READING_LEN, ERR_READING_MSG, ERR_PRINTING }
1295		error;
1296	pid_t zpid = -1;
1297	u_char *newmsg;
1298	int newmsglen;
1299	ns_tcp_tsig_state tsig_state;
1300	int tsig_ret, tsig_required, tsig_present;
1301
1302	switch (xfr) {
1303	case ns_t_axfr:
1304	case ns_t_zxfr:
1305		break;
1306	default:
1307		fprintf(stderr, ";; %s - transfer type not supported\n",
1308			p_type(xfr));
1309		return (ERROR);
1310	}
1311
1312	/*
1313	 *  Create a query packet for the requested zone name.
1314	 */
1315	msglen = res_nmkquery(&res, ns_o_query, zone,
1316			      queryClass, ns_t_axfr, NULL,
1317			      0, 0, buf.qb2, sizeof buf);
1318	if (msglen < 0) {
1319		if (res.options & RES_DEBUG)
1320			fprintf(stderr, ";; res_nmkquery failed\n");
1321		return (ERROR);
1322	}
1323
1324	/*
1325	 * Sign the message if a key was sent
1326	 */
1327	if (key == NULL) {
1328		newmsg = (u_char *)&buf;
1329		newmsglen = msglen;
1330	} else {
1331		DST_KEY *dstkey;
1332		int bufsize, siglen;
1333		u_char sig[64];
1334		int ret;
1335
1336		/* ns_sign() also calls dst_init(), but there is no harm
1337		 * doing it twice
1338		 */
1339		dst_init();
1340
1341		bufsize = msglen + 1024;
1342		newmsg = (u_char *) malloc(bufsize);
1343		if (newmsg == NULL) {
1344			errno = ENOMEM;
1345			return (-1);
1346		}
1347		memcpy(newmsg, (u_char *)&buf, msglen);
1348		newmsglen = msglen;
1349
1350		if (strcmp(key->alg, NS_TSIG_ALG_HMAC_MD5) != 0)
1351			dstkey = NULL;
1352		else
1353			dstkey = dst_buffer_to_key(key->name, KEY_HMAC_MD5,
1354							NS_KEY_TYPE_AUTH_ONLY,
1355							NS_KEY_PROT_ANY,
1356							key->data, key->len);
1357		if (dstkey == NULL) {
1358			errno = EINVAL;
1359			if (key)
1360				free(newmsg);
1361			return (-1);
1362		}
1363
1364		siglen = sizeof(sig);
1365/* newmsglen++; */
1366		ret = ns_sign(newmsg, &newmsglen, bufsize, NOERROR, dstkey, NULL, 0,
1367		      sig, &siglen, 0);
1368		if (ret < 0) {
1369			if (key)
1370				free (newmsg);
1371			if (ret == NS_TSIG_ERROR_NO_SPACE)
1372				errno  = EMSGSIZE;
1373			else if (ret == -1)
1374				errno  = EINVAL;
1375			return (ret);
1376		}
1377		ns_verify_tcp_init(dstkey, sig, siglen, &tsig_state);
1378	}
1379
1380	/*
1381	 *  Set up a virtual circuit to the server.
1382	 */
1383	if ((sockFD = socket(sin->sin_family, SOCK_STREAM, 0)) < 0) {
1384		int e = errno;
1385
1386		perror(";; socket");
1387		return (e);
1388	}
1389
1390	switch (sin->sin_family) {
1391	case AF_INET:
1392		if (bind(sockFD, (struct sockaddr *)&myaddress,
1393			 sizeof myaddress) < 0){
1394			int e = errno;
1395
1396			fprintf(stderr, ";; bind(%s port %u): %s\n",
1397				inet_ntoa(myaddress.sin_addr),
1398				ntohs(myaddress.sin_port),
1399				strerror(e));
1400			(void) close(sockFD);
1401			sockFD = -1;
1402			return (e);
1403		}
1404		if (connect(sockFD, (const struct sockaddr *)sin,
1405			    sizeof *sin) < 0) {
1406			int e = errno;
1407
1408			perror(";; connect");
1409			(void) close(sockFD);
1410			sockFD = -1;
1411			return (e);
1412		}
1413		break;
1414	case AF_INET6:
1415		if (bind(sockFD, (struct sockaddr *)&myaddress6,
1416			 sizeof myaddress6) < 0){
1417			int e = errno;
1418			char buf[80];
1419
1420			fprintf(stderr, ";; bind(%s port %u): %s\n",
1421				inet_ntop(AF_INET6, &myaddress6.sin6_addr,
1422					  buf, sizeof(buf)),
1423				ntohs(myaddress6.sin6_port),
1424				strerror(e));
1425			(void) close(sockFD);
1426			sockFD = -1;
1427			return (e);
1428		}
1429		if (connect(sockFD, (const struct sockaddr *)sin,
1430			    sizeof(struct sockaddr_in6)) < 0) {
1431			int e = errno;
1432
1433			perror(";; connect");
1434			(void) close(sockFD);
1435			sockFD = -1;
1436			return (e);
1437		}
1438		break;
1439	}
1440
1441	/*
1442	 * Send length & message for zone transfer
1443	 */
1444
1445	ns_put16(newmsglen, tmp);
1446        if (write(sockFD, (char *)tmp, NS_INT16SZ) != NS_INT16SZ ||
1447            write(sockFD, (char *)newmsg, newmsglen) != newmsglen) {
1448		int e = errno;
1449		if (key)
1450			free (newmsg);
1451		perror(";; write");
1452		(void) close(sockFD);
1453		sockFD = -1;
1454		return (e);
1455	} else if (key)
1456		free (newmsg);
1457
1458	/*
1459	 * If we're compressing, push a gzip into the pipeline.
1460	 */
1461	if (xfr == ns_t_zxfr) {
1462		enum { rd = 0, wr = 1 };
1463		int z[2];
1464
1465		if (pipe(z) < 0) {
1466			int e = errno;
1467
1468			perror(";; pipe");
1469			(void) close(sockFD);
1470			sockFD = -1;
1471			return (e);
1472		}
1473		zpid = vfork();
1474		if (zpid < 0) {
1475			int e = errno;
1476
1477			perror(";; fork");
1478			(void) close(sockFD);
1479			sockFD = -1;
1480			return (e);
1481		} else if (zpid == 0) {
1482			/* Child. */
1483			(void) close(z[rd]);
1484			(void) dup2(sockFD, STDIN_FILENO);
1485			(void) close(sockFD);
1486			(void) dup2(z[wr], STDOUT_FILENO);
1487			(void) close(z[wr]);
1488			execlp("gzip", "gzip", "-d", "-v", NULL);
1489			perror(";; child: execlp(gunzip)");
1490			_exit(1);
1491		}
1492		/* Parent. */
1493		(void) close(z[wr]);
1494		(void) dup2(z[rd], sockFD);
1495		(void) close(z[rd]);
1496	}
1497	result = 0;
1498	numAnswers = 0;
1499	numRecords = 0;
1500	soacnt = 0;
1501	error = NO_ERRORS;
1502	numRead = 0;
1503
1504	dname[0][0] = '\0';
1505	for (done = 0; !done; (void)NULL) {
1506		/*
1507		 * Read the length of the response.
1508		 */
1509
1510		cp = tmp;
1511		amtToRead = INT16SZ;
1512		while (amtToRead > 0 &&
1513		   (numRead = read(sockFD, cp, amtToRead)) > 0) {
1514			cp += numRead;
1515			amtToRead -= numRead;
1516		}
1517		if (numRead <= 0) {
1518			error = ERR_READING_LEN;
1519			break;
1520		}
1521
1522		len = ns_get16(tmp);
1523		if (len == 0)
1524			break;	/* nothing left to read */
1525
1526		/*
1527		 * The server sent too much data to fit the existing buffer --
1528		 * allocate a new one.
1529		 */
1530		if (len > answerLen) {
1531			if (answerLen != 0)
1532				free(answer);
1533			answerLen = len;
1534			answer = (u_char *)malloc(answerLen);
1535		}
1536
1537		/*
1538		 * Read the response.
1539		 */
1540
1541		amtToRead = len;
1542		cp = answer;
1543		while (amtToRead > 0 &&
1544		       (numRead = read(sockFD, cp, amtToRead)) > 0) {
1545			cp += numRead;
1546			amtToRead -= numRead;
1547		}
1548		if (numRead <= 0) {
1549			error = ERR_READING_MSG;
1550			break;
1551		}
1552
1553		result = print_axfr(stdout, answer, len);
1554		if (result != 0) {
1555			error = ERR_PRINTING;
1556			break;
1557		}
1558		numRecords += htons(((HEADER *)answer)->ancount);
1559		numAnswers++;
1560
1561		/* Header. */
1562		cp = answer + HFIXEDSZ;
1563		/* Question. */
1564		for (count = ntohs(((HEADER *)answer)->qdcount);
1565		     count > 0;
1566		     count--) {
1567			n = dn_skipname(cp, answer + len);
1568			if (n < 0) {
1569				error = ERR_PRINTING;
1570				done++;
1571				break;
1572			}
1573			cp += n + QFIXEDSZ;
1574			if (cp > answer + len) {
1575				error = ERR_PRINTING;
1576				done++;
1577				break;
1578			}
1579		}
1580		/* Answer. */
1581		for (count = ntohs(((HEADER *)answer)->ancount);
1582		     count > 0 && !done;
1583		     count--) {
1584			n = dn_expand(answer, answer + len, cp,
1585				      dname[soacnt], sizeof dname[0]);
1586			if (n < 0) {
1587				error = ERR_PRINTING;
1588				done++;
1589				break;
1590			}
1591			cp += n;
1592			if (cp + 3 * INT16SZ + INT32SZ > answer + len) {
1593				error = ERR_PRINTING;
1594				done++;
1595				break;
1596			}
1597			GETSHORT(type, cp);
1598			cp += INT16SZ;
1599			cp += INT32SZ;	/* ttl */
1600			GETSHORT(rlen, cp);
1601			cp += rlen;
1602			if (cp > answer + len) {
1603				error = ERR_PRINTING;
1604				done++;
1605				break;
1606			}
1607			if (type == T_SOA && soacnt++ &&
1608			    ns_samename(dname[0], dname[1]) == 1) {
1609				done++;
1610				break;
1611			}
1612		}
1613
1614		/*
1615		 * Verify the TSIG
1616		 */
1617
1618		if (key) {
1619			if (ns_find_tsig(answer, answer + len) != NULL)
1620				tsig_present = 1;
1621			else
1622				tsig_present = 0;
1623			if (numAnswers == 1 || soacnt > 1)
1624				tsig_required = 1;
1625			else
1626				tsig_required = 0;
1627			tsig_ret = ns_verify_tcp(answer, &len, &tsig_state,
1628						 tsig_required);
1629			if (tsig_ret == 0) {
1630				if (tsig_present)
1631					printf("; TSIG ok\n");
1632			}
1633			else
1634				printf("; TSIG invalid\n");
1635		}
1636
1637	}
1638
1639	printf(";; Received %d answer%s (%d record%s).\n",
1640	       numAnswers, (numAnswers != 1) ? "s" : "",
1641	       numRecords, (numRecords != 1) ? "s" : "");
1642
1643	(void) close(sockFD);
1644	sockFD = -1;
1645
1646	/*
1647	 * If we were uncompressing, reap the uncompressor.
1648	 */
1649	if (xfr == ns_t_zxfr) {
1650		pid_t pid;
1651		int status = 0;
1652
1653		pid = wait(&status);
1654		if (pid < 0) {
1655			int e = errno;
1656
1657			perror(";; wait");
1658			return (e);
1659		}
1660		if (pid != zpid) {
1661			fprintf(stderr, ";; wrong pid (%lu != %lu)\n",
1662				(u_long)pid, (u_long)zpid);
1663			return (ERROR);
1664		}
1665		printf(";; pid %lu: exit %d, signal %d, core %c\n",
1666		       (u_long)pid, WEXITSTATUS(status),
1667		       WIFSIGNALED(status) ? WTERMSIG(status) : 0,
1668		       WCOREDUMP(status) ? 't' : 'f');
1669	}
1670
1671	switch (error) {
1672	case NO_ERRORS:
1673		return (0);
1674
1675	case ERR_READING_LEN:
1676		return (EMSGSIZE);
1677
1678	case ERR_PRINTING:
1679		return (result);
1680
1681	case ERR_READING_MSG:
1682		return (EMSGSIZE);
1683
1684	default:
1685		return (EFAULT);
1686	}
1687}
1688
1689static int
1690print_axfr(FILE *file, const u_char *msg, size_t msglen) {
1691	ns_msg handle;
1692
1693	if (ns_initparse(msg, msglen, &handle) < 0) {
1694		fprintf(file, ";; ns_initparse: %s\n", strerror(errno));
1695		return (ns_r_formerr);
1696	}
1697	if (ns_msg_getflag(handle, ns_f_rcode) != ns_r_noerror)
1698		return (ns_msg_getflag(handle, ns_f_rcode));
1699
1700	/*
1701	 * We are looking for info from answer resource records.
1702	 * If there aren't any, return with an error. We assume
1703	 * there aren't any question records.
1704	 */
1705	if (ns_msg_count(handle, ns_s_an) == 0)
1706		return (NO_INFO);
1707
1708#ifdef PROTOCOLDEBUG
1709	printf(";;; (message of %d octets has %d answers)\n",
1710	       msglen, ns_msg_count(handle, ns_s_an));
1711#endif
1712	for (;;) {
1713		static char origin[NS_MAXDNAME], name_ctx[NS_MAXDNAME];
1714		const char *name;
1715		char buf[2048];		/* XXX need to malloc/realloc. */
1716		ns_rr rr;
1717
1718		if (ns_parserr(&handle, ns_s_an, -1, &rr)) {
1719			if (errno != ENODEV) {
1720				fprintf(file, ";; ns_parserr: %s\n",
1721					strerror(errno));
1722				return (FORMERR);
1723			}
1724			break;
1725		}
1726		name = ns_rr_name(rr);
1727		if (origin[0] == '\0' && name[0] != '\0') {
1728			if (strcmp(name, ".") != 0)
1729				strcpy(origin, name);
1730			fprintf(file, "$ORIGIN %s.\n", origin);
1731			if (strcmp(name, ".") == 0)
1732				strcpy(origin, name);
1733			if (res.pfcode & RES_PRF_TRUNC)
1734				strcpy(name_ctx, "@");
1735		}
1736		if (ns_sprintrr(&handle, &rr,
1737				(res.pfcode & RES_PRF_TRUNC) ? name_ctx : NULL,
1738				(res.pfcode & RES_PRF_TRUNC) ? origin : NULL,
1739				buf, sizeof buf) < 0) {
1740			fprintf(file, ";; ns_sprintrr: %s\n", strerror(errno));
1741			return (FORMERR);
1742		}
1743		strcpy(name_ctx, name);
1744		fputs(buf, file);
1745		fputc('\n', file);
1746	}
1747	return (SUCCESS);
1748}
1749
1750static struct timeval
1751difftv(struct timeval a, struct timeval b) {
1752	static struct timeval diff;
1753
1754	diff.tv_sec = b.tv_sec - a.tv_sec;
1755	if ((diff.tv_usec = b.tv_usec - a.tv_usec) < 0) {
1756		diff.tv_sec--;
1757		diff.tv_usec += 1000000;
1758	}
1759	return (diff);
1760}
1761
1762static void
1763prnttime(struct timeval t) {
1764	printf("%lu msec", (u_long)(t.tv_sec * 1000 + (t.tv_usec / 1000)));
1765}
1766
1767/*
1768 * Take arguments appearing in simple string (from file or command line)
1769 * place in char**.
1770 */
1771static void
1772stackarg(char *l, char **y) {
1773	int done = 0;
1774
1775	while (!done) {
1776		switch (*l) {
1777		case '\t':
1778		case ' ':
1779			l++;
1780			break;
1781		case '\0':
1782		case '\n':
1783			done++;
1784			*y = NULL;
1785			break;
1786		default:
1787			*y++ = l;
1788			while (!isspace((unsigned char)*l))
1789				l++;
1790			if (*l == '\n')
1791				done++;
1792			*l++ = '\0';
1793			*y = NULL;
1794		}
1795	}
1796}
1797
1798static void
1799reverse6(char *domain, struct in6_addr *in6) {
1800	sprintf(domain, "%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.ip6.arpa",
1801		in6->s6_addr[15] & 0x0f, (in6->s6_addr[15] >> 4) & 0x0f,
1802		in6->s6_addr[14] & 0x0f, (in6->s6_addr[14] >> 4) & 0x0f,
1803		in6->s6_addr[13] & 0x0f, (in6->s6_addr[13] >> 4) & 0x0f,
1804		in6->s6_addr[12] & 0x0f, (in6->s6_addr[12] >> 4) & 0x0f,
1805		in6->s6_addr[11] & 0x0f, (in6->s6_addr[11] >> 4) & 0x0f,
1806		in6->s6_addr[10] & 0x0f, (in6->s6_addr[10] >> 4) & 0x0f,
1807		in6->s6_addr[9] & 0x0f, (in6->s6_addr[9] >> 4) & 0x0f,
1808		in6->s6_addr[8] & 0x0f, (in6->s6_addr[8] >> 4) & 0x0f,
1809		in6->s6_addr[7] & 0x0f, (in6->s6_addr[7] >> 4) & 0x0f,
1810		in6->s6_addr[6] & 0x0f, (in6->s6_addr[6] >> 4) & 0x0f,
1811		in6->s6_addr[5] & 0x0f, (in6->s6_addr[5] >> 4) & 0x0f,
1812		in6->s6_addr[4] & 0x0f, (in6->s6_addr[4] >> 4) & 0x0f,
1813		in6->s6_addr[3] & 0x0f, (in6->s6_addr[3] >> 4) & 0x0f,
1814		in6->s6_addr[2] & 0x0f, (in6->s6_addr[2] >> 4) & 0x0f,
1815		in6->s6_addr[1] & 0x0f, (in6->s6_addr[1] >> 4) & 0x0f,
1816		in6->s6_addr[0] & 0x0f, (in6->s6_addr[0] >> 4) & 0x0f);
1817}
1818