/*

  DNS Library.

  See dnslib.h for usage instructions.

  Notes:
  
	Attempts to avoid malloc where possible.
  	Lots of fun typecasting here :)
	Inverse queries are NOT implemented (Inverse queries are not the same as IN-ADDR.ARPA lookups 
		used to handle IP -> hostname lookups, and which ARE handled by this code).
	If you use TCP only for DNS lookups, automatic server switching will NOT work. You can make
		it work in the same way it works for UDP if necessary, but TCP lookups presently must be
		serialized.

  To support new RR types:
  
	1/ Add dns_decoderr_TYPE function to decode the RR (Called from dns_getanswer)
	2/ Add dns_delrr_TYPE function if necessary to deallocate memory (Called from dns_expirecache)
	3/ Create RR type and decode from RR when dns_getrr is called
	4/ Declare any additional processing (e.g. MX -> A) in dns_needaddproc (Probably not necessary)
	5/ Handle additional processing in dns_query_recurse (Probably not necessary)
	6/ If the new RR has more parts than DNSMAXPARTS, increase this figure if you want to store them

  Possible additions:
	Enable writing cache to disk
	TCP connection pool per server (Almost no lookups will require TCP)

*/

#include <time.h>
#include <string.h>

#include "dnslib.h"
#include "thread.h"
#include "../sam/mycrc.h"
#include "../sam/thread.h"
#include "../sam/debug.h"


#ifdef USE_DMALLOC
#include "../sam/dmalloc.h"
#else
void dmalloc_ignore_leak(void)
{
}
#endif

#ifndef FALSE 
#define FALSE 0
#endif
#ifndef TRUE
#define TRUE (!FALSE)
#endif

int emsg(const char * arg_list, ...);
int  imsg(const char * arg_list, ...);
int dmsg(const char * arg_list, ...);

#define dmsg dns_nolog
#if defined(DNSLIB_TEST) || defined(DNSLIB_CONSOLE)
#define imsg printf
#endif
#define DNS_USERECURSE 1	/* Request recursive lookups */
#define DNS_CACHEMAXSEC 0	/* Maximum seconds to cache RRs (0 to obey TTL) */
#define DNS_RETRYSVRINT 120	/* How often to retry preferred servers (seconds) */

Thread *thlookup;
static Mutex *m_dnsid;
static Mutex *m_dnspkt;
static Mutex *m_cache;
static Mutex *m_tcpw;
static int curserver=0;
static int svrcnt;
static int lastswitch=0;
static int timeoutms;
static int timeoutck;
#define TCPBFSZ 65536

static int sendsock;
static struct dnslist dnssrv[DNS_MAXSRV];

static char *rcodestr[]={
	"No Error",
	"Format Error",
	"Server Failure",
	"Name Error",
	"Not Implemented",
	"Refused",
	"Reserved - 0x06",
	"Reserved - 0x07",
	"Reserved - 0x08",
	"Reserved - 0x09",
	"Reserved - 0x0A",
	"Reserved - 0x0B",
	"Reserved - 0x0C",
	"Reserved - 0x0D",
	"Reserved - 0x0E",
	"Reserved - 0x0F"
};
static int dolog=FALSE;
int dns_log_lots(int x)
{
	dolog = x;	
//	dolog = TRUE;
	return TRUE;
}
#ifdef SURGEMAIL
int is_exit(void);
#endif
int dns_nolog(const char *s,...)
{
	return 0;
}

#ifdef DNS_TEST_MEMCPY
void *dns_memcpy_log(void *dest,const void *src,size_t count,char *file,int line)
{
	if (dolog) imsg("dns_memcpy - %x %x %d %s:%d\n",dest,src,count,file,line);
	return memcpy(dest,src,count);
}
#define dns_memcpy(a,b,c) dns_memcpy_log(a,b,c,__FILE__,__LINE__)
#else
#define dns_memcpy memcpy
#endif

int dns_assignid(DNSPKTD *pkt)
{
	static unsigned short static_id=0; 

	if(th_mutex_wait(m_dnsid,timeoutms)) {
		if(!static_id) static_id=1;
		pkt->header.fields.id=th_pid()+static_id++;
		th_mutex_release(m_dnsid);
		return 1;
	}
	return 0;
}

int dns_getcursvr()
{
	return curserver;
}

int dns_setcursvr(int svr)
{
	if(svr<DNS_MAXSRV) curserver=svr;
	else return 0;
	return 1;
}

int dns_nextserver()
{
	if(curserver<svrcnt-1) curserver++;
	else curserver=0;

	imsg("dns_nextserver - Server change to %d\n",curserver);
	return curserver;
}

int dns_settimeout(int ms)
{
	timeoutck=(ms/1000);
	return timeoutms=ms;
}

int dns_strcomp(char *domain,char *outbf)
{
	int l,i;
	char *s,*c,*o;
	char sdom[256];
	
	ncpy(sdom,domain,255);
	for(i=0,o=outbf;s=mystrtok(i?0:sdom,".",&c);i++) {
		l=strlen(s);
		*o++=(char)l;
		for(;l;l--) *o++=*s++;
	}
	*o++='\0';
	return o-outbf;
}

int dns_strdec(unsigned char *bfin,unsigned char *bfout,int *upto,int pktlen)/* Return the length of the resulting string */
{
	unsigned char *uin,*uout,*sin;
	int len;

	union {
		unsigned short offset;
		struct {
			unsigned char first;
			unsigned char second;
		} bytes;
	} offset;

	uin=bfin+*upto;
	uout=bfout;

	for(;1;) {
		if((*uin)&0xC0) {
			offset.offset=(unsigned short)*(unsigned short *)uin;
			offset.bytes.first=offset.bytes.first&0x3f;
			offset.offset=ntohs(offset.offset);
			if (dolog) imsg("dns_strdec - Following pointer to %d bytes from start of packet\n",offset.offset);
			sin=bfin+offset.offset;
			if(sin>=bfin+pktlen) {
				*bfout='\0';
				return uin-bfout;
			}
			uin+=2;
			if(uin>=bfin+pktlen) {
				*uout='\0';
				return 0;
			}	
			for(;1;) {
				for(len=(int)(*sin++)&0x3f;len;len--) {
					if(uout-bfout>=255) {
						*uout='\0';
						return 255;
					}
					*uout++=*sin++;
				}
				if(*sin) *uout++='.';
				else {
					*uout='\0';
					*upto=uin-bfin; /* Skip pointer */
					if (dolog) imsg("dns_strdec - String ended with pointer: \"%s\"\n",bfout);
					return uout-bfout;
				}
				if((*sin)&0xC0) {
					offset.offset=(unsigned short)*(unsigned short *)sin;
					offset.bytes.first=offset.bytes.first&0x3f;
					offset.offset=ntohs(offset.offset);
					if (dolog) imsg("dns_strdec - Following pointer to %d bytes from start of packet (Pointer from pointer)\n",offset.offset);
					sin=bfin+offset.offset;
					if(sin>=bfin+pktlen) {
						*bfout='\0';
						return uin-bfout;
					}
				}
			}
		} else {
			for(len=(int)(*uin++)&0x3f;len;len--) {
				if(uout-bfout>=255) {
					*uout='\0';
					return 255;
				}
				if(uin>=bfin+pktlen) {
					*uout='\0';
					return 0;
				}			
				*uout++=*uin++;
			}
			if(*uin) *uout++='.';
			else {
				*uout='\0';
				*upto=uin-bfin+1;
				if (dolog) imsg("dns_strdec - String ended with null: \"%s\"\n",bfout);
				return uout-bfout;
			}
		}
	}

}

