[SSSD] [PATCH] Use SRV TTL in fail over code

Jakub Hrozek jhrozek at redhat.com
Tue Mar 3 10:52:13 UTC 2015


On Fri, Feb 27, 2015 at 05:21:29PM +0100, Pavel Březina wrote:
> On 02/25/2015 08:22 PM, Jakub Hrozek wrote:
> >On Wed, Feb 25, 2015 at 04:15:07PM +0100, Pavel Březina wrote:
> >>On 02/17/2015 08:59 PM, Jakub Hrozek wrote:
> >>>Hi,
> >>>
> >>>the attached patch implements using TTLs in our failover code instead of
> >>>hardcoded refresh value and includes a unit test.
> >>
> >>Hi,
> >>the patch looks good in general. but you are missing assignment to _ttl in
> >>fo_discover_servers_recv.
> >
> >What a silly oversight, thanks for catching that.
> 
> Hi,
> I have one question: it seems that you have changed default ttl to its half,
> from 14400 to 7200. Why is that?

Because I didn't realize the default TTL in the resolver is different,
thanks for catching that.

> 
> I also noticed that there is no debug message... it may be useful to print
> acquired ttl.

Done, in the same patch as fixup of the default TTL.

Thanks for the careful review.
-------------- next part --------------
>From 0ee3d38662a351fc4628cafb48850f1bc1b1c1a9 Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhrozek at redhat.com>
Date: Tue, 3 Mar 2015 11:39:39 +0100
Subject: [PATCH 1/2] resolv: Use the same default timeout for SRV queries as
 previously

When we changed the resolver code to use the TTL values from the DNS
queries instead of harcoded ones, we changed the default value by
accident.

Add a separate SRV TTL that is backwards-compatible with the old
harcoded value.
---
 src/resolv/async_resolv.c | 4 +++-
 src/resolv/async_resolv.h | 4 ++++
 2 files changed, 7 insertions(+), 1 deletion(-)

