Hello,

this patchset implements support for MODRDN for ordinary records. As noted in
ticket https://fedorahosted.org/bind-dyndb-ldap/ticket/123, we agreed
yesterday that renaming zones is out of scope and seems unnecessarily complex.

This patch set depends on 'metadb' branch. It is also available from:
https://github.com/pspacek/bind-dyndb-ldap/tree/modrdn

Thank you for your time!

-- 
Petr^2 Spacek
From 6bcb7490416c8a1815e4a637be906bf3e8936e33 Mon Sep 17 00:00:00 2001
From: Petr Spacek <pspa...@redhat.com>
Date: Tue, 19 May 2015 14:38:40 +0200
Subject: [PATCH] Support LDAP MODRDN for ordinary DNS records.

Renaming of DNS zones and configuration objects is still not supported.

https://fedorahosted.org/bind-dyndb-ldap/ticket/123
---
 src/ldap_helper.c | 129 +++++++++++++++++++++++++++++++++---------------------
 1 file changed, 78 insertions(+), 51 deletions(-)

diff --git a/src/ldap_helper.c b/src/ldap_helper.c
index 8922141724c4351db9435fd73e91756d60606390..5ae774aba2c5e43c240fd9dfa639481ea7356778 100644
--- a/src/ldap_helper.c
+++ b/src/ldap_helper.c
@@ -3690,32 +3690,6 @@ update_zone(isc_task_t *task, isc_event_t *event)
 			CHECK(ldap_parse_fwd_zoneentry(entry, inst));
 	}
 
-		/* This code is disabled because we don't have UUID->DN database yet.
-		 if (SYNCREPL_MODDN(pevent->chgtype)) {
-			if (dn_to_dnsname(inst->mctx, pevent->prevdn, &prevname, NULL)
-					== ISC_R_SUCCESS) {
-				CHECK(ldap_delete_zone(inst, pevent->prevdn,
-				      ISC_TRUE, ISC_FALSE));
-			} else {
-				log_debug(5, "update_zone: old zone wasn't managed "
-					     "by plugin, dn '%s'", pevent->prevdn);
-			}
-
-			// fill the cache with records from renamed zone //
-			if (objclass & LDAP_ENTRYCLASS_MASTER) {
-				CHECK(ldap_query(inst, NULL, &ldap_qresult_record, pevent->dn,
-						LDAP_SCOPE_ONELEVEL, attrs_record, 0,
-						"(objectClass=idnsRecord)"));
-
-				for (entry_record = HEAD(ldap_qresult_record->ldap_entries);
-						entry_record != NULL;
-						entry_record = NEXT(entry_record, link)) {
-
-					syncrepl_update(inst, entry_record, NULL);
-				}
-			}
-		}
-		*/
 cleanup:
 	if (inst != NULL) {
 		sync_concurr_limit_signal(inst->sctx);
@@ -4047,11 +4021,23 @@ cleanup:
 	return result;
 }
 
+/**
+ * Create asynchronous ISC event to execute update_config()/zone()/record()
+ * in a task associated with affected DNS zone.
+ *
+ * @param[in,out] entryp  (Possibly fake) LDAP entry to parse.
+ * @param[in]     chgtype One of LDAP_SYNC_CAPI_ADD/MODIFY/DELETE.
+ *
+ * @pre entryp is valid LDAP entry with class, DNS names, DN, etc.
+ *
+ * @post entryp is NULL.
+ */
 static isc_result_t ATTR_NONNULLS ATTR_CHECKRESULT
-syncrepl_update(ldap_instance_t *inst, ldap_entry_t *entry, int chgtype)
+syncrepl_update(ldap_instance_t *inst, ldap_entry_t **entryp, int chgtype)
 {
 	isc_result_t result = ISC_R_SUCCESS;
 	ldap_syncreplevent_t *pevent = NULL;
+	ldap_entry_t *entry = NULL;
 	isc_event_t *wait_event = NULL;
 	dns_name_t *zone_name = NULL;
 	dns_zone_t *zone_ptr = NULL;
@@ -4062,18 +4048,19 @@ syncrepl_update(ldap_instance_t *inst, ldap_entry_t *entry, int chgtype)
 	isc_task_t *task = NULL;
 	sync_state_t sync_state;
 
+	REQUIRE(entryp != NULL);
+	entry = *entryp;
 	REQUIRE(entry->class != LDAP_ENTRYCLASS_NONE);
 
-	log_debug(20, "syncrepl change type: " /*"none%d,"*/ "add%d, del%d, mod%d", /* moddn%d", */
-		  /* !SYNCREPL_ANY(chgtype), */ SYNCREPL_ADD(chgtype),
-		  SYNCREPL_DEL(chgtype), SYNCREPL_MOD(chgtype)/*, SYNCREPL_MODDN(chgtype) */ );
+	log_debug(20, "syncrepl_update change type: add%d, del%d, mod%d",
+		  SYNCREPL_ADD(chgtype), SYNCREPL_DEL(chgtype),
+		  SYNCREPL_MOD(chgtype));
 
 	isc_mem_attach(inst->mctx, &mctx);
 
 	CHECKED_MEM_STRDUP(mctx, entry->dn, dn);
 	CHECKED_MEM_STRDUP(mctx, inst->db_name, dbname);
 
-
 	if (entry->class & LDAP_ENTRYCLASS_MASTER)
 		zone_name = &entry->fqdn;
 	else
@@ -4150,6 +4137,7 @@ syncrepl_update(ldap_instance_t *inst, ldap_entry_t *entry, int chgtype)
 	pevent->entry = entry;
 	wait_event = (isc_event_t *)pevent;
 	isc_task_send(task, (isc_event_t **)&pevent);
+	*entryp = NULL; /* event handler will deallocate the LDAP entry */
 
 	/* Lock syncrepl queue to prevent zone, config and resource records
 	 * from racing with each other. */
@@ -4172,7 +4160,7 @@ cleanup:
 			isc_mem_free(mctx, dn);
 		if (mctx != NULL)
 			isc_mem_detach(&mctx);
-		ldap_entry_destroy(inst->mctx, &entry);
+		ldap_entry_destroy(inst->mctx, entryp);
 		if (task != NULL)
 			isc_task_detach(&task);
 	}