void dns_append8(char *bf,int *upto,char val)
{
	bf[*upto]=val;
	(*upto)++;
}

unsigned char dns_get8(char *bf,int *upto)
{
	return bf[*upto++];
}

void dns_append16(char *bf,int *upto,unsigned short val)
{
	val=htons(val);
	dns_memcpy(&bf[*upto],(char *)&val,2);
	*upto+=2;
}

unsigned short dns_get16(char *bf,int *upto)
{
	unsigned short val=ntohs((unsigned short)*((unsigned short *)&bf[*upto]));
	*upto+=2;
	return val;
}

void dns_append32(char *bf,int *upto,unsigned long val)
{
	val=htonl(val);
	dns_memcpy(&bf[*upto],(char *)&val,4);
	*upto+=4;
}

unsigned long dns_get32(char *bf,int *upto)
{
	unsigned long val=ntohl((unsigned long)*((unsigned long *)&bf[*upto]));
	*upto+=4;
	return val;
}

void dns_appendbin(char *bf,int *upto,char *bin,int len)
{
	dns_memcpy(&bf[*upto],bin,len);
	*upto+=len;
}

void dns_delrr_ns(struct dnsanswer *ans)
{
	free((void *)ans->rrdecoded.parts[0]);
}

void dns_delrr_mx(struct dnsanswer *ans)
{
	free((void *)ans->rrdecoded.parts[1]);
}

void dns_delrr_txt(struct dnsanswer *ans)
{
	free((void *)ans->rrdecoded.parts[0]);
}

void dns_delrr_ptr(struct dnsanswer *ans)
{
	free((void *)ans->rrdecoded.parts[0]);
}

void dns_delrr_cname(struct dnsanswer *ans)
{
	free((void *)ans->rrdecoded.parts[0]);
}

/*

  Cache management

*/

// shrunk cache to help test and find any bugs in it.
#define CACHEHASH 10000
//#define CACHEHASH 1000
static struct dnsanswer *dnscache[CACHEHASH];
static int cachemaxsz=10000;
static int cachetrimsz=5000;
//static int cachemaxsz=1000;
//static int cachetrimsz=800;
static int curcachesz=0;

int dns_cacheinit()
{
	int i;

	for(i=0;i<CACHEHASH;i++) dnscache[i]=(struct dnsanswer *)0;
	return 1;
}

int dns_getcachesz()
{
	return curcachesz;
}

int dns_addcacherr(struct dnsanswer *an)
{
	int hashpos;

	if(!an) return 0;		
	hashpos=do_crc(an->domain,an->dlen)%CACHEHASH; if (hashpos<0) hashpos = -hashpos;

	if (dolog) imsg("dns_addcacherr - Caching RR for %s, type %d, expiring %d\n",an->domain,an->qtype,an->expires);

	if(th_mutex_wait(m_cache,timeoutms)) {
		an->next=dnscache[hashpos];
		dnscache[hashpos]=an;
		curcachesz++;
		th_mutex_release(m_cache);
		return 1;
	}
	return 0;
}

int dns_expirecache(int force, int all)
{	/* Setting force to non-zero causes the cache to be trimmed to around cachetrimsz */
	int i;
	int forceupto,forcefrom;
	unsigned long now;
	struct dnsanswer *can,*pan,*nan;

	if(force) {
		if (dolog) imsg("dns_expirecache - Forced trimming of DNS cache. Current: %d Maximum: %d Trim to: %d\n",curcachesz>cachemaxsz,cachetrimsz);
	} else if(curcachesz>cachemaxsz) {
		if (dolog) imsg("dns_expirecache - Maximum cache size exceeded (%d>%d), reducing to around %d\n",curcachesz>cachemaxsz,cachetrimsz);
		force=1;
	}
	forcefrom=cachetrimsz/CACHEHASH;
	if(th_mutex_wait(m_cache,force?timeoutms:10)) {
		for(i=0,now=time(NULL);i<CACHEHASH;i++) {
			for(can=dnscache[i],forceupto=0,pan=0;can;can=nan) {
				nan=can->next;
				if (!all) if(forceupto++<forcefrom) continue;
				if((can->expires<now || force) && !can->refcnt) {
					if(pan) pan->next=can->next;
					else dnscache[i]=0;
					free(can->domain);
					switch(can->qtype) {
					case DT_NS:
						dns_delrr_ns(can);
						break;
					case DT_MX:
						dns_delrr_mx(can);
						break;
					case DT_TXT:
						dns_delrr_txt(can);
						break;
					case DT_PTR:
						dns_delrr_ptr(can);
						break;
					case DT_CNAME:
						dns_delrr_cname(can);
						break;
					default:
						break;
					}
					free(can);
					curcachesz--;
					continue;
				}
				pan=can;
			}
		}
		if(force) {
			if (dolog) imsg("dns_expirecache - New cache size %d/%d\n",curcachesz,cachemaxsz);
		}
		th_mutex_release(m_cache);
	}
	return 1;
}

int dns_querycache(char *domain,unsigned short type,struct dnsanswer **an,int cancnt,int maxans)
{
	struct dnsanswer *can;
	int ancnt;
	unsigned long now;
	int hashpos;
	
	ancnt=0;
	hashpos=do_crc(domain,strlen(domain))%CACHEHASH; if (hashpos<0) hashpos = -hashpos;
	now=time(NULL);
	if(th_mutex_wait(m_cache,100)) {
		for(ancnt=cancnt,can=dnscache[hashpos];can && ancnt<maxans;can=can->next) {
			if(can->qtype==type && !strcmp(can->domain,domain)) {
				if(can->expires>=now) {
					can->refcnt++;
					if (dolog) imsg("dns_querycache - Cache entry for %s type %d found. Incremented refcnt to %d\n",domain,type,can->refcnt);
					if(ancnt<MAXANSW) an[ancnt++]=can;
					else {
						if (dolog) imsg("dns_querycache - Too many resource records, lookup may be incomplete. Host: %s Type: %d\n",domain,type);
					}
				}
			}
		}
		th_mutex_release(m_cache);
	} else {
		if (dolog) imsg("dns_querycache - WARNING: Timeout obtaining mutex (m_cache)\n");
	}

	return ancnt-cancnt;

}

int dns_needaddproc(int type)
{
	if(type==DT_MX) return 1;
	if(type==DT_NS) return 1;
	return 0;
}

int dns_decoderr_a(char *pkt,int upto,struct dnsanswer *ans, int pktlen)
{
	int i,acnt;

	acnt=ans->rdlength/4;
	if(acnt>DNSMAXPARTS) acnt=DNSMAXPARTS;

	for(i=0;i<DNSMAXPARTS;i++) ans->rrdecoded.parts[i]=0;
	for(i=0;i<acnt;i++) {
		ans->rrdecoded.parts[i]=(*(unsigned long *)(pkt+upto));
		upto+=4;
	}
	return 1;
}

int dns_decoderr_ns(char *pkt,int upto,struct dnsanswer *ans,int pktlen)
{
	char out[265];

	dns_strdec(pkt,out,&upto,pktlen);
	ans->rrdecoded.parts[0]=(int)strdup(out);
	return 1;
}

