server.c 98.8 KB
Newer Older
1
/*
Mark Andrews's avatar
Mark Andrews committed
2
 * Copyright (C) 1999-2002  Internet Software Consortium.
3
 *
4 5 6
 * 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.
7
 *
8 9 10 11 12 13 14 15
 * 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.
16 17
 */

18
/* $Id: server.c,v 1.412 2004/02/17 03:40:20 marka Exp $ */
David Lawrence's avatar
David Lawrence committed
19

20 21 22 23
#include <config.h>

#include <stdlib.h>

24
#include <isc/app.h>
25
#include <isc/base64.h>
26
#include <isc/dir.h>
27
#include <isc/entropy.h>
28
#include <isc/file.h>
29
#include <isc/hash.h>
30
#include <isc/lex.h>
31
#include <isc/parseint.h>
32 33
#include <isc/print.h>
#include <isc/resource.h>
34
#include <isc/stdio.h>
35
#include <isc/string.h>
36
#include <isc/task.h>
37
#include <isc/timer.h>
Michael Graff's avatar
Michael Graff committed
38
#include <isc/util.h>
39

40
#include <isccfg/namedconf.h>
41 42

#include <bind9/check.h>
43

44
#include <dns/adb.h>
45
#include <dns/cache.h>
46
#include <dns/db.h>
Bob Halley's avatar
Bob Halley committed
47
#include <dns/dispatch.h>
48
#include <dns/forward.h>
49
#include <dns/journal.h>
50
#include <dns/keytable.h>
51
#include <dns/master.h>
52
#include <dns/order.h>
53
#include <dns/peer.h>
54
#include <dns/portlist.h>
55
#include <dns/rdataclass.h>
56
#include <dns/rdataset.h>
57
#include <dns/rdatastruct.h>
58
#include <dns/resolver.h>
59
#include <dns/rootns.h>
60
#include <dns/secalg.h>
61
#include <dns/stats.h>
62
#include <dns/tkey.h>
63
#include <dns/view.h>
64
#include <dns/zone.h>
65
#include <dns/zt.h>
66

67
#include <dst/dst.h>
68
#include <dst/result.h>
69

70
#include <named/client.h>
71
#include <named/config.h>
72
#include <named/control.h>
73
#include <named/interfacemgr.h>
74
#include <named/log.h>
75
#include <named/logconf.h>
76
#include <named/lwresd.h>
77
#include <named/main.h>
78
#include <named/os.h>
Bob Halley's avatar
Bob Halley committed
79
#include <named/server.h>
80 81 82
#include <named/tkeyconf.h>
#include <named/tsigconf.h>
#include <named/zoneconf.h>
83

84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105
/*
 * Check an operation for failure.  Assumes that the function
 * using it has a 'result' variable and a 'cleanup' label.
 */
#define CHECK(op) \
	do { result = (op); 				  	 \
	       if (result != ISC_R_SUCCESS) goto cleanup; 	 \
	} while (0)

#define CHECKM(op, msg) \
	do { result = (op); 				  	  \
	       if (result != ISC_R_SUCCESS) {			  \
			isc_log_write(ns_g_lctx,		  \
				      NS_LOGCATEGORY_GENERAL,	  \
				      NS_LOGMODULE_SERVER,	  \
				      ISC_LOG_ERROR,		  \
				      "%s: %s", msg,		  \
				      isc_result_totext(result)); \
			goto cleanup;				  \
		}						  \
	} while (0)						  \

Mark Andrews's avatar
Mark Andrews committed
106 107 108 109 110 111 112 113 114 115 116 117 118
#define CHECKMF(op, msg, file) \
	do { result = (op); 				  	  \
	       if (result != ISC_R_SUCCESS) {			  \
			isc_log_write(ns_g_lctx,		  \
				      NS_LOGCATEGORY_GENERAL,	  \
				      NS_LOGMODULE_SERVER,	  \
				      ISC_LOG_ERROR,		  \
				      "%s '%s': %s", msg, file,	  \
				      isc_result_totext(result)); \
			goto cleanup;				  \
		}						  \
	} while (0)						  \

119 120 121 122 123 124
#define CHECKFATAL(op, msg) \
	do { result = (op); 				  	  \
	       if (result != ISC_R_SUCCESS)			  \
			fatal(msg, result);			  \
	} while (0)						  \

125 126 127 128 129 130 131
struct ns_dispatch {
	isc_sockaddr_t			addr;
	unsigned int			dispatchgen;
	dns_dispatch_t			*dispatch;
	ISC_LINK(struct ns_dispatch)	link;
};

David Lawrence's avatar
David Lawrence committed
132 133 134 135 136
static void
fatal(const char *msg, isc_result_t result);

static void
ns_server_reload(isc_task_t *task, isc_event_t *event);
137

138
static isc_result_t
139
ns_listenelt_fromconfig(cfg_obj_t *listener, cfg_obj_t *config,
140
			ns_aclconfctx_t *actx,
141 142
			isc_mem_t *mctx, ns_listenelt_t **target);
