#ifndef lint
static char sccsid[] = "@(#)db_dump.c	4.3 (Berkeley) 5/30/86";
#endif

/*
 * Copyright (c) 1986 Regents of the University of California
 *	All Rights Reserved
 */

#include <sys/types.h>
#include <sys/time.h>
#include <netinet/in.h>
#include <netdb.h>
#include <stdio.h>
#include <syslog.h>
#include <arpa/nameser.h>
#include "ns.h"
#include "db.h"

extern	char *p_type(), *p_class();

#ifdef DUMPFILE
char	*dumpfile = DUMPFILE;
#else
char	*dumpfile = "/usr/tmp/named_dump.db";
#endif

/*
 * Dump current data base in a format similar to RFC 883.
 */

doadump()
{
	FILE	*fp;

#ifdef DEBUG
	if (debug >= 3)
		fprintf(ddt,"doadump()\n");
#endif

	if ((fp = fopen(dumpfile, "w")) == NULL)
		return;
	fprintf(fp, "$ORIGIN .\n");
	if (hashtab != NULL)
		db_dump(hashtab, fp);
	(void) fclose(fp);
}

db_dump(htp, fp)
	struct hashbuf *htp;
	FILE *fp;
{
	register struct databuf *dp;
	register struct namebuf *np;
	struct namebuf **npp, **nppend;
	char dname[MAXDNAME];
	long n;
	u_long addr;
	u_short i;
	int j;
	char *cp;
	char *proto;
	extern char *inet_ntoa(), *p_protocal(), *p_service();


	npp = htp->h_tab;
	nppend = npp + htp->h_size;
	while (npp < nppend) {
	    for (np = *npp++; np != NULL; np = np->n_next) {
		if (np->n_data == NULL)
			continue;
		fprintf(fp, "%s\t", np->n_dname);
		if (strlen(np->n_dname) < 8)
			(void) putc('\t', fp);
		for (dp = np->n_data; dp != NULL; dp = dp->d_next) {
			if (dp != np->n_data)
				fprintf(fp, "\t\t");
			if (dp->d_zone == 0) {
				if (gettimeofday(&tt, (struct timezone *)0) < 0)
					syslog(LOG_ERR, "gettimeofday: %m");
				if ((n = dp->d_ttl - tt.tv_sec) >= 0)
					fprintf(fp, "%d\t", n);
			} else if (dp->d_ttl > zones[dp->d_zone].z_minimum)
				fprintf(fp, "%d\t", dp->d_ttl);
			fprintf(fp, "%s\t%s\t", p_class(dp->d_class),
				p_type(dp->d_type));
			cp = dp->d_data;
			/*
			 * Print type specific data
			 */
			switch (dp->d_type) {
			case T_A:
				switch (dp->d_class) {
				case C_IN:
					n = htonl(getlong(cp));
					fprintf(fp, "%s\n",
					   inet_ntoa(*(struct in_addr *)&n));
					break;
				}
				break;
			case T_CNAME:
			case T_MB:
			case T_MG:
			case T_MR:
			case T_NS:
			case T_PTR:
				if (cp[0] == '\0')
					fprintf(fp, ".\n");
				else
					fprintf(fp, "%s.\n", cp);
				break;

			case T_HINFO:
				if (n = *cp++) {
					fprintf(fp, "%.*s", n, cp);
					cp += n;
				} else
					fprintf(fp, "\"\"");
				if (n = *cp++)
					fprintf(fp, " %.*s", n, cp);
				else
					fprintf(fp, "\"\"");
				(void) putc('\n', fp);
				break;

			case T_SOA:
				fprintf(fp, "%s", cp);
				cp += strlen(cp) + 1;
				fprintf(fp, " %s\n", cp);
				cp += strlen(cp) + 1;
				fprintf(fp, "\t\t%d", getlong(cp));
				cp += sizeof(u_long);
				fprintf(fp, " %d", getlong(cp));
				cp += sizeof(u_long);
				fprintf(fp, " %d", getlong(cp));
				cp += sizeof(u_long);
				fprintf(fp, " %d", getlong(cp));
				cp += sizeof(u_long);
				fprintf(fp, " %d\n", getlong(cp));
				break;

			case T_MX:
				fprintf(fp,"%d", getshort(cp));
				cp += sizeof(u_short);
				fprintf(fp," %s\n", cp);
				break;


#ifdef	ALLOW_T_UNSPEC
#ifdef	HESIOD
			case T_UNSPECA:
#endif
#endif	ALLOW_T_UNSPEC
			case T_UINFO:
				fprintf(fp, "%s\n", cp);
				break;

			case T_UID:
			case T_GID:
				if (dp->d_size == sizeof(u_long)) {
					fprintf(fp, "%d\n", getlong(cp));
					cp += sizeof(u_long);
				}
				break;

			case T_WKS:
				addr = htonl(getlong(cp));	
				fprintf(fp,"%s ",
				    inet_ntoa(*(struct in_addr *)&addr));
				cp += sizeof(u_long);
				proto = p_protocal(*cp); /* protocal */
				cp += sizeof(char); 
				fprintf(fp, "%s ", proto);
				i = 0;
				while(cp < dp->d_data + dp->d_size) {
					j = *cp++;
					do {
						if(j & 0200)
							fprintf(fp," %s",
							   p_service(i, proto));
						j <<= 1;
					} while(++i & 07);
				} 
				fprintf(fp,"\n");
				break;
#ifdef ALLOW_T_UNSPEC
			case T_UNSPEC:
				/* Dump binary data out in an ASCII-encoded
				   format */
				{
					/* Allocate more than enough space:
					 *  actually need 5/4 size + 20 or so
					 */
					int TmpSize = 2 * dp->d_size + 30;
					char *TmpBuf = (char *) malloc(TmpSize);
					if (TmpBuf == NULL) {
#ifdef DEBUG
					    if (debug)
						fprintf(ddt, "Dump T_UNSPEC: malloc returned NULL\n");
#endif DEBUG
						syslog(LOG_ERR, "Dump T_UNSPEC: malloc: %m");
					}
					if (btoa(cp, dp->d_size, TmpBuf,
						 TmpSize) == CONV_OVERFLOW) {
#ifdef DEBUG
						if (debug)
						    fprintf(ddt, "Dump T_UNSPEC: Output buffer overflow\n");
#endif DEBUG
						    syslog(LOG_ERR, "Dump T_UNSPEC: Output buffer overflow\n");
					} else
						fprintf(fp, "%s\n", TmpBuf);
				}
				break;
#endif ALLOW_T_UNSPEC

			default:
				fprintf(fp, "???\n", cp);
			}
		}
	    }
	}
	npp = htp->h_tab;
	nppend = npp + htp->h_size;
	while (npp < nppend) {
	    for (np = *npp++; np != NULL; np = np->n_next) {
		if (np->n_hash == NULL)
			continue;
		getname(np, dname, sizeof(dname));
		fprintf(fp, "$ORIGIN %s.\n", dname);
		db_dump(np->n_hash, fp);
	    }
	}
}

