#define SRCFILE_NUM 3
/*
Fixed so it now sends successes if it times out before all loookups
are complete (mx_gotsome function added)


Check channel is allocated befor calling 'fn'
	call mx_freechan(int x) from send_free(n)

*/
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#include <errno.h>
#include <string.h>
#include "../sam/tcplib.h"
#include "../sam/lib.h"
#include "../sam/lprintf.h"
#define DMID 15
#include "debug.h"
#include "defines.h"
#ifndef FALSE
#define FALSE 0
#define TRUE (!FALSE)
#endif

#include "dns.h"

#ifndef USE_DMALLOC
void dmalloc_ignore_leak(void){}
#endif

#ifdef ISOSF
/*typedef unsigned int u_int16_t;*/
#endif
/* Global functions */

int mx_lookup_que(char *name, void (*fn)(int n, char *s, int isfb),int n);
int mutex_lookup_wait(void);
int mutex_lookup_release(void);

static int ini_timeout=90;
static int ini_disk_cache=TRUE;
static char workarea[BFSZ];
#define MAX_SWITCH 7
int lookup_info_set(char *s);
static int nullmx;
/* Internal functions */
#ifndef strdup
#ifndef WIN32
char *strdup(const char *s);
#endif
#endif
#include "dnsuse.h"
static int (*emsg)(const char * arg_list, ...);
static int (*imsg)(const char * arg_list, ...);
static int (*dmsg)(const char * arg_list, ...);
char *get_text(unsigned char *base, unsigned char **s, unsigned char *end);

#ifdef WIN32
#define WIN_SOCK 1
#endif

#ifndef WIN_SOCK
#define INVALID_SOCKET  -1
#define SOCKET short
#endif

#ifdef NEED_BCOPY
#define bcopy(src,dst,len) memmove(dst,src,len)
#endif
short tcp_openx(char *name);
void do_test(void);
int bk_close(int x, char *reason);
static char bkresult[BFSZ];
static char bkcname[BFSZ];
static int g1=1;
static int bkns_n; // Number of name servers we know about.
static int bkcur; // current dns server we send requests to.
static int g2=2;
static int bkupto; // not used at all...
static int bktimeout=90; /*90;*/ /* After 10 seconds, try other one */
static int bklen;
static char bkhosts[10][BFSZ];
static Chan *bkudp[10];
static int reqid=1000; /*ID of each dns request */
enum {BK_IDLE,BK_START,BK_SEND,BK_READ};
#define MAX_CURRENT 1100
static time_t bk_lastused[MAX_CURRENT];
struct SYMS { int op; char *name; char *description; };
void mx_free(void);
int mxc_addok(char *name, char *result);
int dnsx_set_ipv6(int x);
int mx_failed(int x, char *reason);
void mx_freeall(int x);
void mx_success(int x,char *result, int addcache);
typedef struct DONE {char *s; int mask; struct DONE *next;} Done;
typedef struct MXREC_S {char *name; char *addr; double pri; int gotipv4; struct MXREC_S *next;} MXREC;
typedef struct MXCHAN {char *sent; int sentlen; int reqid; int bkns; int fallback; int bkns2; int retry; char *oname; int isorbs; char stype[100]; int nask; int loop; time_t at; time_t time_start; int state; int invalid; Done *done; MXREC *mx; int n; char *name; void (*fn)(int n, char *s, int isfb); int last_type,type,twice,done_next,timeout;} Mxchan;
static Mxchan mx[MAX_CURRENT];
static int nchan,mxnew[MAX_CURRENT];
void mx_copy(MXREC *dst, MXREC *src);
void mx_free_mx(MXREC *m);
void mx_free_done(Done *d);
int mx_init_one(int i);
#ifdef DNSTEST
static int dns_debug=TRUE;
#else
static int dns_debug=FALSE;
#endif
int mx_gotsome(int x);
static int do_ipv6 = FALSE;
int dns_set_ipv6(int x)
{
	do_ipv6 = x;
	dnsx_set_ipv6(x);
	return TRUE;
}


static int current_nok;
static int noisey=TRUE;
int dns_set_quiet(int x)
{
	noisey = !x;
	return TRUE;
}
int dns_corrupt(void)
{
	int ret=FALSE;
	if (g1!=1) ret = TRUE;
	if (g2!=2) ret = TRUE;
	if (bkns_n<0) ret = TRUE;
	if (bkns_n>12) ret = TRUE;
	if (ret) {
		imsg("dns: corrupt %d %d %d\n",g1,g2,bkns_n);
	}
	return ret;
}
int mx_nfree(void)
{
	int nfree=0;
	int i;
	for (i=0; i<MAX_CURRENT; i++) if (mx[i].state==BK_IDLE) nfree++;
	return nfree;
}
int mx_lookup_que(char *xname, void (*fn)(int n, char *s, int isfb),int n)
{
	int i;
	char name[BFSZ];
	ncpy(name,xname,BFSZ-1);
	if (name[lib_strlen(name)-1]!='.') ncat(name,".",BFSZ-1);
	for (i=0; i<MAX_CURRENT; i++) if (mx[i].state==BK_IDLE) break;
	if (i>=MAX_CURRENT) { emsg("dns: Could not lookup MX records as limit exceeded\n"); return -1;}
	if (noisey)  imsg("dns: mx lookup que %d: %s\n",i,name);
	mx[i].n = n;
	if (mx[i].name != NULL) free(mx[i].name);
	if (mx[i].oname != NULL) free(mx[i].oname);
	mx[i].name = strdup(name);
	mx[i].oname = strdup(name);dmalloc_ignore_leak();
	mx[i].fn = fn;
	mx[i].retry = 0;
	mx[i].invalid = FALSE;
	mx[i].timeout = 0;
	mx[i].time_start = time(NULL);
	mx[i].done_next = FALSE;
	mx[i].at = time(NULL);
	mx[i].isorbs = FALSE;
	mx[i].fallback = FALSE;
	strcpy(mx[i].stype,"");
	mx_init_one(i);
	if (i>=nchan) nchan = i+1;
	return i;
}
int mx_set_timeout(int x, int secs)
{
	mx[x].timeout = secs;
	return TRUE;
}
int mx_set_orbs(int x, int isorbs)
{
	mx[x].isorbs = isorbs;
	if (noisey)  imsg("dns_stats[%d] %s lookup %s\n",x,isorbs ? "ORBS" : "Norm",mx[x].oname);
	return TRUE;
}
int mx_set_type(int x, char *type)
{
	ncpy(mx[x].stype,type,99);
	if (lib_strlen(type)>0) mx[x].isorbs = TRUE;

	return TRUE;
}
int mx_init_one(int i)
{
	mxnew[i] = TRUE;
	mx[i].state = BK_START;
	mx[i].loop = 0;
	mx[i].nask = 0;
	mx[i].twice = FALSE;
	mx[i].done_next = FALSE;
	mx_free_done(mx[i].done); mx[i].done = NULL;
	mx_free_mx(mx[i].mx); mx[i].mx = NULL;
	return TRUE;
}
int  dnslog(const char * arg_list, ...);
static int mx_nok,mx_nfail,mx_ncached,mx_ncached_fails;
static time_t mx_start;
static int dns_fails;
void mx_success(int x,char *result, int addcache)
{
	int isfallback=FALSE;
	char result_copy[BFSZ];
	void (*fn)(int n, char *s,int isfall);

	mx_nok++;
	if (addcache) dns_fails = 0;



	strncpy(result_copy,result,BFSZ-10);
	result_copy[BFSZ-10] = 0;

	if (strcmp(mx[x].stype,"")==0) {
			if (mx[x].fallback) {
				isfallback = TRUE;
			}
	}


	if (addcache) if (mx[x].isorbs) addcache = FALSE;
	if (noisey)  imsg("dns: mx lookup success for channel %d (%s) fb=%d stype=%s\n",mx[x].n, result,mx[x].fallback,mx[x].stype);
//printf("success %s addcache %d\n",result,addcache);

//printf("stype {%s} last=%d, fallback=%d\n",mx[x].stype,mx[x].last_type==T_A,mx[x].fallback);


	if (addcache && !isfallback) // don't cache fallback ones...
	{
		mxc_addok(mx[x].oname,result);
		dns_disk_add(mx[x].oname,result,TRUE);
		//dns_stats(time(NULL)-mx[x].time_start,TRUE,mx[x].bkns,x,FALSE);
	}
	else {
		;
	}
	fn = mx[x].fn;
	mx[x].fn = NULL;
	if (fn!=NULL) (*fn)(mx[x].n,result_copy,isfallback);
	mx_freeall(x);
}
static int disk_disable=FALSE;
int mx_failed(int x, char *reason)
{
	char bf[BFSZ];
	void (*fn)(int n, char *s, int isfb);
	fn = NULL;
	mx_nfail++;
	if (noisey)  imsg("dns: (%s) mx lookup (%s) failed %s %d\n",bkhosts[mx[x].bkns],mx[x].oname,reason,x);
	if (x<0) return FALSE; /* We don't know which request so let it time out */

	sprintf(bf,"FAILED %.90s",mx[x].oname);
	
	//dns_stats(time(NULL)-mx[x].time_start,FALSE,mx[x].bkns,x,mx[x].isorbs);
	if (strstr(reason,"timedout")!=NULL) {
		if (noisey)  imsg("dns: Timeout so closing dns mx channel %d\n",x);
	}
	if (noisey) imsg("dns: fail %s %d %d\n",mx[x].oname,ini_disk_cache,mx[x].isorbs);

	if (!mx[x].isorbs) {
		if (noisey) dnslog("%s (%s) %d seconds, %s, %s\n",bkhosts[mx[x].bkns],mx[x].oname,time(NULL)-mx[x].time_start,mx[x].invalid ? "Invalid":"Valid",reason);
	}

	if (ini_disk_cache && !mx[x].isorbs ) {
		if (strcmp(reason,"Cached failure")==0) goto cont;
		if (strstrnc(reason,"nullmx")!=NULL) goto cont;		
		if (dns_disk_lookup(mx[x].oname,bf)) {
			if (noisey) imsg("dns Using disk cache %.90s %.90s\n",mx[x].oname,bf);
			mx_success(x,bf,FALSE);
			return FALSE;
		}
	}
cont:
	strcpy(bf,"");
	if (strstrnc(reason,"nullmx")!=NULL) {
		strcpy(bf,"invalid_domain_nullmx");
	} else if (time(NULL)-mx[x].time_start < 20) {
		// Only define a domain as invalid if we get a result in less than 20 seconds.
		// chrisp not sure if this is sane or not.
		if (mx[x].invalid) strcpy(bf,"invalid_domain1");
	}
	mxc_addfail(mx[x].oname,mx[x].invalid,bf);

	if (mx[x].n>=0) {
		fn = mx[x].fn;
		mx[x].fn = NULL;
		if (fn) (*fn)(mx[x].n,bf,FALSE);
	} else {
		//emsg("dns: Tried to call mx_failed when 'n' was invalid\n");
	}
	mx_freeall(x);
	return FALSE;
}
static int stat_nok[100];
static int stat_nbad[100];
static int stat_nbad2[100];
static int stat_oktime[100];
static int stat_badtime[100];
static int stat_badtime2[100];
int dns_stats(int delay, int worked, int n,int x, int isorbs)
{
	if (n<0) return FALSE;
	if (n>10) return FALSE;
	if (noisey)  imsg("dns_stats2[%d] %d seconds, %s, %s %s\n",x,delay,worked ? "Worked" : "Failed" ,mx[x].isorbs ? "ORBS" : "Normal",mx[x].oname);
	if (worked) {
		stat_nok[n]++;
		stat_oktime[n]+=delay;
	} else {
		stat_nbad[n]++;
		stat_badtime[n]+=delay;
		if (!isorbs) {
			stat_nbad2[n]++;
			stat_badtime2[n]+=delay;
		}
	}
	return TRUE;
}
int dns_status_vars(int i, char *host, int bfsz, int *nok, int *oktime, int *nbad, int *badtime, int *nbad2, int *badtime2 )
{
	if (i>=bkns_n) return FALSE;
	ncpy(host,bkhosts[i],BFSZ-1);
	*nok = stat_nok[i];
	*oktime = stat_oktime[i];
	*nbad = stat_nbad[i];
	*badtime = stat_badtime[i];
	*nbad2 = stat_nbad2[i];
	*badtime2 = stat_badtime2[i];
	return TRUE;
}
char *dns_status(int i, char *tmp)
{
	int nok,nbad,nbad2;
	if (i>=bkns_n) return NULL;
	if (bkns_n>8) {
		if (noisey)  imsg("bkns_n corrupted %d\n",bkns_n);
		bkns_n = 4;
		return NULL;
	}
	nok = stat_nok[i];
	nbad = stat_nbad[i];
	nbad2 = stat_nbad2[i];
	if (nok==0) nok = 1;
	if (nbad==0) nbad = 1;
	if (nbad2==0) nbad2 = 1;
	sprintf(tmp,"%.10sDNS %.50s, nok=%d %3.1f nbad=%d %3.1f nbad_normal=%d %3.1f",bkcur==i ? "->" : "",bkhosts[i]
		,stat_nok[i],(double) stat_oktime[i]/(double) nok
		,stat_nbad[i],(double) stat_badtime[i]/(double) nbad
		,stat_nbad2[i],(double) stat_badtime2[i]/(double) nbad2);
	if (noisey)  imsg("badtime %d nbad %d\n",stat_badtime[i],stat_nbad[i]);
	return tmp;
}
char *mx_status(char *bf)
{
//	static char bf[BFSZ];
	sprintf(bf,"dns nok=%d nlookups=%d nfailed=%d cfails=%d Total speed=%4.2f dns speed=%4.2f",mx_nok,mx_nok-mx_ncached,mx_nfail,mx_ncached_fails,(double) (mx_nok+mx_nfail)/(time(NULL)-mx_start),(double) (mx_nok+mx_nfail-mx_ncached)/(time(NULL)-mx_start));
	return bf;
}
void mx_freechan(int x)
{
	int i;
	for (i=0; i<MAX_CURRENT; i++) {
		if (mx[i].n==x) mx_freeall(i);
	}

}
void mx_freeall(int x)
{
	mx[x].n = -1;
	mx[x].state = BK_IDLE;
	mx_free_mx(mx[x].mx);
	mx[x].mx = NULL;
	mx_free_done(mx[x].done);
	mx[x].done = NULL;
	mx[x].fn = NULL;
}
int mx_showall(Chan *c)
{
	int i;
	for (i=0; i<MAX_CURRENT; i++) {
		if (mx[i].state!=0 || i<10) tcp_printf(c," mx[%d] n=%d state=%d tout=%d (%s)\r\n",i,mx[i].n,mx[i].state,(int) (time(NULL) - mx[i].time_start),mx[i].oname);
	}
	return TRUE;
}

