BIND 10 master, updated. 8118f8e4e9c0ad3e7b690bbce265a163e4f8767a [master] Merge branch 'trac2911'
BIND 10 source code commits
bind10-changes at lists.isc.org
Thu May 30 17:19:09 UTC 2013
The branch, master has been updated
via 8118f8e4e9c0ad3e7b690bbce265a163e4f8767a (commit)
via 1f72551a31350aebe5709f011f58e2e0ce5b524d (commit)
via 5e08967f518e716ecd929249f7366a2d7faf6306 (commit)
via cd6f223e8f3da567fc320ca4b65948eae9e806ef (commit)
via 946d6e879a77f482a8887d47deb797990dd66544 (commit)
via 09c5415d6a9db4161f280bf8172d21fd796af068 (commit)
via 717f7ead4cfc365fac6c8d95aa79f835885b5f90 (commit)
via 397c814090c29f55eafc869702d00e0c15bac0fd (commit)
via 68e2c05b1f696136e683765c4d40170a45f721b5 (commit)
via 7917c9005e71b17be80a4ff27e21858d82a53e6b (commit)
via e3a4ca39f16eaaf8107d688c03f878ef1cbb68b9 (commit)
via 0332ecca45c768907451a893663f526139ffc2e2 (commit)
via 8c0de3883e79cab57fb1dbabd56b8f7515823288 (commit)
via 9c25723eb98513953e93a16a5bb5b83cdaad015e (commit)
via 2a2f47d37519d5ec4cd310899d7df482145f5feb (commit)
via 60a17382794076ae299eed542eec4b2f8825c120 (commit)
via cac71bc308843f84ef9d1b427f1d401f036ef1b7 (commit)
via 73f842441ce26df57c3af30f526b59e3eba884cb (commit)
via d43299cb0e6680458c2e831048aaa16acc0d3017 (commit)
via e532e4f5648f23bec3bc01d01bec47abd966e07f (commit)
via 537970619d87f2556abcb7d8cb03eff47eda0d54 (commit)
via c612e8e22e27210ce85ff4b1f2924686567aada1 (commit)
via 1ab87fb58246a94e4172188ac80061cddcaad3a2 (commit)
via f714c4ca5abfd11559854398f6f47ec403a3f7de (commit)
via 4acf38036ce68ae59b2da053af471bd0ec40ecae (commit)
via 42569ae1408198a605b93aa1369e32fb62dc8ee2 (commit)
via 14fa32e0dcb2d77ebc7d8a5938b37e36324b8bef (commit)
via 018e2a97c77a95d84505c45f3e2d106e753b18f7 (commit)
from 7e575b706d7cd4d9d15bc9bb6a623ea9b9077252 (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 8118f8e4e9c0ad3e7b690bbce265a163e4f8767a
Merge: 7e575b7 1f72551
Author: JINMEI Tatuya <jinmei at isc.org>
Date: Thu May 30 09:59:16 2013 -0700
[master] Merge branch 'trac2911'
Fixed Conflicts:
ChangeLog
commit 1f72551a31350aebe5709f011f58e2e0ce5b524d
Author: JINMEI Tatuya <jinmei at isc.org>
Date: Wed May 29 11:31:38 2013 -0700
[2911] indentation fix
commit 5e08967f518e716ecd929249f7366a2d7faf6306
Author: JINMEI Tatuya <jinmei at isc.org>
Date: Wed May 29 11:22:23 2013 -0700
[2911] update log message on multi-SOA to match the latest behavior.
it now unconditionally uses AXFR, so mentioning possible failure of IXFR
doesn't make sense.
commit cd6f223e8f3da567fc320ca4b65948eae9e806ef
Author: JINMEI Tatuya <jinmei at isc.org>
Date: Wed May 29 11:18:11 2013 -0700
[2911] some editorial fixes: align lines more nicely.
commit 946d6e879a77f482a8887d47deb797990dd66544
Author: JINMEI Tatuya <jinmei at isc.org>
Date: Tue May 28 16:01:40 2013 -0700
[2911] more untabify: existed before the branch, but for completeness.
commit 09c5415d6a9db4161f280bf8172d21fd796af068
Author: JINMEI Tatuya <jinmei at isc.org>
Date: Tue May 28 15:51:09 2013 -0700
[2911] fixed a typo
commit 717f7ead4cfc365fac6c8d95aa79f835885b5f90
Author: JINMEI Tatuya <jinmei at isc.org>
Date: Tue May 28 15:45:50 2013 -0700
[2911] removed obsolete part of docstring
commit 397c814090c29f55eafc869702d00e0c15bac0fd
Author: JINMEI Tatuya <jinmei at isc.org>
Date: Tue May 28 15:31:37 2013 -0700
[2911] untabify
commit 68e2c05b1f696136e683765c4d40170a45f721b5
Author: JINMEI Tatuya <jinmei at isc.org>
Date: Thu May 23 23:51:27 2013 -0700
[2911] replace use_ixfr with request_ixfr
commit 7917c9005e71b17be80a4ff27e21858d82a53e6b
Author: JINMEI Tatuya <jinmei at isc.org>
Date: Thu May 23 23:38:58 2013 -0700
[2911] updated BIND10 guide about IXFR-or-AXFR w/ request_ixfr.
commit e3a4ca39f16eaaf8107d688c03f878ef1cbb68b9
Author: JINMEI Tatuya <jinmei at isc.org>
Date: Thu May 23 23:10:43 2013 -0700
[2911] deprecated use_ixfr
commit 0332ecca45c768907451a893663f526139ffc2e2
Author: JINMEI Tatuya <jinmei at isc.org>
Date: Thu May 23 22:47:46 2013 -0700
[2911] updates to retransfer/refresh so it works with new semantics.
actually refresh wasn't defined in spec, so is defined now.
retransfer is revised so it always uses AXFR (BIND 9 compatible).
lettuce tests are adjusted accordingly.
commit 8c0de3883e79cab57fb1dbabd56b8f7515823288
Author: JINMEI Tatuya <jinmei at isc.org>
Date: Wed May 22 17:38:22 2013 -0700
[2911] changed the default policy of request_ixfr to 'yes'.
it should now be safe as we (have already) support fallback and use AXFR
initially.
commit 9c25723eb98513953e93a16a5bb5b83cdaad015e
Author: JINMEI Tatuya <jinmei at isc.org>
Date: Wed May 22 17:35:45 2013 -0700
[2911] suppress IXFR-to-AXFR fallback if IXFR only is specified.
commit 2a2f47d37519d5ec4cd310899d7df482145f5feb
Author: JINMEI Tatuya <jinmei at isc.org>
Date: Wed May 22 17:22:08 2013 -0700
[2911] select initial request type based on config and SOA availability.
commit 60a17382794076ae299eed542eec4b2f8825c120
Author: JINMEI Tatuya <jinmei at isc.org>
Date: Wed May 22 15:32:36 2013 -0700
[2911] updated XfrinConnection ctor so it takes zone soa as a parameter.
this is essentially a refactoring: there's no behavior change.
but it'll help later part of this branch.
commit cac71bc308843f84ef9d1b427f1d401f036ef1b7
Author: JINMEI Tatuya <jinmei at isc.org>
Date: Wed May 22 14:47:14 2013 -0700
[2911] (unrelated style cleanup) folded long lines
commit 73f842441ce26df57c3af30f526b59e3eba884cb
Author: JINMEI Tatuya <jinmei at isc.org>
Date: Wed May 22 14:01:07 2013 -0700
[2911] make sure to use default request_ixfr from the spec, not hardcoding it.
also updated the test so the MockCC refers to the spec default, instead of
hardcoding the values.
commit d43299cb0e6680458c2e831048aaa16acc0d3017
Author: JINMEI Tatuya <jinmei at isc.org>
Date: Tue May 21 21:12:49 2013 -0700
[2911] use request_ixfr, not request_type, just before do_xfrin().
no behavior change yet. also, use AXFR by default, for now, to preserve
the behavior.
commit e532e4f5648f23bec3bc01d01bec47abd966e07f
Author: JINMEI Tatuya <jinmei at isc.org>
Date: Fri Apr 26 16:26:32 2013 -0700
[2911] add request_ixfr zone option to finer policy control on use of IXFR.
currently just recognize the option. no behavior change due to it yet.
commit 537970619d87f2556abcb7d8cb03eff47eda0d54
Author: JINMEI Tatuya <jinmei at isc.org>
Date: Wed May 22 15:16:42 2013 -0700
[2911] refactoring: extract _get_zone_soa from XfrinConnection as free func.
This change will be helpful for later part of this branch.
commit c612e8e22e27210ce85ff4b1f2924686567aada1
Author: JINMEI Tatuya <jinmei at isc.org>
Date: Wed May 22 15:00:07 2013 -0700
[2911] cleanup: remove db_file arg from XfrinConnection ctor.
this class now doesn't rely on the low level interface anymore.
commit 1ab87fb58246a94e4172188ac80061cddcaad3a2
Author: JINMEI Tatuya <jinmei at isc.org>
Date: Wed May 22 14:55:43 2013 -0700
[2911] refactoring: use create_zone() instead of the old datasrc API.
commit f714c4ca5abfd11559854398f6f47ec403a3f7de
Author: JINMEI Tatuya <jinmei at isc.org>
Date: Tue May 21 20:28:07 2013 -0700
[2911] more refactoring: unify xfr command handling into a single method.
commit 4acf38036ce68ae59b2da053af471bd0ec40ecae
Author: JINMEI Tatuya <jinmei at isc.org>
Date: Tue May 21 17:30:50 2013 -0700
[2911] refactoring: extract xfr command handling into separate methods.
no behavior change.
-----------------------------------------------------------------------
Summary of changes:
doc/guide/bind10-guide.xml | 125 ++++--
src/bin/xfrin/b10-xfrin.xml | 2 +-
src/bin/xfrin/tests/xfrin_test.py | 256 +++++++----
src/bin/xfrin/xfrin.py.in | 444 +++++++++++++-------
src/bin/xfrin/xfrin.spec | 56 ++-
src/bin/xfrin/xfrin_messages.mes | 35 +-
.../configurations/ixfr-out/testset1-config.db | 1 -
.../xfrin/retransfer_slave_diffs.conf | 3 +-
.../xfrin/retransfer_slave_notify.conf.orig | 3 +-
.../xfrin/retransfer_slave_notify_v4.conf | 3 +-
tests/lettuce/features/xfrin_bind10.feature | 3 +-
11 files changed, 640 insertions(+), 291 deletions(-)
-----------------------------------------------------------------------
diff --git a/doc/guide/bind10-guide.xml b/doc/guide/bind10-guide.xml
index 568bbea..5924f70 100644
--- a/doc/guide/bind10-guide.xml
+++ b/doc/guide/bind10-guide.xml
@@ -2725,11 +2725,8 @@ TODO
<para>
The <command>b10-xfrin</command> process supports both AXFR and
- IXFR. Due to some implementation limitations of the current
- development release, however, it only tries AXFR by default,
- and care should be taken to enable IXFR.
+ IXFR.
</para>
-<!-- TODO: http://bind10.isc.org/ticket/1279 -->
<section>
<title>Configuration for Incoming Zone Transfers</title>
@@ -2763,34 +2760,82 @@ TODO
> <userinput>config set Xfrin/zones[0]/tsig_key "<option>example.key</option>"</userinput>
</section>
- <section>
- <title>Enabling IXFR</title>
- <para>
- As noted above, <command>b10-xfrin</command> uses AXFR for
- zone transfers by default. To enable IXFR for zone transfers
- for a particular zone, set the <varname>use_ixfr</varname>
- configuration parameter to <quote>true</quote>.
- In the above example of configuration sequence, you'll need
- to add the following before performing <userinput>commit</userinput>:
- <screen>> <userinput>config set Xfrin/zones[0]/use_ixfr true</userinput></screen>
- </para>
+ <section id="request_ixfr">
+ <title>Control the use of IXFR</title>
+ <para>
+ By default, <command>b10-xfrin</command> uses IXFR for
+ transferring zones specified in
+ the <varname>Xfrin/zones</varname> list of the configuration,
+ unless it doesn't know the current SOA serial of the zone
+ (including the case where the zone has never transferred or
+ locally loaded), in which case it automatically uses AXFR.
+ If the attempt of IXFR fails, <command>b10-xfrin</command>
+ automatically retries the transfer using AXFR.
+ In general, this works for any master server implementations
+ including those that don't support IXFR and in any local state
+ of the zone. So there should normally be no need to configure
+ on whether to use IXFR.
+ </para>
+
+ <para>
+ In some cases, however, it may be desirable to specify how and
+ whether to use IXFR and AXFR.
+ The <varname>request_ixfr</varname> configuration item under
+ <varname>Xfrin/zones</varname> can be used to control such
+ policies.
+ It can take the following values.
+ </para>
+ <variablelist>
+ <varlistentry>
+ <term>yes</term>
+ <listitem>
+ <simpara>
+ This is the default behavior as described above.
+ </simpara>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>no</term>
+ <listitem>
+ <simpara>
+ Only use AXFR. Note that this value normally shouldn't
+ be needed thanks to the automatic fallback from IXFR to IXFR.
+ A possible case where this value needs to be used is
+ that the master server has a bug and crashes if it
+ receives an IXFR request.
+ </simpara>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>only</term>
+ <listitem>
+ <simpara>
+ Only use IXFR except when the current SOA serial is not
+ known.
+ This value has a severe drawback, that is, if the master
+ server does not support IXFR zone transfers never
+ succeed (except for the very first one, which will use AXFR),
+ and the zone will eventually expire.
+ Therefore it should not be used in general.
+ Still, in some special cases the use of this value may
+ make sense. For example, if the operator is sure that
+ the master server supports IXFR and the zone is very
+ large, they may want to avoid falling back to AXFR as
+ it can be more expensive.
+ </simpara>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+
+ <note>
+ <simpara>
+ There used to be a boolean configuration item named
+ <varname>use_ixfr</varname>.
+ It was deprecated for the finer control described above.
+ The <varname>request_ixfr</varname> item should be used instead.
+ </simpara>
+ </note>
-<!-- TODO: http://bind10.isc.org/ticket/1279 -->
- <note><simpara>
- One reason why IXFR is disabled by default in the current
- release is because it does not support automatic fallback from IXFR to
- AXFR when it encounters a primary server that doesn't support
- outbound IXFR (and, not many existing implementations support
- it). Another, related reason is that it does not use AXFR even
- if it has no knowledge about the zone (like at the very first
- time the secondary server is set up). IXFR requires the
- "current version" of the zone, so obviously it doesn't work
- in this situation and AXFR is the only workable choice.
- The current release of <command>b10-xfrin</command> does not
- make this selection automatically.
- These features will be implemented in a near future
- version, at which point we will enable IXFR by default.
- </simpara></note>
</section>
<!-- TODO:
@@ -2854,6 +2899,23 @@ what if a NOTIFY is sent?
<screen>> <userinput>Xfrin retransfer zone_name="<option>foo.example.org</option>" master=<option>192.0.2.99</option></userinput></screen>
</para>
+
+ <para>
+ The <command>retransfer</command> command always uses AXFR.
+ To use IXFR for a zone that has already been transferred once,
+ use the <command>refresh</command> command.
+ It honors the <varname>Xfrin/zones/request_ixfr</varname>
+ configuration item (see <xref linkend="request_ixfr"/>.), and
+ if it's configured to use IXFR, it will be used.
+ </para>
+
+ <para>
+ Both the <command>retransfer</command>
+ and <command>refresh</command> commands can be used for
+ an initial transfer before setting up secondary
+ configurations.
+ In this case AXFR will be used for the obvious reason.
+ </para>
</section>
<section>
@@ -2879,7 +2941,6 @@ http://bind10.isc.org/wiki/ScalableZoneLoadDesign#a7.2UpdatingaZone
</para>
</section>
-<!-- TODO: can that retransfer be used to identify a new zone? -->
<!-- TODO: what if doesn't exist at that master IP? -->
</chapter>
diff --git a/src/bin/xfrin/b10-xfrin.xml b/src/bin/xfrin/b10-xfrin.xml
index 0da7ce0..5063732 100644
--- a/src/bin/xfrin/b10-xfrin.xml
+++ b/src/bin/xfrin/b10-xfrin.xml
@@ -111,7 +111,7 @@ in separate zonemgr process.
<varname>class</varname> (defaults to <quote>IN</quote>),
<varname>master_addr</varname> (the zone master to transfer from),
<varname>master_port</varname> (defaults to 53),
- <varname>use_ixfr</varname> (defaults to false), and
+ <varname>request_ixfr</varname> (defaults to yes), and
<varname>tsig_key</varname> (optional TSIG key name to use).
The <varname>tsig_key</varname> is specified using a name that
corresponds to one of the TSIG keys configured in the global
diff --git a/src/bin/xfrin/tests/xfrin_test.py b/src/bin/xfrin/tests/xfrin_test.py
index 2305cdb..ae2d14b 100644
--- a/src/bin/xfrin/tests/xfrin_test.py
+++ b/src/bin/xfrin/tests/xfrin_test.py
@@ -129,17 +129,12 @@ class XfrinTestException(Exception):
class XfrinTestTimeoutException(Exception):
pass
-class MockCC(MockModuleCCSession):
- def get_default_value(self, identifier):
- # The returned values should be identical to the spec file
- # XXX: these should be retrieved from the spec file
- # (see MyCCSession of xfrout_test.py.in)
- if identifier == "zones/master_port":
- return TEST_MASTER_PORT
- if identifier == "zones/class":
- return TEST_RRCLASS_STR
- if identifier == "zones/use_ixfr":
- return False
+class MockCC(MockModuleCCSession, ConfigData):
+ def __init__(self):
+ super().__init__()
+ module_spec = isc.config.module_spec_from_file(
+ xfrin.SPECFILE_LOCATION)
+ ConfigData.__init__(self, module_spec)
def add_remote_config_by_name(self, name, callback):
pass
@@ -242,6 +237,10 @@ class MockDataSourceClient():
self.committed_diffs.append(self.diffs)
self.diffs = []
+ def create_zone(self, zone_name):
+ # pretend it just succeeds
+ pass
+
class MockXfrin(Xfrin):
# This is a class attribute of a callable object that specifies a non
# default behavior triggered in _cc_check_command(). Specific test methods
@@ -265,21 +264,21 @@ class MockXfrin(Xfrin):
MockXfrin.check_command_hook()
def xfrin_start(self, zone_name, rrclass, db_file, master_addrinfo,
- tsig_key, request_type, check_soa=True):
+ tsig_key, request_ixfr, 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]
- self.xfrin_started_request_type = request_type
+ self.xfrin_started_request_ixfr = request_ixfr
return Xfrin.xfrin_start(self, zone_name, rrclass, None,
master_addrinfo, tsig_key,
- request_type, check_soa)
+ request_ixfr, check_soa)
class MockXfrinConnection(XfrinConnection):
def __init__(self, sock_map, zone_name, rrclass, datasrc_client,
shutdown_event, master_addr, tsig_key=None):
super().__init__(sock_map, zone_name, rrclass, MockDataSourceClient(),
- shutdown_event, master_addr, TEST_DB_FILE)
+ shutdown_event, master_addr, begin_soa_rrset)
self.query_data = b''
self.reply_data = b''
self.force_time_out = False
@@ -846,7 +845,9 @@ class TestXfrinConnection(unittest.TestCase):
'''
self.conn._zone_name = zone_name
- self.conn._zone_soa = self.conn._get_zone_soa()
+ self.conn._zone_soa = xfrin._get_zone_soa(self.conn._datasrc_client,
+ zone_name,
+ self.conn._rrclass)
class TestAXFR(TestXfrinConnection):
def setUp(self):
@@ -970,7 +971,9 @@ class TestAXFR(TestXfrinConnection):
RRType.IXFR)
self._set_test_zone(Name('dup-soa.example'))
- self.conn._zone_soa = self.conn._get_zone_soa()
+ self.conn._zone_soa = xfrin._get_zone_soa(self.conn._datasrc_client,
+ self.conn._zone_name,
+ self.conn._rrclass)
self.assertRaises(XfrinException, self.conn._create_query,
RRType.IXFR)
@@ -2411,7 +2414,7 @@ class TestXfrinProcess(unittest.TestCase):
# Normal, successful case. We only check that things are cleaned up
# at the tearDown time.
process_xfrin(self, self, TEST_ZONE_NAME, TEST_RRCLASS, None, None,
- self.master, False, None, RRType.AXFR,
+ self.master, False, None, ZoneInfo.REQUEST_IXFR_DISABLED,
self.create_xfrinconn)
def test_process_xfrin_exception_on_connect(self):
@@ -2419,7 +2422,7 @@ class TestXfrinProcess(unittest.TestCase):
# cleaned up.
self.do_raise_on_connect = True
process_xfrin(self, self, TEST_ZONE_NAME, TEST_RRCLASS, None, None,
- self.master, False, None, RRType.AXFR,
+ self.master, False, None, ZoneInfo.REQUEST_IXFR_DISABLED,
self.create_xfrinconn)
def test_process_xfrin_exception_on_close(self):
@@ -2429,7 +2432,7 @@ class TestXfrinProcess(unittest.TestCase):
self.do_raise_on_connect = True
self.do_raise_on_close = True
process_xfrin(self, self, TEST_ZONE_NAME, TEST_RRCLASS, None, None,
- self.master, False, None, RRType.AXFR,
+ self.master, False, None, ZoneInfo.REQUEST_IXFR_DISABLED,
self.create_xfrinconn)
def test_process_xfrin_exception_on_publish(self):
@@ -2437,7 +2440,7 @@ class TestXfrinProcess(unittest.TestCase):
# everything must still be cleaned up.
self.do_raise_on_publish = True
process_xfrin(self, self, TEST_ZONE_NAME, TEST_RRCLASS, None, None,
- self.master, False, None, RRType.AXFR,
+ self.master, False, None, ZoneInfo.REQUEST_IXFR_DISABLED,
self.create_xfrinconn)
class TestXfrin(unittest.TestCase):
@@ -2537,7 +2540,8 @@ class TestXfrin(unittest.TestCase):
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)
+ self.assertEqual(ZoneInfo.REQUEST_IXFR_DISABLED,
+ self.xfr.xfrin_started_request_ixfr)
def test_command_handler_retransfer_short_command1(self):
# try it when only specifying the zone name (of unknown zone)
@@ -2650,8 +2654,9 @@ 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)
+ # By default we use IXFR (with AXFR fallback)
+ self.assertEqual(ZoneInfo.REQUEST_IXFR_FIRST,
+ self.xfr.xfrin_started_request_ixfr)
def test_command_handler_notify(self):
# at this level, refresh is no different than retransfer.
@@ -2734,12 +2739,19 @@ class TestXfrin(unittest.TestCase):
Name(zone_config['tsig_key']).to_text())
else:
self.assertIsNone(zone_info.tsig_key_name)
- if 'use_ixfr' in zone_config and\
- zone_config.get('use_ixfr'):
- self.assertTrue(zone_info.use_ixfr)
- else:
- # if not set, should default to False
- self.assertFalse(zone_info.use_ixfr)
+ if ('request_ixfr' in zone_config and
+ zone_config.get('request_ixfr')):
+ cfg_val = zone_config.get('request_ixfr')
+ val = zone_info.request_ixfr
+ if cfg_val == 'yes':
+ self.assertEqual(ZoneInfo.REQUEST_IXFR_FIRST, val)
+ elif cfg_val == 'no':
+ self.assertEqual(ZoneInfo.REQUEST_IXFR_DISABLED, val)
+ elif cfg_val == 'only':
+ self.assertEqual(ZoneInfo.REQUEST_IXFR_ONLY, val)
+ else: # check the default
+ self.assertEqual(ZoneInfo.REQUEST_IXFR_FIRST,
+ zone_info.request_ixfr)
def test_config_handler_zones(self):
# This test passes a number of good and bad configs, and checks whether
@@ -2751,7 +2763,7 @@ class TestXfrin(unittest.TestCase):
{ 'name': 'test.example.',
'master_addr': '192.0.2.1',
'master_port': 53,
- 'use_ixfr': False
+ 'request_ixfr': 'yes'
}
]}
self.assertEqual(self.xfr.config_handler(config1)['result'][0], 0)
@@ -2763,12 +2775,24 @@ class TestXfrin(unittest.TestCase):
'master_addr': '192.0.2.2',
'master_port': 53,
'tsig_key': "example.com:SFuWd/q99SzF8Yzd1QbB9g==",
- 'use_ixfr': True
+ 'request_ixfr': 'no'
}
]}
self.assertEqual(self.xfr.config_handler(config2)['result'][0], 0)
self._check_zones_config(config2)
+ config3 = {'transfers_in': 4,
+ 'zones': [
+ { 'name': 'test.example.',
+ 'master_addr': '192.0.2.2',
+ 'master_port': 53,
+ 'tsig_key': "example.com:SFuWd/q99SzF8Yzd1QbB9g==",
+ 'request_ixfr': 'only'
+ }
+ ]}
+ self.assertEqual(self.xfr.config_handler(config3)['result'][0], 0)
+ self._check_zones_config(config3)
+
# test that configuring the zone multiple times fails
zones = { 'transfers_in': 5,
'zones': [
@@ -2783,7 +2807,7 @@ class TestXfrin(unittest.TestCase):
]}
self.assertEqual(self.xfr.config_handler(zones)['result'][0], 1)
# since this has failed, we should still have the previous config
- self._check_zones_config(config2)
+ self._check_zones_config(config3)
zones = { 'zones': [
{ 'name': 'test.example.',
@@ -2793,7 +2817,7 @@ class TestXfrin(unittest.TestCase):
}
]}
self.assertEqual(self.xfr.config_handler(zones)['result'][0], 1)
- self._check_zones_config(config2)
+ self._check_zones_config(config3)
zones = { 'zones': [
{ 'master_addr': '192.0.2.4',
@@ -2802,7 +2826,7 @@ class TestXfrin(unittest.TestCase):
]}
self.assertEqual(self.xfr.config_handler(zones)['result'][0], 1)
# since this has failed, we should still have the previous config
- self._check_zones_config(config2)
+ self._check_zones_config(config3)
zones = { 'zones': [
{ 'name': 'bad..zone.',
@@ -2812,7 +2836,7 @@ class TestXfrin(unittest.TestCase):
]}
self.assertEqual(self.xfr.config_handler(zones)['result'][0], 1)
# since this has failed, we should still have the previous config
- self._check_zones_config(config2)
+ self._check_zones_config(config3)
zones = { 'zones': [
{ 'name': '',
@@ -2822,7 +2846,7 @@ class TestXfrin(unittest.TestCase):
]}
self.assertEqual(self.xfr.config_handler(zones)['result'][0], 1)
# since this has failed, we should still have the previous config
- self._check_zones_config(config2)
+ self._check_zones_config(config3)
zones = { 'zones': [
{ 'name': 'test.example',
@@ -2832,7 +2856,7 @@ class TestXfrin(unittest.TestCase):
]}
self.assertEqual(self.xfr.config_handler(zones)['result'][0], 1)
# since this has failed, we should still have the previous config
- self._check_zones_config(config2)
+ self._check_zones_config(config3)
zones = { 'zones': [
{ 'name': 'test.example',
@@ -2842,7 +2866,7 @@ class TestXfrin(unittest.TestCase):
]}
self.assertEqual(self.xfr.config_handler(zones)['result'][0], 1)
# since this has failed, we should still have the previous config
- self._check_zones_config(config2)
+ self._check_zones_config(config3)
zones = { 'zones': [
{ 'name': 'test.example',
@@ -2854,7 +2878,7 @@ class TestXfrin(unittest.TestCase):
]}
self.assertEqual(self.xfr.config_handler(zones)['result'][0], 1)
# since this has failed, we should still have the previous config
- self._check_zones_config(config2)
+ self._check_zones_config(config3)
# let's also add a zone that is correct too, and make sure
# that the new config is not partially taken
@@ -2871,81 +2895,111 @@ class TestXfrin(unittest.TestCase):
]}
self.assertEqual(self.xfr.config_handler(zones)['result'][0], 1)
# since this has failed, we should still have the previous config
- self._check_zones_config(config2)
+ self._check_zones_config(config3)
+
+ # invalid request_ixfr value
+ zones = { 'zones': [
+ { 'name': 'test.example',
+ 'master_addr': '192.0.2.7',
+ 'request_ixfr': 'bad value'
+ }
+ ]}
+ self.assertEqual(self.xfr.config_handler(zones)['result'][0], 1)
+ # since this has failed, we should still have the previous config
+ self._check_zones_config(config3)
def test_config_handler_zones_default(self):
# Checking it some default config values apply. Using a separate
# test case for a fresh xfr object.
config = { 'zones': [
{ 'name': 'test.example.',
- 'master_addr': '192.0.2.1',
- 'master_port': 53,
+ 'master_addr': '192.0.2.1',
+ 'master_port': 53,
}
]}
self.assertEqual(self.xfr.config_handler(config)['result'][0], 0)
self._check_zones_config(config)
- def common_ixfr_setup(self, xfr_mode, use_ixfr, tsig_key_str = None):
+ def test_config_handler_use_ixfr(self):
+ # use_ixfr was deprecated and explicitly rejected for now.
+ config = { 'zones': [
+ { 'name': 'test.example.',
+ 'master_addr': '192.0.2.1',
+ 'master_port': 53,
+ 'use_ixfr': True
+ }
+ ]}
+ self.assertEqual(self.xfr.config_handler(config)['result'][0], 1)
+
+ def common_ixfr_setup(self, xfr_mode, request_ixfr, tsig_key_str=None):
# This helper method explicitly sets up a zone configuration with
- # use_ixfr, and invokes either retransfer or refresh.
+ # request_ixfr, 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',
'tsig_key': tsig_key_str,
- 'use_ixfr': use_ixfr}]}
+ 'request_ixfr': request_ixfr}]}
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', True)
- self.assertEqual(RRType.IXFR, self.xfr.xfrin_started_request_type)
+ # retransfer always uses AXFR (disabling IXFR), regardless of
+ # request_ixfr value
+ self.common_ixfr_setup('retransfer', 'yes')
+ self.assertEqual(ZoneInfo.REQUEST_IXFR_DISABLED,
+ self.xfr.xfrin_started_request_ixfr)
def test_command_handler_refresh_ixfr_enabled(self):
- # Same for refresh
- self.common_ixfr_setup('refresh', True)
- self.assertEqual(RRType.IXFR, self.xfr.xfrin_started_request_type)
+ # for refresh, it honors zone configuration if defined (the default
+ # case is covered in test_command_handler_refresh
+ self.common_ixfr_setup('refresh', 'no')
+ self.assertEqual(ZoneInfo.REQUEST_IXFR_DISABLED,
+ self.xfr.xfrin_started_request_ixfr)
def test_command_handler_retransfer_with_tsig(self):
- self.common_ixfr_setup('retransfer', False, 'example.com.key')
- self.assertEqual(RRType.AXFR, self.xfr.xfrin_started_request_type)
+ self.common_ixfr_setup('retransfer', 'no', 'example.com.key')
+ self.assertEqual(ZoneInfo.REQUEST_IXFR_DISABLED,
+ self.xfr.xfrin_started_request_ixfr)
def test_command_handler_retransfer_with_tsig_bad_key(self):
# bad keys should not reach xfrin, but should they somehow,
# they are ignored (and result in 'key not found' + error log).
self.assertRaises(XfrinZoneInfoException, self.common_ixfr_setup,
- 'retransfer', False, 'bad.key')
+ 'retransfer', 'no', 'bad.key')
def test_command_handler_retransfer_with_tsig_unknown_key(self):
self.assertRaises(XfrinZoneInfoException, self.common_ixfr_setup,
- 'retransfer', False, 'no.such.key')
+ 'retransfer', 'no', 'no.such.key')
def test_command_handler_refresh_with_tsig(self):
- self.common_ixfr_setup('refresh', False, 'example.com.key')
- self.assertEqual(RRType.AXFR, self.xfr.xfrin_started_request_type)
+ self.common_ixfr_setup('refresh', 'no', 'example.com.key')
+ self.assertEqual(ZoneInfo.REQUEST_IXFR_DISABLED,
+ self.xfr.xfrin_started_request_ixfr)
def test_command_handler_refresh_with_tsig_bad_key(self):
# bad keys should not reach xfrin, but should they somehow,
# they are ignored (and result in 'key not found' + error log).
self.assertRaises(XfrinZoneInfoException, self.common_ixfr_setup,
- 'refresh', False, 'bad.key')
+ 'refresh', 'no', 'bad.key')
def test_command_handler_refresh_with_tsig_unknown_key(self):
self.assertRaises(XfrinZoneInfoException, self.common_ixfr_setup,
- 'refresh', False, 'no.such.key')
+ 'refresh', 'no', 'no.such.key')
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', False)
- self.assertEqual(RRType.AXFR, self.xfr.xfrin_started_request_type)
+ self.common_ixfr_setup('retransfer', 'no')
+ self.assertEqual(ZoneInfo.REQUEST_IXFR_DISABLED,
+ self.xfr.xfrin_started_request_ixfr)
def test_command_handler_refresh_ixfr_disabled(self):
# Same for refresh
- self.common_ixfr_setup('refresh', False)
- self.assertEqual(RRType.AXFR, self.xfr.xfrin_started_request_type)
+ self.common_ixfr_setup('refresh', 'no')
+ self.assertEqual(ZoneInfo.REQUEST_IXFR_DISABLED,
+ self.xfr.xfrin_started_request_ixfr)
class TestXfrinMemoryZones(unittest.TestCase):
def setUp(self):
@@ -3157,6 +3211,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):
"""
@@ -3212,7 +3273,7 @@ class TestXfrinProcess(unittest.TestCase):
"""
pass
- def __do_test(self, rets, transfers, request_type):
+ def __do_test(self, rets, transfers, request_ixfr):
"""
Do the actual test. The request type, prepared sucesses/failures
and expected sequence of transfers is passed to specify what test
@@ -3221,8 +3282,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_type, 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
@@ -3233,7 +3294,7 @@ class TestXfrinProcess(unittest.TestCase):
"""
Everything OK the first time, over IXFR.
"""
- self.__do_test([XFRIN_OK], [RRType.IXFR], RRType.IXFR)
+ self.__do_test([XFRIN_OK], [RRType.IXFR], ZoneInfo.REQUEST_IXFR_FIRST)
# Check there was loadzone command
self.assertTrue(self._send_cc_session.send_called)
self.assertTrue(self._send_cc_session.send_called_correctly)
@@ -3244,23 +3305,27 @@ class TestXfrinProcess(unittest.TestCase):
"""
Everything OK the first time, over AXFR.
"""
- self.__do_test([XFRIN_OK], [RRType.AXFR], RRType.AXFR)
+ self.__do_test([XFRIN_OK], [RRType.AXFR],
+ ZoneInfo.REQUEST_IXFR_DISABLED)
def test_axfr_fail(self):
"""
The transfer failed over AXFR. Should not be retried (we don't expect
- to fail on AXFR, but succeed on IXFR and we didn't use IXFR in the first
- place for some reason.
+ to fail on AXFR, but succeed on IXFR and we didn't use IXFR in the
+ first place for some reason.
+
"""
- self.__do_test([XFRIN_FAIL], [RRType.AXFR], RRType.AXFR)
+ self.__do_test([XFRIN_FAIL], [RRType.AXFR],
+ ZoneInfo.REQUEST_IXFR_DISABLED)
def test_ixfr_fallback(self):
"""
- The transfer fails over IXFR, but suceeds over AXFR. It should fall back
- to it and say everything is OK.
+ The transfer fails over IXFR, but suceeds over AXFR. It should fall
+ back to it and say everything is OK.
+
"""
self.__do_test([XFRIN_FAIL, XFRIN_OK], [RRType.IXFR, RRType.AXFR],
- RRType.IXFR)
+ ZoneInfo.REQUEST_IXFR_FIRST)
def test_ixfr_fail(self):
"""
@@ -3268,18 +3333,55 @@ class TestXfrinProcess(unittest.TestCase):
(only once) and should try both before giving up.
"""
self.__do_test([XFRIN_FAIL, XFRIN_FAIL],
- [RRType.IXFR, RRType.AXFR], RRType.IXFR)
+ [RRType.IXFR, RRType.AXFR], ZoneInfo.REQUEST_IXFR_FIRST)
+
+ def test_ixfr_only(self):
+ """
+ The transfer fails and IXFR_ONLY is specified. It shouldn't fall
+ back to AXFR and should report failure.
+ """
+ self.__do_test([XFRIN_FAIL], [RRType.IXFR], ZoneInfo.REQUEST_IXFR_ONLY)
def test_send_loadzone(self):
"""
Check the loadzone command is sent after successful transfer.
"""
- self.__do_test([XFRIN_OK], [RRType.IXFR], RRType.IXFR)
+ self.__do_test([XFRIN_OK], [RRType.IXFR],
+ ZoneInfo.REQUEST_IXFR_FIRST)
self.assertTrue(self._send_cc_session.send_called)
self.assertTrue(self._send_cc_session.send_called_correctly)
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.
diff --git a/src/bin/xfrin/xfrin.py.in b/src/bin/xfrin/xfrin.py.in
index 2066404..f127a93 100755
--- a/src/bin/xfrin/xfrin.py.in
+++ b/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
@@ -565,18 +566,25 @@ class XfrinConnection(asyncore.dispatcher):
def __init__(self,
sock_map, zone_name, rrclass, datasrc_client,
- shutdown_event, master_addrinfo, db_file, tsig_key=None,
+ shutdown_event, master_addrinfo, zone_soa, tsig_key=None,
idle_timeout=60):
- '''Constructor of the XfirnConnection class.
+ """Constructor of the XfirnConnection class.
+
+ Parameters:
+ sock_map: empty dict, used with asyncore.
+ zone_name (dns.Name): Zone name.
+ rrclass (dns.RRClass): Zone RR class.
+ datasrc_client (DataSourceClient): the data source client object
+ used for the XFR session.
+ shutdown_event (threading.Event): used for synchronization with
+ parent thread.
+ master_addrinfo (tuple: (sock family, sock type, sockaddr)):
+ address and port of the master server.
+ zone_soa (RRset or None): SOA RRset of zone's current SOA or None
+ if it's not available.
+ idle_timeout (int): max idle time for read data from socket.
- db_file: SQLite3 DB file. Unforutnately we still need this for
- temporary workaround in _get_zone_soa(). This should be
- removed when we eliminate the need for the workaround.
- 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.
-
- '''
+ """
asyncore.dispatcher.__init__(self, map=sock_map)
@@ -595,9 +603,8 @@ class XfrinConnection(asyncore.dispatcher):
self._rrclass = rrclass
# Data source handler
- self._db_file = db_file
self._datasrc_client = datasrc_client
- self._zone_soa = self._get_zone_soa()
+ self._zone_soa = zone_soa
self._sock_map = sock_map
self._soa_rr_count = 0
@@ -626,54 +633,6 @@ class XfrinConnection(asyncore.dispatcher):
self.create_socket(self._master_addrinfo[0], self._master_addrinfo[1])
self.socket.setblocking(1)
- def _get_zone_soa(self):
- '''Retrieve the current SOA RR of the zone to be transferred.
-
- It will be used for various purposes in subsequent xfr protocol
- processing. It is validly possible that the zone is currently
- empty and therefore doesn't have an SOA, so this method doesn't
- consider it an error and returns None in such a case. It may or
- may not result in failure in the actual processing depending on
- how the SOA is used.
-
- When the zone has an SOA RR, this method makes sure that it's
- valid, i.e., it has exactly one RDATA; if it is not the case
- this method returns None.
-
- If the underlying data source doesn't even know the zone, this method
- tries to provide backward compatible behavior where xfrin is
- responsible for creating zone in the corresponding DB table.
- For a longer term we should deprecate this behavior by introducing
- more generic zone management framework, but at the moment we try
- to not surprise existing users. (Note also that the part of
- providing the compatible behavior uses the old data source API.
- We'll deprecate this API in a near future, too).
-
- '''
- # 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:
- # The data source doesn't know the zone. For now, we provide
- # backward compatibility and creates a new one ourselves.
- isc.datasrc.sqlite3_ds.load(self._db_file,
- self._zone_name.to_text(),
- lambda : [])
- logger.warn(XFRIN_ZONE_CREATED, self.zone_str())
- # try again
- result, finder = self._datasrc_client.find_zone(self._zone_name)
- if result != DataSourceClient.SUCCESS:
- return None
- result, soa_rrset, _ = finder.find(self._zone_name, RRType.SOA)
- if result != ZoneFinder.SUCCESS:
- logger.info(XFRIN_ZONE_NO_SOA, self.zone_str())
- return None
- if soa_rrset.get_rdata_count() != 1:
- logger.warn(XFRIN_ZONE_MULTIPLE_SOA, self.zone_str(),
- soa_rrset.get_rdata_count())
- return None
- return soa_rrset
-
def __set_xfrstate(self, new_state):
self.__state = new_state
@@ -746,8 +705,9 @@ class XfrinConnection(asyncore.dispatcher):
msg = self._create_query(query_type)
render = MessageRenderer()
- # XXX Currently, python wrapper doesn't accept 'None' parameter in this case,
- # we should remove the if statement and use a universal interface later.
+ # XXX Currently, python wrapper doesn't accept 'None' parameter in this
+ # case, we should remove the if statement and use a universal
+ # interface later.
if self._tsig_key is not None:
self._tsig_ctx = self._tsig_ctx_creator(self._tsig_key)
msg.to_wire(render, self._tsig_ctx)
@@ -1114,16 +1074,95 @@ class XfrinConnection(asyncore.dispatcher):
return False
+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 (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
+ empty and therefore doesn't have an SOA, so this method doesn't
+ consider it an error and returns None in such a case. It may or
+ may not result in failure in the actual processing depending on
+ how the SOA is used.
+
+ When the zone has an SOA RR, this method makes sure that it's
+ valid, i.e., it has exactly one RDATA; if it is not the case
+ this method returns None.
+
+ If the underlying data source doesn't even know the zone, this method
+ tries to provide backward compatible behavior where xfrin is
+ responsible for creating zone in the corresponding DB table.
+ For a longer term we should deprecate this behavior by introducing
+ more generic zone management framework, but at the moment we try
+ to not surprise existing users.
+
+ """
+ # 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)
+ if result != DataSourceClient.SUCCESS:
+ # The data source doesn't know the zone. For now, we provide
+ # backward compatibility and creates a new one ourselves.
+ # For longer term, we should probably separate this level of zone
+ # management outside of xfrin.
+ datasrc_client.create_zone(zone_name)
+ logger.warn(XFRIN_ZONE_CREATED, format_zone_str(zone_name, zone_class))
+ # try again
+ result, finder = datasrc_client.find_zone(zone_name)
+ if result != DataSourceClient.SUCCESS:
+ return None
+ result, soa_rrset, _ = finder.find(zone_name, RRType.SOA)
+ if result != ZoneFinder.SUCCESS:
+ logger.info(XFRIN_ZONE_NO_SOA, format_zone_str(zone_name, zone_class))
+ return None
+ if soa_rrset.get_rdata_count() != 1:
+ logger.warn(XFRIN_ZONE_MULTIPLE_SOA,
+ format_zone_str(zone_name, zone_class),
+ soa_rrset.get_rdata_count())
+ 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_type, conn_class):
+ request_ixfr, conn_class):
conn = None
exception = None
ret = XFRIN_FAIL
try:
# 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.
+ # 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.
@@ -1131,38 +1170,55 @@ def __process_xfrin(server, zone_name, rrclass, db_file,
if db_file is not None:
# temporary hardcoded sqlite initialization. Once we decide on
# the config specification, we need to update this (TODO)
- # this may depend on #1207, or any follow-up ticket created for #1207
+ # this may depend on #1207, or any follow-up ticket created for
+ # #1207
datasrc_type = "sqlite3"
datasrc_config = "{ \"database_file\": \"" + db_file + "\"}"
datasrc_client = DataSourceClient(datasrc_type, datasrc_config)
- # Create a TCP connection for the XFR session and perform the operation.
+ # 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.
sock_map = {}
- # In case we were asked to do IXFR and that one fails, we try again with
- # AXFR. But only if we could actually connect to the server.
+ # In case we were asked to do IXFR and that one fails, we try again
+ # with AXFR. But only if we could actually connect to the server.
#
- # So we start with retry as True, which is set to false on each attempt.
- # In the case of connected but failed IXFR, we set it to true once again.
+ # So we start with retry as True, which is set to false on each
+ # attempt. In the case of connected but failed IXFR, we set it to true
+ # once again.
retry = True
while retry:
retry = False
conn = conn_class(sock_map, zone_name, rrclass, datasrc_client,
- shutdown_event, master_addrinfo, db_file,
+ shutdown_event, master_addrinfo, zone_soa,
tsig_key)
conn.init_socket()
ret = XFRIN_FAIL
if conn.connect_to_master():
ret = conn.do_xfrin(check_soa, request_type)
if ret == XFRIN_FAIL and request_type == RRType.IXFR:
- # IXFR failed for some reason. It might mean the server can't
- # handle it, or we don't have the zone or we are out of sync or
- # whatever else. So we retry with with AXFR, as it may succeed
- # in many such cases.
- retry = True
- request_type = RRType.AXFR
- logger.warn(XFRIN_XFR_TRANSFER_FALLBACK, conn.zone_str())
- conn.close()
- conn = None
+ # IXFR failed for some reason. It might mean the server
+ # can't handle it, or we don't have the zone or we are out
+ # of sync or whatever else. So we retry with with AXFR, as
+ # it may succeed in many such cases; if "IXFR only" is
+ # specified in request_ixfr, however, we suppress the
+ # fallback.
+ if request_ixfr == ZoneInfo.REQUEST_IXFR_ONLY:
+ logger.warn(XFRIN_XFR_TRANSFER_FALLBACK_DISABLED,
+ conn.zone_str())
+ else:
+ retry = True
+ request_type = RRType.AXFR
+ logger.warn(XFRIN_XFR_TRANSFER_FALLBACK,
+ conn.zone_str())
+ conn.close()
+ conn = None
except Exception as ex:
# If exception happens, just remember it here so that we can re-raise
@@ -1188,7 +1244,7 @@ def __process_xfrin(server, zone_name, rrclass, db_file,
def process_xfrin(server, xfrin_recorder, zone_name, rrclass, db_file,
shutdown_event, master_addrinfo, check_soa, tsig_key,
- request_type, conn_class=XfrinConnection):
+ request_ixfr, conn_class=XfrinConnection):
# Even if it should be rare, the main process of xfrin session can
# raise an exception. In order to make sure the lock in xfrin_recorder
# is released in any cases, we delegate the main part to the helper
@@ -1198,14 +1254,17 @@ def process_xfrin(server, xfrin_recorder, zone_name, rrclass, db_file,
try:
__process_xfrin(server, zone_name, rrclass, db_file,
shutdown_event, master_addrinfo, check_soa, tsig_key,
- request_type, conn_class)
+ request_ixfr, conn_class)
except Exception as ex:
# don't log it until we complete decrement().
exception = ex
xfrin_recorder.decrement(zone_name)
if exception is not None:
- typestr = "AXFR" if request_type == RRType.AXFR else "IXFR"
+ if request_ixfr == ZoneInfo.REQUEST_IXFR_DISABLED:
+ typestr = "AXFR"
+ else:
+ typestr = "IXFR"
logger.error(XFRIN_XFR_PROCESS_FAILURE, typestr, zone_name.to_text(),
str(rrclass), str(exception))
@@ -1238,10 +1297,26 @@ class XfrinRecorder:
return ret
class ZoneInfo:
+ # Internal values corresponding to request_ixfr
+ REQUEST_IXFR_FIRST = 0 # request_ixfr=yes, use IXFR 1st then AXFR
+ REQUEST_IXFR_ONLY = 1 # request_ixfr=only, use IXFR only
+ REQUEST_IXFR_DISABLED = 2 # request_ixfr=no, AXFR-only
+
+ # Map from configuration values for request_ixfr to internal values
+ # This is a constant; don't modify.
+ REQUEST_IXFR_CFG_TO_VAL = { 'yes': REQUEST_IXFR_FIRST,
+ 'only': REQUEST_IXFR_ONLY,
+ 'no': REQUEST_IXFR_DISABLED }
+
def __init__(self, config_data, module_cc):
"""Creates a zone_info with the config data element as
specified by the 'zones' list in xfrin.spec. Module_cc is
needed to get the defaults from the specification"""
+ # Handle deprecated config parameter explicitly for the moment.
+ if config_data.get('use_ixfr') is not None:
+ raise XfrinZoneInfoException('use_ixfr was deprecated.' +
+ 'use rquest_ixfr')
+
self._module_cc = module_cc
self.set_name(config_data.get('name'))
self.set_master_addr(config_data.get('master_addr'))
@@ -1249,7 +1324,17 @@ class ZoneInfo:
self.set_master_port(config_data.get('master_port'))
self.set_zone_class(config_data.get('class'))
self.set_tsig_key_name(config_data.get('tsig_key'))
- self.set_use_ixfr(config_data.get('use_ixfr'))
+ self.set_request_ixfr(config_data.get('request_ixfr'))
+
+ @property
+ def request_ixfr(self):
+ """Policy on the use of IXFR.
+
+ Possible values are REQUEST_IXFR_xxx, internally stored in
+ __request_ixfr, read-only outside of the class.
+
+ """
+ return self.__request_ixfr
def set_name(self, name_str):
"""Set the name for this zone given a name string.
@@ -1336,16 +1421,15 @@ class ZoneInfo:
else:
return key
- def set_use_ixfr(self, use_ixfr):
- """Set use_ixfr. If set to True, it will use
- IXFR for incoming transfers. If set to False, it will use AXFR.
- At this moment there is no automatic fallback"""
- # TODO: http://bind10.isc.org/ticket/1279
- if use_ixfr is None:
- self.use_ixfr = \
- self._module_cc.get_default_value("zones/use_ixfr")
- else:
- self.use_ixfr = use_ixfr
+ def set_request_ixfr(self, request_ixfr):
+ if request_ixfr is None:
+ request_ixfr = \
+ self._module_cc.get_default_value("zones/request_ixfr")
+ try:
+ self.__request_ixfr = self.REQUEST_IXFR_CFG_TO_VAL[request_ixfr]
+ except KeyError:
+ raise XfrinZoneInfoException('invalid value for request_ixfr: ' +
+ request_ixfr)
def get_master_addr_info(self):
return (self.master_addr.family, socket.SOCK_STREAM,
@@ -1518,6 +1602,86 @@ class Xfrin:
continue
th.join()
+ def __validate_notify_addr(self, notify_addr, zone_str, zone_info):
+ """Validate notify source as a destination for xfr source.
+
+ This is called from __handle_xfr_command in case xfr is triggered
+ by ZoneMgr either due to incoming Notify or periodic refresh event.
+
+ """
+ if zone_info is None:
+ # TODO what to do? no info known about zone. defaults?
+ errmsg = "Got notification to retransfer unknown zone " + zone_str
+ logger.info(XFRIN_RETRANSFER_UNKNOWN_ZONE, zone_str)
+ return create_answer(1, errmsg)
+ else:
+ master_addr = zone_info.get_master_addr_info()
+ if (notify_addr[0] != master_addr[0] or
+ notify_addr[2] != master_addr[2]):
+ notify_addr_str = format_addrinfo(notify_addr)
+ master_addr_str = format_addrinfo(master_addr)
+ errmsg = "Got notification for " + zone_str\
+ + "from unknown address: " + notify_addr_str;
+ logger.info(XFRIN_NOTIFY_UNKNOWN_MASTER, zone_str,
+ notify_addr_str, master_addr_str)
+ return create_answer(1, errmsg)
+
+ # Notified address is okay
+ return None
+
+ def __get_running_request_ixfr(self, arg_request_ixfr, zone_info):
+ """Determine the request_ixfr policy for a specific transfer.
+
+ This is a dedicated subroutine of __handle_xfr_command.
+
+ """
+ # If explicitly specified, use it.
+ if arg_request_ixfr is not None:
+ return arg_request_ixfr
+ # Otherwise, if zone info is known, use its value.
+ if zone_info is not None:
+ return zone_info.request_ixfr
+ # Otherwise, use the default value for ZoneInfo
+ request_ixfr_def = \
+ self._module_cc.get_default_value("zones/request_ixfr")
+ return ZoneInfo.REQUEST_IXFR_CFG_TO_VAL[request_ixfr_def]
+
+ def __handle_xfr_command(self, args, arg_db, check_soa, addr_validator,
+ request_ixfr):
+ """Common subroutine for handling transfer commands.
+
+ This helper method unifies both cases of transfer command from
+ ZoneMgr or from a user. Depending on who invokes the transfer,
+ details of validation and parameter selection slightly vary.
+ These conditions are passed through parameters and handled in the
+ unified code of this method accordingly.
+
+ If this is from the ZoneMgr due to incoming notify, zone transfer
+ should start from the notify's source address as long as it's
+ configured as a master address, according to RFC1996. The current
+ implementation conforms to it in a limited way: we can only set one
+ master address. Once we add the ability to have multiple master
+ addresses, we should check if it matches one of them, and then use it.
+
+ In case of transfer command from the user, if the command specifies
+ the master address, use that one; otherwise try to use a configured
+ master address for the zone.
+
+ """
+ (zone_name, rrclass) = self._parse_zone_name_and_class(args)
+ master_addr = self._parse_master_and_port(args, zone_name, rrclass)
+ zone_info = self._get_zone_info(zone_name, rrclass)
+ tsig_key = None if zone_info is None else zone_info.get_tsig_key()
+ db_file = arg_db or self._get_db_file()
+ zone_str = format_zone_str(zone_name, rrclass) # for logging
+ answer = addr_validator(master_addr, zone_str, zone_info)
+ if answer is not None:
+ return answer
+ request_ixfr = self.__get_running_request_ixfr(request_ixfr, zone_info)
+ ret = self.xfrin_start(zone_name, rrclass, db_file, master_addr,
+ tsig_key, request_ixfr, check_soa)
+ return create_answer(ret[0], ret[1])
+
def command_handler(self, command, args):
logger.debug(DBG_XFRIN_TRACE, XFRIN_RECEIVED_COMMAND, command)
answer = create_answer(0)
@@ -1525,69 +1689,26 @@ class Xfrin:
if command == 'shutdown':
self._shutdown_event.set()
elif command == 'notify' or command == REFRESH_FROM_ZONEMGR:
- # Xfrin receives the refresh/notify command from zone manager.
- # notify command maybe has the parameters which
- # specify the notifyfrom address and port, according the RFC1996, zone
- # transfer should starts first from the notifyfrom, but now, let 'TODO' it.
- # (using the value now, while we can only set one master address, would be
- # a security hole. Once we add the ability to have multiple master addresses,
- # we should check if it matches one of them, and then use it.)
- (zone_name, rrclass) = self._parse_zone_name_and_class(args)
- zone_str = format_zone_str(zone_name, rrclass)
- zone_info = self._get_zone_info(zone_name, rrclass)
- notify_addr = self._parse_master_and_port(args, zone_name,
- rrclass)
- if zone_info is None:
- # TODO what to do? no info known about zone. defaults?
- errmsg = "Got notification to retransfer unknown zone " + zone_str
- logger.info(XFRIN_RETRANSFER_UNKNOWN_ZONE, zone_str)
- answer = create_answer(1, errmsg)
- else:
- request_type = RRType.AXFR
- if zone_info.use_ixfr:
- request_type = RRType.IXFR
- master_addr = zone_info.get_master_addr_info()
- if notify_addr[0] == master_addr[0] and\
- notify_addr[2] == master_addr[2]:
- ret = self.xfrin_start(zone_name,
- rrclass,
- self._get_db_file(),
- master_addr,
- zone_info.get_tsig_key(), request_type,
- True)
- answer = create_answer(ret[0], ret[1])
- else:
- notify_addr_str = format_addrinfo(notify_addr)
- master_addr_str = format_addrinfo(master_addr)
- errmsg = "Got notification for " + zone_str\
- + "from unknown address: " + notify_addr_str;
- logger.info(XFRIN_NOTIFY_UNKNOWN_MASTER, zone_str,
- notify_addr_str, master_addr_str)
- answer = create_answer(1, errmsg)
-
- elif command == 'retransfer' or command == 'refresh':
- # Xfrin receives the retransfer/refresh from cmdctl(sent by bindctl).
- # If the command has specified master address, do transfer from the
- # master address, or else do transfer from the configured masters.
- (zone_name, rrclass) = self._parse_zone_name_and_class(args)
- master_addr = self._parse_master_and_port(args, zone_name,
- rrclass)
- zone_info = self._get_zone_info(zone_name, rrclass)
- tsig_key = None
- request_type = RRType.AXFR
- if zone_info:
- tsig_key = zone_info.get_tsig_key()
- if zone_info.use_ixfr:
- 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, request_type,
- (False if command == 'retransfer' else True))
- answer = create_answer(ret[0], ret[1])
-
+ # refresh/notify command from zone manager.
+ # The address has to be validated, db_file is local only,
+ # and always perform SOA check.
+ addr_validator = \
+ lambda x, y, z: self.__validate_notify_addr(x, y, z)
+ answer = self.__handle_xfr_command(args, None, True,
+ addr_validator, None)
+ elif command == 'retransfer':
+ # retransfer from cmdctl (sent by bindctl).
+ # No need for address validation, db_file may be specified
+ # with the command, and skip SOA check, always use AXFR.
+ answer = self.__handle_xfr_command(
+ args, args.get('db_file'), False, lambda x, y, z: None,
+ ZoneInfo.REQUEST_IXFR_DISABLED)
+ elif command == 'refresh':
+ # retransfer from cmdctl (sent by bindctl). similar to
+ # retransfer, but do SOA check, and honor request_ixfr config.
+ answer = self.__handle_xfr_command(
+ args, args.get('db_file'), True, lambda x, y, z: None,
+ None)
# return statistics data to the stats daemon
elif command == "getstats":
# The log level is here set to debug in order to avoid
@@ -1608,7 +1729,8 @@ class Xfrin:
if zone_name_str is None:
raise XfrinException('zone name should be provided')
- return (_check_zone_name(zone_name_str), _check_zone_class(args.get('zone_class')))
+ return (_check_zone_name(zone_name_str),
+ _check_zone_class(args.get('zone_class')))
def _parse_master_and_port(self, args, zone_name, zone_class):
"""
@@ -1725,7 +1847,7 @@ class Xfrin:
self._cc_check_command()
def xfrin_start(self, zone_name, rrclass, db_file, master_addrinfo,
- tsig_key, request_type, check_soa=True):
+ tsig_key, request_ixfr, check_soa=True):
if "pydnspp" not in sys.modules:
return (1, "xfrin failed, can't load dns message python library: 'pydnspp'")
@@ -1744,7 +1866,7 @@ class Xfrin:
db_file,
self._shutdown_event,
master_addrinfo, check_soa,
- tsig_key, request_type))
+ tsig_key, request_ixfr))
xfrin_thread.start()
return (0, 'zone xfrin is started')
diff --git a/src/bin/xfrin/xfrin.spec b/src/bin/xfrin/xfrin.spec
index dc993f7..6d1e3a0 100644
--- a/src/bin/xfrin/xfrin.spec
+++ b/src/bin/xfrin/xfrin.spec
@@ -48,6 +48,11 @@
"item_type": "boolean",
"item_optional": false,
"item_default": false
+ },
+ { "item_name": "request_ixfr",
+ "item_type": "string",
+ "item_optional": false,
+ "item_default": "yes"
}
]
}
@@ -56,7 +61,36 @@
"commands": [
{
"command_name": "retransfer",
- "command_description": "retransfer a single zone without checking zone serial number",
+ "command_description": "retransfer a single zone without checking zone serial number, always using AXFR",
+ "command_args": [ {
+ "item_name": "zone_name",
+ "item_type": "string",
+ "item_optional": false,
+ "item_default": ""
+ },
+ {
+ "item_name": "zone_class",
+ "item_type": "string",
+ "item_optional": true,
+ "item_default": "IN"
+ },
+ {
+ "item_name": "master",
+ "item_type": "string",
+ "item_optional": true,
+ "item_default": ""
+ },
+ {
+ "item_name": "port",
+ "item_type": "integer",
+ "item_optional": true,
+ "item_default": 53
+ }
+ ]
+ },
+ {
+ "command_name": "refresh",
+ "command_description": "transfer a single zone with checking zone serial number and honoring the request_ixfr policy",
"command_args": [ {
"item_name": "zone_name",
"item_type": "string",
@@ -102,16 +136,16 @@
"item_optional": false,
"item_default": {
"_SERVER_" : {
- "soaoutv4": 0,
- "soaoutv6": 0,
- "axfrreqv4": 0,
- "axfrreqv6": 0,
- "ixfrreqv4": 0,
- "ixfrreqv6": 0,
- "xfrsuccess": 0,
- "xfrfail": 0,
- "last_ixfr_duration": 0.0,
- "last_axfr_duration": 0.0
+ "soaoutv4": 0,
+ "soaoutv6": 0,
+ "axfrreqv4": 0,
+ "axfrreqv6": 0,
+ "ixfrreqv4": 0,
+ "ixfrreqv6": 0,
+ "xfrsuccess": 0,
+ "xfrfail": 0,
+ "last_ixfr_duration": 0.0,
+ "last_axfr_duration": 0.0
}
},
"item_title": "Zone names",
diff --git a/src/bin/xfrin/xfrin_messages.mes b/src/bin/xfrin/xfrin_messages.mes
index eeddee9..33a2193 100644
--- a/src/bin/xfrin/xfrin_messages.mes
+++ b/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
@@ -212,6 +230,17 @@ such that the remote server doesn't support IXFR, we don't have the SOA record
(or the zone at all), we are out of sync, etc. In many of these situations,
AXFR could still work. Therefore we try that one in case it helps.
+% XFRIN_XFR_TRANSFER_FALLBACK_DISABLED suppressing fallback from IXFR to AXFR for %1
+An IXFR transfer of the given zone failed. By default AXFR will be
+tried next, but this fallback is disabled by configuration, so the
+whole transfer attempt failed at that point. If the reason for the
+failure (which should be logged separately) is temporary, this is
+probably harmless or even desired as another IXFR will take place some
+time later (without falling back to the possibly expensive AXFR). If
+this is a permanent error (e.g., some change at the master server
+completely disables IXFR), the secondary zone will eventually expire,
+so the configuration should be changed to allow AXFR.
+
% XFRIN_XFR_TRANSFER_PROTOCOL_VIOLATION %1 transfer of zone %2 with %3 failed: %4
The XFR transfer for the given zone has failed due to a protocol
error, such as an unexpected response from the primary server. The
@@ -250,9 +279,9 @@ On starting an xfrin session, it is identified that the zone to be
transferred has multiple SOA RRs. Such a zone is broken, but could be
accidentally configured especially in a data source using "non
captive" backend database. The implementation ignores entire SOA RRs
-and tries to continue processing as if the zone were empty. This
-means subsequent AXFR can succeed and possibly replace the zone with
-valid content, but an IXFR attempt will fail.
+and tries to continue processing as if the zone were empty. This also
+means AXFR will be used unconditionally, regardless of the configured value
+for request_ixfr of the zone.
% XFRIN_ZONE_NO_SOA Zone %1 does not have SOA
On starting an xfrin session, it is identified that the zone to be
diff --git a/tests/lettuce/configurations/ixfr-out/testset1-config.db b/tests/lettuce/configurations/ixfr-out/testset1-config.db
index d5eaf83..38d29a7 100644
--- a/tests/lettuce/configurations/ixfr-out/testset1-config.db
+++ b/tests/lettuce/configurations/ixfr-out/testset1-config.db
@@ -2,7 +2,6 @@
"Xfrin": {
"zones": [
{
- "use_ixfr": true,
"class": "IN",
"name": "example.com.",
"master_addr": "178.18.82.80"
diff --git a/tests/lettuce/configurations/xfrin/retransfer_slave_diffs.conf b/tests/lettuce/configurations/xfrin/retransfer_slave_diffs.conf
index 1bc6324..8f5e8fb 100644
--- a/tests/lettuce/configurations/xfrin/retransfer_slave_diffs.conf
+++ b/tests/lettuce/configurations/xfrin/retransfer_slave_diffs.conf
@@ -18,8 +18,7 @@
"zones": [ {
"name": "example",
"master_addr": "::1",
- "master_port": 47807,
- "use_ixfr": true
+ "master_port": 47807
} ]
},
"data_sources": {
diff --git a/tests/lettuce/configurations/xfrin/retransfer_slave_notify.conf.orig b/tests/lettuce/configurations/xfrin/retransfer_slave_notify.conf.orig
index 3040b6c..0d048c1 100644
--- a/tests/lettuce/configurations/xfrin/retransfer_slave_notify.conf.orig
+++ b/tests/lettuce/configurations/xfrin/retransfer_slave_notify.conf.orig
@@ -28,7 +28,8 @@
"zones": [ {
"name": "example.org",
"master_addr": "::1",
- "master_port": 47807
+ "master_port": 47807,
+ "request_ixfr": "no"
} ]
},
"Zonemgr": {
diff --git a/tests/lettuce/configurations/xfrin/retransfer_slave_notify_v4.conf b/tests/lettuce/configurations/xfrin/retransfer_slave_notify_v4.conf
index 67ebfd3..8e55878 100644
--- a/tests/lettuce/configurations/xfrin/retransfer_slave_notify_v4.conf
+++ b/tests/lettuce/configurations/xfrin/retransfer_slave_notify_v4.conf
@@ -28,7 +28,8 @@
"zones": [ {
"name": "example.org",
"master_addr": "127.0.0.1",
- "master_port": 47809
+ "master_port": 47809,
+ "request_ixfr": "no"
} ]
},
"Zonemgr": {
diff --git a/tests/lettuce/features/xfrin_bind10.feature b/tests/lettuce/features/xfrin_bind10.feature
index 90a1144..a7d7f85 100644
--- a/tests/lettuce/features/xfrin_bind10.feature
+++ b/tests/lettuce/features/xfrin_bind10.feature
@@ -182,7 +182,8 @@ Feature: Xfrin
example. 3600 IN SOA ns1.example. hostmaster.example. 94 3600 900 7200 300
"""
- When I send bind10 the command Xfrin retransfer example. IN ::1 47807
+ # To invoke IXFR we need to use refresh command
+ When I send bind10 the command Xfrin refresh example. IN ::1 47807
Then wait for new bind10 stderr message XFRIN_GOT_INCREMENTAL_RESP
Then wait for new bind10 stderr message XFRIN_IXFR_TRANSFER_SUCCESS not XFRIN_XFR_PROCESS_FAILURE
# This can't be 'wait for new'
More information about the bind10-changes
mailing list