@@ -4264,11 +4252,13 @@ int ldap_sync_search_entry (
 	ldap_sync_refresh_t		phase ) {
 
 	ldap_instance_t *inst = ls->ls_private;
-	ldap_entry_t *entry = NULL;
+	ldap_entry_t *old_entry = NULL;
+	ldap_entry_t *new_entry = NULL;
 	isc_result_t result;
 	metadb_node_t *node = NULL;
 	isc_boolean_t mldap_open = ISC_FALSE;
 	const char *ldap_base = NULL;
+	isc_boolean_t modrdn = ISC_FALSE;
 
 #ifdef RBTDB_DEBUG
 	static unsigned int count = 0;
@@ -4281,49 +4271,86 @@ int ldap_sync_search_entry (
 	mldap_open = ISC_TRUE;
 
 	CHECK(sync_concurr_limit_wait(inst->sctx));
-	if (phase == LDAP_SYNC_CAPI_ADD || phase == LDAP_SYNC_CAPI_MODIFY) {
-		CHECK(ldap_entry_parse(inst->mctx, ls->ls_ld, msg, entryUUID,
-					&entry));
-		CHECK(mldap_entry_create(entry, inst->mldapdb, &node));
-		if ((entry->class & LDAP_ENTRYCLASS_CONFIG) == 0)
-			CHECK(mldap_dnsname_store(&entry->fqdn,
-						  &entry->zone_name, node));
-		/* commit new entry into metaLDAP DB before something breaks */
-		metadb_node_close(&node);
-		mldap_closeversion(inst->mldapdb, ISC_TRUE);
-		mldap_open = ISC_FALSE;
+	log_debug(20, "ldap_sync_search_entry phase: %x", phase);
 
-	} else if (phase == LDAP_SYNC_CAPI_DELETE) {
+	/* MODIFY can be rename: get old name from metaDB */
+	if (phase == LDAP_SYNC_CAPI_DELETE || phase == LDAP_SYNC_CAPI_MODIFY) {
 		INSIST(setting_get_str("base", inst->local_settings,
 				       &ldap_base) == ISC_R_SUCCESS);
 		CHECK(ldap_entry_reconstruct(inst->mctx, inst->zone_register,
 					     ldap_base, inst->mldapdb, entryUUID,
-					     &entry));
+					     &old_entry));
+	}
+	if (phase == LDAP_SYNC_CAPI_ADD || phase == LDAP_SYNC_CAPI_MODIFY) {
+		CHECK(ldap_entry_parse(inst->mctx, ls->ls_ld, msg, entryUUID,
+				       &new_entry));
+	}
+	/* detect type of modification */
+	if (phase == LDAP_SYNC_CAPI_MODIFY) {
+		if (old_entry->class != new_entry->class)
+			log_error("unsupported operation: "
+				  "object class in object '%s' changed: "
+				  "rndc reload might be necessary",
+				  new_entry->dn);
+		if ((old_entry->class & LDAP_ENTRYCLASS_CONFIG) == 0)
+			modrdn = !(dns_name_equal(&old_entry->zone_name,
+						  &new_entry->zone_name)
+				   && dns_name_equal(&old_entry->fqdn,
+						     &new_entry->fqdn));
+		if (modrdn == ISC_TRUE) {
+			log_debug(1, "detected entry rename: DN '%s' -> '%s'",
+				  old_entry->dn, new_entry->dn);
+			if (old_entry->class != LDAP_ENTRYCLASS_RR)
+				log_bug("LDAP MODRDN is supported only for "
+					"records, not zones or configs; DN '%s' "
+					"rndc reload might be necessary",
+					new_entry->dn);
+		}
+	}
+	if (phase == LDAP_SYNC_CAPI_DELETE || modrdn == ISC_TRUE) {
+		/* delete old entry from zone and metaDB */
+		CHECK(syncrepl_update(inst, &old_entry, LDAP_SYNC_CAPI_DELETE));
 		CHECK(mldap_entry_delete(inst->mldapdb, entryUUID));
-		/* do not commit into DB until syncrepl_update finished */
-	} else {
+	}
+	if (phase == LDAP_SYNC_CAPI_ADD || phase == LDAP_SYNC_CAPI_MODIFY) {
+		/* store new state into metaDB */
+		CHECK(mldap_entry_create(new_entry, inst->mldapdb, &node));
+		if ((new_entry->class & LDAP_ENTRYCLASS_CONFIG) == 0)
+			CHECK(mldap_dnsname_store(&new_entry->fqdn,
+						  &new_entry->zone_name, node));
+		/* commit new entry into metaLDAP DB before something breaks */
+		metadb_node_close(&node);
+		mldap_closeversion(inst->mldapdb, ISC_TRUE);
+		mldap_open = ISC_FALSE;
+		/* re-add entry under new DN, if necessary */
+		CHECK(syncrepl_update(inst, &new_entry,
+		                      (modrdn == ISC_TRUE)
+					      ? LDAP_SYNC_CAPI_ADD : phase));
+	}
+	if (phase != LDAP_SYNC_CAPI_ADD && phase != LDAP_SYNC_CAPI_MODIFY &&
+	    phase != LDAP_SYNC_CAPI_DELETE) {
 		log_bug("syncrepl phase %x is not supported", phase);
 		CLEANUP_WITH(ISC_R_NOTIMPLEMENTED);
 	}
 
-	CHECK(syncrepl_update(inst, entry, phase));
-	/* commit eventual deletion if the syncrepl event was sent */
-
 #ifdef RBTDB_DEBUG
 	if (++count % 100 == 0)
 		log_info("ldap_sync_search_entry: %u entries read; inuse: %zd",
 			 count, isc_mem_inuse(inst->mctx));
 #endif
 
 cleanup:
 	metadb_node_close(&node);
 	if (mldap_open == ISC_TRUE)
+		/* commit metaDB changes if the syncrepl event was sent */
 		mldap_closeversion(inst->mldapdb, ISC_TF(result == ISC_R_SUCCESS));
 	if (result != ISC_R_SUCCESS) {
 		log_error_r("ldap_sync_search_entry failed");
 		sync_concurr_limit_signal(inst->sctx);
 		/* TODO: Add 'tainted' flag to the LDAP instance. */
 	}
+	ldap_entry_destroy(inst->mctx, &old_entry);
+	ldap_entry_destroy(inst->mctx, &new_entry);
 
 	/* Following return code will never reach upper layers.
 	 * It is limitation in ldap_sync_init() and ldap_sync_poll()
-- 
2.1.0

From 582c7ba40531027a273d1a0e47a44d09b75bf2fa Mon Sep 17 00:00:00 2001
From: Petr Spacek <pspa...@redhat.com>
Date: Tue, 19 May 2015 15:12:08 +0200
Subject: [PATCH] Add memory context to ldap_entry_t.

https://fedorahosted.org/bind-dyndb-ldap/ticket/123
---
 src/ldap_entry.c  | 20 +++++++++++---------
 src/ldap_entry.h  |  3 ++-
 src/ldap_helper.c | 14 +++++++-------
 3 files changed, 20 insertions(+), 17 deletions(-)

diff --git a/src/ldap_entry.c b/src/ldap_entry.c
index 437b31dcca974ee9341560a72b48d69d0ec9b8b9..1b903cb3e7c9ae837cf8b34c7cf2a817985a6ad5 100644
--- a/src/ldap_entry.c
+++ b/src/ldap_entry.c
@@ -129,6 +129,7 @@ ldap_entry_init(isc_mem_t *mctx, ldap_entry_t **entryp) {
 
 	CHECKED_MEM_GET_PTR(mctx, entry);
 	ZERO_PTR(entry);
+	isc_mem_attach(mctx, &entry->mctx);
 	INIT_LIST(entry->attrs);
 	INIT_LINK(entry, link);
 	INIT_BUFFERED_NAME(entry->fqdn);
@@ -141,7 +142,7 @@ ldap_entry_init(isc_mem_t *mctx, ldap_entry_t **entryp) {
 
 cleanup:
 	if (result != ISC_R_SUCCESS)
-		ldap_entry_destroy(mctx, &entry);
+		ldap_entry_destroy(&entry);
 
 	return result;
 }
@@ -203,7 +204,7 @@ ldap_entry_reconstruct(isc_mem_t *mctx, zone_register_t *zr,
 
 cleanup:
 	if (result != ISC_R_SUCCESS)
-		ldap_entry_destroy(mctx, &entry);
+		ldap_entry_destroy(&entry);
 	metadb_node_close(&node);
 	str_destroy(&str);
 	return result;
@@ -273,41 +274,42 @@ cleanup:
 		ber_free(ber, 0);
 	if (result != ISC_R_SUCCESS) {
 		if (entry != NULL)
-			ldap_entry_destroy(mctx, &entry);
+			ldap_entry_destroy(&entry);
 		SAFE_MEM_PUT_PTR(mctx, attr);
 	}
 
 	return result;
 }
 
 void
-ldap_entry_destroy(isc_mem_t *mctx, ldap_entry_t **entryp)
+ldap_entry_destroy(ldap_entry_t **entryp)
 {
 	ldap_entry_t *entry;
 
 	REQUIRE(entryp != NULL);
 
 	entry = *entryp;
 	if (entry == NULL)
 		return;
 
-	ldap_attributelist_destroy(mctx, &entry->attrs);
+	ldap_attributelist_destroy(entry->mctx, &entry->attrs);
 	if (entry->dn != NULL)
 		ldap_memfree(entry->dn);
 	if (entry->uuid != NULL)
 		ber_bvfree(entry->uuid);
 	if (dns_name_dynamic(&entry->fqdn))
-		dns_name_free(&entry->fqdn, mctx);
+		dns_name_free(&entry->fqdn, entry->mctx);
 	if (dns_name_dynamic(&entry->zone_name))
-		dns_name_free(&entry->zone_name, mctx);
+		dns_name_free(&entry->zone_name, entry->mctx);
 	if (entry->lex != NULL) {
 		isc_lex_close(entry->lex);
 		isc_lex_destroy(&entry->lex);
 	}
 	if (entry->rdata_target_mem != NULL)
-		SAFE_MEM_PUT(mctx, entry->rdata_target_mem, DNS_RDATA_MAXLENGTH);
+		SAFE_MEM_PUT(entry->mctx, entry->rdata_target_mem,
+			     DNS_RDATA_MAXLENGTH);
 
-	SAFE_MEM_PUT_PTR(mctx, entry);
+	MEM_PUT_AND_DETACH(entry);
 
 	*entryp = NULL;
 }
diff --git a/src/ldap_entry.h b/src/ldap_entry.h
index 2ccdcc42a5b5196791f0842c35f2b6a60155e3a7..abc2c0bf25f18d3576d43a3284ae815aa27ad1be 100644
--- a/src/ldap_entry.h
+++ b/src/ldap_entry.h
@@ -50,6 +50,7 @@ typedef LIST(ldap_attribute_t)	ldap_attributelist_t;
 typedef unsigned char		ldap_entryclass_t;
 typedef LIST(ldap_entry_t)	ldap_entrylist_t;
 struct ldap_entry {
+	isc_mem_t		*mctx;
 	char			*dn;
 	struct berval		*uuid;
 	ldap_entryclass_t	class;
@@ -115,7 +116,7 @@ ldap_entry_reconstruct(isc_mem_t *mctx, zone_register_t *zr, const char *ldap_ba
 		       ldap_entry_t **entryp) ATTR_NONNULLS ATTR_CHECKRESULT;
 
 void
-ldap_entry_destroy(isc_mem_t *mctx, ldap_entry_t **entryp) ATTR_NONNULLS;
+ldap_entry_destroy(ldap_entry_t **entryp) ATTR_NONNULLS;
 
 isc_result_t
 ldap_entry_getvalues(const ldap_entry_t *entry, const char *attrname,
diff --git a/src/ldap_helper.c b/src/ldap_helper.c
index 5ae774aba2c5e43c240fd9dfa639481ea7356778..ff1249550bffd777051f8a61f06814b11f020423 100644
--- a/src/ldap_helper.c
+++ b/src/ldap_helper.c
@@ -1981,7 +1981,7 @@ cleanup:
 		SAFE_MEM_PUT_PTR(mctx, nsec3p_rdata);
 	}
 	if (fake_entry != NULL)
-		ldap_entry_destroy(mctx, &fake_entry);
+		ldap_entry_destroy(&fake_entry);
 	return result;
 }
 
@@ -3706,7 +3706,7 @@ cleanup:
 	if (pevent->prevdn != NULL)
 		isc_mem_free(mctx, pevent->prevdn);
 	isc_mem_free(mctx, pevent->dn);
-	ldap_entry_destroy(mctx, &entry);
+	ldap_entry_destroy(&entry);
 	isc_mem_detach(&mctx);
 	isc_event_free(&event);
 	isc_task_detach(&task);
@@ -3737,7 +3737,7 @@ cleanup:
 			    "Configuration can be outdated, run `rndc reload`",
 			    pevent->dn);
 
-	ldap_entry_destroy(mctx, &entry);
+	ldap_entry_destroy(&entry);
 	isc_mem_free(mctx, pevent->dbname);
 	isc_mem_free(mctx, pevent->dn);
 	isc_mem_detach(&mctx);
@@ -3972,7 +3972,7 @@ cleanup:
 	isc_mem_free(mctx, pevent->dbname);
 	if (pevent->prevdn != NULL)
 		isc_mem_free(mctx, pevent->prevdn);
-	ldap_entry_destroy(mctx, &entry);
+	ldap_entry_destroy(&entry);
 	isc_mem_free(mctx, pevent->dn);
 	isc_mem_detach(&mctx);
 	isc_event_free(&event);
@@ -4160,7 +4160,7 @@ cleanup:
 			isc_mem_free(mctx, dn);
 		if (mctx != NULL)
 			isc_mem_detach(&mctx);
-		ldap_entry_destroy(inst->mctx, entryp);
+		ldap_entry_destroy(entryp);
 		if (task != NULL)
 			isc_task_detach(&task);
 	}
@@ -4349,8 +4349,8 @@ cleanup:
 		sync_concurr_limit_signal(inst->sctx);
 		/* TODO: Add 'tainted' flag to the LDAP instance. */
 	}
