/*	$NetBSD: host.c,v 1.2.2.2 2000/10/08 16:18:44 he Exp $	*/

#ifndef lint
static const char rcsid[] = "Id: host.c,v 8.34 1999/11/11 19:39:10 cyarnell Exp";
#endif /* not lint */

/*
 * Copyright (c) 1986
 *    The Regents of the University of California.  All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 * 	This product includes software developed by the University of
 * 	California, Berkeley and its contributors.
 * 4. Neither the name of the University nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

/*
 * Portions Copyright (c) 1993 by Digital Equipment Corporation.
 * 
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies, and that
 * the name of Digital Equipment Corporation not be used in advertising or
 * publicity pertaining to distribution of the document or software without
 * specific, written prior permission.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL
 * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS.   IN NO EVENT SHALL DIGITAL EQUIPMENT
 * CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
 * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
 * SOFTWARE.
 */

/*
 * Portions Copyright (c) 1996-1999 by Internet Software Consortium
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS
 * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE
 * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
 * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
 * SOFTWARE.
 */

#ifndef lint
static const char copyright[] =
"@(#) Copyright (c) 1986 Regents of the University of California.\n\
 Portions Copyright (c) 1993 Digital Equipment Corporation.\n\
 Portions Copyright (c) 1996-1999 Internet Software Consortium.\n\
 All rights reserved.\n";
#endif /* not lint */

/*
 * Actually, this program is from Rutgers University, however it is 
 * based on nslookup and other pieces of named tools, so it needs
 * the above copyright notices.
 */

/* Import. */

#include "port_before.h"

#include <sys/types.h>
#include <sys/param.h>
#include <sys/socket.h>

#include <netinet/in.h>
#include <arpa/inet.h>
#include <arpa/nameser.h>

#include <ctype.h>
#include <netdb.h>
#include <resolv.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include <memory.h>
#include <errno.h>
#include <sys/stat.h>
#include <isc/dst.h>

#include "port_after.h"

/* Global. */

#define SIG_RDATA_BY_NAME	18
#define NS_HEADERDATA_SIZE 10

#define NUMNS		8
#define NUMNSADDR	16
#define NUMMX		50
#define NUMRR		127   /* max rr's per node to verify signatures for */

#define SUCCESS		0
#define TIME_OUT	-1
#define NO_INFO 	-2
#define ERROR 		-3
#define NONAUTH 	-4

#define MY_PACKETSZ    64*1024  /* need this to hold tcp answers */

typedef union {
	HEADER	qb1;
	u_char	qb2[MY_PACKETSZ];
} querybuf;

#define SD_RR        1
#define SD_SIG       2
#define SD_BADSIG    4

typedef struct {
	u_char data[MY_PACKETSZ];
	size_t len;
} rrstruct;

static char		chase_domain[NS_MAXDNAME];
static int		chase_class;
static int		chase_type;
static char		chase_sigorigttl[NS_INT32SZ];
static rrstruct		chase_rr[NUMRR];
static int		chase_rr_num;
static char		chase_lastgoodkey[NS_MAXDNAME];
static char		chase_signer[NS_MAXDNAME];
static u_char		chase_sigrdata[MY_PACKETSZ];
static size_t		chase_sigrdata_len;
static u_char		chase_signature[MY_PACKETSZ];
static size_t		chase_signature_len;
static int		chase_step;
static int		sigchase;

static char		cnamebuf[NS_MAXDNAME];
static u_char		hostbuf[NS_MAXDNAME];

static int		sockFD;
static FILE		*filePtr;

static struct __res_state  res, orig;
static char		*cname = NULL;
static const char	*progname = "amnesia";
static int		getclass = ns_c_in, verbose = 0, list = 0;
static int		server_specified = 0;
static int		gettype;
static char		getdomain[NS_MAXDNAME];

/* Forward. */

static int		parsetype(const char *s);
static int		parseclass(const char *s);
static void		printanswer(const struct hostent *hp);
static void		hperror(int errnum);
static int		addrinfo(struct in_addr addr);
static int		gethostinfo(char *name);
static int		getdomaininfo(const char *name, const char *domain);
static int		getinfo(const char *name, const char *domain,
				int type);
static int		printinfo(const querybuf *answer, const u_char *eom,
				  int filter, int isls);
static const u_char *	pr_rr(const u_char *cp, const u_char *msg, FILE *file,
			      int filter);
static const char *	pr_type(int type);
static const char *	pr_class(int class);
static const u_char *	pr_cdname(const u_char *cp, const u_char *msg,
				  char *name, int namelen);
static int		ListHosts(char *namePtr, int queryType);
static const char *	DecodeError(int result);

static void
usage(const char *msg) {
	fprintf(stderr, "%s: usage error (%s)\n", progname, msg);
	fprintf(stderr, "\
Usage: %s [-adlrwv] [-t querytype] [-c class] host [server]\n\
\t-a is equivalent to '-v -t *'\n\
\t-c class to look for non-Internet data\n\
\t-d to turn on debugging output\n\
\t-l to turn on 'list mode'\n\
\t-r to disable recursive processing\n\
\t-s recursively chase signature found in answers\n\
\t-t querytype to look for a specific type of information\n\
\t-v for verbose output\n\
\t-w to wait forever until reply\n\
", progname);
	exit(1);
}

/* Public. */

