dovecot: LDAP handling rewrite. Reconnections are handled a lot ...

dovecot at dovecot.org dovecot at dovecot.org
Sat Dec 29 04:15:01 EET 2007


details:   http://hg.dovecot.org/dovecot/rev/0dcea80312b0
changeset: 7050:0dcea80312b0
user:      Timo Sirainen <tss at iki.fi>
date:      Sat Dec 29 04:14:56 2007 +0200
description:
LDAP handling rewrite. Reconnections are handled a lot better now. If
connection is down, requests are added to queue and they always stay there
at least 4 seconds.

diffstat:

4 files changed, 574 insertions(+), 506 deletions(-)
src/auth/db-ldap.c     |  645 ++++++++++++++++++++++++++----------------------
src/auth/db-ldap.h     |  105 +++++--
src/auth/passdb-ldap.c |  284 +++++++++------------
src/auth/userdb-ldap.c |   46 +--

diffs (truncated from 1453 to 300 lines):

diff -r bbeee3db9967 -r 0dcea80312b0 src/auth/db-ldap.c
--- a/src/auth/db-ldap.c	Sat Dec 29 04:11:59 2007 +0200
+++ b/src/auth/db-ldap.c	Sat Dec 29 04:14:56 2007 +0200
@@ -6,7 +6,9 @@
 
 #include "network.h"
 #include "ioloop.h"
+#include "array.h"
 #include "hash.h"
+#include "queue.h"
 #include "str.h"
 #include "var-expand.h"
 #include "settings.h"
@@ -42,10 +44,6 @@
 #  define LDAP_OPT_SUCCESS LDAP_SUCCESS
 #endif
 
