xref: /minix3/external/bsd/bind/dist/bin/named/builtin.c (revision 00b67f09dd46474d133c95011a48590a8e8f94c7)
1 /*	$NetBSD: builtin.c,v 1.8 2014/12/10 04:37:51 christos Exp $	*/
2 
3 /*
4  * Copyright (C) 2004, 2005, 2007, 2009-2014  Internet Systems Consortium, Inc. ("ISC")
5  * Copyright (C) 2001-2003  Internet Software Consortium.
6  *
7  * Permission to use, copy, modify, and/or distribute this software for any
8  * purpose with or without fee is hereby granted, provided that the above
9  * copyright notice and this permission notice appear in all copies.
10  *
11  * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
12  * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
13  * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
14  * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
15  * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
16  * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
17  * PERFORMANCE OF THIS SOFTWARE.
18  */
19 
20 /* Id: builtin.c,v 1.26 2012/01/21 19:44:18 each Exp  */
21 
22 /*! \file
23  * \brief
24  * The built-in "version", "hostname", "id", "authors" and "empty" databases.
25  */
26 
27 #include <config.h>
28 
29 #include <string.h>
30 #include <stdio.h>
31 
32 #include <isc/mem.h>
33 #include <isc/print.h>
34 #include <isc/result.h>
35 #include <isc/util.h>
36 
37 #include <dns/result.h>
38 #include <dns/sdb.h>
39 
40 #include <named/builtin.h>
41 #include <named/globals.h>
42 #include <named/server.h>
43 #include <named/os.h>
44 
45 typedef struct builtin builtin_t;
46 
47 static isc_result_t do_version_lookup(dns_sdblookup_t *lookup);
48 static isc_result_t do_hostname_lookup(dns_sdblookup_t *lookup);
49 static isc_result_t do_authors_lookup(dns_sdblookup_t *lookup);
50 static isc_result_t do_id_lookup(dns_sdblookup_t *lookup);
51 static isc_result_t do_empty_lookup(dns_sdblookup_t *lookup);
52 static isc_result_t do_dns64_lookup(dns_sdblookup_t *lookup);
53 
54 /*
55  * We can't use function pointers as the db_data directly
56  * because ANSI C does not guarantee that function pointers
57  * can safely be cast to void pointers and back.
58  */
59 
60 struct builtin {
61 	isc_result_t (*do_lookup)(dns_sdblookup_t *lookup);
62 	char *server;
63 	char *contact;
64 };
65 
66 static builtin_t version_builtin = { do_version_lookup,  NULL, NULL };
67 static builtin_t hostname_builtin = { do_hostname_lookup, NULL, NULL };
68 static builtin_t authors_builtin = { do_authors_lookup, NULL, NULL };
69 static builtin_t id_builtin = { do_id_lookup, NULL, NULL };
70 static builtin_t empty_builtin = { do_empty_lookup, NULL, NULL };
71 static builtin_t dns64_builtin = { do_dns64_lookup, NULL, NULL };
72 
73 static dns_sdbimplementation_t *builtin_impl;
74 static dns_sdbimplementation_t *dns64_impl;
75 
76 /*
77  * Pre computed HEX * 16 or 1 table.
78  */
79 static const unsigned char hex16[256] = {
80 	 1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1, /*00*/
81 	 1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,	/*10*/
82 	 1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1, /*20*/
83 	 0, 16, 32, 48, 64, 80, 96,112,128,144,  1,  1,  1,  1,  1,  1,	/*30*/
84 	 1,160,176,192,208,224,240,  1,  1,  1,  1,  1,  1,  1,  1,  1, /*40*/
85 	 1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1, /*50*/
86 	 1,160,176,192,208,224,240,  1,  1,  1,  1,  1,  1,  1,  1,  1, /*60*/
87 	 1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1, /*70*/
88 	 1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1, /*80*/
89 	 1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1, /*90*/
90 	 1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1, /*A0*/
91 	 1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1, /*B0*/
92 	 1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1, /*C0*/
93 	 1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1, /*D0*/
94 	 1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1, /*E0*/
95 	 1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1  /*F0*/
96 };
97 
98 const unsigned char decimal[] = "0123456789";
99 
100 static size_t
dns64_rdata(unsigned char * v,size_t start,unsigned char * rdata)101 dns64_rdata(unsigned char *v, size_t start, unsigned char *rdata) {
102 	size_t i, j = 0;
103 
104 	for (i = 0; i < 4U; i++) {
105 		unsigned char c = v[start++];
106 		if (start == 7U)
107 			start++;
108 		if (c > 99) {
109 			rdata[j++] = 3;
110 			rdata[j++] = decimal[c/100]; c = c % 100;
111 			rdata[j++] = decimal[c/10]; c = c % 10;
112 			rdata[j++] = decimal[c];
113 		} else if (c > 9) {
114 			rdata[j++] = 2;
115 			rdata[j++] = decimal[c/10]; c = c % 10;
116 			rdata[j++] = decimal[c];
117 		} else {
118 			rdata[j++] = 1;
119 			rdata[j++] = decimal[c];
120 		}
121 	}
122 	memmove(&rdata[j], "\07in-addr\04arpa", 14);
123 	return (j + 14);
124 }
125 
126 static isc_result_t
dns64_cname(const dns_name_t * zone,const dns_name_t * name,dns_sdblookup_t * lookup)127 dns64_cname(const dns_name_t *zone, const dns_name_t *name,
128 	    dns_sdblookup_t *lookup)
129 {
130 	size_t zlen, nlen, j, len;
131 	unsigned char v[16], n;
132 	unsigned int i;
133 	unsigned char rdata[sizeof("123.123.123.123.in-addr.arpa.")];
134 	unsigned char *ndata;
135 
136 	/*
137 	 * The combined length of the zone and name is 74.
138 	 *
139 	 * The minimum zone length is 10 ((3)ip6(4)arpa(0)).
140 	 *
141 	 * The length of name should always be even as we are expecting
142 	 * a series of nibbles.
143 	 */
144 	zlen = zone->length;
145 	nlen = name->length;
146 	if ((zlen + nlen) > 74U || zlen < 10U || (nlen % 2) != 0U)
147 		return (ISC_R_NOTFOUND);
148 
149 	/*
150 	 * We assume the zone name is well formed.
151 	 */
152 
153 	/*
154 	 * XXXMPA We could check the dns64 suffix here if we need to.
155 	 */
156 	/*
157 	 * Check that name is a series of nibbles.
158 	 * Compute the byte values that correspond to the nibbles as we go.
159 	 *
160 	 * Shift the final result 4 bits, by setting 'i' to 1, if we if we
161 	 * have a odd number of nibbles so that "must be zero" tests below
162 	 * are byte aligned and we correctly return ISC_R_NOTFOUND or
163 	 * ISC_R_SUCCESS.  We will not generate a CNAME in this case.
164 	 */
165 	ndata = name->ndata;
166 	i = (nlen % 4) == 2U ? 1 : 0;
167 	j = nlen;
168 	memset(v, 0, sizeof(v));
169 	while (j != 0U) {
170 		INSIST((i/2) < sizeof(v));
171 		if (ndata[0] != 1)
172 			return (ISC_R_NOTFOUND);
173 		n = hex16[ndata[1]&0xff];
174 		if (n == 1)
175 			return (ISC_R_NOTFOUND);
176 		v[i/2] = n | (v[i/2]>>4);
177 		j -= 2;
178 		ndata += 2;
179 		i++;
180 	}
181 
182 	/*
183 	 * If we get here then we know name only consisted of nibbles.
184 	 * Now we need to determine if the name exists or not and whether
185 	 * it corresponds to a empty node in the zone or there should be
186 	 * a CNAME.
187 	 */
188 #define ZLEN(x) (10 + (x)/2)
189 	switch (zlen) {
190 	case ZLEN(32):	/* prefix len 32 */
191 		/*
192 		 * The nibbles that map to this byte must be zero for 'name'
193 		 * to exist in the zone.
194 		 */
195 		if (nlen > 16U && v[(nlen-1)/4 - 4] != 0)
196 			return (ISC_R_NOTFOUND);
197 		/*
198 		 * If the total length is not 74 then this is a empty node
199 		 * so return success.
200 		 */
201 		if (nlen + zlen != 74U)
202 			return (ISC_R_SUCCESS);
203 		len = dns64_rdata(v, 8, rdata);
204 		break;
205 	case ZLEN(40):	/* prefix len 40 */
206 		/*
207 		 * The nibbles that map to this byte must be zero for 'name'
208 		 * to exist in the zone.
209 		 */
210 		if (nlen > 12U && v[(nlen-1)/4 - 3] != 0)
211 			return (ISC_R_NOTFOUND);
212 		/*
213 		 * If the total length is not 74 then this is a empty node
214 		 * so return success.
215 		 */
216 		if (nlen + zlen != 74U)
217 			return (ISC_R_SUCCESS);
218 		len = dns64_rdata(v, 6, rdata);
219 		break;
220 	case ZLEN(48):	/* prefix len 48 */
221 		/*
222 		 * The nibbles that map to this byte must be zero for 'name'
223 		 * to exist in the zone.
224 		 */
225 		if (nlen > 8U && v[(nlen-1)/4 - 2] != 0)
226 			return (ISC_R_NOTFOUND);
227 		/*
228 		 * If the total length is not 74 then this is a empty node
229 		 * so return success.
230 		 */
231 		if (nlen + zlen != 74U)
232 			return (ISC_R_SUCCESS);
233 		len = dns64_rdata(v, 5, rdata);
234 		break;
235 	case ZLEN(56):	/* prefix len 56 */
236 		/*
237 		 * The nibbles that map to this byte must be zero for 'name'
238 		 * to exist in the zone.
239 		 */
240 		if (nlen > 4U && v[(nlen-1)/4 - 1] != 0)
241 			return (ISC_R_NOTFOUND);
242 		/*
243 		 * If the total length is not 74 then this is a empty node
244 		 * so return success.
245 		 */
246 		if (nlen + zlen != 74U)
247 			return (ISC_R_SUCCESS);
248 		len = dns64_rdata(v, 4, rdata);
249 		break;
250 	case ZLEN(64):	/* prefix len 64 */
251 		/*
252 		 * The nibbles that map to this byte must be zero for 'name'
253 		 * to exist in the zone.
254 		 */
255 		if (v[(nlen-1)/4] != 0)
256 			return (ISC_R_NOTFOUND);
257 		/*
258 		 * If the total length is not 74 then this is a empty node
259 		 * so return success.
260 		 */
261 		if (nlen + zlen != 74U)
262 			return (ISC_R_SUCCESS);
263 		len = dns64_rdata(v, 3, rdata);
264 		break;
265 	case ZLEN(96):	/* prefix len 96 */
266 		/*
267 		 * If the total length is not 74 then this is a empty node
268 		 * so return success.
269 		 */
270 		if (nlen + zlen != 74U)
271 			return (ISC_R_SUCCESS);
272 		len = dns64_rdata(v, 0, rdata);
273 		break;
274 	default:
275 		/*
276 		 * This should never be reached unless someone adds a
277 		 * zone declaration with this internal type to named.conf.
278 		 */
279 		return (ISC_R_NOTFOUND);
280 	}
281 	return (dns_sdb_putrdata(lookup, dns_rdatatype_cname, 600,
282 				 rdata, (unsigned int)len));
283 }
284 
285 static isc_result_t
builtin_lookup(const char * zone,const char * name,void * dbdata,dns_sdblookup_t * lookup,dns_clientinfomethods_t * methods,dns_clientinfo_t * clientinfo)286 builtin_lookup(const char *zone, const char *name, void *dbdata,
287 	       dns_sdblookup_t *lookup, dns_clientinfomethods_t *methods,
288 	       dns_clientinfo_t *clientinfo)
289 {
290 	builtin_t *b = (builtin_t *) dbdata;
291 
292 	UNUSED(zone);
293 	UNUSED(methods);
294 	UNUSED(clientinfo);
295 
296 	if (strcmp(name, "@") == 0)
297 		return (b->do_lookup(lookup));
298 	else
299 		return (ISC_R_NOTFOUND);
300 }
301 
302 static isc_result_t
dns64_lookup(const dns_name_t * zone,const dns_name_t * name,void * dbdata,dns_sdblookup_t * lookup,dns_clientinfomethods_t * methods,dns_clientinfo_t * clientinfo)303 dns64_lookup(const dns_name_t *zone, const dns_name_t *name, void *dbdata,
304 	     dns_sdblookup_t *lookup, dns_clientinfomethods_t *methods,
305 	     dns_clientinfo_t *clientinfo)
306 {
307 	builtin_t *b = (builtin_t *) dbdata;
308 
309 	UNUSED(methods);
310 	UNUSED(clientinfo);
311 
312 	if (name->labels == 0 && name->length == 0)
313 		return (b->do_lookup(lookup));
314 	else
315 		return (dns64_cname(zone, name, lookup));
316 }
317 
318 static isc_result_t
put_txt(dns_sdblookup_t * lookup,const char * text)319 put_txt(dns_sdblookup_t *lookup, const char *text) {
320 	unsigned char buf[256];
321 	unsigned int len = strlen(text);
322 	if (len > 255)
323 		len = 255; /* Silently truncate */
324 	buf[0] = len;
325 	memmove(&buf[1], text, len);
326 	return (dns_sdb_putrdata(lookup, dns_rdatatype_txt, 0, buf, len + 1));
327 }
328 
329 static isc_result_t
do_version_lookup(dns_sdblookup_t * lookup)330 do_version_lookup(dns_sdblookup_t *lookup) {
331 	if (ns_g_server->version_set) {
332 		if (ns_g_server->version == NULL)
333 			return (ISC_R_SUCCESS);
334 		else
335 			return (put_txt(lookup, ns_g_server->version));
336 	} else {
337 		return (put_txt(lookup, ns_g_version));
338 	}
339 }
340 
341 static isc_result_t
do_hostname_lookup(dns_sdblookup_t * lookup)342 do_hostname_lookup(dns_sdblookup_t *lookup) {
343 	if (ns_g_server->hostname_set) {
344 		if (ns_g_server->hostname == NULL)
345 			return (ISC_R_SUCCESS);
346 		else
347 			return (put_txt(lookup, ns_g_server->hostname));
348 	} else {
349 		char buf[256];
350 		isc_result_t result = ns_os_gethostname(buf, sizeof(buf));
351 		if (result != ISC_R_SUCCESS)
352 			return (result);
353 		return (put_txt(lookup, buf));
354 	}
355 }
356 
357 static isc_result_t
do_authors_lookup(dns_sdblookup_t * lookup)358 do_authors_lookup(dns_sdblookup_t *lookup) {
359 	isc_result_t result;
360 	const char **p;
361 	static const char *authors[] = {
362 		"Mark Andrews",
363 		"Curtis Blackburn",
364 		"James Brister",
365 		"Ben Cottrell",
366 		"John H. DuBois III",
367 		"Francis Dupont",
368 		"Michael Graff",
369 		"Andreas Gustafsson",
370 		"Bob Halley",
371 		"Evan Hunt",
372 		"JINMEI Tatuya",
373 		"David Lawrence",
374 		"Scott Mann",
375 		"Danny Mayer",
376 		"Damien Neil",
377 		"Matt Nelson",
378 		"Jeremy C. Reed",
379 		"Michael Sawyer",
380 		"Brian Wellington",
381 		NULL
382 	};
383 
384 	/*
385 	 * If a version string is specified, disable the authors.bind zone.
386 	 */
387 	if (ns_g_server->version_set)
388 		return (ISC_R_SUCCESS);
389 
390 	for (p = authors; *p != NULL; p++) {
391 		result = put_txt(lookup, *p);
392 		if (result != ISC_R_SUCCESS)
393 			return (result);
394 	}
395 	return (ISC_R_SUCCESS);
396 }
397 
398 static isc_result_t
do_id_lookup(dns_sdblookup_t * lookup)399 do_id_lookup(dns_sdblookup_t *lookup) {
400 
401 	if (ns_g_server->server_usehostname) {
402 		char buf[256];
403 		isc_result_t result = ns_os_gethostname(buf, sizeof(buf));
404 		if (result != ISC_R_SUCCESS)
405 			return (result);
406 		return (put_txt(lookup, buf));
407 	}
408 
409 	if (ns_g_server->server_id == NULL)
410 		return (ISC_R_SUCCESS);
411 	else
412 		return (put_txt(lookup, ns_g_server->server_id));
413 }
414 
415 static isc_result_t
do_dns64_lookup(dns_sdblookup_t * lookup)416 do_dns64_lookup(dns_sdblookup_t *lookup) {
417 	UNUSED(lookup);
418 	return (ISC_R_SUCCESS);
419 }
420 
421 static isc_result_t
do_empty_lookup(dns_sdblookup_t * lookup)422 do_empty_lookup(dns_sdblookup_t *lookup) {
423 
424 	UNUSED(lookup);
425 	return (ISC_R_SUCCESS);
426 }
427 
428 static isc_result_t
builtin_authority(const char * zone,void * dbdata,dns_sdblookup_t * lookup)429 builtin_authority(const char *zone, void *dbdata, dns_sdblookup_t *lookup) {
430 	isc_result_t result;
431 	const char *contact = "hostmaster";
432 	const char *server = "@";
433 	builtin_t *b = (builtin_t *) dbdata;
434 
435 	UNUSED(zone);
436 	UNUSED(dbdata);
437 
438 	if (b == &empty_builtin) {
439 		server = ".";
440 		contact = ".";
441 	} else {
442 		if (b->server != NULL)
443 			server = b->server;
444 		if (b->contact != NULL)
445 			contact = b->contact;
446 	}
447 
448 	result = dns_sdb_putsoa(lookup, server, contact, 0);
449 	if (result != ISC_R_SUCCESS)
450 		return (ISC_R_FAILURE);
451 
452 	result = dns_sdb_putrr(lookup, "ns", 0, server);
453 	if (result != ISC_R_SUCCESS)
454 		return (ISC_R_FAILURE);
455 
456 	return (ISC_R_SUCCESS);
457 }
458 
459 static isc_result_t
builtin_create(const char * zone,int argc,char ** argv,void * driverdata,void ** dbdata)460 builtin_create(const char *zone, int argc, char **argv,
461 	       void *driverdata, void **dbdata)
462 {
463 	REQUIRE(argc >= 1);
464 
465 	UNUSED(zone);
466 	UNUSED(driverdata);
467 
468 	if (strcmp(argv[0], "empty") == 0 || strcmp(argv[0], "dns64") == 0) {
469 		if (argc != 3)
470 			return (DNS_R_SYNTAX);
471 	} else if (argc != 1)
472 		return (DNS_R_SYNTAX);
473 
474 	if (strcmp(argv[0], "version") == 0)
475 		*dbdata = &version_builtin;
476 	else if (strcmp(argv[0], "hostname") == 0)
477 		*dbdata = &hostname_builtin;
478 	else if (strcmp(argv[0], "authors") == 0)
479 		*dbdata = &authors_builtin;
480 	else if (strcmp(argv[0], "id") == 0)
481 		*dbdata = &id_builtin;
482 	else if (strcmp(argv[0], "empty") == 0 ||
483 		 strcmp(argv[0], "dns64") == 0) {
484 		builtin_t *empty;
485 		char *server;
486 		char *contact;
487 		/*
488 		 * We don't want built-in zones to fail.  Fallback to
489 		 * the static configuration if memory allocation fails.
490 		 */
491 		empty = isc_mem_get(ns_g_mctx, sizeof(*empty));
492 		server = isc_mem_strdup(ns_g_mctx, argv[1]);
493 		contact = isc_mem_strdup(ns_g_mctx, argv[2]);
494 		if (empty == NULL || server == NULL || contact == NULL) {
495 			if (strcmp(argv[0], "empty") == 0)
496 				*dbdata = &empty_builtin;
497 			else
498 				*dbdata = &dns64_builtin;
499 			if (server != NULL)
500 				isc_mem_free(ns_g_mctx, server);
501 			if (contact != NULL)
502 				isc_mem_free(ns_g_mctx, contact);
503 			if (empty != NULL)
504 				isc_mem_put(ns_g_mctx, empty, sizeof (*empty));
505 		} else {
506 			if (strcmp(argv[0], "empty") == 0)
507 				memmove(empty, &empty_builtin,
508 					sizeof (empty_builtin));
509 			else
510 				memmove(empty, &dns64_builtin,
511 					sizeof (empty_builtin));
512 			empty->server = server;
513 			empty->contact = contact;
514 			*dbdata = empty;
515 		}
516 	} else
517 		return (ISC_R_NOTIMPLEMENTED);
518 	return (ISC_R_SUCCESS);
519 }
520 
521 static void
builtin_destroy(const char * zone,void * driverdata,void ** dbdata)522 builtin_destroy(const char *zone, void *driverdata, void **dbdata) {
523 	builtin_t *b = (builtin_t *) *dbdata;
524 
525 	UNUSED(zone);
526 	UNUSED(driverdata);
527 
528 	/*
529 	 * Don't free the static versions.
530 	 */
531 	if (*dbdata == &version_builtin || *dbdata == &hostname_builtin ||
532 	    *dbdata == &authors_builtin || *dbdata == &id_builtin ||
533 	    *dbdata == &empty_builtin || *dbdata == &dns64_builtin)
534 		return;
535 
536 	isc_mem_free(ns_g_mctx, b->server);
537 	isc_mem_free(ns_g_mctx, b->contact);
538 	isc_mem_put(ns_g_mctx, b, sizeof (*b));
539 }
540 
541 static dns_sdbmethods_t builtin_methods = {
542 	builtin_lookup,
543 	builtin_authority,
544 	NULL,		/* allnodes */
545 	builtin_create,
546 	builtin_destroy,
547 	NULL
548 };
549 
550 static dns_sdbmethods_t dns64_methods = {
551 	NULL,
552 	builtin_authority,
553 	NULL,		/* allnodes */
554 	builtin_create,
555 	builtin_destroy,
556 	dns64_lookup,
557 };
558 
559 isc_result_t
ns_builtin_init(void)560 ns_builtin_init(void) {
561 	RUNTIME_CHECK(dns_sdb_register("_builtin", &builtin_methods, NULL,
562 				       DNS_SDBFLAG_RELATIVEOWNER |
563 				       DNS_SDBFLAG_RELATIVERDATA,
564 				       ns_g_mctx, &builtin_impl)
565 		      == ISC_R_SUCCESS);
566 	RUNTIME_CHECK(dns_sdb_register("_dns64", &dns64_methods, NULL,
567 				       DNS_SDBFLAG_RELATIVEOWNER |
568 				       DNS_SDBFLAG_RELATIVERDATA |
569 				       DNS_SDBFLAG_DNS64,
570 				       ns_g_mctx, &dns64_impl)
571 		      == ISC_R_SUCCESS);
572 	return (ISC_R_SUCCESS);
573 }
574 
575 void
ns_builtin_deinit(void)576 ns_builtin_deinit(void) {
577 	dns_sdb_unregister(&builtin_impl);
578 	dns_sdb_unregister(&dns64_impl);
579 }
580