int
main(int argc, char **argv) {
	struct in_addr addr;
	struct hostent *hp;
	char *s;
	int inverse = 0, waitmode = 0;
	int ncnames, ch;
	int nkeychains, i;

	dst_init();

	if ((progname = strrchr(argv[0], '/')) == NULL)
		progname = argv[0];
	else
		progname++;
	res_ninit(&res);
	res.retrans = 5;
	while ((ch = getopt(argc, argv, "ac:dlrst:vw")) != -1) {
		switch (ch) {
		case 'a':
			verbose = 1;
			gettype = ns_t_any;
			break;
		case 'c':
			getclass = parseclass(optarg);
			break;
		case 'd':
			res.options |= RES_DEBUG;
			break;
		case 'l':
			list = 1;
			break;
		case 'r':
			res.options &= ~RES_RECURSE;
			break;
		case 's':
			sigchase = 1;
			break;
		case 't':
			gettype = parsetype(optarg);
			break;
		case 'v':
			verbose = 1;
			break;
		case 'w':
			res.retry = 1;
			res.retrans = 15;
			waitmode = 1;
			break;
		default:
			usage("unrecogized switch");
			/*NOTREACHED*/
		}
	}
	if (gettype == 0) {
		if (verbose)
			printf ("Forcing `-t a' for signature trace.\n");
		gettype = ns_t_a;
	}
	argc -= optind;
	argv += optind;
	if (argc < 1)
		usage("missing host argument");
	strncpy(getdomain, *argv++, NS_MAXDNAME);
	getdomain[NS_MAXDNAME-1] = 0;
	argc--;
	if (argc > 1)
		usage("extra undefined arguments");
	if (argc == 1) {
		s = *argv++;
		argc--;
		server_specified++;
		
		if (!inet_aton(s, &addr)) {
			hp = gethostbyname(s);
			if (hp == NULL) {
				fprintf(stderr,
					"Error in looking up server name:\n");
				hperror(res.res_h_errno);
				exit(1);
			}
			memcpy(&res.nsaddr.sin_addr, hp->h_addr, NS_INADDRSZ);
			printf("Using domain server:\n");
			printanswer(hp);
		} else {
			res.nsaddr.sin_family = AF_INET;
			res.nsaddr.sin_addr = addr;
			res.nsaddr.sin_port = htons(NAMESERVER_PORT);
			printf("Using domain server %s:\n",
			       inet_ntoa(res.nsaddr.sin_addr));
		}
		res.nscount = 1;
		res.retry = 2;
	}
	if (strcmp(getdomain, ".") == 0 || !inet_aton(getdomain, &addr))
		addr.s_addr = INADDR_NONE;
	hp = NULL;
	res.res_h_errno = TRY_AGAIN;
/*
 * We handle default domains ourselves, thank you.
 */
	res.options &= ~RES_DEFNAMES;

        if (list)
		exit(ListHosts(getdomain, gettype ? gettype : ns_t_a));
	ncnames = 5; nkeychains = 18;
	while (hp == NULL && res.res_h_errno == TRY_AGAIN) {
		if (addr.s_addr == INADDR_NONE) {
			cname = NULL;
			hp = (struct hostent *)(unsigned long)gethostinfo(getdomain);
			getdomain[0] = 0; /* clear this query */
			if (sigchase && (chase_step & SD_RR)) {
				if (nkeychains-- == 0) {
					printf("Too many sig/key chains. Loop?\n");
					exit(1);
				}
				if (chase_step & SD_SIG) {
					/* start new query, for KEY */
					strcpy (getdomain, chase_signer);
					strcat (getdomain, ".");
					gettype = ns_t_key;
				} else if (!(chase_step & SD_BADSIG)) { 
					/* start new query, for SIG */
					strcpy (getdomain, chase_domain);
					strcat (getdomain, ".");
					gettype = ns_t_sig;
				} else if (hp && !(chase_step & SD_SIG) && 
					   (chase_step & SD_BADSIG)) {
					printf ("%s for %s not found, last verified key %s\n",
						chase_step & SD_SIG ? "Key" : "Signature",
						chase_step & SD_SIG ? chase_signer : chase_domain, 
						chase_lastgoodkey ? chase_lastgoodkey : "None");
				}
			}
			if (!getdomain[0] && cname) {
				if (ncnames-- == 0) {
					printf("Too many cnames.  Loop?\n");
					exit(1);
				}
				strcpy(getdomain, cname);
				strcat(getdomain, ".");
			}
			if (getdomain[0]) {
				if (chase_step & SD_SIG) {
					printf ("Locating key for %s\n", getdomain);
				} else if (chase_step & SD_SIG) {
					printf ("Locating signature for %s record(s) on %s\n",
						sym_ntos(__p_type_syms, chase_type, NULL),
						getdomain);
				}
				hp = NULL;
				res.res_h_errno = TRY_AGAIN;
				continue;
			}
		} else {
			if (addrinfo(addr) == 0)
				hp = NULL;
			else
				hp = (struct hostent *)1;	/* XXX */
		}
		if (!waitmode)
			break;
	}

	if (hp == NULL) {
		hperror(res.res_h_errno);
		exit(1);
	}

	exit(0);
}

/* Private. */

static int
parsetype(const char *s) {
	int type, success;

	type = sym_ston(__p_type_syms, s, &success);
	if (success)
		return (type);
	if (strcmp(s, "*") == 0)
		return (ns_t_any);
	if (atoi(s))
		return (atoi(s));
	fprintf(stderr, "Invalid query type: %s\n", s);
	exit(2);
	/*NOTREACHED*/
}

static int
parseclass(const char *s) {
	int class, success;

	class = sym_ston(__p_class_syms, s, &success);
	if (success)
		return (class);
	if (atoi(s))
		return (atoi(s));
	fprintf(stderr, "Invalid query class: %s\n", s);
	exit(2);
	/*NOTREACHED*/
}

static void
printanswer(const struct hostent *hp) {
	struct in_addr **hptr;
	char **cp;

	printf("Name: %s\n", hp->h_name);
	printf("Address:");
	for (hptr = (struct in_addr **)hp->h_addr_list; *hptr; hptr++)
		printf(" %s", inet_ntoa(**hptr));
	printf("\nAliases:");
	for (cp = hp->h_aliases; cp && *cp && **cp; cp++)
		printf(" %s", *cp);
	printf("\n\n");
}

static void
hperror(int errnum) {
	switch(errnum) {
	case HOST_NOT_FOUND:
		fprintf(stderr, "Host not found.\n");
		break;
	case TRY_AGAIN:
		fprintf(stderr, "Host not found, try again.\n");
		break;
	case NO_RECOVERY:
		fprintf(stderr, "No recovery, Host not found.\n");
		break;
	case NO_ADDRESS:
		fprintf(stderr,
			"There is an entry for this host, but it doesn't have "
			);
		switch (gettype) {
		case ns_t_a:
			fprintf(stderr, "an Internet address.\n");
			break;
		case ns_t_ns:
			fprintf(stderr, "a Name Server.\n");
			break;
		case ns_t_md:
			fprintf(stderr, "a Mail Destination.\n");
			break;
		case ns_t_mf:
			fprintf(stderr, "a Mail Forwarder.\n");
			break;
		case ns_t_cname:
			fprintf(stderr, "a Canonical Name.\n");
			break;
		case ns_t_soa:
			fprintf(stderr, "a Start of Authority record.\n");
			break;
		case ns_t_mb:
			fprintf(stderr, "a Mailbox Domain Name.\n");
			break;
		case ns_t_mg:
			fprintf(stderr, "a Mail Group Member.\n");
			break;
		case ns_t_mr:
			fprintf(stderr, "a Mail Rename Name.\n");
			break;
		case ns_t_null:
			fprintf(stderr, "a Null Resource record.\n");
			break;
		case ns_t_wks:
			fprintf(stderr, "any Well Known Service information.\n");
			break;
		case ns_t_ptr:
			fprintf(stderr, "a Pointer record.\n");
			break;
		case ns_t_hinfo:
			fprintf(stderr, "any Host Information.\n");
			break;
		case ns_t_minfo:
			fprintf(stderr, "any Mailbox Information.\n");
			break;
		case ns_t_mx:
			fprintf(stderr, "a Mail Exchanger record.\n");
			break;
		case ns_t_txt:
			fprintf(stderr, "a Text record.\n");
			break;
		case ns_t_rp:
			fprintf(stderr, "a Responsible Person.\n");
			break;
		case ns_t_srv:
			fprintf(stderr, "a Server Selector.\n");
			break;
		case ns_t_naptr:
			fprintf(stderr, "a URN Naming Authority.\n");
			break;
		default:
			fprintf(stderr, "the information you requested.\n");
			break;
		}
		break;
	}
}

static int
addrinfo(struct in_addr addr) {
	u_int32_t ha = ntohl(addr.s_addr);
	char name[NS_MAXDNAME];

	sprintf(name, "%u.%u.%u.%u.IN-ADDR.ARPA.",
		(ha) & 0xff,
		(ha >> 8) & 0xff,
		(ha >> 16) & 0xff,
		(ha >> 24) & 0xff);
	return (getinfo(name, NULL, ns_t_ptr));
}

