zoneconf.c 56.2 KB
Newer Older
1
/*
2
 * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
3
 *
4 5 6
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
7 8 9
 *
 * See the COPYRIGHT file distributed with this work for additional
 * information regarding copyright ownership.
10 11 12 13
 */

#include <config.h>

14
#include <inttypes.h>
15
#include <stdbool.h>
16

17
#include <isc/buffer.h>
18
#include <isc/file.h>
19
#include <isc/mem.h>
20
#include <isc/print.h>
21
#include <isc/stats.h>
22
#include <isc/string.h>		/* Required for HP/UX (and others?) */
Bob Halley's avatar
Bob Halley committed
23
#include <isc/util.h>
24

25
#include <dns/acl.h>
26
#include <dns/db.h>
27
#include <dns/ipkeylist.h>
28
#include <dns/fixedname.h>
29
#include <dns/journal.h>
30
#include <dns/log.h>
31
#include <dns/name.h>
Evan Hunt's avatar
Evan Hunt committed
32
#include <dns/masterdump.h>
33
#include <dns/rdata.h>
34
#include <dns/rdatatype.h>
35 36 37
#include <dns/rdataset.h>
#include <dns/rdatalist.h>
#include <dns/result.h>
38
#include <dns/sdlz.h>
Mark Andrews's avatar
Mark Andrews committed
39
#include <dns/ssu.h>
40
#include <dns/stats.h>
41
#include <dns/tsig.h>
42
#include <dns/view.h>
43
#include <dns/zone.h>
44

45 46
#include <ns/client.h>

47
#include <named/config.h>
48 49
#include <named/globals.h>
#include <named/log.h>
50
#include <named/server.h>
51
#include <named/zoneconf.h>
52

53 54 55 56
/* ACLs associated with zone */
typedef enum {
	allow_notify,
	allow_query,
Evan Hunt's avatar
Evan Hunt committed
57
	allow_query_on,
58 59 60 61 62
	allow_transfer,
	allow_update,
	allow_update_forwarding
} acl_type_t;

63 64 65 66 67 68
#define RETERR(x) do { \
	isc_result_t _r = (x); \
	if (_r != ISC_R_SUCCESS) \
		return (_r); \
	} while (0)

69 70 71 72 73 74
#define CHECK(x) do { \
	result = (x); \
	if (result != ISC_R_SUCCESS) \
		goto cleanup; \
	} while (0)

75
/*%
76 77 78
 * Convenience function for configuring a single zone ACL.
 */
static isc_result_t
79
configure_zone_acl(const cfg_obj_t *zconfig, const cfg_obj_t *vconfig,
80
		   const cfg_obj_t *config, acl_type_t acltype,
Automatic Updater's avatar
Automatic Updater committed
81
		   cfg_aclconfctx_t *actx, dns_zone_t *zone,
82 83 84 85
		   void (*setzacl)(dns_zone_t *, dns_acl_t *),
		   void (*clearzacl)(dns_zone_t *))
{
	isc_result_t result;
86
	const cfg_obj_t *maps[5] = {NULL, NULL, NULL, NULL, NULL};
87
	const cfg_obj_t *aclobj = NULL;
88
	int i = 0;
89 90
	dns_acl_t **aclp = NULL, *acl = NULL;
	const char *aclname;
Automatic Updater's avatar
Automatic Updater committed
91
	dns_view_t *view;
92

Automatic Updater's avatar
Automatic Updater committed
93
	view = dns_zone_getview(zone);
94 95 96

	switch (acltype) {
	    case allow_notify:
Automatic Updater's avatar
Automatic Updater committed
97 98
		if (view != NULL)
			aclp = &view->notifyacl;
99 100 101
		aclname = "allow-notify";
		break;
	    case allow_query:
Automatic Updater's avatar
Automatic Updater committed
102 103
		if (view != NULL)
			aclp = &view->queryacl;
104 105
		aclname = "allow-query";
		break;
Evan Hunt's avatar
Evan Hunt committed
106 107 108 109 110
	    case allow_query_on:
		if (view != NULL)
			aclp = &view->queryonacl;
		aclname = "allow-query-on";
		break;
111
	    case allow_transfer:
Automatic Updater's avatar
Automatic Updater committed
112 113
		if (view != NULL)
			aclp = &view->transferacl;
114 115 116
		aclname = "allow-transfer";
		break;
	    case allow_update:
Automatic Updater's avatar
Automatic Updater committed
117 118
		if (view != NULL)
			aclp = &view->updateacl;
119 120 121
		aclname = "allow-update";
		break;
	    case allow_update_forwarding:
Automatic Updater's avatar
Automatic Updater committed
122 123
		if (view != NULL)
			aclp = &view->upfwdacl;
124 125
		aclname = "allow-update-forwarding";
		break;
Automatic Updater's avatar
Automatic Updater committed
126 127
	    default:
		INSIST(0);
128
		ISC_UNREACHABLE();
129 130 131 132 133
	}

	/* First check to see if ACL is defined within the zone */
	if (zconfig != NULL) {
		maps[0] = cfg_tuple_get(zconfig, "options");
134
		(void)named_config_get(maps, aclname, &aclobj);
135 136 137 138 139 140 141 142 143 144 145
		if (aclobj != NULL) {
			aclp = NULL;
			goto parse_acl;
		}
	}

	/* Failing that, see if there's a default ACL already in the view */
	if (aclp != NULL && *aclp != NULL) {
		(*setzacl)(zone, *aclp);
		return (ISC_R_SUCCESS);
	}
146

147
	/* Check for default ACLs that haven't been parsed yet */
148 149 150 151 152
	if (vconfig != NULL) {
		const cfg_obj_t *options = cfg_tuple_get(vconfig, "options");
		if (options != NULL)
			maps[i++] = options;
	}
153
	if (config != NULL) {
154
		const cfg_obj_t *options = NULL;
155 156 157
		(void)cfg_map_get(config, "options", &options);
		if (options != NULL)
			maps[i++] = options;
158
	}
159
	maps[i++] = named_g_defaults;
160 161
	maps[i] = NULL;

162
	(void)named_config_get(maps, aclname, &aclobj);
163
	if (aclobj == NULL) {
164 165 166
		(*clearzacl)(zone);
		return (ISC_R_SUCCESS);
	}
167

168
parse_acl:
169
	result = cfg_acl_fromconfig(aclobj, config, named_g_lctx, actx,
170
				    dns_zone_getmctx(zone), 0, &acl);
171 172
	if (result != ISC_R_SUCCESS)
		return (result);
173 174
	(*setzacl)(zone, acl);

Automatic Updater's avatar
Automatic Updater committed
175
	/* Set the view default now */
176 177 178 179
	if (aclp != NULL)
		dns_acl_attach(acl, aclp);

	dns_acl_detach(&acl);
180
	return (ISC_R_SUCCESS);
181 182
}