static isc_result_t
143
ns_listenlist_fromconfig(cfg_obj_t *listenlist, cfg_obj_t *config,
144
			 ns_aclconfctx_t *actx,
145
			 isc_mem_t *mctx, ns_listenlist_t **target);
146

147
static isc_result_t
148 149
configure_forward(cfg_obj_t *config, dns_view_t *view, dns_name_t *origin,
		  cfg_obj_t *forwarders, cfg_obj_t *forwardtype);
150

151 152 153 154
static isc_result_t
configure_alternates(cfg_obj_t *config, dns_view_t *view,
		     cfg_obj_t *alternates);

155 156 157 158
static isc_result_t
configure_zone(cfg_obj_t *config, cfg_obj_t *zconfig, cfg_obj_t *vconfig,
	       isc_mem_t *mctx, dns_view_t *view,
	       ns_aclconfctx_t *aclconf);
159

160 161 162
static void
end_reserved_dispatches(ns_server_t *server, isc_boolean_t all);

163 164 165 166 167 168
/*
 * Configure a single view ACL at '*aclp'.  Get its configuration by
 * calling 'getvcacl' (for per-view configuration) and maybe 'getscacl'
 * (for a global default).
 */
static isc_result_t
169 170 171
configure_view_acl(cfg_obj_t *vconfig, cfg_obj_t *config,
		   const char *aclname, ns_aclconfctx_t *actx,
		   isc_mem_t *mctx, dns_acl_t **aclp)
172 173
{
	isc_result_t result;
174 175 176 177
	cfg_obj_t *maps[3];
	cfg_obj_t *aclobj = NULL;
	int i = 0;

178 179
	if (*aclp != NULL)
		dns_acl_detach(aclp);
180 181 182 183
	if (vconfig != NULL)
		maps[i++] = cfg_tuple_get(vconfig, "options");
	if (config != NULL) {
		cfg_obj_t *options = NULL;
184
		(void)cfg_map_get(config, "options", &options);
185 186 187 188
		if (options != NULL)
			maps[i++] = options;
	}
	maps[i] = NULL;
189

190 191
	result = ns_config_get(maps, aclname, &aclobj);
	if (aclobj == NULL)
192 193 194
		/*
		 * No value available.  *aclp == NULL.
		 */
195 196
		return (ISC_R_SUCCESS);

197
	result = ns_acl_fromconfig(aclobj, config, actx, mctx, aclp);
198 199 200 201

	return (result);
}

202
static isc_result_t
203
configure_view_dnsseckey(cfg_obj_t *vconfig, cfg_obj_t *key,
204 205 206
			 dns_keytable_t *keytable, isc_mem_t *mctx)
{
	dns_rdataclass_t viewclass;
207
	dns_rdata_dnskey_t keystruct;
208 209
	isc_uint32_t flags, proto, alg;
	char *keystr, *keynamestr;
210 211 212 213 214 215 216 217 218 219 220
	unsigned char keydata[4096];
	isc_buffer_t keydatabuf;
	unsigned char rrdata[4096];
	isc_buffer_t rrdatabuf;
	isc_region_t r;
	dns_fixedname_t fkeyname;
	dns_name_t *keyname;
	isc_buffer_t namebuf;
	isc_result_t result;
	dst_key_t *dstkey = NULL;

221 222 223 224 225 226 227
	flags = cfg_obj_asuint32(cfg_tuple_get(key, "flags"));
	proto = cfg_obj_asuint32(cfg_tuple_get(key, "protocol"));
	alg = cfg_obj_asuint32(cfg_tuple_get(key, "algorithm"));
	keyname = dns_fixedname_name(&fkeyname);
	keynamestr = cfg_obj_asstring(cfg_tuple_get(key, "name"));

	if (vconfig == NULL)
228
		viewclass = dns_rdataclass_in;
229 230
	else {
		cfg_obj_t *classobj = cfg_tuple_get(vconfig, "class");
231 232
		CHECK(ns_config_getclass(classobj, dns_rdataclass_in,
					 &viewclass));
233
	}
234
	keystruct.common.rdclass = viewclass;
235
	keystruct.common.rdtype = dns_rdatatype_dnskey;
236
	/*
237
	 * The key data in keystruct is not dynamically allocated.
238 239 240 241 242
	 */
	keystruct.mctx = NULL;

	ISC_LINK_INIT(&keystruct.common, link);

243
	if (flags > 0xffff)
244
		CHECKM(ISC_R_RANGE, "key flags");
245
	if (proto > 0xff)
246
		CHECKM(ISC_R_RANGE, "key protocol");
247
	if (alg > 0xff)
248
		CHECKM(ISC_R_RANGE, "key algorithm");
249 250 251
	keystruct.flags = (isc_uint16_t)flags;
	keystruct.protocol = (isc_uint8_t)proto;
	keystruct.algorithm = (isc_uint8_t)alg;
252 253 254 255

	isc_buffer_init(&keydatabuf, keydata, sizeof(keydata));
	isc_buffer_init(&rrdatabuf, rrdata, sizeof(rrdata));

256
	keystr = cfg_obj_asstring(cfg_tuple_get(key, "key"));
257
	CHECK(isc_base64_decodestring(keystr, &keydatabuf));
258 259 260 261 262 263 264 265 266
	isc_buffer_usedregion(&keydatabuf, &r);
	keystruct.datalen = r.length;
	keystruct.data = r.base;

	CHECK(dns_rdata_fromstruct(NULL,
				   keystruct.common.rdclass,
				   keystruct.common.rdtype,
				   &keystruct, &rrdatabuf));
	dns_fixedname_init(&fkeyname);
267 268
	isc_buffer_init(&namebuf, keynamestr, strlen(keynamestr));
	isc_buffer_add(&namebuf, strlen(keynamestr));
269 270 271 272 273 274 275 276 277 278 279
	CHECK(dns_name_fromtext(keyname, &namebuf,
				dns_rootname, ISC_FALSE,
				NULL));
	CHECK(dst_key_fromdns(keyname, viewclass, &rrdatabuf,
			      mctx, &dstkey));

	CHECK(dns_keytable_add(keytable, &dstkey));
	INSIST(dstkey == NULL);
	return (ISC_R_SUCCESS);

 cleanup:
280 281 282 283 284 285 286 287 288 289 290
	if (result == DST_R_NOCRYPTO) {
		cfg_obj_log(key, ns_g_lctx, ISC_LOG_ERROR,
			    "ignoring trusted key for '%s': no crypto support",
			    keynamestr);
		result = ISC_R_SUCCESS;
	} else {
		cfg_obj_log(key, ns_g_lctx, ISC_LOG_ERROR,
			    "configuring trusted key for '%s': %s",
			    keynamestr, isc_result_totext(result));
		result = ISC_R_FAILURE;
	}
291 292 293 294 295 296 297

	if (dstkey != NULL)
		dst_key_free(&dstkey);

	return (result);
}

