#include <stdio.h>
#include <ctype.h>
#include "global.h"
#include "mbuf.h"
#include "timer.h"
#include "netuser.h"
#include "socket.h"
#include "cmdparse.h"
#include "proc.h"
#define MARC_DOMAIN_C
#include "domain.h"
#include "commands.h"

static struct dserver *Dlist;		/* List of potential servers */
static struct dserver *Dserver;		/* Current one being used */
static char *Dsuffix;			/* Default suffix for names without periods */
static int Ddebug = 0;
static char *Dtypes[] = {
	"",
	"A",
	"NS",
	"MD",
	"MF",
	"CNAME",
	"SOA",
	"MB",
	"MG",
	"MR",
	"NULL",
	"WKS",
	"PTR",
	"HINFO",
	"MINFO",
	"MX",
	"TXT"
};
static int Ndtypes = 17;
static char delim[] = " \t\r\n";
static struct {
	char *name;
	int32 address;
} cache;

static struct rr *dfind __ARGS((FILE *dbase,char *name,int type));
static int doadds __ARGS((int argc,char *argv[],void *p));
static int dodropds __ARGS((int argc,char *argv[],void *p));
static int dolistds __ARGS((int argc,char *argv[],void *p));
static int dosuffix __ARGS((int argc,char *argv[],void *p));
static int dodtrace __ARGS((int argc,char *argv[],void *p));
static void dumpdomain __ARGS((struct dhdr *dhdr,int32 rtt));
static void free_dhdr __ARGS((struct dhdr *dp));
static void free_qu __ARGS((struct quest *qp));
static void free_rr __ARGS((struct rr *rrp));
static struct rr *getrr __ARGS((FILE *fp));
static int isaddr __ARGS((char *s));
static void cache_response __ARGS((struct dhdr *dhdr));
static void putrr __ARGS((FILE *fp,struct rr *rrp));
static int res_mkquery __ARGS((int16 op,char *dname,int16 class,int16 type,
	char *data,int16 datalen,int16 newrr,char *buffer,int16 buflen));
static int rrcmp __ARGS((struct rr *rr1,struct rr *rr2));
static int sendquery __ARGS((int32 server,char *name,int type,int32 timeout,
	struct mbuf **bpp));

static struct cmds Dcmds[] = {
	"addserver",	doadds,		0, 2, NULLCHAR,
	"dropserver",	dodropds,	0, 2, NULLCHAR,
	"listservers",	dolistds,	0, 0, NULLCHAR,
	"suffix",	dosuffix,	0, 0, NULLCHAR,
	"trace",	dodtrace,	0, 0, NULLCHAR,
	NULLCHAR,
};
static int
dodtrace(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	return setbool(&Ddebug,"Domain trace",argc,argv);
}
int
dodomain(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	return subcmd(Dcmds,argc,argv,p);	
}
static int
dosuffix(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	if(argc < 2){
		if(Dsuffix != NULLCHAR)
			tprintf("%s\n",Dsuffix);
		return 0;
	}
	free(Dsuffix);
	Dsuffix = strdup(argv[1]);
	return 0;
}
static int
doadds(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	struct dserver *dp;
	int32 address;

	if((address = resolve(argv[1])) == 0){
		tprintf("Resolver %s unknown\n",argv[1]);
		return 1;
	}
	dp = (struct dserver *)callocw(1,sizeof(struct dserver));
	dp->address = address;
	dp->srtt = (5L * 1000) / MSPTICK; /* About 5 sec */
	dp->mdev = 0;
	dp->timeout = 2 * dp->mdev + dp->srtt + 3;
	dp->next = Dlist;
	if(dp->next != NULLDOM)
		dp->next->prev = dp;
	Dlist = dp;
	Dserver = dp;	/* Make this the first one we try next */
	return 0;
}
static int
dodropds(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	struct dserver *dp;
	int32 addr;

	addr = resolve(argv[1]);
	for(dp = Dlist;dp != NULLDOM;dp = dp->next)
		if(addr == dp->address)
			break;

	if(dp == NULLDOM){
		tprintf("Not found\n");
		return 1;
	}
	if(dp->prev != NULLDOM)
		dp->prev->next = dp->next;
	else
		Dlist = dp->next;
	if(dp->next != NULLDOM)
		dp->next->prev = dp->prev;

	if(Dserver == dp)
		Dserver = Dlist;
	free((char *)dp);
	return 0;
}
static int
dolistds(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	register struct dserver *dp;

	tprintf("Server address      srtt    mdev    timeout\
 queries responses\n");
	for(dp = Dlist;dp != NULLDOM;dp = dp->next){
		tprintf("%-20s%-8lu%-8lu%-8lu%-8lu%-8lu\n",
		 inet_ntoa(dp->address),
		 dp->srtt * MSPTICK,
		 dp->mdev * MSPTICK,
		 dp->timeout * MSPTICK,
		 dp->queries,dp->responses);
	}
	return 0;
}

