PEP8: fix E231: missing whitespace after ','
[samba.git] / python / samba / netcmd / dns.py
index 1e8281223861a8cb3ee89252df0138f5a021cf4a..26c7551bc22480499eba5582e6b66a04d7919914 100644 (file)
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 #
+import logging
 
 import samba.getopt as options
+from samba import WERRORError
+from samba import werror
 from struct import pack
 from socket import inet_ntoa
+from socket import inet_ntop
+from socket import AF_INET
+from socket import AF_INET6
 import shlex
 
+from samba import remove_dc
+from samba.samdb import SamDB
+from samba.auth import system_session
+
 from samba.netcmd import (
     Command,
     CommandError,
     Option,
     SuperCommand,
-    )
+)
 from samba.dcerpc import dnsp, dnsserver
 
+from samba.dnsserver import ARecord, AAAARecord, PTRRecord, CNameRecord, NSRecord, MXRecord, SOARecord, SRVRecord, TXTRecord
 
 def dns_connect(server, lp, creds):
     if server.lower() == 'localhost':
         server = '127.0.0.1'
     binding_str = "ncacn_ip_tcp:%s[sign]" % server
-    dns_conn = dnsserver.dnsserver(binding_str, lp, creds)
+    try:
+        dns_conn = dnsserver.dnsserver(binding_str, lp, creds)
+    except RuntimeError as e:
+        raise CommandError('Connecting to DNS RPC server %s failed with %s' % (server, e))
+
     return dns_conn
 
 
@@ -70,50 +85,50 @@ def bitmap_string(module, bitmap_defs, value):
 
 
 def boot_method_string(boot_method):
-    enum_defs = [ 'DNS_BOOT_METHOD_UNINITIALIZED', 'DNS_BOOT_METHOD_FILE',
-                    'DNS_BOOT_METHOD_REGISTRY', 'DNS_BOOT_METHOD_DIRECTORY' ]
+    enum_defs = ['DNS_BOOT_METHOD_UNINITIALIZED', 'DNS_BOOT_METHOD_FILE',
+                  'DNS_BOOT_METHOD_REGISTRY', 'DNS_BOOT_METHOD_DIRECTORY']
     return enum_string(dnsserver, enum_defs, boot_method)
 
 
 def name_check_flag_string(check_flag):
-    enum_defs = [ 'DNS_ALLOW_RFC_NAMES_ONLY', 'DNS_ALLOW_NONRFC_NAMES',
-                    'DNS_ALLOW_MULTIBYTE_NAMES', 'DNS_ALLOW_ALL_NAMES' ]
+    enum_defs = ['DNS_ALLOW_RFC_NAMES_ONLY', 'DNS_ALLOW_NONRFC_NAMES',
+                  'DNS_ALLOW_MULTIBYTE_NAMES', 'DNS_ALLOW_ALL_NAMES']
     return enum_string(dnsserver, enum_defs, check_flag)
 
 
 def zone_type_string(zone_type):
-    enum_defs = [ 'DNS_ZONE_TYPE_CACHE', 'DNS_ZONE_TYPE_PRIMARY',
-                    'DNS_ZONE_TYPE_SECONDARY', 'DNS_ZONE_TYPE_STUB',
-                    'DNS_ZONE_TYPE_FORWARDER', 'DNS_ZONE_TYPE_SECONDARY_CACHE' ]
+    enum_defs = ['DNS_ZONE_TYPE_CACHE', 'DNS_ZONE_TYPE_PRIMARY',
+                  'DNS_ZONE_TYPE_SECONDARY', 'DNS_ZONE_TYPE_STUB',
+                  'DNS_ZONE_TYPE_FORWARDER', 'DNS_ZONE_TYPE_SECONDARY_CACHE']
     return enum_string(dnsp, enum_defs, zone_type)
 
 
 def zone_update_string(zone_update):
-    enum_defs = [ 'DNS_ZONE_UPDATE_OFF', 'DNS_ZONE_UPDATE_SECURE',
-                    'DNS_ZONE_UPDATE_SECURE' ]
+    enum_defs = ['DNS_ZONE_UPDATE_OFF', 'DNS_ZONE_UPDATE_UNSECURE',
+                  'DNS_ZONE_UPDATE_SECURE']
     return enum_string(dnsp, enum_defs, zone_update)
 
 
 def zone_secondary_security_string(security):
-    enum_defs = [ 'DNS_ZONE_SECSECURE_NO_SECURITY', 'DNS_ZONE_SECSECURE_NS_ONLY',
-                    'DNS_ZONE_SECSECURE_LIST_ONLY', 'DNS_ZONE_SECSECURE_NO_XFER' ]
+    enum_defs = ['DNS_ZONE_SECSECURE_NO_SECURITY', 'DNS_ZONE_SECSECURE_NS_ONLY',
+                  'DNS_ZONE_SECSECURE_LIST_ONLY', 'DNS_ZONE_SECSECURE_NO_XFER']
     return enum_string(dnsserver, enum_defs, security)
 
 
 def zone_notify_level_string(notify_level):
-    enum_defs = [ 'DNS_ZONE_NOTIFY_OFF', 'DNS_ZONE_NOTIFY_ALL_SECONDARIES',
-                    'DNS_ZONE_NOTIFY_LIST_ONLY' ]
+    enum_defs = ['DNS_ZONE_NOTIFY_OFF', 'DNS_ZONE_NOTIFY_ALL_SECONDARIES',
+                  'DNS_ZONE_NOTIFY_LIST_ONLY']
     return enum_string(dnsserver, enum_defs, notify_level)
 
 
 def dp_flags_string(dp_flags):