/* Should increase probably at some point, and make it a hash table */
#define CACHE_SIZE 3000
static char *mxc_result[CACHE_SIZE];
static char *mxc_name[CACHE_SIZE];
static time_t mxc_time[CACHE_SIZE];
static int mxc_nfail[CACHE_SIZE];
static time_t mxc_lastfail[CACHE_SIZE];
static int mxc_next;

void mxc_exit()
{
	mxc_age(TRUE);
}

int mxc_find_raw(char *s)
{
	int i;
	for (i=0; i<CACHE_SIZE; i++) {
		if (mxc_name[i]==NULL) continue;
		if (strcmp(mxc_name[i],s)==0) return i;
	}
	return -1;
}
int mxc_find(char *s)
{
	int i;
	time_t t;
	t = time(NULL);
	for (i=0; i<CACHE_SIZE; i++) {
		if (mxc_name[i]==NULL) continue;
		if ((lib_strlen(mxc_result[i])==0) || ( strcmp(mxc_result[i],"invalid_domain1")==0 )) {
			if (mxc_nfail[i]<5) continue;
			if ((t-mxc_time[i])>10) continue; // so we can use lots of failures quickly.
//			continue; // always ignore cached failures, they are too dangerous to use. 
			// This continue was commented out, chrisp removing as it seems really dangerous
			// to me. 
//			continue; 
		}
		if (strcmp(mxc_name[i],s)==0) return i;
	}
	return -1;
}
int mxc_age(int force)
{
	static time_t last,now;
	int i;
	time(&now);
	if (((now-last)>(60*60*2))||(force)) {
		last = now;
		dmsg("dns: ** Clearing mx cache (had %d entries)\n", mxc_next);
		for (i=0; i<CACHE_SIZE; i++) {
			if (mxc_name[i]!=NULL) free(mxc_name[i]);
			if (mxc_result[i]!=NULL) free(mxc_result[i]);
			mxc_name[i] = NULL;
			mxc_result[i] = NULL;
		}
		mxc_next=0;
	}
	return TRUE;
}

