>From 69914398d34e1d3d5ff2b31932a51bf367bd4f1b Mon Sep 17 00:00:00 2001
From: Martin Nagy <mnagy@redhat.com>
Date: Mon, 12 Jul 2010 04:04:34 +0200
Subject: [PATCH] Make ldap bind asynchronous

Every ldap function that could possibly create a new connection is now
wrapped in a tevent_req. If the connection is created, we will call the
function again after the socket is ready for writing.
---
 src/Makefile.am                            |    3 +
 src/providers/ldap/ldap_req_wrap.c         |  654 ++++++++++++++++++++++++++++
 src/providers/ldap/ldap_req_wrap.h         |  103 +++++
 src/providers/ldap/sdap.h                  |   16 +
 src/providers/ldap/sdap_async.c            |  263 ++++++++---
 src/providers/ldap/sdap_async_connection.c |  217 ++++++----
 src/providers/ldap/sdap_async_private.h    |    8 +
 src/providers/ldap/sdap_fd_events.c        |   78 +++-
 8 files changed, 1176 insertions(+), 166 deletions(-)
 create mode 100644 src/providers/ldap/ldap_req_wrap.c
 create mode 100644 src/providers/ldap/ldap_req_wrap.h

diff --git a/src/Makefile.am b/src/Makefile.am
index 79fcbb9..62bee5d 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -351,6 +351,7 @@ dist_noinst_HEADERS = \
     providers/krb5/krb5_common.h \
     providers/krb5/krb5_utils.h \
     providers/ldap/ldap_common.h \
+    providers/ldap/ldap_req_wrap.h \
     providers/ldap/sdap.h \
     providers/ldap/sdap_access.h \
     providers/ldap/sdap_async.h \
@@ -740,6 +741,7 @@ libsss_ldap_la_SOURCES = \
     providers/ldap/ldap_auth.c \
     providers/ldap/ldap_init.c \
     providers/ldap/ldap_common.c \
+    providers/ldap/ldap_req_wrap.c \
     providers/ldap/sdap_async.c \
     providers/ldap/sdap_async_accounts.c \
     providers/ldap/sdap_async_connection.c \
@@ -819,6 +821,7 @@ libsss_ipa_la_SOURCES = \
     providers/ldap/ldap_id_cleanup.c \
     providers/ldap/ldap_auth.c \
     providers/ldap/ldap_common.c \
+    providers/ldap/ldap_req_wrap.c \
     providers/ldap/sdap_async.c \
     providers/ldap/sdap_async_accounts.c \
     providers/ldap/sdap_async_connection.c \
