BIND 10 trac1371, updated. 48c07943ac1dd24922f46cf970c214b5cf24813f [1371] copyright and description for the diff generator script, EXTRA_DIST update.
BIND 10 source code commits
bind10-changes at lists.isc.org
Sun Nov 20 03:59:01 UTC 2011
The branch, trac1371 has been updated
via 48c07943ac1dd24922f46cf970c214b5cf24813f (commit)
via bea7b0e3fde35a335bb9e6cf170b0fc240650275 (commit)
via 9b1c64b7d164b6b27d126e55391b2bbafeaf8c00 (commit)
via 96bf3ab5271347542e13b52e2c37b9c8810a6fad (commit)
via c59bb2dcd90a5d580a7f3c9e42a54a080f763add (commit)
from 08915b387e64f3cf9d9a86a5a21c4492db3a488c (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 48c07943ac1dd24922f46cf970c214b5cf24813f
Author: JINMEI Tatuya <jinmei at isc.org>
Date: Sat Nov 19 19:56:12 2011 -0800
[1371] copyright and description for the diff generator script, EXTRA_DIST update.
commit bea7b0e3fde35a335bb9e6cf170b0fc240650275
Author: JINMEI Tatuya <jinmei at isc.org>
Date: Sat Nov 19 19:53:55 2011 -0800
[1371] updated logging and comment
commit 9b1c64b7d164b6b27d126e55391b2bbafeaf8c00
Author: JINMEI Tatuya <jinmei at isc.org>
Date: Sat Nov 19 19:44:38 2011 -0800
[1371] supported the case where the requested IXFR serial is the latest one.
commit 96bf3ab5271347542e13b52e2c37b9c8810a6fad
Author: JINMEI Tatuya <jinmei at isc.org>
Date: Sat Nov 19 19:16:47 2011 -0800
[1371] supported incremental (pure) IXFR response
commit c59bb2dcd90a5d580a7f3c9e42a54a080f763add
Author: JINMEI Tatuya <jinmei at isc.org>
Date: Sat Nov 19 18:52:02 2011 -0800
[1371] a small cleanup: introduced some convenient functions test-module wide.
-----------------------------------------------------------------------
Summary of changes:
src/bin/xfrout/tests/Makefile.am | 4 +-
src/bin/xfrout/tests/testdata/creatediff.py | 23 +++++
src/bin/xfrout/tests/xfrout_test.py.in | 121 ++++++++++++++++++++++-----
src/bin/xfrout/xfrout.py.in | 42 +++++++++-
src/bin/xfrout/xfrout_messages.mes | 8 ++
5 files changed, 170 insertions(+), 28 deletions(-)
-----------------------------------------------------------------------
diff --git a/src/bin/xfrout/tests/Makefile.am b/src/bin/xfrout/tests/Makefile.am
index 509df79..ad6d7e6 100644
--- a/src/bin/xfrout/tests/Makefile.am
+++ b/src/bin/xfrout/tests/Makefile.am
@@ -3,8 +3,8 @@ PYTESTS = xfrout_test.py
noinst_SCRIPTS = $(PYTESTS)
EXTRA_DIST = testdata/test.sqlite3
-# This one is actually not necessary, but added for reference
-EXTRA_DIST += testdata/example.com
+# These are actually not necessary, but added for reference
+EXTRA_DIST += testdata/example.com testdata/creatediff.py
# If necessary (rare cases), explicitly specify paths to dynamic libraries
# required by loadable python modules.
diff --git a/src/bin/xfrout/tests/testdata/creatediff.py b/src/bin/xfrout/tests/testdata/creatediff.py
index b23101f..4b22fb4 100755
--- a/src/bin/xfrout/tests/testdata/creatediff.py
+++ b/src/bin/xfrout/tests/testdata/creatediff.py
@@ -1,5 +1,28 @@
#!/usr/bin/env python3.1
+# Copyright (C) 2011 Internet Systems Consortium.
+#
+# Permission to use, copy, modify, and distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SYSTEMS CONSORTIUM
+# DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+# INTERNET SYSTEMS CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
+# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
+# FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+'''This script was used to create zone differences for IXFR tests.
+
+The result was stored in the test SQLite3 database file, so this script
+itself isn't necessary for testing. It's provided here for reference
+purposes.
+
+'''
+
import isc.datasrc
import isc.log
from isc.dns import *
diff --git a/src/bin/xfrout/tests/xfrout_test.py.in b/src/bin/xfrout/tests/xfrout_test.py.in
index aa7bd99..423ead4 100644
--- a/src/bin/xfrout/tests/xfrout_test.py.in
+++ b/src/bin/xfrout/tests/xfrout_test.py.in
@@ -38,13 +38,26 @@ TEST_ZONE_NAME = Name(TEST_ZONE_NAME_STR)
TEST_RRCLASS = RRClass.IN()
IXFR_OK_VERSION = 2011111802
IXFR_NG_VERSION = 2011112800
-
-# SOA intended to be used for the new SOA as a result of transfer.
-soa_rdata = Rdata(RRType.SOA(), TEST_RRCLASS,
- 'master.example.com. admin.example.com ' +
- '2011112001 3600 1800 2419200 7200')
-soa_rrset = RRset(TEST_ZONE_NAME, TEST_RRCLASS, RRType.SOA(), RRTTL(3600))
-soa_rrset.add_rdata(soa_rdata)
+SOA_CURRENT_VERSION = 2011112001
+
+# Shortcut functions to create RRsets commonly used in tests below.
+def create_a(address, ttl=3600):
+ rrset = RRset(Name('a.example.com'), RRClass.IN(), RRType.A(), RRTTL(ttl))
+ rrset.add_rdata(Rdata(RRType.A(), RRClass.IN(), address))
+ return rrset
+
+def create_aaaa(address):
+ rrset = RRset(Name('a.example.com'), RRClass.IN(), RRType.AAAA(),
+ RRTTL(3600))
+ rrset.add_rdata(Rdata(RRType.AAAA(), RRClass.IN(), address))
+ return rrset
+
+def create_soa(serial):
+ rrset = RRset(TEST_ZONE_NAME, RRClass.IN(), RRType.SOA(), RRTTL(3600))
+ rdata_str = 'master.example.com. admin.example.com. ' + \
+ str(serial) + ' 3600 1800 2419200 7200'
+ rrset.add_rdata(Rdata(RRType.SOA(), RRClass.IN(), rdata_str))
+ return rrset
# our fake socket, where we can read and insert messages
class MySocket():
@@ -85,12 +98,6 @@ class MockDataSrcClient:
def __init__(self, type, config):
pass
- def __create_soa(self):
- soa_rrset = RRset(self._zone_name, RRClass.IN(), RRType.SOA(),
- RRTTL(3600))
- soa_rrset.add_rdata(soa_rdata)
- return soa_rrset
-
def find_zone(self, zone_name):
'''Mock version of find_zone().
@@ -113,15 +120,15 @@ class MockDataSrcClient:
'''
if name == TEST_ZONE_NAME and rrtype == RRType.SOA():
- return (ZoneFinder.SUCCESS, self.__create_soa())
+ return (ZoneFinder.SUCCESS, create_soa(SOA_CURRENT_VERSION))
elif name == Name('nosoa.example.com') and rrtype == RRType.SOA():
return (ZoneFinder.NXDOMAIN, None)
elif name == Name('multisoa.example.com') and rrtype == RRType.SOA():
- soa_rrset = self.__create_soa()
+ soa_rrset = create_soa(SOA_CURRENT_VERSION)
soa_rrset.add_rdata(soa_rrset.get_rdata()[0])
return (ZoneFinder.SUCCESS, soa_rrset)
else:
- return (ZoneFinder.SUCCESS, self.__create_soa())
+ return (ZoneFinder.SUCCESS, create_soa(SOA_CURRENT_VERSION))
def get_iterator(self, zone_name, adjust_ttl=False):
if zone_name == Name('notauth.example.com'):
@@ -132,7 +139,7 @@ class MockDataSrcClient:
def get_soa(self): # emulate ZoneIterator.get_soa()
if self._zone_name == Name('nosoa.example.com'):
return None
- soa_rrset = self.__create_soa()
+ soa_rrset = create_soa(SOA_CURRENT_VERSION)
if self._zone_name == Name('multisoa.example.com'):
soa_rrset.add_rdata(soa_rrset.get_rdata()[0])
return soa_rrset
@@ -281,9 +288,7 @@ class TestXfroutSessionBase(unittest.TestCase):
{})
self.set_request_type(RRType.AXFR()) # test AXFR by default
self.mdata = self.create_request_data()
- self.soa_rrset = RRset(TEST_ZONE_NAME, RRClass.IN(), RRType.SOA(),
- RRTTL(3600))
- self.soa_rrset.add_rdata(soa_rdata)
+ self.soa_rrset = create_soa(SOA_CURRENT_VERSION)
# some test replaces a module-wide function. We should ensure the
# original is used elsewhere.
self.orig_get_rrset_len = xfrout.get_rrset_len
@@ -724,6 +729,15 @@ class TestXfroutSession(TestXfroutSessionBase):
self.assertNotEqual(None, self.xfrsess._iterator)
self.assertEqual(None, self.xfrsess._jnl_reader)
+ # Successful case, but the requested SOA serial is equal to that of
+ # the local SOA. Both iterator and jnl_reader should be None,
+ # indicating that the response will contain just one SOA.
+ self.mdata = self.create_request_data(ixfr=SOA_CURRENT_VERSION)
+ self.assertEqual(self.xfrsess._xfrout_setup(
+ self.getmsg(), TEST_ZONE_NAME, TEST_RRCLASS), Rcode.NOERROR())
+ self.assertEqual(None, self.xfrsess._iterator)
+ self.assertEqual(None, self.xfrsess._jnl_reader)
+
# The data source doesn't support journaling. Should fallback to AXFR.
zone_name = Name('nojournal.example.com')
self.mdata = self.create_request_data(ixfr=IXFR_OK_VERSION,
@@ -810,14 +824,14 @@ class TestXfroutSession(TestXfroutSessionBase):
self.xfrsess.dns_xfrout_start(self.sock, self.mdata)
self.assertEqual(self.sock.readsent(), b"success")
- def test_reply_xfrout_query_noerror(self):
+ def test_reply_xfrout_query_axfr(self):
self.xfrsess._soa = self.soa_rrset
self.xfrsess._iterator = [self.soa_rrset]
self.xfrsess._reply_xfrout_query(self.getmsg(), self.sock)
reply_msg = self.sock.read_msg()
self.assertEqual(reply_msg.get_rr_count(Message.SECTION_ANSWER), 2)
- def test_reply_xfrout_query_noerror_with_tsig(self):
+ def test_reply_xfrout_query_axfr_with_tsig(self):
rrset = RRset(Name('a.example.com'), RRClass.IN(), RRType.A(),
RRTTL(3600))
rrset.add_rdata(Rdata(RRType.A(), RRClass.IN(), '192.0.2.1'))
@@ -845,6 +859,36 @@ class TestXfroutSession(TestXfroutSessionBase):
# and it should not have sent anything else
self.assertEqual(0, len(self.sock.sendqueue))
+ def test_reply_xfrout_query_ixfr(self):
+ # Creating a pure (incremental) IXFR response. Intermediate SOA
+ # RRs won't be skipped.
+ self.xfrsess._soa = create_soa(SOA_CURRENT_VERSION)
+ self.xfrsess._iterator = [create_soa(IXFR_OK_VERSION),
+ create_a('192.0.2.2'),
+ create_soa(SOA_CURRENT_VERSION),
+ create_aaaa('2001:db8::1')]
+ self.xfrsess._jnl_reader = self.xfrsess._iterator
+ self.xfrsess._reply_xfrout_query(self.getmsg(), self.sock)
+ reply_msg = self.sock.read_msg(Message.PRESERVE_ORDER)
+ # The answer section should contain everything in the "fake"
+ # iterator and two SOAs.
+ self.assertEqual(reply_msg.get_rr_count(Message.SECTION_ANSWER),
+ len(self.xfrsess._iterator) + 2)
+
+ def test_reply_xfrout_query_ixfr_soa_only(self):
+ # Creating an IXFR response that contains only one RR, which is the
+ # SOA of the current version.
+ self.xfrsess._soa = create_soa(SOA_CURRENT_VERSION)
+ self.xfrsess._iterator = None
+ self.xfrsess._jnl_reader = None
+ self.xfrsess._reply_xfrout_query(self.getmsg(), self.sock)
+ reply_msg = self.sock.read_msg(Message.PRESERVE_ORDER)
+ answer = reply_msg.get_section(Message.SECTION_ANSWER)
+ self.assertEqual(1, len(answer))
+ self.assertEqual(RRType.SOA(), answer[0].get_type())
+ self.assertEqual(SOA_CURRENT_VERSION,
+ xfrout.get_soa_serial(answer[0].get_rdata()[0]))
+
class TestXfroutSessionWithSQLite3(TestXfroutSessionBase):
'''Tests for XFR-out sessions using an SQLite3 DB.
@@ -901,6 +945,39 @@ class TestXfroutSessionWithSQLite3(TestXfroutSessionBase):
self.assertEqual(RRType.IXFR(), response.get_question()[0].get_type())
self.check_axfr_stream(response)
+ def test_ixfr_normal_session(self):
+ # See testdata/creatediff.py. There are 8 changes between two
+ # versions. So the answer section should contain all of these and
+ # two beginning and trailing SOAs.
+ self.xfrsess._request_data = \
+ self.create_request_data(ixfr=IXFR_OK_VERSION)
+ XfroutSession._handle(self.xfrsess)
+ response = self.sock.read_msg(Message.PRESERVE_ORDER);
+ actual_records = response.get_section(Message.SECTION_ANSWER)
+ self.assertEqual(10, len(actual_records))
+ # The first and last RRs must be SOA whose serial is the latest one.
+ soa = actual_records[0]
+ self.assertEqual(RRType.SOA(), soa.get_type())
+ self.assertEqual(SOA_CURRENT_VERSION,
+ xfrout.get_soa_serial(soa.get_rdata()[0]))
+ soa = actual_records[-1]
+ self.assertEqual(RRType.SOA(), soa.get_type())
+ self.assertEqual(SOA_CURRENT_VERSION,
+ xfrout.get_soa_serial(soa.get_rdata()[0]))
+
+ def test_ixfr_soa_only(self):
+ # The requested SOA serial is the latest one. The response should
+ # contain exactly one SOA of that serial.
+ self.xfrsess._request_data = \
+ self.create_request_data(ixfr=SOA_CURRENT_VERSION)
+ XfroutSession._handle(self.xfrsess)
+ response = self.sock.read_msg(Message.PRESERVE_ORDER);
+ answers = response.get_section(Message.SECTION_ANSWER)
+ self.assertEqual(1, len(answers))
+ self.assertEqual(RRType.SOA(), answers[0].get_type())
+ self.assertEqual(SOA_CURRENT_VERSION,
+ xfrout.get_soa_serial(answers[0].get_rdata()[0]))
+
class MyUnixSockServer(UnixSockServer):
def __init__(self):
self._shutdown_event = threading.Event()
diff --git a/src/bin/xfrout/xfrout.py.in b/src/bin/xfrout/xfrout.py.in
index 931b5aa..8990c13 100755
--- a/src/bin/xfrout/xfrout.py.in
+++ b/src/bin/xfrout/xfrout.py.in
@@ -154,6 +154,7 @@ class XfroutSession():
self._zone_config = zone_config
self.ClientClass = client_class # parameterize this for testing
self._soa = None # will be set in _xfrout_setup or in tests
+ self._jnl_reader = None # will be set to a reader for IXFR
self._handle()
def create_tsig_ctx(self, tsig_record, tsig_key_ring):
@@ -398,9 +399,28 @@ class XfroutSession():
rcode, self._soa = self._get_zone_soa(zone_name)
if rcode != Rcode.NOERROR():
return rcode
+
+ # RFC1995 says "If an IXFR query with the same or newer version
+ # number than that of the server is received, it is replied to with
+ # a single SOA record of the server's current version, just as
+ # in AXFR". The claim about AXFR is incorrect, but other than that,
+ # we do as the RFC says.
+ # Note: until we complete #1278 we can only check equality of the
+ # two serials. The "newer version" case would fall back to AXFR-style.
+ begin_serial = get_soa_serial(remote_soa.get_rdata()[0])
+ end_serial = get_soa_serial(self._soa.get_rdata()[0])
+ if begin_serial == end_serial:
+ # clear both iterator and jnl_reader to signal we won't do
+ # iteration in response generation
+ self._iterator = None
+ self._jnl_reader = None
+ logger.info(XFROUT_IXFR_UPTODATE, format_addrinfo(self._remote),
+ format_zone_str(zone_name, zone_class),
+ begin_serial, end_serial)
+ return Rcode.NOERROR()
+
+ # Set up the journal reader or fall back to AXFR-style IXFR
try:
- begin_serial = get_soa_serial(remote_soa.get_rdata()[0])
- end_serial = get_soa_serial(self._soa.get_rdata()[0])
code, self._jnl_reader = self._datasrc_client.get_journal_reader(
zone_name, begin_serial, end_serial)
except isc.datasrc.NotImplemented as ex:
@@ -424,6 +444,9 @@ class XfroutSession():
format_zone_str(zone_name, zone_class))
return Rcode.NOTAUTH()
+ # Use the reader as the iterator to generate the response.
+ self._iterator = self._jnl_reader
+
return Rcode.NOERROR()
def _xfrout_setup(self, request_msg, zone_name, zone_class):
@@ -527,17 +550,27 @@ class XfroutSession():
#TODO, there should be a better way to insert rrset.
msg.make_response()
msg.set_header_flag(Message.HEADERFLAG_AA)
- msg.add_rrset(Message.SECTION_ANSWER, self._soa)
+ # If the iterator is None, we are responding to IXFR with a single
+ # SOA RR.
+ if self._iterator is None:
+ self._send_message_with_last_soa(msg, sock_fd, self._soa, 0)
+ return
+
+ # Add the beginning SOA
+ msg.add_rrset(Message.SECTION_ANSWER, self._soa)
message_upper_len = get_rrset_len(self._soa) + self._tsig_len
+ # Add the rest of the zone/diff contets
for rrset in self._iterator:
# Check if xfrout is shutdown
if self._server._shutdown_event.is_set():
logger.info(XFROUT_STOPPING)
return
- if rrset.get_type() == RRType.SOA():
+ # For AXFR (or AXFR-style IXFR), in which case _jnl_reader is None,
+ # we should skip SOAs from the iterator.
+ if self._jnl_reader is None and rrset.get_type() == RRType.SOA():
continue
# We calculate the maximum size of the RRset (i.e. the
@@ -558,6 +591,7 @@ class XfroutSession():
# Reserve tsig space for signed packet
message_upper_len = rrset_len + self._tsig_len
+ # Add and send the trailing SOA
self._send_message_with_last_soa(msg, sock_fd, self._soa,
message_upper_len)
diff --git a/src/bin/xfrout/xfrout_messages.mes b/src/bin/xfrout/xfrout_messages.mes
index ac7487a..5f86d94 100644
--- a/src/bin/xfrout/xfrout_messages.mes
+++ b/src/bin/xfrout/xfrout_messages.mes
@@ -192,6 +192,14 @@ An IXFR request was received but the underlying data source did
not support journaling. The xfrout daemon fell back to AXFR-style
IXFR.
+% XFROUT_IXFR_UPTODATE IXFR client %1, %2: client version is new enough (ours=%3, theirs=%4)
+An IXFR request was received, but the client's SOA version is the same as
+or newer than that of the server. The xfrout server responds to the
+request with the answer section being just one SOA of that version.
+Note: as of this wrting the 'newer version' cannot be identified due to
+the lack of support for the serial number arithmetic. This will soon
+be implemented.
+
% XFROUT_IXFR_NO_VERSION IXFR client %1, %2: version (%3 to %4) not in journal, falling back to AXFR
An IXFR request was received, but the requested range of differences
were not found in the data source. The xfrout daemon fell back to
More information about the bind10-changes
mailing list