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