xref: /netbsd-src/external/mpl/bind/dist/bin/named/builtin.c (revision bcda20f65a8566e103791ec395f7f499ef322704)
1 /*	$NetBSD: builtin.c,v 1.8 2025/01/26 16:24:33 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  * \brief
18  * The built-in "version", "hostname", "id", "authors" and "empty" databases.
19  */
20 
21 #include <stdio.h>
22 #include <string.h>
23 
24 #include <isc/lex.h>
25 #include <isc/mem.h>
26 #include <isc/result.h>
27 #include <isc/util.h>
28 
29 #include <dns/callbacks.h>
30 #include <dns/dbiterator.h>
31 #include <dns/rdatalist.h>
32 #include <dns/rdatasetiter.h>
33 #include <dns/types.h>
34 
35 #include <named/builtin.h>
36 #include <named/globals.h>
37 #include <named/os.h>
38 #include <named/server.h>
39 
40 #define BDBNODE_MAGIC	    ISC_MAGIC('B', 'D', 'B', 'N')
41 #define VALID_BDBNODE(bdbl) ISC_MAGIC_VALID(bdbl, BDBNODE_MAGIC)
42 
43 /*%
44  * Note that "impmagic" is not the first four bytes of the struct, so
45  * ISC_MAGIC_VALID cannot be used here.
46  */
47 #define BDB_MAGIC      ISC_MAGIC('B', 'D', 'B', '-')
48 #define VALID_BDB(bdb) ((bdb) != NULL && (bdb)->common.impmagic == BDB_MAGIC)
49 
50 #define BDB_DNS64 0x00000001U
51 
52 typedef struct bdbimplementation {
53 	unsigned int flags;
54 	dns_dbimplementation_t *dbimp;
55 } bdbimplementation_t;
56 
57 typedef struct bdbnode bdbnode_t;
58 typedef struct bdb {
59 	dns_db_t common;
60 	bdbimplementation_t *implementation;
61 	isc_result_t (*lookup)(bdbnode_t *node);
62 	char *server;
63 	char *contact;
64 } bdb_t;
65 
66 struct bdbnode {
67 	unsigned int magic;
68 	isc_refcount_t references;
69 	bdb_t *bdb;
70 	ISC_LIST(dns_rdatalist_t) lists;
71 	ISC_LIST(isc_buffer_t) buffers;
72 	dns_name_t *name;
73 	ISC_LINK(bdbnode_t) link;
74 	dns_rdatacallbacks_t callbacks;
75 };
76 
77 typedef struct bdb_rdatasetiter {
78 	dns_rdatasetiter_t common;
79 	dns_rdatalist_t *current;
80 } bdb_rdatasetiter_t;
81 
82 static isc_result_t
83 findrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version,
84 	     dns_rdatatype_t type, dns_rdatatype_t covers, isc_stdtime_t now,
85 	     dns_rdataset_t *rdataset,
86 	     dns_rdataset_t *sigrdataset DNS__DB_FLARG);
87 
88 static void
89 attachnode(dns_db_t *db, dns_dbnode_t *source,
90 	   dns_dbnode_t **targetp DNS__DB_FLARG);
91 
92 static void
93 detachnode(dns_db_t *db, dns_dbnode_t **nodep DNS__DB_FLARG);
94 
95 /*
96  * Helper functions to convert text to wire forma.
97  */
98 static isc_result_t
99 putrdata(bdbnode_t *node, dns_rdatatype_t typeval, dns_ttl_t ttl,
100 	 const unsigned char *rdatap, unsigned int rdlen) {
101 	dns_rdatalist_t *rdatalist = NULL;
102 	dns_rdata_t *rdata = NULL;
103 	isc_buffer_t *rdatabuf = NULL;
104 	isc_mem_t *mctx = NULL;
105 	isc_region_t region;
106 
107 	mctx = node->bdb->common.mctx;
108 
109 	rdatalist = ISC_LIST_HEAD(node->lists);
110 	while (rdatalist != NULL) {
111 		if (rdatalist->type == typeval) {
112 			break;
113 		}
114 		rdatalist = ISC_LIST_NEXT(rdatalist, link);
115 	}
116 
117 	if (rdatalist == NULL) {
118 		rdatalist = isc_mem_get(mctx, sizeof(dns_rdatalist_t));
119 		dns_rdatalist_init(rdatalist);
120 		rdatalist->rdclass = node->bdb->common.rdclass;
121 		rdatalist->type = typeval;
122 		rdatalist->ttl = ttl;
123 		ISC_LIST_APPEND(node->lists, rdatalist, link);
124 	} else if (rdatalist->ttl != ttl) {
125 		return DNS_R_BADTTL;
126 	}
127 
128 	rdata = isc_mem_get(mctx, sizeof(dns_rdata_t));
129 
130 	isc_buffer_allocate(mctx, &rdatabuf, rdlen);
131 	region.base = UNCONST(rdatap);
132 	region.length = rdlen;
133 	isc_buffer_copyregion(rdatabuf, &region);
134 	isc_buffer_usedregion(rdatabuf, &region);
135 	dns_rdata_init(rdata);
136 	dns_rdata_fromregion(rdata, rdatalist->rdclass, rdatalist->type,
137 			     &region);
138 	ISC_LIST_APPEND(rdatalist->rdata, rdata, link);
139 	ISC_LIST_APPEND(node->buffers, rdatabuf, link);
140 
141 	return ISC_R_SUCCESS;
142 }
143 
144 static isc_result_t
145 putrr(bdbnode_t *node, const char *type, dns_ttl_t ttl, const char *data) {
146 	isc_result_t result;
147 	dns_rdatatype_t typeval;
148 	isc_lex_t *lex = NULL;
149 	isc_mem_t *mctx = NULL;
150 	const dns_name_t *origin = NULL;
151 	isc_buffer_t *rb = NULL;
152 	isc_buffer_t b;
153 
154 	REQUIRE(VALID_BDBNODE(node));
155 	REQUIRE(type != NULL);
156 	REQUIRE(data != NULL);
157 
158 	mctx = node->bdb->common.mctx;
159 	origin = &node->bdb->common.origin;
160 
161 	isc_constregion_t r = { .base = type, .length = strlen(type) };
162 	result = dns_rdatatype_fromtext(&typeval, (isc_textregion_t *)&r);
163 	if (result != ISC_R_SUCCESS) {
164 		return result;
165 	}
166 
167 	isc_lex_create(mctx, 64, &lex);
168 
169 	size_t datalen = strlen(data);
170 	isc_buffer_constinit(&b, data, datalen);
171 	isc_buffer_add(&b, datalen);
172 
173 	result = isc_lex_openbuffer(lex, &b);
174 	if (result != ISC_R_SUCCESS) {
175 		return result;
176 	}
177 
178 	isc_buffer_allocate(mctx, &rb, DNS_RDATA_MAXLENGTH);
179 	result = dns_rdata_fromtext(NULL, node->bdb->common.rdclass, typeval,
180 				    lex, origin, 0, mctx, rb, &node->callbacks);
181 	isc_lex_destroy(&lex);
182 
183 	if (result == ISC_R_SUCCESS) {
184 		result = putrdata(node, typeval, ttl, isc_buffer_base(rb),
185 				  isc_buffer_usedlength(rb));
186 	}
187 
188 	isc_buffer_free(&rb);
189 
190 	return result;
191 }
192 
193 /* Reasonable default SOA values */
194 #define DEFAULT_REFRESH 28800U	/* 8 hours */
195 #define DEFAULT_RETRY	7200U	/* 2 hours */
196 #define DEFAULT_EXPIRE	604800U /* 7 days */
197 #define DEFAULT_MINIMUM 86400U	/* 1 day */
198 #define DEFAULT_TTL	(60 * 60 * 24)
199 
200 static isc_result_t
201 putsoa(bdbnode_t *node, const char *mname, const char *rname, uint32_t serial) {
202 	char str[2 * DNS_NAME_MAXTEXT + 5 * (sizeof("2147483647")) + 7];
203 	int n;
204 
205 	REQUIRE(mname != NULL);
206 	REQUIRE(rname != NULL);
207 
208 	n = snprintf(str, sizeof(str), "%s %s %u %u %u %u %u", mname, rname,
209 		     serial, DEFAULT_REFRESH, DEFAULT_RETRY, DEFAULT_EXPIRE,
210 		     DEFAULT_MINIMUM);
211 	if (n >= (int)sizeof(str) || n < 0) {
212 		return ISC_R_NOSPACE;
213 	}
214 	return putrr(node, "SOA", DEFAULT_TTL, str);
215 }
216 
217 static isc_result_t
218 puttxt(bdbnode_t *node, const char *text) {
219 	unsigned char buf[256];
220 	unsigned int len = strlen(text);
221 
222 	if (len > 255) {
223 		len = 255; /* Silently truncate */
224 	}
225 	buf[0] = len;
226 	memmove(&buf[1], text, len);
227 	return putrdata(node, dns_rdatatype_txt, 0, buf, len + 1);
228 }
229 
230 /*
231  * Builtin database implementation functions.
232  */
233 
234 /* Precomputed HEX * 16 or 1 table. */
235 static const unsigned char hex16[256] = {
236 	1, 1,	1,   1,	  1,   1,   1,	 1,   1,   1,	1, 1, 1, 1, 1, 1, /*00*/
237 	1, 1,	1,   1,	  1,   1,   1,	 1,   1,   1,	1, 1, 1, 1, 1, 1, /*10*/
238 	1, 1,	1,   1,	  1,   1,   1,	 1,   1,   1,	1, 1, 1, 1, 1, 1, /*20*/
239 	0, 16,	32,  48,  64,  80,  96,	 112, 128, 144, 1, 1, 1, 1, 1, 1, /*30*/
240 	1, 160, 176, 192, 208, 224, 240, 1,   1,   1,	1, 1, 1, 1, 1, 1, /*40*/
241 	1, 1,	1,   1,	  1,   1,   1,	 1,   1,   1,	1, 1, 1, 1, 1, 1, /*50*/
242 	1, 160, 176, 192, 208, 224, 240, 1,   1,   1,	1, 1, 1, 1, 1, 1, /*60*/
243 	1, 1,	1,   1,	  1,   1,   1,	 1,   1,   1,	1, 1, 1, 1, 1, 1, /*70*/
244 	1, 1,	1,   1,	  1,   1,   1,	 1,   1,   1,	1, 1, 1, 1, 1, 1, /*80*/
245 	1, 1,	1,   1,	  1,   1,   1,	 1,   1,   1,	1, 1, 1, 1, 1, 1, /*90*/
246 	1, 1,	1,   1,	  1,   1,   1,	 1,   1,   1,	1, 1, 1, 1, 1, 1, /*A0*/
247 	1, 1,	1,   1,	  1,   1,   1,	 1,   1,   1,	1, 1, 1, 1, 1, 1, /*B0*/
248 	1, 1,	1,   1,	  1,   1,   1,	 1,   1,   1,	1, 1, 1, 1, 1, 1, /*C0*/
249 	1, 1,	1,   1,	  1,   1,   1,	 1,   1,   1,	1, 1, 1, 1, 1, 1, /*D0*/
250 	1, 1,	1,   1,	  1,   1,   1,	 1,   1,   1,	1, 1, 1, 1, 1, 1, /*E0*/
251 	1, 1,	1,   1,	  1,   1,   1,	 1,   1,   1,	1, 1, 1, 1, 1, 1  /*F0*/
252 };
253 
254 static const unsigned char decimal[] = "0123456789";
255 static const unsigned char ipv4only[] = "\010ipv4only\004arpa";
256 
257 static size_t
258 dns64_rdata(unsigned char *v, size_t start, unsigned char *rdata) {
259 	size_t i, j = 0;
260 
261 	for (i = 0; i < 4U; i++) {
262 		unsigned char c = v[start++];
263 		if (start == 7U) {
264 			start++;
265 		}
266 		if (c > 99) {
267 			rdata[j++] = 3;
268 			rdata[j++] = decimal[c / 100];
269 			c = c % 100;
270 			rdata[j++] = decimal[c / 10];
271 			c = c % 10;
272 			rdata[j++] = decimal[c];
273 		} else if (c > 9) {
274 			rdata[j++] = 2;
275 			rdata[j++] = decimal[c / 10];
276 			c = c % 10;
277 			rdata[j++] = decimal[c];
278 		} else {
279 			rdata[j++] = 1;
280 			rdata[j++] = decimal[c];
281 		}
282 	}
283 	memmove(&rdata[j], "\07in-addr\04arpa", 14);
284 	return j + 14;
285 }
286 
287 static isc_result_t
288 dns64_cname(const dns_name_t *zone, const dns_name_t *name, bdbnode_t *node) {
289 	size_t zlen, nlen, j, len;
290 	unsigned char v[16], n;
291 	unsigned int i;
292 	unsigned char rdata[sizeof("123.123.123.123.in-addr.arpa.")];
293 	unsigned char *ndata;
294 
295 	/*
296 	 * The combined length of the zone and name is 74.
297 	 *
298 	 * The minimum zone length is 10 ((3)ip6(4)arpa(0)).
299 	 *
300 	 * The length of name should always be even as we are expecting
301 	 * a series of nibbles.
302 	 */
303 	zlen = zone->length;
304 	nlen = name->length;
305 	if ((zlen + nlen) > 74U || zlen < 10U || (nlen % 2) != 0U) {
306 		return ISC_R_NOTFOUND;
307 	}
308 
309 	/*
310 	 * Check that name is a series of nibbles.
311 	 * Compute the byte values that correspond to the nibbles as we go.
312 	 *
313 	 * Shift the final result 4 bits, by setting 'i' to 1, if we if we
314 	 * have a odd number of nibbles so that "must be zero" tests below
315 	 * are byte aligned and we correctly return ISC_R_NOTFOUND or
316 	 * ISC_R_SUCCESS.  We will not generate a CNAME in this case.
317 	 */
318 	ndata = name->ndata;
319 	i = (nlen % 4) == 2U ? 1 : 0;
320 	j = nlen;
321 	memset(v, 0, sizeof(v));
322 	while (j != 0U) {
323 		INSIST((i / 2) < sizeof(v));
324 		if (ndata[0] != 1) {
325 			return ISC_R_NOTFOUND;
326 		}
327 		n = hex16[ndata[1] & 0xff];
328 		if (n == 1) {
329 			return ISC_R_NOTFOUND;
330 		}
331 		v[i / 2] = n | (v[i / 2] >> 4);
332 		j -= 2;
333 		ndata += 2;
334 		i++;
335 	}
336 
337 	/*
338 	 * If we get here then we know name only consisted of nibbles.
339 	 * Now we need to determine if the name exists or not and whether
340 	 * it corresponds to a empty node in the zone or there should be
341 	 * a CNAME.
342 	 */
343 #define ZLEN(x) (10 + (x) / 2)
344 	switch (zlen) {
345 	case ZLEN(32): /* prefix len 32 */
346 		/*
347 		 * The nibbles that map to this byte must be zero for 'name'
348 		 * to exist in the zone.
349 		 */
350 		if (nlen > 16U && v[(nlen - 1) / 4 - 4] != 0) {
351 			return ISC_R_NOTFOUND;
352 		}
353 		/*
354 		 * If the total length is not 74 then this is a empty node
355 		 * so return success.
356 		 */
357 		if (nlen + zlen != 74U) {
358 			return ISC_R_SUCCESS;
359 		}
360 		len = dns64_rdata(v, 8, rdata);
361 		break;
362 	case ZLEN(40): /* prefix len 40 */
363 		/*
364 		 * The nibbles that map to this byte must be zero for 'name'
365 		 * to exist in the zone.
366 		 */
367 		if (nlen > 12U && v[(nlen - 1) / 4 - 3] != 0) {
368 			return ISC_R_NOTFOUND;
369 		}
370 		/*
371 		 * If the total length is not 74 then this is a empty node
372 		 * so return success.
373 		 */
374 		if (nlen + zlen != 74U) {
375 			return ISC_R_SUCCESS;
376 		}
377 		len = dns64_rdata(v, 6, rdata);
378 		break;
379 	case ZLEN(48): /* prefix len 48 */
380 		/*
381 		 * The nibbles that map to this byte must be zero for 'name'
382 		 * to exist in the zone.
383 		 */
384 		if (nlen > 8U && v[(nlen - 1) / 4 - 2] != 0) {
385 			return ISC_R_NOTFOUND;
386 		}
387 		/*
388 		 * If the total length is not 74 then this is a empty node
389 		 * so return success.
390 		 */
391 		if (nlen + zlen != 74U) {
392 			return ISC_R_SUCCESS;
393 		}
394 		len = dns64_rdata(v, 5, rdata);
395 		break;
396 	case ZLEN(56): /* prefix len 56 */
397 		/*
398 		 * The nibbles that map to this byte must be zero for 'name'
399 		 * to exist in the zone.
400 		 */
401 		if (nlen > 4U && v[(nlen - 1) / 4 - 1] != 0) {
402 			return ISC_R_NOTFOUND;
403 		}
404 		/*
405 		 * If the total length is not 74 then this is a empty node
406 		 * so return success.
407 		 */
408 		if (nlen + zlen != 74U) {
409 			return ISC_R_SUCCESS;
410 		}
411 		len = dns64_rdata(v, 4, rdata);
412 		break;
413 	case ZLEN(64): /* prefix len 64 */
414 		/*
415 		 * The nibbles that map to this byte must be zero for 'name'
416 		 * to exist in the zone.
417 		 */
418 		if (v[(nlen - 1) / 4] != 0) {
419 			return ISC_R_NOTFOUND;
420 		}
421 		/*
422 		 * If the total length is not 74 then this is a empty node
423 		 * so return success.
424 		 */
425 		if (nlen + zlen != 74U) {
426 			return ISC_R_SUCCESS;
427 		}
428 		len = dns64_rdata(v, 3, rdata);
429 		break;
430 	case ZLEN(96): /* prefix len 96 */
431 		/*
432 		 * If the total length is not 74 then this is a empty node
433 		 * so return success.
434 		 */
435 		if (nlen + zlen != 74U) {
436 			return ISC_R_SUCCESS;
437 		}
438 		len = dns64_rdata(v, 0, rdata);
439 		break;
440 	default:
441 		/*
442 		 * This should never be reached unless someone adds a
443 		 * zone declaration with this internal type to named.conf.
444 		 */
445 		return ISC_R_NOTFOUND;
446 	}
447 
448 	/*
449 	 * Reverse of 192.0.0.170 or 192.0.0.171 maps to ipv4only.arpa.
450 	 */
451 	if ((v[0] == 170 || v[0] == 171) && v[1] == 0 && v[2] == 0 &&
452 	    v[3] == 192)
453 	{
454 		return putrdata(node, dns_rdatatype_ptr, 3600, ipv4only,
455 				sizeof(ipv4only));
456 	}
457 
458 	return putrdata(node, dns_rdatatype_cname, 600, rdata,
459 			(unsigned int)len);
460 }
461 
462 static isc_result_t
463 builtin_lookup(bdb_t *bdb, const dns_name_t *name, bdbnode_t *node) {
464 	if (name->labels == 0 && name->length == 0) {
465 		return bdb->lookup(node);
466 	} else if ((node->bdb->implementation->flags & BDB_DNS64) != 0) {
467 		return dns64_cname(&bdb->common.origin, name, node);
468 	} else {
469 		return ISC_R_NOTFOUND;
470 	}
471 }
472 
473 static isc_result_t
474 builtin_authority(bdb_t *bdb, bdbnode_t *node) {
475 	isc_result_t result;
476 	const char *contact = "hostmaster";
477 	const char *server = "@";
478 
479 	if (bdb->server != NULL) {
480 		server = bdb->server;
481 	}
482 	if (bdb->contact != NULL) {
483 		contact = bdb->contact;
484 	}
485 
486 	result = putsoa(node, server, contact, 0);
487 	if (result != ISC_R_SUCCESS) {
488 		return ISC_R_FAILURE;
489 	}
490 
491 	result = putrr(node, "NS", 0, server);
492 	if (result != ISC_R_SUCCESS) {
493 		return ISC_R_FAILURE;
494 	}
495 
496 	return ISC_R_SUCCESS;
497 }
498 
499 static isc_result_t
500 version_lookup(bdbnode_t *node) {
501 	if (named_g_server->version_set) {
502 		if (named_g_server->version == NULL) {
503 			return ISC_R_SUCCESS;
504 		} else {
505 			return puttxt(node, named_g_server->version);
506 		}
507 	} else {
508 		return puttxt(node, PACKAGE_VERSION);
509 	}
510 }
511 
512 static isc_result_t
513 hostname_lookup(bdbnode_t *node) {
514 	if (named_g_server->hostname_set) {
515 		if (named_g_server->hostname == NULL) {
516 			return ISC_R_SUCCESS;
517 		} else {
518 			return puttxt(node, named_g_server->hostname);
519 		}
520 	} else {
521 		char buf[256];
522 		if (gethostname(buf, sizeof(buf)) != 0) {
523 			return ISC_R_FAILURE;
524 		}
525 		return puttxt(node, buf);
526 	}
527 }
528 
529 static isc_result_t
530 authors_lookup(bdbnode_t *node) {
531 	isc_result_t result;
532 	const char **p = NULL;
533 	static const char *authors[] = {
534 		"Mark Andrews",	      "Curtis Blackburn",
535 		"James Brister",      "Ben Cottrell",
536 		"John H. DuBois III", "Francis Dupont",
537 		"Michael Graff",      "Andreas Gustafsson",
538 		"Bob Halley",	      "Evan Hunt",
539 		"JINMEI Tatuya",      "Witold Krecicki",
540 		"David Lawrence",     "Scott Mann",
541 		"Danny Mayer",	      "Aydin Mercan",
542 		"Damien Neil",	      "Matt Nelson",
543 		"Jeremy C. Reed",     "Michael Sawyer",
544 		"Brian Wellington",   NULL
545 	};
546 
547 	/*
548 	 * If a version string is specified, disable the authors.bind zone.
549 	 */
550 	if (named_g_server->version_set) {
551 		return ISC_R_SUCCESS;
552 	}
553 
554 	for (p = authors; *p != NULL; p++) {
555 		result = puttxt(node, *p);
556 		if (result != ISC_R_SUCCESS) {
557 			return result;
558 		}
559 	}
560 	return ISC_R_SUCCESS;
561 }
562 
563 static isc_result_t
564 id_lookup(bdbnode_t *node) {
565 	if (named_g_server->sctx->usehostname) {
566 		char buf[256];
567 		if (gethostname(buf, sizeof(buf)) != 0) {
568 			return ISC_R_FAILURE;
569 		}
570 		return puttxt(node, buf);
571 	} else if (named_g_server->sctx->server_id != NULL) {
572 		return puttxt(node, named_g_server->sctx->server_id);
573 	} else {
574 		return ISC_R_SUCCESS;
575 	}
576 }
577 
578 static isc_result_t
579 empty_lookup(bdbnode_t *node) {
580 	UNUSED(node);
581 
582 	return ISC_R_SUCCESS;
583 }
584 
585 static isc_result_t
586 ipv4only_lookup(bdbnode_t *node) {
587 	isc_result_t result;
588 	unsigned char data[2][4] = { { 192, 0, 0, 170 }, { 192, 0, 0, 171 } };
589 
590 	for (int i = 0; i < 2; i++) {
591 		result = putrdata(node, dns_rdatatype_a, 3600, data[i], 4);
592 		if (result != ISC_R_SUCCESS) {
593 			return result;
594 		}
595 	}
596 	return ISC_R_SUCCESS;
597 }
598 
599 static isc_result_t
600 ipv4reverse_lookup(bdbnode_t *node) {
601 	isc_result_t result;
602 
603 	result = putrdata(node, dns_rdatatype_ptr, 3600, ipv4only,
604 			  sizeof(ipv4only));
605 	return result;
606 }
607 
608 /*
609  * Rdataset implementation methods. An rdataset in the builtin databases is
610  * implemented as an rdatalist which holds a reference to the dbnode,
611  * to prevent the node being freed while the rdataset is still in use, so
612  * we need local implementations of clone and disassociate but the rest of
613  * the implementation can be the same as dns_rdatalist..
614  */
615 static void
616 disassociate(dns_rdataset_t *rdataset DNS__DB_FLARG) {
617 	dns_dbnode_t *node = rdataset->rdlist.node;
618 	bdbnode_t *bdbnode = (bdbnode_t *)node;
619 	dns_db_t *db = (dns_db_t *)bdbnode->bdb;
620 
621 	detachnode(db, &node DNS__DB_FLARG_PASS);
622 	dns_rdatalist_disassociate(rdataset DNS__DB_FLARG_PASS);
623 }
624 
625 static void
626 rdataset_clone(dns_rdataset_t *source, dns_rdataset_t *target DNS__DB_FLARG) {
627 	dns_dbnode_t *node = source->rdlist.node;
628 	bdbnode_t *bdbnode = (bdbnode_t *)node;
629 	dns_db_t *db = (dns_db_t *)bdbnode->bdb;
630 
631 	dns_rdatalist_clone(source, target DNS__DB_FLARG_PASS);
632 	attachnode(db, node, &target->rdlist.node DNS__DB_FLARG_PASS);
633 }
634 
635 static dns_rdatasetmethods_t bdb_rdataset_methods = {
636 	.disassociate = disassociate,
637 	.first = dns_rdatalist_first,
638 	.next = dns_rdatalist_next,
639 	.current = dns_rdatalist_current,
640 	.clone = rdataset_clone,
641 	.count = dns_rdatalist_count,
642 	.addnoqname = dns_rdatalist_addnoqname,
643 	.getnoqname = dns_rdatalist_getnoqname,
644 };
645 
646 static void
647 new_rdataset(dns_rdatalist_t *rdatalist, dns_db_t *db, dns_dbnode_t *node,
648 	     dns_rdataset_t *rdataset) {
649 	dns_rdatalist_tordataset(rdatalist, rdataset);
650 
651 	rdataset->methods = &bdb_rdataset_methods;
652 	dns_db_attachnode(db, node, &rdataset->rdlist.node);
653 }
654 
655 /*
656  * Rdataset iterator methods
657  */
658 
659 static void
660 rdatasetiter_destroy(dns_rdatasetiter_t **iteratorp DNS__DB_FLARG) {
661 	bdb_rdatasetiter_t *bdbiterator = (bdb_rdatasetiter_t *)(*iteratorp);
662 	detachnode(bdbiterator->common.db,
663 		   &bdbiterator->common.node DNS__DB_FLARG_PASS);
664 	isc_mem_put(bdbiterator->common.db->mctx, bdbiterator,
665 		    sizeof(bdb_rdatasetiter_t));
666 	*iteratorp = NULL;
667 }
668 
669 static isc_result_t
670 rdatasetiter_first(dns_rdatasetiter_t *iterator DNS__DB_FLARG) {
671 	bdb_rdatasetiter_t *bdbiterator = (bdb_rdatasetiter_t *)iterator;
672 	bdbnode_t *bdbnode = (bdbnode_t *)iterator->node;
673 
674 	if (ISC_LIST_EMPTY(bdbnode->lists)) {
675 		return ISC_R_NOMORE;
676 	}
677 	bdbiterator->current = ISC_LIST_HEAD(bdbnode->lists);
678 	return ISC_R_SUCCESS;
679 }
680 
681 static isc_result_t
682 rdatasetiter_next(dns_rdatasetiter_t *iterator DNS__DB_FLARG) {
683 	bdb_rdatasetiter_t *bdbiterator = (bdb_rdatasetiter_t *)iterator;
684 
685 	bdbiterator->current = ISC_LIST_NEXT(bdbiterator->current, link);
686 	if (bdbiterator->current == NULL) {
687 		return ISC_R_NOMORE;
688 	} else {
689 		return ISC_R_SUCCESS;
690 	}
691 }
692 
693 static void
694 rdatasetiter_current(dns_rdatasetiter_t *iterator,
695 		     dns_rdataset_t *rdataset DNS__DB_FLARG) {
696 	bdb_rdatasetiter_t *bdbiterator = (bdb_rdatasetiter_t *)iterator;
697 
698 	new_rdataset(bdbiterator->current, iterator->db, iterator->node,
699 		     rdataset);
700 }
701 
702 static dns_rdatasetitermethods_t rdatasetiter_methods = {
703 	rdatasetiter_destroy, rdatasetiter_first, rdatasetiter_next,
704 	rdatasetiter_current
705 };
706 
707 /*
708  * Database implementation methods
709  */
710 static void
711 destroy(dns_db_t *db) {
712 	bdb_t *bdb = (bdb_t *)db;
713 	isc_refcount_destroy(&bdb->common.references);
714 
715 	if (bdb->server != NULL) {
716 		isc_mem_free(named_g_mctx, bdb->server);
717 	}
718 	if (bdb->contact != NULL) {
719 		isc_mem_free(named_g_mctx, bdb->contact);
720 	}
721 
722 	bdb->common.magic = 0;
723 	bdb->common.impmagic = 0;
724 
725 	dns_name_free(&bdb->common.origin, bdb->common.mctx);
726 
727 	isc_mem_putanddetach(&bdb->common.mctx, bdb, sizeof(bdb_t));
728 }
729 
730 /*
731  * A dummy 'version' value is used so that dns_db_createversion()
732  * can return a non-NULL version to the caller, but there can only be
733  * one version of these databases, so the version value is never used.
734  */
735 static int dummy;
736 
737 static void
738 currentversion(dns_db_t *db, dns_dbversion_t **versionp) {
739 	bdb_t *bdb = (bdb_t *)db;
740 
741 	REQUIRE(VALID_BDB(bdb));
742 
743 	*versionp = (void *)&dummy;
744 	return;
745 }
746 
747 static void
748 attachversion(dns_db_t *db, dns_dbversion_t *source,
749 	      dns_dbversion_t **targetp) {
750 	bdb_t *bdb = (bdb_t *)db;
751 
752 	REQUIRE(VALID_BDB(bdb));
753 	REQUIRE(source != NULL && source == (void *)&dummy);
754 	REQUIRE(targetp != NULL && *targetp == NULL);
755 
756 	*targetp = source;
757 	return;
758 }
759 
760 static void
761 closeversion(dns_db_t *db, dns_dbversion_t **versionp,
762 	     bool commit DNS__DB_FLARG) {
763 	bdb_t *bdb = (bdb_t *)db;
764 
765 	REQUIRE(VALID_BDB(bdb));
766 	REQUIRE(versionp != NULL && *versionp == (void *)&dummy);
767 	REQUIRE(!commit);
768 
769 	*versionp = NULL;
770 }
771 
772 static isc_result_t
773 createnode(bdb_t *bdb, bdbnode_t **nodep) {
774 	bdbnode_t *node = NULL;
775 
776 	REQUIRE(VALID_BDB(bdb));
777 
778 	node = isc_mem_get(bdb->common.mctx, sizeof(bdbnode_t));
779 	*node = (bdbnode_t){
780 		.lists = ISC_LIST_INITIALIZER,
781 		.buffers = ISC_LIST_INITIALIZER,
782 		.link = ISC_LINK_INITIALIZER,
783 	};
784 
785 	dns_db_attach((dns_db_t *)bdb, (dns_db_t **)&node->bdb);
786 	dns_rdatacallbacks_init(&node->callbacks);
787 
788 	isc_refcount_init(&node->references, 1);
789 	node->magic = BDBNODE_MAGIC;
790 
791 	*nodep = node;
792 	return ISC_R_SUCCESS;
793 }
794 
795 static void
796 destroynode(bdbnode_t *node) {
797 	dns_rdatalist_t *list = NULL;
798 	dns_rdata_t *rdata = NULL;
799 	isc_buffer_t *b = NULL;
800 	bdb_t *bdb = NULL;
801 	isc_mem_t *mctx = NULL;
802 
803 	bdb = node->bdb;
804 	mctx = bdb->common.mctx;
805 
806 	while (!ISC_LIST_EMPTY(node->lists)) {
807 		list = ISC_LIST_HEAD(node->lists);
808 		while (!ISC_LIST_EMPTY(list->rdata)) {
809 			rdata = ISC_LIST_HEAD(list->rdata);
810 			ISC_LIST_UNLINK(list->rdata, rdata, link);
811 			isc_mem_put(mctx, rdata, sizeof(dns_rdata_t));
812 		}
813 		ISC_LIST_UNLINK(node->lists, list, link);
814 		isc_mem_put(mctx, list, sizeof(dns_rdatalist_t));
815 	}
816 
817 	while (!ISC_LIST_EMPTY(node->buffers)) {
818 		b = ISC_LIST_HEAD(node->buffers);
819 		ISC_LIST_UNLINK(node->buffers, b, link);
820 		isc_buffer_free(&b);
821 	}
822 
823 	if (node->name != NULL) {
824 		dns_name_free(node->name, mctx);
825 		isc_mem_put(mctx, node->name, sizeof(dns_name_t));
826 	}
827 
828 	node->magic = 0;
829 	isc_mem_put(mctx, node, sizeof(bdbnode_t));
830 	dns_db_detach((dns_db_t **)(void *)&bdb);
831 }
832 
833 static isc_result_t
834 getoriginnode(dns_db_t *db, dns_dbnode_t **nodep DNS__DB_FLARG) {
835 	bdb_t *bdb = (bdb_t *)db;
836 	bdbnode_t *node = NULL;
837 	isc_result_t result;
838 	dns_name_t relname;
839 	dns_name_t *name = NULL;
840 
841 	REQUIRE(VALID_BDB(bdb));
842 	REQUIRE(nodep != NULL && *nodep == NULL);
843 
844 	dns_name_init(&relname, NULL);
845 	name = &relname;
846 
847 	result = createnode(bdb, &node);
848 	if (result != ISC_R_SUCCESS) {
849 		return result;
850 	}
851 
852 	result = builtin_lookup(bdb, name, node);
853 	if (result != ISC_R_SUCCESS && result != ISC_R_NOTFOUND) {
854 		destroynode(node);
855 		return result;
856 	}
857 
858 	result = builtin_authority(bdb, node);
859 	if (result != ISC_R_SUCCESS) {
860 		destroynode(node);
861 		return result;
862 	}
863 
864 	*nodep = node;
865 	return ISC_R_SUCCESS;
866 }
867 
868 static isc_result_t
869 findnode(dns_db_t *db, const dns_name_t *name, bool create,
870 	 dns_dbnode_t **nodep DNS__DB_FLARG) {
871 	bdb_t *bdb = (bdb_t *)db;
872 	bdbnode_t *node = NULL;
873 	isc_result_t result;
874 	bool isorigin;
875 	dns_name_t relname;
876 	unsigned int labels;
877 
878 	REQUIRE(VALID_BDB(bdb));
879 	REQUIRE(nodep != NULL && *nodep == NULL);
880 
881 	UNUSED(create);
882 
883 	isorigin = dns_name_equal(name, &bdb->common.origin);
884 
885 	labels = dns_name_countlabels(name) - dns_name_countlabels(&db->origin);
886 	dns_name_init(&relname, NULL);
887 	dns_name_getlabelsequence(name, 0, labels, &relname);
888 	name = &relname;
889 
890 	result = createnode(bdb, &node);
891 	if (result != ISC_R_SUCCESS) {
892 		return result;
893 	}
894 
895 	result = builtin_lookup(bdb, name, node);
896 	if (result != ISC_R_SUCCESS && (!isorigin || result != ISC_R_NOTFOUND))
897 	{
898 		destroynode(node);
899 		return result;
900 	}
901 
902 	if (isorigin) {
903 		result = builtin_authority(bdb, node);
904 		if (result != ISC_R_SUCCESS) {
905 			destroynode(node);
906 			return result;
907 		}
908 	}
909 
910 	*nodep = node;
911 	return ISC_R_SUCCESS;
912 }
913 
914 static isc_result_t
915 find(dns_db_t *db, const dns_name_t *name, dns_dbversion_t *version,
916      dns_rdatatype_t type, unsigned int options, isc_stdtime_t now,
917      dns_dbnode_t **nodep, dns_name_t *foundname, dns_rdataset_t *rdataset,
918      dns_rdataset_t *sigrdataset DNS__DB_FLARG) {
919 	bdb_t *bdb = (bdb_t *)db;
920 	isc_result_t result;
921 	dns_dbnode_t *node = NULL;
922 	dns_fixedname_t fname;
923 	dns_rdataset_t xrdataset;
924 	dns_name_t *xname = NULL;
925 	unsigned int nlabels, olabels, i;
926 	bool dns64;
927 
928 	REQUIRE(VALID_BDB(bdb));
929 	REQUIRE(nodep == NULL || *nodep == NULL);
930 	REQUIRE(version == NULL || version == (void *)&dummy);
931 
932 	if (!dns_name_issubdomain(name, &db->origin)) {
933 		return DNS_R_NXDOMAIN;
934 	}
935 
936 	olabels = dns_name_countlabels(&db->origin);
937 	nlabels = dns_name_countlabels(name);
938 
939 	xname = dns_fixedname_initname(&fname);
940 
941 	if (rdataset == NULL) {
942 		dns_rdataset_init(&xrdataset);
943 		rdataset = &xrdataset;
944 	}
945 
946 	result = DNS_R_NXDOMAIN;
947 	dns64 = ((bdb->implementation->flags & BDB_DNS64) != 0);
948 	for (i = (dns64 ? nlabels : olabels); i <= nlabels; i++) {
949 		/*
950 		 * Look up the next label.
951 		 */
952 		dns_name_getlabelsequence(name, nlabels - i, i, xname);
953 		result = findnode(db, xname, false, &node DNS__DB_FLARG_PASS);
954 		if (result == ISC_R_NOTFOUND) {
955 			/*
956 			 * No data at zone apex?
957 			 */
958 			if (i == olabels) {
959 				return DNS_R_BADDB;
960 			}
961 			result = DNS_R_NXDOMAIN;
962 			continue;
963 		}
964 		if (result != ISC_R_SUCCESS) {
965 			return result;
966 		}
967 
968 		/*
969 		 * DNS64 zones don't have DNAME or NS records.
970 		 */
971 		if (dns64) {
972 			goto skip;
973 		}
974 
975 		/*
976 		 * Look for a DNAME at the current label, unless this is
977 		 * the qname.
978 		 */
979 		if (i < nlabels) {
980 			result = findrdataset(
981 				db, node, version, dns_rdatatype_dname, 0, now,
982 				rdataset, sigrdataset DNS__DB_FLARG_PASS);
983 			if (result == ISC_R_SUCCESS) {
984 				result = DNS_R_DNAME;
985 				break;
986 			}
987 		}
988 
989 		/*
990 		 * Look for an NS at the current label, unless this is the
991 		 * origin or glue is ok.
992 		 */
993 		if (i != olabels && (options & DNS_DBFIND_GLUEOK) == 0) {
994 			result = findrdataset(
995 				db, node, version, dns_rdatatype_ns, 0, now,
996 				rdataset, sigrdataset DNS__DB_FLARG_PASS);
997 			if (result == ISC_R_SUCCESS) {
998 				if (i == nlabels && type == dns_rdatatype_any) {
999 					result = DNS_R_ZONECUT;
1000 					dns_rdataset_disassociate(rdataset);
1001 					if (sigrdataset != NULL &&
1002 					    dns_rdataset_isassociated(
1003 						    sigrdataset))
1004 					{
1005 						dns_rdataset_disassociate(
1006 							sigrdataset);
1007 					}
1008 				} else {
1009 					result = DNS_R_DELEGATION;
1010 				}
1011 				break;
1012 			}
1013 		}
1014 
1015 		/*
1016 		 * If the current name is not the qname, add another label
1017 		 * and try again.
1018 		 */
1019 		if (i < nlabels) {
1020 			destroynode(node);
1021 			node = NULL;
1022 			continue;
1023 		}
1024 
1025 	skip:
1026 		/*
1027 		 * If we're looking for ANY, we're done.
1028 		 */
1029 		if (type == dns_rdatatype_any) {
1030 			result = ISC_R_SUCCESS;
1031 			break;
1032 		}
1033 
1034 		/*
1035 		 * Look for the qtype.
1036 		 */
1037 		result = findrdataset(db, node, version, type, 0, now, rdataset,
1038 				      sigrdataset DNS__DB_FLARG_PASS);
1039 		if (result == ISC_R_SUCCESS) {
1040 			break;
1041 		}
1042 
1043 		/*
1044 		 * Look for a CNAME.
1045 		 */
1046 		if (type != dns_rdatatype_cname) {
1047 			result = findrdataset(
1048 				db, node, version, dns_rdatatype_cname, 0, now,
1049 				rdataset, sigrdataset DNS__DB_FLARG_PASS);
1050 			if (result == ISC_R_SUCCESS) {
1051 				result = DNS_R_CNAME;
1052 				break;
1053 			}
1054 		}
1055 
1056 		result = DNS_R_NXRRSET;
1057 		break;
1058 	}
1059 
1060 	if (rdataset == &xrdataset && dns_rdataset_isassociated(rdataset)) {
1061 		dns_rdataset_disassociate(rdataset);
1062 	}
1063 
1064 	if (foundname != NULL) {
1065 		dns_name_copy(xname, foundname);
1066 	}
1067 
1068 	if (nodep != NULL) {
1069 		*nodep = node;
1070 	} else if (node != NULL) {
1071 		detachnode(db, &node DNS__DB_FLARG_PASS);
1072 	}
1073 
1074 	return result;
1075 }
1076 
1077 static void
1078 attachnode(dns_db_t *db, dns_dbnode_t *source,
1079 	   dns_dbnode_t **targetp DNS__DB_FLARG) {
1080 	bdb_t *bdb = (bdb_t *)db;
1081 	bdbnode_t *node = (bdbnode_t *)source;
1082 
1083 	REQUIRE(VALID_BDB(bdb));
1084 
1085 	isc_refcount_increment(&node->references);
1086 
1087 	*targetp = source;
1088 }
1089 
1090 static void
1091 detachnode(dns_db_t *db, dns_dbnode_t **nodep DNS__DB_FLARG) {
1092 	bdb_t *bdb = (bdb_t *)db;
1093 	bdbnode_t *node = NULL;
1094 
1095 	REQUIRE(VALID_BDB(bdb));
1096 	REQUIRE(nodep != NULL && *nodep != NULL);
1097 
1098 	node = (bdbnode_t *)(*nodep);
1099 	*nodep = NULL;
1100 
1101 	if (isc_refcount_decrement(&node->references) == 1) {
1102 		destroynode(node);
1103 	}
1104 }
1105 
1106 static isc_result_t
1107 findrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version,
1108 	     dns_rdatatype_t type, dns_rdatatype_t covers, isc_stdtime_t now,
1109 	     dns_rdataset_t *rdataset,
1110 	     dns_rdataset_t *sigrdataset DNS__DB_FLARG) {
1111 	bdbnode_t *bdbnode = (bdbnode_t *)node;
1112 	dns_rdatalist_t *list = NULL;
1113 
1114 	REQUIRE(VALID_BDBNODE(bdbnode));
1115 
1116 	UNUSED(version);
1117 	UNUSED(covers);
1118 	UNUSED(now);
1119 	UNUSED(sigrdataset);
1120 
1121 	if (type == dns_rdatatype_rrsig) {
1122 		return ISC_R_NOTIMPLEMENTED;
1123 	}
1124 
1125 	list = ISC_LIST_HEAD(bdbnode->lists);
1126 	while (list != NULL) {
1127 		if (list->type == type) {
1128 			break;
1129 		}
1130 		list = ISC_LIST_NEXT(list, link);
1131 	}
1132 	if (list == NULL) {
1133 		return ISC_R_NOTFOUND;
1134 	}
1135 
1136 	new_rdataset(list, db, node, rdataset);
1137 
1138 	return ISC_R_SUCCESS;
1139 }
1140 
1141 static isc_result_t
1142 allrdatasets(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version,
1143 	     unsigned int options, isc_stdtime_t now,
1144 	     dns_rdatasetiter_t **iteratorp DNS__DB_FLARG) {
1145 	bdb_rdatasetiter_t *iterator = NULL;
1146 
1147 	REQUIRE(version == NULL || version == &dummy);
1148 
1149 	iterator = isc_mem_get(db->mctx, sizeof(bdb_rdatasetiter_t));
1150 	*iterator = (bdb_rdatasetiter_t){
1151 		.common.methods = &rdatasetiter_methods,
1152 		.common.db = db,
1153 		.common.version = version,
1154 		.common.options = options,
1155 		.common.now = now,
1156 		.common.magic = DNS_RDATASETITER_MAGIC,
1157 	};
1158 
1159 	attachnode(db, node, &iterator->common.node DNS__DB_FLARG_PASS);
1160 
1161 	*iteratorp = (dns_rdatasetiter_t *)iterator;
1162 
1163 	return ISC_R_SUCCESS;
1164 }
1165 
1166 static dns_dbmethods_t bdb_methods = {
1167 	.destroy = destroy,
1168 	.currentversion = currentversion,
1169 	.attachversion = attachversion,
1170 	.closeversion = closeversion,
1171 	.attachnode = attachnode,
1172 	.detachnode = detachnode,
1173 	.findrdataset = findrdataset,
1174 	.allrdatasets = allrdatasets,
1175 	.getoriginnode = getoriginnode,
1176 	.findnode = findnode,
1177 	.find = find,
1178 };
1179 
1180 static isc_result_t
1181 create(isc_mem_t *mctx, const dns_name_t *origin, dns_dbtype_t type,
1182        dns_rdataclass_t rdclass, unsigned int argc, char *argv[],
1183        void *implementation, dns_db_t **dbp) {
1184 	isc_result_t result;
1185 	bool needargs = false;
1186 	bdb_t *bdb = NULL;
1187 
1188 	REQUIRE(implementation != NULL);
1189 
1190 	if (type != dns_dbtype_zone) {
1191 		return ISC_R_NOTIMPLEMENTED;
1192 	}
1193 
1194 	bdb = isc_mem_get(mctx, sizeof(*bdb));
1195 	*bdb = (bdb_t){
1196 		.common = { .methods = &bdb_methods, .rdclass = rdclass },
1197 		.implementation = implementation,
1198 	};
1199 
1200 	isc_refcount_init(&bdb->common.references, 1);
1201 	isc_mem_attach(mctx, &bdb->common.mctx);
1202 	dns_name_init(&bdb->common.origin, NULL);
1203 	dns_name_dupwithoffsets(origin, mctx, &bdb->common.origin);
1204 
1205 	INSIST(argc >= 1);
1206 	if (strcmp(argv[0], "authors") == 0) {
1207 		bdb->lookup = authors_lookup;
1208 	} else if (strcmp(argv[0], "hostname") == 0) {
1209 		bdb->lookup = hostname_lookup;
1210 	} else if (strcmp(argv[0], "id") == 0) {
1211 		bdb->lookup = id_lookup;
1212 	} else if (strcmp(argv[0], "version") == 0) {
1213 		bdb->lookup = version_lookup;
1214 	} else if (strcmp(argv[0], "dns64") == 0) {
1215 		needargs = true;
1216 		bdb->lookup = empty_lookup;
1217 	} else if (strcmp(argv[0], "empty") == 0) {
1218 		needargs = true;
1219 		bdb->lookup = empty_lookup;
1220 	} else if (strcmp(argv[0], "ipv4only") == 0) {
1221 		needargs = true;
1222 		bdb->lookup = ipv4only_lookup;
1223 	} else {
1224 		needargs = true;
1225 		bdb->lookup = ipv4reverse_lookup;
1226 	}
1227 
1228 	if (needargs) {
1229 		if (argc != 3) {
1230 			result = DNS_R_SYNTAX;
1231 			goto cleanup;
1232 		}
1233 
1234 		bdb->server = isc_mem_strdup(named_g_mctx, argv[1]);
1235 		bdb->contact = isc_mem_strdup(named_g_mctx, argv[2]);
1236 	} else if (argc != 1) {
1237 		result = DNS_R_SYNTAX;
1238 		goto cleanup;
1239 	}
1240 
1241 	bdb->common.magic = DNS_DB_MAGIC;
1242 	bdb->common.impmagic = BDB_MAGIC;
1243 
1244 	*dbp = (dns_db_t *)bdb;
1245 
1246 	return ISC_R_SUCCESS;
1247 
1248 cleanup:
1249 	dns_name_free(&bdb->common.origin, mctx);
1250 	if (bdb->server != NULL) {
1251 		isc_mem_free(named_g_mctx, bdb->server);
1252 	}
1253 	if (bdb->contact != NULL) {
1254 		isc_mem_free(named_g_mctx, bdb->contact);
1255 	}
1256 
1257 	isc_mem_putanddetach(&bdb->common.mctx, bdb, sizeof(bdb_t));
1258 	return result;
1259 }
1260 
1261 /*
1262  * Builtin database registration functions
1263  */
1264 static bdbimplementation_t builtin = { .flags = 0 };
1265 static bdbimplementation_t dns64 = { .flags = BDB_DNS64 };
1266 
1267 isc_result_t
1268 named_builtin_init(void) {
1269 	isc_result_t result;
1270 
1271 	result = dns_db_register("_builtin", create, &builtin, named_g_mctx,
1272 				 &builtin.dbimp);
1273 	if (result != ISC_R_SUCCESS) {
1274 		return result;
1275 	}
1276 
1277 	result = dns_db_register("_dns64", create, &dns64, named_g_mctx,
1278 				 &dns64.dbimp);
1279 	if (result != ISC_R_SUCCESS) {
1280 		dns_db_unregister(&builtin.dbimp);
1281 		return result;
1282 	}
1283 
1284 	return ISC_R_SUCCESS;
1285 }
1286 
1287 void
1288 named_builtin_deinit(void) {
1289 	if (builtin.dbimp != NULL) {
1290 		dns_db_unregister(&builtin.dbimp);
1291 	}
1292 	if (dns64.dbimp != NULL) {
1293 		dns_db_unregister(&dns64.dbimp);
1294 	}
1295 }
1296