master.c 62.4 KB
Newer Older
Mark Andrews's avatar
Mark Andrews committed
1
/*
Mark Andrews's avatar
Mark Andrews committed
2
 * Copyright (C) 2004, 2005  Internet Systems Consortium, Inc. ("ISC")
Mark Andrews's avatar
Mark Andrews committed
3
 * Copyright (C) 1999-2003  Internet Software Consortium.
4
 *
Mark Andrews's avatar
Mark Andrews committed
5 6 7
 * Permission to use, copy, modify, and distribute this software for any
 * 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.
Mark Andrews's avatar
Mark Andrews committed
16 17
 */

Mark Andrews's avatar
Mark Andrews committed
18
/* $Id: master.c,v 1.151 2005/01/10 23:43:22 marka Exp $ */
Mark Andrews's avatar
Mark Andrews committed
19 20 21

#include <config.h>

22
#include <isc/event.h>
23
#include <isc/lex.h>
24
#include <isc/magic.h>
25
#include <isc/mem.h>
26
#include <isc/print.h>
27
#include <isc/serial.h>
28
#include <isc/stdtime.h>
29
#include <isc/string.h>
30
#include <isc/task.h>
Bob Halley's avatar
Bob Halley committed
31
#include <isc/util.h>
32

33
#include <dns/callbacks.h>
34 35
#include <dns/events.h>
#include <dns/fixedname.h>
36
#include <dns/master.h>
37 38 39
#include <dns/name.h>
#include <dns/rdata.h>
#include <dns/rdataclass.h>
40 41
#include <dns/rdatalist.h>
#include <dns/rdataset.h>
42
#include <dns/rdatastruct.h>
43
#include <dns/rdatatype.h>
44
#include <dns/result.h>
45
#include <dns/soa.h>
46
#include <dns/time.h>
47
#include <dns/ttl.h>
48

49 50 51 52 53 54
/*
 * Grow the number of dns_rdatalist_t (RDLSZ) and dns_rdata_t (RDSZ) structures
 * by these sizes when we need to.
 *
 * RDLSZ reflects the number of different types with the same name expected.
 * RDSZ reflects the number of rdata expected at a give name that can fit into
55
 * 64k.
56 57 58 59 60
 */

#define RDLSZ 32
#define RDSZ 512

Mark Andrews's avatar
Mark Andrews committed
61
#define NBUFS 4
62 63 64
#define MAXWIRESZ 255

/*
Bob Halley's avatar
Bob Halley committed
65
 * Target buffer size and minimum target size.
Andreas Gustafsson's avatar
Andreas Gustafsson committed
66
 * MINTSIZ must be big enough to hold the largest rdata record.
67
 *
68 69 70 71 72 73 74
 * TSIZ >= MINTSIZ
 */
#define TSIZ (128*1024)
/*
 * max message size - header - root - type - class - ttl - rdlen
 */
#define MINTSIZ (65535 - 12 - 1 - 2 - 2 - 4 - 2)
75 76
/*
 * Size for tokens in the presentation format,
77
 * The largest tokens are the base64 blocks in KEY and CERT records,
78
 * Largest key allowed is about 1372 bytes but
Andreas Gustafsson's avatar
typos  
Andreas Gustafsson committed
79 80
 * there is no fixed upper bound on CERT records.
 * 2K is too small for some X.509s, 8K is overkill.
81 82
 */
#define TOKENSIZ (8*1024)
83

84 85
#define DNS_MASTER_BUFSZ 2048

86 87
typedef ISC_LIST(dns_rdatalist_t) rdatalist_head_t;

88 89
typedef struct dns_incctx dns_incctx_t;

90
/*
91
 * Master file load state.
92
 */
93

94
struct dns_loadctx {
95
	unsigned int		magic;
96 97
	isc_mem_t		*mctx;
	isc_lex_t		*lex;
98
	isc_boolean_t		keep_lex;
99 100 101 102
	dns_rdatacallbacks_t	*callbacks;
	isc_task_t		*task;
	dns_loaddonefunc_t	done;
	void			*done_arg;
103
	unsigned int		options;
104 105 106
	isc_boolean_t		ttl_known;
	isc_boolean_t		default_ttl_known;
	isc_boolean_t		warn_1035;
107
	isc_boolean_t		warn_tcr;
108
	isc_boolean_t		warn_sigexpired;
109
	isc_boolean_t		seen_include;
110 111 112
	isc_uint32_t		ttl;
	isc_uint32_t		default_ttl;
	dns_rdataclass_t	zclass;
113 114 115 116 117
	dns_fixedname_t		fixed_top;
	dns_name_t		*top;			/* top of zone */
	/* Which fixed buffers we are using? */
	unsigned int		loop_cnt;		/* records per quantum,
							 * 0 => all. */
118
	isc_boolean_t		canceled;
119
	isc_mutex_t		lock;
120
	isc_result_t		result;
121 122
	/* locked by lock */
	isc_uint32_t		references;
123 124 125 126 127 128 129 130 131 132 133 134 135
	dns_incctx_t		*inc;
};

