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