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