298
/*
299 300
 * Configure DNSSEC keys for a view.  Currently used only for
 * the security roots.
301
 *
302 303
 * The per-view configuration values and the server-global defaults are read
 * from 'vconfig' and 'config'.  The variable to be configured is '*target'.
304 305
 */
static isc_result_t
306 307
configure_view_dnsseckeys(cfg_obj_t *vconfig, cfg_obj_t *config,
			  isc_mem_t *mctx, dns_keytable_t **target)
308 309
{
	isc_result_t result;
310 311 312 313 314
	cfg_obj_t *keys = NULL;
	cfg_obj_t *voptions = NULL;
	cfg_listelt_t *element, *element2;
	cfg_obj_t *keylist;
	cfg_obj_t *key;
315
	dns_keytable_t *keytable = NULL;
316

317 318
	CHECK(dns_keytable_create(mctx, &keytable));

319 320
	if (vconfig != NULL)
		voptions = cfg_tuple_get(vconfig, "options");
321

322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338
	keys = NULL;
	if (voptions != NULL)
		(void)cfg_map_get(voptions, "trusted-keys", &keys);
	if (keys == NULL)
		(void)cfg_map_get(config, "trusted-keys", &keys);

	for (element = cfg_list_first(keys);
	     element != NULL;
	     element = cfg_list_next(element))
	{
		keylist = cfg_listelt_value(element);
		for (element2 = cfg_list_first(keylist);
		     element2 != NULL;
		     element2 = cfg_list_next(element2))
		{
			key = cfg_listelt_value(element2);
			CHECK(configure_view_dnsseckey(vconfig, key,
339
						       keytable, mctx));
340
		}
341
	}
342

343 344 345 346
	dns_keytable_detach(target);
	*target = keytable; /* Transfer ownership. */
	keytable = NULL;
	result = ISC_R_SUCCESS;
347
	
348 349 350
 cleanup:
	return (result);
}
351

352

353 354 355 356
/*
 * Get a dispatch appropriate for the resolver of a given view.
 */
