check.c 23.4 KB
Newer Older
1
/*
Mark Andrews's avatar
Mark Andrews committed
2
 * Copyright (C) 2001, 2002  Internet Software Consortium.
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
 *
 * 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.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM
 * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
 * INTERNET SOFTWARE CONSORTIUM 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.
 */

Brian Wellington's avatar
Brian Wellington committed
18
/* $Id: check.c,v 1.31 2002/03/14 00:43:00 bwelling Exp $ */
19
20
21
22
23
24

#include <config.h>

#include <stdlib.h>
#include <string.h>

25
#include <isc/buffer.h>
26
#include <isc/log.h>
27
#include <isc/mem.h>
28
#include <isc/netaddr.h>
29
#include <isc/region.h>
30
#include <isc/result.h>
31
#include <isc/sockaddr.h>
32
#include <isc/symtab.h>
Brian Wellington's avatar
Brian Wellington committed
33
#include <isc/util.h>
34

35
#include <dns/fixedname.h>
Mark Andrews's avatar
Mark Andrews committed
36
37
#include <dns/rdataclass.h>
#include <dns/rdatatype.h>
38
39

#include <isccfg/cfg.h>
40
41

#include <bind9/check.h>
42

Mark Andrews's avatar
Mark Andrews committed
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
static isc_result_t
check_orderent(cfg_obj_t *ent, isc_log_t *logctx) {
	isc_result_t result = ISC_R_SUCCESS;
	isc_result_t tresult;
	isc_textregion_t r;
	dns_fixedname_t fixed;
	cfg_obj_t *obj;
	dns_rdataclass_t rdclass;
	dns_rdatatype_t rdtype;
	isc_buffer_t b;
	const char *str;

	dns_fixedname_init(&fixed);
	obj = cfg_tuple_get(ent, "class");
	if (cfg_obj_isstring(obj)) {

		DE_CONST(cfg_obj_asstring(obj), r.base);
		r.length = strlen(r.base);
		tresult = dns_rdataclass_fromtext(&rdclass, &r);
		if (tresult != ISC_R_SUCCESS) {
			cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
				    "rrset-order: invalid class '%s'",
				    r.base);
			result = ISC_R_FAILURE;
		}
	}

	obj = cfg_tuple_get(ent, "type");
	if (cfg_obj_isstring(obj)) {

		DE_CONST(cfg_obj_asstring(obj), r.base);
		r.length = strlen(r.base);
		tresult = dns_rdatatype_fromtext(&rdtype, &r);
		if (tresult != ISC_R_SUCCESS) {
			cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
				    "rrset-order: invalid type '%s'",
				    r.base);
			result = ISC_R_FAILURE;
		}
	}

	obj = cfg_tuple_get(ent, "name");
	if (cfg_obj_isstring(obj)) {
		str = cfg_obj_asstring(obj);
		isc_buffer_init(&b, str, strlen(str));
		isc_buffer_add(&b, strlen(str));
		tresult = dns_name_fromtext(dns_fixedname_name(&fixed), &b,
					    dns_rootname, ISC_FALSE, NULL);
		if (tresult != ISC_R_SUCCESS) {
			cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
				    "rrset-order: invalid name '%s'", str);
			result = ISC_R_FAILURE;
		}
	}

	obj = cfg_tuple_get(ent, "order");
	if (!cfg_obj_isstring(obj) ||
	    strcasecmp("order", cfg_obj_asstring(obj)) != 0) {
		cfg_obj_log(ent, logctx, ISC_LOG_ERROR,
			    "rrset-order: keyword 'order' missing");
		result = ISC_R_FAILURE;
	}

	obj = cfg_tuple_get(ent, "ordering");
	if (!cfg_obj_isstring(obj)) {
	    cfg_obj_log(ent, logctx, ISC_LOG_ERROR,
			"rrset-order: missing ordering");
		result = ISC_R_FAILURE;
111
112
	} else if (strcasecmp(cfg_obj_asstring(obj), "fixed") == 0) {
		cfg_obj_log(obj, logctx, ISC_LOG_WARNING,
Brian Wellington's avatar
Brian Wellington committed
113
			    "rrset-order: order 'fixed' not implemented");
114
	} else if (/* strcasecmp(cfg_obj_asstring(obj), "fixed") != 0 && */
Mark Andrews's avatar
Mark Andrews committed
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
		   strcasecmp(cfg_obj_asstring(obj), "random") != 0 &&
		   strcasecmp(cfg_obj_asstring(obj), "cyclic") != 0) {
		cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
			    "rrset-order: invalid order '%s'",
			    cfg_obj_asstring(obj));
		result = ISC_R_FAILURE;
	}
	return (result);
}

