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