s4 dns_server Bind9: Log opertion durations
[samba.git] / source4 / dns_server / dlz_bind9.c
index 5edf0506b8db10de5b048af22d5ce47b8014fd86..5db1131e4dd9eb91d114192f4e68ded7bedef867 100644 (file)
 #include "gen_ndr/ndr_dnsp.h"
 #include "gen_ndr/server_id.h"
 #include "messaging/messaging.h"
-#include "lib/cmdline/popt_common.h"
+#include <popt.h>
 #include "lib/util/dlinklist.h"
 #include "dlz_minimal.h"
-#include "dns_server/dnsserver_common.h"
+#include "dnsserver_common.h"
 
 struct b9_options {
        const char *url;
@@ -82,6 +82,38 @@ static const char *zone_prefixes[] = {
        NULL
 };
 
+/*
+ * Get a printable string representation of an isc_result_t
+ */
+static const char *isc_result_str( const isc_result_t result) {
+       switch (result) {
+       case ISC_R_SUCCESS:
+               return "ISC_R_SUCCESS";
+       case ISC_R_NOMEMORY:
+               return "ISC_R_NOMEMORY";
+       case ISC_R_NOPERM:
+               return "ISC_R_NOPERM";
+       case ISC_R_NOSPACE:
+               return "ISC_R_NOSPACE";
+       case ISC_R_NOTFOUND:
+               return "ISC_R_NOTFOUND";
+       case ISC_R_FAILURE:
+               return "ISC_R_FAILURE";
+       case ISC_R_NOTIMPLEMENTED:
+               return "ISC_R_NOTIMPLEMENTED";
+       case ISC_R_NOMORE:
+               return "ISC_R_NOMORE";
+       case ISC_R_INVALIDFILE:
+               return "ISC_R_INVALIDFILE";
+       case ISC_R_UNEXPECTED:
+               return "ISC_R_UNEXPECTED";
+       case ISC_R_FILENOTFOUND:
+               return "ISC_R_FILENOTFOUND";
+       default:
+               return "UNKNOWN";
+       }
+}
+
 /*
   return the version of the API
  */
@@ -109,6 +141,27 @@ static void b9_add_helper(struct dlz_bind9_data *state, const char *helper_name,
        }
 }
 
+/*
+ * Add a trailing '.' if it's missing
+ */
+static const char *b9_format_fqdn(TALLOC_CTX *mem_ctx, const char *str)
+{
+       size_t len;
+       const char *tmp;
+
+       if (str == NULL || str[0] == '\0') {
+               return str;
+       }
+
+       len = strlen(str);
+       if (str[len-1] != '.') {
+               tmp = talloc_asprintf(mem_ctx, "%s.", str);
+       } else {
+               tmp = str;
+       }
+       return tmp;
+}
+
 /*
   format a record for bind9
  */