static isc_result_t
check_order(cfg_obj_t *options, isc_log_t *logctx) {
	isc_result_t result = ISC_R_SUCCESS;
	isc_result_t tresult;
	cfg_listelt_t *element;
	cfg_obj_t *obj = NULL;

	if (cfg_map_get(options, "rrset-order", &obj) != ISC_R_SUCCESS)
		return (result);

	for (element = cfg_list_first(obj);
	     element != NULL;
	     element = cfg_list_next(element))
	{
		tresult = check_orderent(cfg_listelt_value(element), logctx);
		if (tresult != ISC_R_SUCCESS)
			result = tresult;
	}
	return (result);
}

146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
static isc_result_t
check_forward(cfg_obj_t *options, isc_log_t *logctx) {
	cfg_obj_t *forward = NULL;
	cfg_obj_t *forwarders = NULL;

	(void)cfg_map_get(options, "forward", &forward);
	(void)cfg_map_get(options, "forwarders", &forwarders);

	if (forward != NULL && forwarders == NULL) {
		cfg_obj_log(forward, logctx, ISC_LOG_ERROR,
			    "no matching 'forwarders' statement");
		return (ISC_R_FAILURE);
	}
	return (ISC_R_SUCCESS);
}

162
163
164
typedef struct {
	const char *name;
	unsigned int scale;
165
	unsigned int max;
166
167
168
} intervaltable;

static isc_result_t
169
check_options(cfg_obj_t *options, isc_log_t *logctx) {
170
171
	isc_result_t result = ISC_R_SUCCESS;
	unsigned int i;
172
	cfg_obj_t *obj = NULL;
173
174

	static intervaltable intervals[] = {
175
176
177
178
179
180
181
182
183
	{ "cleaning-interval", 60, 28 * 24 * 60 },	/* 28 days */
	{ "heartbeat-interval", 60, 28 * 24 * 60 },	/* 28 days */
	{ "interface-interval", 60, 28 * 24 * 60 },	/* 28 days */
	{ "max-transfer-idle-in", 60, 28 * 24 * 60 },	/* 28 days */
	{ "max-transfer-idle-out", 60, 28 * 24 * 60 },	/* 28 days */
	{ "max-transfer-time-in", 60, 28 * 24 * 60 },	/* 28 days */
	{ "max-transfer-time-out", 60, 28 * 24 * 60 },	/* 28 days */
	{ "sig-validity-interval", 86400, 10 * 366 },	/* 10 years */
	{ "statistics-interval", 60, 28 * 24 * 60 },	/* 28 days */
184
185
186
187
188
189
190
191
	};

	/*
	 * Check that fields specified in units of time other than seconds
	 * have reasonable values.
	 */
	for (i = 0; i < sizeof(intervals) / sizeof(intervals[0]); i++) {
		isc_uint32_t val;
192
		obj = NULL;
193
194
195
196
		(void)cfg_map_get(options, intervals[i].name, &obj);
		if (obj == NULL)
			continue;
		val = cfg_obj_asuint32(obj);
197
198
199
200
201
202
203
		if (val > intervals[i].max) {
			cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
				    "%s '%u' is out of range (0..%u)",
				    intervals[i].name, val,
				    intervals[i].max);
			result = ISC_R_RANGE;
		} else if (val > (ISC_UINT32_MAX / intervals[i].scale)) {
204
205
206
207
208
209
210
211
212
			cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
				    "%s '%d' is out of range",
				    intervals[i].name, val);
			result = ISC_R_RANGE;
		}
	}
	return (result);
}

213
214
215
216
217
218
219
220
221
222
223
224
#define MASTERZONE	1
#define SLAVEZONE	2
#define STUBZONE	4
#define HINTZONE	8
#define FORWARDZONE	16

typedef struct {
	const char *name;
	int allowed;
} optionstable;

static isc_result_t
225
check_zoneconf(cfg_obj_t *zconfig, isc_symtab_t *symtab,
226
	       dns_rdataclass_t defclass, isc_log_t *logctx, isc_mem_t *mctx)
