The branch, master has been updated via 002ef728bb0 torture: Fix build on freebsd, missing deps on cmdline via e267cea8179 samba-tool: dbcheck search DnsAdmins from wellknown container via 0db57db80a5 samba-tool: Provision search DnsAdmins from wellknown container via 151f432ca8c samba-tool: Demote computer to wellknown container via fee11c35586 samdb: Create computer in wellknown user container via 4602f4fc1b5 samdb: Create group in wellknown user container via 43ab8a4a1b4 samdb: Create user in wellknown user container via 5e559528b34 pytest: dcerpc/dnsserver: fix tombstone test via 97b9f45a764 pytest/dns_forwarder: remove unused function and imports via aa97974c0e4 pytest segfaults: add a couple more failing tests via 24493ccceb1 pytest samba-tool dns: avoid testing update of '.' PTR via de2b775e9ac pytest: dns_aging: do not insist on non-aging timestamp updates via ad6637afa5e pytest: dns_aging sibling test fails on windows via 7fbb8f8e957 pytest dns_aging: add windows_variation via ebfa200bfd9 pytest: dns_aging: fix two tests (bad arithmetic) via eac8d6b30b3 pytest dns_aging: add sibling tests via 61355d36cbf pytest dns_aging: add simple delete tests via 663a154e3e0 pytest: samba-tool dns: allow identical updates via b2453a0f5c2 pytest: samba-tool dns: allow valid updates via 6fb83b454cc pytest: dns_aging: test delete multiple records via b24b82336f2 pytest: dns_aging: test RPC updates of disparate types via 8d32cdf1849 python dns: dns_record_match() matches IPv6 semantically from 91f5b5f3d07 selftest: Remove -d10 from test startup
https://git.samba.org/?p=samba.git;a=shortlog;h=master - Log ----------------------------------------------------------------- commit 002ef728bb02819385c0a8c2ca1b216ed712d153 Author: Amitay Isaacs <ami...@gmail.com> Date: Wed Jun 16 12:58:27 2021 +1000 torture: Fix build on freebsd, missing deps on cmdline Missing dependency causes build failure on freebsd. [2928/3944] Compiling source4/torture/util_smb.c In file included from ../../source4/torture/util_smb.c:22: ../../lib/cmdline/cmdline.h:22:10: fatal error: 'popt.h' file not found ^~~~~~~~ 1 error generated. Signed-off-by: Amitay Isaacs <ami...@gmail.com> Reviewed-by: Andrew Bartlett <abart...@samba.org> Autobuild-User(master): Andrew Bartlett <abart...@samba.org> Autobuild-Date(master): Tue Jun 22 02:05:17 UTC 2021 on sn-devel-184 commit e267cea8179886995b46f0796c969a56a1becd3f Author: David Mulder <dmul...@suse.com> Date: Wed Aug 26 14:59:24 2020 -0600 samba-tool: dbcheck search DnsAdmins from wellknown container BUG: https://bugzilla.samba.org/show_bug.cgi?id=9143 Signed-off-by: David Mulder <dmul...@suse.com> Reviewed-by: Andrew Bartlett <abart...@samba.org> commit 0db57db80a59e2ecfb1c626f66a72987d9fedcef Author: David Mulder <dmul...@suse.com> Date: Wed Aug 26 14:33:13 2020 -0600 samba-tool: Provision search DnsAdmins from wellknown container BUG: https://bugzilla.samba.org/show_bug.cgi?id=9143 Signed-off-by: David Mulder <dmul...@suse.com> Reviewed-by: Andrew Bartlett <abart...@samba.org> commit 151f432ca8c173e7bad488dfbd507517908102da Author: David Mulder <dmul...@suse.com> Date: Wed Aug 26 10:06:21 2020 -0600 samba-tool: Demote computer to wellknown container BUG: https://bugzilla.samba.org/show_bug.cgi?id=9143 Signed-off-by: David Mulder <dmul...@suse.com> Reviewed-by: Andrew Bartlett <abart...@samba.org> commit fee11c35586adfa7e3ce79f03798732ffb870829 Author: David Mulder <dmul...@suse.com> Date: Wed Aug 26 08:15:07 2020 -0600 samdb: Create computer in wellknown user container BUG: https://bugzilla.samba.org/show_bug.cgi?id=9143 Signed-off-by: David Mulder <dmul...@suse.com> Reviewed-by: Andrew Bartlett <abart...@samba.org> commit 4602f4fc1b537e74fdee8d9f1a390a4ea1ba18d5 Author: David Mulder <dmul...@suse.com> Date: Tue Aug 25 14:16:30 2020 -0600 samdb: Create group in wellknown user container BUG: https://bugzilla.samba.org/show_bug.cgi?id=9143 Signed-off-by: David Mulder <dmul...@suse.com> Reviewed-by: Andrew Bartlett <abart...@samba.org> commit 43ab8a4a1b4152ae86e3dad23f10b40d4f61fb89 Author: David Mulder <dmul...@suse.com> Date: Tue Aug 25 12:44:02 2020 -0600 samdb: Create user in wellknown user container BUG: https://bugzilla.samba.org/show_bug.cgi?id=9143 Signed-off-by: David Mulder <dmul...@suse.com> Reviewed-by: Andrew Bartlett <abart...@samba.org> commit 5e559528b34e4b6b26fc708cdc0976e042d91eb3 Author: Douglas Bagnall <douglas.bagn...@catalyst.net.nz> Date: Fri Mar 26 16:37:52 2021 +1300 pytest: dcerpc/dnsserver: fix tombstone test It worked accidentally, like all our tombstone tests. Signed-off-by: Douglas Bagnall <douglas.bagn...@catalyst.net.nz> Reviewed-by: Andrew Bartlett <abart...@samba.org> commit 97b9f45a76434c5c00f467ec93f21a111bf35c0f Author: Douglas Bagnall <douglas.bagn...@catalyst.net.nz> Date: Wed May 19 01:12:49 2021 +0000 pytest/dns_forwarder: remove unused function and imports Signed-off-by: Douglas Bagnall <douglas.bagn...@catalyst.net.nz> Reviewed-by: Andrew Bartlett <abart...@samba.org> commit aa97974c0e42f5eb7c663b05407964ff816dae3b Author: Douglas Bagnall <douglas.bagn...@catalyst.net.nz> Date: Wed May 19 02:38:20 2021 +0000 pytest segfaults: add a couple more failing tests Signed-off-by: Douglas Bagnall <douglas.bagn...@catalyst.net.nz> Reviewed-by: Andrew Bartlett <abart...@samba.org> commit 24493ccceb16b2b1b05040659216535c89069c54 Author: Douglas Bagnall <douglas.bagn...@catalyst.net.nz> Date: Fri Jun 18 13:26:53 2021 +1200 pytest samba-tool dns: avoid testing update of '.' PTR This will fail for reasons that maybe we don't care about. Signed-off-by: Douglas Bagnall <douglas.bagn...@catalyst.net.nz> Reviewed-by: Andrew Bartlett <abart...@samba.org> commit de2b775e9ac3af87010a2d4b71220980f8e04f07 Author: Douglas Bagnall <douglas.bagn...@catalyst.net.nz> Date: Sun Jun 20 10:18:46 2021 +1200 pytest: dns_aging: do not insist on non-aging timestamp updates With Windows, when aging is off, the record timestamps are updated anyway, but the timestamp change is not replicated. We are not going to do it like that. With aging off, our records will keep their first timestamp. Signed-off-by: Douglas Bagnall <douglas.bagn...@catalyst.net.nz> Reviewed-by: Andrew Bartlett <abart...@samba.org> commit ad6637afa5e912f97dc21cb43f7893bd435c88b2 Author: Douglas Bagnall <douglas.bagn...@catalyst.net.nz> Date: Sat Jun 19 15:54:11 2021 +1200 pytest: dns_aging sibling test fails on windows Signed-off-by: Douglas Bagnall <douglas.bagn...@catalyst.net.nz> Reviewed-by: Andrew Bartlett <abart...@samba.org> commit 7fbb8f8e95786e7361e25ff647326b1f37911c2b Author: Douglas Bagnall <douglas.bagn...@catalyst.net.nz> Date: Sat Jun 19 15:43:29 2021 +1200 pytest dns_aging: add windows_variation We want to sometimes be able to say "we know Windows fails, it fails like this, it is OK", so that when we run the tests on Windows we know the failures are not unexpected. Signed-off-by: Douglas Bagnall <douglas.bagn...@catalyst.net.nz> Reviewed-by: Andrew Bartlett <abart...@samba.org> commit ebfa200bfd9e63588d59c97232ade2ca4d0ae996 Author: Douglas Bagnall <douglas.bagn...@catalyst.net.nz> Date: Sat Jun 19 14:52:47 2021 +1200 pytest: dns_aging: fix two tests (bad arithmetic) oops. Signed-off-by: Douglas Bagnall <douglas.bagn...@catalyst.net.nz> Reviewed-by: Andrew Bartlett <abart...@samba.org> commit eac8d6b30b3045021667bf4eda521eb64bea5075 Author: Douglas Bagnall <douglas.bagn...@catalyst.net.nz> Date: Sat Jun 19 14:18:05 2021 +1200 pytest dns_aging: add sibling tests Signed-off-by: Douglas Bagnall <douglas.bagn...@catalyst.net.nz> Reviewed-by: Andrew Bartlett <abart...@samba.org> commit 61355d36cbfc123ee61479257845eff4864d3d0b Author: Douglas Bagnall <douglas.bagn...@catalyst.net.nz> Date: Sat Jun 19 13:35:54 2021 +1200 pytest dns_aging: add simple delete tests When records are added and deleted and added again, Windows gets all kinds of ideas about what should happen, and many of our tests explore that. Here we focus the simplest case with a variety of timestamp combinations. Signed-off-by: Douglas Bagnall <douglas.bagn...@catalyst.net.nz> Reviewed-by: Andrew Bartlett <abart...@samba.org> commit 663a154e3e0e25b50772063cba9a99ac7fa338c4 Author: Douglas Bagnall <douglas.bagn...@catalyst.net.nz> Date: Thu Jun 17 22:43:39 2021 +1200 pytest: samba-tool dns: allow identical updates We know this should work from tests of the underlying RPC calls on Windows (see dns_aging). Signed-off-by: Douglas Bagnall <douglas.bagn...@catalyst.net.nz> Reviewed-by: Andrew Bartlett <abart...@samba.org> commit b2453a0f5c2fbb0c002ebb5fd274ca57ff66ee9f Author: Douglas Bagnall <douglas.bagn...@catalyst.net.nz> Date: Thu Jun 17 22:22:46 2021 +1200 pytest: samba-tool dns: allow valid updates Without this patch we will get errors like this when in-place RPC updates start to work: AssertionError: unexpectedly None : Successfully updated record '192.168.0.1' to '192.168.0.1', even though the latter is of type 'A' where 'A' was expected. That's because we have always rejected updates that try to modify an existing record. We shouldn't. Signed-off-by: Douglas Bagnall <douglas.bagn...@catalyst.net.nz> Reviewed-by: Andrew Bartlett <abart...@samba.org> commit 6fb83b454ccac7e01ab0f5d48c1c5b6a66bea173 Author: Douglas Bagnall <douglas.bagn...@catalyst.net.nz> Date: Fri Jun 18 18:32:22 2021 +1200 pytest: dns_aging: test delete multiple records Using dns.DNS_QCLASS_ANY we can delete all the records of a certain type. What happens to other timestamps? The answer should be nothing. Signed-off-by: Douglas Bagnall <douglas.bagn...@catalyst.net.nz> Reviewed-by: Andrew Bartlett <abart...@samba.org> commit b24b82336f22570314c8240872213109d80ef641 Author: Douglas Bagnall <douglas.bagn...@catalyst.net.nz> Date: Thu Jun 17 23:10:50 2021 +1200 pytest: dns_aging: test RPC updates of disparate types Can a TXT record be replaced by an A record in an RPC update? According to Windows, yes. Signed-off-by: Douglas Bagnall <douglas.bagn...@catalyst.net.nz> Reviewed-by: Andrew Bartlett <abart...@samba.org> commit 8d32cdf18490be254e3d2c780af1a234ad86cc39 Author: Douglas Bagnall <douglas.bagn...@catalyst.net.nz> Date: Fri Jun 18 11:30:09 2021 +1200 python dns: dns_record_match() matches IPv6 semantically Signed-off-by: Douglas Bagnall <douglas.bagn...@catalyst.net.nz> Reviewed-by: Andrew Bartlett <abart...@samba.org> ----------------------------------------------------------------------- Summary of changes: python/samba/dbchecker.py | 5 +- python/samba/dnsserver.py | 9 +- python/samba/netcmd/domain.py | 5 +- python/samba/provision/sambadns.py | 8 +- python/samba/samdb.py | 17 +- python/samba/tests/dcerpc/dnsserver.py | 92 ++++++- python/samba/tests/dns_aging.py | 426 +++++++++++++++++++++++++++-- python/samba/tests/dns_forwarder.py | 14 +- python/samba/tests/samba_tool/dnscmd.py | 44 +-- python/samba/tests/segfault.py | 20 +- selftest/knownfail.d/dns-aging | 47 +++- selftest/knownfail.d/dnscmd | 2 + selftest/knownfail.d/python-segfaults | 2 + source4/torture/libnetapi/wscript_build | 2 +- source4/torture/libsmbclient/wscript_build | 2 +- source4/torture/smb2/wscript_build | 2 +- source4/torture/wscript_build | 6 +- 17 files changed, 621 insertions(+), 82 deletions(-) create mode 100644 selftest/knownfail.d/dnscmd Changeset truncated at 500 lines: diff --git a/python/samba/dbchecker.py b/python/samba/dbchecker.py index d133b2aa55e..eb6dfe97cb0 100644 --- a/python/samba/dbchecker.py +++ b/python/samba/dbchecker.py @@ -124,7 +124,10 @@ class dbcheck(object): self.link_id_cache = {} self.name_map = {} try: - res = samdb.search(base="CN=DnsAdmins,CN=Users,%s" % samdb.domain_dn(), scope=ldb.SCOPE_BASE, + base_dn = "CN=DnsAdmins,%s" % samdb.get_wellknown_dn( + samdb.get_default_basedn(), + dsdb.DS_GUID_USERS_CONTAINER) + res = samdb.search(base=base_dn, scope=ldb.SCOPE_BASE, attrs=["objectSid"]) dnsadmins_sid = ndr_unpack(security.dom_sid, res[0]["objectSid"][0]) self.name_map['DnsAdmins'] = str(dnsadmins_sid) diff --git a/python/samba/dnsserver.py b/python/samba/dnsserver.py index 9bcab7fb023..8453b442b50 100644 --- a/python/samba/dnsserver.py +++ b/python/samba/dnsserver.py @@ -17,6 +17,7 @@ # import shlex +import socket from samba.dcerpc import dnsserver, dnsp # Note: these are not quite the same as similar looking classes in @@ -309,6 +310,12 @@ def dns_name_equal(n1, n2): return n1.str.rstrip('.').lower() == n2.str.rstrip('.').lower() +def ipv6_normalise(addr): + """Convert an AAAA adresss into a canonical form.""" + packed = socket.inet_pton(socket.AF_INET6, addr) + return socket.inet_ntop(socket.AF_INET6, packed) + + def dns_record_match(dns_conn, server, zone, name, record_type, data): """Find a dns record that matches the specified data""" @@ -350,7 +357,7 @@ def dns_record_match(dns_conn, server, zone, name, record_type, data): if rec.data == urec.data: found = True elif record_type == dnsp.DNS_TYPE_AAAA: - if rec.data == urec.data: + if ipv6_normalise(rec.data) == ipv6_normalise(urec.data): found = True elif record_type == dnsp.DNS_TYPE_PTR: if dns_name_equal(rec.data, urec.data): diff --git a/python/samba/netcmd/domain.py b/python/samba/netcmd/domain.py index eccae4e2f75..de6f2b0ca1d 100644 --- a/python/samba/netcmd/domain.py +++ b/python/samba/netcmd/domain.py @@ -107,6 +107,7 @@ from samba.netcmd.domain_backup import cmd_domain_backup from samba.common import get_string from samba.trust_utils import CreateTrustedDomainRelax +from samba import dsdb string_version_to_constant = { "2008_R2": DS_DOMAIN_FUNCTION_2008_R2, @@ -902,7 +903,9 @@ class cmd_domain_demote(Command): i = 0 newrdn = str(rdn) - computer_dn = ldb.Dn(remote_samdb, "CN=Computers,%s" % str(remote_samdb.domain_dn())) + computer_dn = remote_samdb.get_wellknown_dn( + remote_samdb.get_default_basedn(), + dsdb.DS_GUID_COMPUTERS_CONTAINER) res = remote_samdb.search(base=computer_dn, expression=rdn, scope=ldb.SCOPE_ONELEVEL) if (len(res) != 0): diff --git a/python/samba/provision/sambadns.py b/python/samba/provision/sambadns.py index cd8df680749..6823f9ee56b 100644 --- a/python/samba/provision/sambadns.py +++ b/python/samba/provision/sambadns.py @@ -38,7 +38,8 @@ from samba.dsdb import ( DS_DOMAIN_FUNCTION_2003, DS_DOMAIN_FUNCTION_2008_R2, DS_DOMAIN_FUNCTION_2012_R2, - DS_DOMAIN_FUNCTION_2016 + DS_DOMAIN_FUNCTION_2016, + DS_GUID_USERS_CONTAINER ) from samba.descriptor import ( get_domain_descriptor, @@ -69,8 +70,9 @@ def get_domainguid(samdb, domaindn): def get_dnsadmins_sid(samdb, domaindn): - res = samdb.search(base="CN=DnsAdmins,CN=Users,%s" % domaindn, scope=ldb.SCOPE_BASE, - attrs=["objectSid"]) + base_dn = "CN=DnsAdmins,%s" % samdb.get_wellknown_dn(ldb.Dn(samdb, + domaindn), DS_GUID_USERS_CONTAINER) + res = samdb.search(base=base_dn, scope=ldb.SCOPE_BASE, attrs=["objectSid"]) dnsadmins_sid = ndr_unpack(security.dom_sid, res[0]["objectSid"][0]) return dnsadmins_sid diff --git a/python/samba/samdb.py b/python/samba/samdb.py index 424d6d2e88a..5e4f07bb679 100644 --- a/python/samba/samdb.py +++ b/python/samba/samdb.py @@ -228,7 +228,12 @@ lockoutTime: 0 :param sd: security descriptor of the object """ - group_dn = "CN=%s,%s,%s" % (groupname, (groupou or "CN=Users"), self.domain_dn()) + if groupou: + group_dn = "CN=%s,%s,%s" % (groupname, groupou, self.domain_dn()) + else: + group_dn = "CN=%s,%s" % (groupname, self.get_wellknown_dn( + self.get_default_basedn(), + dsdb.DS_GUID_USERS_CONTAINER)) # The new user record. Note the reliance on the SAMLDB module which # fills in the default information @@ -530,7 +535,12 @@ member: %s if useusernameascn is None and displayname != "": cn = displayname - user_dn = "CN=%s,%s,%s" % (cn, (userou or "CN=Users"), self.domain_dn()) + if userou: + user_dn = "CN=%s,%s,%s" % (cn, userou, self.domain_dn()) + else: + user_dn = "CN=%s,%s" % (cn, self.get_wellknown_dn( + self.get_default_basedn(), + dsdb.DS_GUID_USERS_CONTAINER)) dnsdomain = ldb.Dn(self, self.domain_dn()).canonical_str().replace("/", "") user_principal_name = "%s@%s" % (username, dnsdomain) @@ -763,7 +773,8 @@ member: %s raise Exception('Illegal computername "%s"' % computername) samaccountname = "%s$" % cn - computercontainer_dn = "CN=Computers,%s" % self.domain_dn() + computercontainer_dn = self.get_wellknown_dn(self.get_default_basedn(), + dsdb.DS_GUID_COMPUTERS_CONTAINER) if computerou: computercontainer_dn = self.normalize_dn_in_domain(computerou) diff --git a/python/samba/tests/dcerpc/dnsserver.py b/python/samba/tests/dcerpc/dnsserver.py index 340bb454324..a757e6f5a6d 100644 --- a/python/samba/tests/dcerpc/dnsserver.py +++ b/python/samba/tests/dcerpc/dnsserver.py @@ -330,24 +330,79 @@ class DnsserverTests(RpcInterfaceTestCase): self.delete_record(self.custom_zone, "testrecord", record_type_str, record_str) self.assert_num_records(self.custom_zone, "testrecord", record_type_str, 0) - def test_dns_tombstoned(self): - """ - See what happens when we set a record to be tombstoned. - """ + def test_dns_tombstoned_zero_timestamp(self): + """What happens with a zero EntombedTime tombstone?""" + # A zero-timestamp tombstone record has a special meaning for + # dns_common_replace(), which is the function exposed by + # samdb.dns_replace_by_dn(), and which is *NOT* a general + # purpose record replacement function but a specialised part + # of the dns update mechanism (for both DLZ and internal). + # + # In the earlier stages of handling updates, a record that + # needs to be deleted is set to be a tombstone with a zero + # timestamp. dns_common_replace() notices this specific + # marker, and if there are no other records, marks the node as + # tombstoned, in the process adding a "real" tombstone. + # + # If the tombstone has a non-zero timestamp, as you'll see in + # the next test, dns_common_replace will decide that the node + # is already tombstoned, and that no action needs to be taken. + # + # This test has worked historically, entirely by accident, as + # changing the wType appears to record_str = "192.168.50.50" - record_type_str = "A" - self.add_record(self.custom_zone, "testrecord", record_type_str, record_str) + self.add_record(self.custom_zone, "testrecord", 'A', record_str) dn, record = self.get_record_from_db(self.custom_zone, "testrecord") record.wType = dnsp.DNS_TYPE_TOMBSTONE - res = self.samdb.dns_replace_by_dn(dn, [record]) - if res is not None: - self.fail("Unable to update dns record to be tombstoned.") + record.data = 0 + self.samdb.dns_replace_by_dn(dn, [record]) + + # there should be no A record, and one TOMBSTONE record. + self.assert_num_records(self.custom_zone, "testrecord", 'A', 0) + # we can't make assertions about the tombstone count based on + # RPC calls, as ther are no tombstones in RPCs (there is + # "DNS_TYPE_ZERO" instead). Nor do tombstones show up if we + # use DNS_TYPE_ALL. + self.assert_num_records(self.custom_zone, "testrecord", 'ALL', 0) + + # But we can use LDAP: + records = self.ldap_get_records(self.custom_zone, "testrecord") + self.assertEqual(len(records), 1) + r = records[0] + self.assertEqual(r.wType, dnsp.DNS_TYPE_TOMBSTONE) + self.assertGreater(r.data, 1e17) # ~ October 1916 + + # this should fail, because no A records. + self.delete_record(self.custom_zone, "testrecord", 'A', record_str, + assertion=False) + + def test_dns_tombstoned_nonzero_timestamp(self): + """See what happens when we set a record to be tombstoned with an + EntombedTime timestamp. + """ + # Because this tombstone has a non-zero EntombedTime, + # dns_common_replace() will decide the node was already + # tombstoned and there is nothing to be done, leaving the A + # record where it was. - self.assert_num_records(self.custom_zone, "testrecord", record_type_str) - self.delete_record(self.custom_zone, "testrecord", record_type_str, record_str) - self.assert_num_records(self.custom_zone, "testrecord", record_type_str, 0) + record_str = "192.168.50.50" + self.add_record(self.custom_zone, "testrecord", 'A', record_str) + + dn, record = self.get_record_from_db(self.custom_zone, "testrecord") + record.wType = dnsp.DNS_TYPE_TOMBSTONE + record.data = 0x123456789A + self.samdb.dns_replace_by_dn(dn, [record]) + + # there should be the A record and no TOMBSTONE + self.assert_num_records(self.custom_zone, "testrecord", 'A', 1) + self.assert_num_records(self.custom_zone, "testrecord", 'TOMBSTONE', 0) + # this should succeed + self.delete_record(self.custom_zone, "testrecord", 'A', record_str, + assertion=True) + self.assert_num_records(self.custom_zone, "testrecord", 'TOMBSTONE', 0) + self.assert_num_records(self.custom_zone, "testrecord", 'A', 0) def get_record_from_db(self, zone_name, record_name): """ @@ -376,6 +431,19 @@ class DnsserverTests(RpcInterfaceTestCase): rec = ndr_unpack(dnsp.DnssrvRpcRecord, old_packed_record["dnsRecord"][0]) return (old_packed_record.dn, rec) + def ldap_get_records(self, zone, name): + zone_dn = (f"DC={zone},CN=MicrosoftDNS,DC=DomainDNSZones," + f"{self.samdb.get_default_basedn()}") + + expr = f"(&(objectClass=dnsNode)(name={name}))" + nodes = self.samdb.search(base=zone_dn, + scope=ldb.SCOPE_SUBTREE, + expression=expr, + attrs=["dnsRecord"]) + + records = nodes[0].get('dnsRecord') + return [ndr_unpack(dnsp.DnssrvRpcRecord, r) for r in records] + def test_duplicate_matching(self): """ Make sure that records which should be distinct from each other or duplicate diff --git a/python/samba/tests/dns_aging.py b/python/samba/tests/dns_aging.py index 3219e63151b..2e7369bed00 100644 --- a/python/samba/tests/dns_aging.py +++ b/python/samba/tests/dns_aging.py @@ -25,15 +25,15 @@ from samba.auth import system_session import ldb from samba import credentials from samba.dcerpc import dns, dnsp, dnsserver -from samba.dnsserver import TXTRecord -from samba.dnsserver import recbuf_from_string +from samba.dnsserver import TXTRecord, ARecord +from samba.dnsserver import recbuf_from_string, ipv6_normalise from samba.tests.subunitrun import SubunitOptions, TestProgram from samba import werror, WERRORError from samba.tests.dns_base import DNSTest import samba.getopt as options import optparse import time - +from samba.colour import c_RED, c_GREEN, c_DARK_YELLOW parser = optparse.OptionParser( "dns_aging.py <server name> <server ip> [options]") @@ -314,8 +314,11 @@ class TestDNSAging(DNSTest): r.rr_type = wtype r.rr_class = qclass r.ttl = ttl - r.length = 0xffff - r.rdata = data + if data is not None: + r.length = 0xffff + r.rdata = data + else: + r.length = 0 p.nscount = 1 p.nsrecs = [r] @@ -330,6 +333,12 @@ class TestDNSAging(DNSTest): wtype, qclass=dns.DNS_QCLASS_NONE) + def dns_delete_type(self, name, wtype): + return self.dns_update_non_text(name, + None, + wtype, + qclass=dns.DNS_QCLASS_ANY) + def dns_update_record(self, name, txt, ttl=900): if isinstance(txt, str): txt = [txt] @@ -1622,8 +1631,8 @@ class TestDNSAging(DNSTest): def test_aging_refresh(self): name, txt = 'agingtest', ['test txt'] - no_refresh = 100 - refresh = 80 + no_refresh = 200 + refresh = 160 self.set_zone_int_params(NoRefreshInterval=no_refresh, RefreshInterval=refresh, Aging=1) @@ -1631,22 +1640,22 @@ class TestDNSAging(DNSTest): start_time = before_mod.dwTimeStamp # go back 86 hours, which is in the no-refresh time (but - # wouldn't be if we had stuck to the default of 84). - self.ldap_modify_timestamps(name, -86) + # wouldn't be if we had stuck to the default of 168). + self.ldap_modify_timestamps(name, -170) rec = self.dns_update_record(name, txt) self.assert_timestamps_equal(rec.dwTimeStamp, - start_time - 86) + start_time - 170) - # back to -102 hours, into the refresh zone + # back to -202 hours, into the refresh zone # the update should reset the timestamp to now. - self.ldap_modify_timestamps(name, -16) + self.ldap_modify_timestamps(name, -32) rec = self.dns_update_record(name, txt) self.assert_soon_after(rec.dwTimeStamp, start_time) - # back to -182 hours, beyond the end of the refresh period. + # back to -362 hours, beyond the end of the refresh period. # Actually nothing changes at this time -- we can still # refresh, but the record is liable for scavenging. - self.ldap_modify_timestamps(name, -182) + self.ldap_modify_timestamps(name, -160) rec = self.dns_update_record(name, txt) self.assert_soon_after(rec.dwTimeStamp, start_time) @@ -1798,6 +1807,21 @@ class TestDNSAging(DNSTest): rec = self.dns_update_record('ldap', 'test') self.assertEqual(rec.dwSerial, 123) + def test_rpc_update_disparate_types(self): + """Can we use update to replace a TXT with an AAAA?""" + name = 'x' + old = TXTRecord("x") + new = ARecord("127.0.0.111") + self.rpc_replace(name, None, old) + recs = self.ldap_get_records(name) + self.assertEqual(len(recs), 1) + self.assertEqual(recs[0].wType, old.wType) + + self.rpc_replace(name, old, new) + recs = self.ldap_get_records(name) + self.assertEqual(len(recs), 1) + self.assertEqual(recs[0].wType, new.wType) + def test_add_update_many(self): # Samba fails often in this set, but we want to see how it # goes further down, so we print the problems and defer the @@ -2006,8 +2030,8 @@ class TestDNSAging(DNSTest): # the database directly. # just to be sure we have the right limits. - self.set_zone_int_params(NoRefreshInterval=84, - RefreshInterval=84, + self.set_zone_int_params(NoRefreshInterval=168, + RefreshInterval=168, Aging=1) ts1, ts2, ts3, ts4, ts5, ts6 = ('1', '2', '3', '4', '5', '6') @@ -2227,8 +2251,12 @@ class TestDNSAging(DNSTest): # check the B timestamp rec_b = self.get_unique_ip_record(name, B) - - self.assert_soon_after(rec_b, now) + if not aging: + self.windows_variation( + self.assert_soon_after, rec_b, now, + msg="windows updates non-aging, samba does not") + else: + self.assert_soon_after(rec_b, now) # rewind B rec_b = self.add_ip_record(name, B, dwTimeStamp=b_initial) @@ -2297,6 +2325,82 @@ class TestDNSAging(DNSTest): def test_AAAA_5_days_AAAA_6_days_no_aging(self): self._test_A_and_AAAA_records(IPv6_ADDR, IPv6_ADDR_2, 5, 6, aging=False) + def _test_multi_records_delete(self, aging): + # Batch deleting a type doesn't update other types timestamps. + self.set_aging(aging) + + name = 'aargh' + now = dsdb_dns.unix_to_dns_timestamp(int(time.time())) + + back_5_days = now - 5 * 24 + back_10_days = now - 10 * 24 + back_25_days = now - 25 * 24 + + ip4s = { + '1.1.1.1': now, + '2.2.2.2': back_5_days, + '3.3.3.3': back_10_days, + } + ip6s = { + '::1': now, + '::2': back_5_days, + '::3': back_25_days, + } + + txts = { + '1': now, + '2': back_5_days, + '3': back_25_days, + } + + # For windows, if we don't DNS update something, it won't know + # there's anything. + self.dns_update_record(name, '3') + + for k, v in ip4s.items(): + r = self.add_ip_record(name, k, wtype=dns.DNS_QTYPE_A, dwTimeStamp=v) + + for k, v in ip6s.items(): + r = self.add_ip_record(name, k, wtype=dns.DNS_QTYPE_AAAA, dwTimeStamp=v) + + for k, v in txts.items(): + r = self.ldap_update_record(name, k, dwTimeStamp=v) + + self.dns_delete_type(name, dnsp.DNS_TYPE_A) + + r = self.dns_query(name, dns.DNS_QTYPE_A) + self.assertEqual(r.ancount, 0) + + r = self.dns_query(name, dns.DNS_QTYPE_TXT) + self.assertEqual(r.ancount, 3) + rset = set(x.rdata.txt.str[0] for x in r.answers) + self.assertEqual(rset, set(txts)) + + r = self.dns_query(name, dns.DNS_QTYPE_AAAA) + self.assertEqual(r.ancount, 3) + rset = set(ipv6_normalise(x.rdata) for x in r.answers) + self.assertEqual(rset, set(ip6s)) + + recs = self.ldap_get_records(name) + self.assertEqual(len(recs), 6) + for r in recs: + if r.wType == dns.DNS_QTYPE_AAAA: + k = ipv6_normalise(r.data) + expected = ip6s[k] + elif r.wType == dns.DNS_QTYPE_TXT: + k = r.data.str[0] + expected = txts[k] + else: + self.fail(f"unexpected wType {r.wType}") + + self.assert_timestamps_equal(r.dwTimeStamp, expected) + + def test_multi_records_delete_aging(self): + self._test_multi_records_delete(True) + + def test_multi_records_delete_no_aging(self): + self._test_multi_records_delete(False) + def _test_dns_delete_times(self, n_days, aging=True): # In these tests, Windows replaces the records with # tombstones, while Samba just removes them. Both are @@ -2392,5 +2496,291 @@ class TestDNSAging(DNSTest): def test_dns_delete_times_static_no_aging(self): self._test_dns_delete_times(1e10, False) + def _test_dns_delete_simple(self, a_days, b_days, aging=True, touch=False): + # Here we show that with aging enabled, the timestamp of + # sibling records is *not* modified when a record is deleted. + # + # With aging disabled, it *is* modified, if the dns server has + # seen it updated before ldap set the time (that is, probably + # the dns server overwrites AD). This happens even if AD + # thinks the record is static. + name = 'test' + A = ['A'] + B = ['B'] + self.set_aging(aging) + now = dsdb_dns.unix_to_dns_timestamp(int(time.time())) + a_days_ago = max(now - a_days * 24, 0) + b_days_ago = max(now - b_days * 24, 0) + + if touch: -- Samba Shared Repository