master.c 79.5 KB
Newer Older
Mark Andrews's avatar
Mark Andrews committed
1
/*
Automatic Updater's avatar
Automatic Updater committed
2
 * Copyright (C) 2004-2009, 2011, 2012  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
Mark Andrews's avatar
Mark Andrews committed
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.
Mark Andrews's avatar
Mark Andrews committed
16
17
 */

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

/*! \file */
Mark Andrews's avatar
Mark Andrews committed
21
22
23

#include <config.h>

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

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

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

Mark Andrews's avatar
Mark Andrews committed
65
#define NBUFS 4
66
67
#define MAXWIRESZ 255

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

88
89
90
91
92
/*%
 * Buffers sizes for $GENERATE.
 */
#define DNS_MASTER_LHS 2048
#define DNS_MASTER_RHS MINTSIZ
93

94
95
typedef ISC_LIST(dns_rdatalist_t) rdatalist_head_t;

96
97
typedef struct dns_incctx dns_incctx_t;

98
/*%
99
 * Master file load state.
100
 */
101

102
struct dns_loadctx {
103
	unsigned int		magic;
104
	isc_mem_t		*mctx;
105
106
	dns_masterformat_t	format;

107
108
109
110
	dns_rdatacallbacks_t	*callbacks;
	isc_task_t		*task;
	dns_loaddonefunc_t	done;
	void			*done_arg;
111
112
113
114
115
116
117
118
119

	/* Common methods */
	isc_result_t		(*openfile)(dns_loadctx_t *lctx,
					    const char *filename);
	isc_result_t		(*load)(dns_loadctx_t *lctx);

	/* Members specific to the text format: */
	isc_lex_t		*lex;
	isc_boolean_t		keep_lex;
120
	unsigned int		options;
121
122
123
	isc_boolean_t		ttl_known;
	isc_boolean_t		default_ttl_known;
	isc_boolean_t		warn_1035;
124
	isc_boolean_t		warn_tcr;
125
	isc_boolean_t		warn_sigexpired;
126
	isc_boolean_t		seen_include;
127
128
129
	isc_uint32_t		ttl;
	isc_uint32_t		default_ttl;
	dns_rdataclass_t	zclass;
130
	dns_fixedname_t		fixed_top;
131
	dns_name_t		*top;			/*%< top of zone */
132
133
134
135

	/* Members specific to the raw format: */
	FILE			*f;
	isc_boolean_t		first;
136
	dns_masterrawheader_t	header;
137

138
	/* Which fixed buffers we are using? */
139
	unsigned int		loop_cnt;		/*% records per quantum,
140
							 * 0 => all. */
141
	isc_boolean_t		canceled;
142
	isc_mutex_t		lock;
143
	isc_result_t		result;
144
145
	/* locked by lock */
	isc_uint32_t		references;
146
	dns_incctx_t		*inc;
147
	isc_uint32_t		resign;
148
149
150

	dns_masterincludecb_t	include_cb;
	void			*include_arg;
151
152
153
154
155
156
157
158
159
160
161
162
};

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;
163
164
165
	isc_boolean_t		drop;
	unsigned int		glue_line;
	unsigned int		current_line;
166
167
168
};

#define DNS_LCTX_MAGIC ISC_MAGIC('L','c','t','x')
169
#define DNS_LCTX_VALID(lctx) ISC_MAGIC_VALID(lctx, DNS_LCTX_MAGIC)
170

171
172
#define DNS_AS_STR(t) ((t).value.as_textregion.base)

173
174
175
176
177
178
179
180
181
182
183
184
static isc_result_t
openfile_text(dns_loadctx_t *lctx, const char *master_file);

static isc_result_t
openfile_raw(dns_loadctx_t *lctx, const char *master_file);

static isc_result_t
load_text(dns_loadctx_t *lctx);

static isc_result_t
load_raw(dns_loadctx_t *lctx);

185
static isc_result_t
186
pushfile(const char *master_file, dns_name_t *origin, dns_loadctx_t *lctx);
187
188

