check.c 20.3 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.28 2002/03/04 05:27:29 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/rdataclass.h>
36
#include <dns/fixedname.h>
37 38

#include <isccfg/cfg.h>
39 40

#include <bind9/check.h>
41

42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57
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);
}

58 59 60
typedef struct {
	const char *name;
	unsigned int scale;
61
	unsigned int max;
62 63 64
} intervaltable;

static isc_result_t
65
check_options(cfg_obj_t *options, isc_log_t *logctx) {
66 67
	isc_result_t result = ISC_R_SUCCESS;
	unsigned int i;
68
	cfg_obj_t *obj = NULL;
69 70

	static intervaltable intervals[] = {
71 72 73 74 75 76 77 78 79
	{ "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 */
80 81 82 83 84 85 86 87
	};

	/*
	 * 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;
88
		obj = NULL;
89 90 91 92
		(void)cfg_map_get(options, intervals[i].name, &obj);
		if (obj == NULL)
			continue;
		val = cfg_obj_asuint32(obj);
93 94 95 96 97 98 99
		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)) {
100 101 102 103 104 105 106 107 108
			cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
				    "%s '%d' is out of range",
				    intervals[i].name, val);
			result = ISC_R_RANGE;
		}
	}
	return (result);
}

109 110 111 112 113 114 115 116 117 118 119 120
#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
121
check_zoneconf(cfg_obj_t *zconfig, isc_symtab_t *symtab,
122
	       dns_rdataclass_t defclass, isc_log_t *logctx, isc_mem_t *mctx)
123
{
124 125 126 127 128
	const char *zname;
	const char *typestr;
	unsigned int ztype;
	cfg_obj_t *zoptions;
	cfg_obj_t *obj = NULL;
129
	cfg_obj_t *addrlist = NULL;
130
	isc_symvalue_t symvalue;
Brian Wellington's avatar
Brian Wellington committed
131
	isc_result_t result = ISC_R_SUCCESS;
132
	isc_result_t tresult;
133
	unsigned int i;
134
	dns_rdataclass_t zclass;
135 136
	dns_fixedname_t fixedname;
	isc_buffer_t b;
137 138 139

	static optionstable options[] = {
	{ "allow-query", MASTERZONE | SLAVEZONE | STUBZONE },
140
	{ "allow-notify", SLAVEZONE },
141
	{ "allow-transfer", MASTERZONE | SLAVEZONE },
142 143 144 145 146 147 148
	{ "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 },
149 150
	{ "notify-source", MASTERZONE | SLAVEZONE },
	{ "notify-source-v6", MASTERZONE | SLAVEZONE },
151 152
	{ "transfer-source", SLAVEZONE | STUBZONE },
	{ "transfer-source-v6", SLAVEZONE | STUBZONE },
153 154 155 156 157 158 159 160 161 162 163
	{ "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 },
164
	{ "allow-update-forwarding", SLAVEZONE },
165 166 167 168 169 170 171
	{ "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 },
172
	{ "key-directory", MASTERZONE },
173 174 175 176 177 178 179 180 181
	};

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

Brian Wellington's avatar
bugs  
Brian Wellington committed
182 183 184
	zname = cfg_obj_asstring(cfg_tuple_get(zconfig, "name"));

	zoptions = cfg_tuple_get(zconfig, "options");
185 186

	obj = NULL;
Brian Wellington's avatar
bugs  
Brian Wellington committed
187
	(void)cfg_map_get(zoptions, "type", &obj);
188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211
	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);
	}

212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233
	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);
		}
	}

234 235
	/*
	 * Look for an already existing zone.
236 237
	 * We need to make this cannonical as isc_symtab_define()
	 * deals with strings.
238
	 */
239 240 241 242 243 244
	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) {
245
		cfg_obj_log(zconfig, logctx, ISC_LOG_ERROR,
246
			    "zone '%s': is not a valid name", zname);
247
		result = ISC_R_FAILURE;
248 249
	} else {
		char namebuf[DNS_NAME_FORMATSIZE];
250 251
		char *key;

252 253
		dns_name_format(dns_fixedname_name(&fixedname),
				namebuf, sizeof(namebuf));
254 255 256
		key = isc_mem_strdup(mctx, namebuf);
		if (key == NULL)
			return (ISC_R_NOMEMORY);
257
		symvalue.as_pointer = zconfig;
258
		tresult = isc_symtab_define(symtab, key,
259 260 261
					    ztype == HINTZONE ? 1 : 2,
					    symvalue, isc_symexists_reject);
		if (tresult == ISC_R_EXISTS) {
262 263 264 265 266 267
			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
268
			isc_mem_free(mctx, key);
269 270 271 272 273
			file = cfg_obj_file(symvalue.as_pointer);
			line = cfg_obj_line(symvalue.as_pointer);

			if (file == NULL)
				file = "<unknown file>";
274
			cfg_obj_log(zconfig, logctx, ISC_LOG_ERROR,
275 276 277
				    "zone '%s': already exists "
				    "previous definition: %s:%u",
				    zname, file, line);
278
			result = ISC_R_FAILURE;
Mark Andrews's avatar
Mark Andrews committed
279 280
		} else if (tresult != ISC_R_SUCCESS) {
			isc_mem_strdup(mctx, key);
281
			return (tresult);
Mark Andrews's avatar
Mark Andrews committed
282
		}
283
	}
284 285 286 287

	/*
	 * Look for inappropriate options for the given zone type.
	 */
288 289 290 291 292 293 294 295 296 297 298 299 300 301
	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;
		}
	}

