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