BIND 10 trac1261, updated. fcd39b6e84665a033d7ee4c06bd904e2b416c53a [1261] overall comment update

BIND 10 source code commits bind10-changes at lists.isc.org
Wed Oct 5 07:39:12 UTC 2011


The branch, trac1261 has been updated
       via  fcd39b6e84665a033d7ee4c06bd904e2b416c53a (commit)
       via  ce00497088209db82fbbabb80381acf92039763c (commit)
       via  0fbdaf01b0fc3d7031b51d542b91f6f758f033fa (commit)
       via  b3a1ea108d3df58dcd2d247fdc87b3d1fbd953cf (commit)
       via  2de8b71f8c0e7d02e25aa7ec6fa13f9933c8b534 (commit)
       via  4edd9c38112db5161f46533ffb3886c85880ee03 (commit)
       via  bff7aa9429b7e0a9f26f69dd24c8aa7efc64ffc6 (commit)
       via  1e0d70a994d9cf9cabe10d1205c40b74af2a2bc4 (commit)
       via  738afedababcfc874fe107d9bc408d69d213813e (commit)
       via  8ed59723a5ae90dedcbf741254b65f88a4c98ca1 (commit)
       via  3f2d29d0dc92606fac3ba306c34a32a0bec8159e (commit)
       via  3bfaa404624697f5e2f08076c78f94a8438e851c (commit)
       via  f85f868171956abcc1996235a26a276da2ca6209 (commit)
       via  1982c235382043d87737ec24779d10da216101a6 (commit)
       via  f6c675c19790d3715445a7877cc8d1d193f17071 (commit)
       via  7225bbf8e6e3c892159124e7795f7396b5764bb8 (commit)
       via  2056251f56e4c5e3ff785b924061fecfe1ac21e4 (commit)
       via  42968abbd4edf489d4d667089033d11e4045f463 (commit)
      from  a01b47d66272166135c20bf15a958bed023ff009 (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
commit fcd39b6e84665a033d7ee4c06bd904e2b416c53a
Author: JINMEI Tatuya <jinmei at isc.org>
Date:   Wed Oct 5 00:37:29 2011 -0700

    [1261] overall comment update

commit ce00497088209db82fbbabb80381acf92039763c
Author: JINMEI Tatuya <jinmei at isc.org>
Date:   Tue Oct 4 23:52:22 2011 -0700

    [1261] editorial cleanup: no need for escaping '+' at EOL

commit 0fbdaf01b0fc3d7031b51d542b91f6f758f033fa
Author: JINMEI Tatuya <jinmei at isc.org>
Date:   Tue Oct 4 23:50:39 2011 -0700

    [1261] log message update

commit b3a1ea108d3df58dcd2d247fdc87b3d1fbd953cf
Author: JINMEI Tatuya <jinmei at isc.org>
Date:   Tue Oct 4 23:31:25 2011 -0700

    [1261] corrected the type of zone_name arg for publish_xfrin_news (it's
    now a Name object, not a string)

commit 2de8b71f8c0e7d02e25aa7ec6fa13f9933c8b534
Author: JINMEI Tatuya <jinmei at isc.org>
Date:   Tue Oct 4 23:17:41 2011 -0700

    [1261] install NULL instead of "" for an empty column for backward
    compatibility.  We need this at least until we completely deprecate
    the old API.

commit 4edd9c38112db5161f46533ffb3886c85880ee03
Author: JINMEI Tatuya <jinmei at isc.org>
Date:   Tue Oct 4 22:26:35 2011 -0700

    [1261] use IXFR or AXFR depending on the configuration

commit bff7aa9429b7e0a9f26f69dd24c8aa7efc64ffc6
Author: JINMEI Tatuya <jinmei at isc.org>
Date:   Tue Oct 4 21:17:10 2011 -0700

    [1261] refactoring: pass request type (IXFR or AXFR) from the command handler
    to do_xfrin().

commit 1e0d70a994d9cf9cabe10d1205c40b74af2a2bc4
Author: JINMEI Tatuya <jinmei at isc.org>
Date:   Tue Oct 4 21:16:37 2011 -0700

    [1261] more adjustment for distcheck

commit 738afedababcfc874fe107d9bc408d69d213813e
Author: JINMEI Tatuya <jinmei at isc.org>
Date:   Tue Oct 4 21:15:53 2011 -0700

    [1261] some more adjustment for distcheck

commit 8ed59723a5ae90dedcbf741254b65f88a4c98ca1
Author: JINMEI Tatuya <jinmei at isc.org>
Date:   Tue Oct 4 20:54:02 2011 -0700

    [1261] corrected env var for the build dir.

commit 3f2d29d0dc92606fac3ba306c34a32a0bec8159e
Author: JINMEI Tatuya <jinmei at isc.org>
Date:   Tue Oct 4 20:51:50 2011 -0700

    [1261] missing EXTRA_DIST, and corrected data dir for the writable DB.

commit 3bfaa404624697f5e2f08076c78f94a8438e851c
Author: JINMEI Tatuya <jinmei at isc.org>
Date:   Tue Oct 4 20:39:10 2011 -0700

    [1261] one more missing update for the Diff class about remove->delete

commit f85f868171956abcc1996235a26a276da2ca6209
Author: JINMEI Tatuya <jinmei at isc.org>
Date:   Tue Oct 4 20:34:30 2011 -0700

    [1261] added another sqlite3-based test.  also make sure any remaining diff
    object is cleared as soon as possible after the xfrin session.

commit 1982c235382043d87737ec24779d10da216101a6
Author: JINMEI Tatuya <jinmei at isc.org>
Date:   Tue Oct 4 20:18:47 2011 -0700

    [1261] added a test using a workable sqlite3 DB file and its data source
    client to see it really works for an operational environment (and indeed
    I found a bug in the Diff class).  To make it possible, also updated
    how to set the data source client for XfrinConnection.

commit f6c675c19790d3715445a7877cc8d1d193f17071
Author: JINMEI Tatuya <jinmei at isc.org>
Date:   Tue Oct 4 20:16:54 2011 -0700

    [1261] fixed the interface mismatch: actual DataSource API expectes
    'delete_rrset', not 'remove_rrset'.  To make things consistent,
    also removed the corresponding Diff methods from 'remove_data' to
    'delete_data'.

commit 7225bbf8e6e3c892159124e7795f7396b5764bb8
Author: JINMEI Tatuya <jinmei at isc.org>
Date:   Tue Oct 4 14:49:46 2011 -0700

    [1261] comment cleanup: this TODO is mostly a non issue (our data source API
    internally takes care of it)

commit 2056251f56e4c5e3ff785b924061fecfe1ac21e4
Author: JINMEI Tatuya <jinmei at isc.org>
Date:   Tue Oct 4 14:47:34 2011 -0700

    [1261] added some error case tests.  updated do_xfrin() so that it will
    handle exceptions more correctly.  in particular it should have caught
    various other exceptions from other modules (e.g. one from pydnspp/isc.dns)
    without incorrectly assuming it uses Boost.Python (we don't use it any more).
    This change fixes another problem in an existing test, so I also fixed
    that test case, too.

commit 42968abbd4edf489d4d667089033d11e4045f463
Author: JINMEI Tatuya <jinmei at isc.org>
Date:   Tue Oct 4 13:51:23 2011 -0700

    [1261] minor editorial cleanups

-----------------------------------------------------------------------

Summary of changes:
 configure.ac                                       |    1 +
 src/bin/xfrin/tests/Makefile.am                    |    4 +
 src/bin/xfrin/tests/testdata/Makefile.am           |    2 +
 src/bin/xfrin/tests/testdata/example.com           |   17 ++
 .../xfrin/tests/testdata/example.com.sqlite3}      |  Bin 11264 -> 11264 bytes
 src/bin/xfrin/tests/xfrin_test.py                  |  253 ++++++++++++++++----
 src/bin/xfrin/xfrin.py.in                          |  204 +++++++++++-----
 src/bin/xfrin/xfrin_messages.mes                   |   21 +-
 src/lib/datasrc/sqlite3_accessor.cc                |    8 +-
 src/lib/python/isc/xfrin/diff.py                   |    8 +-
 src/lib/python/isc/xfrin/tests/diff_tests.py       |   14 +-
 11 files changed, 398 insertions(+), 134 deletions(-)
 create mode 100644 src/bin/xfrin/tests/testdata/Makefile.am
 create mode 100644 src/bin/xfrin/tests/testdata/example.com
 copy src/{lib/datasrc/tests/testdata/rwtest.sqlite3 => bin/xfrin/tests/testdata/example.com.sqlite3} (70%)

-----------------------------------------------------------------------
diff --git a/configure.ac b/configure.ac
index a65bdda..d636124 100644
--- a/configure.ac
+++ b/configure.ac
@@ -811,6 +811,7 @@ AC_CONFIG_FILES([Makefile
                  src/bin/sockcreator/tests/Makefile
                  src/bin/xfrin/Makefile
                  src/bin/xfrin/tests/Makefile
+                 src/bin/xfrin/tests/testdata/Makefile
                  src/bin/xfrout/Makefile
                  src/bin/xfrout/tests/Makefile
                  src/bin/zonemgr/Makefile
diff --git a/src/bin/xfrin/tests/Makefile.am b/src/bin/xfrin/tests/Makefile.am
index 3d56009..ae6620c 100644
--- a/src/bin/xfrin/tests/Makefile.am
+++ b/src/bin/xfrin/tests/Makefile.am
@@ -1,3 +1,5 @@
+SUBDIRS = testdata .
+
 PYCOVERAGE_RUN=@PYCOVERAGE_RUN@
 PYTESTS = xfrin_test.py
 EXTRA_DIST = $(PYTESTS)
@@ -20,5 +22,7 @@ endif
 	echo Running test: $$pytest ; \
 	$(LIBRARY_PATH_PLACEHOLDER) \
 	PYTHONPATH=$(abs_top_builddir)/src/lib/dns/python/.libs:$(abs_top_builddir)/src/bin/xfrin:$(COMMON_PYTHON_PATH) \
+	TESTDATASRCDIR=$(abs_top_srcdir)/src/bin/xfrin/tests/testdata/ \
+	TESTDATAOBJDIR=$(abs_top_builddir)/src/bin/xfrin/tests/testdata/ \
 	$(PYCOVERAGE_RUN) $(abs_srcdir)/$$pytest || exit ; \
 	done
diff --git a/src/bin/xfrin/tests/testdata/Makefile.am b/src/bin/xfrin/tests/testdata/Makefile.am
new file mode 100644
index 0000000..5e325cb
--- /dev/null
+++ b/src/bin/xfrin/tests/testdata/Makefile.am
@@ -0,0 +1,2 @@
+EXTRA_DIST = example.com # not necessarily needed, but for reference
+EXTRA_DIST += example.com.sqlite3
diff --git a/src/bin/xfrin/tests/testdata/example.com b/src/bin/xfrin/tests/testdata/example.com
new file mode 100644
index 0000000..2afcd28
--- /dev/null
+++ b/src/bin/xfrin/tests/testdata/example.com
@@ -0,0 +1,17 @@
+;; This is a simplest form of zone file for 'example.com', which is the
+;; source of the corresponding sqlite3 DB file.  This file is provided
+;; for reference purposes only; it's not actually used anywhere.
+
+example.com.		3600	IN SOA	master.example.com. admin.example.com. (
+					1230       ; serial
+					3600       ; refresh (1 hour)
+					1800       ; retry (30 minutes)
+					2419200    ; expire (4 weeks)
+					7200       ; minimum (2 hours)
+					)
+			3600	NS	dns01.example.com.
+			3600	NS	dns02.example.com.
+			3600	NS	dns03.example.com.
+dns01.example.com.	3600	IN A	192.0.2.1
+dns02.example.com.	3600	IN A	192.0.2.2
+dns03.example.com.	3600	IN A	192.0.2.3
diff --git a/src/bin/xfrin/tests/testdata/example.com.sqlite3 b/src/bin/xfrin/tests/testdata/example.com.sqlite3
new file mode 100644
index 0000000..ed241c3
Binary files /dev/null and b/src/bin/xfrin/tests/testdata/example.com.sqlite3 differ
diff --git a/src/bin/xfrin/tests/xfrin_test.py b/src/bin/xfrin/tests/xfrin_test.py
index 1821056..b013b2e 100644
--- a/src/bin/xfrin/tests/xfrin_test.py
+++ b/src/bin/xfrin/tests/xfrin_test.py
@@ -14,6 +14,7 @@
 # WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 
 import unittest
+import shutil
 import socket
 import io
 from isc.testutils.tsigctx_mock import MockTSIGContext
@@ -37,6 +38,9 @@ TEST_MASTER_IPV6_ADDRESS = '::1'
 TEST_MASTER_IPV6_ADDRINFO = (socket.AF_INET6, socket.SOCK_STREAM,
                              socket.IPPROTO_TCP, '',
                              (TEST_MASTER_IPV6_ADDRESS, 53))
+
+TESTDATA_SRCDIR = os.getenv("TESTDATASRCDIR")
+TESTDATA_OBJDIR = os.getenv("TESTDATAOBJDIR")
 # XXX: This should be a non priviledge port that is unlikely to be used.
 # If some other process uses this port test will fail.
 TEST_MASTER_PORT = '53535'
@@ -127,7 +131,7 @@ class MockDataSourceClient():
     def add_rrset(self, rrset):
         self.diffs.append(('add', rrset))
 
-    def remove_rrset(self, rrset):
+    def delete_rrset(self, rrset):
         self.diffs.append(('remove', rrset))
 
     def commit(self):
@@ -156,20 +160,21 @@ class MockXfrin(Xfrin):
             MockXfrin.check_command_hook()
 
     def xfrin_start(self, zone_name, rrclass, db_file, master_addrinfo,
-                    tsig_key, check_soa=True):
+                    tsig_key, request_type, check_soa=True):
         # store some of the arguments for verification, then call this
         # method in the superclass
         self.xfrin_started_master_addr = master_addrinfo[2][0]
         self.xfrin_started_master_port = master_addrinfo[2][1]
-        return Xfrin.xfrin_start(self, zone_name, rrclass, db_file,
+        self.xfrin_started_request_type = request_type
+        return Xfrin.xfrin_start(self, zone_name, rrclass, None,
                                  master_addrinfo, tsig_key,
-                                 check_soa)
+                                 request_type, check_soa)
 
 class MockXfrinConnection(XfrinConnection):
     def __init__(self, sock_map, zone_name, rrclass, db_file, shutdown_event,
                  master_addr):
-        super().__init__(sock_map, zone_name, rrclass, db_file, shutdown_event,
-                         master_addr)
+        super().__init__(sock_map, zone_name, rrclass, MockDataSourceClient(),
+                         db_file, shutdown_event, master_addr)
         self.query_data = b''
         self.reply_data = b''
         self.force_time_out = False
@@ -178,11 +183,6 @@ class MockXfrinConnection(XfrinConnection):
         self.qid = None
         self.response_generator = None
 
-    # The following three implement a simplified mock of DataSourceClient
-    # and ZoneFinder classes for testing purposes.
-    def _get_datasrc_client(self, rrclass):
-        return MockDataSourceClient()
-
     def _asyncore_loop(self):
         if self.force_close:
             self.handle_close()
@@ -422,6 +422,7 @@ class TestXfrinIXFRAdd(TestXfrinState):
         self.assertEqual([], self.conn._diff.get_buffer())
 
     def test_handle_new_delete(self):
+        self.conn._end_serial = 1234
         # SOA RR whose serial is the current one means we are going to a new
         # difference, starting with removing that SOA.
         self.conn._diff.add_data(self.ns_rrset) # put some dummy change
@@ -575,6 +576,19 @@ class TestXfrinConnection(unittest.TestCase):
                 self.assertEqual(diff_exp[1].get_rdata()[0],
                                  diff_actual[1].get_rdata()[0])
 
+    def _create_a(self, address):
+        rrset = RRset(Name('a.example.com'), TEST_RRCLASS, RRType.A(),
+                      RRTTL(3600))
+        rrset.add_rdata(Rdata(RRType.A(), TEST_RRCLASS, address))
+        return rrset
+
+    def _create_soa(self, serial):
+        rrset = RRset(TEST_ZONE_NAME, TEST_RRCLASS, RRType.SOA(),
+                      RRTTL(3600))
+        rdata_str = 'm. r. ' + serial + ' 3600 1800 2419200 7200'
+        rrset.add_rdata(Rdata(RRType.SOA(), TEST_RRCLASS, rdata_str))
+        return rrset
+
 class TestAXFR(TestXfrinConnection):
     def setUp(self):
         super().setUp()
@@ -621,8 +635,8 @@ class TestAXFR(TestXfrinConnection):
         c.close()
 
     def test_init_chclass(self):
-        c = XfrinConnection({}, TEST_ZONE_NAME, RRClass.CH(), TEST_DB_FILE,
-                            threading.Event(), TEST_MASTER_IPV4_ADDRINFO)
+        c = MockXfrinConnection({}, TEST_ZONE_NAME, RRClass.CH(), TEST_DB_FILE,
+                                threading.Event(), TEST_MASTER_IPV4_ADDRINFO)
         axfrmsg = c._create_query(RRType.AXFR())
         self.assertEqual(axfrmsg.get_question()[0].get_class(),
                          RRClass.CH())
@@ -1019,10 +1033,7 @@ class TestAXFR(TestXfrinConnection):
 
     def test_do_soacheck_broken_response(self):
         self.conn.response_generator = self._create_broken_response_data
-        # XXX: TODO: this test failed here, should xfr not raise an
-        # exception but simply drop and return FAIL?
-        #self.assertEqual(self.conn.do_xfrin(True), XFRIN_FAIL)
-        self.assertRaises(MessageTooShort, self.conn.do_xfrin, True)
+        self.assertEqual(self.conn.do_xfrin(True), XFRIN_FAIL)
 
     def test_do_soacheck_badqid(self):
         # the QID mismatch would internally trigger a XfrinException exception,
@@ -1041,19 +1052,6 @@ class TestIXFRResponse(TestXfrinConnection):
         self.conn._datasrc_client = MockDataSourceClient()
         XfrinInitialSOA().set_xfrstate(self.conn, XfrinInitialSOA())
 
-    def create_a(self, address):
-        rrset = RRset(Name('a.example.com'), TEST_RRCLASS, RRType.A(),
-                      RRTTL(3600))
-        rrset.add_rdata(Rdata(RRType.A(), TEST_RRCLASS, address))
-        return rrset
-
-    def create_soa(self, serial):
-        rrset = RRset(TEST_ZONE_NAME, TEST_RRCLASS, RRType.SOA(),
-                      RRTTL(3600))
-        rdata_str = 'm. r. ' + serial + ' 3600 1800 2419200 7200'
-        rrset.add_rdata(Rdata(RRType.SOA(), TEST_RRCLASS, rdata_str))
-        return rrset
-
     def test_ixfr_response(self):
         '''A simplest form of IXFR response.
 
@@ -1069,7 +1067,7 @@ class TestIXFRResponse(TestXfrinConnection):
         self.check_diffs([[('remove', begin_soa_rrset), ('add', soa_rrset)]],
                          self.conn._datasrc_client.committed_diffs)
 
-    def test_ixfr_response_multi_sequence(self):
+    def test_ixfr_response_multi_sequences(self):
         '''Similar to the previous case, but with multiple diff seqs.
 
         '''
@@ -1077,33 +1075,33 @@ class TestIXFRResponse(TestXfrinConnection):
             questions=[Question(TEST_ZONE_NAME, TEST_RRCLASS, RRType.IXFR())],
             answers=[soa_rrset,
                      # removing one A in serial 1230
-                     begin_soa_rrset, self.create_a('192.0.2.1'),
+                     begin_soa_rrset, self._create_a('192.0.2.1'),
                      # adding one A in serial 1231
-                     self.create_soa('1231'), self.create_a('192.0.2.2'),
+                     self._create_soa('1231'), self._create_a('192.0.2.2'),
                      # removing one A in serial 1231
-                     self.create_soa('1231'), self.create_a('192.0.2.3'),
+                     self._create_soa('1231'), self._create_a('192.0.2.3'),
                      # adding one A in serial 1232
-                     self.create_soa('1232'), self.create_a('192.0.2.4'),
+                     self._create_soa('1232'), self._create_a('192.0.2.4'),
                      # removing one A in serial 1232
-                     self.create_soa('1232'), self.create_a('192.0.2.5'),
+                     self._create_soa('1232'), self._create_a('192.0.2.5'),
                      # adding one A in serial 1234
-                     soa_rrset, self.create_a('192.0.2.6'),
+                     soa_rrset, self._create_a('192.0.2.6'),
                      soa_rrset])
         self.conn._handle_xfrin_responses()
         self.assertEqual(type(XfrinIXFREnd()), type(self.conn.get_xfrstate()))
         self.assertEqual([], self.conn._datasrc_client.diffs)
         self.check_diffs([[('remove', begin_soa_rrset),
-                           ('remove', self.create_a('192.0.2.1')),
-                           ('add', self.create_soa('1231')),
-                           ('add', self.create_a('192.0.2.2'))],
-                          [('remove', self.create_soa('1231')),
-                           ('remove', self.create_a('192.0.2.3')),
-                           ('add', self.create_soa('1232')),
-                           ('add', self.create_a('192.0.2.4'))],
-                          [('remove', self.create_soa('1232')),
-                           ('remove', self.create_a('192.0.2.5')),
+                           ('remove', self._create_a('192.0.2.1')),
+                           ('add', self._create_soa('1231')),
+                           ('add', self._create_a('192.0.2.2'))],
+                          [('remove', self._create_soa('1231')),
+                           ('remove', self._create_a('192.0.2.3')),
+                           ('add', self._create_soa('1232')),
+                           ('add', self._create_a('192.0.2.4'))],
+                          [('remove', self._create_soa('1232')),
+                           ('remove', self._create_a('192.0.2.5')),
                            ('add', soa_rrset),
-                           ('add', self.create_a('192.0.2.6'))]],
+                           ('add', self._create_a('192.0.2.6'))]],
                          self.conn._datasrc_client.committed_diffs)
 
     def test_ixfr_response_multi_messages(self):
@@ -1118,8 +1116,46 @@ class TestIXFRResponse(TestXfrinConnection):
             answers=[soa_rrset])
         self.conn._handle_xfrin_responses()
         self.assertEqual(type(XfrinIXFREnd()), type(self.conn.get_xfrstate()))
+        self.check_diffs([[('remove', begin_soa_rrset), ('add', soa_rrset)]],
+                         self.conn._datasrc_client.committed_diffs)
+
+    def test_ixfr_response_broken(self):
+        '''Test with a broken response.
+
+        '''
+        # SOA sequence is out-of-sync
+        self.conn.reply_data = self.conn.create_response_data(
+            questions=[Question(TEST_ZONE_NAME, TEST_RRCLASS, RRType.IXFR())],
+            answers=[soa_rrset, begin_soa_rrset, soa_rrset,
+                     self._create_soa('1235')])
+        self.assertRaises(XfrinProtocolError,
+                          self.conn._handle_xfrin_responses)
+        # no diffs should have been committed
+        self.check_diffs([], self.conn._datasrc_client.committed_diffs)
+
+    def test_ixfr_response_extra(self):
+        '''Test with an extra RR after the end of IXFR diff sequences.
+
+        IXFR should be rejected, but complete diff sequences should be
+        committed; it's not clear whether it's compliant to the protocol
+        specification, but it is how BIND 9 works and we do the same.
+        '''
+        self.conn.reply_data = self.conn.create_response_data(
+            questions=[Question(TEST_ZONE_NAME, TEST_RRCLASS, RRType.IXFR())],
+            answers=[soa_rrset, begin_soa_rrset, soa_rrset, soa_rrset,
+                     self._create_a('192.0.2.1')])
+        self.assertRaises(XfrinProtocolError,
+                          self.conn._handle_xfrin_responses)
+        self.check_diffs([[('remove', begin_soa_rrset), ('add', soa_rrset)]],
+                         self.conn._datasrc_client.committed_diffs)
 
 class TestIXFRSession(TestXfrinConnection):
+    '''Tests for a full IXFR session (query and response).
+
+    Detailed corner cases should have been covered in test_create_query()
+    and TestIXFRResponse, so we'll only check some typical cases to confirm
+    the general logic flow.
+    '''
     def setUp(self):
         super().setUp()
 
@@ -1130,15 +1166,14 @@ class TestIXFRSession(TestXfrinConnection):
                                     RRType.IXFR())],
                 answers=[soa_rrset, begin_soa_rrset, soa_rrset, soa_rrset])
         self.conn.response_generator = create_ixfr_response
-        self.assertEqual(XFRIN_OK, self.conn.do_xfrin(False, True))
+        self.assertEqual(XFRIN_OK, self.conn.do_xfrin(False, RRType.IXFR()))
 
         # Check some details of the IXFR protocol processing
         self.assertEqual(type(XfrinIXFREnd()), type(self.conn.get_xfrstate()))
         self.check_diffs([[('remove', begin_soa_rrset), ('add', soa_rrset)]],
                          self.conn._datasrc_client.committed_diffs)
 
-        # Check if the query was IXFR.  We only check for the RR type.  Other
-        # details are tested in test_create_query().
+        # Check if the query was IXFR.
         qdata = self.conn.query_data[2:]
         qmsg = Message(Message.PARSE)
         qmsg.from_wire(qdata, len(qdata))
@@ -1146,6 +1181,88 @@ class TestIXFRSession(TestXfrinConnection):
         self.assertEqual(TEST_ZONE_NAME, qmsg.get_question()[0].get_name())
         self.assertEqual(RRType.IXFR(), qmsg.get_question()[0].get_type())
 
+    def test_do_xfrin_fail(self):
+        '''IXFR fails due to a protocol error.
+
+        '''
+        def create_ixfr_response():
+            self.conn.reply_data = self.conn.create_response_data(
+                questions=[Question(TEST_ZONE_NAME, TEST_RRCLASS,
+                                    RRType.IXFR())],
+                answers=[soa_rrset, begin_soa_rrset, soa_rrset,
+                         self._create_soa('1235')])
+        self.conn.response_generator = create_ixfr_response
+        self.assertEqual(XFRIN_FAIL, self.conn.do_xfrin(False, RRType.IXFR()))
+
+    def test_do_xfrin_fail(self):
+        '''IXFR fails due to a bogus DNS message.
+
+        '''
+        self._create_broken_response_data()
+        self.assertEqual(XFRIN_FAIL, self.conn.do_xfrin(False, RRType.IXFR()))
+
+class TestIXFRSessionWithSQLite3(TestXfrinConnection):
+    '''Tests for IXFR sessions using an SQLite3 DB.
+
+    These are provided mainly to confirm the implementation actually works
+    in an environment closer to actual operational environments.  So we
+    only check a few common cases; other details are tested using mock
+    data sources.
+
+    '''
+    def setUp(self):
+        self.sqlite3db_src = TESTDATA_SRCDIR + '/example.com.sqlite3'
+        self.sqlite3db_obj = TESTDATA_OBJDIR + '/example.com.sqlite3.copy'
+        super().setUp()
+        if os.path.exists(self.sqlite3db_obj):
+            os.unlink(self.sqlite3db_obj)
+        shutil.copyfile(self.sqlite3db_src, self.sqlite3db_obj)
+        self.conn._datasrc_client = DataSourceClient(self.sqlite3db_obj)
+
+    def tearDown(self):
+        if os.path.exists(self.sqlite3db_obj):
+            os.unlink(self.sqlite3db_obj)
+
+    def get_zone_serial(self):
+        result, finder = self.conn._datasrc_client.find_zone(TEST_ZONE_NAME)
+        self.assertEqual(DataSourceClient.SUCCESS, result)
+        result, soa = finder.find(TEST_ZONE_NAME, RRType.SOA(),
+                                  None, ZoneFinder.FIND_DEFAULT)
+        self.assertEqual(ZoneFinder.SUCCESS, result)
+        self.assertEqual(1, soa.get_rdata_count())
+        return get_soa_serial(soa.get_rdata()[0])
+
+    def test_do_xfrin_sqlite3(self):
+        def create_ixfr_response():
+            self.conn.reply_data = self.conn.create_response_data(
+                questions=[Question(TEST_ZONE_NAME, TEST_RRCLASS,
+                                    RRType.IXFR())],
+                answers=[soa_rrset, begin_soa_rrset, soa_rrset, soa_rrset])
+        self.conn.response_generator = create_ixfr_response
+
+        # Confirm xfrin succeeds and SOA is updated
+        self.assertEqual(1230, self.get_zone_serial())
+        self.assertEqual(XFRIN_OK, self.conn.do_xfrin(False, RRType.IXFR()))
+        self.assertEqual(1234, self.get_zone_serial())
+
+    def test_do_xfrin_sqlite3_fail(self):
+        '''Similar to the previous test, but xfrin fails due to error.
+
+        Check the DB is not changed.
+
+        '''
+        def create_ixfr_response():
+            self.conn.reply_data = self.conn.create_response_data(
+                questions=[Question(TEST_ZONE_NAME, TEST_RRCLASS,
+                                    RRType.IXFR())],
+                answers=[soa_rrset, begin_soa_rrset, soa_rrset,
+                         self._create_soa('1235')])
+        self.conn.response_generator = create_ixfr_response
+
+        self.assertEqual(1230, self.get_zone_serial())
+        self.assertEqual(XFRIN_FAIL, self.conn.do_xfrin(False, RRType.IXFR()))
+        self.assertEqual(1230, self.get_zone_serial())
+
 class TestXfrinRecorder(unittest.TestCase):
     def setUp(self):
         self.recorder = XfrinRecorder()
@@ -1271,6 +1388,8 @@ class TestXfrin(unittest.TestCase):
                                                   self.args)['result'][0], 0)
         self.assertEqual(self.args['master'], self.xfr.xfrin_started_master_addr)
         self.assertEqual(int(self.args['port']), self.xfr.xfrin_started_master_port)
+        # By default we use AXFR (for now)
+        self.assertEqual(RRType.AXFR(), self.xfr.xfrin_started_request_type)
 
     def test_command_handler_retransfer_short_command1(self):
         # try it when only specifying the zone name (of unknown zone)
@@ -1383,6 +1502,8 @@ class TestXfrin(unittest.TestCase):
                          self.xfr.xfrin_started_master_addr)
         self.assertEqual(int(TEST_MASTER_PORT),
                          self.xfr.xfrin_started_master_port)
+        # By default we use AXFR (for now)
+        self.assertEqual(RRType.AXFR(), self.xfr.xfrin_started_request_type)
 
     def test_command_handler_notify(self):
         # at this level, refresh is no different than retransfer.
@@ -1572,6 +1693,38 @@ class TestXfrin(unittest.TestCase):
         # since this has failed, we should still have the previous config
         self._check_zones_config(config2)
 
+    def common_ixfr_setup(self, xfr_mode, ixfr_disabled):
+        # This helper method explicitly sets up a zone configuration with
+        # ixfr_disabled, and invokes either retransfer or refresh.
+        # Shared by some of the following test cases.
+        config = {'zones': [
+                {'name': 'example.com.',
+                 'master_addr': '192.0.2.1',
+                 'ixfr_disabled': ixfr_disabled}]}
+        self.assertEqual(self.xfr.config_handler(config)['result'][0], 0)
+        self.assertEqual(self.xfr.command_handler(xfr_mode,
+                                                  self.args)['result'][0], 0)
+
+    def test_command_handler_retransfer_ixfr_enabled(self):
+        # If IXFR is explicitly enabled in config, IXFR will be used
+        self.common_ixfr_setup('retransfer', False)
+        self.assertEqual(RRType.IXFR(), self.xfr.xfrin_started_request_type)
+
+    def test_command_handler_refresh_ixfr_enabled(self):
+        # Same for refresh
+        self.common_ixfr_setup('refresh', False)
+        self.assertEqual(RRType.IXFR(), self.xfr.xfrin_started_request_type)
+
+    def test_command_handler_retransfer_ixfr_disabled(self):
+        # Similar to the previous case, but explicitly disabled.  AXFR should
+        # be used.
+        self.common_ixfr_setup('retransfer', True)
+        self.assertEqual(RRType.AXFR(), self.xfr.xfrin_started_request_type)
+
+    def test_command_handler_refresh_ixfr_disabled(self):
+        # Same for refresh
+        self.common_ixfr_setup('refresh', True)
+        self.assertEqual(RRType.AXFR(), self.xfr.xfrin_started_request_type)
 
 def raise_interrupt():
     raise KeyboardInterrupt()
diff --git a/src/bin/xfrin/xfrin.py.in b/src/bin/xfrin/xfrin.py.in
index 90e4e18..01ca8d3 100755
--- a/src/bin/xfrin/xfrin.py.in
+++ b/src/bin/xfrin/xfrin.py.in
@@ -144,26 +144,70 @@ class XfrinState:
     response RRs have already been received.
     NOTE: the AXFR part of the state machine is incomplete at this point.
 
-    This implementation uses the state design patter, where each state
-    is represented as a subclass of the base XfrinState class.  The base
-    class defines two abstract interfaces: handle_rr() and finish_message().
-    These methods handle specific part of XFR protocols and (if necessary)
-    perform the state transition.
+    The following diagram summarizes the state transition.  After sending
+    the query, xfrin starts the process with the InitialSOA state (all
+    IXFR/AXFR response begins with an SOA).  When it reaches IXFREnd
+    (or AXFREnd, which is not yet implemented and not shown here), the
+    process successfully completes.
+
+
+            (recv SOA)       (AXFR-style IXFR)
+    InitialSOA------->FirstData------------->AXFR
+                          |                     (non SOA, delete)
+               (pure IXFR,|                           +-------+
+            keep handling)|             (Delete SOA)  V       |
+                          + ->IXFRDeleteSOA------>IXFRDelete--+
+                                   ^                   |
+                (see SOA, not end, |          (see SOA)|
+            commit, keep handling) |                   |
+                                   |                   V
+                      +---------IXFRAdd<----------+IXFRAddSOA
+        (non SOA, add)|         ^  |    (Add SOA)
+                      ----------+  |
+                                   |(see SOA w/ end serial, commit changes)
+                                   V
+                                IXFREnd
+
+    This implementation uses the state design pattern, where each state
+    is represented as a subclass of the base XfrinState class.  Each concrete
+    subclass of XfrinState is assumed to define two methods: handle_rr() and
+    finish_message().  These methods handle specific part of XFR protocols
+    and (if necessary) perform the state transition.
+
+    Conceptually, XfrinState and its subclasses are a "friend" of
+    XfrinConnection and are assumed to be allowed to access its internal
+    information (even though Python does not have a strict access control
+    between different classes).
 
     The XfrinState and its subclasses are designed to be stateless, and
     can be used as singleton objects.  For now, however, we always instantiate
     a new object for every state transition, partly because the introduction
     of singleton will make a code bit complicated, and partly because
     the overhead of object instantiotion wouldn't be significant for xfrin.
+
     '''
     def set_xfrstate(self, conn, new_state):
         '''Set the XfrConnection to a given new state.
 
         As a "friend" class, this method intentionally gets access to the
         connection's "private" method.
+
         '''
         conn._XfrinConnection__set_xfrstate(new_state)
 
+    def handle_rr(self, conn):
+        '''Handle one RR of an XFR response message.
+
+        Depending on the state, the RR is generally added or deleted in the
+        corresponding data source, or in some special cases indicates
+        a specifi transition, such as starting a new IXFR difference
+        sequence or completing the session.
+
+        All subclass has their specific behaviors for this method, so
+        there is no default definition.
+        '''
+        pass
+
     def finish_message(self, conn):
         '''Perform any final processing after handling all RRs of a response.
 
@@ -183,9 +227,10 @@ class XfrinInitialSOA(XfrinState):
         conn._end_serial = get_soa_serial(rr.get_rdata()[0])
 
         # FIXME: we need to check the serial is actually greater than ours.
-        # To do so, however, we need a way to find records from datasource.
-        # Complete that part later as a separate task.  (Always performing
-        # xfr could be inefficient, but shouldn't do any harm otherwise)
+        # To do so, however, we need to implement serial number arithmetic.
+        # Although it wouldn't be a big task, we'll leave it for a separate
+        # task for now.  (Always performing xfr could be inefficient, but
+        # shouldn't do any harm otherwise)
 
         self.set_xfrstate(conn, XfrinFirstData())
         return True
@@ -212,12 +257,12 @@ class XfrinIXFRDeleteSOA(XfrinState):
         if rr.get_type() != RRType.SOA():
             # this shouldn't happen; should this occur it means an internal
             # bug.
-            raise XfrinException(rr.get_type().to_text() + \
-                                     ' RR is given in IXFRDeleteSOA state')
+            raise XfrinException(rr.get_type().to_text() +
+                                 ' RR is given in IXFRDeleteSOA state')
         # This is the beginning state of one difference sequence (changes
         # for one SOA update).  We need to create a new Diff object now.
         conn._diff = Diff(conn._datasrc_client, conn._zone_name)
-        conn._diff.remove_data(rr)
+        conn._diff.delete_data(rr)
         self.set_xfrstate(conn, XfrinIXFRDelete())
         return True
 
@@ -228,7 +273,7 @@ class XfrinIXFRDelete(XfrinState):
             conn._current_serial = get_soa_serial(rr.get_rdata()[0])
             self.set_xfrstate(conn, XfrinIXFRAddSOA())
             return False
-        conn._diff.remove_data(rr)
+        conn._diff.delete_data(rr)
         return True
 
 class XfrinIXFRAddSOA(XfrinState):
@@ -236,8 +281,8 @@ class XfrinIXFRAddSOA(XfrinState):
         if rr.get_type() != RRType.SOA():
             # this shouldn't happen; should this occur it means an internal
             # bug.
-            raise XfrinException(rr.get_type().to_text() + \
-                                     ' RR is given in IXFRAddSOA state')
+            raise XfrinException(rr.get_type().to_text() +
+                                 ' RR is given in IXFRAddSOA state')
         conn._diff.add_data(rr)
         self.set_xfrstate(conn, XfrinIXFRAdd())
         return True
@@ -251,10 +296,10 @@ class XfrinIXFRAdd(XfrinState):
                 self.set_xfrstate(conn, XfrinIXFREnd())
                 return True
             elif soa_serial != conn._current_serial:
-                raise XfrinProtocolError('IXFR out of sync: expected ' + \
-                                             'serial ' + \
-                                             str(conn._current_serial) + \
-                                             ', got ' + str(soa_serial))
+                raise XfrinProtocolError('IXFR out of sync: expected ' +
+                                         'serial ' +
+                                         str(conn._current_serial) +
+                                         ', got ' + str(soa_serial))
             else:
                 conn._diff.commit()
                 self.set_xfrstate(conn, XfrinIXFRDeleteSOA())
@@ -264,8 +309,8 @@ class XfrinIXFRAdd(XfrinState):
 
 class XfrinIXFREnd(XfrinState):
     def handle_rr(self, conn, rr):
-        raise XfrinProtocolError('Extra data after the end of IXFR diffs: ' + \
-                                     rr.to_text())
+        raise XfrinProtocolError('Extra data after the end of IXFR diffs: ' +
+                                 rr.to_text())
 
     def finish_message(self, conn):
         '''Final processing after processing an entire IXFR session.
@@ -278,27 +323,36 @@ class XfrinIXFREnd(XfrinState):
 
 class XfrinAXFR(XfrinState):
     def handle_rr(self, conn, rr):
-        raise XfrinException('Falling back from IXFR to AXFR not ' + \
-                                 'supported yet')
+        raise XfrinException('Falling back from IXFR to AXFR not ' +
+                             'supported yet')
 
 class XfrinConnection(asyncore.dispatcher):
     '''Do xfrin in this class. '''
 
     def __init__(self,
-                 sock_map, zone_name, rrclass, db_file, shutdown_event,
-                 master_addrinfo, tsig_key = None, verbose = False,
-                 idle_timeout = 60):
-        ''' idle_timeout: max idle time for read data from socket.
-            db_file: specify the data source file.
-            check_soa: when it's true, check soa first before sending xfr query
+                 sock_map, zone_name, rrclass, datasrc_client, db_file,
+                 shutdown_event, master_addrinfo, tsig_key = None,
+                 verbose=False, idle_timeout=60):
+        '''Constructor of the XfirnConnection class.
+
+        idle_timeout: max idle time for read data from socket.
+        datasrc_client: the data source client object used for the XFR session.
+                        This will eventually replace db_file completely.
+        db_file: specify the data source file (should soon be deprecated).
+
         '''
 
         asyncore.dispatcher.__init__(self, map=sock_map)
+
+        # The XFR state.  Coceptually this is purely private, so we emphasize
+        # the fact by the double underscore.  Other classes are assumed to
+        # get access to this via get_xfrstate(), and only XfrinState classes
+        # are assumed to be allowed to modify it via __set_xfrstate().
         self.__state = None
+
         # Requested transfer type (RRType.AXFR or RRType.IXFR).  The actual
         # transfer type may differ due to IXFR->AXFR fallback:
         self._request_type = None
-        self._end_serial = None # essentially private
 
         # Zone parameters
         self._zone_name = zone_name
@@ -306,7 +360,7 @@ class XfrinConnection(asyncore.dispatcher):
 
         # Data source handlers
         self._db_file = db_file # temporary for sqlite3 specific code
-        self._datasrc_client = self._get_datasrc_client(rrclass)
+        self._datasrc_client = datasrc_client
 
         self.create_socket(master_addrinfo[0], master_addrinfo[1])
         self._sock_map = sock_map
@@ -325,10 +379,6 @@ class XfrinConnection(asyncore.dispatcher):
     def __create_tsig_ctx(self, key):
         return TSIGContext(key)
 
-    def _get_datasrc_client(self, rrclass):
-        # TODO: create a DataSourceClient assuming the sqlite3 backend for now
-        return None
-
     def __set_xfrstate(self, new_state):
         self.__state = new_state
 
@@ -481,46 +531,62 @@ class XfrinConnection(asyncore.dispatcher):
         # now.
         return XFRIN_OK
 
-    def do_xfrin(self, check_soa, ixfr_first=False):
+    def do_xfrin(self, check_soa, request_type=RRType.AXFR()):
         '''Do an xfr session by sending xfr request and parsing responses.'''
 
         try:
             ret = XFRIN_OK
+            self._request_type = request_type
+            # Right now RRType.[IA]XFR().to_text() is 'TYPExxx', so we need
+            # to hardcode here.
+            request_str = 'IXFR' if request_type == RRType.IXFR() else 'AXFR'
             if check_soa:
                 logstr = 'SOA check for \'%s\' ' % self.zone_str()
                 ret =  self._check_soa_serial()
 
             if ret == XFRIN_OK:
-                if ixfr_first:
-                    # TODO: log it
+                logger.info(XFRIN_XFR_TRANSFER_STARTED, request_str,
+                            self.zone_str())
+                if self._request_type == RRType.IXFR():
                     self._request_type = RRType.IXFR()
-                    self._send_query(RRType.IXFR())
+                    self._send_query(self._request_type)
                     self.__state = XfrinInitialSOA()
                     self._handle_xfrin_responses()
                 else:
-                    logger.info(XFRIN_AXFR_TRANSFER_STARTED, self.zone_str())
-                    self._send_query(RRType.AXFR())
+                    self._send_query(self._request_type)
                     isc.datasrc.sqlite3_ds.load(self._db_file,
                                                 self._zone_name.to_text(),
                                                 self._handle_axfrin_response)
-                    logger.info(XFRIN_AXFR_TRANSFER_SUCCESS, self.zone_str())
+                logger.info(XFRIN_XFR_TRANSFER_SUCCESS, request_str,
+                            self.zone_str())
 
-        except XfrinException as e:
-            logger.error(XFRIN_AXFR_TRANSFER_FAILURE, self.zone_str(), str(e))
+        except (XfrinException, XfrinProtocolError) as e:
+            logger.error(XFRIN_XFR_TRANSFER_FAILURE, request_str,
+                         self.zone_str(), str(e))
             ret = XFRIN_FAIL
-            #TODO, recover data source.
         except isc.datasrc.sqlite3_ds.Sqlite3DSError as e:
+            # Note: this is old code and used only for AXFR.  This will be
+            # soon removed anyway, so we'll leave it.
             logger.error(XFRIN_AXFR_DATABASE_FAILURE, self.zone_str(), str(e))
             ret = XFRIN_FAIL
-        except UserWarning as e:
-            # XXX: this is an exception from our C++ library via the
-            # Boost.Python binding.  It would be better to have more more
-            # specific exceptions, but at this moment this is the finest
-            # granularity.
-            logger.error(XFRIN_AXFR_INTERNAL_FAILURE, self.zone_str(), str(e))
+        except Exception as e:
+            # Catching all possible exceptions like this is generally not a
+            # good practice, but handling an xfr session could result in
+            # so many types of exceptions, including ones from the DNS library
+            # or from the data source library.  Eventually we'd introduce a
+            # hierarchy for exception classes from a base "ISC exception" and
+            # catch it here, but until then we need broadest coverage so that
+            # we won't miss anything.
+
+            logger.error(XFRIN_XFR_OTHER_FAILURE, request_str,
+                         self.zone_str(), str(e))
             ret = XFRIN_FAIL
         finally:
-           self.close()
+            # Make sure any remaining transaction in the diff is closed
+            # (if not yet - possible in case of xfr-level exception) as soon
+            # as possible
+            self._diff = None
+            self.close()
 
         return ret
 
@@ -651,15 +717,27 @@ class XfrinConnection(asyncore.dispatcher):
 
 def process_xfrin(server, xfrin_recorder, zone_name, rrclass, db_file,
                   shutdown_event, master_addrinfo, check_soa, verbose,
-                  tsig_key):
+                  tsig_key, request_type):
     xfrin_recorder.increment(zone_name)
+
+    # Create a data source client used in this XFR session.  Right now we
+    # still assume an sqlite3-based data source, and use both the old and new
+    # data source APIs.  We also need to use a mock client for tests.
+    # For a temporary workaround to deal with these situations, we skip the
+    # creation when the given file is none (the test case).  Eventually
+    # this code will be much cleaner.
+    datasrc_client = None
+    if db_file is not None:
+        datasrc_client = DataSourceClient(db_file)
+
+    # Create a TCP connection for the XFR session and perform the operation.
     sock_map = {}
-    conn = XfrinConnection(sock_map, zone_name, rrclass, db_file,
-                           shutdown_event, master_addrinfo,
+    conn = XfrinConnection(sock_map, zone_name, rrclass, datasrc_client,
+                           db_file, shutdown_event, master_addrinfo,
                            tsig_key, verbose)
     ret = XFRIN_FAIL
     if conn.connect_to_master():
-        ret = conn.do_xfrin(check_soa)
+        ret = conn.do_xfrin(check_soa, request_type)
 
     # Publish the zone transfer result news, so zonemgr can reset the
     # zone timer, and xfrout can notify the zone's slaves if the result
@@ -903,7 +981,7 @@ class Xfrin:
                                            rrclass,
                                            self._get_db_file(),
                                            master_addr,
-                                           zone_info.tsig_key,
+                                           zone_info.tsig_key, RRType.AXFR(),
                                            True)
                     answer = create_answer(ret[0], ret[1])
 
@@ -916,14 +994,17 @@ class Xfrin:
                                                           rrclass)
                 zone_info = self._get_zone_info(zone_name, rrclass)
                 tsig_key = None
+                request_type = RRType.AXFR()
                 if zone_info:
                     tsig_key = zone_info.tsig_key
+                    if not zone_info.ixfr_disabled:
+                        request_type = RRType.IXFR()
                 db_file = args.get('db_file') or self._get_db_file()
                 ret = self.xfrin_start(zone_name,
                                        rrclass,
                                        db_file,
                                        master_addr,
-                                       tsig_key,
+                                       tsig_key, request_type,
                                        (False if command == 'retransfer' else True))
                 answer = create_answer(ret[0], ret[1])
 
@@ -1003,7 +1084,8 @@ class Xfrin:
         news(command: zone_new_data_ready) to zone manager and xfrout.
         if xfrin failed, just tell the bad news to zone manager, so that
         it can reset the refresh timer for that zone. '''
-        param = {'zone_name': zone_name, 'zone_class': zone_class.to_text()}
+        param = {'zone_name': zone_name.to_text(),
+                 'zone_class': zone_class.to_text()}
         if xfr_result == XFRIN_OK:
             msg = create_command(notify_out.ZONE_NEW_DATA_READY_CMD, param)
             # catch the exception, in case msgq has been killed.
@@ -1040,8 +1122,8 @@ class Xfrin:
         while not self._shutdown_event.is_set():
             self._cc_check_command()
 
-    def xfrin_start(self, zone_name, rrclass, db_file, master_addrinfo, tsig_key,
-                    check_soa = True):
+    def xfrin_start(self, zone_name, rrclass, db_file, master_addrinfo,
+                    tsig_key, request_type, check_soa=True):
         if "pydnspp" not in sys.modules:
             return (1, "xfrin failed, can't load dns message python library: 'pydnspp'")
 
@@ -1061,7 +1143,7 @@ class Xfrin:
                                                 self._shutdown_event,
                                                 master_addrinfo, check_soa,
                                                 self._verbose,
-                                                tsig_key))
+                                                tsig_key, request_type))
 
         xfrin_thread.start()
         return (0, 'zone xfrin is started')
