server.c 133 KB
Newer Older
1
/*
Mark Andrews's avatar
Mark Andrews committed
2
 * Copyright (C) 2004-2006  Internet Systems Consortium, Inc. ("ISC")
Mark Andrews's avatar
Mark Andrews committed
3
 * Copyright (C) 1999-2003  Internet Software Consortium.
4
 *
5 6 7
 * 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.
8
 *
Mark Andrews's avatar
Mark Andrews committed
9 10 11 12 13 14 15
 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
 * AND FITNESS.  IN NO EVENT SHALL ISC 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.467 2006/12/04 01:52:45 marka Exp $ */
19 20

/*! \file */
David Lawrence's avatar
David Lawrence committed
21

22 23 24 25
#include <config.h>

#include <stdlib.h>

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

42
#include <isccfg/namedconf.h>
43 44

#include <bind9/check.h>
45

46
#include <dns/acache.h>
47
#include <dns/adb.h>
48
#include <dns/cache.h>
49
#include <dns/db.h>
Bob Halley's avatar
Bob Halley committed
50
#include <dns/dispatch.h>
51 52 53
#ifdef DLZ
#include <dns/dlz.h>
#endif
54
#include <dns/forward.h>
55
#include <dns/journal.h>
56
#include <dns/keytable.h>
57
#include <dns/lib.h>
58
#include <dns/master.h>
59
#include <dns/masterdump.h>
60
#include <dns/order.h>
61
#include <dns/peer.h>
62
#include <dns/portlist.h>
63
#include <dns/rbt.h>
64
#include <dns/rdataclass.h>
65
#include <dns/rdataset.h>
66
#include <dns/rdatastruct.h>
67
#include <dns/resolver.h>
68
#include <dns/rootns.h>
69
#include <dns/secalg.h>
70
#include <dns/stats.h>
71
#include <dns/tkey.h>
72
#include <dns/tsig.h>
73
#include <dns/view.h>
74
#include <dns/zone.h>
75
#include <dns/zt.h>
76

77
#include <dst/dst.h>
78
#include <dst/result.h>
79

80
#include <named/client.h>
81
#include <named/config.h>
82
#include <named/control.h>
83
#include <named/interfacemgr.h>
84
#include <named/log.h>
85
#include <named/logconf.h>
86
#include <named/lwresd.h>
87
#include <named/main.h>
88
#include <named/os.h>
Bob Halley's avatar
Bob Halley committed
89
#include <named/server.h>
90 91 92
#include <named/tkeyconf.h>
#include <named/tsigconf.h>
#include <named/zoneconf.h>
93 94 95 96
#ifdef HAVE_LIBSCF
#include <named/ns_smf_globals.h>
#include <stdlib.h>
#endif
97

98
/*%
99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119
 * 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
120 121 122 123 124 125 126 127 128 129 130 131 132
#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)						  \

133 134 135 136 137 138
#define CHECKFATAL(op, msg) \
	do { result = (op); 				  	  \
	       if (result != ISC_R_SUCCESS)			  \
			fatal(msg, result);			  \
	} while (0)						  \

139 140 141 142 143 144 145
struct ns_dispatch {
	isc_sockaddr_t			addr;
	unsigned int			dispatchgen;
	dns_dispatch_t			*dispatch;
	ISC_LINK(struct ns_dispatch)	link;
};

146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171
struct dumpcontext {
	isc_mem_t			*mctx;
	isc_boolean_t			dumpcache;
	isc_boolean_t			dumpzones;
	FILE				*fp;
	ISC_LIST(struct viewlistentry)	viewlist;
	struct viewlistentry		*view;
	struct zonelistentry		*zone;
	dns_dumpctx_t			*mdctx;
	dns_db_t			*db;
	dns_db_t			*cache;
	isc_task_t			*task;
	dns_dbversion_t			*version;
};

struct viewlistentry {
	dns_view_t			*view;
	ISC_LINK(struct viewlistentry)	link;
	ISC_LIST(struct zonelistentry)	zonelist;
};

struct zonelistentry {
	dns_zone_t			*zone;
	ISC_LINK(struct zonelistentry)	link;
};

172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219
/*
 * These zones should not leak onto the Internet.
 */
