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