diff --git a/src/providers/ldap/ldap_req_wrap.c b/src/providers/ldap/ldap_req_wrap.c
new file mode 100644
index 0000000..25c9af3
--- /dev/null
+++ b/src/providers/ldap/ldap_req_wrap.c
@@ -0,0 +1,654 @@
+/*
+    SSSD
+
+    LDAP tevent_req wrappers
+
+    Authors:
+        Martin Nagy <mnagy@redhat.com>
+
+    Copyright (C) 2010 Red Hat
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <sys/time.h>
+
+#include <talloc.h>
+#include <tevent.h>
+
+#include "providers/ldap/sdap.h"
+#include "providers/ldap/sdap_async_private.h"
+
+#ifdef HAVE_LDAP_CONNCB
+# ifdef LDAP_OPT_CONNECT_ASYNC
+#  define IS_CONNECTING(r) ((r) == LDAP_X_CONNECTING)
+# endif
+#else
+# define IS_CONNECTING(r) 0
+#endif
+
+
+/*
+ * ldap_sasl_bind()
+ */
+struct sasl_bind_state {
+    struct sdap_handle *sh;
+    char *dn;
+    char *mechanism;
+    struct berval *cred;
+    LDAPControl **sctrls;
+    LDAPControl **cctrls;
+
+    int msgid;
+    int ret;
+};
+
+static int ldap_sasl_bind_try(void *cb_data);
+
+struct tevent_req *ldap_sasl_bind_send(void *mem_ctx, struct tevent_context *ev,
+                                       struct sdap_handle *sh, const char *dn,
+                                       const char *mechanism,
+                                       struct berval *cred,
+                                       LDAPControl **sctrls,
+                                       LDAPControl **cctrls)
+{
+    struct tevent_req *req;
+    struct sasl_bind_state *state;
+
+    req = tevent_req_create(mem_ctx, &state, struct sasl_bind_state);
+    if (!req) return NULL;
+
+    state->sh = sh;
+    if (dn) {
+        state->dn = talloc_strdup(state, dn);
+        if (!state->dn) {
+            goto fail;
+        }
+    }
+    if (cred) {
+        state->cred = ber_dupbv(NULL, cred);
+        if (!state->cred) {
+            goto fail;
+        }
+    }
+    if (sctrls) {
+        state->sctrls = ldap_controls_dup(sctrls);
+        if (!state->sctrls) {
+            goto fail;
+        }
+    }
+    if (cctrls) {
+        state->cctrls = ldap_controls_dup(cctrls);
+        if (!state->cctrls) {
+            goto fail;
+        }
+    }
+
+    if (ldap_sasl_bind_try(req)) {
+        tevent_req_post(req, ev);
+    }
+
+    return req;
+
+fail:
+    tevent_req_error(req, ENOMEM);
+    tevent_req_post(req, ev);
+    return req;
+}
+
+static int ldap_sasl_bind_try(void *cb_data)
+{
+    struct tevent_req *req;
+    struct sasl_bind_state *state;
+    int ret;
+
+    req = talloc_get_type(cb_data, struct tevent_req);
+    state = tevent_req_data(req, struct sasl_bind_state);
+
+    DEBUG(4, ("calling ldap_sasl_bind(dn = \"%s\")\n", state->dn));
+    set_fd_retry_cb(state->sh, ldap_sasl_bind_try, cb_data);
+    ret = ldap_sasl_bind(state->sh->ldap, state->dn, state->mechanism,
+                         state->cred, state->sctrls, state->cctrls,
+                         &state->msgid);
+    set_fd_retry_cb(state->sh, NULL, NULL);
+
+    if (IS_CONNECTING(ret)) {
+        DEBUG(4, ("connection in progress, will try again later\n"));
+        return 0;
+    }
+
+    if (ret != LDAP_SUCCESS || state->msgid == -1) {
+        ret = ldap_get_option(state->sh->ldap, LDAP_OPT_RESULT_CODE,
+                              &state->ret);
+        if (ret != LDAP_OPT_SUCCESS) {
+            state->ret = LDAP_LOCAL_ERROR;
+        }
+        DEBUG(1, ("ldap_sasl_bind() failed (%d) [%s]\n", state->ret,
+                  ldap_err2string(state->ret)));
+        tevent_req_error(req, EIO);
+    } else {
+        DEBUG(4, ("ldap_sasl_bind() succeeded\n"));
+        state->ret = LDAP_SUCCESS;
+        tevent_req_done(req);
+    }
+
+    return 1;
+}
+
+int ldap_sasl_bind_recv(struct tevent_req *req, int *retp, int *msgidp)
+{
+    struct sasl_bind_state *state;
+    state = tevent_req_data(req, struct sasl_bind_state);
+
+    /* Free stuff that we allocated. */
+    ber_bvfree(state->cred);
+    ldap_controls_free(state->sctrls);
+    ldap_controls_free(state->cctrls);
+
+    if (retp) *retp = state->ret;
+    if (msgidp) *msgidp = state->msgid;
+
+    TEVENT_REQ_RETURN_ON_ERROR(req);
+
+    return EOK;
+}
+
+/*
+ * ldap_search_ext()
+ */
+struct search_ext_state {
+    struct sdap_handle *sh;
+    char *base;
+    int scope;
+    char *filter;
+    char **attrs;
+    int attrsonly;
+    LDAPControl **sctrls;
+    LDAPControl **cctrls;
+    struct timeval *timeout;
+    int sizelimit;
+
+    int msgid;
+    int ret;
+};
+
+static int ldap_search_ext_try(void *cb_data);
+
+struct tevent_req *ldap_search_ext_send(void *mem_ctx,
+                                        struct tevent_context *ev,
+                                        struct sdap_handle *sh,
+                                        const char *base, int scope,
+                                        const char *filter, const char **attrs,
+                                        int attrsonly, LDAPControl **sctrls,
+                                        LDAPControl **cctrls,
+                                        const struct timeval *timeout,
+                                        int sizelimit)
+{
+    struct tevent_req *req;
+    struct search_ext_state *state;
+    int i;
+
+    req = tevent_req_create(mem_ctx, &state, struct search_ext_state);
+    if (!req) return NULL;
+
+    state->sh = sh;
+    state->scope = scope;
+    state->attrsonly = attrsonly;
+    state->sizelimit = sizelimit;
+
+    if (base) {
+        state->base = talloc_strdup(state, base);
+        if (!state->base) goto fail;
+    }
+    if (filter) {
+        state->filter = talloc_strdup(state, filter);
+        if (!state->filter) goto fail;
+    }
+    if (attrs) {
+        for (i = 0; attrs[i]; i++);
+        state->attrs = talloc_array(state, char *, i + 1);
+        if (!state->attrs) goto fail;
+        for (i = 0; attrs[i]; i++) {
+            state->attrs[i] = talloc_strdup(state->attrs, attrs[i]);
+            if (!state->attrs[i]) goto fail;
+        }
+        state->attrs[i] = NULL;
+    }
+    if (sctrls) {
+        state->sctrls = ldap_controls_dup(sctrls);
+        if (!state->sctrls) goto fail;
+    }
+    if (cctrls) {
+        state->cctrls = ldap_controls_dup(cctrls);
+        if (!state->cctrls) goto fail;
+    }
+    if (timeout) {
+        state->timeout = talloc(state, struct timeval);
+        if (!state->timeout) goto fail;
+        state->timeout->tv_sec = timeout->tv_sec;
+        state->timeout->tv_usec = timeout->tv_usec;
+    }
+
+    if (ldap_search_ext_try(req)) {
+        tevent_req_post(req, ev);
+    }
+
+    return req;
+
+fail:
+    tevent_req_error(req, ENOMEM);
+    tevent_req_post(req, ev);
+    return req;
+}
+
+static int ldap_search_ext_try(void *cb_data)
+{
+    struct tevent_req *req;
+    struct search_ext_state *state;
+
+    req = talloc_get_type(cb_data, struct tevent_req);
+    state = tevent_req_data(req, struct search_ext_state);
+
+    DEBUG(4, ("calling ldap_search_ext()\n"));
+    set_fd_retry_cb(state->sh, ldap_search_ext_try, cb_data);
+    state->ret = ldap_search_ext(state->sh->ldap, state->base, state->scope,
+                                 state->filter, state->attrs, state->attrsonly,
+                                 state->sctrls, state->cctrls, state->timeout,
+                                 state->sizelimit, &state->msgid);
+    set_fd_retry_cb(state->sh, NULL, NULL);
+
+    if (IS_CONNECTING(state->ret)) {
+        DEBUG(4, ("connection in progress, will try again later\n"));
+        return 0;
+    }
+
+    if (state->ret != LDAP_SUCCESS) {
+        DEBUG(1, ("ldap_search_ext() failed (%d) [%s]\n", state->ret,
+                  ldap_err2string(state->ret)));
+        tevent_req_error(req, EIO);
+    } else {
+        DEBUG(4, ("ldap_search_ext() succeeded, msgid = %d\n", state->msgid));
+        tevent_req_done(req);
+    }
+
+    return 1;
+}
+
+int ldap_search_ext_recv(struct tevent_req *req, int *retp, int *msgidp)
+{
+    struct search_ext_state *state;
+    state = tevent_req_data(req, struct search_ext_state);
+
+    /* Free stuff that we allocated. */
+    ldap_controls_free(state->sctrls);
+    ldap_controls_free(state->cctrls);
+
+    if (retp) *retp = state->ret;
+    if (msgidp) *msgidp = state->msgid;
+
+    TEVENT_REQ_RETURN_ON_ERROR(req);
+
+    return EOK;
+}
+
+/*
+ * ldap_extended_operation()
+ */
+struct extended_operation_state {
+    struct sdap_handle *sh;
+    char *requestoid;
+    struct berval *requestdata;
+    LDAPControl **sctrls;
+    LDAPControl **cctrls;
+
+    int msgid;
+    int ret;
+};
+
+static int ldap_extended_operation_try(void *cb_data);
+
+struct tevent_req *ldap_extended_operation_send(void *mem_ctx,
+                                                struct tevent_context *ev,
+                                                struct sdap_handle *sh,
+                                                const char *requestoid,
+                                                struct berval *requestdata,
+                                                LDAPControl **sctrls,
+                                                LDAPControl **cctrls)
+{
+    struct tevent_req *req;
+    struct extended_operation_state *state;
+
+    req = tevent_req_create(mem_ctx, &state, struct extended_operation_state);
+    if (!req) return NULL;
+
+    state->sh = sh;
+
+    if (requestoid) {
+        state->requestoid = talloc_strdup(state, requestoid);
+        if (!state->requestoid) goto fail;
+    }
+    if (requestdata) {
+        state->requestdata = ber_dupbv(NULL, requestdata);
+        if (!state->requestdata) {
+            goto fail;
+        }
+    }
+    if (sctrls) {
+        state->sctrls = ldap_controls_dup(sctrls);
+        if (!state->sctrls) goto fail;
+    }
+    if (cctrls) {
+        state->cctrls = ldap_controls_dup(cctrls);
+        if (!state->cctrls) goto fail;
+    }
+
+    if (ldap_extended_operation_try(req)) {
+        tevent_req_post(req, ev);
+    }
+
+    return req;
+
+fail:
+    tevent_req_error(req, ENOMEM);
+    tevent_req_post(req, ev);
+    return req;
+}
+
+static int ldap_extended_operation_try(void *cb_data)
+{
+    struct tevent_req *req;
+    struct extended_operation_state *state;
+
+    req = talloc_get_type(cb_data, struct tevent_req);
+    state = tevent_req_data(req, struct extended_operation_state);
+
+    DEBUG(4, ("calling ldap_extended_operation()\n"));
+    set_fd_retry_cb(state->sh, ldap_extended_operation_try, cb_data);
+    state->ret = ldap_extended_operation(state->sh->ldap, state->requestoid,
+                                         state->requestdata, state->sctrls,
+                                         state->cctrls, &state->msgid);
+    set_fd_retry_cb(state->sh, NULL, NULL);
+
+    if (IS_CONNECTING(state->ret)) {
+        DEBUG(4, ("connection in progress, will try again later\n"));
+        return 0;
+    }
+
+    if (state->ret != LDAP_SUCCESS) {
+        DEBUG(1, ("ldap_extended_operation() failed (%d) [%s]\n", state->ret,
+                  ldap_err2string(state->ret)));
+        tevent_req_error(req, EIO);
+    } else {
+        DEBUG(4, ("ldap_extended_operation() succeeded, msgid = %d\n",
+                  state->msgid));
+        tevent_req_done(req);
+    }
+
+    return 1;
+}
+
+int ldap_extended_operation_recv(struct tevent_req *req,
+                                 int *retp, int *msgidp)
+{
+    struct extended_operation_state *state;
+    state = tevent_req_data(req, struct extended_operation_state);
+
+    /* Free stuff that we allocated. */
+    ber_bvfree(state->requestdata);
+    ldap_controls_free(state->sctrls);
+    ldap_controls_free(state->cctrls);
+
+    if (retp) *retp = state->ret;
+    if (msgidp) *msgidp = state->msgid;
+
+    TEVENT_REQ_RETURN_ON_ERROR(req);
+
+    return EOK;
+}
+
+/*
+ * ldap_start_tls()
+ */
+struct start_tls_state {
+    struct sdap_handle *sh;
+    LDAPControl **sctrls;
+    LDAPControl **cctrls;
+
+    int msgid;
+    int ret;
+};
+
+static int ldap_start_tls_try(void *cb_data);
+
+struct tevent_req *ldap_start_tls_send(void *mem_ctx,
+                                       struct tevent_context *ev,
+                                       struct sdap_handle *sh,
+                                       LDAPControl **sctrls,
+                                       LDAPControl **cctrls)
+{
+    struct tevent_req *req;
+    struct start_tls_state *state;
+
+    req = tevent_req_create(mem_ctx, &state, struct start_tls_state);
+    if (!req) return NULL;
+
+    state->sh = sh;
+
+    if (sctrls) {
+        state->sctrls = ldap_controls_dup(sctrls);
+        if (!state->sctrls) goto fail;
+    }
+    if (cctrls) {
+        state->cctrls = ldap_controls_dup(cctrls);
+        if (!state->cctrls) goto fail;
+    }
+
+    if (ldap_start_tls_try(req)) {
+        tevent_req_post(req, ev);
+    }
+
+    return req;
+
+fail:
+    tevent_req_error(req, ENOMEM);
+    tevent_req_post(req, ev);
+    return req;
+}
+
+static int ldap_start_tls_try(void *cb_data)
+{
+    struct tevent_req *req;
+    struct start_tls_state *state;
+    int optret;
+    char *errmsg;
+
+    req = talloc_get_type(cb_data, struct tevent_req);
+    state = tevent_req_data(req, struct start_tls_state);
+
+    DEBUG(4, ("calling ldap_start_tls()\n"));
+    set_fd_retry_cb(state->sh, ldap_start_tls_try, cb_data);
+    state->ret = ldap_start_tls(state->sh->ldap, state->sctrls, state->cctrls,
+                                &state->msgid);
+    set_fd_retry_cb(state->sh, NULL, NULL);
+
+    if (IS_CONNECTING(state->ret)) {
+        DEBUG(4, ("connection in progress, will try again later\n"));
+        return 0;
+    }
+
+    if (state->ret != LDAP_SUCCESS) {
+        optret = ldap_get_option(state->sh->ldap,
+                                 SDAP_DIAGNOSTIC_MESSAGE, (void *)&errmsg);
+        if (optret == LDAP_SUCCESS) {
+            DEBUG(1, ("ldap_start_tls failed: (%d) [%s] [%s]\n",
+                      state->ret,
+                      ldap_err2string(state->ret),
+                      errmsg));
+            ldap_memfree(errmsg);
+        } else {
+            DEBUG(1, ("ldap_start_tls failed: (%d) [%s]\n", state->ret,
+                      ldap_err2string(state->ret)));
+        }
+        tevent_req_error(req, EIO);
+    } else {
+        DEBUG(4, ("ldap_start_tls() succeeded\n"));
+        tevent_req_done(req);
+    }
+
+    return 1;
+}
+
+int ldap_start_tls_recv(struct tevent_req *req, int *retp, int *msgidp)
+{
+    struct start_tls_state *state;
+    state = tevent_req_data(req, struct start_tls_state);
+
+    /* Free stuff that we allocated. */
+    ldap_controls_free(state->sctrls);
+    ldap_controls_free(state->cctrls);
+
+    if (retp) *retp = state->ret;
+    if (msgidp) *msgidp = state->msgid;
+
+    TEVENT_REQ_RETURN_ON_ERROR(req);
+
+    return EOK;
+}
+
+/*
+ * ldap_sasl_interactive_bind()
+ */
+struct sasl_interactive_bind_state {
+    struct sdap_handle *sh;
+    char *dn;
+    char *mechanism;
+    LDAPControl **sctrls;
+    LDAPControl **cctrls;
+    unsigned flags;
+    LDAP_SASL_INTERACT_PROC *interact;
+    void *defaults;
+};
+
+static int ldap_sasl_interactive_bind_try(void *cb_data);
+
+struct tevent_req *
+ldap_sasl_interactive_bind_send(void *mem_ctx,
+                                struct tevent_context *ev,
+                                struct sdap_handle *sh,
+                                const char *dn,
+                                const char *mechanism,
+                                LDAPControl **sctrls,
+                                LDAPControl **cctrls,
+                                unsigned flags,
+                                LDAP_SASL_INTERACT_PROC *interact,
+                                void *defaults)
+{
+    struct tevent_req *req;
+    struct sasl_interactive_bind_state *state;
+
+    req = tevent_req_create(mem_ctx, &state,
+                            struct sasl_interactive_bind_state);
+    if (!req) return NULL;
+
+    state->sh = sh;
+    state->flags = flags;
+    state->interact = interact;
+    state->defaults = defaults;
+    if (dn) {
+        state->dn = talloc_strdup(state, dn);
+        if (!state->dn) {
+            goto fail;
+        }
+    }
+    if (sctrls) {
+        state->sctrls = ldap_controls_dup(sctrls);
+        if (!state->sctrls) {
+            goto fail;
+        }
+    }
+    if (cctrls) {
+        state->cctrls = ldap_controls_dup(cctrls);
+        if (!state->cctrls) {
+            goto fail;
+        }
+    }
+
+    if (ldap_sasl_interactive_bind_try(req)) {
+        tevent_req_post(req, ev);
+    }
+
+    return req;
+
+fail:
+    tevent_req_error(req, ENOMEM);
+    tevent_req_post(req, ev);
+    return req;
+}
+
+static int ldap_sasl_interactive_bind_try(void *cb_data)
+{
+    struct tevent_req *req;
+    struct sasl_interactive_bind_state *state;
+    int ret;
+
+    req = talloc_get_type(cb_data, struct tevent_req);
+    state = tevent_req_data(req, struct sasl_interactive_bind_state);
+
+    /* FIXME: Warning, this is a sync call!
+     * No async variant exist in openldap libraries yet */
+
+    DEBUG(4, ("calling ldap_sasl_interactive_bind_s(dn = \"%s\")\n",
+              state->dn));
+    set_fd_retry_cb(state->sh, ldap_sasl_interactive_bind_try, cb_data);
+    ret = ldap_sasl_interactive_bind_s(state->sh->ldap, state->dn,
+                                       state->mechanism, state->sctrls,
+                                       state->cctrls, state->flags,
+                                       state->interact, state->defaults);
+    set_fd_retry_cb(state->sh, NULL, NULL);
+
+    if (IS_CONNECTING(ret)) {
+        DEBUG(4, ("connection in progress, will try again later\n"));
+        return 0;
+    }
+
+    if (ret != LDAP_SUCCESS) {
+        DEBUG(1, ("ldap_sasl_bind failed (%d) [%s]\n",
+                  ret, ldap_err2string(ret)));
+
+        if (ret == LDAP_SERVER_DOWN) {
+            tevent_req_error(req, ETIMEDOUT);
+        } else {
+            tevent_req_error(req, EIO);
+        }
+    } else {
+        DEBUG(4, ("ldap_sasl_interactive_bind() succeeded\n"));
+        tevent_req_done(req);
+    }
+
+    return 1;
+}
+
+int ldap_sasl_interactive_bind_recv(struct tevent_req *req)
+{
+    struct sasl_interactive_bind_state *state;
+    state = tevent_req_data(req, struct sasl_interactive_bind_state);
+
+    /* Free stuff that we allocated. */
+    ldap_controls_free(state->sctrls);
+    ldap_controls_free(state->cctrls);
+
+    TEVENT_REQ_RETURN_ON_ERROR(req);
+
+    return EOK;
+}
diff --git a/src/providers/ldap/ldap_req_wrap.h b/src/providers/ldap/ldap_req_wrap.h
new file mode 100644
index 0000000..85ed258
--- /dev/null
+++ b/src/providers/ldap/ldap_req_wrap.h
@@ -0,0 +1,103 @@
+/*
+    SSSD
+
+    LDAP tevent_req wrappers
+
+    Authors:
+        Martin Nagy <mnagy@redhat.com>
+
+    Copyright (C) 2010 Red Hat
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _LDAP_REQ_WRAP_H_
+#define _LDAP_REQ_WRAP_H_
+
+#include <talloc.h>
+#include <tevent.h>
+
+#include "providers/ldap/sdap.h"
+
+struct tevent_req *ldap_sasl_bind_send(void *mem_ctx,
+                                       struct tevent_context *ev,
+                                       struct sdap_handle *sh,
+                                       const char *dn,
+                                       const char *mechanism,
+                                       struct berval *cred,
+                                       LDAPControl **sctrls,
+                                       LDAPControl **cctrls);
+
+int ldap_sasl_bind_recv(struct tevent_req *req,
+                        int *ret,
+                        int *msgidp);
+
+
+struct tevent_req *ldap_search_ext_send(void *mem_ctx,
+                                        struct tevent_context *ev,
+                                        struct sdap_handle *sh,
+                                        const char *base,
+                                        int scope,
+                                        const char *filter,
+                                        const char **attrs,
+                                        int attrsonly,
+                                        LDAPControl **sctrls,
+                                        LDAPControl **cctrls,
+                                        const struct timeval *timeout,
+                                        int sizelimit);
+
+int ldap_search_ext_recv(struct tevent_req *req,
+                         int *retp,
+                         int *msgidp);
+
+
+struct tevent_req *ldap_extended_operation_send(void *mem_ctx,
+                                                struct tevent_context *ev,
+                                                struct sdap_handle *sh,
+                                                const char *requestoid,
+                                                struct berval *requestdata,
+                                                LDAPControl **sctrls,
+                                                LDAPControl **cctrls);
+
+int ldap_extended_operation_recv(struct tevent_req *req,
+                                 int *retp,
+                                 int *msgidp);
+
+
+struct tevent_req *ldap_start_tls_send(void *mem_ctx,
+                                       struct tevent_context *ev,
+                                       struct sdap_handle *sh,
+                                       LDAPControl **sctrls,
+                                       LDAPControl **cctrls);
+
+int ldap_start_tls_recv(struct tevent_req *req,
+                        int *retp,
+                        int *msgidp);
+
+
+struct tevent_req *
+ldap_sasl_interactive_bind_send(void *mem_ctx,
+                                struct tevent_context *ev,
+                                struct sdap_handle *sh,
+                                const char *dn,
+                                const char *mechanism,
+                                LDAPControl **sctrls,
+                                LDAPControl **cctrls,
+                                unsigned flags,
+                                LDAP_SASL_INTERACT_PROC *interact,
+                                void *defaults);
+
+int ldap_sasl_interactive_bind_recv(struct tevent_req *req);
+
+#endif /* !_LDAP_REQ_WRAP_H_ */
diff --git a/src/providers/ldap/sdap.h b/src/providers/ldap/sdap.h
index 91d91b8..6b255b0 100644
--- a/src/providers/ldap/sdap.h
+++ b/src/providers/ldap/sdap.h
@@ -45,6 +45,7 @@ struct sdap_op;
 
 typedef void (sdap_op_callback_t)(struct sdap_op *op,
                                   struct sdap_msg *, int, void *);
