masterdump.c 48.7 KB
Newer Older
1
/*
Automatic Updater's avatar
Automatic Updater committed
2
 * Copyright (C) 2004-2009, 2011  Internet Systems Consortium, Inc. ("ISC")
Mark Andrews's avatar
Mark Andrews committed
3
 * Copyright (C) 1999-2003  Internet Software Consortium.
4
 *
Automatic Updater's avatar
Automatic Updater committed
5
 * Permission to use, copy, modify, and/or distribute this software for any
6 7
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
8
 *
Mark Andrews's avatar
Mark Andrews committed
9 10 11 12 13 14 15
 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
 * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 * PERFORMANCE OF THIS SOFTWARE.
16 17
 */

Mark Andrews's avatar
Mark Andrews committed
18
/* $Id$ */
19 20

/*! \file */
David Lawrence's avatar
David Lawrence committed
21

22 23
#include <config.h>

24 25
#include <stdlib.h>

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>
Bob Halley's avatar
Bob Halley committed
35
#include <isc/util.h>
36

37 38
#include <dns/db.h>
#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 49
#include <dns/rdataset.h>
#include <dns/rdatasetiter.h>
50 51
#include <dns/rdatatype.h>
#include <dns/result.h>
52 53 54
#include <dns/time.h>
#include <dns/ttl.h>

55 56 57
#define DNS_DCTX_MAGIC		ISC_MAGIC('D', 'c', 't', 'x')
#define DNS_DCTX_VALID(d)	ISC_MAGIC_VALID(d, DNS_DCTX_MAGIC)

58
#define RETERR(x) do { \
59 60 61
	isc_result_t _r = (x); \
	if (_r != ISC_R_SUCCESS) \
		return (_r); \
62 63
	} while (0)

64 65 66 67 68
#define CHECK(x) do { \
	if ((x) != ISC_R_SUCCESS) \
		goto cleanup; \
	} while (0)

