1/*
2 * Copyright (C) 2004-2007, 2011, 2012  Internet Systems Consortium, Inc. ("ISC")
3 * Copyright (C) 1999-2002  Internet Software Consortium.
4 *
5 * Permission to use, copy, modify, and/or distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
10 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
11 * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
12 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
13 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
14 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
15 * PERFORMANCE OF THIS SOFTWARE.
16 */
17
18/* $Id$ */
19
20/*! \file */
21
22#include <config.h>
23
24#include <isc/file.h>
25#include <isc/magic.h>
26#include <isc/mem.h>
27#include <isc/string.h>
28#include <isc/task.h>
29#include <isc/util.h>
30
31#include <dns/log.h>
32#include <dns/name.h>
33#include <dns/rbt.h>
34#include <dns/rdataclass.h>
35#include <dns/result.h>
36#include <dns/view.h>
37#include <dns/zone.h>
38#include <dns/zt.h>
39
40struct dns_zt {
41	/* Unlocked. */
42	unsigned int		magic;
43	isc_mem_t		*mctx;
44	dns_rdataclass_t	rdclass;
45	isc_rwlock_t		rwlock;
46	dns_zt_allloaded_t	loaddone;
47	void *			loaddone_arg;
48	/* Locked by lock. */
49	isc_boolean_t		flush;
50	isc_uint32_t		references;
51	unsigned int		loads_pending;
52	dns_rbt_t		*table;
53};
54
55#define ZTMAGIC			ISC_MAGIC('Z', 'T', 'b', 'l')
56#define VALID_ZT(zt) 		ISC_MAGIC_VALID(zt, ZTMAGIC)
57
58static void
59auto_detach(void *, void *);
60
61static isc_result_t
62load(dns_zone_t *zone, void *uap);
63
64static isc_result_t
65asyncload(dns_zone_t *zone, void *callback);
66
67static isc_result_t
68loadnew(dns_zone_t *zone, void *uap);
69
70static isc_result_t
71freezezones(dns_zone_t *zone, void *uap);
72
73static isc_result_t
74doneloading(dns_zt_t *zt, dns_zone_t *zone, isc_task_t *task);
75
76isc_result_t
77dns_zt_create(isc_mem_t *mctx, dns_rdataclass_t rdclass, dns_zt_t **ztp)
78{
79	dns_zt_t *zt;
80	isc_result_t result;
81
82	REQUIRE(ztp != NULL && *ztp == NULL);
83
84	zt = isc_mem_get(mctx, sizeof(*zt));
85	if (zt == NULL)
86		return (ISC_R_NOMEMORY);
87
88	zt->table = NULL;
89	result = dns_rbt_create(mctx, auto_detach, zt, &zt->table);
90	if (result != ISC_R_SUCCESS)
91		goto cleanup_zt;
92
93	result = isc_rwlock_init(&zt->rwlock, 0, 0);
94	if (result != ISC_R_SUCCESS)
95		goto cleanup_rbt;
96
97	zt->mctx = NULL;
98	isc_mem_attach(mctx, &zt->mctx);
99	zt->references = 1;
100	zt->flush = ISC_FALSE;
101	zt->rdclass = rdclass;
102	zt->magic = ZTMAGIC;
103	zt->loaddone = NULL;
104	zt->loaddone_arg = NULL;
105	zt->loads_pending = 0;
106	*ztp = zt;
107
108	return (ISC_R_SUCCESS);
109
110   cleanup_rbt:
111	dns_rbt_destroy(&zt->table);
112
113   cleanup_zt:
114	isc_mem_put(mctx, zt, sizeof(*zt));
115
116	return (result);
117}
118
119isc_result_t
120dns_zt_mount(dns_zt_t *zt, dns_zone_t *zone) {
121	isc_result_t result;
122	dns_zone_t *dummy = NULL;
123	dns_name_t *name;
124
125	REQUIRE(VALID_ZT(zt));
126
127	name = dns_zone_getorigin(zone);
128
129	RWLOCK(&zt->rwlock, isc_rwlocktype_write);
130
131	result = dns_rbt_addname(zt->table, name, zone);
132	if (result == ISC_R_SUCCESS)
133		dns_zone_attach(zone, &dummy);
134
135	RWUNLOCK(&zt->rwlock, isc_rwlocktype_write);
136
137	return (result);
138}
139
140isc_result_t
141dns_zt_unmount(dns_zt_t *zt, dns_zone_t *zone) {
142	isc_result_t result;
143	dns_name_t *name;
144
145	REQUIRE(VALID_ZT(zt));
146
147	name = dns_zone_getorigin(zone);
148
149	RWLOCK(&zt->rwlock, isc_rwlocktype_write);
150
151	result = dns_rbt_deletename(zt->table, name, ISC_FALSE);
152
153	RWUNLOCK(&zt->rwlock, isc_rwlocktype_write);
154
155	return (result);
156}
157
158isc_result_t
159dns_zt_find(dns_zt_t *zt, dns_name_t *name, unsigned int options,
160	    dns_name_t *foundname, dns_zone_t **zonep)
161{
162	isc_result_t result;
163	dns_zone_t *dummy = NULL;
164	unsigned int rbtoptions = 0;
165
166	REQUIRE(VALID_ZT(zt));
167
168	if ((options & DNS_ZTFIND_NOEXACT) != 0)
169		rbtoptions |= DNS_RBTFIND_NOEXACT;
170
171	RWLOCK(&zt->rwlock, isc_rwlocktype_read);
172
173	result = dns_rbt_findname(zt->table, name, rbtoptions, foundname,
174				  (void **) (void*)&dummy);
175	if (result == ISC_R_SUCCESS || result == DNS_R_PARTIALMATCH)
176		dns_zone_attach(dummy, zonep);
177
178	RWUNLOCK(&zt->rwlock, isc_rwlocktype_read);
179
180	return (result);
181}
182
183void
184dns_zt_attach(dns_zt_t *zt, dns_zt_t **ztp) {
185
186	REQUIRE(VALID_ZT(zt));
187	REQUIRE(ztp != NULL && *ztp == NULL);
188
189	RWLOCK(&zt->rwlock, isc_rwlocktype_write);
190
191	INSIST(zt->references > 0);
192	zt->references++;
193	INSIST(zt->references != 0);
194
195	RWUNLOCK(&zt->rwlock, isc_rwlocktype_write);
196
197	*ztp = zt;
198}
199
200static isc_result_t
201flush(dns_zone_t *zone, void *uap) {
202	UNUSED(uap);
203	return (dns_zone_flush(zone));
204}
205
206static void
207zt_destroy(dns_zt_t *zt) {
208	if (zt->flush)
209		(void)dns_zt_apply(zt, ISC_FALSE, flush, NULL);
210	dns_rbt_destroy(&zt->table);
211	isc_rwlock_destroy(&zt->rwlock);
212	zt->magic = 0;
213	isc_mem_putanddetach(&zt->mctx, zt, sizeof(*zt));
214}
215
216static void
217zt_flushanddetach(dns_zt_t **ztp, isc_boolean_t need_flush) {
218	isc_boolean_t destroy = ISC_FALSE;
219	dns_zt_t *zt;
220
221	REQUIRE(ztp != NULL && VALID_ZT(*ztp));
222
223	zt = *ztp;
224
225	RWLOCK(&zt->rwlock, isc_rwlocktype_write);
226
227	INSIST(zt->references > 0);
228	zt->references--;
229	if (zt->references == 0)
230		destroy = ISC_TRUE;
231	if (need_flush)
232		zt->flush = ISC_TRUE;
233
234	RWUNLOCK(&zt->rwlock, isc_rwlocktype_write);
235
236	if (destroy)
237		zt_destroy(zt);
238
239	*ztp = NULL;
240}
241
242void
243dns_zt_flushanddetach(dns_zt_t **ztp) {
244	zt_flushanddetach(ztp, ISC_TRUE);
245}
246
247void
248dns_zt_detach(dns_zt_t **ztp) {
249	zt_flushanddetach(ztp, ISC_FALSE);
250}
251
252isc_result_t
253dns_zt_load(dns_zt_t *zt, isc_boolean_t stop) {
254	isc_result_t result;
255
256	REQUIRE(VALID_ZT(zt));
257
258	RWLOCK(&zt->rwlock, isc_rwlocktype_read);
259	result = dns_zt_apply(zt, stop, load, NULL);
260	RWUNLOCK(&zt->rwlock, isc_rwlocktype_read);
261	return (result);
262}
263
264static isc_result_t
265load(dns_zone_t *zone, void *uap) {
266	isc_result_t result;
267	UNUSED(uap);
268
269	result = dns_zone_load(zone);
270	if (result == DNS_R_CONTINUE || result == DNS_R_UPTODATE)
271		result = ISC_R_SUCCESS;
272
273	return (result);
274}
275
276isc_result_t
277dns_zt_asyncload(dns_zt_t *zt, dns_zt_allloaded_t alldone, void *arg) {
278	isc_result_t result;
279	static dns_zt_zoneloaded_t dl = doneloading;
280	int pending;
281
282	REQUIRE(VALID_ZT(zt));
283
284	RWLOCK(&zt->rwlock, isc_rwlocktype_write);
285
286	INSIST(zt->loads_pending == 0);
287	result = dns_zt_apply2(zt, ISC_FALSE, NULL, asyncload, &dl);
288
289	pending = zt->loads_pending;
290	if (pending != 0) {
291		zt->loaddone = alldone;
292		zt->loaddone_arg = arg;
293	}
294
295	RWUNLOCK(&zt->rwlock, isc_rwlocktype_write);
296
297	if (pending == 0)
298		alldone(arg);
299
300	return (result);
301}
302
303/*
304 * Initiates asynchronous loading of zone 'zone'.  'callback' is a
305 * pointer to a function which will be used to inform the caller when
306 * the zone loading is complete.
307 */
308static isc_result_t
309asyncload(dns_zone_t *zone, void *callback) {
310	isc_result_t result;
311	dns_zt_zoneloaded_t *loaded = callback;
312	dns_zt_t *zt;
313
314	REQUIRE(zone != NULL);
315	zt = dns_zone_getview(zone)->zonetable;
316	INSIST(VALID_ZT(zt));
317
318	result = dns_zone_asyncload(zone, *loaded, zt);
319	if (result == ISC_R_SUCCESS) {
320		INSIST(zt->references > 0);
321		zt->references++;
322		INSIST(zt->references != 0);
323		zt->loads_pending++;
324	}
325	return (ISC_R_SUCCESS);
326}
327
328isc_result_t
329dns_zt_loadnew(dns_zt_t *zt, isc_boolean_t stop) {
330	isc_result_t result;
331
332	REQUIRE(VALID_ZT(zt));
333
334	RWLOCK(&zt->rwlock, isc_rwlocktype_read);
335	result = dns_zt_apply(zt, stop, loadnew, NULL);
336	RWUNLOCK(&zt->rwlock, isc_rwlocktype_read);
337	return (result);
338}
339
340static isc_result_t
341loadnew(dns_zone_t *zone, void *uap) {
342	isc_result_t result;
343	UNUSED(uap);
344
345	result = dns_zone_loadnew(zone);
346	if (result == DNS_R_CONTINUE || result == DNS_R_UPTODATE ||
347	    result == DNS_R_DYNAMIC)
348		result = ISC_R_SUCCESS;
349	return (result);
350}
351
352isc_result_t
353dns_zt_freezezones(dns_zt_t *zt, isc_boolean_t freeze) {
354	isc_result_t result, tresult;
355
356	REQUIRE(VALID_ZT(zt));
357
358	RWLOCK(&zt->rwlock, isc_rwlocktype_read);
359	result = dns_zt_apply2(zt, ISC_FALSE, &tresult, freezezones, &freeze);
360	RWUNLOCK(&zt->rwlock, isc_rwlocktype_read);
361	if (tresult == ISC_R_NOTFOUND)
362		tresult = ISC_R_SUCCESS;
363	return ((result == ISC_R_SUCCESS) ? tresult : result);
364}
365
366static isc_result_t
367freezezones(dns_zone_t *zone, void *uap) {
368	isc_boolean_t freeze = *(isc_boolean_t *)uap;
369	isc_boolean_t frozen;
370	isc_result_t result = ISC_R_SUCCESS;
371	char classstr[DNS_RDATACLASS_FORMATSIZE];
372	char zonename[DNS_NAME_FORMATSIZE];
373	dns_zone_t *raw = NULL;
374	dns_view_t *view;
375	const char *vname;
376	const char *sep;
377	int level;
378
379	dns_zone_getraw(zone, &raw);
380	if (raw != NULL)
381		zone = raw;
382	if (dns_zone_gettype(zone) != dns_zone_master) {
383		if (raw != NULL)
384			dns_zone_detach(&raw);
385		return (ISC_R_SUCCESS);
386	}
387	if (!dns_zone_isdynamic(zone, ISC_TRUE)) {
388		if (raw != NULL)
389			dns_zone_detach(&raw);
390		return (ISC_R_SUCCESS);
391	}
392
393	frozen = dns_zone_getupdatedisabled(zone);
394	if (freeze) {
395		if (frozen)
396			result = DNS_R_FROZEN;
397		if (result == ISC_R_SUCCESS)
398			result = dns_zone_flush(zone);
399	} else {
400		if (frozen) {
401			result = dns_zone_load(zone);
402			if (result == DNS_R_CONTINUE ||
403			    result == DNS_R_UPTODATE)
404				result = ISC_R_SUCCESS;
405		}
406	}
407	if (result == ISC_R_SUCCESS)
408		dns_zone_setupdatedisabled(zone, freeze);
409	view = dns_zone_getview(zone);
410	if (strcmp(view->name, "_bind") == 0 ||
411	    strcmp(view->name, "_default") == 0)
412	{
413		vname = "";
414		sep = "";
415	} else {
416		vname = view->name;
417		sep = " ";
418	}
419	dns_rdataclass_format(dns_zone_getclass(zone), classstr,
420			      sizeof(classstr));
421	dns_name_format(dns_zone_getorigin(zone), zonename, sizeof(zonename));
422	level = (result != ISC_R_SUCCESS) ? ISC_LOG_ERROR : ISC_LOG_DEBUG(1);
423	isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL, DNS_LOGMODULE_ZONE,
424		      level, "%s zone '%s/%s'%s%s: %s",
425		      freeze ? "freezing" : "thawing",
426		      zonename, classstr, sep, vname,
427		      isc_result_totext(result));
428	if (raw != NULL)
429		dns_zone_detach(&raw);
430	return (result);
431}
432
433isc_result_t
434dns_zt_apply(dns_zt_t *zt, isc_boolean_t stop,
435	     isc_result_t (*action)(dns_zone_t *, void *), void *uap)
436{
437	return (dns_zt_apply2(zt, stop, NULL, action, uap));
438}
439
440isc_result_t
441dns_zt_apply2(dns_zt_t *zt, isc_boolean_t stop, isc_result_t *sub,
442	      isc_result_t (*action)(dns_zone_t *, void *), void *uap)
443{
444	dns_rbtnode_t *node;
445	dns_rbtnodechain_t chain;
446	isc_result_t result, tresult = ISC_R_SUCCESS;
447	dns_zone_t *zone;
448
449	REQUIRE(VALID_ZT(zt));
450	REQUIRE(action != NULL);
451
452	dns_rbtnodechain_init(&chain, zt->mctx);
453	result = dns_rbtnodechain_first(&chain, zt->table, NULL, NULL);
454	if (result == ISC_R_NOTFOUND) {
455		/*
456		 * The tree is empty.
457		 */
458		tresult = result;
459		result = ISC_R_NOMORE;
460	}
461	while (result == DNS_R_NEWORIGIN || result == ISC_R_SUCCESS) {
462		result = dns_rbtnodechain_current(&chain, NULL, NULL,
463						  &node);
464		if (result == ISC_R_SUCCESS) {
465			zone = node->data;
466			if (zone != NULL)
467				result = (action)(zone, uap);
468			if (result != ISC_R_SUCCESS && stop) {
469				tresult = result;
470				goto cleanup;	/* don't break */
471			} else if (result != ISC_R_SUCCESS &&
472				   tresult == ISC_R_SUCCESS)
473				tresult = result;
474		}
475		result = dns_rbtnodechain_next(&chain, NULL, NULL);
476	}
477	if (result == ISC_R_NOMORE)
478		result = ISC_R_SUCCESS;
479
480 cleanup:
481	dns_rbtnodechain_invalidate(&chain);
482	if (sub != NULL)
483		*sub = tresult;
484
485	return (result);
486}
487
488/*
489 * Decrement the loads_pending counter; when counter reaches
490 * zero, call the loaddone callback that was initially set by
491 * dns_zt_asyncload().
492 */
493static isc_result_t
494doneloading(dns_zt_t *zt, dns_zone_t *zone, isc_task_t *task) {
495	isc_boolean_t destroy = ISC_FALSE;
496	dns_zt_allloaded_t alldone = NULL;
497	void *arg = NULL;
498
499	UNUSED(zone);
500	UNUSED(task);
501
502	REQUIRE(VALID_ZT(zt));
503
504	RWLOCK(&zt->rwlock, isc_rwlocktype_write);
505	INSIST(zt->loads_pending != 0);
506	INSIST(zt->references != 0);
507	zt->references--;
508	if (zt->references == 0)
509		destroy = ISC_TRUE;
510	zt->loads_pending--;
511	if (zt->loads_pending == 0) {
512		alldone = zt->loaddone;
513		arg = zt->loaddone_arg;
514		zt->loaddone = NULL;
515		zt->loaddone_arg = NULL;
516	}
517	RWUNLOCK(&zt->rwlock, isc_rwlocktype_write);
518
519	if (alldone != NULL)
520		alldone(arg);
521
522	if (destroy)
523		zt_destroy(zt);
524
525	return (ISC_R_SUCCESS);
526}
527
528/***
529 *** Private
530 ***/
531
532static void
533auto_detach(void *data, void *arg) {
534	dns_zone_t *zone = data;
535
536	UNUSED(arg);
537
538	dns_zone_detach(&zone);
539}
540