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