int dns_decoderr_mx(char *pkt,int upto,struct dnsanswer *ans,int pktlen)
{
	char out[265];

	ans->rrdecoded.parts[0]=dns_get16(pkt,&upto);
	dns_strdec(pkt,out,&upto,pktlen);
	ans->rrdecoded.parts[1]=(int)strdup(out);

	if (dolog) imsg("dns_decoderr_mx - Host: %s Preference: %d\n",(char *)ans->rrdecoded.parts[1],(unsigned short)ans->rrdecoded.parts[0]);
	return 1;
}

int dns_decoderr_txt(char *pkt,int upto,struct dnsanswer *ans,int pktlen)
{
	ans->rrdecoded.parts[0]=(int)malloc(ans->rdlength+1);
	if(!ans->rrdecoded.parts[0]) return 0;

	dns_memcpy((char *)ans->rrdecoded.parts[0],pkt+upto+1,ans->rdlength);
	((char *)ans->rrdecoded.parts[0])[ans->rdlength-1]='\0';

	return 1;
}

int dns_decoderr_ptr(char *pkt,int upto,struct dnsanswer *ans, int pktlen)
{
	char bf[256];
	
	dns_strdec(pkt,bf,&upto,  pktlen);
	ans->rrdecoded.parts[0]=(int)strdup(bf);
	return 1;
}

int dns_decoderr_cname(char *pkt,int upto,struct dnsanswer *ans, int pktlen)
{
	char bf[256];
	
	dns_strdec(pkt,bf,&upto, pktlen);
	ans->rrdecoded.parts[0]=(int)strdup(bf);
	return 1;
}

struct dnsanswer *dns_getanswer(char *pkt,int *upto,int wanttype,int pktlen)
{
	char out[256];
	unsigned int ttl;

	struct dnsanswer *ans=calloc(1,sizeof(struct dnsanswer));

	dns_strdec(pkt,out,upto, pktlen);

	if(*upto>=pktlen) return 0;

	ans->qtype=dns_get16(pkt,upto);
	ans->qclass=dns_get16(pkt,upto);
	ttl=dns_get32(pkt,upto);
	if(DNS_CACHEMAXSEC && ttl>DNS_CACHEMAXSEC) ttl=DNS_CACHEMAXSEC;
	ans->expires=time(0)+ttl;
	ans->rdlength=dns_get16(pkt,upto);
//	ans->rdata=&pkt[*upto];

	ans->domain=strdup(out);
	if(dolog) imsg("dns_getanswer - Free Ans: %p Domain: %p\n",ans,ans->domain);
	ans->dlen=strlen(out);
	ans->refcnt=1;

	switch(ans->qtype) {
	case DT_A:
		dns_decoderr_a(pkt,*upto,ans,pktlen);
		break;
	case DT_NS:
		dns_decoderr_ns(pkt,*upto,ans,pktlen);
		break;
	case DT_MX:
		dns_decoderr_mx(pkt,*upto,ans,pktlen);
		break;
	case DT_TXT:
		dns_decoderr_txt(pkt,*upto,ans,pktlen);
		break;
	case DT_PTR:
		dns_decoderr_ptr(pkt,*upto,ans,pktlen);
		break;
	case DT_CNAME:
		dns_decoderr_cname(pkt,*upto,ans,pktlen);
		break;
	default:
		break;
	}

	if (dolog) imsg("dns_getanswer - Got answer for %s type %d ttl %d (%d)\n",ans->domain,ans->qtype,ttl,ans->expires);

	dns_addcacherr(ans); /* Place answer in cache for future use */

	*upto+=ans->rdlength;

	return ans;
}

struct dnsquestion *dns_getquestion(char *pkt,int *upto, int pktlen)
{
	char out[256];
	int qtype,qclass;

	dns_strdec(pkt,out,upto,pktlen);

	qtype=(int)dns_get16(pkt,upto);
	qclass=(int)dns_get16(pkt,upto);

	if (dolog) imsg("dns_getquestion - domain: %s qtype: %d qclass: %d\n",out,qtype,qclass); 
	
	return 0;
}

static DNSPKTD *waitpackets[PKTHASH]={0};

DNSPKTD *dns_getpacketd(unsigned short id)
{
	int pkthsh;
	DNSPKTD *pd;

	pkthsh=id%PKTHASH;

	if(th_mutex_wait(m_dnspkt,timeoutms)) {
		for(pd=waitpackets[pkthsh];pd;pd=pd->next) {
			if(pd->header.fields.id==id && !pd->failed && pd->wait) break;
		}
		th_mutex_release(m_dnspkt);
	}
	return pd;
}

DNSPKTD *dns_addpaketd(DNSPKTD *pd)
{
	int pkthsh;
	
	pkthsh=pd->header.fields.id%PKTHASH;
	pd->prev=0;
	
	if(th_mutex_wait(m_dnspkt,timeoutms)) {
		if(waitpackets[pkthsh]) waitpackets[pkthsh]->prev=pd;
		pd->next=waitpackets[pkthsh];
		waitpackets[pkthsh]=pd;
		th_mutex_release(m_dnspkt);
		return pd;
	}

	return 0;
}

void dns_failpktd(DNSPKTD *pk,int reason)
{
	pk->reason=reason;
	pk->failed=1;
}

int dns_killpacketd(DNSPKTD *pk)
{
	if(th_mutex_wait(m_dnspkt,timeoutms)) {
		free(pk->question.domain);
		if(pk->next) pk->next->prev=pk->prev;
		if(pk->prev) pk->prev->next=pk->next;
		else waitpackets[pk->header.fields.id%PKTHASH]=pk->next;
		free(pk);
		th_mutex_release(m_dnspkt);
	}
	return 0;
}

int dns_expireall()
{
	int x;
	int expcnt;
	int allcnt;
	time_t now;
	int is_ex;
	DNSPKTD *cp,*np;
	static int lastserver=0;
	static int tocount=0;

	is_ex=0;
#ifdef SURGEMAIL
	is_ex=is_exit();
#endif

	if(th_mutex_wait(m_dnspkt,10)) {
		for(x=0,expcnt=0,allcnt=0;x<PKTHASH;x++) {
			for(now=time(NULL),cp=waitpackets[x];cp;cp=cp->next) {
				if((now-cp->ck>timeoutck || is_ex) && !cp->failed) {
					for(;cp;cp=np) {
						expcnt++;
						if (dolog) imsg("dns_expireall - Discarding outdated packet (%d)\n",cp->header.fields.id);
						np=cp->next;
					//	dns_killpacketd(cp,DNS_TIMEOUT);
						dns_failpktd(cp,DNS_TIMEOUT);
					}
					break;
				}
				allcnt++;
			}
		}
		th_mutex_release(m_dnspkt);	
		if(allcnt && expcnt==allcnt) {
			/* Don't allow the server to switch servers for a single timeout */
			if(lastserver==(x=dns_getcursvr()) && allcnt==1) {
				tocount++;
				if(tocount>1) return 1;
			} else {
				tocount=0;
				lastserver=x;
			}
		}
	}
	return 0;
}