diff --git a/src/bin/xfrin/xfrin_messages.mes b/src/bin/xfrin/xfrin_messages.mes
index fd35463..5e3e347 100644
--- a/src/bin/xfrin/xfrin_messages.mes
+++ b/src/bin/xfrin/xfrin_messages.mes
@@ -15,25 +15,26 @@
 # No namespace declaration - these constants go in the global namespace
 # of the xfrin messages python module.
 
-% XFRIN_AXFR_INTERNAL_FAILURE AXFR transfer of zone %1 failed: %2
-The AXFR transfer for the given zone has failed due to an internal
-problem in the bind10 python wrapper library.
-The error is shown in the log message.
+% XFRIN_XFR_OTHER_FAILURE %1 transfer of zone %2 failed: %3
+The XFR transfer for the given zone has failed due to a problem outside
+of the xfrin module.  Possible reasons are a broken DNS message or failure
+in database connection.  The error is shown in the log message.
 
 % XFRIN_AXFR_DATABASE_FAILURE AXFR transfer of zone %1 failed: %2
 The AXFR transfer for the given zone has failed due to a database problem.
-The error is shown in the log message.
+The error is shown in the log message.  Note: due to the code structure
+this can only happen for AXFR.
 
-% XFRIN_AXFR_TRANSFER_FAILURE AXFR transfer of zone %1 failed: %2
-The AXFR transfer for the given zone has failed due to a protocol error.
+% XFRIN_XFR_TRANSFER_FAILURE %1 transfer of zone %2 failed: %3
+The XFR transfer for the given zone has failed due to a protocol error.
 The error is shown in the log message.
 