static isc_result_t
357
get_view_querysource_dispatch(cfg_obj_t **maps,
358 359 360 361 362 363
			      int af, dns_dispatch_t **dispatchp)
{
	isc_result_t result;
	dns_dispatch_t *disp;
	isc_sockaddr_t sa;
	unsigned int attrs, attrmask;
364
	cfg_obj_t *obj = NULL;
365 366 367 368 369 370 371 372

	/*
	 * Make compiler happy.
	 */
	result = ISC_R_FAILURE;

	switch (af) {
	case AF_INET:
373 374 375
		result = ns_config_get(maps, "query-source", &obj);
		INSIST(result == ISC_R_SUCCESS);

376 377
		break;
	case AF_INET6:
378 379
		result = ns_config_get(maps, "query-source-v6", &obj);
		INSIST(result == ISC_R_SUCCESS);
380 381 382 383
		break;
	default:
		INSIST(0);
	}
384

385
	sa = *(cfg_obj_assockaddr(obj));
386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427
	INSIST(isc_sockaddr_pf(&sa) == af);

	/*
	 * If we don't support this address family, we're done!
	 */
	switch (af) {
	case AF_INET:
		result = isc_net_probeipv4();
		break;
	case AF_INET6:
		result = isc_net_probeipv6();
		break;
	default:
		INSIST(0);
	}
	if (result != ISC_R_SUCCESS)
		return (ISC_R_SUCCESS);

	/*
	 * Try to find a dispatcher that we can share.
	 */
	attrs = 0;
	attrs |= DNS_DISPATCHATTR_UDP;
	switch (af) {
	case AF_INET:
		attrs |= DNS_DISPATCHATTR_IPV4;
		break;
	case AF_INET6:
		attrs |= DNS_DISPATCHATTR_IPV6;
		break;
	}
	attrmask = 0;
	attrmask |= DNS_DISPATCHATTR_UDP;
	attrmask |= DNS_DISPATCHATTR_TCP;
	attrmask |= DNS_DISPATCHATTR_IPV4;
	attrmask |= DNS_DISPATCHATTR_IPV6;

	disp = NULL;
	result = dns_dispatch_getudp(ns_g_dispatchmgr, ns_g_socketmgr,
				     ns_g_taskmgr, &sa, 4096,
				     1000, 32768, 16411, 16433,
				     attrs, attrmask, &disp);
428
	if (result != ISC_R_SUCCESS) {
429 430 431 432 433 434 435 436 437 438 439 440 441 442
		isc_sockaddr_t any;
		char buf[ISC_SOCKADDR_FORMATSIZE];

		switch (af) {
		case AF_INET:
			isc_sockaddr_any(&any);
			break;
		case AF_INET6:
			isc_sockaddr_any6(&any);
			break;
		}
		if (isc_sockaddr_equal(&sa, &any))
			return (ISC_R_SUCCESS);
		isc_sockaddr_format(&sa, buf, sizeof(buf));
443
		isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
444
			      NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
445 446
			      "could not get query source dispatcher (%s)",
			      buf);
447
		return (result);
448
	}
449 450 451 452 453 454

	*dispatchp = disp;

	return (ISC_R_SUCCESS);
}

455 456 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 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504
static isc_result_t
configure_order(dns_order_t *order, cfg_obj_t *ent) {
	dns_rdataclass_t rdclass;
	dns_rdatatype_t rdtype;
	cfg_obj_t *obj;
	dns_fixedname_t fixed;
	unsigned int mode = 0;
	const char *str;
	isc_buffer_t b;
	isc_result_t result;

	result = ns_config_getclass(cfg_tuple_get(ent, "class"),
				    dns_rdataclass_any, &rdclass);
	if (result != ISC_R_SUCCESS)
		return (result);

	result = ns_config_gettype(cfg_tuple_get(ent, "type"),
				   dns_rdatatype_any, &rdtype);
	if (result != ISC_R_SUCCESS)
		return (result);

	obj = cfg_tuple_get(ent, "name");
	if (cfg_obj_isstring(obj)) 
		str = cfg_obj_asstring(obj);
	else
		str = "*";
	isc_buffer_init(&b, str, strlen(str));
	isc_buffer_add(&b, strlen(str));
	dns_fixedname_init(&fixed);
	result = dns_name_fromtext(dns_fixedname_name(&fixed), &b,
				  dns_rootname, ISC_FALSE, NULL);
	if (result != ISC_R_SUCCESS)
		return (result);

	obj = cfg_tuple_get(ent, "ordering");
	INSIST(cfg_obj_isstring(obj));
	str = cfg_obj_asstring(obj);
	if (!strcasecmp(str, "fixed"))
		mode = DNS_RDATASETATTR_FIXEDORDER;
	else if (!strcasecmp(str, "random"))
		mode = DNS_RDATASETATTR_RANDOMIZE;
	else if (!strcasecmp(str, "cyclic"))
		mode = 0;
	else
		INSIST(0);

	return (dns_order_add(order, dns_fixedname_name(&fixed),
			      rdtype, rdclass, mode));
}

