[SSSD] [PATCH] New dynamic DNS update options

Jakub Hrozek jhrozek at redhat.com
Tue Apr 30 15:51:02 UTC 2013


On Mon, Apr 22, 2013 at 02:04:03PM +0200, Pavel Březina wrote:
> On 04/17/2013 09:03 PM, Jakub Hrozek wrote:
> >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.
> 
> Hi,
> good job. I have just few comments inline.
> 
> >0001-dyndns-new-option-dyndns_refresh_interval.patch
> >
> 
> >                  <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.
> 
> goes online sounds better to me.

OK, fixed.

> 
> >+                            This option is optional and applicable only when dyndns_update
> >+                            is true.
> >+                        </para>
> >+                        <para>
> >+                            Default: 0 (disabled)
> >+                        </para>
> >+                    </listitem>
> >+                </varlistentry>
> 
> >+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 */
> 
> I agree there is not much we can do, but we can report it at least. :-)

Added a DEBUG message.

> 
> >
> >0002-resolver-Return-PTR-record-as-string.patch
> >+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++ = '.';
> >+        }
> 
> I'm not opposed this, but wouldn't it be better to use
> sprintf("%02x") instead of this bit wise magic? Something like:
> 
> char hexbyte[3];
> snprintf(hexbyte, 2, "%02x", address[i]);
> talloc_asprintf(straddr, "%c.%c.", hexbyte[1], hexbyte[0]);

OK, that's more readable.

> 
> >+        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));
> 
> address is already uint8_t so I believe applying 0xff is not necessary.

Removed the mask.

> 
> >+    } else {
> >+        DEBUG(SSSDBG_CRIT_FAILURE, ("Unknown address family\n"));
> >+        return NULL;
> >+    }
> >+
> >+    return straddr;
> >+}
> >+
> >
> >0003-dyndns-New-option-dyndns_update_ptr.patch
> 
> nsupdate_msg_add_common() should also take update_msg as parameter
> as do other add functions.
> 

No, the purpose is to create the update message, not add contents to
existing one. I renamed the function to nsupdate_msg_create_common.

> You are leaking update_msg in be_nsupdate_create_msg() and
> be_nsupdate_create_ptr_msg(). If one of the _add function or
> talloc_asprintf_append() fails, you have to free the original
> pointer.
> 
> I know it is all allocated on state so it will be freed later
> anyway, but still...
> 
> Using tmp_ctx and "done" pattern would be much better in these functions.
> 

OK, I went with tmp_ctx.

> Also please rename be_nsupdate_create_msg() to
> be_nsupdate_create_fwd_msg() to make it similar to
> be_nsupdate_create_ptr_msg().
> 

Renamed.

> Patch 4:
> I think dyndns_force_tcp would be a better name for the option.
> 

At first I didn't agree, but you're right that "force" more closely
describes that without this option, nsupdate might still opt for TCP
internally.

> Patch 5:
> Originally, I wanted to write that you shouldn't spend time on this
> patch, but I kinda like the new way of creating nsupdate args. So
> kudos :-)

Patch 5 is the same.
-------------- next part --------------
>From 1a8b23902c7b02b4c4704ce92ab1aae8acdcbb24 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 e4263f95de162f478a07be024a23b6bfe4c142ff..af8fe3de8d8cba0d0d4a3acff1cae8c75378e82e 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1258,10 +1258,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 c3bff6debfd9a522e38653bbcffbc7363f122815..3b3f2619d6e5765653c333906f9ca52798bf916d 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 b4dd936d1d58f7d64f04013a6ae20e749ee0c244..6ca8bdb2eaec2662b31efc7ab19c7753b2809d1e 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 fda33381912699bfdcf255bfe2b7794a66218a4f 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 |  40 ++++++++++++++++++
 src/resolv/async_resolv.h |   4 ++
 src/tests/resolv-tests.c  | 104 ++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 148 insertions(+)

diff --git a/src/resolv/async_resolv.c b/src/resolv/async_resolv.c
index 60d9e05bfa61f1490263a3defa21cab1c709c5ac..b56a68da79fd25b775da8e19702999b1c3cfb57b 100644
--- a/src/resolv/async_resolv.c
+++ b/src/resolv/async_resolv.c
@@ -1416,6 +1416,46 @@ 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) {
+        int i;
+        char hexbyte[3];
+
+        straddr = talloc_asprintf(mem_ctx, "");
+        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 0c112afce8a697eddbfa54b8ab1d1e46b5b46af4 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 3b3f2619d6e5765653c333906f9ca52798bf916d..720cbdb5c0b31ef25425f3ca6f90de7ac81f9ca0 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 6ca8bdb2eaec2662b31efc7ab19c7753b2809d1e..6f143dae1cc6585d00c7c95de012c3e260d9fb7f 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 d1d96904fcda3a3b4e07e9407990dbf97a555af6..59dccfbbcedc5cfd20ec80237d70cd4466b0a569 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 b56a68da79fd25b775da8e19702999b1c3cfb57b..290a1258e7a33fd552adeb6eb2d293108936e79f 100644
--- a/src/resolv/async_resolv.c
+++ b/src/resolv/async_resolv.c
@@ -1457,8 +1457,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 c2a285966c7be3b04fc52f8237c633e10b48c9c9 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     |  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 720cbdb5c0b31ef25425f3ca6f90de7ac81f9ca0..2c27b4aa61baa78a999cf7263ea5dc15ea755636 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 6f143dae1cc6585d00c7c95de012c3e260d9fb7f..ce33d43a2b7e36b68ad83fea4e9b503e3f305177 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 59dccfbbcedc5cfd20ec80237d70cd4466b0a569..5f2cc7d69b7648493e8ed763eba30509e6acdd27 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 dd64ab8def679a5b3f6f3ebfd53530a8432a3373 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 2c27b4aa61baa78a999cf7263ea5dc15ea755636..ffc8c1be1ff9a4a4e4d1601146e43413b48bd168 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 ce33d43a2b7e36b68ad83fea4e9b503e3f305177..e92fb6c96a1b6ed306b2904c533fa8dc1554a473 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 5f2cc7d69b7648493e8ed763eba30509e6acdd27..98d9d15122233073e79669c10cfa0ac9a4f2b659 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