1246847Sjkim/*	$NetBSD: zt.c,v 1.10 2024/02/21 22:52:09 christos Exp $	*/
2246847Sjkim
3246847Sjkim/*
4246847Sjkim * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
5246847Sjkim *
6246847Sjkim * SPDX-License-Identifier: MPL-2.0
7246847Sjkim *
8281075Sdim * This Source Code Form is subject to the terms of the Mozilla Public
9246847Sjkim * License, v. 2.0. If a copy of the MPL was not distributed with this
10246847Sjkim * file, you can obtain one at https://mozilla.org/MPL/2.0/.
11246847Sjkim *
12246847Sjkim * See the COPYRIGHT file distributed with this work for additional
13246847Sjkim * information regarding copyright ownership.
14246847Sjkim */
15246847Sjkim
16246847Sjkim/*! \file */
17246847Sjkim
18246847Sjkim#include <inttypes.h>
19246847Sjkim#include <stdbool.h>
20246847Sjkim
21246847Sjkim#include <isc/atomic.h>
22246847Sjkim#include <isc/file.h>
23246847Sjkim#include <isc/magic.h>
24246847Sjkim#include <isc/mem.h>
25246847Sjkim#include <isc/result.h>
26246847Sjkim#include <isc/string.h>
27246847Sjkim#include <isc/task.h>
28246847Sjkim#include <isc/util.h>
29246847Sjkim
30246847Sjkim#include <dns/log.h>
31246847Sjkim#include <dns/name.h>
32246847Sjkim#include <dns/rbt.h>
33246847Sjkim#include <dns/rdataclass.h>
34246847Sjkim#include <dns/view.h>
35246847Sjkim#include <dns/zone.h>
36246847Sjkim#include <dns/zt.h>
37246847Sjkim
38246847Sjkimstruct zt_load_params {
39246847Sjkim	dns_zt_zoneloaded_t dl;
40246847Sjkim	bool newonly;
41246847Sjkim};
42246847Sjkim
43246847Sjkimstruct dns_zt {
44246849Sjkim	/* Unlocked. */
45246847Sjkim	unsigned int magic;
46246849Sjkim	isc_mem_t *mctx;
47246847Sjkim	dns_rdataclass_t rdclass;
48246847Sjkim	isc_rwlock_t rwlock;
49246847Sjkim	dns_zt_allloaded_t loaddone;
50246847Sjkim	void *loaddone_arg;
51246847Sjkim	struct zt_load_params *loadparams;
52246847Sjkim
53246847Sjkim	/* Atomic */
54246847Sjkim	atomic_bool flush;
55246847Sjkim	isc_refcount_t references;
56246847Sjkim	isc_refcount_t loads_pending;
57249663Sjkim
58249663Sjkim	/* Locked by lock. */
59249663Sjkim	dns_rbt_t *table;
60249663Sjkim};
61249663Sjkim
62249663Sjkimstruct zt_freeze_params {
63246847Sjkim	dns_view_t *view;
64246847Sjkim	bool freeze;
65246847Sjkim};
66246847Sjkim
67246847Sjkim#define ZTMAGIC	     ISC_MAGIC('Z', 'T', 'b', 'l')
68246847Sjkim#define VALID_ZT(zt) ISC_MAGIC_VALID(zt, ZTMAGIC)
69246847Sjkim
70246847Sjkimstatic void
71246847Sjkimauto_detach(void *, void *);
72246847Sjkim
73246847Sjkimstatic isc_result_t
74246847Sjkimload(dns_zone_t *zone, void *uap);
75246847Sjkim
76246847Sjkimstatic isc_result_t
77246847Sjkimasyncload(dns_zone_t *zone, void *callback);
78246847Sjkim
79246847Sjkimstatic isc_result_t
80246847Sjkimfreezezones(dns_zone_t *zone, void *uap);
81246847Sjkim
82246847Sjkimstatic isc_result_t
83246847Sjkimdoneloading(dns_zt_t *zt, dns_zone_t *zone, isc_task_t *task);
84246847Sjkim
85246847Sjkimisc_result_t
86246847Sjkimdns_zt_create(isc_mem_t *mctx, dns_rdataclass_t rdclass, dns_zt_t **ztp) {
87246847Sjkim	dns_zt_t *zt;
88246847Sjkim	isc_result_t result;
89246847Sjkim
90246847Sjkim	REQUIRE(ztp != NULL && *ztp == NULL);
91246847Sjkim
92246847Sjkim	zt = isc_mem_get(mctx, sizeof(*zt));
93246847Sjkim
94246847Sjkim	zt->table = NULL;
95246847Sjkim	result = dns_rbt_create(mctx, auto_detach, zt, &zt->table);
96249663Sjkim	if (result != ISC_R_SUCCESS) {
97249663Sjkim		goto cleanup_zt;
98249663Sjkim	}
99246847Sjkim
100246847Sjkim	isc_rwlock_init(&zt->rwlock, 0, 0);
101246847Sjkim	zt->mctx = NULL;
102246847Sjkim	isc_mem_attach(mctx, &zt->mctx);
103246847Sjkim	isc_refcount_init(&zt->references, 1);
104246847Sjkim	atomic_init(&zt->flush, false);
105246847Sjkim	zt->rdclass = rdclass;
106246847Sjkim	zt->magic = ZTMAGIC;
107246847Sjkim	zt->loaddone = NULL;
108246847Sjkim	zt->loaddone_arg = NULL;
109246847Sjkim	zt->loadparams = NULL;
110246847Sjkim	isc_refcount_init(&zt->loads_pending, 0);
111246847Sjkim	*ztp = zt;
112246847Sjkim
113246847Sjkim	return (ISC_R_SUCCESS);
114246847Sjkim
115246847Sjkimcleanup_zt:
116246847Sjkim	isc_mem_put(mctx, zt, sizeof(*zt));
117246847Sjkim
118246847Sjkim	return (result);
119246847Sjkim}
120246847Sjkim
121246847Sjkimisc_result_t
122246847Sjkimdns_zt_mount(dns_zt_t *zt, dns_zone_t *zone) {
123246847Sjkim	isc_result_t result;
124246847Sjkim	dns_zone_t *dummy = NULL;
125246847Sjkim	dns_name_t *name;
126246847Sjkim
127246847Sjkim	REQUIRE(VALID_ZT(zt));
128246847Sjkim
129246847Sjkim	name = dns_zone_getorigin(zone);
130249112Sjkim
131249112Sjkim	RWLOCK(&zt->rwlock, isc_rwlocktype_write);
132249112Sjkim
133249112Sjkim	result = dns_rbt_addname(zt->table, name, zone);
134249112Sjkim	if (result == ISC_R_SUCCESS) {
135246847Sjkim		dns_zone_attach(zone, &dummy);
136246847Sjkim	}
137246847Sjkim
138249112Sjkim	RWUNLOCK(&zt->rwlock, isc_rwlocktype_write);
139246847Sjkim
140249112Sjkim	return (result);
141249112Sjkim}
142249112Sjkim
143249112Sjkimisc_result_t
144249112Sjkimdns_zt_unmount(dns_zt_t *zt, dns_zone_t *zone) {
145246847Sjkim	isc_result_t result;
146249112Sjkim	dns_name_t *name;
147249112Sjkim
148249112Sjkim	REQUIRE(VALID_ZT(zt));
149249112Sjkim
150249112Sjkim	name = dns_zone_getorigin(zone);
151249112Sjkim
152249112Sjkim	RWLOCK(&zt->rwlock, isc_rwlocktype_write);
153249112Sjkim
154284460Sjkim	result = dns_rbt_deletename(zt->table, name, false);
155249112Sjkim
156249112Sjkim	RWUNLOCK(&zt->rwlock, isc_rwlocktype_write);
157249112Sjkim
158246847Sjkim	return (result);
159249112Sjkim}
160246847Sjkim
161246847Sjkimisc_result_t
162246847Sjkimdns_zt_find(dns_zt_t *zt, const dns_name_t *name, unsigned int options,
163246847Sjkim	    dns_name_t *foundname, dns_zone_t **zonep) {
164246847Sjkim	isc_result_t result;
165246847Sjkim	dns_zone_t *dummy = NULL;
166246847Sjkim	unsigned int rbtoptions = 0;
167246847Sjkim
168246847Sjkim	REQUIRE(VALID_ZT(zt));
169246847Sjkim
170246847Sjkim	if ((options & DNS_ZTFIND_NOEXACT) != 0) {
171246847Sjkim		rbtoptions |= DNS_RBTFIND_NOEXACT;
172246847Sjkim	}
173281075Sdim
174246847Sjkim	RWLOCK(&zt->rwlock, isc_rwlocktype_read);
175246847Sjkim
176246847Sjkim	result = dns_rbt_findname(zt->table, name, rbtoptions, foundname,
177246847Sjkim				  (void **)(void *)&dummy);
178246847Sjkim	if (result == ISC_R_SUCCESS || result == DNS_R_PARTIALMATCH) {
179246847Sjkim		/*
180246847Sjkim		 * If DNS_ZTFIND_MIRROR is set and the zone which was
181246847Sjkim		 * determined to be the deepest match for the supplied name is
182246847Sjkim		 * a mirror zone which is expired or not yet loaded, treat it
183246847Sjkim		 * as non-existent.  This will trigger a fallback to recursion
184246847Sjkim		 * instead of returning a SERVFAIL.
185246847Sjkim		 *
186246847Sjkim		 * Note that currently only the deepest match in the zone table
187246847Sjkim		 * is checked.  Consider a server configured with two mirror
188246847Sjkim		 * zones: "bar" and its child, "foo.bar".  If zone data is
189246847Sjkim		 * available for "bar" but not for "foo.bar", a query with
190246847Sjkim		 * QNAME equal to or below "foo.bar" will cause ISC_R_NOTFOUND
191246847Sjkim		 * to be returned, not DNS_R_PARTIALMATCH, despite zone data
192246847Sjkim		 * being available for "bar".  This is considered to be an edge
193246847Sjkim		 * case, handling which more appropriately is possible, but
194246847Sjkim		 * arguably not worth the added complexity.
195246847Sjkim		 */
196246847Sjkim		if ((options & DNS_ZTFIND_MIRROR) != 0 &&
197246847Sjkim		    dns_zone_gettype(dummy) == dns_zone_mirror &&
198281075Sdim		    !dns_zone_isloaded(dummy))
199249663Sjkim		{
200246847Sjkim			result = ISC_R_NOTFOUND;
201246847Sjkim		} else {
202246847Sjkim			dns_zone_attach(dummy, zonep);
203246847Sjkim		}
204246847Sjkim	}
205246847Sjkim
206246847Sjkim	RWUNLOCK(&zt->rwlock, isc_rwlocktype_read);
207246847Sjkim
208246847Sjkim	return (result);
209246847Sjkim}
210246847Sjkim
211281075Sdimvoid
212249663Sjkimdns_zt_attach(dns_zt_t *zt, dns_zt_t **ztp) {
213249663Sjkim	REQUIRE(VALID_ZT(zt));
214246847Sjkim	REQUIRE(ztp != NULL && *ztp == NULL);
215246847Sjkim
216246847Sjkim	isc_refcount_increment(&zt->references);
217246847Sjkim
218246847Sjkim	*ztp = zt;
219246847Sjkim}
220246847Sjkim
221246847Sjkimstatic isc_result_t
222246847Sjkimflush(dns_zone_t *zone, void *uap) {
223246847Sjkim	UNUSED(uap);
224246847Sjkim	return (dns_zone_flush(zone));
225246847Sjkim}
226246847Sjkim
227246847Sjkimstatic void
228246847Sjkimzt_destroy(dns_zt_t *zt) {
229246847Sjkim	isc_refcount_destroy(&zt->references);
230246847Sjkim	isc_refcount_destroy(&zt->loads_pending);
231246847Sjkim
232246847Sjkim	if (atomic_load_acquire(&zt->flush)) {
233246847Sjkim		(void)dns_zt_apply(zt, isc_rwlocktype_none, false, NULL, flush,
234246847Sjkim				   NULL);
235246847Sjkim	}
236246847Sjkim
237246847Sjkim	dns_rbt_destroy(&zt->table);
238246847Sjkim	isc_rwlock_destroy(&zt->rwlock);
239246847Sjkim	zt->magic = 0;
240246847Sjkim	isc_mem_putanddetach(&zt->mctx, zt, sizeof(*zt));
241246847Sjkim}
242246847Sjkim
243246847Sjkimvoid
244246847Sjkimdns_zt_detach(dns_zt_t **ztp) {
245246847Sjkim	dns_zt_t *zt;
246246847Sjkim
247246847Sjkim	REQUIRE(ztp != NULL && VALID_ZT(*ztp));
248246847Sjkim
249246847Sjkim	zt = *ztp;
250246847Sjkim	*ztp = NULL;
251246847Sjkim
252246847Sjkim	if (isc_refcount_decrement(&zt->references) == 1) {
253246847Sjkim		zt_destroy(zt);
254246847Sjkim	}
255281075Sdim}
256246847Sjkim
257246847Sjkimvoid
258246847Sjkimdns_zt_flush(dns_zt_t *zt) {
259246847Sjkim	REQUIRE(VALID_ZT(zt));
260246847Sjkim	atomic_store_release(&zt->flush, true);
261246847Sjkim}
262246847Sjkim
263281075Sdimisc_result_t
264246847Sjkimdns_zt_load(dns_zt_t *zt, bool stop, bool newonly) {
265246847Sjkim	isc_result_t result;
266246847Sjkim	struct zt_load_params params;
267246847Sjkim	REQUIRE(VALID_ZT(zt));
268246847Sjkim	params.newonly = newonly;
269246847Sjkim	result = dns_zt_apply(zt, isc_rwlocktype_read, stop, NULL, load,
270246847Sjkim			      &params);
271246847Sjkim	return (result);
272246847Sjkim}
273249663Sjkim
274249663Sjkimstatic isc_result_t
275246847Sjkimload(dns_zone_t *zone, void *paramsv) {
276246847Sjkim	isc_result_t result;
277246847Sjkim	struct zt_load_params *params = (struct zt_load_params *)paramsv;
278246847Sjkim	result = dns_zone_load(zone, params->newonly);
279246847Sjkim	if (result == DNS_R_CONTINUE || result == DNS_R_UPTODATE ||
280246847Sjkim	    result == DNS_R_DYNAMIC)
281246847Sjkim	{
282246847Sjkim		result = ISC_R_SUCCESS;
283246847Sjkim	}
284246847Sjkim	return (result);
285246847Sjkim}
286246847Sjkim
287281075Sdimstatic void
288246847Sjkimcall_loaddone(dns_zt_t *zt) {
289246847Sjkim	dns_zt_allloaded_t loaddone = zt->loaddone;
290246847Sjkim	void *loaddone_arg = zt->loaddone_arg;
291246847Sjkim
292246847Sjkim	/*
293281075Sdim	 * Set zt->loaddone, zt->loaddone_arg and zt->loadparams to NULL
294281075Sdim	 * before calling loaddone.
295281075Sdim	 */
296281075Sdim	zt->loaddone = NULL;
297281075Sdim	zt->loaddone_arg = NULL;
298281075Sdim
299281075Sdim	isc_mem_put(zt->mctx, zt->loadparams, sizeof(struct zt_load_params));
300281075Sdim	zt->loadparams = NULL;
301281075Sdim
302281075Sdim	/*
303281075Sdim	 * Call the callback last.
304281075Sdim	 */
305281075Sdim	if (loaddone != NULL) {
306281075Sdim		loaddone(loaddone_arg);
307281075Sdim	}
308281075Sdim}
309281075Sdim
310281075Sdimisc_result_t
311281075Sdimdns_zt_asyncload(dns_zt_t *zt, bool newonly, dns_zt_allloaded_t alldone,
312281075Sdim		 void *arg) {
313281075Sdim	isc_result_t result;
314281075Sdim	uint_fast32_t loads_pending;
315281075Sdim
316281075Sdim	REQUIRE(VALID_ZT(zt));
317281075Sdim
318281075Sdim	/*
319281075Sdim	 * Obtain a reference to zt->loads_pending so that asyncload can
320281075Sdim	 * safely decrement both zt->references and zt->loads_pending
321281075Sdim	 * without going to zero.
322281075Sdim	 */
323281075Sdim	loads_pending = isc_refcount_increment0(&zt->loads_pending);
324281075Sdim	INSIST(loads_pending == 0);
325281075Sdim
326281075Sdim	/*
327281075Sdim	 * Only one dns_zt_asyncload call at a time should be active so
328284460Sjkim	 * these pointers should be NULL.  They are set back to NULL
329284460Sjkim	 * before the zt->loaddone (alldone) is called in call_loaddone.
330284460Sjkim	 */
331284460Sjkim	INSIST(zt->loadparams == NULL);
332284460Sjkim	INSIST(zt->loaddone == NULL);
333284460Sjkim	INSIST(zt->loaddone_arg == NULL);
334284460Sjkim
335284460Sjkim	zt->loadparams = isc_mem_get(zt->mctx, sizeof(struct zt_load_params));
336284460Sjkim	zt->loadparams->dl = doneloading;
337284460Sjkim	zt->loadparams->newonly = newonly;
338284460Sjkim	zt->loaddone = alldone;
339284460Sjkim	zt->loaddone_arg = arg;
340284460Sjkim
341284460Sjkim	result = dns_zt_apply(zt, isc_rwlocktype_read, false, NULL, asyncload,
342284460Sjkim			      zt);
343284460Sjkim
344284460Sjkim	/*
345284460Sjkim	 * Have all the loads completed?
346284460Sjkim	 */
347284460Sjkim	if (isc_refcount_decrement(&zt->loads_pending) == 1) {
348284460Sjkim		call_loaddone(zt);
349284460Sjkim	}
350284460Sjkim
351284460Sjkim	return (result);
352284460Sjkim}
353284460Sjkim
354246847Sjkim/*
355246847Sjkim * Initiates asynchronous loading of zone 'zone'.  'callback' is a
356246847Sjkim * pointer to a function which will be used to inform the caller when
357246847Sjkim * the zone loading is complete.
358246847Sjkim */
359246847Sjkimstatic isc_result_t
360246847Sjkimasyncload(dns_zone_t *zone, void *zt_) {
361281075Sdim	isc_result_t result;
362246847Sjkim	struct dns_zt *zt = (dns_zt_t *)zt_;
363246847Sjkim	REQUIRE(zone != NULL);
364281075Sdim
365246847Sjkim	isc_refcount_increment(&zt->references);
366246847Sjkim	isc_refcount_increment(&zt->loads_pending);
367246847Sjkim
368246847Sjkim	result = dns_zone_asyncload(zone, zt->loadparams->newonly,
369246847Sjkim				    *zt->loadparams->dl, zt);
370246847Sjkim	if (result != ISC_R_SUCCESS) {
371246847Sjkim		/*
372246847Sjkim		 * Caller is holding a reference to zt->loads_pending
373246847Sjkim		 * and zt->references so these can't decrement to zero.
374246847Sjkim		 */
375246847Sjkim		isc_refcount_decrement1(&zt->references);
376246847Sjkim		isc_refcount_decrement1(&zt->loads_pending);
377246847Sjkim	}
378246847Sjkim	return (ISC_R_SUCCESS);
379246847Sjkim}
380246847Sjkim
381246847Sjkimisc_result_t
382246847Sjkimdns_zt_freezezones(dns_zt_t *zt, dns_view_t *view, bool freeze) {
383246847Sjkim	isc_result_t result, tresult;
384246847Sjkim	struct zt_freeze_params params = { view, freeze };
385246847Sjkim
386249663Sjkim	REQUIRE(VALID_ZT(zt));
387249663Sjkim
388249663Sjkim	result = dns_zt_apply(zt, isc_rwlocktype_read, false, &tresult,
389249663Sjkim			      freezezones, &params);
390249663Sjkim	if (tresult == ISC_R_NOTFOUND) {
391249663Sjkim		tresult = ISC_R_SUCCESS;
392246847Sjkim	}
393246847Sjkim	return ((result == ISC_R_SUCCESS) ? tresult : result);
394246847Sjkim}
395246847Sjkim
396246847Sjkimstatic isc_result_t
397246847Sjkimfreezezones(dns_zone_t *zone, void *uap) {
398246847Sjkim	struct zt_freeze_params *params = uap;
399246847Sjkim	bool frozen;
400246847Sjkim	isc_result_t result = ISC_R_SUCCESS;
401246847Sjkim	char classstr[DNS_RDATACLASS_FORMATSIZE];
402246847Sjkim	char zonename[DNS_NAME_FORMATSIZE];
403246847Sjkim	dns_zone_t *raw = NULL;
404246847Sjkim	dns_view_t *view;
405246847Sjkim	const char *vname;
406246847Sjkim	const char *sep;
407246847Sjkim	int level;
408246847Sjkim
409246847Sjkim	dns_zone_getraw(zone, &raw);
410246847Sjkim	if (raw != NULL) {
411246847Sjkim		zone = raw;
412246847Sjkim	}
413246847Sjkim	if (params->view != dns_zone_getview(zone)) {
414246847Sjkim		if (raw != NULL) {
415246847Sjkim			dns_zone_detach(&raw);
416246847Sjkim		}
417246847Sjkim		return (ISC_R_SUCCESS);
418246847Sjkim	}
419246847Sjkim	if (dns_zone_gettype(zone) != dns_zone_primary) {
420246847Sjkim		if (raw != NULL) {
421246847Sjkim			dns_zone_detach(&raw);
422246847Sjkim		}
423246847Sjkim		return (ISC_R_SUCCESS);
424246847Sjkim	}
425246847Sjkim	if (!dns_zone_isdynamic(zone, true)) {
426246847Sjkim		if (raw != NULL) {
427246847Sjkim			dns_zone_detach(&raw);
428246847Sjkim		}
429246847Sjkim		return (ISC_R_SUCCESS);
430246847Sjkim	}
431246847Sjkim
432246847Sjkim	frozen = dns_zone_getupdatedisabled(zone);
433246847Sjkim	if (params->freeze) {
434246847Sjkim		if (frozen) {
435246847Sjkim			result = DNS_R_FROZEN;
436246847Sjkim		}
437246847Sjkim		if (result == ISC_R_SUCCESS) {
438246847Sjkim			result = dns_zone_flush(zone);
439246847Sjkim		}
440246847Sjkim		if (result == ISC_R_SUCCESS) {
441246847Sjkim			dns_zone_setupdatedisabled(zone, params->freeze);
442246847Sjkim		}
443246847Sjkim	} else {
444246847Sjkim		if (frozen) {
445246847Sjkim			result = dns_zone_loadandthaw(zone);
446246847Sjkim			if (result == DNS_R_CONTINUE ||
447246847Sjkim			    result == DNS_R_UPTODATE)
448246847Sjkim			{
449246847Sjkim				result = ISC_R_SUCCESS;
450246847Sjkim			}
451246847Sjkim		}
452246847Sjkim	}
453246847Sjkim	view = dns_zone_getview(zone);
454246847Sjkim	if (strcmp(view->name, "_bind") == 0 || strcmp(view->name, "_defaul"
455246847Sjkim								   "t") == 0)
456246847Sjkim	{
457284460Sjkim		vname = "";
458246847Sjkim		sep = "";
459246847Sjkim	} else {
460246847Sjkim		vname = view->name;
461246847Sjkim		sep = " ";
462246847Sjkim	}
463246847Sjkim	dns_rdataclass_format(dns_zone_getclass(zone), classstr,
464246847Sjkim			      sizeof(classstr));
465246847Sjkim	dns_name_format(dns_zone_getorigin(zone), zonename, sizeof(zonename));
466246847Sjkim	level = (result != ISC_R_SUCCESS) ? ISC_LOG_ERROR : ISC_LOG_DEBUG(1);
467246847Sjkim	isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL, DNS_LOGMODULE_ZONE,
468246847Sjkim		      level, "%s zone '%s/%s'%s%s: %s",
469246847Sjkim		      params->freeze ? "freezing" : "thawing", zonename,
470246847Sjkim		      classstr, sep, vname, isc_result_totext(result));
471246847Sjkim	if (raw != NULL) {
472246847Sjkim		dns_zone_detach(&raw);
473246847Sjkim	}
474246847Sjkim	return (result);
475246847Sjkim}
476246847Sjkim
477246847Sjkimvoid
478246847Sjkimdns_zt_setviewcommit(dns_zt_t *zt) {
479246847Sjkim	dns_rbtnode_t *node;
480246847Sjkim	dns_rbtnodechain_t chain;
481246847Sjkim	isc_result_t result;
482246847Sjkim
483246847Sjkim	REQUIRE(VALID_ZT(zt));
484246847Sjkim
485246847Sjkim	RWLOCK(&zt->rwlock, isc_rwlocktype_read);
486246847Sjkim	dns_rbtnodechain_init(&chain);
487246847Sjkim
488246847Sjkim	result = dns_rbtnodechain_first(&chain, zt->table, NULL, NULL);
489246847Sjkim	while (result == DNS_R_NEWORIGIN || result == ISC_R_SUCCESS) {
490246847Sjkim		result = dns_rbtnodechain_current(&chain, NULL, NULL, &node);
491246847Sjkim		if (result == ISC_R_SUCCESS && node->data != NULL) {
492246847Sjkim			dns_zone_setviewcommit(node->data);
493246847Sjkim		}
494246847Sjkim
495246847Sjkim		result = dns_rbtnodechain_next(&chain, NULL, NULL);
496246847Sjkim	}
497246847Sjkim
498246847Sjkim	dns_rbtnodechain_invalidate(&chain);
499246847Sjkim	RWUNLOCK(&zt->rwlock, isc_rwlocktype_read);
500246847Sjkim}
501246847Sjkim
502246847Sjkimvoid
503246847Sjkimdns_zt_setviewrevert(dns_zt_t *zt) {
504284460Sjkim	dns_rbtnode_t *node;
505284460Sjkim	dns_rbtnodechain_t chain;
506284460Sjkim	isc_result_t result;
507284460Sjkim
508284460Sjkim	REQUIRE(VALID_ZT(zt));
509284460Sjkim
510246847Sjkim	dns_rbtnodechain_init(&chain);
511246847Sjkim
512246847Sjkim	result = dns_rbtnodechain_first(&chain, zt->table, NULL, NULL);
513246847Sjkim	while (result == DNS_R_NEWORIGIN || result == ISC_R_SUCCESS) {
514246847Sjkim		result = dns_rbtnodechain_current(&chain, NULL, NULL, &node);
515246847Sjkim		if (result == ISC_R_SUCCESS && node->data != NULL) {
516246847Sjkim			dns_zone_setviewrevert(node->data);
517246847Sjkim		}
518246847Sjkim
519246847Sjkim		result = dns_rbtnodechain_next(&chain, NULL, NULL);
520246847Sjkim	}
521246847Sjkim
522246847Sjkim	dns_rbtnodechain_invalidate(&chain);
523246847Sjkim}
524246847Sjkim
525246847Sjkimisc_result_t
526246847Sjkimdns_zt_apply(dns_zt_t *zt, isc_rwlocktype_t lock, bool stop, isc_result_t *sub,
527246847Sjkim	     isc_result_t (*action)(dns_zone_t *, void *), void *uap) {
528246847Sjkim	dns_rbtnode_t *node;
529246847Sjkim	dns_rbtnodechain_t chain;
530246847Sjkim	isc_result_t result, tresult = ISC_R_SUCCESS;
531246847Sjkim	dns_zone_t *zone;
532246847Sjkim
533246847Sjkim	REQUIRE(VALID_ZT(zt));
534246847Sjkim	REQUIRE(action != NULL);
535246847Sjkim
536281075Sdim	if (lock != isc_rwlocktype_none) {
537281075Sdim		RWLOCK(&zt->rwlock, lock);
538281075Sdim	}
539281075Sdim
540281075Sdim	dns_rbtnodechain_init(&chain);
541281075Sdim	result = dns_rbtnodechain_first(&chain, zt->table, NULL, NULL);
542246847Sjkim	if (result == ISC_R_NOTFOUND) {
543246847Sjkim		/*
544246847Sjkim		 * The tree is empty.
545246847Sjkim		 */
546246847Sjkim		tresult = result;
547246847Sjkim		result = ISC_R_NOMORE;
548246847Sjkim	}
549246847Sjkim	while (result == DNS_R_NEWORIGIN || result == ISC_R_SUCCESS) {
550246847Sjkim		result = dns_rbtnodechain_current(&chain, NULL, NULL, &node);
551246847Sjkim		if (result == ISC_R_SUCCESS) {
552246847Sjkim			zone = node->data;
553246847Sjkim			if (zone != NULL) {
554246847Sjkim				result = (action)(zone, uap);
555246847Sjkim			}
556246847Sjkim			if (result != ISC_R_SUCCESS && stop) {
557246847Sjkim				tresult = result;
558246847Sjkim				goto cleanup; /* don't break */
559246847Sjkim			} else if (result != ISC_R_SUCCESS &&
560246847Sjkim				   tresult == ISC_R_SUCCESS)
561246847Sjkim			{
562246847Sjkim				tresult = result;
563246847Sjkim			}
564246847Sjkim		}
565246847Sjkim		result = dns_rbtnodechain_next(&chain, NULL, NULL);
566246847Sjkim	}
567284460Sjkim	if (result == ISC_R_NOMORE) {
568284460Sjkim		result = ISC_R_SUCCESS;
569284460Sjkim	}
570284460Sjkim
571284460Sjkimcleanup:
572284460Sjkim	dns_rbtnodechain_invalidate(&chain);
573284460Sjkim	if (sub != NULL) {
574284460Sjkim		*sub = tresult;
575284460Sjkim	}
576284460Sjkim
577284460Sjkim	if (lock != isc_rwlocktype_none) {
578284460Sjkim		RWUNLOCK(&zt->rwlock, lock);
579284460Sjkim	}
580284460Sjkim
581284460Sjkim	return (result);
582284460Sjkim}
583284460Sjkim
584284460Sjkim/*
585284460Sjkim * Decrement the loads_pending counter; when counter reaches
586246847Sjkim * zero, call the loaddone callback that was initially set by
587246847Sjkim * dns_zt_asyncload().
588281075Sdim */
589246847Sjkimstatic isc_result_t
590246847Sjkimdoneloading(dns_zt_t *zt, dns_zone_t *zone, isc_task_t *task) {
591246847Sjkim	UNUSED(zone);
592246847Sjkim	UNUSED(task);
593246847Sjkim
594246847Sjkim	REQUIRE(VALID_ZT(zt));
595246847Sjkim
596246847Sjkim	if (isc_refcount_decrement(&zt->loads_pending) == 1) {
597281075Sdim		call_loaddone(zt);
598281075Sdim	}
599281075Sdim
600281075Sdim	if (isc_refcount_decrement(&zt->references) == 1) {
601281075Sdim		zt_destroy(zt);
602281075Sdim	}
603246847Sjkim
604246847Sjkim	return (ISC_R_SUCCESS);
605246847Sjkim}
606246847Sjkim
607246847Sjkim/***
608246847Sjkim *** Private
609246847Sjkim ***/
610246847Sjkim
611246847Sjkimstatic void
612246847Sjkimauto_detach(void *data, void *arg) {
613246847Sjkim	dns_zone_t *zone = data;
614246847Sjkim
615246847Sjkim	UNUSED(arg);
616246847Sjkim	dns_zone_detach(&zone);
617281075Sdim}
618246847Sjkim