227
{
228
229
230
231
232
	const char *zname;
	const char *typestr;
	unsigned int ztype;
	cfg_obj_t *zoptions;
	cfg_obj_t *obj = NULL;
233
	cfg_obj_t *addrlist = NULL;
234
	isc_symvalue_t symvalue;
Brian Wellington's avatar
Brian Wellington committed
235
	isc_result_t result = ISC_R_SUCCESS;
236
	isc_result_t tresult;
237
	unsigned int i;
238
	dns_rdataclass_t zclass;
239
240
	dns_fixedname_t fixedname;
	isc_buffer_t b;
241
242
243

	static optionstable options[] = {
	{ "allow-query", MASTERZONE | SLAVEZONE | STUBZONE },
244
	{ "allow-notify", SLAVEZONE },
245
	{ "allow-transfer", MASTERZONE | SLAVEZONE },
246
247
248
249
250
251
252
	{ "notify", MASTERZONE | SLAVEZONE },
	{ "also-notify", MASTERZONE | SLAVEZONE },
	{ "dialup", MASTERZONE | SLAVEZONE | STUBZONE },
	{ "forward", MASTERZONE | SLAVEZONE | STUBZONE | FORWARDZONE},
	{ "forwarders", MASTERZONE | SLAVEZONE | STUBZONE | FORWARDZONE},
	{ "maintain-ixfr-base", MASTERZONE | SLAVEZONE },
	{ "max-ixfr-log-size", MASTERZONE | SLAVEZONE },
253
254
	{ "notify-source", MASTERZONE | SLAVEZONE },
	{ "notify-source-v6", MASTERZONE | SLAVEZONE },
255
256
	{ "transfer-source", SLAVEZONE | STUBZONE },
	{ "transfer-source-v6", SLAVEZONE | STUBZONE },
257
258
259
260
261
262
263
264
265
266
267
	{ "max-transfer-time-in", SLAVEZONE | STUBZONE },
	{ "max-transfer-time-out", MASTERZONE | SLAVEZONE },
	{ "max-transfer-idle-in", SLAVEZONE | STUBZONE },
	{ "max-transfer-idle-out", MASTERZONE | SLAVEZONE },
	{ "max-retry-time", SLAVEZONE | STUBZONE },
	{ "min-retry-time", SLAVEZONE | STUBZONE },
	{ "max-refresh-time", SLAVEZONE | STUBZONE },
	{ "min-refresh-time", SLAVEZONE | STUBZONE },
	{ "sig-validity-interval", MASTERZONE },
	{ "zone-statistics", MASTERZONE | SLAVEZONE | STUBZONE },
	{ "allow-update", MASTERZONE },
268
	{ "allow-update-forwarding", SLAVEZONE },
269
270
271
272
273
274
275
	{ "file", MASTERZONE | SLAVEZONE | STUBZONE | HINTZONE},
	{ "ixfr-base", MASTERZONE | SLAVEZONE },
	{ "ixfr-tmp-file", MASTERZONE | SLAVEZONE },
	{ "masters", SLAVEZONE | STUBZONE },
	{ "pubkey", MASTERZONE | SLAVEZONE | STUBZONE },
	{ "update-policy", MASTERZONE },
	{ "database", MASTERZONE | SLAVEZONE | STUBZONE },
276
	{ "key-directory", MASTERZONE },
277
278
279
280
281
282
283
284
285
	};

	static optionstable dialups[] = {
	{ "notify", MASTERZONE | SLAVEZONE },
	{ "notify-passive", SLAVEZONE },
	{ "refresh", SLAVEZONE | STUBZONE },
	{ "passive", SLAVEZONE | STUBZONE },
	};

Brian Wellington's avatar
bugs    
Brian Wellington committed
286
287
288
	zname = cfg_obj_asstring(cfg_tuple_get(zconfig, "name"));

	zoptions = cfg_tuple_get(zconfig, "options");
289
290

	obj = NULL;
Brian Wellington's avatar
bugs    
Brian Wellington committed
291
	(void)cfg_map_get(zoptions, "type", &obj);
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
	if (obj == NULL) {
		cfg_obj_log(zconfig, logctx, ISC_LOG_ERROR,
			    "zone '%s': type not present", zname);
		return (ISC_R_FAILURE);
	}

	typestr = cfg_obj_asstring(obj);
	if (strcasecmp(typestr, "master") == 0)
		ztype = MASTERZONE;
	else if (strcasecmp(typestr, "slave") == 0)
		ztype = SLAVEZONE;
	else if (strcasecmp(typestr, "stub") == 0)
		ztype = STUBZONE;
	else if (strcasecmp(typestr, "forward") == 0)
		ztype = FORWARDZONE;
	else if (strcasecmp(typestr, "hint") == 0)
		ztype = HINTZONE;
	else {
		cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
			    "zone '%s': invalid type %s",
			    zname, typestr);
		return (ISC_R_FAILURE);
	}

