Commit b3c9fffb authored by JINMEI Tatuya's avatar JINMEI Tatuya
Browse files

[1299] handled IXFR 'serial uptodate' case

parent 50fdb098
......@@ -352,6 +352,37 @@ class TestXfrinInitialSOA(TestXfrinState):
self.assertRaises(XfrinProtocolError, self.state.handle_rr, self.conn,
self.ns_rrset)
def test_handle_ixfr_uptodate(self):
self.conn._request_type = RRType.IXFR()
self.conn._request_serial = isc.dns.Serial(1234) # same as soa_rrset
self.assertTrue(self.state.handle_rr(self.conn, soa_rrset))
self.assertEqual(type(XfrinIXFRUptodate()),
type(self.conn.get_xfrstate()))
def test_handle_ixfr_uptodate2(self):
self.conn._request_type = RRType.IXFR()
self.conn._request_serial = isc.dns.Serial(1235) # > soa_rrset
self.assertTrue(self.state.handle_rr(self.conn, soa_rrset))
self.assertEqual(type(XfrinIXFRUptodate()),
type(self.conn.get_xfrstate()))
def test_handle_ixfr_uptodate3(self):
# Similar to the previous case, but checking serial number arithmetic
# comparison
self.conn._request_type = RRType.IXFR()
self.conn._request_serial = isc.dns.Serial(0xffffffff)
self.assertTrue(self.state.handle_rr(self.conn, soa_rrset))
self.assertEqual(type(XfrinFirstData()),
type(self.conn.get_xfrstate()))
def test_handle_axfr_uptodate(self):
# "request serial" should matter only for IXFR
self.conn._request_type = RRType.AXFR()
self.conn._request_serial = isc.dns.Serial(1234) # same as soa_rrset
self.assertTrue(self.state.handle_rr(self.conn, soa_rrset))
self.assertEqual(type(XfrinFirstData()),
type(self.conn.get_xfrstate()))
def test_finish_message(self):
self.assertTrue(self.state.finish_message(self.conn))
......@@ -523,6 +554,19 @@ class TestXfrinIXFREnd(TestXfrinState):
def test_finish_message(self):
self.assertFalse(self.state.finish_message(self.conn))
class TestXfrinIXFREnd(TestXfrinState):
def setUp(self):
super().setUp()
self.state = XfrinIXFRUptodate()
def test_handle_rr(self):
self.assertRaises(XfrinProtocolError, self.state.handle_rr, self.conn,
self.ns_rrset)
def test_finish_message(self):
self.assertRaises(XfrinZoneUptodate, self.state.finish_message,
self.conn)
class TestXfrinAXFR(TestXfrinState):
def setUp(self):
super().setUp()
......@@ -1488,6 +1532,16 @@ class TestIXFRResponse(TestXfrinConnection):
[[('delete', begin_soa_rrset), ('add', soa_rrset)]],
self.conn._datasrc_client.committed_diffs)
def test_ixfr_response_uptodate(self):
'''IXFR response indicates the zone is new enough'''
self.conn.reply_data = self.conn.create_response_data(
questions=[Question(TEST_ZONE_NAME, TEST_RRCLASS, RRType.IXFR())],
answers=[begin_soa_rrset])
self.assertRaises(XfrinZoneUptodate, self.conn._handle_xfrin_responses)
# no diffs should have been committed
check_diffs(self.assertEqual,
[], self.conn._datasrc_client.committed_diffs)
def test_ixfr_response_broken(self):
'''Test with a broken response.
......@@ -1520,6 +1574,22 @@ class TestIXFRResponse(TestXfrinConnection):
[[('delete', begin_soa_rrset), ('add', soa_rrset)]],
self.conn._datasrc_client.committed_diffs)
def test_ixfr_response_uptodate_extra(self):
'''Similar to 'uptodate' test, but with extra bogus data.
In either case an exception will be raised, but in this case it's
considered an error.
'''
self.conn.reply_data = self.conn.create_response_data(
questions=[Question(TEST_ZONE_NAME, TEST_RRCLASS, RRType.IXFR())],
answers=[begin_soa_rrset, soa_rrset])
self.assertRaises(XfrinProtocolError,
self.conn._handle_xfrin_responses)
# no diffs should have been committed
check_diffs(self.assertEqual,
[], self.conn._datasrc_client.committed_diffs)
def test_ixfr_to_axfr_response(self):
'''AXFR-style IXFR response.
......@@ -1623,13 +1693,25 @@ class TestIXFRSession(TestXfrinConnection):
self.conn.response_generator = create_ixfr_response
self.assertEqual(XFRIN_FAIL, self.conn.do_xfrin(False, RRType.IXFR()))
def test_do_xfrin_fail(self):
def test_do_xfrin_fail2(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()))
def test_do_xfrin_uptodate(self):
'''IXFR is (gracefully) aborted because serial is not new
'''
def create_response():
self.conn.reply_data = self.conn.create_response_data(
questions=[Question(TEST_ZONE_NAME, TEST_RRCLASS,
RRType.IXFR())],
answers=[begin_soa_rrset])
self.conn.response_generator = create_response
self.assertEqual(XFRIN_OK, self.conn.do_xfrin(False, RRType.IXFR()))
class TestXFRSessionWithSQLite3(TestXfrinConnection):
'''Tests for XFR sessions using an SQLite3 DB.
......
......@@ -188,12 +188,12 @@ class XfrinState:
(AXFR or
(recv SOA) AXFR-style IXFR) (SOA, add)
InitialSOA------->FirstData------------->AXFR--------->AXFREnd
| | ^ (post xfr
| | | checks, then
| +--+ commit)
| (non SOA, add)
|
| (non SOA, delete)
| | | ^ (post xfr
|(IXFR && | | | checks, then
| recv SOA | +--+ commit)
| not new) | (non SOA, add)
V |
IXFRUptodate | (non SOA, delete)
(pure IXFR,| +-------+
keep handling)| (Delete SOA) V |
+ ->IXFRDeleteSOA------>IXFRDelete--+
......@@ -307,13 +307,14 @@ class XfrinInitialSOA(XfrinState):
+ rr.get_type().to_text() + ' received)')
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 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)
if conn._request_type == RRType.IXFR() and \
conn._end_serial <= conn._request_serial:
logger.info(XFRIN_IXFR_UPTODATE, conn.zone_str(),
conn._request_serial, conn._end_serial)
self.set_xfrstate(conn, XfrinIXFRUptodate())
else:
self.set_xfrstate(conn, XfrinFirstData())
self.set_xfrstate(conn, XfrinFirstData())
return True
class XfrinFirstData(XfrinState):
......@@ -437,6 +438,14 @@ class XfrinIXFREnd(XfrinState):
'''
return False
class XfrinIXFRUptodate(XfrinState):
def handle_rr(self, conn, rr):
raise XfrinProtocolError('Extra data after single IXFR response ' +
rr.to_text())
def finish_message(self, conn):
raise XfrinZoneUptodate
class XfrinAXFR(XfrinState):
def handle_rr(self, conn, rr):
"""
......
......@@ -163,6 +163,16 @@ daemon will now shut down.
An uncaught exception was raised while running the xfrin daemon. The
exception message is printed in the log message.
% XFRIN_IXFR_UPTODATE IXFR requested serial for %1 is %2, master has %3, not updating
The first SOA record in an IXFR response indicates the zone's serial
at the primary server is not newer than the client's. This is
basically unexpected event because normally the client first checks
the SOA serial by an SOA query, but can still happen if the transfer
is manually invoked or (although unlikely) there is a rapid change at
the primary server between the SOA and IXFR queries. The client
implementation confirms the whole response is this single SOA, and
aborts the transfer just like a successful case.
% XFRIN_GOT_INCREMENTAL_RESP got incremental response for %1
In an attempt of IXFR processing, the begenning SOA of the first difference
(following the initial SOA that specified the final SOA for all the
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment