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