[Freeipa-devel] [PATCH 0077] Refactor settings subsystem
Adam Tkac
atkac at redhat.com
Mon Mar 4 13:58:22 UTC 2013
On Tue, Feb 19, 2013 at 02:34:49PM +0100, Petr Spacek wrote:
> On 8.10.2012 15:29, Petr Spacek wrote:
> >Hello,
> >
> >this patch refactors setting subsystem. After some experimenting I chosen
> >simple implementation with static arrays, no RBT trees involved. Speed can be
> >improved by reordering items in arrays.
> >
> >Commit message:
> >
> > Refactor settings subsystem.
> >
> > Settings are stored in tree of settings_set_t structures.
> > All settings should be accessed only through setting* functions.
> > Mutual exclusion during write is done by switching to single
> > thread mode.
> >
> > Setting_get() function doesn't copy strings, so changing the
> > original string can lead to obscure bugs.
> > This way is okay as long as strings are not changed dynamically
> > at run-time.
> >
> > Unknown setting in configuration file leads to failure rather than
> > silent ignoring it.
> >
> > https://fedorahosted.org/bind-dyndb-ldap/ticket/53
> > https://fedorahosted.org/bind-dyndb-ldap/ticket/81
>
> Re-based & amended & tested patch is attached. The main idea is the same.
>
> >Adam, I'm still looking for way how to handle strings in settings. We have to
> >prevent string change/deallocation as long as somebody has a pointer to the
> >string (I mean pointer obtained through setting_get("name_of_setting") call).
> >
> >The only way which I can see is returning setting_string structure like
> >
> >setting_string struct {
> > isc_refcount_t counter;
> > isc_mem_t *mctx;
> > char *str;
> >};
>
> After some poking to isc_task_beginexclusive() I consider the
> "struct setting_string" idea redundant.
>
> >Caller has to call setting_string_free() when string can be freed.
> >Setting_string_free() will decrement counter by one and free whole structure
> >if and only if counter reaches 0.
> >
> >Is it meaningful? Should I separate setting_get_* for each datatype? Or just
> >for setting_get_string() and let setting_get() universal for integers and
> >booleans?
>
> I separated setting_get_bool/uint/str from the universal
> setting_get() to enable type checks.
Hi Peter, check my comments below, please. I found just some minor issues, the
patch overally looks fine.
Regards, Adam
> From ecff7be859f7c53c167b7ccd4d4d0cc2dfc990a6 Mon Sep 17 00:00:00 2001
> From: Petr Spacek <pspacek at redhat.com>
> Date: Thu, 4 Oct 2012 16:44:16 +0200
> Subject: [PATCH] Refactor settings subsystem.
>
> Settings are stored in tree of settings_set_t structures.
> All settings should be accessed only through setting* functions.
> Mutual exclusion during write is done by switching to single
> thread mode.
>
> setting_get_str() function doesn't copy strings, so original strings
> have to stay unchanged.
>
> Unknown setting and duplicate settings in configuration file
> lead to failure rather than silent ignoring them.
>
> https://fedorahosted.org/bind-dyndb-ldap/ticket/53
> https://fedorahosted.org/bind-dyndb-ldap/ticket/81
>
> Signed-off-by: Petr Spacek <pspacek at redhat.com>
> ---
> src/cache.c | 32 +-
> src/cache.h | 4 +-
> src/ldap_entry.c | 6 +-
> src/ldap_entry.h | 2 +-
> src/ldap_helper.c | 783 +++++++++++++++++++++++++++---------------------
> src/ldap_helper.h | 5 +
> src/settings.c | 843 ++++++++++++++++++++++++++++++++++++++--------------
> src/settings.h | 86 ++++--
> src/zone_manager.c | 27 +-
> src/zone_register.c | 82 ++++-
> src/zone_register.h | 9 +-
> 11 files changed, 1243 insertions(+), 636 deletions(-)
> rewrite src/settings.c (60%)
>
> diff --git a/src/cache.c b/src/cache.c
> index fd57fd92eb35dcbbd5c1e1911a93730624e30002..b475d2319137845b7f59fa6d15b21847199554cd 100644
> --- a/src/cache.c
> +++ b/src/cache.c
> @@ -38,11 +38,12 @@
> #include "util.h"
>
> struct ldap_cache {
> - isc_mutex_t mutex; /* TODO: RWLOCK? */
> - isc_mem_t *mctx;
> - dns_rbt_t *rbt;
> - const isc_interval_t *cache_ttl; /* pointer to LDAP instance */
> - const isc_boolean_t *psearch; /* pointer to LDAP instance */
> + isc_mutex_t mutex; /* TODO: RWLOCK? */
> + isc_mem_t *mctx;
> + dns_rbt_t *rbt;
> + /* Values for "fast path" - copied from settings_set_t structure. */
> + isc_interval_t cache_ttl;
> + isc_boolean_t psearch;
> };
>
> typedef struct {
> @@ -78,9 +79,9 @@ cache_node_create(ldap_cache_t *cache, cache_node_t **nodep)
> isc_mem_attach(cache->mctx, &node->mctx);
> ZERO_PTR(&node->rdatalist);
> /* Do not set the ttl when psearch is enabled. */
> - if (*cache->psearch == ISC_FALSE)
> + if (cache->psearch == ISC_FALSE)
> CHECK(isc_time_nowplusinterval(&node->valid_until,
> - cache->cache_ttl));
> + &cache->cache_ttl));
>
> *nodep = node;
> return ISC_R_SUCCESS;
> @@ -91,33 +92,28 @@ cleanup:
> return result;
> }
>
> -/**
> - * @param[in] cache_ttl ISC interval in LDAP instance shared by all caches
> - * @param[in] psearch boolean in LDAP instance shared by all caches
> - */
> isc_result_t
> -new_ldap_cache(isc_mem_t *mctx, const isc_interval_t *cache_ttl,
> - const isc_boolean_t *psearch, ldap_cache_t **cachep)
> +new_ldap_cache(isc_mem_t *mctx, settings_set_t *set, ldap_cache_t **cachep)
> {
> isc_result_t result;
> ldap_cache_t *cache = NULL;
> + isc_uint32_t cache_ttl_int;
>
> - REQUIRE(cache_ttl != NULL);
> - REQUIRE(psearch != NULL);
> REQUIRE(cachep != NULL && *cachep == NULL);
>
> CHECKED_MEM_GET_PTR(mctx, cache);
> ZERO_PTR(cache);
> isc_mem_attach(mctx, &cache->mctx);
>
> - cache->cache_ttl = cache_ttl;
> - if (!isc_interval_iszero(cache_ttl)) {
> + CHECK(setting_get_bool("psearch", set, &cache->psearch));
> + CHECK(setting_get_uint("cache_ttl", set, &cache_ttl_int));
> + isc_interval_set(&cache->cache_ttl, cache_ttl_int, 0);
> + if (cache_ttl_int) {
> CHECK(dns_rbt_create(mctx, cache_node_deleter, NULL,
> &cache->rbt));
> CHECK(isc_mutex_init(&cache->mutex));
> }
>
> - cache->psearch = psearch;
> *cachep = cache;
> return ISC_R_SUCCESS;
>
> diff --git a/src/cache.h b/src/cache.h
> index 7c7e69b305d3021f154ebb17d5b879ba8f34590e..567d176da0e9e31e96fdae869c10643cdc03c27c 100644
> --- a/src/cache.h
> +++ b/src/cache.h
> @@ -23,15 +23,15 @@
> #define _LD_CACHE_H_
>
> #include "types.h"
> +#include "settings.h"
>
> typedef struct ldap_cache ldap_cache_t;
>
> /*
> * Create a new cache.
> */
> isc_result_t
> -new_ldap_cache(isc_mem_t *mctx, const isc_interval_t *cache_ttl,
> - const isc_boolean_t *psearch, ldap_cache_t **cachep);
> +new_ldap_cache(isc_mem_t *mctx, settings_set_t *set, ldap_cache_t **cachep);
>
> /*
> * Free all resources used up by the cache.
> diff --git a/src/ldap_entry.c b/src/ldap_entry.c
> index 509995cd682956dc9afb58e04716f8bb63014f09..d32dc86ecc3af4866105bc96a6012d0ee964f4f5 100644
> --- a/src/ldap_entry.c
> +++ b/src/ldap_entry.c
> @@ -349,7 +349,7 @@ ldap_entry_nextrdtype(ldap_entry_t *entry, ldap_attribute_t **attrp,
> }
>
> isc_result_t
> -ldap_entry_getfakesoa(ldap_entry_t *entry, const ld_string_t *fake_mname,
> +ldap_entry_getfakesoa(ldap_entry_t *entry, const char *fake_mname,
> ld_string_t *target)
> {
> isc_result_t result = ISC_R_NOTFOUND;
> @@ -367,9 +367,9 @@ ldap_entry_getfakesoa(ldap_entry_t *entry, const ld_string_t *fake_mname,
> REQUIRE(target != NULL);
>
> str_clear(target);
> - if (str_len(fake_mname) > 0) {
> + if (strlen(fake_mname) > 0) {
> i = 1;
> - CHECK(str_cat(target, fake_mname));
> + CHECK(str_cat_char(target, fake_mname));
> CHECK(str_cat_char(target, " "));
> }
> for (; soa_attrs[i] != NULL; i++) {
> diff --git a/src/ldap_entry.h b/src/ldap_entry.h
> index 02923a8735c20bc318fdf011b434127c9c52a53b..5a027e672b7591ae57551c175764e7517acea758 100644
> --- a/src/ldap_entry.h
> +++ b/src/ldap_entry.h
> @@ -107,7 +107,7 @@ ldap_entry_nextrdtype(ldap_entry_t *entry, ldap_attribute_t **attrp,
> dns_rdatatype_t *rdtype);
>
> isc_result_t
> -ldap_entry_getfakesoa(ldap_entry_t *entry, const ld_string_t *fake_mname,
> +ldap_entry_getfakesoa(ldap_entry_t *entry, const char *fake_mname,
> ld_string_t *target);
>
> /*
> diff --git a/src/ldap_helper.c b/src/ldap_helper.c
> index 71b96ce5eef2a249f7d16c448c70e2c2068d271d..25a4d125adce57a533d53ea6bf71bd062aa1a8a0 100644
> --- a/src/ldap_helper.c
> +++ b/src/ldap_helper.c
> @@ -157,32 +157,13 @@ struct ldap_instance {
> /* krb5 kinit mutex */
> isc_mutex_t kinit_lock;
>
> - /* Settings. */
> - ld_string_t *uri;
> - ld_string_t *base;
> - unsigned int connections;
> - unsigned int reconnect_interval;
> - unsigned int timeout;
> - ldap_auth_t auth_method;
> - ld_string_t *bind_dn;
> - ld_string_t *password;
> - ld_string_t *krb5_principal;
> - ld_string_t *sasl_mech;
> - ld_string_t *sasl_user;
> - ld_string_t *sasl_auth_name;
> - ld_string_t *sasl_realm;
> - ld_string_t *sasl_password;
> - ld_string_t *krb5_keytab;
> - ld_string_t *fake_mname;
> - isc_boolean_t psearch;
> - isc_interval_t cache_ttl;
> - ld_string_t *ldap_hostname;
> isc_task_t *task;
> isc_thread_t watcher;
> isc_boolean_t exiting;
> - isc_boolean_t sync_ptr;
> - isc_boolean_t dyn_update;
> - isc_boolean_t serial_autoincrement;
> +
> + /* Settings. */
> + settings_set_t *local_settings;
> + settings_set_t *global_settings;
> dns_forwarders_t orig_global_forwarders; /* from named.conf */
> };
>
> @@ -250,6 +231,47 @@ struct ldap_psearchevent {
> int chgtype;
> };
>
> +extern const settings_set_t const settings_default_set;
> +
> +/** Local configuration file */
> +static const setting_t settings_local_default[] = {
> + { "uri", no_default_string },
> + { "connections", no_default_uint },
> + { "reconnect_interval", no_default_uint },
> + { "timeout", no_default_uint },
> + { "cache_ttl", no_default_uint },
> + { "base", no_default_string },
> + { "auth_method", no_default_string },
> + { "auth_method_enum", no_default_uint },
> + { "bind_dn", no_default_string },
> + { "password", no_default_string },
> + { "krb5_principal", no_default_string },
> + { "sasl_mech", no_default_string },
> + { "sasl_user", no_default_string },
> + { "sasl_auth_name", no_default_string },
> + { "sasl_realm", no_default_string },
> + { "sasl_password", no_default_string },
> + { "krb5_keytab", no_default_string },
> + { "fake_mname", no_default_string },
> + { "zone_refresh", no_default_uint },
> + { "psearch", no_default_boolean },
> + { "ldap_hostname", no_default_string },
> + { "sync_ptr", no_default_boolean },
> + { "dyn_update", no_default_boolean },
> + { "serial_autoincrement", no_default_boolean },
> + { "verbose_checks", no_default_boolean },
> + end_of_settings
> +};
> +
> +/** Global settings from idnsConfig object. */
> +static setting_t settings_global_default[] = {
> + { "dyn_update", no_default_boolean },
> + { "sync_ptr", no_default_boolean },
> + { "zone_refresh", no_default_uint },
> +/* { "psearch", no_default_boolean }, unsupported */
> + end_of_settings
> +};
> +
> /*
> * Forward declarations.
> */
> @@ -264,14 +286,14 @@ static isc_result_t findrdatatype_or_create(isc_mem_t *mctx,
> dns_rdatatype_t rdtype, dns_ttl_t ttl, dns_rdatalist_t **rdlistp);
> static isc_result_t add_soa_record(isc_mem_t *mctx, ldap_qresult_t *qresult,
> dns_name_t *origin, ldap_entry_t *entry,
> - ldapdb_rdatalist_t *rdatalist, const ld_string_t *fake_mname);
> + ldapdb_rdatalist_t *rdatalist, const char *fake_mname);
> static isc_result_t parse_rdata(isc_mem_t *mctx, ldap_qresult_t *qresult,
> dns_rdataclass_t rdclass, dns_rdatatype_t rdtype,
> dns_name_t *origin, const char *rdata_text,
> dns_rdata_t **rdatap);
> static isc_result_t ldap_parse_rrentry(isc_mem_t *mctx, ldap_entry_t *entry,
> ldap_qresult_t *qresult, dns_name_t *origin,
> - const ld_string_t *fake_mname, ld_string_t *buf,
> + const char *fake_mname, ld_string_t *buf,
> ldapdb_rdatalist_t *rdatalist);
> static inline isc_result_t ldap_get_zone_serial(ldap_instance_t *inst,
> dns_name_t *zone_name, isc_uint32_t *serial);
> @@ -332,43 +354,136 @@ static void psearch_update(ldap_instance_t *inst, ldap_entry_t *entry,
> static isc_threadresult_t
> ldap_psearch_watcher(isc_threadarg_t arg);
>
> +#define PRINT_BUFF_SIZE 10 /* for unsigned int 2^32 */
> +isc_result_t
> +validate_local_instance_settings(ldap_instance_t *inst, settings_set_t *set) {
> + isc_result_t result;
> +
> + isc_boolean_t psearch;
> + isc_boolean_t serial_autoincrement;
> + isc_uint32_t uint;
> + const char *sasl_mech = NULL;
> + const char *sasl_user = NULL;
> + const char *krb5_principal = NULL;
> + ld_string_t *buff = NULL;
> +
> + char print_buff[PRINT_BUFF_SIZE];
> + const char *auth_method_str = NULL;
> + ldap_auth_t auth_method_enum = AUTH_INVALID;
> +
> + /* Set timer for deadlock detection inside semaphore_wait_timed . */
> + CHECK(setting_get_uint("timeout", set, &uint));
> + if (semaphore_wait_timeout.seconds < uint*SEM_WAIT_TIMEOUT_MUL)
> + semaphore_wait_timeout.seconds = uint*SEM_WAIT_TIMEOUT_MUL;
> +
> + CHECK(setting_get_bool("psearch", set, &psearch));
> + CHECK(setting_get_uint("connections", set, &uint));
> + if (!psearch && uint < 1) {
> + log_error("zone refresh mode requires one connection at least");
> + CLEANUP_WITH(ISC_R_RANGE);
> + }
> + else if (psearch && uint < 2) {
> + log_error("persistent search mode requires two connections "
> + "at least");
> + /* watcher needs one and update_*() requests second connection */
> + CLEANUP_WITH(ISC_R_RANGE);
> + }
> +
> + CHECK(setting_get_bool("serial_autoincrement", set, &serial_autoincrement));
> + if (serial_autoincrement && !psearch) {
> + log_error("SOA serial number auto-increment feature requires "
> + "persistent search");
> + CLEANUP_WITH(ISC_R_FAILURE);
> + }
> +
> + CHECK(setting_get_uint("zone_refresh", set, &uint));
> + if (uint != 0 && psearch) {
> + log_error("zone refresh and persistent search "
> + "cannot be enabled at same time");
> + CLEANUP_WITH(ISC_R_FAILURE);
> + }
> +
> + /* Select authentication method. */
> + CHECK(setting_get_str("auth_method", set, &auth_method_str));
> + auth_method_enum = AUTH_INVALID;
> + for (int i = 0; supported_ldap_auth[i].name != NULL; i++) {
> + if (!strcasecmp(auth_method_str, supported_ldap_auth[i].name)) {
> + auth_method_enum = supported_ldap_auth[i].value;
> + break;
> + }
> + }
> + if (auth_method_enum == AUTH_INVALID) {
> + log_error("unknown authentication method '%s'",
> + auth_method_str);
> + CLEANUP_WITH(ISC_R_FAILURE);
> + }
> + CHECK(isc_string_printf(print_buff, PRINT_BUFF_SIZE, "%u", auth_method_enum));
> + CHECK(setting_set("auth_method_enum", inst->local_settings, print_buff,
> + inst->task));
> +
> + /* check we have the right data when SASL/GSSAPI is selected */
> + CHECK(setting_get_str("sasl_mech", set, &sasl_mech));
> + CHECK(setting_get_str("krb5_principal", set, &krb5_principal));
> + CHECK(setting_get_str("sasl_user", set, &sasl_user));
> +
> + if ((auth_method_enum == AUTH_SASL) &&
> + (strcasecmp(sasl_mech, "GSSAPI") == 0)) {
> + if ((krb5_principal == NULL) || (strlen(krb5_principal) == 0)) {
> + if ((sasl_user == NULL) || (strlen(sasl_user) == 0)) {
> + char hostname[HOST_NAME_MAX];
> + if (gethostname(hostname, HOST_NAME_MAX) != 0) {
> + log_error("SASL mech GSSAPI defined "
> + "but krb5_principal and "
> + "sasl_user are empty and"
> + "gethostname() failed");
> + CLEANUP_WITH(ISC_R_FAILURE);
> + } else {
> + CHECK(str_sprintf(buff,
> + "DNS/%s", hostname));
> + log_debug(2, "SASL mech GSSAPI defined "
> + "but krb5_principal and "
> + "sasl_user are empty, using "
> + "default '%s'",
> + str_buf(buff));
> + CHECK(setting_set("krb5_principal", set,
> + str_buf(buff),
> + inst->task));
> + }
> + } else {
> + CHECK(setting_set("krb5_principal", set,
> + sasl_user,
> + inst->task));
> + }
> + }
> + } else if (auth_method_enum == AUTH_SASL) {
> + log_info("SASL mechanisms other than GSSAPI+Kerberos "
> + "are untested; expect problems");
> + }
> +
> + if (settings_set_isfilled(set) != ISC_TRUE)
> + result = ISC_R_FAILURE;
> +
> +cleanup:
> + if (result != ISC_R_SUCCESS)
> + log_error_r("LDAP config validation failed for database '%s'",
> + inst->db_name);
> + return result;
> +}
> +#undef PRINT_BUFF_SIZE
> +
> +#define PRINT_BUFF_SIZE 255
> isc_result_t
> new_ldap_instance(isc_mem_t *mctx, const char *db_name,
> const char * const *argv, dns_dyndb_arguments_t *dyndb_args,
> isc_task_t *task, ldap_instance_t **ldap_instp)
> {
> - unsigned int i;
> isc_result_t result;
> ldap_instance_t *ldap_inst;
> dns_view_t *view = NULL;
> - ld_string_t *auth_method_str = NULL;
> dns_forwarders_t *orig_global_forwarders = NULL;
> - isc_uint32_t cache_ttl_seconds;
> - setting_t ldap_settings[] = {
> - { "uri", no_default_string },
> - { "connections", default_uint(2) },
> - { "reconnect_interval", default_uint(60) },
> - { "timeout", default_uint(10) },
> - { "base", no_default_string },
> - { "auth_method", default_string("none") },
> - { "bind_dn", default_string("") },
> - { "password", default_string("") },
> - { "krb5_principal", default_string("") },
> - { "sasl_mech", default_string("GSSAPI") },
> - { "sasl_user", default_string("") },
> - { "sasl_auth_name", default_string("") },
> - { "sasl_realm", default_string("") },
> - { "sasl_password", default_string("") },
> - { "krb5_keytab", default_string("") },
> - { "fake_mname", default_string("") },
> - { "psearch", default_boolean(ISC_FALSE) },
> - { "cache_ttl", default_uint(120) },
> - { "ldap_hostname", default_string("") },
> - { "sync_ptr", default_boolean(ISC_FALSE) },
> - { "dyn_update", default_boolean(ISC_FALSE) },
> - { "serial_autoincrement", default_boolean(ISC_FALSE) },
> - end_of_settings
> - };
> + isc_boolean_t psearch;
> + isc_uint32_t connections;
> + char settings_name[PRINT_BUFF_SIZE];
>
> REQUIRE(ldap_instp != NULL && *ldap_instp == NULL);
>
> @@ -381,123 +496,35 @@ new_ldap_instance(isc_mem_t *mctx, const char *db_name,
> dns_view_attach(view, &ldap_inst->view);
> ldap_inst->zmgr = dns_dyndb_get_zonemgr(dyndb_args);
> ISC_LIST_INIT(ldap_inst->orig_global_forwarders.addrs);
> -
> - CHECK(zr_create(mctx, &ldap_inst->zone_register));
> -
> - CHECK(isc_mutex_init(&ldap_inst->kinit_lock));
> -
> - CHECK(str_new(mctx, &auth_method_str));
> - CHECK(str_new(mctx, &ldap_inst->uri));
> - CHECK(str_new(mctx, &ldap_inst->base));
> - CHECK(str_new(mctx, &ldap_inst->bind_dn));
> - CHECK(str_new(mctx, &ldap_inst->password));
> - CHECK(str_new(mctx, &ldap_inst->krb5_principal));
> - CHECK(str_new(mctx, &ldap_inst->sasl_mech));
> - CHECK(str_new(mctx, &ldap_inst->sasl_user));
> - CHECK(str_new(mctx, &ldap_inst->sasl_auth_name));
> - CHECK(str_new(mctx, &ldap_inst->sasl_realm));
> - CHECK(str_new(mctx, &ldap_inst->sasl_password));
> - CHECK(str_new(mctx, &ldap_inst->krb5_keytab));
> - CHECK(str_new(mctx, &ldap_inst->fake_mname));
> - CHECK(str_new(mctx, &ldap_inst->ldap_hostname));
> -
> - i = 0;
> - ldap_settings[i++].target = ldap_inst->uri;
> - ldap_settings[i++].target = &ldap_inst->connections;
> - ldap_settings[i++].target = &ldap_inst->reconnect_interval;
> - ldap_settings[i++].target = &ldap_inst->timeout;
> - ldap_settings[i++].target = ldap_inst->base;
> - ldap_settings[i++].target = auth_method_str;
> - ldap_settings[i++].target = ldap_inst->bind_dn;
> - ldap_settings[i++].target = ldap_inst->password;
> - ldap_settings[i++].target = ldap_inst->krb5_principal;
> - ldap_settings[i++].target = ldap_inst->sasl_mech;
> - ldap_settings[i++].target = ldap_inst->sasl_user;
> - ldap_settings[i++].target = ldap_inst->sasl_auth_name;
> - ldap_settings[i++].target = ldap_inst->sasl_realm;
> - ldap_settings[i++].target = ldap_inst->sasl_password;
> - ldap_settings[i++].target = ldap_inst->krb5_keytab;
> - ldap_settings[i++].target = ldap_inst->fake_mname;
> - ldap_settings[i++].target = &ldap_inst->psearch;
> - ldap_settings[i++].target = &cache_ttl_seconds;
> - ldap_settings[i++].target = ldap_inst->ldap_hostname;
> - ldap_settings[i++].target = &ldap_inst->sync_ptr;
> - ldap_settings[i++].target = &ldap_inst->dyn_update;
> - ldap_settings[i++].target = &ldap_inst->serial_autoincrement;
> - CHECK(set_settings(ldap_settings, argv));
> - isc_interval_set(&ldap_inst->cache_ttl, cache_ttl_seconds, 0);
> -
> - /* Set timer for deadlock detection inside semaphore_wait_timed . */
> - if (semaphore_wait_timeout.seconds < ldap_inst->timeout*SEM_WAIT_TIMEOUT_MUL)
> - semaphore_wait_timeout.seconds = ldap_inst->timeout*SEM_WAIT_TIMEOUT_MUL;
> -
> - /* Validate and check settings. */
> - str_toupper(ldap_inst->sasl_mech);
> - if (ldap_inst->connections < 1) {
> - log_error("at least one connection is required");
> - result = ISC_R_FAILURE;
> - goto cleanup;
> - }
> - /* Select authentication method. */
> - ldap_inst->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_inst->auth_method = supported_ldap_auth[i].value;
> - break;
> - }
> - }
> - if (ldap_inst->auth_method == AUTH_INVALID) {
> - log_error("unknown authentication method '%s'",
> - str_buf(auth_method_str));
> - result = ISC_R_FAILURE;
> - goto cleanup;
> - }
> -
> - /* check we have the right data when SASL/GSSAPI is selected */
> - if ((ldap_inst->auth_method == AUTH_SASL) &&
> - (str_casecmp_char(ldap_inst->sasl_mech, "GSSAPI") == 0)) {
> - if ((ldap_inst->krb5_principal == NULL) ||
> - (str_len(ldap_inst->krb5_principal) == 0)) {
> - if ((ldap_inst->sasl_user == NULL) ||
> - (str_len(ldap_inst->sasl_user) == 0)) {
> - char hostname[255];
> - if (gethostname(hostname, 255) != 0) {
> - log_error("SASL mech GSSAPI defined but krb5_principal"
> - "and sasl_user are empty. Could not get hostname");
> - result = ISC_R_FAILURE;
> - goto cleanup;
> - } else {
> - CHECK(str_sprintf(ldap_inst->krb5_principal,
> - "DNS/%s", hostname));
> - log_debug(2, "SASL mech GSSAPI defined but krb5_principal"
> - "and sasl_user are empty, using default %s",
> - str_buf(ldap_inst->krb5_principal));
> - }
> - } else {
> - str_copy(ldap_inst->krb5_principal, ldap_inst->sasl_user);
> - }
> - }
> - }
> -
> ldap_inst->task = task;
> + ldap_inst->watcher = 0;
>
> - if (ldap_inst->psearch && ldap_inst->connections < 2) {
> - /* watcher needs one and update_*() will request second */
> - log_error("psearch needs at least 2 connections, "
> - "increasing limit");
> - ldap_inst->connections = 2;
> - }
> - if (ldap_inst->serial_autoincrement == ISC_TRUE
> - && ldap_inst->psearch != ISC_TRUE) {
> - log_error("SOA serial number auto-increment feature requires "
> - "persistent search");
> - result = ISC_R_FAILURE;
> - goto cleanup;
> - }
> + isc_string_printf_truncate(settings_name, PRINT_BUFF_SIZE,
> + SETTING_SET_NAME_LOCAL " for database %s",
> + db_name);
> + CHECK(settings_set_create(mctx, settings_local_default,
> + sizeof(settings_local_default), settings_name,
> + &settings_default_set, &ldap_inst->local_settings));
>
> - CHECK(ldap_pool_create(mctx, ldap_inst->connections, &ldap_inst->pool));
> - CHECK(ldap_pool_connect(ldap_inst->pool, ldap_inst));
> + isc_string_printf_truncate(settings_name, PRINT_BUFF_SIZE,
> + SETTING_SET_NAME_GLOBAL " for database %s",
> + db_name);
> + CHECK(settings_set_create(mctx, settings_global_default,
> + sizeof(settings_global_default), settings_name,
> + ldap_inst->local_settings, &ldap_inst->global_settings));
> +
> + CHECK(settings_set_fill(ldap_inst->local_settings, argv, task));
> + CHECK(validate_local_instance_settings(ldap_inst, ldap_inst->local_settings));
> + if (settings_set_isfilled(ldap_inst->global_settings) != ISC_TRUE)
> + CLEANUP_WITH(ISC_R_FAILURE);
> +
> + CHECK(setting_get_bool("psearch", ldap_inst->local_settings, &psearch));
> + CHECK(setting_get_uint("connections", ldap_inst->local_settings, &connections));
> +
> + CHECK(zr_create(mctx, ldap_inst->global_settings,
> + &ldap_inst->zone_register));
> +
> + CHECK(isc_mutex_init(&ldap_inst->kinit_lock));
>
> /* copy global forwarders setting for configuration roll back in
> * configure_zone_forwarders() */
> @@ -521,12 +548,14 @@ new_ldap_instance(isc_mem_t *mctx, const char *db_name,
> } else if (result == ISC_R_NOTFOUND) {
> /* global forwarders are not configured */
> ldap_inst->orig_global_forwarders.fwdpolicy = dns_fwdpolicy_none;
> - result = ISC_R_SUCCESS;
> } else {
> goto cleanup;
> }
>
> - if (ldap_inst->psearch) {
> + CHECK(ldap_pool_create(mctx, connections, &ldap_inst->pool));
> + CHECK(ldap_pool_connect(ldap_inst->pool, ldap_inst));
> +
> + if (psearch) {
> /* Start the watcher thread */
> result = isc_thread_create(ldap_psearch_watcher, ldap_inst,
> &ldap_inst->watcher);
> @@ -542,10 +571,9 @@ cleanup:
> else
> *ldap_instp = ldap_inst;
>
> - str_destroy(&auth_method_str);
> -
> return result;
> }
> +#undef PRINT_BUFF_SIZE
>
> void
> destroy_ldap_instance(ldap_instance_t **ldap_instp)
> @@ -627,7 +655,7 @@ destroy_ldap_instance(ldap_instance_t **ldap_instp)
> dns_rbtnodechain_invalidate(&chain);
>
> /* TODO: Terminate psearch watcher sooner? */
> - if (ldap_inst->psearch && ldap_inst->watcher != 0) {
> + if (ldap_inst->watcher != 0) {
> ldap_inst->exiting = ISC_TRUE;
> /*
> * Wake up the watcher thread. This might look like a hack
> @@ -644,21 +672,6 @@ destroy_ldap_instance(ldap_instance_t **ldap_instp)
> }
>
> ldap_pool_destroy(&ldap_inst->pool);
> -
> - str_destroy(&ldap_inst->uri);
> - str_destroy(&ldap_inst->base);
> - str_destroy(&ldap_inst->bind_dn);
> - str_destroy(&ldap_inst->password);
> - str_destroy(&ldap_inst->krb5_principal);
> - str_destroy(&ldap_inst->sasl_mech);
> - str_destroy(&ldap_inst->sasl_user);
> - str_destroy(&ldap_inst->sasl_auth_name);
> - str_destroy(&ldap_inst->sasl_realm);
> - str_destroy(&ldap_inst->sasl_password);
> - str_destroy(&ldap_inst->krb5_keytab);
> - str_destroy(&ldap_inst->fake_mname);
> - str_destroy(&ldap_inst->ldap_hostname);
> -
> dns_view_detach(&ldap_inst->view);
>
> DESTROYLOCK(&ldap_inst->kinit_lock);
> @@ -671,6 +684,9 @@ destroy_ldap_instance(ldap_instance_t **ldap_instp)
> SAFE_MEM_PUT_PTR(ldap_inst->mctx, addr);
> }
>
> + settings_set_free(&ldap_inst->global_settings);
> + settings_set_free(&ldap_inst->local_settings);
> +
> MEM_PUT_AND_DETACH(ldap_inst);
>
> *ldap_instp = NULL;
> @@ -1152,64 +1168,62 @@ static isc_result_t
> ldap_parse_configentry(ldap_entry_t *entry, ldap_instance_t *inst)
> {
> isc_result_t result;
> - ldap_valuelist_t values;
> - isc_boolean_t sync_ptr_new;
> isc_timer_t *timer_inst;
> isc_interval_t timer_interval;
> isc_uint32_t interval_sec;
> isc_timertype_t timer_type;
>
> - /* BIND functions are thread safe, lock only ldap instance 'inst'. */
> + /* BIND functions are thread safe, ldap instance 'inst' is locked
> + * inside setting* functions. */
>
> log_debug(3, "Parsing configuration object");
>
> /* idnsForwardPolicy change is handled by configure_zone_forwarders() */
> result = configure_zone_forwarders(entry, inst, dns_rootname);
> if (result != ISC_R_SUCCESS && result != ISC_R_DISABLED) {
> log_error_r("global forwarder could not be set up");
> }
>
> - result = ldap_entry_getvalues(entry, "idnsAllowSyncPTR", &values);
> - if (result == ISC_R_SUCCESS) {
> - log_debug(2, "Setting global AllowSyncPTR = %s", HEAD(values)->value);
> - sync_ptr_new = (strcmp(HEAD(values)->value, "TRUE") == 0)
> - ? ISC_TRUE : ISC_FALSE;
> + result = setting_update_from_ldap_entry("dyn_update",
> + inst->global_settings,
> + "idnsAllowDynUpdate",
> + entry, inst->task);
> + if (result != ISC_R_SUCCESS && result != ISC_R_IGNORE)
> + goto cleanup;
>
> - if (inst->sync_ptr != sync_ptr_new) { /* lock BIND only if necessary */
> - result = isc_task_beginexclusive(inst->task);
> - RUNTIME_CHECK(result == ISC_R_SUCCESS ||
> - result == ISC_R_LOCKBUSY);
> - inst->sync_ptr = sync_ptr_new;
> - if (result == ISC_R_SUCCESS) {
> - isc_task_endexclusive(inst->task);
> - }
> - }
> - }
> + result = setting_update_from_ldap_entry("sync_ptr",
> + inst->global_settings,
> + "idnsAllowSyncPTR",
> + entry, inst->task);
> + if (result != ISC_R_SUCCESS && result != ISC_R_IGNORE)
> + goto cleanup;
>
> - result = ldap_entry_getvalues(entry, "idnsZoneRefresh", &values);
> + result = setting_update_from_ldap_entry("zone_refresh",
> + inst->global_settings,
> + "idnsZoneRefresh",
> + entry, inst->task);
> if (result == ISC_R_SUCCESS) {
> - log_debug(2, "Setting global ZoneRefresh timer = %s", HEAD(values)->value);
> - RUNTIME_CHECK(manager_get_db_timer(inst->db_name, &timer_inst) == ISC_R_SUCCESS);
> -
> - result = isc_parse_uint32(&interval_sec, HEAD(values)->value, 10);
> - if (result != ISC_R_SUCCESS) {
> - log_error("Could not parse ZoneRefresh interval");
> - goto cleanup;
> - }
> + RUNTIME_CHECK(manager_get_db_timer(inst->db_name, &timer_inst)
> + == ISC_R_SUCCESS);
> + CHECK(setting_get_uint("zone_refresh", inst->global_settings,
> + &interval_sec));
> isc_interval_set(&timer_interval, interval_sec, 0);
> /* update interval only, not timer type */
> timer_type = isc_timer_gettype(timer_inst);
> result = isc_timer_reset(timer_inst, timer_type, NULL,
> &timer_interval, ISC_TRUE);
> if (result != ISC_R_SUCCESS) {
> - log_error("Could not adjust ZoneRefresh timer");
> + log_error_r("could not adjust ZoneRefresh timer");
> goto cleanup;
> }
> + } else if (result != ISC_R_IGNORE) {
> + goto cleanup;
> }
>
> cleanup:
> /* Configuration errors are not fatal. */
> - return ISC_R_SUCCESS;
> + /* TODO: log something? */
> + return ISC_R_SUCCESS;
> }
>
> /* Parse the zone entry */
> @@ -1232,6 +1246,8 @@ ldap_parse_zoneentry(ldap_entry_t *entry, ldap_instance_t *inst)
> ldapdb_rdatalist_t rdatalist;
> isc_boolean_t zone_dynamic = ISC_FALSE;
> ldap_cache_t *cache = NULL;
> + settings_set_t *zone_settings = NULL;
> + isc_boolean_t serial_autoincrement;
>
> REQUIRE(entry != NULL);
> REQUIRE(inst != NULL);
> @@ -1278,13 +1294,24 @@ ldap_parse_zoneentry(ldap_entry_t *entry, ldap_instance_t *inst)
> result = zr_get_zone_ptr(inst->zone_register, &name, &zone);
> if (result == ISC_R_NOTFOUND || result == DNS_R_PARTIALMATCH) {
> CHECK(create_zone(inst, &name, &zone));
> - CHECK(zr_add_zone(inst->zone_register, zone, dn,
> - &inst->cache_ttl, &inst->psearch));
> + CHECK(zr_add_zone(inst->zone_register, zone, dn));
> publish = ISC_TRUE;
> log_debug(2, "created zone %p: %s", zone, dn);
> } else if (result != ISC_R_SUCCESS)
> goto cleanup;
>
> + CHECK(zr_get_zone_settings(inst->zone_register, &name, &zone_settings));
> +
> + result = setting_update_from_ldap_entry("dyn_update", zone_settings,
> + "idnsAllowDynUpdate", entry, inst->task);
> + if (result != ISC_R_SUCCESS && result != ISC_R_IGNORE)
> + goto cleanup;
> +
> + result = setting_update_from_ldap_entry("sync_ptr", zone_settings,
> + "idnsAllowSyncPTR", entry, inst->task);
> + if (result != ISC_R_SUCCESS && result != ISC_R_IGNORE)
> + goto cleanup;
> +
> log_debug(2, "Setting SSU table for %p: %s", zone, dn);
> /* Get the update policy and update the zone with it. */
> result = ldap_entry_getvalues(entry, "idnsUpdatePolicy", &values);
> @@ -1367,7 +1394,9 @@ ldap_parse_zoneentry(ldap_entry_t *entry, ldap_instance_t *inst)
> CHECK(ldap_get_zone_serial(inst, &name, &ldap_serial));
> CHECK(zr_get_zone_serial_digest(inst->zone_register, &name, &zr_serial,
> &zr_digest));
> - if (inst->serial_autoincrement) {
> + CHECK(setting_get_bool("serial_autoincrement", zone_settings,
> + &serial_autoincrement));
> + if (serial_autoincrement) {
> CHECK(ldapdb_rdatalist_get(inst->mctx, inst, &name,
> &name, &rdatalist));
> CHECK(rdatalist_digest(inst->mctx, &rdatalist, ldap_digest));
> @@ -1429,34 +1458,39 @@ refresh_zones_from_ldap(ldap_instance_t *ldap_inst, isc_boolean_t delete_only)
> ldap_entry_t *entry;
> dns_rbt_t *rbt = NULL;
> isc_boolean_t invalidate_nodechain = ISC_FALSE;
> + isc_boolean_t psearch;
> + const char *base = NULL;
> char *config_attrs[] = {
> "idnsForwardPolicy", "idnsForwarders",
> "idnsAllowSyncPTR", "idnsZoneRefresh",
> "idnsPersistentSearch", NULL
> };
> char *zone_attrs[] = {
> "idnsName", "idnsUpdatePolicy", "idnsAllowQuery",
> - "idnsAllowTransfer", "idnsForwardPolicy",
> - "idnsForwarders", NULL
> + "idnsAllowTransfer", "idnsForwardPolicy", "idnsForwarders",
> + "idnsAllowDynUpdate", "idnsAllowSyncPTR", NULL
> };
>
> REQUIRE(ldap_inst != NULL);
>
> - if (ldap_inst->psearch && !delete_only) {
> + CHECK(setting_get_bool("psearch", ldap_inst->global_settings,
> + &psearch));
> + if (psearch && !delete_only) {
> /* Watcher does the work for us, but deletion is allowed. */
> return ISC_R_SUCCESS;
> }
>
> log_debug(2, "refreshing list of zones for %s", ldap_inst->db_name);
>
> /* Query for configuration and zones from LDAP and release LDAP connection
> * before processing them. It prevents deadlock in situations where
> * ldap_parse_zoneentry() requests another connection. */
> + CHECK(setting_get_str("base", ldap_inst->global_settings, &base));
> CHECK(ldap_pool_getconnection(ldap_inst->pool, &ldap_conn));
> - CHECK(ldap_query(ldap_inst, ldap_conn, &ldap_config_qresult, str_buf(ldap_inst->base),
> + CHECK(ldap_query(ldap_inst, ldap_conn, &ldap_config_qresult, base,
> LDAP_SCOPE_SUBTREE, config_attrs, 0,
> "(objectClass=idnsConfigObject)"));
> - CHECK(ldap_query(ldap_inst, ldap_conn, &ldap_zones_qresult, str_buf(ldap_inst->base),
> + CHECK(ldap_query(ldap_inst, ldap_conn, &ldap_zones_qresult, base,
> LDAP_SCOPE_SUBTREE, zone_attrs, 0,
> "(&(objectClass=idnsZone)(idnsZoneActive=TRUE))"));
> ldap_pool_putconnection(ldap_inst->pool, &ldap_conn);
> @@ -1684,7 +1718,7 @@ free_rdatalist(isc_mem_t *mctx, dns_rdatalist_t *rdlist)
> static isc_result_t
> ldap_parse_rrentry(isc_mem_t *mctx, ldap_entry_t *entry,
> ldap_qresult_t *qresult, dns_name_t *origin,
> - const ld_string_t *fake_mname, ld_string_t *buf,
> + const char *fake_mname, ld_string_t *buf,
> ldapdb_rdatalist_t *rdatalist)
> {
> isc_result_t result;
> @@ -1741,6 +1775,7 @@ ldapdb_nodelist_get(isc_mem_t *mctx, ldap_instance_t *ldap_inst, dns_name_t *nam
> ld_string_t *string = NULL;
> ldapdb_node_t *node;
> dns_name_t node_name;
> + const char *fake_mname = NULL;
>
> REQUIRE(ldap_inst != NULL);
> REQUIRE(name != NULL);
> @@ -1759,6 +1794,8 @@ ldapdb_nodelist_get(isc_mem_t *mctx, ldap_instance_t *ldap_inst, dns_name_t *nam
> goto cleanup;
> }
>
> + CHECK(setting_get_str("fake_mname", ldap_inst->local_settings,
> + &fake_mname));
> for (entry = HEAD(ldap_qresult->ldap_entries);
> entry != NULL;
> entry = NEXT(entry, link)) {
> @@ -1773,7 +1810,7 @@ ldapdb_nodelist_get(isc_mem_t *mctx, ldap_instance_t *ldap_inst, dns_name_t *nam
> dns_name_free(&node_name, mctx);
> if (result == ISC_R_SUCCESS) {
> result = ldap_parse_rrentry(mctx, entry, ldap_qresult,
> - origin, ldap_inst->fake_mname,
> + origin, fake_mname,
> string, &node->rdatalist);
> }
> if (result != ISC_R_SUCCESS) {
> @@ -1805,6 +1842,7 @@ ldapdb_rdatalist_get(isc_mem_t *mctx, ldap_instance_t *ldap_inst, dns_name_t *na
> ldap_entry_t *entry;
> ld_string_t *string = NULL;
> ldap_cache_t *cache = NULL;
> + const char *fake_mname = NULL;
>
> REQUIRE(ldap_inst != NULL);
> REQUIRE(name != NULL);
> @@ -1831,11 +1869,13 @@ ldapdb_rdatalist_get(isc_mem_t *mctx, ldap_instance_t *ldap_inst, dns_name_t *na
> goto cleanup;
> }
>
> + CHECK(setting_get_str("fake_mname", ldap_inst->local_settings,
> + &fake_mname));
> for (entry = HEAD(ldap_qresult->ldap_entries);
> entry != NULL;
> entry = NEXT(entry, link)) {
> CHECK(ldap_parse_rrentry(mctx, entry, ldap_qresult,
> - origin, ldap_inst->fake_mname,
> + origin, fake_mname,
> string, rdatalist));
> }
>
> @@ -1859,7 +1899,7 @@ cleanup:
> static isc_result_t
> add_soa_record(isc_mem_t *mctx, ldap_qresult_t *qresult, dns_name_t *origin,
> ldap_entry_t *entry, ldapdb_rdatalist_t *rdatalist,
> - const ld_string_t *fake_mname)
> + const char *fake_mname)
> {
> isc_result_t result;
> ld_string_t *string = NULL;
> @@ -2117,6 +2157,7 @@ ldap_sasl_interact(LDAP *ld, unsigned flags, void *defaults, void *sin)
> sasl_interact_t *in;
> ldap_instance_t *ldap_inst = defaults;
> int ret = LDAP_OTHER;
> + isc_result_t result;
>
> REQUIRE(ldap_inst != NULL);
> UNUSED(flags);
> @@ -2129,36 +2170,47 @@ ldap_sasl_interact(LDAP *ld, unsigned flags, void *defaults, void *sin)
> switch (in->id) {
> case SASL_CB_USER:
> log_debug(4, "got request for SASL_CB_USER");
> - in->result = str_buf(ldap_inst->sasl_user);
> - in->len = str_len(ldap_inst->sasl_user);
> + CHECK(setting_get_str("sasl_user",
> + ldap_inst->global_settings,
> + (const char **)&in->result));
> + in->len = strlen(in->result);
> ret = LDAP_SUCCESS;
> break;
> case SASL_CB_GETREALM:
> log_debug(4, "got request for SASL_CB_GETREALM");
> - in->result = str_buf(ldap_inst->sasl_realm);
> - in->len = str_len(ldap_inst->sasl_realm);
> + CHECK(setting_get_str("sasl_realm",
> + ldap_inst->global_settings,
> + (const char **)&in->result));
> + in->len = strlen(in->result);
> ret = LDAP_SUCCESS;
> break;
> case SASL_CB_AUTHNAME:
> log_debug(4, "got request for SASL_CB_AUTHNAME");
> - in->result = str_buf(ldap_inst->sasl_auth_name);
> - in->len = str_len(ldap_inst->sasl_auth_name);
> + CHECK(setting_get_str("sasl_auth_name",
> + ldap_inst->global_settings,
> + (const char **)&in->result));
> + in->len = strlen(in->result);
> ret = LDAP_SUCCESS;
> break;
> case SASL_CB_PASS:
> log_debug(4, "got request for SASL_CB_PASS");
> - in->result = str_buf(ldap_inst->sasl_password);
> - in->len = str_len(ldap_inst->sasl_password);
> + CHECK(setting_get_str("sasl_password",
> + ldap_inst->global_settings,
> + (const char **)&in->result));
> + in->len = strlen(in->result);
> ret = LDAP_SUCCESS;
> break;
> default:
> - in->result = NULL;
> - in->len = 0;
> - ret = LDAP_OTHER;
> + goto cleanup;
> }
> }
>
> return ret;
> +
> +cleanup:
> + in->result = NULL;
> + in->len = 0;
> + return LDAP_OTHER;
> }
>
> /*
> @@ -2169,16 +2221,20 @@ static isc_result_t
> ldap_connect(ldap_instance_t *ldap_inst, ldap_connection_t *ldap_conn,
> isc_boolean_t force)
> {
> - LDAP *ld;
> + LDAP *ld = NULL;
> int ret;
> int version;
> struct timeval timeout;
> isc_result_t result = ISC_R_FAILURE;
> + const char *uri = NULL;
> + const char *ldap_hostname = NULL;
> + isc_uint32_t timeout_sec;
>
> REQUIRE(ldap_inst != NULL);
> REQUIRE(ldap_conn != NULL);
>
> - ret = ldap_initialize(&ld, str_buf(ldap_inst->uri));
> + CHECK(setting_get_str("uri", ldap_inst->local_settings, &uri));
> + ret = ldap_initialize(&ld, uri);
> if (ret != LDAP_SUCCESS) {
> log_error("LDAP initialization failed: %s",
> ldap_err2string(ret));
> @@ -2189,15 +2245,18 @@ ldap_connect(ldap_instance_t *ldap_inst, ldap_connection_t *ldap_conn,
> ret = ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION, &version);
> LDAP_OPT_CHECK(ret, "failed to set LDAP version");
>
> - timeout.tv_sec = ldap_inst->timeout;
> + CHECK(setting_get_uint("timeout", ldap_inst->global_settings,
> + &timeout_sec));
> + timeout.tv_sec = timeout_sec;
> timeout.tv_usec = 0;
>
> ret = ldap_set_option(ld, LDAP_OPT_TIMEOUT, &timeout);
> LDAP_OPT_CHECK(ret, "failed to set timeout");
>
> - if (str_len(ldap_inst->ldap_hostname) > 0) {
> - ret = ldap_set_option(ld, LDAP_OPT_HOST_NAME,
> - str_buf(ldap_inst->ldap_hostname));
> + CHECK(setting_get_str("ldap_hostname", ldap_inst->local_settings,
> + &ldap_hostname));
> + if (strlen(ldap_hostname) > 0) {
> + ret = ldap_set_option(ld, LDAP_OPT_HOST_NAME, ldap_hostname);
> LDAP_OPT_CHECK(ret, "failed to set LDAP_OPT_HOST_NAME");
> }
>
> @@ -2226,9 +2285,16 @@ static isc_result_t
> ldap_reconnect(ldap_instance_t *ldap_inst, ldap_connection_t *ldap_conn,
> isc_boolean_t force)
> {
> + isc_result_t result;
> int ret = 0;
> const char *bind_dn = NULL;
> const char *password = NULL;
> + const char *uri = NULL;
> + const char *sasl_mech = NULL;
> + const char *krb5_principal = NULL;
> + const char *krb5_keytab = NULL;
> + ldap_auth_t auth_method_enum = AUTH_INVALID;
> + isc_uint32_t reconnect_interval;
>
> if (force)
> goto force_reconnect;
> @@ -2246,9 +2312,11 @@ ldap_reconnect(ldap_instance_t *ldap_inst, ldap_connection_t *ldap_conn,
>
> /* If either bind_dn or the password is not set, we will use
> * password-less bind. */
> - if (str_len(ldap_inst->bind_dn) > 0 && str_len(ldap_inst->password) > 0) {
> - bind_dn = str_buf(ldap_inst->bind_dn);
> - password = str_buf(ldap_inst->password);
> + CHECK(setting_get_str("bind_dn", ldap_inst->global_settings, &bind_dn));
> + CHECK(setting_get_str("password", ldap_inst->global_settings, &password));
> + if (strlen(bind_dn) == 0 || strlen(password) == 0) {
> + bind_dn = NULL;
> + password = NULL;
> }
>
> /* Set the next possible reconnect time. */
> @@ -2260,39 +2328,52 @@ ldap_reconnect(ldap_instance_t *ldap_inst, ldap_connection_t *ldap_conn,
> const size_t ntimes = sizeof(intervals) / sizeof(intervals[0]);
>
> i = ISC_MIN(ntimes - 1, ldap_conn->tries);
> - seconds = ISC_MIN(intervals[i], ldap_inst->reconnect_interval);
> + CHECK(setting_get_uint("reconnect_interval",
> + ldap_inst->global_settings,
> + &reconnect_interval));
> + seconds = ISC_MIN(intervals[i], reconnect_interval);
> isc_interval_set(&delay, seconds, 0);
> isc_time_nowplusinterval(&ldap_conn->next_reconnect, &delay);
> }
>
> ldap_conn->tries++;
> force_reconnect:
> - log_debug(2, "trying to establish LDAP connection to %s",
> - str_buf(ldap_inst->uri));
> + CHECK(setting_get_str("uri", ldap_inst->local_settings, &uri));
> + log_debug(2, "trying to establish LDAP connection to %s", uri);
>
> - switch (ldap_inst->auth_method) {
> + CHECK(setting_get_uint("auth_method_enum", ldap_inst->local_settings,
> + &auth_method_enum));
> + switch (auth_method_enum) {
> case AUTH_NONE:
> ret = ldap_simple_bind_s(ldap_conn->handle, NULL, NULL);
> break;
> case AUTH_SIMPLE:
> ret = ldap_simple_bind_s(ldap_conn->handle, bind_dn, password);
> break;
> case AUTH_SASL:
> - if (strcmp(str_buf(ldap_inst->sasl_mech), "GSSAPI") == 0) {
> + CHECK(setting_get_str("sasl_mech", ldap_inst->local_settings,
> + &sasl_mech));
> + if (strcmp(sasl_mech, "GSSAPI") == 0) {
> isc_result_t result;
> + CHECK(setting_get_str("krb5_principal",
> + ldap_inst->local_settings,
> + &krb5_principal));
> + CHECK(setting_get_str("krb5_keytab",
> + ldap_inst->local_settings,
> + &krb5_keytab));
> LOCK(&ldap_inst->kinit_lock);
> result = get_krb5_tgt(ldap_inst->mctx,
> - str_buf(ldap_inst->krb5_principal),
> - str_buf(ldap_inst->krb5_keytab));
> + krb5_principal,
> + krb5_keytab);
> UNLOCK(&ldap_inst->kinit_lock);
> if (result != ISC_R_SUCCESS)
> return result;
> }
>
> - log_debug(4, "trying interactive bind using %s mechanism",
> - str_buf(ldap_inst->sasl_mech));
> + log_debug(4, "trying interactive bind using '%s' mechanism",
> + sasl_mech);
> ret = ldap_sasl_interactive_bind_s(ldap_conn->handle, NULL,
> - str_buf(ldap_inst->sasl_mech),
> + sasl_mech,
> NULL, NULL, LDAP_SASL_QUIET,
> ldap_sasl_interact,
> ldap_inst);
> @@ -2331,6 +2412,9 @@ force_reconnect:
> ldap_conn->tries = 0;
>
> return ISC_R_SUCCESS;
> +
> +cleanup:
> + return result;
> }
>
> static isc_result_t
> @@ -2699,70 +2783,66 @@ modify_ldap_common(dns_name_t *owner, ldap_instance_t *ldap_inst,
> isc_result_t result;
> isc_mem_t *mctx = ldap_inst->mctx;
> ldap_connection_t *ldap_conn = NULL;
> - ldap_qresult_t *ldap_qresult = NULL;
> ld_string_t *owner_dn = NULL;
> LDAPMod *change[3] = { NULL };
> LDAPMod *change_ptr = NULL;
> ldap_cache_t *cache = NULL;
> - ldap_entry_t *entry;
> - ldap_valuelist_t values;
> - isc_boolean_t zone_dyn_update = ldap_inst->dyn_update;
> - isc_boolean_t zone_sync_ptr = ldap_inst->sync_ptr;
> + isc_boolean_t zone_dyn_update;
> + isc_boolean_t zone_sync_ptr;
> ld_string_t *owner_dn_ptr = NULL;
> - char *attrs[] = {"idnsAllowSyncPTR", "idnsAllowDynUpdate", NULL};
> ld_string_t *str_ptr = NULL;
> ldapdb_rdatalist_t rdlist_search;
> dns_rdatalist_t *rdlist_ptr = NULL;
> char **vals = NULL;
> + dns_name_t zone_name;
> + struct dns_fixedname ptr_name;
> + char *zone_dn = NULL;
> + settings_set_t *zone_settings = NULL;
>
> /*
> * Find parent zone entry and check if Dynamic Update is allowed.
> * @todo Try the cache first and improve split: SOA records are problematic.
> */
> ISC_LIST_INIT(rdlist_search);
> + dns_name_init(&zone_name, NULL);
> + dns_fixedname_init(&ptr_name);
> CHECK(str_new(mctx, &owner_dn));
> +
> CHECK(dnsname_to_dn(ldap_inst->zone_register, owner, owner_dn));
> - char *zone_dn = strstr(str_buf(owner_dn),", ");
> + zone_dn = strstr(str_buf(owner_dn),", ");
>
> if (zone_dn == NULL) { /* SOA record; owner = zone => owner_dn = zone_dn */
> zone_dn = (char *)str_buf(owner_dn);
> } else {
> zone_dn += 1; /* skip whitespace */
> }
>
> - CHECK(ldap_pool_getconnection(ldap_inst->pool, &ldap_conn));
> - CHECK(ldap_query(ldap_inst, ldap_conn, &ldap_qresult, zone_dn,
> - LDAP_SCOPE_BASE, attrs, 0,
> - "(&(objectClass=idnsZone)(idnsZoneActive=TRUE))"));
> + CHECK(dn_to_dnsname(mctx, zone_dn, &zone_name, NULL));
>
> - /* only 0 or 1 active zone can be returned from query */
> - entry = HEAD(ldap_qresult->ldap_entries);
> - if (entry == NULL) {
> - log_debug(3, "Active zone %s not found", zone_dn);
> - result = DNS_R_NOTAUTH;
> - goto cleanup;
> - }
> -
> - result = ldap_entry_getvalues(entry, "idnsAllowDynUpdate", &values);
> - if (result == ISC_R_SUCCESS) { /* zone specific setting found */
> - zone_dyn_update = (strcmp(HEAD(values)->value, "TRUE") == 0 )
> - ? ISC_TRUE : ISC_FALSE;
> + result = zr_get_zone_settings(ldap_inst->zone_register, &zone_name,
> + &zone_settings);
> + if (result != ISC_R_SUCCESS) {
> + if (result == ISC_R_NOTFOUND)
> + log_debug(3, "active zone '%s' not found", zone_dn);
> + CLEANUP_WITH(DNS_R_NOTAUTH);
> }
>
> + CHECK(setting_get_bool("dyn_update", zone_settings, &zone_dyn_update));
> if (!zone_dyn_update) {
> - log_debug(3, "Dynamic Update is not allowed in zone %s", zone_dn);
> - result = DNS_R_REFUSED;
> - goto cleanup;
> + log_debug(3, "dynamic update is not allowed in zone '%s'",
> + zone_dn);
> + CLEANUP_WITH(DNS_R_REFUSED);
> }
>
> - if (rdlist->type == dns_rdatatype_soa && mod_op == LDAP_MOD_DELETE) {
> - result = ISC_R_SUCCESS;
> - goto cleanup;
> - }
> + if (rdlist->type == dns_rdatatype_soa && mod_op == LDAP_MOD_DELETE)
> + CLEANUP_WITH(ISC_R_SUCCESS);
> +
> /* Flush modified record from the cache */
> CHECK(zr_get_zone_cache(ldap_inst->zone_register, owner, &cache));
> CHECK(discard_from_cache(cache, owner));
>
> + CHECK(ldap_pool_getconnection(ldap_inst->pool, &ldap_conn));
> +
> if (rdlist->type == dns_rdatatype_soa) {
> result = modify_soa_record(ldap_inst, ldap_conn, str_buf(owner_dn),
> HEAD(rdlist->rdata));
> @@ -2784,18 +2864,12 @@ modify_ldap_common(dns_name_t *owner, ldap_instance_t *ldap_inst,
> * use global plugin configuration: option "sync_ptr"
> */
>
> - result = ldap_entry_getvalues(entry, "idnsAllowSyncPTR", &values);
> - if (result == ISC_R_SUCCESS) { /* zone specific setting found */
> - zone_sync_ptr = (strcmp(HEAD(values)->value, "TRUE") == 0)
> - ? ISC_TRUE : ISC_FALSE;
> - }
> -
> + CHECK(setting_get_bool("sync_ptr", zone_settings, &zone_sync_ptr));
> if (!zone_sync_ptr) {
> - log_debug(3, "Sync PTR is not allowed in zone %s", zone_dn);
> - result = ISC_R_SUCCESS;
> - goto cleanup;
> + log_debug(3, "sync PTR is not allowed in zone '%s'", zone_dn);
> + CLEANUP_WITH(ISC_R_SUCCESS);
> }
> - log_debug(3, "Sync PTR is allowed for zone %s", zone_dn);
> + log_debug(3, "sync PTR is allowed for zone '%s'", zone_dn);
>
> /* Get string with IP address from change request
> * and convert it to in_addr structure. */
> @@ -2819,26 +2893,22 @@ modify_ldap_common(dns_name_t *owner, ldap_instance_t *ldap_inst,
> * 192.168.0.1 -> 1.0.168.192.in-addr.arpa
> *
> * @todo Check if it works for IPv6 correctly.
> - */
> - struct dns_fixedname name;
> - dns_fixedname_init(&name);
> - CHECK(dns_byaddr_createptrname2(&isc_ip, 0, dns_fixedname_name(&name)));
> + */
> + CHECK(dns_byaddr_createptrname2(&isc_ip, 0, dns_fixedname_name(&ptr_name)));
>
> /* Find PTR entry in LDAP. */
> - result = ldapdb_rdatalist_get(mctx, ldap_inst, dns_fixedname_name(&name),
> + result = ldapdb_rdatalist_get(mctx, ldap_inst, dns_fixedname_name(&ptr_name),
> NULL, &rdlist_search);
>
> /* Check the value of PTR entry. */
> if (mod_op == LDAP_MOD_DELETE && result == ISC_R_SUCCESS) {
> result = ldapdb_rdatalist_findrdatatype(&rdlist_search,
> dns_rdatatype_ptr, &rdlist_ptr);
> }
>
> if (result != ISC_R_SUCCESS && result != ISC_R_NOTFOUND) {
> - log_error("Can not synchronize PTR record, ldapdb_rdatalist_get = %d",
> - result);
> - result = ISC_R_FAILURE; /* Synchronization required: report error. */
> - goto cleanup;
> + log_error_r("can not synchronize PTR record, ldapdb_rdatalist_get");
> + CLEANUP_WITH(ISC_R_FAILURE); /* Synchronization required: report error. */
> }
>
> /*
> @@ -2854,46 +2924,38 @@ modify_ldap_common(dns_name_t *owner, ldap_instance_t *ldap_inst,
> }
>
> /* Get LDAP entry indentifier. */
> - CHECK(str_new(mctx, &owner_dn_ptr));
> - CHECK(dnsname_to_dn(ldap_inst->zone_register, dns_fixedname_name(&name),
> - owner_dn_ptr));
> + CHECK(str_new(mctx, &owner_dn_ptr));
> + CHECK(dnsname_to_dn(ldap_inst->zone_register, dns_fixedname_name(&ptr_name),
> + owner_dn_ptr));
>
> /*
> * @example
> * owner_dn_ptr = "idnsName=100.0.168, idnsname=192.in-addr.arpa,cn=dns,$SUFFIX"
> * owner_zone_dn_ptr = "idnsname=192.in-addr.arpa,cn=dns,$SUFFIX"
> */
> char *owner_zone_dn_ptr = strstr(str_buf(owner_dn_ptr),", ") + 1;
>
> /* Get attribute "idnsAllowDynUpdate" for reverse zone or use default. */
> - ldap_query_free(ISC_FALSE, &ldap_qresult);
> - zone_dyn_update = ldap_inst->dyn_update;
> - CHECK(ldap_query(ldap_inst, ldap_conn, &ldap_qresult, owner_zone_dn_ptr,
> - LDAP_SCOPE_BASE, attrs, 0,
> - "(&(objectClass=idnsZone)(idnsZoneActive=TRUE))"));
> + dns_name_free(&zone_name, mctx);
> + dns_name_init(&zone_name, NULL);
> + CHECK(dn_to_dnsname(mctx, owner_zone_dn_ptr, &zone_name, NULL));
>
> - /* Only 0 or 1 active zone can be returned from query. */
> - entry = HEAD(ldap_qresult->ldap_entries);
> - if (entry == NULL) {
> - log_debug(3, "Active zone %s not found", zone_dn);
> - result = ISC_R_NOTFOUND;
> + zone_settings = NULL;
> + result = zr_get_zone_settings(ldap_inst->zone_register, &zone_name,
> + &zone_settings);
> + if (result != ISC_R_SUCCESS) {
> + if (result == ISC_R_NOTFOUND)
> + log_debug(3, "active zone '%s' not found", zone_dn);
> goto cleanup;
> }
>
> - result = ldap_entry_getvalues(entry, "idnsAllowDynUpdate", &values);
> - if (result == ISC_R_SUCCESS) { /* zone specific setting found */
> - zone_dyn_update = (strcmp(HEAD(values)->value, "TRUE") == 0)
> - ? ISC_TRUE : ISC_FALSE;
> - }
> -
> + CHECK(setting_get_bool("dyn_update", zone_settings, &zone_dyn_update));
> if (!zone_dyn_update) {
> - log_debug(3, "Dynamic Update is not allowed in zone %s", owner_zone_dn_ptr);
> - result = ISC_R_NOPERM;
> - goto cleanup;
> + log_debug(3, "dynamic update is not allowed in zone "
> + "'%s'", zone_dn);
> + CLEANUP_WITH(ISC_R_NOPERM);
> }
>
> - log_debug(3, "Dynamic Update is allowed for zone %s", owner_zone_dn_ptr);
> -
> /*
> * Get string representation of PTR record value.
> *
> @@ -2951,24 +3013,25 @@ modify_ldap_common(dns_name_t *owner, ldap_instance_t *ldap_inst,
> change_ptr = NULL;
>
> /* Modify PTR record. */
> - CHECK(ldap_modify_do(ldap_inst, ldap_conn, str_buf(owner_dn_ptr), change, delete_node));
> + CHECK(ldap_modify_do(ldap_inst, ldap_conn, str_buf(owner_dn_ptr),
> + change, delete_node));
> cache = NULL;
> CHECK(zr_get_zone_cache(ldap_inst->zone_register,
> - dns_fixedname_name(&name), &cache));
> - CHECK(discard_from_cache(cache, dns_fixedname_name(&name)));
> + dns_fixedname_name(&ptr_name), &cache));
> + CHECK(discard_from_cache(cache, dns_fixedname_name(&ptr_name)));
> }
>
> cleanup:
> - ldap_query_free(ISC_FALSE, &ldap_qresult);
> ldap_pool_putconnection(ldap_inst->pool, &ldap_conn);
> str_destroy(&owner_dn_ptr);
> str_destroy(&owner_dn);
> str_destroy(&str_ptr);
> free_ldapmod(mctx, &change[0]);
> free_ldapmod(mctx, &change[1]);
> if (change_ptr != NULL) free_ldapmod(mctx, &change_ptr);
> ldapdb_rdatalist_destroy(mctx, &rdlist_search);
> free_char_array(mctx, &vals);
> + dns_name_free(&zone_name, mctx);
>
> return result;
> }
> @@ -3275,7 +3338,8 @@ update_zone(isc_task_t *task, isc_event_t *event)
> dns_name_t prevname;
> char *attrs_zone[] = {
> "idnsName", "idnsUpdatePolicy", "idnsAllowQuery",
> - "idnsAllowTransfer", "idnsForwardPolicy", "idnsForwarders", NULL
> + "idnsAllowTransfer", "idnsForwardPolicy", "idnsForwarders",
> + "idnsAllowDynUpdate", "idnsAllowSyncPTR", NULL
> };
> char *attrs_record[] = {
> "objectClass", "dn", NULL
> @@ -3412,6 +3476,7 @@ update_record(isc_task_t *task, isc_event_t *event)
> isc_result_t result;
> ldap_instance_t *inst = NULL;
> ldap_cache_t *cache = NULL;
> + isc_boolean_t serial_autoincrement;
> isc_mem_t *mctx;
> mctx = pevent->mctx;
>
> @@ -3422,6 +3487,7 @@ update_record(isc_task_t *task, isc_event_t *event)
> ldapdb_rdatalist_t rdatalist;
>
> /* Convert domain name from text to struct dns_name_t. */
> + settings_set_t *zone_settings = NULL;
> dns_name_t name;
> dns_name_t origin;
> dns_name_t prevname;
> @@ -3483,8 +3549,12 @@ update_record(isc_task_t *task, isc_event_t *event)
> }
>
> /* Do not bump serial during initial database dump. */
> - if (inst->serial_autoincrement && PSEARCH_ANY(pevent->chgtype)) {
> - CHECK(soa_serial_increment(mctx, inst, &origin));
> + if (PSEARCH_ANY(pevent->chgtype)) {
> + CHECK(zr_get_zone_settings(inst->zone_register, &origin, &zone_settings));
> + CHECK(setting_get_bool("serial_autoincrement", zone_settings,
> + &serial_autoincrement));
> + if (serial_autoincrement)
> + CHECK(soa_serial_increment(mctx, inst, &origin));
> }
> cleanup:
> if (result != ISC_R_SUCCESS)
> @@ -3731,6 +3801,9 @@ ldap_psearch_watcher(isc_threadarg_t arg)
> isc_result_t result;
> sigset_t sigset;
> isc_boolean_t flush_required;
> + isc_boolean_t psearch;
> + isc_uint32_t reconnect_interval;
> + const char *base = NULL;
>
> log_debug(1, "Entering ldap_psearch_watcher");
>
> @@ -3758,10 +3831,12 @@ ldap_psearch_watcher(isc_threadarg_t arg)
> /* Try to connect. */
> while (conn->handle == NULL) {
> CHECK_EXIT;
> + CHECK(setting_get_uint("reconnect_interval", inst->global_settings,
> + &reconnect_interval));
>
> log_error("ldap_psearch_watcher handle is NULL. "
> - "Next try in %ds", inst->reconnect_interval);
> - if (!sane_sleep(inst, inst->reconnect_interval))
> + "Next try in %ds", reconnect_interval);
> + if (!sane_sleep(inst, reconnect_interval))
> goto cleanup;
> handle_connection_error(inst, conn, ISC_TRUE);
> }
> @@ -3772,10 +3847,12 @@ restart:
> /* Perform initial lookup */
> ldap_query_free(ISC_TRUE, &ldap_qresult);
> flush_required = ISC_TRUE;
> - if (inst->psearch) {
> + CHECK(setting_get_str("base", inst->global_settings, &base));
> + CHECK(setting_get_bool("psearch", inst->global_settings, &psearch));
> + if (psearch) {
> log_debug(1, "Sending initial psearch lookup");
> ret = ldap_search_ext(conn->handle,
> - str_buf(inst->base),
> + base,
> LDAP_SCOPE_SUBTREE,
> /*
> * (objectClass==idnsZone AND idnsZoneActive==TRUE)
> @@ -3802,10 +3879,14 @@ restart:
> CHECK_EXIT;
> while (handle_connection_error(inst, conn, ISC_TRUE)
> != ISC_R_SUCCESS) {
> - log_error("ldap_psearch_watcher failed to handle "
> - "LDAP connection error. Reconnection "
> - "in %ds", inst->reconnect_interval);
> - if (!sane_sleep(inst, inst->reconnect_interval))
> + CHECK(setting_get_uint("reconnect_interval",
> + inst->global_settings,
> + &reconnect_interval));
> + log_error("ldap_psearch_watcher failed to "
> + "handle LDAP connection error. "
> + "Reconnection in %ds",
> + reconnect_interval);
> + if (!sane_sleep(inst, reconnect_interval))
> goto cleanup;
> }
> goto restart;
> @@ -3826,7 +3907,10 @@ restart:
> }
>
> if (restart_needed) {
> - if (!sane_sleep(inst, inst->reconnect_interval))
> + CHECK(setting_get_uint("reconnect_interval",
> + inst->global_settings,
> + &reconnect_interval));
> + if (!sane_sleep(inst, reconnect_interval))
> goto cleanup;
>
> goto restart;
> @@ -3894,3 +3978,8 @@ cleanup:
> return (isc_threadresult_t)0;
> }
>
> +settings_set_t *
> +ldap_instance_getsettings_local(ldap_instance_t *ldap_inst)
> +{
> + return ldap_inst->local_settings;
> +}
> diff --git a/src/ldap_helper.h b/src/ldap_helper.h
> index a1e52f044d5e81ace7fb2d3c2ab082ad838944d1..86c3d4ec040a073df8c89d67b93cbd9e1b3bfb77 100644
> --- a/src/ldap_helper.h
> +++ b/src/ldap_helper.h
> @@ -90,4 +90,9 @@ isc_result_t write_to_ldap(dns_name_t *owner, ldap_instance_t *ldap_inst,
> isc_result_t remove_from_ldap(dns_name_t *owner, ldap_instance_t *ldap_inst,
> dns_rdatalist_t *rdlist, isc_boolean_t delete_node);
>
> +/* Get cache associated with ldap_inst */
> +ldap_cache_t *ldap_instance_getcache(ldap_instance_t *ldap_inst);
> +
> +settings_set_t * ldap_instance_getsettings_local(ldap_instance_t *ldap_inst);
> +
> #endif /* !_LD_LDAP_HELPER_H_ */
> diff --git a/src/settings.c b/src/settings.c
> dissimilarity index 60%
> index 08164766172f5f915584ae51b43e3d64798eed71..0e37fc60c50d950c6f68652aafe819b3e2553864 100644
> --- a/src/settings.c
> +++ b/src/settings.c
> @@ -1,217 +1,626 @@
> -/*
> - * Authors: Martin Nagy <mnagy at redhat.com>
> - *
> - * Copyright (C) 2009 Red Hat
> - * see file 'COPYING' for use and warranty information
> - *
> - * This program is free software; you can redistribute it and/or
> - * modify it under the terms of the GNU General Public License as
> - * published by the Free Software Foundation; version 2 or later
> - *
> - * This program is distributed in the hope that it will be useful,
> - * but WITHOUT ANY WARRANTY; without even the implied warranty of
> - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> - * GNU General Public License for more details.
> - *
> - * You should have received a copy of the GNU General Public License
> - * along with this program; if not, write to the Free Software
> - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
> - */
> -
> -#include <isc/util.h>
> -#include <isc/mem.h>
> -#include <isc/result.h>
> -
> -#include <ctype.h>
> -#include <stdlib.h>
> -#include <strings.h>
> -
> -#include "log.h"
> -#include "settings.h"
> -#include "str.h"
> -#include "util.h"
> -#include "types.h"
> -
> -isc_boolean_t verbose_checks = ISC_FALSE; /* log each failure in CHECK() macro */
> -
> -/*
> - * 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;
> -
> - switch (setting->type) {
> - case ST_LD_STRING:
> - CHECK(str_init_char((ld_string_t *)setting->target, value));
> - break;
> - case ST_SIGNED_INTEGER:
> - case 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;
> - }
> - break;
> - case ST_BOOLEAN:
> - if (strncasecmp(value, "yes", 3) == 0)
> - (*(isc_boolean_t *)setting->target) = ISC_TRUE;
> - else if (strncasecmp(value, "no", 2) == 0)
> - (*(isc_boolean_t *)setting->target) = ISC_FALSE;
> - else {
> - log_error("unknown boolean expression (%s: %s)",
> - setting->name, value);
> - result = ISC_R_FAILURE;
> - goto cleanup;
> - }
> - break;
> - default:
> - 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;
> - case ST_BOOLEAN:
> - *(isc_boolean_t *)setting->target =
> - setting->default_value.value_boolean;
> - 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;
> -}
> -
> -isc_result_t
> -get_enum_description(const enum_txt_assoc_t *map, int value, const char **desc) {
> - const enum_txt_assoc_t *record;
> -
> - REQUIRE(map != NULL);
> - REQUIRE(desc != NULL && *desc == NULL);
> -
> - for (record = map;
> - record->description != NULL && record->value != -1;
> - record++) {
> - if (record->value == value) {
> - *desc = record->description;
> - return ISC_R_SUCCESS;
> - }
> - }
> - return ISC_R_NOTFOUND;
> -}
> +/*
> + * Authors: Martin Nagy <mnagy at redhat.com>
> + *
> + * Copyright (C) 2009-2012 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 or later
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
> + */
> +
> +#include <isc/util.h>
> +#include <isc/mem.h>
> +#include <isc/task.h>
> +#include <isc/result.h>
> +#include <isc/string.h>
> +#include <isc/int.h>
> +#include <isc/parseint.h>
> +#include <dns/name.h>
> +
> +#include <ctype.h>
> +#include <stdlib.h>
> +#include <string.h>
> +#include <strings.h>
> +
> +#include "log.h"
> +#include "settings.h"
> +#include "str.h"
> +#include "util.h"
> +#include "types.h"
> +#include "ldap_helper.h"
> +#include "zone_register.h"
> +
> +isc_boolean_t verbose_checks = ISC_FALSE; /* log each failure in CHECK() macro */
> +
> +/** Built-in defaults. */
> +static const setting_t settings_default[] = {
> + { "uri", no_default_string }, /* User have to set this */
> + { "connections", default_uint(2) },
> + { "reconnect_interval", default_uint(60) },
> + { "zone_refresh", default_uint(0) },
> + { "timeout", default_uint(10) },
> + { "cache_ttl", default_uint(120) },
> + { "timeout", default_uint(10) },
> + { "base", no_default_string }, /* User have to set this */
> + { "auth_method", default_string("none") },
> + { "bind_dn", default_string("") },
> + { "password", default_string("") },
> + { "krb5_principal", default_string("") },
> + { "sasl_mech", default_string("GSSAPI") },
> + { "sasl_user", default_string("") },
> + { "sasl_auth_name", default_string("") },
> + { "sasl_realm", default_string("") },
> + { "sasl_password", default_string("") },
> + { "krb5_keytab", default_string("") },
> + { "fake_mname", default_string("") },
> + { "psearch", default_boolean(ISC_FALSE) },
> + { "ldap_hostname", default_string("") },
> + { "sync_ptr", default_boolean(ISC_FALSE) },
> + { "dyn_update", default_boolean(ISC_FALSE) },
> + { "serial_autoincrement", default_boolean(ISC_FALSE) },
> + { "verbose_checks", default_boolean(ISC_FALSE) },
> + end_of_settings
> +};
> +
> +/** Settings set for built-in defaults. */
> +const settings_set_t const settings_default_set = {
> + NULL,
> + "built-in defaults",
> + NULL,
> + (setting_t *) &settings_default[0]
> +};
> +
> +/**
> + * @param[in] name Setting name.
> + * @param[in] set Set of settings to start search in.
> + * @param[in] recursive Continue with search in parent sets if setting was
> + * not found in set passed by caller.
> + * @param[in] filled_only Consider settings without value as non-existent.
> + * @param[out] found Pointer to found setting_t. Ignored if found is NULL.
> + *
> + * @pre found == NULL || (found != NULL && *found == NULL)
> + *
> + * @retval ISC_R_SUCCESS
> + * @retval ISC_R_NOTFOUND
> + */
> +isc_result_t
> +setting_find(const char *name, const settings_set_t *set,
> + isc_boolean_t recursive, isc_boolean_t filled_only,
> + setting_t **found) {
> +
> + REQUIRE(name != NULL);
> + REQUIRE(found == NULL || *found == NULL);
> +
> + while (set != NULL) {
> + log_debug(20, "examining set of settings '%s'", set->name);
> + for (setting_t *setting = set->first_setting;
> + setting->name;
> + setting++) {
> +
> + if (strcmp(name, setting->name) == 0) {
> + if (setting->filled || !filled_only) {
> + if (found != NULL)
> + *found = setting;
> + log_debug(20, "setting '%s' was found "
> + "in set '%s'", name,
> + set->name);
> + return ISC_R_SUCCESS;
> + } else {
> + break; /* continue with parent set */
> + }
> + }
> +
> + }
> + if (recursive)
> + set = set->parent_set;
> + else
> + break;
> + }
> + return ISC_R_NOTFOUND;
> +}
> +
> +
> +/**
> + * Get value associated with a setting. Search starts in set of settings
> + * passed by caller and continues in parent sets until the setting with defined
> + * value is found.
> + *
> + * @warning
> + * This function is not expected to fail because all settings should
> + * have default value defined (in topmost set of settings).
> + * Caller should always check the return value, regardless this assumption.
> + *
> + * @param[out] target Type of pointer must agree with requested setting type.
> + * @retval ISC_R_SUCCESS Required value was found and target was filled in.
> + * @retval ISC_R_NOTFOUND Value is not defined in specified set of
> + * settings either in parent sets.
> + * @retval ISC_R_UNEXPECTED Type mismatch between _get_type() function and type
> + * of setting in settings tree. (I.e. programming
> + * error.)
> + */
> +/* This hack enables type checking for setting_get_ functions. */
> +#define setting_getter(func_name, c_type, enum_type, value_name) \
> +isc_result_t \
> +setting_get_##func_name(const char * const name, \
> + const settings_set_t * const set, \
> + c_type target) \
> +{ \
> + isc_result_t result; \
> + setting_t *setting = NULL; \
> + \
> + REQUIRE(name != NULL); \
> + REQUIRE(target != NULL); \
> + \
> + CHECK(setting_find(name, set, ISC_TRUE, ISC_TRUE, &setting)); \
> + \
> + if (setting->type != enum_type) { \
> + log_bug("incompatible setting data type requested " \
> + "for name '%s' in set of settings '%s'", \
> + name, set->name); \
> + return ISC_R_UNEXPECTED; \
> + } \
> + *target = setting->value.value_name; \
> + \
> + return ISC_R_SUCCESS; \
> + \
> +cleanup: \
> + log_bug("setting '%s' was not found in settings tree", name); \
> + return result; \
> +}
> +
> +setting_getter(uint, isc_uint32_t *, ST_UNSIGNED_INTEGER, value_uint)
> +setting_getter(str, const char **, ST_STRING, value_char)
> +setting_getter(bool, isc_boolean_t *, ST_BOOLEAN, value_boolean)
I don't like this part of code much. When you declare setting_get* functions
this way, you end with 3 copies of nearly same code which leads to bloat of
ldap.so.
In my opinion just one function with switch (enum_type) {} inside which returns
void* might be better. After that we can declare wrappers which will re-type
void* to appropriate type. Example:
isc_result_t
setting_get(const char *name, const settings_set_t *set, enum_type, void *target);
#define setting_get_uint(name, set, target) setting_get(name, set, ST_UNSIGNED_INTEGER, target);
This way we will end with only one copy of setting_get function.
> +
> +/**
> + * Convert and copy value to setting structure. Mutual exclusion during write
> + * is ensured by isc_task_beginexclusive(task).
> + *
> + * @retval ISC_R_SUCCESS New value was converted and copied.
> + * @retval ISC_R_IGNORE New and old values are same, no change was made.
> + * @retval ISC_R_NOMEMORY
> + * @retval ISC_R_UNEXPECTEDEND
> + * @retval ISC_R_UNEXPECTEDTOKEN
> + * @retval others Other errors from isc_parse_uint32().
> + */
> +static isc_result_t
> +set_value(isc_mem_t *mctx, setting_t *setting, const char *value,
> + isc_task_t *task)
> +{
> + isc_result_t result;
> + isc_result_t lock = ISC_R_IGNORE;
> + isc_uint32_t numeric_value;
> + isc_uint32_t len;
> +
> + REQUIRE(setting != NULL);
> + REQUIRE(value != NULL);
> + REQUIRE(task != NULL);
> +
> + /* Check and convert new values. */
> + switch (setting->type) {
> + case ST_STRING:
> + if (setting->filled &&
> + strcmp(setting->value.value_char, value) == 0)
> + return ISC_R_IGNORE;
> + break;
> +
> + case ST_UNSIGNED_INTEGER:
> + if (*value == '\0')
> + CLEANUP_WITH(ISC_R_UNEXPECTEDEND);
> +
> + result = isc_parse_uint32(&numeric_value, value, 10);
> + if (result != ISC_R_SUCCESS) {
> + log_error_r("setting '%s' has to be unsigned integer "
> + "(base 10)", setting->name);
> + goto cleanup;
> + }
> + if (setting->filled &&
> + setting->value.value_uint == numeric_value)
> + CLEANUP_WITH(ISC_R_IGNORE);
> + break;
> +
> + case ST_BOOLEAN:
> + if (strcasecmp(value, "yes") == 0 ||
> + strcasecmp(value, "true") == 0)
> + numeric_value = 1;
> + else if (strcasecmp(value, "no") == 0 ||
> + strcasecmp(value, "false") == 0)
> + numeric_value = 0;
> + else {
> + log_error("unknown boolean expression "
> + "(setting '%s': value '%s')",
> + setting->name, value);
> + CLEANUP_WITH(ISC_R_UNEXPECTEDTOKEN);
> + }
> + if (setting->filled &&
> + setting->value.value_boolean == ISC_TF(numeric_value))
> + CLEANUP_WITH(ISC_R_IGNORE);
> + break;
> + }
> +
> + /* Switch to single thread mode and write new value. */
> + lock = isc_task_beginexclusive(task);
> + RUNTIME_CHECK(lock == ISC_R_SUCCESS || lock == ISC_R_LOCKBUSY);
> +
> + switch (setting->type) {
> + case ST_STRING:
> + len = strlen(value) + 1;
> + if (setting->allocated_dynamically)
> + isc_mem_free(mctx, setting->value.value_char);
> + CHECKED_MEM_ALLOCATE(mctx, setting->value.value_char, len);
> + setting->allocated_dynamically = ISC_TRUE;
> + CHECK(isc_string_copy(setting->value.value_char, len, value));
> + break;
> +
> + case ST_UNSIGNED_INTEGER:
> + setting->value.value_uint = numeric_value;
> + break;
> +
> + case ST_BOOLEAN:
> + setting->value.value_boolean = ISC_TF(numeric_value);
> + break;
> + }
> + setting->filled = 1;
> + result = ISC_R_SUCCESS;
> +
> +cleanup:
> + if (lock == ISC_R_SUCCESS)
> + isc_task_endexclusive(task);
> + return result;
> +}
> +
> +/**
> + * Change value in given set of settings (non-recursively, parent sets are
> + * not affected in any way). Function will fail if setting with given name is
> + * not a part of set of settings.
> + * Mutual exclusion is ensured by set_value().
> + *
> + * @warning
> + * Failure in this function usually points to insufficient input validation
> + * OR logic error.
> + * Caller should always check the return value.
> + *
> + * @retval ISC_R_SUCCESS Value was changed.
> + * @retval ISC_R_IGNORE Value wasn't changed because it is same as original.
> + * @retval ISC_R_NOTFOUND Setting was not found in given set of settings.
> + * @retval ISC_R_NOMEMORY
> + * @retval Others Conversion errors.
> + */
> +isc_result_t
> +setting_set(const char *const name, const settings_set_t *set,
> + const char *const value, isc_task_t *task)
> +{
> + isc_result_t result;
> + setting_t *setting = NULL;
> +
> + CHECK(setting_find(name, set, ISC_FALSE, ISC_FALSE, &setting));
> +
> + return set_value(set->mctx, setting, value, task);
> +
> +cleanup:
> + log_bug("setting '%s' was not found in set of settings '%s'", name,
> + set->name);
> + return result;
> +}
> +
> +/**
> + * Un-set value in given set of settings (non-recursively, parent sets are
> + * not affected in any way). Function will fail if setting with given name is
> + * not a part of set of settings.
> + * Mutual exclusion is ensured by isc_task_beginexclusive().
> + *
> + * @warning
> + * Failure in this function usually points to logic error.
> + * Caller should always check return value.
> + *
> + * @retval ISC_R_SUCCESS Setting was un-set.
> + * @retval ISC_R_IGNORE Setting wasn't changed because wasn't set.
> + * @retval ISC_R_NOTFOUND Required setting was not found
> + * in given set of settings.
> + */
> +isc_result_t
> +setting_unset(const char *const name, const settings_set_t *set,
> + isc_task_t *task)
> +{
> + isc_result_t result;
> + isc_result_t lock = ISC_R_IGNORE;
> + setting_t *setting = NULL;
> +
> + REQUIRE(task != NULL);
> +
> + CHECK(setting_find(name, set, ISC_FALSE, ISC_FALSE, &setting));
> +
> + if (!setting->filled)
> + return ISC_R_IGNORE;
> +
> + lock = isc_task_beginexclusive(task);
> + RUNTIME_CHECK(lock == ISC_R_SUCCESS || lock == ISC_R_LOCKBUSY);
> +
> + switch (setting->type) {
> + case ST_STRING:
> + if (setting->allocated_dynamically)
> + isc_mem_free(set->mctx, setting->value.value_char);
> + setting->allocated_dynamically = ISC_FALSE;
> + break;
> +
> + case ST_UNSIGNED_INTEGER:
> + case ST_BOOLEAN:
> + break;
> + }
> + setting->filled = 0;
> +
> +cleanup:
> + if (lock == ISC_R_SUCCESS)
> + isc_task_endexclusive(task);
> + if (result == ISC_R_NOTFOUND)
> + log_bug("setting '%s' was not found in set of settings '%s'",
> + name, set->name);
> +
> + return result;
> +}
> +
> +/**
> + * Change setting 'name' to value specified by attribute 'attr_name' in LDAP
> + * entry. Setting is un-set if specified value is missing in LDAP entry.
> + *
> + * @warning Multi-value attributes are no supported.
> + *
> + * @retval ISC_R_SUCCESS Setting was changed (set or unset).
> + * @retval ISC_R_IGNORE Setting wasn't changed because value in settings set
> + * and LDAP entry was same.
> + * @retval ISC_R_NOTFOUND Required setting was not found in given set.
> + * @retval Others Memory allocation or conversion errors.
> + */
> +isc_result_t
> +setting_update_from_ldap_entry(const char *name, settings_set_t *set,
> + const char *attr_name, ldap_entry_t *entry,
> + isc_task_t *task) {
> + isc_result_t result;
> + setting_t *setting = NULL;
> + ldap_valuelist_t values;
> +
> + CHECK(setting_find(name, set, ISC_FALSE, ISC_FALSE, &setting));
> + result = ldap_entry_getvalues(entry, attr_name, &values);
> + if (result == ISC_R_NOTFOUND || HEAD(values) == NULL) {
> + CHECK(setting_unset(name, set, task));
> + log_debug(2, "setting '%s' (%s) was deleted in object '%s'",
> + name, attr_name, entry->dn);
> + return ISC_R_SUCCESS;
> +
> + } else if (result != ISC_R_SUCCESS) {
> + goto cleanup;
> + }
> +
> + if (HEAD(values) != TAIL(values)) {
> + log_bug("multi-value attributes are not supported: attribute "
> + "'%s' in entry '%s'", attr_name, entry->dn);
> + return ISC_R_NOTIMPLEMENTED;
> + }
> +
> + CHECK(setting_set(name, set, HEAD(values)->value, task));
> + log_debug(2, "setting '%s' (%s) was changed to '%s' in object '%s'",
> + name, attr_name, HEAD(values)->value, entry->dn);
> +
> +cleanup:
> + if (result == ISC_R_NOTFOUND)
> + log_bug("setting '%s' was not found in settings set '%s'",
> + name, set->name);
> + return result;
> +}
> +
> +/**
> + * Allocate new set of settings, fill it with values from specified default set
> + * and (optionally) link the new set of settings to its parent set.
> + *
> + * @param[in] default_settings Array with pre-filled setting structures.
> + * @param[in] default_set_length Default set length in bytes.
> + * @param[in] set_name Human readable name for this set of settings.
> + *
> + * @pre target != NULL && *target == NULL
> + * @pre default_settings != NULL
> + * @pre default_set_length > 0, default_set_length <= sizeof(default_settings)
> + *
> + * @retval ISC_R_SUCCESS
> + * @retval ISC_R_NOMEMORY
> + *
> + * @note How to create local_settings which overrides default_settings:
> + * @code
> + * const setting_t default_settings[] = {
> + * { "connections", default_uint(2) },
> + * }
> + * const settings_set_t default_settings_set = {
> + * NULL,
> + * NULL,
> + * (setting_t *) &default_settings[0]
> + * };
> + * const setting_t local_settings[] = {
> + * { "connections", no_default_uint },
> + * }
> + *
> + * settings_set_t *local_settings = NULL;
> + * result = settings_set_create(mctx, default_settings,
> + * sizeof(default_settings), &default_settings_set,
> + * &local_settings);
> + * @endcode
> + */
> +isc_result_t
> +settings_set_create(isc_mem_t *mctx, const setting_t default_settings[],
> + const unsigned int default_set_length, const char *set_name,
> + const settings_set_t *const parent_set,
> + settings_set_t **target) {
> + isc_result_t result = ISC_R_FAILURE;
> + settings_set_t *new_set = NULL;
> +
> + REQUIRE(target != NULL && *target == NULL);
> + REQUIRE(default_settings != NULL);
> + REQUIRE(default_set_length > 0);
> +
> + CHECKED_MEM_ALLOCATE(mctx, new_set, default_set_length);
> + ZERO_PTR(new_set);
> + isc_mem_attach(mctx, &new_set->mctx);
> + new_set->parent_set = parent_set;
> +
> + CHECKED_MEM_ALLOCATE(mctx, new_set->first_setting, default_set_length);
> + memcpy(new_set->first_setting, default_settings, default_set_length);
> +
> + CHECKED_MEM_ALLOCATE(mctx, new_set->name, strlen(set_name) + 1);
> + strcpy(new_set->name, set_name);
> +
> + *target = new_set;
> + result = ISC_R_SUCCESS;
> +
> +cleanup:
> + if (result != ISC_R_SUCCESS)
> + settings_set_free(&new_set);
> +
> + return result;
> +}
> +
> +/**
> + * Free dynamically allocated memory associated with given set of settings.
> + * @pre *set is initialized set of settings, set != NULL && *set != NULL
> + * @post *set == NULL
> + */
> +void
> +settings_set_free(settings_set_t **set) {
> + isc_mem_t *mctx = NULL;
> + setting_t *s = NULL;
> +
> + if (set == NULL || *set == NULL)
> + return;
> +
> + if ((*set)->mctx != NULL) {
> + mctx = (*set)->mctx;
> + for (s = (*set)->first_setting; s->name != NULL; s++) {
> + if (s->allocated_dynamically)
> + isc_mem_free(mctx, s->value.value_char);
> + }
> + if ((*set)->first_setting != NULL)
> + isc_mem_free(mctx, (*set)->first_setting);
> + isc_mem_free(mctx, (*set)->name);
> + isc_mem_free(mctx, *set);
> + isc_mem_detach(&mctx);
> + }
> + *set = NULL;
> +}
> +
> +/**
> + * Set all values specified by vector of strings to setting set. Setting name
> + * is separated from it's argument with one or more characters defined by
> + * @link SETTING_NAME_SEPARATORS at endlink.
> + *
> + * @retval ISC_R_SUCCESS All strings in argument vector were processed and set.
> + * @retval Others Memory or parsing errors.
> + *
> + * @warning One string in argument vector is limited to
> + * @link SETTING_LINE_MAXLENGTH at endlink.
> + *
> + * @note
> + * @code{.txt}
> + * Calling settings_set_fill() with argument array
> + *
> + * {"setting1 value 1 ",
> + * "bind_dn cn=Directory manager" }
> + *
> + * will result in setting values to two separate settings:
> + *
> + * "setting1" = "value 1 "
> + * "bind_dn" = "cn=Directory manager"
> + *
> + * Please note the positions of white spaces.
> + * @endcode
> + */
> +isc_result_t
> +settings_set_fill(settings_set_t *set, const char *const *argv,
> + isc_task_t *task)
> +{
> + isc_result_t result;
> + int i;
> + const char *name;
> + char *value;
> +
> + for (i = 0; argv[i] != NULL; i++) {
> + char buff[SETTING_LINE_MAXLENGTH] = "";
> + CHECK(isc_string_copy(buff, SETTING_LINE_MAXLENGTH, argv[i]));
> + value = buff;
> + name = isc_string_separate(&value, SETTING_NAME_SEPARATORS);
> + if (name == NULL || value == NULL)
> + CLEANUP_WITH(ISC_R_UNEXPECTEDEND);
> + value += strspn(value, SETTING_NAME_SEPARATORS);
> + if (setting_find(name, set, ISC_FALSE, ISC_TRUE, NULL)
> + != ISC_R_NOTFOUND) {
> + log_error("multiple definitions of setting '%s' in "
> + "set of settings '%s'", name, set->name);
> + CLEANUP_WITH(ISC_R_EXISTS);
> + }
> + result = setting_set(name, set, value, task);
> + if (result != ISC_R_SUCCESS && result != ISC_R_IGNORE)
> + goto cleanup;
> + }
> +
> + return ISC_R_SUCCESS;
> +
> +cleanup:
> + log_error_r("cannot parse settings from '%s': "
> + "problematic configuration line:"
> + "\n%s\n"
> + "error code", set->name, argv[i]);
> + /* TODO: Free memory in case of error. */
> + return result;
> +}
> +
> +/**
> + * Check if all the settings in given set of setting have defined value,
> + * possibly indirectly through parent set of settings.
> + *
> + * Error message is logged for each setting without defined value.
> + *
> + * @retval ISC_TRUE All settings have value defined.
> + * @retval ISC_FALSE At least one setting do not have defined value.
> + */
> +isc_boolean_t
> +settings_set_isfilled(settings_set_t *set) {
> + isc_result_t result;
> + isc_boolean_t isfiled = ISC_TRUE;
> +
> + REQUIRE(set != NULL);
> +
> + for (int i = 0; set->first_setting[i].name != NULL; i++) {
> + const char *name = set->first_setting[i].name;
> + result = setting_find(name, set, ISC_TRUE, ISC_TRUE, NULL);
> + if (result != ISC_R_SUCCESS) {
> + log_error_r("argument '%s' must be set "
> + "in set of settings '%s'", name, set->name);
> + isfiled = ISC_FALSE;
> + }
> + }
> + return isfiled;
> +}
> +
> +isc_result_t
> +get_enum_description(const enum_txt_assoc_t *map, int value, const char **desc) {
> + const enum_txt_assoc_t *record;
> +
> + REQUIRE(map != NULL);
> + REQUIRE(desc != NULL && *desc == NULL);
> +
> + for (record = map;
> + record->description != NULL && record->value != -1;
> + record++) {
> + if (record->value == value) {
> + *desc = record->description;
> + return ISC_R_SUCCESS;
> + }
> + }
> + return ISC_R_NOTFOUND;
> +}
> diff --git a/src/settings.h b/src/settings.h
> index 53910ee11c2ac1f87db25fac8f24f5743f4312e4..fe86d44b5787319e3f8852081bcd36014c8a03d5 100644
> --- a/src/settings.h
> +++ b/src/settings.h
> @@ -1,7 +1,7 @@
> /*
> * Authors: Martin Nagy <mnagy at redhat.com>
> *
> - * Copyright (C) 2009 Red Hat
> + * Copyright (C) 2009-2012 Red Hat
> * see file 'COPYING' for use and warranty information
> *
> * This program is free software; you can redistribute it and/or
> @@ -23,28 +23,42 @@
>
> #include <isc/types.h>
> #include "types.h"
> +#include "str.h"
> +#include "ldap_entry.h"
> +
> +#define SETTING_LINE_MAXLENGTH 255
> +#define SETTING_NAME_SEPARATORS " \t"
> +#define SETTING_SET_NAME_LOCAL "named.conf"
> +#define SETTING_SET_NAME_GLOBAL "LDAP idnsConfig object"
> +#define SETTING_SET_NAME_ZONE "LDAP idnsZone object"
>
> typedef struct setting setting_t;
> +typedef struct settings_set settings_set_t;
>
> +/* Make sure that cases in get_value_ptr() are synchronized */
> typedef enum {
> - ST_LD_STRING,
> - ST_SIGNED_INTEGER,
> + ST_STRING,
> ST_UNSIGNED_INTEGER,
> ST_BOOLEAN,
> } 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;
> + char *value_char;
> + isc_uint32_t value_uint;
> isc_boolean_t value_boolean;
> - } default_value;
> - void *target;
> + } value;
> + isc_boolean_t filled;
> + isc_boolean_t allocated_dynamically;
This is quite long name, isn't it? What about "dynamic" or "isdynamic"?
> +};
> +
> +struct settings_set {
> + isc_mem_t *mctx;
> + char *name;
> + const settings_set_t *parent_set;
> + setting_t *first_setting;
> };
>
> /*
> @@ -59,24 +73,56 @@ struct 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
> -#define default_boolean(val) 0, 1, ST_BOOLEAN, { .value_boolean = (val) }, NULL
> +#define default_string(val) ST_STRING, { .value_char = (val) }, ISC_TRUE, ISC_FALSE
> +#define default_uint(val) ST_UNSIGNED_INTEGER, { .value_uint = (val) }, ISC_TRUE, ISC_FALSE
> +#define default_boolean(val) ST_BOOLEAN, { .value_boolean = (val) }, ISC_TRUE, ISC_FALSE
> /* 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
> -#define no_default_boolean 0, 1, ST_BOOLEAN, { .value_boolean = ISC_FALSE }, NULL
> +#define no_default_string ST_STRING, { .value_char = NULL }, ISC_FALSE, ISC_FALSE
> +#define no_default_uint ST_UNSIGNED_INTEGER, { .value_uint = 0 }, ISC_FALSE, ISC_FALSE
> +#define no_default_boolean ST_BOOLEAN, { .value_boolean = ISC_FALSE }, ISC_FALSE, ISC_FALSE
>
> /* This is used in the end of setting_t arrays. */
> -#define end_of_settings { NULL, default_sint(0) }
> +#define end_of_settings { NULL, default_uint(0) }
>
> /*
> * Prototypes.
> */
> isc_result_t
> -set_settings(setting_t *settings, const char * const* argv);
> +settings_set_create(isc_mem_t *mctx, const setting_t default_settings[],
> + const unsigned int default_set_length, const char *set_name,
> + const settings_set_t *const parent_set,
> + settings_set_t **target);
> +
> +void
> +settings_set_free(settings_set_t **set);
> +
> +isc_result_t
> +settings_set_fill(settings_set_t *set, const char *const *argv,
> + isc_task_t *task);
> +
> +isc_boolean_t
> +settings_set_isfilled(settings_set_t *set);
> +
> +isc_result_t
> +setting_get_uint(const char * const name, const settings_set_t * const set,
> + isc_uint32_t * target);
> +
> +isc_result_t
> +setting_get_str(const char * const name, const settings_set_t * const set,
> + const char ** target);
> +
> +isc_result_t
> +setting_get_bool(const char * const name, const settings_set_t * const set,
> + isc_boolean_t * target);
> +
> +isc_result_t
> +setting_set(const char *const name, const settings_set_t *set,
> + const char *const value, isc_task_t *task);
> +
> +isc_result_t
> +setting_update_from_ldap_entry(const char *name, settings_set_t *set,
> + const char *attr_name, ldap_entry_t *entry,
> + isc_task_t *task);
>
> isc_result_t
> get_enum_description(const enum_txt_assoc_t *map, int value, const char **desc);
> diff --git a/src/zone_manager.c b/src/zone_manager.c
> index c19c3b6c91ff8114fcb15eacba0f74ec46047986..efc2299c4cd3280dc688d14b77d0d721e1554284 100644
> --- a/src/zone_manager.c
> +++ b/src/zone_manager.c
> @@ -31,6 +31,7 @@
> #include <dns/zone.h>
>
> #include <string.h>
> +#include <unistd.h>
>
> #include "ldap_convert.h"
> #include "ldap_helper.h"
> @@ -112,18 +113,13 @@ manager_create_db_instance(isc_mem_t *mctx, const char *name,
> {
> isc_result_t result;
> db_instance_t *db_inst = NULL;
> - unsigned int zone_refresh;
> + isc_uint32_t zone_refresh;
> isc_boolean_t psearch;
> isc_timermgr_t *timer_mgr;
> isc_interval_t interval;
> isc_timertype_t timer_type = isc_timertype_inactive;
> isc_task_t *task;
> - setting_t manager_settings[] = {
> - { "zone_refresh", default_uint(0) },
> - { "psearch", default_boolean(0) },
> - { "verbose_checks", default_boolean(0) },
> - end_of_settings
> - };
> + settings_set_t *local_settings = NULL;
>
> REQUIRE(name != NULL);
> REQUIRE(dyndb_args != NULL);
> @@ -137,12 +133,6 @@ manager_create_db_instance(isc_mem_t *mctx, const char *name,
> CLEANUP_WITH(ISC_R_EXISTS);
> }
>
> - /* Parse settings. */
> - manager_settings[0].target = &zone_refresh;
> - manager_settings[1].target = &psearch;
> - manager_settings[2].target = &verbose_checks; /* global variable */
> - CHECK(set_settings(manager_settings, argv));
> -
> CHECKED_MEM_GET_PTR(mctx, db_inst);
> ZERO_PTR(db_inst);
>
> @@ -157,13 +147,14 @@ manager_create_db_instance(isc_mem_t *mctx, const char *name,
> *
> * Timer must exist before refresh_zones_from_ldap() is called. */
> timer_mgr = dns_dyndb_get_timermgr(dyndb_args);
> +
> + local_settings = ldap_instance_getsettings_local(db_inst->ldap_inst);
> + CHECK(setting_get_uint("zone_refresh", local_settings, &zone_refresh));
> + CHECK(setting_get_bool("psearch", local_settings, &psearch));
> + CHECK(setting_get_bool("verbose_checks", local_settings, &verbose_checks));
> +
> isc_interval_set(&interval, zone_refresh, 0);
>
> - if (zone_refresh && psearch) {
> - log_error("Zone refresh and persistent search are enabled at same time! "
> - "Only persistent search will be used.");
> - }
> -
> if (zone_refresh && !psearch) {
> timer_type = isc_timertype_ticker;
> } else {
> diff --git a/src/zone_register.c b/src/zone_register.c
> index e52397357f2ef482d8dfee33f7564cd9157afa56..e8d844f7bd1de83a6c894ea18fed127af081fd0f 100644
> --- a/src/zone_register.c
> +++ b/src/zone_register.c
> @@ -27,12 +27,14 @@
> #include <dns/result.h>
> #include <dns/zone.h>
>
> +#include <isc/string.h>
> #include <string.h>
>
> #include "log.h"
> #include "util.h"
> #include "zone_register.h"
> #include "rdlist.h"
> +#include "settings.h"
>
> /*
> * The zone register is a red-black tree that maps a dns name of a zone to the
> @@ -47,19 +49,45 @@ struct zone_register {
> isc_mem_t *mctx;
> isc_rwlock_t rwlock;
> dns_rbt_t *rbt;
> + settings_set_t *global_settings;
> };
>
> typedef struct {
> dns_zone_t *zone;
> char *dn;
> isc_uint32_t serial; /* last value processed by plugin (!= value in DB) */
> unsigned char digest[RDLIST_DIGESTLENGTH]; /* MD5 digest from all RRs in zone record */
> ldap_cache_t *cache;
> + settings_set_t *settings;
> } zone_info_t;
>
> /* Callback for dns_rbt_create(). */
> static void delete_zone_info(void *arg1, void *arg2);
>
> +/**
> + * Zone specific settings from idnsZone object:
> + * NAME 'idnsZone'
> + * MUST ( idnsName $ idnsZoneActive $ idnsSOAmName $ idnsSOArName $
> + * idnsSOAserial $ idnsSOArefresh $ idnsSOAretry $ idnsSOAexpire $
> + * idnsSOAminimum
> + * )
> + * MAY ( idnsUpdatePolicy $ idnsAllowQuery $ idnsAllowTransfer $
> + * idnsAllowSyncPTR $ idnsForwardPolicy $ idnsForwarders
> + * )
> + *
> + * These structures are templates. They will be copied for each zone instance.
> + */
> +static const setting_t zone_settings[] = {
> + { "dyn_update", no_default_boolean },
> + { "update_policy", no_default_string },
> + { "allow_query", no_default_string },
> + { "allow_transfer", no_default_string },
> + { "sync_ptr", no_default_boolean },
> + { "forward_policy", no_default_string },
> + { "forwarders", no_default_string },
> + end_of_settings
> +};
> +
> dns_rbt_t *
> zr_get_rbt(zone_register_t *zr)
> {
> @@ -77,7 +105,7 @@ zr_get_mctx(zone_register_t *zr) {
> * Create a new zone register.
> */
> isc_result_t
> -zr_create(isc_mem_t *mctx, zone_register_t **zrp)
> +zr_create(isc_mem_t *mctx, settings_set_t *glob_settings, zone_register_t **zrp)
> {
> isc_result_t result;
> zone_register_t *zr = NULL;
> @@ -89,6 +117,7 @@ zr_create(isc_mem_t *mctx, zone_register_t **zrp)
> isc_mem_attach(mctx, &zr->mctx);
> CHECK(dns_rbt_create(mctx, delete_zone_info, mctx, &zr->rbt));
> CHECK(isc_rwlock_init(&zr->rwlock, 0, 0));
> + zr->global_settings = glob_settings;
>
> *zrp = zr;
> return ISC_R_SUCCESS;
> @@ -128,23 +157,31 @@ zr_destroy(zone_register_t **zrp)
> /*
> * Create a new zone info structure.
> */
> +#define PRINT_BUFF_SIZE 255
> static isc_result_t
> create_zone_info(isc_mem_t *mctx, dns_zone_t *zone, const char *dn,
> - const isc_interval_t *cache_ttl, const isc_boolean_t *psearch,
> - zone_info_t **zinfop)
> + settings_set_t *global_settings, zone_info_t **zinfop)
> {
> isc_result_t result;
> zone_info_t *zinfo;
> + char settings_name[PRINT_BUFF_SIZE];
>
> REQUIRE(zone != NULL);
> REQUIRE(dn != NULL);
> REQUIRE(zinfop != NULL && *zinfop == NULL);
>
> CHECKED_MEM_GET_PTR(mctx, zinfo);
> ZERO_PTR(zinfo);
> CHECKED_MEM_STRDUP(mctx, dn, zinfo->dn);
> - CHECK(new_ldap_cache(mctx, cache_ttl, psearch, &zinfo->cache));
> + CHECK(new_ldap_cache(mctx, global_settings, &zinfo->cache));
> dns_zone_attach(zone, &zinfo->zone);
> + zinfo->settings = NULL;
> + isc_string_printf_truncate(settings_name, PRINT_BUFF_SIZE,
> + SETTING_SET_NAME_ZONE " %s",
> + dn);
> + CHECK(settings_set_create(mctx, zone_settings, sizeof(zone_settings),
> + settings_name, global_settings,
> + &zinfo->settings));
>
> *zinfop = zinfo;
> return ISC_R_SUCCESS;
> @@ -168,6 +205,7 @@ delete_zone_info(void *arg1, void *arg2)
> return;
>
> destroy_ldap_cache(&zinfo->cache);
> + settings_set_free(&zinfo->settings);
> isc_mem_free(mctx, zinfo->dn);
> dns_zone_detach(&zinfo->zone);
> SAFE_MEM_PUT_PTR(mctx, zinfo);
> @@ -178,8 +216,7 @@ delete_zone_info(void *arg1, void *arg2)
> * must be absolute and the zone cannot already be in the zone register.
> */
> isc_result_t
> -zr_add_zone(zone_register_t *zr, dns_zone_t *zone, const char *dn,
> - const isc_interval_t *cache_ttl, const isc_boolean_t *psearch)
> +zr_add_zone(zone_register_t *zr, dns_zone_t *zone, const char *dn)
> {
> isc_result_t result;
> dns_name_t *name;
> @@ -210,7 +247,7 @@ zr_add_zone(zone_register_t *zr, dns_zone_t *zone, const char *dn,
> goto cleanup;
> }
>
> - CHECK(create_zone_info(zr->mctx, zone, dn, cache_ttl, psearch,
> + CHECK(create_zone_info(zr->mctx, zone, dn, zr->global_settings,
> &new_zinfo));
> CHECK(dns_rbt_addname(zr->rbt, name, new_zinfo));
>
> @@ -412,6 +449,37 @@ zr_get_zone_serial_digest(zone_register_t *zr, dns_name_t *name,
> return result;
> }
>
> +/*
> + * Find a zone with origin 'name' within in the zone register 'zr'. If an
> + * exact match is found, the pointer to the zone's settings is returned through
> + * 'set'.
> + */
> +isc_result_t
> +zr_get_zone_settings(zone_register_t *zr, dns_name_t *name, settings_set_t **set)
> +{
> + isc_result_t result;
> + void *zinfo = NULL;
> +
> + REQUIRE(zr != NULL);
> + REQUIRE(name != NULL);
> + REQUIRE(set != NULL && *set == NULL);
> +
> + if (!dns_name_isabsolute(name)) {
> + log_bug("trying to find zone with a relative name");
> + return ISC_R_FAILURE;
> + }
> +
> + RWLOCK(&zr->rwlock, isc_rwlocktype_read);
> +
> + result = dns_rbt_findname(zr->rbt, name, 0, NULL, &zinfo);
> + if (result == ISC_R_SUCCESS)
> + *set = ((zone_info_t *)zinfo)->settings;
> +
> + RWUNLOCK(&zr->rwlock, isc_rwlocktype_read);
> +
> + return result;
> +}
> +
> /**
> * Set last SOA serial and digest from RRs processed by autoincrement feature.
> */
> diff --git a/src/zone_register.h b/src/zone_register.h
> index cec7400ff893842d499d15f6897d448710ac5407..3f4114d2256c1e8af5f67c69f1829900c9c7b2e9 100644
> --- a/src/zone_register.h
> +++ b/src/zone_register.h
> @@ -22,18 +22,18 @@
> #define _LD_ZONE_REGISTER_H_
>
> #include "cache.h"
> +#include "settings.h"
>
> typedef struct zone_register zone_register_t;
>
> isc_result_t
> -zr_create(isc_mem_t *mctx, zone_register_t **zrp);
> +zr_create(isc_mem_t *mctx, settings_set_t *glob_settings, zone_register_t **zrp);
>
> void
> zr_destroy(zone_register_t **zrp);
>
> isc_result_t
> -zr_add_zone(zone_register_t *zr, dns_zone_t *zone, const char *dn,
> - const isc_interval_t *cache_ttl, const isc_boolean_t *psearch);
> +zr_add_zone(zone_register_t *zr, dns_zone_t *zone, const char *dn);
>
> isc_result_t
> zr_del_zone(zone_register_t *zr, dns_name_t *origin);
> @@ -51,6 +51,9 @@ zr_get_zone_dn(zone_register_t *zr, dns_name_t *name, const char **dn,
> isc_result_t
> zr_get_zone_ptr(zone_register_t *zr, dns_name_t *name, dns_zone_t **zonep);
>
> +isc_result_t
> +zr_get_zone_settings(zone_register_t *zr, dns_name_t *name, settings_set_t **set);
> +
> dns_rbt_t *
> zr_get_rbt(zone_register_t *zr);
>
> --
> 1.7.11.7
>
--
Adam Tkac, Red Hat, Inc.
More information about the Freeipa-devel
mailing list