/*  These next two routines will be moveing to res_debug.c */
/* They are currently here for ease of distributing the WKS record fix */
char *
p_protocal(num)
int num;
{
	struct protoent *pp;
	pp = getprotobynumber(num);
	if(pp == 0)  
		return("???");
	return(pp->p_name);
}

char *
p_service(port, proto)
u_short port;
char *proto;
{
	struct servent *ss;
	ss = getservbyport((int)htons(port), proto);
	if(ss == 0)  
		return("???");
	return(ss->s_name);
}

#ifdef ALLOW_T_UNSPEC
/*
 * Subroutines to convert between 8 bit binary bytes and printable ASCII.
 * Computes the number of bytes, and three kinds of simple checksums.
 * Incoming bytes are collected into 32-bit words, then printed in base 85:
 *	exp(85,5) > exp(2,32)
 * The ASCII characters used are between '!' and 'u';
 * 'z' encodes 32-bit zero; 'x' is used to mark the end of encoded data.
 *
 * Originally by Paul Rutter (philabs!per) and Joe Orost (petsd!joe) for
 * the atob/btoa programs, released with the compress program, in mod.sources.
 * Modified by Mike Schwartz 8/19/86 for use in BIND.
 */

/* Make sure global variable names are unique */
#define Ceor T_UNSPEC_Ceor
#define Csum T_UNSPEC_Csum
#define Crot T_UNSPEC_Crot
#define word T_UNSPEC_word
#define bcount T_UNSPEC_bcount

static long int Ceor, Csum, Crot, word, bcount;

#define EN(c)	((int) ((c) + '!'))
#define DE(c) ((c) - '!')
#define AddToBuf(bufp, c) **bufp = c; (*bufp)++;
#define streq(s0, s1)	strcmp(s0, s1) == 0
#define times85(x)	((((((x<<2)+x)<<2)+x)<<2)+x)


/* Decode ASCII-encoded byte c into binary representation and 
 * place into *bufp, advancing bufp 
 */
static int
byte_atob(c, bufp)
	register c;
	char **bufp;
{
	if (c == 'z') {
		if (bcount != 0)
			return(CONV_BADFMT);
		else {
			putbyte(0, bufp);
			putbyte(0, bufp);
			putbyte(0, bufp);
			putbyte(0, bufp);
		}
	} else if ((c >= '!') && (c < ('!' + 85))) {
		if (bcount == 0) {
			word = DE(c);
			++bcount;
		} else if (bcount < 4) {
			word = times85(word);
			word += DE(c);
			++bcount;
		} else {
			word = times85(word) + DE(c);
			putbyte((int)((word >> 24) & 255), bufp);
			putbyte((int)((word >> 16) & 255), bufp);
			putbyte((int)((word >> 8) & 255), bufp);
			putbyte((int)(word & 255), bufp);
			word = 0;
			bcount = 0;
		}
	} else
		return(CONV_BADFMT);
	return(CONV_SUCCESS);
}

/* Compute checksum info and place c into *bufp, advancing bufp */
static
putbyte(c, bufp)
	register c;
	char **bufp;
{
	Ceor ^= c;
	Csum += c;
	Csum += 1;
	if ((Crot & 0x80000000)) {
		Crot <<= 1;
		Crot += 1;
	} else {
		Crot <<= 1;
	}
	Crot += c;
	AddToBuf(bufp, c);
}