int dns_doquery(short type,int isinverse,char *domain,DNSPKTD **opk,struct dnsanswer **an,int anupto,int transport)
{
	int upto;
	char pkt[1024];
	int i;
	DNSPKTD *qr;
	char cdom[256];

	if (dolog) imsg("dns_doquery - domain: %s type: %d\n",domain,type);

	*opk=qr=calloc(1,sizeof(DNSPKTD));
	if(!qr) return 0;

	if(!dns_assignid(qr)) return 0;

	qr->header.fields.opcode=isinverse?1:0;
	qr->header.fields.qdcount=1;
	qr->header.fields.rd=DNS_USERECURSE;

	qr->question.dlen=(unsigned char)dns_strcomp(domain,cdom);
	qr->question.domain=strdup(cdom);
	if((!qr->question.domain) || qr->question.dlen<=0) return 0;
	qr->question.qtype=type;
	qr->question.qclass=DC_IN;
	qr->an=an;
	qr->anupto=anupto;
	qr->wait=1;
	qr->failed=0;

//	qr->serverid=dns_getcursvr();
	qr->ck=time(NULL);
	dns_addpaketd(qr);

	upto=0;
	/* Header */
	dns_append16(pkt,&upto,qr->header.data[0]);
	dns_append8(pkt,&upto,(char)*(&qr->header.data[1]));
	dns_append8(pkt,&upto,(char)*(&qr->header.data[1]+1));
	for(i=2;i<6;i++) dns_append16(pkt,&upto,qr->header.data[i]);

	/* Question */
	dns_appendbin(pkt,&upto,qr->question.domain,qr->question.dlen);
	dns_append16(pkt,&upto,qr->question.qtype);
	dns_append16(pkt,&upto,DC_IN);

	/* Dump packet contents */

#ifdef TESTDNS
	printf("dns_doquery - Packet dump\n");
	for(i=0;i<upto;i++) if (dolog) imsg("dns %0.4d %0.3x (%c)\n",i,pkt[i],pkt[i]);
	printf("dns_query - End packet dump\n");
#endif

	if(transport==DNS_TRANSUDP) {
		if((qr->serverid=dns_getcursvr()) && lastswitch+DNS_RETRYSVRINT>time(NULL)) { /* Attempt higher priority DNS servers periodically */
			qr->allowhi=1;
			for(i=0;i<qr->serverid;i++) sendto(sendsock,pkt,upto,0,(struct sockaddr *)&dnssrv[i].addr,sizeof(struct sockaddr));	
		}
		return upto==sendto(sendsock,pkt,upto,0,(struct sockaddr *)&dnssrv[qr->serverid].addr,sizeof(struct sockaddr));
	} else if(transport==DNS_TRANSTCP) {
		int tcpstatus;
		short lennbo;
		int csrv;
		
		tcpstatus=DNS_NOCHANN;
		lennbo=htons((short)upto);
		qr->serverid=csrv=dns_getcursvr();

		if(th_mutex_wait(m_tcpw,100)) { /* Mutex TCP writes to ensure size and data are both sent */
			if(dnssrv[csrv].tcpchan && (tcp_write(dnssrv[csrv].tcpchan,(char *)&lennbo,2)==2))
				if(tcp_write(dnssrv[csrv].tcpchan,pkt,upto)==upto) {
					th_mutex_release(m_tcpw);
					return 1;
				}

			/*
				Sending to the current server failed, try to find another server to take our connection.
				The dns_thlookup thread will attempt to restore any dropped connection without blocking.
			*/
			for(qr->serverid=0;qr->serverid<svrcnt && tcpstatus==DNS_NOCHANN;i++) {
				if(dnssrv[qr->serverid].tcpchan && (tcp_write(dnssrv[qr->serverid].tcpchan,(char *)&lennbo,2)==2))
					if(tcp_write(dnssrv[qr->serverid].tcpchan,pkt,upto)==upto) tcpstatus=DNS_NOERROR;
					else {
						th_mutex_release(m_tcpw);
						return 1;
					}
			}
			th_mutex_release(m_tcpw);
		}
		
		if(tcpstatus==DNS_NOCHANN) { /* We can't handle the request at the moment */
			dns_failpktd(qr,DNS_NOCHANN);
		}
	}
	return 0;
}
int dns_thlookup(void *xxx)
{
	unsigned char *rpkt;
	char rpktudp[513];
	struct timeval tv;
	fd_set readfds;
	int i;
	int r;
	int ic;
	int x;
	int want;
	int upto;
	int toomany;
	int len;
	struct sockaddr_in raddr;
	DNSPKTD rs,*qr;
	int pktlen;

	for(;1;) {
#ifdef SURGEMAIL
		if (is_exit()) break;
#endif
		tv.tv_sec=0;
        tv.tv_usec=100000;
		FD_ZERO(&readfds);
		FD_SET(sendsock,&readfds);
		for(i=0;i<svrcnt;i++) {
			if(tcp_isopen(dnssrv[i].tcpchan)) {
				FD_SET(dnssrv[i].tcpchan->sk,&readfds);
			} else { /* Open TCP connection to server if necessary */
				tcp_open_noblock(dnssrv[i].tcpchan,dnssrv[i].host,dnssrv[i].port,0);
				tcp_noblock(dnssrv[i].tcpchan);
				tcp_nodelay(dnssrv[i].tcpchan,1);
				dnssrv[i].tcppktupto=0;
			}
		}
		r=select(FD_SETSIZE,&readfds,0,0,&tv);
		if((!r) || (curcachesz>cachemaxsz+cachetrimsz)) {
			if(dns_expireall()) dns_nextserver();
			dns_expirecache(0,FALSE);
			continue;
		}

		for(ic=0;ic<=svrcnt;ic++) {
			if(ic==svrcnt) { /* Incoming packet is from UDP server */
				if(!FD_ISSET(sendsock,&readfds)) {
					continue;
				}
				len=sizeof(struct sockaddr);
				r=recvfrom(sendsock,rpktudp,513,0,(struct sockaddr *)&raddr,&len);
				if(len>512) {
					imsg("dns_thlookup - Ignoring UDP response larger than 512 octets (RFC 1035)\n");
					continue;
				}
				if (r<0) {
					imsg("dns_thlookup - Read error on socket?\n");
					continue; 
				}
				rpkt=rpktudp;
				pktlen=r;
			} else { /* Incoming packet is from TCP server */
				if(!FD_ISSET(dnssrv[ic].tcpchan->sk,&readfds)) {
					continue;
				}
				if(!dnssrv[ic].tcppktupto) {
					r=tcp_read(dnssrv[ic].tcpchan,(char *)&len,2);
					if(r!=2) {
						if (dolog) imsg("dns_thlookup - closing tcp channel as read %d bytes\n",r);
						dnssrv[ic].tcppktupto=0;
						tcp_close_nice(dnssrv[ic].tcpchan);
						continue;
					}
					dnssrv[ic].tcppktsz=ntohs((unsigned short)len);
				}
				want = dnssrv[ic].tcppktsz-dnssrv[ic].tcppktupto;
				if ((want >= TCPBFSZ) || (want<0)) { // INVALID INCOMING PACKET SIZE 
					emsg("dns: invalid large packet %d\n",want);
					dnssrv[ic].tcppktupto=0;
					tcp_close_nice(dnssrv[ic].tcpchan);
					continue;
				}
				r=tcp_read(dnssrv[ic].tcpchan,dnssrv[ic].tcpbf+dnssrv[ic].tcppktupto,want);
				if(r<=0) { /* Error or disconnected */
					dnssrv[ic].tcppktupto=0;
					tcp_close_nice(dnssrv[ic].tcpchan);
					continue;
				}
				dnssrv[ic].tcppktupto+=r;
				if(dnssrv[ic].tcppktupto!=dnssrv[ic].tcppktsz) continue;
				pktlen=dnssrv[ic].tcppktsz;
				dnssrv[ic].tcppktupto=0;
				rpkt=dnssrv[ic].tcpbf;
			}
			dns_memcpy(&rs.header.data,rpkt,sizeof(rs.header.data));
			
			rs.header.data[0]=ntohs(rs.header.data[0]);
			for(i=2;i<6;i++) rs.header.data[i]=ntohs(rs.header.data[i]);
			upto=sizeof(rs.header.data);

			/* Be paranoid... ensure the packet is one we are looking for - prevent DoS */
			if(!(qr=dns_getpacketd((unsigned short)rs.header.fields.id))) {
				if (dolog) imsg("dns_thlookup - Ignoring unknown/expired packet with ID %d\n",(int)rs.header.fields.id);
				continue;
			}

			/* If a truncated UDP response, return a DNS_TRUNCAT error */
			if((char *) rpkt==rpktudp && rs.header.fields.tc) {
				dns_failpktd(qr,DNS_TRUNCAT);
				continue;
			}

			/* Check status, kill the lookup if query failed */
			if(qr->header.fields.rcode!=DNS_NOERROR)	{
				dns_failpktd(qr,qr->header.fields.rcode);
				continue;
			}


			/* Check for results from other servers we may care about and adjust current server */
			/* Higher priority servers will be periodically tried */
			if(ic==svrcnt && raddr.sin_addr.s_addr!=dnssrv[qr->serverid].addr.sin_addr.s_addr) {
				for(i=0;i<qr->serverid;i++) {
					if(raddr.sin_addr.s_addr==dnssrv[i].addr.sin_addr.s_addr) {
						if(qr->allowhi) {
							imsg("dns_thlookup - Higher priority server responded. Setting current DNS server to %s:%d (%d)\n",dnssrv[i].host,dnssrv[i].port,i);
							dns_setcursvr(i);
						} else {
							imsg("dns_thlookup - WARNING - Possible DNS spoofing attack. Higher priority server responded with known packet ID without being queried");
							continue;
						}
					}
				}
				if(i==qr->serverid) {
					imsg("dns_thlookup - Packet not from permitted source. Ignoring. %d %d\n",i,qr->serverid);
					continue;
				}
			}

			/* Copy some details we'll need later */
			qr->header.fields.qdcount=rs.header.fields.qdcount;
			qr->header.fields.ancount=rs.header.fields.ancount;
			qr->header.fields.nscount=rs.header.fields.nscount;
			qr->header.fields.arcount=rs.header.fields.arcount;

			for(i=0;i<(int)rs.header.fields.qdcount;i++) dns_getquestion(rpkt,&upto,pktlen);
			if (upto>pktlen) {
					emsg("dns_thlookup - packet exceeded at question stage %d %d\n",upto,pktlen);
					continue;
			}
			toomany=0;
			for(i=0;i<(int)rs.header.fields.ancount;i++) {
				qr->an[qr->anupto++]=dns_getanswer(rpkt,&upto,qr->question.qtype,pktlen);
				if(upto>pktlen) break;
				if(qr->anupto==MAXANSW) { toomany=1; break; }
			}
/* I don't think we need this since we are assuming recursive lookups - RL
			if(!i) { // If we didn't get the information we need, consult the given name servers
				for(i=0;i<(int)rs.header.fields.nscount;i++) {
					qr->an[qr->anupto++]=dns_getanswer(rpkt,&upto,qr->question.qtype);
					if(qr->anupto==MAXANSW) { toomany=1; break; }
				}
			}
*/
			if(!i && qr->question.qtype!=DT_MX) { /* Allow fallback to A record for MX */
				dns_failpktd(qr,DNS_INVALID);
				continue;
			}

			if(toomany) {
				if (dolog) imsg("dns_thlookup - Too many resource records, lookup may be incomplete.\n");
			}

			qr->wait=0;

		}
		dns_expireall();
	}
	dns_expireall();
	for(x=0;x<svrcnt;x++) {
		tcp_free(&dnssrv[x].tcpchan);
		free(dnssrv[x].host); dnssrv[x].host = NULL;
		free(dnssrv[x].tcpbf); dnssrv[x].tcpbf = NULL;
	}

	return 0;
}