static isc_result_t
189
commit(dns_rdatacallbacks_t *, dns_loadctx_t *, rdatalist_head_t *,
190
       dns_name_t *, const char *, unsigned int);
191
192
193
194
195
196
197
198
199
200
201
202

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 *);

203
204
205
206
static void
load_quantum(isc_task_t *task, isc_event_t *event);

static isc_result_t
207
task_send(dns_loadctx_t *lctx);
208
209

static void
210
loadctx_destroy(dns_loadctx_t *lctx);
211

Mark Andrews's avatar
Mark Andrews committed
212
213
#define GETTOKEN(lexer, options, token, eol) \
	do { \
214
215
		result = gettoken(lexer, options, token, eol, callbacks); \
		switch (result) { \
216
		case ISC_R_SUCCESS: \
Mark Andrews's avatar
Mark Andrews committed
217
			break; \
218
		case ISC_R_UNEXPECTED: \
Mark Andrews's avatar
Mark Andrews committed
219
			goto insist_and_cleanup; \
Mark Andrews's avatar
Mark Andrews committed
220
		default: \
221
222
			if (MANYERRS(lctx, result)) { \
				SETRESULT(lctx, result); \
223
224
225
226
227
				LOGIT(result); \
				read_till_eol = ISC_TRUE; \
				goto next_line; \
			} else \
				goto log_and_cleanup; \
Mark Andrews's avatar
Mark Andrews committed
228
		} \
229
230
		if ((token)->type == isc_tokentype_special) { \
			result = DNS_R_SYNTAX; \
231
232
			if (MANYERRS(lctx, result)) { \
				SETRESULT(lctx, result); \
233
234
235
236
237
				LOGIT(result); \
				read_till_eol = ISC_TRUE; \
				goto next_line; \
			} else \
				goto log_and_cleanup; \
238
		} \
239
240
	} while (0)

241
242
#define COMMITALL \
	do { \
243
		result = commit(callbacks, lctx, &current_list, \
244
				ictx->current, source, ictx->current_line); \
245
246
		if (MANYERRS(lctx, result)) { \
			SETRESULT(lctx, result); \
247
		} else if (result != ISC_R_SUCCESS) \
248
			goto insist_and_cleanup; \
249
		result = commit(callbacks, lctx, &glue_list, \
250
				ictx->glue, source, ictx->glue_line); \
251
252
		if (MANYERRS(lctx, result)) { \
			SETRESULT(lctx, result); \
253
		} else if (result != ISC_R_SUCCESS) \
254
			goto insist_and_cleanup; \
255
256
257
258
259
260
261
		rdcount = 0; \
		rdlcount = 0; \
		isc_buffer_init(&target, target_mem, target_size); \
		rdcount_save = rdcount; \
		rdlcount_save = rdlcount; \
	} while (0)

262
263
264
265
#define WARNUNEXPECTEDEOF(lexer) \
	do { \
		if (isc_lex_isfile(lexer)) \
			(*callbacks->warn)(callbacks, \
Mark Andrews's avatar
Mark Andrews committed
266
267
				"%s: file does not end with newline", \
				source); \
268
	} while (0)
Mark Andrews's avatar
Mark Andrews committed
269

Mark Andrews's avatar
redo:    
Mark Andrews committed
270
271
#define EXPECTEOL \
	do { \
272
		GETTOKEN(lctx->lex, 0, &token, ISC_TRUE); \
Mark Andrews's avatar
redo:    
Mark Andrews committed
273
		if (token.type != isc_tokentype_eol) { \
274
			isc_lex_ungettoken(lctx->lex, &token); \
Mark Andrews's avatar
redo:    
Mark Andrews committed
275
			result = DNS_R_EXTRATOKEN; \
276
277
			if (MANYERRS(lctx, result)) { \
				SETRESULT(lctx, result); \
Mark Andrews's avatar
redo:    
Mark Andrews committed
278
279
280
281
282
283
284
285
				LOGIT(result); \
				read_till_eol = ISC_TRUE; \
				continue; \
			} else if (result != ISC_R_SUCCESS) \
				goto log_and_cleanup; \
		} \
	} while (0)

