summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJakub Hrozek <jhrozek@redhat.com>2014-12-12 17:10:25 +0100
committerJakub Hrozek <jhrozek@redhat.com>2015-02-11 11:03:34 +0100
commitbf54fbed126ec3d459af40ea370ffadacd31c76d (patch)
treefb600d300e7939e94b0eedc100a95c330de7d782
parent4d7fe714fe74ad242497b2bdbeb7b4e0bf40141f (diff)
downloadsssd-bf54fbed126ec3d459af40ea370ffadacd31c76d.tar.gz
sssd-bf54fbed126ec3d459af40ea370ffadacd31c76d.tar.xz
sssd-bf54fbed126ec3d459af40ea370ffadacd31c76d.zip
RESOLV: Add an internal function to read TTL from a DNS packet
Related: https://fedorahosted.org/sssd/ticket/1884 Adds an internal resolver function that reads the TTL for SRV records as specified by RFC-2181. Several internal c-ares definitions are used until c-ares contains a function that exposes all this information via a parsing function. Reviewed-by: Pavel Březina <pbrezina@redhat.com>
-rw-r--r--Makefile.am27
-rw-r--r--configure.ac1
-rw-r--r--src/external/libresolv.m412
-rw-r--r--src/providers/fail_over_srv.c2
-rw-r--r--src/resolv/async_resolv.c113
-rw-r--r--src/resolv/async_resolv.h4
-rw-r--r--src/resolv/async_resolv_utils.c9
-rw-r--r--src/tests/cmocka/test_resolv_fake.c374
-rw-r--r--src/tests/resolv-tests.c2
9 files changed, 539 insertions, 5 deletions
diff --git a/Makefile.am b/Makefile.am
index ff17b41c1..0975dc2ba 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -222,6 +222,10 @@ if HAVE_CMOCKA
test_sbus_opath \
$(NULL)
+if HAVE_LIBRESOLV
+non_interactive_cmocka_based_tests += test_resolv_fake
+endif # HAVE_LIBRESOLV
+
if BUILD_IFP
non_interactive_cmocka_based_tests += ifp_tests
endif # BUILD_IFP
@@ -2221,6 +2225,29 @@ test_sbus_opath_LDADD = \
libsss_debug.la \
libsss_test_common.la
+if HAVE_LIBRESOLV
+test_resolv_fake_SOURCES = \
+ src/tests/cmocka/test_resolv_fake.c \
+ src/resolv/async_resolv.c \
+ $(NULL)
+test_resolv_fake_CFLAGS = \
+ $(AM_CFLAGS) \
+ $(NULL)
+test_resolv_fake_LDFLAGS = \
+ -Wl,-wrap,ares_query \
+ $(NULL)
+test_resolv_fake_LDADD = \
+ $(CMOCKA_LIBS) \
+ $(POPT_LIBS) \
+ $(TALLOC_LIBS) \
+ $(CARES_LIBS) \
+ $(DHASH_LIBS) \
+ $(RESOLV_LIBS) \
+ libsss_debug.la \
+ libsss_test_common.la \
+ $(NULL)
+endif # HAVE_LIBRESOLV
+
endif # HAVE_CMOCKA
noinst_PROGRAMS = pam_test_client
diff --git a/configure.ac b/configure.ac
index e5ec204ad..dd64df3d9 100644
--- a/configure.ac
+++ b/configure.ac
@@ -173,6 +173,7 @@ m4_include([src/external/sasl.m4])
m4_include([src/external/configlib.m4])
m4_include([src/external/libnfsidmap.m4])
m4_include([src/external/cwrap.m4])
+m4_include([src/external/libresolv.m4])
if test x$build_config_lib = xyes; then
m4_include([src/external/libaugeas.m4])
diff --git a/src/external/libresolv.m4 b/src/external/libresolv.m4
new file mode 100644
index 000000000..225cf2be8
--- /dev/null
+++ b/src/external/libresolv.m4
@@ -0,0 +1,12 @@
+AC_SUBST(RESOLV_CFLAGS)
+AC_SUBST(RESOLV_LIBS)
+
+# Some unit tests require libresolv to fake DNS packets
+SSS_AC_EXPAND_LIB_DIR()
+AC_CHECK_LIB([resolv],
+ [ns_name_compress],
+ [RESOLV_LIBS="-L$sss_extra_libdir -lresolv"],
+ [AC_MSG_WARN([No libresolv detected, some tests will not run])],
+ [-L$sss_extra_libdir])
+
+AM_CONDITIONAL([HAVE_LIBRESOLV], [test x"$RESOLV_LIBS" != "x"])
diff --git a/src/providers/fail_over_srv.c b/src/providers/fail_over_srv.c
index 1b99f3150..5c06d2876 100644
--- a/src/providers/fail_over_srv.c
+++ b/src/providers/fail_over_srv.c
@@ -83,7 +83,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, &state->dns_domain);
+ &reply_list, NULL, &state->dns_domain);
talloc_zfree(subreq);
if (ret == ENOENT) {
ret = ERR_SRV_NOT_FOUND;
diff --git a/src/resolv/async_resolv.c b/src/resolv/async_resolv.c
index f93612440..85c4d99a4 100644
--- a/src/resolv/async_resolv.c
+++ b/src/resolv/async_resolv.c
@@ -62,7 +62,21 @@
#endif
#define DNS__16BIT(p) (((p)[0] << 8) | (p)[1])
+
+/*
+ * Macro DNS__32BIT reads a network long (32 bit) given in network
+ * byte order, and returns its value as an unsigned int. Copied
+ * from c-ares source code.
+ */
+#define DNS__32BIT(p) ((unsigned int) \
+ (((unsigned int)((unsigned char)(p)[0]) << 24U) | \
+ ((unsigned int)((unsigned char)(p)[1]) << 16U) | \
+ ((unsigned int)((unsigned char)(p)[2]) << 8U) | \
+ ((unsigned int)((unsigned char)(p)[3]))))
+
#define DNS_HEADER_ANCOUNT(h) DNS__16BIT((h) + 6)
+#define DNS_RR_LEN(r) DNS__16BIT((r) + 8)
+#define DNS_RR_TTL(r) DNS__32BIT((r) + 4)
#define RESOLV_TIMEOUTMS 2000
@@ -1572,6 +1586,7 @@ struct getsrv_state {
/* parsed data returned by ares */
struct ares_srv_reply *reply_list;
+ uint32_t ttl;
int status;
int timeouts;
int retrying;
@@ -1607,6 +1622,7 @@ resolv_getsrv_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
state->resolv_ctx = ctx;
state->query = query;
state->reply_list = NULL;
+ state->ttl = 0;
state->status = 0;
state->timeouts = 0;
state->retrying = 0;
@@ -1624,6 +1640,92 @@ resolv_getsrv_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
return req;
}
+/*
+ * Implemented based on http://tools.ietf.org/html/rfc2181#section-5
+ *
+ * Especially:
+ * 5.2. TTLs of RRs in an RRSet
+ * Consequently the use of differing TTLs in an RRSet is hereby
+ * deprecated, the TTLs of all RRs in an RRSet must be the same.
+ * ...
+ * Should an authoritative source send such a malformed RRSet, the
+ * client should treat the RRs for all purposes as if all TTLs in the
+ * RRSet had been set to the value of the lowest TTL in the RRSet.
+ *
+ * On success, returns true and sets the TTL in the _ttl parameter. On
+ * failure, returns false and _ttl is undefined.
+ */
+static bool
+resolv_get_ttl(const unsigned char *abuf, const int alen, uint32_t *_ttl)
+{
+ const unsigned char *aptr;
+ int ret;
+ char *name = NULL;
+ long len;
+ uint32_t ttl = 0;
+ uint32_t rr_ttl;
+ unsigned int rr_len;
+ unsigned int ancount;
+ unsigned int i;
+
+ /* Read the number of RRs and then skip past the header */
+ if (alen < NS_HFIXEDSZ) {
+ return false;
+ }
+
+ ancount = DNS_HEADER_ANCOUNT(abuf);
+ if (ancount == 0) {
+ return false;
+ }
+
+ aptr = abuf + NS_HFIXEDSZ;
+
+ /* We only care about len from the question data,
+ * so that we can move past hostname */
+ ret = ares_expand_name(aptr, abuf, alen, &name, &len);
+ ares_free_string(name);
+ if (ret != ARES_SUCCESS) {
+ return false;
+ }
+
+ /* Skip past the question */
+ aptr += len + NS_QFIXEDSZ;
+ if (aptr > abuf + alen) {
+ return false;
+ }
+
+ /* Examine each RR in turn and read the lowest TTL */
+ for (i = 0; i < ancount; i++) {
+ /* Decode the RR up to the data field. */
+ ret = ares_expand_name(aptr, abuf, alen, &name, &len);
+ ares_free_string(name);
+ if (ret != ARES_SUCCESS) {
+ return false;
+ }
+
+ aptr += len;
+ if (aptr + NS_RRFIXEDSZ > abuf + alen) {
+ return false;
+ }
+
+ rr_len = DNS_RR_LEN(aptr);
+ rr_ttl = DNS_RR_TTL(aptr);
+ if (aptr + rr_len > abuf + alen) {
+ return false;
+ }
+ aptr += NS_RRFIXEDSZ + rr_len;
+
+ if (ttl > 0) {
+ ttl = MIN(ttl, rr_ttl);
+ } else {
+ ttl = rr_ttl; /* special-case for first TTL */
+ }
+ }
+
+ *_ttl = ttl;
+ return true;
+}
+
static void
resolv_getsrv_done(void *arg, int status, int timeouts, unsigned char *abuf, int alen)
{
@@ -1631,6 +1733,7 @@ resolv_getsrv_done(void *arg, int status, int timeouts, unsigned char *abuf, int
struct tevent_req *req;
struct getsrv_state *state;
int ret;
+ bool ok;
struct ares_srv_reply *reply_list;
if (rreq->rwatch == NULL) {
@@ -1671,6 +1774,10 @@ resolv_getsrv_done(void *arg, int status, int timeouts, unsigned char *abuf, int
goto fail;
}
state->reply_list = reply_list;
+ ok = resolv_get_ttl(abuf, alen, &state->ttl);
+ if (ok == false) {
+ state->ttl = RESOLV_DEFAULT_TTL;
+ }
tevent_req_done(req);
return;
@@ -1682,7 +1789,8 @@ fail:
int
resolv_getsrv_recv(TALLOC_CTX *mem_ctx, struct tevent_req *req, int *status,
- int *timeouts, struct ares_srv_reply **reply_list)
+ int *timeouts, struct ares_srv_reply **reply_list,
+ uint32_t *ttl)
{
struct getsrv_state *state = tevent_req_data(req, struct getsrv_state);
@@ -1692,6 +1800,9 @@ resolv_getsrv_recv(TALLOC_CTX *mem_ctx, struct tevent_req *req, int *status,
*timeouts = state->timeouts;
if (reply_list)
*reply_list = talloc_steal(mem_ctx, state->reply_list);
+ if (ttl) {
+ *ttl = state->ttl;
+ }
TEVENT_REQ_RETURN_ON_ERROR(req);
diff --git a/src/resolv/async_resolv.h b/src/resolv/async_resolv.h
index 919ba370b..9b08f12ae 100644
--- a/src/resolv/async_resolv.h
+++ b/src/resolv/async_resolv.h
@@ -144,7 +144,8 @@ int resolv_getsrv_recv(TALLOC_CTX *mem_ctx,
struct tevent_req *req,
int *status,
int *timeouts,
- struct ares_srv_reply **reply_list);
+ struct ares_srv_reply **reply_list,
+ uint32_t *ttl);
/* This is an implementation of section "Usage rules" of RFC 2782 */
int
@@ -187,6 +188,7 @@ resolv_discover_srv_send(TALLOC_CTX *mem_ctx,
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);
#endif /* __ASYNC_RESOLV_H__ */
diff --git a/src/resolv/async_resolv_utils.c b/src/resolv/async_resolv_utils.c
index c5ae5f9d4..250681717 100644
--- a/src/resolv/async_resolv_utils.c
+++ b/src/resolv/async_resolv_utils.c
@@ -162,6 +162,7 @@ struct resolv_discover_srv_state {
int domain_index;
struct ares_srv_reply *reply_list;
+ uint32_t ttl;
};
static errno_t resolv_discover_srv_next_domain(struct tevent_req *req);
@@ -272,7 +273,8 @@ static void resolv_discover_srv_done(struct tevent_req *subreq)
req = tevent_req_callback_data(subreq, struct tevent_req);
state = tevent_req_data(req, struct resolv_discover_srv_state);
- ret = resolv_getsrv_recv(state, subreq, &status, NULL, &state->reply_list);
+ ret = resolv_getsrv_recv(state, subreq, &status, NULL,
+ &state->reply_list, &state->ttl);
talloc_zfree(subreq);
if (ret != EOK) {
DEBUG(SSSDBG_OP_FAILURE, "SRV query failed [%d]: %s\n",
@@ -307,6 +309,7 @@ done:
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 resolv_discover_srv_state *state = NULL;
@@ -331,5 +334,9 @@ errno_t resolv_discover_srv_recv(TALLOC_CTX *mem_ctx,
*_reply_list = talloc_steal(mem_ctx, state->reply_list);
}
+ if (*_ttl) {
+ *_ttl = state->ttl;
+ }
+
return EOK;
}
diff --git a/src/tests/cmocka/test_resolv_fake.c b/src/tests/cmocka/test_resolv_fake.c
new file mode 100644
index 000000000..6c201e702
--- /dev/null
+++ b/src/tests/cmocka/test_resolv_fake.c
@@ -0,0 +1,374 @@
+/*
+ Authors:
+ Jakub Hrozek <jhrozek@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 <resolv.h>
+
+#include "tests/cmocka/common_mock.h"
+#include "tests/cmocka/common_mock_resp.h"
+
+#define TEST_BUFSIZE 1024
+#define TEST_DEFAULT_TIMEOUT 5
+#define TEST_SRV_QUERY "_ldap._tcp.sssd.com"
+
+static TALLOC_CTX *global_mock_context = NULL;
+
+struct srv_rrdata {
+ uint16_t port;
+ uint16_t prio;
+ uint16_t weight;
+ uint32_t ttl;
+ const char *hostname;
+};
+
+static ssize_t dns_header(unsigned char **buf, size_t ancount)
+{
+ uint8_t *hb;
+ HEADER *h;
+
+ hb = *buf;
+ memset(hb, 0, NS_HFIXEDSZ);
+
+ h = (HEADER *) hb;
+ h->id = res_randomid(); /* random query ID */
+ h->qr = 1; /* response flag */
+ h->rd = 1; /* recursion desired */
+ h->ra = 1; /* resursion available */
+
+ h->qdcount = htons(1); /* no. of questions */
+ h->ancount = htons(ancount); /* no. of answers */
+ h->arcount = htons(0); /* no. of add'tl records */
+
+ hb += NS_HFIXEDSZ; /* move past the header */
+ *buf = hb;
+
+ return NS_HFIXEDSZ;
+}
+
+static ssize_t dns_question(const char *question,
+ uint16_t type,
+ uint8_t **question_ptr,
+ size_t remaining)
+{
+ unsigned char *qb = *question_ptr;
+ int n;
+
+ n = ns_name_compress(question, qb, remaining, NULL, NULL);
+ assert_true(n > 0);
+
+ qb += n;
+ remaining -= n;
+
+ NS_PUT16(type, qb);
+ NS_PUT16(ns_c_in, qb);
+
+ *question_ptr = qb;
+ return n + 2 * sizeof(uint16_t);
+}
+
+static ssize_t add_rr_common(uint16_t type,
+ uint32_t ttl,
+ size_t rdata_size,
+ const char *key,
+ size_t remaining,
+ uint8_t **rdata_ptr)
+{
+ uint8_t *rd = *rdata_ptr;
+ ssize_t written = 0;
+
+ written = ns_name_compress(key, rd, remaining, NULL, NULL);
+ assert_int_not_equal(written, -1);
+ rd += written;
+ remaining -= written;
+
+ assert_true(remaining > 3 * sizeof(uint16_t) + sizeof(uint32_t));
+ NS_PUT16(type, rd);
+ NS_PUT16(ns_c_in, rd);
+ NS_PUT32(ttl, rd);
+ NS_PUT16(rdata_size, rd);
+
+ assert_true(remaining > rdata_size);
+ *rdata_ptr = rd;
+ return written + 3 * sizeof(uint16_t) + sizeof(uint32_t) + rdata_size;
+}
+
+static ssize_t add_srv_rr(struct srv_rrdata *rr,
+ const char *question,
+ uint8_t *answer,
+ size_t anslen)
+{
+ uint8_t *a = answer;
+ ssize_t resp_size;
+ size_t rdata_size;
+ unsigned char hostname_compressed[MAXDNAME];
+ ssize_t compressed_len;
+
+ rdata_size = 3 * sizeof(uint16_t);
+
+ /* Prepare the data to write */
+ compressed_len = ns_name_compress(rr->hostname,
+ hostname_compressed, MAXDNAME,
+ NULL, NULL);
+ assert_int_not_equal(compressed_len, -1);
+ rdata_size += compressed_len;
+
+ resp_size = add_rr_common(ns_t_srv, rr->ttl, rdata_size,
+ question, anslen, &a);
+
+ NS_PUT16(rr->prio, a);
+ NS_PUT16(rr->weight, a);
+ NS_PUT16(rr->port, a);
+ memcpy(a, hostname_compressed, compressed_len);
+
+ return resp_size;
+}
+
+unsigned char *create_srv_buffer(TALLOC_CTX *mem_ctx,
+ const char *question,
+ struct srv_rrdata *rrs,
+ size_t n_rrs,
+ size_t *_buflen)
+{
+ unsigned char *buf;
+ unsigned char *buf_head;
+ ssize_t len;
+ ssize_t i;
+ ssize_t total = 0;
+
+ buf = talloc_zero_array(mem_ctx, unsigned char, TEST_BUFSIZE);
+ assert_non_null(buf);
+ buf_head = buf;
+
+ len = dns_header(&buf, n_rrs);
+ assert_true(len > 0);
+ total += len;
+
+ len = dns_question(question, ns_t_srv, &buf, TEST_BUFSIZE - total);
+ assert_true(len > 0);
+ total += len;
+
+ /* answer */
+ for (i = 0; i < n_rrs; i++) {
+ len = add_srv_rr(&rrs[i], question, buf, TEST_BUFSIZE - total);
+ assert_true(len > 0);
+ total += len;
+ buf += len;
+ }
+
+ *_buflen = total;
+ return buf_head;
+}
+
+struct fake_ares_query {
+ int status;
+ int timeouts;
+ unsigned char *abuf;
+ int alen;
+};
+
+void mock_ares_query(int status, int timeouts, unsigned char *abuf, int alen)
+{
+ will_return(__wrap_ares_query, status);
+ will_return(__wrap_ares_query, timeouts);
+ will_return(__wrap_ares_query, abuf);
+ will_return(__wrap_ares_query, alen);
+}
+
+void __wrap_ares_query(ares_channel channel, const char *name, int dnsclass,
+ int type, ares_callback callback, void *arg)
+{
+ struct fake_ares_query query;
+
+ query.status = sss_mock_type(int);
+ query.timeouts = sss_mock_type(int);
+ query.abuf = sss_mock_ptr_type(unsigned char *);
+ query.alen = sss_mock_type(int);
+
+ callback(arg, query.status, query.timeouts, query.abuf, query.alen);
+}
+
+/* The unit test */
+struct resolv_fake_ctx {
+ struct resolv_ctx *resolv;
+ struct sss_test_ctx *ctx;
+};
+
+void test_resolv_fake_setup(void **state)
+{
+ struct resolv_fake_ctx *test_ctx;
+ int ret;
+
+ 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 resolv_fake_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_DEFAULT_TIMEOUT, &test_ctx->resolv);
+ assert_int_equal(ret, EOK);
+
+ *state = test_ctx;
+}
+
+void test_resolv_fake_teardown(void **state)
+{
+ struct resolv_fake_ctx *test_ctx =
+ talloc_get_type(*state, struct resolv_fake_ctx);
+
+ talloc_free(test_ctx);
+ talloc_free(global_mock_context);
+ assert_true(leak_check_teardown());
+}
+
+void test_resolv_fake_srv_done(struct tevent_req *req)
+{
+ errno_t ret;
+ TALLOC_CTX *tmp_ctx;
+ int status;
+ uint32_t ttl;
+ struct ares_srv_reply *srv_replies = NULL;
+ struct resolv_fake_ctx *test_ctx =
+ tevent_req_callback_data(req, struct resolv_fake_ctx);
+
+ tmp_ctx = talloc_new(test_ctx);
+ assert_non_null(tmp_ctx);
+
+ ret = resolv_getsrv_recv(tmp_ctx, req, &status, NULL,
+ &srv_replies, &ttl);
+ assert_int_equal(ret, EOK);
+
+ assert_non_null(srv_replies);
+ assert_int_equal(srv_replies->priority, 1);
+ assert_int_equal(srv_replies->weight, 40);
+ assert_int_equal(srv_replies->port, 389);
+ assert_string_equal(srv_replies->host, "ldap.sssd.com");
+
+ srv_replies = srv_replies->next;
+ assert_non_null(srv_replies);
+ assert_int_equal(srv_replies->priority, 1);
+ assert_int_equal(srv_replies->weight, 60);
+ assert_int_equal(srv_replies->port, 389);
+ assert_string_equal(srv_replies->host, "ldap2.sssd.com");
+
+ srv_replies = srv_replies->next;
+ assert_null(srv_replies);
+
+ assert_int_equal(ttl, 500);
+
+ talloc_free(tmp_ctx);
+ test_ctx->ctx->error = EOK;
+ test_ctx->ctx->done = true;
+}
+
+void test_resolv_fake_srv(void **state)
+{
+ int ret;
+ struct tevent_req *req;
+ struct resolv_fake_ctx *test_ctx =
+ talloc_get_type(*state, struct resolv_fake_ctx);
+
+ unsigned char *buf;
+ size_t buflen;
+
+ struct srv_rrdata rr[2];
+
+ rr[0].prio = 1;
+ rr[0].port = 389;
+ rr[0].weight = 40;
+ rr[0].ttl = 600;
+ rr[0].hostname = "ldap.sssd.com";
+
+ rr[1].prio = 1;
+ rr[1].port = 389;
+ rr[1].weight = 60;
+ rr[1].ttl = 500;
+ rr[1].hostname = "ldap2.sssd.com";
+
+ buf = create_srv_buffer(test_ctx, TEST_SRV_QUERY, rr, 2, &buflen);
+ assert_non_null(buf);
+ mock_ares_query(0, 0, buf, buflen);
+
+ req = resolv_getsrv_send(test_ctx, test_ctx->ctx->ev,
+ test_ctx->resolv, TEST_SRV_QUERY);
+ assert_non_null(req);
+ tevent_req_set_callback(req, test_resolv_fake_srv_done, test_ctx);
+
+ ret = test_ev_loop(test_ctx->ctx);
+ assert_int_equal(ret, ERR_OK);
+}
+
+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_resolv_fake_srv,
+ test_resolv_fake_setup,
+ test_resolv_fake_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;
+}
diff --git a/src/tests/resolv-tests.c b/src/tests/resolv-tests.c
index 30d551f63..6c6783b62 100644
--- a/src/tests/resolv-tests.c
+++ b/src/tests/resolv-tests.c
@@ -501,7 +501,7 @@ static void test_internet(struct tevent_req *req)
break;
case TESTING_SRV:
recv_status = resolv_getsrv_recv(tmp_ctx, req, &status, NULL,
- &srv_replies);
+ &srv_replies, NULL);
test_ctx->error = (srv_replies == NULL) ? ENOENT : EOK;
for (srvptr = srv_replies; srvptr != NULL; srvptr = srvptr->next) {
DEBUG(SSSDBG_OP_FAILURE,