[SSSD] [PATCH] Make the dynamic DNS updates backend-agnostic

Jakub Hrozek jhrozek at redhat.com
Thu May 2 10:49:06 UTC 2013


On Thu, May 02, 2013 at 10:33:06AM +0200, Pavel Březina wrote:
> >>Nack.
> >>
> >>Mostly minor things.
> >>
> >>ok_for_dns(): - use switch instead of if-elseif-else
> >>
> >
> >Done
> >
> >>sss_iface_addr_list_get(): - missing space around = in for -
> >>missing space after if (last two)
> >>
> >
> >Done
> 
> +            address->addr = talloc_memdup(address, ifa->ifa_addr,
> +                                          addrsize);
> +            if(address->addr == NULL) {
>                ^ you missed one
> 
> +                ret = ENOMEM;
> +                goto done;
> +            }
> 

Fixed too.

> >>be_nsupdate_create_msg(): - please add a comment that
> >>realm_directive ends with \n so using %szone format is correct -
> >>you should use tmp_ctx - inet_ntop sets errno, so you should use
> >>its value instead of EIO
> >>
> >
> >Done
> >
> >>struct nsupdate_get_addrs_state: - family_order is already present
> >>in be_res and it is set on line 420 state->family_order =
> >>be_res->family_order;
> >>
> >
> >Removed, the code now uses the order from be_res.
> >
> >>nsupdate_get_addrs_send(): - please, strdup hostname hostname is an
> >>error prone variable because it is often placed on stack, when it
> >>comes from gethostname()
> >
> >Done
> >
> >>
> >>nsupdate_get_addrs_done(): - please use ret = EAGAIN to indicate
> >>that the request continues and ret != EAGAIN to finish the request,
> >>instead of calling tevent_req_done() and tevent_req_error() on
> >>several places
> >>
> >
> >Almost done. I kept using return after tevent_req_set_callback. It
> >seems clearer to me than "ret = EAGAIN; goto done;".
> 
> OK. I'm fine with that.
> 
> >>- line 472: fall back between IP versions is already implemented
> >>in resolver. So if it returns ENOENT here, you already know that
> >>you cannot resolve the hostname, no need to retry with other IP
> >>version.
> >>
> >
> >Done. I guess the fallback in resolver predates the original dyndns
> >code.
> >
> >>- line 539: if resolver returned EOK, IPV4_FIRST || IPV6_FIRST does
> >>not tell you what version is in rhostent. It is because the fall
> >>back to next family is already implemented in resolver.
> >
> >As discussed off-list this is really needed as the resolver only
> >falls back in case the first address family does not find anything.
> 
> Besides the space, it's code-wise ack. I will test all dyndns patches at
> ones.
> 
> Just one thing to keep in mind for the future: please, do not use both
> !ptr and ptr == NULL conditions inside one function. Choose one and
> stick with it. I prefer the latter, because pointer is not a boolean value.

I skimmed through the code and fixed a couple of cases I found. I prefer
ptr == NULL, too.

New patches attached.
-------------- next part --------------
>From 0594847be5c8f7e214a4820630243d3b6ef3caef Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhrozek at redhat.com>
Date: Tue, 26 Mar 2013 16:49:26 +0100
Subject: [PATCH 1/2] Refactor dynamic DNS updates

Provides two new layers instead of the previous IPA specific layer:
 1) dp_dyndns.c -- a very generic dyndns layer on the DP level. Its
 purpose it to make it possible for any back end to use dynamic DNS
 updates.
 2) sdap_dyndns.c -- a wrapper around dp_dyndns.c that utilizes some
 LDAP-specific features like autodetecting the address from the LDAP
 connection.

Also converts the dyndns code to new specific error codes.
---
 Makefile.am                      |   29 +-
 src/providers/dp_dyndns.c        |  880 +++++++++++++++++++++++++++
 src/providers/dp_dyndns.h        |   68 +++
 src/providers/ipa/ipa_common.h   |    2 +-
 src/providers/ipa/ipa_dyndns.c   | 1241 +++-----------------------------------
 src/providers/ldap/sdap_dyndns.c |  498 +++++++++++++++
 src/providers/ldap/sdap_dyndns.h |   47 ++
 src/tests/cmocka/test_dyndns.c   |  339 +++++++++++
 src/tests/common.h               |    2 +
 src/tests/common_dom.c           |    9 +-
 src/tests/common_tev.c           |   25 +
 src/util/util_errors.c           |    3 +
 src/util/util_errors.h           |    3 +
 13 files changed, 1969 insertions(+), 1177 deletions(-)
 create mode 100644 src/providers/dp_dyndns.c
 create mode 100644 src/providers/dp_dyndns.h
 create mode 100644 src/providers/ldap/sdap_dyndns.c
 create mode 100644 src/providers/ldap/sdap_dyndns.h
 create mode 100644 src/tests/cmocka/test_dyndns.c

diff --git a/Makefile.am b/Makefile.am
index aadb2ff6af29f7d058fbf5c8c628976e52b802f2..534e1327999dfb34354659a5e2a222d0c594ec5f 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -148,7 +148,8 @@ if HAVE_CMOCKA
     non_interactive_cmocka_based_tests = \
         nss-srv-tests \
         test-find-uid \
-        test-io
+        test-io	      \
+        dyndns-tests
 endif
 
 check_PROGRAMS = \
@@ -432,6 +433,7 @@ dist_noinst_HEADERS = \
     src/confdb/confdb_setup.h \
     src/providers/data_provider.h \
     src/providers/dp_backend.h \
+    src/providers/dp_dyndns.h \
     src/providers/fail_over.h \
     src/providers/fail_over_srv.h \
     src/util/child_common.h \
@@ -453,6 +455,7 @@ dist_noinst_HEADERS = \
     src/providers/ldap/ldap_opts.h \
     src/providers/ldap/sdap_range.h \
     src/providers/ldap/sdap_users.h \
+    src/providers/ldap/sdap_dyndns.h \
     src/providers/ipa/ipa_common.h \
     src/providers/ipa/ipa_config.h \
     src/providers/ipa/ipa_access.h \
@@ -687,6 +690,7 @@ sssd_be_SOURCES = \
     src/providers/data_provider_fo.c \
     src/providers/data_provider_opts.c \
     src/providers/data_provider_callbacks.c \
+    src/providers/dp_dyndns.c \
     $(SSSD_FAILOVER_OBJ)
 sssd_be_LDADD = \
     -ldl \
@@ -1193,8 +1197,9 @@ krb5_child_test_LDADD = \
     libsss_test_common.la
 
 if HAVE_CMOCKA
-TEST_MOCK_OBJ = \
-     src/tests/common.c
+TEST_MOCK_OBJ = 		\
+     src/tests/common.c		\
+     src/tests/leak_check.c
 
 TEST_MOCK_RESP_OBJ = \
      $(TEST_MOCK_OBJ) \
@@ -1252,6 +1257,23 @@ test_io_CFLAGS = \
     $(AM_CFLAGS)
 test_io_LDADD = \
     $(CMOCKA_LIBS)
+
+dyndns_tests_SOURCES = \
+     $(TEST_MOCK_OBJ) \
+     $(SSSD_RESOLV_OBJ) \
+     src/tests/common_tev.c \
+     src/tests/cmocka/test_dyndns.c
+dyndns_tests_CFLAGS = \
+    $(AM_CFLAGS) \
+    -DDYNDNS_TIMEOUT=2
+dyndns_tests_LDFLAGS = \
+    -Wl,-wrap,execv \
+    -Wl,-wrap,getifaddrs \
+    -Wl,-wrap,freeifaddrs
+dyndns_tests_LDADD = \
+    $(CARES_LIBS) \
+    $(CMOCKA_LIBS) \
+    libsss_util.la
 endif
 
 noinst_PROGRAMS = pam_test_client
@@ -1392,6 +1414,7 @@ libsss_ldap_common_la_SOURCES = \
     src/providers/ldap/sdap_idmap.h \
     src/providers/ldap/sdap_range.c \
     src/providers/ldap/sdap_reinit.c \
+    src/providers/ldap/sdap_dyndns.c \
     src/providers/ldap/sdap.c
 libsss_ldap_common_la_LDFLAGS = \
     -avoid-version