+typedef int (fd_wakeup_callback_t)(void *);
 
 struct sdap_handle;
 
@@ -63,18 +64,33 @@ struct sdap_op {
     struct sdap_msg *last;
 };
 
+struct ldap_cb_data;
+
+struct request_guard {
+    struct fd_event_item *ptr;
+};
+
 struct fd_event_item {
     struct fd_event_item *prev;
     struct fd_event_item *next;
 
     int fd;
     struct tevent_fd *fde;
+
+    struct ldap_cb_data *cb_data;
+    fd_wakeup_callback_t *fd_wakeup_cb;
+    void *fd_wakeup_cb_data;
+    struct request_guard *guard;
+    struct tevent_timer *timeout_watcher;
 };
 
 struct ldap_cb_data {
     struct sdap_handle *sh;
     struct tevent_context *ev;
     struct fd_event_item *fd_list;
+
+    fd_wakeup_callback_t *wakeup_cb;
+    void *wakeup_cb_data;
 };
 
 struct sdap_handle {
diff --git a/src/providers/ldap/sdap_async.c b/src/providers/ldap/sdap_async.c
index 0f26024..dbd5176 100644
--- a/src/providers/ldap/sdap_async.c
+++ b/src/providers/ldap/sdap_async.c
@@ -22,6 +22,7 @@
 #include <ctype.h>
 #include "util/util.h"
 #include "providers/ldap/sdap_async_private.h"
+#include "providers/ldap/ldap_req_wrap.h"
 
 #define REALM_SEPARATOR '@'
 #define REPLY_REALLOC_INCREMENT 10
@@ -139,6 +140,8 @@ static void sdap_handle_release(struct sdap_handle *sh)
 }
 
 /* ==Parse-Results-And-Handle-Disconnections============================== */
