Merge branch 'vendor/TCSH'
[dragonfly.git] / contrib / bind-9.3 / lib / dns / masterdump.c
1 /*
2  * Copyright (C) 2004, 2006  Internet Systems Consortium, Inc. ("ISC")
3  * Copyright (C) 1999-2003  Internet Software Consortium.
4  *
5  * Permission to use, copy, modify, and distribute this software for any
6  * purpose with or without fee is hereby granted, provided that the above
7  * copyright notice and this permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
10  * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
11  * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
12  * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
13  * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
14  * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
15  * PERFORMANCE OF THIS SOFTWARE.
16  */
17
18 /* $Id: masterdump.c,v 1.56.2.5.2.15 2006/03/10 00:17:21 marka Exp $ */
19
20 #include <config.h>
21
22 #include <stdlib.h>
23
24 #include <isc/event.h>
25 #include <isc/file.h>
26 #include <isc/magic.h>
27 #include <isc/mem.h>
28 #include <isc/print.h>
29 #include <isc/stdio.h>
30 #include <isc/string.h>
31 #include <isc/task.h>
32 #include <isc/util.h>
33
34 #include <dns/db.h>
35 #include <dns/dbiterator.h>
36 #include <dns/events.h>
37 #include <dns/fixedname.h>
38 #include <dns/log.h>
39 #include <dns/masterdump.h>
40 #include <dns/rdata.h>
41 #include <dns/rdataclass.h>
42 #include <dns/rdataset.h>
43 #include <dns/rdatasetiter.h>
44 #include <dns/rdatatype.h>
45 #include <dns/result.h>
46 #include <dns/time.h>
47 #include <dns/ttl.h>
48
49 #define DNS_DCTX_MAGIC          ISC_MAGIC('D', 'c', 't', 'x')
50 #define DNS_DCTX_VALID(d)       ISC_MAGIC_VALID(d, DNS_DCTX_MAGIC)
51
52 #define RETERR(x) do { \
53         isc_result_t _r = (x); \
54         if (_r != ISC_R_SUCCESS) \
55                 return (_r); \
56         } while (0)
57
58 struct dns_master_style {
59         unsigned int flags;             /* DNS_STYLEFLAG_* */
60         unsigned int ttl_column;
61         unsigned int class_column;
62         unsigned int type_column;
63         unsigned int rdata_column;
64         unsigned int line_length;
65         unsigned int tab_width;
66 };
67
68 /*
69  * The maximum length of the newline+indentation that is output
70  * when inserting a line break in an RR.  This effectively puts an
71  * upper limits on the value of "rdata_column", because if it is
72  * very large, the tabs and spaces needed to reach it will not fit.
73  */
74 #define DNS_TOTEXT_LINEBREAK_MAXLEN 100
75
76 /*
77  * Context structure for a masterfile dump in progress.
78  */
79 typedef struct dns_totext_ctx {
80         dns_master_style_t      style;
81         isc_boolean_t           class_printed;
82         char *                  linebreak;
83         char                    linebreak_buf[DNS_TOTEXT_LINEBREAK_MAXLEN];
84         dns_name_t *            origin;
85         dns_name_t *            neworigin;
86         dns_fixedname_t         origin_fixname;
87         isc_uint32_t            current_ttl;
88         isc_boolean_t           current_ttl_valid;
89 } dns_totext_ctx_t;
90
91 LIBDNS_EXTERNAL_DATA const dns_master_style_t
92 dns_master_style_default = {
93         DNS_STYLEFLAG_OMIT_OWNER |
94         DNS_STYLEFLAG_OMIT_CLASS |
95         DNS_STYLEFLAG_REL_OWNER |
96         DNS_STYLEFLAG_REL_DATA |
97         DNS_STYLEFLAG_OMIT_TTL |
98         DNS_STYLEFLAG_TTL |
99         DNS_STYLEFLAG_COMMENT |
100         DNS_STYLEFLAG_MULTILINE,
101         24, 24, 24, 32, 80, 8
102 };
103
104 LIBDNS_EXTERNAL_DATA const dns_master_style_t
105 dns_master_style_full = {
106         DNS_STYLEFLAG_COMMENT,
107         46, 46, 46, 64, 120, 8
108 };
109
110 LIBDNS_EXTERNAL_DATA const dns_master_style_t
111 dns_master_style_explicitttl = {
112         DNS_STYLEFLAG_OMIT_OWNER |
113         DNS_STYLEFLAG_OMIT_CLASS |
114         DNS_STYLEFLAG_REL_OWNER |
115         DNS_STYLEFLAG_REL_DATA |
116         DNS_STYLEFLAG_COMMENT |
117         DNS_STYLEFLAG_MULTILINE,
118         24, 32, 32, 40, 80, 8
119 };
120
121 LIBDNS_EXTERNAL_DATA const dns_master_style_t
122 dns_master_style_cache = {
123         DNS_STYLEFLAG_OMIT_OWNER |
124         DNS_STYLEFLAG_OMIT_CLASS |
125         DNS_STYLEFLAG_MULTILINE |
126         DNS_STYLEFLAG_TRUST |
127         DNS_STYLEFLAG_NCACHE,
128         24, 32, 32, 40, 80, 8
129 };
130
131 LIBDNS_EXTERNAL_DATA const dns_master_style_t
132 dns_master_style_simple = {
133         0,
134         24, 32, 32, 40, 80, 8
135 };
136
137 /*
138  * A style suitable for dns_rdataset_totext().
139  */
140 LIBDNS_EXTERNAL_DATA const dns_master_style_t
141 dns_master_style_debug = {
142         DNS_STYLEFLAG_REL_OWNER,
143         24, 32, 40, 48, 80, 8
144 };
145
146
147 #define N_SPACES 10
148 static char spaces[N_SPACES+1] = "          ";
149
150 #define N_TABS 10
151 static char tabs[N_TABS+1] = "\t\t\t\t\t\t\t\t\t\t";
152
153 struct dns_dumpctx {
154         unsigned int            magic;
155         isc_mem_t               *mctx;
156         isc_mutex_t             lock;
157         unsigned int            references;
158         isc_boolean_t           canceled;
159         isc_boolean_t           first;
160         isc_boolean_t           do_date;
161         isc_stdtime_t           now;
162         FILE                    *f;
163         dns_db_t                *db;
164         dns_dbversion_t         *version;
165         dns_dbiterator_t        *dbiter;
166         dns_totext_ctx_t        tctx;
167         isc_task_t              *task;
168         dns_dumpdonefunc_t      done;
169         void                    *done_arg;
170         unsigned int            nodes;
171         /* dns_master_dumpinc() */
172         char                    *file;
173         char                    *tmpfile;
174 };
175
176 #define NXDOMAIN(x) (((x)->attributes & DNS_RDATASETATTR_NXDOMAIN) != 0) 
177
178 /*
179  * Output tabs and spaces to go from column '*current' to
180  * column 'to', and update '*current' to reflect the new
181  * current column.
182  */
183 static isc_result_t
184 indent(unsigned int *current, unsigned int to, int tabwidth,
185        isc_buffer_t *target)
186 {
187         isc_region_t r;
188         unsigned char *p;
189         unsigned int from;
190         int ntabs, nspaces, t;
191
192         from = *current;
193
194         if (to < from + 1)
195                 to = from + 1;
196
197         ntabs = to / tabwidth - from / tabwidth;
198         if (ntabs < 0)
199                 ntabs = 0;
200
201         if (ntabs > 0) {
202                 isc_buffer_availableregion(target, &r);
203                 if (r.length < (unsigned) ntabs)
204                         return (ISC_R_NOSPACE);
205                 p = r.base;
206
207                 t = ntabs;
208                 while (t) {
209                         int n = t;
210                         if (n > N_TABS)
211                                 n = N_TABS;
212                         memcpy(p, tabs, n);
213                         p += n;
214                         t -= n;
215                 }
216                 isc_buffer_add(target, ntabs);
217                 from = (to / tabwidth) * tabwidth;
218         }
219
220         nspaces = to - from;
221         INSIST(nspaces >= 0);
222
223         isc_buffer_availableregion(target, &r);
224         if (r.length < (unsigned) nspaces)
225                 return (ISC_R_NOSPACE);
226         p = r.base;
227
228         t = nspaces;
229         while (t) {
230                 int n = t;
231                 if (n > N_SPACES)
232                         n = N_SPACES;
233                 memcpy(p, spaces, n);
234                 p += n;
235                 t -= n;
236         }
237         isc_buffer_add(target, nspaces);
238
239         *current = to;
240         return (ISC_R_SUCCESS);
241 }
242
243 static isc_result_t
244 totext_ctx_init(const dns_master_style_t *style, dns_totext_ctx_t *ctx) {
245         isc_result_t result;
246
247         REQUIRE(style->tab_width != 0);
248
249         ctx->style = *style;
250         ctx->class_printed = ISC_FALSE;
251
252         dns_fixedname_init(&ctx->origin_fixname);
253
254         /*
255          * Set up the line break string if needed.
256          */
257         if ((ctx->style.flags & DNS_STYLEFLAG_MULTILINE) != 0) {
258                 isc_buffer_t buf;
259                 isc_region_t r;
260                 unsigned int col = 0;
261
262                 isc_buffer_init(&buf, ctx->linebreak_buf,
263                                 sizeof(ctx->linebreak_buf));
264
265                 isc_buffer_availableregion(&buf, &r);
266                 if (r.length < 1)
267                         return (DNS_R_TEXTTOOLONG);
268                 r.base[0] = '\n';
269                 isc_buffer_add(&buf, 1);
270
271                 result = indent(&col, ctx->style.rdata_column,
272                                 ctx->style.tab_width, &buf);
273                 /*
274                  * Do not return ISC_R_NOSPACE if the line break string
275                  * buffer is too small, because that would just make
276                  * dump_rdataset() retry indenfinitely with ever
277                  * bigger target buffers.  That's a different buffer,
278                  * so it won't help.  Use DNS_R_TEXTTOOLONG as a substitute.
279                  */
280                 if (result == ISC_R_NOSPACE)
281                         return (DNS_R_TEXTTOOLONG);
282                 if (result != ISC_R_SUCCESS)
283                         return (result);
284
285                 isc_buffer_availableregion(&buf, &r);
286                 if (r.length < 1)
287                         return (DNS_R_TEXTTOOLONG);
288                 r.base[0] = '\0';
289                 isc_buffer_add(&buf, 1);
290                 ctx->linebreak = ctx->linebreak_buf;
291         } else {
292                 ctx->linebreak = NULL;
293         }
294
295         ctx->origin = NULL;
296         ctx->neworigin = NULL;
297         ctx->current_ttl = 0;
298         ctx->current_ttl_valid = ISC_FALSE;
299
300         return (ISC_R_SUCCESS);
301 }
302
303 #define INDENT_TO(col) \
304         do { \
305                  if ((result = indent(&column, ctx->style.col, \
306                                       ctx->style.tab_width, target)) \
307                      != ISC_R_SUCCESS) \
308                             return (result); \
309         } while (0)
310
311
312 static isc_result_t
313 str_totext(const char *source, isc_buffer_t *target) {
314         unsigned int l;
315         isc_region_t region;
316
317         isc_buffer_availableregion(target, &region);
318         l = strlen(source);
319
320         if (l > region.length)
321                 return (ISC_R_NOSPACE);
322
323         memcpy(region.base, source, l);
324         isc_buffer_add(target, l);
325         return (ISC_R_SUCCESS);
326 }
327
328 /*
329  * Convert 'rdataset' to master file text format according to 'ctx',
330  * storing the result in 'target'.  If 'owner_name' is NULL, it
331  * is omitted; otherwise 'owner_name' must be valid and have at least
332  * one label.
333  */
334
335 static isc_result_t
336 rdataset_totext(dns_rdataset_t *rdataset,
337                 dns_name_t *owner_name,
338                 dns_totext_ctx_t *ctx,
339                 isc_boolean_t omit_final_dot,
340                 isc_buffer_t *target)
341 {
342         isc_result_t result;
343         unsigned int column;
344         isc_boolean_t first = ISC_TRUE;
345         isc_uint32_t current_ttl;
346         isc_boolean_t current_ttl_valid;
347         dns_rdatatype_t type;
348
349         REQUIRE(DNS_RDATASET_VALID(rdataset));
350
351         result = dns_rdataset_first(rdataset);
352         REQUIRE(result == ISC_R_SUCCESS);
353
354         current_ttl = ctx->current_ttl;
355         current_ttl_valid = ctx->current_ttl_valid;
356
357         do {
358                 column = 0;
359
360                 /*
361                  * Owner name.
362                  */
363                 if (owner_name != NULL &&
364                     ! ((ctx->style.flags & DNS_STYLEFLAG_OMIT_OWNER) != 0 &&
365                        !first))
366                 {
367                         unsigned int name_start = target->used;
368                         RETERR(dns_name_totext(owner_name,
369                                                omit_final_dot,
370                                                target));
371                         column += target->used - name_start;
372                 }
373
374                 /*
375                  * TTL.
376                  */
377                 if ((ctx->style.flags & DNS_STYLEFLAG_NO_TTL) == 0 &&
378                     !((ctx->style.flags & DNS_STYLEFLAG_OMIT_TTL) != 0 &&
379                       current_ttl_valid &&
380                       rdataset->ttl == current_ttl))
381                 {
382                         char ttlbuf[64];
383                         isc_region_t r;
384                         unsigned int length;
385
386                         INDENT_TO(ttl_column);
387                         length = snprintf(ttlbuf, sizeof(ttlbuf), "%u",
388                                           rdataset->ttl);
389                         INSIST(length <= sizeof(ttlbuf));
390                         isc_buffer_availableregion(target, &r);
391                         if (r.length < length)
392                                 return (ISC_R_NOSPACE);
393                         memcpy(r.base, ttlbuf, length);
394                         isc_buffer_add(target, length);
395                         column += length;
396
397                         /*
398                          * If the $TTL directive is not in use, the TTL we
399                          * just printed becomes the default for subsequent RRs.
400                          */
401                         if ((ctx->style.flags & DNS_STYLEFLAG_TTL) == 0) {
402                                 current_ttl = rdataset->ttl;
403                                 current_ttl_valid = ISC_TRUE;
404                         }
405                 }
406
407                 /*
408                  * Class.
409                  */
410                 if ((ctx->style.flags & DNS_STYLEFLAG_NO_CLASS) == 0 &&
411                     ((ctx->style.flags & DNS_STYLEFLAG_OMIT_CLASS) == 0 ||
412                      ctx->class_printed == ISC_FALSE))
413                 {
414                         unsigned int class_start;
415                         INDENT_TO(class_column);
416                         class_start = target->used;
417                         result = dns_rdataclass_totext(rdataset->rdclass,
418                                                        target);
419                         if (result != ISC_R_SUCCESS)
420                                 return (result);
421                         column += (target->used - class_start);
422                 }
423
424                 /*
425                  * Type.
426                  */
427
428                 if (rdataset->type == 0) {
429                         type = rdataset->covers;
430                 } else {
431                         type = rdataset->type;
432                 }
433
434                 {
435                         unsigned int type_start;
436                         INDENT_TO(type_column);
437                         type_start = target->used;
438                         if (rdataset->type == 0)
439                                 RETERR(str_totext("\\-", target));
440                         result = dns_rdatatype_totext(type, target);
441                         if (result != ISC_R_SUCCESS)
442                                 return (result);
443                         column += (target->used - type_start);
444                 }
445
446                 /*
447                  * Rdata.
448                  */
449                 INDENT_TO(rdata_column);
450                 if (rdataset->type == 0) {
451                         if (NXDOMAIN(rdataset))
452                                 RETERR(str_totext(";-$NXDOMAIN\n", target));
453                         else
454                                 RETERR(str_totext(";-$NXRRSET\n", target));
455                 } else {
456                         dns_rdata_t rdata = DNS_RDATA_INIT;
457                         isc_region_t r;
458
459                         dns_rdataset_current(rdataset, &rdata);
460
461                         RETERR(dns_rdata_tofmttext(&rdata,
462                                                    ctx->origin,
463                                                    ctx->style.flags,
464                                                    ctx->style.line_length -
465                                                        ctx->style.rdata_column,
466                                                    ctx->linebreak,
467                                                    target));
468
469                         isc_buffer_availableregion(target, &r);
470                         if (r.length < 1)
471                                 return (ISC_R_NOSPACE);
472                         r.base[0] = '\n';
473                         isc_buffer_add(target, 1);
474                 }
475
476                 first = ISC_FALSE;
477                 result = dns_rdataset_next(rdataset);
478         } while (result == ISC_R_SUCCESS);
479
480         if (result != ISC_R_NOMORE)
481                 return (result);
482
483         /*
484          * Update the ctx state to reflect what we just printed.
485          * This is done last, only when we are sure we will return
486          * success, because this function may be called multiple
487          * times with increasing buffer sizes until it succeeds,
488          * and failed attempts must not update the state prematurely.
489          */
490         ctx->class_printed = ISC_TRUE;
491         ctx->current_ttl= current_ttl;
492         ctx->current_ttl_valid = current_ttl_valid;
493
494         return (ISC_R_SUCCESS);
495 }
496
497 /*
498  * Print the name, type, and class of an empty rdataset,
499  * such as those used to represent the question section
500  * of a DNS message.
501  */
502 static isc_result_t
503 question_totext(dns_rdataset_t *rdataset,
504                 dns_name_t *owner_name,
505                 dns_totext_ctx_t *ctx,
506                 isc_boolean_t omit_final_dot,
507                 isc_buffer_t *target)
508 {
509         unsigned int column;
510         isc_result_t result;
511         isc_region_t r;
512
513         REQUIRE(DNS_RDATASET_VALID(rdataset));
514         result = dns_rdataset_first(rdataset);
515         REQUIRE(result == ISC_R_NOMORE);
516
517         column = 0;
518
519         /* Owner name */
520         {
521                 unsigned int name_start = target->used;
522                 RETERR(dns_name_totext(owner_name,
523                                        omit_final_dot,
524                                        target));
525                 column += target->used - name_start;
526         }
527
528         /* Class */
529         {
530                 unsigned int class_start;
531                 INDENT_TO(class_column);
532                 class_start = target->used;
533                 result = dns_rdataclass_totext(rdataset->rdclass, target);
534                 if (result != ISC_R_SUCCESS)
535                         return (result);
536                 column += (target->used - class_start);
537         }
538
539         /* Type */
540         {
541                 unsigned int type_start;
542                 INDENT_TO(type_column);
543                 type_start = target->used;
544                 result = dns_rdatatype_totext(rdataset->type, target);
545                 if (result != ISC_R_SUCCESS)
546                         return (result);
547                 column += (target->used - type_start);
548         }
549
550         isc_buffer_availableregion(target, &r);
551         if (r.length < 1)
552                 return (ISC_R_NOSPACE);
553         r.base[0] = '\n';
554         isc_buffer_add(target, 1);
555
556         return (ISC_R_SUCCESS);
557 }
558
559 isc_result_t
560 dns_rdataset_totext(dns_rdataset_t *rdataset,
561                     dns_name_t *owner_name,
562                     isc_boolean_t omit_final_dot,
563                     isc_boolean_t question,
564                     isc_buffer_t *target)
565 {
566         dns_totext_ctx_t ctx;
567         isc_result_t result;
568         result = totext_ctx_init(&dns_master_style_debug, &ctx);
569         if (result != ISC_R_SUCCESS) {
570                 UNEXPECTED_ERROR(__FILE__, __LINE__,
571                                  "could not set master file style");
572                 return (ISC_R_UNEXPECTED);
573         }
574
575         /*
576          * The caller might want to give us an empty owner
577          * name (e.g. if they are outputting into a master
578          * file and this rdataset has the same name as the
579          * previous one.)
580          */
581         if (dns_name_countlabels(owner_name) == 0)
582                 owner_name = NULL;
583
584         if (question)
585                 return (question_totext(rdataset, owner_name, &ctx,
586                                         omit_final_dot, target));
587         else
588                 return (rdataset_totext(rdataset, owner_name, &ctx,
589                                         omit_final_dot, target));
590 }
591
592 isc_result_t
593 dns_master_rdatasettotext(dns_name_t *owner_name,
594                           dns_rdataset_t *rdataset,
595                           const dns_master_style_t *style,
596                           isc_buffer_t *target)
597 {
598         dns_totext_ctx_t ctx;
599         isc_result_t result;
600         result = totext_ctx_init(style, &ctx);
601         if (result != ISC_R_SUCCESS) {
602                 UNEXPECTED_ERROR(__FILE__, __LINE__,
603                                  "could not set master file style");
604                 return (ISC_R_UNEXPECTED);
605         }
606
607         return (rdataset_totext(rdataset, owner_name, &ctx,
608                                 ISC_FALSE, target));
609 }
610
611 isc_result_t
612 dns_master_questiontotext(dns_name_t *owner_name,
613                           dns_rdataset_t *rdataset,
614                           const dns_master_style_t *style,
615                           isc_buffer_t *target)
616 {
617         dns_totext_ctx_t ctx;
618         isc_result_t result;
619         result = totext_ctx_init(style, &ctx);
620         if (result != ISC_R_SUCCESS) {
621                 UNEXPECTED_ERROR(__FILE__, __LINE__,
622                                  "could not set master file style");
623                 return (ISC_R_UNEXPECTED);
624         }
625
626         return (question_totext(rdataset, owner_name, &ctx,
627                                 ISC_FALSE, target));
628 }
629
630 /*
631  * Print an rdataset.  'buffer' is a scratch buffer, which must have been
632  * dynamically allocated by the caller.  It must be large enough to
633  * hold the result from dns_ttl_totext().  If more than that is needed,
634  * the buffer will be grown automatically.
635  */
636
637 static isc_result_t
638 dump_rdataset(isc_mem_t *mctx, dns_name_t *name, dns_rdataset_t *rdataset,
639               dns_totext_ctx_t *ctx,
640               isc_buffer_t *buffer, FILE *f)
641 {
642         isc_region_t r;
643         isc_result_t result;
644
645         REQUIRE(buffer->length > 0);
646
647         /*
648          * Output a $TTL directive if needed.
649          */
650
651         if ((ctx->style.flags & DNS_STYLEFLAG_TTL) != 0) {
652                 if (ctx->current_ttl_valid == ISC_FALSE ||
653                     ctx->current_ttl != rdataset->ttl)
654                 {
655                         if ((ctx->style.flags & DNS_STYLEFLAG_COMMENT) != 0)
656                         {
657                                 isc_buffer_clear(buffer);
658                                 result = dns_ttl_totext(rdataset->ttl,
659                                                         ISC_TRUE, buffer);
660                                 INSIST(result == ISC_R_SUCCESS);
661                                 isc_buffer_usedregion(buffer, &r);
662                                 fprintf(f, "$TTL %u\t; %.*s\n", rdataset->ttl,
663                                         (int) r.length, (char *) r.base);
664                         } else {
665                                 fprintf(f, "$TTL %u\n", rdataset->ttl);
666                         }
667                         ctx->current_ttl = rdataset->ttl;
668                         ctx->current_ttl_valid = ISC_TRUE;
669                 }
670         }
671
672         isc_buffer_clear(buffer);
673
674         /*
675          * Generate the text representation of the rdataset into
676          * the buffer.  If the buffer is too small, grow it.
677          */
678         for (;;) {
679                 int newlength;
680                 void *newmem;
681                 result = rdataset_totext(rdataset, name, ctx,
682                                          ISC_FALSE, buffer);
683                 if (result != ISC_R_NOSPACE)
684                         break;
685
686                 newlength = buffer->length * 2;
687                 newmem = isc_mem_get(mctx, newlength);
688                 if (newmem == NULL)
689                         return (ISC_R_NOMEMORY);
690                 isc_mem_put(mctx, buffer->base, buffer->length);
691                 isc_buffer_init(buffer, newmem, newlength);
692         }
693         if (result != ISC_R_SUCCESS)
694                 return (result);
695
696         /*
697          * Write the buffer contents to the master file.
698          */
699         isc_buffer_usedregion(buffer, &r);
700         result = isc_stdio_write(r.base, 1, (size_t)r.length, f, NULL);
701
702         if (result != ISC_R_SUCCESS) {
703                 UNEXPECTED_ERROR(__FILE__, __LINE__,
704                                  "master file write failed: %s",
705                                  isc_result_totext(result));
706                 return (result);
707         }
708
709         return (ISC_R_SUCCESS);
710 }
711
712 /*
713  * Define the order in which rdatasets should be printed in zone
714  * files.  We will print SOA and NS records before others, SIGs
715  * immediately following the things they sign, and order everything
716  * else by RR number.  This is all just for aesthetics and
717  * compatibility with buggy software that expects the SOA to be first;
718  * the DNS specifications allow any order.
719  */
720
721 static int
722 dump_order(const dns_rdataset_t *rds) {
723         int t;
724         int sig;
725         if (rds->type == dns_rdatatype_rrsig) {
726                 t = rds->covers;
727                 sig = 1;
728         } else {
729                 t = rds->type;
730                 sig = 0;
731         }
732         switch (t) {
733         case dns_rdatatype_soa:
734                 t = 0;
735                 break;
736         case dns_rdatatype_ns:
737                 t = 1;
738                 break;
739         default:
740                 t += 2;
741                 break;
742         }
743         return (t << 1) + sig;
744 }
745
746 static int
747 dump_order_compare(const void *a, const void *b) {
748         return (dump_order(*((const dns_rdataset_t * const *) a)) -
749                 dump_order(*((const dns_rdataset_t * const *) b)));
750 }
751
752 /*
753  * Dump all the rdatasets of a domain name to a master file.  We make
754  * a "best effort" attempt to sort the RRsets in a nice order, but if
755  * there are more than MAXSORT RRsets, we punt and only sort them in
756  * groups of MAXSORT.  This is not expected to ever happen in practice
757  * since much less than 64 RR types have been registered with the
758  * IANA, so far, and the output will be correct (though not
759  * aesthetically pleasing) even if it does happen.
760  */
761
762 #define MAXSORT 64
763
764 static const char *trustnames[] = {
765         "none",
766         "pending",
767         "additional",
768         "glue",
769         "answer",
770         "authauthority",
771         "authanswer",
772         "secure",
773         "local" /* aka ultimate */
774 };
775
776 static isc_result_t
777 dump_rdatasets(isc_mem_t *mctx, dns_name_t *name, dns_rdatasetiter_t *rdsiter,
778                dns_totext_ctx_t *ctx,
779                isc_buffer_t *buffer, FILE *f)
780 {
781         isc_result_t itresult, dumpresult;
782         isc_region_t r;
783         dns_rdataset_t rdatasets[MAXSORT];
784         dns_rdataset_t *sorted[MAXSORT];
785         int i, n;
786
787         itresult = dns_rdatasetiter_first(rdsiter);
788         dumpresult = ISC_R_SUCCESS;
789
790         if (itresult == ISC_R_SUCCESS && ctx->neworigin != NULL) {
791                 isc_buffer_clear(buffer);
792                 itresult = dns_name_totext(ctx->neworigin, ISC_FALSE, buffer);
793                 RUNTIME_CHECK(itresult == ISC_R_SUCCESS);
794                 isc_buffer_usedregion(buffer, &r);
795                 fprintf(f, "$ORIGIN %.*s\n", (int) r.length, (char *) r.base);
796                 ctx->neworigin = NULL;
797         }
798
799  again:
800         for (i = 0;
801              itresult == ISC_R_SUCCESS && i < MAXSORT;
802              itresult = dns_rdatasetiter_next(rdsiter), i++) {
803                 dns_rdataset_init(&rdatasets[i]);
804                 dns_rdatasetiter_current(rdsiter, &rdatasets[i]);
805                 sorted[i] = &rdatasets[i];
806         }
807         n = i;
808         INSIST(n <= MAXSORT);
809
810         qsort(sorted, n, sizeof(sorted[0]), dump_order_compare);
811
812         for (i = 0; i < n; i++) {
813                 dns_rdataset_t *rds = sorted[i];
814                 if (ctx->style.flags & DNS_STYLEFLAG_TRUST) {
815                         unsigned int trust = rds->trust;
816                         INSIST(trust < (sizeof(trustnames) /
817                                         sizeof(trustnames[0])));
818                         fprintf(f, "; %s\n", trustnames[trust]);
819                 }
820                 if (rds->type == 0 &&
821                     (ctx->style.flags & DNS_STYLEFLAG_NCACHE) == 0) {
822                         /* Omit negative cache entries */
823                 } else {
824                         isc_result_t result =
825                                 dump_rdataset(mctx, name, rds, ctx,
826                                                buffer, f);
827                         if (result != ISC_R_SUCCESS)
828                                 dumpresult = result;
829                         if ((ctx->style.flags & DNS_STYLEFLAG_OMIT_OWNER) != 0)
830                                 name = NULL;
831                 }
832                 dns_rdataset_disassociate(rds);
833         }
834
835         if (dumpresult != ISC_R_SUCCESS)
836                 return (dumpresult);
837
838         /*
839          * If we got more data than could be sorted at once,
840          * go handle the rest.
841          */
842         if (itresult == ISC_R_SUCCESS)
843                 goto again;
844
845         if (itresult == ISC_R_NOMORE)
846                 itresult = ISC_R_SUCCESS;
847
848         return (itresult);
849 }
850
851
852 /*
853  * Initial size of text conversion buffer.  The buffer is used
854  * for several purposes: converting origin names, rdatasets,
855  * $DATE timestamps, and comment strings for $TTL directives.
856  *
857  * When converting rdatasets, it is dynamically resized, but
858  * when converting origins, timestamps, etc it is not.  Therefore,
859  * the  initial size must large enough to hold the longest possible
860  * text representation of any domain name (for $ORIGIN).
861  */
862 static const int initial_buffer_length = 1200;
863
864 static isc_result_t
865 dumptostreaminc(dns_dumpctx_t *dctx);
866
867 static void
868 dumpctx_destroy(dns_dumpctx_t *dctx) {
869
870         dctx->magic = 0;
871         DESTROYLOCK(&dctx->lock);
872         if (dctx->version != NULL)
873                 dns_db_closeversion(dctx->db, &dctx->version, ISC_FALSE);
874         dns_dbiterator_destroy(&dctx->dbiter);
875         dns_db_detach(&dctx->db);
876         if (dctx->task != NULL)
877                 isc_task_detach(&dctx->task);
878         if (dctx->file != NULL)
879                 isc_mem_free(dctx->mctx, dctx->file);
880         if (dctx->tmpfile != NULL)
881                 isc_mem_free(dctx->mctx, dctx->tmpfile);
882         isc_mem_putanddetach(&dctx->mctx, dctx, sizeof(*dctx));
883 }
884
885 void
886 dns_dumpctx_attach(dns_dumpctx_t *source, dns_dumpctx_t **target) {
887
888         REQUIRE(DNS_DCTX_VALID(source));
889         REQUIRE(target != NULL && *target == NULL);
890
891         LOCK(&source->lock);
892         INSIST(source->references > 0);
893         source->references++;
894         INSIST(source->references != 0);        /* Overflow? */
895         UNLOCK(&source->lock);
896
897         *target = source;
898 }
899
900 void
901 dns_dumpctx_detach(dns_dumpctx_t **dctxp) {
902         dns_dumpctx_t *dctx;
903         isc_boolean_t need_destroy = ISC_FALSE;
904
905         REQUIRE(dctxp != NULL);
906         dctx = *dctxp;
907         REQUIRE(DNS_DCTX_VALID(dctx));
908
909         *dctxp = NULL;
910
911         LOCK(&dctx->lock);
912         INSIST(dctx->references != 0);
913         dctx->references--;
914         if (dctx->references == 0)
915                 need_destroy = ISC_TRUE;
916         UNLOCK(&dctx->lock);
917         if (need_destroy)
918                 dumpctx_destroy(dctx);
919 }
920
921 dns_dbversion_t *
922 dns_dumpctx_version(dns_dumpctx_t *dctx) {
923         REQUIRE(DNS_DCTX_VALID(dctx));
924         return (dctx->version);
925 }
926
927 dns_db_t *
928 dns_dumpctx_db(dns_dumpctx_t *dctx) {
929         REQUIRE(DNS_DCTX_VALID(dctx));
930         return (dctx->db);
931 }
932
933 void
934 dns_dumpctx_cancel(dns_dumpctx_t *dctx) {
935         REQUIRE(DNS_DCTX_VALID(dctx));
936
937         LOCK(&dctx->lock);
938         dctx->canceled = ISC_TRUE;
939         UNLOCK(&dctx->lock);
940 }
941
942 static isc_result_t
943 closeandrename(FILE *f, isc_result_t result, const char *temp, const char *file)
944 {
945         isc_result_t tresult;
946         isc_boolean_t logit = ISC_TF(result == ISC_R_SUCCESS);
947
948         if (result == ISC_R_SUCCESS)
949                 result = isc_stdio_sync(f);
950         if (result != ISC_R_SUCCESS && logit) {
951                 isc_log_write(dns_lctx, ISC_LOGCATEGORY_GENERAL,
952                               DNS_LOGMODULE_MASTERDUMP, ISC_LOG_ERROR,
953                               "dumping master file: %s: fsync: %s",
954                               temp, isc_result_totext(result));
955                 logit = ISC_FALSE;
956         }
957         tresult = isc_stdio_close(f);
958         if (result == ISC_R_SUCCESS)
959                 result = tresult;
960         if (result != ISC_R_SUCCESS && logit) {
961                 isc_log_write(dns_lctx, ISC_LOGCATEGORY_GENERAL,
962                               DNS_LOGMODULE_MASTERDUMP, ISC_LOG_ERROR,
963                               "dumping master file: %s: fclose: %s",
964                               temp, isc_result_totext(result));
965                 logit = ISC_FALSE;
966         }
967         if (result == ISC_R_SUCCESS)
968                 result = isc_file_rename(temp, file);
969         else
970                 (void)isc_file_remove(temp);
971         if (result != ISC_R_SUCCESS && logit) {
972                 isc_log_write(dns_lctx, ISC_LOGCATEGORY_GENERAL,
973                               DNS_LOGMODULE_MASTERDUMP, ISC_LOG_ERROR,
974                               "dumping master file: rename: %s: %s",
975                               file, isc_result_totext(result));
976         }
977         return (result);
978 }
979
980 static void
981 dump_quantum(isc_task_t *task, isc_event_t *event) {
982         isc_result_t result;
983         isc_result_t tresult;
984         dns_dumpctx_t *dctx;
985
986         REQUIRE(event != NULL);
987         dctx = event->ev_arg;
988         REQUIRE(DNS_DCTX_VALID(dctx));
989         if (dctx->canceled)
990                 result = ISC_R_CANCELED;
991         else
992                 result = dumptostreaminc(dctx);
993         if (result == DNS_R_CONTINUE) {
994                 event->ev_arg = dctx;
995                 isc_task_send(task, &event);
996                 return;
997         }
998
999         if (dctx->file != NULL) {
1000                 tresult = closeandrename(dctx->f, result,
1001                                          dctx->tmpfile, dctx->file);
1002                 if (tresult != ISC_R_SUCCESS && result == ISC_R_SUCCESS)
1003                         result = tresult;
1004         }
1005         (dctx->done)(dctx->done_arg, result);
1006         isc_event_free(&event);
1007         dns_dumpctx_detach(&dctx);
1008 }
1009
1010 static isc_result_t
1011 task_send(dns_dumpctx_t *dctx) {
1012         isc_event_t *event;
1013
1014         event = isc_event_allocate(dctx->mctx, NULL, DNS_EVENT_DUMPQUANTUM,
1015                                    dump_quantum, dctx, sizeof(*event));
1016         if (event == NULL)
1017                 return (ISC_R_NOMEMORY);
1018         isc_task_send(dctx->task, &event);
1019         return (ISC_R_SUCCESS);
1020 }
1021
1022 static isc_result_t
1023 dumpctx_create(isc_mem_t *mctx, dns_db_t *db, dns_dbversion_t *version,
1024                const dns_master_style_t *style, FILE *f, dns_dumpctx_t **dctxp)
1025 {
1026         dns_dumpctx_t *dctx;
1027         isc_result_t result;
1028         isc_boolean_t relative;
1029
1030         dctx = isc_mem_get(mctx, sizeof(*dctx));
1031         if (dctx == NULL)
1032                 return (ISC_R_NOMEMORY);
1033
1034         dctx->mctx = NULL;
1035         dctx->f = f;
1036         dctx->dbiter = NULL;
1037         dctx->db = NULL;
1038         dctx->version = NULL;
1039         dctx->done = NULL;
1040         dctx->done_arg = NULL;
1041         dctx->task = NULL;
1042         dctx->nodes = 0;
1043         dctx->first = ISC_TRUE;
1044         dctx->canceled = ISC_FALSE;
1045         dctx->file = NULL;
1046         dctx->tmpfile = NULL;
1047
1048         result = totext_ctx_init(style, &dctx->tctx);
1049         if (result != ISC_R_SUCCESS) {
1050                 UNEXPECTED_ERROR(__FILE__, __LINE__,
1051                                  "could not set master file style");
1052                 goto cleanup;
1053         }
1054
1055         isc_stdtime_get(&dctx->now);
1056         dns_db_attach(db, &dctx->db);
1057
1058         dctx->do_date = dns_db_iscache(dctx->db);
1059
1060         relative = ((dctx->tctx.style.flags & DNS_STYLEFLAG_REL_OWNER) != 0) ?
1061                         ISC_TRUE : ISC_FALSE;
1062         result = dns_db_createiterator(dctx->db, relative, &dctx->dbiter);
1063         if (result != ISC_R_SUCCESS)
1064                 goto cleanup;
1065
1066         result = isc_mutex_init(&dctx->lock);
1067         if (result != ISC_R_SUCCESS)
1068                 goto cleanup;
1069         if (version != NULL)
1070                 dns_db_attachversion(dctx->db, version, &dctx->version);
1071         else if (!dns_db_iscache(db))
1072                 dns_db_currentversion(dctx->db, &dctx->version);
1073         isc_mem_attach(mctx, &dctx->mctx);
1074         dctx->references = 1;
1075         dctx->magic = DNS_DCTX_MAGIC;
1076         *dctxp = dctx;
1077         return (ISC_R_SUCCESS);
1078
1079  cleanup:
1080         if (dctx->dbiter != NULL)
1081                 dns_dbiterator_destroy(&dctx->dbiter);
1082         if (dctx->db != NULL)
1083                 dns_db_detach(&dctx->db);
1084         if (dctx != NULL)
1085                 isc_mem_put(mctx, dctx, sizeof(*dctx));
1086         return (result);
1087 }
1088
1089 static isc_result_t
1090 dumptostreaminc(dns_dumpctx_t *dctx) {
1091         isc_result_t result;
1092         isc_buffer_t buffer;
1093         char *bufmem;
1094         isc_region_t r;
1095         dns_name_t *name;
1096         dns_fixedname_t fixname;
1097         unsigned int nodes;
1098
1099         bufmem = isc_mem_get(dctx->mctx, initial_buffer_length);
1100         if (bufmem == NULL)
1101                 return (ISC_R_NOMEMORY);
1102
1103         isc_buffer_init(&buffer, bufmem, initial_buffer_length);
1104
1105         dns_fixedname_init(&fixname);
1106         name = dns_fixedname_name(&fixname);
1107
1108         if (dctx->first) {
1109                 /*
1110                  * If the database has cache semantics, output an RFC2540
1111                  * $DATE directive so that the TTLs can be adjusted when
1112                  * it is reloaded.  For zones it is not really needed, and
1113                  * it would make the file incompatible with pre-RFC2540
1114                  * software, so we omit it in the zone case.
1115                  */
1116                 if (dctx->do_date) {
1117                         result = dns_time32_totext(dctx->now, &buffer);
1118                         RUNTIME_CHECK(result == ISC_R_SUCCESS);
1119                         isc_buffer_usedregion(&buffer, &r);
1120                         fprintf(dctx->f, "$DATE %.*s\n",
1121                                 (int) r.length, (char *) r.base);
1122                 }
1123                 result = dns_dbiterator_first(dctx->dbiter);
1124                 dctx->first = ISC_FALSE;
1125         } else
1126                 result = ISC_R_SUCCESS;
1127
1128         nodes = dctx->nodes;
1129         while (result == ISC_R_SUCCESS && (dctx->nodes == 0 || nodes--)) {
1130                 dns_rdatasetiter_t *rdsiter = NULL;
1131                 dns_dbnode_t *node = NULL;
1132
1133                 result = dns_dbiterator_current(dctx->dbiter, &node, name);
1134                 if (result != ISC_R_SUCCESS && result != DNS_R_NEWORIGIN)
1135                         break;
1136                 if (result == DNS_R_NEWORIGIN) {
1137                         dns_name_t *origin =
1138                                 dns_fixedname_name(&dctx->tctx.origin_fixname);
1139                         result = dns_dbiterator_origin(dctx->dbiter, origin);
1140                         RUNTIME_CHECK(result == ISC_R_SUCCESS);
1141                         if ((dctx->tctx.style.flags & DNS_STYLEFLAG_REL_DATA) != 0)
1142                                 dctx->tctx.origin = origin;
1143                         dctx->tctx.neworigin = origin;
1144                 }
1145                 result = dns_db_allrdatasets(dctx->db, node, dctx->version,
1146                                              dctx->now, &rdsiter);
1147                 if (result != ISC_R_SUCCESS) {
1148                         dns_db_detachnode(dctx->db, &node);
1149                         goto fail;
1150                 }
1151                 result = dump_rdatasets(dctx->mctx, name, rdsiter, &dctx->tctx,
1152                                         &buffer, dctx->f);
1153                 dns_rdatasetiter_destroy(&rdsiter);
1154                 if (result != ISC_R_SUCCESS) {
1155                         dns_db_detachnode(dctx->db, &node);
1156                         goto fail;
1157                 }
1158                 dns_db_detachnode(dctx->db, &node);
1159                 result = dns_dbiterator_next(dctx->dbiter);
1160         }
1161
1162         if (dctx->nodes != 0 && result == ISC_R_SUCCESS) {
1163                 result = dns_dbiterator_pause(dctx->dbiter);
1164                 RUNTIME_CHECK(result == ISC_R_SUCCESS);
1165                 result = DNS_R_CONTINUE;
1166         } else if (result == ISC_R_NOMORE)
1167                 result = ISC_R_SUCCESS;
1168  fail:
1169         isc_mem_put(dctx->mctx, buffer.base, buffer.length);
1170         return (result);
1171 }
1172
1173 isc_result_t
1174 dns_master_dumptostreaminc(isc_mem_t *mctx, dns_db_t *db,
1175                            dns_dbversion_t *version,
1176                            const dns_master_style_t *style,
1177                            FILE *f, isc_task_t *task,
1178                            dns_dumpdonefunc_t done, void *done_arg,
1179                            dns_dumpctx_t **dctxp)
1180 {
1181         dns_dumpctx_t *dctx = NULL;
1182         isc_result_t result;
1183
1184         REQUIRE(task != NULL);
1185         REQUIRE(f != NULL);
1186         REQUIRE(done != NULL);
1187
1188         result = dumpctx_create(mctx, db, version, style, f, &dctx);
1189         if (result != ISC_R_SUCCESS)
1190                 return (result);
1191         isc_task_attach(task, &dctx->task);
1192         dctx->done = done;
1193         dctx->done_arg = done_arg;
1194         dctx->nodes = 100;
1195
1196         result = task_send(dctx);
1197         if (result == ISC_R_SUCCESS) {
1198                 dns_dumpctx_attach(dctx, dctxp);
1199                 return (DNS_R_CONTINUE);
1200         }
1201
1202         dns_dumpctx_detach(&dctx);
1203         return (result);
1204 }
1205
1206 /*
1207  * Dump an entire database into a master file.
1208  */
1209 isc_result_t
1210 dns_master_dumptostream(isc_mem_t *mctx, dns_db_t *db,
1211                         dns_dbversion_t *version,
1212                         const dns_master_style_t *style,
1213                         FILE *f)
1214 {
1215         dns_dumpctx_t *dctx = NULL;
1216         isc_result_t result;
1217
1218         result = dumpctx_create(mctx, db, version, style, f, &dctx);
1219         if (result != ISC_R_SUCCESS)
1220                 return (result);
1221
1222         result = dumptostreaminc(dctx);
1223         INSIST(result != DNS_R_CONTINUE);
1224         dns_dumpctx_detach(&dctx);
1225         return (result);
1226 }
1227
1228 static isc_result_t
1229 opentmp(isc_mem_t *mctx, const char *file, char **tempp, FILE **fp) {
1230         FILE *f = NULL;
1231         isc_result_t result;
1232         char *tempname = NULL;
1233         int tempnamelen;
1234
1235         tempnamelen = strlen(file) + 20;
1236         tempname = isc_mem_allocate(mctx, tempnamelen);
1237         if (tempname == NULL)
1238                 return (ISC_R_NOMEMORY);
1239
1240         result = isc_file_mktemplate(file, tempname, tempnamelen);
1241         if (result != ISC_R_SUCCESS)
1242                 goto cleanup;
1243
1244         result = isc_file_openunique(tempname, &f);
1245         if (result != ISC_R_SUCCESS) {
1246                 isc_log_write(dns_lctx, ISC_LOGCATEGORY_GENERAL,
1247                               DNS_LOGMODULE_MASTERDUMP, ISC_LOG_ERROR,
1248                               "dumping master file: %s: open: %s",
1249                               tempname, isc_result_totext(result));
1250                 goto cleanup;
1251         }
1252         *tempp = tempname;
1253         *fp = f;
1254         return (ISC_R_SUCCESS);
1255
1256 cleanup:
1257         isc_mem_free(mctx, tempname);
1258         return (result);
1259 }
1260
1261 isc_result_t
1262 dns_master_dumpinc(isc_mem_t *mctx, dns_db_t *db, dns_dbversion_t *version,
1263                    const dns_master_style_t *style, const char *filename,
1264                    isc_task_t *task, dns_dumpdonefunc_t done, void *done_arg,
1265                    dns_dumpctx_t **dctxp)
1266 {
1267         FILE *f = NULL;
1268         isc_result_t result;
1269         char *tempname = NULL;
1270         char *file = NULL;
1271         dns_dumpctx_t *dctx = NULL;
1272
1273         file = isc_mem_strdup(mctx, filename);
1274         if (file == NULL)
1275                 return (ISC_R_NOMEMORY);
1276
1277         result = opentmp(mctx, filename, &tempname, &f);
1278         if (result != ISC_R_SUCCESS)
1279                 goto cleanup;
1280
1281         result = dumpctx_create(mctx, db, version, style, f, &dctx);
1282         if (result != ISC_R_SUCCESS) {
1283                 (void)isc_stdio_close(f);
1284                 (void)isc_file_remove(tempname);
1285                 goto cleanup;
1286         }
1287
1288         isc_task_attach(task, &dctx->task);
1289         dctx->done = done;
1290         dctx->done_arg = done_arg;
1291         dctx->nodes = 100;
1292         dctx->file = file;
1293         file = NULL;
1294         dctx->tmpfile = tempname;
1295         tempname = NULL;
1296
1297         result = task_send(dctx);
1298         if (result == ISC_R_SUCCESS) {
1299                 dns_dumpctx_attach(dctx, dctxp);
1300                 return (DNS_R_CONTINUE);
1301         }
1302
1303  cleanup:
1304         if (dctx != NULL)
1305                 dns_dumpctx_detach(&dctx);
1306         if (file != NULL)
1307                 isc_mem_free(mctx, file);
1308         if (tempname != NULL)
1309                 isc_mem_free(mctx, tempname);
1310         return (result);
1311 }
1312
1313 isc_result_t
1314 dns_master_dump(isc_mem_t *mctx, dns_db_t *db, dns_dbversion_t *version,
1315                 const dns_master_style_t *style, const char *filename)
1316 {
1317         FILE *f = NULL;
1318         isc_result_t result;
1319         char *tempname;
1320         dns_dumpctx_t *dctx = NULL;
1321
1322         result = opentmp(mctx, filename, &tempname, &f);
1323         if (result != ISC_R_SUCCESS)
1324                 return (result);
1325
1326         result = dumpctx_create(mctx, db, version, style, f, &dctx);
1327         if (result != ISC_R_SUCCESS)
1328                 goto cleanup;
1329
1330         result = dumptostreaminc(dctx);
1331         INSIST(result != DNS_R_CONTINUE);
1332         dns_dumpctx_detach(&dctx);
1333
1334         result = closeandrename(f, result, tempname, filename);
1335
1336  cleanup:
1337         isc_mem_free(mctx, tempname);
1338         return (result);
1339 }
1340
1341 /*
1342  * Dump a database node into a master file.
1343  */
1344 isc_result_t
1345 dns_master_dumpnodetostream(isc_mem_t *mctx, dns_db_t *db,
1346                             dns_dbversion_t *version,
1347                             dns_dbnode_t *node, dns_name_t *name,
1348                             const dns_master_style_t *style,
1349                             FILE *f)
1350 {
1351         isc_result_t result;
1352         isc_buffer_t buffer;
1353         char *bufmem;
1354         isc_stdtime_t now;
1355         dns_totext_ctx_t ctx;
1356         dns_rdatasetiter_t *rdsiter = NULL;
1357
1358         result = totext_ctx_init(style, &ctx);
1359         if (result != ISC_R_SUCCESS) {
1360                 UNEXPECTED_ERROR(__FILE__, __LINE__,
1361                                  "could not set master file style");
1362                 return (ISC_R_UNEXPECTED);
1363         }
1364
1365         isc_stdtime_get(&now);
1366
1367         bufmem = isc_mem_get(mctx, initial_buffer_length);
1368         if (bufmem == NULL)
1369                 return (ISC_R_NOMEMORY);
1370
1371         isc_buffer_init(&buffer, bufmem, initial_buffer_length);
1372
1373         result = dns_db_allrdatasets(db, node, version, now, &rdsiter);
1374         if (result != ISC_R_SUCCESS)
1375                 goto failure;
1376         result = dump_rdatasets(mctx, name, rdsiter, &ctx, &buffer, f);
1377         if (result != ISC_R_SUCCESS)
1378                 goto failure;
1379         dns_rdatasetiter_destroy(&rdsiter);
1380
1381         result = ISC_R_SUCCESS;
1382
1383  failure:
1384         isc_mem_put(mctx, buffer.base, buffer.length);
1385         return (result);
1386 }
1387
1388 isc_result_t
1389 dns_master_dumpnode(isc_mem_t *mctx, dns_db_t *db, dns_dbversion_t *version,
1390                     dns_dbnode_t *node, dns_name_t *name,
1391                     const dns_master_style_t *style, const char *filename)
1392 {
1393         FILE *f = NULL;
1394         isc_result_t result;
1395
1396         result = isc_stdio_open(filename, "w", &f);
1397         if (result != ISC_R_SUCCESS) {
1398                 isc_log_write(dns_lctx, ISC_LOGCATEGORY_GENERAL,
1399                               DNS_LOGMODULE_MASTERDUMP, ISC_LOG_ERROR,
1400                               "dumping node to file: %s: open: %s", filename,
1401                               isc_result_totext(result));
1402                 return (ISC_R_UNEXPECTED);
1403         }
1404
1405         result = dns_master_dumpnodetostream(mctx, db, version, node, name,
1406                                              style, f);
1407
1408         result = isc_stdio_close(f);
1409         if (result != ISC_R_SUCCESS) {
1410                 isc_log_write(dns_lctx, ISC_LOGCATEGORY_GENERAL,
1411                               DNS_LOGMODULE_MASTERDUMP, ISC_LOG_ERROR,
1412                               "dumping master file: %s: close: %s", filename,
1413                               isc_result_totext(result));
1414                 return (ISC_R_UNEXPECTED);
1415         }
1416
1417         return (result);
1418 }
1419
1420 isc_result_t
1421 dns_master_stylecreate(dns_master_style_t **stylep, unsigned int flags,
1422                        unsigned int ttl_column, unsigned int class_column,
1423                        unsigned int type_column, unsigned int rdata_column,
1424                        unsigned int line_length, unsigned int tab_width,
1425                        isc_mem_t *mctx)
1426 {
1427         dns_master_style_t *style;
1428
1429         REQUIRE(stylep != NULL && *stylep == NULL);
1430         style = isc_mem_get(mctx, sizeof(*style));
1431         if (style == NULL)
1432                 return (ISC_R_NOMEMORY);
1433
1434         style->flags = flags;
1435         style->ttl_column = ttl_column;
1436         style->class_column = class_column;
1437         style->type_column = type_column;
1438         style->rdata_column = rdata_column;
1439         style->line_length = line_length;
1440         style->tab_width = tab_width;
1441
1442         *stylep = style;
1443         return (ISC_R_SUCCESS);
1444 }
1445
1446 void
1447 dns_master_styledestroy(dns_master_style_t **stylep, isc_mem_t *mctx) {
1448         dns_master_style_t *style;
1449
1450         REQUIRE(stylep != NULL && *stylep != NULL);
1451         style = *stylep;
1452         *stylep = NULL;
1453         isc_mem_put(mctx, style, sizeof(*style));
1454 }
1455