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