1/*	$NetBSD: zt.c,v 1.10 2024/02/21 22:52:09 christos Exp $	*/
2
3/*
4 * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
5 *
6 * SPDX-License-Identifier: MPL-2.0
7 *
8 * This Source Code Form is subject to the terms of the Mozilla Public
9 * License, v. 2.0. If a copy of the MPL was not distributed with this
10 * file, you can obtain one at https://mozilla.org/MPL/2.0/.
11 *
12 * See the COPYRIGHT file distributed with this work for additional
13 * information regarding copyright ownership.
14 */
15
16/*! \file */
17
18#include <inttypes.h>
19#include <stdbool.h>
20
21#include <isc/atomic.h>
22#include <isc/file.h>
23#include <isc/magic.h>
24#include <isc/mem.h>
25#include <isc/result.h>
26#include <isc/string.h>
27#include <isc/task.h>
28#include <isc/util.h>
29
30#include <dns/log.h>
31#include <dns/name.h>
32#include <dns/rbt.h>
33#include <dns/rdataclass.h>
34#include <dns/view.h>
35#include <dns/zone.h>
36#include <dns/zt.h>
37
38struct zt_load_params {
39	dns_zt_zoneloaded_t dl;
40	bool newonly;
41};
42
43struct dns_zt {
44	/* Unlocked. */
45	unsigned int magic;
46	isc_mem_t *mctx;
47	dns_rdataclass_t rdclass;
48	isc_rwlock_t rwlock;
49	dns_zt_allloaded_t loaddone;
50	void *loaddone_arg;
51	struct zt_load_params *loadparams;
52
53	/* Atomic */
54	atomic_bool flush;
55	isc_refcount_t references;
56	isc_refcount_t loads_pending;
57
58	/* Locked by lock. */
59	dns_rbt_t *table;
60};
61
62struct zt_freeze_params {
63	dns_view_t *view;
64	bool freeze;
65};
66
67#define ZTMAGIC	     ISC_MAGIC('Z', 'T', 'b', 'l')
68#define VALID_ZT(zt) ISC_MAGIC_VALID(zt, ZTMAGIC)
69
70static void
71auto_detach(void *, void *);
72
73static isc_result_t
74load(dns_zone_t *zone, void *uap);
75
76static isc_result_t
77asyncload(dns_zone_t *zone, void *callback);
78
79static isc_result_t
80freezezones(dns_zone_t *zone, void *uap);
81
82static isc_result_t
83doneloading(dns_zt_t *zt, dns_zone_t *zone, isc_task_t *task);
84
85isc_result_t
86dns_zt_create(isc_mem_t *mctx, dns_rdataclass_t rdclass, dns_zt_t **ztp) {
87	dns_zt_t *zt;
88	isc_result_t result;
89
90	REQUIRE(ztp != NULL && *ztp == NULL);
91
92	zt = isc_mem_get(mctx, sizeof(*zt));
93
94	zt->table = NULL;
95	result = dns_rbt_create(mctx, auto_detach, zt, &zt->table);
96	if (result != ISC_R_SUCCESS) {
97		goto cleanup_zt;
98	}
99
100	isc_rwlock_init(&zt->rwlock, 0, 0);
101	zt->mctx = NULL;
102	isc_mem_attach(mctx, &zt->mctx);
103	isc_refcount_init(&zt->references, 1);
104	atomic_init(&zt->flush, false);
105	zt->rdclass = rdclass;
106	zt->magic = ZTMAGIC;
107	zt->loaddone = NULL;
108	zt->loaddone_arg = NULL;
109	zt->loadparams = NULL;
110	isc_refcount_init(&zt->loads_pending, 0);
111	*ztp = zt;
112
113	return (ISC_R_SUCCESS);
114
115cleanup_zt:
116	isc_mem_put(mctx, zt, sizeof(*zt));
117
118	return (result);
119}
120
121isc_result_t
122dns_zt_mount(dns_zt_t *zt, dns_zone_t *zone) {
123	isc_result_t result;
124	dns_zone_t *dummy = NULL;
125	dns_name_t *name;
126
127	REQUIRE(VALID_ZT(zt));
128
129	name = dns_zone_getorigin(zone);
130
131	RWLOCK(&zt->rwlock, isc_rwlocktype_write);
132
133	result = dns_rbt_addname(zt->table, name, zone);
134	if (result == ISC_R_SUCCESS) {
135		dns_zone_attach(zone, &dummy);
136	}
137
138	RWUNLOCK(&zt->rwlock, isc_rwlocktype_write);
139
140	return (result);
141}
142
143isc_result_t
144dns_zt_unmount(dns_zt_t *zt, dns_zone_t *zone) {
145	isc_result_t result;
146	dns_name_t *name;
147
148	REQUIRE(VALID_ZT(zt));
149
150	name = dns_zone_getorigin(zone);
151
152	RWLOCK(&zt->rwlock, isc_rwlocktype_write);
153
154	result = dns_rbt_deletename(zt->table, name, false);
155
156	RWUNLOCK(&zt->rwlock, isc_rwlocktype_write);
157
158	return (result);
159}
160
161isc_result_t
162dns_zt_find(dns_zt_t *zt, const dns_name_t *name, unsigned int options,
163	    dns_name_t *foundname, dns_zone_t **zonep) {
164	isc_result_t result;
165	dns_zone_t *dummy = NULL;
166	unsigned int rbtoptions = 0;
167
168	REQUIRE(VALID_ZT(zt));
169
170	if ((options & DNS_ZTFIND_NOEXACT) != 0) {
171		rbtoptions |= DNS_RBTFIND_NOEXACT;
172	}
173
174	RWLOCK(&zt->rwlock, isc_rwlocktype_read);
175
176	result = dns_rbt_findname(zt->table, name, rbtoptions, foundname,
177				  (void **)(void *)&dummy);
178	if (result == ISC_R_SUCCESS || result == DNS_R_PARTIALMATCH) {
179		/*
180		 * If DNS_ZTFIND_MIRROR is set and the zone which was
181		 * determined to be the deepest match for the supplied name is
182		 * a mirror zone which is expired or not yet loaded, treat it
183		 * as non-existent.  This will trigger a fallback to recursion
184		 * instead of returning a SERVFAIL.
185		 *
186		 * Note that currently only the deepest match in the zone table
187		 * is checked.  Consider a server configured with two mirror
188		 * zones: "bar" and its child, "foo.bar".  If zone data is
189		 * available for "bar" but not for "foo.bar", a query with
190		 * QNAME equal to or below "foo.bar" will cause ISC_R_NOTFOUND
191		 * to be returned, not DNS_R_PARTIALMATCH, despite zone data
192		 * being available for "bar".  This is considered to be an edge
193		 * case, handling which more appropriately is possible, but
194		 * arguably not worth the added complexity.
195		 */
196		if ((options & DNS_ZTFIND_MIRROR) != 0 &&
197		    dns_zone_gettype(dummy) == dns_zone_mirror &&
198		    !dns_zone_isloaded(dummy))
199		{
200			result = ISC_R_NOTFOUND;
201		} else {
202			dns_zone_attach(dummy, zonep);
203		}
204	}
205
206	RWUNLOCK(&zt->rwlock, isc_rwlocktype_read);
207
208	return (result);
209}
210
211void
212dns_zt_attach(dns_zt_t *zt, dns_zt_t **ztp) {
213	REQUIRE(VALID_ZT(zt));
214	REQUIRE(ztp != NULL && *ztp == NULL);
215
216	isc_refcount_increment(&zt->references);
217
218	*ztp = zt;
219}
220
221static isc_result_t
222flush(dns_zone_t *zone, void *uap) {
223	UNUSED(uap);
224	return (dns_zone_flush(zone));
225}
226
227static void
228zt_destroy(dns_zt_t *zt) {
229	isc_refcount_destroy(&zt->references);
230	isc_refcount_destroy(&zt->loads_pending);
231
232	if (atomic_load_acquire(&zt->flush)) {
233		(void)dns_zt_apply(zt, isc_rwlocktype_none, false, NULL, flush,
234				   NULL);
235	}
236
237	dns_rbt_destroy(&zt->table);
238	isc_rwlock_destroy(&zt->rwlock);
239	zt->magic = 0;
240	isc_mem_putanddetach(&zt->mctx, zt, sizeof(*zt));
241}
242
243void
244dns_zt_detach(dns_zt_t **ztp) {
245	dns_zt_t *zt;
246
247	REQUIRE(ztp != NULL && VALID_ZT(*ztp));
248
249	zt = *ztp;
250	*ztp = NULL;
251
252	if (isc_refcount_decrement(&zt->references) == 1) {
253		zt_destroy(zt);
254	}
255}
256
257void
258dns_zt_flush(dns_zt_t *zt) {
259	REQUIRE(VALID_ZT(zt));
260	atomic_store_release(&zt->flush, true);
261}
262
263isc_result_t
264dns_zt_load(dns_zt_t *zt, bool stop, bool newonly) {
265	isc_result_t result;
266	struct zt_load_params params;
267	REQUIRE(VALID_ZT(zt));
268	params.newonly = newonly;
269	result = dns_zt_apply(zt, isc_rwlocktype_read, stop, NULL, load,
270			      &params);
271	return (result);
272}
273
274static isc_result_t
275load(dns_zone_t *zone, void *paramsv) {
276	isc_result_t result;
277	struct zt_load_params *params = (struct zt_load_params *)paramsv;
278	result = dns_zone_load(zone, params->newonly);
279	if (result == DNS_R_CONTINUE || result == DNS_R_UPTODATE ||
280	    result == DNS_R_DYNAMIC)
281	{
282		result = ISC_R_SUCCESS;
283	}
284	return (result);
285}
286
287static void
288call_loaddone(dns_zt_t *zt) {
289	dns_zt_allloaded_t loaddone = zt->loaddone;
290	void *loaddone_arg = zt->loaddone_arg;
291
292	/*
293	 * Set zt->loaddone, zt->loaddone_arg and zt->loadparams to NULL
294	 * before calling loaddone.
295	 */
296	zt->loaddone = NULL;
297	zt->loaddone_arg = NULL;
298
299	isc_mem_put(zt->mctx, zt->loadparams, sizeof(struct zt_load_params));
300	zt->loadparams = NULL;
301
302	/*
303	 * Call the callback last.
304	 */
305	if (loaddone != NULL) {
306		loaddone(loaddone_arg);
307	}
308}
309
310isc_result_t
311dns_zt_asyncload(dns_zt_t *zt, bool newonly, dns_zt_allloaded_t alldone,
312		 void *arg) {
313	isc_result_t result;
314	uint_fast32_t loads_pending;
315
316	REQUIRE(VALID_ZT(zt));
317
318	/*
319	 * Obtain a reference to zt->loads_pending so that asyncload can
320	 * safely decrement both zt->references and zt->loads_pending
321	 * without going to zero.
322	 */
323	loads_pending = isc_refcount_increment0(&zt->loads_pending);
324	INSIST(loads_pending == 0);
325
326	/*
327	 * Only one dns_zt_asyncload call at a time should be active so
328	 * these pointers should be NULL.  They are set back to NULL
329	 * before the zt->loaddone (alldone) is called in call_loaddone.
330	 */
331	INSIST(zt->loadparams == NULL);
332	INSIST(zt->loaddone == NULL);
333	INSIST(zt->loaddone_arg == NULL);
334
335	zt->loadparams = isc_mem_get(zt->mctx, sizeof(struct zt_load_params));
336	zt->loadparams->dl = doneloading;
337	zt->loadparams->newonly = newonly;
338	zt->loaddone = alldone;
339	zt->loaddone_arg = arg;
340
341	result = dns_zt_apply(zt, isc_rwlocktype_read, false, NULL, asyncload,
342			      zt);
343
344	/*
345	 * Have all the loads completed?
346	 */
347	if (isc_refcount_decrement(&zt->loads_pending) == 1) {
348		call_loaddone(zt);
349	}
350
351	return (result);
352}
353
354/*
355 * Initiates asynchronous loading of zone 'zone'.  'callback' is a
356 * pointer to a function which will be used to inform the caller when
357 * the zone loading is complete.
358 */
359static isc_result_t
360asyncload(dns_zone_t *zone, void *zt_) {
361	isc_result_t result;
362	struct dns_zt *zt = (dns_zt_t *)zt_;
363	REQUIRE(zone != NULL);
364
365	isc_refcount_increment(&zt->references);
366	isc_refcount_increment(&zt->loads_pending);
367
368	result = dns_zone_asyncload(zone, zt->loadparams->newonly,
369				    *zt->loadparams->dl, zt);
370	if (result != ISC_R_SUCCESS) {
371		/*
372		 * Caller is holding a reference to zt->loads_pending
373		 * and zt->references so these can't decrement to zero.
374		 */
375		isc_refcount_decrement1(&zt->references);
376		isc_refcount_decrement1(&zt->loads_pending);
377	}
378	return (ISC_R_SUCCESS);
379}
380
381isc_result_t
382dns_zt_freezezones(dns_zt_t *zt, dns_view_t *view, bool freeze) {
383	isc_result_t result, tresult;
384	struct zt_freeze_params params = { view, freeze };
385
386	REQUIRE(VALID_ZT(zt));
387
388	result = dns_zt_apply(zt, isc_rwlocktype_read, false, &tresult,
389			      freezezones, &params);
390	if (tresult == ISC_R_NOTFOUND) {
391		tresult = ISC_R_SUCCESS;
392	}
393	return ((result == ISC_R_SUCCESS) ? tresult : result);
394}
395
396static isc_result_t
397freezezones(dns_zone_t *zone, void *uap) {
398	struct zt_freeze_params *params = uap;
399	bool frozen;
400	isc_result_t result = ISC_R_SUCCESS;
401	char classstr[DNS_RDATACLASS_FORMATSIZE];
402	char zonename[DNS_NAME_FORMATSIZE];
403	dns_zone_t *raw = NULL;
404	dns_view_t *view;
405	const char *vname;
406	const char *sep;
407	int level;
408
409	dns_zone_getraw(zone, &raw);
410	if (raw != NULL) {
411		zone = raw;
412	}
413	if (params->view != dns_zone_getview(zone)) {
414		if (raw != NULL) {
415			dns_zone_detach(&raw);
416		}
417		return (ISC_R_SUCCESS);
418	}
419	if (dns_zone_gettype(zone) != dns_zone_primary) {
420		if (raw != NULL) {
421			dns_zone_detach(&raw);
422		}
423		return (ISC_R_SUCCESS);
424	}
425	if (!dns_zone_isdynamic(zone, true)) {
426		if (raw != NULL) {
427			dns_zone_detach(&raw);
428		}
429		return (ISC_R_SUCCESS);
430	}
431
432	frozen = dns_zone_getupdatedisabled(zone);
433	if (params->freeze) {
434		if (frozen) {
435			result = DNS_R_FROZEN;
436		}
437		if (result == ISC_R_SUCCESS) {
438			result = dns_zone_flush(zone);
439		}
440		if (result == ISC_R_SUCCESS) {
441			dns_zone_setupdatedisabled(zone, params->freeze);
442		}
443	} else {
444		if (frozen) {
445			result = dns_zone_loadandthaw(zone);
446			if (result == DNS_R_CONTINUE ||
447			    result == DNS_R_UPTODATE)
448			{
449				result = ISC_R_SUCCESS;
450			}
451		}
452	}
453	view = dns_zone_getview(zone);
454	if (strcmp(view->name, "_bind") == 0 || strcmp(view->name, "_defaul"
455								   "t") == 0)
456	{
457		vname = "";
458		sep = "";
459	} else {
460		vname = view->name;
461		sep = " ";
462	}
463	dns_rdataclass_format(dns_zone_getclass(zone), classstr,
464			      sizeof(classstr));
465	dns_name_format(dns_zone_getorigin(zone), zonename, sizeof(zonename));
466	level = (result != ISC_R_SUCCESS) ? ISC_LOG_ERROR : ISC_LOG_DEBUG(1);
467	isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL, DNS_LOGMODULE_ZONE,
468		      level, "%s zone '%s/%s'%s%s: %s",
469		      params->freeze ? "freezing" : "thawing", zonename,
470		      classstr, sep, vname, isc_result_totext(result));
471	if (raw != NULL) {
472		dns_zone_detach(&raw);
473	}
474	return (result);
475}
476
477void
478dns_zt_setviewcommit(dns_zt_t *zt) {
479	dns_rbtnode_t *node;
480	dns_rbtnodechain_t chain;
481	isc_result_t result;
482
483	REQUIRE(VALID_ZT(zt));
484
485	RWLOCK(&zt->rwlock, isc_rwlocktype_read);
486	dns_rbtnodechain_init(&chain);
487
488	result = dns_rbtnodechain_first(&chain, zt->table, NULL, NULL);
489	while (result == DNS_R_NEWORIGIN || result == ISC_R_SUCCESS) {
490		result = dns_rbtnodechain_current(&chain, NULL, NULL, &node);
491		if (result == ISC_R_SUCCESS && node->data != NULL) {
492			dns_zone_setviewcommit(node->data);
493		}
494
495		result = dns_rbtnodechain_next(&chain, NULL, NULL);
496	}
497
498	dns_rbtnodechain_invalidate(&chain);
499	RWUNLOCK(&zt->rwlock, isc_rwlocktype_read);
500}
501
502void
503dns_zt_setviewrevert(dns_zt_t *zt) {
504	dns_rbtnode_t *node;
505	dns_rbtnodechain_t chain;
506	isc_result_t result;
507
508	REQUIRE(VALID_ZT(zt));
509
510	dns_rbtnodechain_init(&chain);
511
512	result = dns_rbtnodechain_first(&chain, zt->table, NULL, NULL);
513	while (result == DNS_R_NEWORIGIN || result == ISC_R_SUCCESS) {
514		result = dns_rbtnodechain_current(&chain, NULL, NULL, &node);
515		if (result == ISC_R_SUCCESS && node->data != NULL) {
516			dns_zone_setviewrevert(node->data);
517		}
518
519		result = dns_rbtnodechain_next(&chain, NULL, NULL);
520	}
521
522	dns_rbtnodechain_invalidate(&chain);
523}
524
525isc_result_t
526dns_zt_apply(dns_zt_t *zt, isc_rwlocktype_t lock, bool stop, isc_result_t *sub,
527	     isc_result_t (*action)(dns_zone_t *, void *), void *uap) {
528	dns_rbtnode_t *node;
529	dns_rbtnodechain_t chain;
530	isc_result_t result, tresult = ISC_R_SUCCESS;
531	dns_zone_t *zone;
532
533	REQUIRE(VALID_ZT(zt));
534	REQUIRE(action != NULL);
535
536	if (lock != isc_rwlocktype_none) {
537		RWLOCK(&zt->rwlock, lock);
538	}
539
540	dns_rbtnodechain_init(&chain);
541	result = dns_rbtnodechain_first(&chain, zt->table, NULL, NULL);
542	if (result == ISC_R_NOTFOUND) {
543		/*
544		 * The tree is empty.
545		 */
546		tresult = result;
547		result = ISC_R_NOMORE;
548	}
549	while (result == DNS_R_NEWORIGIN || result == ISC_R_SUCCESS) {
550		result = dns_rbtnodechain_current(&chain, NULL, NULL, &node);
551		if (result == ISC_R_SUCCESS) {
552			zone = node->data;
553			if (zone != NULL) {
554				result = (action)(zone, uap);
555			}
556			if (result != ISC_R_SUCCESS && stop) {
557				tresult = result;
558				goto cleanup; /* don't break */
559			} else if (result != ISC_R_SUCCESS &&
560				   tresult == ISC_R_SUCCESS)
561			{
562				tresult = result;
563			}
564		}
565		result = dns_rbtnodechain_next(&chain, NULL, NULL);
566	}
567	if (result == ISC_R_NOMORE) {
568		result = ISC_R_SUCCESS;
569	}
570
571cleanup:
572	dns_rbtnodechain_invalidate(&chain);
573	if (sub != NULL) {
574		*sub = tresult;
575	}
576
577	if (lock != isc_rwlocktype_none) {
578		RWUNLOCK(&zt->rwlock, lock);
579	}
580
581	return (result);
582}
583
584/*
585 * Decrement the loads_pending counter; when counter reaches
586 * zero, call the loaddone callback that was initially set by
587 * dns_zt_asyncload().
588 */
589static isc_result_t
590doneloading(dns_zt_t *zt, dns_zone_t *zone, isc_task_t *task) {
591	UNUSED(zone);
592	UNUSED(task);
593
594	REQUIRE(VALID_ZT(zt));
595
596	if (isc_refcount_decrement(&zt->loads_pending) == 1) {
597		call_loaddone(zt);
598	}
599
600	if (isc_refcount_decrement(&zt->references) == 1) {
601		zt_destroy(zt);
602	}
603
604	return (ISC_R_SUCCESS);
605}
606
607/***
608 *** Private
609 ***/
610
611static void
612auto_detach(void *data, void *arg) {
613	dns_zone_t *zone = data;
614
615	UNUSED(arg);
616	dns_zone_detach(&zone);
617}
618