-    bitmap_defs = [ 'DNS_DP_AUTOCREATED', 'DNS_DP_LEGACY', 'DNS_DP_DOMAIN_DEFAULT',
-                'DNS_DP_FOREST_DEFAULT', 'DNS_DP_ENLISTED', 'DNS_DP_DELETED' ]
+    bitmap_defs = ['DNS_DP_AUTOCREATED', 'DNS_DP_LEGACY', 'DNS_DP_DOMAIN_DEFAULT',
+                    'DNS_DP_FOREST_DEFAULT', 'DNS_DP_ENLISTED', 'DNS_DP_DELETED']
     return bitmap_string(dnsserver, bitmap_defs, dp_flags)
 
 
 def zone_flags_string(flags):
-    bitmap_defs = [ 'DNS_RPC_ZONE_PAUSED', 'DNS_RPC_ZONE_SHUTDOWN',
+    bitmap_defs = ['DNS_RPC_ZONE_PAUSED', 'DNS_RPC_ZONE_SHUTDOWN',
                     'DNS_RPC_ZONE_REVERSE', 'DNS_RPC_ZONE_AUTOCREATED',
                     'DNS_RPC_ZONE_DSINTEGRATED', 'DNS_RPC_ZONE_AGING',
                     'DNS_RPC_ZONE_UPDATE_UNSECURE', 'DNS_RPC_ZONE_UPDATE_SECURE',
@@ -125,8 +140,8 @@ def ip4_array_string(array):
     ret = []
     if not array:
         return ret
-    for i in xrange(array.AddrCount):
-        addr = '%s' % inet_ntoa(pack('i', array.AddrArray[i]))
+    for i in range(array.AddrCount):
+        addr = inet_ntop(AF_INET, pack('I', array.AddrArray[i]))
         ret.append(addr)
     return ret
 
@@ -135,13 +150,13 @@ def dns_addr_array_string(array):
     ret = []
     if not array:
         return ret
-    for i in xrange(array.AddrCount):
+    for i in range(array.AddrCount):
         if array.AddrArray[i].MaxSa[0] == 0x02:
-            addr = '%d.%d.%d.%d (%d)' % \
-                tuple(array.AddrArray[i].MaxSa[4:8] + [array.AddrArray[i].MaxSa[3]])
+            x = "".join([chr(b) for b in array.AddrArray[i].MaxSa])[4:8]
+            addr = inet_ntop(AF_INET, x)
         elif array.AddrArray[i].MaxSa[0] == 0x17:
-            addr = '%x%x:%x%x:%x%x:%x%x:%x%x:%x%x:%x%x:%x%x (%d)' % \
-                tuple(array.AddrArray[i].MaxSa[4:20] + [array.AddrArray[i].MaxSa[3]])
+            x = "".join([chr(b) for b in array.AddrArray[i].MaxSa])[8:24]
+            addr = inet_ntop(AF_INET6, x)
         else:
             addr = 'UNKNOWN'
         ret.append(addr)
@@ -199,18 +214,18 @@ def print_serverinfo(outf, typeid, serverinfo):
 
     if typeid != dnsserver.DNSSRV_TYPEID_SERVER_INFO:
         outf.write('  aipServerAddrs              : %s\n' %
-                    ip4_array_string(serverinfo.aipServerAddrs))
+                   ip4_array_string(serverinfo.aipServerAddrs))
         outf.write('  aipListenAddrs              : %s\n' %
-                    ip4_array_string(serverinfo.aipListenAddrs))
+                   ip4_array_string(serverinfo.aipListenAddrs))
         outf.write('  aipForwarders               : %s\n' %
-                    ip4_array_string(serverinfo.aipForwarders))
+                   ip4_array_string(serverinfo.aipForwarders))
     else:
         outf.write('  aipServerAddrs              : %s\n' %
-                    dns_addr_array_string(serverinfo.aipServerAddrs))
+                   dns_addr_array_string(serverinfo.aipServerAddrs))
         outf.write('  aipListenAddrs              : %s\n' %
-                    dns_addr_array_string(serverinfo.aipListenAddrs))
+                   dns_addr_array_string(serverinfo.aipListenAddrs))
         outf.write('  aipForwarders               : %s\n' %
-                    dns_addr_array_string(serverinfo.aipForwarders))
+                   dns_addr_array_string(serverinfo.aipForwarders))
 
     outf.write('  dwLogLevel                  : %d\n' % serverinfo.dwLogLevel)
     outf.write('  dwDebugLevel                : %d\n' % serverinfo.dwDebugLevel)
@@ -272,22 +287,22 @@ def print_zoneinfo(outf, typeid, zoneinfo):
     outf.write('  pszDataFile                 : %s\n' % zoneinfo.pszDataFile)
     if typeid != dnsserver.DNSSRV_TYPEID_ZONE_INFO:
         outf.write('  aipMasters                  : %s\n' %
-                    ip4_array_string(zoneinfo.aipMasters))
+                   ip4_array_string(zoneinfo.aipMasters))
     else:
         outf.write('  aipMasters                  : %s\n' %
-                    dns_addr_array_string(zoneinfo.aipMasters))
+                   dns_addr_array_string(zoneinfo.aipMasters))
     outf.write('  fSecureSecondaries          : %s\n' % zone_secondary_security_string(zoneinfo.fSecureSecondaries))
     outf.write('  fNotifyLevel                : %s\n' % zone_notify_level_string(zoneinfo.fNotifyLevel))
     if typeid != dnsserver.DNSSRV_TYPEID_ZONE_INFO:
         outf.write('  aipSecondaries              : %s\n' %
-                    ip4_array_string(zoneinfo.aipSecondaries))
+                   ip4_array_string(zoneinfo.aipSecondaries))
         outf.write('  aipNotify                   : %s\n' %
-                    ip4_array_string(zoneinfo.aipNotify))
+                   ip4_array_string(zoneinfo.aipNotify))
     else:
         outf.write('  aipSecondaries              : %s\n' %
-                    dns_addr_array_string(zoneinfo.aipSecondaries))
+                   dns_addr_array_string(zoneinfo.aipSecondaries))
         outf.write('  aipNotify                   : %s\n' %
-                    dns_addr_array_string(zoneinfo.aipNotify))
+                   dns_addr_array_string(zoneinfo.aipNotify))
     outf.write('  fUseWins                    : %s\n' % bool_string(zoneinfo.fUseWins))
     outf.write('  fUseNbstat                  : %s\n' % bool_string(zoneinfo.fUseNbstat))
     outf.write('  fAging                      : %s\n' % bool_string(zoneinfo.fAging))