struct dns_incctx {
	dns_incctx_t		*parent;
	dns_name_t		*origin;
	dns_name_t		*current;
	dns_name_t		*glue;
	dns_fixedname_t		fixed[NBUFS];		/* working buffers */
	unsigned int		in_use[NBUFS];		/* covert to bitmap? */
	int			glue_in_use;
	int			current_in_use;
	int			origin_in_use;
136 137 138
	isc_boolean_t		drop;
	unsigned int		glue_line;
	unsigned int		current_line;
139 140 141
};

#define DNS_LCTX_MAGIC ISC_MAGIC('L','c','t','x')
142
#define DNS_LCTX_VALID(lctx) ISC_MAGIC_VALID(lctx, DNS_LCTX_MAGIC)
143

144 145
#define DNS_AS_STR(t) ((t).value.as_textregion.base)

146
static isc_result_t
147
pushfile(const char *master_file, dns_name_t *origin, dns_loadctx_t *lctx);
148 149

static isc_result_t
150
commit(dns_rdatacallbacks_t *, dns_loadctx_t *, rdatalist_head_t *,
151
       dns_name_t *, const char *, unsigned int);
152 153 154 155 156 157 158 159 160 161 162 163

static isc_boolean_t
is_glue(rdatalist_head_t *, dns_name_t *);

static dns_rdatalist_t *
grow_rdatalist(int, dns_rdatalist_t *, int, rdatalist_head_t *,
		rdatalist_head_t *, isc_mem_t *mctx);

static dns_rdata_t *
grow_rdata(int, dns_rdata_t *, int, rdatalist_head_t *, rdatalist_head_t *,
	   isc_mem_t *);

164 165 166 167
static void
load_quantum(isc_task_t *task, isc_event_t *event);

static isc_result_t
168
task_send(dns_loadctx_t *lctx);
169 170

static void
171
loadctx_destroy(dns_loadctx_t *lctx);
172

Mark Andrews's avatar
Mark Andrews committed
173 174
#define GETTOKEN(lexer, options, token, eol) \
	do { \
175 176
		result = gettoken(lexer, options, token, eol, callbacks); \
		switch (result) { \
177
		case ISC_R_SUCCESS: \
Mark Andrews's avatar
Mark Andrews committed
178
			break; \
179
		case ISC_R_UNEXPECTED: \
Mark Andrews's avatar
Mark Andrews committed
180
			goto insist_and_cleanup; \
Mark Andrews's avatar
Mark Andrews committed
181
		default: \
182 183
			if (MANYERRS(lctx, result)) { \
				SETRESULT(lctx, result); \
184 185 186 187 188
				LOGIT(result); \
				read_till_eol = ISC_TRUE; \
				goto next_line; \
			} else \
				goto log_and_cleanup; \
Mark Andrews's avatar
Mark Andrews committed
189
		} \
190 191
		if ((token)->type == isc_tokentype_special) { \
			result = DNS_R_SYNTAX; \
192 193
			if (MANYERRS(lctx, result)) { \
				SETRESULT(lctx, result); \
194 195 196 197 198
				LOGIT(result); \
				read_till_eol = ISC_TRUE; \
				goto next_line; \
			} else \
				goto log_and_cleanup; \
199
		} \
200 201
	} while (0)

202 203
#define COMMITALL \
	do { \
204
		result = commit(callbacks, lctx, &current_list, \
205
				ictx->current, source, ictx->current_line); \
206 207
		if (MANYERRS(lctx, result)) { \
			SETRESULT(lctx, result); \
208
		} else if (result != ISC_R_SUCCESS) \
209
			goto insist_and_cleanup; \
210
		result = commit(callbacks, lctx, &glue_list, \
211
				ictx->glue, source, ictx->glue_line); \
212 213
		if (MANYERRS(lctx, result)) { \
			SETRESULT(lctx, result); \
214
		} else if (result != ISC_R_SUCCESS) \
215
			goto insist_and_cleanup; \
216 217 218 219 220 221 222
		rdcount = 0; \
		rdlcount = 0; \
		isc_buffer_init(&target, target_mem, target_size); \
		rdcount_save = rdcount; \
		rdlcount_save = rdlcount; \
	} while (0)

223 224 225 226
#define WARNUNEXPECTEDEOF(lexer) \
	do { \
		if (isc_lex_isfile(lexer)) \
			(*callbacks->warn)(callbacks, \
Mark Andrews's avatar
Mark Andrews committed
227 228
				"%s: file does not end with newline", \
				source); \
229
	} while (0)
Mark Andrews's avatar
Mark Andrews committed
230

Mark Andrews's avatar
redo:  
Mark Andrews committed
231 232
#define EXPECTEOL \
	do { \
233
		GETTOKEN(lctx->lex, 0, &token, ISC_TRUE); \
Mark Andrews's avatar
redo:  
Mark Andrews committed
234
		if (token.type != isc_tokentype_eol) { \
235
			isc_lex_ungettoken(lctx->lex, &token); \
Mark Andrews's avatar
redo:  
Mark Andrews committed
236
			result = DNS_R_EXTRATOKEN; \
237 238
			if (MANYERRS(lctx, result)) { \
				SETRESULT(lctx, result); \
Mark Andrews's avatar
redo:  
Mark Andrews committed
239 240 241 242 243 244 245 246
				LOGIT(result); \
				read_till_eol = ISC_TRUE; \
				continue; \
			} else if (result != ISC_R_SUCCESS) \
				goto log_and_cleanup; \
		} \
	} while (0)

