[SSSD] [PATCH] New dynamic DNS update options

Jakub Hrozek jhrozek at redhat.com
Wed Apr 17 19:03:51 UTC 2013


Hi,

the attached patches implement a couple of new dynamic DNS options. The
AD dyndns code will be just a wrapper around these options.

[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.

There is one place (in IPA dyndns code in this patch but also in AD code
later on) that I wanted to discuss specifically. It may happen that the
periodic update would trigger going online in which case the online
callback would fire and another dyndns update would be invoked as an
online callback. To prevent a race between these two updates, there is
an interval, currently hardcoded to 60 seconds that would just make the
next update quit without doing anything. Ideas on how to fix the problem
without a hardcoded timeout are welcome.

[PATCH 2/5] resolver: Return PTR record as string
Having the possibility to format a PTR record based on an A/AAAA record
is a requirement to update the PTR records.
Includes a unit test.

[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.

The PTR update is performed separately from the forward record update
mostly because the current IPA dyndns code allows the zone to be
specified in the message, so another zone must be updated using another
message.

This option is off by default in the IPA provider.

[PATCH 4/5] dyndns: new option dyndns_use_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.

[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.
-------------- next part --------------
>From 09ec0bb3bc2e0d572c4e3aba6510cd8bee6cf350 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       | 106 +++++++++++++++++++++++++++++++++++
 src/providers/ipa/ipa_dyndns.h       |   3 +
 src/providers/ipa/ipa_opts.h         |   1 +
 src/tests/cmocka/test_dyndns.c       |  85 +++++++++++++++++++++++++++-
 12 files changed, 285 insertions(+), 7 deletions(-)

diff --git a/Makefile.am b/Makefile.am
index 9161f26a577ce13bb4263a50c7c4a24cb12772ae..f57279d75f131995ab25d3257ca1dc88b13a4c42 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1254,10 +1254,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 282d65293c853bb8ddd1a89334a827708a2737d0..e281d58170dc5a0fea78dbe0129566e24fe2a50e 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 f819d9f41d656d023fc682606a16bbcbd40bb244..8ecbe5409f436d21d380cd1b45da64dba6cf471b 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',
@@ -854,6 +855,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..3c4ec52c935624b800ba85b785646e7d1d505890 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
+                            becomes 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 b26eb14c5ebac25e4616e12c36aebfd4e0bba70e..bcf6e9e97e424abbf2f3779b0d3f53027de6553a 100644
--- a/src/providers/dp_dyndns.c
+++ b/src/providers/dp_dyndns.c
@@ -867,6 +867,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)
 {
@@ -894,6 +933,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 },
 
@@ -902,13 +942,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;
@@ -920,6 +963,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..6643fb34aeaae8c852fd2dc06911084355519cb8 100644
--- a/src/providers/ipa/ipa_dyndns.c
+++ b/src/providers/ipa/ipa_dyndns.c
@@ -55,6 +55,97 @@ 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) {
+        /* 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 +154,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 +205,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 936407e04d25035b89c1cbe53858cf10943d799f..6b6e6db94a16b331696e1a666a41d2069abbd3f6 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.1.4

-------------- next part --------------
>From 1e0630ee9bc29bcf8c42e8f38eb12ceeb2faea83 Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhrozek at redhat.com>
Date: Thu, 11 Apr 2013 11:50:13 +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 |  42 +++++++++++++++++++
 src/resolv/async_resolv.h |   4 ++
 src/tests/resolv-tests.c  | 104 ++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 150 insertions(+)

diff --git a/src/resolv/async_resolv.c b/src/resolv/async_resolv.c
index 60d9e05bfa61f1490263a3defa21cab1c709c5ac..3ccae6cc97ba64064322132c5bd538134918e328 100644
--- a/src/resolv/async_resolv.c
+++ b/src/resolv/async_resolv.c
@@ -1416,6 +1416,48 @@ 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;
+    static char hex_digits[] = {
+                '0', '1', '2', '3', '4', '5', '6', '7',
+                '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
+    };
+
+    if (family == AF_INET6) {
+        char *cp;
+        int i;
+
+        straddr = talloc_zero_array(mem_ctx, char, 128);
+        if (!straddr) {
+            return NULL;
+        }
+
+        cp = straddr;
+        for (i = 15; i >= 0; i--) {
+                *cp++ = hex_digits[address[i] & 0x0f];
+                *cp++ = '.';
+                *cp++ = hex_digits[(address[i] >> 4) & 0x0f];
+                *cp++ = '.';
+        }
+        strcpy(cp, "ip6.arpa.");
+    } else if (family == AF_INET) {
+        straddr = talloc_asprintf(mem_ctx,
+                                  "%u.%u.%u.%u.in-addr.arpa.",
+                                  (address[3] & 0xff),
+                                  (address[2] & 0xff),
+                                  (address[1] & 0xff),
+                                  (address[0] & 0xff));
+    } 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.1.4

-------------- next part --------------
>From 0ae07d707922baca2dfc5bee88ff7ecb91bae2cd Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhrozek at redhat.com>
Date: Tue, 9 Apr 2013 23:26:24 +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.
---
 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            | 302 ++++++++++++++++++++++++++---------
 src/providers/dp_dyndns.h            |  18 ++-
 src/providers/ipa/ipa_dyndns.c       |   4 +-
 src/providers/ipa/ipa_opts.h         |   1 +
 src/providers/ldap/sdap_dyndns.c     | 209 +++++++++++++++++++-----
 src/providers/ldap/sdap_dyndns.h     |   1 +
 src/resolv/async_resolv.c            |   4 +-
 src/resolv/async_resolv.h            |   7 +-
 12 files changed, 445 insertions(+), 125 deletions(-)

diff --git a/src/config/SSSDConfig/__init__.py.in b/src/config/SSSDConfig/__init__.py.in
index e281d58170dc5a0fea78dbe0129566e24fe2a50e..0eae53e59fa07b8bf158f7371561d707065ec6bc 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 8ecbe5409f436d21d380cd1b45da64dba6cf471b..628b3534c6e57ef919c5b213efbf5d9675107435 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',
@@ -856,6 +857,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 3c4ec52c935624b800ba85b785646e7d1d505890..9b996d4523424cdb09af8823f4237ec2f07c60c9 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 bcf6e9e97e424abbf2f3779b0d3f53027de6553a..c74ef88588becbc00fe7eed5a777f64d2085c41c 100644
--- a/src/providers/dp_dyndns.c
+++ b/src/providers/dp_dyndns.c
@@ -257,57 +257,13 @@ 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;
-
-    /* in some cases realm could have been NULL if we weren't using TSIG */
-    if (zone == NULL || hostname == NULL) {
-        return EINVAL;
-    }
-
-#ifdef HAVE_NSUPDATE_REALM
-    realm_directive = talloc_asprintf(mem_ctx, "realm %s\n", realm);
-#else
-    realm_directive = talloc_asprintf(mem_ctx, "");
-#endif
-    if (!realm_directive) {
-        ret = ENOMEM;
-        goto done;
-    }
-
-    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(mem_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(mem_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;
 
     /* Remove existing entries as needed */
     if (remove_af & DYNDNS_REMOVE_A) {
@@ -315,8 +271,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) {
@@ -324,8 +279,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;
         }
     }
 
@@ -336,8 +290,7 @@ be_nsupdate_create_msg(TALLOC_CTX *mem_ctx, const char *realm,
                            &(((struct sockaddr_in *)new_record->addr)->sin_addr),
                            ip_addr, INET6_ADDRSTRLEN);
             if (ip == NULL) {
-                ret = EIO;
-                goto done;
+                return NULL;
             }
             break;
 
@@ -346,15 +299,13 @@ be_nsupdate_create_msg(TALLOC_CTX *mem_ctx, const char *realm,
                            &(((struct sockaddr_in6 *)new_record->addr)->sin6_addr),
                            ip_addr, INET6_ADDRSTRLEN);
             if (ip == NULL) {
-                ret = EIO;
-                goto done;
+                return NULL;
             }
             break;
 
         default:
             DEBUG(SSSDBG_CRIT_FAILURE, ("Unknown address family\n"));
-            ret = EINVAL;
-            goto done;
+            return NULL;
         }
 
         /* Format the record update */
@@ -364,12 +315,203 @@ 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_add_common(TALLOC_CTX *mem_ctx, const char *realm,
+                        const char *servername)
+{
+    char *realm_directive;
+    char *update_msg;
+
+#ifdef HAVE_NSUPDATE_REALM
+    realm_directive = talloc_asprintf(mem_ctx, "realm %s\n", realm);
+#else
+    realm_directive = talloc_asprintf(mem_ctx, "");
+#endif
+    if (!realm_directive) {
+        return NULL;
+    }
+
+    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(mem_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(mem_ctx, "%s", realm_directive);
+    }
+    talloc_free(realm_directive);
+    if (update_msg == NULL) {
+        return NULL;
+    }
+
+    return update_msg;
+}
+
+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,
+                       struct sss_iface_addr *old_addresses,
+                       char **_update_msg)
+{
+    int 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_add_common(mem_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;
+    }
+
+    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;
+}
+
+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_add_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;
@@ -395,7 +537,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;
 };
 