@@ -296,10 +311,10 @@ def print_zoneinfo(outf, typeid, zoneinfo):
     outf.write('  dwAvailForScavengeTime      : %d\n' % zoneinfo.dwAvailForScavengeTime)
     if typeid != dnsserver.DNSSRV_TYPEID_ZONE_INFO:
         outf.write('  aipScavengeServers          : %s\n' %
-                    ip4_array_string(zoneinfo.aipScavengeServers))
+                   ip4_array_string(zoneinfo.aipScavengeServers))
     else:
         outf.write('  aipScavengeServers          : %s\n' %
-                    dns_addr_array_string(zoneinfo.aipScavengeServers))
+                   dns_addr_array_string(zoneinfo.aipScavengeServers))
 
     if typeid != dnsserver.DNSSRV_TYPEID_ZONE_INFO_W2K:
         outf.write('  dwRpcStructureVersion       : 0x%x\n' % zoneinfo.dwRpcStructureVersion)
@@ -307,10 +322,10 @@ def print_zoneinfo(outf, typeid, zoneinfo):
         outf.write('  fForwarderSlave             : %d\n' % zoneinfo.fForwarderSlave)
         if typeid != dnsserver.DNSSRV_TYPEID_ZONE_INFO:
             outf.write('  aipLocalMasters             : %s\n' %
-                        ip4_array_string(zoneinfo.aipLocalMasters))
+                       ip4_array_string(zoneinfo.aipLocalMasters))
         else:
             outf.write('  aipLocalMasters             : %s\n' %
-                        dns_addr_array_string(zoneinfo.aipLocalMasters))
+                       dns_addr_array_string(zoneinfo.aipLocalMasters))
         outf.write('  dwDpFlags                   : %s\n' % dp_flags_string(zoneinfo.dwDpFlags))
         outf.write('  pszDpFqdn                   : %s\n' % zoneinfo.pszDpFqdn)
         outf.write('  pwszZoneDn                  : %s\n' % zoneinfo.pwszZoneDn)
@@ -387,167 +402,6 @@ def print_dnsrecords(outf, records):
                 print_dns_record(outf, dns_rec)
 
 
