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 = []
         # 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.

+ 37 - 8
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,

+ 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
 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