[SSSD] [PATCH] New dynamic DNS update options

Jakub Hrozek jhrozek at redhat.com
Fri May 3 11:20:00 UTC 2013


On Thu, May 02, 2013 at 08:03:18PM +0200, Jakub Hrozek wrote:
> > 
> > Code-wise ack.
> 
> The attached patches rename the commit message to include new name of
> dyndns_force_tcp and remove the compilation warning in the resolver
> code.

Pavel spotted a compilation warning in resolv_get_string_ptr_address.
New patches attached, but just the one that includes
resolv_get_string_ptr_address changed.
-------------- next part --------------
>From e548dbd6323bc9a602fc3535ec6773dff79d20b2 Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhrozek at redhat.com>
Date: Tue, 9 Apr 2013 17:40:40 +0200
Subject: [PATCH 1/5] dyndns: new option dyndns_refresh_interval

This new options adds the possibility of updating the DNS entries
periodically regardless if they have changed or not. This feature
will be useful mainly in AD environments where the Windows clients
periodically update their DNS records.
---
 Makefile.am                          |   3 +
 src/config/SSSDConfig/__init__.py.in |   1 +
 src/config/SSSDConfigTest.py         |   2 +
 src/config/etc/sssd.api.conf         |   1 +
 src/man/sssd-ipa.5.xml               |  16 ++++++
 src/providers/dp_dyndns.c            |  53 ++++++++++++++++-
 src/providers/dp_dyndns.h            |  17 +++++-
 src/providers/ipa/ipa_common.c       |   4 +-
 src/providers/ipa/ipa_dyndns.c       | 107 +++++++++++++++++++++++++++++++++++
 src/providers/ipa/ipa_dyndns.h       |   3 +
 src/providers/ipa/ipa_opts.h         |   1 +
 src/tests/cmocka/test_dyndns.c       |  85 +++++++++++++++++++++++++++-
 12 files changed, 286 insertions(+), 7 deletions(-)

diff --git a/Makefile.am b/Makefile.am
index f3da506777bf87d1ffc36ba736b0184f5101be80..563dbc834489daecce664e555958162ea05a7294 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1261,10 +1261,13 @@ test_io_CFLAGS = \
 test_io_LDADD = \
     $(CMOCKA_LIBS)
 
+dyndns_tests_DEPENDENCIES = \
+     $(ldblib_LTLIBRARIES)
 dyndns_tests_SOURCES = \
      $(TEST_MOCK_OBJ) \
      $(SSSD_RESOLV_OBJ) \
      src/tests/common_tev.c \
+     src/tests/common_dom.c \
      src/tests/cmocka/test_dyndns.c \
      src/providers/data_provider_opts.c
 dyndns_tests_CFLAGS = \
diff --git a/src/config/SSSDConfig/__init__.py.in b/src/config/SSSDConfig/__init__.py.in
index 9f11d31c2dab4583cf6ebb956940fd17d890c985..003153d0141ef88ab2c17cd4165bf3280144cf13 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 c0d4a1476a0e08f04e7aa24ecc5293992cced3ca..d3bd9f35575aa154490292833002d31eb2477517 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 02e2a0a6625445f1361dceea866a32b916bea01a..b09cbd18768c1ba9bbc2aa1d632df8de400f6f77 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 bedb1107234bf69e60cc174b1086a181fa80f806..426e9a5bbee76c7da62c8100fec5834e89befd0c 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 81228d5cfb59e472167b50dda819ec4ae49a928c..337817fc73bed655b4ffac41a8bf88cadc0b2e50 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 95403a3ae73fc47c6a9b5240c640325da1045e39..e49ab8f0309bda5da6e6b00f1cb53016b88ae22c 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 51750b2af3e679dafee8cb88081839c11a261757..baece274ed48b9446806bf1103789b2ace9c0d95 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 8023b5336b0800b6bb019afe5a6337f43a3f2a82..52b8051b0ab1b2b91b3972ab8673517b01cfff0d 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 d86c6634b9b5eb016cfc3b363b432f3641d2387f..ced3f0977a1dc1f07476f71e340276060d48d531 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 392fcd86fbe57c7b699390ed68918208886ffaab..57911082ad6a89f09becdee30f99a89a74e7d12e 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 fd92404388e52bfb9b7be544e9a3ffae923ebeb7..98daebf37ae308d3578296bc0d2e9298fa69854b 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();
+    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;
 }
-- 
1.8.2.1

-------------- next part --------------
>From 052f216eb0516e35fa43d0425386fc9697f9beea Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhrozek at redhat.com>
Date: Tue, 30 Apr 2013 16:40:00 +0200
Subject: [PATCH 2/5] resolver: Return PTR record as string

This is a requirement to update the PTR records.
Includes a unit test.
---
 src/resolv/async_resolv.c |  36 ++++++++++++++++
 src/resolv/async_resolv.h |   4 ++
 src/tests/resolv-tests.c  | 104 ++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 144 insertions(+)

diff --git a/src/resolv/async_resolv.c b/src/resolv/async_resolv.c
index 60d9e05bfa61f1490263a3defa21cab1c709c5ac..f673a25c322b713e9a2e5bd6908ce1cf0e0420cd 100644
--- a/src/resolv/async_resolv.c
+++ b/src/resolv/async_resolv.c
@@ -1416,6 +1416,42 @@ resolv_get_string_address_index(TALLOC_CTX *mem_ctx,
     return address;
 }
 
+char *
+resolv_get_string_ptr_address(TALLOC_CTX *mem_ctx,
+                              int family, uint8_t *address)
+{
+    char *straddr;
+
+    if (family == AF_INET6) {
+        int i;
+        char hexbyte[3];
+
+        straddr = talloc_strdup(mem_ctx, "\0");
+        if (!straddr) {
+            return NULL;
+        }
+
+        for (i = 15; i >= 0; i--) {
+            snprintf(hexbyte, 3, "%02x", address[i]);
+            straddr = talloc_asprintf_append(straddr, "%c.%c.",
+                                             hexbyte[1], hexbyte[0]);
+        }
+        straddr = talloc_asprintf_append(straddr, "ip6.arpa.");
+    } else if (family == AF_INET) {
+        straddr = talloc_asprintf(mem_ctx,
+                                  "%u.%u.%u.%u.in-addr.arpa.",
+                                  (address[3]),
+                                  (address[2]),
+                                  (address[1]),
+                                  (address[0]));
+    } else {
+        DEBUG(SSSDBG_CRIT_FAILURE, ("Unknown address family\n"));
+        return NULL;
+    }
+
+    return straddr;
+}
+
 struct sockaddr_storage *
 resolv_get_sockaddr_address(TALLOC_CTX *mem_ctx, struct resolv_hostent *hostent,
                             int port)
diff --git a/src/resolv/async_resolv.h b/src/resolv/async_resolv.h
index 3a02558866a078a0f52774a3fd84f21a0709f3a1..d759a82f37fa8bebc5d6b4b6d9a65e935e4cbb52 100644
--- a/src/resolv/async_resolv.h
+++ b/src/resolv/async_resolv.h
@@ -121,6 +121,10 @@ resolv_get_string_address_index(TALLOC_CTX *mem_ctx,
                                 struct resolv_hostent *hostent,
                                 unsigned int addrindex);
 
+char *
+resolv_get_string_ptr_address(TALLOC_CTX *mem_ctx,
+                              int family, uint8_t *address);
+
 #define resolv_get_string_address(mem_ctx, hostent) \
         resolv_get_string_address_index(mem_ctx, hostent, 0)
 
diff --git a/src/tests/resolv-tests.c b/src/tests/resolv-tests.c
index dd212c055f6c25c9add3c49a701df19c2e802faf..49fe2a5f6c21988cfd1a98aac096a7f94461ce45 100644
--- a/src/tests/resolv-tests.c
+++ b/src/tests/resolv-tests.c
@@ -95,6 +95,62 @@ static int test_loop(struct resolv_test_ctx *data)
     return data->error;
 }
 
+struct resolv_hostent *
+test_create_rhostent(TALLOC_CTX *mem_ctx,
+                     const char *hostname, const char *address)
+{
+    struct resolv_hostent *rhostent;
+    int ret;
+    int family;
+
+    rhostent = talloc_zero(mem_ctx, struct resolv_hostent);
+    if (!rhostent) {
+        return NULL;
+    }
+
+    rhostent->name = talloc_strdup(rhostent, hostname);
+    rhostent->addr_list = talloc_array(rhostent, struct resolv_addr *, 2);
+    if (!rhostent->name ||
+        !rhostent->addr_list) {
+        goto fail;
+    }
+
+    rhostent->addr_list[0] = talloc_zero(rhostent->addr_list,
+                                         struct resolv_addr);
+    if (!rhostent->addr_list[0]) {
+        goto fail;
+    }
+    rhostent->addr_list[0]->ipaddr = talloc_array(rhostent->addr_list[0],
+                                                  uint8_t,
+                                                  sizeof(struct in6_addr));
+    if (!rhostent->addr_list[0]->ipaddr) {
+        goto fail;
+    }
+
+    family = AF_INET;
+    ret = inet_pton(family, address,
+                    rhostent->addr_list[0]->ipaddr);
+    if (ret != 1) {
+        family = AF_INET6;
+        ret = inet_pton(family, address,
+                        rhostent->addr_list[0]->ipaddr);
+        if (ret != 1) {
+            goto fail;
+        }
+    }
+
+    rhostent->addr_list[0]->ttl = RESOLV_DEFAULT_TTL;
+    rhostent->addr_list[1] = NULL;
+    rhostent->family = family;
+    rhostent->aliases = NULL;
+
+    return rhostent;
+
+fail:
+    talloc_free(rhostent);
+    return NULL;
+}
+
 START_TEST(test_copy_hostent)
 {
     void *ctx;
@@ -155,6 +211,53 @@ START_TEST(test_copy_hostent)
 }
 END_TEST
 