247
#define MANYERRS(lctx, result) \
248
		((result != ISC_R_SUCCESS) && \
249
		((lctx)->options & DNS_MASTER_MANYERRORS) != 0)
250

251
#define SETRESULT(lctx, r) \
252
		do { \
253 254
			if ((lctx)->result == ISC_R_SUCCESS) \
				(lctx)->result = r; \
255
		} while (0)
256 257 258 259 260 261 262 263 264 265

#define LOGITFILE(result, filename) \
	if (result == ISC_R_INVALIDFILE || result == ISC_R_FILENOTFOUND || \
	    result == ISC_R_IOERROR || result == ISC_R_TOOMANYOPENFILES || \
	    result == ISC_R_NOPERM) \
		(*callbacks->error)(callbacks, "%s: %s:%lu: %s: %s", \
				    "dns_master_load", source, line, \
				    filename, dns_result_totext(result)); \
	else LOGIT(result)

266 267 268 269 270 271 272
#define LOGIT(result) \
	if (result == ISC_R_NOMEMORY) \
		(*callbacks->error)(callbacks, "dns_master_load: %s", \
				    dns_result_totext(result)); \
	else \
		(*callbacks->error)(callbacks, "%s: %s:%lu: %s", \
				    "dns_master_load", \
273
				    source, line, dns_result_totext(result))
274

275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312

static unsigned char in_addr_arpa_data[]  = "\007IN-ADDR\004ARPA";
static unsigned char in_addr_arpa_offsets[] = { 0, 8, 13 };
static const dns_name_t in_addr_arpa =
{
	DNS_NAME_MAGIC,
	in_addr_arpa_data, 14, 3,
	DNS_NAMEATTR_READONLY | DNS_NAMEATTR_ABSOLUTE,
	in_addr_arpa_offsets, NULL,
	{(void *)-1, (void *)-1},
	{NULL, NULL}
};

static unsigned char ip6_int_data[]  = "\003IP6\003INT";
static unsigned char ip6_int_offsets[] = { 0, 4, 8 };
static const dns_name_t ip6_int =
{
	DNS_NAME_MAGIC,
	ip6_int_data, 9, 3,
	DNS_NAMEATTR_READONLY | DNS_NAMEATTR_ABSOLUTE,
	ip6_int_offsets, NULL,
	{(void *)-1, (void *)-1},
	{NULL, NULL}
};

static unsigned char ip6_arpa_data[]  = "\003IP6\004ARPA";
static unsigned char ip6_arpa_offsets[] = { 0, 4, 9 };
static const dns_name_t ip6_arpa =
{
	DNS_NAME_MAGIC,
	ip6_arpa_data, 10, 3,
	DNS_NAMEATTR_READONLY | DNS_NAMEATTR_ABSOLUTE,
	ip6_arpa_offsets, NULL,
	{(void *)-1, (void *)-1},
	{NULL, NULL}
};


313
static inline isc_result_t
Mark Andrews's avatar
Mark Andrews committed
314
gettoken(isc_lex_t *lex, unsigned int options, isc_token_t *token,
Bob Halley's avatar
Bob Halley committed
315
	 isc_boolean_t eol, dns_rdatacallbacks_t *callbacks)
Mark Andrews's avatar
Mark Andrews committed
316 317 318
{
	isc_result_t result;

Andreas Gustafsson's avatar
Andreas Gustafsson committed
319 320
	options |= ISC_LEXOPT_EOL | ISC_LEXOPT_EOF | ISC_LEXOPT_DNSMULTILINE |
		ISC_LEXOPT_ESCAPE;
Mark Andrews's avatar
Mark Andrews committed
321 322 323 324
	result = isc_lex_gettoken(lex, options, token);
	if (result != ISC_R_SUCCESS) {
		switch (result) {
		case ISC_R_NOMEMORY:
325
			return (ISC_R_NOMEMORY);
Mark Andrews's avatar
Mark Andrews committed
326
		default:
327 328 329 330 331 332 333
			(*callbacks->error)(callbacks,
					    "dns_master_load: %s:%lu:"
					    " isc_lex_gettoken() failed: %s",
					    isc_lex_getsourcename(lex),
					    isc_lex_getsourceline(lex),
					    isc_result_totext(result));
			return (result);
Mark Andrews's avatar
Mark Andrews committed
334 335 336 337 338 339 340
		}
		/*NOTREACHED*/
	}
	if (eol != ISC_TRUE)
		if (token->type == isc_tokentype_eol ||
		    token->type == isc_tokentype_eof) {
			(*callbacks->error)(callbacks,
341
			    "dns_master_load: %s:%lu: unexpected end of %s",
Bob Halley's avatar
Bob Halley committed
342 343
					    isc_lex_getsourcename(lex),
					    isc_lex_getsourceline(lex),
344 345
					    (token->type ==
					     isc_tokentype_eol) ?
Bob Halley's avatar
Bob Halley committed
346
					    "line" : "file");
347
			return (ISC_R_UNEXPECTEDEND);
Mark Andrews's avatar
Mark Andrews committed
348
		}
349
	return (ISC_R_SUCCESS);
Mark Andrews's avatar
Mark Andrews committed
350
}
Mark Andrews's avatar
Mark Andrews committed
351

