#include <config.h>
#include <dns/fixedname.h>
#include <dns/log.h>
#include <dns/master.h>
#include <dns/dlz.h>
#include <dns/ssu.h>
#include <dns/zone.h>
#include <isc/buffer.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>
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);
}
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);
}
isc_result_t
dns_dlzallowzonexfr(dns_view_t *view, dns_name_t *name,
isc_sockaddr_t *clientaddr, dns_db_t **dbp)
{
isc_result_t result;
dns_dlzallowzonexfr_t allowzonexfr;
dns_dlzdb_t *dlzdatabase;
REQUIRE(DNS_DLZ_VALID(view->dlzdatabase));
REQUIRE(name != NULL);
REQUIRE(dbp != NULL && *dbp == NULL);
dlzdatabase = view->dlzdatabase;
allowzonexfr = dlzdatabase->implementation->methods->allowzonexfr;
result = (*allowzonexfr)(dlzdatabase->implementation->driverarg,
dlzdatabase->dbdata, dlzdatabase->mctx,
view->rdclass, name, clientaddr, dbp);
if (result == ISC_R_NOTIMPLEMENTED)
return (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;
RUNTIME_CHECK(isc_once_do(&once, dlz_initialize) == ISC_R_SUCCESS);
REQUIRE(dbp != NULL && *dbp == NULL);
REQUIRE(dlzname != NULL);
REQUIRE(drivername != NULL);
REQUIRE(mctx != NULL);
isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
DNS_LOGMODULE_DLZ, ISC_LOG_INFO,
"Loading '%s' using driver %s", dlzname, drivername);
RWLOCK(&dlz_implock, isc_rwlocktype_read);
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);
}
db = isc_mem_get(mctx, sizeof(dns_dlzdb_t));
if (db == NULL) {
RWUNLOCK(&dlz_implock, isc_rwlocktype_read);
return (ISC_R_NOMEMORY);
}
memset(db, 0, sizeof(dns_dlzdb_t));
db->implementation = impinfo;
result = ((impinfo->methods->create)(mctx, dlzname, argc, argv,
impinfo->driverarg,
&db->dbdata));
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.");
}
RWUNLOCK(&dlz_implock, isc_rwlocktype_read);
isc_mem_put(mctx, db, sizeof(dns_dlzdb_t));
return (result);
}
void
dns_dlzdestroy(dns_dlzdb_t **dbp) {
isc_mem_t *mctx;
dns_dlzdestroy_t destroy;
isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2),
"Unloading DLZ driver.");
REQUIRE(dbp != NULL && DNS_DLZ_VALID(*dbp));
#ifdef BIND9
if ((*dbp)->ssutable != NULL) {
dns_ssutable_detach(&(*dbp)->ssutable);
}
#endif
if ((*dbp) != NULL) {
mctx = (*dbp)->mctx;
destroy = (*dbp)->implementation->methods->destroy;
(*destroy)((*dbp)->implementation->driverarg,(*dbp)->dbdata);
isc_mem_put(mctx, (*dbp), sizeof(dns_dlzdb_t));
isc_mem_detach(&mctx);
}
*dbp = NULL;
}
isc_result_t
dns_dlzfindzone(dns_view_t *view, dns_name_t *name, unsigned int minlabels,
dns_db_t **dbp)
{
dns_fixedname_t fname;
dns_name_t *zonename;
unsigned int namelabels;
unsigned int i;
isc_result_t result;
dns_dlzfindzone_t findzone;
dns_dlzdb_t *dlzdatabase;
REQUIRE(DNS_DLZ_VALID(view->dlzdatabase));
REQUIRE(name != NULL);
REQUIRE(dbp != NULL && *dbp == NULL);
dns_fixedname_init(&fname);
zonename = dns_fixedname_name(&fname);
namelabels = dns_name_countlabels(name);
for (i = namelabels; i > minlabels && i > 1; i--) {
if (i == namelabels) {
result = dns_name_copy(name, zonename, NULL);
if (result != ISC_R_SUCCESS)
return (result);
} else
dns_name_split(name, i, NULL, zonename);
dlzdatabase = view->dlzdatabase;
findzone = dlzdatabase->implementation->methods->findzone;
result = (*findzone)(dlzdatabase->implementation->driverarg,
dlzdatabase->dbdata, dlzdatabase->mctx,
view->rdclass, zonename, dbp);
if (result != ISC_R_NOTFOUND)
return (result);
}
return (ISC_R_NOTFOUND);
}
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;
isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2),
"Registering DLZ driver '%s'", drivername);
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);
RUNTIME_CHECK(isc_once_do(&once, dlz_initialize) == ISC_R_SUCCESS);
RWLOCK(&dlz_implock, isc_rwlocktype_write);
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);
}
dlz_imp = isc_mem_get(mctx, sizeof(dns_dlzimplementation_t));
if (dlz_imp == NULL) {
RWUNLOCK(&dlz_implock, isc_rwlocktype_write);
return (ISC_R_NOMEMORY);
}
memset(dlz_imp, 0, sizeof(dns_dlzimplementation_t));
dlz_imp->name = drivername;
dlz_imp->methods = methods;
dlz_imp->mctx = NULL;
dlz_imp->driverarg = driverarg;
isc_mem_attach(mctx, &dlz_imp->mctx);
ISC_LINK_INIT(dlz_imp, link);
ISC_LIST_APPEND(dlz_implementations, dlz_imp, link);
RWUNLOCK(&dlz_implock, isc_rwlocktype_write);
*dlzimp = dlz_imp;
return (ISC_R_SUCCESS);
}
static isc_result_t
dns_dlzstrtoargvsub(isc_mem_t *mctx, char *s, unsigned int *argcp,
char ***argvp, unsigned int n)
{
isc_result_t result;
restart:
while (*s == ' ' || *s == '\t')
s++;
if (*s == '\0') {
*argcp = n;
*argvp = isc_mem_get(mctx, n * sizeof(char *));
if (*argvp == NULL)
return (ISC_R_NOMEMORY);
} else {
char *p = s;
while (*p != ' ' && *p != '\t' && *p != '\0' && *p != '{') {
if (*p == '\n') {
*p = ' ';
goto restart;
}
p++;
}
if (*p == '{') {
char *t = p;
while (*t != '\0') {
t++;
*(t-1) = *t;
}
while (*p != '\0' && *p != '}') {
p++;
}
if (*p == '}') {
*p = '\0';
p++;
}
} else if (*p != '\0')
*p++ = '\0';
result = dns_dlzstrtoargvsub(mctx, p, argcp, argvp, n + 1);
if (result != ISC_R_SUCCESS)
return (result);
(*argvp)[n] = s;
}
return (ISC_R_SUCCESS);
}
isc_result_t
dns_dlzstrtoargv(isc_mem_t *mctx, char *s,
unsigned int *argcp, char ***argvp)
{
return(dns_dlzstrtoargvsub(mctx, s, argcp, argvp, 0));
}
void
dns_dlzunregister(dns_dlzimplementation_t **dlzimp) {
dns_dlzimplementation_t *dlz_imp;
isc_mem_t *mctx;
isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2),
"Unregistering DLZ driver.");
REQUIRE(dlzimp != NULL && *dlzimp != NULL);
RUNTIME_CHECK(isc_once_do(&once, dlz_initialize) == ISC_R_SUCCESS);
dlz_imp = *dlzimp;
RWLOCK(&dlz_implock, isc_rwlocktype_write);
ISC_LIST_UNLINK(dlz_implementations, dlz_imp, link);
mctx = dlz_imp->mctx;
isc_mem_put(mctx, dlz_imp, sizeof(dns_dlzimplementation_t));
isc_mem_detach(&mctx);
RWUNLOCK(&dlz_implock, isc_rwlocktype_write);
}
#ifdef BIND9
isc_result_t
dns_dlz_writeablezone(dns_view_t *view, 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;
dns_dlzdb_t *dlzdatabase;
REQUIRE(DNS_DLZ_VALID(view->dlzdatabase));
dlzdatabase = view->dlzdatabase;
REQUIRE(dlzdatabase->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);
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);
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, ISC_TRUE);
if (dlzdatabase->ssutable == NULL) {
result = dns_ssutable_createdlz(dlzdatabase->mctx,
&dlzdatabase->ssutable,
view->dlzdatabase);
if (result != ISC_R_SUCCESS)
goto cleanup;
}
dns_zone_setssutable(zone, dlzdatabase->ssutable);
result = dlzdatabase->configure_callback(view, zone);
if (result != ISC_R_SUCCESS)
goto cleanup;
result = dns_view_addzone(view, zone);
cleanup:
if (zone != NULL)
dns_zone_detach(&zone);
return (result);
}
#endif
isc_result_t
dns_dlzconfigure(dns_view_t *view, isc_result_t (*callback)(dns_view_t *,
dns_zone_t *))
{
dns_dlzimplementation_t *impl;
dns_dlzdb_t *dlzdatabase;
isc_result_t result;
REQUIRE(view != NULL);
REQUIRE(DNS_DLZ_VALID(view->dlzdatabase));
REQUIRE(view->dlzdatabase->implementation != NULL);
dlzdatabase = view->dlzdatabase;
impl = dlzdatabase->implementation;
if (impl->methods->configure == NULL)
return (ISC_R_SUCCESS);
dlzdatabase->configure_callback = callback;
result = impl->methods->configure(impl->driverarg,
dlzdatabase->dbdata, view);
return (result);
}
isc_boolean_t
dns_dlz_ssumatch(dns_dlzdb_t *dlzdatabase,
dns_name_t *signer, dns_name_t *name, isc_netaddr_t *tcpaddr,
dns_rdatatype_t type, const dst_key_t *key)
{
dns_dlzimplementation_t *impl;
isc_boolean_t 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 (ISC_FALSE);
}
r = impl->methods->ssumatch(signer, name, tcpaddr, type, key,
impl->driverarg, dlzdatabase->dbdata);
return (r);
}