[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