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