Merge from vendor branch OPENSSL:
[dragonfly.git] / contrib / bind-9.2.4rc7 / lib / dns / masterdump.c
1 /*
2  * Copyright (C) 2004  Internet Systems Consortium, Inc. ("ISC")
3  * Copyright (C) 1999-2001, 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.6 2004/03/09 06:11:03 marka Exp $ */
19
20 #include <config.h>
21
22 #include <stdlib.h>
23
24 #include <isc/file.h>
25 #include <isc/mem.h>
26 #include <isc/stdio.h>
27 #include <isc/string.h>
28 #include <isc/util.h>
29
30 #include <dns/db.h>
31 #include <dns/dbiterator.h>
32 #include <dns/fixedname.h>
33 #include <dns/log.h>
34 #include <dns/masterdump.h>
35 #include <dns/rdata.h>
36 #include <dns/rdataclass.h>
37 #include <dns/rdataset.h>
38 #include <dns/rdatasetiter.h>
39 #include <dns/rdatatype.h>
40 #include <dns/result.h>
41 #include <dns/time.h>
42 #include <dns/ttl.h>
43
44 #define RETERR(x) do { \
45         isc_result_t _r = (x); \
46         if (_r != ISC_R_SUCCESS) \
47                 return (_r); \
48         } while (0)
49
50 struct dns_master_style {
51         unsigned int flags;             /* DNS_STYLEFLAG_* */
52         unsigned int ttl_column;
53         unsigned int class_column;
54         unsigned int type_column;
55         unsigned int rdata_column;
56         unsigned int line_length;
57         unsigned int tab_width;
58 };
59
60 /*
61  * Flags affecting master file formatting.  Flags 0x0000FFFF
62  * define the formatting of the rdata part and are defined in
63  * rdata.h.
64  */
65
66 /* Omit the owner name when possible. */
67 #define DNS_STYLEFLAG_OMIT_OWNER        0x00010000U
68
69 /*
70  * Omit the TTL when possible.  If DNS_STYLEFLAG_TTL is
71  * also set, this means no TTLs are ever printed
72  * because $TTL directives are generated before every
73  * change in the TTL.  In this case, no columns need to
74  * be reserved for the TTL.  Master files generated with
75  * these options will be rejected by BIND 4.x because it
76  * does not recognize the $TTL directive.
77  *
78  * If DNS_STYLEFLAG_TTL is not also set, the TTL will be
79  * omitted when it is equal to the previous TTL.
80  * This is correct according to RFC1035, but the
81  * TTLs may be silently misinterpreted by older
82  * versions of BIND which use the SOA MINTTL as a
83  * default TTL value.
84  */
85 #define DNS_STYLEFLAG_OMIT_TTL          0x00020000U
86
87 /* Omit the class when possible. */
88 #define DNS_STYLEFLAG_OMIT_CLASS        0x00040000U
89
90 /* Output $TTL directives. */
91 #define DNS_STYLEFLAG_TTL               0x00080000U
92
93 /*
94  * Output $ORIGIN directives and print owner names relative to
95  * the origin when possible.
96  */
97 #define DNS_STYLEFLAG_REL_OWNER         0x00100000U
98
99 /* Print domain names in RR data in relative form when possible.
100    For this to take effect, DNS_STYLEFLAG_REL_OWNER must also be set. */
101 #define DNS_STYLEFLAG_REL_DATA          0x00200000U
102
103 /* Print the trust level of each rdataset. */
104 #define DNS_STYLEFLAG_TRUST             0x00400000U
105
106 /* Print negative caching entries. */
107 #define DNS_STYLEFLAG_NCACHE            0x00800000U
108
109
110 /*
111  * The maximum length of the newline+indentation that is output
112  * when inserting a line break in an RR.  This effectively puts an
113  * upper limits on the value of "rdata_column", because if it is
114  * very large, the tabs and spaces needed to reach it will not fit.
115  */
116 #define DNS_TOTEXT_LINEBREAK_MAXLEN 100
117
118 /*
119  * Context structure for a masterfile dump in progress.
120  */
121 typedef struct dns_totext_ctx {
122         dns_master_style_t      style;
123         isc_boolean_t           class_printed;
124         char *                  linebreak;
125         char                    linebreak_buf[DNS_TOTEXT_LINEBREAK_MAXLEN];
126         dns_name_t *            origin;
127         dns_name_t *            neworigin;
128         dns_fixedname_t         origin_fixname;
129         isc_uint32_t            current_ttl;
130         isc_boolean_t           current_ttl_valid;
131 } dns_totext_ctx_t;
132
133 LIBDNS_EXTERNAL_DATA const dns_master_style_t
134 dns_master_style_default = {
135         DNS_STYLEFLAG_OMIT_OWNER |
136         DNS_STYLEFLAG_OMIT_CLASS |
137         DNS_STYLEFLAG_REL_OWNER |
138         DNS_STYLEFLAG_REL_DATA |
139         DNS_STYLEFLAG_OMIT_TTL |
140         DNS_STYLEFLAG_TTL |
141         DNS_STYLEFLAG_COMMENT |
142         DNS_STYLEFLAG_MULTILINE,
143         24, 24, 24, 32, 80, 8
144 };
145
146 LIBDNS_EXTERNAL_DATA const dns_master_style_t
147 dns_master_style_explicitttl = {
148         DNS_STYLEFLAG_OMIT_OWNER |
149         DNS_STYLEFLAG_OMIT_CLASS |
150         DNS_STYLEFLAG_REL_OWNER |
151         DNS_STYLEFLAG_REL_DATA |
152         DNS_STYLEFLAG_COMMENT |
153         DNS_STYLEFLAG_MULTILINE,
154         24, 32, 32, 40, 80, 8
155 };
156
157 const dns_master_style_t
158 dns_master_style_cache = {
159         DNS_STYLEFLAG_OMIT_OWNER |
160         DNS_STYLEFLAG_OMIT_CLASS |
161         DNS_STYLEFLAG_MULTILINE |
162         DNS_STYLEFLAG_TRUST |
163         DNS_STYLEFLAG_NCACHE,
164         24, 32, 32, 40, 80, 8
165 };
166
167 const dns_master_style_t
168 dns_master_style_simple = {
169         0,
170         24, 32, 32, 40, 80, 8
171 };
172
173
174 /*
175  * A style suitable for dns_rdataset_totext().
176  */
177 LIBDNS_EXTERNAL_DATA const dns_master_style_t
178 dns_master_style_debug = {
179         DNS_STYLEFLAG_REL_OWNER,
180         24, 32, 40, 48, 80, 8
181 };
182
183
184 #define N_SPACES 10
185 static char spaces[N_SPACES+1] = "          ";
186
187 #define N_TABS 10
188 static char tabs[N_TABS+1] = "\t\t\t\t\t\t\t\t\t\t";
189
190 #define NXDOMAIN(x) (((x)->attributes & DNS_RDATASETATTR_NXDOMAIN) != 0)
191
192 /*
193  * Output tabs and spaces to go from column '*current' to
194  * column 'to', and update '*current' to reflect the new
195  * current column.
196  */
197 static isc_result_t
198 indent(unsigned int *current, unsigned int to, int tabwidth,
199        isc_buffer_t *target)
200 {
201         isc_region_t r;
202         unsigned char *p;
203         unsigned int from;
204         int ntabs, nspaces, t;
205
206         from = *current;
207
208         if (to < from + 1)
209                 to = from + 1;
210
211         ntabs = to / tabwidth - from / tabwidth;
212         if (ntabs < 0)
213                 ntabs = 0;
214
215         if (ntabs > 0) {
216                 isc_buffer_availableregion(target, &r);
217                 if (r.length < (unsigned) ntabs)
218                         return (ISC_R_NOSPACE);
219                 p = r.base;
220
221                 t = ntabs;
222                 while (t) {
223                         int n = t;
224                         if (n > N_TABS)
225                                 n = N_TABS;
226                         memcpy(p, tabs, n);
227                         p += n;
228                         t -= n;
229                 }
230                 isc_buffer_add(target, ntabs);
231                 from = (to / tabwidth) * tabwidth;
232         }
233
234         nspaces = to - from;
235         INSIST(nspaces >= 0);
236
237         isc_buffer_availableregion(target, &r);
238         if (r.length < (unsigned) nspaces)
239                 return (ISC_R_NOSPACE);
240         p = r.base;
241
242         t = nspaces;
243         while (t) {
244                 int n = t;
245                 if (n > N_SPACES)
246                         n = N_SPACES;
247                 memcpy(p, spaces, n);
248                 p += n;
249                 t -= n;
250         }
251         isc_buffer_add(target, nspaces);
252
253         *current = to;
254         return (ISC_R_SUCCESS);
255 }
256
257 static isc_result_t
258 totext_ctx_init(const dns_master_style_t *style, dns_totext_ctx_t *ctx) {
259         isc_result_t result;
260
261         REQUIRE(style->tab_width != 0);
262
263         ctx->style = *style;
264         ctx->class_printed = ISC_FALSE;
265
266         dns_fixedname_init(&ctx->origin_fixname);
267
268         /*
269          * Set up the line break string if needed.
270          */
271         if ((ctx->style.flags & DNS_STYLEFLAG_MULTILINE) != 0) {
272                 isc_buffer_t buf;
273                 isc_region_t r;
274                 unsigned int col = 0;
275
276                 isc_buffer_init(&buf, ctx->linebreak_buf,
277                                 sizeof(ctx->linebreak_buf));
278
279                 isc_buffer_availableregion(&buf, &r);
280                 if (r.length < 1)
281                         return (DNS_R_TEXTTOOLONG);
282                 r.base[0] = '\n';
283                 isc_buffer_add(&buf, 1);
284
285                 result = indent(&col, ctx->style.rdata_column,
286                                 ctx->style.tab_width, &buf);
287                 /*
288                  * Do not return ISC_R_NOSPACE if the line break string
289                  * buffer is too small, because that would just make
290                  * dump_rdataset() retry indenfinitely with ever
291                  * bigger target buffers.  That's a different buffer,
292                  * so it won't help.  Use DNS_R_TEXTTOOLONG as a substitute.
293                  */
294                 if (result == ISC_R_NOSPACE)
295                         return (DNS_R_TEXTTOOLONG);
296                 if (result != ISC_R_SUCCESS)
297                         return (result);
298
299                 isc_buffer_availableregion(&buf, &r);
300                 if (r.length < 1)
301                         return (DNS_R_TEXTTOOLONG);
302                 r.base[0] = '\0';
303                 isc_buffer_add(&buf, 1);
304                 ctx->linebreak = ctx->linebreak_buf;
305         } else {
306                 ctx->linebreak = NULL;
307         }
308
309         ctx->origin = NULL;
310         ctx->neworigin = NULL;
311         ctx->current_ttl = 0;
312         ctx->current_ttl_valid = ISC_FALSE;
313
314         return (ISC_R_SUCCESS);
315 }
316
317 #define INDENT_TO(col) \
318         do { \
319                  if ((result = indent(&column, ctx->style.col, \
320                                       ctx->style.tab_width, target)) \
321                      != ISC_R_SUCCESS) \
322                             return (result); \
323         } while (0)
324
325
326 static isc_result_t
327 str_totext(const char *source, isc_buffer_t *target) {
328         unsigned int l;
329         isc_region_t region;
330
331         isc_buffer_availableregion(target, &region);
332         l = strlen(source);
333
334         if (l > region.length)
335                 return (ISC_R_NOSPACE);
336
337         memcpy(region.base, source, l);
338         isc_buffer_add(target, l);
339         return (ISC_R_SUCCESS);
340 }
341
342 /*
343  * Convert 'rdataset' to master file text format according to 'ctx',
344  * storing the result in 'target'.  If 'owner_name' is NULL, it
345  * is omitted; otherwise 'owner_name' must be valid and have at least
346  * one label.
347  */
348
349 static isc_result_t
350 rdataset_totext(dns_rdataset_t *rdataset,
351                 dns_name_t *owner_name,
352                 dns_totext_ctx_t *ctx,
353                 isc_boolean_t omit_final_dot,
354                 isc_buffer_t *target)
355 {
356         isc_result_t result;
357         unsigned int column;
358         isc_boolean_t first = ISC_TRUE;
359         isc_uint32_t current_ttl;
360         isc_boolean_t current_ttl_valid;
361         dns_rdatatype_t type;
362
363         REQUIRE(DNS_RDATASET_VALID(rdataset));
364
365         result = dns_rdataset_first(rdataset);
366         REQUIRE(result == ISC_R_SUCCESS);
367
368         current_ttl = ctx->current_ttl;
369         current_ttl_valid = ctx->current_ttl_valid;
370
371         do {
372                 column = 0;
373
374                 /*
375                  * Owner name.
376                  */
377                 if (owner_name != NULL &&
378                     ! ((ctx->style.flags & DNS_STYLEFLAG_OMIT_OWNER) != 0 &&
379                        !first))
380                 {
381                         unsigned int name_start = target->used;
382                         RETERR(dns_name_totext(owner_name,
383                                                omit_final_dot,
384                                                target));
385                         column += target->used - name_start;
386                 }
387
388                 /*
389                  * TTL.
390                  */
391                 if (! ((ctx->style.flags & DNS_STYLEFLAG_OMIT_TTL) != 0 &&
392                        current_ttl_valid &&
393                        rdataset->ttl == current_ttl))
394                 {
395                         char ttlbuf[64];
396                         isc_region_t r;
397                         unsigned int length;
398
399                         INDENT_TO(ttl_column);
400                         length = sprintf(ttlbuf, "%u", rdataset->ttl);
401                         INSIST(length <= sizeof ttlbuf);
402                         isc_buffer_availableregion(target, &r);
403                         if (r.length < length)
404                                 return (ISC_R_NOSPACE);
405                         memcpy(r.base, ttlbuf, length);
406                         isc_buffer_add(target, length);
407                         column += length;
408
409                         /*
410                          * If the $TTL directive is not in use, the TTL we
411                          * just printed becomes the default for subsequent RRs.
412                          */
413                         if ((ctx->style.flags & DNS_STYLEFLAG_TTL) == 0) {
414                                 current_ttl = rdataset->ttl;
415                                 current_ttl_valid = ISC_TRUE;
416                         }
417                 }
418
419                 /*
420                  * Class.
421                  */
422                 if ((ctx->style.flags & DNS_STYLEFLAG_OMIT_CLASS) == 0 ||
423                     ctx->class_printed == ISC_FALSE)
424                 {
425                         unsigned int class_start;
426                         INDENT_TO(class_column);
427                         class_start = target->used;
428                         result = dns_rdataclass_totext(rdataset->rdclass,
429                                                        target);
430                         if (result != ISC_R_SUCCESS)
431                                 return (result);
432                         column += (target->used - class_start);
433                 }
434
435                 /*
436                  * Type.
437                  */
438
439                 if (rdataset->type == 0) {
440                         type = rdataset->covers;
441                 } else {
442                         type = rdataset->type;
443                 }
444
445                 {
446                         unsigned int type_start;
447                         INDENT_TO(type_column);
448                         type_start = target->used;
449                         if (rdataset->type == 0)
450                                 RETERR(str_totext("\\-", target));
451                         result = dns_rdatatype_totext(type, target);
452                         if (result != ISC_R_SUCCESS)
453                                 return (result);
454                         column += (target->used - type_start);
455                 }
456
457                 /*
458                  * Rdata.
459                  */
460                 INDENT_TO(rdata_column);
461                 if (rdataset->type == 0) {
462                         if (NXDOMAIN(rdataset))
463                                 RETERR(str_totext(";-$NXDOMAIN\n", target));
464                         else
465                                 RETERR(str_totext(";-$NXRRSET\n", target));
466                 } else {
467                         dns_rdata_t rdata = DNS_RDATA_INIT;
468                         isc_region_t r;
469
470                         dns_rdataset_current(rdataset, &rdata);
471
472                         RETERR(dns_rdata_tofmttext(&rdata,
473                                                    ctx->origin,
474                                                    ctx->style.flags,
475                                                    ctx->style.line_length -
476                                                        ctx->style.rdata_column,
477                                                    ctx->linebreak,
478                                                    target));
479
480                         isc_buffer_availableregion(target, &r);
481                         if (r.length < 1)
482                                 return (ISC_R_NOSPACE);
483                         r.base[0] = '\n';
484                         isc_buffer_add(target, 1);
485                 }
486
487                 first = ISC_FALSE;
488                 result = dns_rdataset_next(rdataset);
489         } while (result == ISC_R_SUCCESS);
490
491         if (result != ISC_R_NOMORE)
492                 return (result);
493
494         /*
495          * Update the ctx state to reflect what we just printed.
496          * This is done last, only when we are sure we will return
497          * success, because this function may be called multiple
498          * times with increasing buffer sizes until it succeeds,
499          * and failed attempts must not update the state prematurely.
500          */
501         ctx->class_printed = ISC_TRUE;
502         ctx->current_ttl= current_ttl;
503         ctx->current_ttl_valid = current_ttl_valid;
504
505         return (ISC_R_SUCCESS);
506 }
507
508 /*
509  * Print the name, type, and class of an empty rdataset,
510  * such as those used to represent the question section
511  * of a DNS message.
512  */
513 static isc_result_t
514 question_totext(dns_rdataset_t *rdataset,
515                 dns_name_t *owner_name,
516                 dns_totext_ctx_t *ctx,
517                 isc_boolean_t omit_final_dot,
518                 isc_buffer_t *target)
519 {
520         unsigned int column;
521         isc_result_t result;
522         isc_region_t r;
523
524         REQUIRE(DNS_RDATASET_VALID(rdataset));
525         result = dns_rdataset_first(rdataset);
526         REQUIRE(result == ISC_R_NOMORE);
527
528         column = 0;
529
530         /* Owner name */
531         {
532                 unsigned int name_start = target->used;
533                 RETERR(dns_name_totext(owner_name,
534                                        omit_final_dot,
535                                        target));
536                 column += target->used - name_start;
537         }
538
539         /* Class */
540         {
541                 unsigned int class_start;
542                 INDENT_TO(class_column);
543                 class_start = target->used;
544                 result = dns_rdataclass_totext(rdataset->rdclass, target);
545                 if (result != ISC_R_SUCCESS)
546                         return (result);
547                 column += (target->used - class_start);
548         }
549
550         /* Type */
551         {
552                 unsigned int type_start;
553                 INDENT_TO(type_column);
554                 type_start = target->used;
555                 result = dns_rdatatype_totext(rdataset->type, target);
556                 if (result != ISC_R_SUCCESS)
557                         return (result);
558                 column += (target->used - type_start);
559         }
560
561         isc_buffer_availableregion(target, &r);
562         if (r.length < 1)
563                 return (ISC_R_NOSPACE);
564         r.base[0] = '\n';
565         isc_buffer_add(target, 1);
566
567         return (ISC_R_SUCCESS);
568 }
569
570 isc_result_t
571 dns_rdataset_totext(dns_rdataset_t *rdataset,
572                     dns_name_t *owner_name,
573                     isc_boolean_t omit_final_dot,
574                     isc_boolean_t question,
575                     isc_buffer_t *target)
576 {
577         dns_totext_ctx_t ctx;
578         isc_result_t result;
579         result = totext_ctx_init(&dns_master_style_debug, &ctx);
580         if (result != ISC_R_SUCCESS) {
581                 UNEXPECTED_ERROR(__FILE__, __LINE__,
582                                  "could not set master file style");
583                 return (ISC_R_UNEXPECTED);
584         }
585
586         /*
587          * The caller might want to give us an empty owner
588          * name (e.g. if they are outputting into a master
589          * file and this rdataset has the same name as the
590          * previous one.)
591          */
592         if (dns_name_countlabels(owner_name) == 0)
593                 owner_name = NULL;
594
595         if (question)
596                 return (question_totext(rdataset, owner_name, &ctx,
597                                         omit_final_dot, target));
598         else
599                 return (rdataset_totext(rdataset, owner_name, &ctx,
600                                         omit_final_dot, target));
601 }
602
603 isc_result_t
604 dns_master_rdatasettotext(dns_name_t *owner_name,
605                           dns_rdataset_t *rdataset,
606                           const dns_master_style_t *style,
607                           isc_buffer_t *target)
608 {
609         dns_totext_ctx_t ctx;
610         isc_result_t result;
611         result = totext_ctx_init(style, &ctx);
612         if (result != ISC_R_SUCCESS) {
613                 UNEXPECTED_ERROR(__FILE__, __LINE__,
614                                  "could not set master file style");
615                 return (ISC_R_UNEXPECTED);
616         }
617
618         return (rdataset_totext(rdataset, owner_name, &ctx,
619                                 ISC_FALSE, target));
620 }
621
622 isc_result_t
623 dns_master_questiontotext(dns_name_t *owner_name,
624                           dns_rdataset_t *rdataset,
625                           const dns_master_style_t *style,
626                           isc_buffer_t *target)
627 {
628         dns_totext_ctx_t ctx;
629         isc_result_t result;
630         result = totext_ctx_init(style, &ctx);
631         if (result != ISC_R_SUCCESS) {
632                 UNEXPECTED_ERROR(__FILE__, __LINE__,
633                                  "could not set master file style");
634                 return (ISC_R_UNEXPECTED);
635         }
636
637         return (question_totext(rdataset, owner_name, &ctx,
638                                 ISC_FALSE, target));
639 }
640
641 /*
642  * Print an rdataset.  'buffer' is a scratch buffer, which must have been
643  * dynamically allocated by the caller.  It must be large enough to
644  * hold the result from dns_ttl_totext().  If more than that is needed,
645  * the buffer will be grown automatically.
646  */
647
648 static isc_result_t
649 dump_rdataset(isc_mem_t *mctx, dns_name_t *name, dns_rdataset_t *rdataset,
650               dns_totext_ctx_t *ctx,
651               isc_buffer_t *buffer, FILE *f)
652 {
653         isc_region_t r;
654         isc_result_t result;
655
656         REQUIRE(buffer->length > 0);
657
658         /*
659          * Output a $TTL directive if needed.
660          */
661
662         if ((ctx->style.flags & DNS_STYLEFLAG_TTL) != 0) {
663                 if (ctx->current_ttl_valid == ISC_FALSE ||
664                     ctx->current_ttl != rdataset->ttl)
665                 {
666                         if ((ctx->style.flags & DNS_STYLEFLAG_COMMENT) != 0)
667                         {
668                                 isc_buffer_clear(buffer);
669                                 result = dns_ttl_totext(rdataset->ttl,
670                                                         ISC_TRUE, buffer);
671                                 INSIST(result == ISC_R_SUCCESS);
672                                 isc_buffer_usedregion(buffer, &r);
673                                 fprintf(f, "$TTL %u\t; %.*s\n", rdataset->ttl,
674                                         (int) r.length, (char *) r.base);
675                         } else {
676                                 fprintf(f, "$TTL %u\n", rdataset->ttl);
677                         }
678                         ctx->current_ttl = rdataset->ttl;
679                         ctx->current_ttl_valid = ISC_TRUE;
680                 }
681         }
682
683         isc_buffer_clear(buffer);
684
685         /*
686          * Generate the text representation of the rdataset into
687          * the buffer.  If the buffer is too small, grow it.
688          */
689         for (;;) {
690                 int newlength;
691                 void *newmem;
692                 result = rdataset_totext(rdataset, name, ctx,
693                                          ISC_FALSE, buffer);
694                 if (result != ISC_R_NOSPACE)
695                         break;
696
697                 newlength = buffer->length * 2;
698                 newmem = isc_mem_get(mctx, newlength);
699                 if (newmem == NULL)
700                         return (ISC_R_NOMEMORY);
701                 isc_mem_put(mctx, buffer->base, buffer->length);
702                 isc_buffer_init(buffer, newmem, newlength);
703         }
704         if (result != ISC_R_SUCCESS)
705                 return (result);
706
707         /*
708          * Write the buffer contents to the master file.
709          */
710         isc_buffer_usedregion(buffer, &r);
711         result = isc_stdio_write(r.base, 1, (size_t)r.length, f, NULL);
712
713         if (result != ISC_R_SUCCESS) {
714                 UNEXPECTED_ERROR(__FILE__, __LINE__,
715                                  "master file write failed: %s",
716                                  isc_result_totext(result));
717                 return (result);
718         }
719
720         return (ISC_R_SUCCESS);
721 }
722
723 /*
724  * Define the order in which rdatasets should be printed in zone
725  * files.  We will print SOA and NS records before others, SIGs
726  * immediately following the things they sign, and order everything
727  * else by RR number.  This is all just for aesthetics and
728  * compatibility with buggy software that expects the SOA to be first;
729  * the DNS specifications allow any order.
730  */
731
732 static int
733 dump_order(const dns_rdataset_t *rds) {
734         int t;
735         int sig;
736         if (rds->type == dns_rdatatype_sig) {
737                 t = rds->covers;
738                 sig = 1;
739         } else {
740                 t = rds->type;
741                 sig = 0;
742         }
743         switch (t) {
744         case dns_rdatatype_soa:
745                 t = 0;
746                 break;
747         case dns_rdatatype_ns:
748                 t = 1;
749                 break;
750         default:
751                 t += 2;
752                 break;
753         }
754         return (t << 1) + sig;
755 }
756
757 static int
758 dump_order_compare(const void *a, const void *b) {
759         return (dump_order(*((const dns_rdataset_t * const *) a)) -
760                 dump_order(*((const dns_rdataset_t * const *) b)));
761 }
762
763 /*
764  * Dump all the rdatasets of a domain name to a master file.  We make
765  * a "best effort" attempt to sort the RRsets in a nice order, but if
766  * there are more than MAXSORT RRsets, we punt and only sort them in
767  * groups of MAXSORT.  This is not expected to ever happen in practice
768  * since much less than 64 RR types have been registered with the
769  * IANA, so far, and the output will be correct (though not
770  * aesthetically pleasing) even if it does happen.
771  */
772
773 #define MAXSORT 64
774
775 static const char *trustnames[] = {
776         "none",
777         "pending",
778         "additional",
779         "glue",
780         "answer",
781         "authauthority",
782         "authanswer",
783         "secure",
784         "local" /* aka ultimate */
785 };
786
787 static isc_result_t
788 dump_rdatasets(isc_mem_t *mctx, dns_name_t *name, dns_rdatasetiter_t *rdsiter,
789                dns_totext_ctx_t *ctx,
790                isc_buffer_t *buffer, FILE *f)
791 {
792         isc_result_t itresult, dumpresult;
793         isc_region_t r;
794         dns_rdataset_t rdatasets[MAXSORT];
795         dns_rdataset_t *sorted[MAXSORT];
796         int i, n;
797
798         itresult = dns_rdatasetiter_first(rdsiter);
799         dumpresult = ISC_R_SUCCESS;
800
801         if (itresult == ISC_R_SUCCESS && ctx->neworigin != NULL) {
802                 isc_buffer_clear(buffer);
803                 itresult = dns_name_totext(ctx->neworigin, ISC_FALSE, buffer);
804                 RUNTIME_CHECK(itresult == ISC_R_SUCCESS);
805                 isc_buffer_usedregion(buffer, &r);
806                 fprintf(f, "$ORIGIN %.*s\n", (int) r.length, (char *) r.base);
807                 ctx->neworigin = NULL;
808         }
809
810  again:
811         for (i = 0;
812              itresult == ISC_R_SUCCESS && i < MAXSORT;
813              itresult = dns_rdatasetiter_next(rdsiter), i++) {
814                 dns_rdataset_init(&rdatasets[i]);
815                 dns_rdatasetiter_current(rdsiter, &rdatasets[i]);
816                 sorted[i] = &rdatasets[i];
817         }
818         n = i;
819         INSIST(n <= MAXSORT);
820
821         qsort(sorted, n, sizeof(sorted[0]), dump_order_compare);
822
823         for (i = 0; i < n; i++) {
824                 dns_rdataset_t *rds = sorted[i];
825                 if (ctx->style.flags & DNS_STYLEFLAG_TRUST) {
826                         unsigned int trust = rds->trust;
827                         INSIST(trust < (sizeof(trustnames) /
828                                         sizeof(trustnames[0])));
829                         fprintf(f, "; %s\n", trustnames[trust]);
830                 }
831                 if (rds->type == 0 &&
832                     (ctx->style.flags & DNS_STYLEFLAG_NCACHE) == 0) {
833                         /* Omit negative cache entries */
834                 } else {
835                         isc_result_t result =
836                                 dump_rdataset(mctx, name, rds, ctx,
837                                                buffer, f);
838                         if (result != ISC_R_SUCCESS)
839                                 dumpresult = result;
840                         if ((ctx->style.flags & DNS_STYLEFLAG_OMIT_OWNER) != 0)
841                                 name = NULL;
842                 }
843                 dns_rdataset_disassociate(rds);
844         }
845
846         if (dumpresult != ISC_R_SUCCESS)
847                 return (dumpresult);
848
849         /*
850          * If we got more data than could be sorted at once,
851          * go handle the rest.
852          */
853         if (itresult == ISC_R_SUCCESS)
854                 goto again;
855
856         if (itresult == ISC_R_NOMORE)
857                 itresult = ISC_R_SUCCESS;
858
859         return (itresult);
860 }
861
862
863 /*
864  * Initial size of text conversion buffer.  The buffer is used
865  * for several purposes: converting origin names, rdatasets,
866  * $DATE timestamps, and comment strings for $TTL directives.
867  *
868  * When converting rdatasets, it is dynamically resized, but
869  * when converting origins, timestamps, etc it is not.  Therefore,
870  * the  initial size must large enough to hold the longest possible
871  * text representation of any domain name (for $ORIGIN).
872  */
873 static const int initial_buffer_length = 1200;
874
875 /*
876  * Dump an entire database into a master file.
877  */
878 isc_result_t
879 dns_master_dumptostream(isc_mem_t *mctx, dns_db_t *db,
880                         dns_dbversion_t *version,
881                         const dns_master_style_t *style,
882                         FILE *f)
883 {
884         dns_fixedname_t fixname;
885         dns_name_t *name;
886         dns_dbiterator_t *dbiter = NULL;
887         isc_result_t result;
888         isc_buffer_t buffer;
889         char *bufmem;
890         isc_stdtime_t now;
891         isc_region_t r;
892         dns_totext_ctx_t ctx;
893
894         result = totext_ctx_init(style, &ctx);
895         if (result != ISC_R_SUCCESS) {
896                 UNEXPECTED_ERROR(__FILE__, __LINE__,
897                                  "could not set master file style");
898                 return (ISC_R_UNEXPECTED);
899         }
900
901         dns_fixedname_init(&fixname);
902         name = dns_fixedname_name(&fixname);
903
904         isc_stdtime_get(&now);
905
906         bufmem = isc_mem_get(mctx, initial_buffer_length);
907         if (bufmem == NULL)
908                 return (ISC_R_NOMEMORY);
909
910         isc_buffer_init(&buffer, bufmem, initial_buffer_length);
911
912         /*
913          * If the database has cache semantics, output an RFC2540
914          * $DATE directive so that the TTLs can be adjusted when
915          * it is reloaded.  For zones it is not really needed, and
916          * it would make the file incompatible with pre-RFC2540
917          * software, so we omit it in the zone case.
918          */
919         if (dns_db_iscache(db)) {
920                 result = dns_time32_totext(now, &buffer);
921                 RUNTIME_CHECK(result == ISC_R_SUCCESS);
922                 isc_buffer_usedregion(&buffer, &r);
923                 fprintf(f, "$DATE %.*s\n", (int) r.length, (char *) r.base);
924         }
925
926         result = dns_db_createiterator(db,
927                        ((ctx.style.flags & DNS_STYLEFLAG_REL_OWNER) != 0) ?
928                            ISC_TRUE : ISC_FALSE,
929                        &dbiter);
930         if (result != ISC_R_SUCCESS)
931                 goto create_iter_failure;
932
933         result = dns_dbiterator_first(dbiter);
934
935         while (result == ISC_R_SUCCESS) {
936                 dns_rdatasetiter_t *rdsiter = NULL;
937                 dns_dbnode_t *node = NULL;
938                 result = dns_dbiterator_current(dbiter, &node, name);
939                 if (result != ISC_R_SUCCESS && result != DNS_R_NEWORIGIN)
940                         break;
941                 if (result == DNS_R_NEWORIGIN) {
942                         dns_name_t *origin =
943                                 dns_fixedname_name(&ctx.origin_fixname);
944                         result = dns_dbiterator_origin(dbiter, origin);
945                         RUNTIME_CHECK(result == ISC_R_SUCCESS);
946                         if ((ctx.style.flags & DNS_STYLEFLAG_REL_DATA) != 0)
947                                 ctx.origin = origin;
948                         ctx.neworigin = origin;
949                 }
950                 result = dns_db_allrdatasets(db, node, version, now, &rdsiter);
951                 if (result != ISC_R_SUCCESS) {
952                         dns_db_detachnode(db, &node);
953                         goto iter_failure;
954                 }
955                 result = dump_rdatasets(mctx, name, rdsiter, &ctx,
956                                         &buffer, f);
957                 if (result != ISC_R_SUCCESS) {
958                         dns_db_detachnode(db, &node);
959                         goto iter_failure;
960                 }
961                 dns_rdatasetiter_destroy(&rdsiter);
962                 dns_db_detachnode(db, &node);
963                 result = dns_dbiterator_next(dbiter);
964         }
965         if (result != ISC_R_NOMORE)
966                 goto iter_failure;
967
968         result = ISC_R_SUCCESS;
969
970  iter_failure:
971         dns_dbiterator_destroy(&dbiter);
972
973  create_iter_failure:
974         isc_mem_put(mctx, buffer.base, buffer.length);
975         return (result);
976 }
977
978
979 isc_result_t
980 dns_master_dump(isc_mem_t *mctx, dns_db_t *db, dns_dbversion_t *version,
981                 const dns_master_style_t *style, const char *filename)
982 {
983         FILE *f = NULL;
984         isc_result_t result;
985         char *tempname;
986         int tempnamelen;
987
988         tempnamelen = strlen(filename) + 20;
989         tempname = isc_mem_get(mctx, tempnamelen);
990         if (tempname == NULL)
991                 return (ISC_R_NOMEMORY);
992
993         result = isc_file_mktemplate(filename, tempname, tempnamelen);
994         if (result != ISC_R_SUCCESS)
995                 goto cleanup;
996
997         result = isc_file_openunique(tempname, &f);
998         if (result != ISC_R_SUCCESS) {
999                 isc_log_write(dns_lctx, ISC_LOGCATEGORY_GENERAL,
1000                               DNS_LOGMODULE_MASTERDUMP, ISC_LOG_ERROR,
1001                               "dumping master file: %s: open: %s",
1002                               tempname, isc_result_totext(result));
1003                 goto cleanup;
1004         }
1005
1006         result = dns_master_dumptostream(mctx, db, version, style, f);
1007         if (result != ISC_R_SUCCESS) {
1008                 isc_log_write(dns_lctx, ISC_LOGCATEGORY_GENERAL,
1009                               DNS_LOGMODULE_MASTERDUMP, ISC_LOG_ERROR,
1010                               "dumping master file: %s: %s",
1011                               tempname, isc_result_totext(result));
1012                 (void)isc_stdio_close(f);
1013                 (void)isc_file_remove(tempname);
1014                 goto cleanup;
1015         }
1016
1017         result = isc_stdio_sync(f);
1018         if (result != ISC_R_SUCCESS) {
1019                 isc_log_write(dns_lctx, ISC_LOGCATEGORY_GENERAL,
1020                               DNS_LOGMODULE_MASTERDUMP, ISC_LOG_ERROR,
1021                               "dumping master file: %s: fsync: %s",
1022                               tempname, isc_result_totext(result));
1023                 (void)isc_stdio_close(f);
1024                 (void)isc_file_remove(tempname);
1025                 goto cleanup;
1026         }
1027
1028         result = isc_stdio_close(f);
1029         if (result != ISC_R_SUCCESS) {
1030                 isc_log_write(dns_lctx, ISC_LOGCATEGORY_GENERAL,
1031                               DNS_LOGMODULE_MASTERDUMP, ISC_LOG_ERROR,
1032                               "dumping master file: %s: close: %s",
1033                               tempname, isc_result_totext(result));
1034                 (void)isc_file_remove(tempname);
1035                 goto cleanup;
1036         }
1037
1038         result = isc_file_rename(tempname, filename);
1039         if (result != ISC_R_SUCCESS) {
1040                 isc_log_write(dns_lctx, ISC_LOGCATEGORY_GENERAL,
1041                               DNS_LOGMODULE_MASTERDUMP, ISC_LOG_ERROR,
1042                               "dumping master file: rename: %s: %s",
1043                               filename, isc_result_totext(result));
1044                 goto cleanup;           
1045         }
1046
1047  cleanup:
1048         isc_mem_put(mctx, tempname, tempnamelen);
1049         return (result);
1050 }
1051
1052 /*
1053  * Dump a database node into a master file.
1054  */
1055 isc_result_t
1056 dns_master_dumpnodetostream(isc_mem_t *mctx, dns_db_t *db,
1057                             dns_dbversion_t *version,
1058                             dns_dbnode_t *node, dns_name_t *name,
1059                             const dns_master_style_t *style,
1060                             FILE *f)
1061 {
1062         isc_result_t result;
1063         isc_buffer_t buffer;
1064         char *bufmem;
1065         isc_stdtime_t now;
1066         dns_totext_ctx_t ctx;
1067         dns_rdatasetiter_t *rdsiter = NULL;
1068
1069         result = totext_ctx_init(style, &ctx);
1070         if (result != ISC_R_SUCCESS) {
1071                 UNEXPECTED_ERROR(__FILE__, __LINE__,
1072                                  "could not set master file style");
1073                 return (ISC_R_UNEXPECTED);
1074         }
1075
1076         isc_stdtime_get(&now);
1077
1078         bufmem = isc_mem_get(mctx, initial_buffer_length);
1079         if (bufmem == NULL)
1080                 return (ISC_R_NOMEMORY);
1081
1082         isc_buffer_init(&buffer, bufmem, initial_buffer_length);
1083
1084         result = dns_db_allrdatasets(db, node, version, now, &rdsiter);
1085         if (result != ISC_R_SUCCESS)
1086                 goto failure;
1087         result = dump_rdatasets(mctx, name, rdsiter, &ctx, &buffer, f);
1088         if (result != ISC_R_SUCCESS)
1089                 goto failure;
1090         dns_rdatasetiter_destroy(&rdsiter);
1091
1092         result = ISC_R_SUCCESS;
1093
1094  failure:
1095         isc_mem_put(mctx, buffer.base, buffer.length);
1096         return (result);
1097 }
1098
1099 isc_result_t
1100 dns_master_dumpnode(isc_mem_t *mctx, dns_db_t *db, dns_dbversion_t *version,
1101                     dns_dbnode_t *node, dns_name_t *name,
1102                     const dns_master_style_t *style, const char *filename)
1103 {
1104         FILE *f = NULL;
1105         isc_result_t result;
1106
1107         result = isc_stdio_open(filename, "w", &f);
1108         if (result != ISC_R_SUCCESS) {
1109                 isc_log_write(dns_lctx, ISC_LOGCATEGORY_GENERAL,
1110                               DNS_LOGMODULE_MASTERDUMP, ISC_LOG_ERROR,
1111                               "dumping node to file: %s: open: %s", filename,
1112                               isc_result_totext(result));
1113                 return (ISC_R_UNEXPECTED);
1114         }
1115
1116         result = dns_master_dumpnodetostream(mctx, db, version, node, name,
1117                                              style, f);
1118
1119         result = isc_stdio_close(f);
1120         if (result != ISC_R_SUCCESS) {
1121                 isc_log_write(dns_lctx, ISC_LOGCATEGORY_GENERAL,
1122                               DNS_LOGMODULE_MASTERDUMP, ISC_LOG_ERROR,
1123                               "dumping master file: %s: close: %s", filename,
1124                               isc_result_totext(result));
1125                 return (ISC_R_UNEXPECTED);
1126         }
1127
1128         return (result);
1129 }