352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368

void
dns_loadctx_attach(dns_loadctx_t *source, dns_loadctx_t **target) {

	REQUIRE(target != NULL && *target == NULL);
	REQUIRE(DNS_LCTX_VALID(source));

	LOCK(&source->lock);
	INSIST(source->references > 0);
	source->references++;
	INSIST(source->references != 0);	/* Overflow? */
	UNLOCK(&source->lock);

	*target = source;
}

void
369 370
dns_loadctx_detach(dns_loadctx_t **lctxp) {
	dns_loadctx_t *lctx;
371
	isc_boolean_t need_destroy = ISC_FALSE;
372

373 374 375
	REQUIRE(lctxp != NULL);
	lctx = *lctxp;
	REQUIRE(DNS_LCTX_VALID(lctx));
376

377 378 379 380
	LOCK(&lctx->lock);
	INSIST(lctx->references > 0);
	lctx->references--;
	if (lctx->references == 0)
381
		need_destroy = ISC_TRUE;
382
	UNLOCK(&lctx->lock);
383 384

	if (need_destroy)
385 386 387 388 389 390 391 392 393 394 395 396
		loadctx_destroy(lctx);
	*lctxp = NULL;
}

static void
incctx_destroy(isc_mem_t *mctx, dns_incctx_t *ictx) {
	dns_incctx_t *parent;

 again:
	parent = ictx->parent;
	ictx->parent = NULL;

Andreas Gustafsson's avatar
Andreas Gustafsson committed
397
	isc_mem_put(mctx, ictx, sizeof(*ictx));
398 399 400 401 402

	if (parent != NULL) {
		ictx = parent;
		goto again;
	}
403 404 405
}

static void
406
loadctx_destroy(dns_loadctx_t *lctx) {
407 408
	isc_mem_t *mctx;

409
	REQUIRE(DNS_LCTX_VALID(lctx));
410

411 412 413
	lctx->magic = 0;
	if (lctx->inc != NULL)
		incctx_destroy(lctx->mctx, lctx->inc);
414

415
	/* isc_lex_destroy() will close all open streams */
416
	if (lctx->lex != NULL && !lctx->keep_lex)
417
		isc_lex_destroy(&lctx->lex);
418

419 420 421
	if (lctx->task != NULL)
		isc_task_detach(&lctx->task);
	DESTROYLOCK(&lctx->lock);
422
	mctx = NULL;
423 424 425
	isc_mem_attach(lctx->mctx, &mctx);
	isc_mem_detach(&lctx->mctx);
	isc_mem_put(mctx, lctx, sizeof(*lctx));
426 427 428
	isc_mem_detach(&mctx);
}

429 430 431 432 433 434
static isc_result_t
incctx_create(isc_mem_t *mctx, dns_name_t *origin, dns_incctx_t **ictxp) {
	dns_incctx_t *ictx;
	isc_region_t r;
	int i;

Andreas Gustafsson's avatar
Andreas Gustafsson committed
435
	ictx = isc_mem_get(mctx, sizeof(*ictx));
436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454
	if (ictx == NULL)
		return (ISC_R_NOMEMORY);

	for (i = 0; i < NBUFS; i++) {
		dns_fixedname_init(&ictx->fixed[i]);
		ictx->in_use[i] = ISC_FALSE;
	}

	ictx->origin_in_use = 0;
	ictx->origin = dns_fixedname_name(&ictx->fixed[ictx->origin_in_use]);
	ictx->in_use[ictx->origin_in_use] = ISC_TRUE;
	dns_name_toregion(origin, &r);
	dns_name_fromregion(ictx->origin, &r);

	ictx->glue = NULL;
	ictx->current = NULL;
	ictx->glue_in_use = -1;
	ictx->current_in_use = -1;
	ictx->parent = NULL;
455 456 457
	ictx->drop = ISC_FALSE;
	ictx->glue_line = 0;
	ictx->current_line = 0;
458 459 460 461 462

	*ictxp = ictx;
	return (ISC_R_SUCCESS);
}

463
static isc_result_t
464
loadctx_create(isc_mem_t *mctx, unsigned int options, dns_name_t *top,
465 466
	       dns_rdataclass_t zclass, dns_name_t *origin,
	       dns_rdatacallbacks_t *callbacks, isc_task_t *task,
467
	       dns_loaddonefunc_t done, void *done_arg, isc_lex_t *lex,
468
	       dns_loadctx_t **lctxp)
