1 /*
2 * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
3 *
4 * SPDX-License-Identifier: MPL-2.0
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 https://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
35 typedef struct builtin builtin_t;
36
37 static isc_result_t
38 do_version_lookup(dns_sdblookup_t *lookup);
39 static isc_result_t
40 do_hostname_lookup(dns_sdblookup_t *lookup);
41 static isc_result_t
42 do_authors_lookup(dns_sdblookup_t *lookup);
43 static isc_result_t
44 do_id_lookup(dns_sdblookup_t *lookup);
45 static isc_result_t
46 do_empty_lookup(dns_sdblookup_t *lookup);
47 static isc_result_t
48 do_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
56 struct builtin {
57 isc_result_t (*do_lookup)(dns_sdblookup_t *lookup);
58 char *server;
59 char *contact;
60 };
61
62 static builtin_t version_builtin = { do_version_lookup, NULL, NULL };
63 static builtin_t hostname_builtin = { do_hostname_lookup, NULL, NULL };
64 static builtin_t authors_builtin = { do_authors_lookup, NULL, NULL };
65 static builtin_t id_builtin = { do_id_lookup, NULL, NULL };
66 static builtin_t empty_builtin = { do_empty_lookup, NULL, NULL };
67 static builtin_t dns64_builtin = { do_dns64_lookup, NULL, NULL };
68
69 static dns_sdbimplementation_t *builtin_impl;
70 static dns_sdbimplementation_t *dns64_impl;
71
72 /*
73 * Pre computed HEX * 16 or 1 table.
74 */
75 static 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
94 const unsigned char decimal[] = "0123456789";
95
96 static size_t
dns64_rdata(unsigned char * v,size_t start,unsigned char * rdata)97 dns64_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
126 static isc_result_t
dns64_cname(const dns_name_t * zone,const dns_name_t * name,dns_sdblookup_t * lookup)127 dns64_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
298 static isc_result_t
builtin_lookup(const char * zone,const char * name,void * dbdata,dns_sdblookup_t * lookup,dns_clientinfomethods_t * methods,dns_clientinfo_t * clientinfo)299 builtin_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
315 static isc_result_t
dns64_lookup(const dns_name_t * zone,const dns_name_t * name,void * dbdata,dns_sdblookup_t * lookup,dns_clientinfomethods_t * methods,dns_clientinfo_t * clientinfo)316 dns64_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
331 static isc_result_t
put_txt(dns_sdblookup_t * lookup,const char * text)332 put_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
343 static isc_result_t
do_version_lookup(dns_sdblookup_t * lookup)344 do_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
356 static isc_result_t
do_hostname_lookup(dns_sdblookup_t * lookup)357 do_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
374 static isc_result_t
do_authors_lookup(dns_sdblookup_t * lookup)375 do_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
404 static isc_result_t
do_id_lookup(dns_sdblookup_t * lookup)405 do_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
422 static isc_result_t
do_dns64_lookup(dns_sdblookup_t * lookup)423 do_dns64_lookup(dns_sdblookup_t *lookup) {
424 UNUSED(lookup);
425 return (ISC_R_SUCCESS);
426 }
427
428 static isc_result_t
do_empty_lookup(dns_sdblookup_t * lookup)429 do_empty_lookup(dns_sdblookup_t *lookup) {
430 UNUSED(lookup);
431 return (ISC_R_SUCCESS);
432 }
433
434 static isc_result_t
builtin_authority(const char * zone,void * dbdata,dns_sdblookup_t * lookup)435 builtin_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
469 static isc_result_t
builtin_create(const char * zone,int argc,char ** argv,void * driverdata,void ** dbdata)470 builtin_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
539 static void
builtin_destroy(const char * zone,void * driverdata,void ** dbdata)540 builtin_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
561 static dns_sdbmethods_t builtin_methods = {
562 builtin_lookup, builtin_authority, NULL, /* allnodes */
563 builtin_create, builtin_destroy, NULL
564 };
565
566 static dns_sdbmethods_t dns64_methods = {
567 NULL, builtin_authority, NULL, /* allnodes */
568 builtin_create, builtin_destroy, dns64_lookup,
569 };
570
571 isc_result_t
named_builtin_init(void)572 named_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
587 void
named_builtin_deinit(void)588 named_builtin_deinit(void) {
589 dns_sdb_unregister(&builtin_impl);
590 dns_sdb_unregister(&dns64_impl);
591 }
592