static int
gethostinfo(char *name) {
	char *cp, **domain;
	char tmp[NS_MAXDNAME];
	const char *tp;
	int hp, nDomain;
	int asis = 0;
	u_int n;

	if (strcmp(name, ".") == 0)
		return (getdomaininfo(name, NULL));
	for (cp = name, n = 0; *cp; cp++)
		if (*cp == '.')
			n++;
	if (n && cp[-1] == '.') {
		if (cp[-1] == '.')
			cp[-1] = 0;
		hp = getdomaininfo(name, (char *)NULL);
		if (cp[-1] == 0)
			cp[-1] = '.';
		return (hp);
	}
	if (n == 0 && (tp = res_hostalias(&res, name, tmp, sizeof tmp))) {
	        if (verbose)
		    printf("Aliased to \"%s\"\n", tp);
		res.options |= RES_DEFNAMES;	  
		return (getdomaininfo(tp, (char *)NULL));
	}
	if (n >= res.ndots) {
		asis = 1;
		if (verbose)
		    printf("Trying null domain\n");
		hp = getdomaininfo(name, (char*)NULL);
		if (hp)
			return (hp);
	}
	for (domain = res.dnsrch; *domain; domain++) {
		if (verbose)
			printf("Trying domain \"%s\"\n", *domain);
		hp = getdomaininfo(name, *domain);
		if (hp)
			return (hp);
	}
	if (res.res_h_errno != HOST_NOT_FOUND || (res.options & RES_DNSRCH) == 0)
		return (0);
	if (!asis)
		return (0);
	if (verbose)
		printf("Trying null domain\n");
	return (getdomaininfo(name, (char *)NULL));
}

static int
getdomaininfo(const char *name, const char *domain) {
	int val1, val2;

	if (gettype)
		return (getinfo(name, domain, gettype));
	else {
		val1 = getinfo(name, domain, gettype=ns_t_a);
		if (cname || verbose)
			return (val1);
		val2 = getinfo(name, domain, gettype=ns_t_mx);
		return (val1 || val2);
	}
}

static int
getinfo(const char *name, const char *domain, int type) {
	HEADER *hp;
	u_char *eom, *bp, *cp;
	querybuf buf, answer;
	int n, n1, i, j, nmx, ancount, nscount, arcount, qdcount, buflen;
	u_short pref, class;
	char host[NS_MAXDNAME];

	if (domain == NULL)
		sprintf(host, "%.*s", NS_MAXDNAME, name);
	else
		sprintf(host, "%.*s.%.*s",
			NS_MAXDNAME, name, NS_MAXDNAME, domain);

	n = res_nmkquery(&res, QUERY, host, getclass, type, NULL, 0, NULL,
			 buf.qb2, sizeof buf);
	if (n < 0) {
		if (res.options & RES_DEBUG)
			printf("res_nmkquery failed\n");
		res.res_h_errno = NO_RECOVERY;
		return (0);
	}
	n = res_nsend(&res, buf.qb2, n, answer.qb2, sizeof answer);
	if (n < 0) {
		if (res.options & RES_DEBUG)
			printf("res_nsend failed\n");
		res.res_h_errno = TRY_AGAIN;
		return (0);
	}
	eom = answer.qb2 + n;
	return (printinfo(&answer, eom, ns_t_any, 0));
}

static int
printinfo(const querybuf *answer, const u_char *eom, int filter, int isls) {
	int n, n1, i, j, nmx, ancount, nscount, arcount, qdcount, buflen, savesigchase;
	u_short pref, class;
	const u_char *bp, *cp;
	const HEADER *hp;

	/*
	 * Find first satisfactory answer.
	 */
	hp = (HEADER *) answer;
	ancount = ntohs(hp->ancount);
	qdcount = ntohs(hp->qdcount);
	nscount = ntohs(hp->nscount);
	arcount = ntohs(hp->arcount);
	if (res.options & RES_DEBUG || (verbose && isls == 0))
		printf("rcode = %d (%s), ancount=%d\n", 
		       hp->rcode, DecodeError(hp->rcode), ancount);
	if (hp->rcode != NOERROR || (ancount+nscount+arcount) == 0) {
		switch (hp->rcode) {
		case NXDOMAIN:
			res.res_h_errno = HOST_NOT_FOUND;
			return (0);
		case SERVFAIL:
			res.res_h_errno = TRY_AGAIN;
			return (0);
			case NOERROR:
				res.res_h_errno = NO_DATA;
				return (0);
			case FORMERR:
			case NOTIMP:
			case REFUSED:
				res.res_h_errno = NO_RECOVERY;
				return (0);
		}
		return (0);
	}
	bp = hostbuf;
	nmx = 0;
	buflen = sizeof(hostbuf);
	cp = answer->qb2 + HFIXEDSZ;
	if (qdcount > 0) {
		while (qdcount-- > 0) {
			n = dn_skipname(cp, eom);
			if (n < 0) {
				printf("Form error.\n");
				return (0);
			}
			cp += n + QFIXEDSZ;
			if (cp > eom) {
				printf("Form error.\n");
				return (0);
			}
		}
	}
	if (ancount) {
		if (!hp->aa)
			if (verbose && isls == 0)
				printf(
				 "The following answer is not authoritative:\n"
				       );
		if (!hp->ad)
		    if (verbose && isls == 0)
			printf("The following answer is not verified as authentic by the server:\n");
		while (--ancount >= 0 && cp && cp < eom)
			cp = pr_rr(cp, answer->qb2, stdout, filter);
	}
	if (!verbose)
		return (1);

	/* don't chase signatures for non-answer stuff */

	savesigchase = sigchase;
	sigchase = 0;

	if (nscount) {
		printf("For authoritative answers, see:\n");
		while (--nscount >= 0 && cp && cp < eom)
			cp = (u_char *)pr_rr(cp, answer->qb2, stdout, filter);
	}
	if (arcount) {
		printf("Additional information:\n");
		while (--arcount >= 0 && cp && cp < eom)
			cp = (u_char *)pr_rr(cp, answer->qb2, stdout, filter);
	}

	/* restore sigchase value */
	
	sigchase = savesigchase;

	return (1);
}

void print_hex_field (u_int8_t field[], int length, int width, char *pref)
{
	/* Prints an arbitrary bit field, from one address for some number of
		bytes.  Output is formatted via the width, and includes the raw
		hex value and (if printable) the printed value underneath.  "pref"
		is a string used to start each line, e.g., "   " to indent.

		This is very useful in gdb to see what's in a memory field.
	*/
	int		i, start, stop;

	start=0;
	do
	{
		stop=(start+width)<length?(start+width):length;
		printf ("%s", pref);
 		for (i = start; i < stop; i++)
			printf ("%02x ", (u_char) field[i]);
		printf ("\n");

		printf ("%s", pref);
		for (i = start; i < stop; i++)
			if (isprint(field[i]))
				printf (" %c ", (u_char) field[i]);
			else
				printf ("   ");
			printf ("\n");

		start = stop;
	} while (start < length);
}

void memswap (void *s1, void *s2, size_t n)
{
	void *tmp;

	tmp = malloc(n);
	if (!tmp) {
		printf ("Out of memory\n");
		exit (1);
	}
		
	memcpy(tmp, s1, n);
	memcpy(s1, s2, n);
	memcpy(s2, tmp, n);

	free (tmp);
}