char *mxc_strdup(char *s)
{
	char *b = malloc(lib_strlen(s)+1);
	strcpy(b,s);
	return b;
}
int mxc_addfail(char *name, int invalid, char *result)
{
	int r;
	r = mxc_find_raw(name);
	if (r<0) return mxc_add(name,result);
	if (time(NULL)-mxc_lastfail[r] > 300) {
		if (invalid) mxc_nfail[r]++;
		mxc_lastfail[r] = time(NULL);
	}
	if (noisey)  imsg("Added failure for domain %s to cache %d failures recorded\n",name,mxc_nfail[r]);
	return r;
}
int mxc_addok(char *name, char *result)
{
	int i;
	i = mxc_find_raw(name);
	if (i<0) return mxc_add(name,result);
	if (noisey)  imsg("dns: Replacing cache entry with good result %s %s\n",name,result);
	free(mxc_result[i]);
	mxc_result[i] = mxc_strdup(result);
	mxc_time[i] = time(NULL);
	mxc_nfail[i] = 0;
	return i;
}
int mxc_add(char *name, char *result)
{
	int i = mxc_next++;
	char *n,*r;
	if (mxc_next>=CACHE_SIZE) mxc_next = 0;
	n = strdup(name);
	r = mxc_strdup(result);

	if (mxc_name[i]!=NULL) {
		free(mxc_name[i]);
		free(mxc_result[i]);
	}
	mxc_name[i] = n;
	mxc_result[i] = r;
	mxc_time[i] = time(NULL);
	mxc_nfail[i] = 0;
	if (lib_strlen(result)==0) mxc_nfail[i]=1;
	mxc_lastfail[i] = time(NULL);
	return i;
}
int mx_fail_all(void)
{
	int i;
	for (i=0; i<nchan; i++) {
		if (mx[i].state!=BK_IDLE) mx_failed(i,"Nameserver not responding");
	}
	return TRUE;
}
int mx_any_busy(void)
{
	int i;
	for (i=0; i<nchan; i++) {
		if (mx[i].state!=BK_IDLE) return TRUE;
	}
	return FALSE;
}
int mx_bkgnd(int thischan, int msec)
{
	int c;
	int i;
	int r;

	if (mxnew[thischan] || (mx[thischan].state == BK_START)) {
		mxnew[thischan] = FALSE;
//		for (i=0; i<nchan; i++) {
		i = thischan;

			if (mx[i].state == BK_START) {
				lookup_info_set("samdns1");
				c = mxc_find(mx[i].oname);
				lookup_info_set("samdns2");
//c = -1;
				if ((c>=0) && (mx[i].type==T_MX) && (lib_strlen(mx[i].stype)==0)) {
				 if (lib_strlen(mxc_result[c])==0) {
 				   lookup_info_set("samdns3");
				   //if (noisey)  imsg("dns: mx got cached response OF FAILURE ch=%d c=%d (%s)\n",mx[i].n,c,mxc_result[c]);
 				   lookup_info_set("samdns4");
				   mx_failed(i,"Cached failure");
 				   lookup_info_set("samdns5");
				   mx_ncached_fails++;
				 } else if (strcmp(mxc_result[c],"invalid_domain1")==0) {
				   //if (noisey)  imsg("dns: mx got cached response OF FAILURE ch=%d c=%d (%s)\n",mx[i].n,c,mxc_result[c]);
				   mx[i].invalid = TRUE;
 				   lookup_info_set("samdns6");
				   mx_failed(i,"Cached failure");
 				   lookup_info_set("samdns7");
				   mx_ncached_fails++;
				 } else {
 				   lookup_info_set("samdns8");
				   //if (noisey)  imsg("** mx got cached response ch=%d c=%d (%s)\n",mx[i].n,c,mxc_result[c]);
 				   lookup_info_set("samdns9");
				   mx_success(i,mxc_result[c],FALSE);
 				   lookup_info_set("samdns10");
				 }
				 mx_ncached++;
				}
				else {
 				   lookup_info_set("samdns11");
					bk_lookup(i,mx[i].oname);
 				   lookup_info_set("samdns12");
				}
			}
//		}
	}
	// chrisp changed following line as I think all must be checked
	//for (i=0; i<nchan; i++) bk_tick(i,msec);
    lookup_info_set("samdns13");
	r = bk_tick(thischan,msec);
    lookup_info_set("samdns14");
	mxc_age(FALSE);
    lookup_info_set("samdns15");
	return r;
}
int bk_lookup(int x, char *name)
{
	mx_free();
	if(name[lib_strlen(name)-1]=='\n') name[lib_strlen(name)-1]=0;
	if (strcmp(mx[x].stype,"txt")==0) return bk_start(x,name,T_TXT,TRUE);
	if (strcmp(mx[x].stype,"a")==0) return bk_start(x,name,T_A,TRUE);
	if (strcmp(mx[x].stype,"aaaa")==0) return bk_start(x,name,T_AAAA,TRUE);
	if (strcmp(mx[x].stype,"ptr")==0) return bk_start(x,name,T_PTR,TRUE);
	if (strcmp(mx[x].stype,"mx")==0) return bk_start(x,name,T_MX,TRUE);
	if (mx[x].isorbs) return bk_start(x,name,T_A,TRUE);
	return bk_start(x,name,T_MX,TRUE);
}
int bk_set_timeout(int x)
{
	ini_timeout = x;
	return TRUE;
}
int bk_set_workarea(char *work)
{
	ncpy(workarea,work,BFSZ-1);
	return TRUE;
}
int bk_set_disk_cache(int x)
{
	ini_disk_cache = x;
	return TRUE;
}
int bk_clear_nameserver(void)
{
	bkns_n = 0;
	return TRUE;
}
int dns_reset(int n)
{
	char tmp[BFSZ];
	tcp_close_nice(bkudp[n]);
	if (!tcp_open_udp(bkudp[n],bkhosts[n],53)) {
		imsg("tcp_udp_open (%s) failed %s\n",bkhosts[n],tcp_error(bkudp[n],tmp));
	} else {	
		tcp_setblock(bkudp[n],FALSE);
	}
	return TRUE;
}

int bk_add_nameserver(char *s)
{
	char bf[BFSZ];
	char *upto;
	int i;
	int n;
	if (bkns_n>6) return FALSE;
//printf("bk_add_namserer %s\n",s);
	ncpy(bf,s,BFSZ-1);
	s = mystrtok(bf,", ",&upto);
	for (;s!=NULL;) {
		for (i=0; i<bkns_n; i++) {
			if (strcmp(bkhosts[i],s)==0) goto next;
		}
		n = bkns_n++;
		ncpy(bkhosts[n], s,BFSZ-1);
		if (bkudp[n]==NULL) tcp_new(&bkudp[n]);dmalloc_ignore_leak();
		dns_reset(n);
next:	s = mystrtok(NULL,", ",&upto);
	}
	return TRUE;
}
static char g_home[BFSZ];
int bk_init(int (*vdmsg)(const char * arg_list, ...),int (*vimsg)(const char * arg_list, ...),int (*vemsg)(const char * arg_list, ...))
{
	static int done;
	int x;
	if (!done) {
		for (x=0; x<MAX_CURRENT; x++) {
			mx[x].state = BK_IDLE;
		}
	}
	done = TRUE;
	mx_start = time(NULL);
	bkns_n=0;
	bkcur = 0;
	dmsg = vdmsg;
	imsg = vimsg;
	emsg = vemsg;
	bktimeout = ini_timeout;
	if (bktimeout<35) {
		/*TRW2.8d; imsg("dns: Adjusting timeout to %d seconds, as %d is too low\n",bktimeout,35);*/
		imsg("dns: Adjusting timeout to %d seconds, as %d is too low\n",35,bktimeout);
		bktimeout = 35;
	}
	dns_disk_init();
//	bk_default_dns();

	if(bkns_n==0) return FALSE;
	return TRUE;
}
int bk_start(int x, char *name, int xtype,int isnew)
{
	if (noisey) imsg("dns:  mx start(%d,%s,%d,%d) %s\n",x,name,xtype,isnew,mx[x].stype);
	mx[x].nask++;
	if (mx[x].name != NULL) free(mx[x].name);
	mx[x].name = strdup(name);dmalloc_ignore_leak();
	mx[x].at = time(NULL);
	mx[x].state = BK_SEND;
	mx[x].last_type = xtype;
	mx[x].type = xtype;

	return bk_send(x);
}

int bk_comp(char *dname, char *cp, int bflen)
{
	char bf[BFSZ];
	char *s,*cpstart=cp;
	int n;

	ncpy(bf,dname,BFSZ-1);
	s = strtok(bf,".");
	for (;s!=NULL;) {
		n = lib_strlen(s);

		*cp++ = (char ) n;
		bflen--;
		ncpy(cp,s,bflen-1);
		cp += n;
		bflen -= n;
		s = strtok(NULL,".");
	}
	*cp = 0;
	cp++;
	return (int)(cp-cpstart);
}

#define MAXPACKET (8*1024)
static char bkname[BFSZ];
int bk_send(int x) /* Send the current request to the current name server*/
{
	char guard1[100];
	u_char buf[MAXPACKET];
	char guard2[100];
	int n;
	int start;
	int nsent=0;
	short ss;
	int r;
	int rid;
	int i;
	for (i=0; i<100; i++) {
		guard1[i] = 1;
		guard2[i] = 1;
	}

	mx[x].state = BK_SEND;

	ncpy(bkname,mx[x].name,BFSZ-1);
	mx[x].reqid = reqid++;

	rid = mx[x].reqid;

	if (reqid>32000) reqid = 2000;
	n = bk_mkquery(rid,QUERY, bkname, C_IN,mx[x].type, buf, sizeof(buf));
	if (n <= 0) { 	mx_failed(x,"mkquery failed"); 	return FALSE;}

	if (mx[x].sent==NULL) mx[x].sent = malloc(2000);
	if (n<2000) memcpy(mx[x].sent,buf,n);
	mx[x].sentlen = n;
	errno = 0;
	ss = htons((short) n);
	r = 0;
	start = bkcur;
	if (bkcur==bkns_n-1) start = bkcur-1;
	if (start<0) start = 0;

    lookup_info_set("bksend1");

	mx[x].bkns = start;
	mx[x].bkns2 = start+1;

	for (i=start; i<bkns_n; i++) { // Write request to first one that will accept it.
		char tmp3[BFSZ];
	    lookup_info_set("bksend2");
		if (noisey) imsg("dns: Sending request to server [%d] rid=%d\n",i,reqid);
		r = tcp_udp_write(bkudp[i],buf,n);
		if (r!=n) {
			if (noisey) imsg("dns: tcp_udp_write failed %d/%d %s\n",r,n,tcp_error(bkudp[i],tmp3));
			dns_reset(i); // Reset the socket in question.  Should never happen but it does, hopefully this will unjam it..
		}
		if (r==n) {
			nsent++;
		}
		if (nsent>=2) break;
	}
	if (nsent==1) mx[x].bkns2 = -1;
    lookup_info_set("bksend3");
	if (nsent==0) {
		mx_failed(x,"Could not write to dns server");
		return FALSE;
	}
	mx[x].at = time(NULL);
	mx[x].state = BK_READ;
	return TRUE;
}
char *cur_dns_server(void)
{
	return bkhosts[bkcur];
}
int dns_reset_main(void)
{
	bkcur = 0;
	return TRUE;
}
int bk_nextns(int x, char *reason)
{
	bkcur++;
	if (bkcur>=bkns_n) bkcur = 0;
	imsg("dns: switched to new dns server (%s) (%s)\n",bkhosts[bkcur],reason);
	return TRUE;
}

