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