/* Search local domain file, starting at current position,
 * for resource record of specified type.
 * If a record is found, the domain file pointer is left just after it. If
 * not, it is left at end of file.
 */
static struct rr *
dfind(dbase,name,type)
FILE *dbase;
char *name;
int type;
{
	struct rr *rrp;
	int nlen;

	while((rrp = getrr(dbase)) != NULLRR){
		if((nlen = strlen(name)) == strlen(rrp->name)
		 && strnicmp(name,rrp->name,nlen) == 0
		 && rrp->class == CLASS_IN
		 && (type == TYPE_ANY || rrp->type == type))
			break;
		free_rr(rrp);
		pwait(NULL);	/* Give up CPU for a while, this is slow */
	}
	return rrp;
}
static struct rr *
getrr(fp)
FILE *fp;
{
	char *line,*strtok();
	struct rr *rrp;
	char *name,*ttl,*class,*type,*data;
	int i;

	line = mallocw(256);
	/* Search file */
	while(fgets(line,256,fp),!feof(fp)){
		if(line[0] != '#')
			break;
	}
	if(feof(fp)){
		free(line);
		return NULLRR;
	}
 	rrp = (struct rr *)callocw(1,sizeof(struct rr));
	name = strtok(line,delim);
	ttl = strtok(NULLCHAR,delim);
	class = strtok(NULLCHAR,delim);
	type = strtok(NULLCHAR,delim);
	data = strtok(NULLCHAR,delim);
	
	i = strlen(name);
	if(name[i-1] != '.'){
		/* Tack on a trailing period if it's not there */
		rrp->name = mallocw(i+2);
		strcpy(rrp->name,name);
		strcat(rrp->name,".");
	} else 
		rrp->name = strdup(name);
	
	if(!isdigit(ttl[0])){
		/* Optional ttl field is missing; slide the other fields over */
		data = type;
		type = class;
		class = ttl;
		ttl = NULLCHAR;
	} else {
		rrp->ttl = atol(ttl);
	}
	for(i=0;i<NRLIST;i++){
		if(strcmp(type,Dtypes[i]) == 0){
			rrp->type = i;
			break;
		}
	}
	if(strcmp(class,"IN") == 0)
		rrp->class = CLASS_IN;

	if(data == NULLCHAR){
		/* Empty record, just return */
		free(line);
		return rrp;
	}
	switch(rrp->type){
	case TYPE_CNAME:
	case TYPE_MB:
	case TYPE_MG:
	case TYPE_MR:
	case TYPE_NS:
	case TYPE_PTR:
	case TYPE_TXT:
		rrp->rdlength = strlen(data);
		rrp->rdata.name = strdup(data);
		break;
	case TYPE_A:
		rrp->rdlength = 4;
		rrp->rdata.addr = aton(data);
		break;
	case TYPE_HINFO:
		rrp->rdlength = strlen(data);
		rrp->rdata.hinfo.cpu = strdup(data);
		if((data = strtok(NULLCHAR,delim)) != NULLCHAR){
			rrp->rdlength += strlen(data);
			rrp->rdata.hinfo.os = strdup(data);
		}
		break;
	case TYPE_MX:
		rrp->rdata.mx.pref = atoi(data);
		rrp->rdlength = 2;

		/* Get domain name of exchanger */
		if((data = strtok(NULLCHAR,delim)) != NULLCHAR){
			rrp->rdlength += strlen(data);
			rrp->rdata.mx.exch = strdup(data);
		}
		break;
	case TYPE_SOA:
		/* Get domain name of master name server */
		rrp->rdlength = strlen(data);
		rrp->rdata.soa.mname = strdup(data);

		/* Get domain name of irresponsible person */
		if((data = strtok(NULLCHAR,delim)) != NULLCHAR){
			rrp->rdata.soa.rname = strdup(data);
			rrp->rdlength += strlen(data);
		}
		data = strtok(NULLCHAR,delim);
		rrp->rdata.soa.serial = atol(data);
		data = strtok(NULLCHAR,delim);
		rrp->rdata.soa.refresh = atol(data);
		data = strtok(NULLCHAR,delim);
		rrp->rdata.soa.retry = atol(data);
		data = strtok(NULLCHAR,delim);
		rrp->rdata.soa.expire = atol(data);
		data = strtok(NULLCHAR,delim);
		rrp->rdata.soa.minimum = atol(data);
		rrp->rdlength += 20;
		break;
	}
	free(line);
	return rrp;
}
/* Search for address record in local database, looking first for PTR
 * and CNAME records. Return values:
 *  0xffffffff	Not found (domain name may exist, but we don't know yet)
 *  0		Domain name definitely doesn't exist (we have a null record)
 */