286
#define MANYERRS(lctx, result) \
287
		((result != ISC_R_SUCCESS) && \
288
289
		 (result != ISC_R_IOERROR) && \
		 ((lctx)->options & DNS_MASTER_MANYERRORS) != 0)
290

291
#define SETRESULT(lctx, r) \
292
		do { \
293
294
			if ((lctx)->result == ISC_R_SUCCESS) \
				(lctx)->result = r; \
295
		} while (0)
296
297
298
299
300
301
302
303
304
305

#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)

306
307
308
309
310
311
312
#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", \
313
				    source, line, dns_result_totext(result))
314

315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352

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}
};


353
static inline isc_result_t
Mark Andrews's avatar
Mark Andrews committed
354
gettoken(isc_lex_t *lex, unsigned int options, isc_token_t *token,
Bob Halley's avatar
Bob Halley committed
355
	 isc_boolean_t eol, dns_rdatacallbacks_t *callbacks)
Mark Andrews's avatar
Mark Andrews committed
356
357
358
{
	isc_result_t result;

Andreas Gustafsson's avatar
Andreas Gustafsson committed
359
360
	options |= ISC_LEXOPT_EOL | ISC_LEXOPT_EOF | ISC_LEXOPT_DNSMULTILINE |
		ISC_LEXOPT_ESCAPE;
Mark Andrews's avatar
Mark Andrews committed
361
362
363
364
	result = isc_lex_gettoken(lex, options, token);
	if (result != ISC_R_SUCCESS) {
		switch (result) {
		case ISC_R_NOMEMORY:
365
			return (ISC_R_NOMEMORY);
Mark Andrews's avatar
Mark Andrews committed
366
		default:
367
368
369
370
371
372
373
			(*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
374
375
376
377
378
379
380
		}
		/*NOTREACHED*/
	}
	if (eol != ISC_TRUE)
		if (token->type == isc_tokentype_eol ||
		    token->type == isc_tokentype_eof) {
			(*callbacks->error)(callbacks,
381
			    "dns_master_load: %s:%lu: unexpected end of %s",
Bob Halley's avatar
Bob Halley committed
382
383
					    isc_lex_getsourcename(lex),
					    isc_lex_getsourceline(lex),
384
385
					    (token->type ==
					     isc_tokentype_eol) ?
Bob Halley's avatar
Bob Halley committed
386
					    "line" : "file");
387
			return (ISC_R_UNEXPECTEDEND);
Mark Andrews's avatar
Mark Andrews committed
388
		}
389
	return (ISC_R_SUCCESS);
Mark Andrews's avatar
Mark Andrews committed
390
}
Mark Andrews's avatar
Mark Andrews committed
391

392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408

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
409
410
dns_loadctx_detach(dns_loadctx_t **lctxp) {
	dns_loadctx_t *lctx;
411
	isc_boolean_t need_destroy = ISC_FALSE;
412

413
414
415
	REQUIRE(lctxp != NULL);
	lctx = *lctxp;
	REQUIRE(DNS_LCTX_VALID(lctx));
416

417
418
419
420
	LOCK(&lctx->lock);
	INSIST(lctx->references > 0);
	lctx->references--;
	if (lctx->references == 0)
421
		need_destroy = ISC_TRUE;
422
	UNLOCK(&lctx->lock);
423
424

	if (need_destroy)
425
426
427
428
429
430
431
432
433
434
435
436
		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
437
	isc_mem_put(mctx, ictx, sizeof(*ictx));
438
439
440
441
442

	if (parent != NULL) {
		ictx = parent;
		goto again;
	}
443
444
445
}

static void
446
loadctx_destroy(dns_loadctx_t *lctx) {
447
	isc_mem_t *mctx;
448
	isc_result_t result;
449

450
	REQUIRE(DNS_LCTX_VALID(lctx));
451

452
453
454
	lctx->magic = 0;
	if (lctx->inc != NULL)
		incctx_destroy(lctx->mctx, lctx->inc);
455

456
457
458
459
460
461
462
463
464
	if (lctx->f != NULL) {
		result = isc_stdio_close(lctx->f);
		if (result != ISC_R_SUCCESS) {
			UNEXPECTED_ERROR(__FILE__, __LINE__,
					 "isc_stdio_close() failed: %s",
					 isc_result_totext(result));
		}
	}

465
	/* isc_lex_destroy() will close all open streams */
466
	if (lctx->lex != NULL && !lctx->keep_lex)
467
		isc_lex_destroy(&lctx->lex);
468

469
470
471
	if (lctx->task != NULL)
		isc_task_detach(&lctx->task);
	DESTROYLOCK(&lctx->lock);
472
	mctx = NULL;
473
474
475
	isc_mem_attach(lctx->mctx, &mctx);
	isc_mem_detach(&lctx->mctx);
	isc_mem_put(mctx, lctx, sizeof(*lctx));
476
477
478
	isc_mem_detach(&mctx);
}

479
480
481
482
483
484
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
485
	ictx = isc_mem_get(mctx, sizeof(*ictx));
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
	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;
505
506
507
	ictx->drop = ISC_FALSE;
	ictx->glue_line = 0;
	ictx->current_line = 0;
508
509
510
511
512

	*ictxp = ictx;
	return (ISC_R_SUCCESS);
}