+static void sdap_finish_bind(struct ldap_cb_data *cb_data,
+                             struct tevent_fd *fde);
 static void sdap_process_message(struct tevent_context *ev,
                                  struct sdap_handle *sh, LDAPMessage *msg);
 static void sdap_process_result(struct tevent_context *ev, void *pvt);
@@ -146,6 +149,22 @@ static void sdap_process_next_reply(struct tevent_context *ev,
                                     struct tevent_timer *te,
                                     struct timeval tv, void *pvt);
 
+void sdap_async_ldap_result(struct tevent_context *ev,
+                            struct tevent_fd *fde,
+                            uint16_t flags, void *pvt)
+{
+    struct ldap_cb_data *cb_data = talloc_get_type(pvt, struct ldap_cb_data);
+
+    if (flags & TEVENT_FD_WRITE) {
+        sdap_finish_bind(cb_data, fde);
+    }
+
+    if (flags & TEVENT_FD_READ) {
+        sdap_process_result(ev, cb_data->sh);
+    }
+}
+
+
 void sdap_ldap_result(struct tevent_context *ev, struct tevent_fd *fde,
                       uint16_t flags, void *pvt)
 {
@@ -159,6 +178,95 @@ static void sdap_ldap_next_result(struct tevent_context *ev,
     sdap_process_result(ev, pvt);
 }
 
+static void sdap_finish_bind(struct ldap_cb_data *cb_data,
+                             struct tevent_fd *fde)
+{
+    struct fd_event_item *fd_event_item;
+    struct request_guard *guard;
+    struct fd_event_item *guard_ptr;
+
+    DEBUG(8, ("Trace: sh[%p], ldap[%p], fde[%p]\n",
+              cb_data->sh, cb_data->sh->ldap, fde));
+
+    DLIST_FOR_EACH(fd_event_item, cb_data->fd_list) {
+        if (fd_event_item->fde == fde) {
+            break;
+        }
+    }
+    if (fd_event_item != NULL && fd_event_item->fd_wakeup_cb != NULL) {
+        if (fd_event_item->guard) {
+            guard_ptr = fd_event_item->guard->ptr;
+            guard = fd_event_item->guard;
+
+            fd_event_item->guard->ptr = NULL;
+            fd_event_item->guard = NULL;
+        } else {
+            guard = NULL;
+        }
+        if (fd_event_item->fd_wakeup_cb(fd_event_item->fd_wakeup_cb_data)) {
+            talloc_zfree(fd_event_item->timeout_watcher);
+        } else {
+            if (guard) {
+                fd_event_item->guard = guard;
+                guard->ptr = guard_ptr;
+            }
+            return;
+        }
+    } else if (fd_event_item == NULL) {
+        DEBUG(1, ("Bug: Couldn't find fd_event_item\n"));
+    }
+    tevent_fd_set_flags(fde, tevent_fd_get_flags(fde) & ~TEVENT_FD_WRITE);
+}
+
+struct conn_timeout {
+    struct ldap_cb_data *cb_data;
+    struct fd_event_item *fd_event_item;
+};
+
+static void sdap_check_connection_timeout(struct tevent_context *ev,
+                                          struct tevent_timer *te,
+                                          struct timeval current_time,
+                                          void *private_data)
+{
+    struct conn_timeout *ct = talloc_get_type(private_data, struct conn_timeout);
+
+    ct->fd_event_item->timeout_watcher = NULL;
+    sdap_finish_bind(ct->cb_data, ct->fd_event_item->fde);
+}
+
+void sdap_add_timeout_watcher(struct ldap_cb_data *cb_data,
+                              struct fd_event_item *fd_event_item)
+{
+    struct conn_timeout *ct;
+    struct timeval tv = { 0 };
+    struct timeval *timeout;
+    int ret;
+
+    ct = talloc(fd_event_item, struct conn_timeout);
+    if (!ct) goto fail;
+    ct->cb_data = cb_data;
+    ct->fd_event_item = fd_event_item;
+
+    ret = ldap_get_option(cb_data->sh->ldap,
+                          LDAP_OPT_NETWORK_TIMEOUT, &timeout);
+    if (ret == LDAP_OPT_SUCCESS) {
+        tv = tevent_timeval_current_ofs(timeout->tv_sec + 1, 0);
+        ldap_memfree(timeout);
+    } else {
+        DEBUG(1, ("Couldn't get network timeout from ldap\n"));
+        tv = tevent_timeval_current_ofs(1, 0);
+    }
+
+    fd_event_item->timeout_watcher = tevent_add_timer(cb_data->ev,
+            fd_event_item, tv, sdap_check_connection_timeout, ct);
+    if (!fd_event_item->timeout_watcher) goto fail;
+
+    return;
+
+fail:
+    DEBUG(1, ("Out of memory\n"));
+}
+
 static void sdap_process_result(struct tevent_context *ev, void *pvt)
 {
     struct sdap_handle *sh = talloc_get_type(pvt, struct sdap_handle);
@@ -426,6 +534,7 @@ int sdap_op_add(TALLOC_CTX *memctx, struct tevent_context *ev,
 /* ==Modify-Password====================================================== */
 
 struct sdap_exop_modify_passwd_state {
+    struct tevent_context *ev;
     struct sdap_handle *sh;
 
     struct sdap_op *op;
@@ -434,9 +543,7 @@ struct sdap_exop_modify_passwd_state {
     char *user_error_message;
 };
 
-static void sdap_exop_modify_passwd_done(struct sdap_op *op,
-                                         struct sdap_msg *reply,
-                                         int error, void *pvt);
+static void sdap_exop_modify_passwd_step(struct tevent_req *subreq);
 
 struct tevent_req *sdap_exop_modify_passwd_send(TALLOC_CTX *memctx,
                                            struct tevent_context *ev,
@@ -446,17 +553,18 @@ struct tevent_req *sdap_exop_modify_passwd_send(TALLOC_CTX *memctx,
                                            char *new_password)
 {
     struct tevent_req *req = NULL;
+    struct tevent_req *subreq;
     struct sdap_exop_modify_passwd_state *state;
     int ret;
     BerElement *ber = NULL;
     struct berval *bv = NULL;
-    int msgid;
     LDAPControl *request_controls[2];
 
     req = tevent_req_create(memctx, &state,
                             struct sdap_exop_modify_passwd_state);
     if (!req) return NULL;
 
+    state->ev = ev;
     state->sh = sh;
     state->user_error_message = NULL;
 
@@ -496,30 +604,59 @@ struct tevent_req *sdap_exop_modify_passwd_send(TALLOC_CTX *memctx,
 
     DEBUG(4, ("Executing extended operation\n"));
 
-    ret = ldap_extended_operation(state->sh->ldap, LDAP_EXOP_MODIFY_PASSWD,
-                                  bv, request_controls, NULL, &msgid);
+    subreq = ldap_extended_operation_send(state, ev, state->sh,
+                                          LDAP_EXOP_MODIFY_PASSWD, bv,
+                                          request_controls, NULL);
     ber_bvfree(bv);
     ldap_control_free(request_controls[0]);
-    if (ret == -1 || msgid == -1) {
-        DEBUG(1, ("ldap_extended_operation failed\n"));
-        goto fail;
+
+    if (!subreq) {
+        DEBUG(1, ("ldap_extended_operation_send failed.\n"));
+        talloc_zfree(req);
+        return NULL;
     }
-    DEBUG(8, ("ldap_extended_operation sent, msgid = %d\n", msgid));
+
+    tevent_req_set_callback(subreq, sdap_exop_modify_passwd_step, req);
+
+    return req;
+
+fail:
+    tevent_req_error(req, EIO);
+    tevent_req_post(req, ev);
+    return req;
+}
+
+static void sdap_exop_modify_passwd_done(struct sdap_op *op,
+                                         struct sdap_msg *reply,
+                                         int error, void *pvt);
+
+static void sdap_exop_modify_passwd_step(struct tevent_req *subreq)
+{
+    struct tevent_req *req;
+    struct sdap_exop_modify_passwd_state *state;
+    int ret;
+    int msgid;
+    int ldap_ret;
+
+    req = tevent_req_callback_data(subreq, struct tevent_req);
+    state = tevent_req_data(req, struct sdap_exop_modify_passwd_state);
+
+    ret = ldap_extended_operation_recv(subreq, &ldap_ret, &msgid);
+    talloc_zfree(subreq);
+    if (ret != EOK) goto fail;
 
     /* FIXME: get timeouts from configuration, for now 5 secs. */
-    ret = sdap_op_add(state, ev, state->sh, msgid,
+    ret = sdap_op_add(state, state->ev, state->sh, msgid,
                       sdap_exop_modify_passwd_done, req, 5, &state->op);
-    if (ret) {
+    if (ret != EOK) {
         DEBUG(1, ("Failed to set up operation!\n"));
         goto fail;
     }
 
-    return req;
+    return;
 
 fail:
-    tevent_req_error(req, EIO);
-    tevent_req_post(req, ev);
-    return req;
+    tevent_req_error(req, ret);
 }
 
 static void sdap_exop_modify_passwd_done(struct sdap_op *op,
@@ -730,10 +867,6 @@ struct sdap_get_generic_state {
     struct tevent_context *ev;
     struct sdap_options *opts;
     struct sdap_handle *sh;
-    const char *search_base;
-    int scope;
-    const char *filter;
-    const char **attrs;
     struct sdap_attr_map *map;
     int map_num_attrs;
 
@@ -747,9 +880,7 @@ struct sdap_get_generic_state {
 static errno_t add_to_reply(struct sdap_get_generic_state *state,
                             struct sysdb_attrs *msg);
 
-static void sdap_get_generic_done(struct sdap_op *op,
-                                  struct sdap_msg *reply,
-                                  int error, void *pvt);
+static void sdap_get_generic_step(struct tevent_req *subreq);
 
 struct tevent_req *sdap_get_generic_send(TALLOC_CTX *memctx,
                                          struct tevent_context *ev,
@@ -762,13 +893,9 @@ struct tevent_req *sdap_get_generic_send(TALLOC_CTX *memctx,
                                          struct sdap_attr_map *map,
                                          int map_num_attrs)
 {
-    struct tevent_req *req = NULL;
-    struct sdap_get_generic_state *state = NULL;
-    char *errmsg;
-    int lret;
-    int optret;
-    int ret;
-    int msgid;
+    struct tevent_req *req;
+    struct tevent_req *subreq;
+    struct sdap_get_generic_state *state;
 
     req = tevent_req_create(memctx, &state, struct sdap_get_generic_state);
     if (!req) return NULL;
@@ -776,10 +903,6 @@ struct tevent_req *sdap_get_generic_send(TALLOC_CTX *memctx,
     state->ev = ev;
     state->opts = opts;
     state->sh = sh;
-    state->search_base = search_base;
-    state->scope = scope;
-    state->filter = filter;
-    state->attrs = attrs;
     state->map = map;
     state->map_num_attrs = map_num_attrs;
     state->op = NULL;
@@ -787,46 +910,49 @@ struct tevent_req *sdap_get_generic_send(TALLOC_CTX *memctx,
     state->reply_count = 0;
     state->reply = NULL;
 
-    DEBUG(6, ("calling ldap_search_ext with [%s][%s].\n", state->filter,
-                                                          state->search_base));
+    DEBUG(6, ("calling ldap_search_ext with [%s][%s].\n", filter,
+                                                          search_base));
     if (debug_level >= 7) {
         int i;
 
-        if (state->attrs) {
-            for (i = 0; state->attrs[i]; i++) {
-                DEBUG(7, ("Requesting attrs: [%s]\n", state->attrs[i]));
+        if (attrs) {
+            for (i = 0; attrs[i]; i++) {
+                DEBUG(7, ("Requesting attrs: [%s]\n", attrs[i]));
             }
         }
     }
 
-    lret = ldap_search_ext(state->sh->ldap, state->search_base,
-                           state->scope, state->filter,
-                           discard_const(state->attrs),
-                           false, NULL, NULL, NULL, 0, &msgid);
-    if (lret != LDAP_SUCCESS) {
-        DEBUG(3, ("ldap_search_ext failed: %s\n", ldap_err2string(lret)));
-        if (lret == LDAP_SERVER_DOWN) {
-            ret = ETIMEDOUT;
-            optret = ldap_get_option(state->sh->ldap,
-                                     SDAP_DIAGNOSTIC_MESSAGE,
-                                     (void*)&errmsg);
-            if (optret == LDAP_SUCCESS) {
-                DEBUG(3, ("Connection error: %s\n", errmsg));
-                sss_log(SSS_LOG_ERR, "LDAP connection error: %s", errmsg);
-                ldap_memfree(errmsg);
-            }
-            else {
-                sss_log(SSS_LOG_ERR, "LDAP connection error, %s",
-                                     ldap_err2string(lret));
-            }
-        }
+    subreq = ldap_search_ext_send(state, ev, sh, search_base, scope, filter,
+                                  attrs, false, NULL, NULL, NULL, 0);
+    if (!subreq) goto fail;
+    tevent_req_set_callback(subreq, sdap_get_generic_step, req);
 
-        else {
-            ret = EIO;
-        }
-        goto fail;
-    }
-    DEBUG(8, ("ldap_search_ext called, msgid = %d\n", msgid));
+    return req;
+
+fail:
+    tevent_req_error(req, ENOMEM);
+    tevent_req_post(req, ev);
+    return req;
+}
+
+static void sdap_get_generic_done(struct sdap_op *op,
+                                  struct sdap_msg *reply,
+                                  int error, void *pvt);
+
+static void sdap_get_generic_step(struct tevent_req *subreq)
+{
+    struct tevent_req *req;
+    struct sdap_get_generic_state *state;
+    int ret;
+    int msgid;
+    int ldap_ret;
+
+    req = tevent_req_callback_data(subreq, struct tevent_req);
+    state = tevent_req_data(req, struct sdap_get_generic_state);
+
+    ret = ldap_search_ext_recv(subreq, &ldap_ret, &msgid);
+    talloc_zfree(subreq);
+    if (ret != EOK) goto fail;
 
     ret = sdap_op_add(state, state->ev, state->sh, msgid,
                       sdap_get_generic_done, req,
@@ -838,15 +964,12 @@ struct tevent_req *sdap_get_generic_send(TALLOC_CTX *memctx,
         goto fail;
     }
 
-    return req;
+    return;
 
 fail:
     tevent_req_error(req, ret);
-    tevent_req_post(req, ev);
-    return req;
 }
 
-
 static void sdap_get_generic_done(struct sdap_op *op,
                                  struct sdap_msg *reply,
                                  int error, void *pvt)
diff --git a/src/providers/ldap/sdap_async_connection.c b/src/providers/ldap/sdap_async_connection.c
index 806bd5f..dcaf578 100644
--- a/src/providers/ldap/sdap_async_connection.c
+++ b/src/providers/ldap/sdap_async_connection.c
@@ -24,6 +24,7 @@
 #include "util/util.h"
 #include "util/sss_krb5.h"
 #include "providers/ldap/sdap_async_private.h"
+#include "providers/ldap/ldap_req_wrap.h"
 
 #define LDAP_X_SSSD_PASSWORD_EXPIRED 0x555D
 
@@ -40,9 +41,7 @@ struct sdap_connect_state {
     int result;
 };
 
-static void sdap_connect_done(struct sdap_op *op,
-                              struct sdap_msg *reply,
-                              int error, void *pvt);
+static void sdap_connect_tls_done(struct tevent_req *subreq);
 
 struct tevent_req *sdap_connect_send(TALLOC_CTX *memctx,
                                      struct tevent_context *ev,
@@ -51,14 +50,12 @@ struct tevent_req *sdap_connect_send(TALLOC_CTX *memctx,
                                      bool use_start_tls)
 {
     struct tevent_req *req;
+    struct tevent_req *subreq;
     struct sdap_connect_state *state;
     struct timeval tv;
     int ver;
     int lret;
-    int optret;
     int ret = EOK;
-    int msgid;
-    char *errmsg = NULL;
     bool ldap_referrals;
 
     req = tevent_req_create(memctx, &state, struct sdap_connect_state);
@@ -145,37 +142,12 @@ struct tevent_req *sdap_connect_send(TALLOC_CTX *memctx,
 
     DEBUG(4, ("Executing START TLS\n"));
 
-    lret = ldap_start_tls(state->sh->ldap, NULL, NULL, &msgid);
-    if (lret != LDAP_SUCCESS) {
-        optret = ldap_get_option(state->sh->ldap,
-                                 SDAP_DIAGNOSTIC_MESSAGE,
-                                 (void*)&errmsg);
-        if (optret == LDAP_SUCCESS) {
-            DEBUG(3, ("ldap_start_tls failed: [%s] [%s]\n",
-                      ldap_err2string(lret),
-                      errmsg));
-            sss_log(SSS_LOG_ERR, "Could not start TLS. %s", errmsg);
-            ldap_memfree(errmsg);
-        }
-        else {
-            DEBUG(3, ("ldap_start_tls failed: [%s]\n",
-                      ldap_err2string(lret)));
-            sss_log(SSS_LOG_ERR, "Could not start TLS. "
-                                 "Check for certificate issues.");
-        }
-        goto fail;
-    }
-
-    ret = sdap_set_connected(state->sh, state->ev);
-    if (ret) goto fail;
-
-    /* FIXME: get timeouts from configuration, for now 5 secs. */
-    ret = sdap_op_add(state, ev, state->sh, msgid,
-                      sdap_connect_done, req, 5, &state->op);
-    if (ret) {
-        DEBUG(1, ("Failed to set up operation!\n"));
+    subreq = ldap_start_tls_send(state, ev, state->sh, NULL, NULL);
+    if (!subreq) {
+        ret = ENOMEM;
         goto fail;
     }
+    tevent_req_set_callback(subreq, sdap_connect_tls_done, req);
 
     return req;
 
@@ -195,6 +167,41 @@ fail:
 
 static void sdap_connect_done(struct sdap_op *op,
                               struct sdap_msg *reply,
+                              int error, void *pvt);
+
+static void sdap_connect_tls_done(struct tevent_req *subreq)
+{
+    struct tevent_req *req;
+    struct sdap_connect_state *state;
+    int ret;
+    int msgid;
+
+    req = tevent_req_callback_data(subreq, struct tevent_req);
+    state = tevent_req_data(req, struct sdap_connect_state);
+
+    ret = ldap_start_tls_recv(subreq, NULL, &msgid);
+    talloc_zfree(subreq);
+    if (ret != EOK) goto fail;
+
+    ret = sdap_set_connected(state->sh, state->ev);
+    if (!ret != EOK) goto fail;
+
+    /* FIXME: get timeouts from configuration, for now 5 secs. */
+    ret = sdap_op_add(state, state->ev, state->sh, msgid,
+                      sdap_connect_done, req, 5, &state->op);
+    if (ret != EOK) {
+        DEBUG(1, ("Failed to set up operation!\n"));
+        goto fail;
+    }
+
+    return;
+
+fail:
+    tevent_req_error(req, ret);
+}
+
+static void sdap_connect_done(struct sdap_op *op,
+                              struct sdap_msg *reply,
                               int error, void *pvt)
 {
     struct tevent_req *req = talloc_get_type(pvt, struct tevent_req);
@@ -289,9 +296,7 @@ struct simple_bind_state {
     int result;
 };
 
-static void simple_bind_done(struct sdap_op *op,
-                             struct sdap_msg *reply,
-                             int error, void *pvt);
+static void simple_bind_step(struct tevent_req *subreq);
 
 static struct tevent_req *simple_bind_send(TALLOC_CTX *memctx,
                                            struct tevent_context *ev,
@@ -302,9 +307,8 @@ static struct tevent_req *simple_bind_send(TALLOC_CTX *memctx,
     struct tevent_req *req;
     struct simple_bind_state *state;
     int ret = EOK;
-    int msgid;
-    int ldap_err;
     LDAPControl *request_controls[2];
+    struct tevent_req *subreq;
 
     req = tevent_req_create(memctx, &state, struct simple_bind_state);
     if (!req) return NULL;
@@ -330,47 +334,70 @@ static struct tevent_req *simple_bind_send(TALLOC_CTX *memctx,
 
     DEBUG(4, ("Executing simple bind as: %s\n", state->user_dn));
 
-    ret = ldap_sasl_bind(state->sh->ldap, state->user_dn, LDAP_SASL_SIMPLE,
-                         state->pw, request_controls, NULL, &msgid);
+    subreq = ldap_sasl_bind_send(state, ev, sh, user_dn, LDAP_SASL_SIMPLE, pw,
+                                 request_controls, NULL);
     ldap_control_free(request_controls[0]);
-    if (ret == -1 || msgid == -1) {
-        ret = ldap_get_option(state->sh->ldap,
-                              LDAP_OPT_RESULT_CODE, &ldap_err);
-        if (ret != LDAP_OPT_SUCCESS) {
-            DEBUG(1, ("ldap_bind failed (couldn't get ldap error)\n"));
-            ret = LDAP_LOCAL_ERROR;
-        } else {
-            DEBUG(1, ("ldap_bind failed (%d)[%s]\n",
-                      ldap_err, ldap_err2string(ldap_err)));
-            ret = ldap_err;
-        }
+
+    if (!subreq) goto fail;
+    tevent_req_set_callback(subreq, simple_bind_step, req);
+
+    return req;
+
+fail:
+    if (ret == LDAP_SERVER_DOWN) {
+        tevent_req_error(req, ETIMEDOUT);
+    } else {
+        tevent_req_error(req, EIO);
+    }
+    tevent_req_post(req, ev);
+    return req;
+}
+
+static void simple_bind_done(struct sdap_op *op,
+                             struct sdap_msg *reply,
+                             int error, void *pvt);
+
+static void simple_bind_step(struct tevent_req *subreq)
+{
+    struct tevent_req *req;
+    struct simple_bind_state *state;
+    int ret;
+    int msgid;
+    int ldap_ret;
+
+    req = tevent_req_callback_data(subreq, struct tevent_req);
+    state = tevent_req_data(req, struct simple_bind_state);
+
+    ret = ldap_sasl_bind_recv(subreq, &ldap_ret, &msgid);
+    talloc_zfree(subreq);
+    if (ret == ENOMEM) {
+        DEBUG(1, ("out of memory\n"));
+    } else if (ret == EIO) {
+        DEBUG(1, ("ldap_bind failed (%d)[%s]\n", ldap_ret,
+                  ldap_err2string(ldap_ret)));
+        ret = (ldap_ret == LDAP_SERVER_DOWN) ? ETIMEDOUT : EIO;
         goto fail;
     }
     DEBUG(8, ("ldap simple bind sent, msgid = %d\n", msgid));
 
-    if (!sh->connected) {
-        ret = sdap_set_connected(sh, ev);
+    if (!state->sh->connected) {
+        ret = sdap_set_connected(state->sh, state->ev);
         if (ret) goto fail;
     }
 
     /* FIXME: get timeouts from configuration, for now 5 secs. */
-    ret = sdap_op_add(state, ev, sh, msgid,
+    ret = sdap_op_add(state, state->ev, state->sh, msgid,
                       simple_bind_done, req, 5, &state->op);
     if (ret) {
         DEBUG(1, ("Failed to set up operation!\n"));
+        ret = EIO;
         goto fail;
     }
 
-    return req;
+    tevent_req_done(req);
 
 fail:
-    if (ret == LDAP_SERVER_DOWN) {
-        tevent_req_error(req, ETIMEDOUT);
-    } else {
-        tevent_req_error(req, EIO);
-    }
-    tevent_req_post(req, ev);
-    return req;
+    tevent_req_error(req, ret);
 }
 
 static void simple_bind_done(struct sdap_op *op,
@@ -501,6 +528,7 @@ struct sasl_bind_state {
 
 static int sdap_sasl_interact(LDAP *ld, unsigned flags,
                               void *defaults, void *interact);
+static void sasl_bind_interactive_done(struct tevent_req *subreq);
 
 static struct tevent_req *sasl_bind_send(TALLOC_CTX *memctx,
                                          struct tevent_context *ev,
@@ -510,8 +538,8 @@ static struct tevent_req *sasl_bind_send(TALLOC_CTX *memctx,
                                          struct berval *sasl_cred)
 {
     struct tevent_req *req;
+    struct tevent_req *subreq;
     struct sasl_bind_state *state;
-    int ret = EOK;
 
     req = tevent_req_create(memctx, &state, struct sasl_bind_state);
     if (!req) return NULL;
@@ -525,36 +553,45 @@ static struct tevent_req *sasl_bind_send(TALLOC_CTX *memctx,
     DEBUG(4, ("Executing sasl bind mech: %s, user: %s\n",
               sasl_mech, sasl_user));
 
-    /* FIXME: Warning, this is a sync call!
-     * No async variant exist in openldap libraries yet */
-
-    ret = ldap_sasl_interactive_bind_s(state->sh->ldap, NULL,
-                                       sasl_mech, NULL, NULL,
-                                       LDAP_SASL_QUIET,
-                                       (*sdap_sasl_interact), state);
-    state->result = ret;
-    if (ret != LDAP_SUCCESS) {
-        DEBUG(1, ("ldap_sasl_bind failed (%d)[%s]\n",
-                  ret, ldap_err2string(ret)));
-        goto fail;
-    }
-
-    if (!sh->connected) {
-        ret = sdap_set_connected(sh, ev);
-        if (ret) goto fail;
+    subreq = ldap_sasl_interactive_bind_send(state, ev, sh, NULL, sasl_mech,
+                                             NULL, NULL, LDAP_SASL_QUIET,
+                                             (*sdap_sasl_interact), state);
+    if (!subreq) {
+        tevent_req_error(req, ENOMEM);
+        tevent_req_post(req, ev);
+        return req;
     }
+    tevent_req_set_callback(subreq, sasl_bind_interactive_done, req);
 
-    tevent_req_post(req, ev);
     return req;
+}
 
-fail:
-    if (ret == LDAP_SERVER_DOWN) {
-        tevent_req_error(req, ETIMEDOUT);
-    } else {
-        tevent_req_error(req, EIO);
+static void sasl_bind_interactive_done(struct tevent_req *subreq)
+{
+    struct tevent_req *req;
+    struct sasl_bind_state *state;
+    int ret;
+
+    req = tevent_req_callback_data(subreq, struct tevent_req);
+    state = tevent_req_data(req, struct sasl_bind_state);
+
+    ret = ldap_sasl_interactive_bind_recv(subreq);
+    talloc_zfree(subreq);
+
+    if (ret != EOK) {
+        tevent_req_error(req, ret);
+        return;
     }
-    tevent_req_post(req, ev);
-    return req;
+
+    if (!state->sh->connected) {
+        ret = sdap_set_connected(state->sh, state->ev);
+        if (ret != EOK) {
+            tevent_req_error(req, EIO);
+            return;
+        }
+    }
+
+    tevent_req_done(req);
 }
 
 static int sdap_sasl_interact(LDAP *ld, unsigned flags,
diff --git a/src/providers/ldap/sdap_async_private.h b/src/providers/ldap/sdap_async_private.h
index bc897fd..0f8acd8 100644
--- a/src/providers/ldap/sdap_async_private.h
+++ b/src/providers/ldap/sdap_async_private.h
@@ -30,6 +30,14 @@ struct sdap_handle *sdap_handle_create(TALLOC_CTX *memctx);
 
 void sdap_ldap_result(struct tevent_context *ev, struct tevent_fd *fde,
                       uint16_t flags, void *pvt);
+void set_fd_retry_cb(struct sdap_handle *sh,
+                     fd_wakeup_callback_t *cb, void *cb_data);
+void sdap_async_ldap_result(struct tevent_context *ev,
+                            struct tevent_fd *fde,
+                            uint16_t flags, void *pvt);
+void sdap_add_timeout_watcher(struct ldap_cb_data *cb_data,
+                              struct fd_event_item *fd_event_item);
+
 
 int setup_ldap_connection_callbacks(struct sdap_handle *sh,
                                     struct tevent_context *ev);
diff --git a/src/providers/ldap/sdap_fd_events.c b/src/providers/ldap/sdap_fd_events.c
index 347cf8b..057efbb 100644
--- a/src/providers/ldap/sdap_fd_events.c
+++ b/src/providers/ldap/sdap_fd_events.c
@@ -61,6 +61,15 @@ int remove_ldap_connection_callbacks(struct sdap_handle *sh)
 }
 
 #ifdef HAVE_LDAP_CONNCB
+void set_fd_retry_cb(struct sdap_handle *sh,
+                     fd_wakeup_callback_t *fd_cb, void *fd_cb_data)
+{
+    struct ldap_cb_data *cb_data;
+
+    cb_data = talloc_get_type(sh->sdap_fd_events->conncb->lc_arg, struct ldap_cb_data);
+    cb_data->wakeup_cb = fd_cb;
+    cb_data->wakeup_cb_data = fd_cb_data;
+}
 
 static int remove_connection_callback(TALLOC_CTX *mem_ctx)
 {
@@ -79,6 +88,24 @@ static int remove_connection_callback(TALLOC_CTX *mem_ctx)
     return EOK;
 }
 
+static int request_guard_destructor(struct request_guard *guard)
+{
+    if (guard->ptr) {
+        guard->ptr->guard = NULL;
+        talloc_free(guard->ptr);
+    }
+    return 0;
+}
+
+static int fd_event_item_destructor(struct fd_event_item *fd_event_item)
+{
+    if (fd_event_item->guard) {
+        fd_event_item->guard->ptr = NULL;
+    }
+    DLIST_REMOVE(fd_event_item->cb_data->fd_list, fd_event_item);
+    return 0;
+}
+
 static int sdap_ldap_connect_callback_add(LDAP *ld, Sockbuf *sb,
                                           LDAPURLDesc *srv,
                                           struct sockaddr *addr,
@@ -87,6 +114,7 @@ static int sdap_ldap_connect_callback_add(LDAP *ld, Sockbuf *sb,
     int ret;
     ber_socket_t ber_fd;
     struct fd_event_item *fd_event_item;
+    struct request_guard *guard;
     struct ldap_cb_data *cb_data = talloc_get_type(ctx->lc_arg,
                                                    struct ldap_cb_data);
 
@@ -101,7 +129,7 @@ static int sdap_ldap_connect_callback_add(LDAP *ld, Sockbuf *sb,
         DEBUG(1, ("ber_sockbuf_ctrl failed.\n"));
         return EINVAL;
     }
-    DEBUG(9, ("New LDAP connection to [%s] with fd [%d].\n",
+    DEBUG(5, ("New LDAP connection to [%s] with fd [%d].\n",
               ldap_url_desc2str(srv), ber_fd));
 
     fd_event_item = talloc_zero(cb_data, struct fd_event_item);
@@ -111,16 +139,37 @@ static int sdap_ldap_connect_callback_add(LDAP *ld, Sockbuf *sb,
     }
 
     fd_event_item->fde = tevent_add_fd(cb_data->ev, fd_event_item, ber_fd,
+#ifdef LDAP_OPT_CONNECT_ASYNC
+                                       TEVENT_FD_READ | TEVENT_FD_WRITE,
+                                       sdap_async_ldap_result,
+#else
                                        TEVENT_FD_READ, sdap_ldap_result,
-                                       cb_data->sh);
+#endif
+                                       cb_data);
     if (fd_event_item->fde == NULL) {
         DEBUG(1, ("tevent_add_fd failed.\n"));
         talloc_free(fd_event_item);
         return ENOMEM;
     }
     fd_event_item->fd = ber_fd;
+    fd_event_item->cb_data = cb_data;
+
+    fd_event_item->fd_wakeup_cb = cb_data->wakeup_cb;
+    fd_event_item->fd_wakeup_cb_data = cb_data->wakeup_cb_data;
+    if (fd_event_item->fd_wakeup_cb) {
+        guard = talloc(fd_event_item->fd_wakeup_cb_data, struct request_guard);
+        if (guard == NULL) {
+            talloc_free(fd_event_item);
+            return ENOMEM;
+        }
+        guard->ptr = fd_event_item;
+        fd_event_item->guard = guard;
+        talloc_set_destructor(guard, request_guard_destructor);
+    }
 
     DLIST_ADD(cb_data->fd_list, fd_event_item);
+    talloc_set_destructor(fd_event_item, fd_event_item_destructor);
+    sdap_add_timeout_watcher(cb_data, fd_event_item);
 
     return LDAP_SUCCESS;
 }
@@ -161,7 +210,15 @@ static void sdap_ldap_connect_callback_del(LDAP *ld, Sockbuf *sb,
     return;
 }
 
-#else
+#else /* !HAVE_LDAP_CONNCB */
+
+void set_fd_retry_cb(struct sdap_handle *sh,
+                     fd_wakeup_callback_t *fd_cb, void *fd_cb_data)
+{
+    (void)sh;
+    (void)fd_cb;
+    (void)fd_cb_data;
+}
 
 static int sdap_install_ldap_callbacks(struct sdap_handle *sh,
                                        struct tevent_context *ev)
@@ -199,7 +256,7 @@ static int sdap_install_ldap_callbacks(struct sdap_handle *sh,
     return EOK;
 }
 
-#endif
+#endif /* HAVE_LDAP_CONNCB */
 
 
 errno_t setup_ldap_connection_callbacks(struct sdap_handle *sh,
@@ -245,6 +302,15 @@ errno_t setup_ldap_connection_callbacks(struct sdap_handle *sh,
         goto fail;
     }
 
+#ifdef LDAP_OPT_CONNECT_ASYNC
+    ret = ldap_set_option(sh->ldap, LDAP_OPT_CONNECT_ASYNC, LDAP_OPT_ON);
+    if (ret != LDAP_OPT_SUCCESS) {
+        DEBUG(1, ("Failed to set connection as asynchronous\n"));
+        ret = EFAULT;
+        goto fail;
+    }
+#endif
+
     talloc_set_destructor((TALLOC_CTX *) sh->sdap_fd_events->conncb,
                           remove_connection_callback);
 
@@ -253,10 +319,10 @@ errno_t setup_ldap_connection_callbacks(struct sdap_handle *sh,
 fail:
     talloc_zfree(sh->sdap_fd_events);
     return ret;
-#else
+#else /* !HAVE_LDAP_CONNCB */
     DEBUG(9, ("LDAP connection callbacks are not supported.\n"));
     return EOK;
-#endif
+#endif /* HAVE_LDAP_CONNCB */
 }
 
 errno_t sdap_set_connected(struct sdap_handle *sh, struct tevent_context *ev)
-- 
1.6.2.5