static const struct {
	const char	*zone;
	isc_boolean_t	rfc1918;
} empty_zones[] = {
#ifdef notyet
	/* RFC 1918 */
	{ "10.IN-ADDR.ARPA", ISC_TRUE },
	{ "16.172.IN-ADDR.ARPA", ISC_TRUE },
	{ "17.172.IN-ADDR.ARPA", ISC_TRUE },
	{ "18.172.IN-ADDR.ARPA", ISC_TRUE },
	{ "19.172.IN-ADDR.ARPA", ISC_TRUE },
	{ "20.172.IN-ADDR.ARPA", ISC_TRUE },
	{ "21.172.IN-ADDR.ARPA", ISC_TRUE },
	{ "22.172.IN-ADDR.ARPA", ISC_TRUE },
	{ "23.172.IN-ADDR.ARPA", ISC_TRUE },
	{ "24.172.IN-ADDR.ARPA", ISC_TRUE },
	{ "25.172.IN-ADDR.ARPA", ISC_TRUE },
	{ "26.172.IN-ADDR.ARPA", ISC_TRUE },
	{ "27.172.IN-ADDR.ARPA", ISC_TRUE },
	{ "28.172.IN-ADDR.ARPA", ISC_TRUE },
	{ "29.172.IN-ADDR.ARPA", ISC_TRUE },
	{ "30.172.IN-ADDR.ARPA", ISC_TRUE },
	{ "31.172.IN-ADDR.ARPA", ISC_TRUE },
	{ "168.192.IN-ADDR.ARPA", ISC_TRUE },
#endif

	/* RFC 3330 */
	{ "127.IN-ADDR.ARPA", ISC_FALSE },	/* LOOPBACK */
	{ "254.169.IN-ADDR.ARPA", ISC_FALSE },	/* LINK LOCAL */
	{ "2.0.192.IN-ADDR.ARPA", ISC_FALSE },	/* TEST NET */
	{ "255.255.255.255.IN-ADDR.ARPA", ISC_FALSE },	/* BROADCAST */

	/* Local IPv6 Unicast Addresses */
	{ "0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.IP6.ARPA", ISC_FALSE },
	{ "1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.IP6.ARPA", ISC_FALSE },
	/* LOCALLY ASSIGNED LOCAL ADDRES S SCOPE */
	{ "D.F.IP6.ARPA", ISC_FALSE },
	{ "8.E.F.IP6.ARPA", ISC_FALSE },	/* LINK LOCAL */
	{ "9.E.F.IP6.ARPA", ISC_FALSE },	/* LINK LOCAL */
	{ "A.E.F.IP6.ARPA", ISC_FALSE },	/* LINK LOCAL */
	{ "B.E.F.IP6.ARPA", ISC_FALSE },	/* LINK LOCAL */

	{ NULL, ISC_FALSE }
};

David Lawrence's avatar
David Lawrence committed
220 221 222 223 224
static void
fatal(const char *msg, isc_result_t result);

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

226
static isc_result_t
227
ns_listenelt_fromconfig(const cfg_obj_t *listener, const cfg_obj_t *config,
228
			cfg_aclconfctx_t *actx,
229 230
			isc_mem_t *mctx, ns_listenelt_t **target);
static isc_result_t
231
ns_listenlist_fromconfig(const cfg_obj_t *listenlist, const cfg_obj_t *config,
232
			 cfg_aclconfctx_t *actx,
233
			 isc_mem_t *mctx, ns_listenlist_t **target);
234

235
static isc_result_t
236 237
configure_forward(const cfg_obj_t *config, dns_view_t *view, dns_name_t *origin,
		  const cfg_obj_t *forwarders, const cfg_obj_t *forwardtype);
238

239
static isc_result_t
240 241
configure_alternates(const cfg_obj_t *config, dns_view_t *view,
		     const cfg_obj_t *alternates);
242

