From e68fceb53577805ac9b13b8ccc25f6effe149b95 Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhrozek@redhat.com>
Date: Wed, 28 Apr 2010 19:26:04 +0200
Subject: [PATCH] Use all available servers in LDAP provider

---
 src/providers/ldap/ldap_auth.c             |   39 +++++++++++++++---
 src/providers/ldap/sdap_async.c            |    6 ++-
 src/providers/ldap/sdap_async_connection.c |   61 ++++++++++++++++++++++++---
 3 files changed, 92 insertions(+), 14 deletions(-)

diff --git a/src/providers/ldap/ldap_auth.c b/src/providers/ldap/ldap_auth.c
index 83b175d..d2840d6 100644
--- a/src/providers/ldap/ldap_auth.c
+++ b/src/providers/ldap/ldap_auth.c
@@ -478,6 +478,7 @@ struct auth_state {
     struct fo_server *srv;
 };
 
+static struct tevent_req *auth_get_server(struct tevent_req *req);
 static void auth_resolve_done(struct tevent_req *subreq);
 static void auth_connect_done(struct tevent_req *subreq);
 static void auth_get_user_dn_done(struct tevent_req *subreq);
@@ -489,7 +490,7 @@ static struct tevent_req *auth_send(TALLOC_CTX *memctx,
                                     const char *username,
                                     struct dp_opt_blob password)
 {
-    struct tevent_req *req, *subreq;
+    struct tevent_req *req;
     struct auth_state *state;
 
     req = tevent_req_create(memctx, &state, struct auth_state);
@@ -501,10 +502,7 @@ static struct tevent_req *auth_send(TALLOC_CTX *memctx,
     state->password = password;
     state->srv = NULL;
 
-    subreq = be_resolve_server_send(state, ev, ctx->be, ctx->service->name);
-    if (!subreq) goto fail;
-
-    tevent_req_set_callback(subreq, auth_resolve_done, req);
+    if (!auth_get_server(req)) goto fail;
 
     return req;
 
@@ -513,6 +511,27 @@ fail:
     return NULL;
 }
 
+static struct tevent_req *auth_get_server(struct tevent_req *req)
+{
+    struct tevent_req *next_req;
+    struct auth_state *state = tevent_req_data(req,
+                                               struct auth_state);
+
+     /* NOTE: this call may cause service->uri to be refreshed
+      * with a new valid server. Do not use service->uri before */
+    next_req = be_resolve_server_send(state,
+                                      state->ev,
+                                      state->ctx->be,
+                                      state->ctx->service->name);
+    if (!next_req) {
+        DEBUG(1, ("be_resolve_server_send failed.\n"));
+        return NULL;
+    }
+
+    tevent_req_set_callback(next_req, auth_resolve_done, req);
+    return next_req;
+}
+
 static void auth_resolve_done(struct tevent_req *subreq)
 {
     struct tevent_req *req = tevent_req_callback_data(subreq,
@@ -524,7 +543,9 @@ static void auth_resolve_done(struct tevent_req *subreq)
     ret = be_resolve_server_recv(subreq, &state->srv);
     talloc_zfree(subreq);
     if (ret) {
-        tevent_req_error(req, ret);
+        /* all servers have been tried and none
+         * was found good, go offline */
+        tevent_req_error(req, EIO);
         return;
     }
 
@@ -553,6 +574,12 @@ static void auth_connect_done(struct tevent_req *subreq)
             /* mark this server as bad if connection failed */
             fo_set_port_status(state->srv, PORT_NOT_WORKING);
         }
+        if (ret == ETIMEDOUT) {
+            if (auth_get_server(req) == NULL) {
+                tevent_req_error(req, ENOMEM);
+            }
+            return;
+        }
 
         tevent_req_error(req, ret);
         return;
diff --git a/src/providers/ldap/sdap_async.c b/src/providers/ldap/sdap_async.c
index a25c50b..f30f116 100644
--- a/src/providers/ldap/sdap_async.c
+++ b/src/providers/ldap/sdap_async.c
@@ -768,7 +768,11 @@ struct tevent_req *sdap_get_generic_send(TALLOC_CTX *memctx,
                            false, NULL, NULL, NULL, 0, &msgid);
     if (lret != LDAP_SUCCESS) {
         DEBUG(3, ("ldap_search_ext failed: %s\n", ldap_err2string(lret)));
-        ret = EIO;
+        if (lret == LDAP_SERVER_DOWN) {
+            ret = ETIMEDOUT;
+        } else {
+            ret = EIO;
+        }
         goto fail;
     }
     DEBUG(8, ("ldap_search_ext called, msgid = %d\n", msgid));
diff --git a/src/providers/ldap/sdap_async_connection.c b/src/providers/ldap/sdap_async_connection.c
index 9871dc2..e30f391 100644
--- a/src/providers/ldap/sdap_async_connection.c
+++ b/src/providers/ldap/sdap_async_connection.c
@@ -813,6 +813,7 @@ struct sdap_cli_connect_state {
     struct tevent_context *ev;
     struct sdap_options *opts;
     struct sdap_service *service;
+    struct be_ctx *be;
 
     bool use_rootdse;
     struct sysdb_attrs *rootdse;
@@ -822,6 +823,7 @@ struct sdap_cli_connect_state {
     struct fo_server *srv;
 };
 
+static int sdap_cli_resolve_next(struct tevent_req *req);
 static void sdap_cli_resolve_done(struct tevent_req *subreq);
 static void sdap_cli_connect_done(struct tevent_req *subreq);
 static void sdap_cli_rootdse_step(struct tevent_req *req);
@@ -838,8 +840,9 @@ struct tevent_req *sdap_cli_connect_send(TALLOC_CTX *memctx,
                                          struct sdap_service *service,
                                          struct sysdb_attrs **rootdse)
 {
-    struct tevent_req *req, *subreq;
     struct sdap_cli_connect_state *state;
+    struct tevent_req *req;
+    int ret;
 
     req = tevent_req_create(memctx, &state, struct sdap_cli_connect_state);
     if (!req) return NULL;
@@ -847,6 +850,7 @@ struct tevent_req *sdap_cli_connect_send(TALLOC_CTX *memctx,
     state->ev = ev;
     state->opts = opts;
     state->service = service;
+    state->be = be;
     state->srv = NULL;
 
     if (rootdse) {
@@ -857,16 +861,30 @@ struct tevent_req *sdap_cli_connect_send(TALLOC_CTX *memctx,
         state->rootdse = NULL;
     }
 
+    ret = sdap_cli_resolve_next(req);
+    if (ret) {
+        tevent_req_error(req, ret);
+        tevent_req_post(req, ev);
+    }
+    return req;
+}
+
+static int sdap_cli_resolve_next(struct tevent_req *req)
+{
+    struct sdap_cli_connect_state *state = tevent_req_data(req,
+                                             struct sdap_cli_connect_state);
+    struct tevent_req *subreq;
+
     /* NOTE: this call may cause service->uri to be refreshed
      * with a new valid server. Do not use service->uri before */
-    subreq = be_resolve_server_send(state, ev, be, service->name);
+    subreq = be_resolve_server_send(state, state->ev,
+                                    state->be, state->service->name);
     if (!subreq) {
-        talloc_zfree(req);
-        return NULL;
+        return ENOMEM;
     }
-    tevent_req_set_callback(subreq, sdap_cli_resolve_done, req);
 
-    return req;
+    tevent_req_set_callback(subreq, sdap_cli_resolve_done, req);
+    return EOK;
 }
 
 static void sdap_cli_resolve_done(struct tevent_req *subreq)
@@ -889,7 +907,7 @@ static void sdap_cli_resolve_done(struct tevent_req *subreq)
     subreq = sdap_connect_send(state, state->ev, state->opts,
                                state->service->uri,
                                dp_opt_get_bool(state->opts->basic,
-                                                            SDAP_ID_TLS));
+                                               SDAP_ID_TLS));
     if (!subreq) {
         tevent_req_error(req, ENOMEM);
         return;
@@ -909,6 +927,15 @@ static void sdap_cli_connect_done(struct tevent_req *subreq)
     ret = sdap_connect_recv(subreq, state, &state->sh);
     talloc_zfree(subreq);
     if (ret) {
+        if (ret == ETIMEDOUT) { /* retry another server */
+            fo_set_port_status(state->srv, PORT_NOT_WORKING);
+            ret = sdap_cli_resolve_next(req);
+            if (ret != EOK) {
+                tevent_req_error(req, ret);
+            }
+            return;
+        }
+
         tevent_req_error(req, ret);
         return;
     }
@@ -978,6 +1005,15 @@ static void sdap_cli_rootdse_done(struct tevent_req *subreq)
     ret = sdap_get_rootdse_recv(subreq, state, &state->rootdse);
     talloc_zfree(subreq);
     if (ret) {
+        if (ret == ETIMEDOUT) { /* retry another server */
+            fo_set_port_status(state->srv, PORT_NOT_WORKING);
+            ret = sdap_cli_resolve_next(req);
+            if (ret != EOK) {
+                tevent_req_error(req, ret);
+            }
+            return;
+        }
+
         tevent_req_error(req, ret);
         return;
     }
@@ -1030,12 +1066,23 @@ static void sdap_cli_kinit_done(struct tevent_req *subreq)
 {
     struct tevent_req *req = tevent_req_callback_data(subreq,
                                                       struct tevent_req);
+    struct sdap_cli_connect_state *state = tevent_req_data(req,
+                                             struct sdap_cli_connect_state);
     enum sdap_result result;
     int ret;
 
     ret = sdap_kinit_recv(subreq, &result);
     talloc_zfree(subreq);
     if (ret) {
+        if (ret == ETIMEDOUT) { /* child timed out, retry another server */
+            fo_set_port_status(state->srv, PORT_NOT_WORKING);
+            ret = sdap_cli_resolve_next(req);
+            if (ret != EOK) {
+                tevent_req_error(req, ret);
+            }
+            return;
+        }
+
         tevent_req_error(req, ret);
         return;
     }
-- 
1.6.6.1