int dns_init(char **serverlist,int lsvrcnt,int dothread)
{
	int x;
	char *s;
	char xip[BFSZ];
	sendsock=socket(AF_INET, SOCK_DGRAM, 0);
	if(dothread) thlookup=th_new(dns_thlookup,0);
	timeoutms=2000;
	timeoutck=(timeoutms/1000);

	if(lsvrcnt>DNS_MAXSRV) lsvrcnt=DNS_MAXSRV;
	for(x=0;x<lsvrcnt;x++) {
		if (!tcp_lookup(serverlist[x],xip)) emsg("dns: lookup failed (%s)\n",serverlist[x]);
		dnssrv[x].host=strdup(xip); dmalloc_ignore_leak();
		if(!dnssrv[x].host) return 0;
		if(s=strchr(dnssrv[x].host,':')) {
			*s='\0';
			dnssrv[x].port=atoi(s+1);
		}
		if(!dnssrv[x].port) dnssrv[x].port=53;

        memset(&dnssrv[x].addr,0,sizeof(dnssrv[x].addr));
        dnssrv[x].addr.sin_family=AF_INET;
		dnssrv[x].addr.sin_addr.s_addr=inet_addr(dnssrv[x].host);
        dnssrv[x].addr.sin_port=htons(dnssrv[x].port);
        memset(&(dnssrv[x].addr.sin_zero),'\0',8);
		dnssrv[x].tcpchan=0;
		tcp_new(&dnssrv[x].tcpchan); dmalloc_ignore_leak();
		if(!dnssrv[x].tcpchan) return 0;
		dnssrv[x].tcpbf=malloc(TCPBFSZ+1);dmalloc_ignore_leak();
		if(!dnssrv[x].tcpbf) return 0;
	}
	if(!x) return 0;

	svrcnt=lsvrcnt;

	m_dnsid=th_mutex_new("DNS_ID");
	m_dnspkt=th_mutex_new("DNS_PKT");
	m_cache=th_mutex_new("DNS_CACHE");
	m_tcpw=th_mutex_new("DNS_TCPW");

	return 1;
}
static int stat_nok[DNS_MAXSRV];
static int stat_nbad[DNS_MAXSRV];
int dnslib_stats(int worked, int n)
{
	if (n<0) return FALSE;
	if (n>=DNS_MAXSRV) return FALSE;
	if (worked) {
		stat_nok[n]++;
	} else {
		stat_nbad[n]++;
	}
	return TRUE;
}
char *dnslib_status(int i, char *tmp)
{
	if (i<0) return NULL;
	if (i>=svrcnt) return NULL;
	sprintf(tmp,"%.10sDNS %.50s, nok=%d nbad=%d",curserver==i ? "->" : "",dnssrv[i].host
		,stat_nok[i]
		,stat_nbad[i]);
	return tmp;
}