diff --git a/src/resolv/async_resolv.c b/src/resolv/async_resolv.c
index c9657ef414eb4d728ca38f4be6b117bc8ebaf8d7..1035924208391f31297a423960adf51a45870665 100644
--- a/src/resolv/async_resolv.c
+++ b/src/resolv/async_resolv.c
@@ -1757,8 +1757,10 @@ resolv_getsrv_done(void *arg, int status, int timeouts, unsigned char *abuf, int
     state->reply_list = reply_list;
     ok = resolv_get_ttl(abuf, alen, &state->ttl);
     if (ok == false) {
-        state->ttl = RESOLV_DEFAULT_TTL;
+        DEBUG(SSSDBG_MINOR_FAILURE, "Could not read TTL, using the default..\n");
+        state->ttl = RESOLV_DEFAULT_SRV_TTL;
     }
+    DEBUG(SSSDBG_TRACE_LIBS, "Using TTL [%"PRIu32"]\n", state->ttl);
 
     tevent_req_done(req);
     return;
diff --git a/src/resolv/async_resolv.h b/src/resolv/async_resolv.h
index 109779bb96b01ac03bb7d2293b8e43850b77ef46..876abff14e6e22c67585a476a7169423281f01c7 100644
--- a/src/resolv/async_resolv.h
+++ b/src/resolv/async_resolv.h
@@ -36,6 +36,10 @@
 #define RESOLV_DEFAULT_TTL 7200
 #endif  /* RESOLV_DEFAULT_TTL */
 
+#ifndef RESOLV_DEFAULT_SRV_TTL
+#define RESOLV_DEFAULT_SRV_TTL 14400
+#endif  /* RESOLV_DEFAULT_SRV_TTL */
+
 #include "util/util.h"
 
 /*
-- 
2.1.0

-------------- next part --------------
>From 845770ac29a5628c71adfabcab8240bc9d29a7f0 Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhrozek at redhat.com>
Date: Fri, 12 Dec 2014 17:10:40 +0100
Subject: [PATCH 2/2] FO: Use SRV TTL in fail over code

Resolves:
https://fedorahosted.org/sssd/ticket/1884

Removes the hardcoded SRV TTL timeout and uses TTL from the DNS instead.
---
 Makefile.am                      |  19 ++
 src/providers/ad/ad_srv.c        |   8 +-
 src/providers/ad/ad_srv.h        |   1 +
 src/providers/data_provider_fo.c |   1 -
 src/providers/fail_over.c        |   8 +-
 src/providers/fail_over.h        |   1 -
 src/providers/fail_over_srv.c    |  27 +-
 src/providers/fail_over_srv.h    |   4 +
 src/providers/ipa/ipa_srv.c      |   6 +
 src/providers/ipa/ipa_srv.h      |   1 +
 src/tests/cmocka/test_fo_srv.c   | 599 +++++++++++++++++++++++++++++++++++++++
 11 files changed, 666 insertions(+), 9 deletions(-)
 create mode 100644 src/tests/cmocka/test_fo_srv.c

diff --git a/Makefile.am b/Makefile.am
index 434f28f524b7cc578093d9c2e738b019f0b89388..ffbdf970a5702fcd745611b1ba739d31b4e98c0e 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -220,6 +220,7 @@ if HAVE_CMOCKA
         test_child_common \
         responder_cache_req-tests \
         test_sbus_opath \
+        test_fo_srv \
         $(NULL)
 
 if HAVE_LIBRESOLV
@@ -2258,6 +2259,24 @@ test_resolv_fake_LDADD = \
     $(NULL)
 endif # HAVE_LIBRESOLV
 
+test_fo_srv_SOURCES = \
+    src/tests/cmocka/test_fo_srv.c \
+    src/providers/fail_over.c \
+    src/providers/fail_over_srv.c \
+    $(NULL)
+test_fo_srv_CFLAGS = \
+    $(AM_CFLAGS) \
+    $(NULL)
+test_fo_srv_LDADD = \
+    $(CMOCKA_LIBS) \
+    $(POPT_LIBS) \
+    $(TALLOC_LIBS) \
+    $(CARES_LIBS) \
+    $(DHASH_LIBS) \
+    $(SSSD_INTERNAL_LTLIBS) \
+    libsss_test_common.la \
+    $(NULL)
+
 endif # HAVE_CMOCKA
 
 noinst_PROGRAMS = pam_test_client
diff --git a/src/providers/ad/ad_srv.c b/src/providers/ad/ad_srv.c
index ac9dfa187213e6cdb9cdc3440f013e5b73e11fc4..910f82dd049902c01bdb14edf0cdf44b18c94b0b 100644
--- a/src/providers/ad/ad_srv.c
+++ b/src/providers/ad/ad_srv.c
@@ -176,7 +176,7 @@ static void ad_get_dc_servers_done(struct tevent_req *subreq)
     req = tevent_req_callback_data(subreq, struct tevent_req);
     state = tevent_req_data(req, struct ad_get_dc_servers_state);
 
-    ret = fo_discover_srv_recv(state, subreq, &domain,
+    ret = fo_discover_srv_recv(state, subreq, &domain, NULL,
                                &state->servers, &state->num_servers);
     talloc_zfree(subreq);
     if (ret != EOK) {
@@ -616,6 +616,7 @@ struct ad_srv_plugin_state {
 
     const char *site;
     char *dns_domain;
+    uint32_t ttl;
     char *forest;
     struct fo_server_info *primary_servers;
     size_t num_primary_servers;
@@ -837,6 +838,7 @@ static void ad_srv_plugin_servers_done(struct tevent_req *subreq)
     state = tevent_req_data(req, struct ad_srv_plugin_state);
 
     ret = fo_discover_servers_recv(state, subreq, &state->dns_domain,
+                                   &state->ttl,
                                    &state->primary_servers,
                                    &state->num_primary_servers,
                                    &state->backup_servers,
@@ -874,6 +876,7 @@ static void ad_srv_plugin_servers_done(struct tevent_req *subreq)
 errno_t ad_srv_plugin_recv(TALLOC_CTX *mem_ctx,
                            struct tevent_req *req,
                            char **_dns_domain,
+                           uint32_t *_ttl,
                            struct fo_server_info **_primary_servers,
                            size_t *_num_primary_servers,
                            struct fo_server_info **_backup_servers,
@@ -904,6 +907,9 @@ errno_t ad_srv_plugin_recv(TALLOC_CTX *mem_ctx,
         *_dns_domain = talloc_steal(mem_ctx, state->dns_domain);
     }
 
+    if (_ttl) {
+        *_ttl = state->ttl;
+    }
 
     return EOK;
 }
diff --git a/src/providers/ad/ad_srv.h b/src/providers/ad/ad_srv.h
index be3ac2826186008a4db6333e5d27e408aaed2e51..ae5efe44755fa09f74064014cce749e35b1831da 100644
--- a/src/providers/ad/ad_srv.h
+++ b/src/providers/ad/ad_srv.h
@@ -42,6 +42,7 @@ struct tevent_req *ad_srv_plugin_send(TALLOC_CTX *mem_ctx,
 errno_t ad_srv_plugin_recv(TALLOC_CTX *mem_ctx,
                             struct tevent_req *req,
                             char **_dns_domain,
+                            uint32_t *_ttl,
                             struct fo_server_info **_primary_servers,
                             size_t *_num_primary_servers,
                             struct fo_server_info **_backup_servers,
diff --git a/src/providers/data_provider_fo.c b/src/providers/data_provider_fo.c
index ef1f0b2ed3c0164414c6c4c4dad29804d542c886..dab796d4647a552c2fd1c626c490278de91c2e81 100644
--- a/src/providers/data_provider_fo.c
+++ b/src/providers/data_provider_fo.c
@@ -70,7 +70,6 @@ static int be_fo_get_options(struct be_ctx *ctx,
     opts->service_resolv_timeout = dp_opt_get_int(ctx->be_res->opts,
                                                   DP_RES_OPT_RESOLVER_TIMEOUT);
     opts->retry_timeout = 30;
-    opts->srv_retry_timeout = 14400;
     opts->srv_retry_neg_timeout = 15;
     opts->family_order = ctx->be_res->family_order;
 
diff --git a/src/providers/fail_over.c b/src/providers/fail_over.c
index c16c70ddb0c16a744c237e25e7ab4b356eaff2be..8142081526d7902946e756ec05774c471126545e 100644
--- a/src/providers/fail_over.c
+++ b/src/providers/fail_over.c
@@ -117,6 +117,7 @@ struct srv_data {
     struct fo_server *meta;
 
     int srv_lookup_status;
+    int ttl;
     struct timeval last_status_change;
 };
 
@@ -149,7 +150,6 @@ fo_context_init(TALLOC_CTX *mem_ctx, struct fo_options *opts)
         return NULL;
     }
 
-    ctx->opts->srv_retry_timeout = opts->srv_retry_timeout;
     ctx->opts->srv_retry_neg_timeout = opts->srv_retry_neg_timeout;
     ctx->opts->retry_timeout = opts->retry_timeout;
     ctx->opts->family_order  = opts->family_order;
@@ -272,7 +272,7 @@ get_srv_data_status(struct srv_data *data)
     if (data->srv_lookup_status == SRV_RESOLVE_ERROR) {
         timeout = data->meta->service->ctx->opts->srv_retry_neg_timeout;
     } else {
-        timeout = data->meta->service->ctx->opts->srv_retry_timeout;
+        timeout = data->ttl;
     }
 
     if (timeout && STATUS_DIFF(data, tv) > timeout) {
@@ -1285,8 +1285,9 @@ resolve_srv_done(struct tevent_req *subreq)
     size_t num_backup_servers = 0;
     char *dns_domain = NULL;
     int ret;
+    uint32_t ttl;
 
-    ret = state->fo_ctx->srv_recv_fn(state, subreq, &dns_domain,
+    ret = state->fo_ctx->srv_recv_fn(state, subreq, &dns_domain, &ttl,
                                      &primary_servers, &num_primary_servers,
                                      &backup_servers, &num_backup_servers);
     talloc_free(subreq);
@@ -1300,6 +1301,7 @@ resolve_srv_done(struct tevent_req *subreq)
             goto done;
         }
 
+        state->meta->srv_data->ttl = ttl;
         talloc_zfree(state->meta->srv_data->dns_domain);
         state->meta->srv_data->dns_domain = talloc_steal(state->meta->srv_data,
                                                          dns_domain);
diff --git a/src/providers/fail_over.h b/src/providers/fail_over.h
index b1ec6a23c5e475c7df4fce21626a53df96fe62e8..d44ad2ff145dc6b3617e6f2ea665c7d82d923ddb 100644
--- a/src/providers/fail_over.h
+++ b/src/providers/fail_over.h
@@ -78,7 +78,6 @@ struct fo_server;
  * try when looking up the service.
  */
 struct fo_options {
-    time_t srv_retry_timeout;
     time_t srv_retry_neg_timeout;
     time_t retry_timeout;
     int service_resolv_timeout;
diff --git a/src/providers/fail_over_srv.c b/src/providers/fail_over_srv.c
index 5c06d28767b8afbc4e573a7881e7855f35747460..5f474eaee4f359cb6c4af3896b7ba18413eb4995 100644
--- a/src/providers/fail_over_srv.c
+++ b/src/providers/fail_over_srv.c
@@ -30,6 +30,7 @@ struct fo_discover_srv_state {
     char *dns_domain;
     struct fo_server_info *servers;
     size_t num_servers;
+    uint32_t ttl;
 };
 
 static void fo_discover_srv_done(struct tevent_req *subreq);
@@ -83,7 +84,7 @@ static void fo_discover_srv_done(struct tevent_req *subreq)
     state = tevent_req_data(req, struct fo_discover_srv_state);
 
     ret = resolv_discover_srv_recv(state, subreq,
-                                   &reply_list, NULL, &state->dns_domain);
+                                   &reply_list, &state->ttl, &state->dns_domain);
     talloc_zfree(subreq);
     if (ret == ENOENT) {
         ret = ERR_SRV_NOT_FOUND;
@@ -143,6 +144,7 @@ done:
 errno_t fo_discover_srv_recv(TALLOC_CTX *mem_ctx,
                              struct tevent_req *req,
                              char **_dns_domain,
+                             uint32_t *_ttl,
                              struct fo_server_info **_servers,
                              size_t *_num_servers)
 {
@@ -159,6 +161,10 @@ errno_t fo_discover_srv_recv(TALLOC_CTX *mem_ctx,
         *_servers = talloc_steal(mem_ctx, state->servers);
     }
 
+    if (_ttl != NULL) {
+        *_ttl = state->ttl;
+    }
+
     if (_num_servers != NULL) {
         *_num_servers = state->num_servers;
     }
@@ -175,6 +181,7 @@ struct fo_discover_servers_state {
     const char *backup_domain;
 
     char *dns_domain;
+    uint32_t ttl;
     struct fo_server_info *primary_servers;
     size_t num_primary_servers;
     struct fo_server_info *backup_servers;
@@ -212,6 +219,7 @@ struct tevent_req *fo_discover_servers_send(TALLOC_CTX *mem_ctx,
             state->backup_servers = NULL;
             state->num_backup_servers = 0;
             state->dns_domain = NULL;
+            state->ttl = 0;
 
             ret = EOK;
             goto immediately;
@@ -289,6 +297,7 @@ static void fo_discover_servers_primary_done(struct tevent_req *subreq)
 
     ret = fo_discover_srv_recv(state, subreq,
                                &state->dns_domain,
+                               &state->ttl,
                                &state->primary_servers,
                                &state->num_primary_servers);
     talloc_zfree(subreq);
@@ -361,7 +370,7 @@ static void fo_discover_servers_backup_done(struct tevent_req *subreq)
     state = tevent_req_data(req, struct fo_discover_servers_state);
 
     ret = fo_discover_srv_recv(state, subreq, NULL,
-                               &state->backup_servers,
+                               NULL, &state->backup_servers,
                                &state->num_backup_servers);
     talloc_zfree(subreq);
     if (ret != EOK) {
@@ -385,6 +394,7 @@ static void fo_discover_servers_backup_done(struct tevent_req *subreq)
 errno_t fo_discover_servers_recv(TALLOC_CTX *mem_ctx,
                                  struct tevent_req *req,
                                  char **_dns_domain,
+                                 uint32_t *_ttl,
                                  struct fo_server_info **_primary_servers,
                                  size_t *_num_primary_servers,
                                  struct fo_server_info **_backup_servers,
@@ -415,6 +425,10 @@ errno_t fo_discover_servers_recv(TALLOC_CTX *mem_ctx,
         *_dns_domain = talloc_steal(mem_ctx, state->dns_domain);
     }
 
+    if (_ttl) {
+        *_ttl = state->ttl;
+    }
+
 
     return EOK;
 }
@@ -436,6 +450,7 @@ struct fo_resolve_srv_dns_state {
     const char *discovery_domain;
 
     char *dns_domain;
+    uint32_t ttl;
     struct fo_server_info *servers;
     size_t num_servers;
 };
@@ -644,7 +659,8 @@ static void fo_resolve_srv_dns_done(struct tevent_req *subreq)
     req = tevent_req_callback_data(subreq, struct tevent_req);
     state = tevent_req_data(req, struct fo_resolve_srv_dns_state);
 
-    ret = fo_discover_srv_recv(state, subreq, &state->dns_domain,
+    ret = fo_discover_srv_recv(state, subreq,
+                               &state->dns_domain, &state->ttl,
                                &state->servers, &state->num_servers);
     talloc_zfree(subreq);
     if (ret != EOK) {
@@ -663,6 +679,7 @@ done:
 errno_t fo_resolve_srv_dns_recv(TALLOC_CTX *mem_ctx,
                                 struct tevent_req *req,
                                 char **_dns_domain,
+                                uint32_t *_ttl,
                                 struct fo_server_info **_primary_servers,
                                 size_t *_num_primary_servers,
                                 struct fo_server_info **_backup_servers,
@@ -694,5 +711,9 @@ errno_t fo_resolve_srv_dns_recv(TALLOC_CTX *mem_ctx,
         *_dns_domain = talloc_steal(mem_ctx, state->dns_domain);
     }
 
+    if (_ttl) {
+        *_ttl = state->ttl;
+    }
+
     return EOK;
 }
diff --git a/src/providers/fail_over_srv.h b/src/providers/fail_over_srv.h
index c4b50ba33ab8370c62e8af48aca62de69f0f5627..fe4088e029ee05a77f699a9dee389f160615527b 100644
--- a/src/providers/fail_over_srv.h
+++ b/src/providers/fail_over_srv.h
@@ -65,6 +65,7 @@ typedef errno_t
 (*fo_srv_lookup_plugin_recv_t)(TALLOC_CTX *mem_ctx,
                                struct tevent_req *req,
                                char **_dns_domain,
+                               uint32_t *_ttl,
                                struct fo_server_info **_primary_servers,
                                size_t *_num_primary_servers,
                                struct fo_server_info **_backup_servers,
@@ -80,6 +81,7 @@ struct tevent_req *fo_discover_srv_send(TALLOC_CTX *mem_ctx,
 errno_t fo_discover_srv_recv(TALLOC_CTX *mem_ctx,
                              struct tevent_req *req,
                              char **_dns_domain,
+                             uint32_t *_ttl,
                              struct fo_server_info **_servers,
                              size_t *_num_servers);
 
@@ -94,6 +96,7 @@ struct tevent_req *fo_discover_servers_send(TALLOC_CTX *mem_ctx,
 errno_t fo_discover_servers_recv(TALLOC_CTX *mem_ctx,
                                  struct tevent_req *req,
                                  char **_dns_domain,
+                                 uint32_t *_ttl,
                                  struct fo_server_info **_primary_servers,
                                  size_t *_num_primary_servers,
                                  struct fo_server_info **_backup_servers,
@@ -121,6 +124,7 @@ struct tevent_req *fo_resolve_srv_dns_send(TALLOC_CTX *mem_ctx,
 errno_t fo_resolve_srv_dns_recv(TALLOC_CTX *mem_ctx,
                                 struct tevent_req *req,
                                 char **_dns_domain,
+                                uint32_t *_ttl,
                                 struct fo_server_info **_primary_servers,
                                 size_t *_num_primary_servers,
                                 struct fo_server_info **_backup_servers,
diff --git a/src/providers/ipa/ipa_srv.c b/src/providers/ipa/ipa_srv.c
index 4be5f3317f4d8ad0e4cd09a3093122b3f710a0b2..7477711347bfe4936a324baa1d0c1c23c3877679 100644
--- a/src/providers/ipa/ipa_srv.c
+++ b/src/providers/ipa/ipa_srv.c
@@ -69,6 +69,7 @@ fail:
 
 struct ipa_srv_plugin_state {
     char *dns_domain;
+    uint32_t ttl;
     struct fo_server_info *primary_servers;
     size_t num_primary_servers;
     struct fo_server_info *backup_servers;
@@ -164,6 +165,7 @@ static void ipa_srv_plugin_done(struct tevent_req *subreq)
     state = tevent_req_data(req, struct ipa_srv_plugin_state);
 
     ret = fo_discover_servers_recv(state, subreq, &state->dns_domain,
+                                   &state->ttl,
                                    &state->primary_servers,
                                    &state->num_primary_servers,
                                    &state->backup_servers,
@@ -183,6 +185,7 @@ static void ipa_srv_plugin_done(struct tevent_req *subreq)
 errno_t ipa_srv_plugin_recv(TALLOC_CTX *mem_ctx,
                             struct tevent_req *req,
                             char **_dns_domain,
+                            uint32_t *_ttl,
                             struct fo_server_info **_primary_servers,
                             size_t *_num_primary_servers,
                             struct fo_server_info **_backup_servers,
@@ -213,6 +216,9 @@ errno_t ipa_srv_plugin_recv(TALLOC_CTX *mem_ctx,
         *_dns_domain = talloc_steal(mem_ctx, state->dns_domain);
     }
 
+    if (_ttl) {
+        *_ttl = state->ttl;
+    }
 
     return EOK;
 }
diff --git a/src/providers/ipa/ipa_srv.h b/src/providers/ipa/ipa_srv.h
index 1bfd2eeaebec6ae9d2826aa76060746a32a634fc..d089c9f64714b8adc8e789d2a35766fbf339d7fa 100644
--- a/src/providers/ipa/ipa_srv.h
+++ b/src/providers/ipa/ipa_srv.h
@@ -39,6 +39,7 @@ struct tevent_req *ipa_srv_plugin_send(TALLOC_CTX *mem_ctx,
 errno_t ipa_srv_plugin_recv(TALLOC_CTX *mem_ctx,
                             struct tevent_req *req,
                             char **_dns_domain,
+                            uint32_t *_ttl,
                             struct fo_server_info **_primary_servers,
                             size_t *_num_primary_servers,
                             struct fo_server_info **_backup_servers,
diff --git a/src/tests/cmocka/test_fo_srv.c b/src/tests/cmocka/test_fo_srv.c
new file mode 100644
index 0000000000000000000000000000000000000000..3ee308bd5967f44abda1980e7bff8e0a5c00dde0
--- /dev/null
+++ b/src/tests/cmocka/test_fo_srv.c
@@ -0,0 +1,599 @@
+/*
+    Authors:
+        Jakub Hrozek <jhrozek at redhat.com>
+
+    Copyright (C) 2014 Red Hat
+
+    SSSD tests: Resolver tests using a fake resolver library
+
+    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 <talloc.h>
+#include <tevent.h>
+#include <errno.h>
+#include <popt.h>
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include <sys/types.h>
+#include <stdarg.h>
+#include <stdlib.h>
+
+#include "providers/fail_over_srv.h"
+#include "tests/cmocka/common_mock.h"
+#include "tests/cmocka/common_mock_resp.h"
+
+#define TEST_RESOLV_TIMEOUT 5
+#define TEST_FO_TIMEOUT     3000
+#define TEST_SRV_TTL        500
+#define TEST_SRV_SHORT_TTL  2
+
+static TALLOC_CTX *global_mock_context = NULL;
+
+enum host_database default_host_dbs[] = { DB_FILES, DB_DNS, DB_SENTINEL };
+
+struct resolv_ctx {
+    int foo;
+};
+
+/* mock resolver interface. The resolver test is separate */
+int resolv_init(TALLOC_CTX *mem_ctx, struct tevent_context *ev_ctx,
+                int timeout, struct resolv_ctx **ctxp)
+{
+    *ctxp = talloc(mem_ctx, struct resolv_ctx);
+    return EOK;
+}
+
+struct tevent_req *
+resolv_gethostbyname_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+                          struct resolv_ctx *ctx, const char *name,
+                          enum restrict_family family_order,
+                          enum host_database *db)
+{
+    return test_req_succeed_send(mem_ctx, ev);
+}
+
+int resolv_gethostbyname_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
+                              int *status, int *timeouts,
+                              struct resolv_hostent **rhostent)
+{
+    return test_request_recv(req);
+}
+
+const char *resolv_strerror(int ares_code)
+{
+    return NULL;
+}
+
+struct tevent_req *resolv_discover_srv_send(TALLOC_CTX *mem_ctx,
+                                            struct tevent_context *ev,
+                                            struct resolv_ctx *resolv_ctx,
+                                            const char *service,
+                                            const char *protocol,
+                                            const char **discovery_domains)
+{
+    return test_req_succeed_send(mem_ctx, ev);
+}
+
+errno_t resolv_discover_srv_recv(TALLOC_CTX *mem_ctx,
+                                 struct tevent_req *req,
+                                 struct ares_srv_reply **_reply_list,
+                                 uint32_t *_ttl,
+                                 char **_dns_domain)
+{
+    struct ares_srv_reply *reply_list;
+    uint32_t ttl;
+    char *dns_domain;
+
+    /* Need to always consume all mocked values */
+    reply_list = sss_mock_ptr_type(struct ares_srv_reply *);
+    ttl = sss_mock_ptr_type(uint32_t);
+    dns_domain = sss_mock_ptr_type(char *);
+
+    if (_reply_list != NULL) {
+        *_reply_list = reply_list;
+    }
+
+    if (_ttl != NULL) {
+        *_ttl = ttl;
+    }
+
+    if (_dns_domain != NULL) {
+        *_dns_domain = dns_domain;
+    }
+
+    return test_request_recv(req);
+}
+
+struct ares_srv_reply *pop_lowest_prio(struct ares_srv_reply **r)
+{
+    struct ares_srv_reply *lowest;
+    struct ares_srv_reply *iter;
+    struct ares_srv_reply *prev;
+
+    lowest = *r;
+    iter = lowest;
+    while (iter != NULL) {
+        if (iter->priority < lowest->priority) {
+            lowest = iter;
+        }
+
+        iter = iter->next;
+    }
+
+    prev = NULL;
+    iter = *r;
+    while (iter != lowest) {
+        prev = iter;
+        iter = iter->next;
+    }
+
+    /* iter points to the lowest prio. Prev points to the item before */
+    if (prev) {
+        prev->next = lowest->next;
+    } else {
+        *r = lowest->next;
+    }
+
+    return lowest;
+}
+
+int resolv_sort_srv_reply(struct ares_srv_reply **reply)
+{
+    struct ares_srv_reply *r;
+    struct ares_srv_reply *lowest;
+    struct ares_srv_reply *sorted = NULL;
+    struct ares_srv_reply *sorted_head = NULL;
+
+    r = *reply;
+    if (r == NULL || r->next == NULL) {
+        return EOK;
+    }
+
+    do {
+        lowest = pop_lowest_prio(&r);
+        if (sorted) {
+            sorted->next = lowest;
+            sorted = sorted->next;
+        } else {
+            sorted = lowest;
+            sorted_head = sorted;
+        }
+    } while (r != NULL);
+
+    *reply = sorted_head;
+    return EOK;
+}
+
+struct tevent_req *resolv_get_domain_send(TALLOC_CTX *mem_ctx,
+                                          struct tevent_context *ev,
+                                          struct resolv_ctx *resolv_ctx,
+                                          const char *hostname,
+                                          enum host_database *host_dbs,
+                                          enum restrict_family family_order)
+{
+    return test_req_succeed_send(mem_ctx, ev);
+}
+
+errno_t resolv_get_domain_recv(TALLOC_CTX *mem_ctx,
+                               struct tevent_req *req,
+                               char **_dns_domain)
+{
+    return test_request_recv(req);
+}
+
+/* The unit test */
+struct test_fo_srv_ctx {
+    struct resolv_ctx *resolv;
+    struct fo_ctx *fo_ctx;
+    struct fo_resolve_srv_dns_ctx *srv_ctx;
+    struct fo_service *fo_svc;
+    struct sss_test_ctx *ctx;
+};
+
+int test_fo_srv_data_cmp(void *ud1, void *ud2)
+{
+    return strcasecmp((char*) ud1, (char*) ud2);
+}
+
+void test_fo_srv_setup(void **state)
+{
+    struct test_fo_srv_ctx *test_ctx;
+    errno_t ret;
+    struct fo_options fopts;
+    bool ok;
+
+    assert_true(leak_check_setup());
+    global_mock_context = talloc_new(global_talloc_context);
+    assert_non_null(global_mock_context);
+
+    test_ctx = talloc_zero(global_mock_context,
+                           struct test_fo_srv_ctx);
+    assert_non_null(test_ctx);
+
+    test_ctx->ctx = create_ev_test_ctx(test_ctx);
+    assert_non_null(test_ctx->ctx);
+
+    ret = resolv_init(test_ctx, test_ctx->ctx->ev,
+                      TEST_RESOLV_TIMEOUT, &test_ctx->resolv);
+    assert_non_null(test_ctx->resolv);
+
+    memset(&fopts, 0, sizeof(fopts));
+    fopts.retry_timeout = TEST_FO_TIMEOUT;
+    fopts.family_order  = IPV4_FIRST;
+
+    test_ctx->fo_ctx = fo_context_init(test_ctx, &fopts);
+    assert_non_null(test_ctx->fo_ctx);
+
+    test_ctx->srv_ctx = fo_resolve_srv_dns_ctx_init(test_ctx, test_ctx->resolv,
+                                                    IPV4_FIRST, default_host_dbs,
+                                                    "client.sssd.com", "sssd.local");
+    assert_non_null(test_ctx->srv_ctx);
+
+    ok = fo_set_srv_lookup_plugin(test_ctx->fo_ctx,
+                                  fo_resolve_srv_dns_send,
+                                  fo_resolve_srv_dns_recv,
+                                  test_ctx->srv_ctx);
+    assert_true(ok);
+
+    ret = fo_new_service(test_ctx->fo_ctx, "ldap",
+                         test_fo_srv_data_cmp,
+                         &test_ctx->fo_svc);
+    assert_int_equal(ret, ERR_OK);
+
+    *state = test_ctx;
+}
+
+void test_fo_srv_teardown(void **state)
+{
+    struct test_fo_srv_ctx *test_ctx =
+        talloc_get_type(*state, struct test_fo_srv_ctx);
+
+    talloc_free(test_ctx);
+    talloc_free(global_mock_context);
+    assert_true(leak_check_teardown());
+}
+
+/* reply_list and dns_domain must be a talloc context so it can be used as
+ * talloc_steal argument later
+ */
+static void mock_srv_results(struct ares_srv_reply *reply_list,
+                             uint32_t ttl,
+                             char *dns_domain)
+{
+    will_return(resolv_discover_srv_recv, reply_list);
+    will_return(resolv_discover_srv_recv, ttl);
+    will_return(resolv_discover_srv_recv, dns_domain);
+}
+
+static void check_server(struct fo_server *srv, int port, const char *name)
+{
+    assert_non_null(srv);
+    assert_true(fo_is_srv_lookup(srv));
+    assert_int_equal(fo_get_server_port(srv), port);
+    assert_string_equal(fo_get_server_name(srv), name);
+}
+
+static void test_fo_srv_done1(struct tevent_req *req);
+static void test_fo_srv_done2(struct tevent_req *req);
+static void test_fo_srv_done3(struct tevent_req *req);
+static void test_fo_srv_done4(struct tevent_req *req);
+
+void test_fo_srv(void **state)
+{
+    errno_t ret;
+    struct tevent_req *req;
+    struct test_fo_srv_ctx *test_ctx =
+        talloc_get_type(*state, struct test_fo_srv_ctx);
+    struct ares_srv_reply *s1;
+    struct ares_srv_reply *s2;
+    char *dns_domain;
+
+    s1 = talloc_zero(test_ctx, struct ares_srv_reply);
+    assert_non_null(s1);
+    s1->host = talloc_strdup(s1, "ldap1.sssd.com");
+    assert_non_null(s1->host);
+    s1->weight = 100;
+    s1->priority = 1;
+    s1->port = 389;
+
+    s2 = talloc_zero(test_ctx, struct ares_srv_reply);
+    assert_non_null(s2);
+    s2->host = talloc_strdup(s2, "ldap2.sssd.com");
+    assert_non_null(s2->host);
+    s2->weight = 100;
+    s2->priority = 2;
+    s2->port = 389;
+
+    s1->next = s2;
+
+    dns_domain = talloc_strdup(test_ctx, "sssd.com");
+    assert_non_null(dns_domain);
+
+    mock_srv_results(s1, TEST_SRV_TTL, dns_domain);
+
+    ret = fo_add_srv_server(test_ctx->fo_svc, "_ldap", "sssd.com",
+                            "sssd.local", "tcp", test_ctx);
+    assert_int_equal(ret, ERR_OK);
+
+    req = fo_resolve_service_send(test_ctx, test_ctx->ctx->ev,
+                                  test_ctx->resolv, test_ctx->fo_ctx,
+                                  test_ctx->fo_svc);
+    assert_non_null(req);
+    tevent_req_set_callback(req, test_fo_srv_done1, test_ctx);
+
+    ret = test_ev_loop(test_ctx->ctx);
+    assert_int_equal(ret, ERR_OK);
+}
+
+static void test_fo_srv_done1(struct tevent_req *req)
+{
+    struct test_fo_srv_ctx *test_ctx = \
+        tevent_req_callback_data(req, struct test_fo_srv_ctx);
+    struct fo_server *srv;
+    errno_t ret;
+
+    ret = fo_resolve_service_recv(req, &srv);
+    talloc_zfree(req);
+    assert_int_equal(ret, ERR_OK);
+
+    /* ldap1.sssd.com has lower priority, it must always be first */
+    check_server(srv, 389, "ldap1.sssd.com");
+
+    /* Mark the server as working and request the service again. The same server
+     * must be returned */
+    fo_set_server_status(srv, SERVER_WORKING);
+
+    req = fo_resolve_service_send(test_ctx, test_ctx->ctx->ev,
+                                  test_ctx->resolv, test_ctx->fo_ctx,
+                                  test_ctx->fo_svc);
+    assert_non_null(req);
+    tevent_req_set_callback(req, test_fo_srv_done2, test_ctx);
+}
+
+static void test_fo_srv_done2(struct tevent_req *req)
+{
+    struct test_fo_srv_ctx *test_ctx = \
+        tevent_req_callback_data(req, struct test_fo_srv_ctx);
+    struct fo_server *srv;
+    errno_t ret;
+
+    ret = fo_resolve_service_recv(req, &srv);
+    talloc_zfree(req);
+    assert_int_equal(ret, ERR_OK);
+
+    /* Must be ldap1 again */
+    check_server(srv, 389, "ldap1.sssd.com");
+
+    /* Mark it at wrong, next lookup should yield ldap2 */
+    fo_set_server_status(srv, SERVER_NOT_WORKING);
+
+    req = fo_resolve_service_send(test_ctx, test_ctx->ctx->ev,
+                                  test_ctx->resolv, test_ctx->fo_ctx,
+                                  test_ctx->fo_svc);
+    assert_non_null(req);
+    tevent_req_set_callback(req, test_fo_srv_done3, test_ctx);
+}
+
+static void test_fo_srv_done3(struct tevent_req *req)
+{
+    struct test_fo_srv_ctx *test_ctx = \
+        tevent_req_callback_data(req, struct test_fo_srv_ctx);
+    struct fo_server *srv;
+    errno_t ret;
+
+    ret = fo_resolve_service_recv(req, &srv);
+    talloc_zfree(req);
+    assert_int_equal(ret, ERR_OK);
+
+    /* Must be ldap2 now */
+    check_server(srv, 389, "ldap2.sssd.com");
+
+    /* Mark is at wrong, next lookup must reach the end of the server list */
+    fo_set_server_status(srv, SERVER_NOT_WORKING);
+
+    req = fo_resolve_service_send(test_ctx, test_ctx->ctx->ev,
+                                  test_ctx->resolv, test_ctx->fo_ctx,
+                                  test_ctx->fo_svc);
+    assert_non_null(req);
+    tevent_req_set_callback(req, test_fo_srv_done4, test_ctx);
+}
+
+static void test_fo_srv_done4(struct tevent_req *req)
+{
+    struct test_fo_srv_ctx *test_ctx = \
+        tevent_req_callback_data(req, struct test_fo_srv_ctx);
+    struct fo_server *srv;
+    errno_t ret;
+
+    ret = fo_resolve_service_recv(req, &srv);
+    talloc_zfree(req);
+    /* No servers are left..*/
+    assert_int_equal(ret, ENOENT);
+
+    test_ctx->ctx->error = ERR_OK;
+    test_ctx->ctx->done = true;
+}
+
+/* Make sure that two queries more than TTL seconds apart resolve
+ * into two different lists
+ */
+static void test_fo_srv_before(struct tevent_req *req);
+static void test_fo_srv_after(struct tevent_req *req);
+
+void test_fo_srv_ttl_change(void **state)
+{
+    errno_t ret;
+    struct tevent_req *req;
+    struct test_fo_srv_ctx *test_ctx =
+        talloc_get_type(*state, struct test_fo_srv_ctx);
+    struct ares_srv_reply *s1;
+    struct ares_srv_reply *s2;
+    char *dns_domain;
+
+    s1 = talloc_zero(test_ctx, struct ares_srv_reply);
+    assert_non_null(s1);
+    s1->host = talloc_strdup(s1, "ldap1.sssd.com");
+    assert_non_null(s1->host);
+    s1->weight = 100;
+    s1->priority = 1;
+    s1->port = 389;
+
+    s2 = talloc_zero(test_ctx, struct ares_srv_reply);
+    assert_non_null(s2);
+    s2->host = talloc_strdup(s2, "ldap2.sssd.com");
+    assert_non_null(s2->host);
+    s2->weight = 100;
+    s2->priority = 2;
+    s2->port = 389;
+
+    s1->next = s2;
+
+    dns_domain = talloc_strdup(test_ctx, "sssd.com");
+    assert_non_null(dns_domain);
+
+    mock_srv_results(s1, TEST_SRV_SHORT_TTL, dns_domain);
+
+    ret = fo_add_srv_server(test_ctx->fo_svc, "_ldap", "sssd.com",
+                            "sssd.local", "tcp", test_ctx);
+    assert_int_equal(ret, ERR_OK);
+
+    ret = fo_add_server(test_ctx->fo_svc, "ldap1.sssd.com",
+                        389, (void *) discard_const("ldap://ldap1.sssd.com"),
+                        true);
+    assert_int_equal(ret, ERR_OK);
+
+    req = fo_resolve_service_send(test_ctx, test_ctx->ctx->ev,
+                                  test_ctx->resolv, test_ctx->fo_ctx,
+                                  test_ctx->fo_svc);
+    assert_non_null(req);
+    tevent_req_set_callback(req, test_fo_srv_before, test_ctx);
+
+    ret = test_ev_loop(test_ctx->ctx);
+    assert_int_equal(ret, ERR_OK);
+}
+
+static void test_fo_srv_before(struct tevent_req *req)
+{
+    struct test_fo_srv_ctx *test_ctx = \
+        tevent_req_callback_data(req, struct test_fo_srv_ctx);
+    struct fo_server *srv;
+    struct ares_srv_reply *s1;
+    struct ares_srv_reply *s2;
+    char *dns_domain;
+    errno_t ret;
+
+    ret = fo_resolve_service_recv(req, &srv);
+    talloc_zfree(req);
+    assert_int_equal(ret, ERR_OK);
+
+    DEBUG(SSSDBG_TRACE_FUNC, "Before TTL change\n");
+
+    check_server(srv, 389, "ldap1.sssd.com");
+    fo_set_server_status(srv, SERVER_WORKING);
+
+    /* Simulate changing the DNS environment. Change the host names */
+    s1 = talloc_zero(test_ctx, struct ares_srv_reply);
+    assert_non_null(s1);
+    s1->host = talloc_strdup(s1, "ldap2.sssd.com");
+    assert_non_null(s1->host);
+    s1->weight = 100;
+    s1->priority = 2;
+    s1->port = 389;
+
+    s2 = talloc_zero(test_ctx, struct ares_srv_reply);
+    assert_non_null(s2);
+    s2->host = talloc_strdup(s2, "ldap3.sssd.com");
+    assert_non_null(s2->host);
+    s2->weight = 100;
+    s2->priority = 1;
+    s2->port = 389;
+
+    s1->next = s2;
+
+    dns_domain = talloc_strdup(test_ctx, "sssd.com");
+    assert_non_null(dns_domain);
+
+    mock_srv_results(s1, TEST_SRV_SHORT_TTL, dns_domain);
+    sleep(TEST_SRV_SHORT_TTL + 1);
+
+    req = fo_resolve_service_send(test_ctx, test_ctx->ctx->ev,
+                                  test_ctx->resolv, test_ctx->fo_ctx,
+                                  test_ctx->fo_svc);
+    assert_non_null(req);
+    tevent_req_set_callback(req, test_fo_srv_after, test_ctx);
+}
+
+static void test_fo_srv_after(struct tevent_req *req)
+{
+    struct test_fo_srv_ctx *test_ctx = \
+        tevent_req_callback_data(req, struct test_fo_srv_ctx);
+    struct fo_server *srv;
+    errno_t ret;
+
+    ret = fo_resolve_service_recv(req, &srv);
+    talloc_zfree(req);
+    assert_int_equal(ret, ERR_OK);
+
+    /* Must be a different server now */
+    check_server(srv, 389, "ldap3.sssd.com");
+
+    test_ctx->ctx->error = ERR_OK;
+    test_ctx->ctx->done = true;
+}
+
+int main(int argc, const char *argv[])
+{
+    int rv;
+    poptContext pc;
+    int opt;
+    struct poptOption long_options[] = {
+        POPT_AUTOHELP
+        SSSD_DEBUG_OPTS
+        POPT_TABLEEND
+    };
+
+    const UnitTest tests[] = {
+        unit_test_setup_teardown(test_fo_srv,
+                                 test_fo_srv_setup,
+                                 test_fo_srv_teardown),
+        unit_test_setup_teardown(test_fo_srv_ttl_change,
+                                 test_fo_srv_setup,
+                                 test_fo_srv_teardown),
+    };
+
+    /* Set debug level to invalid value so we can deside if -d 0 was used. */
+    debug_level = SSSDBG_INVALID;
+
+    pc = poptGetContext(argv[0], argc, argv, long_options, 0);
+    while((opt = poptGetNextOpt(pc)) != -1) {
+        switch(opt) {
+        default:
+            fprintf(stderr, "\nInvalid option %s: %s\n\n",
+                    poptBadOption(pc, 0), poptStrerror(opt));
+            poptPrintUsage(pc, stderr, 0);
+            return 1;
+        }
+    }
+    poptFreeContext(pc);
+
+    DEBUG_CLI_INIT(debug_level);
+
+    /* Even though normally the tests should clean up after themselves
+     * they might not after a failed run. Remove the old db to be sure */
+    tests_set_cwd();
+
+    rv = run_tests(tests);
+    return rv;
+}
-- 
2.1.0



More information about the sssd-devel mailing list