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