469
{
470
	dns_loadctx_t *lctx;
471 472 473 474
	isc_result_t result;
	isc_region_t r;
	isc_lexspecials_t specials;

475
	REQUIRE(lctxp != NULL && *lctxp == NULL);
476 477 478 479 480 481 482 483
	REQUIRE(callbacks != NULL);
	REQUIRE(callbacks->add != NULL);
	REQUIRE(callbacks->error != NULL);
	REQUIRE(callbacks->warn != NULL);
	REQUIRE(mctx != NULL);
	REQUIRE(dns_name_isabsolute(top));
	REQUIRE(dns_name_isabsolute(origin));
	REQUIRE((task == NULL && done == NULL) ||
484
		(task != NULL && done != NULL));
485

486 487
	lctx = isc_mem_get(mctx, sizeof(*lctx));
	if (lctx == NULL)
488
		return (ISC_R_NOMEMORY);
489
	result = isc_mutex_init(&lctx->lock);
490
	if (result != ISC_R_SUCCESS) {
Andreas Gustafsson's avatar
Andreas Gustafsson committed
491
		isc_mem_put(mctx, lctx, sizeof(*lctx));
492 493 494 495 496
		UNEXPECTED_ERROR(__FILE__, __LINE__,
				 "isc_mutex_init() failed: %s",
				 isc_result_totext(result));
		return (ISC_R_UNEXPECTED);
	}
497

498 499 500
	lctx->inc = NULL;
	result = incctx_create(mctx, origin, &lctx->inc);
	if (result != ISC_R_SUCCESS) 
501
		goto cleanup_ctx;
502

503
	if (lex != NULL) {
504
		lctx->lex = lex;
505 506
		lctx->keep_lex = ISC_TRUE;
	} else {
507 508 509 510
		lctx->lex = NULL;
		result = isc_lex_create(mctx, TOKENSIZ, &lctx->lex);
		if (result != ISC_R_SUCCESS)
			goto cleanup_inc;
511
		lctx->keep_lex = ISC_FALSE;
512 513 514 515 516 517 518
		memset(specials, 0, sizeof(specials));
		specials['('] = 1;
		specials[')'] = 1;
		specials['"'] = 1;
		isc_lex_setspecials(lctx->lex, specials);
		isc_lex_setcomments(lctx->lex, ISC_LEXCOMMENT_DNSMASTERFILE);
	}
519 520 521 522 523 524

	lctx->ttl_known = ISC_FALSE;
	lctx->ttl = 0;
	lctx->default_ttl_known = ISC_FALSE;
	lctx->default_ttl = 0;
	lctx->warn_1035 = ISC_TRUE;	/* XXX Argument? */
525
	lctx->warn_tcr = ISC_TRUE;	/* XXX Argument? */
526
	lctx->warn_sigexpired = ISC_TRUE;	/* XXX Argument? */
527 528 529 530 531 532 533
	lctx->options = options;
	lctx->seen_include = ISC_FALSE;
	lctx->zclass = zclass;
	lctx->result = ISC_R_SUCCESS;

	dns_fixedname_init(&lctx->fixed_top);
	lctx->top = dns_fixedname_name(&lctx->fixed_top);
534
	dns_name_toregion(top, &r);
535
	dns_name_fromregion(lctx->top, &r);
536

537 538 539
	lctx->loop_cnt = (done != NULL) ? 100 : 0;
	lctx->callbacks = callbacks;
	lctx->task = NULL;
540
	if (task != NULL)
541 542 543 544 545 546 547 548 549
		isc_task_attach(task, &lctx->task);
	lctx->done = done;
	lctx->done_arg = done_arg;
	lctx->canceled = ISC_FALSE;
	lctx->mctx = NULL;
	isc_mem_attach(mctx, &lctx->mctx);
	lctx->references = 1;			/* Implicit attach. */
	lctx->magic = DNS_LCTX_MAGIC;
	*lctxp = lctx;
550 551
	return (ISC_R_SUCCESS);

552 553
 cleanup_inc:
	incctx_destroy(mctx, lctx->inc);
554
 cleanup_ctx:
555
	isc_mem_put(mctx, lctx, sizeof(*lctx));
556
	return (result);
557
}
558

559 560 561 562 563
static isc_result_t
genname(char *name, int it, char *buffer, size_t length) {
	char fmt[sizeof("%04000000000d")];
	char numbuf[128];
	char *cp;
564
	char mode[2];
565 566 567 568 569 570 571 572 573 574 575
	int delta = 0;
	isc_textregion_t r;
	unsigned int n;
	unsigned int width;

	r.base = buffer;
	r.length = length;

	while (*name != '\0') {
		if (*name == '$') {
			name++;
576 577 578 579 580 581 582
			if (*name == '$') {
				if (r.length == 0)
					return (ISC_R_NOSPACE);
				r.base[0] = *name++;
				isc_textregion_consume(&r, 1);
				continue;
			}
583 584 585 586
			strcpy(fmt, "%d");
			/* Get format specifier. */
			if (*name == '{' ) {
				n = sscanf(name, "{%d,%u,%1[doxX]}",
587
					   &delta, &width, mode);
588 589 590 591 592 593 594 595 596
				switch (n) {
				case 1:
					break;
				case 2:
					n = snprintf(fmt, sizeof(fmt),
						     "%%0%ud", width);
					break;
				case 3:
					n = snprintf(fmt, sizeof(fmt),
597
						     "%%0%u%c", width, mode[0]);
598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642
					break;
				default:
					return (DNS_R_SYNTAX);
				}
				if (n >= sizeof(fmt))
					return (ISC_R_NOSPACE);
				/* Skip past closing brace. */
				while (*name != '\0' && *name++ != '}')
					continue;
			}
			n = snprintf(numbuf, sizeof(numbuf), fmt, it + delta);
			if (n >= sizeof(numbuf))
				return (ISC_R_NOSPACE);
			cp = numbuf;
			while (*cp != '\0') {
				if (r.length == 0)
					return (ISC_R_NOSPACE);
				r.base[0] = *cp++;
				isc_textregion_consume(&r, 1);
			}
		} else if (*name == '\\') {
			if (r.length == 0)
				return (ISC_R_NOSPACE);
			r.base[0] = *name++;
			isc_textregion_consume(&r, 1);
			if (*name == '\0')
				continue;
			if (r.length == 0)
				return (ISC_R_NOSPACE);
			r.base[0] = *name++;
			isc_textregion_consume(&r, 1);
		} else {
			if (r.length == 0)
				return (ISC_R_NOSPACE);
			r.base[0] = *name++;
			isc_textregion_consume(&r, 1);
		}
	}
	if (r.length == 0)
		return (ISC_R_NOSPACE);
	r.base[0] = '\0';
	return (ISC_R_SUCCESS);
}