int dns_query_recurse(char *host,unsigned short type,struct dnsanswer *an[MAXANSW],int *ancnt,struct dnslist ldnssrv[DNS_MAXSRV],int *error,int transport)
{
	DNSPKTD *pk;
	int rrcnt,lrrcnt;
	int gotit,havens;
	int anupto;
	struct dnslist *xdnssrv;
	int rd;
	int israw;

	pk=0;
	israw=0;
	gotit=0;
	havens=0;
	anupto=*ancnt;
	xdnssrv=anupto?ldnssrv:dnssrv;

	if(type==DT_MX_RAW) {
		type=DT_MX;
		israw=1;
	}

	if(type<DT_LOW||type>DT_HIGH) return 0;
	for(rrcnt=0;rrcnt<MAXANSW && !anupto;rrcnt++) an[rrcnt]=0;

	if(!(rrcnt=dns_querycache(host,type,an,*ancnt,MAXANSW))) {		
		if(!dns_doquery(type,0,host,&pk,an,*ancnt,transport)) {
			free(pk);
			return 0;
		}
		for(;pk->wait && !pk->failed;) {
			lib_msleep(10);
		//	if(time(NULL)-pk->ck>timeoutck) break;
		}
		if(pk->failed) {
			*error=pk->reason;
			dns_killpacketd(pk);
			return 0;
		}
		rrcnt=pk->header.fields.ancount+pk->header.fields.nscount+pk->header.fields.arcount;

		if(pk->wait) {
			if (dolog) imsg("dns_query_recurse - Timeout waiting for response (domain: %s qtype: %d id: %d)\n",host,(int)type,pk->header.fields.id);
			*error=DNS_TIMEOUT;
			return 0;
		} else if (dolog) imsg("dns_query_recurse - Got response (domain: %s qtype: %d)\n",host,(int)type);
	} else if(rrcnt>0) {
		if (dolog) imsg("dns_query_recurse - Got cached response (domain: %s qtype: %d rrcnt: %d)\n",host,(int)type,rrcnt);
	}

	if(rrcnt<0) rrcnt=0;
	*ancnt+=rrcnt;
	if(*ancnt>MAXANSW) *ancnt=MAXANSW;

	/* Do any additional processing */
	for(lrrcnt=anupto,gotit=0;lrrcnt<anupto+rrcnt;lrrcnt++) {
		if(!an[lrrcnt]) continue;
		if(an[lrrcnt]->qtype==type) {
			if(dns_needaddproc(type)) {
				switch(type) {
				case DT_MX:
					if(israw) break;
					if(rd=dns_query_recurse((char *)an[lrrcnt]->rrdecoded.parts[1],DT_A,an,ancnt,xdnssrv,error,transport)) {
						struct sockaddr_in raddr;
						raddr.sin_addr.s_addr=an[*ancnt-rd]->rrdecoded.parts[0];
						if (dolog) imsg("dns_query_recurse - Got A record for MX %s (%s)\n",(char *)an[lrrcnt]->rrdecoded.parts[1],inet_ntoa(raddr.sin_addr));
					}
					if(*error!=DNS_NOERROR) {
						if(pk) dns_killpacketd(pk);
						return 0;
					}
					break;
				default:
					break;
				}
			}
			gotit=1;
		}
		if(an[lrrcnt]->qtype==DT_NS && !gotit) { /* Name server that should be able to answer us */
			if(havens) continue; /* Already tried one NS successfully */


			if (dolog) imsg("dns_query_recurse - NON-RECURSIVE LOOKUPS NOT IMPLEMENTED. Ensure your name servers support recursive lookups\n");


/* THIS CODE SHOULD DO A QUERY AGAINST THE SPECIFIED MX SERVERS IN A RESPONSE!

  Needed if the server you are connecting to doesn't support recursive lookups (Or you don't request them).

  ChrisP said not to bother, I agree it probably isn't necessary. Should you want to use this code, you need to ensure the xdnssrv list
  is updated with the A records from the servers returned from the query, dns_doquery sends to the right servers, and 
  dns_thlookup to accept responses from them. Should be simple enough.

  - RL.


			if(rd=dns_query_recurse((char *)an[lrrcnt]->rrdecoded.parts[0],DT_A,an,ancnt,xdnssrv)) {
				ldnssrv[0].host=(char *)an[lrrcnt]->rrdecoded.parts[0];
				ldnssrv[0].addr.sin_addr.s_addr=an[*ancnt-rd]->rrdecoded.parts[0];

				if (dolog) imsg("dns_query_recurse - Got A record for NS %s (%s)\n",(char *)an[lrrcnt]->rrdecoded.parts[0],inet_ntoa(ldnssrv[0].addr.sin_addr)); 

				ldnssrv[0].port=53;
				havens=1;
				break;
			}
			if(*error!=DNS_NOERROR) return 0;
*/

		}
	}

	if(pk) dns_killpacketd(pk);

	if(gotit) { /* Construct data to return... */
		dnslib_stats(TRUE, curserver);
		return *ancnt-anupto;
	} else if(havens) { /* We have to try other name servers to get an answer */
		return dns_query_recurse(host,type,an,ancnt,xdnssrv,error,transport);
	} else if(type==DT_MX) {
		return dns_query_recurse(host,DT_A,an,ancnt,xdnssrv,error,transport); /* Do an A record lookup to use as MX */
	}
	dnslib_stats(FALSE, curserver);
	return 0;
}

struct dnsanswer *dns_getrr_local(struct dnsquery *dq,char *host,int type)
{
	int i;
	for(i=0;i<dq->ancnt;i++) {
		if(!dq->an[i]) continue;
		if(type==dq->an[i]->qtype && !strcmp(host,dq->an[i]->domain)) return dq->an[i];
	}
	return 0;
	
}

