zoneconf.c 57.4 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
#include <inttypes.h>
13
#include <stdbool.h>
14

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

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

44 45
#include <ns/client.h>

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

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

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

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

76
/*%
77 78 79
 * Convenience function for configuring a single zone ACL.
 */
static isc_result_t
80
configure_zone_acl(const cfg_obj_t *zconfig, const cfg_obj_t *vconfig,
81
		   const cfg_obj_t *config, acl_type_t acltype,
Automatic Updater's avatar
Automatic Updater committed
82
		   cfg_aclconfctx_t *actx, dns_zone_t *zone,
83
		   void (*setzacl)(dns_zone_t *, dns_acl_t *),
84 85
		   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 89 90 91
	int i = 0;
	dns_acl_t **aclp = NULL, *acl = NULL;
	const char *aclname;
	dns_view_t *view;
92

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

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

	/* First check to see if ACL is defined within the zone */
	if (zconfig != NULL) {
		maps[0] = cfg_tuple_get(zconfig, "options");
140
		(void)named_config_get(maps, aclname, &aclobj);
141 142 143 144 145 146 147 148 149 150 151
		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);
	}
152

153
	/* Check for default ACLs that haven't been parsed yet */
154 155
	if (vconfig != NULL) {
		const cfg_obj_t *options = cfg_tuple_get(vconfig, "options");
156
		if (options != NULL) {
157
			maps[i++] = options;
158
		}
159
	}
160
	if (config != NULL) {
161
		const cfg_obj_t *options = NULL;
162
		(void)cfg_map_get(config, "options", &options);
163
		if (options != NULL) {
164
			maps[i++] = options;
165
		}
166
	}
167
	maps[i++] = named_g_defaults;
168 169
	maps[i] = NULL;

170
	(void)named_config_get(maps, aclname, &aclobj);
171
	if (aclobj == NULL) {
172 173 174
		(*clearzacl)(zone);
		return (ISC_R_SUCCESS);
	}
175

176
parse_acl:
177
	result = cfg_acl_fromconfig(aclobj, config, named_g_lctx, actx,
178
				    dns_zone_getmctx(zone), 0, &acl);
179
	if (result != ISC_R_SUCCESS) {
180
		return (result);
181
	}
182 183
	(*setzacl)(zone, acl);

Automatic Updater's avatar
Automatic Updater committed
184
	/* Set the view default now */
185
	if (aclp != NULL) {
186
		dns_acl_attach(acl, aclp);
187
	}
188 189

	dns_acl_detach(&acl);
190
	return (ISC_R_SUCCESS);
191 192
}

193
/*%
194
 * Parse the zone update-policy statement.
195
 */
