Sophie

Sophie

distrib > Scientific%20Linux > 5x > x86_64 > by-pkgid > 927b2e27eb4d289c88ad87f4a2fdfa8f > files > 14

dovecot-1.0.7-9.el5_11.4.src.rpm

diff -up dovecot-1.0.7/src/auth/db-ldap.c.original dovecot-1.0.7/src/auth/db-ldap.c
--- dovecot-1.0.7/src/auth/db-ldap.c.original	2012-12-20 12:22:59.484032507 +0900
+++ dovecot-1.0.7/src/auth/db-ldap.c	2012-12-20 12:24:21.158032638 +0900
@@ -6,7 +6,9 @@
 
 #include "network.h"
 #include "ioloop.h"
+#include "array.h"
 #include "hash.h"
+#include "queue.h"
 #include "str.h"
 #include "settings.h"
 #include "userdb.h"
@@ -41,6 +43,13 @@
 #  define LDAP_OPT_SUCCESS LDAP_SUCCESS
 #endif
 
+struct db_ldap_sasl_bind_context {
+	const char *authcid;
+	const char *passwd;
+	const char *realm;
+	const char *authzid;
+};
+
 #define DEF(type, name) \
 	{ type, #name, offsetof(struct ldap_settings, name) }
 
@@ -99,7 +108,7 @@ struct ldap_settings default_ldap_settin
 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)
 {
@@ -146,49 +155,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);
-	}
+	db_ldap_conn_close(conn);
+	if (db_ldap_connect(conn) < 0)
+		db_ldap_conn_close(conn);
 }
 
-static void ldap_handle_error(struct ldap_connection *conn)
+static int ldap_handle_error(struct ldap_connection *conn)
 {
 	int err = ldap_get_errno(conn);
 
@@ -210,7 +184,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:
@@ -223,132 +197,286 @@ static void ldap_handle_error(struct lda
 	default:
 		/* connection problems */
 		ldap_conn_reconnect(conn);
-		break;
+		return 0;
 	}
 }
 