302 303 304
	/*
	 * Slave & stub zones must have a "masters" field.
	 */
305 306 307
	if (ztype == SLAVEZONE || ztype == STUBZONE) {
		obj = NULL;
		if (cfg_map_get(zoptions, "masters", &obj) != ISC_R_SUCCESS) {
308
			cfg_obj_log(zoptions, logctx, ISC_LOG_ERROR,
309 310 311 312
				    "zone '%s': missing 'masters' entry",
				    zname);
			result = ISC_R_FAILURE;
		}
313 314 315 316 317 318 319
		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;
		}
320 321
	}

322 323 324
	/*
	 * Master zones can't have both "allow-update" and "update-policy".
	 */
325 326 327 328 329 330 331 332 333 334 335 336 337 338 339
	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;
		}
	}

340 341 342
	/*
	 * Check the excessively complicated "dialup" option.
	 */
343 344
	if (ztype == MASTERZONE || ztype == SLAVEZONE || ztype == STUBZONE) {
		cfg_obj_t *dialup = NULL;
345
		(void)cfg_map_get(zoptions, "dialup", &dialup);
346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373
		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;
			}
		}
	}

374 375 376 377 378 379
	/*
	 * Check that forwarding is reasonable.
	 */
	if (check_forward(zoptions, logctx) != ISC_R_SUCCESS)
		result = ISC_R_FAILURE;

380 381 382
	/*
	 * Check various options.
	 */
383
	tresult = check_options(zoptions, logctx);
384 385 386
	if (tresult != ISC_R_SUCCESS)
		result = tresult;

387 388 389
	return (result);
}

390
isc_result_t
391
bind9_check_key(cfg_obj_t *key, isc_log_t *logctx) {
392 393 394 395
	cfg_obj_t *algobj = NULL;
	cfg_obj_t *secretobj = NULL;
	const char *keyname = cfg_obj_asstring(cfg_map_getname(key));
	
396 397
	(void)cfg_map_get(key, "algorithm", &algobj);
	(void)cfg_map_get(key, "secret", &secretobj);
398 399 400 401 402
	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
403
		return (ISC_R_FAILURE);
404
	}
Brian Wellington's avatar
style  
Brian Wellington committed
405
	return (ISC_R_SUCCESS);
406
}
407 408 409 410 411 412 413 414 415 416 417 418 419 420 421

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;

422
		symvalue.as_pointer = key;
