check.c 23.8 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.
 */

18
/* $Id: check.c,v 1.33 2002/04/26 00:40:28 marka 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
			cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
				    "%s '%d' is out of range",
				    intervals[i].name, val);
			result = ISC_R_RANGE;
		}
	}
210
211
212
213
214
215
216
217
218
219
220
221
	obj = NULL;
	(void)cfg_map_get(options, "preferred-glue", &obj);
	if (obj != NULL) {
		const char *str;
                str = cfg_obj_asstring(obj);
                if (strcasecmp(str, "a") != 0 &&
		    strcasecmp(str, "aaaa") != 0 &&
		    strcasecmp(str, "none") != 0)
			cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
				    "preferred-glue unexpected value '%s'",
				    str);
	}
222
223
224
	return (result);
}

225
226
227
228
229
230
231
232
233
234
235
236
#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
237
check_zoneconf(cfg_obj_t *zconfig, isc_symtab_t *symtab,
238
	       dns_rdataclass_t defclass, isc_log_t *logctx, isc_mem_t *mctx)
239
{
240
241
242
243
244
	const char *zname;
	const char *typestr;
	unsigned int ztype;
	cfg_obj_t *zoptions;
	cfg_obj_t *obj = NULL;
245
	cfg_obj_t *addrlist = NULL;
246
	isc_symvalue_t symvalue;
Brian Wellington's avatar
Brian Wellington committed
247
	isc_result_t result = ISC_R_SUCCESS;
248
	isc_result_t tresult;
249
	unsigned int i;
250
	dns_rdataclass_t zclass;
251
252
	dns_fixedname_t fixedname;
	isc_buffer_t b;
253
254
255

	static optionstable options[] = {
	{ "allow-query", MASTERZONE | SLAVEZONE | STUBZONE },
256
	{ "allow-notify", SLAVEZONE },
257
	{ "allow-transfer", MASTERZONE | SLAVEZONE },
258
259
260
261
262
263
264
	{ "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 },
265
266
	{ "notify-source", MASTERZONE | SLAVEZONE },
	{ "notify-source-v6", MASTERZONE | SLAVEZONE },
267
268
	{ "transfer-source", SLAVEZONE | STUBZONE },
	{ "transfer-source-v6", SLAVEZONE | STUBZONE },
269
270
271
272
273
274
275
276
277
278
279
	{ "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 },
280
	{ "allow-update-forwarding", SLAVEZONE },
281
282
283
284
285
286
287
	{ "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 },
288
	{ "key-directory", MASTERZONE },
289
290
291
292
293
294
295
296
297
	};

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

Brian Wellington's avatar
bugs    
Brian Wellington committed
298
299
300
	zname = cfg_obj_asstring(cfg_tuple_get(zconfig, "name"));

	zoptions = cfg_tuple_get(zconfig, "options");
301
302

	obj = NULL;
Brian Wellington's avatar
bugs    
Brian Wellington committed
303
	(void)cfg_map_get(zoptions, "type", &obj);
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
	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);
	}

328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
	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);
		}
	}

350
351
	/*
	 * Look for an already existing zone.
352
353
	 * We need to make this cannonical as isc_symtab_define()
	 * deals with strings.
354
	 */