-	ldap_entry_destroy(inst->mctx, &old_entry);
-	ldap_entry_destroy(inst->mctx, &new_entry);
+	ldap_entry_destroy(&old_entry);
+	ldap_entry_destroy(&new_entry);
 
 	/* Following return code will never reach upper layers.
 	 * It is limitation in ldap_sync_init() and ldap_sync_poll()
-- 
2.1.0

From 6581c73db9830ec4c419806addf2ce5a0a07c6e5 Mon Sep 17 00:00:00 2001
From: Petr Spacek <pspa...@redhat.com>
Date: Tue, 19 May 2015 15:16:29 +0200
Subject: [PATCH] Remove dangling declarations for ldap_entrylist_*.

---
 src/ldap_entry.h | 12 ------------
 1 file changed, 12 deletions(-)

diff --git a/src/ldap_entry.h b/src/ldap_entry.h
index abc2c0bf25f18d3576d43a3284ae815aa27ad1be..3e88c42daeb70aae25fc436669d7d7d267eff688 100644
--- a/src/ldap_entry.h
+++ b/src/ldap_entry.h
@@ -48,7 +48,6 @@ typedef LIST(ldap_attribute_t)	ldap_attributelist_t;
 
 /* Represents LDAP entry and it's attributes */
 typedef unsigned char		ldap_entryclass_t;
