Commit 9351ba5d authored by JINMEI Tatuya's avatar JINMEI Tatuya
Browse files

[master] [1299] handle the case of primary's SOA serial is smaller than that of xfrin.

(there are some corner cases that haven't been addressed yet)
parent 83769196
......@@ -21,6 +21,7 @@ import sqlite3
import sys
import io
from isc.testutils.tsigctx_mock import MockTSIGContext
from isc.testutils.rrset_utils import *
from xfrin import *
import xfrin
from isc.xfrin.diff import Diff
......@@ -601,6 +602,7 @@ class TestXfrinConnection(unittest.TestCase):
'response': True,
'auth': True,
'rcode': Rcode.NOERROR(),
'answers': default_answers,
'tsig': False,
'axfr_after_soa': self._create_normal_response_data
}
......@@ -659,6 +661,7 @@ class TestXfrinConnection(unittest.TestCase):
response=self.soa_response_params['response'],
auth=self.soa_response_params['auth'],
rcode=self.soa_response_params['rcode'],
answers=self.soa_response_params['answers'],
questions=self.soa_response_params['questions'],
tsig_ctx=verify_ctx)
if self.soa_response_params['axfr_after_soa'] != None:
......@@ -948,6 +951,25 @@ class TestAXFR(TestXfrinConnection):
self.conn.response_generator = self._create_soa_response_data
self.assertRaises(XfrinException, self.conn._check_soa_serial)
def test_soacheck_uptodate(self):
# Primary's SOA serial is identical the local serial
self.soa_response_params['answers'] = [begin_soa_rrset]
self.conn.response_generator = self._create_soa_response_data
self.assertRaises(XfrinZoneUptodate, self.conn._check_soa_serial)
def test_soacheck_uptodate2(self):
# Primary's SOA serial is "smaller" than the local serial
self.soa_response_params['answers'] = [create_soa(1229)]
self.conn.response_generator = self._create_soa_response_data
self.assertRaises(XfrinZoneUptodate, self.conn._check_soa_serial)
def test_soacheck_uptodate3(self):
# Similar to the previous case, but checking the comparison is based
# on the serial number arithmetic.
self.soa_response_params['answers'] = [create_soa(0xffffffff)]
self.conn.response_generator = self._create_soa_response_data
self.assertRaises(XfrinZoneUptodate, self.conn._check_soa_serial)
def test_soacheck_with_tsig(self):
# Use a mock tsig context emulating a validly signed response
self.conn._tsig_key = TSIG_KEY
......@@ -1256,6 +1278,11 @@ class TestAXFR(TestXfrinConnection):
self.conn.response_generator = self._create_soa_response_data
self.assertEqual(self.conn.do_xfrin(True), XFRIN_OK)
def test_do_soacheck_uptodate(self):
self.soa_response_params['answers'] = [begin_soa_rrset]
self.conn.response_generator = self._create_soa_response_data
self.assertEqual(self.conn.do_xfrin(True), XFRIN_OK)
def test_do_soacheck_and_xfrin_with_tsig(self):
# We are going to have a SOA query/response transaction, followed by
# AXFR, all TSIG signed. xfrin should use a new TSIG context for
......
......@@ -75,9 +75,10 @@ DEFAULT_MASTER_PORT = 53
DEFAULT_ZONE_CLASS = RRClass.IN()
__version__ = 'BIND10'
# define xfrin rcode
XFRIN_OK = 0
XFRIN_FAIL = 1
# Internal result codes of an xfr session
XFRIN_OK = 0 # normal success
XFRIN_FAIL = 1 # general failure (internal/external)
class XfrinException(Exception):
pass
......@@ -87,6 +88,11 @@ class XfrinProtocolError(Exception):
'''
pass
class XfrinZoneUptodate(Exception):
'''TBD
'''
pass
class XfrinZoneInfoException(Exception):
"""This exception is raised if there is an error in the given
configuration (part), or when a command does not have a required
......@@ -709,15 +715,28 @@ class XfrinConnection(asyncore.dispatcher):
msg = Message(Message.PARSE)
msg.from_wire(soa_response)
# TSIG related checks, including an unexpected signed response
self._check_response_tsig(msg, soa_response)
# Validate the header. Unlike AXFR/IXFR, we should be more strict
# Validate the message. Unlike AXFR/IXFR, we should be more strict
# for SOA queries and check the AA flag, too.
self._check_response_tsig(msg, soa_response)
self._check_response_header(msg)
if not msg.get_header_flag(Message.HEADERFLAG_AA):
raise XfrinException('non-authoritative answer to SOA query')
# Examine the answer section
soa = None
for rrset in msg.get_section(Message.SECTION_ANSWER):
if rrset.get_type() == RRType.SOA():
soa = rrset
primary_serial = get_soa_serial(soa.get_rdata()[0])
if (self._request_serial is not None) and \
self._request_serial >= primary_serial:
if self._request_serial != primary_serial:
logger.info(XFRIN_ZONE_SERIAL_AHEAD, primary_serial,
self.zone_str(),
format_addrinfo(self._master_addrinfo),
self._request_serial)
raise XfrinZoneUptodate
# TODO, need select soa record from data source then compare the two
# serial, current just return OK, since this function hasn't been used
# now.
......@@ -744,6 +763,11 @@ class XfrinConnection(asyncore.dispatcher):
logger.info(XFRIN_XFR_TRANSFER_SUCCESS, request_str,
self.zone_str())
except XfrinZoneUptodate:
# Eventually we'll probably have to treat this case as a trigger
# of trying another primary server, etc, but for now We treat it
# as "success".
pass
except (XfrinException, XfrinProtocolError) as e:
logger.error(XFRIN_XFR_TRANSFER_FAILURE, request_str,
self.zone_str(),
......
......@@ -45,6 +45,16 @@ 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.
% XFRIN_ZONE_SERIAL_AHEAD Serial number (%1) for %2 received from master %3 < ours (%4)
The response to an SOA query prior to xfr indicated that the zone's
SOA serial at the primary server is smaller than that of the xfrin
client. This is not necessarily an error especially if that
particular primary server is another secondary server which hasn't got
the latest version of the zone. But if the primary server is known to
be the real source of the zone, some unexpected inconsistency may have
happened, and you may want to take a closer look. In this case xfrin
doesn't perform subsequent zone transfer.
% 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
......
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