243
static isc_result_t
244 245
configure_zone(const cfg_obj_t *config, const cfg_obj_t *zconfig,
	       const cfg_obj_t *vconfig, isc_mem_t *mctx, dns_view_t *view,
246
	       cfg_aclconfctx_t *aclconf);
247

248 249 250
static void
end_reserved_dispatches(ns_server_t *server, isc_boolean_t all);

251
/*%
252 253 254 255 256
 * 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
257
configure_view_acl(const cfg_obj_t *vconfig, const cfg_obj_t *config,
258
		   const char *aclname, cfg_aclconfctx_t *actx,
259
		   isc_mem_t *mctx, dns_acl_t **aclp)
260 261
{
	isc_result_t result;
262 263
	const cfg_obj_t *maps[3];
	const cfg_obj_t *aclobj = NULL;
264 265
	int i = 0;

266 267
	if (*aclp != NULL)
		dns_acl_detach(aclp);
268 269 270
	if (vconfig != NULL)
		maps[i++] = cfg_tuple_get(vconfig, "options");
	if (config != NULL) {
271
		const cfg_obj_t *options = NULL;
272
		(void)cfg_map_get(config, "options", &options);
273 274 275 276
		if (options != NULL)
			maps[i++] = options;
	}
	maps[i] = NULL;
277

278
	(void)ns_config_get(maps, aclname, &aclobj);
279
	if (aclobj == NULL)
280 281 282
		/*
		 * No value available.  *aclp == NULL.
		 */
283 284
		return (ISC_R_SUCCESS);

285 286
	result = cfg_acl_fromconfig(aclobj, config, ns_g_lctx,
				    actx, mctx, aclp);
287 288 289 290

	return (result);
}

291
static isc_result_t
292
configure_view_dnsseckey(const cfg_obj_t *vconfig, const cfg_obj_t *key,
293 294 295
			 dns_keytable_t *keytable, isc_mem_t *mctx)
{
	dns_rdataclass_t viewclass;
296
	dns_rdata_dnskey_t keystruct;
297
	isc_uint32_t flags, proto, alg;
298
	const char *keystr, *keynamestr;
299 300 301 302 303 304 305 306 307 308 309
	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;

310 311 312 313 314 315 316
	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)
317
		viewclass = dns_rdataclass_in;
318
	else {
319
		const cfg_obj_t *classobj = cfg_tuple_get(vconfig, "class");
320 321
		CHECK(ns_config_getclass(classobj, dns_rdataclass_in,
					 &viewclass));
322
	}
323
	keystruct.common.rdclass = viewclass;
324
	keystruct.common.rdtype = dns_rdatatype_dnskey;
325
	/*
326
	 * The key data in keystruct is not dynamically allocated.
327 328 329 330 331
	 */
	keystruct.mctx = NULL;

	ISC_LINK_INIT(&keystruct.common, link);

332
	if (flags > 0xffff)
333
		CHECKM(ISC_R_RANGE, "key flags");
334
	if (proto > 0xff)
335
		CHECKM(ISC_R_RANGE, "key protocol");
336
	if (alg > 0xff)
337
		CHECKM(ISC_R_RANGE, "key algorithm");
338 339 340
	keystruct.flags = (isc_uint16_t)flags;
	keystruct.protocol = (isc_uint8_t)proto;
	keystruct.algorithm = (isc_uint8_t)alg;
341 342 343 344

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

345
	keystr = cfg_obj_asstring(cfg_tuple_get(key, "key"));
346
	CHECK(isc_base64_decodestring(keystr, &keydatabuf));
347 348 349 350 351 352 353 354 355
	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);
356 357
	isc_buffer_init(&namebuf, keynamestr, strlen(keynamestr));
	isc_buffer_add(&namebuf, strlen(keynamestr));
358 359 360 361 362 363 364 365 366 367 368
	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:
369 370 371 372 373 374 375 376 377 378 379
	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;
	}
380 381 382 383 384 385 386

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

	return (result);
}