diff --git a/src/providers/dp_dyndns.c b/src/providers/dp_dyndns.c
new file mode 100644
index 0000000000000000000000000000000000000000..7e5cc690e1d199f9c00b0b3d18168e5987ffc31f
--- /dev/null
+++ b/src/providers/dp_dyndns.c
@@ -0,0 +1,880 @@
+/*
+    SSSD
+
+    dp_dyndns.c
+
+    Authors:
+        Stephen Gallagher <sgallagh at redhat.com>
+        Jakub Hrozek <jhrozek at redhat.com>
+
+    Copyright (C) 2013 Red Hat
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <arpa/inet.h>
+#include <net/if.h>
+#include <ifaddrs.h>
+#include <ctype.h>
+#include "util/util.h"
+#include "confdb/confdb.h"
+#include "util/child_common.h"
+#include "providers/data_provider.h"
+#include "providers/dp_backend.h"
+#include "providers/dp_dyndns.h"
+#include "resolv/async_resolv.h"
+
+#ifndef DYNDNS_TIMEOUT
+#define DYNDNS_TIMEOUT 15
+#endif /* DYNDNS_TIMEOUT */
+
+struct sss_iface_addr {
+    struct sss_iface_addr *next;
+    struct sss_iface_addr *prev;
+
+    struct sockaddr_storage *addr;
+};
+
+struct sss_iface_addr *
+sss_iface_addr_add(TALLOC_CTX *mem_ctx, struct sss_iface_addr **list,
+                   struct sockaddr_storage *ss)
+{
+    struct sss_iface_addr *address;
+
+    address = talloc(mem_ctx, struct sss_iface_addr);
+    if (address == NULL) {
+        return NULL;
+    }
+
+    address->addr = talloc_memdup(address, ss,
+                                  sizeof(struct sockaddr_storage));
+    if(address->addr == NULL) {
+        talloc_zfree(address);
+        return NULL;
+    }
+    DLIST_ADD(*list, address);
+
+    return address;
+}
+
+errno_t
+sss_iface_addr_list_as_str_list(TALLOC_CTX *mem_ctx,
+                                struct sss_iface_addr *ifaddr_list,
+                                char ***_straddrs)
+{
+    struct sss_iface_addr *ifaddr;
+    size_t count;
+    int ai;
+    char **straddrs;
+    const char *ip;
+    char ip_addr[INET6_ADDRSTRLEN];
+    errno_t ret;
+
+    count = 0;
+    DLIST_FOR_EACH(ifaddr, ifaddr_list) {
+        count++;
+    }
+
+    straddrs = talloc_array(mem_ctx, char *, count+1);
+    if (straddrs == NULL) {
+        return ENOMEM;
+    }
+
+    ai = 0;
+    DLIST_FOR_EACH(ifaddr, ifaddr_list) {
+        switch(ifaddr->addr->ss_family) {
+        case AF_INET:
+            errno = 0;
+            ip = inet_ntop(ifaddr->addr->ss_family,
+                           &(((struct sockaddr_in *)ifaddr->addr)->sin_addr),
+                           ip_addr, INET6_ADDRSTRLEN);
+            if (ip == NULL) {
+                ret = errno;
+                goto fail;
+            }
+            break;
+
+        case AF_INET6:
+            errno = 0;
+            ip = inet_ntop(ifaddr->addr->ss_family,
+                           &(((struct sockaddr_in6 *)ifaddr->addr)->sin6_addr),
+                           ip_addr, INET6_ADDRSTRLEN);
+            if (ip == NULL) {
+                ret = errno;
+                goto fail;
+            }
+            break;
+
+        default:
+            DEBUG(SSSDBG_CRIT_FAILURE, ("Unknown address family\n"));
+            continue;
+        }
+
+        straddrs[ai] = talloc_strdup(straddrs, ip);
+        if (straddrs[ai] == NULL) {
+            ret = ENOMEM;
+            goto fail;
+        }
+        ai++;
+    }
+
+    straddrs[count] = NULL;
+    *_straddrs = straddrs;
+    return EOK;
+
+fail:
+    talloc_free(straddrs);
+    return ret;
+}
+
+static bool
+ok_for_dns(struct sockaddr *sa)
+{
+    char straddr[INET6_ADDRSTRLEN];
+    struct in6_addr *addr6;
+    struct in_addr *addr;
+
+    switch (sa->sa_family) {
+    case AF_INET6:
+        addr6 = &((struct sockaddr_in6 *) sa)->sin6_addr;
+
+        if (inet_ntop(AF_INET6, addr6, straddr, INET6_ADDRSTRLEN) == NULL) {
+            DEBUG(SSSDBG_MINOR_FAILURE,
+                  ("inet_ntop failed, won't log IP addresses\n"));
+            snprintf(straddr, INET6_ADDRSTRLEN, "unknown");
+        }
+
+        if (IN6_IS_ADDR_LINKLOCAL(addr6)) {
+            DEBUG(SSSDBG_FUNC_DATA, ("Link local IPv6 address %s\n", straddr));
+            return false;
+        } else if (IN6_IS_ADDR_LOOPBACK(addr6)) {
+            DEBUG(SSSDBG_FUNC_DATA, ("Loopback IPv6 address %s\n", straddr));
+            return false;
+        } else if (IN6_IS_ADDR_MULTICAST(addr6)) {
+            DEBUG(SSSDBG_FUNC_DATA, ("Multicast IPv6 address %s\n", straddr));
+            return false;
+        }
+        break;
+    case AF_INET:
+        addr = &((struct sockaddr_in *) sa)->sin_addr;
+
+        if (inet_ntop(AF_INET, addr, straddr, INET6_ADDRSTRLEN) == NULL) {
+            DEBUG(SSSDBG_MINOR_FAILURE,
+                  ("inet_ntop failed, won't log IP addresses\n"));
+            snprintf(straddr, INET6_ADDRSTRLEN, "unknown");
+        }
+
+        if (IN_MULTICAST(ntohl(addr->s_addr))) {
+            DEBUG(SSSDBG_FUNC_DATA, ("Multicast IPv4 address %s\n", straddr));
+            return false;
+        } else if (inet_netof(*addr) == IN_LOOPBACKNET) {
+            DEBUG(SSSDBG_FUNC_DATA, ("Loopback IPv4 address %s\n", straddr));
+            return false;
+        } else if ((addr->s_addr & 0xffff0000) == 0xa9fe0000) {
+            /* 169.254.0.0/16 */
+            DEBUG(SSSDBG_FUNC_DATA, ("Link-local IPv4 address %s\n", straddr));
+            return false;
+        } else if (addr->s_addr == htonl(INADDR_BROADCAST)) {
+            DEBUG(SSSDBG_FUNC_DATA, ("Broadcast IPv4 address %s\n", straddr));
+            return false;
+        }
+        break;
+    default:
+        DEBUG(SSSDBG_CRIT_FAILURE, ("Unknown address family\n"));
+        return false;
+    }
+
+    return true;
+}
+
+/* Collect IP addresses associated with an interface */
+errno_t
+sss_iface_addr_list_get(TALLOC_CTX *mem_ctx, const char *ifname,
+                        struct sss_iface_addr **_addrlist)
+{
+    struct ifaddrs *ifaces = NULL;
+    struct ifaddrs *ifa;
+    errno_t ret;
+    size_t addrsize;
+    struct sss_iface_addr *address;
+    struct sss_iface_addr *addrlist = NULL;
+
+    /* Get the IP addresses associated with the
+     * specified interface
+     */
+    errno = 0;
+    ret = getifaddrs(&ifaces);
+    if (ret == -1) {
+        ret = errno;
+        DEBUG(SSSDBG_OP_FAILURE,
+              ("Could not read interfaces [%d][%s]\n", ret, strerror(ret)));
+        goto done;
+    }
+
+    for (ifa = ifaces; ifa != NULL; ifa = ifa->ifa_next) {
+        /* Some interfaces don't have an ifa_addr */
+        if (!ifa->ifa_addr) continue;
+
+        /* Add IP addresses to the list */
+        if ((ifa->ifa_addr->sa_family == AF_INET ||
+             ifa->ifa_addr->sa_family == AF_INET6) &&
+             strcasecmp(ifa->ifa_name, ifname) == 0 &&
+             ok_for_dns(ifa->ifa_addr)) {
+
+            /* Add this address to the IP address list */
+            address = talloc_zero(mem_ctx, struct sss_iface_addr);
+            if (!address) {
+                goto done;
+            }
+
+            addrsize = ifa->ifa_addr->sa_family == AF_INET ? \
+                                sizeof(struct sockaddr_in) : \
+                                sizeof(struct sockaddr_in6);
+
+            address->addr = talloc_memdup(address, ifa->ifa_addr,
+                                          addrsize);
+            if (address->addr == NULL) {
+                ret = ENOMEM;
+                goto done;
+            }
+            DLIST_ADD(addrlist, address);
+        }
+    }
+
+    ret = EOK;
+    *_addrlist = addrlist;
+done:
+    freeifaddrs(ifaces);
+    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)
+{
+    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;
+    }
+
+    /* Remove existing entries as needed */
+    if (remove_af & DYNDNS_REMOVE_A) {
+        update_msg = talloc_asprintf_append(update_msg,
+                                            "update delete %s. in A\nsend\n",
+                                            hostname);
+        if (update_msg == NULL) {
+            ret = ENOMEM;
+            goto done;
+        }
+    }
+    if (remove_af & DYNDNS_REMOVE_AAAA) {
+        update_msg = talloc_asprintf_append(update_msg,
+                                            "update delete %s. in AAAA\nsend\n",
+                                            hostname);
+        if (update_msg == NULL) {
+            ret = ENOMEM;
+            goto done;
+        }
+    }
+
+    DLIST_FOR_EACH(new_record, addresses) {
+        switch(new_record->addr->ss_family) {
+        case AF_INET:
+            ip = inet_ntop(new_record->addr->ss_family,
+                           &(((struct sockaddr_in *)new_record->addr)->sin_addr),
+                           ip_addr, INET6_ADDRSTRLEN);
+            if (ip == NULL) {
+                ret = errno;
+                goto done;
+            }
+            break;
+
+        case AF_INET6:
+            ip = inet_ntop(new_record->addr->ss_family,
+                           &(((struct sockaddr_in6 *)new_record->addr)->sin6_addr),
+                           ip_addr, INET6_ADDRSTRLEN);
+            if (ip == NULL) {
+                ret = errno;
+                goto done;
+            }
+            break;
+
+        default:
+            DEBUG(SSSDBG_CRIT_FAILURE, ("Unknown address family\n"));
+            ret = EINVAL;
+            goto done;
+        }
+
+        /* Format the record update */
+        update_msg = talloc_asprintf_append(update_msg,
+                "update add %s. %d in %s %s\n",
+                hostname, ttl,
+                new_record->addr->ss_family == AF_INET ? "A" : "AAAA",
+                ip_addr);
+        if (update_msg == NULL) {
+            ret = ENOMEM;
+            goto done;
+        }
+    }
+
+    update_msg = talloc_asprintf_append(update_msg, "send\n");
+    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:
+    talloc_free(tmp_ctx);
+    return ret;
+}
+
+struct nsupdate_get_addrs_state {
+    struct tevent_context *ev;
+    struct be_resolv_ctx *be_res;
+    enum host_database *db;
+    const char *hostname;
+
+    /* Use sss_addr in this request */
+    char **addrlist;
+    size_t count;
+};
+
+static void nsupdate_get_addrs_done(struct tevent_req *subreq);
+
+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 ret;
+    struct tevent_req *req;
+    struct tevent_req *subreq;
+    struct nsupdate_get_addrs_state *state;
+
+    req = tevent_req_create(mem_ctx, &state, struct nsupdate_get_addrs_state);
+    if (req == NULL) {
+        return NULL;
+    }
+    state->be_res = be_res;
+    state->ev = ev;
+    state->hostname = talloc_strdup(state, hostname);
+    if (state->hostname == NULL) {
+        ret = ENOMEM;
+        goto done;
+    }
+
+    state->db = talloc_array(state, enum host_database, 2);
+    if (state->db == NULL) {
+        ret = ENOMEM;
+        goto done;
+    }
+    state->db[0] = DB_DNS;
+    state->db[1] = DB_SENTINEL;
+
+    subreq = resolv_gethostbyname_send(state, ev, be_res->resolv, hostname,
+                                       state->be_res->family_order,
+                                       state->db);
+    if (subreq == NULL) {
+        ret = ENOMEM;
+        goto done;
+    }
+    tevent_req_set_callback(subreq, nsupdate_get_addrs_done, req);
+
+    ret = ERR_OK;
+done:
+    if (ret != ERR_OK) {
+        tevent_req_error(req, ret);
+        tevent_req_post(req, ev);
+    }
+    return req;
+}
+
+static void
+nsupdate_get_addrs_done(struct tevent_req *subreq)
+{
+    errno_t ret;
+    size_t count;
+    struct tevent_req *req =
+            tevent_req_callback_data(subreq, struct tevent_req);
+    struct nsupdate_get_addrs_state *state = tevent_req_data(req,
+                                     struct nsupdate_get_addrs_state);
+    struct resolv_hostent *rhostent;
+    int i;
+    int resolv_status;
+
+    ret = resolv_gethostbyname_recv(subreq, state, &resolv_status, NULL,
+                                    &rhostent);
+    talloc_zfree(subreq);
+
+    /* If the retry did not match, simply quit */
+    if (ret == ENOENT) {
+        /* If the resolver is set to honor both address families
+         * it automatically retries the other one internally, so ENOENT
+         * means neither matched and we can simply quit.
+         */
+        ret = EOK;
+        goto done;
+    } else if (ret != EOK) {
+        DEBUG(SSSDBG_OP_FAILURE,
+              ("Could not resolve address for this machine, error [%d]: %s, "
+               "resolver returned: [%d]: %s\n", ret, sss_strerror(ret),
+               resolv_status, resolv_strerror(resolv_status)));
+        goto done;
+    }
+
+    /* EOK */
+
+    if (rhostent->addr_list) {
+        for (count=0; rhostent->addr_list[count]; count++);
+    } else {
+        /* The address list is NULL. This is probably a bug in
+         * c-ares, but we need to handle it gracefully.
+         */
+        DEBUG(SSSDBG_MINOR_FAILURE,
+              ("Lookup of [%s] returned no addresses. Skipping.\n",
+               rhostent->name));
+        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);
+
+        if (state->addrlist[state->count + i] == NULL) {
+            ret = ENOMEM;
+            goto done;
+        }
+    }
+    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
+     * get the complete list.
+     */
+    if (((state->be_res->family_order == IPV4_FIRST &&
+          rhostent->family == AF_INET) ||
+        (state->be_res->family_order == IPV6_FIRST &&
+         rhostent->family == AF_INET6))) {
+
+        state->be_res->family_order = \
+                            (state->be_res->family_order == IPV4_FIRST) ? \
+                             IPV6_ONLY : \
+                             IPV4_ONLY;
+
+        subreq = resolv_gethostbyname_send(state, state->ev,
+                                           state->be_res->resolv,
+                                           state->hostname,
+                                           state->be_res->family_order,
+                                           state->db);
+        if (!subreq) {
+            ret = ENOMEM;
+            goto done;
+        }
+        tevent_req_set_callback(subreq, nsupdate_get_addrs_done, req);
+        return;
+    }
+
+    /* The second address matched either immediatelly or after a retry.
+     * No need to retry again. */
+    ret = EOK;
+
+done:
+    if (ret == EOK) {
+        /* All done */
+        tevent_req_done(req);
+    } else if (ret != EAGAIN) {
+        DEBUG(SSSDBG_OP_FAILURE,
+              ("nsupdate_get_addrs_done failed: [%d]: [%s]\n",
+               sss_strerror(ret)));
+        tevent_req_error(req, ret);
+    }
+    /* EAGAIN - another lookup in progress */
+}
+
+errno_t
+nsupdate_get_addrs_recv(struct tevent_req *req,
+                        TALLOC_CTX *mem_ctx,
+                        char ***_addrlist)
+{
+    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);
+    return EOK;
+}
+
+/* Write the nsupdate_msg into the already forked child, wait until
+ * the child finishes
+ *
+ * This is not a typical tevent_req styled request as it ends either after
+ * a timeout or when the child finishes operation.
+ */
+struct nsupdate_child_state {
+    int pipefd_to_child;
+    struct tevent_timer *timeout_handler;
+
+    int child_status;
+};
+
+static void
+nsupdate_child_timeout(struct tevent_context *ev,
+                       struct tevent_timer *te,
+                       struct timeval tv, void *pvt);
+static void
+nsupdate_child_handler(int child_status,
+                       struct tevent_signal *sige,
+                       void *pvt);
+
+static void nsupdate_child_stdin_done(struct tevent_req *subreq);
+
+static struct tevent_req *
+nsupdate_child_send(TALLOC_CTX *mem_ctx,
+                    struct tevent_context *ev,
+                    int pipefd_to_child,
+                    pid_t child_pid,
+                    char *child_stdin)
+{
+    errno_t ret;
+    struct tevent_req *req;
+    struct tevent_req *subreq;
+    struct nsupdate_child_state *state;
+    struct timeval tv;
+
+    req = tevent_req_create(mem_ctx, &state, struct nsupdate_child_state);
+    if (req == NULL) {
+        return NULL;
+    }
+    state->pipefd_to_child = pipefd_to_child;
+
+    /* Set up SIGCHLD handler */
+    ret = child_handler_setup(ev, child_pid, nsupdate_child_handler, req);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_OP_FAILURE, ("Could not set up child handlers [%d]: %s\n",
+              ret, sss_strerror(ret)));
+        ret = ERR_DYNDNS_FAILED;
+        goto done;
+    }
+
+    /* Set up timeout handler */
+    tv = tevent_timeval_current_ofs(DYNDNS_TIMEOUT, 0);
+    state->timeout_handler = tevent_add_timer(ev, req, tv,
+                                              nsupdate_child_timeout, req);
+    if(state->timeout_handler == NULL) {
+        ret = ERR_DYNDNS_FAILED;
+        goto done;
+    }
+
+    /* Write the update message to the nsupdate child */
+    subreq = write_pipe_send(req, ev,
+                             (uint8_t *) child_stdin,
+                             strlen(child_stdin)+1,
+                             state->pipefd_to_child);
+    if (subreq == NULL) {
+        ret = ERR_DYNDNS_FAILED;
+        goto done;
+    }
+    tevent_req_set_callback(subreq, nsupdate_child_stdin_done, req);
+
+    ret = EOK;
+done:
+    if (ret != EOK) {
+        tevent_req_error(req, ret);
+        tevent_req_post(req, ev);
+    }
+    return req;
+}
+
+static void
+nsupdate_child_timeout(struct tevent_context *ev,
+                       struct tevent_timer *te,
+                       struct timeval tv, void *pvt)
+{
+    struct tevent_req *req =
+            talloc_get_type(pvt, struct tevent_req);
+    struct nsupdate_child_state *state =
+            tevent_req_data(req, struct nsupdate_child_state);
+
+    DEBUG(SSSDBG_CRIT_FAILURE, ("Timeout reached for dynamic DNS update\n"));
+    state->child_status = ETIMEDOUT;
+    tevent_req_error(req, ERR_DYNDNS_TIMEOUT);
+}
+
+static void
+nsupdate_child_stdin_done(struct tevent_req *subreq)
+{
+    errno_t ret;
+    struct tevent_req *req =
+            tevent_req_callback_data(subreq, struct tevent_req);
+    struct nsupdate_child_state *state =
+            tevent_req_data(req, struct nsupdate_child_state);
+
+    /* Verify that the buffer was sent, then return
+     * and wait for the sigchld handler to finish.
+     */
+    DEBUG(SSSDBG_TRACE_LIBS, ("Sending nsupdate data complete\n"));
+
+    ret = write_pipe_recv(subreq);
+    talloc_zfree(subreq);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_OP_FAILURE, ("Sending nsupdate data failed [%d]: %s\n",
+              ret, sss_strerror(ret)));
+        tevent_req_error(req, ERR_DYNDNS_FAILED);
+        return;
+    }
+
+    close(state->pipefd_to_child);
+    state->pipefd_to_child = -1;
+
+    /* Now either wait for the timeout to fire or the child
+     * to finish
+     */
+}
+
+static void
+nsupdate_child_handler(int child_status,
+                       struct tevent_signal *sige,
+                       void *pvt)
+{
+    struct tevent_req *req = talloc_get_type(pvt, struct tevent_req);
+    struct nsupdate_child_state *state =
+            tevent_req_data(req, struct nsupdate_child_state);
+
+    state->child_status = child_status;
+
+    if (WIFEXITED(child_status) && WEXITSTATUS(child_status) != 0) {
+        DEBUG(SSSDBG_OP_FAILURE,
+              ("Dynamic DNS child failed with status [%d]\n", child_status));
+        tevent_req_error(req, ERR_DYNDNS_FAILED);
+        return;
+    }
+
+    if (WIFSIGNALED(child_status)) {
+        DEBUG(SSSDBG_OP_FAILURE,
+              ("Dynamic DNS child was terminated by signal [%d]\n",
+               WTERMSIG(child_status)));
+        tevent_req_error(req, ERR_DYNDNS_FAILED);
+        return;
+    }
+
+    tevent_req_done(req);
+}
+
+static errno_t
+nsupdate_child_recv(struct tevent_req *req, int *child_status)
+{
+    struct nsupdate_child_state *state =
+            tevent_req_data(req, struct nsupdate_child_state);
+
+    *child_status = state->child_status;
+
+    TEVENT_REQ_RETURN_ON_ERROR(req);
+
+    return ERR_OK;
+}
+
+/* Fork a nsupdate child, write the nsupdate_msg into stdin and wait for the child
+ * to finish one way or another
+ */
+struct be_nsupdate_state {
+    int child_status;
+};
+
+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)
+{
+    int pipefd_to_child[2];
+    pid_t child_pid;
+    errno_t ret;
+    struct tevent_req *req = NULL;
+    struct tevent_req *subreq = NULL;
+    struct be_nsupdate_state *state;
+    char *args[3];
+
+    req = tevent_req_create(mem_ctx, &state, struct be_nsupdate_state);
+    if (req == NULL) {
+        return NULL;
+    }
+    state->child_status = 0;
+
+    ret = pipe(pipefd_to_child);
+    if (ret == -1) {
+        ret = errno;
+        DEBUG(SSSDBG_CRIT_FAILURE,
+              ("pipe failed [%d][%s].\n", ret, strerror(ret)));
+        goto done;
+    }
+
+    child_pid = fork();
+
+    if (child_pid == 0) { /* child */
+        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;
+        }
+
+        close(pipefd_to_child[1]);
+        ret = dup2(pipefd_to_child[0], STDIN_FILENO);
+        if (ret == -1) {
+            ret = errno;
+            DEBUG(SSSDBG_CRIT_FAILURE,
+                  ("dup2 failed [%d][%s].\n", ret, strerror(ret)));
+            goto done;
+        }
+
+        errno = 0;
+        execv(NSUPDATE_PATH, args);
+        /* The child should never end up here */
+        ret = errno;
+        DEBUG(SSSDBG_CRIT_FAILURE, ("execv failed [%d][%s].\n", ret, strerror(ret)));
+        goto done;
+    } else if (child_pid > 0) { /* parent */
+        close(pipefd_to_child[0]);
+
+        subreq = nsupdate_child_send(state, ev, pipefd_to_child[1],
+                                     child_pid, nsupdate_msg);
+        if (subreq == NULL) {
+            ret = ERR_DYNDNS_FAILED;
+            goto done;
+        }
+        tevent_req_set_callback(subreq, be_nsupdate_done, req);
+    } else { /* error */
+        ret = errno;
+        DEBUG(SSSDBG_CRIT_FAILURE,
+              ("fork failed [%d][%s].\n", ret, strerror(ret)));
+        goto done;
+    }
+
+    ret = EOK;
+done:
+    if (ret != EOK) {
+        tevent_req_error(req, ret);
+        tevent_req_post(req, ev);
+    }
+    return req;
+}
+
+static void
+be_nsupdate_done(struct tevent_req *subreq)
+{
+    struct tevent_req *req =
+            tevent_req_callback_data(subreq, struct tevent_req);
+    struct be_nsupdate_state *state =
+            tevent_req_data(req, struct be_nsupdate_state);
+    errno_t ret;
+
+    ret = nsupdate_child_recv(subreq, &state->child_status);
+    talloc_zfree(subreq);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_OP_FAILURE, ("nsupdate child execution failed [%d]: %s\n",
+              ret, sss_strerror(ret)));
+        tevent_req_error(req, ret);
+        return;
+    }
+
+    DEBUG(SSSDBG_FUNC_DATA,
+           ("nsupdate child status: %d\n", state->child_status));
+    tevent_req_done(req);
+}
+
+errno_t
+be_nsupdate_recv(struct tevent_req *req, int *child_status)
+{
+    struct be_nsupdate_state *state =
+            tevent_req_data(req, struct be_nsupdate_state);
+
+    *child_status = state->child_status;
+
+    TEVENT_REQ_RETURN_ON_ERROR(req);
+
+    return EOK;
+}
diff --git a/src/providers/dp_dyndns.h b/src/providers/dp_dyndns.h
new file mode 100644
index 0000000000000000000000000000000000000000..b0020560a8accf8811f311c592a6bf436111156a
--- /dev/null
+++ b/src/providers/dp_dyndns.h
@@ -0,0 +1,68 @@
+/*
+    SSSD
+
+    dp_dyndns.h
+
+    Authors:
+        Jakub Hrozek <jhrozek at redhat.com>
+
+    Copyright (C) 2013 Red Hat
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/* dynamic dns helpers */
+struct sss_iface_addr;
+
+#define DYNDNS_REMOVE_A     0x1
+#define DYNDNS_REMOVE_AAAA  0x2
+
+errno_t
+sss_iface_addr_list_get(TALLOC_CTX *mem_ctx, const char *ifname,
+                        struct sss_iface_addr **_addrlist);
+
+struct sss_iface_addr *
+sss_iface_addr_add(TALLOC_CTX *mem_ctx, struct sss_iface_addr **list,
+                   struct sockaddr_storage *ss);
+
+errno_t
+sss_iface_addr_list_as_str_list(TALLOC_CTX *mem_ctx,
+                                struct sss_iface_addr *ifaddr_list,
+                                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);
+
+/* Returns:
+ *    * ERR_OK              - on success
+ *    * ERR_DYNDNS_FAILED   - if nsupdate fails for any reason
+ *    * ERR_DYNDNS_TIMEOUT  - if the update times out. child_status
+ *                            is ETIMEDOUT in this case
+ */
+struct tevent_req *be_nsupdate_send(TALLOC_CTX *mem_ctx,
+                                    struct tevent_context *ev,
+                                    char *nsupdate_msg);
+errno_t be_nsupdate_recv(struct tevent_req *req, int *child_status);
+
+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);
diff --git a/src/providers/ipa/ipa_common.h b/src/providers/ipa/ipa_common.h
index ae1c91731e9637c06dad555d1a87ef2e0cf53184..52a0afe19ccfbfd143e4787b3cd05527291ac3d6 100644
--- a/src/providers/ipa/ipa_common.h
+++ b/src/providers/ipa/ipa_common.h
@@ -138,7 +138,7 @@ struct ipa_options {
     /* id provider */
     struct sdap_options *id;
     struct ipa_id_ctx *id_ctx;
-    struct resolv_ctx *resolv;
+    struct be_resolv_ctx *be_res;
 
     /* auth and chpass provider */
     struct dp_option *auth;
diff --git a/src/providers/ipa/ipa_dyndns.c b/src/providers/ipa/ipa_dyndns.c
index 0bd8ad002c82fdfce6834c9b0209a8b69536a7bf..79918a261be9c15a4f86c2885ac8723b5ed51cb6 100644
--- a/src/providers/ipa/ipa_dyndns.c
+++ b/src/providers/ipa/ipa_dyndns.c
@@ -22,863 +22,94 @@
     along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
 
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <sys/ioctl.h>
-#include <arpa/inet.h>
-#include <net/if.h>
-#include <ifaddrs.h>
 #include <ctype.h>
 #include "util/util.h"
-#include "confdb/confdb.h"
+#include "providers/ldap/sdap_dyndns.h"
 #include "providers/ipa/ipa_common.h"
 #include "providers/ipa/ipa_dyndns.h"
-#include "util/child_common.h"
 #include "providers/data_provider.h"
-#include "providers/ldap/ldap_common.h"
-#include "providers/ldap/sdap_async_private.h"
-#include "resolv/async_resolv.h"
-
-#define IPA_DYNDNS_TIMEOUT 15
-
-#define IPA_DYNDNS_REMOVE_A     0x1
-#define IPA_DYNDNS_REMOVE_AAAA  0x2
-
-struct ipa_ipaddress {
-    struct ipa_ipaddress *next;
-    struct ipa_ipaddress *prev;
-
-    struct sockaddr_storage *addr;
-    bool matched;
-};
-
-struct ipa_dyndns_ctx {
-    struct ipa_options *ipa_ctx;
-    struct sdap_id_op* sdap_op;
-    char *hostname;
-    struct ipa_ipaddress *addresses;
-    bool use_server_with_nsupdate;
-    uint8_t remove_af;
-    enum restrict_family family_order;
-};
-
-
-static struct tevent_req * ipa_dyndns_update_send(struct ipa_options *ctx);
-
-static void ipa_dyndns_update_done(struct tevent_req *req);
-
-static errno_t
-ipa_ipaddress_list_as_string_list(TALLOC_CTX *mem_ctx,
-                                  struct ipa_ipaddress *ipa_addr_list,
-                                  char ***_straddrs)
-{
-    struct ipa_ipaddress *ipa_addr;
-    size_t count;
-    int ai;
-    char **straddrs;
-    const char *ip;
-    char ip_addr[INET6_ADDRSTRLEN];
-    errno_t ret;
-
-    count = 0;
-    DLIST_FOR_EACH(ipa_addr, ipa_addr_list) {
-        count++;
-    }
-
-    straddrs = talloc_array(mem_ctx, char *, count+1);
-    if (straddrs == NULL) {
-        return ENOMEM;
-    }
-
-    ai = 0;
-    DLIST_FOR_EACH(ipa_addr, ipa_addr_list) {
-        switch(ipa_addr->addr->ss_family) {
-        case AF_INET:
-            errno = 0;
-            ip = inet_ntop(ipa_addr->addr->ss_family,
-                           &(((struct sockaddr_in *)ipa_addr->addr)->sin_addr),
-                           ip_addr, INET6_ADDRSTRLEN);
-            if (ip == NULL) {
-                ret = errno;
-                goto fail;
-            }
-            break;
-
-        case AF_INET6:
-            errno = 0;
-            ip = inet_ntop(ipa_addr->addr->ss_family,
-                           &(((struct sockaddr_in6 *)ipa_addr->addr)->sin6_addr),
-                           ip_addr, INET6_ADDRSTRLEN);
-            if (ip == NULL) {
-                ret = errno;
-                goto fail;
-            }
-            break;
-
-        default:
-            DEBUG(0, ("Unknown address family\n"));
-            continue;
-        }
-
-        straddrs[ai] = talloc_strdup(straddrs, ip);
-        if (straddrs[ai] == NULL) {
-            ret = ENOMEM;
-            goto fail;
-        }
-        ai++;
-    }
-
-    straddrs[count] = NULL;
-    *_straddrs = straddrs;
-    return EOK;
-
-fail:
-    talloc_free(straddrs);
-    return ret;
-}
 
+void ipa_dyndns_update(void *pvt);
 
 errno_t ipa_dyndns_init(struct be_ctx *be_ctx,
                         struct ipa_options *ctx)
 {
     errno_t ret;
 
-    ctx->resolv = be_ctx->be_res->resolv;
+    ctx->be_res = be_ctx->be_res;
+    if (ctx->be_res == NULL) {
+        DEBUG(SSSDBG_OP_FAILURE, ("Resolver must be initialized in order "
+              "to use the IPA dynamic DNS updates\n"));
+        return EINVAL;
+    }
 
     ret = be_add_online_cb(be_ctx, be_ctx,
                            ipa_dyndns_update,
                            ctx, NULL);
     if (ret != EOK) {
-        DEBUG(1, ("Could not set up online callback\n"));
+        DEBUG(SSSDBG_CRIT_FAILURE, ("Could not set up online callback\n"));
         return ret;
     }
 
     return EOK;
 }
 
+static struct tevent_req *ipa_dyndns_update_send(struct ipa_options *ctx);
+static errno_t ipa_dyndns_update_recv(struct tevent_req *req);
+
+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 tevent_req *req = ipa_dyndns_update_send(ctx);
     if (req == NULL) {
-        DEBUG(1, ("Could not update DNS\n"));
+        DEBUG(SSSDBG_CRIT_FAILURE, ("Could not update DNS\n"));
         return;
     }
-    tevent_req_set_callback(req, ipa_dyndns_update_done, NULL);
+    tevent_req_set_callback(req, ipa_dyndns_nsupdate_done, NULL);
 }
 
