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