void print_hex (u_int8_t field[], int length)
{
	/* Prints the hex values of a field...not as pretty as the print_hex_field.
	*/
	int		i, start, stop;

	start=0;
	do
	{
		stop=length;
 		for (i = start; i < stop; i++)
			printf ("%02x ", (u_char) field[i]);
		start = stop;
		if (start < length) printf ("\n");
	} while (start < length);
}

/*
 * Print resource record fields in human readable form.
 */
static const u_char *
pr_rr(const u_char *cp, const u_char *msg, FILE *file, int filter) {
	int type, class, dlen, n, c, proto, ttl;
	struct in_addr inaddr;
	u_char in6addr[NS_IN6ADDRSZ];
	const u_char *savecp = cp;
	const u_char *cp1;
	struct protoent *protop;
	struct servent *servp;
	char punc = ' ';
	int doprint;
	char name[NS_MAXDNAME];
	char thisdomain[NS_MAXDNAME];
	char tmpbuf[sizeof "ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255"];
	u_char canonrr[MY_PACKETSZ];
	size_t canonrr_len = 0;

	if ((cp = (u_char *)pr_cdname(cp, msg, name, sizeof(name))) == NULL)
		return (NULL);			/* compression error */
	strcpy(thisdomain, name);

	type = ns_get16(cp);
	cp += INT16SZ;

	class = ns_get16(cp);
	cp += INT16SZ;

	ttl = ns_get32(cp);
	cp += INT32SZ;

	if (filter == type || filter == ns_t_any ||
	    (filter == ns_t_a && (type == ns_t_ptr || type == ns_t_ns)))
		doprint = 1;
	else
		doprint = 0;

	if (doprint) {
		if (verbose)
			fprintf(file, "%s\t%d%s\t%s",
				name, ttl, pr_class(class), pr_type(type));
		else
			fprintf(file, "%s%s %s",
				name, pr_class(class), pr_type(type));
		if (verbose)
			punc = '\t';
		else
			punc = ' ';
	}
	dlen = ns_get16(cp);
	cp += INT16SZ;
	cp1 = cp;

	/*
	 * Print type specific data, if appropriate.
	 */
	switch (type) {
	case ns_t_a:
		memcpy(&inaddr, cp, NS_INADDRSZ);
	        if (doprint)
			fprintf(file,"%c%s", punc, inet_ntoa(inaddr));
		cp += dlen;
		break;
	case ns_t_aaaa:
		memcpy(in6addr, cp, NS_IN6ADDRSZ);
	        if (doprint) {
			if (inet_ntop(AF_INET6, in6addr, tmpbuf,
				      sizeof tmpbuf) != NULL)
				fprintf(file,"%c%s", punc, tmpbuf);
			else
				fprintf(file,"%c???", punc);
		}
		cp += dlen;
		break;
	case ns_t_cname:
		if (dn_expand(msg, msg + 512, cp, cnamebuf, 
			      sizeof(cnamebuf)) >= 0)
			cname = cnamebuf;				
	case ns_t_mb:
	case ns_t_mg:
	case ns_t_mr:
	case ns_t_ns:
	case ns_t_ptr:
	{
		const u_char *startrdata = cp;
		u_char cdname[NS_MAXCDNAME];

		cp = (u_char *)pr_cdname(cp, msg, name, sizeof name);
		if (doprint)
			fprintf(file, "%c%s", punc, name);

		/* Extract DNSSEC canonical RR. */

		n = ns_name_unpack(msg, msg+MY_PACKETSZ, startrdata,
				   cdname, sizeof cdname);
		if (n >= 0)
			n = ns_name_ntol(cdname, cdname, sizeof cdname);
		if (n >= 0) {
			/* Copy header. */
			memcpy(canonrr, cp1 - NS_HEADERDATA_SIZE, NS_HEADERDATA_SIZE);
			/* Overwrite length field. */
			ns_put16(n, canonrr + NS_HEADERDATA_SIZE - NS_INT16SZ);
			/* Copy unpacked name. */
			memcpy(canonrr + NS_HEADERDATA_SIZE, cdname, n);
			canonrr_len = NS_HEADERDATA_SIZE + n;
		}
		break;
	}

	case ns_t_hinfo:
	case ns_t_isdn:
		{
			const u_char *cp2 = cp + dlen;
			n = *cp++;
			if (n != 0) {
				if (doprint)
					fprintf(file,"%c%.*s", punc, n, cp);
				cp += n;
			}
			if ((cp < cp2) && (n = *cp++)) {
				if (doprint)
					fprintf(file,"%c%.*s", punc, n, cp);
				cp += n;
			} else if (type == ns_t_hinfo)
				if (doprint)
					fprintf(file,
					  "\n; *** Warning *** OS-type missing"
						);
		}
		break;

	case ns_t_soa:
	{
		const u_char *startname = cp;
		u_char cdname[NS_MAXCDNAME];

		cp = (u_char *)pr_cdname(cp, msg, name, sizeof name);
		if (doprint)
			fprintf(file, "\t%s", name);

		n = ns_name_unpack(msg, msg + 512, startname,
				   cdname, sizeof cdname);
		if (n >= 0)
			n = ns_name_ntol(cdname, cdname, sizeof cdname);
		if (n >= 0) {
			/* Copy header. */
			memcpy(canonrr, cp1 - NS_HEADERDATA_SIZE, NS_HEADERDATA_SIZE);
			/* Copy expanded name. */
			memcpy(canonrr + NS_HEADERDATA_SIZE, cdname, n);
			canonrr_len = NS_HEADERDATA_SIZE + n;
		}

		startname = cp;
		cp = (u_char *)pr_cdname(cp, msg, name, sizeof name);
		if (doprint)
			fprintf(file, " %s", name);

		n = ns_name_unpack(msg, msg + 512, startname,
				   cdname, sizeof cdname);
		if (n >= 0)
			n = ns_name_ntol(cdname, cdname, sizeof cdname);
		if (n >= 0) {
			/* Copy expanded name. */
			memcpy(canonrr + canonrr_len, cdname, n);
			canonrr_len += n;
			/* Copy rest of SOA. */
			memcpy(canonrr + canonrr_len, cp, 5 * INT32SZ);
			canonrr_len += 5 * INT32SZ;
			/* Overwrite length field. */
			ns_put16(canonrr_len - NS_HEADERDATA_SIZE,
				 canonrr + NS_HEADERDATA_SIZE - NS_INT16SZ);
		}

		if (doprint)
			fprintf(file, "(\n\t\t\t%ld\t;serial (version)",
				ns_get32(cp));
		cp += INT32SZ;
		if (doprint)
			fprintf(file, "\n\t\t\t%ld\t;refresh period",
				ns_get32(cp));
		cp += INT32SZ;
		if (doprint)
			fprintf(file,
				"\n\t\t\t%ld\t;retry refresh this often",
				ns_get32(cp));
		cp += INT32SZ;
		if (doprint)
			fprintf(file, "\n\t\t\t%ld\t;expiration period",
				ns_get32(cp));
		cp += INT32SZ;
		if (doprint)
			fprintf(file, "\n\t\t\t%ld\t;minimum TTL\n\t\t\t)",
				ns_get32(cp));
		cp += INT32SZ;
		break;
	}
	case ns_t_mx:
	case ns_t_afsdb:
	case ns_t_rt:
	{
		const u_char *startrdata = cp;
		u_char cdname[NS_MAXCDNAME];

		if (doprint) {
			if (type == ns_t_mx && !verbose)
				fprintf(file," (pri=%d) by ", ns_get16(cp));
			else if (verbose)
				fprintf(file,"\t%d ", ns_get16(cp));
			else
				fprintf(file," ");
		}
		cp += sizeof(u_short);
		cp = (u_char *)pr_cdname(cp, msg, name, sizeof(name));
		if (doprint)
			fprintf(file, "%s", name);

		n = ns_name_unpack(msg, msg+512, startrdata + sizeof(u_short),
				   cdname, sizeof cdname);
		if (n >= 0)
			n = ns_name_ntol(cdname, cdname, sizeof cdname);
		if (n >= 0) {
			/* Copy header. */
			memcpy(canonrr, cp1 - NS_HEADERDATA_SIZE,
			       NS_HEADERDATA_SIZE);
			/* Overwrite length field. */
			ns_put16(sizeof(u_short) + n,
				 canonrr + NS_HEADERDATA_SIZE - NS_INT16SZ);
			/* Copy u_short. */
			memcpy(canonrr + NS_HEADERDATA_SIZE, startrdata,
			       sizeof(u_short));
			/* Copy expanded name. */
			memcpy(canonrr + NS_HEADERDATA_SIZE + sizeof(u_short),
			       cdname, n);
			canonrr_len = NS_HEADERDATA_SIZE + sizeof(u_short) + n;
		}
		break;
	}

	case ns_t_srv:
		if (doprint)
			fprintf(file," %d", ns_get16(cp));
		cp += sizeof(u_short);
		if (doprint)
			fprintf(file," %d", ns_get16(cp));
		cp += sizeof(u_short);
		if (doprint)
			fprintf(file," %d", ns_get16(cp));
		cp += sizeof(u_short);
		cp = (u_char *)pr_cdname(cp, msg, name, sizeof(name));
		if (doprint)
			fprintf(file,"%s",name);
		break;

	case ns_t_naptr:
		/* order */
		if (doprint)
			fprintf(file, " %d", ns_get16(cp));
		cp += sizeof(u_short);
		/* preference */
		if (doprint)
			fprintf(file, " %d", ns_get16(cp));
		cp += NS_INT16SZ;
		/* Flags */
		n = *cp++;
		if (doprint) {
			if (n)
				fprintf(file, "%c%.*s", punc, n, cp);
			else 
				fprintf(file, "%c\"\"",punc);
		}
		cp += n;
		/* Service */
		n = *cp++;
		if (doprint) {
			if (n)
				fprintf(file, "%c%.*s", punc, n, cp);
			else 
				fprintf(file,"%c\"\"",punc);
		}
		cp += n;
		/* Regexp  */
		n = *cp++;
		if (doprint) {
			if (n)
				fprintf(file, "%c%.*s", punc, n, cp);
			else 
				fprintf(file, "%c\"\"",punc);
		}
		cp += n;
		/* replacement  */
		cp = (u_char *)pr_cdname(cp, msg, name, sizeof(name));
		if (doprint)
			fprintf(file, "%s", name);
		break;

	case ns_t_minfo:
	case ns_t_rp:
		cp = (u_char *)pr_cdname(cp, msg, name, sizeof name);
		if (doprint) {
			if (type == ns_t_rp) {
				char *p;

				p = strchr(name, '.');
				if (p != NULL)
					*p = '@';
			}
			fprintf(file, "%c%s", punc, name);
		}
		cp = (u_char *)pr_cdname(cp, msg, name, sizeof(name));
		if (doprint)
			fprintf(file, " %s", name);
		break;

	case ns_t_x25:
		n = *cp++;
		if (n != 0) {
			if (doprint)
				fprintf(file, "%c%.*s", punc, n, cp);
			cp += n;
		}
		break;

	case ns_t_txt:
		{
			int n, j;
			const u_char *end = cp + dlen;
		 
			while (cp < end) {
				if (doprint)
					(void) fputs(" \"", file);
				n = *cp++;
				if (n != 0)
					for (j = n; j > 0 && cp < end ; j --) {
						if (doprint) {
							if (*cp == '\n' ||
							    *cp == '"' ||
							    *cp == '\\')
								putc('\\',
								     file);
							putc(*cp, file);
						}
						cp++;
					}
				if (doprint)
					putc('"', file);
			}
		}
		break;

	case ns_t_wks:
		if (dlen < INT32SZ + 1)
			break;
		memcpy(&inaddr, cp, INADDRSZ);
		cp += INT32SZ;
		proto = *cp++;
		protop = getprotobynumber(proto);
		if (doprint) {
			if (protop)
				fprintf(file, "%c%s %s", punc,
					inet_ntoa(inaddr), protop->p_name);
			else
				fprintf(file, "%c%s %d", punc,
					inet_ntoa(inaddr), proto);
		}
		n = 0;
		while (cp < cp1 + dlen) {
			c = *cp++;
			do {
 				if (c & 0200) {
					servp = NULL;
					if (protop)
						servp = getservbyport(htons(n),
								      protop->
								       p_name);
					if (doprint) {
						if (servp)
							fprintf(file, " %s",
								servp->s_name);
						else
							fprintf(file, " %d",
								n);
					}
				}
				c <<= 1;
			} while (++n & 07);
		}
		break;
	case ns_t_nxt:
	{
		const u_char *startrdata = cp;
		u_char cdname[NS_MAXCDNAME];
		size_t bitmaplen;

		cp = (u_char *) pr_cdname(cp, msg, name, sizeof name);
		if (doprint)
			fprintf(file, "%c%s", punc, name);
		bitmaplen = dlen - (cp - startrdata);

		/* extract dnssec canonical rr */

		n = ns_name_unpack(msg, msg+MY_PACKETSZ, startrdata,
				   cdname, sizeof cdname);
		if (n >= 0)
			n = ns_name_ntol(cdname, cdname, sizeof cdname);
		if (n >= 0) {
			/* Copy header. */
			memcpy(canonrr, cp1 - NS_HEADERDATA_SIZE,
			       NS_HEADERDATA_SIZE);
			/* Overwrite length field. */
			ns_put16(n + bitmaplen, 
				 canonrr + NS_HEADERDATA_SIZE - NS_INT16SZ);
			/* Copy expanded name. */
			memcpy(canonrr + NS_HEADERDATA_SIZE, cdname, n);
			/* Copy type bit map. */
			memcpy(canonrr + NS_HEADERDATA_SIZE + n, cp,
			       bitmaplen);
			canonrr_len = NS_HEADERDATA_SIZE + n + bitmaplen;
		}
		cp += bitmaplen;
		break;
	}
	case ns_t_sig:
	{
		int tc;
		const u_char *origttl;

		/* type covered */
		tc = ns_get16(cp);
		if (doprint && verbose)
			fprintf(file, "%c%s", punc, sym_ntos(__p_type_syms, tc, NULL));
		cp += sizeof(u_short);
		/* algorithm */
		if (doprint && verbose)
			fprintf(file, " %d", *cp);
		cp++;
		/* labels */
		if (doprint && verbose)
			fprintf(file, " %d", *cp);
		cp++;
		/* original ttl */
		origttl = cp;
		if (doprint && verbose)
			fprintf(file, " %lu", ns_get32(cp));
		cp += INT32SZ;
		/* signature expiration */
		if (doprint && verbose)
			fprintf(file, " %lu", ns_get32(cp));
		cp += INT32SZ;
		/* time signed */
		if (doprint && verbose)
			fprintf(file, " %lu", ns_get32(cp));
		cp += INT32SZ;
		/* key footprint */
		if (doprint && verbose)
			fprintf(file, " %d", ns_get16(cp));
		cp += sizeof(u_short);
		/* signer's name */
		cp = (u_char *)pr_cdname(cp, msg, name, sizeof(name));
		if (doprint && verbose)
			fprintf(file, " %s", name);
		else if (doprint && !verbose)
			fprintf (file, " %s for type %s", name, 
				 sym_ntos(__p_type_syms, tc, NULL));
		/* signature */
		{
			char str[MY_PACKETSZ];
			size_t len = cp1-cp+dlen;
			
			b64_ntop (cp, len, str, MY_PACKETSZ-1);

			if (sigchase && !(chase_step & SD_SIG) && 
			    strcmp (chase_domain, thisdomain) == 0 && 
			    chase_class == class & chase_type == tc)
			{
				u_char cdname[NS_MAXCDNAME];

				if (doprint && !verbose)
					fprintf(file, " (chasing key)");
				
				strcpy(chase_signer, name);

				memcpy(&chase_sigorigttl[0], origttl, 
				       NS_INT32SZ);

				n = ns_name_ntol(cp1 + SIG_RDATA_BY_NAME, 
						 cdname, sizeof cdname);
				if (n >= 0) {
					memcpy(chase_sigrdata, cp1,
					       SIG_RDATA_BY_NAME);
					memcpy(chase_sigrdata + SIG_RDATA_BY_NAME,
					       cdname, n);
					chase_sigrdata_len += SIG_RDATA_BY_NAME + n;
					memcpy(chase_signature, cp, len);
					chase_signature_len = len;

					chase_step |= SD_SIG;
				}
			} else if (sigchase) {
				chase_step |= SD_BADSIG;
			}

			cp += len;
			if (doprint && verbose)
				fprintf (file, " %s", str);
		}
		break;
	}
	case ns_t_key:
		/* flags */
		if (doprint && verbose)
			fprintf(file, "%c%d", punc, ns_get16(cp));
		cp += sizeof(u_short);
		/* protocol */
		if (doprint && verbose)
			fprintf(file, " %d", *cp);
		cp++;
		/* algorithm */
		n = *cp;
		if (doprint && verbose)
			fprintf(file, " %d", *cp);
		cp++;
		switch (n) {
		case 1: /* MD5/RSA */
		{
			char str[MY_PACKETSZ];
			size_t len = cp1-cp+dlen;

			b64_ntop (cp, len, str, MY_PACKETSZ-1);
			cp += len;
			
			if (doprint && verbose)
				fprintf (file, " %s", str);
			break;
		}
		
		default:
			fprintf (stderr, "Unknown algorithm %d\n", n);
			cp = cp1 + dlen;
			break;
		}
		
		if (sigchase && (chase_step & (SD_SIG|SD_RR)) && 
		    strcmp (getdomain, name) == 0 && 
		    getclass == class & gettype == type)
		{
			DST_KEY *dstkey;
			int rc, len, i, j;
			
			/* convert dnskey to dstkey */
		
			dstkey = dst_dnskey_to_key (name, cp1, dlen);
		
			/* fix ttl in rr */
			
			for (i = 0; i < NUMRR && chase_rr[i].len; i++)
			{
				len = dn_skipname(chase_rr[i].data, 
						  chase_rr[i].data + 
						  chase_rr[i].len);
				if (len>=0) 
					memcpy(chase_rr[i].data + len + NS_INT16SZ +
					       NS_INT16SZ,
					       &chase_sigorigttl, INT32SZ);
			}

			/* sort rr's (qsort() is too slow) */
			
			for (i = 0; i < NUMRR && chase_rr[i].len; i++)
				for (j = i + 1; i < NUMRR && chase_rr[j].len; j++)
					if (memcmp(chase_rr[i].data, chase_rr[j].data, MY_PACKETSZ) > 0)
						memswap(&chase_rr[i], &chase_rr[j], sizeof(rrstruct));
			
			/* append rr's to sigrdata */

			for (i = 0; i < NUMRR && chase_rr[i].len; i++)
			{
				memcpy (chase_sigrdata + chase_sigrdata_len, 
					chase_rr[i].data, chase_rr[i].len);
				chase_sigrdata_len += chase_rr[i].len;
			}

			/* print rr-data and signature */

			if (verbose) {
				print_hex_field(chase_sigrdata,
						chase_sigrdata_len,
						21,"DATA: ");
				print_hex_field(chase_signature,
						chase_signature_len,
						21,"SIG: ");
			}
			
			/* do the works */

			if (dstkey)
				rc = dst_verify_data(SIG_MODE_ALL, dstkey, NULL,
						     chase_sigrdata, 
						     chase_sigrdata_len,
						     chase_signature, 
						     chase_signature_len);
			else
				rc = 1;

			dst_free_key(dstkey);

			if (verbose)
			{
				fprintf(file, "\nVerification %s", rc == 0 ? 
					"was SUCCESSFULL" :
					"FAILED");
			}
			else
			{
				fprintf (file, 
					 " that %s verify our %s "
					 "record(s) on %s", 
					 rc == 0 ? "successfully" :
					 "DOES NOT", 
					 sym_ntos(__p_type_syms, chase_type, 
						  NULL),
					 chase_domain);
			}

			if (rc == 0)
			{
				strcpy (chase_lastgoodkey, name);
			}
			else
			{
				/* don't trace further after a failure */
				sigchase = 0; 
			}

			chase_step = 0;
			chase_signature_len = 0;
			chase_sigrdata_len = 0;
			memset(chase_sigorigttl, 0, NS_INT32SZ);
			memset(chase_rr, 0, sizeof(chase_rr));
			chase_rr_num = 0;
		}
		break;

	default:
		if (doprint)
			fprintf(file, "%c???", punc);
		cp += dlen;
		break;
	}
	if (cp != cp1 + dlen)
		fprintf(file, "packet size error (%p != %p)\n",
			cp, cp1 + dlen);

	if (sigchase && !(chase_step & SD_SIG) && 
	    strcmp (getdomain, thisdomain) == 0 && getclass == class && 
	    gettype == type && type != ns_t_sig)
	{
		u_char cdname[NS_MAXCDNAME];

		if (doprint && !verbose)
			fprintf (file, " (chasing signature %d)", sigchase-1);

		/* unpack rr */

		n = ns_name_unpack(msg, msg + MY_PACKETSZ, savecp,
				   cdname, sizeof cdname);
		if (n >= 0)
			n = ns_name_ntol(cdname, cdname, sizeof cdname);
		if (n >= 0) {
			memcpy(chase_rr[chase_rr_num].data, cdname, n);
			memcpy(chase_rr[chase_rr_num].data + n,
			       canonrr_len ? canonrr : cp1 - NS_HEADERDATA_SIZE,
			       canonrr_len ? canonrr_len : dlen + NS_HEADERDATA_SIZE);
			chase_rr[chase_rr_num].len = 
				n + (canonrr_len != 0 ? canonrr_len : 
				     dlen + NS_HEADERDATA_SIZE);
			
			strcpy(chase_domain, getdomain);
			chase_class = class;
			chase_type = type;
			chase_step |= SD_RR;
			chase_rr_num++;
		}
	}

	if (doprint)
		fprintf(file, "\n");

	return (cp);
}