513
static isc_result_t
514
loadctx_create(dns_masterformat_t format, isc_mem_t *mctx,
515
	       unsigned int options, isc_uint32_t resign, dns_name_t *top,
516
517
	       dns_rdataclass_t zclass, dns_name_t *origin,
	       dns_rdatacallbacks_t *callbacks, isc_task_t *task,
518
519
520
	       dns_loaddonefunc_t done, void *done_arg,
	       dns_masterincludecb_t include_cb, void *include_arg,
	       isc_lex_t *lex, dns_loadctx_t **lctxp)
521
{
522
	dns_loadctx_t *lctx;
523
524
525
526
	isc_result_t result;
	isc_region_t r;
	isc_lexspecials_t specials;

527
	REQUIRE(lctxp != NULL && *lctxp == NULL);
528
529
530
531
532
533
534
535
	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) ||
536
		(task != NULL && done != NULL));
537

538
539
	lctx = isc_mem_get(mctx, sizeof(*lctx));
	if (lctx == NULL)
540
		return (ISC_R_NOMEMORY);
541
	result = isc_mutex_init(&lctx->lock);
542
	if (result != ISC_R_SUCCESS) {
Andreas Gustafsson's avatar
Andreas Gustafsson committed
543
		isc_mem_put(mctx, lctx, sizeof(*lctx));
544
		return (result);
545
	}
546

547
548
	lctx->inc = NULL;
	result = incctx_create(mctx, origin, &lctx->inc);
Automatic Updater's avatar
Automatic Updater committed
549
	if (result != ISC_R_SUCCESS)
550
		goto cleanup_ctx;
551

552
553
554
555
556
557
558
559
560
561
562
563
564
565
	lctx->format = format;
	switch (format) {
	default:
		INSIST(0);
	case dns_masterformat_text:
		lctx->openfile = openfile_text;
		lctx->load = load_text;
		break;
	case dns_masterformat_raw:
		lctx->openfile = openfile_raw;
		lctx->load = load_raw;
		break;
	}

566
	if (lex != NULL) {
567
		lctx->lex = lex;
568
569
		lctx->keep_lex = ISC_TRUE;
	} else {
570
571
572
573
		lctx->lex = NULL;
		result = isc_lex_create(mctx, TOKENSIZ, &lctx->lex);
		if (result != ISC_R_SUCCESS)
			goto cleanup_inc;
574
		lctx->keep_lex = ISC_FALSE;
575
576
577
578
579
580
581
		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);
	}