183
/*%
184
 * Parse the zone update-policy statement.
185
 */
186
static isc_result_t
187
configure_zone_ssutable(const cfg_obj_t *zconfig, dns_zone_t *zone,
188 189
			const char *zname)
{
190 191
	const cfg_obj_t *updatepolicy = NULL;
	const cfg_listelt_t *element, *element2;
192 193
	dns_ssutable_t *table = NULL;
	isc_mem_t *mctx = dns_zone_getmctx(zone);
194
	bool autoddns = false;
195 196 197
	isc_result_t result;

	(void)cfg_map_get(zconfig, "update-policy", &updatepolicy);
198

199
	if (updatepolicy == NULL) {
200
		dns_zone_setssutable(zone, NULL);
201
		return (ISC_R_SUCCESS);
202
	}
203

Automatic Updater's avatar
Automatic Updater committed
204 205
	if (cfg_obj_isstring(updatepolicy) &&
	    strcmp("local", cfg_obj_asstring(updatepolicy)) == 0) {
206
		autoddns = true;
Automatic Updater's avatar
Automatic Updater committed
207 208
		updatepolicy = NULL;
	}
209

210 211 212 213 214 215 216 217
	result = dns_ssutable_create(mctx, &table);
	if (result != ISC_R_SUCCESS)
		return (result);

	for (element = cfg_list_first(updatepolicy);
	     element != NULL;
	     element = cfg_list_next(element))
	{
218 219 220 221 222 223
		const cfg_obj_t *stmt = cfg_listelt_value(element);
		const cfg_obj_t *mode = cfg_tuple_get(stmt, "mode");
		const cfg_obj_t *identity = cfg_tuple_get(stmt, "identity");
		const cfg_obj_t *matchtype = cfg_tuple_get(stmt, "matchtype");
		const cfg_obj_t *dname = cfg_tuple_get(stmt, "name");
		const cfg_obj_t *typelist = cfg_tuple_get(stmt, "types");
224
		const char *str;
225 226
		bool grant = false;
		bool usezone = false;
227
		dns_ssumatchtype_t mtype = dns_ssumatchtype_name;
228 229 230 231 232 233
		dns_fixedname_t fname, fident;
		isc_buffer_t b;
		dns_rdatatype_t *types;
		unsigned int i, n;

		str = cfg_obj_asstring(mode);
234
		if (strcasecmp(str, "grant") == 0) {
235
			grant = true;
236
		} else if (strcasecmp(str, "deny") == 0) {
237
			grant = false;
238
		} else {
239
			INSIST(0);
240 241
			ISC_UNREACHABLE();
		}
242 243

		str = cfg_obj_asstring(matchtype);
244 245
		CHECK(dns_ssu_mtypefromstring(str, &mtype));
		if (mtype == dns_ssumatchtype_subdomain) {
246
			usezone = true;
247
		}
248 249 250

		dns_fixedname_init(&fident);
		str = cfg_obj_asstring(identity);
251
		isc_buffer_constinit(&b, str, strlen(str));
252 253
		isc_buffer_add(&b, strlen(str));
		result = dns_name_fromtext(dns_fixedname_name(&fident), &b,
254
					   dns_rootname, 0, NULL);
255
		if (result != ISC_R_SUCCESS) {
256
			cfg_obj_log(identity, named_g_lctx, ISC_LOG_ERROR,
257 258 259 260 261
				    "'%s' is not a valid name", str);
			goto cleanup;
		}

		dns_fixedname_init(&fname);
262 263 264 265 266
		if (usezone) {
			result = dns_name_copy(dns_zone_getorigin(zone),
					       dns_fixedname_name(&fname),
					       NULL);
			if (result != ISC_R_SUCCESS) {
267 268
				cfg_obj_log(identity, named_g_lctx,
					    ISC_LOG_ERROR,
269 270 271 272 273 274
					    "error copying origin: %s",
					    isc_result_totext(result));
				goto cleanup;
			}
		} else {
			str = cfg_obj_asstring(dname);
275
			isc_buffer_constinit(&b, str, strlen(str));
276 277
			isc_buffer_add(&b, strlen(str));
			result = dns_name_fromtext(dns_fixedname_name(&fname),
278
						   &b, dns_rootname, 0, NULL);
279
			if (result != ISC_R_SUCCESS) {
280 281
				cfg_obj_log(identity, named_g_lctx,
					    ISC_LOG_ERROR,
282 283 284
					    "'%s' is not a valid name", str);
				goto cleanup;
			}
285 286
		}

287
		n = named_config_listcount(typelist);
288 289 290 291 292 293 294 295 296 297 298 299 300 301 302
		if (n == 0)
			types = NULL;
		else {
			types = isc_mem_get(mctx, n * sizeof(dns_rdatatype_t));
			if (types == NULL) {
				result = ISC_R_NOMEMORY;
				goto cleanup;
			}
		}

		i = 0;
		for (element2 = cfg_list_first(typelist);
		     element2 != NULL;
		     element2 = cfg_list_next(element2))
		{
303
			const cfg_obj_t *typeobj;
304 305 306 307 308 309
			isc_textregion_t r;

			INSIST(i < n);

			typeobj = cfg_listelt_value(element2);
			str = cfg_obj_asstring(typeobj);
310
			DE_CONST(str, r.base);
311 312 313 314
			r.length = strlen(str);

			result = dns_rdatatype_fromtext(&types[i++], &r);
			if (result != ISC_R_SUCCESS) {
315 316
				cfg_obj_log(identity, named_g_lctx,
					    ISC_LOG_ERROR,
317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334
					    "'%s' is not a valid type", str);
				isc_mem_put(mctx, types,
					    n * sizeof(dns_rdatatype_t));
				goto cleanup;
			}
		}
		INSIST(i == n);

		result = dns_ssutable_addrule(table, grant,
					      dns_fixedname_name(&fident),
					      mtype,
					      dns_fixedname_name(&fname),
					      n, types);
		if (types != NULL)
			isc_mem_put(mctx, types, n * sizeof(dns_rdatatype_t));
		if (result != ISC_R_SUCCESS) {
			goto cleanup;
		}
335
	}
336

337
	/*
338 339 340
	 * If "update-policy local;" and a session key exists,
	 * then use the default policy, which is equivalent to:
	 * update-policy { grant <session-keyname> zonesub any; };
341 342 343 344
	 */
	if (autoddns) {
		dns_rdatatype_t any = dns_rdatatype_any;

345 346 347
		if (named_g_server->session_keyname == NULL) {
			isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
				      NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR,
348 349 350 351 352 353 354
				      "failed to enable auto DDNS policy "
				      "for zone %s: session key not found",
				      zname);
			result = ISC_R_NOTFOUND;
			goto cleanup;
		}

355
		result = dns_ssutable_addrule(table, true,
356
					      named_g_server->session_keyname,
357
					      dns_ssumatchtype_local,
358 359 360 361 362
					      dns_zone_getorigin(zone),
					      1, &any);

		if (result != ISC_R_SUCCESS)
			goto cleanup;
363
	}