505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524
static isc_result_t
configure_peer(cfg_obj_t *cpeer, isc_mem_t *mctx, dns_peer_t **peerp) {
	isc_sockaddr_t *sa;
	isc_netaddr_t na;
	dns_peer_t *peer;
	cfg_obj_t *obj;
	char *str;
	isc_result_t result;

	sa = cfg_obj_assockaddr(cfg_map_getname(cpeer));
	isc_netaddr_fromsockaddr(&na, sa);

	peer = NULL;
	result = dns_peer_new(mctx, &na, &peer);
	if (result != ISC_R_SUCCESS)
		return (result);

	obj = NULL;
	(void)cfg_map_get(cpeer, "bogus", &obj);
	if (obj != NULL)
525
		CHECK(dns_peer_setbogus(peer, cfg_obj_asboolean(obj)));
526 527 528 529

	obj = NULL;
	(void)cfg_map_get(cpeer, "provide-ixfr", &obj);
	if (obj != NULL)
530
		CHECK(dns_peer_setprovideixfr(peer, cfg_obj_asboolean(obj)));
531 532 533 534

	obj = NULL;
	(void)cfg_map_get(cpeer, "request-ixfr", &obj);
	if (obj != NULL)
535
		CHECK(dns_peer_setrequestixfr(peer, cfg_obj_asboolean(obj)));
536

537 538 539
	obj = NULL;
	(void)cfg_map_get(cpeer, "edns", &obj);
	if (obj != NULL)
540
		CHECK(dns_peer_setsupportedns(peer, cfg_obj_asboolean(obj)));
541

542 543 544
	obj = NULL;
	(void)cfg_map_get(cpeer, "transfers", &obj);
	if (obj != NULL)
545
		CHECK(dns_peer_settransfers(peer, cfg_obj_asuint32(obj)));
546 547 548 549 550 551

	obj = NULL;
	(void)cfg_map_get(cpeer, "transfer-format", &obj);
	if (obj != NULL) {
		str = cfg_obj_asstring(obj);
		if (strcasecmp(str, "many-answers") == 0)
552 553
			CHECK(dns_peer_settransferformat(peer,
							 dns_many_answers));
554
		else if (strcasecmp(str, "one-answer") == 0)
555 556
			CHECK(dns_peer_settransferformat(peer,
							 dns_one_answer));
557 558 559 560 561 562 563 564 565 566 567
		else
			INSIST(0);
	}

	obj = NULL;
	(void)cfg_map_get(cpeer, "keys", &obj);
	if (obj != NULL) {
		result = dns_peer_setkeybycharp(peer, cfg_obj_asstring(obj));
		if (result != ISC_R_SUCCESS)
			goto cleanup;
	}
568 569 570 571 572 573 574 575 576 577 578 579

	obj = NULL;
	if (isc_sockaddr_pf(sa) == AF_INET)
		(void)cfg_map_get(cpeer, "transfer-source", &obj);
	else
		(void)cfg_map_get(cpeer, "transfer-source-v6", &obj);
	if (obj != NULL) {
		result = dns_peer_settransfersource(peer,
						    cfg_obj_assockaddr(obj));
		if (result != ISC_R_SUCCESS)
			goto cleanup;
	}
580 581 582 583 584 585 586 587
	*peerp = peer;
	return (ISC_R_SUCCESS);

 cleanup:
	dns_peer_detach(&peer);
	return (result);
}

588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633
static isc_result_t
disable_algorithms(cfg_obj_t *disabled, dns_resolver_t *resolver) {
	isc_result_t result;
	cfg_obj_t *algorithms;
	cfg_listelt_t *element;
	const char *str;
	dns_fixedname_t fixed;
	dns_name_t *name;
	isc_buffer_t b;

	dns_fixedname_init(&fixed);
	name = dns_fixedname_name(&fixed);
	str = cfg_obj_asstring(cfg_tuple_get(disabled, "name"));
	isc_buffer_init(&b, str, strlen(str));
	isc_buffer_add(&b, strlen(str));
	CHECK(dns_name_fromtext(name, &b, dns_rootname, ISC_FALSE, NULL));

	algorithms = cfg_tuple_get(disabled, "algorithms");
	for (element = cfg_list_first(algorithms);
	     element != NULL;
	     element = cfg_list_next(element))
	{
		isc_textregion_t r;
		dns_secalg_t alg;

		r.base = cfg_obj_asstring(cfg_listelt_value(element));
		r.length = strlen(r.base);

		result = dns_secalg_fromtext(&alg, &r);
		if (result != ISC_R_SUCCESS) {
			isc_uint8_t ui;
			result = isc_parse_uint8(&ui, r.base, 10);
			alg = ui;
		}
		if (result != ISC_R_SUCCESS) {
			cfg_obj_log(cfg_listelt_value(element),
				    ns_g_lctx, ISC_LOG_ERROR,
				    "invalid algorithm");
			CHECK(result);
		}
		CHECK(dns_resolver_disable_algorithm(resolver, name, alg));
	}
 cleanup:
	return (result);
}

634
/*
635 636
 * Configure 'view' according to 'vconfig', taking defaults from 'config'
 * where values are missing in 'vconfig'.
637
 *
638 639
 * When configuring the default view, 'vconfig' will be NULL and the
 * global defaults in 'config' used exclusively.
640
 */
641
static isc_result_t
642
configure_view(dns_view_t *view, cfg_obj_t *config, cfg_obj_t *vconfig,
643 644
	       isc_mem_t *mctx, ns_aclconfctx_t *actx,
	       isc_boolean_t need_hints)
