zt.c 12.9 KB
Newer Older
Mark Andrews's avatar
Mark Andrews committed
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.
Mark Andrews's avatar
Mark Andrews committed
10 11
 */

12 13

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

Mark Andrews's avatar
Mark Andrews committed
15
#include <config.h>
16

17
#include <inttypes.h>
18
#include <stdbool.h>
19

20
#include <isc/file.h>
Bob Halley's avatar
Bob Halley committed
21
#include <isc/magic.h>
22
#include <isc/mem.h>
23
#include <isc/string.h>
Evan Hunt's avatar
Evan Hunt committed
24
#include <isc/task.h>
Michael Graff's avatar
Michael Graff committed
25
#include <isc/util.h>
26

27 28
#include <dns/log.h>
#include <dns/name.h>
David Lawrence's avatar
David Lawrence committed
29
#include <dns/rbt.h>
30
#include <dns/rdataclass.h>
31
#include <dns/result.h>
32
#include <dns/view.h>
33
#include <dns/zone.h>
David Lawrence's avatar
David Lawrence committed
34
#include <dns/zt.h>
Mark Andrews's avatar
Mark Andrews committed
35

36 37 38 39 40
struct zt_load_params {
	dns_zt_zoneloaded_t dl;
	bool newonly;
};

Mark Andrews's avatar
Mark Andrews committed
41
struct dns_zt {
42
	/* Unlocked. */
Brian Wellington's avatar
Brian Wellington committed
43
	unsigned int		magic;
44
	isc_mem_t		*mctx;
Mark Andrews's avatar
Mark Andrews committed
45
	dns_rdataclass_t	rdclass;
46
	isc_rwlock_t		rwlock;
47 48
	dns_zt_allloaded_t	loaddone;
	void *			loaddone_arg;
49
	struct zt_load_params 	*loadparams;
50
	/* Locked by lock. */
51
	bool		flush;
52
	uint32_t		references;
53
	unsigned int		loads_pending;
54
	dns_rbt_t		*table;
Mark Andrews's avatar
Mark Andrews committed
55 56
};

57
#define ZTMAGIC			ISC_MAGIC('Z', 'T', 'b', 'l')
58
#define VALID_ZT(zt) 		ISC_MAGIC_VALID(zt, ZTMAGIC)
Mark Andrews's avatar
Mark Andrews committed
59

60

61 62 63 64 65
static void
auto_detach(void *, void *);

static isc_result_t
load(dns_zone_t *zone, void *uap);
Mark Andrews's avatar
Mark Andrews committed
66

67 68 69
static isc_result_t
asyncload(dns_zone_t *zone, void *callback);

70 71 72
static isc_result_t
freezezones(dns_zone_t *zone, void *uap);

73
static isc_result_t
Evan Hunt's avatar
Evan Hunt committed
74
doneloading(dns_zt_t *zt, dns_zone_t *zone, isc_task_t *task);
75

76
isc_result_t
Evan Hunt's avatar
Evan Hunt committed
77
dns_zt_create(isc_mem_t *mctx, dns_rdataclass_t rdclass, dns_zt_t **ztp) {
78 79 80 81
	dns_zt_t *zt;
	isc_result_t result;

	REQUIRE(ztp != NULL && *ztp == NULL);
Mark Andrews's avatar
Mark Andrews committed
82

Andreas Gustafsson's avatar
Andreas Gustafsson committed
83
	zt = isc_mem_get(mctx, sizeof(*zt));
84
	if (zt == NULL)
85
		return (ISC_R_NOMEMORY);
Mark Andrews's avatar
Mark Andrews committed
86

87
	zt->table = NULL;
88
	result = dns_rbt_create(mctx, auto_detach, zt, &zt->table);
89 90
	if (result != ISC_R_SUCCESS)
		goto cleanup_zt;
91

92
	result = isc_rwlock_init(&zt->rwlock, 0, 0);
93
	if (result != ISC_R_SUCCESS)
94
		goto cleanup_rbt;
95

96 97
	zt->mctx = NULL;
	isc_mem_attach(mctx, &zt->mctx);
98
	zt->references = 1;
99
	zt->flush = false;
100 101
	zt->rdclass = rdclass;
	zt->magic = ZTMAGIC;
102 103
	zt->loaddone = NULL;
	zt->loaddone_arg = NULL;
104
	zt->loadparams = NULL;
105
	zt->loads_pending = 0;
106
	*ztp = zt;
107

108 109 110 111 112 113
	return (ISC_R_SUCCESS);

   cleanup_rbt:
	dns_rbt_destroy(&zt->table);

   cleanup_zt:
Andreas Gustafsson's avatar
Andreas Gustafsson committed
114
	isc_mem_put(mctx, zt, sizeof(*zt));
115 116

	return (result);
Mark Andrews's avatar
Mark Andrews committed
117 118
}