int is_exit(void);
int bk_timeout(int x) // logging removed incase causese mutex issue. 
{
	int r;
	int delay=0;
	int timeout=FALSE;
	static time_t last_timeout;
	char bf[BFSZ];
	int max_switch =MAX_SWITCH;
	if (mx[x].state==BK_IDLE) return FALSE;
	if (mx[x].timeout>0) if ((time(NULL) - mx[x].at)>mx[x].timeout) {
		timeout = TRUE;
	}
	if (is_exit()) timeout = TRUE;
	if (timeout || ((time(NULL) - mx[x].at)>bktimeout)) {
		delay = (int)(time(NULL) - mx[x].at);
		sprintf(bf,"timedout d=%d",delay);
		mx[x].at = time(NULL);
		if (mx_gotsome(x)) {
			//imsg("dns: timedout but gotsome was true, so not switching\n");
			mx_failed(x,bf);
			return TRUE;
		}
		if (delay < 20) {
			//imsg("dns: timedout but timeout less than 20 seconds so not switching\n");
			mx_failed(x,bf);
			return TRUE; /* Short timeout, so don't switch name servers */
		}

	//	printf(" orbs=%d done_next=%d (%s)\n",mx[x].isorbs,mx[x].done_next,mx[x].stype);
/*
		imsg("dns: orbs=%d done_next=%d (%s)\n",mx[x].isorbs,mx[x].done_next,mx[x].stype);
		if (!mx[x].isorbs) { // so it's a lookup that should have worked
			if (!mx[x].done_next) { // If we haven't tried another dns server, then try one.
				mx[x].done_next = TRUE;
				if ((strcmp(mx[x].stype,"mx")==0) || (lib_strlen(mx[x].stype)==0)) {
					r = bk_nextns(x,"timeout_do_next");
					imsg("dns: Sending request to next name server %s\n",mx[x].name);
					printf("dns: Sending request to next name server %s\n",mx[x].name);
					bk_start(x,mx[x].name,T_MX,TRUE);
					return FALSE; // don't timeout yet...
				}
			}
		}
*/
		// Ok fail that lookup then... 
		mx_failed(x,bf);

		if ((time(NULL)-last_timeout)>30) {
		 //imsg("dns: bkns_n %d, dns_fails %d>%d, nok %d\n",bkns_n,dns_fails,max_switch,current_nok);
		 if (dns_fails>max_switch || current_nok==0) {
			 // chrisp, thinks next line does nothing as bkns_n must be greater than one...
			if ((bkns_n>1) || (dns_fails>100) || current_nok==0) {
				last_timeout = time(NULL);
				dns_fails = 0;
				r = bk_nextns(x,"timeout");
				return TRUE;
			} else {
//				if (noisey) imsg("dns: Not switching to next dns server as we only have the one\n");
			}
		 } else {
			 //imsg("dns: Not switching as only had %d/%d fails see dns_switch_nfails\n",dns_fails,max_switch);
		 }
		} else {
			//imsg("dns: Not switching to next server yet as did that %d seconds ago\n",time(NULL)-last_timeout);
		}
		dns_fails++;
		return TRUE;
	}
	return FALSE;
}
int bk_tick(int x, int msec)
{
	if (bk_timeout(x))
	{
		return TRUE;
	}
	switch (mx[x].state) {
	  case BK_IDLE:
		break;
	  case BK_SEND:
	    lookup_info_set("samdnstick1");
		bk_send(x);
	    lookup_info_set("samdnstick2");
		break;
	  case BK_READ:
	    lookup_info_set("samdnstick3");
		bk_read(x,msec,mx[x].bkns);
		if (mx[x].bkns2>=0) bk_read(x,msec,mx[x].bkns2);
	    lookup_info_set("samdnstick4");
		break;
	}

	return FALSE;
}
static char bkans[32*1024];
static char bk_lastip[BFSZ];
int bk_reading(int x);

int bk_read(int x, int msec, int u)
{
	/* Try and read, if we get data then process it
	   send new request if CNAME is response */
	int n;
	char tmp[BFSZ];
	short len;
//	int u = mx[x].bkns;
	char udpin[8192];

    lookup_info_set("bkread1");
	n = tcp_udp_read(bkudp[u],udpin,8192);
	ncpy(bk_lastip,bkhosts[u],BFSZ	-1);

    lookup_info_set("bkread2");
	tcp_lasterror(bkudp[u]);
	// New code, next 4 lines, this is much more efficient than doing a select on it.
	if (n==0) goto close_it;
	if (n<2) {
		if (tcp_wouldblock(bkudp[u])) return TRUE;
	}
	if (n<2) {
		if (tcp_wouldblock(bkudp[u])) return TRUE;
close_it:
		imsg("dns: Socket read got only %d of %d bytes wanted from %s %s\n",n,2,bkhosts[u],tcp_error(bkudp[u],tmp));
		mx_failed(x,"bk_read failed");
	    lookup_info_set("bkread3");
		return bk_nextns(x,"bk_read"); /* Try next if exists */
	}
	len = n;
    lookup_info_set("bkread4");
	if ((len<0) || (len>sizeof(bkans))) {
		imsg("dns: Lookup got packet length %d %d %s\n",len,(int) sizeof(bkans),tcp_error(bkudp[u],tmp));
		mx_failed(x,"bk_read2 failed");
		bk_nextns(x,"packet size");
		return FALSE;
	}
	memcpy(bkans,udpin,len);
	bklen = len;
	bkupto = 0;
    lookup_info_set("bkread5");

	dns_stats((int)(time(NULL)-mx[x].time_start),TRUE,u,x,FALSE);

	return bk_reading(x);
}
int bk_reading_real(int x);
void bk_process(char *ans, int len, char *lastip, int *trunc);
int bk_reading(int x)
{
	int r;
	r = bk_reading_real(x);
	return r;
}
int bk_read_tcp(int x, Chan *c)
{
	int n;
	char tmp[BFSZ];
	short len;
	int got;
	int trunc=FALSE;

	if (!tcp_waitfor(c,2000)) {
		mx_failed(x,"No data arrived");
		return FALSE;
	}

	n = tcp_read_timed(c,(char *) &len,2,2000);
	tcp_getip(c,bk_lastip); // save the ip address.

	if (n<=0) {
		mx_failed(x,"No data arrived");
		return FALSE;
	}
	len = htons(len);

	if ((len<0) || (len>sizeof(bkans))) {
		emsg("dns: Lookup got packet length %d %d %s\n",len,(int) sizeof(bkans),tcp_error(c,tmp));
		imsg("dns: Lookup got packet length %d %d %s\n",len,(int) sizeof(bkans),tcp_error(c,tmp));
		mx_failed(x,"bk_read2 failed");
		bk_nextns(x,"packet size");
		return FALSE;
	}
	// I don't see how this works, when does it ever read the data? 
	imsg("dns: tcp read packet is %d bytes\n",len);
	bklen = len;
	bkupto = 0;
	got = tcp_read_timed(c,bkans,len,4000);
	imsg("dns: tcp_read_timed got %d of %d bytes\n",got,len);
	bk_process(bkans,bklen,bk_lastip,&trunc);
	return TRUE;
}
int bk_redo_tcp(int x)
{
	int r;
	short ss;
	Chan *c=NULL;
	imsg("dns: doing request via tcp as packet was too big\n");
	tcp_new(&c);
	if (!tcp_open_noblock(c,bkhosts[bkcur],53,NULL)) {
		imsg("dns: tcp open (%s) port 53 failed\n",bkhosts[bkcur]);
		goto failed;
	}
	if (!tcp_wait_write(c,5000)) {
		imsg("dns: tcp open (%s) port 53 failed\n",bkhosts[bkcur]);
		goto failed;
	}

	ss = htons((short) mx[x].sentlen);
	r = tcp_write_timed(c,(char *) &ss,2,2000);
	if (r!=2) goto failed;
	r = tcp_write_timed(c,mx[x].sent,mx[x].sentlen,2000);
	if (r!=mx[x].sentlen) goto failed;
	mx[x].state = BK_READ; // reset state so read will work..
	r = bk_read_tcp(x,c);
	if (noisey) imsg("dns: tcp request RESENT via tcp\n");
	tcp_free(&c);


	return r;
failed:
	tcp_free(&c);
	mx_failed(x,"tcp request");
	return FALSE;
}
int bk_reading_real(int x)
{
	int tot=0;
	int trunc=FALSE;


	bkupto = 0;

	if (bklen>512) {
	    lookup_info_set("bkread111");
		return bk_redo_tcp(x);
	}
    lookup_info_set("bkproccess1");
	bk_process(bkans,bklen,bk_lastip,&trunc);
	if (trunc) {
		return bk_redo_tcp(x);
	}
	return TRUE;
}
static int base_len;
void show_query(unsigned char *answer, char *dnsip, int *trunc);
void bk_process(char *ans, int len, char *dnsip, int *trunc)
{
	*trunc = FALSE;
	strcpy(bkresult,"");
	base_len = len;
	show_query((u_char *) ans, dnsip,trunc);
    lookup_info_set("bkproccess2");
}
/*---------------------------------------------------------*/
/*------My decoding STUFF ----------------------------*/
char *dns_type_name(int x);
int get_short(unsigned char **s);
char *get_string(unsigned char **s);
int  show_q(u_char **answer);
int get_long(unsigned char **s);
int show_rr(u_char **answer);
static u_char *base;