387
/*%
388 389
 * Configure DNSSEC keys for a view.  Currently used only for
 * the security roots.
390
 *
391 392
 * The per-view configuration values and the server-global defaults are read
 * from 'vconfig' and 'config'.  The variable to be configured is '*target'.
393 394
 */
static isc_result_t
395
configure_view_dnsseckeys(const cfg_obj_t *vconfig, const cfg_obj_t *config,
396
			  isc_mem_t *mctx, dns_keytable_t **target)
397 398
{
	isc_result_t result;
399 400 401 402 403
	const cfg_obj_t *keys = NULL;
	const cfg_obj_t *voptions = NULL;
	const cfg_listelt_t *element, *element2;
	const cfg_obj_t *keylist;
	const cfg_obj_t *key;
404
	dns_keytable_t *keytable = NULL;
405

406 407
	CHECK(dns_keytable_create(mctx, &keytable));

408 409
	if (vconfig != NULL)
		voptions = cfg_tuple_get(vconfig, "options");
410

411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427
	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,
428
						       keytable, mctx));
429
		}
430
	}
431

432 433 434 435
	dns_keytable_detach(target);
	*target = keytable; /* Transfer ownership. */
	keytable = NULL;
	result = ISC_R_SUCCESS;
436
	
437 438 439
 cleanup:
	return (result);
}
440

441
static isc_result_t
442
mustbesecure(const cfg_obj_t *mbs, dns_resolver_t *resolver)
443
{
444 445
	const cfg_listelt_t *element;
	const cfg_obj_t *obj;
446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463
	const char *str;
	dns_fixedname_t fixed;
	dns_name_t *name;
	isc_boolean_t value;
	isc_result_t result;
	isc_buffer_t b;
	
	dns_fixedname_init(&fixed);
	name = dns_fixedname_name(&fixed);
	for (element = cfg_list_first(mbs);
	     element != NULL;
	     element = cfg_list_next(element))
	{
		obj = cfg_listelt_value(element);
		str = cfg_obj_asstring(cfg_tuple_get(obj, "name"));
		isc_buffer_init(&b, str, strlen(str));
		isc_buffer_add(&b, strlen(str));
		CHECK(dns_name_fromtext(name, &b, dns_rootname,
464
					ISC_FALSE, NULL));
465 466 467 468 469 470 471 472 473
		value = cfg_obj_asboolean(cfg_tuple_get(obj, "value"));
		CHECK(dns_resolver_setmustbesecure(resolver, name, value));
	}

	result = ISC_R_SUCCESS;
	
 cleanup:
	return (result);
}
474

475
/*%
476 477 478
 * Get a dispatch appropriate for the resolver of a given view.
 */
static isc_result_t
479
get_view_querysource_dispatch(const cfg_obj_t **maps,
480 481 482 483 484 485
			      int af, dns_dispatch_t **dispatchp)
{
	isc_result_t result;
	dns_dispatch_t *disp;
	isc_sockaddr_t sa;
	unsigned int attrs, attrmask;
486
	const cfg_obj_t *obj = NULL;
487 488 489 490 491 492 493 494

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

	switch (af) {
	case AF_INET:
495 496
		result = ns_config_get(maps, "query-source", &obj);
		INSIST(result == ISC_R_SUCCESS);
497 498
		break;
	case AF_INET6:
499 500
		result = ns_config_get(maps, "query-source-v6", &obj);
		INSIST(result == ISC_R_SUCCESS);
501 502 503 504
		break;
	default:
		INSIST(0);
	}
505

506
	sa = *(cfg_obj_assockaddr(obj));
507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548
	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);
549
	if (result != ISC_R_SUCCESS) {
550 551 552 553 554 555 556 557 558 559 560 561 562 563
		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));
564
		isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
565
			      NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
566 567
			      "could not get query source dispatcher (%s)",
			      buf);
568
		return (result);
569
	}
570 571 572 573 574 575

	*dispatchp = disp;

	return (ISC_R_SUCCESS);
}

