Commit 1b3e21e0 authored by Jelte Jansen's avatar Jelte Jansen
Browse files

[1390] pass IXFR request on to xfrout

actual correct handling will be done in #1371 and #1372
parent 24c2111e
......@@ -91,9 +91,9 @@ public:
bool processNormalQuery(const IOMessage& io_message, MessagePtr message,
OutputBufferPtr buffer,
auto_ptr<TSIGContext> tsig_context);
bool processAxfrQuery(const IOMessage& io_message, MessagePtr message,
OutputBufferPtr buffer,
auto_ptr<TSIGContext> tsig_context);
bool processXfrQuery(const IOMessage& io_message, MessagePtr message,
OutputBufferPtr buffer,
auto_ptr<TSIGContext> tsig_context);
bool processNotify(const IOMessage& io_message, MessagePtr message,
OutputBufferPtr buffer,
auto_ptr<TSIGContext> tsig_context);
......@@ -472,10 +472,11 @@ AuthSrv::processMessage(const IOMessage& io_message, MessagePtr message,
ConstQuestionPtr question = *message->beginQuestion();
const RRType &qtype = question->getType();
if (qtype == RRType::AXFR()) {
sendAnswer = impl_->processAxfrQuery(io_message, message, buffer,
sendAnswer = impl_->processXfrQuery(io_message, message, buffer,
tsig_context);
} else if (qtype == RRType::IXFR()) {
makeErrorMessage(message, buffer, Rcode::NOTIMP(), tsig_context);
sendAnswer = impl_->processXfrQuery(io_message, message, buffer,
tsig_context);
} else {
sendAnswer = impl_->processNormalQuery(io_message, message, buffer,
tsig_context);
......@@ -543,9 +544,9 @@ AuthSrvImpl::processNormalQuery(const IOMessage& io_message, MessagePtr message,
}
bool
AuthSrvImpl::processAxfrQuery(const IOMessage& io_message, MessagePtr message,
OutputBufferPtr buffer,
auto_ptr<TSIGContext> tsig_context)
AuthSrvImpl::processXfrQuery(const IOMessage& io_message, MessagePtr message,
OutputBufferPtr buffer,
auto_ptr<TSIGContext> tsig_context)
{
// Increment query counter.
incCounter(io_message.getSocket().getProtocol());
......
......@@ -425,6 +425,60 @@ TEST_F(AuthSrvTest, AXFRDisconnectFail) {
xfrout.enableDisconnect();
}
TEST_F(AuthSrvTest, IXFRConnectFail) {
EXPECT_FALSE(xfrout.isConnected()); // check prerequisite
xfrout.disableConnect();
UnitTestUtil::createRequestMessage(request_message, opcode, default_qid,
Name("example.com"), RRClass::IN(), RRType::IXFR());
createRequestPacket(request_message, IPPROTO_TCP);
server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
EXPECT_TRUE(dnsserv.hasAnswer());
headerCheck(*parse_message, default_qid, Rcode::SERVFAIL(),
opcode.getCode(), QR_FLAG, 1, 0, 0, 0);
EXPECT_FALSE(xfrout.isConnected());
}
TEST_F(AuthSrvTest, IXFRSendFail) {
// first send a valid query, making the connection with the xfr process
// open.
UnitTestUtil::createRequestMessage(request_message, opcode, default_qid,
Name("example.com"), RRClass::IN(), RRType::IXFR());
createRequestPacket(request_message, IPPROTO_TCP);
server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
EXPECT_TRUE(xfrout.isConnected());
xfrout.disableSend();
parse_message->clear(Message::PARSE);
response_obuffer->clear();
UnitTestUtil::createRequestMessage(request_message, opcode, default_qid,
Name("example.com"), RRClass::IN(), RRType::IXFR());
createRequestPacket(request_message, IPPROTO_TCP);
server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
EXPECT_TRUE(dnsserv.hasAnswer());
headerCheck(*parse_message, default_qid, Rcode::SERVFAIL(),
opcode.getCode(), QR_FLAG, 1, 0, 0, 0);
// The connection should have been closed due to the send failure.
EXPECT_FALSE(xfrout.isConnected());
}
TEST_F(AuthSrvTest, IXFRDisconnectFail) {
// In our usage disconnect() shouldn't fail. So we'll see the exception
// should it be thrown.
xfrout.disableSend();
xfrout.disableDisconnect();
UnitTestUtil::createRequestMessage(request_message, opcode, default_qid,
Name("example.com"), RRClass::IN(), RRType::IXFR());
createRequestPacket(request_message, IPPROTO_TCP);
EXPECT_THROW(server.processMessage(*io_message, parse_message,
response_obuffer, &dnsserv),
XfroutError);
EXPECT_TRUE(xfrout.isConnected());
// XXX: we need to re-enable disconnect. otherwise an exception would be
// thrown via the destructor of the server.
xfrout.enableDisconnect();
}
TEST_F(AuthSrvTest, notify) {
UnitTestUtil::createRequestMessage(request_message, Opcode::NOTIFY(), default_qid,
Name("example.com"), RRClass::IN(), RRType::SOA());
......@@ -743,6 +797,20 @@ TEST_F(AuthSrvTest, queryCounterTCPAXFR) {
EXPECT_EQ(1, server.getCounter(AuthCounters::COUNTER_TCP_QUERY));
}
// Submit TCP IXFR query and check query counter
TEST_F(AuthSrvTest, queryCounterTCPIXFR) {
// The counter should be initialized to 0.
EXPECT_EQ(0, server.getCounter(AuthCounters::COUNTER_TCP_QUERY));
UnitTestUtil::createRequestMessage(request_message, opcode, default_qid,
Name("example.com"), RRClass::IN(), RRType::IXFR());
createRequestPacket(request_message, IPPROTO_TCP);
// On success, the AXFR query has been passed to a separate process,
// so we shouldn't have to respond.
server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
// After processing TCP AXFR query, the counter should be 1.
EXPECT_EQ(1, server.getCounter(AuthCounters::COUNTER_TCP_QUERY));
}
// class for queryCounterUnexpected test
// getProtocol() returns IPPROTO_IP
class DummyUnknownSocket : public IOSocket {
......
......@@ -159,15 +159,27 @@ class TestXfroutSessionBase(unittest.TestCase):
def message_has_tsig(self, msg):
return msg.get_tsig_record() is not None
def create_request_data(self, with_question=True, with_tsig=False):
def create_request_data(self, with_question=True, with_tsig=False,
ixfr=False, with_soa=False):
msg = Message(Message.RENDER)
query_id = 0x1035
msg.set_qid(query_id)
msg.set_opcode(Opcode.QUERY())
msg.set_rcode(Rcode.NOERROR())
if with_question:
msg.add_question(Question(Name("example.com"), RRClass.IN(),
RRType.AXFR()))
if ixfr:
msg.add_question(Question(Name("example.com"), RRClass.IN(),
RRType.IXFR()))
else:
msg.add_question(Question(Name("example.com"), RRClass.IN(),
RRType.AXFR()))
if with_soa:
soa_rrset = RRset(Name('example.com'), RRClass.IN(), RRType.SOA(),
RRTTL(0))
soa_rrset.add_rdata(Rdata(RRType.SOA(), RRClass.IN(),
'. . 1 0 0 0 0'))
msg.add_rrset(Message.SECTION_AUTHORITY, soa_rrset)
renderer = MessageRenderer()
if with_tsig:
......@@ -248,6 +260,16 @@ class TestXfroutSession(TestXfroutSessionBase):
request_data = self.create_request_data(with_question=False)
rcode, msg = self.xfrsess._parse_query_message(request_data)
self.assertEqual(Rcode.FORMERR(), rcode)
# Broken request: IXFR without SOA
request_data = self.create_request_data(ixfr=True)
rcode, msg = self.xfrsess._parse_query_message(request_data)
self.assertEqual(Rcode.FORMERR(), rcode)
# NOERROR
request_data = self.create_request_data(ixfr=True, with_soa=True)
rcode, msg = self.xfrsess._parse_query_message(request_data)
self.assertEqual(rcode.to_text(), "NOERROR")
# tsig signed query message
request_data = self.create_request_data(with_tsig=True)
......
......@@ -224,6 +224,22 @@ class XfroutSession():
# the auth server, but since it's far from our xfrout itself,
# we check it by ourselves.
if msg.get_rr_count(Message.SECTION_QUESTION) != 1:
# TODO: Log?
return Rcode.FORMERR(), msg
request_type = msg.get_question()[0].get_type()
# If it is an IXFR query, there should be a SOA in the authority
# section too
if request_type == RRType.IXFR():
if msg.get_rr_count(Message.SECTION_AUTHORITY) != 1:
# TODO: Log?
return Rcode.FORMERR(), msg
self._request_type = 'IXFR'
elif request_type == RRType.AXFR():
self._request_type = 'AXFR'
else:
# TODO: Log?
return Rcode.FORMERR(), msg
# ACL checks
......@@ -370,19 +386,19 @@ class XfroutSession():
format_addrinfo(self._remote), zone_str, ex)
rcode_ = Rcode.SERVFAIL()
if rcode_ != Rcode.NOERROR():
logger.info(XFROUT_AXFR_TRANSFER_FAILED, self._request_type,
logger.info(XFROUT_XFR_TRANSFER_FAILED, self._request_type,
format_addrinfo(self._remote), zone_str, rcode_)
return self._reply_query_with_error_rcode(msg, sock_fd, rcode_)
try:
logger.info(XFROUT_AXFR_TRANSFER_STARTED, self._request_type,
logger.info(XFROUT_XFR_TRANSFER_STARTED, self._request_type,
format_addrinfo(self._remote), zone_str)
self._reply_xfrout_query(msg, sock_fd)
except Exception as err:
logger.error(XFROUT_AXFR_TRANSFER_ERROR, self._request_type,
logger.error(XFROUT_XFR_TRANSFER_ERROR, self._request_type,
format_addrinfo(self._remote), zone_str, err)
pass
logger.info(XFROUT_AXFR_TRANSFER_DONE, self._request_type,
logger.info(XFROUT_XFR_TRANSFER_DONE, self._request_type,
format_addrinfo(self._remote), zone_str)
def _clear_message(self, msg):
......
......@@ -15,37 +15,6 @@
# No namespace declaration - these constants go in the global namespace
# of the xfrout messages python module.
% XFROUT_AXFR_TRANSFER_DONE %1 client %2: transfer of %3 complete
The transfer of the given zone has been completed successfully, or was
aborted due to a shutdown event.
% XFROUT_AXFR_TRANSFER_ERROR %1 client %2: error transferring zone %3: %4
An uncaught exception was encountered while sending the response to
an AXFR query. The error message of the exception is included in the
log message, but this error most likely points to incomplete exception
handling in the code.
% XFROUT_XFR_TRANSFER_CHECK_ERROR %1 client %2: check for transfer of %3 failed: %4
Pre-response check for an incomding XFR request failed unexpectedly.
The most likely cause of this is that some low level error in the data
source, but it may also be other general (more unlikely) errors such
as memory shortage. Some detail of the error is also included in the
message. The xfrout server tries to return a SERVFAIL response in this case.
% XFROUT_AXFR_TRANSFER_FAILED %1 client %2: transfer of %3 failed, rcode: %4
A transfer out for the given zone failed. An error response is sent
to the client. The given rcode is the rcode that is set in the error
response. This is either NOTAUTH (we are not authoritative for the
zone), SERVFAIL (our internal database is missing the SOA record for
the zone), or REFUSED (the limit of simultaneous outgoing AXFR
transfers, as specified by the configuration value
Xfrout/max_transfers_out, has been reached).
# Still a TODO, but when implemented, REFUSED can also mean
# the client is not allowed to transfer the zone
% XFROUT_AXFR_TRANSFER_STARTED %1 client %2: transfer of zone %3 has started
A transfer out of the given zone has started.
% XFROUT_BAD_TSIG_KEY_STRING bad TSIG key string: %1
The TSIG key string as read from the configuration does not represent
a valid TSIG key.
......@@ -87,6 +56,9 @@ are missing on the system, or the PYTHONPATH variable is not correct.
The specific place where this library needs to be depends on your
system and your specific installation.
% XFROUT_IXFR_TRANSFER_STARTED %1 client %2: IXFR transfer of zone %3 has started
An incremental transfer out of the given zone has started.
% XFROUT_NEW_CONFIG Update xfrout configuration
New configuration settings have been sent from the configuration
manager. The xfrout daemon will now apply them.
......@@ -178,3 +150,33 @@ on, but the file is in use. The most likely cause is that another
xfrout daemon process is still running. This xfrout daemon (the one
printing this message) will not start.
% XFROUT_XFR_TRANSFER_DONE %1 client %2: transfer of %3 complete
The transfer of the given zone has been completed successfully, or was
aborted due to a shutdown event.
% XFROUT_XFR_TRANSFER_ERROR %1 client %2: error transferring zone %3: %4
An uncaught exception was encountered while sending the response to
an AXFR query. The error message of the exception is included in the
log message, but this error most likely points to incomplete exception
handling in the code.
% XFROUT_XFR_TRANSFER_CHECK_ERROR %1 client %2: check for transfer of %3 failed: %4
Pre-response check for an incomding XFR request failed unexpectedly.
The most likely cause of this is that some low level error in the data
source, but it may also be other general (more unlikely) errors such
as memory shortage. Some detail of the error is also included in the
message. The xfrout server tries to return a SERVFAIL response in this case.
% XFROUT_XFR_TRANSFER_FAILED %1 client %2: transfer of %3 failed, rcode: %4
A transfer out for the given zone failed. An error response is sent
to the client. The given rcode is the rcode that is set in the error
response. This is either NOTAUTH (we are not authoritative for the
zone), SERVFAIL (our internal database is missing the SOA record for
the zone), or REFUSED (the limit of simultaneous outgoing AXFR
transfers, as specified by the configuration value
Xfrout/max_transfers_out, has been reached).
# Still a TODO, but when implemented, REFUSED can also mean
# the client is not allowed to transfer the zone
% XFROUT_XFR_TRANSFER_STARTED %1 client %2: transfer of zone %3 has started
A transfer out of the given zone has started.
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