#define MAXMX 100
static MXREC mxrecs[MAXMX+100];
static int nmx;
static int ismx;
void mx_replace(char *name, char *newname)
{
	MXREC *m;
	int i;
	if (dns_debug) dmsg("dns: mx replace %s -> %s\n",name,newname);
	for (i=0; i<nmx; i++) {
		m = &mxrecs[i];
		if (dns_debug) dmsg("dns mx replace %d %s\n",i,m->name);
		if (strcmpnc(name,m->name)==0) {
			if (dns_debug) dmsg("dns Replacing mx entry [%d] %s = %s\n",i,m->name,newname);
			free(m->name);
			m->name = strdup(newname);
		}
	}
}
void mx_free(void)
{
	MXREC *m;
	int i;
	for (i=0; i<nmx; i++) {
		m = &mxrecs[i];
		if (m->name!=NULL) free(m->name);
		if (m->addr!=NULL) free(m->addr);
		m->name = NULL;
		m->addr = NULL;
		m->gotipv4 = FALSE;
	}
	nmx = 0;
}
int is_digits(char *s)
{
	for (;*s!=0;s++) {
		if (sisdigit(*s)) continue;
		if (*s=='.') continue;
		return FALSE;
	}
	return TRUE;

}
int mx_add_name(char *name, int pri)
{
	int i;
	char *s;
	int r;
	double dpri = pri;
	r = myrand()%20; if (r<0) r = -r;
	dpri = dpri + (r)/300.0;
//printf("dns: mx_add_name(%s,%d) \n",name,pri);
	if (strchr(name,':')==NULL) { // give priority to ipv6
			dpri+= 0.5;
	}
	for (i=0; i<nmx; i++) {
		if (strcmpnc(name,mxrecs[i].name)==0) {
			if (pri<mxrecs[i].pri) {
				//imsg("dns: mx_add_name %s %d %g idx=%d\n",name,pri,dpri,i);
				mxrecs[i].pri = dpri;
			}
			return i;
		}
	}
	if ((nmx+1)>=MAXMX) {
		emsg("Too many MX entries for host %s\n",name);
		return 0;
	}
	mxrecs[nmx].name = strdup(name);dmalloc_ignore_leak();
	mxrecs[nmx].addr = NULL;
	if (strcmp(name,"(nullmx)")==0 && pri==0) {
		mxrecs[nmx].addr = strdup(name);		
		nullmx = TRUE;
	} else {
		if (is_digits(name)) {
			mxrecs[nmx].addr = strdup(name);
			s = mxrecs[nmx].addr;
			if (lib_strlen(s)>1) s[lib_strlen(s)-1] = 0;
		}
	}
	// Randomize order as we are supposed to.
	mxrecs[nmx++].pri = dpri;
	//imsg("dns: mx_add_name2 %s %d %g idx=%d\n",name,pri,dpri,nmx-1);

	return nmx-1;
}
int mx_cmp(const void *aa, const void *bb);
int mx_cmp(const void *aa, const void *bb)
{
	MXREC *a= (MXREC *) aa;
	MXREC *b= (MXREC *) bb;
	return (int) (10.0*(a->pri - b->pri));
}

void mx_add_addr(char *name, char *addr, int istext, int isipv4)
{
	int i;
	char old[BFSZ];

	if (noisey) imsg("dns: mx_add_addr(%s %s)\n",name,addr);
	for (i=0; i<nmx; i++) {
		if (strcmpnc(mxrecs[i].name,name)==0) {
			//imsg("dns: mx_add[%d]\n",i);
			strcpy(old,"");
			if (isipv4) mxrecs[i].gotipv4 = TRUE;
			if (mxrecs[i].addr!=NULL) {
				ncpy(old,mxrecs[i].addr,BFSZ-1);
				if (istext) ncat(old,"\xfe",BFSZ-1);
				else ncat(old," ",BFSZ-1);
			}
			if (mxrecs[i].addr!=NULL) free(mxrecs[i].addr); mxrecs[i].addr = NULL;
			if (strstr(old,addr)==NULL) {
				ncat(old,addr,BFSZ-1);	
			}
			mxrecs[i].addr = strdup(old);dmalloc_ignore_leak();
			//imsg("dns: Inserted Result (%s)\n",old);
		}
	}
	/* (check if name is the original name) */
	if (strcmpnc(name,bkname)==0) {
		i = mx_add_name(name,90000); // changed from 120
		//imsg("dns: mx_add[%d] bkname matched\n",i);
		strcpy(old,"");
		if (mxrecs[i].addr!=NULL) {
			ncpy(old,mxrecs[i].addr,BFSZ-1);
			if (istext) ncat(old,"\xfe",BFSZ-1);
			else ncat(old," ",BFSZ-1);
			if (strstr(old,addr)==NULL) {
				ncat(old,addr,BFSZ-1);
			}
			free(mxrecs[i].addr); mxrecs[i].addr = NULL;
		} else {
			if (strstr(old,addr)==NULL) {
				ncat(old,addr,BFSZ-1);
			}
		}
		mxrecs[i].addr = strdup(old);
		if (isipv4) mxrecs[i].gotipv4 = TRUE;
		// imsg("dns: mx_insert2(%s)\n",old);
	}
}

int bk_havedone(int x, char *name, int mask)
{
	Done *d;
	for (d=mx[x].done;d!=NULL;d=d->next) {
		if (strcmp(d->s,name)==0) {
			if ((d->mask & mask)>0) return TRUE;
			else return FALSE;
		}
	}
	return FALSE;
}