69 70 71 72 73 74 75 76
struct dns_master_style {
	unsigned int flags;		/* DNS_STYLEFLAG_* */
	unsigned int ttl_column;
	unsigned int class_column;
	unsigned int type_column;
	unsigned int rdata_column;
	unsigned int line_length;
	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 84 85 86 87
 * upper limits on the value of "rdata_column", because if it is
 * very large, the tabs and spaces needed to reach it will not fit.
 */
#define DNS_TOTEXT_LINEBREAK_MAXLEN 100

88
/*%
89 90 91 92 93
 * Context structure for a masterfile dump in progress.
 */
typedef struct dns_totext_ctx {
	dns_master_style_t	style;
	isc_boolean_t 		class_printed;
94 95 96
	char *			linebreak;
	char 			linebreak_buf[DNS_TOTEXT_LINEBREAK_MAXLEN];
	dns_name_t *		origin;
97
	dns_name_t *		neworigin;
98 99 100 101 102
	dns_fixedname_t		origin_fixname;
	isc_uint32_t 		current_ttl;
	isc_boolean_t 		current_ttl_valid;
} dns_totext_ctx_t;

Danny Mayer's avatar
Danny Mayer committed
103
LIBDNS_EXTERNAL_DATA const dns_master_style_t
104 105 106 107 108 109 110 111
dns_master_style_default = {
	DNS_STYLEFLAG_OMIT_OWNER |
	DNS_STYLEFLAG_OMIT_CLASS |
	DNS_STYLEFLAG_REL_OWNER |
	DNS_STYLEFLAG_REL_DATA |
	DNS_STYLEFLAG_OMIT_TTL |
	DNS_STYLEFLAG_TTL |
	DNS_STYLEFLAG_COMMENT |
112
	DNS_STYLEFLAG_RRCOMMENT |
113
	DNS_STYLEFLAG_MULTILINE,
114
	24, 24, 24, 32, 80, 8, UINT_MAX
115 116
};

117 118
LIBDNS_EXTERNAL_DATA const dns_master_style_t
dns_master_style_full = {
119 120
	DNS_STYLEFLAG_COMMENT |
	DNS_STYLEFLAG_RESIGN,
121
	46, 46, 46, 64, 120, 8, UINT_MAX
122 123
};

Danny Mayer's avatar
Danny Mayer committed
124
LIBDNS_EXTERNAL_DATA const dns_master_style_t
125 126 127 128 129 130
dns_master_style_explicitttl = {
	DNS_STYLEFLAG_OMIT_OWNER |
	DNS_STYLEFLAG_OMIT_CLASS |
	DNS_STYLEFLAG_REL_OWNER |
	DNS_STYLEFLAG_REL_DATA |
	DNS_STYLEFLAG_COMMENT |
131
	DNS_STYLEFLAG_RRCOMMENT |
132
	DNS_STYLEFLAG_MULTILINE,
133
	24, 32, 32, 40, 80, 8, UINT_MAX
134 135
};

136
LIBDNS_EXTERNAL_DATA const dns_master_style_t
Brian Wellington's avatar
Brian Wellington committed
137 138 139
dns_master_style_cache = {
	DNS_STYLEFLAG_OMIT_OWNER |
	DNS_STYLEFLAG_OMIT_CLASS |
140 141 142
	DNS_STYLEFLAG_MULTILINE |
	DNS_STYLEFLAG_TRUST |
	DNS_STYLEFLAG_NCACHE,
143
	24, 32, 32, 40, 80, 8, UINT_MAX
Brian Wellington's avatar
Brian Wellington committed
144 145
};

146 147
LIBDNS_EXTERNAL_DATA const dns_master_style_t
dns_master_style_simple = {
Michael Sawyer's avatar
Michael Sawyer committed
148
	0,
149
	24, 32, 32, 40, 80, 8, UINT_MAX
Michael Sawyer's avatar
Michael Sawyer committed
150 151
};

152
/*%
153 154
 * A style suitable for dns_rdataset_totext().
 */
Danny Mayer's avatar
Danny Mayer committed
155
LIBDNS_EXTERNAL_DATA const dns_master_style_t
156
dns_master_style_debug = {
Brian Wellington's avatar
Brian Wellington committed
157
	DNS_STYLEFLAG_REL_OWNER,
158
	24, 32, 40, 48, 80, 8, UINT_MAX
159 160 161 162
};


#define N_SPACES 10
163
static char spaces[N_SPACES+1] = "          ";
164 165

#define N_TABS 10
166
static char tabs[N_TABS+1] = "\t\t\t\t\t\t\t\t\t\t";
167

168
#ifdef BIND9
169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189
struct dns_dumpctx {
	unsigned int		magic;
	isc_mem_t		*mctx;
	isc_mutex_t		lock;
	unsigned int		references;
	isc_boolean_t		canceled;
	isc_boolean_t		first;
	isc_boolean_t		do_date;
	isc_stdtime_t		now;
	FILE			*f;
	dns_db_t		*db;
	dns_dbversion_t		*version;
	dns_dbiterator_t	*dbiter;
	dns_totext_ctx_t	tctx;
	isc_task_t		*task;
	dns_dumpdonefunc_t	done;
	void			*done_arg;
	unsigned int		nodes;
	/* dns_master_dumpinc() */
	char			*file;
	char 			*tmpfile;
190
	dns_masterformat_t	format;
191
	dns_masterrawheader_t	header;
192 193 194 195
	isc_result_t		(*dumpsets)(isc_mem_t *mctx, dns_name_t *name,
					    dns_rdatasetiter_t *rdsiter,
					    dns_totext_ctx_t *ctx,
					    isc_buffer_t *buffer, FILE *f);
196
};
197
#endif /* BIND9 */
198

Automatic Updater's avatar
Automatic Updater committed
199
#define NXDOMAIN(x) (((x)->attributes & DNS_RDATASETATTR_NXDOMAIN) != 0)
200

201
/*%
202
 * Output tabs and spaces to go from column '*current' to
203 204 205
 * column 'to', and update '*current' to reflect the new
 * current column.
 */
206
static isc_result_t
Bob Halley's avatar
Bob Halley committed
207 208 209
indent(unsigned int *current, unsigned int to, int tabwidth,
       isc_buffer_t *target)
{
210 211
	isc_region_t r;
	unsigned char *p;
Bob Halley's avatar
lint  
Bob Halley committed
212 213
	unsigned int from;
	int ntabs, nspaces, t;
214 215 216 217 218 219 220

	from = *current;

	if (to < from + 1)
		to = from + 1;

	ntabs = to / tabwidth - from / tabwidth;
221
	if (ntabs < 0)
222 223 224
		ntabs = 0;

	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 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245
		t = ntabs;
		while (t) {
			int n = t;
			if (n > N_TABS)
				n = N_TABS;
			memcpy(p, tabs, n);
			p += n;
			t -= n;
		}
		isc_buffer_add(target, ntabs);
		from = (to / tabwidth) * tabwidth;
	}

