/*
 * Copyright (c) 1986 Regents of the University of California
 * All Rights Reserved
 *
 * Redistribution and use in source and binary forms are permitted
 * provided that this notice is preserved and that due credit is given
 * to the University of California at Berkeley. The name of the University
 * may not be used to endorse or promote products derived from this
 * software without specific prior written permission. This software
 * is provided ``as is'' without express or implied warranty.
 */

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

#if _MINIX
#include <sys/types.h>
#include <sys/ioctl.h>
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include <net/hton.h>
#include <net/netlib.h>
#include <net/gen/in.h>
#include <net/gen/inet.h>
#include <net/gen/netdb.h>
#include <net/gen/nameser.h>
#include <net/gen/resolv.h>
#include <net/gen/socket.h>
#include <net/gen/tcp.h>
#include <net/gen/tcp_io.h>

#undef ERROR
#else
#include <stdio.h>
#include <sys/types.h>
#include <arpa/nameser.h>
#include <netdb.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <resolv.h>
#include <sys/param.h>
#include <strings.h>
#include <ctype.h>
#endif

extern int h_errno;

#define NUMMX 50

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

#define NAME_LEN 256

#ifndef T_TXT
#define T_TXT 16
#endif
#ifndef NO_DATA
#define NO_DATA NO_ADDRESS
#endif
#ifndef C_HS
#define C_HS 4
#endif

FILE *filePtr;

struct state orig;
extern struct state _res;
static u8_t *cname = NULL;
int getclass = C_IN;
int gettype, getdeftype = T_A;
int verbose = 0;
int list = 0;
int server_specified = 0;

union querybuf;

int main _ARGS(( int c, char *v[] ));

static int parsetype _ARGS(( char *s ));
static int parseclass _ARGS(( char *s ));
static void hperror _ARGS(( int errno ));
static void printanswer _ARGS(( struct hostent *hp ));
static int ListHosts _ARGS(( char *namePtr, int queryType ));
static int gethostinfo _ARGS(( char *name ));
static int getdomaininfo _ARGS(( char *name, char *domain ));
static int getinfo _ARGS(( char *name, char *domain, int type ));
static int printinfo _ARGS(( union querybuf *answer, u8_t *eom, int filter, 
								int isls ));
static char *DecodeError _ARGS(( int result ));
static u8_t *pr_rr _ARGS(( u8_t *cp, u8_t *msg, FILE *file, int filter ));
static u8_t * pr_cdname _ARGS(( u8_t *cp, u8_t *msg, u8_t *name, int namelen ));
static char *pr_class _ARGS(( int class ));
static char *pr_type _ARGS(( int type ));
static int tcpip_writeall _ARGS(( int fd, char *buf, unsigned siz ));