void bk_donedid(int x, char *name, int mask)
{
	Done *d;
	for (d=mx[x].done;d!=NULL;d=d->next) {
		if (strcmp(d->s,name)==0) {
			d->mask = d->mask | mask;
			dmsg("dns: donedid %d %s mask=%d (%d)\n",x,name,d->mask,mask);
			return;
		}
	}
	d = malloc(sizeof(Done));
	d->next = mx[x].done;
	d->s = strdup(name);
	d->mask = mask;
	dmsg("dns: donedid %d %s mask=%d (%d)\n",x,name,d->mask,mask);
	mx[x].done = d;
}
void mx_free_done(Done *d)
{
	Done *nxt;

	for (;d!=NULL;d=nxt) {
		nxt = d->next;
		free(d->s);
		free(d);
	}

}
void mx_copy(MXREC *dst, MXREC *src)
{
	if (src->name!=NULL) dst->name = strdup(src->name);
	if (src->addr!=NULL) dst->addr = strdup(src->addr);
	dst->pri = src->pri;

}
void mx_free_mx(MXREC *m)
{
	MXREC *nxt;
	for (;m!=NULL; m=nxt) {
		nxt = m->next;
		if (m->name!=NULL) free(m->name);
		if (m->addr!=NULL) free(m->addr);
		m->name = NULL;
		m->addr = NULL;
		free(m);
	}
}
char *sort_ipv6(char *list, char *out)
{
	char *p[200];
	int np;
	int i;
	char bf[BFSZ];
	ncpy(bf,list,BFSZ-1);
	strcpy(out,"");
	np = token_split(bf,p," ",99);
	for (i=0; i<np; i++) {
		if (strchr(p[i],':')!=NULL) {
			ncat(out,p[i],BFSZ-1);
			ncat(out," ",BFSZ-1);
		}
	}
	for (i=0; i<np; i++) {
		if (strchr(p[i],':')==NULL) {
			ncat(out,p[i],BFSZ-1);
			ncat(out," ",BFSZ-1);
		}
	}
	return out;
}
static char cur_question[BFSZ];
static char bkcaddr[BFSZ];
static int g_x;
int mx_find(int id)
{
	int i;
	for (i=0; i<nchan; i++) {
		if (mx[i].reqid==id) return i;
	}	
	return -1;
}
void show_query(unsigned char *answer, char *dnsip, int *trunc)
{
	int rcode;
	int   id,op,qdcount,ancount,nscount,arcount;
	int i, do_mx_again;
	int fails[100];
	char tmp[BFSZ];
	int truncated = FALSE;
	int x;
	static int done_first;
	int oldnmx;
	MXREC *amx;
	*trunc = FALSE;

	strcpy(cur_question,"(NONE)");
	base = answer;
	id = get_short(&answer);
	x = mx_find(id);
	if (x<0) {
		if (noisey) imsg("dns: Unable to find id of request %d\n",id);
		return;
	}
	g_x = x;
	if ( (x<0) || (x>=nchan) ) {
		if (noisey) imsg("*** Warning *** Discarding old dns packet id=%d x=%d\n",id,x);
		return;
	}
	if ( mx[x].state != BK_READ ) {
		if (noisey) imsg("*** Warning *** Discarding old dns packet id=%d in state %d\n",id,mx[x].state);
		return;
	}
	mx[x].nask--;
if (noisey) imsg("dns: dns=%s REQUEST ID %d:%d %s oname=%s nask=%d\n",dnsip,x,id,mx[x].name,mx[x].oname,mx[x].nask);
	strcpy(bkcname,""); /* Clear the cname */
	ncpy(bkname,mx[x].name,BFSZ-1);

	op = get_short(&answer);
	rcode = op & 0x0f;

	truncated = FALSE;
	if ((op & 512) >0) truncated = TRUE; 
	if (truncated) imsg("dns: Truncated result (%x) %d\n",(int) op,truncated);
	*trunc = truncated;
	if (truncated) {
		if (noisey) imsg("dns: will retry...\n");
		return;
	}

	if (rcode==2) { // then need to try again as name server gave temporary failure...
		// Need to set a flag so it tries again or at least gives 'timeout' type response....
		if (noisey) imsg("dns: Name server failed - temporary error - so will need to try again (%s)\n",mx[x].oname);		
	}
	if (rcode==3) {
		if (noisey) imsg("dns: dns=%s Name server says that domain doesn't exist (%s)\n",dnsip,mx[x].oname);		
	}
	if (rcode==5) {
		imsg("dns: dns=%s Security error, your name server is not configured to allow the lookup (%s)\n",dnsip,mx[x].oname);		
	}
	
	qdcount = get_short(&answer);
	ancount = get_short(&answer);
	nscount = get_short(&answer);
	arcount = get_short(&answer);

	/* Add each MX name to an array, with priority and number */
	mx_free();
	nmx = 0;
	ismx = FALSE;
	for (amx = mx[x].mx; amx!=NULL; amx = amx->next) {
		if (nmx+4>MAXMX) {
			emsg("Too many MX entries for host %.80s\n",bkname);
		} else {
			mx_copy(&mxrecs[nmx++],amx);
		}
	}
	if (qdcount>100) {
		if (noisey) imsg("dns: show_q Bad data qdcount, discarding packet\n");
		return;
	}
	if ((ancount+nscount+arcount)>200) {
		if (noisey) imsg("dns: show_q Bad data qdcount, discarding packet\n");
		return;
	}
	for (i=0; i<qdcount; i++) {
		if (!show_q(&answer)) {
			if (noisey) imsg("dns: show_q Bad data, discarding packet\n");
			return;
		}
		if ( (char *)(answer + 1000) >= (char *)(bkans + sizeof(bkans)) ) {
			if (noisey) imsg("dns: Gone past end of packet\n");
			return;
		}
	}
	oldnmx = nmx;
if (noisey) imsg("dns dns=%s got result with %d %d %d records \n",dnsip,ancount,nscount,arcount);
	nullmx = FALSE;
	for (i=0; i<(ancount+nscount+arcount);i++) {
		if (!show_rr(&answer)) {
			if (noisey) imsg("dns: dns=%s show_rr Bad data, discarding packet\n",dnsip);
			return;
		}
		if ( (char *)(answer+1000) >= (char *)(bkans + sizeof(bkans)) ) {
			imsg("dns: Gone past end of packet\n");
			return;
		}
	}
	
	if (nmx==oldnmx) if (ismx) {
		if (noisey)  imsg("dns dns=%s Got no mx records back from mx lookup took %d seconds\n",dnsip,time(NULL) - mx[x].at);
		if (((time(NULL) - mx[x].at)>20) || (rcode==2)) {  // If took a long time, or if 'temporary failure' error, then retry.
			if (!mx[x].twice) {
				/* Repeat the original request, as this brain dead dns server will respond the next time*/
				if (noisey)  imsg("dns: Doing SECOND MX query for MX host %s\n",mx[x].name);
				mx[x].twice = TRUE;
				ncpy(tmp,mx[x].name,200);
				bk_start(x,tmp,T_MX,FALSE);
				return;
			}
		}
	}
	for (i=0; i<100; i++) fails[i] = FALSE;
	strcpy(bkresult,"");
	strcpy(bkcaddr,"");
	if (nmx>1) {
		qsort(mxrecs,nmx,sizeof(MXREC),mx_cmp);
	}
	do_mx_again=FALSE;

	if (nullmx) {
		if (noisey) imsg("dns: nullmx  record foudn (%s) reject messages\n",bkname);
		mx[x].invalid = TRUE; // HOPEFULLY THIS MAKES IT GIVE UP IMMEDIATELY...
		mx_failed(x,"(nullmx) record found");
		return;
	}
	for (i=0; i<nmx; i++)
	{
		if (!bk_havedone(x,mxrecs[i].name,1) && !mxrecs[i].gotipv4) {
				fails[i]=TRUE;
				do_mx_again=TRUE;
		} else if (mxrecs[i].addr==NULL) {
			if (noisey) imsg("dns: MX[%d] %s = %s  pri=%g\n",i,mxrecs[i].name,"UNKNOWN",mxrecs[i].pri);
			if (!bk_havedone(x,mxrecs[i].name,1)) {
				fails[i]=TRUE;
				do_mx_again=TRUE;
			} else {
				if (dns_debug) dmsg("dns Already looked up that mx record once");
			}
		}
		else
		{
			char tmp3[BFSZ];
			if (noisey) imsg("dns: MX[%d] %s = %s  pri=%g\n",i,mxrecs[i].name,mxrecs[i].addr,mxrecs[i].pri);
//printf("dns: MX[%d] %s = %s  pri=%g\n",i,mxrecs[i].name,mxrecs[i].addr,mxrecs[i].pri);
			if (mx[x].isorbs) { // anything non standard..
				//imsg("dns: not sorting result\n",mx[x].type);
				ncat(bkresult,mxrecs[i].addr,BFSZ-1);
				ncat(bkresult," ",BFSZ-1);
			} else {
				//imsg("dns: sorting result\n");
				ncat(bkresult,sort_ipv6(mxrecs[i].addr,tmp3),BFSZ-1);
				ncat(bkresult," ",BFSZ-1);
			}
		}
	}


	if (do_ipv6) { // Ask about any mx hosts to see if they have aaaa addresses.
		for(i=0;i<nmx;i++) {
			if (!bk_havedone(x,mxrecs[i].name,4)) {
				bk_donedid(x,mxrecs[i].name,4);
				if (noisey)  imsg("dns: Doing AAAA query for MX host %s\n",mxrecs[i].name);
				bk_start(x,mxrecs[i].name,T_AAAA,FALSE);
				goto done;
			}
		}
	}




	if (!do_mx_again) if (lib_strlen(bkresult)!=0) {
		dmsg("dns: bkresult and !domxagain (%s)\n",bkresult);
		mx_success(x,bkresult,TRUE);
		return;
	}

	mx[x].loop++;
	if (mx[x].loop>9)
	{
		imsg("dns: mx lookup loop has tried 9 times\n",bkname);
		if (lib_strlen(bkresult)>0) {
			if (noisey)  imsg("dns: mx lookup loop locked (%s), but se seem to have an answer\n",bkname);
			mx_success(x,bkresult,TRUE);
			return;
		} else {
			if (noisey)  imsg("dns: mx lookup loop locked (%s), and no answer\n",bkname);
			mx_failed(x,"dns loop");
			return;
		}
	}
//	if (!mx[x].isorbs )
	  if (do_mx_again) {
		for(i=0;i<nmx;i++) {
			/* Send plain query about mx host, should do this for each one. */
			if(fails[i]) if(!bk_havedone(x,mxrecs[i].name,1)) {
				if (noisey)  imsg("dns: Doing A query for MX host %s\n",mxrecs[i].name);
				bk_donedid(x,mxrecs[i].name,1);
				bk_start(x,mxrecs[i].name,T_A,FALSE);
				goto done;
			}
		}
	  }
	if (lib_strlen(bkcname)>0)
	{
		/* Send MX query about cname */
		if (!bk_havedone(x,bkcname,2)) {
			i = mx_add_name(bkcname,90000); // change from 120
			if (mxrecs[i].addr!=NULL) free(mxrecs[i].addr); mxrecs[i].addr = NULL;
			bk_donedid(x,bkcname,2);
			if (mx[x].last_type==T_TXT) {
				if (noisey)  imsg("dns: Doing TXT query with cname %s\n",bkcname);
				bk_start(x,bkcname,T_TXT,FALSE);
			} else {
				if (noisey)  imsg("dns: Doing mx query with cname %s\n",bkcname);
				bk_start(x,bkcname,T_MX,FALSE);
			}
			goto done;
		} else goto doplain;
	}
	else
	{
		/* Send plain query for original name*/
doplain:	if (mx[x].last_type==T_A || (mx[x].isorbs) )
		{
			if (noisey) imsg("*** Warning *** dns No address for %s\n",bkname);
			if (mx[x].nask<=0) {
				goto try_mx;
			}
			goto done;
		}
		if (noisey)  imsg("dns: Doing A query with original name %s ID was %d:%d, qdcount=%d (%s)\n",bkname,x,id,qdcount,cur_question);
		bk_start(x,bkname,T_A,FALSE);
		mx[x].fallback = TRUE;
		goto done;
	}

try_mx:
	if (mx[x].nask<=0) {
		if (!mx[x].isorbs )  for(i=0;i<nmx;i++) {
			/* Send plain query about mx host, should do this for each one. */
			if(!bk_havedone(x,mxrecs[i].name,2)) {
				dmsg("dns: Doing MX query for MX host %s\n",mxrecs[i].name);
				bk_donedid(x,mxrecs[i].name,2);
				bk_start(x,mxrecs[i].name,T_MX,FALSE);
				goto done;
			}
		}
		if (rcode==3) mx[x].invalid = TRUE; // Only invalid in this case. 
		mx_failed(x,"Nothing outstanding so finished");
		return;
	}

done:
	/* Copy array of mxrecs back into linked list */
	mx_free_mx(mx[x].mx); mx[x].mx = NULL;
	for (i=0; i<nmx; i++) {
		amx = malloc(sizeof(MXREC)); memset(amx,0,sizeof(MXREC));
		mx_copy(amx,&mxrecs[i]);
		amx->next = mx[x].mx;
		mx[x].mx = amx;
	}

}

