PEP8: fix E303: too many blank lines (2)
[nivanova/samba-autobuild/.git] / python / samba / netcmd / dns.py
1 # DNS management tool
2 #
3 # Copyright (C) Amitay Isaacs 2011-2012
4 #
5 # This program is free software; you can redistribute it and/or modify
6 # it under the terms of the GNU General Public License as published by
7 # the Free Software Foundation; either version 3 of the License, or
8 # (at your option) any later version.
9 #
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 # GNU General Public License for more details.
14 #
15 # You should have received a copy of the GNU General Public License
16 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
17 #
18 import logging
19
20 import samba.getopt as options
21 from samba import WERRORError
22 from samba import werror
23 from struct import pack
24 from socket import inet_ntoa
25 from socket import inet_ntop
26 from socket import AF_INET
27 from socket import AF_INET6
28 import shlex
29
30 from samba import remove_dc
31 from samba.samdb import SamDB
32 from samba.auth import system_session
33
34 from samba.netcmd import (
35     Command,
36     CommandError,
37     Option,
38     SuperCommand,
39 )
40 from samba.dcerpc import dnsp, dnsserver
41
42 from samba.dnsserver import ARecord, AAAARecord, PTRRecord, CNameRecord, NSRecord, MXRecord, SOARecord, SRVRecord, TXTRecord
43
44
45 def dns_connect(server, lp, creds):
46     if server.lower() == 'localhost':
47         server = '127.0.0.1'
48     binding_str = "ncacn_ip_tcp:%s[sign]" % server
49     try:
50         dns_conn = dnsserver.dnsserver(binding_str, lp, creds)
51     except RuntimeError as e:
52         raise CommandError('Connecting to DNS RPC server %s failed with %s' % (server, e))
53
54     return dns_conn
55
56
57 def bool_string(flag):
58     if flag == 0:
59         ret = 'FALSE'
60     elif flag == 1:
61         ret = 'TRUE'
62     else:
63         ret = 'UNKNOWN (0x%x)' % flag
64     return ret
65
66
67 def enum_string(module, enum_defs, value):
68     ret = None
69     for e in enum_defs:
70         if value == getattr(module, e):
71             ret = e
72             break
73     if not ret:
74         ret = 'UNKNOWN (0x%x)' % value
75     return ret
76
77
78 def bitmap_string(module, bitmap_defs, value):
79     ret = ''
80     for b in bitmap_defs:
81         if value & getattr(module, b):
82             ret += '%s ' % b
83     if not ret:
84         ret = 'NONE'
85     return ret
86
87
88 def boot_method_string(boot_method):
89     enum_defs = ['DNS_BOOT_METHOD_UNINITIALIZED', 'DNS_BOOT_METHOD_FILE',
90                   'DNS_BOOT_METHOD_REGISTRY', 'DNS_BOOT_METHOD_DIRECTORY']
91     return enum_string(dnsserver, enum_defs, boot_method)
92
93
94 def name_check_flag_string(check_flag):
95     enum_defs = ['DNS_ALLOW_RFC_NAMES_ONLY', 'DNS_ALLOW_NONRFC_NAMES',
96                   'DNS_ALLOW_MULTIBYTE_NAMES', 'DNS_ALLOW_ALL_NAMES']
97     return enum_string(dnsserver, enum_defs, check_flag)
98
99
100 def zone_type_string(zone_type):
101     enum_defs = ['DNS_ZONE_TYPE_CACHE', 'DNS_ZONE_TYPE_PRIMARY',
102                   'DNS_ZONE_TYPE_SECONDARY', 'DNS_ZONE_TYPE_STUB',
103                   'DNS_ZONE_TYPE_FORWARDER', 'DNS_ZONE_TYPE_SECONDARY_CACHE']
104     return enum_string(dnsp, enum_defs, zone_type)
105
106
107 def zone_update_string(zone_update):
108     enum_defs = ['DNS_ZONE_UPDATE_OFF', 'DNS_ZONE_UPDATE_UNSECURE',
109                   'DNS_ZONE_UPDATE_SECURE']
110     return enum_string(dnsp, enum_defs, zone_update)
111
112
113 def zone_secondary_security_string(security):
114     enum_defs = ['DNS_ZONE_SECSECURE_NO_SECURITY', 'DNS_ZONE_SECSECURE_NS_ONLY',
115                   'DNS_ZONE_SECSECURE_LIST_ONLY', 'DNS_ZONE_SECSECURE_NO_XFER']
116     return enum_string(dnsserver, enum_defs, security)
117
118
119 def zone_notify_level_string(notify_level):
120     enum_defs = ['DNS_ZONE_NOTIFY_OFF', 'DNS_ZONE_NOTIFY_ALL_SECONDARIES',
121                   'DNS_ZONE_NOTIFY_LIST_ONLY']
122     return enum_string(dnsserver, enum_defs, notify_level)
123
124
125 def dp_flags_string(dp_flags):
126     bitmap_defs = ['DNS_DP_AUTOCREATED', 'DNS_DP_LEGACY', 'DNS_DP_DOMAIN_DEFAULT',
127                     'DNS_DP_FOREST_DEFAULT', 'DNS_DP_ENLISTED', 'DNS_DP_DELETED']
128     return bitmap_string(dnsserver, bitmap_defs, dp_flags)
129
130
131 def zone_flags_string(flags):
132     bitmap_defs = ['DNS_RPC_ZONE_PAUSED', 'DNS_RPC_ZONE_SHUTDOWN',
133                     'DNS_RPC_ZONE_REVERSE', 'DNS_RPC_ZONE_AUTOCREATED',
134                     'DNS_RPC_ZONE_DSINTEGRATED', 'DNS_RPC_ZONE_AGING',
135                     'DNS_RPC_ZONE_UPDATE_UNSECURE', 'DNS_RPC_ZONE_UPDATE_SECURE',
136                     'DNS_RPC_ZONE_READONLY']
137     return bitmap_string(dnsserver, bitmap_defs, flags)
138
139
140 def ip4_array_string(array):
141     ret = []
142     if not array:
143         return ret
144     for i in range(array.AddrCount):
145         addr = inet_ntop(AF_INET, pack('I', array.AddrArray[i]))
146         ret.append(addr)
147     return ret
148
149
150 def dns_addr_array_string(array):
151     ret = []
152     if not array:
153         return ret
154     for i in range(array.AddrCount):
155         if array.AddrArray[i].MaxSa[0] == 0x02:
156             x = "".join([chr(b) for b in array.AddrArray[i].MaxSa])[4:8]
157             addr = inet_ntop(AF_INET, x)
158         elif array.AddrArray[i].MaxSa[0] == 0x17:
159             x = "".join([chr(b) for b in array.AddrArray[i].MaxSa])[8:24]
160             addr = inet_ntop(AF_INET6, x)
161         else:
162             addr = 'UNKNOWN'
163         ret.append(addr)
164     return ret
165
166
167 def dns_type_flag(rec_type):
168     rtype = rec_type.upper()
169     if rtype == 'A':
170         record_type = dnsp.DNS_TYPE_A
171     elif rtype == 'AAAA':
172         record_type = dnsp.DNS_TYPE_AAAA
173     elif rtype == 'PTR':
174         record_type = dnsp.DNS_TYPE_PTR
175     elif rtype == 'NS':
176         record_type = dnsp.DNS_TYPE_NS
177     elif rtype == 'CNAME':
178         record_type = dnsp.DNS_TYPE_CNAME
179     elif rtype == 'SOA':
180         record_type = dnsp.DNS_TYPE_SOA
181     elif rtype == 'MX':
182         record_type = dnsp.DNS_TYPE_MX
183     elif rtype == 'SRV':
184         record_type = dnsp.DNS_TYPE_SRV
185     elif rtype == 'TXT':
186         record_type = dnsp.DNS_TYPE_TXT
187     elif rtype == 'ALL':
188         record_type = dnsp.DNS_TYPE_ALL
189     else:
190         raise CommandError('Unknown type of DNS record %s' % rec_type)
191     return record_type
192
193
194 def dns_client_version(cli_version):
195     version = cli_version.upper()
196     if version == 'W2K':
197         client_version = dnsserver.DNS_CLIENT_VERSION_W2K
198     elif version == 'DOTNET':
199         client_version = dnsserver.DNS_CLIENT_VERSION_DOTNET
200     elif version == 'LONGHORN':
201         client_version = dnsserver.DNS_CLIENT_VERSION_LONGHORN
202     else:
203         raise CommandError('Unknown client version %s' % cli_version)
204     return client_version
205
206
207 def print_serverinfo(outf, typeid, serverinfo):
208     outf.write('  dwVersion                   : 0x%x\n' % serverinfo.dwVersion)
209     outf.write('  fBootMethod                 : %s\n' % boot_method_string(serverinfo.fBootMethod))
210     outf.write('  fAdminConfigured            : %s\n' % bool_string(serverinfo.fAdminConfigured))
211     outf.write('  fAllowUpdate                : %s\n' % bool_string(serverinfo.fAllowUpdate))
212     outf.write('  fDsAvailable                : %s\n' % bool_string(serverinfo.fDsAvailable))
213     outf.write('  pszServerName               : %s\n' % serverinfo.pszServerName)
214     outf.write('  pszDsContainer              : %s\n' % serverinfo.pszDsContainer)
215
216     if typeid != dnsserver.DNSSRV_TYPEID_SERVER_INFO:
217         outf.write('  aipServerAddrs              : %s\n' %
218                    ip4_array_string(serverinfo.aipServerAddrs))
219         outf.write('  aipListenAddrs              : %s\n' %
220                    ip4_array_string(serverinfo.aipListenAddrs))
221         outf.write('  aipForwarders               : %s\n' %
222                    ip4_array_string(serverinfo.aipForwarders))
223     else:
224         outf.write('  aipServerAddrs              : %s\n' %
225                    dns_addr_array_string(serverinfo.aipServerAddrs))
226         outf.write('  aipListenAddrs              : %s\n' %
227                    dns_addr_array_string(serverinfo.aipListenAddrs))
228         outf.write('  aipForwarders               : %s\n' %
229                    dns_addr_array_string(serverinfo.aipForwarders))
230
231     outf.write('  dwLogLevel                  : %d\n' % serverinfo.dwLogLevel)
232     outf.write('  dwDebugLevel                : %d\n' % serverinfo.dwDebugLevel)
233     outf.write('  dwForwardTimeout            : %d\n' % serverinfo.dwForwardTimeout)
234     outf.write('  dwRpcPrototol               : 0x%x\n' % serverinfo.dwRpcProtocol)
235     outf.write('  dwNameCheckFlag             : %s\n' % name_check_flag_string(serverinfo.dwNameCheckFlag))
236     outf.write('  cAddressAnswerLimit         : %d\n' % serverinfo.cAddressAnswerLimit)
237     outf.write('  dwRecursionRetry            : %d\n' % serverinfo.dwRecursionRetry)
238     outf.write('  dwRecursionTimeout          : %d\n' % serverinfo.dwRecursionTimeout)
239     outf.write('  dwMaxCacheTtl               : %d\n' % serverinfo.dwMaxCacheTtl)
240     outf.write('  dwDsPollingInterval         : %d\n' % serverinfo.dwDsPollingInterval)
241     outf.write('  dwScavengingInterval        : %d\n' % serverinfo.dwScavengingInterval)
242     outf.write('  dwDefaultRefreshInterval    : %d\n' % serverinfo.dwDefaultRefreshInterval)
243     outf.write('  dwDefaultNoRefreshInterval  : %d\n' % serverinfo.dwDefaultNoRefreshInterval)
244     outf.write('  fAutoReverseZones           : %s\n' % bool_string(serverinfo.fAutoReverseZones))
245     outf.write('  fAutoCacheUpdate            : %s\n' % bool_string(serverinfo.fAutoCacheUpdate))
246     outf.write('  fRecurseAfterForwarding     : %s\n' % bool_string(serverinfo.fRecurseAfterForwarding))
247     outf.write('  fForwardDelegations         : %s\n' % bool_string(serverinfo.fForwardDelegations))
248     outf.write('  fNoRecursion                : %s\n' % bool_string(serverinfo.fNoRecursion))
249     outf.write('  fSecureResponses            : %s\n' % bool_string(serverinfo.fSecureResponses))
250     outf.write('  fRoundRobin                 : %s\n' % bool_string(serverinfo.fRoundRobin))
251     outf.write('  fLocalNetPriority           : %s\n' % bool_string(serverinfo.fLocalNetPriority))
252     outf.write('  fBindSecondaries            : %s\n' % bool_string(serverinfo.fBindSecondaries))
253     outf.write('  fWriteAuthorityNs           : %s\n' % bool_string(serverinfo.fWriteAuthorityNs))
254     outf.write('  fStrictFileParsing          : %s\n' % bool_string(serverinfo.fStrictFileParsing))
255     outf.write('  fLooseWildcarding           : %s\n' % bool_string(serverinfo.fLooseWildcarding))
256     outf.write('  fDefaultAgingState          : %s\n' % bool_string(serverinfo.fDefaultAgingState))
257
258     if typeid != dnsserver.DNSSRV_TYPEID_SERVER_INFO_W2K:
259         outf.write('  dwRpcStructureVersion       : 0x%x\n' % serverinfo.dwRpcStructureVersion)
260         outf.write('  aipLogFilter                : %s\n' % dns_addr_array_string(serverinfo.aipLogFilter))
261         outf.write('  pwszLogFilePath             : %s\n' % serverinfo.pwszLogFilePath)
262         outf.write('  pszDomainName               : %s\n' % serverinfo.pszDomainName)
263         outf.write('  pszForestName               : %s\n' % serverinfo.pszForestName)
264         outf.write('  pszDomainDirectoryPartition : %s\n' % serverinfo.pszDomainDirectoryPartition)
265         outf.write('  pszForestDirectoryPartition : %s\n' % serverinfo.pszForestDirectoryPartition)
266
267         outf.write('  dwLocalNetPriorityNetMask   : 0x%x\n' % serverinfo.dwLocalNetPriorityNetMask)
268         outf.write('  dwLastScavengeTime          : %d\n' % serverinfo.dwLastScavengeTime)
269         outf.write('  dwEventLogLevel             : %d\n' % serverinfo.dwEventLogLevel)
270         outf.write('  dwLogFileMaxSize            : %d\n' % serverinfo.dwLogFileMaxSize)
271         outf.write('  dwDsForestVersion           : %d\n' % serverinfo.dwDsForestVersion)
272         outf.write('  dwDsDomainVersion           : %d\n' % serverinfo.dwDsDomainVersion)
273         outf.write('  dwDsDsaVersion              : %d\n' % serverinfo.dwDsDsaVersion)
274
275     if typeid == dnsserver.DNSSRV_TYPEID_SERVER_INFO:
276         outf.write('  fReadOnlyDC                 : %s\n' % bool_string(serverinfo.fReadOnlyDC))
277
278
279 def print_zoneinfo(outf, typeid, zoneinfo):
280     outf.write('  pszZoneName                 : %s\n' % zoneinfo.pszZoneName)
281     outf.write('  dwZoneType                  : %s\n' % zone_type_string(zoneinfo.dwZoneType))
282     outf.write('  fReverse                    : %s\n' % bool_string(zoneinfo.fReverse))
283     outf.write('  fAllowUpdate                : %s\n' % zone_update_string(zoneinfo.fAllowUpdate))
284     outf.write('  fPaused                     : %s\n' % bool_string(zoneinfo.fPaused))
285     outf.write('  fShutdown                   : %s\n' % bool_string(zoneinfo.fShutdown))
286     outf.write('  fAutoCreated                : %s\n' % bool_string(zoneinfo.fAutoCreated))
287     outf.write('  fUseDatabase                : %s\n' % bool_string(zoneinfo.fUseDatabase))
288     outf.write('  pszDataFile                 : %s\n' % zoneinfo.pszDataFile)
289     if typeid != dnsserver.DNSSRV_TYPEID_ZONE_INFO:
290         outf.write('  aipMasters                  : %s\n' %
291                    ip4_array_string(zoneinfo.aipMasters))
292     else:
293         outf.write('  aipMasters                  : %s\n' %
294                    dns_addr_array_string(zoneinfo.aipMasters))
295     outf.write('  fSecureSecondaries          : %s\n' % zone_secondary_security_string(zoneinfo.fSecureSecondaries))
296     outf.write('  fNotifyLevel                : %s\n' % zone_notify_level_string(zoneinfo.fNotifyLevel))
297     if typeid != dnsserver.DNSSRV_TYPEID_ZONE_INFO:
298         outf.write('  aipSecondaries              : %s\n' %
299                    ip4_array_string(zoneinfo.aipSecondaries))
300         outf.write('  aipNotify                   : %s\n' %
301                    ip4_array_string(zoneinfo.aipNotify))
302     else:
303         outf.write('  aipSecondaries              : %s\n' %
304                    dns_addr_array_string(zoneinfo.aipSecondaries))
305         outf.write('  aipNotify                   : %s\n' %
306                    dns_addr_array_string(zoneinfo.aipNotify))
307     outf.write('  fUseWins                    : %s\n' % bool_string(zoneinfo.fUseWins))
308     outf.write('  fUseNbstat                  : %s\n' % bool_string(zoneinfo.fUseNbstat))
309     outf.write('  fAging                      : %s\n' % bool_string(zoneinfo.fAging))
310     outf.write('  dwNoRefreshInterval         : %d\n' % zoneinfo.dwNoRefreshInterval)
311     outf.write('  dwRefreshInterval           : %d\n' % zoneinfo.dwRefreshInterval)
312     outf.write('  dwAvailForScavengeTime      : %d\n' % zoneinfo.dwAvailForScavengeTime)
313     if typeid != dnsserver.DNSSRV_TYPEID_ZONE_INFO:
314         outf.write('  aipScavengeServers          : %s\n' %
315                    ip4_array_string(zoneinfo.aipScavengeServers))
316     else:
317         outf.write('  aipScavengeServers          : %s\n' %
318                    dns_addr_array_string(zoneinfo.aipScavengeServers))
319
320     if typeid != dnsserver.DNSSRV_TYPEID_ZONE_INFO_W2K:
321         outf.write('  dwRpcStructureVersion       : 0x%x\n' % zoneinfo.dwRpcStructureVersion)
322         outf.write('  dwForwarderTimeout          : %d\n' % zoneinfo.dwForwarderTimeout)
323         outf.write('  fForwarderSlave             : %d\n' % zoneinfo.fForwarderSlave)
324         if typeid != dnsserver.DNSSRV_TYPEID_ZONE_INFO:
325             outf.write('  aipLocalMasters             : %s\n' %
326                        ip4_array_string(zoneinfo.aipLocalMasters))
327         else:
328             outf.write('  aipLocalMasters             : %s\n' %
329                        dns_addr_array_string(zoneinfo.aipLocalMasters))
330         outf.write('  dwDpFlags                   : %s\n' % dp_flags_string(zoneinfo.dwDpFlags))
331         outf.write('  pszDpFqdn                   : %s\n' % zoneinfo.pszDpFqdn)
332         outf.write('  pwszZoneDn                  : %s\n' % zoneinfo.pwszZoneDn)
333         outf.write('  dwLastSuccessfulSoaCheck    : %d\n' % zoneinfo.dwLastSuccessfulSoaCheck)
334         outf.write('  dwLastSuccessfulXfr         : %d\n' % zoneinfo.dwLastSuccessfulXfr)
335
336     if typeid == dnsserver.DNSSRV_TYPEID_ZONE_INFO:
337         outf.write('  fQueuedForBackgroundLoad    : %s\n' % bool_string(zoneinfo.fQueuedForBackgroundLoad))
338         outf.write('  fBackgroundLoadInProgress   : %s\n' % bool_string(zoneinfo.fBackgroundLoadInProgress))
339         outf.write('  fReadOnlyZone               : %s\n' % bool_string(zoneinfo.fReadOnlyZone))
340         outf.write('  dwLastXfrAttempt            : %d\n' % zoneinfo.dwLastXfrAttempt)
341         outf.write('  dwLastXfrResult             : %d\n' % zoneinfo.dwLastXfrResult)
342
343
344 def print_zone(outf, typeid, zone):
345     outf.write('  pszZoneName                 : %s\n' % zone.pszZoneName)
346     outf.write('  Flags                       : %s\n' % zone_flags_string(zone.Flags))
347     outf.write('  ZoneType                    : %s\n' % zone_type_string(zone.ZoneType))
348     outf.write('  Version                     : %s\n' % zone.Version)
349
350     if typeid != dnsserver.DNSSRV_TYPEID_ZONE_W2K:
351         outf.write('  dwDpFlags                   : %s\n' % dp_flags_string(zone.dwDpFlags))
352         outf.write('  pszDpFqdn                   : %s\n' % zone.pszDpFqdn)
353
354
355 def print_enumzones(outf, typeid, zones):
356     outf.write('  %d zone(s) found\n' % zones.dwZoneCount)
357     for zone in zones.ZoneArray:
358         outf.write('\n')
359         print_zone(outf, typeid, zone)
360
361
362 def print_dns_record(outf, rec):
363     if rec.wType == dnsp.DNS_TYPE_A:
364         mesg = 'A: %s' % (rec.data)
365     elif rec.wType == dnsp.DNS_TYPE_AAAA:
366         mesg = 'AAAA: %s' % (rec.data)
367     elif rec.wType == dnsp.DNS_TYPE_PTR:
368         mesg = 'PTR: %s' % (rec.data.str)
369     elif rec.wType == dnsp.DNS_TYPE_NS:
370         mesg = 'NS: %s' % (rec.data.str)
371     elif rec.wType == dnsp.DNS_TYPE_CNAME:
372         mesg = 'CNAME: %s' % (rec.data.str)
373     elif rec.wType == dnsp.DNS_TYPE_SOA:
374         mesg = 'SOA: serial=%d, refresh=%d, retry=%d, expire=%d, minttl=%d, ns=%s, email=%s' % (
375                     rec.data.dwSerialNo,
376                     rec.data.dwRefresh,
377                     rec.data.dwRetry,
378                     rec.data.dwExpire,
379                     rec.data.dwMinimumTtl,
380                     rec.data.NamePrimaryServer.str,
381                     rec.data.ZoneAdministratorEmail.str)
382     elif rec.wType == dnsp.DNS_TYPE_MX:
383         mesg = 'MX: %s (%d)' % (rec.data.nameExchange.str, rec.data.wPreference)
384     elif rec.wType == dnsp.DNS_TYPE_SRV:
385         mesg = 'SRV: %s (%d, %d, %d)' % (rec.data.nameTarget.str, rec.data.wPort,
386                                          rec.data.wPriority, rec.data.wWeight)
387     elif rec.wType == dnsp.DNS_TYPE_TXT:
388         slist = ['"%s"' % name.str for name in rec.data.str]
389         mesg = 'TXT: %s' % ','.join(slist)
390     else:
391         mesg = 'Unknown: '
392     outf.write('    %s (flags=%x, serial=%d, ttl=%d)\n' % (
393                 mesg, rec.dwFlags, rec.dwSerial, rec.dwTtlSeconds))
394
395
396 def print_dnsrecords(outf, records):
397     for rec in records.rec:
398         outf.write('  Name=%s, Records=%d, Children=%d\n' % (
399                     rec.dnsNodeName.str,
400                     rec.wRecordCount,
401                     rec.dwChildCount))
402         for dns_rec in rec.records:
403                 print_dns_record(outf, dns_rec)
404
405
406 # Convert data into a dns record
407 def data_to_dns_record(record_type, data):
408     if record_type == dnsp.DNS_TYPE_A:
409         rec = ARecord(data)
410     elif record_type == dnsp.DNS_TYPE_AAAA:
411         rec = AAAARecord(data)
412     elif record_type == dnsp.DNS_TYPE_PTR:
413         rec = PTRRecord(data)
414     elif record_type == dnsp.DNS_TYPE_CNAME:
415         rec = CNameRecord(data)
416     elif record_type == dnsp.DNS_TYPE_NS:
417         rec = NSRecord(data)
418     elif record_type == dnsp.DNS_TYPE_MX:
419         tmp = data.split(' ')
420         if len(tmp) != 2:
421             raise CommandError('Data requires 2 elements - mail_server, preference')
422         mail_server = tmp[0]
423         preference = int(tmp[1])
424         rec = MXRecord(mail_server, preference)
425     elif record_type == dnsp.DNS_TYPE_SRV:
426         tmp = data.split(' ')
427         if len(tmp) != 4:
428             raise CommandError('Data requires 4 elements - server, port, priority, weight')
429         server = tmp[0]
430         port = int(tmp[1])
431         priority = int(tmp[2])
432         weight = int(tmp[3])
433         rec = SRVRecord(server, port, priority=priority, weight=weight)
434     elif record_type == dnsp.DNS_TYPE_SOA:
435         tmp = data.split(' ')
436         if len(tmp) != 7:
437             raise CommandError('Data requires 7 elements - nameserver, email, serial, '
438                                'refresh, retry, expire, minimumttl')
439         nameserver = tmp[0]
440         email = tmp[1]
441         serial = int(tmp[2])
442         refresh = int(tmp[3])
443         retry = int(tmp[4])
444         expire = int(tmp[5])
445         minimum = int(tmp[6])
446         rec = SOARecord(nameserver, email, serial=serial, refresh=refresh,
447                         retry=retry, expire=expire, minimum=minimum)
448     elif record_type == dnsp.DNS_TYPE_TXT:
449         slist = shlex.split(data)
450         rec = TXTRecord(slist)
451     else:
452         raise CommandError('Unsupported record type')
453     return rec
454
455
456 # Match dns name (of type DNS_RPC_NAME)
457 def dns_name_equal(n1, n2):
458     return n1.str.rstrip('.').lower() == n2.str.rstrip('.').lower()
459
460
461 # Match a dns record with specified data
462 def dns_record_match(dns_conn, server, zone, name, record_type, data):
463     urec = data_to_dns_record(record_type, data)
464
465     select_flags = dnsserver.DNS_RPC_VIEW_AUTHORITY_DATA
466
467     try:
468         buflen, res = dns_conn.DnssrvEnumRecords2(
469             dnsserver.DNS_CLIENT_VERSION_LONGHORN, 0, server, zone, name, None,
470             record_type, select_flags, None, None)
471     except WERRORError as e:
472         if e.args[0] == werror.WERR_DNS_ERROR_NAME_DOES_NOT_EXIST:
473             # Either the zone doesn't exist, or there were no records.
474             # We can't differentiate the two.
475             return None
476         raise e
477
478     if not res or res.count == 0:
479         return None
480
481     for rec in res.rec[0].records:
482         if rec.wType != record_type:
483             continue
484
485         found = False
486         if record_type == dnsp.DNS_TYPE_A:
487             if rec.data == urec.data:
488                 found = True
489         elif record_type == dnsp.DNS_TYPE_AAAA:
490             if rec.data == urec.data:
491                 found = True
492         elif record_type == dnsp.DNS_TYPE_PTR:
493             if dns_name_equal(rec.data, urec.data):
494                 found = True
495         elif record_type == dnsp.DNS_TYPE_CNAME:
496             if dns_name_equal(rec.data, urec.data):
497                 found = True
498         elif record_type == dnsp.DNS_TYPE_NS:
499             if dns_name_equal(rec.data, urec.data):
500                 found = True
501         elif record_type == dnsp.DNS_TYPE_MX:
502             if dns_name_equal(rec.data.nameExchange, urec.data.nameExchange) and \
503                rec.data.wPreference == urec.data.wPreference:
504                 found = True
505         elif record_type == dnsp.DNS_TYPE_SRV:
506             if rec.data.wPriority == urec.data.wPriority and \
507                rec.data.wWeight == urec.data.wWeight and \
508                rec.data.wPort == urec.data.wPort and \
509                dns_name_equal(rec.data.nameTarget, urec.data.nameTarget):
510                 found = True
511         elif record_type == dnsp.DNS_TYPE_SOA:
512             if rec.data.dwSerialNo == urec.data.dwSerialNo and \
513                rec.data.dwRefresh == urec.data.dwRefresh and \
514                rec.data.dwRetry == urec.data.dwRetry and \
515                rec.data.dwExpire == urec.data.dwExpire and \
516                rec.data.dwMinimumTtl == urec.data.dwMinimumTtl and \
517                dns_name_equal(rec.data.NamePrimaryServer,
518                               urec.data.NamePrimaryServer) and \
519                dns_name_equal(rec.data.ZoneAdministratorEmail,
520                               urec.data.ZoneAdministratorEmail):
521                 found = True
522         elif record_type == dnsp.DNS_TYPE_TXT:
523             if rec.data.count == urec.data.count:
524                 found = True
525                 for i in range(rec.data.count):
526                     found = found and \
527                             (rec.data.str[i].str == urec.data.str[i].str)
528
529         if found:
530             return rec
531
532     return None
533
534
535 class cmd_serverinfo(Command):
536     """Query for Server information."""
537
538     synopsis = '%prog <server> [options]'
539
540     takes_args = ['server']
541
542     takes_optiongroups = {
543         "sambaopts": options.SambaOptions,
544         "versionopts": options.VersionOptions,
545         "credopts": options.CredentialsOptions,
546     }
547
548     takes_options = [
549         Option('--client-version', help='Client Version',
550                default='longhorn', metavar='w2k|dotnet|longhorn',
551                choices=['w2k', 'dotnet', 'longhorn'], dest='cli_ver'),
552     ]
553
554     def run(self, server, cli_ver, sambaopts=None, credopts=None,
555             versionopts=None):
556         self.lp = sambaopts.get_loadparm()
557         self.creds = credopts.get_credentials(self.lp)
558         dns_conn = dns_connect(server, self.lp, self.creds)
559
560         client_version = dns_client_version(cli_ver)
561
562         typeid, res = dns_conn.DnssrvQuery2(client_version, 0, server,
563                                             None, 'ServerInfo')
564         print_serverinfo(self.outf, typeid, res)
565
566
567 class cmd_zoneinfo(Command):
568     """Query for zone information."""
569
570     synopsis = '%prog <server> <zone> [options]'
571
572     takes_args = ['server', 'zone']
573
574     takes_optiongroups = {
575         "sambaopts": options.SambaOptions,
576         "versionopts": options.VersionOptions,
577         "credopts": options.CredentialsOptions,
578     }
579
580     takes_options = [
581         Option('--client-version', help='Client Version',
582                default='longhorn', metavar='w2k|dotnet|longhorn',
583                choices=['w2k', 'dotnet', 'longhorn'], dest='cli_ver'),
584     ]
585
586     def run(self, server, zone, cli_ver, sambaopts=None, credopts=None,
587             versionopts=None):
588         self.lp = sambaopts.get_loadparm()
589         self.creds = credopts.get_credentials(self.lp)
590         dns_conn = dns_connect(server, self.lp, self.creds)
591
592         client_version = dns_client_version(cli_ver)
593
594         typeid, res = dns_conn.DnssrvQuery2(client_version, 0, server, zone,
595                                             'ZoneInfo')
596         print_zoneinfo(self.outf, typeid, res)
597
598
599 class cmd_zonelist(Command):
600     """Query for zones."""
601
602     synopsis = '%prog <server> [options]'
603
604     takes_args = ['server']
605
606     takes_optiongroups = {
607         "sambaopts": options.SambaOptions,
608         "versionopts": options.VersionOptions,
609         "credopts": options.CredentialsOptions,
610     }
611
612     takes_options = [
613         Option('--client-version', help='Client Version',
614                default='longhorn', metavar='w2k|dotnet|longhorn',
615                choices=['w2k', 'dotnet', 'longhorn'], dest='cli_ver'),
616         Option('--primary', help='List primary zones (default)',
617                action='store_true', dest='primary'),
618         Option('--secondary', help='List secondary zones',
619                action='store_true', dest='secondary'),
620         Option('--cache', help='List cached zones',
621                action='store_true', dest='cache'),
622         Option('--auto', help='List automatically created zones',
623                action='store_true', dest='auto'),
624         Option('--forward', help='List forward zones',
625                action='store_true', dest='forward'),
626         Option('--reverse', help='List reverse zones',
627                action='store_true', dest='reverse'),
628         Option('--ds', help='List directory integrated zones',
629                action='store_true', dest='ds'),
630         Option('--non-ds', help='List non-directory zones',
631                action='store_true', dest='nonds')
632     ]
633
634     def run(self, server, cli_ver, primary=False, secondary=False, cache=False,
635             auto=False, forward=False, reverse=False, ds=False, nonds=False,
636             sambaopts=None, credopts=None, versionopts=None):
637         request_filter = 0
638
639         if primary:
640             request_filter |= dnsserver.DNS_ZONE_REQUEST_PRIMARY
641         if secondary:
642             request_filter |= dnsserver.DNS_ZONE_REQUEST_SECONDARY
643         if cache:
644             request_filter |= dnsserver.DNS_ZONE_REQUEST_CACHE
645         if auto:
646             request_filter |= dnsserver.DNS_ZONE_REQUEST_AUTO
647         if forward:
648             request_filter |= dnsserver.DNS_ZONE_REQUEST_FORWARD
649         if reverse:
650             request_filter |= dnsserver.DNS_ZONE_REQUEST_REVERSE
651         if ds:
652             request_filter |= dnsserver.DNS_ZONE_REQUEST_DS
653         if nonds:
654             request_filter |= dnsserver.DNS_ZONE_REQUEST_NON_DS
655
656         if request_filter == 0:
657             request_filter = dnsserver.DNS_ZONE_REQUEST_PRIMARY
658
659         self.lp = sambaopts.get_loadparm()
660         self.creds = credopts.get_credentials(self.lp)
661         dns_conn = dns_connect(server, self.lp, self.creds)
662
663         client_version = dns_client_version(cli_ver)
664
665         typeid, res = dns_conn.DnssrvComplexOperation2(client_version,
666                                                        0, server, None,
667                                                        'EnumZones',
668                                                        dnsserver.DNSSRV_TYPEID_DWORD,
669                                                        request_filter)
670
671         if client_version == dnsserver.DNS_CLIENT_VERSION_W2K:
672             typeid = dnsserver.DNSSRV_TYPEID_ZONE_W2K
673         else:
674             typeid = dnsserver.DNSSRV_TYPEID_ZONE
675         print_enumzones(self.outf, typeid, res)
676
677
678 class cmd_zonecreate(Command):
679     """Create a zone."""
680
681     synopsis = '%prog <server> <zone> [options]'
682
683     takes_args = ['server', 'zone']
684
685     takes_optiongroups = {
686         "sambaopts": options.SambaOptions,
687         "versionopts": options.VersionOptions,
688         "credopts": options.CredentialsOptions,
689     }
690
691     takes_options = [
692         Option('--client-version', help='Client Version',
693                default='longhorn', metavar='w2k|dotnet|longhorn',
694                choices=['w2k', 'dotnet', 'longhorn'], dest='cli_ver')
695     ]
696
697     def run(self, server, zone, cli_ver, sambaopts=None, credopts=None,
698             versionopts=None):
699
700         self.lp = sambaopts.get_loadparm()
701         self.creds = credopts.get_credentials(self.lp)
702         dns_conn = dns_connect(server, self.lp, self.creds)
703
704         zone = zone.lower()
705
706         client_version = dns_client_version(cli_ver)
707         if client_version == dnsserver.DNS_CLIENT_VERSION_W2K:
708             typeid = dnsserver.DNSSRV_TYPEID_ZONE_CREATE_W2K
709             zone_create_info = dnsserver.DNS_RPC_ZONE_CREATE_INFO_W2K()
710             zone_create_info.pszZoneName = zone
711             zone_create_info.dwZoneType = dnsp.DNS_ZONE_TYPE_PRIMARY
712             zone_create_info.fAging = 0
713             zone_create_info.fDsIntegrated = 1
714             zone_create_info.fLoadExisting = 1
715         elif client_version == dnsserver.DNS_CLIENT_VERSION_DOTNET:
716             typeid = dnsserver.DNSSRV_TYPEID_ZONE_CREATE_DOTNET
717             zone_create_info = dnsserver.DNS_RPC_ZONE_CREATE_INFO_DOTNET()
718             zone_create_info.pszZoneName = zone
719             zone_create_info.dwZoneType = dnsp.DNS_ZONE_TYPE_PRIMARY
720             zone_create_info.fAging = 0
721             zone_create_info.fDsIntegrated = 1
722             zone_create_info.fLoadExisting = 1
723             zone_create_info.dwDpFlags = dnsserver.DNS_DP_DOMAIN_DEFAULT
724         else:
725             typeid = dnsserver.DNSSRV_TYPEID_ZONE_CREATE
726             zone_create_info = dnsserver.DNS_RPC_ZONE_CREATE_INFO_LONGHORN()
727             zone_create_info.pszZoneName = zone
728             zone_create_info.dwZoneType = dnsp.DNS_ZONE_TYPE_PRIMARY
729             zone_create_info.fAging = 0
730             zone_create_info.fDsIntegrated = 1
731             zone_create_info.fLoadExisting = 1
732             zone_create_info.dwDpFlags = dnsserver.DNS_DP_DOMAIN_DEFAULT
733
734         res = dns_conn.DnssrvOperation2(client_version, 0, server, None,
735                                         0, 'ZoneCreate', typeid,
736                                         zone_create_info)
737
738         typeid = dnsserver.DNSSRV_TYPEID_NAME_AND_PARAM
739         name_and_param = dnsserver.DNS_RPC_NAME_AND_PARAM()
740         name_and_param.pszNodeName = 'AllowUpdate'
741         name_and_param.dwParam = dnsp.DNS_ZONE_UPDATE_SECURE
742
743         try:
744             res = dns_conn.DnssrvOperation2(client_version, 0, server, zone,
745                                             0, 'ResetDwordProperty', typeid,
746                                             name_and_param)
747         except WERRORError as e:
748             if e.args[0] == werror.WERR_DNS_ERROR_ZONE_ALREADY_EXISTS:
749                 self.outf.write('Zone already exists.')
750             raise e
751
752         self.outf.write('Zone %s created successfully\n' % zone)
753
754
755 class cmd_zonedelete(Command):
756     """Delete a zone."""
757
758     synopsis = '%prog <server> <zone> [options]'
759
760     takes_args = ['server', 'zone']
761
762     takes_optiongroups = {
763         "sambaopts": options.SambaOptions,
764         "versionopts": options.VersionOptions,
765         "credopts": options.CredentialsOptions,
766     }
767
768     def run(self, server, zone, sambaopts=None, credopts=None,
769             versionopts=None):
770
771         self.lp = sambaopts.get_loadparm()
772         self.creds = credopts.get_credentials(self.lp)
773         dns_conn = dns_connect(server, self.lp, self.creds)
774
775         zone = zone.lower()
776         try:
777             res = dns_conn.DnssrvOperation2(dnsserver.DNS_CLIENT_VERSION_LONGHORN,
778                                             0, server, zone, 0, 'DeleteZoneFromDs',
779                                             dnsserver.DNSSRV_TYPEID_NULL,
780                                             None)
781         except WERRORError as e:
782             if e.args[0] == werror.WERR_DNS_ERROR_ZONE_DOES_NOT_EXIST:
783                 self.outf.write('Zone does not exist and so could not be deleted.')
784             raise e
785
786         self.outf.write('Zone %s deleted successfully\n' % zone)
787
788
789 class cmd_query(Command):
790     """Query a name."""
791
792     synopsis = '%prog <server> <zone> <name> <A|AAAA|CNAME|MX|NS|SOA|SRV|TXT|ALL> [options]'
793
794     takes_args = ['server', 'zone', 'name', 'rtype']
795
796     takes_optiongroups = {
797         "sambaopts": options.SambaOptions,
798         "versionopts": options.VersionOptions,
799         "credopts": options.CredentialsOptions,
800     }
801
802     takes_options = [
803         Option('--authority', help='Search authoritative records (default)',
804                action='store_true', dest='authority'),
805         Option('--cache', help='Search cached records',
806                action='store_true', dest='cache'),
807         Option('--glue', help='Search glue records',
808                action='store_true', dest='glue'),
809         Option('--root', help='Search root hints',
810                action='store_true', dest='root'),
811         Option('--additional', help='List additional records',
812                action='store_true', dest='additional'),
813         Option('--no-children', help='Do not list children',
814                action='store_true', dest='no_children'),
815         Option('--only-children', help='List only children',
816                action='store_true', dest='only_children')
817     ]
818
819     def run(self, server, zone, name, rtype, authority=False, cache=False,
820             glue=False, root=False, additional=False, no_children=False,
821             only_children=False, sambaopts=None, credopts=None,
822             versionopts=None):
823         record_type = dns_type_flag(rtype)
824
825         if name.find('*') != -1:
826             self.outf.write('use "@" to dump entire domain, looking up %s\n' %
827                             name)
828
829         select_flags = 0
830         if authority:
831             select_flags |= dnsserver.DNS_RPC_VIEW_AUTHORITY_DATA
832         if cache:
833             select_flags |= dnsserver.DNS_RPC_VIEW_CACHE_DATA
834         if glue:
835             select_flags |= dnsserver.DNS_RPC_VIEW_GLUE_DATA
836         if root:
837             select_flags |= dnsserver.DNS_RPC_VIEW_ROOT_HINT_DATA
838         if additional:
839             select_flags |= dnsserver.DNS_RPC_VIEW_ADDITIONAL_DATA
840         if no_children:
841             select_flags |= dnsserver.DNS_RPC_VIEW_NO_CHILDREN
842         if only_children:
843             select_flags |= dnsserver.DNS_RPC_VIEW_ONLY_CHILDREN
844
845         if select_flags == 0:
846             select_flags = dnsserver.DNS_RPC_VIEW_AUTHORITY_DATA
847
848         if select_flags == dnsserver.DNS_RPC_VIEW_ADDITIONAL_DATA:
849             self.outf.write('Specify either --authority or --root along with --additional.\n')
850             self.outf.write('Assuming --authority.\n')
851             select_flags |= dnsserver.DNS_RPC_VIEW_AUTHORITY_DATA
852
853         self.lp = sambaopts.get_loadparm()
854         self.creds = credopts.get_credentials(self.lp)
855         dns_conn = dns_connect(server, self.lp, self.creds)
856
857         try:
858             buflen, res = dns_conn.DnssrvEnumRecords2(
859                 dnsserver.DNS_CLIENT_VERSION_LONGHORN, 0, server, zone, name,
860                 None, record_type, select_flags, None, None)
861         except WERRORError as e:
862             if e.args[0] == werror.WERR_DNS_ERROR_NAME_DOES_NOT_EXIST:
863                 self.outf.write('Record or zone does not exist.')
864             raise e
865
866         print_dnsrecords(self.outf, res)
867
868
869 class cmd_roothints(Command):
870     """Query root hints."""
871
872     synopsis = '%prog <server> [<name>] [options]'
873
874     takes_args = ['server', 'name?']
875
876     takes_optiongroups = {
877         "sambaopts": options.SambaOptions,
878         "versionopts": options.VersionOptions,
879         "credopts": options.CredentialsOptions,
880     }
881
882     def run(self, server, name='.', sambaopts=None, credopts=None,
883             versionopts=None):
884         record_type = dnsp.DNS_TYPE_NS
885         select_flags = (dnsserver.DNS_RPC_VIEW_ROOT_HINT_DATA |
886                         dnsserver.DNS_RPC_VIEW_ADDITIONAL_DATA)
887
888         self.lp = sambaopts.get_loadparm()
889         self.creds = credopts.get_credentials(self.lp)
890         dns_conn = dns_connect(server, self.lp, self.creds)
891
892         buflen, res = dns_conn.DnssrvEnumRecords2(
893             dnsserver.DNS_CLIENT_VERSION_LONGHORN, 0, server, '..RootHints',
894             name, None, record_type, select_flags, None, None)
895         print_dnsrecords(self.outf, res)
896
897
898 class cmd_add_record(Command):
899     """Add a DNS record
900
901        For each type data contents are as follows:
902          A      ipv4_address_string
903          AAAA   ipv6_address_string
904          PTR    fqdn_string
905          CNAME  fqdn_string
906          NS     fqdn_string
907          MX     "fqdn_string preference"
908          SRV    "fqdn_string port priority weight"
909          TXT    "'string1' 'string2' ..."
910     """
911
912     synopsis = '%prog <server> <zone> <name> <A|AAAA|PTR|CNAME|NS|MX|SRV|TXT> <data>'
913
914     takes_args = ['server', 'zone', 'name', 'rtype', 'data']
915
916     takes_optiongroups = {
917         "sambaopts": options.SambaOptions,
918         "versionopts": options.VersionOptions,
919         "credopts": options.CredentialsOptions,
920     }
921
922     def run(self, server, zone, name, rtype, data, sambaopts=None,
923             credopts=None, versionopts=None):
924
925         if rtype.upper() not in ('A', 'AAAA', 'PTR', 'CNAME', 'NS', 'MX', 'SRV', 'TXT'):
926             raise CommandError('Adding record of type %s is not supported' % rtype)
927
928         record_type = dns_type_flag(rtype)
929         rec = data_to_dns_record(record_type, data)
930
931         self.lp = sambaopts.get_loadparm()
932         self.creds = credopts.get_credentials(self.lp)
933         dns_conn = dns_connect(server, self.lp, self.creds)
934
935         add_rec_buf = dnsserver.DNS_RPC_RECORD_BUF()
936         add_rec_buf.rec = rec
937
938         try:
939             dns_conn.DnssrvUpdateRecord2(dnsserver.DNS_CLIENT_VERSION_LONGHORN,
940                                          0, server, zone, name, add_rec_buf, None)
941         except WERRORError as e:
942             if e.args[0] == werror.WERR_DNS_ERROR_NAME_DOES_NOT_EXIST:
943                 self.outf.write('Zone does not exist; record could not be added.\n')
944             raise e
945
946         self.outf.write('Record added successfully\n')
947
948
949 class cmd_update_record(Command):
950     """Update a DNS record
951
952        For each type data contents are as follows:
953          A      ipv4_address_string
954          AAAA   ipv6_address_string
955          PTR    fqdn_string
956          CNAME  fqdn_string
957          NS     fqdn_string
958          MX     "fqdn_string preference"
959          SOA    "fqdn_dns fqdn_email serial refresh retry expire minimumttl"
960          SRV    "fqdn_string port priority weight"
961          TXT    "'string1' 'string2' ..."
962     """
963
964     synopsis = '%prog <server> <zone> <name> <A|AAAA|PTR|CNAME|NS|MX|SOA|SRV|TXT> <olddata> <newdata>'
965
966     takes_args = ['server', 'zone', 'name', 'rtype', 'olddata', 'newdata']
967
968     takes_optiongroups = {
969         "sambaopts": options.SambaOptions,
970         "versionopts": options.VersionOptions,
971         "credopts": options.CredentialsOptions,
972     }
973
974     def run(self, server, zone, name, rtype, olddata, newdata,
975             sambaopts=None, credopts=None, versionopts=None):
976
977         if rtype.upper() not in ('A', 'AAAA', 'PTR', 'CNAME', 'NS', 'MX', 'SOA', 'SRV', 'TXT'):
978             raise CommandError('Updating record of type %s is not supported' % rtype)
979
980         record_type = dns_type_flag(rtype)
981         rec = data_to_dns_record(record_type, newdata)
982
983         self.lp = sambaopts.get_loadparm()
984         self.creds = credopts.get_credentials(self.lp)
985         dns_conn = dns_connect(server, self.lp, self.creds)
986
987         rec_match = dns_record_match(dns_conn, server, zone, name, record_type,
988                                      olddata)
989         if not rec_match:
990             raise CommandError('Record or zone does not exist.')
991
992         # Copy properties from existing record to new record
993         rec.dwFlags = rec_match.dwFlags
994         rec.dwSerial = rec_match.dwSerial
995         rec.dwTtlSeconds = rec_match.dwTtlSeconds
996         rec.dwTimeStamp = rec_match.dwTimeStamp
997
998         add_rec_buf = dnsserver.DNS_RPC_RECORD_BUF()
999         add_rec_buf.rec = rec
1000
1001         del_rec_buf = dnsserver.DNS_RPC_RECORD_BUF()
1002         del_rec_buf.rec = rec_match
1003
1004         try:
1005             dns_conn.DnssrvUpdateRecord2(dnsserver.DNS_CLIENT_VERSION_LONGHORN,
1006                                          0,
1007                                          server,
1008                                          zone,
1009                                          name,
1010                                          add_rec_buf,
1011                                          del_rec_buf)
1012         except WERRORError as e:
1013             if e.args[0] == werror.WERR_DNS_ERROR_NAME_DOES_NOT_EXIST:
1014                 self.outf.write('Zone does not exist; record could not be updated.\n')
1015             raise e
1016
1017         self.outf.write('Record updated successfully\n')
1018
1019
1020 class cmd_delete_record(Command):
1021     """Delete a DNS record
1022
1023        For each type data contents are as follows:
1024          A      ipv4_address_string
1025          AAAA   ipv6_address_string
1026          PTR    fqdn_string
1027          CNAME  fqdn_string
1028          NS     fqdn_string
1029          MX     "fqdn_string preference"
1030          SRV    "fqdn_string port priority weight"
1031          TXT    "'string1' 'string2' ..."
1032     """
1033
1034     synopsis = '%prog <server> <zone> <name> <A|AAAA|PTR|CNAME|NS|MX|SRV|TXT> <data>'
1035
1036     takes_args = ['server', 'zone', 'name', 'rtype', 'data']
1037
1038     takes_optiongroups = {
1039         "sambaopts": options.SambaOptions,
1040         "versionopts": options.VersionOptions,
1041         "credopts": options.CredentialsOptions,
1042     }
1043
1044     def run(self, server, zone, name, rtype, data, sambaopts=None, credopts=None, versionopts=None):
1045
1046         if rtype.upper() not in ('A', 'AAAA', 'PTR', 'CNAME', 'NS', 'MX', 'SRV', 'TXT'):
1047             raise CommandError('Deleting record of type %s is not supported' % rtype)
1048
1049         record_type = dns_type_flag(rtype)
1050         rec = data_to_dns_record(record_type, data)
1051
1052         self.lp = sambaopts.get_loadparm()
1053         self.creds = credopts.get_credentials(self.lp)
1054         dns_conn = dns_connect(server, self.lp, self.creds)
1055
1056         del_rec_buf = dnsserver.DNS_RPC_RECORD_BUF()
1057         del_rec_buf.rec = rec
1058
1059         try:
1060             dns_conn.DnssrvUpdateRecord2(dnsserver.DNS_CLIENT_VERSION_LONGHORN,
1061                                          0,
1062                                          server,
1063                                          zone,
1064                                          name,
1065                                          None,
1066                                          del_rec_buf)
1067         except WERRORError as e:
1068             if e.args[0] == werror.WERR_DNS_ERROR_NAME_DOES_NOT_EXIST:
1069                 self.outf.write('Zone does not exist; record could not be deleted.\n')
1070             raise e
1071
1072         self.outf.write('Record deleted successfully\n')
1073
1074
1075 class cmd_cleanup_record(Command):
1076     """Cleanup DNS records for a DNS host.
1077
1078     example:
1079
1080         samba-tool dns cleanup dc1 dc1.samdom.test.site -U USER%PASSWORD
1081
1082     NOTE: This command in many cases will only mark the `dNSTombstoned` attr
1083     as `TRUE` on the DNS records. Querying will no longer return results but
1084     there may still be some placeholder entries in the database.
1085     """
1086
1087     synopsis = '%prog <server> <dnshostname>'
1088
1089     takes_args = ['server', 'dnshostname']
1090
1091     takes_optiongroups = {
1092         "sambaopts": options.SambaOptions,
1093         "versionopts": options.VersionOptions,
1094         "credopts": options.CredentialsOptions,
1095     }
1096
1097     takes_options = [
1098         Option("-v", "--verbose", help="Be verbose", action="store_true"),
1099         Option("-q", "--quiet", help="Be quiet", action="store_true"),
1100     ]
1101
1102     def run(self, server, dnshostname, sambaopts=None, credopts=None,
1103             versionopts=None, verbose=False, quiet=False):
1104         lp = sambaopts.get_loadparm()
1105         creds = credopts.get_credentials(lp)
1106
1107         logger = self.get_logger()
1108         if verbose:
1109             logger.setLevel(logging.DEBUG)
1110         elif quiet:
1111             logger.setLevel(logging.WARNING)
1112         else:
1113             logger.setLevel(logging.INFO)
1114
1115         samdb = SamDB(url="ldap://%s" % server,
1116                       session_info=system_session(),
1117                       credentials=creds, lp=lp)
1118
1119         remove_dc.remove_dns_references(samdb, logger, dnshostname,
1120                                         ignore_no_name=True)
1121
1122
1123 class cmd_dns(SuperCommand):
1124     """Domain Name Service (DNS) management."""
1125
1126     subcommands = {}
1127     subcommands['serverinfo'] = cmd_serverinfo()
1128     subcommands['zoneinfo'] = cmd_zoneinfo()
1129     subcommands['zonelist'] = cmd_zonelist()
1130     subcommands['zonecreate'] = cmd_zonecreate()
1131     subcommands['zonedelete'] = cmd_zonedelete()
1132     subcommands['query'] = cmd_query()
1133     subcommands['roothints'] = cmd_roothints()
1134     subcommands['add'] = cmd_add_record()
1135     subcommands['update'] = cmd_update_record()
1136     subcommands['delete'] = cmd_delete_record()
1137     subcommands['cleanup'] = cmd_cleanup_record()