int32
dresolve(name)
char *name;
{
	register struct rr *rrp;
	char *sname,*tmp;
	int32 addr;
	FILE *dbase;
	int crecurse = 0;
	int found = 0;

	if(cache.name != NULLCHAR && strcmp(cache.name,name) == 0)
		return cache.address;

	if((dbase = fopen(Dfile,READ_TEXT)) == NULLFILE)
		return 0xffffffff;

	sname = strdup(name);

	while(!found && (rrp = dfind(dbase,sname,TYPE_ANY)) != NULLRR){
		switch(rrp->type){
		case TYPE_A:
			/* All set! */
			found++;
			if(rrp->rdlength == 0)
				addr = 0;	/* Neg response */
			else
				addr = rrp->rdata.addr;
			break;
		case TYPE_CNAME:
		case TYPE_PTR:
			if(++crecurse == MAXCNAME){
				/* Too many CNAME recursions */
				found++;
				break;
			}
			/* Look for what it points to */
			tmp = strdup(rrp->rdata.name);
			free(sname);
			sname = tmp;
			rewind(dbase);	/* Start again */
			break;
		}
		free_rr(rrp);
	}
done:	fclose(dbase);
	if(found){
		free(cache.name);
		cache.name = strdup(name);
		cache.address = addr;
	} else
		addr = 0xffffffff;
	free(sname);
	return addr;
}

/* Main entry point for domain name -> address resolution. Returns 0 if
 * name is definitely not valid.
 */