119
isc_result_t
120
dns_zt_mount(dns_zt_t *zt, dns_zone_t *zone) {
121
	isc_result_t result;
Mark Andrews's avatar
Mark Andrews committed
122
	dns_zone_t *dummy = NULL;
123
	dns_name_t *name;
Mark Andrews's avatar
Mark Andrews committed
124 125 126

	REQUIRE(VALID_ZT(zt));

127
	name = dns_zone_getorigin(zone);
128 129 130

	RWLOCK(&zt->rwlock, isc_rwlocktype_write);

131
	result = dns_rbt_addname(zt->table, name, zone);
132
	if (result == ISC_R_SUCCESS)
Bob Halley's avatar
Bob Halley committed
133
		dns_zone_attach(zone, &dummy);
134

135 136
	RWUNLOCK(&zt->rwlock, isc_rwlocktype_write);

Mark Andrews's avatar
Mark Andrews committed
137 138 139
	return (result);
}

140
isc_result_t
141
dns_zt_unmount(dns_zt_t *zt, dns_zone_t *zone) {
142
	isc_result_t result;
143
	dns_name_t *name;
Mark Andrews's avatar
Mark Andrews committed
144 145 146

	REQUIRE(VALID_ZT(zt));

147
	name = dns_zone_getorigin(zone);
148 149 150

	RWLOCK(&zt->rwlock, isc_rwlocktype_write);

151
	result = dns_rbt_deletename(zt->table, name, false);
152 153 154

	RWUNLOCK(&zt->rwlock, isc_rwlocktype_write);

Mark Andrews's avatar
Mark Andrews committed
155 156 157
	return (result);
}

158
isc_result_t
159
dns_zt_find(dns_zt_t *zt, const dns_name_t *name, unsigned int options,
Bob Halley's avatar
Bob Halley committed
160
	    dns_name_t *foundname, dns_zone_t **zonep)
Mark Andrews's avatar
Mark Andrews committed
161
{
162
	isc_result_t result;
Bob Halley's avatar
Bob Halley committed
163
	dns_zone_t *dummy = NULL;
Bob Halley's avatar
Bob Halley committed
164
	unsigned int rbtoptions = 0;
Mark Andrews's avatar
Mark Andrews committed
165 166 167

	REQUIRE(VALID_ZT(zt));

Bob Halley's avatar
Bob Halley committed
168 169 170
	if ((options & DNS_ZTFIND_NOEXACT) != 0)
		rbtoptions |= DNS_RBTFIND_NOEXACT;

171
	RWLOCK(&zt->rwlock, isc_rwlocktype_read);
172

Bob Halley's avatar
Bob Halley committed
173
	result = dns_rbt_findname(zt->table, name, rbtoptions, foundname,
Mark Andrews's avatar
Mark Andrews committed
174
				  (void **) (void*)&dummy);
175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190
	if (result == ISC_R_SUCCESS || result == DNS_R_PARTIALMATCH) {
		/*
		 * If DNS_ZTFIND_MIRROR is set and the zone which was
		 * determined to be the deepest match for the supplied name is
		 * a mirror zone which is expired or not yet loaded, treat it
		 * as non-existent.  This will trigger a fallback to recursion
		 * instead of returning a SERVFAIL.
		 */
		if ((options & DNS_ZTFIND_MIRROR) != 0 &&
		    dns_zone_ismirror(dummy) && !dns_zone_isloaded(dummy))
		{
			result = ISC_R_NOTFOUND;
		} else {
			dns_zone_attach(dummy, zonep);
		}
	}
191 192

	RWUNLOCK(&zt->rwlock, isc_rwlocktype_read);
Bob Halley's avatar
Bob Halley committed
193

Mark Andrews's avatar
Mark Andrews committed
194 195 196
	return (result);
}