645
{
646 647 648 649
	cfg_obj_t *maps[4];
	cfg_obj_t *cfgmaps[3];
	cfg_obj_t *options = NULL;
	cfg_obj_t *voptions = NULL;
650
	cfg_obj_t *forwardtype;
651
	cfg_obj_t *forwarders;
652
	cfg_obj_t *alternates;
653
	cfg_obj_t *zonelist;
654
	cfg_obj_t *disabled;
655 656 657
	cfg_obj_t *obj;
	cfg_listelt_t *element;
	in_port_t port;
658
	dns_cache_t *cache = NULL;
659
	isc_result_t result;
660
	isc_uint32_t max_adb_size;
661
	isc_uint32_t max_cache_size;
Mark Andrews's avatar
Mark Andrews committed
662
	isc_uint32_t lame_ttl;
Brian Wellington's avatar
Brian Wellington committed
663
	dns_tsig_keyring_t *ring;
664
	dns_view_t *pview = NULL;	/* Production view */
665
	isc_mem_t *cmctx;
666 667
	dns_dispatch_t *dispatch4 = NULL;
	dns_dispatch_t *dispatch6 = NULL;
668
	isc_boolean_t reused_cache = ISC_FALSE;
669 670
	int i;
	char *str;
671
	dns_order_t *order = NULL;
672
	isc_uint32_t udpsize;
673

674
	REQUIRE(DNS_VIEW_VALID(view));
675

676
	cmctx = NULL;
677

678
	if (config != NULL)
679
		(void)cfg_map_get(config, "options", &options);
680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697

	i = 0;
	if (vconfig != NULL) {
		voptions = cfg_tuple_get(vconfig, "options");
		maps[i++] = voptions;
	}
	if (options != NULL)
		maps[i++] = options;
	maps[i++] = ns_g_defaults;
	maps[i] = NULL;

	i = 0;
	if (voptions != NULL)
		cfgmaps[i++] = voptions;
	if (config != NULL)
		cfgmaps[i++] = config;
	cfgmaps[i] = NULL;

698 699 700
	/*
	 * Set the view's port number for outgoing queries.
	 */
701
	CHECKM(ns_config_getport(config, &port), "port");
702
	dns_view_setdstport(view, port);
703

704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720
	/*
	 * Configure the zones.
	 */
	zonelist = NULL;
	if (voptions != NULL)
		(void)cfg_map_get(voptions, "zone", &zonelist);
	else
		(void)cfg_map_get(config, "zone", &zonelist);
	for (element = cfg_list_first(zonelist);
	     element != NULL;
	     element = cfg_list_next(element))
	{
		cfg_obj_t *zconfig = cfg_listelt_value(element);
		CHECK(configure_zone(config, zconfig, vconfig, mctx, view,
				     actx));
	}

721
	/*
722 723 724
	 * Configure the view's cache.  Try to reuse an existing
	 * cache if possible, otherwise create a new cache.
	 * Note that the ADB is not preserved in either case.
725 726
	 *
	 * XXX Determining when it is safe to reuse a cache is
727 728 729 730 731 732
	 * tricky.  When the view's configuration changes, the cached
	 * data may become invalid because it reflects our old
	 * view of the world.  As more view attributes become
	 * configurable, we will have to add code here to check
	 * whether they have changed in ways that could
	 * invalidate the cache.
733
	 */
734 735 736 737 738 739 740 741
	result = dns_viewlist_find(&ns_g_server->viewlist,
				   view->name, view->rdclass,
				   &pview);
	if (result != ISC_R_NOTFOUND && result != ISC_R_SUCCESS)
		goto cleanup;
	if (pview != NULL) {
		INSIST(pview->cache != NULL);
		isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
742 743
			      NS_LOGMODULE_SERVER, ISC_LOG_DEBUG(3),
			      "reusing existing cache");
744
		reused_cache = ISC_TRUE;
745 746 747
		dns_cache_attach(pview->cache, &cache);
		dns_view_detach(&pview);
	} else {
748 749
		CHECK(isc_mem_create(0, 0, &cmctx));
		CHECK(dns_cache_create(cmctx, ns_g_taskmgr, ns_g_timermgr,
750 751
				       view->rdclass, "rbt", 0, NULL, &cache));
	}
752
	dns_view_setcache(view, cache);
753

754 755 756 757 758 759
	/*
	 * cache-file cannot be inherited if views are present, but this
	 * should be caught by the configuration checking stage.
	 */
	obj = NULL;
	result = ns_config_get(maps, "cache-file", &obj);
760
	if (result == ISC_R_SUCCESS && strcmp(view->name, "_bind") != 0) {
761
		CHECK(dns_cache_setfilename(cache, cfg_obj_asstring(obj)));
762 763 764 765
		if (!reused_cache)
			CHECK(dns_cache_load(cache));
	}

766 767 768
	obj = NULL;
	result = ns_config_get(maps, "cleaning-interval", &obj);
	INSIST(result == ISC_R_SUCCESS);
769
	dns_cache_setcleaninginterval(cache, cfg_obj_asuint32(obj) * 60);
770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790

	obj = NULL;
	result = ns_config_get(maps, "max-cache-size", &obj);
	INSIST(result == ISC_R_SUCCESS);
	if (cfg_obj_isstring(obj)) {
		str = cfg_obj_asstring(obj);
		INSIST(strcasecmp(str, "unlimited") == 0);
		max_cache_size = ISC_UINT32_MAX;
	} else {
		isc_resourcevalue_t value;
		value = cfg_obj_asuint64(obj);
		if (value > ISC_UINT32_MAX) {
			cfg_obj_log(obj, ns_g_lctx, ISC_LOG_ERROR,
				    "'max-cache-size "
				    "%" ISC_PRINT_QUADFORMAT "d' is too large",
				    value);
			result = ISC_R_RANGE;
			goto cleanup;
		}
		max_cache_size = (isc_uint32_t)value;
	}
