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