On 22.7.2013 13:23, Petr Spacek wrote:
Hello,

Replace LDAP persistent search with syncrepl (RFC 4533).

All direct operations with LDAP Persistent Search control are replaced
by ldap_sync_* calls.

Syncrepl code works in exactly same way as old psearch code:
Only the DN of the modified object is re-used from the message,
data from the object are fetched via separate LDAP search.

Current code is not able to detect object renaming because we don't have
UUID->DN mapping yet.

Another limitation is that current code can't detect unchanged entries,
so serial is incremented after each parsed LDAP object.

Clang noticed potential NULL dereference in cleanup section of ldap_syncrepl_watcher(). Fixed patch is attached.

--
Petr^2 Spacek
From e5abf7bcacfb6596fc23dd68dad6f4b4684903b4 Mon Sep 17 00:00:00 2001
From: Petr Spacek <pspa...@redhat.com>
Date: Mon, 22 Jul 2013 12:59:38 +0200
Subject: [PATCH] Replace LDAP persistent search with syncrepl (RFC 4533).

All direct operations with LDAP Persistent Search control are replaced
by ldap_sync_* calls.

Syncrepl code works in exactly same way as old psearch code:
Only the DN of the modified object is re-used from the message,
data from the object are fetched via separate LDAP search.

Current code is not able to detect object renaming because we don't have
UUID->DN mapping yet.

Another limitation is that current code can't detect unchanged entries,
so serial is incremented after each parsed LDAP object.

Signed-off-by: Petr Spacek <pspa...@redhat.com>
---
 src/ldap_entry.c  |  20 +--
 src/ldap_helper.c | 472 ++++++++++++++++++++++--------------------------------
 2 files changed, 197 insertions(+), 295 deletions(-)

diff --git a/src/ldap_entry.c b/src/ldap_entry.c
index 9a3cdf2183b4bbd22663f78440559b573d946bc4..986126dd10fdfdd93666362b87433138615d9bca 100644
--- a/src/ldap_entry.c
+++ b/src/ldap_entry.c
@@ -241,9 +241,11 @@ ldap_entry_destroy(isc_mem_t *mctx, ldap_entry_t **entryp)
 {
 	ldap_entry_t *entry;
 
-	REQUIRE(entryp != NULL && *entryp != NULL);
+	REQUIRE(entryp != NULL);
 
 	entry = *entryp;
+	if (entry == NULL)
+		return;
 
 	ldap_attributelist_destroy(mctx, &entry->attrs);
 	if (entry->dn != NULL)
@@ -440,22 +442,6 @@ ldap_entry_getclass(ldap_entry_t *entry, ldap_entryclass_t *class)
 
 	*class = entryclass;
 	return ISC_R_SUCCESS;
-
-#if 0
-	/* Preserve current attribute iterator */
-	lastattr = = entry->lastattr;
-	entry->lastattr = NULL;
-
-	while ((attr = ldap_entry_nextattr(entry, "objectClass")) != NULL) {
-		if (!strcasecmp(attr->ldap_values[0], "idnsrecord")) {
-			entryclass |= LDAP_ENTRYCLASS_RR;
-		} else if (!strcasecmp(attr->ldap_values[0], "idnszone")) {
-			entryclass |= LDAP_ENTRYCLASS_ZONE;
-		}
-	}
-
-	entry->lastattr = lastattr;
-#endif
 }
 
 ld_string_t*