196
static isc_result_t
197
configure_zone_ssutable(const cfg_obj_t *zconfig, dns_zone_t *zone,
198 199
			const char *zname) {
	const cfg_obj_t *updatepolicy = NULL;
200
	const cfg_listelt_t *element, *element2;
201 202 203 204
	dns_ssutable_t *table = NULL;
	isc_mem_t *mctx = dns_zone_getmctx(zone);
	bool autoddns = false;
	isc_result_t result;
205 206

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

208
	if (updatepolicy == NULL) {
209
		dns_zone_setssutable(zone, NULL);
210
		return (ISC_R_SUCCESS);
211
	}
212

Automatic Updater's avatar
Automatic Updater committed
213
	if (cfg_obj_isstring(updatepolicy) &&
214 215
	    strcmp("local", cfg_obj_asstring(updatepolicy)) == 0)
	{
216
		autoddns = true;
Automatic Updater's avatar
Automatic Updater committed
217 218
		updatepolicy = NULL;
	}
219

220
	result = dns_ssutable_create(mctx, &table);
221
	if (result != ISC_R_SUCCESS) {
222
		return (result);
223
	}
224

225
	for (element = cfg_list_first(updatepolicy); element != NULL;
226 227 228 229 230 231 232 233 234 235 236
	     element = cfg_list_next(element))
	{
		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");
		const char *str;
		bool grant = false;
		bool usezone = false;
237
		dns_ssumatchtype_t mtype = dns_ssumatchtype_name;
238 239 240 241
		dns_fixedname_t fname, fident;
		isc_buffer_t b;
		dns_rdatatype_t *types;
		unsigned int i, n;
242 243

		str = cfg_obj_asstring(mode);
244
		if (strcasecmp(str, "grant") == 0) {
245
			grant = true;
246
		} else if (strcasecmp(str, "deny") == 0) {
247
			grant = false;
248
		} else {
249
			INSIST(0);
250 251
			ISC_UNREACHABLE();
		}
252 253

		str = cfg_obj_asstring(matchtype);
254
		CHECK(dns_ssu_mtypefromstring(str, &mtype));
255 256
		if (mtype == dns_ssumatchtype_subdomain &&
		    strcasecmp(str, "zonesub") == 0) {
257
			usezone = true;
258
		}
259 260 261

		dns_fixedname_init(&fident);
		str = cfg_obj_asstring(identity);
262
		isc_buffer_constinit(&b, str, strlen(str));
263 264
		isc_buffer_add(&b, strlen(str));
		result = dns_name_fromtext(dns_fixedname_name(&fident), &b,
265
					   dns_rootname, 0, NULL);
266
		if (result != ISC_R_SUCCESS) {
267
			cfg_obj_log(identity, named_g_lctx, ISC_LOG_ERROR,
268 269 270 271 272
				    "'%s' is not a valid name", str);
			goto cleanup;
		}

		dns_fixedname_init(&fname);
273
		if (usezone) {
274
			dns_name_copynf(dns_zone_getorigin(zone),
275
					dns_fixedname_name(&fname));
276 277
		} else {
			str = cfg_obj_asstring(dname);
278
			isc_buffer_constinit(&b, str, strlen(str));
279 280
			isc_buffer_add(&b, strlen(str));
			result = dns_name_fromtext(dns_fixedname_name(&fname),
281
						   &b, dns_rootname, 0, NULL);
282
			if (result != ISC_R_SUCCESS) {
283 284
				cfg_obj_log(identity, named_g_lctx,
					    ISC_LOG_ERROR,
285 286 287
					    "'%s' is not a valid name", str);
				goto cleanup;
			}
288 289
		}

290
		n = named_config_listcount(typelist);
291
		if (n == 0) {
292
			types = NULL;
293
		} else {
294 295 296 297
			types = isc_mem_get(mctx, n * sizeof(dns_rdatatype_t));
		}

		i = 0;
298
		for (element2 = cfg_list_first(typelist); element2 != NULL;
299 300
		     element2 = cfg_list_next(element2))
		{
301
			const cfg_obj_t *typeobj;
302 303 304 305 306 307
			isc_textregion_t r;

			INSIST(i < n);

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

			result = dns_rdatatype_fromtext(&types[i++], &r);
			if (result != ISC_R_SUCCESS) {
313 314
				cfg_obj_log(identity, named_g_lctx,
					    ISC_LOG_ERROR,
315 316 317 318 319 320 321 322
					    "'%s' is not a valid type", str);
				isc_mem_put(mctx, types,
					    n * sizeof(dns_rdatatype_t));
				goto cleanup;
			}
		}
		INSIST(i == n);

323 324 325
		result = dns_ssutable_addrule(
			table, grant, dns_fixedname_name(&fident), mtype,
			dns_fixedname_name(&fname), n, types);
326
		if (types != NULL) {
327
			isc_mem_put(mctx, types, n * sizeof(dns_rdatatype_t));
328
		}
329 330 331
		if (result != ISC_R_SUCCESS) {
			goto cleanup;
		}
332
	}
333

334
	/*
335 336 337
	 * 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; };
338 339 340 341
	 */
	if (autoddns) {
		dns_rdatatype_t any = dns_rdatatype_any;

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

352 353 354 355
		result = dns_ssutable_addrule(
			table, true, named_g_server->session_keyname,
			dns_ssumatchtype_local, dns_zone_getorigin(zone), 1,
			&any);
356

357
		if (result != ISC_R_SUCCESS) {
358
			goto cleanup;
359
		}
360
	}
361 362 363 364

	result = ISC_R_SUCCESS;
	dns_zone_setssutable(zone, table);

365
cleanup:
366 367 368 369
	dns_ssutable_detach(&table);
	return (result);
}