static isc_result_t
643 644 645
generate(dns_loadctx_t *lctx, char *range, char *lhs, char *gtype, char *rhs,
	 const char *source, unsigned int line)
{
646 647 648 649 650
	char *target_mem = NULL;
	char *lhsbuf = NULL;
	char *rhsbuf = NULL;
	dns_fixedname_t ownerfixed;
	dns_name_t *owner;
651
	dns_rdata_t rdata = DNS_RDATA_INIT;
652 653 654 655 656 657 658 659 660 661 662
	dns_rdatacallbacks_t *callbacks;
	dns_rdatalist_t rdatalist;
	dns_rdatatype_t type;
	rdatalist_head_t head;
	int n;
	int target_size = MINTSIZ;	/* only one rdata at a time */
	isc_buffer_t buffer;
	isc_buffer_t target;
	isc_result_t result;
	isc_textregion_t r;
	unsigned int start, stop, step, i;
663
	dns_incctx_t *ictx;
664

665 666
	ictx = lctx->inc;
	callbacks = lctx->callbacks;
667 668 669 670
	dns_fixedname_init(&ownerfixed);
	owner = dns_fixedname_name(&ownerfixed);
	ISC_LIST_INIT(head);

671 672 673
	target_mem = isc_mem_get(lctx->mctx, target_size);
	rhsbuf = isc_mem_get(lctx->mctx, DNS_MASTER_BUFSZ);
	lhsbuf = isc_mem_get(lctx->mctx, DNS_MASTER_BUFSZ);
674 675 676 677 678 679 680
	if (target_mem == NULL || rhsbuf == NULL || lhsbuf == NULL) {
		result = ISC_R_NOMEMORY;
		goto error_cleanup;
	}
	isc_buffer_init(&target, target_mem, target_size);

	n = sscanf(range, "%u-%u/%u", &start, &stop, &step);
681
	if (n < 2 || stop < start) {
682
	       (*callbacks->error)(callbacks,
683
				  "%s: %s:%lu: invalid range '%s'",
684
				  "$GENERATE", source, line, range);
685 686 687 688 689 690 691 692 693 694 695 696 697
		result = DNS_R_SYNTAX;
		goto insist_cleanup;
	}
	if (n == 2)
		step = 1;

	/*
	 * Get type.
	 */
	r.base = gtype;
	r.length = strlen(gtype);
	result = dns_rdatatype_fromtext(&type, &r);
	if (result != ISC_R_SUCCESS) {
698
		(*callbacks->error)(callbacks,
699
				   "%s: %s:%lu: unknown RR type '%s'",
700
				   "$GENERATE", source, line, gtype);
701 702 703 704 705 706 707
		goto insist_cleanup;
	}

	switch (type) {
	case dns_rdatatype_ns:
	case dns_rdatatype_ptr:
	case dns_rdatatype_cname:
708
	case dns_rdatatype_dname:
709 710 711 712
		break;

	case dns_rdatatype_a:
	case dns_rdatatype_aaaa:
713 714
		if (lctx->zclass == dns_rdataclass_in ||
		    lctx->zclass == dns_rdataclass_hs)
715 716 717
			break;
		/* FALLTHROUGH */
	default:
718
	       (*callbacks->error)(callbacks,
719
				  "%s: %s:%lu: unsupported type '%s'",
720
				  "$GENERATE", source, line, gtype);
721 722 723 724
		result = ISC_R_NOTIMPLEMENTED;
		goto error_cleanup;
	}

725 726
	ISC_LIST_INIT(rdatalist.rdata);
	ISC_LINK_INIT(&rdatalist, link);
727
	for (i = start; i <= stop; i += step) {
728 729 730 731 732 733 734 735 736 737
		result = genname(lhs, i, lhsbuf, DNS_MASTER_BUFSZ);
		if (result != ISC_R_SUCCESS)
			goto error_cleanup;
		result = genname(rhs, i, rhsbuf, DNS_MASTER_BUFSZ);
		if (result != ISC_R_SUCCESS)
			goto error_cleanup;

		isc_buffer_init(&buffer, lhsbuf, strlen(lhsbuf));
		isc_buffer_add(&buffer, strlen(lhsbuf));
		isc_buffer_setactive(&buffer, strlen(lhsbuf));
738
		result = dns_name_fromtext(owner, &buffer, ictx->origin,
739
					   0, NULL);
740 741 742
		if (result != ISC_R_SUCCESS)
			goto error_cleanup;

743 744 745 746
		if ((lctx->options & DNS_MASTER_ZONE) != 0 &&
		    (lctx->options & DNS_MASTER_SLAVE) == 0 &&
		    !dns_name_issubdomain(owner, lctx->top))
		{
747 748 749 750 751 752
			char namebuf[DNS_NAME_FORMATSIZE];
			dns_name_format(owner, namebuf, sizeof(namebuf));
			/*
			 * Ignore out-of-zone data.
			 */
			(*callbacks->warn)(callbacks,
753
					   "%s:%lu: "
754 755 756 757 758
					   "ignoring out-of-zone data (%s)",
					   source, line, namebuf);
			continue;
		}

759 760 761 762
		isc_buffer_init(&buffer, rhsbuf, strlen(rhsbuf));
		isc_buffer_add(&buffer, strlen(rhsbuf));
		isc_buffer_setactive(&buffer, strlen(rhsbuf));

763
		result = isc_lex_openbuffer(lctx->lex, &buffer);
764 765 766 767
		if (result != ISC_R_SUCCESS)
			goto error_cleanup;

		isc_buffer_init(&target, target_mem, target_size);
768
		result = dns_rdata_fromtext(&rdata, lctx->zclass, type,
769
					    lctx->lex, ictx->origin, 0,
770
					    lctx->mctx, &target, callbacks);
771
		RUNTIME_CHECK(isc_lex_close(lctx->lex) == ISC_R_SUCCESS);
772 773 774 775 776
		if (result != ISC_R_SUCCESS)
			goto error_cleanup;

		rdatalist.type = type;
		rdatalist.covers = 0;
777 778
		rdatalist.rdclass = lctx->zclass;
		rdatalist.ttl = lctx->ttl;
779 780
		ISC_LIST_PREPEND(head, &rdatalist, link);
		ISC_LIST_APPEND(rdatalist.rdata, &rdata, link);
781
		result = commit(callbacks, lctx, &head, owner, source, line);
782
		ISC_LIST_UNLINK(rdatalist.rdata, &rdata, link);
783 784
		if (result != ISC_R_SUCCESS)
			goto error_cleanup;
785
		dns_rdata_reset(&rdata);
786 787 788 789 790 791 792 793 794 795
	}
	result = ISC_R_SUCCESS;
	goto cleanup;

 error_cleanup:
	if (result == ISC_R_NOMEMORY)
		(*callbacks->error)(callbacks, "$GENERATE: %s",
				    dns_result_totext(result));
	else
		(*callbacks->error)(callbacks, "$GENERATE: %s:%lu: %s",
796
				    source, line, dns_result_totext(result));
797 798 799 800 801 802

 insist_cleanup:
	INSIST(result != ISC_R_SUCCESS);

 cleanup:
	if (target_mem != NULL)
803
		isc_mem_put(lctx->mctx, target_mem, target_size);
804
	if (lhsbuf != NULL)
805
		isc_mem_put(lctx->mctx, lhsbuf, DNS_MASTER_BUFSZ);
806
	if (rhsbuf != NULL)
807
		isc_mem_put(lctx->mctx, rhsbuf, DNS_MASTER_BUFSZ);
808 809 810
	return (result);
}