-% XFRIN_AXFR_TRANSFER_STARTED AXFR transfer of zone %1 started
+% XFRIN_XFR_TRANSFER_STARTED %1 transfer of zone %2 started
 A connection to the master server has been made, the serial value in
 the SOA record has been checked, and a zone transfer has been started.
 
-% XFRIN_AXFR_TRANSFER_SUCCESS AXFR transfer of zone %1 succeeded
-The AXFR transfer of the given zone was successfully completed.
+% XFRIN_XFR_TRANSFER_SUCCESS %1 transfer of zone %2 succeeded
+The XFR transfer of the given zone was successfully completed.
 
 % XFRIN_BAD_MASTER_ADDR_FORMAT bad format for master address: %1
 The given master address is not a valid IP address.
diff --git a/src/lib/datasrc/sqlite3_accessor.cc b/src/lib/datasrc/sqlite3_accessor.cc
index 69d5649..3c2a705 100644
--- a/src/lib/datasrc/sqlite3_accessor.cc
+++ b/src/lib/datasrc/sqlite3_accessor.cc
@@ -637,8 +637,12 @@ doUpdate(SQLite3Parameters& dbparams, StatementID stmt_id,
     const size_t column_count =
         sizeof(update_params) / sizeof(update_params[0]);
     for (int i = 0; i < column_count; ++i) {
-        if (sqlite3_bind_text(stmt, ++param_id, update_params[i].c_str(), -1,
-                              SQLITE_TRANSIENT) != SQLITE_OK) {
+        // The old sqlite3 data source API assumes NULL for an empty column.
+        // We need to provide compatibility at least for now.
+        if (sqlite3_bind_text(stmt, ++param_id,
+                              update_params[i].empty() ? NULL :
+                              update_params[i].c_str(),
+                              -1, SQLITE_TRANSIENT) != SQLITE_OK) {
             isc_throw(DataSourceError, "failed to bind SQLite3 parameter: " <<
                       sqlite3_errmsg(dbparams.db_));
         }
diff --git a/src/lib/python/isc/xfrin/diff.py b/src/lib/python/isc/xfrin/diff.py
index 6c05401..f2f442b 100644
--- a/src/lib/python/isc/xfrin/diff.py
+++ b/src/lib/python/isc/xfrin/diff.py
@@ -73,7 +73,7 @@ class Diff:
         """
         Schedules an operation with rr.
 
-        It does all the real work of add_data and remove_data, including
+        It does all the real work of add_data and delete_data, including
         all checks.
         """
         self.__check_commited()
@@ -95,9 +95,9 @@ class Diff:
         """
         self.__data_common(rr, 'add')
 
-    def remove_data(self, rr):
+    def delete_data(self, rr):
         """
-        Schedules removal of an RR from the zone in this diff.
+        Schedules deleting an RR from the zone in this diff.
 
         The rr is of isc.dns.RRset type and it must contain only one RR.
         If this is not the case or if the diff was already commited, this
@@ -156,7 +156,7 @@ class Diff:
             if operation == 'add':
                 self.__updater.add_rrset(rrset)
             elif operation == 'remove':
-                self.__updater.remove_rrset(rrset)
+                self.__updater.delete_rrset(rrset)
             else:
                 raise ValueError('Unknown operation ' + operation)
         # As everything is already in, drop the buffer
diff --git a/src/lib/python/isc/xfrin/tests/diff_tests.py b/src/lib/python/isc/xfrin/tests/diff_tests.py
index 963b16d..3749c6b 100644
--- a/src/lib/python/isc/xfrin/tests/diff_tests.py
+++ b/src/lib/python/isc/xfrin/tests/diff_tests.py
@@ -87,7 +87,7 @@ class DiffTest(unittest.TestCase):
         """
         self.__data_operations.append(('add', rrset))
 
-    def remove_rrset(self, rrset):
+    def delete_rrset(self, rrset):
         """
         This one is part of pretending to be a zone updater. It writes down
         removal of an rrset was requested.
@@ -164,7 +164,7 @@ class DiffTest(unittest.TestCase):
         Also try passing an rrset that has different amount of RRs than 1.
         """
         diff = Diff(self, Name('example.org.'))
-        self.__data_common(diff, diff.remove_data, 'remove')
+        self.__data_common(diff, diff.delete_data, 'remove')
 
     def test_apply(self):
         """
@@ -174,7 +174,7 @@ class DiffTest(unittest.TestCase):
         # Prepare the diff
         diff = Diff(self, Name('example.org.'))
         diff.add_data(self.__rrset1)
-        diff.remove_data(self.__rrset2)
+        diff.delete_data(self.__rrset2)
         dlist = [('add', self.__rrset1), ('remove', self.__rrset2)]
         self.assertEqual(dlist, diff.get_buffer())
         # Do the apply, hook the compact method
@@ -209,7 +209,7 @@ class DiffTest(unittest.TestCase):
         # Now check all range of other methods raise ValueError
         self.assertRaises(ValueError, diff.commit)
         self.assertRaises(ValueError, diff.add_data, self.__rrset2)
-        self.assertRaises(ValueError, diff.remove_data, self.__rrset1)
+        self.assertRaises(ValueError, diff.delete_data, self.__rrset1)
         diff.apply = orig_apply
         self.assertRaises(ValueError, diff.apply)
         # This one does not state it should raise, so check it doesn't
@@ -249,11 +249,11 @@ class DiffTest(unittest.TestCase):
         # Similar with remove
         self.__apply_called = False
         for i in range(0, 99):
-            diff.remove_data(self.__rrset2)
+            diff.delete_data(self.__rrset2)
         expected = [('remove', self.__rrset2)] * 99
         self.assertEqual(expected, diff.get_buffer())
         self.assertFalse(self.__apply_called)
-        diff.remove_data(self.__rrset2)
+        diff.delete_data(self.__rrset2)
         self.assertTrue(self.__apply_called)
 
     def test_compact(self):
@@ -295,7 +295,7 @@ class DiffTest(unittest.TestCase):
                 if op == 'add':
                     diff.add_data(rrset)
                 else:
-                    diff.remove_data(rrset)
+                    diff.delete_data(rrset)
         # Compact it
         diff.compact()
         # Now check they got compacted. They should be in the same order as




More information about the bind10-changes mailing list