197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213
void
dns_zt_attach(dns_zt_t *zt, dns_zt_t **ztp) {

	REQUIRE(VALID_ZT(zt));
	REQUIRE(ztp != NULL && *ztp == NULL);

	RWLOCK(&zt->rwlock, isc_rwlocktype_write);

	INSIST(zt->references > 0);
	zt->references++;
	INSIST(zt->references != 0);

	RWUNLOCK(&zt->rwlock, isc_rwlocktype_write);

	*ztp = zt;
}

214 215 216
static isc_result_t
flush(dns_zone_t *zone, void *uap) {
	UNUSED(uap);
217
	return (dns_zone_flush(zone));
218 219
}

220 221 222
static void
zt_destroy(dns_zt_t *zt) {
	if (zt->flush)
223
		(void)dns_zt_apply(zt, false, NULL, flush, NULL);
224 225 226 227 228 229
	dns_rbt_destroy(&zt->table);
	isc_rwlock_destroy(&zt->rwlock);
	zt->magic = 0;
	isc_mem_putanddetach(&zt->mctx, zt, sizeof(*zt));
}

230
static void
231 232
zt_flushanddetach(dns_zt_t **ztp, bool need_flush) {
	bool destroy = false;
233 234 235 236 237 238
	dns_zt_t *zt;

	REQUIRE(ztp != NULL && VALID_ZT(*ztp));

	zt = *ztp;

239
	RWLOCK(&zt->rwlock, isc_rwlocktype_write);
240

241 242 243
	INSIST(zt->references > 0);
	zt->references--;
	if (zt->references == 0)
244
		destroy = true;
245
	if (need_flush)
246
		zt->flush = true;
247 248

	RWUNLOCK(&zt->rwlock, isc_rwlocktype_write);
249

250 251
	if (destroy)
		zt_destroy(zt);
252 253 254 255

	*ztp = NULL;
}

256 257
void
dns_zt_flushanddetach(dns_zt_t **ztp) {
258
	zt_flushanddetach(ztp, true);
259 260 261 262
}

void
dns_zt_detach(dns_zt_t **ztp) {
263
	zt_flushanddetach(ztp, false);
264 265
}

266
isc_result_t
267
dns_zt_load(dns_zt_t *zt, bool stop, bool newonly) {
268
	isc_result_t result;
269
	struct zt_load_params params;
270
	REQUIRE(VALID_ZT(zt));
271
	params.newonly = newonly;
272
	RWLOCK(&zt->rwlock, isc_rwlocktype_read);
273
	result = dns_zt_apply(zt, stop, NULL, load, &params);
274 275
	RWUNLOCK(&zt->rwlock, isc_rwlocktype_read);
	return (result);
Mark Andrews's avatar
Mark Andrews committed
276 277
}

278
static isc_result_t
279
load(dns_zone_t *zone, void *paramsv) {
280
	isc_result_t result;
281 282 283 284
	struct zt_load_params *params = (struct zt_load_params*) paramsv;
	result = dns_zone_load(zone, params->newonly);
	if (result == DNS_R_CONTINUE || result == DNS_R_UPTODATE ||
	    result == DNS_R_DYNAMIC) {
285
		result = ISC_R_SUCCESS;
286
	}
287 288 289 290
	return (result);
}

