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