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