2011-10-17 06:15:40 +04:00
# DNS management tool
#
2012-02-16 03:17:25 +04:00
# Copyright (C) Amitay Isaacs 2011-2012
2011-10-17 06:15:40 +04:00
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
import samba . getopt as options
2016-11-25 06:29:31 +03:00
from samba import WERRORError
2017-02-13 01:12:54 +03:00
from samba import werror
2011-10-17 06:15:40 +04:00
from struct import pack
2021-06-20 05:52:48 +03:00
from socket import inet_ntop , inet_pton
2014-11-21 05:55:25 +03:00
from socket import AF_INET
from socket import AF_INET6
2019-05-24 00:58:12 +03:00
import struct
samba-tool dns zoneoptions: timestamp manipulation options
There was a bug in Samba before 4.9 that marked all records intended
to be static with a current timestamp, and all records intended to be
dynamic with a zero timestamp. This was exactly the opposite of
correct behaviour.
It follows that a domain which has been upgraded past 4.9, but on
which aging is not enabled, records intended to be static will have a
timestamp from before the upgrade date (unless their nodes have
suffered a DNS update, which due to another bug, will change the
timestmap). The following command will make these truly static:
$ samba-tool dns zoneoptions --mark-old-records-static=2018-07-23 -U...
where '2018-07-23' should be replaced by the approximate date of the
upgrade beyond 4.9.
It seems riskier making blanket conversions of static records into
dynamic records, but there are sometimes useful patterns in the names
given to machines that we can exploit. For example, if there is a
group of machines with names like 'desktop-123' that are all supposed
to using dynamic DNS, the adminstrator can go
$ samba-tool dns zoneoptions --mark-records-dynamic-regex='desktop-\d+'
and there's a --mark-records-static-regex for symmetry.
These options are deliberately long and cumbersome to type, so people
have a chance to think before they get to the end. We also introduce a
'--dry-run' (or '-n') option so they can inspect the likely results
before going ahead.
*NOTE* ageing will still not work properly after this commit, due to
other bugs that will be fixed in other commits.
Signed-off-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz>
Reviewed-by: Andrew Bartlett <abartlet@samba.org>
2021-05-27 00:46:02 +03:00
import time
import ldb
from samba . ndr import ndr_unpack , ndr_pack
import re
2011-10-17 06:15:40 +04:00
samba-tool dns zoneoptions: timestamp manipulation options
There was a bug in Samba before 4.9 that marked all records intended
to be static with a current timestamp, and all records intended to be
dynamic with a zero timestamp. This was exactly the opposite of
correct behaviour.
It follows that a domain which has been upgraded past 4.9, but on
which aging is not enabled, records intended to be static will have a
timestamp from before the upgrade date (unless their nodes have
suffered a DNS update, which due to another bug, will change the
timestmap). The following command will make these truly static:
$ samba-tool dns zoneoptions --mark-old-records-static=2018-07-23 -U...
where '2018-07-23' should be replaced by the approximate date of the
upgrade beyond 4.9.
It seems riskier making blanket conversions of static records into
dynamic records, but there are sometimes useful patterns in the names
given to machines that we can exploit. For example, if there is a
group of machines with names like 'desktop-123' that are all supposed
to using dynamic DNS, the adminstrator can go
$ samba-tool dns zoneoptions --mark-records-dynamic-regex='desktop-\d+'
and there's a --mark-records-static-regex for symmetry.
These options are deliberately long and cumbersome to type, so people
have a chance to think before they get to the end. We also introduce a
'--dry-run' (or '-n') option so they can inspect the likely results
before going ahead.
*NOTE* ageing will still not work properly after this commit, due to
other bugs that will be fixed in other commits.
Signed-off-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz>
Reviewed-by: Andrew Bartlett <abartlet@samba.org>
2021-05-27 00:46:02 +03:00
from samba import remove_dc , dsdb_dns
2018-01-12 04:14:00 +03:00
from samba . samdb import SamDB
from samba . auth import system_session
2011-10-17 06:15:40 +04:00
from samba . netcmd import (
Command ,
CommandError ,
Option ,
SuperCommand ,
2018-07-30 09:14:37 +03:00
)
2011-10-17 06:15:40 +04:00
from samba . dcerpc import dnsp , dnsserver
2021-03-26 10:52:20 +03:00
from samba . dnsserver import record_from_string , DNSParseError , flag_from_string
2021-05-27 00:45:18 +03:00
from samba . dnsserver import dns_record_match
2011-10-17 06:15:40 +04:00
2018-07-30 09:20:39 +03:00
2011-10-17 06:15:40 +04:00
def dns_connect ( server , lp , creds ) :
2012-11-14 14:32:06 +04:00
if server . lower ( ) == ' localhost ' :
server = ' 127.0.0.1 '
2011-10-17 06:15:40 +04:00
binding_str = " ncacn_ip_tcp: %s [sign] " % server
2015-08-07 07:27:23 +03:00
try :
dns_conn = dnsserver . dnsserver ( binding_str , lp , creds )
2018-02-14 00:07:23 +03:00
except RuntimeError as e :
2015-08-07 07:27:23 +03:00
raise CommandError ( ' Connecting to DNS RPC server %s failed with %s ' % ( server , e ) )
2011-10-17 06:15:40 +04:00
return dns_conn
2012-09-16 16:18:39 +04:00
2022-08-12 07:38:59 +03:00
class DnsConnWrapper :
""" A wrapper around a dnsserver.dnsserver connection that makes it
harder not to report friendly messages .
2022-08-17 08:59:50 +03:00
If , rather than
dns_conn = dns_connect ( server , lp , creds )
you use
dns_conn = DnsConnWrapper ( server , lp , creds )
2023-06-06 14:17:58 +03:00
then various common errors ( for example , misspelled zones ) on
2022-08-17 08:59:50 +03:00
common operations will raise CommandErrors that turn into
relatively nice messages ( when compared to tracebacks ) .
In addition , if you provide a messages keyword argument , it will
override the defaults . Note that providing None will turn off the
default , letting the original exception shine through .
messages = {
werror . WERR_DNS_ERROR_ZONE_DOES_NOT_EXIST : (
f ' Zone { zone } does not exist and so could not be deleted. ' ) ,
werror . WERR_DNS_ERROR_NAME_DOES_NOT_EXIST : None
}
res = dns_conn . DnssrvOperation2 ( # ...
messages = messages )
This example changes the message for ZONE_DOES_NOT_EXIST and
avoids catching NAME_DOES_NOT_EXIST .
Only WERRORErrors are intercepted .
2022-08-12 07:38:59 +03:00
"""
default_messages = {
2022-08-17 08:57:16 +03:00
werror . WERR_DNS_ERROR_DS_UNAVAILABLE : " Could not contact RPC server " ,
2022-08-17 08:58:03 +03:00
werror . WERR_DNS_ERROR_ZONE_ALREADY_EXISTS : ' Zone already exists ' ,
2022-08-17 08:58:50 +03:00
werror . WERR_DNS_ERROR_RECORD_DOES_NOT_EXIST : ' The record does not exist ' ,
2022-08-17 08:59:50 +03:00
werror . WERR_DNS_ERROR_NAME_DOES_NOT_EXIST : ' The zone does not exist ' ,
2022-08-18 01:58:54 +03:00
werror . WERR_ACCESS_DENIED : ' Insufficient permissions ' ,
2022-08-12 07:38:59 +03:00
}
def __init__ ( self , server , lp , creds ) :
self . dns_conn = dns_connect ( server , lp , creds )
def __getattr__ ( self , name ) :
attr = getattr ( self . dns_conn , name )
if name not in {
" DnssrvComplexOperation2 " ,
" DnssrvEnumRecords2 " ,
" DnssrvOperation2 " ,
" DnssrvQuery2 " ,
" DnssrvUpdateRecord2 " } :
return attr
2023-02-23 05:54:37 +03:00
def f ( * args , messages = None ) :
if messages is None :
messages = { }
2022-08-12 07:38:59 +03:00
try :
return attr ( * args )
except WERRORError as e :
werr , errstr = e . args
if werr in messages :
if werr is None :
# None overrides a default message, leaving the bare exception
raise
raise CommandError ( f " { messages [ werr ] } [ { errstr } ] " , e )
if werr in self . default_messages :
raise CommandError ( f " { self . default_messages [ werr ] } [ { errstr } ] " , e )
raise
return f
2011-10-17 06:15:40 +04:00
def bool_string ( flag ) :
if flag == 0 :
ret = ' FALSE '
elif flag == 1 :
ret = ' TRUE '
else :
ret = ' UNKNOWN (0x %x ) ' % flag
return ret
2012-09-16 16:18:39 +04:00
2011-10-17 06:15:40 +04:00
def enum_string ( module , enum_defs , value ) :
ret = None
for e in enum_defs :
if value == getattr ( module , e ) :
ret = e
break
if not ret :
ret = ' UNKNOWN (0x %x ) ' % value
return ret
2012-09-16 16:18:39 +04:00
2011-10-17 06:15:40 +04:00
def bitmap_string ( module , bitmap_defs , value ) :
ret = ' '
for b in bitmap_defs :
if value & getattr ( module , b ) :
ret + = ' %s ' % b
if not ret :
ret = ' NONE '
return ret
2012-09-16 16:18:39 +04:00
2011-10-17 06:15:40 +04:00
def boot_method_string ( boot_method ) :
2018-07-30 09:16:43 +03:00
enum_defs = [ ' DNS_BOOT_METHOD_UNINITIALIZED ' , ' DNS_BOOT_METHOD_FILE ' ,
2018-09-03 16:05:48 +03:00
' DNS_BOOT_METHOD_REGISTRY ' , ' DNS_BOOT_METHOD_DIRECTORY ' ]
2011-10-17 06:15:40 +04:00
return enum_string ( dnsserver , enum_defs , boot_method )
2012-09-16 16:18:39 +04:00
2011-10-17 06:15:40 +04:00
def name_check_flag_string ( check_flag ) :
2018-07-30 09:16:43 +03:00
enum_defs = [ ' DNS_ALLOW_RFC_NAMES_ONLY ' , ' DNS_ALLOW_NONRFC_NAMES ' ,
2018-09-03 16:05:48 +03:00
' DNS_ALLOW_MULTIBYTE_NAMES ' , ' DNS_ALLOW_ALL_NAMES ' ]
2011-10-17 06:15:40 +04:00
return enum_string ( dnsserver , enum_defs , check_flag )
2012-09-16 16:18:39 +04:00
2011-10-17 06:15:40 +04:00
def zone_type_string ( zone_type ) :
2018-07-30 09:16:43 +03:00
enum_defs = [ ' DNS_ZONE_TYPE_CACHE ' , ' DNS_ZONE_TYPE_PRIMARY ' ,
2018-09-03 16:05:48 +03:00
' DNS_ZONE_TYPE_SECONDARY ' , ' DNS_ZONE_TYPE_STUB ' ,
' DNS_ZONE_TYPE_FORWARDER ' , ' DNS_ZONE_TYPE_SECONDARY_CACHE ' ]
2011-10-17 06:15:40 +04:00
return enum_string ( dnsp , enum_defs , zone_type )
2012-09-16 16:18:39 +04:00
2011-10-17 06:15:40 +04:00
def zone_update_string ( zone_update ) :
2018-07-30 09:16:43 +03:00
enum_defs = [ ' DNS_ZONE_UPDATE_OFF ' , ' DNS_ZONE_UPDATE_UNSECURE ' ,
2018-09-03 16:05:48 +03:00
' DNS_ZONE_UPDATE_SECURE ' ]
2011-10-17 06:15:40 +04:00
return enum_string ( dnsp , enum_defs , zone_update )
2012-09-16 16:18:39 +04:00
2011-10-17 06:15:40 +04:00
def zone_secondary_security_string ( security ) :
2018-07-30 09:16:43 +03:00
enum_defs = [ ' DNS_ZONE_SECSECURE_NO_SECURITY ' , ' DNS_ZONE_SECSECURE_NS_ONLY ' ,
2018-09-03 16:05:48 +03:00
' DNS_ZONE_SECSECURE_LIST_ONLY ' , ' DNS_ZONE_SECSECURE_NO_XFER ' ]
2011-10-17 06:15:40 +04:00
return enum_string ( dnsserver , enum_defs , security )
2012-09-16 16:18:39 +04:00
2011-10-17 06:15:40 +04:00
def zone_notify_level_string ( notify_level ) :
2018-07-30 09:16:43 +03:00
enum_defs = [ ' DNS_ZONE_NOTIFY_OFF ' , ' DNS_ZONE_NOTIFY_ALL_SECONDARIES ' ,
2018-09-03 16:05:48 +03:00
' DNS_ZONE_NOTIFY_LIST_ONLY ' ]
2011-10-17 06:15:40 +04:00
return enum_string ( dnsserver , enum_defs , notify_level )
2012-09-16 16:18:39 +04:00
2011-10-17 06:15:40 +04:00
def dp_flags_string ( dp_flags ) :
2018-07-30 09:16:43 +03:00
bitmap_defs = [ ' DNS_DP_AUTOCREATED ' , ' DNS_DP_LEGACY ' , ' DNS_DP_DOMAIN_DEFAULT ' ,
2018-09-03 16:05:48 +03:00
' DNS_DP_FOREST_DEFAULT ' , ' DNS_DP_ENLISTED ' , ' DNS_DP_DELETED ' ]
2011-10-17 06:15:40 +04:00
return bitmap_string ( dnsserver , bitmap_defs , dp_flags )
2012-09-16 16:18:39 +04:00
2011-10-17 06:15:40 +04:00
def zone_flags_string ( flags ) :
2018-07-30 09:16:43 +03:00
bitmap_defs = [ ' DNS_RPC_ZONE_PAUSED ' , ' DNS_RPC_ZONE_SHUTDOWN ' ,
2018-09-03 16:05:48 +03:00
' DNS_RPC_ZONE_REVERSE ' , ' DNS_RPC_ZONE_AUTOCREATED ' ,
' DNS_RPC_ZONE_DSINTEGRATED ' , ' DNS_RPC_ZONE_AGING ' ,
' DNS_RPC_ZONE_UPDATE_UNSECURE ' , ' DNS_RPC_ZONE_UPDATE_SECURE ' ,
' DNS_RPC_ZONE_READONLY ' ]
2011-10-17 06:15:40 +04:00
return bitmap_string ( dnsserver , bitmap_defs , flags )
2012-09-16 16:18:39 +04:00
2011-10-17 06:15:40 +04:00
def ip4_array_string ( array ) :
ret = [ ]
if not array :
return ret
2018-05-04 14:19:57 +03:00
for i in range ( array . AddrCount ) :
2014-11-21 05:55:25 +03:00
addr = inet_ntop ( AF_INET , pack ( ' I ' , array . AddrArray [ i ] ) )
2011-10-17 06:15:40 +04:00
ret . append ( addr )
return ret
2012-09-16 16:18:39 +04:00
2011-10-17 06:15:40 +04:00
def dns_addr_array_string ( array ) :
ret = [ ]
if not array :
return ret
2018-05-04 14:19:57 +03:00
for i in range ( array . AddrCount ) :
2011-10-17 06:15:40 +04:00
if array . AddrArray [ i ] . MaxSa [ 0 ] == 0x02 :
2019-05-24 00:58:12 +03:00
x = struct . pack ( ' 4B ' , * array . AddrArray [ i ] . MaxSa [ 4 : 8 ] )
2014-11-21 05:55:25 +03:00
addr = inet_ntop ( AF_INET , x )
2011-10-17 06:15:40 +04:00
elif array . AddrArray [ i ] . MaxSa [ 0 ] == 0x17 :
2019-05-24 00:58:12 +03:00
x = struct . pack ( ' 16B ' , * array . AddrArray [ i ] . MaxSa [ 8 : 24 ] )
2014-11-21 05:55:25 +03:00
addr = inet_ntop ( AF_INET6 , x )
2011-10-17 06:15:40 +04:00
else :
addr = ' UNKNOWN '
ret . append ( addr )
return ret
2012-09-16 16:18:39 +04:00
2011-10-17 06:15:40 +04:00
def dns_type_flag ( rec_type ) :
2021-03-26 10:52:20 +03:00
try :
return flag_from_string ( rec_type )
except DNSParseError as e :
raise CommandError ( * e . args )
2011-10-17 06:15:40 +04:00
2012-09-16 16:18:39 +04:00
2011-10-17 06:15:40 +04:00
def dns_client_version ( cli_version ) :
version = cli_version . upper ( )
if version == ' W2K ' :
client_version = dnsserver . DNS_CLIENT_VERSION_W2K
elif version == ' DOTNET ' :
client_version = dnsserver . DNS_CLIENT_VERSION_DOTNET
elif version == ' LONGHORN ' :
client_version = dnsserver . DNS_CLIENT_VERSION_LONGHORN
else :
raise CommandError ( ' Unknown client version %s ' % cli_version )
return client_version
2012-09-16 16:18:39 +04:00
2011-10-17 06:15:40 +04:00
def print_serverinfo ( outf , typeid , serverinfo ) :
outf . write ( ' dwVersion : 0x %x \n ' % serverinfo . dwVersion )
outf . write ( ' fBootMethod : %s \n ' % boot_method_string ( serverinfo . fBootMethod ) )
outf . write ( ' fAdminConfigured : %s \n ' % bool_string ( serverinfo . fAdminConfigured ) )
outf . write ( ' fAllowUpdate : %s \n ' % bool_string ( serverinfo . fAllowUpdate ) )
outf . write ( ' fDsAvailable : %s \n ' % bool_string ( serverinfo . fDsAvailable ) )
outf . write ( ' pszServerName : %s \n ' % serverinfo . pszServerName )
outf . write ( ' pszDsContainer : %s \n ' % serverinfo . pszDsContainer )
if typeid != dnsserver . DNSSRV_TYPEID_SERVER_INFO :
outf . write ( ' aipServerAddrs : %s \n ' %
2018-07-30 09:15:34 +03:00
ip4_array_string ( serverinfo . aipServerAddrs ) )
2011-10-17 06:15:40 +04:00
outf . write ( ' aipListenAddrs : %s \n ' %
2018-07-30 09:15:34 +03:00
ip4_array_string ( serverinfo . aipListenAddrs ) )
2011-10-17 06:15:40 +04:00
outf . write ( ' aipForwarders : %s \n ' %
2018-07-30 09:15:34 +03:00
ip4_array_string ( serverinfo . aipForwarders ) )
2011-10-17 06:15:40 +04:00
else :
outf . write ( ' aipServerAddrs : %s \n ' %
2018-07-30 09:15:34 +03:00
dns_addr_array_string ( serverinfo . aipServerAddrs ) )
2011-10-17 06:15:40 +04:00
outf . write ( ' aipListenAddrs : %s \n ' %
2018-07-30 09:15:34 +03:00
dns_addr_array_string ( serverinfo . aipListenAddrs ) )
2011-10-17 06:15:40 +04:00
outf . write ( ' aipForwarders : %s \n ' %
2018-07-30 09:15:34 +03:00
dns_addr_array_string ( serverinfo . aipForwarders ) )
2011-10-17 06:15:40 +04:00
outf . write ( ' dwLogLevel : %d \n ' % serverinfo . dwLogLevel )
outf . write ( ' dwDebugLevel : %d \n ' % serverinfo . dwDebugLevel )
outf . write ( ' dwForwardTimeout : %d \n ' % serverinfo . dwForwardTimeout )
outf . write ( ' dwRpcPrototol : 0x %x \n ' % serverinfo . dwRpcProtocol )
outf . write ( ' dwNameCheckFlag : %s \n ' % name_check_flag_string ( serverinfo . dwNameCheckFlag ) )
outf . write ( ' cAddressAnswerLimit : %d \n ' % serverinfo . cAddressAnswerLimit )
outf . write ( ' dwRecursionRetry : %d \n ' % serverinfo . dwRecursionRetry )
outf . write ( ' dwRecursionTimeout : %d \n ' % serverinfo . dwRecursionTimeout )
outf . write ( ' dwMaxCacheTtl : %d \n ' % serverinfo . dwMaxCacheTtl )
outf . write ( ' dwDsPollingInterval : %d \n ' % serverinfo . dwDsPollingInterval )
outf . write ( ' dwScavengingInterval : %d \n ' % serverinfo . dwScavengingInterval )
outf . write ( ' dwDefaultRefreshInterval : %d \n ' % serverinfo . dwDefaultRefreshInterval )
outf . write ( ' dwDefaultNoRefreshInterval : %d \n ' % serverinfo . dwDefaultNoRefreshInterval )
outf . write ( ' fAutoReverseZones : %s \n ' % bool_string ( serverinfo . fAutoReverseZones ) )
outf . write ( ' fAutoCacheUpdate : %s \n ' % bool_string ( serverinfo . fAutoCacheUpdate ) )
outf . write ( ' fRecurseAfterForwarding : %s \n ' % bool_string ( serverinfo . fRecurseAfterForwarding ) )
outf . write ( ' fForwardDelegations : %s \n ' % bool_string ( serverinfo . fForwardDelegations ) )
outf . write ( ' fNoRecursion : %s \n ' % bool_string ( serverinfo . fNoRecursion ) )
outf . write ( ' fSecureResponses : %s \n ' % bool_string ( serverinfo . fSecureResponses ) )
outf . write ( ' fRoundRobin : %s \n ' % bool_string ( serverinfo . fRoundRobin ) )
outf . write ( ' fLocalNetPriority : %s \n ' % bool_string ( serverinfo . fLocalNetPriority ) )
outf . write ( ' fBindSecondaries : %s \n ' % bool_string ( serverinfo . fBindSecondaries ) )
outf . write ( ' fWriteAuthorityNs : %s \n ' % bool_string ( serverinfo . fWriteAuthorityNs ) )
outf . write ( ' fStrictFileParsing : %s \n ' % bool_string ( serverinfo . fStrictFileParsing ) )
outf . write ( ' fLooseWildcarding : %s \n ' % bool_string ( serverinfo . fLooseWildcarding ) )
outf . write ( ' fDefaultAgingState : %s \n ' % bool_string ( serverinfo . fDefaultAgingState ) )
if typeid != dnsserver . DNSSRV_TYPEID_SERVER_INFO_W2K :
outf . write ( ' dwRpcStructureVersion : 0x %x \n ' % serverinfo . dwRpcStructureVersion )
outf . write ( ' aipLogFilter : %s \n ' % dns_addr_array_string ( serverinfo . aipLogFilter ) )
outf . write ( ' pwszLogFilePath : %s \n ' % serverinfo . pwszLogFilePath )
outf . write ( ' pszDomainName : %s \n ' % serverinfo . pszDomainName )
outf . write ( ' pszForestName : %s \n ' % serverinfo . pszForestName )
outf . write ( ' pszDomainDirectoryPartition : %s \n ' % serverinfo . pszDomainDirectoryPartition )
outf . write ( ' pszForestDirectoryPartition : %s \n ' % serverinfo . pszForestDirectoryPartition )
outf . write ( ' dwLocalNetPriorityNetMask : 0x %x \n ' % serverinfo . dwLocalNetPriorityNetMask )
outf . write ( ' dwLastScavengeTime : %d \n ' % serverinfo . dwLastScavengeTime )
outf . write ( ' dwEventLogLevel : %d \n ' % serverinfo . dwEventLogLevel )
outf . write ( ' dwLogFileMaxSize : %d \n ' % serverinfo . dwLogFileMaxSize )
outf . write ( ' dwDsForestVersion : %d \n ' % serverinfo . dwDsForestVersion )
outf . write ( ' dwDsDomainVersion : %d \n ' % serverinfo . dwDsDomainVersion )
outf . write ( ' dwDsDsaVersion : %d \n ' % serverinfo . dwDsDsaVersion )
if typeid == dnsserver . DNSSRV_TYPEID_SERVER_INFO :
outf . write ( ' fReadOnlyDC : %s \n ' % bool_string ( serverinfo . fReadOnlyDC ) )
def print_zoneinfo ( outf , typeid , zoneinfo ) :
outf . write ( ' pszZoneName : %s \n ' % zoneinfo . pszZoneName )
outf . write ( ' dwZoneType : %s \n ' % zone_type_string ( zoneinfo . dwZoneType ) )
outf . write ( ' fReverse : %s \n ' % bool_string ( zoneinfo . fReverse ) )
outf . write ( ' fAllowUpdate : %s \n ' % zone_update_string ( zoneinfo . fAllowUpdate ) )
outf . write ( ' fPaused : %s \n ' % bool_string ( zoneinfo . fPaused ) )
outf . write ( ' fShutdown : %s \n ' % bool_string ( zoneinfo . fShutdown ) )
outf . write ( ' fAutoCreated : %s \n ' % bool_string ( zoneinfo . fAutoCreated ) )
outf . write ( ' fUseDatabase : %s \n ' % bool_string ( zoneinfo . fUseDatabase ) )
outf . write ( ' pszDataFile : %s \n ' % zoneinfo . pszDataFile )
if typeid != dnsserver . DNSSRV_TYPEID_ZONE_INFO :
outf . write ( ' aipMasters : %s \n ' %
2018-07-30 09:15:34 +03:00
ip4_array_string ( zoneinfo . aipMasters ) )
2011-10-17 06:15:40 +04:00
else :
outf . write ( ' aipMasters : %s \n ' %
2018-07-30 09:15:34 +03:00
dns_addr_array_string ( zoneinfo . aipMasters ) )
2011-10-17 06:15:40 +04:00
outf . write ( ' fSecureSecondaries : %s \n ' % zone_secondary_security_string ( zoneinfo . fSecureSecondaries ) )
outf . write ( ' fNotifyLevel : %s \n ' % zone_notify_level_string ( zoneinfo . fNotifyLevel ) )
if typeid != dnsserver . DNSSRV_TYPEID_ZONE_INFO :
outf . write ( ' aipSecondaries : %s \n ' %
2018-07-30 09:15:34 +03:00
ip4_array_string ( zoneinfo . aipSecondaries ) )
2011-10-17 06:15:40 +04:00
outf . write ( ' aipNotify : %s \n ' %
2018-07-30 09:15:34 +03:00
ip4_array_string ( zoneinfo . aipNotify ) )
2011-10-17 06:15:40 +04:00
else :
outf . write ( ' aipSecondaries : %s \n ' %
2018-07-30 09:15:34 +03:00
dns_addr_array_string ( zoneinfo . aipSecondaries ) )
2011-10-17 06:15:40 +04:00
outf . write ( ' aipNotify : %s \n ' %
2018-07-30 09:15:34 +03:00
dns_addr_array_string ( zoneinfo . aipNotify ) )
2011-10-17 06:15:40 +04:00
outf . write ( ' fUseWins : %s \n ' % bool_string ( zoneinfo . fUseWins ) )
outf . write ( ' fUseNbstat : %s \n ' % bool_string ( zoneinfo . fUseNbstat ) )
outf . write ( ' fAging : %s \n ' % bool_string ( zoneinfo . fAging ) )
outf . write ( ' dwNoRefreshInterval : %d \n ' % zoneinfo . dwNoRefreshInterval )
outf . write ( ' dwRefreshInterval : %d \n ' % zoneinfo . dwRefreshInterval )
outf . write ( ' dwAvailForScavengeTime : %d \n ' % zoneinfo . dwAvailForScavengeTime )
if typeid != dnsserver . DNSSRV_TYPEID_ZONE_INFO :
outf . write ( ' aipScavengeServers : %s \n ' %
2018-07-30 09:15:34 +03:00
ip4_array_string ( zoneinfo . aipScavengeServers ) )
2011-10-17 06:15:40 +04:00
else :
outf . write ( ' aipScavengeServers : %s \n ' %
2018-07-30 09:15:34 +03:00
dns_addr_array_string ( zoneinfo . aipScavengeServers ) )
2011-10-17 06:15:40 +04:00
if typeid != dnsserver . DNSSRV_TYPEID_ZONE_INFO_W2K :
outf . write ( ' dwRpcStructureVersion : 0x %x \n ' % zoneinfo . dwRpcStructureVersion )
outf . write ( ' dwForwarderTimeout : %d \n ' % zoneinfo . dwForwarderTimeout )
outf . write ( ' fForwarderSlave : %d \n ' % zoneinfo . fForwarderSlave )
if typeid != dnsserver . DNSSRV_TYPEID_ZONE_INFO :
outf . write ( ' aipLocalMasters : %s \n ' %
2018-07-30 09:15:34 +03:00
ip4_array_string ( zoneinfo . aipLocalMasters ) )
2011-10-17 06:15:40 +04:00
else :
outf . write ( ' aipLocalMasters : %s \n ' %
2018-07-30 09:15:34 +03:00
dns_addr_array_string ( zoneinfo . aipLocalMasters ) )
2011-10-17 06:15:40 +04:00
outf . write ( ' dwDpFlags : %s \n ' % dp_flags_string ( zoneinfo . dwDpFlags ) )
outf . write ( ' pszDpFqdn : %s \n ' % zoneinfo . pszDpFqdn )
outf . write ( ' pwszZoneDn : %s \n ' % zoneinfo . pwszZoneDn )
outf . write ( ' dwLastSuccessfulSoaCheck : %d \n ' % zoneinfo . dwLastSuccessfulSoaCheck )
outf . write ( ' dwLastSuccessfulXfr : %d \n ' % zoneinfo . dwLastSuccessfulXfr )
if typeid == dnsserver . DNSSRV_TYPEID_ZONE_INFO :
outf . write ( ' fQueuedForBackgroundLoad : %s \n ' % bool_string ( zoneinfo . fQueuedForBackgroundLoad ) )
outf . write ( ' fBackgroundLoadInProgress : %s \n ' % bool_string ( zoneinfo . fBackgroundLoadInProgress ) )
outf . write ( ' fReadOnlyZone : %s \n ' % bool_string ( zoneinfo . fReadOnlyZone ) )
outf . write ( ' dwLastXfrAttempt : %d \n ' % zoneinfo . dwLastXfrAttempt )
outf . write ( ' dwLastXfrResult : %d \n ' % zoneinfo . dwLastXfrResult )
def print_zone ( outf , typeid , zone ) :
outf . write ( ' pszZoneName : %s \n ' % zone . pszZoneName )
outf . write ( ' Flags : %s \n ' % zone_flags_string ( zone . Flags ) )
outf . write ( ' ZoneType : %s \n ' % zone_type_string ( zone . ZoneType ) )
outf . write ( ' Version : %s \n ' % zone . Version )
if typeid != dnsserver . DNSSRV_TYPEID_ZONE_W2K :
outf . write ( ' dwDpFlags : %s \n ' % dp_flags_string ( zone . dwDpFlags ) )
outf . write ( ' pszDpFqdn : %s \n ' % zone . pszDpFqdn )
def print_enumzones ( outf , typeid , zones ) :
outf . write ( ' %d zone(s) found \n ' % zones . dwZoneCount )
for zone in zones . ZoneArray :
outf . write ( ' \n ' )
print_zone ( outf , typeid , zone )
def print_dns_record ( outf , rec ) :
if rec . wType == dnsp . DNS_TYPE_A :
mesg = ' A: %s ' % ( rec . data )
2011-12-14 08:54:31 +04:00
elif rec . wType == dnsp . DNS_TYPE_AAAA :
mesg = ' AAAA: %s ' % ( rec . data )
elif rec . wType == dnsp . DNS_TYPE_PTR :
mesg = ' PTR: %s ' % ( rec . data . str )
2011-10-17 06:15:40 +04:00
elif rec . wType == dnsp . DNS_TYPE_NS :
mesg = ' NS: %s ' % ( rec . data . str )
elif rec . wType == dnsp . DNS_TYPE_CNAME :
mesg = ' CNAME: %s ' % ( rec . data . str )
elif rec . wType == dnsp . DNS_TYPE_SOA :
2012-12-06 09:10:42 +04:00
mesg = ' SOA: serial= %d , refresh= %d , retry= %d , expire= %d , minttl= %d , ns= %s , email= %s ' % (
2011-10-17 06:15:40 +04:00
rec . data . dwSerialNo ,
rec . data . dwRefresh ,
rec . data . dwRetry ,
rec . data . dwExpire ,
2012-12-06 09:10:42 +04:00
rec . data . dwMinimumTtl ,
2011-10-17 06:15:40 +04:00
rec . data . NamePrimaryServer . str ,
rec . data . ZoneAdministratorEmail . str )
elif rec . wType == dnsp . DNS_TYPE_MX :
2012-02-15 13:56:38 +04:00
mesg = ' MX: %s ( %d ) ' % ( rec . data . nameExchange . str , rec . data . wPreference )
2011-10-17 06:15:40 +04:00
elif rec . wType == dnsp . DNS_TYPE_SRV :
2012-02-15 13:56:38 +04:00
mesg = ' SRV: %s ( %d , %d , %d ) ' % ( rec . data . nameTarget . str , rec . data . wPort ,
rec . data . wPriority , rec . data . wWeight )
2012-02-28 08:14:49 +04:00
elif rec . wType == dnsp . DNS_TYPE_TXT :
slist = [ ' " %s " ' % name . str for name in rec . data . str ]
mesg = ' TXT: %s ' % ' , ' . join ( slist )
else :
mesg = ' Unknown: '
2011-10-17 06:15:40 +04:00
outf . write ( ' %s (flags= %x , serial= %d , ttl= %d ) \n ' % (
mesg , rec . dwFlags , rec . dwSerial , rec . dwTtlSeconds ) )
def print_dnsrecords ( outf , records ) :
for rec in records . rec :
outf . write ( ' Name= %s , Records= %d , Children= %d \n ' % (
rec . dnsNodeName . str ,
rec . wRecordCount ,
rec . dwChildCount ) )
for dns_rec in rec . records :
print_dns_record ( outf , dns_rec )
2012-02-14 06:19:36 +04:00
# Convert data into a dns record
def data_to_dns_record ( record_type , data ) :
2021-03-26 10:41:29 +03:00
try :
rec = record_from_string ( record_type , data )
except DNSParseError as e :
raise CommandError ( * e . args ) from None
2012-02-14 06:19:36 +04:00
return rec
2011-10-17 06:15:40 +04:00
class cmd_serverinfo ( Command ) :
2012-10-08 14:32:58 +04:00
""" Query for Server information. """
2011-10-17 06:15:40 +04:00
synopsis = ' % prog <server> [options] '
2018-07-30 09:17:02 +03:00
takes_args = [ ' server ' ]
2011-10-17 06:15:40 +04:00
2012-02-07 10:27:18 +04:00
takes_optiongroups = {
" sambaopts " : options . SambaOptions ,
" versionopts " : options . VersionOptions ,
" credopts " : options . CredentialsOptions ,
}
2011-10-17 06:15:40 +04:00
takes_options = [
2011-10-21 04:27:28 +04:00
Option ( ' --client-version ' , help = ' Client Version ' ,
2018-07-30 09:15:34 +03:00
default = ' longhorn ' , metavar = ' w2k|dotnet|longhorn ' ,
2018-07-30 09:19:05 +03:00
choices = [ ' w2k ' , ' dotnet ' , ' longhorn ' ] , dest = ' cli_ver ' ) ,
2011-10-17 06:15:40 +04:00
]
2012-09-16 16:18:39 +04:00
def run ( self , server , cli_ver , sambaopts = None , credopts = None ,
versionopts = None ) :
2011-10-17 06:15:40 +04:00
self . lp = sambaopts . get_loadparm ( )
self . creds = credopts . get_credentials ( self . lp )
2022-08-12 07:40:03 +03:00
dns_conn = DnsConnWrapper ( server , self . lp , self . creds )
2011-10-17 06:15:40 +04:00
client_version = dns_client_version ( cli_ver )
2012-09-16 16:18:39 +04:00
typeid , res = dns_conn . DnssrvQuery2 ( client_version , 0 , server ,
None , ' ServerInfo ' )
2011-10-17 06:15:40 +04:00
print_serverinfo ( self . outf , typeid , res )
2021-04-19 15:07:50 +03:00
def _add_integer_options ( table , takes_options , integer_properties ) :
""" Generate options for cmd_zoneoptions """
for k , doc , _min , _max in table :
o = ' -- ' + k . lower ( )
opt = Option ( o ,
help = f " { doc } [ { _min } - { _max } ] " ,
type = " int " ,
dest = k )
takes_options . append ( opt )
integer_properties . append ( ( k , _min , _max , o ) )
class cmd_zoneoptions ( Command ) :
""" Change zone aging options. """
synopsis = ' % prog <server> <zone> [options] '
takes_args = [ ' server ' , ' zone ' ]
takes_optiongroups = {
" sambaopts " : options . SambaOptions ,
" versionopts " : options . VersionOptions ,
" credopts " : options . CredentialsOptions ,
}
takes_options = [
Option ( ' --client-version ' , help = ' Client Version ' ,
default = ' longhorn ' , metavar = ' w2k|dotnet|longhorn ' ,
choices = [ ' w2k ' , ' dotnet ' , ' longhorn ' ] , dest = ' cli_ver ' ) ,
2021-06-01 03:58:28 +03:00
Option ( ' --mark-old-records-static ' , metavar = " YYYY-MM-DD " ,
samba-tool dns zoneoptions: timestamp manipulation options
There was a bug in Samba before 4.9 that marked all records intended
to be static with a current timestamp, and all records intended to be
dynamic with a zero timestamp. This was exactly the opposite of
correct behaviour.
It follows that a domain which has been upgraded past 4.9, but on
which aging is not enabled, records intended to be static will have a
timestamp from before the upgrade date (unless their nodes have
suffered a DNS update, which due to another bug, will change the
timestmap). The following command will make these truly static:
$ samba-tool dns zoneoptions --mark-old-records-static=2018-07-23 -U...
where '2018-07-23' should be replaced by the approximate date of the
upgrade beyond 4.9.
It seems riskier making blanket conversions of static records into
dynamic records, but there are sometimes useful patterns in the names
given to machines that we can exploit. For example, if there is a
group of machines with names like 'desktop-123' that are all supposed
to using dynamic DNS, the adminstrator can go
$ samba-tool dns zoneoptions --mark-records-dynamic-regex='desktop-\d+'
and there's a --mark-records-static-regex for symmetry.
These options are deliberately long and cumbersome to type, so people
have a chance to think before they get to the end. We also introduce a
'--dry-run' (or '-n') option so they can inspect the likely results
before going ahead.
*NOTE* ageing will still not work properly after this commit, due to
other bugs that will be fixed in other commits.
Signed-off-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz>
Reviewed-by: Andrew Bartlett <abartlet@samba.org>
2021-05-27 00:46:02 +03:00
help = " Make records older than this (YYYY-MM-DD) static " ) ,
Option ( ' --mark-records-static-regex ' , metavar = " REGEXP " ,
help = " Make records matching this regular expression static " ) ,
Option ( ' --mark-records-dynamic-regex ' , metavar = " REGEXP " ,
help = " Make records matching this regular expression dynamic " ) ,
Option ( ' -n ' , ' --dry-run ' , action = ' store_true ' ,
help = " Don ' t change anything, say what would happen " ) ,
2021-04-19 15:07:50 +03:00
]
integer_properties = [ ]
# Any zone parameter that is stored as an integer (which is most of
# them) can be added to this table. The name should be the dnsp
# mixed case name, which will get munged into a lowercase name for
# the option. (e.g. "Aging" becomes "--aging").
#
# Note: just because we add a name here doesn't mean we will use
# it.
_add_integer_options ( [
# ( name, help-string, min, max )
( ' Aging ' , ' Enable record aging ' , 0 , 1 ) ,
( ' NoRefreshInterval ' ,
' Aging no refresh interval in hours (0: use default) ' ,
0 , 10 * 365 * 24 ) ,
( ' RefreshInterval ' ,
' Aging refresh interval in hours (0: use default) ' ,
0 , 10 * 365 * 24 ) ,
] ,
takes_options ,
integer_properties )
def run ( self , server , zone , cli_ver , sambaopts = None , credopts = None ,
samba-tool dns zoneoptions: timestamp manipulation options
There was a bug in Samba before 4.9 that marked all records intended
to be static with a current timestamp, and all records intended to be
dynamic with a zero timestamp. This was exactly the opposite of
correct behaviour.
It follows that a domain which has been upgraded past 4.9, but on
which aging is not enabled, records intended to be static will have a
timestamp from before the upgrade date (unless their nodes have
suffered a DNS update, which due to another bug, will change the
timestmap). The following command will make these truly static:
$ samba-tool dns zoneoptions --mark-old-records-static=2018-07-23 -U...
where '2018-07-23' should be replaced by the approximate date of the
upgrade beyond 4.9.
It seems riskier making blanket conversions of static records into
dynamic records, but there are sometimes useful patterns in the names
given to machines that we can exploit. For example, if there is a
group of machines with names like 'desktop-123' that are all supposed
to using dynamic DNS, the adminstrator can go
$ samba-tool dns zoneoptions --mark-records-dynamic-regex='desktop-\d+'
and there's a --mark-records-static-regex for symmetry.
These options are deliberately long and cumbersome to type, so people
have a chance to think before they get to the end. We also introduce a
'--dry-run' (or '-n') option so they can inspect the likely results
before going ahead.
*NOTE* ageing will still not work properly after this commit, due to
other bugs that will be fixed in other commits.
Signed-off-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz>
Reviewed-by: Andrew Bartlett <abartlet@samba.org>
2021-05-27 00:46:02 +03:00
versionopts = None , dry_run = False ,
mark_old_records_static = None ,
mark_records_static_regex = None ,
mark_records_dynamic_regex = None ,
* * kwargs ) :
2021-04-19 15:07:50 +03:00
self . lp = sambaopts . get_loadparm ( )
self . creds = credopts . get_credentials ( self . lp )
2022-08-12 07:40:03 +03:00
dns_conn = DnsConnWrapper ( server , self . lp , self . creds )
2021-04-19 15:07:50 +03:00
client_version = dns_client_version ( cli_ver )
nap_type = dnsserver . DNSSRV_TYPEID_NAME_AND_PARAM
for k , _min , _max , o in self . integer_properties :
if kwargs . get ( k ) is None :
continue
v = kwargs [ k ]
if _min is not None and v < _min :
raise CommandError ( f " { o } must be at least { _min } " )
if _max is not None and v > _max :
raise CommandError ( f " { o } can ' t exceed { _max } " )
name_param = dnsserver . DNS_RPC_NAME_AND_PARAM ( )
name_param . dwParam = v
name_param . pszNodeName = k
samba-tool dns zoneoptions: timestamp manipulation options
There was a bug in Samba before 4.9 that marked all records intended
to be static with a current timestamp, and all records intended to be
dynamic with a zero timestamp. This was exactly the opposite of
correct behaviour.
It follows that a domain which has been upgraded past 4.9, but on
which aging is not enabled, records intended to be static will have a
timestamp from before the upgrade date (unless their nodes have
suffered a DNS update, which due to another bug, will change the
timestmap). The following command will make these truly static:
$ samba-tool dns zoneoptions --mark-old-records-static=2018-07-23 -U...
where '2018-07-23' should be replaced by the approximate date of the
upgrade beyond 4.9.
It seems riskier making blanket conversions of static records into
dynamic records, but there are sometimes useful patterns in the names
given to machines that we can exploit. For example, if there is a
group of machines with names like 'desktop-123' that are all supposed
to using dynamic DNS, the adminstrator can go
$ samba-tool dns zoneoptions --mark-records-dynamic-regex='desktop-\d+'
and there's a --mark-records-static-regex for symmetry.
These options are deliberately long and cumbersome to type, so people
have a chance to think before they get to the end. We also introduce a
'--dry-run' (or '-n') option so they can inspect the likely results
before going ahead.
*NOTE* ageing will still not work properly after this commit, due to
other bugs that will be fixed in other commits.
Signed-off-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz>
Reviewed-by: Andrew Bartlett <abartlet@samba.org>
2021-05-27 00:46:02 +03:00
if dry_run :
print ( f " would set { k } to { v } for { zone } " , file = self . outf )
continue
2021-04-19 15:07:50 +03:00
try :
dns_conn . DnssrvOperation2 ( client_version ,
0 ,
server ,
zone ,
0 ,
' ResetDwordProperty ' ,
nap_type ,
name_param )
except WERRORError as e :
raise CommandError ( f " Could not set { k } to { v } " ) from None
print ( f " Set { k } to { v } " , file = self . outf )
samba-tool dns zoneoptions: timestamp manipulation options
There was a bug in Samba before 4.9 that marked all records intended
to be static with a current timestamp, and all records intended to be
dynamic with a zero timestamp. This was exactly the opposite of
correct behaviour.
It follows that a domain which has been upgraded past 4.9, but on
which aging is not enabled, records intended to be static will have a
timestamp from before the upgrade date (unless their nodes have
suffered a DNS update, which due to another bug, will change the
timestmap). The following command will make these truly static:
$ samba-tool dns zoneoptions --mark-old-records-static=2018-07-23 -U...
where '2018-07-23' should be replaced by the approximate date of the
upgrade beyond 4.9.
It seems riskier making blanket conversions of static records into
dynamic records, but there are sometimes useful patterns in the names
given to machines that we can exploit. For example, if there is a
group of machines with names like 'desktop-123' that are all supposed
to using dynamic DNS, the adminstrator can go
$ samba-tool dns zoneoptions --mark-records-dynamic-regex='desktop-\d+'
and there's a --mark-records-static-regex for symmetry.
These options are deliberately long and cumbersome to type, so people
have a chance to think before they get to the end. We also introduce a
'--dry-run' (or '-n') option so they can inspect the likely results
before going ahead.
*NOTE* ageing will still not work properly after this commit, due to
other bugs that will be fixed in other commits.
Signed-off-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz>
Reviewed-by: Andrew Bartlett <abartlet@samba.org>
2021-05-27 00:46:02 +03:00
# We don't want to allow more than one of these --mark-*
# options at a time, as they are sensitive to ordering and
# the order is not documented.
n_mark_options = 0
for x in ( mark_old_records_static ,
mark_records_static_regex ,
mark_records_dynamic_regex ) :
if x is not None :
n_mark_options + = 1
if n_mark_options > 1 :
raise CommandError ( " Multiple --mark-* options will not work \n " )
if mark_old_records_static is not None :
self . mark_old_records_static ( server , zone ,
mark_old_records_static ,
dry_run )
if mark_records_static_regex is not None :
self . mark_records_static_regex ( server ,
zone ,
mark_records_static_regex ,
dry_run )
if mark_records_dynamic_regex is not None :
self . mark_records_dynamic_regex ( server ,
zone ,
mark_records_dynamic_regex ,
dry_run )
def _get_dns_nodes ( self , server , zone_name ) :
samdb = SamDB ( url = " ldap:// %s " % server ,
session_info = system_session ( ) ,
credentials = self . creds , lp = self . lp )
zone_dn = ( f " DC= { zone_name } ,CN=MicrosoftDNS,DC=DomainDNSZones, "
f " { samdb . get_default_basedn ( ) } " )
nodes = samdb . search ( base = zone_dn ,
scope = ldb . SCOPE_SUBTREE ,
expression = ( " (&(objectClass=dnsNode) "
" (!(dNSTombstoned=TRUE))) " ) ,
attrs = [ " dnsRecord " , " name " ] )
return samdb , nodes
def mark_old_records_static ( self , server , zone_name , date_string , dry_run ) :
try :
ts = time . strptime ( date_string , " % Y- % m- %d " )
t = time . mktime ( ts )
except ValueError as e :
raise CommandError ( f " Invalid date { date_string } : should be YYY-MM-DD " )
threshold = dsdb_dns . unix_to_dns_timestamp ( int ( t ) )
samdb , nodes = self . _get_dns_nodes ( server , zone_name )
for node in nodes :
if " dnsRecord " not in node :
continue
values = list ( node [ " dnsRecord " ] )
changes = 0
for i , v in enumerate ( values ) :
rec = ndr_unpack ( dnsp . DnssrvRpcRecord , v )
if rec . dwTimeStamp < threshold and rec . dwTimeStamp != 0 :
rec . dwTimeStamp = 0
values [ i ] = ndr_pack ( rec )
changes + = 1
if changes == 0 :
continue
name = node [ " name " ] [ 0 ] . decode ( )
if dry_run :
print ( f " would make { changes } / { len ( values ) } records static "
f " on { name } . { zone_name } . " , file = self . outf )
continue
msg = ldb . Message . from_dict ( samdb ,
{ ' dn ' : node . dn ,
' dnsRecord ' : values
} ,
ldb . FLAG_MOD_REPLACE )
samdb . modify ( msg )
print ( f " made { changes } / { len ( values ) } records static on "
f " { name } . { zone_name } . " , file = self . outf )
def mark_records_static_regex ( self , server , zone_name , regex , dry_run ) :
""" Make the records of nodes with matching names static.
"""
r = re . compile ( regex )
samdb , nodes = self . _get_dns_nodes ( server , zone_name )
for node in nodes :
name = node [ " name " ] [ 0 ] . decode ( )
if not r . search ( name ) :
continue
if " dnsRecord " not in node :
continue
values = list ( node [ " dnsRecord " ] )
if len ( values ) == 0 :
continue
changes = 0
for i , v in enumerate ( values ) :
rec = ndr_unpack ( dnsp . DnssrvRpcRecord , v )
if rec . dwTimeStamp != 0 :
rec . dwTimeStamp = 0
values [ i ] = ndr_pack ( rec )
changes + = 1
if changes == 0 :
continue
if dry_run :
print ( f " would make { changes } / { len ( values ) } records static "
f " on { name } . { zone_name } . " , file = self . outf )
continue
msg = ldb . Message . from_dict ( samdb ,
{ ' dn ' : node . dn ,
' dnsRecord ' : values
} ,
ldb . FLAG_MOD_REPLACE )
samdb . modify ( msg )
print ( f " made { changes } / { len ( values ) } records static on "
f " { name } . { zone_name } . " , file = self . outf )
def mark_records_dynamic_regex ( self , server , zone_name , regex , dry_run ) :
""" Make the records of nodes with matching names dynamic, with a
current timestamp . In this case we only adjust the A , AAAA ,
and TXT records .
"""
r = re . compile ( regex )
samdb , nodes = self . _get_dns_nodes ( server , zone_name )
now = time . time ( )
dns_timestamp = dsdb_dns . unix_to_dns_timestamp ( int ( now ) )
safe_wtypes = {
dnsp . DNS_TYPE_A ,
dnsp . DNS_TYPE_AAAA ,
dnsp . DNS_TYPE_TXT
}
for node in nodes :
name = node [ " name " ] [ 0 ] . decode ( )
if not r . search ( name ) :
continue
if " dnsRecord " not in node :
continue
values = list ( node [ " dnsRecord " ] )
if len ( values ) == 0 :
continue
changes = 0
for i , v in enumerate ( values ) :
rec = ndr_unpack ( dnsp . DnssrvRpcRecord , v )
if rec . wType in safe_wtypes and rec . dwTimeStamp == 0 :
rec . dwTimeStamp = dns_timestamp
values [ i ] = ndr_pack ( rec )
changes + = 1
if changes == 0 :
continue
if dry_run :
print ( f " would make { changes } / { len ( values ) } records dynamic "
f " on { name } . { zone_name } . " , file = self . outf )
continue
msg = ldb . Message . from_dict ( samdb ,
{ ' dn ' : node . dn ,
' dnsRecord ' : values
} ,
ldb . FLAG_MOD_REPLACE )
samdb . modify ( msg )
print ( f " made { changes } / { len ( values ) } records dynamic on "
f " { name } . { zone_name } . " , file = self . outf )
2021-04-19 15:07:50 +03:00
2011-10-17 06:15:40 +04:00
class cmd_zoneinfo ( Command ) :
2012-10-08 14:32:58 +04:00
""" Query for zone information. """
2011-10-17 06:15:40 +04:00
synopsis = ' % prog <server> <zone> [options] '
2018-07-30 09:17:02 +03:00
takes_args = [ ' server ' , ' zone ' ]
2011-10-17 06:15:40 +04:00
2012-02-07 10:27:18 +04:00
takes_optiongroups = {
" sambaopts " : options . SambaOptions ,
" versionopts " : options . VersionOptions ,
" credopts " : options . CredentialsOptions ,
}
2011-10-17 06:15:40 +04:00
takes_options = [
2011-10-21 04:27:28 +04:00
Option ( ' --client-version ' , help = ' Client Version ' ,
2018-07-30 09:15:34 +03:00
default = ' longhorn ' , metavar = ' w2k|dotnet|longhorn ' ,
2018-07-30 09:19:05 +03:00
choices = [ ' w2k ' , ' dotnet ' , ' longhorn ' ] , dest = ' cli_ver ' ) ,
2011-10-17 06:15:40 +04:00
]
2012-09-16 16:18:39 +04:00
def run ( self , server , zone , cli_ver , sambaopts = None , credopts = None ,
versionopts = None ) :
2011-10-17 06:15:40 +04:00
self . lp = sambaopts . get_loadparm ( )
self . creds = credopts . get_credentials ( self . lp )
2022-08-12 07:40:03 +03:00
dns_conn = DnsConnWrapper ( server , self . lp , self . creds )
2011-10-17 06:15:40 +04:00
client_version = dns_client_version ( cli_ver )
2012-09-16 16:18:39 +04:00
typeid , res = dns_conn . DnssrvQuery2 ( client_version , 0 , server , zone ,
2011-10-17 06:15:40 +04:00
' ZoneInfo ' )
print_zoneinfo ( self . outf , typeid , res )
class cmd_zonelist ( Command ) :
2012-10-08 14:32:58 +04:00
""" Query for zones. """
2011-10-17 06:15:40 +04:00
synopsis = ' % prog <server> [options] '
2018-07-30 09:17:02 +03:00
takes_args = [ ' server ' ]
2011-10-17 06:15:40 +04:00
2012-02-07 10:27:18 +04:00
takes_optiongroups = {
" sambaopts " : options . SambaOptions ,
" versionopts " : options . VersionOptions ,
" credopts " : options . CredentialsOptions ,
}
2011-10-17 06:15:40 +04:00
takes_options = [
2011-10-21 04:27:28 +04:00
Option ( ' --client-version ' , help = ' Client Version ' ,
2018-07-30 09:15:34 +03:00
default = ' longhorn ' , metavar = ' w2k|dotnet|longhorn ' ,
2018-07-30 09:19:05 +03:00
choices = [ ' w2k ' , ' dotnet ' , ' longhorn ' ] , dest = ' cli_ver ' ) ,
2011-10-17 06:15:40 +04:00
Option ( ' --primary ' , help = ' List primary zones (default) ' ,
2018-07-30 09:15:34 +03:00
action = ' store_true ' , dest = ' primary ' ) ,
2011-10-17 06:15:40 +04:00
Option ( ' --secondary ' , help = ' List secondary zones ' ,
2018-07-30 09:15:34 +03:00
action = ' store_true ' , dest = ' secondary ' ) ,
2011-10-17 06:15:40 +04:00
Option ( ' --cache ' , help = ' List cached zones ' ,
2018-07-30 09:15:34 +03:00
action = ' store_true ' , dest = ' cache ' ) ,
2011-10-17 06:15:40 +04:00
Option ( ' --auto ' , help = ' List automatically created zones ' ,
2018-07-30 09:15:34 +03:00
action = ' store_true ' , dest = ' auto ' ) ,
2011-10-17 06:15:40 +04:00
Option ( ' --forward ' , help = ' List forward zones ' ,
2018-07-30 09:15:34 +03:00
action = ' store_true ' , dest = ' forward ' ) ,
2011-10-17 06:15:40 +04:00
Option ( ' --reverse ' , help = ' List reverse zones ' ,
2018-07-30 09:15:34 +03:00
action = ' store_true ' , dest = ' reverse ' ) ,
2011-10-17 06:15:40 +04:00
Option ( ' --ds ' , help = ' List directory integrated zones ' ,
2018-07-30 09:15:34 +03:00
action = ' store_true ' , dest = ' ds ' ) ,
2011-10-17 06:15:40 +04:00
Option ( ' --non-ds ' , help = ' List non-directory zones ' ,
2018-07-30 09:15:34 +03:00
action = ' store_true ' , dest = ' nonds ' )
2011-10-17 06:15:40 +04:00
]
def run ( self , server , cli_ver , primary = False , secondary = False , cache = False ,
2018-07-30 09:15:34 +03:00
auto = False , forward = False , reverse = False , ds = False , nonds = False ,
sambaopts = None , credopts = None , versionopts = None ) :
2011-10-17 06:15:40 +04:00
request_filter = 0
if primary :
request_filter | = dnsserver . DNS_ZONE_REQUEST_PRIMARY
if secondary :
request_filter | = dnsserver . DNS_ZONE_REQUEST_SECONDARY
if cache :
request_filter | = dnsserver . DNS_ZONE_REQUEST_CACHE
if auto :
request_filter | = dnsserver . DNS_ZONE_REQUEST_AUTO
if forward :
request_filter | = dnsserver . DNS_ZONE_REQUEST_FORWARD
if reverse :
request_filter | = dnsserver . DNS_ZONE_REQUEST_REVERSE
if ds :
request_filter | = dnsserver . DNS_ZONE_REQUEST_DS
if nonds :
request_filter | = dnsserver . DNS_ZONE_REQUEST_NON_DS
if request_filter == 0 :
request_filter = dnsserver . DNS_ZONE_REQUEST_PRIMARY
self . lp = sambaopts . get_loadparm ( )
self . creds = credopts . get_credentials ( self . lp )
2022-08-12 07:40:03 +03:00
dns_conn = DnsConnWrapper ( server , self . lp , self . creds )
2011-10-17 06:15:40 +04:00
client_version = dns_client_version ( cli_ver )
typeid , res = dns_conn . DnssrvComplexOperation2 ( client_version ,
2018-07-30 09:15:34 +03:00
0 , server , None ,
' EnumZones ' ,
dnsserver . DNSSRV_TYPEID_DWORD ,
request_filter )
2011-10-17 06:15:40 +04:00
if client_version == dnsserver . DNS_CLIENT_VERSION_W2K :
typeid = dnsserver . DNSSRV_TYPEID_ZONE_W2K
else :
typeid = dnsserver . DNSSRV_TYPEID_ZONE
print_enumzones ( self . outf , typeid , res )
2011-12-20 05:06:47 +04:00
class cmd_zonecreate ( Command ) :
2012-10-08 14:32:58 +04:00
""" Create a zone. """
2011-12-20 05:06:47 +04:00
synopsis = ' % prog <server> <zone> [options] '
2018-07-30 09:17:02 +03:00
takes_args = [ ' server ' , ' zone ' ]
2011-12-20 05:06:47 +04:00
2012-02-07 10:27:18 +04:00
takes_optiongroups = {
" sambaopts " : options . SambaOptions ,
" versionopts " : options . VersionOptions ,
" credopts " : options . CredentialsOptions ,
}
2011-12-20 05:06:47 +04:00
takes_options = [
Option ( ' --client-version ' , help = ' Client Version ' ,
2018-07-30 09:15:34 +03:00
default = ' longhorn ' , metavar = ' w2k|dotnet|longhorn ' ,
2023-06-15 19:24:50 +03:00
choices = [ ' w2k ' , ' dotnet ' , ' longhorn ' ] , dest = ' cli_ver ' ) ,
Option ( ' --dns-directory-partition ' ,
help = ' Specify the naming context for the new zone, which '
' affects the replication scope (domain or forest wide '
2023-06-21 21:52:03 +03:00
' replication, default: domain). ' ,
2023-06-15 19:24:50 +03:00
default = ' domain ' ,
metavar = ' domain|forest ' ,
choices = [ ' domain ' , ' forest ' ] ,
dest = ' dns_dp ' ) ,
2011-12-20 05:06:47 +04:00
]
2023-06-15 19:24:50 +03:00
def run ( self ,
server ,
zone ,
cli_ver ,
dns_dp ,
sambaopts = None ,
credopts = None ,
2011-12-20 05:06:47 +04:00
versionopts = None ) :
self . lp = sambaopts . get_loadparm ( )
self . creds = credopts . get_credentials ( self . lp )
2022-08-12 07:40:03 +03:00
dns_conn = DnsConnWrapper ( server , self . lp , self . creds )
2011-12-20 05:06:47 +04:00
zone = zone . lower ( )
2023-06-15 19:24:50 +03:00
dns_directorypartition = dnsserver . DNS_DP_DOMAIN_DEFAULT
if dns_dp == ' forest ' :
dns_directorypartition = dnsserver . DNS_DP_FOREST_DEFAULT
2011-12-20 05:06:47 +04:00
client_version = dns_client_version ( cli_ver )
if client_version == dnsserver . DNS_CLIENT_VERSION_W2K :
typeid = dnsserver . DNSSRV_TYPEID_ZONE_CREATE_W2K
zone_create_info = dnsserver . DNS_RPC_ZONE_CREATE_INFO_W2K ( )
zone_create_info . pszZoneName = zone
zone_create_info . dwZoneType = dnsp . DNS_ZONE_TYPE_PRIMARY
zone_create_info . fAging = 0
2013-05-27 06:26:36 +04:00
zone_create_info . fDsIntegrated = 1
zone_create_info . fLoadExisting = 1
2011-12-20 05:06:47 +04:00
elif client_version == dnsserver . DNS_CLIENT_VERSION_DOTNET :
typeid = dnsserver . DNSSRV_TYPEID_ZONE_CREATE_DOTNET
zone_create_info = dnsserver . DNS_RPC_ZONE_CREATE_INFO_DOTNET ( )
zone_create_info . pszZoneName = zone
zone_create_info . dwZoneType = dnsp . DNS_ZONE_TYPE_PRIMARY
zone_create_info . fAging = 0
2013-05-27 06:26:36 +04:00
zone_create_info . fDsIntegrated = 1
zone_create_info . fLoadExisting = 1
2023-06-15 19:24:50 +03:00
zone_create_info . dwDpFlags = dns_directorypartition
2011-12-20 05:06:47 +04:00
else :
typeid = dnsserver . DNSSRV_TYPEID_ZONE_CREATE
zone_create_info = dnsserver . DNS_RPC_ZONE_CREATE_INFO_LONGHORN ( )
zone_create_info . pszZoneName = zone
zone_create_info . dwZoneType = dnsp . DNS_ZONE_TYPE_PRIMARY
zone_create_info . fAging = 0
2013-05-27 06:26:36 +04:00
zone_create_info . fDsIntegrated = 1
zone_create_info . fLoadExisting = 1
2023-06-15 19:24:50 +03:00
zone_create_info . dwDpFlags = dns_directorypartition
2011-12-20 05:06:47 +04:00
2022-05-05 12:32:13 +03:00
dns_conn . DnssrvOperation2 ( client_version , 0 , server , None ,
0 , ' ZoneCreate ' , typeid ,
zone_create_info )
2013-05-27 06:37:20 +04:00
typeid = dnsserver . DNSSRV_TYPEID_NAME_AND_PARAM
name_and_param = dnsserver . DNS_RPC_NAME_AND_PARAM ( )
name_and_param . pszNodeName = ' AllowUpdate '
name_and_param . dwParam = dnsp . DNS_ZONE_UPDATE_SECURE
2022-08-13 03:55:01 +03:00
messages = {
werror . WERR_DNS_ERROR_ZONE_ALREADY_EXISTS : (
f ' Zone " { zone } " already exists. ' )
}
dns_conn . DnssrvOperation2 ( client_version , 0 , server , zone ,
0 , ' ResetDwordProperty ' , typeid ,
name_and_param , messages = messages )
2016-11-25 06:29:31 +03:00
2011-12-20 05:06:47 +04:00
self . outf . write ( ' Zone %s created successfully \n ' % zone )
class cmd_zonedelete ( Command ) :
2012-10-08 14:32:58 +04:00
""" Delete a zone. """
2011-12-20 05:06:47 +04:00
synopsis = ' % prog <server> <zone> [options] '
2018-07-30 09:17:02 +03:00
takes_args = [ ' server ' , ' zone ' ]
2011-12-20 05:06:47 +04:00
2012-02-07 10:27:18 +04:00
takes_optiongroups = {
" sambaopts " : options . SambaOptions ,
" versionopts " : options . VersionOptions ,
" credopts " : options . CredentialsOptions ,
}
2012-09-16 16:18:39 +04:00
def run ( self , server , zone , sambaopts = None , credopts = None ,
versionopts = None ) :
2011-12-20 05:06:47 +04:00
self . lp = sambaopts . get_loadparm ( )
self . creds = credopts . get_credentials ( self . lp )
2022-08-12 07:40:03 +03:00
dns_conn = DnsConnWrapper ( server , self . lp , self . creds )
2011-12-20 05:06:47 +04:00
zone = zone . lower ( )
2022-08-12 07:44:31 +03:00
messages = {
werror . WERR_DNS_ERROR_ZONE_DOES_NOT_EXIST : (
f ' Zone { zone } does not exist and so could not be deleted. ' ) ,
}
res = dns_conn . DnssrvOperation2 ( dnsserver . DNS_CLIENT_VERSION_LONGHORN ,
0 , server , zone , 0 , ' DeleteZoneFromDs ' ,
dnsserver . DNSSRV_TYPEID_NULL ,
None , messages = messages )
2016-11-25 06:29:31 +03:00
self . outf . write ( ' Zone %s deleted successfully \n ' % zone )
2011-12-20 05:06:47 +04:00
2011-10-17 06:15:40 +04:00
class cmd_query ( Command ) :
2011-10-21 04:27:28 +04:00
""" Query a name. """
2011-10-17 06:15:40 +04:00
2020-06-05 09:56:21 +03:00
synopsis = ( ' % prog <server> <zone> <name> '
' <A|AAAA|PTR|CNAME|MX|NS|SOA|SRV|TXT|ALL> [options] ' )
2011-10-17 06:15:40 +04:00
2018-07-30 09:17:02 +03:00
takes_args = [ ' server ' , ' zone ' , ' name ' , ' rtype ' ]
2011-10-17 06:15:40 +04:00
2012-02-07 10:27:18 +04:00
takes_optiongroups = {
" sambaopts " : options . SambaOptions ,
" versionopts " : options . VersionOptions ,
" credopts " : options . CredentialsOptions ,
}
2011-10-17 06:15:40 +04:00
takes_options = [
Option ( ' --authority ' , help = ' Search authoritative records (default) ' ,
2018-07-30 09:15:34 +03:00
action = ' store_true ' , dest = ' authority ' ) ,
2011-10-17 06:15:40 +04:00
Option ( ' --cache ' , help = ' Search cached records ' ,
2018-07-30 09:15:34 +03:00
action = ' store_true ' , dest = ' cache ' ) ,
2011-10-17 06:15:40 +04:00
Option ( ' --glue ' , help = ' Search glue records ' ,
2018-07-30 09:15:34 +03:00
action = ' store_true ' , dest = ' glue ' ) ,
2011-10-17 06:15:40 +04:00
Option ( ' --root ' , help = ' Search root hints ' ,
2018-07-30 09:15:34 +03:00
action = ' store_true ' , dest = ' root ' ) ,
2011-10-17 06:15:40 +04:00
Option ( ' --additional ' , help = ' List additional records ' ,
2018-07-30 09:15:34 +03:00
action = ' store_true ' , dest = ' additional ' ) ,
2011-10-17 06:15:40 +04:00
Option ( ' --no-children ' , help = ' Do not list children ' ,
2018-07-30 09:15:34 +03:00
action = ' store_true ' , dest = ' no_children ' ) ,
2011-10-17 06:15:40 +04:00
Option ( ' --only-children ' , help = ' List only children ' ,
2018-07-30 09:15:34 +03:00
action = ' store_true ' , dest = ' only_children ' )
2011-10-17 06:15:40 +04:00
]
2012-09-16 16:18:39 +04:00
def run ( self , server , zone , name , rtype , authority = False , cache = False ,
glue = False , root = False , additional = False , no_children = False ,
only_children = False , sambaopts = None , credopts = None ,
versionopts = None ) :
2011-10-17 06:15:40 +04:00
record_type = dns_type_flag ( rtype )
2013-08-02 12:53:56 +04:00
if name . find ( ' * ' ) != - 1 :
2017-07-20 00:13:43 +03:00
self . outf . write ( ' use " @ " to dump entire domain, looking up %s \n ' %
name )
2013-08-02 12:53:56 +04:00
2011-10-17 06:15:40 +04:00
select_flags = 0
if authority :
select_flags | = dnsserver . DNS_RPC_VIEW_AUTHORITY_DATA
if cache :
select_flags | = dnsserver . DNS_RPC_VIEW_CACHE_DATA
if glue :
select_flags | = dnsserver . DNS_RPC_VIEW_GLUE_DATA
if root :
select_flags | = dnsserver . DNS_RPC_VIEW_ROOT_HINT_DATA
if additional :
select_flags | = dnsserver . DNS_RPC_VIEW_ADDITIONAL_DATA
if no_children :
select_flags | = dnsserver . DNS_RPC_VIEW_NO_CHILDREN
if only_children :
select_flags | = dnsserver . DNS_RPC_VIEW_ONLY_CHILDREN
if select_flags == 0 :
select_flags = dnsserver . DNS_RPC_VIEW_AUTHORITY_DATA
2011-10-21 04:27:28 +04:00
if select_flags == dnsserver . DNS_RPC_VIEW_ADDITIONAL_DATA :
self . outf . write ( ' Specify either --authority or --root along with --additional. \n ' )
self . outf . write ( ' Assuming --authority. \n ' )
select_flags | = dnsserver . DNS_RPC_VIEW_AUTHORITY_DATA
2011-10-17 06:15:40 +04:00
self . lp = sambaopts . get_loadparm ( )
self . creds = credopts . get_credentials ( self . lp )
2022-08-12 07:40:03 +03:00
dns_conn = DnsConnWrapper ( server , self . lp , self . creds )
2011-10-17 06:15:40 +04:00
2022-08-12 07:46:03 +03:00
messages = {
werror . WERR_DNS_ERROR_NAME_DOES_NOT_EXIST : (
' Record or zone does not exist. ' )
}
buflen , res = dns_conn . DnssrvEnumRecords2 (
dnsserver . DNS_CLIENT_VERSION_LONGHORN , 0 , server , zone , name ,
None , record_type , select_flags , None , None ,
messages = messages )
2016-11-25 06:29:31 +03:00
2011-10-17 06:15:40 +04:00
print_dnsrecords ( self . outf , res )
class cmd_roothints ( Command ) :
2012-10-08 14:32:58 +04:00
""" Query root hints. """
2011-10-17 06:15:40 +04:00
synopsis = ' % prog <server> [<name>] [options] '
2018-07-30 09:17:02 +03:00
takes_args = [ ' server ' , ' name? ' ]
2011-10-17 06:15:40 +04:00
2012-02-07 10:27:18 +04:00
takes_optiongroups = {
" sambaopts " : options . SambaOptions ,
" versionopts " : options . VersionOptions ,
" credopts " : options . CredentialsOptions ,
}
2012-09-16 16:18:39 +04:00
def run ( self , server , name = ' . ' , sambaopts = None , credopts = None ,
versionopts = None ) :
2011-10-17 06:15:40 +04:00
record_type = dnsp . DNS_TYPE_NS
select_flags = ( dnsserver . DNS_RPC_VIEW_ROOT_HINT_DATA |
dnsserver . DNS_RPC_VIEW_ADDITIONAL_DATA )
self . lp = sambaopts . get_loadparm ( )
self . creds = credopts . get_credentials ( self . lp )
2022-08-12 07:40:03 +03:00
dns_conn = DnsConnWrapper ( server , self . lp , self . creds )
2011-10-17 06:15:40 +04:00
2012-09-16 16:18:39 +04:00
buflen , res = dns_conn . DnssrvEnumRecords2 (
dnsserver . DNS_CLIENT_VERSION_LONGHORN , 0 , server , ' ..RootHints ' ,
name , None , record_type , select_flags , None , None )
2011-10-17 06:15:40 +04:00
print_dnsrecords ( self . outf , res )
class cmd_add_record ( Command ) :
2012-02-14 06:41:45 +04:00
""" Add a DNS record
2011-10-17 06:15:40 +04:00
2012-02-14 06:41:45 +04:00
For each type data contents are as follows :
A ipv4_address_string
AAAA ipv6_address_string
PTR fqdn_string
CNAME fqdn_string
NS fqdn_string
MX " fqdn_string preference "
SRV " fqdn_string port priority weight "
2012-02-28 08:14:49 +04:00
TXT " ' string1 ' ' string2 ' ... "
2012-02-14 06:41:45 +04:00
"""
2012-02-28 08:14:49 +04:00
synopsis = ' % prog <server> <zone> <name> <A|AAAA|PTR|CNAME|NS|MX|SRV|TXT> <data> '
2011-10-17 06:15:40 +04:00
2018-07-30 09:17:02 +03:00
takes_args = [ ' server ' , ' zone ' , ' name ' , ' rtype ' , ' data ' ]
2011-10-17 06:15:40 +04:00
2012-02-07 10:27:18 +04:00
takes_optiongroups = {
" sambaopts " : options . SambaOptions ,
" versionopts " : options . VersionOptions ,
" credopts " : options . CredentialsOptions ,
}
2012-09-16 16:18:39 +04:00
def run ( self , server , zone , name , rtype , data , sambaopts = None ,
credopts = None , versionopts = None ) :
2011-10-17 06:15:40 +04:00
2018-07-30 09:19:05 +03:00
if rtype . upper ( ) not in ( ' A ' , ' AAAA ' , ' PTR ' , ' CNAME ' , ' NS ' , ' MX ' , ' SRV ' , ' TXT ' ) :
2011-10-17 06:15:40 +04:00
raise CommandError ( ' Adding record of type %s is not supported ' % rtype )
2012-02-14 06:41:45 +04:00
record_type = dns_type_flag ( rtype )
rec = data_to_dns_record ( record_type , data )
2011-10-17 06:15:40 +04:00
self . lp = sambaopts . get_loadparm ( )
self . creds = credopts . get_credentials ( self . lp )
2022-08-12 07:40:03 +03:00
dns_conn = DnsConnWrapper ( server , self . lp , self . creds )
2011-10-17 06:15:40 +04:00
add_rec_buf = dnsserver . DNS_RPC_RECORD_BUF ( )
add_rec_buf . rec = rec
2022-08-12 07:51:25 +03:00
messages = {
werror . WERR_DNS_ERROR_NAME_DOES_NOT_EXIST : (
' Zone does not exist; record could not be added. '
f ' zone[ { zone } ] name[ { name } ' ) ,
werror . WERR_DNS_ERROR_RECORD_ALREADY_EXISTS : (
' Record already exists; record could not be added. '
f ' zone[ { zone } ] name[ { name } ] ' )
}
dns_conn . DnssrvUpdateRecord2 ( dnsserver . DNS_CLIENT_VERSION_LONGHORN ,
0 , server , zone , name , add_rec_buf , None ,
messages = messages )
2016-11-25 06:29:31 +03:00
2011-12-20 05:07:11 +04:00
self . outf . write ( ' Record added successfully \n ' )
2011-10-17 06:15:40 +04:00
class cmd_update_record ( Command ) :
2012-02-14 06:41:45 +04:00
""" Update a DNS record
For each type data contents are as follows :
A ipv4_address_string
AAAA ipv6_address_string
PTR fqdn_string
CNAME fqdn_string
NS fqdn_string
MX " fqdn_string preference "
2012-12-06 09:11:18 +04:00
SOA " fqdn_dns fqdn_email serial refresh retry expire minimumttl "
2012-02-14 06:41:45 +04:00
SRV " fqdn_string port priority weight "
2012-02-28 08:14:49 +04:00
TXT " ' string1 ' ' string2 ' ... "
2012-02-14 06:41:45 +04:00
"""
2011-10-17 06:15:40 +04:00
2012-12-06 09:11:18 +04:00
synopsis = ' % prog <server> <zone> <name> <A|AAAA|PTR|CNAME|NS|MX|SOA|SRV|TXT> <olddata> <newdata> '
2011-10-17 06:15:40 +04:00
2018-07-30 09:17:02 +03:00
takes_args = [ ' server ' , ' zone ' , ' name ' , ' rtype ' , ' olddata ' , ' newdata ' ]
2011-10-17 06:15:40 +04:00
2012-02-07 10:27:18 +04:00
takes_optiongroups = {
" sambaopts " : options . SambaOptions ,
" versionopts " : options . VersionOptions ,
" credopts " : options . CredentialsOptions ,
}
2011-10-17 06:15:40 +04:00
def run ( self , server , zone , name , rtype , olddata , newdata ,
2018-07-30 09:15:34 +03:00
sambaopts = None , credopts = None , versionopts = None ) :
2011-10-17 06:15:40 +04:00
2021-06-20 05:52:48 +03:00
rtype = rtype . upper ( )
if rtype not in ( ' A ' , ' AAAA ' , ' PTR ' , ' CNAME ' , ' NS ' , ' MX ' , ' SOA ' , ' SRV ' , ' TXT ' ) :
2011-10-17 06:15:40 +04:00
raise CommandError ( ' Updating record of type %s is not supported ' % rtype )
2021-06-20 05:52:48 +03:00
try :
if rtype == ' A ' :
inet_pton ( AF_INET , newdata )
elif rtype == ' AAAA ' :
inet_pton ( AF_INET6 , newdata )
except OSError as e :
raise CommandError ( f " bad data for { rtype } : { e !r} " )
2012-02-14 06:41:45 +04:00
record_type = dns_type_flag ( rtype )
rec = data_to_dns_record ( record_type , newdata )
2011-10-17 06:15:40 +04:00
self . lp = sambaopts . get_loadparm ( )
self . creds = credopts . get_credentials ( self . lp )
2022-08-18 00:21:39 +03:00
dns_conn = DnsConnWrapper ( server , self . lp , self . creds )
2011-10-17 06:15:40 +04:00
2021-05-27 00:45:18 +03:00
try :
2022-08-18 00:21:39 +03:00
rec_match = dns_record_match ( dns_conn . dns_conn , server , zone ,
name , record_type , olddata )
2021-05-27 00:45:18 +03:00
except DNSParseError as e :
raise CommandError ( * e . args ) from None
2011-10-17 06:15:40 +04:00
if not rec_match :
2016-11-25 06:29:31 +03:00
raise CommandError ( ' Record or zone does not exist. ' )
2011-10-17 06:15:40 +04:00
# Copy properties from existing record to new record
rec . dwFlags = rec_match . dwFlags
rec . dwSerial = rec_match . dwSerial
rec . dwTtlSeconds = rec_match . dwTtlSeconds
rec . dwTimeStamp = rec_match . dwTimeStamp
add_rec_buf = dnsserver . DNS_RPC_RECORD_BUF ( )
add_rec_buf . rec = rec
del_rec_buf = dnsserver . DNS_RPC_RECORD_BUF ( )
del_rec_buf . rec = rec_match
2022-08-18 00:21:39 +03:00
messages = {
werror . WERR_DNS_ERROR_NAME_DOES_NOT_EXIST : (
f ' Zone { zone } does not exist; record could not be updated. ' ) ,
}
dns_conn . DnssrvUpdateRecord2 ( dnsserver . DNS_CLIENT_VERSION_LONGHORN ,
0 ,
server ,
zone ,
name ,
add_rec_buf ,
del_rec_buf ,
messages = messages )
2016-11-25 06:29:31 +03:00
2013-04-18 12:32:44 +04:00
self . outf . write ( ' Record updated successfully \n ' )
2011-10-17 06:15:40 +04:00
class cmd_delete_record ( Command ) :
2012-02-14 06:41:45 +04:00
""" Delete a DNS record
For each type data contents are as follows :
A ipv4_address_string
AAAA ipv6_address_string
PTR fqdn_string
CNAME fqdn_string
NS fqdn_string
MX " fqdn_string preference "
SRV " fqdn_string port priority weight "
2012-02-28 08:14:49 +04:00
TXT " ' string1 ' ' string2 ' ... "
2012-02-14 06:41:45 +04:00
"""
2011-10-17 06:15:40 +04:00
2012-02-28 08:14:49 +04:00
synopsis = ' % prog <server> <zone> <name> <A|AAAA|PTR|CNAME|NS|MX|SRV|TXT> <data> '
2011-10-17 06:15:40 +04:00
2018-07-30 09:17:02 +03:00
takes_args = [ ' server ' , ' zone ' , ' name ' , ' rtype ' , ' data ' ]
2011-10-17 06:15:40 +04:00
2012-02-07 10:27:18 +04:00
takes_optiongroups = {
" sambaopts " : options . SambaOptions ,
" versionopts " : options . VersionOptions ,
" credopts " : options . CredentialsOptions ,
}
2011-10-17 06:15:40 +04:00
def run ( self , server , zone , name , rtype , data , sambaopts = None , credopts = None , versionopts = None ) :
2018-07-30 09:19:05 +03:00
if rtype . upper ( ) not in ( ' A ' , ' AAAA ' , ' PTR ' , ' CNAME ' , ' NS ' , ' MX ' , ' SRV ' , ' TXT ' ) :
2011-10-17 06:15:40 +04:00
raise CommandError ( ' Deleting record of type %s is not supported ' % rtype )
2012-02-14 06:41:45 +04:00
record_type = dns_type_flag ( rtype )
2016-11-28 01:12:18 +03:00
rec = data_to_dns_record ( record_type , data )
2012-02-14 06:41:45 +04:00
2011-10-17 06:15:40 +04:00
self . lp = sambaopts . get_loadparm ( )
self . creds = credopts . get_credentials ( self . lp )
2022-08-12 07:40:03 +03:00
dns_conn = DnsConnWrapper ( server , self . lp , self . creds )
2011-10-17 06:15:40 +04:00
del_rec_buf = dnsserver . DNS_RPC_RECORD_BUF ( )
2016-11-28 01:12:18 +03:00
del_rec_buf . rec = rec
2011-10-17 06:15:40 +04:00
2022-08-12 08:17:16 +03:00
messages = {
werror . WERR_DNS_ERROR_NAME_DOES_NOT_EXIST : (
' Zone does not exist; record could not be deleted. '
f ' zone[ { zone } ] name[ { name } ' ) ,
werror . WERR_DNS_ERROR_RECORD_ALREADY_EXISTS : (
' Record already exists; record could not be deleted. '
f ' zone[ { zone } ] name[ { name } ] ' )
}
dns_conn . DnssrvUpdateRecord2 ( dnsserver . DNS_CLIENT_VERSION_LONGHORN ,
0 ,
server ,
zone ,
name ,
None ,
del_rec_buf ,
messages = messages )
2016-11-25 06:29:31 +03:00
2013-04-18 12:32:44 +04:00
self . outf . write ( ' Record deleted successfully \n ' )
2011-10-17 06:15:40 +04:00
2018-01-12 04:14:00 +03:00
class cmd_cleanup_record ( Command ) :
""" Cleanup DNS records for a DNS host.
example :
samba - tool dns cleanup dc1 dc1 . samdom . test . site - U USER % PASSWORD
2018-01-31 06:12:05 +03:00
NOTE : This command in many cases will only mark the ` dNSTombstoned ` attr
as ` TRUE ` on the DNS records . Querying will no longer return results but
there may still be some placeholder entries in the database .
2018-01-12 04:14:00 +03:00
"""
synopsis = ' % prog <server> <dnshostname> '
takes_args = [ ' server ' , ' dnshostname ' ]
takes_optiongroups = {
" sambaopts " : options . SambaOptions ,
" versionopts " : options . VersionOptions ,
" credopts " : options . CredentialsOptions ,
}
2018-04-19 07:43:50 +03:00
takes_options = [
Option ( " -v " , " --verbose " , help = " Be verbose " , action = " store_true " ) ,
Option ( " -q " , " --quiet " , help = " Be quiet " , action = " store_true " ) ,
]
2018-01-12 04:14:00 +03:00
def run ( self , server , dnshostname , sambaopts = None , credopts = None ,
versionopts = None , verbose = False , quiet = False ) :
lp = sambaopts . get_loadparm ( )
creds = credopts . get_credentials ( lp )
2018-08-21 03:45:15 +03:00
logger = self . get_logger ( verbose = verbose , quiet = quiet )
2018-01-12 04:14:00 +03:00
samdb = SamDB ( url = " ldap:// %s " % server ,
session_info = system_session ( ) ,
credentials = creds , lp = lp )
2018-01-31 01:52:34 +03:00
remove_dc . remove_dns_references ( samdb , logger , dnshostname ,
ignore_no_name = True )
2018-01-12 04:14:00 +03:00
2011-10-17 06:15:40 +04:00
class cmd_dns ( SuperCommand ) :
2012-10-08 14:32:58 +04:00
""" Domain Name Service (DNS) management. """
2011-10-17 06:15:40 +04:00
subcommands = { }
subcommands [ ' serverinfo ' ] = cmd_serverinfo ( )
2021-04-19 15:07:50 +03:00
subcommands [ ' zoneoptions ' ] = cmd_zoneoptions ( )
2011-10-17 06:15:40 +04:00
subcommands [ ' zoneinfo ' ] = cmd_zoneinfo ( )
subcommands [ ' zonelist ' ] = cmd_zonelist ( )
2011-12-20 05:06:47 +04:00
subcommands [ ' zonecreate ' ] = cmd_zonecreate ( )
subcommands [ ' zonedelete ' ] = cmd_zonedelete ( )
2011-10-17 06:15:40 +04:00
subcommands [ ' query ' ] = cmd_query ( )
subcommands [ ' roothints ' ] = cmd_roothints ( )
subcommands [ ' add ' ] = cmd_add_record ( )
subcommands [ ' update ' ] = cmd_update_record ( )
subcommands [ ' delete ' ] = cmd_delete_record ( )
2018-01-12 04:14:00 +03:00
subcommands [ ' cleanup ' ] = cmd_cleanup_record ( )