summaryrefslogtreecommitdiffstats
path: root/src/ldap_driver.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/ldap_driver.c')
-rw-r--r--src/ldap_driver.c1076
1 files changed, 1076 insertions, 0 deletions
diff --git a/src/ldap_driver.c b/src/ldap_driver.c
new file mode 100644
index 0000000..8003c85
--- /dev/null
+++ b/src/ldap_driver.c
@@ -0,0 +1,1076 @@
+/* Authors: Martin Nagy <mnagy@redhat.com>
+ * Adam Tkac <atkac@redhat.com>
+ *
+ * Copyright (C) 2008, 2009 Red Hat
+ * see file 'COPYING' for use and warranty information
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; version 2 only
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <isc/buffer.h>
+#include <isc/mem.h>
+#include <isc/refcount.h>
+#include <isc/util.h>
+
+#include <dns/db.h>
+#include <dns/rdata.h>
+#include <dns/rdataclass.h>
+#include <dns/rdatalist.h>
+#include <dns/rdataset.h>
+#include <dns/rdatatype.h>
+#include <dns/result.h>
+#include <dns/types.h>
+
+#include <string.h> /* For memcpy */
+
+#include "cache.h"
+#include "ldap_helper.h"
+#include "log.h"
+#include "rdlist.h"
+#include "util.h"
+#include "zone_manager.h"
+
+#define LDAPDB_MAGIC ISC_MAGIC('L', 'D', 'P', 'D')
+#define VALID_LDAPDB(ldapdb) \
+ ((ldapdb) != NULL && (ldapdb)->common.impmagic == LDAPDB_MAGIC)
+
+#define LDAPDBNODE_MAGIC ISC_MAGIC('L', 'D', 'P', 'N')
+#define VALID_LDAPDBNODE(ldapdbnode) ISC_MAGIC_VALID(ldapdbnode, \
+ LDAPDBNODE_MAGIC)
+
+static dns_rdatasetmethods_t rdataset_methods;
+
+typedef struct {
+ dns_db_t common;
+ isc_refcount_t refs;
+ isc_mutex_t lock; /* convert to isc_rwlock_t ? */
+ ldap_db_t *ldap_db;
+ ldap_cache_t *ldap_cache;
+} ldapdb_t;
+
+typedef struct {
+ unsigned int magic;
+ isc_refcount_t refs;
+ dns_name_t owner;
+ ldapdb_rdatalist_t rdatalist;
+} ldapdbnode_t;
+
+static int dummy;
+static void *ldapdb_version = &dummy;
+
+static void free_ldapdb(ldapdb_t *ldapdb);
+static void detachnode(dns_db_t *db, dns_dbnode_t **targetp);
+static unsigned int rdatalist_length(const dns_rdatalist_t *rdlist);
+
+/* ldapdbnode_t functions */
+static isc_result_t
+ldapdbnode_create(isc_mem_t *mctx, dns_name_t *owner, ldapdbnode_t **nodep)
+{
+ ldapdbnode_t *node = NULL;
+ isc_result_t result;
+
+ REQUIRE(nodep != NULL && *nodep == NULL);
+
+ CHECKED_MEM_GET_PTR(mctx, node);
+ CHECK(isc_refcount_init(&node->refs, 1));
+
+ dns_name_init(&node->owner, NULL);
+ CHECK(dns_name_dup(owner, mctx, &node->owner));
+
+ node->magic = LDAPDBNODE_MAGIC;
+
+ ISC_LIST_INIT(node->rdatalist);
+
+ *nodep = node;
+
+ return ISC_R_SUCCESS;
+
+cleanup:
+ SAFE_MEM_PUT_PTR(mctx, node);
+
+ return result;
+}
+
+/*
+ * Clone rdlist and convert it into rdataset.
+ */
+static isc_result_t
+clone_rdatalist_to_rdataset(isc_mem_t *mctx, dns_rdatalist_t *rdlist,
+ dns_rdataset_t *rdataset)
+{
+ isc_result_t result;
+ dns_rdatalist_t *new_rdlist = NULL;
+
+ REQUIRE(mctx != NULL);
+
+ CHECK(rdatalist_clone(mctx, rdlist, &new_rdlist));
+
+ CHECK(dns_rdatalist_tordataset(new_rdlist, rdataset));
+ rdataset->methods = &rdataset_methods;
+ isc_mem_attach(mctx, (isc_mem_t **)&rdataset->private5);
+
+ return result;
+
+cleanup:
+ if (new_rdlist != NULL) {
+ free_rdatalist(mctx, rdlist);
+ isc_mem_put(mctx, new_rdlist, sizeof(*new_rdlist));
+ }
+
+ return result;
+}
+
+/*
+ * Our own function for disassociating rdatasets. We will also free the
+ * rdatalist that we put inside from clone_rdatalist_to_rdataset.
+ */
+void
+ldapdb_rdataset_disassociate(dns_rdataset_t *rdataset)
+{
+ dns_rdatalist_t *rdlist;
+ isc_mem_t *mctx;
+
+ REQUIRE(rdataset != NULL);
+
+ rdlist = rdataset->private1;
+ mctx = rdataset->private5;
+ if (rdlist == NULL)
+ return;
+ rdataset->private1 = NULL;
+ rdataset->private5 = NULL;
+
+ free_rdatalist(mctx, rdlist);
+ SAFE_MEM_PUT_PTR(mctx, rdlist);
+
+ isc_mem_detach(&mctx);
+}
+
+/*
+ * Functions.
+ *
+ * Most of them don't need db parameter but we are checking if it is valid.
+ * Invalid db parameter indicates bug in code.
+ */
+
+static void
+attach(dns_db_t *source, dns_db_t **targetp)
+{
+ ldapdb_t *ldapdb = (ldapdb_t *)source;
+
+ REQUIRE(VALID_LDAPDB(ldapdb));
+
+ isc_refcount_increment(&ldapdb->refs, NULL);
+ *targetp = source;
+}
+
+static void
+detach(dns_db_t **dbp)
+{
+ ldapdb_t *ldapdb = (ldapdb_t *)(*dbp);
+ unsigned int refs;
+
+ REQUIRE(VALID_LDAPDB(ldapdb));
+
+ isc_refcount_decrement(&ldapdb->refs, &refs);
+
+ if (refs == 0)
+ free_ldapdb(ldapdb);
+
+ *dbp = NULL;
+}
+
+static void
+free_ldapdb(ldapdb_t *ldapdb)
+{
+ DESTROYLOCK(&ldapdb->lock);
+ dns_name_free(&ldapdb->common.origin, ldapdb->common.mctx);
+ isc_mem_putanddetach(&ldapdb->common.mctx, ldapdb, sizeof(*ldapdb));
+}
+
+static isc_result_t
+beginload(dns_db_t *db, dns_addrdatasetfunc_t *addp, dns_dbload_t **dbloadp)
+{
+
+ UNUSED(db);
+ UNUSED(addp);
+ UNUSED(dbloadp);
+
+ fatal_error("ldapdb: method beginload() should never be called");
+
+ /* Not reached */
+ return ISC_R_SUCCESS;
+}
+
+static isc_result_t
+endload(dns_db_t *db, dns_dbload_t **dbloadp)
+{
+
+ UNUSED(db);
+ UNUSED(dbloadp);
+
+ fatal_error("ldapdb: method endload() should never be called");
+
+ /* Not reached */
+ return ISC_R_SUCCESS;
+}
+
+static isc_result_t
+dump(dns_db_t *db, dns_dbversion_t *version, const char *filename,
+ dns_masterformat_t masterformat)
+{
+
+ UNUSED(db);
+ UNUSED(version);
+ UNUSED(filename);
+ UNUSED(masterformat);
+
+ fatal_error("ldapdb: method dump() should never be called");
+
+ /* Not reached */
+ return ISC_R_SUCCESS;
+}
+
+static void
+currentversion(dns_db_t *db, dns_dbversion_t **versionp)
+{
+ ldapdb_t *ldapdb = (ldapdb_t *)db;
+
+ REQUIRE(VALID_LDAPDB(ldapdb));
+ REQUIRE(versionp != NULL && *versionp == NULL);
+
+ *versionp = ldapdb_version;
+}
+
+static isc_result_t
+newversion(dns_db_t *db, dns_dbversion_t **versionp)
+{
+ ldapdb_t *ldapdb = (ldapdb_t *)db;
+
+ REQUIRE(VALID_LDAPDB(ldapdb));
+ REQUIRE(versionp != NULL && *versionp == NULL);
+
+ *versionp = ldapdb_version;
+ return ISC_R_SUCCESS;
+}
+
+static void
+attachversion(dns_db_t *db, dns_dbversion_t *source,
+ dns_dbversion_t **targetp)
+{
+ ldapdb_t *ldapdb = (ldapdb_t *)db;
+
+ REQUIRE(VALID_LDAPDB(ldapdb));
+ REQUIRE(source == ldapdb_version);
+ REQUIRE(targetp != NULL && *targetp == NULL);
+
+ *targetp = ldapdb_version;
+}
+
+static void
+closeversion(dns_db_t *db, dns_dbversion_t **versionp, isc_boolean_t commit)
+{
+ ldapdb_t *ldapdb = (ldapdb_t *)db;
+
+ UNUSED(commit);
+
+ REQUIRE(VALID_LDAPDB(ldapdb));
+ REQUIRE(versionp != NULL && *versionp == ldapdb_version);
+
+ *versionp = NULL;
+}
+
+static isc_result_t
+findnode(dns_db_t *db, dns_name_t *name, isc_boolean_t create,
+ dns_dbnode_t **nodep)
+{
+ ldapdb_t *ldapdb = (ldapdb_t *) db;
+ isc_result_t result;
+ ldapdb_rdatalist_t rdatalist;
+ ldapdbnode_t *node = NULL;
+
+ REQUIRE(VALID_LDAPDB(ldapdb));
+
+ result = cached_ldap_rdatalist_get(ldapdb->common.mctx,
+ ldapdb->ldap_cache, ldapdb->ldap_db,
+ name, &rdatalist);
+
+ if (result == ISC_R_NOMEMORY)
+ return ISC_R_NOMEMORY;
+
+ if (create == ISC_FALSE) {
+ /* No partial matches are allowed in this function */
+ if (result == DNS_R_PARTIALMATCH) {
+ result = ISC_R_NOTFOUND;
+ goto cleanup;
+ } else if (result != ISC_R_SUCCESS) {
+ return result;
+ }
+ }
+
+ CHECK(ldapdbnode_create(ldapdb->common.mctx, name, &node));
+
+ memcpy(&node->rdatalist, &rdatalist, sizeof(rdatalist));
+
+ *nodep = node;
+
+ return ISC_R_SUCCESS;
+
+cleanup:
+ ldapdb_rdatalist_destroy(ldapdb->common.mctx, &rdatalist);
+
+ return result;
+}
+
+/* XXX add support for DNAME redirection */
+static isc_result_t
+find(dns_db_t *db, dns_name_t *name, dns_dbversion_t *version,
+ dns_rdatatype_t type, unsigned int options, isc_stdtime_t now,
+ dns_dbnode_t **nodep, dns_name_t *foundname, dns_rdataset_t *rdataset,
+ dns_rdataset_t *sigrdataset)
+{
+ ldapdb_t *ldapdb = (ldapdb_t *) db;
+ isc_result_t result;
+ ldapdbnode_t *node = NULL;
+ dns_rdatalist_t *rdlist = NULL;
+ isc_boolean_t is_cname = ISC_FALSE;
+ ldapdb_rdatalist_t rdatalist;
+
+ UNUSED(now);
+ UNUSED(options);
+ UNUSED(sigrdataset);
+
+ REQUIRE(VALID_LDAPDB(ldapdb));
+ REQUIRE(!(node != NULL && type == dns_rdatatype_any));
+ //REQUIRE(!(node == NULL && rdataset != NULL));
+
+ if (version != NULL) {
+ REQUIRE(version == ldapdb_version);
+ }
+
+ result = cached_ldap_rdatalist_get(ldapdb->common.mctx,
+ ldapdb->ldap_cache, ldapdb->ldap_db,
+ name, &rdatalist);
+ INSIST(result != DNS_R_PARTIALMATCH); /* XXX Not yet implemented */
+
+ if (result != ISC_R_SUCCESS && result != DNS_R_PARTIALMATCH)
+ return (result == ISC_R_NOTFOUND) ? DNS_R_NXDOMAIN : result;
+
+ result = ldapdb_rdatalist_findrdatatype(&rdatalist, type, &rdlist);
+ if (result != ISC_R_SUCCESS) {
+ /* No exact rdtype match. Check CNAME */
+
+ rdlist = HEAD(rdatalist);
+ while (rdlist != NULL && rdlist->type != dns_rdatatype_cname)
+ rdlist = NEXT(rdlist, link);
+
+ /* CNAME was found */
+ if (rdlist != NULL) {
+ result = ISC_R_SUCCESS;
+ is_cname = ISC_TRUE;
+ }
+ }
+
+ if (result != ISC_R_SUCCESS) {
+ result = DNS_R_NXRRSET;
+ goto cleanup;
+ }
+
+ /* XXX currently we implemented only exact authoritative matches */
+ CHECK(dns_name_copy(name, foundname, NULL));
+
+ if (rdataset != NULL && type != dns_rdatatype_any) {
+ /* dns_rdatalist_tordataset returns success only */
+ CHECK(clone_rdatalist_to_rdataset(ldapdb->common.mctx, rdlist,
+ rdataset));
+ }
+
+ if (nodep != NULL) {
+ CHECK(ldapdbnode_create(ldapdb->common.mctx, name, &node));
+ memcpy(&node->rdatalist, &rdatalist, sizeof(rdatalist));
+ *nodep = node;
+ } else {
+ ldapdb_rdatalist_destroy(ldapdb->common.mctx, &rdatalist);
+ }
+
+ return (is_cname == ISC_TRUE) ? DNS_R_CNAME : ISC_R_SUCCESS;
+
+cleanup:
+ ldapdb_rdatalist_destroy(ldapdb->common.mctx, &rdatalist);
+ return result;
+}
+
+static isc_result_t
+findzonecut(dns_db_t *db, dns_name_t *name, unsigned int options,
+ isc_stdtime_t now, dns_dbnode_t **nodep, dns_name_t *foundname,
+ dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset)
+{
+ UNUSED(db);
+ UNUSED(name);
+ UNUSED(options);
+ UNUSED(now);
+ UNUSED(nodep);
+ UNUSED(foundname);
+ UNUSED(rdataset);
+ UNUSED(sigrdataset);
+
+ return ISC_R_NOTIMPLEMENTED;
+}
+
+static void
+attachnode(dns_db_t *db, dns_dbnode_t *source, dns_dbnode_t **targetp)
+{
+ ldapdbnode_t *node = (ldapdbnode_t *) source;
+
+ REQUIRE(VALID_LDAPDBNODE(node));
+
+ UNUSED(db);
+
+ isc_refcount_increment(&node->refs, NULL);
+ *targetp = source;
+}
+
+static void
+detachnode(dns_db_t *db, dns_dbnode_t **targetp)
+{
+ ldapdbnode_t *node = (ldapdbnode_t *)(*targetp);
+ ldapdb_t *ldapdb = (ldapdb_t *) db;
+ unsigned int refs;
+
+ /*
+ * Don't check for db and targetp validity, it's done in
+ * dns_db_detachnode
+ */
+
+ REQUIRE(VALID_LDAPDBNODE(node));
+ isc_refcount_decrement(&node->refs, &refs);
+ if (refs == 0) {
+ ldapdb_rdatalist_destroy(ldapdb->common.mctx, &node->rdatalist);
+ dns_name_free(&node->owner, ldapdb->common.mctx);
+ SAFE_MEM_PUT_PTR(ldapdb->common.mctx, node);
+ }
+
+ *targetp = NULL;
+}
+
+static isc_result_t
+expirenode(dns_db_t *db, dns_dbnode_t *node, isc_stdtime_t now)
+{
+ UNUSED(db);
+ UNUSED(node);
+ UNUSED(now);
+
+ return ISC_R_NOTIMPLEMENTED;
+}
+
+static void
+printnode(dns_db_t *db, dns_dbnode_t *node, FILE *out)
+{
+ UNUSED(db);
+ UNUSED(node);
+ UNUSED(out);
+}
+
+static isc_result_t
+createiterator(dns_db_t *db, unsigned int options,
+ dns_dbiterator_t **iteratorp)
+{
+ UNUSED(db);
+ UNUSED(options);
+ UNUSED(iteratorp);
+
+ return ISC_R_NOTIMPLEMENTED;
+}
+
+static isc_result_t
+findrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version,
+ dns_rdatatype_t type, dns_rdatatype_t covers, isc_stdtime_t now,
+ dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset)
+{
+ ldapdb_t *ldapdb = (ldapdb_t *) db;
+ ldapdbnode_t *ldapdbnode = (ldapdbnode_t *) node;
+ dns_rdatalist_t *rdlist = NULL;
+ isc_result_t result;
+
+ UNUSED(db);
+ UNUSED(now);
+ UNUSED(sigrdataset);
+
+ REQUIRE(covers == 0); /* Only meaningful with DNSSEC capable DB*/
+ REQUIRE(VALID_LDAPDBNODE(ldapdbnode));
+
+ if (version != NULL) {
+ REQUIRE(version == ldapdb_version);
+ }
+
+ result = ldapdb_rdatalist_findrdatatype(&ldapdbnode->rdatalist, type,
+ &rdlist);
+ if (result != ISC_R_SUCCESS)
+ return result;
+
+ result = clone_rdatalist_to_rdataset(ldapdb->common.mctx, rdlist,
+ rdataset);
+
+ return result;
+}
+
+static isc_result_t
+allrdatasets(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version,
+ isc_stdtime_t now, dns_rdatasetiter_t **iteratorp)
+{
+ UNUSED(db);
+ UNUSED(node);
+ UNUSED(version);
+ UNUSED(now);
+ UNUSED(iteratorp);
+
+ return ISC_R_NOTIMPLEMENTED;
+}
+
+/*
+ * Remove duplicates between rdlists. If rm_from1 == true then remove rdata
+ * from the first rdatalist. same rdata are removed from rdlist1 or 2 and are
+ * returned in diff.
+ */
+static void
+rdatalist_removedups(dns_rdatalist_t *rdlist1, dns_rdatalist_t *rdlist2,
+ isc_boolean_t rm_from1,
+ dns_rdatalist_t *diff)
+{
+ dns_rdata_t *rdata1, *rdata2;
+
+ rdata1 = HEAD(rdlist1->rdata);
+ while (rdata1 != NULL) {
+ rdata2 = HEAD(rdlist2->rdata);
+ while (rdata2 != NULL) {
+ if (dns_rdata_compare(rdata1, rdata2) != 0) {
+ rdata2 = NEXT(rdata2, link);
+ continue;
+ }
+ /* same rdata has been found */
+ if (rm_from1) {
+ ISC_LIST_UNLINK(rdlist1->rdata, rdata1, link);
+ APPEND(diff->rdata, rdata1, link);
+ } else {
+ ISC_LIST_UNLINK(rdlist2->rdata, rdata2, link);
+ APPEND(diff->rdata, rdata2, link);
+ }
+ break;
+ }
+ rdata1 = NEXT(rdata1, link);
+ }
+}
+
+static isc_result_t
+addrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version,
+ isc_stdtime_t now, dns_rdataset_t *rdataset, unsigned int options,
+ dns_rdataset_t *addedrdataset)
+{
+ ldapdbnode_t *ldapdbnode = (ldapdbnode_t *) node;
+ ldapdb_t *ldapdb = (ldapdb_t *) db;
+ dns_rdatalist_t *rdlist = NULL, *new_rdlist = NULL;
+ dns_rdatalist_t *found_rdlist = NULL;
+ dns_rdatalist_t diff;
+ isc_result_t result;
+ isc_boolean_t rdatalist_exists = ISC_FALSE;
+
+ UNUSED(now);
+ UNUSED(db);
+ UNUSED(addedrdataset);
+
+ REQUIRE(VALID_LDAPDBNODE(ldapdbnode));
+ /* version == NULL is valid only for cache databases */
+ REQUIRE(version == ldapdb_version);
+ REQUIRE((options & DNS_DBADD_FORCE) == 0);
+
+ dns_rdatalist_init(&diff);
+
+ result = dns_rdatalist_fromrdataset(rdataset, &rdlist);
+ INSIST(result == ISC_R_SUCCESS);
+ INSIST(rdlist->rdclass == dns_rdataclass_in);
+
+ CHECK(rdatalist_clone(ldapdb->common.mctx, rdlist, &new_rdlist));
+
+ result = ldapdb_rdatalist_findrdatatype(&ldapdbnode->rdatalist,
+ rdlist->type, &found_rdlist);
+ if (result == ISC_R_SUCCESS) {
+ rdatalist_exists = ISC_TRUE;
+
+ if (rdlist->ttl != found_rdlist->ttl) {
+ /*
+ * TODO: support it. When supported handle
+ * DNS_DBADD_EXACTTTL option well.
+ */
+ log_error("Multiple TTLs for one name are not "
+ "supported");
+ result = ISC_R_NOTIMPLEMENTED;
+ goto cleanup;
+ }
+
+ if ((options & DNS_DBADD_MERGE) != 0 ||
+ (options & DNS_DBADD_EXACT) != 0) {
+ rdatalist_removedups(found_rdlist, new_rdlist,
+ ISC_FALSE, &diff);
+
+ if ((options & DNS_DBADD_MERGE) != 0)
+ free_rdatalist(ldapdb->common.mctx, &diff);
+ else if (rdatalist_length(&diff) != 0) {
+ free_rdatalist(ldapdb->common.mctx, &diff);
+ result = DNS_R_NOTEXACT;
+ goto cleanup;
+ }
+ } else {
+ /* Replace existing rdataset */
+ free_rdatalist(ldapdb->common.mctx, found_rdlist);
+ }
+ }
+
+ CHECK(write_to_ldap(&ldapdbnode->owner, ldapdb->ldap_db, new_rdlist));
+ CHECK(discard_from_cache(ldapdb->ldap_cache, &ldapdbnode->owner));
+
+ if (addedrdataset != NULL) {
+ result = dns_rdatalist_tordataset(new_rdlist, addedrdataset);
+ /* Use strong condition here, returns only SUCCESS */
+ INSIST(result == ISC_R_SUCCESS);
+ }
+
+ if (rdatalist_exists) {
+ ISC_LIST_APPENDLIST(found_rdlist->rdata, new_rdlist->rdata,
+ link);
+ SAFE_MEM_PUT_PTR(ldapdb->common.mctx, new_rdlist);
+ } else
+ APPEND(ldapdbnode->rdatalist, new_rdlist, link);
+
+
+ return ISC_R_SUCCESS;
+
+cleanup:
+ if (new_rdlist != NULL) {
+ free_rdatalist(ldapdb->common.mctx, new_rdlist);
+ SAFE_MEM_PUT_PTR(ldapdb->common.mctx, new_rdlist);
+ }
+
+ return result;
+}
+
+static unsigned int
+rdatalist_length(const dns_rdatalist_t *rdlist)
+{
+ dns_rdata_t *ptr = HEAD(rdlist->rdata);
+ unsigned int length = 0;
+
+ while (ptr != NULL) {
+ length++;
+ ptr = NEXT(ptr, link);
+ }
+
+ return length;
+}
+
+static isc_result_t
+subtractrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version,
+ dns_rdataset_t *rdataset, unsigned int options,
+ dns_rdataset_t *newrdataset)
+{
+ ldapdb_t *ldapdb = (ldapdb_t *) db;
+ ldapdbnode_t *ldapdbnode = (ldapdbnode_t *) node;
+ dns_rdatalist_t *found_rdlist = NULL;
+ dns_rdatalist_t *rdlist;
+ dns_rdatalist_t diff;
+ isc_result_t result;
+
+ REQUIRE(version == ldapdb_version);
+
+ result = dns_rdatalist_fromrdataset(rdataset, &rdlist);
+ /* Use strong condition here, no other value is returned */
+ INSIST(result == ISC_R_SUCCESS);
+
+ /* Do we want to use memcpy here? */
+ dns_rdatalist_init(&diff);
+ diff.rdclass = rdlist->rdclass;
+ diff.type = rdlist->type;
+ diff.covers = rdlist->covers;
+ diff.ttl = rdlist->ttl;
+
+ result = ldapdb_rdatalist_findrdatatype(&ldapdbnode->rdatalist,
+ rdlist->type, &found_rdlist);
+
+ if (result == ISC_R_NOTFOUND)
+ return DNS_R_NXRRSET;
+
+ /* We found correct type, remove maching rdata */
+ rdatalist_removedups(rdlist, found_rdlist, ISC_FALSE, &diff);
+
+ if ((options & DNS_DBSUB_EXACT) != 0 &&
+ rdatalist_length(&diff) != rdatalist_length(rdlist)) {
+ /* Not exact match, rollback */
+ result = DNS_R_NOTEXACT;
+ goto cleanup;
+ }
+
+ if (rdatalist_length(&diff) == 0) {
+ result = DNS_R_UNCHANGED;
+ goto cleanup;
+ }
+
+ CHECK(remove_from_ldap(&ldapdbnode->owner, ldapdb->ldap_db, &diff));
+
+ if (newrdataset != NULL) {
+ result = dns_rdatalist_tordataset(found_rdlist, newrdataset);
+ /* Use strong condition here, no other value is returned */
+ INSIST(result == ISC_R_SUCCESS);
+ }
+
+ free_rdatalist(ldapdb->common.mctx, &diff);
+
+ return ISC_R_SUCCESS;
+
+cleanup:
+ /* Roll back changes */
+ ISC_LIST_APPENDLIST(found_rdlist->rdata, diff.rdata, link);
+
+ return result;
+}
+
+static isc_result_t
+deleterdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version,
+ dns_rdatatype_t type, dns_rdatatype_t covers)
+{
+ UNUSED(db);
+ UNUSED(node);
+ UNUSED(version);
+ UNUSED(type);
+ UNUSED(covers);
+
+ REQUIRE("deleterdataset" == NULL);
+
+ return ISC_R_NOTIMPLEMENTED;
+}
+
+static isc_boolean_t
+issecure(dns_db_t *db)
+{
+ UNUSED(db);
+
+ return ISC_FALSE;
+}
+
+static unsigned int
+nodecount(dns_db_t *db)
+{
+ UNUSED(db);
+
+ return ISC_R_NOTIMPLEMENTED;
+}
+
+static isc_boolean_t
+ispersistent(dns_db_t *db)
+{
+ UNUSED(db);
+
+ return ISC_R_NOTIMPLEMENTED;
+}
+
+static void
+overmem(dns_db_t *db, isc_boolean_t overmem)
+{
+ UNUSED(db);
+ UNUSED(overmem);
+}
+
+static void
+settask(dns_db_t *db, isc_task_t *task)
+{
+ UNUSED(db);
+ UNUSED(task);
+}
+
+static isc_result_t
+getoriginnode(dns_db_t *db, dns_dbnode_t **nodep)
+{
+ ldapdb_t *ldapdb = (ldapdb_t *) db;
+
+ return findnode(db, &ldapdb->common.origin, ISC_FALSE, nodep);
+}
+
+static void
+transfernode(dns_db_t *db, dns_dbnode_t **sourcep, dns_dbnode_t **targetp)
+{
+ UNUSED(db);
+ UNUSED(sourcep);
+ UNUSED(targetp);
+}
+
+static isc_result_t
+getnsec3parameters(dns_db_t *db, dns_dbversion_t *version, dns_hash_t *hash,
+ isc_uint8_t *flags, isc_uint16_t *iterations,
+ unsigned char *salt, size_t *salt_len)
+{
+ UNUSED(db);
+ UNUSED(version);
+ UNUSED(hash);
+ UNUSED(flags);
+ UNUSED(iterations);
+ UNUSED(salt);
+ UNUSED(salt_len);
+
+ return ISC_R_NOTIMPLEMENTED;
+}
+
+static isc_result_t
+findnsec3node(dns_db_t *db, dns_name_t *name, isc_boolean_t create,
+ dns_dbnode_t **nodep)
+{
+ UNUSED(db);
+ UNUSED(name);
+ UNUSED(create);
+ UNUSED(nodep);
+
+ return ISC_R_NOTIMPLEMENTED;
+}
+
+static isc_result_t
+setsigningtime(dns_db_t *db, dns_rdataset_t *rdataset, isc_stdtime_t resign)
+{
+ UNUSED(db);
+ UNUSED(rdataset);
+ UNUSED(resign);
+
+ return ISC_R_NOTIMPLEMENTED;
+}
+
+static isc_result_t
+getsigningtime(dns_db_t *db, dns_rdataset_t *rdataset, dns_name_t *name)
+{
+ UNUSED(db);
+ UNUSED(rdataset);
+ UNUSED(name);
+
+ return ISC_R_NOTIMPLEMENTED;
+}
+
+static void
+resigned(dns_db_t *db, dns_rdataset_t *rdataset, dns_dbversion_t *version)
+{
+ UNUSED(db);
+ UNUSED(rdataset);
+ UNUSED(version);
+}
+
+static isc_boolean_t
+isdnssec(dns_db_t *db)
+{
+ UNUSED(db);
+
+ return ISC_R_NOTIMPLEMENTED;
+}
+
+static dns_stats_t *
+getrrsetstats(dns_db_t *db)
+{
+ UNUSED(db);
+
+ return NULL;
+}
+
+static dns_dbmethods_t ldapdb_methods = {
+ attach,
+ detach,
+ beginload,
+ endload,
+ dump,
+ currentversion,
+ newversion,
+ attachversion,
+ closeversion,
+ findnode,
+ find,
+ findzonecut,
+ attachnode,
+ detachnode,
+ expirenode,
+ printnode,
+ createiterator,
+ findrdataset,
+ allrdatasets,
+ addrdataset,
+ subtractrdataset,
+ deleterdataset,
+ issecure,
+ nodecount,
+ ispersistent,
+ overmem,
+ settask,
+ getoriginnode,
+ transfernode,
+ getnsec3parameters,
+ findnsec3node,
+ setsigningtime,
+ getsigningtime,
+ resigned,
+ isdnssec,
+ getrrsetstats
+};
+
+static isc_result_t
+ldapdb_create(isc_mem_t *mctx, dns_name_t *name, dns_dbtype_t type,
+ dns_rdataclass_t rdclass, unsigned int argc, char *argv[],
+ void *driverarg, dns_db_t **dbp)
+{
+ ldapdb_t *ldapdb = NULL;
+ isc_result_t result;
+ int lock_is_initialized = 0;
+
+ UNUSED(driverarg); /* Currently we don't need any data */
+
+ /* Database instance name. */
+ REQUIRE(argc > 0);
+
+ REQUIRE(type == dns_dbtype_zone);
+ REQUIRE(rdclass == dns_rdataclass_in);
+ REQUIRE(dbp != NULL && *dbp == NULL);
+
+ CHECKED_MEM_GET_PTR(mctx, ldapdb);
+ ZERO_PTR(ldapdb);
+
+ isc_mem_attach(mctx, &ldapdb->common.mctx);
+
+ dns_name_init(&ldapdb->common.origin, NULL);
+ isc_ondestroy_init(&ldapdb->common.ondest);
+
+ CHECK(isc_mutex_init(&ldapdb->lock));
+ lock_is_initialized = 1;
+
+ ldapdb->common.magic = DNS_DB_MAGIC;
+ ldapdb->common.impmagic = LDAPDB_MAGIC;
+
+ ldapdb->common.methods = &ldapdb_methods;
+ ldapdb->common.attributes = 0;
+ ldapdb->common.rdclass = rdclass;
+
+ CHECK(dns_name_dupwithoffsets(name, mctx, &ldapdb->common.origin));
+
+ CHECK(isc_refcount_init(&ldapdb->refs, 1));
+ CHECK(manager_get_ldap_db_and_cache(argv[0], &ldapdb->ldap_db,
+ &ldapdb->ldap_cache));
+
+ *dbp = (dns_db_t *)ldapdb;
+
+ return ISC_R_SUCCESS;
+
+cleanup:
+ if (ldapdb != NULL) {
+ if (lock_is_initialized)
+ DESTROYLOCK(&ldapdb->lock);
+ if (dns_name_dynamic(&ldapdb->common.origin))
+ dns_name_free(&ldapdb->common.origin, mctx);
+
+ isc_mem_putanddetach(&ldapdb->common.mctx, ldapdb,
+ sizeof(*ldapdb));
+ }
+
+ return result;
+}
+
+static dns_dbimplementation_t *ldapdb_imp;
+const char *ldapdb_impname = "dynamic-ldap";
+
+
+isc_result_t
+dynamic_driver_init(isc_mem_t *mctx, const char *name, const char * const *argv,
+ dns_view_t *view, dns_zonemgr_t *zmgr)
+{
+ isc_result_t result;
+ ldap_db_t *ldap_db = NULL;
+ ldap_cache_t *ldap_cache = NULL;
+
+ REQUIRE(mctx != NULL);
+ REQUIRE(name != NULL);
+ REQUIRE(argv != NULL);
+ REQUIRE(view != NULL);
+
+ log_debug(2, "Registering dynamic ldap driver for %s.", name);
+
+ /* Test argv. */
+ int i = 0;
+ while (argv[i] != NULL) {
+ log_debug(2, "Arg: %s", argv[i]);
+ i++;
+ }
+
+ /*
+ * We need to discover what rdataset methods does
+ * dns_rdatalist_tordataset use. We then make a copy for ourselves
+ * with the exception that we modify the disassociate method to free
+ * the rdlist we allocate for it in clone_rdatalist_to_rdataset().
+ */
+ if (rdataset_methods.disassociate == NULL) {
+ dns_rdataset_t rdset;
+ dns_rdatalist_t rdatalist;
+
+ dns_rdataset_init(&rdset);
+ dns_rdatalist_tordataset(&rdatalist, &rdset);
+ memcpy(&rdataset_methods, rdset.methods,
+ sizeof(dns_rdatasetmethods_t));
+ rdataset_methods.disassociate = ldapdb_rdataset_disassociate;
+ }
+
+ /* Register new DNS DB implementation. */
+ result = dns_db_register(ldapdb_impname, &ldapdb_create, NULL, mctx,
+ &ldapdb_imp);
+ if (result != ISC_R_SUCCESS && result != ISC_R_EXISTS)
+ return result;
+
+ CHECK(new_ldap_db(mctx, view, &ldap_db, argv));
+ CHECK(new_ldap_cache(mctx, &ldap_cache, argv));
+ CHECK(manager_add_db_instance(mctx, name, ldap_db, ldap_cache, zmgr));
+
+ /*
+ * XXX now fetch all zones and initialize ldap zone manager
+ * (periodically check for new zones)
+ * - manager has to share server zonemgr (ns_g_server->zonemgr)
+ *
+ * XXX manager has to this this for each zone:
+ * - dns_zone_create
+ * - dns_zone_setorigin
+ * - dns_zone_setview
+ * - dns_zone_setacache (probably not needed)
+ * - dns_zone_setclass
+ * - dns_zone_settype
+ * - dns_zone_setdbtype (note: pass all connection arguments etc here -
+ * will be used by ldapdb_create)
+ * - continue as in bin/server.c - ns_zone_configure()
+ * - dns_zonemgr_managezone
+ *
+ * zone has to be bind-ed to specified view:
+ * - dns_view_findzone (check if zone already exists)
+ * - dns_view_addzone
+ */
+
+ return ISC_R_SUCCESS;
+
+cleanup:
+ if (ldap_db != NULL)
+ destroy_ldap_db(&ldap_db);
+ if (ldap_cache != NULL)
+ destroy_ldap_cache(&ldap_cache);
+
+ return result;
+}
+
+void
+dynamic_driver_destroy(void)
+{
+ dns_db_unregister(&ldapdb_imp);
+ destroy_manager();
+}