-static bool ok_for_dns(struct sockaddr *sa)
+static void ipa_dyndns_nsupdate_done(struct tevent_req *req)
 {
-    char straddr[INET6_ADDRSTRLEN];
-
-    if (sa->sa_family == AF_INET6) {
-        struct in6_addr *addr = &((struct sockaddr_in6 *) sa)->sin6_addr;
-
-        if (inet_ntop(AF_INET6, addr, straddr, INET6_ADDRSTRLEN) == NULL) {
-            DEBUG(SSSDBG_MINOR_FAILURE,
-                  ("inet_ntop failed, won't log IP addresses\n"));
-            snprintf(straddr, INET6_ADDRSTRLEN, "unknown");
-        }
-
-        if (IN6_IS_ADDR_LINKLOCAL(addr)) {
-            DEBUG(SSSDBG_FUNC_DATA, ("Link local IPv6 address %s\n", straddr));
-            return false;
-        } else if (IN6_IS_ADDR_LOOPBACK(addr)) {
-            DEBUG(SSSDBG_FUNC_DATA, ("Loopback IPv6 address %s\n", straddr));
-            return false;
-        } else if (IN6_IS_ADDR_MULTICAST(addr)) {
-            DEBUG(SSSDBG_FUNC_DATA, ("Multicast IPv6 address %s\n", straddr));
-            return false;
-        }
-    } else if (sa->sa_family == AF_INET) {
-        struct in_addr *addr = &((struct sockaddr_in *) sa)->sin_addr;
-
-        if (inet_ntop(AF_INET, addr, straddr, INET6_ADDRSTRLEN) == NULL) {
-            DEBUG(SSSDBG_MINOR_FAILURE,
-                  ("inet_ntop failed, won't log IP addresses\n"));
-            snprintf(straddr, INET6_ADDRSTRLEN, "unknown");
-        }
-
-        if (IN_MULTICAST(ntohl(addr->s_addr))) {
-            DEBUG(SSSDBG_FUNC_DATA, ("Multicast IPv4 address %s\n", straddr));
-            return false;
-        } else if (inet_netof(*addr) == IN_LOOPBACKNET) {
-            DEBUG(SSSDBG_FUNC_DATA, ("Loopback IPv4 address %s\n", straddr));
-            return false;
-        } else if ((addr->s_addr & 0xffff0000) == 0xa9fe0000) {
-            /* 169.254.0.0/16 */
-            DEBUG(SSSDBG_FUNC_DATA, ("Link-local IPv4 address %s\n", straddr));
-            return false;
-        } else if (addr->s_addr == htonl(INADDR_BROADCAST)) {
-            DEBUG(SSSDBG_FUNC_DATA, ("Broadcast IPv4 address %s\n", straddr));
-            return false;
-        }
-    } else {
-        DEBUG(SSSDBG_CRIT_FAILURE, ("Unknown address family\n"));
-        return false;
+    int ret = ipa_dyndns_update_recv(req);
+    talloc_free(req);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_OP_FAILURE, ("Updating DNS entry failed [%d]: %s\n",
+              ret, sss_strerror(ret)));
+        return;
     }
 
-    return true;
+    DEBUG(SSSDBG_OP_FAILURE, ("DNS update finished\n"));
 }
 
-static void ipa_dyndns_sdap_connect_done(struct tevent_req *subreq);
-static int ipa_dyndns_add_ldap_iface(struct ipa_dyndns_ctx *state,
-                                     struct sdap_handle *sh);
-static int ipa_dyndns_gss_tsig_update_step(struct tevent_req *req);
+struct ipa_dyndns_update_state {
+    struct ipa_options *ipa_ctx;
+};
 