576
static isc_result_t
577
configure_order(dns_order_t *order, const cfg_obj_t *ent) {
578 579
	dns_rdataclass_t rdclass;
	dns_rdatatype_t rdtype;
580
	const cfg_obj_t *obj;
581 582 583 584 585
	dns_fixedname_t fixed;
	unsigned int mode = 0;
	const char *str;
	isc_buffer_t b;
	isc_result_t result;
586
	isc_boolean_t addroot;
587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602

	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 = "*";
603
	addroot = ISC_TF(strcmp(str, "*") == 0);
604 605 606 607
	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,
608
				   dns_rootname, ISC_FALSE, NULL);
609 610 611 612 613 614 615 616 617 618 619 620 621 622 623
	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);

624 625 626
	/*
	 * "*" should match everything including the root (BIND 8 compat).
	 * As dns_name_matcheswildcard(".", "*.") returns FALSE add a
Mark Andrews's avatar
Mark Andrews committed
627
	 * explicit entry for "." when the name is "*".
628 629 630 631 632 633 634 635
	 */
	if (addroot) {
		result = dns_order_add(order, dns_rootname,
				       rdtype, rdclass, mode);
		if (result != ISC_R_SUCCESS)
			return (result);
	}

636 637 638 639
	return (dns_order_add(order, dns_fixedname_name(&fixed),
			      rdtype, rdclass, mode));
}

640
static isc_result_t
641
configure_peer(const cfg_obj_t *cpeer, isc_mem_t *mctx, dns_peer_t **peerp) {
642 643
	isc_netaddr_t na;
	dns_peer_t *peer;
644
	const cfg_obj_t *obj;
645
	const char *str;
646
	isc_result_t result;
647
	unsigned int prefixlen;
648

649
	cfg_obj_asnetprefix(cfg_map_getname(cpeer), &na, &prefixlen);
650 651 652 653 654 655 656 657 658

	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)
659
		CHECK(dns_peer_setbogus(peer, cfg_obj_asboolean(obj)));
660 661 662 663

	obj = NULL;
	(void)cfg_map_get(cpeer, "provide-ixfr", &obj);
	if (obj != NULL)
664
		CHECK(dns_peer_setprovideixfr(peer, cfg_obj_asboolean(obj)));
665 666 667 668

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

671 672 673
	obj = NULL;
	(void)cfg_map_get(cpeer, "edns", &obj);
	if (obj != NULL)
674
		CHECK(dns_peer_setsupportedns(peer, cfg_obj_asboolean(obj)));
675

676 677 678 679 680 681 682 683 684 685 686
	obj = NULL;
	(void)cfg_map_get(cpeer, "edns-udp-size", &obj);
	if (obj != NULL) {
		isc_uint32_t udpsize = cfg_obj_asuint32(obj);
		if (udpsize < 512)
			udpsize = 512;
		if (udpsize > 4096)
			udpsize = 4096;
		CHECK(dns_peer_setudpsize(peer, (isc_uint16_t)udpsize));
	}

687 688 689 690 691 692 693 694 695 696 697
	obj = NULL;
	(void)cfg_map_get(cpeer, "max-udp-size", &obj);
	if (obj != NULL) {
		isc_uint32_t udpsize = cfg_obj_asuint32(obj);
		if (udpsize < 512)
			udpsize = 512;
		if (udpsize > 4096)
			udpsize = 4096;
		CHECK(dns_peer_setmaxudp(peer, (isc_uint16_t)udpsize));
	}

698 699 700
	obj = NULL;
	(void)cfg_map_get(cpeer, "transfers", &obj);
	if (obj != NULL)
701
		CHECK(dns_peer_settransfers(peer, cfg_obj_asuint32(obj)));
702 703 704 705 706 707

	obj = NULL;
	(void)cfg_map_get(cpeer, "transfer-format", &obj);
	if (obj != NULL) {
		str = cfg_obj_asstring(obj);
		if (strcasecmp(str, "many-answers") == 0)
708 709
			CHECK(dns_peer_settransferformat(peer,
							 dns_many_answers));
710
		else if (strcasecmp(str, "one-answer") == 0)
711 712
			CHECK(dns_peer_settransferformat(peer,
							 dns_one_answer));
713 714 715 716 717 718 719 720 721 722 723
		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;
	}