423 424 425
		tresult = isc_symtab_define(symtab, keyname, 1,
					    symvalue, isc_symexists_reject);
		if (tresult == ISC_R_EXISTS) {
426 427 428 429 430 431 432 433 434 435
			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>";
436
			cfg_obj_log(key, logctx, ISC_LOG_ERROR,
437 438 439
				    "key '%s': already exists "
				    "previous definition: %s:%u",
				    keyname, file, line);
440 441 442 443 444 445 446 447 448 449
			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);
}
450 451 452 453 454 455 456

static void
freekey(char *key, unsigned int type, isc_symvalue_t value, void *userarg) {
	UNUSED(type);
	UNUSED(value);
	isc_mem_free(userarg, key);
}
457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483

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
484 485
				RUNTIME_CHECK(isc_netaddr_totext(&na, &target)
					      == ISC_R_SUCCESS);
486 487 488 489 490 491 492 493 494 495 496 497
				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);
}
498
		
Brian Wellington's avatar
Brian Wellington committed
499
static isc_result_t
500
check_viewconf(cfg_obj_t *config, cfg_obj_t *vconfig, dns_rdataclass_t vclass,
501
	       isc_log_t *logctx, isc_mem_t *mctx)
502
{
503
	cfg_obj_t *servers = NULL;
Brian Wellington's avatar
Brian Wellington committed
504 505 506
	cfg_obj_t *zones = NULL;
	cfg_obj_t *keys = NULL;
	cfg_listelt_t *element;
507
	isc_symtab_t *symtab = NULL;
Brian Wellington's avatar
Brian Wellington committed
508
	isc_result_t result = ISC_R_SUCCESS;
509
	isc_result_t tresult = ISC_R_SUCCESS;
Brian Wellington's avatar
Brian Wellington committed
510

511 512 513 514
	/*
	 * Check that all zone statements are syntactically correct and
	 * there are no duplicate zones.
	 */
515 516
	tresult = isc_symtab_create(mctx, 100, freekey, mctx,
				    ISC_TRUE, &symtab);
517
	if (tresult != ISC_R_SUCCESS)
518 519
		return (ISC_R_NOMEMORY);

520 521 522 523
	if (vconfig != NULL)
		(void)cfg_map_get(vconfig, "zone", &zones);
	else
		(void)cfg_map_get(config, "zone", &zones);
524

Brian Wellington's avatar
Brian Wellington committed
525 526 527 528
	for (element = cfg_list_first(zones);
	     element != NULL;
	     element = cfg_list_next(element))
	{
529
		isc_result_t tresult;
Brian Wellington's avatar
Brian Wellington committed
530 531
		cfg_obj_t *zone = cfg_listelt_value(element);

532
		tresult = check_zoneconf(zone, symtab, vclass, logctx, mctx);
533
		if (tresult != ISC_R_SUCCESS)
Brian Wellington's avatar
Brian Wellington committed
534 535 536
			result = ISC_R_FAILURE;
	}

537 538 539 540 541 542
	isc_symtab_destroy(&symtab);

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

547
	(void)cfg_map_get(config, "key", &keys);
548 549 550 551 552 553 554 555 556 557 558 559 560
	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)
561
			result = ISC_R_FAILURE;
562
		else if (tresult != ISC_R_SUCCESS) {
563 564
			isc_symtab_destroy(&symtab);
			return (tresult);
Brian Wellington's avatar
Brian Wellington committed
565 566 567
		}
	}

568 569
	isc_symtab_destroy(&symtab);

570 571 572
	/*
	 * Check that forwarding is reasonable.
	 */
573
	if (vconfig == NULL) {
574
		cfg_obj_t *options = NULL;
575
		(void)cfg_map_get(config, "options", &options);
576 577 578 579 580 581 582 583
		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;
	}

584 585 586 587 588 589 590 591

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

592
	if (vconfig != NULL)
593
		tresult = check_options(vconfig, logctx);
594
	else
595
		tresult = check_options(config, logctx);
596 597 598
	if (tresult != ISC_R_SUCCESS)
		result = tresult;

Brian Wellington's avatar
Brian Wellington committed
599 600 601 602
	return (result);
}


