URL: https://github.com/SSSD/sssd/pull/790
Author: thalman
 Title: #790: Lookahead resolving of host names
Action: opened

PR body:
"""
This is a continuation of issues #3973 and #3974.

If krb5_kdcinfo_lookahead is set to certain limit, then resolving of SRV record 
is
stopped once we reach that limit.

The krb5_kdcinfo_lookahead option contains two numbers separated by colon (for
example 5:2). First number specifies number of primary servers that we will use 
and
write to kdc info file. The second number specifies number of backup servers  

Resolves:
https://pagure.io/SSSD/sssd/issue/3975
"""

To pull the PR as Git branch:
git remote add ghsssd https://github.com/SSSD/sssd
git fetch ghsssd pull/790/head:pr790
git checkout pr790
From 501df3344f81e40d66d15f53b9584164ddf34b07 Mon Sep 17 00:00:00 2001
From: Tomas Halman <thal...@redhat.com>
Date: Fri, 15 Mar 2019 10:27:50 +0100
Subject: [PATCH 1/5] krb5: Lookahead resolving of host names

The caller that initializes
the fail over service (maybe with be_fo_add_service) should provide
a hint with the value of the lookahead option. Then, if a request for
server resolution is triggered, the fail over code would resolve a server
and afterwards check if enough fo_server entries with a valid hostname
in the struct server_common structure. If not, the request would
check if any of the fo_server structures represents a SRV query and
try to resolve the query to receive more host names.

Resolves:
https://pagure.io/SSSD/sssd/issue/3975
---
 src/providers/ad/ad_common.c     | 15 +++++++++++++++
 src/providers/ad/ad_opts.c       |  1 +
 src/providers/ipa/ipa_common.c   | 15 +++++++++++++++
 src/providers/ipa/ipa_opts.c     |  2 ++
 src/providers/krb5/krb5_common.c | 25 +++++++++++++++++++++++++
 src/providers/krb5/krb5_common.h |  5 +++++
 src/providers/krb5/krb5_opts.c   |  1 +
 src/providers/ldap/ldap_opts.c   |  1 +
 src/providers/ldap/sdap.h        |  1 +
 9 files changed, 66 insertions(+)

