summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorMartin Nagy <mnagy@redhat.com>2009-03-30 13:01:56 +0200
committerMartin Nagy <mnagy@redhat.com>2009-03-30 13:08:32 +0200
commita148a9f5f2659c60a534f82d0ec8736f90afa223 (patch)
tree2244198110d012ec0afa16a1a3eee64ee4057061 /src
parentdd3b35c40b5709f8ea3ded23d4bb5fb4a9320352 (diff)
downloadldap_driver-a148a9f5f2659c60a534f82d0ec8736f90afa223.tar.gz
ldap_driver-a148a9f5f2659c60a534f82d0ec8736f90afa223.tar.xz
ldap_driver-a148a9f5f2659c60a534f82d0ec8736f90afa223.zip
Prepare the tree for conversion to autoconf.
All source files are now moved to src/ and the old Makefile is removed.
Diffstat (limited to 'src')
-rw-r--r--src/cache.c249
-rw-r--r--src/cache.h58
-rw-r--r--src/ldap_convert.c278
-rw-r--r--src/ldap_convert.h45
-rw-r--r--src/ldap_driver.c1076
-rw-r--r--src/ldap_helper.c1707
-rw-r--r--src/ldap_helper.h105
-rw-r--r--src/log.c71
-rw-r--r--src/log.h48
-rw-r--r--src/rdlist.c138
-rw-r--r--src/rdlist.h32
-rw-r--r--src/semaphore.c108
-rw-r--r--src/semaphore.h45
-rw-r--r--src/settings.c176
-rw-r--r--src/settings.h73
-rw-r--r--src/str.c591
-rw-r--r--src/str.h76
-rw-r--r--src/util.h92
-rw-r--r--src/zone_manager.c209
-rw-r--r--src/zone_manager.h40
20 files changed, 5217 insertions, 0 deletions
diff --git a/src/cache.c b/src/cache.c
new file mode 100644
index 0000000..fa57563
--- /dev/null
+++ b/src/cache.c
@@ -0,0 +1,249 @@
+/* Authors: Martin Nagy <mnagy@redhat.com>
+ *
+ * Copyright (C) 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/mem.h>
+#include <isc/mutex.h>
+#include <isc/result.h>
+#include <isc/time.h>
+#include <isc/util.h>
+
+#include <dns/rbt.h>
+#include <dns/result.h>
+
+#include <string.h>
+
+#include "cache.h"
+#include "ldap_helper.h"
+#include "log.h"
+#include "rdlist.h"
+#include "settings.h"
+#include "util.h"
+
+/* These macros require that variable 'is_locked' exists. */
+#define CONTROLED_LOCK(lock) \
+ do { \
+ LOCK(lock); \
+ is_locked = 1; \
+ } while (0)
+
+#define CONTROLED_UNLOCK(lock) \
+ do { \
+ if (is_locked) { \
+ UNLOCK(lock); \
+ is_locked = 0; \
+ } \
+ } while (0)
+
+struct ldap_cache {
+ isc_mutex_t mutex;
+ isc_mem_t *mctx;
+ dns_rbt_t *rbt;
+ isc_interval_t cache_ttl;
+};
+
+typedef struct {
+ isc_mem_t *mctx;
+ ldapdb_rdatalist_t rdatalist;
+ isc_time_t valid_until;
+} cache_node_t;
+
+static void
+cache_node_deleter(void *data, void *deleter_arg)
+{
+ cache_node_t *node = (cache_node_t *)data;
+
+ UNUSED(deleter_arg);
+ REQUIRE(data != NULL);
+
+ ldapdb_rdatalist_destroy(node->mctx, &node->rdatalist);
+ MEM_PUT_AND_DETACH(node);
+}
+
+static isc_result_t
+cache_node_create(ldap_cache_t *cache, ldapdb_rdatalist_t rdatalist,
+ cache_node_t **nodep)
+{
+ isc_result_t result;
+ cache_node_t *node;
+
+ REQUIRE(cache != NULL);
+ REQUIRE(nodep != NULL && *nodep == NULL);
+
+ CHECKED_MEM_GET_PTR(cache->mctx, node);
+ ZERO_PTR(node);
+ isc_mem_attach(cache->mctx, &node->mctx);
+ node->rdatalist = rdatalist;
+ CHECK(isc_time_nowplusinterval(&node->valid_until, &cache->cache_ttl));
+
+ *nodep = node;
+ return ISC_R_SUCCESS;
+
+cleanup:
+ SAFE_MEM_PUT_PTR(cache->mctx, node);
+
+ return result;
+}
+
+isc_result_t
+new_ldap_cache(isc_mem_t *mctx, ldap_cache_t **cachep,
+ const char * const *argv)
+{
+ isc_result_t result;
+ ldap_cache_t *cache = NULL;
+ unsigned int cache_ttl;
+ setting_t cache_settings[] = {
+ { "cache_ttl", default_uint(120) },
+ end_of_settings
+ };
+
+ REQUIRE(cachep != NULL && *cachep == NULL);
+
+ cache_settings[0].target = &cache_ttl;
+ CHECK(set_settings(cache_settings, argv));
+
+ CHECKED_MEM_GET_PTR(mctx, cache);
+ ZERO_PTR(cache);
+ isc_mem_attach(mctx, &cache->mctx);
+
+ isc_interval_set(&cache->cache_ttl, cache_ttl, 0);
+
+ if (cache_ttl) {
+ CHECK(dns_rbt_create(mctx, cache_node_deleter, NULL,
+ &cache->rbt));
+ CHECK(isc_mutex_init(&cache->mutex));
+ }
+
+ *cachep = cache;
+ return ISC_R_SUCCESS;
+
+cleanup:
+ if (cache != NULL)
+ destroy_ldap_cache(&cache);
+
+ return result;
+}
+
+void
+destroy_ldap_cache(ldap_cache_t **cachep)
+{
+ ldap_cache_t *cache;
+ int is_locked = 0;
+
+ REQUIRE(cachep != NULL && *cachep != NULL);
+
+ cache = *cachep;
+
+ if (cache->rbt) {
+ CONTROLED_LOCK(&cache->mutex);
+ dns_rbt_destroy(&cache->rbt);
+ cache->rbt = NULL;
+ CONTROLED_UNLOCK(&cache->mutex);
+ DESTROYLOCK(&cache->mutex);
+ }
+
+ MEM_PUT_AND_DETACH(cache);
+
+ *cachep = NULL;
+}
+
+isc_result_t
+cached_ldap_rdatalist_get(isc_mem_t *mctx, ldap_cache_t *cache,
+ ldap_db_t *ldap_db, dns_name_t *name,
+ ldapdb_rdatalist_t *rdatalist)
+{
+ isc_result_t result;
+ ldapdb_rdatalist_t rdlist;
+ cache_node_t *node = NULL;
+ int in_cache = 0;
+ int is_locked = 0;
+
+ REQUIRE(cache != NULL);
+
+ if (cache->rbt == NULL)
+ return ldapdb_rdatalist_get(mctx, ldap_db, name, rdatalist);
+
+ CONTROLED_LOCK(&cache->mutex);
+ result = dns_rbt_findname(cache->rbt, name, 0, NULL, (void *)&node);
+ if (result == ISC_R_SUCCESS) {
+ isc_time_t now;
+
+ CHECK(isc_time_now(&now));
+
+ /* Check if the record is still valid. */
+ if (isc_time_compare(&now, &node->valid_until) > 0) {
+ CHECK(dns_rbt_deletename(cache->rbt, name, ISC_FALSE));
+ in_cache = 0;
+ } else {
+ rdlist = node->rdatalist;
+ in_cache = 1;
+ }
+ } else if (result != ISC_R_NOTFOUND && result != DNS_R_PARTIALMATCH) {
+ goto cleanup;
+ }
+ CONTROLED_UNLOCK(&cache->mutex);
+
+ if (!in_cache) {
+ INIT_LIST(rdlist);
+ result = ldapdb_rdatalist_get(mctx, ldap_db, name, &rdlist);
+ if (result != ISC_R_SUCCESS && result != ISC_R_NOTFOUND)
+ goto cleanup;
+ CONTROLED_LOCK(&cache->mutex);
+ /* Check again to make sure. */
+ node = NULL;
+ result = dns_rbt_findname(cache->rbt, name, 0, NULL,
+ (void *)&node);
+ if (result == ISC_R_NOTFOUND || result == DNS_R_PARTIALMATCH) {
+ node = NULL;
+ CHECK(cache_node_create(cache, rdlist, &node));
+ CHECK(dns_rbt_addname(cache->rbt, name, (void *)node));
+ }
+ CONTROLED_UNLOCK(&cache->mutex);
+ }
+
+ CHECK(ldap_rdatalist_copy(mctx, rdlist, rdatalist));
+
+ if (EMPTY(*rdatalist))
+ result = ISC_R_NOTFOUND;
+
+cleanup:
+ CONTROLED_UNLOCK(&cache->mutex);
+ return result;
+}
+
+isc_result_t
+discard_from_cache(ldap_cache_t *cache, dns_name_t *name)
+{
+ isc_result_t result;
+
+ REQUIRE(cache != NULL);
+ REQUIRE(name != NULL);
+
+ if (cache->rbt == NULL) {
+ result = ISC_R_SUCCESS;
+ } else {
+ LOCK(&cache->mutex);
+ result = dns_rbt_deletename(cache->rbt, name, ISC_FALSE);
+ UNLOCK(&cache->mutex);
+ }
+
+ if (result == ISC_R_NOTFOUND)
+ result = ISC_R_SUCCESS;
+
+ return result;
+}
diff --git a/src/cache.h b/src/cache.h
new file mode 100644
index 0000000..a8f0558
--- /dev/null
+++ b/src/cache.h
@@ -0,0 +1,58 @@
+/* Authors: Martin Nagy <mnagy@redhat.com>
+ *
+ * Copyright (C) 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
+ */
+
+#ifndef _LD_CACHE_H_
+#define _LD_CACHE_H_
+
+#include "ldap_helper.h"
+
+typedef struct ldap_cache ldap_cache_t;
+
+/*
+ * Create a new cache.
+ */
+isc_result_t
+new_ldap_cache(isc_mem_t *mctx, ldap_cache_t **cachep,
+ const char * const *argv);
+
+/*
+ * Free all resources used up by the cache.
+ */
+void
+destroy_ldap_cache(ldap_cache_t **cachep);
+
+
+/*
+ * If caching is enabled, lookup 'name' in 'cache'. If the record is found and
+ * is not expired, make a copy and return it. If the record is not found or is
+ * expired, look it up in LDAP and cache it.
+ */
+isc_result_t
+cached_ldap_rdatalist_get(isc_mem_t *mctx, ldap_cache_t *cache,
+ ldap_db_t *ldap_db, dns_name_t *name,
+ ldapdb_rdatalist_t *rdatalist);
+
+/*
+ * Discard 'name' from the cache. If caching is not really turned on or 'name'
+ * is not cached, this function will still return ISC_R_SUCCESS.
+ */
+isc_result_t
+discard_from_cache(ldap_cache_t *cache, dns_name_t *name);
+
+#endif /* !_LD_CACHE_H_ */
diff --git a/src/ldap_convert.c b/src/ldap_convert.c
new file mode 100644
index 0000000..24da32f
--- /dev/null
+++ b/src/ldap_convert.c
@@ -0,0 +1,278 @@
+/* Authors: Martin Nagy <mnagy@redhat.com>
+ *
+ * Copyright (C) 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/util.h>
+
+#include <dns/name.h>
+#include <dns/rdatatype.h>
+#include <dns/result.h>
+#include <dns/types.h>
+
+#define LDAP_DEPRECATED 1
+#include <ldap.h>
+
+#include <errno.h>
+#include <strings.h>
+
+#include "str.h"
+#include "ldap_convert.h"
+#include "ldap_helper.h"
+#include "log.h"
+#include "util.h"
+
+/*
+ * Consistency must be preserved in these tables.
+ * ldap_dns_records[i] must always corespond to dns_records[i]
+ */
+const char *ldap_dns_records[] = {
+ "ARecord", "AAAARecord", "A6Record", "NSRecord",
+ "CNAMERecord", "PTRRecord", "SRVRecord", "TXTRecord", "MXRecord",
+ "MDRecord", "HINFORecord", "MINFORecord", "AFSDBRecord", "SIGRecord",
+ "KEYRecord", "LOCRecord", "NXTRecord", "NAPTRRecord", "KXRecord",
+ "CERTRecord", "DNAMERecord", "DSRecord", "SSHFPRecord",
+ "RRSIGRecord", "NSECRecord", NULL
+};
+
+const char *dns_records[] = {
+ "A", "AAAA", "A6", "NS",
+ "CNAME", "PTR", "SRV", "TXT", "MX",
+ "MD", "HINFO", "MINFO", "AFSDB", "SIG",
+ "KEY", "LOC", "NXT", "NAPTR", "KX",
+ "CERT", "DNAME", "DS", "SSHFP",
+ "RRSIG", "NSEC", NULL
+};
+
+static isc_result_t dn_to_text(const char *dn, ld_string_t *target);
+static isc_result_t explode_dn(const char *dn, char ***explodedp, int notypes);
+static isc_result_t explode_rdn(const char *rdn, char ***explodedp,
+ int notypes);
+
+
+isc_result_t
+dn_to_dnsname(isc_mem_t *mctx, const char *dn, dns_name_t *target)
+{
+ isc_result_t result;
+ DECLARE_BUFFERED_NAME(name);
+ ld_string_t *str = NULL;
+ isc_buffer_t buffer;
+
+ REQUIRE(mctx != NULL);
+ REQUIRE(dn != NULL);
+
+ INIT_BUFFERED_NAME(name);
+ CHECK(str_new(mctx, &str));
+
+ CHECK(dn_to_text(dn, str));
+ str_to_isc_buffer(str, &buffer);
+ CHECK(dns_name_fromtext(&name, &buffer, dns_rootname, 0, NULL));
+
+cleanup:
+ if (result != ISC_R_FAILURE)
+ result = dns_name_dupwithoffsets(&name, mctx, target);
+
+ str_destroy(&str);
+
+ return result;
+}
+
+/*
+ * Convert LDAP dn to DNS name.
+ *
+ * Example:
+ * dn = "idnsName=foo, idnsName=bar, idnsName=example.org, cn=dns,"
+ * "dc=example, dc=org"
+ *
+ * The resulting string will be "foo.bar.example.org."
+ */
+static isc_result_t
+dn_to_text(const char *dn, ld_string_t *target)
+{
+ isc_result_t result;
+ char **exploded_dn = NULL;
+ char **exploded_rdn = NULL;
+
+ REQUIRE(dn != NULL);
+ REQUIRE(target != NULL);
+
+ result = ISC_R_SUCCESS;
+
+ CHECK(explode_dn(dn, &exploded_dn, 0));
+ str_clear(target);
+ for (unsigned int i = 0; exploded_dn[i] != NULL; i++) {
+ if (strncasecmp(exploded_dn[i], "idnsName", 8) != 0)
+ break;
+ CHECK(explode_rdn(exploded_dn[i], &exploded_rdn, 1));
+ CHECK(str_cat_char(target, exploded_rdn[0]));
+ CHECK(str_cat_char(target, "."));
+
+ ldap_value_free(exploded_rdn);
+ exploded_rdn = NULL;
+ }
+
+ if (str_len(target) == 0)
+ CHECK(str_init_char(target, "."));
+
+cleanup:
+ if (exploded_dn != NULL)
+ ldap_value_free(exploded_dn);
+ if (exploded_rdn != NULL)
+ ldap_value_free(exploded_rdn);
+
+ log_error("converted: %s", str_buf(target));
+ return result;
+}
+
+static isc_result_t
+explode_dn(const char *dn, char ***explodedp, int notypes)
+{
+ char **exploded;
+
+ REQUIRE(dn != NULL);
+ REQUIRE(explodedp != NULL && *explodedp == NULL);
+
+ exploded = ldap_explode_dn(dn, notypes);
+ if (exploded == NULL) {
+ if (errno == ENOMEM) {
+ return ISC_R_NOMEMORY;
+ } else {
+ log_error("ldap_explode_dn(\"%s\") failed, "
+ "error code %d", dn, errno);
+ return ISC_R_FAILURE;
+ }
+ }
+
+ *explodedp = exploded;
+
+ return ISC_R_SUCCESS;
+}
+
+static isc_result_t
+explode_rdn(const char *rdn, char ***explodedp, int notypes)
+{
+ char **exploded;
+
+ REQUIRE(rdn != NULL);
+ REQUIRE(explodedp != NULL && *explodedp == NULL);
+
+ exploded = ldap_explode_rdn(rdn, notypes);
+ if (exploded == NULL) {
+ if (errno == ENOMEM) {
+ return ISC_R_NOMEMORY;
+ } else {
+ log_error("ldap_explode_rdn(\"%s\") failed, "
+ "error code %d", rdn, errno);
+ return ISC_R_FAILURE;
+ }
+ }
+
+ *explodedp = exploded;
+
+ return ISC_R_SUCCESS;
+}
+
+isc_result_t
+dnsname_to_dn(ldap_db_t *ldap_db, dns_name_t *name, ld_string_t *target)
+{
+ isc_result_t result;
+ int label_count;
+ const char *zone_dn = NULL;
+
+ REQUIRE(ldap_db != NULL);
+ REQUIRE(name != NULL);
+ REQUIRE(target != NULL);
+
+ /* Find the DN of the zone we belong to. */
+ {
+ DECLARE_BUFFERED_NAME(zone);
+ int dummy;
+ unsigned int common_labels;
+
+ INIT_BUFFERED_NAME(zone);
+
+ CHECK(get_zone_dn(ldap_db, name, &zone_dn, &zone));
+
+ dns_name_fullcompare(name, &zone, &dummy, &common_labels);
+ label_count = dns_name_countlabels(name) - common_labels;
+ }
+
+ str_clear(target);
+ if (label_count > 0) {
+ DECLARE_BUFFER(buffer, DNS_NAME_MAXTEXT);
+ dns_name_t labels;
+
+ INIT_BUFFER(buffer);
+ dns_name_init(&labels, NULL);
+
+ dns_name_getlabelsequence(name, 0, label_count, &labels);
+ CHECK(dns_name_totext(&labels, ISC_TRUE, &buffer));
+
+ CHECK(str_cat_char(target, "idnsName="));
+ CHECK(str_cat_isc_buffer(target, &buffer));
+ CHECK(str_cat_char(target, ", "));
+ }
+ CHECK(str_cat_char(target, zone_dn));
+
+cleanup:
+ return result;
+}
+
+isc_result_t
+ldap_record_to_rdatatype(const char *ldap_record, dns_rdatatype_t *rdtype)
+{
+ isc_result_t result;
+ unsigned i;
+ isc_consttextregion_t region;
+
+ for (i = 0; ldap_dns_records[i] != NULL; i++) {
+ if (!strcasecmp(ldap_record, ldap_dns_records[i]))
+ break;
+ }
+ if (dns_records[i] == NULL)
+ return ISC_R_NOTFOUND;
+
+ region.base = dns_records[i];
+ region.length = strlen(region.base);
+ result = dns_rdatatype_fromtext(rdtype, (isc_textregion_t *)&region);
+ if (result != ISC_R_SUCCESS) {
+ log_error("dns_rdatatype_fromtext() failed");
+ }
+
+ return result;
+}
+
+isc_result_t
+rdatatype_to_ldap_attribute(dns_rdatatype_t rdtype, const char **target)
+{
+ unsigned i;
+ char rdtype_str[DNS_RDATATYPE_FORMATSIZE];
+
+ dns_rdatatype_format(rdtype, rdtype_str, DNS_RDATATYPE_FORMATSIZE);
+ for (i = 0; dns_records[i] != NULL; i++) {
+ if (!strcmp(rdtype_str, dns_records[i]))
+ break;
+ }
+ if (ldap_dns_records[i] == NULL)
+ return ISC_R_NOTFOUND;
+
+ *target = ldap_dns_records[i];
+
+ return ISC_R_SUCCESS;
+}
diff --git a/src/ldap_convert.h b/src/ldap_convert.h
new file mode 100644
index 0000000..d35144a
--- /dev/null
+++ b/src/ldap_convert.h
@@ -0,0 +1,45 @@
+/* Authors: Martin Nagy <mnagy@redhat.com>
+ *
+ * Copyright (C) 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
+ */
+
+#ifndef _LD_LDAP_CONVERT_H_
+#define _LD_LDAP_CONVERT_H_
+
+#include <dns/types.h>
+
+#include "str.h"
+#include "ldap_helper.h"
+
+/*
+ * Convert LDAP DN 'dn', to dns_name_t 'target'. 'target' needs to be
+ * initialized with dns_name_init() before the call and freed by the caller
+ * after it using dns_name_free().
+ */
+isc_result_t dn_to_dnsname(isc_mem_t *mctx, const char *dn,
+ dns_name_t *target);
+
+isc_result_t dnsname_to_dn(ldap_db_t *ldap_db, dns_name_t *name,
+ ld_string_t *target);
+
+isc_result_t ldap_record_to_rdatatype(const char *ldap_record,
+ dns_rdatatype_t *rdtype);
+
+isc_result_t rdatatype_to_ldap_attribute(dns_rdatatype_t rdtype,
+ const char **target);
+
+#endif /* !_LD_LDAP_CONVERT_H_ */
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();
+}
diff --git a/src/ldap_helper.c b/src/ldap_helper.c
new file mode 100644
index 0000000..427191a
--- /dev/null
+++ b/src/ldap_helper.c
@@ -0,0 +1,1707 @@
+/* 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 <dns/rbt.h>
+#include <dns/rdata.h>
+#include <dns/rdataclass.h>
+#include <dns/rdatalist.h>
+#include <dns/rdatatype.h>
+#include <dns/result.h>
+#include <dns/ttl.h>
+#include <dns/view.h>
+#include <dns/zone.h>
+
+#include <isc/buffer.h>
+#include <isc/lex.h>
+#include <isc/mem.h>
+#include <isc/mutex.h>
+#include <isc/region.h>
+#include <isc/rwlock.h>
+#include <isc/util.h>
+
+#define LDAP_DEPRECATED 1
+#include <ldap.h>
+#include <sasl/sasl.h>
+#include <stddef.h>
+#include <string.h>
+#include <strings.h>
+
+#include "ldap_convert.h"
+#include "ldap_helper.h"
+#include "log.h"
+#include "semaphore.h"
+#include "settings.h"
+#include "str.h"
+#include "util.h"
+
+
+/* Max type length definitions, from lib/dns/master.c */
+#define MINTSIZ (65535 - 12 - 1 - 2 - 2 - 4 - 2)
+#define TOKENSIZ (8*1024)
+
+#define LDAP_OPT_CHECK(r, ...) \
+ do { \
+ if ((r) != LDAP_OPT_SUCCESS) { \
+ log_error(__VA_ARGS__); \
+ goto cleanup; \
+ } \
+ } while (0)
+
+/*
+ * LDAP related typedefs and structs.
+ */
+
+typedef struct ldap_auth_pair ldap_auth_pair_t;
+typedef struct settings settings_t;
+typedef struct ldap_value ldap_value_t;
+typedef struct ldap_attribute ldap_attribute_t;
+typedef struct ldap_entry ldap_entry_t;
+typedef LIST(ldap_value_t) ldap_value_list_t;
+typedef LIST(ldap_attribute_t) ldap_attribute_list_t;
+typedef LIST(ldap_entry_t) ldap_entry_list_t;
+
+/* Authentication method. */
+typedef enum ldap_auth {
+ AUTH_INVALID = 0,
+ AUTH_NONE,
+ AUTH_SIMPLE,
+ AUTH_SASL,
+} ldap_auth_t;
+
+struct ldap_auth_pair {
+ enum ldap_auth value; /* Value actually passed to ldap_bind(). */
+ char *name; /* String representation used in configuration file */
+};
+
+/* These are typedefed in ldap_helper.h */
+struct ldap_db {
+ isc_mem_t *mctx;
+ dns_view_t *view;
+
+ /* List of LDAP connections. */
+ semaphore_t conn_semaphore;
+ LIST(ldap_instance_t) conn_list;
+
+ /* Our own list of zones. */
+ isc_rwlock_t zone_rwlock;
+ dns_rbt_t *zone_names;
+
+ /* Settings. */
+ ld_string_t *host;
+ ld_string_t *base;
+ unsigned int connections;
+ ldap_auth_t auth_method;
+ ld_string_t *bind_dn;
+ ld_string_t *password;
+ ld_string_t *sasl_mech;
+ ld_string_t *sasl_user;
+ ld_string_t *sasl_realm;
+};
+
+struct ldap_instance {
+ ldap_db_t *database;
+ isc_mutex_t lock;
+ LINK(ldap_instance_t) link;
+ ld_string_t *query_string;
+ ld_string_t *base;
+
+ LDAP *handle;
+ LDAPMessage *result;
+
+ /* Parsing. */
+ isc_lex_t *lex;
+ isc_buffer_t rdata_target;
+ unsigned char *rdata_target_mem;
+
+ /* Cache. */
+ ldap_entry_list_t ldap_entries;
+ isc_boolean_t cache_active;
+
+ /* Temporary stuff. */
+ LDAPMessage *entry;
+ BerElement *ber;
+ char *attribute;
+ char **values;
+ char *dn;
+};
+
+struct ldap_entry {
+ LDAPMessage *entry;
+ ldap_attribute_t *last_attr;
+ ldap_attribute_list_t attributes;
+ LINK(ldap_entry_t) link;
+};
+
+struct ldap_attribute {
+ char *name;
+ char **ldap_values;
+ ldap_value_t *last_value;
+ ldap_value_list_t values;
+ LINK(ldap_attribute_t) link;
+};
+
+struct ldap_value {
+ char *value;
+ LINK(ldap_value_t) link;
+};
+
+/*
+ * Constants.
+ */
+
+extern const char *ldapdb_impname;
+
+/* Supported authentication types. */
+const ldap_auth_pair_t supported_ldap_auth[] = {
+ { AUTH_NONE, "none" },
+ { AUTH_SIMPLE, "simple" },
+ { AUTH_SASL, "sasl" },
+ { AUTH_INVALID, NULL },
+};
+
+/*
+ * Forward declarations.
+ */
+
+/* TODO: reorganize this stuff & clean it up. */
+void string_deleter(void *arg1, void *arg2);
+static isc_result_t new_ldap_instance(ldap_db_t *ldap_db,
+ ldap_instance_t **ldap_instp);
+static void destroy_ldap_instance(ldap_instance_t **ldap_instp);
+static isc_result_t add_or_modify_zone(ldap_db_t *ldap_db, const char *dn,
+ const char *db_name, dns_zonemgr_t *zmgr);
+
+static isc_result_t findrdatatype_or_create(isc_mem_t *mctx,
+ ldapdb_rdatalist_t *rdatalist, ldap_entry_t *entry,
+ dns_rdatatype_t rdtype, dns_rdatalist_t **rdlistp);
+static isc_result_t add_soa_record(isc_mem_t *mctx, ldap_instance_t *ldap_inst,
+ dns_name_t *name, ldap_entry_t *entry,
+ ldapdb_rdatalist_t *rdatalist);
+static dns_rdataclass_t get_rdataclass(ldap_entry_t *ldap_entry);
+static dns_ttl_t get_ttl(ldap_entry_t *ldap_entry);
+static isc_result_t get_values(const ldap_entry_t *entry,
+ const char *attr_name, ldap_value_list_t *values);
+static isc_result_t get_soa_record(ldap_entry_t *entry, ld_string_t *target);
+static ldap_attribute_t *get_next_attr(ldap_entry_t *entry,
+ const char **attr_list);
+static ldap_value_t *get_next_value(ldap_attribute_t *attr);
+static isc_boolean_t array_contains_nocase(const char **haystack,
+ const char *needle);
+static isc_result_t get_next_rdatatype(ldap_entry_t *entry,
+ ldap_attribute_t **attr, dns_rdatatype_t *rdtype);
+static isc_result_t get_next_rdatatext(ldap_attribute_t *attr,
+ ld_string_t *rdata_text);
+static isc_result_t parse_rdata(isc_mem_t *mctx, ldap_instance_t *ldap_inst,
+ dns_rdataclass_t rdclass, dns_rdatatype_t rdtype,
+ dns_name_t *origin, const char *rdata_text,
+ dns_rdata_t **rdatap);
+
+static isc_result_t cache_query_results(ldap_instance_t *inst);
+static isc_result_t fill_ldap_entry(ldap_instance_t *inst,
+ ldap_entry_t *ldap_entry);
+static isc_result_t fill_ldap_attribute(ldap_instance_t *inst,
+ ldap_attribute_t *ldap_attr);
+static void free_query_cache(ldap_instance_t *inst);
+static void free_ldap_attributes(isc_mem_t *mctx, ldap_entry_t *entry);
+static void free_ldap_values(isc_mem_t *mctx, ldap_attribute_t *attr);
+
+
+static const LDAPMessage *next_entry(ldap_instance_t *inst);
+static const char *get_dn(ldap_instance_t *inst);
+
+static ldap_instance_t * get_connection(ldap_db_t *ldap_db);
+static void put_connection(ldap_instance_t *ldap_inst);
+static isc_result_t ldap_connect(ldap_instance_t *ldap_inst);
+static isc_result_t ldap_query(ldap_instance_t *ldap_inst, const char *base,
+ int scope, char **attrs, int attrsonly, const char *filter, ...);
+
+/* Functions for writing to LDAP. */
+static isc_result_t ldap_modify_do(ldap_instance_t *ldap_inst, const char *dn,
+ LDAPMod **mods);
+static isc_result_t ldap_rdatalist_to_ldapmod(isc_mem_t *mctx,
+ dns_rdatalist_t *rdlist, LDAPMod **changep, int mod_op);
+static void free_ldapmod(isc_mem_t *mctx, LDAPMod **changep);
+static void free_char_array(isc_mem_t *mctx, char ***valsp);
+static isc_result_t modify_ldap_common(dns_name_t *owner, ldap_db_t *ldap_db,
+ dns_rdatalist_t *rdlist, int mod_op);
+
+isc_result_t
+new_ldap_db(isc_mem_t *mctx, dns_view_t *view, ldap_db_t **ldap_dbp,
+ const char * const *argv)
+{
+ unsigned int i;
+ isc_result_t result;
+ ldap_db_t *ldap_db;
+ ldap_instance_t *ldap_inst;
+ ld_string_t *auth_method_str = NULL;
+ setting_t ldap_settings[] = {
+ { "host", no_default_string },
+ { "connections", default_uint(1) },
+ { "base", no_default_string },
+ { "auth_method", default_string("none") },
+ { "bind_dn", default_string("") },
+ { "password", default_string("") },
+ { "sasl_mech", default_string("ANONYMOUS") },
+ { "sasl_user", default_string("") },
+ { "sasl_realm", default_string("") },
+ end_of_settings
+ };
+
+ REQUIRE(mctx != NULL);
+ REQUIRE(view != NULL);
+ REQUIRE(ldap_dbp != NULL && *ldap_dbp == NULL);
+
+ ldap_db = isc_mem_get(mctx, sizeof(ldap_db_t));
+ if (ldap_db == NULL)
+ return ISC_R_NOMEMORY;
+
+ ZERO_PTR(ldap_db);
+
+ isc_mem_attach(mctx, &ldap_db->mctx);
+ ldap_db->view = view;
+ /* commented out for now, cause named to hang */
+ //dns_view_attach(view, &ldap_db->view);
+
+ INIT_LIST(ldap_db->conn_list);
+
+ CHECK(isc_rwlock_init(&ldap_db->zone_rwlock, 0, 0));
+ CHECK(dns_rbt_create(mctx, string_deleter, mctx, &ldap_db->zone_names));
+
+ CHECK(str_new(mctx, &auth_method_str));
+ CHECK(str_new(mctx, &ldap_db->host));
+ CHECK(str_new(mctx, &ldap_db->base));
+ CHECK(str_new(mctx, &ldap_db->bind_dn));
+ CHECK(str_new(mctx, &ldap_db->password));
+ CHECK(str_new(mctx, &ldap_db->sasl_mech));
+ CHECK(str_new(mctx, &ldap_db->sasl_user));
+ CHECK(str_new(mctx, &ldap_db->sasl_realm));
+
+ ldap_settings[0].target = ldap_db->host;
+ ldap_settings[1].target = &ldap_db->connections;
+ ldap_settings[2].target = ldap_db->base;
+ ldap_settings[3].target = auth_method_str;
+ ldap_settings[4].target = ldap_db->bind_dn;
+ ldap_settings[5].target = ldap_db->password;
+ ldap_settings[6].target = ldap_db->sasl_mech;
+ ldap_settings[7].target = ldap_db->sasl_user;
+ ldap_settings[8].target = ldap_db->sasl_realm;
+
+ CHECK(set_settings(ldap_settings, argv));
+
+ /* Validate and check settings. */
+ str_toupper(ldap_db->sasl_mech);
+ if (ldap_db->connections < 1) {
+ log_error("at least one connection is required");
+ result = ISC_R_FAILURE;
+ goto cleanup;
+ }
+ /* Select authentication method. */
+ ldap_db->auth_method = AUTH_INVALID;
+ for (i = 0; supported_ldap_auth[i].name != NULL; i++) {
+ if (!str_casecmp_char(auth_method_str,
+ supported_ldap_auth[i].name)) {
+ ldap_db->auth_method = supported_ldap_auth[i].value;
+ break;
+ }
+ }
+ if (ldap_db->auth_method == AUTH_INVALID) {
+ log_error("unknown authentication method '%s'",
+ str_buf(auth_method_str));
+ result = ISC_R_FAILURE;
+ goto cleanup;
+ }
+
+ CHECK(semaphore_init(&ldap_db->conn_semaphore, ldap_db->connections));
+
+ for (i = 0; i < ldap_db->connections; i++) {
+ ldap_inst = NULL;
+ CHECK(new_ldap_instance(ldap_db, &ldap_inst));
+ ldap_connect(ldap_inst);
+ APPEND(ldap_db->conn_list, ldap_inst, link);
+ }
+
+cleanup:
+ if (result != ISC_R_SUCCESS)
+ destroy_ldap_db(&ldap_db);
+ else
+ *ldap_dbp = ldap_db;
+
+ str_destroy(&auth_method_str);
+
+ return result;
+}
+
+void
+destroy_ldap_db(ldap_db_t **ldap_dbp)
+{
+ ldap_db_t *ldap_db;
+ ldap_instance_t *elem;
+ ldap_instance_t *next;
+
+ REQUIRE(ldap_dbp != NULL && *ldap_dbp != NULL);
+
+ ldap_db = *ldap_dbp;
+
+ elem = HEAD(ldap_db->conn_list);
+ while (elem != NULL) {
+ next = NEXT(elem, link);
+ UNLINK(ldap_db->conn_list, elem, link);
+ destroy_ldap_instance(&elem);
+ elem = next;
+ }
+
+ str_destroy(&ldap_db->host);
+ str_destroy(&ldap_db->base);
+ str_destroy(&ldap_db->bind_dn);
+ str_destroy(&ldap_db->password);
+ str_destroy(&ldap_db->sasl_mech);
+ str_destroy(&ldap_db->sasl_user);
+ str_destroy(&ldap_db->sasl_realm);
+
+ semaphore_destroy(&ldap_db->conn_semaphore);
+ /* commented out for now, causes named to hang */
+ //dns_view_detach(&ldap_db->view);
+
+ dns_rbt_destroy(&ldap_db->zone_names);
+ isc_rwlock_destroy(&ldap_db->zone_rwlock);
+
+ isc_mem_putanddetach(&ldap_db->mctx, ldap_db, sizeof(ldap_db_t));
+
+ *ldap_dbp = NULL;
+}
+
+static isc_result_t
+new_ldap_instance(ldap_db_t *ldap_db, ldap_instance_t **ldap_instp)
+{
+ isc_result_t result;
+ ldap_instance_t *ldap_inst;
+
+ REQUIRE(ldap_db != NULL);
+ REQUIRE(ldap_instp != NULL && *ldap_instp == NULL);
+
+ ldap_inst = isc_mem_get(ldap_db->mctx, sizeof(ldap_instance_t));
+ if (ldap_inst == NULL)
+ return ISC_R_NOMEMORY;
+
+ ZERO_PTR(ldap_inst);
+
+ ldap_inst->database = ldap_db;
+ INIT_LINK(ldap_inst, link);
+ result = isc_mutex_init(&ldap_inst->lock);
+ if (result != ISC_R_SUCCESS) {
+ isc_mem_put(ldap_db->mctx, ldap_db, sizeof(ldap_instance_t));
+ return result;
+ }
+
+ CHECK(str_new(ldap_db->mctx, &ldap_inst->query_string));
+ CHECK(str_new(ldap_db->mctx, &ldap_inst->base));
+
+ CHECK(isc_lex_create(ldap_db->mctx, TOKENSIZ, &ldap_inst->lex));
+ CHECKED_MEM_GET(ldap_db->mctx, ldap_inst->rdata_target_mem, MINTSIZ);
+
+ *ldap_instp = ldap_inst;
+
+ return ISC_R_SUCCESS;
+
+cleanup:
+ destroy_ldap_instance(&ldap_inst);
+
+ return result;
+}
+
+static void
+destroy_ldap_instance(ldap_instance_t **ldap_instp)
+{
+ ldap_instance_t *ldap_inst;
+
+ REQUIRE(ldap_instp != NULL && *ldap_instp != NULL);
+
+ ldap_inst = *ldap_instp;
+ DESTROYLOCK(&ldap_inst->lock);
+ if (ldap_inst->handle != NULL)
+ ldap_unbind_ext_s(ldap_inst->handle, NULL, NULL);
+
+ str_destroy(&ldap_inst->query_string);
+ str_destroy(&ldap_inst->base);
+
+ if (ldap_inst->lex != NULL)
+ isc_lex_destroy(&ldap_inst->lex);
+ if (ldap_inst->rdata_target_mem != NULL) {
+ isc_mem_put(ldap_inst->database->mctx,
+ ldap_inst->rdata_target_mem, MINTSIZ);
+ }
+
+ isc_mem_put(ldap_inst->database->mctx, *ldap_instp, sizeof(ldap_instance_t));
+ *ldap_instp = NULL;
+}
+
+/* TODO: Delete old zones. */
+isc_result_t
+refresh_zones_from_ldap(ldap_db_t *ldap_db, const char *name,
+ dns_zonemgr_t *zmgr)
+{
+ isc_result_t result = ISC_R_SUCCESS;
+ ldap_instance_t *ldap_inst;
+ char *attrs[] = {
+ "idnsName", NULL
+ };
+
+ REQUIRE(ldap_db != NULL);
+ REQUIRE(name != NULL);
+
+ log_debug(2, "refreshing list of zones");
+
+ ldap_inst = get_connection(ldap_db);
+
+ ldap_query(ldap_inst, str_buf(ldap_db->base), LDAP_SCOPE_SUBTREE,
+ attrs, 0, "(objectClass=idnsZone)");
+
+ while (next_entry(ldap_inst))
+ CHECK(add_or_modify_zone(ldap_db, get_dn(ldap_inst), name, zmgr));
+
+cleanup:
+ put_connection(ldap_inst);
+
+ log_debug(2, "finished refreshing list of zones");
+
+ return result;
+}
+
+static const char *
+get_dn(ldap_instance_t *inst)
+{
+ if (inst->dn) {
+ ldap_memfree(inst->dn);
+ inst->dn = NULL;
+ }
+
+ if (inst->handle && inst->entry)
+ inst->dn = ldap_get_dn(inst->handle, inst->entry);
+
+ return inst->dn;
+
+}
+
+
+void
+string_deleter(void *arg1, void *arg2)
+{
+ char *string = (char *)arg1;
+ isc_mem_t *mctx = (isc_mem_t *)arg2;
+
+ REQUIRE(string != NULL);
+ REQUIRE(mctx != NULL);
+
+ isc_mem_free(mctx, string);
+}
+
+isc_result_t
+get_zone_dn(ldap_db_t *ldap_db, dns_name_t *name, const char **dn,
+ dns_name_t *matched_name)
+{
+ isc_result_t result;
+ dns_rbt_t *rbt;
+ void *data = NULL;
+
+ REQUIRE(ldap_db != NULL);
+ REQUIRE(name != NULL);
+ REQUIRE(dn != NULL && *dn == NULL);
+ REQUIRE(matched_name != NULL);
+
+ RWLOCK(&ldap_db->zone_rwlock, isc_rwlocktype_read);
+ rbt = ldap_db->zone_names;
+
+ result = dns_rbt_findname(rbt, name, 0, matched_name, &data);
+ if (result == DNS_R_PARTIALMATCH)
+ result = ISC_R_SUCCESS;
+ if (result == ISC_R_SUCCESS) {
+ INSIST(data != NULL);
+ *dn = data;
+ }
+
+ RWUNLOCK(&ldap_db->zone_rwlock, isc_rwlocktype_read);
+
+ return result;
+}
+
+static isc_result_t
+add_zone_dn(ldap_db_t *ldap_db, dns_name_t *name, const char *dn)
+{
+ isc_result_t result;
+ dns_rbt_t *rbt;
+ void *data = NULL;
+ char *new_dn = NULL;
+
+ REQUIRE(ldap_db != NULL);
+ REQUIRE(name != NULL);
+ REQUIRE(dn != NULL);
+
+ RWLOCK(&ldap_db->zone_rwlock, isc_rwlocktype_write);
+ rbt = ldap_db->zone_names;
+
+ CHECKED_MEM_STRDUP(ldap_db->mctx, dn, new_dn);
+
+ /* First make sure the node doesn't exist. */
+ result = dns_rbt_findname(rbt, name, 0, NULL, &data);
+ if (result == ISC_R_SUCCESS)
+ CHECK(dns_rbt_deletename(rbt, name, ISC_FALSE));
+ else if (result != ISC_R_NOTFOUND && result != DNS_R_PARTIALMATCH)
+ goto cleanup;
+
+ /* Now add it. */
+ CHECK(dns_rbt_addname(rbt, name, (void *)new_dn));
+
+ RWUNLOCK(&ldap_db->zone_rwlock, isc_rwlocktype_write);
+
+ return ISC_R_SUCCESS;
+
+cleanup:
+ RWUNLOCK(&ldap_db->zone_rwlock, isc_rwlocktype_write);
+
+ if (new_dn)
+ isc_mem_free(ldap_db->mctx, new_dn);
+
+ return result;
+}
+
+/* FIXME: Better error handling. */
+static isc_result_t
+add_or_modify_zone(ldap_db_t *ldap_db, const char *dn, const char *db_name,
+ dns_zonemgr_t *zmgr)
+{
+ isc_result_t result;
+ dns_zone_t *zone;
+ dns_name_t name;
+ dns_acl_t *updateacl = NULL;
+ const char *argv[2];
+
+ REQUIRE(ldap_db != NULL);
+ REQUIRE(dn != NULL);
+ REQUIRE(db_name != NULL);
+
+ argv[0] = ldapdb_impname;
+ argv[1] = db_name;
+
+ zone = NULL;
+ dns_name_init(&name, NULL);
+
+ CHECK(dn_to_dnsname(ldap_db->mctx, dn, &name));
+
+ /* If the zone doesn't exist, create it. */
+ result = dns_view_findzone(ldap_db->view, &name, &zone);
+ if (result == ISC_R_NOTFOUND) {
+ CHECK(dns_zone_create(&zone, ldap_db->mctx));
+ dns_zone_setview(zone, ldap_db->view);
+ CHECK(dns_zone_setorigin(zone, &name));
+ dns_zone_setclass(zone, dns_rdataclass_in);
+ dns_zone_settype(zone, dns_zone_master);
+ CHECK(dns_zone_setdbtype(zone, 2, argv));
+
+ /* XXX Temporary set update ACLs to any */
+ CHECK(dns_acl_any(ldap_db->mctx, &updateacl));
+ dns_zone_setupdateacl(zone, updateacl);
+ dns_acl_detach(&updateacl);
+
+ CHECK(dns_zonemgr_managezone(zmgr, zone));
+ CHECK(dns_view_addzone(ldap_db->view, zone));
+ CHECK(add_zone_dn(ldap_db, &name, dn));
+ } else if (result != ISC_R_SUCCESS) {
+ goto cleanup;
+ }
+
+ /*
+ * ACLs:
+ * dns_zone_setqueryacl()
+ * dns_zone_setqueryonacl()
+ * dns_zone_setupdateacl()
+ * dns_zone_setforwardacl()
+ * dns_zone_setxfracl()
+ */
+
+ /*
+ * maybe?
+ * dns_zone_setnotifytype()
+ * dns_zone_setalsonotify()
+ */
+
+cleanup:
+ if (dns_name_dynamic(&name))
+ dns_name_free(&name, ldap_db->mctx);
+ if (zone != NULL)
+ dns_zone_detach(&zone);
+
+ return result;
+}
+
+static isc_result_t
+findrdatatype_or_create(isc_mem_t *mctx, ldapdb_rdatalist_t *rdatalist,
+ ldap_entry_t *entry, dns_rdatatype_t rdtype,
+ dns_rdatalist_t **rdlistp)
+{
+ isc_result_t result;
+ dns_rdatalist_t *rdlist = NULL;
+ dns_ttl_t ttl;
+
+ REQUIRE(rdatalist != NULL);
+ REQUIRE(entry != NULL);
+ REQUIRE(rdlistp != NULL);
+
+ *rdlistp = NULL;
+
+ ttl = get_ttl(entry);
+
+ result = ldapdb_rdatalist_findrdatatype(rdatalist, rdtype, &rdlist);
+ if (result != ISC_R_SUCCESS) {
+ CHECKED_MEM_GET_PTR(mctx, rdlist);
+
+ dns_rdatalist_init(rdlist);
+ rdlist->rdclass = get_rdataclass(entry);
+ rdlist->type = rdtype;
+ rdlist->ttl = ttl;
+ APPEND(*rdatalist, rdlist, link);
+ result = ISC_R_SUCCESS;
+ } else {
+ /*
+ * No support for different TTLs yet.
+ */
+ if (rdlist->ttl != ttl)
+ result = ISC_R_FAILURE;
+ }
+
+ *rdlistp = rdlist;
+ return ISC_R_SUCCESS;
+
+cleanup:
+ SAFE_MEM_PUT_PTR(mctx, rdlist);
+
+ return result;
+}
+
+/*
+ * ldapdb_rdatalist_t related functions.
+ */
+isc_result_t
+ldapdb_rdatalist_findrdatatype(ldapdb_rdatalist_t *rdatalist,
+ dns_rdatatype_t rdtype,
+ dns_rdatalist_t **rdlistp)
+{
+ dns_rdatalist_t *rdlist;
+
+ REQUIRE(rdatalist != NULL);
+ REQUIRE(rdlistp != NULL && *rdlistp == NULL);
+
+ rdlist = HEAD(*rdatalist);
+ while (rdlist != NULL && rdlist->type != rdtype) {
+ rdlist = NEXT(rdlist, link);
+ }
+
+ *rdlistp = rdlist;
+
+ return (rdlist == NULL) ? ISC_R_NOTFOUND : ISC_R_SUCCESS;
+}
+
+void
+ldapdb_rdatalist_destroy(isc_mem_t *mctx, ldapdb_rdatalist_t *rdatalist)
+{
+ dns_rdatalist_t *rdlist;
+
+ REQUIRE(rdatalist != NULL);
+
+ while (!EMPTY(*rdatalist)) {
+ rdlist = HEAD(*rdatalist);
+ free_rdatalist(mctx, rdlist);
+ UNLINK(*rdatalist, rdlist, link);
+ isc_mem_put(mctx, rdlist, sizeof(*rdlist));
+ }
+}
+
+void
+free_rdatalist(isc_mem_t *mctx, dns_rdatalist_t *rdlist)
+{
+ dns_rdata_t *rdata;
+ isc_region_t r;
+
+ REQUIRE(rdlist != NULL);
+
+ while (!EMPTY(rdlist->rdata)) {
+ rdata = HEAD(rdlist->rdata);
+ UNLINK(rdlist->rdata, rdata, link);
+ dns_rdata_toregion(rdata, &r);
+ isc_mem_put(mctx, r.base, r.length);
+ isc_mem_put(mctx, rdata, sizeof(*rdata));
+ }
+}
+
+isc_result_t
+ldapdb_rdatalist_get(isc_mem_t *mctx, ldap_db_t *ldap_db, dns_name_t *name,
+ ldapdb_rdatalist_t *rdatalist)
+{
+ isc_result_t result;
+ ldap_instance_t *ldap_inst;
+ ldap_entry_t *entry;
+ ldap_attribute_t *attr;
+ ld_string_t *string = NULL;
+
+ dns_rdataclass_t rdclass;
+ dns_ttl_t ttl;
+ dns_rdatatype_t rdtype;
+ dns_rdata_t *rdata = NULL;
+ dns_rdatalist_t *rdlist = NULL;
+
+ REQUIRE(mctx != NULL);
+ REQUIRE(ldap_db != NULL);
+ REQUIRE(name != NULL);
+ REQUIRE(rdatalist != NULL);
+
+ ldap_inst = get_connection(ldap_db);
+
+ INIT_LIST(*rdatalist);
+ CHECK(str_new(mctx, &string));
+ CHECK(dnsname_to_dn(ldap_db, name, string));
+
+ CHECK(ldap_query(ldap_inst, str_buf(string), LDAP_SCOPE_BASE, NULL, 0,
+ "(objectClass=idnsRecord)"));
+ CHECK(cache_query_results(ldap_inst));
+
+ if (EMPTY(ldap_inst->ldap_entries)) {
+ result = ISC_R_NOTFOUND;
+ goto cleanup;
+ }
+
+ for (entry = HEAD(ldap_inst->ldap_entries);
+ entry != NULL;
+ entry = NEXT(entry, link)) {
+
+ result = add_soa_record(mctx, ldap_inst, name, entry,
+ rdatalist);
+ if (result != ISC_R_SUCCESS && result != ISC_R_NOTFOUND)
+ goto cleanup;
+
+ rdclass = get_rdataclass(entry);
+ ttl = get_ttl(entry);
+
+ for (result = get_next_rdatatype(entry, &attr, &rdtype);
+ result == ISC_R_SUCCESS;
+ result = get_next_rdatatype(entry, &attr, &rdtype)) {
+
+ CHECK(findrdatatype_or_create(mctx, rdatalist, entry,
+ rdtype, &rdlist));
+ for (result = get_next_rdatatext(attr, string);
+ result == ISC_R_SUCCESS;
+ result = get_next_rdatatext(attr, string)) {
+ CHECK(parse_rdata(mctx, ldap_inst, rdclass,
+ rdtype, name, str_buf(string),
+ &rdata));
+ APPEND(rdlist->rdata, rdata, link);
+ rdata = NULL;
+ }
+ rdlist = NULL;
+ }
+ }
+
+ result = ISC_R_SUCCESS;
+
+cleanup:
+ put_connection(ldap_inst);
+ str_destroy(&string);
+
+ if (result != ISC_R_SUCCESS)
+ ldapdb_rdatalist_destroy(mctx, rdatalist);
+
+ return result;
+}
+
+static dns_rdataclass_t
+get_rdataclass(ldap_entry_t *ldap_entry)
+{
+ UNUSED(ldap_entry);
+
+ /*
+ * Not implemented for now.
+ * Probably won't ever be.
+ */
+
+ return dns_rdataclass_in;
+}
+
+static dns_ttl_t
+get_ttl(ldap_entry_t *ldap_entry)
+{
+ UNUSED(ldap_entry);
+
+ /*
+ * TODO: Not implemented yet.
+ */
+#if 0
+ isc_textregion_t ttl_text;
+
+ ttl_text.base = "86400";
+ ttl_text.length = strlen(ttl_text.base);
+ result = dns_ttl_fromtext(&ttl_text, &ttl);
+ if (result != ISC_R_SUCCESS) {
+ seen_error = ISC_TRUE;
+ break;
+ }
+#endif
+
+ return 86400;
+}
+
+static isc_result_t
+get_soa_record(ldap_entry_t *entry, ld_string_t *target)
+{
+ isc_result_t result;
+ ldap_value_list_t values;
+
+ const char *soa_attrs[] = {
+ "idnsSOAmName", "idnsSOArName", "idnsSOAserial",
+ "idnsSOArefresh", "idnsSOAretry", "idnsSOAexpire",
+ "idnsSOAminimum", NULL
+ };
+
+ REQUIRE(entry != NULL);
+ REQUIRE(target != NULL);
+
+ str_clear(target);
+ for (unsigned i = 0; soa_attrs[i] != NULL; i++) {
+ CHECK(get_values(entry, soa_attrs[i], &values));
+ CHECK(str_cat_char(target, HEAD(values)->value));
+ CHECK(str_cat_char(target, " "));
+ }
+
+cleanup:
+ return result;
+}
+
+static isc_result_t
+add_soa_record(isc_mem_t *mctx, ldap_instance_t *ldap_inst, dns_name_t *name,
+ ldap_entry_t *entry, ldapdb_rdatalist_t *rdatalist)
+{
+ isc_result_t result;
+ ld_string_t *string = NULL;
+ dns_rdataclass_t rdclass;
+ dns_rdata_t *rdata = NULL;
+ dns_rdatalist_t *rdlist = NULL;
+
+ CHECK(str_new(mctx, &string));
+
+ CHECK(get_soa_record(entry, string));
+ rdclass = get_rdataclass(entry);
+
+ CHECK(get_soa_record(entry, string));
+ CHECK(parse_rdata(mctx, ldap_inst, rdclass, dns_rdatatype_soa, name,
+ str_buf(string), &rdata));
+
+ CHECK(findrdatatype_or_create(mctx, rdatalist, entry, dns_rdatatype_soa,
+ &rdlist));
+
+ APPEND(rdlist->rdata, rdata, link);
+
+cleanup:
+ str_destroy(&string);
+ if (result != ISC_R_SUCCESS)
+ SAFE_MEM_PUT_PTR(mctx, rdata);
+
+ return result;
+}
+
+static isc_result_t
+get_next_rdatatype(ldap_entry_t *entry, ldap_attribute_t **attrp,
+ dns_rdatatype_t *rdtype)
+{
+ isc_result_t result;
+ ldap_attribute_t *attr;
+
+ result = ISC_R_NOTFOUND;
+
+ for (attr = get_next_attr(entry, NULL);
+ attr != NULL;
+ attr = get_next_attr(entry, NULL)) {
+ result = ldap_record_to_rdatatype(attr->name, rdtype);
+ if (result == ISC_R_SUCCESS)
+ break;
+ }
+
+ if (result == ISC_R_SUCCESS)
+ *attrp = attr;
+ else if (result == ISC_R_NOTFOUND)
+ *attrp = NULL;
+
+ return result;
+}
+
+static isc_result_t
+get_next_rdatatext(ldap_attribute_t *attr, ld_string_t *rdata_text)
+{
+ ldap_value_t *value;
+
+ REQUIRE(attr != NULL);
+ REQUIRE(rdata_text != NULL);
+
+ str_clear(rdata_text);
+
+ value = get_next_value(attr);
+ if (value == NULL)
+ return ISC_R_NOTFOUND;
+
+ str_init_char(rdata_text, value->value);
+
+ return ISC_R_SUCCESS;
+}
+
+static isc_result_t
+parse_rdata(isc_mem_t *mctx, ldap_instance_t *ldap_inst,
+ dns_rdataclass_t rdclass, dns_rdatatype_t rdtype,
+ dns_name_t *origin, const char *rdata_text, dns_rdata_t **rdatap)
+{
+ isc_result_t result;
+ isc_consttextregion_t text;
+ isc_buffer_t lex_buffer;
+ isc_region_t rdatamem;
+ dns_rdata_t *rdata;
+
+ REQUIRE(mctx != NULL);
+ REQUIRE(ldap_inst != NULL);
+ REQUIRE(rdata_text != NULL);
+ REQUIRE(rdatap != NULL);
+
+ rdata = NULL;
+ rdatamem.base = NULL;
+
+ text.base = rdata_text;
+ text.length = strlen(text.base);
+
+ isc_buffer_init(&lex_buffer, text.base, text.length);
+ isc_buffer_add(&lex_buffer, text.length);
+ isc_buffer_setactive(&lex_buffer, text.length);
+
+ CHECK(isc_lex_openbuffer(ldap_inst->lex, &lex_buffer));
+
+ isc_buffer_init(&ldap_inst->rdata_target, ldap_inst->rdata_target_mem,
+ MINTSIZ);
+ CHECK(dns_rdata_fromtext(NULL, rdclass, rdtype, ldap_inst->lex, origin,
+ 0, mctx, &ldap_inst->rdata_target, NULL));
+
+ CHECKED_MEM_GET_PTR(mctx, rdata);
+ dns_rdata_init(rdata);
+
+ rdatamem.length = isc_buffer_usedlength(&ldap_inst->rdata_target);
+ CHECKED_MEM_GET(mctx, rdatamem.base, rdatamem.length);
+
+ memcpy(rdatamem.base, isc_buffer_base(&ldap_inst->rdata_target),
+ rdatamem.length);
+ dns_rdata_fromregion(rdata, rdclass, rdtype, &rdatamem);
+
+ isc_lex_close(ldap_inst->lex);
+
+ *rdatap = rdata;
+ return ISC_R_SUCCESS;
+
+cleanup:
+ isc_lex_close(ldap_inst->lex);
+ if (rdata != NULL)
+ isc_mem_put(mctx, rdata, sizeof(*rdata));
+ if (rdatamem.base != NULL)
+ isc_mem_put(mctx, rdatamem.base, rdatamem.length);
+
+ return result;
+}
+
+static ldap_attribute_t *
+get_next_attr(ldap_entry_t *entry, const char **attr_list)
+{
+ ldap_attribute_t *attr;
+
+ REQUIRE(entry != NULL);
+
+ if (entry->last_attr == NULL)
+ attr = HEAD(entry->attributes);
+ else
+ attr = NEXT(entry->last_attr, link);
+
+ if (attr_list != NULL) {
+ while (attr != NULL && !array_contains_nocase(attr_list, attr->name))
+ attr = NEXT(attr, link);
+ }
+
+ if (attr != NULL)
+ entry->last_attr = attr;
+
+ return attr;
+}
+
+static isc_result_t
+get_values(const ldap_entry_t *entry, const char *attr_name,
+ ldap_value_list_t *values)
+{
+ ldap_attribute_t *attr;
+
+ REQUIRE(entry != NULL);
+ REQUIRE(attr_name != NULL);
+ REQUIRE(values != NULL);
+
+ for (attr = HEAD(entry->attributes);
+ attr != NULL;
+ attr = NEXT(attr, link)) {
+ if (!strcasecmp(attr->name, attr_name)) {
+ *values = attr->values;
+ return ISC_R_SUCCESS;
+ }
+ }
+
+ return ISC_R_NOTFOUND;
+}
+
+static ldap_value_t *
+get_next_value(ldap_attribute_t *attr)
+{
+ ldap_value_t *value;
+
+ REQUIRE(attr != NULL);
+
+ if (attr->last_value == NULL)
+ value = HEAD(attr->values);
+ else
+ value = NEXT(attr->last_value, link);
+
+ if (value != NULL)
+ attr->last_value = value;
+
+ return value;
+}
+
+static isc_boolean_t
+array_contains_nocase(const char **haystack, const char *needle)
+{
+ for (unsigned int i = 0; haystack[i] != NULL; i++) {
+ if (strcasecmp(needle, haystack[i]) == 0)
+ return isc_boolean_true;
+ }
+
+ return isc_boolean_false;
+}
+
+static ldap_instance_t *
+get_connection(ldap_db_t *ldap_db)
+{
+ ldap_instance_t *ldap_inst;
+
+ REQUIRE(ldap_db != NULL);
+
+ semaphore_wait(&ldap_db->conn_semaphore);
+ ldap_inst = HEAD(ldap_db->conn_list);
+ while (ldap_inst != NULL) {
+ if (isc_mutex_trylock(&ldap_inst->lock) == ISC_R_SUCCESS)
+ break;
+ ldap_inst = NEXT(ldap_inst, link);
+ }
+
+ RUNTIME_CHECK(ldap_inst != NULL);
+
+ INIT_LIST(ldap_inst->ldap_entries);
+ /* TODO: find a clever way to not really require this */
+ str_copy(ldap_inst->base, ldap_db->base);
+
+ return ldap_inst;
+}
+
+static void
+put_connection(ldap_instance_t *ldap_inst)
+{
+ if (ldap_inst == NULL)
+ return;
+
+ if (ldap_inst->dn) {
+ ldap_memfree(ldap_inst->dn);
+ ldap_inst->dn = NULL;
+ }
+ if (ldap_inst->values) {
+ ldap_value_free(ldap_inst->values);
+ ldap_inst->values = NULL;
+ }
+ if (ldap_inst->attribute) {
+ ldap_memfree(ldap_inst->attribute);
+ ldap_inst->attribute = NULL;
+ }
+ if (ldap_inst->ber) {
+ ber_free(ldap_inst->ber, 0);
+ ldap_inst->ber = NULL;
+ }
+ if (ldap_inst->result) {
+ ldap_msgfree(ldap_inst->result);
+ ldap_inst->result = NULL;
+ }
+
+ free_query_cache(ldap_inst);
+
+ UNLOCK(&ldap_inst->lock);
+ semaphore_signal(&ldap_inst->database->conn_semaphore);
+}
+
+
+/* FIXME: Handle the case where the LDAP handle is NULL -> try to reconnect. */
+static isc_result_t
+ldap_query(ldap_instance_t *ldap_inst, const char *base, int scope, char **attrs,
+ int attrsonly, const char *filter, ...)
+{
+ va_list ap;
+ int ret;
+ const char *err_string;
+
+ REQUIRE(ldap_inst != NULL);
+
+ va_start(ap, filter);
+ str_vsprintf(ldap_inst->query_string, filter, ap);
+ va_end(ap);
+
+ log_debug(2, "querying '%s' with '%s'", base,
+ str_buf(ldap_inst->query_string));
+
+ if (ldap_inst->handle == NULL) {
+ err_string = "not connected";
+ goto cleanup;
+ }
+
+ ret = ldap_search_ext_s(ldap_inst->handle, base, scope,
+ str_buf(ldap_inst->query_string), attrs,
+ attrsonly, NULL, NULL, NULL, LDAP_NO_LIMIT,
+ &ldap_inst->result);
+
+ log_debug(2, "entry count: %d", ldap_count_entries(ldap_inst->handle,
+ ldap_inst->result));
+
+ return ISC_R_SUCCESS;
+
+cleanup:
+ log_error("error reading from ldap: %s", err_string);
+
+ return ISC_R_FAILURE;
+}
+
+static isc_result_t
+cache_query_results(ldap_instance_t *inst)
+{
+ isc_result_t result;
+ LDAP *ld;
+ LDAPMessage *res;
+ LDAPMessage *entry;
+ ldap_entry_t *ldap_entry;
+
+ REQUIRE(inst != NULL);
+ REQUIRE(EMPTY(inst->ldap_entries));
+ REQUIRE(inst->result != NULL);
+
+ INIT_LIST(inst->ldap_entries);
+
+ if (inst->cache_active)
+ free_query_cache(inst);
+
+ ld = inst->handle;
+ res = inst->result;
+
+ for (entry = ldap_first_entry(ld, res);
+ entry != NULL;
+ entry = ldap_next_entry(ld, entry)) {
+ CHECKED_MEM_GET_PTR(inst->database->mctx, ldap_entry);
+ ZERO_PTR(ldap_entry);
+
+ ldap_entry->entry = entry;
+ INIT_LIST(ldap_entry->attributes);
+ INIT_LINK(ldap_entry, link);
+ CHECK(fill_ldap_entry(inst, ldap_entry));
+
+ APPEND(inst->ldap_entries, ldap_entry, link);
+ }
+
+ return ISC_R_SUCCESS;
+
+cleanup:
+ free_query_cache(inst);
+
+ return result;
+}
+
+static isc_result_t
+fill_ldap_entry(ldap_instance_t *inst, ldap_entry_t *ldap_entry)
+{
+ isc_result_t result;
+ ldap_attribute_t *ldap_attr;
+ char *attribute;
+ BerElement *ber;
+ LDAPMessage *entry;
+
+ REQUIRE(inst != NULL);
+ REQUIRE(ldap_entry != NULL);
+
+ result = ISC_R_SUCCESS;
+ entry = ldap_entry->entry;
+
+ for (attribute = ldap_first_attribute(inst->handle, entry, &ber);
+ attribute != NULL;
+ attribute = ldap_next_attribute(inst->handle, entry, ber)) {
+ CHECKED_MEM_GET_PTR(inst->database->mctx, ldap_attr);
+ ZERO_PTR(ldap_attr);
+
+ ldap_attr->name = attribute;
+ INIT_LIST(ldap_attr->values);
+ INIT_LINK(ldap_attr, link);
+ CHECK(fill_ldap_attribute(inst, ldap_attr));
+
+ APPEND(ldap_entry->attributes, ldap_attr, link);
+ }
+
+ if (ber != NULL)
+ ber_free(ber, 0);
+
+cleanup:
+ if (result != ISC_R_SUCCESS) {
+ free_ldap_attributes(inst->database->mctx, ldap_entry);
+ }
+
+ return result;
+}
+
+static isc_result_t
+fill_ldap_attribute(ldap_instance_t *inst, ldap_attribute_t *ldap_attr)
+{
+ isc_result_t result;
+ char **values;
+ ldap_value_t *ldap_val;
+
+ REQUIRE(inst != NULL);
+ REQUIRE(ldap_attr != NULL);
+
+ values = ldap_get_values(inst->handle, inst->result, ldap_attr->name);
+ /* TODO: proper ldap error handling */
+ if (values == NULL)
+ return ISC_R_FAILURE;
+
+ ldap_attr->ldap_values = values;
+
+ for (unsigned int i = 0; values[i] != NULL; i++) {
+ CHECKED_MEM_GET_PTR(inst->database->mctx, ldap_val);
+ ldap_val->value = values[i];
+ INIT_LINK(ldap_val, link);
+
+ APPEND(ldap_attr->values, ldap_val, link);
+ }
+
+ return ISC_R_SUCCESS;
+
+cleanup:
+ free_ldap_values(inst->database->mctx, ldap_attr);
+ ldap_value_free(values);
+
+ return result;
+}
+
+static void
+free_query_cache(ldap_instance_t *inst)
+{
+ ldap_entry_t *entry, *next;
+
+ entry = HEAD(inst->ldap_entries);
+ while (entry != NULL) {
+ next = NEXT(entry, link);
+ UNLINK(inst->ldap_entries, entry, link);
+ free_ldap_attributes(inst->database->mctx, entry);
+ isc_mem_put(inst->database->mctx, entry, sizeof(*entry));
+ entry = next;
+ }
+
+ inst->cache_active = isc_boolean_false;
+}
+
+static void
+free_ldap_attributes(isc_mem_t *mctx, ldap_entry_t *entry)
+{
+ ldap_attribute_t *attr, *next;
+
+ attr = HEAD(entry->attributes);
+ while (attr != NULL) {
+ next = NEXT(attr, link);
+ UNLINK(entry->attributes, attr, link);
+ free_ldap_values(mctx, attr);
+ ldap_value_free(attr->ldap_values);
+ ldap_memfree(attr->name);
+ isc_mem_put(mctx, attr, sizeof(*attr));
+ attr = next;
+ }
+}
+
+static void
+free_ldap_values(isc_mem_t *mctx, ldap_attribute_t *attr)
+{
+ ldap_value_t *value, *next;
+
+ value = HEAD(attr->values);
+ while (value != NULL) {
+ next = NEXT(value, link);
+ UNLINK(attr->values, value, link);
+ isc_mem_put(mctx, value, sizeof(*value));
+ value = next;
+ }
+}
+
+/* FIXME: this function is obsolete, remove. */
+static const LDAPMessage *
+next_entry(ldap_instance_t *inst)
+{
+ if (inst->ber) {
+ ber_free(inst->ber, 0);
+ inst->ber = NULL;
+ }
+
+ if (inst->handle && inst->entry)
+ inst->entry = ldap_next_entry(inst->handle, inst->entry);
+ else if (inst->handle && inst->result)
+ inst->entry = ldap_first_entry(inst->handle, inst->result);
+ else
+ inst->entry = NULL;
+
+ return inst->entry;
+}
+
+/* FIXME: Not tested. */
+static int
+ldap_sasl_interact(LDAP *ld, unsigned flags, void *defaults, void *sin)
+{
+ sasl_interact_t *in = (sasl_interact_t *)sin;
+ ldap_db_t *ldap_db = (ldap_db_t *)defaults;
+
+ REQUIRE(ldap_db != NULL);
+ UNUSED(flags);
+
+ if (ld == NULL || sin == NULL)
+ return LDAP_PARAM_ERROR;
+
+ for (in = sin; in != NULL && in->id != SASL_CB_LIST_END; in++) {
+ switch (in->id) {
+ case SASL_CB_USER:
+ log_error("SASL_CB_USER");
+ in->result = str_buf(ldap_db->sasl_user);
+ in->len = str_len(ldap_db->sasl_user);
+ break;
+ case SASL_CB_NOECHOPROMPT:
+ log_error("SASL_CB_NOECHOPROMPT");
+ break;
+ case SASL_CB_ECHOPROMPT:
+ log_error("SASL_CB_ECHOPROMPT");
+ break;
+ case SASL_CB_GETREALM:
+ log_error("SASL_CB_GETREALM");
+ break;
+ case SASL_CB_AUTHNAME:
+ log_error("SASL_CB_AUTHNAME");
+ in->result = str_buf(ldap_db->sasl_user);
+ in->len = str_len(ldap_db->sasl_user);
+ break;
+ case SASL_CB_PASS:
+ log_error("SASL_CB_PASS");
+ in->result = str_buf(ldap_db->password);
+ in->len = str_len(ldap_db->password);
+ break;
+ default:
+ log_error("SASL_UNKNOWN");
+ in->result = "";
+ in->len = 0;
+ break;
+ }
+ log_error("result: %s", in->result);
+ }
+
+ return LDAP_SUCCESS;
+}
+
+/*
+ * Initialize the LDAP handle and bind to the server. Needed authentication
+ * credentials and settings are available from the ldap_inst->database.
+ */
+static isc_result_t
+ldap_connect(ldap_instance_t *ldap_inst)
+{
+ LDAP *ld;
+ int ret;
+ int version;
+ const char *bind_dn;
+ const char *password;
+ struct berval *servercred = NULL;
+ ldap_db_t *ldap_db;
+
+ REQUIRE(ldap_inst != NULL);
+
+ ldap_db = ldap_inst->database;
+
+ if (str_len(ldap_db->bind_dn) == 0 ||
+ str_len(ldap_db->password) == 0) {
+ bind_dn = NULL;
+ password = NULL;
+ } else {
+ bind_dn = str_buf(ldap_db->bind_dn);
+ password = str_buf(ldap_db->password);
+ }
+
+ ret = ldap_initialize(&ld, str_buf(ldap_db->host));
+ if (ret != LDAP_SUCCESS) {
+ log_error("LDAP initialization failed: %s",
+ ldap_err2string(ret));
+ goto cleanup;
+ }
+
+ version = LDAP_VERSION3;
+ ret = ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION, &version);
+ LDAP_OPT_CHECK(ret, "failed to set LDAP version");
+
+ /*
+ ret = ldap_set_option(ld, LDAP_OPT_TIMELIMIT, (void *)&ldap_db->timeout);
+ LDAP_OPT_CHECK(ret, "failed to set timeout: %s", ldap_err2string(ret));
+ */
+
+ log_debug(2, "trying to establish LDAP connection to %s",
+ str_buf(ldap_db->host));
+
+
+ switch (ldap_db->auth_method) {
+ case AUTH_NONE:
+ ret = ldap_simple_bind_s(ld, NULL, NULL);
+ break;
+ case AUTH_SIMPLE:
+ ret = ldap_simple_bind_s(ld, bind_dn, password);
+ break;
+ case AUTH_SASL:
+ log_error("%s", str_buf(ldap_db->sasl_mech));
+ ret = ldap_sasl_interactive_bind_s(ld, NULL,
+ str_buf(ldap_db->sasl_mech),
+ NULL, NULL, LDAP_SASL_QUIET,
+ ldap_sasl_interact,
+ ldap_db);
+ ber_bvfree(servercred);
+ break;
+ default:
+ fatal_error("bug in ldap_connect(): unsupported "
+ "authentication mechanism");
+ goto cleanup;
+ }
+
+ if (ret != LDAP_SUCCESS) {
+ log_error("bind to LDAP server failed: %s",
+ ldap_err2string(ret));
+ goto cleanup;
+ }
+
+ ldap_inst->handle = ld;
+
+ return ISC_R_SUCCESS;
+
+cleanup:
+
+ if (ld != NULL)
+ ldap_unbind_ext_s(ld, NULL, NULL);
+
+ return ISC_R_FAILURE;
+}
+
+/* FIXME: Handle the case where the LDAP handle is NULL -> try to reconnect. */
+/* FIXME: Handle cases where the entry actually doesn't exist. */
+static isc_result_t
+ldap_modify_do(ldap_instance_t *ldap_inst, const char *dn, LDAPMod **mods)
+{
+ int ret;
+
+ REQUIRE(ldap_inst != NULL);
+ REQUIRE(dn != NULL);
+ REQUIRE(mods != NULL);
+
+ log_debug(2, "writing to to '%s'", dn);
+
+ ret = ldap_modify_ext_s(ldap_inst->handle, dn, mods, NULL, NULL);
+ if (ret != LDAP_SUCCESS) {
+ log_error("error writing to ldap: %s", ldap_err2string(ret));
+ return ISC_R_FAILURE;
+ }
+
+ return ISC_R_SUCCESS;
+}
+
+static isc_result_t
+ldap_rdatalist_to_ldapmod(isc_mem_t *mctx, dns_rdatalist_t *rdlist,
+ LDAPMod **changep, int mod_op)
+{
+ isc_result_t result;
+ LDAPMod *change = NULL;
+ char **vals = NULL;
+ const char *attr_name_c;
+ char *attr_name;
+
+
+ REQUIRE(changep != NULL && *changep == NULL);
+
+ CHECKED_MEM_GET_PTR(mctx, change);
+ ZERO_PTR(change);
+
+ result = rdatatype_to_ldap_attribute(rdlist->type, &attr_name_c);
+ if (result != ISC_R_SUCCESS) {
+ result = ISC_R_FAILURE;
+ goto cleanup;
+ }
+ DE_CONST(attr_name_c, attr_name);
+ CHECK(ldap_rdata_to_char_array(mctx, HEAD(rdlist->rdata), &vals));
+
+ change->mod_op = mod_op;
+ change->mod_type = attr_name;
+ change->mod_values = vals;
+
+ *changep = change;
+ return ISC_R_SUCCESS;
+
+cleanup:
+ free_ldapmod(mctx, &change);
+
+ return result;
+}
+
+static void
+free_ldapmod(isc_mem_t *mctx, LDAPMod **changep)
+{
+ LDAPMod *change;
+
+ REQUIRE(changep != NULL);
+
+ change = *changep;
+ if (change == NULL)
+ return;
+
+ free_char_array(mctx, &change->mod_values);
+ SAFE_MEM_PUT_PTR(mctx, change);
+
+ *changep = NULL;
+}
+
+isc_result_t
+ldap_rdata_to_char_array(isc_mem_t *mctx, dns_rdata_t *rdata_head,
+ char ***valsp)
+{
+ isc_result_t result;
+ char **vals;
+ unsigned int i;
+ unsigned int rdata_count = 0;
+ size_t vals_size;
+ dns_rdata_t *rdata;
+
+ REQUIRE(rdata_head != NULL);
+ REQUIRE(valsp != NULL && *valsp == NULL);
+
+ for (rdata = rdata_head; rdata != NULL; rdata = NEXT(rdata, link))
+ rdata_count++;
+
+ vals_size = (rdata_count + 1) * sizeof(char *);
+
+ CHECKED_MEM_ALLOCATE(mctx, vals, vals_size);
+ memset(vals, 0, vals_size);
+
+ rdata = rdata_head;
+ for (i = 0; i < rdata_count && rdata != NULL; i++) {
+ DECLARE_BUFFER(buffer, MINTSIZ);
+ isc_region_t region;
+
+ /* Convert rdata to text. */
+ INIT_BUFFER(buffer);
+ CHECK(dns_rdata_totext(rdata, NULL, &buffer));
+ isc_buffer_usedregion(&buffer, &region);
+
+ /* Now allocate the string with the right size. */
+ CHECKED_MEM_ALLOCATE(mctx, vals[i], region.length + 1);
+ memcpy(vals[i], region.base, region.length);
+ vals[i][region.length] = '\0';
+
+ rdata = NEXT(rdata, link);
+ }
+
+ *valsp = vals;
+ return ISC_R_SUCCESS;
+
+cleanup:
+ free_char_array(mctx, &vals);
+ return result;
+}
+
+static void
+free_char_array(isc_mem_t *mctx, char ***valsp)
+{
+ char **vals;
+ unsigned int i;
+
+ REQUIRE(valsp != NULL);
+
+ vals = *valsp;
+ if (vals == NULL)
+ return;
+
+ for (i = 0; vals[i] != NULL; i++)
+ isc_mem_free(mctx, vals[i]);
+
+ isc_mem_free(mctx, vals);
+ *valsp = NULL;
+}
+
+/*
+ * TODO: Handle updating of the SOA record, use the settings to determine if
+ * this is allowed.
+ */
+static isc_result_t
+modify_ldap_common(dns_name_t *owner, ldap_db_t *ldap_db,
+ dns_rdatalist_t *rdlist, int mod_op)
+{
+ isc_result_t result;
+ isc_mem_t *mctx;
+ ldap_instance_t *ldap_inst;
+ ld_string_t *owner_dn = NULL;
+ LDAPMod *change[2];
+
+ change[0] = change[1] = NULL;
+
+ mctx = ldap_db->mctx;
+ ldap_inst = get_connection(ldap_db);
+
+ CHECK(str_new(mctx, &owner_dn));
+ CHECK(dnsname_to_dn(ldap_db, owner, owner_dn));
+ CHECK(ldap_rdatalist_to_ldapmod(mctx, rdlist, &change[0], mod_op));
+ CHECK(ldap_modify_do(ldap_inst, str_buf(owner_dn), change));
+
+cleanup:
+ put_connection(ldap_inst);
+ str_destroy(&owner_dn);
+ free_ldapmod(mctx, &change[0]);
+
+ return result;
+}
+
+isc_result_t
+write_to_ldap(dns_name_t *owner, ldap_db_t *ldap_db, dns_rdatalist_t *rdlist)
+{
+ return modify_ldap_common(owner, ldap_db, rdlist, LDAP_MOD_ADD);
+}
+
+isc_result_t
+remove_from_ldap(dns_name_t *owner, ldap_db_t *ldap_db,
+ dns_rdatalist_t *rdlist)
+{
+ return modify_ldap_common(owner, ldap_db, rdlist, LDAP_MOD_DELETE);
+}
diff --git a/src/ldap_helper.h b/src/ldap_helper.h
new file mode 100644
index 0000000..a0e4aea
--- /dev/null
+++ b/src/ldap_helper.h
@@ -0,0 +1,105 @@
+/* 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
+ */
+
+#ifndef _LD_LDAP_HELPER_H_
+#define _LD_LDAP_HELPER_H_
+
+#include <isc/util.h>
+
+typedef struct ldap_db ldap_db_t;
+typedef struct ldap_instance ldap_instance_t;
+
+/*
+ * some nice words about ldapdb_rdatalist_t:
+ * - it is list of all RRs which have same owner name
+ * - rdata buffer is reachable only via dns_rdata_toregion()
+ *
+ * structure:
+ *
+ * class1 class2
+ * type1 type2
+ * ttl1 ttl2
+ * rdata1 -> rdata2 -> rdata3 rdata4 -> rdata5
+ * next_rdatalist -> next_rdatalist ...
+ */
+typedef LIST(dns_rdatalist_t) ldapdb_rdatalist_t;
+
+isc_result_t ldapdb_rdatalist_findrdatatype(ldapdb_rdatalist_t *rdatalist,
+ dns_rdatatype_t rdtype,
+ dns_rdatalist_t **rdlistp);
+/*
+ * ldapdb_rdatalist_findrdatatype
+ *
+ * find rdatalist in rdatalist which matches rdtype and return it in rdlistp.
+ *
+ * Returns ISC_R_SUCCESS or ISC_R_NOTFOUND
+ */
+
+void ldapdb_rdatalist_destroy(isc_mem_t *mctx, ldapdb_rdatalist_t *rdatalist);
+/*
+ * ldapdb_rdatalist_destroy
+ *
+ * Free rdatalist list and free all associated rdata buffers.
+ */
+
+void free_rdatalist(isc_mem_t *mctx, dns_rdatalist_t *rdlist);
+/*
+ * free_rdatalist
+ *
+ * Free all dynamically allocated memory inside rdlist.
+ */
+
+isc_result_t ldapdb_rdatalist_get(isc_mem_t *mctx, ldap_db_t *ldap_db,
+ dns_name_t *name,
+ ldapdb_rdatalist_t *rdatalist);
+/*
+ * ldapdb_rdatalist_get
+ *
+ * Find all RRs in ldap database with specified name and return them in
+ * rdatalist.
+ *
+ * XXX Add partial match handling.
+ *
+ * Possible errors include:
+ *
+ * ISC_R_NOMEMORY
+ * ISC_R_NOTFOUND
+ * DNS_R_PARTIALMATCH
+ */
+
+isc_result_t new_ldap_db(isc_mem_t *mctx, dns_view_t *view, ldap_db_t **ldap_dbp,
+ const char * const *argv);
+void destroy_ldap_db(ldap_db_t **ldap_db);
+isc_result_t refresh_zones_from_ldap(ldap_db_t *ldap_db, const char *name,
+ dns_zonemgr_t *zmgr);
+
+isc_result_t
+get_zone_dn(ldap_db_t *ldap_db, dns_name_t *name, const char **dn,
+ dns_name_t *matched_name);
+
+/* Functions for writing to LDAP. */
+isc_result_t ldap_rdata_to_char_array(isc_mem_t *mctx, dns_rdata_t *rdata_head,
+ char ***valsp);
+isc_result_t write_to_ldap(dns_name_t *owner, ldap_db_t *ldap_db,
+ dns_rdatalist_t *rdlist);
+isc_result_t remove_from_ldap(dns_name_t *owner, ldap_db_t *ldap_db,
+ dns_rdatalist_t *rdlist);
+
+#endif /* !_LD_LDAP_HELPER_H_ */
diff --git a/src/log.c b/src/log.c
new file mode 100644
index 0000000..216a602
--- /dev/null
+++ b/src/log.c
@@ -0,0 +1,71 @@
+/* Authors: Martin Nagy <mnagy@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
+ */
+
+/*
+ * Some general comments about the driver go here. ABRAKA
+ */
+
+/* Includes, group nicely and keep files ordered! ABRAKA */
+#include <stdio.h>
+
+#include <dns/log.h>
+
+#include "log.h"
+
+#define MSG_BUFFER_SIZE 2048
+
+/*
+ * TODO:
+ * - Some compiler format checks would be nice.
+ * - Think about log_unexpected_file_line(), maybe use something else.
+ */
+
+
+void
+log_debug(int level, const char *format, ...)
+{
+ va_list args;
+
+ va_start(args, format);
+ /*
+ isc_log_vwrite(dns_lctx, DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_DYNDB,
+ ISC_LOG_DEBUG(level), format, args);
+ */
+ /*
+ * For now, behave same as log_error(), so we can see every debugging
+ * logs without the need to specify -d.
+ */
+ isc_log_vwrite(dns_lctx, DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_DYNDB,
+ ISC_LOG_ERROR, format, args);
+ (void)level;
+
+ va_end(args);
+}
+
+void
+log_error(const char *format, ...)
+{
+ va_list args;
+
+ va_start(args, format);
+ isc_log_vwrite(dns_lctx, DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_DYNDB,
+ ISC_LOG_ERROR, format, args);
+ va_end(args);
+
+}
diff --git a/src/log.h b/src/log.h
new file mode 100644
index 0000000..00f772d
--- /dev/null
+++ b/src/log.h
@@ -0,0 +1,48 @@
+/* Authors: Martin Nagy <mnagy@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
+ */
+
+#ifndef _LD_LOG_H_
+#define _LD_LOG_H_
+
+#include <isc/error.h>
+
+#define fatal_error(...) \
+ isc_error_fatal(__FILE__, __LINE__, __VA_ARGS__)
+
+/*
+ * Change these to use our string library.
+ */
+
+#define log_func(logstr) log_debug(2, "%s: %s", __func__, (logstr))
+#define log_func_va(logstr, ...) \
+ log_debug(2, "%s: " logstr, __func__, __VA_ARGS__)
+
+#define log_func_enter() log_func("entering")
+#define log_func_enter_args(logstr, ...) \
+ log_func_va("entering, args: " logstr, __VA_ARGS__)
+
+#define log_func_exit() log_func("exiting")
+#define log_func_exit_result(res) \
+ log_func_va("exiting with %s", isc_result_totext(res))
+
+/* Basic logging functions */
+void log_debug(int level, const char *format, ...);
+void log_error(const char *format, ...);
+
+#endif /* !_LD_LOG_H_ */
diff --git a/src/rdlist.c b/src/rdlist.c
new file mode 100644
index 0000000..48b2a0b
--- /dev/null
+++ b/src/rdlist.c
@@ -0,0 +1,138 @@
+/* Authors: Adam Tkac <atkac@redhat.com>
+ * Martin Nagy <mnagy@redhat.com>
+ *
+ * Copyright (C) 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/mem.h>
+#include <isc/result.h>
+#include <isc/util.h>
+
+#include <dns/rdata.h>
+#include <dns/rdatalist.h>
+
+#include <string.h>
+
+#include "ldap_helper.h" /* TODO: Move things from ldap_helper here? */
+#include "rdlist.h"
+#include "util.h"
+
+
+static isc_result_t
+rdata_clone(isc_mem_t *mctx, dns_rdata_t *source, dns_rdata_t **targetp)
+{
+ isc_result_t result;
+ dns_rdata_t *target = NULL;
+ isc_region_t target_region, source_region;
+
+ REQUIRE(mctx != NULL);
+ REQUIRE(source != NULL);
+ REQUIRE(targetp != NULL && *targetp == NULL);
+
+ CHECKED_MEM_GET_PTR(mctx, target);
+
+ dns_rdata_init(target);
+
+ dns_rdata_toregion(source, &source_region);
+
+ CHECKED_MEM_GET(mctx, target_region.base, source_region.length);
+
+ target_region.length = source_region.length;
+ memcpy(target_region.base, source_region.base, source_region.length);
+ dns_rdata_fromregion(target, source->rdclass, source->type,
+ &target_region);
+
+ *targetp = target;
+
+ return ISC_R_SUCCESS;
+
+cleanup:
+ SAFE_MEM_PUT_PTR(mctx, target);
+
+ return result;
+}
+
+isc_result_t
+rdatalist_clone(isc_mem_t *mctx, dns_rdatalist_t *source,
+ dns_rdatalist_t **targetp)
+{
+ dns_rdatalist_t *target;
+ dns_rdata_t *source_rdata;
+ dns_rdata_t *target_rdata;
+ isc_result_t result;
+
+ REQUIRE(mctx != NULL);
+ REQUIRE(source != NULL);
+ REQUIRE(targetp != NULL && *targetp == NULL);
+
+ CHECKED_MEM_GET_PTR(mctx, target);
+
+ dns_rdatalist_init(target);
+ target->rdclass = source->rdclass;
+ target->type = source->type;
+ target->covers = source->covers;
+ target->ttl = source->ttl;
+
+ source_rdata = HEAD(source->rdata);
+ while (source_rdata != NULL) {
+ target_rdata = NULL;
+ CHECK(rdata_clone(mctx, source_rdata, &target_rdata));
+ APPEND(target->rdata, target_rdata, link);
+ source_rdata = NEXT(source_rdata, link);
+ }
+
+ *targetp = target;
+
+ return ISC_R_SUCCESS;
+
+cleanup:
+ if (target)
+ free_rdatalist(mctx, target);
+ SAFE_MEM_PUT_PTR(mctx, target);
+
+ return result;
+}
+
+isc_result_t
+ldap_rdatalist_copy(isc_mem_t *mctx, ldapdb_rdatalist_t source,
+ ldapdb_rdatalist_t *target)
+{
+ dns_rdatalist_t *rdlist;
+ dns_rdatalist_t *new_rdlist;
+ isc_result_t result;
+
+ REQUIRE(mctx != NULL);
+ REQUIRE(target != NULL);
+
+ INIT_LIST(*target);
+
+ rdlist = HEAD(source);
+ while (rdlist != NULL) {
+ new_rdlist = NULL;
+ CHECK(rdatalist_clone(mctx, rdlist, &new_rdlist));
+ APPEND(*target, new_rdlist, link);
+
+ rdlist = NEXT(rdlist, link);
+ }
+
+ return ISC_R_SUCCESS;
+
+cleanup:
+ ldapdb_rdatalist_destroy(mctx, target);
+
+ return result;
+}
diff --git a/src/rdlist.h b/src/rdlist.h
new file mode 100644
index 0000000..d3ce2d0
--- /dev/null
+++ b/src/rdlist.h
@@ -0,0 +1,32 @@
+/* Authors: Adam Tkac <atkac@redhat.com>
+ * Martin Nagy <mnagy@redhat.com>
+ *
+ * Copyright (C) 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
+ */
+
+#ifndef _LD_RDLIST_H_
+#define _LD_RDLIST_H_
+
+isc_result_t
+rdatalist_clone(isc_mem_t *mctx, dns_rdatalist_t *source,
+ dns_rdatalist_t **targetp);
+
+isc_result_t
+ldap_rdatalist_copy(isc_mem_t *mctx, ldapdb_rdatalist_t source,
+ ldapdb_rdatalist_t *target);
+
+#endif /* !_LD_RDLIST_H_ */
diff --git a/src/semaphore.c b/src/semaphore.c
new file mode 100644
index 0000000..33ae9f8
--- /dev/null
+++ b/src/semaphore.c
@@ -0,0 +1,108 @@
+/* Authors: Martin Nagy <mnagy@redhat.com>
+ *
+ * Copyright (C) 2008 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
+ */
+
+/*
+ * Note: This implementation doesn't prevent from starvation. This means that
+ * if a thread signals the semaphore and then waits for it, it may catch it's
+ * own signal. However, for our purposes, this shouldn't be needed.
+ */
+
+#include <isc/condition.h>
+#include <isc/result.h>
+#include <isc/util.h>
+
+#include "semaphore.h"
+
+/*
+ * Initialize a semaphore.
+ *
+ * sem - allocated semaphore that will be initialized
+ * value - number of times we can acquire the semaphore.
+ */
+isc_result_t
+semaphore_init(semaphore_t *sem, int value)
+{
+ isc_result_t result;
+
+ REQUIRE(sem != NULL);
+ REQUIRE(value > 0);
+
+ sem->value = value;
+ result = isc_mutex_init(&sem->mutex);
+ if (result != ISC_R_SUCCESS)
+ return result;
+
+ result = isc_condition_init(&sem->cond);
+ if (result != ISC_R_SUCCESS)
+ isc_mutex_destroy(&sem->mutex);
+
+ return result;
+}
+
+/*
+ * Destroy a semaphore.
+ *
+ * sem - semaphore to be destroyed.
+ */
+void
+semaphore_destroy(semaphore_t *sem)
+{
+ if (sem == NULL)
+ return;
+
+ RUNTIME_CHECK(isc_mutex_destroy(&sem->mutex) == ISC_R_SUCCESS);
+ RUNTIME_CHECK(isc_condition_destroy(&sem->cond) == ISC_R_SUCCESS);
+}
+
+/*
+ * Wait on semaphore. This operation will try to acquire a lock on the
+ * semaphore. If the semaphore is already acquired as many times at it allows,
+ * the function will block until someone releases the lock.
+ */
+void
+semaphore_wait(semaphore_t *sem)
+{
+ REQUIRE(sem != NULL);
+
+ LOCK(&sem->mutex);
+
+ sem->value--;
+ if (sem->value < 0)
+ WAIT(&sem->cond, &sem->mutex);
+
+ UNLOCK(&sem->mutex);
+}
+
+/*
+ * Release the semaphore. This will make sure that another thread (probably
+ * already waiting) will be able to acquire the semaphore.
+ */
+void
+semaphore_signal(semaphore_t *sem)
+{
+ REQUIRE(sem != NULL);
+
+ LOCK(&sem->mutex);
+
+ sem->value++;
+ if (sem->value >= 0)
+ SIGNAL(&sem->cond);
+
+ UNLOCK(&sem->mutex);
+}
diff --git a/src/semaphore.h b/src/semaphore.h
new file mode 100644
index 0000000..d417443
--- /dev/null
+++ b/src/semaphore.h
@@ -0,0 +1,45 @@
+/* Authors: Martin Nagy <mnagy@redhat.com>
+ *
+ * Copyright (C) 2008 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
+ */
+
+#ifndef _LD_SEMAPHORE_H_
+#define _LD_SEMAPHORE_H_
+
+#include <isc/condition.h>
+#include <isc/mutex.h>
+
+/*
+ * Semaphore can be "acquired" multiple times. However, it has a maximum
+ * number of times someone can acquire him. If a semaphore is already acquired
+ * more times than allowed, it will block until other thread release its,
+ */
+struct semaphore {
+ int value; /* Maximum number of times you can LOCK()) */
+ isc_mutex_t mutex; /* Mutex protecting this whole struct. */
+ isc_condition_t cond; /* Condition used for waiting on release. */
+};
+
+typedef struct semaphore semaphore_t;
+
+/* Public functions. */
+isc_result_t semaphore_init(semaphore_t *sem, int value);
+void semaphore_destroy(semaphore_t *sem);
+void semaphore_wait(semaphore_t *sem);
+void semaphore_signal(semaphore_t *sem);
+
+#endif /* !_LD_SEMAPHORE_H_ */
diff --git a/src/settings.c b/src/settings.c
new file mode 100644
index 0000000..f217076
--- /dev/null
+++ b/src/settings.c
@@ -0,0 +1,176 @@
+/* Authors: Martin Nagy <mnagy@redhat.com>
+ *
+ * Copyright (C) 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/util.h>
+#include <isc/mem.h>
+#include <isc/result.h>
+
+#include <ctype.h>
+#include <stdlib.h>
+
+#include "log.h"
+#include "settings.h"
+#include "str.h"
+#include "util.h"
+
+
+/*
+ * Forward declarations.
+ */
+static int args_are_equal(const char *setting_argument,
+ const char *argv_argument);
+static isc_result_t set_value(setting_t *setting, const char *value);
+static isc_result_t set_default_value(setting_t *setting);
+static const char * get_value_str(const char *arg);
+
+isc_result_t
+set_settings(setting_t settings[], const char * const* argv)
+{
+ isc_result_t result;
+ int i, j;
+ const char *value;
+
+ for (i = 0; argv[i] != NULL; i++) {
+ for (j = 0; settings[j].name != NULL; j++) {
+ if (args_are_equal(settings[j].name, argv[i])) {
+ value = get_value_str(argv[i]);
+ CHECK(set_value(&settings[j], value));
+ break;
+ }
+ }
+ }
+
+ /* When all is done, check that all the required settings are set. */
+ for (j = 0; settings[j].name != NULL; j++) {
+ if (settings[j].set != 0)
+ continue;
+ if (!settings[j].has_a_default) {
+ log_error("argument %s must be set", settings[j].name);
+ result = ISC_R_FAILURE;
+ goto cleanup;
+ }
+ CHECK(set_default_value(&settings[j]));
+ }
+
+ return ISC_R_SUCCESS;
+
+cleanup:
+ /* TODO: Free memory in case of error. */
+ return result;
+}
+
+/*
+ * Return 1 if the argument names are equal. The argv_argument also needs to
+ * contain an additional space at the end.
+ */
+static int
+args_are_equal(const char *setting_argument, const char *argv_argument)
+{
+ if (setting_argument == NULL || argv_argument == NULL)
+ return 0;
+
+ for (;;) {
+ if (*setting_argument == '\0')
+ break;
+ if (*argv_argument == '\0')
+ return 0;
+ if (*setting_argument != *argv_argument)
+ return 0;
+ setting_argument++;
+ argv_argument++;
+ }
+
+ /* Now make sure we also found a space at the end of argv_argument. */
+ if (!isspace(*argv_argument) && *argv_argument != '\0')
+ return 0;
+
+ return 1;
+}
+
+static isc_result_t
+set_value(setting_t *setting, const char *value)
+{
+ isc_result_t result;
+ int numeric_value;
+
+ if (setting->type == ST_LD_STRING) {
+ CHECK(str_init_char((ld_string_t *)setting->target, value));
+ } else if (setting->type == ST_SIGNED_INTEGER ||
+ setting->type == ST_UNSIGNED_INTEGER) {
+ if (*value == '\0') {
+ result = ISC_R_FAILURE;
+ goto cleanup;
+ }
+ /* TODO: better type checking. */
+ numeric_value = atoi(value);
+ if (setting->type == ST_SIGNED_INTEGER) {
+ (*(signed *)setting->target) = (signed)numeric_value;
+ } else if (numeric_value < 0) {
+ log_error("argument %s must be an unsigned integer",
+ setting->name);
+ result = ISC_R_FAILURE;
+ goto cleanup;
+ } else {
+ (*(unsigned *)setting->target) = (unsigned)numeric_value;
+ }
+ } else {
+ fatal_error("unknown type in function set_value()");
+ result = ISC_R_FAILURE;
+ goto cleanup;
+ }
+
+ setting->set = 1;
+
+ return ISC_R_SUCCESS;
+
+cleanup:
+ return result;
+}
+
+static isc_result_t
+set_default_value(setting_t *setting)
+{
+ switch (setting->type) {
+ case ST_LD_STRING:
+ return set_value(setting, setting->default_value.value_char);
+ break;
+ case ST_SIGNED_INTEGER:
+ *(signed *)setting->target = setting->default_value.value_sint;
+ break;
+ case ST_UNSIGNED_INTEGER:
+ *(unsigned *)setting->target = setting->default_value.value_uint;
+ break;
+ default:
+ fatal_error("unknown type in function set_default_value()");
+ return ISC_R_FAILURE;
+ }
+
+ return ISC_R_SUCCESS;
+}
+
+static const char *
+get_value_str(const char *arg)
+{
+ while (*arg != '\0' && !isspace(*arg))
+ arg++;
+ while (*arg != '\0' && isspace(*arg))
+ arg++;
+
+ return arg;
+}
diff --git a/src/settings.h b/src/settings.h
new file mode 100644
index 0000000..fce1b2a
--- /dev/null
+++ b/src/settings.h
@@ -0,0 +1,73 @@
+/* Authors: Martin Nagy <mnagy@redhat.com>
+ *
+ * Copyright (C) 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
+ */
+
+#ifndef _LD_SETTINGS_H_
+#define _LD_SETTINGS_H_
+
+typedef struct setting setting_t;
+
+typedef enum {
+ ST_LD_STRING,
+ ST_SIGNED_INTEGER,
+ ST_UNSIGNED_INTEGER,
+} setting_type_t;
+
+struct setting {
+ const char *name;
+ int set;
+ int has_a_default;
+ setting_type_t type;
+ union {
+ const char *value_char;
+ signed int value_sint;
+ unsigned int value_uint;
+ } default_value;
+ void *target;
+};
+
+/*
+ * These defines are used as initializers for setting_t, for example:
+ *
+ * setting_t my_setting = {
+ * "name", default_string("this is the default"),
+ * &target_variable
+ * }
+ *
+ * setting_t my_setting = {
+ * "name", no_default_string, &target_variable
+ * }
+ */
+#define default_string(val) 0, 1, ST_LD_STRING, { .value_char = (val) }, NULL
+#define default_sint(val) 0, 1, ST_SIGNED_INTEGER, { .value_sint = (val) }, NULL
+#define default_uint(val) 0, 1, ST_UNSIGNED_INTEGER, { .value_uint = (val) }, NULL
+/* No defaults. */
+#define no_default_string 0, 0, ST_LD_STRING, { .value_char = NULL }, NULL
+#define no_default_sint 0, 0, ST_SIGNED_INTEGER, { .value_sint = 0 }, NULL
+#define no_default_uint 0, 0, ST_UNSIGNED_INTEGER, { .value_uint = 0 }, NULL
+
+/* This is used in the end of setting_t arrays. */
+#define end_of_settings { NULL, default_sint(0) }
+
+/*
+ * Prototypes.
+ */
+isc_result_t
+set_settings(setting_t *settings, const char * const* argv);
+
+#endif /* !_LD_SETTINGS_H_ */
diff --git a/src/str.c b/src/str.c
new file mode 100644
index 0000000..f5908c9
--- /dev/null
+++ b/src/str.c
@@ -0,0 +1,591 @@
+/* Authors: Martin Nagy <mnagy@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
+ */
+
+/*
+ * TODO:
+ * Write some test cases.
+ *
+ * Review all the REQUIRE() macros.
+ */
+
+#include <isc/buffer.h>
+#include <isc/mem.h>
+#include <isc/mutex.h>
+#include <isc/region.h>
+#include <isc/util.h>
+
+#include <dns/result.h>
+
+#include <ctype.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+
+#include "str.h"
+#include "util.h"
+
+
+#define IGNORE(expr) if (expr) return
+#define IGNORE_R(expr) if (expr) return ISC_R_SUCCESS
+
+#define ALLOC_BASE_SIZE 16
+
+/* Custom string, these shouldn't use these directly */
+struct ld_string {
+ isc_mem_t *mctx; /* Memory context. */
+ char *data; /* String is stored here. */
+ size_t allocated; /* Number of bytes allocated. */
+#if ISC_MEM_TRACKLINES
+ const char *file; /* File where the allocation occured. */
+ int line; /* Line in the file. */
+#endif
+};
+
+struct ld_split {
+ isc_mem_t *mctx; /* Memory context. */
+ char *data; /* Splits. */
+ size_t allocated; /* Number of bytes allocated. */
+ char *splits[LD_MAX_SPLITS];
+ size_t split_count; /* Number of splits. */
+};
+
+/*
+ * Private functions.
+ */
+
+
+/*
+ * Make sure we have enough space for at least len + 1 bytes.
+ * This function is private.
+ */
+static isc_result_t
+str_alloc(ld_string_t *str, size_t len)
+{
+ size_t new_size;
+ char *new_buffer;
+
+ REQUIRE(str != NULL);
+ REQUIRE(str->mctx != NULL);
+ IGNORE_R(str->allocated > len);
+
+ len++; /* Account for the last '\0'. */
+ new_size = ISC_MAX(str->allocated, ALLOC_BASE_SIZE);
+ while (new_size <= len)
+ new_size *= 2;
+
+ new_size *= sizeof (char);
+#if ISC_MEM_TRACKLINES
+ new_buffer = isc__mem_get(str->mctx, new_size, str->file, str->line);
+#else
+ new_buffer = isc_mem_get(str->mctx, new_size);
+#endif
+
+ if (new_buffer == NULL)
+ return ISC_R_NOMEMORY;
+
+ if (str->data != NULL) {
+ memcpy(new_buffer, str->data, len);
+ new_buffer[len] = '\0';
+ isc_mem_put(str->mctx, str->data, str->allocated);
+ } else {
+ new_buffer[0] = '\0';
+ }
+
+ str->data = new_buffer;
+ str->allocated = new_size;
+
+ return ISC_R_SUCCESS;
+}
+
+/*
+ * Return length of a string. This function is internal, we may decide to
+ * implement caching of the string length in the future for performance
+ * reasons.
+ */
+static size_t
+str_len_internal(const ld_string_t *str)
+{
+ REQUIRE(str != NULL);
+
+ if (str->allocated == 0)
+ return 0;
+
+ return strlen(str->data);
+}
+
+
+/*
+ * Public functions.
+ */
+
+
+/*
+ * Allocate a new string.
+ */
+isc_result_t
+str__new(isc_mem_t *mctx, ld_string_t **new_str _STR_MEM_FLARG)
+{
+ ld_string_t *str;
+
+ REQUIRE(mctx != NULL);
+ REQUIRE(new_str != NULL && *new_str == NULL);
+
+#if ISC_MEM_TRACKLINES
+ str = isc__mem_get(mctx, sizeof(ld_string_t), file, line);
+#else
+ str = isc_mem_get(mctx, sizeof(ld_string_t));
+#endif
+ if (str == NULL)
+ return ISC_R_NOMEMORY;
+
+ str->data = NULL;
+ str->allocated = 0;
+ str->mctx = NULL;
+
+ isc_mem_attach(mctx, &str->mctx);
+
+#if ISC_MEM_TRACKLINES
+ str->file = file;
+ str->line = line;
+#endif
+
+ *new_str = str;
+
+ return ISC_R_SUCCESS;
+}
+
+/*
+ * Destroy string, i.e. also free the ld_string_t struct.
+ */
+void
+str__destroy(ld_string_t **str _STR_MEM_FLARG)
+{
+ IGNORE(str == NULL || *str == NULL);
+
+ if ((*str)->allocated) {
+#if ISC_MEM_TRACKLINES
+ isc__mem_put((*str)->mctx, (*str)->data,
+ (*str)->allocated * sizeof(char), file, line);
+#else
+ isc_mem_put((*str)->mctx, (*str)->data,
+ (*str)->allocated * sizeof(char));
+#endif
+ }
+
+#if ISC_MEM_TRACKLINES
+ isc__mem_putanddetach(&(*str)->mctx, *str, sizeof(ld_string_t),
+ file, line);
+#else
+ isc_mem_putanddetach(&(*str)->mctx, *str, sizeof(ld_string_t));
+#endif
+
+ *str = NULL;
+}
+
+/*
+ * Return length of a string.
+ */
+size_t
+str_len(const ld_string_t *str)
+{
+ return str_len_internal(str);
+}
+
+/*
+ * Return a const char * type.
+ */
+const char *
+str_buf(const ld_string_t *src)
+{
+ REQUIRE(src != NULL && src->data != NULL);
+
+ return src->data;
+}
+
+/*
+ * Copy string from src to dest.
+ */
+isc_result_t
+str_copy(ld_string_t *dest, const ld_string_t *src)
+{
+ isc_result_t result;
+ size_t len;
+
+ REQUIRE(dest != NULL);
+ REQUIRE(src != NULL);
+ IGNORE_R(src->data == NULL);
+
+ len = str_len_internal(src);
+ CHECK(str_alloc(dest, len));
+ memcpy(dest->data, src->data, len + 1);
+
+ return ISC_R_SUCCESS;
+
+cleanup:
+ return result;
+}
+
+/*
+ * Make a new string and copy src to it.
+ */
+isc_result_t
+str_clone(ld_string_t **dest, const ld_string_t *src _STR_MEM_FLARG)
+{
+ isc_result_t result;
+
+ REQUIRE(src != NULL);
+ REQUIRE(dest != NULL && *dest == NULL);
+
+ CHECK(str__new(src->mctx, dest _STR_MEM_FLARG_PASS));
+ CHECK(str_copy(*dest, src));
+
+ return ISC_R_SUCCESS;
+
+cleanup:
+ return result;
+}
+
+void
+str_clear(ld_string_t *dest)
+{
+ REQUIRE(dest != NULL);
+
+ if (dest->allocated)
+ dest->data[0] = '\0';
+}
+
+/*
+ * Initialize string from char *.
+ */
+isc_result_t
+str_init_char(ld_string_t *dest, const char *src)
+{
+ isc_result_t result;
+ size_t len;
+
+ REQUIRE(dest != NULL);
+ IGNORE_R(src == NULL);
+
+ len = strlen(src);
+ CHECK(str_alloc(dest, len));
+ memcpy(dest->data, src, len);
+ dest->data[len] = '\0';
+
+ return ISC_R_SUCCESS;
+
+cleanup:
+ return result;
+}
+
+/*
+ * Concatenate char *src to string dest.
+ * TODO: make str_cat_char() simply use str_cat_char_len()
+ */
+isc_result_t
+str_cat_char(ld_string_t *dest, const char *src)
+{
+ isc_result_t result;
+ char *from;
+ size_t dest_size;
+ size_t src_size;
+
+ REQUIRE(dest != NULL);
+ IGNORE_R(src == NULL);
+
+ dest_size = str_len_internal(dest);
+ src_size = strlen(src);
+
+ IGNORE_R(src_size == 0);
+
+ CHECK(str_alloc(dest, dest_size + src_size));
+ from = dest->data + dest_size;
+ memcpy(from, src, src_size + 1);
+
+ return ISC_R_SUCCESS;
+
+cleanup:
+ return result;
+}
+
+isc_result_t
+str_cat_char_len(ld_string_t *dest, const char *src, size_t len)
+{
+ isc_result_t result;
+ char *from;
+ size_t dest_size;
+
+ REQUIRE(dest != NULL);
+ IGNORE_R(src == NULL);
+ IGNORE_R(len == 0);
+
+ dest_size = str_len_internal(dest);
+
+ CHECK(str_alloc(dest, dest_size + len));
+ from = dest->data + dest_size;
+ memcpy(from, src, len);
+ from[len] = '\0';
+
+ return ISC_R_SUCCESS;
+
+cleanup:
+ return result;
+}
+
+isc_result_t
+str_cat_isc_region(ld_string_t *dest, const isc_region_t *region)
+{
+ REQUIRE(dest != NULL);
+ REQUIRE(region != NULL);
+
+ return str_cat_char_len(dest, (char *)region->base, region->length);
+}
+
+isc_result_t
+str_cat_isc_buffer(ld_string_t *dest, const isc_buffer_t *buffer)
+{
+ isc_region_t region;
+ isc_buffer_t *deconst_buffer;
+
+ REQUIRE(dest != NULL);
+ REQUIRE(ISC_BUFFER_VALID(buffer));
+
+ DE_CONST(buffer, deconst_buffer);
+ isc_buffer_usedregion(deconst_buffer, &region);
+
+ return str_cat_isc_region(dest, &region);
+}
+
+/*
+ * Concatenate string src to string dest.
+ */
+isc_result_t
+str_cat(ld_string_t *dest, const ld_string_t *src)
+{
+ REQUIRE(dest != NULL);
+ IGNORE_R(src == NULL);
+ IGNORE_R(src->data == NULL);
+
+ return str_cat_char(dest, src->data);
+}
+
+/*
+ * A sprintf() like function.
+ */
+isc_result_t
+str_sprintf(ld_string_t *dest, const char *format, ...)
+{
+ isc_result_t result;
+ va_list ap;
+
+ REQUIRE(dest != NULL);
+ REQUIRE(format != NULL);
+
+ va_start(ap, format);
+ result = str_vsprintf(dest, format, ap);
+ va_end(ap);
+
+ return result;
+}
+
+isc_result_t
+str_vsprintf(ld_string_t *dest, const char *format, va_list ap)
+{
+ int len;
+ isc_result_t result;
+
+ REQUIRE(dest != NULL);
+ REQUIRE(format != NULL);
+
+ len = vsnprintf(dest->data, dest->allocated, format, ap);
+ if (len > 0) {
+ CHECK(str_alloc(dest, len));
+ len = vsnprintf(dest->data, dest->allocated, format, ap);
+ }
+
+ if (len < 0)
+ result = ISC_R_FAILURE;
+
+ return ISC_R_SUCCESS;
+
+cleanup:
+ return result;
+}
+
+void
+str_toupper(ld_string_t *str)
+{
+ char *ptr;
+
+ REQUIRE(str != NULL);
+
+ if (str->data == NULL)
+ return;
+
+ for (ptr = str->data; *ptr != '\0'; ptr++)
+ *ptr = toupper((unsigned char)*ptr);
+}
+
+void
+str_to_isc_buffer(const ld_string_t *src, isc_buffer_t *dest)
+{
+ size_t len;
+
+ REQUIRE(src != NULL);
+ REQUIRE(dest != NULL);
+
+ len = str_len_internal(src) - 1;
+
+ isc_buffer_init(dest, src->data, len);
+ isc_buffer_add(dest, len);
+}
+
+int
+str_casecmp_char(const ld_string_t *s1, const char *s2)
+{
+ REQUIRE(s1 != NULL && s1->data != NULL);
+ REQUIRE(s2 != NULL);
+
+ return strcasecmp(s1->data, s2);
+}
+
+/*
+ * TODO: Review.
+ */
+isc_result_t
+str_new_split(isc_mem_t *mctx, ld_split_t **splitp)
+{
+ isc_result_t result;
+ ld_split_t *split;
+
+ REQUIRE(splitp != NULL && *splitp == NULL);
+
+ CHECKED_MEM_GET_PTR(mctx, split);
+ ZERO_PTR(split);
+ isc_mem_attach(mctx, &split->mctx);
+
+ *splitp = split;
+ return ISC_R_SUCCESS;
+
+cleanup:
+ return result;
+}
+
+void
+str_destroy_split(ld_split_t **splitp)
+{
+ ld_split_t *split;
+
+ IGNORE(splitp == NULL || *splitp == NULL);
+
+ split = *splitp;
+
+ if (split->allocated)
+ isc_mem_free(split->mctx, split->data);
+
+ isc_mem_putanddetach(&split->mctx, split, sizeof(*split));
+
+ *splitp = NULL;
+}
+
+static isc_result_t
+str_split_initialize(ld_split_t *split, const char *str)
+{
+ size_t size;
+
+ REQUIRE(split != NULL);
+ REQUIRE(split->mctx != NULL);
+ REQUIRE(str != NULL && *str != '\0');
+
+ if (split->allocated != 0) {
+ isc_mem_put(split->mctx, split->data, split->allocated);
+ split->allocated = 0;
+ }
+ split->splits[0] = NULL;
+ split->split_count = 0;
+
+ size = strlen(str) + 1;
+ split->data = isc_mem_strdup(split->mctx, str);
+ if (split->data == NULL)
+ return ISC_R_NOMEMORY;
+
+ split->allocated = size;
+
+ return ISC_R_SUCCESS;
+}
+
+isc_result_t
+str_split(const ld_string_t *src, const char delimiter, ld_split_t *split)
+{
+ isc_result_t result;
+ unsigned int current_pos;
+ int save;
+
+ REQUIRE(src != NULL);
+ REQUIRE(delimiter != '\0');
+ REQUIRE(split != NULL);
+
+ CHECK(str_split_initialize(split, src->data));
+
+ /* Replace all delimiters with '\0'. */
+ for (unsigned int i = 0; i < split->allocated; i++) {
+ if (split->data[i] == delimiter)
+ split->data[i] = '\0';
+ }
+
+ /* Now save the right positions. */
+ current_pos = 0;
+ save = 1;
+ for (unsigned int i = 0;
+ i < split->allocated && current_pos < LD_MAX_SPLITS;
+ i++) {
+ if (save && split->data[i] != '\0') {
+ split->splits[current_pos] = split->data + i;
+ current_pos++;
+ save = 0;
+ } else if (split->data[i] == '\0') {
+ save = 1;
+ }
+ }
+ split->splits[current_pos] = NULL;
+ split->split_count = current_pos;
+
+ return ISC_R_SUCCESS;
+
+cleanup:
+ return result;
+}
+
+size_t
+str_split_count(const ld_split_t *split)
+{
+ REQUIRE(split != NULL);
+
+ return split->split_count;
+}
+
+const char *
+str_split_get(const ld_split_t *split, unsigned int split_number)
+{
+ REQUIRE(split != NULL);
+ REQUIRE(split->split_count >= split_number);
+
+ return split->splits[split_number];
+}
diff --git a/src/str.h b/src/str.h
new file mode 100644
index 0000000..4fdf177
--- /dev/null
+++ b/src/str.h
@@ -0,0 +1,76 @@
+/* Authors: Martin Nagy <mnagy@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
+ */
+
+#ifndef _LD_STR_H_
+#define _LD_STR_H_
+
+#include <isc/mem.h>
+
+#define LD_MAX_SPLITS 256
+
+#if ISC_MEM_TRACKLINES
+#define _STR_MEM_FILELINE , __FILE__, __LINE__
+#define _STR_MEM_FLARG , const char *file, int line
+#define _STR_MEM_FLARG_PASS , file, line
+#else
+#define _STR_MEM_FILELINE
+#define _STR_MEM_FLAG
+#define _STR_MEM_FLARG_PASS
+#endif
+
+typedef struct ld_string ld_string_t;
+typedef struct ld_split ld_split_t;
+
+/*
+ * Public functions.
+ */
+
+#define str_new(m, s) str__new((m), (s) _STR_MEM_FILELINE)
+#define str_destroy(s) str__destroy((s) _STR_MEM_FILELINE)
+
+size_t str_len(const ld_string_t *str);
+const char * str_buf(const ld_string_t *src);
+isc_result_t str_copy(ld_string_t *dest, const ld_string_t *src);
+isc_result_t str_clone(ld_string_t **dest, const ld_string_t *src _STR_MEM_FLARG);
+void str_clear(ld_string_t *dest);
+isc_result_t str_init_char(ld_string_t *dest, const char *src);
+isc_result_t str_cat_char(ld_string_t *dest, const char *src);
+isc_result_t str_cat_char_len(ld_string_t *dest, const char *src, size_t len);
+isc_result_t str_cat_isc_region(ld_string_t *dest, const isc_region_t *region);
+isc_result_t str_cat_isc_buffer(ld_string_t *dest, const isc_buffer_t *buffer);
+isc_result_t str_cat(ld_string_t *dest, const ld_string_t *src);
+isc_result_t str_sprintf(ld_string_t *dest, const char *format, ...);
+isc_result_t str_vsprintf(ld_string_t *dest, const char *format, va_list ap);
+void str_toupper(ld_string_t *str);
+
+void str_to_isc_buffer(const ld_string_t *src, isc_buffer_t *dest);
+
+int str_casecmp_char(const ld_string_t *s1, const char *s2);
+
+isc_result_t str_new_split(isc_mem_t *mctx, ld_split_t **splitp);
+void str_destroy_split(ld_split_t **splitp);
+isc_result_t str_split(const ld_string_t *src, const char delimiter, ld_split_t *split);
+size_t str_split_count(const ld_split_t *split);
+const char * str_split_get(const ld_split_t *split, unsigned int split_number);
+
+/* These are pseudo-private functions and shouldn't be called directly. */
+isc_result_t str__new(isc_mem_t *mctx, ld_string_t **new_str _STR_MEM_FLARG);
+void str__destroy(ld_string_t **str _STR_MEM_FLARG);
+
+#endif /* !_LD_STR_H_ */
diff --git a/src/util.h b/src/util.h
new file mode 100644
index 0000000..372cd4d
--- /dev/null
+++ b/src/util.h
@@ -0,0 +1,92 @@
+/* Authors: Martin Nagy <mnagy@redhat.com>
+ *
+ * Copyright (C) 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
+ */
+
+#ifndef _LD_UTIL_H_
+#define _LD_UTIL_H_
+
+#define CHECK(op) \
+ do { result = (op); \
+ if (result != ISC_R_SUCCESS) goto cleanup; \
+ } while (0)
+
+#define CHECKED_MEM_ALLOCATE(m, target_ptr, s) \
+ do { \
+ (target_ptr) = isc_mem_allocate((m), (s)); \
+ if ((target_ptr) == NULL) { \
+ result = ISC_R_NOMEMORY; \
+ goto cleanup; \
+ } \
+ } while (0)
+
+#define CHECKED_MEM_GET(m, target_ptr, s) \
+ do { \
+ (target_ptr) = isc_mem_get((m), (s)); \
+ if ((target_ptr) == NULL) { \
+ result = ISC_R_NOMEMORY; \
+ goto cleanup; \
+ } \
+ } while (0)
+
+#define CHECKED_MEM_GET_PTR(m, target_ptr) \
+ CHECKED_MEM_GET(m, target_ptr, sizeof(*(target_ptr)))
+
+#define CHECKED_MEM_STRDUP(m, source, target) \
+ do { \
+ (target) = isc_mem_strdup((m), (source)); \
+ if ((target) == NULL) { \
+ result = ISC_R_NOMEMORY; \
+ goto cleanup; \
+ } \
+ } while (0)
+
+#define ZERO_PTR(ptr) memset((ptr), 0, sizeof(*(ptr)))
+
+#define SAFE_MEM_PUT(m, target_ptr, target_size) \
+ do { \
+ if ((target_ptr) != NULL) \
+ isc_mem_put((m), (target_ptr), \
+ (target_size)); \
+ } while (0)
+
+#define SAFE_MEM_PUT_PTR(m, target_ptr) \
+ SAFE_MEM_PUT((m), (target_ptr), sizeof(*(target_ptr)))
+
+#define MEM_PUT_AND_DETACH(target_ptr) \
+ isc_mem_putanddetach(&(target_ptr)->mctx, target_ptr, \
+ sizeof(*(target_ptr)))
+
+#define DECLARE_BUFFER(name, len) \
+ isc_buffer_t name; \
+ unsigned char name##__base[len]
+
+#define INIT_BUFFER(name) \
+ isc_buffer_init(&name, name##__base, sizeof(name##__base))
+
+#define DECLARE_BUFFERED_NAME(name) \
+ dns_name_t name; \
+ DECLARE_BUFFER(name##__buffer, DNS_NAME_MAXWIRE)
+
+#define INIT_BUFFERED_NAME(name) \
+ do { \
+ INIT_BUFFER(name##__buffer); \
+ dns_name_init(&name, NULL); \
+ dns_name_setbuffer(&name, &name##__buffer); \
+ } while (0)
+
+#endif /* !_LD_UTIL_H_ */
diff --git a/src/zone_manager.c b/src/zone_manager.c
new file mode 100644
index 0000000..9b1ae69
--- /dev/null
+++ b/src/zone_manager.c
@@ -0,0 +1,209 @@
+/* Authors: Martin Nagy <mnagy@redhat.com>
+ *
+ * Copyright (C) 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/mem.h>
+#include <isc/once.h>
+#include <isc/result.h>
+#include <isc/util.h>
+
+#include <dns/view.h>
+#include <dns/zone.h>
+
+#include "ldap_convert.h"
+#include "ldap_helper.h"
+#include "log.h"
+#include "util.h"
+#include "zone_manager.h"
+
+struct db_instance {
+ isc_mem_t *mctx;
+ char *name;
+ ldap_db_t *ldap_db;
+ ldap_cache_t *ldap_cache;
+ dns_zonemgr_t *dns_zone_manager;
+ LINK(db_instance_t) link;
+};
+
+static isc_once_t initialize_once = ISC_ONCE_INIT;
+static isc_mutex_t instance_list_lock;
+static LIST(db_instance_t) instance_list;
+
+static void initialize_manager(void);
+static void destroy_db_instance(db_instance_t **db_instp);
+static isc_result_t find_db_instance(const char *name, db_instance_t **instance);
+
+
+static void
+initialize_manager(void)
+{
+ INIT_LIST(instance_list);
+ isc_mutex_init(&instance_list_lock);
+}
+
+void
+destroy_manager(void)
+{
+ db_instance_t *db_inst;
+ db_instance_t *next;
+
+ isc_once_do(&initialize_once, initialize_manager);
+
+ LOCK(&instance_list_lock);
+ db_inst = HEAD(instance_list);
+ while (db_inst != NULL) {
+ next = NEXT(db_inst, link);
+ UNLINK(instance_list, db_inst, link);
+ destroy_db_instance(&db_inst);
+ db_inst = next;
+ }
+ UNLOCK(&instance_list_lock);
+}
+
+static void
+destroy_db_instance(db_instance_t **db_instp)
+{
+ db_instance_t *db_inst;
+
+ REQUIRE(db_instp != NULL && *db_instp != NULL);
+
+ db_inst = *db_instp;
+
+ destroy_ldap_db(&db_inst->ldap_db);
+ destroy_ldap_cache(&db_inst->ldap_cache);
+ if (db_inst->name != NULL)
+ isc_mem_free(db_inst->mctx, db_inst->name);
+
+ isc_mem_putanddetach(&db_inst->mctx, db_inst, sizeof(*db_inst));
+
+ *db_instp = NULL;
+}
+
+isc_result_t
+manager_add_db_instance(isc_mem_t *mctx, const char *name, ldap_db_t *ldap_db,
+ ldap_cache_t *ldap_cache, dns_zonemgr_t *zmgr)
+{
+ isc_result_t result;
+ db_instance_t *db_inst;
+
+ REQUIRE(mctx != NULL);
+ REQUIRE(name != NULL);
+ REQUIRE(ldap_db != NULL);
+ REQUIRE(ldap_cache != NULL);
+ REQUIRE(zmgr != NULL);
+
+ isc_once_do(&initialize_once, initialize_manager);
+
+ db_inst = NULL;
+
+ result = find_db_instance(name, &db_inst);
+ if (result == ISC_R_SUCCESS) {
+ db_inst = NULL;
+ result = ISC_R_FAILURE;
+ log_error("'%s' already exists", name);
+ goto cleanup;
+ } else {
+ result = ISC_R_SUCCESS;
+ }
+
+ CHECKED_MEM_GET_PTR(mctx, db_inst);
+ CHECKED_MEM_STRDUP(mctx, name, db_inst->name);
+ db_inst->mctx = NULL;
+ isc_mem_attach(mctx, &db_inst->mctx);
+ db_inst->ldap_db = ldap_db;
+ db_inst->ldap_cache = ldap_cache;
+ db_inst->dns_zone_manager = zmgr;
+
+ LOCK(&instance_list_lock);
+ APPEND(instance_list, db_inst, link);
+ UNLOCK(&instance_list_lock);
+
+ refresh_zones_from_ldap(ldap_db, name, zmgr);
+
+ return ISC_R_SUCCESS;
+
+cleanup:
+ if (db_inst != NULL)
+ destroy_db_instance(&db_inst);
+
+ return result;
+}
+
+void
+manager_refresh_zones(void)
+{
+ db_instance_t *db_inst;
+
+ LOCK(&instance_list_lock);
+ db_inst = HEAD(instance_list);
+ while (db_inst != NULL) {
+ refresh_zones_from_ldap(db_inst->ldap_db, db_inst->name,
+ db_inst->dns_zone_manager);
+ db_inst = NEXT(db_inst, link);
+ }
+
+ UNLOCK(&instance_list_lock);
+}
+
+isc_result_t
+manager_get_ldap_db_and_cache(const char *name, ldap_db_t **ldap_db,
+ ldap_cache_t **ldap_cache)
+{
+ isc_result_t result;
+ db_instance_t *db_inst;
+
+ REQUIRE(name != NULL);
+ REQUIRE(ldap_db != NULL);
+ REQUIRE(ldap_cache != NULL);
+
+ isc_once_do(&initialize_once, initialize_manager);
+
+ db_inst = NULL;
+ CHECK(find_db_instance(name, &db_inst));
+
+ *ldap_db = db_inst->ldap_db;
+ *ldap_cache = db_inst->ldap_cache;
+
+cleanup:
+ return result;
+}
+
+static isc_result_t
+find_db_instance(const char *name, db_instance_t **instance)
+{
+ db_instance_t *iterator;
+
+ REQUIRE(name != NULL);
+ REQUIRE(instance != NULL && *instance == NULL);
+
+ LOCK(&instance_list_lock);
+ iterator = HEAD(instance_list);
+ while (iterator != NULL) {
+ if (strcmp(name, iterator->name) == 0)
+ break;
+ iterator = NEXT(iterator, link);
+ }
+ UNLOCK(&instance_list_lock);
+
+ if (iterator != NULL) {
+ *instance = iterator;
+ return ISC_R_SUCCESS;
+ }
+
+ return ISC_R_NOTFOUND;
+}
diff --git a/src/zone_manager.h b/src/zone_manager.h
new file mode 100644
index 0000000..353cb85
--- /dev/null
+++ b/src/zone_manager.h
@@ -0,0 +1,40 @@
+/* Authors: Martin Nagy <mnagy@redhat.com>
+ *
+ * Copyright (C) 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
+ */
+
+#ifndef _LD_ZONE_MANAGER_H_
+#define _LD_ZONE_MANAGER_H_
+
+#include <dns/types.h>
+
+#include "cache.h"
+#include "ldap_helper.h"
+
+typedef struct db_instance db_instance_t;
+
+void destroy_manager(void);
+
+isc_result_t
+manager_add_db_instance(isc_mem_t *mctx, const char *name, ldap_db_t *ldap_db,
+ ldap_cache_t *ldap_cache, dns_zonemgr_t *zmgr);
+
+isc_result_t
+manager_get_ldap_db_and_cache(const char *name, ldap_db_t **ldap_db,
+ ldap_cache_t **ldap_cache);
+
+#endif /* !_LD_ZONE_MANAGER_H_ */