xref: /netbsd-src/external/mpl/bind/dist/lib/dns/zt.c (revision 9fd8799cb5ceb66c69f2eb1a6d26a1d587ba1f1e)
1 /*	$NetBSD: zt.c,v 1.5 2020/05/24 19:46:23 christos Exp $	*/
2 
3 /*
4  * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
5  *
6  * This Source Code Form is subject to the terms of the Mozilla Public
7  * License, v. 2.0. If a copy of the MPL was not distributed with this
8  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9  *
10  * See the COPYRIGHT file distributed with this work for additional
11  * information regarding copyright ownership.
12  */
13 
14 /*! \file */
15 
16 #include <inttypes.h>
17 #include <stdbool.h>
18 
19 #include <isc/atomic.h>
20 #include <isc/file.h>
21 #include <isc/magic.h>
22 #include <isc/mem.h>
23 #include <isc/string.h>
24 #include <isc/task.h>
25 #include <isc/util.h>
26 
27 #include <dns/log.h>
28 #include <dns/name.h>
29 #include <dns/rbt.h>
30 #include <dns/rdataclass.h>
31 #include <dns/result.h>
32 #include <dns/view.h>
33 #include <dns/zone.h>
34 #include <dns/zt.h>
35 
36 struct zt_load_params {
37 	dns_zt_zoneloaded_t dl;
38 	bool newonly;
39 };
40 
41 struct dns_zt {
42 	/* Unlocked. */
43 	unsigned int magic;
44 	isc_mem_t *mctx;
45 	dns_rdataclass_t rdclass;
46 	isc_rwlock_t rwlock;
47 	dns_zt_allloaded_t loaddone;
48 	void *loaddone_arg;
49 	struct zt_load_params *loadparams;
50 
51 	/* Atomic */
52 	atomic_bool flush;
53 	isc_refcount_t references;
54 	isc_refcount_t loads_pending;
55 
56 	/* Locked by lock. */
57 	dns_rbt_t *table;
58 };
59 
60 #define ZTMAGIC	     ISC_MAGIC('Z', 'T', 'b', 'l')
61 #define VALID_ZT(zt) ISC_MAGIC_VALID(zt, ZTMAGIC)
62 
63 static void
64 auto_detach(void *, void *);
65 
66 static isc_result_t
67 load(dns_zone_t *zone, void *uap);
68 
69 static isc_result_t
70 asyncload(dns_zone_t *zone, void *callback);
71 
72 static isc_result_t
73 freezezones(dns_zone_t *zone, void *uap);
74 
75 static isc_result_t
76 doneloading(dns_zt_t *zt, dns_zone_t *zone, isc_task_t *task);
77 
78 isc_result_t
79 dns_zt_create(isc_mem_t *mctx, dns_rdataclass_t rdclass, dns_zt_t **ztp) {
80 	dns_zt_t *zt;
81 	isc_result_t result;
82 
83 	REQUIRE(ztp != NULL && *ztp == NULL);
84 
85 	zt = isc_mem_get(mctx, sizeof(*zt));
86 
87 	zt->table = NULL;
88 	result = dns_rbt_create(mctx, auto_detach, zt, &zt->table);
89 	if (result != ISC_R_SUCCESS) {
90 		goto cleanup_zt;
91 	}
92 
93 	result = isc_rwlock_init(&zt->rwlock, 0, 0);
94 	if (result != ISC_R_SUCCESS) {
95 		goto cleanup_rbt;
96 	}
97 
98 	zt->mctx = NULL;
99 	isc_mem_attach(mctx, &zt->mctx);
100 	isc_refcount_init(&zt->references, 1);
101 	atomic_init(&zt->flush, false);
102 	zt->rdclass = rdclass;
103 	zt->magic = ZTMAGIC;
104 	zt->loaddone = NULL;
105 	zt->loaddone_arg = NULL;
106 	zt->loadparams = NULL;
107 	isc_refcount_init(&zt->loads_pending, 0);
108 	*ztp = zt;
109 
110 	return (ISC_R_SUCCESS);
111 
112 cleanup_rbt:
113 	dns_rbt_destroy(&zt->table);
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 	if (atomic_load_acquire(&zt->flush)) {
230 		(void)dns_zt_apply(zt, false, NULL, flush, NULL);
231 	}
232 	dns_rbt_destroy(&zt->table);
233 	isc_rwlock_destroy(&zt->rwlock);
234 	zt->magic = 0;
235 	isc_mem_putanddetach(&zt->mctx, zt, sizeof(*zt));
236 }
237 
238 static void
239 zt_flushanddetach(dns_zt_t **ztp, bool need_flush) {
240 	dns_zt_t *zt;
241 
242 	REQUIRE(ztp != NULL && VALID_ZT(*ztp));
243 
244 	zt = *ztp;
245 	*ztp = NULL;
246 
247 	if (need_flush) {
248 		atomic_store_release(&zt->flush, true);
249 	}
250 
251 	if (isc_refcount_decrement(&zt->references) == 1) {
252 		zt_destroy(zt);
253 	}
254 }
255 
256 void
257 dns_zt_flushanddetach(dns_zt_t **ztp) {
258 	zt_flushanddetach(ztp, true);
259 }
260 
261 void
262 dns_zt_detach(dns_zt_t **ztp) {
263 	zt_flushanddetach(ztp, false);
264 }
265 
266 isc_result_t
267 dns_zt_load(dns_zt_t *zt, bool stop, bool newonly) {
268 	isc_result_t result;
269 	struct zt_load_params params;
270 	REQUIRE(VALID_ZT(zt));
271 	params.newonly = newonly;
272 	RWLOCK(&zt->rwlock, isc_rwlocktype_read);
273 	result = dns_zt_apply(zt, stop, NULL, load, &params);
274 	RWUNLOCK(&zt->rwlock, isc_rwlocktype_read);
275 	return (result);
276 }
277 
278 static isc_result_t
279 load(dns_zone_t *zone, void *paramsv) {
280 	isc_result_t result;
281 	struct zt_load_params *params = (struct zt_load_params *)paramsv;
282 	result = dns_zone_load(zone, params->newonly);
283 	if (result == DNS_R_CONTINUE || result == DNS_R_UPTODATE ||
284 	    result == DNS_R_DYNAMIC)
285 	{
286 		result = ISC_R_SUCCESS;
287 	}
288 	return (result);
289 }
290 
291 static void
292 call_loaddone(dns_zt_t *zt) {
293 	dns_zt_allloaded_t loaddone = zt->loaddone;
294 	void *loaddone_arg = zt->loaddone_arg;
295 
296 	/*
297 	 * Set zt->loaddone, zt->loaddone_arg and zt->loadparams to NULL
298 	 * before calling loaddone.
299 	 */
300 	zt->loaddone = NULL;
301 	zt->loaddone_arg = NULL;
302 
303 	isc_mem_put(zt->mctx, zt->loadparams, sizeof(struct zt_load_params));
304 	zt->loadparams = NULL;
305 
306 	/*
307 	 * Call the callback last.
308 	 */
309 	if (loaddone != NULL) {
310 		loaddone(loaddone_arg);
311 	}
312 }
313 
314 isc_result_t
315 dns_zt_asyncload(dns_zt_t *zt, bool newonly, dns_zt_allloaded_t alldone,
316 		 void *arg) {
317 	isc_result_t result;
318 	uint_fast32_t loads_pending;
319 
320 	REQUIRE(VALID_ZT(zt));
321 
322 	/*
323 	 * Obtain a reference to zt->loads_pending so that asyncload can
324 	 * safely decrement both zt->references and zt->loads_pending
325 	 * without going to zero.
326 	 */
327 	loads_pending = isc_refcount_increment0(&zt->loads_pending);
328 	INSIST(loads_pending == 0);
329 
330 	/*
331 	 * Only one dns_zt_asyncload call at a time should be active so
332 	 * these pointers should be NULL.  They are set back to NULL
333 	 * before the zt->loaddone (alldone) is called in call_loaddone.
334 	 */
335 	INSIST(zt->loadparams == NULL);
336 	INSIST(zt->loaddone == NULL);
337 	INSIST(zt->loaddone_arg == NULL);
338 
339 	zt->loadparams = isc_mem_get(zt->mctx, sizeof(struct zt_load_params));
340 	zt->loadparams->dl = doneloading;
341 	zt->loadparams->newonly = newonly;
342 	zt->loaddone = alldone;
343 	zt->loaddone_arg = arg;
344 
345 	RWLOCK(&zt->rwlock, isc_rwlocktype_read);
346 	result = dns_zt_apply(zt, false, NULL, asyncload, zt);
347 	RWUNLOCK(&zt->rwlock, isc_rwlocktype_read);
348 
349 	/*
350 	 * Have all the loads completed?
351 	 */
352 	if (isc_refcount_decrement(&zt->loads_pending) == 1) {
353 		call_loaddone(zt);
354 	}
355 
356 	return (result);
357 }
358 
359 /*
360  * Initiates asynchronous loading of zone 'zone'.  'callback' is a
361  * pointer to a function which will be used to inform the caller when
362  * the zone loading is complete.
363  */
364 static isc_result_t
365 asyncload(dns_zone_t *zone, void *zt_) {
366 	isc_result_t result;
367 	struct dns_zt *zt = (dns_zt_t *)zt_;
368 	REQUIRE(zone != NULL);
369 
370 	isc_refcount_increment(&zt->references);
371 	isc_refcount_increment(&zt->loads_pending);
372 
373 	result = dns_zone_asyncload(zone, zt->loadparams->newonly,
374 				    *zt->loadparams->dl, zt);
375 	if (result != ISC_R_SUCCESS) {
376 		/*
377 		 * Caller is holding a reference to zt->loads_pending
378 		 * and zt->references so these can't decrement to zero.
379 		 */
380 		INSIST(isc_refcount_decrement(&zt->loads_pending) > 1);
381 		INSIST(isc_refcount_decrement(&zt->references) > 1);
382 	}
383 	return (ISC_R_SUCCESS);
384 }
385 
386 isc_result_t
387 dns_zt_freezezones(dns_zt_t *zt, bool freeze) {
388 	isc_result_t result, tresult;
389 
390 	REQUIRE(VALID_ZT(zt));
391 
392 	RWLOCK(&zt->rwlock, isc_rwlocktype_read);
393 	result = dns_zt_apply(zt, false, &tresult, freezezones, &freeze);
394 	RWUNLOCK(&zt->rwlock, isc_rwlocktype_read);
395 	if (tresult == ISC_R_NOTFOUND) {
396 		tresult = ISC_R_SUCCESS;
397 	}
398 	return ((result == ISC_R_SUCCESS) ? tresult : result);
399 }
400 
401 static isc_result_t
402 freezezones(dns_zone_t *zone, void *uap) {
403 	bool freeze = *(bool *)uap;
404 	bool frozen;
405 	isc_result_t result = ISC_R_SUCCESS;
406 	char classstr[DNS_RDATACLASS_FORMATSIZE];
407 	char zonename[DNS_NAME_FORMATSIZE];
408 	dns_zone_t *raw = NULL;
409 	dns_view_t *view;
410 	const char *vname;
411 	const char *sep;
412 	int level;
413 
414 	dns_zone_getraw(zone, &raw);
415 	if (raw != NULL) {
416 		zone = raw;
417 	}
418 	if (dns_zone_gettype(zone) != dns_zone_master) {
419 		if (raw != NULL) {
420 			dns_zone_detach(&raw);
421 		}
422 		return (ISC_R_SUCCESS);
423 	}
424 	if (!dns_zone_isdynamic(zone, true)) {
425 		if (raw != NULL) {
426 			dns_zone_detach(&raw);
427 		}
428 		return (ISC_R_SUCCESS);
429 	}
430 
431 	frozen = dns_zone_getupdatedisabled(zone);
432 	if (freeze) {
433 		if (frozen) {
434 			result = DNS_R_FROZEN;
435 		}
436 		if (result == ISC_R_SUCCESS) {
437 			result = dns_zone_flush(zone);
438 		}
439 		if (result == ISC_R_SUCCESS) {
440 			dns_zone_setupdatedisabled(zone, freeze);
441 		}
442 	} else {
443 		if (frozen) {
444 			result = dns_zone_loadandthaw(zone);
445 			if (result == DNS_R_CONTINUE ||
446 			    result == DNS_R_UPTODATE) {
447 				result = ISC_R_SUCCESS;
448 			}
449 		}
450 	}
451 	view = dns_zone_getview(zone);
452 	if (strcmp(view->name, "_bind") == 0 || strcmp(view->name, "_defaul"
453 								   "t") == 0)
454 	{
455 		vname = "";
456 		sep = "";
457 	} else {
458 		vname = view->name;
459 		sep = " ";
460 	}
461 	dns_rdataclass_format(dns_zone_getclass(zone), classstr,
462 			      sizeof(classstr));
463 	dns_name_format(dns_zone_getorigin(zone), zonename, sizeof(zonename));
464 	level = (result != ISC_R_SUCCESS) ? ISC_LOG_ERROR : ISC_LOG_DEBUG(1);
465 	isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL, DNS_LOGMODULE_ZONE,
466 		      level, "%s zone '%s/%s'%s%s: %s",
467 		      freeze ? "freezing" : "thawing", zonename, classstr, sep,
468 		      vname, isc_result_totext(result));
469 	if (raw != NULL) {
470 		dns_zone_detach(&raw);
471 	}
472 	return (result);
473 }
474 
475 void
476 dns_zt_setviewcommit(dns_zt_t *zt) {
477 	dns_rbtnode_t *node;
478 	dns_rbtnodechain_t chain;
479 	isc_result_t result;
480 
481 	REQUIRE(VALID_ZT(zt));
482 
483 	dns_rbtnodechain_init(&chain);
484 
485 	result = dns_rbtnodechain_first(&chain, zt->table, NULL, NULL);
486 	while (result == DNS_R_NEWORIGIN || result == ISC_R_SUCCESS) {
487 		result = dns_rbtnodechain_current(&chain, NULL, NULL, &node);
488 		if (result == ISC_R_SUCCESS && node->data != NULL) {
489 			dns_zone_setviewcommit(node->data);
490 		}
491 
492 		result = dns_rbtnodechain_next(&chain, NULL, NULL);
493 	}
494 
495 	dns_rbtnodechain_invalidate(&chain);
496 }
497 
498 void
499 dns_zt_setviewrevert(dns_zt_t *zt) {
500 	dns_rbtnode_t *node;
501 	dns_rbtnodechain_t chain;
502 	isc_result_t result;
503 
504 	REQUIRE(VALID_ZT(zt));
505 
506 	dns_rbtnodechain_init(&chain);
507 
508 	result = dns_rbtnodechain_first(&chain, zt->table, NULL, NULL);
509 	while (result == DNS_R_NEWORIGIN || result == ISC_R_SUCCESS) {
510 		result = dns_rbtnodechain_current(&chain, NULL, NULL, &node);
511 		if (result == ISC_R_SUCCESS && node->data != NULL) {
512 			dns_zone_setviewrevert(node->data);
513 		}
514 
515 		result = dns_rbtnodechain_next(&chain, NULL, NULL);
516 	}
517 
518 	dns_rbtnodechain_invalidate(&chain);
519 }
520 
521 isc_result_t
522 dns_zt_apply(dns_zt_t *zt, bool stop, isc_result_t *sub,
523 	     isc_result_t (*action)(dns_zone_t *, void *), void *uap) {
524 	dns_rbtnode_t *node;
525 	dns_rbtnodechain_t chain;
526 	isc_result_t result, tresult = ISC_R_SUCCESS;
527 	dns_zone_t *zone;
528 
529 	REQUIRE(VALID_ZT(zt));
530 	REQUIRE(action != NULL);
531 
532 	dns_rbtnodechain_init(&chain);
533 	result = dns_rbtnodechain_first(&chain, zt->table, NULL, NULL);
534 	if (result == ISC_R_NOTFOUND) {
535 		/*
536 		 * The tree is empty.
537 		 */
538 		tresult = result;
539 		result = ISC_R_NOMORE;
540 	}
541 	while (result == DNS_R_NEWORIGIN || result == ISC_R_SUCCESS) {
542 		result = dns_rbtnodechain_current(&chain, NULL, NULL, &node);
543 		if (result == ISC_R_SUCCESS) {
544 			zone = node->data;
545 			if (zone != NULL) {
546 				result = (action)(zone, uap);
547 			}
548 			if (result != ISC_R_SUCCESS && stop) {
549 				tresult = result;
550 				goto cleanup; /* don't break */
551 			} else if (result != ISC_R_SUCCESS &&
552 				   tresult == ISC_R_SUCCESS) {
553 				tresult = result;
554 			}
555 		}
556 		result = dns_rbtnodechain_next(&chain, NULL, NULL);
557 	}
558 	if (result == ISC_R_NOMORE) {
559 		result = ISC_R_SUCCESS;
560 	}
561 
562 cleanup:
563 	dns_rbtnodechain_invalidate(&chain);
564 	if (sub != NULL) {
565 		*sub = tresult;
566 	}
567 
568 	return (result);
569 }
570 
571 /*
572  * Decrement the loads_pending counter; when counter reaches
573  * zero, call the loaddone callback that was initially set by
574  * dns_zt_asyncload().
575  */
576 static isc_result_t
577 doneloading(dns_zt_t *zt, dns_zone_t *zone, isc_task_t *task) {
578 	UNUSED(zone);
579 	UNUSED(task);
580 
581 	REQUIRE(VALID_ZT(zt));
582 
583 	if (isc_refcount_decrement(&zt->loads_pending) == 1) {
584 		call_loaddone(zt);
585 	}
586 
587 	if (isc_refcount_decrement(&zt->references) == 1) {
588 		zt_destroy(zt);
589 	}
590 
591 	return (ISC_R_SUCCESS);
592 }
593 
594 /***
595  *** Private
596  ***/
597 
598 static void
599 auto_detach(void *data, void *arg) {
600 	dns_zone_t *zone = data;
601 
602 	UNUSED(arg);
603 	dns_zone_detach(&zone);
604 }
605