3 # Copyright (C) Amitay Isaacs 2011-2012
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.
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.
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/>.
19 import samba.getopt as options
20 from samba import WERRORError
21 from samba import werror
22 from struct import pack
23 from socket import inet_ntoa
24 from socket import inet_ntop
25 from socket import AF_INET
26 from socket import AF_INET6
29 from samba.netcmd import (
35 from samba.dcerpc import dnsp, dnsserver
37 from samba.dnsserver import ARecord, AAAARecord, PTRRecord, CNameRecord, NSRecord, MXRecord, SOARecord, SRVRecord, TXTRecord
39 def dns_connect(server, lp, creds):
40 if server.lower() == 'localhost':
42 binding_str = "ncacn_ip_tcp:%s[sign]" % server
44 dns_conn = dnsserver.dnsserver(binding_str, lp, creds)
45 except RuntimeError, e:
46 raise CommandError('Connecting to DNS RPC server %s failed with %s' % (server, e))
51 def bool_string(flag):
57 ret = 'UNKNOWN (0x%x)' % flag
61 def enum_string(module, enum_defs, value):
64 if value == getattr(module, e):
68 ret = 'UNKNOWN (0x%x)' % value
72 def bitmap_string(module, bitmap_defs, value):
75 if value & getattr(module, b):
82 def boot_method_string(boot_method):
83 enum_defs = [ 'DNS_BOOT_METHOD_UNINITIALIZED', 'DNS_BOOT_METHOD_FILE',
84 'DNS_BOOT_METHOD_REGISTRY', 'DNS_BOOT_METHOD_DIRECTORY' ]
85 return enum_string(dnsserver, enum_defs, boot_method)
88 def name_check_flag_string(check_flag):
89 enum_defs = [ 'DNS_ALLOW_RFC_NAMES_ONLY', 'DNS_ALLOW_NONRFC_NAMES',
90 'DNS_ALLOW_MULTIBYTE_NAMES', 'DNS_ALLOW_ALL_NAMES' ]
91 return enum_string(dnsserver, enum_defs, check_flag)
94 def zone_type_string(zone_type):
95 enum_defs = [ 'DNS_ZONE_TYPE_CACHE', 'DNS_ZONE_TYPE_PRIMARY',
96 'DNS_ZONE_TYPE_SECONDARY', 'DNS_ZONE_TYPE_STUB',
97 'DNS_ZONE_TYPE_FORWARDER', 'DNS_ZONE_TYPE_SECONDARY_CACHE' ]
98 return enum_string(dnsp, enum_defs, zone_type)
101 def zone_update_string(zone_update):
102 enum_defs = [ 'DNS_ZONE_UPDATE_OFF', 'DNS_ZONE_UPDATE_UNSECURE',
103 'DNS_ZONE_UPDATE_SECURE' ]
104 return enum_string(dnsp, enum_defs, zone_update)
107 def zone_secondary_security_string(security):
108 enum_defs = [ 'DNS_ZONE_SECSECURE_NO_SECURITY', 'DNS_ZONE_SECSECURE_NS_ONLY',
109 'DNS_ZONE_SECSECURE_LIST_ONLY', 'DNS_ZONE_SECSECURE_NO_XFER' ]
110 return enum_string(dnsserver, enum_defs, security)
113 def zone_notify_level_string(notify_level):
114 enum_defs = [ 'DNS_ZONE_NOTIFY_OFF', 'DNS_ZONE_NOTIFY_ALL_SECONDARIES',
115 'DNS_ZONE_NOTIFY_LIST_ONLY' ]
116 return enum_string(dnsserver, enum_defs, notify_level)
119 def dp_flags_string(dp_flags):
120 bitmap_defs = [ 'DNS_DP_AUTOCREATED', 'DNS_DP_LEGACY', 'DNS_DP_DOMAIN_DEFAULT',
121 'DNS_DP_FOREST_DEFAULT', 'DNS_DP_ENLISTED', 'DNS_DP_DELETED' ]
122 return bitmap_string(dnsserver, bitmap_defs, dp_flags)
125 def zone_flags_string(flags):
126 bitmap_defs = [ 'DNS_RPC_ZONE_PAUSED', 'DNS_RPC_ZONE_SHUTDOWN',
127 'DNS_RPC_ZONE_REVERSE', 'DNS_RPC_ZONE_AUTOCREATED',
128 'DNS_RPC_ZONE_DSINTEGRATED', 'DNS_RPC_ZONE_AGING',
129 'DNS_RPC_ZONE_UPDATE_UNSECURE', 'DNS_RPC_ZONE_UPDATE_SECURE',
130 'DNS_RPC_ZONE_READONLY']
131 return bitmap_string(dnsserver, bitmap_defs, flags)
134 def ip4_array_string(array):
138 for i in xrange(array.AddrCount):
139 addr = inet_ntop(AF_INET, pack('I', array.AddrArray[i]))
144 def dns_addr_array_string(array):
148 for i in xrange(array.AddrCount):
149 if array.AddrArray[i].MaxSa[0] == 0x02:
150 x = "".join([chr(b) for b in array.AddrArray[i].MaxSa])[4:8]
151 addr = inet_ntop(AF_INET, x)
152 elif array.AddrArray[i].MaxSa[0] == 0x17:
153 x = "".join([chr(b) for b in array.AddrArray[i].MaxSa])[8:24]
154 addr = inet_ntop(AF_INET6, x)
161 def dns_type_flag(rec_type):
162 rtype = rec_type.upper()
164 record_type = dnsp.DNS_TYPE_A
165 elif rtype == 'AAAA':
166 record_type = dnsp.DNS_TYPE_AAAA
168 record_type = dnsp.DNS_TYPE_PTR
170 record_type = dnsp.DNS_TYPE_NS
171 elif rtype == 'CNAME':
172 record_type = dnsp.DNS_TYPE_CNAME
174 record_type = dnsp.DNS_TYPE_SOA
176 record_type = dnsp.DNS_TYPE_MX
178 record_type = dnsp.DNS_TYPE_SRV
180 record_type = dnsp.DNS_TYPE_TXT
182 record_type = dnsp.DNS_TYPE_ALL
184 raise CommandError('Unknown type of DNS record %s' % rec_type)
188 def dns_client_version(cli_version):
189 version = cli_version.upper()
191 client_version = dnsserver.DNS_CLIENT_VERSION_W2K
192 elif version == 'DOTNET':
193 client_version = dnsserver.DNS_CLIENT_VERSION_DOTNET
194 elif version == 'LONGHORN':
195 client_version = dnsserver.DNS_CLIENT_VERSION_LONGHORN
197 raise CommandError('Unknown client version %s' % cli_version)
198 return client_version
201 def print_serverinfo(outf, typeid, serverinfo):
202 outf.write(' dwVersion : 0x%x\n' % serverinfo.dwVersion)
203 outf.write(' fBootMethod : %s\n' % boot_method_string(serverinfo.fBootMethod))
204 outf.write(' fAdminConfigured : %s\n' % bool_string(serverinfo.fAdminConfigured))
205 outf.write(' fAllowUpdate : %s\n' % bool_string(serverinfo.fAllowUpdate))
206 outf.write(' fDsAvailable : %s\n' % bool_string(serverinfo.fDsAvailable))
207 outf.write(' pszServerName : %s\n' % serverinfo.pszServerName)
208 outf.write(' pszDsContainer : %s\n' % serverinfo.pszDsContainer)
210 if typeid != dnsserver.DNSSRV_TYPEID_SERVER_INFO:
211 outf.write(' aipServerAddrs : %s\n' %
212 ip4_array_string(serverinfo.aipServerAddrs))
213 outf.write(' aipListenAddrs : %s\n' %
214 ip4_array_string(serverinfo.aipListenAddrs))
215 outf.write(' aipForwarders : %s\n' %
216 ip4_array_string(serverinfo.aipForwarders))
218 outf.write(' aipServerAddrs : %s\n' %
219 dns_addr_array_string(serverinfo.aipServerAddrs))
220 outf.write(' aipListenAddrs : %s\n' %
221 dns_addr_array_string(serverinfo.aipListenAddrs))
222 outf.write(' aipForwarders : %s\n' %
223 dns_addr_array_string(serverinfo.aipForwarders))
225 outf.write(' dwLogLevel : %d\n' % serverinfo.dwLogLevel)
226 outf.write(' dwDebugLevel : %d\n' % serverinfo.dwDebugLevel)
227 outf.write(' dwForwardTimeout : %d\n' % serverinfo.dwForwardTimeout)
228 outf.write(' dwRpcPrototol : 0x%x\n' % serverinfo.dwRpcProtocol)
229 outf.write(' dwNameCheckFlag : %s\n' % name_check_flag_string(serverinfo.dwNameCheckFlag))
230 outf.write(' cAddressAnswerLimit : %d\n' % serverinfo.cAddressAnswerLimit)
231 outf.write(' dwRecursionRetry : %d\n' % serverinfo.dwRecursionRetry)
232 outf.write(' dwRecursionTimeout : %d\n' % serverinfo.dwRecursionTimeout)
233 outf.write(' dwMaxCacheTtl : %d\n' % serverinfo.dwMaxCacheTtl)
234 outf.write(' dwDsPollingInterval : %d\n' % serverinfo.dwDsPollingInterval)
235 outf.write(' dwScavengingInterval : %d\n' % serverinfo.dwScavengingInterval)
236 outf.write(' dwDefaultRefreshInterval : %d\n' % serverinfo.dwDefaultRefreshInterval)
237 outf.write(' dwDefaultNoRefreshInterval : %d\n' % serverinfo.dwDefaultNoRefreshInterval)
238 outf.write(' fAutoReverseZones : %s\n' % bool_string(serverinfo.fAutoReverseZones))
239 outf.write(' fAutoCacheUpdate : %s\n' % bool_string(serverinfo.fAutoCacheUpdate))
240 outf.write(' fRecurseAfterForwarding : %s\n' % bool_string(serverinfo.fRecurseAfterForwarding))
241 outf.write(' fForwardDelegations : %s\n' % bool_string(serverinfo.fForwardDelegations))
242 outf.write(' fNoRecursion : %s\n' % bool_string(serverinfo.fNoRecursion))
243 outf.write(' fSecureResponses : %s\n' % bool_string(serverinfo.fSecureResponses))
244 outf.write(' fRoundRobin : %s\n' % bool_string(serverinfo.fRoundRobin))
245 outf.write(' fLocalNetPriority : %s\n' % bool_string(serverinfo.fLocalNetPriority))
246 outf.write(' fBindSecondaries : %s\n' % bool_string(serverinfo.fBindSecondaries))
247 outf.write(' fWriteAuthorityNs : %s\n' % bool_string(serverinfo.fWriteAuthorityNs))
248 outf.write(' fStrictFileParsing : %s\n' % bool_string(serverinfo.fStrictFileParsing))
249 outf.write(' fLooseWildcarding : %s\n' % bool_string(serverinfo.fLooseWildcarding))
250 outf.write(' fDefaultAgingState : %s\n' % bool_string(serverinfo.fDefaultAgingState))
252 if typeid != dnsserver.DNSSRV_TYPEID_SERVER_INFO_W2K:
253 outf.write(' dwRpcStructureVersion : 0x%x\n' % serverinfo.dwRpcStructureVersion)
254 outf.write(' aipLogFilter : %s\n' % dns_addr_array_string(serverinfo.aipLogFilter))
255 outf.write(' pwszLogFilePath : %s\n' % serverinfo.pwszLogFilePath)
256 outf.write(' pszDomainName : %s\n' % serverinfo.pszDomainName)
257 outf.write(' pszForestName : %s\n' % serverinfo.pszForestName)
258 outf.write(' pszDomainDirectoryPartition : %s\n' % serverinfo.pszDomainDirectoryPartition)
259 outf.write(' pszForestDirectoryPartition : %s\n' % serverinfo.pszForestDirectoryPartition)
261 outf.write(' dwLocalNetPriorityNetMask : 0x%x\n' % serverinfo.dwLocalNetPriorityNetMask)
262 outf.write(' dwLastScavengeTime : %d\n' % serverinfo.dwLastScavengeTime)
263 outf.write(' dwEventLogLevel : %d\n' % serverinfo.dwEventLogLevel)
264 outf.write(' dwLogFileMaxSize : %d\n' % serverinfo.dwLogFileMaxSize)
265 outf.write(' dwDsForestVersion : %d\n' % serverinfo.dwDsForestVersion)
266 outf.write(' dwDsDomainVersion : %d\n' % serverinfo.dwDsDomainVersion)
267 outf.write(' dwDsDsaVersion : %d\n' % serverinfo.dwDsDsaVersion)
269 if typeid == dnsserver.DNSSRV_TYPEID_SERVER_INFO:
270 outf.write(' fReadOnlyDC : %s\n' % bool_string(serverinfo.fReadOnlyDC))
273 def print_zoneinfo(outf, typeid, zoneinfo):
274 outf.write(' pszZoneName : %s\n' % zoneinfo.pszZoneName)
275 outf.write(' dwZoneType : %s\n' % zone_type_string(zoneinfo.dwZoneType))
276 outf.write(' fReverse : %s\n' % bool_string(zoneinfo.fReverse))
277 outf.write(' fAllowUpdate : %s\n' % zone_update_string(zoneinfo.fAllowUpdate))
278 outf.write(' fPaused : %s\n' % bool_string(zoneinfo.fPaused))
279 outf.write(' fShutdown : %s\n' % bool_string(zoneinfo.fShutdown))
280 outf.write(' fAutoCreated : %s\n' % bool_string(zoneinfo.fAutoCreated))
281 outf.write(' fUseDatabase : %s\n' % bool_string(zoneinfo.fUseDatabase))
282 outf.write(' pszDataFile : %s\n' % zoneinfo.pszDataFile)
283 if typeid != dnsserver.DNSSRV_TYPEID_ZONE_INFO:
284 outf.write(' aipMasters : %s\n' %
285 ip4_array_string(zoneinfo.aipMasters))
287 outf.write(' aipMasters : %s\n' %
288 dns_addr_array_string(zoneinfo.aipMasters))
289 outf.write(' fSecureSecondaries : %s\n' % zone_secondary_security_string(zoneinfo.fSecureSecondaries))
290 outf.write(' fNotifyLevel : %s\n' % zone_notify_level_string(zoneinfo.fNotifyLevel))
291 if typeid != dnsserver.DNSSRV_TYPEID_ZONE_INFO:
292 outf.write(' aipSecondaries : %s\n' %
293 ip4_array_string(zoneinfo.aipSecondaries))
294 outf.write(' aipNotify : %s\n' %
295 ip4_array_string(zoneinfo.aipNotify))
297 outf.write(' aipSecondaries : %s\n' %
298 dns_addr_array_string(zoneinfo.aipSecondaries))
299 outf.write(' aipNotify : %s\n' %
300 dns_addr_array_string(zoneinfo.aipNotify))
301 outf.write(' fUseWins : %s\n' % bool_string(zoneinfo.fUseWins))
302 outf.write(' fUseNbstat : %s\n' % bool_string(zoneinfo.fUseNbstat))
303 outf.write(' fAging : %s\n' % bool_string(zoneinfo.fAging))
304 outf.write(' dwNoRefreshInterval : %d\n' % zoneinfo.dwNoRefreshInterval)
305 outf.write(' dwRefreshInterval : %d\n' % zoneinfo.dwRefreshInterval)
306 outf.write(' dwAvailForScavengeTime : %d\n' % zoneinfo.dwAvailForScavengeTime)
307 if typeid != dnsserver.DNSSRV_TYPEID_ZONE_INFO:
308 outf.write(' aipScavengeServers : %s\n' %
309 ip4_array_string(zoneinfo.aipScavengeServers))
311 outf.write(' aipScavengeServers : %s\n' %
312 dns_addr_array_string(zoneinfo.aipScavengeServers))
314 if typeid != dnsserver.DNSSRV_TYPEID_ZONE_INFO_W2K:
315 outf.write(' dwRpcStructureVersion : 0x%x\n' % zoneinfo.dwRpcStructureVersion)
316 outf.write(' dwForwarderTimeout : %d\n' % zoneinfo.dwForwarderTimeout)
317 outf.write(' fForwarderSlave : %d\n' % zoneinfo.fForwarderSlave)
318 if typeid != dnsserver.DNSSRV_TYPEID_ZONE_INFO:
319 outf.write(' aipLocalMasters : %s\n' %
320 ip4_array_string(zoneinfo.aipLocalMasters))
322 outf.write(' aipLocalMasters : %s\n' %
323 dns_addr_array_string(zoneinfo.aipLocalMasters))
324 outf.write(' dwDpFlags : %s\n' % dp_flags_string(zoneinfo.dwDpFlags))
325 outf.write(' pszDpFqdn : %s\n' % zoneinfo.pszDpFqdn)
326 outf.write(' pwszZoneDn : %s\n' % zoneinfo.pwszZoneDn)
327 outf.write(' dwLastSuccessfulSoaCheck : %d\n' % zoneinfo.dwLastSuccessfulSoaCheck)
328 outf.write(' dwLastSuccessfulXfr : %d\n' % zoneinfo.dwLastSuccessfulXfr)
330 if typeid == dnsserver.DNSSRV_TYPEID_ZONE_INFO:
331 outf.write(' fQueuedForBackgroundLoad : %s\n' % bool_string(zoneinfo.fQueuedForBackgroundLoad))
332 outf.write(' fBackgroundLoadInProgress : %s\n' % bool_string(zoneinfo.fBackgroundLoadInProgress))
333 outf.write(' fReadOnlyZone : %s\n' % bool_string(zoneinfo.fReadOnlyZone))
334 outf.write(' dwLastXfrAttempt : %d\n' % zoneinfo.dwLastXfrAttempt)
335 outf.write(' dwLastXfrResult : %d\n' % zoneinfo.dwLastXfrResult)
338 def print_zone(outf, typeid, zone):
339 outf.write(' pszZoneName : %s\n' % zone.pszZoneName)
340 outf.write(' Flags : %s\n' % zone_flags_string(zone.Flags))
341 outf.write(' ZoneType : %s\n' % zone_type_string(zone.ZoneType))
342 outf.write(' Version : %s\n' % zone.Version)
344 if typeid != dnsserver.DNSSRV_TYPEID_ZONE_W2K:
345 outf.write(' dwDpFlags : %s\n' % dp_flags_string(zone.dwDpFlags))
346 outf.write(' pszDpFqdn : %s\n' % zone.pszDpFqdn)
349 def print_enumzones(outf, typeid, zones):
350 outf.write(' %d zone(s) found\n' % zones.dwZoneCount)
351 for zone in zones.ZoneArray:
353 print_zone(outf, typeid, zone)
356 def print_dns_record(outf, rec):
357 if rec.wType == dnsp.DNS_TYPE_A:
358 mesg = 'A: %s' % (rec.data)
359 elif rec.wType == dnsp.DNS_TYPE_AAAA:
360 mesg = 'AAAA: %s' % (rec.data)
361 elif rec.wType == dnsp.DNS_TYPE_PTR:
362 mesg = 'PTR: %s' % (rec.data.str)
363 elif rec.wType == dnsp.DNS_TYPE_NS:
364 mesg = 'NS: %s' % (rec.data.str)
365 elif rec.wType == dnsp.DNS_TYPE_CNAME:
366 mesg = 'CNAME: %s' % (rec.data.str)
367 elif rec.wType == dnsp.DNS_TYPE_SOA:
368 mesg = 'SOA: serial=%d, refresh=%d, retry=%d, expire=%d, minttl=%d, ns=%s, email=%s' % (
373 rec.data.dwMinimumTtl,
374 rec.data.NamePrimaryServer.str,
375 rec.data.ZoneAdministratorEmail.str)
376 elif rec.wType == dnsp.DNS_TYPE_MX:
377 mesg = 'MX: %s (%d)' % (rec.data.nameExchange.str, rec.data.wPreference)
378 elif rec.wType == dnsp.DNS_TYPE_SRV:
379 mesg = 'SRV: %s (%d, %d, %d)' % (rec.data.nameTarget.str, rec.data.wPort,
380 rec.data.wPriority, rec.data.wWeight)
381 elif rec.wType == dnsp.DNS_TYPE_TXT:
382 slist = ['"%s"' % name.str for name in rec.data.str]
383 mesg = 'TXT: %s' % ','.join(slist)
386 outf.write(' %s (flags=%x, serial=%d, ttl=%d)\n' % (
387 mesg, rec.dwFlags, rec.dwSerial, rec.dwTtlSeconds))
390 def print_dnsrecords(outf, records):
391 for rec in records.rec:
392 outf.write(' Name=%s, Records=%d, Children=%d\n' % (
396 for dns_rec in rec.records:
397 print_dns_record(outf, dns_rec)
402 # Convert data into a dns record
403 def data_to_dns_record(record_type, data):
404 if record_type == dnsp.DNS_TYPE_A:
406 elif record_type == dnsp.DNS_TYPE_AAAA:
407 rec = AAAARecord(data)
408 elif record_type == dnsp.DNS_TYPE_PTR:
409 rec = PTRRecord(data)
410 elif record_type == dnsp.DNS_TYPE_CNAME:
411 rec = CNameRecord(data)
412 elif record_type == dnsp.DNS_TYPE_NS:
414 elif record_type == dnsp.DNS_TYPE_MX:
415 tmp = data.split(' ')
417 raise CommandError('Data requires 2 elements - mail_server, preference')
419 preference = int(tmp[1])
420 rec = MXRecord(mail_server, preference)
421 elif record_type == dnsp.DNS_TYPE_SRV:
422 tmp = data.split(' ')
424 raise CommandError('Data requires 4 elements - server, port, priority, weight')
427 priority = int(tmp[2])
429 rec = SRVRecord(server, port, priority=priority, weight=weight)
430 elif record_type == dnsp.DNS_TYPE_SOA:
431 tmp = data.split(' ')
433 raise CommandError('Data requires 7 elements - nameserver, email, serial, '
434 'refresh, retry, expire, minimumttl')
438 refresh = int(tmp[3])
441 minimum = int(tmp[6])
442 rec = SOARecord(nameserver, email, serial=serial, refresh=refresh,
443 retry=retry, expire=expire, minimum=minimum)
444 elif record_type == dnsp.DNS_TYPE_TXT:
445 slist = shlex.split(data)
446 rec = TXTRecord(slist)
448 raise CommandError('Unsupported record type')
452 # Match dns name (of type DNS_RPC_NAME)
453 def dns_name_equal(n1, n2):
454 return n1.str.rstrip('.').lower() == n2.str.rstrip('.').lower()
457 # Match a dns record with specified data
458 def dns_record_match(dns_conn, server, zone, name, record_type, data):
459 urec = data_to_dns_record(record_type, data)
461 select_flags = dnsserver.DNS_RPC_VIEW_AUTHORITY_DATA
464 buflen, res = dns_conn.DnssrvEnumRecords2(
465 dnsserver.DNS_CLIENT_VERSION_LONGHORN, 0, server, zone, name, None,
466 record_type, select_flags, None, None)
467 except WERRORError as e:
468 if e.args[0] == werror.WERR_DNS_ERROR_NAME_DOES_NOT_EXIST:
469 # Either the zone doesn't exist, or there were no records.
470 # We can't differentiate the two.
474 if not res or res.count == 0:
477 for rec in res.rec[0].records:
478 if rec.wType != record_type:
482 if record_type == dnsp.DNS_TYPE_A:
483 if rec.data == urec.data:
485 elif record_type == dnsp.DNS_TYPE_AAAA:
486 if rec.data == urec.data:
488 elif record_type == dnsp.DNS_TYPE_PTR:
489 if dns_name_equal(rec.data, urec.data):
491 elif record_type == dnsp.DNS_TYPE_CNAME:
492 if dns_name_equal(rec.data, urec.data):
494 elif record_type == dnsp.DNS_TYPE_NS:
495 if dns_name_equal(rec.data, urec.data):
497 elif record_type == dnsp.DNS_TYPE_MX:
498 if dns_name_equal(rec.data.nameExchange, urec.data.nameExchange) and \
499 rec.data.wPreference == urec.data.wPreference:
501 elif record_type == dnsp.DNS_TYPE_SRV:
502 if rec.data.wPriority == urec.data.wPriority and \
503 rec.data.wWeight == urec.data.wWeight and \
504 rec.data.wPort == urec.data.wPort and \
505 dns_name_equal(rec.data.nameTarget, urec.data.nameTarget):
507 elif record_type == dnsp.DNS_TYPE_SOA:
508 if rec.data.dwSerialNo == urec.data.dwSerialNo and \
509 rec.data.dwRefresh == urec.data.dwRefresh and \
510 rec.data.dwRetry == urec.data.dwRetry and \
511 rec.data.dwExpire == urec.data.dwExpire and \
512 rec.data.dwMinimumTtl == urec.data.dwMinimumTtl and \
513 dns_name_equal(rec.data.NamePrimaryServer,
514 urec.data.NamePrimaryServer) and \
515 dns_name_equal(rec.data.ZoneAdministratorEmail,
516 urec.data.ZoneAdministratorEmail):
518 elif record_type == dnsp.DNS_TYPE_TXT:
519 if rec.data.count == urec.data.count:
521 for i in xrange(rec.data.count):
523 (rec.data.str[i].str == urec.data.str[i].str)
531 class cmd_serverinfo(Command):
532 """Query for Server information."""
534 synopsis = '%prog <server> [options]'
536 takes_args = [ 'server' ]
538 takes_optiongroups = {
539 "sambaopts": options.SambaOptions,
540 "versionopts": options.VersionOptions,
541 "credopts": options.CredentialsOptions,
545 Option('--client-version', help='Client Version',
546 default='longhorn', metavar='w2k|dotnet|longhorn',
547 choices=['w2k','dotnet','longhorn'], dest='cli_ver'),
550 def run(self, server, cli_ver, sambaopts=None, credopts=None,
552 self.lp = sambaopts.get_loadparm()
553 self.creds = credopts.get_credentials(self.lp)
554 dns_conn = dns_connect(server, self.lp, self.creds)
556 client_version = dns_client_version(cli_ver)
558 typeid, res = dns_conn.DnssrvQuery2(client_version, 0, server,
560 print_serverinfo(self.outf, typeid, res)
563 class cmd_zoneinfo(Command):
564 """Query for zone information."""
566 synopsis = '%prog <server> <zone> [options]'
568 takes_args = [ 'server', 'zone' ]
570 takes_optiongroups = {
571 "sambaopts": options.SambaOptions,
572 "versionopts": options.VersionOptions,
573 "credopts": options.CredentialsOptions,
577 Option('--client-version', help='Client Version',
578 default='longhorn', metavar='w2k|dotnet|longhorn',
579 choices=['w2k','dotnet','longhorn'], dest='cli_ver'),
582 def run(self, server, zone, cli_ver, sambaopts=None, credopts=None,
584 self.lp = sambaopts.get_loadparm()
585 self.creds = credopts.get_credentials(self.lp)
586 dns_conn = dns_connect(server, self.lp, self.creds)
588 client_version = dns_client_version(cli_ver)
590 typeid, res = dns_conn.DnssrvQuery2(client_version, 0, server, zone,
592 print_zoneinfo(self.outf, typeid, res)
595 class cmd_zonelist(Command):
596 """Query for zones."""
598 synopsis = '%prog <server> [options]'
600 takes_args = [ 'server' ]
602 takes_optiongroups = {
603 "sambaopts": options.SambaOptions,
604 "versionopts": options.VersionOptions,
605 "credopts": options.CredentialsOptions,
609 Option('--client-version', help='Client Version',
610 default='longhorn', metavar='w2k|dotnet|longhorn',
611 choices=['w2k','dotnet','longhorn'], dest='cli_ver'),
612 Option('--primary', help='List primary zones (default)',
613 action='store_true', dest='primary'),
614 Option('--secondary', help='List secondary zones',
615 action='store_true', dest='secondary'),
616 Option('--cache', help='List cached zones',
617 action='store_true', dest='cache'),
618 Option('--auto', help='List automatically created zones',
619 action='store_true', dest='auto'),
620 Option('--forward', help='List forward zones',
621 action='store_true', dest='forward'),
622 Option('--reverse', help='List reverse zones',
623 action='store_true', dest='reverse'),
624 Option('--ds', help='List directory integrated zones',
625 action='store_true', dest='ds'),
626 Option('--non-ds', help='List non-directory zones',
627 action='store_true', dest='nonds')
630 def run(self, server, cli_ver, primary=False, secondary=False, cache=False,
631 auto=False, forward=False, reverse=False, ds=False, nonds=False,
632 sambaopts=None, credopts=None, versionopts=None):
636 request_filter |= dnsserver.DNS_ZONE_REQUEST_PRIMARY
638 request_filter |= dnsserver.DNS_ZONE_REQUEST_SECONDARY
640 request_filter |= dnsserver.DNS_ZONE_REQUEST_CACHE
642 request_filter |= dnsserver.DNS_ZONE_REQUEST_AUTO
644 request_filter |= dnsserver.DNS_ZONE_REQUEST_FORWARD
646 request_filter |= dnsserver.DNS_ZONE_REQUEST_REVERSE
648 request_filter |= dnsserver.DNS_ZONE_REQUEST_DS
650 request_filter |= dnsserver.DNS_ZONE_REQUEST_NON_DS
652 if request_filter == 0:
653 request_filter = dnsserver.DNS_ZONE_REQUEST_PRIMARY
655 self.lp = sambaopts.get_loadparm()
656 self.creds = credopts.get_credentials(self.lp)
657 dns_conn = dns_connect(server, self.lp, self.creds)
659 client_version = dns_client_version(cli_ver)
661 typeid, res = dns_conn.DnssrvComplexOperation2(client_version,
664 dnsserver.DNSSRV_TYPEID_DWORD,
667 if client_version == dnsserver.DNS_CLIENT_VERSION_W2K:
668 typeid = dnsserver.DNSSRV_TYPEID_ZONE_W2K
670 typeid = dnsserver.DNSSRV_TYPEID_ZONE
671 print_enumzones(self.outf, typeid, res)
674 class cmd_zonecreate(Command):
677 synopsis = '%prog <server> <zone> [options]'
679 takes_args = [ 'server', 'zone' ]
681 takes_optiongroups = {
682 "sambaopts": options.SambaOptions,
683 "versionopts": options.VersionOptions,
684 "credopts": options.CredentialsOptions,
688 Option('--client-version', help='Client Version',
689 default='longhorn', metavar='w2k|dotnet|longhorn',
690 choices=['w2k','dotnet','longhorn'], dest='cli_ver')
693 def run(self, server, zone, cli_ver, sambaopts=None, credopts=None,
696 self.lp = sambaopts.get_loadparm()
697 self.creds = credopts.get_credentials(self.lp)
698 dns_conn = dns_connect(server, self.lp, self.creds)
702 client_version = dns_client_version(cli_ver)
703 if client_version == dnsserver.DNS_CLIENT_VERSION_W2K:
704 typeid = dnsserver.DNSSRV_TYPEID_ZONE_CREATE_W2K
705 zone_create_info = dnsserver.DNS_RPC_ZONE_CREATE_INFO_W2K()
706 zone_create_info.pszZoneName = zone
707 zone_create_info.dwZoneType = dnsp.DNS_ZONE_TYPE_PRIMARY
708 zone_create_info.fAging = 0
709 zone_create_info.fDsIntegrated = 1
710 zone_create_info.fLoadExisting = 1
711 elif client_version == dnsserver.DNS_CLIENT_VERSION_DOTNET:
712 typeid = dnsserver.DNSSRV_TYPEID_ZONE_CREATE_DOTNET
713 zone_create_info = dnsserver.DNS_RPC_ZONE_CREATE_INFO_DOTNET()
714 zone_create_info.pszZoneName = zone
715 zone_create_info.dwZoneType = dnsp.DNS_ZONE_TYPE_PRIMARY
716 zone_create_info.fAging = 0
717 zone_create_info.fDsIntegrated = 1
718 zone_create_info.fLoadExisting = 1
719 zone_create_info.dwDpFlags = dnsserver.DNS_DP_DOMAIN_DEFAULT
721 typeid = dnsserver.DNSSRV_TYPEID_ZONE_CREATE
722 zone_create_info = dnsserver.DNS_RPC_ZONE_CREATE_INFO_LONGHORN()
723 zone_create_info.pszZoneName = zone
724 zone_create_info.dwZoneType = dnsp.DNS_ZONE_TYPE_PRIMARY
725 zone_create_info.fAging = 0
726 zone_create_info.fDsIntegrated = 1
727 zone_create_info.fLoadExisting = 1
728 zone_create_info.dwDpFlags = dnsserver.DNS_DP_DOMAIN_DEFAULT
730 res = dns_conn.DnssrvOperation2(client_version, 0, server, None,
731 0, 'ZoneCreate', typeid,
734 typeid = dnsserver.DNSSRV_TYPEID_NAME_AND_PARAM
735 name_and_param = dnsserver.DNS_RPC_NAME_AND_PARAM()
736 name_and_param.pszNodeName = 'AllowUpdate'
737 name_and_param.dwParam = dnsp.DNS_ZONE_UPDATE_SECURE
740 res = dns_conn.DnssrvOperation2(client_version, 0, server, zone,
741 0, 'ResetDwordProperty', typeid,
743 except WERRORError as e:
744 if e.args[0] == werror.WERR_DNS_ERROR_ZONE_ALREADY_EXISTS:
745 self.outf.write('Zone already exists.')
748 self.outf.write('Zone %s created successfully\n' % zone)
751 class cmd_zonedelete(Command):
754 synopsis = '%prog <server> <zone> [options]'
756 takes_args = [ 'server', 'zone' ]
758 takes_optiongroups = {
759 "sambaopts": options.SambaOptions,
760 "versionopts": options.VersionOptions,
761 "credopts": options.CredentialsOptions,
764 def run(self, server, zone, sambaopts=None, credopts=None,
767 self.lp = sambaopts.get_loadparm()
768 self.creds = credopts.get_credentials(self.lp)
769 dns_conn = dns_connect(server, self.lp, self.creds)
773 res = dns_conn.DnssrvOperation2(dnsserver.DNS_CLIENT_VERSION_LONGHORN,
774 0, server, zone, 0, 'DeleteZoneFromDs',
775 dnsserver.DNSSRV_TYPEID_NULL,
777 except WERRORError as e:
778 if e.args[0] == werror.WERR_DNS_ERROR_ZONE_DOES_NOT_EXIST:
779 self.outf.write('Zone does not exist and so could not be deleted.')
782 self.outf.write('Zone %s deleted successfully\n' % zone)
785 class cmd_query(Command):
788 synopsis = '%prog <server> <zone> <name> <A|AAAA|CNAME|MX|NS|SOA|SRV|TXT|ALL> [options]'
790 takes_args = [ 'server', 'zone', 'name', 'rtype' ]
792 takes_optiongroups = {
793 "sambaopts": options.SambaOptions,
794 "versionopts": options.VersionOptions,
795 "credopts": options.CredentialsOptions,
799 Option('--authority', help='Search authoritative records (default)',
800 action='store_true', dest='authority'),
801 Option('--cache', help='Search cached records',
802 action='store_true', dest='cache'),
803 Option('--glue', help='Search glue records',
804 action='store_true', dest='glue'),
805 Option('--root', help='Search root hints',
806 action='store_true', dest='root'),
807 Option('--additional', help='List additional records',
808 action='store_true', dest='additional'),
809 Option('--no-children', help='Do not list children',
810 action='store_true', dest='no_children'),
811 Option('--only-children', help='List only children',
812 action='store_true', dest='only_children')
815 def run(self, server, zone, name, rtype, authority=False, cache=False,
816 glue=False, root=False, additional=False, no_children=False,
817 only_children=False, sambaopts=None, credopts=None,
819 record_type = dns_type_flag(rtype)
821 if name.find('*') != -1:
822 raise CommandError('Wildcard searches not supported. To dump entire zone use "@"')
826 select_flags |= dnsserver.DNS_RPC_VIEW_AUTHORITY_DATA
828 select_flags |= dnsserver.DNS_RPC_VIEW_CACHE_DATA
830 select_flags |= dnsserver.DNS_RPC_VIEW_GLUE_DATA
832 select_flags |= dnsserver.DNS_RPC_VIEW_ROOT_HINT_DATA
834 select_flags |= dnsserver.DNS_RPC_VIEW_ADDITIONAL_DATA
836 select_flags |= dnsserver.DNS_RPC_VIEW_NO_CHILDREN
838 select_flags |= dnsserver.DNS_RPC_VIEW_ONLY_CHILDREN
840 if select_flags == 0:
841 select_flags = dnsserver.DNS_RPC_VIEW_AUTHORITY_DATA
843 if select_flags == dnsserver.DNS_RPC_VIEW_ADDITIONAL_DATA:
844 self.outf.write('Specify either --authority or --root along with --additional.\n')
845 self.outf.write('Assuming --authority.\n')
846 select_flags |= dnsserver.DNS_RPC_VIEW_AUTHORITY_DATA
848 self.lp = sambaopts.get_loadparm()
849 self.creds = credopts.get_credentials(self.lp)
850 dns_conn = dns_connect(server, self.lp, self.creds)
853 buflen, res = dns_conn.DnssrvEnumRecords2(
854 dnsserver.DNS_CLIENT_VERSION_LONGHORN, 0, server, zone, name,
855 None, record_type, select_flags, None, None)
856 except WERRORError as e:
857 if e.args[0] == werror.WERR_DNS_ERROR_NAME_DOES_NOT_EXIST:
858 self.outf.write('Record or zone does not exist.')
861 print_dnsrecords(self.outf, res)
864 class cmd_roothints(Command):
865 """Query root hints."""
867 synopsis = '%prog <server> [<name>] [options]'
869 takes_args = [ 'server', 'name?' ]
871 takes_optiongroups = {
872 "sambaopts": options.SambaOptions,
873 "versionopts": options.VersionOptions,
874 "credopts": options.CredentialsOptions,
877 def run(self, server, name='.', sambaopts=None, credopts=None,
879 record_type = dnsp.DNS_TYPE_NS
880 select_flags = (dnsserver.DNS_RPC_VIEW_ROOT_HINT_DATA |
881 dnsserver.DNS_RPC_VIEW_ADDITIONAL_DATA)
883 self.lp = sambaopts.get_loadparm()
884 self.creds = credopts.get_credentials(self.lp)
885 dns_conn = dns_connect(server, self.lp, self.creds)
887 buflen, res = dns_conn.DnssrvEnumRecords2(
888 dnsserver.DNS_CLIENT_VERSION_LONGHORN, 0, server, '..RootHints',
889 name, None, record_type, select_flags, None, None)
890 print_dnsrecords(self.outf, res)
893 class cmd_add_record(Command):
896 For each type data contents are as follows:
897 A ipv4_address_string
898 AAAA ipv6_address_string
902 MX "fqdn_string preference"
903 SRV "fqdn_string port priority weight"
904 TXT "'string1' 'string2' ..."
907 synopsis = '%prog <server> <zone> <name> <A|AAAA|PTR|CNAME|NS|MX|SRV|TXT> <data>'
909 takes_args = [ 'server', 'zone', 'name', 'rtype', 'data' ]
911 takes_optiongroups = {
912 "sambaopts": options.SambaOptions,
913 "versionopts": options.VersionOptions,
914 "credopts": options.CredentialsOptions,
917 def run(self, server, zone, name, rtype, data, sambaopts=None,
918 credopts=None, versionopts=None):
920 if rtype.upper() not in ('A','AAAA','PTR','CNAME','NS','MX','SRV','TXT'):
921 raise CommandError('Adding record of type %s is not supported' % rtype)
923 record_type = dns_type_flag(rtype)
924 rec = data_to_dns_record(record_type, data)
926 self.lp = sambaopts.get_loadparm()
927 self.creds = credopts.get_credentials(self.lp)
928 dns_conn = dns_connect(server, self.lp, self.creds)
930 add_rec_buf = dnsserver.DNS_RPC_RECORD_BUF()
931 add_rec_buf.rec = rec
934 dns_conn.DnssrvUpdateRecord2(dnsserver.DNS_CLIENT_VERSION_LONGHORN,
935 0, server, zone, name, add_rec_buf, None)
936 except WERRORError as e:
937 if e.args[0] == werror.WERR_DNS_ERROR_NAME_DOES_NOT_EXIST:
938 self.outf.write('Zone does not exist; record could not be added.\n')
941 self.outf.write('Record added successfully\n')
944 class cmd_update_record(Command):
945 """Update a DNS record
947 For each type data contents are as follows:
948 A ipv4_address_string
949 AAAA ipv6_address_string
953 MX "fqdn_string preference"
954 SOA "fqdn_dns fqdn_email serial refresh retry expire minimumttl"
955 SRV "fqdn_string port priority weight"
956 TXT "'string1' 'string2' ..."
959 synopsis = '%prog <server> <zone> <name> <A|AAAA|PTR|CNAME|NS|MX|SOA|SRV|TXT> <olddata> <newdata>'
961 takes_args = [ 'server', 'zone', 'name', 'rtype', 'olddata', 'newdata' ]
963 takes_optiongroups = {
964 "sambaopts": options.SambaOptions,
965 "versionopts": options.VersionOptions,
966 "credopts": options.CredentialsOptions,
969 def run(self, server, zone, name, rtype, olddata, newdata,
970 sambaopts=None, credopts=None, versionopts=None):
972 if rtype.upper() not in ('A','AAAA','PTR','CNAME','NS','MX','SOA','SRV','TXT'):
973 raise CommandError('Updating record of type %s is not supported' % rtype)
975 record_type = dns_type_flag(rtype)
976 rec = data_to_dns_record(record_type, newdata)
978 self.lp = sambaopts.get_loadparm()
979 self.creds = credopts.get_credentials(self.lp)
980 dns_conn = dns_connect(server, self.lp, self.creds)
982 rec_match = dns_record_match(dns_conn, server, zone, name, record_type,
985 raise CommandError('Record or zone does not exist.')
987 # Copy properties from existing record to new record
988 rec.dwFlags = rec_match.dwFlags
989 rec.dwSerial = rec_match.dwSerial
990 rec.dwTtlSeconds = rec_match.dwTtlSeconds
991 rec.dwTimeStamp = rec_match.dwTimeStamp
993 add_rec_buf = dnsserver.DNS_RPC_RECORD_BUF()
994 add_rec_buf.rec = rec
996 del_rec_buf = dnsserver.DNS_RPC_RECORD_BUF()
997 del_rec_buf.rec = rec_match
1000 dns_conn.DnssrvUpdateRecord2(dnsserver.DNS_CLIENT_VERSION_LONGHORN,
1007 except WERRORError as e:
1008 if e.args[0] == werror.WERR_DNS_ERROR_NAME_DOES_NOT_EXIST:
1009 self.outf.write('Zone does not exist; record could not be updated.\n')
1012 self.outf.write('Record updated successfully\n')
1015 class cmd_delete_record(Command):
1016 """Delete a DNS record
1018 For each type data contents are as follows:
1019 A ipv4_address_string
1020 AAAA ipv6_address_string
1024 MX "fqdn_string preference"
1025 SRV "fqdn_string port priority weight"
1026 TXT "'string1' 'string2' ..."
1029 synopsis = '%prog <server> <zone> <name> <A|AAAA|PTR|CNAME|NS|MX|SRV|TXT> <data>'
1031 takes_args = [ 'server', 'zone', 'name', 'rtype', 'data' ]
1033 takes_optiongroups = {
1034 "sambaopts": options.SambaOptions,
1035 "versionopts": options.VersionOptions,
1036 "credopts": options.CredentialsOptions,
1039 def run(self, server, zone, name, rtype, data, sambaopts=None, credopts=None, versionopts=None):
1041 if rtype.upper() not in ('A','AAAA','PTR','CNAME','NS','MX','SRV','TXT'):
1042 raise CommandError('Deleting record of type %s is not supported' % rtype)
1044 record_type = dns_type_flag(rtype)
1045 rec = data_to_dns_record(record_type, data)
1047 self.lp = sambaopts.get_loadparm()
1048 self.creds = credopts.get_credentials(self.lp)
1049 dns_conn = dns_connect(server, self.lp, self.creds)
1051 del_rec_buf = dnsserver.DNS_RPC_RECORD_BUF()
1052 del_rec_buf.rec = rec
1055 dns_conn.DnssrvUpdateRecord2(dnsserver.DNS_CLIENT_VERSION_LONGHORN,
1062 except WERRORError as e:
1063 if e.args[0] == werror.WERR_DNS_ERROR_NAME_DOES_NOT_EXIST:
1064 self.outf.write('Zone does not exist; record could not be deleted.\n')
1067 self.outf.write('Record deleted successfully\n')
1070 class cmd_dns(SuperCommand):
1071 """Domain Name Service (DNS) management."""
1074 subcommands['serverinfo'] = cmd_serverinfo()
1075 subcommands['zoneinfo'] = cmd_zoneinfo()
1076 subcommands['zonelist'] = cmd_zonelist()
1077 subcommands['zonecreate'] = cmd_zonecreate()
1078 subcommands['zonedelete'] = cmd_zonedelete()
1079 subcommands['query'] = cmd_query()
1080 subcommands['roothints'] = cmd_roothints()
1081 subcommands['add'] = cmd_add_record()
1082 subcommands['update'] = cmd_update_record()
1083 subcommands['delete'] = cmd_delete_record()