-#
-# Always create a copy of strings when creating DNS_RPC_RECORDs
-# to overcome the bug in pidl generated python bindings.
-#
-
-class ARecord(dnsserver.DNS_RPC_RECORD):
-    def __init__(self, ip_addr, serial=1, ttl=900, rank=dnsp.DNS_RANK_ZONE,
-                    node_flag=0):
-        super(ARecord, self).__init__()
-        self.wType = dnsp.DNS_TYPE_A
-        self.dwFlags = rank | node_flag
-        self.dwSerial = serial
-        self.dwTtlSeconds = ttl
-        self._ip_addr = ip_addr[:]
-        self.data = self._ip_addr
-
-
-class AAAARecord(dnsserver.DNS_RPC_RECORD):
-
-    def __init__(self, ip6_addr, serial=1, ttl=900, rank=dnsp.DNS_RANK_ZONE,
-                    node_flag=0):
-        super(AAAARecord, self).__init__()
-        self.wType = dnsp.DNS_TYPE_AAAA
-        self.dwFlags = rank | node_flag
-        self.dwSerial = serial
-        self.dwTtlSeconds = ttl
-        self._ip6_addr = ip6_addr[:]
-        self.data = self._ip6_addr
-
-
-class PTRRecord(dnsserver.DNS_RPC_RECORD):
-
-    def __init__(self, ptr, serial=1, ttl=900, rank=dnsp.DNS_RANK_ZONE,
-                 node_flag=0):
-        super(PTRRecord, self).__init__()
-        self.wType = dnsp.DNS_TYPE_PTR
-        self.dwFlags = rank | node_flag
-        self.dwSerial = serial
-        self.dwTtlSeconds = ttl
-        self._ptr = ptr[:]
-        ptr_name = dnsserver.DNS_RPC_NAME()
-        ptr_name.str = self._ptr
-        ptr_name.len = len(ptr)
-        self.data = ptr_name
-
-
-class CNameRecord(dnsserver.DNS_RPC_RECORD):
-
-    def __init__(self, cname, serial=1, ttl=900, rank=dnsp.DNS_RANK_ZONE,
-                    node_flag=0):
-        super(CNameRecord, self).__init__()
-        self.wType = dnsp.DNS_TYPE_CNAME
-        self.dwFlags = rank | node_flag
-        self.dwSerial = serial
-        self.dwTtlSeconds = ttl
-        self._cname = cname[:]
-        cname_name = dnsserver.DNS_RPC_NAME()
-        cname_name.str = self._cname
-        cname_name.len = len(cname)
-        self.data = cname_name
-
-
-class NSRecord(dnsserver.DNS_RPC_RECORD):
-
-    def __init__(self, dns_server, serial=1, ttl=900, rank=dnsp.DNS_RANK_ZONE,
-                    node_flag=0):
-        super(NSRecord, self).__init__()
-        self.wType = dnsp.DNS_TYPE_NS
-        self.dwFlags = rank | node_flag
-        self.dwSerial = serial
-        self.dwTtlSeconds = ttl
-        self._dns_server = dns_server[:]
-        ns = dnsserver.DNS_RPC_NAME()
-        ns.str = self._dns_server
-        ns.len = len(dns_server)
-        self.data = ns
-
-
-class MXRecord(dnsserver.DNS_RPC_RECORD):
-
-    def __init__(self, mail_server, preference, serial=1, ttl=900,
-                 rank=dnsp.DNS_RANK_ZONE, node_flag=0):
-        super(MXRecord, self).__init__()
-        self.wType = dnsp.DNS_TYPE_MX
-        self.dwFlags = rank | node_flag
-        self.dwSerial = serial
-        self.dwTtlSeconds = ttl
-        self._mail_server = mail_server[:]
-        mx = dnsserver.DNS_RPC_RECORD_NAME_PREFERENCE()
-        mx.wPreference = preference
-        mx.nameExchange.str = self._mail_server
-        mx.nameExchange.len = len(mail_server)
-        self.data = mx
-
-
-class SOARecord(dnsserver.DNS_RPC_RECORD):
-
-    def __init__(self, mname, rname, serial=1, refresh=900, retry=600,
-                 expire=86400, minimum=3600, ttl=3600, rank=dnsp.DNS_RANK_ZONE,
-                 node_flag=dnsp.DNS_RPC_FLAG_AUTH_ZONE_ROOT):
-        super(SOARecord, self).__init__()
-        self.wType = dnsp.DNS_TYPE_SOA
-        self.dwFlags = rank | node_flag
-        self.dwSerial = serial
-        self.dwTtlSeconds = ttl
-        self._mname = mname[:]
-        self._rname = rname[:]
-        soa = dnsserver.DNS_RPC_RECORD_SOA()
-        soa.dwSerialNo = serial
-        soa.dwRefresh = refresh
-        soa.dwRetry = retry
-        soa.dwExpire = expire
-        soa.dwMinimumTtl = minimum
-        soa.NamePrimaryServer.str = self._mname
-        soa.NamePrimaryServer.len = len(mname)
-        soa.ZoneAdministratorEmail.str = self._rname
-        soa.ZoneAdministratorEmail.len = len(rname)
-        self.data = soa
-
-
-class SRVRecord(dnsserver.DNS_RPC_RECORD):
-
-    def __init__(self, target, port, priority=0, weight=100, serial=1, ttl=900,
-                rank=dnsp.DNS_RANK_ZONE, node_flag=0):
-        super(SRVRecord, self).__init__()
-        self.wType = dnsp.DNS_TYPE_SRV
-        self.dwFlags = rank | node_flag
-        self.dwSerial = serial
-        self.dwTtlSeconds = ttl
-        self._target = target[:]
-        srv = dnsserver.DNS_RPC_RECORD_SRV()
-        srv.wPriority = priority
-        srv.wWeight = weight
-        srv.wPort = port
-        srv.nameTarget.str = self._target
-        srv.nameTarget.len = len(target)
-        self.data = srv
-
-
-class TXTRecord(dnsserver.DNS_RPC_RECORD):
-
-    def __init__(self, slist, serial=1, ttl=900, rank=dnsp.DNS_RANK_ZONE,
-                node_flag=0):
-        super(TXTRecord, self).__init__()
-        self.wType = dnsp.DNS_TYPE_TXT
-        self.dwFlags = rank | node_flag
-        self.dwSerial = serial
-        self.dwTtlSeconds = ttl
-        self._slist = []
-        for s in slist:
-            self._slist.append(s[:])
-        names = []
-        for s in self._slist:
-            name = dnsserver.DNS_RPC_NAME()
-            name.str = s
-            name.len = len(s)
-            names.append(name)
-        txt = dnsserver.DNS_RPC_RECORD_STRING()
-        txt.count = len(slist)
-        txt.str = names
-        self.data = txt
 
 
 # Convert data into a dns record
@@ -615,13 +469,16 @@ def dns_record_match(dns_conn, server, zone, name, record_type, data):
         buflen, res = dns_conn.DnssrvEnumRecords2(
             dnsserver.DNS_CLIENT_VERSION_LONGHORN, 0, server, zone, name, None,
             record_type, select_flags, None, None)
-    except RuntimeError, e:
-        return None
+    except WERRORError as e:
+        if e.args[0] == werror.WERR_DNS_ERROR_NAME_DOES_NOT_EXIST:
+            # Either the zone doesn't exist, or there were no records.
+            # We can't differentiate the two.
+            return None
+        raise e
 
     if not res or res.count == 0:
         return None
 
-    rec_match = None
     for rec in res.rec[0].records:
         if rec.wType != record_type:
             continue
@@ -666,15 +523,14 @@ def dns_record_match(dns_conn, server, zone, name, record_type, data):
         elif record_type == dnsp.DNS_TYPE_TXT:
             if rec.data.count == urec.data.count:
                 found = True
-                for i in xrange(rec.data.count):
+                for i in range(rec.data.count):
                     found = found and \
                             (rec.data.str[i].str == urec.data.str[i].str)
 
         if found:
-            rec_match = rec
-            break
+            return rec
 
