s4 dns_server Bind9: Log opertion durations
[samba.git] / source4 / dns_server / dlz_bind9.c
index 677578a97e901fb24607271ded2b8c0c04975299..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 "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,
@@ -210,7 +276,7 @@ static bool b9_format(struct dlz_bind9_data *state,
        }
 
        default:
-               state->log(ISC_LOG_ERROR, "samba b9_putrr: unhandled record type %u",
+               state->log(ISC_LOG_ERROR, "samba_dlz b9_format: unhandled record type %u",
                           rec->wType);
                return false;
        }
@@ -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)
 
 /*
@@ -379,14 +449,14 @@ static bool b9_parse(struct dlz_bind9_data *state,
                break;
 
        default:
-               state->log(ISC_LOG_ERROR, "samba b9_parse: unhandled record type %u",
+               state->log(ISC_LOG_ERROR, "samba_dlz b9_parse: unhandled record type %u",
                           rec->wType);
                return false;
        }
 
        /* we should be at the end of the buffer now */
        if (strtok_r(NULL, "\t ", &saveptr) != NULL) {
-               state->log(ISC_LOG_ERROR, "samba b9_parse: unexpected data at end of string for '%s'",
+               state->log(ISC_LOG_ERROR, "samba_dlz b9_parse: unexpected data at end of string for '%s'",
                           rdatastr);
                return false;
        }
@@ -466,7 +536,7 @@ static isc_result_t b9_putnamedrr(struct dlz_bind9_data *state,
    parse options
  */
 static isc_result_t parse_options(struct dlz_bind9_data *state,
-                                 unsigned int argc, char *argv[],
+                                 unsigned int argc, const char **argv,
                                  struct b9_options *options)
 {
        int opt;
@@ -477,7 +547,7 @@ static isc_result_t parse_options(struct dlz_bind9_data *state,
                { NULL }
        };
 
-       pc = poptGetContext("dlz_bind9", argc, (const char **)argv, long_options,
+       pc = poptGetContext("dlz_bind9", argc, argv, long_options,
                        POPT_CONTEXT_KEEP_FIRST);
        while ((opt = poptGetNextOpt(pc)) != -1) {
                switch (opt) {
@@ -571,7 +641,7 @@ static int dlz_state_debug_unregister(struct dlz_bind9_data *state)
   called to initialise the driver
  */
 _PUBLIC_ isc_result_t dlz_create(const char *dlzname,
-                                unsigned int argc, char *argv[],
+                                unsigned int argc, const char **argv,
                                 void **dbdata, ...)
 {
        struct dlz_bind9_data *state;
@@ -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;
 }
 
 
@@ -808,56 +982,82 @@ static isc_result_t dlz_lookup_types(struct dlz_bind9_data *state,
                                     const char **types)
 {
        TALLOC_CTX *tmp_ctx = talloc_new(state);
-       const char *attrs[] = { "dnsRecord", NULL };
-       int ret = LDB_SUCCESS, i;
-       struct ldb_result *res;
-       struct ldb_message_element *el;
        struct ldb_dn *dn;
+       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;
                }
 
-               ret = ldb_search(state->samdb, tmp_ctx, &res, dn, LDB_SCOPE_BASE,
-                                attrs, "objectClass=dnsNode");
-               if (ret == LDB_SUCCESS) {
+               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;
                }
        }
-       if (ret != LDB_SUCCESS || res->count == 0) {
-               talloc_free(tmp_ctx);
-               return ISC_R_NOTFOUND;
-       }
-
-       el = ldb_msg_find_element(res->msgs[0], "dnsRecord");
-       if (el == NULL || el->num_values == 0) {
+       if (!W_ERROR_IS_OK(werr)) {
                talloc_free(tmp_ctx);
                return ISC_R_NOTFOUND;
        }
 
-       for (i=0; i<el->num_values; i++) {
-               struct dnsp_DnssrvRpcRecord rec;
-               enum ndr_err_code ndr_err;
+       for (i=0; i < num_records; i++) {
                isc_result_t result;
 
-               ndr_err = ndr_pull_struct_blob(&el->values[i], tmp_ctx, &rec,
-                                              (ndr_pull_flags_fn_t)ndr_pull_dnsp_DnssrvRpcRecord);
-               if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
-                       state->log(ISC_LOG_ERROR, "samba_dlz: failed to parse dnsRecord for %s",
-                                  ldb_dn_get_linearized(dn));
-                       talloc_free(tmp_ctx);
-                       return ISC_R_FAILURE;
-               }
-
-               result = b9_putrr(state, lookup, &rec, types);
+               result = b9_putrr(state, lookup, &records[i], types);
                if (result != ISC_R_SUCCESS) {
                        talloc_free(tmp_ctx);
                        return result;
@@ -871,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
@@ -882,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;
 }
 
 
@@ -892,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);
 }
 
 /*
@@ -901,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,
@@ -926,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++) {
@@ -936,6 +1188,9 @@ _PUBLIC_ isc_result_t dlz_allnodes(const char *zone, void *dbdata,
                TALLOC_CTX *el_ctx = talloc_new(tmp_ctx);
                const char *rdn, *name;
                const struct ldb_val *v;
+               WERROR werr;
+               struct dnsp_DnssrvRpcRecord *recs = NULL;
+               uint16_t num_recs = 0;
 
                el = ldb_msg_find_element(res->msgs[i], "dnsRecord");
                if (el == NULL || el->num_values == 0) {
@@ -956,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) {
@@ -964,34 +1220,42 @@ _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;
                }
 
-               for (j=0; j<el->num_values; j++) {
-                       struct dnsp_DnssrvRpcRecord rec;
-                       enum ndr_err_code ndr_err;
-                       isc_result_t result;
+               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));
+                       talloc_free(el_ctx);
+                       continue;
+               }
 
-                       ndr_err = ndr_pull_struct_blob(&el->values[j], el_ctx, &rec,
-                                                      (ndr_pull_flags_fn_t)ndr_pull_dnsp_DnssrvRpcRecord);
-                       if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
-                               state->log(ISC_LOG_ERROR, "samba_dlz: failed to parse dnsRecord for %s",
-                                          ldb_dn_get_linearized(dn));
-                               continue;
-                       }
+               for (j=0; j < num_recs; j++) {
+                       isc_result_t rc;
 
-                       result = b9_putnamedrr(state, allnodes, name, &rec);
-                       if (result != ISC_R_SUCCESS) {
+                       rc = b9_putnamedrr(state, allnodes, name, &recs[j]);
+                       if (rc != ISC_R_SUCCESS) {
                                continue;
                        }
                }
+
+               talloc_free(el_ctx);
        }
 
        talloc_free(tmp_ctx);
-
-       return ISC_R_SUCCESS;
+exit:
+       DNS_COMMON_LOG_OPERATION(
+               isc_result_str(result),
+               &start,
+               zone,
+               NULL,
+               NULL);
+       return result;
 }
 
 
@@ -1000,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;
 }
 
 /*
@@ -1032,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);
        }
@@ -1056,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);
 }
 
 
@@ -1064,39 +1351,46 @@ _PUBLIC_ void dlz_closeversion(const char *zone, isc_boolean_t commit,
  */
 static bool b9_has_soa(struct dlz_bind9_data *state, struct ldb_dn *dn, const char *zone)
 {
-       const char *attrs[] = { "dnsRecord", NULL };
-       struct ldb_result *res;
-       struct ldb_message_element *el;
        TALLOC_CTX *tmp_ctx = talloc_new(state);
-       int ret, i;
+       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_fmt(dn, "DC=@,DC=%s", zone)) {
+       if (!ldb_dn_add_child_val(dn,
+                                 "DC",
+                                 zone_name_val)) {
                talloc_free(tmp_ctx);
                return false;
        }
 
-       ret = ldb_search(state->samdb, tmp_ctx, &res, dn, LDB_SCOPE_BASE,
-                        attrs, "objectClass=dnsNode");
-       if (ret != LDB_SUCCESS) {
+       /*
+        * 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;
        }
 
-       el = ldb_msg_find_element(res->msgs[0], "dnsRecord");
-       if (el == NULL) {
+       werr = dns_common_lookup(state->samdb, tmp_ctx, dn,
+                                &records, &num_records, NULL);
+       if (!W_ERROR_IS_OK(werr)) {
                talloc_free(tmp_ctx);
                return false;
        }
-       for (i=0; i<el->num_values; i++) {
-               struct dnsp_DnssrvRpcRecord rec;
-               enum ndr_err_code ndr_err;
 
-               ndr_err = ndr_pull_struct_blob(&el->values[i], tmp_ctx, &rec,
-                                              (ndr_pull_flags_fn_t)ndr_pull_dnsp_DnssrvRpcRecord);
-               if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
-                       continue;
-               }
-               if (rec.wType == DNS_TYPE_SOA) {
+       for (i=0; i < num_records; i++) {
+               if (records[i].wType == DNS_TYPE_SOA) {
                        talloc_free(tmp_ctx);
                        return true;
                }
@@ -1145,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;
@@ -1216,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);
@@ -1238,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) {
@@ -1267,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);
@@ -1275,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 */
@@ -1345,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 */
@@ -1357,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 */
@@ -1365,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);
 
@@ -1373,65 +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)
-{
-       struct ldb_message *msg;
-       enum ndr_err_code ndr_err;
-       struct ldb_val v;
-       int ret;
-
-       msg = ldb_msg_new(rec);
-       if (msg == NULL) {
-               return ISC_R_NOMEMORY;
-       }
-       msg->dn = dn;
-       ret = ldb_msg_add_string(msg, "objectClass", "dnsNode");
-       if (ret != LDB_SUCCESS) {
-               return ISC_R_FAILURE;
-       }
-
-       ndr_err = ndr_push_struct_blob(&v, rec, rec, (ndr_push_flags_fn_t)ndr_push_dnsp_DnssrvRpcRecord);
-       if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
-               return ISC_R_FAILURE;
-       }
-       ret = ldb_msg_add_value(msg, "dnsRecord", &v, NULL);
-       if (ret != LDB_SUCCESS) {
-               return ISC_R_FAILURE;
-       }
-
-       ret = ldb_add(state->samdb, msg);
-       if (ret != LDB_SUCCESS) {
-               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
  */
@@ -1440,6 +1773,8 @@ static bool b9_record_match(struct dlz_bind9_data *state,
 {
        bool status;
        int i;
+       struct in6_addr rec1_in_addr6;
+       struct in6_addr rec2_in_addr6;
 
        if (rec1->wType != rec2->wType) {
                return false;
@@ -1453,8 +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:
-               return strcmp(rec1->data.ipv6, rec2->data.ipv6) == 0;
+       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:
@@ -1492,7 +1839,7 @@ static bool b9_record_match(struct dlz_bind9_data *state,
                        rec1->data.soa.expire == rec2->data.soa.expire &&
                        rec1->data.soa.minimum == rec2->data.soa.minimum;
        default:
-               state->log(ISC_LOG_ERROR, "samba b9_putrr: unhandled record type %u",
+               state->log(ISC_LOG_ERROR, "samba_dlz b9_record_match: unhandled record type %u",
                           rec1->wType);
                break;
        }
@@ -1517,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;
@@ -1531,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));
 }
 
 /*
@@ -1539,134 +1892,143 @@ 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;
-       enum ndr_err_code ndr_err;
+       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->dwSerial    = state->soa_serial;
-       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);
+       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;
+       }
+       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);
-               if (result == ISC_R_SUCCESS) {
-                       state->log(ISC_LOG_INFO, "samba_dlz: added %s %s", name, rdatastr);
-               }
-               return result;
+               result = ISC_R_FAILURE;
+               goto exit;
        }
 
-       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;
-               }
+       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<el->num_values; i++) {
-               struct dnsp_DnssrvRpcRecord rec2;
-
-               ndr_err = ndr_pull_struct_blob(&el->values[i], rec, &rec2,
-                                              (ndr_pull_flags_fn_t)ndr_pull_dnsp_DnssrvRpcRecord);
-               if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
-                       state->log(ISC_LOG_ERROR, "samba_dlz: failed to parse dnsRecord for %s",
-                                  ldb_dn_get_linearized(dn));
-                       talloc_free(rec);
-                       return ISC_R_FAILURE;
-               }
-
-               if (b9_record_match(state, rec, &rec2)) {
+       for (i=first; i < num_recs; i++) {
+               if (b9_record_match(state, rec, &recs[i])) {
                        break;
                }
        }
-       if (i == el->num_values) {
+       if (i == UINT16_MAX) {
+               state->log(ISC_LOG_ERROR, "samba_dlz: failed to already %u dnsRecord values for %s",
+                          i, ldb_dn_get_linearized(dn));
+               talloc_free(rec);
+               result = ISC_R_FAILURE;
+               goto exit;
+       }
+
+       if (i == num_recs) {
                /* adding a new value */
-               el->values = talloc_realloc(el, el->values, struct ldb_val, el->num_values+1);
-               if (el->values == NULL) {
+               recs = talloc_realloc(rec, recs,
+                                     struct dnsp_DnssrvRpcRecord,
+                                     num_recs + 1);
+               if (recs == NULL) {
                        talloc_free(rec);
-                       return ISC_R_NOMEMORY;
+                       result = ISC_R_NOMEMORY;
+                       goto exit;
                }
-               el->num_values++;
-       }
+               num_recs++;
 
-       ndr_err = ndr_push_struct_blob(&el->values[i], rec, rec,
-                                      (ndr_push_flags_fn_t)ndr_push_dnsp_DnssrvRpcRecord);
-       if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
-               state->log(ISC_LOG_ERROR, "samba_dlz: failed to push dnsRecord for %s",
-                          ldb_dn_get_linearized(dn));
-               talloc_free(rec);
-               return ISC_R_FAILURE;
+               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 */
-       el->flags = LDB_FLAG_MOD_REPLACE;
-       ret = ldb_modify(state->samdb, res->msgs[0]);
+       werr = dns_common_replace(state->samdb, rec, dn,
+                                 needs_add,
+                                 state->soa_serial,
+                                 recs, num_recs);
        b9_reset_session_info(state);
-       if (ret != LDB_SUCCESS) {
-               state->log(ISC_LOG_ERROR, "samba_dlz: failed to modify %s - %s",
-                          ldb_dn_get_linearized(dn), ldb_errstring(state->samdb));
+       if (!W_ERROR_IS_OK(werr)) {
+               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;
 }
 
 /*
@@ -1674,106 +2036,96 @@ _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;
-       enum ndr_err_code ndr_err;
+       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) {
+       werr = dns_common_lookup(state->samdb, rec, dn,
+                                &recs, &num_recs, NULL);
+       if (!W_ERROR_IS_OK(werr)) {
                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;
+               result = ISC_R_NOTFOUND;
+               goto exit;
        }
 
-       for (i=0; i<el->num_values; i++) {
-               struct dnsp_DnssrvRpcRecord rec2;
-
-               ndr_err = ndr_pull_struct_blob(&el->values[i], rec, &rec2,
-                                              (ndr_pull_flags_fn_t)ndr_pull_dnsp_DnssrvRpcRecord);
-               if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
-                       state->log(ISC_LOG_ERROR, "samba_dlz: failed to parse dnsRecord for %s",
-                                  ldb_dn_get_linearized(dn));
-                       talloc_free(rec);
-                       return ISC_R_FAILURE;
-               }
-
-               if (b9_record_match(state, rec, &rec2)) {
+       for (i=0; i < num_recs; i++) {
+               if (b9_record_match(state, rec, &recs[i])) {
+                       recs[i] = (struct dnsp_DnssrvRpcRecord) {
+                               .wType = DNS_TYPE_TOMBSTONE,
+                       };
                        break;
                }
        }
-       if (i == el->num_values) {
+       if (i == num_recs) {
                talloc_free(rec);
-               return ISC_R_NOTFOUND;
-       }
-
-       if (i < el->num_values-1) {
-               memmove(&el->values[i], &el->values[i+1], sizeof(el->values[0])*((el->num_values-1)-i));
+               result = ISC_R_NOTFOUND;
+               goto exit;
        }
-       el->num_values--;
 
        if (!b9_set_session_info(state, name)) {
                talloc_free(rec);
-               return ISC_R_FAILURE;
-       }
-
-       if (el->num_values == 0) {
-               el->flags = LDB_FLAG_MOD_DELETE;
-       } else {
-               el->flags = LDB_FLAG_MOD_REPLACE;
+               result = ISC_R_FAILURE;
+               goto exit;
        }
-       ret = ldb_modify(state->samdb, res->msgs[0]);
 
+       /* modify the record */
+       werr = dns_common_replace(state->samdb, rec, dn,
+                                 false,/* needs_add */
+                                 state->soa_serial,
+                                 recs, num_recs);
        b9_reset_session_info(state);
-       if (ret != LDB_SUCCESS) {
+       if (!W_ERROR_IS_OK(werr)) {
                state->log(ISC_LOG_ERROR, "samba_dlz: failed to modify %s - %s",
-                          ldb_dn_get_linearized(dn), ldb_errstring(state->samdb));
+                          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;
 }
 
 
@@ -1782,26 +2134,28 @@ _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, i;
-       struct ldb_message_element *el;
-       enum ndr_err_code ndr_err;
+       isc_result_t result = ISC_R_SUCCESS;
        enum dns_record_type dns_type;
        bool found = false;
+       struct dnsp_DnssrvRpcRecord *recs = NULL;
+       uint16_t num_recs = 0;
+       uint16_t ri = 0;
+       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;
        }
 
        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);
@@ -1810,74 +2164,64 @@ _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) {
+       werr = dns_common_lookup(state->samdb, tmp_ctx, dn,
+                                &recs, &num_recs, NULL);
+       if (!W_ERROR_IS_OK(werr)) {
                talloc_free(tmp_ctx);
-               return ISC_R_NOTFOUND;
+               result = ISC_R_NOTFOUND;
+               goto exit;
        }
 
-       for (i=0; i<el->num_values; i++) {
-               struct dnsp_DnssrvRpcRecord rec2;
-
-               ndr_err = ndr_pull_struct_blob(&el->values[i], tmp_ctx, &rec2,
-                                              (ndr_pull_flags_fn_t)ndr_pull_dnsp_DnssrvRpcRecord);
-               if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
-                       state->log(ISC_LOG_ERROR, "samba_dlz: failed to parse dnsRecord for %s",
-                                  ldb_dn_get_linearized(dn));
-                       talloc_free(tmp_ctx);
-                       return ISC_R_FAILURE;
+       for (ri=0; ri < num_recs; ri++) {
+               if (dns_type != recs[ri].wType) {
+                       continue;
                }
 
-               if (dns_type == rec2.wType) {
-                       if (i < el->num_values-1) {
-                               memmove(&el->values[i], &el->values[i+1],
-                                       sizeof(el->values[0])*((el->num_values-1)-i));
-                       }
-                       el->num_values--;
-                       i--;
-                       found = true;
-               }
+               found = true;
+               recs[ri] = (struct dnsp_DnssrvRpcRecord) {
+                       .wType = DNS_TYPE_TOMBSTONE,
+               };
        }
 
        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;
-       }
-
-       if (el->num_values == 0) {
-               el->flags = LDB_FLAG_MOD_DELETE;
-       } else {
-               el->flags = LDB_FLAG_MOD_REPLACE;
+               result = ISC_R_FAILURE;
+               goto exit;
        }
-       ret = ldb_modify(state->samdb, res->msgs[0]);
 
+       /* modify the record */
+       werr = dns_common_replace(state->samdb, tmp_ctx, dn,
+                                 false,/* needs_add */
+                                 state->soa_serial,
+                                 recs, num_recs);
        b9_reset_session_info(state);
-       if (ret != LDB_SUCCESS) {
-               state->log(ISC_LOG_ERROR, "samba_dlz: failed to delete type %s in %s - %s",
-                          type, ldb_dn_get_linearized(dn), ldb_errstring(state->samdb));
+       if (!W_ERROR_IS_OK(werr)) {
+               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;
 }