/* Read the ASCII-encoded data from inbuf, of length inbuflen, and convert
   it into T_UNSPEC (binary data) in outbuf, not to exceed outbuflen bytes;
   outbuflen must be divisible by 4.  (Note: this is because outbuf is filled
   in 4 bytes at a time.  If the actual data doesn't end on an even 4-byte
   boundary, there will be no problem...it will be padded with 0 bytes, and
   numbytes will indicate the correct number of bytes.  The main point is
   that since the buffer is filled in 4 bytes at a time, even if there is
   not a full 4 bytes of data at the end, there has to be room to 0-pad the
   data, so the buffer must be of size divisible by 4).  Place the number of
   output bytes in numbytes, and return a failure/success status  */
int
atob(inbuf, inbuflen, outbuf, outbuflen, numbytes)
	char *inbuf;
	int inbuflen;
	char *outbuf;
	int outbuflen;
	int *numbytes;
{
	int inc, nb;
	long int oeor, osum, orot;
	char *inp, *outp = outbuf, *endoutp = &outbuf[outbuflen];

	if ( (outbuflen % 4) != 0)
		return(CONV_BADBUFLEN);
	Ceor = Csum = Crot = word = bcount = 0;
	for (inp = inbuf, inc = 0; inc < inbuflen; inp++, inc++) {
		if (outp > endoutp)
			return(CONV_OVERFLOW);
		if (*inp == 'x') {
			inp +=2;
			break;
		} else {
			if (byte_atob(*inp, &outp) == CONV_BADFMT)
				return(CONV_BADFMT);
		}
	}

	/* Get byte count and checksum information from end of buffer */
	if(sscanf(inp, "%ld %lx %lx %lx", numbytes, &oeor, &osum, &orot) != 4)
		return(CONV_BADFMT);
	if ((oeor != Ceor) || (osum != Csum) || (orot != Crot))
		return(CONV_BADCKSUM);
	return(CONV_SUCCESS);
}

/* Encode binary byte c into ASCII representation and place into *bufp,
   advancing bufp */
static
byte_btoa(c, bufp)
	register c;
	char **bufp;
{
	Ceor ^= c;
	Csum += c;
	Csum += 1;
	if ((Crot & 0x80000000)) {
		Crot <<= 1;
		Crot += 1;
	} else {
		Crot <<= 1;
	}
	Crot += c;

	word <<= 8;
	word |= c;
	if (bcount == 3) {
		if (word == 0) {
			AddToBuf(bufp, 'z');
		} else {
		    register int tmp = 0;
		    register long int tmpword = word;
			
		    if (tmpword < 0) {	
			   /* Because some don't support unsigned long */
		    	tmp = 32;
		    	tmpword -= (long)(85 * 85 * 85 * 85 * 32);
		    }
		    if (tmpword < 0) {
		    	tmp = 64;
		    	tmpword -= (long)(85 * 85 * 85 * 85 * 32);
		    }
		    AddToBuf(bufp,
		    	 EN((tmpword / (long)(85 * 85 * 85 * 85)) + tmp));
		    tmpword %= (long)(85 * 85 * 85 * 85);
		    AddToBuf(bufp, EN(tmpword / (85 * 85 * 85)));
		    tmpword %= (85 * 85 * 85);
		    AddToBuf(bufp, EN(tmpword / (85 * 85)));
		    tmpword %= (85 * 85);
		    AddToBuf(bufp, EN(tmpword / 85));
		    tmpword %= 85;
		    AddToBuf(bufp, EN(tmpword));
		}
		bcount = 0;
	} else {
		bcount += 1;
	}
}


/*
 * Encode the binary data from inbuf, of length inbuflen, into a
 * null-terminated ASCII representation in outbuf, not to exceed outbuflen
 * bytes. Return success/failure status
 */
int
btoa(inbuf, inbuflen, outbuf, outbuflen)
	char *inbuf;
	int inbuflen;
	char *outbuf;
	int outbuflen;
{
	long int inc, nb;
	long int oeor, osum, orot;
	char *inp, *outp = outbuf, *endoutp = &outbuf[outbuflen -1];

	Ceor = Csum = Crot = word = bcount = 0;
	for (inp = inbuf, inc = 0; inc < inbuflen; inp++, inc++) {
		byte_btoa((unsigned char) (*inp), &outp);
		if (outp >= endoutp)
			return(CONV_OVERFLOW);
	}
	while (bcount != 0) {
		byte_btoa(0, &outp);
		if (outp >= endoutp)
			return(CONV_OVERFLOW);
	}
	/* Put byte count and checksum information at end of buffer, delimited
	   by 'x' */
	sprintf(outp, "x %ld %lx %lx %lx", inbuflen, Ceor, Csum, Crot);
	if (&outp[strlen(outp) - 1] >= endoutp)
		return(CONV_OVERFLOW);
	else
		return(CONV_SUCCESS);
}
#endif ALLOW_T_UNSPEC