355
356
357
358
359
360
	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) {
361
		cfg_obj_log(zconfig, logctx, ISC_LOG_ERROR,
362
			    "zone '%s': is not a valid name", zname);
363
		result = ISC_R_FAILURE;
364
365
	} else {
		char namebuf[DNS_NAME_FORMATSIZE];
366
367
		char *key;

368
369
		dns_name_format(dns_fixedname_name(&fixedname),
				namebuf, sizeof(namebuf));
370
371
372
		key = isc_mem_strdup(mctx, namebuf);
		if (key == NULL)
			return (ISC_R_NOMEMORY);
373
		symvalue.as_pointer = zconfig;
374
		tresult = isc_symtab_define(symtab, key,
375
376
377
					    ztype == HINTZONE ? 1 : 2,
					    symvalue, isc_symexists_reject);
		if (tresult == ISC_R_EXISTS) {
378
379
380
381
382
383
			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
384
			isc_mem_free(mctx, key);
385
386
387
388
389
			file = cfg_obj_file(symvalue.as_pointer);
			line = cfg_obj_line(symvalue.as_pointer);

			if (file == NULL)
				file = "<unknown file>";
390
			cfg_obj_log(zconfig, logctx, ISC_LOG_ERROR,
391
392
393
				    "zone '%s': already exists "
				    "previous definition: %s:%u",
				    zname, file, line);
394
			result = ISC_R_FAILURE;
Mark Andrews's avatar
Mark Andrews committed
395
396
		} else if (tresult != ISC_R_SUCCESS) {
			isc_mem_strdup(mctx, key);
397
			return (tresult);
Mark Andrews's avatar
Mark Andrews committed
398
		}
399
	}
400
401
402
403

	/*
	 * Look for inappropriate options for the given zone type.
	 */
404
405
406
407
408
409
410
411
412
413
414
415
416
417
	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;
		}
	}

418
419
420
	/*
	 * Slave & stub zones must have a "masters" field.
	 */
421
422
423
	if (ztype == SLAVEZONE || ztype == STUBZONE) {
		obj = NULL;
		if (cfg_map_get(zoptions, "masters", &obj) != ISC_R_SUCCESS) {
424
			cfg_obj_log(zoptions, logctx, ISC_LOG_ERROR,
425
426
427
				    "zone '%s': missing 'masters' entry",
				    zname);
			result = ISC_R_FAILURE;
428
429
430
431
432
433
434
435
		} else {
			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;
			}
436
		}
437
438
	}

439
440
441
	/*
	 * Master zones can't have both "allow-update" and "update-policy".
	 */
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
	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;
		}
	}

457
458
459
	/*
	 * Check the excessively complicated "dialup" option.
	 */
460
461
	if (ztype == MASTERZONE || ztype == SLAVEZONE || ztype == STUBZONE) {
		cfg_obj_t *dialup = NULL;
462
		(void)cfg_map_get(zoptions, "dialup", &dialup);
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
		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;
			}
		}
	}

491
492
493
494
495
496
	/*
	 * Check that forwarding is reasonable.
	 */
	if (check_forward(zoptions, logctx) != ISC_R_SUCCESS)
		result = ISC_R_FAILURE;

497
498
499
	/*
	 * Check various options.
	 */
500
	tresult = check_options(zoptions, logctx);
501
502
503
	if (tresult != ISC_R_SUCCESS)
		result = tresult;

504
505
506
	return (result);
}

507
isc_result_t
508
bind9_check_key(cfg_obj_t *key, isc_log_t *logctx) {
509
510
511
512
	cfg_obj_t *algobj = NULL;
	cfg_obj_t *secretobj = NULL;
	const char *keyname = cfg_obj_asstring(cfg_map_getname(key));
	
513
514
	(void)cfg_map_get(key, "algorithm", &algobj);
	(void)cfg_map_get(key, "secret", &secretobj);
515
516
517
518
519
	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
520
		return (ISC_R_FAILURE);
521
	}
Brian Wellington's avatar
style    
Brian Wellington committed
522
	return (ISC_R_SUCCESS);
523
}
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538

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;

539
		symvalue.as_pointer = key;
540
541
542
		tresult = isc_symtab_define(symtab, keyname, 1,
					    symvalue, isc_symexists_reject);
		if (tresult == ISC_R_EXISTS) {
543
544
545
546
547
548
549
550
551
552
			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>";
553
			cfg_obj_log(key, logctx, ISC_LOG_ERROR,
554
555
556
				    "key '%s': already exists "
				    "previous definition: %s:%u",
				    keyname, file, line);
557
558
559
560
561
562
563
564
565
566
			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);
}
567
568
569
570
571
572
573

