masterdump.c revision 9aba20edee4e704433a464ae43b070b0775de506
/*
* Copyright (C) 1999-2001 Internet Software Consortium.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM
* DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
* INTERNET SOFTWARE CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
* FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
* NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
* WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
/* $Id: masterdump.c,v 1.57 2001/09/04 14:18:27 marka Exp $ */
#include <config.h>
#include <stdlib.h>
#include <dns/dbiterator.h>
#include <dns/fixedname.h>
#include <dns/masterdump.h>
#include <dns/rdataclass.h>
#include <dns/rdataset.h>
#include <dns/rdatasetiter.h>
#include <dns/rdatatype.h>
#define RETERR(x) do { \
isc_result_t _r = (x); \
if (_r != ISC_R_SUCCESS) \
return (_r); \
} while (0)
struct dns_master_style {
unsigned int flags; /* DNS_STYLEFLAG_* */
unsigned int ttl_column;
unsigned int class_column;
unsigned int type_column;
unsigned int rdata_column;
unsigned int line_length;
unsigned int tab_width;
};
/*
* Flags affecting master file formatting. Flags 0x0000FFFF
* define the formatting of the rdata part and are defined in
* rdata.h.
*/
/* Omit the owner name when possible. */
#define DNS_STYLEFLAG_OMIT_OWNER 0x00010000U
/*
* Omit the TTL when possible. If DNS_STYLEFLAG_TTL is
* also set, this means no TTLs are ever printed
* because $TTL directives are generated before every
* change in the TTL. In this case, no columns need to
* be reserved for the TTL. Master files generated with
* these options will be rejected by BIND 4.x because it
* does not recognize the $TTL directive.
*
* If DNS_STYLEFLAG_TTL is not also set, the TTL will be
* omitted when it is equal to the previous TTL.
* This is correct according to RFC1035, but the
* TTLs may be silently misinterpreted by older
* versions of BIND which use the SOA MINTTL as a
* default TTL value.
*/
#define DNS_STYLEFLAG_OMIT_TTL 0x00020000U
/* Omit the class when possible. */
#define DNS_STYLEFLAG_OMIT_CLASS 0x00040000U
/* Output $TTL directives. */
#define DNS_STYLEFLAG_TTL 0x00080000U
/*
* Output $ORIGIN directives and print owner names relative to
* the origin when possible.
*/
#define DNS_STYLEFLAG_REL_OWNER 0x00100000U
/* Print domain names in RR data in relative form when possible.
For this to take effect, DNS_STYLEFLAG_REL_OWNER must also be set. */
#define DNS_STYLEFLAG_REL_DATA 0x00200000U
/* Print the trust level of each rdataset. */
#define DNS_STYLEFLAG_TRUST 0x00400000U
/* Print negative caching entries. */
#define DNS_STYLEFLAG_NCACHE 0x00800000U
/*
* The maximum length of the newline+indentation that is output
* when inserting a line break in an RR. This effectively puts an
* upper limits on the value of "rdata_column", because if it is
* very large, the tabs and spaces needed to reach it will not fit.
*/
#define DNS_TOTEXT_LINEBREAK_MAXLEN 100
/*
* Context structure for a masterfile dump in progress.
*/
typedef struct dns_totext_ctx {
char * linebreak;
dns_name_t * origin;
24, 24, 24, 32, 80, 8
};
24, 32, 32, 40, 80, 8
};
const dns_master_style_t
24, 32, 32, 40, 80, 8
};
const dns_master_style_t
0,
24, 32, 32, 40, 80, 8
};
/*
* A style suitable for dns_rdataset_totext().
*/
24, 32, 40, 48, 80, 8
};
#define N_SPACES 10
#define N_TABS 10
struct dns_dumpctx {
unsigned int magic;
unsigned int references;
FILE *f;
void *done_arg;
unsigned int nodes;
/* dns_master_dumpinc() */
char *file;
char *tmpfile;
};
/*
* Output tabs and spaces to go from column '*current' to
* column 'to', and update '*current' to reflect the new
* current column.
*/
static isc_result_t
{
isc_region_t r;
unsigned char *p;
unsigned int from;
if (ntabs < 0)
ntabs = 0;
if (ntabs > 0) {
return (ISC_R_NOSPACE);
p = r.base;
t = ntabs;
while (t) {
int n = t;
if (n > N_TABS)
n = N_TABS;
p += n;
t -= n;
}
}
return (ISC_R_NOSPACE);
p = r.base;
t = nspaces;
while (t) {
int n = t;
if (n > N_SPACES)
n = N_SPACES;
p += n;
t -= n;
}
return (ISC_R_SUCCESS);
}
static isc_result_t
/*
* Set up the line break string if needed.
*/
isc_region_t r;
unsigned int col = 0;
sizeof(ctx->linebreak_buf));
isc_buffer_availableregion(&buf, &r);
if (r.length < 1)
return (DNS_R_TEXTTOOLONG);
r.base[0] = '\n';
/*
* Do not return ISC_R_NOSPACE if the line break string
* buffer is too small, because that would just make
* dump_rdataset() retry indenfinitely with ever
* bigger target buffers. That's a different buffer,
* so it won't help. Use DNS_R_TEXTTOOLONG as a substitute.
*/
if (result == ISC_R_NOSPACE)
return (DNS_R_TEXTTOOLONG);
if (result != ISC_R_SUCCESS)
return (result);
isc_buffer_availableregion(&buf, &r);
if (r.length < 1)
return (DNS_R_TEXTTOOLONG);
r.base[0] = '\0';
} else {
}
ctx->current_ttl = 0;
return (ISC_R_SUCCESS);
}
do { \
!= ISC_R_SUCCESS) \
return (result); \
} while (0)
static isc_result_t
unsigned int l;
return (ISC_R_NOSPACE);
isc_buffer_add(target, l);
return (ISC_R_SUCCESS);
}
/*
* Convert 'rdataset' to master file text format according to 'ctx',
* storing the result in 'target'. If 'owner_name' is NULL, it
* is omitted; otherwise 'owner_name' must be valid and have at least
* one label.
*/
static isc_result_t
{
unsigned int column;
do {
column = 0;
/*
* Owner name.
*/
if (owner_name != NULL &&
!first))
{
target));
}
/*
* TTL.
*/
{
char ttlbuf[64];
isc_region_t r;
unsigned int length;
return (ISC_R_NOSPACE);
/*
* If the $TTL directive is not in use, the TTL we
* just printed becomes the default for subsequent RRs.
*/
}
}
/*
* Class.
*/
{
unsigned int class_start;
target);
if (result != ISC_R_SUCCESS)
return (result);
}
/*
* Type.
*/
} else {
}
{
unsigned int type_start;
if (result != ISC_R_SUCCESS)
return (result);
}
/*
* Rdata.
*/
} else {
isc_region_t r;
target));
if (r.length < 1)
return (ISC_R_NOSPACE);
r.base[0] = '\n';
}
} while (result == ISC_R_SUCCESS);
if (result != ISC_R_NOMORE)
return (result);
/*
* Update the ctx state to reflect what we just printed.
* This is done last, only when we are sure we will return
* success, because this function may be called multiple
* times with increasing buffer sizes until it succeeds,
* and failed attempts must not update the state prematurely.
*/
return (ISC_R_SUCCESS);
}
/*
* Print the name, type, and class of an empty rdataset,
* such as those used to represent the question section
* of a DNS message.
*/
static isc_result_t
{
unsigned int column;
isc_region_t r;
column = 0;
/* Owner name */
{
target));
}
/* Class */
{
unsigned int class_start;
if (result != ISC_R_SUCCESS)
return (result);
}
/* Type */
{
unsigned int type_start;
if (result != ISC_R_SUCCESS)
return (result);
}
if (r.length < 1)
return (ISC_R_NOSPACE);
r.base[0] = '\n';
return (ISC_R_SUCCESS);
}
{
if (result != ISC_R_SUCCESS) {
"could not set master file style");
return (ISC_R_UNEXPECTED);
}
/*
* The caller might want to give us an empty owner
* name (e.g. if they are outputting into a master
* file and this rdataset has the same name as the
* previous one.)
*/
if (dns_name_countlabels(owner_name) == 0)
owner_name = NULL;
if (question)
omit_final_dot, target));
else
omit_final_dot, target));
}
const dns_master_style_t *style,
{
if (result != ISC_R_SUCCESS) {
"could not set master file style");
return (ISC_R_UNEXPECTED);
}
}
const dns_master_style_t *style,
{
if (result != ISC_R_SUCCESS) {
"could not set master file style");
return (ISC_R_UNEXPECTED);
}
}
/*
* Print an rdataset. 'buffer' is a scratch buffer, which must have been
* dynamically allocated by the caller. It must be large enough to
* hold the result from dns_ttl_totext(). If more than that is needed,
* the buffer will be grown automatically.
*/
static isc_result_t
{
isc_region_t r;
/*
* Output a $TTL directive if needed.
*/
{
{
isc_buffer_usedregion(buffer, &r);
} else {
}
}
}
/*
* Generate the text representation of the rdataset into
* the buffer. If the buffer is too small, grow it.
*/
for (;;) {
int newlength;
void *newmem;
if (result != ISC_R_NOSPACE)
break;
return (ISC_R_NOMEMORY);
}
if (result != ISC_R_SUCCESS)
return (result);
/*
* Write the buffer contents to the master file.
*/
isc_buffer_usedregion(buffer, &r);
if (result != ISC_R_SUCCESS) {
"master file write failed: %s",
return (result);
}
return (ISC_R_SUCCESS);
}
/*
* Define the order in which rdatasets should be printed in zone
* files. We will print SOA and NS records before others, SIGs
* immediately following the things they sign, and order everything
* else by RR number. This is all just for aesthetics and
* compatibility with buggy software that expects the SOA to be first;
* the DNS specifications allow any order.
*/
static int
int t;
int sig;
sig = 1;
} else {
sig = 0;
}
switch (t) {
case dns_rdatatype_soa:
t = 0;
break;
case dns_rdatatype_ns:
t = 1;
break;
default:
t += 2;
break;
}
return (t << 1) + sig;
}
static int
dump_order_compare(const void *a, const void *b) {
return (dump_order(*((const dns_rdataset_t * const *) a)) -
dump_order(*((const dns_rdataset_t * const *) b)));
}
/*
* Dump all the rdatasets of a domain name to a master file. We make
* a "best effort" attempt to sort the RRsets in a nice order, but if
* there are more than MAXSORT RRsets, we punt and only sort them in
* groups of MAXSORT. This is not expected to ever happen in practice
* since much less than 64 RR types have been registered with the
* IANA, so far, and the output will be correct (though not
* aesthetically pleasing) even if it does happen.
*/
#define MAXSORT 64
const char *trustnames[] = {
"none",
"pending",
"additional",
"glue",
"answer",
"authauthority",
"authanswer",
"secure",
"local" /* aka ultimate */
};
static isc_result_t
{
int i, n;
for (i = 0;
dns_rdataset_init(&rdatasets[i]);
}
n = i;
for (i = 0; i < n; i++) {
sizeof(trustnames[0])));
}
/* Omit negative cache entries */
} else {
buffer, f);
if (result != ISC_R_SUCCESS)
dumpresult = result;
}
}
if (dumpresult != ISC_R_SUCCESS)
return (dumpresult);
/*
* If we got more data than could be sorted at once,
* go handle the rest.
*/
if (itresult == ISC_R_SUCCESS)
goto again;
if (itresult == ISC_R_NOMORE)
return (itresult);
}
/*
* Initial size of text conversion buffer. The buffer is used
* for several purposes: converting origin names, rdatasets,
* $DATE timestamps, and comment strings for $TTL directives.
*
* When converting rdatasets, it is dynamically resized, but
* when converting origins, timestamps, etc it is not. Therefore,
* the initial size must large enough to hold the longest possible
* text representation of any domain name (for $ORIGIN).
*/
static const int initial_buffer_length = 1200;
static isc_result_t
static void
}
void
source->references++;
}
void
dctx->references--;
if (dctx->references == 0)
if (need_destroy)
}
void
}
static isc_result_t
{
if (result == ISC_R_SUCCESS)
result = isc_stdio_sync(f);
"dumping master file: %s: fsync: %s",
}
tresult = isc_stdio_close(f);
if (result == ISC_R_SUCCESS)
"dumping master file: %s: fclose: %s",
}
if (result == ISC_R_SUCCESS)
else
(void)isc_file_remove(temp);
"dumping master file: rename: %s: %s",
}
return (result);
}
static void
else
if (result == DNS_R_CONTINUE) {
return;
}
}
static isc_result_t
return (ISC_R_NOMEMORY);
return (ISC_R_SUCCESS);
}
static isc_result_t
{
return (ISC_R_NOMEMORY);
dctx->f = f;
if (result != ISC_R_SUCCESS) {
"could not set master file style");
goto cleanup;
}
if (result != ISC_R_SUCCESS)
goto cleanup;
if (result != ISC_R_SUCCESS)
goto cleanup;
else if (!dns_db_iscache(db))
return (ISC_R_SUCCESS);
return (result);
}
static isc_result_t
char *bufmem;
isc_region_t r;
unsigned int nodes;
return (ISC_R_NOMEMORY);
/*
* If the database has cache semantics, output an RFC2540
* $DATE directive so that the TTLs can be adjusted when
* it is reloaded. For zones it is not really needed, and
* it would make the file incompatible with pre-RFC2540
* software, so we omit it in the zone case.
*/
isc_buffer_usedregion(&buffer, &r);
}
} else
break;
if (result == DNS_R_NEWORIGIN) {
dns_name_t *origin =
isc_buffer_usedregion(&buffer, &r);
(char *) r.base);
}
if (result != ISC_R_SUCCESS) {
goto fail;
}
if (result != ISC_R_SUCCESS) {
goto fail;
}
}
} else if (result == ISC_R_NOMORE)
fail:
return (result);
}
const dns_master_style_t *style,
{
if (result != ISC_R_SUCCESS)
return (result);
if (result == ISC_R_SUCCESS) {
return (DNS_R_CONTINUE);
}
return (result);
}
/*
* Dump an entire database into a master file.
*/
const dns_master_style_t *style,
FILE *f)
{
if (result != ISC_R_SUCCESS)
return (result);
return (result);
}
static isc_result_t
int tempnamelen;
return (ISC_R_NOMEMORY);
if (result != ISC_R_SUCCESS)
goto cleanup;
if (result != ISC_R_SUCCESS) {
"dumping master file: %s: open: %s",
goto cleanup;
}
*fp = f;
return (ISC_R_SUCCESS);
return (result);
}
{
return (ISC_R_NOMEMORY);
if (result != ISC_R_SUCCESS)
goto cleanup;
if (result != ISC_R_SUCCESS) {
(void)isc_stdio_close(f);
(void)isc_file_remove(tempname);
goto cleanup;
}
if (result == ISC_R_SUCCESS) {
return (DNS_R_CONTINUE);
}
return (result);
}
{
char *tempname;
if (result != ISC_R_SUCCESS)
return (result);
if (result != ISC_R_SUCCESS)
goto cleanup;
return (result);
}
/*
* Dump a database node into a master file.
*/
const dns_master_style_t *style,
FILE *f)
{
char *bufmem;
if (result != ISC_R_SUCCESS) {
"could not set master file style");
return (ISC_R_UNEXPECTED);
}
return (ISC_R_NOMEMORY);
if (result != ISC_R_SUCCESS)
goto failure;
if (result != ISC_R_SUCCESS)
goto failure;
return (result);
}
{
if (result != ISC_R_SUCCESS) {
"dumping node to file: %s: open: %s", filename,
return (ISC_R_UNEXPECTED);
}
style, f);
result = isc_stdio_close(f);
if (result != ISC_R_SUCCESS) {
"dumping master file: %s: close: %s", filename,
return (ISC_R_UNEXPECTED);
}
return (result);
}