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