364 365 366 367 368 369 370 371 372

	result = ISC_R_SUCCESS;
	dns_zone_setssutable(zone, table);

 cleanup:
	dns_ssutable_detach(&table);
	return (result);
}

373 374 375 376 377 378 379 380 381 382 383 384 385 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
/*
 * This is the TTL used for internally generated RRsets for static-stub zones.
 * The value doesn't matter because the mapping is static, but needs to be
 * defined for the sake of implementation.
 */
#define STATICSTUB_SERVER_TTL 86400

/*%
 * Configure an apex NS with glues for a static-stub zone.
 * For example, for the zone named "example.com", the following RRs will be
 * added to the zone DB:
 * example.com. NS example.com.
 * example.com. A 192.0.2.1
 * example.com. AAAA 2001:db8::1
 */
static isc_result_t
configure_staticstub_serveraddrs(const cfg_obj_t *zconfig, dns_zone_t *zone,
				 dns_rdatalist_t *rdatalist_ns,
				 dns_rdatalist_t *rdatalist_a,
				 dns_rdatalist_t *rdatalist_aaaa)
{
	const cfg_listelt_t *element;
	isc_mem_t *mctx = dns_zone_getmctx(zone);
	isc_region_t region, sregion;
	dns_rdata_t *rdata;
	isc_result_t result = ISC_R_SUCCESS;

	for (element = cfg_list_first(zconfig);
	     element != NULL;
	     element = cfg_list_next(element))
	{
		const isc_sockaddr_t* sa;
		isc_netaddr_t na;
		const cfg_obj_t *address = cfg_listelt_value(element);
		dns_rdatalist_t *rdatalist;

		sa = cfg_obj_assockaddr(address);
		if (isc_sockaddr_getport(sa) != 0) {
411
			cfg_obj_log(zconfig, named_g_lctx, ISC_LOG_ERROR,
412 413 414 415 416 417
				    "port is not configurable for "
				    "static stub server-addresses");
			return (ISC_R_FAILURE);
		}
		isc_netaddr_fromsockaddr(&na, sa);
		if (isc_netaddr_getzone(&na) != 0) {
418
			cfg_obj_log(zconfig, named_g_lctx, ISC_LOG_ERROR,
419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440
					    "scoped address is not allowed "
					    "for static stub "
					    "server-addresses");
			return (ISC_R_FAILURE);
		}

		switch (na.family) {
		case AF_INET:
			region.length = sizeof(na.type.in);
			rdatalist = rdatalist_a;
			break;
		default:
			INSIST(na.family == AF_INET6);
			region.length = sizeof(na.type.in6);
			rdatalist = rdatalist_aaaa;
			break;
		}

		rdata = isc_mem_get(mctx, sizeof(*rdata) + region.length);
		if (rdata == NULL)
			return (ISC_R_NOMEMORY);
		region.base = (unsigned char *)(rdata + 1);
441
		memmove(region.base, &na.type, region.length);
442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468
		dns_rdata_init(rdata);
		dns_rdata_fromregion(rdata, dns_zone_getclass(zone),
				     rdatalist->type, &region);
		ISC_LIST_APPEND(rdatalist->rdata, rdata, link);
	}

	/*
	 * If no address is specified (unlikely in this context, but possible),
	 * there's nothing to do anymore.
	 */
	if (ISC_LIST_EMPTY(rdatalist_a->rdata) &&
	    ISC_LIST_EMPTY(rdatalist_aaaa->rdata)) {
		return (ISC_R_SUCCESS);
	}

	/* Add to the list an apex NS with the ns name being the origin name */
	dns_name_toregion(dns_zone_getorigin(zone), &sregion);
	rdata = isc_mem_get(mctx, sizeof(*rdata) + sregion.length);
	if (rdata == NULL) {
		/*
		 * Already allocated data will be freed in the caller, so
		 * we can simply return here.
		 */
		return (ISC_R_NOMEMORY);
	}
	region.length = sregion.length;
	region.base = (unsigned char *)(rdata + 1);
469
	memmove(region.base, sregion.base, region.length);
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 505 506
	dns_rdata_init(rdata);
	dns_rdata_fromregion(rdata, dns_zone_getclass(zone),
			     dns_rdatatype_ns, &region);
	ISC_LIST_APPEND(rdatalist_ns->rdata, rdata, link);

	return (result);
}

