1/*	$NetBSD: builtin.c,v 1.5 2012/12/04 23:38:38 spz Exp $	*/
2
3/*
4 * Copyright (C) 2004, 2005, 2007, 2009-2012  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
45typedef struct builtin builtin_t;
46
47static isc_result_t do_version_lookup(dns_sdblookup_t *lookup);
48static isc_result_t do_hostname_lookup(dns_sdblookup_t *lookup);
49static isc_result_t do_authors_lookup(dns_sdblookup_t *lookup);
50static isc_result_t do_id_lookup(dns_sdblookup_t *lookup);
51static isc_result_t do_empty_lookup(dns_sdblookup_t *lookup);
52static 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
60struct builtin {
61	isc_result_t (*do_lookup)(dns_sdblookup_t *lookup);
62	char *server;
63	char *contact;
64};
65
66static builtin_t version_builtin = { do_version_lookup,  NULL, NULL };
67static builtin_t hostname_builtin = { do_hostname_lookup, NULL, NULL };
68static builtin_t authors_builtin = { do_authors_lookup, NULL, NULL };
69static builtin_t id_builtin = { do_id_lookup, NULL, NULL };
70static builtin_t empty_builtin = { do_empty_lookup, NULL, NULL };
71static builtin_t dns64_builtin = { do_dns64_lookup, NULL, NULL };
72
73static dns_sdbimplementation_t *builtin_impl;
74static dns_sdbimplementation_t *dns64_impl;
75
76/*
77 * Pre computed HEX * 16 or 1 table.
78 */
79static 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
98const unsigned char decimal[] = "0123456789";
99
100static size_t
101dns64_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	memcpy(&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{
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, rdata, len));
282}
283
284static isc_result_t
285builtin_lookup(const char *zone, const char *name, void *dbdata,
286	       dns_sdblookup_t *lookup, dns_clientinfomethods_t *methods,
287	       dns_clientinfo_t *clientinfo)
288{
289	builtin_t *b = (builtin_t *) dbdata;
290
291	UNUSED(zone);
292	UNUSED(methods);
293	UNUSED(clientinfo);
294
295	if (strcmp(name, "@") == 0)
296		return (b->do_lookup(lookup));
297	else
298		return (ISC_R_NOTFOUND);
299}
300
301static isc_result_t
302dns64_lookup(const dns_name_t *zone, const dns_name_t *name, void *dbdata,
303	     dns_sdblookup_t *lookup, dns_clientinfomethods_t *methods,
304	     dns_clientinfo_t *clientinfo)
305{
306	builtin_t *b = (builtin_t *) dbdata;
307
308	UNUSED(methods);
309	UNUSED(clientinfo);
310
311	if (name->labels == 0 && name->length == 0)
312		return (b->do_lookup(lookup));
313	else
314		return (dns64_cname(zone, name, lookup));
315}
316
317static isc_result_t
318put_txt(dns_sdblookup_t *lookup, const char *text) {
319	unsigned char buf[256];
320	unsigned int len = strlen(text);
321	if (len > 255)
322		len = 255; /* Silently truncate */
323	buf[0] = len;
324	memcpy(&buf[1], text, len);
325	return (dns_sdb_putrdata(lookup, dns_rdatatype_txt, 0, buf, len + 1));
326}
327
328static isc_result_t
329do_version_lookup(dns_sdblookup_t *lookup) {
330	if (ns_g_server->version_set) {
331		if (ns_g_server->version == NULL)
332			return (ISC_R_SUCCESS);
333		else
334			return (put_txt(lookup, ns_g_server->version));
335	} else {
336		return (put_txt(lookup, ns_g_version));
337	}
338}
339
340static isc_result_t
341do_hostname_lookup(dns_sdblookup_t *lookup) {
342	if (ns_g_server->hostname_set) {
343		if (ns_g_server->hostname == NULL)
344			return (ISC_R_SUCCESS);
345		else
346			return (put_txt(lookup, ns_g_server->hostname));
347	} else {
348		char buf[256];
349		isc_result_t result = ns_os_gethostname(buf, sizeof(buf));
350		if (result != ISC_R_SUCCESS)
351			return (result);
352		return (put_txt(lookup, buf));
353	}
354}
355
356static isc_result_t
357do_authors_lookup(dns_sdblookup_t *lookup) {
358	isc_result_t result;
359	const char **p;
360	static const char *authors[] = {
361		"Mark Andrews",
362		"Curtis Blackburn",
363		"James Brister",
364		"Ben Cottrell",
365		"John H. DuBois III",
366		"Francis Dupont",
367		"Michael Graff",
368		"Andreas Gustafsson",
369		"Bob Halley",
370		"Evan Hunt",
371		"JINMEI Tatuya",
372		"David Lawrence",
373		"Scott Mann",
374		"Danny Mayer",
375		"Damien Neil",
376		"Matt Nelson",
377		"Jeremy C. Reed",
378		"Michael Sawyer",
379		"Brian Wellington",
380		NULL
381	};
382
383	/*
384	 * If a version string is specified, disable the authors.bind zone.
385	 */
386	if (ns_g_server->version_set)
387		return (ISC_R_SUCCESS);
388
389	for (p = authors; *p != NULL; p++) {
390		result = put_txt(lookup, *p);
391		if (result != ISC_R_SUCCESS)
392			return (result);
393	}
394	return (ISC_R_SUCCESS);
395}
396
397static isc_result_t
398do_id_lookup(dns_sdblookup_t *lookup) {
399
400	if (ns_g_server->server_usehostname) {
401		char buf[256];
402		isc_result_t result = ns_os_gethostname(buf, sizeof(buf));
403		if (result != ISC_R_SUCCESS)
404			return (result);
405		return (put_txt(lookup, buf));
406	}
407
408	if (ns_g_server->server_id == NULL)
409		return (ISC_R_SUCCESS);
410	else
411		return (put_txt(lookup, ns_g_server->server_id));
412}
413
414static isc_result_t
415do_dns64_lookup(dns_sdblookup_t *lookup) {
416	UNUSED(lookup);
417	return (ISC_R_SUCCESS);
418}
419
420static isc_result_t
421do_empty_lookup(dns_sdblookup_t *lookup) {
422
423	UNUSED(lookup);
424	return (ISC_R_SUCCESS);
425}
426
427static isc_result_t
428builtin_authority(const char *zone, void *dbdata, dns_sdblookup_t *lookup) {
429	isc_result_t result;
430	const char *contact = "hostmaster";
431	const char *server = "@";
432	builtin_t *b = (builtin_t *) dbdata;
433
434	UNUSED(zone);
435	UNUSED(dbdata);
436
437	if (b == &empty_builtin) {
438		server = ".";
439		contact = ".";
440	} else {
441		if (b->server != NULL)
442			server = b->server;
443		if (b->contact != NULL)
444			contact = b->contact;
445	}
446
447	result = dns_sdb_putsoa(lookup, server, contact, 0);
448	if (result != ISC_R_SUCCESS)
449		return (ISC_R_FAILURE);
450
451	result = dns_sdb_putrr(lookup, "ns", 0, server);
452	if (result != ISC_R_SUCCESS)
453		return (ISC_R_FAILURE);
454
455	return (ISC_R_SUCCESS);
456}
457
458static isc_result_t
459builtin_create(const char *zone, int argc, char **argv,
460	       void *driverdata, void **dbdata)
461{
462	REQUIRE(argc >= 1);
463
464	UNUSED(zone);
465	UNUSED(driverdata);
466
467	if (strcmp(argv[0], "empty") == 0 || strcmp(argv[0], "dns64") == 0) {
468		if (argc != 3)
469			return (DNS_R_SYNTAX);
470	} else if (argc != 1)
471		return (DNS_R_SYNTAX);
472
473	if (strcmp(argv[0], "version") == 0)
474		*dbdata = &version_builtin;
475	else if (strcmp(argv[0], "hostname") == 0)
476		*dbdata = &hostname_builtin;
477	else if (strcmp(argv[0], "authors") == 0)
478		*dbdata = &authors_builtin;
479	else if (strcmp(argv[0], "id") == 0)
480		*dbdata = &id_builtin;
481	else if (strcmp(argv[0], "empty") == 0 ||
482		 strcmp(argv[0], "dns64") == 0) {
483		builtin_t *empty;
484		char *server;
485		char *contact;
486		/*
487		 * We don't want built-in zones to fail.  Fallback to
488		 * the static configuration if memory allocation fails.
489		 */
490		empty = isc_mem_get(ns_g_mctx, sizeof(*empty));
491		server = isc_mem_strdup(ns_g_mctx, argv[1]);
492		contact = isc_mem_strdup(ns_g_mctx, argv[2]);
493		if (empty == NULL || server == NULL || contact == NULL) {
494			if (strcmp(argv[0], "empty") == 0)
495				*dbdata = &empty_builtin;
496			else
497				*dbdata = &dns64_builtin;
498			if (server != NULL)
499				isc_mem_free(ns_g_mctx, server);
500			if (contact != NULL)
501				isc_mem_free(ns_g_mctx, contact);
502			if (empty != NULL)
503				isc_mem_put(ns_g_mctx, empty, sizeof (*empty));
504		} else {
505			if (strcmp(argv[0], "empty") == 0)
506				memcpy(empty, &empty_builtin,
507				       sizeof (empty_builtin));
508			else
509				memcpy(empty, &dns64_builtin,
510				       sizeof (empty_builtin));
511			empty->server = server;
512			empty->contact = contact;
513			*dbdata = empty;
514		}
515	} else
516		return (ISC_R_NOTIMPLEMENTED);
517	return (ISC_R_SUCCESS);
518}
519
520static void
521builtin_destroy(const char *zone, void *driverdata, void **dbdata) {
522	builtin_t *b = (builtin_t *) *dbdata;
523
524	UNUSED(zone);
525	UNUSED(driverdata);
526
527	/*
528	 * Don't free the static versions.
529	 */
530	if (*dbdata == &version_builtin || *dbdata == &hostname_builtin ||
531	    *dbdata == &authors_builtin || *dbdata == &id_builtin ||
532	    *dbdata == &empty_builtin || *dbdata == &dns64_builtin)
533		return;
534
535	isc_mem_free(ns_g_mctx, b->server);
536	isc_mem_free(ns_g_mctx, b->contact);
537	isc_mem_put(ns_g_mctx, b, sizeof (*b));
538}
539
540static dns_sdbmethods_t builtin_methods = {
541	builtin_lookup,
542	builtin_authority,
543	NULL,		/* allnodes */
544	builtin_create,
545	builtin_destroy,
546	NULL
547};
548
549static dns_sdbmethods_t dns64_methods = {
550	NULL,
551	builtin_authority,
552	NULL,		/* allnodes */
553	builtin_create,
554	builtin_destroy,
555	dns64_lookup,
556};
557
558isc_result_t
559ns_builtin_init(void) {
560	RUNTIME_CHECK(dns_sdb_register("_builtin", &builtin_methods, NULL,
561				       DNS_SDBFLAG_RELATIVEOWNER |
562				       DNS_SDBFLAG_RELATIVERDATA,
563				       ns_g_mctx, &builtin_impl)
564		      == ISC_R_SUCCESS);
565	RUNTIME_CHECK(dns_sdb_register("_dns64", &dns64_methods, NULL,
566				       DNS_SDBFLAG_RELATIVEOWNER |
567				       DNS_SDBFLAG_RELATIVERDATA |
568				       DNS_SDBFLAG_DNS64,
569				       ns_g_mctx, &dns64_impl)
570		      == ISC_R_SUCCESS);
571	return (ISC_R_SUCCESS);
572}
573
574void
575ns_builtin_deinit(void) {
576	dns_sdb_unregister(&builtin_impl);
577	dns_sdb_unregister(&dns64_impl);
578}
579