582
583
584
585
586
587

	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? */
588
	lctx->warn_tcr = ISC_TRUE;	/* XXX Argument? */
589
	lctx->warn_sigexpired = ISC_TRUE;	/* XXX Argument? */
590
591
592
	lctx->options = options;
	lctx->seen_include = ISC_FALSE;
	lctx->zclass = zclass;
593
	lctx->resign = resign;
594
	lctx->result = ISC_R_SUCCESS;
595
596
	lctx->include_cb = include_cb;
	lctx->include_arg = include_arg;
597
598
599

	dns_fixedname_init(&lctx->fixed_top);
	lctx->top = dns_fixedname_name(&lctx->fixed_top);
600
	dns_name_toregion(top, &r);
601
	dns_name_fromregion(lctx->top, &r);
602

603
604
	lctx->f = NULL;
	lctx->first = ISC_TRUE;
605
	dns_master_initrawheader(&lctx->header);
606

607
608
609
	lctx->loop_cnt = (done != NULL) ? 100 : 0;
	lctx->callbacks = callbacks;
	lctx->task = NULL;
610
	if (task != NULL)
611
612
613
614
615
616
617
618
619
		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;
620
621
	return (ISC_R_SUCCESS);

622
623
 cleanup_inc:
	incctx_destroy(mctx, lctx->inc);
624
 cleanup_ctx:
625
	isc_mem_put(mctx, lctx, sizeof(*lctx));
626
	return (result);
627
}
628

629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
static const char *hex = "0123456789abcdef0123456789ABCDEF";

/*%
 * Convert value into a nibble sequence from least significant to most
 * significant nibble.  Zero fill upper most significant nibbles if
 * required to make the width.
 *
 * Returns the number of characters that should have been written without
 * counting the terminating NUL.
 */
static unsigned int
nibbles(char *numbuf, size_t length, unsigned int width, char mode, int value) {
	unsigned int count = 0;

	/*
	 * This reserve space for the NUL string terminator.
	 */
646
	if (length > 0U) {
647
648
649
650
651
652
		*numbuf = '\0';
		length--;
	}
	do {
		char val = hex[(value & 0x0f) + ((mode == 'n') ? 0 : 16)];
		value >>= 4;
653
		if (length > 0U) {
654
655
656
657
658
659
660
661
662
663
664
665
666
			*numbuf++ = val;
			*numbuf = '\0';
			length--;
		}
		if (width > 0)
			width--;
		count++;
		/*
		 * If width is non zero then we need to add a label seperator.
		 * If value is non zero then we need to add another label and
		 * that requires a label seperator.
		 */
		if (width > 0 || value != 0) {
667
			if (length > 0U) {
668
669
670
671
672
673
674
675
676
677
678
679
				*numbuf++ = '.';
				*numbuf = '\0';
				length--;
			}
			if (width > 0)
				width--;
			count++;
		}
	} while (value != 0 || width > 0);
	return (count);
}

