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