316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
	obj = cfg_tuple_get(zconfig, "class");
	if (cfg_obj_isstring(obj)) {
		isc_textregion_t r;

		DE_CONST(cfg_obj_asstring(obj), r.base);
		r.length = strlen(r.base);
		result = dns_rdataclass_fromtext(&zclass, &r);
		if (result != ISC_R_SUCCESS) {
			cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
				    "zone '%s': invalid class %s",
				    zname, r.base);
			return (ISC_R_FAILURE);
		}
		if (zclass != defclass) {
			cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
				    "zone '%s': class '%s' does not "
				    "match view/default class",
				    zname, r.base);
			return (ISC_R_FAILURE);
		}
	}

338
339
	/*
	 * Look for an already existing zone.
340
341
	 * We need to make this cannonical as isc_symtab_define()
	 * deals with strings.
342
	 */
343
344
345
346
347
348
	dns_fixedname_init(&fixedname);
	isc_buffer_init(&b, zname, strlen(zname));
	isc_buffer_add(&b, strlen(zname));
	result = dns_name_fromtext(dns_fixedname_name(&fixedname), &b,
				   dns_rootname, ISC_TRUE, NULL);
	if (result != ISC_R_SUCCESS) {
349
		cfg_obj_log(zconfig, logctx, ISC_LOG_ERROR,
350
			    "zone '%s': is not a valid name", zname);
351
		result = ISC_R_FAILURE;
352
353
	} else {
		char namebuf[DNS_NAME_FORMATSIZE];
354
355
		char *key;

356
357
		dns_name_format(dns_fixedname_name(&fixedname),
				namebuf, sizeof(namebuf));
358
359
360
		key = isc_mem_strdup(mctx, namebuf);
		if (key == NULL)
			return (ISC_R_NOMEMORY);
361
		symvalue.as_pointer = zconfig;
362
		tresult = isc_symtab_define(symtab, key,
363
364
365
					    ztype == HINTZONE ? 1 : 2,
					    symvalue, isc_symexists_reject);
		if (tresult == ISC_R_EXISTS) {
366
367
368
369
370
371
			const char *file;
			unsigned int line;

			RUNTIME_CHECK(isc_symtab_lookup(symtab, key,
					    ztype == HINTZONE ? 1 : 2,
					    &symvalue) == ISC_R_SUCCESS);
Mark Andrews's avatar
Mark Andrews committed
372
			isc_mem_free(mctx, key);
373
374
375
376
377
			file = cfg_obj_file(symvalue.as_pointer);
			line = cfg_obj_line(symvalue.as_pointer);

			if (file == NULL)
				file = "<unknown file>";
378
			cfg_obj_log(zconfig, logctx, ISC_LOG_ERROR,
379
380
381
				    "zone '%s': already exists "
				    "previous definition: %s:%u",
				    zname, file, line);
382
			result = ISC_R_FAILURE;
Mark Andrews's avatar
Mark Andrews committed
383
384
		} else if (tresult != ISC_R_SUCCESS) {
			isc_mem_strdup(mctx, key);
385
			return (tresult);
Mark Andrews's avatar
Mark Andrews committed
386
		}
387
	}
388
389
390
391

	/*
	 * Look for inappropriate options for the given zone type.
	 */
392
393
394
395
396
397
398
399
400
401
402
403
404
405
	for (i = 0; i < sizeof(options) / sizeof(options[0]); i++) {
		obj = NULL;
		if ((options[i].allowed & ztype) == 0 &&
		    cfg_map_get(zoptions, options[i].name, &obj) ==
		    ISC_R_SUCCESS)
		{
			cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
				    "option '%s' is not allowed in '%s' "
				    "zone '%s'",
				    options[i].name, typestr, zname);
			result = ISC_R_FAILURE;
		}
	}

406
407
408
	/*
	 * Slave & stub zones must have a "masters" field.
	 */
409
410
411
	if (ztype == SLAVEZONE || ztype == STUBZONE) {
		obj = NULL;
		if (cfg_map_get(zoptions, "masters", &obj) != ISC_R_SUCCESS) {
412
			cfg_obj_log(zoptions, logctx, ISC_LOG_ERROR,
413
414
415
416
				    "zone '%s': missing 'masters' entry",
				    zname);
			result = ISC_R_FAILURE;
		}
417
418
419
420
421
422
423
		addrlist = cfg_tuple_get(obj, "addresses");
		if (cfg_list_first(addrlist) == NULL) {
			cfg_obj_log(zoptions, logctx, ISC_LOG_ERROR,
				    "zone '%s': empty 'masters' entry",
				    zname);
			result = ISC_R_FAILURE;
		}
424
425
	}

