xref: /netbsd-src/external/mpl/bind/dist/lib/dns/zt.c (revision 8e33eff89e26cf71871ead62f0d5063e1313c33a)
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 
38 struct zt_load_params {
39 	dns_zt_zoneloaded_t dl;
40 	bool newonly;
41 };
42 
43 struct 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 
62 struct 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 
70 static void
71 auto_detach(void *, void *);
72 
73 static isc_result_t
74 load(dns_zone_t *zone, void *uap);
75 
76 static isc_result_t
77 asyncload(dns_zone_t *zone, void *callback);
78 
79 static isc_result_t
80 freezezones(dns_zone_t *zone, void *uap);
81 
82 static isc_result_t
83 doneloading(dns_zt_t *zt, dns_zone_t *zone, isc_task_t *task);
84 
85 isc_result_t
86 dns_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 
115 cleanup_zt:
116 	isc_mem_put(mctx, zt, sizeof(*zt));
117 
118 	return (result);
119 }
120 
121 isc_result_t
122 dns_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 
143 isc_result_t
144 dns_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 
161 isc_result_t
162 dns_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 
211 void
212 dns_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 
221 static isc_result_t
222 flush(dns_zone_t *zone, void *uap) {
223 	UNUSED(uap);
224 	return (dns_zone_flush(zone));
225 }
226 
227 static void
228 zt_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 
243 void
244 dns_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 
257 void
258 dns_zt_flush(dns_zt_t *zt) {
259 	REQUIRE(VALID_ZT(zt));
260 	atomic_store_release(&zt->flush, true);
261 }
262 
263 isc_result_t
264 dns_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 
274 static isc_result_t
275 load(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 
287 static void
288 call_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 
310 isc_result_t
311 dns_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  */
359 static isc_result_t
360 asyncload(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 
381 isc_result_t
382 dns_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 
396 static isc_result_t
397 freezezones(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 
477 void
478 dns_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 
502 void
503 dns_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 
525 isc_result_t
526 dns_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 
571 cleanup:
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  */
589 static isc_result_t
590 doneloading(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 
611 static void
612 auto_detach(void *data, void *arg) {
613 	dns_zone_t *zone = data;
614 
615 	UNUSED(arg);
616 	dns_zone_detach(&zone);
617 }
618