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