[SCM] Samba Shared Repository - branch master updated

Andrew Bartlett abartlet at samba.org
Tue Jun 22 02:06:02 UTC 2021


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 <amitay at 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 <amitay at gmail.com>
    Reviewed-by: Andrew Bartlett <abartlet at samba.org>
    
    Autobuild-User(master): Andrew Bartlett <abartlet at samba.org>
    Autobuild-Date(master): Tue Jun 22 02:05:17 UTC 2021 on sn-devel-184

commit e267cea8179886995b46f0796c969a56a1becd3f
Author: David Mulder <dmulder at 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 <dmulder at suse.com>
    Reviewed-by: Andrew Bartlett <abartlet at samba.org>

commit 0db57db80a59e2ecfb1c626f66a72987d9fedcef
Author: David Mulder <dmulder at 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 <dmulder at suse.com>
    Reviewed-by: Andrew Bartlett <abartlet at samba.org>

commit 151f432ca8c173e7bad488dfbd507517908102da
Author: David Mulder <dmulder at 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 <dmulder at suse.com>
    Reviewed-by: Andrew Bartlett <abartlet at samba.org>

commit fee11c35586adfa7e3ce79f03798732ffb870829
Author: David Mulder <dmulder at 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 <dmulder at suse.com>
    Reviewed-by: Andrew Bartlett <abartlet at samba.org>

commit 4602f4fc1b537e74fdee8d9f1a390a4ea1ba18d5
Author: David Mulder <dmulder at 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 <dmulder at suse.com>
    Reviewed-by: Andrew Bartlett <abartlet at samba.org>

commit 43ab8a4a1b4152ae86e3dad23f10b40d4f61fb89
Author: David Mulder <dmulder at 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 <dmulder at suse.com>
    Reviewed-by: Andrew Bartlett <abartlet at samba.org>

commit 5e559528b34e4b6b26fc708cdc0976e042d91eb3
Author: Douglas Bagnall <douglas.bagnall at 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.bagnall at catalyst.net.nz>
    Reviewed-by: Andrew Bartlett <abartlet at samba.org>

commit 97b9f45a76434c5c00f467ec93f21a111bf35c0f
Author: Douglas Bagnall <douglas.bagnall at 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.bagnall at catalyst.net.nz>
    Reviewed-by: Andrew Bartlett <abartlet at samba.org>

commit aa97974c0e42f5eb7c663b05407964ff816dae3b
Author: Douglas Bagnall <douglas.bagnall at 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.bagnall at catalyst.net.nz>
    Reviewed-by: Andrew Bartlett <abartlet at samba.org>

commit 24493ccceb16b2b1b05040659216535c89069c54
Author: Douglas Bagnall <douglas.bagnall at 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.bagnall at catalyst.net.nz>
    Reviewed-by: Andrew Bartlett <abartlet at samba.org>

commit de2b775e9ac3af87010a2d4b71220980f8e04f07
Author: Douglas Bagnall <douglas.bagnall at 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.bagnall at catalyst.net.nz>
    Reviewed-by: Andrew Bartlett <abartlet at samba.org>

commit ad6637afa5e912f97dc21cb43f7893bd435c88b2
Author: Douglas Bagnall <douglas.bagnall at 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.bagnall at catalyst.net.nz>
    Reviewed-by: Andrew Bartlett <abartlet at samba.org>

commit 7fbb8f8e95786e7361e25ff647326b1f37911c2b
Author: Douglas Bagnall <douglas.bagnall at 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.bagnall at catalyst.net.nz>
    Reviewed-by: Andrew Bartlett <abartlet at samba.org>

commit ebfa200bfd9e63588d59c97232ade2ca4d0ae996
Author: Douglas Bagnall <douglas.bagnall at 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.bagnall at catalyst.net.nz>
    Reviewed-by: Andrew Bartlett <abartlet at samba.org>

commit eac8d6b30b3045021667bf4eda521eb64bea5075
Author: Douglas Bagnall <douglas.bagnall at catalyst.net.nz>
Date:   Sat Jun 19 14:18:05 2021 +1200

    pytest dns_aging: add sibling tests
    
    Signed-off-by: Douglas Bagnall <douglas.bagnall at catalyst.net.nz>
    Reviewed-by: Andrew Bartlett <abartlet at samba.org>

commit 61355d36cbfc123ee61479257845eff4864d3d0b
Author: Douglas Bagnall <douglas.bagnall at 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.bagnall at catalyst.net.nz>
    Reviewed-by: Andrew Bartlett <abartlet at samba.org>

commit 663a154e3e0e25b50772063cba9a99ac7fa338c4
Author: Douglas Bagnall <douglas.bagnall at 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.bagnall at catalyst.net.nz>
    Reviewed-by: Andrew Bartlett <abartlet at samba.org>

commit b2453a0f5c2fbb0c002ebb5fd274ca57ff66ee9f
Author: Douglas Bagnall <douglas.bagnall at 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.bagnall at catalyst.net.nz>
    Reviewed-by: Andrew Bartlett <abartlet at samba.org>

commit 6fb83b454ccac7e01ab0f5d48c1c5b6a66bea173
Author: Douglas Bagnall <douglas.bagnall at 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.bagnall at catalyst.net.nz>
    Reviewed-by: Andrew Bartlett <abartlet at samba.org>

commit b24b82336f22570314c8240872213109d80ef641
Author: Douglas Bagnall <douglas.bagnall at 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.bagnall at catalyst.net.nz>
    Reviewed-by: Andrew Bartlett <abartlet at samba.org>

commit 8d32cdf18490be254e3d2c780af1a234ad86cc39
Author: Douglas Bagnall <douglas.bagnall at 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.bagnall at catalyst.net.nz>
    Reviewed-by: Andrew Bartlett <abartlet at 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



More information about the samba-cvs mailing list