masterdump.c revision 1.4.4.2 1 /* $NetBSD: masterdump.c,v 1.4.4.2 2024/02/29 12:34:32 martin Exp $ */
2
3 /*
4 * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
5 *
6 * SPDX-License-Identifier: MPL-2.0
7 *
8 * This Source Code Form is subject to the terms of the Mozilla Public
9 * License, v. 2.0. If a copy of the MPL was not distributed with this
10 * file, you can obtain one at https://mozilla.org/MPL/2.0/.
11 *
12 * See the COPYRIGHT file distributed with this work for additional
13 * information regarding copyright ownership.
14 */
15
16 /*! \file */
17
18 #include <inttypes.h>
19 #include <stdbool.h>
20 #include <stdlib.h>
21
22 #include <isc/atomic.h>
23 #include <isc/buffer.h>
24 #include <isc/event.h>
25 #include <isc/file.h>
26 #include <isc/magic.h>
27 #include <isc/mem.h>
28 #include <isc/print.h>
29 #include <isc/refcount.h>
30 #include <isc/result.h>
31 #include <isc/stdio.h>
32 #include <isc/string.h>
33 #include <isc/task.h>
34 #include <isc/time.h>
35 #include <isc/types.h>
36 #include <isc/util.h>
37
38 #include <dns/db.h>
39 #include <dns/dbiterator.h>
40 #include <dns/events.h>
41 #include <dns/fixedname.h>
42 #include <dns/log.h>
43 #include <dns/master.h>
44 #include <dns/masterdump.h>
45 #include <dns/ncache.h>
46 #include <dns/rdata.h>
47 #include <dns/rdataclass.h>
48 #include <dns/rdataset.h>
49 #include <dns/rdatasetiter.h>
50 #include <dns/rdatatype.h>
51 #include <dns/time.h>
52 #include <dns/ttl.h>
53
54 #define DNS_DCTX_MAGIC ISC_MAGIC('D', 'c', 't', 'x')
55 #define DNS_DCTX_VALID(d) ISC_MAGIC_VALID(d, DNS_DCTX_MAGIC)
56
57 #define RETERR(x) \
58 do { \
59 isc_result_t _r = (x); \
60 if (_r != ISC_R_SUCCESS) \
61 return ((_r)); \
62 } while (0)
63
64 #define CHECK(x) \
65 do { \
66 if ((x) != ISC_R_SUCCESS) \
67 goto cleanup; \
68 } while (0)
69
70 struct dns_master_style {
71 dns_masterstyle_flags_t flags; /* DNS_STYLEFLAG_* */
72 unsigned int ttl_column;
73 unsigned int class_column;
74 unsigned int type_column;
75 unsigned int rdata_column;
76 unsigned int line_length;
77 unsigned int tab_width;
78 unsigned int split_width;
79 };
80
81 /*%
82 * The maximum length of the newline+indentation that is output
83 * when inserting a line break in an RR. This effectively puts an
84 * upper limits on the value of "rdata_column", because if it is
85 * very large, the tabs and spaces needed to reach it will not fit.
86 */
87 #define DNS_TOTEXT_LINEBREAK_MAXLEN 100
88
89 /*% Does the rdataset 'r' contain a stale answer? */
90 #define STALE(r) (((r)->attributes & DNS_RDATASETATTR_STALE) != 0)
91 /*% Does the rdataset 'r' contain an expired answer? */
92 #define ANCIENT(r) (((r)->attributes & DNS_RDATASETATTR_ANCIENT) != 0)
93
94 /*%
95 * Context structure for a masterfile dump in progress.
96 */
97 typedef struct dns_totext_ctx {
98 dns_master_style_t style;
99 bool class_printed;
100 char *linebreak;
101 char linebreak_buf[DNS_TOTEXT_LINEBREAK_MAXLEN];
102 dns_name_t *origin;
103 dns_name_t *neworigin;
104 dns_fixedname_t origin_fixname;
105 uint32_t current_ttl;
106 bool current_ttl_valid;
107 dns_ttl_t serve_stale_ttl;
108 dns_indent_t indent;
109 } dns_totext_ctx_t;
110
111 const dns_master_style_t dns_master_style_keyzone = {
112 DNS_STYLEFLAG_OMIT_OWNER | DNS_STYLEFLAG_OMIT_CLASS |
113 DNS_STYLEFLAG_REL_OWNER | DNS_STYLEFLAG_REL_DATA |
114 DNS_STYLEFLAG_OMIT_TTL | DNS_STYLEFLAG_TTL |
115 DNS_STYLEFLAG_COMMENT | DNS_STYLEFLAG_RRCOMMENT |
116 DNS_STYLEFLAG_MULTILINE | DNS_STYLEFLAG_KEYDATA,
117 24,
118 24,
119 24,
120 32,
121 80,
122 8,
123 UINT_MAX
124 };
125
126 const dns_master_style_t dns_master_style_default = {
127 DNS_STYLEFLAG_OMIT_OWNER | DNS_STYLEFLAG_OMIT_CLASS |
128 DNS_STYLEFLAG_REL_OWNER | DNS_STYLEFLAG_REL_DATA |
129 DNS_STYLEFLAG_OMIT_TTL | DNS_STYLEFLAG_TTL |
130 DNS_STYLEFLAG_COMMENT | DNS_STYLEFLAG_RRCOMMENT |
131 DNS_STYLEFLAG_MULTILINE,
132 24,
133 24,
134 24,
135 32,
136 80,
137 8,
138 UINT_MAX
139 };
140
141 const dns_master_style_t dns_master_style_full = {
142 DNS_STYLEFLAG_COMMENT | DNS_STYLEFLAG_RESIGN,
143 46,
144 46,
145 46,
146 64,
147 120,
148 8,
149 UINT_MAX
150 };
151
152 const dns_master_style_t dns_master_style_explicitttl = {
153 DNS_STYLEFLAG_OMIT_OWNER | DNS_STYLEFLAG_OMIT_CLASS |
154 DNS_STYLEFLAG_REL_OWNER | DNS_STYLEFLAG_REL_DATA |
155 DNS_STYLEFLAG_COMMENT | DNS_STYLEFLAG_RRCOMMENT |
156 DNS_STYLEFLAG_MULTILINE,
157 24,
158 32,
159 32,
160 40,
161 80,
162 8,
163 UINT_MAX
164 };
165
166 const dns_master_style_t dns_master_style_cache = {
167 DNS_STYLEFLAG_OMIT_OWNER | DNS_STYLEFLAG_OMIT_CLASS |
168 DNS_STYLEFLAG_MULTILINE | DNS_STYLEFLAG_RRCOMMENT |
169 DNS_STYLEFLAG_TRUST | DNS_STYLEFLAG_NCACHE,
170 24,
171 32,
172 32,
173 40,
174 80,
175 8,
176 UINT_MAX
177 };
178
179 const dns_master_style_t dns_master_style_cache_with_expired = {
180 DNS_STYLEFLAG_OMIT_OWNER | DNS_STYLEFLAG_OMIT_CLASS |
181 DNS_STYLEFLAG_MULTILINE | DNS_STYLEFLAG_RRCOMMENT |
182 DNS_STYLEFLAG_TRUST | DNS_STYLEFLAG_NCACHE |
183 DNS_STYLEFLAG_EXPIRED,
184 24,
185 32,
186 32,
187 40,
188 80,
189 8,
190 UINT_MAX
191 };
192
193 const dns_master_style_t dns_master_style_simple = { 0, 24, 32, 32,
194 40, 80, 8, UINT_MAX };
195
196 /*%
197 * A style suitable for dns_rdataset_totext().
198 */
199 const dns_master_style_t dns_master_style_debug = {
200 DNS_STYLEFLAG_REL_OWNER, 24, 32, 40, 48, 80, 8, UINT_MAX
201 };
202
203 /*%
204 * Similar, but indented (i.e., prepended with indentctx.string).
205 */
206 const dns_master_style_t dns_master_style_indent = {
207 DNS_STYLEFLAG_REL_OWNER | DNS_STYLEFLAG_INDENT,
208 24,
209 32,
210 40,
211 48,
212 80,
213 8,
214 UINT_MAX
215 };
216
217 /*%
218 * Similar, but with each line commented out.
219 */
220 const dns_master_style_t dns_master_style_comment = {
221 DNS_STYLEFLAG_REL_OWNER | DNS_STYLEFLAG_MULTILINE |
222 DNS_STYLEFLAG_RRCOMMENT | DNS_STYLEFLAG_COMMENTDATA,
223 24,
224 32,
225 40,
226 48,
227 80,
228 8,
229 UINT_MAX
230 };
231
232 /*%
233 * YAML style
234 */
235 const dns_master_style_t dns_master_style_yaml = {
236 DNS_STYLEFLAG_YAML | DNS_STYLEFLAG_REL_OWNER | DNS_STYLEFLAG_INDENT,
237 24,
238 32,
239 40,
240 48,
241 80,
242 8,
243 UINT_MAX
244 };
245
246 #define N_SPACES 10
247 static char spaces[N_SPACES + 1] = " ";
248
249 #define N_TABS 10
250 static char tabs[N_TABS + 1] = "\t\t\t\t\t\t\t\t\t\t";
251
252 struct dns_dumpctx {
253 unsigned int magic;
254 isc_mem_t *mctx;
255 isc_mutex_t lock;
256 isc_refcount_t references;
257 atomic_bool canceled;
258 bool do_date;
259 isc_stdtime_t now;
260 FILE *f;
261 dns_db_t *db;
262 dns_dbversion_t *version;
263 dns_dbiterator_t *dbiter;
264 dns_totext_ctx_t tctx;
265 isc_task_t *task;
266 dns_dumpdonefunc_t done;
267 void *done_arg;
268 /* dns_master_dumpasync() */
269 isc_result_t result;
270 char *file;
271 char *tmpfile;
272 dns_masterformat_t format;
273 dns_masterrawheader_t header;
274 isc_result_t (*dumpsets)(isc_mem_t *mctx, const dns_name_t *name,
275 dns_rdatasetiter_t *rdsiter,
276 dns_totext_ctx_t *ctx, isc_buffer_t *buffer,
277 FILE *f);
278 };
279
280 #define NXDOMAIN(x) (((x)->attributes & DNS_RDATASETATTR_NXDOMAIN) != 0)
281
282 static const dns_indent_t default_indent = { "\t", 1 };
283 static const dns_indent_t default_yamlindent = { " ", 1 };
284
285 /*%
286 * Output tabs and spaces to go from column '*current' to
287 * column 'to', and update '*current' to reflect the new
288 * current column.
289 */
290 static isc_result_t
291 indent(unsigned int *current, unsigned int to, int tabwidth,
292 isc_buffer_t *target) {
293 isc_region_t r;
294 unsigned char *p;
295 unsigned int from;
296 int ntabs, nspaces, t;
297
298 from = *current;
299
300 if (to < from + 1) {
301 to = from + 1;
302 }
303
304 ntabs = to / tabwidth - from / tabwidth;
305 if (ntabs < 0) {
306 ntabs = 0;
307 }
308
309 if (ntabs > 0) {
310 isc_buffer_availableregion(target, &r);
311 if (r.length < (unsigned)ntabs) {
312 return (ISC_R_NOSPACE);
313 }
314 p = r.base;
315
316 t = ntabs;
317 while (t) {
318 int n = t;
319 if (n > N_TABS) {
320 n = N_TABS;
321 }
322 memmove(p, tabs, n);
323 p += n;
324 t -= n;
325 }
326 isc_buffer_add(target, ntabs);
327 from = (to / tabwidth) * tabwidth;
328 }
329
330 nspaces = to - from;
331 INSIST(nspaces >= 0);
332
333 isc_buffer_availableregion(target, &r);
334 if (r.length < (unsigned)nspaces) {
335 return (ISC_R_NOSPACE);
336 }
337 p = r.base;
338
339 t = nspaces;
340 while (t) {
341 int n = t;
342 if (n > N_SPACES) {
343 n = N_SPACES;
344 }
345 memmove(p, spaces, n);
346 p += n;
347 t -= n;
348 }
349 isc_buffer_add(target, nspaces);
350
351 *current = to;
352 return (ISC_R_SUCCESS);
353 }
354
355 static isc_result_t
356 totext_ctx_init(const dns_master_style_t *style, const dns_indent_t *indentctx,
357 dns_totext_ctx_t *ctx) {
358 isc_result_t result;
359
360 REQUIRE(style->tab_width != 0);
361
362 if (indentctx == NULL) {
363 if ((style->flags & DNS_STYLEFLAG_YAML) != 0) {
364 indentctx = &default_yamlindent;
365 } else {
366 indentctx = &default_indent;
367 }
368 }
369
370 ctx->style = *style;
371 ctx->class_printed = false;
372
373 dns_fixedname_init(&ctx->origin_fixname);
374
375 /*
376 * Set up the line break string if needed.
377 */
378 if ((ctx->style.flags & DNS_STYLEFLAG_MULTILINE) != 0) {
379 isc_buffer_t buf;
380 isc_region_t r;
381 unsigned int col = 0;
382
383 isc_buffer_init(&buf, ctx->linebreak_buf,
384 sizeof(ctx->linebreak_buf));
385
386 isc_buffer_availableregion(&buf, &r);
387 if (r.length < 1) {
388 return (DNS_R_TEXTTOOLONG);
389 }
390 r.base[0] = '\n';
391 isc_buffer_add(&buf, 1);
392
393 if ((ctx->style.flags & DNS_STYLEFLAG_INDENT) != 0 ||
394 (ctx->style.flags & DNS_STYLEFLAG_YAML) != 0)
395 {
396 unsigned int i, len = strlen(indentctx->string);
397 for (i = 0; i < indentctx->count; i++) {
398 if (isc_buffer_availablelength(&buf) < len) {
399 return (DNS_R_TEXTTOOLONG);
400 }
401 isc_buffer_putstr(&buf, indentctx->string);
402 }
403 }
404
405 if ((ctx->style.flags & DNS_STYLEFLAG_COMMENTDATA) != 0) {
406 isc_buffer_availableregion(&buf, &r);
407 if (r.length < 1) {
408 return (DNS_R_TEXTTOOLONG);
409 }
410 r.base[0] = ';';
411 isc_buffer_add(&buf, 1);
412 }
413
414 result = indent(&col, ctx->style.rdata_column,
415 ctx->style.tab_width, &buf);
416 /*
417 * Do not return ISC_R_NOSPACE if the line break string
418 * buffer is too small, because that would just make
419 * dump_rdataset() retry indefinitely with ever
420 * bigger target buffers. That's a different buffer,
421 * so it won't help. Use DNS_R_TEXTTOOLONG as a substitute.
422 */
423 if (result == ISC_R_NOSPACE) {
424 return (DNS_R_TEXTTOOLONG);
425 }
426 if (result != ISC_R_SUCCESS) {
427 return (result);
428 }
429
430 isc_buffer_availableregion(&buf, &r);
431 if (r.length < 1) {
432 return (DNS_R_TEXTTOOLONG);
433 }
434 r.base[0] = '\0';
435 isc_buffer_add(&buf, 1);
436 ctx->linebreak = ctx->linebreak_buf;
437 } else {
438 ctx->linebreak = NULL;
439 }
440
441 ctx->origin = NULL;
442 ctx->neworigin = NULL;
443 ctx->current_ttl = 0;
444 ctx->current_ttl_valid = false;
445 ctx->serve_stale_ttl = 0;
446 ctx->indent = *indentctx;
447
448 return (ISC_R_SUCCESS);
449 }
450
451 #define INDENT_TO(col) \
452 do { \
453 if ((ctx->style.flags & DNS_STYLEFLAG_YAML) != 0) { \
454 if ((result = str_totext(" ", target)) != \
455 ISC_R_SUCCESS) \
456 return ((result)); \
457 } else if ((result = indent(&column, ctx->style.col, \
458 ctx->style.tab_width, target)) != \
459 ISC_R_SUCCESS) \
460 return ((result)); \
461 } while (0)
462
463 static isc_result_t
464 str_totext(const char *source, isc_buffer_t *target) {
465 unsigned int l;
466 isc_region_t region;
467
468 isc_buffer_availableregion(target, ®ion);
469 l = strlen(source);
470
471 if (l > region.length) {
472 return (ISC_R_NOSPACE);
473 }
474
475 memmove(region.base, source, l);
476 isc_buffer_add(target, l);
477 return (ISC_R_SUCCESS);
478 }
479
480 static isc_result_t
481 ncache_summary(dns_rdataset_t *rdataset, bool omit_final_dot,
482 dns_totext_ctx_t *ctx, isc_buffer_t *target) {
483 isc_result_t result = ISC_R_SUCCESS;
484 dns_rdataset_t rds;
485 dns_name_t name;
486
487 dns_rdataset_init(&rds);
488 dns_name_init(&name, NULL);
489
490 do {
491 dns_ncache_current(rdataset, &name, &rds);
492 for (result = dns_rdataset_first(&rds); result == ISC_R_SUCCESS;
493 result = dns_rdataset_next(&rds))
494 {
495 if ((ctx->style.flags & DNS_STYLEFLAG_INDENT) != 0 ||
496 (ctx->style.flags & DNS_STYLEFLAG_YAML) != 0)
497 {
498 unsigned int i;
499 for (i = 0; i < ctx->indent.count; i++) {
500 CHECK(str_totext(ctx->indent.string,
501 target));
502 }
503 }
504
505 if ((ctx->style.flags & DNS_STYLEFLAG_YAML) != 0) {
506 CHECK(str_totext("- ", target));
507 } else {
508 CHECK(str_totext("; ", target));
509 }
510
511 CHECK(dns_name_totext(&name, omit_final_dot, target));
512 CHECK(str_totext(" ", target));
513 CHECK(dns_rdatatype_totext(rds.type, target));
514 if (rds.type == dns_rdatatype_rrsig) {
515 CHECK(str_totext(" ", target));
516 CHECK(dns_rdatatype_totext(rds.covers, target));
517 CHECK(str_totext(" ...\n", target));
518 } else {
519 dns_rdata_t rdata = DNS_RDATA_INIT;
520 dns_rdataset_current(&rds, &rdata);
521 CHECK(str_totext(" ", target));
522 CHECK(dns_rdata_tofmttext(&rdata, dns_rootname,
523 0, 0, 0, " ",
524 target));
525 CHECK(str_totext("\n", target));
526 }
527 }
528 dns_rdataset_disassociate(&rds);
529 result = dns_rdataset_next(rdataset);
530 } while (result == ISC_R_SUCCESS);
531
532 if (result == ISC_R_NOMORE) {
533 result = ISC_R_SUCCESS;
534 }
535 cleanup:
536 if (dns_rdataset_isassociated(&rds)) {
537 dns_rdataset_disassociate(&rds);
538 }
539
540 return (result);
541 }
542
543 /*
544 * Convert 'rdataset' to master file text format according to 'ctx',
545 * storing the result in 'target'. If 'owner_name' is NULL, it
546 * is omitted; otherwise 'owner_name' must be valid and have at least
547 * one label.
548 */
549
550 static isc_result_t
551 rdataset_totext(dns_rdataset_t *rdataset, const dns_name_t *owner_name,
552 dns_totext_ctx_t *ctx, bool omit_final_dot,
553 isc_buffer_t *target) {
554 isc_result_t result;
555 unsigned int column;
556 bool first = true;
557 uint32_t current_ttl;
558 bool current_ttl_valid;
559 dns_rdatatype_t type;
560 unsigned int type_start;
561 dns_fixedname_t fixed;
562 dns_name_t *name = NULL;
563 unsigned int i;
564
565 REQUIRE(DNS_RDATASET_VALID(rdataset));
566
567 rdataset->attributes |= DNS_RDATASETATTR_LOADORDER;
568 result = dns_rdataset_first(rdataset);
569
570 current_ttl = ctx->current_ttl;
571 current_ttl_valid = ctx->current_ttl_valid;
572
573 if (owner_name != NULL) {
574 name = dns_fixedname_initname(&fixed);
575 dns_name_copy(owner_name, name);
576 dns_rdataset_getownercase(rdataset, name);
577 }
578
579 while (result == ISC_R_SUCCESS) {
580 column = 0;
581
582 /*
583 * Indent?
584 */
585 if ((ctx->style.flags & DNS_STYLEFLAG_INDENT) != 0 ||
586 (ctx->style.flags & DNS_STYLEFLAG_YAML) != 0)
587 {
588 for (i = 0; i < ctx->indent.count; i++) {
589 RETERR(str_totext(ctx->indent.string, target));
590 }
591 }
592
593 /*
594 * YAML or comment prefix?
595 */
596 if ((ctx->style.flags & DNS_STYLEFLAG_YAML) != 0) {
597 RETERR(str_totext("- ", target));
598 } else if ((ctx->style.flags & DNS_STYLEFLAG_COMMENTDATA) != 0)
599 {
600 RETERR(str_totext(";", target));
601 }
602
603 /*
604 * Owner name.
605 */
606 if (name != NULL &&
607 !((ctx->style.flags & DNS_STYLEFLAG_OMIT_OWNER) != 0 &&
608 !first))
609 {
610 unsigned int name_start = target->used;
611 RETERR(dns_name_totext(name, omit_final_dot, target));
612 column += target->used - name_start;
613 }
614
615 /*
616 * TTL.
617 */
618 if ((ctx->style.flags & DNS_STYLEFLAG_NO_TTL) == 0 &&
619 !((ctx->style.flags & DNS_STYLEFLAG_OMIT_TTL) != 0 &&
620 current_ttl_valid && rdataset->ttl == current_ttl))
621 {
622 char ttlbuf[64];
623 isc_region_t r;
624 unsigned int length;
625
626 INDENT_TO(ttl_column);
627 if ((ctx->style.flags & DNS_STYLEFLAG_TTL_UNITS) != 0) {
628 length = target->used;
629 result = dns_ttl_totext(rdataset->ttl, false,
630 false, target);
631 if (result != ISC_R_SUCCESS) {
632 return (result);
633 }
634 column += target->used - length;
635 } else {
636 length = snprintf(ttlbuf, sizeof(ttlbuf), "%u",
637 rdataset->ttl);
638 INSIST(length <= sizeof(ttlbuf));
639 isc_buffer_availableregion(target, &r);
640 if (r.length < length) {
641 return (ISC_R_NOSPACE);
642 }
643 memmove(r.base, ttlbuf, length);
644 isc_buffer_add(target, length);
645 column += length;
646 }
647
648 /*
649 * If the $TTL directive is not in use, the TTL we
650 * just printed becomes the default for subsequent RRs.
651 */
652 if ((ctx->style.flags & DNS_STYLEFLAG_TTL) == 0) {
653 current_ttl = rdataset->ttl;
654 current_ttl_valid = true;
655 }
656 }
657
658 /*
659 * Class.
660 */
661 if ((ctx->style.flags & DNS_STYLEFLAG_NO_CLASS) == 0 &&
662 ((ctx->style.flags & DNS_STYLEFLAG_OMIT_CLASS) == 0 ||
663 !ctx->class_printed))
664 {
665 unsigned int class_start;
666 INDENT_TO(class_column);
667 class_start = target->used;
668 if ((ctx->style.flags & DNS_STYLEFLAG_UNKNOWNFORMAT) !=
669 0)
670 {
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 {
705 return (ISC_R_NOSPACE);
706 }
707 isc_buffer_putstr(target, KEYDATA);
708 break;
709 }
710 FALLTHROUGH;
711 default:
712 if ((ctx->style.flags & DNS_STYLEFLAG_UNKNOWNFORMAT) !=
713 0)
714 {
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("could not set master file style");
865 return (ISC_R_UNEXPECTED);
866 }
867
868 /*
869 * The caller might want to give us an empty owner
870 * name (e.g. if they are outputting into a master
871 * file and this rdataset has the same name as the
872 * previous one.)
873 */
874 if (dns_name_countlabels(owner_name) == 0) {
875 owner_name = NULL;
876 }
877
878 if (question) {
879 return (question_totext(rdataset, owner_name, &ctx,
880 omit_final_dot, target));
881 } else {
882 return (rdataset_totext(rdataset, owner_name, &ctx,
883 omit_final_dot, target));
884 }
885 }
886
887 isc_result_t
888 dns_master_rdatasettotext(const dns_name_t *owner_name,
889 dns_rdataset_t *rdataset,
890 const dns_master_style_t *style, dns_indent_t *indent,
891 isc_buffer_t *target) {
892 dns_totext_ctx_t ctx;
893 isc_result_t result;
894 result = totext_ctx_init(style, indent, &ctx);
895 if (result != ISC_R_SUCCESS) {
896 UNEXPECTED_ERROR("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("could not set master file style");
913 return (ISC_R_UNEXPECTED);
914 }
915
916 return (question_totext(rdataset, owner_name, &ctx, false, target));
917 }
918
919 /*
920 * Print an rdataset. 'buffer' is a scratch buffer, which must have been
921 * dynamically allocated by the caller. It must be large enough to
922 * hold the result from dns_ttl_totext(). If more than that is needed,
923 * the buffer will be grown automatically.
924 */
925
926 static isc_result_t
927 dump_rdataset(isc_mem_t *mctx, const dns_name_t *name, dns_rdataset_t *rdataset,
928 dns_totext_ctx_t *ctx, isc_buffer_t *buffer, FILE *f) {
929 isc_region_t r;
930 isc_result_t result;
931
932 REQUIRE(buffer->length > 0);
933
934 /*
935 * Output a $TTL directive if needed.
936 */
937
938 if ((ctx->style.flags & DNS_STYLEFLAG_TTL) != 0) {
939 if (!ctx->current_ttl_valid ||
940 ctx->current_ttl != rdataset->ttl)
941 {
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("master file write failed: %s",
989 isc_result_totext(result));
990 return (result);
991 }
992
993 return (ISC_R_SUCCESS);
994 }
995
996 /*
997 * Define the order in which rdatasets should be printed in zone
998 * files. We will print SOA and NS records before others, SIGs
999 * immediately following the things they sign, and order everything
1000 * else by RR number. This is all just for aesthetics and
1001 * compatibility with buggy software that expects the SOA to be first;
1002 * the DNS specifications allow any order.
1003 */
1004
1005 static int
1006 dump_order(const dns_rdataset_t *rds) {
1007 int t;
1008 int sig;
1009 if (rds->type == dns_rdatatype_rrsig) {
1010 t = rds->covers;
1011 sig = 1;
1012 } else {
1013 t = rds->type;
1014 sig = 0;
1015 }
1016 switch (t) {
1017 case dns_rdatatype_soa:
1018 t = 0;
1019 break;
1020 case dns_rdatatype_ns:
1021 t = 1;
1022 break;
1023 default:
1024 t += 2;
1025 break;
1026 }
1027 return ((t << 1) + sig);
1028 }
1029
1030 static int
1031 dump_order_compare(const void *a, const void *b) {
1032 return (dump_order(*((const dns_rdataset_t *const *)a)) -
1033 dump_order(*((const dns_rdataset_t *const *)b)));
1034 }
1035
1036 /*
1037 * Dump all the rdatasets of a domain name to a master file. We make
1038 * a "best effort" attempt to sort the RRsets in a nice order, but if
1039 * there are more than MAXSORT RRsets, we punt and only sort them in
1040 * groups of MAXSORT. This is not expected to ever happen in practice
1041 * since much less than 64 RR types have been registered with the
1042 * IANA, so far, and the output will be correct (though not
1043 * aesthetically pleasing) even if it does happen.
1044 */
1045
1046 #define MAXSORT 64
1047
1048 static isc_result_t
1049 dump_rdatasets_text(isc_mem_t *mctx, const dns_name_t *name,
1050 dns_rdatasetiter_t *rdsiter, dns_totext_ctx_t *ctx,
1051 isc_buffer_t *buffer, FILE *f) {
1052 isc_result_t itresult, dumpresult;
1053 isc_region_t r;
1054 dns_rdataset_t rdatasets[MAXSORT];
1055 dns_rdataset_t *sorted[MAXSORT];
1056 int i, n;
1057
1058 itresult = dns_rdatasetiter_first(rdsiter);
1059 dumpresult = ISC_R_SUCCESS;
1060
1061 if (itresult == ISC_R_SUCCESS && ctx->neworigin != NULL) {
1062 isc_buffer_clear(buffer);
1063 itresult = dns_name_totext(ctx->neworigin, false, buffer);
1064 RUNTIME_CHECK(itresult == ISC_R_SUCCESS);
1065 isc_buffer_usedregion(buffer, &r);
1066 fprintf(f, "$ORIGIN %.*s\n", (int)r.length, (char *)r.base);
1067 ctx->neworigin = NULL;
1068 }
1069
1070 again:
1071 for (i = 0; itresult == ISC_R_SUCCESS && i < MAXSORT;
1072 itresult = dns_rdatasetiter_next(rdsiter), i++)
1073 {
1074 dns_rdataset_init(&rdatasets[i]);
1075 dns_rdatasetiter_current(rdsiter, &rdatasets[i]);
1076 sorted[i] = &rdatasets[i];
1077 }
1078 n = i;
1079 INSIST(n <= MAXSORT);
1080
1081 qsort(sorted, n, sizeof(sorted[0]), dump_order_compare);
1082
1083 for (i = 0; i < n; i++) {
1084 dns_rdataset_t *rds = sorted[i];
1085
1086 if (ANCIENT(rds) &&
1087 (ctx->style.flags & DNS_STYLEFLAG_EXPIRED) == 0)
1088 {
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 {
1235 int newlength;
1236 void *newmem;
1237
1238 newlength = buffer->length * 2;
1239 newmem = isc_mem_get(mctx, newlength);
1240 isc_mem_put(mctx, buffer->base, buffer->length);
1241 isc_buffer_init(buffer, newmem, newlength);
1242 goto restart;
1243 }
1244 isc_buffer_putuint16(buffer, dlen);
1245 isc_buffer_copyregion(buffer, &r);
1246 totallen += sizeof(dlen) + r.length;
1247
1248 result = dns_rdataset_next(rdataset);
1249 } while (result == ISC_R_SUCCESS);
1250
1251 if (result != ISC_R_NOMORE) {
1252 return (result);
1253 }
1254
1255 /*
1256 * Fill in the total length field.
1257 * XXX: this is a bit tricky. Since we have already "used" the space
1258 * for the total length in the buffer, we first remember the entire
1259 * buffer length in the region, "rewind", and then write the value.
1260 */
1261 isc_buffer_usedregion(buffer, &r);
1262 isc_buffer_clear(buffer);
1263 isc_buffer_putuint32(buffer, totallen);
1264 INSIST(isc_buffer_usedlength(buffer) < totallen);
1265
1266 /*
1267 * Write the buffer contents to the raw master file.
1268 */
1269 result = isc_stdio_write(r.base, 1, (size_t)r.length, f, NULL);
1270
1271 if (result != ISC_R_SUCCESS) {
1272 UNEXPECTED_ERROR("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_copy(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 /*
1321 * Initial size of text conversion buffer. The buffer is used
1322 * for several purposes: converting origin names, rdatasets,
1323 * $DATE timestamps, and comment strings for $TTL directives.
1324 *
1325 * When converting rdatasets, it is dynamically resized, but
1326 * when converting origins, timestamps, etc it is not. Therefore,
1327 * the initial size must large enough to hold the longest possible
1328 * text representation of any domain name (for $ORIGIN).
1329 */
1330 static const int initial_buffer_length = 1200;
1331
1332 static isc_result_t
1333 dumptostream(dns_dumpctx_t *dctx);
1334
1335 static void
1336 dumpctx_destroy(dns_dumpctx_t *dctx) {
1337 dctx->magic = 0;
1338 isc_mutex_destroy(&dctx->lock);
1339 dns_dbiterator_destroy(&dctx->dbiter);
1340 if (dctx->version != NULL) {
1341 dns_db_closeversion(dctx->db, &dctx->version, false);
1342 }
1343 dns_db_detach(&dctx->db);
1344 if (dctx->task != NULL) {
1345 isc_task_detach(&dctx->task);
1346 }
1347 if (dctx->file != NULL) {
1348 isc_mem_free(dctx->mctx, dctx->file);
1349 }
1350 if (dctx->tmpfile != NULL) {
1351 isc_mem_free(dctx->mctx, dctx->tmpfile);
1352 }
1353 isc_mem_putanddetach(&dctx->mctx, dctx, sizeof(*dctx));
1354 }
1355
1356 void
1357 dns_dumpctx_attach(dns_dumpctx_t *source, dns_dumpctx_t **target) {
1358 REQUIRE(DNS_DCTX_VALID(source));
1359 REQUIRE(target != NULL && *target == NULL);
1360
1361 isc_refcount_increment(&source->references);
1362
1363 *target = source;
1364 }
1365
1366 void
1367 dns_dumpctx_detach(dns_dumpctx_t **dctxp) {
1368 dns_dumpctx_t *dctx;
1369
1370 REQUIRE(dctxp != NULL);
1371 dctx = *dctxp;
1372 *dctxp = NULL;
1373 REQUIRE(DNS_DCTX_VALID(dctx));
1374
1375 if (isc_refcount_decrement(&dctx->references) == 1) {
1376 dumpctx_destroy(dctx);
1377 }
1378 }
1379
1380 dns_dbversion_t *
1381 dns_dumpctx_version(dns_dumpctx_t *dctx) {
1382 REQUIRE(DNS_DCTX_VALID(dctx));
1383 return (dctx->version);
1384 }
1385
1386 dns_db_t *
1387 dns_dumpctx_db(dns_dumpctx_t *dctx) {
1388 REQUIRE(DNS_DCTX_VALID(dctx));
1389 return (dctx->db);
1390 }
1391
1392 void
1393 dns_dumpctx_cancel(dns_dumpctx_t *dctx) {
1394 REQUIRE(DNS_DCTX_VALID(dctx));
1395
1396 atomic_store_release(&dctx->canceled, true);
1397 }
1398
1399 static isc_result_t
1400 flushandsync(FILE *f, isc_result_t result, const char *temp) {
1401 bool logit = (result == ISC_R_SUCCESS);
1402
1403 if (result == ISC_R_SUCCESS) {
1404 result = isc_stdio_flush(f);
1405 }
1406 if (result != ISC_R_SUCCESS && logit) {
1407 if (temp != NULL) {
1408 isc_log_write(dns_lctx, ISC_LOGCATEGORY_GENERAL,
1409 DNS_LOGMODULE_MASTERDUMP, ISC_LOG_ERROR,
1410 "dumping to master file: %s: flush: %s",
1411 temp, isc_result_totext(result));
1412 } else {
1413 isc_log_write(dns_lctx, ISC_LOGCATEGORY_GENERAL,
1414 DNS_LOGMODULE_MASTERDUMP, ISC_LOG_ERROR,
1415 "dumping to stream: flush: %s",
1416 isc_result_totext(result));
1417 }
1418 logit = false;
1419 }
1420
1421 if (result == ISC_R_SUCCESS) {
1422 result = isc_stdio_sync(f);
1423 }
1424 if (result != ISC_R_SUCCESS && logit) {
1425 if (temp != NULL) {
1426 isc_log_write(dns_lctx, ISC_LOGCATEGORY_GENERAL,
1427 DNS_LOGMODULE_MASTERDUMP, ISC_LOG_ERROR,
1428 "dumping to master file: %s: fsync: %s",
1429 temp, isc_result_totext(result));
1430 } else {
1431 isc_log_write(dns_lctx, ISC_LOGCATEGORY_GENERAL,
1432 DNS_LOGMODULE_MASTERDUMP, ISC_LOG_ERROR,
1433 "dumping to stream: fsync: %s",
1434 isc_result_totext(result));
1435 }
1436 }
1437 return (result);
1438 }
1439
1440 static isc_result_t
1441 closeandrename(FILE *f, isc_result_t result, const char *temp,
1442 const char *file) {
1443 isc_result_t tresult;
1444 bool logit = (result == ISC_R_SUCCESS);
1445
1446 result = flushandsync(f, result, temp);
1447 if (result != ISC_R_SUCCESS) {
1448 logit = false;
1449 }
1450
1451 tresult = isc_stdio_close(f);
1452 if (result == ISC_R_SUCCESS) {
1453 result = tresult;
1454 }
1455 if (result != ISC_R_SUCCESS && logit) {
1456 isc_log_write(dns_lctx, ISC_LOGCATEGORY_GENERAL,
1457 DNS_LOGMODULE_MASTERDUMP, ISC_LOG_ERROR,
1458 "dumping master file: %s: fclose: %s", temp,
1459 isc_result_totext(result));
1460 logit = false;
1461 }
1462 if (result == ISC_R_SUCCESS) {
1463 result = isc_file_rename(temp, file);
1464 } else {
1465 (void)isc_file_remove(temp);
1466 }
1467 if (result != ISC_R_SUCCESS && logit) {
1468 isc_log_write(dns_lctx, ISC_LOGCATEGORY_GENERAL,
1469 DNS_LOGMODULE_MASTERDUMP, ISC_LOG_ERROR,
1470 "dumping master file: rename: %s: %s", file,
1471 isc_result_totext(result));
1472 }
1473 return (result);
1474 }
1475
1476 /*
1477 * This will run in a libuv threadpool thread.
1478 */
1479 static void
1480 master_dump_cb(void *data) {
1481 isc_result_t result = ISC_R_UNSET;
1482 dns_dumpctx_t *dctx = data;
1483 REQUIRE(DNS_DCTX_VALID(dctx));
1484
1485 if (atomic_load_acquire(&dctx->canceled)) {
1486 result = ISC_R_CANCELED;
1487 } else {
1488 result = dumptostream(dctx);
1489 }
1490
1491 if (dctx->file != NULL) {
1492 isc_result_t tresult = ISC_R_UNSET;
1493 tresult = closeandrename(dctx->f, result, dctx->tmpfile,
1494 dctx->file);
1495 if (tresult != ISC_R_SUCCESS && result == ISC_R_SUCCESS) {
1496 result = tresult;
1497 }
1498 } else {
1499 result = flushandsync(dctx->f, result, NULL);
1500 }
1501
1502 dctx->result = result;
1503 }
1504
1505 /*
1506 * This will run in a network/task manager thread when the dump is complete.
1507 */
1508 static void
1509 master_dump_done_cb(void *data, isc_result_t result) {
1510 dns_dumpctx_t *dctx = data;
1511
1512 if (result == ISC_R_SUCCESS && dctx->result != ISC_R_SUCCESS) {
1513 result = dctx->result;
1514 }
1515
1516 (dctx->done)(dctx->done_arg, result);
1517 dns_dumpctx_detach(&dctx);
1518 }
1519
1520 /*
1521 * This must be run from a network/task manager thread.
1522 */
1523 static void
1524 setup_dump(isc_task_t *task, isc_event_t *event) {
1525 dns_dumpctx_t *dctx = NULL;
1526
1527 REQUIRE(isc_nm_tid() >= 0);
1528 REQUIRE(event != NULL);
1529
1530 dctx = event->ev_arg;
1531
1532 REQUIRE(DNS_DCTX_VALID(dctx));
1533
1534 isc_nm_work_offload(isc_task_getnetmgr(task), master_dump_cb,
1535 master_dump_done_cb, dctx);
1536
1537 isc_event_free(&event);
1538 }
1539
1540 static isc_result_t
1541 task_send(dns_dumpctx_t *dctx) {
1542 isc_event_t *event;
1543
1544 event = isc_event_allocate(dctx->mctx, NULL, DNS_EVENT_DUMPQUANTUM,
1545 setup_dump, dctx, sizeof(*event));
1546 isc_task_send(dctx->task, &event);
1547 return (ISC_R_SUCCESS);
1548 }
1549
1550 static isc_result_t
1551 dumpctx_create(isc_mem_t *mctx, dns_db_t *db, dns_dbversion_t *version,
1552 const dns_master_style_t *style, FILE *f, dns_dumpctx_t **dctxp,
1553 dns_masterformat_t format, dns_masterrawheader_t *header) {
1554 dns_dumpctx_t *dctx;
1555 isc_result_t result;
1556 unsigned int options;
1557
1558 dctx = isc_mem_get(mctx, sizeof(*dctx));
1559
1560 dctx->mctx = NULL;
1561 dctx->f = f;
1562 dctx->dbiter = NULL;
1563 dctx->db = NULL;
1564 dctx->version = NULL;
1565 dctx->done = NULL;
1566 dctx->done_arg = NULL;
1567 dctx->task = NULL;
1568 atomic_init(&dctx->canceled, false);
1569 dctx->file = NULL;
1570 dctx->tmpfile = NULL;
1571 dctx->format = format;
1572 if (header == NULL) {
1573 dns_master_initrawheader(&dctx->header);
1574 } else {
1575 dctx->header = *header;
1576 }
1577
1578 switch (format) {
1579 case dns_masterformat_text:
1580 dctx->dumpsets = dump_rdatasets_text;
1581 break;
1582 case dns_masterformat_raw:
1583 dctx->dumpsets = dump_rdatasets_raw;
1584 break;
1585 default:
1586 UNREACHABLE();
1587 }
1588
1589 result = totext_ctx_init(style, NULL, &dctx->tctx);
1590 if (result != ISC_R_SUCCESS) {
1591 UNEXPECTED_ERROR("could not set master file style");
1592 goto cleanup;
1593 }
1594
1595 isc_stdtime_get(&dctx->now);
1596 dns_db_attach(db, &dctx->db);
1597
1598 dctx->do_date = dns_db_iscache(dctx->db);
1599 if (dctx->do_date) {
1600 (void)dns_db_getservestalettl(dctx->db,
1601 &dctx->tctx.serve_stale_ttl);
1602 }
1603
1604 if (dctx->format == dns_masterformat_text &&
1605 (dctx->tctx.style.flags & DNS_STYLEFLAG_REL_OWNER) != 0)
1606 {
1607 options = DNS_DB_RELATIVENAMES;
1608 } else {
1609 options = 0;
1610 }
1611 result = dns_db_createiterator(dctx->db, options, &dctx->dbiter);
1612 if (result != ISC_R_SUCCESS) {
1613 goto cleanup;
1614 }
1615
1616 isc_mutex_init(&dctx->lock);
1617
1618 if (version != NULL) {
1619 dns_db_attachversion(dctx->db, version, &dctx->version);
1620 } else if (!dns_db_iscache(db)) {
1621 dns_db_currentversion(dctx->db, &dctx->version);
1622 }
1623 isc_mem_attach(mctx, &dctx->mctx);
1624
1625 isc_refcount_init(&dctx->references, 1);
1626 dctx->magic = DNS_DCTX_MAGIC;
1627 *dctxp = dctx;
1628 return (ISC_R_SUCCESS);
1629
1630 cleanup:
1631 if (dctx->dbiter != NULL) {
1632 dns_dbiterator_destroy(&dctx->dbiter);
1633 }
1634 if (dctx->db != NULL) {
1635 dns_db_detach(&dctx->db);
1636 }
1637 isc_mem_put(mctx, dctx, sizeof(*dctx));
1638 return (result);
1639 }
1640
1641 static isc_result_t
1642 writeheader(dns_dumpctx_t *dctx) {
1643 isc_result_t result = ISC_R_SUCCESS;
1644 isc_buffer_t buffer;
1645 char *bufmem;
1646 isc_region_t r;
1647 dns_masterrawheader_t rawheader;
1648 uint32_t rawversion, now32;
1649
1650 bufmem = isc_mem_get(dctx->mctx, initial_buffer_length);
1651
1652 isc_buffer_init(&buffer, bufmem, initial_buffer_length);
1653
1654 switch (dctx->format) {
1655 case dns_masterformat_text:
1656 /*
1657 * If the database has cache semantics, output an
1658 * RFC2540 $DATE directive so that the TTLs can be
1659 * adjusted when it is reloaded. For zones it is not
1660 * really needed, and it would make the file
1661 * incompatible with pre-RFC2540 software, so we omit
1662 * it in the zone case.
1663 */
1664 if (dctx->do_date) {
1665 fprintf(dctx->f, "; using a %u second stale ttl\n",
1666 dctx->tctx.serve_stale_ttl);
1667 result = dns_time32_totext(dctx->now, &buffer);
1668 RUNTIME_CHECK(result == ISC_R_SUCCESS);
1669 isc_buffer_usedregion(&buffer, &r);
1670 fprintf(dctx->f, "$DATE %.*s\n", (int)r.length,
1671 (char *)r.base);
1672 }
1673 break;
1674 case dns_masterformat_raw:
1675 r.base = (unsigned char *)&rawheader;
1676 r.length = sizeof(rawheader);
1677 isc_buffer_region(&buffer, &r);
1678 now32 = dctx->now;
1679 rawversion = 1;
1680 if ((dctx->header.flags & DNS_MASTERRAW_COMPAT) != 0) {
1681 rawversion = 0;
1682 }
1683
1684 isc_buffer_putuint32(&buffer, dctx->format);
1685 isc_buffer_putuint32(&buffer, rawversion);
1686 isc_buffer_putuint32(&buffer, now32);
1687
1688 if (rawversion == 1) {
1689 isc_buffer_putuint32(&buffer, dctx->header.flags);
1690 isc_buffer_putuint32(&buffer,
1691 dctx->header.sourceserial);
1692 isc_buffer_putuint32(&buffer, dctx->header.lastxfrin);
1693 }
1694
1695 INSIST(isc_buffer_usedlength(&buffer) <= sizeof(rawheader));
1696 result = isc_stdio_write(buffer.base, 1,
1697 isc_buffer_usedlength(&buffer),
1698 dctx->f, NULL);
1699 if (result != ISC_R_SUCCESS) {
1700 break;
1701 }
1702
1703 break;
1704 default:
1705 UNREACHABLE();
1706 }
1707
1708 isc_mem_put(dctx->mctx, buffer.base, buffer.length);
1709 return (result);
1710 }
1711
1712 static isc_result_t
1713 dumptostream(dns_dumpctx_t *dctx) {
1714 isc_result_t result = ISC_R_SUCCESS;
1715 isc_buffer_t buffer;
1716 char *bufmem;
1717 dns_name_t *name;
1718 dns_fixedname_t fixname;
1719 unsigned int options = DNS_DB_STALEOK;
1720
1721 if ((dctx->tctx.style.flags & DNS_STYLEFLAG_EXPIRED) != 0) {
1722 options |= DNS_DB_EXPIREDOK;
1723 }
1724
1725 bufmem = isc_mem_get(dctx->mctx, initial_buffer_length);
1726
1727 isc_buffer_init(&buffer, bufmem, initial_buffer_length);
1728
1729 name = dns_fixedname_initname(&fixname);
1730
1731 CHECK(writeheader(dctx));
1732
1733 result = dns_dbiterator_first(dctx->dbiter);
1734 if (result != ISC_R_SUCCESS && result != ISC_R_NOMORE) {
1735 goto cleanup;
1736 }
1737
1738 while (result == ISC_R_SUCCESS) {
1739 dns_rdatasetiter_t *rdsiter = NULL;
1740 dns_dbnode_t *node = NULL;
1741
1742 result = dns_dbiterator_current(dctx->dbiter, &node, name);
1743 if (result != ISC_R_SUCCESS && result != DNS_R_NEWORIGIN) {
1744 break;
1745 }
1746 if (result == DNS_R_NEWORIGIN) {
1747 dns_name_t *origin =
1748 dns_fixedname_name(&dctx->tctx.origin_fixname);
1749 result = dns_dbiterator_origin(dctx->dbiter, origin);
1750 RUNTIME_CHECK(result == ISC_R_SUCCESS);
1751 if ((dctx->tctx.style.flags & DNS_STYLEFLAG_REL_DATA) !=
1752 0)
1753 {
1754 dctx->tctx.origin = origin;
1755 }
1756 dctx->tctx.neworigin = origin;
1757 }
1758
1759 result = dns_dbiterator_pause(dctx->dbiter);
1760 RUNTIME_CHECK(result == ISC_R_SUCCESS);
1761
1762 result = dns_db_allrdatasets(dctx->db, node, dctx->version,
1763 options, dctx->now, &rdsiter);
1764 if (result != ISC_R_SUCCESS) {
1765 dns_db_detachnode(dctx->db, &node);
1766 goto cleanup;
1767 }
1768 result = (dctx->dumpsets)(dctx->mctx, name, rdsiter,
1769 &dctx->tctx, &buffer, dctx->f);
1770 dns_rdatasetiter_destroy(&rdsiter);
1771 if (result != ISC_R_SUCCESS) {
1772 dns_db_detachnode(dctx->db, &node);
1773 goto cleanup;
1774 }
1775 dns_db_detachnode(dctx->db, &node);
1776 result = dns_dbiterator_next(dctx->dbiter);
1777 }
1778
1779 if (result == ISC_R_NOMORE) {
1780 result = ISC_R_SUCCESS;
1781 }
1782 cleanup:
1783 RUNTIME_CHECK(dns_dbiterator_pause(dctx->dbiter) == ISC_R_SUCCESS);
1784 isc_mem_put(dctx->mctx, buffer.base, buffer.length);
1785 return (result);
1786 }
1787
1788 isc_result_t
1789 dns_master_dumptostreamasync(isc_mem_t *mctx, dns_db_t *db,
1790 dns_dbversion_t *version,
1791 const dns_master_style_t *style, FILE *f,
1792 isc_task_t *task, dns_dumpdonefunc_t done,
1793 void *done_arg, dns_dumpctx_t **dctxp) {
1794 dns_dumpctx_t *dctx = NULL;
1795 isc_result_t result;
1796
1797 REQUIRE(task != NULL);
1798 REQUIRE(f != NULL);
1799 REQUIRE(done != NULL);
1800
1801 result = dumpctx_create(mctx, db, version, style, f, &dctx,
1802 dns_masterformat_text, NULL);
1803 if (result != ISC_R_SUCCESS) {
1804 return (result);
1805 }
1806 isc_task_attach(task, &dctx->task);
1807 dctx->done = done;
1808 dctx->done_arg = done_arg;
1809
1810 result = task_send(dctx);
1811 if (result == ISC_R_SUCCESS) {
1812 dns_dumpctx_attach(dctx, dctxp);
1813 return (DNS_R_CONTINUE);
1814 }
1815
1816 dns_dumpctx_detach(&dctx);
1817 return (result);
1818 }
1819
1820 isc_result_t
1821 dns_master_dumptostream(isc_mem_t *mctx, dns_db_t *db, dns_dbversion_t *version,
1822 const dns_master_style_t *style,
1823 dns_masterformat_t format,
1824 dns_masterrawheader_t *header, FILE *f) {
1825 dns_dumpctx_t *dctx = NULL;
1826 isc_result_t result;
1827
1828 result = dumpctx_create(mctx, db, version, style, f, &dctx, format,
1829 header);
1830 if (result != ISC_R_SUCCESS) {
1831 return (result);
1832 }
1833
1834 result = dumptostream(dctx);
1835 INSIST(result != DNS_R_CONTINUE);
1836 dns_dumpctx_detach(&dctx);
1837
1838 result = flushandsync(f, result, NULL);
1839 return (result);
1840 }
1841
1842 static isc_result_t
1843 opentmp(isc_mem_t *mctx, dns_masterformat_t format, const char *file,
1844 char **tempp, FILE **fp) {
1845 FILE *f = NULL;
1846 isc_result_t result;
1847 char *tempname = NULL;
1848 int tempnamelen;
1849
1850 tempnamelen = strlen(file) + 20;
1851 tempname = isc_mem_allocate(mctx, tempnamelen);
1852
1853 result = isc_file_mktemplate(file, tempname, tempnamelen);
1854 if (result != ISC_R_SUCCESS) {
1855 goto cleanup;
1856 }
1857
1858 if (format == dns_masterformat_text) {
1859 result = isc_file_openunique(tempname, &f);
1860 } else {
1861 result = isc_file_bopenunique(tempname, &f);
1862 }
1863 if (result != ISC_R_SUCCESS) {
1864 isc_log_write(dns_lctx, ISC_LOGCATEGORY_GENERAL,
1865 DNS_LOGMODULE_MASTERDUMP, ISC_LOG_ERROR,
1866 "dumping master file: %s: open: %s", tempname,
1867 isc_result_totext(result));
1868 goto cleanup;
1869 }
1870
1871 #if defined(POSIX_FADV_DONTNEED)
1872 posix_fadvise(fileno(f), 0, 0, POSIX_FADV_DONTNEED);
1873 #endif
1874
1875 *tempp = tempname;
1876 *fp = f;
1877 return (ISC_R_SUCCESS);
1878
1879 cleanup:
1880 isc_mem_free(mctx, tempname);
1881 return (result);
1882 }
1883
1884 isc_result_t
1885 dns_master_dumpasync(isc_mem_t *mctx, dns_db_t *db, dns_dbversion_t *version,
1886 const dns_master_style_t *style, const char *filename,
1887 isc_task_t *task, dns_dumpdonefunc_t done, void *done_arg,
1888 dns_dumpctx_t **dctxp, dns_masterformat_t format,
1889 dns_masterrawheader_t *header) {
1890 FILE *f = NULL;
1891 isc_result_t result;
1892 char *tempname = NULL;
1893 char *file = NULL;
1894 dns_dumpctx_t *dctx = NULL;
1895
1896 file = isc_mem_strdup(mctx, filename);
1897
1898 result = opentmp(mctx, format, filename, &tempname, &f);
1899 if (result != ISC_R_SUCCESS) {
1900 goto cleanup;
1901 }
1902
1903 result = dumpctx_create(mctx, db, version, style, f, &dctx, format,
1904 header);
1905 if (result != ISC_R_SUCCESS) {
1906 (void)isc_stdio_close(f);
1907 (void)isc_file_remove(tempname);
1908 goto cleanup;
1909 }
1910
1911 isc_task_attach(task, &dctx->task);
1912 dctx->done = done;
1913 dctx->done_arg = done_arg;
1914 dctx->file = file;
1915 file = NULL;
1916 dctx->tmpfile = tempname;
1917 tempname = NULL;
1918
1919 result = task_send(dctx);
1920 if (result == ISC_R_SUCCESS) {
1921 dns_dumpctx_attach(dctx, dctxp);
1922 return (DNS_R_CONTINUE);
1923 }
1924
1925 cleanup:
1926 if (dctx != NULL) {
1927 dns_dumpctx_detach(&dctx);
1928 }
1929 if (file != NULL) {
1930 isc_mem_free(mctx, file);
1931 }
1932 if (tempname != NULL) {
1933 isc_mem_free(mctx, tempname);
1934 }
1935 return (result);
1936 }
1937
1938 isc_result_t
1939 dns_master_dump(isc_mem_t *mctx, dns_db_t *db, dns_dbversion_t *version,
1940 const dns_master_style_t *style, const char *filename,
1941 dns_masterformat_t format, dns_masterrawheader_t *header) {
1942 FILE *f = NULL;
1943 isc_result_t result;
1944 char *tempname;
1945 dns_dumpctx_t *dctx = NULL;
1946
1947 result = opentmp(mctx, format, filename, &tempname, &f);
1948 if (result != ISC_R_SUCCESS) {
1949 return (result);
1950 }
1951
1952 result = dumpctx_create(mctx, db, version, style, f, &dctx, format,
1953 header);
1954 if (result != ISC_R_SUCCESS) {
1955 goto cleanup;
1956 }
1957
1958 result = dumptostream(dctx);
1959 INSIST(result != DNS_R_CONTINUE);
1960 dns_dumpctx_detach(&dctx);
1961
1962 result = closeandrename(f, result, tempname, filename);
1963
1964 cleanup:
1965 isc_mem_free(mctx, tempname);
1966 return (result);
1967 }
1968
1969 /*
1970 * Dump a database node into a master file.
1971 * XXX: this function assumes the text format.
1972 */
1973 isc_result_t
1974 dns_master_dumpnodetostream(isc_mem_t *mctx, dns_db_t *db,
1975 dns_dbversion_t *version, dns_dbnode_t *node,
1976 const dns_name_t *name,
1977 const dns_master_style_t *style, FILE *f) {
1978 isc_result_t result;
1979 isc_buffer_t buffer;
1980 char *bufmem;
1981 isc_stdtime_t now;
1982 dns_totext_ctx_t ctx;
1983 dns_rdatasetiter_t *rdsiter = NULL;
1984 unsigned int options = DNS_DB_STALEOK;
1985
1986 if ((style->flags & DNS_STYLEFLAG_EXPIRED) != 0) {
1987 options |= DNS_DB_EXPIREDOK;
1988 }
1989
1990 result = totext_ctx_init(style, NULL, &ctx);
1991 if (result != ISC_R_SUCCESS) {
1992 UNEXPECTED_ERROR("could not set master file style");
1993 return (ISC_R_UNEXPECTED);
1994 }
1995
1996 isc_stdtime_get(&now);
1997
1998 bufmem = isc_mem_get(mctx, initial_buffer_length);
1999
2000 isc_buffer_init(&buffer, bufmem, initial_buffer_length);
2001
2002 result = dns_db_allrdatasets(db, node, version, options, now, &rdsiter);
2003 if (result != ISC_R_SUCCESS) {
2004 goto failure;
2005 }
2006 result = dump_rdatasets_text(mctx, name, rdsiter, &ctx, &buffer, f);
2007 if (result != ISC_R_SUCCESS) {
2008 goto failure;
2009 }
2010 dns_rdatasetiter_destroy(&rdsiter);
2011
2012 result = ISC_R_SUCCESS;
2013
2014 failure:
2015 isc_mem_put(mctx, buffer.base, buffer.length);
2016 return (result);
2017 }
2018
2019 isc_result_t
2020 dns_master_dumpnode(isc_mem_t *mctx, dns_db_t *db, dns_dbversion_t *version,
2021 dns_dbnode_t *node, const dns_name_t *name,
2022 const dns_master_style_t *style, const char *filename) {
2023 FILE *f = NULL;
2024 isc_result_t result;
2025
2026 result = isc_stdio_open(filename, "w", &f);
2027 if (result != ISC_R_SUCCESS) {
2028 isc_log_write(dns_lctx, ISC_LOGCATEGORY_GENERAL,
2029 DNS_LOGMODULE_MASTERDUMP, ISC_LOG_ERROR,
2030 "dumping node to file: %s: open: %s", filename,
2031 isc_result_totext(result));
2032 return (ISC_R_UNEXPECTED);
2033 }
2034
2035 result = dns_master_dumpnodetostream(mctx, db, version, node, name,
2036 style, f);
2037 if (result != ISC_R_SUCCESS) {
2038 isc_log_write(dns_lctx, ISC_LOGCATEGORY_GENERAL,
2039 DNS_LOGMODULE_MASTERDUMP, ISC_LOG_ERROR,
2040 "dumping master file: %s: dump: %s", filename,
2041 isc_result_totext(result));
2042 (void)isc_stdio_close(f);
2043 return (ISC_R_UNEXPECTED);
2044 }
2045
2046 result = isc_stdio_close(f);
2047 if (result != ISC_R_SUCCESS) {
2048 isc_log_write(dns_lctx, ISC_LOGCATEGORY_GENERAL,
2049 DNS_LOGMODULE_MASTERDUMP, ISC_LOG_ERROR,
2050 "dumping master file: %s: close: %s", filename,
2051 isc_result_totext(result));
2052 return (ISC_R_UNEXPECTED);
2053 }
2054
2055 return (result);
2056 }
2057
2058 dns_masterstyle_flags_t
2059 dns_master_styleflags(const dns_master_style_t *style) {
2060 REQUIRE(style != NULL);
2061 return (style->flags);
2062 }
2063
2064 isc_result_t
2065 dns_master_stylecreate(dns_master_style_t **stylep,
2066 dns_masterstyle_flags_t flags, unsigned int ttl_column,
2067 unsigned int class_column, unsigned int type_column,
2068 unsigned int rdata_column, unsigned int line_length,
2069 unsigned int tab_width, unsigned int split_width,
2070 isc_mem_t *mctx) {
2071 dns_master_style_t *style;
2072
2073 REQUIRE(stylep != NULL && *stylep == NULL);
2074 style = isc_mem_get(mctx, sizeof(*style));
2075
2076 style->flags = flags;
2077 style->ttl_column = ttl_column;
2078 style->class_column = class_column;
2079 style->type_column = type_column;
2080 style->rdata_column = rdata_column;
2081 style->line_length = line_length;
2082 style->tab_width = tab_width;
2083 style->split_width = split_width;
2084 *stylep = style;
2085 return (ISC_R_SUCCESS);
2086 }
2087
2088 void
2089 dns_master_styledestroy(dns_master_style_t **stylep, isc_mem_t *mctx) {
2090 dns_master_style_t *style;
2091
2092 REQUIRE(stylep != NULL && *stylep != NULL);
2093 style = *stylep;
2094 *stylep = NULL;
2095 isc_mem_put(mctx, style, sizeof(*style));
2096 }
2097