#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 "tcp.h"
#include "dns.h"
#include "dmail.h"

#ifdef unix
#undef socket_close
#define socket_close(a) socketclosemonitor(__FILE__, __LINE__, a)
int socketopenmonitor(char *file, int line, int sk);
int socketclosemonitor(char *file, int line, int sk);
#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 n, int nocache, int type);
int mx_bkgnd(void);


static int nopen;

/* Internal functions */
int bk_lookup(int ch, char *name);
int bk_isbusy(int ch);
int bk_tick(int x);
int bk_init(void);
int bk_start(int x, char *name, int xtype, int isnew);
int bk_get(char *s);
int bk_send(int x);
int bk_nextns(int x,char *reason);
int bk_open(int x);
int bk_read(int x);
int bk_mkquery(int x, int op, char *dname, int xclass
	    , int type, u_char *data, int datalen);
int mxc_age(int force);
int bk_close(int x);

int bk_comp(char *dname, char *cp);
void bk_putshort(u_int src, u_char *dst) ;
int mxc_add(char *name, char *result);

int strcmpnc(const char *s1, const char *s2);
int strncmpnc(const char *s1, const char *s2, size_t n);
int mxc_addfail(char *name);
int mx_any_busy(void);

int dns_disk_write(void);
int dns_disk_init(void);
int dns_disk_lookup(char *name, char *result);
int dns_disk_add(char *name, char *result, int dodisk);

extern int werrno;
#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);
static char bkresult[BFSZ];
static char bkcname[BFSZ];
static int bkns;
static int bkns_n,bkupto;
static int bktimeout=90; /*90;*/ /* After 10 seconds, try other one */
static int bklen;
static char bkhosts[10][100];
static int bkid=1000; /*ID of each dns request */
enum {BK_IDLE,BK_START,BK_SEND,BK_READ,BK_READY,BK_FAILED};
struct SYMS { int op; char *name; char *description; };
void mx_free(void);
int mxc_addok(char *name, char *result);

#define MAX_CURRENT 200
int reqid[MAX_CURRENT];
static SOCKET bksk[MAX_CURRENT];
static char *bksk_name[MAX_CURRENT];
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; int pri; struct MXREC_S *next;} MXREC;

typedef struct MXCHAN {
	int nocache;
	int retry;
	char *oname;
	int nask;
	int loop;
	int at;
	int state;
	int invalid;
	Done *done;
	MXREC *mx;
	int n;
	char *name;
	void (*fn)(int n, char *s);
	int last_type,type,type_want,twice;
} Mxchan; 

static Mxchan mx[MAX_CURRENT];
static int nchan,mxnew;
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 char current_dns[BFSZ];

void mx_setfd(fd_set *readfd,fd_set *writefd)
{
	int x;
	for (x=0; x<MAX_CURRENT; x++) {
		if (bksk[x]!=0) if (bksk[x]!=INVALID_SOCKET) FD_SET(bksk[x],readfd);
	}
}