724 725

	obj = NULL;
726
	if (na.family == AF_INET)
727 728 729 730 731 732 733 734
		(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;
735
		ns_add_reserved_dispatch(ns_g_server, cfg_obj_assockaddr(obj));
736
	}
737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763

	obj = NULL;
	if (na.family == AF_INET)
		(void)cfg_map_get(cpeer, "notify-source", &obj);
	else
		(void)cfg_map_get(cpeer, "notify-source-v6", &obj);
	if (obj != NULL) {
		result = dns_peer_setnotifysource(peer,
						  cfg_obj_assockaddr(obj));
		if (result != ISC_R_SUCCESS)
			goto cleanup;
		ns_add_reserved_dispatch(ns_g_server, cfg_obj_assockaddr(obj));
	}

	obj = NULL;
	if (na.family == AF_INET)
		(void)cfg_map_get(cpeer, "query-source", &obj);
	else
		(void)cfg_map_get(cpeer, "query-source-v6", &obj);
	if (obj != NULL) {
		result = dns_peer_setquerysource(peer,
						 cfg_obj_assockaddr(obj));
		if (result != ISC_R_SUCCESS)
			goto cleanup;
		ns_add_reserved_dispatch(ns_g_server, cfg_obj_assockaddr(obj));
	}

764 765 766 767 768 769 770 771
	*peerp = peer;
	return (ISC_R_SUCCESS);

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

772
static isc_result_t
773
disable_algorithms(const cfg_obj_t *disabled, dns_resolver_t *resolver) {
774
	isc_result_t result;
775 776
	const cfg_obj_t *algorithms;
	const cfg_listelt_t *element;
777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796
	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;

797
		DE_CONST(cfg_obj_asstring(cfg_listelt_value(element)), r.base);
798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817
		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);
}

818
static isc_boolean_t
819 820
on_disable_list(const cfg_obj_t *disablelist, dns_name_t *zonename) {
	const cfg_listelt_t *element;
821 822 823
	dns_fixedname_t fixed;
	dns_name_t *name;
	isc_result_t result;
824
	const cfg_obj_t *value;
825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847
	const char *str;
	isc_buffer_t b;

	dns_fixedname_init(&fixed);
	name = dns_fixedname_name(&fixed);
	
	for (element = cfg_list_first(disablelist);
	     element != NULL;
	     element = cfg_list_next(element))
	{
		value = cfg_listelt_value(element);
		str = cfg_obj_asstring(value);
		isc_buffer_init(&b, str, strlen(str));
		isc_buffer_add(&b, strlen(str));
		result = dns_name_fromtext(name, &b, dns_rootname,
					   ISC_TRUE, NULL);
		RUNTIME_CHECK(result == ISC_R_SUCCESS);
		if (dns_name_equal(name, zonename))
			return (ISC_TRUE);
	}
	return (ISC_FALSE);
}

848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879
static void
check_dbtype(dns_zone_t **zonep, unsigned int dbtypec, const char **dbargv,
	     isc_mem_t *mctx)
{
	char **argv = NULL;
	unsigned int i;
	isc_result_t result;

	result = dns_zone_getdbtype(*zonep, &argv, mctx);
	if (result != ISC_R_SUCCESS) {
		dns_zone_detach(zonep);
		return;
	}

	/*
	 * Check that all the arguments match.
	 */
	for (i = 0; i < dbtypec; i++)
		if (argv[i] == NULL || strcmp(argv[i], dbargv[i]) != 0) {
			dns_zone_detach(zonep);
			break;
		}

	/*
	 * Check that there are not extra arguments.
	 */
	if (i == dbtypec && argv[i] != NULL)
		dns_zone_detach(zonep);
	isc_mem_free(mctx, argv);
}


880
/*
881 882
 * Configure 'view' according to 'vconfig', taking defaults from 'config'
 * where values are missing in 'vconfig'.
883
 *
884 885
 * When configuring the default view, 'vconfig' will be NULL and the
 * global defaults in 'config' used exclusively.
886
 */
