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.
 */

18
/* $Id: check.c,v 1.32 2002/04/17 01:23:15 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 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
				    "zone '%s': missing 'masters' entry",
				    zname);
			result = ISC_R_FAILURE;
416 417 418 419 420 421 422 423
		} 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;
			}
424
		}
425 426
	}

427 428 429
	/*
	 * Master zones can't have both "allow-update" and "update-policy".
	 */
430 431 432 433 434 435 436 437 438 439 440 441 442 443 444
	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;
		}
	}

445 446 447
	/*
	 * Check the excessively complicated "dialup" option.
	 */
448 449
	if (ztype == MASTERZONE || ztype == SLAVEZONE || ztype == STUBZONE) {
		cfg_obj_t *dialup = NULL;
450
		(void)cfg_map_get(zoptions, "dialup", &dialup);
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 478
		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;
			}
		}
	}

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

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

492 493 494
	return (result);
}

495
isc_result_t
496
bind9_check_key(cfg_obj_t *key, isc_log_t *logctx) {
497 498 499 500
	cfg_obj_t *algobj = NULL;
	cfg_obj_t *secretobj = NULL;
	const char *keyname = cfg_obj_asstring(cfg_map_getname(key));
	
501 502
	(void)cfg_map_get(key, "algorithm", &algobj);
	(void)cfg_map_get(key, "secret", &secretobj);
503 504 505 506 507
	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
508
		return (ISC_R_FAILURE);
509
	}
Brian Wellington's avatar
style  
Brian Wellington committed
510
	return (ISC_R_SUCCESS);
511
}
512 513 514 515 516 517 518 519 520 521 522 523 524 525 526

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;

527
		symvalue.as_pointer = key;
528 529 530
		tresult = isc_symtab_define(symtab, keyname, 1,
					    symvalue, isc_symexists_reject);
		if (tresult == ISC_R_EXISTS) {
531 532 533 534 535 536 537 538 539 540
			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>";
541
			cfg_obj_log(key, logctx, ISC_LOG_ERROR,
542 543 544
				    "key '%s': already exists "
				    "previous definition: %s:%u",
				    keyname, file, line);
545 546 547 548 549 550 551 552 553 554
			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);
}
555 556 557 558 559 560 561

static void
freekey(char *key, unsigned int type, isc_symvalue_t value, void *userarg) {
	UNUSED(type);
	UNUSED(value);
	isc_mem_free(userarg, key);
}
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 588

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
589 590
				RUNTIME_CHECK(isc_netaddr_totext(&na, &target)
					      == ISC_R_SUCCESS);
591 592 593 594 595 596 597 598 599 600 601 602
				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);
}
603
		
Brian Wellington's avatar
Brian Wellington committed
604
static isc_result_t
605
check_viewconf(cfg_obj_t *config, cfg_obj_t *vconfig, dns_rdataclass_t vclass,
606
	       isc_log_t *logctx, isc_mem_t *mctx)
607
{
608
	cfg_obj_t *servers = NULL;
Brian Wellington's avatar
Brian Wellington committed
609 610 611
	cfg_obj_t *zones = NULL;
	cfg_obj_t *keys = NULL;
	cfg_listelt_t *element;
612
	isc_symtab_t *symtab = NULL;
Brian Wellington's avatar
Brian Wellington committed
613
	isc_result_t result = ISC_R_SUCCESS;
614
	isc_result_t tresult = ISC_R_SUCCESS;
Brian Wellington's avatar
Brian Wellington committed
615

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

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

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

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

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

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

652
	(void)cfg_map_get(config, "key", &keys);
653 654 655 656 657 658 659 660 661 662 663 664 665
	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)
666
			result = ISC_R_FAILURE;
667
		else if (tresult != ISC_R_SUCCESS) {
668 669
			isc_symtab_destroy(&symtab);
			return (tresult);
Brian Wellington's avatar
Brian Wellington committed
670 671 672
		}
	}

673 674
	isc_symtab_destroy(&symtab);

675 676 677
	/*
	 * Check that forwarding is reasonable.
	 */
678
	if (vconfig == NULL) {
679
		cfg_obj_t *options = NULL;
680
		(void)cfg_map_get(config, "options", &options);
681 682 683 684 685 686 687 688
		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
689 690 691 692 693 694 695
	/*
	 * Check that rrset-order is reasonable.
	 */
	if (vconfig != NULL) {
		if (check_order(vconfig, logctx) != ISC_R_SUCCESS)
			result = ISC_R_FAILURE;
	}
696 697 698 699 700 701 702 703

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

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

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


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

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

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

731
	if (options != NULL &&
732
	    check_options(options, logctx) != ISC_R_SUCCESS)
733 734 735 736 737
		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
738 739 740 741
		result = ISC_R_FAILURE;

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

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

	if (views == NULL) {
747
		if (check_viewconf(config, NULL, dns_rdataclass_in,
748
				   logctx, mctx) != ISC_R_SUCCESS)
Brian Wellington's avatar
Brian Wellington committed
749
			result = ISC_R_FAILURE;
750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766
	} 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
767
		cfg_obj_t *vname = cfg_tuple_get(view, "name");
768
		cfg_obj_t *voptions = cfg_tuple_get(view, "options");
769 770 771
		cfg_obj_t *vclassobj = cfg_tuple_get(view, "class");
		dns_rdataclass_t vclass = dns_rdataclass_in;
		isc_result_t tresult = ISC_R_SUCCESS;
772

773 774 775 776 777 778 779
		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)
780
				cfg_obj_log(vclassobj, logctx, ISC_LOG_ERROR,
781 782 783 784
					    "view '%s': invalid class %s",
					    cfg_obj_asstring(vname), r.base);
		}
		if (tresult == ISC_R_SUCCESS)
785
			tresult = check_viewconf(config, voptions,
786 787
						 vclass, logctx, mctx);
		if (tresult != ISC_R_SUCCESS)
Brian Wellington's avatar
Brian Wellington committed
788
			result = ISC_R_FAILURE;
789 790
	}

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

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

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

			aclname = cfg_obj_asstring(cfg_tuple_get(acl, "name"));
815 816 817
			for (i = 0;
			     i < sizeof(builtin) / sizeof(builtin[0]);
			     i++)
818 819 820 821 822 823 824 825
				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;
				}
826 827 828 829 830 831 832 833 834

			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) {
835 836 837 838 839 840
					const char *file = cfg_obj_file(acl);
					unsigned int line = cfg_obj_line(acl);

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

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

852 853
	return (result);
}