void *dns_getrr(struct dnsquery *dq)
{
	int done;
	struct sockaddr_in ain;
	struct dnsanswer *an;
	int addproc;
	
	if(!dq) return 0;
	if(dq->rrupto>dq->ancnt) return 0;
	for(done=0,addproc=0;dq->rrupto<dq->ancnt;dq->rrupto++) {
		if(!dq->an[dq->rrupto]) continue;
		if(dq->an[dq->rrupto]->qtype==dq->type) {
			dq->gotans++;
			break;
		}
	}
	if(dq->rrupto>=dq->ancnt) {
		if(!dq->gotans) {
			if(dq->type==DT_MX && !dq->israw) { /* Special case, fallback to A record */
				an=dns_getrr_local(dq,dq->host,DT_A);
				addproc=1;
				if(!an) return 0;
			} else return 0;
		} else return 0;
	}
	switch(dq->type) {
	case DT_MX:
		{
			struct dnsrr_mx *rr;
			rr=(struct dnsrr_mx *)dq->work;
			if(!addproc) { 
				rr->host=(char *)dq->an[dq->rrupto]->rrdecoded.parts[1];
				rr->pref=dq->an[dq->rrupto]->rrdecoded.parts[0];
				an=dns_getrr_local(dq,rr->host,DT_A);
				if(dq->israw) {
					if(!an) if(dolog) imsg("dns_getrr - Returning raw MX data that is known to be incorrect. MX RR must have an A record!");
					dq->rrupto++;
					done=1;
				} else if(!an) {
					if (dolog) imsg("dns_getrr - Skipping MX record with no coresponding A record (%s)\n",rr->host);
				} else {
					rr->addr=an->rrdecoded.parts[0];
					ain.sin_addr.s_addr=rr->addr;
					strcpy(rr->ascaddr,inet_ntoa(ain.sin_addr));
					dq->rrupto++;
					done=1;
				}
			} else { /* Special case, fallback to A record */
				rr->host=dq->host;
				rr->pref=-1;
				rr->addr=an->rrdecoded.parts[0]; /* First address in A record response */
				ain.sin_addr.s_addr=rr->addr;
				strcpy(rr->ascaddr,inet_ntoa(ain.sin_addr));
				dq->rrupto++;
				done=1;
			}
		}
		break;
	case DT_A:
		{
			struct dnsrr_a *rr;
			rr=(struct dnsrr_a *)dq->work;
			for(rr->addrcnt=0;rr->addrcnt<DNSMAXPARTS;rr->addrcnt++) {
				rr->addr[rr->addrcnt]=dq->an[dq->rrupto]->rrdecoded.parts[rr->addrcnt];
				if(!rr->addr[rr->addrcnt]) break;
				ain.sin_addr.s_addr=rr->addr[rr->addrcnt];
				strcpy(rr->ascaddr[rr->addrcnt],inet_ntoa(ain.sin_addr));
			}
			dq->rrupto++;
			done=1;
		}
		break;
	case DT_TXT:
		{
			struct dnsrr_txt *rr;
			rr=(struct dnsrr_txt *)dq->work;
			rr->len=dq->an[dq->rrupto]->rdlength;
			rr->text=(char *)dq->an[dq->rrupto]->rrdecoded.parts[0];
			dq->rrupto++;
			done=1;
		}
		break;
	case DT_PTR:
	{
		struct dnsrr_ptr *rr;
		rr=(struct dnsrr_ptr *)dq->work;
		rr->domain=(char *)dq->an[dq->rrupto]->rrdecoded.parts[0];
		dq->rrupto++;
		done=1;
	}
	break;
	case DT_CNAME:
	{
		struct dnsrr_cname *rr;
		rr=(struct dnsrr_cname *)dq->work;
		rr->domain=(char *)dq->an[dq->rrupto]->rrdecoded.parts[0];
		dq->rrupto++;
		done=1;
	}
	break;
	default:
		if (dolog) imsg("dns_getrr - Don't know how to format type %d RRs. Please add code to handle this!\n",dq->type);
	}
	return done?(void *)&dq->work:0;
}

void dns_rewindrr(struct dnsquery *dq)
{
	dq->rrupto=0;
}

struct dnsquery *dns_query_t(char *host,unsigned short type,int transport)
{

	DNSQUERY *qw;

	qw=calloc(1,sizeof(DNSQUERY));
	if(!qw) return 0;
	qw->host=strdup(host);
	if(!qw->host) {
		free(qw);
		return 0;
	}
	if (type==DT_MX_RAW) {
		qw->israw = TRUE;
	}
	qw->type=type;
	qw->ancnt=0;
	qw->status=DNS_NOERROR;
	dns_query_recurse(qw->host,(unsigned short)qw->type,qw->an,&qw->ancnt,qw->ldnssrv,&qw->status,transport);
	if(qw->type==DT_MX_RAW) qw->type=DT_MX;
	return qw;
}

int dns_endquery(struct dnsquery *qw)
{
	int i;

	if(!qw) return 0;
	if(th_mutex_wait(m_cache,timeoutms)) {
		for(i=0;i<qw->ancnt;i++) {
			if(!qw->an[i]) continue;
			qw->an[i]->refcnt--;
			if (dolog) imsg("dns_endquery - Reduced refcnt on %s type %d to %d\n",qw->an[i]->domain,qw->an[i]->qtype,qw->an[i]->refcnt);
			if(qw->an[i]->refcnt<0) {
				if (dolog) imsg("dns_endquery - Reference counter below 0, correcting. Please report this to Netwin\n");
				qw->an[i]->refcnt=0;
			}
		}
		th_mutex_release(m_cache);
	} else {
		if (dolog) imsg("dns_endquery - WARNING: Timeout obtaining mutex. Cache memory could not be freed!\n");
		return 0;
	}
	free(qw->host);
	free(qw);
	return 1;
}

char *dns_reverse(char *ip,char *out,struct dnsquery **qp)
{
	int ptcnt,ncnt;
	char *s;
	char part[4][4]={0};
	char addr[BFSZ]={0};
	struct dnsquery *dq;
	struct dnsrr_ptr *rr;

	if(qp) *qp=0;
	if (strstrnc(ip,"in-addr")==NULL) {
		for(s=ip,ptcnt=0,ncnt=0;*s;s++) {
			if(*s=='.') {
				ptcnt++;
				ncnt=0;
				if(ptcnt>=4) {*out='\0'; return out;}
				continue;
			}
			part[ptcnt][ncnt++]=*s;
			if(ncnt>=4) {*out='\0'; return out;}
		}
		for(ptcnt=3;ptcnt>=0;ptcnt--) {
			if(*part[ptcnt]) {
				strcat(addr,part[ptcnt]);
				strcat(addr,".");
			}
		}
		strcat(addr,"IN-ADDR.ARPA");
	} else {
		ncpy(addr,ip,100);
	}
	dq=dns_query(addr,DT_PTR);
	if(qp) *qp=dq;
	if(!dq) {*out='\0'; return out;}
	rr=dns_getrr(dq);
	if(!rr) *out='\0';
	else ncpy(out,rr->domain,100);
	if(!qp) dns_endquery(dq);
	return out;
}

char *dns_ascstatus(struct dnsquery *qw)
{
	/* Special dnslib status codes */
	if(qw->status==DNS_TIMEOUT) return "Timeout";
	if(qw->status==DNS_INVALID) return "Invalid Domain";
	if(qw->status==DNS_TRUNCAT) return "Truncated";
	if(qw->status==DNS_NOCHANN) return "No available channels";

	/* Real DNS status codes */
	if(qw->status<DT_LOW || qw->status>DT_HIGH) return "Invalid status";
	return rcodestr[qw->status]; /* Is a real rcode */
}

#ifdef DNSLIB_TEST

char *stripcrlf(char *s)
{
	char *p;
	for(p=s;*p;p++) if(*p=='\r'||*p=='\n') {*p='\0'; break;}
	return s;
}

int main()
{

    WSADATA wsaData;
	DNSQUERY *qw;
	DNSRR_MX *mx;
	DNSRR_A *a;
	DNSRR_TXT *txt;
	DNSRR_PTR *ptr;
	char *servers[10];
	FILE *f;
	char dbf[1024];

#ifdef USE_DMALLOC
	d_malloc_init(printf);
#endif

	if(WSAStartup(MAKEWORD(1,1),&wsaData)) return 0;

	servers[0]="10.2.192.100";
//	servers[1]="10.2.192.100";


	th_init_threads();

	dns_init(servers,1,1);

	dolog=1;

	f=fopen("c:\\domains.txt","r");
	while(fgets(dbf,1024,f)) {
		stripcrlf(dbf);

		qw=dns_query_t(dbf,DT_MX_RAW,DNS_TRANSUDP);
		printf("\nMX test for %s (%d)\n",qw->host,curserver);
		while(mx=dns_getrr(qw)) {
			printf("Host: %s (%s) Preference: %d\n",mx->host,mx->ascaddr,mx->pref);
		}
		dns_endquery(qw);

		qw=dns_query_t(dbf,DT_TXT,DNS_TRANSUDP);
		printf("\nTXT test for %s (%d)\n",qw->host,curserver);
		while(txt=dns_getrr(qw)) {
			printf("Host: %s Len: %d Text: %s\n",qw->host,txt->len,txt->text);
		}

		dns_endquery(qw);
	
		qw=dns_query_t(dbf,DT_A,DNS_TRANSUDP);

		printf("\nA test for %s (%d)\n",qw->host,curserver);
		while(a=dns_getrr(qw)) {
			printf("Host: %s IP: %s\n",qw->host,a->ascaddr);
		}

		dns_endquery(qw);
	}

	fclose(f);


#ifdef USE_DMALLOC
	d_check();
#endif


	return 0;
}
#endif