isc_result_t
291 292
dns_zt_asyncload(dns_zt_t *zt, bool newonly,
		 dns_zt_allloaded_t alldone, void *arg) {
293
	isc_result_t result;
294 295 296
	int pending;

	REQUIRE(VALID_ZT(zt));
297 298 299 300 301 302
	zt->loadparams = isc_mem_get(zt->mctx, sizeof(struct zt_load_params));
	if (zt->loadparams == NULL) {
		return (ISC_R_NOMEMORY);
	}
	zt->loadparams->dl = doneloading;
	zt->loadparams->newonly = newonly;
303
	RWLOCK(&zt->rwlock, isc_rwlocktype_write);
304
	INSIST(zt->loads_pending == 0);
305
	result = dns_zt_apply(zt, false, NULL, asyncload, zt);
306 307 308 309 310 311 312

	pending = zt->loads_pending;
	if (pending != 0) {
		zt->loaddone = alldone;
		zt->loaddone_arg = arg;
	}

313
	RWUNLOCK(&zt->rwlock, isc_rwlocktype_write);
314

315 316 317
	if (pending == 0) {
		isc_mem_put(zt->mctx, zt->loadparams, sizeof(struct zt_load_params));
		zt->loadparams = NULL;
318
		alldone(arg);
319
	}
320

321
	return (result);
Mark Andrews's avatar
Mark Andrews committed
322 323
}

324
/*
Automatic Updater's avatar
Automatic Updater committed
325
 * Initiates asynchronous loading of zone 'zone'.  'callback' is a
326 327 328 329
 * pointer to a function which will be used to inform the caller when
 * the zone loading is complete.
 */
static isc_result_t
330
asyncload(dns_zone_t *zone, void *zt_) {
331
	isc_result_t result;
332
	struct dns_zt *zt = (dns_zt_t*) zt_;
333
	REQUIRE(zone != NULL);
Evan Hunt's avatar
Evan Hunt committed
334 335 336 337
	INSIST(zt->references > 0);
	zt->references++;
	zt->loads_pending++;

338
	result = dns_zone_asyncload(zone, zt->loadparams->newonly, *zt->loadparams->dl, zt);
Evan Hunt's avatar
Evan Hunt committed
339 340 341
	if (result != ISC_R_SUCCESS) {
		zt->references--;
		zt->loads_pending--;
342 343
		INSIST(zt->references > 0);
	}
344 345 346
	return (ISC_R_SUCCESS);
}

347
isc_result_t
348
dns_zt_freezezones(dns_zt_t *zt, bool freeze) {
349 350 351 352 353
	isc_result_t result, tresult;

	REQUIRE(VALID_ZT(zt));

	RWLOCK(&zt->rwlock, isc_rwlocktype_read);
354
	result = dns_zt_apply(zt, false, &tresult, freezezones, &freeze);
355
	RWUNLOCK(&zt->rwlock, isc_rwlocktype_read);
356 357
	if (tresult == ISC_R_NOTFOUND)
		tresult = ISC_R_SUCCESS;
358 359 360 361 362
	return ((result == ISC_R_SUCCESS) ? tresult : result);
}