+START_TEST(test_address_to_string)
+{
+    void *ctx;
+    struct resolv_hostent *rhe;
+    char *str_addr;
+    char *ptr_addr;
+
+    ctx = talloc_new(global_talloc_context);
+    fail_if(ctx == NULL);
+    ck_leaks_push(ctx);
+
+    rhe = test_create_rhostent(ctx, "www.example.com", "1.2.3.4");
+    fail_if(rhe == NULL);
+
+    str_addr = resolv_get_string_address_index(ctx, rhe, 0);
+    fail_if(str_addr == NULL);
+    fail_unless(strcmp(str_addr, "1.2.3.4") == 0, "Unexpected address\n");
+    talloc_free(str_addr);
+
+    ptr_addr = resolv_get_string_ptr_address(ctx, rhe->family,
+                                             rhe->addr_list[0]->ipaddr);
+    fail_if(ptr_addr == NULL);
+    fail_unless(strcmp(ptr_addr, "4.3.2.1.in-addr.arpa.") == 0, "Unexpected PTR address\n");
+    talloc_free(ptr_addr);
+
+    talloc_free(rhe);
+
+    rhe = test_create_rhostent(ctx, "www6.example.com", "2607:f8b0:400c:c03::6a");
+    fail_if(rhe == NULL);
+
+    str_addr = resolv_get_string_address_index(ctx, rhe, 0);
+    fail_if(str_addr == NULL);
+    fail_unless(strcmp(str_addr, "2607:f8b0:400c:c03::6a") == 0, "Unexpected address\n");
+    talloc_free(str_addr);
+
+    ptr_addr = resolv_get_string_ptr_address(ctx, rhe->family,
+                                             rhe->addr_list[0]->ipaddr);
+    fail_if(ptr_addr == NULL);
+    fail_unless(strcmp(ptr_addr,
+                       "a.6.0.0.0.0.0.0.0.0.0.0.0.0.0.0.3.0.c.0.c.0.0.4.0.b.8.f.7.0.6.2.ip6.arpa.") == 0, "Unexpected PTR address\n");
+    talloc_free(ptr_addr);
+
+    talloc_free(rhe);
+    ck_leaks_pop(ctx);
+}
+END_TEST
+
 static void test_ip_addr(struct tevent_req *req)
 {
     int recv_status;
@@ -791,6 +894,7 @@ Suite *create_resolv_suite(void)
     tcase_add_checked_fixture(tc_resolv, ck_leak_check_setup, ck_leak_check_teardown);
     /* Do some testing */
     tcase_add_test(tc_resolv, test_copy_hostent);
+    tcase_add_test(tc_resolv, test_address_to_string);
     tcase_add_test(tc_resolv, test_resolv_ip_addr);
     tcase_add_test(tc_resolv, test_resolv_sort_srv_reply);
     if (use_net_test) {
-- 
1.8.2.1

-------------- next part --------------
>From 08ab31df612f8a7f90a4a68603a39c7f700ebd34 Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhrozek at redhat.com>
Date: Tue, 30 Apr 2013 16:40:09 +0200
Subject: [PATCH 3/5] dyndns: New option dyndns_update_ptr

https://fedorahosted.org/sssd/ticket/1832

While some servers, such as FreeIPA allow the PTR record to be
synchronized when the forward record is updated, other servers,
including Active Directory, require that the PTR record is synchronized
manually.

This patch adds a new option, dyndns_update_ptr that automatically
generates appropriate DNS update message for updating the reverse zone.

This option is off by default in the IPA provider.

Also renames be_nsupdate_create_msg to be_nsupdate_create_fwd_msg
---
 src/config/SSSDConfig/__init__.py.in |   1 +
 src/config/SSSDConfigTest.py         |   2 +
 src/config/etc/sssd.api.conf         |   1 +
 src/man/sssd-ipa.5.xml               |  20 +++
 src/providers/dp_dyndns.c            | 329 ++++++++++++++++++++++++++---------
 src/providers/dp_dyndns.h            |  28 ++-
 src/providers/ipa/ipa_dyndns.c       |   4 +-
 src/providers/ipa/ipa_opts.h         |   1 +
 src/providers/ldap/sdap_dyndns.c     | 217 ++++++++++++++++++-----
 src/providers/ldap/sdap_dyndns.h     |   1 +
 src/resolv/async_resolv.c            |   4 +-
 src/resolv/async_resolv.h            |   7 +-
 12 files changed, 476 insertions(+), 139 deletions(-)

diff --git a/src/config/SSSDConfig/__init__.py.in b/src/config/SSSDConfig/__init__.py.in
index 003153d0141ef88ab2c17cd4165bf3280144cf13..79bf6aa4e97ed75391f97472a780d6349fd1060a 100644
--- a/src/config/SSSDConfig/__init__.py.in
+++ b/src/config/SSSDConfig/__init__.py.in
@@ -129,6 +129,7 @@ option_strings = {
     '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"),
+    'dyndns_update_ptr' : _("Whether the provider should explicitly update the PTR record as well"),
 
     # [provider/ipa]
     'ipa_domain' : _('IPA domain'),
diff --git a/src/config/SSSDConfigTest.py b/src/config/SSSDConfigTest.py
index d3bd9f35575aa154490292833002d31eb2477517..86ff29df1f8225a846903ad6c61c2a6147c9bc69 100755
--- a/src/config/SSSDConfigTest.py
+++ b/src/config/SSSDConfigTest.py
@@ -511,6 +511,7 @@ class SSSDConfigTestSSSDDomain(unittest.TestCase):
             'dyndns_ttl',
             'dyndns_iface',
             'dyndns_refresh_interval',
+            'dyndns_update_ptr',
             'override_gid',
             'case_sensitive',
             'override_homedir',
@@ -858,6 +859,7 @@ class SSSDConfigTestSSSDDomain(unittest.TestCase):
             'dyndns_ttl',
             'dyndns_iface',
             'dyndns_refresh_interval',
+            'dyndns_update_ptr',
             'override_gid',
             'case_sensitive',
             'override_homedir',
diff --git a/src/config/etc/sssd.api.conf b/src/config/etc/sssd.api.conf
index b09cbd18768c1ba9bbc2aa1d632df8de400f6f77..396063b4fd9fe08e65860bd24a68350401a0d7f0 100644
--- a/src/config/etc/sssd.api.conf
+++ b/src/config/etc/sssd.api.conf
@@ -126,6 +126,7 @@ dyndns_update = bool, None, false
 dyndns_ttl = int, None, false
 dyndns_iface = str, None, false
 dyndns_refresh_interval = int, None, false
+dyndns_update_ptr = bool, None, false
 
 # Special providers
 [provider/permit]
diff --git a/src/man/sssd-ipa.5.xml b/src/man/sssd-ipa.5.xml
index 426e9a5bbee76c7da62c8100fec5834e89befd0c..21a28047969043e1abb8f54a708fddc7727d8073 100644
--- a/src/man/sssd-ipa.5.xml
+++ b/src/man/sssd-ipa.5.xml
@@ -220,6 +220,26 @@
                 </varlistentry>
 
                 <varlistentry>
+                    <term>dyndns_update_ptr (bool)</term>
+                    <listitem>
+                        <para>
+                            Whether the PTR record should also be explicitly
+                            updated when updating the client's DNS records.
+                            Applicable only when dyndns_update is true.
+                        </para>
+                        <para>
+                            This options should be False in most IPA
+                            deployments as the IPA server generates the
+                            PTR records automatically when forward records
+                            are changed.
+                        </para>
+                        <para>
+                            Default: False (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 337817fc73bed655b4ffac41a8bf88cadc0b2e50..79701c6294c595c33e69f978fe044a7fdaf5a9c6 100644
--- a/src/providers/dp_dyndns.c
+++ b/src/providers/dp_dyndns.c
@@ -262,64 +262,14 @@ done:
     return ret;
 }
 
-errno_t
-be_nsupdate_create_msg(TALLOC_CTX *mem_ctx, const char *realm,
-                       const char *zone, const char *servername,
-                       const char *hostname, const unsigned int ttl,
-                       uint8_t remove_af, struct sss_iface_addr *addresses,
-                       char **_update_msg)
+static char *
+nsupdate_msg_add_fwd(char *update_msg, struct sss_iface_addr *addresses,
+                     const char *hostname, int ttl, uint8_t remove_af)
 {
-    int ret;
-    char *realm_directive;
-    char ip_addr[INET6_ADDRSTRLEN];
-    const char *ip;
     struct sss_iface_addr *new_record;
-    char *update_msg;
-    TALLOC_CTX *tmp_ctx;
-
-    /* in some cases realm could have been NULL if we weren't using TSIG */
-    if (zone == NULL || hostname == NULL) {
-        return EINVAL;
-    }
-
-    tmp_ctx = talloc_new(NULL);
-    if (tmp_ctx == NULL) return ENOMEM;
-
-#ifdef HAVE_NSUPDATE_REALM
-    realm_directive = talloc_asprintf(tmp_ctx, "realm %s\n", realm);
-#else
-    realm_directive = talloc_asprintf(tmp_ctx, "");
-#endif
-    if (!realm_directive) {
-        ret = ENOMEM;
-        goto done;
-    }
-
-    /* The realm_directive would now either contain an empty string or be
-     * completely empty so we don't need to add another newline here
-     */
-    if (servername) {
-        DEBUG(SSSDBG_FUNC_DATA,
-              ("Creating update message for server [%s], realm [%s] "
-               "and zone [%s].\n", servername, realm, zone));
-
-        /* Add the server, realm and zone headers */
-        update_msg = talloc_asprintf(tmp_ctx, "server %s\n%szone %s.\n",
-                                     servername, realm_directive, zone);
-    } else {
-        DEBUG(SSSDBG_FUNC_DATA,
-              ("Creating update message for realm [%s] and zone [%s].\n",
-               realm, zone));
-
-        /* Add the realm and zone headers */
-        update_msg = talloc_asprintf(tmp_ctx, "%szone %s.\n",
-                                     realm_directive, zone);
-    }
-    talloc_free(realm_directive);
-    if (update_msg == NULL) {
-        ret = ENOMEM;
-        goto done;
-    }
+    char ip_addr[INET6_ADDRSTRLEN];
+    const char *ip;
+    errno_t ret;
 
     /* Remove existing entries as needed */
     if (remove_af & DYNDNS_REMOVE_A) {
@@ -327,8 +277,7 @@ be_nsupdate_create_msg(TALLOC_CTX *mem_ctx, const char *realm,
                                             "update delete %s. in A\nsend\n",
                                             hostname);
         if (update_msg == NULL) {
-            ret = ENOMEM;
-            goto done;
+            return NULL;
         }
     }
     if (remove_af & DYNDNS_REMOVE_AAAA) {
@@ -336,8 +285,7 @@ be_nsupdate_create_msg(TALLOC_CTX *mem_ctx, const char *realm,
                                             "update delete %s. in AAAA\nsend\n",
                                             hostname);
         if (update_msg == NULL) {
-            ret = ENOMEM;
-            goto done;
+            return NULL;
         }
     }
 
@@ -349,7 +297,9 @@ be_nsupdate_create_msg(TALLOC_CTX *mem_ctx, const char *realm,
                            ip_addr, INET6_ADDRSTRLEN);
             if (ip == NULL) {
                 ret = errno;
-                goto done;
+                DEBUG(SSSDBG_OP_FAILURE,
+                      ("inet_ntop failed [%d]: %s\n", ret, strerror(ret)));
+                return NULL;
             }
             break;
 
@@ -359,14 +309,15 @@ be_nsupdate_create_msg(TALLOC_CTX *mem_ctx, const char *realm,
                            ip_addr, INET6_ADDRSTRLEN);
             if (ip == NULL) {
                 ret = errno;
-                goto done;
+                DEBUG(SSSDBG_OP_FAILURE,
+                      ("inet_ntop failed [%d]: %s\n", ret, strerror(ret)));
+                return NULL;
             }
             break;
 
         default:
             DEBUG(SSSDBG_CRIT_FAILURE, ("Unknown address family\n"));
-            ret = EINVAL;
-            goto done;
+            return NULL;
         }
 
         /* Format the record update */
@@ -376,12 +327,179 @@ be_nsupdate_create_msg(TALLOC_CTX *mem_ctx, const char *realm,
                 new_record->addr->ss_family == AF_INET ? "A" : "AAAA",
                 ip_addr);
         if (update_msg == NULL) {
+            return NULL;
+        }
+
+    }
+
+    return talloc_asprintf_append(update_msg, "send\n");
+}
+
+static char *
+nsupdate_msg_add_ptr(char *update_msg, struct sss_iface_addr *addresses,
+                     const char *hostname, int ttl, uint8_t remove_af,
+                     struct sss_iface_addr *old_addresses)
+{
+    struct sss_iface_addr *new_record, *old_record;
+    char *strptr;
+    uint8_t *addr;
+
+    DLIST_FOR_EACH(old_record, old_addresses) {
+        switch(old_record->addr->ss_family) {
+        case AF_INET:
+            if (!(remove_af & DYNDNS_REMOVE_A)) {
+                continue;
+            }
+            addr = (uint8_t *) &((struct sockaddr_in *) old_record->addr)->sin_addr;
+            break;
+        case AF_INET6:
+            if (!(remove_af & DYNDNS_REMOVE_AAAA)) {
+                continue;
+            }
+            addr = (uint8_t *) &((struct sockaddr_in6 *) old_record->addr)->sin6_addr;
+            break;
+        default:
+            DEBUG(SSSDBG_CRIT_FAILURE, ("Unknown address family\n"));
+            return NULL;
+        }
+
+        strptr = resolv_get_string_ptr_address(update_msg, old_record->addr->ss_family,
+                                               addr);
+        if (strptr == NULL) {
+            return NULL;
+        }
+
+        /* example: update delete 38.78.16.10.in-addr.arpa. in PTR */
+        update_msg = talloc_asprintf_append(update_msg,
+                                            "update delete %s in PTR\n", strptr);
+        talloc_free(strptr);
+        if (update_msg == NULL) {
+            return NULL;
+        }
+    }
+
+    /* example: update add 11.78.16.10.in-addr.arpa. 85000 in PTR testvm.example.com */
+    DLIST_FOR_EACH(new_record, addresses) {
+        switch(new_record->addr->ss_family) {
+        case AF_INET:
+            addr = (uint8_t *) &((struct sockaddr_in *) new_record->addr)->sin_addr;
+            break;
+        case AF_INET6:
+            addr = (uint8_t *) &((struct sockaddr_in6 *) new_record->addr)->sin6_addr;
+            break;
+        default:
+            DEBUG(SSSDBG_CRIT_FAILURE, ("Unknown address family\n"));
+            return NULL;
+        }
+
+        strptr = resolv_get_string_ptr_address(update_msg, new_record->addr->ss_family,
+                                               addr);
+        if (strptr == NULL) {
+            return NULL;
+        }
+
+        /* example: update delete 38.78.16.10.in-addr.arpa. in PTR */
+        update_msg = talloc_asprintf_append(update_msg,
+                                            "update add %s %d in PTR %s.\n",
+                                            strptr, ttl, hostname);
+        talloc_free(strptr);
+        if (update_msg == NULL) {
+            return NULL;
+        }
+    }
+
+    return talloc_asprintf_append(update_msg, "send\n");
+}
+
+static char *
+nsupdate_msg_create_common(TALLOC_CTX *mem_ctx, const char *realm,
+                           const char *servername)
+{
+    char *realm_directive;
+    char *update_msg;
+    TALLOC_CTX *tmp_ctx;
+
+    tmp_ctx = talloc_new(NULL);
+    if (tmp_ctx == NULL) return NULL;
+
+#ifdef HAVE_NSUPDATE_REALM
+    realm_directive = talloc_asprintf(tmp_ctx, "realm %s\n", realm);
+#else
+    realm_directive = talloc_asprintf(tmp_ctx, "");
+#endif
+    if (!realm_directive) {
+        goto fail;
+    }
+
+    /* The realm_directive would now either contain an empty string or be
+     * completely empty so we don't need to add another newline here
+     */
+    if (servername) {
+        DEBUG(SSSDBG_FUNC_DATA,
+              ("Creating update message for server [%s] and realm [%s]\n.",
+               servername, realm));
+
+        /* Add the server, realm and headers */
+        update_msg = talloc_asprintf(tmp_ctx, "server %s\n%s",
+                                     servername, realm_directive);
+    } else {
+        DEBUG(SSSDBG_FUNC_DATA,
+              ("Creating update message for realm [%s].\n", realm));
+        /* Add the realm headers */
+        update_msg = talloc_asprintf(tmp_ctx, "%s", realm_directive);
+    }
+    talloc_free(realm_directive);
+    if (update_msg == NULL) {
+        goto fail;
+    }
+
+    update_msg = talloc_steal(mem_ctx, update_msg);
+    talloc_free(tmp_ctx);
+    return update_msg;
+
+fail:
+    talloc_free(tmp_ctx);
+    return NULL;
+}
+
+errno_t
+be_nsupdate_create_fwd_msg(TALLOC_CTX *mem_ctx, const char *realm,
+                           const char *zone, const char *servername,
+                           const char *hostname, const unsigned int ttl,
+                           uint8_t remove_af, struct sss_iface_addr *addresses,
+                           struct sss_iface_addr *old_addresses,
+                           char **_update_msg)
+{
+    int ret;
+    char *update_msg;
+    TALLOC_CTX *tmp_ctx;
+
+    /* in some cases realm could have been NULL if we weren't using TSIG */
+    if (hostname == NULL) {
+        return EINVAL;
+    }
+
+    tmp_ctx = talloc_new(NULL);
+    if (tmp_ctx == NULL) return ENOMEM;
+
+    update_msg = nsupdate_msg_create_common(tmp_ctx, realm, servername);
+    if (update_msg == NULL) {
+        ret = ENOMEM;
+        goto done;
+    }
+
+    if (zone) {
+        DEBUG(SSSDBG_FUNC_DATA,
+              ("Setting the zone explicitly to [%s].\n", zone));
+        update_msg = talloc_asprintf_append(update_msg, "zone %s.\n", zone);
+        if (update_msg == NULL) {
             ret = ENOMEM;
             goto done;
         }
     }
 
-    update_msg = talloc_asprintf_append(update_msg, "send\n");
+    update_msg = nsupdate_msg_add_fwd(update_msg, addresses, hostname,
+                                      ttl, remove_af);
     if (update_msg == NULL) {
         ret = ENOMEM;
         goto done;
@@ -400,6 +518,47 @@ done:
     return ret;
 }
 
+errno_t
+be_nsupdate_create_ptr_msg(TALLOC_CTX *mem_ctx, const char *realm,
+                           const char *servername, const char *hostname,
+                           const unsigned int ttl, uint8_t remove_af,
+                           struct sss_iface_addr *addresses,
+                           struct sss_iface_addr *old_addresses,
+                           char **_update_msg)
+{
+    errno_t ret;
+    char *update_msg;
+
+    /* in some cases realm could have been NULL if we weren't using TSIG */
+    if (hostname == NULL) {
+        return EINVAL;
+    }
+
+    update_msg = nsupdate_msg_create_common(mem_ctx, realm, servername);
+    if (update_msg == NULL) {
+        ret = ENOMEM;
+        goto done;
+    }
+
+    update_msg = nsupdate_msg_add_ptr(update_msg, addresses, hostname,
+                                      ttl, remove_af, old_addresses);
+    if (update_msg == NULL) {
+        ret = ENOMEM;
+        goto done;
+    }
+
+    DEBUG(SSSDBG_TRACE_FUNC,
+          (" -- Begin nsupdate message -- \n%s",
+           update_msg));
+    DEBUG(SSSDBG_TRACE_FUNC,
+          (" -- End nsupdate message -- \n"));
+
+    ret = ERR_OK;
+    *_update_msg = talloc_steal(mem_ctx, update_msg);
+done:
+    return ret;
+}
+
 struct nsupdate_get_addrs_state {
     struct tevent_context *ev;
     struct be_resolv_ctx *be_res;
@@ -407,7 +566,7 @@ struct nsupdate_get_addrs_state {
     const char *hostname;
 
     /* Use sss_addr in this request */
-    char **addrlist;
+    struct sss_iface_addr *addrlist;
     size_t count;
 };
 
@@ -472,6 +631,7 @@ nsupdate_get_addrs_done(struct tevent_req *subreq)
     struct nsupdate_get_addrs_state *state = tevent_req_data(req,
                                      struct nsupdate_get_addrs_state);
     struct resolv_hostent *rhostent;
+    struct sss_iface_addr *addr;
     int i;
     int resolv_status;
 
@@ -509,25 +669,25 @@ nsupdate_get_addrs_done(struct tevent_req *subreq)
         count = 0;
     }
 
-    state->addrlist = talloc_realloc(state, state->addrlist, char *,
-                                     state->count + count + 1);
-    if (!state->addrlist) {
-        ret = ENOMEM;
-        goto done;
-    }
-
     for (i=0; i < count; i++) {
-        state->addrlist[state->count + i] = \
-                        resolv_get_string_address_index(state->addrlist,
-                                                        rhostent, i);
+        addr = talloc(state, struct sss_iface_addr);
+        if (addr == NULL) {
+            ret = ENOMEM;
+            goto done;
+        }
 
-        if (state->addrlist[state->count + i] == NULL) {
+        addr->addr = resolv_get_sockaddr_address_index(addr, rhostent, 0, i);
+        if (addr == NULL) {
             ret = ENOMEM;
             goto done;
         }
+
+        if (state->addrlist) {
+            talloc_steal(state->addrlist, addr);
+        }
+        DLIST_ADD(state->addrlist, addr);
     }
     state->count += count;
-    state->addrlist[state->count] = NULL;
 
     /* If the resolver is set to honor both address families
      * and the first one matched, retry the second one to
@@ -576,14 +736,22 @@ done:
 errno_t
 nsupdate_get_addrs_recv(struct tevent_req *req,
                         TALLOC_CTX *mem_ctx,
-                        char ***_addrlist)
+                        struct sss_iface_addr **_addrlist,
+                        size_t *_count)
 {
     struct nsupdate_get_addrs_state *state = tevent_req_data(req,
                                     struct nsupdate_get_addrs_state);
 
     TEVENT_REQ_RETURN_ON_ERROR(req);
 
-    *_addrlist = talloc_steal(mem_ctx, state->addrlist);
+    if (_addrlist) {
+        *_addrlist = talloc_steal(mem_ctx, state->addrlist);
+    }
+
+    if (_count) {
+        *_count = state->count;
+    }
+
     return EOK;
 }
 
@@ -948,6 +1116,7 @@ static struct dp_option default_dyndns_opts[] = {
     { "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 },
+    { "dyndns_update_ptr", DP_OPT_BOOL, BOOL_TRUE, BOOL_FALSE },
 
     DP_OPTION_TERMINATOR
 };
diff --git a/src/providers/dp_dyndns.h b/src/providers/dp_dyndns.h
index e49ab8f0309bda5da6e6b00f1cb53016b88ae22c..8fdbe487b5a857cb02b13e099176ce07b27da0c4 100644
--- a/src/providers/dp_dyndns.h
+++ b/src/providers/dp_dyndns.h
@@ -46,6 +46,7 @@ enum dp_dyndns_opts {
     DP_OPT_DYNDNS_REFRESH_INTERVAL,
     DP_OPT_DYNDNS_IFACE,
     DP_OPT_DYNDNS_TTL,
+    DP_OPT_DYNDNS_UPDATE_PTR,
 
     DP_OPT_DYNDNS /* attrs counter */
 };
@@ -79,11 +80,20 @@ sss_iface_addr_list_as_str_list(TALLOC_CTX *mem_ctx,
                                 char ***_straddrs);
 
 errno_t
-be_nsupdate_create_msg(TALLOC_CTX *mem_ctx, const char *realm,
-                       const char *zone, const char *servername,
-                       const char *hostname, const unsigned int ttl,
-                       uint8_t remove_af, struct sss_iface_addr *addresses,
-                       char **_update_msg);
+be_nsupdate_create_fwd_msg(TALLOC_CTX *mem_ctx, const char *realm,
+                           const char *zone, const char *servername,
+                           const char *hostname, const unsigned int ttl,
+                           uint8_t remove_af, struct sss_iface_addr *addresses,
+                           struct sss_iface_addr *old_addresses,
+                           char **_update_msg);
+
+errno_t
+be_nsupdate_create_ptr_msg(TALLOC_CTX *mem_ctx, const char *realm,
+                           const char *servername, const char *hostname,
+                           const unsigned int ttl, uint8_t remove_af,
+                           struct sss_iface_addr *addresses,
+                           struct sss_iface_addr *old_addresses,
+                           char **_update_msg);
 
 /* Returns:
  *    * ERR_OK              - on success
@@ -100,8 +110,10 @@ struct tevent_req * nsupdate_get_addrs_send(TALLOC_CTX *mem_ctx,
                                             struct tevent_context *ev,
                                             struct be_resolv_ctx *be_res,
                                             const char *hostname);
-errno_t nsupdate_get_addrs_recv(struct tevent_req *req,
-                                TALLOC_CTX *mem_ctx,
-                                char ***_addrlist);
+errno_t
+nsupdate_get_addrs_recv(struct tevent_req *req,
+                        TALLOC_CTX *mem_ctx,
+                        struct sss_iface_addr **_addrlist,
+                        size_t *_count);
 
 #endif /* DP_DYNDNS_H_ */
diff --git a/src/providers/ipa/ipa_dyndns.c b/src/providers/ipa/ipa_dyndns.c
index 52b8051b0ab1b2b91b3972ab8673517b01cfff0d..bb38d105f41b46cf26f0f11c399a23f2a9642a92 100644
--- a/src/providers/ipa/ipa_dyndns.c
+++ b/src/providers/ipa/ipa_dyndns.c
@@ -242,7 +242,9 @@ ipa_dyndns_update_send(struct ipa_options *ctx)
     }
 
     subreq = sdap_dyndns_update_send(state, sdap_ctx->be->ev,
-                                     sdap_ctx->be, sdap_ctx,
+                                     sdap_ctx->be,
+                                     ctx->dyndns_ctx->opts,
+                                     sdap_ctx,
                                      dp_opt_get_string(ctx->dyndns_ctx->opts,
                                                        DP_OPT_DYNDNS_IFACE),
                                      dp_opt_get_string(ctx->basic,
diff --git a/src/providers/ipa/ipa_opts.h b/src/providers/ipa/ipa_opts.h
index 57911082ad6a89f09becdee30f99a89a74e7d12e..bfb09e36cb3562b61c5bdb1dd928c5b124bdd0ce 100644
--- a/src/providers/ipa/ipa_opts.h
+++ b/src/providers/ipa/ipa_opts.h
@@ -56,6 +56,7 @@ struct dp_option ipa_dyndns_opts[] = {
     { "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 },
+    { "dyndns_update_ptr", DP_OPT_BOOL, BOOL_FALSE, BOOL_FALSE },
     DP_OPTION_TERMINATOR
 };
 
diff --git a/src/providers/ldap/sdap_dyndns.c b/src/providers/ldap/sdap_dyndns.c
index e7fad7ba5c5ed7ef3b70713d938068f0890884f9..8f3c4fa4a6171fa9a490384db0204db0a6b97983 100644
--- a/src/providers/ldap/sdap_dyndns.c
+++ b/src/providers/ldap/sdap_dyndns.c
@@ -43,6 +43,7 @@ sdap_dyndns_get_addrs_recv(struct tevent_req *req,
 struct sdap_dyndns_update_state {
     struct tevent_context *ev;
     struct be_resolv_ctx *be_res;
+    struct dp_option *opts;
 
     const char *hostname;
     const char *dns_zone;
@@ -51,22 +52,29 @@ struct sdap_dyndns_update_state {
     int ttl;
 
     struct sss_iface_addr *addresses;
+    struct sss_iface_addr *dns_addrlist;
     uint8_t remove_af;
 
+    bool update_ptr;
     bool check_diff;
     bool use_server_with_nsupdate;
     char *update_msg;
 };
 
 static void sdap_dyndns_update_addrs_done(struct tevent_req *subreq);
-static void sdap_dyndns_addrs_check_done(struct tevent_req *subreq);
+static void sdap_dyndns_dns_addrs_done(struct tevent_req *subreq);
+static errno_t sdap_dyndns_addrs_diff(struct sdap_dyndns_update_state *state,
+                                      bool *_do_update);
 static errno_t sdap_dyndns_update_step(struct tevent_req *req);
+static errno_t sdap_dyndns_update_ptr_step(struct tevent_req *req);
 static void sdap_dyndns_update_done(struct tevent_req *subreq);
+static void sdap_dyndns_update_ptr_done(struct tevent_req *subreq);
 
 struct tevent_req *
 sdap_dyndns_update_send(TALLOC_CTX *mem_ctx,
                         struct tevent_context *ev,
                         struct be_ctx *be_ctx,
+                        struct dp_option *opts,
                         struct sdap_id_ctx *sdap_ctx,
                         const char *ifname,
                         const char *hostname,
@@ -86,6 +94,7 @@ sdap_dyndns_update_send(TALLOC_CTX *mem_ctx,
         return NULL;
     }
     state->check_diff = check_diff;
+    state->update_ptr = dp_opt_get_bool(opts, DP_OPT_DYNDNS_UPDATE_PTR);
     state->hostname = hostname;
     state->dns_zone = dns_zone;
     state->realm = realm;
@@ -94,6 +103,7 @@ sdap_dyndns_update_send(TALLOC_CTX *mem_ctx,
     state->ttl = ttl;
     state->be_res = be_ctx->be_res;
     state->ev = ev;
+    state->opts = opts;
 
     if (ifname) {
        /* Unless one family is restricted, just replace all
@@ -155,8 +165,10 @@ sdap_dyndns_update_addrs_done(struct tevent_req *subreq)
         return;
     }
 
-    if (state->check_diff) {
-        /* Check if we need the update at all */
+    if (state->check_diff || state->update_ptr) {
+        /* Check if we need the update at all. In case we are updating the PTR
+         * records as well, we need to know the old addresses to be able to
+         * reliably delete the PTR records */
         subreq = nsupdate_get_addrs_send(state, state->ev,
                                          state->be_res, state->hostname);
         if (subreq == NULL) {
@@ -164,7 +176,7 @@ sdap_dyndns_update_addrs_done(struct tevent_req *subreq)
             tevent_req_error(req, ret);
             return;
         }
-        tevent_req_set_callback(subreq, sdap_dyndns_addrs_check_done, req);
+        tevent_req_set_callback(subreq, sdap_dyndns_dns_addrs_done, req);
         return;
     }
 
@@ -178,37 +190,81 @@ sdap_dyndns_update_addrs_done(struct tevent_req *subreq)
 }
 
 static void
-sdap_dyndns_addrs_check_done(struct tevent_req *subreq)
+sdap_dyndns_dns_addrs_done(struct tevent_req *subreq)
+{
+    struct tevent_req *req;
+    struct sdap_dyndns_update_state *state;
+    errno_t ret;
+    bool do_update;
+
+    req = tevent_req_callback_data(subreq, struct tevent_req);
+    state = tevent_req_data(req, struct sdap_dyndns_update_state);
+
+    ret = nsupdate_get_addrs_recv(subreq, state, &state->dns_addrlist, NULL);
+    talloc_zfree(subreq);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_OP_FAILURE,
+              ("Could not receive list of current addresses [%d]: %s\n",
+              ret, sss_strerror(ret)));
+        tevent_req_error(req, ret);
+        return;
+    }
+
+    if (state->check_diff) {
+        ret = sdap_dyndns_addrs_diff(state, &do_update);
+        if (ret != EOK) {
+            DEBUG(SSSDBG_OP_FAILURE, ("Could not check the diff between DNS "
+                  "and current addresses [%d]: %s\n", ret, strerror(ret)));
+            tevent_req_error(req, ret);
+            return;
+        }
+
+        if (do_update == false) {
+            DEBUG(SSSDBG_TRACE_FUNC,
+                  ("No DNS update needed, addresses did not change\n"));
+            tevent_req_done(req);
+            return;
+        }
+        DEBUG(SSSDBG_TRACE_FUNC,
+              ("Detected IP addresses change, will perform an update\n"));
+    }
+
+    /* Either we needed the addresses for updating PTR records only or
+     * the addresses have changed (or both) */
+    ret = sdap_dyndns_update_step(req);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_OP_FAILURE, ("Could not start the update [%d]: %s\n",
+              ret, sss_strerror(ret)));
+        tevent_req_error(req, ret);
+    }
+    return;
+}
+
+static errno_t
+sdap_dyndns_addrs_diff(struct sdap_dyndns_update_state *state, bool *_do_update)
 {
     errno_t ret;
     int i;
-    struct tevent_req *req;
-    struct sdap_dyndns_update_state *state;
     char **str_dnslist = NULL, **str_local_list = NULL;
     char **dns_only = NULL, **local_only = NULL;
     bool do_update;
 
-    req = tevent_req_callback_data(subreq, struct tevent_req);
-    state = tevent_req_data(req, struct sdap_dyndns_update_state);
-
-    ret = nsupdate_get_addrs_recv(subreq, state, &str_dnslist);
-    talloc_zfree(subreq);
-    if (ret != EOK) {
-        DEBUG(SSSDBG_OP_FAILURE,
-              ("Could not receive list of current addresses [%d]: %s\n",
-              ret, sss_strerror(ret)));
-        tevent_req_error(req, ret);
-        return;
-    }
-
     ret = sss_iface_addr_list_as_str_list(state,
-                                          state->addresses, &str_local_list);
+                                          state->dns_addrlist, &str_dnslist);
     if (ret != EOK) {
         DEBUG(SSSDBG_OP_FAILURE,
                ("Converting DNS IP addresses to strings failed: [%d]: %s\n",
                ret, sss_strerror(ret)));
-        tevent_req_error(req, ret);
-        return;
+        return ret;
+    }
+
+    ret = sss_iface_addr_list_as_str_list(state,
+                                          state->addresses, &str_local_list);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_OP_FAILURE,
+               ("Converting local IP addresses to strings failed: [%d]: %s\n",
+               ret, sss_strerror(ret)));
+        return ret;
     }
 
     /* Compare the lists */
@@ -217,8 +273,7 @@ sdap_dyndns_addrs_check_done(struct tevent_req *subreq)
     if (ret != EOK) {
         DEBUG(SSSDBG_OP_FAILURE,
               ("diff_string_lists failed: [%d]: %s\n", ret, sss_strerror(ret)));
-        tevent_req_error(req, ret);
-        return;
+        return ret;
     }
 
     if (dns_only) {
@@ -237,22 +292,8 @@ sdap_dyndns_addrs_check_done(struct tevent_req *subreq)
         }
     }
 
-    if (do_update) {
-        DEBUG(SSSDBG_TRACE_FUNC,
-              ("Detected IP addresses change, will perform an update\n"));
-        ret = sdap_dyndns_update_step(req);
-        if (ret != EOK) {
-            DEBUG(SSSDBG_OP_FAILURE, ("Could not start the update [%d]: %s\n",
-                  ret, sss_strerror(ret)));
-            tevent_req_error(req, ret);
-        }
-        return;
-    }
-
-    DEBUG(SSSDBG_TRACE_FUNC,
-          ("No DNS update needed, addresses did not change\n"));
-    tevent_req_done(req);
-    return;
+    *_do_update = do_update;
+    return EOK;
 }
 
 static errno_t