diff --git a/src/providers/ad/ad_common.c b/src/providers/ad/ad_common.c
index 5e7bd02f51..55452b4741 100644
--- a/src/providers/ad/ad_common.c
+++ b/src/providers/ad/ad_common.c
@@ -1305,6 +1305,21 @@ ad_get_auth_options(TALLOC_CTX *mem_ctx,
     DEBUG(SSSDBG_CONF_SETTINGS, "Option %s set to %s\n",
           krb5_options[KRB5_USE_KDCINFO].opt_name,
           ad_opts->service->krb5_service->write_kdcinfo ? "true" : "false");
+    if (ad_opts->service->krb5_service->write_kdcinfo) {
+        ret = sss_krb5_parse_lookahead(
+            dp_opt_get_string(krb5_options, KRB5_KDCINFO_LOOKAHEAD),
+            &ad_opts->service->krb5_service->lookahead_primary,
+            &ad_opts->service->krb5_service->lookahead_backup);
+        if (ret != EOK) {
+            DEBUG(SSSDBG_MINOR_FAILURE,
+                  "Could not parse %s parameter\n",
+                  krb5_options[KRB5_KDCINFO_LOOKAHEAD].opt_name);
+        }
+        DEBUG(SSSDBG_CONF_SETTINGS, "Option %s set to %d:%d\n",
+              krb5_options[KRB5_KDCINFO_LOOKAHEAD].opt_name,
+              ad_opts->service->krb5_service->lookahead_primary,
+              ad_opts->service->krb5_service->lookahead_backup);
+    }
 
     *_opts = talloc_steal(mem_ctx, krb5_options);
 
diff --git a/src/providers/ad/ad_opts.c b/src/providers/ad/ad_opts.c
index 9ca18c40e1..30bb333e1d 100644
--- a/src/providers/ad/ad_opts.c
+++ b/src/providers/ad/ad_opts.c
@@ -110,6 +110,7 @@ struct dp_option ad_def_ldap_opts[] = {
     { "krb5_realm", DP_OPT_STRING, NULL_STRING, NULL_STRING },
     { "krb5_canonicalize", DP_OPT_BOOL, BOOL_FALSE, BOOL_FALSE },
     { "krb5_use_kdcinfo", DP_OPT_BOOL, BOOL_TRUE, BOOL_TRUE },
+    { "krb5_kdcinfo_lookahead", DP_OPT_STRING, NULL_STRING, NULL_STRING },
     { "ldap_pwd_policy", DP_OPT_STRING, { "none" }, NULL_STRING },
     { "ldap_referrals", DP_OPT_BOOL, BOOL_FALSE, BOOL_FALSE },
     { "account_cache_expiration", DP_OPT_NUMBER, { .number = 0 }, NULL_NUMBER },
diff --git a/src/providers/ipa/ipa_common.c b/src/providers/ipa/ipa_common.c
index 1ed2e2203d..a510b4de96 100644
--- a/src/providers/ipa/ipa_common.c
+++ b/src/providers/ipa/ipa_common.c
@@ -801,6 +801,21 @@ int ipa_get_auth_options(struct ipa_options *ipa_opts,
     DEBUG(SSSDBG_CONF_SETTINGS, "Option %s set to %s\n",
           ipa_opts->auth[KRB5_USE_KDCINFO].opt_name,
           ipa_opts->service->krb5_service->write_kdcinfo ? "true" : "false");
+    if (ipa_opts->service->krb5_service->write_kdcinfo) {
+        ret = sss_krb5_parse_lookahead(
+            dp_opt_get_string(ipa_opts->auth, KRB5_KDCINFO_LOOKAHEAD),
+            &ipa_opts->service->krb5_service->lookahead_primary,
+            &ipa_opts->service->krb5_service->lookahead_backup);
+        if (ret != EOK) {
+            DEBUG(SSSDBG_MINOR_FAILURE,
+                  "Could not parse %s parameter\n",
+                  ipa_opts->auth[KRB5_KDCINFO_LOOKAHEAD].opt_name);
+        }
+        DEBUG(SSSDBG_CONF_SETTINGS, "Option %s set to %d:%d\n",
+              ipa_opts->auth[KRB5_KDCINFO_LOOKAHEAD].opt_name,
+              ipa_opts->service->krb5_service->lookahead_primary,
+              ipa_opts->service->krb5_service->lookahead_backup);
+    }
 
     *_opts = ipa_opts->auth;
     ret = EOK;
diff --git a/src/providers/ipa/ipa_opts.c b/src/providers/ipa/ipa_opts.c
index 313193fdb4..c38a7da0ed 100644
--- a/src/providers/ipa/ipa_opts.c
+++ b/src/providers/ipa/ipa_opts.c
@@ -122,6 +122,7 @@ struct dp_option ipa_def_ldap_opts[] = {
     { "krb5_realm", DP_OPT_STRING, NULL_STRING, NULL_STRING },
     { "krb5_canonicalize", DP_OPT_BOOL, BOOL_TRUE, BOOL_TRUE },
     { "krb5_use_kdcinfo", DP_OPT_BOOL, BOOL_TRUE, BOOL_TRUE },
+    { "krb5_kdcinfo_lookahead", DP_OPT_STRING, NULL_STRING, NULL_STRING },
     { "ldap_pwd_policy", DP_OPT_STRING, { "none" } , NULL_STRING },
     { "ldap_referrals", DP_OPT_BOOL, BOOL_TRUE, BOOL_TRUE },
     { "account_cache_expiration", DP_OPT_NUMBER, { .number = 0 }, NULL_NUMBER },
@@ -319,6 +320,7 @@ struct dp_option ipa_def_krb5_opts[] = {
     { "krb5_canonicalize", DP_OPT_BOOL, BOOL_TRUE, BOOL_TRUE },
     { "krb5_use_enterprise_principal", DP_OPT_BOOL, BOOL_FALSE, BOOL_FALSE },
     { "krb5_use_kdcinfo", DP_OPT_BOOL, BOOL_TRUE, BOOL_TRUE },
+    { "krb5_kdcinfo_lookahead", DP_OPT_STRING, NULL_STRING, NULL_STRING },
     { "krb5_map_user", DP_OPT_STRING, NULL_STRING, NULL_STRING },
     DP_OPTION_TERMINATOR
 };
diff --git a/src/providers/krb5/krb5_common.c b/src/providers/krb5/krb5_common.c
index 588ce5ee12..3a398489e5 100644
--- a/src/providers/krb5/krb5_common.c
+++ b/src/providers/krb5/krb5_common.c
@@ -390,6 +390,30 @@ errno_t sss_krb5_get_options(TALLOC_CTX *memctx, struct confdb_ctx *cdb,
     return ret;
 }
 
+errno_t sss_krb5_parse_lookahead(const char *param, int *primary, int *backup)
+{
+    int ret;
+    *primary = -1;
+    *backup = -1;
+
+    if (param == NULL)
+        return EOK;
+
+    if (strchr(param, ':')) {
+        ret = sscanf(param, "%d:%d", primary, backup);
+        if (ret != 2) {
+            return EINVAL;
+        }
+    } else {
+        ret = sscanf(param, "%d", primary);
+        if (ret != 1) {
+            return EINVAL;
+        }
+    }
+    return EOK;
+}
+
+
 static int remove_info_files_destructor(void *p)
 {
     int ret;
@@ -927,6 +951,7 @@ struct krb5_service *krb5_service_new(TALLOC_CTX *mem_ctx,
           realm,
           use_kdcinfo ? "true" : "false");
     service->write_kdcinfo = use_kdcinfo;
+
     service->be_ctx = be_ctx;
     return service;
 }
diff --git a/src/providers/krb5/krb5_common.h b/src/providers/krb5/krb5_common.h
index be541626b6..8d0386b259 100644
--- a/src/providers/krb5/krb5_common.h
+++ b/src/providers/krb5/krb5_common.h
@@ -59,6 +59,7 @@ enum krb5_opts {
     KRB5_CANONICALIZE,
     KRB5_USE_ENTERPRISE_PRINCIPAL,
     KRB5_USE_KDCINFO,
+    KRB5_KDCINFO_LOOKAHEAD,
     KRB5_MAP_USER,
 
     KRB5_OPTS
@@ -71,6 +72,8 @@ struct krb5_service {
     char *name;
     char *realm;
     bool write_kdcinfo;
+    int lookahead_primary;
+    int lookahead_backup;
     bool removal_callback_available;
 };
 
@@ -160,6 +163,8 @@ errno_t krb5_try_kdcip(struct confdb_ctx *cdb, const char *conf_path,
 errno_t sss_krb5_get_options(TALLOC_CTX *memctx, struct confdb_ctx *cdb,
                              const char *conf_path, struct dp_option **_opts);
 
+errno_t sss_krb5_parse_lookahead(const char *param, int *primary, int *backup);
+
 errno_t write_krb5info_file(struct krb5_service *krb5_service,
                             const char **server_list,
                             const char *service);
diff --git a/src/providers/krb5/krb5_opts.c b/src/providers/krb5/krb5_opts.c
index 6bec527678..05395e0f4b 100644
--- a/src/providers/krb5/krb5_opts.c
+++ b/src/providers/krb5/krb5_opts.c
@@ -42,6 +42,7 @@ struct dp_option default_krb5_opts[] = {
     { "krb5_canonicalize", DP_OPT_BOOL, BOOL_FALSE, BOOL_FALSE },
     { "krb5_use_enterprise_principal", DP_OPT_BOOL, BOOL_FALSE, BOOL_FALSE },
     { "krb5_use_kdcinfo", DP_OPT_BOOL, BOOL_TRUE, BOOL_TRUE },
+    { "krb5_kdcinfo_lookahead", DP_OPT_STRING, NULL_STRING, NULL_STRING },
     { "krb5_map_user", DP_OPT_STRING, NULL_STRING, NULL_STRING },
     DP_OPTION_TERMINATOR
 };
diff --git a/src/providers/ldap/ldap_opts.c b/src/providers/ldap/ldap_opts.c
index 2482d90eaf..dc56f07125 100644
--- a/src/providers/ldap/ldap_opts.c
+++ b/src/providers/ldap/ldap_opts.c
@@ -82,6 +82,7 @@ struct dp_option default_basic_opts[] = {
     { "krb5_realm", DP_OPT_STRING, NULL_STRING, NULL_STRING },
     { "krb5_canonicalize", DP_OPT_BOOL, BOOL_TRUE, BOOL_TRUE },
     { "krb5_use_kdcinfo", DP_OPT_BOOL, BOOL_TRUE, BOOL_TRUE },
+    { "krb5_kdcinfo_lookahead", DP_OPT_STRING, NULL_STRING, NULL_STRING },
     { "ldap_pwd_policy", DP_OPT_STRING, { "none" }, NULL_STRING },
     { "ldap_referrals", DP_OPT_BOOL, BOOL_TRUE, BOOL_TRUE },
     { "account_cache_expiration", DP_OPT_NUMBER, { .number = 0 }, NULL_NUMBER },
diff --git a/src/providers/ldap/sdap.h b/src/providers/ldap/sdap.h
index 76cc16e2ec..56290d6bc4 100644
--- a/src/providers/ldap/sdap.h
+++ b/src/providers/ldap/sdap.h
@@ -199,6 +199,7 @@ enum sdap_basic_opt {
     SDAP_KRB5_REALM,
     SDAP_KRB5_CANONICALIZE,
     SDAP_KRB5_USE_KDCINFO,
+    SDAP_KRB5_KDCINFO_LOOKAHEAD,
     SDAP_PWD_POLICY,
     SDAP_REFERRALS,
     SDAP_ACCOUNT_CACHE_EXPIRATION,

From c82f0d7705f9e3fcfbe5c60135d2b2556e457931 Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhro...@redhat.com>
Date: Thu, 21 Mar 2019 22:23:45 +0100
Subject: [PATCH 2/5] failover: stateless mode of resolve_srv request

The fail over requests typically not only resolve a server, but also
update internal state pointers such as last_tried_server. In this
patchset, we only want to trigger DNS SRV resolution with a later patch,
therefore we add a flag to the resolve_srv_send() request which skips
touching the last_tried_server.

Note that the 'meta' server pointer still must be touched because this
is what the "_srv_" identifier expands to.
---
 src/providers/fail_over.c | 28 ++++++++++++++++++++++------
 1 file changed, 22 insertions(+), 6 deletions(-)

diff --git a/src/providers/fail_over.c b/src/providers/fail_over.c
index eb6e65120f..37ea43fb73 100644
--- a/src/providers/fail_over.c
+++ b/src/providers/fail_over.c
@@ -995,10 +995,17 @@ static void fo_resolve_service_done(struct tevent_req *subreq);
 static bool fo_resolve_service_server(struct tevent_req *req);
 
 /* Forward declarations for SRV resolving */
+
+/* If the flag is set, resolve_srv_send will not
+ * touch any state, just resolve servers
+ */
+#define RESOLVE_SRV_FLG_STATELESS   0x001
+
 static struct tevent_req *
 resolve_srv_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
                     struct resolv_ctx *resolv, struct fo_ctx *ctx,
-                    struct fo_server *server);
+                    struct fo_server *server,
+                    int flags);
 static int
 resolve_srv_recv(struct tevent_req *req, struct fo_server **server);
 
@@ -1041,7 +1048,7 @@ fo_resolve_service_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
     if (fo_is_srv_lookup(server)) {
         /* Don't know the server yet, must do a SRV lookup */
         subreq = resolve_srv_send(state, ev, resolv,
-                                  ctx, server);
+                                  ctx, server, 0);
         if (subreq == NULL) {
             ret = ENOMEM;
             goto done;
@@ -1258,12 +1265,14 @@ struct resolve_srv_state {
     struct resolv_ctx *resolv;
     struct tevent_context *ev;
     struct fo_ctx *fo_ctx;
+    int flags;
 };
 
 static struct tevent_req *
 resolve_srv_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
                  struct resolv_ctx *resolv, struct fo_ctx *ctx,
-                 struct fo_server *server)
+                 struct fo_server *server,
+                 int flags)
 {
     int ret;
     struct tevent_req *req;
@@ -1280,6 +1289,7 @@ resolve_srv_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
     state->resolv = resolv;
     state->fo_ctx = ctx;
     state->meta = server->srv_data->meta;
+    state->flags = flags;
 
     status = get_srv_data_status(server->srv_data);
     DEBUG(SSSDBG_FUNC_DATA, "The status of SRV lookup is %s\n",
@@ -1440,10 +1450,16 @@ resolve_srv_done(struct tevent_req *subreq)
         state->out = state->meta->next;
 
         /* And remove meta server from the server list. It will be
-         * inserted again during srv collapse. */
+         * inserted again during srv collapse. Note that this needs
+         * to be done even in the stateless run.
+         */
         DLIST_REMOVE(state->service->server_list, state->meta);
-        if (state->service->last_tried_server == state->meta) {
-            state->service->last_tried_server = state->out;
+
+        if ((state->flags & RESOLVE_SRV_FLG_STATELESS) == false) {
+            /* Stateless runs do not alter service state */
+            if (state->service->last_tried_server == state->meta) {
+                state->service->last_tried_server = state->out;
+            }
         }
 
         set_srv_data_status(state->meta->srv_data, SRV_RESOLVED);

From 66ca6968be03d779e2da3d14a17f1b1fd51209a2 Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhro...@redhat.com>
Date: Thu, 21 Mar 2019 23:57:37 +0100
Subject: [PATCH 3/5] failover: Split notifying pending resolve requests into a
 reusable function

We'll want to add one more step to the service resolution request, so
let's split the part of the code that notifies concurrent callers to a
reusable function. Also, the return value of gethostbyname is saved in
state to be used later in the notify function.
---
 src/providers/fail_over.c | 48 +++++++++++++++++++++++++++------------
 1 file changed, 33 insertions(+), 15 deletions(-)

diff --git a/src/providers/fail_over.c b/src/providers/fail_over.c
index 37ea43fb73..6543af3882 100644
--- a/src/providers/fail_over.c
+++ b/src/providers/fail_over.c
@@ -986,12 +986,15 @@ struct resolve_service_state {
     struct tevent_context *ev;
     struct tevent_timer *timeout_handler;
     struct fo_ctx *fo_ctx;
+
+    int gethostbyname_ret;
 };
 
 static errno_t fo_resolve_service_activate_timeout(struct tevent_req *req,
             struct tevent_context *ev, const unsigned long timeout_seconds);
 static void fo_resolve_service_cont(struct tevent_req *subreq);
 static void fo_resolve_service_done(struct tevent_req *subreq);
+static void fo_resolve_service_notify(struct tevent_req *req);
 static bool fo_resolve_service_server(struct tevent_req *req);
 
 /* Forward declarations for SRV resolving */
@@ -1154,8 +1157,7 @@ fo_resolve_service_server(struct tevent_req *req)
             tevent_req_error(req, ENOMEM);
             return true;
         }
-        tevent_req_set_callback(subreq, fo_resolve_service_done,
-                                state->server->common);
+        tevent_req_set_callback(subreq, fo_resolve_service_done, req);
         fo_set_server_status(state->server, SERVER_RESOLVING_NAME);
         /* FALLTHROUGH */
         SSS_ATTRIBUTE_FALLTHROUGH;
@@ -1179,21 +1181,23 @@ fo_resolve_service_server(struct tevent_req *req)
 static void
 fo_resolve_service_done(struct tevent_req *subreq)
 {
-    struct server_common *common = tevent_req_callback_data(subreq,
-                                                        struct server_common);
+    struct tevent_req *req = tevent_req_callback_data(subreq,
+                                                      struct tevent_req);
+    struct resolve_service_state *state = tevent_req_data(req,
+                                        struct resolve_service_state);
+    struct server_common *common;
     int resolv_status;
-    struct resolve_service_request *request;
-    int ret;
 
+    common = state->server->common;
     if (common->rhostent != NULL) {
         talloc_zfree(common->rhostent);
     }
 
-    ret = resolv_gethostbyname_recv(subreq, common,
-                                    &resolv_status, NULL,
-                                    &common->rhostent);
+    state->gethostbyname_ret = resolv_gethostbyname_recv(subreq, common,
+                                                         &resolv_status, NULL,
+                                                         &common->rhostent);
     talloc_zfree(subreq);
-    if (ret != EOK) {
+    if (state->gethostbyname_ret != EOK) {
         DEBUG(SSSDBG_CRIT_FAILURE, "Failed to resolve server '%s': %s\n",
                   common->name,
                   resolv_strerror(resolv_status));
@@ -1203,15 +1207,29 @@ fo_resolve_service_done(struct tevent_req *subreq)
          * If there are no more servers to try, the next request would
          * just shortcut with ENOENT.
          */
-        if (ret == ENOENT) {
-            ret = EAGAIN;
+        if (state->gethostbyname_ret == ENOENT) {
+            state->gethostbyname_ret = EAGAIN;
         }
         set_server_common_status(common, SERVER_NOT_WORKING);
     } else {
         set_server_common_status(common, SERVER_NAME_RESOLVED);
     }
 
-    /* Take care of all requests for this server. */
+    /* If we are not resolving other names, just use gethostbyname_ret
+     * to mark the request done
+     */
+    fo_resolve_service_notify(req);
+}
+
+static void fo_resolve_service_notify(struct tevent_req *req)
+{
+    struct resolve_service_request *request;
+    struct resolve_service_state *state = tevent_req_data(req,
+                                        struct resolve_service_state);
+    struct server_common *common;
+
+    common = state->server->common;
+
     while ((request = common->request_list) != NULL) {
         DLIST_REMOVE(common->request_list, request);
 
@@ -1221,8 +1239,8 @@ fo_resolve_service_done(struct tevent_req *subreq)
          */
         tevent_req_defer_callback(request->req, request->ev);
 
-        if (ret) {
-            tevent_req_error(request->req, ret);
+        if (state->gethostbyname_ret) {
+            tevent_req_error(request->req, state->gethostbyname_ret);
         } else {
             tevent_req_done(request->req);
         }

From 338b6f165b2861cff3911af248aa9afba6eac1ce Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhro...@redhat.com>
Date: Fri, 22 Mar 2019 00:02:13 +0100
Subject: [PATCH 4/5] failover: Expand SRV query if needed to have at least N
 addresses

The administrator might choose to write up to N addresses of KDCs into
the kdcinfo files. But some setups might configure the server list like
this:
    krb5_server = kdc1, _srv_

In that case, the fail over request should make sure to also continue in
the server list and resolve the SRV query.

The code takes advantage of the restriction where only one SRV record
can be places in the server list. Therefore, the resolving can stop once
SRV result is resolved, because at that point all fo_server entries were
converted to host names.
---
 src/providers/ad/ad_common.c     |   4 +-
 src/providers/backend.h          |   5 +-
 src/providers/data_provider_fo.c |  11 ++-
 src/providers/fail_over.c        | 115 ++++++++++++++++++++++++++++++-
 src/providers/fail_over.h        |   2 +
 src/providers/ipa/ipa_common.c   |   2 +-
 src/providers/krb5/krb5_common.c |   2 +-
 src/providers/ldap/ldap_common.c |   2 +-
 src/tests/cmocka/test_fo_srv.c   |   1 +
 src/tests/fail_over-tests.c      |  10 +--
 10 files changed, 138 insertions(+), 16 deletions(-)

diff --git a/src/providers/ad/ad_common.c b/src/providers/ad/ad_common.c
index 55452b4741..c35ace1284 100644
--- a/src/providers/ad/ad_common.c
+++ b/src/providers/ad/ad_common.c
@@ -766,13 +766,13 @@ ad_failover_init(TALLOC_CTX *mem_ctx, struct be_ctx *bectx,
         goto done;
     }
 
-    ret = be_fo_add_service(bectx, ad_service, ad_user_data_cmp);
+    ret = be_fo_add_service(bectx, ad_service, 0, 0, ad_user_data_cmp);
     if (ret != EOK) {
         DEBUG(SSSDBG_CRIT_FAILURE, "Failed to create failover service!\n");
         goto done;
     }
 
-    ret = be_fo_add_service(bectx, ad_gc_service, ad_user_data_cmp);
+    ret = be_fo_add_service(bectx, ad_gc_service, 0, 0, ad_user_data_cmp);
     if (ret != EOK) {
         DEBUG(SSSDBG_CRIT_FAILURE, "Failed to create GC failover service!\n");
         goto done;
diff --git a/src/providers/backend.h b/src/providers/backend.h
index 4df706aeac..ee89fc304e 100644
--- a/src/providers/backend.h
+++ b/src/providers/backend.h
@@ -162,7 +162,10 @@ typedef void (be_svc_callback_fn_t)(void *, struct fo_server *);
 
 int be_init_failover(struct be_ctx *ctx);
 int be_fo_is_srv_identifier(const char *server);
-int be_fo_add_service(struct be_ctx *ctx, const char *service_name,
+int be_fo_add_service(struct be_ctx *ctx,
+                      const char *service_name,
+                      size_t n_lookahead_primary,
+                      size_t n_lookahead_backup,
                       datacmp_fn user_data_cmp);
 int be_fo_service_add_callback(TALLOC_CTX *memctx,
                                struct be_ctx *ctx, const char *service_name,
diff --git a/src/providers/data_provider_fo.c b/src/providers/data_provider_fo.c
index 473b667e58..3557cc5803 100644
--- a/src/providers/data_provider_fo.c
+++ b/src/providers/data_provider_fo.c
@@ -127,7 +127,10 @@ static struct be_svc_data *be_fo_find_svc_data(struct be_ctx *ctx,
     return 0;
 }
 
-int be_fo_add_service(struct be_ctx *ctx, const char *service_name,
+int be_fo_add_service(struct be_ctx *ctx,
+                      const char *service_name,
+                      size_t n_lookahead_primary,
+                      size_t n_lookahead_backup,
                       datacmp_fn user_data_cmp)
 {
     struct fo_service *service;
@@ -145,7 +148,11 @@ int be_fo_add_service(struct be_ctx *ctx, const char *service_name,
 
     /* if not in the be service list, try to create new one */
 
-    ret = fo_new_service(ctx->be_fo->fo_ctx, service_name, user_data_cmp,
+    ret = fo_new_service(ctx->be_fo->fo_ctx,
+                         service_name,
+                         n_lookahead_primary,
+                         n_lookahead_backup,
+                         user_data_cmp,
                          &service);
     if (ret != EOK && ret != EEXIST) {
         DEBUG(SSSDBG_CRIT_FAILURE, "Failed to create failover service!\n");
diff --git a/src/providers/fail_over.c b/src/providers/fail_over.c
index 6543af3882..3819c12ebd 100644
--- a/src/providers/fail_over.c
+++ b/src/providers/fail_over.c
@@ -76,6 +76,13 @@ struct fo_service {
      * is needed in fail over duplicate servers detection.
      */
     datacmp_fn user_data_cmp;
+    /* How many host names should be resolved after service resolution
+     * This is typically set if the resolve callback needs a certain
+     * number of server names to e.g. write them to the kdcinfo
+     * plugin
+     */
+    size_t n_lookahead_primary;
+    size_t n_lookahead_backup;
 };
 
 struct fo_server {
@@ -426,6 +433,8 @@ service_destructor(struct fo_service *service)
 
 int
 fo_new_service(struct fo_ctx *ctx, const char *name,
+               size_t n_lookahead_primary,
+               size_t n_lookahead_backup,
                datacmp_fn user_data_cmp,
                struct fo_service **_service)
 {
@@ -455,6 +464,8 @@ fo_new_service(struct fo_ctx *ctx, const char *name,
     }
 
     service->user_data_cmp = user_data_cmp;
+    service->n_lookahead_primary = n_lookahead_primary;
+    service->n_lookahead_backup = n_lookahead_backup;
 
     service->ctx = ctx;
     DLIST_ADD(ctx->service_list, service);
@@ -981,6 +992,7 @@ set_lookup_hook(struct tevent_context *ev,
 
 struct resolve_service_state {
     struct fo_server *server;
+    struct fo_service *service;
 
     struct resolv_ctx *resolv;
     struct tevent_context *ev;
@@ -988,6 +1000,10 @@ struct resolve_service_state {
     struct fo_ctx *fo_ctx;
 
     int gethostbyname_ret;
+
+    bool srv_resolved;
+    size_t n_primary_names;
+    size_t n_backup_names;
 };
 
 static errno_t fo_resolve_service_activate_timeout(struct tevent_req *req,
@@ -996,6 +1012,8 @@ static void fo_resolve_service_cont(struct tevent_req *subreq);
 static void fo_resolve_service_done(struct tevent_req *subreq);
 static void fo_resolve_service_notify(struct tevent_req *req);
 static bool fo_resolve_service_server(struct tevent_req *req);
+static int fo_resolve_service_lookahead(struct tevent_req *req);
+static void fo_resolve_lookahead_done(struct tevent_req *subreq);
 
 /* Forward declarations for SRV resolving */
 
@@ -1032,6 +1050,7 @@ fo_resolve_service_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
     state->resolv = resolv;
     state->ev = ev;
     state->fo_ctx = ctx;
+    state->service = service;
 
     ret = get_first_server_entity(service, &server);
     if (ret != EOK) {
@@ -1126,6 +1145,7 @@ fo_resolve_service_cont(struct tevent_req *subreq)
     int ret;
 
     ret = resolve_srv_recv(subreq, &state->server);
+    state->srv_resolved = true;
     talloc_zfree(subreq);
 
     /* We will proceed normally on ERR_SRV_DUPLICATES and if the server
@@ -1170,7 +1190,24 @@ fo_resolve_service_server(struct tevent_req *req)
             return true;
         }
         break;
-    default: /* The name is already resolved. Return immediately. */
+    default: /* The name is already resolved */
+
+        /* Check if we need to resolve additional names */
+        ret = fo_resolve_service_lookahead(req);
+        if (ret == EAGAIN) {
+            /* If yes, the request must be added to the waiting queue */
+            ret = set_lookup_hook(state->ev, state->server, req);
+            if (ret != EOK) {
+                tevent_req_error(req, ret);
+                return true;
+            }
+            return false;
+        } else if (ret != EOK) {
+            tevent_req_error(req, ret);
+            return true;
+        }
+
+        /* If not, just mark the request as done */
         tevent_req_done(req);
         return true;
     }
@@ -1187,6 +1224,7 @@ fo_resolve_service_done(struct tevent_req *subreq)
                                         struct resolve_service_state);
     struct server_common *common;
     int resolv_status;
+    int lookahead_ret;
 
     common = state->server->common;
     if (common->rhostent != NULL) {
@@ -1211,13 +1249,21 @@ fo_resolve_service_done(struct tevent_req *subreq)
             state->gethostbyname_ret = EAGAIN;
         }
         set_server_common_status(common, SERVER_NOT_WORKING);
-    } else {
-        set_server_common_status(common, SERVER_NAME_RESOLVED);
+        goto done;
+    }
+
+    set_server_common_status(common, SERVER_NAME_RESOLVED);
+
+    lookahead_ret = fo_resolve_service_lookahead(req);
+    if (lookahead_ret == EAGAIN) {
+        return;
     }
 
     /* If we are not resolving other names, just use gethostbyname_ret
      * to mark the request done
      */
+done:
+    /* Take care of all requests for this server. */
     fo_resolve_service_notify(req);
 }
 
@@ -1247,6 +1293,69 @@ static void fo_resolve_service_notify(struct tevent_req *req)
     }
 }
 
+static int
+fo_resolve_service_lookahead(struct tevent_req *req)
+{
+    struct resolve_service_state *state = tevent_req_data(req,
+                                        struct resolve_service_state);
+    struct fo_server *srv_iter;
+    struct tevent_req *subreq;
+
+    if (state->service->n_lookahead_primary == 0
+            && state->service->n_lookahead_backup == 0) {
+        return EOK;
+    }
+
+    DLIST_FOR_EACH(srv_iter, state->server) {
+        if (state->n_primary_names >= state->service->n_lookahead_primary
+              && state->n_backup_names >= state->service->n_lookahead_backup) {
+            return EOK;
+        }
+
+        if (fo_is_srv_lookup(srv_iter) && state->srv_resolved == false) {
+            subreq = resolve_srv_send(state, state->ev, state->resolv,
+                                      state->fo_ctx, srv_iter,
+                                      RESOLVE_SRV_FLG_STATELESS);
+            if (subreq == NULL) {
+                return ENOMEM;
+            }
+
+            tevent_req_set_callback(subreq, fo_resolve_lookahead_done, req);
+            return EAGAIN;
+        }
+
+        if (fo_get_server_name(srv_iter) != NULL) {
+            srv_iter->primary ? \
+                state->n_primary_names++ : \
+                state->n_backup_names++;
+            continue;
+        }
+    }
+
+    return EOK;
+}
+
+static void
+fo_resolve_lookahead_done(struct tevent_req *subreq)
+{
+    struct tevent_req *req = tevent_req_callback_data(subreq,
+                                                      struct tevent_req);
+    errno_t ret;
+
+    ret = resolve_srv_recv(subreq, NULL);
+    talloc_zfree(subreq);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_MINOR_FAILURE, "SRV resolution failed: %d\n", ret);
+        /* Don't fail the whole request */
+    }
+
+    /* We already resolved the SRV query and since there can be only
+     * one per failover service and we already resolved the name,
+     * just mark the request as done
+     */
+    fo_resolve_service_notify(req);
+}
+
 int
 fo_resolve_service_recv(struct tevent_req *req,
                         TALLOC_CTX *ref_ctx,
diff --git a/src/providers/fail_over.h b/src/providers/fail_over.h
index bc81427105..da094ea9a3 100644
--- a/src/providers/fail_over.h
+++ b/src/providers/fail_over.h
@@ -104,6 +104,8 @@ typedef int (*datacmp_fn)(void*, void*);
  */
 int fo_new_service(struct fo_ctx *ctx,
                    const char *name,
+                   size_t n_lookahead_primary,
+                   size_t n_lookahead_backup,
                    datacmp_fn user_data_cmp,
                    struct fo_service **_service);
 
diff --git a/src/providers/ipa/ipa_common.c b/src/providers/ipa/ipa_common.c
index a510b4de96..45b422c5b6 100644
--- a/src/providers/ipa/ipa_common.c
+++ b/src/providers/ipa/ipa_common.c
@@ -1046,7 +1046,7 @@ int ipa_service_init(TALLOC_CTX *memctx, struct be_ctx *ctx,
         goto done;
     }
 
-    ret = be_fo_add_service(ctx, "IPA", ipa_user_data_cmp);
+    ret = be_fo_add_service(ctx, "IPA", 0, 0, ipa_user_data_cmp);
     if (ret != EOK) {
         DEBUG(SSSDBG_CRIT_FAILURE, "Failed to create failover service!\n");
         goto done;
diff --git a/src/providers/krb5/krb5_common.c b/src/providers/krb5/krb5_common.c
index 3a398489e5..bd89eddba0 100644
--- a/src/providers/krb5/krb5_common.c
+++ b/src/providers/krb5/krb5_common.c
@@ -979,7 +979,7 @@ int krb5_service_init(TALLOC_CTX *memctx, struct be_ctx *ctx,
         goto done;
     }
 
-    ret = be_fo_add_service(ctx, service_name, krb5_user_data_cmp);
+    ret = be_fo_add_service(ctx, service_name, 0, 0, krb5_user_data_cmp);
     if (ret != EOK) {
         DEBUG(SSSDBG_CRIT_FAILURE, "Failed to create failover service!\n");
         goto done;
diff --git a/src/providers/ldap/ldap_common.c b/src/providers/ldap/ldap_common.c
index 237749aaef..03924aeaec 100644
--- a/src/providers/ldap/ldap_common.c
+++ b/src/providers/ldap/ldap_common.c
@@ -551,7 +551,7 @@ int sdap_service_init(TALLOC_CTX *memctx, struct be_ctx *ctx,
         goto done;
     }
 
-    ret = be_fo_add_service(ctx, service_name, ldap_user_data_cmp);
+    ret = be_fo_add_service(ctx, service_name, 0, 0, ldap_user_data_cmp);
     if (ret != EOK) {
         DEBUG(SSSDBG_CRIT_FAILURE, "Failed to create failover service!\n");
         goto done;
diff --git a/src/tests/cmocka/test_fo_srv.c b/src/tests/cmocka/test_fo_srv.c
index a11ebbb549..9606d1a524 100644
--- a/src/tests/cmocka/test_fo_srv.c
+++ b/src/tests/cmocka/test_fo_srv.c
@@ -241,6 +241,7 @@ static int test_fo_setup(void **state)
     assert_non_null(test_ctx->fo_ctx);
 
     ret = fo_new_service(test_ctx->fo_ctx, "ldap",
+                         0, 0,
                          test_fo_srv_data_cmp,
                          &test_ctx->fo_svc);
     assert_int_equal(ret, ERR_OK);
diff --git a/src/tests/fail_over-tests.c b/src/tests/fail_over-tests.c
index 5312b2772a..4f40f9e4cf 100644
--- a/src/tests/fail_over-tests.c
+++ b/src/tests/fail_over-tests.c
@@ -115,11 +115,11 @@ START_TEST(test_fo_new_service)
         sprintf(buf, "service_%d", i);
 
         ck_leaks_push(ctx);
-        ret = fo_new_service(ctx->fo_ctx, buf, NULL, &services[i]);
+        ret = fo_new_service(ctx->fo_ctx, buf, 0, 0, NULL, &services[i]);
         fail_if(ret != EOK);
     }
 
-    ret = fo_new_service(ctx->fo_ctx, "service_3", NULL, &service);
+    ret = fo_new_service(ctx->fo_ctx, "service_3", 0, 0, NULL, &service);
     fail_if(ret != EEXIST);
 
     for (i = 9; i >= 0; i--) {
@@ -233,11 +233,11 @@ START_TEST(test_fo_resolve_service)
     fail_if(ctx == NULL);
 
     /* Add service. */
-    fail_if(fo_new_service(ctx->fo_ctx, "http", NULL, &service[0]) != EOK);
+    fail_if(fo_new_service(ctx->fo_ctx, "http", 0, 0, NULL, &service[0]) != EOK);
 
-    fail_if(fo_new_service(ctx->fo_ctx, "ldap", NULL, &service[1]) != EOK);
+    fail_if(fo_new_service(ctx->fo_ctx, "ldap", 0, 0, NULL, &service[1]) != EOK);
 
-    fail_if(fo_new_service(ctx->fo_ctx, "ntp", NULL, &service[2]) != EOK);
+    fail_if(fo_new_service(ctx->fo_ctx, "ntp", 0, 0, NULL, &service[2]) != EOK);
 
     /* Add servers. */
     fail_if(fo_add_server(service[0], "localhost", 20, NULL, true) != EOK);

From 465560f881dfb146dfc18241676171571f013f33 Mon Sep 17 00:00:00 2001
From: Tomas Halman <thal...@redhat.com>
Date: Mon, 25 Mar 2019 15:39:24 +0100
Subject: [PATCH 5/5] failover: Read lookahead configuration

We read configuration option krb5_kdcinfo_lookahead from configuration
and we use it for failover services initialization
---
 src/providers/ad/ad_common.c              | 25 +++++++---------------
 src/providers/ad/ad_common.h              |  2 ++
 src/providers/ad/ad_init.c                |  2 ++
 src/providers/ad/ad_opts.c                |  1 +
 src/providers/ad/ad_subdomains.c          | 12 ++++++++++-
 src/providers/ipa/ipa_common.c            | 20 ++++++++---------
 src/providers/ipa/ipa_subdomains_server.c |  7 ++++++
 src/providers/krb5/krb5_common.c          | 26 ++++++++++++++---------
 src/providers/krb5/krb5_common.h          |  8 ++++---
 src/providers/krb5/krb5_init.c            | 19 +++++++++++++++--
 src/providers/ldap/ldap_common.c          |  6 +++++-
 11 files changed, 83 insertions(+), 45 deletions(-)

diff --git a/src/providers/ad/ad_common.c b/src/providers/ad/ad_common.c
index c35ace1284..1b73338136 100644
--- a/src/providers/ad/ad_common.c
+++ b/src/providers/ad/ad_common.c
@@ -729,6 +729,8 @@ ad_failover_init(TALLOC_CTX *mem_ctx, struct be_ctx *bectx,
                  const char *ad_gc_service,
                  const char *ad_domain,
                  bool use_kdcinfo,
+                 size_t n_lookahead_primary,
+                 size_t n_lookahead_backup,
                  struct ad_service **_service)
 {
     errno_t ret;
@@ -766,13 +768,13 @@ ad_failover_init(TALLOC_CTX *mem_ctx, struct be_ctx *bectx,
         goto done;
     }
 
-    ret = be_fo_add_service(bectx, ad_service, 0, 0, ad_user_data_cmp);
+    ret = be_fo_add_service(bectx, ad_service, n_lookahead_primary, n_lookahead_backup, ad_user_data_cmp);
     if (ret != EOK) {
         DEBUG(SSSDBG_CRIT_FAILURE, "Failed to create failover service!\n");
         goto done;
     }
 
-    ret = be_fo_add_service(bectx, ad_gc_service, 0, 0, ad_user_data_cmp);
+    ret = be_fo_add_service(bectx, ad_gc_service, n_lookahead_primary, n_lookahead_backup, ad_user_data_cmp);
     if (ret != EOK) {
         DEBUG(SSSDBG_CRIT_FAILURE, "Failed to create GC failover service!\n");
         goto done;
@@ -1305,21 +1307,10 @@ ad_get_auth_options(TALLOC_CTX *mem_ctx,
     DEBUG(SSSDBG_CONF_SETTINGS, "Option %s set to %s\n",
           krb5_options[KRB5_USE_KDCINFO].opt_name,
           ad_opts->service->krb5_service->write_kdcinfo ? "true" : "false");
-    if (ad_opts->service->krb5_service->write_kdcinfo) {
-        ret = sss_krb5_parse_lookahead(
-            dp_opt_get_string(krb5_options, KRB5_KDCINFO_LOOKAHEAD),
-            &ad_opts->service->krb5_service->lookahead_primary,
-            &ad_opts->service->krb5_service->lookahead_backup);
-        if (ret != EOK) {
-            DEBUG(SSSDBG_MINOR_FAILURE,
-                  "Could not parse %s parameter\n",
-                  krb5_options[KRB5_KDCINFO_LOOKAHEAD].opt_name);
-        }
-        DEBUG(SSSDBG_CONF_SETTINGS, "Option %s set to %d:%d\n",
-              krb5_options[KRB5_KDCINFO_LOOKAHEAD].opt_name,
-              ad_opts->service->krb5_service->lookahead_primary,
-              ad_opts->service->krb5_service->lookahead_backup);
-    }
+    sss_krb5_parse_lookahead(
+        dp_opt_get_string(krb5_options, KRB5_KDCINFO_LOOKAHEAD),
+        &ad_opts->service->krb5_service->lookahead_primary,
+        &ad_opts->service->krb5_service->lookahead_backup);
 
     *_opts = talloc_steal(mem_ctx, krb5_options);
 
diff --git a/src/providers/ad/ad_common.h b/src/providers/ad/ad_common.h
index 662276cb67..6370f502ec 100644
--- a/src/providers/ad/ad_common.h
+++ b/src/providers/ad/ad_common.h
@@ -146,6 +146,8 @@ ad_failover_init(TALLOC_CTX *mem_ctx, struct be_ctx *ctx,
                  const char *ad_gc_service,
                  const char *ad_domain,
                  bool use_kdcinfo,
+                 size_t n_lookahead_primary,
+                 size_t n_lookahead_backup,
                  struct ad_service **_service);
 
 void
diff --git a/src/providers/ad/ad_init.c b/src/providers/ad/ad_init.c
index a9085717c5..4f1bd6dd16 100644
--- a/src/providers/ad/ad_init.c
+++ b/src/providers/ad/ad_init.c
@@ -160,6 +160,8 @@ static errno_t ad_init_options(TALLOC_CTX *mem_ctx,
                            ad_realm, AD_SERVICE_NAME, AD_GC_SERVICE_NAME,
                            dp_opt_get_string(ad_options->basic, AD_DOMAIN),
                            false, /* will be set in ad_get_auth_options() */
+                           (size_t) -1,
+                           (size_t) -1,
                            &ad_options->service);
     if (ret != EOK) {
         DEBUG(SSSDBG_FATAL_FAILURE, "Failed to init AD failover service: "
diff --git a/src/providers/ad/ad_opts.c b/src/providers/ad/ad_opts.c
index 30bb333e1d..dbc23a8203 100644
--- a/src/providers/ad/ad_opts.c
+++ b/src/providers/ad/ad_opts.c
@@ -173,6 +173,7 @@ struct dp_option ad_def_krb5_opts[] = {
     { "krb5_canonicalize", DP_OPT_BOOL, BOOL_FALSE, BOOL_FALSE },
     { "krb5_use_enterprise_principal", DP_OPT_BOOL, BOOL_TRUE, BOOL_TRUE },
     { "krb5_use_kdcinfo", DP_OPT_BOOL, BOOL_TRUE, BOOL_TRUE },
+    { "krb5_kdcinfo_lookahead", DP_OPT_STRING, NULL_STRING, NULL_STRING },
     { "krb5_map_user", DP_OPT_STRING, NULL_STRING, NULL_STRING },
     DP_OPTION_TERMINATOR
 };
diff --git a/src/providers/ad/ad_subdomains.c b/src/providers/ad/ad_subdomains.c
index 517b879783..e642d9f7d0 100644
--- a/src/providers/ad/ad_subdomains.c
+++ b/src/providers/ad/ad_subdomains.c
@@ -280,6 +280,8 @@ ad_subdom_ad_ctx_new(struct be_ctx *be_ctx,
     const char *keytab;
     char *subdom_conf_path;
     bool use_kdcinfo = false;
+    size_t n_lookahead_primary = (size_t) -1;
+    size_t n_lookahead_backup = (size_t) -1;
 
     realm = dp_opt_get_cstring(id_ctx->ad_options->basic, AD_KRB5_REALM);
     hostname = dp_opt_get_cstring(id_ctx->ad_options->basic, AD_HOSTNAME);
@@ -331,6 +333,11 @@ ad_subdom_ad_ctx_new(struct be_ctx *be_ctx,
             && id_ctx->ad_options->auth_ctx->opts != NULL) {
         use_kdcinfo = dp_opt_get_bool(id_ctx->ad_options->auth_ctx->opts,
                                       KRB5_USE_KDCINFO);
+        sss_krb5_parse_lookahead(
+            dp_opt_get_string(id_ctx->ad_options->auth_ctx->opts,
+                              KRB5_KDCINFO_LOOKAHEAD),
+            &n_lookahead_primary,
+            &n_lookahead_backup);
     }
 
     DEBUG(SSSDBG_TRACE_ALL,
@@ -339,7 +346,10 @@ ad_subdom_ad_ctx_new(struct be_ctx *be_ctx,
 
     ret = ad_failover_init(ad_options, be_ctx, servers, backup_servers,
                            subdom->realm, service_name, gc_service_name,
-                           subdom->name, use_kdcinfo, &ad_options->service);
+                           subdom->name, use_kdcinfo,
+                           n_lookahead_primary,
+                           n_lookahead_backup,
+                           &ad_options->service);
     if (ret != EOK) {
         DEBUG(SSSDBG_OP_FAILURE, "Cannot initialize AD failover\n");
         talloc_free(ad_options);
diff --git a/src/providers/ipa/ipa_common.c b/src/providers/ipa/ipa_common.c
index 45b422c5b6..3336ecb502 100644
--- a/src/providers/ipa/ipa_common.c
+++ b/src/providers/ipa/ipa_common.c
@@ -802,19 +802,10 @@ int ipa_get_auth_options(struct ipa_options *ipa_opts,
           ipa_opts->auth[KRB5_USE_KDCINFO].opt_name,
           ipa_opts->service->krb5_service->write_kdcinfo ? "true" : "false");
     if (ipa_opts->service->krb5_service->write_kdcinfo) {
-        ret = sss_krb5_parse_lookahead(
+        sss_krb5_parse_lookahead(
             dp_opt_get_string(ipa_opts->auth, KRB5_KDCINFO_LOOKAHEAD),
             &ipa_opts->service->krb5_service->lookahead_primary,
             &ipa_opts->service->krb5_service->lookahead_backup);
-        if (ret != EOK) {
-            DEBUG(SSSDBG_MINOR_FAILURE,
-                  "Could not parse %s parameter\n",
-                  ipa_opts->auth[KRB5_KDCINFO_LOOKAHEAD].opt_name);
-        }
-        DEBUG(SSSDBG_CONF_SETTINGS, "Option %s set to %d:%d\n",
-              ipa_opts->auth[KRB5_KDCINFO_LOOKAHEAD].opt_name,
-              ipa_opts->service->krb5_service->lookahead_primary,
-              ipa_opts->service->krb5_service->lookahead_backup);
     }
 
     *_opts = ipa_opts->auth;
@@ -1011,6 +1002,8 @@ int ipa_service_init(TALLOC_CTX *memctx, struct be_ctx *ctx,
     struct ipa_service *service;
     char *realm;
     int ret;
+    size_t n_lookahead_primary;
+    size_t n_lookahead_backup;
 
     tmp_ctx = talloc_new(NULL);
     if (!tmp_ctx) {
@@ -1046,7 +1039,12 @@ int ipa_service_init(TALLOC_CTX *memctx, struct be_ctx *ctx,
         goto done;
     }
 
-    ret = be_fo_add_service(ctx, "IPA", 0, 0, ipa_user_data_cmp);
+    sss_krb5_parse_lookahead(dp_opt_get_string(options->auth, KRB5_KDCINFO_LOOKAHEAD),
+                             &n_lookahead_primary, &n_lookahead_backup);
+
+    ret = be_fo_add_service(ctx, "IPA",
+                            n_lookahead_primary, n_lookahead_backup,
+                            ipa_user_data_cmp);
     if (ret != EOK) {
         DEBUG(SSSDBG_CRIT_FAILURE, "Failed to create failover service!\n");
         goto done;
diff --git a/src/providers/ipa/ipa_subdomains_server.c b/src/providers/ipa/ipa_subdomains_server.c
index 43a3053cb2..ce33fce58c 100644
--- a/src/providers/ipa/ipa_subdomains_server.c
+++ b/src/providers/ipa/ipa_subdomains_server.c
@@ -225,6 +225,8 @@ ipa_ad_ctx_new(struct be_ctx *be_ctx,
     errno_t ret;
     const char *extra_attrs;
     bool use_kdcinfo = false;
+    size_t n_lookahead_primary = (size_t)-1;
+    size_t n_lookahead_backup = (size_t)-1;
 
     ad_domain = subdom->name;
     DEBUG(SSSDBG_TRACE_LIBS, "Setting up AD subdomain %s\n", subdom->name);
@@ -284,6 +286,10 @@ ipa_ad_ctx_new(struct be_ctx *be_ctx,
     if (id_ctx->ipa_options != NULL && id_ctx->ipa_options->auth != NULL) {
         use_kdcinfo = dp_opt_get_bool(id_ctx->ipa_options->auth,
                                       KRB5_USE_KDCINFO);
+        sss_krb5_parse_lookahead(
+            dp_opt_get_string(id_ctx->ipa_options->auth, KRB5_KDCINFO_LOOKAHEAD),
+            &n_lookahead_primary,
+            &n_lookahead_backup);
     }
 
     DEBUG(SSSDBG_TRACE_ALL,
@@ -297,6 +303,7 @@ ipa_ad_ctx_new(struct be_ctx *be_ctx,
                            subdom->realm,
                            service_name, gc_service_name,
                            subdom->name, use_kdcinfo,
+                           n_lookahead_backup, n_lookahead_backup,
                            &ad_options->service);
     if (ret != EOK) {
         DEBUG(SSSDBG_OP_FAILURE, "Cannot initialize AD failover\n");
diff --git a/src/providers/krb5/krb5_common.c b/src/providers/krb5/krb5_common.c
index bd89eddba0..d819db2db0 100644
--- a/src/providers/krb5/krb5_common.c
+++ b/src/providers/krb5/krb5_common.c
@@ -390,27 +390,30 @@ errno_t sss_krb5_get_options(TALLOC_CTX *memctx, struct confdb_ctx *cdb,
     return ret;
 }
 
-errno_t sss_krb5_parse_lookahead(const char *param, int *primary, int *backup)
+void sss_krb5_parse_lookahead(const char *param, size_t *primary, size_t *backup)
 {
     int ret;
-    *primary = -1;
-    *backup = -1;
+    *primary = (size_t) -1;
+    *backup = (size_t) -1;
 
     if (param == NULL)
-        return EOK;
+        return;
 
     if (strchr(param, ':')) {
-        ret = sscanf(param, "%d:%d", primary, backup);
+        ret = sscanf(param, "%zu:%zu", primary, backup);
         if (ret != 2) {
-            return EINVAL;
+            DEBUG(SSSDBG_MINOR_FAILURE, "Could not parse krb5_kdcinfo_lookahead!\n");
         }
     } else {
-        ret = sscanf(param, "%d", primary);
+        ret = sscanf(param, "%zu", primary);
         if (ret != 1) {
-            return EINVAL;
+            DEBUG(SSSDBG_MINOR_FAILURE, "Could not parse krb5_kdcinfo_lookahead!\n");
         }
     }
-    return EOK;
+
+    DEBUG(SSSDBG_CONF_SETTINGS,
+          "Option krb5_kdcinfo_lookahead set to %zu:%zu",
+          *primary, *backup);
 }
 
 
@@ -962,6 +965,8 @@ int krb5_service_init(TALLOC_CTX *memctx, struct be_ctx *ctx,
                       const char *backup_servers,
                       const char *realm,
                       bool use_kdcinfo,
+                      size_t n_lookahead_primary,
+                      size_t n_lookahead_backup,
                       struct krb5_service **_service)
 {
     TALLOC_CTX *tmp_ctx;
@@ -979,7 +984,8 @@ int krb5_service_init(TALLOC_CTX *memctx, struct be_ctx *ctx,
         goto done;
     }
 
-    ret = be_fo_add_service(ctx, service_name, 0, 0, krb5_user_data_cmp);
+    ret = be_fo_add_service(ctx, service_name, n_lookahead_primary,
+                            n_lookahead_backup, krb5_user_data_cmp);
     if (ret != EOK) {
         DEBUG(SSSDBG_CRIT_FAILURE, "Failed to create failover service!\n");
         goto done;
diff --git a/src/providers/krb5/krb5_common.h b/src/providers/krb5/krb5_common.h
index 8d0386b259..5c8ee78bf2 100644
--- a/src/providers/krb5/krb5_common.h
+++ b/src/providers/krb5/krb5_common.h
@@ -72,8 +72,8 @@ struct krb5_service {
     char *name;
     char *realm;
     bool write_kdcinfo;
-    int lookahead_primary;
-    int lookahead_backup;
+    size_t lookahead_primary;
+    size_t lookahead_backup;
     bool removal_callback_available;
 };
 
@@ -163,7 +163,7 @@ errno_t krb5_try_kdcip(struct confdb_ctx *cdb, const char *conf_path,
 errno_t sss_krb5_get_options(TALLOC_CTX *memctx, struct confdb_ctx *cdb,
                              const char *conf_path, struct dp_option **_opts);
 
-errno_t sss_krb5_parse_lookahead(const char *param, int *primary, int *backup);
+void sss_krb5_parse_lookahead(const char *param, size_t *primary, size_t *backup);
 
 errno_t write_krb5info_file(struct krb5_service *krb5_service,
                             const char **server_list,
@@ -186,6 +186,8 @@ int krb5_service_init(TALLOC_CTX *memctx, struct be_ctx *ctx,
                       const char *backup_servers,
                       const char *realm,
                       bool use_kdcinfo,
+                      size_t n_lookahead_primary,
+                      size_t n_lookahead_backup,
                       struct krb5_service **_service);
 
 void remove_krb5_info_files_callback(void *pvt);
diff --git a/src/providers/krb5/krb5_init.c b/src/providers/krb5/krb5_init.c
index 66ae68fb47..3f4c1b3619 100644
--- a/src/providers/krb5/krb5_init.c
+++ b/src/providers/krb5/krb5_init.c
@@ -40,6 +40,8 @@ static errno_t krb5_init_kpasswd(struct krb5_ctx *ctx,
     const char *backup_servers;
     const char *kdc_servers;
     bool use_kdcinfo;
+    size_t n_lookahead_primary;
+    size_t n_lookahead_backup;
     errno_t ret;
 
     realm = dp_opt_get_string(ctx->opts, KRB5_REALM);
@@ -52,6 +54,9 @@ static errno_t krb5_init_kpasswd(struct krb5_ctx *ctx,
     primary_servers = dp_opt_get_string(ctx->opts, KRB5_KPASSWD);
     backup_servers = dp_opt_get_string(ctx->opts, KRB5_BACKUP_KPASSWD);
     use_kdcinfo = dp_opt_get_bool(ctx->opts, KRB5_USE_KDCINFO);
+    sss_krb5_parse_lookahead(dp_opt_get_string(ctx->opts, KRB5_KDCINFO_LOOKAHEAD),
+                             &n_lookahead_primary, &n_lookahead_backup);
+
 
     if (primary_servers == NULL && backup_servers != NULL) {
         DEBUG(SSSDBG_CONF_SETTINGS, "kpasswd server wasn't specified but "
@@ -67,7 +72,10 @@ static errno_t krb5_init_kpasswd(struct krb5_ctx *ctx,
     } else {
         ret = krb5_service_init(ctx, be_ctx, SSS_KRB5KPASSWD_FO_SRV,
                                 primary_servers, backup_servers, realm,
-                                use_kdcinfo, &ctx->kpasswd_service);
+                                use_kdcinfo,
+                                n_lookahead_primary,
+                                n_lookahead_backup,
+                                &ctx->kpasswd_service);
         if (ret != EOK) {
             DEBUG(SSSDBG_FATAL_FAILURE,
                   "Failed to init KRB5KPASSWD failover service!\n");
@@ -84,6 +92,8 @@ static errno_t krb5_init_kdc(struct krb5_ctx *ctx, struct be_ctx *be_ctx)
     const char *backup_servers;
     const char *realm;
     bool use_kdcinfo;
+    size_t n_lookahead_primary;
+    size_t n_lookahead_backup;
     errno_t ret;
 
     realm = dp_opt_get_string(ctx->opts, KRB5_REALM);
@@ -96,10 +106,15 @@ static errno_t krb5_init_kdc(struct krb5_ctx *ctx, struct be_ctx *be_ctx)
     backup_servers = dp_opt_get_string(ctx->opts, KRB5_BACKUP_KDC);
 
     use_kdcinfo = dp_opt_get_bool(ctx->opts, KRB5_USE_KDCINFO);
+    sss_krb5_parse_lookahead(dp_opt_get_string(ctx->opts, KRB5_KDCINFO_LOOKAHEAD),
+                             &n_lookahead_primary, &n_lookahead_backup);
 
     ret = krb5_service_init(ctx, be_ctx, SSS_KRB5KDC_FO_SRV,
                             primary_servers, backup_servers, realm,
-                            use_kdcinfo, &ctx->service);
+                            use_kdcinfo,
+                            n_lookahead_primary,
+                            n_lookahead_backup,
+                            &ctx->service);
     if (ret != EOK) {
         DEBUG(SSSDBG_FATAL_FAILURE, "Failed to init KRB5 failover service!\n");
         return ret;
diff --git a/src/providers/ldap/ldap_common.c b/src/providers/ldap/ldap_common.c
index 03924aeaec..3a19bcd30e 100644
--- a/src/providers/ldap/ldap_common.c
+++ b/src/providers/ldap/ldap_common.c
@@ -366,6 +366,8 @@ int sdap_gssapi_init(TALLOC_CTX *mem_ctx,
                             krb5_backup_servers, krb5_realm,
                             dp_opt_get_bool(opts,
                                             SDAP_KRB5_USE_KDCINFO),
+                            (size_t) -1,
+                            (size_t) -1,
                             &service);
     if (ret != EOK) {
         DEBUG(SSSDBG_FATAL_FAILURE, "Failed to init KRB5 failover service!\n");
@@ -551,7 +553,9 @@ int sdap_service_init(TALLOC_CTX *memctx, struct be_ctx *ctx,
         goto done;
     }
 
-    ret = be_fo_add_service(ctx, service_name, 0, 0, ldap_user_data_cmp);
+    ret = be_fo_add_service(ctx, service_name,
+                            (size_t) -1, (size_t) -1,
+                            ldap_user_data_cmp);
     if (ret != EOK) {
         DEBUG(SSSDBG_CRIT_FAILURE, "Failed to create failover service!\n");
         goto done;
_______________________________________________
sssd-devel mailing list -- sssd-devel@lists.fedorahosted.org
To unsubscribe send an email to sssd-devel-le...@lists.fedorahosted.org
Fedora Code of Conduct: https://getfedora.org/code-of-conduct.html
List Guidelines: https://fedoraproject.org/wiki/Mailing_list_guidelines
List Archives: 
https://lists.fedorahosted.org/archives/list/sssd-devel@lists.fedorahosted.org

Reply via email to