	nspaces = to - from;
	INSIST(nspaces >= 0);

246
	isc_buffer_availableregion(target, &r);
247
	if (r.length < (unsigned) nspaces)
248
		return (ISC_R_NOSPACE);
249 250
	p = r.base;

251
	t = nspaces;
252 253 254 255 256 257 258 259
	while (t) {
		int n = t;
		if (n > N_SPACES)
			n = N_SPACES;
		memcpy(p, spaces, n);
		p += n;
		t -= n;
	}
260
	isc_buffer_add(target, nspaces);
261 262

	*current = to;
263
	return (ISC_R_SUCCESS);
264 265
}

266
static isc_result_t
267
totext_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 273

	ctx->style = *style;
	ctx->class_printed = ISC_FALSE;
274

275 276
	dns_fixedname_init(&ctx->origin_fixname);

277 278 279
	/*
	 * Set up the line break string if needed.
	 */
280 281 282
	if ((ctx->style.flags & DNS_STYLEFLAG_MULTILINE) != 0) {
		isc_buffer_t buf;
		isc_region_t r;
Bob Halley's avatar
Bob Halley committed
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)
Bob Halley's avatar
Bob Halley committed
290
			return (DNS_R_TEXTTOOLONG);
291 292 293
		r.base[0] = '\n';
		isc_buffer_add(&buf, 1);

294
		result = indent(&col, ctx->style.rdata_column,
295 296
				ctx->style.tab_width, &buf);
		/*
297
		 * Do not return ISC_R_NOSPACE if the line break string
298
		 * buffer is too small, because that would just make
Francis Dupont's avatar
Francis Dupont committed
299
		 * dump_rdataset() retry indefinitely with ever
300
		 * bigger target buffers.  That's a different buffer,
Bob Halley's avatar
Bob Halley committed
301
		 * so it won't help.  Use DNS_R_TEXTTOOLONG as a substitute.
302
		 */
303
		if (result == ISC_R_NOSPACE)
Bob Halley's avatar
Bob Halley committed
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)
Bob Halley's avatar
Bob Halley committed
310
			return (DNS_R_TEXTTOOLONG);
311 312
		r.base[0] = '\0';
		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 321
	ctx->current_ttl = 0;
	ctx->current_ttl_valid = ISC_FALSE;
322

323
	return (ISC_R_SUCCESS);
324 325 326 327 328 329
}

#define INDENT_TO(col) \
	do { \
		 if ((result = indent(&column, ctx->style.col, \
				      ctx->style.tab_width, target)) \
330
		     != ISC_R_SUCCESS) \
331
			    return (result); \
Brian Wellington's avatar
Brian Wellington committed
332
	} while (0)
333 334


335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350
static isc_result_t
str_totext(const char *source, isc_buffer_t *target) {
	unsigned int l;
	isc_region_t region;

	isc_buffer_availableregion(target, &region);
	l = strlen(source);

	if (l > region.length)
		return (ISC_R_NOSPACE);

	memcpy(region.base, source, l);
	isc_buffer_add(target, l);
	return (ISC_R_SUCCESS);
}