/*%
 * Configure an apex NS with an out-of-zone NS names for a static-stub zone.
 * For example, for the zone named "example.com", something like the following
 * RRs will be added to the zone DB:
 * example.com. NS ns.example.net.
 */
static isc_result_t
configure_staticstub_servernames(const cfg_obj_t *zconfig, dns_zone_t *zone,
				 dns_rdatalist_t *rdatalist, const char *zname)
{
	const cfg_listelt_t *element;
	isc_mem_t *mctx = dns_zone_getmctx(zone);
	dns_rdata_t *rdata;
	isc_region_t sregion, region;
	isc_result_t result = ISC_R_SUCCESS;

	for (element = cfg_list_first(zconfig);
	     element != NULL;
	     element = cfg_list_next(element))
	{
		const cfg_obj_t *obj;
		const char *str;
		dns_fixedname_t fixed_name;
		dns_name_t *nsname;
		isc_buffer_t b;

		obj = cfg_listelt_value(element);
		str = cfg_obj_asstring(obj);

507
		nsname = dns_fixedname_initname(&fixed_name);
508

509
		isc_buffer_constinit(&b, str, strlen(str));
510 511 512
		isc_buffer_add(&b, strlen(str));
		result = dns_name_fromtext(nsname, &b, dns_rootname, 0, NULL);
		if (result != ISC_R_SUCCESS) {
513
			cfg_obj_log(zconfig, named_g_lctx, ISC_LOG_ERROR,
514 515 516 517 518
					    "server-name '%s' is not a valid "
					    "name", str);
			return (result);
		}
		if (dns_name_issubdomain(nsname, dns_zone_getorigin(zone))) {
519
			cfg_obj_log(zconfig, named_g_lctx, ISC_LOG_ERROR,
520 521 522 523 524 525 526 527 528 529 530 531
				    "server-name '%s' must not be a "
				    "subdomain of zone name '%s'",
				    str, zname);
			return (ISC_R_FAILURE);
		}

		dns_name_toregion(nsname, &sregion);
		rdata = isc_mem_get(mctx, sizeof(*rdata) + sregion.length);
		if (rdata == NULL)
			return (ISC_R_NOMEMORY);
		region.length = sregion.length;
		region.base = (unsigned char *)(rdata + 1);
532
		memmove(region.base, sregion.base, region.length);
533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548
		dns_rdata_init(rdata);
		dns_rdata_fromregion(rdata, dns_zone_getclass(zone),
				     dns_rdatatype_ns, &region);
		ISC_LIST_APPEND(rdatalist->rdata, rdata, link);
	}

	return (result);
}

