test_dyndns.c revision 12e7e87ccbae0d5c2f338cd019ca51556cbcd3ae
2ca39b9545cad63df411619615736d9070ad7932Christian Maeder/*
2ca39b9545cad63df411619615736d9070ad7932Christian Maeder Authors:
e9458b1a7a19a63aa4c179f9ab20f4d50681c168Jens Elkner Jakub Hrozek <jhrozek@redhat.com>
2ca39b9545cad63df411619615736d9070ad7932Christian Maeder
2ca39b9545cad63df411619615736d9070ad7932Christian Maeder Copyright (C) 2013 Red Hat
2ca39b9545cad63df411619615736d9070ad7932Christian Maeder
2ca39b9545cad63df411619615736d9070ad7932Christian Maeder SSSD tests: Dynamic DNS tests
2ca39b9545cad63df411619615736d9070ad7932Christian Maeder
2ca39b9545cad63df411619615736d9070ad7932Christian Maeder This program is free software; you can redistribute it and/or modify
2ca39b9545cad63df411619615736d9070ad7932Christian Maeder it under the terms of the GNU General Public License as published by
2ca39b9545cad63df411619615736d9070ad7932Christian Maeder the Free Software Foundation; either version 3 of the License, or
2ca39b9545cad63df411619615736d9070ad7932Christian Maeder (at your option) any later version.
2ca39b9545cad63df411619615736d9070ad7932Christian Maeder
2ca39b9545cad63df411619615736d9070ad7932Christian Maeder This program is distributed in the hope that it will be useful,
6f70475dddc12732bdbef3e3dd116373e34cd6b9Christian Maeder but WITHOUT ANY WARRANTY; without even the implied warranty of
2ca39b9545cad63df411619615736d9070ad7932Christian Maeder MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2ca39b9545cad63df411619615736d9070ad7932Christian Maeder GNU General Public License for more details.
2ca39b9545cad63df411619615736d9070ad7932Christian Maeder
2ca39b9545cad63df411619615736d9070ad7932Christian Maeder You should have received a copy of the GNU General Public License
2ca39b9545cad63df411619615736d9070ad7932Christian Maeder along with this program. If not, see <http://www.gnu.org/licenses/>.
2ca39b9545cad63df411619615736d9070ad7932Christian Maeder*/
2ca39b9545cad63df411619615736d9070ad7932Christian Maeder
2ca39b9545cad63df411619615736d9070ad7932Christian Maeder#include <talloc.h>
2ca39b9545cad63df411619615736d9070ad7932Christian Maeder#include <tevent.h>
2ca39b9545cad63df411619615736d9070ad7932Christian Maeder#include <errno.h>
2ca39b9545cad63df411619615736d9070ad7932Christian Maeder#include <popt.h>
2ca39b9545cad63df411619615736d9070ad7932Christian Maeder#include <unistd.h>
2ca39b9545cad63df411619615736d9070ad7932Christian Maeder#include <sys/types.h>
2ca39b9545cad63df411619615736d9070ad7932Christian Maeder#include <ifaddrs.h>
b036d1e059b08fc4be7cf9f87703849723cf41eccmaeder#include <arpa/inet.h>
2ca39b9545cad63df411619615736d9070ad7932Christian Maeder
2ca39b9545cad63df411619615736d9070ad7932Christian Maeder/* In order to access opaque types */
2ca39b9545cad63df411619615736d9070ad7932Christian Maeder#include "providers/dp_dyndns.c"
2ca39b9545cad63df411619615736d9070ad7932Christian Maeder
2ca39b9545cad63df411619615736d9070ad7932Christian Maeder#include "tests/cmocka/common_mock.h"
2ca39b9545cad63df411619615736d9070ad7932Christian Maeder#include "src/providers/dp_dyndns.h"
2ca39b9545cad63df411619615736d9070ad7932Christian Maeder
2ca39b9545cad63df411619615736d9070ad7932Christian Maeder#define TESTS_PATH "tests_dyndns"
2ca39b9545cad63df411619615736d9070ad7932Christian Maeder#define TEST_CONF_DB "test_dyndns_conf.ldb"
2ca39b9545cad63df411619615736d9070ad7932Christian Maeder#define TEST_DOM_NAME "dyndns_test"
2ca39b9545cad63df411619615736d9070ad7932Christian Maeder#define TEST_SYSDB_FILE "cache_"TEST_DOM_NAME".ldb"
d2db653e792249571fa437303fd5c1b152f4db9ccmaeder#define TEST_ID_PROVIDER "ldap"
2ca39b9545cad63df411619615736d9070ad7932Christian Maeder
2ca39b9545cad63df411619615736d9070ad7932Christian Maederenum mock_nsupdate_states {
2ca39b9545cad63df411619615736d9070ad7932Christian Maeder MOCK_NSUPDATE_OK,
aeabd99d28fef3b392ca208c5ac84f7892af0ddbnotanartist MOCK_NSUPDATE_ERR,
6f70475dddc12732bdbef3e3dd116373e34cd6b9Christian Maeder MOCK_NSUPDATE_TIMEOUT,
2ca39b9545cad63df411619615736d9070ad7932Christian Maeder};
6f70475dddc12732bdbef3e3dd116373e34cd6b9Christian Maeder
6f70475dddc12732bdbef3e3dd116373e34cd6b9Christian Maederstatic TALLOC_CTX *global_mock_context = NULL;
6f70475dddc12732bdbef3e3dd116373e34cd6b9Christian Maeder
6f70475dddc12732bdbef3e3dd116373e34cd6b9Christian Maederstruct dyndns_test_ctx {
254f324c749726c14a3b405d148d777007557626notanartist struct sss_test_ctx *tctx;
254f324c749726c14a3b405d148d777007557626notanartist
254f324c749726c14a3b405d148d777007557626notanartist struct be_ctx *be_ctx;
254f324c749726c14a3b405d148d777007557626notanartist struct be_nsupdate_ctx *update_ctx;
254f324c749726c14a3b405d148d777007557626notanartist
b036d1e059b08fc4be7cf9f87703849723cf41eccmaeder enum mock_nsupdate_states state;
254f324c749726c14a3b405d148d777007557626notanartist int child_status;
254f324c749726c14a3b405d148d777007557626notanartist int child_retval;
6f70475dddc12732bdbef3e3dd116373e34cd6b9Christian Maeder};
6f70475dddc12732bdbef3e3dd116373e34cd6b9Christian Maeder
6f70475dddc12732bdbef3e3dd116373e34cd6b9Christian Maederstatic struct dyndns_test_ctx *dyndns_test_ctx;
aeabd99d28fef3b392ca208c5ac84f7892af0ddbnotanartist
aeabd99d28fef3b392ca208c5ac84f7892af0ddbnotanartistvoid __wrap_execv(const char *path, char *const argv[])
aeabd99d28fef3b392ca208c5ac84f7892af0ddbnotanartist{
6f70475dddc12732bdbef3e3dd116373e34cd6b9Christian Maeder int err;
6f70475dddc12732bdbef3e3dd116373e34cd6b9Christian Maeder
6f70475dddc12732bdbef3e3dd116373e34cd6b9Christian Maeder switch (dyndns_test_ctx->state) {
6f70475dddc12732bdbef3e3dd116373e34cd6b9Christian Maeder case MOCK_NSUPDATE_OK:
aeabd99d28fef3b392ca208c5ac84f7892af0ddbnotanartist DEBUG(SSSDBG_FUNC_DATA, "nsupdate success test case\n");
aeabd99d28fef3b392ca208c5ac84f7892af0ddbnotanartist err = 0;
6f70475dddc12732bdbef3e3dd116373e34cd6b9Christian Maeder break;
6f70475dddc12732bdbef3e3dd116373e34cd6b9Christian Maeder case MOCK_NSUPDATE_ERR:
6f70475dddc12732bdbef3e3dd116373e34cd6b9Christian Maeder DEBUG(SSSDBG_FUNC_DATA, "nsupdate error test case\n");
6f70475dddc12732bdbef3e3dd116373e34cd6b9Christian Maeder err = 1;
6f70475dddc12732bdbef3e3dd116373e34cd6b9Christian Maeder break;
6f70475dddc12732bdbef3e3dd116373e34cd6b9Christian Maeder case MOCK_NSUPDATE_TIMEOUT:
6f70475dddc12732bdbef3e3dd116373e34cd6b9Christian Maeder DEBUG(SSSDBG_FUNC_DATA, "nsupdate timeout test case\n");
6f70475dddc12732bdbef3e3dd116373e34cd6b9Christian Maeder err = 2;
6f70475dddc12732bdbef3e3dd116373e34cd6b9Christian Maeder sleep(3);
6f70475dddc12732bdbef3e3dd116373e34cd6b9Christian Maeder break;
6f70475dddc12732bdbef3e3dd116373e34cd6b9Christian Maeder default:
6f70475dddc12732bdbef3e3dd116373e34cd6b9Christian Maeder DEBUG(SSSDBG_CRIT_FAILURE, "unknown test case\n");
6f70475dddc12732bdbef3e3dd116373e34cd6b9Christian Maeder err = 255;
aeabd99d28fef3b392ca208c5ac84f7892af0ddbnotanartist break;
be110dccc9f7bd9e987b35943b16ccb22922248fChristian Maeder }
be110dccc9f7bd9e987b35943b16ccb22922248fChristian Maeder
be110dccc9f7bd9e987b35943b16ccb22922248fChristian Maeder DEBUG(SSSDBG_TRACE_LIBS, "Child exiting with status %d\n", err);
be110dccc9f7bd9e987b35943b16ccb22922248fChristian Maeder _exit(err);
aeabd99d28fef3b392ca208c5ac84f7892af0ddbnotanartist}
be110dccc9f7bd9e987b35943b16ccb22922248fChristian Maeder
be110dccc9f7bd9e987b35943b16ccb22922248fChristian Maederint __wrap_getifaddrs(struct ifaddrs **_ifap)
d2db653e792249571fa437303fd5c1b152f4db9ccmaeder{
d2db653e792249571fa437303fd5c1b152f4db9ccmaeder struct ifaddrs *ifap = NULL;
6f70475dddc12732bdbef3e3dd116373e34cd6b9Christian Maeder struct ifaddrs *ifap_prev = NULL;
2ca39b9545cad63df411619615736d9070ad7932Christian Maeder struct ifaddrs *ifap_head = NULL;
2ca39b9545cad63df411619615736d9070ad7932Christian Maeder char *name;
2ca39b9545cad63df411619615736d9070ad7932Christian Maeder char *straddr;
2ca39b9545cad63df411619615736d9070ad7932Christian Maeder struct sockaddr_in *sa;
2ca39b9545cad63df411619615736d9070ad7932Christian Maeder
2ca39b9545cad63df411619615736d9070ad7932Christian Maeder while ((name = sss_mock_ptr_type(char *)) != NULL) {
6f70475dddc12732bdbef3e3dd116373e34cd6b9Christian Maeder straddr = sss_mock_ptr_type(char *);
2ca39b9545cad63df411619615736d9070ad7932Christian Maeder if (straddr == NULL) {
6f70475dddc12732bdbef3e3dd116373e34cd6b9Christian Maeder errno = EINVAL;
d2db653e792249571fa437303fd5c1b152f4db9ccmaeder goto fail;
2ca39b9545cad63df411619615736d9070ad7932Christian Maeder }
8762d0e3d492aba4d1621fb0de685f0be1372864notanartist
2ca39b9545cad63df411619615736d9070ad7932Christian Maeder ifap = talloc_zero(global_mock_context, struct ifaddrs);
2ca39b9545cad63df411619615736d9070ad7932Christian Maeder if (ifap == NULL) {
2ca39b9545cad63df411619615736d9070ad7932Christian Maeder errno = ENOMEM; /* getifaddrs sets errno, too */
080fd6c2323c2d92d2c909af35dd8f8bda053b98cmaeder goto fail;
2ca39b9545cad63df411619615736d9070ad7932Christian Maeder }
7af6ad49991a7f73b5d4233c89648a5a523f72bdTill Mossakowski
2ca39b9545cad63df411619615736d9070ad7932Christian Maeder if (ifap_prev) {
dc62afbf79603699b39b2387f48298634f642e67cmaeder ifap_prev->ifa_next = ifap;
dc62afbf79603699b39b2387f48298634f642e67cmaeder } else {
2ca39b9545cad63df411619615736d9070ad7932Christian Maeder ifap_head = ifap;
2ca39b9545cad63df411619615736d9070ad7932Christian Maeder }
ifap_prev = ifap;
ifap->ifa_name = talloc_strdup(ifap, name);
if (ifap->ifa_name == NULL) {
errno = ENOMEM;
goto fail;
}
/* Do not alocate directly on ifap->ifa_addr to
* avoid alignment warnings */
sa = talloc(ifap, struct sockaddr_in);
if (sa == NULL) {
errno = ENOMEM;
goto fail;
}
sa->sin_family = AF_INET;
/* convert straddr into ifa_addr */
if (inet_pton(AF_INET, straddr, &sa->sin_addr) != 1) {
goto fail;
}
ifap->ifa_addr = (struct sockaddr *) sa;
}
*_ifap = ifap_head;
return 0;
fail:
talloc_free(ifap);
return -1;
}
void __wrap_freeifaddrs(struct ifaddrs *ifap)
{
talloc_free(ifap);
}
static void dyndns_test_done(struct tevent_req *req)
{
struct dyndns_test_ctx *ctx =
tevent_req_callback_data(req, struct dyndns_test_ctx);
ctx->child_retval = -1;
ctx->tctx->error = be_nsupdate_recv(req, &ctx->child_status);
talloc_zfree(req);
ctx->tctx->done = true;
}
void will_return_getifaddrs(const char *ifname, const char *straddr)
{
will_return(__wrap_getifaddrs, ifname);
if (ifname) {
will_return(__wrap_getifaddrs, straddr);
}
}
void dyndns_test_get_ifaddr(void **state)
{
errno_t ret;
struct sss_iface_addr *addrlist;
char straddr[128];
check_leaks_push(dyndns_test_ctx);
will_return_getifaddrs("eth0", "192.168.0.1");
will_return_getifaddrs("eth1", "192.168.0.2");
will_return_getifaddrs(NULL, NULL); /* sentinel */
ret = sss_iface_addr_list_get(dyndns_test_ctx, "eth0", &addrlist);
assert_int_equal(ret, EOK);
/* There must be only one address with the correct value */
assert_non_null(addrlist);
assert_non_null(addrlist->addr);
assert_null(addrlist->next);
assert_null(addrlist->prev);
assert_non_null(inet_ntop(AF_INET,
&((struct sockaddr_in *) addrlist->addr)->sin_addr,
straddr, INET_ADDRSTRLEN));
assert_string_equal(straddr, "192.168.0.1");
talloc_free(addrlist);
assert_true(check_leaks_pop(dyndns_test_ctx) == true);
}
void dyndns_test_ok(void **state)
{
struct tevent_req *req;
errno_t ret;
TALLOC_CTX *tmp_ctx;
tmp_ctx = talloc_new(global_talloc_context);
assert_non_null(tmp_ctx);
check_leaks_push(tmp_ctx);
dyndns_test_ctx->state = MOCK_NSUPDATE_OK;
req = be_nsupdate_send(tmp_ctx, dyndns_test_ctx->tctx->ev,
BE_NSUPDATE_AUTH_GSS_TSIG,
discard_const("test message"), false);
assert_non_null(req);
tevent_req_set_callback(req, dyndns_test_done, dyndns_test_ctx);
/* Wait until the test finishes with EOK */
ret = test_ev_loop(dyndns_test_ctx->tctx);
DEBUG(SSSDBG_TRACE_LIBS,
"Child request returned [%d]: %s\n", ret, strerror(ret));
assert_int_equal(ret, EOK);
assert_true(WIFEXITED(dyndns_test_ctx->child_status));
assert_int_equal(WEXITSTATUS(dyndns_test_ctx->child_status), 0);
assert_true(check_leaks_pop(tmp_ctx) == true);
talloc_free(tmp_ctx);
}
void dyndns_test_error(void **state)
{
struct tevent_req *req;
errno_t ret;
TALLOC_CTX *tmp_ctx;
tmp_ctx = talloc_new(global_talloc_context);
assert_non_null(tmp_ctx);
check_leaks_push(tmp_ctx);
dyndns_test_ctx->state = MOCK_NSUPDATE_ERR;
req = be_nsupdate_send(tmp_ctx, dyndns_test_ctx->tctx->ev,
BE_NSUPDATE_AUTH_GSS_TSIG,
discard_const("test message"), false);
assert_non_null(req);
tevent_req_set_callback(req, dyndns_test_done, dyndns_test_ctx);
/* Wait until the test finishes with EIO (child error) */
ret = test_ev_loop(dyndns_test_ctx->tctx);
DEBUG(SSSDBG_TRACE_LIBS,
"Child request returned [%d]: %s\n", ret, strerror(ret));
assert_int_equal(ret, ERR_DYNDNS_FAILED);
assert_true(WIFEXITED(dyndns_test_ctx->child_status));
assert_int_equal(WEXITSTATUS(dyndns_test_ctx->child_status), 1);
assert_true(check_leaks_pop(tmp_ctx) == true);
talloc_free(tmp_ctx);
}
void dyndns_test_timeout(void **state)
{
struct tevent_req *req;
errno_t ret;
TALLOC_CTX *tmp_ctx;
tmp_ctx = talloc_new(global_talloc_context);
assert_non_null(tmp_ctx);
check_leaks_push(tmp_ctx);
dyndns_test_ctx->state = MOCK_NSUPDATE_TIMEOUT;
req = be_nsupdate_send(tmp_ctx, dyndns_test_ctx->tctx->ev,
BE_NSUPDATE_AUTH_GSS_TSIG,
discard_const("test message"), false);
assert_non_null(req);
tevent_req_set_callback(req, dyndns_test_done, dyndns_test_ctx);
/* Wait until the test finishes with EIO (child error) */
ret = test_ev_loop(dyndns_test_ctx->tctx);
/* The event queue may not be empty. We need to make sure that all events
* are processed. Unfortunately, tevent_loop_wait() contains a bug that
* prevents exiting the loop even if there are no remaining events, thus
* we have to use tevent_loop_once().
*
* FIXME: use tevent_loop_wait() when the bug is fixed
* https://bugzilla.samba.org/show_bug.cgi?id=10012
*/
tevent_loop_once(dyndns_test_ctx->tctx->ev); /* SIGCHLD handler */
tevent_loop_once(dyndns_test_ctx->tctx->ev); /* nsupdate_child_handler */
DEBUG(SSSDBG_TRACE_LIBS,
"Child request returned [%d]: %s\n", ret, strerror(ret));
assert_int_equal(ret, ERR_DYNDNS_TIMEOUT);
assert_true(check_leaks_pop(tmp_ctx) == true);
talloc_free(tmp_ctx);
}
void dyndns_test_timer(void *pvt)
{
struct dyndns_test_ctx *ctx = talloc_get_type(pvt, struct dyndns_test_ctx);
static int ncalls = 0;
ncalls++;
if (ncalls == 1) {
be_nsupdate_timer_schedule(ctx->tctx->ev, ctx->update_ctx);
} else if (ncalls == 2) {
ctx->tctx->done = true;
}
ctx->tctx->error = ERR_OK;
}
void dyndns_test_interval(void **state)
{
errno_t ret;
TALLOC_CTX *tmp_ctx;
tmp_ctx = talloc_new(global_talloc_context);
assert_non_null(tmp_ctx);
check_leaks_push(tmp_ctx);
ret = be_nsupdate_init(tmp_ctx, dyndns_test_ctx->be_ctx, NULL,
&dyndns_test_ctx->update_ctx);
assert_int_equal(ret, EOK);
ret = be_nsupdate_init_timer(dyndns_test_ctx->update_ctx,
dyndns_test_ctx->be_ctx->ev,
dyndns_test_timer, dyndns_test_ctx);
assert_int_equal(ret, EOK);
/* Wait until the timer hits */
ret = test_ev_loop(dyndns_test_ctx->tctx);
DEBUG(SSSDBG_TRACE_LIBS,
"Child request returned [%d]: %s\n", ret, strerror(ret));
assert_int_equal(ret, ERR_OK);
talloc_free(dyndns_test_ctx->update_ctx);
assert_true(check_leaks_pop(tmp_ctx) == true);
talloc_free(tmp_ctx);
}
/* Testsuite setup and teardown */
void dyndns_test_setup(void **state)
{
struct sss_test_conf_param params[] = {
{ "dyndns_update", "true" },
{ "dyndns_refresh_interval", "2" },
{ NULL, NULL }, /* Sentinel */
};
assert_true(leak_check_setup());
global_mock_context = talloc_new(global_talloc_context);
assert_non_null(global_mock_context);
dyndns_test_ctx = talloc_zero(global_talloc_context, struct dyndns_test_ctx);
assert_non_null(dyndns_test_ctx);
dyndns_test_ctx->tctx = create_dom_test_ctx(dyndns_test_ctx, TESTS_PATH,
TEST_CONF_DB, TEST_DOM_NAME,
TEST_ID_PROVIDER, params);
assert_non_null(dyndns_test_ctx->tctx);
dyndns_test_ctx->be_ctx = talloc_zero(dyndns_test_ctx, struct be_ctx);
assert_non_null(dyndns_test_ctx->be_ctx);
dyndns_test_ctx->be_ctx->cdb = dyndns_test_ctx->tctx->confdb;
dyndns_test_ctx->be_ctx->ev = dyndns_test_ctx->tctx->ev;
dyndns_test_ctx->be_ctx->conf_path = dyndns_test_ctx->tctx->conf_dom_path;
}
void dyndns_test_simple_setup(void **state)
{
assert_true(leak_check_setup());
global_mock_context = talloc_new(global_talloc_context);
assert_non_null(global_mock_context);
dyndns_test_ctx = talloc_zero(global_talloc_context, struct dyndns_test_ctx);
assert_non_null(dyndns_test_ctx);
}
void dyndns_test_teardown(void **state)
{
talloc_free(dyndns_test_ctx);
talloc_free(global_mock_context);
assert_true(leak_check_teardown());
}
int main(int argc, const char *argv[])
{
int rv;
int no_cleanup = 0;
poptContext pc;
int opt;
struct poptOption long_options[] = {
POPT_AUTOHELP
SSSD_DEBUG_OPTS
{"no-cleanup", 'n', POPT_ARG_NONE, &no_cleanup, 0,
_("Do not delete the test database after a test run"), NULL },
POPT_TABLEEND
};
const UnitTest tests[] = {
/* Utility functions unit test */
unit_test_setup_teardown(dyndns_test_get_ifaddr,
dyndns_test_simple_setup,
dyndns_test_teardown),
/* Dynamic DNS update unit tests*/
unit_test_setup_teardown(dyndns_test_ok,
dyndns_test_setup, dyndns_test_teardown),
unit_test_setup_teardown(dyndns_test_error,
dyndns_test_setup, dyndns_test_teardown),
unit_test_setup_teardown(dyndns_test_timeout,
dyndns_test_setup, dyndns_test_teardown),
unit_test_setup_teardown(dyndns_test_interval,
dyndns_test_setup, dyndns_test_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();
test_dom_suite_cleanup(TESTS_PATH, TEST_CONF_DB, TEST_SYSDB_FILE);
test_dom_suite_setup(TESTS_PATH);
rv = run_tests(tests);
if (rv == 0 && !no_cleanup) {
test_dom_suite_cleanup(TESTS_PATH, TEST_CONF_DB, TEST_SYSDB_FILE);
}
return rv;
}