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