int32
resolve(name)
char *name;
{
	int32 addr;
	struct dhdr dhdr;
	struct dserver *dp = Dserver;
	char *sname,*tmp;
	int len,i;
	int32 rtt,abserr;
	struct mbuf *bp;
	struct rr *rrp;
	int crecurse = 0;

	if(name == NULLCHAR)
		return 0;

	if(*name == '[')
		return aton(name + 1);

	if(isaddr(name))
		return aton(name);


	sname = strdup(name);
	if(strchr(sname,'.') == NULLCHAR && Dsuffix != NULLCHAR){
		/* Append default suffix */
		tmp = mallocw(strlen(sname)+strlen(Dsuffix)+2);
		sprintf(tmp,"%s.%s",sname,Dsuffix);
		free(sname);
		sname = tmp;
	}
	if(sname[strlen(sname)-1] != '.'){
		/* Append trailing dot */
		tmp = mallocw(strlen(sname)+2);
		sprintf(tmp,"%s.",sname);
		free(sname);
		sname = tmp;
	}
	while((addr = dresolve(sname)) == 0xffffffff){
		/* Not found in local file. Query the domain servers. */
		if(dp == NULLDOM)
			break;		/* no servers! */
		for(;;){
			dp->queries++;
			len = sendquery(dp->address,sname,TYPE_A,dp->timeout,&bp);
			if(len > 0)
				break;	/* Got a response */
			if(errno == EABORT){
				addr = 0;	/* Killed by "reset" command */
				break;
			}
			/* Timeout; back off this one and try another server */
			dp->timeout <<= 1;
			if((dp = dp->next) == NULLDOM)
				dp = Dlist;
		}
		if(len <= 0)
			break;
		dp->responses++;
		ntohdomain(&dhdr,&bp);	/* Convert response to local format */

		/* Compute and update the round trip time */
		rtt = (int32) ((int16)Clock - dhdr.id);
		abserr = rtt > dp->srtt ? rtt - dp->srtt : dp->srtt - rtt;
		dp->srtt = ((AGAIN-1) * dp->srtt + rtt + (AGAIN/2)) >> LAGAIN;
		dp->mdev = ((DGAIN-1) * dp->mdev + abserr + (DGAIN/2)) >> LDGAIN;
		dp->timeout = 2 * dp->mdev + dp->srtt + 3;

		if(Ddebug)
			dumpdomain(&dhdr,rtt);
		cache_response(&dhdr);	/* Add records to local disk file */
		/* Look for an answer in this response */
		for(i=0;i<dhdr.ancount;i++){
			rrp = dhdr.ans[i];
			if(strnicmp(rrp->name,sname,strlen(sname)) != 0)
				continue;
			/* Got one! */
			switch(rrp->type){
			case TYPE_CNAME:
				/* Change the query name to the cname
				 * and go back again
				 */
				if(++crecurse == MAXCNAME){
					/* Too many CNAME recursions */
					addr = 0;
					break;
				}
				free(sname);
				sname = strdup(rrp->rdata.name);
				/* Go back and rescan the response */
				i = -1;
				continue;
			case TYPE_A:
				if(rrp->rdlength == 0)
					addr = 0;
				else
					addr = rrp->rdata.addr;
				break;
			}
			break;
		}
		free_dhdr(&dhdr);
		if(addr != 0xffffffff)
			break;
	}
	free(sname);
	if(addr == 0xffffffff)
		addr = 0;
	return addr;
}
/* Send a query to a server */
static int
sendquery(server,name,type,timeout,bpp)
int32 server;		/* Server's IP address */
char *name;		/* Domain name being looked up */
int type;		/* Type of resource record sought */
int32 timeout;		/* Query timeout */
struct mbuf **bpp;	/* Place to stash reply */
{
	char *buf;
	int len;
	struct sockaddr_in server_in;
	int s,rval;

	s = socket(AF_INET,SOCK_DGRAM,0);
	buf = mallocw(512);
	len = res_mkquery(0,name,CLASS_IN,TYPE_A,NULLCHAR,0,0,buf,512);
	server_in.sin_family = AF_INET;
	server_in.sin_port = IPPORT_DOMAIN;
	server_in.sin_addr.s_addr = server;
	if(Ddebug){
		printf("resolve: querying server %s for %s\n",
		 inet_ntoa(server),name);
	}
	sendto(s,buf,len,0,(char *)&server_in,sizeof(server_in));
	free(buf);
	alarm(max(timeout,1));
	/* Wait for something to happen */
	rval = recv_mbuf(s,bpp,0,NULLCHAR,0);
	alarm(0L);
	close_s(s);
	return rval;
}