static isc_result_t
freezezones(dns_zone_t *zone, void *uap) {
363 364
	bool freeze = *(bool *)uap;
	bool frozen;
365 366 367
	isc_result_t result = ISC_R_SUCCESS;
	char classstr[DNS_RDATACLASS_FORMATSIZE];
	char zonename[DNS_NAME_FORMATSIZE];
368
	dns_zone_t *raw = NULL;
369 370 371 372 373
	dns_view_t *view;
	const char *vname;
	const char *sep;
	int level;

374 375 376 377 378 379
	dns_zone_getraw(zone, &raw);
	if (raw != NULL)
		zone = raw;
	if (dns_zone_gettype(zone) != dns_zone_master) {
		if (raw != NULL)
			dns_zone_detach(&raw);
380
		return (ISC_R_SUCCESS);
381
	}
382
	if (!dns_zone_isdynamic(zone, true)) {
383 384
		if (raw != NULL)
			dns_zone_detach(&raw);
385
		return (ISC_R_SUCCESS);
386
	}
387 388 389 390 391 392 393

	frozen = dns_zone_getupdatedisabled(zone);
	if (freeze) {
		if (frozen)
			result = DNS_R_FROZEN;
		if (result == ISC_R_SUCCESS)
			result = dns_zone_flush(zone);
394 395
		if (result == ISC_R_SUCCESS)
			dns_zone_setupdatedisabled(zone, freeze);
396 397
	} else {
		if (frozen) {
398
			result = dns_zone_loadandthaw(zone);
399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422
			if (result == DNS_R_CONTINUE ||
			    result == DNS_R_UPTODATE)
				result = ISC_R_SUCCESS;
		}
	}
	view = dns_zone_getview(zone);
	if (strcmp(view->name, "_bind") == 0 ||
	    strcmp(view->name, "_default") == 0)
	{
		vname = "";
		sep = "";
	} else {
		vname = view->name;
		sep = " ";
	}
	dns_rdataclass_format(dns_zone_getclass(zone), classstr,
			      sizeof(classstr));
	dns_name_format(dns_zone_getorigin(zone), zonename, sizeof(zonename));
	level = (result != ISC_R_SUCCESS) ? ISC_LOG_ERROR : ISC_LOG_DEBUG(1);
	isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL, DNS_LOGMODULE_ZONE,
		      level, "%s zone '%s/%s'%s%s: %s",
		      freeze ? "freezing" : "thawing",
		      zonename, classstr, sep, vname,
		      isc_result_totext(result));
423 424
	if (raw != NULL)
		dns_zone_detach(&raw);
425 426 427
	return (result);
}

428 429 430 431 432 433 434 435 436 437 438 439 440 441 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 469 470 471 472 473 474 475
void
dns_zt_setviewcommit(dns_zt_t *zt) {
	dns_rbtnode_t *node;
	dns_rbtnodechain_t chain;
	isc_result_t result;

	REQUIRE(VALID_ZT(zt));

	dns_rbtnodechain_init(&chain, zt->mctx);

	result = dns_rbtnodechain_first(&chain, zt->table, NULL, NULL);
	while (result == DNS_R_NEWORIGIN || result == ISC_R_SUCCESS) {
		result = dns_rbtnodechain_current(&chain, NULL, NULL,
						  &node);
		if (result == ISC_R_SUCCESS && node->data != NULL) {
			dns_zone_setviewcommit(node->data);
		}

		result = dns_rbtnodechain_next(&chain, NULL, NULL);
	}

	dns_rbtnodechain_invalidate(&chain);
}

void
dns_zt_setviewrevert(dns_zt_t *zt) {
	dns_rbtnode_t *node;
	dns_rbtnodechain_t chain;
	isc_result_t result;

	REQUIRE(VALID_ZT(zt));

	dns_rbtnodechain_init(&chain, zt->mctx);

	result = dns_rbtnodechain_first(&chain, zt->table, NULL, NULL);
	while (result == DNS_R_NEWORIGIN || result == ISC_R_SUCCESS) {
		result = dns_rbtnodechain_current(&chain, NULL, NULL,
						  &node);
		if (result == ISC_R_SUCCESS && node->data != NULL) {
			dns_zone_setviewrevert(node->data);
		}

		result = dns_rbtnodechain_next(&chain, NULL, NULL);
	}

	dns_rbtnodechain_invalidate(&chain);
}

476
isc_result_t
477
dns_zt_apply(dns_zt_t *zt, bool stop, isc_result_t *sub,
478
	     isc_result_t (*action)(dns_zone_t *, void *), void *uap)