-    return rec_match
+    return None
 
 
 class cmd_serverinfo(Command):
@@ -682,7 +538,7 @@ class cmd_serverinfo(Command):
 
     synopsis = '%prog <server> [options]'
 
-    takes_args = [ 'server' ]
+    takes_args = ['server']
 
     takes_optiongroups = {
         "sambaopts": options.SambaOptions,
@@ -692,8 +548,8 @@ class cmd_serverinfo(Command):
 
     takes_options = [
         Option('--client-version', help='Client Version',
-                default='longhorn', metavar='w2k|dotnet|longhorn',
-                choices=['w2k','dotnet','longhorn'], dest='cli_ver'),
+               default='longhorn', metavar='w2k|dotnet|longhorn',
+               choices=['w2k', 'dotnet', 'longhorn'], dest='cli_ver'),
     ]
 
     def run(self, server, cli_ver, sambaopts=None, credopts=None,
@@ -714,7 +570,7 @@ class cmd_zoneinfo(Command):
 
     synopsis = '%prog <server> <zone> [options]'
 
-    takes_args = [ 'server', 'zone' ]
+    takes_args = ['server', 'zone']
 
     takes_optiongroups = {
         "sambaopts": options.SambaOptions,
@@ -724,8 +580,8 @@ class cmd_zoneinfo(Command):
 
     takes_options = [
         Option('--client-version', help='Client Version',
-                default='longhorn', metavar='w2k|dotnet|longhorn',
-                choices=['w2k','dotnet','longhorn'], dest='cli_ver'),
+               default='longhorn', metavar='w2k|dotnet|longhorn',
+               choices=['w2k', 'dotnet', 'longhorn'], dest='cli_ver'),
     ]
 
     def run(self, server, zone, cli_ver, sambaopts=None, credopts=None,
@@ -746,7 +602,7 @@ class cmd_zonelist(Command):
 
     synopsis = '%prog <server> [options]'
 
-    takes_args = [ 'server' ]
+    takes_args = ['server']
 
     takes_optiongroups = {
         "sambaopts": options.SambaOptions,
@@ -756,29 +612,29 @@ class cmd_zonelist(Command):
 
     takes_options = [
         Option('--client-version', help='Client Version',
-                default='longhorn', metavar='w2k|dotnet|longhorn',
-                choices=['w2k','dotnet','longhorn'], dest='cli_ver'),
+               default='longhorn', metavar='w2k|dotnet|longhorn',
+               choices=['w2k', 'dotnet', 'longhorn'], dest='cli_ver'),
         Option('--primary', help='List primary zones (default)',
-                action='store_true', dest='primary'),
+               action='store_true', dest='primary'),
         Option('--secondary', help='List secondary zones',
-                action='store_true', dest='secondary'),
+               action='store_true', dest='secondary'),
         Option('--cache', help='List cached zones',
-                action='store_true', dest='cache'),
+               action='store_true', dest='cache'),
         Option('--auto', help='List automatically created zones',
-                action='store_true', dest='auto'),
+               action='store_true', dest='auto'),
         Option('--forward', help='List forward zones',
-                action='store_true', dest='forward'),
+               action='store_true', dest='forward'),
         Option('--reverse', help='List reverse zones',
-                action='store_true', dest='reverse'),
+               action='store_true', dest='reverse'),
         Option('--ds', help='List directory integrated zones',
-                action='store_true', dest='ds'),
+               action='store_true', dest='ds'),
         Option('--non-ds', help='List non-directory zones',
-                action='store_true', dest='nonds')
+               action='store_true', dest='nonds')
     ]
 
     def run(self, server, cli_ver, primary=False, secondary=False, cache=False,
-                auto=False, forward=False, reverse=False, ds=False, nonds=False,
-                sambaopts=None, credopts=None, versionopts=None):
+            auto=False, forward=False, reverse=False, ds=False, nonds=False,
+            sambaopts=None, credopts=None, versionopts=None):
         request_filter = 0
 
         if primary:
@@ -808,10 +664,10 @@ class cmd_zonelist(Command):
         client_version = dns_client_version(cli_ver)
 
         typeid, res = dns_conn.DnssrvComplexOperation2(client_version,
-                                                        0, server, None,
-                                                        'EnumZones',
-                                                        dnsserver.DNSSRV_TYPEID_DWORD,
-                                                        request_filter)
+                                                       0, server, None,
+                                                       'EnumZones',
+                                                       dnsserver.DNSSRV_TYPEID_DWORD,
+                                                       request_filter)
 
         if client_version == dnsserver.DNS_CLIENT_VERSION_W2K:
             typeid = dnsserver.DNSSRV_TYPEID_ZONE_W2K
@@ -825,7 +681,7 @@ class cmd_zonecreate(Command):
 
     synopsis = '%prog <server> <zone> [options]'
 