791 792
	dns_cache_setcachesize(cache, max_cache_size);

793
	dns_cache_detach(&cache);
794

795 796 797
	/*
	 * Resolver.
	 *
Bob Halley's avatar
Bob Halley committed
798
	 * XXXRTH  Hardwired number of tasks.
799
	 */
800 801
	CHECK(get_view_querysource_dispatch(maps, AF_INET, &dispatch4));
	CHECK(get_view_querysource_dispatch(maps, AF_INET6, &dispatch6));
802 803
	if (dispatch4 == NULL && dispatch6 == NULL) {
		UNEXPECTED_ERROR(__FILE__, __LINE__,
Mark Andrews's avatar
Mark Andrews committed
804 805
				 "unable to obtain neither an IPv4 nor"
				 " an IPv6 dispatch");
806 807 808
		result = ISC_R_UNEXPECTED;
		goto cleanup;
	}
809 810
	CHECK(dns_view_createresolver(view, ns_g_taskmgr, 31,
				      ns_g_socketmgr, ns_g_timermgr,
811
				      0, ns_g_dispatchmgr,
812 813 814 815 816
				      dispatch4, dispatch6));
	if (dispatch4 != NULL)
		dns_dispatch_detach(&dispatch4);
	if (dispatch6 != NULL)
		dns_dispatch_detach(&dispatch6);
817

818
	/*
Mark Andrews's avatar
Mark Andrews committed
819
	 * Set the ADB cache size to 1/8th of the max-cache-size.
820
	 */
Mark Andrews's avatar
Mark Andrews committed
821 822
	max_adb_size = 0;
	if (max_cache_size != 0) {
Andreas Gustafsson's avatar
spacing  
Andreas Gustafsson committed
823
		max_adb_size = max_cache_size / 8;
Mark Andrews's avatar
Mark Andrews committed
824 825
		if (max_adb_size == 0)
			max_adb_size = 1;	/* Force minimum. */
826 827 828
	}
	dns_adb_setadbsize(view->adb, max_adb_size);

Mark Andrews's avatar
Mark Andrews committed
829 830 831
	/*
	 * Set resolver's lame-ttl.
	 */
832 833 834 835
	obj = NULL;
	result = ns_config_get(maps, "lame-ttl", &obj);
	INSIST(result == ISC_R_SUCCESS);
	lame_ttl = cfg_obj_asuint32(obj);
836 837
	if (lame_ttl > 1800)
		lame_ttl = 1800;
Mark Andrews's avatar
Mark Andrews committed
838 839
	dns_resolver_setlamettl(view->resolver, lame_ttl);
	
840 841 842 843 844 845 846 847 848 849 850 851
	/*
	 * Set the resolver's EDNS UDP size.
	 */
	obj = NULL;
	result = ns_config_get(maps, "edns-udp-size", &obj);
	INSIST(result == ISC_R_SUCCESS);
	udpsize = cfg_obj_asuint32(obj);
	if (udpsize < 512)
		udpsize = 512;
	if (udpsize > 4096)
		udpsize = 4096;
	dns_resolver_setudpsize(view->resolver, udpsize);
852 853 854 855 856 857 858 859 860 861 862 863 864 865
	
	/*
	 * Set supported DNSSEC algorithms.
	 */
	dns_resolver_reset_algorithms(view->resolver);
	disabled = NULL;
	(void)ns_config_get(maps, "disable-algorithms", &disabled);
	if (disabled != NULL) {
		for (element = cfg_list_first(disabled);
		     element != NULL;
		     element = cfg_list_next(element))
			CHECK(disable_algorithms(cfg_listelt_value(element),
						 view->resolver));
	}
866

867
	/*
868 869
	 * A global or view "forwarders" option, if present,
	 * creates an entry for "." in the forwarding table.
870
	 */
871
	forwardtype = NULL;
872
	forwarders = NULL;
873
	(void)ns_config_get(maps, "forward", &forwardtype);
874
	(void)ns_config_get(maps, "forwarders", &forwarders);
875 876 877
	if (forwarders != NULL)
		CHECK(configure_forward(config, view, dns_rootname, 
					forwarders, forwardtype));
878

879 880 881 882 883 884 885 886
	/*
	 * Dual Stack Servers.
	 */
	alternates = NULL;
	(void)ns_config_get(maps, "dual-stack-servers", &alternates);
	if (alternates != NULL)
		CHECK(configure_alternates(config, view, alternates));

887
	/*
888
	 * We have default hints for class IN if we need them.
889
	 */
890
	if (view->rdclass == dns_rdataclass_in && view->hints == NULL)
891
		dns_view_sethints(view, ns_g_server->in_roothints);
892