370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388
/*
 * 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,
389
				 dns_rdatalist_t *rdatalist_aaaa) {
390
	const cfg_listelt_t *element;
391 392 393 394
	isc_mem_t *mctx = dns_zone_getmctx(zone);
	isc_region_t region, sregion;
	dns_rdata_t *rdata;
	isc_result_t result = ISC_R_SUCCESS;
395 396

	for (element = cfg_list_first(zconfig); element != NULL;
397 398
	     element = cfg_list_next(element))
	{
399
		const isc_sockaddr_t *sa;
400 401 402
		isc_netaddr_t na;
		const cfg_obj_t *address = cfg_listelt_value(element);
		dns_rdatalist_t *rdatalist;
403 404 405

		sa = cfg_obj_assockaddr(address);
		if (isc_sockaddr_getport(sa) != 0) {
406
			cfg_obj_log(zconfig, named_g_lctx, ISC_LOG_ERROR,
407 408 409 410 411 412
				    "port is not configurable for "
				    "static stub server-addresses");
			return (ISC_R_FAILURE);
		}
		isc_netaddr_fromsockaddr(&na, sa);
		if (isc_netaddr_getzone(&na) != 0) {
413
			cfg_obj_log(zconfig, named_g_lctx, ISC_LOG_ERROR,
414 415 416
				    "scoped address is not allowed "
				    "for static stub "
				    "server-addresses");
417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433
			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);
		region.base = (unsigned char *)(rdata + 1);
434
		memmove(region.base, &na.type, region.length);
435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454
		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);
	region.length = sregion.length;
	region.base = (unsigned char *)(rdata + 1);
455
	memmove(region.base, sregion.base, region.length);
456
	dns_rdata_init(rdata);
457 458
	dns_rdata_fromregion(rdata, dns_zone_getclass(zone), dns_rdatatype_ns,
			     &region);
459 460 461 462 463 464 465 466 467 468 469 470 471
	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,
472 473
				 dns_rdatalist_t *rdatalist,
				 const char *zname) {
474
	const cfg_listelt_t *element;
475 476 477 478
	isc_mem_t *mctx = dns_zone_getmctx(zone);
	dns_rdata_t *rdata;
	isc_region_t sregion, region;
	isc_result_t result = ISC_R_SUCCESS;
479 480

	for (element = cfg_list_first(zconfig); element != NULL;
481 482
	     element = cfg_list_next(element))
	{
483
		const cfg_obj_t *obj;
484 485 486 487
		const char *str;
		dns_fixedname_t fixed_name;
		dns_name_t *nsname;
		isc_buffer_t b;
488 489 490 491

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

492
		nsname = dns_fixedname_initname(&fixed_name);
493

494
		isc_buffer_constinit(&b, str, strlen(str));
495 496 497
		isc_buffer_add(&b, strlen(str));
		result = dns_name_fromtext(nsname, &b, dns_rootname, 0, NULL);
		if (result != ISC_R_SUCCESS) {
498
			cfg_obj_log(zconfig, named_g_lctx, ISC_LOG_ERROR,
499 500 501
				    "server-name '%s' is not a valid "
				    "name",
				    str);
502 503 504
			return (result);
		}
		if (dns_name_issubdomain(nsname, dns_zone_getorigin(zone))) {
505
			cfg_obj_log(zconfig, named_g_lctx, ISC_LOG_ERROR,
506 507 508 509 510 511 512 513 514 515
				    "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);
		region.length = sregion.length;
		region.base = (unsigned char *)(rdata + 1);
516
		memmove(region.base, sregion.base, region.length);
517 518 519 520 521 522 523 524 525 526 527 528 529 530
		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,
531 532
		     const char *zname, const char *dbtype) {
	int i = 0;
533
	const cfg_obj_t *obj;
534 535
	isc_mem_t *mctx = dns_zone_getmctx(zone);
	dns_db_t *db = NULL;
536
	dns_dbversion_t *dbversion = NULL;
537 538 539 540 541
	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;
542 543
	dns_rdatalist_t *rdatalists[] = { &rdatalist_ns, &rdatalist_a,
					  &rdatalist_aaaa, NULL };
544 545
	dns_rdata_t *rdata;
	isc_region_t region;
546 547 548

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

	dns_rdataset_init(&rdataset);
553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571

	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);
572 573
	if (result == ISC_R_SUCCESS) {
		INSIST(obj != NULL);
574
		CHECK(configure_staticstub_serveraddrs(obj, zone, &rdatalist_ns,
575 576
						       &rdatalist_a,
						       &rdatalist_aaaa));
577 578 579 580
	}

	obj = NULL;
	result = cfg_map_get(zconfig, "server-names", &obj);
581 582
	if (result == ISC_R_SUCCESS) {
		INSIST(obj != NULL);
583
		CHECK(configure_staticstub_servernames(obj, zone, &rdatalist_ns,
584
						       zname));
585 586 587 588 589 590 591
	}

	/*
	 * Sanity check: there should be at least one NS RR at the zone apex
	 * to trigger delegation.
	 */
	if (ISC_LIST_EMPTY(rdatalist_ns.rdata)) {
592 593
		isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
			      NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR,
594
			      "No NS record is configured for a "
595 596
			      "static-stub zone '%s'",
			      zname);