static int
res_mkquery(op,dname,class,type,data,datalen,newrr,buffer,buflen)
int16 op;	/* operation */
char *dname;	/* Domain name */
int16 class;	/* Class of inquiry (IN, etc) */
int16 type;	/* Type of inquiry (A, MX, etc) */
char *data;
int16 datalen;
int16 newrr;
char *buffer;	/* Area for query */
int16 buflen;	/* Length of same */
{
	char *cp,*cp1;
	int16 parameter;
	int16 dlen,len;

	cp = buffer;
	cp = put16(cp,(int16)Clock);	/* Use clock for timestamping */
	parameter = 0x100;	/* Recursion desired */
	cp = put16(cp,parameter);
	cp = put16(cp,1);
	cp = put16(cp,0);
	cp = put16(cp,0);
	cp = put16(cp,0);
	dlen = strlen(dname);
	for(;;){
		/* Look for next dot */
		cp1 = strchr(dname,'.');
		if(cp1 != NULLCHAR)
			len = cp1-dname;	/* More to come */
		else
			len = dlen;	/* Last component */
		*cp++ = len;		/* Write length of component */
		if(len == 0)
			break;
		/* Copy component up to (but not including) dot */
		strncpy(cp,dname,len);
		cp += len;
		if(cp1 == NULLCHAR){
			*cp++ = 0;	/* Last one; write null and finish */
			break;
		}
		dname += len+1;
		dlen -= len+1;
	}
	cp = put16(cp,type);
	cp = put16(cp,class);
	return cp - buffer;
}
/* Append any non-duplicate resource records to the local file */
static void
cache_response(dhdr)
struct dhdr *dhdr;
{
	FILE *fp;
	int i;
	long ttl = 500;	/* Default TTL for negative records without SOA */
	struct rr *rrp,*frrp;
	struct quest *qp;

	fp = fopen(Dfile,APPEND_TEXT);
	if(fp == NULLFILE){
		printf("Can't append to %s!!\n",Dfile);
		return;
	}
	/* Scan domain file looking for duplicates */
	while((frrp = getrr(fp)) != NULLRR){
		for(i=0;i< dhdr->ancount;i++){
			rrp = dhdr->ans[i];
			if(rrp->type == TYPE_SOA)
				ttl = rrp->ttl;
			if(rrcmp(rrp,frrp) == 0)
				rrp->dupe = 1;
		}
		for(i=0;i< dhdr->nscount;i++){
			rrp = dhdr->ns[i];
			if(rrp->type == TYPE_SOA)
				ttl = rrp->ttl;
			if(rrcmp(rrp,frrp) == 0)
				rrp->dupe = 1;
		}
		for(i=0;i< dhdr->arcount;i++){
			rrp = dhdr->add[i];
			if(rrp->type == TYPE_SOA)
				ttl = rrp->ttl;
			if(rrcmp(rrp,frrp) == 0)
				rrp->dupe = 1;
		}
		free_rr(frrp);
	}
	/* Now append any non-duplicate records */
	fseek(fp,0L,2);
	for(i=0;i< dhdr->ancount;i++){
		rrp = dhdr->ans[i];
		if(!rrp->dupe)
			putrr(fp,rrp);
	}
	for(i=0;i< dhdr->nscount;i++){
		rrp = dhdr->ns[i];
		if(!rrp->dupe)
			putrr(fp,rrp);
	}
	for(i=0;i< dhdr->arcount;i++){
		rrp = dhdr->add[i];
		if(!rrp->dupe)
			putrr(fp,rrp);
	}
	if(dhdr->aa && (dhdr->rcode == NAME_ERROR || dhdr->ancount == 0)){
		/* Add negative reply to file. This assumes that there was
		 * only one question, which is true for all questions we send.
		 */
		qp = dhdr->qlist[0];
		rrp = (struct rr *)callocw(1,sizeof(struct rr));
		rrp->name = strdup(qp->qname);
		rrp->type = qp->qtype;
		rrp->class = qp->qclass;
		rrp->ttl = ttl;
		rrp->rdlength = 0;	/* no data */
		putrr(fp,rrp);
		free_rr(rrp);
	}
	fclose(fp);
}
/* Print a resource record */
static void
putrr(fp,rrp)
FILE *fp;
struct rr *rrp;
{
	if(fp == NULLFILE || rrp == NULLRR)
		return;