426
427
428
	/*
	 * Master zones can't have both "allow-update" and "update-policy".
	 */
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
	if (ztype == MASTERZONE) {
		isc_result_t res1, res2;
		obj = NULL;
		res1 = cfg_map_get(zoptions, "allow-update", &obj);
		obj = NULL;
		res2 = cfg_map_get(zoptions, "update-policy", &obj);
		if (res1 == ISC_R_SUCCESS && res2 == ISC_R_SUCCESS) {
			cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
				    "zone '%s': 'allow-update' is ignored "
				    "when 'update-policy' is present",
				    zname);
			result = ISC_R_FAILURE;
		}
	}

444
445
446
	/*
	 * Check the excessively complicated "dialup" option.
	 */
447
448
	if (ztype == MASTERZONE || ztype == SLAVEZONE || ztype == STUBZONE) {
		cfg_obj_t *dialup = NULL;
449
		(void)cfg_map_get(zoptions, "dialup", &dialup);
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
		if (dialup != NULL && cfg_obj_isstring(dialup)) {
			char *str = cfg_obj_asstring(dialup);
			for (i = 0;
			     i < sizeof(dialups) / sizeof(dialups[0]);
			     i++)
			{
				if (strcasecmp(dialups[i].name, str) != 0)
					continue;
				if ((dialups[i].allowed & ztype) == 0) {
					cfg_obj_log(obj, logctx,
						    ISC_LOG_ERROR,
						    "dialup type '%s' is not "
						    "allowed in '%s' "
						    "zone '%s'",
						    str, typestr, zname);
					result = ISC_R_FAILURE;
				}
				break;
			}
			if (i == sizeof(dialups) / sizeof(dialups[0])) {
				cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
					    "invalid dialup type '%s' in zone "
					    "'%s'", str, zname);
				result = ISC_R_FAILURE;
			}
		}
	}

478
479
480
481
482
483
	/*
	 * Check that forwarding is reasonable.
	 */
	if (check_forward(zoptions, logctx) != ISC_R_SUCCESS)
		result = ISC_R_FAILURE;

484
485
486
	/*
	 * Check various options.
	 */
487
	tresult = check_options(zoptions, logctx);
488
489
490
	if (tresult != ISC_R_SUCCESS)
		result = tresult;

491
492
493
	return (result);
}

494
isc_result_t
495
bind9_check_key(cfg_obj_t *key, isc_log_t *logctx) {
496
497
498
499
	cfg_obj_t *algobj = NULL;
	cfg_obj_t *secretobj = NULL;
	const char *keyname = cfg_obj_asstring(cfg_map_getname(key));
	
500
501
	(void)cfg_map_get(key, "algorithm", &algobj);
	(void)cfg_map_get(key, "secret", &secretobj);
502
503
504
505
506
	if (secretobj == NULL || algobj == NULL) {
		cfg_obj_log(key, logctx, ISC_LOG_ERROR,
			    "key '%s' must have both 'secret' and "
			    "'algorithm' defined",
			    keyname);
Brian Wellington's avatar
style    
Brian Wellington committed
507
		return (ISC_R_FAILURE);
508
	}
Brian Wellington's avatar
style    
Brian Wellington committed
509
	return (ISC_R_SUCCESS);
510
}
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525

static isc_result_t
check_keylist(cfg_obj_t *keys, isc_symtab_t *symtab, isc_log_t *logctx) {
	isc_result_t result = ISC_R_SUCCESS;
	isc_result_t tresult;
	cfg_listelt_t *element;

	for (element = cfg_list_first(keys);
	     element != NULL;
	     element = cfg_list_next(element))
	{
		cfg_obj_t *key = cfg_listelt_value(element);
		const char *keyname = cfg_obj_asstring(cfg_map_getname(key));
		isc_symvalue_t symvalue;

526
		symvalue.as_pointer = key;
527
528
529
		tresult = isc_symtab_define(symtab, keyname, 1,
					    symvalue, isc_symexists_reject);
		if (tresult == ISC_R_EXISTS) {
530
531
532
533
534
535
536
537
538
539
			const char *file;
			unsigned int line;

			RUNTIME_CHECK(isc_symtab_lookup(symtab, keyname,
					    1, &symvalue) == ISC_R_SUCCESS);
			file = cfg_obj_file(symvalue.as_pointer);
			line = cfg_obj_line(symvalue.as_pointer);

			if (file == NULL)
				file = "<unknown file>";
540
			cfg_obj_log(key, logctx, ISC_LOG_ERROR,
541
542
543
				    "key '%s': already exists "
				    "previous definition: %s:%u",
				    keyname, file, line);
544
545
546
547
548
549
550
551
552
553
			result = tresult;
		} else if (tresult != ISC_R_SUCCESS)
			return (tresult);

		tresult = bind9_check_key(key, logctx);
		if (tresult != ISC_R_SUCCESS)
			return (tresult);
	}
	return (result);
}
554
555
556
557
558
559
560

