xref: /netbsd-src/external/mpl/bind/dist/lib/dns/masterdump.c (revision cef8759bd76c1b621f8eab8faa6f208faabc2e15)
1 /*	$NetBSD: masterdump.c,v 1.8 2020/08/03 17:23:41 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 (/*CONSTCOND*/0)
62 
63 #define CHECK(x)                          \
64 	do {                              \
65 		if ((x) != ISC_R_SUCCESS) \
66 			goto cleanup;     \
67 	} while (/*CONSTCOND*/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 (/*CONSTCOND*/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