NetBSD/usr.sbin/named/db_dump.c

788 lines
19 KiB
C

/*
* Copyright (c) 1986, 1988, 1990 Regents of the University of California.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#ifndef lint
/*static char sccsid[] = "from: @(#)db_dump.c 4.33 (Berkeley) 3/3/91";*/
static char rcsid[] = "$Id: db_dump.c,v 1.3 1995/06/07 17:40:51 cgd Exp $";
#endif /* not lint */
#include <sys/param.h>
#include <sys/time.h>
#include <sys/stat.h>
#include <netinet/in.h>
#include <netdb.h>
#include <stdio.h>
#include <syslog.h>
#include <arpa/nameser.h>
#include <resolv.h>
#include "ns.h"
#include "db.h"
#include "pathnames.h"
#ifdef DUMPFILE
char *dumpfile = DUMPFILE;
#else
char *dumpfile = _PATH_DUMPFILE;
#endif
extern char *cache_file;
/*
* Dump current cache in a format similar to RFC 883.
*
* We try to be careful and determine whether the operation succeeded
* so that the new cache file can be installed.
*/
#define DB_ROOT_TIMBUF 3600
doachkpt()
{
extern int errno;
FILE *fp;
time_t dumptime;
char tmpcheckfile[256];
/* nowhere to checkpoint cache... */
if (cache_file == NULL) {
#ifdef DEBUG
if (debug >= 3)
fprintf(ddt,"doachkpt(to where?)\n");
#endif
return;
}
#ifdef DEBUG
if (debug >= 3)
fprintf(ddt,"doachkpt()\n");
#endif
(void) sprintf(tmpcheckfile, "%s.chk", cache_file);
if ((fp = fopen(tmpcheckfile, "w")) == NULL) {
#ifdef DEBUG
if (debug >= 3)
fprintf(ddt,"doachkpt(can't open %s for write)\n", tmpcheckfile);
#endif
return;
}
(void) gettime(&tt);
dumptime = tt.tv_sec;
fprintf(fp, "; Dumped at %s", ctime(&dumptime));
fflush(fp);
if (ferror(fp)) {
#ifdef DEBUG
if (debug >= 3)
fprintf(ddt,"doachkpt(write to checkpoint file failed)\n");
#endif
return;
}
if (fcachetab != NULL) {
int n;
if ((n = scan_root(hashtab)) < MINROOTS) {
syslog(LOG_ERR, "%d root hints... (too low)", n);
fprintf(fp, "; ---- Root hint cache dump ----\n");
(void) db_dump(fcachetab, fp, DB_Z_CACHE, "");
}
}
if (hashtab != NULL) {
fprintf(fp, "; ---- Cache dump ----\n");
if (db_dump(hashtab, fp, DB_Z_CACHE, "") == NODBFILE) {
#ifdef DEBUG
if (debug >= 3)
fprintf(ddt,"doachkpt(checkpoint failed)\n");
#endif
(void) fclose(fp);
return;
}
}
(void) fsync(fileno(fp));
if (fclose(fp) == EOF) {
#ifdef DEBUG
if (debug >= 3)
fprintf(ddt,"doachkpt(close failed)\n");
#endif
return;
}
if (rename(tmpcheckfile, cache_file)) {
#ifdef DEBUG
if (debug >= 3)
fprintf(ddt,"doachkpt(install %s to %s failed, %d)\n",
tmpcheckfile,cache_file, errno);
#endif
}
}
/*
* What we do is scan the root hint cache to make sure there are at least
* MINROOTS root pointers with non-0 TTL's so that the checkpoint will not
* lose the root. Failing this, all pointers are written out w/ TTL ~0
* (root pointers timed out and prime_cache() not done or failed).
*/
#define TIMBUF 300
int
scan_root(htp)
struct hashbuf *htp;
{
register struct databuf *dp;
register struct namebuf *np;
struct timeval soon;
int roots = 0;
#ifdef DEBUG
if (debug)
fprintf(ddt,"scan_root(0x%x)\n", htp);
#endif
/* metric by which we determine whether a root NS pointer is still */
/* valid (will be written out if we do a dump). we also add some */
/* time buffer for safety... */
(void) gettime(&soon);
soon.tv_sec += TIMBUF;
for (np = htp->h_tab[0]; np != NULL; np = np->n_next) {
if (np->n_dname[0] == '\0') {
dp = np->n_data;
while (dp != NULL) {
if (dp->d_type == T_NS &&
dp->d_ttl > soon.tv_sec) {
roots++;
if (roots >= MINROOTS)
return (roots);
}
dp = dp->d_next;
}
}
}
return (roots);
}
#ifdef notdef
mark_cache(htp, ttl)
struct hashbuf *htp;
int ttl;
{
register struct databuf *dp;
register struct namebuf *np;
struct namebuf **npp, **nppend;
struct timeval soon;
#ifdef DEBUG
if (debug)
fprintf(ddt,"mark_cache()\n");
#endif
(void) gettime(&soon);
soon.tv_sec += TIMBUF;
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;
for (dp = np->n_data; dp != NULL; dp = dp->d_next) {
if (dp->d_ttl < soon.tv_sec)
dp->d_ttl = ttl;
}
}
}
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;
mark_cache(np->n_hash, ttl);
}
}
}
#endif notdef
/*
* Dump current data base in a format similar to RFC 883.
*/
doadump()
{
FILE *fp;
time_t dumptime;
#ifdef DEBUG
if (debug >= 3)
fprintf(ddt,"doadump()\n");
#endif
if ((fp = fopen(dumpfile, "w")) == NULL)
return;
gettime(&tt);
dumptime = tt.tv_sec;
fprintf(fp, "; Dumped at %s", ctime(&dumptime));
fprintf(fp, "; --- Cache & Data ---\n");
if (hashtab != NULL)
(void) db_dump(hashtab, fp, DB_Z_ALL, "");
fprintf(fp, "; --- Hints ---\n");
if (fcachetab != NULL)
(void) db_dump(fcachetab, fp, DB_Z_ALL, "");
(void) fclose(fp);
}
#ifdef ALLOW_UPDATES
/* Create a disk database to back up zones
*/
zonedump(zp)
register struct zoneinfo *zp;
{
FILE *fp;
char *fname;
struct hashbuf *htp;
char *op;
struct stat st;
/* Only dump zone if there is a cache specified */
if (zp->z_source && *(zp->z_source)) {
#ifdef DEBUG
if (debug)
fprintf(ddt, "zonedump(%s)\n", zp->z_source);
#endif
if ((fp = fopen(zp->z_source, "w")) == NULL)
return;
if (op = index(zp->z_origin, '.'))
op++;
gettime(&tt);
htp = hashtab;
if (nlookup(zp->z_origin, &htp, &fname, 0) != NULL) {
db_dump(htp, fp, zp-zones, (op == NULL ? "" : op));
#ifdef ALLOW_UPDATES
zp->hasChanged = 0; /* Checkpointed */
#endif ALLOW_UPDATES
}
(void) fclose(fp);
if (stat(zp->z_source, &st) == 0)
zp->z_ftime = st.st_mtime;
}
#ifdef DEBUG
else if (debug)
fprintf(ddt, "zonedump: no zone to dump\n");
#endif
}
#endif
int
db_dump(htp, fp, zone, origin)
int zone;
struct hashbuf *htp;
FILE *fp;
char *origin;
{
register struct databuf *dp;
register struct namebuf *np;
struct namebuf **npp, **nppend;
char dname[MAXDNAME];
u_long n;
u_long addr;
u_short i;
int j;
register u_char *cp;
u_char *end;
char *proto;
extern char *inet_ntoa(), *protocolname(), *servicename();
int found_data, tab, printed_origin = 0;
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;
/* Blecch - can't tell if there is data here for the
* right zone, so can't print name yet
*/
found_data = 0;
/* we want a snapshot in time... */
for (dp = np->n_data; dp != NULL; dp = dp->d_next) {
/* Is the data for this zone? */
if (zone != DB_Z_ALL && dp->d_zone != zone)
continue;
if (dp->d_zone == DB_Z_CACHE &&
dp->d_ttl <= tt.tv_sec &&
(dp->d_flags & DB_F_HINT) == 0)
continue;
if (!printed_origin) {
fprintf(fp, "$ORIGIN %s.\n", origin);
printed_origin++;
}
tab = 0;
if (!found_data) {
if (np->n_dname[0] == 0) {
if (origin[0] == 0)
fprintf(fp, ".\t");
else
fprintf(fp, ".%s.\t", origin); /* ??? */
} else
fprintf(fp, "%s\t", np->n_dname);
if (strlen(np->n_dname) < 8)
tab = 1;
found_data++;
} else {
(void) putc('\t', fp);
tab = 1;
}
if (dp->d_zone == DB_Z_CACHE) {
if (dp->d_flags & DB_F_HINT &&
(long)(dp->d_ttl - tt.tv_sec) < DB_ROOT_TIMBUF)
fprintf(fp, "%d\t", DB_ROOT_TIMBUF);
else
fprintf(fp, "%d\t",
(int)(dp->d_ttl - tt.tv_sec));
} else if (dp->d_ttl != 0 &&
dp->d_ttl != zones[dp->d_zone].z_minimum)
fprintf(fp, "%d\t", (int)dp->d_ttl);
else if (tab)
(void) putc('\t', fp);
fprintf(fp, "%s\t%s\t", p_class(dp->d_class),
p_type(dp->d_type));
cp = (u_char *)dp->d_data;
/*
* Print type specific data
*/
switch (dp->d_type) {
case T_A:
switch (dp->d_class) {
case C_IN:
case C_HS:
GETLONG(n, cp);
n = htonl(n);
fprintf(fp, "%s",
inet_ntoa(*(struct in_addr *)&n));
break;
}
if (dp->d_nstime)
fprintf(fp, "\t; %d", dp->d_nstime);
fprintf(fp, "\n");
break;
case T_CNAME:
case T_MB:
case T_MG:
case T_MR:
case T_PTR:
if (cp[0] == '\0')
fprintf(fp, ".\n");
else
fprintf(fp, "%s.\n", cp);
break;
case T_NS:
cp = (u_char *)dp->d_data;
if (cp[0] == '\0')
fprintf(fp, ".\t");
else
fprintf(fp, "%s.", cp);
if (dp->d_nstime)
fprintf(fp, "\t; %d???", dp->d_nstime);
fprintf(fp, "\n");
break;
case T_HINFO:
if (n = *cp++) {
fprintf(fp, "\"%.*s\"", (int)n, cp);
cp += n;
} else
fprintf(fp, "\"\"");
if (n = *cp++)
fprintf(fp, " \"%.*s\"", (int)n, cp);
else
fprintf(fp, " \"\"");
(void) putc('\n', fp);
break;
case T_SOA:
fprintf(fp, "%s.", cp);
cp += strlen((char *)cp) + 1;
fprintf(fp, " %s. (\n", cp);
cp += strlen((char *)cp) + 1;
GETLONG(n, cp);
fprintf(fp, "\t\t%lu", n);
GETLONG(n, cp);
fprintf(fp, " %lu", n);
GETLONG(n, cp);
fprintf(fp, " %lu", n);
GETLONG(n, cp);
fprintf(fp, " %lu", n);
GETLONG(n, cp);
fprintf(fp, " %lu )\n", n);
break;
case T_MX:
GETSHORT(n, cp);
fprintf(fp,"%lu", n);
fprintf(fp," %s.\n", cp);
break;
case T_TXT:
end = (u_char *)dp->d_data + dp->d_size;
(void) putc('"', fp);
while (cp < end) {
if (n = *cp++) {
for (j = n ; j > 0 && cp < end ; j--)
if (*cp == '\n') {
(void) putc('\\', fp);
(void) putc(*cp++, fp);
} else
(void) putc(*cp++, fp);
}
}
(void) fputs("\"\n", fp);
break;
case T_UINFO:
fprintf(fp, "\"%s\"\n", cp);
break;
case T_UID:
case T_GID:
if (dp->d_size == sizeof(u_long)) {
GETLONG(n, cp);
fprintf(fp, "%lu\n", n);
}
break;
case T_WKS:
GETLONG(addr, cp);
addr = htonl(addr);
fprintf(fp,"%s ",
inet_ntoa(*(struct in_addr *)&addr));
proto = protocolname(*cp);
cp += sizeof(char);
fprintf(fp, "%s ", proto);
i = 0;
while(cp < (u_char *)dp->d_data + dp->d_size) {
j = *cp++;
do {
if (j & 0200)
fprintf(fp," %s",
servicename(i, proto));
j <<= 1;
} while(++i & 07);
}
fprintf(fp,"\n");
break;
case T_MINFO:
fprintf(fp, "%s.", cp);
cp += strlen((char *)cp) + 1;
fprintf(fp, " %s.\n", cp);
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");
}
}
}
}
if (ferror(fp))
return(NODBFILE);
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));
if (db_dump(np->n_hash, fp, zone, dname) == NODBFILE)
return(NODBFILE);
}
}
return(OK);
}
#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' */
(void) 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