680
681
682
683
684
static isc_result_t
genname(char *name, int it, char *buffer, size_t length) {
	char fmt[sizeof("%04000000000d")];
	char numbuf[128];
	char *cp;
685
	char mode[2];
686
687
688
689
	int delta = 0;
	isc_textregion_t r;
	unsigned int n;
	unsigned int width;
690
	isc_boolean_t nibblemode;
691
692
693
694
695
696
697

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

	while (*name != '\0') {
		if (*name == '$') {
			name++;
698
699
700
701
702
703
704
			if (*name == '$') {
				if (r.length == 0)
					return (ISC_R_NOSPACE);
				r.base[0] = *name++;
				isc_textregion_consume(&r, 1);
				continue;
			}
705
			nibblemode = ISC_FALSE;
706
707
708
			strcpy(fmt, "%d");
			/* Get format specifier. */
			if (*name == '{' ) {
709
				n = sscanf(name, "{%d,%u,%1[doxXnN]}",
710
					   &delta, &width, mode);
711
712
713
714
715
716
717
718
				switch (n) {
				case 1:
					break;
				case 2:
					n = snprintf(fmt, sizeof(fmt),
						     "%%0%ud", width);
					break;
				case 3:
719
720
					if (mode[0] == 'n' || mode[0] == 'N')
						nibblemode = ISC_TRUE;
721
					n = snprintf(fmt, sizeof(fmt),
722
						     "%%0%u%c", width, mode[0]);
723
724
725
726
727
728
729
730
731
732
					break;
				default:
					return (DNS_R_SYNTAX);
				}
				if (n >= sizeof(fmt))
					return (ISC_R_NOSPACE);
				/* Skip past closing brace. */
				while (*name != '\0' && *name++ != '}')
					continue;
			}
733
734
735
736
737
738
			if (nibblemode)
				n = nibbles(numbuf, sizeof(numbuf), width,
					    mode[0], it + delta);
			else
				n = snprintf(numbuf, sizeof(numbuf), fmt,
					     it + delta);
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
			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);
}

772
773
774
775
776
777
778
779
780
781
782
783
784
static isc_result_t
openfile_text(dns_loadctx_t *lctx, const char *master_file) {
	return (isc_lex_openfile(lctx->lex, master_file));
}

static isc_result_t
openfile_raw(dns_loadctx_t *lctx, const char *master_file) {
	isc_result_t result;

	result = isc_stdio_open(master_file, "r", &lctx->f);
	if (result != ISC_R_SUCCESS && result != ISC_R_FILENOTFOUND) {
		UNEXPECTED_ERROR(__FILE__, __LINE__,
				 "isc_stdio_open() failed: %s",
Automatic Updater's avatar
Automatic Updater committed
785
				 isc_result_totext(result));
786
787
788
789
790
	}

	return (result);
}

791
static isc_result_t
792
793
794
generate(dns_loadctx_t *lctx, char *range, char *lhs, char *gtype, char *rhs,
	 const char *source, unsigned int line)
{
795
796
797
798
799
	char *target_mem = NULL;
	char *lhsbuf = NULL;
	char *rhsbuf = NULL;
	dns_fixedname_t ownerfixed;
	dns_name_t *owner;
800
	dns_rdata_t rdata = DNS_RDATA_INIT;
801
802
803
804
805
806
807
808
809
810
811
	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;
812
	dns_incctx_t *ictx;
813

814
815
	ictx = lctx->inc;
	callbacks = lctx->callbacks;
816
817
818
819
	dns_fixedname_init(&ownerfixed);
	owner = dns_fixedname_name(&ownerfixed);
	ISC_LIST_INIT(head);

820
	target_mem = isc_mem_get(lctx->mctx, target_size);
821
822
	rhsbuf = isc_mem_get(lctx->mctx, DNS_MASTER_RHS);
	lhsbuf = isc_mem_get(lctx->mctx, DNS_MASTER_LHS);
823
824
825
826
827
828
829
	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);
830
	if (n < 2 || stop < start) {
831
	       (*callbacks->error)(callbacks,
832
				  "%s: %s:%lu: invalid range '%s'",
833
				  "$GENERATE", source, line, range);
834
835
836
837
838
839
840
841
842
843
844
845
846
		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) {
847
		(*callbacks->error)(callbacks,
848
				   "%s: %s:%lu: unknown RR type '%s'",
849
				   "$GENERATE", source, line, gtype);
850
851
852
		goto insist_cleanup;
	}

853
854
	ISC_LIST_INIT(rdatalist.rdata);
	ISC_LINK_INIT(&rdatalist, link);
855
	for (i = start; i <= stop; i += step) {
856
		result = genname(lhs, i, lhsbuf, DNS_MASTER_LHS);
857
858
		if (result != ISC_R_SUCCESS)
			goto error_cleanup;
859
		result = genname(rhs, i, rhsbuf, DNS_MASTER_RHS);
860
861
862
863
864
865
		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));