static void
freekey(char *key, unsigned int type, isc_symvalue_t value, void *userarg) {
	UNUSED(type);
	UNUSED(value);
	isc_mem_free(userarg, key);
}
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600

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
601
602
				RUNTIME_CHECK(isc_netaddr_totext(&na, &target)
					      == ISC_R_SUCCESS);
603
604
605
606
607
608
609
610
611
612
613
614
				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);
}
615
		
Brian Wellington's avatar
Brian Wellington committed
616
static isc_result_t
617
check_viewconf(cfg_obj_t *config, cfg_obj_t *vconfig, dns_rdataclass_t vclass,
618
	       isc_log_t *logctx, isc_mem_t *mctx)
619
{
620
	cfg_obj_t *servers = NULL;
Brian Wellington's avatar
Brian Wellington committed
621
622
623
	cfg_obj_t *zones = NULL;
	cfg_obj_t *keys = NULL;
	cfg_listelt_t *element;
624
	isc_symtab_t *symtab = NULL;
Brian Wellington's avatar
Brian Wellington committed
625
	isc_result_t result = ISC_R_SUCCESS;
626
	isc_result_t tresult = ISC_R_SUCCESS;
Brian Wellington's avatar
Brian Wellington committed
627

628
629
630
631
	/*
	 * Check that all zone statements are syntactically correct and
	 * there are no duplicate zones.
	 */
632
633
	tresult = isc_symtab_create(mctx, 100, freekey, mctx,
				    ISC_TRUE, &symtab);
634
	if (tresult != ISC_R_SUCCESS)
635
636
		return (ISC_R_NOMEMORY);

637
638
639
640
	if (vconfig != NULL)
		(void)cfg_map_get(vconfig, "zone", &zones);
	else
		(void)cfg_map_get(config, "zone", &zones);
641

Brian Wellington's avatar
Brian Wellington committed
642
643
644
645
	for (element = cfg_list_first(zones);
	     element != NULL;
	     element = cfg_list_next(element))
	{
646
		isc_result_t tresult;
Brian Wellington's avatar
Brian Wellington committed
647
648
		cfg_obj_t *zone = cfg_listelt_value(element);

649
		tresult = check_zoneconf(zone, symtab, vclass, logctx, mctx);
650
		if (tresult != ISC_R_SUCCESS)
Brian Wellington's avatar
Brian Wellington committed
651
652
653
			result = ISC_R_FAILURE;
	}

654
655
656
657
658
659
	isc_symtab_destroy(&symtab);

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

664
	(void)cfg_map_get(config, "key", &keys);
665
666
667
668
669
670
671
672
673
674
675
676
677
	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)
678
			result = ISC_R_FAILURE;
679
		else if (tresult != ISC_R_SUCCESS) {
680
681
			isc_symtab_destroy(&symtab);
			return (tresult);
Brian Wellington's avatar
Brian Wellington committed
682
683
684
		}
	}

685
686
	isc_symtab_destroy(&symtab);

687
688
689
	/*
	 * Check that forwarding is reasonable.
	 */
690
	if (vconfig == NULL) {
691
		cfg_obj_t *options = NULL;
692
		(void)cfg_map_get(config, "options", &options);
693
694
695
696
697
698
699
700
		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
701
702
703
704
705
706
707
	/*
	 * Check that rrset-order is reasonable.
	 */
	if (vconfig != NULL) {
		if (check_order(vconfig, logctx) != ISC_R_SUCCESS)
			result = ISC_R_FAILURE;
	}
708
709
710
711
712
713
714
715

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

716
	if (vconfig != NULL)
717
		tresult = check_options(vconfig, logctx);
718
	else
719
		tresult = check_options(config, logctx);
720
721
722
	if (tresult != ISC_R_SUCCESS)
		result = tresult;

Brian Wellington's avatar
Brian Wellington committed
723
724
725
726
	return (result);
}