351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379
static isc_result_t
ncache_summary(dns_rdataset_t *rdataset, isc_boolean_t omit_final_dot,
	       isc_buffer_t *target)
{
	isc_result_t result = ISC_R_SUCCESS;
	dns_rdataset_t rds;
	dns_name_t name;

	dns_rdataset_init(&rds);
	dns_name_init(&name, NULL);

	do {
		dns_ncache_current(rdataset, &name, &rds);
		for (result = dns_rdataset_first(&rds);
		     result == ISC_R_SUCCESS;
		     result = dns_rdataset_next(&rds)) {
			CHECK(str_totext("; ", target));
			CHECK(dns_name_totext(&name, omit_final_dot, target));
			CHECK(str_totext(" ", target));
			CHECK(dns_rdatatype_totext(rds.type, target));
			if (rds.type == dns_rdatatype_rrsig) {
				CHECK(str_totext(" ", target));
				CHECK(dns_rdatatype_totext(rds.covers, target));
				CHECK(str_totext(" ...\n", target));
			} else {
				dns_rdata_t rdata = DNS_RDATA_INIT;
				dns_rdataset_current(&rds, &rdata);
				CHECK(str_totext(" ", target));
				CHECK(dns_rdata_tofmttext(&rdata, dns_rootname,
380
							  0, 0, 0, " ", target));
381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396
				CHECK(str_totext("\n", target));
			}
		}
		dns_rdataset_disassociate(&rds);
		result = dns_rdataset_next(rdataset);
	} while (result == ISC_R_SUCCESS);

	if (result == ISC_R_NOMORE)
		result = ISC_R_SUCCESS;
 cleanup:
	if (dns_rdataset_isassociated(&rds))
		dns_rdataset_disassociate(&rds);

	return (result);
}

397 398 399 400 401 402 403
/*
 * Convert 'rdataset' to master file text format according to 'ctx',
 * storing the result in 'target'.  If 'owner_name' is NULL, it
 * is omitted; otherwise 'owner_name' must be valid and have at least
 * one label.
 */