-void db_ldap_search(struct ldap_connection *conn, struct ldap_request *request,
-		    int scope)
+static int db_ldap_request_bind(struct ldap_connection *conn,
+				struct ldap_request *request)
 {
-	int try, msgid = -1;
+	struct ldap_request_bind *brequest =
+		(struct ldap_request_bind *)request;
 
-	if (db_ldap_connect(conn) < 0) {
-		request->callback(conn, request, NULL);
-		return;
+	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;
+}
 
-	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;
+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;
 
-		msgid = ldap_search(conn->ld, request->base, scope,
-				    request->filter, request->attributes, 0);
-		if (msgid != -1)
+	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;
 
-		i_error("LDAP: ldap_search() failed (filter %s): %s",
-			request->filter, ldap_get_error(conn));
-		ldap_handle_error(conn);
+		/* 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;
 	}
 
-	if (msgid != -1)
-		hash_insert(conn->requests, POINTER_CAST(msgid), request);
-	else
-		db_ldap_add_delayed_request(conn, request);
+	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;
+	}
 }
 
-static void ldap_conn_retry_requests(struct ldap_connection *conn)
+void db_ldap_request(struct ldap_connection *conn,
+		     struct ldap_request *request)
 {
-	struct hash_table *old_requests;
-        struct hash_iterate_context *iter;
-	struct ldap_request *request, **p, *next;
-	void *key, *value;
-	bool have_hash_binds = FALSE;
-
-	i_assert(conn->connected);
+	request->msgid = -1;
+	request->create_time = ioloop_time;
 
-	if (hash_size(conn->requests) == 0 &&
-	    conn->delayed_requests_head == NULL)
+	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;
+	}
 
-	old_requests = conn->requests;
-	conn->requests = hash_create(default_pool, conn->pool, 0, NULL, NULL);
+	queue_append(conn->request_queue, &request);
+	(void)db_ldap_request_queue_next(conn);
+}
 
-	conn->retrying = TRUE;
-	/* first retry all the search requests */
-	iter = hash_iterate_init(old_requests);
-	while (hash_iterate(iter, &key, &value)) {
-		request = value;
-
-		if (request->filter == NULL) {
-			/* bind request */
-			have_hash_binds = TRUE;
-		} else {
-			i_assert(conn->connected);
-			db_ldap_search(conn, request, conn->set.ldap_scope);
-		}
+static int db_ldap_connect_finish(struct ldap_connection *conn, int ret)
+{
+	if (ret == LDAP_SERVER_DOWN) {
+		i_error("LDAP: Can't connect to server: %s",
+			conn->set.uris != NULL ?
+			conn->set.uris : conn->set.hosts);
+		return -1;
+	}
+	if (ret != LDAP_SUCCESS) {
+		i_error("LDAP: binding failed (dn %s): %s",
+			conn->set.dn == NULL ? "(none)" : conn->set.dn,
+			ldap_get_error(conn));
+		return -1;
 	}
-	hash_iterate_deinit(iter);
 
-	/* then delayed search requests */
-	p = &conn->delayed_requests_head;
-	while (*p != NULL) {
-		request = *p;
+	conn->conn_state = LDAP_CONN_STATE_BOUND_DEFAULT;
+	while (db_ldap_request_queue_next(conn))
+		;
+	return 0;
+}
 
-		if (request->filter != NULL) {
-			*p = request->next;
+static void db_ldap_default_bind_finished(struct ldap_connection *conn,
+					  LDAPMessage *res)
+{
+	int ret;
 
-			i_assert(conn->connected);
-			db_ldap_search(conn, request, conn->set.ldap_scope);
-		} else {
-			p = &(*p)->next;
-		}
+	i_assert(conn->pending_count == 0);
+	conn->default_bind_msgid = -1;
+
+	ret = ldap_result2error(conn->ld, res, FALSE);
+	if (db_ldap_connect_finish(conn, ret) < 0) {
+		/* lost connection, close it */
+		db_ldap_conn_close(conn);
 	}
+}
+
+static void db_ldap_abort_requests(struct ldap_connection *conn,
+				   unsigned int max_count,
+				   unsigned int timeout_secs)
+{
+	struct ldap_request *const *requestp, *request;
+	time_t diff;
+
+	while (queue_count(conn->request_queue) > 0 && max_count > 0) {
+		requestp = array_idx(&conn->request_array,
+				     queue_idx(conn->request_queue, 0));
+		request = *requestp;
+
+		diff = ioloop_time - request->create_time;
+		if (diff < (time_t)timeout_secs)
+			break;
 
-	if (have_hash_binds && conn->set.auth_bind) {
-		/* next retry all the bind requests. without auth binds the
-		   only bind request can be the initial connection binding,
-		   which we don't care to retry. */
-		iter = hash_iterate_init(old_requests);
-		while (hash_iterate(iter, &key, &value)) {
-			request = value;
+		/* timed out, abort */
+		queue_delete_tail(conn->request_queue);
 
-			if (request->filter == NULL)
-				request->callback(conn, request, NULL);
+		if (request->msgid != -1) {
+			i_assert(conn->pending_count > 0);
+			conn->pending_count--;
 		}
-		hash_iterate_deinit(iter);
+		request->callback(conn, request, NULL);
+		max_count--;
 	}
-	if (conn->delayed_requests_head != NULL && conn->set.auth_bind) {
-		request = conn->delayed_requests_head;
-		for (; request != NULL; request = next) {
-			next = request->next;
+}
 
-			i_assert(request->filter == NULL);
-			request->callback(conn, request, NULL);
+static void
+db_ldap_handle_result(struct ldap_connection *conn, LDAPMessage *res)
+{
+	struct ldap_request *const *requests, *request = NULL;
+	unsigned int i, count;
+	int msgid, ret;
+
+	msgid = ldap_msgid(res);
+	if (msgid == conn->default_bind_msgid) {
+		db_ldap_default_bind_finished(conn, res);
+		return;
+	}
+
+	count = queue_count(conn->request_queue);
+	requests = count == 0 ? NULL : array_idx(&conn->request_array, 0);
+	for (i = 0; i < count; i++) {
+		request = requests[queue_idx(conn->request_queue, i)];
+		if (request->msgid == msgid)
+			break;
+		if (request->msgid == -1) {
+			request = NULL;
+			break;
 		}
-		conn->delayed_requests_head = NULL;
 	}
-	hash_destroy(old_requests);
+	if (request == NULL) {
+		i_error("LDAP: Reply with unknown msgid %d", msgid);
+		return;
+	}
 
-	i_assert(conn->delayed_requests_head == NULL);
-	conn->delayed_requests_tail = NULL;
-	conn->retrying = FALSE;
+	if (request->type == LDAP_REQUEST_TYPE_BIND) {
+		i_assert(conn->conn_state == LDAP_CONN_STATE_BINDING);
+		i_assert(conn->pending_count == 1);
+		conn->conn_state = LDAP_CONN_STATE_BOUND_AUTH;
+	}
+	i_assert(conn->pending_count > 0);
+	conn->pending_count--;
+	queue_delete(conn->request_queue, i);
+	
+	ret = ldap_result2error(conn->ld, res, 0);
+	if (ret != LDAP_SUCCESS && request->type == LDAP_REQUEST_TYPE_SEARCH) {
+		/* handle search failures here */
+		struct ldap_request_search *srequest =
+			(struct ldap_request_search *)request;
+
+		auth_request_log_error(request->auth_request, "ldap",
+				       "ldap_search(%s) failed: %s",
+				       srequest->filter, ldap_err2string(ret));
+		res = NULL;
+	}
+
+	T_FRAME(
+		request->callback(conn, request, res);
+	);
+
+	if (i > 0) {
+		/* see if there are timed out requests */
+		db_ldap_abort_requests(conn, i,
+				       DB_LDAP_REQUEST_LOST_TIMEOUT_SECS);
+	}
 }
 
 static void ldap_input(void *context)
 {
 	struct ldap_connection *conn = context;
-        struct ldap_request *request;
 	struct timeval timeout;
 	LDAPMessage *res;
-	int ret, msgid;
+	int ret;
 
 	for (;;) {
 		if (conn->ld == NULL)
@@ -366,25 +494,27 @@ static void ldap_input(void *context)
 		if (ret <= 0)
 			break;
 
-		msgid = ldap_msgid(res);
-		request = hash_lookup(conn->requests, POINTER_CAST(msgid));
-		if (request == NULL) {
-			i_error("LDAP: Reply with unknown msgid %d",
-				msgid);
-		} else {
-			hash_remove(conn->requests, POINTER_CAST(msgid));
-			request->callback(conn, request, res);
-		}
-
+		db_ldap_handle_result(conn, res);
 		ldap_msgfree(res);
 	}
+	conn->last_reply_stamp = ioloop_time;
 
-	if (ret < 0) {
+	if (ret == 0) {
+		/* send more requests */
+		while (db_ldap_request_queue_next(conn))
+			;
+	} else if (ldap_get_errno(conn) != LDAP_SERVER_DOWN) {
 		i_error("LDAP: ldap_result() failed: %s", ldap_get_error(conn));
 		ldap_conn_reconnect(conn);
+	} else if (queue_count(conn->request_queue) > 0 ||
+		   ioloop_time - conn->last_reply_stamp <
+						DB_LDAP_IDLE_RECONNECT_SECS) {
+		i_error("LDAP: Connection lost to LDAP server, reconnecting");
+		ldap_conn_reconnect(conn);
 	} else {
-		if (!conn->binding)
-			db_ldap_handle_next_delayed_request(conn);
+		/* server probably disconnected an idle connection. don't
+		   reconnect until the next request comes. */
+		db_ldap_conn_close(conn);
 	}
 }
 
@@ -393,7 +523,7 @@ static int
 sasl_interact(LDAP *ld __attr_unused__, unsigned flags __attr_unused__,
 	      void *defaults, void *interact)
 {
-	struct ldap_sasl_bind_context *context = defaults;
+	struct db_ldap_sasl_bind_context *context = defaults;
 	sasl_interact_t *in;
 	const char *str;
 
@@ -425,81 +555,23 @@ sasl_interact(LDAP *ld __attr_unused__, 
 }
 #endif
 
-static int db_ldap_connect_finish(struct ldap_connection *conn, int ret)
-{
-	if (ret == LDAP_SERVER_DOWN) {
-		i_error("LDAP: Can't connect to server: %s",
-			conn->set.uris != NULL ?
-			conn->set.uris : conn->set.hosts);
-		return -1;
-	}
-	if (ret != LDAP_SUCCESS) {
-		i_error("LDAP: binding failed (dn %s): %s",
-			conn->set.dn == NULL ? "(none)" : conn->set.dn,
-			ldap_get_error(conn));
-		return -1;
-	}
-
-	if (!conn->connected) {
-		conn->connected = TRUE;
-
-		/* in case there are requests waiting, retry them */
-		ldap_conn_retry_requests(conn);
-	}
-	return 0;
-}
-
-static void db_ldap_bind_callback(struct ldap_connection *conn,
-				  struct ldap_request *ldap_request,
-				  LDAPMessage *res)
-{
-	int ret;
-
-	conn->binding = FALSE;
-	conn->connecting = FALSE;
-	i_free(ldap_request);
-
-	if (res == NULL) {
-		/* aborted */
-		return;
-	}
-
-	ret = ldap_result2error(conn->ld, res, FALSE);
-	if (db_ldap_connect_finish(conn, ret) < 0) {
-		/* lost connection, close it */
-		ldap_conn_close(conn, TRUE);
-	}
-}
-
 static int db_ldap_bind(struct ldap_connection *conn)
 {
-	struct ldap_request *ldap_request;
 	int msgid;
 
-	i_assert(!conn->binding);
-
-	ldap_request = i_new(struct ldap_request, 1);
-	ldap_request->callback = db_ldap_bind_callback;
-	ldap_request->context = conn;
-
 	msgid = ldap_bind(conn->ld, conn->set.dn, conn->set.dnpass,
 			  LDAP_AUTH_SIMPLE);
 	if (msgid == -1) {
+		i_assert(ldap_get_errno(conn) != LDAP_SUCCESS);
 		if (db_ldap_connect_finish(conn, ldap_get_errno(conn)) < 0) {
 			/* lost connection, close it */
-			ldap_conn_close(conn, TRUE);
+			db_ldap_conn_close(conn);
 		}
-		i_free(ldap_request);
 		return -1;
 	}
 
-	conn->connecting = TRUE;
-	conn->binding = TRUE;
-	hash_insert(conn->requests, POINTER_CAST(msgid), ldap_request);
-
-	/* we're binding back to the original DN, not doing an
-	   authentication bind */
-	conn->last_auth_bind = FALSE;
+	conn->conn_state = LDAP_CONN_STATE_BINDING;
+	conn->default_bind_msgid = msgid;
 	return 0;
 }
 
@@ -522,10 +594,10 @@ int db_ldap_connect(struct ldap_connecti
 	unsigned int ldap_version;
 	int ret;
 
-	if (conn->connected || conn->connecting)
+	if (conn->conn_state != LDAP_CONN_STATE_DISCONNECTED)
 		return 0;
-	i_assert(!conn->binding);
 
+	i_assert(conn->pending_count == 0);
 	if (conn->ld == NULL) {
 		if (conn->set.uris != NULL) {
 #ifdef LDAP_HAVE_INITIALIZE
@@ -578,7 +650,7 @@ int db_ldap_connect(struct ldap_connecti
 
 	if (conn->set.sasl_bind) {
 #ifdef HAVE_LDAP_SASL
-		struct ldap_sasl_bind_context context;
+		struct db_ldap_sasl_bind_context context;
 
 		memset(&context, 0, sizeof(context));
 		context.authcid = conn->set.dn;
@@ -594,49 +666,50 @@ int db_ldap_connect(struct ldap_connecti
 						   sasl_interact, &context);
 		if (db_ldap_connect_finish(conn, ret) < 0)
 			return -1;
-		db_ldap_get_fd(conn);
 #else
 		i_fatal("LDAP: sasl_bind=yes but no SASL support compiled in");
 #endif
+		conn->conn_state = LDAP_CONN_STATE_BOUND_DEFAULT;
 	} else {
 		if (db_ldap_bind(conn) < 0)
 			return -1;
-		db_ldap_get_fd(conn);
 	}
 
+	db_ldap_get_fd(conn);
 	conn->io = io_add(conn->fd, IO_READ, ldap_input, conn);
 	return 0;
 }
 
-static void ldap_conn_close(struct ldap_connection *conn, bool flush_requests)
+static void db_ldap_disconnect_timeout(struct ldap_connection *conn)
 {
-	struct hash_iterate_context *iter;
-	struct ldap_request *request, *next;
-	void *key, *value;
+	db_ldap_abort_requests(conn, -1U,
+				DB_LDAP_REQUEST_DISCONNECT_TIMEOUT_SECS);
+
+	if (queue_count(conn->request_queue) == 0) {
+		/* no requests left, remove this timeout handler */
+		timeout_remove(&conn->to);
+	}
+}
 
-	if (flush_requests) {
-		iter = hash_iterate_init(conn->requests);
-		while (hash_iterate(iter, &key, &value)) {
-			request = value;
+static void db_ldap_conn_close(struct ldap_connection *conn)
+{
+	struct ldap_request *const *requests, *request;
+	unsigned int i;
 
-			request->callback(conn, request, NULL);
-		}
-		hash_iterate_deinit(iter);
-		hash_clear(conn->requests, FALSE);
+	conn->conn_state = LDAP_CONN_STATE_DISCONNECTED;
+	conn->default_bind_msgid = -1;
 
-		request = conn->delayed_requests_head;
-		for (; request != NULL; request = next) {
-			next = request->next;
+	if (conn->pending_count != 0) {
+		requests = array_idx(&conn->request_array, 0);
+		for (i = 0; i < conn->pending_count; i++) {
+			request = requests[queue_idx(conn->request_queue, i)];
 
-			request->callback(conn, request, NULL);
+			i_assert(request->msgid != -1);
+			request->msgid = -1;
 		}
-		conn->delayed_requests_head = NULL;
-		conn->delayed_requests_tail = NULL;
+		conn->pending_count = 0;
 	}
 
-	conn->connected = FALSE;
-	conn->binding = FALSE;
-
 	if (conn->io != NULL)
 		io_remove(&conn->io);
 
@@ -645,6 +718,14 @@ static void ldap_conn_close(struct ldap_
 		conn->ld = NULL;
 	}
 	conn->fd = -1;
+
+	if (queue_count(conn->request_queue) == 0) {
+		if (conn->to != NULL)
+			timeout_remove(&conn->to);
+	} else if (conn->to == NULL) {
+		conn->to = timeout_add(DB_LDAP_REQUEST_DISCONNECT_TIMEOUT_SECS *
+				       1000/2, db_ldap_disconnect_timeout, conn);
+	}
 }
 
 void db_ldap_set_attrs(struct ldap_connection *conn, const char *attrlist,
@@ -758,10 +839,10 @@ struct ldap_connection *db_ldap_init(con
 	pool = pool_alloconly_create("ldap_connection", 1024);
 	conn = p_new(pool, struct ldap_connection, 1);
 	conn->pool = pool;
-
 	conn->refcount = 1;
-	conn->requests = hash_create(default_pool, pool, 0, NULL, NULL);
 
+	conn->conn_state = LDAP_CONN_STATE_DISCONNECTED;
+	conn->default_bind_msgid = -1;
 	conn->fd = -1;
 	conn->config_path = p_strdup(pool, config_path);
 	conn->set = default_ldap_settings;
@@ -805,6 +886,9 @@ struct ldap_connection *db_ldap_init(con
 		}
 	}
 
+	ARRAY_CREATE(&conn->request_array, default_pool, struct ldap_request *, DB_LDAP_MAX_QUEUE_SIZE);
+	conn->request_queue = queue_init(&conn->request_array.arr);
+
 	conn->next = ldap_connections;
         ldap_connections = conn;
 	return conn;
@@ -827,9 +911,14 @@ void db_ldap_unref(struct ldap_connectio
 		}
 	}
 
-	ldap_conn_close(conn, TRUE);
+	db_ldap_abort_requests(conn, -1U, 0);
+	i_assert(conn->pending_count == 0);
+	db_ldap_conn_close(conn);
+	i_assert(conn->to == NULL);
+
+	array_free(&conn->request_array);
+	queue_deinit(&conn->request_queue);
 
-	hash_destroy(conn->requests);
 	if (conn->pass_attr_map != NULL)
 		hash_destroy(conn->pass_attr_map);
 	if (conn->user_attr_map != NULL)
diff -up dovecot-1.0.7/src/auth/db-ldap.h.original dovecot-1.0.7/src/auth/db-ldap.h
--- dovecot-1.0.7/src/auth/db-ldap.h.original	2012-12-20 12:23:07.822032545 +0900
+++ dovecot-1.0.7/src/auth/db-ldap.h	2012-12-20 12:23:59.276938779 +0900
@@ -5,6 +5,19 @@
    This define enables them until the code here can be refactored */
 #define LDAP_DEPRECATED 1
 
+/* Maximum number of requests in queue. After this new requests are dropped. */
+#define DB_LDAP_MAX_QUEUE_SIZE 1024
+/* Maximum number of pending requests before delaying new requests. */
+#define DB_LDAP_MAX_PENDING_REQUESTS 128
+/* If LDAP connection is down, fail requests after waiting for this long. */
+#define DB_LDAP_REQUEST_DISCONNECT_TIMEOUT_SECS 4
+/* If request is still in queue after this many seconds and other requests
+ *    have been replied, assume the request was lost and abort it. */
+#define DB_LDAP_REQUEST_LOST_TIMEOUT_SECS 60
+/* If server disconnects us, don't reconnect if no requests have been sent
+ *    for this many seconds. */
+#define DB_LDAP_IDLE_RECONNECT_SECS 60
+
 #include <ldap.h>
 
 struct auth_request;
@@ -49,6 +62,48 @@ struct ldap_settings {
 	gid_t gid;
 };
 
+enum ldap_request_type {
+	LDAP_REQUEST_TYPE_SEARCH,
+	LDAP_REQUEST_TYPE_BIND
+};
+
+struct ldap_request {
+	enum ldap_request_type type;
+
+	/* msgid for sent requests, -1 if not sent */
+	int msgid;
+	/* timestamp when request was created */
+	time_t create_time;
+
+	db_search_callback_t *callback;
+	struct auth_request *auth_request;
+};
+
+struct ldap_request_search {
+	struct ldap_request request;
+
+	const char *base;
+	const char *filter;
+	char **attributes; /* points to pass_attr_names / user_attr_names */
+};
+
+struct ldap_request_bind {
+	struct ldap_request request;
+
+	const char *dn;
+};
+
+enum ldap_connection_state {
+	/* Not connected */
+	LDAP_CONN_STATE_DISCONNECTED,
+	/* Binding - either to default dn or doing auth bind */
+	LDAP_CONN_STATE_BINDING,
+	/* Bound to auth dn */
+	LDAP_CONN_STATE_BOUND_AUTH,
+	/* Bound to default dn */
+	LDAP_CONN_STATE_BOUND_DEFAULT
+};
+
 struct ldap_connection {
 	struct ldap_connection *next;
 
@@ -59,45 +114,30 @@ struct ldap_connection {
         struct ldap_settings set;
 
 	LDAP *ld;
-	int fd; /* only set when connected/connecting */
+	enum ldap_connection_state conn_state;
+	int default_bind_msgid;
+
+	int fd;
 	struct io *io;
+	struct timeout *to;
+
+	/* Request queue contains sent requests at tail (msgid != -1) and
+ 	   queued requests at head (msgid == -1). */
+	struct queue *request_queue;
+	ARRAY_DEFINE(request_array, struct ldap_request *);
+	/* Number of messages in queue with msgid != -1 */
+	unsigned int pending_count;
 
-	struct hash_table *requests;
-	struct ldap_request *delayed_requests_head, *delayed_requests_tail;
+	/* Timestamp when we last received a reply */
+	time_t last_reply_stamp;
 
 	char **pass_attr_names, **user_attr_names;
 	struct hash_table *pass_attr_map, *user_attr_map;
-
-	unsigned int connected:1;
-	unsigned int connecting:1;
-	unsigned int binding:1;
-	unsigned int retrying:1; /* just reconnected, resending requests */
-	unsigned int last_auth_bind:1;
-};
-
-struct ldap_request {
-	struct ldap_request *next; /* in conn->delayed_requests */
-
-	db_search_callback_t *callback;
-	void *context;
-
-	/* for bind requests, base contains the DN and filter=NULL */
-	const char *base;
-	const char *filter;
-	char **attributes; /* points to pass_attr_names / user_attr_names */
-};
-
-struct ldap_sasl_bind_context {
-	const char *authcid;
-	const char *passwd;
-	const char *realm;
-	const char *authzid;
 };
 
-void db_ldap_add_delayed_request(struct ldap_connection *conn,
-				 struct ldap_request *request);
-void db_ldap_search(struct ldap_connection *conn, struct ldap_request *request,
-		    int scope);
+/* Send/queue request */
+void db_ldap_request(struct ldap_connection *conn,
+		     struct ldap_request *request);
 
 void db_ldap_set_attrs(struct ldap_connection *conn, const char *attrlist,
 		       char ***attr_names_r, struct hash_table *attr_map,
diff -up dovecot-1.0.7/src/auth/passdb-ldap.c.original dovecot-1.0.7/src/auth/passdb-ldap.c
--- dovecot-1.0.7/src/auth/passdb-ldap.c.original	2012-12-20 12:23:18.139032645 +0900
+++ dovecot-1.0.7/src/auth/passdb-ldap.c	2012-12-20 12:24:21.158032638 +0900
@@ -4,6 +4,7 @@
 
 #ifdef PASSDB_LDAP
 
+#include "ioloop.h"
 #include "hash.h"
 #include "str.h"
 #include "var-expand.h"
@@ -26,7 +27,11 @@ struct ldap_passdb_module {
 };
 
 struct passdb_ldap_request {
-	struct ldap_request request;
+	union {
+		struct ldap_request ldap;
+		struct ldap_request_search search;
+		struct ldap_request_bind bind;
+	} request;
 
 	union {
 		verify_plain_callback_t *verify_plain;
@@ -147,30 +152,24 @@ handle_request_get_entry(struct ldap_con
 {
 	enum passdb_result passdb_result;
 	LDAPMessage *entry;
-	int ret;
 
 	passdb_result = PASSDB_RESULT_INTERNAL_FAILURE;
 
 	if (res != NULL) {
-		/* LDAP query returned something */
-		ret = ldap_result2error(conn->ld, res, 0);
-		if (ret != LDAP_SUCCESS) {
-			auth_request_log_error(auth_request, "ldap",
-					       "ldap_search(%s) failed: %s",
-					       request->request.filter,
-					       ldap_err2string(ret));
+		/* LDAP search was successful */
+		entry = ldap_first_entry(conn->ld, res);
+		if (entry == NULL) {
+			passdb_result = PASSDB_RESULT_USER_UNKNOWN;
+			auth_request_log_info(auth_request, "ldap",
+					      "unknown user");
 		} else {
-			/* get the reply */
-			entry = ldap_first_entry(conn->ld, res);
-			if (entry != NULL) {
+			if (ldap_next_entry(conn->ld, entry) == NULL) {
 				/* success */
 				return entry;
 			}
 
-			/* no entries returned */
-			auth_request_log_info(auth_request, "ldap",
-					      "unknown user");
-			passdb_result = PASSDB_RESULT_USER_UNKNOWN;
+			auth_request_log_error(auth_request, "ldap",
+				"Multiple replies found for user");
 		}
 	}
 
@@ -184,12 +183,13 @@ handle_request_get_entry(struct ldap_con
 	return NULL;
 }
 
-static void handle_request(struct ldap_connection *conn,
-			   struct ldap_request *request, LDAPMessage *res)
+static void
+ldap_lookup_pass_callback(struct ldap_connection *conn,
+			  struct ldap_request *request, LDAPMessage *res)
 {
 	struct passdb_ldap_request *ldap_request =
 		(struct passdb_ldap_request *)request;
-        struct auth_request *auth_request = request->context;
+	struct auth_request *auth_request = request->auth_request;
 	enum passdb_result passdb_result;
 	LDAPMessage *entry;
 	const char *password, *scheme;
@@ -242,72 +242,16 @@ static void handle_request(struct ldap_c
 	auth_request_unref(&auth_request);
 }
 
-static void authbind_start(struct ldap_connection *conn,
-			   struct ldap_request *ldap_request)
-{
-	struct passdb_ldap_request *passdb_ldap_request =
-		(struct passdb_ldap_request *)ldap_request;
-	struct auth_request *auth_request = ldap_request->context;
-	int msgid;
-
-	i_assert(ldap_request->base != NULL);
-
-	if (*auth_request->mech_password == '\0') {
-		/* Assume that empty password fails. This is especially
-		   important with Windows 2003 AD, which always returns success
-		   with empty passwords. */
-		auth_request_log_info(auth_request, "ldap",
-				      "Login attempt with empty password");
-		passdb_ldap_request->callback.
-			verify_plain(PASSDB_RESULT_PASSWORD_MISMATCH,
-				     auth_request);
-		return;
-	}
-
-	if (conn->connected && hash_size(conn->requests) == 0) {
-		/* switch back to the default dn before doing the next search
-		   request */
-		conn->last_auth_bind = TRUE;
-		i_assert(!conn->binding);
-
-		/* the DN is kept in base variable, a bit ugly.. */
-		msgid = ldap_bind(conn->ld, ldap_request->base,
-				  auth_request->mech_password,
-				  LDAP_AUTH_SIMPLE);
-		if (msgid == -1) {
-			auth_request_log_error(auth_request, "ldap",
-				"ldap_bind(%s) failed: %s",
-				ldap_request->base, ldap_get_error(conn));
-			passdb_ldap_request->callback.
-				verify_plain(PASSDB_RESULT_INTERNAL_FAILURE,
-					     auth_request);
-			return;
-		}
-
-		conn->binding = TRUE;
-		hash_insert(conn->requests, POINTER_CAST(msgid), ldap_request);
-
-		auth_request_log_debug(auth_request, "ldap", "bind: dn=%s",
-				       ldap_request->base);
-	} else {
-		db_ldap_add_delayed_request(conn, ldap_request);
-	}
-
-	/* Bind started */
-	auth_request_ref(auth_request);
-}
-
 static void
-handle_request_authbind(struct ldap_connection *conn,
+ldap_auth_bind_callback(struct ldap_connection *conn,
 			struct ldap_request *ldap_request, LDAPMessage *res)
 {
 	struct passdb_ldap_request *passdb_ldap_request =
 		(struct passdb_ldap_request *)ldap_request;
-	struct auth_request *auth_request = ldap_request->context;
+	struct auth_request *auth_request = ldap_request->auth_request;
 	enum passdb_result passdb_result;
 	int ret;
 
-	conn->binding = FALSE;
 	passdb_result = PASSDB_RESULT_INTERNAL_FAILURE;
 
 	if (res != NULL) {
@@ -325,24 +269,42 @@ handle_request_authbind(struct ldap_conn
 		}
 	}
 
-	if (conn->retrying && res == NULL) {
-		/* reconnected, retry binding */
-		authbind_start(conn, ldap_request);
-	} else {
+	passdb_ldap_request->callback.
+		verify_plain(passdb_result, auth_request);
+        auth_request_unref(&auth_request);
+}
+
+static void ldap_auth_bind(struct ldap_connection *conn,
+			   struct ldap_request_bind *brequest)
+{
+	struct passdb_ldap_request *passdb_ldap_request =
+		(struct passdb_ldap_request *)brequest;
+	struct auth_request *auth_request = brequest->request.auth_request;
+
+	if (*auth_request->mech_password == '\0') {
+		/* Assume that empty password fails. This is especially
+ 		   important with Windows 2003 AD, which always returns success
+ 		   		   with empty passwords. */
+		auth_request_log_info(auth_request, "ldap",
+				      "Login attempt with empty password");
 		passdb_ldap_request->callback.
-			verify_plain(passdb_result, auth_request);
+			verify_plain(PASSDB_RESULT_PASSWORD_MISMATCH,
+				     auth_request);
+		return;
 	}
-        auth_request_unref(&auth_request);
+
+	brequest->request.callback = ldap_auth_bind_callback;
+	db_ldap_request(conn, &brequest->request);
 }
 
-static void
-handle_request_authbind_search(struct ldap_connection *conn,
-			       struct ldap_request *ldap_request,
-			       LDAPMessage *res)
+static void ldap_bind_lookup_dn_callback(struct ldap_connection *conn,
+					 struct ldap_request *ldap_request,
+					 LDAPMessage *res)
 {
 	struct passdb_ldap_request *passdb_ldap_request =
 		(struct passdb_ldap_request *)ldap_request;
-	struct auth_request *auth_request = ldap_request->context;
+	struct ldap_request_bind *brequest;
+	struct auth_request *auth_request = ldap_request->auth_request;
 	LDAPMessage *entry;
 	char *dn;
 
@@ -353,111 +315,109 @@ handle_request_authbind_search(struct ld
 
 	ldap_query_save_result(conn, entry, auth_request);
 
+	/* convert search request to bind request */
+	brequest = &passdb_ldap_request->request.bind;
+	memset(brequest, 0, sizeof(*brequest));
+	brequest->request.type = LDAP_REQUEST_TYPE_BIND;
+	brequest->request.auth_request = auth_request;
+
 	/* switch the handler to the authenticated bind handler */
 	dn = ldap_get_dn(conn->ld, entry);
-	ldap_request->base = p_strdup(auth_request->pool, dn);
+	brequest->dn = p_strdup(auth_request->pool, dn);
 	ldap_memfree(dn);
 
-	ldap_request->filter = NULL;
-	ldap_request->callback = handle_request_authbind;
-
-        authbind_start(conn, ldap_request);
-	auth_request_unref(&auth_request);
+	ldap_auth_bind(conn, brequest);
 }
 
 static void ldap_lookup_pass(struct auth_request *auth_request,
-			     struct ldap_request *ldap_request)
+			     struct passdb_ldap_request *request)
 {
 	struct passdb_module *_module = auth_request->passdb->passdb;
 	struct ldap_passdb_module *module =
 		(struct ldap_passdb_module *)_module;
 	struct ldap_connection *conn = module->conn;
-        const struct var_expand_table *vars;
+	struct ldap_request_search *srequest = &request->request.search;
+	const struct var_expand_table *vars;
 	const char **attr_names = (const char **)conn->pass_attr_names;
 	string_t *str;
 
+	srequest->request.type = LDAP_REQUEST_TYPE_SEARCH;
 	vars = auth_request_get_var_expand_table(auth_request, ldap_escape);
 
 	str = t_str_new(512);
 	var_expand(str, conn->set.base, vars);
-	ldap_request->base = p_strdup(auth_request->pool, str_c(str));
+	srequest->base = p_strdup(auth_request->pool, str_c(str));
 
 	str_truncate(str, 0);
 	var_expand(str, conn->set.pass_filter, vars);
-	ldap_request->filter = p_strdup(auth_request->pool, str_c(str));
-
-	auth_request_ref(auth_request);
-	ldap_request->callback = handle_request;
-	ldap_request->context = auth_request;
-	ldap_request->attributes = conn->pass_attr_names;
+	srequest->filter = p_strdup(auth_request->pool, str_c(str));
+	srequest->attributes = conn->pass_attr_names;
 
 	auth_request_log_debug(auth_request, "ldap", "pass search: "
 			       "base=%s scope=%s filter=%s fields=%s",
-			       ldap_request->base, conn->set.scope,
-			       ldap_request->filter,
-			       attr_names == NULL ? "(all)" :
+			       srequest->base, conn->set.scope,
+			       srequest->filter, attr_names == NULL ? "(all)" :
 			       t_strarray_join(attr_names, ","));
 
-	db_ldap_search(conn, ldap_request, conn->set.ldap_scope);
+	srequest->request.callback = ldap_lookup_pass_callback;
+	db_ldap_request(conn, &srequest->request);
 }
 
-static void
-ldap_verify_plain_auth_bind_userdn(struct auth_request *auth_request,
-				   struct ldap_request *ldap_request)
-{
-	struct passdb_module *_module = auth_request->passdb->passdb;
-	struct ldap_passdb_module *module =
-		(struct ldap_passdb_module *)_module;
-	struct ldap_connection *conn = module->conn;
-        const struct var_expand_table *vars;
-	string_t *dn;
-
-	vars = auth_request_get_var_expand_table(auth_request, ldap_escape);
-	dn = t_str_new(512);
-	var_expand(dn, conn->set.auth_bind_userdn, vars);
-
-	ldap_request->callback = handle_request_authbind;
-	ldap_request->context = auth_request;
-
-	ldap_request->base = p_strdup(auth_request->pool, str_c(dn));
-        authbind_start(conn, ldap_request);
-}
-
-static void
-ldap_verify_plain_authbind(struct auth_request *auth_request,
-			   struct ldap_request *ldap_request)
+static void ldap_bind_lookup_dn(struct auth_request *auth_request,
+				struct passdb_ldap_request *request)
 {
 	struct passdb_module *_module = auth_request->passdb->passdb;
 	struct ldap_passdb_module *module =
 		(struct ldap_passdb_module *)_module;
 	struct ldap_connection *conn = module->conn;
+	struct ldap_request_search *srequest = &request->request.search;
 	const struct var_expand_table *vars;
 	string_t *str;
 
+	srequest->request.type = LDAP_REQUEST_TYPE_SEARCH;
 	vars = auth_request_get_var_expand_table(auth_request, ldap_escape);
 
 	str = t_str_new(512);
 	var_expand(str, conn->set.base, vars);
-	ldap_request->base = p_strdup(auth_request->pool, str_c(str));
+	srequest->base = p_strdup(auth_request->pool, str_c(str));
 
 	str_truncate(str, 0);
 	var_expand(str, conn->set.pass_filter, vars);
-	ldap_request->filter = p_strdup(auth_request->pool, str_c(str));
+	srequest->filter = p_strdup(auth_request->pool, str_c(str));
 
 	/* we don't need the attributes to perform authentication, but they
-	   may contain some extra parameters. if a password is returned,
-	   it's just ignored. */
-	ldap_request->attributes = conn->pass_attr_names;
-
-	auth_request_ref(auth_request);
-	ldap_request->context = auth_request;
-	ldap_request->callback = handle_request_authbind_search;
+ * 	   may contain some extra parameters. if a password is returned,
+ * 	   	   it's just ignored. */
+	srequest->attributes = conn->pass_attr_names;
 
 	auth_request_log_debug(auth_request, "ldap",
 			       "bind search: base=%s filter=%s",
-			       ldap_request->base, ldap_request->filter);
+			       srequest->base, srequest->filter);
 
-        db_ldap_search(conn, ldap_request, LDAP_SCOPE_SUBTREE);
+	srequest->request.callback = ldap_bind_lookup_dn_callback;
+        db_ldap_request(conn, &srequest->request);
+}
+
+static void
+ldap_verify_plain_auth_bind_userdn(struct auth_request *auth_request,
+				   struct passdb_ldap_request *request)
+{
+	struct passdb_module *_module = auth_request->passdb->passdb;
+	struct ldap_passdb_module *module =
+		(struct ldap_passdb_module *)_module;
+	struct ldap_connection *conn = module->conn;
+	struct ldap_request_bind *brequest = &request->request.bind;
+	const struct var_expand_table *vars;
+	string_t *dn;
+
+	brequest->request.type = LDAP_REQUEST_TYPE_BIND;
+
+	vars = auth_request_get_var_expand_table(auth_request, ldap_escape);
+	dn = t_str_new(512);
+	var_expand(dn, conn->set.auth_bind_userdn, vars);
+
+	brequest->dn = p_strdup(auth_request->pool, str_c(dn));
+	ldap_auth_bind(conn, brequest);
 }
 
 static void
@@ -481,12 +441,15 @@ ldap_verify_plain(struct auth_request *r
 	ldap_request = p_new(request->pool, struct passdb_ldap_request, 1);
 	ldap_request->callback.verify_plain = callback;
 
-	if (conn->set.auth_bind_userdn != NULL)
-		ldap_verify_plain_auth_bind_userdn(request, &ldap_request->request);
-	else if (conn->set.auth_bind)
-		ldap_verify_plain_authbind(request, &ldap_request->request);
+	auth_request_ref(request);
+	ldap_request->request.ldap.auth_request = request;
+
+	if (!conn->set.auth_bind)
+		ldap_lookup_pass(request, ldap_request);
+	else if (conn->set.auth_bind_userdn == NULL)
+		ldap_bind_lookup_dn(request, ldap_request);
 	else
-		ldap_lookup_pass(request, &ldap_request->request);
+		ldap_verify_plain_auth_bind_userdn(request, ldap_request);
 }
 
 static void ldap_lookup_credentials(struct auth_request *request,
@@ -497,7 +460,10 @@ static void ldap_lookup_credentials(stru
 	ldap_request = p_new(request->pool, struct passdb_ldap_request, 1);
 	ldap_request->callback.lookup_credentials = callback;
 
-        ldap_lookup_pass(request, &ldap_request->request);
+	auth_request_ref(request);
+	ldap_request->request.ldap.auth_request = request;
+
+	ldap_lookup_pass(request, ldap_request);
 }
 
 static struct passdb_module *
@@ -512,8 +478,6 @@ passdb_ldap_preinit(struct auth_passdb *
 		hash_create(default_pool, conn->pool, 0, str_hash,
 			    (hash_cmp_callback_t *)strcmp);
 
-	if (conn->set.auth_bind_userdn != NULL)
-		conn->set.auth_bind = TRUE;
 	db_ldap_set_attrs(conn, conn->set.pass_attrs, &conn->pass_attr_names,
 			  conn->pass_attr_map, default_attr_map,
 			  conn->set.auth_bind ? "password" : NULL);
diff -up dovecot-1.0.7/src/auth/userdb-ldap.c.original dovecot-1.0.7/src/auth/userdb-ldap.c
--- dovecot-1.0.7/src/auth/userdb-ldap.c.original	2012-12-20 12:23:25.626032605 +0900
+++ dovecot-1.0.7/src/auth/userdb-ldap.c	2012-12-20 15:46:46.243907948 +0900
@@ -21,9 +21,9 @@ struct ldap_userdb_module {
 };
 
 struct userdb_ldap_request {
-	struct ldap_request request;
-        struct auth_request *auth_request;
-        userdb_callback_t *userdb_callback;
+	struct ldap_request_search request;
+	struct auth_request *auth_request;
+	userdb_callback_t *userdb_callback;
 };
 
 static const char *default_attr_map[] = {
@@ -132,8 +132,9 @@ ldap_query_get_result(struct ldap_connec
 	return reply;
 }
 
-static void handle_request(struct ldap_connection *conn,
-			   struct ldap_request *request, LDAPMessage *res)
+static void userdb_ldap_lookup_callback(struct ldap_connection *conn,
+					struct ldap_request *request,
+					LDAPMessage *res)
 {
 	struct userdb_ldap_request *urequest =
 		(struct userdb_ldap_request *) request;
@@ -141,33 +142,21 @@ static void handle_request(struct ldap_c
 	LDAPMessage *entry;
 	struct auth_stream_reply *reply = NULL;
 	enum userdb_result result = USERDB_RESULT_INTERNAL_FAILURE;
-	int ret;
 
 	if (res != NULL) {
-		ret = ldap_result2error(conn->ld, res, 0);
-		if (ret != LDAP_SUCCESS) {
-			auth_request_log_error(auth_request, "ldap",
-					       "ldap_search() failed: %s", ldap_err2string(ret));
-			urequest->userdb_callback(result, NULL, auth_request);
-			return;
-		}
-	}
-
-	entry = res == NULL ? NULL : ldap_first_entry(conn->ld, res);
-	if (entry == NULL) {
-		if (res != NULL) {
+		entry = ldap_first_entry(conn->ld, res);
+		if (entry == NULL) {
 			result = USERDB_RESULT_USER_UNKNOWN;
-			auth_request_log_error(auth_request, "ldap",
-					       "Authenticated user not found");
-		}
-	} else {
-		reply = ldap_query_get_result(conn, entry, auth_request);
-		if (ldap_next_entry(conn->ld, entry) == NULL)
-			result = USERDB_RESULT_OK;
-		else {
-			auth_request_log_error(auth_request, "ldap",
-				"Multiple replies found for user");
-			reply = NULL;
+			auth_request_log_info(auth_request, "ldap",
+					      "Unknown user");
+		} else {
+			reply = ldap_query_get_result(conn, entry, auth_request);
+			if (ldap_next_entry(conn->ld, entry) == NULL)
+				result = USERDB_RESULT_OK;
+			else {
+				auth_request_log_error(auth_request, "ldap",
+					"Multiple replies found for user");
+			}
 		}
 	}
 
@@ -189,7 +178,6 @@ static void userdb_ldap_lookup(struct au
 
 	auth_request_ref(auth_request);
 	request = p_new(auth_request->pool, struct userdb_ldap_request, 1);
-	request->request.callback = handle_request;
 	request->auth_request = auth_request;
 	request->userdb_callback = callback;
 
@@ -212,7 +200,8 @@ static void userdb_ldap_lookup(struct au
 			       attr_names == NULL ? "(all)" :
 			       t_strarray_join(attr_names, ","));
 
-	db_ldap_search(conn, &request->request, conn->set.ldap_scope);
+	request->request.request.callback = userdb_ldap_lookup_callback;
+	db_ldap_request(conn, &request->request.request);
 }
 
 static struct userdb_module *
diff -up dovecot-1.0.7/src/lib/Makefile.am.original dovecot-1.0.7/src/lib/Makefile.am
--- dovecot-1.0.7/src/lib/Makefile.am.original	2012-12-20 12:21:09.472032565 +0900
+++ dovecot-1.0.7/src/lib/Makefile.am	2012-12-20 12:22:14.677032752 +0900
@@ -64,6 +64,7 @@ liblib_a_SOURCES = \
 	primes.c \
 	printf-upper-bound.c \
 	process-title.c \
+	queue.c \
 	randgen.c \
 	read-full.c \
 	restrict-access.c \
@@ -137,6 +138,7 @@ headers = \
 	primes.h \
 	printf-upper-bound.h \
 	process-title.h \
+	queue.h \
 	randgen.h \
 	read-full.h \
 	restrict-access.h \
diff -up dovecot-1.0.7/src/lib/Makefile.in.original dovecot-1.0.7/src/lib/Makefile.in
--- dovecot-1.0.7/src/lib/Makefile.in.original	2012-12-20 12:21:15.437032470 +0900
+++ dovecot-1.0.7/src/lib/Makefile.in	2012-12-20 12:22:19.882032587 +0900
@@ -349,6 +349,7 @@ liblib_a_SOURCES = \
 	primes.c \
 	printf-upper-bound.c \
 	process-title.c \
+	queue.c \
 	randgen.c \
 	read-full.c \
 	restrict-access.c \
@@ -422,6 +423,7 @@ headers = \
 	primes.h \
 	printf-upper-bound.h \
 	process-title.h \
+	queue.h \
 	randgen.h \
 	read-full.h \
 	restrict-access.h \
diff -up dovecot-1.0.7/src/lib/array.h.original dovecot-1.0.7/src/lib/array.h
--- dovecot-1.0.7/src/lib/array.h.original	2012-12-20 12:26:31.671032595 +0900
+++ dovecot-1.0.7/src/lib/array.h	2012-12-20 15:49:13.180940053 +0900
@@ -236,6 +236,17 @@ _array_insert_space(struct array *array,
 	ARRAY_TYPE_CAST_MODIFIABLE(array) \
 		_array_insert_space(&(array)->arr, idx)
 
+static inline void
+array_copy(struct array *dest, unsigned int dest_idx,
+	   const struct array *src, unsigned int src_idx, unsigned int count)
+{
+	i_assert(dest->element_size == src->element_size);
+
+	buffer_copy(dest->buffer, dest_idx * dest->element_size,
+		    dest->buffer, src_idx * src->element_size,
+		    count * dest->element_size);
+}
+
 static inline unsigned int
 _array_count(const struct array *array)
 {
diff -up dovecot-1.0.7/src/lib/data-stack.c.original dovecot-1.0.7/src/lib/data-stack.c
--- dovecot-1.0.7/src/lib/data-stack.c.original	2012-12-20 12:21:21.838033271 +0900
+++ dovecot-1.0.7/src/lib/data-stack.c	2012-12-20 12:22:27.104032594 +0900
@@ -177,6 +177,13 @@ unsigned int t_pop(void)
         return --data_stack_frame;
 }
 
+void t_pop_check(unsigned int *id)
+{
+	if (unlikely(t_pop() != *id))
+		i_panic("Leaked t_pop() call");
+	*id = 0;
+}
+
 static struct stack_block *mem_block_alloc(size_t min_size)
 {
 	struct stack_block *block;
diff -up dovecot-1.0.7/src/lib/data-stack.h.original dovecot-1.0.7/src/lib/data-stack.h
--- dovecot-1.0.7/src/lib/data-stack.h.original	2012-12-20 12:21:28.059032699 +0900
+++ dovecot-1.0.7/src/lib/data-stack.h	2012-12-20 12:22:27.107033201 +0900
@@ -42,6 +42,22 @@ extern unsigned int data_stack_frame;
 */
 unsigned int t_push(void);
 unsigned int t_pop(void);
+/* Simplifies the if (t_pop() != x) check by comparing it internally and
+   panicking if it doesn't match. */
+void t_pop_check(unsigned int *id);
+
+/* Usage: T_FRAME_BEGIN { code } T_FRAME_END */
+#define T_FRAME_BEGIN \
+	STMT_START { unsigned int _data_stack_cur_id = t_push();
+#define T_FRAME_END \
+	t_pop_check(&_data_stack_cur_id); } STMT_END
+
+/* Usage: T_FRAME(code). This doesn't work if there are commas within the code
+   outside function parameters. */
+#define T_FRAME(_data_stack_code) \
+	T_FRAME_BEGIN { \
+		_data_stack_code; \
+	} T_FRAME_END
 
 /* WARNING: Be careful when using this functions, it's too easy to
    accidentally save the returned value somewhere permanently.
diff -up dovecot-1.0.7/src/lib/macros.h.original dovecot-1.0.7/src/lib/macros.h
--- dovecot-1.0.7/src/lib/macros.h.original	2012-12-20 12:21:36.962029701 +0900
+++ dovecot-1.0.7/src/lib/macros.h	2012-12-20 12:22:33.289032688 +0900
@@ -135,23 +135,31 @@
 #  define GNUC_PRETTY_FUNCTION ""
 #endif
 
+#ifdef __GNUC__
+#  define unlikely(expr) __builtin_expect(!!(expr), 0)
+#  define likely(expr) __builtin_expect(!!(expr), 1)
+#else
+#  define unlikely(expr)
+#  define likely(expr)
+#endif
+
 /* Provide macros for error handling. */
 #ifdef DISABLE_ASSERTS
 #  define i_assert(expr)
 #elif defined (__GNUC__) && !defined (__STRICT_ANSI__)
 
 #define i_assert(expr)			STMT_START{			\
-     if (!(expr))							\
+   if (unlikely(!(expr)))						\
        i_panic("file %s: line %d (%s): assertion failed: (%s)",		\
-		__FILE__,							\
-		__LINE__,							\
+		__FILE__,						\
+		__LINE__,						\
 		__PRETTY_FUNCTION__,					\
 		#expr);			}STMT_END
 
 #else /* !__GNUC__ */
 
 #define i_assert(expr)			STMT_START{		\
-     if (!(expr))						\
+   if (unlikely(!(expr)))					\
        i_panic("file %s: line %d: assertion failed: (%s)",	\
 	      __FILE__,						\
 	      __LINE__,						\
diff -up dovecot-1.0.7/src/lib/queue.c.original dovecot-1.0.7/src/lib/queue.c
--- dovecot-1.0.7/src/lib/queue.c.original	2012-12-20 12:21:47.313908055 +0900
+++ dovecot-1.0.7/src/lib/queue.c	2012-12-20 12:22:40.735032897 +0900
@@ -0,0 +1,123 @@
+/* Copyright (c) 2003-2007 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "array.h"
+#include "queue.h"
+
+struct queue *queue_init(struct array *array)
+{
+	struct queue *queue;
+
+	queue = i_new(struct queue, 1);
+	queue->arr = array;
+	queue->area_size =
+		buffer_get_size(queue->arr->buffer) / queue->arr->element_size;
+	i_assert(queue->area_size > 0);
+	return queue;
+}
+
+void queue_deinit(struct queue **_queue)
+{
+	struct queue *queue = *_queue;
+
+	*_queue = NULL;
+	i_free(queue);
+}
+
+static void queue_grow(struct queue *queue)
+{
+	unsigned int orig_area_size, count;
+
+	i_assert(queue->full && queue->head == queue->tail);
+
+	orig_area_size = queue->area_size;
+	(void)_array_append_space(queue->arr);
+	queue->area_size =
+		buffer_get_size(queue->arr->buffer) / queue->arr->element_size;
+	i_assert(orig_area_size < queue->area_size);
+
+	count = I_MIN(queue->area_size - orig_area_size, queue->head);
+	array_copy(queue->arr, orig_area_size, queue->arr, 0, count);
+	if (count < queue->area_size - orig_area_size)
+		queue->head = orig_area_size + count;
+	else {
+		array_copy(queue->arr, 0, queue->arr, count,
+			   queue->head - count);
+		queue->head -= count;
+	}
+
+	i_assert(queue->head != queue->tail);
+	queue->full = FALSE;
+}
+
+void queue_append(struct queue *queue, const void *data)
+{
+	if (queue->full) {
+		queue_grow(queue);
+		i_assert(!queue->full);
+	}
+
+	_array_idx_set(queue->arr, queue->head, data);
+	queue->head = (queue->head + 1) % queue->area_size;
+	queue->full = queue->head == queue->tail;
+}
+
+void queue_delete(struct queue *queue, unsigned int n)
+{
+	unsigned int idx, count = queue_count(queue);
+
+	i_assert(n < count);
+
+	queue->full = FALSE;
+	if (n == 0) {
+		/* optimized deletion from tail */
+		queue->tail = (queue->tail + 1) % queue->area_size;
+		return;
+	}
+	if (n == count-1) {
+		/* optimized deletion from head */
+		queue->head = (queue->head + queue->area_size - 1) %
+			queue->area_size;
+		return;
+	}
+
+	idx = queue_idx(queue, n);
+	if ((n < count/2 || idx > queue->head) && idx > queue->tail) {
+		/* move tail forward.
+ 		   ..tail##idx##head.. or ##head..tail##idx## */
+		array_copy(queue->arr, queue->tail + 1,
+			   queue->arr, queue->tail,
+			   idx - queue->tail);
+		queue->tail++;
+		i_assert(queue->tail < queue->area_size);
+	} else {
+		/* move head backward.
+ 		   ..tail##idx##head.. or ##idx##head..tail## */
+		i_assert(idx < queue->head);
+		array_copy(queue->arr, idx,
+			   queue->arr, idx + 1,
+			   queue->head - idx);
+		queue->head = (queue->head + queue->area_size - 1) %
+			queue->area_size;
+	}
+	i_assert(queue->head < queue->area_size && queue->head != queue->tail);
+}
+
+void queue_delete_tail(struct queue *queue)
+{
+	queue_delete(queue, 0);
+}
+
+void queue_clear(struct queue *queue)
+{
+	queue->head = queue->tail = 0;
+	queue->full = FALSE;
+}
+
+unsigned int queue_count(const struct queue *queue)
+{
+	unsigned int area_size = queue->area_size;
+
+	return queue->full ? area_size :
+		(area_size - queue->tail + queue->head) % area_size;
+}
diff -up dovecot-1.0.7/src/lib/queue.h.original dovecot-1.0.7/src/lib/queue.h
--- dovecot-1.0.7/src/lib/queue.h.original	2012-12-20 12:21:52.547970456 +0900
+++ dovecot-1.0.7/src/lib/queue.h	2012-12-20 12:22:40.735032897 +0900
@@ -0,0 +1,40 @@
+#ifndef QUEUE_H
+#define QUEUE_H
+
+/* Dynamically growing queue. Use the array directly to access the data,
+   for example:
+
+   count = queue_count(queue);
+   for (i = 0; i < count; i++) {
+       data = array[queue_idx(i)];
+   }
+*/
+
+struct queue {
+	struct array *arr;
+	unsigned int head, tail, area_size;
+	bool full;
+};
+
+struct queue *queue_init(struct array *array);
+void queue_deinit(struct queue **queue);
+
+/* Append item to head */
+void queue_append(struct queue *queue, const void *data);
+/* Delete last item from tail */
+void queue_delete_tail(struct queue *queue);
+/* Remove item from n'th position */
+void queue_delete(struct queue *queue, unsigned int n);
+/* Clear the entire queue */
+void queue_clear(struct queue *queue);
+
+/* Returns the number of items in queue. */
+unsigned int queue_count(const struct queue *queue);
+
+/* Returns array index of n'th element in queue. */
+static inline unsigned int queue_idx(const struct queue *queue, unsigned int n)
+{
+	return (queue->tail + n) % queue->area_size;
+}
+
+#endif