masterdump.c revision c03bb27f0675a6e60ceea66b451548e8481bc05c
/*
* Copyright (C) 1999, 2000 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.35 2000/10/25 04:26:37 marka Exp $ */
#include <config.h>
#include <stdlib.h>
#include <dns/dbiterator.h>
#include <dns/fixedname.h>
#include <dns/masterdump.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
/*
* 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;
/*
* The default master file style.
*
* Because the TTL is always omitted, and the class is almost always
* omitted, neither is allocated any columns.
*/
const dns_master_style_t
24, 24, 24, 32, 80, 8
};
/*
* A master file style that prints TTL values on each record line,
* never using $TTL statements. The TTL has a tab stop of its
* own, but the class and type share one.
*/
const dns_master_style_t
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
/*
* 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)
/*
* 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.
*/
{
unsigned int type_start;
if (result != ISC_R_SUCCESS)
return (result);
}
/*
* Rdata.
*/
{
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);
}
/*
* Provide a backwards compatible interface for printing a
* single rdataset or question section. This is now used
* only by wire_test.c.
*/
{
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 (no_rdata_or_ttl)
omit_final_dot, target));
else
omit_final_dot, target));
}
/*
* 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 **) a)) -
dump_order(*((const dns_rdataset_t **) 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
static isc_result_t
{
int i, n;
for (i = 0;
dns_rdataset_init(&rdatasets[i]);
}
n = i;
for (i = 0; i < n; i++) {
/*
* XXX We only dump the rdataset if it isn't a
* negative caching entry. Maybe our dumping routines
* will learn how to usefully dump such an entry later
* on.
*/
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;
/*
* Dump an entire database into a master file.
*/
const dns_master_style_t *style,
FILE *f)
{
char *bufmem;
isc_region_t r;
if (result != ISC_R_SUCCESS) {
"could not set master file style");
return (ISC_R_UNEXPECTED);
}
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.
*/
if (dns_db_iscache(db)) {
isc_buffer_usedregion(&buffer, &r);
}
&dbiter);
if (result != ISC_R_SUCCESS)
goto create_iter_failure;
while (result == ISC_R_SUCCESS) {
break;
if (result == DNS_R_NEWORIGIN) {
dns_name_t *origin =
isc_buffer_usedregion(&buffer, &r);
(char *) r.base);
}
if (result != ISC_R_SUCCESS) {
goto iter_failure;
}
&buffer, f);
if (result != ISC_R_SUCCESS) {
goto iter_failure;
}
}
if (result != ISC_R_NOMORE)
goto iter_failure;
return (result);
}
{
char *tempname;
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;
}
if (result != ISC_R_SUCCESS) {
"dumping master file: %s: %s",
(void)isc_stdio_close(f);
(void)isc_file_remove(tempname);
goto cleanup;
}
result = isc_stdio_close(f);
if (result != ISC_R_SUCCESS) {
"dumping master file: %s: close: %s",
(void)isc_file_remove(tempname);
goto cleanup;
}
if (result != ISC_R_SUCCESS) {
"dumping master file: %s: close: %s",
goto cleanup;
}
if (result != ISC_R_SUCCESS) {
"dumping master file: rename: %s: %s",
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);
}