887
static isc_result_t
888 889 890
configure_view(dns_view_t *view, const cfg_obj_t *config,
	       const cfg_obj_t *vconfig, isc_mem_t *mctx,
	       cfg_aclconfctx_t *actx, isc_boolean_t need_hints)
891
{
892 893 894 895 896 897 898 899
	const cfg_obj_t *maps[4];
	const cfg_obj_t *cfgmaps[3];
	const cfg_obj_t *options = NULL;
	const cfg_obj_t *voptions = NULL;
	const cfg_obj_t *forwardtype;
	const cfg_obj_t *forwarders;
	const cfg_obj_t *alternates;
	const cfg_obj_t *zonelist;
900
#ifdef DLZ
901
 	const cfg_obj_t *dlz;
902 903 904
 	unsigned int dlzargc;
 	char **dlzargv;
#endif
905 906 907
	const cfg_obj_t *disabled;
	const cfg_obj_t *obj;
	const cfg_listelt_t *element;
908
	in_port_t port;
909
	dns_cache_t *cache = NULL;
910
	isc_result_t result;
911
	isc_uint32_t max_adb_size;
912
	isc_uint32_t max_cache_size;
913
	isc_uint32_t max_acache_size;
Mark Andrews's avatar
Mark Andrews committed
914
	isc_uint32_t lame_ttl;
Brian Wellington's avatar
Brian Wellington committed
915
	dns_tsig_keyring_t *ring;
916
	dns_view_t *pview = NULL;	/* Production view */
917
	isc_mem_t *cmctx;
918 919
	dns_dispatch_t *dispatch4 = NULL;
	dns_dispatch_t *dispatch6 = NULL;
920
	isc_boolean_t reused_cache = ISC_FALSE;
921
	int i;
922
	const char *str;
923
	dns_order_t *order = NULL;
924
	isc_uint32_t udpsize;
925
	unsigned int check = 0;
926
	dns_zone_t *zone = NULL;
927
	isc_uint32_t max_clients_per_query;
928 929 930 931 932
	const char *sep = ": view ";
	const char *viewname = view->name;
	const char *forview = " for view ";
	isc_boolean_t rfc1918;
	isc_boolean_t empty_zones_enable;
933
	const cfg_obj_t *disablelist = NULL;
934

935
	REQUIRE(DNS_VIEW_VALID(view));
936

937
	cmctx = NULL;
938

939
	if (config != NULL)
940
		(void)cfg_map_get(config, "options", &options);
941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958

	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;

959 960 961 962 963 964
	if (!strcmp(viewname, "_default")) {
		sep = "";
		viewname = "";
		forview = "";
	}

965 966 967
	/*
	 * Set the view's port number for outgoing queries.
	 */
968
	CHECKM(ns_config_getport(config, &port), "port");
969
	dns_view_setdstport(view, port);
970

971 972
	/*
	 * Create additional cache for this view and zones under the view
973 974
	 * if explicitly enabled.
	 * XXX950 default to on.
975 976
	 */
	obj = NULL;
977 978
	(void)ns_config_get(maps, "acache-enable", &obj);
	if (obj != NULL && cfg_obj_asboolean(obj)) {
979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016
		cmctx = NULL;
		CHECK(isc_mem_create(0, 0, &cmctx));
		CHECK(dns_acache_create(&view->acache, cmctx, ns_g_taskmgr,
					ns_g_timermgr));
		isc_mem_detach(&cmctx);
	}
	if (view->acache != NULL) {
		obj = NULL;
		result = ns_config_get(maps, "acache-cleaning-interval", &obj);
		INSIST(result == ISC_R_SUCCESS);
		dns_acache_setcleaninginterval(view->acache,
					       cfg_obj_asuint32(obj) * 60);

		obj = NULL;
		result = ns_config_get(maps, "max-acache-size", &obj);
		INSIST(result == ISC_R_SUCCESS);
		if (cfg_obj_isstring(obj)) {
			str = cfg_obj_asstring(obj);
			INSIST(strcasecmp(str, "unlimited") == 0);
			max_acache_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-acache-size "
					    "%" ISC_PRINT_QUADFORMAT
					    "d' is too large",
					    value);
				result = ISC_R_RANGE;
				goto cleanup;
			}
			max_acache_size = (isc_uint32_t)value;
		}
		dns_acache_setcachesize(view->acache, max_acache_size);
	}