-static struct tevent_req *
-ipa_dyndns_gss_tsig_update_send(struct ipa_dyndns_ctx *ctx);
-
-static void ipa_dyndns_gss_tsig_update_done(struct tevent_req *subreq);
+static void ipa_dyndns_sdap_update_done(struct tevent_req *subreq);
 
 static struct tevent_req *
 ipa_dyndns_update_send(struct ipa_options *ctx)
 {
     int ret;
-    char *iface;
-    struct ipa_dyndns_ctx *state;
-    struct ifaddrs *ifaces;
-    struct ifaddrs *ifa;
-    struct ipa_ipaddress *address;
+    struct ipa_dyndns_update_state *state;
     struct tevent_req *req, *subreq;
-    size_t addrsize;
-    struct sdap_id_ctx *id_ctx = ctx->id_ctx->sdap_id_ctx;
+    struct sdap_id_ctx *sdap_ctx = ctx->id_ctx->sdap_id_ctx;
+    char *dns_zone;
+    const char *servername;
+    int i;
 
-    DEBUG (9, ("Performing update\n"));
+    DEBUG(SSSDBG_TRACE_FUNC, ("Performing update\n"));
 
-    req = tevent_req_create(ctx, &state, struct ipa_dyndns_ctx);
+    req = tevent_req_create(ctx, &state, struct ipa_dyndns_update_state);
     if (req == NULL) {
         return NULL;
     }
     state->ipa_ctx = ctx;
-    state->use_server_with_nsupdate = false;
-    state->family_order = id_ctx->be->be_res->family_order;
 
-    iface = dp_opt_get_string(ctx->basic, IPA_DYNDNS_IFACE);
-
-    if (iface) {
-        /* Get the IP addresses associated with the
-         * specified interface
-         */
-        errno = 0;
-        ret = getifaddrs(&ifaces);
-        if (ret == -1) {
-            ret = errno;
-            DEBUG(0, ("Could not read interfaces [%d][%s]\n",
-                      ret, strerror(ret)));
-            goto failed;
-        }
-
-        for(ifa = ifaces; ifa != NULL; ifa=ifa->ifa_next) {
-            /* Some interfaces don't have an ifa_addr */
-            if (!ifa->ifa_addr) continue;
-
-            /* Add IP addresses to the list */
-            if((ifa->ifa_addr->sa_family == AF_INET ||
-                ifa->ifa_addr->sa_family == AF_INET6) &&
-               strcasecmp(ifa->ifa_name, iface) == 0 &&
-               ok_for_dns(ifa->ifa_addr)) {
-
-                /* Add this address to the IP address list */
-                address = talloc_zero(state, struct ipa_ipaddress);
-                if (!address) {
-                    goto failed;
-                }
-
-                addrsize = ifa->ifa_addr->sa_family == AF_INET ? \
-                                    sizeof(struct sockaddr_in) : \
-                                    sizeof(struct sockaddr_in6);
-
-                address->addr = talloc_memdup(address, ifa->ifa_addr,
-                                              addrsize);
-                if(address->addr == NULL) {
-                    goto failed;
-                }
-                DLIST_ADD(state->addresses, address);
-            }
-        }
-
-        freeifaddrs(ifaces);
-
-        ret = ipa_dyndns_gss_tsig_update_step(req);
-        if (ret != EOK) {
-            goto failed;
-        }
-    }
-
-    else {
-        /* Detect DYNDNS interface from LDAP connection */
-        state->sdap_op = sdap_id_op_create(state, state->ipa_ctx->id_ctx->sdap_id_ctx->conn_cache);
-        if (!state->sdap_op) {
-            DEBUG(1, ("sdap_id_op_create failed\n"));
-            ret = ENOMEM;
-            goto failed;
-        }
-
-        subreq = sdap_id_op_connect_send(state->sdap_op, state, &ret);
-        if (!subreq) {
-            DEBUG(1, ("sdap_id_op_connect_send failed: [%d](%s)\n",
-                ret, strerror(ret)));
-
-            goto failed;
-        }
-
-        tevent_req_set_callback(subreq, ipa_dyndns_sdap_connect_done, req);
-    }
-
-    return req;
-
-failed:
-    talloc_free(req);
-    return NULL;
-}
-
-static void ipa_dyndns_sdap_connect_done(struct tevent_req *subreq)
-{
-    struct tevent_req *req = tevent_req_callback_data(subreq, struct tevent_req);
-    struct ipa_dyndns_ctx *state = tevent_req_data(req, struct ipa_dyndns_ctx);
-    int ret, dp_error;
-
-    ret = sdap_id_op_connect_recv(subreq, &dp_error);
-    talloc_zfree(subreq);
-
-    if (ret != EOK) {
-        if (dp_error == DP_ERR_OFFLINE) {
-            DEBUG(9,("No LDAP server is available, dynamic DNS update is skipped in OFFLINE mode.\n"));
-        } else {
-            DEBUG(9,("Failed to connect to LDAP server: [%d](%s)\n",
-                ret, strerror(ret)));
-        }
-
-        goto failed;
-    }
-
-    ret = ipa_dyndns_add_ldap_iface(state, sdap_id_op_handle(state->sdap_op));
-    talloc_zfree(state->sdap_op);
-    if (ret != EOK) {
-        goto failed;
-    }
-
-    ret = ipa_dyndns_gss_tsig_update_step(req);
-    if (ret != EOK) {
-        goto failed;
-    }
-
-    return;
-
-failed:
-    tevent_req_error(req, ret);
-}
-
-static int ipa_dyndns_add_ldap_iface(struct ipa_dyndns_ctx *state,
-                                     struct sdap_handle *sh)
-{
-    int ret;
-    int fd;
-    struct ipa_ipaddress *address;
-    struct sockaddr_storage ss;
-    socklen_t ss_len = sizeof(ss);
-
-    if (!sh) {
-        return EINVAL;
-    }
-
-    /* Get the file descriptor for the primary LDAP connection */
-    ret = get_fd_from_ldap(sh->ldap, &fd);
-    if (ret != EOK) {
-        return ret;
-    }
-
-    errno = 0;
-    ret = getsockname(fd, (struct sockaddr *) &ss, &ss_len);
-    if (ret == -1) {
-        ret = errno;
-        DEBUG(SSSDBG_CRIT_FAILURE, ("Failed to get socket name\n"));
-        return ret;
-    }
-
-    switch(ss.ss_family) {
-    case AF_INET:
-    case AF_INET6:
-        address = talloc(state, struct ipa_ipaddress);
-        if (!address) {
-            return ENOMEM;
-        }
-        address->addr = talloc_memdup(address, &ss,
-                                      sizeof(struct sockaddr_storage));
-        if(address->addr == NULL) {
-            talloc_zfree(address);
-            return ENOMEM;
-        }
-        DLIST_ADD(state->addresses, address);
-        break;
-    default:
-        DEBUG(1, ("Connection to LDAP is neither IPv4 nor IPv6\n"));
-        return EIO;
-    }
-
-    return EOK;
-}
-
-static struct tevent_req *
-ipa_dyndns_update_get_addrs_send(TALLOC_CTX *mem_ctx,
-                                 struct ipa_dyndns_ctx *ctx,
-                                 enum restrict_family family_order);
-static errno_t
-ipa_dyndns_update_get_addrs_recv(struct tevent_req *req,
-                                 TALLOC_CTX *mem_ctx,
-                                 char ***_addrlist);
-
-static errno_t
-ipa_dyndns_gss_tsig_update_setup_check(struct ipa_dyndns_ctx *state);
-static void
-ipa_dyndns_gss_tsig_update_check(struct tevent_req *subreq);
-
-static int ipa_dyndns_gss_tsig_update_step(struct tevent_req *req)
-{
-    struct ipa_dyndns_ctx *state = tevent_req_data(req, struct ipa_dyndns_ctx);
-    char *ipa_hostname;
-    struct tevent_req *subreq;
-    errno_t ret;
-
-    /* Get the IPA hostname */
-    ipa_hostname = dp_opt_get_string(state->ipa_ctx->basic,
-                                     IPA_HOSTNAME);
-    if (!ipa_hostname) {
-        /* This should never happen, but we'll protect
-         * against it anyway.
-         */
-        return EINVAL;
-    }
-
-    state->hostname = talloc_strdup(state, ipa_hostname);
-    if (state->hostname == NULL) {
-        return ENOMEM;
-    }
-
-    DEBUG(7, ("Checking if the update is needed\n"));
-
-    ret = ipa_dyndns_gss_tsig_update_setup_check(state);
-    if (ret != EOK) {
-        return ret;
-    }
-
-    subreq = ipa_dyndns_update_get_addrs_send(state, state,
-                                              state->family_order);
-    if (subreq == NULL) {
-        return ENOMEM;
-    }
-    tevent_req_set_callback(subreq,
-                            ipa_dyndns_gss_tsig_update_check,
-                            req);
-    return EOK;
-}
-
-static errno_t
-ipa_dyndns_gss_tsig_update_setup_check(struct ipa_dyndns_ctx *state)
-{
-    if (dp_opt_get_string(state->ipa_ctx->basic, IPA_DYNDNS_IFACE)) {
-       /* Unless one family is restricted, just replace all
-        * address families during the update
-        */
-        switch (state->family_order) {
-        case IPV4_ONLY:
-            state->remove_af |= IPA_DYNDNS_REMOVE_A;
-            break;
-        case IPV6_ONLY:
-            state->remove_af |= IPA_DYNDNS_REMOVE_AAAA;
-            break;
-        case IPV4_FIRST:
-        case IPV6_FIRST:
-            state->remove_af |= (IPA_DYNDNS_REMOVE_A |
-                                IPA_DYNDNS_REMOVE_AAAA);
-            break;
-        }
-    } else {
-        /* If the interface isn't specified, we ONLY want to have the address
-         * that's connected to the LDAP server stored, so we need to check
-         * (and later remove) both address families.
-         */
-        state->family_order = IPV4_FIRST;
-        state->remove_af = (IPA_DYNDNS_REMOVE_A |
-                            IPA_DYNDNS_REMOVE_AAAA);
-    }
-
-    return EOK;
-}
-
-static void
-ipa_dyndns_gss_tsig_update_check(struct tevent_req *subreq)
-{
-    struct tevent_req *req =
-            tevent_req_callback_data(subreq, struct tevent_req);
-    struct ipa_dyndns_ctx *state = tevent_req_data(req,
-                                                   struct ipa_dyndns_ctx);
-
-    errno_t ret;
-    char **str_dnslist = NULL, **str_local_list = NULL;
-    char **dns_only = NULL, **local_only = NULL;
-    bool do_update = false;
-    int i;
-    TALLOC_CTX *tmp_ctx;
-
-    tmp_ctx = talloc_new(NULL);
-    if (!tmp_ctx) {
-        ret = ENOMEM;
-        goto fail;
-    }
-
-    ret = ipa_dyndns_update_get_addrs_recv(subreq, tmp_ctx, &str_dnslist);
-    talloc_zfree(subreq);
-    if (ret != EOK) {
-        DEBUG(3, ("Getting the current list of addresses failed [%d]: %s\n",
-                  ret, strerror(ret)));
-        goto fail;
-    }
-
-    ret = ipa_ipaddress_list_as_string_list(tmp_ctx,
-                                            state->addresses, &str_local_list);
-    if (ret != EOK) {
-        DEBUG(3, ("Converting DNS IP addresses to strings failed: [%d]: %s\n",
-                  ret, strerror(ret)));
-        goto fail;
-    }
-
-    /* Compare the lists */
-    ret = diff_string_lists(tmp_ctx, str_dnslist, str_local_list,
-                            &dns_only, &local_only, NULL);
-    if (ret != EOK) {
-        DEBUG(3, ("diff_string_lists failed: [%d]: %s\n", ret, strerror(ret)));
-        goto fail;
-    }
-
-    if (dns_only) {
-        for (i=0; dns_only[i]; i++) {
-            DEBUG(7, ("Address in DNS only: %s\n", dns_only[i]));
-            do_update = true;
-        }
-    }
-
-    if (local_only) {
-        for (i=0; local_only[i]; i++) {
-            DEBUG(7, ("Address on localhost only: %s\n", local_only[i]));
-            do_update = true;
-        }
-    }
-
-    if (do_update) {
-        DEBUG(6, ("Detected IP addresses change, will perform an update\n"));
-        subreq = ipa_dyndns_gss_tsig_update_send(state);
-        if(subreq == NULL) {
-            ret = ENOMEM;
-            goto fail;
-        }
-        tevent_req_set_callback(subreq,
-                                ipa_dyndns_gss_tsig_update_done,
-                                req);
-        talloc_free(tmp_ctx);
-        return;
-    }
-
-    DEBUG(6, ("No DNS update needed, addresses did not change\n"));
-    tevent_req_done(req);
-    talloc_free(tmp_ctx);
-    return;
-
-fail:
-    talloc_free(tmp_ctx);
-    tevent_req_error(req, ret);
-}
-
-struct ipa_dyndns_update_get_addrs_state {
-    struct ipa_dyndns_ctx *dctx;
-
-    enum host_database *db;
-    enum restrict_family family_order;
-
-    char **addrlist;
-    size_t count;
-};
-
-static void ipa_dyndns_update_get_addrs_done(struct tevent_req *subreq);
-static errno_t ipa_dyndns_update_get_addrs_step(struct tevent_req *req);
-
-static struct tevent_req *
-ipa_dyndns_update_get_addrs_send(TALLOC_CTX *mem_ctx,
-                                 struct ipa_dyndns_ctx *ctx,
-                                 enum restrict_family family_order)
-{
-    errno_t ret;
-    struct tevent_req *req;
-    struct ipa_dyndns_update_get_addrs_state *state;
-    struct sdap_id_ctx *id_ctx = ctx->ipa_ctx->id_ctx->sdap_id_ctx;
-
-    req = tevent_req_create(mem_ctx, &state,
-                            struct ipa_dyndns_update_get_addrs_state);
-    if (req == NULL) {
-        return NULL;
-    }
-    state->dctx = ctx;
-    state->family_order = family_order;
-
-    state->db = talloc_array(state, enum host_database, 2);
-    if (state->db == NULL) {
-        ret = ENOMEM;
-        goto immediate;
-    }
-    state->db[0] = DB_DNS;
-    state->db[1] = DB_SENTINEL;
-
-    ret = ipa_dyndns_update_get_addrs_step(req);
-    if (ret != EOK) {
-        goto immediate;
-    }
-
-immediate:
-    if (ret != EOK) {
-        tevent_req_error(req, ret);
-        tevent_req_post(req, id_ctx->be->ev);
-    }
-    return req;
-}
-
-static errno_t
-ipa_dyndns_update_get_addrs_step(struct tevent_req *req)
-{
-    struct tevent_req *subreq;
-    struct ipa_dyndns_update_get_addrs_state *state = tevent_req_data(req,
-                                        struct ipa_dyndns_update_get_addrs_state);
-    struct ipa_id_ctx *ipa_id_ctx = state->dctx->ipa_ctx->id_ctx;
-
-    subreq = resolv_gethostbyname_send(state,
-                                       ipa_id_ctx->sdap_id_ctx->be->ev,
-                                       state->dctx->ipa_ctx->resolv,
-                                       state->dctx->hostname,
-                                       state->family_order,
-                                       state->db);
-    if (!subreq) {
-        return ENOMEM;
-    }
-
-    tevent_req_set_callback(subreq, ipa_dyndns_update_get_addrs_done, req);
-    return EOK;
-}
-
-static void
-ipa_dyndns_update_get_addrs_done(struct tevent_req *subreq)
-{
-    int ret;
-    size_t count;
-    struct tevent_req *req =
-            tevent_req_callback_data(subreq, struct tevent_req);
-    struct ipa_dyndns_update_get_addrs_state *state = tevent_req_data(req,
-                                     struct ipa_dyndns_update_get_addrs_state);
-    struct resolv_hostent *rhostent;
-    int i;
-    int resolv_status;
-
-    ret = resolv_gethostbyname_recv(subreq, state, &resolv_status, NULL,
-                                    &rhostent);
-    talloc_zfree(subreq);
-
-    /* If the retry did not match, simply quit */
-    if (ret == ENOENT) {
-        /* If the resolver is set to honor both address families
-         * retry the second one
-         */
-        if (state->family_order == IPV4_FIRST ||
-            state->family_order == IPV6_FIRST) {
-
-            state->family_order = (state->family_order == IPV4_FIRST) ? \
-                                   IPV6_ONLY : IPV4_ONLY;
-
-            ret = ipa_dyndns_update_get_addrs_step(req);
-            if (ret != EOK) {
-                tevent_req_error(req, ret);
-            }
-            return;
-        }
-
-        /* Nothing to retry, simply quit */
-        tevent_req_done(req);
-        return;
-    } else if (ret != EOK) {
-        DEBUG(SSSDBG_OP_FAILURE,
-              ("Could not resolve address for this machine, error [%d]: %s, "
-               "resolver returned: [%d]: %s\n", ret, strerror(ret),
-               resolv_status, resolv_strerror(resolv_status)));
-        tevent_req_error(req, ret);
-        return;
-    }
-
-    /* EOK */
-
-    if (rhostent->addr_list) {
-        for (count=0; rhostent->addr_list[count]; count++);
-    } else {
-        /* The address list is NULL. This is probably a bug in
-         * c-ares, but we need to handle it gracefully.
-         */
-        DEBUG(SSSDBG_MINOR_FAILURE,
-              ("Lookup of [%s] returned no addresses. Skipping.\n",
-               rhostent->name));
-        count = 0;
-    }
-
-    state->addrlist = talloc_realloc(state, state->addrlist, char *,
-                                        state->count + count + 1);
-    if (!state->addrlist) {
-        tevent_req_error(req, ENOMEM);
-        return;
-    }
-
-    for (i=0; i < count; i++) {
-        state->addrlist[state->count + i] = \
-                        resolv_get_string_address_index(state->addrlist,
-                                                        rhostent, i);
-
-        if (state->addrlist[state->count + i] == NULL) {
-            tevent_req_error(req, ENOMEM);
-            return;
-        }
-    }
-    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
-     * get the complete list.
-     */
-    if (((state->family_order == IPV4_FIRST &&
-            rhostent->family == AF_INET) ||
-        (state->family_order == IPV6_FIRST &&
-            rhostent->family == AF_INET6))) {
-
-        state->family_order = (state->family_order == IPV4_FIRST) ? \
-                                IPV6_ONLY : IPV4_ONLY;
-
-        ret = ipa_dyndns_update_get_addrs_step(req);
-        if (ret != EOK) {
-            tevent_req_error(req, ret);
-        }
-        return;
-    }
-
-    /* The second address matched either immediatelly or after a retry.
-     * No need to retry again. */
-    tevent_req_done(req);
-    return;
-}
-
-static errno_t
-ipa_dyndns_update_get_addrs_recv(struct tevent_req *req,
-                                 TALLOC_CTX *mem_ctx,
-                                 char ***_addrlist)
-{
-    struct ipa_dyndns_update_get_addrs_state *state = tevent_req_data(req,
-                                    struct ipa_dyndns_update_get_addrs_state);
-
-    TEVENT_REQ_RETURN_ON_ERROR(req);
-
-    *_addrlist = talloc_steal(mem_ctx, state->addrlist);
-    return EOK;
-}
-
-struct ipa_nsupdate_ctx {
-    char *update_msg;
-    struct ipa_dyndns_ctx *dyndns_ctx;
-    int pipefd_to_child;
-    struct tevent_timer *timeout_handler;
-    int child_status;
-};
-
-
-static int create_nsupdate_message(struct ipa_nsupdate_ctx *ctx,
-                                   uint8_t remove_af,
-                                   bool use_server_with_nsupdate);
-
-static struct tevent_req *
-fork_nsupdate_send(struct ipa_nsupdate_ctx *ctx);
-
-static void fork_nsupdate_done(struct tevent_req *subreq);
-
-static struct tevent_req *
-ipa_dyndns_gss_tsig_update_send(struct ipa_dyndns_ctx *ctx)
-{
-    int ret;
-    struct ipa_nsupdate_ctx *state;
-    struct tevent_req *req;
-    struct tevent_req *subreq;
-
-    req = tevent_req_create(ctx, &state, struct ipa_nsupdate_ctx);
-    if(req == NULL) {
-        return NULL;
-    }
-    state->dyndns_ctx = ctx;
-    state->child_status = 0;
-
-    /* Format the message to pass to the nsupdate command */
-    ret = create_nsupdate_message(state, ctx->remove_af,
-                                  ctx->use_server_with_nsupdate);
-    if (ret != EOK) {
-        goto failed;
-    }
-
-    /* Fork a child process to perform the DNS update */
-    subreq = fork_nsupdate_send(state);
-    if(subreq == NULL) {
-        goto failed;
-    }
-    tevent_req_set_callback(subreq, fork_nsupdate_done, req);
-
-    return req;
-
-failed:
-    talloc_free(req);
-    return NULL;
-}
-
-struct nsupdate_send_ctx {
-    struct ipa_nsupdate_ctx *nsupdate_ctx;
-    int child_status;
-};
-
-static int create_nsupdate_message(struct ipa_nsupdate_ctx *ctx,
-                                   uint8_t remove_af,
-                                   bool use_server_with_nsupdate)
-{
-    int ret, i, ttl;
-    char *servername = NULL;
-    char *realm;
-    char *realm_directive;
-    char *zone;
-    char ip_addr[INET6_ADDRSTRLEN];
-    const char *ip;
-    struct ipa_ipaddress *new_record;
-    TALLOC_CTX *tmp_ctx;
-
-    tmp_ctx = talloc_new(NULL);
-    if (!tmp_ctx) return ENOMEM;
-
-    realm = dp_opt_get_string(ctx->dyndns_ctx->ipa_ctx->basic, IPA_KRB5_REALM);
-    if (!realm) {
-        ret = EIO;
-        goto done;
-    }
-
-#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;
-    }
-
-    zone = dp_opt_get_string(ctx->dyndns_ctx->ipa_ctx->basic,
-                             IPA_DOMAIN);
-    if (!zone) {
+    dns_zone = dp_opt_get_string(ctx->basic, IPA_DOMAIN);
+    if (!dns_zone) {
         ret = EIO;
         goto done;
     }
@@ -886,393 +117,73 @@ static int create_nsupdate_message(struct ipa_nsupdate_ctx *ctx,
     /* The DNS zone for IPA is the lower-case
      * version of the IPA domain
      */
-    for(i = 0; zone[i] != '\0'; i++) {
-        zone[i] = tolower(zone[i]);
+    for (i = 0; dns_zone[i] != '\0'; i++) {
+        dns_zone[i] = tolower(dns_zone[i]);
     }
 
-    if (use_server_with_nsupdate) {
-        if (strncmp(ctx->dyndns_ctx->ipa_ctx->service->sdap->uri,
-                    "ldap://", 7) != 0) {
-            DEBUG(1, ("Unexpected format of LDAP URI.\n"));
-            ret = EIO;
-            goto done;
-        }
-        servername = ctx->dyndns_ctx->ipa_ctx->service->sdap->uri + 7;
-        if (!servername) {
-            ret = EIO;
-            goto done;
-        }
-
-        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 */
-        ctx->update_msg = talloc_asprintf(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 */
-        ctx->update_msg = talloc_asprintf(ctx, "%szone %s.\n",
-                                               realm_directive, zone);
-    }
-    if (ctx->update_msg == NULL) {
-        ret = ENOMEM;
+    if (strncmp(ctx->service->sdap->uri,
+                "ldap://", 7) != 0) {
+        DEBUG(SSSDBG_CRIT_FAILURE, ("Unexpected format of LDAP URI.\n"));
+        ret = EIO;
         goto done;
     }
-
-    /* Get the TTL details for the record(s) */
-
-    ttl = dp_opt_get_int(ctx->dyndns_ctx->ipa_ctx->basic,
-                             IPA_DYNDNS_TTL);
-    /* Should not happen but just in case set the default */
-    if (!ttl) {
-        ttl = 1200;
-    }
-
-    /* Remove existing entries as needed */
-    if (remove_af & IPA_DYNDNS_REMOVE_A) {
-        ctx->update_msg = talloc_asprintf_append(ctx->update_msg,
-                                            "update delete %s. in A\nsend\n",
-                                            ctx->dyndns_ctx->hostname);
-        if (ctx->update_msg == NULL) {
-            ret = ENOMEM;
-            goto done;
-        }
-    }
-    if (remove_af & IPA_DYNDNS_REMOVE_AAAA) {
-        ctx->update_msg = talloc_asprintf_append(ctx->update_msg,
-                                         "update delete %s. in AAAA\nsend\n",
-                                          ctx->dyndns_ctx->hostname);
-        if (ctx->update_msg == NULL) {
-            ret = ENOMEM;
-            goto done;
-        }
-    }
-
-    DLIST_FOR_EACH(new_record, ctx->dyndns_ctx->addresses) {
-        switch(new_record->addr->ss_family) {
-        case AF_INET:
-            ip = inet_ntop(new_record->addr->ss_family,
-                           &(((struct sockaddr_in *)new_record->addr)->sin_addr),
-                           ip_addr, INET6_ADDRSTRLEN);
-            if (ip == NULL) {
-                ret = EIO;
-                goto done;
-            }
-            break;
-
-        case AF_INET6:
-            ip = inet_ntop(new_record->addr->ss_family,
-                           &(((struct sockaddr_in6 *)new_record->addr)->sin6_addr),
-                           ip_addr, INET6_ADDRSTRLEN);
-            if (ip == NULL) {
-                ret = EIO;
-                goto done;
-            }
-            break;
-
-        default:
-            DEBUG(0, ("Unknown address family\n"));
-            ret = EIO;
-            goto done;
-        }
-
-        /* Format the record update */
-        ctx->update_msg = talloc_asprintf_append(
-                ctx->update_msg,
-                "update add %s. %d in %s %s\n",
-                ctx->dyndns_ctx->hostname,
-                ttl,
-                new_record->addr->ss_family == AF_INET ? "A" : "AAAA",
-                ip_addr);
-        if (ctx->update_msg == NULL) {
-            ret = ENOMEM;
-            goto done;
-        }
+    servername = ctx->service->sdap->uri + 7;
+    if (!servername) {
+        ret = EIO;
+        goto done;
     }
 
-    ctx->update_msg = talloc_asprintf_append(ctx->update_msg, "send\n");
-    if (ctx->update_msg == NULL) {
-        ret = ENOMEM;
+    subreq = sdap_dyndns_update_send(state, sdap_ctx->be->ev,
+                                     sdap_ctx->be, sdap_ctx,
+                                     dp_opt_get_string(ctx->basic,
+                                                       IPA_DYNDNS_IFACE),
+                                     dp_opt_get_string(ctx->basic,
+                                                       IPA_HOSTNAME),
+                                     dns_zone,
+                                     dp_opt_get_string(ctx->basic,
+                                                       IPA_KRB5_REALM),
+                                     servername,
+                                     dp_opt_get_int(ctx->basic,
+                                                    IPA_DYNDNS_TTL),
+                                     true);
+    if (!subreq) {
+        ret = EIO;
+        DEBUG(SSSDBG_OP_FAILURE,
+              ("sdap_id_op_connect_send failed: [%d](%s)\n",
+               ret, sss_strerror(ret)));
         goto done;
     }
-
-    DEBUG(SSSDBG_TRACE_FUNC,
-          (" -- Begin nsupdate message -- \n%s",
-           ctx->update_msg));
-    DEBUG(SSSDBG_TRACE_FUNC,
-          (" -- End nsupdate message -- \n"));
+    tevent_req_set_callback(subreq, ipa_dyndns_sdap_update_done, req);
 
     ret = EOK;
-
 done:
-    talloc_free(tmp_ctx);
-    return ret;
-}
-
-static void ipa_dyndns_stdin_done(struct tevent_req *subreq);
-
-static void ipa_dyndns_child_handler(int child_status,
-                                     struct tevent_signal *sige,
-                                     void *pvt);
-
-static void ipa_dyndns_timeout(struct tevent_context *ev,
-                               struct tevent_timer *te,
-                               struct timeval tv, void *pvt);
-
-static struct tevent_req *
-fork_nsupdate_send(struct ipa_nsupdate_ctx *ctx)
-{
-    int pipefd_to_child[2];
-    pid_t pid;
-    int ret;
-    errno_t err;
-    struct timeval tv;
-    struct tevent_req *req = NULL;
-    struct tevent_req *subreq = NULL;
-    struct nsupdate_send_ctx *state;
-    char *args[3];
-
-    req = tevent_req_create(ctx, &state, struct nsupdate_send_ctx);
-    if (req == NULL) {
-        return NULL;
+    if (ret != EOK) {
+        tevent_req_error(req, ret);
+        tevent_req_post(req, sdap_ctx->be->ev);
     }
-    state->nsupdate_ctx = ctx;
-    state->child_status = 0;
-
-    ret = pipe(pipefd_to_child);
-    if (ret == -1) {
-        err = errno;
-        DEBUG(1, ("pipe failed [%d][%s].\n", err, strerror(err)));
-        return NULL;
-    }
-
-    pid = fork();
-
-    if (pid == 0) { /* child */
-        args[0] = talloc_strdup(ctx, NSUPDATE_PATH);
-        args[1] = talloc_strdup(ctx, "-g");
-        args[2] = NULL;
-        if (args[0] == NULL || args[1] == NULL) {
-            return NULL;
-        }
-
-        close(pipefd_to_child[1]);
-        ret = dup2(pipefd_to_child[0], STDIN_FILENO);
-        if (ret == -1) {
-            err = errno;
-            DEBUG(1, ("dup2 failed [%d][%s].\n", err, strerror(err)));
-            return NULL;
-        }
-
-        errno = 0;
-        execv(NSUPDATE_PATH, args);
-        err = errno;
-        DEBUG(SSSDBG_CRIT_FAILURE, ("execv failed [%d][%s].\n", err, strerror(err)));
-        return NULL;
-    }
-
-    else if (pid > 0) { /* parent */
-        close(pipefd_to_child[0]);
-
-        ctx->pipefd_to_child = pipefd_to_child[1];
-
-        /* Write the update message to the nsupdate child */
-        subreq = write_pipe_send(req,
-                                 ctx->dyndns_ctx->ipa_ctx->id_ctx->sdap_id_ctx->be->ev,
-                                 (uint8_t *)ctx->update_msg,
-                                 strlen(ctx->update_msg)+1,
-                                 ctx->pipefd_to_child);
-        if (subreq == NULL) {
-            return NULL;
-        }
-        tevent_req_set_callback(subreq, ipa_dyndns_stdin_done, req);
-
-        /* Set up SIGCHLD handler */
-        ret = child_handler_setup(ctx->dyndns_ctx->ipa_ctx->id_ctx->sdap_id_ctx->be->ev,
-                                  pid, ipa_dyndns_child_handler, req);
-        if (ret != EOK) {
-            return NULL;
-        }
-
-        /* Set up timeout handler */
-        tv = tevent_timeval_current_ofs(IPA_DYNDNS_TIMEOUT, 0);
-        ctx->timeout_handler = tevent_add_timer(
-                ctx->dyndns_ctx->ipa_ctx->id_ctx->sdap_id_ctx->be->ev,
-                req, tv, ipa_dyndns_timeout, req);
-        if(ctx->timeout_handler == NULL) {
-            return NULL;
-        }
-    }
-
-    else { /* error */
-        err = errno;
-        DEBUG(1, ("fork failed [%d][%s].\n", err, strerror(err)));
-        return NULL;
-    }
-
     return req;
 }
 
-static void ipa_dyndns_timeout(struct tevent_context *ev,
-                               struct tevent_timer *te,
-                               struct timeval tv, void *pvt)
+static void ipa_dyndns_sdap_update_done(struct tevent_req *subreq)
 {
-    struct tevent_req *req =
-            talloc_get_type(pvt, struct tevent_req);
+    struct tevent_req *req = tevent_req_callback_data(subreq, struct tevent_req);
+    errno_t ret;
 
-    DEBUG(1, ("Timeout reached for dynamic DNS update\n"));
-
-    tevent_req_error(req, ETIMEDOUT);
-}
-
-static void ipa_dyndns_stdin_done(struct tevent_req *subreq)
-{
-    /* Verify that the buffer was sent, then return
-     * and wait for the sigchld handler to finish.
-     */
-    DEBUG(9, ("Sending nsupdate data complete\n"));
-
-    int ret;
-    struct tevent_req *req =
-            tevent_req_callback_data(subreq, struct tevent_req);
-    struct nsupdate_send_ctx *state =
-            tevent_req_data(req, struct nsupdate_send_ctx);
-
-    ret = write_pipe_recv(subreq);
+    ret = sdap_dyndns_update_recv(subreq);
     talloc_zfree(subreq);
     if (ret != EOK) {
-        DEBUG(1, ("Sending nsupdate data failed\n"));
+        DEBUG(SSSDBG_OP_FAILURE,
+              ("Dynamic DNS update failed [%d]: %s\n", ret, sss_strerror(ret)));
         tevent_req_error(req, ret);
         return;
     }
 
-    close(state->nsupdate_ctx->pipefd_to_child);
-    state->nsupdate_ctx->pipefd_to_child = -1;
-}
-
-static void ipa_dyndns_child_handler(int child_status,
-                                     struct tevent_signal *sige,
-                                     void *pvt)
-{
-    struct tevent_req *req = talloc_get_type(pvt, struct tevent_req);
-    struct nsupdate_send_ctx *state =
-            tevent_req_data(req, struct nsupdate_send_ctx);
-
-    state->child_status = child_status;
-
-    if (WIFEXITED(child_status) && WEXITSTATUS(child_status) != 0) {
-        DEBUG(1, ("Dynamic DNS child failed with status [%d]\n",
-                  child_status));
-        tevent_req_error(req, EIO);
-        return;
-    }
-
-    if WIFSIGNALED(child_status) {
-        DEBUG(1, ("Dynamic DNS child was terminated by signal [%d]\n",
-                  WTERMSIG(child_status)));
-        tevent_req_error(req, EIO);
-        return;
-    }
-
     tevent_req_done(req);
 }
 
-static int ipa_dyndns_child_recv(struct tevent_req *req, int *child_status)
+static errno_t ipa_dyndns_update_recv(struct tevent_req *req)
 {
-    struct nsupdate_send_ctx *state =
-            tevent_req_data(req, struct nsupdate_send_ctx);
-
-    *child_status = state->child_status;
-
     TEVENT_REQ_RETURN_ON_ERROR(req);
 
     return EOK;
 }
-
-static int ipa_dyndns_generic_recv(struct tevent_req *req)
-{
-    TEVENT_REQ_RETURN_ON_ERROR(req);
-
-    return EOK;
-}
-
-static void fork_nsupdate_done(struct tevent_req *subreq)
-{
-    int ret;
-    struct tevent_req *req =
-            tevent_req_callback_data(subreq, struct tevent_req);
-    struct ipa_nsupdate_ctx *state = tevent_req_data(req,
-                                                     struct ipa_nsupdate_ctx);
-
-    ret = ipa_dyndns_child_recv(subreq, &state->child_status);
-    talloc_zfree(subreq);
-    if (ret != EOK) {
-        tevent_req_error(req, ret);
-        return;
-    }
-
-    tevent_req_done(req);
-}
-
-static int fork_nsupdate_recv(struct tevent_req *req, int *child_status)
-{
-    struct ipa_nsupdate_ctx *state =
-            tevent_req_data(req, struct ipa_nsupdate_ctx);
-
-    *child_status = state->child_status;
-
-    TEVENT_REQ_RETURN_ON_ERROR(req);
-
-    return EOK;
-}
-
-static void ipa_dyndns_gss_tsig_update_done(struct tevent_req *subreq)
-{
-    /* Check the return code from the sigchld handler
-     * and return it to the parent request.
-     */
-    int ret;
-    int child_status;
-
-    struct tevent_req *req =
-            tevent_req_callback_data(subreq, struct tevent_req);
-    struct ipa_dyndns_ctx *state = tevent_req_data(req, struct ipa_dyndns_ctx);
-
-    ret = fork_nsupdate_recv(subreq, &child_status);
-    talloc_zfree(subreq);
-    if (ret != EOK) {
-        if (state->use_server_with_nsupdate == false &&
-            WIFEXITED(child_status) && WEXITSTATUS(child_status) != 0) {
-            DEBUG(9, ("nsupdate failed, retrying with server name.\n"));
-            state->use_server_with_nsupdate = true;
-            ret = ipa_dyndns_gss_tsig_update_step(req);
-            if (ret != EOK) {
-                tevent_req_error(req, ret);
-            }
-            return;
-        } else {
-            tevent_req_error(req, ret);
-            return;
-        }
-    }
-
-    tevent_req_done(req);
-}
-
-static void ipa_dyndns_update_done(struct tevent_req *req)
-{
-    int ret = ipa_dyndns_generic_recv(req);
-    talloc_free(req);
-    if (ret != EOK) {
-        DEBUG(1, ("Updating DNS entry failed\n"));
-        return;
-    }
-
-    DEBUG(1, ("DNS update finished\n"));
-}
diff --git a/src/providers/ldap/sdap_dyndns.c b/src/providers/ldap/sdap_dyndns.c
new file mode 100644
index 0000000000000000000000000000000000000000..e7fad7ba5c5ed7ef3b70713d938068f0890884f9
--- /dev/null
+++ b/src/providers/ldap/sdap_dyndns.c
@@ -0,0 +1,498 @@
+/*
+    SSSD
+
+    sdap_dyndns.c: LDAP specific dynamic DNS update
+
+    Authors:
+        Jakub Hrozek <jhrozek at redhat.com>
+
+    Copyright (C) 2013 Red Hat
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "util/util.h"
+#include "resolv/async_resolv.h"
+#include "providers/dp_backend.h"
+#include "providers/dp_dyndns.h"
+#include "providers/ldap/sdap_async_private.h"
+#include "providers/ldap/sdap_id_op.h"
+#include "providers/ldap/ldap_common.h"
+
+static struct tevent_req *
+sdap_dyndns_get_addrs_send(TALLOC_CTX *mem_ctx,
+                           struct tevent_context *ev,
+                           struct sdap_id_ctx *sdap_ctx,
+                           const char *iface);
+static errno_t
+sdap_dyndns_get_addrs_recv(struct tevent_req *req,
+                           TALLOC_CTX *mem_ctx,
+                           struct sss_iface_addr **_addresses);
+
+struct sdap_dyndns_update_state {
+    struct tevent_context *ev;
+    struct be_resolv_ctx *be_res;
+
+    const char *hostname;
+    const char *dns_zone;
+    const char *realm;
+    const char *servername;
+    int ttl;
+
+    struct sss_iface_addr *addresses;
+    uint8_t remove_af;
+
+    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 errno_t sdap_dyndns_update_step(struct tevent_req *req);
+static void sdap_dyndns_update_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 sdap_id_ctx *sdap_ctx,
+                        const char *ifname,
+                        const char *hostname,
+                        const char *dns_zone,
+                        const char *realm,
+                        const char *servername,
+                        const int ttl,
+                        bool check_diff)
+{
+    errno_t ret;
+    struct tevent_req *req;
+    struct tevent_req *subreq;
+    struct sdap_dyndns_update_state *state;
+
+    req = tevent_req_create(mem_ctx, &state, struct sdap_dyndns_update_state);
+    if (req == NULL) {
+        return NULL;
+    }
+    state->check_diff = check_diff;
+    state->hostname = hostname;
+    state->dns_zone = dns_zone;
+    state->realm = realm;
+    state->servername = servername;
+    state->use_server_with_nsupdate = false;
+    state->ttl = ttl;
+    state->be_res = be_ctx->be_res;
+    state->ev = ev;
+
+    if (ifname) {
+       /* Unless one family is restricted, just replace all
+        * address families during the update
+        */
+        switch (state->be_res->family_order) {
+        case IPV4_ONLY:
+            state->remove_af |= DYNDNS_REMOVE_A;
+            break;
+        case IPV6_ONLY:
+            state->remove_af |= DYNDNS_REMOVE_AAAA;
+            break;
+        case IPV4_FIRST:
+        case IPV6_FIRST:
+            state->remove_af |= (DYNDNS_REMOVE_A |
+                                 DYNDNS_REMOVE_AAAA);
+            break;
+        }
+    } else {
+        /* If the interface isn't specified, we ONLY want to have the address
+         * that's connected to the LDAP server stored, so we need to check
+         * (and later remove) both address families.
+         */
+        state->remove_af = (DYNDNS_REMOVE_A | DYNDNS_REMOVE_AAAA);
+    }
+
+    subreq = sdap_dyndns_get_addrs_send(state, state->ev, sdap_ctx, ifname);
+    if (!subreq) {
+        ret = EIO;
+        DEBUG(SSSDBG_OP_FAILURE, ("sdap_id_op_connect_send failed: [%d](%s)\n",
+              ret, sss_strerror(ret)));
+        goto done;
+    }
+    tevent_req_set_callback(subreq, sdap_dyndns_update_addrs_done, req);
+
+    ret = EOK;
+done:
+    if (ret != EOK) {
+        tevent_req_error(req, ret);
+        tevent_req_post(req, ev);
+    }
+    return req;
+}
+
+static void
+sdap_dyndns_update_addrs_done(struct tevent_req *subreq)
+{
+    errno_t ret;
+    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 = sdap_dyndns_get_addrs_recv(subreq, state, &state->addresses);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_OP_FAILURE, ("Can't get addresses for DNS update\n"));
+        tevent_req_error(req, ret);
+        return;
+    }
+
+    if (state->check_diff) {
+        /* Check if we need the update at all */
+        subreq = nsupdate_get_addrs_send(state, state->ev,
+                                         state->be_res, state->hostname);
+        if (subreq == NULL) {
+            DEBUG(SSSDBG_OP_FAILURE, ("Can't initiate address check\n"));
+            tevent_req_error(req, ret);
+            return;
+        }
+        tevent_req_set_callback(subreq, sdap_dyndns_addrs_check_done, req);
+        return;
+    }
+
+    /* Perform update */
+    ret = sdap_dyndns_update_step(req);
+    if (ret != EOK) {
+        tevent_req_error(req, ret);
+        return;
+    }
+    /* Execution will resume in sdap_dyndns_update_done */
+}
+
+static void
+sdap_dyndns_addrs_check_done(struct tevent_req *subreq)
+{
+    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);
+    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;
+    }
+
+    /* Compare the lists */
+    ret = diff_string_lists(state, str_dnslist, str_local_list,
+                            &dns_only, &local_only, NULL);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_OP_FAILURE,
+              ("diff_string_lists failed: [%d]: %s\n", ret, sss_strerror(ret)));
+        tevent_req_error(req, ret);
+        return;
+    }
+
+    if (dns_only) {
+        for (i=0; dns_only[i]; i++) {
+            DEBUG(SSSDBG_TRACE_LIBS,
+                  ("Address in DNS only: %s\n", dns_only[i]));
+            do_update = true;
+        }
+    }
+
+    if (local_only) {
+        for (i=0; local_only[i]; i++) {
+            DEBUG(SSSDBG_TRACE_LIBS,
+                  ("Address on localhost only: %s\n", local_only[i]));
+            do_update = true;
+        }
+    }
+
+    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;
+}
+
+static errno_t
+sdap_dyndns_update_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_msg(state, state->realm, state->dns_zone,
+                                 servername, state->hostname,
+                                 state->ttl, state->remove_af,
+                                 state->addresses,
+                                 &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->update_msg);
+    if (subreq == NULL) {
+        return EIO;
+    }
+
+    tevent_req_set_callback(subreq, sdap_dyndns_update_done, req);
+    return EOK;
+}
+
+static void
+sdap_dyndns_update_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_step(req);
+            if (ret == EOK) {
+                return;
+            }
+        }
+
+        tevent_req_error(req, ret);
+        return;
+    }
+
+    tevent_req_done(req);
+}
+
+errno_t
+sdap_dyndns_update_recv(struct tevent_req *req)
+{
+    TEVENT_REQ_RETURN_ON_ERROR(req);
+    return EOK;
+}
+
+/* A request to get addresses to update with */
+struct sdap_dyndns_get_addrs_state {
+    struct sdap_id_op* sdap_op;
+    struct sss_iface_addr *addresses;
+};
+
+static void sdap_dyndns_get_addrs_done(struct tevent_req *subreq);
+static errno_t sdap_dyndns_add_ldap_conn(struct sdap_dyndns_get_addrs_state *state,
+                                         struct sdap_handle *sh);
+
+static struct tevent_req *
+sdap_dyndns_get_addrs_send(TALLOC_CTX *mem_ctx,
+                           struct tevent_context *ev,
+                           struct sdap_id_ctx *sdap_ctx,
+                           const char *iface)
+{
+    errno_t ret;
+    struct tevent_req *req;
+    struct tevent_req *subreq;
+    struct sdap_dyndns_get_addrs_state *state;
+
+    req = tevent_req_create(mem_ctx, &state,
+                            struct sdap_dyndns_get_addrs_state);
+    if (req == NULL) {
+        return NULL;
+    }
+
+    if (iface) {
+        ret = sss_iface_addr_list_get(state, iface, &state->addresses);
+        if (ret != EOK) {
+            DEBUG(SSSDBG_OP_FAILURE,
+                  ("Cannot get list of addresses from interface %s\n", iface));
+        }
+        /* We're done. Just fake an async request completion */
+        goto done;
+    }
+
+    /* Detect DYNDNS address from LDAP connection */
+    state->sdap_op = sdap_id_op_create(state, sdap_ctx->conn_cache);
+    if (!state->sdap_op) {
+        ret = ENOMEM;
+        DEBUG(SSSDBG_OP_FAILURE, ("sdap_id_op_create failed\n"));
+        goto done;
+    }
+
+    subreq = sdap_id_op_connect_send(state->sdap_op, state, &ret);
+    if (!subreq) {
+        ret = EIO;
+        DEBUG(SSSDBG_OP_FAILURE, ("sdap_id_op_connect_send failed: [%d](%s)\n",
+              ret, sss_strerror(ret)));
+        goto done;
+    }
+    tevent_req_set_callback(subreq, sdap_dyndns_get_addrs_done, req);
+
+    ret = EAGAIN;
+done:
+    if (ret == EOK) {
+        tevent_req_done(req);
+        tevent_req_post(req, ev);
+    } else if (ret != EAGAIN) {
+        tevent_req_error(req, ret);
+        tevent_req_post(req, ev);
+    }
+
+    /* EAGAIN - resolution in progress */
+    return req;
+}
+
+static void
+sdap_dyndns_get_addrs_done(struct tevent_req *subreq)
+{
+    errno_t ret;
+    int dp_error;
+    struct tevent_req *req;
+    struct sdap_dyndns_get_addrs_state *state;
+
+    req = tevent_req_callback_data(subreq, struct tevent_req);
+    state = tevent_req_data(req, struct sdap_dyndns_get_addrs_state);
+
+    ret = sdap_id_op_connect_recv(subreq, &dp_error);
+    talloc_zfree(subreq);
+    if (ret != EOK) {
+        if (dp_error == DP_ERR_OFFLINE) {
+            DEBUG(SSSDBG_MINOR_FAILURE, ("No LDAP server is available, "
+                  "dynamic DNS update is skipped in offline mode.\n"));
+            ret = ERR_DYNDNS_OFFLINE;
+        } else {
+            DEBUG(SSSDBG_OP_FAILURE,
+                  ("Failed to connect to LDAP server: [%d](%s)\n",
+                  ret, sss_strerror(ret)));
+        }
+        tevent_req_error(req, ret);
+        return;
+    }
+
+    ret = sdap_dyndns_add_ldap_conn(state, sdap_id_op_handle(state->sdap_op));
+    if (ret != EOK) {
+        DEBUG(SSSDBG_OP_FAILURE, ("Can't get addresses from LDAP connection\n"));
+        tevent_req_error(req, ret);
+        return;
+    }
+
+    /* Got the address! Done! */
+    tevent_req_done(req);
+}
+
+static errno_t
+sdap_dyndns_add_ldap_conn(struct sdap_dyndns_get_addrs_state *state,
+                          struct sdap_handle *sh)
+{
+    int ret;
+    int fd;
+    struct sss_iface_addr *address;
+    struct sockaddr_storage ss;
+    socklen_t ss_len = sizeof(ss);
+
+    if (sh == NULL) {
+        return EINVAL;
+    }
+
+    /* Get the file descriptor for the primary LDAP connection */
+    ret = get_fd_from_ldap(sh->ldap, &fd);
+    if (ret != EOK) {
+        return ret;
+    }
+
+    errno = 0;
+    ret = getsockname(fd, (struct sockaddr *) &ss, &ss_len);
+    if (ret == -1) {
+        ret = errno;
+        DEBUG(SSSDBG_CRIT_FAILURE, ("Failed to get socket name\n"));
+        return ret;
+    }
+
+    switch(ss.ss_family) {
+    case AF_INET:
+    case AF_INET6:
+        address = sss_iface_addr_add(state, &state->addresses, &ss);
+        if (address == NULL) {
+            return ENOMEM;
+        }
+        break;
+    default:
+        DEBUG(SSSDBG_CRIT_FAILURE,
+              ("Connection to LDAP is neither IPv4 nor IPv6\n"));
+        return EIO;
+    }
+
+    return EOK;
+}
+
+static errno_t
+sdap_dyndns_get_addrs_recv(struct tevent_req *req,
+                           TALLOC_CTX *mem_ctx,
+                           struct sss_iface_addr **_addresses)
+{
+    struct sdap_dyndns_get_addrs_state *state;
+
+    state = tevent_req_data(req, struct sdap_dyndns_get_addrs_state);
+
+    TEVENT_REQ_RETURN_ON_ERROR(req);
+
+    *_addresses = talloc_steal(mem_ctx, state->addresses);
+    return EOK;
+}
diff --git a/src/providers/ldap/sdap_dyndns.h b/src/providers/ldap/sdap_dyndns.h
new file mode 100644
index 0000000000000000000000000000000000000000..1602938e17d01f30a99bae37a6d496643281a680
--- /dev/null
+++ b/src/providers/ldap/sdap_dyndns.h
@@ -0,0 +1,47 @@
+/*
+    SSSD
+
+    sdap_dyndns.h: LDAP specific dynamic DNS update
+
+    Authors:
+        Jakub Hrozek <jhrozek at redhat.com>
+
+    Copyright (C) 2013 Red Hat
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef SDAP_DYNDNS_H_
+#define SDAP_DYNDNS_H_
+
+#include "util/util.h"
+#include "providers/dp_backend.h"
+#include "providers/ldap/ldap_common.h"
+
+struct tevent_req *
+sdap_dyndns_update_send(TALLOC_CTX *mem_ctx,
+                        struct tevent_context *ev,
+                        struct be_ctx *be_ctx,
+                        struct sdap_id_ctx *sdap_ctx,
+                        const char *ifname,
+                        const char *hostname,
+                        const char *dns_zone,
+                        const char *realm,
+                        const char *servername,
+                        const int ttl,
+                        bool check_diff);
+
+errno_t sdap_dyndns_update_recv(struct tevent_req *req);
+
+#endif /* SDAP_DYNDNS_H_ */
diff --git a/src/tests/cmocka/test_dyndns.c b/src/tests/cmocka/test_dyndns.c
new file mode 100644
index 0000000000000000000000000000000000000000..fd92404388e52bfb9b7be544e9a3ffae923ebeb7
--- /dev/null
+++ b/src/tests/cmocka/test_dyndns.c
@@ -0,0 +1,339 @@
+/*
+    Authors:
+        Jakub Hrozek <jhrozek at redhat.com>
+
+    Copyright (C) 2013 Red Hat
+
+    SSSD tests: Dynamic DNS tests
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <talloc.h>
+#include <tevent.h>
+#include <errno.h>
+#include <popt.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <ifaddrs.h>
+#include <arpa/inet.h>
+
+/* In order to access opaque types */
+#include "providers/dp_dyndns.c"
+
+#include "tests/cmocka/common_mock.h"
+#include "src/providers/dp_dyndns.h"
+
+enum mock_nsupdate_states {
+    MOCK_NSUPDATE_OK,
+    MOCK_NSUPDATE_ERR,
+    MOCK_NSUPDATE_TIMEOUT,
+};
+
+struct dyndns_test_ctx {
+    struct sss_test_ctx *tctx;
+
+    enum mock_nsupdate_states state;
+    int child_status;
+    int child_retval;
+};
+
+static struct dyndns_test_ctx *dyndns_test_ctx;
+
+void __wrap_execv(const char *path, char *const argv[])
+{
+    int err;
+
+    switch (dyndns_test_ctx->state) {
+        case MOCK_NSUPDATE_OK:
+            DEBUG(SSSDBG_FUNC_DATA, ("nsupdate success test case\n"));
+            err = 0;
+            break;
+        case MOCK_NSUPDATE_ERR:
+            DEBUG(SSSDBG_FUNC_DATA, ("nsupdate error test case\n"));
+            err = 1;
+            break;
+        case MOCK_NSUPDATE_TIMEOUT:
+            DEBUG(SSSDBG_FUNC_DATA, ("nsupdate timeout test case\n"));
+            err = 2;
+            sleep(3);
+            break;
+        default:
+            DEBUG(SSSDBG_CRIT_FAILURE, ("unknown test case\n"));
+            err = 255;
+            break;
+    }
+
+    DEBUG(SSSDBG_TRACE_LIBS, ("Child exiting with status %d\n", err));
+    _exit(err);
+}
+
+int __wrap_getifaddrs(struct ifaddrs **_ifap)
+{
+    struct ifaddrs *ifap;
+    struct ifaddrs *ifap_prev = NULL;
+    struct ifaddrs *ifap_head = NULL;
+    char *name;
+    char *straddr;
+
+    while ((name = sss_mock_ptr_type(char *)) != NULL) {
+        straddr = sss_mock_ptr_type(char *);
+        if (straddr == NULL) {
+            errno = EINVAL;
+            goto fail;
+        }
+
+        ifap = talloc_zero(global_talloc_context, struct ifaddrs);
+        if (ifap == NULL) {
+            errno = ENOMEM;    /* getifaddrs sets errno, too */
+            goto fail;
+        }
+
+        if (ifap_prev) {
+            ifap_prev->ifa_next = ifap;
+        } else {
+            ifap_head = ifap;
+        }
+        ifap_prev = ifap;
+
+        ifap->ifa_name = talloc_strdup(ifap, name);
+        if (ifap == NULL) {
+            errno = ENOMEM;
+            goto fail;
+        }
+
+        ifap->ifa_addr = (struct sockaddr *) talloc(ifap, struct sockaddr_in);
+        if (ifap->ifa_addr == NULL) {
+            errno = ENOMEM;
+            goto fail;
+        }
+        ((struct sockaddr_in *) ifap->ifa_addr)->sin_family = AF_INET;
+
+        /* convert straddr into ifa_addr */
+        if (inet_pton(AF_INET, straddr,
+                      &(((struct sockaddr_in *) ifap->ifa_addr)->sin_addr)) != 1) {
+            goto fail;
+        }
+    }
+
+    *_ifap = ifap_head;
+    return 0;
+
+fail:
+    talloc_free(ifap);
+    return -1;
+}
+
+void __wrap_freeifaddrs(struct ifaddrs *ifap)
+{
+    talloc_free(ifap);
+}
+
+static void dyndns_test_done(struct tevent_req *req)
+{
+    struct dyndns_test_ctx *ctx =
+            tevent_req_callback_data(req, struct dyndns_test_ctx);
+
+    ctx->child_retval = -1;
+    ctx->tctx->error = be_nsupdate_recv(req, &ctx->child_status);
+    talloc_zfree(req);
+
+    ctx->tctx->done = true;
+}
+
+void will_return_getifaddrs(const char *ifname, const char *straddr)
+{
+    will_return(__wrap_getifaddrs, ifname);
+    if (ifname) {
+        will_return(__wrap_getifaddrs, straddr);
+    }
+}
+
+void dyndns_test_get_ifaddr(void **state)
+{
+    errno_t ret;
+    struct sss_iface_addr *addrlist;
+    char straddr[128];
+
+    check_leaks_push(dyndns_test_ctx);
+    will_return_getifaddrs("eth0", "192.168.0.1");
+    will_return_getifaddrs("eth1", "192.168.0.2");
+    will_return_getifaddrs(NULL, NULL); /* sentinel */
+    ret = sss_iface_addr_list_get(dyndns_test_ctx, "eth0", &addrlist);
+    assert_int_equal(ret, EOK);
+
+    /* There must be only one address with the correct value */
+    assert_non_null(addrlist);
+    assert_non_null(addrlist->addr);
+    assert_null(addrlist->next);
+    assert_null(addrlist->prev);
+
+    assert_non_null(inet_ntop(AF_INET,
+                              &((struct sockaddr_in *) addrlist->addr)->sin_addr,
+                              straddr, INET6_ADDRSTRLEN));
+    assert_string_equal(straddr, "192.168.0.1");
+
+    talloc_free(addrlist);
+    assert_true(check_leaks_pop(dyndns_test_ctx) == true);
+}
+
+void dyndns_test_ok(void **state)
+{
+    struct tevent_req *req;
+    errno_t ret;
+    TALLOC_CTX *tmp_ctx;
+
+    tmp_ctx = talloc_new(global_talloc_context);
+    assert_non_null(tmp_ctx);
+    check_leaks_push(tmp_ctx);
+
+    dyndns_test_ctx->state = MOCK_NSUPDATE_OK;
+
+    req = be_nsupdate_send(tmp_ctx, dyndns_test_ctx->tctx->ev,
+                           discard_const("test message"));
+    assert_non_null(req);
+    tevent_req_set_callback(req, dyndns_test_done, dyndns_test_ctx);
+
+    /* Wait until the test finishes with EOK */
+    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, EOK);
+
+    assert_true(WIFEXITED(dyndns_test_ctx->child_status));
+    assert_int_equal(WEXITSTATUS(dyndns_test_ctx->child_status), 0);
+
+    assert_true(check_leaks_pop(tmp_ctx) == true);
+    talloc_free(tmp_ctx);
+}
+
+void dyndns_test_error(void **state)
+{
+    struct tevent_req *req;
+    errno_t ret;
+    TALLOC_CTX *tmp_ctx;
+
+    tmp_ctx = talloc_new(global_talloc_context);
+    assert_non_null(tmp_ctx);
+    check_leaks_push(tmp_ctx);
+
+    dyndns_test_ctx->state = MOCK_NSUPDATE_ERR;
+
+    req = be_nsupdate_send(tmp_ctx, dyndns_test_ctx->tctx->ev,
+                           discard_const("test message"));
+    assert_non_null(req);
+    tevent_req_set_callback(req, dyndns_test_done, dyndns_test_ctx);
+
+    /* Wait until the test finishes with EIO (child error) */
+    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_DYNDNS_FAILED);
+
+    assert_true(WIFEXITED(dyndns_test_ctx->child_status));
+    assert_int_equal(WEXITSTATUS(dyndns_test_ctx->child_status), 1);
+
+    assert_true(check_leaks_pop(tmp_ctx) == true);
+    talloc_free(tmp_ctx);
+}
+
+void dyndns_test_timeout(void **state)
+{
+    struct tevent_req *req;
+    errno_t ret;
+    TALLOC_CTX *tmp_ctx;
+
+    tmp_ctx = talloc_new(global_talloc_context);
+    assert_non_null(tmp_ctx);
+    check_leaks_push(tmp_ctx);
+
+    dyndns_test_ctx->state = MOCK_NSUPDATE_TIMEOUT;
+
+    req = be_nsupdate_send(tmp_ctx, dyndns_test_ctx->tctx->ev,
+                           discard_const("test message"));
+    assert_non_null(req);
+    tevent_req_set_callback(req, dyndns_test_done, dyndns_test_ctx);
+
+    /* Wait until the test finishes with EIO (child error) */
+    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_DYNDNS_TIMEOUT);
+
+    assert_true(check_leaks_pop(tmp_ctx) == true);
+    talloc_free(tmp_ctx);
+}
+
+/* Testsuite setup and teardown */
+void dyndns_test_setup(void **state)
+{
+    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);
+    assert_non_null(dyndns_test_ctx->tctx);
+}
+
+void dyndns_test_teardown(void **state)
+{
+    talloc_free(dyndns_test_ctx);
+    assert_true(leak_check_teardown());
+}
+
+int main(int argc, const char *argv[])
+{
+    int rv;
+    poptContext pc;
+    int opt;
+    struct poptOption long_options[] = {
+        POPT_AUTOHELP
+        SSSD_DEBUG_OPTS
+        POPT_TABLEEND
+    };
+
+    const UnitTest tests[] = {
+        /* Utility functions unit test */
+        unit_test(dyndns_test_get_ifaddr),
+
+        /* Dynamic DNS update unit tests*/
+        unit_test_setup_teardown(dyndns_test_ok,
+                                 dyndns_test_setup, dyndns_test_teardown),
+        unit_test_setup_teardown(dyndns_test_error,
+                                 dyndns_test_setup, dyndns_test_teardown),
+        unit_test_setup_teardown(dyndns_test_timeout,
+                                 dyndns_test_setup, dyndns_test_teardown),
+    };
+
+    /* Set debug level to invalid value so we can deside if -d 0 was used. */
+    debug_level = SSSDBG_INVALID;
+
+    pc = poptGetContext(argv[0], argc, argv, long_options, 0);
+    while((opt = poptGetNextOpt(pc)) != -1) {
+        switch(opt) {
+        default:
+            fprintf(stderr, "\nInvalid option %s: %s\n\n",
+                    poptBadOption(pc, 0), poptStrerror(opt));
+            poptPrintUsage(pc, stderr, 0);
+            return 1;
+        }
+    }
+    poptFreeContext(pc);
+
+    DEBUG_INIT(debug_level);
+    tests_set_cwd();
+    rv = run_tests(tests);
+
+    return rv;
+}
diff --git a/src/tests/common.h b/src/tests/common.h
index e7fc812c7803cd5a5b85bb466f781e4a8efc3012..931e603c1c59c7a73434b344cb1633faf37bf83c 100644
--- a/src/tests/common.h
+++ b/src/tests/common.h
@@ -72,6 +72,8 @@ struct sss_test_conf_param {
     const char *value;
 };
 
