Home | History | Annotate | Line # | Download | only in dns
dlz.c revision 1.1.4.2
      1 /*	$NetBSD: dlz.c,v 1.1.4.2 2024/02/29 11:38:38 martin 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 (at) 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 
     80 static ISC_LIST(dns_dlzimplementation_t) dlz_implementations;
     81 static isc_rwlock_t dlz_implock;
     82 static isc_once_t once = ISC_ONCE_INIT;
     83 
     84 static void
     85 dlz_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  */
     93 static dns_dlzimplementation_t *
     94 dlz_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 
    111 isc_result_t
    112 dns_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 
    158 isc_result_t
    159 dns_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 
    240 void
    241 dns_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  */
    275 isc_result_t
    276 dns_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  */
    360 isc_result_t
    361 dns_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  */
    369 void
    370 dns_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 master 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  */
    415 isc_result_t
    416 dns_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 		result = dns_ssutable_createdlz(dlzdb->mctx, &dlzdb->ssutable,
    473 						dlzdb);
    474 		if (result != ISC_R_SUCCESS) {
    475 			goto cleanup;
    476 		}
    477 	}
    478 	dns_zone_setssutable(zone, dlzdb->ssutable);
    479 
    480 	result = dlzdb->configure_callback(view, dlzdb, zone);
    481 	if (result != ISC_R_SUCCESS) {
    482 		goto cleanup;
    483 	}
    484 
    485 	result = dns_view_addzone(view, zone);
    486 
    487 cleanup:
    488 	if (zone != NULL) {
    489 		dns_zone_detach(&zone);
    490 	}
    491 
    492 	return (result);
    493 }
    494 
    495 /*%
    496  * Configure a DLZ driver. This is optional, and if supplied gives
    497  * the backend an opportunity to configure parameters related to DLZ.
    498  */
    499 isc_result_t
    500 dns_dlzconfigure(dns_view_t *view, dns_dlzdb_t *dlzdb,
    501 		 dlzconfigure_callback_t callback) {
    502 	dns_dlzimplementation_t *impl;
    503 	isc_result_t result;
    504 
    505 	REQUIRE(DNS_DLZ_VALID(dlzdb));
    506 	REQUIRE(dlzdb->implementation != NULL);
    507 
    508 	impl = dlzdb->implementation;
    509 
    510 	if (impl->methods->configure == NULL) {
    511 		return (ISC_R_SUCCESS);
    512 	}
    513 
    514 	dlzdb->configure_callback = callback;
    515 
    516 	result = impl->methods->configure(impl->driverarg, dlzdb->dbdata, view,
    517 					  dlzdb);
    518 	return (result);
    519 }
    520 
    521 bool
    522 dns_dlz_ssumatch(dns_dlzdb_t *dlzdatabase, const dns_name_t *signer,
    523 		 const dns_name_t *name, const isc_netaddr_t *tcpaddr,
    524 		 dns_rdatatype_t type, const dst_key_t *key) {
    525 	dns_dlzimplementation_t *impl;
    526 	bool r;
    527 
    528 	REQUIRE(dlzdatabase != NULL);
    529 	REQUIRE(dlzdatabase->implementation != NULL);
    530 	REQUIRE(dlzdatabase->implementation->methods != NULL);
    531 	impl = dlzdatabase->implementation;
    532 
    533 	if (impl->methods->ssumatch == NULL) {
    534 		isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
    535 			      DNS_LOGMODULE_DLZ, ISC_LOG_INFO,
    536 			      "No ssumatch method for DLZ database");
    537 		return (false);
    538 	}
    539 
    540 	r = impl->methods->ssumatch(signer, name, tcpaddr, type, key,
    541 				    impl->driverarg, dlzdatabase->dbdata);
    542 	return (r);
    543 }
    544