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