-    takes_args = [ 'server', 'zone' ]
+    takes_args = ['server', 'zone']
 
     takes_optiongroups = {
         "sambaopts": options.SambaOptions,
@@ -835,8 +691,8 @@ class cmd_zonecreate(Command):
 
     takes_options = [
         Option('--client-version', help='Client Version',
-                default='longhorn', metavar='w2k|dotnet|longhorn',
-                choices=['w2k','dotnet','longhorn'], dest='cli_ver')
+               default='longhorn', metavar='w2k|dotnet|longhorn',
+               choices=['w2k', 'dotnet', 'longhorn'], dest='cli_ver')
     ]
 
     def run(self, server, zone, cli_ver, sambaopts=None, credopts=None,
@@ -854,28 +710,46 @@ class cmd_zonecreate(Command):
             zone_create_info = dnsserver.DNS_RPC_ZONE_CREATE_INFO_W2K()
             zone_create_info.pszZoneName = zone
             zone_create_info.dwZoneType = dnsp.DNS_ZONE_TYPE_PRIMARY
-            zone_create_info.fAllowUpdate = dnsp.DNS_ZONE_UPDATE_SECURE
             zone_create_info.fAging = 0
+            zone_create_info.fDsIntegrated = 1
+            zone_create_info.fLoadExisting = 1
         elif client_version == dnsserver.DNS_CLIENT_VERSION_DOTNET:
             typeid = dnsserver.DNSSRV_TYPEID_ZONE_CREATE_DOTNET
             zone_create_info = dnsserver.DNS_RPC_ZONE_CREATE_INFO_DOTNET()
             zone_create_info.pszZoneName = zone
             zone_create_info.dwZoneType = dnsp.DNS_ZONE_TYPE_PRIMARY
-            zone_create_info.fAllowUpdate = dnsp.DNS_ZONE_UPDATE_SECURE
             zone_create_info.fAging = 0
+            zone_create_info.fDsIntegrated = 1
+            zone_create_info.fLoadExisting = 1
             zone_create_info.dwDpFlags = dnsserver.DNS_DP_DOMAIN_DEFAULT
         else:
             typeid = dnsserver.DNSSRV_TYPEID_ZONE_CREATE
             zone_create_info = dnsserver.DNS_RPC_ZONE_CREATE_INFO_LONGHORN()
             zone_create_info.pszZoneName = zone
             zone_create_info.dwZoneType = dnsp.DNS_ZONE_TYPE_PRIMARY
-            zone_create_info.fAllowUpdate = dnsp.DNS_ZONE_UPDATE_SECURE
             zone_create_info.fAging = 0
+            zone_create_info.fDsIntegrated = 1
+            zone_create_info.fLoadExisting = 1
             zone_create_info.dwDpFlags = dnsserver.DNS_DP_DOMAIN_DEFAULT
 
         res = dns_conn.DnssrvOperation2(client_version, 0, server, None,
                                         0, 'ZoneCreate', typeid,
                                         zone_create_info)
+
+        typeid = dnsserver.DNSSRV_TYPEID_NAME_AND_PARAM
+        name_and_param = dnsserver.DNS_RPC_NAME_AND_PARAM()
+        name_and_param.pszNodeName = 'AllowUpdate'
+        name_and_param.dwParam = dnsp.DNS_ZONE_UPDATE_SECURE
+
+        try:
+            res = dns_conn.DnssrvOperation2(client_version, 0, server, zone,
+                                            0, 'ResetDwordProperty', typeid,
+                                            name_and_param)
+        except WERRORError as e:
+            if e.args[0] == werror.WERR_DNS_ERROR_ZONE_ALREADY_EXISTS:
+                self.outf.write('Zone already exists.')
+            raise e
+
         self.outf.write('Zone %s created successfully\n' % zone)
 
 
@@ -884,7 +758,7 @@ class cmd_zonedelete(Command):
 
     synopsis = '%prog <server> <zone> [options]'
 
-    takes_args = [ 'server', 'zone' ]
+    takes_args = ['server', 'zone']
 
     takes_optiongroups = {
         "sambaopts": options.SambaOptions,
@@ -900,11 +774,17 @@ class cmd_zonedelete(Command):
         dns_conn = dns_connect(server, self.lp, self.creds)
 
         zone = zone.lower()
-        res = dns_conn.DnssrvOperation2(dnsserver.DNS_CLIENT_VERSION_LONGHORN,
-                                        0, server, zone, 0, 'DeleteZoneFromDs',
-                                        dnsserver.DNSSRV_TYPEID_NULL,
-                                        None)
-        self.outf.write('Zone %s delete successfully\n' % zone)
+        try:
+            res = dns_conn.DnssrvOperation2(dnsserver.DNS_CLIENT_VERSION_LONGHORN,
+                                            0, server, zone, 0, 'DeleteZoneFromDs',
+                                            dnsserver.DNSSRV_TYPEID_NULL,
+                                            None)
+        except WERRORError as e:
+            if e.args[0] == werror.WERR_DNS_ERROR_ZONE_DOES_NOT_EXIST:
+                self.outf.write('Zone does not exist and so could not be deleted.')
+            raise e
+
+        self.outf.write('Zone %s deleted successfully\n' % zone)
 
 
 class cmd_query(Command):
@@ -912,7 +792,7 @@ class cmd_query(Command):
 
     synopsis = '%prog <server> <zone> <name> <A|AAAA|CNAME|MX|NS|SOA|SRV|TXT|ALL> [options]'
 
-    takes_args = [ 'server', 'zone', 'name', 'rtype' ]
+    takes_args = ['server', 'zone', 'name', 'rtype']
 
     takes_optiongroups = {
         "sambaopts": options.SambaOptions,
@@ -922,19 +802,19 @@ class cmd_query(Command):
 
     takes_options = [
         Option('--authority', help='Search authoritative records (default)',
-                action='store_true', dest='authority'),
+               action='store_true', dest='authority'),
         Option('--cache', help='Search cached records',
-                action='store_true', dest='cache'),
+               action='store_true', dest='cache'),
         Option('--glue', help='Search glue records',
-                action='store_true', dest='glue'),
+               action='store_true', dest='glue'),
         Option('--root', help='Search root hints',
-                action='store_true', dest='root'),
+               action='store_true', dest='root'),
         Option('--additional', help='List additional records',
-                action='store_true', dest='additional'),
+               action='store_true', dest='additional'),
         Option('--no-children', help='Do not list children',
-                action='store_true', dest='no_children'),
+               action='store_true', dest='no_children'),
         Option('--only-children', help='List only children',
-                action='store_true', dest='only_children')
+               action='store_true', dest='only_children')
     ]
 
     def run(self, server, zone, name, rtype, authority=False, cache=False,
@@ -943,6 +823,10 @@ class cmd_query(Command):
             versionopts=None):
         record_type = dns_type_flag(rtype)
 