int
main(c, v)
	char **v;
{
	char *domain;
	ipaddr_t addr;
	register struct hostent *hp;
	register char *s, *p;
	register inverse = 0;
	register waitmode = 0;
	u8_t *oldcname;
	int ncnames;
	int isaddr;

	res_init();
	_res.retrans = 5;

	if (c < 2) {
		fprintf(stderr, "Usage: host [-w] [-v] [-r] [-d] [-V] [-t querytype] [-c class] [-a] host [server]\n  -w to wait forever until reply\n  -v for verbose output\n  -r to disable recursive processing\n  -d to turn on debugging output\n  -t querytype to look for a specific type of information\n  -c class to look for non-Internet data\n  -a is equivalent to '-v -t *'\n  -V to always use a virtual circuit\n");
		exit(1);
	}
	while (c > 2 && v[1][0] == '-') {
		if (strcmp (v[1], "-w") == 0) {
			_res.retry = 1;
			_res.retrans = 15;
			waitmode = 1;
			v++;
			c--;
		}
		else if (strcmp (v[1], "-r") == 0) {
			_res.options &= ~RES_RECURSE;
			v++;
			c--;
		}
		else if (strcmp (v[1], "-d") == 0) {
			_res.options |= RES_DEBUG;
			v++;
			c--;
		}
		else if (strcmp (v[1], "-v") == 0) {
			verbose = 1;
			v++;
			c--;
		}
		else if (strcmp (v[1], "-l") == 0) {
			list = 1;
			v++;
			c--;
		}
		else if (strncmp (v[1], "-t", 2) == 0) {
			v++;
			c--;
			gettype = parsetype(v[1]);
			v++;
			c--;
		}
		else if (strncmp (v[1], "-c", 2) == 0) {
			v++;
			c--;
			getclass = parseclass(v[1]);
			v++;
			c--;
		}
		else if (strcmp (v[1], "-a") == 0) {
			verbose = 1;
			gettype = T_ANY;
			v++;
			c--;
		}		
		else if (strcmp (v[1], "-V") == 0) {
			_res.options |= RES_USEVC;
			v++;
			c--;
		}
        }
	if (c > 2) {
		s = v[2];
		server_specified++;
		
		if ((p = strchr(s, ':')) != NULL) *p++ = 0;
		isaddr = inet_aton(s, &addr);
		if (p != NULL) p[-1] = ':';
		if (!isaddr) {
		  hp = gethostbyname(s);
		  if (hp == NULL) {
		    fprintf(stderr,"Error in looking up server name:\n");
		    hperror(h_errno);
		    exit(1);
		  }
		  _res.nsaddr= *(ipaddr_t *)hp->h_addr;
		  printf("Using domain server:\n");
		  printanswer(hp);
		}
		else {
		  _res.nsaddr_list[0]= addr;
		  _res.nsport_list[0]= htons(NAMESERVER_PORT);
		  printf("Using domain server %s",
			 inet_ntoa(_res.nsaddr));
		  if (p != NULL) {
		    printf(" on port %d", atoi(p));
		    _res.nsport_list[0]= htons(atoi(p));
		  }
		  printf(":\n");
		}
	      }
	domain = v[1];
	if (strcmp (domain, ".") != 0 && inet_aton(domain, &addr)) {
	  static char ipname[sizeof("255.255.255.255.in-addr.arpa.")];
	  sprintf(ipname, "%d.%d.%d.%d.in-addr.arpa.",
	    ((unsigned char *) &addr)[3],
	    ((unsigned char *) &addr)[2],
	    ((unsigned char *) &addr)[1],
	    ((unsigned char *) &addr)[0]);
	  domain = ipname;
	  getdeftype = T_PTR;
	}

	hp = NULL;
	h_errno = TRY_AGAIN;
/*
 * we handle default domains ourselves, thank you
 */
	_res.options &= ~RES_DEFNAMES;

        if (list)
	  exit(ListHosts(domain, gettype ? gettype : getdeftype));
	oldcname = NULL;
	ncnames = 5;
	while (hp == NULL && h_errno == TRY_AGAIN) {
	  cname = NULL;
	  if (oldcname == NULL)
	    hp = (struct hostent *)gethostinfo(domain);
	  else
	    hp = (struct hostent *)gethostinfo((char *)oldcname);
	  if (cname) {
	    if (ncnames-- == 0) {
	      printf("Too many cnames.  Possible loop.\n");
	      exit(1);
	    }
	    oldcname = cname;
	    hp = NULL;
	    h_errno = TRY_AGAIN;
	    continue;
	  }
	  if (!waitmode)
	    break;
	}

	if (hp == NULL) {
	  hperror(h_errno);
	  exit(1);
	}

	exit(0);

}

static int
parsetype(s)
	char *s;
{
if (strcmp(s,"a") == 0)
  return(1);
if (strcmp(s,"ns") == 0)
  return(2);
if (strcmp(s,"md") == 0)
  return(3);
if (strcmp(s,"mf") == 0)
  return(4);
if (strcmp(s,"cname") == 0)
  return(5);
if (strcmp(s,"soa") == 0)
  return(6);
if (strcmp(s,"mb") == 0)
  return(7);
if (strcmp(s,"mg") == 0)
  return(8);
if (strcmp(s,"mr") == 0)
  return(9);
if (strcmp(s,"null") == 0)
  return(10);
if (strcmp(s,"wks") == 0)
  return(11);
if (strcmp(s,"ptr") == 0)
  return(12);
if (strcmp(s,"hinfo") == 0)
  return(13);
if (strcmp(s,"minfo") == 0)
  return(14);
if (strcmp(s,"mx") == 0)
  return(15);
if (strcmp(s,"txt") == 0)	/* Roy */
  return(T_TXT);		/* Roy */
if (strcmp(s,"uinfo") == 0)
  return(100);
if (strcmp(s,"uid") == 0)
  return(101);
if (strcmp(s,"gid") == 0)
  return(102);
if (strcmp(s,"unspec") == 0)
  return(103);
if (strcmp(s,"any") == 0)
  return(255);
if (strcmp(s,"*") == 0)
  return(255);
if (atoi(s))
  return(atoi(s));
fprintf(stderr, "Invalid query type: %s\n", s);
exit(2);
}

