Patrick Welche <prlw1@cam.ac.uk>
[netbsd-mini2440.git] / external / bsd / bind / dist / lib / dns / masterdump.c
blobbf94a5233274737fb709be6f74ac2c94c491acd6
1 /* $NetBSD$ */
3 /*
4 * Copyright (C) 2004-2009 Internet Systems Consortium, Inc. ("ISC")
5 * Copyright (C) 1999-2003 Internet Software Consortium.
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.
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.
20 /* Id: masterdump.c,v 1.99 2009/11/17 23:55:18 marka Exp */
22 /*! \file */
24 #include <config.h>
26 #include <stdlib.h>
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>
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>
57 #define DNS_DCTX_MAGIC ISC_MAGIC('D', 'c', 't', 'x')
58 #define DNS_DCTX_VALID(d) ISC_MAGIC_VALID(d, DNS_DCTX_MAGIC)
60 #define RETERR(x) do { \
61 isc_result_t _r = (x); \
62 if (_r != ISC_R_SUCCESS) \
63 return (_r); \
64 } while (0)
66 #define CHECK(x) do { \
67 if ((x) != ISC_R_SUCCESS) \
68 goto cleanup; \
69 } while (0)
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;
81 /*%
82 * The maximum length of the newline+indentation that is output
83 * when inserting a line break in an RR. This effectively puts an
84 * upper limits on the value of "rdata_column", because if it is
85 * very large, the tabs and spaces needed to reach it will not fit.
87 #define DNS_TOTEXT_LINEBREAK_MAXLEN 100
89 /*%
90 * Context structure for a masterfile dump in progress.
92 typedef struct dns_totext_ctx {
93 dns_master_style_t style;
94 isc_boolean_t class_printed;
95 char * linebreak;
96 char linebreak_buf[DNS_TOTEXT_LINEBREAK_MAXLEN];
97 dns_name_t * origin;
98 dns_name_t * neworigin;
99 dns_fixedname_t origin_fixname;
100 isc_uint32_t current_ttl;
101 isc_boolean_t current_ttl_valid;
102 } dns_totext_ctx_t;
104 LIBDNS_EXTERNAL_DATA const dns_master_style_t
105 dns_master_style_default = {
106 DNS_STYLEFLAG_OMIT_OWNER |
107 DNS_STYLEFLAG_OMIT_CLASS |
108 DNS_STYLEFLAG_REL_OWNER |
109 DNS_STYLEFLAG_REL_DATA |
110 DNS_STYLEFLAG_OMIT_TTL |
111 DNS_STYLEFLAG_TTL |
112 DNS_STYLEFLAG_COMMENT |
113 DNS_STYLEFLAG_MULTILINE,
114 24, 24, 24, 32, 80, 8
117 LIBDNS_EXTERNAL_DATA const dns_master_style_t
118 dns_master_style_full = {
119 DNS_STYLEFLAG_COMMENT |
120 DNS_STYLEFLAG_RESIGN,
121 46, 46, 46, 64, 120, 8
124 LIBDNS_EXTERNAL_DATA const dns_master_style_t
125 dns_master_style_explicitttl = {
126 DNS_STYLEFLAG_OMIT_OWNER |
127 DNS_STYLEFLAG_OMIT_CLASS |
128 DNS_STYLEFLAG_REL_OWNER |
129 DNS_STYLEFLAG_REL_DATA |
130 DNS_STYLEFLAG_COMMENT |
131 DNS_STYLEFLAG_MULTILINE,
132 24, 32, 32, 40, 80, 8
135 LIBDNS_EXTERNAL_DATA const dns_master_style_t
136 dns_master_style_cache = {
137 DNS_STYLEFLAG_OMIT_OWNER |
138 DNS_STYLEFLAG_OMIT_CLASS |
139 DNS_STYLEFLAG_MULTILINE |
140 DNS_STYLEFLAG_TRUST |
141 DNS_STYLEFLAG_NCACHE,
142 24, 32, 32, 40, 80, 8
145 LIBDNS_EXTERNAL_DATA const dns_master_style_t
146 dns_master_style_simple = {
148 24, 32, 32, 40, 80, 8
152 * A style suitable for dns_rdataset_totext().
154 LIBDNS_EXTERNAL_DATA const dns_master_style_t
155 dns_master_style_debug = {
156 DNS_STYLEFLAG_REL_OWNER,
157 24, 32, 40, 48, 80, 8
161 #define N_SPACES 10
162 static char spaces[N_SPACES+1] = " ";
164 #define N_TABS 10
165 static char tabs[N_TABS+1] = "\t\t\t\t\t\t\t\t\t\t";
167 #ifdef BIND9
168 struct dns_dumpctx {
169 unsigned int magic;
170 isc_mem_t *mctx;
171 isc_mutex_t lock;
172 unsigned int references;
173 isc_boolean_t canceled;
174 isc_boolean_t first;
175 isc_boolean_t do_date;
176 isc_stdtime_t now;
177 FILE *f;
178 dns_db_t *db;
179 dns_dbversion_t *version;
180 dns_dbiterator_t *dbiter;
181 dns_totext_ctx_t tctx;
182 isc_task_t *task;
183 dns_dumpdonefunc_t done;
184 void *done_arg;
185 unsigned int nodes;
186 /* dns_master_dumpinc() */
187 char *file;
188 char *tmpfile;
189 dns_masterformat_t format;
190 isc_result_t (*dumpsets)(isc_mem_t *mctx, dns_name_t *name,
191 dns_rdatasetiter_t *rdsiter,
192 dns_totext_ctx_t *ctx,
193 isc_buffer_t *buffer, FILE *f);
195 #endif /* BIND9 */
197 #define NXDOMAIN(x) (((x)->attributes & DNS_RDATASETATTR_NXDOMAIN) != 0)
200 * Output tabs and spaces to go from column '*current' to
201 * column 'to', and update '*current' to reflect the new
202 * current column.
204 static isc_result_t
205 indent(unsigned int *current, unsigned int to, int tabwidth,
206 isc_buffer_t *target)
208 isc_region_t r;
209 unsigned char *p;
210 unsigned int from;
211 int ntabs, nspaces, t;
213 from = *current;
215 if (to < from + 1)
216 to = from + 1;
218 ntabs = to / tabwidth - from / tabwidth;
219 if (ntabs < 0)
220 ntabs = 0;
222 if (ntabs > 0) {
223 isc_buffer_availableregion(target, &r);
224 if (r.length < (unsigned) ntabs)
225 return (ISC_R_NOSPACE);
226 p = r.base;
228 t = ntabs;
229 while (t) {
230 int n = t;
231 if (n > N_TABS)
232 n = N_TABS;
233 memcpy(p, tabs, n);
234 p += n;
235 t -= n;
237 isc_buffer_add(target, ntabs);
238 from = (to / tabwidth) * tabwidth;
241 nspaces = to - from;
242 INSIST(nspaces >= 0);
244 isc_buffer_availableregion(target, &r);
245 if (r.length < (unsigned) nspaces)
246 return (ISC_R_NOSPACE);
247 p = r.base;
249 t = nspaces;
250 while (t) {
251 int n = t;
252 if (n > N_SPACES)
253 n = N_SPACES;
254 memcpy(p, spaces, n);
255 p += n;
256 t -= n;
258 isc_buffer_add(target, nspaces);
260 *current = to;
261 return (ISC_R_SUCCESS);
264 static isc_result_t
265 totext_ctx_init(const dns_master_style_t *style, dns_totext_ctx_t *ctx) {
266 isc_result_t result;
268 REQUIRE(style->tab_width != 0);
270 ctx->style = *style;
271 ctx->class_printed = ISC_FALSE;
273 dns_fixedname_init(&ctx->origin_fixname);
276 * Set up the line break string if needed.
278 if ((ctx->style.flags & DNS_STYLEFLAG_MULTILINE) != 0) {
279 isc_buffer_t buf;
280 isc_region_t r;
281 unsigned int col = 0;
283 isc_buffer_init(&buf, ctx->linebreak_buf,
284 sizeof(ctx->linebreak_buf));
286 isc_buffer_availableregion(&buf, &r);
287 if (r.length < 1)
288 return (DNS_R_TEXTTOOLONG);
289 r.base[0] = '\n';
290 isc_buffer_add(&buf, 1);
292 result = indent(&col, ctx->style.rdata_column,
293 ctx->style.tab_width, &buf);
295 * Do not return ISC_R_NOSPACE if the line break string
296 * buffer is too small, because that would just make
297 * dump_rdataset() retry indefinitely with ever
298 * bigger target buffers. That's a different buffer,
299 * so it won't help. Use DNS_R_TEXTTOOLONG as a substitute.
301 if (result == ISC_R_NOSPACE)
302 return (DNS_R_TEXTTOOLONG);
303 if (result != ISC_R_SUCCESS)
304 return (result);
306 isc_buffer_availableregion(&buf, &r);
307 if (r.length < 1)
308 return (DNS_R_TEXTTOOLONG);
309 r.base[0] = '\0';
310 isc_buffer_add(&buf, 1);
311 ctx->linebreak = ctx->linebreak_buf;
312 } else {
313 ctx->linebreak = NULL;
316 ctx->origin = NULL;
317 ctx->neworigin = NULL;
318 ctx->current_ttl = 0;
319 ctx->current_ttl_valid = ISC_FALSE;
321 return (ISC_R_SUCCESS);
324 #define INDENT_TO(col) \
325 do { \
326 if ((result = indent(&column, ctx->style.col, \
327 ctx->style.tab_width, target)) \
328 != ISC_R_SUCCESS) \
329 return (result); \
330 } while (0)
333 static isc_result_t
334 str_totext(const char *source, isc_buffer_t *target) {
335 unsigned int l;
336 isc_region_t region;
338 isc_buffer_availableregion(target, &region);
339 l = strlen(source);
341 if (l > region.length)
342 return (ISC_R_NOSPACE);
344 memcpy(region.base, source, l);
345 isc_buffer_add(target, l);
346 return (ISC_R_SUCCESS);
349 static isc_result_t
350 ncache_summary(dns_rdataset_t *rdataset, isc_boolean_t omit_final_dot,
351 isc_buffer_t *target)
353 isc_result_t result = ISC_R_SUCCESS;
354 dns_rdataset_t rds;
355 dns_name_t name;
357 dns_rdataset_init(&rds);
358 dns_name_init(&name, NULL);
360 do {
361 dns_ncache_current(rdataset, &name, &rds);
362 for (result = dns_rdataset_first(&rds);
363 result == ISC_R_SUCCESS;
364 result = dns_rdataset_next(&rds)) {
365 CHECK(str_totext("; ", target));
366 CHECK(dns_name_totext(&name, omit_final_dot, target));
367 CHECK(str_totext(" ", target));
368 CHECK(dns_rdatatype_totext(rds.type, target));
369 if (rds.type == dns_rdatatype_rrsig) {
370 CHECK(str_totext(" ", target));
371 CHECK(dns_rdatatype_totext(rds.covers, target));
372 CHECK(str_totext(" ...\n", target));
373 } else {
374 dns_rdata_t rdata = DNS_RDATA_INIT;
375 dns_rdataset_current(&rds, &rdata);
376 CHECK(str_totext(" ", target));
377 CHECK(dns_rdata_tofmttext(&rdata, dns_rootname,
378 0, 0, " ", target));
379 CHECK(str_totext("\n", target));
382 dns_rdataset_disassociate(&rds);
383 result = dns_rdataset_next(rdataset);
384 } while (result == ISC_R_SUCCESS);
386 if (result == ISC_R_NOMORE)
387 result = ISC_R_SUCCESS;
388 cleanup:
389 if (dns_rdataset_isassociated(&rds))
390 dns_rdataset_disassociate(&rds);
392 return (result);
396 * Convert 'rdataset' to master file text format according to 'ctx',
397 * storing the result in 'target'. If 'owner_name' is NULL, it
398 * is omitted; otherwise 'owner_name' must be valid and have at least
399 * one label.
402 static isc_result_t
403 rdataset_totext(dns_rdataset_t *rdataset,
404 dns_name_t *owner_name,
405 dns_totext_ctx_t *ctx,
406 isc_boolean_t omit_final_dot,
407 isc_buffer_t *target)
409 isc_result_t result;
410 unsigned int column;
411 isc_boolean_t first = ISC_TRUE;
412 isc_uint32_t current_ttl;
413 isc_boolean_t current_ttl_valid;
414 dns_rdatatype_t type;
416 REQUIRE(DNS_RDATASET_VALID(rdataset));
418 rdataset->attributes |= DNS_RDATASETATTR_LOADORDER;
419 result = dns_rdataset_first(rdataset);
420 REQUIRE(result == ISC_R_SUCCESS);
422 current_ttl = ctx->current_ttl;
423 current_ttl_valid = ctx->current_ttl_valid;
425 do {
426 column = 0;
429 * Owner name.
431 if (owner_name != NULL &&
432 ! ((ctx->style.flags & DNS_STYLEFLAG_OMIT_OWNER) != 0 &&
433 !first))
435 unsigned int name_start = target->used;
436 RETERR(dns_name_totext(owner_name,
437 omit_final_dot,
438 target));
439 column += target->used - name_start;
443 * TTL.
445 if ((ctx->style.flags & DNS_STYLEFLAG_NO_TTL) == 0 &&
446 !((ctx->style.flags & DNS_STYLEFLAG_OMIT_TTL) != 0 &&
447 current_ttl_valid &&
448 rdataset->ttl == current_ttl))
450 char ttlbuf[64];
451 isc_region_t r;
452 unsigned int length;
454 INDENT_TO(ttl_column);
455 length = snprintf(ttlbuf, sizeof(ttlbuf), "%u",
456 rdataset->ttl);
457 INSIST(length <= sizeof(ttlbuf));
458 isc_buffer_availableregion(target, &r);
459 if (r.length < length)
460 return (ISC_R_NOSPACE);
461 memcpy(r.base, ttlbuf, length);
462 isc_buffer_add(target, length);
463 column += length;
466 * If the $TTL directive is not in use, the TTL we
467 * just printed becomes the default for subsequent RRs.
469 if ((ctx->style.flags & DNS_STYLEFLAG_TTL) == 0) {
470 current_ttl = rdataset->ttl;
471 current_ttl_valid = ISC_TRUE;
476 * Class.
478 if ((ctx->style.flags & DNS_STYLEFLAG_NO_CLASS) == 0 &&
479 ((ctx->style.flags & DNS_STYLEFLAG_OMIT_CLASS) == 0 ||
480 ctx->class_printed == ISC_FALSE))
482 unsigned int class_start;
483 INDENT_TO(class_column);
484 class_start = target->used;
485 result = dns_rdataclass_totext(rdataset->rdclass,
486 target);
487 if (result != ISC_R_SUCCESS)
488 return (result);
489 column += (target->used - class_start);
493 * Type.
496 if (rdataset->type == 0) {
497 type = rdataset->covers;
498 } else {
499 type = rdataset->type;
503 unsigned int type_start;
504 INDENT_TO(type_column);
505 type_start = target->used;
506 if (rdataset->type == 0)
507 RETERR(str_totext("\\-", target));
508 result = dns_rdatatype_totext(type, target);
509 if (result != ISC_R_SUCCESS)
510 return (result);
511 column += (target->used - type_start);
515 * Rdata.
517 INDENT_TO(rdata_column);
518 if (rdataset->type == 0) {
519 if (NXDOMAIN(rdataset))
520 RETERR(str_totext(";-$NXDOMAIN\n", target));
521 else
522 RETERR(str_totext(";-$NXRRSET\n", target));
524 * Print a summary of the cached records which make
525 * up the negative response.
527 RETERR(ncache_summary(rdataset, omit_final_dot,
528 target));
529 break;
530 } else {
531 dns_rdata_t rdata = DNS_RDATA_INIT;
532 isc_region_t r;
534 dns_rdataset_current(rdataset, &rdata);
536 RETERR(dns_rdata_tofmttext(&rdata,
537 ctx->origin,
538 ctx->style.flags,
539 ctx->style.line_length -
540 ctx->style.rdata_column,
541 ctx->linebreak,
542 target));
544 isc_buffer_availableregion(target, &r);
545 if (r.length < 1)
546 return (ISC_R_NOSPACE);
547 r.base[0] = '\n';
548 isc_buffer_add(target, 1);
551 first = ISC_FALSE;
552 result = dns_rdataset_next(rdataset);
553 } while (result == ISC_R_SUCCESS);
555 if (result != ISC_R_NOMORE)
556 return (result);
559 * Update the ctx state to reflect what we just printed.
560 * This is done last, only when we are sure we will return
561 * success, because this function may be called multiple
562 * times with increasing buffer sizes until it succeeds,
563 * and failed attempts must not update the state prematurely.
565 ctx->class_printed = ISC_TRUE;
566 ctx->current_ttl= current_ttl;
567 ctx->current_ttl_valid = current_ttl_valid;
569 return (ISC_R_SUCCESS);
573 * Print the name, type, and class of an empty rdataset,
574 * such as those used to represent the question section
575 * of a DNS message.
577 static isc_result_t
578 question_totext(dns_rdataset_t *rdataset,
579 dns_name_t *owner_name,
580 dns_totext_ctx_t *ctx,
581 isc_boolean_t omit_final_dot,
582 isc_buffer_t *target)
584 unsigned int column;
585 isc_result_t result;
586 isc_region_t r;
588 REQUIRE(DNS_RDATASET_VALID(rdataset));
589 result = dns_rdataset_first(rdataset);
590 REQUIRE(result == ISC_R_NOMORE);
592 column = 0;
594 /* Owner name */
596 unsigned int name_start = target->used;
597 RETERR(dns_name_totext(owner_name,
598 omit_final_dot,
599 target));
600 column += target->used - name_start;
603 /* Class */
605 unsigned int class_start;
606 INDENT_TO(class_column);
607 class_start = target->used;
608 result = dns_rdataclass_totext(rdataset->rdclass, target);
609 if (result != ISC_R_SUCCESS)
610 return (result);
611 column += (target->used - class_start);
614 /* Type */
616 unsigned int type_start;
617 INDENT_TO(type_column);
618 type_start = target->used;
619 result = dns_rdatatype_totext(rdataset->type, target);
620 if (result != ISC_R_SUCCESS)
621 return (result);
622 column += (target->used - type_start);
625 isc_buffer_availableregion(target, &r);
626 if (r.length < 1)
627 return (ISC_R_NOSPACE);
628 r.base[0] = '\n';
629 isc_buffer_add(target, 1);
631 return (ISC_R_SUCCESS);
634 isc_result_t
635 dns_rdataset_totext(dns_rdataset_t *rdataset,
636 dns_name_t *owner_name,
637 isc_boolean_t omit_final_dot,
638 isc_boolean_t question,
639 isc_buffer_t *target)
641 dns_totext_ctx_t ctx;
642 isc_result_t result;
643 result = totext_ctx_init(&dns_master_style_debug, &ctx);
644 if (result != ISC_R_SUCCESS) {
645 UNEXPECTED_ERROR(__FILE__, __LINE__,
646 "could not set master file style");
647 return (ISC_R_UNEXPECTED);
651 * The caller might want to give us an empty owner
652 * name (e.g. if they are outputting into a master
653 * file and this rdataset has the same name as the
654 * previous one.)
656 if (dns_name_countlabels(owner_name) == 0)
657 owner_name = NULL;
659 if (question)
660 return (question_totext(rdataset, owner_name, &ctx,
661 omit_final_dot, target));
662 else
663 return (rdataset_totext(rdataset, owner_name, &ctx,
664 omit_final_dot, target));
667 isc_result_t
668 dns_master_rdatasettotext(dns_name_t *owner_name,
669 dns_rdataset_t *rdataset,
670 const dns_master_style_t *style,
671 isc_buffer_t *target)
673 dns_totext_ctx_t ctx;
674 isc_result_t result;
675 result = totext_ctx_init(style, &ctx);
676 if (result != ISC_R_SUCCESS) {
677 UNEXPECTED_ERROR(__FILE__, __LINE__,
678 "could not set master file style");
679 return (ISC_R_UNEXPECTED);
682 return (rdataset_totext(rdataset, owner_name, &ctx,
683 ISC_FALSE, target));
686 isc_result_t
687 dns_master_questiontotext(dns_name_t *owner_name,
688 dns_rdataset_t *rdataset,
689 const dns_master_style_t *style,
690 isc_buffer_t *target)
692 dns_totext_ctx_t ctx;
693 isc_result_t result;
694 result = totext_ctx_init(style, &ctx);
695 if (result != ISC_R_SUCCESS) {
696 UNEXPECTED_ERROR(__FILE__, __LINE__,
697 "could not set master file style");
698 return (ISC_R_UNEXPECTED);
701 return (question_totext(rdataset, owner_name, &ctx,
702 ISC_FALSE, target));
705 #ifdef BIND9
707 * Print an rdataset. 'buffer' is a scratch buffer, which must have been
708 * dynamically allocated by the caller. It must be large enough to
709 * hold the result from dns_ttl_totext(). If more than that is needed,
710 * the buffer will be grown automatically.
713 static isc_result_t
714 dump_rdataset(isc_mem_t *mctx, dns_name_t *name, dns_rdataset_t *rdataset,
715 dns_totext_ctx_t *ctx,
716 isc_buffer_t *buffer, FILE *f)
718 isc_region_t r;
719 isc_result_t result;
721 REQUIRE(buffer->length > 0);
724 * Output a $TTL directive if needed.
727 if ((ctx->style.flags & DNS_STYLEFLAG_TTL) != 0) {
728 if (ctx->current_ttl_valid == ISC_FALSE ||
729 ctx->current_ttl != rdataset->ttl)
731 if ((ctx->style.flags & DNS_STYLEFLAG_COMMENT) != 0)
733 isc_buffer_clear(buffer);
734 result = dns_ttl_totext(rdataset->ttl,
735 ISC_TRUE, buffer);
736 INSIST(result == ISC_R_SUCCESS);
737 isc_buffer_usedregion(buffer, &r);
738 fprintf(f, "$TTL %u\t; %.*s\n", rdataset->ttl,
739 (int) r.length, (char *) r.base);
740 } else {
741 fprintf(f, "$TTL %u\n", rdataset->ttl);
743 ctx->current_ttl = rdataset->ttl;
744 ctx->current_ttl_valid = ISC_TRUE;
748 isc_buffer_clear(buffer);
751 * Generate the text representation of the rdataset into
752 * the buffer. If the buffer is too small, grow it.
754 for (;;) {
755 int newlength;
756 void *newmem;
757 result = rdataset_totext(rdataset, name, ctx,
758 ISC_FALSE, buffer);
759 if (result != ISC_R_NOSPACE)
760 break;
762 newlength = buffer->length * 2;
763 newmem = isc_mem_get(mctx, newlength);
764 if (newmem == NULL)
765 return (ISC_R_NOMEMORY);
766 isc_mem_put(mctx, buffer->base, buffer->length);
767 isc_buffer_init(buffer, newmem, newlength);
769 if (result != ISC_R_SUCCESS)
770 return (result);
773 * Write the buffer contents to the master file.
775 isc_buffer_usedregion(buffer, &r);
776 result = isc_stdio_write(r.base, 1, (size_t)r.length, f, NULL);
778 if (result != ISC_R_SUCCESS) {
779 UNEXPECTED_ERROR(__FILE__, __LINE__,
780 "master file write failed: %s",
781 isc_result_totext(result));
782 return (result);
785 return (ISC_R_SUCCESS);
789 * Define the order in which rdatasets should be printed in zone
790 * files. We will print SOA and NS records before others, SIGs
791 * immediately following the things they sign, and order everything
792 * else by RR number. This is all just for aesthetics and
793 * compatibility with buggy software that expects the SOA to be first;
794 * the DNS specifications allow any order.
797 static int
798 dump_order(const dns_rdataset_t *rds) {
799 int t;
800 int sig;
801 if (rds->type == dns_rdatatype_rrsig) {
802 t = rds->covers;
803 sig = 1;
804 } else {
805 t = rds->type;
806 sig = 0;
808 switch (t) {
809 case dns_rdatatype_soa:
810 t = 0;
811 break;
812 case dns_rdatatype_ns:
813 t = 1;
814 break;
815 default:
816 t += 2;
817 break;
819 return (t << 1) + sig;
822 static int
823 dump_order_compare(const void *a, const void *b) {
824 return (dump_order(*((const dns_rdataset_t * const *) a)) -
825 dump_order(*((const dns_rdataset_t * const *) b)));
829 * Dump all the rdatasets of a domain name to a master file. We make
830 * a "best effort" attempt to sort the RRsets in a nice order, but if
831 * there are more than MAXSORT RRsets, we punt and only sort them in
832 * groups of MAXSORT. This is not expected to ever happen in practice
833 * since much less than 64 RR types have been registered with the
834 * IANA, so far, and the output will be correct (though not
835 * aesthetically pleasing) even if it does happen.
838 #define MAXSORT 64
840 static const char *trustnames[] = {
841 "none",
842 "pending-additional",
843 "pending-answer",
844 "additional",
845 "glue",
846 "answer",
847 "authauthority",
848 "authanswer",
849 "secure",
850 "local" /* aka ultimate */
853 const char *
854 dns_trust_totext(dns_trust_t trust) {
855 if (trust >= sizeof(trustnames)/sizeof(*trustnames))
856 return ("bad");
857 return (trustnames[trust]);
860 static isc_result_t
861 dump_rdatasets_text(isc_mem_t *mctx, dns_name_t *name,
862 dns_rdatasetiter_t *rdsiter, dns_totext_ctx_t *ctx,
863 isc_buffer_t *buffer, FILE *f)
865 isc_result_t itresult, dumpresult;
866 isc_region_t r;
867 dns_rdataset_t rdatasets[MAXSORT];
868 dns_rdataset_t *sorted[MAXSORT];
869 int i, n;
871 itresult = dns_rdatasetiter_first(rdsiter);
872 dumpresult = ISC_R_SUCCESS;
874 if (itresult == ISC_R_SUCCESS && ctx->neworigin != NULL) {
875 isc_buffer_clear(buffer);
876 itresult = dns_name_totext(ctx->neworigin, ISC_FALSE, buffer);
877 RUNTIME_CHECK(itresult == ISC_R_SUCCESS);
878 isc_buffer_usedregion(buffer, &r);
879 fprintf(f, "$ORIGIN %.*s\n", (int) r.length, (char *) r.base);
880 ctx->neworigin = NULL;
883 again:
884 for (i = 0;
885 itresult == ISC_R_SUCCESS && i < MAXSORT;
886 itresult = dns_rdatasetiter_next(rdsiter), i++) {
887 dns_rdataset_init(&rdatasets[i]);
888 dns_rdatasetiter_current(rdsiter, &rdatasets[i]);
889 sorted[i] = &rdatasets[i];
891 n = i;
892 INSIST(n <= MAXSORT);
894 qsort(sorted, n, sizeof(sorted[0]), dump_order_compare);
896 for (i = 0; i < n; i++) {
897 dns_rdataset_t *rds = sorted[i];
898 if (ctx->style.flags & DNS_STYLEFLAG_TRUST) {
899 unsigned int trust = rds->trust;
900 INSIST(trust < (sizeof(trustnames) /
901 sizeof(trustnames[0])));
902 fprintf(f, "; %s\n", trustnames[trust]);
904 if (rds->type == 0 &&
905 (ctx->style.flags & DNS_STYLEFLAG_NCACHE) == 0) {
906 /* Omit negative cache entries */
907 } else {
908 isc_result_t result =
909 dump_rdataset(mctx, name, rds, ctx,
910 buffer, f);
911 if (result != ISC_R_SUCCESS)
912 dumpresult = result;
913 if ((ctx->style.flags & DNS_STYLEFLAG_OMIT_OWNER) != 0)
914 name = NULL;
916 if (ctx->style.flags & DNS_STYLEFLAG_RESIGN &&
917 rds->attributes & DNS_RDATASETATTR_RESIGN) {
918 isc_buffer_t b;
919 char buf[sizeof("YYYYMMDDHHMMSS")];
920 memset(buf, 0, sizeof(buf));
921 isc_buffer_init(&b, buf, sizeof(buf) - 1);
922 dns_time64_totext((isc_uint64_t)rds->resign, &b);
923 fprintf(f, "; resign=%s\n", buf);
925 dns_rdataset_disassociate(rds);
928 if (dumpresult != ISC_R_SUCCESS)
929 return (dumpresult);
932 * If we got more data than could be sorted at once,
933 * go handle the rest.
935 if (itresult == ISC_R_SUCCESS)
936 goto again;
938 if (itresult == ISC_R_NOMORE)
939 itresult = ISC_R_SUCCESS;
941 return (itresult);
945 * Dump given RRsets in the "raw" format.
947 static isc_result_t
948 dump_rdataset_raw(isc_mem_t *mctx, dns_name_t *name, dns_rdataset_t *rdataset,
949 isc_buffer_t *buffer, FILE *f)
951 isc_result_t result;
952 isc_uint32_t totallen;
953 isc_uint16_t dlen;
954 isc_region_t r, r_hdr;
956 REQUIRE(buffer->length > 0);
957 REQUIRE(DNS_RDATASET_VALID(rdataset));
959 restart:
960 totallen = 0;
961 result = dns_rdataset_first(rdataset);
962 REQUIRE(result == ISC_R_SUCCESS);
964 isc_buffer_clear(buffer);
967 * Common header and owner name (length followed by name)
968 * These fields should be in a moderate length, so we assume we
969 * can store all of them in the initial buffer.
971 isc_buffer_availableregion(buffer, &r_hdr);
972 INSIST(r_hdr.length >= sizeof(dns_masterrawrdataset_t));
973 isc_buffer_putuint32(buffer, totallen); /* XXX: leave space */
974 isc_buffer_putuint16(buffer, rdataset->rdclass); /* 16-bit class */
975 isc_buffer_putuint16(buffer, rdataset->type); /* 16-bit type */
976 isc_buffer_putuint16(buffer, rdataset->covers); /* same as type */
977 isc_buffer_putuint32(buffer, rdataset->ttl); /* 32-bit TTL */
978 isc_buffer_putuint32(buffer, dns_rdataset_count(rdataset));
979 totallen = isc_buffer_usedlength(buffer);
980 INSIST(totallen <= sizeof(dns_masterrawrdataset_t));
982 dns_name_toregion(name, &r);
983 INSIST(isc_buffer_availablelength(buffer) >=
984 (sizeof(dlen) + r.length));
985 dlen = (isc_uint16_t)r.length;
986 isc_buffer_putuint16(buffer, dlen);
987 isc_buffer_copyregion(buffer, &r);
988 totallen += sizeof(dlen) + r.length;
990 do {
991 dns_rdata_t rdata = DNS_RDATA_INIT;
992 isc_region_t r;
994 dns_rdataset_current(rdataset, &rdata);
995 dns_rdata_toregion(&rdata, &r);
996 INSIST(r.length <= 0xffffU);
997 dlen = (isc_uint16_t)r.length;
1000 * Copy the rdata into the buffer. If the buffer is too small,
1001 * grow it. This should be rare, so we'll simply restart the
1002 * entire procedure (or should we copy the old data and
1003 * continue?).
1005 if (isc_buffer_availablelength(buffer) <
1006 sizeof(dlen) + r.length) {
1007 int newlength;
1008 void *newmem;
1010 newlength = buffer->length * 2;
1011 newmem = isc_mem_get(mctx, newlength);
1012 if (newmem == NULL)
1013 return (ISC_R_NOMEMORY);
1014 isc_mem_put(mctx, buffer->base, buffer->length);
1015 isc_buffer_init(buffer, newmem, newlength);
1016 goto restart;
1018 isc_buffer_putuint16(buffer, dlen);
1019 isc_buffer_copyregion(buffer, &r);
1020 totallen += sizeof(dlen) + r.length;
1022 result = dns_rdataset_next(rdataset);
1023 } while (result == ISC_R_SUCCESS);
1025 if (result != ISC_R_NOMORE)
1026 return (result);
1029 * Fill in the total length field.
1030 * XXX: this is a bit tricky. Since we have already "used" the space
1031 * for the total length in the buffer, we first remember the entire
1032 * buffer length in the region, "rewind", and then write the value.
1034 isc_buffer_usedregion(buffer, &r);
1035 isc_buffer_clear(buffer);
1036 isc_buffer_putuint32(buffer, totallen);
1037 INSIST(isc_buffer_usedlength(buffer) < totallen);
1040 * Write the buffer contents to the raw master file.
1042 result = isc_stdio_write(r.base, 1, (size_t)r.length, f, NULL);
1044 if (result != ISC_R_SUCCESS) {
1045 UNEXPECTED_ERROR(__FILE__, __LINE__,
1046 "raw master file write failed: %s",
1047 isc_result_totext(result));
1048 return (result);
1051 return (result);
1054 static isc_result_t
1055 dump_rdatasets_raw(isc_mem_t *mctx, dns_name_t *name,
1056 dns_rdatasetiter_t *rdsiter, dns_totext_ctx_t *ctx,
1057 isc_buffer_t *buffer, FILE *f)
1059 isc_result_t result;
1060 dns_rdataset_t rdataset;
1062 for (result = dns_rdatasetiter_first(rdsiter);
1063 result == ISC_R_SUCCESS;
1064 result = dns_rdatasetiter_next(rdsiter)) {
1066 dns_rdataset_init(&rdataset);
1067 dns_rdatasetiter_current(rdsiter, &rdataset);
1069 if (rdataset.type == 0 &&
1070 (ctx->style.flags & DNS_STYLEFLAG_NCACHE) == 0) {
1071 /* Omit negative cache entries */
1072 } else {
1073 result = dump_rdataset_raw(mctx, name, &rdataset,
1074 buffer, f);
1076 dns_rdataset_disassociate(&rdataset);
1079 if (result == ISC_R_NOMORE)
1080 result = ISC_R_SUCCESS;
1082 return (result);
1086 * Initial size of text conversion buffer. The buffer is used
1087 * for several purposes: converting origin names, rdatasets,
1088 * $DATE timestamps, and comment strings for $TTL directives.
1090 * When converting rdatasets, it is dynamically resized, but
1091 * when converting origins, timestamps, etc it is not. Therefore,
1092 * the initial size must large enough to hold the longest possible
1093 * text representation of any domain name (for $ORIGIN).
1095 static const int initial_buffer_length = 1200;
1097 static isc_result_t
1098 dumptostreaminc(dns_dumpctx_t *dctx);
1100 static void
1101 dumpctx_destroy(dns_dumpctx_t *dctx) {
1103 dctx->magic = 0;
1104 DESTROYLOCK(&dctx->lock);
1105 dns_dbiterator_destroy(&dctx->dbiter);
1106 if (dctx->version != NULL)
1107 dns_db_closeversion(dctx->db, &dctx->version, ISC_FALSE);
1108 dns_db_detach(&dctx->db);
1109 if (dctx->task != NULL)
1110 isc_task_detach(&dctx->task);
1111 if (dctx->file != NULL)
1112 isc_mem_free(dctx->mctx, dctx->file);
1113 if (dctx->tmpfile != NULL)
1114 isc_mem_free(dctx->mctx, dctx->tmpfile);
1115 isc_mem_putanddetach(&dctx->mctx, dctx, sizeof(*dctx));
1118 void
1119 dns_dumpctx_attach(dns_dumpctx_t *source, dns_dumpctx_t **target) {
1121 REQUIRE(DNS_DCTX_VALID(source));
1122 REQUIRE(target != NULL && *target == NULL);
1124 LOCK(&source->lock);
1125 INSIST(source->references > 0);
1126 source->references++;
1127 INSIST(source->references != 0); /* Overflow? */
1128 UNLOCK(&source->lock);
1130 *target = source;
1133 void
1134 dns_dumpctx_detach(dns_dumpctx_t **dctxp) {
1135 dns_dumpctx_t *dctx;
1136 isc_boolean_t need_destroy = ISC_FALSE;
1138 REQUIRE(dctxp != NULL);
1139 dctx = *dctxp;
1140 REQUIRE(DNS_DCTX_VALID(dctx));
1142 *dctxp = NULL;
1144 LOCK(&dctx->lock);
1145 INSIST(dctx->references != 0);
1146 dctx->references--;
1147 if (dctx->references == 0)
1148 need_destroy = ISC_TRUE;
1149 UNLOCK(&dctx->lock);
1150 if (need_destroy)
1151 dumpctx_destroy(dctx);
1154 dns_dbversion_t *
1155 dns_dumpctx_version(dns_dumpctx_t *dctx) {
1156 REQUIRE(DNS_DCTX_VALID(dctx));
1157 return (dctx->version);
1160 dns_db_t *
1161 dns_dumpctx_db(dns_dumpctx_t *dctx) {
1162 REQUIRE(DNS_DCTX_VALID(dctx));
1163 return (dctx->db);
1166 void
1167 dns_dumpctx_cancel(dns_dumpctx_t *dctx) {
1168 REQUIRE(DNS_DCTX_VALID(dctx));
1170 LOCK(&dctx->lock);
1171 dctx->canceled = ISC_TRUE;
1172 UNLOCK(&dctx->lock);
1175 static isc_result_t
1176 closeandrename(FILE *f, isc_result_t result, const char *temp, const char *file)
1178 isc_result_t tresult;
1179 isc_boolean_t logit = ISC_TF(result == ISC_R_SUCCESS);
1181 if (result == ISC_R_SUCCESS)
1182 result = isc_stdio_sync(f);
1183 if (result != ISC_R_SUCCESS && logit) {
1184 isc_log_write(dns_lctx, ISC_LOGCATEGORY_GENERAL,
1185 DNS_LOGMODULE_MASTERDUMP, ISC_LOG_ERROR,
1186 "dumping master file: %s: fsync: %s",
1187 temp, isc_result_totext(result));
1188 logit = ISC_FALSE;
1190 tresult = isc_stdio_close(f);
1191 if (result == ISC_R_SUCCESS)
1192 result = tresult;
1193 if (result != ISC_R_SUCCESS && logit) {
1194 isc_log_write(dns_lctx, ISC_LOGCATEGORY_GENERAL,
1195 DNS_LOGMODULE_MASTERDUMP, ISC_LOG_ERROR,
1196 "dumping master file: %s: fclose: %s",
1197 temp, isc_result_totext(result));
1198 logit = ISC_FALSE;
1200 if (result == ISC_R_SUCCESS)
1201 result = isc_file_rename(temp, file);
1202 else
1203 (void)isc_file_remove(temp);
1204 if (result != ISC_R_SUCCESS && logit) {
1205 isc_log_write(dns_lctx, ISC_LOGCATEGORY_GENERAL,
1206 DNS_LOGMODULE_MASTERDUMP, ISC_LOG_ERROR,
1207 "dumping master file: rename: %s: %s",
1208 file, isc_result_totext(result));
1210 return (result);
1213 static void
1214 dump_quantum(isc_task_t *task, isc_event_t *event) {
1215 isc_result_t result;
1216 isc_result_t tresult;
1217 dns_dumpctx_t *dctx;
1219 REQUIRE(event != NULL);
1220 dctx = event->ev_arg;
1221 REQUIRE(DNS_DCTX_VALID(dctx));
1222 if (dctx->canceled)
1223 result = ISC_R_CANCELED;
1224 else
1225 result = dumptostreaminc(dctx);
1226 if (result == DNS_R_CONTINUE) {
1227 event->ev_arg = dctx;
1228 isc_task_send(task, &event);
1229 return;
1232 if (dctx->file != NULL) {
1233 tresult = closeandrename(dctx->f, result,
1234 dctx->tmpfile, dctx->file);
1235 if (tresult != ISC_R_SUCCESS && result == ISC_R_SUCCESS)
1236 result = tresult;
1238 (dctx->done)(dctx->done_arg, result);
1239 isc_event_free(&event);
1240 dns_dumpctx_detach(&dctx);
1243 static isc_result_t
1244 task_send(dns_dumpctx_t *dctx) {
1245 isc_event_t *event;
1247 event = isc_event_allocate(dctx->mctx, NULL, DNS_EVENT_DUMPQUANTUM,
1248 dump_quantum, dctx, sizeof(*event));
1249 if (event == NULL)
1250 return (ISC_R_NOMEMORY);
1251 isc_task_send(dctx->task, &event);
1252 return (ISC_R_SUCCESS);
1255 static isc_result_t
1256 dumpctx_create(isc_mem_t *mctx, dns_db_t *db, dns_dbversion_t *version,
1257 const dns_master_style_t *style, FILE *f, dns_dumpctx_t **dctxp,
1258 dns_masterformat_t format)
1260 dns_dumpctx_t *dctx;
1261 isc_result_t result;
1262 unsigned int options;
1264 dctx = isc_mem_get(mctx, sizeof(*dctx));
1265 if (dctx == NULL)
1266 return (ISC_R_NOMEMORY);
1268 dctx->mctx = NULL;
1269 dctx->f = f;
1270 dctx->dbiter = NULL;
1271 dctx->db = NULL;
1272 dctx->version = NULL;
1273 dctx->done = NULL;
1274 dctx->done_arg = NULL;
1275 dctx->task = NULL;
1276 dctx->nodes = 0;
1277 dctx->first = ISC_TRUE;
1278 dctx->canceled = ISC_FALSE;
1279 dctx->file = NULL;
1280 dctx->tmpfile = NULL;
1281 dctx->format = format;
1283 switch (format) {
1284 case dns_masterformat_text:
1285 dctx->dumpsets = dump_rdatasets_text;
1286 break;
1287 case dns_masterformat_raw:
1288 dctx->dumpsets = dump_rdatasets_raw;
1289 break;
1290 default:
1291 INSIST(0);
1292 break;
1295 result = totext_ctx_init(style, &dctx->tctx);
1296 if (result != ISC_R_SUCCESS) {
1297 UNEXPECTED_ERROR(__FILE__, __LINE__,
1298 "could not set master file style");
1299 goto cleanup;
1302 isc_stdtime_get(&dctx->now);
1303 dns_db_attach(db, &dctx->db);
1305 dctx->do_date = dns_db_iscache(dctx->db);
1307 if (dctx->format == dns_masterformat_text &&
1308 (dctx->tctx.style.flags & DNS_STYLEFLAG_REL_OWNER) != 0) {
1309 options = DNS_DB_RELATIVENAMES;
1310 } else
1311 options = 0;
1312 result = dns_db_createiterator(dctx->db, options, &dctx->dbiter);
1313 if (result != ISC_R_SUCCESS)
1314 goto cleanup;
1316 result = isc_mutex_init(&dctx->lock);
1317 if (result != ISC_R_SUCCESS)
1318 goto cleanup;
1319 if (version != NULL)
1320 dns_db_attachversion(dctx->db, version, &dctx->version);
1321 else if (!dns_db_iscache(db))
1322 dns_db_currentversion(dctx->db, &dctx->version);
1323 isc_mem_attach(mctx, &dctx->mctx);
1324 dctx->references = 1;
1325 dctx->magic = DNS_DCTX_MAGIC;
1326 *dctxp = dctx;
1327 return (ISC_R_SUCCESS);
1329 cleanup:
1330 if (dctx->dbiter != NULL)
1331 dns_dbiterator_destroy(&dctx->dbiter);
1332 if (dctx->db != NULL)
1333 dns_db_detach(&dctx->db);
1334 if (dctx != NULL)
1335 isc_mem_put(mctx, dctx, sizeof(*dctx));
1336 return (result);
1339 static isc_result_t
1340 dumptostreaminc(dns_dumpctx_t *dctx) {
1341 isc_result_t result;
1342 isc_buffer_t buffer;
1343 char *bufmem;
1344 isc_region_t r;
1345 dns_name_t *name;
1346 dns_fixedname_t fixname;
1347 unsigned int nodes;
1348 dns_masterrawheader_t rawheader;
1349 isc_uint32_t now32;
1350 isc_time_t start;
1352 bufmem = isc_mem_get(dctx->mctx, initial_buffer_length);
1353 if (bufmem == NULL)
1354 return (ISC_R_NOMEMORY);
1356 isc_buffer_init(&buffer, bufmem, initial_buffer_length);
1358 dns_fixedname_init(&fixname);
1359 name = dns_fixedname_name(&fixname);
1361 if (dctx->first) {
1362 switch (dctx->format) {
1363 case dns_masterformat_text:
1365 * If the database has cache semantics, output an
1366 * RFC2540 $DATE directive so that the TTLs can be
1367 * adjusted when it is reloaded. For zones it is not
1368 * really needed, and it would make the file
1369 * incompatible with pre-RFC2540 software, so we omit
1370 * it in the zone case.
1372 if (dctx->do_date) {
1373 result = dns_time32_totext(dctx->now, &buffer);
1374 RUNTIME_CHECK(result == ISC_R_SUCCESS);
1375 isc_buffer_usedregion(&buffer, &r);
1376 fprintf(dctx->f, "$DATE %.*s\n",
1377 (int) r.length, (char *) r.base);
1379 break;
1380 case dns_masterformat_raw:
1381 r.base = (unsigned char *)&rawheader;
1382 r.length = sizeof(rawheader);
1383 isc_buffer_region(&buffer, &r);
1384 isc_buffer_putuint32(&buffer, dns_masterformat_raw);
1385 isc_buffer_putuint32(&buffer, DNS_RAWFORMAT_VERSION);
1386 if (sizeof(now32) != sizeof(dctx->now)) {
1388 * We assume isc_stdtime_t is a 32-bit integer,
1389 * which should be the case on most cases.
1390 * If it turns out to be uncommon, we'll need
1391 * to bump the version number and revise the
1392 * header format.
1394 isc_log_write(dns_lctx,
1395 ISC_LOGCATEGORY_GENERAL,
1396 DNS_LOGMODULE_MASTERDUMP,
1397 ISC_LOG_INFO,
1398 "dumping master file in raw "
1399 "format: stdtime is not 32bits");
1400 now32 = 0;
1401 } else
1402 now32 = dctx->now;
1403 isc_buffer_putuint32(&buffer, now32);
1404 INSIST(isc_buffer_usedlength(&buffer) <=
1405 sizeof(rawheader));
1406 result = isc_stdio_write(buffer.base, 1,
1407 isc_buffer_usedlength(&buffer),
1408 dctx->f, NULL);
1409 if (result != ISC_R_SUCCESS)
1410 return (result);
1411 isc_buffer_clear(&buffer);
1412 break;
1413 default:
1414 INSIST(0);
1417 result = dns_dbiterator_first(dctx->dbiter);
1418 dctx->first = ISC_FALSE;
1419 } else
1420 result = ISC_R_SUCCESS;
1422 nodes = dctx->nodes;
1423 isc_time_now(&start);
1424 while (result == ISC_R_SUCCESS && (dctx->nodes == 0 || nodes--)) {
1425 dns_rdatasetiter_t *rdsiter = NULL;
1426 dns_dbnode_t *node = NULL;
1428 result = dns_dbiterator_current(dctx->dbiter, &node, name);
1429 if (result != ISC_R_SUCCESS && result != DNS_R_NEWORIGIN)
1430 break;
1431 if (result == DNS_R_NEWORIGIN) {
1432 dns_name_t *origin =
1433 dns_fixedname_name(&dctx->tctx.origin_fixname);
1434 result = dns_dbiterator_origin(dctx->dbiter, origin);
1435 RUNTIME_CHECK(result == ISC_R_SUCCESS);
1436 if ((dctx->tctx.style.flags & DNS_STYLEFLAG_REL_DATA) != 0)
1437 dctx->tctx.origin = origin;
1438 dctx->tctx.neworigin = origin;
1440 result = dns_db_allrdatasets(dctx->db, node, dctx->version,
1441 dctx->now, &rdsiter);
1442 if (result != ISC_R_SUCCESS) {
1443 dns_db_detachnode(dctx->db, &node);
1444 goto fail;
1446 result = (dctx->dumpsets)(dctx->mctx, name, rdsiter,
1447 &dctx->tctx, &buffer, dctx->f);
1448 dns_rdatasetiter_destroy(&rdsiter);
1449 if (result != ISC_R_SUCCESS) {
1450 dns_db_detachnode(dctx->db, &node);
1451 goto fail;
1453 dns_db_detachnode(dctx->db, &node);
1454 result = dns_dbiterator_next(dctx->dbiter);
1458 * Work out how many nodes can be written in the time between
1459 * two requests to the nameserver. Smooth the resulting number and
1460 * use it as a estimate for the number of nodes to be written in the
1461 * next iteration.
1463 if (dctx->nodes != 0 && result == ISC_R_SUCCESS) {
1464 unsigned int pps = dns_pps; /* packets per second */
1465 unsigned int interval;
1466 isc_uint64_t usecs;
1467 isc_time_t end;
1469 isc_time_now(&end);
1470 if (pps < 100)
1471 pps = 100;
1472 interval = 1000000 / pps; /* interval in usecs */
1473 if (interval == 0)
1474 interval = 1;
1475 usecs = isc_time_microdiff(&end, &start);
1476 if (usecs == 0) {
1477 dctx->nodes = dctx->nodes * 2;
1478 if (dctx->nodes > 1000)
1479 dctx->nodes = 1000;
1480 } else {
1481 nodes = dctx->nodes * interval;
1482 nodes /= (unsigned int)usecs;
1483 if (nodes == 0)
1484 nodes = 1;
1485 else if (nodes > 1000)
1486 nodes = 1000;
1488 /* Smooth and assign. */
1489 dctx->nodes = (nodes + dctx->nodes * 7) / 8;
1491 isc_log_write(dns_lctx, ISC_LOGCATEGORY_GENERAL,
1492 DNS_LOGMODULE_MASTERDUMP,
1493 ISC_LOG_DEBUG(1),
1494 "dumptostreaminc(%p) new nodes -> %d\n",
1495 dctx, dctx->nodes);
1497 result = DNS_R_CONTINUE;
1498 } else if (result == ISC_R_NOMORE)
1499 result = ISC_R_SUCCESS;
1500 fail:
1501 RUNTIME_CHECK(dns_dbiterator_pause(dctx->dbiter) == ISC_R_SUCCESS);
1502 isc_mem_put(dctx->mctx, buffer.base, buffer.length);
1503 return (result);
1506 isc_result_t
1507 dns_master_dumptostreaminc(isc_mem_t *mctx, dns_db_t *db,
1508 dns_dbversion_t *version,
1509 const dns_master_style_t *style,
1510 FILE *f, isc_task_t *task,
1511 dns_dumpdonefunc_t done, void *done_arg,
1512 dns_dumpctx_t **dctxp)
1514 dns_dumpctx_t *dctx = NULL;
1515 isc_result_t result;
1517 REQUIRE(task != NULL);
1518 REQUIRE(f != NULL);
1519 REQUIRE(done != NULL);
1521 result = dumpctx_create(mctx, db, version, style, f, &dctx,
1522 dns_masterformat_text);
1523 if (result != ISC_R_SUCCESS)
1524 return (result);
1525 isc_task_attach(task, &dctx->task);
1526 dctx->done = done;
1527 dctx->done_arg = done_arg;
1528 dctx->nodes = 100;
1530 result = task_send(dctx);
1531 if (result == ISC_R_SUCCESS) {
1532 dns_dumpctx_attach(dctx, dctxp);
1533 return (DNS_R_CONTINUE);
1536 dns_dumpctx_detach(&dctx);
1537 return (result);
1541 * Dump an entire database into a master file.
1543 isc_result_t
1544 dns_master_dumptostream(isc_mem_t *mctx, dns_db_t *db,
1545 dns_dbversion_t *version,
1546 const dns_master_style_t *style,
1547 FILE *f)
1549 return (dns_master_dumptostream2(mctx, db, version, style,
1550 dns_masterformat_text, f));
1553 isc_result_t
1554 dns_master_dumptostream2(isc_mem_t *mctx, dns_db_t *db,
1555 dns_dbversion_t *version,
1556 const dns_master_style_t *style,
1557 dns_masterformat_t format, FILE *f)
1559 dns_dumpctx_t *dctx = NULL;
1560 isc_result_t result;
1562 result = dumpctx_create(mctx, db, version, style, f, &dctx, format);
1563 if (result != ISC_R_SUCCESS)
1564 return (result);
1566 result = dumptostreaminc(dctx);
1567 INSIST(result != DNS_R_CONTINUE);
1568 dns_dumpctx_detach(&dctx);
1569 return (result);
1572 static isc_result_t
1573 opentmp(isc_mem_t *mctx, const char *file, char **tempp, FILE **fp) {
1574 FILE *f = NULL;
1575 isc_result_t result;
1576 char *tempname = NULL;
1577 int tempnamelen;
1579 tempnamelen = strlen(file) + 20;
1580 tempname = isc_mem_allocate(mctx, tempnamelen);
1581 if (tempname == NULL)
1582 return (ISC_R_NOMEMORY);
1584 result = isc_file_mktemplate(file, tempname, tempnamelen);
1585 if (result != ISC_R_SUCCESS)
1586 goto cleanup;
1588 result = isc_file_openunique(tempname, &f);
1589 if (result != ISC_R_SUCCESS) {
1590 isc_log_write(dns_lctx, ISC_LOGCATEGORY_GENERAL,
1591 DNS_LOGMODULE_MASTERDUMP, ISC_LOG_ERROR,
1592 "dumping master file: %s: open: %s",
1593 tempname, isc_result_totext(result));
1594 goto cleanup;
1596 *tempp = tempname;
1597 *fp = f;
1598 return (ISC_R_SUCCESS);
1600 cleanup:
1601 isc_mem_free(mctx, tempname);
1602 return (result);
1605 isc_result_t
1606 dns_master_dumpinc(isc_mem_t *mctx, dns_db_t *db, dns_dbversion_t *version,
1607 const dns_master_style_t *style, const char *filename,
1608 isc_task_t *task, dns_dumpdonefunc_t done, void *done_arg,
1609 dns_dumpctx_t **dctxp)
1611 return (dns_master_dumpinc2(mctx, db, version, style, filename, task,
1612 done, done_arg, dctxp,
1613 dns_masterformat_text));
1616 isc_result_t
1617 dns_master_dumpinc2(isc_mem_t *mctx, dns_db_t *db, dns_dbversion_t *version,
1618 const dns_master_style_t *style, const char *filename,
1619 isc_task_t *task, dns_dumpdonefunc_t done, void *done_arg,
1620 dns_dumpctx_t **dctxp, dns_masterformat_t format)
1622 FILE *f = NULL;
1623 isc_result_t result;
1624 char *tempname = NULL;
1625 char *file = NULL;
1626 dns_dumpctx_t *dctx = NULL;
1628 file = isc_mem_strdup(mctx, filename);
1629 if (file == NULL)
1630 return (ISC_R_NOMEMORY);
1632 result = opentmp(mctx, filename, &tempname, &f);
1633 if (result != ISC_R_SUCCESS)
1634 goto cleanup;
1636 result = dumpctx_create(mctx, db, version, style, f, &dctx, format);
1637 if (result != ISC_R_SUCCESS) {
1638 (void)isc_stdio_close(f);
1639 (void)isc_file_remove(tempname);
1640 goto cleanup;
1643 isc_task_attach(task, &dctx->task);
1644 dctx->done = done;
1645 dctx->done_arg = done_arg;
1646 dctx->nodes = 100;
1647 dctx->file = file;
1648 file = NULL;
1649 dctx->tmpfile = tempname;
1650 tempname = NULL;
1652 result = task_send(dctx);
1653 if (result == ISC_R_SUCCESS) {
1654 dns_dumpctx_attach(dctx, dctxp);
1655 return (DNS_R_CONTINUE);
1658 cleanup:
1659 if (dctx != NULL)
1660 dns_dumpctx_detach(&dctx);
1661 if (file != NULL)
1662 isc_mem_free(mctx, file);
1663 if (tempname != NULL)
1664 isc_mem_free(mctx, tempname);
1665 return (result);
1668 isc_result_t
1669 dns_master_dump(isc_mem_t *mctx, dns_db_t *db, dns_dbversion_t *version,
1670 const dns_master_style_t *style, const char *filename)
1672 return (dns_master_dump2(mctx, db, version, style, filename,
1673 dns_masterformat_text));
1676 isc_result_t
1677 dns_master_dump2(isc_mem_t *mctx, dns_db_t *db, dns_dbversion_t *version,
1678 const dns_master_style_t *style, const char *filename,
1679 dns_masterformat_t format)
1681 FILE *f = NULL;
1682 isc_result_t result;
1683 char *tempname;
1684 dns_dumpctx_t *dctx = NULL;
1686 result = opentmp(mctx, filename, &tempname, &f);
1687 if (result != ISC_R_SUCCESS)
1688 return (result);
1690 result = dumpctx_create(mctx, db, version, style, f, &dctx, format);
1691 if (result != ISC_R_SUCCESS)
1692 goto cleanup;
1694 result = dumptostreaminc(dctx);
1695 INSIST(result != DNS_R_CONTINUE);
1696 dns_dumpctx_detach(&dctx);
1698 result = closeandrename(f, result, tempname, filename);
1700 cleanup:
1701 isc_mem_free(mctx, tempname);
1702 return (result);
1706 * Dump a database node into a master file.
1707 * XXX: this function assumes the text format.
1709 isc_result_t
1710 dns_master_dumpnodetostream(isc_mem_t *mctx, dns_db_t *db,
1711 dns_dbversion_t *version,
1712 dns_dbnode_t *node, dns_name_t *name,
1713 const dns_master_style_t *style,
1714 FILE *f)
1716 isc_result_t result;
1717 isc_buffer_t buffer;
1718 char *bufmem;
1719 isc_stdtime_t now;
1720 dns_totext_ctx_t ctx;
1721 dns_rdatasetiter_t *rdsiter = NULL;
1723 result = totext_ctx_init(style, &ctx);
1724 if (result != ISC_R_SUCCESS) {
1725 UNEXPECTED_ERROR(__FILE__, __LINE__,
1726 "could not set master file style");
1727 return (ISC_R_UNEXPECTED);
1730 isc_stdtime_get(&now);
1732 bufmem = isc_mem_get(mctx, initial_buffer_length);
1733 if (bufmem == NULL)
1734 return (ISC_R_NOMEMORY);
1736 isc_buffer_init(&buffer, bufmem, initial_buffer_length);
1738 result = dns_db_allrdatasets(db, node, version, now, &rdsiter);
1739 if (result != ISC_R_SUCCESS)
1740 goto failure;
1741 result = dump_rdatasets_text(mctx, name, rdsiter, &ctx, &buffer, f);
1742 if (result != ISC_R_SUCCESS)
1743 goto failure;
1744 dns_rdatasetiter_destroy(&rdsiter);
1746 result = ISC_R_SUCCESS;
1748 failure:
1749 isc_mem_put(mctx, buffer.base, buffer.length);
1750 return (result);
1753 isc_result_t
1754 dns_master_dumpnode(isc_mem_t *mctx, dns_db_t *db, dns_dbversion_t *version,
1755 dns_dbnode_t *node, dns_name_t *name,
1756 const dns_master_style_t *style, const char *filename)
1758 FILE *f = NULL;
1759 isc_result_t result;
1761 result = isc_stdio_open(filename, "w", &f);
1762 if (result != ISC_R_SUCCESS) {
1763 isc_log_write(dns_lctx, ISC_LOGCATEGORY_GENERAL,
1764 DNS_LOGMODULE_MASTERDUMP, ISC_LOG_ERROR,
1765 "dumping node to file: %s: open: %s", filename,
1766 isc_result_totext(result));
1767 return (ISC_R_UNEXPECTED);
1770 result = dns_master_dumpnodetostream(mctx, db, version, node, name,
1771 style, f);
1773 result = isc_stdio_close(f);
1774 if (result != ISC_R_SUCCESS) {
1775 isc_log_write(dns_lctx, ISC_LOGCATEGORY_GENERAL,
1776 DNS_LOGMODULE_MASTERDUMP, ISC_LOG_ERROR,
1777 "dumping master file: %s: close: %s", filename,
1778 isc_result_totext(result));
1779 return (ISC_R_UNEXPECTED);
1782 return (result);
1784 #endif /* BIND9 */
1786 isc_result_t
1787 dns_master_stylecreate(dns_master_style_t **stylep, unsigned int flags,
1788 unsigned int ttl_column, unsigned int class_column,
1789 unsigned int type_column, unsigned int rdata_column,
1790 unsigned int line_length, unsigned int tab_width,
1791 isc_mem_t *mctx)
1793 dns_master_style_t *style;
1795 REQUIRE(stylep != NULL && *stylep == NULL);
1796 style = isc_mem_get(mctx, sizeof(*style));
1797 if (style == NULL)
1798 return (ISC_R_NOMEMORY);
1800 style->flags = flags;
1801 style->ttl_column = ttl_column;
1802 style->class_column = class_column;
1803 style->type_column = type_column;
1804 style->rdata_column = rdata_column;
1805 style->line_length = line_length;
1806 style->tab_width = tab_width;
1808 *stylep = style;
1809 return (ISC_R_SUCCESS);
1812 void
1813 dns_master_styledestroy(dns_master_style_t **stylep, isc_mem_t *mctx) {
1814 dns_master_style_t *style;
1816 REQUIRE(stylep != NULL && *stylep != NULL);
1817 style = *stylep;
1818 *stylep = NULL;
1819 isc_mem_put(mctx, style, sizeof(*style));