/*%
 * Configure static-stub zone.
 */
static isc_result_t
configure_staticstub(const cfg_obj_t *zconfig, dns_zone_t *zone,
		     const char *zname, const char *dbtype)
{
Automatic Updater's avatar
Automatic Updater committed
549
	int i = 0;
550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588
	const cfg_obj_t *obj;
	isc_mem_t *mctx = dns_zone_getmctx(zone);
	dns_db_t *db = NULL;
	dns_dbversion_t *dbversion = NULL;
	dns_dbnode_t *apexnode = NULL;
	dns_name_t apexname;
	isc_result_t result;
	dns_rdataset_t rdataset;
	dns_rdatalist_t rdatalist_ns, rdatalist_a, rdatalist_aaaa;
	dns_rdatalist_t* rdatalists[] = {
		&rdatalist_ns, &rdatalist_a, &rdatalist_aaaa, NULL
	};
	dns_rdata_t *rdata;
	isc_region_t region;

	/* Create the DB beforehand */
	RETERR(dns_db_create(mctx, dbtype, dns_zone_getorigin(zone),
			     dns_dbtype_stub, dns_zone_getclass(zone),
			     0, NULL, &db));
	dns_zone_setdb(zone, db);

	dns_rdatalist_init(&rdatalist_ns);
	rdatalist_ns.rdclass = dns_zone_getclass(zone);
	rdatalist_ns.type = dns_rdatatype_ns;
	rdatalist_ns.ttl = STATICSTUB_SERVER_TTL;

	dns_rdatalist_init(&rdatalist_a);
	rdatalist_a.rdclass = dns_zone_getclass(zone);
	rdatalist_a.type = dns_rdatatype_a;
	rdatalist_a.ttl = STATICSTUB_SERVER_TTL;

	dns_rdatalist_init(&rdatalist_aaaa);
	rdatalist_aaaa.rdclass = dns_zone_getclass(zone);
	rdatalist_aaaa.type = dns_rdatatype_aaaa;
	rdatalist_aaaa.ttl = STATICSTUB_SERVER_TTL;

	/* Prepare zone RRs from the configuration */
	obj = NULL;
	result = cfg_map_get(zconfig, "server-addresses", &obj);
589 590
	if (result == ISC_R_SUCCESS) {
		INSIST(obj != NULL);
591 592 593 594 595 596 597 598 599 600
		result = configure_staticstub_serveraddrs(obj, zone,
							  &rdatalist_ns,
							  &rdatalist_a,
							  &rdatalist_aaaa);
		if (result != ISC_R_SUCCESS)
			goto cleanup;
	}

	obj = NULL;
	result = cfg_map_get(zconfig, "server-names", &obj);
601 602
	if (result == ISC_R_SUCCESS) {
		INSIST(obj != NULL);
603 604 605 606 607 608 609 610 611 612 613 614
		result = configure_staticstub_servernames(obj, zone,
							  &rdatalist_ns,
							  zname);
		if (result != ISC_R_SUCCESS)
			goto cleanup;
	}

	/*
	 * Sanity check: there should be at least one NS RR at the zone apex
	 * to trigger delegation.
	 */
	if (ISC_LIST_EMPTY(rdatalist_ns.rdata)) {
615 616
		isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
			      NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR,
617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632
			      "No NS record is configured for a "
			      "static-stub zone '%s'", zname);
		result = ISC_R_FAILURE;
		goto cleanup;
	}

	/*
	 * Now add NS and glue A/AAAA RRsets to the zone DB.
	 * First open a new version for the add operation and get a pointer
	 * to the apex node (all RRs are of the apex name).
	 */
	result = dns_db_newversion(db, &dbversion);
	if (result != ISC_R_SUCCESS)
		goto cleanup;
	dns_name_init(&apexname, NULL);
	dns_name_clone(dns_zone_getorigin(zone), &apexname);
633
	result = dns_db_findnode(db, &apexname, false, &apexnode);