int show_q(u_char **answer)
{
/* if we get a cname, then resend mx request with new name */
	char *s;
	int t,c;
	s = get_string(answer);
	t = (int)get_short(answer);
	c = (int)get_short(answer);
	/*if (dns_debug) */
	if (noisey) imsg("dns: Question: %s, type=%x, class=%x\n", s, t,c);
	if (t==T_MX) ismx = TRUE;
	strncpy(cur_question,s,90);
	cur_question[90] = 0;
	if (lib_strlen(s)<3) if (t==0) if (c==0) return FALSE;
	return TRUE;
}
char * rr_a(u_char **answer);
char * rr_mx(u_char **answer, int *pri);
char * rr_cname(u_char **answer);
static char mxaddr[BFSZ];
int show_rr(u_char **answer)
{
	int rdlen;
	char bf2[BFSZ];
	char host[BFSZ];
	int pri;
	u_char *s,*save;
	int xtype,xclass,ttl;
	char *cn;
	char *txt;
	nullmx = FALSE;
	s = get_string(answer);
	if (s == NULL) { strcpy(host, "(NULL)"); s = host; }
	else ncpy(host,s,BFSZ-1);
	xtype = get_short(answer);
	xclass = get_short(answer);
	ttl = get_long(answer);
	rdlen = get_short(answer);


       

	if (rdlen>1000) {
		return FALSE;
	}
//imsg("dns record type=%d class=%d ttl=%d rdlen =%d host (%s)\n",xtype,xclass,ttl,rdlen,host);
	save = *answer;
	sprintf(bf2,"%.90s t=%x c=%d ttl=%d len=%d ",host,xtype,xclass,ttl,rdlen);
	if (xtype==T_A) {
		rr_a(answer);
		if (noisey) imsg("dns: Got T_A address %s %s\n",s,mxaddr);
		if (strcmp(s,bkcname)==0) ncpy(bkcaddr,mxaddr,BFSZ-1);
		mx_add_addr(s,mxaddr,FALSE,TRUE);
	} else if (xtype==T_AAAA) {
		rr_aaaa(answer);
		if (noisey) imsg("dns: Got T_AAAA address %s %s\n",s,mxaddr);
		if (strcmp(s,bkcname)==0) ncpy(bkcaddr,mxaddr,BFSZ-1);

		if (do_ipv6) { // Ask about any mx hosts to see if they have aaaa addresses.
			mx_add_addr(s,mxaddr,FALSE,FALSE); // must not be true or it may  miss ipv4 addresses...
		} else {
			if (noisey) imsg("dns: Dropped T_AAAA address as ipv6 disabled %s %s\n",s,mxaddr);
		}
	} else if (xtype==T_CNAME) {
		cn = rr_cname(answer);
		dmsg("dns: GOT CNAME (%s) \n",cn);
		if (strcmpnc(bkname	,host)==0) {
			ncpy(bkcname,cn,BFSZ-1);
			mx_replace(bkname,bkcname);
		} else {
			if (noisey)  imsg("*** Warning *** Dont match (%s) (%s) \n",host,bkname);
		}
		dmsg("dns: %s CNAME %s\n",bf2,cn);
	} else if (xtype==T_MX) {
		cn = rr_mx(answer,&pri);
		if (dns_debug) dmsg("dns: %s MX %d %s\n",bf2,pri,cn);
		if (noisey) imsg("dns: MX %s %d (%s)\n",bf2,pri,cn);
	} else if (xtype==T_TXT) {
		char *start=*answer;
		int loop=0;
		txt = get_text(base,answer,start+rdlen);
		if (noisey)		imsg("dns: TXT %s\n",txt);
		mx_add_addr(s,txt,TRUE,TRUE);
	} else if (xtype==T_PTR) {
		txt = rr_cname(answer);
//printf("ptr result (%s) (%s)\n",s,txt);
		mx_add_addr(bkname,txt,FALSE,TRUE);
	} else if (xtype==T_NS) {
		txt = rr_cname(answer);
		//if (noisey) 
		if (noisey) imsg("dns: Name Server (%s)\n",txt);
	} else {
//printf("IGNORED RECORD %d\n",xtype);
		if (noisey) imsg("ignore %d\n",xtype);
		if (dns_debug) dmsg("dns: %s Ignored\n",bf2);
	}
	*answer = save;
	*answer += rdlen;
	return TRUE;
}
char *rr_a(u_char **answer)
{
	struct in_addr in;
	in.s_addr = htonl(get_long(answer)); /* Double convert as none is needed */
	ncpy(mxaddr,inet_ntoa(in),BFSZ-1);
	return mxaddr;
}
char *rr_aaaa(u_char **answer)	// 128 bit raw internet address...
{
	static char tmp[BFSZ];
	strcpy(tmp,"ipv6disabled");
#ifdef DO_IPV6
	{
		struct in6_addr addr;
//		printf("sizeof(addr) %d\n",sizeof(addr));	
		memcpy(&addr,*answer,16); (*answer) += 16;
		tcp_ipv6_to_string(&addr,tmp);
	}
#endif
	ncpy(mxaddr,tmp,BFSZ-1);
	return tmp;
}
char *rr_cname(u_char **answer)
{
	char *s;
	s = get_string(answer);
	return s;
}
char *rr_mx(u_char **answer, int *pri)
{
	char *name;
	*pri = get_short(answer);
	name = get_string(answer);
	if (lib_strlen(name)==0) {
//		imsg("dns: Stupid blank mx response, replaced with name itself (%s)\n",bkname);
		imsg("dns: Stupid blank mx response, I believe this is a nullmx record, meaning do not send email (%s)\n",bkname);
//		name = bkname;
		name = "(nullmx)";
	}
	mx_add_name(name,*pri);
	return name;
}
char *get_string(unsigned char **s)
{
/*
	Trying to describe this sytnax. 
	
	
	*/
	static char full[1024];
	static char bf[1024];
	int jmp = -1;
	int nloop = 0;
	u_char *j = *s;
	u_char *xend=NULL;
	unsigned int n = *j;
	strcpy(full,"");
	for (;n>0;) {
		nloop++;
		if (nloop>80) break;
		if (n>64) {
			jmp = get_short(&j);
			if (xend==NULL) xend = j;
			jmp = jmp & 0x3fff;
			j = jmp+base;
			n = *j;
			if (n>60) break;
		}
		if (n<0) n = 0;
		if (n>BFSZ-1) n = 0;
		strncpy(bf,j+1,n);
		bf[n] = 0;
		ncat(full,bf,BFSZ-1);
		ncat(full,".",BFSZ-1);
		j += n+1;
		n = *j;
//		printf("get string full %s\n",full);
	}
	j++;
	if (xend!=NULL) j = xend;
	*s = j;
	return full;
}


char *get_text(u_char *base, unsigned char **s, u_char *end)
{
	static char full[BFSZ];
	char tmp[BFSZ];
	int nloop=0;
//	int i;
	int jmp = -1;
	u_char *xend=NULL;
	strcpy(full,"");
	for (;*s < end;) {
		u_char *j = *s;
		unsigned int n = *j;
		if (n<0) break;
		if (n>255) break;
		ncpy(tmp,j+1,n);
		ncat(full,tmp,BFSZ-1);
		*s += n+1;
	}


	return full;	
}


int get_short(unsigned char **s)
{
	unsigned short x;
	memcpy(&x,*s,sizeof(short));
	x = (unsigned) htons(x);
	*s += 2;
	return x;
}
int get_long(unsigned char **s)
{
	int x;
	memcpy(&x,*s,sizeof(int));
	x = htonl(x);
	*s += 4;
	return x;
}

struct SYMS  rrtypes[] = {
	{T_A,		"A",		"address"},
	{T_NS,		"NS",		"name server"},
	{T_MD,		"MD",		"mail destination (deprecated)"},
	{T_MF,		"MF",		"mail forwarder (deprecated)"},
	{T_CNAME,	"CNAME",	"canonical name"},
	{T_SOA,		"SOA",		"start of authority"},
	{T_MB,		"MB",		"mailbox"},
	{T_MG,		"MG",		"mail group member"},
	{T_MR,		"MR",		"mail rename"},
	{T_NULL,	"NULL",		"null"},
	{T_WKS,		"WKS",		"well-known service (deprecated)"},
	{T_PTR,		"PTR",		"domain name pointer"},
	{T_HINFO,	"HINFO",	"host information"},
	{T_MINFO,	"MINFO",	"mailbox information"},
	{T_MX,		"MX",		"mail exchanger"},
	{T_TXT,		"TXT",		"text"},
	{T_RP,		"RP",		"responsible person"},
	{T_AFSDB,	"AFSDB",	"DCE or AFS server"},
	{T_X25,		"X25",		"X25 address"},
	{T_ISDN,	"ISDN",		"ISDN address"},
	{T_RT,		"RT",		"router"},
	{T_NSAP,	"NSAP",		"nsap address"},
	{T_NSAP_PTR,	"NSAP_PTR",	"domain name pointer"},
	{T_SIG,		"SIG",		"signature"},
	{T_KEY,		"KEY",		"key"},
	{T_PX,		"PX",		"mapping information"},
	{T_GPOS,	"GPOS",		"geographical position (withdrawn)"},
	{T_AAAA,	"AAAA",		"IPv6 address"},
	{T_LOC,		"LOC",		"location"},
#ifdef FREDXXX
	{T_NXT,		"NXT",		"next valid name (unimplemented)"},
	{T_EID,		"EID",		"endpoint identifier (unimplemented)"},
	{T_NIMLOC,	"NIMLOC",	"NIMROD locator (unimplemented)"},
	{T_SRV,		"SRV",		"server selection"},
	{T_ATMA,	"ATMA",		"ATM address (unimplemented)"},
	{T_IXFR,	"IXFR",		"incremental zone transfer"},
#endif
	{T_AXFR,	"AXFR",		"zone transfer"},
	{T_MAILB,	"MAILB",	"mailbox-related data (deprecated)"},
	{T_MAILA,	"MAILA",	"mail agent (deprecated)"},
#ifdef FREDXXX
	{T_NAPTR,	"NAPTR",	"URN Naming Authority"},
#endif
	{T_ANY,		"ANY",		"\"any\""},
	{0, 		NULL,		NULL}
};