404
static isc_result_t
405 406 407 408 409 410
rdataset_totext(dns_rdataset_t *rdataset,
		dns_name_t *owner_name,
		dns_totext_ctx_t *ctx,
		isc_boolean_t omit_final_dot,
		isc_buffer_t *target)
{
411
	isc_result_t result;
412 413 414 415
	unsigned int column;
	isc_boolean_t first = ISC_TRUE;
	isc_uint32_t current_ttl;
	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 423 424 425 426
	result = dns_rdataset_first(rdataset);

	current_ttl = ctx->current_ttl;
	current_ttl_valid = ctx->current_ttl_valid;

427
	while (result == ISC_R_SUCCESS) {
428
		column = 0;
429

430 431 432
		/*
		 * Owner name.
		 */
433
		if (owner_name != NULL &&
434 435
		    ! ((ctx->style.flags & DNS_STYLEFLAG_OMIT_OWNER) != 0 &&
		       !first))
436 437 438 439 440 441 442 443
		{
			unsigned int name_start = target->used;
			RETERR(dns_name_totext(owner_name,
					       omit_final_dot,
					       target));
			column += target->used - name_start;
		}

444 445 446
		/*
		 * TTL.
		 */
447 448 449 450
		if ((ctx->style.flags & DNS_STYLEFLAG_NO_TTL) == 0 &&
		    !((ctx->style.flags & DNS_STYLEFLAG_OMIT_TTL) != 0 &&
		      current_ttl_valid &&
		      rdataset->ttl == current_ttl))
451 452 453 454 455 456
		{
			char ttlbuf[64];
			isc_region_t r;
			unsigned int length;

			INDENT_TO(ttl_column);
457 458
			length = snprintf(ttlbuf, sizeof(ttlbuf), "%u",
					  rdataset->ttl);
Andreas Gustafsson's avatar
Andreas Gustafsson committed
459
			INSIST(length <= sizeof(ttlbuf));
460
			isc_buffer_availableregion(target, &r);
461
			if (r.length < length)
462
				return (ISC_R_NOSPACE);
463 464 465 466 467
			memcpy(r.base, ttlbuf, length);
			isc_buffer_add(target, length);
			column += length;

			/*
468
			 * If the $TTL directive is not in use, the TTL we
469 470
			 * just printed becomes the default for subsequent RRs.
			 */
471
			if ((ctx->style.flags & DNS_STYLEFLAG_TTL) == 0) {
472 473 474 475 476
				current_ttl = rdataset->ttl;
				current_ttl_valid = ISC_TRUE;
			}
		}

477 478 479
		/*
		 * Class.
		 */
480 481 482
		if ((ctx->style.flags & DNS_STYLEFLAG_NO_CLASS) == 0 &&
		    ((ctx->style.flags & DNS_STYLEFLAG_OMIT_CLASS) == 0 ||
		     ctx->class_printed == ISC_FALSE))
483 484 485 486
		{
			unsigned int class_start;
			INDENT_TO(class_column);
			class_start = target->used;
487 488
			result = dns_rdataclass_totext(rdataset->rdclass,
						       target);
489
			if (result != ISC_R_SUCCESS)
490 491 492 493
				return (result);
			column += (target->used - class_start);
		}

494 495 496
		/*
		 * Type.
		 */
497

498
		if ((rdataset->attributes & DNS_RDATASETATTR_NEGATIVE) != 0) {
499 500 501 502 503
			type = rdataset->covers;
		} else {
			type = rdataset->type;
		}

504 505 506 507 508 509 510 511
		INDENT_TO(type_column);
		type_start = target->used;
		if ((rdataset->attributes & DNS_RDATASETATTR_NEGATIVE) != 0)
			RETERR(str_totext("\\-", target));
		result = dns_rdatatype_totext(type, target);
		if (result != ISC_R_SUCCESS)
			return (result);
		column += (target->used - type_start);
512

513 514
		/*
		 * Rdata.
515
		 */
516
		INDENT_TO(rdata_column);
517
		if ((rdataset->attributes & DNS_RDATASETATTR_NEGATIVE) != 0) {
518 519 520 521
			if (NXDOMAIN(rdataset))
				RETERR(str_totext(";-$NXDOMAIN\n", target));
			else
				RETERR(str_totext(";-$NXRRSET\n", target));
522 523 524 525 526 527 528
			/*
			 * Print a summary of the cached records which make
			 * up the negative response.
			 */
			RETERR(ncache_summary(rdataset, omit_final_dot,
					      target));
			break;
529
		} else {
530
			dns_rdata_t rdata = DNS_RDATA_INIT;
531 532 533 534
			isc_region_t r;

			dns_rdataset_current(rdataset, &rdata);

535 536 537 538 539
			RETERR(dns_rdata_tofmttext(&rdata,
						   ctx->origin,
						   ctx->style.flags,
						   ctx->style.line_length -
						       ctx->style.rdata_column,
540
						   ctx->style.split_width,
541 542
						   ctx->linebreak,
						   target));
543

544
			isc_buffer_availableregion(target, &r);
545
			if (r.length < 1)
546
				return (ISC_R_NOSPACE);
547 548 549 550 551 552
			r.base[0] = '\n';
			isc_buffer_add(target, 1);
		}

		first = ISC_FALSE;
		result = dns_rdataset_next(rdataset);
553
	}
554

555
	if (result != ISC_R_NOMORE)
556 557 558 559
		return (result);

	/*
	 * Update the ctx state to reflect what we just printed.
560 561
	 * This is done last, only when we are sure we will return
	 * 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 566 567 568
	 */
	ctx->class_printed = ISC_TRUE;
	ctx->current_ttl= current_ttl;
	ctx->current_ttl_valid = current_ttl_valid;

569
	return (ISC_R_SUCCESS);
570 571 572 573 574 575 576
}

/*
 * Print the name, type, and class of an empty rdataset,
 * such as those used to represent the question section
 * of a DNS message.
 */
