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