/*
 * Return a string for the type.  A few get special treatment when
 * not in verbose mode, to make the program more chatty and easier to
 * understand.
 */
static const char *
pr_type(int type) {
	if (!verbose) switch (type) {
	case ns_t_a:
		return ("has address");
	case ns_t_cname:
		return ("is a nickname for");
	case ns_t_mx:
		return ("mail is handled");
	case ns_t_txt:
		return ("descriptive text");
	case ns_t_sig:
                return ("has a signature signed by");
	case ns_t_key:
                return ("has a key");
	case ns_t_nxt:
		return ("next valid name");
	case ns_t_afsdb:
		return ("DCE or AFS service from");
	}
	if (verbose)
		return (sym_ntos(__p_type_syms, type, NULL));
	else
		return (sym_ntop(__p_type_syms, type, NULL));
}

/*
 * Return a mnemonic for class
 */
static const char *
pr_class(int class) {
	static char spacestr[20];

	if (!verbose) switch (class) {
	case ns_c_in:		/* internet class */
		return ("");
	case ns_c_hs:		/* hesiod class */
		return ("");
	}

	spacestr[0] = ' ';
	strcpy(&spacestr[1], p_class(class));
	return (spacestr);
}

static const u_char *
pr_cdname(const u_char *cp, const u_char *msg, char *name, int namelen) {
	int n = dn_expand(msg, msg + MY_PACKETSZ, cp, name, namelen - 2);

	if (n < 0)
		return (NULL);
	if (name[0] == '\0') {
		name[0] = '.';
		name[1] = '\0';
	}
	return (cp + n);
}

