From a148a9f5f2659c60a534f82d0ec8736f90afa223 Mon Sep 17 00:00:00 2001 From: Martin Nagy Date: Mon, 30 Mar 2009 13:01:56 +0200 Subject: Prepare the tree for conversion to autoconf. All source files are now moved to src/ and the old Makefile is removed. --- Makefile | 20 - cache.c | 249 -------- cache.h | 58 -- ldap_convert.c | 278 --------- ldap_convert.h | 45 -- ldap_driver.c | 1076 --------------------------------- ldap_helper.c | 1707 ---------------------------------------------------- ldap_helper.h | 105 ---- log.c | 71 --- log.h | 48 -- rdlist.c | 138 ----- rdlist.h | 32 - semaphore.c | 108 ---- semaphore.h | 45 -- settings.c | 176 ------ settings.h | 73 --- src/cache.c | 249 ++++++++ src/cache.h | 58 ++ src/ldap_convert.c | 278 +++++++++ src/ldap_convert.h | 45 ++ src/ldap_driver.c | 1076 +++++++++++++++++++++++++++++++++ src/ldap_helper.c | 1707 ++++++++++++++++++++++++++++++++++++++++++++++++++++ src/ldap_helper.h | 105 ++++ src/log.c | 71 +++ src/log.h | 48 ++ src/rdlist.c | 138 +++++ src/rdlist.h | 32 + src/semaphore.c | 108 ++++ src/semaphore.h | 45 ++ src/settings.c | 176 ++++++ src/settings.h | 73 +++ src/str.c | 591 ++++++++++++++++++ src/str.h | 76 +++ src/util.h | 92 +++ src/zone_manager.c | 209 +++++++ src/zone_manager.h | 40 ++ str.c | 591 ------------------ str.h | 76 --- util.h | 92 --- zone_manager.c | 209 ------- zone_manager.h | 40 -- 41 files changed, 5217 insertions(+), 5237 deletions(-) delete mode 100644 Makefile delete mode 100644 cache.c delete mode 100644 cache.h delete mode 100644 ldap_convert.c delete mode 100644 ldap_convert.h delete mode 100644 ldap_driver.c delete mode 100644 ldap_helper.c delete mode 100644 ldap_helper.h delete mode 100644 log.c delete mode 100644 log.h delete mode 100644 rdlist.c delete mode 100644 rdlist.h delete mode 100644 semaphore.c delete mode 100644 semaphore.h delete mode 100644 settings.c delete mode 100644 settings.h create mode 100644 src/cache.c create mode 100644 src/cache.h create mode 100644 src/ldap_convert.c create mode 100644 src/ldap_convert.h create mode 100644 src/ldap_driver.c create mode 100644 src/ldap_helper.c create mode 100644 src/ldap_helper.h create mode 100644 src/log.c create mode 100644 src/log.h create mode 100644 src/rdlist.c create mode 100644 src/rdlist.h create mode 100644 src/semaphore.c create mode 100644 src/semaphore.h create mode 100644 src/settings.c create mode 100644 src/settings.h create mode 100644 src/str.c create mode 100644 src/str.h create mode 100644 src/util.h create mode 100644 src/zone_manager.c create mode 100644 src/zone_manager.h delete mode 100644 str.c delete mode 100644 str.h delete mode 100644 util.h delete mode 100644 zone_manager.c delete mode 100644 zone_manager.h diff --git a/Makefile b/Makefile deleted file mode 100644 index ceb4bcf..0000000 --- a/Makefile +++ /dev/null @@ -1,20 +0,0 @@ -.PHONY: all clean - -LIBMAJOR = 1 -LIBMINOR = 0 - -LIBNAME = libdnsldap.so.$(LIBMAJOR).$(LIBMINOR).0 -LIBSONAME = libdnsldap.so.$(LIBMAJOR) -OBJS = cache.o ldap_driver.o semaphore.o ldap_convert.o ldap_helper.o log.o -OBJS += rdlist.o settings.o str.o zone_manager.o - -CFLAGS := -Wall -Wextra -pedantic -std=c99 -g -fPIC $(CFLAGS) - - -all: $(LIBNAME) - -clean: - rm -f $(LIBNAME) *.o - -$(LIBNAME): $(OBJS) - $(CC) -ldns -lldap -shared -Wl,-soname,$(LIBSONAME) $+ -o $@ diff --git a/cache.c b/cache.c deleted file mode 100644 index fa57563..0000000 --- a/cache.c +++ /dev/null @@ -1,249 +0,0 @@ -/* Authors: Martin Nagy - * - * 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 -#include -#include -#include -#include - -#include -#include - -#include - -#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/cache.h b/cache.h deleted file mode 100644 index a8f0558..0000000 --- a/cache.h +++ /dev/null @@ -1,58 +0,0 @@ -/* Authors: Martin Nagy - * - * 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/ldap_convert.c b/ldap_convert.c deleted file mode 100644 index 24da32f..0000000 --- a/ldap_convert.c +++ /dev/null @@ -1,278 +0,0 @@ -/* Authors: Martin Nagy - * - * 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 -#include -#include - -#include -#include -#include -#include - -#define LDAP_DEPRECATED 1 -#include - -#include -#include - -#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 *)®ion); - 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/ldap_convert.h b/ldap_convert.h deleted file mode 100644 index d35144a..0000000 --- a/ldap_convert.h +++ /dev/null @@ -1,45 +0,0 @@ -/* Authors: Martin Nagy - * - * 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 - -#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/ldap_driver.c b/ldap_driver.c deleted file mode 100644 index 8003c85..0000000 --- a/ldap_driver.c +++ /dev/null @@ -1,1076 +0,0 @@ -/* Authors: Martin Nagy - * Adam Tkac - * - * 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 -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#include /* 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/ldap_helper.c b/ldap_helper.c deleted file mode 100644 index 427191a..0000000 --- a/ldap_helper.c +++ /dev/null @@ -1,1707 +0,0 @@ -/* Authors: Martin Nagy - * Adam Tkac - * - * 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 -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#define LDAP_DEPRECATED 1 -#include -#include -#include -#include -#include - -#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, ®ion); - - /* 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/ldap_helper.h b/ldap_helper.h deleted file mode 100644 index a0e4aea..0000000 --- a/ldap_helper.h +++ /dev/null @@ -1,105 +0,0 @@ -/* Authors: Martin Nagy - * Adam Tkac - * - * 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 - -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/log.c b/log.c deleted file mode 100644 index 216a602..0000000 --- a/log.c +++ /dev/null @@ -1,71 +0,0 @@ -/* Authors: Martin Nagy - * - * 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 - -#include - -#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/log.h b/log.h deleted file mode 100644 index 00f772d..0000000 --- a/log.h +++ /dev/null @@ -1,48 +0,0 @@ -/* Authors: Martin Nagy - * - * 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 - -#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/rdlist.c b/rdlist.c deleted file mode 100644 index 48b2a0b..0000000 --- a/rdlist.c +++ /dev/null @@ -1,138 +0,0 @@ -/* Authors: Adam Tkac - * Martin Nagy - * - * 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 -#include -#include - -#include -#include - -#include - -#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/rdlist.h b/rdlist.h deleted file mode 100644 index d3ce2d0..0000000 --- a/rdlist.h +++ /dev/null @@ -1,32 +0,0 @@ -/* Authors: Adam Tkac - * Martin Nagy - * - * 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/semaphore.c b/semaphore.c deleted file mode 100644 index 33ae9f8..0000000 --- a/semaphore.c +++ /dev/null @@ -1,108 +0,0 @@ -/* Authors: Martin Nagy - * - * 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 -#include -#include - -#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/semaphore.h b/semaphore.h deleted file mode 100644 index d417443..0000000 --- a/semaphore.h +++ /dev/null @@ -1,45 +0,0 @@ -/* Authors: Martin Nagy - * - * 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 -#include - -/* - * 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/settings.c b/settings.c deleted file mode 100644 index f217076..0000000 --- a/settings.c +++ /dev/null @@ -1,176 +0,0 @@ -/* Authors: Martin Nagy - * - * 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 -#include -#include - -#include -#include - -#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/settings.h b/settings.h deleted file mode 100644 index fce1b2a..0000000 --- a/settings.h +++ /dev/null @@ -1,73 +0,0 @@ -/* Authors: Martin Nagy - * - * 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/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 + * + * 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 +#include +#include +#include +#include + +#include +#include + +#include + +#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 + * + * 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 + * + * 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 +#include +#include + +#include +#include +#include +#include + +#define LDAP_DEPRECATED 1 +#include + +#include +#include + +#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 *)®ion); + 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 + * + * 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 + +#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 + * Adam Tkac + * + * 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 +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include /* 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 + * Adam Tkac + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#define LDAP_DEPRECATED 1 +#include +#include +#include +#include +#include + +#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, ®ion); + + /* 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 + * Adam Tkac + * + * 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 + +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 + * + * 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 + +#include + +#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 + * + * 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 + +#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 + * Martin Nagy + * + * 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 +#include +#include + +#include +#include + +#include + +#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 + * Martin Nagy + * + * 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 + * + * 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 +#include +#include + +#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 + * + * 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 +#include + +/* + * 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 + * + * 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 +#include +#include + +#include +#include + +#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 + * + * 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 + * + * 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 +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include + +#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, ®ion); + + return str_cat_isc_region(dest, ®ion); +} + +/* + * 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 + * + * 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 + +#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 + * + * 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 + * + * 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 +#include +#include +#include + +#include +#include + +#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 + * + * 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 + +#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_ */ diff --git a/str.c b/str.c deleted file mode 100644 index f5908c9..0000000 --- a/str.c +++ /dev/null @@ -1,591 +0,0 @@ -/* Authors: Martin Nagy - * - * 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 -#include -#include -#include -#include - -#include - -#include -#include -#include -#include -#include -#include - -#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, ®ion); - - return str_cat_isc_region(dest, ®ion); -} - -/* - * 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/str.h b/str.h deleted file mode 100644 index 4fdf177..0000000 --- a/str.h +++ /dev/null @@ -1,76 +0,0 @@ -/* Authors: Martin Nagy - * - * 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 - -#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/util.h b/util.h deleted file mode 100644 index 372cd4d..0000000 --- a/util.h +++ /dev/null @@ -1,92 +0,0 @@ -/* Authors: Martin Nagy - * - * 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/zone_manager.c b/zone_manager.c deleted file mode 100644 index 9b1ae69..0000000 --- a/zone_manager.c +++ /dev/null @@ -1,209 +0,0 @@ -/* Authors: Martin Nagy - * - * 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 -#include -#include -#include - -#include -#include - -#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/zone_manager.h b/zone_manager.h deleted file mode 100644 index 353cb85..0000000 --- a/zone_manager.h +++ /dev/null @@ -1,40 +0,0 @@ -/* Authors: Martin Nagy - * - * 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 - -#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_ */ -- cgit