634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675
	if (result != ISC_R_SUCCESS)
		goto cleanup;

	/* Add NS RRset */
	dns_rdataset_init(&rdataset);
	RUNTIME_CHECK(dns_rdatalist_tordataset(&rdatalist_ns, &rdataset)
		      == ISC_R_SUCCESS);
	result = dns_db_addrdataset(db, apexnode, dbversion, 0, &rdataset,
				    0, NULL);
	dns_rdataset_disassociate(&rdataset);
	if (result != ISC_R_SUCCESS)
		goto cleanup;

	/* Add glue A RRset, if any */
	if (!ISC_LIST_EMPTY(rdatalist_a.rdata)) {
		RUNTIME_CHECK(dns_rdatalist_tordataset(&rdatalist_a, &rdataset)
			      == ISC_R_SUCCESS);
		result = dns_db_addrdataset(db, apexnode, dbversion, 0,
					    &rdataset, 0, NULL);
		dns_rdataset_disassociate(&rdataset);
		if (result != ISC_R_SUCCESS)
			goto cleanup;
	}

	/* Add glue AAAA RRset, if any */
	if (!ISC_LIST_EMPTY(rdatalist_aaaa.rdata)) {
		RUNTIME_CHECK(dns_rdatalist_tordataset(&rdatalist_aaaa,
						       &rdataset)
			      == ISC_R_SUCCESS);
		result = dns_db_addrdataset(db, apexnode, dbversion, 0,
					    &rdataset, 0, NULL);
		dns_rdataset_disassociate(&rdataset);
		if (result != ISC_R_SUCCESS)
			goto cleanup;
	}

	result = ISC_R_SUCCESS;

  cleanup:
	if (apexnode != NULL)
		dns_db_detachnode(db, &apexnode);
	if (dbversion != NULL)
676
		dns_db_closeversion(db, &dbversion, true);
677 678 679 680 681 682 683 684 685 686 687
	if (db != NULL)
		dns_db_detach(&db);
	for (i = 0; rdatalists[i] != NULL; i++) {
		while ((rdata = ISC_LIST_HEAD(rdatalists[i]->rdata)) != NULL) {
			ISC_LIST_UNLINK(rdatalists[i]->rdata, rdata, link);
			dns_rdata_toregion(rdata, &region);
			isc_mem_put(mctx, rdata,
				    sizeof(*rdata) + region.length);
		}
	}

688 689
	INSIST(dbversion == NULL);

690 691 692
	return (result);
}

693
/*%
694 695 696
 * Convert a config file zone type into a server zone type.
 */
static inline dns_zonetype_t
697 698
zonetype_fromconfig(const cfg_obj_t *map) {
	const cfg_obj_t *obj = NULL;
699 700 701
	isc_result_t result;

	result = cfg_map_get(map, "type", &obj);
702
	INSIST(result == ISC_R_SUCCESS && obj != NULL);
703
	return (named_config_getzonetype(obj));
704 705
}

706
/*%
707 708 709
 * Helper function for strtoargv().  Pardon the gratuitous recursion.
 */
static isc_result_t
Andreas Gustafsson's avatar
Andreas Gustafsson committed
710 711 712
strtoargvsub(isc_mem_t *mctx, char *s, unsigned int *argcp,
	     char ***argvp, unsigned int n)
{
713
	isc_result_t result;
Automatic Updater's avatar
Automatic Updater committed
714

715 716 717
	/* Discard leading whitespace. */
	while (*s == ' ' || *s == '\t')
		s++;
Automatic Updater's avatar
Automatic Updater committed
718

719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739
	if (*s == '\0') {
		/* We have reached the end of the string. */
		*argcp = n;
		*argvp = isc_mem_get(mctx, n * sizeof(char *));
		if (*argvp == NULL)
			return (ISC_R_NOMEMORY);
	} else {
		char *p = s;
		while (*p != ' ' && *p != '\t' && *p != '\0')
			p++;
		if (*p != '\0')
			*p++ = '\0';

		result = strtoargvsub(mctx, p, argcp, argvp, n + 1);
		if (result != ISC_R_SUCCESS)
			return (result);
		(*argvp)[n] = s;
	}
	return (ISC_R_SUCCESS);
}

740
/*%
741 742 743 744 745 746 747
 * Tokenize the string "s" into whitespace-separated words,
 * return the number of words in '*argcp' and an array
 * of pointers to the words in '*argvp'.  The caller
 * must free the array using isc_mem_put().  The string
 * is modified in-place.
 */
static isc_result_t
Andreas Gustafsson's avatar
Andreas Gustafsson committed
748
strtoargv(isc_mem_t *mctx, char *s, unsigned int *argcp, char ***argvp) {
749 750 751
	return (strtoargvsub(mctx, s, argcp, argvp, 0));
}

752
static void
753 754 755
checknames(dns_zonetype_t ztype, const cfg_obj_t **maps,
	   const cfg_obj_t **objp)
{
756
	const char *zone = NULL;
757
	isc_result_t result;
758 759

	switch (ztype) {
760 761 762 763 764 765 766
	case dns_zone_slave:
	case dns_zone_mirror:
		zone = "slave";
		break;
	case dns_zone_master:
		zone = "master";
		break;
767 768
	default:
		INSIST(0);
769
		ISC_UNREACHABLE();
770
	}
771
	result = named_checknames_get(maps, zone, objp);
772
	INSIST(result == ISC_R_SUCCESS && objp != NULL && *objp != NULL);
773 774
}

775 776 777 778 779 780 781 782
/*
 * Callback to see if a non-recursive query coming from 'srcaddr' to
 * 'destaddr', with optional key 'mykey' for class 'rdclass' would be
 * delivered to 'myview'.
 *
 * We run this unlocked as both the view list and the interface list
 * are updated when the appropriate task has exclusivity.
 */