static int
ListHosts(char *namePtr, int queryType) {
	querybuf buf, answer;
	struct sockaddr_in sin;
	const HEADER *headerPtr;
	const struct hostent *hp;
	enum { NO_ERRORS, ERR_READING_LEN, ERR_READING_MSG, ERR_PRINTING }
		error = NO_ERRORS;

	int msglen, amtToRead, numRead, i, len, dlen, type, nscount, n;
	int numAnswers = 0, soacnt = 0, result = 0;
	u_char tmp[NS_INT16SZ];
	char name[NS_MAXDNAME], dname[2][NS_MAXDNAME], domain[NS_MAXDNAME];
	u_char *cp, *nmp, *eom;

	/* Names and addresses of name servers to try. */
	char nsname[NUMNS][NS_MAXDNAME];
	int nshaveaddr[NUMNS];
	struct in_addr nsipaddr[NUMNSADDR];
	int numns, numnsaddr, thisns;

	/*
	 * Normalize to not have trailing dot.  We do string compares below
	 * of info from name server, and it won't have trailing dots.
	 */
	i = strlen(namePtr);
	if (namePtr[i-1] == '.')
		namePtr[i-1] = 0;

	if (server_specified) {
		memcpy(&nsipaddr[0], &res.nsaddr.sin_addr, NS_INADDRSZ);
		numnsaddr = 1;
	} else {
		/*
		 * First we have to find out where to look.  This needs a NS
		 * query, possibly followed by looking up addresses for some
		 * of the names.
		 */
		msglen = res_nmkquery(&res, ns_o_query, namePtr,
				      ns_c_in, ns_t_ns,
				      NULL, 0, NULL,
				      buf.qb2, sizeof buf);
		if (msglen < 0) {
			printf("res_nmkquery failed\n");
			return (ERROR);
		}

		msglen = res_nsend(&res, buf.qb2, msglen,
				   answer.qb2, sizeof answer);
		if (msglen < 0) {
			printf("Cannot find nameserver -- try again later\n");
			return (ERROR);
		}
		if (res.options & RES_DEBUG || verbose)
			printf("rcode = %d (%s), ancount=%d\n", 
			       answer.qb1.rcode, DecodeError(answer.qb1.rcode),
			       ntohs(answer.qb1.ancount));

		/*
		 * Analyze response to our NS lookup.
		 */

		nscount = ntohs(answer.qb1.ancount) +
			  ntohs(answer.qb1.nscount) +
			  ntohs(answer.qb1.arcount);

		if (answer.qb1.rcode != NOERROR || nscount == 0) {
			switch (answer.qb1.rcode) {
			case NXDOMAIN:
				/* Check if it's an authoritive answer */
				if (answer.qb1.aa)
					printf("No such domain\n");
				else
					printf("Unable to get information about domain -- try again later.\n");
				break;
			case SERVFAIL:
				printf("Unable to get information about that domain -- try again later.\n");
				break;
			case NOERROR:
				printf("That domain exists, but seems to be a leaf node.\n");
				break;
			case FORMERR:
			case NOTIMP:
			case REFUSED:
				printf("Unrecoverable error looking up domain name.\n");
				break;
			}
			return (0);
		}

		cp = answer.qb2 + HFIXEDSZ;
		eom = answer.qb2 + msglen;
		if (ntohs(answer.qb1.qdcount) > 0) {
			n = dn_skipname(cp, eom);
			if (n < 0) {
				printf("Form error.\n");
				return (ERROR);
			}
			cp += n + QFIXEDSZ;
			if (cp > eom) {
				printf("Form error.\n");
				return (ERROR);
			}
		}

		numns = 0;
		numnsaddr = 0;

		/*
		 * Look at response from NS lookup for NS and A records.
		 */

		for ((void)NULL; nscount; nscount--) {
			cp += dn_expand(answer.qb2, answer.qb2 + msglen, cp,
					domain, sizeof(domain));
			if (cp + 3 * INT16SZ + INT32SZ > eom) {
				printf("Form error.\n");
				return (ERROR);
			}
			type = ns_get16(cp);
			cp += INT16SZ + INT16SZ + INT32SZ;
			dlen = ns_get16(cp);
			cp += INT16SZ;
			if (cp + dlen > eom) {
				printf("Form error.\n");
				return (ERROR);
			}
			if (type == ns_t_ns) {
				if (dn_expand(answer.qb2, eom,
					      cp, name, sizeof(name)) >= 0) {
					if (numns < NUMNS &&
					    ns_samename((char *)domain, 
							namePtr) == 1) {
						for (i = 0; i < numns; i++)
							if (ns_samename(
								     nsname[i],
								   (char *)name
								       ) == 1)
								/* duplicate */
								break;
						if (i >= numns) {
							strncpy(nsname[numns],
								(char *)name,
								sizeof(name));
							nshaveaddr[numns] = 0;
							numns++;
						}
					}
				}
			} else if (type == ns_t_a) {
				if (numnsaddr < NUMNSADDR)
					for (i = 0; i < numns; i++) {
						if (ns_samename(nsname[i],
							       (char *)domain)
						    == 1) {
							nshaveaddr[i]++;
							memcpy(
							  &nsipaddr[numnsaddr],
							  cp, NS_INADDRSZ);
							numnsaddr++;
							break;
						}
					}
			}
			cp += dlen;
		}

		/*
		 * Usually we'll get addresses for all the servers in the
		 * additional info section.  But in case we don't, look up
		 * their addresses.
		 */

		for (i = 0; i < numns; i++) {
			if (nshaveaddr[i] == 0) {
				struct in_addr **hptr;
				int numaddrs = 0;

				hp = gethostbyname(nsname[i]);
				if (hp) {
					for (hptr = (struct in_addr **)
					     	hp->h_addr_list;
					     *hptr != NULL;
					     hptr++)
						if (numnsaddr < NUMNSADDR) {
							memcpy(
							  &nsipaddr[numnsaddr],
							  *hptr, NS_INADDRSZ);
							numnsaddr++;
							numaddrs++;
						}
				}
				if (res.options & RES_DEBUG || verbose)
					printf(
				  "Found %d addresses for %s by extra query\n",
					       numaddrs, nsname[i]);
			} else if (res.options & RES_DEBUG || verbose)
				printf("Found %d addresses for %s\n",
				       nshaveaddr[i], nsname[i]);
		}
	}
	/*
	 * Now nsipaddr has numnsaddr addresses for name servers that
	 * serve the requested domain.  Now try to find one that will
	 * accept a zone transfer.
	 */
	thisns = 0;

 again:
	numAnswers = 0;
	soacnt = 0;

	/*
	 * Create a query packet for the requested domain name.
	 */
	msglen = res_nmkquery(&res, QUERY, namePtr, getclass, ns_t_axfr, NULL,
			      0, NULL, buf.qb2, sizeof buf);
	if (msglen < 0) {
		if (res.options & RES_DEBUG)
			fprintf(stderr, "ListHosts: Res_mkquery failed\n");
		return (ERROR);
	}

	memset(&sin, 0, sizeof sin);
	sin.sin_family = AF_INET;
	sin.sin_port =  htons(NAMESERVER_PORT);

	/*
	 * Set up a virtual circuit to the server.
	 */

	for ((void)NULL; thisns < numnsaddr; thisns++) {
		if ((sockFD = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
			perror("ListHosts");
			return (ERROR);
		}
		memcpy(&sin.sin_addr, &nsipaddr[thisns], NS_INADDRSZ);
		if (res.options & RES_DEBUG || verbose)
			printf("Trying %s\n", inet_ntoa(sin.sin_addr));
		if (connect(sockFD, (struct sockaddr *)&sin, sizeof(sin)) >= 0)
			break;
		if (verbose)
			perror("Connection failed, trying next server");
		close(sockFD);
		sockFD = -1;
	}	
	if (thisns >= numnsaddr) {
		printf("No server for that domain responded\n");
		if (!verbose)
			perror("Error from the last server was");
		return (ERROR);
	}

	/*
	 * Send length & message for zone transfer 
	 */

	ns_put16(msglen, tmp);
        if (write(sockFD, (char *)tmp, INT16SZ) != INT16SZ ||
	    write(sockFD, (char *)buf.qb2, msglen) != msglen) {
		perror("ListHosts");
		(void) close(sockFD);
		sockFD = -1;
		return (ERROR);
	}

	filePtr = stdout;

	for (;;) {
		/*
		 * Read the length of the response.
		 */
		cp = buf.qb2;
		amtToRead = INT16SZ;
		while (amtToRead > 0 &&
		       (numRead = read(sockFD, cp, amtToRead)) > 0) {
			cp += numRead;
			amtToRead -= numRead;
		}
		if (numRead <= 0) {
			error = ERR_READING_LEN;
			break;
		}	

		if ((len = ns_get16(buf.qb2)) == 0)
			break;	/* Protocol violation. */

		/*
		 * Read the response.
		 */

		amtToRead = len;
		cp = buf.qb2;
		while (amtToRead > 0 &&
		       (numRead = read(sockFD, cp, amtToRead)) > 0) {
			cp += numRead;
			amtToRead -= numRead;
		}
		if (numRead <= 0) {
			error = ERR_READING_MSG;
			break;
		}

		i = buf.qb1.rcode;
		if (i != NOERROR || ntohs(buf.qb1.ancount) == 0) {
			if (thisns + 1 < numnsaddr &&
			    (i == SERVFAIL || i == NOTIMP || i == REFUSED)) {
				if (res.options & RES_DEBUG || verbose)
					printf(
				     "Server failed, trying next server: %s\n",
					       i != NOERROR
					       ? DecodeError(i)
					       : "Premature end of data");
				(void) close(sockFD);
				sockFD = -1;
				thisns++;
				goto again;
			}
			printf("Server failed: %s\n", i != NOERROR
			       ? DecodeError(i) : "Premature end of data");
			break;
		}

		result = printinfo(&buf, cp, queryType, 1);
		if (! result) {
			error = ERR_PRINTING;
			break;
		}
		numAnswers++;
		cp = buf.qb2 + HFIXEDSZ;
		if (ntohs(buf.qb1.qdcount) > 0) {
			n = dn_skipname(cp, buf.qb2 + len);
			if (n < 0) {
				error = ERR_PRINTING;
				break;
			}
			cp += n + QFIXEDSZ;
		}
		nmp = cp;
		n = dn_skipname(cp, buf.qb2 + len);
		if (n < 0) {
			error = ERR_PRINTING;
			break;
		}
		cp += n;
		if (cp + INT16SZ > buf.qb2 + len) {
			error = ERR_PRINTING;
			break;
		}
		if ((ns_get16(cp) == ns_t_soa)) {
			(void) dn_expand(buf.qb2, buf.qb2 + len, nmp,
					 dname[soacnt], sizeof dname[0]);
			if (soacnt) {
				if (ns_samename(dname[0], dname[1]) == 1)
					break;
			} else
				soacnt++;
		}
        }

	(void) close(sockFD);
	sockFD = -1;

	switch (error) {
	case NO_ERRORS:
		return (SUCCESS);

	case ERR_READING_LEN:
		return (ERROR);

	case ERR_PRINTING:
		fprintf(stderr,"*** Error during listing of %s: %s\n", 
				namePtr, DecodeError(result));
		return (result);

	case ERR_READING_MSG:
		headerPtr = (HEADER *) &buf;
		fprintf(stderr,"ListHosts: error receiving zone transfer:\n");
		fprintf(stderr,
	       "  result: %s, answers = %d, authority = %d, additional = %d\n",
			p_rcode(headerPtr->rcode), 
		    	ntohs(headerPtr->ancount), ntohs(headerPtr->nscount), 
			ntohs(headerPtr->arcount));
		return (ERROR);
	default:
		return (ERROR);
	}
}

static const char *
DecodeError(int result) {
	switch(result) {
	case NOERROR: 	return ("Success");
	case FORMERR:	return ("Format error");
	case SERVFAIL:	return ("Server failed");
	case NXDOMAIN:	return ("Non-existent domain");
	case NOTIMP:	return ("Not implemented");
	case REFUSED:	return ("Query refused");
	case NO_INFO: 	return ("No information");
	case ERROR: 	return ("Unspecified error");
	case TIME_OUT: 	return ("Timed out");
	case NONAUTH: 	return ("Non-authoritative answer");
	default:	return ("BAD ERROR VALUE");
	}
	/* NOTREACHED */
}