577
static isc_result_t
578 579 580 581 582 583 584
question_totext(dns_rdataset_t *rdataset,
		dns_name_t *owner_name,
		dns_totext_ctx_t *ctx,
		isc_boolean_t omit_final_dot,
		isc_buffer_t *target)
{
	unsigned int column;
585
	isc_result_t result;
586
	isc_region_t r;
587 588 589

	REQUIRE(DNS_RDATASET_VALID(rdataset));
	result = dns_rdataset_first(rdataset);
590
	REQUIRE(result == ISC_R_NOMORE);
591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608

	column = 0;

	/* Owner name */
	{
		unsigned int name_start = target->used;
		RETERR(dns_name_totext(owner_name,
				       omit_final_dot,
				       target));
		column += target->used - name_start;
	}

	/* Class */
	{
		unsigned int class_start;
		INDENT_TO(class_column);
		class_start = target->used;
		result = dns_rdataclass_totext(rdataset->rdclass, target);
609
		if (result != ISC_R_SUCCESS)
610 611 612 613 614 615 616 617 618 619
			return (result);
		column += (target->used - class_start);
	}

	/* Type */
	{
		unsigned int type_start;
		INDENT_TO(type_column);
		type_start = target->used;
		result = dns_rdatatype_totext(rdataset->type, target);
620
		if (result != ISC_R_SUCCESS)
621 622 623
			return (result);
		column += (target->used - type_start);
	}
624

625
	isc_buffer_availableregion(target, &r);
626
	if (r.length < 1)
627
		return (ISC_R_NOSPACE);
628 629 630
	r.base[0] = '\n';
	isc_buffer_add(target, 1);

631
	return (ISC_R_SUCCESS);
632 633
}

634
isc_result_t
635 636 637
dns_rdataset_totext(dns_rdataset_t *rdataset,
		    dns_name_t *owner_name,
		    isc_boolean_t omit_final_dot,
638
		    isc_boolean_t question,
639 640 641
		    isc_buffer_t *target)
{
	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 646
		UNEXPECTED_ERROR(__FILE__, __LINE__,
				 "could not set master file style");
647
		return (ISC_R_UNEXPECTED);
648 649 650 651 652 653 654 655 656 657
	}

	/*
	 * The caller might want to give us an empty owner
	 * name (e.g. if they are outputting into a master
	 * file and this rdataset has the same name as the
	 * previous one.)
	 */
	if (dns_name_countlabels(owner_name) == 0)
		owner_name = NULL;
658

659
	if (question)
660
		return (question_totext(rdataset, owner_name, &ctx,
661 662
					omit_final_dot, target));
	else
663
		return (rdataset_totext(rdataset, owner_name, &ctx,
664 665 666
					omit_final_dot, target));
}

667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688
isc_result_t
dns_master_rdatasettotext(dns_name_t *owner_name,
			  dns_rdataset_t *rdataset,
			  const dns_master_style_t *style,
			  isc_buffer_t *target)
{
	dns_totext_ctx_t ctx;
	isc_result_t result;
	result = totext_ctx_init(style, &ctx);
	if (result != ISC_R_SUCCESS) {
		UNEXPECTED_ERROR(__FILE__, __LINE__,
				 "could not set master file style");
		return (ISC_R_UNEXPECTED);
	}

	return (rdataset_totext(rdataset, owner_name, &ctx,
				ISC_FALSE, target));
}

isc_result_t
dns_master_questiontotext(dns_name_t *owner_name,
			  dns_rdataset_t *rdataset,
689
			  const dns_master_style_t *style,
690 691
			  isc_buffer_t *target)
{
692 693 694 695 696 697 698 699 700 701
	dns_totext_ctx_t ctx;
	isc_result_t result;
	result = totext_ctx_init(style, &ctx);
	if (result != ISC_R_SUCCESS) {
		UNEXPECTED_ERROR(__FILE__, __LINE__,
				 "could not set master file style");
		return (ISC_R_UNEXPECTED);
	}

	return (question_totext(rdataset, owner_name, &ctx,
702 703 704
				ISC_FALSE, target));
}

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 710 711 712
 * hold the result from dns_ttl_totext().  If more than that is needed,
 * the buffer will be grown automatically.
 */

