Home | History | Annotate | Line # | Download | only in dns
masterdump.c revision 1.2
      1 /*	$NetBSD: masterdump.c,v 1.2 2018/08/12 13:02:35 christos Exp $	*/
      2 
      3 /*
      4  * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
      5  *
      6  * This Source Code Form is subject to the terms of the Mozilla Public
      7  * License, v. 2.0. If a copy of the MPL was not distributed with this
      8  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
      9  *
     10  * See the COPYRIGHT file distributed with this work for additional
     11  * information regarding copyright ownership.
     12  */
     13 
     14 /*! \file */
     15 
     16 #include <config.h>
     17 
     18 #include <stdlib.h>
     19 
     20 #include <isc/buffer.h>
     21 #include <isc/event.h>
     22 #include <isc/file.h>
     23 #include <isc/magic.h>
     24 #include <isc/mem.h>
     25 #include <isc/print.h>
     26 #include <isc/stdio.h>
     27 #include <isc/string.h>
     28 #include <isc/task.h>
     29 #include <isc/time.h>
     30 #include <isc/types.h>
     31 #include <isc/util.h>
     32 
     33 #include <dns/db.h>
     34 #include <dns/dbiterator.h>
     35 #include <dns/events.h>
     36 #include <dns/fixedname.h>
     37 #include <dns/lib.h>
     38 #include <dns/log.h>
     39 #include <dns/master.h>
     40 #include <dns/masterdump.h>
     41 #include <dns/ncache.h>
     42 #include <dns/rdata.h>
     43 #include <dns/rdataclass.h>
     44 #include <dns/rdataset.h>
     45 #include <dns/rdatasetiter.h>
     46 #include <dns/rdatatype.h>
     47 #include <dns/result.h>
     48 #include <dns/time.h>
     49 #include <dns/ttl.h>
     50 
     51 #define DNS_DCTX_MAGIC		ISC_MAGIC('D', 'c', 't', 'x')
     52 #define DNS_DCTX_VALID(d)	ISC_MAGIC_VALID(d, DNS_DCTX_MAGIC)
     53 
     54 #define RETERR(x) do { \
     55 	isc_result_t _r = (x); \
     56 	if (_r != ISC_R_SUCCESS) \
     57 		return (_r); \
     58 	} while (/*CONSTCOND*/0)
     59 
     60 #define CHECK(x) do { \
     61 	if ((x) != ISC_R_SUCCESS) \
     62 		goto cleanup; \
     63 	} while (/*CONSTCOND*/0)
     64 
     65 struct dns_master_style {
     66 	dns_masterstyle_flags_t flags;		/* DNS_STYLEFLAG_* */
     67 	unsigned int ttl_column;
     68 	unsigned int class_column;
     69 	unsigned int type_column;
     70 	unsigned int rdata_column;
     71 	unsigned int line_length;
     72 	unsigned int tab_width;
     73 	unsigned int split_width;
     74 };
     75 
     76 /*%
     77  * The maximum length of the newline+indentation that is output
     78  * when inserting a line break in an RR.  This effectively puts an
     79  * upper limits on the value of "rdata_column", because if it is
     80  * very large, the tabs and spaces needed to reach it will not fit.
     81  */
     82 #define DNS_TOTEXT_LINEBREAK_MAXLEN 100
     83 
     84 /*%
     85  * Context structure for a masterfile dump in progress.
     86  */
     87 typedef struct dns_totext_ctx {
     88 	dns_master_style_t	style;
     89 	isc_boolean_t 		class_printed;
     90 	char *			linebreak;
     91 	char 			linebreak_buf[DNS_TOTEXT_LINEBREAK_MAXLEN];
     92 	dns_name_t *		origin;
     93 	dns_name_t *		neworigin;
     94 	dns_fixedname_t		origin_fixname;
     95 	isc_uint32_t 		current_ttl;
     96 	isc_boolean_t 		current_ttl_valid;
     97 	dns_ttl_t		serve_stale_ttl;
     98 } dns_totext_ctx_t;
     99 
    100 LIBDNS_EXTERNAL_DATA const dns_master_style_t
    101 dns_master_style_keyzone = {
    102 	DNS_STYLEFLAG_OMIT_OWNER |
    103 	DNS_STYLEFLAG_OMIT_CLASS |
    104 	DNS_STYLEFLAG_REL_OWNER |
    105 	DNS_STYLEFLAG_REL_DATA |
    106 	DNS_STYLEFLAG_OMIT_TTL |
    107 	DNS_STYLEFLAG_TTL |
    108 	DNS_STYLEFLAG_COMMENT |
    109 	DNS_STYLEFLAG_RRCOMMENT |
    110 	DNS_STYLEFLAG_MULTILINE |
    111 	DNS_STYLEFLAG_KEYDATA,
    112 	24, 24, 24, 32, 80, 8, UINT_MAX
    113 };
    114 
    115 LIBDNS_EXTERNAL_DATA const dns_master_style_t
    116 dns_master_style_default = {
    117 	DNS_STYLEFLAG_OMIT_OWNER |
    118 	DNS_STYLEFLAG_OMIT_CLASS |
    119 	DNS_STYLEFLAG_REL_OWNER |
    120 	DNS_STYLEFLAG_REL_DATA |
    121 	DNS_STYLEFLAG_OMIT_TTL |
    122 	DNS_STYLEFLAG_TTL |
    123 	DNS_STYLEFLAG_COMMENT |
    124 	DNS_STYLEFLAG_RRCOMMENT |
    125 	DNS_STYLEFLAG_MULTILINE,
    126 	24, 24, 24, 32, 80, 8, UINT_MAX
    127 };
    128 
    129 LIBDNS_EXTERNAL_DATA const dns_master_style_t
    130 dns_master_style_full = {
    131 	DNS_STYLEFLAG_COMMENT |
    132 	DNS_STYLEFLAG_RESIGN,
    133 	46, 46, 46, 64, 120, 8, UINT_MAX
    134 };
    135 
    136 LIBDNS_EXTERNAL_DATA const dns_master_style_t
    137 dns_master_style_explicitttl = {
    138 	DNS_STYLEFLAG_OMIT_OWNER |
    139 	DNS_STYLEFLAG_OMIT_CLASS |
    140 	DNS_STYLEFLAG_REL_OWNER |
    141 	DNS_STYLEFLAG_REL_DATA |
    142 	DNS_STYLEFLAG_COMMENT |
    143 	DNS_STYLEFLAG_RRCOMMENT |
    144 	DNS_STYLEFLAG_MULTILINE,
    145 	24, 32, 32, 40, 80, 8, UINT_MAX
    146 };
    147 
    148 LIBDNS_EXTERNAL_DATA const dns_master_style_t
    149 dns_master_style_cache = {
    150 	DNS_STYLEFLAG_OMIT_OWNER |
    151 	DNS_STYLEFLAG_OMIT_CLASS |
    152 	DNS_STYLEFLAG_MULTILINE |
    153 	DNS_STYLEFLAG_RRCOMMENT |
    154 	DNS_STYLEFLAG_TRUST |
    155 	DNS_STYLEFLAG_NCACHE,
    156 	24, 32, 32, 40, 80, 8, UINT_MAX
    157 };
    158 
    159 LIBDNS_EXTERNAL_DATA const dns_master_style_t
    160 dns_master_style_simple = {
    161 	0,
    162 	24, 32, 32, 40, 80, 8, UINT_MAX
    163 };
    164 
    165 /*%
    166  * A style suitable for dns_rdataset_totext().
    167  */
    168 LIBDNS_EXTERNAL_DATA const dns_master_style_t
    169 dns_master_style_debug = {
    170 	DNS_STYLEFLAG_REL_OWNER,
    171 	24, 32, 40, 48, 80, 8, UINT_MAX
    172 };
    173 
    174 /*%
    175  * Similar, but indented (i.e., prepended with dns_master_indentstr).
    176  */
    177 LIBDNS_EXTERNAL_DATA const dns_master_style_t
    178 dns_master_style_indent = {
    179 	DNS_STYLEFLAG_REL_OWNER |
    180 	DNS_STYLEFLAG_INDENT,
    181 	24, 32, 40, 48, 80, 8, UINT_MAX
    182 };
    183 
    184 /*%
    185  * Similar, but with each line commented out.
    186  */
    187 LIBDNS_EXTERNAL_DATA const dns_master_style_t
    188 dns_master_style_comment = {
    189 	DNS_STYLEFLAG_REL_OWNER |
    190 	DNS_STYLEFLAG_MULTILINE |
    191 	DNS_STYLEFLAG_RRCOMMENT |
    192 	DNS_STYLEFLAG_COMMENTDATA,
    193 	24, 32, 40, 48, 80, 8, UINT_MAX
    194 };
    195 
    196 /*%
    197  * YAML style
    198  */
    199 LIBDNS_EXTERNAL_DATA const dns_master_style_t
    200 dns_master_style_yaml = {
    201 	DNS_STYLEFLAG_YAML |
    202 	DNS_STYLEFLAG_REL_OWNER |
    203 	DNS_STYLEFLAG_INDENT,
    204 	24, 32, 40, 48, 80, 8, UINT_MAX
    205 };
    206 
    207 /*%
    208  * Default indent string.
    209  */
    210 LIBDNS_EXTERNAL_DATA const char *dns_master_indentstr = "\t";
    211 LIBDNS_EXTERNAL_DATA unsigned int dns_master_indent = 1;
    212 
    213 #define N_SPACES 10
    214 static char spaces[N_SPACES+1] = "          ";
    215 
    216 #define N_TABS 10
    217 static char tabs[N_TABS+1] = "\t\t\t\t\t\t\t\t\t\t";
    218 
    219 struct dns_dumpctx {
    220 	unsigned int		magic;
    221 	isc_mem_t		*mctx;
    222 	isc_mutex_t		lock;
    223 	unsigned int		references;
    224 	isc_boolean_t		canceled;
    225 	isc_boolean_t		first;
    226 	isc_boolean_t		do_date;
    227 	isc_stdtime_t		now;
    228 	FILE			*f;
    229 	dns_db_t		*db;
    230 	dns_dbversion_t		*version;
    231 	dns_dbiterator_t	*dbiter;
    232 	dns_totext_ctx_t	tctx;
    233 	isc_task_t		*task;
    234 	dns_dumpdonefunc_t	done;
    235 	void			*done_arg;
    236 	unsigned int		nodes;
    237 	/* dns_master_dumpinc() */
    238 	char			*file;
    239 	char 			*tmpfile;
    240 	dns_masterformat_t	format;
    241 	dns_masterrawheader_t	header;
    242 	isc_result_t		(*dumpsets)(isc_mem_t *mctx,
    243 					    const dns_name_t *name,
    244 					    dns_rdatasetiter_t *rdsiter,
    245 					    dns_totext_ctx_t *ctx,
    246 					    isc_buffer_t *buffer, FILE *f);
    247 };
    248 
    249 #define NXDOMAIN(x) (((x)->attributes & DNS_RDATASETATTR_NXDOMAIN) != 0)
    250 
    251 /*%
    252  * Output tabs and spaces to go from column '*current' to
    253  * column 'to', and update '*current' to reflect the new
    254  * current column.
    255  */
    256 static isc_result_t
    257 indent(unsigned int *current, unsigned int to, int tabwidth,
    258        isc_buffer_t *target)
    259 {
    260 	isc_region_t r;
    261 	unsigned char *p;
    262 	unsigned int from;
    263 	int ntabs, nspaces, t;
    264 
    265 	from = *current;
    266 
    267 	if (to < from + 1)
    268 		to = from + 1;
    269 
    270 	ntabs = to / tabwidth - from / tabwidth;
    271 	if (ntabs < 0)
    272 		ntabs = 0;
    273 
    274 	if (ntabs > 0) {
    275 		isc_buffer_availableregion(target, &r);
    276 		if (r.length < (unsigned) ntabs)
    277 			return (ISC_R_NOSPACE);
    278 		p = r.base;
    279 
    280 		t = ntabs;
    281 		while (t) {
    282 			int n = t;
    283 			if (n > N_TABS)
    284 				n = N_TABS;
    285 			memmove(p, tabs, n);
    286 			p += n;
    287 			t -= n;
    288 		}
    289 		isc_buffer_add(target, ntabs);
    290 		from = (to / tabwidth) * tabwidth;
    291 	}
    292 
    293 	nspaces = to - from;
    294 	INSIST(nspaces >= 0);
    295 
    296 	isc_buffer_availableregion(target, &r);
    297 	if (r.length < (unsigned) nspaces)
    298 		return (ISC_R_NOSPACE);
    299 	p = r.base;
    300 
    301 	t = nspaces;
    302 	while (t) {
    303 		int n = t;
    304 		if (n > N_SPACES)
    305 			n = N_SPACES;
    306 		memmove(p, spaces, n);
    307 		p += n;
    308 		t -= n;
    309 	}
    310 	isc_buffer_add(target, nspaces);
    311 
    312 	*current = to;
    313 	return (ISC_R_SUCCESS);
    314 }
    315 
    316 static isc_result_t
    317 totext_ctx_init(const dns_master_style_t *style, dns_totext_ctx_t *ctx) {
    318 	isc_result_t result;
    319 
    320 	REQUIRE(style->tab_width != 0);
    321 
    322 	ctx->style = *style;
    323 	ctx->class_printed = ISC_FALSE;
    324 
    325 	dns_fixedname_init(&ctx->origin_fixname);
    326 
    327 	/*
    328 	 * Set up the line break string if needed.
    329 	 */
    330 	if ((ctx->style.flags & DNS_STYLEFLAG_MULTILINE) != 0) {
    331 		isc_buffer_t buf;
    332 		isc_region_t r;
    333 		unsigned int col = 0;
    334 
    335 		isc_buffer_init(&buf, ctx->linebreak_buf,
    336 				sizeof(ctx->linebreak_buf));
    337 
    338 		isc_buffer_availableregion(&buf, &r);
    339 		if (r.length < 1)
    340 			return (DNS_R_TEXTTOOLONG);
    341 		r.base[0] = '\n';
    342 		isc_buffer_add(&buf, 1);
    343 
    344 		if ((ctx->style.flags & DNS_STYLEFLAG_INDENT) != 0 ||
    345 		    (ctx->style.flags & DNS_STYLEFLAG_YAML) != 0)
    346 		{
    347 			unsigned int i, len = strlen(dns_master_indentstr);
    348 			for (i = 0; i < dns_master_indent; i++) {
    349 				if (isc_buffer_availablelength(&buf) < len)
    350 					return (DNS_R_TEXTTOOLONG);
    351 				isc_buffer_putstr(&buf, dns_master_indentstr);
    352 			}
    353 		}
    354 
    355 		if ((ctx->style.flags & DNS_STYLEFLAG_COMMENTDATA) != 0) {
    356 			isc_buffer_availableregion(&buf, &r);
    357 			if (r.length < 1)
    358 				return (DNS_R_TEXTTOOLONG);
    359 			r.base[0] = ';';
    360 			isc_buffer_add(&buf, 1);
    361 		}
    362 
    363 		result = indent(&col, ctx->style.rdata_column,
    364 				ctx->style.tab_width, &buf);
    365 		/*
    366 		 * Do not return ISC_R_NOSPACE if the line break string
    367 		 * buffer is too small, because that would just make
    368 		 * dump_rdataset() retry indefinitely with ever
    369 		 * bigger target buffers.  That's a different buffer,
    370 		 * so it won't help.  Use DNS_R_TEXTTOOLONG as a substitute.
    371 		 */
    372 		if (result == ISC_R_NOSPACE)
    373 			return (DNS_R_TEXTTOOLONG);
    374 		if (result != ISC_R_SUCCESS)
    375 			return (result);
    376 
    377 		isc_buffer_availableregion(&buf, &r);
    378 		if (r.length < 1)
    379 			return (DNS_R_TEXTTOOLONG);
    380 		r.base[0] = '\0';
    381 		isc_buffer_add(&buf, 1);
    382 		ctx->linebreak = ctx->linebreak_buf;
    383 	} else {
    384 		ctx->linebreak = NULL;
    385 	}
    386 
    387 	ctx->origin = NULL;
    388 	ctx->neworigin = NULL;
    389 	ctx->current_ttl = 0;
    390 	ctx->current_ttl_valid = ISC_FALSE;
    391 	ctx->serve_stale_ttl = 0;
    392 
    393 	return (ISC_R_SUCCESS);
    394 }
    395 
    396 #define INDENT_TO(col) \
    397 	do { \
    398 		 if ((ctx->style.flags & DNS_STYLEFLAG_YAML) != 0) { \
    399 			if ((result = str_totext(" ", target)) \
    400 			    != ISC_R_SUCCESS) \
    401 				return (result); \
    402 		 } else if ((result = indent(&column, ctx->style.col, \
    403 				      ctx->style.tab_width, target)) \
    404 		     != ISC_R_SUCCESS) \
    405 			    return (result); \
    406 	} while (/*CONSTCOND*/0)
    407 
    408 
    409 static isc_result_t
    410 str_totext(const char *source, isc_buffer_t *target) {
    411 	unsigned int l;
    412 	isc_region_t region;
    413 
    414 	isc_buffer_availableregion(target, &region);
    415 	l = strlen(source);
    416 
    417 	if (l > region.length)
    418 		return (ISC_R_NOSPACE);
    419 
    420 	memmove(region.base, source, l);
    421 	isc_buffer_add(target, l);
    422 	return (ISC_R_SUCCESS);
    423 }
    424 
    425 static isc_result_t
    426 ncache_summary(dns_rdataset_t *rdataset, isc_boolean_t omit_final_dot,
    427 	       isc_buffer_t *target)
    428 {
    429 	isc_result_t result = ISC_R_SUCCESS;
    430 	dns_rdataset_t rds;
    431 	dns_name_t name;
    432 
    433 	dns_rdataset_init(&rds);
    434 	dns_name_init(&name, NULL);
    435 
    436 	do {
    437 		dns_ncache_current(rdataset, &name, &rds);
    438 		for (result = dns_rdataset_first(&rds);
    439 		     result == ISC_R_SUCCESS;
    440 		     result = dns_rdataset_next(&rds)) {
    441 			CHECK(str_totext("; ", target));
    442 			CHECK(dns_name_totext(&name, omit_final_dot, target));
    443 			CHECK(str_totext(" ", target));
    444 			CHECK(dns_rdatatype_totext(rds.type, target));
    445 			if (rds.type == dns_rdatatype_rrsig) {
    446 				CHECK(str_totext(" ", target));
    447 				CHECK(dns_rdatatype_totext(rds.covers, target));
    448 				CHECK(str_totext(" ...\n", target));
    449 			} else {
    450 				dns_rdata_t rdata = DNS_RDATA_INIT;
    451 				dns_rdataset_current(&rds, &rdata);
    452 				CHECK(str_totext(" ", target));
    453 				CHECK(dns_rdata_tofmttext(&rdata, dns_rootname,
    454 							  0, 0, 0, " ", target));
    455 				CHECK(str_totext("\n", target));
    456 			}
    457 		}
    458 		dns_rdataset_disassociate(&rds);
    459 		result = dns_rdataset_next(rdataset);
    460 	} while (result == ISC_R_SUCCESS);
    461 
    462 	if (result == ISC_R_NOMORE)
    463 		result = ISC_R_SUCCESS;
    464  cleanup:
    465 	if (dns_rdataset_isassociated(&rds))
    466 		dns_rdataset_disassociate(&rds);
    467 
    468 	return (result);
    469 }
    470 
    471 /*
    472  * Convert 'rdataset' to master file text format according to 'ctx',
    473  * storing the result in 'target'.  If 'owner_name' is NULL, it
    474  * is omitted; otherwise 'owner_name' must be valid and have at least
    475  * one label.
    476  */
    477 
    478 static isc_result_t
    479 rdataset_totext(dns_rdataset_t *rdataset,
    480 		const dns_name_t *owner_name,
    481 		dns_totext_ctx_t *ctx,
    482 		isc_boolean_t omit_final_dot,
    483 		isc_buffer_t *target)
    484 {
    485 	isc_result_t result;
    486 	unsigned int column;
    487 	isc_boolean_t first = ISC_TRUE;
    488 	isc_uint32_t current_ttl;
    489 	isc_boolean_t current_ttl_valid;
    490 	dns_rdatatype_t type;
    491 	unsigned int type_start;
    492 	dns_fixedname_t fixed;
    493 	dns_name_t *name = NULL;
    494 	unsigned int i;
    495 
    496 	REQUIRE(DNS_RDATASET_VALID(rdataset));
    497 
    498 	rdataset->attributes |= DNS_RDATASETATTR_LOADORDER;
    499 	result = dns_rdataset_first(rdataset);
    500 
    501 	current_ttl = ctx->current_ttl;
    502 	current_ttl_valid = ctx->current_ttl_valid;
    503 
    504 	if (owner_name != NULL) {
    505 		name = dns_fixedname_initname(&fixed);
    506 		dns_name_copy(owner_name, name, NULL);
    507 		dns_rdataset_getownercase(rdataset, name);
    508 	}
    509 
    510 	while (result == ISC_R_SUCCESS) {
    511 		column = 0;
    512 
    513 		/*
    514 		 * Indent?
    515 		 */
    516 		if ((ctx->style.flags & DNS_STYLEFLAG_INDENT) != 0 ||
    517 		    (ctx->style.flags & DNS_STYLEFLAG_YAML) != 0)
    518 			for (i = 0; i < dns_master_indent; i++)
    519 				RETERR(str_totext(dns_master_indentstr,
    520 						  target));
    521 
    522 		/*
    523 		 * YAML enumerator?
    524 		 */
    525 		if ((ctx->style.flags & DNS_STYLEFLAG_YAML) != 0) {
    526 			RETERR(str_totext("- ", target));
    527 		}
    528 
    529 		/*
    530 		 * Comment?
    531 		 */
    532 		if ((ctx->style.flags & DNS_STYLEFLAG_COMMENTDATA) != 0)
    533 			RETERR(str_totext(";", target));
    534 
    535 		/*
    536 		 * Owner name.
    537 		 */
    538 		if (name != NULL &&
    539 		    ! ((ctx->style.flags & DNS_STYLEFLAG_OMIT_OWNER) != 0 &&
    540 		       !first))
    541 		{
    542 			unsigned int name_start = target->used;
    543 			RETERR(dns_name_totext(name, omit_final_dot, target));
    544 			column += target->used - name_start;
    545 		}
    546 
    547 		/*
    548 		 * TTL.
    549 		 */
    550 		if ((ctx->style.flags & DNS_STYLEFLAG_NO_TTL) == 0 &&
    551 		    !((ctx->style.flags & DNS_STYLEFLAG_OMIT_TTL) != 0 &&
    552 		      current_ttl_valid &&
    553 		      rdataset->ttl == current_ttl))
    554 		{
    555 			char ttlbuf[64];
    556 			isc_region_t r;
    557 			unsigned int length;
    558 
    559 			INDENT_TO(ttl_column);
    560 			if ((ctx->style.flags & DNS_STYLEFLAG_TTL_UNITS) != 0) {
    561 				length = target->used;
    562 				result = dns_ttl_totext2(rdataset->ttl,
    563 							ISC_FALSE, ISC_FALSE,
    564 							target);
    565 				if (result != ISC_R_SUCCESS)
    566 					return (result);
    567 				column += target->used - length;
    568 			} else {
    569 				length = snprintf(ttlbuf, sizeof(ttlbuf), "%u",
    570 						  rdataset->ttl);
    571 				INSIST(length <= sizeof(ttlbuf));
    572 				isc_buffer_availableregion(target, &r);
    573 				if (r.length < length)
    574 					return (ISC_R_NOSPACE);
    575 				memmove(r.base, ttlbuf, length);
    576 				isc_buffer_add(target, length);
    577 				column += length;
    578 			}
    579 
    580 			/*
    581 			 * If the $TTL directive is not in use, the TTL we
    582 			 * just printed becomes the default for subsequent RRs.
    583 			 */
    584 			if ((ctx->style.flags & DNS_STYLEFLAG_TTL) == 0) {
    585 				current_ttl = rdataset->ttl;
    586 				current_ttl_valid = ISC_TRUE;
    587 			}
    588 		}
    589 
    590 		/*
    591 		 * Class.
    592 		 */
    593 		if ((ctx->style.flags & DNS_STYLEFLAG_NO_CLASS) == 0 &&
    594 		    ((ctx->style.flags & DNS_STYLEFLAG_OMIT_CLASS) == 0 ||
    595 		     ctx->class_printed == ISC_FALSE))
    596 		{
    597 			unsigned int class_start;
    598 			INDENT_TO(class_column);
    599 			class_start = target->used;
    600 			if ((ctx->style.flags & DNS_STYLEFLAG_UNKNOWNFORMAT) != 0)
    601 				result = dns_rdataclass_tounknowntext
    602 					(rdataset->rdclass, target);
    603 			else
    604 				result = dns_rdataclass_totext
    605 					(rdataset->rdclass, target);
    606 			if (result != ISC_R_SUCCESS)
    607 				return (result);
    608 			column += (target->used - class_start);
    609 		}
    610 
    611 		/*
    612 		 * Type.
    613 		 */
    614 
    615 		if ((rdataset->attributes & DNS_RDATASETATTR_NEGATIVE) != 0) {
    616 			type = rdataset->covers;
    617 		} else {
    618 			type = rdataset->type;
    619 		}
    620 
    621 		INDENT_TO(type_column);
    622 		type_start = target->used;
    623 		if ((rdataset->attributes & DNS_RDATASETATTR_NEGATIVE) != 0)
    624 			RETERR(str_totext("\\-", target));
    625 		switch (type) {
    626 		case dns_rdatatype_keydata:
    627 #define KEYDATA "KEYDATA"
    628 			if ((ctx->style.flags & DNS_STYLEFLAG_KEYDATA) != 0) {
    629 				if (isc_buffer_availablelength(target) <
    630 				    (sizeof(KEYDATA) - 1))
    631 					return (ISC_R_NOSPACE);
    632 				isc_buffer_putstr(target, KEYDATA);
    633 				break;
    634 			}
    635 			/* FALLTHROUGH */
    636 		default:
    637 			if ((ctx->style.flags & DNS_STYLEFLAG_UNKNOWNFORMAT) != 0)
    638 				result = dns_rdatatype_tounknowntext(type, target);
    639 			else
    640 				result = dns_rdatatype_totext(type, target);
    641 			if (result != ISC_R_SUCCESS)
    642 				return (result);
    643 		}
    644 		column += (target->used - type_start);
    645 
    646 		/*
    647 		 * Rdata.
    648 		 */
    649 		INDENT_TO(rdata_column);
    650 		if ((rdataset->attributes & DNS_RDATASETATTR_NEGATIVE) != 0) {
    651 			if ((ctx->style.flags & DNS_STYLEFLAG_INDENT) != 0 ||
    652 			    (ctx->style.flags & DNS_STYLEFLAG_YAML) != 0)
    653 			{
    654 				for (i = 0; i < dns_master_indent; i++)
    655 					RETERR(str_totext(dns_master_indentstr,
    656 							  target));
    657 			}
    658 			if (NXDOMAIN(rdataset))
    659 				RETERR(str_totext(";-$NXDOMAIN\n", target));
    660 			else
    661 				RETERR(str_totext(";-$NXRRSET\n", target));
    662 			/*
    663 			 * Print a summary of the cached records which make
    664 			 * up the negative response.
    665 			 */
    666 			RETERR(ncache_summary(rdataset, omit_final_dot,
    667 					      target));
    668 			break;
    669 		} else {
    670 			dns_rdata_t rdata = DNS_RDATA_INIT;
    671 			isc_region_t r;
    672 
    673 			dns_rdataset_current(rdataset, &rdata);
    674 
    675 			RETERR(dns_rdata_tofmttext(&rdata,
    676 						   ctx->origin,
    677 						   ctx->style.flags,
    678 						   ctx->style.line_length -
    679 						       ctx->style.rdata_column,
    680 						   ctx->style.split_width,
    681 						   ctx->linebreak,
    682 						   target));
    683 
    684 			isc_buffer_availableregion(target, &r);
    685 			if (r.length < 1)
    686 				return (ISC_R_NOSPACE);
    687 			r.base[0] = '\n';
    688 			isc_buffer_add(target, 1);
    689 		}
    690 
    691 		first = ISC_FALSE;
    692 		result = dns_rdataset_next(rdataset);
    693 	}
    694 
    695 	if (result != ISC_R_NOMORE)
    696 		return (result);
    697 
    698 	/*
    699 	 * Update the ctx state to reflect what we just printed.
    700 	 * This is done last, only when we are sure we will return
    701 	 * success, because this function may be called multiple
    702 	 * times with increasing buffer sizes until it succeeds,
    703 	 * and failed attempts must not update the state prematurely.
    704 	 */
    705 	ctx->class_printed = ISC_TRUE;
    706 	ctx->current_ttl= current_ttl;
    707 	ctx->current_ttl_valid = current_ttl_valid;
    708 
    709 	return (ISC_R_SUCCESS);
    710 }
    711 
    712 /*
    713  * Print the name, type, and class of an empty rdataset,
    714  * such as those used to represent the question section
    715  * of a DNS message.
    716  */
    717 static isc_result_t
    718 question_totext(dns_rdataset_t *rdataset,
    719 		const dns_name_t *owner_name,
    720 		dns_totext_ctx_t *ctx,
    721 		isc_boolean_t omit_final_dot,
    722 		isc_buffer_t *target)
    723 {
    724 	unsigned int column;
    725 	isc_result_t result;
    726 	isc_region_t r;
    727 
    728 	REQUIRE(DNS_RDATASET_VALID(rdataset));
    729 	result = dns_rdataset_first(rdataset);
    730 	REQUIRE(result == ISC_R_NOMORE);
    731 
    732 	column = 0;
    733 
    734 	/* Owner name */
    735 	{
    736 		unsigned int name_start = target->used;
    737 		RETERR(dns_name_totext(owner_name,
    738 				       omit_final_dot,
    739 				       target));
    740 		column += target->used - name_start;
    741 	}
    742 
    743 	/* Class */
    744 	{
    745 		unsigned int class_start;
    746 		INDENT_TO(class_column);
    747 		class_start = target->used;
    748 		if ((ctx->style.flags & DNS_STYLEFLAG_UNKNOWNFORMAT) != 0)
    749 			result = dns_rdataclass_tounknowntext(rdataset->rdclass,
    750 							      target);
    751 		else
    752 			result = dns_rdataclass_totext(rdataset->rdclass,
    753 						       target);
    754 		if (result != ISC_R_SUCCESS)
    755 			return (result);
    756 		column += (target->used - class_start);
    757 	}
    758 
    759 	/* Type */
    760 	{
    761 		unsigned int type_start;
    762 		INDENT_TO(type_column);
    763 		type_start = target->used;
    764 		if ((ctx->style.flags & DNS_STYLEFLAG_UNKNOWNFORMAT) != 0)
    765 			result = dns_rdatatype_tounknowntext(rdataset->type,
    766 							     target);
    767 		else
    768 			result = dns_rdatatype_totext(rdataset->type,
    769 						      target);
    770 		if (result != ISC_R_SUCCESS)
    771 			return (result);
    772 		column += (target->used - type_start);
    773 	}
    774 
    775 	isc_buffer_availableregion(target, &r);
    776 	if (r.length < 1)
    777 		return (ISC_R_NOSPACE);
    778 	r.base[0] = '\n';
    779 	isc_buffer_add(target, 1);
    780 
    781 	return (ISC_R_SUCCESS);
    782 }
    783 
    784 isc_result_t
    785 dns_rdataset_totext(dns_rdataset_t *rdataset,
    786 		    const dns_name_t *owner_name,
    787 		    isc_boolean_t omit_final_dot,
    788 		    isc_boolean_t question,
    789 		    isc_buffer_t *target)
    790 {
    791 	dns_totext_ctx_t ctx;
    792 	isc_result_t result;
    793 	result = totext_ctx_init(&dns_master_style_debug, &ctx);
    794 	if (result != ISC_R_SUCCESS) {
    795 		UNEXPECTED_ERROR(__FILE__, __LINE__,
    796 				 "could not set master file style");
    797 		return (ISC_R_UNEXPECTED);
    798 	}
    799 
    800 	/*
    801 	 * The caller might want to give us an empty owner
    802 	 * name (e.g. if they are outputting into a master
    803 	 * file and this rdataset has the same name as the
    804 	 * previous one.)
    805 	 */
    806 	if (dns_name_countlabels(owner_name) == 0)
    807 		owner_name = NULL;
    808 
    809 	if (question)
    810 		return (question_totext(rdataset, owner_name, &ctx,
    811 					omit_final_dot, target));
    812 	else
    813 		return (rdataset_totext(rdataset, owner_name, &ctx,
    814 					omit_final_dot, target));
    815 }
    816 
    817 isc_result_t
    818 dns_master_rdatasettotext(const dns_name_t *owner_name,
    819 			  dns_rdataset_t *rdataset,
    820 			  const dns_master_style_t *style,
    821 			  isc_buffer_t *target)
    822 {
    823 	dns_totext_ctx_t ctx;
    824 	isc_result_t result;
    825 	result = totext_ctx_init(style, &ctx);
    826 	if (result != ISC_R_SUCCESS) {
    827 		UNEXPECTED_ERROR(__FILE__, __LINE__,
    828 				 "could not set master file style");
    829 		return (ISC_R_UNEXPECTED);
    830 	}
    831 
    832 	return (rdataset_totext(rdataset, owner_name, &ctx,
    833 				ISC_FALSE, target));
    834 }
    835 
    836 isc_result_t
    837 dns_master_questiontotext(const dns_name_t *owner_name,
    838 			  dns_rdataset_t *rdataset,
    839 			  const dns_master_style_t *style,
    840 			  isc_buffer_t *target)
    841 {
    842 	dns_totext_ctx_t ctx;
    843 	isc_result_t result;
    844 	result = totext_ctx_init(style, &ctx);
    845 	if (result != ISC_R_SUCCESS) {
    846 		UNEXPECTED_ERROR(__FILE__, __LINE__,
    847 				 "could not set master file style");
    848 		return (ISC_R_UNEXPECTED);
    849 	}
    850 
    851 	return (question_totext(rdataset, owner_name, &ctx,
    852 				ISC_FALSE, target));
    853 }
    854 
    855 /*
    856  * Print an rdataset.  'buffer' is a scratch buffer, which must have been
    857  * dynamically allocated by the caller.  It must be large enough to
    858  * hold the result from dns_ttl_totext().  If more than that is needed,
    859  * the buffer will be grown automatically.
    860  */
    861 
    862 static isc_result_t
    863 dump_rdataset(isc_mem_t *mctx, const dns_name_t *name,
    864 	      dns_rdataset_t *rdataset, dns_totext_ctx_t *ctx,
    865 	      isc_buffer_t *buffer, FILE *f)
    866 {
    867 	isc_region_t r;
    868 	isc_result_t result;
    869 
    870 	REQUIRE(buffer->length > 0);
    871 
    872 	/*
    873 	 * Output a $TTL directive if needed.
    874 	 */
    875 
    876 	if ((ctx->style.flags & DNS_STYLEFLAG_TTL) != 0) {
    877 		if (ctx->current_ttl_valid == ISC_FALSE ||
    878 		    ctx->current_ttl != rdataset->ttl)
    879 		{
    880 			if ((ctx->style.flags & DNS_STYLEFLAG_COMMENT) != 0)
    881 			{
    882 				isc_buffer_clear(buffer);
    883 				result = dns_ttl_totext(rdataset->ttl,
    884 							ISC_TRUE, buffer);
    885 				INSIST(result == ISC_R_SUCCESS);
    886 				isc_buffer_usedregion(buffer, &r);
    887 				fprintf(f, "$TTL %u\t; %.*s\n", rdataset->ttl,
    888 					(int) r.length, (char *) r.base);
    889 			} else {
    890 				fprintf(f, "$TTL %u\n", rdataset->ttl);
    891 			}
    892 			ctx->current_ttl = rdataset->ttl;
    893 			ctx->current_ttl_valid = ISC_TRUE;
    894 		}
    895 	}
    896 
    897 	isc_buffer_clear(buffer);
    898 
    899 	/*
    900 	 * Generate the text representation of the rdataset into
    901 	 * the buffer.  If the buffer is too small, grow it.
    902 	 */
    903 	for (;;) {
    904 		int newlength;
    905 		void *newmem;
    906 		result = rdataset_totext(rdataset, name, ctx,
    907 					 ISC_FALSE, buffer);
    908 		if (result != ISC_R_NOSPACE)
    909 			break;
    910 
    911 		newlength = buffer->length * 2;
    912 		newmem = isc_mem_get(mctx, newlength);
    913 		if (newmem == NULL)
    914 			return (ISC_R_NOMEMORY);
    915 		isc_mem_put(mctx, buffer->base, buffer->length);
    916 		isc_buffer_init(buffer, newmem, newlength);
    917 	}
    918 	if (result != ISC_R_SUCCESS)
    919 		return (result);
    920 
    921 	/*
    922 	 * Write the buffer contents to the master file.
    923 	 */
    924 	isc_buffer_usedregion(buffer, &r);
    925 	result = isc_stdio_write(r.base, 1, (size_t)r.length, f, NULL);
    926 
    927 	if (result != ISC_R_SUCCESS) {
    928 		UNEXPECTED_ERROR(__FILE__, __LINE__,
    929 				 "master file write failed: %s",
    930 				 isc_result_totext(result));
    931 		return (result);
    932 	}
    933 
    934 	return (ISC_R_SUCCESS);
    935 }
    936 
    937 /*
    938  * Define the order in which rdatasets should be printed in zone
    939  * files.  We will print SOA and NS records before others, SIGs
    940  * immediately following the things they sign, and order everything
    941  * else by RR number.  This is all just for aesthetics and
    942  * compatibility with buggy software that expects the SOA to be first;
    943  * the DNS specifications allow any order.
    944  */
    945 
    946 static int
    947 dump_order(const dns_rdataset_t *rds) {
    948 	int t;
    949 	int sig;
    950 	if (rds->type == dns_rdatatype_rrsig) {
    951 		t = rds->covers;
    952 		sig = 1;
    953 	} else {
    954 		t = rds->type;
    955 		sig = 0;
    956 	}
    957 	switch (t) {
    958 	case dns_rdatatype_soa:
    959 		t = 0;
    960 		break;
    961 	case dns_rdatatype_ns:
    962 		t = 1;
    963 		break;
    964 	default:
    965 		t += 2;
    966 		break;
    967 	}
    968 	return (t << 1) + sig;
    969 }
    970 
    971 static int
    972 dump_order_compare(const void *a, const void *b) {
    973 	return (dump_order(*((const dns_rdataset_t * const *) a)) -
    974 		dump_order(*((const dns_rdataset_t * const *) b)));
    975 }
    976 
    977 /*
    978  * Dump all the rdatasets of a domain name to a master file.  We make
    979  * a "best effort" attempt to sort the RRsets in a nice order, but if
    980  * there are more than MAXSORT RRsets, we punt and only sort them in
    981  * groups of MAXSORT.  This is not expected to ever happen in practice
    982  * since much less than 64 RR types have been registered with the
    983  * IANA, so far, and the output will be correct (though not
    984  * aesthetically pleasing) even if it does happen.
    985  */
    986 
    987 #define MAXSORT 64
    988 
    989 static isc_result_t
    990 dump_rdatasets_text(isc_mem_t *mctx, const dns_name_t *name,
    991 		    dns_rdatasetiter_t *rdsiter, dns_totext_ctx_t *ctx,
    992 		    isc_buffer_t *buffer, FILE *f)
    993 {
    994 	isc_result_t itresult, dumpresult;
    995 	isc_region_t r;
    996 	dns_rdataset_t rdatasets[MAXSORT];
    997 	dns_rdataset_t *sorted[MAXSORT];
    998 	int i, n;
    999 
   1000 	itresult = dns_rdatasetiter_first(rdsiter);
   1001 	dumpresult = ISC_R_SUCCESS;
   1002 
   1003 	if (itresult == ISC_R_SUCCESS && ctx->neworigin != NULL) {
   1004 		isc_buffer_clear(buffer);
   1005 		itresult = dns_name_totext(ctx->neworigin, ISC_FALSE, buffer);
   1006 		RUNTIME_CHECK(itresult == ISC_R_SUCCESS);
   1007 		isc_buffer_usedregion(buffer, &r);
   1008 		fprintf(f, "$ORIGIN %.*s\n", (int) r.length, (char *) r.base);
   1009 		ctx->neworigin = NULL;
   1010 	}
   1011 
   1012  again:
   1013 	for (i = 0;
   1014 	     itresult == ISC_R_SUCCESS && i < MAXSORT;
   1015 	     itresult = dns_rdatasetiter_next(rdsiter), i++) {
   1016 		dns_rdataset_init(&rdatasets[i]);
   1017 		dns_rdatasetiter_current(rdsiter, &rdatasets[i]);
   1018 		sorted[i] = &rdatasets[i];
   1019 	}
   1020 	n = i;
   1021 	INSIST(n <= MAXSORT);
   1022 
   1023 	qsort(sorted, n, sizeof(sorted[0]), dump_order_compare);
   1024 
   1025 	for (i = 0; i < n; i++) {
   1026 		dns_rdataset_t *rds = sorted[i];
   1027 		if (ctx->style.flags & DNS_STYLEFLAG_TRUST) {
   1028 			if ((ctx->style.flags & DNS_STYLEFLAG_INDENT) != 0 ||
   1029 			    (ctx->style.flags & DNS_STYLEFLAG_YAML) != 0)
   1030 			{
   1031 				unsigned int j;
   1032 				for (j = 0; j < dns_master_indent; j++)
   1033 					fprintf(f, "%s", dns_master_indentstr);
   1034 			}
   1035 			fprintf(f, "; %s\n", dns_trust_totext(rds->trust));
   1036 		}
   1037 		if (((rds->attributes & DNS_RDATASETATTR_NEGATIVE) != 0) &&
   1038 		    (ctx->style.flags & DNS_STYLEFLAG_NCACHE) == 0) {
   1039 			/* Omit negative cache entries */
   1040 		} else {
   1041 			isc_result_t result;
   1042 			if (rds->ttl < ctx->serve_stale_ttl)
   1043 				fprintf(f, "; stale\n");
   1044 			result = dump_rdataset(mctx, name, rds, ctx, buffer, f);
   1045 			if (result != ISC_R_SUCCESS)
   1046 				dumpresult = result;
   1047 			if ((ctx->style.flags & DNS_STYLEFLAG_OMIT_OWNER) != 0)
   1048 				name = NULL;
   1049 		}
   1050 		if (ctx->style.flags & DNS_STYLEFLAG_RESIGN &&
   1051 		    rds->attributes & DNS_RDATASETATTR_RESIGN) {
   1052 			isc_buffer_t b;
   1053 			char buf[sizeof("YYYYMMDDHHMMSS")];
   1054 			memset(buf, 0, sizeof(buf));
   1055 			isc_buffer_init(&b, buf, sizeof(buf) - 1);
   1056 			dns_time64_totext((isc_uint64_t)rds->resign, &b);
   1057 			if ((ctx->style.flags & DNS_STYLEFLAG_INDENT) != 0 ||
   1058 			    (ctx->style.flags & DNS_STYLEFLAG_YAML) != 0)
   1059 			{
   1060 				unsigned int j;
   1061 				for (j = 0; j < dns_master_indent; j++)
   1062 					fprintf(f, "%s", dns_master_indentstr);
   1063 			}
   1064 			fprintf(f, "; resign=%s\n", buf);
   1065 		}
   1066 		dns_rdataset_disassociate(rds);
   1067 	}
   1068 
   1069 	if (dumpresult != ISC_R_SUCCESS)
   1070 		return (dumpresult);
   1071 
   1072 	/*
   1073 	 * If we got more data than could be sorted at once,
   1074 	 * go handle the rest.
   1075 	 */
   1076 	if (itresult == ISC_R_SUCCESS)
   1077 		goto again;
   1078 
   1079 	if (itresult == ISC_R_NOMORE)
   1080 		itresult = ISC_R_SUCCESS;
   1081 
   1082 	return (itresult);
   1083 }
   1084 
   1085 /*
   1086  * Dump given RRsets in the "raw" format.
   1087  */
   1088 static isc_result_t
   1089 dump_rdataset_raw(isc_mem_t *mctx, const dns_name_t *name,
   1090 		  dns_rdataset_t *rdataset, isc_buffer_t *buffer, FILE *f)
   1091 {
   1092 	isc_result_t result;
   1093 	isc_uint32_t totallen;
   1094 	isc_uint16_t dlen;
   1095 	isc_region_t r, r_hdr;
   1096 
   1097 	REQUIRE(buffer->length > 0);
   1098 	REQUIRE(DNS_RDATASET_VALID(rdataset));
   1099 
   1100 	rdataset->attributes |= DNS_RDATASETATTR_LOADORDER;
   1101  restart:
   1102 	totallen = 0;
   1103 	result = dns_rdataset_first(rdataset);
   1104 	REQUIRE(result == ISC_R_SUCCESS);
   1105 
   1106 	isc_buffer_clear(buffer);
   1107 
   1108 	/*
   1109 	 * Common header and owner name (length followed by name)
   1110 	 * These fields should be in a moderate length, so we assume we
   1111 	 * can store all of them in the initial buffer.
   1112 	 */
   1113 	isc_buffer_availableregion(buffer, &r_hdr);
   1114 	INSIST(r_hdr.length >= sizeof(dns_masterrawrdataset_t));
   1115 	isc_buffer_putuint32(buffer, totallen);	/* XXX: leave space */
   1116 	isc_buffer_putuint16(buffer, rdataset->rdclass); /* 16-bit class */
   1117 	isc_buffer_putuint16(buffer, rdataset->type); /* 16-bit type */
   1118 	isc_buffer_putuint16(buffer, rdataset->covers);	/* same as type */
   1119 	isc_buffer_putuint32(buffer, rdataset->ttl); /* 32-bit TTL */
   1120 	isc_buffer_putuint32(buffer, dns_rdataset_count(rdataset));
   1121 	totallen = isc_buffer_usedlength(buffer);
   1122 	INSIST(totallen <= sizeof(dns_masterrawrdataset_t));
   1123 
   1124 	dns_name_toregion(name, &r);
   1125 	INSIST(isc_buffer_availablelength(buffer) >=
   1126 	       (sizeof(dlen) + r.length));
   1127 	dlen = (isc_uint16_t)r.length;
   1128 	isc_buffer_putuint16(buffer, dlen);
   1129 	isc_buffer_copyregion(buffer, &r);
   1130 	totallen += sizeof(dlen) + r.length;
   1131 
   1132 	do {
   1133 		dns_rdata_t rdata = DNS_RDATA_INIT;
   1134 
   1135 		dns_rdataset_current(rdataset, &rdata);
   1136 		dns_rdata_toregion(&rdata, &r);
   1137 		INSIST(r.length <= 0xffffU);
   1138 		dlen = (isc_uint16_t)r.length;
   1139 
   1140 		/*
   1141 		 * Copy the rdata into the buffer.  If the buffer is too small,
   1142 		 * grow it.  This should be rare, so we'll simply restart the
   1143 		 * entire procedure (or should we copy the old data and
   1144 		 * continue?).
   1145 		 */
   1146 		if (isc_buffer_availablelength(buffer) <
   1147 						 sizeof(dlen) + r.length) {
   1148 			int newlength;
   1149 			void *newmem;
   1150 
   1151 			newlength = buffer->length * 2;
   1152 			newmem = isc_mem_get(mctx, newlength);
   1153 			if (newmem == NULL)
   1154 				return (ISC_R_NOMEMORY);
   1155 			isc_mem_put(mctx, buffer->base, buffer->length);
   1156 			isc_buffer_init(buffer, newmem, newlength);
   1157 			goto restart;
   1158 		}
   1159 		isc_buffer_putuint16(buffer, dlen);
   1160 		isc_buffer_copyregion(buffer, &r);
   1161 		totallen += sizeof(dlen) + r.length;
   1162 
   1163 		result = dns_rdataset_next(rdataset);
   1164 	} while (result == ISC_R_SUCCESS);
   1165 
   1166 	if (result != ISC_R_NOMORE)
   1167 		return (result);
   1168 
   1169 	/*
   1170 	 * Fill in the total length field.
   1171 	 * XXX: this is a bit tricky.  Since we have already "used" the space
   1172 	 * for the total length in the buffer, we first remember the entire
   1173 	 * buffer length in the region, "rewind", and then write the value.
   1174 	 */
   1175 	isc_buffer_usedregion(buffer, &r);
   1176 	isc_buffer_clear(buffer);
   1177 	isc_buffer_putuint32(buffer, totallen);
   1178 	INSIST(isc_buffer_usedlength(buffer) < totallen);
   1179 
   1180 	/*
   1181 	 * Write the buffer contents to the raw master file.
   1182 	 */
   1183 	result = isc_stdio_write(r.base, 1, (size_t)r.length, f, NULL);
   1184 
   1185 	if (result != ISC_R_SUCCESS) {
   1186 		UNEXPECTED_ERROR(__FILE__, __LINE__,
   1187 				 "raw master file write failed: %s",
   1188 				 isc_result_totext(result));
   1189 		return (result);
   1190 	}
   1191 
   1192 	return (result);
   1193 }
   1194 
   1195 static isc_result_t
   1196 dump_rdatasets_raw(isc_mem_t *mctx, const dns_name_t *name,
   1197 		   dns_rdatasetiter_t *rdsiter, dns_totext_ctx_t *ctx,
   1198 		   isc_buffer_t *buffer, FILE *f)
   1199 {
   1200 	isc_result_t result;
   1201 	dns_rdataset_t rdataset;
   1202 
   1203 	for (result = dns_rdatasetiter_first(rdsiter);
   1204 	     result == ISC_R_SUCCESS;
   1205 	     result = dns_rdatasetiter_next(rdsiter)) {
   1206 
   1207 		dns_rdataset_init(&rdataset);
   1208 		dns_rdatasetiter_current(rdsiter, &rdataset);
   1209 
   1210 		if (((rdataset.attributes & DNS_RDATASETATTR_NEGATIVE) != 0) &&
   1211 		    (ctx->style.flags & DNS_STYLEFLAG_NCACHE) == 0) {
   1212 			/* Omit negative cache entries */
   1213 		} else {
   1214 			result = dump_rdataset_raw(mctx, name, &rdataset,
   1215 						   buffer, f);
   1216 		}
   1217 		dns_rdataset_disassociate(&rdataset);
   1218 		if (result != ISC_R_SUCCESS)
   1219 			return (result);
   1220 	}
   1221 
   1222 	if (result == ISC_R_NOMORE)
   1223 		result = ISC_R_SUCCESS;
   1224 
   1225 	return (result);
   1226 }
   1227 
   1228 static isc_result_t
   1229 dump_rdatasets_map(isc_mem_t *mctx, const dns_name_t *name,
   1230 		   dns_rdatasetiter_t *rdsiter, dns_totext_ctx_t *ctx,
   1231 		   isc_buffer_t *buffer, FILE *f)
   1232 {
   1233 	UNUSED(mctx);
   1234 	UNUSED(name);
   1235 	UNUSED(rdsiter);
   1236 	UNUSED(ctx);
   1237 	UNUSED(buffer);
   1238 	UNUSED(f);
   1239 
   1240 	return (ISC_R_NOTIMPLEMENTED);
   1241 }
   1242 
   1243 /*
   1244  * Initial size of text conversion buffer.  The buffer is used
   1245  * for several purposes: converting origin names, rdatasets,
   1246  * $DATE timestamps, and comment strings for $TTL directives.
   1247  *
   1248  * When converting rdatasets, it is dynamically resized, but
   1249  * when converting origins, timestamps, etc it is not.  Therefore,
   1250  * the initial size must large enough to hold the longest possible
   1251  * text representation of any domain name (for $ORIGIN).
   1252  */
   1253 static const int initial_buffer_length = 1200;
   1254 
   1255 static isc_result_t
   1256 dumptostreaminc(dns_dumpctx_t *dctx);
   1257 
   1258 static void
   1259 dumpctx_destroy(dns_dumpctx_t *dctx) {
   1260 
   1261 	dctx->magic = 0;
   1262 	DESTROYLOCK(&dctx->lock);
   1263 	dns_dbiterator_destroy(&dctx->dbiter);
   1264 	if (dctx->version != NULL)
   1265 		dns_db_closeversion(dctx->db, &dctx->version, ISC_FALSE);
   1266 	dns_db_detach(&dctx->db);
   1267 	if (dctx->task != NULL)
   1268 		isc_task_detach(&dctx->task);
   1269 	if (dctx->file != NULL)
   1270 		isc_mem_free(dctx->mctx, dctx->file);
   1271 	if (dctx->tmpfile != NULL)
   1272 		isc_mem_free(dctx->mctx, dctx->tmpfile);
   1273 	isc_mem_putanddetach(&dctx->mctx, dctx, sizeof(*dctx));
   1274 }
   1275 
   1276 void
   1277 dns_dumpctx_attach(dns_dumpctx_t *source, dns_dumpctx_t **target) {
   1278 
   1279 	REQUIRE(DNS_DCTX_VALID(source));
   1280 	REQUIRE(target != NULL && *target == NULL);
   1281 
   1282 	LOCK(&source->lock);
   1283 	INSIST(source->references > 0);
   1284 	source->references++;
   1285 	INSIST(source->references != 0);	/* Overflow? */
   1286 	UNLOCK(&source->lock);
   1287 
   1288 	*target = source;
   1289 }
   1290 
   1291 void
   1292 dns_dumpctx_detach(dns_dumpctx_t **dctxp) {
   1293 	dns_dumpctx_t *dctx;
   1294 	isc_boolean_t need_destroy = ISC_FALSE;
   1295 
   1296 	REQUIRE(dctxp != NULL);
   1297 	dctx = *dctxp;
   1298 	REQUIRE(DNS_DCTX_VALID(dctx));
   1299 
   1300 	*dctxp = NULL;
   1301 
   1302 	LOCK(&dctx->lock);
   1303 	INSIST(dctx->references != 0);
   1304 	dctx->references--;
   1305 	if (dctx->references == 0)
   1306 		need_destroy = ISC_TRUE;
   1307 	UNLOCK(&dctx->lock);
   1308 	if (need_destroy)
   1309 		dumpctx_destroy(dctx);
   1310 }
   1311 
   1312 dns_dbversion_t *
   1313 dns_dumpctx_version(dns_dumpctx_t *dctx) {
   1314 	REQUIRE(DNS_DCTX_VALID(dctx));
   1315 	return (dctx->version);
   1316 }
   1317 
   1318 dns_db_t *
   1319 dns_dumpctx_db(dns_dumpctx_t *dctx) {
   1320 	REQUIRE(DNS_DCTX_VALID(dctx));
   1321 	return (dctx->db);
   1322 }
   1323 
   1324 void
   1325 dns_dumpctx_cancel(dns_dumpctx_t *dctx) {
   1326 	REQUIRE(DNS_DCTX_VALID(dctx));
   1327 
   1328 	LOCK(&dctx->lock);
   1329 	dctx->canceled = ISC_TRUE;
   1330 	UNLOCK(&dctx->lock);
   1331 }
   1332 
   1333 static isc_result_t
   1334 flushandsync(FILE *f, isc_result_t result, const char *temp) {
   1335 	isc_boolean_t logit = ISC_TF(result == ISC_R_SUCCESS);
   1336 
   1337 	if (result == ISC_R_SUCCESS)
   1338 		result = isc_stdio_flush(f);
   1339 	if (result != ISC_R_SUCCESS && logit) {
   1340 		if (temp != NULL)
   1341 			isc_log_write(dns_lctx, ISC_LOGCATEGORY_GENERAL,
   1342 				      DNS_LOGMODULE_MASTERDUMP, ISC_LOG_ERROR,
   1343 				      "dumping to master file: %s: flush: %s",
   1344 				      temp, isc_result_totext(result));
   1345 		else
   1346 			isc_log_write(dns_lctx, ISC_LOGCATEGORY_GENERAL,
   1347 				      DNS_LOGMODULE_MASTERDUMP, ISC_LOG_ERROR,
   1348 				      "dumping to stream: flush: %s",
   1349 				      isc_result_totext(result));
   1350 		logit = ISC_FALSE;
   1351 	}
   1352 
   1353 	if (result == ISC_R_SUCCESS)
   1354 		result = isc_stdio_sync(f);
   1355 	if (result != ISC_R_SUCCESS && logit) {
   1356 		if (temp != NULL)
   1357 			isc_log_write(dns_lctx, ISC_LOGCATEGORY_GENERAL,
   1358 				      DNS_LOGMODULE_MASTERDUMP, ISC_LOG_ERROR,
   1359 				      "dumping to master file: %s: fsync: %s",
   1360 				      temp, isc_result_totext(result));
   1361 		else
   1362 			isc_log_write(dns_lctx, ISC_LOGCATEGORY_GENERAL,
   1363 				      DNS_LOGMODULE_MASTERDUMP, ISC_LOG_ERROR,
   1364 				      "dumping to stream: fsync: %s",
   1365 				      isc_result_totext(result));
   1366 	}
   1367 	return (result);
   1368 }
   1369 
   1370 static isc_result_t
   1371 closeandrename(FILE *f, isc_result_t result, const char *temp, const char *file)
   1372 {
   1373 	isc_result_t tresult;
   1374 	isc_boolean_t logit = ISC_TF(result == ISC_R_SUCCESS);
   1375 
   1376 	result = flushandsync(f, result, temp);
   1377 	if (result != ISC_R_SUCCESS)
   1378 		logit = ISC_FALSE;
   1379 
   1380 	tresult = isc_stdio_close(f);
   1381 	if (result == ISC_R_SUCCESS)
   1382 		result = tresult;
   1383 	if (result != ISC_R_SUCCESS && logit) {
   1384 		isc_log_write(dns_lctx, ISC_LOGCATEGORY_GENERAL,
   1385 			      DNS_LOGMODULE_MASTERDUMP, ISC_LOG_ERROR,
   1386 			      "dumping master file: %s: fclose: %s",
   1387 			      temp, isc_result_totext(result));
   1388 		logit = ISC_FALSE;
   1389 	}
   1390 	if (result == ISC_R_SUCCESS)
   1391 		result = isc_file_rename(temp, file);
   1392 	else
   1393 		(void)isc_file_remove(temp);
   1394 	if (result != ISC_R_SUCCESS && logit) {
   1395 		isc_log_write(dns_lctx, ISC_LOGCATEGORY_GENERAL,
   1396 			      DNS_LOGMODULE_MASTERDUMP, ISC_LOG_ERROR,
   1397 			      "dumping master file: rename: %s: %s",
   1398 			      file, isc_result_totext(result));
   1399 	}
   1400 	return (result);
   1401 }
   1402 
   1403 static void
   1404 dump_quantum(isc_task_t *task, isc_event_t *event) {
   1405 	isc_result_t result;
   1406 	isc_result_t tresult;
   1407 	dns_dumpctx_t *dctx;
   1408 
   1409 	REQUIRE(event != NULL);
   1410 	dctx = event->ev_arg;
   1411 	REQUIRE(DNS_DCTX_VALID(dctx));
   1412 	if (dctx->canceled)
   1413 		result = ISC_R_CANCELED;
   1414 	else
   1415 		result = dumptostreaminc(dctx);
   1416 	if (result == DNS_R_CONTINUE) {
   1417 		event->ev_arg = dctx;
   1418 		isc_task_send(task, &event);
   1419 		return;
   1420 	}
   1421 
   1422 	if (dctx->file != NULL) {
   1423 		tresult = closeandrename(dctx->f, result,
   1424 					 dctx->tmpfile, dctx->file);
   1425 		if (tresult != ISC_R_SUCCESS && result == ISC_R_SUCCESS)
   1426 			result = tresult;
   1427 	} else
   1428 		result = flushandsync(dctx->f, result, NULL);
   1429 	(dctx->done)(dctx->done_arg, result);
   1430 	isc_event_free(&event);
   1431 	dns_dumpctx_detach(&dctx);
   1432 }
   1433 
   1434 static isc_result_t
   1435 task_send(dns_dumpctx_t *dctx) {
   1436 	isc_event_t *event;
   1437 
   1438 	event = isc_event_allocate(dctx->mctx, NULL, DNS_EVENT_DUMPQUANTUM,
   1439 				   dump_quantum, dctx, sizeof(*event));
   1440 	if (event == NULL)
   1441 		return (ISC_R_NOMEMORY);
   1442 	isc_task_send(dctx->task, &event);
   1443 	return (ISC_R_SUCCESS);
   1444 }
   1445 
   1446 static isc_result_t
   1447 dumpctx_create(isc_mem_t *mctx, dns_db_t *db, dns_dbversion_t *version,
   1448 	       const dns_master_style_t *style, FILE *f, dns_dumpctx_t **dctxp,
   1449 	       dns_masterformat_t format, dns_masterrawheader_t *header)
   1450 {
   1451 	dns_dumpctx_t *dctx;
   1452 	isc_result_t result;
   1453 	unsigned int options;
   1454 
   1455 	dctx = isc_mem_get(mctx, sizeof(*dctx));
   1456 	if (dctx == NULL)
   1457 		return (ISC_R_NOMEMORY);
   1458 
   1459 	dctx->mctx = NULL;
   1460 	dctx->f = f;
   1461 	dctx->dbiter = NULL;
   1462 	dctx->db = NULL;
   1463 	dctx->version = NULL;
   1464 	dctx->done = NULL;
   1465 	dctx->done_arg = NULL;
   1466 	dctx->task = NULL;
   1467 	dctx->nodes = 0;
   1468 	dctx->first = ISC_TRUE;
   1469 	dctx->canceled = ISC_FALSE;
   1470 	dctx->file = NULL;
   1471 	dctx->tmpfile = NULL;
   1472 	dctx->format = format;
   1473 	if (header == NULL)
   1474 		dns_master_initrawheader(&dctx->header);
   1475 	else
   1476 		dctx->header = *header;
   1477 
   1478 	switch (format) {
   1479 	case dns_masterformat_text:
   1480 		dctx->dumpsets = dump_rdatasets_text;
   1481 		break;
   1482 	case dns_masterformat_raw:
   1483 		dctx->dumpsets = dump_rdatasets_raw;
   1484 		break;
   1485 	case dns_masterformat_map:
   1486 		dctx->dumpsets = dump_rdatasets_map;
   1487 		break;
   1488 	default:
   1489 		INSIST(0);
   1490 		break;
   1491 	}
   1492 
   1493 	result = totext_ctx_init(style, &dctx->tctx);
   1494 	if (result != ISC_R_SUCCESS) {
   1495 		UNEXPECTED_ERROR(__FILE__, __LINE__,
   1496 				 "could not set master file style");
   1497 		goto cleanup;
   1498 	}
   1499 
   1500 	isc_stdtime_get(&dctx->now);
   1501 	dns_db_attach(db, &dctx->db);
   1502 
   1503 	dctx->do_date = dns_db_iscache(dctx->db);
   1504 	if (dctx->do_date) {
   1505 	    /*
   1506 	     * Adjust the date backwards by the serve-stale TTL, if any.
   1507 	     * This is so the TTL will be loaded correctly when next started.
   1508 	     */
   1509 	    (void)dns_db_getservestalettl(dctx->db,
   1510 					  &dctx->tctx.serve_stale_ttl);
   1511 	    dctx->now -= dctx->tctx.serve_stale_ttl;
   1512 	}
   1513 
   1514 	if (dctx->format == dns_masterformat_text &&
   1515 	    (dctx->tctx.style.flags & DNS_STYLEFLAG_REL_OWNER) != 0) {
   1516 		options = DNS_DB_RELATIVENAMES;
   1517 	} else
   1518 		options = 0;
   1519 	result = dns_db_createiterator(dctx->db, options, &dctx->dbiter);
   1520 	if (result != ISC_R_SUCCESS)
   1521 		goto cleanup;
   1522 
   1523 	result = isc_mutex_init(&dctx->lock);
   1524 	if (result != ISC_R_SUCCESS)
   1525 		goto cleanup;
   1526 	if (version != NULL)
   1527 		dns_db_attachversion(dctx->db, version, &dctx->version);
   1528 	else if (!dns_db_iscache(db))
   1529 		dns_db_currentversion(dctx->db, &dctx->version);
   1530 	isc_mem_attach(mctx, &dctx->mctx);
   1531 	dctx->references = 1;
   1532 	dctx->magic = DNS_DCTX_MAGIC;
   1533 	*dctxp = dctx;
   1534 	return (ISC_R_SUCCESS);
   1535 
   1536  cleanup:
   1537 	if (dctx->dbiter != NULL)
   1538 		dns_dbiterator_destroy(&dctx->dbiter);
   1539 	if (dctx->db != NULL)
   1540 		dns_db_detach(&dctx->db);
   1541 	if (dctx != NULL)
   1542 		isc_mem_put(mctx, dctx, sizeof(*dctx));
   1543 	return (result);
   1544 }
   1545 
   1546 static isc_result_t
   1547 writeheader(dns_dumpctx_t *dctx) {
   1548 	isc_result_t result = ISC_R_SUCCESS;
   1549 	isc_buffer_t buffer;
   1550 	char *bufmem;
   1551 	isc_region_t r;
   1552 	dns_masterrawheader_t rawheader;
   1553 	isc_uint32_t rawversion, now32;
   1554 
   1555 	bufmem = isc_mem_get(dctx->mctx, initial_buffer_length);
   1556 	if (bufmem == NULL)
   1557 		return (ISC_R_NOMEMORY);
   1558 
   1559 	isc_buffer_init(&buffer, bufmem, initial_buffer_length);
   1560 
   1561 	switch (dctx->format) {
   1562 	case dns_masterformat_text:
   1563 		/*
   1564 		 * If the database has cache semantics, output an
   1565 		 * RFC2540 $DATE directive so that the TTLs can be
   1566 		 * adjusted when it is reloaded.  For zones it is not
   1567 		 * really needed, and it would make the file
   1568 		 * incompatible with pre-RFC2540 software, so we omit
   1569 		 * it in the zone case.
   1570 		 */
   1571 		if (dctx->do_date) {
   1572 			fprintf(dctx->f,
   1573 				"; using a %u second stale ttl\n",
   1574 				dctx->tctx.serve_stale_ttl);
   1575 			result = dns_time32_totext(dctx->now, &buffer);
   1576 			RUNTIME_CHECK(result == ISC_R_SUCCESS);
   1577 			isc_buffer_usedregion(&buffer, &r);
   1578 			fprintf(dctx->f, "$DATE %.*s\n",
   1579 				(int) r.length, (char *) r.base);
   1580 		}
   1581 		break;
   1582 	case dns_masterformat_raw:
   1583 	case dns_masterformat_map:
   1584 		r.base = (unsigned char *)&rawheader;
   1585 		r.length = sizeof(rawheader);
   1586 		isc_buffer_region(&buffer, &r);
   1587 #if !defined(STDTIME_ON_32BITS) || (STDTIME_ON_32BITS + 0) != 1
   1588 		/*
   1589 		 * We assume isc_stdtime_t is a 32-bit integer,
   1590 		 * which should be the case on most platforms.
   1591 		 * If it turns out to be uncommon, we'll need
   1592 		 * to bump the version number and revise the
   1593 		 * header format.
   1594 		 */
   1595 		isc_log_write(dns_lctx,
   1596 			      ISC_LOGCATEGORY_GENERAL,
   1597 			      DNS_LOGMODULE_MASTERDUMP,
   1598 			      ISC_LOG_INFO,
   1599 			      "dumping master file in raw "
   1600 			      "format: stdtime is not 32bits");
   1601 		now32 = 0;
   1602 #else
   1603 		now32 = dctx->now;
   1604 #endif
   1605 		rawversion = 1;
   1606 		if ((dctx->header.flags & DNS_MASTERRAW_COMPAT) != 0)
   1607 			rawversion = 0;
   1608 
   1609 		isc_buffer_putuint32(&buffer, dctx->format);
   1610 		isc_buffer_putuint32(&buffer, rawversion);
   1611 		isc_buffer_putuint32(&buffer, now32);
   1612 
   1613 		if (rawversion == 1) {
   1614 			isc_buffer_putuint32(&buffer, dctx->header.flags);
   1615 			isc_buffer_putuint32(&buffer,
   1616 					     dctx->header.sourceserial);
   1617 			isc_buffer_putuint32(&buffer, dctx->header.lastxfrin);
   1618 		}
   1619 
   1620 		INSIST(isc_buffer_usedlength(&buffer) <= sizeof(rawheader));
   1621 		result = isc_stdio_write(buffer.base, 1,
   1622 					 isc_buffer_usedlength(&buffer),
   1623 					 dctx->f, NULL);
   1624 		if (result != ISC_R_SUCCESS)
   1625 			break;
   1626 
   1627 		break;
   1628 	default:
   1629 		INSIST(0);
   1630 	}
   1631 
   1632 	isc_mem_put(dctx->mctx, buffer.base, buffer.length);
   1633 	return (result);
   1634 }
   1635 
   1636 static isc_result_t
   1637 dumptostreaminc(dns_dumpctx_t *dctx) {
   1638 	isc_result_t result = ISC_R_SUCCESS;
   1639 	isc_buffer_t buffer;
   1640 	char *bufmem;
   1641 	dns_name_t *name;
   1642 	dns_fixedname_t fixname;
   1643 	unsigned int nodes;
   1644 	isc_time_t start;
   1645 
   1646 	bufmem = isc_mem_get(dctx->mctx, initial_buffer_length);
   1647 	if (bufmem == NULL)
   1648 		return (ISC_R_NOMEMORY);
   1649 
   1650 	isc_buffer_init(&buffer, bufmem, initial_buffer_length);
   1651 
   1652 	name = dns_fixedname_initname(&fixname);
   1653 
   1654 	if (dctx->first) {
   1655 		CHECK(writeheader(dctx));
   1656 
   1657 		/*
   1658 		 * Fast format is not currently written incrementally,
   1659 		 * so we make the call to dns_db_serialize() here.
   1660 		 * If the database is anything other than an rbtdb,
   1661 		 * this should result in not implemented
   1662 		 */
   1663 		if (dctx->format == dns_masterformat_map) {
   1664 			result = dns_db_serialize(dctx->db, dctx->version,
   1665 						  dctx->f);
   1666 			goto cleanup;
   1667 		}
   1668 
   1669 		result = dns_dbiterator_first(dctx->dbiter);
   1670 		if (result != ISC_R_SUCCESS && result != ISC_R_NOMORE)
   1671 			goto cleanup;
   1672 
   1673 		dctx->first = ISC_FALSE;
   1674 	} else
   1675 		result = ISC_R_SUCCESS;
   1676 
   1677 	nodes = dctx->nodes;
   1678 	isc_time_now(&start);
   1679 	while (result == ISC_R_SUCCESS && (dctx->nodes == 0 || nodes--)) {
   1680 		dns_rdatasetiter_t *rdsiter = NULL;
   1681 		dns_dbnode_t *node = NULL;
   1682 
   1683 		result = dns_dbiterator_current(dctx->dbiter, &node, name);
   1684 		if (result != ISC_R_SUCCESS && result != DNS_R_NEWORIGIN)
   1685 			break;
   1686 		if (result == DNS_R_NEWORIGIN) {
   1687 			dns_name_t *origin =
   1688 				dns_fixedname_name(&dctx->tctx.origin_fixname);
   1689 			result = dns_dbiterator_origin(dctx->dbiter, origin);
   1690 			RUNTIME_CHECK(result == ISC_R_SUCCESS);
   1691 			if ((dctx->tctx.style.flags &
   1692 			     DNS_STYLEFLAG_REL_DATA) != 0)
   1693 				dctx->tctx.origin = origin;
   1694 			dctx->tctx.neworigin = origin;
   1695 		}
   1696 		result = dns_db_allrdatasets(dctx->db, node, dctx->version,
   1697 					     dctx->now, &rdsiter);
   1698 		if (result != ISC_R_SUCCESS) {
   1699 			dns_db_detachnode(dctx->db, &node);
   1700 			goto cleanup;
   1701 		}
   1702 		result = (dctx->dumpsets)(dctx->mctx, name, rdsiter,
   1703 					  &dctx->tctx, &buffer, dctx->f);
   1704 		dns_rdatasetiter_destroy(&rdsiter);
   1705 		if (result != ISC_R_SUCCESS) {
   1706 			dns_db_detachnode(dctx->db, &node);
   1707 			goto cleanup;
   1708 		}
   1709 		dns_db_detachnode(dctx->db, &node);
   1710 		result = dns_dbiterator_next(dctx->dbiter);
   1711 	}
   1712 
   1713 	/*
   1714 	 * Work out how many nodes can be written in the time between
   1715 	 * two requests to the nameserver.  Smooth the resulting number and
   1716 	 * use it as a estimate for the number of nodes to be written in the
   1717 	 * next iteration.
   1718 	 */
   1719 	if (dctx->nodes != 0 && result == ISC_R_SUCCESS) {
   1720 		unsigned int pps = dns_pps;	/* packets per second */
   1721 		unsigned int interval;
   1722 		isc_uint64_t usecs;
   1723 		isc_time_t end;
   1724 
   1725 		isc_time_now(&end);
   1726 		if (pps < 100)
   1727 			pps = 100;
   1728 		interval = 1000000 / pps;	/* interval in usecs */
   1729 		if (interval == 0)
   1730 			interval = 1;
   1731 		usecs = isc_time_microdiff(&end, &start);
   1732 		if (usecs == 0) {
   1733 			dctx->nodes = dctx->nodes * 2;
   1734 			if (dctx->nodes > 1000)
   1735 				dctx->nodes = 1000;
   1736 		} else {
   1737 			nodes = dctx->nodes * interval;
   1738 			nodes /= (unsigned int)usecs;
   1739 			if (nodes == 0)
   1740 				nodes = 1;
   1741 			else if (nodes > 1000)
   1742 				nodes = 1000;
   1743 
   1744 			/* Smooth and assign. */
   1745 			dctx->nodes = (nodes + dctx->nodes * 7) / 8;
   1746 
   1747 			isc_log_write(dns_lctx, ISC_LOGCATEGORY_GENERAL,
   1748 				      DNS_LOGMODULE_MASTERDUMP,
   1749 				      ISC_LOG_DEBUG(1),
   1750 				      "dumptostreaminc(%p) new nodes -> %d",
   1751 				      dctx, dctx->nodes);
   1752 		}
   1753 		result = DNS_R_CONTINUE;
   1754 	} else if (result == ISC_R_NOMORE)
   1755 		result = ISC_R_SUCCESS;
   1756  cleanup:
   1757 	RUNTIME_CHECK(dns_dbiterator_pause(dctx->dbiter) == ISC_R_SUCCESS);
   1758 	isc_mem_put(dctx->mctx, buffer.base, buffer.length);
   1759 	return (result);
   1760 }
   1761 
   1762 isc_result_t
   1763 dns_master_dumptostreaminc(isc_mem_t *mctx, dns_db_t *db,
   1764 			   dns_dbversion_t *version,
   1765 			   const dns_master_style_t *style,
   1766 			   FILE *f, isc_task_t *task,
   1767 			   dns_dumpdonefunc_t done, void *done_arg,
   1768 			   dns_dumpctx_t **dctxp)
   1769 {
   1770 	dns_dumpctx_t *dctx = NULL;
   1771 	isc_result_t result;
   1772 
   1773 	REQUIRE(task != NULL);
   1774 	REQUIRE(f != NULL);
   1775 	REQUIRE(done != NULL);
   1776 
   1777 	result = dumpctx_create(mctx, db, version, style, f, &dctx,
   1778 				dns_masterformat_text, NULL);
   1779 	if (result != ISC_R_SUCCESS)
   1780 		return (result);
   1781 	isc_task_attach(task, &dctx->task);
   1782 	dctx->done = done;
   1783 	dctx->done_arg = done_arg;
   1784 	dctx->nodes = 100;
   1785 
   1786 	result = task_send(dctx);
   1787 	if (result == ISC_R_SUCCESS) {
   1788 		dns_dumpctx_attach(dctx, dctxp);
   1789 		return (DNS_R_CONTINUE);
   1790 	}
   1791 
   1792 	dns_dumpctx_detach(&dctx);
   1793 	return (result);
   1794 }
   1795 
   1796 /*
   1797  * Dump an entire database into a master file.
   1798  */
   1799 isc_result_t
   1800 dns_master_dumptostream(isc_mem_t *mctx, dns_db_t *db,
   1801 			dns_dbversion_t *version,
   1802 			const dns_master_style_t *style,
   1803 			FILE *f)
   1804 {
   1805 	return (dns_master_dumptostream3(mctx, db, version, style,
   1806 					 dns_masterformat_text, NULL, f));
   1807 }
   1808 
   1809 isc_result_t
   1810 dns_master_dumptostream2(isc_mem_t *mctx, dns_db_t *db,
   1811 			 dns_dbversion_t *version,
   1812 			 const dns_master_style_t *style,
   1813 			 dns_masterformat_t format, FILE *f)
   1814 {
   1815 	return (dns_master_dumptostream3(mctx, db, version, style,
   1816 					 format, NULL, f));
   1817 }
   1818 
   1819 isc_result_t
   1820 dns_master_dumptostream3(isc_mem_t *mctx, dns_db_t *db,
   1821 			 dns_dbversion_t *version,
   1822 			 const dns_master_style_t *style,
   1823 			 dns_masterformat_t format,
   1824 			 dns_masterrawheader_t *header, FILE *f)
   1825 {
   1826 	dns_dumpctx_t *dctx = NULL;
   1827 	isc_result_t result;
   1828 
   1829 	result = dumpctx_create(mctx, db, version, style, f, &dctx,
   1830 				format, header);
   1831 	if (result != ISC_R_SUCCESS)
   1832 		return (result);
   1833 
   1834 	result = dumptostreaminc(dctx);
   1835 	INSIST(result != DNS_R_CONTINUE);
   1836 	dns_dumpctx_detach(&dctx);
   1837 
   1838 	result = flushandsync(f, result, NULL);
   1839 	return (result);
   1840 }
   1841 
   1842 static isc_result_t
   1843 opentmp(isc_mem_t *mctx, dns_masterformat_t format, const char *file,
   1844 	char **tempp, FILE **fp) {
   1845 	FILE *f = NULL;
   1846 	isc_result_t result;
   1847 	char *tempname = NULL;
   1848 	int tempnamelen;
   1849 
   1850 	tempnamelen = strlen(file) + 20;
   1851 	tempname = isc_mem_allocate(mctx, tempnamelen);
   1852 	if (tempname == NULL)
   1853 		return (ISC_R_NOMEMORY);
   1854 
   1855 	result = isc_file_mktemplate(file, tempname, tempnamelen);
   1856 	if (result != ISC_R_SUCCESS)
   1857 		goto cleanup;
   1858 
   1859 	if (format == dns_masterformat_text)
   1860 		result = isc_file_openunique(tempname, &f);
   1861 	else
   1862 		result = isc_file_bopenunique(tempname, &f);
   1863 	if (result != ISC_R_SUCCESS) {
   1864 		isc_log_write(dns_lctx, ISC_LOGCATEGORY_GENERAL,
   1865 			      DNS_LOGMODULE_MASTERDUMP, ISC_LOG_ERROR,
   1866 			      "dumping master file: %s: open: %s",
   1867 			      tempname, isc_result_totext(result));
   1868 		goto cleanup;
   1869 	}
   1870 	*tempp = tempname;
   1871 	*fp = f;
   1872 	return (ISC_R_SUCCESS);
   1873 
   1874 cleanup:
   1875 	isc_mem_free(mctx, tempname);
   1876 	return (result);
   1877 }
   1878 
   1879 isc_result_t
   1880 dns_master_dumpinc(isc_mem_t *mctx, dns_db_t *db, dns_dbversion_t *version,
   1881 		   const dns_master_style_t *style, const char *filename,
   1882 		   isc_task_t *task, dns_dumpdonefunc_t done, void *done_arg,
   1883 		   dns_dumpctx_t **dctxp)
   1884 {
   1885 	return (dns_master_dumpinc3(mctx, db, version, style, filename, task,
   1886 				    done, done_arg, dctxp,
   1887 				    dns_masterformat_text, NULL));
   1888 }
   1889 
   1890 isc_result_t
   1891 dns_master_dumpinc2(isc_mem_t *mctx, dns_db_t *db, dns_dbversion_t *version,
   1892 		    const dns_master_style_t *style, const char *filename,
   1893 		    isc_task_t *task, dns_dumpdonefunc_t done, void *done_arg,
   1894 		    dns_dumpctx_t **dctxp, dns_masterformat_t format)
   1895 {
   1896 	return (dns_master_dumpinc3(mctx, db, version, style, filename, task,
   1897 				    done, done_arg, dctxp, format, NULL));
   1898 }
   1899 
   1900 isc_result_t
   1901 dns_master_dumpinc3(isc_mem_t *mctx, dns_db_t *db, dns_dbversion_t *version,
   1902 		    const dns_master_style_t *style, const char *filename,
   1903 		    isc_task_t *task, dns_dumpdonefunc_t done, void *done_arg,
   1904 		    dns_dumpctx_t **dctxp, dns_masterformat_t format,
   1905 		    dns_masterrawheader_t *header)
   1906 {
   1907 	FILE *f = NULL;
   1908 	isc_result_t result;
   1909 	char *tempname = NULL;
   1910 	char *file = NULL;
   1911 	dns_dumpctx_t *dctx = NULL;
   1912 
   1913 	file = isc_mem_strdup(mctx, filename);
   1914 	if (file == NULL)
   1915 		return (ISC_R_NOMEMORY);
   1916 
   1917 	result = opentmp(mctx, format, filename, &tempname, &f);
   1918 	if (result != ISC_R_SUCCESS)
   1919 		goto cleanup;
   1920 
   1921 	result = dumpctx_create(mctx, db, version, style, f, &dctx,
   1922 				format, header);
   1923 	if (result != ISC_R_SUCCESS) {
   1924 		(void)isc_stdio_close(f);
   1925 		(void)isc_file_remove(tempname);
   1926 		goto cleanup;
   1927 	}
   1928 
   1929 	isc_task_attach(task, &dctx->task);
   1930 	dctx->done = done;
   1931 	dctx->done_arg = done_arg;
   1932 	dctx->nodes = 100;
   1933 	dctx->file = file;
   1934 	file = NULL;
   1935 	dctx->tmpfile = tempname;
   1936 	tempname = NULL;
   1937 
   1938 	result = task_send(dctx);
   1939 	if (result == ISC_R_SUCCESS) {
   1940 		dns_dumpctx_attach(dctx, dctxp);
   1941 		return (DNS_R_CONTINUE);
   1942 	}
   1943 
   1944  cleanup:
   1945 	if (dctx != NULL)
   1946 		dns_dumpctx_detach(&dctx);
   1947 	if (file != NULL)
   1948 		isc_mem_free(mctx, file);
   1949 	if (tempname != NULL)
   1950 		isc_mem_free(mctx, tempname);
   1951 	return (result);
   1952 }
   1953 
   1954 isc_result_t
   1955 dns_master_dump(isc_mem_t *mctx, dns_db_t *db, dns_dbversion_t *version,
   1956 		const dns_master_style_t *style, const char *filename)
   1957 {
   1958 	return (dns_master_dump3(mctx, db, version, style, filename,
   1959 				 dns_masterformat_text, NULL));
   1960 }
   1961 
   1962 isc_result_t
   1963 dns_master_dump2(isc_mem_t *mctx, dns_db_t *db, dns_dbversion_t *version,
   1964 		 const dns_master_style_t *style, const char *filename,
   1965 		 dns_masterformat_t format)
   1966 {
   1967 	return (dns_master_dump3(mctx, db, version, style, filename,
   1968 				 format, NULL));
   1969 }
   1970 
   1971 isc_result_t
   1972 dns_master_dump3(isc_mem_t *mctx, dns_db_t *db, dns_dbversion_t *version,
   1973 		 const dns_master_style_t *style, const char *filename,
   1974 		 dns_masterformat_t format, dns_masterrawheader_t *header)
   1975 {
   1976 	FILE *f = NULL;
   1977 	isc_result_t result;
   1978 	char *tempname;
   1979 	dns_dumpctx_t *dctx = NULL;
   1980 
   1981 	result = opentmp(mctx, format, filename, &tempname, &f);
   1982 	if (result != ISC_R_SUCCESS)
   1983 		return (result);
   1984 
   1985 	result = dumpctx_create(mctx, db, version, style, f, &dctx,
   1986 				format, header);
   1987 	if (result != ISC_R_SUCCESS)
   1988 		goto cleanup;
   1989 
   1990 	result = dumptostreaminc(dctx);
   1991 	INSIST(result != DNS_R_CONTINUE);
   1992 	dns_dumpctx_detach(&dctx);
   1993 
   1994 	result = closeandrename(f, result, tempname, filename);
   1995 
   1996  cleanup:
   1997 	isc_mem_free(mctx, tempname);
   1998 	return (result);
   1999 }
   2000 
   2001 /*
   2002  * Dump a database node into a master file.
   2003  * XXX: this function assumes the text format.
   2004  */
   2005 isc_result_t
   2006 dns_master_dumpnodetostream(isc_mem_t *mctx, dns_db_t *db,
   2007 			    dns_dbversion_t *version,
   2008 			    dns_dbnode_t *node, const dns_name_t *name,
   2009 			    const dns_master_style_t *style,
   2010 			    FILE *f)
   2011 {
   2012 	isc_result_t result;
   2013 	isc_buffer_t buffer;
   2014 	char *bufmem;
   2015 	isc_stdtime_t now;
   2016 	dns_totext_ctx_t ctx;
   2017 	dns_rdatasetiter_t *rdsiter = NULL;
   2018 
   2019 	result = totext_ctx_init(style, &ctx);
   2020 	if (result != ISC_R_SUCCESS) {
   2021 		UNEXPECTED_ERROR(__FILE__, __LINE__,
   2022 				 "could not set master file style");
   2023 		return (ISC_R_UNEXPECTED);
   2024 	}
   2025 
   2026 	isc_stdtime_get(&now);
   2027 
   2028 	bufmem = isc_mem_get(mctx, initial_buffer_length);
   2029 	if (bufmem == NULL)
   2030 		return (ISC_R_NOMEMORY);
   2031 
   2032 	isc_buffer_init(&buffer, bufmem, initial_buffer_length);
   2033 
   2034 	result = dns_db_allrdatasets(db, node, version, now, &rdsiter);
   2035 	if (result != ISC_R_SUCCESS)
   2036 		goto failure;
   2037 	result = dump_rdatasets_text(mctx, name, rdsiter, &ctx, &buffer, f);
   2038 	if (result != ISC_R_SUCCESS)
   2039 		goto failure;
   2040 	dns_rdatasetiter_destroy(&rdsiter);
   2041 
   2042 	result = ISC_R_SUCCESS;
   2043 
   2044  failure:
   2045 	isc_mem_put(mctx, buffer.base, buffer.length);
   2046 	return (result);
   2047 }
   2048 
   2049 isc_result_t
   2050 dns_master_dumpnode(isc_mem_t *mctx, dns_db_t *db, dns_dbversion_t *version,
   2051 		    dns_dbnode_t *node, const dns_name_t *name,
   2052 		    const dns_master_style_t *style, const char *filename)
   2053 {
   2054 	FILE *f = NULL;
   2055 	isc_result_t result;
   2056 
   2057 	result = isc_stdio_open(filename, "w", &f);
   2058 	if (result != ISC_R_SUCCESS) {
   2059 		isc_log_write(dns_lctx, ISC_LOGCATEGORY_GENERAL,
   2060 			      DNS_LOGMODULE_MASTERDUMP, ISC_LOG_ERROR,
   2061 			      "dumping node to file: %s: open: %s", filename,
   2062 			      isc_result_totext(result));
   2063 		return (ISC_R_UNEXPECTED);
   2064 	}
   2065 
   2066 	result = dns_master_dumpnodetostream(mctx, db, version, node, name,
   2067 					     style, f);
   2068 	if (result != ISC_R_SUCCESS) {
   2069 		isc_log_write(dns_lctx, ISC_LOGCATEGORY_GENERAL,
   2070 			      DNS_LOGMODULE_MASTERDUMP, ISC_LOG_ERROR,
   2071 			      "dumping master file: %s: dump: %s", filename,
   2072 			      isc_result_totext(result));
   2073 		(void)isc_stdio_close(f);
   2074 		return (ISC_R_UNEXPECTED);
   2075 	}
   2076 
   2077 	result = isc_stdio_close(f);
   2078 	if (result != ISC_R_SUCCESS) {
   2079 		isc_log_write(dns_lctx, ISC_LOGCATEGORY_GENERAL,
   2080 			      DNS_LOGMODULE_MASTERDUMP, ISC_LOG_ERROR,
   2081 			      "dumping master file: %s: close: %s", filename,
   2082 			      isc_result_totext(result));
   2083 		return (ISC_R_UNEXPECTED);
   2084 	}
   2085 
   2086 	return (result);
   2087 }
   2088 
   2089 dns_masterstyle_flags_t
   2090 dns_master_styleflags(const dns_master_style_t *style) {
   2091 	REQUIRE(style != NULL);
   2092 	return (style->flags);
   2093 }
   2094 
   2095 isc_result_t
   2096 dns_master_stylecreate(dns_master_style_t **stylep,
   2097 		       dns_masterstyle_flags_t flags,
   2098 		       unsigned int ttl_column, unsigned int class_column,
   2099 		       unsigned int type_column, unsigned int rdata_column,
   2100 		       unsigned int line_length, unsigned int tab_width,
   2101 		       isc_mem_t *mctx)
   2102 {
   2103 	return (dns_master_stylecreate2(stylep, flags, ttl_column,
   2104 					class_column, type_column,
   2105 					rdata_column, line_length,
   2106 					tab_width, 0xffffffff, mctx));
   2107 }
   2108 
   2109 isc_result_t
   2110 dns_master_stylecreate2(dns_master_style_t **stylep,
   2111 			dns_masterstyle_flags_t flags,
   2112 			unsigned int ttl_column, unsigned int class_column,
   2113 			unsigned int type_column, unsigned int rdata_column,
   2114 			unsigned int line_length, unsigned int tab_width,
   2115 			unsigned int split_width, isc_mem_t *mctx)
   2116 {
   2117 	dns_master_style_t *style;
   2118 
   2119 	REQUIRE(stylep != NULL && *stylep == NULL);
   2120 	style = isc_mem_get(mctx, sizeof(*style));
   2121 	if (style == NULL)
   2122 		return (ISC_R_NOMEMORY);
   2123 
   2124 	style->flags = flags;
   2125 	style->ttl_column = ttl_column;
   2126 	style->class_column = class_column;
   2127 	style->type_column = type_column;
   2128 	style->rdata_column = rdata_column;
   2129 	style->line_length = line_length;
   2130 	style->tab_width = tab_width;
   2131 	style->split_width = split_width;
   2132 	*stylep = style;
   2133 	return (ISC_R_SUCCESS);
   2134 }
   2135 
   2136 void
   2137 dns_master_styledestroy(dns_master_style_t **stylep, isc_mem_t *mctx) {
   2138 	dns_master_style_t *style;
   2139 
   2140 	REQUIRE(stylep != NULL && *stylep != NULL);
   2141 	style = *stylep;
   2142 	*stylep = NULL;
   2143 	isc_mem_put(mctx, style, sizeof(*style));
   2144 }
   2145