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