char *dns_type_name(int x)
{
	int i;
	for (i=0;rrtypes[i].name!=NULL;i++) {
		if (rrtypes[i].op==x) return rrtypes[i].name;
	}
	return "Unknown";
}
/*------RESOLVER LIBRARY STUFF ----------------------------*/
int bk_mkquery(int x, int op, char *dname, int xclass
	    , int type, u_char *buf, int buflen)
{
	register HEADER *hp;
	register u_char *cp;
	register int n;
	u_char *dnptrs[20], **dpp, **lastdnptr;

	/*
	 * Initialize header fields.
	 */
//	if (type==T_PTR) op = IQUERY;
	if ((buf == NULL) || (buflen < HFIXEDSZ))
		return (-1);
	memset(buf, 0, HFIXEDSZ);
	hp = (HEADER *) buf;
	hp->id = htons((short) x);
	hp->opcode = op;

	hp->rd = 1; /* Recursion */
	hp->rcode = NOERROR;
	cp = buf + HFIXEDSZ;
	buflen -= HFIXEDSZ;
	dpp = dnptrs;
	*dpp++ = buf;
	*dpp++ = NULL;
	lastdnptr = dnptrs + sizeof dnptrs / sizeof dnptrs[0];
	/*
	 * perform opcode specific processing
	 */

	if ((buflen -= QFIXEDSZ) < 0)
		return (-1);
	n = bk_comp(dname,cp,buflen);
	cp += n;
	buflen -= n;
	bk_putshort((u_short) type, cp);
	cp += INT16SZ;
	bk_putshort((u_short) xclass, cp);
	cp += INT16SZ;
	hp->qdcount = htons(1);
	return (int)(cp - buf);
}

void bk_putshort(u_int src, u_char *dst)
{
	register u_int16_t t_s = (u_int16_t)(src);
	register u_char *t_cp = (u_char *)(dst);
	*t_cp++ = (u_char) (t_s >> 8);
	*t_cp   = (u_char) t_s;
	(dst) += NS_INT16SZ;
}


/*---------------------------------------------------------*/
#ifdef WIN32
int getdtablesize(void);
#endif


int mx_gotsome(int x)
{
	MXREC *amx;
	int i;

	mx_free();
	nmx = 0;
	for (amx = mx[x].mx; amx!=NULL; amx = amx->next) {
		if (nmx+4>MAXMX) {
			emsg("Too many MX entries for host %.80s\n",bkname);
		} else {
			mx_copy(&mxrecs[nmx++],amx);
		}
	}

	strcpy(bkresult,"");
	if (nmx>1) {
		qsort(mxrecs,nmx,sizeof(MXREC),mx_cmp);
	}
	for (i=0; i<nmx; i++)	{
		if (mxrecs[i].addr!=NULL) {
			if (noisey) imsg("dns: DONE mx[%d] addr=%s name=%s pri=%g\n",i,mxrecs[i].addr,mxrecs[i].name,mxrecs[i].pri);
			ncat(bkresult,mxrecs[i].addr,BFSZ-1);
			if (lib_strlen(mx[i].stype)>0) {
				ncat(bkresult,"\xfe",BFSZ-1);
			} else {
				ncat(bkresult," ",BFSZ-1);
			}			
		}
	}
	mx_free();
	if (lib_strlen(bkresult)!=0) {
		if (noisey) imsg("dns: gotsome (%s)",bkresult);
		mx_success(x,bkresult,TRUE);
		return TRUE;
	}
	return FALSE;
}
/*
3000 of them, 3000 * 80 = 240K
File format of disk cache.

  Name|result
  Name|result
  Name|result

Index in memory
  Name, Offset

Add
	Find, if different delete old entry
		Add new entry at end of file
Find, Seek and read line.

On startup, when data file wastage exceeds 30% re-write.



    idx file.



    #name

A single file.

dns name, cache string



*/
#define DISK_CACHE dns_file("dns_disk.dat")
typedef struct DCACHE {char *name; char *result; time_t when;  struct DCACHE *next; }Dcache ;
#define DHASH 1000
static Dcache *hcache[DHASH];
static int ndisk;

char *dns_file(char *fname)
{
	static char bf[BFSZ];

	if (lib_strlen(workarea)==0) strcpy(workarea,".");
	sprintf(bf,"%s/%s", workarea, fname);
	return bf;
}
int dns_free_disk_cache(void);
void dns_disk_clear(void)
{
	remove(DISK_CACHE);
	dns_free_disk_cache();
}
#define CACHESZ 3000
int dns_free_some(Dcache *last)
{
	Dcache *d,*next;
	for (d=last;d!=NULL;d=next) {
		next = d->next;
		ndisk--;
		free(d->name);
		free(d->result);
		free(d);
	} 
	return TRUE;
}
int dns_exit(void)
{
	int i;

	for (i=0; i<nchan; i++) {
		if (mx[i].state!=BK_IDLE) {
			imsg("dns_exit chan %d state %d\n",i,mx[i].state);
			mx_failed(i,"Shutting down dns_exit called");
		}
	}	
	dns_free_disk_cache();

	return TRUE;
}
int dns_free_disk_cache(void)
{
	int i;
	for (i=0; i<DHASH; i++) {
		dns_free_some(hcache[i]);
		hcache[i] = NULL;
	}
	return TRUE;
}
int dns_disk_trim(void)
{
	static int upto;
	int i;
	if (ndisk<CACHESZ) return TRUE;
	for (i=upto; i<DHASH; i++) {
		dns_free_some(hcache[i]);
		hcache[i] = NULL;
		if (ndisk<CACHESZ) return TRUE;
	}
	upto = 0;
	for (i=upto; i<DHASH; i++) {
		dns_free_some(hcache[i]);
		hcache[i] = NULL;
		if (ndisk<CACHESZ) return TRUE;
	}
	return TRUE;
}
int dns_disk_add(char *name, char *result, int dodisk)
{
	/* Find in memory if exists and result same return */
	/* If changed then append to file and change in memory */
	/* If not in memory add to memory and append to file  */
	static int nfix;
	Dcache *d;
	int n=0;
	int h;

	if (!ini_disk_cache) 		return FALSE;
	

	dns_disk_trim();
	if (lib_strlen(name)<=1) return FALSE;
	if (lib_strlen(name)>100) return FALSE;
	if (lib_strlen(result)>BFSZ-100) return FALSE;
	h = lib_str_hash(name,DHASH);
	for (d=hcache[h];d!=NULL;d=d->next) {
		if (strcmp(name,d->name)==0) {
			if (strcmp(result,d->result)==0) {
				return TRUE;
			}
			if (dodisk) if (time(NULL)- d->when < 60*60) {
				return TRUE; // Don't update if changed in last hour
			}
			free(d->result);
			d->when = time(NULL);
			d->result = strdup(result);
			nfix++;
			goto add_disk;
		}
	}
	ndisk++;
	d = calloc(1,sizeof(Dcache));
	d->when = time(NULL);
	d->name = strdup(name);
	d->result = strdup(result);
	d->next = hcache[h];
	hcache[h] = d;
add_disk:
/*	if (dodisk) {
		f = fopen(DISK_CACHE,"a");
		if (f==NULL) {
			emsg("dns: Couldn't write to %s %s\n",DISK_CACHE,strerror(errno));
			return FALSE;
		}
		fprintf(f,"%s|%s\n",name,result);
		fclose(f);
		if (nfix>500) {
			nfix = 0;
			if (noisey)  imsg("dns: Writing compressed dns disk cache\n");
			dns_disk_write();
		}
	}
	*/
	return TRUE;
}
int dns_disk_lookup(char *name, char *result)
{
	/* Find in memory, if exists return */
	Dcache *d;
	int h;
	if (noisey) imsg("dns: Cache lookup %d entries\n",ndisk);
	h = lib_str_hash(name,DHASH);
	for (d=hcache[h];d!=NULL;d=d->next) {
		if (strcmp(name,d->name)==0) {
			ncpy(result,d->result,BFSZ-1);
			if (noisey) imsg("dns: Cache lookup %d entries - found (%s) (%s)\n",ndisk,name,result);
			return TRUE;
		}
	}
	return FALSE;
}
int dns_disk_init(void)
{
	/* Read in disk cache, write out compressed */
	FILE *f;
	char name[BFSZ];
	char result[BFSZ];
	char bf[BFSZ];
	char *s;
	int nread=0;
	if (!ini_disk_cache) 		return FALSE;

	dmsg("dns: dns_disk_init called\n");
	f = fopen(DISK_CACHE,"r");
	if (f==NULL) {
		dmsg("dns Could not read dns disk cache %s %s\n",DISK_CACHE,strerror(errno));
		return FALSE;
	}
	for (;!feof(f);) {
		if (fgets(bf,BFSZ-1,f)==NULL) break;
		nread++;
		s = strtok(bf,"|\r\n"); if (s==NULL) continue;
		ncpy(name,s,BFSZ-1);
		s = strtok(NULL,"|\r\n"); if (s==NULL) continue;
		ncpy(result,s,BFSZ-1);
		dns_disk_add(name,result,FALSE);
	}
	fclose(f);
	dmsg("dns: dns_disk_init rewriting %d read %d\n",ndisk,nread);
	//dns_disk_write();
	dmsg("dns: dns_disk_init ndisk %d read %d\n",ndisk,nread);
	return TRUE;
}
int dns_disk_write(void)
{
	FILE *f;
	Dcache *d;
	int i;
	char bf[BFSZ];
	Wordlist *wlist=NULL,*w;
	if (!ini_disk_cache) 		return FALSE;

	f = fopen(DISK_CACHE,"w");
	if (f==NULL) {
		emsg("dns: Could not write to %s %s\n",DISK_CACHE,strerror(errno));
		return FALSE;
	}
	mutex_lookup_wait();
	for (i=0; i<DHASH; i++) {
		for (d=hcache[i];d!=NULL;d=d->next) {
			//fprintf(f,"%s|%s\n",d->name,d->result);
			lprintf(bf,BFSZ-1,"%s|%s\n",d->name,d->result);
			word_add(&wlist,bf);
		}
	}
	mutex_lookup_release();
	for (w=wlist;w!=NULL;w=w->next) {
		fprintf(f,"%s",w->word);
	}
	word_free(&wlist); 
	fclose(f);
	return TRUE;
}


