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