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. --- 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 ++ 20 files changed, 5217 insertions(+) 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 (limited to 'src') 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_ */ -- cgit