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