int mx_lookup_que(char *xname, void (*fn)(int n, char *s),int n,int nocache,int type)
{
	int i;
	char name[BFSZ];
	strcpy(name,xname);
	if (name[strlen(name)-1]!='.') strcat(name,".");
	for (i=0; i<MAX_CURRENT; i++) if (mx[i].state==BK_IDLE) break;
	if (i>=MAX_CURRENT) { emsg("** Could not lookup MX records as limit exceeded\n"); return 2;}
	vmsg("** 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);
	mx[i].fn = fn;
	mx[i].retry = 0;
	mx[i].type_want = type;
	mx[i].nocache = nocache;
	mx[i].invalid = FALSE;
	mx_init_one(i);
	if (i>=nchan) nchan = i+1;
	return TRUE;
}
int mx_init_one(int i)
{
	mxnew = TRUE;
	mx[i].state = BK_START;
	mx[i].loop = 0;
	mx[i].nask = 0;
	mx[i].twice = FALSE;
	mx_free_done(mx[i].done); mx[i].done = NULL;
	mx_free_mx(mx[i].mx); mx[i].mx = NULL;
	return TRUE;
}
static int mx_nok,mx_nfail,mx_start,mx_ncached,mx_ncached_fails;
static int dns_fails;
static int ngood;
void mx_success(int x,char *result, int addcache)
{
	char result_copy[BFSZ];
	void (*fn)(int n, char *s);	

	if (addcache) ngood++;
	mx_nok++;
	if (addcache) dns_fails = 0;
	strncpy(result_copy,result,BFSZ-10);
	result_copy[BFSZ-10] = 0;
	vmsg("** mx lookup success for channel %d (%s)\n",mx[x].n, result);
	if (addcache)
	{
		mxc_addok(mx[x].oname,result);
		dns_disk_add(mx[x].oname,result,TRUE);
		stats_add(STAT_DNS, 2, 0);
	}
	else
		stats_add(STAT_DNS, 1, 0);
	fn = mx[x].fn;
	(*fn)(mx[x].n,result_copy);
	mx_freeall(x);
}
int mx_failed(int x, char *reason)
{
	char bf[BFSZ];
	char was[BFSZ];
	void (*fn)(int n, char *s);	
	mx_nfail++;
	strcpy(was,"");
	if (x>=0) ncpy(was,mx[x].oname,100);
	vmsg("** DNS (%s) mx lookup (%s) failed %s\n",current_dns,was,reason);
	if (x<0) return FALSE; /* We don't know which request so let it time out */

	fn = mx[x].fn;
	sprintf(bf,"FAILED %s",mx[x].oname);

	if ((!ini_true(M_DNS_DISK_DISABLE)) && (!mx[x].nocache)) {
		if (strcmp(reason,"Cached failure")==0) {
			emsg("DNS cached failure\n");
			goto cont;
		}
		if (dns_disk_lookup(mx[x].oname,bf)) {
			emsg("DNS Using disk cache %.90s %.90s\n",mx[x].oname,bf);
			mx_success(x,bf,FALSE);		
			return FALSE;
		} else {
			emsg("DNS disk cache couldn't find address (%.90s) %.90s\n",mx[x].oname,bf);
		}
	}
cont:
	strcpy(bf,"");
	mxc_addfail(mx[x].oname);

	if (mx[x].invalid) strcpy(bf,"invalid_domain");
	if (mx[x].n>=0) {
		(*fn)(mx[x].n,bf);
	} else {
		emsg("** Tried to call mx_failed when 'n' was invalid\n");
	}
	stats_add(STAT_DNS, 3, 0);
	mx_freeall(x);
	return FALSE;
}
char *mx_status(void)
{
	static char bf[BFSZ];
	int now = time(NULL);
	if (mx_start==now) now++;
	sprintf(bf,"DNS nopen=%d nok=%d nlookups=%d nfailed_real=%d nfailed=%d Total speed=%4.2f DNS speed=%4.2f"
		,nopen,mx_nok,mx_nfail+mx_nok-mx_ncached,mx_nfail-mx_ncached_fails,mx_nfail,(double) (mx_nok+mx_nfail)/(now-mx_start),(double) (mx_nok+mx_nfail-mx_ncached)/(now-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;		
	bk_close(x);
}

#define CACHE_SIZE 1000
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 int mxc_next;
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 (strlen(mxc_result[i])==0) {
			if (mxc_nfail[i]<2) continue;
			if ((t-mxc_time[i])>100) 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("** 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(strlen(s)+1);
	strcpy(b,s);
	return b;
}
int mxc_addfail(char *name)
{
	int r;
	r = mxc_find_raw(name);
	if (r<0) return mxc_add(name,"");
	mxc_nfail[r]++;
	vmsg("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);
	vmsg("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 (strlen(result)==0) mxc_nfail[i]=1;
	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_retry_all(int i)
{
	if (mx[i].state!=BK_IDLE) {
		mx_init_one(i);
		mx[i].retry++;
		if ((mx[i].retry>bkns_n) && (mx[i].retry>3)) {
			mx_failed(i,"Tried all name servers");
		}
	}
	return TRUE;
}
int mx_bkgnd(void)
{
	int c;
	int i;
	static int last;
	int t = time(NULL);

	if ((t-last)>60) {
		vmsg("mx: %s\n",mx_status());
		last = t;
	}


	if (mxnew) {
		for (i=0; i<nchan; i++) {
			if (mx[i].state == BK_START) {
				c = mxc_find(mx[i].oname);
				if (c>=0) {
				 if (strlen(mxc_result[c])==0) {
				   vmsg("** mx got cached response OF FAILURE ch=%d c=%d (%s)",mx[i].n,c,mxc_result[c]);
				   mx[i].invalid = TRUE;
				   mx_failed(i,"Cached failure");
				   mx_ncached_fails++;
				 } else {
				   vmsg("** mx got cached response ch=%d c=%d (%s)",mx[i].n,c,mxc_result[c]);
				   mx_success(i,mxc_result[c],FALSE);
				 }
				 mx_ncached++;
				}
				else {
					if (nopen<10) bk_lookup(i,mx[i].oname);
				}
			}
		}
		mxnew = FALSE;
	}
	for (i=0; i<nchan; i++) bk_tick(i);
	mxc_age(FALSE);
	return TRUE;
}

int bk_lookup(int ch, char *name)
{
	mx_free();
	if(name[strlen(name)-1]=='\n') name[strlen(name)-1]=0;
	return bk_start(ch,name,T_MX,TRUE);
}

int bk_init(void)
{
	int n, i;
	int x;
	static int done;
	mx_start = time(NULL);
	bkns_n=0;
	bkns = 0;

	if (!done) {
		for (x=0; x<MAX_CURRENT; x++) {
			bksk[x] = INVALID_SOCKET;
		}
	}
	done = TRUE;

	if (ini_arrayn(M_DNSTIMEOUT)>0) bktimeout=atoi(ini_array(M_DNSTIMEOUT, 0));
	if (bktimeout<35) {
		/*TRW2.8d; vmsg("DNS: Adjusting timeout to %d seconds, as %d is too low\n",bktimeout,35);*/
		vmsg("DNS: Adjusting timeout to %d seconds, as %d is too low\n",35,bktimeout);
		bktimeout = 35;
	}
	dns_disk_init();
	n=ini_arrayn(M_DNSHOST);
	if(n==0)
	{
#ifdef unix
		char *s;
		FILE *fin;
		char bf[100];

		fin=fopen("/etc/resolv.conf", "r");
		if(fin==NULL)
			return FALSE;
		while(!feof(fin))
		{
			if(fgets(bf, 99, fin)==NULL)
				continue;
			s = strtok(bf," \r\n\t");
			if (s==NULL) continue;	
			if(strcmp(s, "nameserver")==0) {
				s = strtok(NULL," \r\n\t");
				if (s==NULL) continue;
				strcpy(bkhosts[bkns_n++], s);
			}
		}
		fclose(fin);
		if(bkns_n==0)
			return FALSE;
		else
			return TRUE;
#endif
#ifdef WIN32
#define KEY_COMPUTER_NAME "SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters"
    HKEY MyKey;
	char Line[64], *s, *t;
    int  linelen = 64;
    DWORD type_of_entry, result;
/*
	FIXED_INFO Info;

	result=GetNetworkParams(&Info, sizeof(FIXED_INFO));

	if(result==ERROR_SUCCESS)
	{
	}
*/
	RegOpenKey(HKEY_LOCAL_MACHINE, KEY_COMPUTER_NAME, &MyKey);
	result = RegQueryValueEx(MyKey, "NameServer", 0, &type_of_entry, 
		(unsigned char *)Line, &linelen);
	RegCloseKey(MyKey);
	if (result == ERROR_SUCCESS && type_of_entry == REG_SZ)
	{
		t=Line;
		while((*t==' ')||(*t=='\t'))
			t++;
		if(*t==0)
			return FALSE;

		s=strchr(t, ' ');
		while(s!=NULL)
		{
			*s++=0;
			while((*s==' ')||(*s=='\t'))
				s++;
			if(*s==0)
				break;
			strcpy(bkhosts[bkns_n++], t);
			t=s;
			s=strchr(t, ' ');
		}
		if(*t!=0)
			strcpy(bkhosts[bkns_n++], t);

		if(bkns_n==0)
			return FALSE;

		return TRUE;
	}
	else
		return FALSE;
#endif
	}
	if(n>10) n=10;
	for(i=0;i<n;i++)
	{
		if(strlen(ini_array(M_DNSHOST, i))>0)
			strcpy(bkhosts[bkns_n++], ini_array(M_DNSHOST, i));
	}


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

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

	strcpy(bf,dname);
	s = strtok(bf,".");
	for (;s!=NULL;) {
		n = strlen(s);
		*cp++ = (char ) n;
		strcpy(cp,s);
		cp += n;
		s = strtok(NULL,".");
	}
	*cp = 0;
	cp++;
	return cp-cpstart;
}

#define MAXPACKET (8*1024)
static char bkname[BFSZ];
int bk_send(int x) /* Send the current request to the current name server*/
{
	u_char buf[MAXPACKET];
	int n;
	static int lastmsg;
	short ss;
	int r;
	
	mx[x].state = BK_SEND;

	strcpy(bkname,mx[x].name);
	reqid[x]++;
	if (reqid[x]>99) reqid[x] = 2;
	n = bk_mkquery(x*100+reqid[x],QUERY, bkname, C_IN,mx[x].type, buf, sizeof(buf));
	if (n <= 0) { 	mx_failed(x,"mkquery failed"); 	return FALSE;}
	errno = 0;
	if (!bk_open(x)) return TRUE;
	if (bksk[x]==INVALID_SOCKET) return TRUE;
	ss = htons((short) n);
	r = socket_write(bksk[x],(char *) &ss,2);
	tcp_lasterror();
	if (r!=2) goto xxx;
	r = socket_write(bksk[x],buf,n);
	tcp_lasterror();
xxx:	if (r!=n) {
		if (r>0) {
			emsg("** Partial socket write to DNS failure\n");
			goto nextone;
		}
		if (tcp_errbusy()) {
			if (lastmsg+5<time(NULL)) { 
				dmsg("** DNS Waiting to send lookup [%d] %s to %s",x,bkname,current_dns);
				lastmsg = time(NULL);
			}
			return TRUE;
		}
#ifdef WIN32
		if (werrno==WSAENOTCONN)
			return TRUE;
#else
        if (werrno==ENOTCONN) return TRUE;  
#endif
		tcp_perror("write");
		emsg("*** Error *** DNS socket_write failed %d\n",werrno);
nextone:	if (bk_nextns(x,"socket_write")) return TRUE;
		if(r==0)
			return TRUE;
		else
			return FALSE;
	}
	mx[x].at = time(NULL);
	mx[x].state = BK_READ;
	return TRUE;
}
int bk_nextns(int x, char *reason)
{
	bkns++;
	emsg("**DNS Trying next name server due to (%s) from %s",reason,current_dns);
	if (bksk[x]!=INVALID_SOCKET) {
		socket_close(bksk[x]);
		nopen--;
	}
	bksk[x] = INVALID_SOCKET;
	if (bkns>=bkns_n) {
		bkns = 0;
		mx_retry_all(x);
		return FALSE;
	}
	mx_retry_all(x);
	return TRUE;
}
int bk_close(int x)
{
	if (bksk_name[x]!=NULL) free(bksk_name[x]);
	bksk_name[x] = NULL;
	if (bksk[x]!=INVALID_SOCKET) {
		socket_close(bksk[x]);
		bksk[x] = INVALID_SOCKET;
		nopen--;
	}
	return TRUE;
}
int bk_open(int x)
{
	int dst_port=53;
        struct sockaddr_in xsin;
	char name[BFSZ];
        int st;
	static time_t lastused;
	int w = TRUE;
	struct hostent *hp;

	if (bksk[x]==0) bksk[x] = INVALID_SOCKET;
	if (bksk[x]!=INVALID_SOCKET) return TRUE;
	if (bkns>=bkns_n) {return FALSE;}
	strcpy(name,bkhosts[bkns]);
	vmsg("DNS: ** DNS Connect to (%s)\n",name);
	ncpy(current_dns,name,100);
	if (bksk_name[x]!=NULL) free(bksk_name[x]);
	bksk_name[x] = strdup(name);
	memset(&xsin,0,sizeof(xsin));
	if(lib_isip(name))
	{
		xsin.sin_family = AF_INET;
		xsin.sin_addr.s_addr = inet_addr(name);
	}
	else
	{
		xsin.sin_addr.s_addr = 0;
		hp = gethostbyname(name);
		if (hp==NULL) {
			emsg("** DNS host lookup of dns server (%s) failed",name);
			return FALSE;
		}
		memcpy(&(xsin.sin_addr),hp->h_addr,  hp->h_length);
		xsin.sin_family = hp->h_addrtype;
	}
	bksk[x] = socket(xsin.sin_family,SOCK_STREAM, 0);
#ifdef unix
	socketopenmonitor(__FILE__, __LINE__, bksk[x]);
#endif
	tcp_lasterror();		
	if (bksk[x]==INVALID_SOCKET) {
	        emsg("*** Error *** DNS Can't open socket to {%s} ",name);
	        return FALSE;
	}
	nopen++; 
	xsin.sin_port = htons((unsigned short) dst_port);
	if (socket_ioctl(bksk[x],FIONBIO,&w)!=0) {
		emsg("*** Error *** DNS Can't set socket as non blocking");
		socket_close(bksk[x]);
		bksk[x] = INVALID_SOCKET;
		nopen--;
		return FALSE;
	}

        st = connect(bksk[x],(struct sockaddr *)   &xsin, sizeof(xsin));
	tcp_lasterror();
     	if (st!=0) {
		if (tcp_errbusy()) return TRUE;
		tcp_perror(name);
		emsg("*** Error *** DNS Can't connect to (%s)",name);
		socket_close(bksk[x]);
		bksk[x] = INVALID_SOCKET;
		nopen--;
		bk_nextns(x,"connect");
		return FALSE;
        }
	return TRUE;
}

int bk_timeout(int x)
{
	int r;
	static time_t last_timeout;
	char bf[BFSZ];
	int max_switch = atoi(ini_array(M_DNS_SWITCH_NFAILS, 0));
	if (max_switch==0) max_switch = 7;
	if (mx[x].state==BK_IDLE) return FALSE;
	if (mx[x].state==BK_READY) return FALSE;
	if (mx[x].state==BK_FAILED) return FALSE;
	if ((time(NULL) - mx[x].at)>bktimeout) {
		sprintf(bf,"timedout %d",time(NULL) - mx[x].at);
		mx[x].at = time(NULL);
		if (mx[x].type_want!=T_PTR)
		{
			if (mx_gotsome(x)) return FALSE;
		}
		if (max_switch>0) mx_failed(x,bf);
		if ((time(NULL)-last_timeout)>300) {
		 if (dns_fails>max_switch) {
			if ((bkns_n>1) || (dns_fails>100)) {
				dns_fails = 0;
				last_timeout = time(NULL);
				r = bk_nextns(x,"timeout");		
				return !r;
			} else vmsg("DNS: Not switching to next DNS server as we only have the one\n");
		 } else {
			 vmsg("DNS: Not switching as only had %d/%d fails see dns_switch_nfails\n",dns_fails,max_switch);
		 }
		} else {
			vmsg("DNS: Not switching to next server yet as did that %d seconds ago\n",time(NULL)-last_timeout);
		}
		dns_fails++;
	}
	return FALSE;
}
int bk_tick(int x)
{
	if (bk_timeout(x))
	{
		return TRUE;
	}
	switch (mx[x].state) {
	  case BK_IDLE:
		break;
	  case BK_SEND:
		bk_send(x);
		break;
	  case BK_READ:
		bk_read(x);
		break;
	  case BK_READY:
		return TRUE;
	  case BK_FAILED:
		return TRUE;
	}

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

int bk_read(int x)
{
	/* Try and read, if we get data then process it
	   send new request if CNAME is response */
	int n;
	short len;
	if (!bk_open(x)) return FALSE;
	
	n = socket_read(bksk[x],(char *) &len,2);
	tcp_lasterror();
	if (n<2) {
		if (tcp_errbusy()) return TRUE;		
		wsock_error("*** Error *** DNS socket_read failed ");
		imsg("** Socket read got only %d of %d bytes wanted \n",n,2);
		return bk_nextns(x,"bk_read"); /* Try next if exists */
	} 
	len = htons(len);
	
	if ((len<0) || (len>sizeof(bkans))) {
		emsg("** DNS Lookup got packet length %d %d",len,sizeof(bkans));
		wsock_error("Received bad packet length");
		bk_nextns(x,"packet size");
		return FALSE;
	}
	bklen = len;
	bkupto = 0;
	return bk_reading(x);
}
void lib_msleep(int x)
{
#ifdef WIN32
	Sleep(x);
#else
	usleep(x*1000);
#endif
}
void bk_process(char *ans, int len);
int bk_reading(int x)
{
	int n;
	int tot=0;
	int wanted;
	
again:	wanted = bklen-bkupto;
	if (bksk[x]==INVALID_SOCKET) if (!bk_open(x)) return FALSE;
	n = socket_read(bksk[x],bkans+bkupto,wanted);
	tcp_lasterror();
	if (n<(wanted)) {
		if (tcp_errbusy() || (n>0)) {
			if (n>0) bkupto += n;
			lib_msleep(10);
			tot++;
			if (tot>1000) {
				bk_nextns(x,"got_rubish_from_dns_timeout");
				emsg("** Socket read dns returned garbage, trying next name server %d/%d %d/%d",n,wanted,bkupto,bklen);
				return FALSE;
			}
			goto again;
		}
		wsock_error("*** Error *** DNS socket_read error ");
		bk_nextns(x,"socket_read");
		emsg("** Socket read failed, trying next name server %d/%d %d/%d",n,wanted,bkupto,bklen);
		if (n>0) emsg("** Socket read failed, PROBABLE FAULT IN dns code\n");
		return FALSE;
	}
	if (tot>0) emsg("** DNS waited %d/100ths of a second for the packet to arrive\n",tot);
	bk_process(bkans,bklen);
	return TRUE;
}
void show_query(unsigned char *answer);
void bk_process(char *ans, int len)
{
	char caton[256];
	int x;

	memset(caton, 0, 255);
	x=1;
	memcpy(caton, ans, len>255?255:len);
	x=2;
	strcpy(bkresult,"");
	show_query((u_char *) ans);
}
/*---------------------------------------------------------*/
/*------My decoding STUFF ----------------------------*/
char *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("** mx replace %s -> %s",name,newname);
	for (i=0; i<nmx; i++) {
		m = &mxrecs[i];
		if (dns_debug) dmsg("** mx replace %d %s",i,m->name);
		if (strcmpnc(name,m->name)==0) {
			if (dns_debug) dmsg("** Replacing mx entry [%d] %s = %s",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;
	}
	nmx = 0;
}
int is_digits(char *s)
{
	for (;*s!=0;s++) {
		if (isdigit(*s)) continue;
		if (*s=='.') continue;
		return FALSE;
	}
	return TRUE;

}
int mx_add_name(char *name, int pri)
{
	int i;
	char *s;
	dmsg("** mx_add_name(%s,%d) ",name,pri);
	for (i=0; i<nmx; i++) {
		if (strcmpnc(name,mxrecs[i].name)==0) {
			if (pri<mxrecs[i].pri) mxrecs[i].pri = pri;
			return i;
		}
	}
	if ((nmx+1)>=MAXMX) {
		emsg("Too many MX entries for host %s",name);
		return 0;
	}
	mxrecs[nmx].name = strdup(name);
	mxrecs[nmx].addr = NULL;
	if (is_digits(name)) {
		mxrecs[nmx].addr = strdup(name);
		s = mxrecs[nmx].addr;
		if (strlen(s)>1) s[strlen(s)-1] = 0;
	}
	mxrecs[nmx++].pri = pri;
	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 a->pri - b->pri;
}
void mx_add_addr(char *name, char *addr)
{
	int i;

	for (i=0; i<nmx; i++) {
		if (strcmpnc(mxrecs[i].name,name)==0) {
			if (mxrecs[i].addr!=NULL) free(mxrecs[i].addr);
			mxrecs[i].addr = strdup(addr);
		}
	}
	/* (check if name is the original name) */
	if (strcmpnc(name,bkname)==0) {
		i = mx_add_name(name,120);
		if (mxrecs[i].addr!=NULL) free(mxrecs[i].addr);
		mxrecs[i].addr = strdup(addr);
	}
}

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("** donedid %d %s mask=%d (%d)",x,name,d->mask,mask);
			return;
		}
	}
	d = malloc(sizeof(Done));
	d->next = mx[x].done;
	d->s = strdup(name);
	d->mask = mask;
	dmsg("** donedid %d %s mask=%d (%d)",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);
	}
}
static char cur_question[BFSZ];
static char bkcaddr[BFSZ];
static int g_x;
void show_query(unsigned char *answer)
{
	int   id,op,qdcount,ancount,nscount,arcount;
	int i, do_mx_again;
	int fails[100];
	char tmp[BFSZ];
	int x;
	int oldnmx;
	MXREC *amx;

	strcpy(cur_question,"(NONE)");
	base = answer;
	id = get_short(&answer);
	x = id / 100;
	g_x = x;
	if ( (x<0) || (x>=nchan) ) {
		dmsg("*** Warning *** Discarding old dns packet id=%d x=%d",id,x);
		return;
	}
	if ( reqid[x] != (id%100) ) {
		dmsg("*** Warning *** Discarding old dns packet id=%d x=%d",id,x);
		return;
	}
	if ( mx[x].state != BK_READ ) {
		dmsg("*** Warning *** Discarding old dns packet id=%d in state %d",id,mx[id].state);
		return;
	}
	mx[x].nask--;
	dmsg("** REQUEST ID %d:%d %s oname=%s nask=%d",x,id,mx[x].name,mx[x].oname,mx[x].nask);
	strcpy(bkcname,""); /* Clear the cname */
	strcpy(bkname,mx[x].name);

	op = get_short(&answer);
	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",bkname);
		} else {
			mx_copy(&mxrecs[nmx++],amx);
		}
	}
	if (qdcount>100) {
		vmsg("** show_q Bad data qdcount, discarding packet\n");
		socket_close(bksk[x]);
		bksk[x] = INVALID_SOCKET;
		nopen--;
		return;
	}
	if ((ancount+nscount+arcount)>200) {
		vmsg("** show_q Bad data qdcount, discarding packet\n");
		socket_close(bksk[x]);
		bksk[x] = INVALID_SOCKET;
		nopen--;
		return;
	}
	for (i=0; i<qdcount; i++) {
		if (!show_q(&answer)) {
			vmsg("** show_q Bad data, discarding packet\n");
			socket_close(bksk[x]);
			bksk[x] = INVALID_SOCKET;
			nopen--;
			return;
		}
		if ((answer+1000)>=bkans+sizeof(bkans)) {
			vmsg("** Gone past end of packet\n");
			socket_close(bksk[x]);
			nopen--;
			bksk[x] = INVALID_SOCKET;
			return;
		}
	}
	oldnmx = nmx;
	for (i=0; i<(ancount+nscount+arcount);i++) {
		if (!show_rr(&answer)) {
			vmsg("** show_rr Bad data, discarding packet\n");
			socket_close(bksk[x]);
			bksk[x] = INVALID_SOCKET;
			nopen--;
			return;
		}
		if ((answer+1000)>=bkans+sizeof(bkans)) {
			vmsg("** Gone past end of packet\n");
			socket_close(bksk[x]);
			bksk[x] = INVALID_SOCKET;
			nopen--;
			return;
		}
	}
	if (nmx==oldnmx) if (ismx) {
		vmsg("** Got no mx records back from mx lookup took %d seconds\n",time(NULL) - mx[x].at);
		if ((time(NULL) - mx[x].at)>20) {
			if (!mx[x].twice) {
				/* Repeat the original request, as this brain dead dns server will respond the next time*/
				vmsg("** DNS Doing SECOND MX query for MX host %s",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;
	for (i=0; i<nmx; i++)
	{
		if (mx[x].type_want==T_PTR)
		{
			if(mxrecs[i].name!=NULL)
			{
				strcat(bkresult,mxrecs[i].name);
				strcat(bkresult," ");
			}
			continue;
		}
		if (mxrecs[i].addr==NULL)
		{
			if (dns_debug) dmsg("** DNS MX[%d] %s = %s  pri=%d",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("** Already looked up that mx record once");
			}
		}
		else
		{
			if (dns_debug) dmsg("** DNS MX[%d] %s = %s  pri=%d",i,mxrecs[i].name,mxrecs[i].addr,mxrecs[i].pri);
			strcat(bkresult,mxrecs[i].addr);
			strcat(bkresult," ");
		}
	}
	if (!do_mx_again) if (strlen(bkresult)!=0) {
		dmsg("** bkresult and !domxagain (%s)",bkresult);
		mx_success(x,bkresult,TRUE);
		return;
	}
	
	mx[x].loop++;
	if (mx[x].loop>5)
	{
		if (strlen(bkresult)>0) {
			vmsg("** DNS mx lookup loop locked (%s), but se seem to have an answer",bkname);
			mx_success(x,bkresult,TRUE);
			return;
		} else {
			vmsg("** DNS mx lookup loop locked (%s), and no answer",bkname);
			mx_failed(x,"DNS loop");
			return; 
		}
	}
	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)) {
				vmsg("** DNS Doing A query for MX host %s",mxrecs[i].name);
				bk_donedid(x,mxrecs[i].name,1);
				bk_start(x,mxrecs[i].name,T_A,FALSE);
				goto done;
			}
		}
	}
	if (strlen(bkcname)>0)
	{
		/* Send MX query about cname */
		if (!bk_havedone(x,bkcname,2)) {
			i = mx_add_name(bkcname,120);
			if (mxrecs[i].addr!=NULL) free(mxrecs[i].addr);
			bk_donedid(x,bkcname,2);
			vmsg("** DNS Doing mx query with cname %s",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)
		{
			if (mx[x].nask<=0) {
				goto try_mx;
			}
			vmsg("** DNS no address found for (%s)",bkname);
			goto done;
		}
			if (mx[x].last_type==T_PTR) {
				vmsg("** DNS no name found for (%s)",bkname);
				return;
			}
		vmsg("** DNS Doing A query with original name %s ID was %d:%d, qdcount=%d (%s)",bkname,x,id,qdcount,cur_question);
		bk_start(x,bkname,T_A,FALSE);
		goto done;
	}

try_mx:
	if (mx[x].nask<=0) {
		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",mxrecs[i].name);
				bk_donedid(x,mxrecs[i].name,2);
				bk_start(x,mxrecs[i].name,T_MX,FALSE);
				goto done;
			}
		}
		mx[x].invalid = TRUE;
		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) */
	dmsg("** DNS Question: %s, type=%x, class=%x", s, t,c);
	if (t==T_MX) ismx = TRUE;
	strncpy(cur_question,s,90);
	cur_question[90] = 0;
	if (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;
	s = get_string(answer);
	if (s==NULL) strcpy(host,"(NULL)");
	else strcpy(host,s);
	xtype = get_short(answer);
	xclass = get_short(answer);
	ttl = get_long(answer);
	rdlen = get_short(answer);
	
	if (rdlen>1000) {
		return FALSE;
	}
	save = *answer;
	sprintf(bf2,"%s t=%x c=%d ttl=%d len=%d ",host,xtype,xclass,ttl,rdlen);
	if (xtype==T_A) {
		rr_a(answer);
		dmsg("** Got T_A address %s %s",s,mxaddr);
		if (strcmp(s,bkcname)==0) strcpy(bkcaddr,mxaddr);
		mx_add_addr(s,mxaddr);
	} else if (xtype==T_CNAME) {
		cn = rr_cname(answer); 
		dmsg("** DNS GOT CNAME (%s) ",cn);
		if (strcmpnc(bkname	,host)==0) {
			strcpy(bkcname,cn);
			mx_replace(bkname,bkcname);
		} else {
			vmsg("*** Warning *** Dont match (%s) (%s) ",host,bkname);
		}
		dmsg("** DNS %s CNAME %s",bf2,cn);
	} else if (xtype==T_PTR) {
		cn = rr_cname(answer);
		mx_add_name(cn,0);	
		dmsg("** DNS got PTR (%s)", cn);
		strcpy(bkresult,cn);
	} else if (xtype==T_MX) {
		cn = rr_mx(answer,&pri);
		if (dns_debug) dmsg("** DNS %s MX %d %s",bf2,pri,cn);
	} else {
		if (dns_debug) dmsg("** DNS %s Ignored",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 */
	strcpy(mxaddr,inet_ntoa(in));
	return mxaddr;
}
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);
	mx_add_name(name,*pri);	
	return name;
}
char *get_string(unsigned char **s)
{
	static char full[1024];
	static char bf[1024];
	int jmp = -1;
	u_char *j = *s;
	u_char *xend=NULL;
	unsigned int n = *j;
	strcpy(full,"");
	for (;n>0;) {
		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) break;
		}
		strncpy(bf,j+1,n);
		bf[n] = 0;
		strcat(full,bf);
		strcat(full,".");
		j += n+1;
		n = *j;
	}
	j++;
	if (xend!=NULL) j = xend;
	*s = j;
	return full;
}
int get_short(unsigned char **s)
{
	short x;
	memcpy(&x,*s,sizeof(short));
	x = 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 *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 ((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);
	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 (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

void wsock_error(char *s);

/*
 * This array is designed for mapping upper and lower case letter
 * together for a case independent comparison.  The mappings are
 * based upon ascii character sequences.
 */
static const u_char charmap[] = {
	0000, 0001, 0002, 0003, 0004, 0005, 0006, 0007,
	0010, 0011, 0012, 0013, 0014, 0015, 0016, 0017,
	0020, 0021, 0022, 0023, 0024, 0025, 0026, 0027,
	0030, 0031, 0032, 0033, 0034, 0035, 0036, 0037,
	0040, 0041, 0042, 0043, 0044, 0045, 0046, 0047,
	0050, 0051, 0052, 0053, 0054, 0055, 0056, 0057,
	0060, 0061, 0062, 0063, 0064, 0065, 0066, 0067,
	0070, 0071, 0072, 0073, 0074, 0075, 0076, 0077,
	0100, 0141, 0142, 0143, 0144, 0145, 0146, 0147,
	0150, 0151, 0152, 0153, 0154, 0155, 0156, 0157,
	0160, 0161, 0162, 0163, 0164, 0165, 0166, 0167,
	0170, 0171, 0172, 0133, 0134, 0135, 0136, 0137,
	0140, 0141, 0142, 0143, 0144, 0145, 0146, 0147,
	0150, 0151, 0152, 0153, 0154, 0155, 0156, 0157,
	0160, 0161, 0162, 0163, 0164, 0165, 0166, 0167,
	0170, 0171, 0172, 0173, 0174, 0175, 0176, 0177,
	0200, 0201, 0202, 0203, 0204, 0205, 0206, 0207,
	0210, 0211, 0212, 0213, 0214, 0215, 0216, 0217,
	0220, 0221, 0222, 0223, 0224, 0225, 0226, 0227,
	0230, 0231, 0232, 0233, 0234, 0235, 0236, 0237,
	0240, 0241, 0242, 0243, 0244, 0245, 0246, 0247,
	0250, 0251, 0252, 0253, 0254, 0255, 0256, 0257,
	0260, 0261, 0262, 0263, 0264, 0265, 0266, 0267,
	0270, 0271, 0272, 0273, 0274, 0275, 0276, 0277,
	0300, 0301, 0302, 0303, 0304, 0305, 0306, 0307,
	0310, 0311, 0312, 0313, 0314, 0315, 0316, 0317,
	0320, 0321, 0322, 0323, 0324, 0325, 0326, 0327,
	0330, 0331, 0332, 0333, 0334, 0335, 0336, 0337,
	0340, 0341, 0342, 0343, 0344, 0345, 0346, 0347,
	0350, 0351, 0352, 0353, 0354, 0355, 0356, 0357,
	0360, 0361, 0362, 0363, 0364, 0365, 0366, 0367,
	0370, 0371, 0372, 0373, 0374, 0375, 0376, 0377
};

int
strcmpnc(const char *s1, const char *s2) {
	const u_char *cm = charmap,
		     *us1 = (const u_char *)s1,
		     *us2 = (const u_char *)s2;

	while (cm[*us1] == cm[*us2++])
		if (*us1++ == '\0')
			return (0);
	return (cm[*us1] - cm[*--us2]);
}

int
strncmpnc(const char *s1, const char *s2, size_t n) {
	if (n != 0) {
		const u_char *cm = charmap,
			     *us1 = (const u_char *)s1,
			     *us2 = (const u_char *)s2;

		do {
			if (cm[*us1] != cm[*us2++])
				return (cm[*us1] - cm[*--us2]);
			if (*us1++ == '\0')
				break;
		} while (--n != 0);
	}
	return (0);
}


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) {
			strcat(bkresult,mxrecs[i].addr);
			strcat(bkresult," ");
		}
	}
	mx_free();
	if (strlen(bkresult)!=0) {
		dmsg("** 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; struct DCACHE *next; }Dcache ;
static Dcache *dcache;
static int ndisk;

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

	sprintf(bf,"%s%s", ini_array(M_WORK, 0), fname);
	return bf;
}
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,*last;
	FILE *f;

	if (ndisk>1000) {
		for (d=dcache;d!=NULL;d=d->next)  {
			if (d->next!=NULL) last=d;
		}		
		if (last!=NULL) {
			ndisk--;
			d = last->next;
			free(d->name);
			free(d->result);
			free(d);
			last->next = NULL;	
		}
	}
	if (strlen(name)<=1) return FALSE;
	if (strlen(name)>100) return FALSE;
	if (strlen(result)>BFSZ-100) return FALSE;
	for (d=dcache;d!=NULL;d=d->next) {
		if (strcmp(name,d->name)==0) {
			if (strcmp(result,d->result)==0) return TRUE;
			free(d->result);
			d->result = strdup(result);
			nfix++;
			goto add_disk;
		}
	}
	ndisk++;
	d = calloc(1,sizeof(Dcache));
	d->name = strdup(name);
	d->result = strdup(result);
	d->next = dcache;
	dcache = d;
add_disk:
	if (dodisk) {
		f = fopen(DISK_CACHE,"a");
		if (f==NULL) {
			emsg("** Couldn't write to %s %s\n",DISK_CACHE,strerror(errno));
			return FALSE;
		}
		fprintf(f,"%s|%s\n",name,result);
		fclose(f);
		if (nfix>100) {
			nfix = 0;
			vmsg("** 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;
	for (d=dcache;d!=NULL;d=d->next) {
		if (strcmp(name,d->name)==0) {
			ncpy(result,d->result,BFSZ-1);
			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;

	f = fopen(DISK_CACHE,"r");
	if (f==NULL) {
		emsg("** 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;
		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);
	dns_disk_write();
	return TRUE;
}	
int dns_disk_write(void)
{
	FILE *f;
	Dcache *d;
	f = fopen(DISK_CACHE,"w");
	if (f==NULL) {
		emsg("** Could not write to %s %s\n",DISK_CACHE,strerror(errno));
		return FALSE;
	}	
	for (d=dcache;d!=NULL;d=d->next) {
		fprintf(f,"%s|%s\n",d->name,d->result);
	}
	fclose(f);			
	return TRUE;
}