static int
parseclass(s)
	char *s;
{
if (strcmp(s,"in") == 0)
  return(C_IN);
if (strcmp(s,"chaos") == 0)
  return(C_CHAOS);
if (strcmp(s,"hs") == 0)
  return(C_HS);
if (strcmp(s,"any") == 0)
  return(C_ANY);
if (atoi(s))
  return(atoi(s));
fprintf(stderr, "Invalid query class: %s\n", s);
exit(2);
}

static void
printanswer(hp)
	register struct hostent *hp;
{
	register char **cp;
	register ipaddr_t **hptr;

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

static void
hperror(errno) 
int errno;
{
switch(errno) {
	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 what you requested.\n");
		break;
	}
}


typedef union querybuf {
	dns_hdr_t qb1;
	u8_t qb2[PACKETSZ];
} querybuf_t;

static u8_t hostbuf[BUFSIZ+1];


static int
gethostinfo(name)
	char *name;
{
	register char *cp, **domain;
	int n;
	int hp;
	int nDomain;

	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 && (cp = __hostalias(name))) {
	        if (verbose)
		    printf("Aliased to \"%s\"\n", cp);
		_res.options |= RES_DEFNAMES;	  
		return (getdomaininfo(cp, (char *)NULL));
	}
#ifdef MAXDS
	for (nDomain = 0;
	     _res.defdname_list[nDomain][0] != 0;
	     nDomain++) {
	    for (domain = _res.dnsrch_list[nDomain]; *domain; domain++) {
	        if (verbose)
		    printf("Trying domain \"%s\"\n", *domain);
		hp = getdomaininfo(name, *domain);
		if (hp)
			return (hp);
	    }
	}
#else
	for (domain = _res.dnsrch; *domain; domain++) {
	  if (verbose)
	    printf("Trying domain \"%s\"\n", *domain);
	  hp = getdomaininfo(name, *domain);
	  if (hp)
	    return (hp);
	}
#endif
	if (h_errno != HOST_NOT_FOUND ||
	   (_res.options & RES_DNSRCH) == 0)
		return (0);
	if (verbose)
	    printf("Trying null domain\n");
	return (getdomaininfo(name, (char *)NULL));
}

static int
getdomaininfo(name, domain)
	char *name, *domain;
{
  return getinfo(name, domain, gettype ? gettype : getdeftype);
}

static int
getinfo(name, domain, type)
	char *name, *domain;
{

	dns_hdr_t *hp;
	u8_t *eom, *bp, *cp;
	querybuf_t buf, answer;
	int n, n1, i, j, nmx, ancount, nscount, arcount, qdcount, buflen;
	u_short pref, class;
	char host[2*MAXDNAME+2];

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

	n = res_mkquery(QUERY, host, getclass, type, (char *)NULL, 0, NULL,
		(char *)&buf, sizeof(buf));
	if (n < 0) {
		if (_res.options & RES_DEBUG)
			printf("res_mkquery failed\n");
		h_errno = NO_RECOVERY;
		return(0);
	}
	n = res_send((char *)&buf, n, (char *)&answer, sizeof(answer));
	if (n < 0) {
		if (_res.options & RES_DEBUG)
			printf("res_send failed\n");
		h_errno = TRY_AGAIN;
		return (0);
	}
	eom = (u8_t *)&answer + n;
	return(printinfo(&answer, eom, T_ANY, 0));
}

