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
44 def dns_connect(server, lp, creds):
45 if server.lower() == 'localhost':
47 binding_str = "ncacn_ip_tcp:%s[sign]" % server
49 dns_conn = dnsserver.dnsserver(binding_str, lp, creds)
50 except RuntimeError as e:
51 raise CommandError('Connecting to DNS RPC server %s failed with %s' % (server, e))
56 def bool_string(flag):
62 ret = 'UNKNOWN (0x%x)' % flag
66 def enum_string(module, enum_defs, value):
69 if value == getattr(module, e):
73 ret = 'UNKNOWN (0x%x)' % value
77 def bitmap_string(module, bitmap_defs, value):
80 if value & getattr(module, b):
87 def boot_method_string(boot_method):
88 enum_defs = ['DNS_BOOT_METHOD_UNINITIALIZED', 'DNS_BOOT_METHOD_FILE',
89 'DNS_BOOT_METHOD_REGISTRY', 'DNS_BOOT_METHOD_DIRECTORY']
90 return enum_string(dnsserver, enum_defs, boot_method)
93 def name_check_flag_string(check_flag):
94 enum_defs = ['DNS_ALLOW_RFC_NAMES_ONLY', 'DNS_ALLOW_NONRFC_NAMES',
95 'DNS_ALLOW_MULTIBYTE_NAMES', 'DNS_ALLOW_ALL_NAMES']
96 return enum_string(dnsserver, enum_defs, check_flag)
99 def zone_type_string(zone_type):
100 enum_defs = ['DNS_ZONE_TYPE_CACHE', 'DNS_ZONE_TYPE_PRIMARY',
101 'DNS_ZONE_TYPE_SECONDARY', 'DNS_ZONE_TYPE_STUB',
102 'DNS_ZONE_TYPE_FORWARDER', 'DNS_ZONE_TYPE_SECONDARY_CACHE']
103 return enum_string(dnsp, enum_defs, zone_type)
106 def zone_update_string(zone_update):
107 enum_defs = ['DNS_ZONE_UPDATE_OFF', 'DNS_ZONE_UPDATE_UNSECURE',
108 'DNS_ZONE_UPDATE_SECURE']
109 return enum_string(dnsp, enum_defs, zone_update)
112 def zone_secondary_security_string(security):
113 enum_defs = ['DNS_ZONE_SECSECURE_NO_SECURITY', 'DNS_ZONE_SECSECURE_NS_ONLY',
114 'DNS_ZONE_SECSECURE_LIST_ONLY', 'DNS_ZONE_SECSECURE_NO_XFER']
115 return enum_string(dnsserver, enum_defs, security)
118 def zone_notify_level_string(notify_level):
119 enum_defs = ['DNS_ZONE_NOTIFY_OFF', 'DNS_ZONE_NOTIFY_ALL_SECONDARIES',
120 'DNS_ZONE_NOTIFY_LIST_ONLY']
121 return enum_string(dnsserver, enum_defs, notify_level)
124 def dp_flags_string(dp_flags):
125 bitmap_defs = ['DNS_DP_AUTOCREATED', 'DNS_DP_LEGACY', 'DNS_DP_DOMAIN_DEFAULT',
126 'DNS_DP_FOREST_DEFAULT', 'DNS_DP_ENLISTED', 'DNS_DP_DELETED']
127 return bitmap_string(dnsserver, bitmap_defs, dp_flags)
130 def zone_flags_string(flags):
131 bitmap_defs = ['DNS_RPC_ZONE_PAUSED', 'DNS_RPC_ZONE_SHUTDOWN',
132 'DNS_RPC_ZONE_REVERSE', 'DNS_RPC_ZONE_AUTOCREATED',
133 'DNS_RPC_ZONE_DSINTEGRATED', 'DNS_RPC_ZONE_AGING',
134 'DNS_RPC_ZONE_UPDATE_UNSECURE', 'DNS_RPC_ZONE_UPDATE_SECURE',
135 'DNS_RPC_ZONE_READONLY']
136 return bitmap_string(dnsserver, bitmap_defs, flags)
139 def ip4_array_string(array):
143 for i in range(array.AddrCount):
144 addr = inet_ntop(AF_INET, pack('I', array.AddrArray[i]))
149 def dns_addr_array_string(array):
153 for i in range(array.AddrCount):
154 if array.AddrArray[i].MaxSa[0] == 0x02:
155 x = "".join([chr(b) for b in array.AddrArray[i].MaxSa])[4:8]
156 addr = inet_ntop(AF_INET, x)
157 elif array.AddrArray[i].MaxSa[0] == 0x17:
158 x = "".join([chr(b) for b in array.AddrArray[i].MaxSa])[8:24]
159 addr = inet_ntop(AF_INET6, x)
166 def dns_type_flag(rec_type):
167 rtype = rec_type.upper()
169 record_type = dnsp.DNS_TYPE_A
170 elif rtype == 'AAAA':
171 record_type = dnsp.DNS_TYPE_AAAA
173 record_type = dnsp.DNS_TYPE_PTR
175 record_type = dnsp.DNS_TYPE_NS
176 elif rtype == 'CNAME':
177 record_type = dnsp.DNS_TYPE_CNAME
179 record_type = dnsp.DNS_TYPE_SOA
181 record_type = dnsp.DNS_TYPE_MX
183 record_type = dnsp.DNS_TYPE_SRV
185 record_type = dnsp.DNS_TYPE_TXT
187 record_type = dnsp.DNS_TYPE_ALL
189 raise CommandError('Unknown type of DNS record %s' % rec_type)
193 def dns_client_version(cli_version):
194 version = cli_version.upper()
196 client_version = dnsserver.DNS_CLIENT_VERSION_W2K
197 elif version == 'DOTNET':
198 client_version = dnsserver.DNS_CLIENT_VERSION_DOTNET
199 elif version == 'LONGHORN':
200 client_version = dnsserver.DNS_CLIENT_VERSION_LONGHORN
202 raise CommandError('Unknown client version %s' % cli_version)
203 return client_version
206 def print_serverinfo(outf, typeid, serverinfo):
207 outf.write(' dwVersion : 0x%x\n' % serverinfo.dwVersion)
208 outf.write(' fBootMethod : %s\n' % boot_method_string(serverinfo.fBootMethod))
209 outf.write(' fAdminConfigured : %s\n' % bool_string(serverinfo.fAdminConfigured))
210 outf.write(' fAllowUpdate : %s\n' % bool_string(serverinfo.fAllowUpdate))
211 outf.write(' fDsAvailable : %s\n' % bool_string(serverinfo.fDsAvailable))
212 outf.write(' pszServerName : %s\n' % serverinfo.pszServerName)
213 outf.write(' pszDsContainer : %s\n' % serverinfo.pszDsContainer)
215 if typeid != dnsserver.DNSSRV_TYPEID_SERVER_INFO:
216 outf.write(' aipServerAddrs : %s\n' %
217 ip4_array_string(serverinfo.aipServerAddrs))
218 outf.write(' aipListenAddrs : %s\n' %
219 ip4_array_string(serverinfo.aipListenAddrs))
220 outf.write(' aipForwarders : %s\n' %
221 ip4_array_string(serverinfo.aipForwarders))
223 outf.write(' aipServerAddrs : %s\n' %
224 dns_addr_array_string(serverinfo.aipServerAddrs))
225 outf.write(' aipListenAddrs : %s\n' %
226 dns_addr_array_string(serverinfo.aipListenAddrs))
227 outf.write(' aipForwarders : %s\n' %
228 dns_addr_array_string(serverinfo.aipForwarders))
230 outf.write(' dwLogLevel : %d\n' % serverinfo.dwLogLevel)
231 outf.write(' dwDebugLevel : %d\n' % serverinfo.dwDebugLevel)
232 outf.write(' dwForwardTimeout : %d\n' % serverinfo.dwForwardTimeout)
233 outf.write(' dwRpcPrototol : 0x%x\n' % serverinfo.dwRpcProtocol)
234 outf.write(' dwNameCheckFlag : %s\n' % name_check_flag_string(serverinfo.dwNameCheckFlag))
235 outf.write(' cAddressAnswerLimit : %d\n' % serverinfo.cAddressAnswerLimit)
236 outf.write(' dwRecursionRetry : %d\n' % serverinfo.dwRecursionRetry)
237 outf.write(' dwRecursionTimeout : %d\n' % serverinfo.dwRecursionTimeout)
238 outf.write(' dwMaxCacheTtl : %d\n' % serverinfo.dwMaxCacheTtl)
239 outf.write(' dwDsPollingInterval : %d\n' % serverinfo.dwDsPollingInterval)
240 outf.write(' dwScavengingInterval : %d\n' % serverinfo.dwScavengingInterval)
241 outf.write(' dwDefaultRefreshInterval : %d\n' % serverinfo.dwDefaultRefreshInterval)
242 outf.write(' dwDefaultNoRefreshInterval : %d\n' % serverinfo.dwDefaultNoRefreshInterval)
243 outf.write(' fAutoReverseZones : %s\n' % bool_string(serverinfo.fAutoReverseZones))
244 outf.write(' fAutoCacheUpdate : %s\n' % bool_string(serverinfo.fAutoCacheUpdate))
245 outf.write(' fRecurseAfterForwarding : %s\n' % bool_string(serverinfo.fRecurseAfterForwarding))
246 outf.write(' fForwardDelegations : %s\n' % bool_string(serverinfo.fForwardDelegations))
247 outf.write(' fNoRecursion : %s\n' % bool_string(serverinfo.fNoRecursion))
248 outf.write(' fSecureResponses : %s\n' % bool_string(serverinfo.fSecureResponses))
249 outf.write(' fRoundRobin : %s\n' % bool_string(serverinfo.fRoundRobin))
250 outf.write(' fLocalNetPriority : %s\n' % bool_string(serverinfo.fLocalNetPriority))
251 outf.write(' fBindSecondaries : %s\n' % bool_string(serverinfo.fBindSecondaries))
252 outf.write(' fWriteAuthorityNs : %s\n' % bool_string(serverinfo.fWriteAuthorityNs))
253 outf.write(' fStrictFileParsing : %s\n' % bool_string(serverinfo.fStrictFileParsing))
254 outf.write(' fLooseWildcarding : %s\n' % bool_string(serverinfo.fLooseWildcarding))
255 outf.write(' fDefaultAgingState : %s\n' % bool_string(serverinfo.fDefaultAgingState))
257 if typeid != dnsserver.DNSSRV_TYPEID_SERVER_INFO_W2K:
258 outf.write(' dwRpcStructureVersion : 0x%x\n' % serverinfo.dwRpcStructureVersion)
259 outf.write(' aipLogFilter : %s\n' % dns_addr_array_string(serverinfo.aipLogFilter))
260 outf.write(' pwszLogFilePath : %s\n' % serverinfo.pwszLogFilePath)
261 outf.write(' pszDomainName : %s\n' % serverinfo.pszDomainName)
262 outf.write(' pszForestName : %s\n' % serverinfo.pszForestName)
263 outf.write(' pszDomainDirectoryPartition : %s\n' % serverinfo.pszDomainDirectoryPartition)
264 outf.write(' pszForestDirectoryPartition : %s\n' % serverinfo.pszForestDirectoryPartition)
266 outf.write(' dwLocalNetPriorityNetMask : 0x%x\n' % serverinfo.dwLocalNetPriorityNetMask)
267 outf.write(' dwLastScavengeTime : %d\n' % serverinfo.dwLastScavengeTime)
268 outf.write(' dwEventLogLevel : %d\n' % serverinfo.dwEventLogLevel)
269 outf.write(' dwLogFileMaxSize : %d\n' % serverinfo.dwLogFileMaxSize)
270 outf.write(' dwDsForestVersion : %d\n' % serverinfo.dwDsForestVersion)
271 outf.write(' dwDsDomainVersion : %d\n' % serverinfo.dwDsDomainVersion)
272 outf.write(' dwDsDsaVersion : %d\n' % serverinfo.dwDsDsaVersion)
274 if typeid == dnsserver.DNSSRV_TYPEID_SERVER_INFO:
275 outf.write(' fReadOnlyDC : %s\n' % bool_string(serverinfo.fReadOnlyDC))
278 def print_zoneinfo(outf, typeid, zoneinfo):
279 outf.write(' pszZoneName : %s\n' % zoneinfo.pszZoneName)
280 outf.write(' dwZoneType : %s\n' % zone_type_string(zoneinfo.dwZoneType))
281 outf.write(' fReverse : %s\n' % bool_string(zoneinfo.fReverse))
282 outf.write(' fAllowUpdate : %s\n' % zone_update_string(zoneinfo.fAllowUpdate))
283 outf.write(' fPaused : %s\n' % bool_string(zoneinfo.fPaused))
284 outf.write(' fShutdown : %s\n' % bool_string(zoneinfo.fShutdown))
285 outf.write(' fAutoCreated : %s\n' % bool_string(zoneinfo.fAutoCreated))
286 outf.write(' fUseDatabase : %s\n' % bool_string(zoneinfo.fUseDatabase))
287 outf.write(' pszDataFile : %s\n' % zoneinfo.pszDataFile)
288 if typeid != dnsserver.DNSSRV_TYPEID_ZONE_INFO:
289 outf.write(' aipMasters : %s\n' %
290 ip4_array_string(zoneinfo.aipMasters))
292 outf.write(' aipMasters : %s\n' %
293 dns_addr_array_string(zoneinfo.aipMasters))
294 outf.write(' fSecureSecondaries : %s\n' % zone_secondary_security_string(zoneinfo.fSecureSecondaries))
295 outf.write(' fNotifyLevel : %s\n' % zone_notify_level_string(zoneinfo.fNotifyLevel))
296 if typeid != dnsserver.DNSSRV_TYPEID_ZONE_INFO:
297 outf.write(' aipSecondaries : %s\n' %
298 ip4_array_string(zoneinfo.aipSecondaries))
299 outf.write(' aipNotify : %s\n' %
300 ip4_array_string(zoneinfo.aipNotify))
302 outf.write(' aipSecondaries : %s\n' %
303 dns_addr_array_string(zoneinfo.aipSecondaries))
304 outf.write(' aipNotify : %s\n' %
305 dns_addr_array_string(zoneinfo.aipNotify))
306 outf.write(' fUseWins : %s\n' % bool_string(zoneinfo.fUseWins))
307 outf.write(' fUseNbstat : %s\n' % bool_string(zoneinfo.fUseNbstat))
308 outf.write(' fAging : %s\n' % bool_string(zoneinfo.fAging))
309 outf.write(' dwNoRefreshInterval : %d\n' % zoneinfo.dwNoRefreshInterval)
310 outf.write(' dwRefreshInterval : %d\n' % zoneinfo.dwRefreshInterval)
311 outf.write(' dwAvailForScavengeTime : %d\n' % zoneinfo.dwAvailForScavengeTime)
312 if typeid != dnsserver.DNSSRV_TYPEID_ZONE_INFO:
313 outf.write(' aipScavengeServers : %s\n' %
314 ip4_array_string(zoneinfo.aipScavengeServers))
316 outf.write(' aipScavengeServers : %s\n' %
317 dns_addr_array_string(zoneinfo.aipScavengeServers))
319 if typeid != dnsserver.DNSSRV_TYPEID_ZONE_INFO_W2K:
320 outf.write(' dwRpcStructureVersion : 0x%x\n' % zoneinfo.dwRpcStructureVersion)
321 outf.write(' dwForwarderTimeout : %d\n' % zoneinfo.dwForwarderTimeout)
322 outf.write(' fForwarderSlave : %d\n' % zoneinfo.fForwarderSlave)
323 if typeid != dnsserver.DNSSRV_TYPEID_ZONE_INFO:
324 outf.write(' aipLocalMasters : %s\n' %
325 ip4_array_string(zoneinfo.aipLocalMasters))
327 outf.write(' aipLocalMasters : %s\n' %
328 dns_addr_array_string(zoneinfo.aipLocalMasters))
329 outf.write(' dwDpFlags : %s\n' % dp_flags_string(zoneinfo.dwDpFlags))
330 outf.write(' pszDpFqdn : %s\n' % zoneinfo.pszDpFqdn)
331 outf.write(' pwszZoneDn : %s\n' % zoneinfo.pwszZoneDn)
332 outf.write(' dwLastSuccessfulSoaCheck : %d\n' % zoneinfo.dwLastSuccessfulSoaCheck)
333 outf.write(' dwLastSuccessfulXfr : %d\n' % zoneinfo.dwLastSuccessfulXfr)
335 if typeid == dnsserver.DNSSRV_TYPEID_ZONE_INFO:
336 outf.write(' fQueuedForBackgroundLoad : %s\n' % bool_string(zoneinfo.fQueuedForBackgroundLoad))
337 outf.write(' fBackgroundLoadInProgress : %s\n' % bool_string(zoneinfo.fBackgroundLoadInProgress))
338 outf.write(' fReadOnlyZone : %s\n' % bool_string(zoneinfo.fReadOnlyZone))
339 outf.write(' dwLastXfrAttempt : %d\n' % zoneinfo.dwLastXfrAttempt)
340 outf.write(' dwLastXfrResult : %d\n' % zoneinfo.dwLastXfrResult)
343 def print_zone(outf, typeid, zone):
344 outf.write(' pszZoneName : %s\n' % zone.pszZoneName)
345 outf.write(' Flags : %s\n' % zone_flags_string(zone.Flags))
346 outf.write(' ZoneType : %s\n' % zone_type_string(zone.ZoneType))
347 outf.write(' Version : %s\n' % zone.Version)
349 if typeid != dnsserver.DNSSRV_TYPEID_ZONE_W2K:
350 outf.write(' dwDpFlags : %s\n' % dp_flags_string(zone.dwDpFlags))
351 outf.write(' pszDpFqdn : %s\n' % zone.pszDpFqdn)
354 def print_enumzones(outf, typeid, zones):
355 outf.write(' %d zone(s) found\n' % zones.dwZoneCount)
356 for zone in zones.ZoneArray:
358 print_zone(outf, typeid, zone)
361 def print_dns_record(outf, rec):
362 if rec.wType == dnsp.DNS_TYPE_A:
363 mesg = 'A: %s' % (rec.data)
364 elif rec.wType == dnsp.DNS_TYPE_AAAA:
365 mesg = 'AAAA: %s' % (rec.data)
366 elif rec.wType == dnsp.DNS_TYPE_PTR:
367 mesg = 'PTR: %s' % (rec.data.str)
368 elif rec.wType == dnsp.DNS_TYPE_NS:
369 mesg = 'NS: %s' % (rec.data.str)
370 elif rec.wType == dnsp.DNS_TYPE_CNAME:
371 mesg = 'CNAME: %s' % (rec.data.str)
372 elif rec.wType == dnsp.DNS_TYPE_SOA:
373 mesg = 'SOA: serial=%d, refresh=%d, retry=%d, expire=%d, minttl=%d, ns=%s, email=%s' % (
378 rec.data.dwMinimumTtl,
379 rec.data.NamePrimaryServer.str,
380 rec.data.ZoneAdministratorEmail.str)
381 elif rec.wType == dnsp.DNS_TYPE_MX:
382 mesg = 'MX: %s (%d)' % (rec.data.nameExchange.str, rec.data.wPreference)
383 elif rec.wType == dnsp.DNS_TYPE_SRV:
384 mesg = 'SRV: %s (%d, %d, %d)' % (rec.data.nameTarget.str, rec.data.wPort,
385 rec.data.wPriority, rec.data.wWeight)
386 elif rec.wType == dnsp.DNS_TYPE_TXT:
387 slist = ['"%s"' % name.str for name in rec.data.str]
388 mesg = 'TXT: %s' % ','.join(slist)
391 outf.write(' %s (flags=%x, serial=%d, ttl=%d)\n' % (
392 mesg, rec.dwFlags, rec.dwSerial, rec.dwTtlSeconds))
395 def print_dnsrecords(outf, records):
396 for rec in records.rec:
397 outf.write(' Name=%s, Records=%d, Children=%d\n' % (
401 for dns_rec in rec.records:
402 print_dns_record(outf, dns_rec)
407 # Convert data into a dns record
408 def data_to_dns_record(record_type, data):
409 if record_type == dnsp.DNS_TYPE_A:
411 elif record_type == dnsp.DNS_TYPE_AAAA:
412 rec = AAAARecord(data)
413 elif record_type == dnsp.DNS_TYPE_PTR:
414 rec = PTRRecord(data)
415 elif record_type == dnsp.DNS_TYPE_CNAME:
416 rec = CNameRecord(data)
417 elif record_type == dnsp.DNS_TYPE_NS:
419 elif record_type == dnsp.DNS_TYPE_MX:
420 tmp = data.split(' ')
422 raise CommandError('Data requires 2 elements - mail_server, preference')
424 preference = int(tmp[1])
425 rec = MXRecord(mail_server, preference)
426 elif record_type == dnsp.DNS_TYPE_SRV:
427 tmp = data.split(' ')
429 raise CommandError('Data requires 4 elements - server, port, priority, weight')
432 priority = int(tmp[2])
434 rec = SRVRecord(server, port, priority=priority, weight=weight)
435 elif record_type == dnsp.DNS_TYPE_SOA:
436 tmp = data.split(' ')
438 raise CommandError('Data requires 7 elements - nameserver, email, serial, '
439 'refresh, retry, expire, minimumttl')
443 refresh = int(tmp[3])
446 minimum = int(tmp[6])
447 rec = SOARecord(nameserver, email, serial=serial, refresh=refresh,
448 retry=retry, expire=expire, minimum=minimum)
449 elif record_type == dnsp.DNS_TYPE_TXT:
450 slist = shlex.split(data)
451 rec = TXTRecord(slist)
453 raise CommandError('Unsupported record type')
457 # Match dns name (of type DNS_RPC_NAME)
458 def dns_name_equal(n1, n2):
459 return n1.str.rstrip('.').lower() == n2.str.rstrip('.').lower()
462 # Match a dns record with specified data
463 def dns_record_match(dns_conn, server, zone, name, record_type, data):
464 urec = data_to_dns_record(record_type, data)
466 select_flags = dnsserver.DNS_RPC_VIEW_AUTHORITY_DATA
469 buflen, res = dns_conn.DnssrvEnumRecords2(
470 dnsserver.DNS_CLIENT_VERSION_LONGHORN, 0, server, zone, name, None,
471 record_type, select_flags, None, None)
472 except WERRORError as e:
473 if e.args[0] == werror.WERR_DNS_ERROR_NAME_DOES_NOT_EXIST:
474 # Either the zone doesn't exist, or there were no records.
475 # We can't differentiate the two.
479 if not res or res.count == 0:
482 for rec in res.rec[0].records:
483 if rec.wType != record_type:
487 if record_type == dnsp.DNS_TYPE_A:
488 if rec.data == urec.data:
490 elif record_type == dnsp.DNS_TYPE_AAAA:
491 if rec.data == urec.data:
493 elif record_type == dnsp.DNS_TYPE_PTR:
494 if dns_name_equal(rec.data, urec.data):
496 elif record_type == dnsp.DNS_TYPE_CNAME:
497 if dns_name_equal(rec.data, urec.data):
499 elif record_type == dnsp.DNS_TYPE_NS:
500 if dns_name_equal(rec.data, urec.data):
502 elif record_type == dnsp.DNS_TYPE_MX:
503 if dns_name_equal(rec.data.nameExchange, urec.data.nameExchange) and \
504 rec.data.wPreference == urec.data.wPreference:
506 elif record_type == dnsp.DNS_TYPE_SRV:
507 if rec.data.wPriority == urec.data.wPriority and \
508 rec.data.wWeight == urec.data.wWeight and \
509 rec.data.wPort == urec.data.wPort and \
510 dns_name_equal(rec.data.nameTarget, urec.data.nameTarget):
512 elif record_type == dnsp.DNS_TYPE_SOA:
513 if rec.data.dwSerialNo == urec.data.dwSerialNo and \
514 rec.data.dwRefresh == urec.data.dwRefresh and \
515 rec.data.dwRetry == urec.data.dwRetry and \
516 rec.data.dwExpire == urec.data.dwExpire and \
517 rec.data.dwMinimumTtl == urec.data.dwMinimumTtl and \
518 dns_name_equal(rec.data.NamePrimaryServer,
519 urec.data.NamePrimaryServer) and \
520 dns_name_equal(rec.data.ZoneAdministratorEmail,
521 urec.data.ZoneAdministratorEmail):
523 elif record_type == dnsp.DNS_TYPE_TXT:
524 if rec.data.count == urec.data.count:
526 for i in range(rec.data.count):
528 (rec.data.str[i].str == urec.data.str[i].str)
536 class cmd_serverinfo(Command):
537 """Query for Server information."""
539 synopsis = '%prog <server> [options]'
541 takes_args = ['server']
543 takes_optiongroups = {
544 "sambaopts": options.SambaOptions,
545 "versionopts": options.VersionOptions,
546 "credopts": options.CredentialsOptions,
550 Option('--client-version', help='Client Version',
551 default='longhorn', metavar='w2k|dotnet|longhorn',
552 choices=['w2k', 'dotnet', 'longhorn'], dest='cli_ver'),
555 def run(self, server, cli_ver, sambaopts=None, credopts=None,
557 self.lp = sambaopts.get_loadparm()
558 self.creds = credopts.get_credentials(self.lp)
559 dns_conn = dns_connect(server, self.lp, self.creds)
561 client_version = dns_client_version(cli_ver)
563 typeid, res = dns_conn.DnssrvQuery2(client_version, 0, server,
565 print_serverinfo(self.outf, typeid, res)
568 class cmd_zoneinfo(Command):
569 """Query for zone information."""
571 synopsis = '%prog <server> <zone> [options]'
573 takes_args = ['server', 'zone']
575 takes_optiongroups = {
576 "sambaopts": options.SambaOptions,
577 "versionopts": options.VersionOptions,
578 "credopts": options.CredentialsOptions,
582 Option('--client-version', help='Client Version',
583 default='longhorn', metavar='w2k|dotnet|longhorn',
584 choices=['w2k', 'dotnet', 'longhorn'], dest='cli_ver'),
587 def run(self, server, zone, cli_ver, sambaopts=None, credopts=None,
589 self.lp = sambaopts.get_loadparm()
590 self.creds = credopts.get_credentials(self.lp)
591 dns_conn = dns_connect(server, self.lp, self.creds)
593 client_version = dns_client_version(cli_ver)
595 typeid, res = dns_conn.DnssrvQuery2(client_version, 0, server, zone,
597 print_zoneinfo(self.outf, typeid, res)
600 class cmd_zonelist(Command):
601 """Query for zones."""
603 synopsis = '%prog <server> [options]'
605 takes_args = ['server']
607 takes_optiongroups = {
608 "sambaopts": options.SambaOptions,
609 "versionopts": options.VersionOptions,
610 "credopts": options.CredentialsOptions,
614 Option('--client-version', help='Client Version',
615 default='longhorn', metavar='w2k|dotnet|longhorn',
616 choices=['w2k', 'dotnet', 'longhorn'], dest='cli_ver'),
617 Option('--primary', help='List primary zones (default)',
618 action='store_true', dest='primary'),
619 Option('--secondary', help='List secondary zones',
620 action='store_true', dest='secondary'),
621 Option('--cache', help='List cached zones',
622 action='store_true', dest='cache'),
623 Option('--auto', help='List automatically created zones',
624 action='store_true', dest='auto'),
625 Option('--forward', help='List forward zones',
626 action='store_true', dest='forward'),
627 Option('--reverse', help='List reverse zones',
628 action='store_true', dest='reverse'),
629 Option('--ds', help='List directory integrated zones',
630 action='store_true', dest='ds'),
631 Option('--non-ds', help='List non-directory zones',
632 action='store_true', dest='nonds')
635 def run(self, server, cli_ver, primary=False, secondary=False, cache=False,
636 auto=False, forward=False, reverse=False, ds=False, nonds=False,
637 sambaopts=None, credopts=None, versionopts=None):
641 request_filter |= dnsserver.DNS_ZONE_REQUEST_PRIMARY
643 request_filter |= dnsserver.DNS_ZONE_REQUEST_SECONDARY
645 request_filter |= dnsserver.DNS_ZONE_REQUEST_CACHE
647 request_filter |= dnsserver.DNS_ZONE_REQUEST_AUTO
649 request_filter |= dnsserver.DNS_ZONE_REQUEST_FORWARD
651 request_filter |= dnsserver.DNS_ZONE_REQUEST_REVERSE
653 request_filter |= dnsserver.DNS_ZONE_REQUEST_DS
655 request_filter |= dnsserver.DNS_ZONE_REQUEST_NON_DS
657 if request_filter == 0:
658 request_filter = dnsserver.DNS_ZONE_REQUEST_PRIMARY
660 self.lp = sambaopts.get_loadparm()
661 self.creds = credopts.get_credentials(self.lp)
662 dns_conn = dns_connect(server, self.lp, self.creds)
664 client_version = dns_client_version(cli_ver)
666 typeid, res = dns_conn.DnssrvComplexOperation2(client_version,
669 dnsserver.DNSSRV_TYPEID_DWORD,
672 if client_version == dnsserver.DNS_CLIENT_VERSION_W2K:
673 typeid = dnsserver.DNSSRV_TYPEID_ZONE_W2K
675 typeid = dnsserver.DNSSRV_TYPEID_ZONE
676 print_enumzones(self.outf, typeid, res)
679 class cmd_zonecreate(Command):
682 synopsis = '%prog <server> <zone> [options]'
684 takes_args = ['server', 'zone']
686 takes_optiongroups = {
687 "sambaopts": options.SambaOptions,
688 "versionopts": options.VersionOptions,
689 "credopts": options.CredentialsOptions,
693 Option('--client-version', help='Client Version',
694 default='longhorn', metavar='w2k|dotnet|longhorn',
695 choices=['w2k', 'dotnet', 'longhorn'], dest='cli_ver')
698 def run(self, server, zone, cli_ver, sambaopts=None, credopts=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)
707 client_version = dns_client_version(cli_ver)
708 if client_version == dnsserver.DNS_CLIENT_VERSION_W2K:
709 typeid = dnsserver.DNSSRV_TYPEID_ZONE_CREATE_W2K
710 zone_create_info = dnsserver.DNS_RPC_ZONE_CREATE_INFO_W2K()
711 zone_create_info.pszZoneName = zone
712 zone_create_info.dwZoneType = dnsp.DNS_ZONE_TYPE_PRIMARY
713 zone_create_info.fAging = 0
714 zone_create_info.fDsIntegrated = 1
715 zone_create_info.fLoadExisting = 1
716 elif client_version == dnsserver.DNS_CLIENT_VERSION_DOTNET:
717 typeid = dnsserver.DNSSRV_TYPEID_ZONE_CREATE_DOTNET
718 zone_create_info = dnsserver.DNS_RPC_ZONE_CREATE_INFO_DOTNET()
719 zone_create_info.pszZoneName = zone
720 zone_create_info.dwZoneType = dnsp.DNS_ZONE_TYPE_PRIMARY
721 zone_create_info.fAging = 0
722 zone_create_info.fDsIntegrated = 1
723 zone_create_info.fLoadExisting = 1
724 zone_create_info.dwDpFlags = dnsserver.DNS_DP_DOMAIN_DEFAULT
726 typeid = dnsserver.DNSSRV_TYPEID_ZONE_CREATE
727 zone_create_info = dnsserver.DNS_RPC_ZONE_CREATE_INFO_LONGHORN()
728 zone_create_info.pszZoneName = zone
729 zone_create_info.dwZoneType = dnsp.DNS_ZONE_TYPE_PRIMARY
730 zone_create_info.fAging = 0
731 zone_create_info.fDsIntegrated = 1
732 zone_create_info.fLoadExisting = 1
733 zone_create_info.dwDpFlags = dnsserver.DNS_DP_DOMAIN_DEFAULT
735 res = dns_conn.DnssrvOperation2(client_version, 0, server, None,
736 0, 'ZoneCreate', typeid,
739 typeid = dnsserver.DNSSRV_TYPEID_NAME_AND_PARAM
740 name_and_param = dnsserver.DNS_RPC_NAME_AND_PARAM()
741 name_and_param.pszNodeName = 'AllowUpdate'
742 name_and_param.dwParam = dnsp.DNS_ZONE_UPDATE_SECURE
745 res = dns_conn.DnssrvOperation2(client_version, 0, server, zone,
746 0, 'ResetDwordProperty', typeid,
748 except WERRORError as e:
749 if e.args[0] == werror.WERR_DNS_ERROR_ZONE_ALREADY_EXISTS:
750 self.outf.write('Zone already exists.')
753 self.outf.write('Zone %s created successfully\n' % zone)
756 class cmd_zonedelete(Command):
759 synopsis = '%prog <server> <zone> [options]'
761 takes_args = ['server', 'zone']
763 takes_optiongroups = {
764 "sambaopts": options.SambaOptions,
765 "versionopts": options.VersionOptions,
766 "credopts": options.CredentialsOptions,
769 def run(self, server, zone, sambaopts=None, credopts=None,
772 self.lp = sambaopts.get_loadparm()
773 self.creds = credopts.get_credentials(self.lp)
774 dns_conn = dns_connect(server, self.lp, self.creds)
778 res = dns_conn.DnssrvOperation2(dnsserver.DNS_CLIENT_VERSION_LONGHORN,
779 0, server, zone, 0, 'DeleteZoneFromDs',
780 dnsserver.DNSSRV_TYPEID_NULL,
782 except WERRORError as e:
783 if e.args[0] == werror.WERR_DNS_ERROR_ZONE_DOES_NOT_EXIST:
784 self.outf.write('Zone does not exist and so could not be deleted.')
787 self.outf.write('Zone %s deleted successfully\n' % zone)
790 class cmd_query(Command):
793 synopsis = '%prog <server> <zone> <name> <A|AAAA|CNAME|MX|NS|SOA|SRV|TXT|ALL> [options]'
795 takes_args = ['server', 'zone', 'name', 'rtype']
797 takes_optiongroups = {
798 "sambaopts": options.SambaOptions,
799 "versionopts": options.VersionOptions,
800 "credopts": options.CredentialsOptions,
804 Option('--authority', help='Search authoritative records (default)',
805 action='store_true', dest='authority'),
806 Option('--cache', help='Search cached records',
807 action='store_true', dest='cache'),
808 Option('--glue', help='Search glue records',
809 action='store_true', dest='glue'),
810 Option('--root', help='Search root hints',
811 action='store_true', dest='root'),
812 Option('--additional', help='List additional records',
813 action='store_true', dest='additional'),
814 Option('--no-children', help='Do not list children',
815 action='store_true', dest='no_children'),
816 Option('--only-children', help='List only children',
817 action='store_true', dest='only_children')
820 def run(self, server, zone, name, rtype, authority=False, cache=False,
821 glue=False, root=False, additional=False, no_children=False,
822 only_children=False, sambaopts=None, credopts=None,
824 record_type = dns_type_flag(rtype)
826 if name.find('*') != -1:
827 self.outf.write('use "@" to dump entire domain, looking up %s\n' %
832 select_flags |= dnsserver.DNS_RPC_VIEW_AUTHORITY_DATA
834 select_flags |= dnsserver.DNS_RPC_VIEW_CACHE_DATA
836 select_flags |= dnsserver.DNS_RPC_VIEW_GLUE_DATA
838 select_flags |= dnsserver.DNS_RPC_VIEW_ROOT_HINT_DATA
840 select_flags |= dnsserver.DNS_RPC_VIEW_ADDITIONAL_DATA
842 select_flags |= dnsserver.DNS_RPC_VIEW_NO_CHILDREN
844 select_flags |= dnsserver.DNS_RPC_VIEW_ONLY_CHILDREN
846 if select_flags == 0:
847 select_flags = dnsserver.DNS_RPC_VIEW_AUTHORITY_DATA
849 if select_flags == dnsserver.DNS_RPC_VIEW_ADDITIONAL_DATA:
850 self.outf.write('Specify either --authority or --root along with --additional.\n')
851 self.outf.write('Assuming --authority.\n')
852 select_flags |= dnsserver.DNS_RPC_VIEW_AUTHORITY_DATA
854 self.lp = sambaopts.get_loadparm()
855 self.creds = credopts.get_credentials(self.lp)
856 dns_conn = dns_connect(server, self.lp, self.creds)
859 buflen, res = dns_conn.DnssrvEnumRecords2(
860 dnsserver.DNS_CLIENT_VERSION_LONGHORN, 0, server, zone, name,
861 None, record_type, select_flags, None, None)
862 except WERRORError as e:
863 if e.args[0] == werror.WERR_DNS_ERROR_NAME_DOES_NOT_EXIST:
864 self.outf.write('Record or zone does not exist.')
867 print_dnsrecords(self.outf, res)
870 class cmd_roothints(Command):
871 """Query root hints."""
873 synopsis = '%prog <server> [<name>] [options]'
875 takes_args = ['server', 'name?']
877 takes_optiongroups = {
878 "sambaopts": options.SambaOptions,
879 "versionopts": options.VersionOptions,
880 "credopts": options.CredentialsOptions,
883 def run(self, server, name='.', sambaopts=None, credopts=None,
885 record_type = dnsp.DNS_TYPE_NS
886 select_flags = (dnsserver.DNS_RPC_VIEW_ROOT_HINT_DATA |
887 dnsserver.DNS_RPC_VIEW_ADDITIONAL_DATA)
889 self.lp = sambaopts.get_loadparm()
890 self.creds = credopts.get_credentials(self.lp)
891 dns_conn = dns_connect(server, self.lp, self.creds)
893 buflen, res = dns_conn.DnssrvEnumRecords2(
894 dnsserver.DNS_CLIENT_VERSION_LONGHORN, 0, server, '..RootHints',
895 name, None, record_type, select_flags, None, None)
896 print_dnsrecords(self.outf, res)
899 class cmd_add_record(Command):
902 For each type data contents are as follows:
903 A ipv4_address_string
904 AAAA ipv6_address_string
908 MX "fqdn_string preference"
909 SRV "fqdn_string port priority weight"
910 TXT "'string1' 'string2' ..."
913 synopsis = '%prog <server> <zone> <name> <A|AAAA|PTR|CNAME|NS|MX|SRV|TXT> <data>'
915 takes_args = ['server', 'zone', 'name', 'rtype', 'data']
917 takes_optiongroups = {
918 "sambaopts": options.SambaOptions,
919 "versionopts": options.VersionOptions,
920 "credopts": options.CredentialsOptions,
923 def run(self, server, zone, name, rtype, data, sambaopts=None,
924 credopts=None, versionopts=None):
926 if rtype.upper() not in ('A', 'AAAA', 'PTR', 'CNAME', 'NS', 'MX', 'SRV', 'TXT'):
927 raise CommandError('Adding record of type %s is not supported' % rtype)
929 record_type = dns_type_flag(rtype)
930 rec = data_to_dns_record(record_type, data)
932 self.lp = sambaopts.get_loadparm()
933 self.creds = credopts.get_credentials(self.lp)
934 dns_conn = dns_connect(server, self.lp, self.creds)
936 add_rec_buf = dnsserver.DNS_RPC_RECORD_BUF()
937 add_rec_buf.rec = rec
940 dns_conn.DnssrvUpdateRecord2(dnsserver.DNS_CLIENT_VERSION_LONGHORN,
941 0, server, zone, name, add_rec_buf, None)
942 except WERRORError as e:
943 if e.args[0] == werror.WERR_DNS_ERROR_NAME_DOES_NOT_EXIST:
944 self.outf.write('Zone does not exist; record could not be added.\n')
947 self.outf.write('Record added successfully\n')
950 class cmd_update_record(Command):
951 """Update a DNS record
953 For each type data contents are as follows:
954 A ipv4_address_string
955 AAAA ipv6_address_string
959 MX "fqdn_string preference"
960 SOA "fqdn_dns fqdn_email serial refresh retry expire minimumttl"
961 SRV "fqdn_string port priority weight"
962 TXT "'string1' 'string2' ..."
965 synopsis = '%prog <server> <zone> <name> <A|AAAA|PTR|CNAME|NS|MX|SOA|SRV|TXT> <olddata> <newdata>'
967 takes_args = ['server', 'zone', 'name', 'rtype', 'olddata', 'newdata']
969 takes_optiongroups = {
970 "sambaopts": options.SambaOptions,
971 "versionopts": options.VersionOptions,
972 "credopts": options.CredentialsOptions,
975 def run(self, server, zone, name, rtype, olddata, newdata,
976 sambaopts=None, credopts=None, versionopts=None):
978 if rtype.upper() not in ('A', 'AAAA', 'PTR', 'CNAME', 'NS', 'MX', 'SOA', 'SRV', 'TXT'):
979 raise CommandError('Updating record of type %s is not supported' % rtype)
981 record_type = dns_type_flag(rtype)
982 rec = data_to_dns_record(record_type, newdata)
984 self.lp = sambaopts.get_loadparm()
985 self.creds = credopts.get_credentials(self.lp)
986 dns_conn = dns_connect(server, self.lp, self.creds)
988 rec_match = dns_record_match(dns_conn, server, zone, name, record_type,
991 raise CommandError('Record or zone does not exist.')
993 # Copy properties from existing record to new record
994 rec.dwFlags = rec_match.dwFlags
995 rec.dwSerial = rec_match.dwSerial
996 rec.dwTtlSeconds = rec_match.dwTtlSeconds
997 rec.dwTimeStamp = rec_match.dwTimeStamp
999 add_rec_buf = dnsserver.DNS_RPC_RECORD_BUF()
1000 add_rec_buf.rec = rec
1002 del_rec_buf = dnsserver.DNS_RPC_RECORD_BUF()
1003 del_rec_buf.rec = rec_match
1006 dns_conn.DnssrvUpdateRecord2(dnsserver.DNS_CLIENT_VERSION_LONGHORN,
1013 except WERRORError as e:
1014 if e.args[0] == werror.WERR_DNS_ERROR_NAME_DOES_NOT_EXIST:
1015 self.outf.write('Zone does not exist; record could not be updated.\n')
1018 self.outf.write('Record updated successfully\n')
1021 class cmd_delete_record(Command):
1022 """Delete a DNS record
1024 For each type data contents are as follows:
1025 A ipv4_address_string
1026 AAAA ipv6_address_string
1030 MX "fqdn_string preference"
1031 SRV "fqdn_string port priority weight"
1032 TXT "'string1' 'string2' ..."
1035 synopsis = '%prog <server> <zone> <name> <A|AAAA|PTR|CNAME|NS|MX|SRV|TXT> <data>'
1037 takes_args = ['server', 'zone', 'name', 'rtype', 'data']
1039 takes_optiongroups = {
1040 "sambaopts": options.SambaOptions,
1041 "versionopts": options.VersionOptions,
1042 "credopts": options.CredentialsOptions,
1045 def run(self, server, zone, name, rtype, data, sambaopts=None, credopts=None, versionopts=None):
1047 if rtype.upper() not in ('A', 'AAAA', 'PTR', 'CNAME', 'NS', 'MX', 'SRV', 'TXT'):
1048 raise CommandError('Deleting record of type %s is not supported' % rtype)
1050 record_type = dns_type_flag(rtype)
1051 rec = data_to_dns_record(record_type, data)
1053 self.lp = sambaopts.get_loadparm()
1054 self.creds = credopts.get_credentials(self.lp)
1055 dns_conn = dns_connect(server, self.lp, self.creds)
1057 del_rec_buf = dnsserver.DNS_RPC_RECORD_BUF()
1058 del_rec_buf.rec = rec
1061 dns_conn.DnssrvUpdateRecord2(dnsserver.DNS_CLIENT_VERSION_LONGHORN,
1068 except WERRORError as e:
1069 if e.args[0] == werror.WERR_DNS_ERROR_NAME_DOES_NOT_EXIST:
1070 self.outf.write('Zone does not exist; record could not be deleted.\n')
1073 self.outf.write('Record deleted successfully\n')
1076 class cmd_cleanup_record(Command):
1077 """Cleanup DNS records for a DNS host.
1081 samba-tool dns cleanup dc1 dc1.samdom.test.site -U USER%PASSWORD
1083 NOTE: This command in many cases will only mark the `dNSTombstoned` attr
1084 as `TRUE` on the DNS records. Querying will no longer return results but
1085 there may still be some placeholder entries in the database.
1088 synopsis = '%prog <server> <dnshostname>'
1090 takes_args = ['server', 'dnshostname']
1092 takes_optiongroups = {
1093 "sambaopts": options.SambaOptions,
1094 "versionopts": options.VersionOptions,
1095 "credopts": options.CredentialsOptions,
1099 Option("-v", "--verbose", help="Be verbose", action="store_true"),
1100 Option("-q", "--quiet", help="Be quiet", action="store_true"),
1103 def run(self, server, dnshostname, sambaopts=None, credopts=None,
1104 versionopts=None, verbose=False, quiet=False):
1105 lp = sambaopts.get_loadparm()
1106 creds = credopts.get_credentials(lp)
1108 logger = self.get_logger()
1110 logger.setLevel(logging.DEBUG)
1112 logger.setLevel(logging.WARNING)
1114 logger.setLevel(logging.INFO)
1116 samdb = SamDB(url="ldap://%s" % server,
1117 session_info=system_session(),
1118 credentials=creds, lp=lp)
1120 remove_dc.remove_dns_references(samdb, logger, dnshostname,
1121 ignore_no_name=True)
1124 class cmd_dns(SuperCommand):
1125 """Domain Name Service (DNS) management."""
1128 subcommands['serverinfo'] = cmd_serverinfo()
1129 subcommands['zoneinfo'] = cmd_zoneinfo()
1130 subcommands['zonelist'] = cmd_zonelist()
1131 subcommands['zonecreate'] = cmd_zonecreate()
1132 subcommands['zonedelete'] = cmd_zonedelete()
1133 subcommands['query'] = cmd_query()
1134 subcommands['roothints'] = cmd_roothints()
1135 subcommands['add'] = cmd_add_record()
1136 subcommands['update'] = cmd_update_record()
1137 subcommands['delete'] = cmd_delete_record()
1138 subcommands['cleanup'] = cmd_cleanup_record()