diff --git a/src/ldap_helper.c b/src/ldap_helper.c
index 6a04d2b7da3977e3517fd7f449b7d218be6e1e93..15f2afd2edf5a4094a4ac9a61e93292f12da3f45 100644
--- a/src/ldap_helper.c
+++ b/src/ldap_helper.c
@@ -184,7 +184,6 @@ struct ldap_connection {
 	isc_mutex_t		lock;
 
 	LDAP			*handle;
-	LDAPControl		*serverctrls[2]; /* psearch/NULL or NULL/NULL */
 	int			msgid;
 
 	/* For reconnection logic. */
@@ -222,11 +221,11 @@ const ldap_auth_pair_t supported_ldap_auth[] = {
 };
 
 #define LDAPDB_EVENTCLASS 	ISC_EVENTCLASS(0xDDDD)
-#define LDAPDB_EVENT_PSEARCH	(LDAPDB_EVENTCLASS + 0)
+#define LDAPDB_EVENT_SYNCREPL	(LDAPDB_EVENTCLASS + 0)
 
-typedef struct ldap_psearchevent ldap_psearchevent_t;
-struct ldap_psearchevent {
-	ISC_EVENT_COMMON(ldap_psearchevent_t);
+typedef struct ldap_syncreplevent ldap_syncreplevent_t;
+struct ldap_syncreplevent {
+	ISC_EVENT_COMMON(ldap_syncreplevent_t);
 	isc_mem_t *mctx;
 	char *dbname;
 	char *dn;
@@ -338,17 +337,9 @@ static void ldap_pool_putconnection(ldap_pool_t *pool,
 static isc_result_t ldap_pool_connect(ldap_pool_t *pool,
 		ldap_instance_t *ldap_inst) ATTR_NONNULLS;
 
-/* Functions for manipulating LDAP persistent search control */
-static isc_result_t ldap_pscontrol_create(LDAPControl **ctrlp) ATTR_NONNULLS;
-
-static isc_threadresult_t ldap_psearch_watcher(isc_threadarg_t arg) ATTR_NONNULLS;
-static void psearch_update(ldap_instance_t *inst, ldap_entry_t *entry,
-		LDAPControl **ctrls) ATTR_NONNULL(1, 2);
-
-
 /* Persistent updates watcher */
 static isc_threadresult_t
-ldap_psearch_watcher(isc_threadarg_t arg);
+ldap_syncrepl_watcher(isc_threadarg_t arg) ATTR_NONNULLS;
 
 #define PRINT_BUFF_SIZE 10 /* for unsigned int 2^32 */
 isc_result_t
@@ -573,10 +564,10 @@ new_ldap_instance(isc_mem_t *mctx, const char *db_name,
 	CHECK(ldap_pool_connect(ldap_inst->pool, ldap_inst));
 
 	/* Start the watcher thread */
-	result = isc_thread_create(ldap_psearch_watcher, ldap_inst,
+	result = isc_thread_create(ldap_syncrepl_watcher, ldap_inst,
 				   &ldap_inst->watcher);
 	if (result != ISC_R_SUCCESS) {
-		log_error("Failed to create psearch watcher thread");
+		log_error("Failed to create syncrepl watcher thread");
 		goto cleanup;
 	}
 
@@ -665,8 +656,6 @@ new_ldap_connection(ldap_pool_t *pool, ldap_connection_t **ldap_connp)
 
 	isc_mem_attach(pool->mctx, &ldap_conn->mctx);
 
-	CHECK(ldap_pscontrol_create(&ldap_conn->serverctrls[0]));
-
 	*ldap_connp = ldap_conn;
 
 	return ISC_R_SUCCESS;
@@ -692,10 +681,6 @@ destroy_ldap_connection(ldap_connection_t **ldap_connp)
 	if (ldap_conn->handle != NULL)
 		ldap_unbind_ext_s(ldap_conn->handle, NULL, NULL);
 
-	if (ldap_conn->serverctrls[0] != NULL) {
-		ldap_control_free(ldap_conn->serverctrls[0]);
-	}
-
 	MEM_PUT_AND_DETACH(*ldap_connp);
 }
 
@@ -3319,65 +3304,18 @@ cleanup:
 	return result;
 }
 
-#define LDAP_CONTROL_PERSISTENTSEARCH "2.16.840.1.113730.3.4.3"
-#define LDAP_CONTROL_ENTRYCHANGE "2.16.840.1.113730.3.4.7"
+#define LDAP_ENTRYCHANGE_ALL	(LDAP_SYNC_CAPI_ADD | LDAP_SYNC_CAPI_DELETE | LDAP_SYNC_CAPI_MODIFY)
 
-#define LDAP_ENTRYCHANGE_NONE	0 /* entry change control is not present */
-#define LDAP_ENTRYCHANGE_ADD	1
-#define LDAP_ENTRYCHANGE_DEL	2
-#define LDAP_ENTRYCHANGE_MOD	4
-#define LDAP_ENTRYCHANGE_MODDN	8
-#define LDAP_ENTRYCHANGE_ALL	(1 | 2 | 4 | 8)
-
-#define PSEARCH_ADD(chgtype) ((chgtype & LDAP_ENTRYCHANGE_ADD) != 0)
-#define PSEARCH_DEL(chgtype) ((chgtype & LDAP_ENTRYCHANGE_DEL) != 0)
-#define PSEARCH_MOD(chgtype) ((chgtype & LDAP_ENTRYCHANGE_MOD) != 0)
-#define PSEARCH_MODDN(chgtype) ((chgtype & LDAP_ENTRYCHANGE_MODDN) != 0)
-#define PSEARCH_ANY(chgtype) ((chgtype & LDAP_ENTRYCHANGE_ALL) != 0)
-/*
- * Creates persistent search (aka psearch,
- * http://tools.ietf.org/id/draft-ietf-ldapext-psearch-03.txt) control.
+#define SYNCREPL_ADD(chgtype) (chgtype == LDAP_SYNC_CAPI_ADD)
+#define SYNCREPL_DEL(chgtype) (chgtype == LDAP_SYNC_CAPI_DELETE)
+#define SYNCREPL_MOD(chgtype) (chgtype == LDAP_SYNC_CAPI_MODIFY)
+/* SYNCREPL_MODDN: Change in DN can be detected only via UUID->DN mapping:
+ * Map UUID to (remembered) DN and compare remembered DN with new one. */
+/* SYNCREPL_ANY: Initial database dump should be detected via sync_ctx state:
+ * All changes received before first 'intermediate' message contain initial
+ * state of the database.
+#define SYNCREPL_ANY(chgtype) ((chgtype & LDAP_ENTRYCHANGE_ALL) != 0)
  */
-static isc_result_t
-ldap_pscontrol_create(LDAPControl **ctrlp)
-{
-	BerElement *ber = NULL;
-	struct berval *berval = NULL;
-	isc_result_t result = ISC_R_FAILURE;
-
-	REQUIRE(ctrlp != NULL && *ctrlp == NULL);
-
-	ber = ber_alloc_t(LBER_USE_DER);
-	if (ber == NULL)
-		return ISC_R_NOMEMORY;
-
-	/*
-	 * Check the draft above, section 4 to get info about PS control
-	 * format.
-	 *
-	 * We are interested in all changes in DNS related DNs and we
-	 * want to get initial state of the "watched" LDAP subtree.
-	 */
-	if (ber_printf(ber, "{ibb}", LDAP_ENTRYCHANGE_ALL, 0, 1) == -1)
-		goto cleanup;
-
-	if (ber_flatten(ber, &berval) < 0)
-		goto cleanup;
-
-	if (ldap_control_create(LDAP_CONTROL_PERSISTENTSEARCH, 1, berval, 1, ctrlp)
-			!= LDAP_SUCCESS)
-		goto cleanup;
-
-	result = ISC_R_SUCCESS;
-
-cleanup:
-	if (ber != NULL)
-		ber_free(ber, 1);
-	if (berval != NULL)
-		ber_bvfree(berval);
-
-	return result;
-}
 
 static inline isc_result_t
 ldap_get_zone_serial(ldap_instance_t *inst, dns_name_t *zone_name,
@@ -3470,25 +3408,25 @@ cleanup:
 static void
 update_zone(isc_task_t *task, isc_event_t *event)
 {
-	ldap_psearchevent_t *pevent = (ldap_psearchevent_t *)event;
+	ldap_syncreplevent_t *pevent = (ldap_syncreplevent_t *)event;
 	isc_result_t result ;
 	ldap_instance_t *inst = NULL;
 	ldap_qresult_t *ldap_qresult_zone = NULL;
-	ldap_qresult_t *ldap_qresult_record = NULL;
+	/* ldap_qresult_t *ldap_qresult_record = NULL; */
 	ldap_entryclass_t objclass;
 	ldap_entry_t *entry_zone = NULL;
-	ldap_entry_t *entry_record = NULL;
+	/* ldap_entry_t *entry_record = NULL; */
 	isc_mem_t *mctx;
 	dns_name_t prevname;
 	dns_name_t currname;
 	char *attrs_zone[] = {
 		"idnsName", "idnsUpdatePolicy", "idnsAllowQuery",
 		"idnsAllowTransfer", "idnsForwardPolicy", "idnsForwarders",
 		"idnsAllowDynUpdate", "idnsAllowSyncPTR", "objectClass", NULL
 	};
-	char *attrs_record[] = {
+	/* char *attrs_record[] = {
 			"objectClass", "dn", NULL
-	};
+	}; */
 
 	UNUSED(task);
 
@@ -3517,44 +3455,46 @@ update_zone(isc_task_t *task, isc_event_t *event)
 		else if (objclass & LDAP_ENTRYCLASS_FORWARD)
 			CHECK(ldap_parse_fwd_zoneentry(entry_zone, inst));
 
-		if (PSEARCH_MODDN(pevent->chgtype)) {
+		/* 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 */
+			// 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)) {
 
-					psearch_update(inst, entry_record, NULL);
+					syncrepl_update(inst, entry_record, NULL);
 				}
 			}
 		}
+		*/
 
 		INSIST(NEXT(entry_zone, link) == NULL); /* no multiple zones with same DN */
 	} else {
 		CHECK(ldap_delete_zone(inst, pevent->dn, ISC_TRUE, ISC_FALSE));
 	}
 
 cleanup:
 	if (result != ISC_R_SUCCESS)
-		log_error_r("update_zone (psearch) failed for '%s'. "
+		log_error_r("update_zone (syncrepl) failed for '%s'. "
 			  "Zones can be outdated, run `rndc reload`",
 			  pevent->dn);
 
 	ldap_query_free(ISC_FALSE, &ldap_qresult_zone);
-	ldap_query_free(ISC_FALSE, &ldap_qresult_record);
+	/* ldap_query_free(ISC_FALSE, &ldap_qresult_record); */
 	if (dns_name_dynamic(&currname))
 		dns_name_free(&currname, inst->mctx);
 	if (dns_name_dynamic(&prevname))
@@ -3570,7 +3510,7 @@ cleanup:
 static void
 update_config(isc_task_t *task, isc_event_t *event)
 {
-	ldap_psearchevent_t *pevent = (ldap_psearchevent_t *)event;
+	ldap_syncreplevent_t *pevent = (ldap_syncreplevent_t *)event;
 	isc_result_t result ;
 	ldap_instance_t *inst = NULL;
 	ldap_qresult_t *ldap_qresult = NULL;
@@ -3604,7 +3544,7 @@ update_config(isc_task_t *task, isc_event_t *event)
 
 cleanup:
 	if (result != ISC_R_SUCCESS)
-		log_error_r("update_config (psearch) failed for '%s'. "
+		log_error_r("update_config (syncrepl) failed for '%s'. "
 			  "Configuration can be outdated, run `rndc reload`",
 			  pevent->dn);
 
@@ -3621,13 +3561,13 @@ cleanup:
  * If it exists it is replaced with newer version.
  *
  * @param task Task indentifier.
- * @param event Internal data of type ldap_psearchevent_t.
+ * @param event Internal data of type ldap_syncreplevent_t.
  */
 static void
 update_record(isc_task_t *task, isc_event_t *event)
 {
-	/* Psearch event */
-	ldap_psearchevent_t *pevent = (ldap_psearchevent_t *)event;
+	/* syncrepl event */
+	ldap_syncreplevent_t *pevent = (ldap_syncreplevent_t *)event;
 	isc_result_t result;
 	ldap_instance_t *inst = NULL;
 	ldap_cache_t *cache = NULL;
@@ -3659,8 +3599,10 @@ update_record(isc_task_t *task, isc_event_t *event)
 	zone_found = ISC_TRUE;
 
 update_restart:
-	if (PSEARCH_DEL(pevent->chgtype) || PSEARCH_MODDN(pevent->chgtype)) {
-		log_debug(5, "psearch_update: removing name from cache, dn: '%s'",
+	/* 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 cache, dn: '%s'",
 		          pevent->dn);
 	}
 
@@ -3671,33 +3613,36 @@ update_restart:
 
 	/* TODO: double check correctness before replacing ldap_query() with
 	 *       data from *event */
-	if (PSEARCH_MODDN(pevent->chgtype)) {
-		/* remove previous name only if it was inside DNS subtree */
+	/* This code is disabled because we don't have UUID->DN database yet.
+	if (SYNCREPL_MODDN(pevent->chgtype)) {
+		// remove previous name only if it was inside DNS subtree //
 		if (dn_to_dnsname(mctx, pevent->prevdn, &prevname, &prevorigin)
 				== ISC_R_SUCCESS) {
-			log_debug(5, "psearch_update: removing name from cache, dn: '%s'",
+			log_debug(5, "syncrepl_update: removing name from cache, dn: '%s'",
 					  pevent->prevdn);
 			cache = NULL;
 			result = zr_get_zone_cache(inst->zone_register, &prevname, &cache);
 			if (result == ISC_R_SUCCESS)
 				CHECK(discard_from_cache(cache, &prevname));
 			else if (result != ISC_R_NOTFOUND)
 				goto cleanup;
 		} else {
-			log_debug(5, "psearch_update: old name wasn't managed "
+			log_debug(5, "syncrepl_update: old name wasn't managed "
 					"by plugin, dn '%s'", pevent->prevdn);
 		}
 	}
+	*/
 
-	if (PSEARCH_ADD(pevent->chgtype) || PSEARCH_MOD(pevent->chgtype) ||
-			PSEARCH_MODDN(pevent->chgtype) || !PSEARCH_ANY(pevent->chgtype)) {
+	if (SYNCREPL_ADD(pevent->chgtype) || SYNCREPL_MOD(pevent->chgtype)) /* ||
+			SYNCREPL_MODDN(pevent->chgtype) ||
+			!SYNCREPL_ANY(pevent->chgtype)) */ {
 		/* 
-		 * Find new data in LDAP. !PSEARCH_ANY indicates unchanged entry
+		 * Find new data in LDAP. !SYNCREPL_ANY indicates unchanged entry
 		 * found during initial lookup (i.e. database dump).
 		 *
 		 * @todo Change this to convert ldap_entry_t to ldapdb_rdatalist_t.
 		 */
-		log_debug(5, "psearch_update: updating name in cache, dn: '%s'",
+		log_debug(5, "syncrepl_update: updating name in cache, dn: '%s'",
 		          pevent->dn);
 		CHECK(ldapdb_rdatalist_get(mctx, inst, &name, &origin, &rdatalist));
 	
@@ -3711,7 +3656,10 @@ update_restart:
 	}
 
 	/* Do not bump serial during initial database dump. */
-	if (PSEARCH_ANY(pevent->chgtype)) {
+	/* This optimization is disabled because we don't have SYNCREPL_ANY
+	 * detection at the moment.
+	if (SYNCREPL_ANY(pevent->chgtype)) { */
+	{
 		zone_settings = NULL;
 		CHECK(zr_get_zone_settings(inst->zone_register, &origin, &zone_settings));
 		CHECK(setting_get_bool("serial_autoincrement", zone_settings,
@@ -3762,7 +3710,7 @@ cleanup:
 
 	} else if (result != ISC_R_SUCCESS) {
 		/* error other than invalid zone */
-		log_error_r("update_record (psearch) failed, dn '%s' change type 0x%x. "
+		log_error_r("update_record (syncrepl) failed, dn '%s' change type 0x%x. "
 			  "Records can be outdated, run `rndc reload`",
 			  pevent->dn, pevent->chgtype);
 	}
@@ -3785,97 +3733,35 @@ cleanup:
 	isc_event_free(&event);
 }
 
-/*
- * Parses persistent search entrychange control.
- *
- * This entry says if particular entry was added/modified/deleted.
- * Details are in http://tools.ietf.org/id/draft-ietf-ldapext-psearch-03.txt
- */
-static isc_result_t
-ldap_parse_entrychangectrl(LDAPControl **ctrls, int *chgtypep, char **prevdnp)
-{
-	int i;
-	isc_result_t result = ISC_R_SUCCESS;
-	BerElement *ber = NULL;
-	ber_int_t chgtype;
-	ber_tag_t berret;
-	char *prevdn = NULL;
-
-	REQUIRE(ctrls != NULL);
-	REQUIRE(chgtypep != NULL);
-	REQUIRE(prevdnp != NULL && *prevdnp == NULL);
-
-	/* Find entrycontrol OID */
-	for (i = 0; ctrls[i] != NULL; i++) {
-		if (strcmp(ctrls[i]->ldctl_oid,
-		    LDAP_CONTROL_ENTRYCHANGE) == 0)
-			break;
-	}
-
-	if (ctrls[i] == NULL)
-		return ISC_R_NOTFOUND;
-
-	ber = ber_init(&(ctrls[i]->ldctl_value));
-	if (ber == NULL)
-		return ISC_R_NOMEMORY;
-
-	berret = ber_scanf(ber, "{e", &chgtype);
-	if (berret == LBER_ERROR) {
-		result = ISC_R_FAILURE;
-		goto cleanup;
-	}
-
-	if (chgtype == LDAP_ENTRYCHANGE_MODDN) {
-		berret = ber_scanf(ber, "a", &prevdn);
-		if (berret == LBER_ERROR) {
-			result = ISC_R_FAILURE;
-			goto cleanup;
-		}
-	}
-
-	*chgtypep = chgtype;
-	*prevdnp = prevdn;
-
-cleanup:
-	if (ber != NULL)
-		ber_free(ber, 1);
-
-	return result;
-}
-
 static void
-psearch_update(ldap_instance_t *inst, ldap_entry_t *entry, LDAPControl **ctrls)
+syncrepl_update(ldap_instance_t *inst, ldap_entry_t *entry, int chgtype)
 {
 	ldap_entryclass_t class;
 	isc_result_t result = ISC_R_SUCCESS;
-	ldap_psearchevent_t *pevent = NULL;
-	int chgtype = LDAP_ENTRYCHANGE_NONE;
+	ldap_syncreplevent_t *pevent = NULL;
 	char *dn = NULL;
 	char *prevdn_ldap = NULL;
 	char *prevdn = NULL;
 	char *dbname = NULL;
 	isc_mem_t *mctx = NULL;
 	isc_taskaction_t action = NULL;
 
 	CHECK(ldap_entry_getclass(entry, &class));
 
-	if (ctrls != NULL)
-		CHECK(ldap_parse_entrychangectrl(ctrls, &chgtype, &prevdn_ldap));
-
-
-	log_debug(20,"psearch change type: none%d, add%d, del%d, mod%d, moddn%d",
-				!PSEARCH_ANY(chgtype), PSEARCH_ADD(chgtype),
-				PSEARCH_DEL(chgtype), PSEARCH_MOD(chgtype),
-				PSEARCH_MODDN(chgtype));
+	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) */ );
 
 	isc_mem_attach(inst->mctx, &mctx);
 
 	CHECKED_MEM_STRDUP(mctx, entry->dn, dn);
 	CHECKED_MEM_STRDUP(mctx, inst->db_name, dbname);
 
-	if (PSEARCH_MODDN(chgtype)) {
+	/* This code is disabled because we don't have UUID->DN database yet.
+	if (SYNCREPL_MODDN(chgtype)) {
 		CHECKED_MEM_STRDUP(mctx, prevdn_ldap, prevdn);
 	}
+	*/
 
 	/*
 	 * We are very simple. Every update (add/mod/del) means that
@@ -3897,10 +3783,10 @@ psearch_update(ldap_instance_t *inst, ldap_entry_t *entry, LDAPControl **ctrls)
 		goto cleanup;
 	}
 
-	pevent = (ldap_psearchevent_t *)isc_event_allocate(inst->mctx,
-				inst, LDAPDB_EVENT_PSEARCH,
+	pevent = (ldap_syncreplevent_t *)isc_event_allocate(inst->mctx,
+				inst, LDAPDB_EVENT_SYNCREPL,
 				action, NULL,
-				sizeof(ldap_psearchevent_t));
+				sizeof(ldap_syncreplevent_t));
 
 	if (pevent == NULL) {
 		result = ISC_R_NOMEMORY;
@@ -3915,8 +3801,6 @@ psearch_update(ldap_instance_t *inst, ldap_entry_t *entry, LDAPControl **ctrls)
 	isc_task_send(inst->task, (isc_event_t **)&pevent);
 
 cleanup:
-	if (ctrls != NULL)
-		ldap_controls_free(ctrls);
 	if (result != ISC_R_SUCCESS) {
 		if (dbname != NULL)
 			isc_mem_free(mctx, dbname);
@@ -3929,7 +3813,7 @@ cleanup:
 		if (prevdn_ldap != NULL)
 			ldap_memfree(prevdn);
 
-		log_error_r("psearch_update failed for '%s' zone. "
+		log_error_r("syncrepl_update failed for '%s' zone. "
 			  "Zone can be outdated, run `rndc reload`",
 			  entry->dn);
 	}
@@ -3991,23 +3875,121 @@ install_usr1handler(void)
 }
 
 /*
+ * Called when a reference is returned by ldap_sync_init()/ldap_sync_poll().
+ */
+static int
+ldap_sync_search_reference (
+	ldap_sync_t			*ls,
+	LDAPMessage			*msg ) {
+
+	UNUSED(ls);
+	UNUSED(msg);
+
+	log_error("ldap_sync_search_reference is not yet handled");
+	return LDAP_SUCCESS;
+}
+
+/*
+ * Called when an entry is returned by ldap_sync_init()/ldap_sync_poll().
+ * If phase is LDAP_SYNC_CAPI_ADD or LDAP_SYNC_CAPI_MODIFY,
+ * the entry has been either added or modified, and thus
+ * the complete view of the entry should be in the LDAPMessage.
+ * If phase is LDAP_SYNC_CAPI_PRESENT or LDAP_SYNC_CAPI_DELETE,
+ * only the DN should be in the LDAPMessage.
+ */
+int ldap_sync_search_entry (
+	ldap_sync_t			*ls,
+	LDAPMessage			*msg,
+	struct berval			*entryUUID,
+	ldap_sync_refresh_t		phase ) {
+
+	ldap_instance_t *inst = ls->ls_private;
+	ldap_entry_t *entry = NULL;
+	isc_result_t result;
+
+	/* TODO: Use this for UUID->DN mapping and MODDN detection. */
+	UNUSED(entryUUID);
+
+	CHECK(ldap_entry_create(inst->mctx, ls->ls_ld, msg, &entry));
+	syncrepl_update(inst, entry, phase);
+
+cleanup:
+	if (result != ISC_R_SUCCESS)
+		log_error_r("ldap_sync_search_entry failed");
+		/* TODO: Add 'tainted' flag to the LDAP instance. */
+	ldap_entry_destroy(inst->mctx, &entry);
+
+	/* Following return code will never reach upper layers.
+	 * It is limitation in ldap_sync_init() and ldap_sync_poll()
+	 * provided by OpenLDAP libs at the time of writing (2013-07-22). */
+	return LDAP_SUCCESS;
+}
+
+/*
+ * Called when specific intermediate/final messages are returned
+ * by ldap_sync_init()/ldap_sync_poll().
+ * If phase is LDAP_SYNC_CAPI_PRESENTS or LDAP_SYNC_CAPI_DELETES,
+ * a "presents" or "deletes" phase begins.
+ * If phase is LDAP_SYNC_CAPI_DONE, a special "presents" phase
+ * with refreshDone set to "TRUE" has been returned, to indicate
+ * that the refresh phase of a refreshAndPersist is complete.
+ * In the above cases, syncUUIDs is NULL.
+ *
+ * If phase is LDAP_SYNC_CAPI_PRESENTS_IDSET or
+ * LDAP_SYNC_CAPI_DELETES_IDSET, syncUUIDs is an array of UUIDs
+ * that are either present or have been deleted.
+ */
+int ldap_sync_intermediate (
+	ldap_sync_t			*ls,
+	LDAPMessage			*msg,
+	BerVarray			syncUUIDs,
+	ldap_sync_refresh_t		phase ) {
+
+	UNUSED(ls);
+	UNUSED(msg);
+	UNUSED(syncUUIDs);
+	UNUSED(phase);
+
+	log_error("ldap_sync_intermediate is not yet handled");
+	return LDAP_SUCCESS;
+}
+
+/*
+ * Called when a searchResultDone is returned
+ * by ldap_sync_init()/ldap_sync_poll().
+ * In refreshAndPersist, this can only occur if the search for any reason
+ * is being terminated by the server.
+ */
+int ldap_sync_search_result (
+	ldap_sync_t			*ls,
+	LDAPMessage			*msg,
+	int				refreshDeletes ) {
+
+	UNUSED(ls);
+	UNUSED(msg);
+	UNUSED(refreshDeletes);
+
+	log_error("ldap_sync_search_result is not yet handled");
+	return LDAP_SUCCESS;
+}
+
+/*
  * NOTE:
- * Every blocking call in psearch_watcher thread must be preemptible.
+ * Every blocking call in syncrepl_watcher thread must be preemptible.
  */
 static isc_threadresult_t
-ldap_psearch_watcher(isc_threadarg_t arg)
+ldap_syncrepl_watcher(isc_threadarg_t arg)
 {
 	ldap_instance_t *inst = (ldap_instance_t *)arg;
 	ldap_connection_t *conn = NULL;
-	ldap_qresult_t *ldap_qresult = NULL;
-	struct timeval tv;
-	int ret, cnt;
+	int ret;
 	isc_result_t result;
 	sigset_t sigset;
 	isc_uint32_t reconnect_interval;
 	const char *base = NULL;
+	ldap_sync_t sync_ctx;
 
-	log_debug(1, "Entering ldap_psearch_watcher");
+	log_debug(1, "Entering ldap_syncrepl_watcher");
 
 	install_usr1handler();
 
@@ -4023,131 +4005,65 @@ ldap_psearch_watcher(isc_threadarg_t arg)
 	/* pthread_sigmask fails only due invalid args */
 	RUNTIME_CHECK(ret == 0);
 
-	/* Wait indefinitely */
-	tv.tv_sec = -1;
-	tv.tv_usec = 0;
-
 	/* Pick connection, one is reserved purely for this thread */
 	CHECK(ldap_pool_getconnection(inst->pool, &conn));
 
 	/* 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. "
+		log_error("ldap_syncrepl_watcher handle is NULL. "
 		          "Next try in %ds", reconnect_interval);
 		if (!sane_sleep(inst, reconnect_interval))
 			goto cleanup;
 		handle_connection_error(inst, conn, ISC_TRUE);
 	}
 
-	CHECK(ldap_query_create(conn->mctx, &ldap_qresult));
-
-restart:
-	/* Perform initial lookup */
-	ldap_query_free(ISC_TRUE, &ldap_qresult);
+	if (ldap_sync_initialize(&sync_ctx) == NULL) {
+		log_error("cannot initialize LDAP syncrepl context");
+		return (isc_threadresult_t)ISC_R_NOMEMORY;
+	}
 	CHECK(setting_get_str("base", inst->global_settings, &base));
-	log_debug(1, "Sending initial psearch lookup");
-	ret = ldap_search_ext(conn->handle,
-			      base,
-			      LDAP_SCOPE_SUBTREE,
-			      /*    class = record
-			       * OR class = config
-			       * OR class = zone
-			       * OR class = forward
-			       *
-			       * Inactive zones are handled
-			       * in update_zone. */
-			      "(|"
-			      "(objectClass=idnsRecord)"
-			      "(objectClass=idnsConfigObject)"
-			      "(objectClass=idnsZone)"
-			      "(objectClass=idnsForwardZone))",
-			      NULL, 0, conn->serverctrls, NULL, NULL,
-			      LDAP_NO_LIMIT, &conn->msgid);
-	if (ret != LDAP_SUCCESS) {
-		log_error("failed to send initial psearch request");
-		ldap_unbind_ext_s(conn->handle, NULL, NULL);
-		goto cleanup;
-	}
+	sync_ctx.ls_base = ldap_strdup(base);
+	sync_ctx.ls_ld = conn->handle;
+	sync_ctx.ls_search_entry = ldap_sync_search_entry;
+	sync_ctx.ls_search_reference = ldap_sync_search_reference;
+	sync_ctx.ls_intermediate = ldap_sync_intermediate;
+	sync_ctx.ls_search_result = ldap_sync_search_result;
+	sync_ctx.ls_private = inst;
 
+	ret = ldap_sync_init(&sync_ctx, LDAP_SYNC_REFRESH_AND_PERSIST);
+	/* TODO: error handling, set tainted flag & do full reload? */
+
+	log_debug(1, "Sending initial syncrepl lookup");
 	while (!inst->exiting) {
-		ret = ldap_result(conn->handle, conn->msgid, 0, &tv,
-				  &ldap_qresult->result);
-		if (ret <= 0) {
+		ret = ldap_sync_poll(&sync_ctx);
+		if (ret != LDAP_SUCCESS) {
+			/* TODO: LDAP_SYNC_REFRESH_REQUIRED = tainted flag */
 			/* Don't reconnect if signaled to exit */
 			CHECK_EXIT;
 			while (handle_connection_error(inst, conn, ISC_TRUE)
 			       != ISC_R_SUCCESS) {
 				CHECK(setting_get_uint("reconnect_interval",
 						       inst->global_settings,
 						       &reconnect_interval));
-				log_error("ldap_psearch_watcher failed to "
+				log_error("ldap_syncrepl_watcher failed to "
 					  "handle LDAP connection error. "
 					  "Reconnection in %ds",
 					  reconnect_interval);
 				if (!sane_sleep(inst, reconnect_interval))
 					goto cleanup;
 			}
-			goto restart;
-		}
-
-		switch (ret) {
-		case LDAP_RES_SEARCH_ENTRY:
-			break;
-		default:
-			log_debug(3, "Ignoring psearch msg with retcode %x",
-				  ret);
-		}
-
-		conn->tries = 0;
-		cnt = ldap_count_entries(conn->handle, ldap_qresult->result);
-
-		if (cnt > 0) {
-			log_debug(3, "Got psearch updates (%d)", cnt);
-			result = ldap_entrylist_append(conn->mctx,
-						       conn->handle,
-						       ldap_qresult->result,
-						       &ldap_qresult->ldap_entries);
-			if (result != ISC_R_SUCCESS) {
-				/*
-				 * Error means inconsistency of our zones
-				 * data.
-				 */
-				log_error_r("ldap_psearch_watcher failed, zones "
-					  "might be outdated. Run `rndc reload`");
-				goto soft_err;
-			}
-
-			ldap_entry_t *entry;
-			for (entry = HEAD(ldap_qresult->ldap_entries);
-			     entry != NULL;
-			     entry = NEXT(entry, link)) {
-				LDAPControl **ctrls = NULL;
-				ret = ldap_get_entry_controls(conn->handle,
-							      entry->ldap_entry,
-							      &ctrls);
-				if (ret != LDAP_SUCCESS) {
-					log_error("failed to extract controls "
-						  "from psearch update. Zones "
-						  "might be outdated, run "
-						  "`rndc reload");
-					goto soft_err;
-				}
-
-				psearch_update(inst, entry, ctrls);
-			}
-soft_err:
-			ldap_query_free(ISC_TRUE, &ldap_qresult);
 		}
 	}
 
-	log_debug(1, "Ending ldap_psearch_watcher");
-
 cleanup:
-	ldap_query_free(ISC_FALSE, &ldap_qresult);
+	log_debug(1, "Ending ldap_syncrepl_watcher");
+	ldap_sync_destroy(&sync_ctx, 0);
+	if (conn != NULL)
+		conn->handle = NULL; /* prevent double call to ldap_unbind() */
 	ldap_pool_putconnection(inst->pool, &conn);
 
 	return (isc_threadresult_t)0;
-- 
1.8.3.1

_______________________________________________
Freeipa-devel mailing list
Freeipa-devel@redhat.com
https://www.redhat.com/mailman/listinfo/freeipa-devel

Reply via email to