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

Jakub Hrozek jhrozek at redhat.com
Thu Apr 11 08:16:52 UTC 2013


These two patches depend on patches in that decouple the memory checking
from the check tool.

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

The DP layer includes unit tests that also make sure that
https://fedorahosted.org/sssd/ticket/1802 is fixed.

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

These options will be reused in the AD dynamic DNS updates code.
-------------- next part --------------
>From 0d2ebf6e61f33dd6a19b3bb16cf58546d633da56 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                      |   28 +-
 src/providers/dp_dyndns.c        |  868 ++++++++++++++++++++++++++
 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   |  251 ++++++++
 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, 1868 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 09056b5d9fec1d23213132940d11c312bcbfc23d..3d7f89e5dee6c089564919d9cafe670f66b146c6 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 = \
@@ -431,6 +432,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 \
@@ -452,6 +454,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 \
@@ -684,6 +687,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 \
@@ -1189,8 +1193,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) \
@@ -1248,6 +1253,22 @@ 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 \
+     src/providers/dp_dyndns.c
+dyndns_tests_CFLAGS = \
+    $(AM_CFLAGS) \
+    -DDYNDNS_TIMEOUT=2
+dyndns_tests_LDFLAGS = \
+    -Wl,-wrap,execv
+dyndns_tests_LDADD = \
+    $(CARES_LIBS) \
+    $(CMOCKA_LIBS) \
+    libsss_util.la
 endif
 
 noinst_PROGRAMS = pam_test_client
