Browse Source

[2911] select initial request type based on config and SOA availability.

JINMEI Tatuya 12 years ago
parent
commit
2a2f47d375
3 changed files with 93 additions and 10 deletions
  1. 38 2
      src/bin/xfrin/tests/xfrin_test.py
  2. 37 8
      src/bin/xfrin/xfrin.py.in
  3. 18 0
      src/bin/xfrin/xfrin_messages.mes

+ 38 - 2
src/bin/xfrin/tests/xfrin_test.py

@@ -3205,6 +3205,13 @@ class TestXfrinProcess(unittest.TestCase):
         self.__published = []
         self.__published = []
         # How many connections were created.
         # How many connections were created.
         self.__created_connections = 0
         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):
     def __get_connection(self, *args):
         """
         """
@@ -3269,8 +3276,8 @@ class TestXfrinProcess(unittest.TestCase):
         self.__rets = rets
         self.__rets = rets
         published = rets[-1]
         published = rets[-1]
         xfrin.process_xfrin(self, XfrinRecorder(), Name("example.org."),
         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([], self.__rets)
         self.assertEqual(transfers, self.__transfers)
         self.assertEqual(transfers, self.__transfers)
         # Create a connection for each attempt
         # 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)
         self.assertTrue(self._send_cc_session.recv_called_correctly)
         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):
 class TestFormatting(unittest.TestCase):
     # If the formatting functions are moved to a more general library
     # If the formatting functions are moved to a more general library
     # (ticket #1379), these tests should be moved with them.
     # (ticket #1379), these tests should be moved with them.

+ 37 - 8
src/bin/xfrin/xfrin.py.in

@@ -31,6 +31,7 @@ from isc.config.ccsession import *
 from isc.statistics import Counters
 from isc.statistics import Counters
 from isc.notify import notify_out
 from isc.notify import notify_out
 import isc.util.process
 import isc.util.process
+from isc.util.address_formatter import AddressFormatter
 from isc.datasrc import DataSourceClient, ZoneFinder
 from isc.datasrc import DataSourceClient, ZoneFinder
 import isc.net.parse
 import isc.net.parse
 from isc.xfrin.diff import Diff
 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.
     """Retrieve the current SOA RR of the zone to be transferred.
 
 
     This function is essentially private to the module, but will also
     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
     It will be used for various purposes in subsequent xfr protocol
     processing.   It is validly possible that the zone is currently
     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).
     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
     # get the zone finder.  this must be SUCCESS (not even
     # PARTIALMATCH) because we are specifying the zone origin name.
     # PARTIALMATCH) because we are specifying the zone origin name.
     result, finder = datasrc_client.find_zone(zone_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 None
     return soa_rrset
     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,
 def __process_xfrin(server, zone_name, rrclass, db_file,
                     shutdown_event, master_addrinfo, check_soa, tsig_key,
                     shutdown_event, master_addrinfo, check_soa, tsig_key,
                     request_ixfr, conn_class):
                     request_ixfr, conn_class):
@@ -1148,9 +1178,12 @@ def __process_xfrin(server, zone_name, rrclass, db_file,
             datasrc_config = "{ \"database_file\": \"" + db_file + "\"}"
             datasrc_config = "{ \"database_file\": \"" + db_file + "\"}"
             datasrc_client = DataSourceClient(datasrc_type, datasrc_config)
             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
         # Create a TCP connection for the XFR session and perform the
         # operation.
         # 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
         # attempt. In the case of connected but failed IXFR, we set it to true
         # once again.
         # once again.
         retry = True
         retry = True
-        if request_ixfr == ZoneInfo.REQUEST_IXFR_DISABLED:
-            request_type = RRType.AXFR
-        else:
-            request_type = RRType.IXFR
         while retry:
         while retry:
             retry = False
             retry = False
             conn = conn_class(sock_map, zone_name, rrclass, datasrc_client,
             conn = conn_class(sock_map, zone_name, rrclass, datasrc_client,

+ 18 - 0
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
 There was an error importing the python DNS module pydnspp. The most
 likely cause is a PYTHONPATH problem.
 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
 % 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
 The zone was received, but it failed sanity validation. The previous version
 of zone (if any is available) will be used. Look for previous
 of zone (if any is available) will be used. Look for previous