Blame SOURCES/0027-DYNDNS-SSSD-does-not-batch-DDNS-update-requests.patch

836b22
From 6a77f9470ccd3fc90d850b71b0f0564573342c01 Mon Sep 17 00:00:00 2001
836b22
From: Tomas Halman <thalman@redhat.com>
836b22
Date: Mon, 29 Oct 2018 14:47:08 +0100
836b22
Subject: [PATCH] DYNDNS: SSSD does not batch DDNS update requests
836b22
MIME-Version: 1.0
836b22
Content-Type: text/plain; charset=UTF-8
836b22
Content-Transfer-Encoding: 8bit
836b22
836b22
SSSD includes a 'send' command in between each record modification
836b22
and does not batch DDNS update requests. This is problematic in
836b22
complex AD environments because those requests may not be processed
836b22
by the same server.
836b22
836b22
Now forward zone update is done in two steps - one per
836b22
protocol family. If dyndns_update_per_family is set
836b22
to false, update is performed in single step.
836b22
836b22
Resolves:
836b22
https://github.com/SSSD/sssd/issues/4823
836b22
836b22
Reviewed-by: Pavel Březina <pbrezina@redhat.com>
836b22
(cherry picked from commit 5565dd365e704f6ded4f95db5bfbefd5dffc888b)
836b22
836b22
Reviewed-by: Alexey Tikhonov <atikhono@redhat.com>
836b22
---
836b22
 src/man/sssd-ad.5.xml            |  15 +++
836b22
 src/man/sssd-ipa.5.xml           |  15 +++
836b22
 src/providers/ad/ad_opts.c       |   1 +
836b22
 src/providers/be_dyndns.c        | 203 ++++++++++++++++++++-----------
836b22
 src/providers/be_dyndns.h        |  10 +-
836b22
 src/providers/ipa/ipa_opts.c     |   1 +
836b22
 src/providers/ldap/sdap_dyndns.c | 121 ++----------------
836b22
 src/tests/cmocka/test_dyndns.c   |  98 +++++++++++++--
836b22
 8 files changed, 264 insertions(+), 200 deletions(-)
836b22
836b22
diff --git a/src/man/sssd-ad.5.xml b/src/man/sssd-ad.5.xml
836b22
index 9e9e52eb3..6fc57ca21 100644
836b22
--- a/src/man/sssd-ad.5.xml
836b22
+++ b/src/man/sssd-ad.5.xml
836b22
@@ -1066,6 +1066,21 @@ ad_gpo_map_deny = +my_pam_service
836b22
                     </listitem>
836b22
                 </varlistentry>
836b22
 
836b22
+                <varlistentry>
836b22
+                    <term>dyndns_update_per_family (boolean)</term>
836b22
+                    <listitem>
836b22
+                        <para>
836b22
+                            DNS update is by default performed in two steps -
836b22
+                            IPv4 update and then IPv6 update. In some cases
836b22
+                            it might be desirable to perform IPv4 and IPv6
836b22
+                            update in single step.
836b22
+                        </para>
836b22
+                        <para>
836b22
+                            Default: true
836b22
+                        </para>
836b22
+                    </listitem>
836b22
+                </varlistentry>
836b22
+
836b22
                 <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="include/override_homedir.xml" />
836b22
                 <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="include/homedir_substring.xml" />
836b22
 
836b22
diff --git a/src/man/sssd-ipa.5.xml b/src/man/sssd-ipa.5.xml
836b22
index e46957d5f..2f1450213 100644
836b22
--- a/src/man/sssd-ipa.5.xml
836b22
+++ b/src/man/sssd-ipa.5.xml
836b22
@@ -313,6 +313,21 @@
836b22
                     </listitem>
836b22
                 </varlistentry>
836b22
 
836b22
+                <varlistentry>
836b22
+                    <term>dyndns_update_per_family (boolean)</term>
836b22
+                    <listitem>
836b22
+                        <para>
836b22
+                            DNS update is by default performed in two steps -
836b22
+                            IPv4 update and then IPv6 update. In some cases
836b22
+                            it might be desirable to perform IPv4 and IPv6
836b22
+                            update in single step.
836b22
+                        </para>
836b22
+                        <para>
836b22
+                            Default: true
836b22
+                        </para>
836b22
+                    </listitem>
836b22
+                </varlistentry>
836b22
+
836b22
                 <varlistentry>
836b22
                     <term>ipa_deskprofile_search_base (string)</term>
836b22
                     <listitem>
