summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAaron Haslett <aaronhaslett@catalyst.net.nz>2018-06-01 16:07:46 +1200
committerAndrew Bartlett <abartlet@samba.org>2018-07-12 04:31:55 +0200
commit50d961c1a2de87067606897b794a47c80513bb64 (patch)
treea5a4e5102272594ff277ed6f2bae093cd022e811
parent6bd2f82b9ff7629effe8280ab8f6bf9d721cf767 (diff)
downloadsamba-50d961c1a2de87067606897b794a47c80513bb64.tar.gz
dns: dns record scavenging function (without task)
DNS record scavenging function with testing. The logic of the custom match rule in previous commit is inverted so that calculations using zone properties can be taken out of the function's inner loop. Periodic task to come. BUG: https://bugzilla.samba.org/show_bug.cgi?id=10812 Signed-off-by: Aaron Haslett <aaronhaslett@catalyst.net.nz> Reviewed-by: Gary Lockyer <gary@catalyst.net.nz> Reviewed-by: Andrew Bartlett <abartlet@samba.org>
-rw-r--r--python/samba/tests/dns.py49
-rw-r--r--selftest/knownfail.d/dns-scavenging2
-rw-r--r--source4/dsdb/kcc/kcc_service.h1
-rw-r--r--source4/dsdb/kcc/scavenge_dns_records.c441
-rw-r--r--source4/dsdb/kcc/scavenge_dns_records.h48
-rw-r--r--source4/dsdb/pydsdb.c73
-rw-r--r--source4/dsdb/wscript_build10
-rw-r--r--source4/rpc_server/dnsserver/dnsdb.c2
8 files changed, 617 insertions, 9 deletions
diff --git a/python/samba/tests/dns.py b/python/samba/tests/dns.py
index c0f71dbf633..faf4c524076 100644
--- a/python/samba/tests/dns.py
+++ b/python/samba/tests/dns.py
@@ -1094,6 +1094,11 @@ class TestZones(DNSTest):
self.assertEqual(recs[0].dwTimeStamp, 0)
def test_dns_tombstone_custom_match_rule(self):
+ lp = self.get_loadparm()
+ self.samdb = SamDB(url = lp.samdb_url(), lp = lp,
+ session_info=system_session(),
+ credentials=self.creds)
+
name,txt = 'agingtest', ['test txt']
name2,txt2 = 'agingtest2', ['test txt2']
name3,txt3 = 'agingtest3', ['test txt3']
@@ -1140,27 +1145,63 @@ class TestZones(DNSTest):
self.assertEquals(len(res), 0)
def test_basic_scavenging(self):
+ lp = self.get_loadparm()
+ self.samdb = SamDB(url = lp.samdb_url(), lp = lp,
+ session_info=system_session(),
+ credentials=self.creds)
+
self.create_zone(self.zone, aging_enabled=True)
interval = 1
self.set_params(NoRefreshInterval=interval, RefreshInterval=interval,
zone=self.zone, Aging=1,
AllowUpdate = dnsp.DNS_ZONE_UPDATE_UNSECURE)
name, txt = 'agingtest', ['test txt']
- rec = self.dns_update_record(name,txt)
- rec = self.dns_update_record(name+'2',txt)
+ name2, txt2 = 'agingtest2', ['test txt2']
+ name3, txt3 = 'agingtest3', ['test txt3']
+ self.dns_update_record(name,txt)
+ self.dns_update_record(name2,txt)
+ self.dns_update_record(name2,txt2)
+ self.dns_update_record(name3,txt)
+ self.dns_update_record(name3,txt2)
+ last_add = self.dns_update_record(name3,txt3)
+
def mod_ts(rec):
self.assertTrue(rec.dwTimeStamp > 0)
- rec.dwTimeStamp -= interval*5
+ if rec.data.str == txt:
+ rec.dwTimeStamp -= interval*5
self.ldap_modify_dnsrecs(name, mod_ts)
+ self.ldap_modify_dnsrecs(name2, mod_ts)
+ self.ldap_modify_dnsrecs(name3, mod_ts)
self.assertTrue(callable(getattr(dsdb, '_scavenge_dns_records', None)))
dsdb._scavenge_dns_records(self.samdb)
recs = self.ldap_get_dns_records(name)
self.assertEqual(len(recs), 1)
self.assertEqual(recs[0].wType, dnsp.DNS_TYPE_TOMBSTONE)
- recs = self.ldap_get_dns_records(name+'2')
+
+ recs = self.ldap_get_dns_records(name2)
self.assertEqual(len(recs), 1)
self.assertEqual(recs[0].wType, dnsp.DNS_TYPE_TXT)
+ self.assertEqual(recs[0].data.str, txt2)
+
+ recs = self.ldap_get_dns_records(name3)
+ self.assertEqual(len(recs), 2)
+ txts = {str(r.data.str) for r in recs}
+ self.assertEqual(txts, {str(txt2),str(txt3)})
+ self.assertEqual(recs[0].wType, dnsp.DNS_TYPE_TXT)
+ self.assertEqual(recs[1].wType, dnsp.DNS_TYPE_TXT)
+
+ for make_it_work in [False, True]:
+ inc = -1 if make_it_work else 1
+ def mod_ts(rec):
+ rec.data = (last_add.dwTimeStamp - 24*14) + inc
+ self.ldap_modify_dnsrecs(name, mod_ts)
+ dsdb._dns_delete_tombstones(self.samdb)
+ recs = self.ldap_get_records(name)
+ if make_it_work:
+ self.assertEqual(len(recs), 0)
+ else:
+ self.assertEqual(len(recs), 1)
def delete_zone(self, zone):
self.rpc_conn.DnssrvOperation2(dnsserver.DNS_CLIENT_VERSION_LONGHORN,
diff --git a/selftest/knownfail.d/dns-scavenging b/selftest/knownfail.d/dns-scavenging
index 8de310186d1..86c0fa81e37 100644
--- a/selftest/knownfail.d/dns-scavenging
+++ b/selftest/knownfail.d/dns-scavenging
@@ -3,5 +3,3 @@
#
# Will be removed once the tests are implemented.
#
-samba.tests.dns.__main__.TestZones.test_basic_scavenging\(fl2003dc:local\)
-samba.tests.dns.__main__.TestZones.test_dns_tombstone_custom_match_rule\(fl2003dc:local\)
diff --git a/source4/dsdb/kcc/kcc_service.h b/source4/dsdb/kcc/kcc_service.h
index b62fb12f1d0..a4569033866 100644
--- a/source4/dsdb/kcc/kcc_service.h
+++ b/source4/dsdb/kcc/kcc_service.h
@@ -91,6 +91,7 @@ struct kccsrv_service {
struct kcc_connection_list;
#include "dsdb/kcc/garbage_collect_tombstones.h"
+#include "dsdb/kcc/scavenge_dns_records.h"
#include "dsdb/kcc/kcc_service_proto.h"
#endif /* _DSDB_REPL_KCC_SERVICE_H_ */
diff --git a/source4/dsdb/kcc/scavenge_dns_records.c b/source4/dsdb/kcc/scavenge_dns_records.c
new file mode 100644
index 00000000000..2f4f482e3da
--- /dev/null
+++ b/source4/dsdb/kcc/scavenge_dns_records.c
@@ -0,0 +1,441 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ DNS tombstoning routines
+
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2018
+
+ 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 <ldb_errors.h>
+#include "../lib/util/dlinklist.h"
+#include "librpc/gen_ndr/ndr_misc.h"
+#include "librpc/gen_ndr/ndr_drsuapi.h"
+#include "librpc/gen_ndr/ndr_drsblobs.h"
+#include "param/param.h"
+#include "lib/util/dlinklist.h"
+#include "ldb.h"
+#include "dsdb/kcc/scavenge_dns_records.h"
+#include "lib/ldb-samba/ldb_matching_rules.h"
+#include "lib/util/time.h"
+#include "dns_server/dnsserver_common.h"
+#include "librpc/gen_ndr/ndr_dnsp.h"
+#include "param/param.h"
+
+#include "librpc/gen_ndr/ndr_misc.h"
+#include "librpc/gen_ndr/ndr_drsuapi.h"
+#include "librpc/gen_ndr/ndr_drsblobs.h"
+
+/*
+ * Copy only non-expired dns records from one message element to another.
+ */
+NTSTATUS copy_current_records(TALLOC_CTX *mem_ctx,
+ struct ldb_message_element *old_el,
+ struct ldb_message_element *el,
+ NTTIME t)
+{
+ unsigned int i, num_kept = 0;
+ struct dnsp_DnssrvRpcRecord *recs = NULL;
+ enum ndr_err_code ndr_err;
+ TALLOC_CTX *tmp_ctx = talloc_new(NULL);
+
+ if (tmp_ctx == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ recs = talloc_zero_array(
+ tmp_ctx, struct dnsp_DnssrvRpcRecord, el->num_values);
+ if (recs == NULL) {
+ TALLOC_FREE(tmp_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ for (i = 0; i < el->num_values; i++) {
+ ndr_err = ndr_pull_struct_blob(
+ &(old_el->values[i]),
+ tmp_ctx,
+ &(recs[num_kept]),
+ (ndr_pull_flags_fn_t)ndr_pull_dnsp_DnssrvRpcRecord);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ TALLOC_FREE(tmp_ctx);
+ DBG_ERR("Failed to pull dns rec blob.\n");
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+ if (recs[num_kept].dwTimeStamp > t ||
+ recs[num_kept].dwTimeStamp == 0) {
+ num_kept++;
+ }
+ }
+
+ if (num_kept == el->num_values) {
+ TALLOC_FREE(tmp_ctx);
+ return NT_STATUS_OK;
+ }
+
+ el->values = talloc_zero_array(mem_ctx, struct ldb_val, num_kept);
+ if (el->values == NULL) {
+ TALLOC_FREE(tmp_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+ el->num_values = num_kept;
+ for (i = 0; i < el->num_values; i++) {
+ ndr_err = ndr_push_struct_blob(
+ &(el->values[i]),
+ mem_ctx,
+ &(recs[i]),
+ (ndr_push_flags_fn_t)ndr_push_dnsp_DnssrvRpcRecord);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ TALLOC_FREE(tmp_ctx);
+ DBG_ERR("Failed to push dnsp_DnssrvRpcRecord\n");
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+ }
+
+ TALLOC_FREE(tmp_ctx);
+ return NT_STATUS_OK;
+}
+
+/*
+ * Check all records in a zone and tombstone them if they're expired.
+ */
+NTSTATUS dns_tombstone_records_zone(TALLOC_CTX *mem_ctx,
+ struct ldb_context *samdb,
+ struct dns_server_zone *zone,
+ struct ldb_val *true_struct,
+ struct ldb_val *tombstone_blob,
+ NTTIME t,
+ char **error_string)
+{
+ WERROR werr;
+ NTSTATUS status;
+ unsigned int i;
+ struct dnsserver_zoneinfo *zi = NULL;
+ struct ldb_result *res = NULL;
+ struct ldb_message_element *el = NULL;
+ struct ldb_message_element *tombstone_el = NULL;
+ struct ldb_message_element *old_el = NULL;
+ int ret;
+ struct GUID guid;
+ struct GUID_txt_buf buf_guid;
+ const char *attrs[] = {"dnsRecord",
+ "dNSTombstoned",
+ "objectGUID",
+ NULL};
+
+ *error_string = NULL;
+
+ /* Get NoRefreshInterval and RefreshInterval from zone properties.*/
+ zi = talloc(mem_ctx, struct dnsserver_zoneinfo);
+ if (zi == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ werr = dns_get_zone_properties(samdb, mem_ctx, zone->dn, zi);
+ if (W_ERROR_EQUAL(DNS_ERR(NOTZONE), werr)) {
+ return NT_STATUS_PROPSET_NOT_FOUND;
+ } else if (!W_ERROR_IS_OK(werr)) {
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ /* Subtract them from current time to get the earliest possible.
+ * timestamp allowed for a non-expired DNS record. */
+ t -= zi->dwNoRefreshInterval + zi->dwRefreshInterval;
+
+ /* Custom match gets dns records in the zone with dwTimeStamp < t. */
+ ret = ldb_search(samdb,
+ mem_ctx,
+ &res,
+ zone->dn,
+ LDB_SCOPE_SUBTREE,
+ attrs,
+ "(&(objectClass=dnsNode)"
+ "(&(!(dnsTombstoned=TRUE))"
+ "(dnsRecord:" DSDB_MATCH_FOR_DNS_TO_TOMBSTONE_TIME
+ ":=%lu)))",
+ t);
+ if (ret != LDB_SUCCESS) {
+ *error_string = talloc_asprintf(mem_ctx,
+ "Failed to search for dns "
+ "objects in zone %s: %s",
+ ldb_dn_get_linearized(zone->dn),
+ ldb_errstring(samdb));
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ /*
+ * Do a constrained update on each expired DNS node. To do a constrained
+ * update we leave the dnsRecord element as is, and just change the flag
+ * to MOD_DELETE, then add a new element with the changes we want. LDB
+ * will run the deletion first, and bail out if a binary comparison
+ * between the attribute we pass and the one in the database shows a
+ * change. This prevents race conditions.
+ */
+ for (i = 0; i < res->count; i++) {
+ old_el = ldb_msg_find_element(res->msgs[i], "dnsRecord");
+ old_el->flags = LDB_FLAG_MOD_DELETE;
+
+ ret = ldb_msg_add_empty(
+ res->msgs[i], "dnsRecord", LDB_FLAG_MOD_ADD, &el);
+ if (ret != LDB_SUCCESS) {
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ el->num_values = old_el->num_values;
+ status = copy_current_records(mem_ctx, old_el, el, t);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ /* If nothing was expired, do nothing. */
+ if (el->num_values == old_el->num_values &&
+ el->num_values != 0) {
+ continue;
+ }
+
+ el->flags = LDB_FLAG_MOD_ADD;
+
+ /* If everything was expired, we tombstone the node. */
+ if (el->num_values == 0) {
+ el->values = tombstone_blob;
+ el->num_values = 1;
+
+ tombstone_el = ldb_msg_find_element(res->msgs[i],
+ "dnsTombstoned");
+ if (tombstone_el == NULL) {
+ ret = ldb_msg_add_value(res->msgs[i],
+ "dnsTombstoned",
+ true_struct,
+ &tombstone_el);
+ if (ret != LDB_SUCCESS) {
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+ tombstone_el->flags = LDB_FLAG_MOD_ADD;
+ } else {
+ tombstone_el->flags = LDB_FLAG_MOD_REPLACE;
+ tombstone_el->values = true_struct;
+ }
+ tombstone_el->num_values = 1;
+ } else {
+ /*
+ * Do not change the status of dnsTombstoned
+ * if we found any live records
+ */
+ ldb_msg_remove_attr(res->msgs[i],
+ "dnsTombstoned");
+ }
+
+ /* Set DN to the GUID in case the object was moved. */
+ el = ldb_msg_find_element(res->msgs[i], "objectGUID");
+ if (el == NULL) {
+ *error_string =
+ talloc_asprintf(mem_ctx,
+ "record has no objectGUID "
+ "in zone %s",
+ ldb_dn_get_linearized(zone->dn));
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ status = GUID_from_ndr_blob(el->values, &guid);
+ if (!NT_STATUS_IS_OK(status)) {
+ *error_string =
+ discard_const_p(char, "Error: Invalid GUID.\n");
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ GUID_buf_string(&guid, &buf_guid);
+ res->msgs[i]->dn =
+ ldb_dn_new_fmt(mem_ctx, samdb, "<GUID=%s>", buf_guid.buf);
+
+ /* Remove the GUID so we're not trying to modify it. */
+ ldb_msg_remove_attr(res->msgs[i], "objectGUID");
+
+ ret = ldb_modify(samdb, res->msgs[i]);
+ if (ret != LDB_SUCCESS) {
+ *error_string =
+ talloc_asprintf(mem_ctx,
+ "Failed to modify dns record "
+ "in zone %s: %s",
+ ldb_dn_get_linearized(zone->dn),
+ ldb_errstring(samdb));
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*
+ * Tombstone all expired DNS records.
+ */
+NTSTATUS dns_tombstone_records(TALLOC_CTX *mem_ctx,
+ struct ldb_context *samdb,
+ char **error_string)
+{
+ struct dns_server_zone *zones = NULL;
+ struct dns_server_zone *z = NULL;
+ NTSTATUS ret;
+ struct dnsp_DnssrvRpcRecord tombstone_struct;
+ struct ldb_val tombstone_blob;
+ struct ldb_val true_struct;
+ NTTIME t;
+ enum ndr_err_code ndr_err;
+ TALLOC_CTX *tmp_ctx = NULL;
+ uint8_t true_str[4] = "TRUE";
+
+ unix_to_nt_time(&t, time(NULL));
+ t /= 10 * 1000 * 1000;
+ t /= 3600;
+
+ tombstone_struct = (struct dnsp_DnssrvRpcRecord){
+ .wType = DNS_TYPE_TOMBSTONE, .data = {.timestamp = t}};
+
+ true_struct = (struct ldb_val){.data = true_str, .length = 4};
+
+ ndr_err = ndr_push_struct_blob(
+ &tombstone_blob,
+ mem_ctx,
+ &tombstone_struct,
+ (ndr_push_flags_fn_t)ndr_push_dnsp_DnssrvRpcRecord);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ *error_string = discard_const_p(char,
+ "Failed to push "
+ "dnsp_DnssrvRpcRecord\n");
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ dns_common_zones(samdb, mem_ctx, NULL, &zones);
+ for (z = zones; z; z = z->next) {
+ tmp_ctx = talloc_new(NULL);
+ ret = dns_tombstone_records_zone(tmp_ctx,
+ samdb,
+ z,
+ &true_struct,
+ &tombstone_blob,
+ t,
+ error_string);
+ TALLOC_FREE(tmp_ctx);
+ if (NT_STATUS_EQUAL(ret, NT_STATUS_PROPSET_NOT_FOUND)) {
+ continue;
+ } else if (!NT_STATUS_IS_OK(ret)) {
+ return ret;
+ }
+ }
+ return NT_STATUS_OK;
+}
+
+/*
+ * Delete all DNS tombstones that have been around for longer than the server
+ * property 'DsTombstoneInterval' which we store in smb.conf
+ */
+NTSTATUS dns_delete_tombstones(TALLOC_CTX *mem_ctx,
+ struct ldb_context *samdb,
+ char **error_string)
+{
+ struct dns_server_zone *zones = NULL;
+ struct dns_server_zone *z = NULL;
+ int ret, i;
+ NTTIME current_time;
+ enum ndr_err_code ndr_err;
+ struct ldb_result *res = NULL;
+ int tombstone_time;
+ TALLOC_CTX *tmp_ctx = NULL;
+ struct loadparm_context *lp_ctx = NULL;
+ struct ldb_message_element *el = NULL;
+ struct dnsp_DnssrvRpcRecord *rec = NULL;
+ const char *attrs[] = {"dnsRecord", "dNSTombstoned", NULL};
+ rec = talloc_zero(mem_ctx, struct dnsp_DnssrvRpcRecord);
+
+ unix_to_nt_time(&current_time, time(NULL));
+ current_time /= 10 * 1000 * 1000;
+ current_time /= 3600;
+
+ lp_ctx = (struct loadparm_context *)ldb_get_opaque(samdb, "loadparm");
+ tombstone_time =
+ current_time -
+ lpcfg_parm_int(
+ lp_ctx, NULL, "dnsserver", "dns_tombstone_interval", 24 * 14);
+
+ dns_common_zones(samdb, mem_ctx, NULL, &zones);
+ for (z = zones; z; z = z->next) {
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /*
+ * This can load a very large set, but on the
+ * assumption that the number of tombstones is
+ * relatively small compared with the number of active
+ * records, and that this is an indexed lookup, this
+ * should be OK. We can make a match rule if
+ * returning the set of tombstones becomes an issue.
+ */
+
+ ret = ldb_search(samdb,
+ tmp_ctx,
+ &res,
+ z->dn,
+ LDB_SCOPE_SUBTREE,
+ attrs,
+ "(&(objectClass=dnsNode)(dNSTombstoned=TRUE))");
+
+ if (ret != LDB_SUCCESS) {
+ TALLOC_FREE(tmp_ctx);
+ *error_string =
+ talloc_asprintf(mem_ctx,
+ "Failed to "
+ "search for tombstoned "
+ "dns objects in zone %s: %s",
+ ldb_dn_get_linearized(z->dn),
+ ldb_errstring(samdb));
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ for (i = 0; i < res->count; i++) {
+ el = ldb_msg_find_element(res->msgs[i], "dnsRecord");
+ ndr_err = ndr_pull_struct_blob(
+ el->values,
+ tmp_ctx,
+ rec,
+ (ndr_pull_flags_fn_t)ndr_pull_dnsp_DnssrvRpcRecord);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ TALLOC_FREE(tmp_ctx);
+ DBG_ERR("Failed to pull dns rec blob.\n");
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ if (rec->wType != DNS_TYPE_TOMBSTONE) {
+ continue;
+ }
+
+ if (rec->data.timestamp > tombstone_time) {
+ continue;
+ }
+
+ ret = dsdb_delete(samdb, res->msgs[i]->dn, 0);
+ if (ret != LDB_ERR_NO_SUCH_OBJECT &&
+ ret != LDB_SUCCESS) {
+ TALLOC_FREE(tmp_ctx);
+ DBG_ERR("Failed to delete dns node \n");
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+ }
+
+ TALLOC_FREE(tmp_ctx);
+ }
+ return NT_STATUS_OK;
+}
diff --git a/source4/dsdb/kcc/scavenge_dns_records.h b/source4/dsdb/kcc/scavenge_dns_records.h
new file mode 100644
index 00000000000..799fedba258
--- /dev/null
+++ b/source4/dsdb/kcc/scavenge_dns_records.h
@@ -0,0 +1,48 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ DNS tombstoning routines
+
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2018
+
+ 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 "param/param.h"
+#include "dsdb/samdb/samdb.h"
+#include "dsdb/common/util.h"
+#include "dns_server/dnsserver_common.h"
+
+NTSTATUS dns_tombstone_records(TALLOC_CTX *mem_ctx,
+ struct ldb_context *samdb,
+ char **error_string);
+
+NTSTATUS dns_delete_tombstones(TALLOC_CTX *mem_ctx,
+ struct ldb_context *samdb,
+ char **error_string);
+NTSTATUS remove_expired_records(TALLOC_CTX *mem_ctx,
+ struct ldb_message_element *el,
+ NTTIME t);
+NTSTATUS dns_tombstone_records_zone(TALLOC_CTX *mem_ctx,
+ struct ldb_context *samdb,
+ struct dns_server_zone *zone,
+ struct ldb_val *true_struct,
+ struct ldb_val *tombstone_blob,
+ NTTIME t,
+ char **error_string);
+
+NTSTATUS copy_current_records(TALLOC_CTX *mem_ctx,
+ struct ldb_message_element *old_el,
+ struct ldb_message_element *el,
+ NTTIME t);
diff --git a/source4/dsdb/pydsdb.c b/source4/dsdb/pydsdb.c
index a25f3411ae7..62d2a9120a9 100644
--- a/source4/dsdb/pydsdb.c
+++ b/source4/dsdb/pydsdb.c
@@ -32,6 +32,7 @@
#include "param/pyparam.h"
#include "lib/util/dlinklist.h"
#include "dsdb/kcc/garbage_collect_tombstones.h"
+#include "dsdb/kcc/scavenge_dns_records.h"
/* FIXME: These should be in a header file somewhere */
@@ -1150,6 +1151,74 @@ static PyObject *py_dsdb_allocate_rid(PyObject *self, PyObject *args)
return PyInt_FromLong(rid);
}
+static PyObject *py_dns_delete_tombstones(PyObject *self, PyObject *args)
+{
+ PyObject *py_ldb;
+ NTSTATUS status;
+ struct ldb_context *ldb = NULL;
+ TALLOC_CTX *mem_ctx = NULL;
+ char *error_string = NULL;
+
+ if (!PyArg_ParseTuple(args, "O", &py_ldb)) {
+ return NULL;
+ }
+ PyErr_LDB_OR_RAISE(py_ldb, ldb);
+
+ mem_ctx = talloc_new(ldb);
+ if (mem_ctx == NULL) {
+ return PyErr_NoMemory();
+ }
+
+ status = dns_delete_tombstones(mem_ctx, ldb, &error_string);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ if (error_string) {
+ PyErr_Format(PyExc_RuntimeError, "%s", error_string);
+ } else {
+ PyErr_SetNTSTATUS(status);
+ }
+ TALLOC_FREE(mem_ctx);
+ return NULL;
+ }
+
+ TALLOC_FREE(mem_ctx);
+ Py_RETURN_NONE;
+}
+
+static PyObject *py_scavenge_dns_records(PyObject *self, PyObject *args)
+{
+ PyObject *py_ldb;
+ NTSTATUS status;
+ struct ldb_context *ldb = NULL;
+ TALLOC_CTX *mem_ctx = NULL;
+ char *error_string = NULL;
+
+ if (!PyArg_ParseTuple(args, "O", &py_ldb)) {
+ return NULL;
+ }
+ PyErr_LDB_OR_RAISE(py_ldb, ldb);
+
+ mem_ctx = talloc_new(ldb);
+ if (mem_ctx == NULL) {
+ return PyErr_NoMemory();
+ }
+
+ status = dns_tombstone_records(mem_ctx, ldb, &error_string);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ if (error_string) {
+ PyErr_Format(PyExc_RuntimeError, "%s", error_string);
+ } else {
+ PyErr_SetNTSTATUS(status);
+ }
+ TALLOC_FREE(mem_ctx);
+ return NULL;
+ }
+
+ TALLOC_FREE(mem_ctx);
+ Py_RETURN_NONE;
+}
+
static PyObject *py_dsdb_garbage_collect_tombstones(PyObject *self, PyObject *args)
{
PyObject *py_ldb, *py_list_dn;
@@ -1383,6 +1452,10 @@ static PyMethodDef py_dsdb_methods[] = {
{ "_dsdb_garbage_collect_tombstones", (PyCFunction)py_dsdb_garbage_collect_tombstones, METH_VARARGS,
"_dsdb_kcc_check_deleted(samdb, [dn], current_time, tombstone_lifetime)"
" -> (num_objects_expunged, num_links_expunged)" },
+ { "_scavenge_dns_records", (PyCFunction)py_scavenge_dns_records,
+ METH_VARARGS, NULL},
+ { "_dns_delete_tombstones", (PyCFunction)py_dns_delete_tombstones,
+ METH_VARARGS, NULL},
{ "_dsdb_create_own_rid_set", (PyCFunction)py_dsdb_create_own_rid_set, METH_VARARGS,
"_dsdb_create_own_rid_set(samdb)"
" -> None" },
diff --git a/source4/dsdb/wscript_build b/source4/dsdb/wscript_build
index e1f1e61c55e..be99e9950ef 100644
--- a/source4/dsdb/wscript_build
+++ b/source4/dsdb/wscript_build
@@ -42,12 +42,17 @@ bld.SAMBA_LIBRARY('dsdb_garbage_collect_tombstones',
deps='samdb RPC_NDR_DRSUAPI',
private_library=True)
+bld.SAMBA_LIBRARY('scavenge_dns_records',
+ source='kcc/scavenge_dns_records.c',
+ deps='samdb RPC_NDR_DRSUAPI dnsserver_common',
+ private_library=True)
+
bld.SAMBA_MODULE('service_kcc',
source='kcc/kcc_service.c kcc/kcc_connection.c kcc/kcc_periodic.c kcc/kcc_drs_replica_info.c',
autoproto='kcc/kcc_service_proto.h',
subsystem='service',
init_function='server_service_kcc_init',
- deps='samdb process_model RPC_NDR_IRPC RPC_NDR_DRSUAPI UTIL_RUNCMD dsdb_garbage_collect_tombstones',
+ deps='samdb process_model RPC_NDR_IRPC RPC_NDR_DRSUAPI UTIL_RUNCMD dsdb_garbage_collect_tombstones scavenge_dns_records',
internal_module=False,
enabled=bld.AD_DC_BUILD_IS_ENABLED()
)
@@ -71,6 +76,7 @@ for env in bld.gen_python_environments():
# the dependency on dcerpc here is because gensec
# depends on dcerpc but the waf circular dependency finder
# removes it so we end up with unresolved symbols.
- deps='samdb %s dcerpc com_err %s %s dsdb_garbage_collect_tombstones' % (pyldb_util, pyrpc_util, pyparam_util),
+ deps='samdb %s dcerpc com_err %s %s dsdb_garbage_collect_tombstones scavenge_dns_records' %\
+ (pyldb_util, pyrpc_util, pyparam_util),
realname='samba/dsdb.so'
)
diff --git a/source4/rpc_server/dnsserver/dnsdb.c b/source4/rpc_server/dnsserver/dnsdb.c
index 350e29aa1e0..899c7ecedb6 100644
--- a/source4/rpc_server/dnsserver/dnsdb.c
+++ b/source4/rpc_server/dnsserver/dnsdb.c
@@ -145,7 +145,7 @@ struct dnsserver_zone *dnsserver_db_enumerate_zones(TALLOC_CTX *mem_ctx,
element = ldb_msg_find_element(res->msgs[i], "dNSProperty");
if(element != NULL){
- props = talloc_zero_array(mem_ctx,
+ props = talloc_zero_array(tmp_ctx,
struct dnsp_DnsProperty,
element->num_values);
for (j = 0; j < element->num_values; j++ ) {