-typedef LIST(ldap_entry_t)	ldap_entrylist_t;
 struct ldap_entry {
 	isc_mem_t		*mctx;
 	char			*dn;
@@ -90,17 +89,6 @@ struct ldap_attribute {
 isc_result_t ATTR_NONNULLS ATTR_CHECKRESULT
 ldap_entry_init(isc_mem_t *mctx, ldap_entry_t **entryp);
 
-isc_result_t
-ldap_entrylist_create(isc_mem_t *mctx, LDAP *ld, LDAPMessage *msg,
-		      ldap_entrylist_t *entrylist) ATTR_NONNULLS ATTR_CHECKRESULT;
-
-void
-ldap_entrylist_destroy(isc_mem_t *mctx, ldap_entrylist_t *entrylist) ATTR_NONNULLS;
-
-isc_result_t
-ldap_entrylist_append(isc_mem_t *mctx, LDAP *ld, LDAPMessage *msg,
-		      ldap_entrylist_t *entrylist) ATTR_NONNULLS ATTR_CHECKRESULT;
-
 /*
  * ldap_entry_create
  *
-- 
2.1.0

From 1d5260101cfd6c731fb2758692ca33619e11ef00 Mon Sep 17 00:00:00 2001
From: Petr Spacek <pspa...@redhat.com>
Date: Wed, 20 May 2015 08:51:06 +0200
Subject: [PATCH] Remove dependency on entry->dn to avoid problems with
 reconstructed entries.

Reconstructed entries might have inaccurate DNs and sometimes DN
reconstruction is not possible (e.g. for forward zones) because we do
not have enough information.

Most use cases for entry->dn is in logging so all these cases were
replaced by new function ldap_entry_logname() which constructs best
available entry identifier.

The only remaining use case outside ldap_entry module is creating new
master zone: We need to store zone's DN in zone register for later use
in dynamic updates.

https://fedorahosted.org/bind-dyndb-ldap/ticket/123
---
 src/ldap_entry.c  | 104 ++++++++++++++++++++---------
 src/ldap_entry.h  |  12 +++-
 src/ldap_helper.c | 194 +++++++++++++++++++++++++++++-------------------------
 src/ldap_helper.h |   2 +
 src/settings.c    |  11 ++--
 5 files changed, 195 insertions(+), 128 deletions(-)

diff --git a/src/ldap_entry.c b/src/ldap_entry.c
index 1b903cb3e7c9ae837cf8b34c7cf2a817985a6ad5..b35b43e35189433b54051e961ad30fdb8debdcbe 100644
--- a/src/ldap_entry.c
+++ b/src/ldap_entry.c
@@ -18,6 +18,7 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  */
+#include <uuid/uuid.h>
 
 #include <dns/rdata.h>
 #include <dns/ttl.h>
@@ -158,9 +159,8 @@ cleanup:
  * @warning fake entry->dn might be inaccurate
  */
 isc_result_t
-ldap_entry_reconstruct(isc_mem_t *mctx, zone_register_t *zr,
-		       const char *ldap_base, mldapdb_t *mldap,
-		       struct berval *uuid, ldap_entry_t **entryp) {
+ldap_entry_reconstruct(isc_mem_t *mctx, mldapdb_t *mldap, struct berval *uuid,
+		       ldap_entry_t **entryp) {
 	isc_result_t result;
 	ldap_entry_t *entry = NULL;
 	ld_string_t *str = NULL;
@@ -180,25 +180,8 @@ ldap_entry_reconstruct(isc_mem_t *mctx, zone_register_t *zr,
 		CLEANUP_WITH(ISC_R_NOMEMORY);
 
 	CHECK(mldap_class_get(node, &entry->class));
-	/* create fake DN from remembered DNS names and object class */
-	if ((entry->class & LDAP_ENTRYCLASS_CONFIG) != 0) {
-		/* idnsConfig objects do not have DNS name */
-		CHECK(str_cat_char(str, ldap_base));
-	} else {
+	if ((entry->class & LDAP_ENTRYCLASS_CONFIG) == 0)
 		CHECK(mldap_dnsname_get(node, &entry->fqdn, &entry->zone_name));
-		if ((entry->class &
-		     (LDAP_ENTRYCLASS_MASTER | LDAP_ENTRYCLASS_FORWARD)) != 0) {
-			INSIST(dns_name_equal(dns_rootname, &entry->zone_name)
-			       == ISC_TRUE);
-			CHECK(dnsname_to_dn(zr, &entry->fqdn, &entry->fqdn, str));
-		} else if ((entry->class & LDAP_ENTRYCLASS_RR) != 0) {
-			CHECK(dnsname_to_dn(zr, &entry->fqdn, &entry->zone_name,
-					    str));
-		}
-	}
-	entry->dn = ldap_strdup(str_buf(str));
-	if (entry->dn == NULL)
-		CLEANUP_WITH(ISC_R_NOMEMORY);
 
 	*entryp = entry;
 
@@ -308,6 +291,7 @@ ldap_entry_destroy(ldap_entry_t **entryp)
 	if (entry->rdata_target_mem != NULL)
 		SAFE_MEM_PUT(entry->mctx, entry->rdata_target_mem,
 			     DNS_RDATA_MAXLENGTH);
+	str_destroy(&entry->logname);
 
 	MEM_PUT_AND_DETACH(entry);
 
@@ -468,8 +452,8 @@ ldap_entry_parseclass(ldap_entry_t *entry, ldap_entryclass_t *class)
 	 * objectClass attribute. */
 	if (ldap_entry_getvalues(entry, "objectClass", &values)
 	    != ISC_R_SUCCESS) {
-		log_error("entry without supported objectClass: DN '%s'",
-			  (entry->dn != NULL) ? entry->dn : "<NULL>");
+		log_error("entry without supported objectClass: %s",
+			  ldap_entry_logname(entry));
 		return ISC_R_UNEXPECTED;
 	}
 
@@ -485,14 +469,14 @@ ldap_entry_parseclass(ldap_entry_t *entry, ldap_entryclass_t *class)
 	}
 
 	if (class == LDAP_ENTRYCLASS_NONE) {
-		log_error("entry '%s' has no supported object class",
-			  entry->dn);
+		log_error("%s has no supported object class",
+			  ldap_entry_logname(entry));
 		return ISC_R_NOTIMPLEMENTED;
 
 	} else if ((entryclass & LDAP_ENTRYCLASS_MASTER) &&
 		   (entryclass & LDAP_ENTRYCLASS_FORWARD)) {
-		log_error("zone '%s' has to have type either "
-			  "'master' or 'forward'", entry->dn);
+		log_error("%s has to have type either "
+			  "'master' or 'forward'", ldap_entry_logname(entry));
 		return ISC_R_UNEXPECTED;
 	}
 
@@ -538,7 +522,7 @@ cleanup:
 }
 
 dns_ttl_t
-ldap_entry_getttl(const ldap_entry_t *entry)
+ldap_entry_getttl(ldap_entry_t *entry)
 {
 	const char *ttl_attr = "dnsTTL";
 	isc_textregion_t ttl_text;
@@ -558,10 +542,70 @@ ldap_entry_getttl(const ldap_entry_t *entry)
 	if (result != ISC_R_SUCCESS)
 		return DEFAULT_TTL;
 	else if (ttl > 0x7fffffffUL) {
-		log_error("entry '%s': entry TTL %u > MAXTTL, setting TTL to 0",
-			  entry->dn, ttl);
+		log_error("%s: entry TTL %u > MAXTTL, setting TTL to 0",
+			  ldap_entry_logname(entry), ttl);
 		ttl = 0;
 	}
 
 	return ttl;
 }
+
+/**
+ * Convert a combination of LDAP_ENTRYCLASS_* to a string.
+ */
+const char *
+ldap_entry_getclassname(const ldap_entryclass_t class) {
+	if ((class & LDAP_ENTRYCLASS_MASTER) != 0)
+		return "master zone";
+	else if ((class & LDAP_ENTRYCLASS_FORWARD) != 0)
+		return "forward zone";
+	else if ((class & LDAP_ENTRYCLASS_CONFIG) != 0)
+		return "config object";
+	else if ((class & LDAP_ENTRYCLASS_RR) != 0)
+		return "resource record";
+	else if (class != 0)
+		return "entry with unknown combination of object classes";
+	else
+		return "entry with empty object class";
+}
+
+/**
+ * Return human-readable entry identifier.
+ * The identifier is guaranteed to be non-NULL so it can be used without
+ * further checking.
+ */
+const char *
+ldap_entry_logname(ldap_entry_t * const entry) {
+	isc_result_t result;
+	ld_string_t *str = NULL;
+	char uuid_buf[sizeof("01234567-89ab-cdef-0123-456789abcdef")];
+
+	if (entry->logname != NULL)
+		return str_buf(entry->logname);
+
+	CHECK(str_new(entry->mctx, &str));
+	CHECK(str_cat_char(str, ldap_entry_getclassname(entry->class)));
+	if (entry->dn) {
+		if (str_len(str) > 0)
+			CHECK(str_cat_char(str, " "));
+		CHECK(str_cat_char(str, "DN '"));
+		CHECK(str_cat_char(str, entry->dn));
+		CHECK(str_cat_char(str, "'"));
+	} else if (entry->uuid) {
+		INSIST(entry->uuid->bv_len == 16);
+		uuid_unparse((*(const uuid_t *) entry->uuid->bv_val), uuid_buf);
+		if (str_len(str) > 0)
+			CHECK(str_cat_char(str, " "));
+		CHECK(str_cat_char(str, "entryUUID "));
+		CHECK(str_cat_char(str, uuid_buf));
+	}
+	/* sanity check */
+	if (str == NULL || str_len(str) <= 0)
+		goto cleanup;
+	entry->logname = str;
+	return str_buf(entry->logname);
+
+cleanup:
+	str_destroy(&str);
+	return "<failed to obtain LDAP entry identifier>";
+}
diff --git a/src/ldap_entry.h b/src/ldap_entry.h
index 3e88c42daeb70aae25fc436669d7d7d267eff688..fed20217c7a54c3f09dfa9b0e05c5d9fbd13bd96 100644
--- a/src/ldap_entry.h
+++ b/src/ldap_entry.h
@@ -64,6 +64,10 @@ struct ldap_entry {
 	isc_lex_t		*lex;
 	isc_buffer_t		rdata_target;
 	unsigned char		*rdata_target_mem;
+
+	/* Human-readable identifier. It has to be accessed via
+	 * ldap_entry_logname(). */
+	ld_string_t		*logname;
 };
 
 /* Represents LDAP attribute and it's values */
@@ -99,8 +103,7 @@ ldap_entry_parse(isc_mem_t *mctx, LDAP *ld, LDAPMessage *ldap_entry,
 		  struct berval	*uuid, ldap_entry_t **entryp) ATTR_NONNULLS ATTR_CHECKRESULT;
 
 isc_result_t
-ldap_entry_reconstruct(isc_mem_t *mctx, zone_register_t *zr, const char *ldap_base,
-		       mldapdb_t *mldap, struct berval *uuid,
+ldap_entry_reconstruct(isc_mem_t *mctx, mldapdb_t *mldap, struct berval *uuid,
 		       ldap_entry_t **entryp) ATTR_NONNULLS ATTR_CHECKRESULT;
 
 void
@@ -141,6 +144,9 @@ isc_result_t
 ldap_attr_nextvalue(ldap_attribute_t *attr, ld_string_t *value) ATTR_NONNULLS ATTR_CHECKRESULT;
 
 dns_ttl_t
-ldap_entry_getttl(const ldap_entry_t *entry) ATTR_NONNULLS ATTR_CHECKRESULT;
+ldap_entry_getttl(ldap_entry_t *entry) ATTR_NONNULLS ATTR_CHECKRESULT;
+
+const char *
+ldap_entry_logname(ldap_entry_t * const entry) ATTR_NONNULLS ATTR_CHECKRESULT;
 
 #endif /* !_LD_LDAP_ENTRY_H_ */
diff --git a/src/ldap_helper.c b/src/ldap_helper.c
index ff1249550bffd777051f8a61f06814b11f020423..bb581fcbf9e2284ec70a164c47f4f88cbe68ac8c 100644
--- a/src/ldap_helper.c
+++ b/src/ldap_helper.c
@@ -42,6 +42,7 @@
 #include <dns/soa.h>
 #include <dns/update.h>
 
+#include <isc/atomic.h>
 #include <isc/buffer.h>
 #include <isc/dir.h>
 #include <isc/mem.h>
@@ -177,6 +178,10 @@ struct ldap_instance {
 	isc_task_t		*task;
 	isc_thread_t		watcher;
 	isc_boolean_t		exiting;
+	/* Non-zero if this instance 'tainted' by a unrecoverable problem.
+	 * It should be accessed using isc_atomic_*() because it might be
+	 * modified from multiple threads. */
+	isc_int32_t		tainted;
 
 	/* Settings. */
 	settings_set_t		*local_settings;
@@ -229,7 +234,6 @@ struct ldap_syncreplevent {
 	ISC_EVENT_COMMON(ldap_syncreplevent_t);
 	isc_mem_t *mctx;
 	char *dbname;
-	char *dn;
 	char *prevdn;
 	int chgtype;
 	ldap_entry_t *entry;
@@ -1309,13 +1313,13 @@ configure_zone_ssutable(dns_zone_t *zone, const char *update_str)
 
 static isc_result_t ATTR_NONNULLS ATTR_CHECKRESULT
 delete_forwarding_table(ldap_instance_t *inst, dns_name_t *name,
-			const char *msg_obj_type, const char *dn) {
+			const char *msg_obj_type, const char *logname) {
 	isc_result_t result;
 
 	result = dns_fwdtable_delete(inst->view->fwdtable, name);
 	if (result != ISC_R_SUCCESS && result != ISC_R_NOTFOUND) {
-		log_error_r("%s '%s': failed to delete forwarders",
-			    msg_obj_type, dn);
+		log_error_r("%s %s: failed to delete forwarders",
+			    msg_obj_type, logname);
 		return result;
 	} else {
 		return ISC_R_SUCCESS; /* ISC_R_NOTFOUND = nothing to delete */
@@ -1398,7 +1402,7 @@ cleanup:
  * I hope it will not break something...
  */
 static isc_result_t ATTR_NONNULLS ATTR_CHECKRESULT
-unpublish_zone(ldap_instance_t *inst, dns_name_t *name, const char *dn) {
+unpublish_zone(ldap_instance_t *inst, dns_name_t *name, const char *logname) {
 	isc_result_t result;
 	isc_result_t lock_state = ISC_R_IGNORE;
 	dns_zone_t *raw = NULL;
@@ -1415,15 +1419,15 @@ unpublish_zone(ldap_instance_t *inst, dns_name_t *name, const char *dn) {
 	}
 	CHECK(dns_view_findzone(inst->view, name, &zone_in_view));
 	INSIST(zone_in_view == raw || zone_in_view == secure);
-	CHECK(delete_forwarding_table(inst, name, "zone", dn));
+	CHECK(delete_forwarding_table(inst, name, "zone", logname));
 	CHECK(dns_zt_unmount(inst->view->zonetable, zone_in_view));
 
 cleanup:
 	if (freeze)
 		dns_view_freeze(inst->view);
 	run_exclusive_exit(inst, lock_state);
 	if (result != ISC_R_SUCCESS)
-		log_error_r("zone '%s' un-publication failed", dn);
+		log_error_r("%s un-publication failed", logname);
 	if (raw != NULL)
 		dns_zone_detach(&raw);
 	if (secure != NULL)
@@ -1458,7 +1462,6 @@ static isc_result_t ATTR_NONNULLS ATTR_CHECKRESULT
 configure_zone_forwarders(ldap_entry_t *entry, ldap_instance_t *inst,
 			  dns_name_t *name)
 {
-	const char *dn = entry->dn;
 	isc_result_t result;
 	isc_result_t orig_result;
 	isc_result_t lock_state = ISC_R_IGNORE;
@@ -1517,12 +1520,13 @@ configure_zone_forwarders(ldap_entry_t *entry, ldap_instance_t *inst,
 			else if (strcasecmp(value->value, "none") == 0)
 				fwdpolicy = dns_fwdpolicy_none;
 			else {
-				log_error("%s '%s': invalid value '%s' in "
+				log_error("%s %s: invalid value '%s' in "
 					  "idnsForwardPolicy attribute; "
 					  "valid values: first, only, none"
 					  "%s",
-					  msg_obj_type, dn, value->value,
-					  msg_use_global_fwds);
+					  msg_obj_type,
+					  ldap_entry_logname(entry),
+					  value->value, msg_use_global_fwds);
 				CLEANUP_WITH(ISC_R_UNEXPECTEDTOKEN);
 			}
 		}
@@ -1533,8 +1537,9 @@ configure_zone_forwarders(ldap_entry_t *entry, ldap_instance_t *inst,
 	} else {
 		result = ldap_entry_getvalues(entry, "idnsForwarders", &values);
 		if (result == ISC_R_NOTFOUND || EMPTY(values)) {
-			log_debug(5, "%s '%s': idnsForwarders attribute is "
-				  "not present%s", msg_obj_type, dn,
+			log_debug(5, "%s %s: idnsForwarders attribute is "
+				  "not present%s", msg_obj_type,
+				  ldap_entry_logname(entry),
 				  msg_forwarders_not_def);
 			if (is_global_config) {
 				ISC_LIST_INIT(values);
@@ -1548,8 +1553,8 @@ configure_zone_forwarders(ldap_entry_t *entry, ldap_instance_t *inst,
 
 	CHECK(get_enum_description(forwarder_policy_txts, fwdpolicy,
 				   &msg_forward_policy));
-	log_debug(5, "%s '%s': forward policy is '%s'", msg_obj_type, dn,
-		  msg_forward_policy);
+	log_debug(5, "%s %s: forward policy is '%s'", msg_obj_type,
+		  ldap_entry_logname(entry), msg_forward_policy);
 
 	for (value = HEAD(values); value != NULL; value = NEXT(value, link)) {
 #if LIBDNS_VERSION_MAJOR < 140
@@ -1561,8 +1566,9 @@ configure_zone_forwarders(ldap_entry_t *entry, ldap_instance_t *inst,
 
 		if (acl_parse_forwarder(value->value, inst->mctx, &fwdr)
 				!= ISC_R_SUCCESS) {
-			log_error("%s '%s': could not parse forwarder '%s'",
-					msg_obj_type, dn, value->value);
+			log_error("%s %s: could not parse forwarder '%s'",
+				  msg_obj_type, ldap_entry_logname(entry),
+				  value->value);
 			continue;
 		}
 
@@ -1575,18 +1581,19 @@ configure_zone_forwarders(ldap_entry_t *entry, ldap_instance_t *inst,
 				&fwdr->addr,
 #endif
 				forwarder_txt, ISC_SOCKADDR_FORMATSIZE);
-		log_debug(5, "%s '%s': adding forwarder '%s'", msg_obj_type,
-			  dn, forwarder_txt);
+		log_debug(5, "%s %s: adding forwarder '%s'", msg_obj_type,
+			  ldap_entry_logname(entry), forwarder_txt);
 	}
 
 	if (fwdpolicy != dns_fwdpolicy_none && ISC_LIST_EMPTY(fwdrs)) {
-		log_debug(5, "%s '%s': all idnsForwarders are invalid%s",
-			  msg_obj_type, dn, msg_use_global_fwds);
+		log_debug(5, "%s %s: all idnsForwarders are invalid%s",
+			  msg_obj_type, ldap_entry_logname(entry),
+			  msg_use_global_fwds);
 		CLEANUP_WITH(ISC_R_UNEXPECTEDTOKEN);
 	} else if (fwdpolicy == dns_fwdpolicy_none) {
-		log_debug(5, "%s '%s': forwarding explicitly disabled "
+		log_debug(5, "%s %s: forwarding explicitly disabled "
 			  "(policy 'none', ignoring global forwarders)",
-			  msg_obj_type, dn);
+			  msg_obj_type, ldap_entry_logname(entry));
 	}
 
 	/* Check for old and new forwarding settings equality. */
@@ -1626,8 +1633,9 @@ configure_zone_forwarders(ldap_entry_t *entry, ldap_instance_t *inst,
 	} else {
 		fwdtbl_update_requested = ISC_TRUE;
 		if (result != ISC_R_SUCCESS && result != ISC_R_NOTFOUND)
-			log_error_r("%s '%s': can't obtain old forwarding "
-				    "settings", msg_obj_type, dn);
+			log_error_r("%s %s: can't obtain old forwarding "
+				    "settings", msg_obj_type,
+				    ldap_entry_logname(entry));
 	}
 
 	if (fwdtbl_update_requested) {
@@ -1650,21 +1658,22 @@ configure_zone_forwarders(ldap_entry_t *entry, ldap_instance_t *inst,
 			goto cleanup;
 
 		/* Something was changed - set forward table up. */
-		CHECK(delete_forwarding_table(inst, name, msg_obj_type, dn));
+		CHECK(delete_forwarding_table(inst, name, msg_obj_type,
+					      ldap_entry_logname(entry)));
 #if LIBDNS_VERSION_MAJOR < 140
 		result = dns_fwdtable_add(inst->view->fwdtable, name, &fwdrs,
 					  fwdpolicy);
 #else /* LIBDNS_VERSION_MAJOR >= 140 */
 		result = dns_fwdtable_addfwd(inst->view->fwdtable, name, &fwdrs,
 					     fwdpolicy);
 #endif
 		if (result != ISC_R_SUCCESS)
-			log_error_r("%s '%s': forwarding table update failed",
-				    msg_obj_type, dn);
+			log_error_r("%s %s: forwarding table update failed",
+				    msg_obj_type, ldap_entry_logname(entry));
 	} else {
 		result = ISC_R_SUCCESS;
-		log_debug(5, "%s '%s': forwarding table unmodified",
-			  msg_obj_type, dn);
+		log_debug(5, "%s %s: forwarding table unmodified",
+			  msg_obj_type, ldap_entry_logname(entry));
 	}
 	if (result == ISC_R_SUCCESS) {
 		fwdtbl_deletion_requested = ISC_FALSE;
@@ -1688,13 +1697,15 @@ cleanup:
 	}
 	if (fwdtbl_deletion_requested) {
 		orig_result = result;
-		result = delete_forwarding_table(inst, name, msg_obj_type, dn);
+		result = delete_forwarding_table(inst, name, msg_obj_type,
+						 ldap_entry_logname(entry));
 		if (result == ISC_R_SUCCESS)
 			result = orig_result;
 	}
 	if (fwdtbl_deletion_requested || fwdtbl_update_requested) {
-		log_debug(5, "%s '%s': forwarder table was updated: %s",
-			  msg_obj_type, dn, dns_result_totext(result));
+		log_debug(5, "%s %s: forwarder table was updated: %s",
+			  msg_obj_type, ldap_entry_logname(entry),
+			  dns_result_totext(result));
 		orig_result = result;
 		run_exclusive_enter(inst, &lock_state);
 		result = dns_view_flushcache(inst->view);
@@ -1746,17 +1757,13 @@ cleanup:
 static isc_result_t ATTR_NONNULLS ATTR_CHECKRESULT
 ldap_parse_fwd_zoneentry(ldap_entry_t *entry, ldap_instance_t *inst)
 {
-	const char *dn;
 	ldap_valuelist_t values;
 	char name_txt[DNS_NAME_FORMATSIZE];
 	isc_result_t result;
 
 	REQUIRE(entry != NULL);
 	REQUIRE(inst != NULL);
 
-	/* Derive the DNS name of the zone from the DN. */
-	dn = entry->dn;
-
 	CHECK(ldap_entry_getvalues(entry, "idnsZoneActive", &values));
 	if (HEAD(values) != NULL &&
 	    strcasecmp(HEAD(values)->value, "TRUE") != 0) {
@@ -1769,7 +1776,8 @@ ldap_parse_fwd_zoneentry(ldap_entry_t *entry, ldap_instance_t *inst)
 	/* Zone is active */
 	result = configure_zone_forwarders(entry, inst, &entry->fqdn);
 	if (result != ISC_R_DISABLED && result != ISC_R_SUCCESS) {
-		log_error_r("forward zone '%s': could not configure forwarding", dn);
+		log_error_r("%s: could not configure forwarding",
+			    ldap_entry_logname(entry));
 		goto cleanup;
 	}
 
@@ -2282,7 +2290,6 @@ ldap_parse_master_zoneentry(ldap_entry_t * const entry, dns_db_t * const olddb,
 			    ldap_instance_t * const inst,
 			    isc_task_t * const task)
 {
-	const char *dn;
 	ldap_valuelist_t values;
 	dns_zone_t *raw = NULL;
 	dns_zone_t *secure = NULL;
@@ -2311,9 +2318,6 @@ ldap_parse_master_zoneentry(ldap_entry_t * const entry, dns_db_t * const olddb,
 
 	dns_diff_init(inst->mctx, &diff);
 
-	/* Derive the dns name of the zone from the DN. */
-	dn = entry->dn;
-
 	run_exclusive_enter(inst, &lock_state);
 
 	result = configure_zone_forwarders(entry, inst, &entry->fqdn);
@@ -2331,11 +2335,11 @@ ldap_parse_master_zoneentry(ldap_entry_t * const entry, dns_db_t * const olddb,
 	result = zr_get_zone_ptr(inst->zone_register, &entry->fqdn,
 				 &raw, &secure);
 	if (result == ISC_R_NOTFOUND || result == DNS_R_PARTIALMATCH) {
-		CHECK(create_zone(inst, dn, &entry->fqdn, olddb, want_secure,
-				  &raw, &secure));
+		CHECK(create_zone(inst, entry->dn, &entry->fqdn, olddb,
+				  want_secure, &raw, &secure));
 		new_zone = ISC_TRUE;
-		log_debug(2, "created zone %s: raw %p; secure %p", dn, raw,
-			  secure);
+		log_debug(2, "created %s: raw %p; secure %p",
+			  ldap_entry_logname(entry), raw, secure);
 	} else if (result != ISC_R_SUCCESS)
 		goto cleanup;
 	else if (want_secure != ISC_TF(secure != NULL)) {
@@ -2418,7 +2422,8 @@ ldap_parse_master_zoneentry(ldap_entry_t * const entry, dns_db_t * const olddb,
 			CHECK(publish_zone(task, inst, toview));
 		CHECK(load_zone(toview, ISC_FALSE));
 	} else if (activity_changed == ISC_TRUE) { /* Zone was deactivated */
-		CHECK(unpublish_zone(inst, &entry->fqdn, entry->dn));
+		CHECK(unpublish_zone(inst, &entry->fqdn,
+				     ldap_entry_logname(entry)));
 		dns_zone_log(toview, ISC_LOG_INFO, "zone deactivated "
 			     "and removed from view");
 	}
@@ -2433,13 +2438,14 @@ cleanup:
 		dns_db_detach(&ldapdb);
 	if (new_zone == ISC_TRUE && configured == ISC_FALSE) {
 		/* Failure in ACL parsing or so. */
-		log_error_r("zone '%s': publishing failed, rolling back due to",
-			    entry->dn);
+		log_error_r("%s: publishing failed, rolling back due to",
+			    ldap_entry_logname(entry));
 		/* TODO: verify this */
 		result = ldap_delete_zone2(inst, &entry->fqdn,
 					   ISC_TRUE, ISC_FALSE);
 		if (result != ISC_R_SUCCESS)
-			log_error_r("zone '%s': rollback failed: ", entry->dn);
+			log_error_r("%s: rollback failed: ",
+				    ldap_entry_logname(entry));
 	}
 	run_exclusive_exit(inst, lock_state);
 	if (raw != NULL)
@@ -2560,7 +2566,6 @@ ldap_parse_rrentry(isc_mem_t *mctx, ldap_entry_t *entry, dns_name_t *origin,
 	dns_rdata_t *rdata = NULL;
 	dns_rdatalist_t *rdlist = NULL;
 	ldap_attribute_t *attr;
-	const char *dn = "<NULL entry>";
 	const char *data_str = "<NULL data>";
 	ld_string_t *data_buf = NULL;
 
@@ -2599,11 +2604,10 @@ ldap_parse_rrentry(isc_mem_t *mctx, ldap_entry_t *entry, dns_name_t *origin,
 	return ISC_R_SUCCESS;
 
 cleanup:
-	if (entry != NULL)
-		dn = entry->dn;
 	if (data_buf != NULL && str_len(data_buf) != 0)
 		data_str = str_buf(data_buf);
-	log_error_r("failed to parse RR entry: dn '%s': data '%s'", dn, data_str);
+	log_error_r("failed to parse RR entry: %s: data '%s'",
+		    ldap_entry_logname(entry), data_str);
 	str_destroy(&data_buf);
 	return result;
 }
@@ -3698,14 +3702,13 @@ cleanup:
 			dns_name_free(&prevname, inst->mctx);
 	}
 	if (result != ISC_R_SUCCESS)
-		log_error_r("update_zone (syncrepl) failed for '%s'. "
-			  "Zones can be outdated, run `rndc reload`",
-			  pevent->dn);
+		log_error_r("update_zone (syncrepl) failed for %s. "
+			    "Zones can be outdated, run `rndc reload`",
+			    ldap_entry_logname(entry));
 
 	isc_mem_free(mctx, pevent->dbname);
 	if (pevent->prevdn != NULL)
 		isc_mem_free(mctx, pevent->prevdn);
-	isc_mem_free(mctx, pevent->dn);
 	ldap_entry_destroy(&entry);
 	isc_mem_detach(&mctx);
 	isc_event_free(&event);
@@ -3733,13 +3736,12 @@ cleanup:
 		sync_event_signal(inst->sctx, event);
 	}
 	if (result != ISC_R_SUCCESS)
-		log_error_r("update_config (syncrepl) failed for '%s'. "
+		log_error_r("update_config (syncrepl) failed for %s. "
 			    "Configuration can be outdated, run `rndc reload`",
-			    pevent->dn);
+			    ldap_entry_logname(entry));
 
 	ldap_entry_destroy(&entry);
 	isc_mem_free(mctx, pevent->dbname);
-	isc_mem_free(mctx, pevent->dn);
 	isc_mem_detach(&mctx);
 	isc_event_free(&event);
 	isc_task_detach(&task);
@@ -3817,8 +3819,8 @@ update_restart:
 	/* This code is disabled because we don't have UUID->DN database yet.
 	    || SYNCREPL_MODDN(pevent->chgtype)) { */
 	if (SYNCREPL_DEL(pevent->chgtype)) {
-		log_debug(5, "syncrepl_update: removing name from rbtdb, dn: '%s'",
-			  pevent->dn);
+		log_debug(5, "syncrepl_update: removing name from rbtdb, "
+			  "%s", ldap_entry_logname(entry));
 		/* Do nothing. rdatalist is initialized to empty list,
 		 * so resulting diff will remove all the data from node. */
 	}
@@ -3854,8 +3856,8 @@ update_restart:
 
 	if (SYNCREPL_ADD(pevent->chgtype) || SYNCREPL_MOD(pevent->chgtype)) {
 		/* Parse new data from LDAP. */
-		log_debug(5, "syncrepl_update: updating name in rbtdb, dn: '%s'",
-		          pevent->dn);
+		log_debug(5, "syncrepl_update: updating name in rbtdb, "
+			  "%s", ldap_entry_logname(entry));
 		CHECK(setting_get_str("fake_mname", inst->local_settings,
 				      &fake_mname));
 		CHECK(ldap_parse_rrentry(mctx, entry, &entry->zone_name, fake_mname,
@@ -3928,33 +3930,35 @@ cleanup:
 	   (result == DNS_R_NOTLOADED || result == DNS_R_BADZONE)) {
 		dns_zone_log(raw, ISC_LOG_DEBUG(1),
 			     "reloading invalid zone after a change; "
-			     "reload triggered by change in '%s'",
-			     pevent->dn);
+			     "reload triggered by change in %s",
+			     ldap_entry_logname(entry));
 		if (secure != NULL)
 			result = load_zone(secure, ISC_TRUE);
 		else if (raw != NULL)
 			result = load_zone(raw, ISC_TRUE);
 		if (result == ISC_R_SUCCESS || result == DNS_R_UPTODATE ||
 		    result == DNS_R_DYNAMIC || result == DNS_R_CONTINUE) {
 			/* zone reload succeeded, fire current event again */
 			log_debug(1, "restarting update_record after zone reload "
-				     "caused by change in '%s'", pevent->dn);
+				     "caused by change in %s",
+				     ldap_entry_logname(entry));
 			zone_reloaded = ISC_TRUE;
 			result = dns_zone_getserial2(raw, &serial);
 			if (result == ISC_R_SUCCESS)
 				goto update_restart;
 		} else {
 			dns_zone_log(raw, ISC_LOG_ERROR,
 				    "unable to reload invalid zone; "
-				    "reload triggered by change in '%s':%s",
-				    pevent->dn, dns_result_totext(result));
+				    "reload triggered by change in %s: %s",
+				    ldap_entry_logname(entry),
+				    dns_result_totext(result));
 		}
 
 	} else if (result != ISC_R_SUCCESS) {
 		/* error other than invalid zone */
-		log_error_r("update_record (syncrepl) failed, dn '%s' change type 0x%x. "
-			  "Records can be outdated, run `rndc reload`",
-			  pevent->dn, pevent->chgtype);
+		log_error_r("update_record (syncrepl) failed, %s change type "
+			    "0x%x. Records can be outdated, run `rndc reload`",
+			    ldap_entry_logname(entry), pevent->chgtype);
 	}
 
 	if (inst != NULL) {
@@ -3973,7 +3977,6 @@ cleanup:
 	if (pevent->prevdn != NULL)
 		isc_mem_free(mctx, pevent->prevdn);
 	ldap_entry_destroy(&entry);
-	isc_mem_free(mctx, pevent->dn);
 	isc_mem_detach(&mctx);
 	isc_event_free(&event);
 	isc_task_detach(&task);
@@ -4058,7 +4061,6 @@ syncrepl_update(ldap_instance_t *inst, ldap_entry_t **entryp, int chgtype)
 
 	isc_mem_attach(inst->mctx, &mctx);
 
-	CHECKED_MEM_STRDUP(mctx, entry->dn, dn);
 	CHECKED_MEM_STRDUP(mctx, inst->db_name, dbname);
 
 	if (entry->class & LDAP_ENTRYCLASS_MASTER)
@@ -4078,7 +4080,8 @@ syncrepl_update(ldap_instance_t *inst, ldap_entry_t **entryp, int chgtype)
 		else {
 			/* TODO: Fix race condition:
 			 * zone is not (yet) present in zone register */
-			log_debug(1, "TODO: DN '%s': task fallback", entry->dn);
+			log_debug(1, "TODO: %s: task fallback",
+				  ldap_entry_logname(entry));
 			isc_task_attach(inst->task, &task);
 			result = ISC_R_SUCCESS;
 		}
@@ -4131,7 +4134,6 @@ syncrepl_update(ldap_instance_t *inst, ldap_entry_t **entryp, int chgtype)
 
 	pevent->mctx = mctx;
 	pevent->dbname = dbname;
-	pevent->dn = dn;
 	pevent->prevdn = NULL;
 	pevent->chgtype = chgtype;
 	pevent->entry = entry;
@@ -4148,16 +4150,14 @@ cleanup:
 	if (zone_ptr != NULL)
 		dns_zone_detach(&zone_ptr);
 	if (result != ISC_R_SUCCESS)
-		log_error_r("syncrepl_update failed for object '%s'",
-			    entry->dn);
+		log_error_r("syncrepl_update failed for %s",
+			    ldap_entry_logname(entry));
 	if (wait_event == NULL) {
 		/* Event was not sent */
 		sync_concurr_limit_signal(inst->sctx);
 
 		if (dbname != NULL)
 			isc_mem_free(mctx, dbname);
-		if (dn != NULL)
-			isc_mem_free(mctx, dn);
 		if (mctx != NULL)
 			isc_mem_detach(&mctx);
 		ldap_entry_destroy(entryp);
@@ -4277,34 +4277,34 @@ int ldap_sync_search_entry (
 	if (phase == LDAP_SYNC_CAPI_DELETE || phase == LDAP_SYNC_CAPI_MODIFY) {
 		INSIST(setting_get_str("base", inst->local_settings,
 				       &ldap_base) == ISC_R_SUCCESS);
-		CHECK(ldap_entry_reconstruct(inst->mctx, inst->zone_register,
-					     ldap_base, inst->mldapdb, entryUUID,
-					     &old_entry));
+		CHECK(ldap_entry_reconstruct(inst->mctx, inst->mldapdb,
+					     entryUUID, &old_entry));
 	}
 	if (phase == LDAP_SYNC_CAPI_ADD || phase == LDAP_SYNC_CAPI_MODIFY) {
 		CHECK(ldap_entry_parse(inst->mctx, ls->ls_ld, msg, entryUUID,
 				       &new_entry));
 	}
 	/* detect type of modification */
 	if (phase == LDAP_SYNC_CAPI_MODIFY) {
 		if (old_entry->class != new_entry->class)
 			log_error("unsupported operation: "
-				  "object class in object '%s' changed: "
+				  "object class in %s changed: "
 				  "rndc reload might be necessary",
-				  new_entry->dn);
+				  ldap_entry_logname(new_entry));
 		if ((old_entry->class & LDAP_ENTRYCLASS_CONFIG) == 0)
 			modrdn = !(dns_name_equal(&old_entry->zone_name,
 						  &new_entry->zone_name)
 				   && dns_name_equal(&old_entry->fqdn,
 						     &new_entry->fqdn));
 		if (modrdn == ISC_TRUE) {
-			log_debug(1, "detected entry rename: DN '%s' -> '%s'",
-				  old_entry->dn, new_entry->dn);
+			log_debug(1, "detected entry rename: %s -> %s",
+				  ldap_entry_logname(old_entry),
+				  ldap_entry_logname(new_entry));
 			if (old_entry->class != LDAP_ENTRYCLASS_RR)
 				log_bug("LDAP MODRDN is supported only for "
-					"records, not zones or configs; DN '%s' "
+					"records, not zones or configs; %s; "
 					"rndc reload might be necessary",
-					new_entry->dn);
+					ldap_entry_logname(new_entry));
 		}
 	}
 	if (phase == LDAP_SYNC_CAPI_DELETE || modrdn == ISC_TRUE) {
@@ -4616,3 +4616,17 @@ ldap_instance_isexiting(ldap_instance_t *ldap_inst)
 {
 	return ldap_inst->exiting;
 }
+
+/**
+ * Mark LDAP instance as 'tainted' by unrecoverable error, e.g. unsupported
+ * MODRDN. Full reload is required to recover consistency
+ * (if it is even possible). */
+void
+ldap_instance_taint(ldap_instance_t *ldap_inst) {
+	isc_atomic_store(&ldap_inst->tainted, 1);
+}
+
+isc_boolean_t
+ldap_instance_istained(ldap_instance_t *ldap_inst) {
+	return ISC_TF(isc_atomic_cmpxchg(&ldap_inst->tainted, 0, 0) != 0);
+}
diff --git a/src/ldap_helper.h b/src/ldap_helper.h
index 7e328226eb34c956b8e17015ad1a3516a2a11dd3..a2ed9c424ce0b02b4c1274d82d0eeff8eab5f9fe 100644
--- a/src/ldap_helper.h
+++ b/src/ldap_helper.h
@@ -104,4 +104,6 @@ isc_task_t * ldap_instance_gettask(ldap_instance_t *ldap_inst);
 
 isc_boolean_t ldap_instance_isexiting(ldap_instance_t *ldap_inst) ATTR_NONNULLS ATTR_CHECKRESULT;
 
+void ldap_instance_taint(ldap_instance_t *ldap_inst) ATTR_NONNULLS;
+
 #endif /* !_LD_LDAP_HELPER_H_ */
diff --git a/src/settings.c b/src/settings.c
index 8ddbd656ccccd3adb8486685eb47622aabf8cd3d..fd4d962490220889bde581a41eb9399a530e1991 100644
--- a/src/settings.c
+++ b/src/settings.c
@@ -432,23 +432,24 @@ setting_update_from_ldap_entry(const char *name, settings_set_t *set,
 	result = ldap_entry_getvalues(entry, attr_name, &values);
 	if (result == ISC_R_NOTFOUND || HEAD(values) == NULL) {
 		CHECK(setting_unset(name, set));
-		log_debug(2, "setting '%s' (%s) was deleted in object '%s'",
-			  name, attr_name, entry->dn);
+		log_debug(2, "setting '%s' (%s) was deleted in object %s",
+			  name, attr_name, ldap_entry_logname(entry));
 		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);
+			"'%s' in %s", attr_name,
+			ldap_entry_logname(entry));
 		return ISC_R_NOTIMPLEMENTED;
 	}
 
 	CHECK(setting_set(name, set, HEAD(values)->value));
-	log_debug(2, "setting '%s' (%s) was changed to '%s' in object '%s'",
-		  name, attr_name, HEAD(values)->value, entry->dn);
+	log_debug(2, "setting '%s' (%s) was changed to '%s' in %s", name,
+		  attr_name, HEAD(values)->value, ldap_entry_logname(entry));
 
 cleanup:
 	if (result == ISC_R_NOTFOUND)
-- 
2.1.0

-- 
Manage your subscription for the Freeipa-devel mailing list:
https://www.redhat.com/mailman/listinfo/freeipa-devel
Contribute to FreeIPA: http://www.freeipa.org/page/Contribute/Code

Reply via email to