866
		result = dns_name_fromtext(owner, &buffer, ictx->origin,
867
					   0, NULL);
868
869
870
		if (result != ISC_R_SUCCESS)
			goto error_cleanup;

871
872
		if ((lctx->options & DNS_MASTER_ZONE) != 0 &&
		    (lctx->options & DNS_MASTER_SLAVE) == 0 &&
873
		    (lctx->options & DNS_MASTER_KEY) == 0 &&
874
875
		    !dns_name_issubdomain(owner, lctx->top))
		{
876
877
878
879
880
881
			char namebuf[DNS_NAME_FORMATSIZE];
			dns_name_format(owner, namebuf, sizeof(namebuf));
			/*
			 * Ignore out-of-zone data.
			 */
			(*callbacks->warn)(callbacks,
882
					   "%s:%lu: "
883
884
885
886
887
					   "ignoring out-of-zone data (%s)",
					   source, line, namebuf);
			continue;
		}

888
889
890
891
		isc_buffer_init(&buffer, rhsbuf, strlen(rhsbuf));
		isc_buffer_add(&buffer, strlen(rhsbuf));
		isc_buffer_setactive(&buffer, strlen(rhsbuf));

892
		result = isc_lex_openbuffer(lctx->lex, &buffer);
893
894
895
896
		if (result != ISC_R_SUCCESS)
			goto error_cleanup;

		isc_buffer_init(&target, target_mem, target_size);
897
		result = dns_rdata_fromtext(&rdata, lctx->zclass, type,
898
					    lctx->lex, ictx->origin, 0,
899
					    lctx->mctx, &target, callbacks);
900
		RUNTIME_CHECK(isc_lex_close(lctx->lex) == ISC_R_SUCCESS);
901
902
903
904
905
		if (result != ISC_R_SUCCESS)
			goto error_cleanup;

		rdatalist.type = type;
		rdatalist.covers = 0;
906
907
		rdatalist.rdclass = lctx->zclass;
		rdatalist.ttl = lctx->ttl;
908
909
		ISC_LIST_PREPEND(head, &rdatalist, link);
		ISC_LIST_APPEND(rdatalist.rdata, &rdata, link);
910
		result = commit(callbacks, lctx, &head, owner, source, line);
911
		ISC_LIST_UNLINK(rdatalist.rdata, &rdata, link);
912
913
		if (result != ISC_R_SUCCESS)
			goto error_cleanup;
914
		dns_rdata_reset(&rdata);
915
916
917
918
919
920
921
922
923
924
	}
	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",
925
				    source, line, dns_result_totext(result));
926
927
928
929
930
931

 insist_cleanup:
	INSIST(result != ISC_R_SUCCESS);

 cleanup:
	if (target_mem != NULL)
932
		isc_mem_put(lctx->mctx, target_mem, target_size);
933
	if (lhsbuf != NULL)
934
		isc_mem_put(lctx->mctx, lhsbuf, DNS_MASTER_LHS);
935
	if (rhsbuf != NULL)
936
		isc_mem_put(lctx->mctx, rhsbuf, DNS_MASTER_RHS);
937
938
939
	return (result);
}

940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
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;
	}
}

956
957
958
959
960
961
962
963
964
965
966
967
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;
Automatic Updater's avatar
Automatic Updater committed
968

969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
	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);
}

994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
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);
	}
}