@@ -1388,6 +1409,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..81d1f330a69af701a98504e927f8ec2534ee81d6
--- /dev/null
+++ b/src/providers/dp_dyndns.c
@@ -0,0 +1,868 @@
+/*
+    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) {
+        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];
+
+    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;
+    }
+
+    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;
+
+    /* in some cases realm could have been NULL if we weren't using TSIG */
+    if (zone == NULL || hostname == NULL) {
+        return EINVAL;
+    }
+
+#ifdef HAVE_NSUPDATE_REALM
+    realm_directive = talloc_asprintf(mem_ctx, "realm %s\n", realm);
+#else
+    realm_directive = talloc_asprintf(mem_ctx, "");
+#endif
+    if (!realm_directive) {
+        ret = ENOMEM;
+        goto done;
+    }
+
+    if (servername) {
+        DEBUG(SSSDBG_FUNC_DATA,
+              ("Creating update message for server [%s], realm [%s] "
+               "and zone [%s].\n", servername, realm, zone));
+
+        /* Add the server, realm and zone headers */
+        update_msg = talloc_asprintf(mem_ctx, "server %s\n%szone %s.\n",
+                                     servername, realm_directive, zone);
+    } else {
+        DEBUG(SSSDBG_FUNC_DATA,
+              ("Creating update message for realm [%s] and zone [%s].\n",
+               realm, zone));
+
+        /* Add the realm and zone headers */
+        update_msg = talloc_asprintf(mem_ctx, "%szone %s.\n",
+                                     realm_directive, zone);
+    }
+    talloc_free(realm_directive);
+    if (update_msg == NULL) {
+        ret = ENOMEM;
+        goto done;
+    }
+
+    /* 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 = 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(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:
+    return ret;
+}
+
+struct nsupdate_get_addrs_state {
+    struct tevent_context *ev;
+    struct be_resolv_ctx *be_res;
+    enum host_database *db;
+    enum restrict_family family_order;
+    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->family_order = be_res->family_order;
+    state->ev = ev;
+    state->hostname = hostname;
+
+    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->family_order,
+                                       state->db);
+    if (!subreq) {
+        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
+         * 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;
+
+            subreq = resolv_gethostbyname_send(state, state->ev, state->be_res->resolv,
+                                               state->hostname, state->family_order,
+                                               state->db);
+            if (!subreq) {
+                tevent_req_error(req, ENOMEM);
+                return;
+            }
+            tevent_req_set_callback(subreq, nsupdate_get_addrs_done, req);
+            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, sss_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;
+
+        subreq = resolv_gethostbyname_send(state, state->ev, state->be_res->resolv,
+                                           state->hostname, state->family_order,
+                                           state->db);
+        if (!subreq) {
+            tevent_req_error(req, ENOMEM);
+            return;
+        }
+        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. */
+    tevent_req_done(req);
+    return;
+}
+
+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..d1d96904fcda3a3b4e07e9407990dbf97a555af6
--- /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) {
+        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) {
+            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..81cc3d0f4bc6b49fb8e3eb2a383c9b0da4f40864
--- /dev/null
+++ b/src/tests/cmocka/test_dyndns.c
@@ -0,0 +1,251 @@
+/*
+    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 "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);
+}
+
+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 dyndns_test_get_ifaddr(void **state)
+{
+#if 0
+    errno_t ret;
+    struct sss_iface_addr *addrlist;
+
+    check_leaks_push(dyndns_test_ctx);
+    ret = sss_iface_addr_list_get(dyndns_test_ctx, "wlan0", &addrlist);
+    assert_int_equal(ret, EOK);
+
+    talloc_free(addrlist);
+    assert_true(check_leaks_pop(dyndns_test_ctx) == true);
+#endif
+}
+
+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 f835621004d19259b9d9411da6c1d64893a82ebb..31b2590cc2257815811c3c80f57b6c0991a1a728 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.1.4

-------------- next part --------------
>From ab5cc1b4d72eed8b9f81c24bb3857cf99d54b1f1 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               | 24 +++++++--
 src/providers/dp_dyndns.c            | 57 +++++++++++++++++++++
 src/providers/dp_dyndns.h            | 27 ++++++++++
 src/providers/ipa/ipa_common.c       | 96 ++++++++++++++++++++++++++++++++++++
 src/providers/ipa/ipa_common.h       |  9 ++--
 src/providers/ipa/ipa_dyndns.c       |  9 ++--
 src/providers/ipa/ipa_init.c         | 29 +++++------
 src/providers/ipa/ipa_opts.h         | 10 ++--
 12 files changed, 245 insertions(+), 33 deletions(-)

diff --git a/Makefile.am b/Makefile.am
index 3d7f89e5dee6c089564919d9cafe670f66b146c6..1c4738051234060f7f6646636b3be3f747ed475a 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1259,7 +1259,8 @@ dyndns_tests_SOURCES = \
      $(SSSD_RESOLV_OBJ) \
      src/tests/common_tev.c \
      src/tests/cmocka/test_dyndns.c \
-     src/providers/dp_dyndns.c
+     src/providers/dp_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 1f997f23813d3e6e3f7360ae8d9e0b8aea391345..282d65293c853bb8ddd1a89334a827708a2737d0 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 8f003f5518dbfa7ec2b5c44bbfb4bb36658c7120..f819d9f41d656d023fc682606a16bbcbd40bb244 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',
@@ -848,6 +851,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..b42c0a74e693a91a95ba3a6e5d15ce127abd9de2 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,13 +126,19 @@
                             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.
@@ -140,13 +146,19 @@
                             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
@@ -154,6 +166,12 @@
                             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 81d1f330a69af701a98504e927f8ec2534ee81d6..1d7fee8da3278b7a0339233c637c6a857630b2f2 100644
--- a/src/providers/dp_dyndns.c
+++ b/src/providers/dp_dyndns.c
@@ -866,3 +866,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..3726f9850aeefc00b33dd6a051fbac96947c6460 100644
--- a/src/providers/dp_dyndns.h
+++ b/src/providers/dp_dyndns.h
@@ -22,12 +22,37 @@
     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_opts(TALLOC_CTX *mem_ctx, struct be_ctx *be_ctx,
+                              struct dp_option *defopts,
+                              struct dp_option **_opts);
+
+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 +91,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..6bb5682e1ff4139e9529bc3837369a752a02baf0 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,
@@ -138,7 +135,8 @@ struct ipa_options {
     /* id provider */
     struct sdap_options *id;
     struct ipa_id_ctx *id_ctx;
-    struct be_resolv_ctx *be_res;
+    struct be_resolv_ctx *be_res; /* Is it needed still? */
+    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 a3d0210a2dd80500ef27f78b7e85b1a5cd7cca4a..936407e04d25035b89c1cbe53858cf10943d799f 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.1.4



More information about the sssd-devel mailing list