mirror of
https://gitlab.isc.org/isc-projects/bind9
synced 2025-08-22 10:10:06 +00:00
After switching to per-thread resources in the zonemgr, the performance was decreased because the memory context, zonetask and loadtask was picked from the pool at random. Pin the zone to single threadid (.tid) and align the memory context, zonetask and loadtask to be the same, this sets the hard affinity of the zone to the netmgr thread.
540 lines
15 KiB
C
540 lines
15 KiB
C
/*
|
|
* Copyright (C) Internet Systems Consortium, Inc. ("ISC")
|
|
*
|
|
* SPDX-License-Identifier: MPL-2.0 AND 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 https://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 <isc/buffer.h>
|
|
#include <isc/commandline.h>
|
|
#include <isc/magic.h>
|
|
#include <isc/mem.h>
|
|
#include <isc/netmgr.h>
|
|
#include <isc/once.h>
|
|
#include <isc/random.h>
|
|
#include <isc/rwlock.h>
|
|
#include <isc/string.h>
|
|
#include <isc/util.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>
|
|
|
|
/***
|
|
*** 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) {
|
|
isc_rwlock_init(&dlz_implock, 0, 0);
|
|
ISC_LIST_INIT(dlz_implementations);
|
|
}
|
|
|
|
/*%
|
|
* Searches the dlz_implementations list for a driver matching name.
|
|
*/
|
|
static 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_free(mctx, db->dlzname);
|
|
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 primary 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, 0);
|
|
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) {
|
|
dns_ssutable_createdlz(dlzdb->mctx, &dlzdb->ssutable, dlzdb);
|
|
}
|
|
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);
|
|
}
|