783
static bool
784 785 786 787 788 789 790 791 792 793 794 795 796
isself(dns_view_t *myview, dns_tsigkey_t *mykey,
       const isc_sockaddr_t *srcaddr, const isc_sockaddr_t *dstaddr,
       dns_rdataclass_t rdclass, void *arg)
{
	ns_interfacemgr_t *interfacemgr = (ns_interfacemgr_t *) arg;
	dns_aclenv_t *env = ns_interfacemgr_getaclenv(interfacemgr);
	dns_view_t *view;
	dns_tsigkey_t *key = NULL;
	dns_name_t *tsig = NULL;
	isc_netaddr_t netsrc;
	isc_netaddr_t netdst;

	if (interfacemgr == NULL)
797
		return (true);
798 799

	if (!ns_interfacemgr_listeningon(interfacemgr, dstaddr))
800
		return (false);
801 802 803 804 805 806 807 808 809 810 811 812 813 814 815

	isc_netaddr_fromsockaddr(&netsrc, srcaddr);
	isc_netaddr_fromsockaddr(&netdst, dstaddr);

	for (view = ISC_LIST_HEAD(named_g_server->viewlist);
	     view != NULL;
	     view = ISC_LIST_NEXT(view, link)) {

		if (view->matchrecursiveonly)
			continue;

		if (rdclass != view->rdclass)
			continue;

		if (mykey != NULL) {
816
			bool match;
817 818 819 820 821 822 823 824 825 826 827 828
			isc_result_t result;

			result = dns_view_gettsig(view, &mykey->name, &key);
			if (result != ISC_R_SUCCESS)
				continue;
			match = dst_key_compare(mykey->key, key->key);
			dns_tsigkey_detach(&key);
			if (!match)
				continue;
			tsig = dns_tsigkey_identity(mykey);
		}

829 830 831 832
		if (dns_acl_allowed(&netsrc, tsig, view->matchclients, env) &&
		    dns_acl_allowed(&netdst, tsig, view->matchdestinations,
				    env))
		{
833
			break;
834
		}
835
	}
836
	return (view == myview);
837 838
}

839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869
/*%
 * For mirror zones, change "notify yes;" to "notify explicit;", informing the
 * user only if "notify" was explicitly configured rather than inherited from
 * default configuration.
 */
static dns_notifytype_t
process_notifytype(dns_notifytype_t ntype, dns_zonetype_t ztype,
		   const char *zname, const cfg_obj_t **maps)
{
	const cfg_obj_t *obj = NULL;

	/*
	 * Return the original setting if this is not a mirror zone or if the
	 * zone is configured with something else than "notify yes;".
	 */
	if (ztype != dns_zone_mirror || ntype != dns_notifytype_yes) {
		return (ntype);
	}

	/*
	 * Only log a message if "notify" was set in the configuration
	 * hierarchy supplied in 'maps'.
	 */
	if (named_config_get(maps, "notify", &obj) == ISC_R_SUCCESS) {
		cfg_obj_log(obj, named_g_lctx, ISC_LOG_INFO,
			    "'notify explicit;' will be used for mirror zone "
			    "'%s'", zname);
	}

	return (dns_notifytype_explicit);
}
870

871
isc_result_t
872
named_zone_configure(const cfg_obj_t *config, const cfg_obj_t *vconfig,
873
		  const cfg_obj_t *zconfig, cfg_aclconfctx_t *ac,
874
		  dns_zone_t *zone, dns_zone_t *raw)
