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