masterdump.c revision 1.12 1 /* $NetBSD: masterdump.c,v 1.12 2022/09/23 12:15:29 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
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
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
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, ®ion);
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
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
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 result = dns_rdataclass_tounknowntext(
674 rdataset->rdclass, target);
675 } else {
676 result = dns_rdataclass_totext(
677 rdataset->rdclass, target);
678 }
679 if (result != ISC_R_SUCCESS) {
680 return (result);
681 }
682 column += (target->used - class_start);
683 }
684
685 /*
686 * Type.
687 */
688
689 if ((rdataset->attributes & DNS_RDATASETATTR_NEGATIVE) != 0) {
690 type = rdataset->covers;
691 } else {
692 type = rdataset->type;
693 }
694
695 INDENT_TO(type_column);
696 type_start = target->used;
697 if ((rdataset->attributes & DNS_RDATASETATTR_NEGATIVE) != 0) {
698 RETERR(str_totext("\\-", target));
699 }
700 switch (type) {
701 case dns_rdatatype_keydata:
702 #define KEYDATA "KEYDATA"
703 if ((ctx->style.flags & DNS_STYLEFLAG_KEYDATA) != 0) {
704 if (isc_buffer_availablelength(target) <
705 (sizeof(KEYDATA) - 1)) {
706 return (ISC_R_NOSPACE);
707 }
708 isc_buffer_putstr(target, KEYDATA);
709 break;
710 }
711 FALLTHROUGH;
712 default:
713 if ((ctx->style.flags & DNS_STYLEFLAG_UNKNOWNFORMAT) !=
714 0) {
715 result = dns_rdatatype_tounknowntext(type,
716 target);
717 } else {
718 result = dns_rdatatype_totext(type, target);
719 }
720 if (result != ISC_R_SUCCESS) {
721 return (result);
722 }
723 }
724 column += (target->used - type_start);
725
726 /*
727 * Rdata.
728 */
729 INDENT_TO(rdata_column);
730 if ((rdataset->attributes & DNS_RDATASETATTR_NEGATIVE) != 0) {
731 if (NXDOMAIN(rdataset)) {
732 RETERR(str_totext(";-$NXDOMAIN\n", target));
733 } else {
734 RETERR(str_totext(";-$NXRRSET\n", target));
735 }
736 /*
737 * Print a summary of the cached records which make
738 * up the negative response.
739 */
740 RETERR(ncache_summary(rdataset, omit_final_dot, ctx,
741 target));
742 break;
743 } else {
744 dns_rdata_t rdata = DNS_RDATA_INIT;
745 isc_region_t r;
746
747 dns_rdataset_current(rdataset, &rdata);
748
749 RETERR(dns_rdata_tofmttext(
750 &rdata, ctx->origin, ctx->style.flags,
751 ctx->style.line_length -
752 ctx->style.rdata_column,
753 ctx->style.split_width, ctx->linebreak,
754 target));
755
756 isc_buffer_availableregion(target, &r);
757 if (r.length < 1) {
758 return (ISC_R_NOSPACE);
759 }
760 r.base[0] = '\n';
761 isc_buffer_add(target, 1);
762 }
763
764 first = false;
765 result = dns_rdataset_next(rdataset);
766 }
767
768 if (result != ISC_R_NOMORE) {
769 return (result);
770 }
771
772 /*
773 * Update the ctx state to reflect what we just printed.
774 * This is done last, only when we are sure we will return
775 * success, because this function may be called multiple
776 * times with increasing buffer sizes until it succeeds,
777 * and failed attempts must not update the state prematurely.
778 */
779 ctx->class_printed = true;
780 ctx->current_ttl = current_ttl;
781 ctx->current_ttl_valid = current_ttl_valid;
782
783 return (ISC_R_SUCCESS);
784 }
785
786 /*
787 * Print the name, type, and class of an empty rdataset,
788 * such as those used to represent the question section
789 * of a DNS message.
790 */
791 static isc_result_t
792 question_totext(dns_rdataset_t *rdataset, const dns_name_t *owner_name,
793 dns_totext_ctx_t *ctx, bool omit_final_dot,
794 isc_buffer_t *target) {
795 unsigned int column;
796 isc_result_t result;
797 isc_region_t r;
798
799 REQUIRE(DNS_RDATASET_VALID(rdataset));
800 result = dns_rdataset_first(rdataset);
801 REQUIRE(result == ISC_R_NOMORE);
802
803 column = 0;
804
805 /* Owner name */
806 {
807 unsigned int name_start = target->used;
808 RETERR(dns_name_totext(owner_name, omit_final_dot, target));
809 column += target->used - name_start;
810 }
811
812 /* Class */
813 {
814 unsigned int class_start;
815 INDENT_TO(class_column);
816 class_start = target->used;
817 if ((ctx->style.flags & DNS_STYLEFLAG_UNKNOWNFORMAT) != 0) {
818 result = dns_rdataclass_tounknowntext(rdataset->rdclass,
819 target);
820 } else {
821 result = dns_rdataclass_totext(rdataset->rdclass,
822 target);
823 }
824 if (result != ISC_R_SUCCESS) {
825 return (result);
826 }
827 column += (target->used - class_start);
828 }
829
830 /* Type */
831 {
832 unsigned int type_start;
833 INDENT_TO(type_column);
834 type_start = target->used;
835 if ((ctx->style.flags & DNS_STYLEFLAG_UNKNOWNFORMAT) != 0) {
836 result = dns_rdatatype_tounknowntext(rdataset->type,
837 target);
838 } else {
839 result = dns_rdatatype_totext(rdataset->type, target);
840 }
841 if (result != ISC_R_SUCCESS) {
842 return (result);
843 }
844 column += (target->used - type_start);
845 }
846
847 isc_buffer_availableregion(target, &r);
848 if (r.length < 1) {
849 return (ISC_R_NOSPACE);
850 }
851 r.base[0] = '\n';
852 isc_buffer_add(target, 1);
853
854 return (ISC_R_SUCCESS);
855 }
856
857 isc_result_t
858 dns_rdataset_totext(dns_rdataset_t *rdataset, const dns_name_t *owner_name,
859 bool omit_final_dot, bool question, isc_buffer_t *target) {
860 dns_totext_ctx_t ctx;
861 isc_result_t result;
862 result = totext_ctx_init(&dns_master_style_debug, NULL, &ctx);
863 if (result != ISC_R_SUCCESS) {
864 UNEXPECTED_ERROR(__FILE__, __LINE__,
865 "could not set master file style");
866 return (ISC_R_UNEXPECTED);
867 }
868
869 /*
870 * The caller might want to give us an empty owner
871 * name (e.g. if they are outputting into a master
872 * file and this rdataset has the same name as the
873 * previous one.)
874 */
875 if (dns_name_countlabels(owner_name) == 0) {
876 owner_name = NULL;
877 }
878
879 if (question) {
880 return (question_totext(rdataset, owner_name, &ctx,
881 omit_final_dot, target));
882 } else {
883 return (rdataset_totext(rdataset, owner_name, &ctx,
884 omit_final_dot, target));
885 }
886 }
887
888 isc_result_t
889 dns_master_rdatasettotext(const dns_name_t *owner_name,
890 dns_rdataset_t *rdataset,
891 const dns_master_style_t *style, dns_indent_t *indent,
892 isc_buffer_t *target) {
893 dns_totext_ctx_t ctx;
894 isc_result_t result;
895 result = totext_ctx_init(style, indent, &ctx);
896 if (result != ISC_R_SUCCESS) {
897 UNEXPECTED_ERROR(__FILE__, __LINE__,
898 "could not set master file style");
899 return (ISC_R_UNEXPECTED);
900 }
901
902 return (rdataset_totext(rdataset, owner_name, &ctx, false, target));
903 }
904
905 isc_result_t
906 dns_master_questiontotext(const dns_name_t *owner_name,
907 dns_rdataset_t *rdataset,
908 const dns_master_style_t *style,
909 isc_buffer_t *target) {
910 dns_totext_ctx_t ctx;
911 isc_result_t result;
912 result = totext_ctx_init(style, NULL, &ctx);
913 if (result != ISC_R_SUCCESS) {
914 UNEXPECTED_ERROR(__FILE__, __LINE__,
915 "could not set master file style");
916 return (ISC_R_UNEXPECTED);
917 }
918
919 return (question_totext(rdataset, owner_name, &ctx, false, target));
920 }
921
922 /*
923 * Print an rdataset. 'buffer' is a scratch buffer, which must have been
924 * dynamically allocated by the caller. It must be large enough to
925 * hold the result from dns_ttl_totext(). If more than that is needed,
926 * the buffer will be grown automatically.
927 */
928
929 static isc_result_t
930 dump_rdataset(isc_mem_t *mctx, const dns_name_t *name, dns_rdataset_t *rdataset,
931 dns_totext_ctx_t *ctx, isc_buffer_t *buffer, FILE *f) {
932 isc_region_t r;
933 isc_result_t result;
934
935 REQUIRE(buffer->length > 0);
936
937 /*
938 * Output a $TTL directive if needed.
939 */
940
941 if ((ctx->style.flags & DNS_STYLEFLAG_TTL) != 0) {
942 if (!ctx->current_ttl_valid ||
943 ctx->current_ttl != rdataset->ttl) {
944 if ((ctx->style.flags & DNS_STYLEFLAG_COMMENT) != 0) {
945 isc_buffer_clear(buffer);
946 result = dns_ttl_totext(rdataset->ttl, true,
947 true, buffer);
948 INSIST(result == ISC_R_SUCCESS);
949 isc_buffer_usedregion(buffer, &r);
950 fprintf(f, "$TTL %u\t; %.*s\n", rdataset->ttl,
951 (int)r.length, (char *)r.base);
952 } else {
953 fprintf(f, "$TTL %u\n", rdataset->ttl);
954 }
955 ctx->current_ttl = rdataset->ttl;
956 ctx->current_ttl_valid = true;
957 }
958 }
959
960 isc_buffer_clear(buffer);
961
962 /*
963 * Generate the text representation of the rdataset into
964 * the buffer. If the buffer is too small, grow it.
965 */
966 for (;;) {
967 int newlength;
968 void *newmem;
969 result = rdataset_totext(rdataset, name, ctx, false, buffer);
970 if (result != ISC_R_NOSPACE) {
971 break;
972 }
973
974 newlength = buffer->length * 2;
975 newmem = isc_mem_get(mctx, newlength);
976 isc_mem_put(mctx, buffer->base, buffer->length);
977 isc_buffer_init(buffer, newmem, newlength);
978 }
979 if (result != ISC_R_SUCCESS) {
980 return (result);
981 }
982
983 /*
984 * Write the buffer contents to the master file.
985 */
986 isc_buffer_usedregion(buffer, &r);
987 result = isc_stdio_write(r.base, 1, (size_t)r.length, f, NULL);
988
989 if (result != ISC_R_SUCCESS) {
990 UNEXPECTED_ERROR(__FILE__, __LINE__,
991 "master file write failed: %s",
992 isc_result_totext(result));
993 return (result);
994 }
995
996 return (ISC_R_SUCCESS);
997 }
998
999 /*
1000 * Define the order in which rdatasets should be printed in zone
1001 * files. We will print SOA and NS records before others, SIGs
1002 * immediately following the things they sign, and order everything
1003 * else by RR number. This is all just for aesthetics and
1004 * compatibility with buggy software that expects the SOA to be first;
1005 * the DNS specifications allow any order.
1006 */
1007
1008 static int
1009 dump_order(const dns_rdataset_t *rds) {
1010 int t;
1011 int sig;
1012 if (rds->type == dns_rdatatype_rrsig) {
1013 t = rds->covers;
1014 sig = 1;
1015 } else {
1016 t = rds->type;
1017 sig = 0;
1018 }
1019 switch (t) {
1020 case dns_rdatatype_soa:
1021 t = 0;
1022 break;
1023 case dns_rdatatype_ns:
1024 t = 1;
1025 break;
1026 default:
1027 t += 2;
1028 break;
1029 }
1030 return ((t << 1) + sig);
1031 }
1032
1033 static int
1034 dump_order_compare(const void *a, const void *b) {
1035 return (dump_order(*((const dns_rdataset_t *const *)a)) -
1036 dump_order(*((const dns_rdataset_t *const *)b)));
1037 }
1038
1039 /*
1040 * Dump all the rdatasets of a domain name to a master file. We make
1041 * a "best effort" attempt to sort the RRsets in a nice order, but if
1042 * there are more than MAXSORT RRsets, we punt and only sort them in
1043 * groups of MAXSORT. This is not expected to ever happen in practice
1044 * since much less than 64 RR types have been registered with the
1045 * IANA, so far, and the output will be correct (though not
1046 * aesthetically pleasing) even if it does happen.
1047 */
1048
1049 #define MAXSORT 64
1050
1051 static isc_result_t
1052 dump_rdatasets_text(isc_mem_t *mctx, const dns_name_t *name,
1053 dns_rdatasetiter_t *rdsiter, dns_totext_ctx_t *ctx,
1054 isc_buffer_t *buffer, FILE *f) {
1055 isc_result_t itresult, dumpresult;
1056 isc_region_t r;
1057 dns_rdataset_t rdatasets[MAXSORT];
1058 dns_rdataset_t *sorted[MAXSORT];
1059 int i, n;
1060
1061 itresult = dns_rdatasetiter_first(rdsiter);
1062 dumpresult = ISC_R_SUCCESS;
1063
1064 if (itresult == ISC_R_SUCCESS && ctx->neworigin != NULL) {
1065 isc_buffer_clear(buffer);
1066 itresult = dns_name_totext(ctx->neworigin, false, buffer);
1067 RUNTIME_CHECK(itresult == ISC_R_SUCCESS);
1068 isc_buffer_usedregion(buffer, &r);
1069 fprintf(f, "$ORIGIN %.*s\n", (int)r.length, (char *)r.base);
1070 ctx->neworigin = NULL;
1071 }
1072
1073 again:
1074 for (i = 0; itresult == ISC_R_SUCCESS && i < MAXSORT;
1075 itresult = dns_rdatasetiter_next(rdsiter), i++)
1076 {
1077 dns_rdataset_init(&rdatasets[i]);
1078 dns_rdatasetiter_current(rdsiter, &rdatasets[i]);
1079 sorted[i] = &rdatasets[i];
1080 }
1081 n = i;
1082 INSIST(n <= MAXSORT);
1083
1084 qsort(sorted, n, sizeof(sorted[0]), dump_order_compare);
1085
1086 for (i = 0; i < n; i++) {
1087 dns_rdataset_t *rds = sorted[i];
1088
1089 if (ANCIENT(rds) &&
1090 (ctx->style.flags & DNS_STYLEFLAG_EXPIRED) == 0) {
1091 /* Omit expired entries */
1092 dns_rdataset_disassociate(rds);
1093 continue;
1094 }
1095
1096 if ((ctx->style.flags & DNS_STYLEFLAG_TRUST) != 0) {
1097 if ((ctx->style.flags & DNS_STYLEFLAG_INDENT) != 0 ||
1098 (ctx->style.flags & DNS_STYLEFLAG_YAML) != 0)
1099 {
1100 unsigned int j;
1101 for (j = 0; j < ctx->indent.count; j++) {
1102 fprintf(f, "%s", ctx->indent.string);
1103 }
1104 }
1105 fprintf(f, "; %s\n", dns_trust_totext(rds->trust));
1106 }
1107 if (((rds->attributes & DNS_RDATASETATTR_NEGATIVE) != 0) &&
1108 (ctx->style.flags & DNS_STYLEFLAG_NCACHE) == 0)
1109 {
1110 /* Omit negative cache entries */
1111 } else {
1112 isc_result_t result;
1113 if (STALE(rds)) {
1114 fprintf(f, "; stale\n");
1115 } else if (ANCIENT(rds)) {
1116 isc_buffer_t b;
1117 char buf[sizeof("YYYYMMDDHHMMSS")];
1118 memset(buf, 0, sizeof(buf));
1119 isc_buffer_init(&b, buf, sizeof(buf) - 1);
1120 dns_time64_totext((uint64_t)rds->ttl, &b);
1121 fprintf(f,
1122 "; expired since %s "
1123 "(awaiting cleanup)\n",
1124 buf);
1125 }
1126 result = dump_rdataset(mctx, name, rds, ctx, buffer, f);
1127 if (result != ISC_R_SUCCESS) {
1128 dumpresult = result;
1129 }
1130 if ((ctx->style.flags & DNS_STYLEFLAG_OMIT_OWNER) != 0)
1131 {
1132 name = NULL;
1133 }
1134 }
1135 if (((ctx->style.flags & DNS_STYLEFLAG_RESIGN) != 0) &&
1136 ((rds->attributes & DNS_RDATASETATTR_RESIGN) != 0))
1137 {
1138 isc_buffer_t b;
1139 char buf[sizeof("YYYYMMDDHHMMSS")];
1140 memset(buf, 0, sizeof(buf));
1141 isc_buffer_init(&b, buf, sizeof(buf) - 1);
1142 dns_time64_totext((uint64_t)rds->resign, &b);
1143 if ((ctx->style.flags & DNS_STYLEFLAG_INDENT) != 0 ||
1144 (ctx->style.flags & DNS_STYLEFLAG_YAML) != 0)
1145 {
1146 unsigned int j;
1147 for (j = 0; j < ctx->indent.count; j++) {
1148 fprintf(f, "%s", ctx->indent.string);
1149 }
1150 }
1151 fprintf(f, "; resign=%s\n", buf);
1152 }
1153 dns_rdataset_disassociate(rds);
1154 }
1155
1156 if (dumpresult != ISC_R_SUCCESS) {
1157 return (dumpresult);
1158 }
1159
1160 /*
1161 * If we got more data than could be sorted at once,
1162 * go handle the rest.
1163 */
1164 if (itresult == ISC_R_SUCCESS) {
1165 goto again;
1166 }
1167
1168 if (itresult == ISC_R_NOMORE) {
1169 itresult = ISC_R_SUCCESS;
1170 }
1171
1172 return (itresult);
1173 }
1174
1175 /*
1176 * Dump given RRsets in the "raw" format.
1177 */
1178 static isc_result_t
1179 dump_rdataset_raw(isc_mem_t *mctx, const dns_name_t *name,
1180 dns_rdataset_t *rdataset, isc_buffer_t *buffer, FILE *f) {
1181 isc_result_t result;
1182 uint32_t totallen;
1183 uint16_t dlen;
1184 isc_region_t r, r_hdr;
1185
1186 REQUIRE(buffer->length > 0);
1187 REQUIRE(DNS_RDATASET_VALID(rdataset));
1188
1189 rdataset->attributes |= DNS_RDATASETATTR_LOADORDER;
1190 restart:
1191 totallen = 0;
1192 result = dns_rdataset_first(rdataset);
1193 REQUIRE(result == ISC_R_SUCCESS);
1194
1195 isc_buffer_clear(buffer);
1196
1197 /*
1198 * Common header and owner name (length followed by name)
1199 * These fields should be in a moderate length, so we assume we
1200 * can store all of them in the initial buffer.
1201 */
1202 isc_buffer_availableregion(buffer, &r_hdr);
1203 INSIST(r_hdr.length >= sizeof(dns_masterrawrdataset_t));
1204 isc_buffer_putuint32(buffer, totallen); /* XXX: leave space */
1205 isc_buffer_putuint16(buffer, rdataset->rdclass); /* 16-bit class */
1206 isc_buffer_putuint16(buffer, rdataset->type); /* 16-bit type */
1207 isc_buffer_putuint16(buffer, rdataset->covers); /* same as type */
1208 isc_buffer_putuint32(buffer, rdataset->ttl); /* 32-bit TTL */
1209 isc_buffer_putuint32(buffer, dns_rdataset_count(rdataset));
1210 totallen = isc_buffer_usedlength(buffer);
1211 INSIST(totallen <= sizeof(dns_masterrawrdataset_t));
1212
1213 dns_name_toregion(name, &r);
1214 INSIST(isc_buffer_availablelength(buffer) >= (sizeof(dlen) + r.length));
1215 dlen = (uint16_t)r.length;
1216 isc_buffer_putuint16(buffer, dlen);
1217 isc_buffer_copyregion(buffer, &r);
1218 totallen += sizeof(dlen) + r.length;
1219
1220 do {
1221 dns_rdata_t rdata = DNS_RDATA_INIT;
1222
1223 dns_rdataset_current(rdataset, &rdata);
1224 dns_rdata_toregion(&rdata, &r);
1225 INSIST(r.length <= 0xffffU);
1226 dlen = (uint16_t)r.length;
1227
1228 /*
1229 * Copy the rdata into the buffer. If the buffer is too small,
1230 * grow it. This should be rare, so we'll simply restart the
1231 * entire procedure (or should we copy the old data and
1232 * continue?).
1233 */
1234 if (isc_buffer_availablelength(buffer) <
1235 sizeof(dlen) + r.length) {
1236 int newlength;
1237 void *newmem;
1238
1239 newlength = buffer->length * 2;
1240 newmem = isc_mem_get(mctx, newlength);
1241 isc_mem_put(mctx, buffer->base, buffer->length);
1242 isc_buffer_init(buffer, newmem, newlength);
1243 goto restart;
1244 }
1245 isc_buffer_putuint16(buffer, dlen);
1246 isc_buffer_copyregion(buffer, &r);
1247 totallen += sizeof(dlen) + r.length;
1248
1249 result = dns_rdataset_next(rdataset);
1250 } while (result == ISC_R_SUCCESS);
1251
1252 if (result != ISC_R_NOMORE) {
1253 return (result);
1254 }
1255
1256 /*
1257 * Fill in the total length field.
1258 * XXX: this is a bit tricky. Since we have already "used" the space
1259 * for the total length in the buffer, we first remember the entire
1260 * buffer length in the region, "rewind", and then write the value.
1261 */
1262 isc_buffer_usedregion(buffer, &r);
1263 isc_buffer_clear(buffer);
1264 isc_buffer_putuint32(buffer, totallen);
1265 INSIST(isc_buffer_usedlength(buffer) < totallen);
1266
1267 /*
1268 * Write the buffer contents to the raw master file.
1269 */
1270 result = isc_stdio_write(r.base, 1, (size_t)r.length, f, NULL);
1271
1272 if (result != ISC_R_SUCCESS) {
1273 UNEXPECTED_ERROR(__FILE__, __LINE__,
1274 "raw master file write failed: %s",
1275 isc_result_totext(result));
1276 return (result);
1277 }
1278
1279 return (result);
1280 }
1281
1282 static isc_result_t
1283 dump_rdatasets_raw(isc_mem_t *mctx, const dns_name_t *owner_name,
1284 dns_rdatasetiter_t *rdsiter, dns_totext_ctx_t *ctx,
1285 isc_buffer_t *buffer, FILE *f) {
1286 isc_result_t result;
1287 dns_rdataset_t rdataset;
1288 dns_fixedname_t fixed;
1289 dns_name_t *name;
1290
1291 name = dns_fixedname_initname(&fixed);
1292 dns_name_copynf(owner_name, name);
1293 for (result = dns_rdatasetiter_first(rdsiter); result == ISC_R_SUCCESS;
1294 result = dns_rdatasetiter_next(rdsiter))
1295 {
1296 dns_rdataset_init(&rdataset);
1297 dns_rdatasetiter_current(rdsiter, &rdataset);
1298
1299 dns_rdataset_getownercase(&rdataset, name);
1300
1301 if (((rdataset.attributes & DNS_RDATASETATTR_NEGATIVE) != 0) &&
1302 (ctx->style.flags & DNS_STYLEFLAG_NCACHE) == 0)
1303 {
1304 /* Omit negative cache entries */
1305 } else {
1306 result = dump_rdataset_raw(mctx, name, &rdataset,
1307 buffer, f);
1308 }
1309 dns_rdataset_disassociate(&rdataset);
1310 if (result != ISC_R_SUCCESS) {
1311 return (result);
1312 }
1313 }
1314
1315 if (result == ISC_R_NOMORE) {
1316 result = ISC_R_SUCCESS;
1317 }
1318
1319 return (result);
1320 }
1321
1322 static isc_result_t
1323 dump_rdatasets_map(isc_mem_t *mctx, const dns_name_t *name,
1324 dns_rdatasetiter_t *rdsiter, dns_totext_ctx_t *ctx,
1325 isc_buffer_t *buffer, FILE *f) {
1326 UNUSED(mctx);
1327 UNUSED(name);
1328 UNUSED(rdsiter);
1329 UNUSED(ctx);
1330 UNUSED(buffer);
1331 UNUSED(f);
1332
1333 return (ISC_R_NOTIMPLEMENTED);
1334 }
1335
1336 /*
1337 * Initial size of text conversion buffer. The buffer is used
1338 * for several purposes: converting origin names, rdatasets,
1339 * $DATE timestamps, and comment strings for $TTL directives.
1340 *
1341 * When converting rdatasets, it is dynamically resized, but
1342 * when converting origins, timestamps, etc it is not. Therefore,
1343 * the initial size must large enough to hold the longest possible
1344 * text representation of any domain name (for $ORIGIN).
1345 */
1346 static const int initial_buffer_length = 1200;
1347
1348 static isc_result_t
1349 dumptostream(dns_dumpctx_t *dctx);
1350
1351 static void
1352 dumpctx_destroy(dns_dumpctx_t *dctx) {
1353 dctx->magic = 0;
1354 isc_mutex_destroy(&dctx->lock);
1355 dns_dbiterator_destroy(&dctx->dbiter);
1356 if (dctx->version != NULL) {
1357 dns_db_closeversion(dctx->db, &dctx->version, false);
1358 }
1359 dns_db_detach(&dctx->db);
1360 if (dctx->task != NULL) {
1361 isc_task_detach(&dctx->task);
1362 }
1363 if (dctx->file != NULL) {
1364 isc_mem_free(dctx->mctx, dctx->file);
1365 }
1366 if (dctx->tmpfile != NULL) {
1367 isc_mem_free(dctx->mctx, dctx->tmpfile);
1368 }
1369 isc_mem_putanddetach(&dctx->mctx, dctx, sizeof(*dctx));
1370 }
1371
1372 void
1373 dns_dumpctx_attach(dns_dumpctx_t *source, dns_dumpctx_t **target) {
1374 REQUIRE(DNS_DCTX_VALID(source));
1375 REQUIRE(target != NULL && *target == NULL);
1376
1377 isc_refcount_increment(&source->references);
1378
1379 *target = source;
1380 }
1381
1382 void
1383 dns_dumpctx_detach(dns_dumpctx_t **dctxp) {
1384 dns_dumpctx_t *dctx;
1385
1386 REQUIRE(dctxp != NULL);
1387 dctx = *dctxp;
1388 *dctxp = NULL;
1389 REQUIRE(DNS_DCTX_VALID(dctx));
1390
1391 if (isc_refcount_decrement(&dctx->references) == 1) {
1392 dumpctx_destroy(dctx);
1393 }
1394 }
1395
1396 dns_dbversion_t *
1397 dns_dumpctx_version(dns_dumpctx_t *dctx) {
1398 REQUIRE(DNS_DCTX_VALID(dctx));
1399 return (dctx->version);
1400 }
1401
1402 dns_db_t *
1403 dns_dumpctx_db(dns_dumpctx_t *dctx) {
1404 REQUIRE(DNS_DCTX_VALID(dctx));
1405 return (dctx->db);
1406 }
1407
1408 void
1409 dns_dumpctx_cancel(dns_dumpctx_t *dctx) {
1410 REQUIRE(DNS_DCTX_VALID(dctx));
1411
1412 atomic_store_release(&dctx->canceled, true);
1413 }
1414
1415 static isc_result_t
1416 flushandsync(FILE *f, isc_result_t result, const char *temp) {
1417 bool logit = (result == ISC_R_SUCCESS);
1418
1419 if (result == ISC_R_SUCCESS) {
1420 result = isc_stdio_flush(f);
1421 }
1422 if (result != ISC_R_SUCCESS && logit) {
1423 if (temp != NULL) {
1424 isc_log_write(dns_lctx, ISC_LOGCATEGORY_GENERAL,
1425 DNS_LOGMODULE_MASTERDUMP, ISC_LOG_ERROR,
1426 "dumping to master file: %s: flush: %s",
1427 temp, isc_result_totext(result));
1428 } else {
1429 isc_log_write(dns_lctx, ISC_LOGCATEGORY_GENERAL,
1430 DNS_LOGMODULE_MASTERDUMP, ISC_LOG_ERROR,
1431 "dumping to stream: flush: %s",
1432 isc_result_totext(result));
1433 }
1434 logit = false;
1435 }
1436
1437 if (result == ISC_R_SUCCESS) {
1438 result = isc_stdio_sync(f);
1439 }
1440 if (result != ISC_R_SUCCESS && logit) {
1441 if (temp != NULL) {
1442 isc_log_write(dns_lctx, ISC_LOGCATEGORY_GENERAL,
1443 DNS_LOGMODULE_MASTERDUMP, ISC_LOG_ERROR,
1444 "dumping to master file: %s: fsync: %s",
1445 temp, isc_result_totext(result));
1446 } else {
1447 isc_log_write(dns_lctx, ISC_LOGCATEGORY_GENERAL,
1448 DNS_LOGMODULE_MASTERDUMP, ISC_LOG_ERROR,
1449 "dumping to stream: fsync: %s",
1450 isc_result_totext(result));
1451 }
1452 }
1453 return (result);
1454 }
1455
1456 static isc_result_t
1457 closeandrename(FILE *f, isc_result_t result, const char *temp,
1458 const char *file) {
1459 isc_result_t tresult;
1460 bool logit = (result == ISC_R_SUCCESS);
1461
1462 result = flushandsync(f, result, temp);
1463 if (result != ISC_R_SUCCESS) {
1464 logit = false;
1465 }
1466
1467 tresult = isc_stdio_close(f);
1468 if (result == ISC_R_SUCCESS) {
1469 result = tresult;
1470 }
1471 if (result != ISC_R_SUCCESS && logit) {
1472 isc_log_write(dns_lctx, ISC_LOGCATEGORY_GENERAL,
1473 DNS_LOGMODULE_MASTERDUMP, ISC_LOG_ERROR,
1474 "dumping master file: %s: fclose: %s", temp,
1475 isc_result_totext(result));
1476 logit = false;
1477 }
1478 if (result == ISC_R_SUCCESS) {
1479 result = isc_file_rename(temp, file);
1480 } else {
1481 (void)isc_file_remove(temp);
1482 }
1483 if (result != ISC_R_SUCCESS && logit) {
1484 isc_log_write(dns_lctx, ISC_LOGCATEGORY_GENERAL,
1485 DNS_LOGMODULE_MASTERDUMP, ISC_LOG_ERROR,
1486 "dumping master file: rename: %s: %s", file,
1487 isc_result_totext(result));
1488 }
1489 return (result);
1490 }
1491
1492 /*
1493 * This will run in a libuv threadpool thread.
1494 */
1495 static void
1496 master_dump_cb(void *data) {
1497 isc_result_t result = ISC_R_UNSET;
1498 dns_dumpctx_t *dctx = data;
1499 REQUIRE(DNS_DCTX_VALID(dctx));
1500
1501 if (atomic_load_acquire(&dctx->canceled)) {
1502 result = ISC_R_CANCELED;
1503 } else {
1504 result = dumptostream(dctx);
1505 }
1506
1507 if (dctx->file != NULL) {
1508 isc_result_t tresult = ISC_R_UNSET;
1509 tresult = closeandrename(dctx->f, result, dctx->tmpfile,
1510 dctx->file);
1511 if (tresult != ISC_R_SUCCESS && result == ISC_R_SUCCESS) {
1512 result = tresult;
1513 }
1514 } else {
1515 result = flushandsync(dctx->f, result, NULL);
1516 }
1517
1518 dctx->result = result;
1519 }
1520
1521 /*
1522 * This will run in a network/task manager thread when the dump is complete.
1523 */
1524 static void
1525 master_dump_done_cb(void *data, isc_result_t result) {
1526 dns_dumpctx_t *dctx = data;
1527
1528 if (result == ISC_R_SUCCESS && dctx->result != ISC_R_SUCCESS) {
1529 result = dctx->result;
1530 }
1531
1532 (dctx->done)(dctx->done_arg, result);
1533 dns_dumpctx_detach(&dctx);
1534 }
1535
1536 /*
1537 * This must be run from a network/task manager thread.
1538 */
1539 static void
1540 setup_dump(isc_task_t *task, isc_event_t *event) {
1541 dns_dumpctx_t *dctx = NULL;
1542
1543 REQUIRE(isc_nm_tid() >= 0);
1544 REQUIRE(event != NULL);
1545
1546 dctx = event->ev_arg;
1547
1548 REQUIRE(DNS_DCTX_VALID(dctx));
1549
1550 isc_nm_work_offload(isc_task_getnetmgr(task), master_dump_cb,
1551 master_dump_done_cb, dctx);
1552
1553 isc_event_free(&event);
1554 }
1555
1556 static isc_result_t
1557 task_send(dns_dumpctx_t *dctx) {
1558 isc_event_t *event;
1559
1560 event = isc_event_allocate(dctx->mctx, NULL, DNS_EVENT_DUMPQUANTUM,
1561 setup_dump, dctx, sizeof(*event));
1562 isc_task_send(dctx->task, &event);
1563 return (ISC_R_SUCCESS);
1564 }
1565
1566 static isc_result_t
1567 dumpctx_create(isc_mem_t *mctx, dns_db_t *db, dns_dbversion_t *version,
1568 const dns_master_style_t *style, FILE *f, dns_dumpctx_t **dctxp,
1569 dns_masterformat_t format, dns_masterrawheader_t *header) {
1570 dns_dumpctx_t *dctx;
1571 isc_result_t result;
1572 unsigned int options;
1573
1574 dctx = isc_mem_get(mctx, sizeof(*dctx));
1575
1576 dctx->mctx = NULL;
1577 dctx->f = f;
1578 dctx->dbiter = NULL;
1579 dctx->db = NULL;
1580 dctx->version = NULL;
1581 dctx->done = NULL;
1582 dctx->done_arg = NULL;
1583 dctx->task = NULL;
1584 atomic_init(&dctx->canceled, false);
1585 dctx->file = NULL;
1586 dctx->tmpfile = NULL;
1587 dctx->format = format;
1588 if (header == NULL) {
1589 dns_master_initrawheader(&dctx->header);
1590 } else {
1591 dctx->header = *header;
1592 }
1593
1594 switch (format) {
1595 case dns_masterformat_text:
1596 dctx->dumpsets = dump_rdatasets_text;
1597 break;
1598 case dns_masterformat_raw:
1599 dctx->dumpsets = dump_rdatasets_raw;
1600 break;
1601 case dns_masterformat_map:
1602 dctx->dumpsets = dump_rdatasets_map;
1603 break;
1604 default:
1605 UNREACHABLE();
1606 }
1607
1608 result = totext_ctx_init(style, NULL, &dctx->tctx);
1609 if (result != ISC_R_SUCCESS) {
1610 UNEXPECTED_ERROR(__FILE__, __LINE__,
1611 "could not set master file style");
1612 goto cleanup;
1613 }
1614
1615 isc_stdtime_get(&dctx->now);
1616 dns_db_attach(db, &dctx->db);
1617
1618 dctx->do_date = dns_db_iscache(dctx->db);
1619 if (dctx->do_date) {
1620 (void)dns_db_getservestalettl(dctx->db,
1621 &dctx->tctx.serve_stale_ttl);
1622 }
1623
1624 if (dctx->format == dns_masterformat_text &&
1625 (dctx->tctx.style.flags & DNS_STYLEFLAG_REL_OWNER) != 0)
1626 {
1627 options = DNS_DB_RELATIVENAMES;
1628 } else {
1629 options = 0;
1630 }
1631 result = dns_db_createiterator(dctx->db, options, &dctx->dbiter);
1632 if (result != ISC_R_SUCCESS) {
1633 goto cleanup;
1634 }
1635
1636 isc_mutex_init(&dctx->lock);
1637
1638 if (version != NULL) {
1639 dns_db_attachversion(dctx->db, version, &dctx->version);
1640 } else if (!dns_db_iscache(db)) {
1641 dns_db_currentversion(dctx->db, &dctx->version);
1642 }
1643 isc_mem_attach(mctx, &dctx->mctx);
1644
1645 isc_refcount_init(&dctx->references, 1);
1646 dctx->magic = DNS_DCTX_MAGIC;
1647 *dctxp = dctx;
1648 return (ISC_R_SUCCESS);
1649
1650 cleanup:
1651 if (dctx->dbiter != NULL) {
1652 dns_dbiterator_destroy(&dctx->dbiter);
1653 }
1654 if (dctx->db != NULL) {
1655 dns_db_detach(&dctx->db);
1656 }
1657 isc_mem_put(mctx, dctx, sizeof(*dctx));
1658 return (result);
1659 }
1660
1661 static isc_result_t
1662 writeheader(dns_dumpctx_t *dctx) {
1663 isc_result_t result = ISC_R_SUCCESS;
1664 isc_buffer_t buffer;
1665 char *bufmem;
1666 isc_region_t r;
1667 dns_masterrawheader_t rawheader;
1668 uint32_t rawversion, now32;
1669
1670 bufmem = isc_mem_get(dctx->mctx, initial_buffer_length);
1671
1672 isc_buffer_init(&buffer, bufmem, initial_buffer_length);
1673
1674 switch (dctx->format) {
1675 case dns_masterformat_text:
1676 /*
1677 * If the database has cache semantics, output an
1678 * RFC2540 $DATE directive so that the TTLs can be
1679 * adjusted when it is reloaded. For zones it is not
1680 * really needed, and it would make the file
1681 * incompatible with pre-RFC2540 software, so we omit
1682 * it in the zone case.
1683 */
1684 if (dctx->do_date) {
1685 fprintf(dctx->f, "; using a %u second stale ttl\n",
1686 dctx->tctx.serve_stale_ttl);
1687 result = dns_time32_totext(dctx->now, &buffer);
1688 RUNTIME_CHECK(result == ISC_R_SUCCESS);
1689 isc_buffer_usedregion(&buffer, &r);
1690 fprintf(dctx->f, "$DATE %.*s\n", (int)r.length,
1691 (char *)r.base);
1692 }
1693 break;
1694 case dns_masterformat_raw:
1695 case dns_masterformat_map:
1696 r.base = (unsigned char *)&rawheader;
1697 r.length = sizeof(rawheader);
1698 isc_buffer_region(&buffer, &r);
1699 now32 = dctx->now;
1700 rawversion = 1;
1701 if ((dctx->header.flags & DNS_MASTERRAW_COMPAT) != 0) {
1702 rawversion = 0;
1703 }
1704
1705 isc_buffer_putuint32(&buffer, dctx->format);
1706 isc_buffer_putuint32(&buffer, rawversion);
1707 isc_buffer_putuint32(&buffer, now32);
1708
1709 if (rawversion == 1) {
1710 isc_buffer_putuint32(&buffer, dctx->header.flags);
1711 isc_buffer_putuint32(&buffer,
1712 dctx->header.sourceserial);
1713 isc_buffer_putuint32(&buffer, dctx->header.lastxfrin);
1714 }
1715
1716 INSIST(isc_buffer_usedlength(&buffer) <= sizeof(rawheader));
1717 result = isc_stdio_write(buffer.base, 1,
1718 isc_buffer_usedlength(&buffer),
1719 dctx->f, NULL);
1720 if (result != ISC_R_SUCCESS) {
1721 break;
1722 }
1723
1724 break;
1725 default:
1726 UNREACHABLE();
1727 }
1728
1729 isc_mem_put(dctx->mctx, buffer.base, buffer.length);
1730 return (result);
1731 }
1732
1733 static isc_result_t
1734 dumptostream(dns_dumpctx_t *dctx) {
1735 isc_result_t result = ISC_R_SUCCESS;
1736 isc_buffer_t buffer;
1737 char *bufmem;
1738 dns_name_t *name;
1739 dns_fixedname_t fixname;
1740
1741 bufmem = isc_mem_get(dctx->mctx, initial_buffer_length);
1742
1743 isc_buffer_init(&buffer, bufmem, initial_buffer_length);
1744
1745 name = dns_fixedname_initname(&fixname);
1746
1747 CHECK(writeheader(dctx));
1748
1749 /*
1750 * Fast format is not currently written incrementally,
1751 * so we make the call to dns_db_serialize() here.
1752 * If the database is anything other than an rbtdb,
1753 * this should result in not implemented
1754 */
1755 if (dctx->format == dns_masterformat_map) {
1756 result = dns_db_serialize(dctx->db, dctx->version, dctx->f);
1757 goto cleanup;
1758 }
1759
1760 result = dns_dbiterator_first(dctx->dbiter);
1761 if (result != ISC_R_SUCCESS && result != ISC_R_NOMORE) {
1762 goto cleanup;
1763 }
1764
1765 while (result == ISC_R_SUCCESS) {
1766 dns_rdatasetiter_t *rdsiter = NULL;
1767 dns_dbnode_t *node = NULL;
1768
1769 result = dns_dbiterator_current(dctx->dbiter, &node, name);
1770 if (result != ISC_R_SUCCESS && result != DNS_R_NEWORIGIN) {
1771 break;
1772 }
1773 if (result == DNS_R_NEWORIGIN) {
1774 dns_name_t *origin =
1775 dns_fixedname_name(&dctx->tctx.origin_fixname);
1776 result = dns_dbiterator_origin(dctx->dbiter, origin);
1777 RUNTIME_CHECK(result == ISC_R_SUCCESS);
1778 if ((dctx->tctx.style.flags & DNS_STYLEFLAG_REL_DATA) !=
1779 0) {
1780 dctx->tctx.origin = origin;
1781 }
1782 dctx->tctx.neworigin = origin;
1783 }
1784
1785 result = dns_dbiterator_pause(dctx->dbiter);
1786 RUNTIME_CHECK(result == ISC_R_SUCCESS);
1787
1788 result = dns_db_allrdatasets(dctx->db, node, dctx->version,
1789 dctx->now, &rdsiter);
1790 if (result != ISC_R_SUCCESS) {
1791 dns_db_detachnode(dctx->db, &node);
1792 goto cleanup;
1793 }
1794 result = (dctx->dumpsets)(dctx->mctx, name, rdsiter,
1795 &dctx->tctx, &buffer, dctx->f);
1796 dns_rdatasetiter_destroy(&rdsiter);
1797 if (result != ISC_R_SUCCESS) {
1798 dns_db_detachnode(dctx->db, &node);
1799 goto cleanup;
1800 }
1801 dns_db_detachnode(dctx->db, &node);
1802 result = dns_dbiterator_next(dctx->dbiter);
1803 }
1804
1805 if (result == ISC_R_NOMORE) {
1806 result = ISC_R_SUCCESS;
1807 }
1808 cleanup:
1809 RUNTIME_CHECK(dns_dbiterator_pause(dctx->dbiter) == ISC_R_SUCCESS);
1810 isc_mem_put(dctx->mctx, buffer.base, buffer.length);
1811 return (result);
1812 }
1813
1814 isc_result_t
1815 dns_master_dumptostreamasync(isc_mem_t *mctx, dns_db_t *db,
1816 dns_dbversion_t *version,
1817 const dns_master_style_t *style, FILE *f,
1818 isc_task_t *task, dns_dumpdonefunc_t done,
1819 void *done_arg, dns_dumpctx_t **dctxp) {
1820 dns_dumpctx_t *dctx = NULL;
1821 isc_result_t result;
1822
1823 REQUIRE(task != NULL);
1824 REQUIRE(f != NULL);
1825 REQUIRE(done != NULL);
1826
1827 result = dumpctx_create(mctx, db, version, style, f, &dctx,
1828 dns_masterformat_text, NULL);
1829 if (result != ISC_R_SUCCESS) {
1830 return (result);
1831 }
1832 isc_task_attach(task, &dctx->task);
1833 dctx->done = done;
1834 dctx->done_arg = done_arg;
1835
1836 result = task_send(dctx);
1837 if (result == ISC_R_SUCCESS) {
1838 dns_dumpctx_attach(dctx, dctxp);
1839 return (DNS_R_CONTINUE);
1840 }
1841
1842 dns_dumpctx_detach(&dctx);
1843 return (result);
1844 }
1845
1846 isc_result_t
1847 dns_master_dumptostream(isc_mem_t *mctx, dns_db_t *db, dns_dbversion_t *version,
1848 const dns_master_style_t *style,
1849 dns_masterformat_t format,
1850 dns_masterrawheader_t *header, FILE *f) {
1851 dns_dumpctx_t *dctx = NULL;
1852 isc_result_t result;
1853
1854 result = dumpctx_create(mctx, db, version, style, f, &dctx, format,
1855 header);
1856 if (result != ISC_R_SUCCESS) {
1857 return (result);
1858 }
1859
1860 result = dumptostream(dctx);
1861 INSIST(result != DNS_R_CONTINUE);
1862 dns_dumpctx_detach(&dctx);
1863
1864 result = flushandsync(f, result, NULL);
1865 return (result);
1866 }
1867
1868 static isc_result_t
1869 opentmp(isc_mem_t *mctx, dns_masterformat_t format, const char *file,
1870 char **tempp, FILE **fp) {
1871 FILE *f = NULL;
1872 isc_result_t result;
1873 char *tempname = NULL;
1874 int tempnamelen;
1875
1876 tempnamelen = strlen(file) + 20;
1877 tempname = isc_mem_allocate(mctx, tempnamelen);
1878
1879 result = isc_file_mktemplate(file, tempname, tempnamelen);
1880 if (result != ISC_R_SUCCESS) {
1881 goto cleanup;
1882 }
1883
1884 if (format == dns_masterformat_text) {
1885 result = isc_file_openunique(tempname, &f);
1886 } else {
1887 result = isc_file_bopenunique(tempname, &f);
1888 }
1889 if (result != ISC_R_SUCCESS) {
1890 isc_log_write(dns_lctx, ISC_LOGCATEGORY_GENERAL,
1891 DNS_LOGMODULE_MASTERDUMP, ISC_LOG_ERROR,
1892 "dumping master file: %s: open: %s", tempname,
1893 isc_result_totext(result));
1894 goto cleanup;
1895 }
1896
1897 #if defined(POSIX_FADV_DONTNEED)
1898 posix_fadvise(fileno(f), 0, 0, POSIX_FADV_DONTNEED);
1899 #endif
1900
1901 *tempp = tempname;
1902 *fp = f;
1903 return (ISC_R_SUCCESS);
1904
1905 cleanup:
1906 isc_mem_free(mctx, tempname);
1907 return (result);
1908 }
1909
1910 isc_result_t
1911 dns_master_dumpasync(isc_mem_t *mctx, dns_db_t *db, dns_dbversion_t *version,
1912 const dns_master_style_t *style, const char *filename,
1913 isc_task_t *task, dns_dumpdonefunc_t done, void *done_arg,
1914 dns_dumpctx_t **dctxp, dns_masterformat_t format,
1915 dns_masterrawheader_t *header) {
1916 FILE *f = NULL;
1917 isc_result_t result;
1918 char *tempname = NULL;
1919 char *file = NULL;
1920 dns_dumpctx_t *dctx = NULL;
1921
1922 file = isc_mem_strdup(mctx, filename);
1923
1924 result = opentmp(mctx, format, filename, &tempname, &f);
1925 if (result != ISC_R_SUCCESS) {
1926 goto cleanup;
1927 }
1928
1929 result = dumpctx_create(mctx, db, version, style, f, &dctx, format,
1930 header);
1931 if (result != ISC_R_SUCCESS) {
1932 (void)isc_stdio_close(f);
1933 (void)isc_file_remove(tempname);
1934 goto cleanup;
1935 }
1936
1937 isc_task_attach(task, &dctx->task);
1938 dctx->done = done;
1939 dctx->done_arg = done_arg;
1940 dctx->file = file;
1941 file = NULL;
1942 dctx->tmpfile = tempname;
1943 tempname = NULL;
1944
1945 result = task_send(dctx);
1946 if (result == ISC_R_SUCCESS) {
1947 dns_dumpctx_attach(dctx, dctxp);
1948 return (DNS_R_CONTINUE);
1949 }
1950
1951 cleanup:
1952 if (dctx != NULL) {
1953 dns_dumpctx_detach(&dctx);
1954 }
1955 if (file != NULL) {
1956 isc_mem_free(mctx, file);
1957 }
1958 if (tempname != NULL) {
1959 isc_mem_free(mctx, tempname);
1960 }
1961 return (result);
1962 }
1963
1964 isc_result_t
1965 dns_master_dump(isc_mem_t *mctx, dns_db_t *db, dns_dbversion_t *version,
1966 const dns_master_style_t *style, const char *filename,
1967 dns_masterformat_t format, dns_masterrawheader_t *header) {
1968 FILE *f = NULL;
1969 isc_result_t result;
1970 char *tempname;
1971 dns_dumpctx_t *dctx = NULL;
1972
1973 result = opentmp(mctx, format, filename, &tempname, &f);
1974 if (result != ISC_R_SUCCESS) {
1975 return (result);
1976 }
1977
1978 result = dumpctx_create(mctx, db, version, style, f, &dctx, format,
1979 header);
1980 if (result != ISC_R_SUCCESS) {
1981 goto cleanup;
1982 }
1983
1984 result = dumptostream(dctx);
1985 INSIST(result != DNS_R_CONTINUE);
1986 dns_dumpctx_detach(&dctx);
1987
1988 result = closeandrename(f, result, tempname, filename);
1989
1990 cleanup:
1991 isc_mem_free(mctx, tempname);
1992 return (result);
1993 }
1994
1995 /*
1996 * Dump a database node into a master file.
1997 * XXX: this function assumes the text format.
1998 */
1999 isc_result_t
2000 dns_master_dumpnodetostream(isc_mem_t *mctx, dns_db_t *db,
2001 dns_dbversion_t *version, dns_dbnode_t *node,
2002 const dns_name_t *name,
2003 const dns_master_style_t *style, FILE *f) {
2004 isc_result_t result;
2005 isc_buffer_t buffer;
2006 char *bufmem;
2007 isc_stdtime_t now;
2008 dns_totext_ctx_t ctx;
2009 dns_rdatasetiter_t *rdsiter = NULL;
2010
2011 result = totext_ctx_init(style, NULL, &ctx);
2012 if (result != ISC_R_SUCCESS) {
2013 UNEXPECTED_ERROR(__FILE__, __LINE__,
2014 "could not set master file style");
2015 return (ISC_R_UNEXPECTED);
2016 }
2017
2018 isc_stdtime_get(&now);
2019
2020 bufmem = isc_mem_get(mctx, initial_buffer_length);
2021
2022 isc_buffer_init(&buffer, bufmem, initial_buffer_length);
2023
2024 result = dns_db_allrdatasets(db, node, version, now, &rdsiter);
2025 if (result != ISC_R_SUCCESS) {
2026 goto failure;
2027 }
2028 result = dump_rdatasets_text(mctx, name, rdsiter, &ctx, &buffer, f);
2029 if (result != ISC_R_SUCCESS) {
2030 goto failure;
2031 }
2032 dns_rdatasetiter_destroy(&rdsiter);
2033
2034 result = ISC_R_SUCCESS;
2035
2036 failure:
2037 isc_mem_put(mctx, buffer.base, buffer.length);
2038 return (result);
2039 }
2040
2041 isc_result_t
2042 dns_master_dumpnode(isc_mem_t *mctx, dns_db_t *db, dns_dbversion_t *version,
2043 dns_dbnode_t *node, const dns_name_t *name,
2044 const dns_master_style_t *style, const char *filename) {
2045 FILE *f = NULL;
2046 isc_result_t result;
2047
2048 result = isc_stdio_open(filename, "w", &f);
2049 if (result != ISC_R_SUCCESS) {
2050 isc_log_write(dns_lctx, ISC_LOGCATEGORY_GENERAL,
2051 DNS_LOGMODULE_MASTERDUMP, ISC_LOG_ERROR,
2052 "dumping node to file: %s: open: %s", filename,
2053 isc_result_totext(result));
2054 return (ISC_R_UNEXPECTED);
2055 }
2056
2057 result = dns_master_dumpnodetostream(mctx, db, version, node, name,
2058 style, f);
2059 if (result != ISC_R_SUCCESS) {
2060 isc_log_write(dns_lctx, ISC_LOGCATEGORY_GENERAL,
2061 DNS_LOGMODULE_MASTERDUMP, ISC_LOG_ERROR,
2062 "dumping master file: %s: dump: %s", filename,
2063 isc_result_totext(result));
2064 (void)isc_stdio_close(f);
2065 return (ISC_R_UNEXPECTED);
2066 }
2067
2068 result = isc_stdio_close(f);
2069 if (result != ISC_R_SUCCESS) {
2070 isc_log_write(dns_lctx, ISC_LOGCATEGORY_GENERAL,
2071 DNS_LOGMODULE_MASTERDUMP, ISC_LOG_ERROR,
2072 "dumping master file: %s: close: %s", filename,
2073 isc_result_totext(result));
2074 return (ISC_R_UNEXPECTED);
2075 }
2076
2077 return (result);
2078 }
2079
2080 dns_masterstyle_flags_t
2081 dns_master_styleflags(const dns_master_style_t *style) {
2082 REQUIRE(style != NULL);
2083 return (style->flags);
2084 }
2085
2086 isc_result_t
2087 dns_master_stylecreate(dns_master_style_t **stylep,
2088 dns_masterstyle_flags_t flags, unsigned int ttl_column,
2089 unsigned int class_column, unsigned int type_column,
2090 unsigned int rdata_column, unsigned int line_length,
2091 unsigned int tab_width, unsigned int split_width,
2092 isc_mem_t *mctx) {
2093 dns_master_style_t *style;
2094
2095 REQUIRE(stylep != NULL && *stylep == NULL);
2096 style = isc_mem_get(mctx, sizeof(*style));
2097
2098 style->flags = flags;
2099 style->ttl_column = ttl_column;
2100 style->class_column = class_column;
2101 style->type_column = type_column;
2102 style->rdata_column = rdata_column;
2103 style->line_length = line_length;
2104 style->tab_width = tab_width;
2105 style->split_width = split_width;
2106 *stylep = style;
2107 return (ISC_R_SUCCESS);
2108 }
2109
2110 void
2111 dns_master_styledestroy(dns_master_style_t **stylep, isc_mem_t *mctx) {
2112 dns_master_style_t *style;
2113
2114 REQUIRE(stylep != NULL && *stylep != NULL);
2115 style = *stylep;
2116 *stylep = NULL;
2117 isc_mem_put(mctx, style, sizeof(*style));
2118 }
2119