811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826
static void
limit_ttl(dns_rdatacallbacks_t *callbacks, const char *source, unsigned int line,
	  isc_uint32_t *ttlp)
{
	if (*ttlp > 0x7fffffffUL) {
		(callbacks->warn)(callbacks,
				  "%s: %s:%lu: "
				  "$TTL %lu > MAXTTL, "
				  "setting $TTL to 0",
				  "dns_master_load",
				  source, line,
				  *ttlp);
		*ttlp = 0;
	}
}

827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864
static isc_result_t
check_ns(dns_loadctx_t *lctx, isc_token_t *token, const char *source,
	 unsigned long line)
{
	char *tmp = NULL;
	isc_result_t result = ISC_R_SUCCESS;
	void (*callback)(struct dns_rdatacallbacks *, const char *, ...);

	if ((lctx->options & DNS_MASTER_FATALNS) != 0)
		callback = lctx->callbacks->error;
	else
		callback = lctx->callbacks->warn;
		
	if (token->type == isc_tokentype_string) {
		struct in_addr addr;
		struct in6_addr addr6;

		tmp = isc_mem_strdup(lctx->mctx, DNS_AS_STR(*token));
		if (tmp == NULL)
			return (ISC_R_NOMEMORY);
		/*
		 * Catch both "1.2.3.4" and "1.2.3.4."
		 */
		if (tmp[strlen(tmp) - 1] == '.')
			tmp[strlen(tmp) - 1] = '\0';
		if (inet_aton(tmp, &addr) == 1 ||
		    inet_pton(AF_INET6, tmp, &addr6) == 1)
			result = DNS_R_NSISADDRESS;
	}
	if (result != ISC_R_SUCCESS)
		(*callback)(lctx->callbacks, "%s:%lu: NS record '%s' "
			    "appears to be an address",
			    source, line, DNS_AS_STR(*token));
	if (tmp != NULL)
		isc_mem_free(lctx->mctx, tmp);
	return (result);
}