875 876
{
	isc_result_t result;
877
	const char *zname;
878
	dns_rdataclass_t zclass;
879
	dns_rdataclass_t vclass;
880
	const cfg_obj_t *maps[5];
881
	const cfg_obj_t *nodefault[4];
882 883 884
	const cfg_obj_t *zoptions = NULL;
	const cfg_obj_t *options = NULL;
	const cfg_obj_t *obj;
885
	const char *filename = NULL;
886
	const char *dupcheck;
887
	dns_notifytype_t notifytype = dns_notifytype_yes;
888
	uint32_t count;
889 890 891
	unsigned int dbargc;
	char **dbargv;
	static char default_dbtype[] = "rbt";
Evan Hunt's avatar
Evan Hunt committed
892 893
	static char dlz_dbtype[] = "dlz";
	char *cpval = default_dbtype;
894
	isc_mem_t *mctx = dns_zone_getmctx(zone);
895
	dns_dialuptype_t dialup = dns_dialuptype_no;
896 897
	dns_zonetype_t ztype;
	int i;
898
	int32_t journal_size;
899 900
	bool multi;
	bool alt;
901
	dns_view_t *view;
902 903 904
	bool check = false, fail = false;
	bool warn = false, ignore = false;
	bool ixfrdiff;
905
	dns_masterformat_t masterformat;
Evan Hunt's avatar
Evan Hunt committed
906
	const dns_master_style_t *masterstyle = &dns_master_style_default;
907
	isc_stats_t *zoneqrystats;
908
	dns_stats_t *rcvquerystats;
909
	dns_zonestat_level_t statlevel = dns_zonestat_none;
910
	int seconds;
911
	dns_zone_t *mayberaw = (raw != NULL) ? raw : zone;
Evan Hunt's avatar
Evan Hunt committed
912
	isc_dscp_t dscp;
Michael Graff's avatar
Michael Graff committed
913

914 915 916
	i = 0;
	if (zconfig != NULL) {
		zoptions = cfg_tuple_get(zconfig, "options");
917 918 919 920 921 922
		nodefault[i] = maps[i] = zoptions;
		i++;
	}
	if (vconfig != NULL) {
		nodefault[i] = maps[i] = cfg_tuple_get(vconfig, "options");
		i++;
923 924 925
	}
	if (config != NULL) {
		(void)cfg_map_get(config, "options", &options);
926 927 928 929
		if (options != NULL) {
			nodefault[i] = maps[i] = options;
			i++;
		}
930
	}
931
	nodefault[i] = NULL;
932
	maps[i++] = named_g_defaults;
933
	maps[i] = NULL;
934 935

	if (vconfig != NULL)
936
		RETERR(named_config_getclass(cfg_tuple_get(vconfig, "class"),
937 938 939 940
					  dns_rdataclass_in, &vclass));
	else
		vclass = dns_rdataclass_in;

941 942 943
	/*
	 * Configure values common to all zone types.
	 */
944

945
	zname = cfg_obj_asstring(cfg_tuple_get(zconfig, "name"));
946

947
	RETERR(named_config_getclass(cfg_tuple_get(zconfig, "class"),
948
				  vclass, &zclass));
949
	dns_zone_setclass(zone, zclass);
950 951
	if (raw != NULL)
		dns_zone_setclass(raw, zclass);
952

953
	ztype = zonetype_fromconfig(zoptions);
954 955 956 957 958
	if (raw != NULL) {
		dns_zone_settype(raw, ztype);
		dns_zone_settype(zone, dns_zone_master);
	} else
		dns_zone_settype(zone, ztype);
Automatic Updater's avatar
Automatic Updater committed
959

960 961 962
	obj = NULL;
	result = cfg_map_get(zoptions, "database", &obj);
	if (result == ISC_R_SUCCESS)
963 964 965 966
		cpval = isc_mem_strdup(mctx, cfg_obj_asstring(obj));
	if (cpval == NULL)
		return(ISC_R_NOMEMORY);

Evan Hunt's avatar
Evan Hunt committed
967 968 969 970 971 972 973
	obj = NULL;
	result = cfg_map_get(zoptions, "dlz", &obj);
	if (result == ISC_R_SUCCESS) {
		const char *dlzname = cfg_obj_asstring(obj);
		size_t len;

		if (cpval != default_dbtype) {
974 975
		       isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
				     NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR,
Evan Hunt's avatar
Evan Hunt committed
976 977 978 979 980 981 982
				     "zone '%s': both 'database' and 'dlz' "
				     "specified", zname);
		       return (ISC_R_FAILURE);
		}

		len = strlen(dlzname) + 5;
		cpval = isc_mem_allocate(mctx, len);
983 984
		if (cpval == NULL)
			return (ISC_R_NOMEMORY);
Evan Hunt's avatar
Evan Hunt committed
985 986 987
		snprintf(cpval, len, "dlz %s", dlzname);
	}

988 989 990 991 992 993
	result = strtoargv(mctx, cpval, &dbargc, &dbargv);
	if (result != ISC_R_SUCCESS && cpval != default_dbtype) {
		isc_mem_free(mctx, cpval);
		return (result);
	}

994 995 996 997 998
	/*
	 * ANSI C is strange here.  There is no logical reason why (char **)
	 * cannot be promoted automatically to (const char * const *) by the
	 * compiler w/o generating a warning.
	 */
999
	result = dns_zone_setdbtype(zone, dbargc, (const char * const *)dbargv);
1000
	isc_mem_put(mctx, dbargv, dbargc * sizeof(*dbargv));
Evan Hunt's avatar
Evan Hunt committed
1001
	if (cpval != default_dbtype && cpval != dlz_dbtype)
1002 1003 1004
		isc_mem_free(mctx, cpval);
	if (result != ISC_R_SUCCESS)
		return (result);
1005

1006 1007 1008 1009
	obj = NULL;
	result = cfg_map_get(zoptions, "file", &obj);
	if (result == ISC_R_SUCCESS)
		filename = cfg_obj_asstring(obj);
1010

1011 1012 1013 1014
	/*
	 * Unless we're using some alternative database, a master zone
	 * will be needing a master file.
	 */
1015 1016
	if (ztype == dns_zone_master && cpval == default_dbtype &&
	    filename == NULL) {
1017 1018
		isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
			      NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR,
1019 1020 1021
			      "zone '%s': 'file' not specified",
			      zname);
		return (ISC_R_FAILURE);
1022 1023
	}

1024
	if (ztype == dns_zone_slave || ztype == dns_zone_mirror)
1025 1026 1027
		masterformat = dns_masterformat_raw;
	else
		masterformat = dns_masterformat_text;
1028
	obj = NULL;
1029
	result = named_config_get(maps, "masterfile-format", &obj);
1030
	if (result == ISC_R_SUCCESS) {
1031