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