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