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