summaryrefslogtreecommitdiffstats
path: root/source4/rpc_server/dnsserver/dnsdb.c
diff options
context:
space:
mode:
Diffstat (limited to 'source4/rpc_server/dnsserver/dnsdb.c')
-rw-r--r--source4/rpc_server/dnsserver/dnsdb.c1272
1 files changed, 1272 insertions, 0 deletions
diff --git a/source4/rpc_server/dnsserver/dnsdb.c b/source4/rpc_server/dnsserver/dnsdb.c
new file mode 100644
index 0000000..bde54a0
--- /dev/null
+++ b/source4/rpc_server/dnsserver/dnsdb.c
@@ -0,0 +1,1272 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ DNS Server
+
+ Copyright (C) Amitay Isaacs 2011
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "dnsserver.h"
+#include "lib/util/dlinklist.h"
+#include "librpc/gen_ndr/ndr_dnsp.h"
+#include "librpc/gen_ndr/ndr_security.h"
+#include "librpc/gen_ndr/ndr_misc.h"
+#include "dsdb/samdb/samdb.h"
+#include "libcli/security/security.h"
+#include "dsdb/common/util.h"
+
+#undef strcasecmp
+
+/* There are only 2 fixed partitions for DNS */
+struct dnsserver_partition *dnsserver_db_enumerate_partitions(TALLOC_CTX *mem_ctx,
+ struct dnsserver_serverinfo *serverinfo,
+ struct ldb_context *samdb)
+{
+ struct dnsserver_partition *partitions, *p;
+
+ partitions = NULL;
+
+ /* Domain partition */
+ p = talloc_zero(mem_ctx, struct dnsserver_partition);
+ if (p == NULL) {
+ goto failed;
+ }
+
+ p->partition_dn = ldb_dn_new(p, samdb, serverinfo->pszDomainDirectoryPartition);
+ if (p->partition_dn == NULL) {
+ goto failed;
+ }
+
+ p->pszDpFqdn = samdb_dn_to_dns_domain(p, p->partition_dn);
+ p->dwDpFlags = DNS_DP_AUTOCREATED | DNS_DP_DOMAIN_DEFAULT | DNS_DP_ENLISTED;
+ p->is_forest = false;
+
+ DLIST_ADD_END(partitions, p);
+
+ /* Forest Partition */
+ p = talloc_zero(mem_ctx, struct dnsserver_partition);
+ if (p == NULL) {
+ goto failed;
+ }
+
+ p->partition_dn = ldb_dn_new(p, samdb, serverinfo->pszForestDirectoryPartition);
+ if (p->partition_dn == NULL) {
+ goto failed;
+ }
+
+ p->pszDpFqdn = samdb_dn_to_dns_domain(p, p->partition_dn);
+ p->dwDpFlags = DNS_DP_AUTOCREATED | DNS_DP_FOREST_DEFAULT | DNS_DP_ENLISTED;
+ p->is_forest = true;
+
+ DLIST_ADD_END(partitions, p);
+
+ return partitions;
+
+failed:
+ return NULL;
+
+}
+
+
+/* Search for all dnsZone records */
+struct dnsserver_zone *dnsserver_db_enumerate_zones(TALLOC_CTX *mem_ctx,
+ struct ldb_context *samdb,
+ struct dnsserver_partition *p)
+{
+ TALLOC_CTX *tmp_ctx;
+ const char * const attrs[] = {"name", "dNSProperty", NULL};
+ struct ldb_dn *dn;
+ struct ldb_result *res;
+ struct dnsserver_zone *zones, *z;
+ int i, j, ret;
+
+ tmp_ctx = talloc_new(mem_ctx);
+ if (tmp_ctx == NULL) {
+ return NULL;
+ }
+
+ dn = ldb_dn_copy(tmp_ctx, p->partition_dn);
+ if (dn == NULL) {
+ goto failed;
+ }
+ if (!ldb_dn_add_child_fmt(dn, "CN=MicrosoftDNS")) {
+ goto failed;
+ }
+
+ ret = ldb_search(samdb, tmp_ctx, &res, dn, LDB_SCOPE_SUBTREE,
+ attrs, "(objectClass=dnsZone)");
+ if (ret != LDB_SUCCESS) {
+ DEBUG(0, ("dnsserver: Failed to find DNS Zones in %s\n",
+ ldb_dn_get_linearized(dn)));
+ goto failed;
+ }
+
+ zones = NULL;
+ for(i=0; i<res->count; i++) {
+ char *name;
+ struct ldb_message_element *element = NULL;
+ struct dnsp_DnsProperty *props = NULL;
+ enum ndr_err_code err;
+ z = talloc_zero(mem_ctx, struct dnsserver_zone);
+ if (z == NULL) {
+ goto failed;
+ }
+
+ z->partition = p;
+ name = talloc_strdup(z,
+ ldb_msg_find_attr_as_string(res->msgs[i],
+ "name", NULL));
+ if (strcmp(name, "..TrustAnchors") == 0) {
+ talloc_free(z);
+ continue;
+ }
+ if (strcmp(name, "RootDNSServers") == 0) {
+ talloc_free(name);
+ z->name = talloc_strdup(z, ".");
+ } else {
+ z->name = name;
+ }
+ z->zone_dn = talloc_steal(z, res->msgs[i]->dn);
+
+ DLIST_ADD_END(zones, z);
+ DEBUG(2, ("dnsserver: Found DNS zone %s\n", z->name));
+
+ element = ldb_msg_find_element(res->msgs[i], "dNSProperty");
+ if(element != NULL){
+ props = talloc_zero_array(tmp_ctx,
+ struct dnsp_DnsProperty,
+ element->num_values);
+ for (j = 0; j < element->num_values; j++ ) {
+ err = ndr_pull_struct_blob(
+ &(element->values[j]),
+ mem_ctx,
+ &props[j],
+ (ndr_pull_flags_fn_t)
+ ndr_pull_dnsp_DnsProperty);
+ if (!NDR_ERR_CODE_IS_SUCCESS(err)){
+ /*
+ * Per Microsoft we must
+ * ignore invalid data here
+ * and continue as a Windows
+ * server can put in a
+ * structure with an invalid
+ * length.
+ *
+ * We can safely fill in an
+ * extra empty property here
+ * because
+ * dns_zoneinfo_load_zone_property()
+ * just ignores
+ * DSPROPERTY_ZONE_EMPTY
+ */
+ ZERO_STRUCT(props[j]);
+ props[j].id = DSPROPERTY_ZONE_EMPTY;
+ continue;
+ }
+ }
+ z->tmp_props = props;
+ z->num_props = element->num_values;
+ }
+ }
+ return zones;
+
+failed:
+ talloc_free(tmp_ctx);
+ return NULL;
+}
+
+
+/* Find DNS partition information */
+struct dnsserver_partition_info *dnsserver_db_partition_info(TALLOC_CTX *mem_ctx,
+ struct ldb_context *samdb,
+ struct dnsserver_partition *p)
+{
+ const char * const attrs[] = { "instanceType", "msDs-masteredBy", NULL };
+ const char * const attrs_none[] = { NULL };
+ struct ldb_result *res;
+ struct ldb_message_element *el;
+ struct ldb_dn *dn;
+ struct dnsserver_partition_info *partinfo;
+ int i, ret, instance_type;
+ TALLOC_CTX *tmp_ctx;
+
+ tmp_ctx = talloc_new(mem_ctx);
+ if (tmp_ctx == NULL) {
+ return NULL;
+ }
+
+ partinfo = talloc_zero(mem_ctx, struct dnsserver_partition_info);
+ if (partinfo == NULL) {
+ talloc_free(tmp_ctx);
+ return NULL;
+ }
+
+ /* Search for the active replica and state */
+ ret = ldb_search(samdb, tmp_ctx, &res, p->partition_dn, LDB_SCOPE_BASE,
+ attrs, NULL);
+ if (ret != LDB_SUCCESS || res->count != 1) {
+ goto failed;
+ }
+
+ /* Set the state of the partition */
+ instance_type = ldb_msg_find_attr_as_int(res->msgs[0], "instanceType", -1);
+ if (instance_type == -1) {
+ partinfo->dwState = DNS_DP_STATE_UNKNOWN;
+ } else if (instance_type & INSTANCE_TYPE_NC_COMING) {
+ partinfo->dwState = DNS_DP_STATE_REPL_INCOMING;
+ } else if (instance_type & INSTANCE_TYPE_NC_GOING) {
+ partinfo->dwState = DNS_DP_STATE_REPL_OUTGOING;
+ } else {
+ partinfo->dwState = DNS_DP_OKAY;
+ }
+
+ el = ldb_msg_find_element(res->msgs[0], "msDs-masteredBy");
+ if (el == NULL) {
+ partinfo->dwReplicaCount = 0;
+ partinfo->ReplicaArray = NULL;
+ } else {
+ partinfo->dwReplicaCount = el->num_values;
+ partinfo->ReplicaArray = talloc_zero_array(partinfo,
+ struct DNS_RPC_DP_REPLICA *,
+ el->num_values);
+ if (partinfo->ReplicaArray == NULL) {
+ goto failed;
+ }
+ for (i=0; i<el->num_values; i++) {
+ partinfo->ReplicaArray[i] = talloc_zero(partinfo,
+ struct DNS_RPC_DP_REPLICA);
+ if (partinfo->ReplicaArray[i] == NULL) {
+ goto failed;
+ }
+ partinfo->ReplicaArray[i]->pszReplicaDn = talloc_strdup(
+ partinfo,
+ (const char *)el->values[i].data);
+ if (partinfo->ReplicaArray[i]->pszReplicaDn == NULL) {
+ goto failed;
+ }
+ }
+ }
+ talloc_free(res);
+
+ /* Search for cross-reference object */
+ dn = ldb_dn_copy(tmp_ctx, ldb_get_config_basedn(samdb));
+ if (dn == NULL) {
+ goto failed;
+ }
+
+ ret = ldb_search(samdb, tmp_ctx, &res, dn, LDB_SCOPE_DEFAULT, attrs_none,
+ "(nCName=%s)", ldb_dn_get_linearized(p->partition_dn));
+ if (ret != LDB_SUCCESS || res->count != 1) {
+ goto failed;
+ }
+ partinfo->pszCrDn = talloc_strdup(partinfo, ldb_dn_get_linearized(res->msgs[0]->dn));
+ if (partinfo->pszCrDn == NULL) {
+ goto failed;
+ }
+ talloc_free(res);
+
+ talloc_free(tmp_ctx);
+ return partinfo;
+
+failed:
+ talloc_free(tmp_ctx);
+ talloc_free(partinfo);
+ return NULL;
+}
+
+
+/* Increment serial number and update timestamp */
+static unsigned int dnsserver_update_soa(TALLOC_CTX *mem_ctx,
+ struct ldb_context *samdb,
+ struct dnsserver_zone *z,
+ WERROR *werr)
+{
+ const char * const attrs[] = { "dnsRecord", NULL };
+ struct ldb_result *res;
+ struct dnsp_DnssrvRpcRecord rec;
+ struct ldb_message_element *el;
+ enum ndr_err_code ndr_err;
+ int ret, i, serial = -1;
+
+ *werr = WERR_INTERNAL_DB_ERROR;
+
+ ret = ldb_search(samdb, mem_ctx, &res, z->zone_dn, LDB_SCOPE_ONELEVEL, attrs,
+ "(&(objectClass=dnsNode)(name=@))");
+ if (ret != LDB_SUCCESS || res->count == 0) {
+ return -1;
+ }
+
+ el = ldb_msg_find_element(res->msgs[0], "dnsRecord");
+ if (el == NULL) {
+ return -1;
+ }
+
+ for (i=0; i<el->num_values; i++) {
+ ndr_err = ndr_pull_struct_blob(&el->values[i], mem_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) {
+ serial = rec.data.soa.serial + 1;
+ rec.dwSerial = serial;
+ rec.dwTimeStamp = 0;
+ rec.data.soa.serial = serial;
+
+ ndr_err = ndr_push_struct_blob(&el->values[i], mem_ctx, &rec,
+ (ndr_push_flags_fn_t)ndr_push_dnsp_DnssrvRpcRecord);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ *werr = WERR_NOT_ENOUGH_MEMORY;
+ return -1;
+ }
+ break;
+ }
+ }
+
+ if (serial != -1) {
+ el->flags = LDB_FLAG_MOD_REPLACE;
+ ret = ldb_modify(samdb, res->msgs[0]);
+ if (ret != LDB_SUCCESS) {
+ if (ret == LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS) {
+ *werr = WERR_ACCESS_DENIED;
+ }
+ return -1;
+ }
+ }
+
+ *werr = WERR_OK;
+
+ return serial;
+}
+
+
+/* Add DNS record to the database */
+static WERROR dnsserver_db_do_add_rec(TALLOC_CTX *mem_ctx,
+ struct ldb_context *samdb,
+ struct ldb_dn *dn,
+ int num_rec,
+ struct dnsp_DnssrvRpcRecord *rec)
+{
+ struct ldb_message *msg;
+ struct ldb_val v;
+ int ret;
+ enum ndr_err_code ndr_err;
+ int i;
+
+ msg = ldb_msg_new(mem_ctx);
+ W_ERROR_HAVE_NO_MEMORY(msg);
+
+ msg->dn = dn;
+ ret = ldb_msg_add_string(msg, "objectClass", "dnsNode");
+ if (ret != LDB_SUCCESS) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ if (num_rec > 0 && rec) {
+ for (i=0; i<num_rec; i++) {
+ ndr_err = ndr_push_struct_blob(&v, mem_ctx, &rec[i],
+ (ndr_push_flags_fn_t)ndr_push_dnsp_DnssrvRpcRecord);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return WERR_GEN_FAILURE;
+ }
+
+ ret = ldb_msg_add_value(msg, "dnsRecord", &v, NULL);
+ if (ret != LDB_SUCCESS) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+ }
+ }
+
+ ret = ldb_add(samdb, msg);
+ if (ret != LDB_SUCCESS) {
+ return WERR_INTERNAL_DB_ERROR;
+ }
+
+ return WERR_OK;
+}
+
+
+/* Add dnsNode record to the database with DNS record */
+WERROR dnsserver_db_add_empty_node(TALLOC_CTX *mem_ctx,
+ struct ldb_context *samdb,
+ struct dnsserver_zone *z,
+ const char *name)
+{
+ const char * const attrs[] = { "name", NULL };
+ struct ldb_result *res;
+ struct ldb_dn *dn;
+ char *encoded_name = ldb_binary_encode_string(mem_ctx, name);
+ struct ldb_val name_val = data_blob_string_const(name);
+ int ret;
+
+ ret = ldb_search(samdb, mem_ctx, &res, z->zone_dn, LDB_SCOPE_BASE, attrs,
+ "(&(objectClass=dnsNode)(name=%s))",
+ encoded_name);
+ if (ret != LDB_SUCCESS) {
+ return WERR_INTERNAL_DB_ERROR;
+ }
+
+ if (res->count > 0) {
+ talloc_free(res);
+ return WERR_DNS_ERROR_RECORD_ALREADY_EXISTS;
+ }
+
+ dn = ldb_dn_copy(mem_ctx, z->zone_dn);
+ W_ERROR_HAVE_NO_MEMORY(dn);
+
+ if (!ldb_dn_add_child_val(dn, "DC", name_val)) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ return dnsserver_db_do_add_rec(mem_ctx, samdb, dn, 0, NULL);
+}
+
+static void set_record_rank(struct dnsserver_zone *z,
+ const char *name,
+ struct dnsp_DnssrvRpcRecord *rec)
+{
+ if (z->zoneinfo->dwZoneType == DNS_ZONE_TYPE_PRIMARY) {
+ if (strcmp(name, "@") != 0 && rec->wType == DNS_TYPE_NS) {
+ rec->rank = DNS_RANK_NS_GLUE;
+ } else {
+ rec->rank = DNS_RANK_ZONE;
+ }
+ } else if (strcmp(z->name, ".") == 0) {
+ rec->rank = DNS_RANK_ROOT_HINT;
+ }
+}
+
+
+/* Add a DNS record */
+WERROR dnsserver_db_add_record(TALLOC_CTX *mem_ctx,
+ struct ldb_context *samdb,
+ struct dnsserver_zone *z,
+ const char *name,
+ struct DNS_RPC_RECORD *add_record)
+{
+ const char * const attrs[] = { "dnsRecord", "dNSTombstoned", NULL };
+ struct ldb_result *res;
+ struct dnsp_DnssrvRpcRecord *rec = NULL;
+ struct ldb_message_element *el;
+ struct ldb_dn *dn;
+ enum ndr_err_code ndr_err;
+ int ret, i;
+ int serial;
+ WERROR werr;
+ bool was_tombstoned = false;
+ char *encoded_name = ldb_binary_encode_string(mem_ctx, name);
+
+ werr = dns_to_dnsp_convert(mem_ctx, add_record, &rec, true);
+ if (!W_ERROR_IS_OK(werr)) {
+ return werr;
+ }
+
+ /* Set the correct rank for the record. */
+ set_record_rank(z, name, rec);
+
+ serial = dnsserver_update_soa(mem_ctx, samdb, z, &werr);
+ if (serial < 0) {
+ return werr;
+ }
+
+ rec->dwSerial = serial;
+ rec->dwTimeStamp = 0;
+
+ ret = ldb_search(samdb, mem_ctx, &res, z->zone_dn, LDB_SCOPE_ONELEVEL, attrs,
+ "(&(objectClass=dnsNode)(name=%s))",
+ encoded_name);
+ if (ret != LDB_SUCCESS) {
+ return WERR_INTERNAL_DB_ERROR;
+ }
+
+ if (res->count == 0) {
+ dn = dnsserver_name_to_dn(mem_ctx, z, name);
+ W_ERROR_HAVE_NO_MEMORY(dn);
+
+ return dnsserver_db_do_add_rec(mem_ctx, samdb, dn, 1, rec);
+ }
+
+ el = ldb_msg_find_element(res->msgs[0], "dnsRecord");
+ if (el == NULL) {
+ ret = ldb_msg_add_empty(res->msgs[0], "dnsRecord", 0, &el);
+ if (ret != LDB_SUCCESS) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+ }
+
+ was_tombstoned = ldb_msg_find_attr_as_bool(res->msgs[0],
+ "dNSTombstoned", false);
+ if (was_tombstoned) {
+ el->num_values = 0;
+ }
+
+ for (i=0; i<el->num_values; i++) {
+ struct dnsp_DnssrvRpcRecord rec2;
+
+ ndr_err = ndr_pull_struct_blob(&el->values[i], mem_ctx, &rec2,
+ (ndr_pull_flags_fn_t)ndr_pull_dnsp_DnssrvRpcRecord);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return WERR_GEN_FAILURE;
+ }
+
+ if (dns_record_match(rec, &rec2)) {
+ break;
+ }
+ }
+ if (i < el->num_values) {
+ return WERR_DNS_ERROR_RECORD_ALREADY_EXISTS;
+ }
+ if (i == el->num_values) {
+ /* adding a new value */
+ el->values = talloc_realloc(el, el->values, struct ldb_val, el->num_values+1);
+ W_ERROR_HAVE_NO_MEMORY(el->values);
+ el->num_values++;
+ }
+
+ ndr_err = ndr_push_struct_blob(&el->values[i], mem_ctx, rec,
+ (ndr_push_flags_fn_t)ndr_push_dnsp_DnssrvRpcRecord);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return WERR_GEN_FAILURE;
+ }
+
+ el->flags = LDB_FLAG_MOD_REPLACE;
+
+ el = ldb_msg_find_element(res->msgs[0], "dNSTombstoned");
+ if (el != NULL) {
+ el->flags = LDB_FLAG_MOD_DELETE;
+ }
+
+ ret = ldb_modify(samdb, res->msgs[0]);
+ if (ret != LDB_SUCCESS) {
+ return WERR_INTERNAL_DB_ERROR;
+ }
+
+ return WERR_OK;
+}
+
+
+/* Update a DNS record */
+WERROR dnsserver_db_update_record(TALLOC_CTX *mem_ctx,
+ struct ldb_context *samdb,
+ struct dnsserver_zone *z,
+ const char *name,
+ struct DNS_RPC_RECORD *add_record,
+ struct DNS_RPC_RECORD *del_record)
+{
+ const char * const attrs[] = { "dnsRecord", NULL };
+ struct ldb_result *res;
+ struct dnsp_DnssrvRpcRecord rec2;
+ struct dnsp_DnssrvRpcRecord *arec = NULL, *drec = NULL;
+ struct ldb_message_element *el;
+ enum ndr_err_code ndr_err;
+ int ret, i;
+ int serial;
+ WERROR werr;
+ bool updating_ttl = false;
+ char *encoded_name = ldb_binary_encode_string(mem_ctx, name);
+
+ werr = dns_to_dnsp_convert(mem_ctx, add_record, &arec, true);
+ if (!W_ERROR_IS_OK(werr)) {
+ return werr;
+ }
+
+ werr = dns_to_dnsp_convert(mem_ctx, del_record, &drec, true);
+ if (!W_ERROR_IS_OK(werr)) {
+ return werr;
+ }
+
+ ret = ldb_search(samdb, mem_ctx, &res, z->zone_dn, LDB_SCOPE_ONELEVEL, attrs,
+ "(&(objectClass=dnsNode)(name=%s)(!(dNSTombstoned=TRUE)))",
+ encoded_name);
+ if (ret != LDB_SUCCESS) {
+ return WERR_INTERNAL_DB_ERROR;
+ }
+
+ if (res->count == 0) {
+ return WERR_DNS_ERROR_RECORD_DOES_NOT_EXIST;
+ }
+
+ el = ldb_msg_find_element(res->msgs[0], "dnsRecord");
+ if (el == NULL || el->num_values == 0) {
+ return WERR_DNS_ERROR_RECORD_DOES_NOT_EXIST;
+ }
+
+ for (i=0; i<el->num_values; i++) {
+ ndr_err = ndr_pull_struct_blob(&el->values[i], mem_ctx, &rec2,
+ (ndr_pull_flags_fn_t)ndr_pull_dnsp_DnssrvRpcRecord);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return WERR_GEN_FAILURE;
+ }
+
+ if (dns_record_match(arec, &rec2)) {
+ break;
+ }
+ }
+ if (i < el->num_values) {
+ /*
+ * The record already exists, which is an error UNLESS we are
+ * doing an in-place update.
+ *
+ * Therefore we need to see if drec also matches, in which
+ * case it's OK, though we can only update dwTtlSeconds and
+ * reset the timestamp to zero.
+ */
+ updating_ttl = dns_record_match(drec, &rec2);
+ if (! updating_ttl) {
+ return WERR_DNS_ERROR_RECORD_ALREADY_EXISTS;
+ }
+ /* In this case the next loop is redundant */
+ }
+
+ for (i=0; i<el->num_values; i++) {
+ ndr_err = ndr_pull_struct_blob(&el->values[i], mem_ctx, &rec2,
+ (ndr_pull_flags_fn_t)ndr_pull_dnsp_DnssrvRpcRecord);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return WERR_GEN_FAILURE;
+ }
+
+ if (dns_record_match(drec, &rec2)) {
+ /*
+ * we are replacing this one with arec, which is done
+ * by pushing arec into el->values[i] below, after the
+ * various manipulations.
+ */
+ break;
+ }
+ }
+ if (i == el->num_values) {
+ return WERR_DNS_ERROR_RECORD_DOES_NOT_EXIST;
+ }
+
+ /*
+ * If we're updating a SOA record, use the specified serial.
+ *
+ * Otherwise, if we are updating ttl in place (i.e., not changing
+ * .wType and .data on a record), we should increment the existing
+ * serial, and save to the SOA.
+ *
+ * Outside of those two cases, we look for the zone's SOA record and
+ * use its serial.
+ */
+ if (arec->wType != DNS_TYPE_SOA) {
+ if (updating_ttl) {
+ /*
+ * In this case, we keep some of the old values.
+ */
+ arec->dwSerial = rec2.dwSerial;
+ arec->dwReserved = rec2.dwReserved;
+ /*
+ * TODO: if the old TTL and the new TTL are
+ * different, the serial number is incremented.
+ */
+ } else {
+ arec->dwReserved = 0;
+ serial = dnsserver_update_soa(mem_ctx, samdb, z, &werr);
+ if (serial < 0) {
+ return werr;
+ }
+ arec->dwSerial = serial;
+ }
+ }
+
+ /* Set the correct rank for the record. */
+ set_record_rank(z, name, arec);
+ /*
+ * Successful RPC updates *always* zero timestamp and flags and set
+ * version.
+ */
+ arec->dwTimeStamp = 0;
+ arec->version = 5;
+ arec->flags = 0;
+
+ ndr_err = ndr_push_struct_blob(&el->values[i], mem_ctx, arec,
+ (ndr_push_flags_fn_t)ndr_push_dnsp_DnssrvRpcRecord);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return WERR_GEN_FAILURE;
+ }
+
+ el->flags = LDB_FLAG_MOD_REPLACE;
+ ret = ldb_modify(samdb, res->msgs[0]);
+ if (ret != LDB_SUCCESS) {
+ return WERR_INTERNAL_DB_ERROR;
+ }
+
+ return WERR_OK;
+}
+
+
+/* Delete a DNS record */
+WERROR dnsserver_db_delete_record(TALLOC_CTX *mem_ctx,
+ struct ldb_context *samdb,
+ struct dnsserver_zone *z,
+ const char *name,
+ struct DNS_RPC_RECORD *del_record)
+{
+ const char * const attrs[] = { "dnsRecord", NULL };
+ struct ldb_result *res;
+ struct dnsp_DnssrvRpcRecord *rec = NULL;
+ struct ldb_message_element *el;
+ enum ndr_err_code ndr_err;
+ int ret, i;
+ int serial;
+ WERROR werr;
+
+ serial = dnsserver_update_soa(mem_ctx, samdb, z, &werr);
+ if (serial < 0) {
+ return werr;
+ }
+
+ werr = dns_to_dnsp_convert(mem_ctx, del_record, &rec, false);
+ if (!W_ERROR_IS_OK(werr)) {
+ return werr;
+ }
+
+ ret = ldb_search(samdb, mem_ctx, &res, z->zone_dn, LDB_SCOPE_ONELEVEL, attrs,
+ "(&(objectClass=dnsNode)(name=%s))",
+ ldb_binary_encode_string(mem_ctx, name));
+ if (ret != LDB_SUCCESS) {
+ return WERR_INTERNAL_DB_ERROR;
+ }
+
+ if (res->count == 0) {
+ return WERR_DNS_ERROR_RECORD_DOES_NOT_EXIST;
+ }
+ if (res->count > 1) {
+ return WERR_DNS_ERROR_RCODE_SERVER_FAILURE;
+ }
+
+ el = ldb_msg_find_element(res->msgs[0], "dnsRecord");
+ if (el == NULL || el->num_values == 0) {
+ return WERR_DNS_ERROR_RECORD_DOES_NOT_EXIST;
+ }
+
+ for (i=0; i<el->num_values; i++) {
+ struct dnsp_DnssrvRpcRecord rec2;
+
+ ndr_err = ndr_pull_struct_blob(&el->values[i], mem_ctx, &rec2,
+ (ndr_pull_flags_fn_t)ndr_pull_dnsp_DnssrvRpcRecord);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return WERR_GEN_FAILURE;
+ }
+
+ if (dns_record_match(rec, &rec2)) {
+ break;
+ }
+ }
+ if (i == el->num_values) {
+ return WERR_DNS_ERROR_RECORD_DOES_NOT_EXIST;
+ }
+ 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--;
+
+ if (el->num_values == 0) {
+ ret = ldb_delete(samdb, res->msgs[0]->dn);
+ } else {
+ el->flags = LDB_FLAG_MOD_REPLACE;
+ ret = ldb_modify(samdb, res->msgs[0]);
+ }
+ if (ret != LDB_SUCCESS) {
+ return WERR_INTERNAL_DB_ERROR;
+ }
+
+ return WERR_OK;
+}
+
+
+static bool dnsserver_db_msg_add_dnsproperty(TALLOC_CTX *mem_ctx,
+ struct ldb_message *msg,
+ struct dnsp_DnsProperty *prop)
+{
+ DATA_BLOB *prop_blob;
+ enum ndr_err_code ndr_err;
+ int ret;
+
+ prop_blob = talloc_zero(mem_ctx, DATA_BLOB);
+ if (prop_blob == NULL) return false;
+
+ ndr_err = ndr_push_struct_blob(prop_blob, mem_ctx, prop,
+ (ndr_push_flags_fn_t)ndr_push_dnsp_DnsProperty);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return false;
+ }
+ ret = ldb_msg_add_steal_value(msg, "dNSProperty", prop_blob);
+ if (ret != LDB_SUCCESS) {
+ return false;
+ }
+ return true;
+}
+
+WERROR dnsserver_db_do_reset_dword(struct ldb_context *samdb,
+ struct dnsserver_zone *z,
+ struct DNS_RPC_NAME_AND_PARAM *n_p)
+{
+ struct ldb_message_element *element = NULL;
+ struct dnsp_DnsProperty *prop = NULL;
+ enum ndr_err_code err;
+ TALLOC_CTX *tmp_ctx = NULL;
+ const char * const attrs[] = {"dNSProperty", NULL};
+ struct ldb_result *res = NULL;
+ int i, ret, prop_id;
+
+ if (strcasecmp(n_p->pszNodeName, "Aging") == 0) {
+ z->zoneinfo->fAging = n_p->dwParam;
+ prop_id = DSPROPERTY_ZONE_AGING_STATE;
+ } else if (strcasecmp(n_p->pszNodeName, "RefreshInterval") == 0) {
+ z->zoneinfo->dwRefreshInterval = n_p->dwParam;
+ prop_id = DSPROPERTY_ZONE_REFRESH_INTERVAL;
+ } else if (strcasecmp(n_p->pszNodeName, "NoRefreshInterval") == 0) {
+ z->zoneinfo->dwNoRefreshInterval = n_p->dwParam;
+ prop_id = DSPROPERTY_ZONE_NOREFRESH_INTERVAL;
+ } else if (strcasecmp(n_p->pszNodeName, "AllowUpdate") == 0) {
+ z->zoneinfo->fAllowUpdate = n_p->dwParam;
+ prop_id = DSPROPERTY_ZONE_ALLOW_UPDATE;
+ } else {
+ return WERR_UNKNOWN_PROPERTY;
+ }
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ ret = ldb_search(samdb, tmp_ctx, &res, z->zone_dn, LDB_SCOPE_BASE,
+ attrs, "(objectClass=dnsZone)");
+ if (ret != LDB_SUCCESS) {
+ DBG_ERR("dnsserver: no zone: %s\n",
+ ldb_dn_get_linearized(z->zone_dn));
+ TALLOC_FREE(tmp_ctx);
+ return WERR_INTERNAL_DB_ERROR;
+ }
+
+ if (res->count != 1) {
+ DBG_ERR("dnsserver: duplicate zone: %s\n",
+ ldb_dn_get_linearized(z->zone_dn));
+ TALLOC_FREE(tmp_ctx);
+ return WERR_GEN_FAILURE;
+ }
+
+ element = ldb_msg_find_element(res->msgs[0], "dNSProperty");
+ if (element == NULL) {
+ DBG_ERR("dnsserver: zone %s has no properties.\n",
+ ldb_dn_get_linearized(z->zone_dn));
+ TALLOC_FREE(tmp_ctx);
+ return WERR_INTERNAL_DB_ERROR;
+ }
+
+ for (i = 0; i < element->num_values; i++) {
+ prop = talloc_zero(element, struct dnsp_DnsProperty);
+ if (prop == NULL) {
+ TALLOC_FREE(tmp_ctx);
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+ err = ndr_pull_struct_blob(
+ &(element->values[i]),
+ tmp_ctx,
+ prop,
+ (ndr_pull_flags_fn_t)ndr_pull_dnsp_DnsProperty);
+ if (!NDR_ERR_CODE_IS_SUCCESS(err)){
+ /*
+ * If we can't pull it then try again parsing
+ * it again. A Windows server in the domain
+ * will permit the addition of an invalidly
+ * formed property with a 0 length and cause a
+ * failure here
+ */
+ struct dnsp_DnsProperty_short
+ *short_property
+ = talloc_zero(element,
+ struct dnsp_DnsProperty_short);
+ if (short_property == NULL) {
+ TALLOC_FREE(tmp_ctx);
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+ err = ndr_pull_struct_blob_all(
+ &(element->values[i]),
+ tmp_ctx,
+ short_property,
+ (ndr_pull_flags_fn_t)ndr_pull_dnsp_DnsProperty_short);
+ if (!NDR_ERR_CODE_IS_SUCCESS(err)) {
+ /*
+ * Unknown invalid data should be
+ * ignored and logged to match Windows
+ * behaviour
+ */
+ DBG_NOTICE("dnsserver: couldn't PULL "
+ "dnsProperty value#%d in "
+ "zone %s while trying to "
+ "reset id %d\n",
+ i,
+ ldb_dn_get_linearized(z->zone_dn),
+ prop_id);
+ continue;
+ }
+
+ /*
+ * Initialise the parts of the property not
+ * overwritten by value() in the IDL for
+ * re-push
+ */
+ *prop = (struct dnsp_DnsProperty){
+ .namelength = short_property->namelength,
+ .id = short_property->id,
+ .name = short_property->name
+ /* .data will be filled in below */
+ };
+ }
+
+ if (prop->id == prop_id) {
+ switch (prop_id) {
+ case DSPROPERTY_ZONE_AGING_STATE:
+ prop->data.aging_enabled = n_p->dwParam;
+ break;
+ case DSPROPERTY_ZONE_NOREFRESH_INTERVAL:
+ prop->data.norefresh_hours = n_p->dwParam;
+ break;
+ case DSPROPERTY_ZONE_REFRESH_INTERVAL:
+ prop->data.refresh_hours = n_p->dwParam;
+ break;
+ case DSPROPERTY_ZONE_ALLOW_UPDATE:
+ prop->data.allow_update_flag = n_p->dwParam;
+ break;
+ }
+
+ err = ndr_push_struct_blob(
+ &(element->values[i]),
+ tmp_ctx,
+ prop,
+ (ndr_push_flags_fn_t)ndr_push_dnsp_DnsProperty);
+ if (!NDR_ERR_CODE_IS_SUCCESS(err)){
+ DBG_ERR("dnsserver: couldn't PUSH dns prop id "
+ "%d in zone %s\n",
+ prop->id,
+ ldb_dn_get_linearized(z->zone_dn));
+ TALLOC_FREE(tmp_ctx);
+ return WERR_INTERNAL_DB_ERROR;
+ }
+ }
+ }
+
+ element->flags = LDB_FLAG_MOD_REPLACE;
+ ret = ldb_modify(samdb, res->msgs[0]);
+ if (ret != LDB_SUCCESS) {
+ TALLOC_FREE(tmp_ctx);
+ DBG_ERR("dnsserver: Failed to modify zone %s prop %s: %s\n",
+ z->name,
+ n_p->pszNodeName,
+ ldb_errstring(samdb));
+ return WERR_INTERNAL_DB_ERROR;
+ }
+ TALLOC_FREE(tmp_ctx);
+
+ return WERR_OK;
+}
+
+/* Create dnsZone record to database and set security descriptor */
+static WERROR dnsserver_db_do_create_zone(TALLOC_CTX *tmp_ctx,
+ struct ldb_context *samdb,
+ struct ldb_dn *zone_dn,
+ struct dnsserver_zone *z)
+{
+ const char * const attrs[] = { "objectSID", NULL };
+ struct ldb_message *msg;
+ struct ldb_result *res;
+ struct ldb_message_element *el;
+ const char sddl_template[] = "D:AI(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;CC;;;AU)(A;;RPLCLORC;;;WD)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;CI;RPWPCRCCDCLCRCWOWDSDDTSW;;;ED)(A;CIID;RPWPCRCCDCLCRCWOWDSDDTSW;;;%s)(A;CIID;RPWPCRCCDCLCRCWOWDSDDTSW;;;ED)(OA;CIID;RPWPCR;91e647de-d96f-4b70-9557-d63ff4f3ccd8;;PS)(A;CIID;RPWPCRCCDCLCLORCWOWDSDDTSW;;;EA)(A;CIID;LC;;;RU)(A;CIID;RPWPCRCCLCLORCWOWDSDSW;;;BA)S:AI";
+ char *sddl;
+ struct dom_sid dnsadmins_sid;
+ const struct dom_sid *domain_sid;
+ struct security_descriptor *secdesc;
+ struct dnsp_DnsProperty *prop;
+ DATA_BLOB *sd_encoded;
+ enum ndr_err_code ndr_err;
+ int ret;
+
+ /* Get DnsAdmins SID */
+ ret = ldb_search(samdb, tmp_ctx, &res, ldb_get_default_basedn(samdb),
+ LDB_SCOPE_DEFAULT, attrs, "(sAMAccountName=DnsAdmins)");
+ if (ret != LDB_SUCCESS || res->count != 1) {
+ return WERR_INTERNAL_DB_ERROR;
+ }
+
+ el = ldb_msg_find_element(res->msgs[0], "objectSID");
+ if (el == NULL || el->num_values != 1) {
+ return WERR_INTERNAL_DB_ERROR;
+ }
+
+ ndr_err = ndr_pull_struct_blob(&el->values[0], tmp_ctx, &dnsadmins_sid,
+ (ndr_pull_flags_fn_t)ndr_pull_dom_sid);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return WERR_INTERNAL_DB_ERROR;
+ }
+
+ /* create security descriptor with DnsAdmins GUID in sddl template */
+ sddl = talloc_asprintf(tmp_ctx, sddl_template,
+ dom_sid_string(tmp_ctx, &dnsadmins_sid));
+ if (sddl == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+ talloc_free(res);
+
+ domain_sid = samdb_domain_sid(samdb);
+ if (domain_sid == NULL) {
+ return WERR_INTERNAL_DB_ERROR;
+ }
+
+ secdesc = sddl_decode(tmp_ctx, sddl, domain_sid);
+ if (secdesc == NULL) {
+ return WERR_GEN_FAILURE;
+ }
+
+ msg = ldb_msg_new(tmp_ctx);
+ W_ERROR_HAVE_NO_MEMORY(msg);
+
+ msg->dn = zone_dn;
+ ret = ldb_msg_add_string(msg, "objectClass", "dnsZone");
+ if (ret != LDB_SUCCESS) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ sd_encoded = talloc_zero(tmp_ctx, DATA_BLOB);
+ W_ERROR_HAVE_NO_MEMORY(sd_encoded);
+
+ ndr_err = ndr_push_struct_blob(sd_encoded, tmp_ctx, secdesc,
+ (ndr_push_flags_fn_t)ndr_push_security_descriptor);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return WERR_GEN_FAILURE;
+ }
+
+ ret = ldb_msg_add_steal_value(msg, "nTSecurityDescriptor", sd_encoded);
+ if (ret != LDB_SUCCESS) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ /* dns zone Properties */
+ prop = talloc_zero(tmp_ctx, struct dnsp_DnsProperty);
+ W_ERROR_HAVE_NO_MEMORY(prop);
+
+ prop->version = 1;
+
+ /* zone type */
+ prop->id = DSPROPERTY_ZONE_TYPE;
+ prop->data.zone_type = z->zoneinfo->dwZoneType;
+ if (!dnsserver_db_msg_add_dnsproperty(tmp_ctx, msg, prop)) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ /* allow update */
+ prop->id = DSPROPERTY_ZONE_ALLOW_UPDATE;
+ prop->data.allow_update_flag = z->zoneinfo->fAllowUpdate;
+ if (!dnsserver_db_msg_add_dnsproperty(tmp_ctx, msg, prop)) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ /* secure time */
+ prop->id = DSPROPERTY_ZONE_SECURE_TIME;
+ prop->data.zone_secure_time = 0;
+ if (!dnsserver_db_msg_add_dnsproperty(tmp_ctx, msg, prop)) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ /* norefresh interval */
+ prop->id = DSPROPERTY_ZONE_NOREFRESH_INTERVAL;
+ prop->data.norefresh_hours = 168;
+ if (!dnsserver_db_msg_add_dnsproperty(tmp_ctx, msg, prop)) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ /* refresh interval */
+ prop->id = DSPROPERTY_ZONE_REFRESH_INTERVAL;
+ prop->data.refresh_hours = 168;
+ if (!dnsserver_db_msg_add_dnsproperty(tmp_ctx, msg, prop)) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ /* aging state */
+ prop->id = DSPROPERTY_ZONE_AGING_STATE;
+ prop->data.aging_enabled = z->zoneinfo->fAging;
+ if (!dnsserver_db_msg_add_dnsproperty(tmp_ctx, msg, prop)) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ /* aging enabled time */
+ prop->id = DSPROPERTY_ZONE_AGING_ENABLED_TIME;
+ prop->data.next_scavenging_cycle_hours = 0;
+ if (!dnsserver_db_msg_add_dnsproperty(tmp_ctx, msg, prop)) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ talloc_free(prop);
+
+ ret = ldb_add(samdb, msg);
+ if (ret != LDB_SUCCESS) {
+ DEBUG(0, ("dnsserver: Failed to create zone (%s): %s\n",
+ z->name, ldb_errstring(samdb)));
+
+ if (ret == LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS) {
+ return WERR_ACCESS_DENIED;
+ }
+
+ return WERR_INTERNAL_DB_ERROR;
+ }
+
+ return WERR_OK;
+}
+
+
+/* Create new dnsZone record and @ record (SOA + NS) */
+WERROR dnsserver_db_create_zone(struct ldb_context *samdb,
+ struct dnsserver_partition *partitions,
+ struct dnsserver_zone *zone,
+ struct loadparm_context *lp_ctx)
+{
+ struct dnsserver_partition *p;
+ bool in_forest = false;
+ WERROR status;
+ struct ldb_dn *dn;
+ TALLOC_CTX *tmp_ctx;
+ struct dnsp_DnssrvRpcRecord *dns_rec;
+ struct dnsp_soa soa;
+ char *tmpstr, *server_fqdn, *soa_email;
+ struct ldb_val name_val = data_blob_string_const(zone->name);
+
+ /* We only support primary zones for now */
+ if (zone->zoneinfo->dwZoneType != DNS_ZONE_TYPE_PRIMARY) {
+ return WERR_CALL_NOT_IMPLEMENTED;
+ }
+
+ /* Get the correct partition */
+ if (zone->partition->dwDpFlags & DNS_DP_FOREST_DEFAULT) {
+ in_forest = true;
+ }
+ for (p = partitions; p; p = p->next) {
+ if (in_forest == p->is_forest) {
+ break;
+ }
+ }
+ if (p == NULL) {
+ return WERR_DNS_ERROR_DP_DOES_NOT_EXIST;
+ }
+
+ tmp_ctx = talloc_new(NULL);
+ W_ERROR_HAVE_NO_MEMORY(tmp_ctx);
+
+ dn = ldb_dn_copy(tmp_ctx, p->partition_dn);
+ W_ERROR_HAVE_NO_MEMORY_AND_FREE(dn, tmp_ctx);
+
+ if (!ldb_dn_add_child_fmt(dn, "CN=MicrosoftDNS")) {
+ talloc_free(tmp_ctx);
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ if (!ldb_dn_add_child_val(dn, "DC", name_val)) {
+ talloc_free(tmp_ctx);
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ /* Add dnsZone record */
+ status = dnsserver_db_do_create_zone(tmp_ctx, samdb, dn, zone);
+ if (!W_ERROR_IS_OK(status)) {
+ talloc_free(tmp_ctx);
+ return status;
+ }
+
+ if (!ldb_dn_add_child_fmt(dn, "DC=@")) {
+ talloc_free(tmp_ctx);
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ dns_rec = talloc_zero_array(tmp_ctx, struct dnsp_DnssrvRpcRecord, 2);
+ W_ERROR_HAVE_NO_MEMORY_AND_FREE(dns_rec, tmp_ctx);
+
+ tmpstr = talloc_asprintf(tmp_ctx, "%s.%s",
+ lpcfg_netbios_name(lp_ctx),
+ lpcfg_realm(lp_ctx));
+ W_ERROR_HAVE_NO_MEMORY_AND_FREE(tmpstr, tmp_ctx);
+ server_fqdn = strlower_talloc(tmp_ctx, tmpstr);
+ W_ERROR_HAVE_NO_MEMORY_AND_FREE(server_fqdn, tmp_ctx);
+ talloc_free(tmpstr);
+
+ tmpstr = talloc_asprintf(tmp_ctx, "hostmaster.%s",
+ lpcfg_realm(lp_ctx));
+ W_ERROR_HAVE_NO_MEMORY_AND_FREE(tmpstr, tmp_ctx);
+ soa_email = strlower_talloc(tmp_ctx, tmpstr);
+ W_ERROR_HAVE_NO_MEMORY_AND_FREE(soa_email, tmp_ctx);
+ talloc_free(tmpstr);
+
+ /* SOA Record - values same as defined in provision/sambadns.py */
+ soa.serial = 1;
+ soa.refresh = 900;
+ soa.retry = 600;
+ soa.expire = 86400;
+ soa.minimum = 3600;
+ soa.mname = server_fqdn;
+ soa.rname = soa_email;
+
+ dns_rec[0].wType = DNS_TYPE_SOA;
+ dns_rec[0].rank = DNS_RANK_ZONE;
+ dns_rec[0].dwSerial = soa.serial;
+ dns_rec[0].dwTtlSeconds = 3600;
+ dns_rec[0].dwTimeStamp = 0;
+ dns_rec[0].data.soa = soa;
+
+ /* NS Record */
+ dns_rec[1].wType = DNS_TYPE_NS;
+ dns_rec[1].rank = DNS_RANK_ZONE;
+ dns_rec[1].dwSerial = soa.serial;
+ dns_rec[1].dwTtlSeconds = 3600;
+ dns_rec[1].dwTimeStamp = 0;
+ dns_rec[1].data.ns = server_fqdn;
+
+ /* Add @ Record */
+ status = dnsserver_db_do_add_rec(tmp_ctx, samdb, dn, 2, dns_rec);
+
+ talloc_free(tmp_ctx);
+ return status;
+}
+
+
+/* Delete dnsZone record and all DNS records in the zone */
+WERROR dnsserver_db_delete_zone(struct ldb_context *samdb,
+ struct dnsserver_zone *zone)
+{
+ int ret;
+
+ ret = ldb_transaction_start(samdb);
+ if (ret != LDB_SUCCESS) {
+ return WERR_INTERNAL_DB_ERROR;
+ }
+
+ ret = dsdb_delete(samdb, zone->zone_dn, DSDB_TREE_DELETE);
+ if (ret != LDB_SUCCESS) {
+ ldb_transaction_cancel(samdb);
+
+ if (ret == LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS) {
+ return WERR_ACCESS_DENIED;
+ }
+ return WERR_INTERNAL_DB_ERROR;
+ }
+
+ ret = ldb_transaction_commit(samdb);
+ if (ret != LDB_SUCCESS) {
+ return WERR_INTERNAL_DB_ERROR;
+ }
+
+ return WERR_OK;
+}