s4 dns_server Bind9: Log opertion durations
[samba.git] / source4 / dns_server / dnsserver_common.c
index bbbfe920f4eb2b305798d5d9b720e43ed29cad98..ab8079a72968604fc231250bbc0eeabd27cabc12 100644 (file)
@@ -145,13 +145,14 @@ WERROR dns_common_lookup(struct ldb_context *samdb,
                         uint16_t *num_records,
                         bool *tombstoned)
 {
+       const struct timeval start = timeval_current();
        static const char * const attrs[] = {
                "dnsRecord",
                "dNSTombstoned",
                NULL
        };
        int ret;
-       WERROR werr;
+       WERROR werr = WERR_OK;
        struct ldb_message *msg = NULL;
        struct ldb_message_element *el;
 
@@ -169,12 +170,14 @@ WERROR dns_common_lookup(struct ldb_context *samdb,
                        "(&(objectClass=dnsNode)(!(dNSTombstoned=TRUE)))");
        }
        if (ret == LDB_ERR_NO_SUCH_OBJECT) {
-               return WERR_DNS_ERROR_NAME_DOES_NOT_EXIST;
+               werr = WERR_DNS_ERROR_NAME_DOES_NOT_EXIST;
+               goto exit;
        }
        if (ret != LDB_SUCCESS) {
                /* TODO: we need to check if there's a glue record we need to
                 * create a referral to */
-               return DNS_ERR(NAME_ERROR);
+               werr = DNS_ERR(NAME_ERROR);
+               goto exit;
        }
 
        if (tombstoned != NULL) {
@@ -198,7 +201,8 @@ WERROR dns_common_lookup(struct ldb_context *samdb,
                                            struct dnsp_DnssrvRpcRecord,
                                            1);
                        if (recs == NULL) {
-                               return WERR_NOT_ENOUGH_MEMORY;
+                               werr = WERR_NOT_ENOUGH_MEMORY;
+                               goto exit;
                        }
                        recs[0] = (struct dnsp_DnssrvRpcRecord) {
                                .wType = DNS_TYPE_TOMBSTONE,
@@ -214,24 +218,34 @@ WERROR dns_common_lookup(struct ldb_context *samdb,
                        *tombstoned = true;
                        *records = recs;
                        *num_records = 1;
-                       return WERR_OK;
+                       werr = WERR_OK;
+                       goto exit;
                } else {
                        /*
                         * Because we are not looking for a tombstone
                         * in this codepath, we just pretend it does
                         * not exist at all.
                         */
-                       return WERR_DNS_ERROR_NAME_DOES_NOT_EXIST;
+                       werr = WERR_DNS_ERROR_NAME_DOES_NOT_EXIST;
+                       goto exit;
                }
        }
 
        werr = dns_common_extract(samdb, el, mem_ctx, records, num_records);
        TALLOC_FREE(msg);
        if (!W_ERROR_IS_OK(werr)) {
-               return werr;
+               goto exit;
        }
 
-       return WERR_OK;
+       werr = WERR_OK;
+exit:
+       DNS_COMMON_LOG_OPERATION(
+               win_errstr(werr),
+               &start,
+               NULL,
+               dn == NULL ? NULL : ldb_dn_get_linearized(dn),
+               NULL);
+       return werr;
 }
 
 /*
@@ -482,7 +496,7 @@ static int dns_wildcard_lookup(struct ldb_context *samdb,
                                      samdb,
                                      frame,
                                      parent,
-                                     LDB_SCOPE_ONELEVEL,
+                                     LDB_SCOPE_SUBTREE,
                                      query,
                                      attrs,
                                      NULL,
@@ -533,8 +547,9 @@ WERROR dns_common_wildcard_lookup(struct ldb_context *samdb,
                                  struct dnsp_DnssrvRpcRecord **records,
                                  uint16_t *num_records)
 {
+       const struct timeval start = timeval_current();
        int ret;
-       WERROR werr;
+       WERROR werr = WERR_OK;
        struct ldb_message *msg = NULL;
        struct ldb_message_element *el = NULL;
        const struct ldb_val *name = NULL;
@@ -545,16 +560,18 @@ WERROR dns_common_wildcard_lookup(struct ldb_context *samdb,
        name = ldb_dn_get_rdn_val(dn);
        if (name == NULL) {
                return DNS_ERR(NAME_ERROR);
+               goto exit;
        }
 
        /* Don't look for a wildcard for @ */
        if (name->length == 1 && name->data[0] == '@') {
-               return dns_common_lookup(samdb,
+               werr = dns_common_lookup(samdb,
                                         mem_ctx,
                                         dn,
                                         records,
                                         num_records,
                                         NULL);
+               goto exit;
        }
 
        werr =  dns_name_check(
@@ -562,7 +579,7 @@ WERROR dns_common_wildcard_lookup(struct ldb_context *samdb,
                        strlen((const char*)name->data),
                        (const char*) name->data);
        if (!W_ERROR_IS_OK(werr)) {
-               return werr;
+               goto exit;
        }
 
        /*
@@ -576,29 +593,41 @@ WERROR dns_common_wildcard_lookup(struct ldb_context *samdb,
                                 num_records,
                                 NULL);
        if (!W_ERROR_EQUAL(werr, WERR_DNS_ERROR_NAME_DOES_NOT_EXIST)) {
-               return werr;
+               goto exit;
        }
 
        ret = dns_wildcard_lookup(samdb, mem_ctx, dn, &msg);
        if (ret == LDB_ERR_OPERATIONS_ERROR) {
-               return DNS_ERR(SERVER_FAILURE);
+               werr = DNS_ERR(SERVER_FAILURE);
+               goto exit;
        }
        if (ret != LDB_SUCCESS) {
-               return DNS_ERR(NAME_ERROR);
+               werr = DNS_ERR(NAME_ERROR);
+               goto exit;
        }
 
        el = ldb_msg_find_element(msg, "dnsRecord");
        if (el == NULL) {
-               return WERR_DNS_ERROR_NAME_DOES_NOT_EXIST;
+               werr = WERR_DNS_ERROR_NAME_DOES_NOT_EXIST;
+               goto exit;
        }
 
        werr = dns_common_extract(samdb, el, mem_ctx, records, num_records);
        TALLOC_FREE(msg);
        if (!W_ERROR_IS_OK(werr)) {
                return werr;
+               goto exit;
        }
 
-       return WERR_OK;
+       werr = WERR_OK;
+exit:
+       DNS_COMMON_LOG_OPERATION(
+               win_errstr(werr),
+               &start,
+               NULL,
+               dn == NULL ? NULL : ldb_dn_get_linearized(dn),
+               NULL);
+       return werr;
 }
 
 static int rec_cmp(const struct dnsp_DnssrvRpcRecord *r1,
@@ -742,6 +771,94 @@ bool dns_name_is_static(struct dnsp_DnssrvRpcRecord *records,
        return false;
 }
 
+/*
+ * Helper function to copy a dnsp_ip4_array struct to an IP4_ARRAY struct.
+ * The new structure and it's data are allocated on the supplied talloc context
+ */
+static struct IP4_ARRAY *copy_ip4_array(TALLOC_CTX *ctx,
+                                       const char *name,
+                                       struct dnsp_ip4_array array)
+{
+
+       struct IP4_ARRAY *ip4_array = NULL;
+       unsigned int i;
+
+       ip4_array = talloc_zero(ctx, struct IP4_ARRAY);
+       if (ip4_array == NULL) {
+               DBG_ERR("Out of memory copying property [%s]\n", name);
+               return NULL;
+       }
+
+       ip4_array->AddrCount = array.addrCount;
+       if (ip4_array->AddrCount == 0) {
+               return ip4_array;
+       }
+
+       ip4_array->AddrArray =
+           talloc_array(ip4_array, uint32_t, ip4_array->AddrCount);
+       if (ip4_array->AddrArray == NULL) {
+               TALLOC_FREE(ip4_array);
+               DBG_ERR("Out of memory copying property [%s] values\n", name);
+               return NULL;
+       }
+
+       for (i = 0; i < ip4_array->AddrCount; i++) {
+               ip4_array->AddrArray[i] = array.addr[i];
+       }
+
+       return ip4_array;
+}
+
+bool dns_zoneinfo_load_zone_property(struct dnsserver_zoneinfo *zoneinfo,
+                                    struct dnsp_DnsProperty *prop)
+{
+       switch (prop->id) {
+       case DSPROPERTY_ZONE_TYPE:
+               zoneinfo->dwZoneType = prop->data.zone_type;
+               break;
+       case DSPROPERTY_ZONE_ALLOW_UPDATE:
+               zoneinfo->fAllowUpdate = prop->data.allow_update_flag;
+               break;
+       case DSPROPERTY_ZONE_NOREFRESH_INTERVAL:
+               zoneinfo->dwNoRefreshInterval = prop->data.norefresh_hours;
+               break;
+       case DSPROPERTY_ZONE_REFRESH_INTERVAL:
+               zoneinfo->dwRefreshInterval = prop->data.refresh_hours;
+               break;
+       case DSPROPERTY_ZONE_AGING_STATE:
+               zoneinfo->fAging = prop->data.aging_enabled;
+               break;
+       case DSPROPERTY_ZONE_SCAVENGING_SERVERS:
+               zoneinfo->aipScavengeServers = copy_ip4_array(
+                   zoneinfo, "ZONE_SCAVENGING_SERVERS", prop->data.servers);
+               if (zoneinfo->aipScavengeServers == NULL) {
+                       return false;
+               }
+               break;
+       case DSPROPERTY_ZONE_AGING_ENABLED_TIME:
+               zoneinfo->dwAvailForScavengeTime =
+                   prop->data.next_scavenging_cycle_hours;
+               break;
+       case DSPROPERTY_ZONE_MASTER_SERVERS:
+               zoneinfo->aipLocalMasters = copy_ip4_array(
+                   zoneinfo, "ZONE_MASTER_SERVERS", prop->data.master_servers);
+               if (zoneinfo->aipLocalMasters == NULL) {
+                       return false;
+               }
+               break;
+       case DSPROPERTY_ZONE_EMPTY:
+       case DSPROPERTY_ZONE_SECURE_TIME:
+       case DSPROPERTY_ZONE_DELETED_FROM_HOSTNAME:
+       case DSPROPERTY_ZONE_AUTO_NS_SERVERS:
+       case DSPROPERTY_ZONE_DCPROMO_CONVERT:
+       case DSPROPERTY_ZONE_SCAVENGING_SERVERS_DA:
+       case DSPROPERTY_ZONE_MASTER_SERVERS_DA:
+       case DSPROPERTY_ZONE_NS_SERVERS_DA:
+       case DSPROPERTY_ZONE_NODE_DBFLAGS:
+               break;
+       }
+       return true;
+}
 WERROR dns_get_zone_properties(struct ldb_context *samdb,
                               TALLOC_CTX *mem_ctx,
                               struct ldb_dn *zone_dn,
@@ -774,6 +891,7 @@ WERROR dns_get_zone_properties(struct ldb_context *samdb,
        }
 
        for (i = 0; i < element->num_values; i++) {
+               bool valid_property;
                prop = talloc_zero(mem_ctx, struct dnsp_DnsProperty);
                if (prop == NULL) {
                        return WERR_NOT_ENOUGH_MEMORY;
@@ -787,42 +905,10 @@ WERROR dns_get_zone_properties(struct ldb_context *samdb,
                        return DNS_ERR(SERVER_FAILURE);
                }
 
-               switch (prop->id) {
-               case DSPROPERTY_ZONE_AGING_STATE:
-                       zoneinfo->fAging = prop->data.aging_enabled;
-                       break;
-               case DSPROPERTY_ZONE_NOREFRESH_INTERVAL:
-                       zoneinfo->dwNoRefreshInterval =
-                           prop->data.norefresh_hours;
-                       break;
-               case DSPROPERTY_ZONE_REFRESH_INTERVAL:
-                       zoneinfo->dwRefreshInterval = prop->data.refresh_hours;
-                       break;
-               case DSPROPERTY_ZONE_ALLOW_UPDATE:
-                       zoneinfo->fAllowUpdate = prop->data.allow_update_flag;
-                       break;
-               case DSPROPERTY_ZONE_AGING_ENABLED_TIME:
-                       zoneinfo->dwAvailForScavengeTime =
-                           prop->data.next_scavenging_cycle_hours;
-                       break;
-               case DSPROPERTY_ZONE_SCAVENGING_SERVERS:
-                       zoneinfo->aipScavengeServers->AddrCount =
-                           prop->data.servers.addrCount;
-                       zoneinfo->aipScavengeServers->AddrArray =
-                           prop->data.servers.addr;
-                       break;
-               case DSPROPERTY_ZONE_EMPTY:
-               case DSPROPERTY_ZONE_TYPE:
-               case DSPROPERTY_ZONE_SECURE_TIME:
-               case DSPROPERTY_ZONE_DELETED_FROM_HOSTNAME:
-               case DSPROPERTY_ZONE_MASTER_SERVERS:
-               case DSPROPERTY_ZONE_AUTO_NS_SERVERS:
-               case DSPROPERTY_ZONE_DCPROMO_CONVERT:
-               case DSPROPERTY_ZONE_SCAVENGING_SERVERS_DA:
-               case DSPROPERTY_ZONE_MASTER_SERVERS_DA:
-               case DSPROPERTY_ZONE_NS_SERVERS_DA:
-               case DSPROPERTY_ZONE_NODE_DBFLAGS:
-                       break;
+               valid_property =
+                   dns_zoneinfo_load_zone_property(zoneinfo, prop);
+               if (!valid_property) {
+                       return DNS_ERR(SERVER_FAILURE);
                }
        }
 
@@ -837,6 +923,7 @@ WERROR dns_common_replace(struct ldb_context *samdb,
                          struct dnsp_DnssrvRpcRecord *records,
                          uint16_t rec_count)
 {
+       const struct timeval start = timeval_current();
        struct ldb_message_element *el;
        uint16_t i;
        int ret;
@@ -855,14 +942,17 @@ WERROR dns_common_replace(struct ldb_context *samdb,
 
        zone_dn = ldb_dn_copy(mem_ctx, dn);
        if (zone_dn == NULL) {
-               return WERR_NOT_ENOUGH_MEMORY;
+               werr = WERR_NOT_ENOUGH_MEMORY;
+               goto exit;
        }
        if (!ldb_dn_remove_child_components(zone_dn, 1)) {
-               return DNS_ERR(SERVER_FAILURE);
+               werr = DNS_ERR(SERVER_FAILURE);
+               goto exit;
        }
        zoneinfo = talloc(mem_ctx, struct dnsserver_zoneinfo);
        if (zoneinfo == NULL) {
-               return WERR_NOT_ENOUGH_MEMORY;
+               werr = WERR_NOT_ENOUGH_MEMORY;
+               goto exit;
        }
        werr = dns_get_zone_properties(samdb, mem_ctx, zone_dn, zoneinfo);
        if (W_ERROR_EQUAL(DNS_ERR(NOTZONE), werr)) {
@@ -872,17 +962,18 @@ WERROR dns_common_replace(struct ldb_context *samdb,
                 */
                zoneinfo->fAging = 0;
        } else if (!W_ERROR_IS_OK(werr)) {
-               return werr;
+               goto exit;
        }
 
        werr = check_name_list(mem_ctx, rec_count, records);
        if (!W_ERROR_IS_OK(werr)) {
-               return werr;
+               goto exit;
        }
 
        ret = ldb_msg_add_empty(msg, "dnsRecord", LDB_FLAG_MOD_REPLACE, &el);
        if (ret != LDB_SUCCESS) {
-               return DNS_ERR(SERVER_FAILURE);
+               werr = DNS_ERR(SERVER_FAILURE);
+               goto exit;
        }
 
        /*
@@ -891,7 +982,10 @@ WERROR dns_common_replace(struct ldb_context *samdb,
         */
        el->values = talloc_zero_array(el, struct ldb_val, MAX(1, rec_count));
        if (rec_count > 0) {
-               W_ERROR_HAVE_NO_MEMORY(el->values);
+               if (el->values == NULL) {
+                       werr = WERR_NOT_ENOUGH_MEMORY;
+                       goto exit;
+               }
 
                /*
                 * We store a sorted list with the high wType values first
@@ -927,27 +1021,32 @@ WERROR dns_common_replace(struct ldb_context *samdb,
                                (ndr_push_flags_fn_t)ndr_push_dnsp_DnssrvRpcRecord);
                if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
                        DEBUG(0, ("Failed to push dnsp_DnssrvRpcRecord\n"));
-                       return DNS_ERR(SERVER_FAILURE);
+                       werr = DNS_ERR(SERVER_FAILURE);
+                       goto exit;
                }
                el->num_values++;
        }
 
        if (needs_add) {
                if (el->num_values == 0) {
-                       return WERR_OK;
+                       werr = WERR_OK;
+                       goto exit;
                }
 
                ret = ldb_msg_add_string(msg, "objectClass", "dnsNode");
                if (ret != LDB_SUCCESS) {
-                       return DNS_ERR(SERVER_FAILURE);
+                       werr = DNS_ERR(SERVER_FAILURE);
+                       goto exit;
                }
 
                ret = ldb_add(samdb, msg);
                if (ret != LDB_SUCCESS) {
-                       return DNS_ERR(SERVER_FAILURE);
+                       werr = DNS_ERR(SERVER_FAILURE);
+                       goto exit;
                }
 
                return WERR_OK;
+               goto exit;
        }
 
        if (el->num_values == 0) {
@@ -961,7 +1060,8 @@ WERROR dns_common_replace(struct ldb_context *samdb,
                         * This is already a tombstoned object.
                         * Just leave it instead of updating the time stamp.
                         */
-                       return WERR_OK;
+                       werr = WERR_OK;
+                       goto exit;
                }
 
                tv = timeval_current();
@@ -975,7 +1075,8 @@ WERROR dns_common_replace(struct ldb_context *samdb,
                                (ndr_push_flags_fn_t)ndr_push_dnsp_DnssrvRpcRecord);
                if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
                        DEBUG(0, ("Failed to push dnsp_DnssrvRpcRecord\n"));
-                       return DNS_ERR(SERVER_FAILURE);
+                       werr = DNS_ERR(SERVER_FAILURE);
+                       goto exit;
                }
                el->num_values++;
 
@@ -986,23 +1087,34 @@ WERROR dns_common_replace(struct ldb_context *samdb,
                ret = ldb_msg_add_empty(msg, "dNSTombstoned",
                                        LDB_FLAG_MOD_REPLACE, NULL);
                if (ret != LDB_SUCCESS) {
-                       return DNS_ERR(SERVER_FAILURE);
+                       werr = DNS_ERR(SERVER_FAILURE);
+                       goto exit;
                }
 
                ret = ldb_msg_add_fmt(msg, "dNSTombstoned", "%s",
                                      become_tombstoned ? "TRUE" : "FALSE");
                if (ret != LDB_SUCCESS) {
-                       return DNS_ERR(SERVER_FAILURE);
+                       werr = DNS_ERR(SERVER_FAILURE);
+                       goto exit;
                }
        }
 
        ret = ldb_modify(samdb, msg);
        if (ret != LDB_SUCCESS) {
                NTSTATUS nt = dsdb_ldb_err_to_ntstatus(ret);
-               return ntstatus_to_werror(nt);
+               werr = ntstatus_to_werror(nt);
+               goto exit;
        }
 
-       return WERR_OK;
+       werr = WERR_OK;
+exit:
+       DNS_COMMON_LOG_OPERATION(
+               win_errstr(werr),
+               &start,
+               NULL,
+               dn == NULL ? NULL : ldb_dn_get_linearized(dn),
+               NULL);
+       return werr;
 }
 
 bool dns_name_match(const char *zone, const char *name, size_t *host_part_len)
@@ -1056,7 +1168,10 @@ WERROR dns_common_name2dn(struct ldb_context *samdb,
        struct ldb_dn *dn;
        const struct dns_server_zone *z;
        size_t host_part_len = 0;
+       struct ldb_val host_part;
        WERROR werr;
+       bool ok;
+       const char *casefold = NULL;
 
        if (name == NULL) {
                return DNS_ERR(FORMAT_ERROR);
@@ -1065,7 +1180,13 @@ WERROR dns_common_name2dn(struct ldb_context *samdb,
        if (strcmp(name, "") == 0) {
                base = ldb_get_default_basedn(samdb);
                dn = ldb_dn_copy(mem_ctx, base);
-               ldb_dn_add_child_fmt(dn, "DC=@,DC=RootDNSServers,CN=MicrosoftDNS,CN=System");
+               ok = ldb_dn_add_child_fmt(dn,
+                                         "DC=@,DC=RootDNSServers,CN=MicrosoftDNS,CN=System");
+               if (ok == false) {
+                       TALLOC_FREE(dn);
+                       return WERR_NOT_ENOUGH_MEMORY;
+               }
+
                *_dn = dn;
                return WERR_OK;
        }
@@ -1091,13 +1212,50 @@ WERROR dns_common_name2dn(struct ldb_context *samdb,
 
        if (host_part_len == 0) {
                dn = ldb_dn_copy(mem_ctx, z->dn);
-               ldb_dn_add_child_fmt(dn, "DC=@");
+               ok = ldb_dn_add_child_fmt(dn, "DC=@");
+               if (! ok) {
+                       TALLOC_FREE(dn);
+                       return WERR_NOT_ENOUGH_MEMORY;
+               }
                *_dn = dn;
                return WERR_OK;
        }
 
        dn = ldb_dn_copy(mem_ctx, z->dn);
-       ldb_dn_add_child_fmt(dn, "DC=%*.*s", (int)host_part_len, (int)host_part_len, name);
+       if (dn == NULL) {
+               TALLOC_FREE(dn);
+               return WERR_NOT_ENOUGH_MEMORY;
+       }
+
+       host_part = data_blob_const(name, host_part_len);
+
+       ok = ldb_dn_add_child_val(dn, "DC", host_part);
+
+       if (ok == false) {
+               TALLOC_FREE(dn);
+               return WERR_NOT_ENOUGH_MEMORY;
+       }
+
+       /*
+        * Check the new DN here for validity, so as to catch errors
+        * early
+        */
+       ok = ldb_dn_validate(dn);
+       if (ok == false) {
+               TALLOC_FREE(dn);
+               return DNS_ERR(NAME_ERROR);
+       }
+
+       /*
+        * The value from this check is saved in the DN, and doing
+        * this here allows an easy return here.
+        */
+       casefold = ldb_dn_get_casefold(dn);
+       if (casefold == NULL) {
+               TALLOC_FREE(dn);
+               return DNS_ERR(NAME_ERROR);
+       }
+
        *_dn = dn;
        return WERR_OK;
 }
@@ -1128,12 +1286,14 @@ NTSTATUS dns_common_zones(struct ldb_context *samdb,
                          struct ldb_dn *base_dn,
                          struct dns_server_zone **zones_ret)
 {
+       const struct timeval start = timeval_current();
        int ret;
        static const char * const attrs[] = { "name", NULL};
        struct ldb_result *res;
        int i;
        struct dns_server_zone *new_list = NULL;
        TALLOC_CTX *frame = talloc_stackframe();
+       NTSTATUS result = NT_STATUS_OK;
 
        if (base_dn) {
                /* This search will work against windows */
@@ -1150,7 +1310,8 @@ NTSTATUS dns_common_zones(struct ldb_context *samdb,
        }
        if (ret != LDB_SUCCESS) {
                TALLOC_FREE(frame);
-               return NT_STATUS_INTERNAL_DB_CORRUPTION;
+               result = NT_STATUS_INTERNAL_DB_CORRUPTION;
+               goto exit;
        }
 
        TYPESAFE_QSORT(res->msgs, res->count, dns_common_sort_zones);
@@ -1161,7 +1322,8 @@ NTSTATUS dns_common_zones(struct ldb_context *samdb,
                z = talloc_zero(mem_ctx, struct dns_server_zone);
                if (z == NULL) {
                        TALLOC_FREE(frame);
-                       return NT_STATUS_NO_MEMORY;
+                       result = NT_STATUS_NO_MEMORY;
+                       goto exit;
                }
 
                z->name = ldb_msg_find_attr_as_string(res->msgs[i], "name", NULL);
@@ -1186,7 +1348,15 @@ NTSTATUS dns_common_zones(struct ldb_context *samdb,
 
        *zones_ret = new_list;
        TALLOC_FREE(frame);
-       return NT_STATUS_OK;
+       result = NT_STATUS_OK;
+exit:
+       DNS_COMMON_LOG_OPERATION(
+               nt_errstr(result),
+               &start,
+               NULL,
+               base_dn == NULL ? NULL : ldb_dn_get_linearized(base_dn),
+               NULL);
+       return result;
 }
 
 /*