+struct sss_test_ctx *create_ev_test_ctx(TALLOC_CTX *mem_ctx);
+
 struct sss_test_ctx *
 create_dom_test_ctx(TALLOC_CTX *mem_ctx,
                     const char *tests_path,
diff --git a/src/tests/common_dom.c b/src/tests/common_dom.c
index db3297872790e0a72aeec76391c0564db40a7231..00e7f5ae2bfc972b8a61982c54bc5df70cbbcb20 100644
--- a/src/tests/common_dom.c
+++ b/src/tests/common_dom.c
@@ -42,19 +42,12 @@ create_dom_test_ctx(TALLOC_CTX *mem_ctx,
     errno_t ret;
     char *dompath;
 
-    test_ctx = talloc_zero(mem_ctx, struct sss_test_ctx);
+    test_ctx = create_ev_test_ctx(mem_ctx);
     if (test_ctx == NULL) {
         DEBUG(SSSDBG_CRIT_FAILURE, ("talloc_zero failed\n"));
         goto fail;
     }
 
-    /* Create an event context */
-    test_ctx->ev = tevent_context_init(test_ctx);
-    if (test_ctx->ev == NULL) {
-        DEBUG(SSSDBG_CRIT_FAILURE, ("tevent_context_init failed\n"));
-        goto fail;
-    }
-
     conf_db = talloc_asprintf(test_ctx, "%s/%s", tests_path, confdb_path);
     if (conf_db == NULL) {
         DEBUG(SSSDBG_CRIT_FAILURE, ("talloc_asprintf failed\n"));
diff --git a/src/tests/common_tev.c b/src/tests/common_tev.c
index e9db8d053b9e642f363187901019519e0f14f817..81b97d331140d01dd9a7f5073a04e666776b1d31 100644
--- a/src/tests/common_tev.c
+++ b/src/tests/common_tev.c
@@ -26,6 +26,31 @@
 
 #include "tests/common.h"
 
+struct sss_test_ctx *
+create_ev_test_ctx(TALLOC_CTX *mem_ctx)
+{
+    struct sss_test_ctx *test_ctx;
+
+    test_ctx = talloc_zero(mem_ctx, struct sss_test_ctx);
+    if (test_ctx == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, ("talloc_zero failed\n"));
+        goto fail;
+    }
+
+    /* Create an event context */
+    test_ctx->ev = tevent_context_init(test_ctx);
+    if (test_ctx->ev == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, ("tevent_context_init failed\n"));
+        goto fail;
+    }
+
+    return test_ctx;
+
+fail:
+    talloc_free(test_ctx);
+    return NULL;
+}
+
 struct tevent_req *
 test_request_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev, errno_t err)
 {
diff --git a/src/util/util_errors.c b/src/util/util_errors.c
index b5c1928cb49996b9ce1dc62a75fc8e1456546c0c..b617f540691a245d1132469a1f019bcb0eb6e775 100644
--- a/src/util/util_errors.c
+++ b/src/util/util_errors.c
@@ -43,6 +43,9 @@ struct err_string error_to_str[] = {
     { "Host Access Denied" }, /* ERR_ACCESS_DENIED */
     { "SRV record not found" }, /* ERR_SRV_NOT_FOUND */
     { "SRV lookup error" }, /* ERR_SRV_LOOKUP_ERROR */
+    { "Dynamic DNS update failed" }, /* ERR_DYNDNS_FAILED */
+    { "Dynamic DNS update timed out" }, /* ERR_DYNDNS_TIMEOUT */
+    { "Dynamic DNS update not possible while offline" }, /* ERR_DYNDNS_OFFLINE */
 };
 
 
diff --git a/src/util/util_errors.h b/src/util/util_errors.h
index 4f7c0086ea7a562da9932386fbb04a3c8ab23eb3..a602a6ea92f72a51f5e21342940b2072bbe9296d 100644
--- a/src/util/util_errors.h
+++ b/src/util/util_errors.h
@@ -65,6 +65,9 @@ enum sssd_errors {
     ERR_ACCESS_DENIED,
     ERR_SRV_NOT_FOUND,
     ERR_SRV_LOOKUP_ERROR,
+    ERR_DYNDNS_FAILED,
+    ERR_DYNDNS_TIMEOUT,
+    ERR_DYNDNS_OFFLINE,
     ERR_LAST            /* ALWAYS LAST */
 };
 
-- 
1.8.2.1

-------------- next part --------------
>From 2e1a34a1447daf19e88acc9b74d66364543a9d5b Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhrozek at redhat.com>
Date: Tue, 9 Apr 2013 14:20:28 +0200
Subject: [PATCH 2/2] Convert IPA-specific options to be back-end agnostic

This patch introduces new options for dynamic DNS updates that are not
specific to any back end. The current ipa dyndns options are still
usable, just with a deprecation warning.
---
 Makefile.am                          |  3 +-
 src/config/SSSDConfig/__init__.py.in |  3 ++
 src/config/SSSDConfigTest.py         |  6 +++
 src/config/etc/sssd.api.conf         |  5 ++
 src/man/sssd-ipa.5.xml               | 28 +++++++++--
 src/providers/dp_dyndns.c            | 57 +++++++++++++++++++++
 src/providers/dp_dyndns.h            | 24 +++++++++
 src/providers/ipa/ipa_common.c       | 96 ++++++++++++++++++++++++++++++++++++
 src/providers/ipa/ipa_common.h       |  7 +--
 src/providers/ipa/ipa_dyndns.c       |  9 ++--
 src/providers/ipa/ipa_init.c         | 29 +++++------
 src/providers/ipa/ipa_opts.h         | 10 ++--
 12 files changed, 243 insertions(+), 34 deletions(-)

diff --git a/Makefile.am b/Makefile.am
index 534e1327999dfb34354659a5e2a222d0c594ec5f..e4263f95de162f478a07be024a23b6bfe4c142ff 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1262,7 +1262,8 @@ dyndns_tests_SOURCES = \
      $(TEST_MOCK_OBJ) \
      $(SSSD_RESOLV_OBJ) \
      src/tests/common_tev.c \
-     src/tests/cmocka/test_dyndns.c
+     src/tests/cmocka/test_dyndns.c \
+     src/providers/data_provider_opts.c
 dyndns_tests_CFLAGS = \
     $(AM_CFLAGS) \
     -DDYNDNS_TIMEOUT=2
diff --git a/src/config/SSSDConfig/__init__.py.in b/src/config/SSSDConfig/__init__.py.in
index f603a21835d818523b9ced247334b5de5fd7031c..c3bff6debfd9a522e38653bbcffbc7363f122815 100644
--- a/src/config/SSSDConfig/__init__.py.in
+++ b/src/config/SSSDConfig/__init__.py.in
@@ -125,6 +125,9 @@ option_strings = {
     'entry_cache_service_timeout' : _('Entry cache timeout length (seconds)'),
     'entry_cache_autofs_timeout' : _('Entry cache timeout length (seconds)'),
     'entry_cache_sudo_timeout' : _('Entry cache timeout length (seconds)'),
+    '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"),
 
     # [provider/ipa]
     'ipa_domain' : _('IPA domain'),
diff --git a/src/config/SSSDConfigTest.py b/src/config/SSSDConfigTest.py
index 6ae458b1a438ff0bb7b2e10765b1fff3f5494163..c0d4a1476a0e08f04e7aa24ecc5293992cced3ca 100755
--- a/src/config/SSSDConfigTest.py
+++ b/src/config/SSSDConfigTest.py
@@ -507,6 +507,9 @@ class SSSDConfigTestSSSDDomain(unittest.TestCase):
             'account_cache_expiration',
             'dns_resolver_timeout',
             'dns_discovery_domain',
+            'dyndns_update',
+            'dyndns_ttl',
+            'dyndns_iface',
             'override_gid',
             'case_sensitive',
             'override_homedir',
@@ -850,6 +853,9 @@ class SSSDConfigTestSSSDDomain(unittest.TestCase):
             'lookup_family_order',
             'dns_resolver_timeout',
             'dns_discovery_domain',
+            'dyndns_update',
+            'dyndns_ttl',
+            'dyndns_iface',
             'override_gid',
             'case_sensitive',
             'override_homedir',
diff --git a/src/config/etc/sssd.api.conf b/src/config/etc/sssd.api.conf
index 3ed9d583ccbfd8f148d5a2c79f9c658d8fd7d8f1..02e2a0a6625445f1361dceea866a32b916bea01a 100644
--- a/src/config/etc/sssd.api.conf
+++ b/src/config/etc/sssd.api.conf
@@ -121,6 +121,11 @@ entry_cache_service_timeout = int, None, false
 entry_cache_autofs_timeout = int, None, false
 entry_cache_sudo_timeout = int, None, false
 
+# Dynamic DNS updates
+dyndns_update = bool, None, false
+dyndns_ttl = int, None, false
+dyndns_iface = str, None, false
+
 # Special providers
 [provider/permit]
 
diff --git a/src/man/sssd-ipa.5.xml b/src/man/sssd-ipa.5.xml
index f8cb0939daba2daa49318d098a9fa1be307fc910..bedb1107234bf69e60cc174b1086a181fa80f806 100644
--- a/src/man/sssd-ipa.5.xml
+++ b/src/man/sssd-ipa.5.xml
@@ -113,7 +113,7 @@
                 </varlistentry>
 
                 <varlistentry>
-                    <term>ipa_dyndns_update (boolean)</term>
+                    <term>dyndns_update (boolean)</term>
                     <listitem>
                         <para>
                             Optional. This option tells SSSD to automatically
@@ -126,34 +126,52 @@
                             realm must be set properly in /etc/krb5.conf
                         </para>
                         <para>
+                            NOTE: While it is still possible to use the old
+                            <emphasis>ipa_dyndns_update</emphasis> option, users
+                            should migrate to using <emphasis>dyndns_update</emphasis>
+                            in their config file.
+                        </para>
+                        <para>
                             Default: false
                         </para>
                     </listitem>
                 </varlistentry>
 
                 <varlistentry>
-                    <term>ipa_dyndns_ttl (integer)</term>
+                    <term>dyndns_ttl (integer)</term>
                     <listitem>
                         <para>
                             The TTL to apply to the client DNS record when updating it.
-                            If ipa_dyndns_update is false this has no effect. This will
+                            If dyndns_update is false this has no effect. This will
                             override the TTL serverside if set by an administrator.
                         </para>
                         <para>
+                            NOTE: While it is still possible to use the old
+                            <emphasis>ipa_dyndns_ttl</emphasis> option, users
+                            should migrate to using <emphasis>dyndns_ttl</emphasis>
+                            in their config file.
+                        </para>
+                        <para>
                             Default: 1200 (seconds)
                         </para>
                     </listitem>
                 </varlistentry>
 
                 <varlistentry>
-                    <term>ipa_dyndns_iface (string)</term>
+                    <term>dyndns_iface (string)</term>
                     <listitem>
                         <para>
-                            Optional. Applicable only when ipa_dyndns_update
+                            Optional. Applicable only when dyndns_update
                             is true. Choose the interface whose IP address
                             should be used for dynamic DNS updates.
                         </para>
                         <para>
+                            NOTE: While it is still possible to use the old
+                            <emphasis>ipa_dyndns_iface</emphasis> option, users
+                            should migrate to using <emphasis>dyndns_iface</emphasis>
+                            in their config file.
+                        </para>
+                        <para>
                             Default: Use the IP address of the IPA LDAP connection
                         </para>
                     </listitem>
diff --git a/src/providers/dp_dyndns.c b/src/providers/dp_dyndns.c
index 7e5cc690e1d199f9c00b0b3d18168e5987ffc31f..81228d5cfb59e472167b50dda819ec4ae49a928c 100644
--- a/src/providers/dp_dyndns.c
+++ b/src/providers/dp_dyndns.c
@@ -878,3 +878,60 @@ be_nsupdate_recv(struct tevent_req *req, int *child_status)
 
     return EOK;
 }
+
+errno_t
+be_nsupdate_check(void)
+{
+    errno_t ret;
+    struct stat stat_buf;
+
+    /* Ensure that nsupdate exists */
+    errno = 0;
+    ret = stat(NSUPDATE_PATH, &stat_buf);
+    if (ret == -1) {
+        ret = errno;
+        if (ret == ENOENT) {
+            DEBUG(SSSDBG_MINOR_FAILURE,
+                  ("%s does not exist. Dynamic DNS updates disabled\n",
+                  NSUPDATE_PATH));
+        } else {
+            DEBUG(SSSDBG_OP_FAILURE,
+                  ("Could not set up dynamic DNS updates: [%d][%s]\n",
+                  ret, strerror(ret)));
+        }
+    }
+
+    return ret;
+}
+
+static struct dp_option default_dyndns_opts[] = {
+    { "dyndns_update", DP_OPT_BOOL, BOOL_FALSE, BOOL_FALSE },
+    { "dyndns_iface", DP_OPT_STRING, NULL_STRING, NULL_STRING },
+    { "dyndns_ttl", DP_OPT_NUMBER, { .number = 1200 }, NULL_NUMBER },
+
+    DP_OPTION_TERMINATOR
+};
+
+errno_t
+be_nsupdate_init(TALLOC_CTX *mem_ctx, struct be_ctx *be_ctx,
+                 struct dp_option *defopts, 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);
+    if (ctx == NULL) return ENOMEM;
+
+    src_opts = defopts ? defopts : default_dyndns_opts;
+
+    ret = dp_get_options(ctx, be_ctx->cdb, be_ctx->conf_path,
+                         src_opts, DP_OPT_DYNDNS, &ctx->opts);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_OP_FAILURE, ("Cannot retrieve dynamic DNS options\n"));
+        return ret;
+    }
+
+    *_ctx = ctx;
+    return EOK;
+}
diff --git a/src/providers/dp_dyndns.h b/src/providers/dp_dyndns.h
index b0020560a8accf8811f311c592a6bf436111156a..95403a3ae73fc47c6a9b5240c640325da1045e39 100644
--- a/src/providers/dp_dyndns.h
+++ b/src/providers/dp_dyndns.h
@@ -22,12 +22,34 @@
     along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
 
