Browse Source

[1371] supported the case where the requested IXFR serial is the latest one.

JINMEI Tatuya 13 years ago
parent
commit
9b1c64b7d1
2 changed files with 65 additions and 4 deletions
  1. 37 1
      src/bin/xfrout/tests/xfrout_test.py.in
  2. 28 3
      src/bin/xfrout/xfrout.py.in

+ 37 - 1
src/bin/xfrout/tests/xfrout_test.py.in

@@ -729,6 +729,15 @@ class TestXfroutSession(TestXfroutSessionBase):
         self.assertNotEqual(None, self.xfrsess._iterator)
         self.assertEqual(None, self.xfrsess._jnl_reader)
 
+        # Successful case, but the requested SOA serial is equal to that of
+        # the local SOA.  Both iterator and jnl_reader should be None,
+        # indicating that the response will contain just one SOA.
+        self.mdata = self.create_request_data(ixfr=SOA_CURRENT_VERSION)
+        self.assertEqual(self.xfrsess._xfrout_setup(
+                self.getmsg(), TEST_ZONE_NAME, TEST_RRCLASS), Rcode.NOERROR())
+        self.assertEqual(None, self.xfrsess._iterator)
+        self.assertEqual(None, self.xfrsess._jnl_reader)
+
         # The data source doesn't support journaling.  Should fallback to AXFR.
         zone_name = Name('nojournal.example.com')
         self.mdata = self.create_request_data(ixfr=IXFR_OK_VERSION,
@@ -851,7 +860,7 @@ class TestXfroutSession(TestXfroutSessionBase):
         self.assertEqual(0, len(self.sock.sendqueue))
 
     def test_reply_xfrout_query_ixfr(self):
-        # Creating an pure (incremental) IXFR response.  Intermediate SOA
+        # Creating a pure (incremental) IXFR response.  Intermediate SOA
         # RRs won't be skipped.
         self.xfrsess._soa = create_soa(SOA_CURRENT_VERSION)
         self.xfrsess._iterator = [create_soa(IXFR_OK_VERSION),
@@ -866,6 +875,20 @@ class TestXfroutSession(TestXfroutSessionBase):
         self.assertEqual(reply_msg.get_rr_count(Message.SECTION_ANSWER),
                          len(self.xfrsess._iterator) + 2)
 
+    def test_reply_xfrout_query_ixfr_soa_only(self):
+        # Creating an IXFR response that contains only one RR, which is the
+        # SOA of the current version.
+        self.xfrsess._soa = create_soa(SOA_CURRENT_VERSION)
+        self.xfrsess._iterator = None
+        self.xfrsess._jnl_reader = None
+        self.xfrsess._reply_xfrout_query(self.getmsg(), self.sock)
+        reply_msg = self.sock.read_msg(Message.PRESERVE_ORDER)
+        answer = reply_msg.get_section(Message.SECTION_ANSWER)
+        self.assertEqual(1, len(answer))
+        self.assertEqual(RRType.SOA(), answer[0].get_type())
+        self.assertEqual(SOA_CURRENT_VERSION,
+                         xfrout.get_soa_serial(answer[0].get_rdata()[0]))
+
 class TestXfroutSessionWithSQLite3(TestXfroutSessionBase):
     '''Tests for XFR-out sessions using an SQLite3 DB.
 
@@ -942,6 +965,19 @@ class TestXfroutSessionWithSQLite3(TestXfroutSessionBase):
         self.assertEqual(SOA_CURRENT_VERSION,
                          xfrout.get_soa_serial(soa.get_rdata()[0]))
 
+    def test_ixfr_soa_only(self):
+        # The requested SOA serial is the latest one.  The response should
+        # contain exactly one SOA of that serial.
+        self.xfrsess._request_data = \
+            self.create_request_data(ixfr=SOA_CURRENT_VERSION)
+        XfroutSession._handle(self.xfrsess)
+        response = self.sock.read_msg(Message.PRESERVE_ORDER);
+        answers = response.get_section(Message.SECTION_ANSWER)
+        self.assertEqual(1, len(answers))
+        self.assertEqual(RRType.SOA(), answers[0].get_type())
+        self.assertEqual(SOA_CURRENT_VERSION,
+                         xfrout.get_soa_serial(answers[0].get_rdata()[0]))
+
 class MyUnixSockServer(UnixSockServer):
     def __init__(self):
         self._shutdown_event = threading.Event()

+ 28 - 3
src/bin/xfrout/xfrout.py.in

@@ -399,9 +399,25 @@ class XfroutSession():
         rcode, self._soa = self._get_zone_soa(zone_name)
         if rcode != Rcode.NOERROR():
             return rcode
+
+        # RFC1995 says "If an IXFR query with the same or newer version
+        # number than that of the server is received, it is replied to with
+        # a single SOA record of the server's current version, just as
+        # in AXFR".  The claim about AXFR is incorrect, but other than that,
+        # we do as the RFC says.
+        # Note: until we complete #1278 we can only check equality of the
+        # two serials.  The "newer version" case would fall back to AXFR-style.
+        begin_serial = get_soa_serial(remote_soa.get_rdata()[0])
+        end_serial = get_soa_serial(self._soa.get_rdata()[0])
+        if begin_serial == end_serial:
+            # clear both iterator and jnl_reader to signal we won't do
+            # iteration in response generation
+            self._iterator = None
+            self._jnl_reader = None
+            # Log it.
+            return Rcode.NOERROR()
+
         try:
-            begin_serial = get_soa_serial(remote_soa.get_rdata()[0])
-            end_serial = get_soa_serial(self._soa.get_rdata()[0])
             code, self._jnl_reader = self._datasrc_client.get_journal_reader(
                 zone_name, begin_serial, end_serial)
         except isc.datasrc.NotImplemented as ex:
@@ -531,10 +547,18 @@ class XfroutSession():
         #TODO, there should be a better way to insert rrset.
         msg.make_response()
         msg.set_header_flag(Message.HEADERFLAG_AA)
-        msg.add_rrset(Message.SECTION_ANSWER, self._soa)
 
+        # If the iterator is None, we are responding to IXFR with a single
+        # SOA RR.
+        if self._iterator is None:
+            self._send_message_with_last_soa(msg, sock_fd, self._soa, 0)
+            return
+
+        # Add the beginning SOA
+        msg.add_rrset(Message.SECTION_ANSWER, self._soa)
         message_upper_len = get_rrset_len(self._soa) + self._tsig_len
 
+        # Add the rest of the zone/diff contets
         for rrset in self._iterator:
             # Check if xfrout is shutdown
             if  self._server._shutdown_event.is_set():
@@ -564,6 +588,7 @@ class XfroutSession():
             # Reserve tsig space for signed packet
             message_upper_len = rrset_len + self._tsig_len
 
+        # Add and send the trailing SOA
         self._send_message_with_last_soa(msg, sock_fd, self._soa,
                                          message_upper_len)