1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028
	/*
	 * 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))
	{
1029
		const cfg_obj_t *zconfig = cfg_listelt_value(element);
1030 1031 1032 1033
		CHECK(configure_zone(config, zconfig, vconfig, mctx, view,
				     actx));
	}

1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066
#ifdef DLZ
	/*
	 * Create Dynamically Loadable Zone driver.
	 */
	dlz = NULL;
	if (voptions != NULL)
		(void)cfg_map_get(voptions, "dlz", &dlz);
	else
		(void)cfg_map_get(config, "dlz", &dlz);

	obj = NULL;
	if (dlz != NULL) {
		(void)cfg_map_get(cfg_tuple_get(dlz, "options"),
				  "database", &obj);
		if (obj != NULL) {
			char *s = isc_mem_strdup(mctx, cfg_obj_asstring(obj));
			if (s == NULL) {
				result = ISC_R_NOMEMORY;
				goto cleanup;
			}
			
			result = dns_dlzstrtoargv(mctx, s, &dlzargc, &dlzargv);
			if (result != ISC_R_SUCCESS) {
				isc_mem_free(mctx, s);
				goto cleanup;
			}

			obj = cfg_tuple_get(dlz, "name");
			result = dns_dlzcreate(mctx, cfg_obj_asstring(obj),
					       dlzargv[0], dlzargc, dlzargv,
					       &view->dlzdatabase);
			isc_mem_free(mctx, s);
			isc_mem_put(mctx, dlzargv, dlzargc * sizeof(*dlzargv));
1067
			if (result != ISC_R_SUCCESS)
1068 1069 1070 1071 1072
				goto cleanup;
		}
	}
#endif

1073
	/*
1074 1075 1076
	 * 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.
1077 1078
	 *
	 * XXX Determining when it is safe to reuse a cache is
1079 1080 1081 1082 1083 1084
	 * 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.
1085
	 */
1086 1087 1088 1089 1090 1091 1092 1093
	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,
1094 1095
			      NS_LOGMODULE_SERVER, ISC_LOG_DEBUG(3),
			      "reusing existing cache");
1096
		reused_cache = ISC_TRUE;
1097 1098 1099
		dns_cache_attach(pview->cache, &cache);
		dns_view_detach(&pview);
	} else {
1100 1101
		CHECK(isc_mem_create(0, 0, &cmctx));
		CHECK(dns_cache_create(cmctx, ns_g_taskmgr, ns_g_timermgr,
1102 1103
				       view->rdclass, "rbt", 0, NULL, &cache));
	}
1104
	dns_view_setcache(view, cache);
1105

1106 1107 1108 1109 1110 1111
	/*
	 * 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);
1112
	if (result == ISC_R_SUCCESS && strcmp(view->name, "_bind") != 0) {
1113
		CHECK(dns_cache_setfilename(cache, cfg_obj_asstring(obj)));
1114 1115 1116 1117
		if (!reused_cache)
			CHECK(dns_cache_load(cache));
	}

1118 1119 1120
	obj = NULL;
	result = ns_config_get(maps, "cleaning-interval", &obj);
	INSIST(result == ISC_R_SUCCESS);
1121
	dns_cache_setcleaninginterval(cache, cfg_obj_asuint32(obj) * 60);
1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142

	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;
	}
1143 1144
	dns_cache_setcachesize(cache, max_cache_size);

1145
	dns_cache_detach(&cache);
1146

1147 1148 1149 1150
	/*
	 * Check-names.
	 */
	obj = NULL;
1151
	result = ns_checknames_get(maps, "response"