summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/config/SSSDConfig/__init__.py.in1
-rwxr-xr-xsrc/config/SSSDConfigTest.py2
-rw-r--r--src/config/etc/sssd.api.conf1
-rw-r--r--src/man/sssd-ipa.5.xml16
-rw-r--r--src/providers/dp_dyndns.c53
-rw-r--r--src/providers/dp_dyndns.h17
-rw-r--r--src/providers/ipa/ipa_common.c4
-rw-r--r--src/providers/ipa/ipa_dyndns.c107
-rw-r--r--src/providers/ipa/ipa_dyndns.h3
-rw-r--r--src/providers/ipa/ipa_opts.h1
-rw-r--r--src/tests/cmocka/test_dyndns.c85
11 files changed, 283 insertions, 7 deletions
diff --git a/src/config/SSSDConfig/__init__.py.in b/src/config/SSSDConfig/__init__.py.in
index 9f11d31c..003153d0 100644
--- a/src/config/SSSDConfig/__init__.py.in
+++ b/src/config/SSSDConfig/__init__.py.in
@@ -128,6 +128,7 @@ option_strings = {
'dyndns_update' : _("Whether to automatically update the client's DNS entry"),
'dyndns_ttl' : _("The TTL to apply to the client's DNS entry after updating it"),
'dyndns_iface' : _("The interface whose IP should be used for dynamic DNS updates"),
+ 'dyndns_refresh_interval' : _("How often to periodically update the client's DNS entry"),
# [provider/ipa]
'ipa_domain' : _('IPA domain'),
diff --git a/src/config/SSSDConfigTest.py b/src/config/SSSDConfigTest.py
index 2b4df874..18328d06 100755
--- a/src/config/SSSDConfigTest.py
+++ b/src/config/SSSDConfigTest.py
@@ -510,6 +510,7 @@ class SSSDConfigTestSSSDDomain(unittest.TestCase):
'dyndns_update',
'dyndns_ttl',
'dyndns_iface',
+ 'dyndns_refresh_interval',
'override_gid',
'case_sensitive',
'override_homedir',
@@ -856,6 +857,7 @@ class SSSDConfigTestSSSDDomain(unittest.TestCase):
'dyndns_update',
'dyndns_ttl',
'dyndns_iface',
+ 'dyndns_refresh_interval',
'override_gid',
'case_sensitive',
'override_homedir',
diff --git a/src/config/etc/sssd.api.conf b/src/config/etc/sssd.api.conf
index 02e2a0a6..b09cbd18 100644
--- a/src/config/etc/sssd.api.conf
+++ b/src/config/etc/sssd.api.conf
@@ -125,6 +125,7 @@ entry_cache_sudo_timeout = int, None, false
dyndns_update = bool, None, false
dyndns_ttl = int, None, false
dyndns_iface = str, None, false
+dyndns_refresh_interval = int, None, false
# Special providers
[provider/permit]
diff --git a/src/man/sssd-ipa.5.xml b/src/man/sssd-ipa.5.xml
index bedb1107..426e9a5b 100644
--- a/src/man/sssd-ipa.5.xml
+++ b/src/man/sssd-ipa.5.xml
@@ -204,6 +204,22 @@
</varlistentry>
<varlistentry>
+ <term>dyndns_refresh_interval (integer)</term>
+ <listitem>
+ <para>
+ How often should the back end perform periodic DNS update in
+ addition to the automatic update performed when the back end
+ goes online.
+ This option is optional and applicable only when dyndns_update
+ is true.
+ </para>
+ <para>
+ Default: 0 (disabled)
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
<term>ipa_hbac_search_base (string)</term>
<listitem>
<para>
diff --git a/src/providers/dp_dyndns.c b/src/providers/dp_dyndns.c
index 81228d5c..337817fc 100644
--- a/src/providers/dp_dyndns.c
+++ b/src/providers/dp_dyndns.c
@@ -879,6 +879,45 @@ be_nsupdate_recv(struct tevent_req *req, int *child_status)
return EOK;
}
+static void be_nsupdate_timer(struct tevent_context *ev,
+ struct tevent_timer *te,
+ struct timeval current_time,
+ void *pvt)
+{
+ struct be_nsupdate_ctx *ctx = talloc_get_type(pvt, struct be_nsupdate_ctx);
+
+ talloc_zfree(ctx->refresh_timer);
+ ctx->timer_callback(ctx->timer_pvt);
+
+ /* timer_callback is responsible for calling be_nsupdate_timer_schedule
+ * again */
+}
+
+void be_nsupdate_timer_schedule(struct tevent_context *ev,
+ struct be_nsupdate_ctx *ctx)
+{
+ int refresh;
+ struct timeval tv;
+
+ if (ctx->refresh_timer) {
+ DEBUG(SSSDBG_FUNC_DATA, ("Timer already scheduled\n"));
+ return;
+ }
+
+ refresh = dp_opt_get_int(ctx->opts, DP_OPT_DYNDNS_REFRESH_INTERVAL);
+ if (refresh == 0) return;
+ DEBUG(SSSDBG_FUNC_DATA, ("Scheduling timer in %d seconds\n", refresh));
+
+ tv = tevent_timeval_current_ofs(refresh, 0);
+ ctx->refresh_timer = tevent_add_timer(ev, ctx, tv,
+ be_nsupdate_timer, ctx);
+
+ if (!ctx->refresh_timer) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ ("Failed to add dyndns refresh timer event\n"));
+ }
+}
+
errno_t
be_nsupdate_check(void)
{
@@ -906,6 +945,7 @@ be_nsupdate_check(void)
static struct dp_option default_dyndns_opts[] = {
{ "dyndns_update", DP_OPT_BOOL, BOOL_FALSE, BOOL_FALSE },
+ { "dyndns_refresh_interval", DP_OPT_NUMBER, NULL_NUMBER, NULL_NUMBER },
{ "dyndns_iface", DP_OPT_STRING, NULL_STRING, NULL_STRING },
{ "dyndns_ttl", DP_OPT_NUMBER, { .number = 1200 }, NULL_NUMBER },
@@ -914,13 +954,16 @@ static struct dp_option default_dyndns_opts[] = {
errno_t
be_nsupdate_init(TALLOC_CTX *mem_ctx, struct be_ctx *be_ctx,
- struct dp_option *defopts, struct be_nsupdate_ctx **_ctx)
+ struct dp_option *defopts,
+ nsupdate_timer_fn_t timer_callback,
+ void *timer_pvt,
+ struct be_nsupdate_ctx **_ctx)
{
errno_t ret;
struct dp_option *src_opts;
struct be_nsupdate_ctx *ctx;
- ctx = talloc(mem_ctx, struct be_nsupdate_ctx);
+ ctx = talloc_zero(mem_ctx, struct be_nsupdate_ctx);
if (ctx == NULL) return ENOMEM;
src_opts = defopts ? defopts : default_dyndns_opts;
@@ -932,6 +975,10 @@ be_nsupdate_init(TALLOC_CTX *mem_ctx, struct be_ctx *be_ctx,
return ret;
}
+ ctx->timer_callback = timer_callback;
+ ctx->timer_pvt = timer_pvt;
+ be_nsupdate_timer_schedule(be_ctx->ev, ctx);
+
*_ctx = ctx;
- return EOK;
+ return ERR_OK;
}
diff --git a/src/providers/dp_dyndns.h b/src/providers/dp_dyndns.h
index 95403a3a..e49ab8f0 100644
--- a/src/providers/dp_dyndns.h
+++ b/src/providers/dp_dyndns.h
@@ -29,12 +29,21 @@
/* dynamic dns helpers */
struct sss_iface_addr;
+typedef void (*nsupdate_timer_fn_t)(void *pvt);
+
struct be_nsupdate_ctx {
struct dp_option *opts;
+
+ time_t last_refresh;
+ bool timer_in_progress;
+ struct tevent_timer *refresh_timer;
+ nsupdate_timer_fn_t timer_callback;
+ void *timer_pvt;
};
enum dp_dyndns_opts {
DP_OPT_DYNDNS_UPDATE,
+ DP_OPT_DYNDNS_REFRESH_INTERVAL,
DP_OPT_DYNDNS_IFACE,
DP_OPT_DYNDNS_TTL,
@@ -48,7 +57,13 @@ errno_t be_nsupdate_check(void);
errno_t
be_nsupdate_init(TALLOC_CTX *mem_ctx, struct be_ctx *be_ctx,
- struct dp_option *defopts, struct be_nsupdate_ctx **ctx);
+ struct dp_option *defopts,
+ nsupdate_timer_fn_t timer_callback,
+ void *timer_pvt,
+ struct be_nsupdate_ctx **_ctx);
+
+void be_nsupdate_timer_schedule(struct tevent_context *ev,
+ struct be_nsupdate_ctx *ctx);
errno_t
sss_iface_addr_list_get(TALLOC_CTX *mem_ctx, const char *ifname,
diff --git a/src/providers/ipa/ipa_common.c b/src/providers/ipa/ipa_common.c
index 51750b2a..baece274 100644
--- a/src/providers/ipa/ipa_common.c
+++ b/src/providers/ipa/ipa_common.c
@@ -28,6 +28,7 @@
#include "db/sysdb_selinux.h"
#include "providers/ipa/ipa_common.h"
+#include "providers/ipa/ipa_dyndns.h"
#include "providers/ldap/sdap_async_private.h"
#include "providers/dp_dyndns.h"
#include "util/sss_krb5.h"
@@ -1018,7 +1019,8 @@ errno_t ipa_get_dyndns_options(struct be_ctx *be_ctx,
bool update;
int ttl;
- ret = be_nsupdate_init(ctx, be_ctx, ipa_dyndns_opts, &ctx->dyndns_ctx);
+ ret = be_nsupdate_init(ctx, be_ctx, ipa_dyndns_opts, ipa_dyndns_timer,
+ ctx, &ctx->dyndns_ctx);
if (ret != EOK) {
DEBUG(SSSDBG_OP_FAILURE,
("Cannot initialize IPA dyndns opts [%d]: %s\n",
diff --git a/src/providers/ipa/ipa_dyndns.c b/src/providers/ipa/ipa_dyndns.c
index 8023b533..52b8051b 100644
--- a/src/providers/ipa/ipa_dyndns.c
+++ b/src/providers/ipa/ipa_dyndns.c
@@ -55,6 +55,98 @@ errno_t ipa_dyndns_init(struct be_ctx *be_ctx,
return EOK;
}
+struct ipa_dyndns_timer_ctx {
+ struct sdap_id_op *sdap_op;
+ struct tevent_context *ev;
+
+ struct ipa_options *ctx;
+};
+
+static void ipa_dyndns_timer_connected(struct tevent_req *req);
+
+void ipa_dyndns_timer(void *pvt)
+{
+ struct ipa_options *ctx = talloc_get_type(pvt, struct ipa_options);
+ struct sdap_id_ctx *sdap_ctx = ctx->id_ctx->sdap_id_ctx;
+ struct tevent_req *req;
+ struct ipa_dyndns_timer_ctx *timer_ctx;
+ errno_t ret;
+
+ timer_ctx = talloc_zero(ctx, struct ipa_dyndns_timer_ctx);
+ if (timer_ctx == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, ("Out of memory\n"));
+ /* Not much we can do */
+ return;
+ }
+ timer_ctx->ev = sdap_ctx->be->ev;
+ timer_ctx->ctx = ctx;
+
+ /* In order to prevent the connection triggering an
+ * online callback which would in turn trigger a concurrent DNS
+ * update
+ */
+ ctx->dyndns_ctx->timer_in_progress = true;
+
+ /* Make sure to have a valid LDAP connection */
+ timer_ctx->sdap_op = sdap_id_op_create(timer_ctx, sdap_ctx->conn_cache);
+ if (timer_ctx->sdap_op == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, ("sdap_id_op_create failed\n"));
+ goto fail;
+ }
+
+ req = sdap_id_op_connect_send(timer_ctx->sdap_op, timer_ctx, &ret);
+ if (req == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, ("sdap_id_op_connect_send failed: [%d](%s)\n",
+ ret, sss_strerror(ret)));
+ goto fail;
+ }
+ tevent_req_set_callback(req, ipa_dyndns_timer_connected, timer_ctx);
+ return;
+
+fail:
+ ctx->dyndns_ctx->timer_in_progress = false;
+ be_nsupdate_timer_schedule(timer_ctx->ev, ctx->dyndns_ctx);
+ talloc_free(timer_ctx);
+}
+
+static void ipa_dyndns_timer_connected(struct tevent_req *req)
+{
+ errno_t ret;
+ int dp_error;
+ struct ipa_dyndns_timer_ctx *timer_ctx = tevent_req_callback_data(req,
+ struct ipa_dyndns_timer_ctx);
+ struct tevent_context *ev;
+ struct ipa_options *ctx;
+
+ ctx = timer_ctx->ctx;
+ ev = timer_ctx->ev;
+ ctx->dyndns_ctx->timer_in_progress = false;
+
+ ret = sdap_id_op_connect_recv(req, &dp_error);
+ talloc_zfree(req);
+ talloc_free(timer_ctx);
+ if (ret != EOK) {
+ if (dp_error == DP_ERR_OFFLINE) {
+ DEBUG(SSSDBG_MINOR_FAILURE, ("No IPA server is available, "
+ "dynamic DNS update is skipped in offline mode.\n"));
+ /* Another timer will be scheduled when provider goes online
+ * and ipa_dyndns_update() is called */
+ } else {
+ DEBUG(SSSDBG_OP_FAILURE,
+ ("Failed to connect to LDAP server: [%d](%s)\n",
+ ret, sss_strerror(ret)));
+
+ /* Just schedule another dyndns retry */
+ be_nsupdate_timer_schedule(ev, ctx->dyndns_ctx);
+ }
+ return;
+ }
+
+ /* all OK just call ipa_dyndns_update and schedule another refresh */
+ be_nsupdate_timer_schedule(ev, ctx->dyndns_ctx);
+ return ipa_dyndns_update(ctx);
+}
+
static struct tevent_req *ipa_dyndns_update_send(struct ipa_options *ctx);
static errno_t ipa_dyndns_update_recv(struct tevent_req *req);
@@ -63,6 +155,11 @@ static void ipa_dyndns_nsupdate_done(struct tevent_req *subreq);
void ipa_dyndns_update(void *pvt)
{
struct ipa_options *ctx = talloc_get_type(pvt, struct ipa_options);
+ struct sdap_id_ctx *sdap_ctx = ctx->id_ctx->sdap_id_ctx;
+
+ /* Schedule timer after provider went offline */
+ be_nsupdate_timer_schedule(sdap_ctx->be->ev, ctx->dyndns_ctx);
+
struct tevent_req *req = ipa_dyndns_update_send(ctx);
if (req == NULL) {
DEBUG(SSSDBG_CRIT_FAILURE, ("Could not update DNS\n"));
@@ -109,6 +206,16 @@ ipa_dyndns_update_send(struct ipa_options *ctx)
}
state->ipa_ctx = ctx;
+ if (ctx->dyndns_ctx->last_refresh + 60 > time(NULL) ||
+ ctx->dyndns_ctx->timer_in_progress) {
+ DEBUG(SSSDBG_FUNC_DATA, ("Last periodic update ran recently or timer"
+ "in progress, not scheduling another update\n"));
+ tevent_req_done(req);
+ tevent_req_post(req, sdap_ctx->be->ev);
+ return req;
+ }
+ state->ipa_ctx->dyndns_ctx->last_refresh = time(NULL);
+
dns_zone = dp_opt_get_string(ctx->basic, IPA_DOMAIN);
if (!dns_zone) {
ret = EIO;
diff --git a/src/providers/ipa/ipa_dyndns.h b/src/providers/ipa/ipa_dyndns.h
index d86c6634..ced3f097 100644
--- a/src/providers/ipa/ipa_dyndns.h
+++ b/src/providers/ipa/ipa_dyndns.h
@@ -25,6 +25,9 @@
#ifndef IPA_DYNDNS_H_
#define IPA_DYNDNS_H_
+void ipa_dyndns_update(void *pvt);
+void ipa_dyndns_timer(void *pvt);
+
errno_t ipa_dyndns_init(struct be_ctx *be_ctx,
struct ipa_options *ctx);
diff --git a/src/providers/ipa/ipa_opts.h b/src/providers/ipa/ipa_opts.h
index 392fcd86..57911082 100644
--- a/src/providers/ipa/ipa_opts.h
+++ b/src/providers/ipa/ipa_opts.h
@@ -53,6 +53,7 @@ struct dp_option ipa_basic_opts[] = {
struct dp_option ipa_dyndns_opts[] = {
{ "dyndns_update", DP_OPT_BOOL, BOOL_FALSE, BOOL_FALSE },
+ { "dyndns_refresh_interval", DP_OPT_NUMBER, NULL_NUMBER, NULL_NUMBER },
{ "dyndns_iface", DP_OPT_STRING, NULL_STRING, NULL_STRING },
{ "dyndns_ttl", DP_OPT_NUMBER, { .number = 1200 }, NULL_NUMBER },
DP_OPTION_TERMINATOR
diff --git a/src/tests/cmocka/test_dyndns.c b/src/tests/cmocka/test_dyndns.c
index 93d3373a..0657bf1b 100644
--- a/src/tests/cmocka/test_dyndns.c
+++ b/src/tests/cmocka/test_dyndns.c
@@ -35,6 +35,12 @@
#include "tests/cmocka/common_mock.h"
#include "src/providers/dp_dyndns.h"
+#define TESTS_PATH "tests_dyndns"
+#define TEST_CONF_DB "test_dyndns_conf.ldb"
+#define TEST_SYSDB_FILE "cache_dyndns_test.ldb"
+#define TEST_DOM_NAME "dyndns_test"
+#define TEST_ID_PROVIDER "ldap"
+
enum mock_nsupdate_states {
MOCK_NSUPDATE_OK,
MOCK_NSUPDATE_ERR,
@@ -44,6 +50,9 @@ enum mock_nsupdate_states {
struct dyndns_test_ctx {
struct sss_test_ctx *tctx;
+ struct be_ctx *be_ctx;
+ struct be_nsupdate_ctx *update_ctx;
+
enum mock_nsupdate_states state;
int child_status;
int child_retval;
@@ -275,15 +284,73 @@ void dyndns_test_timeout(void **state)
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_timer, dyndns_test_ctx,
+ &dyndns_test_ctx->update_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());
dyndns_test_ctx = talloc_zero(global_talloc_context, struct dyndns_test_ctx);
assert_non_null(dyndns_test_ctx);
- dyndns_test_ctx->tctx = create_ev_test_ctx(dyndns_test_ctx);
+ dyndns_test_ctx->tctx = create_dom_test_ctx(dyndns_test_ctx, TESTS_PATH,
+ TEST_CONF_DB, TEST_SYSDB_FILE,
+ 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 = talloc_asprintf(dyndns_test_ctx,
+ CONFDB_DOMAIN_PATH_TMPL,
+ TEST_DOM_NAME);
+ assert_non_null(dyndns_test_ctx->be_ctx->conf_path);
}
void dyndns_test_teardown(void **state)
@@ -295,11 +362,14 @@ void dyndns_test_teardown(void **state)
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
};
@@ -314,6 +384,8 @@ int main(int argc, const char *argv[])
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. */
@@ -332,8 +404,17 @@ int main(int argc, const char *argv[])
poptFreeContext(pc);
DEBUG_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);
+ test_dom_suite_cleanup(TESTS_PATH, TEST_CONF_DB, TEST_SYSDB_FILE);
+ test_dom_suite_setup(TESTS_PATH);
+ tests_set_cwd();
+ rv = run_tests(tests);
+ if (rv == 0 && !no_cleanup) {
+ test_dom_suite_cleanup(TESTS_PATH, TEST_CONF_DB, TEST_SYSDB_FILE);
+ }
return rv;
}