727
isc_result_t
728
bind9_check_namedconf(cfg_obj_t *config, isc_log_t *logctx, isc_mem_t *mctx) {
729
	cfg_obj_t *options = NULL;
730
	cfg_obj_t *servers = NULL;
731
	cfg_obj_t *views = NULL;
732
	cfg_obj_t *acls = NULL;
733
734
	cfg_obj_t *obj;
	cfg_listelt_t *velement;
Brian Wellington's avatar
Brian Wellington committed
735
	isc_result_t result = ISC_R_SUCCESS;
736
	isc_result_t tresult;
737

738
	static const char *builtin[] = { "localhost", "localnets",
739
					 "any", "none"};
740

741
742
	(void)cfg_map_get(config, "options", &options);

743
	if (options != NULL &&
744
	    check_options(options, logctx) != ISC_R_SUCCESS)
745
746
747
748
749
		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
750
751
752
753
		result = ISC_R_FAILURE;

	if (options != NULL && 
	    check_order(options, logctx) != ISC_R_SUCCESS)
754
		result = ISC_R_FAILURE;
755

756
757
758
	(void)cfg_map_get(config, "view", &views);

	if (views == NULL) {
759
		if (check_viewconf(config, NULL, dns_rdataclass_in,
760
				   logctx, mctx) != ISC_R_SUCCESS)
Brian Wellington's avatar
Brian Wellington committed
761
			result = ISC_R_FAILURE;
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
	} 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
779
		cfg_obj_t *vname = cfg_tuple_get(view, "name");
780
		cfg_obj_t *voptions = cfg_tuple_get(view, "options");
781
782
783
		cfg_obj_t *vclassobj = cfg_tuple_get(view, "class");
		dns_rdataclass_t vclass = dns_rdataclass_in;
		isc_result_t tresult = ISC_R_SUCCESS;
784

785
786
787
788
789
790
791
		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)
792
				cfg_obj_log(vclassobj, logctx, ISC_LOG_ERROR,
793
794
795
796
					    "view '%s': invalid class %s",
					    cfg_obj_asstring(vname), r.base);
		}
		if (tresult == ISC_R_SUCCESS)
797
			tresult = check_viewconf(config, voptions,
798
799
						 vclass, logctx, mctx);
		if (tresult != ISC_R_SUCCESS)
Brian Wellington's avatar
Brian Wellington committed
800
			result = ISC_R_FAILURE;
801
802
	}

803
804
	if (views != NULL && options != NULL) {
		obj = NULL;
805
806
		tresult = cfg_map_get(options, "cache-file", &obj);
		if (tresult == ISC_R_SUCCESS) {
807
808
809
810
811
812
			cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
				    "'cache-file' cannot be a global "
				    "option if views are present");
			result = ISC_R_FAILURE;
		}
	}
813

814
815
816
        tresult = cfg_map_get(config, "acl", &acls);
        if (tresult == ISC_R_SUCCESS) {
		cfg_listelt_t *elt;
817
		cfg_listelt_t *elt2;
818
819
820
821
822
823
		const char *aclname;

		for (elt = cfg_list_first(acls);
		     elt != NULL;
		     elt = cfg_list_next(elt)) {
			cfg_obj_t *acl = cfg_listelt_value(elt);
824
			unsigned int i;
825
826

			aclname = cfg_obj_asstring(cfg_tuple_get(acl, "name"));
827
828
829
			for (i = 0;
			     i < sizeof(builtin) / sizeof(builtin[0]);
			     i++)
830
831
832
833
834
835
836
837
				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;
				}
838
839
840
841
842
843
844
845
846

			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) {
847
848
849
850
851
852
					const char *file = cfg_obj_file(acl);
					unsigned int line = cfg_obj_line(acl);

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

853
854
					cfg_obj_log(acl2, logctx, ISC_LOG_ERROR,
						    "attempt to redefine "
855
856
857
						    "acl '%s' previous "
						    "definition: %s:%u",
						     name, file, line);
858
859
860
					result = ISC_R_FAILURE;
				}
			}
861
862
863
		}
	}

864
865
	return (result);
}