@@ -271,11 +312,11 @@ sdap_dyndns_update_step(struct tevent_req *req)
         servername = state->servername;
     }
 
-    ret = be_nsupdate_create_msg(state, state->realm, state->dns_zone,
-                                 servername, state->hostname,
-                                 state->ttl, state->remove_af,
-                                 state->addresses,
-                                 &state->update_msg);
+    ret = be_nsupdate_create_fwd_msg(state, state->realm, state->dns_zone,
+                                     servername, state->hostname,
+                                     state->ttl, state->remove_af,
+                                     state->addresses, state->dns_addrlist,
+                                     &state->update_msg);
     if (ret != EOK) {
         DEBUG(SSSDBG_OP_FAILURE, ("Can't get addresses for DNS update\n"));
         return ret;
@@ -321,6 +362,90 @@ sdap_dyndns_update_done(struct tevent_req *subreq)
         return;
     }
 
+    if (state->update_ptr == false) {
+        DEBUG(SSSDBG_TRACE_FUNC, ("No PTR update requested, done\n"));
+        tevent_req_done(req);
+        return;
+    }
+
+    talloc_free(state->update_msg);
+    ret = sdap_dyndns_update_ptr_step(req);
+    if (ret != EOK) {
+        tevent_req_error(req, ret);
+        return;
+    }
+    /* Execution will resume in sdap_dyndns_update_ptr_done */
+}
+
+static errno_t
+sdap_dyndns_update_ptr_step(struct tevent_req *req)
+{
+    errno_t ret;
+    struct sdap_dyndns_update_state *state;
+    const char *servername;
+    struct tevent_req *subreq;
+
+    state = tevent_req_data(req, struct sdap_dyndns_update_state);
+
+    servername = NULL;
+    if (state->use_server_with_nsupdate == true &&
+        state->servername) {
+        servername = state->servername;
+    }
+
+    ret = be_nsupdate_create_ptr_msg(state, state->realm,
+                                     servername, state->hostname,
+                                     state->ttl, state->remove_af,
+                                     state->addresses, state->dns_addrlist,
+                                     &state->update_msg);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_OP_FAILURE, ("Can't get addresses for DNS update\n"));
+        return ret;
+    }
+
+    /* Fork a child process to perform the DNS update */
+    subreq = be_nsupdate_send(state, state->ev, state->auth_type,
+                              state->update_msg,
+                              dp_opt_get_bool(state->opts,
+                                              DP_OPT_DYNDNS_USE_TCP));
+    if (subreq == NULL) {
+        return EIO;
+    }
+
+    tevent_req_set_callback(subreq, sdap_dyndns_update_ptr_done, req);
+    return EOK;
+}
+
+static void
+sdap_dyndns_update_ptr_done(struct tevent_req *subreq)
+{
+    errno_t ret;
+    int child_status;
+    struct tevent_req *req;
+    struct sdap_dyndns_update_state *state;
+
+    req = tevent_req_callback_data(subreq, struct tevent_req);
+    state = tevent_req_data(req, struct sdap_dyndns_update_state);
+
+    ret = be_nsupdate_recv(subreq, &child_status);
+    talloc_zfree(subreq);
+    if (ret != EOK) {
+        /* If the update didn't succeed, we can retry using the server name */
+        if (state->use_server_with_nsupdate == false && state->servername &&
+            WIFEXITED(child_status) && WEXITSTATUS(child_status) != 0) {
+            state->use_server_with_nsupdate = true;
+            DEBUG(SSSDBG_MINOR_FAILURE,
+                   ("nsupdate failed, retrying with server name\n"));
+            ret = sdap_dyndns_update_ptr_step(req);
+            if (ret == EOK) {
+                return;
+            }
+        }
+
+        tevent_req_error(req, ret);
+        return;
+    }
+
     tevent_req_done(req);
 }
 
