Browse Source

Merge branch 'master' of ssh://git.bind10.isc.org//var/bind10/git/bind10

Jeremy C. Reed 13 years ago
parent
commit
911cb21ff7
4 changed files with 65 additions and 44 deletions
  1. 2 11
      ChangeLog
  2. 0 7
      doc/guide/bind10-guide.xml
  3. 23 8
      src/bin/xfrin/tests/xfrin_test.py
  4. 40 18
      src/bin/xfrin/xfrin.py.in

+ 2 - 11
ChangeLog

@@ -1,11 +1,11 @@
-297.	[build]		jreed
+296.	[build]		jreed
 	Do not install the unittest libraries. At this time, they
 	are not useful without source tree (and they may or may
 	not have googletest support). Also, convert several makefiles
 	to build tests at "check" time and not build time.
 	(Trac #1091, git 2adf4a90ad79754d52126e7988769580d20501c3)
 
-296.	[bug]		jinmei
+295.	[bug]		jinmei
 	__init__.py for isc.dns was installed in the wrong directory,
 	which would now make xfrin fail to start.  It was also bad
 	in that it replaced any existing __init__.py in th public
@@ -14,15 +14,6 @@
 	case it should be removed.
 	(Trac #1285, git af3b17472694f58b3d6a56d0baf64601b0f6a6a1)
 
-295.	[func]*		jinmei
-	b10-xfrin: the AXFR implementation is unified with IXFR, and
-	handles corner cases more carefully.  Note: As a result of this
-	change, xfrin does not create a new (SQLite3) zone in a fresh DB
-	file upon receiving AXFR any more.  Initial zone content must be
-	prepared by hand (e.g. with b10-loadzone) until a more generic
-	tool for zone management is provided.
-	(Trac #1209, git 5ca7b409bccc815cee58c804236504fda1c1c147)
-
 294.	[func]		jelte, jinmei, vorner
 	b10-xfrin now supports incoming IXFR.  See BIND 10 Guide for
 	how to configure it and operational notes.

+ 0 - 7
doc/guide/bind10-guide.xml

@@ -1276,13 +1276,6 @@ TODO
      In the current development release of BIND 10, incoming zone
      transfers are only available for SQLite3-based data sources,
      that is, they don't work for an in-memory data source.
-     Furthermore, the corresponding SQLite3 database must be
-     configured with a list of zone names by hand.  One possible way
-     to do this is to use the <command>b10-loadzone</command> command
-     to load dummy zone content of the zone for which the secondary
-     service is provided (and then force transfer using AXFR from the primary
-     server).  In future versions we will provide more convenient way
-     to set up the secondary.
     </simpara></note>
 
     <para>

+ 23 - 8
src/bin/xfrin/tests/xfrin_test.py

@@ -1483,11 +1483,14 @@ class TestXFRSessionWithSQLite3(TestXfrinConnection):
     def setUp(self):
         self.sqlite3db_src = TESTDATA_SRCDIR + '/example.com.sqlite3'
         self.sqlite3db_obj = TESTDATA_OBJDIR + '/example.com.sqlite3.copy'
+        self.empty_sqlite3db_obj = TESTDATA_OBJDIR + '/empty.sqlite3'
         self.sqlite3db_cfg = "{ \"database_file\": \"" +\
                              self.sqlite3db_obj + "\"}"
         super().setUp()
         if os.path.exists(self.sqlite3db_obj):
             os.unlink(self.sqlite3db_obj)
+        if os.path.exists(self.empty_sqlite3db_obj):
+            os.unlink(self.empty_sqlite3db_obj)
         shutil.copyfile(self.sqlite3db_src, self.sqlite3db_obj)
         self.conn._datasrc_client = DataSourceClient("sqlite3",
                                                      self.sqlite3db_cfg)
@@ -1495,6 +1498,8 @@ class TestXFRSessionWithSQLite3(TestXfrinConnection):
     def tearDown(self):
         if os.path.exists(self.sqlite3db_obj):
             os.unlink(self.sqlite3db_obj)
+        if os.path.exists(self.empty_sqlite3db_obj):
+            os.unlink(self.empty_sqlite3db_obj)
 
     def get_zone_serial(self):
         result, finder = self.conn._datasrc_client.find_zone(TEST_ZONE_NAME)
@@ -1612,20 +1617,30 @@ class TestXFRSessionWithSQLite3(TestXfrinConnection):
         self.axfr_failure_check(RRType.AXFR())
 
     def test_do_axfrin_nozone_sqlite3(self):
+        '''AXFR test with an empty SQLite3 DB file, thus no target zone there.
+
+        For now, we provide backward compatible behavior: xfrin will create
+        the zone (after even setting up the entire schema) in the zone.
+        Note: a future version of this test will make it fail.
+
+        '''
+        self.conn._db_file = self.empty_sqlite3db_obj
+        self.conn._datasrc_client = DataSourceClient(
+            "sqlite3",
+            "{ \"database_file\": \"" + self.empty_sqlite3db_obj + "\"}")
         def create_response():
-            # Within this test, owner names of the question/RRs don't matter,
-            # so we use pre-defined names (which are "out of zone") for
-            # simplicity.
             self.conn.reply_data = self.conn.create_response_data(
                 questions=[Question(TEST_ZONE_NAME, TEST_RRCLASS,
                                     RRType.AXFR())],
-                answers=[soa_rrset, self._create_ns(), soa_rrset, soa_rrset])
+                answers=[soa_rrset, self._create_ns(), soa_rrset])
         self.conn.response_generator = create_response
-        self.conn._zone_name = Name('nosuchzone.example')
-        self.assertEqual(XFRIN_FAIL, self.conn.do_xfrin(False, RRType.AXFR()))
-        # This should fail in the FirstData state
-        self.assertEqual(type(XfrinFirstData()),
+        self.conn._zone_name = Name('example.com')
+        self.assertEqual(XFRIN_OK, self.conn.do_xfrin(False, RRType.AXFR()))
+        self.assertEqual(type(XfrinAXFREnd()),
                          type(self.conn.get_xfrstate()))
+        self.assertEqual(1234, self.get_zone_serial())
+        self.assertFalse(self.record_exist(Name('dns01.example.com'),
+                                           RRType.A()))
 
 class TestXfrinRecorder(unittest.TestCase):
     def setUp(self):

+ 40 - 18
src/bin/xfrin/xfrin.py.in

@@ -504,6 +504,25 @@ class XfrinConnection(asyncore.dispatcher):
             logger.error(XFRIN_CONNECT_MASTER, self._master_address, str(e))
             return False
 
+    def _get_zone_soa(self):
+        result, finder = self._datasrc_client.find_zone(self._zone_name)
+        if result != DataSourceClient.SUCCESS:
+            raise XfrinException('Zone not found in the given data ' +
+                                 'source: ' + self.zone_str())
+        result, soa_rrset = finder.find(self._zone_name, RRType.SOA(),
+                                        None, ZoneFinder.FIND_DEFAULT)
+        if result != ZoneFinder.SUCCESS:
+            raise XfrinException('SOA RR not found in zone: ' +
+                                 self.zone_str())
+        # Especially for database-based zones, a working zone may be in
+        # a broken state where it has more than one SOA RR.  We proactively
+        # check the condition and abort the xfr attempt if we identify it.
+        if soa_rrset.get_rdata_count() != 1:
+            raise XfrinException('Invalid number of SOA RRs for ' +
+                                 self.zone_str() + ': ' +
+                                 str(soa_rrset.get_rdata_count()))
+        return soa_rrset
+
     def _create_query(self, query_type):
         '''Create an XFR-related query message.
 
@@ -528,24 +547,24 @@ class XfrinConnection(asyncore.dispatcher):
         if query_type == RRType.IXFR():
             # get the zone finder.  this must be SUCCESS (not even
             # PARTIALMATCH) because we are specifying the zone origin name.
-            result, finder = self._datasrc_client.find_zone(self._zone_name)
-            if result != DataSourceClient.SUCCESS:
-                raise XfrinException('Zone not found in the given data ' +
-                                     'source: ' + self.zone_str())
-            result, soa_rrset = finder.find(self._zone_name, RRType.SOA(),
-                                            None, ZoneFinder.FIND_DEFAULT)
-            if result != ZoneFinder.SUCCESS:
-                raise XfrinException('SOA RR not found in zone: ' +
-                                     self.zone_str())
-            # Especially for database-based zones, a working zone may be in
-            # a broken state where it has more than one SOA RR.  We proactively
-            # check the condition and abort the xfr attempt if we identify it.
-            if soa_rrset.get_rdata_count() != 1:
-                raise XfrinException('Invalid number of SOA RRs for ' +
-                                     self.zone_str() + ': ' +
-                                     str(soa_rrset.get_rdata_count()))
-            msg.add_rrset(Message.SECTION_AUTHORITY, soa_rrset)
-            self._request_serial = get_soa_serial(soa_rrset.get_rdata()[0])
+            zone_soa_rr = self._get_zone_soa()
+            msg.add_rrset(Message.SECTION_AUTHORITY, zone_soa_rr)
+            self._request_serial = get_soa_serial(zone_soa_rr.get_rdata()[0])
+        else:
+            # For AXFR, we temporarily provide backward compatible behavior
+            # where xfrin is responsible for creating zone in the corresponding
+            # DB table.  Note that the code below uses the old data source
+            # API and assumes SQLite3 in an ugly manner.  We'll have to
+            # develop a better way of managing zones in a generic way and
+            # eliminate the code like the one here.
+            try:
+                self._get_zone_soa()
+            except XfrinException:
+                def empty_rr_generator():
+                    return []
+                isc.datasrc.sqlite3_ds.load(self._db_file,
+                                            self._zone_name.to_text(),
+                                            empty_rr_generator)
         return msg
 
     def _send_data(self, data):
@@ -803,6 +822,9 @@ def process_xfrin(server, xfrin_recorder, zone_name, rrclass, db_file,
     sock_map = {}
     conn = XfrinConnection(sock_map, zone_name, rrclass, datasrc_client,
                            shutdown_event, master_addrinfo, tsig_key)
+    # XXX: We still need _db_file for temporary workaround in _create_query().
+    # This should be removed when we eliminate the need for the workaround.
+    conn._db_file = db_file
     ret = XFRIN_FAIL
     if conn.connect_to_master():
         ret = conn.do_xfrin(check_soa, request_type)