713
static isc_result_t
714
dump_rdataset(isc_mem_t *mctx, dns_name_t *name, dns_rdataset_t *rdataset,
715
	      dns_totext_ctx_t *ctx,
716 717 718
	      isc_buffer_t *buffer, FILE *f)
{
	isc_region_t r;
719
	isc_result_t result;
720

721 722
	REQUIRE(buffer->length > 0);

723 724 725
	/*
	 * Output a $TTL directive if needed.
	 */
726

727 728 729 730 731 732 733 734 735
	if ((ctx->style.flags & DNS_STYLEFLAG_TTL) != 0) {
		if (ctx->current_ttl_valid == ISC_FALSE ||
		    ctx->current_ttl != rdataset->ttl)
		{
			if ((ctx->style.flags & DNS_STYLEFLAG_COMMENT) != 0)
			{
				isc_buffer_clear(buffer);
				result = dns_ttl_totext(rdataset->ttl,
							ISC_TRUE, buffer);
736
				INSIST(result == ISC_R_SUCCESS);
737
				isc_buffer_usedregion(buffer, &r);
738 739 740 741 742 743 744 745 746
				fprintf(f, "$TTL %u\t; %.*s\n", rdataset->ttl,
					(int) r.length, (char *) r.base);
			} else {
				fprintf(f, "$TTL %u\n", rdataset->ttl);
			}
			ctx->current_ttl = rdataset->ttl;
			ctx->current_ttl_valid = ISC_TRUE;
		}
	}
747

748 749 750 751 752
	isc_buffer_clear(buffer);

	/*
	 * Generate the text representation of the rdataset into
	 * the buffer.  If the buffer is too small, grow it.
753
	 */
754 755 756 757 758
	for (;;) {
		int newlength;
		void *newmem;
		result = rdataset_totext(rdataset, name, ctx,
					 ISC_FALSE, buffer);
759
		if (result != ISC_R_NOSPACE)
760 761 762 763 764
			break;

		newlength = buffer->length * 2;
		newmem = isc_mem_get(mctx, newlength);
		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 771
		return (result);

772 773 774
	/*
	 * Write the buffer contents to the master file.
	 */
775
	isc_buffer_usedregion(buffer, &r);
776
	result = isc_stdio_write(r.base, 1, (size_t)r.length, f, NULL);
777

778
	if (result != ISC_R_SUCCESS) {
779
		UNEXPECTED_ERROR(__FILE__, __LINE__,
780
				 "master file write failed: %s",
781 782
				 isc_result_totext(result));
		return (result);
783
	}
784

785
	return (ISC_R_SUCCESS);
786 787 788
}

/*
789 790 791 792 793 794 795 796 797 798 799 800
 * Define the order in which rdatasets should be printed in zone
 * files.  We will print SOA and NS records before others, SIGs
 * immediately following the things they sign, and order everything
 * else by RR number.  This is all just for aesthetics and
 * compatibility with buggy software that expects the SOA to be first;
 * the DNS specifications allow any order.
 */

static int
dump_order(const dns_rdataset_t *rds) {
	int t;
	int sig;
801
	if (rds->type == dns_rdatatype_rrsig) {
802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823
		t = rds->covers;
		sig = 1;
	} else {
		t = rds->type;
		sig = 0;
	}
	switch (t) {
	case dns_rdatatype_soa:
		t = 0;
		break;
	case dns_rdatatype_ns:
		t = 1;
		break;
	default:
		t += 2;
		break;
	}
	return (t << 1) + sig;
}

static int
dump_order_compare(const void *a, const void *b) {
824 825
	return (dump_order(*((const dns_rdataset_t * const *) a)) -
		dump_order(*((const dns_rdataset_t * const *) b)));
826 827 828 829 830 831 832 833 834 835
}

/*
 * Dump all the rdatasets of a domain name to a master file.  We make
 * a "best effort" attempt to sort the RRsets in a nice order, but if
 * there are more than MAXSORT RRsets, we punt and only sort them in
 * groups of MAXSORT.  This is not expected to ever happen in practice
 * since much less than 64 RR types have been registered with the
 * IANA, so far, and the output will be correct (though not
 * aesthetically pleasing) even if it does happen.
836
 */
837 838 839

#define MAXSORT 64

840
static isc_result_t
841 842 843
dump_rdatasets_text(isc_mem_t *mctx, dns_name_t *name,
		    dns_rdatasetiter_t *rdsiter, dns_totext_ctx_t *ctx,
		    isc_buffer_t *buffer, FILE *f)
844
{
845
	isc_result_t itresult, dumpresult;
846
	isc_region_t r;
847 848 849 850 851 852 853
	dns_rdataset_t rdatasets[MAXSORT];
	dns_rdataset_t *sorted[MAXSORT];
	int i, n;

	itresult = dns_rdatasetiter_first(rdsiter);
	dumpresult = ISC_R_SUCCESS;

854 855 856 857 858 859 860 861 862
	if (itresult == ISC_R_SUCCESS && ctx->neworigin != NULL) {
		isc_buffer_clear(buffer);
		itresult = dns_name_totext(ctx->neworigin, ISC_FALSE, buffer);
		RUNTIME_CHECK(itresult == ISC_R_SUCCESS);
		isc_buffer_usedregion(buffer, &r);
		fprintf(f, "$ORIGIN %.*s\n", (int) r.length, (char *) r.base);
		ctx->neworigin = NULL;
	}

863 864 865 866 867 868 869
 again:
	for (i = 0;
	     itresult == ISC_R_SUCCESS && i < MAXSORT;
	     itresult = dns_rdatasetiter_next(rdsiter), i++) {
		dns_rdataset_init(&rdatasets[i]);
		dns_rdatasetiter_current(rdsiter, &rdatasets[i]);
		sorted[i] = &rdatasets[i];
870
	}
871 872
	n = i;
	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 879
		if (ctx->style.flags & DNS_STYLEFLAG_TRUST)
			fprintf(f, "; %s\n", dns_trust_totext(rds->trust));
880
		if (((rds->attributes & DNS_RDATASETATTR_NEGATIVE) != 0) &&
881 882 883
		    (ctx->style.flags & DNS_STYLEFLAG_NCACHE) == 0) {
			/* Omit negative cache entries */
		} else {
884
			isc_result_t result =
885
				dump_rdataset(mctx, name, rds, ctx,
Danny Mayer's avatar
Danny Mayer committed
886
					       buffer, f);
887 888
			if (result != ISC_R_SUCCESS)
				dumpresult = result;
889 890
			if ((ctx->style.flags & DNS_STYLEFLAG_OMIT_OWNER) != 0)
				name = NULL;
891
		}
892 893 894 895 896 897 898 899 900
		if (ctx->style.flags & DNS_STYLEFLAG_RESIGN &&
		    rds->attributes & DNS_RDATASETATTR_RESIGN) {
			isc_buffer_t b;
			char buf[sizeof("YYYYMMDDHHMMSS")];
			memset(buf, 0, sizeof(buf));
			isc_buffer_init(&b, buf, sizeof(buf) - 1);
			dns_time64_totext((isc_uint64_t)rds->resign, &b);
			fprintf(f, "; resign=%s\n", buf);
		}
901
		dns_rdataset_disassociate(rds);
902 903
	}

904 905
	if (dumpresult != ISC_R_SUCCESS)
		return (dumpresult);
906

907 908 909 910 911 912
	/*
	 * If we got more data than could be sorted at once,
	 * go handle the rest.
	 */
	if (itresult == ISC_R_SUCCESS)
		goto again;
913

914 915 916 917
	if (itresult == ISC_R_NOMORE)
		itresult = ISC_R_SUCCESS;

	return (itresult);
918 919
}

920 921 922 923 924 925 926 927 928 929 930 931 932 933 934
/*
 * Dump given RRsets in the "raw" format.
 */
static isc_result_t
dump_rdataset_raw(isc_mem_t *mctx, dns_name_t *name, dns_rdataset_t *rdataset,
		  isc_buffer_t *buffer, FILE *f)
{
	isc_result_t result;
	isc_uint32_t totallen;
	isc_uint16_t dlen;
	isc_region_t r, r_hdr;

	REQUIRE(buffer->length > 0);
	REQUIRE(DNS_RDATASET_VALID(rdataset));