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/>.
20 import samba.getopt as options
21 from samba import WERRORError
22 from samba import werror
23 from struct import pack
24 from socket import inet_ntoa
25 from socket import inet_ntop
26 from socket import AF_INET
27 from socket import AF_INET6
30 from samba import remove_dc
31 from samba.samdb import SamDB
32 from samba.auth import system_session
34 from samba.netcmd import (
40 from samba.dcerpc import dnsp, dnsserver
42 from samba.dnsserver import ARecord, AAAARecord, PTRRecord, CNameRecord, NSRecord, MXRecord, SOARecord, SRVRecord, TXTRecord
45 def dns_connect(server, lp, creds):
46 if server.lower() == 'localhost':
48 binding_str = "ncacn_ip_tcp:%s[sign]" % server
50 dns_conn = dnsserver.dnsserver(binding_str, lp, creds)
51 except RuntimeError as e:
52 raise CommandError('Connecting to DNS RPC server %s failed with %s' % (server, e))
57 def bool_string(flag):
63 ret = 'UNKNOWN (0x%x)' % flag
67 def enum_string(module, enum_defs, value):
70 if value == getattr(module, e):
74 ret = 'UNKNOWN (0x%x)' % value
78 def bitmap_string(module, bitmap_defs, value):
81 if value & getattr(module, b):
88 def boot_method_string(boot_method):
89 enum_defs = ['DNS_BOOT_METHOD_UNINITIALIZED', 'DNS_BOOT_METHOD_FILE',
90 'DNS_BOOT_METHOD_REGISTRY', 'DNS_BOOT_METHOD_DIRECTORY']
91 return enum_string(dnsserver, enum_defs, boot_method)
94 def name_check_flag_string(check_flag):
95 enum_defs = ['DNS_ALLOW_RFC_NAMES_ONLY', 'DNS_ALLOW_NONRFC_NAMES',
96 'DNS_ALLOW_MULTIBYTE_NAMES', 'DNS_ALLOW_ALL_NAMES']
97 return enum_string(dnsserver, enum_defs, check_flag)
100 def zone_type_string(zone_type):
101 enum_defs = ['DNS_ZONE_TYPE_CACHE', 'DNS_ZONE_TYPE_PRIMARY',
102 'DNS_ZONE_TYPE_SECONDARY', 'DNS_ZONE_TYPE_STUB',
103 'DNS_ZONE_TYPE_FORWARDER', 'DNS_ZONE_TYPE_SECONDARY_CACHE']
104 return enum_string(dnsp, enum_defs, zone_type)
107 def zone_update_string(zone_update):
108 enum_defs = ['DNS_ZONE_UPDATE_OFF', 'DNS_ZONE_UPDATE_UNSECURE',
109 'DNS_ZONE_UPDATE_SECURE']
110 return enum_string(dnsp, enum_defs, zone_update)
113 def zone_secondary_security_string(security):
114 enum_defs = ['DNS_ZONE_SECSECURE_NO_SECURITY', 'DNS_ZONE_SECSECURE_NS_ONLY',
115 'DNS_ZONE_SECSECURE_LIST_ONLY', 'DNS_ZONE_SECSECURE_NO_XFER']
116 return enum_string(dnsserver, enum_defs, security)
119 def zone_notify_level_string(notify_level):
120 enum_defs = ['DNS_ZONE_NOTIFY_OFF', 'DNS_ZONE_NOTIFY_ALL_SECONDARIES',
121 'DNS_ZONE_NOTIFY_LIST_ONLY']
122 return enum_string(dnsserver, enum_defs, notify_level)
125 def dp_flags_string(dp_flags):
126 bitmap_defs = ['DNS_DP_AUTOCREATED', 'DNS_DP_LEGACY', 'DNS_DP_DOMAIN_DEFAULT',
127 'DNS_DP_FOREST_DEFAULT', 'DNS_DP_ENLISTED', 'DNS_DP_DELETED']
128 return bitmap_string(dnsserver, bitmap_defs, dp_flags)
131 def zone_flags_string(flags):
132 bitmap_defs = ['DNS_RPC_ZONE_PAUSED', 'DNS_RPC_ZONE_SHUTDOWN',
133 'DNS_RPC_ZONE_REVERSE', 'DNS_RPC_ZONE_AUTOCREATED',
134 'DNS_RPC_ZONE_DSINTEGRATED', 'DNS_RPC_ZONE_AGING',
135 'DNS_RPC_ZONE_UPDATE_UNSECURE', 'DNS_RPC_ZONE_UPDATE_SECURE',
136 'DNS_RPC_ZONE_READONLY']
137 return bitmap_string(dnsserver, bitmap_defs, flags)
140 def ip4_array_string(array):
144 for i in range(array.AddrCount):
145 addr = inet_ntop(AF_INET, pack('I', array.AddrArray[i]))
150 def dns_addr_array_string(array):
154 for i in range(array.AddrCount):
155 if array.AddrArray[i].MaxSa[0] == 0x02:
156 x = "".join([chr(b) for b in array.AddrArray[i].MaxSa])[4:8]
157 addr = inet_ntop(AF_INET, x)
158 elif array.AddrArray[i].MaxSa[0] == 0x17:
159 x = "".join([chr(b) for b in array.AddrArray[i].MaxSa])[8:24]
160 addr = inet_ntop(AF_INET6, x)
167 def dns_type_flag(rec_type):
168 rtype = rec_type.upper()
170 record_type = dnsp.DNS_TYPE_A
171 elif rtype == 'AAAA':
172 record_type = dnsp.DNS_TYPE_AAAA
174 record_type = dnsp.DNS_TYPE_PTR
176 record_type = dnsp.DNS_TYPE_NS
177 elif rtype == 'CNAME':
178 record_type = dnsp.DNS_TYPE_CNAME
180 record_type = dnsp.DNS_TYPE_SOA
182 record_type = dnsp.DNS_TYPE_MX
184 record_type = dnsp.DNS_TYPE_SRV
186 record_type = dnsp.DNS_TYPE_TXT
188 record_type = dnsp.DNS_TYPE_ALL
190 raise CommandError('Unknown type of DNS record %s' % rec_type)
194 def dns_client_version(cli_version):
195 version = cli_version.upper()
197 client_version = dnsserver.DNS_CLIENT_VERSION_W2K
198 elif version == 'DOTNET':
199 client_version = dnsserver.DNS_CLIENT_VERSION_DOTNET
200 elif version == 'LONGHORN':
201 client_version = dnsserver.DNS_CLIENT_VERSION_LONGHORN
203 raise CommandError('Unknown client version %s' % cli_version)
204 return client_version
207 def print_serverinfo(outf, typeid, serverinfo):
208 outf.write(' dwVersion : 0x%x\n' % serverinfo.dwVersion)
209 outf.write(' fBootMethod : %s\n' % boot_method_string(serverinfo.fBootMethod))
210 outf.write(' fAdminConfigured : %s\n' % bool_string(serverinfo.fAdminConfigured))
211 outf.write(' fAllowUpdate : %s\n' % bool_string(serverinfo.fAllowUpdate))
212 outf.write(' fDsAvailable : %s\n' % bool_string(serverinfo.fDsAvailable))
213 outf.write(' pszServerName : %s\n' % serverinfo.pszServerName)
214 outf.write(' pszDsContainer : %s\n' % serverinfo.pszDsContainer)
216 if typeid != dnsserver.DNSSRV_TYPEID_SERVER_INFO:
217 outf.write(' aipServerAddrs : %s\n' %
218 ip4_array_string(serverinfo.aipServerAddrs))
219 outf.write(' aipListenAddrs : %s\n' %
220 ip4_array_string(serverinfo.aipListenAddrs))
221 outf.write(' aipForwarders : %s\n' %
222 ip4_array_string(serverinfo.aipForwarders))
224 outf.write(' aipServerAddrs : %s\n' %
225 dns_addr_array_string(serverinfo.aipServerAddrs))
226 outf.write(' aipListenAddrs : %s\n' %
227 dns_addr_array_string(serverinfo.aipListenAddrs))
228 outf.write(' aipForwarders : %s\n' %
229 dns_addr_array_string(serverinfo.aipForwarders))
231 outf.write(' dwLogLevel : %d\n' % serverinfo.dwLogLevel)
232 outf.write(' dwDebugLevel : %d\n' % serverinfo.dwDebugLevel)
233 outf.write(' dwForwardTimeout : %d\n' % serverinfo.dwForwardTimeout)
234 outf.write(' dwRpcPrototol : 0x%x\n' % serverinfo.dwRpcProtocol)
235 outf.write(' dwNameCheckFlag : %s\n' % name_check_flag_string(serverinfo.dwNameCheckFlag))
236 outf.write(' cAddressAnswerLimit : %d\n' % serverinfo.cAddressAnswerLimit)
237 outf.write(' dwRecursionRetry : %d\n' % serverinfo.dwRecursionRetry)
238 outf.write(' dwRecursionTimeout : %d\n' % serverinfo.dwRecursionTimeout)
239 outf.write(' dwMaxCacheTtl : %d\n' % serverinfo.dwMaxCacheTtl)
240 outf.write(' dwDsPollingInterval : %d\n' % serverinfo.dwDsPollingInterval)
241 outf.write(' dwScavengingInterval : %d\n' % serverinfo.dwScavengingInterval)
242 outf.write(' dwDefaultRefreshInterval : %d\n' % serverinfo.dwDefaultRefreshInterval)
243 outf.write(' dwDefaultNoRefreshInterval : %d\n' % serverinfo.dwDefaultNoRefreshInterval)
244 outf.write(' fAutoReverseZones : %s\n' % bool_string(serverinfo.fAutoReverseZones))
245 outf.write(' fAutoCacheUpdate : %s\n' % bool_string(serverinfo.fAutoCacheUpdate))
246 outf.write(' fRecurseAfterForwarding : %s\n' % bool_string(serverinfo.fRecurseAfterForwarding))
247 outf.write(' fForwardDelegations : %s\n' % bool_string(serverinfo.fForwardDelegations))
248 outf.write(' fNoRecursion : %s\n' % bool_string(serverinfo.fNoRecursion))
249 outf.write(' fSecureResponses : %s\n' % bool_string(serverinfo.fSecureResponses))
250 outf.write(' fRoundRobin : %s\n' % bool_string(serverinfo.fRoundRobin))
251 outf.write(' fLocalNetPriority : %s\n' % bool_string(serverinfo.fLocalNetPriority))
252 outf.write(' fBindSecondaries : %s\n' % bool_string(serverinfo.fBindSecondaries))
253 outf.write(' fWriteAuthorityNs : %s\n' % bool_string(serverinfo.fWriteAuthorityNs))
254 outf.write(' fStrictFileParsing : %s\n' % bool_string(serverinfo.fStrictFileParsing))
255 outf.write(' fLooseWildcarding : %s\n' % bool_string(serverinfo.fLooseWildcarding))
256 outf.write(' fDefaultAgingState : %s\n' % bool_string(serverinfo.fDefaultAgingState))
258 if typeid != dnsserver.DNSSRV_TYPEID_SERVER_INFO_W2K:
259 outf.write(' dwRpcStructureVersion : 0x%x\n' % serverinfo.dwRpcStructureVersion)
260 outf.write(' aipLogFilter : %s\n' % dns_addr_array_string(serverinfo.aipLogFilter))
261 outf.write(' pwszLogFilePath : %s\n' % serverinfo.pwszLogFilePath)
262 outf.write(' pszDomainName : %s\n' % serverinfo.pszDomainName)
263 outf.write(' pszForestName : %s\n' % serverinfo.pszForestName)
264 outf.write(' pszDomainDirectoryPartition : %s\n' % serverinfo.pszDomainDirectoryPartition)
265 outf.write(' pszForestDirectoryPartition : %s\n' % serverinfo.pszForestDirectoryPartition)
267 outf.write(' dwLocalNetPriorityNetMask : 0x%x\n' % serverinfo.dwLocalNetPriorityNetMask)
268 outf.write(' dwLastScavengeTime : %d\n' % serverinfo.dwLastScavengeTime)
269 outf.write(' dwEventLogLevel : %d\n' % serverinfo.dwEventLogLevel)
270 outf.write(' dwLogFileMaxSize : %d\n' % serverinfo.dwLogFileMaxSize)
271 outf.write(' dwDsForestVersion : %d\n' % serverinfo.dwDsForestVersion)
272 outf.write(' dwDsDomainVersion : %d\n' % serverinfo.dwDsDomainVersion)
273 outf.write(' dwDsDsaVersion : %d\n' % serverinfo.dwDsDsaVersion)
275 if typeid == dnsserver.DNSSRV_TYPEID_SERVER_INFO:
276 outf.write(' fReadOnlyDC : %s\n' % bool_string(serverinfo.fReadOnlyDC))
279 def print_zoneinfo(outf, typeid, zoneinfo):
280 outf.write(' pszZoneName : %s\n' % zoneinfo.pszZoneName)
281 outf.write(' dwZoneType : %s\n' % zone_type_string(zoneinfo.dwZoneType))
282 outf.write(' fReverse : %s\n' % bool_string(zoneinfo.fReverse))
283 outf.write(' fAllowUpdate : %s\n' % zone_update_string(zoneinfo.fAllowUpdate))
284 outf.write(' fPaused : %s\n' % bool_string(zoneinfo.fPaused))
285 outf.write(' fShutdown : %s\n' % bool_string(zoneinfo.fShutdown))
286 outf.write(' fAutoCreated : %s\n' % bool_string(zoneinfo.fAutoCreated))
287 outf.write(' fUseDatabase : %s\n' % bool_string(zoneinfo.fUseDatabase))
288 outf.write(' pszDataFile : %s\n' % zoneinfo.pszDataFile)
289 if typeid != dnsserver.DNSSRV_TYPEID_ZONE_INFO:
290 outf.write(' aipMasters : %s\n' %
291 ip4_array_string(zoneinfo.aipMasters))
293 outf.write(' aipMasters : %s\n' %
294 dns_addr_array_string(zoneinfo.aipMasters))
295 outf.write(' fSecureSecondaries : %s\n' % zone_secondary_security_string(zoneinfo.fSecureSecondaries))
296 outf.write(' fNotifyLevel : %s\n' % zone_notify_level_string(zoneinfo.fNotifyLevel))
297 if typeid != dnsserver.DNSSRV_TYPEID_ZONE_INFO:
298 outf.write(' aipSecondaries : %s\n' %
299 ip4_array_string(zoneinfo.aipSecondaries))
300 outf.write(' aipNotify : %s\n' %
301 ip4_array_string(zoneinfo.aipNotify))
303 outf.write(' aipSecondaries : %s\n' %
304 dns_addr_array_string(zoneinfo.aipSecondaries))
305 outf.write(' aipNotify : %s\n' %
306 dns_addr_array_string(zoneinfo.aipNotify))
307 outf.write(' fUseWins : %s\n' % bool_string(zoneinfo.fUseWins))
308 outf.write(' fUseNbstat : %s\n' % bool_string(zoneinfo.fUseNbstat))
309 outf.write(' fAging : %s\n' % bool_string(zoneinfo.fAging))
310 outf.write(' dwNoRefreshInterval : %d\n' % zoneinfo.dwNoRefreshInterval)
311 outf.write(' dwRefreshInterval : %d\n' % zoneinfo.dwRefreshInterval)
312 outf.write(' dwAvailForScavengeTime : %d\n' % zoneinfo.dwAvailForScavengeTime)
313 if typeid != dnsserver.DNSSRV_TYPEID_ZONE_INFO:
314 outf.write(' aipScavengeServers : %s\n' %
315 ip4_array_string(zoneinfo.aipScavengeServers))
317 outf.write(' aipScavengeServers : %s\n' %
318 dns_addr_array_string(zoneinfo.aipScavengeServers))
320 if typeid != dnsserver.DNSSRV_TYPEID_ZONE_INFO_W2K:
321 outf.write(' dwRpcStructureVersion : 0x%x\n' % zoneinfo.dwRpcStructureVersion)
322 outf.write(' dwForwarderTimeout : %d\n' % zoneinfo.dwForwarderTimeout)
323 outf.write(' fForwarderSlave : %d\n' % zoneinfo.fForwarderSlave)
324 if typeid != dnsserver.DNSSRV_TYPEID_ZONE_INFO:
325 outf.write(' aipLocalMasters : %s\n' %
326 ip4_array_string(zoneinfo.aipLocalMasters))
328 outf.write(' aipLocalMasters : %s\n' %
329 dns_addr_array_string(zoneinfo.aipLocalMasters))
330 outf.write(' dwDpFlags : %s\n' % dp_flags_string(zoneinfo.dwDpFlags))
331 outf.write(' pszDpFqdn : %s\n' % zoneinfo.pszDpFqdn)
332 outf.write(' pwszZoneDn : %s\n' % zoneinfo.pwszZoneDn)
333 outf.write(' dwLastSuccessfulSoaCheck : %d\n' % zoneinfo.dwLastSuccessfulSoaCheck)
334 outf.write(' dwLastSuccessfulXfr : %d\n' % zoneinfo.dwLastSuccessfulXfr)
336 if typeid == dnsserver.DNSSRV_TYPEID_ZONE_INFO:
337 outf.write(' fQueuedForBackgroundLoad : %s\n' % bool_string(zoneinfo.fQueuedForBackgroundLoad))
338 outf.write(' fBackgroundLoadInProgress : %s\n' % bool_string(zoneinfo.fBackgroundLoadInProgress))
339 outf.write(' fReadOnlyZone : %s\n' % bool_string(zoneinfo.fReadOnlyZone))
340 outf.write(' dwLastXfrAttempt : %d\n' % zoneinfo.dwLastXfrAttempt)
341 outf.write(' dwLastXfrResult : %d\n' % zoneinfo.dwLastXfrResult)
344 def print_zone(outf, typeid, zone):
345 outf.write(' pszZoneName : %s\n' % zone.pszZoneName)
346 outf.write(' Flags : %s\n' % zone_flags_string(zone.Flags))
347 outf.write(' ZoneType : %s\n' % zone_type_string(zone.ZoneType))
348 outf.write(' Version : %s\n' % zone.Version)
350 if typeid != dnsserver.DNSSRV_TYPEID_ZONE_W2K:
351 outf.write(' dwDpFlags : %s\n' % dp_flags_string(zone.dwDpFlags))
352 outf.write(' pszDpFqdn : %s\n' % zone.pszDpFqdn)
355 def print_enumzones(outf, typeid, zones):
356 outf.write(' %d zone(s) found\n' % zones.dwZoneCount)
357 for zone in zones.ZoneArray:
359 print_zone(outf, typeid, zone)
362 def print_dns_record(outf, rec):
363 if rec.wType == dnsp.DNS_TYPE_A:
364 mesg = 'A: %s' % (rec.data)
365 elif rec.wType == dnsp.DNS_TYPE_AAAA:
366 mesg = 'AAAA: %s' % (rec.data)
367 elif rec.wType == dnsp.DNS_TYPE_PTR:
368 mesg = 'PTR: %s' % (rec.data.str)
369 elif rec.wType == dnsp.DNS_TYPE_NS:
370 mesg = 'NS: %s' % (rec.data.str)
371 elif rec.wType == dnsp.DNS_TYPE_CNAME:
372 mesg = 'CNAME: %s' % (rec.data.str)
373 elif rec.wType == dnsp.DNS_TYPE_SOA:
374 mesg = 'SOA: serial=%d, refresh=%d, retry=%d, expire=%d, minttl=%d, ns=%s, email=%s' % (
379 rec.data.dwMinimumTtl,
380 rec.data.NamePrimaryServer.str,
381 rec.data.ZoneAdministratorEmail.str)
382 elif rec.wType == dnsp.DNS_TYPE_MX:
383 mesg = 'MX: %s (%d)' % (rec.data.nameExchange.str, rec.data.wPreference)
384 elif rec.wType == dnsp.DNS_TYPE_SRV:
385 mesg = 'SRV: %s (%d, %d, %d)' % (rec.data.nameTarget.str, rec.data.wPort,
386 rec.data.wPriority, rec.data.wWeight)
387 elif rec.wType == dnsp.DNS_TYPE_TXT:
388 slist = ['"%s"' % name.str for name in rec.data.str]
389 mesg = 'TXT: %s' % ','.join(slist)
392 outf.write(' %s (flags=%x, serial=%d, ttl=%d)\n' % (
393 mesg, rec.dwFlags, rec.dwSerial, rec.dwTtlSeconds))
396 def print_dnsrecords(outf, records):
397 for rec in records.rec:
398 outf.write(' Name=%s, Records=%d, Children=%d\n' % (
402 for dns_rec in rec.records:
403 print_dns_record(outf, dns_rec)
406 # Convert data into a dns record
407 def data_to_dns_record(record_type, data):
408 if record_type == dnsp.DNS_TYPE_A:
410 elif record_type == dnsp.DNS_TYPE_AAAA:
411 rec = AAAARecord(data)
412 elif record_type == dnsp.DNS_TYPE_PTR:
413 rec = PTRRecord(data)
414 elif record_type == dnsp.DNS_TYPE_CNAME:
415 rec = CNameRecord(data)
416 elif record_type == dnsp.DNS_TYPE_NS:
418 elif record_type == dnsp.DNS_TYPE_MX:
419 tmp = data.split(' ')
421 raise CommandError('Data requires 2 elements - mail_server, preference')
423 preference = int(tmp[1])
424 rec = MXRecord(mail_server, preference)
425 elif record_type == dnsp.DNS_TYPE_SRV:
426 tmp = data.split(' ')
428 raise CommandError('Data requires 4 elements - server, port, priority, weight')
431 priority = int(tmp[2])
433 rec = SRVRecord(server, port, priority=priority, weight=weight)
434 elif record_type == dnsp.DNS_TYPE_SOA:
435 tmp = data.split(' ')
437 raise CommandError('Data requires 7 elements - nameserver, email, serial, '
438 'refresh, retry, expire, minimumttl')
442 refresh = int(tmp[3])
445 minimum = int(tmp[6])
446 rec = SOARecord(nameserver, email, serial=serial, refresh=refresh,
447 retry=retry, expire=expire, minimum=minimum)
448 elif record_type == dnsp.DNS_TYPE_TXT:
449 slist = shlex.split(data)
450 rec = TXTRecord(slist)
452 raise CommandError('Unsupported record type')
456 # Match dns name (of type DNS_RPC_NAME)
457 def dns_name_equal(n1, n2):
458 return n1.str.rstrip('.').lower() == n2.str.rstrip('.').lower()
461 # Match a dns record with specified data
462 def dns_record_match(dns_conn, server, zone, name, record_type, data):
463 urec = data_to_dns_record(record_type, data)
465 select_flags = dnsserver.DNS_RPC_VIEW_AUTHORITY_DATA
468 buflen, res = dns_conn.DnssrvEnumRecords2(
469 dnsserver.DNS_CLIENT_VERSION_LONGHORN, 0, server, zone, name, None,
470 record_type, select_flags, None, None)
471 except WERRORError as e:
472 if e.args[0] == werror.WERR_DNS_ERROR_NAME_DOES_NOT_EXIST:
473 # Either the zone doesn't exist, or there were no records.
474 # We can't differentiate the two.
478 if not res or res.count == 0:
481 for rec in res.rec[0].records:
482 if rec.wType != record_type:
486 if record_type == dnsp.DNS_TYPE_A:
487 if rec.data == urec.data:
489 elif record_type == dnsp.DNS_TYPE_AAAA:
490 if rec.data == urec.data:
492 elif record_type == dnsp.DNS_TYPE_PTR:
493 if dns_name_equal(rec.data, urec.data):
495 elif record_type == dnsp.DNS_TYPE_CNAME:
496 if dns_name_equal(rec.data, urec.data):
498 elif record_type == dnsp.DNS_TYPE_NS:
499 if dns_name_equal(rec.data, urec.data):
501 elif record_type == dnsp.DNS_TYPE_MX:
502 if dns_name_equal(rec.data.nameExchange, urec.data.nameExchange) and \
503 rec.data.wPreference == urec.data.wPreference:
505 elif record_type == dnsp.DNS_TYPE_SRV:
506 if rec.data.wPriority == urec.data.wPriority and \
507 rec.data.wWeight == urec.data.wWeight and \
508 rec.data.wPort == urec.data.wPort and \
509 dns_name_equal(rec.data.nameTarget, urec.data.nameTarget):
511 elif record_type == dnsp.DNS_TYPE_SOA:
512 if rec.data.dwSerialNo == urec.data.dwSerialNo and \
513 rec.data.dwRefresh == urec.data.dwRefresh and \
514 rec.data.dwRetry == urec.data.dwRetry and \
515 rec.data.dwExpire == urec.data.dwExpire and \
516 rec.data.dwMinimumTtl == urec.data.dwMinimumTtl and \
517 dns_name_equal(rec.data.NamePrimaryServer,
518 urec.data.NamePrimaryServer) and \
519 dns_name_equal(rec.data.ZoneAdministratorEmail,
520 urec.data.ZoneAdministratorEmail):
522 elif record_type == dnsp.DNS_TYPE_TXT:
523 if rec.data.count == urec.data.count:
525 for i in range(rec.data.count):
527 (rec.data.str[i].str == urec.data.str[i].str)
535 class cmd_serverinfo(Command):
536 """Query for Server information."""
538 synopsis = '%prog <server> [options]'
540 takes_args = ['server']
542 takes_optiongroups = {
543 "sambaopts": options.SambaOptions,
544 "versionopts": options.VersionOptions,
545 "credopts": options.CredentialsOptions,
549 Option('--client-version', help='Client Version',
550 default='longhorn', metavar='w2k|dotnet|longhorn',
551 choices=['w2k', 'dotnet', 'longhorn'], dest='cli_ver'),
554 def run(self, server, cli_ver, sambaopts=None, credopts=None,
556 self.lp = sambaopts.get_loadparm()
557 self.creds = credopts.get_credentials(self.lp)
558 dns_conn = dns_connect(server, self.lp, self.creds)
560 client_version = dns_client_version(cli_ver)
562 typeid, res = dns_conn.DnssrvQuery2(client_version, 0, server,
564 print_serverinfo(self.outf, typeid, res)
567 class cmd_zoneinfo(Command):
568 """Query for zone information."""
570 synopsis = '%prog <server> <zone> [options]'
572 takes_args = ['server', 'zone']
574 takes_optiongroups = {
575 "sambaopts": options.SambaOptions,
576 "versionopts": options.VersionOptions,
577 "credopts": options.CredentialsOptions,
581 Option('--client-version', help='Client Version',
582 default='longhorn', metavar='w2k|dotnet|longhorn',
583 choices=['w2k', 'dotnet', 'longhorn'], dest='cli_ver'),
586 def run(self, server, zone, cli_ver, sambaopts=None, credopts=None,
588 self.lp = sambaopts.get_loadparm()
589 self.creds = credopts.get_credentials(self.lp)
590 dns_conn = dns_connect(server, self.lp, self.creds)
592 client_version = dns_client_version(cli_ver)
594 typeid, res = dns_conn.DnssrvQuery2(client_version, 0, server, zone,
596 print_zoneinfo(self.outf, typeid, res)
599 class cmd_zonelist(Command):
600 """Query for zones."""
602 synopsis = '%prog <server> [options]'
604 takes_args = ['server']
606 takes_optiongroups = {
607 "sambaopts": options.SambaOptions,
608 "versionopts": options.VersionOptions,
609 "credopts": options.CredentialsOptions,
613 Option('--client-version', help='Client Version',
614 default='longhorn', metavar='w2k|dotnet|longhorn',
615 choices=['w2k', 'dotnet', 'longhorn'], dest='cli_ver'),
616 Option('--primary', help='List primary zones (default)',
617 action='store_true', dest='primary'),
618 Option('--secondary', help='List secondary zones',
619 action='store_true', dest='secondary'),
620 Option('--cache', help='List cached zones',
621 action='store_true', dest='cache'),
622 Option('--auto', help='List automatically created zones',
623 action='store_true', dest='auto'),
624 Option('--forward', help='List forward zones',
625 action='store_true', dest='forward'),
626 Option('--reverse', help='List reverse zones',
627 action='store_true', dest='reverse'),
628 Option('--ds', help='List directory integrated zones',
629 action='store_true', dest='ds'),
630 Option('--non-ds', help='List non-directory zones',
631 action='store_true', dest='nonds')
634 def run(self, server, cli_ver, primary=False, secondary=False, cache=False,
635 auto=False, forward=False, reverse=False, ds=False, nonds=False,
636 sambaopts=None, credopts=None, versionopts=None):
640 request_filter |= dnsserver.DNS_ZONE_REQUEST_PRIMARY
642 request_filter |= dnsserver.DNS_ZONE_REQUEST_SECONDARY
644 request_filter |= dnsserver.DNS_ZONE_REQUEST_CACHE
646 request_filter |= dnsserver.DNS_ZONE_REQUEST_AUTO
648 request_filter |= dnsserver.DNS_ZONE_REQUEST_FORWARD
650 request_filter |= dnsserver.DNS_ZONE_REQUEST_REVERSE
652 request_filter |= dnsserver.DNS_ZONE_REQUEST_DS
654 request_filter |= dnsserver.DNS_ZONE_REQUEST_NON_DS
656 if request_filter == 0:
657 request_filter = dnsserver.DNS_ZONE_REQUEST_PRIMARY
659 self.lp = sambaopts.get_loadparm()
660 self.creds = credopts.get_credentials(self.lp)
661 dns_conn = dns_connect(server, self.lp, self.creds)
663 client_version = dns_client_version(cli_ver)
665 typeid, res = dns_conn.DnssrvComplexOperation2(client_version,
668 dnsserver.DNSSRV_TYPEID_DWORD,
671 if client_version == dnsserver.DNS_CLIENT_VERSION_W2K:
672 typeid = dnsserver.DNSSRV_TYPEID_ZONE_W2K
674 typeid = dnsserver.DNSSRV_TYPEID_ZONE
675 print_enumzones(self.outf, typeid, res)
678 class cmd_zonecreate(Command):
681 synopsis = '%prog <server> <zone> [options]'
683 takes_args = ['server', 'zone']
685 takes_optiongroups = {
686 "sambaopts": options.SambaOptions,
687 "versionopts": options.VersionOptions,
688 "credopts": options.CredentialsOptions,
692 Option('--client-version', help='Client Version',
693 default='longhorn', metavar='w2k|dotnet|longhorn',
694 choices=['w2k', 'dotnet', 'longhorn'], dest='cli_ver')
697 def run(self, server, zone, cli_ver, sambaopts=None, credopts=None,
700 self.lp = sambaopts.get_loadparm()
701 self.creds = credopts.get_credentials(self.lp)
702 dns_conn = dns_connect(server, self.lp, self.creds)
706 client_version = dns_client_version(cli_ver)
707 if client_version == dnsserver.DNS_CLIENT_VERSION_W2K:
708 typeid = dnsserver.DNSSRV_TYPEID_ZONE_CREATE_W2K
709 zone_create_info = dnsserver.DNS_RPC_ZONE_CREATE_INFO_W2K()
710 zone_create_info.pszZoneName = zone
711 zone_create_info.dwZoneType = dnsp.DNS_ZONE_TYPE_PRIMARY
712 zone_create_info.fAging = 0
713 zone_create_info.fDsIntegrated = 1
714 zone_create_info.fLoadExisting = 1
715 elif client_version == dnsserver.DNS_CLIENT_VERSION_DOTNET:
716 typeid = dnsserver.DNSSRV_TYPEID_ZONE_CREATE_DOTNET
717 zone_create_info = dnsserver.DNS_RPC_ZONE_CREATE_INFO_DOTNET()
718 zone_create_info.pszZoneName = zone
719 zone_create_info.dwZoneType = dnsp.DNS_ZONE_TYPE_PRIMARY
720 zone_create_info.fAging = 0
721 zone_create_info.fDsIntegrated = 1
722 zone_create_info.fLoadExisting = 1
723 zone_create_info.dwDpFlags = dnsserver.DNS_DP_DOMAIN_DEFAULT
725 typeid = dnsserver.DNSSRV_TYPEID_ZONE_CREATE
726 zone_create_info = dnsserver.DNS_RPC_ZONE_CREATE_INFO_LONGHORN()
727 zone_create_info.pszZoneName = zone
728 zone_create_info.dwZoneType = dnsp.DNS_ZONE_TYPE_PRIMARY
729 zone_create_info.fAging = 0
730 zone_create_info.fDsIntegrated = 1
731 zone_create_info.fLoadExisting = 1
732 zone_create_info.dwDpFlags = dnsserver.DNS_DP_DOMAIN_DEFAULT
734 res = dns_conn.DnssrvOperation2(client_version, 0, server, None,
735 0, 'ZoneCreate', typeid,
738 typeid = dnsserver.DNSSRV_TYPEID_NAME_AND_PARAM
739 name_and_param = dnsserver.DNS_RPC_NAME_AND_PARAM()
740 name_and_param.pszNodeName = 'AllowUpdate'
741 name_and_param.dwParam = dnsp.DNS_ZONE_UPDATE_SECURE
744 res = dns_conn.DnssrvOperation2(client_version, 0, server, zone,
745 0, 'ResetDwordProperty', typeid,
747 except WERRORError as e:
748 if e.args[0] == werror.WERR_DNS_ERROR_ZONE_ALREADY_EXISTS:
749 self.outf.write('Zone already exists.')
752 self.outf.write('Zone %s created successfully\n' % zone)
755 class cmd_zonedelete(Command):
758 synopsis = '%prog <server> <zone> [options]'
760 takes_args = ['server', 'zone']
762 takes_optiongroups = {
763 "sambaopts": options.SambaOptions,
764 "versionopts": options.VersionOptions,
765 "credopts": options.CredentialsOptions,
768 def run(self, server, zone, sambaopts=None, credopts=None,
771 self.lp = sambaopts.get_loadparm()
772 self.creds = credopts.get_credentials(self.lp)
773 dns_conn = dns_connect(server, self.lp, self.creds)
777 res = dns_conn.DnssrvOperation2(dnsserver.DNS_CLIENT_VERSION_LONGHORN,
778 0, server, zone, 0, 'DeleteZoneFromDs',
779 dnsserver.DNSSRV_TYPEID_NULL,
781 except WERRORError as e:
782 if e.args[0] == werror.WERR_DNS_ERROR_ZONE_DOES_NOT_EXIST:
783 self.outf.write('Zone does not exist and so could not be deleted.')
786 self.outf.write('Zone %s deleted successfully\n' % zone)
789 class cmd_query(Command):
792 synopsis = '%prog <server> <zone> <name> <A|AAAA|CNAME|MX|NS|SOA|SRV|TXT|ALL> [options]'
794 takes_args = ['server', 'zone', 'name', 'rtype']
796 takes_optiongroups = {
797 "sambaopts": options.SambaOptions,
798 "versionopts": options.VersionOptions,
799 "credopts": options.CredentialsOptions,
803 Option('--authority', help='Search authoritative records (default)',
804 action='store_true', dest='authority'),
805 Option('--cache', help='Search cached records',
806 action='store_true', dest='cache'),
807 Option('--glue', help='Search glue records',
808 action='store_true', dest='glue'),
809 Option('--root', help='Search root hints',
810 action='store_true', dest='root'),
811 Option('--additional', help='List additional records',
812 action='store_true', dest='additional'),
813 Option('--no-children', help='Do not list children',
814 action='store_true', dest='no_children'),
815 Option('--only-children', help='List only children',
816 action='store_true', dest='only_children')
819 def run(self, server, zone, name, rtype, authority=False, cache=False,
820 glue=False, root=False, additional=False, no_children=False,
821 only_children=False, sambaopts=None, credopts=None,
823 record_type = dns_type_flag(rtype)
825 if name.find('*') != -1:
826 self.outf.write('use "@" to dump entire domain, looking up %s\n' %
831 select_flags |= dnsserver.DNS_RPC_VIEW_AUTHORITY_DATA
833 select_flags |= dnsserver.DNS_RPC_VIEW_CACHE_DATA
835 select_flags |= dnsserver.DNS_RPC_VIEW_GLUE_DATA
837 select_flags |= dnsserver.DNS_RPC_VIEW_ROOT_HINT_DATA
839 select_flags |= dnsserver.DNS_RPC_VIEW_ADDITIONAL_DATA
841 select_flags |= dnsserver.DNS_RPC_VIEW_NO_CHILDREN
843 select_flags |= dnsserver.DNS_RPC_VIEW_ONLY_CHILDREN
845 if select_flags == 0:
846 select_flags = dnsserver.DNS_RPC_VIEW_AUTHORITY_DATA
848 if select_flags == dnsserver.DNS_RPC_VIEW_ADDITIONAL_DATA:
849 self.outf.write('Specify either --authority or --root along with --additional.\n')
850 self.outf.write('Assuming --authority.\n')
851 select_flags |= dnsserver.DNS_RPC_VIEW_AUTHORITY_DATA
853 self.lp = sambaopts.get_loadparm()
854 self.creds = credopts.get_credentials(self.lp)
855 dns_conn = dns_connect(server, self.lp, self.creds)
858 buflen, res = dns_conn.DnssrvEnumRecords2(
859 dnsserver.DNS_CLIENT_VERSION_LONGHORN, 0, server, zone, name,
860 None, record_type, select_flags, None, None)
861 except WERRORError as e:
862 if e.args[0] == werror.WERR_DNS_ERROR_NAME_DOES_NOT_EXIST:
863 self.outf.write('Record or zone does not exist.')
866 print_dnsrecords(self.outf, res)
869 class cmd_roothints(Command):
870 """Query root hints."""
872 synopsis = '%prog <server> [<name>] [options]'
874 takes_args = ['server', 'name?']
876 takes_optiongroups = {
877 "sambaopts": options.SambaOptions,
878 "versionopts": options.VersionOptions,
879 "credopts": options.CredentialsOptions,
882 def run(self, server, name='.', sambaopts=None, credopts=None,
884 record_type = dnsp.DNS_TYPE_NS
885 select_flags = (dnsserver.DNS_RPC_VIEW_ROOT_HINT_DATA |
886 dnsserver.DNS_RPC_VIEW_ADDITIONAL_DATA)
888 self.lp = sambaopts.get_loadparm()
889 self.creds = credopts.get_credentials(self.lp)
890 dns_conn = dns_connect(server, self.lp, self.creds)
892 buflen, res = dns_conn.DnssrvEnumRecords2(
893 dnsserver.DNS_CLIENT_VERSION_LONGHORN, 0, server, '..RootHints',
894 name, None, record_type, select_flags, None, None)
895 print_dnsrecords(self.outf, res)
898 class cmd_add_record(Command):
901 For each type data contents are as follows:
902 A ipv4_address_string
903 AAAA ipv6_address_string
907 MX "fqdn_string preference"
908 SRV "fqdn_string port priority weight"
909 TXT "'string1' 'string2' ..."
912 synopsis = '%prog <server> <zone> <name> <A|AAAA|PTR|CNAME|NS|MX|SRV|TXT> <data>'
914 takes_args = ['server', 'zone', 'name', 'rtype', 'data']
916 takes_optiongroups = {
917 "sambaopts": options.SambaOptions,
918 "versionopts": options.VersionOptions,
919 "credopts": options.CredentialsOptions,
922 def run(self, server, zone, name, rtype, data, sambaopts=None,
923 credopts=None, versionopts=None):
925 if rtype.upper() not in ('A', 'AAAA', 'PTR', 'CNAME', 'NS', 'MX', 'SRV', 'TXT'):
926 raise CommandError('Adding record of type %s is not supported' % rtype)
928 record_type = dns_type_flag(rtype)
929 rec = data_to_dns_record(record_type, data)
931 self.lp = sambaopts.get_loadparm()
932 self.creds = credopts.get_credentials(self.lp)
933 dns_conn = dns_connect(server, self.lp, self.creds)
935 add_rec_buf = dnsserver.DNS_RPC_RECORD_BUF()
936 add_rec_buf.rec = rec
939 dns_conn.DnssrvUpdateRecord2(dnsserver.DNS_CLIENT_VERSION_LONGHORN,
940 0, server, zone, name, add_rec_buf, None)
941 except WERRORError as e:
942 if e.args[0] == werror.WERR_DNS_ERROR_NAME_DOES_NOT_EXIST:
943 self.outf.write('Zone does not exist; record could not be added.\n')
946 self.outf.write('Record added successfully\n')
949 class cmd_update_record(Command):
950 """Update a DNS record
952 For each type data contents are as follows:
953 A ipv4_address_string
954 AAAA ipv6_address_string
958 MX "fqdn_string preference"
959 SOA "fqdn_dns fqdn_email serial refresh retry expire minimumttl"
960 SRV "fqdn_string port priority weight"
961 TXT "'string1' 'string2' ..."
964 synopsis = '%prog <server> <zone> <name> <A|AAAA|PTR|CNAME|NS|MX|SOA|SRV|TXT> <olddata> <newdata>'
966 takes_args = ['server', 'zone', 'name', 'rtype', 'olddata', 'newdata']
968 takes_optiongroups = {
969 "sambaopts": options.SambaOptions,
970 "versionopts": options.VersionOptions,
971 "credopts": options.CredentialsOptions,
974 def run(self, server, zone, name, rtype, olddata, newdata,
975 sambaopts=None, credopts=None, versionopts=None):
977 if rtype.upper() not in ('A', 'AAAA', 'PTR', 'CNAME', 'NS', 'MX', 'SOA', 'SRV', 'TXT'):
978 raise CommandError('Updating record of type %s is not supported' % rtype)
980 record_type = dns_type_flag(rtype)
981 rec = data_to_dns_record(record_type, newdata)
983 self.lp = sambaopts.get_loadparm()
984 self.creds = credopts.get_credentials(self.lp)
985 dns_conn = dns_connect(server, self.lp, self.creds)
987 rec_match = dns_record_match(dns_conn, server, zone, name, record_type,
990 raise CommandError('Record or zone does not exist.')
992 # Copy properties from existing record to new record
993 rec.dwFlags = rec_match.dwFlags
994 rec.dwSerial = rec_match.dwSerial
995 rec.dwTtlSeconds = rec_match.dwTtlSeconds
996 rec.dwTimeStamp = rec_match.dwTimeStamp
998 add_rec_buf = dnsserver.DNS_RPC_RECORD_BUF()
999 add_rec_buf.rec = rec
1001 del_rec_buf = dnsserver.DNS_RPC_RECORD_BUF()
1002 del_rec_buf.rec = rec_match
1005 dns_conn.DnssrvUpdateRecord2(dnsserver.DNS_CLIENT_VERSION_LONGHORN,
1012 except WERRORError as e:
1013 if e.args[0] == werror.WERR_DNS_ERROR_NAME_DOES_NOT_EXIST:
1014 self.outf.write('Zone does not exist; record could not be updated.\n')
1017 self.outf.write('Record updated successfully\n')
1020 class cmd_delete_record(Command):
1021 """Delete a DNS record
1023 For each type data contents are as follows:
1024 A ipv4_address_string
1025 AAAA ipv6_address_string
1029 MX "fqdn_string preference"
1030 SRV "fqdn_string port priority weight"
1031 TXT "'string1' 'string2' ..."
1034 synopsis = '%prog <server> <zone> <name> <A|AAAA|PTR|CNAME|NS|MX|SRV|TXT> <data>'
1036 takes_args = ['server', 'zone', 'name', 'rtype', 'data']
1038 takes_optiongroups = {
1039 "sambaopts": options.SambaOptions,
1040 "versionopts": options.VersionOptions,
1041 "credopts": options.CredentialsOptions,
1044 def run(self, server, zone, name, rtype, data, sambaopts=None, credopts=None, versionopts=None):
1046 if rtype.upper() not in ('A', 'AAAA', 'PTR', 'CNAME', 'NS', 'MX', 'SRV', 'TXT'):
1047 raise CommandError('Deleting record of type %s is not supported' % rtype)
1049 record_type = dns_type_flag(rtype)
1050 rec = data_to_dns_record(record_type, data)
1052 self.lp = sambaopts.get_loadparm()
1053 self.creds = credopts.get_credentials(self.lp)
1054 dns_conn = dns_connect(server, self.lp, self.creds)
1056 del_rec_buf = dnsserver.DNS_RPC_RECORD_BUF()
1057 del_rec_buf.rec = rec
1060 dns_conn.DnssrvUpdateRecord2(dnsserver.DNS_CLIENT_VERSION_LONGHORN,
1067 except WERRORError as e:
1068 if e.args[0] == werror.WERR_DNS_ERROR_NAME_DOES_NOT_EXIST:
1069 self.outf.write('Zone does not exist; record could not be deleted.\n')
1072 self.outf.write('Record deleted successfully\n')
1075 class cmd_cleanup_record(Command):
1076 """Cleanup DNS records for a DNS host.
1080 samba-tool dns cleanup dc1 dc1.samdom.test.site -U USER%PASSWORD
1082 NOTE: This command in many cases will only mark the `dNSTombstoned` attr
1083 as `TRUE` on the DNS records. Querying will no longer return results but
1084 there may still be some placeholder entries in the database.
1087 synopsis = '%prog <server> <dnshostname>'
1089 takes_args = ['server', 'dnshostname']
1091 takes_optiongroups = {
1092 "sambaopts": options.SambaOptions,
1093 "versionopts": options.VersionOptions,
1094 "credopts": options.CredentialsOptions,
1098 Option("-v", "--verbose", help="Be verbose", action="store_true"),
1099 Option("-q", "--quiet", help="Be quiet", action="store_true"),
1102 def run(self, server, dnshostname, sambaopts=None, credopts=None,
1103 versionopts=None, verbose=False, quiet=False):
1104 lp = sambaopts.get_loadparm()
1105 creds = credopts.get_credentials(lp)
1107 logger = self.get_logger()
1109 logger.setLevel(logging.DEBUG)
1111 logger.setLevel(logging.WARNING)
1113 logger.setLevel(logging.INFO)
1115 samdb = SamDB(url="ldap://%s" % server,
1116 session_info=system_session(),
1117 credentials=creds, lp=lp)
1119 remove_dc.remove_dns_references(samdb, logger, dnshostname,
1120 ignore_no_name=True)
1123 class cmd_dns(SuperCommand):
1124 """Domain Name Service (DNS) management."""
1127 subcommands['serverinfo'] = cmd_serverinfo()
1128 subcommands['zoneinfo'] = cmd_zoneinfo()
1129 subcommands['zonelist'] = cmd_zonelist()
1130 subcommands['zonecreate'] = cmd_zonecreate()
1131 subcommands['zonedelete'] = cmd_zonedelete()
1132 subcommands['query'] = cmd_query()
1133 subcommands['roothints'] = cmd_roothints()
1134 subcommands['add'] = cmd_add_record()
1135 subcommands['update'] = cmd_update_record()
1136 subcommands['delete'] = cmd_delete_record()
1137 subcommands['cleanup'] = cmd_cleanup_record()