static void
freekey(char *key, unsigned int type, isc_symvalue_t value, void *userarg) {
	UNUSED(type);
	UNUSED(value);
	isc_mem_free(userarg, key);
}
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587

static isc_result_t
check_servers(cfg_obj_t *servers, isc_log_t *logctx) {
	isc_result_t result = ISC_R_SUCCESS;
	cfg_listelt_t *e1, *e2;
	cfg_obj_t *v1, *v2;
	isc_sockaddr_t *s1, *s2;
	isc_netaddr_t na;

	for (e1 = cfg_list_first(servers); e1 != NULL; e1 = cfg_list_next(e1)) {
		v1 = cfg_listelt_value(e1);
		s1 = cfg_obj_assockaddr(cfg_map_getname(v1));
		e2 = e1;
		while ((e2 = cfg_list_next(e2)) != NULL) {
			v2 = cfg_listelt_value(e2);
			s2 = cfg_obj_assockaddr(cfg_map_getname(v2));
			if (isc_sockaddr_eqaddr(s1, s2)) {
				const char *file = cfg_obj_file(v1);
				unsigned int line = cfg_obj_line(v1);
				isc_buffer_t target;
				char buf[128];

				if (file == NULL)
					file = "<unknown file>";

				isc_netaddr_fromsockaddr(&na, s2);
				isc_buffer_init(&target, buf, sizeof(buf) - 1);
Mark Andrews's avatar
Mark Andrews committed
588
589
				RUNTIME_CHECK(isc_netaddr_totext(&na, &target)
					      == ISC_R_SUCCESS);
590
591
592
593
594
595
596
597
598
599
600
601
				buf[isc_buffer_usedlength(&target)] = '\0';

				cfg_obj_log(v2, logctx, ISC_LOG_ERROR,
					    "server '%s': already exists "
					    "previous definition: %s:%u",
					    buf, file, line);
				result = ISC_R_FAILURE;
			}
		}
	}
	return (result);
}
602
		
Brian Wellington's avatar
Brian Wellington committed
603
static isc_result_t
604
check_viewconf(cfg_obj_t *config, cfg_obj_t *vconfig, dns_rdataclass_t vclass,
605
	       isc_log_t *logctx, isc_mem_t *mctx)