-/* If server disconnects us, don't reconnect if no requests have been sent
-   for this many seconds. */
-#define LDAP_IDLE_RECONNECT_SECS 60
-
 struct db_ldap_result_iterate_context {
 	struct ldap_connection *conn;
 	LDAPMessage *entry;
@@ -61,6 +59,13 @@ struct db_ldap_result_iterate_context {
 
 	string_t *var, *debug;
 	unsigned int value_idx;
+};
+
+struct db_ldap_sasl_bind_context {
+	const char *authcid;
+	const char *passwd;
+	const char *realm;
+	const char *authzid;
 };
 
 #define DEF_STR(name) DEF_STRUCT_STR(name, ldap_settings)
@@ -118,7 +123,7 @@ static struct ldap_connection *ldap_conn
 static struct ldap_connection *ldap_connections = NULL;
 
 static int db_ldap_bind(struct ldap_connection *conn);
-static void ldap_conn_close(struct ldap_connection *conn, bool flush_requests);
+static void db_ldap_conn_close(struct ldap_connection *conn);
 
 static int deref2str(const char *str)
 {
@@ -165,49 +170,14 @@ const char *ldap_get_error(struct ldap_c
 	return ldap_err2string(ldap_get_errno(conn));
 }
 
-void db_ldap_add_delayed_request(struct ldap_connection *conn,
-				 struct ldap_request *request)
-{
-	request->next = NULL;
-
-	if (conn->delayed_requests_head == NULL)
-		conn->delayed_requests_head = request;
-	else
-		conn->delayed_requests_tail->next = request;
-	conn->delayed_requests_tail = request;
-}
-
-static void db_ldap_handle_next_delayed_request(struct ldap_connection *conn)
-{
-	struct ldap_request *request;
-
-	if (conn->delayed_requests_head == NULL)
-		return;
-
-	request = conn->delayed_requests_head;
-	conn->delayed_requests_head = request->next;
-	if (conn->delayed_requests_head == NULL)
-		conn->delayed_requests_tail = NULL;
-
-	conn->retrying = TRUE;
-	if (request->filter == NULL)
-		request->callback(conn, request, NULL);
-	else
-		db_ldap_search(conn, request, conn->set.ldap_scope);
-	conn->retrying = FALSE;
-}
-
 static void ldap_conn_reconnect(struct ldap_connection *conn)
 {
-	ldap_conn_close(conn, FALSE);
-
-	if (db_ldap_connect(conn) < 0) {
-		/* failed to reconnect. fail all requests. */
-		ldap_conn_close(conn, TRUE);
-	}
-}
-
-static void ldap_handle_error(struct ldap_connection *conn)
+	db_ldap_conn_close(conn);
+	if (db_ldap_connect(conn) < 0)
+		db_ldap_conn_close(conn);
+}
+
+static int ldap_handle_error(struct ldap_connection *conn)
 {
 	int err = ldap_get_errno(conn);
 
@@ -229,7 +199,7 @@ static void ldap_handle_error(struct lda
 	case LDAP_ALIAS_DEREF_PROBLEM:
 	case LDAP_FILTER_ERROR:
 		/* invalid input */
-		break;
+		return -1;
 	case LDAP_SERVER_DOWN:
 	case LDAP_TIMEOUT:
 	case LDAP_UNAVAILABLE:
@@ -242,132 +212,285 @@ static void ldap_handle_error(struct lda
 	default:
 		/* connection problems */
 		ldap_conn_reconnect(conn);
+		return 0;
+	}
+}
+
+static int db_ldap_request_bind(struct ldap_connection *conn,
+				struct ldap_request *request)
+{
+	struct ldap_request_bind *brequest =
+		(struct ldap_request_bind *)request;
+
+	i_assert(request->type == LDAP_REQUEST_TYPE_BIND);
+	i_assert(request->msgid == -1);
+	i_assert(conn->conn_state == LDAP_CONN_STATE_BOUND_AUTH ||
+		 conn->conn_state == LDAP_CONN_STATE_BOUND_DEFAULT);
+	i_assert(conn->pending_count == 0);
+
+	request->msgid = ldap_bind(conn->ld, brequest->dn,
+				   request->auth_request->mech_password,
+				   LDAP_AUTH_SIMPLE);
+	if (request->msgid == -1) {
+		auth_request_log_error(request->auth_request, "ldap",
+				       "ldap_bind(%s) failed: %s",
+				       brequest->dn, ldap_get_error(conn));
+		if (ldap_handle_error(conn) < 0) {
+			/* broken request, remove it */
+			return 0;
+		}
+		return -1;
+	}
+	conn->conn_state = LDAP_CONN_STATE_BINDING;
+	return 1;
+}
+
+static int db_ldap_request_search(struct ldap_connection *conn,
+				  struct ldap_request *request)
+{
+	struct ldap_request_search *srequest =
+		(struct ldap_request_search *)request;
+
+	i_assert(conn->conn_state == LDAP_CONN_STATE_BOUND_DEFAULT);
+	i_assert(request->msgid == -1);
+
+	request->msgid =
+		ldap_search(conn->ld, srequest->base, conn->set.ldap_scope,
+			    srequest->filter, srequest->attributes, 0);
+	if (request->msgid == -1) {
+		auth_request_log_error(request->auth_request, "ldap",
+				       "ldap_search() failed (filter %s): %s",
+				       srequest->filter, ldap_get_error(conn));
+		if (ldap_handle_error(conn) < 0) {
+			/* broken request, remove it */
+			return 0;
+		}
+		return -1;
+	}
+	return 1;
+}
+
+static bool db_ldap_request_queue_next(struct ldap_connection *conn)
+{
+	struct ldap_request *const *requestp, *request;
+	unsigned int queue_size = queue_count(conn->request_queue);
+	int ret = -1;
+
+	if (conn->pending_count == queue_size) {
+		/* no non-pending requests */
+		return FALSE;
+	}
+	if (queue_size > DB_LDAP_MAX_PENDING_REQUESTS) {
+		/* wait until server has replied to some requests */
+		return FALSE;
+	}
+
+	if (db_ldap_connect(conn) < 0)
+		return FALSE;
+
+	requestp = array_idx(&conn->request_array,
+			     queue_idx(conn->request_queue,
+				       conn->pending_count));
+	request = *requestp;
+
+	if (conn->pending_count > 0 &&
+	    request->type == LDAP_REQUEST_TYPE_BIND) {
+		/* we can't do binds until all existing requests are finished */
+		return FALSE;
+	}
+
+	switch (conn->conn_state) {
+	case LDAP_CONN_STATE_DISCONNECTED:
+	case LDAP_CONN_STATE_BINDING:
+		/* wait until we're in bound state */
+		return FALSE;
+	case LDAP_CONN_STATE_BOUND_AUTH:
+		if (request->type == LDAP_REQUEST_TYPE_BIND)
+			break;
+
+		/* bind to default dn first */
+		i_assert(conn->pending_count == 0);
+		(void)db_ldap_bind(conn);
+		return FALSE;
+	case LDAP_CONN_STATE_BOUND_DEFAULT:
+		/* we can do anything in this state */
 		break;
 	}
-}
-
-void db_ldap_search(struct ldap_connection *conn, struct ldap_request *request,
-		    int scope)
-{
-	int try, msgid = -1;
-
-	if (db_ldap_connect(conn) < 0) {
+
+	switch (request->type) {
+	case LDAP_REQUEST_TYPE_BIND:
+		ret = db_ldap_request_bind(conn, request);
+		break;
+	case LDAP_REQUEST_TYPE_SEARCH:
+		ret = db_ldap_request_search(conn, request);
+		break;
+	}
+
+	if (ret > 0) {
+		/* success */
+		i_assert(request->msgid != -1);
+		conn->pending_count++;
+		return TRUE;
+	} else if (ret < 0) {
+		/* disconnected */
+		return FALSE;
+	} else {
+		/* broken request, remove from queue */
+		queue_delete_tail(conn->request_queue);
+		request->callback(conn, request, NULL);
+		return TRUE;
+	}
+}
+
+void db_ldap_request(struct ldap_connection *conn,
+		     struct ldap_request *request)
+{
+	request->msgid = -1;
+	request->create_time = ioloop_time;
+
+	if (conn->request_queue->full &&
+	    queue_count(conn->request_queue) >= DB_LDAP_MAX_QUEUE_SIZE) {
+		/* Queue is full already, fail this request */
+		auth_request_log_error(request->auth_request, "ldap",
+				       "Request queue is full");
 		request->callback(conn, request, NULL);
 		return;
 	}
 
-	for (try = 0; conn->connected && !conn->binding && try < 2; try++) {
-		if (conn->last_auth_bind) {
-			/* switch back to the default dn before doing the
-			   search request. */
-			if (db_ldap_bind(conn) < 0) {
-				request->callback(conn, request, NULL);
-				return;
-			}
-			break;
-		}
-
-		msgid = ldap_search(conn->ld, request->base, scope,
-				    request->filter, request->attributes, 0);
-		if (msgid != -1)
-			break;
-
-		i_error("LDAP: ldap_search() failed (filter %s): %s",
-			request->filter, ldap_get_error(conn));
-		ldap_handle_error(conn);
-	}
-
-	if (msgid != -1)
-		hash_insert(conn->requests, POINTER_CAST(msgid), request);
-	else
-		db_ldap_add_delayed_request(conn, request);
-	conn->last_request_stamp = ioloop_time;
-}
-
-static void ldap_conn_retry_requests(struct ldap_connection *conn)
-{
-	struct hash_table *old_requests;


More information about the dovecot-cvs mailing list