893 894
	/*
	 * If we still have no hints, this is a non-IN view with no
895 896
	 * "hints zone" configured.  Issue a warning, except if this
	 * is a root server.  Root servers never need to consult 
Andreas Gustafsson's avatar
Andreas Gustafsson committed
897
	 * their hints, so it's no point requiring users to configure
898
	 * them.
899 900
	 */
	if (view->hints == NULL) {
901
		dns_zone_t *rootzone = NULL;
902
		(void)dns_view_findzone(view, dns_rootname, &rootzone);
903 904
		if (rootzone != NULL) {
			dns_zone_detach(&rootzone);
905 906 907
			need_hints = ISC_FALSE;
		}
		if (need_hints)
908 909 910
			isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
				      NS_LOGMODULE_SERVER, ISC_LOG_WARNING,
				      "no root hints for view '%s'",
911
				      view->name);
912
	}
913

Brian Wellington's avatar
Brian Wellington committed
914
	/*
915
	 * Configure the view's TSIG keys.
Brian Wellington's avatar
Brian Wellington committed
916 917
	 */
	ring = NULL;
918
	CHECK(ns_tsigkeyring_fromconfig(config, vconfig, view->mctx, &ring));
Brian Wellington's avatar
Brian Wellington committed
919 920
	dns_view_setkeyring(view, ring);

921 922 923 924
	/*
	 * Configure the view's peer list.
	 */
	{
925 926
		cfg_obj_t *peers = NULL;
		cfg_listelt_t *element;
927
		dns_peerlist_t *newpeers = NULL;
928

929 930 931 932 933 934 935 936
		(void)ns_config_get(cfgmaps, "server", &peers);
		CHECK(dns_peerlist_new(mctx, &newpeers));
		for (element = cfg_list_first(peers);
		     element != NULL;
		     element = cfg_list_next(element))
		{
			cfg_obj_t *cpeer = cfg_listelt_value(element);
			dns_peer_t *peer;
937

938 939 940 941
			CHECK(configure_peer(cpeer, mctx, &peer));
			dns_peerlist_addpeer(newpeers, peer);
			dns_peer_detach(&peer);
		}
942 943 944
		dns_peerlist_detach(&view->peers);
		view->peers = newpeers; /* Transfer ownership. */
	}
945

946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967
	/*
	 *	Configure the views rrset-order.
	 */
	{
		cfg_obj_t *rrsetorder = NULL;
		cfg_listelt_t *element;

		(void)ns_config_get(maps, "rrset-order", &rrsetorder);
		CHECK(dns_order_create(mctx, &order));
		for (element = cfg_list_first(rrsetorder);
		     element != NULL;
		     element = cfg_list_next(element))
		{
			cfg_obj_t *ent = cfg_listelt_value(element);

			CHECK(configure_order(order, ent));
		}
		if (view->order != NULL)
			dns_order_detach(&view->order);
		dns_order_attach(order, &view->order);
		dns_order_detach(&order);
	}
968 969 970 971 972
	/*
	 * Copy the aclenv object.
	 */
	dns_aclenv_copy(&view->aclenv, &ns_g_server->aclenv);

973
	/*
974
	 * Configure the "match-clients" and "match-destinations" ACL.
975
	 */
976 977
	CHECK(configure_view_acl(vconfig, config, "match-clients", actx,
				 ns_g_mctx, &view->matchclients));
978 979 980 981 982 983 984 985 986 987 988 989
	CHECK(configure_view_acl(vconfig, config, "match-destinations", actx,
				 ns_g_mctx, &view->matchdestinations));

	/*
	 * Configure the "match-recursive-only" option.
	 */
	obj = NULL;
	(void) ns_config_get(maps, "match-recursive-only", &obj);
	if (obj != NULL && cfg_obj_asboolean(obj))
		view->matchrecursiveonly = ISC_TRUE;
	else
		view->matchrecursiveonly = ISC_FALSE;
990

991 992 993
	/*
	 * Configure other configurable data.
	 */
994 995 996 997 998 999 1000 1001 1002 1003
	obj = NULL;
	result = ns_config_get(maps, "recursion", &obj);
	INSIST(result == ISC_R_SUCCESS);
	view->recursion = cfg_obj_asboolean(obj);

	obj = NULL;
	result = ns_config_get(maps, "auth-nxdomain", &obj);
	INSIST(result == ISC_R_SUCCESS);
	view->auth_nxdomain = cfg_obj_asboolean(obj);

Bob Halley's avatar
Bob Halley committed
1004 1005 1006 1007 1008
	obj = NULL;
	result = ns_config_get(maps, "minimal-responses", &obj);
	INSIST(result == ISC_R_SUCCESS);
	view->minimalresponses = cfg_obj_asboolean(obj);

1009 1010 1011 1012 1013
	obj = NULL;
	result = ns_config_get(maps, "transfer-format", &obj);
	INSIST(result == ISC_R_SUCCESS);
	str = cfg_obj_asstring(obj);
	if (strcasecmp(str, "many-answers") == 0)
1014
		view->transfer_format = dns_many_answers;