606
{
607
	cfg_obj_t *servers = NULL;
Brian Wellington's avatar
Brian Wellington committed
608
609
610
	cfg_obj_t *zones = NULL;
	cfg_obj_t *keys = NULL;
	cfg_listelt_t *element;
611
	isc_symtab_t *symtab = NULL;
Brian Wellington's avatar
Brian Wellington committed
612
	isc_result_t result = ISC_R_SUCCESS;
613
	isc_result_t tresult = ISC_R_SUCCESS;
Brian Wellington's avatar
Brian Wellington committed
614

615
616
617
618
	/*
	 * Check that all zone statements are syntactically correct and
	 * there are no duplicate zones.
	 */
619
620
	tresult = isc_symtab_create(mctx, 100, freekey, mctx,
				    ISC_TRUE, &symtab);
621
	if (tresult != ISC_R_SUCCESS)
622
623
		return (ISC_R_NOMEMORY);

624
625
626
627
	if (vconfig != NULL)
		(void)cfg_map_get(vconfig, "zone", &zones);
	else
		(void)cfg_map_get(config, "zone", &zones);
628

Brian Wellington's avatar
Brian Wellington committed
629
630
631
632
	for (element = cfg_list_first(zones);
	     element != NULL;
	     element = cfg_list_next(element))
	{
633
		isc_result_t tresult;
Brian Wellington's avatar
Brian Wellington committed
634
635
		cfg_obj_t *zone = cfg_listelt_value(element);

636
		tresult = check_zoneconf(zone, symtab, vclass, logctx, mctx);
637
		if (tresult != ISC_R_SUCCESS)
Brian Wellington's avatar
Brian Wellington committed
638
639
640
			result = ISC_R_FAILURE;
	}

641
642
643
644
645
646
	isc_symtab_destroy(&symtab);

	/*
	 * Check that all key statements are syntactically correct and
	 * there are no duplicate keys.
	 */
647
648
	tresult = isc_symtab_create(mctx, 100, NULL, NULL, ISC_TRUE, &symtab);
	if (tresult != ISC_R_SUCCESS)
649
650
		return (ISC_R_NOMEMORY);

651
	(void)cfg_map_get(config, "key", &keys);
652
653
654
655
656
657
658
659
660
661
662
663
664
	tresult = check_keylist(keys, symtab, logctx);
	if (tresult == ISC_R_EXISTS)
		result = ISC_R_FAILURE;
	else if (tresult != ISC_R_SUCCESS) {
		isc_symtab_destroy(&symtab);
		return (tresult);
	}
	
	if (vconfig != NULL) {
		keys = NULL;
		(void)cfg_map_get(vconfig, "key", &keys);
		tresult = check_keylist(keys, symtab, logctx);
		if (tresult == ISC_R_EXISTS)
665
			result = ISC_R_FAILURE;
666
		else if (tresult != ISC_R_SUCCESS) {
667
668
			isc_symtab_destroy(&symtab);
			return (tresult);
Brian Wellington's avatar
Brian Wellington committed
669
670
671
		}
	}

672
673
	isc_symtab_destroy(&symtab);

674
675
676
	/*
	 * Check that forwarding is reasonable.
	 */
677
	if (vconfig == NULL) {
678
		cfg_obj_t *options = NULL;
679
		(void)cfg_map_get(config, "options", &options);
680
681
682
683
684
685
686
687
		if (options != NULL)
			if (check_forward(options, logctx) != ISC_R_SUCCESS)
				result = ISC_R_FAILURE;
	} else {
		if (check_forward(vconfig, logctx) != ISC_R_SUCCESS)
			result = ISC_R_FAILURE;
	}

Mark Andrews's avatar
Mark Andrews committed
688
689
690
691
692
693
694
	/*
	 * Check that rrset-order is reasonable.
	 */
	if (vconfig != NULL) {
		if (check_order(vconfig, logctx) != ISC_R_SUCCESS)
			result = ISC_R_FAILURE;
	}
695
696
697
698
699
700
701
702

	if (vconfig != NULL) {
		(void)cfg_map_get(vconfig, "server", &servers);
		if (servers != NULL &&
		    check_servers(servers, logctx) != ISC_R_SUCCESS)
			result = ISC_R_FAILURE;
	}

703
	if (vconfig != NULL)
704
		tresult = check_options(vconfig, logctx);
705
	else
706
		tresult = check_options(config, logctx);
707
708
709
	if (tresult != ISC_R_SUCCESS)
		result = tresult;

Brian Wellington's avatar
Brian Wellington committed
710
711
712
713
	return (result);
}


