diff options
Diffstat (limited to 'python')
| -rw-r--r-- | python/samba/remove_dc.py | 98 |
1 files changed, 96 insertions, 2 deletions
diff --git a/python/samba/remove_dc.py b/python/samba/remove_dc.py index 41b9621e278..45e78881d4e 100644 --- a/python/samba/remove_dc.py +++ b/python/samba/remove_dc.py @@ -19,7 +19,9 @@ import ldb from ldb import LdbError from samba.ndr import ndr_unpack -from samba.dcerpc import misc +from samba.dcerpc import misc, dnsp +from samba.dcerpc.dnsp import DNS_TYPE_NS, DNS_TYPE_A, DNS_TYPE_AAAA, \ + DNS_TYPE_CNAME, DNS_TYPE_SRV, DNS_TYPE_PTR class DemoteException(Exception): """Base element for demote errors""" @@ -87,14 +89,106 @@ def remove_dns_references(samdb, dnsHostName): if len(zones) == 0: return + dnsHostNameUpper = dnsHostName.upper() + try: - rec = samdb.dns_lookup(dnsHostName) + primary_recs = samdb.dns_lookup(dnsHostName) except RuntimeError as (enum, estr): if enum == 0x000025F2: #WERR_DNS_ERROR_NAME_DOES_NOT_EXIST return raise DemoteException("lookup of %s failed: %s" % (dnsHostName, estr)) samdb.dns_replace(dnsHostName, []) + res = samdb.search("", + scope=ldb.SCOPE_BASE, attrs=["namingContexts"]) + assert len(res) == 1 + ncs = res[0]["namingContexts"] + + # Work out the set of names we will likely have an A record on by + # default. This is by default all the partitions of type + # domainDNS. By finding the canocial name of all the partitions, + # we find the likely candidates. We only remove the record if it + # maches the IP that was used by the dnsHostName. This avoids us + # needing to look a the dns_update_list file from in the demote + # script. + + def dns_name_from_dn(dn): + # The canonical string of DC=example,DC=com is + # example.com/ + # + # The canonical string of CN=Configuration,DC=example,DC=com + # is example.com/Configuration + return ldb.Dn(samdb, dn).canonical_str().split('/', 1)[0] + + # By using a set here, duplicates via (eg) example.com/Configuration + # do not matter, they become just example.com + a_names_to_remove_from \ + = set(dns_name_from_dn(dn) for dn in ncs) + + def a_rec_to_remove(dnsRecord): + if dnsRecord.wType == DNS_TYPE_A or dnsRecord.wType == DNS_TYPE_AAAA: + for rec in primary_recs: + if rec.wType == dnsRecord.wType and rec.data == dnsRecord.data: + return True + return False + + for a_name in a_names_to_remove_from: + try: + logger.debug("checking for DNS records to remove on %s" % a_name) + a_recs = samdb.dns_lookup(a_name) + except RuntimeError as (enum, estr): + if enum == 0x000025F2: #WERR_DNS_ERROR_NAME_DOES_NOT_EXIST + return + raise DemoteException("lookup of %s failed: %s" % (a_name, estr)) + + orig_num_recs = len(a_recs) + a_recs = [ r for r in a_recs if not a_rec_to_remove(r) ] + + if len(a_recs) != orig_num_recs: + print "updating %s keeping %d values, removing %s values" % \ + (a_name, len(a_recs), orig_num_recs - len(a_recs)) + samdb.dns_replace(a_name, a_recs) + + # Find all the CNAME, NS, PTR and SRV records that point at the + # name we are removing + + def to_remove(value): + dnsRecord = ndr_unpack(dnsp.DnssrvRpcRecord, value) + if dnsRecord.wType == DNS_TYPE_NS \ + or dnsRecord.wType == DNS_TYPE_CNAME \ + or dnsRecord.wType == DNS_TYPE_PTR: + if dnsRecord.data.upper() == dnsHostNameUpper: + return True + elif dnsRecord.wType == DNS_TYPE_SRV: + if dnsRecord.data.nameTarget.upper() == dnsHostNameUpper: + return True + return False + + for zone in zones: + print "checking %s" % zone.dn + records = samdb.search(base=zone.dn, scope=ldb.SCOPE_SUBTREE, + expression="(&(objectClass=dnsNode)" + "(!(dNSTombstoned=TRUE)))", + attrs=["dnsRecord"]) + for record in records: + try: + values = record["dnsRecord"] + except KeyError: + next + orig_num_values = len(values) + + # Remove references to dnsHostName in A, AAAA, NS, CNAME and SRV + values = [ ndr_unpack(dnsp.DnssrvRpcRecord, v) + for v in values if not to_remove(v) ] + + if len(values) != orig_num_values: + print "updating %s keeping %d values, removing %s values" \ + % (record.dn, len(values), orig_num_values - len(values)) + + # This requires the values to be unpacked, so this + # has been done in the list comprehension above + samdb.dns_replace_by_dn(record.dn, values) + def offline_remove_server(samdb, server_dn, remove_computer_obj=False, remove_server_obj=False, |