static int
printinfo(answer, eom, filter, isls)
	querybuf_t *answer;
	u8_t *eom;
        int filter;
        int isls;
{
	dns_hdr_t *hp;
	u8_t *bp, *cp;
	int n, n1, i, j, nmx, ancount, nscount, arcount, qdcount, buflen;
	u_short pref, class;

	/*
	 * find first satisfactory answer
	 */
	hp = (dns_hdr_t *) answer;
	ancount = ntohs(hp->dh_ancount);
	qdcount = ntohs(hp->dh_qdcount);
	nscount = ntohs(hp->dh_nscount);
	arcount = ntohs(hp->dh_arcount);
	if (_res.options & RES_DEBUG || (verbose && isls == 0))
		printf("rcode = %d (%s), ancount=%d\n", 
		       hp->dh_flag2 & DHF_RCODE,
		       DecodeError(hp->dh_flag2 & DHF_RCODE), ancount);
	if (hp->dh_flag2 & DHF_RCODE != NOERROR || 
		(ancount+nscount+arcount) == 0) {
		switch (hp->dh_flag2 & DHF_RCODE) {
			case NXDOMAIN:
				/* Check if it's an authoritive answer */
				if (hp->dh_flag1 & DHF_AA) {
					h_errno = HOST_NOT_FOUND;
					return(0);
				} else {
					h_errno = TRY_AGAIN;
					return(0);
				}
			case SERVFAIL:
				h_errno = TRY_AGAIN;
				return(0);
#ifdef OLDJEEVES
			/*
			 * Jeeves (TOPS-20 server) still does not
			 * support MX records.  For the time being,
			 * we must accept FORMERRs as the same as
			 * NOERROR.
			 */
			case FORMERR:
#endif /* OLDJEEVES */
			case NOERROR:
/* TpB - set a return error for this case. NO_DATA */
				h_errno = NO_DATA;
				return(0); /* was 1,but now indicates exception */
#ifndef OLDJEEVES
			case FORMERR:
#endif /* OLDJEEVES */
			case NOTIMP:
			case REFUSED:
				h_errno = NO_RECOVERY;
				return(0);
		}
		return (0);
	}
	bp = hostbuf;
	nmx = 0;
	buflen = sizeof(hostbuf);
	cp = (u8_t *)answer + sizeof(dns_hdr_t);
	if (qdcount) {
		cp += dn_skipname((u8_t *)cp,(u8_t *)eom) + QFIXEDSZ;
		while (--qdcount > 0)
			cp += dn_skipname((u8_t *)cp,(u8_t *)eom) + QFIXEDSZ;
	}
	if (ancount) {
	  if (!(hp->dh_flag1 & DHF_AA))
	    if (verbose && isls == 0)
	      printf("The following answer is not authoritative:\n");
	  while (--ancount >= 0 && cp && cp < eom) {
	    cp = pr_rr(cp, (u8_t *)answer, stdout, filter);
/*
 * When we ask for address and there is a CNAME, it seems to return
 * both the CNAME and the address.  Since we trace down the CNAME
 * chain ourselves, we don't really want to print the address at
 * this point.
 */
	    if (cname && ! verbose)
	      return (1);
	  }
	}
	if (! verbose)
	  return (1);
	if (nscount) {
	  printf("For authoritative answers, see:\n");
	  while (--nscount >= 0 && cp && cp < eom) {
	    cp = pr_rr(cp, (u8_t *)answer, stdout, filter);
	  }
	}
	if (arcount) {
	  printf("Additional information:\n");
	  while (--arcount >= 0 && cp && cp < eom) {
	    cp = pr_rr(cp, (u8_t *)answer, stdout, filter);
	  }
	}
	return(1);
 }

static u8_t cnamebuf[MAXDNAME];

/*
 * Print resource record fields in human readable form.
 */