714
isc_result_t
715
bind9_check_namedconf(cfg_obj_t *config, isc_log_t *logctx, isc_mem_t *mctx) {
716
	cfg_obj_t *options = NULL;
717
	cfg_obj_t *servers = NULL;
718
	cfg_obj_t *views = NULL;
719
	cfg_obj_t *acls = NULL;
720
721
	cfg_obj_t *obj;
	cfg_listelt_t *velement;
Brian Wellington's avatar
Brian Wellington committed
722
	isc_result_t result = ISC_R_SUCCESS;
723
	isc_result_t tresult;
724

725
	static const char *builtin[] = { "localhost", "localnets",
726
					 "any", "none"};
727

728
729
	(void)cfg_map_get(config, "options", &options);

730
	if (options != NULL &&
731
	    check_options(options, logctx) != ISC_R_SUCCESS)
732
733
734
735
736
		result = ISC_R_FAILURE;

	(void)cfg_map_get(config, "server", &servers);
	if (servers != NULL &&
	    check_servers(servers, logctx) != ISC_R_SUCCESS)
Mark Andrews's avatar
Mark Andrews committed
737
738
739
740
		result = ISC_R_FAILURE;

	if (options != NULL && 
	    check_order(options, logctx) != ISC_R_SUCCESS)
741
		result = ISC_R_FAILURE;
742

743
744
745
	(void)cfg_map_get(config, "view", &views);

	if (views == NULL) {
746
		if (check_viewconf(config, NULL, dns_rdataclass_in,
747
				   logctx, mctx) != ISC_R_SUCCESS)
Brian Wellington's avatar
Brian Wellington committed
748
			result = ISC_R_FAILURE;
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
	} else {
		cfg_obj_t *zones = NULL;

		(void)cfg_map_get(config, "zone", &zones);
		if (zones != NULL) {
			cfg_obj_log(zones, logctx, ISC_LOG_ERROR,
				    "when using 'view' statements, "
				    "all zones must be in views");
			result = ISC_R_FAILURE;
		}
	}

	for (velement = cfg_list_first(views);
	     velement != NULL;
	     velement = cfg_list_next(velement))
	{
		cfg_obj_t *view = cfg_listelt_value(velement);
Brian Wellington's avatar
Brian Wellington committed
766
		cfg_obj_t *vname = cfg_tuple_get(view, "name");
767
		cfg_obj_t *voptions = cfg_tuple_get(view, "options");
768
769
770
		cfg_obj_t *vclassobj = cfg_tuple_get(view, "class");
		dns_rdataclass_t vclass = dns_rdataclass_in;
		isc_result_t tresult = ISC_R_SUCCESS;
771

772
773
774
775
776
777
778
		if (cfg_obj_isstring(vclassobj)) {
			isc_textregion_t r;

			DE_CONST(cfg_obj_asstring(vclassobj), r.base);
			r.length = strlen(r.base);
			tresult = dns_rdataclass_fromtext(&vclass, &r);
			if (tresult != ISC_R_SUCCESS)
779
				cfg_obj_log(vclassobj, logctx, ISC_LOG_ERROR,
780
781
782
783
					    "view '%s': invalid class %s",
					    cfg_obj_asstring(vname), r.base);
		}
		if (tresult == ISC_R_SUCCESS)
784
			tresult = check_viewconf(config, voptions,
785
786
						 vclass, logctx, mctx);
		if (tresult != ISC_R_SUCCESS)
Brian Wellington's avatar
Brian Wellington committed
787
			result = ISC_R_FAILURE;
788
789
	}

790
791
	if (views != NULL && options != NULL) {
		obj = NULL;
792
793
		tresult = cfg_map_get(options, "cache-file", &obj);
		if (tresult == ISC_R_SUCCESS) {
794
795
796
797
798
799
			cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
				    "'cache-file' cannot be a global "
				    "option if views are present");
			result = ISC_R_FAILURE;
		}
	}
800

801
802
803
        tresult = cfg_map_get(config, "acl", &acls);
        if (tresult == ISC_R_SUCCESS) {
		cfg_listelt_t *elt;
804
		cfg_listelt_t *elt2;
805
806
807
808
809
810
		const char *aclname;

		for (elt = cfg_list_first(acls);
		     elt != NULL;
		     elt = cfg_list_next(elt)) {
			cfg_obj_t *acl = cfg_listelt_value(elt);
811
			unsigned int i;
812
813

			aclname = cfg_obj_asstring(cfg_tuple_get(acl, "name"));
814
815
816
			for (i = 0;
			     i < sizeof(builtin) / sizeof(builtin[0]);
			     i++)
817
818
819
820
821
822
823
824
				if (strcasecmp(aclname, builtin[i]) == 0) {
					cfg_obj_log(acl, logctx, ISC_LOG_ERROR,
						    "attempt to redefine "
						    "builtin acl '%s'",
				    		    aclname);
					result = ISC_R_FAILURE;
					break;
				}
825
826
827
828
829
830
831
832
833

			for (elt2 = cfg_list_next(elt);
			     elt2 != NULL;
			     elt2 = cfg_list_next(elt2)) {
				cfg_obj_t *acl2 = cfg_listelt_value(elt2);
				const char *name;
				name = cfg_obj_asstring(cfg_tuple_get(acl2,
								      "name"));
				if (strcasecmp(aclname, name) == 0) {
834
835
836
837
838
839
					const char *file = cfg_obj_file(acl);
					unsigned int line = cfg_obj_line(acl);

					if (file == NULL)
						file = "<unknown file>";

840
841
					cfg_obj_log(acl2, logctx, ISC_LOG_ERROR,
						    "attempt to redefine "
842
843
844
						    "acl '%s' previous "
						    "definition: %s:%u",
						     name, file, line);
845
846
847
					result = ISC_R_FAILURE;
				}
			}
848
849
850
		}
	}

851
852
	return (result);
}