@@ -457,6 +599,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;
 
@@ -512,25 +655,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) {
-        tevent_req_error(req, ENOMEM);
-        return;
-    }
-
     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) {
+            tevent_req_error(req, ENOMEM);
+            return;
+        }
 
-        if (state->addrlist[state->count + i] == NULL) {
+        addr->addr = resolv_get_sockaddr_address_index(addr, rhostent, 0, i);
+        if (addr == NULL) {
             tevent_req_error(req, ENOMEM);
             return;
         }
+
+        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
@@ -564,14 +707,22 @@ nsupdate_get_addrs_done(struct tevent_req *subreq)
 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;
 }
 
@@ -936,6 +1087,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..e1fea15b8b19121724ef82d051695828125dda67 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 */
 };
@@ -83,8 +84,17 @@ 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,
+                       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
  *    * ERR_DYNDNS_FAILED   - if nsupdate fails for any reason
@@ -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 6643fb34aeaae8c852fd2dc06911084355519cb8..24a55c180ea8ba8feb0b0d453a82f26995c19be7 100644
--- a/src/providers/ipa/ipa_dyndns.c
+++ b/src/providers/ipa/ipa_dyndns.c
@@ -241,7 +241,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 6b6e6db94a16b331696e1a666a41d2069abbd3f6..d26ec649377a747c432f29b28c0194973ddaffd9 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 d1d96904fcda3a3b4e07e9407990dbf97a555af6..a6c73fc9b4aab9b2c41337264e61f46b4a5b84d6 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
@@ -274,7 +315,7 @@ sdap_dyndns_update_step(struct tevent_req *req)
     ret = be_nsupdate_create_msg(state, state->realm, state->dns_zone,
                                  servername, state->hostname,
                                  state->ttl, state->remove_af,
-                                 state->addresses,
+                                 state->addresses, state->dns_addrlist,
                                  &state->update_msg);
     if (ret != EOK) {
         DEBUG(SSSDBG_OP_FAILURE, ("Can't get addresses for DNS update\n"));
@@ -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 3ccae6cc97ba64064322132c5bd538134918e328..341ace801953f07e7e1cd54fc1207f78b357e032 100644
--- a/src/resolv/async_resolv.c
+++ b/src/resolv/async_resolv.c
@@ -1459,8 +1459,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.1.4

-------------- next part --------------
>From d0e2eeb405dfcf21c16a479649bb6212d936700c 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_use_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     |  4 +++-
 src/tests/cmocka/test_dyndns.c       |  6 +++---
 9 files changed, 42 insertions(+), 8 deletions(-)

diff --git a/src/config/SSSDConfig/__init__.py.in b/src/config/SSSDConfig/__init__.py.in
index 0eae53e59fa07b8bf158f7371561d707065ec6bc..85a62680fe484b205baf6312a618ef70c7d25439 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_use_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 628b3534c6e57ef919c5b213efbf5d9675107435..fd71c7be34ebe9f061ba51340a5cc328761859a6 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_use_tcp',
             'override_gid',
             'case_sensitive',
             'override_homedir',
@@ -858,6 +859,7 @@ class SSSDConfigTestSSSDDomain(unittest.TestCase):
             'dyndns_iface',
             'dyndns_refresh_interval',
             'dyndns_update_ptr',
+            'dyndns_use_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..7305d59a8d63455069a6b3e321b87db4a20ccd71 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_use_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 9b996d4523424cdb09af8823f4237ec2f07c60c9..072326243c0782e5ac7eba1b89acdb3bc99421ee 100644
--- a/src/man/sssd-ipa.5.xml
+++ b/src/man/sssd-ipa.5.xml
@@ -240,6 +240,19 @@
                 </varlistentry>
 
                 <varlistentry>
+                    <term>dyndns_use_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 c74ef88588becbc00fe7eed5a777f64d2085c41c..8d4058c1cef2049749339c34da22cea6f2a72fc3 100644
--- a/src/providers/dp_dyndns.c
+++ b/src/providers/dp_dyndns.c
@@ -906,7 +906,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 use_tcp)
 {
     int pipefd_to_child[2];
     pid_t child_pid;
@@ -914,7 +915,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) {
@@ -933,14 +934,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 (use_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) {
@@ -1088,6 +1099,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_use_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 e1fea15b8b19121724ef82d051695828125dda67..bb3fc48b58494b30a2e4b5c1ed65e49d8d148cf2 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_USE_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 use_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 d26ec649377a747c432f29b28c0194973ddaffd9..b539ca5966236b906e093084f67424f9a8fde1b9 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_use_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 a6c73fc9b4aab9b2c41337264e61f46b4a5b84d6..9e0ee98624b5a3b0514068b8c4e0d6d0ec0c0222 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_USE_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.1.4

-------------- next part --------------
>From 65b442365efca1535d0d5a908f5717b1788349da 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 85a62680fe484b205baf6312a618ef70c7d25439..c21c7922a3a7ce09248506de797e2ef3405cb43d 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_use_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 fd71c7be34ebe9f061ba51340a5cc328761859a6..bc66c47a51a40a97d5e183c37a1d5ce7192595fd 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_use_tcp',
+            'dyndns_auth',
             'override_gid',
             'case_sensitive',
             'override_homedir',
@@ -860,6 +861,7 @@ class SSSDConfigTestSSSDDomain(unittest.TestCase):
             'dyndns_refresh_interval',
             'dyndns_update_ptr',
             'dyndns_use_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 7305d59a8d63455069a6b3e321b87db4a20ccd71..5dab3d2f0b4e41495e2d084999617fe6c36ed07b 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_use_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 8d4058c1cef2049749339c34da22cea6f2a72fc3..9e41631f40827d0744262f03d72932ee5646a937 100644
--- a/src/providers/dp_dyndns.c
+++ b/src/providers/dp_dyndns.c
@@ -903,9 +903,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 use_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 use_tcp)
 {
@@ -915,7 +919,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) {
@@ -934,24 +938,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 (use_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) {
@@ -961,6 +947,12 @@ struct tevent_req *be_nsupdate_send(TALLOC_CTX *mem_ctx,
             goto done;
         }
 
+        args = be_nsupdate_args(state, auth_type, use_tcp);
+        if (args == NULL) {
+            ret = ENOMEM;
+            goto done;
+        }
+
         errno = 0;
         execv(NSUPDATE_PATH, args);
         /* The child should never end up here */
@@ -993,6 +985,58 @@ done:
     return req;
 }
 
+static char **
+be_nsupdate_args(TALLOC_CTX *mem_ctx,
+                 enum be_nsupdate_auth auth_type,
+                 bool use_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 (use_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)
 {
@@ -1100,6 +1144,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_use_tcp", DP_OPT_BOOL, BOOL_FALSE, BOOL_FALSE },
+    { "dyndns_auth", DP_OPT_STRING, { "gss-tsig" }, NULL_STRING },
 
     DP_OPTION_TERMINATOR
 };
@@ -1114,6 +1159,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;
@@ -1127,6 +1173,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 bb3fc48b58494b30a2e4b5c1ed65e49d8d148cf2..91025e11056a51637cd5ae081d48b281c86c4172 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_USE_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 use_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 24a55c180ea8ba8feb0b0d453a82f26995c19be7..3cde10f67b9e4c17f95981b8b2dfa76ea6c51fdb 100644
--- a/src/providers/ipa/ipa_dyndns.c
+++ b/src/providers/ipa/ipa_dyndns.c
@@ -244,6 +244,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 b539ca5966236b906e093084f67424f9a8fde1b9..c4b4136269aa0be7f519b786d9c74723603229c2 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_use_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 9e0ee98624b5a3b0514068b8c4e0d6d0ec0c0222..04cf83872fde320db10bdfded8ea41a844a480b7 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_USE_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.1.4



More information about the sssd-devel mailing list