self.__published = []
# How many connections were created.
self.__created_connections = 0
+ # prepare for possible replacement
+ self.__orig_get_zone_soa = xfrin._get_zone_soa
+ xfrin._get_zone_soa = lambda x, y, z: begin_soa_rdata
+
+ def tearDown(self):
+ # restore original value
+ xfrin._get_zone_soa = self.__orig_get_zone_soa
def __get_connection(self, *args):
"""
self.__rets = rets
published = rets[-1]
xfrin.process_xfrin(self, XfrinRecorder(), Name("example.org."),
- RRClass.IN, None, None, None, True, None,
- request_ixfr, self.__get_connection)
+ RRClass.IN, None, None, TEST_MASTER_IPV4_ADDRINFO,
+ True, None, request_ixfr, self.__get_connection)
self.assertEqual([], self.__rets)
self.assertEqual(transfers, self.__transfers)
# Create a connection for each attempt
self.assertTrue(self._send_cc_session.recv_called)
self.assertTrue(self._send_cc_session.recv_called_correctly)
+ def test_initial_request_type(self):
+ """Check initial xfr reuqest type (AXFR or IXFR).
+
+ Varying the policy of use of IXFR and availability of current
+ zone SOA. We are only interested in the initial request type,
+ so won't check the xfr results.
+
+ """
+ for soa in [begin_soa_rdata, None]:
+ for request_ixfr in [ZoneInfo.REQUEST_IXFR_FIRST,
+ ZoneInfo.REQUEST_IXFR_ONLY,
+ ZoneInfo.REQUEST_IXFR_DISABLED]:
+ # set up our dummy _get_zone_soa()
+ xfrin._get_zone_soa = lambda x, y, z: soa
+
+ # Clear all counters
+ self.__transfers = []
+ self.__published = []
+ self.__created_connections = 0
+
+ # Determine the expected type
+ expected_type = RRType.IXFR
+ if (soa is None or
+ request_ixfr == ZoneInfo.REQUEST_IXFR_DISABLED):
+ expected_type = RRType.AXFR
+
+ # perform the test
+ self.__do_test([XFRIN_OK], [expected_type], request_ixfr)
+
class TestFormatting(unittest.TestCase):
# If the formatting functions are moved to a more general library
# (ticket #1379), these tests should be moved with them.
from isc.statistics import Counters
from isc.notify import notify_out
import isc.util.process
+from isc.util.address_formatter import AddressFormatter
from isc.datasrc import DataSourceClient, ZoneFinder
import isc.net.parse
from isc.xfrin.diff import Diff
"""Retrieve the current SOA RR of the zone to be transferred.
This function is essentially private to the module, but will also
- be called from tests; no one else should use this function directly.
+ be called (or tweaked) from tests; no one else should use this
+ function directly.
It will be used for various purposes in subsequent xfr protocol
processing. It is validly possible that the zone is currently
We'll deprecate this API in a near future, too).
"""
+ # datasrc_client should never be None in production case (only tests could
+ # specify None)
+ if datasrc_client is None:
+ return None
+
# get the zone finder. this must be SUCCESS (not even
# PARTIALMATCH) because we are specifying the zone origin name.
result, finder = datasrc_client.find_zone(zone_name)
return None
return soa_rrset
+def __get_initial_xfr_type(zone_soa, request_ixfr, zname, zclass, master_addr):
+ """Determine the initial xfr request type.
+
+ This is a dedicated subroutine of __process_xfrin.
+ """
+ if zone_soa is None:
+ # This is a kind of special case, so we log it at info level.
+ logger.info(XFRIN_INITIAL_AXFR, format_zone_str(zname, zclass),
+ AddressFormatter(master_addr))
+ return RRType.AXFR
+ if request_ixfr == ZoneInfo.REQUEST_IXFR_DISABLED:
+ logger.debug(DBG_XFRIN_TRACE, XFRIN_INITIAL_IXFR_DISABLED,
+ format_zone_str(zname, zclass),
+ AddressFormatter(master_addr))
+ return RRType.AXFR
+
+ assert(request_ixfr == ZoneInfo.REQUEST_IXFR_FIRST or
+ request_ixfr == ZoneInfo.REQUEST_IXFR_ONLY)
+ logger.debug(DBG_XFRIN_TRACE, XFRIN_INITIAL_IXFR,
+ format_zone_str(zname, zclass),
+ AddressFormatter(master_addr))
+ return RRType.IXFR
+
def __process_xfrin(server, zone_name, rrclass, db_file,
shutdown_event, master_addrinfo, check_soa, tsig_key,
request_ixfr, conn_class):
datasrc_config = "{ \"database_file\": \"" + db_file + "\"}"
datasrc_client = DataSourceClient(datasrc_type, datasrc_config)
- zone_soa = None
- if datasrc_client is not None:
- zone_soa = _get_zone_soa(datasrc_client, zone_name, rrclass)
+ # Get the current zone SOA (if available) and determine the initial
+ # reuqest type: AXFR or IXFR.
+ zone_soa = _get_zone_soa(datasrc_client, zone_name, rrclass)
+ request_type = __get_initial_xfr_type(zone_soa, request_ixfr,
+ zone_name, rrclass,
+ master_addrinfo[2])
# Create a TCP connection for the XFR session and perform the
# operation.
# attempt. In the case of connected but failed IXFR, we set it to true
# once again.
retry = True
- if request_ixfr == ZoneInfo.REQUEST_IXFR_DISABLED:
- request_type = RRType.AXFR
- else:
- request_type = RRType.IXFR
while retry:
retry = False
conn = conn_class(sock_map, zone_name, rrclass, datasrc_client,
There was an error importing the python DNS module pydnspp. The most
likely cause is a PYTHONPATH problem.
+% XFRIN_INITIAL_AXFR no SOA available for %1 yet, requesting AXFR of initial version from %2
+On starting the zone transfer, it's detected that there is no SOA
+record available for the zone. This is always the case for the very
+first transfer or if the administrator has removed the locally copied
+data by hand for some reason. In this case trying IXFR does not make
+sense for the obvious reason, so AXFR will be used from the beginning,
+regardless of the request_ixfr configuration (even if "only" is
+specified).
+
+% XFRIN_INITIAL_IXFR requesting IXFR for %1 from %2
+IXFR will be used for the initial request type for the specified zone
+transfer. It will fall back to AXFR if the initial request fails
+(and unless specified not to do so by configuration).
+
+% XFRIN_INITIAL_IXFR_DISABLED IXFR disabled for %1, requesting AXFR from %2
+The use of IXFR is disabled by configuration for the specified zone,
+so only AXFR will be tried.
+
% XFRIN_INVALID_ZONE_DATA zone %1 received from %2 is broken and unusable
The zone was received, but it failed sanity validation. The previous version
of zone (if any is available) will be used. Look for previous