+        if name.find('*') != -1:
+            self.outf.write('use "@" to dump entire domain, looking up %s\n' %
+                            name)
+
         select_flags = 0
         if authority:
             select_flags |= dnsserver.DNS_RPC_VIEW_AUTHORITY_DATA
@@ -971,9 +855,15 @@ class cmd_query(Command):
         self.creds = credopts.get_credentials(self.lp)
         dns_conn = dns_connect(server, self.lp, self.creds)
 
-        buflen, res = dns_conn.DnssrvEnumRecords2(
+        try:
+            buflen, res = dns_conn.DnssrvEnumRecords2(
                 dnsserver.DNS_CLIENT_VERSION_LONGHORN, 0, server, zone, name,
                 None, record_type, select_flags, None, None)
+        except WERRORError as e:
+            if e.args[0] == werror.WERR_DNS_ERROR_NAME_DOES_NOT_EXIST:
+                self.outf.write('Record or zone does not exist.')
+            raise e
+
         print_dnsrecords(self.outf, res)
 
 
@@ -982,7 +872,7 @@ class cmd_roothints(Command):
 
     synopsis = '%prog <server> [<name>] [options]'
 
-    takes_args = [ 'server', 'name?' ]
+    takes_args = ['server', 'name?']
 
     takes_optiongroups = {
         "sambaopts": options.SambaOptions,
@@ -1022,7 +912,7 @@ class cmd_add_record(Command):
 
     synopsis = '%prog <server> <zone> <name> <A|AAAA|PTR|CNAME|NS|MX|SRV|TXT> <data>'
 
-    takes_args = [ 'server', 'zone', 'name', 'rtype', 'data' ]
+    takes_args = ['server', 'zone', 'name', 'rtype', 'data']
 
     takes_optiongroups = {
         "sambaopts": options.SambaOptions,
@@ -1033,7 +923,7 @@ class cmd_add_record(Command):
     def run(self, server, zone, name, rtype, data, sambaopts=None,
             credopts=None, versionopts=None):
 
-        if rtype.upper() not in ('A','AAAA','PTR','CNAME','NS','MX','SRV','TXT'):
+        if rtype.upper() not in ('A', 'AAAA', 'PTR', 'CNAME', 'NS', 'MX', 'SRV', 'TXT'):
             raise CommandError('Adding record of type %s is not supported' % rtype)
 
         record_type = dns_type_flag(rtype)
@@ -1043,16 +933,17 @@ class cmd_add_record(Command):
         self.creds = credopts.get_credentials(self.lp)
         dns_conn = dns_connect(server, self.lp, self.creds)
 
-        rec_match = dns_record_match(dns_conn, server, zone, name, record_type,
-                data)
-        if rec_match is not None:
-            raise CommandError('Record already exists')
-
         add_rec_buf = dnsserver.DNS_RPC_RECORD_BUF()
         add_rec_buf.rec = rec
 
-        dns_conn.DnssrvUpdateRecord2(dnsserver.DNS_CLIENT_VERSION_LONGHORN,
-                                     0, server, zone, name, add_rec_buf, None)
+        try:
+            dns_conn.DnssrvUpdateRecord2(dnsserver.DNS_CLIENT_VERSION_LONGHORN,
+                                         0, server, zone, name, add_rec_buf, None)
+        except WERRORError as e:
+            if e.args[0] == werror.WERR_DNS_ERROR_NAME_DOES_NOT_EXIST:
+                self.outf.write('Zone does not exist; record could not be added.\n')
+            raise e
+
         self.outf.write('Record added successfully\n')
 
 
@@ -1073,7 +964,7 @@ class cmd_update_record(Command):
 
     synopsis = '%prog <server> <zone> <name> <A|AAAA|PTR|CNAME|NS|MX|SOA|SRV|TXT> <olddata> <newdata>'
 
-    takes_args = [ 'server', 'zone', 'name', 'rtype', 'olddata', 'newdata' ]
+    takes_args = ['server', 'zone', 'name', 'rtype', 'olddata', 'newdata']
 
     takes_optiongroups = {
         "sambaopts": options.SambaOptions,
@@ -1082,9 +973,9 @@ class cmd_update_record(Command):
     }
 
     def run(self, server, zone, name, rtype, olddata, newdata,
-                sambaopts=None, credopts=None, versionopts=None):
+            sambaopts=None, credopts=None, versionopts=None):
 
-        if rtype.upper() not in ('A','AAAA','PTR','CNAME','NS','MX','SOA','SRV','TXT'):
+        if rtype.upper() not in ('A', 'AAAA', 'PTR', 'CNAME', 'NS', 'MX', 'SOA', 'SRV', 'TXT'):
             raise CommandError('Updating record of type %s is not supported' % rtype)
 
         record_type = dns_type_flag(rtype)
@@ -1095,9 +986,9 @@ class cmd_update_record(Command):
         dns_conn = dns_connect(server, self.lp, self.creds)
 
         rec_match = dns_record_match(dns_conn, server, zone, name, record_type,
-                olddata)
+                                     olddata)
         if not rec_match:
-            raise CommandError('Record does not exist')
+            raise CommandError('Record or zone does not exist.')
 
         # Copy properties from existing record to new record
         rec.dwFlags = rec_match.dwFlags
@@ -1111,13 +1002,19 @@ class cmd_update_record(Command):
         del_rec_buf = dnsserver.DNS_RPC_RECORD_BUF()
         del_rec_buf.rec = rec_match
 
-        dns_conn.DnssrvUpdateRecord2(dnsserver.DNS_CLIENT_VERSION_LONGHORN,
-                                        0,
-                                        server,
-                                        zone,
-                                        name,
-                                        add_rec_buf,
-                                        del_rec_buf)
+        try:
+            dns_conn.DnssrvUpdateRecord2(dnsserver.DNS_CLIENT_VERSION_LONGHORN,
+                                         0,
+                                         server,
+                                         zone,
+                                         name,
+                                         add_rec_buf,
+                                         del_rec_buf)
+        except WERRORError as e:
+            if e.args[0] == werror.WERR_DNS_ERROR_NAME_DOES_NOT_EXIST:
+                self.outf.write('Zone does not exist; record could not be updated.\n')
+            raise e
+
         self.outf.write('Record updated successfully\n')
 
 
@@ -1137,7 +1034,7 @@ class cmd_delete_record(Command):
 
     synopsis = '%prog <server> <zone> <name> <A|AAAA|PTR|CNAME|NS|MX|SRV|TXT> <data>'
 
-    takes_args = [ 'server', 'zone', 'name', 'rtype', 'data' ]
+    takes_args = ['server', 'zone', 'name', 'rtype', 'data']
 
     takes_optiongroups = {
         "sambaopts": options.SambaOptions,
@@ -1147,32 +1044,83 @@ class cmd_delete_record(Command):
 
     def run(self, server, zone, name, rtype, data, sambaopts=None, credopts=None, versionopts=None):
 
-        if rtype.upper() not in ('A','AAAA','PTR','CNAME','NS','MX','SRV','TXT'):
+        if rtype.upper() not in ('A', 'AAAA', 'PTR', 'CNAME', 'NS', 'MX', 'SRV', 'TXT'):
             raise CommandError('Deleting record of type %s is not supported' % rtype)
 
         record_type = dns_type_flag(rtype)
+        rec = data_to_dns_record(record_type, data)
 
         self.lp = sambaopts.get_loadparm()
         self.creds = credopts.get_credentials(self.lp)
         dns_conn = dns_connect(server, self.lp, self.creds)
 
-        rec_match = dns_record_match(dns_conn, server, zone, name, record_type, data)
-        if not rec_match:
-            raise CommandError('Record does not exist')
-
         del_rec_buf = dnsserver.DNS_RPC_RECORD_BUF()
-        del_rec_buf.rec = rec_match
+        del_rec_buf.rec = rec
+
+        try:
+            dns_conn.DnssrvUpdateRecord2(dnsserver.DNS_CLIENT_VERSION_LONGHORN,
+                                         0,
+                                         server,
+                                         zone,
+                                         name,
+                                         None,
+                                         del_rec_buf)
+        except WERRORError as e:
+            if e.args[0] == werror.WERR_DNS_ERROR_NAME_DOES_NOT_EXIST:
+                self.outf.write('Zone does not exist; record could not be deleted.\n')
+            raise e
 
-        dns_conn.DnssrvUpdateRecord2(dnsserver.DNS_CLIENT_VERSION_LONGHORN,
-                                        0,
-                                        server,
-                                        zone,
-                                        name,
-                                        None,
-                                        del_rec_buf)
         self.outf.write('Record deleted successfully\n')
 
 
+class cmd_cleanup_record(Command):
+    """Cleanup DNS records for a DNS host.
+
+    example:
+
+        samba-tool dns cleanup dc1 dc1.samdom.test.site -U USER%PASSWORD
+
+    NOTE: This command in many cases will only mark the `dNSTombstoned` attr
+    as `TRUE` on the DNS records. Querying will no longer return results but
+    there may still be some placeholder entries in the database.
+    """
+
+    synopsis = '%prog <server> <dnshostname>'
+
+    takes_args = ['server', 'dnshostname']
+
+    takes_optiongroups = {
+        "sambaopts": options.SambaOptions,
+        "versionopts": options.VersionOptions,
+        "credopts": options.CredentialsOptions,
+    }
+
+    takes_options = [
+        Option("-v", "--verbose", help="Be verbose", action="store_true"),
+        Option("-q", "--quiet", help="Be quiet", action="store_true"),
+    ]
+
+    def run(self, server, dnshostname, sambaopts=None, credopts=None,
+            versionopts=None, verbose=False, quiet=False):
+        lp = sambaopts.get_loadparm()
+        creds = credopts.get_credentials(lp)
+
+        logger = self.get_logger()
+        if verbose:
+            logger.setLevel(logging.DEBUG)
+        elif quiet:
+            logger.setLevel(logging.WARNING)
+        else:
+            logger.setLevel(logging.INFO)
+
+        samdb = SamDB(url="ldap://%s" % server,
+                      session_info=system_session(),
+                      credentials=creds, lp=lp)
+
+        remove_dc.remove_dns_references(samdb, logger, dnshostname,
+                                        ignore_no_name=True)
+
+
 class cmd_dns(SuperCommand):
     """Domain Name Service (DNS) management."""
 
@@ -1187,3 +1135,4 @@ class cmd_dns(SuperCommand):
     subcommands['add'] = cmd_add_record()
     subcommands['update'] = cmd_update_record()
     subcommands['delete'] = cmd_delete_record()
+    subcommands['cleanup'] = cmd_cleanup_record()