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