diff --git a/src/providers/ldap/sdap_dyndns.h b/src/providers/ldap/sdap_dyndns.h
index 1602938e17d01f30a99bae37a6d496643281a680..a18a71fc9d62d2de8f9cc574cf376a959d7a4c99 100644
--- a/src/providers/ldap/sdap_dyndns.h
+++ b/src/providers/ldap/sdap_dyndns.h
@@ -33,6 +33,7 @@ struct tevent_req *
 sdap_dyndns_update_send(TALLOC_CTX *mem_ctx,
                         struct tevent_context *ev,
                         struct be_ctx *be_ctx,
+                        struct dp_option *opts,
                         struct sdap_id_ctx *sdap_ctx,
                         const char *ifname,
                         const char *hostname,
diff --git a/src/resolv/async_resolv.c b/src/resolv/async_resolv.c
index f673a25c322b713e9a2e5bd6908ce1cf0e0420cd..1eb0acf83cb5bb48d92a58b8baf872f4ca4d8278 100644
--- a/src/resolv/async_resolv.c
+++ b/src/resolv/async_resolv.c
@@ -1453,8 +1453,8 @@ resolv_get_string_ptr_address(TALLOC_CTX *mem_ctx,
 }
 
 struct sockaddr_storage *
-resolv_get_sockaddr_address(TALLOC_CTX *mem_ctx, struct resolv_hostent *hostent,
-                            int port)
+resolv_get_sockaddr_address_index(TALLOC_CTX *mem_ctx, struct resolv_hostent *hostent,
+                                  int port, int index)
 {
     struct sockaddr_storage *sockaddr;
 
diff --git a/src/resolv/async_resolv.h b/src/resolv/async_resolv.h
index d759a82f37fa8bebc5d6b4b6d9a65e935e4cbb52..9bf5e0c40e44c385858fa4d85adebe6e022ca006 100644
--- a/src/resolv/async_resolv.h
+++ b/src/resolv/async_resolv.h
@@ -129,8 +129,11 @@ resolv_get_string_ptr_address(TALLOC_CTX *mem_ctx,
         resolv_get_string_address_index(mem_ctx, hostent, 0)
 
 struct sockaddr_storage *
-resolv_get_sockaddr_address(TALLOC_CTX *mem_ctx, struct resolv_hostent *hostent,
-                            int port);
+resolv_get_sockaddr_address_index(TALLOC_CTX *mem_ctx, struct resolv_hostent *hostent,
+                                  int port, int index);
+
+#define resolv_get_sockaddr_address(mem_ctx, rhostent, port) \
+        resolv_get_sockaddr_address_index(mem_ctx, rhostent, port, 0)
 
 /** Get SRV record **/
 struct tevent_req *resolv_getsrv_send(TALLOC_CTX *mem_ctx,
-- 
1.8.2.1

-------------- next part --------------
>From bef5b9e3c9491cad607bfb89f0179cb1495d1708 Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhrozek at redhat.com>
Date: Tue, 16 Apr 2013 14:19:15 +0200
Subject: [PATCH 4/5] dyndns: new option dyndns_force_tcp

https://fedorahosted.org/sssd/ticket/1831

Adds a new option that can be used to force nsupdate to only use TCP to
communicate with the DNS server.
---
 src/config/SSSDConfig/__init__.py.in |  1 +
 src/config/SSSDConfigTest.py         |  2 ++
 src/config/etc/sssd.api.conf         |  1 +
 src/man/sssd-ipa.5.xml               | 13 +++++++++++++
 src/providers/dp_dyndns.c            | 18 +++++++++++++++---
 src/providers/dp_dyndns.h            |  4 +++-
 src/providers/ipa/ipa_opts.h         |  1 +
 src/providers/ldap/sdap_dyndns.c     |  6 ++++--
 src/tests/cmocka/test_dyndns.c       |  6 +++---
 9 files changed, 43 insertions(+), 9 deletions(-)

diff --git a/src/config/SSSDConfig/__init__.py.in b/src/config/SSSDConfig/__init__.py.in
index 79bf6aa4e97ed75391f97472a780d6349fd1060a..c5475ffd8b599d3f60d7f1c992847d3ce00847b7 100644
--- a/src/config/SSSDConfig/__init__.py.in
+++ b/src/config/SSSDConfig/__init__.py.in
@@ -130,6 +130,7 @@ option_strings = {
     '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"),
     'dyndns_update_ptr' : _("Whether the provider should explicitly update the PTR record as well"),
+    'dyndns_force_tcp' : _("Whether the nsupdate utility should default to using TCP"),
 
     # [provider/ipa]
     'ipa_domain' : _('IPA domain'),
diff --git a/src/config/SSSDConfigTest.py b/src/config/SSSDConfigTest.py
index 86ff29df1f8225a846903ad6c61c2a6147c9bc69..13418b90669a881721cf172314c0674afa77ec2c 100755
--- a/src/config/SSSDConfigTest.py
+++ b/src/config/SSSDConfigTest.py
@@ -512,6 +512,7 @@ class SSSDConfigTestSSSDDomain(unittest.TestCase):
             'dyndns_iface',
             'dyndns_refresh_interval',
             'dyndns_update_ptr',
+            'dyndns_force_tcp',
             'override_gid',
             'case_sensitive',
             'override_homedir',
@@ -860,6 +861,7 @@ class SSSDConfigTestSSSDDomain(unittest.TestCase):
             'dyndns_iface',
             'dyndns_refresh_interval',
             'dyndns_update_ptr',
+            'dyndns_force_tcp',
             'override_gid',
             'case_sensitive',
             'override_homedir',
diff --git a/src/config/etc/sssd.api.conf b/src/config/etc/sssd.api.conf
index 396063b4fd9fe08e65860bd24a68350401a0d7f0..013fa9fe5a38bdd5c3dd1aa648287c56a0aa4211 100644
--- a/src/config/etc/sssd.api.conf
+++ b/src/config/etc/sssd.api.conf
@@ -127,6 +127,7 @@ dyndns_ttl = int, None, false
 dyndns_iface = str, None, false
 dyndns_refresh_interval = int, None, false
 dyndns_update_ptr = bool, None, false
+dyndns_force_tcp = bool, None, false
 
 # Special providers
 [provider/permit]
diff --git a/src/man/sssd-ipa.5.xml b/src/man/sssd-ipa.5.xml
index 21a28047969043e1abb8f54a708fddc7727d8073..795a72237dee7093e790135244ce0ff9baf7e61b 100644
--- a/src/man/sssd-ipa.5.xml
+++ b/src/man/sssd-ipa.5.xml
@@ -240,6 +240,19 @@
                 </varlistentry>
 
                 <varlistentry>
+                    <term>dyndns_force_tcp (bool)</term>
+                    <listitem>
+                        <para>
+                            Whether the nsupdate utility should default to using
+                            TCP for communicating with the DNS server.
+                        </para>
+                        <para>
+                            Default: False (let nsupdate choose the protocol)
+                        </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 79701c6294c595c33e69f978fe044a7fdaf5a9c6..36cce458216b51ab6a9b233b6c7dde29705828eb 100644
--- a/src/providers/dp_dyndns.c
+++ b/src/providers/dp_dyndns.c
@@ -935,7 +935,8 @@ static void be_nsupdate_done(struct tevent_req *subreq);
 
 struct tevent_req *be_nsupdate_send(TALLOC_CTX *mem_ctx,
                                     struct tevent_context *ev,
-                                    char *nsupdate_msg)
+                                    char *nsupdate_msg,
+                                    bool force_tcp)
 {
     int pipefd_to_child[2];
     pid_t child_pid;
@@ -943,7 +944,7 @@ struct tevent_req *be_nsupdate_send(TALLOC_CTX *mem_ctx,
     struct tevent_req *req = NULL;
     struct tevent_req *subreq = NULL;
     struct be_nsupdate_state *state;
-    char *args[3];
+    char *args[4];
 
     req = tevent_req_create(mem_ctx, &state, struct be_nsupdate_state);
     if (req == NULL) {
@@ -962,14 +963,24 @@ struct tevent_req *be_nsupdate_send(TALLOC_CTX *mem_ctx,
     child_pid = fork();
 
     if (child_pid == 0) { /* child */
+        memset(args, 0, 4 * sizeof(char *));
+
         args[0] = talloc_strdup(state, NSUPDATE_PATH);
         args[1] = talloc_strdup(state, "-g");
-        args[2] = NULL;
         if (args[0] == NULL || args[1] == NULL) {
             ret = ENOMEM;
             goto done;
         }
 
+        if (force_tcp) {
+            DEBUG(SSSDBG_FUNC_DATA, ("TCP is set to on\n"));
+            args[2] = talloc_strdup(state, "-v");
+            if (args[2] == NULL) {
+                ret = ENOMEM;
+                goto done;
+            }
+        }
+
         close(pipefd_to_child[1]);
         ret = dup2(pipefd_to_child[0], STDIN_FILENO);
         if (ret == -1) {
@@ -1117,6 +1128,7 @@ static struct dp_option default_dyndns_opts[] = {
     { "dyndns_iface", DP_OPT_STRING, NULL_STRING, NULL_STRING },
     { "dyndns_ttl", DP_OPT_NUMBER, { .number = 1200 }, NULL_NUMBER },
     { "dyndns_update_ptr", DP_OPT_BOOL, BOOL_TRUE, BOOL_FALSE },
+    { "dyndns_force_tcp", DP_OPT_BOOL, BOOL_FALSE, BOOL_FALSE },
 
     DP_OPTION_TERMINATOR
 };
diff --git a/src/providers/dp_dyndns.h b/src/providers/dp_dyndns.h
index 8fdbe487b5a857cb02b13e099176ce07b27da0c4..a1e31e450c10f64abf8bc1ec86d408abfcd557e8 100644
--- a/src/providers/dp_dyndns.h
+++ b/src/providers/dp_dyndns.h
@@ -47,6 +47,7 @@ enum dp_dyndns_opts {
     DP_OPT_DYNDNS_IFACE,
     DP_OPT_DYNDNS_TTL,
     DP_OPT_DYNDNS_UPDATE_PTR,
+    DP_OPT_DYNDNS_FORCE_TCP,
 
     DP_OPT_DYNDNS /* attrs counter */
 };
@@ -103,7 +104,8 @@ be_nsupdate_create_ptr_msg(TALLOC_CTX *mem_ctx, const char *realm,
  */
 struct tevent_req *be_nsupdate_send(TALLOC_CTX *mem_ctx,
                                     struct tevent_context *ev,
-                                    char *nsupdate_msg);
+                                    char *nsupdate_msg,
+                                    bool force_tcp);
 errno_t be_nsupdate_recv(struct tevent_req *req, int *child_status);
 
 struct tevent_req * nsupdate_get_addrs_send(TALLOC_CTX *mem_ctx,
diff --git a/src/providers/ipa/ipa_opts.h b/src/providers/ipa/ipa_opts.h
index bfb09e36cb3562b61c5bdb1dd928c5b124bdd0ce..97dd6ea9fd1444ed1661240efc8e95773d160380 100644
--- a/src/providers/ipa/ipa_opts.h
+++ b/src/providers/ipa/ipa_opts.h
@@ -57,6 +57,7 @@ struct dp_option ipa_dyndns_opts[] = {
     { "dyndns_iface", DP_OPT_STRING, NULL_STRING, NULL_STRING },
     { "dyndns_ttl", DP_OPT_NUMBER, { .number = 1200 }, NULL_NUMBER },
     { "dyndns_update_ptr", DP_OPT_BOOL, BOOL_FALSE, BOOL_FALSE },
+    { "dyndns_force_tcp", DP_OPT_BOOL, BOOL_FALSE, BOOL_FALSE },
     DP_OPTION_TERMINATOR
 };
 
diff --git a/src/providers/ldap/sdap_dyndns.c b/src/providers/ldap/sdap_dyndns.c
index 8f3c4fa4a6171fa9a490384db0204db0a6b97983..3be34a490396058f8ccd237b733e41b0e4b63cdb 100644
--- a/src/providers/ldap/sdap_dyndns.c
+++ b/src/providers/ldap/sdap_dyndns.c
@@ -323,7 +323,9 @@ sdap_dyndns_update_step(struct tevent_req *req)
     }
 
     /* Fork a child process to perform the DNS update */
-    subreq = be_nsupdate_send(state, state->ev, state->update_msg);
+    subreq = be_nsupdate_send(state, state->ev, state->update_msg,
+                              dp_opt_get_bool(state->opts,
+                                              DP_OPT_DYNDNS_FORCE_TCP));
     if (subreq == NULL) {
         return EIO;
     }
@@ -407,7 +409,7 @@ sdap_dyndns_update_ptr_step(struct tevent_req *req)
     subreq = be_nsupdate_send(state, state->ev, state->auth_type,
                               state->update_msg,
                               dp_opt_get_bool(state->opts,
-                                              DP_OPT_DYNDNS_USE_TCP));
+                                              DP_OPT_DYNDNS_FORCE_TCP));
     if (subreq == NULL) {
         return EIO;
     }
diff --git a/src/tests/cmocka/test_dyndns.c b/src/tests/cmocka/test_dyndns.c
index 98daebf37ae308d3578296bc0d2e9298fa69854b..cb79df98fc136d1d552d22e6cd7832af20ab866b 100644
--- a/src/tests/cmocka/test_dyndns.c
+++ b/src/tests/cmocka/test_dyndns.c
@@ -210,7 +210,7 @@ void dyndns_test_ok(void **state)
     dyndns_test_ctx->state = MOCK_NSUPDATE_OK;
 
     req = be_nsupdate_send(tmp_ctx, dyndns_test_ctx->tctx->ev,
-                           discard_const("test message"));
+                           discard_const("test message"), false);
     assert_non_null(req);
     tevent_req_set_callback(req, dyndns_test_done, dyndns_test_ctx);
 
@@ -240,7 +240,7 @@ void dyndns_test_error(void **state)
     dyndns_test_ctx->state = MOCK_NSUPDATE_ERR;
 
     req = be_nsupdate_send(tmp_ctx, dyndns_test_ctx->tctx->ev,
-                           discard_const("test message"));
+                           discard_const("test message"), false);
     assert_non_null(req);
     tevent_req_set_callback(req, dyndns_test_done, dyndns_test_ctx);
 
@@ -270,7 +270,7 @@ void dyndns_test_timeout(void **state)
     dyndns_test_ctx->state = MOCK_NSUPDATE_TIMEOUT;
 
     req = be_nsupdate_send(tmp_ctx, dyndns_test_ctx->tctx->ev,
-                           discard_const("test message"));
+                           discard_const("test message"), false);
     assert_non_null(req);
     tevent_req_set_callback(req, dyndns_test_done, dyndns_test_ctx);
 
-- 
1.8.2.1

-------------- next part --------------
>From 31c2f5d4cb89d96f39d393add23c6f310c71a159 Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhrozek at redhat.com>
Date: Tue, 16 Apr 2013 15:11:38 +0200
Subject: [PATCH 5/5] dyndns: new option dyndns_auth

This options is mostly provided for future expansion. Currently it is
undocumented and both IPA and AD dynamic DNS updates default to
GSS-TSIG. Allowed values are GSS-TSIG and none.
---
 src/config/SSSDConfig/__init__.py.in |  1 +
 src/config/SSSDConfigTest.py         |  2 +
 src/config/etc/sssd.api.conf         |  1 +
 src/providers/dp_dyndns.c            | 94 ++++++++++++++++++++++++++++--------
 src/providers/dp_dyndns.h            |  8 +++
 src/providers/ipa/ipa_dyndns.c       |  1 +
 src/providers/ipa/ipa_opts.h         |  1 +
 src/providers/ldap/sdap_dyndns.c     |  6 ++-
 src/providers/ldap/sdap_dyndns.h     |  2 +
 src/tests/cmocka/test_dyndns.c       |  3 ++
 10 files changed, 99 insertions(+), 20 deletions(-)

diff --git a/src/config/SSSDConfig/__init__.py.in b/src/config/SSSDConfig/__init__.py.in
index c5475ffd8b599d3f60d7f1c992847d3ce00847b7..bc7bb0a781a64b264d350a7766cf45de869273cf 100644
--- a/src/config/SSSDConfig/__init__.py.in
+++ b/src/config/SSSDConfig/__init__.py.in
@@ -131,6 +131,7 @@ option_strings = {
     'dyndns_refresh_interval' : _("How often to periodically update the client's DNS entry"),
     'dyndns_update_ptr' : _("Whether the provider should explicitly update the PTR record as well"),
     'dyndns_force_tcp' : _("Whether the nsupdate utility should default to using TCP"),
+    'dyndns_auth' : _("What kind of authentication should be used to perform the DNS update"),
 
     # [provider/ipa]
     'ipa_domain' : _('IPA domain'),
diff --git a/src/config/SSSDConfigTest.py b/src/config/SSSDConfigTest.py
index 13418b90669a881721cf172314c0674afa77ec2c..237db070d61ed29f9254160eafe74c8e5f00dbb2 100755
--- a/src/config/SSSDConfigTest.py
+++ b/src/config/SSSDConfigTest.py
@@ -513,6 +513,7 @@ class SSSDConfigTestSSSDDomain(unittest.TestCase):
             'dyndns_refresh_interval',
             'dyndns_update_ptr',
             'dyndns_force_tcp',
+            'dyndns_auth',
             'override_gid',
             'case_sensitive',
             'override_homedir',
@@ -862,6 +863,7 @@ class SSSDConfigTestSSSDDomain(unittest.TestCase):
             'dyndns_refresh_interval',
             'dyndns_update_ptr',
             'dyndns_force_tcp',
+            'dyndns_auth',
             'override_gid',
             'case_sensitive',
             'override_homedir',
diff --git a/src/config/etc/sssd.api.conf b/src/config/etc/sssd.api.conf
index 013fa9fe5a38bdd5c3dd1aa648287c56a0aa4211..54cd703e34eb11ac5f11d5ed344b1ac3102c57a0 100644
--- a/src/config/etc/sssd.api.conf
+++ b/src/config/etc/sssd.api.conf
@@ -128,6 +128,7 @@ dyndns_iface = str, None, false
 dyndns_refresh_interval = int, None, false
 dyndns_update_ptr = bool, None, false
 dyndns_force_tcp = bool, None, false
+dyndns_auth = str, None, false
 
 # Special providers
 [provider/permit]
diff --git a/src/providers/dp_dyndns.c b/src/providers/dp_dyndns.c
index 36cce458216b51ab6a9b233b6c7dde29705828eb..a7bf54f9b0fc9b33caf2de9d838ea0b2da6b0c42 100644
--- a/src/providers/dp_dyndns.c
+++ b/src/providers/dp_dyndns.c
@@ -932,9 +932,13 @@ struct be_nsupdate_state {
 };
 
 static void be_nsupdate_done(struct tevent_req *subreq);
+static char **be_nsupdate_args(TALLOC_CTX *mem_ctx,
+                               enum be_nsupdate_auth auth_type,
+                               bool force_tcp);
 
 struct tevent_req *be_nsupdate_send(TALLOC_CTX *mem_ctx,
                                     struct tevent_context *ev,
+                                    enum be_nsupdate_auth auth_type,
                                     char *nsupdate_msg,
                                     bool force_tcp)
 {
@@ -944,7 +948,7 @@ struct tevent_req *be_nsupdate_send(TALLOC_CTX *mem_ctx,
     struct tevent_req *req = NULL;
     struct tevent_req *subreq = NULL;
     struct be_nsupdate_state *state;
-    char *args[4];
+    char **args;
 
     req = tevent_req_create(mem_ctx, &state, struct be_nsupdate_state);
     if (req == NULL) {
@@ -963,24 +967,6 @@ struct tevent_req *be_nsupdate_send(TALLOC_CTX *mem_ctx,
     child_pid = fork();
 
     if (child_pid == 0) { /* child */
-        memset(args, 0, 4 * sizeof(char *));
-
-        args[0] = talloc_strdup(state, NSUPDATE_PATH);
-        args[1] = talloc_strdup(state, "-g");
-        if (args[0] == NULL || args[1] == NULL) {
-            ret = ENOMEM;
-            goto done;
-        }
-
-        if (force_tcp) {
-            DEBUG(SSSDBG_FUNC_DATA, ("TCP is set to on\n"));
-            args[2] = talloc_strdup(state, "-v");
-            if (args[2] == NULL) {
-                ret = ENOMEM;
-                goto done;
-            }
-        }
-
         close(pipefd_to_child[1]);
         ret = dup2(pipefd_to_child[0], STDIN_FILENO);
         if (ret == -1) {
@@ -990,6 +976,12 @@ struct tevent_req *be_nsupdate_send(TALLOC_CTX *mem_ctx,
             goto done;
         }
 
+        args = be_nsupdate_args(state, auth_type, force_tcp);
+        if (args == NULL) {
+            ret = ENOMEM;
+            goto done;
+        }
+
         errno = 0;
         execv(NSUPDATE_PATH, args);
         /* The child should never end up here */
@@ -1022,6 +1014,58 @@ done:
     return req;
 }
 
+static char **
+be_nsupdate_args(TALLOC_CTX *mem_ctx,
+                 enum be_nsupdate_auth auth_type,
+                 bool force_tcp)
+{
+    char **argv;
+    int argc = 0;
+
+    argv = talloc_zero_array(mem_ctx, char *, 4);
+    if (argv == NULL) {
+        return NULL;
+    }
+
+    argv[argc] = talloc_strdup(argv, NSUPDATE_PATH);
+    if (argv[argc] == NULL) {
+        goto fail;
+    }
+    argc++;
+
+    switch (auth_type) {
+    case BE_NSUPDATE_AUTH_NONE:
+        DEBUG(SSSDBG_FUNC_DATA, ("nsupdate auth type: none\n"));
+        break;
+    case BE_NSUPDATE_AUTH_GSS_TSIG:
+        DEBUG(SSSDBG_FUNC_DATA, ("nsupdate auth type: GSS-TSIG\n"));
+        argv[argc] = talloc_strdup(argv, "-g");
+        if (argv[argc] == NULL) {
+            goto fail;
+        }
+        argc++;
+        break;
+    default:
+        DEBUG(SSSDBG_CRIT_FAILURE, ("Unknown nsupdate auth type\n"));
+        goto fail;
+    }
+
+    if (force_tcp) {
+        DEBUG(SSSDBG_FUNC_DATA, ("TCP is set to on\n"));
+        argv[argc] = talloc_strdup(argv, "-v");
+        if (argv[argc] == NULL) {
+            goto fail;
+        }
+        argc++;
+    }
+
+    return argv;
+
+fail:
+    talloc_free(argv);
+    return NULL;
+}
+
 static void
 be_nsupdate_done(struct tevent_req *subreq)
 {
@@ -1129,6 +1173,7 @@ static struct dp_option default_dyndns_opts[] = {
     { "dyndns_ttl", DP_OPT_NUMBER, { .number = 1200 }, NULL_NUMBER },
     { "dyndns_update_ptr", DP_OPT_BOOL, BOOL_TRUE, BOOL_FALSE },
     { "dyndns_force_tcp", DP_OPT_BOOL, BOOL_FALSE, BOOL_FALSE },
+    { "dyndns_auth", DP_OPT_STRING, { "gss-tsig" }, NULL_STRING },
 
     DP_OPTION_TERMINATOR
 };
@@ -1143,6 +1188,7 @@ be_nsupdate_init(TALLOC_CTX *mem_ctx, struct be_ctx *be_ctx,
     errno_t ret;
     struct dp_option *src_opts;
     struct be_nsupdate_ctx *ctx;
+    char *strauth;
 
     ctx = talloc_zero(mem_ctx, struct be_nsupdate_ctx);
     if (ctx == NULL) return ENOMEM;
@@ -1156,6 +1202,16 @@ be_nsupdate_init(TALLOC_CTX *mem_ctx, struct be_ctx *be_ctx,
         return ret;
     }
 
+    strauth = dp_opt_get_string(ctx->opts, DP_OPT_DYNDNS_AUTH);
+    if (strcasecmp(strauth, "gss-tsig") == 0) {
+        ctx->auth_type = BE_NSUPDATE_AUTH_GSS_TSIG;
+    } else if (strcasecmp(strauth, "none") == 0) {
+        ctx->auth_type = BE_NSUPDATE_AUTH_NONE;
+    } else {
+        DEBUG(SSSDBG_OP_FAILURE, ("Uknown dyndns auth type %s\n", strauth));
+        return EINVAL;
+    }
+
     ctx->timer_callback = timer_callback;
     ctx->timer_pvt = timer_pvt;
     be_nsupdate_timer_schedule(be_ctx->ev, ctx);
diff --git a/src/providers/dp_dyndns.h b/src/providers/dp_dyndns.h
index a1e31e450c10f64abf8bc1ec86d408abfcd557e8..14b2dd08b03a422b2c0bc5bc5b184baf54491bca 100644
--- a/src/providers/dp_dyndns.h
+++ b/src/providers/dp_dyndns.h
@@ -31,8 +31,14 @@ struct sss_iface_addr;
 
 typedef void (*nsupdate_timer_fn_t)(void *pvt);
 
+enum be_nsupdate_auth {
+    BE_NSUPDATE_AUTH_NONE,
+    BE_NSUPDATE_AUTH_GSS_TSIG,
+};
+
 struct be_nsupdate_ctx {
     struct dp_option *opts;
+    enum be_nsupdate_auth auth_type;
 
     time_t last_refresh;
     bool timer_in_progress;
@@ -48,6 +54,7 @@ enum dp_dyndns_opts {
     DP_OPT_DYNDNS_TTL,
     DP_OPT_DYNDNS_UPDATE_PTR,
     DP_OPT_DYNDNS_FORCE_TCP,
+    DP_OPT_DYNDNS_AUTH,
 
     DP_OPT_DYNDNS /* attrs counter */
 };
@@ -104,6 +111,7 @@ be_nsupdate_create_ptr_msg(TALLOC_CTX *mem_ctx, const char *realm,
  */
 struct tevent_req *be_nsupdate_send(TALLOC_CTX *mem_ctx,
                                     struct tevent_context *ev,
+                                    enum be_nsupdate_auth auth_type,
                                     char *nsupdate_msg,
                                     bool force_tcp);
 errno_t be_nsupdate_recv(struct tevent_req *req, int *child_status);
diff --git a/src/providers/ipa/ipa_dyndns.c b/src/providers/ipa/ipa_dyndns.c
index bb38d105f41b46cf26f0f11c399a23f2a9642a92..b752f116f5149f1c576a017afd1091a225d8277a 100644
--- a/src/providers/ipa/ipa_dyndns.c
+++ b/src/providers/ipa/ipa_dyndns.c
@@ -245,6 +245,7 @@ ipa_dyndns_update_send(struct ipa_options *ctx)
                                      sdap_ctx->be,
                                      ctx->dyndns_ctx->opts,
                                      sdap_ctx,
+                                     ctx->dyndns_ctx->auth_type,
                                      dp_opt_get_string(ctx->dyndns_ctx->opts,
                                                        DP_OPT_DYNDNS_IFACE),
                                      dp_opt_get_string(ctx->basic,
diff --git a/src/providers/ipa/ipa_opts.h b/src/providers/ipa/ipa_opts.h
index 97dd6ea9fd1444ed1661240efc8e95773d160380..de9592b85c50ced32b7ab1c19c5f59eef89d6354 100644
--- a/src/providers/ipa/ipa_opts.h
+++ b/src/providers/ipa/ipa_opts.h
@@ -58,6 +58,7 @@ struct dp_option ipa_dyndns_opts[] = {
     { "dyndns_ttl", DP_OPT_NUMBER, { .number = 1200 }, NULL_NUMBER },
     { "dyndns_update_ptr", DP_OPT_BOOL, BOOL_FALSE, BOOL_FALSE },
     { "dyndns_force_tcp", DP_OPT_BOOL, BOOL_FALSE, BOOL_FALSE },
+    { "dyndns_auth", DP_OPT_STRING, { "gss-tsig" }, NULL_STRING },
     DP_OPTION_TERMINATOR
 };
 
diff --git a/src/providers/ldap/sdap_dyndns.c b/src/providers/ldap/sdap_dyndns.c
index 3be34a490396058f8ccd237b733e41b0e4b63cdb..fbeb6a3d4fd5a53c70584daf4e40f74d8f9799e4 100644
--- a/src/providers/ldap/sdap_dyndns.c
+++ b/src/providers/ldap/sdap_dyndns.c
@@ -57,6 +57,7 @@ struct sdap_dyndns_update_state {
 
     bool update_ptr;
     bool check_diff;
+    enum be_nsupdate_auth auth_type;
     bool use_server_with_nsupdate;
     char *update_msg;
 };
@@ -76,6 +77,7 @@ sdap_dyndns_update_send(TALLOC_CTX *mem_ctx,
                         struct be_ctx *be_ctx,
                         struct dp_option *opts,
                         struct sdap_id_ctx *sdap_ctx,
+                        enum be_nsupdate_auth auth_type,
                         const char *ifname,
                         const char *hostname,
                         const char *dns_zone,
@@ -104,6 +106,7 @@ sdap_dyndns_update_send(TALLOC_CTX *mem_ctx,
     state->be_res = be_ctx->be_res;
     state->ev = ev;
     state->opts = opts;
+    state->auth_type = auth_type;
 
     if (ifname) {
        /* Unless one family is restricted, just replace all
@@ -323,7 +326,8 @@ sdap_dyndns_update_step(struct tevent_req *req)
     }
 
     /* Fork a child process to perform the DNS update */
-    subreq = be_nsupdate_send(state, state->ev, state->update_msg,
+    subreq = be_nsupdate_send(state, state->ev, state->auth_type,
+                              state->update_msg,
                               dp_opt_get_bool(state->opts,
                                               DP_OPT_DYNDNS_FORCE_TCP));
     if (subreq == NULL) {
diff --git a/src/providers/ldap/sdap_dyndns.h b/src/providers/ldap/sdap_dyndns.h
index a18a71fc9d62d2de8f9cc574cf376a959d7a4c99..66de64a59c9b5408641b7f4c32a3e1dc814c80af 100644
--- a/src/providers/ldap/sdap_dyndns.h
+++ b/src/providers/ldap/sdap_dyndns.h
@@ -27,6 +27,7 @@
 
 #include "util/util.h"
 #include "providers/dp_backend.h"
+#include "providers/dp_dyndns.h"
 #include "providers/ldap/ldap_common.h"
 
 struct tevent_req *
@@ -35,6 +36,7 @@ sdap_dyndns_update_send(TALLOC_CTX *mem_ctx,
                         struct be_ctx *be_ctx,
                         struct dp_option *opts,
                         struct sdap_id_ctx *sdap_ctx,
+                        enum be_nsupdate_auth auth_type,
                         const char *ifname,
                         const char *hostname,
                         const char *dns_zone,
diff --git a/src/tests/cmocka/test_dyndns.c b/src/tests/cmocka/test_dyndns.c
index cb79df98fc136d1d552d22e6cd7832af20ab866b..9b3d636c54539926433d323562a7d620d94a15f3 100644
--- a/src/tests/cmocka/test_dyndns.c
+++ b/src/tests/cmocka/test_dyndns.c
@@ -210,6 +210,7 @@ void dyndns_test_ok(void **state)
     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);
@@ -240,6 +241,7 @@ void dyndns_test_error(void **state)
     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);
@@ -270,6 +272,7 @@ void dyndns_test_timeout(void **state)
     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);
-- 
1.8.2.1



More information about the sssd-devel mailing list