cce0d66c21cab43fb5f69d0be89803ef19d0f4dd
[obnox/samba/samba-obnox.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, minttl=%d, ns=%s, email=%s' % (
359                     rec.data.dwSerialNo,
360                     rec.data.dwRefresh,
361                     rec.data.dwRetry,
362                     rec.data.dwExpire,
363                     rec.data.dwMinimumTtl,
364                     rec.data.NamePrimaryServer.str,
365                     rec.data.ZoneAdministratorEmail.str)
366     elif rec.wType == dnsp.DNS_TYPE_MX:
367         mesg = 'MX: %s (%d)' % (rec.data.nameExchange.str, rec.data.wPreference)
368     elif rec.wType == dnsp.DNS_TYPE_SRV:
369         mesg = 'SRV: %s (%d, %d, %d)' % (rec.data.nameTarget.str, rec.data.wPort,
370                                          rec.data.wPriority, rec.data.wWeight)
371     elif rec.wType == dnsp.DNS_TYPE_TXT:
372         slist = ['"%s"' % name.str for name in rec.data.str]
373         mesg = 'TXT: %s' % ','.join(slist)
374     else:
375         mesg = 'Unknown: '
376     outf.write('    %s (flags=%x, serial=%d, ttl=%d)\n' % (
377                 mesg, rec.dwFlags, rec.dwSerial, rec.dwTtlSeconds))
378
379
380 def print_dnsrecords(outf, records):
381     for rec in records.rec:
382         outf.write('  Name=%s, Records=%d, Children=%d\n' % (
383                     rec.dnsNodeName.str,
384                     rec.wRecordCount,
385                     rec.dwChildCount))
386         for dns_rec in rec.records:
387                 print_dns_record(outf, dns_rec)
388
389
390 #
391 # Always create a copy of strings when creating DNS_RPC_RECORDs
392 # to overcome the bug in pidl generated python bindings.
393 #
394
395 class ARecord(dnsserver.DNS_RPC_RECORD):
396     def __init__(self, ip_addr, serial=1, ttl=900, rank=dnsp.DNS_RANK_ZONE,
397                     node_flag=0):
398         super(ARecord, self).__init__()
399         self.wType = dnsp.DNS_TYPE_A
400         self.dwFlags = rank | node_flag
401         self.dwSerial = serial
402         self.dwTtlSeconds = ttl
403         self._ip_addr = ip_addr[:]
404         self.data = self._ip_addr
405
406
407 class AAAARecord(dnsserver.DNS_RPC_RECORD):
408
409     def __init__(self, ip6_addr, serial=1, ttl=900, rank=dnsp.DNS_RANK_ZONE,
410                     node_flag=0):
411         super(AAAARecord, self).__init__()
412         self.wType = dnsp.DNS_TYPE_AAAA
413         self.dwFlags = rank | node_flag
414         self.dwSerial = serial
415         self.dwTtlSeconds = ttl
416         self._ip6_addr = ip6_addr[:]
417         self.data = self._ip6_addr
418
419
420 class PTRRecord(dnsserver.DNS_RPC_RECORD):
421
422     def __init__(self, ptr, serial=1, ttl=900, rank=dnsp.DNS_RANK_ZONE,
423                  node_flag=0):
424         super(PTRRecord, self).__init__()
425         self.wType = dnsp.DNS_TYPE_PTR
426         self.dwFlags = rank | node_flag
427         self.dwSerial = serial
428         self.dwTtlSeconds = ttl
429         self._ptr = ptr[:]
430         ptr_name = dnsserver.DNS_RPC_NAME()
431         ptr_name.str = self._ptr
432         ptr_name.len = len(ptr)
433         self.data = ptr_name
434
435
436 class CNameRecord(dnsserver.DNS_RPC_RECORD):
437
438     def __init__(self, cname, serial=1, ttl=900, rank=dnsp.DNS_RANK_ZONE,
439                     node_flag=0):
440         super(CNameRecord, self).__init__()
441         self.wType = dnsp.DNS_TYPE_CNAME
442         self.dwFlags = rank | node_flag
443         self.dwSerial = serial
444         self.dwTtlSeconds = ttl
445         self._cname = cname[:]
446         cname_name = dnsserver.DNS_RPC_NAME()
447         cname_name.str = self._cname
448         cname_name.len = len(cname)
449         self.data = cname_name
450
451
452 class NSRecord(dnsserver.DNS_RPC_RECORD):
453
454     def __init__(self, dns_server, serial=1, ttl=900, rank=dnsp.DNS_RANK_ZONE,
455                     node_flag=0):
456         super(NSRecord, self).__init__()
457         self.wType = dnsp.DNS_TYPE_NS
458         self.dwFlags = rank | node_flag
459         self.dwSerial = serial
460         self.dwTtlSeconds = ttl
461         self._dns_server = dns_server[:]
462         ns = dnsserver.DNS_RPC_NAME()
463         ns.str = self._dns_server
464         ns.len = len(dns_server)
465         self.data = ns
466
467
468 class MXRecord(dnsserver.DNS_RPC_RECORD):
469
470     def __init__(self, mail_server, preference, serial=1, ttl=900,
471                  rank=dnsp.DNS_RANK_ZONE, node_flag=0):
472         super(MXRecord, self).__init__()
473         self.wType = dnsp.DNS_TYPE_MX
474         self.dwFlags = rank | node_flag
475         self.dwSerial = serial
476         self.dwTtlSeconds = ttl
477         self._mail_server = mail_server[:]
478         mx = dnsserver.DNS_RPC_RECORD_NAME_PREFERENCE()
479         mx.wPreference = preference
480         mx.nameExchange.str = self._mail_server
481         mx.nameExchange.len = len(mail_server)
482         self.data = mx
483
484
485 class SOARecord(dnsserver.DNS_RPC_RECORD):
486
487     def __init__(self, mname, rname, serial=1, refresh=900, retry=600,
488                  expire=86400, minimum=3600, ttl=3600, rank=dnsp.DNS_RANK_ZONE,
489                  node_flag=dnsp.DNS_RPC_FLAG_AUTH_ZONE_ROOT):
490         super(SOARecord, self).__init__()
491         self.wType = dnsp.DNS_TYPE_SOA
492         self.dwFlags = rank | node_flag
493         self.dwSerial = serial
494         self.dwTtlSeconds = ttl
495         self._mname = mname[:]
496         self._rname = rname[:]
497         soa = dnsserver.DNS_RPC_RECORD_SOA()
498         soa.dwSerialNo = serial
499         soa.dwRefresh = refresh
500         soa.dwRetry = retry
501         soa.dwExpire = expire
502         soa.dwMinimumTtl = minimum
503         soa.NamePrimaryServer.str = self._mname
504         soa.NamePrimaryServer.len = len(mname)
505         soa.ZoneAdministratorEmail.str = self._rname
506         soa.ZoneAdministratorEmail.len = len(rname)
507         self.data = soa
508
509
510 class SRVRecord(dnsserver.DNS_RPC_RECORD):
511
512     def __init__(self, target, port, priority=0, weight=100, serial=1, ttl=900,
513                 rank=dnsp.DNS_RANK_ZONE, node_flag=0):
514         super(SRVRecord, self).__init__()
515         self.wType = dnsp.DNS_TYPE_SRV
516         self.dwFlags = rank | node_flag
517         self.dwSerial = serial
518         self.dwTtlSeconds = ttl
519         self._target = target[:]
520         srv = dnsserver.DNS_RPC_RECORD_SRV()
521         srv.wPriority = priority
522         srv.wWeight = weight
523         srv.wPort = port
524         srv.nameTarget.str = self._target
525         srv.nameTarget.len = len(target)
526         self.data = srv
527
528
529 class TXTRecord(dnsserver.DNS_RPC_RECORD):
530
531     def __init__(self, slist, serial=1, ttl=900, rank=dnsp.DNS_RANK_ZONE,
532                 node_flag=0):
533         super(TXTRecord, self).__init__()
534         self.wType = dnsp.DNS_TYPE_TXT
535         self.dwFlags = rank | node_flag
536         self.dwSerial = serial
537         self.dwTtlSeconds = ttl
538         self._slist = []
539         for s in slist:
540             self._slist.append(s[:])
541         names = []
542         for s in self._slist:
543             name = dnsserver.DNS_RPC_NAME()
544             name.str = s
545             name.len = len(s)
546             names.append(name)
547         txt = dnsserver.DNS_RPC_RECORD_STRING()
548         txt.count = len(slist)
549         txt.str = names
550         self.data = txt
551
552
553 # Convert data into a dns record
554 def data_to_dns_record(record_type, data):
555     if record_type == dnsp.DNS_TYPE_A:
556         rec = ARecord(data)
557     elif record_type == dnsp.DNS_TYPE_AAAA:
558         rec = AAAARecord(data)
559     elif record_type == dnsp.DNS_TYPE_PTR:
560         rec = PTRRecord(data)
561     elif record_type == dnsp.DNS_TYPE_CNAME:
562         rec = CNameRecord(data)
563     elif record_type == dnsp.DNS_TYPE_NS:
564         rec = NSRecord(data)
565     elif record_type == dnsp.DNS_TYPE_MX:
566         tmp = data.split(' ')
567         if len(tmp) != 2:
568             raise CommandError('Data requires 2 elements - mail_server, preference')
569         mail_server = tmp[0]
570         preference = int(tmp[1])
571         rec = MXRecord(mail_server, preference)
572     elif record_type == dnsp.DNS_TYPE_SRV:
573         tmp = data.split(' ')
574         if len(tmp) != 4:
575             raise CommandError('Data requires 4 elements - server, port, priority, weight')
576         server = tmp[0]
577         port = int(tmp[1])
578         priority = int(tmp[2])
579         weight = int(tmp[3])
580         rec = SRVRecord(server, port, priority=priority, weight=weight)
581     elif record_type == dnsp.DNS_TYPE_SOA:
582         tmp = data.split(' ')
583         if len(tmp) != 7:
584             raise CommandError('Data requires 7 elements - nameserver, email, serial, '
585                                'refresh, retry, expire, minimumttl')
586         nameserver = tmp[0]
587         email = tmp[1]
588         serial = int(tmp[2])
589         refresh = int(tmp[3])
590         retry = int(tmp[4])
591         expire = int(tmp[5])
592         minimum = int(tmp[6])
593         rec = SOARecord(nameserver, email, serial=serial, refresh=refresh,
594                         retry=retry, expire=expire, minimum=minimum)
595     elif record_type == dnsp.DNS_TYPE_TXT:
596         slist = shlex.split(data)
597         rec = TXTRecord(slist)
598     else:
599         raise CommandError('Unsupported record type')
600     return rec
601
602
603 # Match dns name (of type DNS_RPC_NAME)
604 def dns_name_equal(n1, n2):
605     return n1.str.rstrip('.').lower() == n2.str.rstrip('.').lower()
606
607
608 # Match a dns record with specified data
609 def dns_record_match(dns_conn, server, zone, name, record_type, data):
610     urec = data_to_dns_record(record_type, data)
611
612     select_flags = dnsserver.DNS_RPC_VIEW_AUTHORITY_DATA
613
614     try:
615         buflen, res = dns_conn.DnssrvEnumRecords2(
616             dnsserver.DNS_CLIENT_VERSION_LONGHORN, 0, server, zone, name, None,
617             record_type, select_flags, None, None)
618     except RuntimeError, e:
619         return None
620
621     if not res or res.count == 0:
622         return None
623
624     rec_match = None
625     for rec in res.rec[0].records:
626         if rec.wType != record_type:
627             continue
628
629         found = False
630         if record_type == dnsp.DNS_TYPE_A:
631             if rec.data == urec.data:
632                 found = True
633         elif record_type == dnsp.DNS_TYPE_AAAA:
634             if rec.data == urec.data:
635                 found = True
636         elif record_type == dnsp.DNS_TYPE_PTR:
637             if dns_name_equal(rec.data, urec.data):
638                 found = True
639         elif record_type == dnsp.DNS_TYPE_CNAME:
640             if dns_name_equal(rec.data, urec.data):
641                 found = True
642         elif record_type == dnsp.DNS_TYPE_NS:
643             if dns_name_equal(rec.data, urec.data):
644                 found = True
645         elif record_type == dnsp.DNS_TYPE_MX:
646             if dns_name_equal(rec.data.nameExchange, urec.data.nameExchange) and \
647                rec.data.wPreference == urec.data.wPreference:
648                 found = True
649         elif record_type == dnsp.DNS_TYPE_SRV:
650             if rec.data.wPriority == urec.data.wPriority and \
651                rec.data.wWeight == urec.data.wWeight and \
652                rec.data.wPort == urec.data.wPort and \
653                dns_name_equal(rec.data.nameTarget, urec.data.nameTarget):
654                 found = True
655         elif record_type == dnsp.DNS_TYPE_SOA:
656             if rec.data.dwSerialNo == urec.data.dwSerialNo and \
657                rec.data.dwRefresh == urec.data.dwRefresh and \
658                rec.data.dwRetry == urec.data.dwRetry and \
659                rec.data.dwExpire == urec.data.dwExpire and \
660                rec.data.dwMinimumTtl == urec.data.dwMinimumTtl and \
661                dns_name_equal(rec.data.NamePrimaryServer,
662                               urec.data.NamePrimaryServer) and \
663                dns_name_equal(rec.data.ZoneAdministratorEmail,
664                               urec.data.ZoneAdministratorEmail):
665                 found = True
666         elif record_type == dnsp.DNS_TYPE_TXT:
667             if rec.data.count == urec.data.count:
668                 found = True
669                 for i in xrange(rec.data.count):
670                     found = found and \
671                             (rec.data.str[i].str == urec.data.str[i].str)
672
673         if found:
674             rec_match = rec
675             break
676
677     return rec_match
678
679
680 class cmd_serverinfo(Command):
681     """Query for Server information."""
682
683     synopsis = '%prog <server> [options]'
684
685     takes_args = [ 'server' ]
686
687     takes_optiongroups = {
688         "sambaopts": options.SambaOptions,
689         "versionopts": options.VersionOptions,
690         "credopts": options.CredentialsOptions,
691     }
692
693     takes_options = [
694         Option('--client-version', help='Client Version',
695                 default='longhorn', metavar='w2k|dotnet|longhorn',
696                 choices=['w2k','dotnet','longhorn'], dest='cli_ver'),
697     ]
698
699     def run(self, server, cli_ver, sambaopts=None, credopts=None,
700             versionopts=None):
701         self.lp = sambaopts.get_loadparm()
702         self.creds = credopts.get_credentials(self.lp)
703         dns_conn = dns_connect(server, self.lp, self.creds)
704
705         client_version = dns_client_version(cli_ver)
706
707         typeid, res = dns_conn.DnssrvQuery2(client_version, 0, server,
708                                             None, 'ServerInfo')
709         print_serverinfo(self.outf, typeid, res)
710
711
712 class cmd_zoneinfo(Command):
713     """Query for zone information."""
714
715     synopsis = '%prog <server> <zone> [options]'
716
717     takes_args = [ 'server', 'zone' ]
718
719     takes_optiongroups = {
720         "sambaopts": options.SambaOptions,
721         "versionopts": options.VersionOptions,
722         "credopts": options.CredentialsOptions,
723     }
724
725     takes_options = [
726         Option('--client-version', help='Client Version',
727                 default='longhorn', metavar='w2k|dotnet|longhorn',
728                 choices=['w2k','dotnet','longhorn'], dest='cli_ver'),
729     ]
730
731     def run(self, server, zone, cli_ver, sambaopts=None, credopts=None,
732             versionopts=None):
733         self.lp = sambaopts.get_loadparm()
734         self.creds = credopts.get_credentials(self.lp)
735         dns_conn = dns_connect(server, self.lp, self.creds)
736
737         client_version = dns_client_version(cli_ver)
738
739         typeid, res = dns_conn.DnssrvQuery2(client_version, 0, server, zone,
740                                             'ZoneInfo')
741         print_zoneinfo(self.outf, typeid, res)
742
743
744 class cmd_zonelist(Command):
745     """Query for zones."""
746
747     synopsis = '%prog <server> [options]'
748
749     takes_args = [ 'server' ]
750
751     takes_optiongroups = {
752         "sambaopts": options.SambaOptions,
753         "versionopts": options.VersionOptions,
754         "credopts": options.CredentialsOptions,
755     }
756
757     takes_options = [
758         Option('--client-version', help='Client Version',
759                 default='longhorn', metavar='w2k|dotnet|longhorn',
760                 choices=['w2k','dotnet','longhorn'], dest='cli_ver'),
761         Option('--primary', help='List primary zones (default)',
762                 action='store_true', dest='primary'),
763         Option('--secondary', help='List secondary zones',
764                 action='store_true', dest='secondary'),
765         Option('--cache', help='List cached zones',
766                 action='store_true', dest='cache'),
767         Option('--auto', help='List automatically created zones',
768                 action='store_true', dest='auto'),
769         Option('--forward', help='List forward zones',
770                 action='store_true', dest='forward'),
771         Option('--reverse', help='List reverse zones',
772                 action='store_true', dest='reverse'),
773         Option('--ds', help='List directory integrated zones',
774                 action='store_true', dest='ds'),
775         Option('--non-ds', help='List non-directory zones',
776                 action='store_true', dest='nonds')
777     ]
778
779     def run(self, server, cli_ver, primary=False, secondary=False, cache=False,
780                 auto=False, forward=False, reverse=False, ds=False, nonds=False,
781                 sambaopts=None, credopts=None, versionopts=None):
782         request_filter = 0
783
784         if primary:
785             request_filter |= dnsserver.DNS_ZONE_REQUEST_PRIMARY
786         if secondary:
787             request_filter |= dnsserver.DNS_ZONE_REQUEST_SECONDARY
788         if cache:
789             request_filter |= dnsserver.DNS_ZONE_REQUEST_CACHE
790         if auto:
791             request_filter |= dnsserver.DNS_ZONE_REQUEST_AUTO
792         if forward:
793             request_filter |= dnsserver.DNS_ZONE_REQUEST_FORWARD
794         if reverse:
795             request_filter |= dnsserver.DNS_ZONE_REQUEST_REVERSE
796         if ds:
797             request_filter |= dnsserver.DNS_ZONE_REQUEST_DS
798         if nonds:
799             request_filter |= dnsserver.DNS_ZONE_REQUEST_NON_DS
800
801         if request_filter == 0:
802             request_filter = dnsserver.DNS_ZONE_REQUEST_PRIMARY
803
804         self.lp = sambaopts.get_loadparm()
805         self.creds = credopts.get_credentials(self.lp)
806         dns_conn = dns_connect(server, self.lp, self.creds)
807
808         client_version = dns_client_version(cli_ver)
809
810         typeid, res = dns_conn.DnssrvComplexOperation2(client_version,
811                                                         0, server, None,
812                                                         'EnumZones',
813                                                         dnsserver.DNSSRV_TYPEID_DWORD,
814                                                         request_filter)
815
816         if client_version == dnsserver.DNS_CLIENT_VERSION_W2K:
817             typeid = dnsserver.DNSSRV_TYPEID_ZONE_W2K
818         else:
819             typeid = dnsserver.DNSSRV_TYPEID_ZONE
820         print_enumzones(self.outf, typeid, res)
821
822
823 class cmd_zonecreate(Command):
824     """Create a zone."""
825
826     synopsis = '%prog <server> <zone> [options]'
827
828     takes_args = [ 'server', 'zone' ]
829
830     takes_optiongroups = {
831         "sambaopts": options.SambaOptions,
832         "versionopts": options.VersionOptions,
833         "credopts": options.CredentialsOptions,
834     }
835
836     takes_options = [
837         Option('--client-version', help='Client Version',
838                 default='longhorn', metavar='w2k|dotnet|longhorn',
839                 choices=['w2k','dotnet','longhorn'], dest='cli_ver')
840     ]
841
842     def run(self, server, zone, cli_ver, sambaopts=None, credopts=None,
843             versionopts=None):
844
845         self.lp = sambaopts.get_loadparm()
846         self.creds = credopts.get_credentials(self.lp)
847         dns_conn = dns_connect(server, self.lp, self.creds)
848
849         zone = zone.lower()
850
851         client_version = dns_client_version(cli_ver)
852         if client_version == dnsserver.DNS_CLIENT_VERSION_W2K:
853             typeid = dnsserver.DNSSRV_TYPEID_ZONE_CREATE_W2K
854             zone_create_info = dnsserver.DNS_RPC_ZONE_CREATE_INFO_W2K()
855             zone_create_info.pszZoneName = zone
856             zone_create_info.dwZoneType = dnsp.DNS_ZONE_TYPE_PRIMARY
857             zone_create_info.fAllowUpdate = dnsp.DNS_ZONE_UPDATE_SECURE
858             zone_create_info.fAging = 0
859             zone_create_info.fDsIntegrated = 1
860             zone_create_info.fLoadExisting = 1
861         elif client_version == dnsserver.DNS_CLIENT_VERSION_DOTNET:
862             typeid = dnsserver.DNSSRV_TYPEID_ZONE_CREATE_DOTNET
863             zone_create_info = dnsserver.DNS_RPC_ZONE_CREATE_INFO_DOTNET()
864             zone_create_info.pszZoneName = zone
865             zone_create_info.dwZoneType = dnsp.DNS_ZONE_TYPE_PRIMARY
866             zone_create_info.fAllowUpdate = dnsp.DNS_ZONE_UPDATE_SECURE
867             zone_create_info.fAging = 0
868             zone_create_info.fDsIntegrated = 1
869             zone_create_info.fLoadExisting = 1
870             zone_create_info.dwDpFlags = dnsserver.DNS_DP_DOMAIN_DEFAULT
871         else:
872             typeid = dnsserver.DNSSRV_TYPEID_ZONE_CREATE
873             zone_create_info = dnsserver.DNS_RPC_ZONE_CREATE_INFO_LONGHORN()
874             zone_create_info.pszZoneName = zone
875             zone_create_info.dwZoneType = dnsp.DNS_ZONE_TYPE_PRIMARY
876             zone_create_info.fAllowUpdate = dnsp.DNS_ZONE_UPDATE_SECURE
877             zone_create_info.fAging = 0
878             zone_create_info.fDsIntegrated = 1
879             zone_create_info.fLoadExisting = 1
880             zone_create_info.dwDpFlags = dnsserver.DNS_DP_DOMAIN_DEFAULT
881
882         res = dns_conn.DnssrvOperation2(client_version, 0, server, None,
883                                         0, 'ZoneCreate', typeid,
884                                         zone_create_info)
885         self.outf.write('Zone %s created successfully\n' % zone)
886
887
888 class cmd_zonedelete(Command):
889     """Delete a zone."""
890
891     synopsis = '%prog <server> <zone> [options]'
892
893     takes_args = [ 'server', 'zone' ]
894
895     takes_optiongroups = {
896         "sambaopts": options.SambaOptions,
897         "versionopts": options.VersionOptions,
898         "credopts": options.CredentialsOptions,
899     }
900
901     def run(self, server, zone, sambaopts=None, credopts=None,
902             versionopts=None):
903
904         self.lp = sambaopts.get_loadparm()
905         self.creds = credopts.get_credentials(self.lp)
906         dns_conn = dns_connect(server, self.lp, self.creds)
907
908         zone = zone.lower()
909         res = dns_conn.DnssrvOperation2(dnsserver.DNS_CLIENT_VERSION_LONGHORN,
910                                         0, server, zone, 0, 'DeleteZoneFromDs',
911                                         dnsserver.DNSSRV_TYPEID_NULL,
912                                         None)
913         self.outf.write('Zone %s delete successfully\n' % zone)
914
915
916 class cmd_query(Command):
917     """Query a name."""
918
919     synopsis = '%prog <server> <zone> <name> <A|AAAA|CNAME|MX|NS|SOA|SRV|TXT|ALL> [options]'
920
921     takes_args = [ 'server', 'zone', 'name', 'rtype' ]
922
923     takes_optiongroups = {
924         "sambaopts": options.SambaOptions,
925         "versionopts": options.VersionOptions,
926         "credopts": options.CredentialsOptions,
927     }
928
929     takes_options = [
930         Option('--authority', help='Search authoritative records (default)',
931                 action='store_true', dest='authority'),
932         Option('--cache', help='Search cached records',
933                 action='store_true', dest='cache'),
934         Option('--glue', help='Search glue records',
935                 action='store_true', dest='glue'),
936         Option('--root', help='Search root hints',
937                 action='store_true', dest='root'),
938         Option('--additional', help='List additional records',
939                 action='store_true', dest='additional'),
940         Option('--no-children', help='Do not list children',
941                 action='store_true', dest='no_children'),
942         Option('--only-children', help='List only children',
943                 action='store_true', dest='only_children')
944     ]
945
946     def run(self, server, zone, name, rtype, authority=False, cache=False,
947             glue=False, root=False, additional=False, no_children=False,
948             only_children=False, sambaopts=None, credopts=None,
949             versionopts=None):
950         record_type = dns_type_flag(rtype)
951
952         select_flags = 0
953         if authority:
954             select_flags |= dnsserver.DNS_RPC_VIEW_AUTHORITY_DATA
955         if cache:
956             select_flags |= dnsserver.DNS_RPC_VIEW_CACHE_DATA
957         if glue:
958             select_flags |= dnsserver.DNS_RPC_VIEW_GLUE_DATA
959         if root:
960             select_flags |= dnsserver.DNS_RPC_VIEW_ROOT_HINT_DATA
961         if additional:
962             select_flags |= dnsserver.DNS_RPC_VIEW_ADDITIONAL_DATA
963         if no_children:
964             select_flags |= dnsserver.DNS_RPC_VIEW_NO_CHILDREN
965         if only_children:
966             select_flags |= dnsserver.DNS_RPC_VIEW_ONLY_CHILDREN
967
968         if select_flags == 0:
969             select_flags = dnsserver.DNS_RPC_VIEW_AUTHORITY_DATA
970
971         if select_flags == dnsserver.DNS_RPC_VIEW_ADDITIONAL_DATA:
972             self.outf.write('Specify either --authority or --root along with --additional.\n')
973             self.outf.write('Assuming --authority.\n')
974             select_flags |= dnsserver.DNS_RPC_VIEW_AUTHORITY_DATA
975
976         self.lp = sambaopts.get_loadparm()
977         self.creds = credopts.get_credentials(self.lp)
978         dns_conn = dns_connect(server, self.lp, self.creds)
979
980         buflen, res = dns_conn.DnssrvEnumRecords2(
981                 dnsserver.DNS_CLIENT_VERSION_LONGHORN, 0, server, zone, name,
982                 None, record_type, select_flags, None, None)
983         print_dnsrecords(self.outf, res)
984
985
986 class cmd_roothints(Command):
987     """Query root hints."""
988
989     synopsis = '%prog <server> [<name>] [options]'
990
991     takes_args = [ 'server', 'name?' ]
992
993     takes_optiongroups = {
994         "sambaopts": options.SambaOptions,
995         "versionopts": options.VersionOptions,
996         "credopts": options.CredentialsOptions,
997     }
998
999     def run(self, server, name='.', sambaopts=None, credopts=None,
1000             versionopts=None):
1001         record_type = dnsp.DNS_TYPE_NS
1002         select_flags = (dnsserver.DNS_RPC_VIEW_ROOT_HINT_DATA |
1003                         dnsserver.DNS_RPC_VIEW_ADDITIONAL_DATA)
1004
1005         self.lp = sambaopts.get_loadparm()
1006         self.creds = credopts.get_credentials(self.lp)
1007         dns_conn = dns_connect(server, self.lp, self.creds)
1008
1009         buflen, res = dns_conn.DnssrvEnumRecords2(
1010             dnsserver.DNS_CLIENT_VERSION_LONGHORN, 0, server, '..RootHints',
1011             name, None, record_type, select_flags, None, None)
1012         print_dnsrecords(self.outf, res)
1013
1014
1015 class cmd_add_record(Command):
1016     """Add a DNS record
1017
1018        For each type data contents are as follows:
1019          A      ipv4_address_string
1020          AAAA   ipv6_address_string
1021          PTR    fqdn_string
1022          CNAME  fqdn_string
1023          NS     fqdn_string
1024          MX     "fqdn_string preference"
1025          SRV    "fqdn_string port priority weight"
1026          TXT    "'string1' 'string2' ..."
1027     """
1028
1029     synopsis = '%prog <server> <zone> <name> <A|AAAA|PTR|CNAME|NS|MX|SRV|TXT> <data>'
1030
1031     takes_args = [ 'server', 'zone', 'name', 'rtype', 'data' ]
1032
1033     takes_optiongroups = {
1034         "sambaopts": options.SambaOptions,
1035         "versionopts": options.VersionOptions,
1036         "credopts": options.CredentialsOptions,
1037     }
1038
1039     def run(self, server, zone, name, rtype, data, sambaopts=None,
1040             credopts=None, versionopts=None):
1041
1042         if rtype.upper() not in ('A','AAAA','PTR','CNAME','NS','MX','SRV','TXT'):
1043             raise CommandError('Adding record of type %s is not supported' % rtype)
1044
1045         record_type = dns_type_flag(rtype)
1046         rec = data_to_dns_record(record_type, data)
1047
1048         self.lp = sambaopts.get_loadparm()
1049         self.creds = credopts.get_credentials(self.lp)
1050         dns_conn = dns_connect(server, self.lp, self.creds)
1051
1052         rec_match = dns_record_match(dns_conn, server, zone, name, record_type,
1053                 data)
1054         if rec_match is not None:
1055             raise CommandError('Record already exists')
1056
1057         add_rec_buf = dnsserver.DNS_RPC_RECORD_BUF()
1058         add_rec_buf.rec = rec
1059
1060         dns_conn.DnssrvUpdateRecord2(dnsserver.DNS_CLIENT_VERSION_LONGHORN,
1061                                      0, server, zone, name, add_rec_buf, None)
1062         self.outf.write('Record added successfully\n')
1063
1064
1065 class cmd_update_record(Command):
1066     """Update a DNS record
1067
1068        For each type data contents are as follows:
1069          A      ipv4_address_string
1070          AAAA   ipv6_address_string
1071          PTR    fqdn_string
1072          CNAME  fqdn_string
1073          NS     fqdn_string
1074          MX     "fqdn_string preference"
1075          SOA    "fqdn_dns fqdn_email serial refresh retry expire minimumttl"
1076          SRV    "fqdn_string port priority weight"
1077          TXT    "'string1' 'string2' ..."
1078     """
1079
1080     synopsis = '%prog <server> <zone> <name> <A|AAAA|PTR|CNAME|NS|MX|SOA|SRV|TXT> <olddata> <newdata>'
1081
1082     takes_args = [ 'server', 'zone', 'name', 'rtype', 'olddata', 'newdata' ]
1083
1084     takes_optiongroups = {
1085         "sambaopts": options.SambaOptions,
1086         "versionopts": options.VersionOptions,
1087         "credopts": options.CredentialsOptions,
1088     }
1089
1090     def run(self, server, zone, name, rtype, olddata, newdata,
1091                 sambaopts=None, credopts=None, versionopts=None):
1092
1093         if rtype.upper() not in ('A','AAAA','PTR','CNAME','NS','MX','SOA','SRV','TXT'):
1094             raise CommandError('Updating record of type %s is not supported' % rtype)
1095
1096         record_type = dns_type_flag(rtype)
1097         rec = data_to_dns_record(record_type, newdata)
1098
1099         self.lp = sambaopts.get_loadparm()
1100         self.creds = credopts.get_credentials(self.lp)
1101         dns_conn = dns_connect(server, self.lp, self.creds)
1102
1103         rec_match = dns_record_match(dns_conn, server, zone, name, record_type,
1104                 olddata)
1105         if not rec_match:
1106             raise CommandError('Record does not exist')
1107
1108         # Copy properties from existing record to new record
1109         rec.dwFlags = rec_match.dwFlags
1110         rec.dwSerial = rec_match.dwSerial
1111         rec.dwTtlSeconds = rec_match.dwTtlSeconds
1112         rec.dwTimeStamp = rec_match.dwTimeStamp
1113
1114         add_rec_buf = dnsserver.DNS_RPC_RECORD_BUF()
1115         add_rec_buf.rec = rec
1116
1117         del_rec_buf = dnsserver.DNS_RPC_RECORD_BUF()
1118         del_rec_buf.rec = rec_match
1119
1120         dns_conn.DnssrvUpdateRecord2(dnsserver.DNS_CLIENT_VERSION_LONGHORN,
1121                                         0,
1122                                         server,
1123                                         zone,
1124                                         name,
1125                                         add_rec_buf,
1126                                         del_rec_buf)
1127         self.outf.write('Record updated successfully\n')
1128
1129
1130 class cmd_delete_record(Command):
1131     """Delete a DNS record
1132
1133        For each type data contents are as follows:
1134          A      ipv4_address_string
1135          AAAA   ipv6_address_string
1136          PTR    fqdn_string
1137          CNAME  fqdn_string
1138          NS     fqdn_string
1139          MX     "fqdn_string preference"
1140          SRV    "fqdn_string port priority weight"
1141          TXT    "'string1' 'string2' ..."
1142     """
1143
1144     synopsis = '%prog <server> <zone> <name> <A|AAAA|PTR|CNAME|NS|MX|SRV|TXT> <data>'
1145
1146     takes_args = [ 'server', 'zone', 'name', 'rtype', 'data' ]
1147
1148     takes_optiongroups = {
1149         "sambaopts": options.SambaOptions,
1150         "versionopts": options.VersionOptions,
1151         "credopts": options.CredentialsOptions,
1152     }
1153
1154     def run(self, server, zone, name, rtype, data, sambaopts=None, credopts=None, versionopts=None):
1155
1156         if rtype.upper() not in ('A','AAAA','PTR','CNAME','NS','MX','SRV','TXT'):
1157             raise CommandError('Deleting record of type %s is not supported' % rtype)
1158
1159         record_type = dns_type_flag(rtype)
1160
1161         self.lp = sambaopts.get_loadparm()
1162         self.creds = credopts.get_credentials(self.lp)
1163         dns_conn = dns_connect(server, self.lp, self.creds)
1164
1165         rec_match = dns_record_match(dns_conn, server, zone, name, record_type, data)
1166         if not rec_match:
1167             raise CommandError('Record does not exist')
1168
1169         del_rec_buf = dnsserver.DNS_RPC_RECORD_BUF()
1170         del_rec_buf.rec = rec_match
1171
1172         dns_conn.DnssrvUpdateRecord2(dnsserver.DNS_CLIENT_VERSION_LONGHORN,
1173                                         0,
1174                                         server,
1175                                         zone,
1176                                         name,
1177                                         None,
1178                                         del_rec_buf)
1179         self.outf.write('Record deleted successfully\n')
1180
1181
1182 class cmd_dns(SuperCommand):
1183     """Domain Name Service (DNS) management."""
1184
1185     subcommands = {}
1186     subcommands['serverinfo'] = cmd_serverinfo()
1187     subcommands['zoneinfo'] = cmd_zoneinfo()
1188     subcommands['zonelist'] = cmd_zonelist()
1189     subcommands['zonecreate'] = cmd_zonecreate()
1190     subcommands['zonedelete'] = cmd_zonedelete()
1191     subcommands['query'] = cmd_query()
1192     subcommands['roothints'] = cmd_roothints()
1193     subcommands['add'] = cmd_add_record()
1194     subcommands['update'] = cmd_update_record()
1195     subcommands['delete'] = cmd_delete_record()