From: JINMEI Tatuya Date: Thu, 23 May 2013 00:22:08 +0000 (-0700) Subject: [2911] select initial request type based on config and SOA availability. X-Git-Tag: bind10-1.2.0beta1-release~418^2~9^2~1^2~13 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=2a2f47d37519d5ec4cd310899d7df482145f5feb;p=thirdparty%2Fkea.git [2911] select initial request type based on config and SOA availability. --- diff --git a/src/bin/xfrin/tests/xfrin_test.py b/src/bin/xfrin/tests/xfrin_test.py index 8c39995132..5e1c12f233 100644 --- a/src/bin/xfrin/tests/xfrin_test.py +++ b/src/bin/xfrin/tests/xfrin_test.py @@ -3205,6 +3205,13 @@ class TestXfrinProcess(unittest.TestCase): 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): """ @@ -3269,8 +3276,8 @@ class TestXfrinProcess(unittest.TestCase): 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 @@ -3333,6 +3340,35 @@ class TestXfrinProcess(unittest.TestCase): 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. diff --git a/src/bin/xfrin/xfrin.py.in b/src/bin/xfrin/xfrin.py.in index b46f507cb6..af40108331 100755 --- a/src/bin/xfrin/xfrin.py.in +++ b/src/bin/xfrin/xfrin.py.in @@ -31,6 +31,7 @@ from isc.config.ccsession import * 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 @@ -1077,7 +1078,8 @@ def _get_zone_soa(datasrc_client, zone_name, zone_class): """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 @@ -1100,6 +1102,11 @@ def _get_zone_soa(datasrc_client, zone_name, zone_class): 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) @@ -1125,6 +1132,29 @@ def _get_zone_soa(datasrc_client, zone_name, zone_class): 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): @@ -1148,9 +1178,12 @@ def __process_xfrin(server, zone_name, rrclass, db_file, 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. @@ -1162,10 +1195,6 @@ def __process_xfrin(server, zone_name, rrclass, db_file, # 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, diff --git a/src/bin/xfrin/xfrin_messages.mes b/src/bin/xfrin/xfrin_messages.mes index eeddee9c07..f659f50a35 100644 --- a/src/bin/xfrin/xfrin_messages.mes +++ b/src/bin/xfrin/xfrin_messages.mes @@ -80,6 +80,24 @@ is not equal to the requested SOA serial. 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