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