builtin.c revision 1.3
1/* $NetBSD: builtin.c,v 1.3 2020/05/24 19:46:12 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/*! \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 35typedef struct builtin builtin_t; 36 37static isc_result_t 38do_version_lookup(dns_sdblookup_t *lookup); 39static isc_result_t 40do_hostname_lookup(dns_sdblookup_t *lookup); 41static isc_result_t 42do_authors_lookup(dns_sdblookup_t *lookup); 43static isc_result_t 44do_id_lookup(dns_sdblookup_t *lookup); 45static isc_result_t 46do_empty_lookup(dns_sdblookup_t *lookup); 47static isc_result_t 48do_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 56struct builtin { 57 isc_result_t (*do_lookup)(dns_sdblookup_t *lookup); 58 char *server; 59 char *contact; 60}; 61 62static builtin_t version_builtin = { do_version_lookup, NULL, NULL }; 63static builtin_t hostname_builtin = { do_hostname_lookup, NULL, NULL }; 64static builtin_t authors_builtin = { do_authors_lookup, NULL, NULL }; 65static builtin_t id_builtin = { do_id_lookup, NULL, NULL }; 66static builtin_t empty_builtin = { do_empty_lookup, NULL, NULL }; 67static builtin_t dns64_builtin = { do_dns64_lookup, NULL, NULL }; 68 69static dns_sdbimplementation_t *builtin_impl; 70static dns_sdbimplementation_t *dns64_impl; 71 72/* 73 * Pre computed HEX * 16 or 1 table. 74 */ 75static 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 94const unsigned char decimal[] = "0123456789"; 95 96static size_t 97dns64_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 126static isc_result_t 127dns64_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 298static isc_result_t 299builtin_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 315static isc_result_t 316dns64_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 331static isc_result_t 332put_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 343static isc_result_t 344do_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 356static isc_result_t 357do_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 374static isc_result_t 375do_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 404static isc_result_t 405do_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 422static isc_result_t 423do_dns64_lookup(dns_sdblookup_t *lookup) { 424 UNUSED(lookup); 425 return (ISC_R_SUCCESS); 426} 427 428static isc_result_t 429do_empty_lookup(dns_sdblookup_t *lookup) { 430 UNUSED(lookup); 431 return (ISC_R_SUCCESS); 432} 433 434static isc_result_t 435builtin_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 469static isc_result_t 470builtin_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 539static void 540builtin_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 561static dns_sdbmethods_t builtin_methods = { 562 builtin_lookup, builtin_authority, NULL, /* allnodes */ 563 builtin_create, builtin_destroy, NULL 564}; 565 566static dns_sdbmethods_t dns64_methods = { 567 NULL, builtin_authority, NULL, /* allnodes */ 568 builtin_create, builtin_destroy, dns64_lookup, 569}; 570 571isc_result_t 572named_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 587void 588named_builtin_deinit(void) { 589 dns_sdb_unregister(&builtin_impl); 590 dns_sdb_unregister(&dns64_impl); 591} 592