masterdump.c revision 7ab0e69f61e61e81d489c95c7ebd981e74e7ef16
/*
* Copyright (C) 1999 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.
*/
#include <config.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <errno.h>
#include <isc/assertions.h>
#include <dns/fixedname.h>
#include <dns/rdataclass.h>
#include <dns/rdatatype.h>
#include <dns/rdataset.h>
#include <dns/rdatasetiter.h>
#include <dns/dbiterator.h>
#include <dns/masterdump.h>
/* XXX */
#define RETERR(x) do { \
dns_result_t __r = (x); \
if (__r != DNS_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 {
/*
* The default master file style.
*
* Because the TTL is always omitted, and the class is almost always
* omitted, neither is allocated any columns.
*/
24, 24, 24, 32, 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 dns_result_t
isc_region_t r;
unsigned char *p;
if (ntabs < 0)
ntabs = 0;
if (ntabs > 0) {
isc_buffer_available(target, &r);
return (DNS_R_NOSPACE);
p = r.base;
t = ntabs;
while (t) {
int n = t;
if (n > N_TABS)
n = N_TABS;
p += n;
t -= n;
}
}
isc_buffer_available(target, &r);
return (DNS_R_NOSPACE);
p = r.base;
t = nspaces;
while (t) {
int n = t;
if (n > N_SPACES)
n = N_SPACES;
p += n;
t -= n;
}
return (DNS_R_SUCCESS);
}
static dns_result_t
{
/* Set up the line break string. */
isc_region_t r;
int col = 0;
isc_buffer_available(&buf, &r);
if (r.length < 1)
return (DNS_R_TEXTTOLONG);
r.base[0] = '\n';
/*
* Do not return DNS_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_TEXTTOLONG as a substitute.
*/
if (result == DNS_R_NOSPACE)
return (DNS_R_TEXTTOLONG);
if (result != DNS_R_SUCCESS)
return (result);
isc_buffer_available(&buf, &r);
if (r.length < 1)
return (DNS_R_TEXTTOLONG);
r.base[0] = '\0';
} else {
/*
* Single-line output. Use a single space where
* a line break would otherwise be used.
*/
}
return (DNS_R_SUCCESS);
}
do { \
!= DNS_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 dns_result_t
{
unsigned int column;
do {
column = 0;
/* Owner name. */
if (owner_name != NULL &&
{
target));
}
/* TTL. */
{
char ttlbuf[64];
isc_region_t r;
unsigned int length;
isc_buffer_available(target, &r);
return (DNS_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;
if (result != DNS_R_SUCCESS)
return (result);
}
/* Type. */
{
unsigned int type_start;
if (result != DNS_R_SUCCESS)
return (result);
}
/* Rdata. */
{
isc_region_t r;
isc_buffer_available(target, &r);
if (r.length < 1)
return (DNS_R_NOSPACE);
r.base[0] = '\n';
}
} while (result == DNS_R_SUCCESS);
if (result != DNS_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 (DNS_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 dns_result_t
{
unsigned int column;
column = 0;
/* Owner name */
{
target));
}
/* Class */
{
unsigned int class_start;
if (result != DNS_R_SUCCESS)
return (result);
}
/* Type */
{
unsigned int type_start;
if (result != DNS_R_SUCCESS)
return (result);
}
return (DNS_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 != DNS_R_SUCCESS) {
"could not set master file style");
return (DNS_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 dns_result_t
{
isc_region_t r;
/* Output a $TTL directive if needed. */
{
{
isc_buffer_used(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 != DNS_R_NOSPACE)
break;
return (DNS_R_NOMEMORY);
}
if (result != DNS_R_SUCCESS)
return (result);
/* Write the buffer contents to the master file. */
isc_buffer_used(buffer, &r);
"master file write failed: %s\n",
return (DNS_R_UNEXPECTED);
}
return (DNS_R_SUCCESS);
}
/*
* Dump all the rdatasets of a domain name to a master file.
*/
static dns_result_t
{
while (result == DNS_R_SUCCESS) {
if (result != DNS_R_SUCCESS)
return (result);
}
if (result != DNS_R_NOMORE)
return (result);
return (DNS_R_SUCCESS);
}
/*
* 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).
*/
const int initial_buffer_length = 1200;
/*
* Dump an entire database into a master file.
*/
static dns_result_t
const dns_master_style_t *style,
FILE *f)
{
char *bufmem;
isc_region_t r;
if (result != DNS_R_SUCCESS) {
"could not set master file style");
return (DNS_R_UNEXPECTED);
}
return (DNS_R_UNEXPECTED);
return (DNS_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_used(&buffer, &r);
}
&dbiter);
if (result != DNS_R_SUCCESS)
goto create_iter_failure;
while (result == DNS_R_SUCCESS) {
break;
if (result == DNS_R_NEWORIGIN) {
isc_buffer_used(&buffer, &r);
(char *) r.base);
}
if (result != DNS_R_SUCCESS) {
goto iter_failure;
}
&buffer, f);
if (result != DNS_R_SUCCESS) {
goto iter_failure;
}
}
if (result != DNS_R_NOMORE)
goto iter_failure;
return (result);
}
{
FILE *f;
if (f == NULL) {
"could not open %s",
filename);
return (DNS_R_UNEXPECTED);
}
if (fclose(f) != 0) {
"error closing %s",
filename);
return (DNS_R_UNEXPECTED);
}
return (result);
}