mirror of
https://gitlab.isc.org/isc-projects/bind9
synced 2025-09-07 18:15:34 +00:00
Using isc_mem_put(mctx, ...) + isc_mem_detach(mctx) required juggling with the local variables when mctx was part of the freed object. The isc_mem_putanddetach function can handle this case internally, but it wasn't used everywhere. This commit apply the semantic patching plus bit of manual work to replace all such occurrences with proper usage of isc_mem_putanddetach().
543 lines
15 KiB
C
543 lines
15 KiB
C
/*
|
|
* Portions Copyright (C) Internet Systems Consortium, Inc. ("ISC")
|
|
*
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
*
|
|
* See the COPYRIGHT file distributed with this work for additional
|
|
* information regarding copyright ownership.
|
|
*/
|
|
|
|
/*
|
|
* Copyright (C) 2002 Stichting NLnet, Netherlands, stichting@nlnet.nl.
|
|
*
|
|
* Permission to use, copy, modify, and distribute this software for any
|
|
* purpose with or without fee is hereby granted, provided that the
|
|
* above copyright notice and this permission notice appear in all
|
|
* copies.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS" AND STICHTING NLNET
|
|
* DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
|
|
* STICHTING NLNET BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR
|
|
* CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
|
|
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
|
|
* OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE
|
|
* USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
*
|
|
* The development of Dynamically Loadable Zones (DLZ) for Bind 9 was
|
|
* conceived and contributed by Rob Butler.
|
|
*
|
|
* Permission to use, copy, modify, and distribute this software for any
|
|
* purpose with or without fee is hereby granted, provided that the
|
|
* above copyright notice and this permission notice appear in all
|
|
* copies.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS" AND ROB BUTLER
|
|
* DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
|
|
* ROB BUTLER BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR
|
|
* CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
|
|
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
|
|
* OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE
|
|
* USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
*/
|
|
|
|
|
|
/*! \file */
|
|
|
|
/***
|
|
*** Imports
|
|
***/
|
|
|
|
#include <stdbool.h>
|
|
|
|
#include <dns/db.h>
|
|
#include <dns/dlz.h>
|
|
#include <dns/fixedname.h>
|
|
#include <dns/log.h>
|
|
#include <dns/master.h>
|
|
#include <dns/ssu.h>
|
|
#include <dns/zone.h>
|
|
|
|
|
|
#include <isc/buffer.h>
|
|
#include <isc/commandline.h>
|
|
#include <isc/magic.h>
|
|
#include <isc/mem.h>
|
|
#include <isc/once.h>
|
|
#include <isc/rwlock.h>
|
|
#include <isc/string.h>
|
|
#include <isc/util.h>
|
|
|
|
/***
|
|
*** Supported DLZ DB Implementations Registry
|
|
***/
|
|
|
|
static ISC_LIST(dns_dlzimplementation_t) dlz_implementations;
|
|
static isc_rwlock_t dlz_implock;
|
|
static isc_once_t once = ISC_ONCE_INIT;
|
|
|
|
static void
|
|
dlz_initialize(void) {
|
|
RUNTIME_CHECK(isc_rwlock_init(&dlz_implock, 0, 0) == ISC_R_SUCCESS);
|
|
ISC_LIST_INIT(dlz_implementations);
|
|
}
|
|
|
|
/*%
|
|
* Searches the dlz_implementations list for a driver matching name.
|
|
*/
|
|
static inline dns_dlzimplementation_t *
|
|
dlz_impfind(const char *name) {
|
|
dns_dlzimplementation_t *imp;
|
|
|
|
for (imp = ISC_LIST_HEAD(dlz_implementations);
|
|
imp != NULL;
|
|
imp = ISC_LIST_NEXT(imp, link))
|
|
if (strcasecmp(name, imp->name) == 0)
|
|
return (imp);
|
|
return (NULL);
|
|
}
|
|
|
|
/***
|
|
*** Basic DLZ Methods
|
|
***/
|
|
|
|
isc_result_t
|
|
dns_dlzallowzonexfr(dns_view_t *view, const dns_name_t *name,
|
|
const isc_sockaddr_t *clientaddr, dns_db_t **dbp)
|
|
{
|
|
isc_result_t result = ISC_R_NOTFOUND;
|
|
dns_dlzallowzonexfr_t allowzonexfr;
|
|
dns_dlzdb_t *dlzdb;
|
|
|
|
/*
|
|
* Performs checks to make sure data is as we expect it to be.
|
|
*/
|
|
REQUIRE(name != NULL);
|
|
REQUIRE(dbp != NULL && *dbp == NULL);
|
|
|
|
/*
|
|
* Find a driver in which the zone exists and transfer is supported
|
|
*/
|
|
for (dlzdb = ISC_LIST_HEAD(view->dlz_searched);
|
|
dlzdb != NULL;
|
|
dlzdb = ISC_LIST_NEXT(dlzdb, link))
|
|
{
|
|
REQUIRE(DNS_DLZ_VALID(dlzdb));
|
|
|
|
allowzonexfr = dlzdb->implementation->methods->allowzonexfr;
|
|
result = (*allowzonexfr)(dlzdb->implementation->driverarg,
|
|
dlzdb->dbdata, dlzdb->mctx,
|
|
view->rdclass, name, clientaddr, dbp);
|
|
|
|
/*
|
|
* In these cases, we found the right database. Non-success
|
|
* result codes indicate the zone might not transfer.
|
|
*/
|
|
switch (result) {
|
|
case ISC_R_SUCCESS:
|
|
case ISC_R_NOPERM:
|
|
case ISC_R_DEFAULT:
|
|
return (result);
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (result == ISC_R_NOTIMPLEMENTED)
|
|
result = ISC_R_NOTFOUND;
|
|
|
|
return (result);
|
|
}
|
|
|
|
isc_result_t
|
|
dns_dlzcreate(isc_mem_t *mctx, const char *dlzname, const char *drivername,
|
|
unsigned int argc, char *argv[], dns_dlzdb_t **dbp)
|
|
{
|
|
dns_dlzimplementation_t *impinfo;
|
|
isc_result_t result;
|
|
dns_dlzdb_t *db = NULL;
|
|
|
|
/*
|
|
* initialize the dlz_implementations list, this is guaranteed
|
|
* to only really happen once.
|
|
*/
|
|
RUNTIME_CHECK(isc_once_do(&once, dlz_initialize) == ISC_R_SUCCESS);
|
|
|
|
/*
|
|
* Performs checks to make sure data is as we expect it to be.
|
|
*/
|
|
REQUIRE(dbp != NULL && *dbp == NULL);
|
|
REQUIRE(dlzname != NULL);
|
|
REQUIRE(drivername != NULL);
|
|
REQUIRE(mctx != NULL);
|
|
|
|
/* write log message */
|
|
isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
|
|
DNS_LOGMODULE_DLZ, ISC_LOG_INFO,
|
|
"Loading '%s' using driver %s", dlzname, drivername);
|
|
|
|
/* lock the dlz_implementations list so we can search it. */
|
|
RWLOCK(&dlz_implock, isc_rwlocktype_read);
|
|
|
|
/* search for the driver implementation */
|
|
impinfo = dlz_impfind(drivername);
|
|
if (impinfo == NULL) {
|
|
RWUNLOCK(&dlz_implock, isc_rwlocktype_read);
|
|
|
|
isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
|
|
DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
|
|
"unsupported DLZ database driver '%s'."
|
|
" %s not loaded.",
|
|
drivername, dlzname);
|
|
|
|
return (ISC_R_NOTFOUND);
|
|
}
|
|
|
|
/* Allocate memory to hold the DLZ database driver */
|
|
db = isc_mem_get(mctx, sizeof(dns_dlzdb_t));
|
|
|
|
/* Make sure memory region is set to all 0's */
|
|
memset(db, 0, sizeof(dns_dlzdb_t));
|
|
|
|
ISC_LINK_INIT(db, link);
|
|
db->implementation = impinfo;
|
|
if (dlzname != NULL)
|
|
db->dlzname = isc_mem_strdup(mctx, dlzname);
|
|
|
|
/* Create a new database using implementation 'drivername'. */
|
|
result = ((impinfo->methods->create)(mctx, dlzname, argc, argv,
|
|
impinfo->driverarg,
|
|
&db->dbdata));
|
|
|
|
/* mark the DLZ driver as valid */
|
|
if (result == ISC_R_SUCCESS) {
|
|
RWUNLOCK(&dlz_implock, isc_rwlocktype_read);
|
|
db->magic = DNS_DLZ_MAGIC;
|
|
isc_mem_attach(mctx, &db->mctx);
|
|
isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
|
|
DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2),
|
|
"DLZ driver loaded successfully.");
|
|
*dbp = db;
|
|
return (ISC_R_SUCCESS);
|
|
} else {
|
|
isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
|
|
DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
|
|
"DLZ driver failed to load.");
|
|
}
|
|
|
|
/* impinfo->methods->create failed. */
|
|
RWUNLOCK(&dlz_implock, isc_rwlocktype_read);
|
|
isc_mem_put(mctx, db, sizeof(dns_dlzdb_t));
|
|
return (result);
|
|
}
|
|
|
|
void
|
|
dns_dlzdestroy(dns_dlzdb_t **dbp) {
|
|
dns_dlzdestroy_t destroy;
|
|
dns_dlzdb_t *db;
|
|
|
|
/* Write debugging message to log */
|
|
isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
|
|
DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2),
|
|
"Unloading DLZ driver.");
|
|
|
|
/*
|
|
* Perform checks to make sure data is as we expect it to be.
|
|
*/
|
|
REQUIRE(dbp != NULL && DNS_DLZ_VALID(*dbp));
|
|
|
|
db = *dbp;
|
|
*dbp = NULL;
|
|
|
|
if (db->ssutable != NULL)
|
|
dns_ssutable_detach(&db->ssutable);
|
|
|
|
/* call the drivers destroy method */
|
|
if (db->dlzname != NULL)
|
|
isc_mem_free(db->mctx, db->dlzname);
|
|
destroy = db->implementation->methods->destroy;
|
|
(*destroy)(db->implementation->driverarg, db->dbdata);
|
|
/* return memory and detach */
|
|
isc_mem_putanddetach(&db->mctx, db, sizeof(dns_dlzdb_t));
|
|
}
|
|
|
|
/*%
|
|
* Registers a DLZ driver. This basically just adds the dlz
|
|
* driver to the list of available drivers in the dlz_implementations list.
|
|
*/
|
|
isc_result_t
|
|
dns_dlzregister(const char *drivername, const dns_dlzmethods_t *methods,
|
|
void *driverarg, isc_mem_t *mctx,
|
|
dns_dlzimplementation_t **dlzimp)
|
|
{
|
|
|
|
dns_dlzimplementation_t *dlz_imp;
|
|
|
|
/* Write debugging message to log */
|
|
isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
|
|
DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2),
|
|
"Registering DLZ driver '%s'", drivername);
|
|
|
|
/*
|
|
* Performs checks to make sure data is as we expect it to be.
|
|
*/
|
|
REQUIRE(drivername != NULL);
|
|
REQUIRE(methods != NULL);
|
|
REQUIRE(methods->create != NULL);
|
|
REQUIRE(methods->destroy != NULL);
|
|
REQUIRE(methods->findzone != NULL);
|
|
REQUIRE(mctx != NULL);
|
|
REQUIRE(dlzimp != NULL && *dlzimp == NULL);
|
|
|
|
/*
|
|
* initialize the dlz_implementations list, this is guaranteed
|
|
* to only really happen once.
|
|
*/
|
|
RUNTIME_CHECK(isc_once_do(&once, dlz_initialize) == ISC_R_SUCCESS);
|
|
|
|
/* lock the dlz_implementations list so we can modify it. */
|
|
RWLOCK(&dlz_implock, isc_rwlocktype_write);
|
|
|
|
/*
|
|
* check that another already registered driver isn't using
|
|
* the same name
|
|
*/
|
|
dlz_imp = dlz_impfind(drivername);
|
|
if (dlz_imp != NULL) {
|
|
isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
|
|
DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2),
|
|
"DLZ Driver '%s' already registered",
|
|
drivername);
|
|
RWUNLOCK(&dlz_implock, isc_rwlocktype_write);
|
|
return (ISC_R_EXISTS);
|
|
}
|
|
|
|
/*
|
|
* Allocate memory for a dlz_implementation object. Error if
|
|
* we cannot.
|
|
*/
|
|
dlz_imp = isc_mem_get(mctx, sizeof(dns_dlzimplementation_t));
|
|
|
|
/* Make sure memory region is set to all 0's */
|
|
memset(dlz_imp, 0, sizeof(dns_dlzimplementation_t));
|
|
|
|
/* Store the data passed into this method */
|
|
dlz_imp->name = drivername;
|
|
dlz_imp->methods = methods;
|
|
dlz_imp->mctx = NULL;
|
|
dlz_imp->driverarg = driverarg;
|
|
|
|
/* attach the new dlz_implementation object to a memory context */
|
|
isc_mem_attach(mctx, &dlz_imp->mctx);
|
|
|
|
/*
|
|
* prepare the dlz_implementation object to be put in a list,
|
|
* and append it to the list
|
|
*/
|
|
ISC_LINK_INIT(dlz_imp, link);
|
|
ISC_LIST_APPEND(dlz_implementations, dlz_imp, link);
|
|
|
|
/* Unlock the dlz_implementations list. */
|
|
RWUNLOCK(&dlz_implock, isc_rwlocktype_write);
|
|
|
|
/* Pass back the dlz_implementation that we created. */
|
|
*dlzimp = dlz_imp;
|
|
|
|
return (ISC_R_SUCCESS);
|
|
}
|
|
|
|
/*%
|
|
* Tokenize the string "s" into whitespace-separated words,
|
|
* return the number of words in '*argcp' and an array
|
|
* of pointers to the words in '*argvp'. The caller
|
|
* must free the array using isc_mem_put(). The string
|
|
* is modified in-place.
|
|
*/
|
|
isc_result_t
|
|
dns_dlzstrtoargv(isc_mem_t *mctx, char *s,
|
|
unsigned int *argcp, char ***argvp)
|
|
{
|
|
return(isc_commandline_strtoargv(mctx, s, argcp, argvp, 0));
|
|
}
|
|
|
|
/*%
|
|
* Unregisters a DLZ driver. This basically just removes the dlz
|
|
* driver from the list of available drivers in the dlz_implementations list.
|
|
*/
|
|
void
|
|
dns_dlzunregister(dns_dlzimplementation_t **dlzimp) {
|
|
dns_dlzimplementation_t *dlz_imp;
|
|
|
|
/* Write debugging message to log */
|
|
isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
|
|
DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2),
|
|
"Unregistering DLZ driver.");
|
|
|
|
/*
|
|
* Performs checks to make sure data is as we expect it to be.
|
|
*/
|
|
REQUIRE(dlzimp != NULL && *dlzimp != NULL);
|
|
|
|
/*
|
|
* initialize the dlz_implementations list, this is guaranteed
|
|
* to only really happen once.
|
|
*/
|
|
RUNTIME_CHECK(isc_once_do(&once, dlz_initialize) == ISC_R_SUCCESS);
|
|
|
|
dlz_imp = *dlzimp;
|
|
|
|
/* lock the dlz_implementations list so we can modify it. */
|
|
RWLOCK(&dlz_implock, isc_rwlocktype_write);
|
|
|
|
/* remove the dlz_implementation object from the list */
|
|
ISC_LIST_UNLINK(dlz_implementations, dlz_imp, link);
|
|
|
|
/*
|
|
* Return the memory back to the available memory pool and
|
|
* remove it from the memory context.
|
|
*/
|
|
isc_mem_putanddetach(&dlz_imp->mctx, dlz_imp, sizeof(*dlz_imp));
|
|
|
|
/* Unlock the dlz_implementations list. */
|
|
RWUNLOCK(&dlz_implock, isc_rwlocktype_write);
|
|
}
|
|
|
|
/*
|
|
* Create a writeable DLZ zone. This can be called by DLZ drivers
|
|
* during configure() to create a zone that can be updated. The zone
|
|
* type is set to dns_zone_dlz, which is equivalent to a master zone
|
|
*
|
|
* This function uses a callback setup in dns_dlzconfigure() to call
|
|
* into the server zone code to setup the remaining pieces of server
|
|
* specific functionality on the zone
|
|
*/
|
|
isc_result_t
|
|
dns_dlz_writeablezone(dns_view_t *view, dns_dlzdb_t *dlzdb,
|
|
const char *zone_name)
|
|
{
|
|
dns_zone_t *zone = NULL;
|
|
dns_zone_t *dupzone = NULL;
|
|
isc_result_t result;
|
|
isc_buffer_t buffer;
|
|
dns_fixedname_t fixorigin;
|
|
dns_name_t *origin;
|
|
|
|
REQUIRE(DNS_DLZ_VALID(dlzdb));
|
|
|
|
REQUIRE(dlzdb->configure_callback != NULL);
|
|
|
|
isc_buffer_constinit(&buffer, zone_name, strlen(zone_name));
|
|
isc_buffer_add(&buffer, strlen(zone_name));
|
|
dns_fixedname_init(&fixorigin);
|
|
result = dns_name_fromtext(dns_fixedname_name(&fixorigin),
|
|
&buffer, dns_rootname, 0, NULL);
|
|
if (result != ISC_R_SUCCESS)
|
|
goto cleanup;
|
|
origin = dns_fixedname_name(&fixorigin);
|
|
|
|
if (!dlzdb->search) {
|
|
isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
|
|
DNS_LOGMODULE_DLZ, ISC_LOG_WARNING,
|
|
"DLZ %s has 'search no;', but attempted to "
|
|
"register writeable zone %s.",
|
|
dlzdb->dlzname, zone_name);
|
|
result = ISC_R_SUCCESS;
|
|
goto cleanup;
|
|
}
|
|
|
|
/* See if the zone already exists */
|
|
result = dns_view_findzone(view, origin, &dupzone);
|
|
if (result == ISC_R_SUCCESS) {
|
|
dns_zone_detach(&dupzone);
|
|
result = ISC_R_EXISTS;
|
|
goto cleanup;
|
|
}
|
|
INSIST(dupzone == NULL);
|
|
|
|
/* Create it */
|
|
result = dns_zone_create(&zone, view->mctx);
|
|
if (result != ISC_R_SUCCESS)
|
|
goto cleanup;
|
|
result = dns_zone_setorigin(zone, origin);
|
|
if (result != ISC_R_SUCCESS)
|
|
goto cleanup;
|
|
dns_zone_setview(zone, view);
|
|
|
|
dns_zone_setadded(zone, true);
|
|
|
|
if (dlzdb->ssutable == NULL) {
|
|
result = dns_ssutable_createdlz(dlzdb->mctx,
|
|
&dlzdb->ssutable, dlzdb);
|
|
if (result != ISC_R_SUCCESS)
|
|
goto cleanup;
|
|
}
|
|
dns_zone_setssutable(zone, dlzdb->ssutable);
|
|
|
|
result = dlzdb->configure_callback(view, dlzdb, zone);
|
|
if (result != ISC_R_SUCCESS)
|
|
goto cleanup;
|
|
|
|
result = dns_view_addzone(view, zone);
|
|
|
|
|
|
cleanup:
|
|
if (zone != NULL)
|
|
dns_zone_detach(&zone);
|
|
|
|
return (result);
|
|
}
|
|
|
|
/*%
|
|
* Configure a DLZ driver. This is optional, and if supplied gives
|
|
* the backend an opportunity to configure parameters related to DLZ.
|
|
*/
|
|
isc_result_t
|
|
dns_dlzconfigure(dns_view_t *view, dns_dlzdb_t *dlzdb,
|
|
dlzconfigure_callback_t callback)
|
|
{
|
|
dns_dlzimplementation_t *impl;
|
|
isc_result_t result;
|
|
|
|
REQUIRE(DNS_DLZ_VALID(dlzdb));
|
|
REQUIRE(dlzdb->implementation != NULL);
|
|
|
|
impl = dlzdb->implementation;
|
|
|
|
if (impl->methods->configure == NULL)
|
|
return (ISC_R_SUCCESS);
|
|
|
|
dlzdb->configure_callback = callback;
|
|
|
|
result = impl->methods->configure(impl->driverarg, dlzdb->dbdata,
|
|
view, dlzdb);
|
|
return (result);
|
|
}
|
|
|
|
bool
|
|
dns_dlz_ssumatch(dns_dlzdb_t *dlzdatabase, const dns_name_t *signer,
|
|
const dns_name_t *name, const isc_netaddr_t *tcpaddr,
|
|
dns_rdatatype_t type, const dst_key_t *key)
|
|
{
|
|
dns_dlzimplementation_t *impl;
|
|
bool r;
|
|
|
|
REQUIRE(dlzdatabase != NULL);
|
|
REQUIRE(dlzdatabase->implementation != NULL);
|
|
REQUIRE(dlzdatabase->implementation->methods != NULL);
|
|
impl = dlzdatabase->implementation;
|
|
|
|
if (impl->methods->ssumatch == NULL) {
|
|
isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
|
|
DNS_LOGMODULE_DLZ, ISC_LOG_INFO,
|
|
"No ssumatch method for DLZ database");
|
|
return (false);
|
|
}
|
|
|
|
r = impl->methods->ssumatch(signer, name, tcpaddr, type, key,
|
|
impl->driverarg, dlzdatabase->dbdata);
|
|
return (r);
|
|
}
|