1011
static isc_result_t
1012
load_text(dns_loadctx_t *lctx) {
Bob Halley's avatar
Bob Halley committed
1013
	dns_rdataclass_t rdclass;
Bob Halley's avatar
Bob Halley committed
1014
	dns_rdatatype_t type, covers;
1015
	isc_uint32_t ttl_offset = 0;
1016
	dns_name_t *new_name;
1017
1018
	isc_boolean_t current_has_delegation = ISC_FALSE;
	isc_boolean_t done = ISC_FALSE;
Mark Andrews's avatar
Mark Andrews committed
1019
	isc_boolean_t finish_origin = ISC_FALSE;
1020
	isc_boolean_t finish_include = ISC_FALSE;
Mark Andrews's avatar
Mark Andrews committed
1021
	isc_boolean_t read_till_eol = ISC_FALSE;
1022
	isc_boolean_t initialws;
1023
	char *include_file = NULL;
1024
	isc_token_t token;
1025
	isc_result_t result = ISC_R_UNEXPECTED;
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
	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;
1036
	isc_buffer_t target_ft;
1037
1038
1039
1040
1041
1042
1043
	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;
1044
	int target_size = TSIZ;
Mark Andrews's avatar
Mark Andrews committed
1045
	int new_in_use;
1046
1047
1048
	unsigned int loop_cnt = 0;
	isc_mem_t *mctx;
	dns_rdatacallbacks_t *callbacks;
1049
	dns_incctx_t *ictx;
1050
1051
1052
1053
	char *range = NULL;
	char *lhs = NULL;
	char *gtype = NULL;
	char *rhs = NULL;
1054
1055
	const char *source = "";
	unsigned long line = 0;
Brian Wellington's avatar
Brian Wellington committed
1056
	isc_boolean_t explicit_ttl;
1057
	isc_stdtime_t now;
1058
1059
	char classname1[DNS_RDATACLASS_FORMATSIZE];
	char classname2[DNS_RDATACLASS_FORMATSIZE];
1060
	unsigned int options = 0;
1061

1062
1063
1064
1065
	REQUIRE(DNS_LCTX_VALID(lctx));
	callbacks = lctx->callbacks;
	mctx = lctx->mctx;
	ictx = lctx->inc;
1066
1067
1068
1069

	ISC_LIST_INIT(glue_list);
	ISC_LIST_INIT(current_list);

1070
1071
	isc_stdtime_get(&now);

1072
1073
1074
1075
	/*
	 * Allocate target_size of buffer space.  This is greater than twice
	 * the maximum individual RR data size.
	 */
1076
1077
	target_mem = isc_mem_get(mctx, target_size);
	if (target_mem == NULL) {
1078
		result = ISC_R_NOMEMORY;
Mark Andrews's avatar
Mark Andrews committed
1079
		goto log_and_cleanup;
1080
	}
1081
	isc_buffer_init(&target, target_mem, target_size);
1082
	target_save = target;
1083

1084
1085
1086
1087
	if ((lctx->options & DNS_MASTER_CHECKNAMES) != 0)
		options |= DNS_RDATA_CHECKNAMES;
	if ((lctx->options & DNS_MASTER_CHECKNAMESFAIL) != 0)
		options |= DNS_RDATA_CHECKNAMESFAIL;
1088
1089
1090
1091
	if ((lctx->options & DNS_MASTER_CHECKMX) != 0)
		options |= DNS_RDATA_CHECKMX;
	if ((lctx->options & DNS_MASTER_CHECKMXFAIL) != 0)
		options |= DNS_RDATA_CHECKMXFAIL;
Mark Andrews's avatar
Mark Andrews committed
1092
	source = isc_lex_getsourcename(lctx->lex);
1093
	do {
1094
		initialws = ISC_FALSE;
Mark Andrews's avatar
Mark Andrews committed
1095
		line = isc_lex_getsourceline(lctx->lex);
1096
1097
		GETTOKEN(lctx->lex, ISC_LEXOPT_INITIALWS | ISC_LEXOPT_QSTRING,
			 &token, ISC_TRUE);
Mark Andrews's avatar
Mark Andrews committed
1098
		line = isc_lex_getsourceline(lctx->lex);
1099
1100

		if (token.type == isc_tokentype_eof) {
1101
			if (read_till_eol)
1102
				WARNUNEXPECTEDEOF(lctx->lex);
1103
			/* Pop the include stack? */
1104
			if (ictx->parent != NULL) {
1105
				COMMITALL;
1106
				lctx->inc = ictx->parent;
1107
1108
				ictx->parent = NULL;
				incctx_destroy(lctx->