1/* $NetBSD: dlz.c,v 1.8 2024/02/21 22:52:06 christos Exp $ */ 2 3/* 4 * Copyright (C) Internet Systems Consortium, Inc. ("ISC") 5 * 6 * SPDX-License-Identifier: MPL-2.0 AND ISC 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/* 17 * Copyright (C) 2002 Stichting NLnet, Netherlands, stichting@nlnet.nl. 18 * 19 * Permission to use, copy, modify, and distribute this software for any 20 * purpose with or without fee is hereby granted, provided that the 21 * above copyright notice and this permission notice appear in all 22 * copies. 23 * 24 * THE SOFTWARE IS PROVIDED "AS IS" AND STICHTING NLNET 25 * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL 26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL 27 * STICHTING NLNET BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR 28 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS 29 * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 30 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE 31 * USE OR PERFORMANCE OF THIS SOFTWARE. 32 * 33 * The development of Dynamically Loadable Zones (DLZ) for Bind 9 was 34 * conceived and contributed by Rob Butler. 35 * 36 * Permission to use, copy, modify, and distribute this software for any 37 * purpose with or without fee is hereby granted, provided that the 38 * above copyright notice and this permission notice appear in all 39 * copies. 40 * 41 * THE SOFTWARE IS PROVIDED "AS IS" AND ROB BUTLER 42 * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL 43 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL 44 * ROB BUTLER BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR 45 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS 46 * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 47 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE 48 * USE OR PERFORMANCE OF THIS SOFTWARE. 49 */ 50 51/*! \file */ 52 53/*** 54 *** Imports 55 ***/ 56 57#include <stdbool.h> 58 59#include <isc/buffer.h> 60#include <isc/commandline.h> 61#include <isc/magic.h> 62#include <isc/mem.h> 63#include <isc/once.h> 64#include <isc/rwlock.h> 65#include <isc/string.h> 66#include <isc/util.h> 67 68#include <dns/db.h> 69#include <dns/dlz.h> 70#include <dns/fixedname.h> 71#include <dns/log.h> 72#include <dns/master.h> 73#include <dns/ssu.h> 74#include <dns/zone.h> 75 76/*** 77 *** Supported DLZ DB Implementations Registry 78 ***/ 79 80static ISC_LIST(dns_dlzimplementation_t) dlz_implementations; 81static isc_rwlock_t dlz_implock; 82static isc_once_t once = ISC_ONCE_INIT; 83 84static void 85dlz_initialize(void) { 86 isc_rwlock_init(&dlz_implock, 0, 0); 87 ISC_LIST_INIT(dlz_implementations); 88} 89 90/*% 91 * Searches the dlz_implementations list for a driver matching name. 92 */ 93static dns_dlzimplementation_t * 94dlz_impfind(const char *name) { 95 dns_dlzimplementation_t *imp; 96 97 for (imp = ISC_LIST_HEAD(dlz_implementations); imp != NULL; 98 imp = ISC_LIST_NEXT(imp, link)) 99 { 100 if (strcasecmp(name, imp->name) == 0) { 101 return (imp); 102 } 103 } 104 return (NULL); 105} 106 107/*** 108 *** Basic DLZ Methods 109 ***/ 110 111isc_result_t 112dns_dlzallowzonexfr(dns_view_t *view, const dns_name_t *name, 113 const isc_sockaddr_t *clientaddr, dns_db_t **dbp) { 114 isc_result_t result = ISC_R_NOTFOUND; 115 dns_dlzallowzonexfr_t allowzonexfr; 116 dns_dlzdb_t *dlzdb; 117 118 /* 119 * Performs checks to make sure data is as we expect it to be. 120 */ 121 REQUIRE(name != NULL); 122 REQUIRE(dbp != NULL && *dbp == NULL); 123 124 /* 125 * Find a driver in which the zone exists and transfer is supported 126 */ 127 for (dlzdb = ISC_LIST_HEAD(view->dlz_searched); dlzdb != NULL; 128 dlzdb = ISC_LIST_NEXT(dlzdb, link)) 129 { 130 REQUIRE(DNS_DLZ_VALID(dlzdb)); 131 132 allowzonexfr = dlzdb->implementation->methods->allowzonexfr; 133 result = (*allowzonexfr)(dlzdb->implementation->driverarg, 134 dlzdb->dbdata, dlzdb->mctx, 135 view->rdclass, name, clientaddr, dbp); 136 137 /* 138 * In these cases, we found the right database. Non-success 139 * result codes indicate the zone might not transfer. 140 */ 141 switch (result) { 142 case ISC_R_SUCCESS: 143 case ISC_R_NOPERM: 144 case ISC_R_DEFAULT: 145 return (result); 146 default: 147 break; 148 } 149 } 150 151 if (result == ISC_R_NOTIMPLEMENTED) { 152 result = ISC_R_NOTFOUND; 153 } 154 155 return (result); 156} 157 158isc_result_t 159dns_dlzcreate(isc_mem_t *mctx, const char *dlzname, const char *drivername, 160 unsigned int argc, char *argv[], dns_dlzdb_t **dbp) { 161 dns_dlzimplementation_t *impinfo; 162 isc_result_t result; 163 dns_dlzdb_t *db = NULL; 164 165 /* 166 * initialize the dlz_implementations list, this is guaranteed 167 * to only really happen once. 168 */ 169 RUNTIME_CHECK(isc_once_do(&once, dlz_initialize) == ISC_R_SUCCESS); 170 171 /* 172 * Performs checks to make sure data is as we expect it to be. 173 */ 174 REQUIRE(dbp != NULL && *dbp == NULL); 175 REQUIRE(dlzname != NULL); 176 REQUIRE(drivername != NULL); 177 REQUIRE(mctx != NULL); 178 179 /* write log message */ 180 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_DLZ, 181 ISC_LOG_INFO, "Loading '%s' using driver %s", dlzname, 182 drivername); 183 184 /* lock the dlz_implementations list so we can search it. */ 185 RWLOCK(&dlz_implock, isc_rwlocktype_read); 186 187 /* search for the driver implementation */ 188 impinfo = dlz_impfind(drivername); 189 if (impinfo == NULL) { 190 RWUNLOCK(&dlz_implock, isc_rwlocktype_read); 191 192 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, 193 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, 194 "unsupported DLZ database driver '%s'." 195 " %s not loaded.", 196 drivername, dlzname); 197 198 return (ISC_R_NOTFOUND); 199 } 200 201 /* Allocate memory to hold the DLZ database driver */ 202 db = isc_mem_get(mctx, sizeof(dns_dlzdb_t)); 203 204 /* Make sure memory region is set to all 0's */ 205 memset(db, 0, sizeof(dns_dlzdb_t)); 206 207 ISC_LINK_INIT(db, link); 208 db->implementation = impinfo; 209 if (dlzname != NULL) { 210 db->dlzname = isc_mem_strdup(mctx, dlzname); 211 } 212 213 /* Create a new database using implementation 'drivername'. */ 214 result = ((impinfo->methods->create)(mctx, dlzname, argc, argv, 215 impinfo->driverarg, &db->dbdata)); 216 217 /* mark the DLZ driver as valid */ 218 if (result == ISC_R_SUCCESS) { 219 RWUNLOCK(&dlz_implock, isc_rwlocktype_read); 220 db->magic = DNS_DLZ_MAGIC; 221 isc_mem_attach(mctx, &db->mctx); 222 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, 223 DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2), 224 "DLZ driver loaded successfully."); 225 *dbp = db; 226 return (ISC_R_SUCCESS); 227 } else { 228 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, 229 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, 230 "DLZ driver failed to load."); 231 } 232 233 /* impinfo->methods->create failed. */ 234 RWUNLOCK(&dlz_implock, isc_rwlocktype_read); 235 isc_mem_free(mctx, db->dlzname); 236 isc_mem_put(mctx, db, sizeof(dns_dlzdb_t)); 237 return (result); 238} 239 240void 241dns_dlzdestroy(dns_dlzdb_t **dbp) { 242 dns_dlzdestroy_t destroy; 243 dns_dlzdb_t *db; 244 245 /* Write debugging message to log */ 246 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_DLZ, 247 ISC_LOG_DEBUG(2), "Unloading DLZ driver."); 248 249 /* 250 * Perform checks to make sure data is as we expect it to be. 251 */ 252 REQUIRE(dbp != NULL && DNS_DLZ_VALID(*dbp)); 253 254 db = *dbp; 255 *dbp = NULL; 256 257 if (db->ssutable != NULL) { 258 dns_ssutable_detach(&db->ssutable); 259 } 260 261 /* call the drivers destroy method */ 262 if (db->dlzname != NULL) { 263 isc_mem_free(db->mctx, db->dlzname); 264 } 265 destroy = db->implementation->methods->destroy; 266 (*destroy)(db->implementation->driverarg, db->dbdata); 267 /* return memory and detach */ 268 isc_mem_putanddetach(&db->mctx, db, sizeof(dns_dlzdb_t)); 269} 270 271/*% 272 * Registers a DLZ driver. This basically just adds the dlz 273 * driver to the list of available drivers in the dlz_implementations list. 274 */ 275isc_result_t 276dns_dlzregister(const char *drivername, const dns_dlzmethods_t *methods, 277 void *driverarg, isc_mem_t *mctx, 278 dns_dlzimplementation_t **dlzimp) { 279 dns_dlzimplementation_t *dlz_imp; 280 281 /* Write debugging message to log */ 282 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_DLZ, 283 ISC_LOG_DEBUG(2), "Registering DLZ driver '%s'", 284 drivername); 285 286 /* 287 * Performs checks to make sure data is as we expect it to be. 288 */ 289 REQUIRE(drivername != NULL); 290 REQUIRE(methods != NULL); 291 REQUIRE(methods->create != NULL); 292 REQUIRE(methods->destroy != NULL); 293 REQUIRE(methods->findzone != NULL); 294 REQUIRE(mctx != NULL); 295 REQUIRE(dlzimp != NULL && *dlzimp == NULL); 296 297 /* 298 * initialize the dlz_implementations list, this is guaranteed 299 * to only really happen once. 300 */ 301 RUNTIME_CHECK(isc_once_do(&once, dlz_initialize) == ISC_R_SUCCESS); 302 303 /* lock the dlz_implementations list so we can modify it. */ 304 RWLOCK(&dlz_implock, isc_rwlocktype_write); 305 306 /* 307 * check that another already registered driver isn't using 308 * the same name 309 */ 310 dlz_imp = dlz_impfind(drivername); 311 if (dlz_imp != NULL) { 312 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, 313 DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2), 314 "DLZ Driver '%s' already registered", drivername); 315 RWUNLOCK(&dlz_implock, isc_rwlocktype_write); 316 return (ISC_R_EXISTS); 317 } 318 319 /* 320 * Allocate memory for a dlz_implementation object. Error if 321 * we cannot. 322 */ 323 dlz_imp = isc_mem_get(mctx, sizeof(dns_dlzimplementation_t)); 324 325 /* Make sure memory region is set to all 0's */ 326 memset(dlz_imp, 0, sizeof(dns_dlzimplementation_t)); 327 328 /* Store the data passed into this method */ 329 dlz_imp->name = drivername; 330 dlz_imp->methods = methods; 331 dlz_imp->mctx = NULL; 332 dlz_imp->driverarg = driverarg; 333 334 /* attach the new dlz_implementation object to a memory context */ 335 isc_mem_attach(mctx, &dlz_imp->mctx); 336 337 /* 338 * prepare the dlz_implementation object to be put in a list, 339 * and append it to the list 340 */ 341 ISC_LINK_INIT(dlz_imp, link); 342 ISC_LIST_APPEND(dlz_implementations, dlz_imp, link); 343 344 /* Unlock the dlz_implementations list. */ 345 RWUNLOCK(&dlz_implock, isc_rwlocktype_write); 346 347 /* Pass back the dlz_implementation that we created. */ 348 *dlzimp = dlz_imp; 349 350 return (ISC_R_SUCCESS); 351} 352 353/*% 354 * Tokenize the string "s" into whitespace-separated words, 355 * return the number of words in '*argcp' and an array 356 * of pointers to the words in '*argvp'. The caller 357 * must free the array using isc_mem_put(). The string 358 * is modified in-place. 359 */ 360isc_result_t 361dns_dlzstrtoargv(isc_mem_t *mctx, char *s, unsigned int *argcp, char ***argvp) { 362 return (isc_commandline_strtoargv(mctx, s, argcp, argvp, 0)); 363} 364 365/*% 366 * Unregisters a DLZ driver. This basically just removes the dlz 367 * driver from the list of available drivers in the dlz_implementations list. 368 */ 369void 370dns_dlzunregister(dns_dlzimplementation_t **dlzimp) { 371 dns_dlzimplementation_t *dlz_imp; 372 373 /* Write debugging message to log */ 374 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_DLZ, 375 ISC_LOG_DEBUG(2), "Unregistering DLZ driver."); 376 377 /* 378 * Performs checks to make sure data is as we expect it to be. 379 */ 380 REQUIRE(dlzimp != NULL && *dlzimp != NULL); 381 382 /* 383 * initialize the dlz_implementations list, this is guaranteed 384 * to only really happen once. 385 */ 386 RUNTIME_CHECK(isc_once_do(&once, dlz_initialize) == ISC_R_SUCCESS); 387 388 dlz_imp = *dlzimp; 389 390 /* lock the dlz_implementations list so we can modify it. */ 391 RWLOCK(&dlz_implock, isc_rwlocktype_write); 392 393 /* remove the dlz_implementation object from the list */ 394 ISC_LIST_UNLINK(dlz_implementations, dlz_imp, link); 395 396 /* 397 * Return the memory back to the available memory pool and 398 * remove it from the memory context. 399 */ 400 isc_mem_putanddetach(&dlz_imp->mctx, dlz_imp, sizeof(*dlz_imp)); 401 402 /* Unlock the dlz_implementations list. */ 403 RWUNLOCK(&dlz_implock, isc_rwlocktype_write); 404} 405 406/* 407 * Create a writeable DLZ zone. This can be called by DLZ drivers 408 * during configure() to create a zone that can be updated. The zone 409 * type is set to dns_zone_dlz, which is equivalent to a primary zone 410 * 411 * This function uses a callback setup in dns_dlzconfigure() to call 412 * into the server zone code to setup the remaining pieces of server 413 * specific functionality on the zone 414 */ 415isc_result_t 416dns_dlz_writeablezone(dns_view_t *view, dns_dlzdb_t *dlzdb, 417 const char *zone_name) { 418 dns_zone_t *zone = NULL; 419 dns_zone_t *dupzone = NULL; 420 isc_result_t result; 421 isc_buffer_t buffer; 422 dns_fixedname_t fixorigin; 423 dns_name_t *origin; 424 425 REQUIRE(DNS_DLZ_VALID(dlzdb)); 426 427 REQUIRE(dlzdb->configure_callback != NULL); 428 429 isc_buffer_constinit(&buffer, zone_name, strlen(zone_name)); 430 isc_buffer_add(&buffer, strlen(zone_name)); 431 dns_fixedname_init(&fixorigin); 432 result = dns_name_fromtext(dns_fixedname_name(&fixorigin), &buffer, 433 dns_rootname, 0, NULL); 434 if (result != ISC_R_SUCCESS) { 435 goto cleanup; 436 } 437 origin = dns_fixedname_name(&fixorigin); 438 439 if (!dlzdb->search) { 440 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, 441 DNS_LOGMODULE_DLZ, ISC_LOG_WARNING, 442 "DLZ %s has 'search no;', but attempted to " 443 "register writeable zone %s.", 444 dlzdb->dlzname, zone_name); 445 result = ISC_R_SUCCESS; 446 goto cleanup; 447 } 448 449 /* See if the zone already exists */ 450 result = dns_view_findzone(view, origin, &dupzone); 451 if (result == ISC_R_SUCCESS) { 452 dns_zone_detach(&dupzone); 453 result = ISC_R_EXISTS; 454 goto cleanup; 455 } 456 INSIST(dupzone == NULL); 457 458 /* Create it */ 459 result = dns_zone_create(&zone, view->mctx); 460 if (result != ISC_R_SUCCESS) { 461 goto cleanup; 462 } 463 result = dns_zone_setorigin(zone, origin); 464 if (result != ISC_R_SUCCESS) { 465 goto cleanup; 466 } 467 dns_zone_setview(zone, view); 468 469 dns_zone_setadded(zone, true); 470 471 if (dlzdb->ssutable == NULL) { 472 dns_ssutable_createdlz(dlzdb->mctx, &dlzdb->ssutable, dlzdb); 473 } 474 dns_zone_setssutable(zone, dlzdb->ssutable); 475 476 result = dlzdb->configure_callback(view, dlzdb, zone); 477 if (result != ISC_R_SUCCESS) { 478 goto cleanup; 479 } 480 481 result = dns_view_addzone(view, zone); 482 483cleanup: 484 if (zone != NULL) { 485 dns_zone_detach(&zone); 486 } 487 488 return (result); 489} 490 491/*% 492 * Configure a DLZ driver. This is optional, and if supplied gives 493 * the backend an opportunity to configure parameters related to DLZ. 494 */ 495isc_result_t 496dns_dlzconfigure(dns_view_t *view, dns_dlzdb_t *dlzdb, 497 dlzconfigure_callback_t callback) { 498 dns_dlzimplementation_t *impl; 499 isc_result_t result; 500 501 REQUIRE(DNS_DLZ_VALID(dlzdb)); 502 REQUIRE(dlzdb->implementation != NULL); 503 504 impl = dlzdb->implementation; 505 506 if (impl->methods->configure == NULL) { 507 return (ISC_R_SUCCESS); 508 } 509 510 dlzdb->configure_callback = callback; 511 512 result = impl->methods->configure(impl->driverarg, dlzdb->dbdata, view, 513 dlzdb); 514 return (result); 515} 516 517bool 518dns_dlz_ssumatch(dns_dlzdb_t *dlzdatabase, const dns_name_t *signer, 519 const dns_name_t *name, const isc_netaddr_t *tcpaddr, 520 dns_rdatatype_t type, const dst_key_t *key) { 521 dns_dlzimplementation_t *impl; 522 bool r; 523 524 REQUIRE(dlzdatabase != NULL); 525 REQUIRE(dlzdatabase->implementation != NULL); 526 REQUIRE(dlzdatabase->implementation->methods != NULL); 527 impl = dlzdatabase->implementation; 528 529 if (impl->methods->ssumatch == NULL) { 530 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, 531 DNS_LOGMODULE_DLZ, ISC_LOG_INFO, 532 "No ssumatch method for DLZ database"); 533 return (false); 534 } 535 536 r = impl->methods->ssumatch(signer, name, tcpaddr, type, key, 537 impl->driverarg, dlzdatabase->dbdata); 538 return (r); 539} 540