1135446Strhodes/*
2234010Sdougb * Copyright (C) 2004-2007, 2011, 2012  Internet Systems Consortium, Inc. ("ISC")
3135446Strhodes * Copyright (C) 1999-2002  Internet Software Consortium.
4135446Strhodes *
5193149Sdougb * Permission to use, copy, modify, and/or distribute this software for any
6135446Strhodes * purpose with or without fee is hereby granted, provided that the above
7135446Strhodes * copyright notice and this permission notice appear in all copies.
8135446Strhodes *
9135446Strhodes * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
10135446Strhodes * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
11135446Strhodes * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
12135446Strhodes * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
13135446Strhodes * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
14135446Strhodes * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
15135446Strhodes * PERFORMANCE OF THIS SOFTWARE.
16135446Strhodes */
17135446Strhodes
18234010Sdougb/* $Id$ */
19135446Strhodes
20170222Sdougb/*! \file */
21170222Sdougb
22135446Strhodes#include <config.h>
23135446Strhodes
24170222Sdougb#include <isc/file.h>
25135446Strhodes#include <isc/magic.h>
26135446Strhodes#include <isc/mem.h>
27170222Sdougb#include <isc/string.h>
28254897Serwin#include <isc/task.h>
29135446Strhodes#include <isc/util.h>
30135446Strhodes
31170222Sdougb#include <dns/log.h>
32170222Sdougb#include <dns/name.h>
33135446Strhodes#include <dns/rbt.h>
34170222Sdougb#include <dns/rdataclass.h>
35135446Strhodes#include <dns/result.h>
36170222Sdougb#include <dns/view.h>
37135446Strhodes#include <dns/zone.h>
38135446Strhodes#include <dns/zt.h>
39135446Strhodes
40135446Strhodesstruct dns_zt {
41135446Strhodes	/* Unlocked. */
42135446Strhodes	unsigned int		magic;
43135446Strhodes	isc_mem_t		*mctx;
44135446Strhodes	dns_rdataclass_t	rdclass;
45135446Strhodes	isc_rwlock_t		rwlock;
46254897Serwin	dns_zt_allloaded_t	loaddone;
47254897Serwin	void *			loaddone_arg;
48135446Strhodes	/* Locked by lock. */
49254897Serwin	isc_boolean_t		flush;
50135446Strhodes	isc_uint32_t		references;
51254897Serwin	unsigned int		loads_pending;
52135446Strhodes	dns_rbt_t		*table;
53135446Strhodes};
54135446Strhodes
55135446Strhodes#define ZTMAGIC			ISC_MAGIC('Z', 'T', 'b', 'l')
56135446Strhodes#define VALID_ZT(zt) 		ISC_MAGIC_VALID(zt, ZTMAGIC)
57135446Strhodes
58135446Strhodesstatic void
59135446Strhodesauto_detach(void *, void *);
60135446Strhodes
61135446Strhodesstatic isc_result_t
62135446Strhodesload(dns_zone_t *zone, void *uap);
63135446Strhodes
64135446Strhodesstatic isc_result_t
65254897Serwinasyncload(dns_zone_t *zone, void *callback);
66254897Serwin
67254897Serwinstatic isc_result_t
68135446Strhodesloadnew(dns_zone_t *zone, void *uap);
69135446Strhodes
70170222Sdougbstatic isc_result_t
71170222Sdougbfreezezones(dns_zone_t *zone, void *uap);
72170222Sdougb
73254897Serwinstatic isc_result_t
74254897Serwindoneloading(dns_zt_t *zt, dns_zone_t *zone, isc_task_t *task);
75254897Serwin
76135446Strhodesisc_result_t
77193149Sdougbdns_zt_create(isc_mem_t *mctx, dns_rdataclass_t rdclass, dns_zt_t **ztp)
78193149Sdougb{
79135446Strhodes	dns_zt_t *zt;
80135446Strhodes	isc_result_t result;
81135446Strhodes
82135446Strhodes	REQUIRE(ztp != NULL && *ztp == NULL);
83135446Strhodes
84135446Strhodes	zt = isc_mem_get(mctx, sizeof(*zt));
85135446Strhodes	if (zt == NULL)
86135446Strhodes		return (ISC_R_NOMEMORY);
87135446Strhodes
88135446Strhodes	zt->table = NULL;
89135446Strhodes	result = dns_rbt_create(mctx, auto_detach, zt, &zt->table);
90135446Strhodes	if (result != ISC_R_SUCCESS)
91135446Strhodes		goto cleanup_zt;
92135446Strhodes
93135446Strhodes	result = isc_rwlock_init(&zt->rwlock, 0, 0);
94170222Sdougb	if (result != ISC_R_SUCCESS)
95135446Strhodes		goto cleanup_rbt;
96135446Strhodes
97254897Serwin	zt->mctx = NULL;
98254897Serwin	isc_mem_attach(mctx, &zt->mctx);
99135446Strhodes	zt->references = 1;
100254897Serwin	zt->flush = ISC_FALSE;
101135446Strhodes	zt->rdclass = rdclass;
102135446Strhodes	zt->magic = ZTMAGIC;
103254897Serwin	zt->loaddone = NULL;
104254897Serwin	zt->loaddone_arg = NULL;
105254897Serwin	zt->loads_pending = 0;
106135446Strhodes	*ztp = zt;
107135446Strhodes
108135446Strhodes	return (ISC_R_SUCCESS);
109135446Strhodes
110135446Strhodes   cleanup_rbt:
111135446Strhodes	dns_rbt_destroy(&zt->table);
112135446Strhodes
113135446Strhodes   cleanup_zt:
114135446Strhodes	isc_mem_put(mctx, zt, sizeof(*zt));
115135446Strhodes
116135446Strhodes	return (result);
117135446Strhodes}
118135446Strhodes
119135446Strhodesisc_result_t
120135446Strhodesdns_zt_mount(dns_zt_t *zt, dns_zone_t *zone) {
121135446Strhodes	isc_result_t result;
122135446Strhodes	dns_zone_t *dummy = NULL;
123135446Strhodes	dns_name_t *name;
124135446Strhodes
125135446Strhodes	REQUIRE(VALID_ZT(zt));
126135446Strhodes
127135446Strhodes	name = dns_zone_getorigin(zone);
128135446Strhodes
129135446Strhodes	RWLOCK(&zt->rwlock, isc_rwlocktype_write);
130135446Strhodes
131135446Strhodes	result = dns_rbt_addname(zt->table, name, zone);
132135446Strhodes	if (result == ISC_R_SUCCESS)
133135446Strhodes		dns_zone_attach(zone, &dummy);
134135446Strhodes
135135446Strhodes	RWUNLOCK(&zt->rwlock, isc_rwlocktype_write);
136135446Strhodes
137135446Strhodes	return (result);
138135446Strhodes}
139135446Strhodes
140135446Strhodesisc_result_t
141135446Strhodesdns_zt_unmount(dns_zt_t *zt, dns_zone_t *zone) {
142135446Strhodes	isc_result_t result;
143135446Strhodes	dns_name_t *name;
144135446Strhodes
145135446Strhodes	REQUIRE(VALID_ZT(zt));
146135446Strhodes
147135446Strhodes	name = dns_zone_getorigin(zone);
148135446Strhodes
149135446Strhodes	RWLOCK(&zt->rwlock, isc_rwlocktype_write);
150135446Strhodes
151135446Strhodes	result = dns_rbt_deletename(zt->table, name, ISC_FALSE);
152135446Strhodes
153135446Strhodes	RWUNLOCK(&zt->rwlock, isc_rwlocktype_write);
154135446Strhodes
155135446Strhodes	return (result);
156135446Strhodes}
157135446Strhodes
158135446Strhodesisc_result_t
159135446Strhodesdns_zt_find(dns_zt_t *zt, dns_name_t *name, unsigned int options,
160135446Strhodes	    dns_name_t *foundname, dns_zone_t **zonep)
161135446Strhodes{
162135446Strhodes	isc_result_t result;
163135446Strhodes	dns_zone_t *dummy = NULL;
164135446Strhodes	unsigned int rbtoptions = 0;
165135446Strhodes
166135446Strhodes	REQUIRE(VALID_ZT(zt));
167135446Strhodes
168135446Strhodes	if ((options & DNS_ZTFIND_NOEXACT) != 0)
169135446Strhodes		rbtoptions |= DNS_RBTFIND_NOEXACT;
170135446Strhodes
171135446Strhodes	RWLOCK(&zt->rwlock, isc_rwlocktype_read);
172135446Strhodes
173135446Strhodes	result = dns_rbt_findname(zt->table, name, rbtoptions, foundname,
174135446Strhodes				  (void **) (void*)&dummy);
175135446Strhodes	if (result == ISC_R_SUCCESS || result == DNS_R_PARTIALMATCH)
176135446Strhodes		dns_zone_attach(dummy, zonep);
177135446Strhodes
178135446Strhodes	RWUNLOCK(&zt->rwlock, isc_rwlocktype_read);
179135446Strhodes
180135446Strhodes	return (result);
181135446Strhodes}
182135446Strhodes
183135446Strhodesvoid
184135446Strhodesdns_zt_attach(dns_zt_t *zt, dns_zt_t **ztp) {
185135446Strhodes
186135446Strhodes	REQUIRE(VALID_ZT(zt));
187135446Strhodes	REQUIRE(ztp != NULL && *ztp == NULL);
188135446Strhodes
189135446Strhodes	RWLOCK(&zt->rwlock, isc_rwlocktype_write);
190135446Strhodes
191135446Strhodes	INSIST(zt->references > 0);
192135446Strhodes	zt->references++;
193135446Strhodes	INSIST(zt->references != 0);
194135446Strhodes
195135446Strhodes	RWUNLOCK(&zt->rwlock, isc_rwlocktype_write);
196135446Strhodes
197135446Strhodes	*ztp = zt;
198135446Strhodes}
199135446Strhodes
200135446Strhodesstatic isc_result_t
201135446Strhodesflush(dns_zone_t *zone, void *uap) {
202135446Strhodes	UNUSED(uap);
203135446Strhodes	return (dns_zone_flush(zone));
204135446Strhodes}
205135446Strhodes
206135446Strhodesstatic void
207254897Serwinzt_destroy(dns_zt_t *zt) {
208254897Serwin	if (zt->flush)
209254897Serwin		(void)dns_zt_apply(zt, ISC_FALSE, flush, NULL);
210254897Serwin	dns_rbt_destroy(&zt->table);
211254897Serwin	isc_rwlock_destroy(&zt->rwlock);
212254897Serwin	zt->magic = 0;
213254897Serwin	isc_mem_putanddetach(&zt->mctx, zt, sizeof(*zt));
214254897Serwin}
215254897Serwin
216254897Serwinstatic void
217135446Strhodeszt_flushanddetach(dns_zt_t **ztp, isc_boolean_t need_flush) {
218135446Strhodes	isc_boolean_t destroy = ISC_FALSE;
219135446Strhodes	dns_zt_t *zt;
220135446Strhodes
221135446Strhodes	REQUIRE(ztp != NULL && VALID_ZT(*ztp));
222135446Strhodes
223135446Strhodes	zt = *ztp;
224135446Strhodes
225135446Strhodes	RWLOCK(&zt->rwlock, isc_rwlocktype_write);
226135446Strhodes
227135446Strhodes	INSIST(zt->references > 0);
228135446Strhodes	zt->references--;
229135446Strhodes	if (zt->references == 0)
230135446Strhodes		destroy = ISC_TRUE;
231254897Serwin	if (need_flush)
232254897Serwin		zt->flush = ISC_TRUE;
233135446Strhodes
234135446Strhodes	RWUNLOCK(&zt->rwlock, isc_rwlocktype_write);
235135446Strhodes
236254897Serwin	if (destroy)
237254897Serwin		zt_destroy(zt);
238135446Strhodes
239135446Strhodes	*ztp = NULL;
240135446Strhodes}
241135446Strhodes
242135446Strhodesvoid
243135446Strhodesdns_zt_flushanddetach(dns_zt_t **ztp) {
244135446Strhodes	zt_flushanddetach(ztp, ISC_TRUE);
245135446Strhodes}
246135446Strhodes
247135446Strhodesvoid
248135446Strhodesdns_zt_detach(dns_zt_t **ztp) {
249135446Strhodes	zt_flushanddetach(ztp, ISC_FALSE);
250135446Strhodes}
251135446Strhodes
252135446Strhodesisc_result_t
253135446Strhodesdns_zt_load(dns_zt_t *zt, isc_boolean_t stop) {
254135446Strhodes	isc_result_t result;
255135446Strhodes
256135446Strhodes	REQUIRE(VALID_ZT(zt));
257135446Strhodes
258135446Strhodes	RWLOCK(&zt->rwlock, isc_rwlocktype_read);
259135446Strhodes	result = dns_zt_apply(zt, stop, load, NULL);
260135446Strhodes	RWUNLOCK(&zt->rwlock, isc_rwlocktype_read);
261135446Strhodes	return (result);
262135446Strhodes}
263135446Strhodes
264135446Strhodesstatic isc_result_t
265135446Strhodesload(dns_zone_t *zone, void *uap) {
266135446Strhodes	isc_result_t result;
267135446Strhodes	UNUSED(uap);
268254897Serwin
269135446Strhodes	result = dns_zone_load(zone);
270135446Strhodes	if (result == DNS_R_CONTINUE || result == DNS_R_UPTODATE)
271135446Strhodes		result = ISC_R_SUCCESS;
272254897Serwin
273135446Strhodes	return (result);
274135446Strhodes}
275135446Strhodes
276135446Strhodesisc_result_t
277254897Serwindns_zt_asyncload(dns_zt_t *zt, dns_zt_allloaded_t alldone, void *arg) {
278254897Serwin	isc_result_t result;
279254897Serwin	static dns_zt_zoneloaded_t dl = doneloading;
280254897Serwin	int pending;
281254897Serwin
282254897Serwin	REQUIRE(VALID_ZT(zt));
283254897Serwin
284254897Serwin	RWLOCK(&zt->rwlock, isc_rwlocktype_write);
285254897Serwin
286254897Serwin	INSIST(zt->loads_pending == 0);
287254897Serwin	result = dns_zt_apply2(zt, ISC_FALSE, NULL, asyncload, &dl);
288254897Serwin
289254897Serwin	pending = zt->loads_pending;
290254897Serwin	if (pending != 0) {
291254897Serwin		zt->loaddone = alldone;
292254897Serwin		zt->loaddone_arg = arg;
293254897Serwin	}
294254897Serwin
295254897Serwin	RWUNLOCK(&zt->rwlock, isc_rwlocktype_write);
296254897Serwin
297254897Serwin	if (pending == 0)
298254897Serwin		alldone(arg);
299254897Serwin
300254897Serwin	return (result);
301254897Serwin}
302254897Serwin
303254897Serwin/*
304254897Serwin * Initiates asynchronous loading of zone 'zone'.  'callback' is a
305254897Serwin * pointer to a function which will be used to inform the caller when
306254897Serwin * the zone loading is complete.
307254897Serwin */
308254897Serwinstatic isc_result_t
309254897Serwinasyncload(dns_zone_t *zone, void *callback) {
310254897Serwin	isc_result_t result;
311254897Serwin	dns_zt_zoneloaded_t *loaded = callback;
312254897Serwin	dns_zt_t *zt;
313254897Serwin
314254897Serwin	REQUIRE(zone != NULL);
315254897Serwin	zt = dns_zone_getview(zone)->zonetable;
316254897Serwin	INSIST(VALID_ZT(zt));
317254897Serwin
318254897Serwin	result = dns_zone_asyncload(zone, *loaded, zt);
319254897Serwin	if (result == ISC_R_SUCCESS) {
320254897Serwin		INSIST(zt->references > 0);
321254897Serwin		zt->references++;
322254897Serwin		INSIST(zt->references != 0);
323254897Serwin		zt->loads_pending++;
324254897Serwin	}
325254897Serwin	return (ISC_R_SUCCESS);
326254897Serwin}
327254897Serwin
328254897Serwinisc_result_t
329135446Strhodesdns_zt_loadnew(dns_zt_t *zt, isc_boolean_t stop) {
330135446Strhodes	isc_result_t result;
331135446Strhodes
332135446Strhodes	REQUIRE(VALID_ZT(zt));
333135446Strhodes
334135446Strhodes	RWLOCK(&zt->rwlock, isc_rwlocktype_read);
335135446Strhodes	result = dns_zt_apply(zt, stop, loadnew, NULL);
336135446Strhodes	RWUNLOCK(&zt->rwlock, isc_rwlocktype_read);
337135446Strhodes	return (result);
338135446Strhodes}
339135446Strhodes
340135446Strhodesstatic isc_result_t
341135446Strhodesloadnew(dns_zone_t *zone, void *uap) {
342135446Strhodes	isc_result_t result;
343135446Strhodes	UNUSED(uap);
344254897Serwin
345135446Strhodes	result = dns_zone_loadnew(zone);
346135446Strhodes	if (result == DNS_R_CONTINUE || result == DNS_R_UPTODATE ||
347135446Strhodes	    result == DNS_R_DYNAMIC)
348135446Strhodes		result = ISC_R_SUCCESS;
349135446Strhodes	return (result);
350135446Strhodes}
351135446Strhodes
352135446Strhodesisc_result_t
353170222Sdougbdns_zt_freezezones(dns_zt_t *zt, isc_boolean_t freeze) {
354170222Sdougb	isc_result_t result, tresult;
355170222Sdougb
356170222Sdougb	REQUIRE(VALID_ZT(zt));
357170222Sdougb
358170222Sdougb	RWLOCK(&zt->rwlock, isc_rwlocktype_read);
359170222Sdougb	result = dns_zt_apply2(zt, ISC_FALSE, &tresult, freezezones, &freeze);
360170222Sdougb	RWUNLOCK(&zt->rwlock, isc_rwlocktype_read);
361254897Serwin	if (tresult == ISC_R_NOTFOUND)
362254897Serwin		tresult = ISC_R_SUCCESS;
363170222Sdougb	return ((result == ISC_R_SUCCESS) ? tresult : result);
364170222Sdougb}
365170222Sdougb
366170222Sdougbstatic isc_result_t
367170222Sdougbfreezezones(dns_zone_t *zone, void *uap) {
368170222Sdougb	isc_boolean_t freeze = *(isc_boolean_t *)uap;
369170222Sdougb	isc_boolean_t frozen;
370170222Sdougb	isc_result_t result = ISC_R_SUCCESS;
371170222Sdougb	char classstr[DNS_RDATACLASS_FORMATSIZE];
372170222Sdougb	char zonename[DNS_NAME_FORMATSIZE];
373254897Serwin	dns_zone_t *raw = NULL;
374170222Sdougb	dns_view_t *view;
375170222Sdougb	const char *vname;
376170222Sdougb	const char *sep;
377170222Sdougb	int level;
378170222Sdougb
379254897Serwin	dns_zone_getraw(zone, &raw);
380254897Serwin	if (raw != NULL)
381254897Serwin		zone = raw;
382254897Serwin	if (dns_zone_gettype(zone) != dns_zone_master) {
383254897Serwin		if (raw != NULL)
384254897Serwin			dns_zone_detach(&raw);
385170222Sdougb		return (ISC_R_SUCCESS);
386254897Serwin	}
387254897Serwin	if (!dns_zone_isdynamic(zone, ISC_TRUE)) {
388254897Serwin		if (raw != NULL)
389254897Serwin			dns_zone_detach(&raw);
390254897Serwin		return (ISC_R_SUCCESS);
391254897Serwin	}
392170222Sdougb
393170222Sdougb	frozen = dns_zone_getupdatedisabled(zone);
394170222Sdougb	if (freeze) {
395170222Sdougb		if (frozen)
396170222Sdougb			result = DNS_R_FROZEN;
397170222Sdougb		if (result == ISC_R_SUCCESS)
398170222Sdougb			result = dns_zone_flush(zone);
399170222Sdougb	} else {
400170222Sdougb		if (frozen) {
401170222Sdougb			result = dns_zone_load(zone);
402170222Sdougb			if (result == DNS_R_CONTINUE ||
403170222Sdougb			    result == DNS_R_UPTODATE)
404170222Sdougb				result = ISC_R_SUCCESS;
405170222Sdougb		}
406170222Sdougb	}
407170222Sdougb	if (result == ISC_R_SUCCESS)
408170222Sdougb		dns_zone_setupdatedisabled(zone, freeze);
409170222Sdougb	view = dns_zone_getview(zone);
410170222Sdougb	if (strcmp(view->name, "_bind") == 0 ||
411170222Sdougb	    strcmp(view->name, "_default") == 0)
412170222Sdougb	{
413170222Sdougb		vname = "";
414170222Sdougb		sep = "";
415170222Sdougb	} else {
416170222Sdougb		vname = view->name;
417170222Sdougb		sep = " ";
418170222Sdougb	}
419170222Sdougb	dns_rdataclass_format(dns_zone_getclass(zone), classstr,
420170222Sdougb			      sizeof(classstr));
421170222Sdougb	dns_name_format(dns_zone_getorigin(zone), zonename, sizeof(zonename));
422170222Sdougb	level = (result != ISC_R_SUCCESS) ? ISC_LOG_ERROR : ISC_LOG_DEBUG(1);
423170222Sdougb	isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL, DNS_LOGMODULE_ZONE,
424170222Sdougb		      level, "%s zone '%s/%s'%s%s: %s",
425170222Sdougb		      freeze ? "freezing" : "thawing",
426170222Sdougb		      zonename, classstr, sep, vname,
427170222Sdougb		      isc_result_totext(result));
428254897Serwin	if (raw != NULL)
429254897Serwin		dns_zone_detach(&raw);
430170222Sdougb	return (result);
431170222Sdougb}
432170222Sdougb
433170222Sdougbisc_result_t
434135446Strhodesdns_zt_apply(dns_zt_t *zt, isc_boolean_t stop,
435135446Strhodes	     isc_result_t (*action)(dns_zone_t *, void *), void *uap)
436135446Strhodes{
437170222Sdougb	return (dns_zt_apply2(zt, stop, NULL, action, uap));
438170222Sdougb}
439170222Sdougb
440170222Sdougbisc_result_t
441170222Sdougbdns_zt_apply2(dns_zt_t *zt, isc_boolean_t stop, isc_result_t *sub,
442170222Sdougb	      isc_result_t (*action)(dns_zone_t *, void *), void *uap)
443170222Sdougb{
444135446Strhodes	dns_rbtnode_t *node;
445135446Strhodes	dns_rbtnodechain_t chain;
446170222Sdougb	isc_result_t result, tresult = ISC_R_SUCCESS;
447135446Strhodes	dns_zone_t *zone;
448135446Strhodes
449135446Strhodes	REQUIRE(VALID_ZT(zt));
450135446Strhodes	REQUIRE(action != NULL);
451135446Strhodes
452135446Strhodes	dns_rbtnodechain_init(&chain, zt->mctx);
453135446Strhodes	result = dns_rbtnodechain_first(&chain, zt->table, NULL, NULL);
454135446Strhodes	if (result == ISC_R_NOTFOUND) {
455135446Strhodes		/*
456135446Strhodes		 * The tree is empty.
457135446Strhodes		 */
458254897Serwin		tresult = result;
459135446Strhodes		result = ISC_R_NOMORE;
460135446Strhodes	}
461135446Strhodes	while (result == DNS_R_NEWORIGIN || result == ISC_R_SUCCESS) {
462135446Strhodes		result = dns_rbtnodechain_current(&chain, NULL, NULL,
463135446Strhodes						  &node);
464135446Strhodes		if (result == ISC_R_SUCCESS) {
465135446Strhodes			zone = node->data;
466135446Strhodes			if (zone != NULL)
467135446Strhodes				result = (action)(zone, uap);
468170222Sdougb			if (result != ISC_R_SUCCESS && stop) {
469170222Sdougb				tresult = result;
470135446Strhodes				goto cleanup;	/* don't break */
471170222Sdougb			} else if (result != ISC_R_SUCCESS &&
472170222Sdougb				   tresult == ISC_R_SUCCESS)
473170222Sdougb				tresult = result;
474135446Strhodes		}
475135446Strhodes		result = dns_rbtnodechain_next(&chain, NULL, NULL);
476135446Strhodes	}
477135446Strhodes	if (result == ISC_R_NOMORE)
478135446Strhodes		result = ISC_R_SUCCESS;
479135446Strhodes
480135446Strhodes cleanup:
481135446Strhodes	dns_rbtnodechain_invalidate(&chain);
482170222Sdougb	if (sub != NULL)
483170222Sdougb		*sub = tresult;
484135446Strhodes
485135446Strhodes	return (result);
486135446Strhodes}
487135446Strhodes
488254897Serwin/*
489254897Serwin * Decrement the loads_pending counter; when counter reaches
490254897Serwin * zero, call the loaddone callback that was initially set by
491254897Serwin * dns_zt_asyncload().
492254897Serwin */
493254897Serwinstatic isc_result_t
494254897Serwindoneloading(dns_zt_t *zt, dns_zone_t *zone, isc_task_t *task) {
495254897Serwin	isc_boolean_t destroy = ISC_FALSE;
496254897Serwin	dns_zt_allloaded_t alldone = NULL;
497254897Serwin	void *arg = NULL;
498254897Serwin
499254897Serwin	UNUSED(zone);
500254897Serwin	UNUSED(task);
501254897Serwin
502254897Serwin	REQUIRE(VALID_ZT(zt));
503254897Serwin
504254897Serwin	RWLOCK(&zt->rwlock, isc_rwlocktype_write);
505254897Serwin	INSIST(zt->loads_pending != 0);
506254897Serwin	INSIST(zt->references != 0);
507254897Serwin	zt->references--;
508254897Serwin	if (zt->references == 0)
509254897Serwin		destroy = ISC_TRUE;
510254897Serwin	zt->loads_pending--;
511254897Serwin	if (zt->loads_pending == 0) {
512254897Serwin		alldone = zt->loaddone;
513254897Serwin		arg = zt->loaddone_arg;
514254897Serwin		zt->loaddone = NULL;
515254897Serwin		zt->loaddone_arg = NULL;
516254897Serwin	}
517254897Serwin	RWUNLOCK(&zt->rwlock, isc_rwlocktype_write);
518254897Serwin
519254897Serwin	if (alldone != NULL)
520254897Serwin		alldone(arg);
521254897Serwin
522254897Serwin	if (destroy)
523254897Serwin		zt_destroy(zt);
524254897Serwin
525254897Serwin	return (ISC_R_SUCCESS);
526254897Serwin}
527254897Serwin
528135446Strhodes/***
529135446Strhodes *** Private
530135446Strhodes ***/
531135446Strhodes
532135446Strhodesstatic void
533135446Strhodesauto_detach(void *data, void *arg) {
534135446Strhodes	dns_zone_t *zone = data;
535135446Strhodes
536135446Strhodes	UNUSED(arg);
537135446Strhodes
538135446Strhodes	dns_zone_detach(&zone);
539135446Strhodes}
540