+
+#ifndef DP_DYNDNS_H_
+#define DP_DYNDNS_H_
+
 /* dynamic dns helpers */
 struct sss_iface_addr;
 
+struct be_nsupdate_ctx {
+    struct dp_option *opts;
+};
+
+enum dp_dyndns_opts {
+    DP_OPT_DYNDNS_UPDATE,
+    DP_OPT_DYNDNS_IFACE,
+    DP_OPT_DYNDNS_TTL,
+
+    DP_OPT_DYNDNS /* attrs counter */
+};
+
 #define DYNDNS_REMOVE_A     0x1
 #define DYNDNS_REMOVE_AAAA  0x2
 
+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);
+
 errno_t
 sss_iface_addr_list_get(TALLOC_CTX *mem_ctx, const char *ifname,
                         struct sss_iface_addr **_addrlist);
@@ -66,3 +88,5 @@ struct tevent_req * nsupdate_get_addrs_send(TALLOC_CTX *mem_ctx,
 errno_t nsupdate_get_addrs_recv(struct tevent_req *req,
                                 TALLOC_CTX *mem_ctx,
                                 char ***_addrlist);
+
+#endif /* DP_DYNDNS_H_ */
diff --git a/src/providers/ipa/ipa_common.c b/src/providers/ipa/ipa_common.c
index be1bd1d2f8c42143d7ff1fe205ffb774ffe19dd0..51750b2af3e679dafee8cb88081839c11a261757 100644
--- a/src/providers/ipa/ipa_common.c
+++ b/src/providers/ipa/ipa_common.c
@@ -29,6 +29,7 @@
 #include "db/sysdb_selinux.h"
 #include "providers/ipa/ipa_common.h"
 #include "providers/ldap/sdap_async_private.h"
+#include "providers/dp_dyndns.h"
 #include "util/sss_krb5.h"
 #include "db/sysdb_services.h"
 #include "db/sysdb_autofs.h"
@@ -1008,3 +1009,98 @@ done:
     talloc_free(tmp_ctx);
     return ret;
 }
