Browse Source

[1372] some small updates for IXFR support. commiting it to share.

JINMEI Tatuya 13 years ago
parent
commit
4a68215905
2 changed files with 51 additions and 13 deletions
  1. 27 2
      src/bin/xfrout/tests/xfrout_test.py.in
  2. 24 11
      src/bin/xfrout/xfrout.py.in

+ 27 - 2
src/bin/xfrout/tests/xfrout_test.py.in

@@ -159,15 +159,32 @@ class TestXfroutSessionBase(unittest.TestCase):
     def message_has_tsig(self, msg):
         return msg.get_tsig_record() is not None
 
-    def create_request_data(self, with_question=True, with_tsig=False):
+    def create_request_data(self, with_question=True, with_tsig=False,
+                            ixfr=None):
+        '''Create a commonly used XFR request data.
+
+        By default the request type is AXFR; if 'ixfr' is an integer,
+        the request type will be IXFR and an SOA with the serial being
+        the value of the parameter will be included in the authority
+        section.
+
+        '''
         msg = Message(Message.RENDER)
         query_id = 0x1035
         msg.set_qid(query_id)
         msg.set_opcode(Opcode.QUERY())
         msg.set_rcode(Rcode.NOERROR())
+        req_type = RRType.AXFR() if ixfr is None else RRType.IXFR()
         if with_question:
             msg.add_question(Question(Name("example.com"), RRClass.IN(),
-                                      RRType.AXFR()))
+                                      req_type))
+        if req_type == RRType.IXFR():
+            soa = RRset(Name('example.com'), RRClass.IN(), RRType.SOA(),
+                        RRTTL(0))
+            # In the RDATA only the serial matters.
+            soa.add_rdata(Rdata(RRType.SOA(), RRClass.IN(),
+                                'm r ' + str(ixfr) + ' 1 1 1 1'))
+            msg.add_rrset(Message.SECTION_AUTHORITY, soa)
 
         renderer = MessageRenderer()
         if with_tsig:
@@ -241,9 +258,17 @@ class TestXfroutSession(TestXfroutSessionBase):
         XfroutSession._handle(self.xfrsess)
 
     def test_parse_query_message(self):
+        # Valid AXFR
         [get_rcode, get_msg] = self.xfrsess._parse_query_message(self.mdata)
+        self.assertEqual(RRType.AXFR(), self.xfrsess._request_type)
         self.assertEqual(get_rcode.to_text(), "NOERROR")
 
+        # Valid IXFR
+        request_data = self.create_request_data(ixfr=2011111801)
+        rcode, msg = self.xfrsess._parse_query_message(request_data)
+        self.assertEqual(RRType.IXFR(), self.xfrsess._request_type)
+        self.assertEqual(Rcode.NOERROR(), rcode)
+
         # Broken request: no question
         request_data = self.create_request_data(with_question=False)
         rcode, msg = self.xfrsess._parse_query_message(request_data)

+ 24 - 11
src/bin/xfrout/xfrout.py.in

@@ -143,7 +143,8 @@ class XfroutSession():
         self._tsig_ctx = None
         self._tsig_len = 0
         self._remote = remote
-        self._request_type = 'AXFR' # could be IXFR when we support it
+        self._request_type = None
+        self._request_typestr = None
         self._acl = default_acl
         self._zone_config = zone_config
         self.ClientClass = client_class # parameterize this for testing
@@ -222,20 +223,32 @@ class XfroutSession():
         # we check it by ourselves.
         if msg.get_rr_count(Message.SECTION_QUESTION) != 1:
             return Rcode.FORMERR(), msg
+        question = msg.get_question()[0]
+
+        # Identify the request type
+        self._request_type = question.get_type()
+        if self._request_type == RRType.AXFR():
+            self._request_typestr = 'AXFR'
+        elif self._request_type == RRType.IXFR():
+            self._request_typestr = 'IXFR'
+        else:
+            # Likewise, this should be impossible.
+            raise Runtimeerror('Unexpected XFR type: ' + \
+                                   str(self._request_type))
 
         # ACL checks
-        zone_name = msg.get_question()[0].get_name()
-        zone_class = msg.get_question()[0].get_class()
+        zone_name = question.get_name()
+        zone_class = question.get_class()
         acl = self._get_transfer_acl(zone_name, zone_class)
         acl_result = acl.execute(
             isc.acl.dns.RequestContext(self._remote[2], msg.get_tsig_record()))
         if acl_result == DROP:
-            logger.info(XFROUT_QUERY_DROPPED, self._request_type,
+            logger.info(XFROUT_QUERY_DROPPED, self._request_typestr,
                         format_addrinfo(self._remote),
                         format_zone_str(zone_name, zone_class))
             return None, None
         elif acl_result == REJECT:
-            logger.info(XFROUT_QUERY_REJECTED, self._request_type,
+            logger.info(XFROUT_QUERY_REJECTED, self._request_typestr,
                         format_addrinfo(self._remote),
                         format_zone_str(zone_name, zone_class))
             return Rcode.REFUSED(), msg
@@ -348,7 +361,7 @@ class XfroutSession():
             return self._reply_query_with_error_rcode(msg, sock_fd,
                                                       Rcode.FORMERR())
         elif not quota_ok:
-            logger.warn(XFROUT_QUERY_QUOTA_EXCCEEDED, self._request_type,
+            logger.warn(XFROUT_QUERY_QUOTA_EXCCEEDED, self._request_typestr,
                         format_addrinfo(self._remote),
                         self._server._max_transfers_out)
             return self._reply_query_with_error_rcode(msg, sock_fd,
@@ -363,23 +376,23 @@ class XfroutSession():
         try:
             rcode_ = self._check_xfrout_available(zone_name)
         except Exception as ex:
-            logger.error(XFROUT_XFR_TRANSFER_CHECK_ERROR, self._request_type,
+            logger.error(XFROUT_XFR_TRANSFER_CHECK_ERROR, self._request_typestr,
                          format_addrinfo(self._remote), zone_str, ex)
             rcode_ = Rcode.SERVFAIL()
         if rcode_ != Rcode.NOERROR():
-            logger.info(XFROUT_AXFR_TRANSFER_FAILED, self._request_type,
+            logger.info(XFROUT_AXFR_TRANSFER_FAILED, self._request_typestr,
                         format_addrinfo(self._remote), zone_str, rcode_)
             return self._reply_query_with_error_rcode(msg, sock_fd, rcode_)
 
         try:
-            logger.info(XFROUT_AXFR_TRANSFER_STARTED, self._request_type,
+            logger.info(XFROUT_AXFR_TRANSFER_STARTED, self._request_typestr,
                         format_addrinfo(self._remote), zone_str)
             self._reply_xfrout_query(msg, sock_fd)
         except Exception as err:
-            logger.error(XFROUT_AXFR_TRANSFER_ERROR, self._request_type,
+            logger.error(XFROUT_AXFR_TRANSFER_ERROR, self._request_typestr,
                     format_addrinfo(self._remote), zone_str, err)
             pass
-        logger.info(XFROUT_AXFR_TRANSFER_DONE, self._request_type,
+        logger.info(XFROUT_AXFR_TRANSFER_DONE, self._request_typestr,
                     format_addrinfo(self._remote), zone_str)
 
     def _clear_message(self, msg):