	fprintf(fp,"%s\t%lu",rrp->name,rrp->ttl);
	if(rrp->class == CLASS_IN)
		fprintf(fp,"\tIN");
	else
		fprintf(fp,"\t%u",rrp->class);
	if(rrp->type < Ndtypes)
		fprintf(fp,"\t%s",Dtypes[rrp->type]);
	else
		fprintf(fp,"\t%u",rrp->type);
	if(rrp->rdlength == 0){
		/* Null data portion, indicates nonexistent record */
		fprintf(fp,"\n");
		return;
	}
	switch(rrp->type){
	case TYPE_CNAME:
	case TYPE_MB:
	case TYPE_MG:
	case TYPE_MR:
	case TYPE_NS:
	case TYPE_PTR:
	case TYPE_TXT:
		/* These are all printable text strings */
		fprintf(fp,"\t%s\n",rrp->rdata.data);
		break;
	case TYPE_A:
		fprintf(fp,"\t%s\n",inet_ntoa(rrp->rdata.addr));
		break;
	case TYPE_MX:
		fprintf(fp,"\t%u\t%s\n",rrp->rdata.mx.pref,
		 rrp->rdata.mx.exch);
		break;
	case TYPE_SOA:
		fprintf(fp,"\t%s\t%s\t%lu\t%lu\t%lu\t%lu\t%lu\n",
		 rrp->rdata.soa.mname,rrp->rdata.soa.rname,
		 rrp->rdata.soa.serial,rrp->rdata.soa.refresh,
		 rrp->rdata.soa.retry,rrp->rdata.soa.expire,
		 rrp->rdata.soa.minimum);
		break;
	default:
		fprintf(fp,"\n");
		break;
	}
}
/* Free a domain message */
static void
free_dhdr(dp)
struct dhdr *dp;
{
	int i;

	if(dp->qdcount != 0){
		for(i=0;i<dp->qdcount;i++)
			free_qu(dp->qlist[i]);
		free((char *)dp->qlist);
	}
	if(dp->ancount != 0){
		for(i=0;i<dp->ancount;i++)
			free_rr(dp->ans[i]);
		free((char *)dp->ans);
	}
	if(dp->nscount != 0){
		for(i=0;i<dp->nscount;i++)
			free_rr(dp->ns[i]);
		free((char *)dp->ns);
	}
	if(dp->arcount != 0){
		for(i=0;i<dp->arcount;i++)
			free_rr(dp->add[i]);
		free((char *)dp->add);
	}
}

/* Free a question record */
static void
free_qu(qp)
struct quest *qp;
{
	free(qp->qname);
	free((char *)qp);
}