836b22
diff --git a/src/providers/ad/ad_opts.c b/src/providers/ad/ad_opts.c
836b22
index f181e5073..f2596a935 100644
836b22
--- a/src/providers/ad/ad_opts.c
836b22
+++ b/src/providers/ad/ad_opts.c
836b22
@@ -282,6 +282,7 @@ struct sdap_attr_map ad_autofs_entry_map[] = {
836b22
 
836b22
 struct dp_option ad_dyndns_opts[] = {
836b22
     { "dyndns_update", DP_OPT_BOOL, BOOL_TRUE, BOOL_FALSE },
836b22
+    { "dyndns_update_per_family", DP_OPT_BOOL, BOOL_TRUE, BOOL_TRUE },
836b22
     { "dyndns_refresh_interval", DP_OPT_NUMBER, { .number = 86400 }, NULL_NUMBER },
836b22
     { "dyndns_iface", DP_OPT_STRING, NULL_STRING, NULL_STRING },
836b22
     { "dyndns_ttl", DP_OPT_NUMBER, { .number = 3600 }, NULL_NUMBER },
836b22
diff --git a/src/providers/be_dyndns.c b/src/providers/be_dyndns.c
836b22
index ebe1fcd16..0907f9a53 100644
836b22
--- a/src/providers/be_dyndns.c
836b22
+++ b/src/providers/be_dyndns.c
836b22
@@ -267,72 +267,80 @@ done:
836b22
 
836b22
 static char *
836b22
 nsupdate_msg_add_fwd(char *update_msg, struct sss_iface_addr *addresses,
836b22
-                     const char *hostname, int ttl, uint8_t remove_af)
836b22
+                     const char *hostname, int ttl, uint8_t remove_af, bool update_per_family)
836b22
 {
836b22
     struct sss_iface_addr *new_record;
836b22
     char ip_addr[INET6_ADDRSTRLEN];
836b22
+    char *updateipv4 = talloc_strdup(update_msg, "");
836b22
+    char *updateipv6 = talloc_strdup(update_msg, "");
836b22
     errno_t ret;
836b22
 
836b22
-    /* A addresses first */
836b22
     /* Remove existing entries as needed */
836b22
     if (remove_af & DYNDNS_REMOVE_A) {
836b22
-        update_msg = talloc_asprintf_append(update_msg,
836b22
+        updateipv4 = talloc_asprintf_append(updateipv4,
836b22
                                             "update delete %s. in A\n",
836b22
                                             hostname);
836b22
-        if (update_msg == NULL) {
836b22
+        if (updateipv4 == NULL) {
836b22
             return NULL;
836b22
         }
836b22
     }
836b22
-    DLIST_FOR_EACH(new_record, addresses) {
836b22
-        if (new_record->addr->ss_family == AF_INET) {
836b22
-            ret = addr_to_str(new_record->addr, ip_addr, INET6_ADDRSTRLEN);
836b22
-            if (ret != EOK) {
836b22
-                DEBUG(SSSDBG_MINOR_FAILURE, "addr_to_str failed: %d:[%s],\n",
836b22
-                      ret, sss_strerror(ret));
836b22
-                return NULL;
836b22
-            }
836b22
-
836b22
-            /* Format the record update */
836b22
-            update_msg = talloc_asprintf_append(update_msg,
836b22
-                                                "update add %s. %d in %s %s\n",
836b22
-                                                hostname, ttl, "A", ip_addr);
836b22
-            if (update_msg == NULL) {
836b22
-                return NULL;
836b22
-            }
836b22
-        }
836b22
-    }
836b22
-    update_msg = talloc_asprintf_append(update_msg, "send\n");
836b22
 
836b22
-    /* AAAA addresses next */
836b22
-    /* Remove existing entries as needed */
836b22
     if (remove_af & DYNDNS_REMOVE_AAAA) {
836b22
-        update_msg = talloc_asprintf_append(update_msg,
836b22
+        updateipv6 = talloc_asprintf_append(updateipv6,
836b22
                                             "update delete %s. in AAAA\n",
836b22
                                             hostname);
836b22
-        if (update_msg == NULL) {
836b22
+        if (updateipv6 == NULL) {
836b22
             return NULL;
836b22
         }
836b22
     }
836b22
+
836b22
     DLIST_FOR_EACH(new_record, addresses) {
836b22
-        if (new_record->addr->ss_family == AF_INET6) {
836b22
-            ret = addr_to_str(new_record->addr, ip_addr, INET6_ADDRSTRLEN);
836b22
-            if (ret != EOK) {
836b22
-                DEBUG(SSSDBG_MINOR_FAILURE, "addr_to_str failed: %d:[%s],\n",
836b22
-                      ret, sss_strerror(ret));
836b22
+        ret = addr_to_str(new_record->addr, ip_addr, INET6_ADDRSTRLEN);
836b22
+        if (ret != EOK) {
836b22
+            DEBUG(SSSDBG_MINOR_FAILURE, "addr_to_str failed: %d:[%s],\n",
836b22
+                  ret, sss_strerror(ret));
836b22
+            return NULL;
836b22
+        }
836b22
+
836b22
+        switch (new_record->addr->ss_family) {
836b22
+        case AF_INET:
836b22
+            updateipv4 = talloc_asprintf_append(updateipv4,
836b22
+                                                "update add %s. %d in %s %s\n",
836b22
+                                                hostname, ttl, "A", ip_addr);
836b22
+            if (updateipv4 == NULL) {
836b22
                 return NULL;
836b22
             }
836b22
 
836b22
-            /* Format the record update */
836b22
-            update_msg = talloc_asprintf_append(update_msg,
836b22
+            break;
836b22
+        case AF_INET6:
836b22
+            updateipv6 = talloc_asprintf_append(updateipv6,
836b22
                                                 "update add %s. %d in %s %s\n",
836b22
                                                 hostname, ttl, "AAAA", ip_addr);
836b22
-            if (update_msg == NULL) {
836b22
+            if (updateipv6 == NULL) {
836b22
                 return NULL;
836b22
             }
836b22
+
836b22
+            break;
836b22
         }
836b22
     }
836b22
 
836b22
-    return talloc_asprintf_append(update_msg, "send\n");
836b22
+    if (update_per_family && updateipv4[0] && updateipv6[0]) {
836b22
+        /* update per family and both families present */
836b22
+        return talloc_asprintf_append(update_msg,
836b22
+                                            "%s"
836b22
+                                            "send\n"
836b22
+                                            "%s"
836b22
+                                            "send\n",
836b22
+                                            updateipv4,
836b22
+                                            updateipv6);
836b22
+    }
836b22
+
836b22
+    return talloc_asprintf_append(update_msg,
836b22
+                                  "%s"
836b22
+                                  "%s"
836b22
+                                  "send\n",
836b22
+                                  updateipv4,
836b22
+                                  updateipv6);
836b22
 }
836b22
 
836b22
 static uint8_t *nsupdate_convert_address(struct sockaddr_storage *add_address)
836b22
@@ -355,46 +363,86 @@ static uint8_t *nsupdate_convert_address(struct sockaddr_storage *add_address)
836b22
     return addr;
836b22
 }
836b22
 
836b22
-static char *nsupdate_msg_add_ptr(char *update_msg,
836b22
-                                  struct sockaddr_storage *address,
836b22
-                                  const char *hostname,
836b22
-                                  int ttl,
836b22
-                                  bool delete)
836b22
+static char *
836b22
+nsupdate_msg_add_ptr(char *update_msg, struct sss_iface_addr *addresses,
836b22
+                     const char *hostname, int ttl, uint8_t remove_af,
836b22
+                     bool update_per_family)
836b22
 {
836b22
-    char *strptr;
836b22
+    char *updateipv4 = talloc_strdup(update_msg, "");
836b22
+    char *updateipv6 = talloc_strdup(update_msg, "");
836b22
+    char *ptr;
836b22
+    struct sss_iface_addr *address_it;
836b22
     uint8_t *addr;
836b22
 
836b22
-    addr = nsupdate_convert_address(address);
836b22
-    if (addr == NULL) {
836b22
+    if (!updateipv4 || !updateipv6) {
836b22
         return NULL;
836b22
     }
836b22
 
836b22
-    strptr = resolv_get_string_ptr_address(update_msg, address->ss_family,
836b22
-                                           addr);
836b22
-    if (strptr == NULL) {
836b22
-        return NULL;
836b22
-    }
836b22
+    DLIST_FOR_EACH(address_it, addresses) {
836b22
+        addr = nsupdate_convert_address(address_it->addr);
836b22
+        if (addr == NULL) {
836b22
+            return NULL;
836b22
+        }
836b22
 
836b22
-    if (delete) {
836b22
-        /* example: update delete 38.78.16.10.in-addr.arpa. in PTR */
836b22
-        update_msg = talloc_asprintf_append(update_msg,
836b22
-                                            "update delete %s in PTR\n"
836b22
-                                            "send\n",
836b22
-                                            strptr);
836b22
-    } else {
836b22
-        /* example: update delete 38.78.16.10.in-addr.arpa. in PTR */
836b22
-        update_msg = talloc_asprintf_append(update_msg,
836b22
-                                            "update add %s %d in PTR %s.\n"
836b22
-                                            "send\n",
836b22
-                                            strptr, ttl, hostname);
836b22
+        ptr = resolv_get_string_ptr_address(update_msg, address_it->addr->ss_family,
836b22
+                                            addr);
836b22
+        if (ptr == NULL) {
836b22
+            return NULL;
836b22
+        }
836b22
+
836b22
+        switch (address_it->addr->ss_family) {
836b22
+        case AF_INET:
836b22
+            if (remove_af & DYNDNS_REMOVE_A) {
836b22
+                updateipv4 = talloc_asprintf_append(updateipv4,
836b22
+                                                    "update delete %s in PTR\n",
836b22
+                                                    ptr);
836b22
+                if (updateipv4 == NULL) {
836b22
+                    return NULL;
836b22
+                }
836b22
+            }
836b22
+
836b22
+            updateipv4 = talloc_asprintf_append(updateipv4,
836b22
+                                                "update add %s %d in PTR %s.\n",
836b22
+                                                ptr, ttl, hostname);
836b22
+            break;
836b22
+        case AF_INET6:
836b22
+            if (remove_af & DYNDNS_REMOVE_AAAA) {
836b22
+                updateipv6 = talloc_asprintf_append(updateipv6,
836b22
+                                                    "update delete %s in PTR\n",
836b22
+                                                    ptr);
836b22
+                if (updateipv6 == NULL) {
836b22
+                    return NULL;
836b22
+                }
836b22
+            }
836b22
+            updateipv6 = talloc_asprintf_append(updateipv6,
836b22
+                                                "update add %s %d in PTR %s.\n",
836b22
+                                                ptr, ttl, hostname);
836b22
+            break;
836b22
+        }
836b22
+
836b22
+        talloc_free(ptr);
836b22
+        if (!updateipv4 || !updateipv6) {
836b22
+            return NULL;
836b22
+        }
836b22
     }
836b22
 
836b22
-    talloc_free(strptr);
836b22
-    if (update_msg == NULL) {
836b22
-        return NULL;
836b22
+    if (update_per_family && updateipv4[0] && updateipv6[0]) {
836b22
+        /* update per family and both families present */
836b22
+        return talloc_asprintf_append(update_msg,
836b22
+                                      "%s"
836b22
+                                      "send\n"
836b22
+                                      "%s"
836b22
+                                      "send\n",
836b22
+                                      updateipv4,
836b22
+                                      updateipv6);
836b22
     }
836b22
 
836b22
-    return update_msg;
836b22
+    return talloc_asprintf_append(update_msg,
836b22
+                                  "%s"
836b22
+                                  "%s"
836b22
+                                  "send\n",
836b22
+                                  updateipv4,
836b22
+                                  updateipv6);
836b22
 }
836b22
 
836b22
 static char *
836b22
@@ -464,6 +512,7 @@ be_nsupdate_create_fwd_msg(TALLOC_CTX *mem_ctx, const char *realm,
836b22
                            const char *servername,
836b22
                            const char *hostname, const unsigned int ttl,
836b22
                            uint8_t remove_af, struct sss_iface_addr *addresses,
836b22
+                           bool update_per_family,
836b22
                            char **_update_msg)
836b22
 {
836b22
     int ret;
836b22
@@ -485,7 +534,7 @@ be_nsupdate_create_fwd_msg(TALLOC_CTX *mem_ctx, const char *realm,
836b22
     }
836b22
 
836b22
     update_msg = nsupdate_msg_add_fwd(update_msg, addresses, hostname,
836b22
-                                      ttl, remove_af);
836b22
+                                      ttl, remove_af, update_per_family);
836b22
     if (update_msg == NULL) {
836b22
         ret = ENOMEM;
836b22
         goto done;
836b22
@@ -506,28 +555,32 @@ done:
836b22
 
836b22
 errno_t
836b22
 be_nsupdate_create_ptr_msg(TALLOC_CTX *mem_ctx, const char *realm,
836b22
-                           const char *servername, const char *hostname,
836b22
-                           const unsigned int ttl,
836b22
-                           struct sockaddr_storage *address,
836b22
-                           bool delete,
836b22
+                           const char *servername,
836b22
+                           const char *hostname, const unsigned int ttl,
836b22
+                           uint8_t remove_af, struct sss_iface_addr *addresses,
836b22
+                           bool update_per_family,
836b22
                            char **_update_msg)
836b22
 {
836b22
     errno_t ret;
836b22
     char *update_msg;
836b22
+    TALLOC_CTX *tmp_ctx;
836b22
 
836b22
     /* in some cases realm could have been NULL if we weren't using TSIG */
836b22
     if (hostname == NULL) {
836b22
         return EINVAL;
836b22
     }
836b22
 
836b22
-    update_msg = nsupdate_msg_create_common(mem_ctx, realm, servername);
836b22
+    tmp_ctx = talloc_new(NULL);
836b22
+    if (tmp_ctx == NULL) return ENOMEM;
836b22
+
836b22
+    update_msg = nsupdate_msg_create_common(tmp_ctx, realm, servername);
836b22
     if (update_msg == NULL) {
836b22
         ret = ENOMEM;
836b22
         goto done;
836b22
     }
836b22
 
836b22
-    update_msg = nsupdate_msg_add_ptr(update_msg, address, hostname, ttl,
836b22
-                                      delete);
836b22
+    update_msg = nsupdate_msg_add_ptr(update_msg, addresses, hostname,
836b22
+                                      ttl, remove_af, update_per_family);
836b22
     if (update_msg == NULL) {
836b22
         ret = ENOMEM;
836b22
         goto done;
836b22
@@ -540,9 +593,10 @@ be_nsupdate_create_ptr_msg(TALLOC_CTX *mem_ctx, const char *realm,
836b22
           update_msg);
836b22
 
836b22
     ret = ERR_OK;
836b22
-    *_update_msg = update_msg;
836b22
+    *_update_msg = talloc_steal(mem_ctx, update_msg);
836b22
 
836b22
 done:
836b22
+    talloc_free(tmp_ctx);
836b22
     return ret;
836b22
 }
836b22
 
836b22
@@ -1196,6 +1250,7 @@ be_nsupdate_check(void)
836b22
 
836b22
 static struct dp_option default_dyndns_opts[] = {
836b22
     { "dyndns_update", DP_OPT_BOOL, BOOL_FALSE, BOOL_FALSE },
836b22
+    { "dyndns_update_per_family", DP_OPT_BOOL, BOOL_TRUE, BOOL_TRUE },
836b22
     { "dyndns_refresh_interval", DP_OPT_NUMBER, NULL_NUMBER, NULL_NUMBER },
836b22
     { "dyndns_iface", DP_OPT_STRING, NULL_STRING, NULL_STRING },
836b22
     { "dyndns_ttl", DP_OPT_NUMBER, { .number = 1200 }, NULL_NUMBER },
836b22
diff --git a/src/providers/be_dyndns.h b/src/providers/be_dyndns.h
836b22
index 9f39e5d48..48f4597bb 100644
836b22
--- a/src/providers/be_dyndns.h
836b22
+++ b/src/providers/be_dyndns.h
836b22
@@ -49,6 +49,7 @@ struct be_nsupdate_ctx {
836b22
 
836b22
 enum dp_dyndns_opts {
836b22
     DP_OPT_DYNDNS_UPDATE,
836b22
+    DP_OPT_DYNDNS_UPDATE_PER_FAMILY,
836b22
     DP_OPT_DYNDNS_REFRESH_INTERVAL,
836b22
     DP_OPT_DYNDNS_IFACE,
836b22
     DP_OPT_DYNDNS_TTL,
836b22
@@ -92,14 +93,15 @@ be_nsupdate_create_fwd_msg(TALLOC_CTX *mem_ctx, const char *realm,
836b22
                            const char *servername,
836b22
                            const char *hostname, const unsigned int ttl,
836b22
                            uint8_t remove_af, struct sss_iface_addr *addresses,
836b22
+                           bool update_per_family,
836b22
                            char **_update_msg);
836b22
 
836b22
 errno_t
836b22
 be_nsupdate_create_ptr_msg(TALLOC_CTX *mem_ctx, const char *realm,
836b22
-                           const char *servername, const char *hostname,
836b22
-                           const unsigned int ttl,
836b22
-                           struct sockaddr_storage *address,
836b22
-                           bool delete,
836b22
+                           const char *servername,
836b22
+                           const char *hostname, const unsigned int ttl,
836b22
+                           uint8_t remove_af, struct sss_iface_addr *addresses,
836b22
+                           bool update_per_family,
836b22
                            char **_update_msg);
836b22
 
836b22
 /* Returns:
836b22
diff --git a/src/providers/ipa/ipa_opts.c b/src/providers/ipa/ipa_opts.c
836b22
index c43b21778..3b395150c 100644
836b22
--- a/src/providers/ipa/ipa_opts.c
836b22
+++ b/src/providers/ipa/ipa_opts.c
836b22
@@ -56,6 +56,7 @@ struct dp_option ipa_basic_opts[] = {
836b22
 
836b22
 struct dp_option ipa_dyndns_opts[] = {
836b22
     { "dyndns_update", DP_OPT_BOOL, BOOL_FALSE, BOOL_FALSE },
836b22
+    { "dyndns_update_per_family", DP_OPT_BOOL, BOOL_TRUE, BOOL_TRUE },
836b22
     { "dyndns_refresh_interval", DP_OPT_NUMBER, NULL_NUMBER, NULL_NUMBER },
836b22
     { "dyndns_iface", DP_OPT_STRING, NULL_STRING, NULL_STRING },
836b22
     { "dyndns_ttl", DP_OPT_NUMBER, { .number = 1200 }, NULL_NUMBER },
836b22
diff --git a/src/providers/ldap/sdap_dyndns.c b/src/providers/ldap/sdap_dyndns.c
836b22
index 20d97ca41..1f7155a3d 100644
836b22
--- a/src/providers/ldap/sdap_dyndns.c
836b22
+++ b/src/providers/ldap/sdap_dyndns.c
836b22
@@ -55,13 +55,12 @@ struct sdap_dyndns_update_state {
836b22
     struct sss_iface_addr *dns_addrlist;
836b22
     uint8_t remove_af;
836b22
 
836b22
+    bool update_per_family;
836b22
     bool update_ptr;
836b22
     bool check_diff;
836b22
     enum be_nsupdate_auth auth_type;
836b22
     bool fallback_mode;
836b22
     char *update_msg;
836b22
-    struct sss_iface_addr *ptr_addr_iter;
836b22
-    bool del_phase;
836b22
 };
836b22
 
836b22
 static void sdap_dyndns_update_addrs_done(struct tevent_req *subreq);
836b22
@@ -72,12 +71,6 @@ static errno_t sdap_dyndns_update_step(struct tevent_req *req);
836b22
 static errno_t sdap_dyndns_update_ptr_step(struct tevent_req *req);
836b22
 static void sdap_dyndns_update_done(struct tevent_req *subreq);
836b22
 static void sdap_dyndns_update_ptr_done(struct tevent_req *subreq);
836b22
-static errno_t
836b22
-sdap_dyndns_next_ptr_record(struct sdap_dyndns_update_state *state,
836b22
-                            struct tevent_req *req);
836b22
-static struct sss_iface_addr*
836b22
-sdap_get_address_to_delete(struct sss_iface_addr *address_it,
836b22
-                           uint8_t remove_af);
836b22
 
836b22
 static bool should_retry(int nsupdate_ret, int child_status)
836b22
 {
836b22
@@ -113,6 +106,7 @@ sdap_dyndns_update_send(TALLOC_CTX *mem_ctx,
836b22
         return NULL;
836b22
     }
836b22
     state->check_diff = check_diff;
836b22
+    state->update_per_family = dp_opt_get_bool(opts, DP_OPT_DYNDNS_UPDATE_PER_FAMILY);
836b22
     state->update_ptr = dp_opt_get_bool(opts, DP_OPT_DYNDNS_UPDATE_PTR);
836b22
     state->hostname = hostname;
836b22
     state->realm = realm;
836b22
@@ -123,8 +117,6 @@ sdap_dyndns_update_send(TALLOC_CTX *mem_ctx,
836b22
     state->ev = ev;
836b22
     state->opts = opts;
836b22
     state->auth_type = auth_type;
836b22
-    state->ptr_addr_iter = NULL;
836b22
-    state->del_phase = true;
836b22
 
836b22
     /* fallback servername is overriden by user option */
836b22
     conf_servername = dp_opt_get_string(opts, DP_OPT_DYNDNS_SERVER);
836b22
@@ -346,6 +338,7 @@ sdap_dyndns_update_step(struct tevent_req *req)
836b22
                                      state->hostname,
836b22
                                      state->ttl, state->remove_af,
836b22
                                      state->addresses,
836b22
+                                     state->update_per_family,
836b22
                                      &state->update_msg);
836b22
     if (ret != EOK) {
836b22
         DEBUG(SSSDBG_OP_FAILURE, "Can't get addresses for DNS update\n");
836b22
@@ -400,15 +393,6 @@ sdap_dyndns_update_done(struct tevent_req *subreq)
836b22
 
836b22
     talloc_free(state->update_msg);
836b22
 
836b22
-    /* init iterator for addresses to be deleted */
836b22
-    state->ptr_addr_iter = sdap_get_address_to_delete(state->dns_addrlist,
836b22
-                                                      state->remove_af);
836b22
-    if (state->ptr_addr_iter == NULL) {
836b22
-        /* init iterator for addresses to be added */
836b22
-        state->del_phase = false;
836b22
-        state->ptr_addr_iter = state->addresses;
836b22
-    }
836b22
-
836b22
     ret = sdap_dyndns_update_ptr_step(req);
836b22
     if (ret != EOK) {
836b22
         tevent_req_error(req, ret);
836b22
@@ -417,50 +401,6 @@ sdap_dyndns_update_done(struct tevent_req *subreq)
836b22
     /* Execution will resume in sdap_dyndns_update_ptr_done */
836b22
 }
836b22
 
836b22
-
836b22
-static bool remove_addr(int address_family, uint8_t remove_af)
836b22
-{
836b22
-    bool ret = false;
836b22
-
836b22
-    switch(address_family) {
836b22
-    case AF_INET:
836b22
-        if (remove_af & DYNDNS_REMOVE_A) {
836b22
-            ret = true;
836b22
-        }
836b22
-        break;
836b22
-    case AF_INET6:
836b22
-        if (remove_af & DYNDNS_REMOVE_AAAA) {
836b22
-            ret = true;
836b22
-        }
836b22
-        break;
836b22
-    default:
836b22
-        DEBUG(SSSDBG_CRIT_FAILURE, "Unknown address family\n");
836b22
-        ret = false;
836b22
-    }
836b22
-
836b22
-    return ret;
836b22
-}
836b22
-
836b22
-static struct sss_iface_addr*
836b22
-sdap_get_address_to_delete(struct sss_iface_addr *address_it,
836b22
-                           uint8_t remove_af)
836b22
-{
836b22
-    struct sockaddr_storage* address;
836b22
-
836b22
-    while (address_it != NULL) {
836b22
-        address = sss_iface_addr_get_address(address_it);
836b22
-
836b22
-        /* skip addresses that are not to be deleted */
836b22
-        if (remove_addr(address->ss_family, remove_af)) {
836b22
-            break;
836b22
-        }
836b22
-
836b22
-        address_it = sss_iface_addr_get_next(address_it);
836b22
-    }
836b22
-
836b22
-    return address_it;
836b22
-}
836b22
-
836b22
 static errno_t
836b22
 sdap_dyndns_update_ptr_step(struct tevent_req *req)
836b22
 {
836b22
@@ -469,7 +409,6 @@ sdap_dyndns_update_ptr_step(struct tevent_req *req)
836b22
     const char *servername;
836b22
     const char *realm;
836b22
     struct tevent_req *subreq;
836b22
-    struct sockaddr_storage *address;
836b22
 
836b22
     state = tevent_req_data(req, struct sdap_dyndns_update_state);
836b22
 
836b22
@@ -480,13 +419,11 @@ sdap_dyndns_update_ptr_step(struct tevent_req *req)
836b22
         realm = state->realm;
836b22
     }
836b22
 
836b22
-    address = sss_iface_addr_get_address(state->ptr_addr_iter);
836b22
-    if (address == NULL) {
836b22
-        return EIO;
836b22
-    }
836b22
-
836b22
-    ret = be_nsupdate_create_ptr_msg(state, realm, servername, state->hostname,
836b22
-                                     state->ttl, address, state->del_phase,
836b22
+    ret = be_nsupdate_create_ptr_msg(state, realm, servername,
836b22
+                                     state->hostname,
836b22
+                                     state->ttl, state->remove_af,
836b22
+                                     state->addresses,
836b22
+                                     state->update_per_family,
836b22
                                      &state->update_msg);
836b22
 
836b22
     if (ret != EOK) {
836b22
@@ -532,55 +469,13 @@ sdap_dyndns_update_ptr_done(struct tevent_req *subreq)
836b22
             }
836b22
         }
836b22
 
836b22
-        ret = sdap_dyndns_next_ptr_record(state, req);
836b22
-        if (ret == EAGAIN) {
836b22
-            return;
836b22
-        }
836b22
-
836b22
         tevent_req_error(req, ret);
836b22
         return;
836b22
     }
836b22
 
836b22
-    ret = sdap_dyndns_next_ptr_record(state, req);
836b22
-    if (ret == EAGAIN) {
836b22
-        return;
836b22
-    }
836b22
-
836b22
     tevent_req_done(req);
836b22
 }
836b22
 
836b22
-static errno_t
836b22
-sdap_dyndns_next_ptr_record(struct sdap_dyndns_update_state *state,
836b22
-                            struct tevent_req *req)
836b22
-{
836b22
-    errno_t ret;
836b22
-
836b22
-    if (state->del_phase) {
836b22
-        /* iterate to next address to delete */
836b22
-        state->ptr_addr_iter = sdap_get_address_to_delete(
836b22
-            sss_iface_addr_get_next(state->ptr_addr_iter), state->remove_af);
836b22
-        if (state->ptr_addr_iter == NULL) {
836b22
-            /* init iterator for addresses to be added */
836b22
-            state->del_phase = false;
836b22
-            state->ptr_addr_iter = state->addresses;
836b22
-        }
836b22
-    } else {
836b22
-        /* iterate to next address to add */
836b22
-        state->ptr_addr_iter = sss_iface_addr_get_next(state->ptr_addr_iter);
836b22
-    }
836b22
-
836b22
-    if (state->ptr_addr_iter != NULL) {
836b22
-
836b22
-        state->fallback_mode = false;
836b22
-        ret = sdap_dyndns_update_ptr_step(req);
836b22
-        if (ret == EOK) {
836b22
-            return EAGAIN;
836b22
-        }
836b22
-    }
836b22
-
836b22
-    return EOK;
836b22
-}
836b22
-
836b22
 errno_t
836b22
 sdap_dyndns_update_recv(struct tevent_req *req)
836b22
 {
836b22
diff --git a/src/tests/cmocka/test_dyndns.c b/src/tests/cmocka/test_dyndns.c
836b22
index 8888b5371..57180291e 100644
836b22
--- a/src/tests/cmocka/test_dyndns.c
836b22
+++ b/src/tests/cmocka/test_dyndns.c
836b22
@@ -385,7 +385,7 @@ void dyndns_test_create_fwd_msg(void **state)
836b22
 
836b22
     ret = be_nsupdate_create_fwd_msg(dyndns_test_ctx, NULL, NULL, "bran_stark",
836b22
                                      1234, DYNDNS_REMOVE_A | DYNDNS_REMOVE_AAAA,
836b22
-                                     addrlist, &msg;;
836b22
+                                     addrlist, true, &msg;;
836b22
     assert_int_equal(ret, EOK);
836b22
 
836b22
     assert_string_equal(msg,
836b22
@@ -397,11 +397,24 @@ void dyndns_test_create_fwd_msg(void **state)
836b22
                         "send\n");
836b22
     talloc_zfree(msg);
836b22
 
836b22
+    ret = be_nsupdate_create_fwd_msg(dyndns_test_ctx, NULL, NULL, "bran_stark",
836b22
+                                     1234, DYNDNS_REMOVE_A | DYNDNS_REMOVE_AAAA,
836b22
+                                     addrlist, false, &msg;;
836b22
+    assert_int_equal(ret, EOK);
836b22
+
836b22
+    assert_string_equal(msg,
836b22
+                        "\nupdate delete bran_stark. in A\n"
836b22
+                        "update add bran_stark. 1234 in A 192.168.0.2\n"
836b22
+                        "update delete bran_stark. in AAAA\n"
836b22
+                        "update add bran_stark. 1234 in AAAA 2001:cdba::555\n"
836b22
+                        "send\n");
836b22
+    talloc_zfree(msg);
836b22
+
836b22
     /* fallback case realm and server */
836b22
     ret = be_nsupdate_create_fwd_msg(dyndns_test_ctx, "North", "Winterfell",
836b22
                                      "bran_stark",
836b22
                                      1234, DYNDNS_REMOVE_A | DYNDNS_REMOVE_AAAA,
836b22
-                                     addrlist, &msg;;
836b22
+                                     addrlist, true, &msg;;
836b22
     assert_int_equal(ret, EOK);
836b22
 
836b22
     assert_string_equal(msg,
836b22
@@ -423,7 +436,7 @@ void dyndns_test_create_fwd_msg(void **state)
836b22
     ret = be_nsupdate_create_fwd_msg(dyndns_test_ctx, "North", NULL,
836b22
                                      "bran_stark",
836b22
                                      1234, DYNDNS_REMOVE_A | DYNDNS_REMOVE_AAAA,
836b22
-                                     addrlist, &msg;;
836b22
+                                     addrlist, true, &msg;;
836b22
     assert_int_equal(ret, EOK);
836b22
 
836b22
     assert_string_equal(msg,
836b22
@@ -444,7 +457,7 @@ void dyndns_test_create_fwd_msg(void **state)
836b22
     ret = be_nsupdate_create_fwd_msg(dyndns_test_ctx, NULL, "Winterfell",
836b22
                                      "bran_stark",
836b22
                                      1234, DYNDNS_REMOVE_A | DYNDNS_REMOVE_AAAA,
836b22
-                                     addrlist, &msg;;
836b22
+                                     addrlist, true, &msg;;
836b22
     assert_int_equal(ret, EOK);
836b22
 
836b22
     assert_string_equal(msg,
836b22
@@ -461,7 +474,7 @@ void dyndns_test_create_fwd_msg(void **state)
836b22
     /* remove just A */
836b22
     ret = be_nsupdate_create_fwd_msg(dyndns_test_ctx, NULL, NULL, "bran_stark",
836b22
                                      1234, DYNDNS_REMOVE_A,
836b22
-                                     addrlist, &msg;;
836b22
+                                     addrlist, true, &msg;;
836b22
     assert_int_equal(ret, EOK);
836b22
 
836b22
     assert_string_equal(msg,
836b22
@@ -475,7 +488,7 @@ void dyndns_test_create_fwd_msg(void **state)
836b22
     /* remove just AAAA */
836b22
     ret = be_nsupdate_create_fwd_msg(dyndns_test_ctx, NULL, NULL, "bran_stark",
836b22
                                      1234, DYNDNS_REMOVE_AAAA,
836b22
-                                     addrlist, &msg;;
836b22
+                                     addrlist, true, &msg;;
836b22
     assert_int_equal(ret, EOK);
836b22
 
836b22
     assert_string_equal(msg,
836b22
@@ -519,7 +532,7 @@ void dyndns_test_create_fwd_msg_mult(void **state)
836b22
 
836b22
     ret = be_nsupdate_create_fwd_msg(dyndns_test_ctx, NULL, NULL, "bran_stark",
836b22
                                      1234, DYNDNS_REMOVE_A | DYNDNS_REMOVE_AAAA,
836b22
-                                     addrlist, &msg;;
836b22
+                                     addrlist, true, &msg;;
836b22
     assert_int_equal(ret, EOK);
836b22
 
836b22
     assert_string_equal(msg,
836b22
@@ -564,7 +577,7 @@ void dyndns_test_create_fwd_msg_A(void **state)
836b22
 
836b22
     ret = be_nsupdate_create_fwd_msg(dyndns_test_ctx, NULL, NULL, "bran_stark",
836b22
                                      1234, DYNDNS_REMOVE_A | DYNDNS_REMOVE_AAAA,
836b22
-                                     addrlist, &msg;;
836b22
+                                     addrlist, true, &msg;;
836b22
     assert_int_equal(ret, EOK);
836b22
 
836b22
     assert_string_equal(msg,
836b22
@@ -608,7 +621,7 @@ void dyndns_test_create_fwd_msg_AAAA(void **state)
836b22
 
836b22
     ret = be_nsupdate_create_fwd_msg(dyndns_test_ctx, NULL, NULL, "bran_stark",
836b22
                                      1234, DYNDNS_REMOVE_A | DYNDNS_REMOVE_AAAA,
836b22
-                                     addrlist, &msg;;
836b22
+                                     addrlist, true, &msg;;
836b22
     assert_int_equal(ret, EOK);
836b22
 
836b22
     assert_string_equal(msg,
836b22
@@ -624,6 +637,70 @@ void dyndns_test_create_fwd_msg_AAAA(void **state)
836b22
     assert_true(check_leaks_pop(dyndns_test_ctx) == true);
836b22
 }
836b22
 
836b22
+void dyndns_test_create_ptr_msg(void **state)
836b22
+{
836b22
+    errno_t ret;
836b22
+    char *msg;
836b22
+    struct sss_iface_addr *addrlist;
836b22
+    int i;
836b22
+
836b22
+    check_leaks_push(dyndns_test_ctx);
836b22
+
836b22
+     /* getifaddrs is called twice in sss_get_dualstack_addresses() */
836b22
+    for (i = 0; i < 2; i++) {
836b22
+        will_return_getifaddrs("eth0", "192.168.0.2", AF_INET);
836b22
+        will_return_getifaddrs("eth0", "192.168.0.1", AF_INET);
836b22
+        will_return_getifaddrs("eth0", "2001:cdba::555", AF_INET6);
836b22
+        will_return_getifaddrs("eth0", "2001:cdba::444", AF_INET6);
836b22
+        will_return_getifaddrs(NULL, NULL, 0); /* sentinel */
836b22
+    }
836b22
+
836b22
+    struct sockaddr_in sin;
836b22
+    memset(&sin, 0, sizeof (sin));
836b22
+    sin.sin_family = AF_INET;
836b22
+    sin.sin_addr.s_addr = inet_addr ("192.168.0.2");
836b22
+    ret = sss_get_dualstack_addresses(dyndns_test_ctx,
836b22
+                                      (struct sockaddr *) &sin,
836b22
+                                      &addrlist);
836b22
+    assert_int_equal(ret, EOK);
836b22
+
836b22
+    ret = be_nsupdate_create_ptr_msg(dyndns_test_ctx, NULL, NULL, "bran_stark",
836b22
+                                     1234, DYNDNS_REMOVE_A | DYNDNS_REMOVE_AAAA,
836b22
+                                     addrlist, true, &msg;;
836b22
+    assert_int_equal(ret, EOK);
836b22
+    assert_string_equal(msg,
836b22
+                        "\nupdate delete 1.0.168.192.in-addr.arpa. in PTR\n"
836b22
+                        "update add 1.0.168.192.in-addr.arpa. 1234 in PTR bran_stark.\n"
836b22
+                        "update delete 2.0.168.192.in-addr.arpa. in PTR\n"
836b22
+                        "update add 2.0.168.192.in-addr.arpa. 1234 in PTR bran_stark.\n"
836b22
+                        "send\n"
836b22
+                        "update delete 4.4.4.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.a.b.d.c.1.0.0.2.ip6.arpa. in PTR\n"
836b22
+                        "update add 4.4.4.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.a.b.d.c.1.0.0.2.ip6.arpa. 1234 in PTR bran_stark.\n"
836b22
+                        "update delete 5.5.5.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.a.b.d.c.1.0.0.2.ip6.arpa. in PTR\n"
836b22
+                        "update add 5.5.5.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.a.b.d.c.1.0.0.2.ip6.arpa. 1234 in PTR bran_stark.\n"
836b22
+                        "send\n");
836b22
+    talloc_zfree(msg);
836b22
+
836b22
+    ret = be_nsupdate_create_ptr_msg(dyndns_test_ctx, NULL, NULL, "bran_stark",
836b22
+                                     1234, DYNDNS_REMOVE_A | DYNDNS_REMOVE_AAAA,
836b22
+                                     addrlist, false, &msg;;
836b22
+    assert_int_equal(ret, EOK);
836b22
+    assert_string_equal(msg,
836b22
+                        "\nupdate delete 1.0.168.192.in-addr.arpa. in PTR\n"
836b22
+                        "update add 1.0.168.192.in-addr.arpa. 1234 in PTR bran_stark.\n"
836b22
+                        "update delete 2.0.168.192.in-addr.arpa. in PTR\n"
836b22
+                        "update add 2.0.168.192.in-addr.arpa. 1234 in PTR bran_stark.\n"
836b22
+                        "update delete 4.4.4.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.a.b.d.c.1.0.0.2.ip6.arpa. in PTR\n"
836b22
+                        "update add 4.4.4.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.a.b.d.c.1.0.0.2.ip6.arpa. 1234 in PTR bran_stark.\n"
836b22
+                        "update delete 5.5.5.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.a.b.d.c.1.0.0.2.ip6.arpa. in PTR\n"
836b22
+                        "update add 5.5.5.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.a.b.d.c.1.0.0.2.ip6.arpa. 1234 in PTR bran_stark.\n"
836b22
+                        "send\n");
836b22
+    talloc_zfree(msg);
836b22
+
836b22
+    talloc_free(addrlist);
836b22
+    assert_true(check_leaks_pop(dyndns_test_ctx) == true);
836b22
+}
836b22
+
836b22
 void dyndns_test_dualstack(void **state)
836b22
 {
836b22
     errno_t ret;
836b22
@@ -1052,6 +1129,9 @@ int main(int argc, const char *argv[])
836b22
         cmocka_unit_test_setup_teardown(dyndns_test_create_fwd_msg_AAAA,
836b22
                                         dyndns_test_setup,
836b22
                                         dyndns_test_teardown),
836b22
+        cmocka_unit_test_setup_teardown(dyndns_test_create_ptr_msg,
836b22
+                                        dyndns_test_setup,
836b22
+                                        dyndns_test_teardown),
836b22
     };
836b22
 
836b22
     /* Set debug level to invalid value so we can decide if -d 0 was used. */
836b22
-- 
836b22
2.21.1
836b22