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