+
+errno_t ipa_get_dyndns_options(struct be_ctx *be_ctx,
+                               struct ipa_options *ctx)
+{
+    errno_t ret;
+    char *val;
+    bool update;
+    int ttl;
+
+    ret = be_nsupdate_init(ctx, be_ctx, ipa_dyndns_opts, &ctx->dyndns_ctx);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_OP_FAILURE,
+              ("Cannot initialize IPA dyndns opts [%d]: %s\n",
+               ret, sss_strerror(ret)));
+        return ret;
+    }
+
+    if (ctx->basic == NULL) {
+        DEBUG(SSSDBG_MINOR_FAILURE, ("IPA basic options not (yet) "
+              "initialized, cannot copy legacy options\n"));
+        return EOK;
+    }
+
+    /* Reuse legacy option values */
+    ret = confdb_get_string(be_ctx->cdb, ctx, be_ctx->conf_path,
+                            "ipa_dyndns_update", NULL, &val);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_OP_FAILURE, ("Cannot get the value of %s\n",
+              "ipa_dyndns_update"));
+        /* Not fatal */
+    } else if (ret == EOK && val) {
+        if (strcasecmp(val, "FALSE") == 0) {
+            update = false;
+        } else if (strcasecmp(val, "TRUE") == 0) {
+            update = true;
+        } else {
+            DEBUG(SSSDBG_MINOR_FAILURE,
+                  ("ipa_dyndns_update value is not a boolean!\n"));
+            talloc_free(val);
+            return EINVAL;
+        }
+
+        DEBUG(SSSDBG_MINOR_FAILURE, ("Deprecation warning: The option %s is "
+              "deprecated and should not be used in favor of %s\n",
+              "ipa_dyndns_update", "dyndns_update"));
+
+        ret = dp_opt_set_bool(ctx->dyndns_ctx->opts,
+                              DP_OPT_DYNDNS_UPDATE, update);
+        talloc_free(val);
+        if (ret != EOK) {
+            DEBUG(SSSDBG_OP_FAILURE, ("Cannot set option value\n"));
+            return ret;
+        }
+    }
+
+    ret = confdb_get_int(be_ctx->cdb, be_ctx->conf_path,
+                         "ipa_dyndns_ttl", -1, &ttl);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_OP_FAILURE, ("Cannot get the value of %s\n",
+              "ipa_dyndns_ttl"));
+        /* Not fatal */
+    } else if (ret == EOK && ttl != -1) {
+        DEBUG(SSSDBG_MINOR_FAILURE, ("Deprecation warning: The option %s is "
+              "deprecated and should not be used in favor of %s\n",
+              "ipa_dyndns_ttl", "dyndns_ttl"));
+
+        ret = dp_opt_set_int(ctx->dyndns_ctx->opts, DP_OPT_DYNDNS_TTL, ttl);
+        if (ret != EOK) {
+            DEBUG(SSSDBG_OP_FAILURE, ("Cannot set option value\n"));
+            return ret;
+        }
+    }
+
+    /* Reuse legacy option values */
+    ret = confdb_get_string(be_ctx->cdb, ctx, be_ctx->conf_path,
+                            "ipa_dyndns_iface", NULL, &val);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_OP_FAILURE, ("Cannot get the value of %s\n",
+              "ipa_dyndns_iface"));
+        /* Not fatal */
+    } else if (ret == EOK && val) {
+        DEBUG(SSSDBG_MINOR_FAILURE, ("Deprecation warning: The option %s is "
+              "deprecated and should not be used in favor of %s\n",
+              "ipa_dyndns_iface", "dyndns_iface"));
+
+        ret = dp_opt_set_string(ctx->dyndns_ctx->opts,
+                                DP_OPT_DYNDNS_IFACE, val);
+        if (ret != EOK) {
+            DEBUG(SSSDBG_OP_FAILURE, ("Cannot set option value\n"));
+            return ret;
+        }
+    }
+
+    return EOK;
+}
diff --git a/src/providers/ipa/ipa_common.h b/src/providers/ipa/ipa_common.h
index 52a0afe19ccfbfd143e4787b3cd05527291ac3d6..2784eeed3d134b2212e57dc88d1e2a391b468fd4 100644
--- a/src/providers/ipa/ipa_common.h
+++ b/src/providers/ipa/ipa_common.h
@@ -37,9 +37,6 @@ enum ipa_basic_opt {
     IPA_SERVER,
     IPA_BACKUP_SERVER,
     IPA_HOSTNAME,
-    IPA_DYNDNS_UPDATE,
-    IPA_DYNDNS_TTL,
-    IPA_DYNDNS_IFACE,
     IPA_HBAC_SEARCH_BASE,
     IPA_HOST_SEARCH_BASE,
     IPA_SELINUX_SEARCH_BASE,
@@ -139,6 +136,7 @@ struct ipa_options {
     struct sdap_options *id;
     struct ipa_id_ctx *id_ctx;
     struct be_resolv_ctx *be_res;
+    struct be_nsupdate_ctx *dyndns_ctx;
 
     /* auth and chpass provider */
     struct dp_option *auth;
@@ -169,6 +167,9 @@ int ipa_get_autofs_options(struct ipa_options *ipa_opts,
                            const char *conf_path,
                            struct sdap_options **_opts);
 
+errno_t ipa_get_dyndns_options(struct be_ctx *be_ctx,
+                               struct ipa_options *ctx);
+
 int ipa_autofs_init(struct be_ctx *be_ctx,
                     struct ipa_id_ctx *id_ctx,
                     struct bet_ops **ops,
diff --git a/src/providers/ipa/ipa_dyndns.c b/src/providers/ipa/ipa_dyndns.c
index 79918a261be9c15a4f86c2885ac8723b5ed51cb6..8023b5336b0800b6bb019afe5a6337f43a3f2a82 100644
--- a/src/providers/ipa/ipa_dyndns.c
+++ b/src/providers/ipa/ipa_dyndns.c
@@ -28,6 +28,7 @@
 #include "providers/ipa/ipa_common.h"
 #include "providers/ipa/ipa_dyndns.h"
 #include "providers/data_provider.h"
+#include "providers/dp_dyndns.h"
 
 void ipa_dyndns_update(void *pvt);
 
@@ -135,16 +136,16 @@ ipa_dyndns_update_send(struct ipa_options *ctx)
 
     subreq = sdap_dyndns_update_send(state, sdap_ctx->be->ev,
                                      sdap_ctx->be, sdap_ctx,
-                                     dp_opt_get_string(ctx->basic,
-                                                       IPA_DYNDNS_IFACE),
+                                     dp_opt_get_string(ctx->dyndns_ctx->opts,
+                                                       DP_OPT_DYNDNS_IFACE),
                                      dp_opt_get_string(ctx->basic,
                                                        IPA_HOSTNAME),
                                      dns_zone,
                                      dp_opt_get_string(ctx->basic,
                                                        IPA_KRB5_REALM),
                                      servername,
-                                     dp_opt_get_int(ctx->basic,
-                                                    IPA_DYNDNS_TTL),
+                                     dp_opt_get_int(ctx->dyndns_ctx->opts,
+                                                    DP_OPT_DYNDNS_TTL),
                                      true);
     if (!subreq) {
         ret = EIO;
diff --git a/src/providers/ipa/ipa_init.c b/src/providers/ipa/ipa_init.c
index b65a6cea1ddd05272a8ae522be5c8b070a93ac1e..404bf53e4362da12df3008bdde78229a7288f678 100644
--- a/src/providers/ipa/ipa_init.c
+++ b/src/providers/ipa/ipa_init.c
@@ -40,6 +40,7 @@
 #include "providers/ldap/sdap_access.h"
 #include "providers/ipa/ipa_subdomains.h"
 #include "providers/ipa/ipa_srv.h"
+#include "providers/dp_dyndns.h"
 
 struct ipa_options *ipa_options = NULL;
 
@@ -110,11 +111,9 @@ int sssm_ipa_id_init(struct be_ctx *bectx,
 {
     struct ipa_id_ctx *ipa_ctx;
     struct sdap_id_ctx *sdap_ctx;
-    struct stat stat_buf;
     const char *hostname;
     const char *ipa_domain;
     struct ipa_srv_plugin_ctx *srv_ctx;
-    errno_t err;
     int ret;
 
     if (!ipa_options) {
@@ -153,7 +152,12 @@ int sssm_ipa_id_init(struct be_ctx *bectx,
         goto done;
     }
 
-    if(dp_opt_get_bool(ipa_options->basic, IPA_DYNDNS_UPDATE)) {
+    ret = ipa_get_dyndns_options(bectx, ipa_options);
+    if (ret != EOK) {
+        goto done;
+    }
+
+    if (dp_opt_get_bool(ipa_options->dyndns_ctx->opts, DP_OPT_DYNDNS_UPDATE)) {
         /* Perform automatic DNS updates when the
          * IP address changes.
          * Register a callback for successful LDAP
@@ -161,21 +165,10 @@ int sssm_ipa_id_init(struct be_ctx *bectx,
          * identify that we have gone online.
          */
 
-        /* Ensure that nsupdate exists */
-        errno = 0;
-        ret = stat(NSUPDATE_PATH, &stat_buf);
-        if (ret == -1) {
-            err = errno;
-            if (err == ENOENT) {
-                DEBUG(0, ("%s does not exist. Dynamic DNS updates disabled\n",
-                          NSUPDATE_PATH));
-            }
-            else {
-                DEBUG(0, ("Could not set up dynamic DNS updates: [%d][%s]\n",
-                          err, strerror(err)));
-            }
-        }
-        else {
+        DEBUG(SSSDBG_CONF_SETTINGS,
+              ("Dynamic DNS updates are on. Checking for nsupdate..\n"));
+        ret = be_nsupdate_check();
+        if (ret == EOK) {
             /* nsupdate is available. Dynamic updates
              * are supported
              */
diff --git a/src/providers/ipa/ipa_opts.h b/src/providers/ipa/ipa_opts.h
index 6adbdd96a907c26938b8cb80d22f156b09bbacc3..392fcd86fbe57c7b699390ed68918208886ffaab 100644
--- a/src/providers/ipa/ipa_opts.h
+++ b/src/providers/ipa/ipa_opts.h
@@ -35,9 +35,6 @@ struct dp_option ipa_basic_opts[] = {
     { "ipa_server", DP_OPT_STRING, NULL_STRING, NULL_STRING },
     { "ipa_backup_server", DP_OPT_STRING, NULL_STRING, NULL_STRING },
     { "ipa_hostname", DP_OPT_STRING, NULL_STRING, NULL_STRING },
-    { "ipa_dyndns_update", DP_OPT_BOOL, BOOL_FALSE, BOOL_FALSE },
-    { "ipa_dyndns_ttl", DP_OPT_NUMBER, { .number = 1200}, NULL_NUMBER},
-    { "ipa_dyndns_iface", DP_OPT_STRING, NULL_STRING, NULL_STRING},
     { "ipa_hbac_search_base", DP_OPT_STRING, NULL_STRING, NULL_STRING},
     { "ipa_host_search_base", DP_OPT_STRING, NULL_STRING, NULL_STRING },
     { "ipa_selinux_search_base", DP_OPT_STRING, NULL_STRING, NULL_STRING },
@@ -54,6 +51,13 @@ struct dp_option ipa_basic_opts[] = {
     DP_OPTION_TERMINATOR
 };
 
+struct dp_option ipa_dyndns_opts[] = {
+    { "dyndns_update", DP_OPT_BOOL, BOOL_FALSE, BOOL_FALSE },
+    { "dyndns_iface", DP_OPT_STRING, NULL_STRING, NULL_STRING },
+    { "dyndns_ttl", DP_OPT_NUMBER, { .number = 1200 }, NULL_NUMBER },
+    DP_OPTION_TERMINATOR
+};
+
 struct dp_option ipa_def_ldap_opts[] = {
     { "ldap_uri", DP_OPT_STRING, NULL_STRING, NULL_STRING },
     { "ldap_backup_uri", DP_OPT_STRING, NULL_STRING, NULL_STRING },
-- 
1.8.2.1



More information about the sssd-devel mailing list