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