597 598 599 600 601 602 603 604 605
		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).
	 */
606 607
	CHECK(dns_db_newversion(db, &dbversion));

608 609
	dns_name_init(&apexname, NULL);
	dns_name_clone(dns_zone_getorigin(zone), &apexname);
610
	CHECK(dns_db_findnode(db, &apexname, false, &apexnode));
611 612

	/* Add NS RRset */
613 614 615 616
	RUNTIME_CHECK(dns_rdatalist_tordataset(&rdatalist_ns, &rdataset) ==
		      ISC_R_SUCCESS);
	CHECK(dns_db_addrdataset(db, apexnode, dbversion, 0, &rdataset, 0,
				 NULL));
617 618 619 620
	dns_rdataset_disassociate(&rdataset);

	/* Add glue A RRset, if any */
	if (!ISC_LIST_EMPTY(rdatalist_a.rdata)) {
621 622 623 624 625
		RUNTIME_CHECK(
			dns_rdatalist_tordataset(&rdatalist_a, &rdataset) ==
			ISC_R_SUCCESS);
		CHECK(dns_db_addrdataset(db, apexnode, dbversion, 0, &rdataset,
					 0, NULL));
626 627 628 629 630
		dns_rdataset_disassociate(&rdataset);
	}

	/* Add glue AAAA RRset, if any */
	if (!ISC_LIST_EMPTY(rdatalist_aaaa.rdata)) {
631 632 633 634 635
		RUNTIME_CHECK(
			dns_rdatalist_tordataset(&rdatalist_aaaa, &rdataset) ==
			ISC_R_SUCCESS);
		CHECK(dns_db_addrdataset(db, apexnode, dbversion, 0, &rdataset,
					 0, NULL));
636 637 638
		dns_rdataset_disassociate(&rdataset);
	}

639 640 641
	dns_db_closeversion(db, &dbversion, true);
	dns_zone_setdb(zone, db);

642 643
	result = ISC_R_SUCCESS;

644
cleanup:
645 646 647 648
	if (dns_rdataset_isassociated(&rdataset)) {
		dns_rdataset_disassociate(&rdataset);
	}
	if (apexnode != NULL) {
649
		dns_db_detachnode(db, &apexnode);
650 651 652 653 654
	}
	if (dbversion != NULL) {
		dns_db_closeversion(db, &dbversion, false);
	}
	if (db != NULL) {
655
		dns_db_detach(&db);
656
	}
657 658 659 660 661 662 663 664 665
	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);
		}
	}

666 667
	INSIST(dbversion == NULL);

668 669 670
	return (result);
}

671
/*%
672 673 674
 * Convert a config file zone type into a server zone type.
 */
static inline dns_zonetype_t
675
zonetype_fromconfig(const cfg_obj_t *map) {
676
	const cfg_obj_t *obj = NULL;
677
	isc_result_t result;
678 679

	result = cfg_map_get(map, "type", &obj);
680
	INSIST(result == ISC_R_SUCCESS && obj != NULL);
681
	return (named_config_getzonetype(obj));
682 683
}

684
/*%
685 686 687
 * Helper function for strtoargv().  Pardon the gratuitous recursion.
 */