@@ -119,6 +172,7 @@ static bool b9_format(struct dlz_bind9_data *state,
 {
        uint32_t i;
        char *tmp;
+       const char *fqdn;
 
        switch (rec->wType) {
        case DNS_TYPE_A:
@@ -133,7 +187,7 @@ static bool b9_format(struct dlz_bind9_data *state,
 
        case DNS_TYPE_CNAME:
                *type = "cname";
-               *data = rec->data.cname;
+               *data = b9_format_fqdn(mem_ctx, rec->data.cname);
                break;
 
        case DNS_TYPE_TXT:
@@ -147,23 +201,30 @@ static bool b9_format(struct dlz_bind9_data *state,
 
        case DNS_TYPE_PTR:
                *type = "ptr";
-               *data = rec->data.ptr;
+               *data = b9_format_fqdn(mem_ctx, rec->data.ptr);
                break;
 
        case DNS_TYPE_SRV:
                *type = "srv";
+               fqdn = b9_format_fqdn(mem_ctx, rec->data.srv.nameTarget);
+               if (fqdn == NULL) {
+                       return false;
+               }
                *data = talloc_asprintf(mem_ctx, "%u %u %u %s",
                                        rec->data.srv.wPriority,
                                        rec->data.srv.wWeight,
                                        rec->data.srv.wPort,
-                                       rec->data.srv.nameTarget);
+                                       fqdn);
                break;
 
        case DNS_TYPE_MX:
                *type = "mx";
+               fqdn = b9_format_fqdn(mem_ctx, rec->data.mx.nameTarget);
+               if (fqdn == NULL) {
+                       return false;
+               }
                *data = talloc_asprintf(mem_ctx, "%u %s",
-                                       rec->data.mx.wPriority,
-                                       rec->data.mx.nameTarget);
+                                       rec->data.mx.wPriority, fqdn);
                break;
 
        case DNS_TYPE_HINFO:
@@ -175,7 +236,7 @@ static bool b9_format(struct dlz_bind9_data *state,
 
        case DNS_TYPE_NS:
                *type = "ns";
-               *data = rec->data.ns;
+               *data = b9_format_fqdn(mem_ctx, rec->data.ns);
                break;
 
        case DNS_TYPE_SOA: {
@@ -186,8 +247,9 @@ static bool b9_format(struct dlz_bind9_data *state,
                 * point at ourselves. This is how AD DNS servers
                 * force clients to send updates to the right local DC
                 */
-               mname = talloc_asprintf(mem_ctx, "%s.%s",
-                                       lpcfg_netbios_name(state->lp), lpcfg_dnsdomain(state->lp));
+               mname = talloc_asprintf(mem_ctx, "%s.%s.",
+                                       lpcfg_netbios_name(state->lp),
+                                       lpcfg_dnsdomain(state->lp));
                if (mname == NULL) {
                        return false;
                }
@@ -196,11 +258,15 @@ static bool b9_format(struct dlz_bind9_data *state,
                        return false;
                }
 
+               fqdn = b9_format_fqdn(mem_ctx, rec->data.soa.rname);
+               if (fqdn == NULL) {
+                       return false;
+               }
+
                state->soa_serial = rec->data.soa.serial;
 
                *data = talloc_asprintf(mem_ctx, "%s %s %u %u %u %u %u",
-                                       mname,
-                                       rec->data.soa.rname,
+                                       mname, fqdn,
                                        rec->data.soa.serial,
                                        rec->data.soa.refresh,
                                        rec->data.soa.retry,
@@ -273,8 +339,12 @@ static bool b9_dns_type(const char *type, enum dns_record_type *dtype)
 
 #define DNS_PARSE_UINT(ret, str, sep, saveptr) do {  \
        char *istr = strtok_r(str, sep, &saveptr); \
+       int error = 0;\
        if ((istr) == NULL) return false; \
-       (ret) = strtoul(istr, NULL, 10); \
+       (ret) = strtoul_err(istr, NULL, 10, &error); \
+       if (error != 0) {\
+               return false;\
+       }\
        } while (0)
 
 /*
@@ -580,8 +650,13 @@ _PUBLIC_ isc_result_t dlz_create(const char *dlzname,
        isc_result_t result;
        struct ldb_dn *dn;
        NTSTATUS nt_status;
+       int ret;
+       char *errstring = NULL;
 
        if (dlz_bind9_state != NULL) {
+               dlz_bind9_state->log(ISC_LOG_ERROR,
+                                    "samba_dlz: dlz_create ignored, #refs=%d",
+                                    dlz_bind9_state_ref_count);
                *dbdata = dlz_bind9_state;
                dlz_bind9_state_ref_count++;
                return ISC_R_SUCCESS;
@@ -648,18 +723,38 @@ _PUBLIC_ isc_result_t dlz_create(const char *dlzname,
        }
 
        if (state->options.url == NULL) {
-               state->options.url = lpcfg_private_path(state, state->lp, "dns/sam.ldb");
+               state->options.url = talloc_asprintf(state,
+                                                    "%s/dns/sam.ldb",
+                                                    lpcfg_binddns_dir(state->lp));
                if (state->options.url == NULL) {
                        result = ISC_R_NOMEMORY;
                        goto failed;
                }
+
+               if (!file_exist(state->options.url)) {
+                       state->options.url = talloc_asprintf(state,
+                                                            "%s/dns/sam.ldb",
+                                                            lpcfg_private_dir(state->lp));
+                       if (state->options.url == NULL) {
+                               result = ISC_R_NOMEMORY;
+                               goto failed;
+                       }
+               }
        }
 
-       state->samdb = samdb_connect_url(state, state->ev_ctx, state->lp,
-                                       system_session(state->lp), 0, state->options.url);
-       if (state->samdb == NULL) {
-               state->log(ISC_LOG_ERROR, "samba_dlz: Failed to connect to %s",
-                       state->options.url);
+       ret = samdb_connect_url(state,
+                               state->ev_ctx,
+                               state->lp,
+                               system_session(state->lp),
+                               0,
+                               state->options.url,
+                               NULL,
+                               &state->samdb,
+                               &errstring);
+       if (ret != LDB_SUCCESS) {
+               state->log(ISC_LOG_ERROR,
+                          "samba_dlz: Failed to connect to %s: %s",
+                          errstring, ldb_strerror(ret));
                result = ISC_R_FAILURE;
                goto failed;
        }
@@ -687,6 +782,10 @@ _PUBLIC_ isc_result_t dlz_create(const char *dlzname,
        return ISC_R_SUCCESS;
 
 failed:
+       state->log(ISC_LOG_INFO,
+                  "samba_dlz: FAILED dlz_create call result=%d #refs=%d",
+                  result,
+                  dlz_bind9_state_ref_count);
        talloc_free(state);
        return result;
 }
@@ -697,13 +796,17 @@ failed:
 _PUBLIC_ void dlz_destroy(void *dbdata)
 {
        struct dlz_bind9_data *state = talloc_get_type_abort(dbdata, struct dlz_bind9_data);
-       state->log(ISC_LOG_INFO, "samba_dlz: shutting down");
 
        dlz_bind9_state_ref_count--;
        if (dlz_bind9_state_ref_count == 0) {
+               state->log(ISC_LOG_INFO, "samba_dlz: shutting down");
                talloc_unlink(state, state->samdb);
                talloc_free(state);
                dlz_bind9_state = NULL;
+       } else {
+               state->log(ISC_LOG_INFO,
+                          "samba_dlz: dlz_destroy called. %d refs remaining.",
+                          dlz_bind9_state_ref_count);
        }
 }
 
@@ -720,8 +823,11 @@ static isc_result_t b9_find_zone_dn(struct dlz_bind9_data *state, const char *zo
        int i;
 
        for (i=0; zone_prefixes[i]; i++) {
+               const char *casefold;
                struct ldb_dn *dn;
                struct ldb_result *res;
+               struct ldb_val zone_name_val
+                       = data_blob_string_const(zone_name);
 
                dn = ldb_dn_copy(tmp_ctx, ldb_get_default_basedn(state->samdb));
                if (dn == NULL) {
@@ -729,11 +835,40 @@ static isc_result_t b9_find_zone_dn(struct dlz_bind9_data *state, const char *zo
                        return ISC_R_NOMEMORY;
                }
 
-               if (!ldb_dn_add_child_fmt(dn, "DC=%s,%s", zone_name, zone_prefixes[i])) {
+               /*
+                * This dance ensures that it is not possible to put
+                * (eg) an extra DC=x, into the DNS name being
+                * queried
+                */
+
+               if (!ldb_dn_add_child_fmt(dn,
+                                         "DC=X,%s",
+                                         zone_prefixes[i])) {
                        talloc_free(tmp_ctx);
                        return ISC_R_NOMEMORY;
                }
 
+               ret = ldb_dn_set_component(dn,
+                                          0,
+                                          "DC",
+                                          zone_name_val);
+               if (ret != LDB_SUCCESS) {
+                       talloc_free(tmp_ctx);
+                       return ISC_R_NOMEMORY;
+               }
+
+               /*
+                * Check if this is a plausibly valid DN early
+                * (time spent here will be saved during the
+                * search due to an internal cache)
+                */
+               casefold = ldb_dn_get_casefold(dn);
+
+               if (casefold == NULL) {
+                       talloc_free(tmp_ctx);
+                       return ISC_R_NOTFOUND;
+               }
+
                ret = ldb_search(state->samdb, tmp_ctx, &res, dn, LDB_SCOPE_BASE, attrs, "objectClass=dnsZone");
                if (ret == LDB_SUCCESS) {
                        if (zone_dn != NULL) {
@@ -764,19 +899,42 @@ static isc_result_t b9_find_name_dn(struct dlz_bind9_data *state, const char *na
                isc_result_t result;
                result = b9_find_zone_dn(state, p, mem_ctx, dn);
                if (result == ISC_R_SUCCESS) {
+                       const char *casefold;
+
                        /* we found a zone, now extend the DN to get
                         * the full DN
                         */
                        bool ret;
                        if (p == name) {
                                ret = ldb_dn_add_child_fmt(*dn, "DC=@");
+                               if (ret == false) {
+                                       talloc_free(*dn);
+                                       return ISC_R_NOMEMORY;
+                               }
                        } else {
-                               ret = ldb_dn_add_child_fmt(*dn, "DC=%.*s", (int)(p-name)-1, name);
+                               struct ldb_val name_val
+                                       = data_blob_const(name,
+                                                         (int)(p-name)-1);
+
+                               if (!ldb_dn_add_child_val(*dn,
+                                                         "DC",
+                                                         name_val)) {
+                                       talloc_free(*dn);
+                                       return ISC_R_NOMEMORY;
+                               }
                        }
-                       if (!ret) {
-                               talloc_free(*dn);
-                               return ISC_R_NOMEMORY;
+
+                       /*
+                        * Check if this is a plausibly valid DN early
+                        * (time spent here will be saved during the
+                        * search due to an internal cache)
+                        */
+                       casefold = ldb_dn_get_casefold(*dn);
+
+                       if (casefold == NULL) {
+                               return ISC_R_NOTFOUND;
                        }
+
                        return ISC_R_SUCCESS;
                }
                p = strchr(p, '.');
@@ -792,10 +950,26 @@ static isc_result_t b9_find_name_dn(struct dlz_bind9_data *state, const char *na
 /*
   see if we handle a given zone
  */
+#if DLZ_DLOPEN_VERSION < 3
 _PUBLIC_ isc_result_t dlz_findzonedb(void *dbdata, const char *name)
+#else
+_PUBLIC_ isc_result_t dlz_findzonedb(void *dbdata, const char *name,
+                                    dns_clientinfomethods_t *methods,
+                                    dns_clientinfo_t *clientinfo)
+#endif
 {
+       struct timeval start = timeval_current();
        struct dlz_bind9_data *state = talloc_get_type_abort(dbdata, struct dlz_bind9_data);
-       return b9_find_zone_dn(state, name, NULL, NULL);
+       isc_result_t result = ISC_R_SUCCESS;
+
+       result = b9_find_zone_dn(state, name, NULL, NULL);
+        DNS_COMMON_LOG_OPERATION(
+               isc_result_str(result),
+               &start,
+               NULL,
+               name,
+               NULL);
+       return result;
 }
 
 
@@ -812,21 +986,65 @@ static isc_result_t dlz_lookup_types(struct dlz_bind9_data *state,
        WERROR werr = WERR_DNS_ERROR_NAME_DOES_NOT_EXIST;
        struct dnsp_DnssrvRpcRecord *records = NULL;
        uint16_t num_records = 0, i;
+       struct ldb_val zone_name_val
+               = data_blob_string_const(zone);
+       struct ldb_val name_val
+               = data_blob_string_const(name);
 
        for (i=0; zone_prefixes[i]; i++) {
+               int ret;
+               const char *casefold;
                dn = ldb_dn_copy(tmp_ctx, ldb_get_default_basedn(state->samdb));
                if (dn == NULL) {
                        talloc_free(tmp_ctx);
                        return ISC_R_NOMEMORY;
                }
 
-               if (!ldb_dn_add_child_fmt(dn, "DC=%s,DC=%s,%s", name, zone, zone_prefixes[i])) {
+               /*
+                * This dance ensures that it is not possible to put
+                * (eg) an extra DC=x, into the DNS name being
+                * queried
+                */
+
+               if (!ldb_dn_add_child_fmt(dn,
+                                         "DC=X,DC=X,%s",
+                                         zone_prefixes[i])) {
                        talloc_free(tmp_ctx);
                        return ISC_R_NOMEMORY;
                }
 
-               werr = dns_common_lookup(state->samdb, tmp_ctx, dn,
-                                        &records, &num_records, NULL);
+               ret = ldb_dn_set_component(dn,
+                                          1,
+                                          "DC",
+                                          zone_name_val);
+               if (ret != LDB_SUCCESS) {
+                       talloc_free(tmp_ctx);
+                       return ISC_R_NOMEMORY;
+               }
+
+               ret = ldb_dn_set_component(dn,
+                                          0,
+                                          "DC",
+                                          name_val);
+               if (ret != LDB_SUCCESS) {
+                       talloc_free(tmp_ctx);
+                       return ISC_R_NOMEMORY;
+               }
+
+               /*
+                * Check if this is a plausibly valid DN early
+                * (time spent here will be saved during the
+                * search due to an internal cache)
+                */
+               casefold = ldb_dn_get_casefold(dn);
+
+               if (casefold == NULL) {
+                       talloc_free(tmp_ctx);
+                       return ISC_R_NOTFOUND;
+               }
+
+               werr = dns_common_wildcard_lookup(state->samdb, tmp_ctx, dn,
+                                        &records, &num_records);
                if (W_ERROR_IS_OK(werr)) {
                        break;
                }
@@ -853,7 +1071,7 @@ static isc_result_t dlz_lookup_types(struct dlz_bind9_data *state,
 /*
   lookup one record
  */
-#ifdef BIND_VERSION_9_8
+#if DLZ_DLOPEN_VERSION == 1
 _PUBLIC_ isc_result_t dlz_lookup(const char *zone, const char *name,
                                 void *dbdata, dns_sdlzlookup_t *lookup)
 #else
@@ -864,7 +1082,18 @@ _PUBLIC_ isc_result_t dlz_lookup(const char *zone, const char *name,
 #endif
 {
        struct dlz_bind9_data *state = talloc_get_type_abort(dbdata, struct dlz_bind9_data);
-       return dlz_lookup_types(state, zone, name, lookup, NULL);
+       isc_result_t result = ISC_R_SUCCESS;
+       struct timeval start = timeval_current();
+
+       result = dlz_lookup_types(state, zone, name, lookup, NULL);
+       DNS_COMMON_LOG_OPERATION(
+               isc_result_str(result),
+               &start,
+               zone,
+               name,
+               NULL);
+
+       return result;
 }
 
 
@@ -874,7 +1103,9 @@ _PUBLIC_ isc_result_t dlz_lookup(const char *zone, const char *name,
 _PUBLIC_ isc_result_t dlz_allowzonexfr(void *dbdata, const char *name, const char *client)
 {
        /* just say yes for all our zones for now */
-       return dlz_findzonedb(dbdata, name);
+       struct dlz_bind9_data *state = talloc_get_type(
+               dbdata, struct dlz_bind9_data);
+       return b9_find_zone_dn(state, name, NULL, NULL);
 }
 
 /*
@@ -883,23 +1114,61 @@ _PUBLIC_ isc_result_t dlz_allowzonexfr(void *dbdata, const char *name, const cha
 _PUBLIC_ isc_result_t dlz_allnodes(const char *zone, void *dbdata,
                                   dns_sdlzallnodes_t *allnodes)
 {
+       struct timeval start = timeval_current();
        struct dlz_bind9_data *state = talloc_get_type_abort(dbdata, struct dlz_bind9_data);
        const char *attrs[] = { "dnsRecord", NULL };
-       int ret = LDB_SUCCESS, i, j;
-       struct ldb_dn *dn;
+       int ret = LDB_ERR_NO_SUCH_OBJECT;
+       size_t i, j;
+       struct ldb_dn *dn = NULL;
        struct ldb_result *res;
        TALLOC_CTX *tmp_ctx = talloc_new(state);
+       struct ldb_val zone_name_val = data_blob_string_const(zone);
+       isc_result_t result = ISC_R_SUCCESS;
 
        for (i=0; zone_prefixes[i]; i++) {
+               const char *casefold;
+
                dn = ldb_dn_copy(tmp_ctx, ldb_get_default_basedn(state->samdb));
                if (dn == NULL) {
                        talloc_free(tmp_ctx);
-                       return ISC_R_NOMEMORY;
+                       result = ISC_R_NOMEMORY;
+                       goto exit;
                }
 
-               if (!ldb_dn_add_child_fmt(dn, "DC=%s,%s", zone, zone_prefixes[i])) {
+               /*
+                * This dance ensures that it is not possible to put
+                * (eg) an extra DC=x, into the DNS name being
+                * queried
+                */
+
+               if (!ldb_dn_add_child_fmt(dn,
+                                         "DC=X,%s",
+                                         zone_prefixes[i])) {
                        talloc_free(tmp_ctx);
-                       return ISC_R_NOMEMORY;
+                       result = ISC_R_NOMEMORY;
+                       goto exit;
+               }
+
+               ret = ldb_dn_set_component(dn,
+                                          0,
+                                          "DC",
+                                          zone_name_val);
+               if (ret != LDB_SUCCESS) {
+                       talloc_free(tmp_ctx);
+                       result = ISC_R_NOMEMORY;
+                       goto exit;
+               }
+
+               /*
+                * Check if this is a plausibly valid DN early
+                * (time spent here will be saved during the
+                * search due to an internal cache)
+                */
+               casefold = ldb_dn_get_casefold(dn);
+
+               if (casefold == NULL) {
+                       result = ISC_R_NOTFOUND;
+                       goto exit;
                }
 
                ret = ldb_search(state->samdb, tmp_ctx, &res, dn, LDB_SCOPE_SUBTREE,
@@ -908,9 +1177,10 @@ _PUBLIC_ isc_result_t dlz_allnodes(const char *zone, void *dbdata,
                        break;
                }
        }
-       if (ret != LDB_SUCCESS) {
+       if (ret != LDB_SUCCESS || dn == NULL) {
                talloc_free(tmp_ctx);
-               return ISC_R_NOTFOUND;
+               result = ISC_R_NOTFOUND;
+               goto exit;
        }
 
        for (i=0; i<res->count; i++) {
@@ -941,7 +1211,8 @@ _PUBLIC_ isc_result_t dlz_allnodes(const char *zone, void *dbdata,
                rdn = talloc_strndup(el_ctx, (char *)v->data, v->length);
                if (rdn == NULL) {
                        talloc_free(tmp_ctx);
-                       return ISC_R_NOMEMORY;
+                       result = ISC_R_NOMEMORY;
+                       goto exit;
                }
 
                if (strcmp(rdn, "@") == 0) {
@@ -949,12 +1220,14 @@ _PUBLIC_ isc_result_t dlz_allnodes(const char *zone, void *dbdata,
                } else {
                        name = talloc_asprintf(el_ctx, "%s.%s", rdn, zone);
                }
+               name = b9_format_fqdn(el_ctx, name);
                if (name == NULL) {
                        talloc_free(tmp_ctx);
-                       return ISC_R_NOMEMORY;
+                       result = ISC_R_NOMEMORY;
+                       goto exit;
                }
 
-               werr = dns_common_extract(el, el_ctx, &recs, &num_recs);
+               werr = dns_common_extract(state->samdb, el, el_ctx, &recs, &num_recs);
                if (!W_ERROR_IS_OK(werr)) {
                        state->log(ISC_LOG_ERROR, "samba_dlz: failed to parse dnsRecord for %s, %s",
                                   ldb_dn_get_linearized(dn), win_errstr(werr));
@@ -963,10 +1236,10 @@ _PUBLIC_ isc_result_t dlz_allnodes(const char *zone, void *dbdata,
                }
 
                for (j=0; j < num_recs; j++) {
-                       isc_result_t result;
+                       isc_result_t rc;
 
-                       result = b9_putnamedrr(state, allnodes, name, &recs[j]);
-                       if (result != ISC_R_SUCCESS) {
+                       rc = b9_putnamedrr(state, allnodes, name, &recs[j]);
+                       if (rc != ISC_R_SUCCESS) {
                                continue;
                        }
                }
@@ -975,8 +1248,14 @@ _PUBLIC_ isc_result_t dlz_allnodes(const char *zone, void *dbdata,
        }
 
        talloc_free(tmp_ctx);
-
-       return ISC_R_SUCCESS;
+exit:
+       DNS_COMMON_LOG_OPERATION(
+               isc_result_str(result),
+               &start,
+               zone,
+               NULL,
+               NULL);
+       return result;
 }
 
 
@@ -985,30 +1264,41 @@ _PUBLIC_ isc_result_t dlz_allnodes(const char *zone, void *dbdata,
  */
 _PUBLIC_ isc_result_t dlz_newversion(const char *zone, void *dbdata, void **versionp)
 {
+       struct timeval start = timeval_current();
        struct dlz_bind9_data *state = talloc_get_type_abort(dbdata, struct dlz_bind9_data);
+       isc_result_t result = ISC_R_SUCCESS;
 
        state->log(ISC_LOG_INFO, "samba_dlz: starting transaction on zone %s", zone);
 
        if (state->transaction_token != NULL) {
                state->log(ISC_LOG_INFO, "samba_dlz: transaction already started for zone %s", zone);
-               return ISC_R_FAILURE;
+               result = ISC_R_FAILURE;
+               goto exit;
        }
 
        state->transaction_token = talloc_zero(state, int);
        if (state->transaction_token == NULL) {
-               return ISC_R_NOMEMORY;
+               result = ISC_R_NOMEMORY;
+               goto exit;
        }
 
        if (ldb_transaction_start(state->samdb) != LDB_SUCCESS) {
                state->log(ISC_LOG_INFO, "samba_dlz: failed to start a transaction for zone %s", zone);
                talloc_free(state->transaction_token);
                state->transaction_token = NULL;
-               return ISC_R_FAILURE;
+               result = ISC_R_FAILURE;
+               goto exit;
        }
 
        *versionp = (void *)state->transaction_token;
-
-       return ISC_R_SUCCESS;
+exit:
+       DNS_COMMON_LOG_OPERATION(
+               isc_result_str(result),
+               &start,
+               zone,
+               NULL,
+               NULL);
+       return result;
 }
 
 /*
@@ -1017,23 +1307,27 @@ _PUBLIC_ isc_result_t dlz_newversion(const char *zone, void *dbdata, void **vers
 _PUBLIC_ void dlz_closeversion(const char *zone, isc_boolean_t commit,
                               void *dbdata, void **versionp)
 {
+       struct timeval start = timeval_current();
        struct dlz_bind9_data *state = talloc_get_type_abort(dbdata, struct dlz_bind9_data);
+       const char *data = NULL;
+
+       data = commit ? "commit" : "cancel";
 
        if (state->transaction_token != (int *)*versionp) {
                state->log(ISC_LOG_INFO, "samba_dlz: transaction not started for zone %s", zone);
-               return;
+               goto exit;
        }
 
        if (commit) {
                if (ldb_transaction_commit(state->samdb) != LDB_SUCCESS) {
                        state->log(ISC_LOG_INFO, "samba_dlz: failed to commit a transaction for zone %s", zone);
-                       return;
+                       goto exit;
                }
                state->log(ISC_LOG_INFO, "samba_dlz: committed transaction on zone %s", zone);
        } else {
                if (ldb_transaction_cancel(state->samdb) != LDB_SUCCESS) {
                        state->log(ISC_LOG_INFO, "samba_dlz: failed to cancel a transaction for zone %s", zone);
-                       return;
+                       goto exit;
                }
                state->log(ISC_LOG_INFO, "samba_dlz: cancelling transaction on zone %s", zone);
        }
@@ -1041,6 +1335,14 @@ _PUBLIC_ void dlz_closeversion(const char *zone, isc_boolean_t commit,
        talloc_free(state->transaction_token);
        state->transaction_token = NULL;
        *versionp = NULL;
+
+exit:
+       DNS_COMMON_LOG_OPERATION(
+               isc_result_str(ISC_R_SUCCESS),
+               &start,
+               zone,
+               NULL,
+               data);
 }
 
 
@@ -1053,8 +1355,29 @@ static bool b9_has_soa(struct dlz_bind9_data *state, struct ldb_dn *dn, const ch
        WERROR werr;
        struct dnsp_DnssrvRpcRecord *records = NULL;
        uint16_t num_records = 0, i;
+       struct ldb_val zone_name_val
+               = data_blob_string_const(zone);
+
+       /*
+        * This dance ensures that it is not possible to put
+        * (eg) an extra DC=x, into the DNS name being
+        * queried
+        */
+
+       if (!ldb_dn_add_child_val(dn,
+                                 "DC",
+                                 zone_name_val)) {
+               talloc_free(tmp_ctx);
+               return false;
+       }
 
-       if (!ldb_dn_add_child_fmt(dn, "DC=@,DC=%s", zone)) {
+       /*
+        * The SOA record is alwas stored under DC=@,DC=zonename
+        * This can probably be removed when dns_common_lookup makes a fallback
+        * lookup on @ pseudo record
+        */
+
+       if (!ldb_dn_add_child_fmt(dn,"DC=@")) {
                talloc_free(tmp_ctx);
                return false;
        }
@@ -1116,7 +1439,12 @@ static bool b9_zone_exists(struct dlz_bind9_data *state, const char *name)
 /*
   configure a writeable zone
  */
+#if DLZ_DLOPEN_VERSION < 3
 _PUBLIC_ isc_result_t dlz_configure(dns_view_t *view, void *dbdata)
+#else
+_PUBLIC_ isc_result_t dlz_configure(dns_view_t *view, dns_dlzdb_t *dlzdb,
+                                   void *dbdata)
+#endif
 {
        struct dlz_bind9_data *state = talloc_get_type_abort(dbdata, struct dlz_bind9_data);
        TALLOC_CTX *tmp_ctx;
@@ -1187,7 +1515,11 @@ _PUBLIC_ isc_result_t dlz_configure(dns_view_t *view, void *dbdata)
                                return ISC_R_NOMEMORY;
                        }
 
+#if DLZ_DLOPEN_VERSION < 3
                        result = state->writeable_zone(view, zone);
+#else
+                       result = state->writeable_zone(view, dlzdb, zone);
+#endif
                        if (result != ISC_R_SUCCESS) {
                                state->log(ISC_LOG_ERROR, "samba_dlz: Failed to configure zone '%s'",
                                           zone);
@@ -1209,21 +1541,27 @@ _PUBLIC_ isc_boolean_t dlz_ssumatch(const char *signer, const char *name, const
                                    const char *type, const char *key, uint32_t keydatalen, uint8_t *keydata,
                                    void *dbdata)
 {
+       struct timeval start = timeval_current();
        struct dlz_bind9_data *state = talloc_get_type_abort(dbdata, struct dlz_bind9_data);
        TALLOC_CTX *tmp_ctx;
        DATA_BLOB ap_req;
        struct cli_credentials *server_credentials;
        char *keytab_name;
+       char *keytab_file = NULL;
        int ret;
        int ldb_ret;
        NTSTATUS nt_status;
        struct gensec_security *gensec_ctx;
        struct auth_session_info *session_info;
        struct ldb_dn *dn;
-       isc_result_t result;
+       isc_result_t rc;
        struct ldb_result *res;
        const char * attrs[] = { NULL };
        uint32_t access_mask;
+       struct gensec_settings *settings = NULL;
+       const struct gensec_security_ops **backends = NULL;
+       size_t idx = 0;
+       isc_boolean_t result = ISC_FALSE;
 
        /* Remove cached credentials, if any */
        if (state->session_info) {
@@ -1238,7 +1576,8 @@ _PUBLIC_ isc_boolean_t dlz_ssumatch(const char *signer, const char *name, const
        tmp_ctx = talloc_new(NULL);
        if (tmp_ctx == NULL) {
                state->log(ISC_LOG_ERROR, "samba_dlz: no memory");
-               return ISC_FALSE;
+               result = ISC_FALSE;
+               goto exit;
        }
 
        ap_req = data_blob_const(keydata, keydatalen);
@@ -1246,62 +1585,130 @@ _PUBLIC_ isc_boolean_t dlz_ssumatch(const char *signer, const char *name, const
        if (!server_credentials) {
                state->log(ISC_LOG_ERROR, "samba_dlz: failed to init server credentials");
                talloc_free(tmp_ctx);
-               return ISC_FALSE;
+               result = ISC_FALSE;
+               goto exit;
        }
 
        cli_credentials_set_krb5_context(server_credentials, state->smb_krb5_ctx);
        cli_credentials_set_conf(server_credentials, state->lp);
 
-       keytab_name = talloc_asprintf(tmp_ctx, "file:%s/dns.keytab",
-                                       lpcfg_private_dir(state->lp));
+       keytab_file = talloc_asprintf(tmp_ctx,
+                                     "%s/dns.keytab",
+                                     lpcfg_binddns_dir(state->lp));
+       if (keytab_file == NULL) {
+               state->log(ISC_LOG_ERROR, "samba_dlz: Out of memory!");
+               talloc_free(tmp_ctx);
+               result = ISC_FALSE;
+               goto exit;
+       }
+
+       if (!file_exist(keytab_file)) {
+               keytab_file = talloc_asprintf(tmp_ctx,
+                                             "%s/dns.keytab",
+                                             lpcfg_private_dir(state->lp));
+               if (keytab_file == NULL) {
+                       state->log(ISC_LOG_ERROR, "samba_dlz: Out of memory!");
+                       talloc_free(tmp_ctx);
+                       result = ISC_FALSE;
+                       goto exit;
+               }
+       }
+
+       keytab_name = talloc_asprintf(tmp_ctx, "FILE:%s", keytab_file);
+       if (keytab_name == NULL) {
+               state->log(ISC_LOG_ERROR, "samba_dlz: Out of memory!");
+               talloc_free(tmp_ctx);
+               result = ISC_FALSE;
+               goto exit;
+       }
+
        ret = cli_credentials_set_keytab_name(server_credentials, state->lp, keytab_name,
                                                CRED_SPECIFIED);
        if (ret != 0) {
                state->log(ISC_LOG_ERROR, "samba_dlz: failed to obtain server credentials from %s",
                           keytab_name);
                talloc_free(tmp_ctx);
-               return ISC_FALSE;
+               result = ISC_FALSE;
+               goto exit;
        }
        talloc_free(keytab_name);
 
-       nt_status = gensec_server_start(tmp_ctx,
-                                       lpcfg_gensec_settings(tmp_ctx, state->lp),
+       settings = lpcfg_gensec_settings(tmp_ctx, state->lp);
+       if (settings == NULL) {
+               state->log(ISC_LOG_ERROR, "samba_dlz: lpcfg_gensec_settings failed");
+               talloc_free(tmp_ctx);
+               result = ISC_FALSE;
+               goto exit;
+       }
+       backends = talloc_zero_array(settings,
+                                    const struct gensec_security_ops *, 3);
+       if (backends == NULL) {
+               state->log(ISC_LOG_ERROR, "samba_dlz: talloc_zero_array gensec_security_ops failed");
+               talloc_free(tmp_ctx);
+               result = ISC_FALSE;
+               goto exit;
+       }
+       settings->backends = backends;
+
+       gensec_init();
+
+       backends[idx++] = gensec_security_by_oid(NULL, GENSEC_OID_KERBEROS5);
+       backends[idx++] = gensec_security_by_oid(NULL, GENSEC_OID_SPNEGO);
+
+       nt_status = gensec_server_start(tmp_ctx, settings,
                                        state->auth_context, &gensec_ctx);
        if (!NT_STATUS_IS_OK(nt_status)) {
                state->log(ISC_LOG_ERROR, "samba_dlz: failed to start gensec server");
                talloc_free(tmp_ctx);
-               return ISC_FALSE;
+               result = ISC_FALSE;
+               goto exit;
        }
 
        gensec_set_credentials(gensec_ctx, server_credentials);
 
-       nt_status = gensec_start_mech_by_name(gensec_ctx, "spnego");
+       nt_status = gensec_start_mech_by_oid(gensec_ctx, GENSEC_OID_SPNEGO);
        if (!NT_STATUS_IS_OK(nt_status)) {
                state->log(ISC_LOG_ERROR, "samba_dlz: failed to start spnego");
                talloc_free(tmp_ctx);
-               return ISC_FALSE;
-       }
-
-       nt_status = gensec_update_ev(gensec_ctx, tmp_ctx, state->ev_ctx, ap_req, &ap_req);
+               result = ISC_FALSE;
+               goto exit;
+       }
+
+       /*
+        * We only allow SPNEGO/KRB5 and make sure the backend
+        * to is RPC/IPC free.
+        *
+        * See gensec_gssapi_update_internal() as
+        * GENSEC_SERVER.
+        *
+        * It allows gensec_update() not to block.
+        *
+        * If that changes in future we need to use
+        * gensec_update_send/recv here!
+        */
+       nt_status = gensec_update(gensec_ctx, tmp_ctx, ap_req, &ap_req);
        if (!NT_STATUS_IS_OK(nt_status)) {
                state->log(ISC_LOG_ERROR, "samba_dlz: spnego update failed");
                talloc_free(tmp_ctx);
-               return ISC_FALSE;
+               result = ISC_FALSE;
+               goto exit;
        }
 
        nt_status = gensec_session_info(gensec_ctx, tmp_ctx, &session_info);
        if (!NT_STATUS_IS_OK(nt_status)) {
                state->log(ISC_LOG_ERROR, "samba_dlz: failed to create session info");
                talloc_free(tmp_ctx);
-               return ISC_FALSE;
+               result = ISC_FALSE;
+               goto exit;
        }
 
        /* Get the DN from name */
-       result = b9_find_name_dn(state, name, tmp_ctx, &dn);
-       if (result != ISC_R_SUCCESS) {
+       rc = b9_find_name_dn(state, name, tmp_ctx, &dn);
+       if (rc != ISC_R_SUCCESS) {
                state->log(ISC_LOG_ERROR, "samba_dlz: failed to find name %s", name);
                talloc_free(tmp_ctx);
-               return ISC_FALSE;
+               result = ISC_FALSE;
+               goto exit;
        }
 
        /* make sure the dn exists, or find parent dn in case new object is being added */
@@ -1316,7 +1723,8 @@ _PUBLIC_ isc_boolean_t dlz_ssumatch(const char *signer, const char *name, const
                talloc_free(res);
        } else {
                talloc_free(tmp_ctx);
-               return ISC_FALSE;
+               result = ISC_FALSE;
+               goto exit;
        }
 
        /* Do ACL check */
@@ -1328,7 +1736,8 @@ _PUBLIC_ isc_boolean_t dlz_ssumatch(const char *signer, const char *name, const
                        "samba_dlz: disallowing update of signer=%s name=%s type=%s error=%s",
                        signer, name, type, ldb_strerror(ldb_ret));
                talloc_free(tmp_ctx);
-               return ISC_FALSE;
+               result = ISC_FALSE;
+               goto exit;
        }
 
        /* Cache session_info, so it can be used in the actual add/delete operation */
@@ -1336,7 +1745,8 @@ _PUBLIC_ isc_boolean_t dlz_ssumatch(const char *signer, const char *name, const
        if (state->update_name == NULL) {
                state->log(ISC_LOG_ERROR, "samba_dlz: memory allocation error");
                talloc_free(tmp_ctx);
-               return ISC_FALSE;
+               result = ISC_FALSE;
+               goto exit;
        }
        state->session_info = talloc_steal(state, session_info);
 
@@ -1344,48 +1754,17 @@ _PUBLIC_ isc_boolean_t dlz_ssumatch(const char *signer, const char *name, const
                   signer, name, tcpaddr, type, key);
 
        talloc_free(tmp_ctx);
-       return ISC_TRUE;
-}
-
-
-/*
-  add a new record
- */
-static isc_result_t b9_add_record(struct dlz_bind9_data *state, const char *name,
-                                 struct ldb_dn *dn,
-                                 struct dnsp_DnssrvRpcRecord *rec)
-{
-       WERROR werr;
-
-       werr = dns_common_replace(state->samdb, rec, dn,
-                                 true,/* needs_add */
-                                 state->soa_serial,
-                                 rec, 1);
-       if (!W_ERROR_IS_OK(werr)) {
-               state->log(ISC_LOG_ERROR, "samba_dlz: failed to add %s - %s",
-                          ldb_dn_get_linearized(dn), win_errstr(werr));
-               return ISC_R_FAILURE;
-       }
-
-       return ISC_R_SUCCESS;
-}
-
-/*
-  see if two DNS names are the same
- */
-static bool dns_name_equal(const char *name1, const char *name2)
-{
-       size_t len1 = strlen(name1);
-       size_t len2 = strlen(name2);
-       if (name1[len1-1] == '.') len1--;
-       if (name2[len2-1] == '.') len2--;
-       if (len1 != len2) {
-               return false;
-       }
-       return strncasecmp_m(name1, name2, len1) == 0;
+       result = ISC_TRUE;
+exit:
+       DNS_COMMON_LOG_OPERATION(
+               isc_result_str(result),
+               &start,
+               NULL,
+               name,
+               NULL);
+       return result;
 }
 
-
 /*
   see if two dns records match
  */
@@ -1409,10 +1788,20 @@ static bool b9_record_match(struct dlz_bind9_data *state,
        switch (rec1->wType) {
        case DNS_TYPE_A:
                return strcmp(rec1->data.ipv4, rec2->data.ipv4) == 0;
-       case DNS_TYPE_AAAA:
-               inet_pton(AF_INET6, rec1->data.ipv6, &rec1_in_addr6);
-               inet_pton(AF_INET6, rec2->data.ipv6, &rec2_in_addr6);
+       case DNS_TYPE_AAAA: {
+               int ret;
+
+               ret = inet_pton(AF_INET6, rec1->data.ipv6, &rec1_in_addr6);
+               if (ret != 1) {
+                       return false;
+               }
+               ret = inet_pton(AF_INET6, rec2->data.ipv6, &rec2_in_addr6);
+               if (ret != 1) {
+                       return false;
+               }
+
                return memcmp(&rec1_in_addr6, &rec2_in_addr6, sizeof(rec1_in_addr6)) == 0;
+       }
        case DNS_TYPE_CNAME:
                return dns_name_equal(rec1->data.cname, rec2->data.cname);
        case DNS_TYPE_TXT:
@@ -1475,7 +1864,10 @@ static bool b9_set_session_info(struct dlz_bind9_data *state, const char *name)
                return true;
        }
 
-       ret = ldb_set_opaque(state->samdb, "sessionInfo", state->session_info);
+       ret = ldb_set_opaque(
+               state->samdb,
+               DSDB_SESSION_INFO,
+               state->session_info);
        if (ret != LDB_SUCCESS) {
                state->log(ISC_LOG_ERROR, "samba_dlz: unable to set session info");
                return false;
@@ -1489,7 +1881,10 @@ static bool b9_set_session_info(struct dlz_bind9_data *state, const char *name)
  */
 static void b9_reset_session_info(struct dlz_bind9_data *state)
 {
-       ldb_set_opaque(state->samdb, "sessionInfo", system_session(state->lp));
+       ldb_set_opaque(
+               state->samdb,
+               DSDB_SESSION_INFO,
+               system_session(state->lp));
 }
 
 /*
@@ -1497,88 +1892,75 @@ static void b9_reset_session_info(struct dlz_bind9_data *state)
  */
 _PUBLIC_ isc_result_t dlz_addrdataset(const char *name, const char *rdatastr, void *dbdata, void *version)
 {
+       struct timeval start = timeval_current();
        struct dlz_bind9_data *state = talloc_get_type_abort(dbdata, struct dlz_bind9_data);
        struct dnsp_DnssrvRpcRecord *rec;
        struct ldb_dn *dn;
-       isc_result_t result;
-       struct ldb_result *res;
-       const char *attrs[] = { "dnsRecord", NULL };
-       int ret, i;
-       struct ldb_message_element *el;
+       isc_result_t result = ISC_R_SUCCESS;
+       bool tombstoned = false;
+       bool needs_add = false;
        struct dnsp_DnssrvRpcRecord *recs = NULL;
        uint16_t num_recs = 0;
+       uint16_t first = 0;
+       uint16_t i;
        NTTIME t;
        WERROR werr;
 
        if (state->transaction_token != (void*)version) {
                state->log(ISC_LOG_INFO, "samba_dlz: bad transaction version");
-               return ISC_R_FAILURE;
+               result = ISC_R_FAILURE;
+               goto exit;
        }
 
        rec = talloc_zero(state, struct dnsp_DnssrvRpcRecord);
        if (rec == NULL) {
-               return ISC_R_NOMEMORY;
+               result = ISC_R_NOMEMORY;
+               goto exit;
        }
 
-       unix_to_nt_time(&t, time(NULL));
-       t /= 10*1000*1000; /* convert to seconds (NT time is in 100ns units) */
-       t /= 3600;         /* convert to hours */
-
        rec->rank        = DNS_RANK_ZONE;
-       rec->dwTimeStamp = (uint32_t)t;
 
        if (!b9_parse(state, rdatastr, rec)) {
                state->log(ISC_LOG_INFO, "samba_dlz: failed to parse rdataset '%s'", rdatastr);
                talloc_free(rec);
-               return ISC_R_FAILURE;
+               result = ISC_R_FAILURE;
+               goto exit;
        }
 
        /* find the DN of the record */
        result = b9_find_name_dn(state, name, rec, &dn);
        if (result != ISC_R_SUCCESS) {
                talloc_free(rec);
-               return result;
+               goto exit;
        }
 
        /* get any existing records */
-       ret = ldb_search(state->samdb, rec, &res, dn, LDB_SCOPE_BASE, attrs, "objectClass=dnsNode");
-       if (ret == LDB_ERR_NO_SUCH_OBJECT) {
-               if (!b9_set_session_info(state, name)) {
-                       talloc_free(rec);
-                       return ISC_R_FAILURE;
-               }
-               result = b9_add_record(state, name, dn, rec);
-               b9_reset_session_info(state);
-               talloc_free(rec);
-               if (result == ISC_R_SUCCESS) {
-                       state->log(ISC_LOG_INFO, "samba_dlz: added %s %s", name, rdatastr);
-               }
-               return result;
-       }
-
-       el = ldb_msg_find_element(res->msgs[0], "dnsRecord");
-       if (el == NULL) {
-               ret = ldb_msg_add_empty(res->msgs[0], "dnsRecord", LDB_FLAG_MOD_ADD, &el);
-               if (ret != LDB_SUCCESS) {
-                       state->log(ISC_LOG_ERROR, "samba_dlz: failed to add dnsRecord for %s",
-                                  ldb_dn_get_linearized(dn));
-                       talloc_free(rec);
-                       return ISC_R_FAILURE;
-               }
+       werr = dns_common_lookup(state->samdb, rec, dn,
+                                &recs, &num_recs, &tombstoned);
+       if (W_ERROR_EQUAL(werr, WERR_DNS_ERROR_NAME_DOES_NOT_EXIST)) {
+               needs_add = true;
+               werr = WERR_OK;
        }
-
-       werr = dns_common_extract(el, rec, &recs, &num_recs);
        if (!W_ERROR_IS_OK(werr)) {
                state->log(ISC_LOG_ERROR, "samba_dlz: failed to parse dnsRecord for %s, %s",
                           ldb_dn_get_linearized(dn), win_errstr(werr));
                talloc_free(rec);
-               return ISC_R_FAILURE;
+               result = ISC_R_FAILURE;
+               goto exit;
+       }
+
+       if (tombstoned) {
+               /*
+                * we need to keep the existing tombstone record
+                * and ignore it
+                */
+               first = num_recs;
        }
 
        /* there are existing records. We need to see if this will
         * replace a record or add to it
         */
-       for (i=0; i < num_recs; i++) {
+       for (i=first; i < num_recs; i++) {
                if (b9_record_match(state, rec, &recs[i])) {
                        break;
                }
@@ -1587,7 +1969,8 @@ _PUBLIC_ isc_result_t dlz_addrdataset(const char *name, const char *rdatastr, vo
                state->log(ISC_LOG_ERROR, "samba_dlz: failed to already %u dnsRecord values for %s",
                           i, ldb_dn_get_linearized(dn));
                talloc_free(rec);
-               return ISC_R_FAILURE;
+               result = ISC_R_FAILURE;
+               goto exit;
        }
 
        if (i == num_recs) {
@@ -1597,35 +1980,55 @@ _PUBLIC_ isc_result_t dlz_addrdataset(const char *name, const char *rdatastr, vo
                                      num_recs + 1);
                if (recs == NULL) {
                        talloc_free(rec);
-                       return ISC_R_NOMEMORY;
+                       result = ISC_R_NOMEMORY;
+                       goto exit;
                }
                num_recs++;
+
+               if (dns_name_is_static(recs, num_recs)) {
+                       rec->dwTimeStamp = 0;
+               } else {
+                       unix_to_nt_time(&t, time(NULL));
+                       t /= 10 * 1000 * 1000; /* convert to seconds */
+                       t /= 3600;           /* convert to hours */
+                       rec->dwTimeStamp = (uint32_t)t;
+               }
        }
 
        recs[i] = *rec;
 
        if (!b9_set_session_info(state, name)) {
                talloc_free(rec);
-               return ISC_R_FAILURE;
+               result = ISC_R_FAILURE;
+               goto exit;
        }
 
        /* modify the record */
        werr = dns_common_replace(state->samdb, rec, dn,
-                                 false,/* needs_add */
+                                 needs_add,
                                  state->soa_serial,
                                  recs, num_recs);
        b9_reset_session_info(state);
        if (!W_ERROR_IS_OK(werr)) {
-               state->log(ISC_LOG_ERROR, "samba_dlz: failed to modify %s - %s",
+               state->log(ISC_LOG_ERROR, "samba_dlz: failed to %s %s - %s",
+                          needs_add ? "add" : "modify",
                           ldb_dn_get_linearized(dn), win_errstr(werr));
                talloc_free(rec);
-               return ISC_R_FAILURE;
+               result = ISC_R_FAILURE;
+               goto exit;
        }
 
        state->log(ISC_LOG_INFO, "samba_dlz: added rdataset %s '%s'", name, rdatastr);
 
        talloc_free(rec);
-       return ISC_R_SUCCESS;
+exit:
+       DNS_COMMON_LOG_OPERATION(
+               isc_result_str(result),
+               &start,
+               NULL,
+               name,
+               rdatastr);
+       return result;
 }
 
 /*
@@ -1633,64 +2036,49 @@ _PUBLIC_ isc_result_t dlz_addrdataset(const char *name, const char *rdatastr, vo
  */
 _PUBLIC_ isc_result_t dlz_subrdataset(const char *name, const char *rdatastr, void *dbdata, void *version)
 {
+       struct timeval start = timeval_current();
        struct dlz_bind9_data *state = talloc_get_type_abort(dbdata, struct dlz_bind9_data);
        struct dnsp_DnssrvRpcRecord *rec;
        struct ldb_dn *dn;
-       isc_result_t result;
-       struct ldb_result *res;
-       const char *attrs[] = { "dnsRecord", NULL };
-       int ret, i;
-       struct ldb_message_element *el;
+       isc_result_t result = ISC_R_SUCCESS;
        struct dnsp_DnssrvRpcRecord *recs = NULL;
        uint16_t num_recs = 0;
+       uint16_t i;
        WERROR werr;
 
        if (state->transaction_token != (void*)version) {
                state->log(ISC_LOG_ERROR, "samba_dlz: bad transaction version");
-               return ISC_R_FAILURE;
+               result = ISC_R_FAILURE;
+               goto exit;
        }
 
        rec = talloc_zero(state, struct dnsp_DnssrvRpcRecord);
        if (rec == NULL) {
-               return ISC_R_NOMEMORY;
+               result = ISC_R_NOMEMORY;
+               goto exit;
        }
 
        if (!b9_parse(state, rdatastr, rec)) {
                state->log(ISC_LOG_ERROR, "samba_dlz: failed to parse rdataset '%s'", rdatastr);
                talloc_free(rec);
-               return ISC_R_FAILURE;
+               result = ISC_R_FAILURE;
+               goto exit;
        }
 
        /* find the DN of the record */
        result = b9_find_name_dn(state, name, rec, &dn);
        if (result != ISC_R_SUCCESS) {
                talloc_free(rec);
-               return result;
+               goto exit;
        }
 
        /* get the existing records */
-       ret = ldb_search(state->samdb, rec, &res, dn, LDB_SCOPE_BASE, attrs, "objectClass=dnsNode");
-       if (ret == LDB_ERR_NO_SUCH_OBJECT) {
-               talloc_free(rec);
-               return ISC_R_NOTFOUND;
-       }
-
-       /* there are existing records. We need to see if any match
-        */
-       el = ldb_msg_find_element(res->msgs[0], "dnsRecord");
-       if (el == NULL || el->num_values == 0) {
-               state->log(ISC_LOG_ERROR, "samba_dlz: no dnsRecord attribute for %s",
-                          ldb_dn_get_linearized(dn));
-               talloc_free(rec);
-               return ISC_R_FAILURE;
-       }
-
-       werr = dns_common_extract(el, rec, &recs, &num_recs);
+       werr = dns_common_lookup(state->samdb, rec, dn,
+                                &recs, &num_recs, NULL);
        if (!W_ERROR_IS_OK(werr)) {
-               state->log(ISC_LOG_ERROR, "samba_dlz: failed to parse dnsRecord for %s, %s",
-                          ldb_dn_get_linearized(dn), win_errstr(werr));
                talloc_free(rec);
-               return ISC_R_FAILURE;
+               result = ISC_R_NOTFOUND;
+               goto exit;
        }
 
        for (i=0; i < num_recs; i++) {
@@ -1703,12 +2091,14 @@ _PUBLIC_ isc_result_t dlz_subrdataset(const char *name, const char *rdatastr, vo
        }
        if (i == num_recs) {
                talloc_free(rec);
-               return ISC_R_NOTFOUND;
+               result = ISC_R_NOTFOUND;
+               goto exit;
        }
 
        if (!b9_set_session_info(state, name)) {
                talloc_free(rec);
-               return ISC_R_FAILURE;
+               result = ISC_R_FAILURE;
+               goto exit;
        }
 
        /* modify the record */
@@ -1721,13 +2111,21 @@ _PUBLIC_ isc_result_t dlz_subrdataset(const char *name, const char *rdatastr, vo
                state->log(ISC_LOG_ERROR, "samba_dlz: failed to modify %s - %s",
                           ldb_dn_get_linearized(dn), win_errstr(werr));
                talloc_free(rec);
-               return ISC_R_FAILURE;
+               result = ISC_R_FAILURE;
+               goto exit;
        }
 
        state->log(ISC_LOG_INFO, "samba_dlz: subtracted rdataset %s '%s'", name, rdatastr);
 
        talloc_free(rec);
-       return ISC_R_SUCCESS;
+exit:
+       DNS_COMMON_LOG_OPERATION(
+               isc_result_str(result),
+               &start,
+               NULL,
+               name,
+               rdatastr);
+       return result;
 }
 
 
@@ -1736,14 +2134,11 @@ _PUBLIC_ isc_result_t dlz_subrdataset(const char *name, const char *rdatastr, vo
  */
 _PUBLIC_ isc_result_t dlz_delrdataset(const char *name, const char *type, void *dbdata, void *version)
 {
+       struct timeval start = timeval_current();
        struct dlz_bind9_data *state = talloc_get_type_abort(dbdata, struct dlz_bind9_data);
        TALLOC_CTX *tmp_ctx;
        struct ldb_dn *dn;
-       isc_result_t result;
-       struct ldb_result *res;
-       const char *attrs[] = { "dnsRecord", NULL };
-       int ret;
-       struct ldb_message_element *el;
+       isc_result_t result = ISC_R_SUCCESS;
        enum dns_record_type dns_type;
        bool found = false;
        struct dnsp_DnssrvRpcRecord *recs = NULL;
@@ -1753,12 +2148,14 @@ _PUBLIC_ isc_result_t dlz_delrdataset(const char *name, const char *type, void *
 
        if (state->transaction_token != (void*)version) {
                state->log(ISC_LOG_ERROR, "samba_dlz: bad transaction version");
-               return ISC_R_FAILURE;
+               result = ISC_R_FAILURE;
+               goto exit;
        }
 
        if (!b9_dns_type(type, &dns_type)) {
                state->log(ISC_LOG_ERROR, "samba_dlz: bad dns type %s in delete", type);
-               return ISC_R_FAILURE;
+               result = ISC_R_FAILURE;
+               goto exit;
        }
 
        tmp_ctx = talloc_new(state);
@@ -1767,30 +2164,16 @@ _PUBLIC_ isc_result_t dlz_delrdataset(const char *name, const char *type, void *
        result = b9_find_name_dn(state, name, tmp_ctx, &dn);
        if (result != ISC_R_SUCCESS) {
                talloc_free(tmp_ctx);
-               return result;
+               goto exit;
        }
 
        /* get the existing records */
-       ret = ldb_search(state->samdb, tmp_ctx, &res, dn, LDB_SCOPE_BASE, attrs, "objectClass=dnsNode");
-       if (ret == LDB_ERR_NO_SUCH_OBJECT) {
-               talloc_free(tmp_ctx);
-               return ISC_R_NOTFOUND;
-       }
-
-       /* there are existing records. We need to see if any match the type
-        */
-       el = ldb_msg_find_element(res->msgs[0], "dnsRecord");
-       if (el == NULL || el->num_values == 0) {
-               talloc_free(tmp_ctx);
-               return ISC_R_NOTFOUND;
-       }
-
-       werr = dns_common_extract(el, tmp_ctx, &recs, &num_recs);
+       werr = dns_common_lookup(state->samdb, tmp_ctx, dn,
+                                &recs, &num_recs, NULL);
        if (!W_ERROR_IS_OK(werr)) {
-               state->log(ISC_LOG_ERROR, "samba_dlz: failed to parse dnsRecord for %s, %s",
-                          ldb_dn_get_linearized(dn), win_errstr(werr));
                talloc_free(tmp_ctx);
-               return ISC_R_FAILURE;
+               result = ISC_R_NOTFOUND;
+               goto exit;
        }
 
        for (ri=0; ri < num_recs; ri++) {
@@ -1806,12 +2189,14 @@ _PUBLIC_ isc_result_t dlz_delrdataset(const char *name, const char *type, void *
 
        if (!found) {
                talloc_free(tmp_ctx);
-               return ISC_R_FAILURE;
+               result = ISC_R_FAILURE;
+               goto exit;
        }
 
        if (!b9_set_session_info(state, name)) {
                talloc_free(tmp_ctx);
-               return ISC_R_FAILURE;
+               result = ISC_R_FAILURE;
+               goto exit;
        }
 
        /* modify the record */
@@ -1824,11 +2209,19 @@ _PUBLIC_ isc_result_t dlz_delrdataset(const char *name, const char *type, void *
                state->log(ISC_LOG_ERROR, "samba_dlz: failed to modify %s - %s",
                           ldb_dn_get_linearized(dn), win_errstr(werr));
                talloc_free(tmp_ctx);
-               return ISC_R_FAILURE;
+               result = ISC_R_FAILURE;
+               goto exit;
        }
 
        state->log(ISC_LOG_INFO, "samba_dlz: deleted rdataset %s of type %s", name, type);
 
        talloc_free(tmp_ctx);
-       return ISC_R_SUCCESS;
+exit:
+       DNS_COMMON_LOG_OPERATION(
+               isc_result_str(result),
+               &start,
+               NULL,
+               name,
+               type);
+       return result;
 }