#include <string.h>

static unsigned long crc_table[]= {
	0x00000000,0x77073096,0xee0e612c,0x990951ba,0x076dc419,
	0x706af48f,0xe963a535,0x9e6495a3,0x0edb8832,0x79dcb8a4,
	0xe0d5e91e,0x97d2d988,0x09b64c2b,0x7eb17cbd,0xe7b82d07,
	0x90bf1d91,0x1db71064,0x6ab020f2,0xf3b97148,0x84be41de,
	0x1adad47d,0x6ddde4eb,0xf4d4b551,0x83d385c7,0x136c9856,
	0x646ba8c0,0xfd62f97a,0x8a65c9ec,0x14015c4f,0x63066cd9,
	0xfa0f3d63,0x8d080df5,0x3b6e20c8,0x4c69105e,0xd56041e4,
	0xa2677172,0x3c03e4d1,0x4b04d447,0xd20d85fd,0xa50ab56b,
	0x35b5a8fa,0x42b2986c,0xdbbbc9d6,0xacbcf940,0x32d86ce3,
	0x45df5c75,0xdcd60dcf,0xabd13d59,0x26d930ac,0x51de003a,
	0xc8d75180,0xbfd06116,0x21b4f4b5,0x56b3c423,0xcfba9599,
	0xb8bda50f,0x2802b89e,0x5f058808,0xc60cd9b2,0xb10be924,
	0x2f6f7c87,0x58684c11,0xc1611dab,0xb6662d3d,0x76dc4190,
	0x01db7106,0x98d220bc,0xefd5102a,0x71b18589,0x06b6b51f,
	0x9fbfe4a5,0xe8b8d433,0x7807c9a2,0x0f00f934,0x9609a88e,
	0xe10e9818,0x7f6a0dbb,0x086d3d2d,0x91646c97,0xe6635c01,
	0x6b6b51f4,0x1c6c6162,0x856530d8,0xf262004e,0x6c0695ed,
	0x1b01a57b,0x8208f4c1,0xf50fc457,0x65b0d9c6,0x12b7e950,
	0x8bbeb8ea,0xfcb9887c,0x62dd1ddf,0x15da2d49,0x8cd37cf3,
	0xfbd44c65,0x4db26158,0x3ab551ce,0xa3bc0074,0xd4bb30e2,
	0x4adfa541,0x3dd895d7,0xa4d1c46d,0xd3d6f4fb,0x4369e96a,
	0x346ed9fc,0xad678846,0xda60b8d0,0x44042d73,0x33031de5,
	0xaa0a4c5f,0xdd0d7cc9,0x5005713c,0x270241aa,0xbe0b1010,
	0xc90c2086,0x5768b525,0x206f85b3,0xb966d409,0xce61e49f,
	0x5edef90e,0x29d9c998,0xb0d09822,0xc7d7a8b4,0x59b33d17,
	0x2eb40d81,0xb7bd5c3b,0xc0ba6cad,0xedb88320,0x9abfb3b6,
	0x03b6e20c,0x74b1d29a,0xead54739,0x9dd277af,0x04db2615,
	0x73dc1683,0xe3630b12,0x94643b84,0x0d6d6a3e,0x7a6a5aa8,
	0xe40ecf0b,0x9309ff9d,0x0a00ae27,0x7d079eb1,0xf00f9344,
	0x8708a3d2,0x1e01f268,0x6906c2fe,0xf762575d,0x806567cb,
	0x196c3671,0x6e6b06e7,0xfed41b76,0x89d32be0,0x10da7a5a,
	0x67dd4acc,0xf9b9df6f,0x8ebeeff9,0x17b7be43,0x60b08ed5,
	0xd6d6a3e8,0xa1d1937e,0x38d8c2c4,0x4fdff252,0xd1bb67f1,
	0xa6bc5767,0x3fb506dd,0x48b2364b,0xd80d2bda,0xaf0a1b4c,
	0x36034af6,0x41047a60,0xdf60efc3,0xa867df55,0x316e8eef,
	0x4669be79,0xcb61b38c,0xbc66831a,0x256fd2a0,0x5268e236,
	0xcc0c7795,0xbb0b4703,0x220216b9,0x5505262f,0xc5ba3bbe,
	0xb2bd0b28,0x2bb45a92,0x5cb36a04,0xc2d7ffa7,0xb5d0cf31,
	0x2cd99e8b,0x5bdeae1d,0x9b64c2b0,0xec63f226,0x756aa39c,
	0x026d930a,0x9c0906a9,0xeb0e363f,0x72076785,0x05005713,
	0x95bf4a82,0xe2b87a14,0x7bb12bae,0x0cb61b38,0x92d28e9b,
	0xe5d5be0d,0x7cdcefb7,0x0bdbdf21,0x86d3d2d4,0xf1d4e242,
	0x68ddb3f8,0x1fda836e,0x81be16cd,0xf6b9265b,0x6fb077e1,
	0x18b74777,0x88085ae6,0xff0f6a70,0x66063bca,0x11010b5c,
	0x8f659eff,0xf862ae69,0x616bffd3,0x166ccf45,0xa00ae278,
	0xd70dd2ee,0x4e048354,0x3903b3c2,0xa7672661,0xd06016f7,
	0x4969474d,0x3e6e77db,0xaed16a4a,0xd9d65adc,0x40df0b66,
	0x37d83bf0,0xa9bcae53,0xdebb9ec5,0x47b2cf7f,0x30b5ffe9,
	0xbdbdf21c,0xcabac28a,0x53b39330,0x24b4a3a6,0xbad03605,
	0xcdd70693,0x54de5729,0x23d967bf,0xb3667a2e,0xc4614ab8,
	0x5d681b02,0x2a6f2b94,0xb40bbe37,0xc30c8ea1,0x5a05df1b,
	0x2d02ef8d
};

unsigned long do_crc(char *bf, int len)
{
  unsigned long c = 0xffffffffL;
  int n;

  for (n=0;n<len;n++) {
	  c=crc_table[(c^bf[n])&0xff]^(c>>8);
  }
  return c^0xffffffffL;
}

int dnslib_exit(void)
{
	dns_expirecache(TRUE,TRUE);
	return TRUE;
}

void dns_dump(void)
{
	FILE *f;
	int pkthsh;
	DNSPKTD *pd;
	
	f=fopen("surgedns.pend","w");	

	if(th_mutex_wait(m_dnspkt,10000)) {
		for(pkthsh=0;pkthsh<PKTHASH;pkthsh++) {			
			for(pd=waitpackets[pkthsh];pd;pd=pd->next) {
				if(!pd->question.domain) fprintf(f,"-1\tInvalid Domain\n");
				else fprintf(f,"%d\t%s\n",pd->question.qtype,pd->question.domain);
			}
		}
		th_mutex_release(m_dnspkt);
	}

	fclose(f);
	return;
}