static isc_result_t
688
strtoargvsub(isc_mem_t *mctx, char *s, unsigned int *argcp, char ***argvp,
689
	     unsigned int n) {
690
	isc_result_t result;
Automatic Updater's avatar
Automatic Updater committed
691

692
	/* Discard leading whitespace. */
693
	while (*s == ' ' || *s == '\t') {
694
		s++;
695
	}
Automatic Updater's avatar
Automatic Updater committed
696

697 698 699 700 701 702
	if (*s == '\0') {
		/* We have reached the end of the string. */
		*argcp = n;
		*argvp = isc_mem_get(mctx, n * sizeof(char *));
	} else {
		char *p = s;
703
		while (*p != ' ' && *p != '\t' && *p != '\0') {
704
			p++;
705 706
		}
		if (*p != '\0') {
707
			*p++ = '\0';
708
		}
709 710

		result = strtoargvsub(mctx, p, argcp, argvp, n + 1);
711
		if (result != ISC_R_SUCCESS) {
712
			return (result);
713
		}
714 715 716 717 718
		(*argvp)[n] = s;
	}
	return (ISC_R_SUCCESS);
}

719
/*%
720 721 722 723 724 725 726
 * 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
727
strtoargv(isc_mem_t *mctx, char *s, unsigned int *argcp, char ***argvp) {
728 729 730
	return (strtoargvsub(mctx, s, argcp, argvp, 0));
}

731
static void
732 733
checknames(dns_zonetype_t ztype, const cfg_obj_t **maps,
	   const cfg_obj_t **objp) {
734
	isc_result_t result;
735 736

	switch (ztype) {
737 738
	case dns_zone_slave:
	case dns_zone_mirror:
739 740 741 742
		result = named_checknames_get(maps, "secondary", objp);
		if (result != ISC_R_SUCCESS) {
			result = named_checknames_get(maps, "slave", objp);
		}
743 744
		break;
	case dns_zone_master:
745 746 747 748
		result = named_checknames_get(maps, "primary", objp);
		if (result != ISC_R_SUCCESS) {
			result = named_checknames_get(maps, "master", objp);
		}
749
		break;
750 751
	default:
		INSIST(0);
752
		ISC_UNREACHABLE();
753
	}
754

755
	INSIST(result == ISC_R_SUCCESS && objp != NULL && *objp != NULL);
756 757
}

758 759 760 761 762 763 764 765
/*
 * 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.
 */
766
static bool
767
isself(dns_view_t *myview, dns_tsigkey_t *mykey, const isc_sockaddr_t *srcaddr,
768
       const isc_sockaddr_t *dstaddr, dns_rdataclass_t rdclass, void *arg) {
769
	ns_interfacemgr_t *interfacemgr = (ns_interfacemgr_t *)arg;
770 771 772 773 774
	dns_aclenv_t *env = ns_interfacemgr_getaclenv(interfacemgr);
	dns_view_t *view;
	dns_tsigkey_t *key = NULL;
	isc_netaddr_t netsrc;
	isc_netaddr_t netdst;
775

776
	if (interfacemgr == NULL) {
777
		return (true);
778
	}
779

780
	if (!ns_interfacemgr_listeningon(interfacemgr, dstaddr)) {
781
		return (false);
782
	}
783 784 785 786

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

787
	for (view = ISC_LIST_HEAD(named_g_server->viewlist); view != NULL;
788 789
	     view = ISC_LIST_NEXT(view, link))
	{
790
		const dns_name_t *tsig = NULL;
791

792
		if (view->matchrecursiveonly) {
793
			continue;
794
		}
795

796
		if (rdclass != view->rdclass) {
797
			continue;
798
		}
799 800

		if (mykey != NULL) {
801
			bool match;
802 803 804
			isc_result_t result;

			result = dns_view_gettsig(view, &mykey->name, &key);
805
			if (result != ISC_R_SUCCESS) {
806
				continue;
807
			}
808 809
			match = dst_key_compare(mykey->key, key->key);
			dns_tsigkey_detach(&key);
810
			if (!match) {
811
				continue;
812
			}
813 814 815
			tsig = dns_tsigkey_identity(mykey);
		}

816 817
		if (dns_acl_allowed(&netsrc, tsig, view->matchclients, env) &&
		    dns_acl_allowed(&netdst, tsig, view->matchdestinations,
818 819
				    env))