static u8_t *
pr_rr(cp, msg, file, filter)
	u8_t *cp, *msg;
	FILE *file;
        int filter;
{
	int type, class, dlen, n, c, proto, ttl;
	ipaddr_t inaddr;
	u8_t *cp1;
	struct protoent *protop;
	struct servent *servp;
	char punc;
	int doprint;
	u8_t name[MAXDNAME];

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

	type = _getshort(cp);
	cp += sizeof(u_short);

	class = _getshort(cp);
	cp += sizeof(u_short);

	ttl = _getlong(cp);
	cp += sizeof(u_long);

	if (filter == type || filter == T_ANY ||
	    (filter == T_A && (type == T_PTR || type == 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 = _getshort(cp);
	cp += sizeof(u_short);
	cp1 = cp;
	/*
	 * Print type specific data, if appropriate
	 */
	switch (type) {
	case T_A:
		switch (class) {
		case C_IN:
			bcopy((char *)cp, (char *)&inaddr, sizeof(inaddr));
			if (dlen == 4) {
			        if (doprint)
				  fprintf(file,"%c%s", punc,
					inet_ntoa(inaddr));
				cp += dlen;
			} else if (dlen == 7) {
			        if (doprint) {
				  fprintf(file,"%c%s", punc,
					  inet_ntoa(inaddr));
				  fprintf(file,", protocol = %d", cp[4]);
				  fprintf(file,", port = %d",
					  (cp[5] << 8) + cp[6]);
				}
				cp += dlen;
			}
			break;
		}
		break;
	case T_CNAME:
		if (dn_expand(msg, msg + 512, cp, cnamebuf, 
			      sizeof(cnamebuf)-1) >= 0) {
			strcat((char *) cnamebuf, ".");
			if (gettype != T_CNAME && gettype != T_ANY)
				cname = cnamebuf;				
		}
	case T_MB:
#ifdef OLDRR
	case T_MD:
	case T_MF:
#endif /* OLDRR */
	case T_MG:
	case T_MR:
	case T_NS:
	case T_PTR:
		cp = pr_cdname(cp, msg, name, sizeof(name));
		if (doprint)
		  fprintf(file,"%c%s",punc, name);
		break;

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

	case T_SOA:
		cp = pr_cdname(cp, msg, name, sizeof(name));
		if (doprint)
		  fprintf(file,"\t%s", name);
		cp = pr_cdname(cp, msg, name, sizeof(name));
		if (doprint)
		  fprintf(file," %s", name);
		if (doprint)
		  fprintf(file,"(\n\t\t\t%ld\t;serial (version)", _getlong(cp));
		cp += sizeof(u_long);
		if (doprint)
		  fprintf(file,"\n\t\t\t%ld\t;refresh period", _getlong(cp));
		cp += sizeof(u_long);
		if (doprint)
		  fprintf(file,"\n\t\t\t%ld\t;retry refresh this often", _getlong(cp));
		cp += sizeof(u_long);
		if (doprint)
		  fprintf(file,"\n\t\t\t%ld\t;expiration period", _getlong(cp));
		cp += sizeof(u_long);
		if (doprint)
		  fprintf(file,"\n\t\t\t%ld\t;minimum TTL\n\t\t\t)", _getlong(cp));
		cp += sizeof(u_long);
		break;

	case T_MX:
		if (doprint)
		  if (verbose)
		    fprintf(file,"\t%d ",_getshort(cp));
		  else
		    fprintf(file," (pri=%d) by ",_getshort(cp));
		cp += sizeof(u_short);
		cp = pr_cdname(cp, msg, name, sizeof(name));
		if (doprint)
		  fprintf(file, "%s", name);
		break;

	case T_MINFO:
		cp = pr_cdname(cp, msg, name, sizeof(name));
		if (doprint)
		  fprintf(file,"%c%s",punc, name);
		cp = pr_cdname(cp, msg, name, sizeof(name));
		if (doprint)
		  fprintf(file," %s", name);
		break;

		/* Roy start */
	case T_TXT:
		if (n = *cp++) {
			if (doprint)
			  fprintf(file,"%c%.*s", punc, n, cp);
			cp += n;
		}
		break;
		/* Roy end */

	case T_UINFO:
		if (doprint)
		  fprintf(file,"%c%s", punc, cp);
		cp += dlen;
		break;

	case T_UID:
	case T_GID:
		if (dlen == 4) {
			if (doprint)
			  fprintf(file,"%c%ld", punc, _getlong(cp));
			cp += sizeof(int);
		}
		break;

	case T_WKS:
		if (dlen < sizeof(u_long) + 1)
			break;
		bcopy((char *)cp, (char *)&inaddr, sizeof(inaddr));
		cp += sizeof(u_long);
		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;

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

static	char nbuf[20];

/*
 * Return a string for the type
 */
static char *
pr_type(type)
	int type;
{
	switch (type) {
	case T_A:
		return(verbose? "A" : "has address");
	case T_NS:		/* authoritative server */
		return("NS");
#ifdef OLDRR
	case T_MD:		/* mail destination */
		return("MD");
	case T_MF:		/* mail forwarder */
		return("MF");
#endif /* OLDRR */
	case T_CNAME:		/* connonical name */
		return(verbose? "CNAME" : "is a nickname for");
	case T_SOA:		/* start of authority zone */
		return("SOA");
	case T_MB:		/* mailbox domain name */
		return("MB");
	case T_MG:		/* mail group member */
		return("MG");
	case T_MX:		/* mail routing info */
		return(verbose? "MX" : "mail is handled");
	/* Roy start */
	case T_TXT:		/* TXT - descriptive info */
		return(verbose? "TXT" : "descriptive text");
	/* Roy end */
	case T_MR:		/* mail rename name */
		return("MR");
	case T_NULL:		/* null resource record */
		return("NULL");
	case T_WKS:		/* well known service */
		return("WKS");
	case T_PTR:		/* domain name pointer */
		return("PTR");
	case T_HINFO:		/* host information */
		return("HINFO");
	case T_MINFO:		/* mailbox information */
		return("MINFO");
	case T_AXFR:		/* zone transfer */
		return("AXFR");
	case T_MAILB:		/* mail box */
		return("MAILB");
	case T_MAILA:		/* mail address */
		return("MAILA");
	case T_ANY:		/* matches any type */
		return("ANY");
	case T_UINFO:
		return("UINFO");
	case T_UID:
		return("UID");
	case T_GID:
		return("GID");
	default:
		return (sprintf(nbuf, "%d", type) == EOF ? NULL : nbuf);
	}
}

/*
 * Return a mnemonic for class
 */
static char *
pr_class(class)
	int class;
{

	switch (class) {
	case C_IN:		/* internet class */
		return(verbose? " IN" : "");
	case C_CHAOS:		/* chaos class */
		return(verbose? " CHAOS" : "");
	case C_HS:		/* Hesiod class */
		return(verbose? " HS" : "");
	case C_ANY:		/* matches any class */
		return(" ANY");
	default:
		return (sprintf(nbuf," %d", class) == EOF ? NULL : nbuf);
	}
}

static u8_t *
pr_cdname(cp, msg, name, namelen)
	u8_t *cp, *msg;
        u8_t *name;
        int namelen;
{
	int n;

	if ((n = dn_expand(msg, msg + 512, cp, name, namelen - 2)) < 0)
		return (NULL);
	if (name[0] == '\0') {
		name[0] = '.';
		name[1] = '\0';
	}
	return (cp + n);
}

char *resultcodes[] = {
	"NOERROR",
	"FORMERR",
	"SERVFAIL",
	"NXDOMAIN",
	"NOTIMP",
	"REFUSED",
	"6",
	"7",
	"8",
	"9",
	"10",
	"11",
	"12",
	"13",
	"14",
	"NOCHANGE",
};



/*
 ******************************************************************************
 *
 *  ListHosts --
 *
 *	Requests the name server to do a zone transfer so we
 *	find out what hosts it knows about.
 *
 *  Results:
 *	SUCCESS		the listing was successful.
 *	ERROR		the server could not be contacted because 
 *			a socket could not be obtained or an error
 *			occured while receiving, or the output file
 *			could not be opened.
 *
 ******************************************************************************
 */

static int
ListHosts(namePtr, queryType)
    char *namePtr;
    int  queryType;  /* e.g. T_A */
{
	querybuf_t 		buf, answer;
	dns_hdr_t		*headerPtr;

	int 			msglen;
	int 			amtToRead;
	int 			numRead;
	int 			i;
	int 			numAnswers = 0;
	int 			result;
	int 			soacnt = 0;
	u_short 		len;
	int			dlen;
	int			type;
	int			nscount;
	u8_t 			*cp, *nmp;
	u8_t 			name[NAME_LEN];
	char 			dname[2][NAME_LEN];
	u8_t 			domain[NAME_LEN];
/* names and addresses of name servers to try */
#define NUMNS 8
	char			nsname[NUMNS][NAME_LEN];
	int			nshaveaddr[NUMNS];
#define IPADDRSIZE 4
#define NUMNSADDR 16
	char	 		nsipaddr[NUMNSADDR][IPADDRSIZE];
	int			numns;
	int			numnsaddr;
	int			thisns;
	struct hostent		*hp;
	enum {
	    NO_ERRORS, 
	    ERR_READING_LEN, 
	    ERR_READING_MSG,
	    ERR_PRINTING
	} error = NO_ERRORS;
	char *tcp_serv_name;
	int tcp_fd;
	nwio_tcpconf_t tcpconf;
	nwio_tcpcl_t clopt;
	int terrno;

/*
 * 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) {
	  bcopy((char *)&_res.nsaddr, nsipaddr[0], IPADDRSIZE);
	  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_mkquery(QUERY, namePtr, C_IN, T_NS,
				(char *)0, 0, (struct rrec *)0, 
				(char *)&buf, sizeof(buf));

	if (msglen < 0) {
		printf("res_mkquery failed\n");
		return (ERROR);
	}

	msglen = res_send((char *)&buf,msglen,(char *)&answer, sizeof(answer));
	
	if (msglen < 0) {
		printf("Unable to get to nameserver -- try again later\n");
		return (ERROR);
	}
	if (_res.options & RES_DEBUG || verbose)
		printf("rcode = %d (%s), ancount=%d\n", 
		       answer.qb1.dh_flag2 & DHF_RCODE,
		       DecodeError(answer.qb1.dh_flag2 & DHF_RCODE),
		       ntohs(answer.qb1.dh_ancount));

/*
 * Analyze response to our NS lookup
 */

	nscount = ntohs(answer.qb1.dh_ancount) + ntohs(answer.qb1.dh_nscount) +
		  ntohs(answer.qb1.dh_arcount);

	if (answer.qb1.dh_flag2 & DHF_RCODE != NOERROR || nscount == 0) {
		switch (answer.qb1.dh_flag2 & DHF_RCODE) {
			case NXDOMAIN:
				/* Check if it's an authoritive answer */
				if (answer.qb1.dh_flag1 & DHF_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 + sizeof(dns_hdr_t);
	if (ntohs(answer.qb1.dh_qdcount) > 0)
	  cp += dn_skipname(cp, answer.qb2 + msglen) + QFIXEDSZ;

	numns = 0;
	numnsaddr = 0;

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

	for (;nscount; nscount--) {
	  cp += dn_expand(answer.qb2, answer.qb2 + msglen, cp,
			  domain, sizeof(domain));
	  type = _getshort(cp);
	  cp += sizeof(u_short) + sizeof(u_short) + sizeof(u_long);
	  dlen = _getshort(cp);
	  cp += sizeof(u_short);
	  if (type == T_NS) {
	    if (dn_expand(answer.qb2, answer.qb2 + msglen, cp, 
			  name, sizeof(name)) >= 0) {
	      if (numns < NUMNS && strcasecmp((char *)domain, namePtr) == 0) {
		for (i = 0; i < numns; i++)
		  if (strcasecmp(nsname[i], (char *)name) == 0)
		    break;  /* duplicate */
		if (i >= numns) {
		  strncpy(nsname[numns], (char *)name, sizeof(name));
		  nshaveaddr[numns] = 0;
		  numns++;
		}
	      }
	    }
	  }
	  else if (type == T_A) {
	    if (numnsaddr < NUMNSADDR)
	      for (i = 0; i < numns; i++) {
		if (strcasecmp(nsname[i], (char *)domain) == 0) {
		  nshaveaddr[i]++;
		  bcopy((char *)cp, nsipaddr[numnsaddr],IPADDRSIZE);
		  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]) {
	    register long **hptr;
	    int numaddrs = 0;

	    hp = gethostbyname(nsname[i]);
	    if (hp) {
	      for (hptr = (long **)hp->h_addr_list; *hptr; hptr++)
		if (numnsaddr < NUMNSADDR) {
		  bcopy((char *)*hptr, nsipaddr[numnsaddr],IPADDRSIZE);
		  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_mkquery(QUERY, namePtr, getclass, T_AXFR,
				(char *)0, 0, (struct rrec *)0, 
				(char *) &buf, sizeof(buf));
	if (msglen < 0) {
	    if (_res.options & RES_DEBUG) {
		fprintf(stderr, "ListHosts: Res_mkquery failed\n");
	    }
	    return (ERROR);
	}

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

	tcp_serv_name= getenv("TCP_DEVICE");
	if (!tcp_serv_name)
		tcp_serv_name= TCP_DEVICE;
	for (;thisns < numnsaddr; thisns++) 
	{
		tcp_fd= open(tcp_serv_name, O_RDWR);
		if (tcp_fd == -1)
		{
			fprintf(stderr, "unable to open '%s': %s\n", tcp_serv_name,
				strerror(errno));
			return ERROR;
		}

		tcpconf.nwtc_flags= NWTC_EXCL | NWTC_LP_SEL | NWTC_SET_RA | 
								NWTC_SET_RP;
		tcpconf.nwtc_remaddr= *(ipaddr_t *)nsipaddr[thisns];
		tcpconf.nwtc_remport= _res.nsport_list[0];
		result= ioctl(tcp_fd, NWIOSTCPCONF, &tcpconf);
		if (result == -1)
		{
			fprintf(stderr, "tcp_ioc_setconf failed: %s\n", 
				strerror(errno));
			close(tcp_fd);
			return ERROR;
		}
		if (_res.options & RES_DEBUG || verbose)
			printf("Trying %s\n", inet_ntoa(tcpconf.nwtc_remaddr));
		clopt.nwtcl_flags= 0;
		result= ioctl(tcp_fd, NWIOTCPCONN, &clopt);
		if (result == 0)
			break;
		terrno= errno;
		if (verbose)
			fprintf(stderr, 
				"Connection failed, trying next server: %s\n",
				strerror(errno));
		close(tcp_fd);
	}	
	if (thisns >= numnsaddr) {
	  printf("No server for that domain responded\n");
	  if (!verbose)
	    fprintf(stderr, "Error from the last server was: %s\n", 
						strerror(terrno));
	  return(ERROR);
	}

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

        len = htons(msglen);

	result= tcpip_writeall(tcp_fd, (char *)&len, sizeof(len));
	if (result != sizeof(len))
	{
		fprintf(stderr, "write failed: %s\n", strerror(errno));
		close(tcp_fd);
		return ERROR;
	}
	result= tcpip_writeall(tcp_fd, (char *)&buf, msglen);
	if (result != msglen)
	{
		fprintf(stderr, "write failed: %s\n",
			strerror(errno));
		close(tcp_fd);
		return ERROR;
	}
	filePtr = stdout;

	while (1) {

	    /*
	     * Read the length of the response.
	     */

	    cp = (u8_t *) &buf;
	    amtToRead = sizeof(u_short);
	    while(amtToRead > 0)
	    {
		result = read(tcp_fd, (char *)cp, amtToRead);
		if (result <= 0)
			break;
		cp 	  += result;
		amtToRead -= result;
	    }
	    if (amtToRead) {
		error = ERR_READING_LEN;
		break;
	    }	

	    if ((len = htons(*(u_short *)&buf)) == 0) {
		break;	/* nothing left to read */
	    }

	    /*
	     * Read the response.
	     */

	    amtToRead = len;
	    cp = (u8_t *) &buf;
	    while(amtToRead > 0)
	    {
		result = read(tcp_fd, (char *)cp, amtToRead);
		if (result<= 0)
			break;
		cp 	  += result;
		amtToRead -= result;
	    }
	    if (amtToRead) {
		error = ERR_READING_MSG;
		break;
	    }

	    i = buf.qb1.dh_flag2 & DHF_RCODE;
	    if (i != NOERROR || ntohs(buf.qb1.dh_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");
		close(tcp_fd);
		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 + sizeof(dns_hdr_t);
	    if (ntohs(buf.qb1.dh_qdcount) > 0)
		cp += dn_skipname(cp, buf.qb2 + len) + QFIXEDSZ;

	    nmp = cp;
	    cp += dn_skipname(cp, (u_char *)&buf + len);
	    if ((_getshort(cp) == T_SOA)) {
		dn_expand(buf.qb2, buf.qb2 + len, nmp, (u8_t *)dname[soacnt],
			sizeof(dname[0]));
	        if (soacnt) {
		    if (strcmp(dname[0], dname[1]) == 0)
			break;
		} else
		    soacnt++;
	    }
        }

	close(tcp_fd);

	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 = (dns_hdr_t *) &buf;
		fprintf(stderr,"ListHosts: error receiving zone transfer:\n");
		fprintf(stderr,
	       "  result: %s, answers = %d, authority = %d, additional = %d\n", 
		    	resultcodes[headerPtr->dh_flag2 & DHF_RCODE], 
		    	ntohs(headerPtr->dh_ancount), 
			ntohs(headerPtr->dh_nscount), 
			ntohs(headerPtr->dh_arcount));
		return(ERROR);
	    default:
		return(ERROR);
	}
}

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

static int tcpip_writeall(fd, buf, siz)
int fd;
char *buf;
unsigned siz;
{
	unsigned siz_org;
	int nbytes;

	siz_org= siz;

	while (siz)
	{
		nbytes= write(fd, buf, siz);
		if (nbytes == -1)
			return nbytes;
		assert(siz >= nbytes);
		buf += nbytes;
		siz -= nbytes;
	}
	return siz_org;
}