865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881
static void
check_wildcard(dns_incctx_t *ictx, const char *source, unsigned long line,
	       dns_rdatacallbacks_t *callbacks)
{
	dns_name_t *name;

	name = (ictx->glue != NULL) ? ictx->glue : ictx->current;
	if (dns_name_internalwildcard(name)) {
		char namebuf[DNS_NAME_FORMATSIZE];

		dns_name_format(name, namebuf, sizeof(namebuf));
		(*callbacks->warn)(callbacks, "%s:%lu: warning: ownername "
				   "'%s' contains an non-terminal wildcard",
				   source, line, namebuf);
	}
}

882
static isc_result_t
883
load(dns_loadctx_t *lctx) {
Bob Halley's avatar
Bob Halley committed
884
	dns_rdataclass_t rdclass;
Bob Halley's avatar
Bob Halley committed
885
	dns_rdatatype_t type, covers;
886
	isc_uint32_t ttl_offset = 0;
887
	dns_name_t *new_name;
888 889
	isc_boolean_t current_has_delegation = ISC_FALSE;
	isc_boolean_t done = ISC_FALSE;
Mark Andrews's avatar
Mark Andrews committed
890
	isc_boolean_t finish_origin = ISC_FALSE;
891
	isc_boolean_t finish_include = ISC_FALSE;
Mark Andrews's avatar
Mark Andrews committed
892
	isc_boolean_t read_till_eol = ISC_FALSE;
893
	isc_boolean_t initialws;
894
	char *include_file = NULL;
895
	isc_token_t token;
896
	isc_result_t result = ISC_R_UNEXPECTED;
897 898 899 900 901 902 903 904 905 906
	rdatalist_head_t glue_list;
	rdatalist_head_t current_list;
	dns_rdatalist_t *this;
	dns_rdatalist_t *rdatalist = NULL;
	dns_rdatalist_t *new_rdatalist;
	int rdlcount = 0;
	int rdlcount_save = 0;
	int rdatalist_size = 0;
	isc_buffer_t buffer;
	isc_buffer_t target;
907
	isc_buffer_t target_ft;
908 909 910 911 912 913 914
	isc_buffer_t target_save;
	dns_rdata_t *rdata = NULL;
	dns_rdata_t *new_rdata;
	int rdcount = 0;
	int rdcount_save = 0;
	int rdata_size = 0;
	unsigned char *target_mem = NULL;
915
	int target_size = TSIZ;
Mark Andrews's avatar
Mark Andrews committed
916
	int new_in_use;
917 918 919
	unsigned int loop_cnt = 0;
	isc_mem_t *mctx;
	dns_rdatacallbacks_t *callbacks;
920
	dns_incctx_t *ictx;
921 922 923 924
	char *range = NULL;
	char *lhs = NULL;
	char *gtype = NULL;
	char *rhs = NULL;
925 926
	const char *source = "";
	unsigned long line = 0;
Brian Wellington's avatar
Brian Wellington committed
927
	isc_boolean_t explicit_ttl;
928
	isc_stdtime_t now;
929 930
	char classname1[DNS_RDATACLASS_FORMATSIZE];
	char classname2[DNS_RDATACLASS_FORMATSIZE];
931
	unsigned int options = 0;
932

933 934 935 936
	REQUIRE(DNS_LCTX_VALID(lctx));
	callbacks = lctx->callbacks;
	mctx = lctx->mctx;
	ictx = lctx->inc;
937 938 939 940

	ISC_LIST_INIT(glue_list);
	ISC_LIST_INIT(current_list);

941 942
	isc_stdtime_get(&now);

943 944 945 946
	/*
	 * Allocate target_size of buffer space.  This is greater than twice
	 * the maximum individual RR data size.
	 */
947 948
	target_mem = isc_mem_get(mctx, target_size);
	if (target_mem == NULL) {
949
		result = ISC_R_NOMEMORY;
Mark Andrews's avatar
Mark Andrews committed
950
		goto log_and_cleanup;
951
	}
952
	isc_buffer_init(&target, target_mem, target_size);
953
	target_save = target;
954

955 956 957 958
	if ((lctx->options & DNS_MASTER_CHECKNAMES) != 0)
		options |= DNS_RDATA_CHECKNAMES;
	if ((lctx->options & DNS_MASTER_CHECKNAMESFAIL) != 0)
		options |= DNS_RDATA_CHECKNAMESFAIL;
Mark Andrews's avatar
Mark Andrews committed
959
	source = isc_lex_getsourcename(lctx->lex);
960
	do {
961
		initialws = ISC_FALSE;
Mark Andrews's avatar
Mark Andrews committed
962
		line = isc_lex_getsourceline(lctx->lex);
963
		GETTOKEN(lctx->lex, ISC_LEXOPT_INITIALWS, &token, ISC_TRUE);
Mark Andrews's avatar
Mark Andrews committed
964
		line = isc_lex_getsourceline(lctx->lex);
965 966

		if (token.type == isc_tokentype_eof) {
967
			if (read_till_eol)
968
				WARNUNEXPECTEDEOF(lctx->lex);
969
			/* Pop the include stack? */
970
			if (ictx->parent != NULL) {
971
				COMMITALL;
972
				lctx->inc = ictx->parent;
973 974
				ictx->parent