/* Free a resource record */
static void
free_rr(rrp)
struct rr *rrp;
{
	if(rrp == NULLRR)
		return;
	free(rrp->name);
	if(rrp->rdlength != 0){
		switch(rrp->type){
		case TYPE_CNAME:
		case TYPE_MB:
		case TYPE_MG:
		case TYPE_MR:
		case TYPE_NS:
		case TYPE_PTR:
			free(rrp->rdata.name);
			break;
		case TYPE_A:
			break;	/* Nothing allocated in rdata section */
		case TYPE_HINFO:
			free(rrp->rdata.hinfo.cpu);
			free(rrp->rdata.hinfo.os);
			break;
		case TYPE_MX:
			free(rrp->rdata.mx.exch);
			break;
		case TYPE_SOA:
			free(rrp->rdata.soa.mname);
			free(rrp->rdata.soa.rname);
			break;
		case TYPE_TXT:
			free(rrp->rdata.data);
			break;
		}
	}
	free((char *)rrp);
}
/* Compare two resource records, returning 0 if equal, nonzero otherwise */
static int
rrcmp(rr1,rr2)
register struct rr *rr1,*rr2;
{
	int i;

	if(rr1 == NULLRR || rr2 == NULLRR)
		return -1;
	if((i = strlen(rr1->name)) != strlen(rr2->name))
		return 1;
	if((i = strnicmp(rr1->name,rr2->name,i)) != 0)
		return i;
	if(rr1->type != rr2->type)
		return 2;
	if(rr1->class != rr2->class)
		return 3;
	/* Note: rdlengths are not compared because they vary depending
	 * on the representation (ASCII or encoded) this record was
	 * generated from.
	 */
	switch(rr1->type){
	case TYPE_A:
		i = rr1->rdata.addr != rr2->rdata.addr;
		break;
	case TYPE_SOA:
		i = rr1->rdata.soa.serial != rr2->rdata.soa.serial;
		break;
	case TYPE_HINFO:
		i = strcmp(rr1->rdata.hinfo.cpu,rr2->rdata.hinfo.cpu) ||
			strcmp(rr1->rdata.hinfo.os,rr2->rdata.hinfo.os);
		break;
	case TYPE_MX:
		i = strcmp(rr1->rdata.mx.exch,rr2->rdata.mx.exch);
		break;
	case TYPE_MB:
	case TYPE_MG:
	case TYPE_MR:
	case TYPE_NULL:
	case TYPE_WKS:
	case TYPE_PTR:
	case TYPE_MINFO:
	case TYPE_TXT:
	case TYPE_NS:
		i = strcmp(rr1->rdata.data,rr2->rdata.data);
		break;
	case TYPE_MD:
	case TYPE_MF:
	case TYPE_CNAME:
		i = strcmp(rr1->rdata.data,rr2->rdata.data);
		break;
	}
	return i;
}
/* Return 1 if string appears to be an IP address in dotted decimal;
 * return 0 otherwise (i.e., if string is a domain name)
 */
static int
isaddr(s)
register char *s;
{
	char c;

	if(s == NULLCHAR)
		return 1;	/* Can't happen */

	while((c = *s++) != '\0'){
		if(!isdigit(c) && c != '.')
			return 0;
	}
	return 1;
}
static void
dumpdomain(dhdr,rtt)
struct dhdr *dhdr;
int32 rtt;
{
	int i;
	struct rr *rrp;
	struct quest *qp;

	printf("response id %u (rtt %lu sec) qr %u opcode %u aa %u tc %u rd %u ra %u rcode %u\n",
	 dhdr->id,((long)rtt * MSPTICK)/1000,
	 dhdr->qr,dhdr->opcode,dhdr->aa,dhdr->tc,dhdr->rd,
	 dhdr->ra,dhdr->rcode);
	printf("%u questions:\n",dhdr->qdcount);
	for(i=0;i< dhdr->qdcount;i++){
		qp = dhdr->qlist[i];
		printf("%s type %u class %u\n",qp->qname,
		 qp->qtype,qp->qclass);
	}
	printf("%u answers:\n",dhdr->ancount);
	for(i=0;i< dhdr->ancount;i++){
		rrp = dhdr->ans[i];
		putrr(stdout,rrp);
	}
	printf("%u authority:\n",dhdr->nscount);
	for(i=0;i< dhdr->nscount;i++){
		rrp = dhdr->ns[i];
		putrr(stdout,rrp);
	}
	printf("%u additional:\n",dhdr->arcount);
	for(i=0;i< dhdr->arcount;i++){
		rrp = dhdr->add[i];
		putrr(stdout,rrp);
	}
	fflush(stdout);
}

