void dict_ldap_deinit(void)
{
+ ldap_clients_cleanup();
dict_driver_unregister(&dict_driver_ldap);
}
libdovecot_ldap_la_SOURCES = \
ldap-client.c \
ldap-connection.c \
+ ldap-connection-pool.c \
ldap-iterator.c \
ldap-search.c \
ldap-compare.c \
ldap-client.h
noinst_HEADERS = \
+ ldap-connection-pool.h \
ldap-private.h
pkginc_libdir=$(pkgincludedir)
/* Copyright (c) 2016 Dovecot authors, see the included COPYING file */
#include "lib.h"
+#include "ldap-connection-pool.h"
#include "ldap-private.h"
+/* Max number of ldap-connections that can be created. For now this is
+ unlimited since we're assuming our callers aren't calling us with many
+ different settings. */
+#define LDAP_CONN_POOL_MAX_CONNECTIONS UINT_MAX
+
struct ldap_client {
- /* for now we support just a single connection, but this could be
- extended to a connection pool. */
- struct ldap_connection *conn;
+ struct ldap_connection_list *list;
};
+static struct ldap_connection_pool *ldap_conn_pool = NULL;
+
int ldap_client_init(const struct ldap_client_settings *set,
struct ldap_client **client_r, const char **error_r)
{
struct ldap_client *client;
+ if (ldap_conn_pool == NULL)
+ ldap_conn_pool = ldap_connection_pool_init(LDAP_CONN_POOL_MAX_CONNECTIONS);
+
client = i_new(struct ldap_client, 1);
- if (ldap_connection_init(client, set, &client->conn, error_r) < 0) {
+ if (ldap_connection_pool_get(ldap_conn_pool, client, set,
+ &client->list, error_r) < 0) {
i_free(client);
return -1;
}
*_client = NULL;
- ldap_connection_deinit(&client->conn);
+ ldap_connection_pool_unref(ldap_conn_pool, &client->list);
i_free(client);
}
void ldap_client_switch_ioloop(struct ldap_client *client)
{
- ldap_connection_switch_ioloop(client->conn);
+ ldap_connection_switch_ioloop(client->list->conn);
}
#undef ldap_search_start
const struct ldap_search_input *input,
ldap_result_callback_t *callback, void *context)
{
- return ldap_connection_search_start(client->conn, input, callback, context);
+ /* FIXME: we could support multiple concurrent LDAP connections to
+ the same host. */
+ return ldap_connection_search_start(client->list->conn, input, callback, context);
}
#undef ldap_compare_start
const struct ldap_compare_input *input,
ldap_result_callback_t *callback, void *context)
{
- return ldap_connection_compare_start(client->conn, input, callback, context);
+ return ldap_connection_compare_start(client->list->conn, input, callback, context);
+}
+
+void ldap_clients_cleanup(void)
+{
+ if (ldap_conn_pool != NULL &&
+ !ldap_connection_pool_have_references(ldap_conn_pool))
+ ldap_connection_pool_deinit(&ldap_conn_pool);
}
typedef void ldap_result_callback_t(struct ldap_result *result, void *context);
struct ldap_client_settings {
+ /* NOTE: when adding here, remember to update
+ ldap_connection_have_settings() and ldap_connection_init() */
const char *uri;
const char *bind_dn;
const char *password;
void ldap_client_deinit(struct ldap_client **client);
void ldap_client_switch_ioloop(struct ldap_client *client);
+/* Deinitialize all pooled LDAP connections if there are no references left.
+ This allows freeing the memory at deinit, but still allows multiple
+ independent code parts to use lib-ldap and call this function. */
+void ldap_clients_cleanup(void);
+
void ldap_search_start(struct ldap_client *client,
const struct ldap_search_input *input,
ldap_result_callback_t *callback,
--- /dev/null
+/* Copyright (c) 2016 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "llist.h"
+#include "ldap-private.h"
+#include "ldap-connection-pool.h"
+
+struct ldap_connection_pool {
+ struct ldap_connection_list *conn_list;
+ unsigned int conn_count;
+
+ unsigned int max_connections;
+};
+
+static void ldap_connection_list_remove(struct ldap_connection_pool *pool,
+ struct ldap_connection_list *list)
+{
+ DLLIST_REMOVE(&pool->conn_list, list);
+ pool->conn_count--;
+
+ ldap_connection_deinit(&list->conn);
+ i_free(list);
+}
+
+static void
+ldap_connection_pool_shrink_to(struct ldap_connection_pool *pool,
+ unsigned int max_count)
+{
+ struct ldap_connection_list *list, *next;
+
+ list = pool->conn_list;
+ for (; list != NULL && pool->conn_count > max_count; list = next) {
+ next = list->next;
+ if (list->refcount == 0)
+ ldap_connection_list_remove(pool, list);
+ }
+}
+
+struct ldap_connection_pool *
+ldap_connection_pool_init(unsigned int max_connections)
+{
+ struct ldap_connection_pool *pool;
+
+ pool = i_new(struct ldap_connection_pool, 1);
+ pool->max_connections = max_connections;
+ return pool;
+}
+
+void ldap_connection_pool_deinit(struct ldap_connection_pool **_pool)
+{
+ struct ldap_connection_pool *pool = *_pool;
+
+ *_pool = NULL;
+
+ ldap_connection_pool_shrink_to(pool, 0);
+ i_assert(pool->conn_list == NULL);
+ i_free(pool);
+}
+
+int ldap_connection_pool_get(struct ldap_connection_pool *pool,
+ struct ldap_client *client,
+ const struct ldap_client_settings *set,
+ struct ldap_connection_list **list_r,
+ const char **error_r)
+{
+ struct ldap_connection_list *list;
+ struct ldap_connection *conn;
+
+ for (list = pool->conn_list; list != NULL; list = list->next) {
+ if (ldap_connection_have_settings(list->conn, set)) {
+ list->refcount++;
+ *list_r = list;
+ return 0;
+ }
+ }
+ if (ldap_connection_init(client, set, &conn, error_r) < 0)
+ return -1;
+
+ list = i_new(struct ldap_connection_list, 1);
+ list->conn = conn;
+ list->refcount++;
+
+ DLLIST_PREPEND(&pool->conn_list, list);
+ pool->conn_count++;
+
+ ldap_connection_pool_shrink_to(pool, pool->max_connections);
+ *list_r = list;
+ return 0;
+}
+
+void ldap_connection_pool_unref(struct ldap_connection_pool *pool,
+ struct ldap_connection_list **_list)
+{
+ struct ldap_connection_list *list = *_list;
+
+ *_list = NULL;
+
+ i_assert(list->refcount > 0);
+
+ if (--list->refcount == 0)
+ ldap_connection_pool_shrink_to(pool, pool->max_connections);
+}
+
+bool ldap_connection_pool_have_references(struct ldap_connection_pool *pool)
+{
+ struct ldap_connection_list *list;
+
+ for (list = pool->conn_list; list != NULL; list = list->next) {
+ if (list->refcount > 0)
+ return TRUE;
+ }
+ return FALSE;
+}
--- /dev/null
+#ifndef LDAP_CONNECTION_POOL_H
+#define LDAP_CONNECTION_POOL_H
+
+struct ldap_client;
+struct ldap_client_settings;
+
+struct ldap_connection_list {
+ struct ldap_connection_list *prev, *next;
+ struct ldap_connection *conn;
+ int refcount;
+};
+
+struct ldap_connection_pool *
+ldap_connection_pool_init(unsigned int max_connections);
+void ldap_connection_pool_deinit(struct ldap_connection_pool **_pool);
+/* Returns TRUE if there are connections with refcount>0 */
+bool ldap_connection_pool_have_references(struct ldap_connection_pool *pool);
+
+int ldap_connection_pool_get(struct ldap_connection_pool *pool,
+ struct ldap_client *client,
+ const struct ldap_client_settings *set,
+ struct ldap_connection_list **list_r,
+ const char **error_r);
+void ldap_connection_pool_unref(struct ldap_connection_pool *pool,
+ struct ldap_connection_list **list);
+
+#endif
return 0;
}
+bool ldap_connection_have_settings(struct ldap_connection *conn,
+ const struct ldap_client_settings *set)
+{
+ const struct ldap_client_settings *conn_set = &conn->set;
+
+ if (strcmp(conn_set->uri, set->uri) != 0)
+ return FALSE;
+ if (null_strcmp(conn_set->bind_dn, set->bind_dn) != 0)
+ return FALSE;
+ if (null_strcmp(conn_set->password, set->password) != 0)
+ return FALSE;
+ if (conn_set->timeout_secs != set->timeout_secs ||
+ conn_set->max_idle_time_secs != set->max_idle_time_secs ||
+ conn_set->debug != set->debug ||
+ conn_set->require_ssl != set->require_ssl ||
+ conn_set->start_tls != set->start_tls)
+ return FALSE;
+
+ if (set->ssl_set == NULL || !set->start_tls)
+ return TRUE;
+
+ /* check SSL settings */
+ if (null_strcmp(conn->ssl_set.protocols, set->ssl_set->protocols) != 0)
+ return FALSE;
+ if (null_strcmp(conn->ssl_set.cipher_list, set->ssl_set->cipher_list) != 0)
+ return FALSE;
+ if (null_strcmp(conn->ssl_set.ca_file, set->ssl_set->ca_file) != 0)
+ return FALSE;
+ if (null_strcmp(conn->ssl_set.cert, set->ssl_set->cert) != 0)
+ return FALSE;
+ if (null_strcmp(conn->ssl_set.key, set->ssl_set->key) != 0)
+ return FALSE;
+ return TRUE;
+}
+
int ldap_connection_init(struct ldap_client *client,
const struct ldap_client_settings *set,
struct ldap_connection **conn_r, const char **error_r)
conn->ssl_set.crypto_device = NULL;
if (set->ssl_set != NULL) {
+ /* keep in sync with ldap_connection_have_settings() */
+ conn->set.ssl_set = &conn->ssl_set;
conn->ssl_set.protocols = p_strdup(pool, set->ssl_set->protocols);
conn->ssl_set.cipher_list = p_strdup(pool, set->ssl_set->cipher_list);
conn->ssl_set.ca_file = p_strdup(pool, set->ssl_set->ca_file);
conn->ssl_set.cert = p_strdup(pool, set->ssl_set->cert);
conn->ssl_set.key = p_strdup(pool, set->ssl_set->key);
}
+ i_assert(ldap_connection_have_settings(conn, set));
if (ldap_connection_setup(conn, error_r) < 0) {
ldap_connection_deinit(&conn);
struct ldap_connection **conn_r, const char **error_r);
void ldap_connection_deinit(struct ldap_connection **_conn);
void ldap_connection_switch_ioloop(struct ldap_connection *conn);
+bool ldap_connection_have_settings(struct ldap_connection *conn,
+ const struct ldap_client_settings *set);
void ldap_connection_search_start(struct ldap_connection *conn,
const struct ldap_search_input *input,