Commit 5ca7b409 authored by JINMEI Tatuya's avatar JINMEI Tatuya
Browse files

[master] Merge branch 'trac1209' with resolving a minor conflict.

parents 1e6ee8d8 dcbc2db0
......@@ -1272,6 +1272,19 @@ TODO
and care should be taken to enable IXFR.
</para>
<note><simpara>
In the current development release of BIND 10, incoming zone
transfers are only available for SQLite3-based data sources,
that is, they don't work for an in-memory data source.
Furthermore, the corresponding SQLite3 database must be
configured with a list of zone names by hand. One possible way
to do this is to use the <command>b10-loadzone</command> command
to load dummy zone content of the zone for which the secondary
service is provided (and then force transfer using AXFR from the primary
server). In future versions we will provide more convenient way
to set up the secondary.
</simpara></note>
<para>
To enable IXFR, you need to
configure <command>b10-xfrin</command> with an explicit zone
......@@ -1311,12 +1324,6 @@ TODO
version, at which point we will enable IXFR by default.
</para>
<note><simpara>
In the current development release of BIND 10, incoming zone
transfers are only available for SQLite3-based data sources,
that is, they don't work for an in-memory data source.
</simpara></note>
<!-- TODO:
how to tell bind10 you are a secondary?
......
This diff is collapsed.
......@@ -148,8 +148,8 @@ class XfrinState:
IXFR/AXFR response begins with an SOA). When it reaches IXFREnd
or AXFREnd, the process successfully completes.
(recv SOA) (AXFR-style IXFR) (SOA, add)
(AXFR or
(recv SOA) AXFR-style IXFR) (SOA, add)
InitialSOA------->FirstData------------->AXFR--------->AXFREnd
| | ^ (post xfr
| | | checks, then
......@@ -321,7 +321,7 @@ class XfrinFirstData(XfrinState):
else:
logger.debug(DBG_XFRIN_TRACE, XFRIN_GOT_NONINCREMENTAL_RESP,
conn.zone_str())
# We are now goint to add RRs to the new zone. We need create
# We are now going to add RRs to the new zone. We need create
# a Diff object. It will be used throughtout the XFR session.
conn._diff = Diff(conn._datasrc_client, conn._zone_name, True)
self.set_xfrstate(conn, XfrinAXFR())
......@@ -405,6 +405,12 @@ class XfrinAXFR(XfrinState):
if rr.get_type() == RRType.SOA():
# SOA means end. Don't commit it yet - we need to perform
# post-transfer checks
soa_serial = get_soa_serial(rr.get_rdata()[0])
if conn._end_serial != soa_serial:
logger.warn(XFRIN_AXFR_INCONSISTENT_SOA, conn.zone_str(),
conn._end_serial, soa_serial)
self.set_xfrstate(conn, XfrinAXFREnd())
# Yes, we've eaten this RR.
return True
......@@ -432,15 +438,14 @@ class XfrinConnection(asyncore.dispatcher):
'''Do xfrin in this class. '''
def __init__(self,
sock_map, zone_name, rrclass, datasrc_client, db_file,
shutdown_event, master_addrinfo, tsig_key = None,
verbose=False, idle_timeout=60):
'''Constructor of the XfrinConnection class.
sock_map, zone_name, rrclass, datasrc_client,
shutdown_event, master_addrinfo, tsig_key=None,
idle_timeout=60):
'''Constructor of the XfirnConnection class.
idle_timeout: max idle time for read data from socket.
datasrc_client: the data source client object used for the XFR session.
This will eventually replace db_file completely.
db_file: specify the data source file (should soon be deprecated).
'''
......@@ -460,8 +465,7 @@ class XfrinConnection(asyncore.dispatcher):
self._zone_name = zone_name
self._rrclass = rrclass
# Data source handlers
self._db_file = db_file # temporary for sqlite3 specific code
# Data source handler
self._datasrc_client = datasrc_client
self.create_socket(master_addrinfo[0], master_addrinfo[1])
......@@ -470,7 +474,6 @@ class XfrinConnection(asyncore.dispatcher):
self._idle_timeout = idle_timeout
self.setblocking(1)
self._shutdown_event = shutdown_event
self._verbose = verbose
self._master_address = master_addrinfo[2]
self._tsig_key = tsig_key
self._tsig_ctx = None
......@@ -648,16 +651,9 @@ class XfrinConnection(asyncore.dispatcher):
if ret == XFRIN_OK:
logger.info(XFRIN_XFR_TRANSFER_STARTED, request_str,
self.zone_str())
if self._request_type == RRType.IXFR():
self._request_type = RRType.IXFR()
self._send_query(self._request_type)
self.__state = XfrinInitialSOA()
self._handle_xfrin_responses()
else:
self._send_query(self._request_type)
isc.datasrc.sqlite3_ds.load(self._db_file,
self._zone_name.to_text(),
self._handle_axfrin_response)
self._send_query(self._request_type)
self.__state = XfrinInitialSOA()
self._handle_xfrin_responses()
logger.info(XFRIN_XFR_TRANSFER_SUCCESS, request_str,
self.zone_str())
......@@ -665,11 +661,6 @@ class XfrinConnection(asyncore.dispatcher):
logger.error(XFRIN_XFR_TRANSFER_FAILURE, request_str,
self.zone_str(), str(e))
ret = XFRIN_FAIL
except isc.datasrc.sqlite3_ds.Sqlite3DSError as e:
# Note: this is old code and used only for AXFR. This will be
# soon removed anyway, so we'll leave it.
logger.error(XFRIN_AXFR_DATABASE_FAILURE, self.zone_str(), str(e))
ret = XFRIN_FAIL
except Exception as e:
# Catching all possible exceptions like this is generally not a
# good practice, but handling an xfr session could result in
......@@ -717,9 +708,6 @@ class XfrinConnection(asyncore.dispatcher):
self._check_response_header(msg)
if msg.get_rr_count(Message.SECTION_ANSWER) == 0:
raise XfrinException('answer section is empty')
if msg.get_rr_count(Message.SECTION_QUESTION) > 1:
raise XfrinException('query section count greater than 1')
......@@ -775,31 +763,6 @@ class XfrinConnection(asyncore.dispatcher):
if self._shutdown_event.is_set():
raise XfrinException('xfrin is forced to stop')
def _handle_axfrin_response(self):
'''Return a generator for the response to a zone transfer. '''
while True:
data_len = self._get_request_response(2)
msg_len = socket.htons(struct.unpack('H', data_len)[0])
recvdata = self._get_request_response(msg_len)
msg = Message(Message.PARSE)
msg.from_wire(recvdata)
# TSIG related checks, including an unexpected signed response
self._check_response_tsig(msg, recvdata)
# Perform response status validation
self._check_response_status(msg)
answer_section = msg.get_section(Message.SECTION_ANSWER)
for rr in self._handle_answer_section(answer_section):
yield rr
if self._soa_rr_count == 2:
break
if self._shutdown_event.is_set():
raise XfrinException('xfrin is forced to stop')
def handle_read(self):
'''Read query's response from socket. '''
......@@ -817,8 +780,8 @@ class XfrinConnection(asyncore.dispatcher):
pass
def process_xfrin(server, xfrin_recorder, zone_name, rrclass, db_file,
shutdown_event, master_addrinfo, check_soa, verbose,
tsig_key, request_type):
shutdown_event, master_addrinfo, check_soa, tsig_key,
request_type):
xfrin_recorder.increment(zone_name)
# Create a data source client used in this XFR session. Right now we
......@@ -834,8 +797,7 @@ def process_xfrin(server, xfrin_recorder, zone_name, rrclass, db_file,
# Create a TCP connection for the XFR session and perform the operation.
sock_map = {}
conn = XfrinConnection(sock_map, zone_name, rrclass, datasrc_client,
db_file, shutdown_event, master_addrinfo,
tsig_key, verbose)
shutdown_event, master_addrinfo, tsig_key)
ret = XFRIN_FAIL
if conn.connect_to_master():
ret = conn.do_xfrin(check_soa, request_type)
......@@ -977,13 +939,12 @@ class ZoneInfo:
(str(self.master_addr), self.master_port))
class Xfrin:
def __init__(self, verbose = False):
def __init__(self):
self._max_transfers_in = 10
self._zones = {}
self._cc_setup()
self.recorder = XfrinRecorder()
self._shutdown_event = threading.Event()
self._verbose = verbose
def _cc_setup(self):
'''This method is used only as part of initialization, but is
......@@ -1243,7 +1204,6 @@ class Xfrin:
db_file,
self._shutdown_event,
master_addrinfo, check_soa,
self._verbose,
tsig_key, request_type))
xfrin_thread.start()
......@@ -1263,9 +1223,9 @@ def set_signal_handler():
def set_cmd_options(parser):
parser.add_option("-v", "--verbose", dest="verbose", action="store_true",
help="display more about what is going on")
help="This option is obsolete and has no effect.")
def main(xfrin_class, use_signal = True):
def main(xfrin_class, use_signal=True):
"""The main loop of the Xfrin daemon.
@param xfrin_class: A class of the Xfrin object. This is normally Xfrin,
......@@ -1282,7 +1242,7 @@ def main(xfrin_class, use_signal = True):
if use_signal:
set_signal_handler()
xfrind = xfrin_class(verbose = options.verbose)
xfrind = xfrin_class()
xfrind.startup()
except KeyboardInterrupt:
logger.info(XFRIN_STOPPED_BY_KEYBOARD)
......
......@@ -103,3 +103,19 @@ which is the RR following the initial SOA. Non incremental transfer is
either AXFR or AXFR-style IXFR. In the latter case, it means that
in a response to IXFR query the first data is not SOA or its SOA serial
is not equal to the requested SOA serial.
% XFRIN_AXFR_INCONSISTENT_SOA AXFR SOAs are inconsistent for %1: %2 expected, %3 received
The serial fields of the first and last SOAs of AXFR (including AXFR-style
IXFR) are not the same. According to RFC 5936 these two SOAs must be the
"same" (not only for the serial), but it is still not clear what the
receiver should do if this condition does not hold. There was a discussion
about this at the IETF dnsext wg:
http://www.ietf.org/mail-archive/web/dnsext/current/msg07908.html
and the general feeling seems that it would be better to reject the
transfer if a mismatch is detected. On the other hand, also as noted
in that email thread, neither BIND 9 nor NSD performs any comparison
on the SOAs. For now, we only check the serials (ignoring other fields)
and only leave a warning log message when a mismatch is found. If it
turns out to happen with a real world primary server implementation
and that server actually feeds broken data (e.g. mixed versions of
zone), we can consider a stricter action.
......@@ -110,7 +110,7 @@ Return an updater to make updates to a specific zone.\n\
The RR class of the zone is the one that the client is expected to\n\
handle (see the detailed description of this class).\n\
\n\
If the specified zone is not found via the client, a NULL pointer will\n\
If the specified zone is not found via the client, a None object will\n\
be returned; in other words a completely new zone cannot be created\n\
using an updater. It must be created beforehand (even if it's an empty\n\
placeholder) in a way specific to the underlying data source.\n\
......
......@@ -120,9 +120,12 @@ DataSourceClient_getUpdater(PyObject* po_self, PyObject* args) {
PyBool_Check(replace_obj)) {
bool replace = (replace_obj != Py_False);
try {
return (createZoneUpdaterObject(
self->cppobj->getUpdater(PyName_ToName(name_obj),
replace)));
ZoneUpdaterPtr updater =
self->cppobj->getUpdater(PyName_ToName(name_obj), replace);
if (!updater) {
return (Py_None);
}
return (createZoneUpdaterObject(updater));
} catch (const isc::NotImplemented& ne) {
PyErr_SetString(getDataSourceException("NotImplemented"),
ne.what());
......
......@@ -383,6 +383,11 @@ class DataSrcUpdater(unittest.TestCase):
self.assertEqual("www.example.com. 3600 IN A 192.0.2.1\n",
rrset.to_text())
def test_update_for_no_zone(self):
dsc = isc.datasrc.DataSourceClient(WRITE_ZONE_DB_FILE)
self.assertEqual(None,
dsc.get_updater(isc.dns.Name("notexistent.example"),
True))
if __name__ == "__main__":
isc.log.init("bind10")
......
Supports Markdown
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