479
{
Bob Halley's avatar
Bob Halley committed
480 481
	dns_rbtnode_t *node;
	dns_rbtnodechain_t chain;
482
	isc_result_t result, tresult = ISC_R_SUCCESS;
Bob Halley's avatar
Bob Halley committed
483 484 485
	dns_zone_t *zone;

	REQUIRE(VALID_ZT(zt));
Mark Andrews's avatar
Mark Andrews committed
486
	REQUIRE(action != NULL);
Bob Halley's avatar
Bob Halley committed
487 488 489

	dns_rbtnodechain_init(&chain, zt->mctx);
	result = dns_rbtnodechain_first(&chain, zt->table, NULL, NULL);
490
	if (result == ISC_R_NOTFOUND) {
491 492 493
		/*
		 * The tree is empty.
		 */
494
		tresult = result;
495
		result = ISC_R_NOMORE;
496
	}
497
	while (result == DNS_R_NEWORIGIN || result == ISC_R_SUCCESS) {
Bob Halley's avatar
Bob Halley committed
498 499
		result = dns_rbtnodechain_current(&chain, NULL, NULL,
						  &node);
500
		if (result == ISC_R_SUCCESS) {
Bob Halley's avatar
Bob Halley committed
501 502
			zone = node->data;
			if (zone != NULL)
503
				result = (action)(zone, uap);
504 505
			if (result != ISC_R_SUCCESS && stop) {
				tresult = result;
506
				goto cleanup;	/* don't break */
507
			} else if (result != ISC_R_SUCCESS &&
508
				   tresult == ISC_R_SUCCESS)
509
				tresult = result;
Bob Halley's avatar
Bob Halley committed
510 511 512
		}
		result = dns_rbtnodechain_next(&chain, NULL, NULL);
	}
513 514
	if (result == ISC_R_NOMORE)
		result = ISC_R_SUCCESS;
515 516

 cleanup:
Bob Halley's avatar
Bob Halley committed
517
	dns_rbtnodechain_invalidate(&chain);
518 519
	if (sub != NULL)
		*sub = tresult;
520

521
	return (result);
Bob Halley's avatar
Bob Halley committed
522 523
}

524 525 526 527 528
/*
 * Decrement the loads_pending counter; when counter reaches
 * zero, call the loaddone callback that was initially set by
 * dns_zt_asyncload().
 */
529
static isc_result_t
Evan Hunt's avatar
Evan Hunt committed
530
doneloading(dns_zt_t *zt, dns_zone_t *zone, isc_task_t *task) {
531
	bool destroy = false;
532 533 534 535
	dns_zt_allloaded_t alldone = NULL;
	void *arg = NULL;

	UNUSED(zone);
Evan Hunt's avatar
Evan Hunt committed
536
	UNUSED(task);
537 538 539 540 541

	REQUIRE(VALID_ZT(zt));

	RWLOCK(&zt->rwlock, isc_rwlocktype_write);
	INSIST(zt->loads_pending != 0);
542 543 544
	INSIST(zt->references != 0);
	zt->references--;
	if (zt->references == 0)
545
		destroy = true;
546 547 548 549 550 551
	zt->loads_pending--;
	if (zt->loads_pending == 0) {
		alldone = zt->loaddone;
		arg = zt->loaddone_arg;
		zt->loaddone = NULL;
		zt->loaddone_arg = NULL;
552 553
		isc_mem_put(zt->mctx, zt->loadparams, sizeof(struct zt_load_params));
		zt->loadparams = NULL;
554 555 556 557 558
	}
	RWUNLOCK(&zt->rwlock, isc_rwlocktype_write);

	if (alldone != NULL)
		alldone(arg);
559

560 561 562
	if (destroy)
		zt_destroy(zt);

563
	return (ISC_R_SUCCESS);
564 565
}

Bob Halley's avatar
Bob Halley committed
566 567 568 569
/***
 *** Private
 ***/

Mark Andrews's avatar
Mark Andrews committed
570
static void
571 572
auto_detach(void *data, void *arg) {
	dns_zone_t *zone = data;
Mark Andrews's avatar
Mark Andrews committed
573

574
	UNUSED(arg);
575
	dns_zone_detach(&zone);
Mark Andrews's avatar
Mark Andrews committed
576
}