603
isc_result_t
604
bind9_check_namedconf(cfg_obj_t *config, isc_log_t *logctx, isc_mem_t *mctx) {
605
	cfg_obj_t *options = NULL;
606
	cfg_obj_t *servers = NULL;
607
	cfg_obj_t *views = NULL;
608
	cfg_obj_t *acls = NULL;
609 610
	cfg_obj_t *obj;
	cfg_listelt_t *velement;
Brian Wellington's avatar
Brian Wellington committed
611
	isc_result_t result = ISC_R_SUCCESS;
612
	isc_result_t tresult;
613

614
	static const char *builtin[] = { "localhost", "localnets",
615
					 "any", "none"};
616

617 618
	(void)cfg_map_get(config, "options", &options);

619
	if (options != NULL &&
620
	    check_options(options, logctx) != ISC_R_SUCCESS)
621 622 623 624 625
		result = ISC_R_FAILURE;

	(void)cfg_map_get(config, "server", &servers);
	if (servers != NULL &&
	    check_servers(servers, logctx) != ISC_R_SUCCESS)
626
		result = ISC_R_FAILURE;
627

628 629 630
	(void)cfg_map_get(config, "view", &views);

	if (views == NULL) {
631
		if (check_viewconf(config, NULL, dns_rdataclass_in,
632
				   logctx, mctx) != ISC_R_SUCCESS)
Brian Wellington's avatar
Brian Wellington committed
633
			result = ISC_R_FAILURE;
634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650
	} 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
651
		cfg_obj_t *vname = cfg_tuple_get(view, "name");
652
		cfg_obj_t *voptions = cfg_tuple_get(view, "options");
653 654 655
		cfg_obj_t *vclassobj = cfg_tuple_get(view, "class");
		dns_rdataclass_t vclass = dns_rdataclass_in;
		isc_result_t tresult = ISC_R_SUCCESS;
656

657 658 659 660 661 662 663
		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)
664
				cfg_obj_log(vclassobj, logctx, ISC_LOG_ERROR,
665 666 667 668
					    "view '%s': invalid class %s",
					    cfg_obj_asstring(vname), r.base);
		}
		if (tresult == ISC_R_SUCCESS)
669
			tresult = check_viewconf(config, voptions,
670 671
						 vclass, logctx, mctx);
		if (tresult != ISC_R_SUCCESS)
Brian Wellington's avatar
Brian Wellington committed
672
			result = ISC_R_FAILURE;
673 674
	}

675 676
	if (views != NULL && options != NULL) {
		obj = NULL;
677 678
		tresult = cfg_map_get(options, "cache-file", &obj);
		if (tresult == ISC_R_SUCCESS) {
679 680 681 682 683 684
			cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
				    "'cache-file' cannot be a global "
				    "option if views are present");
			result = ISC_R_FAILURE;
		}
	}
685

686 687 688
        tresult = cfg_map_get(config, "acl", &acls);
        if (tresult == ISC_R_SUCCESS) {
		cfg_listelt_t *elt;
689
		cfg_listelt_t *elt2;
690 691 692 693 694 695
		const char *aclname;

		for (elt = cfg_list_first(acls);
		     elt != NULL;
		     elt = cfg_list_next(elt)) {
			cfg_obj_t *acl = cfg_listelt_value(elt);
696
			unsigned int i;
697 698

			aclname = cfg_obj_asstring(cfg_tuple_get(acl, "name"));
699 700 701
			for (i = 0;
			     i < sizeof(builtin) / sizeof(builtin[0]);
			     i++)
702 703 704 705 706 707 708 709
				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;
				}
710 711 712 713 714 715 716 717 718

			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) {
719 720 721 722 723 724
					const char *file = cfg_obj_file(acl);
					unsigned int line = cfg_obj_line(acl);

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

725 726
					cfg_obj_log(acl2, logctx, ISC_LOG_ERROR,
						    "attempt to redefine "
727 728 729
						    "acl '%s' previous "
						    "definition: %s:%u",
						     name, file, line);
730 731 732
					result = ISC_R_FAILURE;
				}
			}
733 734 735
		}
	}

736 737
	return (result);
}