mirror of
https://gitlab.isc.org/isc-projects/kea
synced 2025-09-01 14:35:29 +00:00
Merge branch 'master' into trac2390_2
This commit is contained in:
11
ChangeLog
11
ChangeLog
@@ -1,3 +1,14 @@
|
|||||||
|
563. [build] jinmei
|
||||||
|
Added --disable-rpath configure option to avoid embedding library
|
||||||
|
paths to binaries. Patch from Adam Tkac.
|
||||||
|
(Trac #2667, git 1c50c5a6ee7e9675e3ab154f2c7f975ef519fca2)
|
||||||
|
|
||||||
|
562. [func]* vorner
|
||||||
|
The b10-xfrin now performs basic sanity check on just received
|
||||||
|
zone. It'll reject severely broken zones (such as missng NS
|
||||||
|
records).
|
||||||
|
(Trac #2439, git 44699b4b18162581cd1dd39be5fb76ca536012e6)
|
||||||
|
|
||||||
561. [bug] kambe, jelte
|
561. [bug] kambe, jelte
|
||||||
b10-stats-httpd no longer dumps request information to the console,
|
b10-stats-httpd no longer dumps request information to the console,
|
||||||
but uses the bind10 logging system. Additionally, the logging
|
but uses the bind10 logging system. Additionally, the logging
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
SUBDIRS = guide
|
SUBDIRS = guide
|
||||||
|
|
||||||
EXTRA_DIST = version.ent.in
|
EXTRA_DIST = version.ent.in differences.txt
|
||||||
|
|
||||||
devel:
|
devel:
|
||||||
mkdir -p html
|
mkdir -p html
|
||||||
|
12
doc/differences.txt
Normal file
12
doc/differences.txt
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
Differences of Bind 10 to other software
|
||||||
|
========================================
|
||||||
|
|
||||||
|
Bind 9
|
||||||
|
------
|
||||||
|
|
||||||
|
TODO: There are definitely more differences than just this.
|
||||||
|
|
||||||
|
* When an incoming zone transfer fails, for example because the
|
||||||
|
received zone doesn't contain a NS record, bind 9 stops serving the
|
||||||
|
zone and returns SERVFAIL to queries for that zone. Bind 10 still
|
||||||
|
uses the previous version of zone.
|
@@ -14,9 +14,10 @@ AC_LANG([C++])
|
|||||||
# Checks for BIND 10 headers and libraries
|
# Checks for BIND 10 headers and libraries
|
||||||
AX_ISC_BIND10
|
AX_ISC_BIND10
|
||||||
|
|
||||||
# We use -R, -rpath etc so the resulting program will be more likekly to
|
# We use -R option etc so the resulting program will be more likekly to
|
||||||
# "just work" by default. Embedding a specific library path is a controversial
|
# "just work" by default. Embedding a specific library path is a controversial
|
||||||
# practice, though; if you don't like it you can remove the following setting.
|
# practice, though; if you don't like it you can remove the following setting,
|
||||||
|
# or use the --disable-rpath option.
|
||||||
if test "x$BIND10_RPATH" != "x"; then
|
if test "x$BIND10_RPATH" != "x"; then
|
||||||
LDFLAGS="$LDFLAGS $BIND10_RPATH"
|
LDFLAGS="$LDFLAGS $BIND10_RPATH"
|
||||||
fi
|
fi
|
||||||
|
@@ -3,29 +3,38 @@ dnl
|
|||||||
dnl @summary figure out whether and which "rpath" linker option is available
|
dnl @summary figure out whether and which "rpath" linker option is available
|
||||||
dnl
|
dnl
|
||||||
dnl This macro checks if the linker supports an option to embed a path
|
dnl This macro checks if the linker supports an option to embed a path
|
||||||
dnl to a runtime library (often installed in an uncommon place), such as
|
dnl to a runtime library (often installed in an uncommon place), such as the
|
||||||
dnl gcc's -rpath option. If found, it sets the ISC_RPATH_FLAG variable to
|
dnl commonly used -R option. If found, it sets the ISC_RPATH_FLAG variable to
|
||||||
dnl the found option flag. The main configure.ac can use it as follows:
|
dnl the found option flag. The main configure.ac can use it as follows:
|
||||||
dnl if test "x$ISC_RPATH_FLAG" != "x"; then
|
dnl if test "x$ISC_RPATH_FLAG" != "x"; then
|
||||||
dnl LDFLAGS="$LDFLAGS ${ISC_RPATH_FLAG}/usr/local/lib/some_library"
|
dnl LDFLAGS="$LDFLAGS ${ISC_RPATH_FLAG}/usr/local/lib/some_library"
|
||||||
dnl fi
|
dnl fi
|
||||||
|
dnl
|
||||||
|
dnl If you pass --disable-rpath to configure, ISC_RPATH_FLAG is not set
|
||||||
|
|
||||||
AC_DEFUN([AX_ISC_RPATH], [
|
AC_DEFUN([AX_ISC_RPATH], [
|
||||||
|
|
||||||
# We'll tweak both CXXFLAGS and CCFLAGS so this function will work whichever
|
AC_ARG_ENABLE(rpath,
|
||||||
# language is used in the main script. Note also that it's not LDFLAGS;
|
[AC_HELP_STRING([--disable-rpath], [don't hardcode library path into binaries])],
|
||||||
# technically this is a linker flag, but we've noticed $LDFLAGS can be placed
|
rpath=$enableval, rpath=yes)
|
||||||
# where the compiler could interpret it as a compiler option, leading to
|
|
||||||
# subtle failure mode. So, in the check below using the compiler flag is
|
if test x$rpath != xno; then
|
||||||
# safer (in the actual Makefiles the flag should be set in LDFLAGS).
|
# We'll tweak both CXXFLAGS and CCFLAGS so this function will work
|
||||||
|
# whichever language is used in the main script. Note also that it's not
|
||||||
|
#LDFLAGS; technically this is a linker flag, but we've noticed $LDFLAGS
|
||||||
|
# can be placed where the compiler could interpret it as a compiler
|
||||||
|
# option, leading to subtle failure mode. So, in the check below using
|
||||||
|
# the compiler flag is safer (in the actual Makefiles the flag should be
|
||||||
|
# set in LDFLAGS).
|
||||||
CXXFLAGS_SAVED="$CXXFLAGS"
|
CXXFLAGS_SAVED="$CXXFLAGS"
|
||||||
CXXFLAGS="$CXXFLAGS -Wl,-R/usr/lib"
|
CXXFLAGS="$CXXFLAGS -Wl,-R/usr/lib"
|
||||||
CCFLAGS_SAVED="$CCFLAGS"
|
CCFLAGS_SAVED="$CCFLAGS"
|
||||||
CCFLAGS="$CCFLAGS -Wl,-R/usr/lib"
|
CCFLAGS="$CCFLAGS -Wl,-R/usr/lib"
|
||||||
|
|
||||||
# check -Wl,-R and -R rather than gcc specific -rpath to be as portable
|
# check -Wl,-R and -R rather than gcc specific -rpath to be as portable
|
||||||
# as possible. -Wl,-R seems to be safer, so we try it first. In some cases
|
# as possible. -Wl,-R seems to be safer, so we try it first. In some
|
||||||
# -R is not actually recognized but AC_TRY_LINK doesn't fail due to that.
|
# cases -R is not actually recognized but AC_TRY_LINK doesn't fail due to
|
||||||
|
# that.
|
||||||
AC_MSG_CHECKING([whether -Wl,-R flag is available in linker])
|
AC_MSG_CHECKING([whether -Wl,-R flag is available in linker])
|
||||||
AC_TRY_LINK([],[],
|
AC_TRY_LINK([],[],
|
||||||
[ AC_MSG_RESULT(yes)
|
[ AC_MSG_RESULT(yes)
|
||||||
@@ -42,5 +51,6 @@ AC_TRY_LINK([],[],
|
|||||||
|
|
||||||
CXXFLAGS=$CXXFLAGS_SAVED
|
CXXFLAGS=$CXXFLAGS_SAVED
|
||||||
CCFLAGS=$CCFLAGS_SAVED
|
CCFLAGS=$CCFLAGS_SAVED
|
||||||
|
fi
|
||||||
|
|
||||||
])dnl AX_ISC_RPATH
|
])dnl AX_ISC_RPATH
|
||||||
|
@@ -93,7 +93,7 @@
|
|||||||
file. It has the same name, with ".backup" appended to it. If a
|
file. It has the same name, with ".backup" appended to it. If a
|
||||||
file of that name already exists, the file will have the suffix
|
file of that name already exists, the file will have the suffix
|
||||||
".backup-1". If that exists, the file will be suffixed ".backup-2",
|
".backup-1". If that exists, the file will be suffixed ".backup-2",
|
||||||
and so on). Exit status is 0 if the upgrade is either succesful or
|
and so on). Exit status is 0 if the upgrade is either successful or
|
||||||
aborted by the user, and non-zero if there is an error.
|
aborted by the user, and non-zero if there is an error.
|
||||||
</para>
|
</para>
|
||||||
|
|
||||||
|
@@ -687,7 +687,7 @@ public:
|
|||||||
virtual void commit() {
|
virtual void commit() {
|
||||||
if (options_ == NULL) {
|
if (options_ == NULL) {
|
||||||
isc_throw(isc::InvalidOperation, "parser logic error: storage must be set before "
|
isc_throw(isc::InvalidOperation, "parser logic error: storage must be set before "
|
||||||
"commiting option data.");
|
"committing option data.");
|
||||||
} else if (!option_descriptor_.option) {
|
} else if (!option_descriptor_.option) {
|
||||||
// Before we can commit the new option should be configured. If it is not
|
// Before we can commit the new option should be configured. If it is not
|
||||||
// than somebody must have called commit() before build().
|
// than somebody must have called commit() before build().
|
||||||
@@ -1855,7 +1855,7 @@ configureDhcp4Server(Dhcpv4Srv&, ConstElementPtr config_set) {
|
|||||||
LOG_INFO(dhcp4_logger, DHCP4_CONFIG_COMPLETE).arg(config_details);
|
LOG_INFO(dhcp4_logger, DHCP4_CONFIG_COMPLETE).arg(config_details);
|
||||||
|
|
||||||
// Everything was fine. Configuration is successful.
|
// Everything was fine. Configuration is successful.
|
||||||
answer = isc::config::createAnswer(0, "Configuration commited.");
|
answer = isc::config::createAnswer(0, "Configuration committed.");
|
||||||
return (answer);
|
return (answer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -61,6 +61,7 @@ Dhcpv4Srv::Dhcpv4Srv(uint16_t port, const char* dbconfig) {
|
|||||||
string srvid_file = CfgMgr::instance().getDataDir() + "/" + string(SERVER_ID_FILE);
|
string srvid_file = CfgMgr::instance().getDataDir() + "/" + string(SERVER_ID_FILE);
|
||||||
if (loadServerID(srvid_file)) {
|
if (loadServerID(srvid_file)) {
|
||||||
LOG_DEBUG(dhcp4_logger, DBG_DHCP4_START, DHCP4_SERVERID_LOADED)
|
LOG_DEBUG(dhcp4_logger, DBG_DHCP4_START, DHCP4_SERVERID_LOADED)
|
||||||
|
.arg(srvidToString(getServerID()))
|
||||||
.arg(srvid_file);
|
.arg(srvid_file);
|
||||||
} else {
|
} else {
|
||||||
generateServerID();
|
generateServerID();
|
||||||
|
@@ -716,7 +716,7 @@ public:
|
|||||||
virtual void commit() {
|
virtual void commit() {
|
||||||
if (options_ == NULL) {
|
if (options_ == NULL) {
|
||||||
isc_throw(isc::InvalidOperation, "parser logic error: storage must be set before "
|
isc_throw(isc::InvalidOperation, "parser logic error: storage must be set before "
|
||||||
"commiting option data.");
|
"committing option data.");
|
||||||
} else if (!option_descriptor_.option) {
|
} else if (!option_descriptor_.option) {
|
||||||
// Before we can commit the new option should be configured. If it is not
|
// Before we can commit the new option should be configured. If it is not
|
||||||
// than somebody must have called commit() before build().
|
// than somebody must have called commit() before build().
|
||||||
@@ -1908,7 +1908,7 @@ configureDhcp6Server(Dhcpv6Srv&, ConstElementPtr config_set) {
|
|||||||
LOG_INFO(dhcp6_logger, DHCP6_CONFIG_COMPLETE).arg(config_details);
|
LOG_INFO(dhcp6_logger, DHCP6_CONFIG_COMPLETE).arg(config_details);
|
||||||
|
|
||||||
// Everything was fine. Configuration is successful.
|
// Everything was fine. Configuration is successful.
|
||||||
answer = isc::config::createAnswer(0, "Configuration commited.");
|
answer = isc::config::createAnswer(0, "Configuration committed.");
|
||||||
return (answer);
|
return (answer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -78,6 +78,7 @@ Dhcpv6Srv::Dhcpv6Srv(uint16_t port)
|
|||||||
string duid_file = CfgMgr::instance().getDataDir() + "/" + string(SERVER_DUID_FILE);
|
string duid_file = CfgMgr::instance().getDataDir() + "/" + string(SERVER_DUID_FILE);
|
||||||
if (loadServerID(duid_file)) {
|
if (loadServerID(duid_file)) {
|
||||||
LOG_DEBUG(dhcp6_logger, DBG_DHCP6_START, DHCP6_SERVERID_LOADED)
|
LOG_DEBUG(dhcp6_logger, DBG_DHCP6_START, DHCP6_SERVERID_LOADED)
|
||||||
|
.arg(duidToString(getServerID()))
|
||||||
.arg(duid_file);
|
.arg(duid_file);
|
||||||
} else {
|
} else {
|
||||||
generateServerID();
|
generateServerID();
|
||||||
|
@@ -153,13 +153,19 @@ class MockCC(MockModuleCCSession):
|
|||||||
def remove_remote_config(self, module_name):
|
def remove_remote_config(self, module_name):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
class MockRRsetCollection:
|
||||||
|
'''
|
||||||
|
A mock RRset collection. We don't use it really (we mock the method that
|
||||||
|
it is passed to too), so it's empty.
|
||||||
|
'''
|
||||||
|
pass
|
||||||
|
|
||||||
class MockDataSourceClient():
|
class MockDataSourceClient():
|
||||||
'''A simple mock data source client.
|
'''A simple mock data source client.
|
||||||
|
|
||||||
This class provides a minimal set of wrappers related the data source
|
This class provides a minimal set of wrappers related the data source
|
||||||
API that would be used by Diff objects. For our testing purposes they
|
API that would be used by Diff objects. For our testing purposes they
|
||||||
only keep truck of the history of the changes.
|
only keep track of the history of the changes.
|
||||||
|
|
||||||
'''
|
'''
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.force_fail = False # if True, raise an exception on commit
|
self.force_fail = False # if True, raise an exception on commit
|
||||||
@@ -217,6 +223,12 @@ class MockDataSourceClient():
|
|||||||
self._journaling_enabled = journaling
|
self._journaling_enabled = journaling
|
||||||
return self
|
return self
|
||||||
|
|
||||||
|
def get_rrset_collection(self):
|
||||||
|
'''
|
||||||
|
Pretend to be a zone updater and provide a (dummy) rrset collection.
|
||||||
|
'''
|
||||||
|
return MockRRsetCollection()
|
||||||
|
|
||||||
def add_rrset(self, rrset):
|
def add_rrset(self, rrset):
|
||||||
self.diffs.append(('add', rrset))
|
self.diffs.append(('add', rrset))
|
||||||
|
|
||||||
@@ -726,11 +738,27 @@ class TestXfrinConnection(unittest.TestCase):
|
|||||||
'tsig_1st': None,
|
'tsig_1st': None,
|
||||||
'tsig_2nd': None
|
'tsig_2nd': None
|
||||||
}
|
}
|
||||||
|
self.__orig_check_zone = xfrin.check_zone
|
||||||
|
xfrin.check_zone = self.__check_zone
|
||||||
|
self._check_zone_result = True
|
||||||
|
self._check_zone_params = None
|
||||||
|
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
self.conn.close()
|
self.conn.close()
|
||||||
if os.path.exists(TEST_DB_FILE):
|
if os.path.exists(TEST_DB_FILE):
|
||||||
os.remove(TEST_DB_FILE)
|
os.remove(TEST_DB_FILE)
|
||||||
|
xfrin.check_zone = self.__orig_check_zone
|
||||||
|
|
||||||
|
def __check_zone(self, name, rrclass, rrsets, callbacks):
|
||||||
|
'''
|
||||||
|
A mock function used instead of dns.check_zone.
|
||||||
|
'''
|
||||||
|
self._check_zone_params = (name, rrclass, rrsets, callbacks)
|
||||||
|
# Call both callbacks to see they do nothing. This checks
|
||||||
|
# the transfer depends on the result only.
|
||||||
|
callbacks[0]("Test error")
|
||||||
|
callbacks[1]("Test warning")
|
||||||
|
return self._check_zone_result
|
||||||
|
|
||||||
def _create_normal_response_data(self):
|
def _create_normal_response_data(self):
|
||||||
# This helper method creates a simple sequence of DNS messages that
|
# This helper method creates a simple sequence of DNS messages that
|
||||||
@@ -825,6 +853,7 @@ class TestAXFR(TestXfrinConnection):
|
|||||||
|
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
time.time = self.orig_time_time
|
time.time = self.orig_time_time
|
||||||
|
super().tearDown()
|
||||||
|
|
||||||
def __create_mock_tsig(self, key, error, has_last_signature=True):
|
def __create_mock_tsig(self, key, error, has_last_signature=True):
|
||||||
# This helper function creates a MockTSIGContext for a given key
|
# This helper function creates a MockTSIGContext for a given key
|
||||||
@@ -1297,6 +1326,33 @@ class TestAXFR(TestXfrinConnection):
|
|||||||
[[('add', ns_rr), ('add', a_rr), ('add', soa_rrset)]],
|
[[('add', ns_rr), ('add', a_rr), ('add', soa_rrset)]],
|
||||||
self.conn._datasrc_client.committed_diffs)
|
self.conn._datasrc_client.committed_diffs)
|
||||||
|
|
||||||
|
def test_axfr_response_fail_validation(self):
|
||||||
|
"""
|
||||||
|
Test we reject a zone transfer if it fails the check_zone validation.
|
||||||
|
"""
|
||||||
|
a_rr = self._create_a('192.0.2.1')
|
||||||
|
self.conn._send_query(RRType.AXFR())
|
||||||
|
self.conn.reply_data = self.conn.create_response_data(
|
||||||
|
questions=[Question(TEST_ZONE_NAME, TEST_RRCLASS,
|
||||||
|
RRType.AXFR())],
|
||||||
|
# begin serial=1230, end serial=1234. end will be used.
|
||||||
|
answers=[begin_soa_rrset, a_rr, soa_rrset])
|
||||||
|
# Make it fail the validation
|
||||||
|
self._check_zone_result = False
|
||||||
|
self.assertRaises(XfrinZoneError, self.conn._handle_xfrin_responses)
|
||||||
|
self.assertEqual(type(XfrinAXFREnd()), type(self.conn.get_xfrstate()))
|
||||||
|
self.assertEqual([], self.conn._datasrc_client.committed_diffs)
|
||||||
|
# Check the validation is called with the correct parameters
|
||||||
|
self.assertEqual(TEST_ZONE_NAME, self._check_zone_params[0])
|
||||||
|
self.assertEqual(TEST_RRCLASS, self._check_zone_params[1])
|
||||||
|
self.assertTrue(isinstance(self._check_zone_params[2],
|
||||||
|
MockRRsetCollection))
|
||||||
|
# Check we can safely call the callbacks. They have no sideeffects
|
||||||
|
# we can check (checking logging is hard), but we at least check
|
||||||
|
# they don't crash.
|
||||||
|
self._check_zone_params[3][0]("Test error")
|
||||||
|
self._check_zone_params[3][1]("Test warning")
|
||||||
|
|
||||||
def test_axfr_response_extra(self):
|
def test_axfr_response_extra(self):
|
||||||
'''Test with an extra RR after the end of AXFR session.
|
'''Test with an extra RR after the end of AXFR session.
|
||||||
|
|
||||||
@@ -1499,6 +1555,15 @@ class TestAXFR(TestXfrinConnection):
|
|||||||
self.conn.response_generator = self._create_normal_response_data
|
self.conn.response_generator = self._create_normal_response_data
|
||||||
self.assertEqual(self.conn.do_xfrin(False), XFRIN_FAIL)
|
self.assertEqual(self.conn.do_xfrin(False), XFRIN_FAIL)
|
||||||
|
|
||||||
|
def test_do_xfrin_invalid_zone(self):
|
||||||
|
"""
|
||||||
|
Test receiving an invalid zone. We mock the check and see the whole
|
||||||
|
transfer is rejected.
|
||||||
|
"""
|
||||||
|
self._check_zone_result = False
|
||||||
|
self.conn.response_generator = self._create_normal_response_data
|
||||||
|
self.assertEqual(self.conn.do_xfrin(False), XFRIN_FAIL)
|
||||||
|
|
||||||
def test_do_soacheck_and_xfrin(self):
|
def test_do_soacheck_and_xfrin(self):
|
||||||
self.conn.response_generator = self._create_soa_response_data
|
self.conn.response_generator = self._create_soa_response_data
|
||||||
self.assertEqual(self.conn.do_xfrin(True), XFRIN_OK)
|
self.assertEqual(self.conn.do_xfrin(True), XFRIN_OK)
|
||||||
@@ -1576,6 +1641,26 @@ class TestIXFRResponse(TestXfrinConnection):
|
|||||||
[[('delete', begin_soa_rrset), ('add', soa_rrset)]],
|
[[('delete', begin_soa_rrset), ('add', soa_rrset)]],
|
||||||
self.conn._datasrc_client.committed_diffs)
|
self.conn._datasrc_client.committed_diffs)
|
||||||
|
|
||||||
|
def test_ixfr_response_fail_validation(self):
|
||||||
|
'''
|
||||||
|
An IXFR that fails validation later on. Check it is rejected.
|
||||||
|
'''
|
||||||
|
self.conn.reply_data = self.conn.create_response_data(
|
||||||
|
questions=[Question(TEST_ZONE_NAME, TEST_RRCLASS, RRType.IXFR())],
|
||||||
|
answers=[soa_rrset, begin_soa_rrset, soa_rrset, soa_rrset])
|
||||||
|
self._check_zone_result = False
|
||||||
|
self.assertRaises(XfrinZoneError, self.conn._handle_xfrin_responses)
|
||||||
|
self.assertEqual([], self.conn._datasrc_client.committed_diffs)
|
||||||
|
self.assertEqual(TEST_ZONE_NAME, self._check_zone_params[0])
|
||||||
|
self.assertEqual(TEST_RRCLASS, self._check_zone_params[1])
|
||||||
|
self.assertTrue(isinstance(self._check_zone_params[2],
|
||||||
|
MockRRsetCollection))
|
||||||
|
# Check we can safely call the callbacks. They have no sideeffects
|
||||||
|
# we can check (checking logging is hard), but we at least check
|
||||||
|
# they don't crash.
|
||||||
|
self._check_zone_params[3][0]("Test error")
|
||||||
|
self._check_zone_params[3][1]("Test warning")
|
||||||
|
|
||||||
def test_ixfr_response_multi_sequences(self):
|
def test_ixfr_response_multi_sequences(self):
|
||||||
'''Similar to the previous case, but with multiple diff seqs.
|
'''Similar to the previous case, but with multiple diff seqs.
|
||||||
|
|
||||||
|
@@ -36,6 +36,7 @@ from isc.xfrin.diff import Diff
|
|||||||
from isc.server_common.auth_command import auth_loadzone_command
|
from isc.server_common.auth_command import auth_loadzone_command
|
||||||
from isc.server_common.tsig_keyring import init_keyring, get_keyring
|
from isc.server_common.tsig_keyring import init_keyring, get_keyring
|
||||||
from isc.log_messages.xfrin_messages import *
|
from isc.log_messages.xfrin_messages import *
|
||||||
|
from isc.dns import *
|
||||||
|
|
||||||
isc.log.init("b10-xfrin", buffer=True)
|
isc.log.init("b10-xfrin", buffer=True)
|
||||||
logger = isc.log.Logger("xfrin")
|
logger = isc.log.Logger("xfrin")
|
||||||
@@ -45,13 +46,6 @@ logger = isc.log.Logger("xfrin")
|
|||||||
DBG_PROCESS = logger.DBGLVL_TRACE_BASIC
|
DBG_PROCESS = logger.DBGLVL_TRACE_BASIC
|
||||||
DBG_COMMANDS = logger.DBGLVL_TRACE_DETAIL
|
DBG_COMMANDS = logger.DBGLVL_TRACE_DETAIL
|
||||||
|
|
||||||
try:
|
|
||||||
from pydnspp import *
|
|
||||||
except ImportError as e:
|
|
||||||
# C++ loadable module may not be installed; even so the xfrin process
|
|
||||||
# must keep running, so we warn about it and move forward.
|
|
||||||
logger.error(XFRIN_IMPORT_DNS, str(e))
|
|
||||||
|
|
||||||
isc.util.process.rename()
|
isc.util.process.rename()
|
||||||
|
|
||||||
# If B10_FROM_BUILD is set in the environment, we use data files
|
# If B10_FROM_BUILD is set in the environment, we use data files
|
||||||
@@ -100,8 +94,17 @@ class XfrinProtocolError(Exception):
|
|||||||
'''
|
'''
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
class XfrinZoneError(Exception):
|
||||||
|
'''
|
||||||
|
An exception raised when the received zone is broken enough to be unusable.
|
||||||
|
'''
|
||||||
|
pass
|
||||||
|
|
||||||
class XfrinZoneUptodate(Exception):
|
class XfrinZoneUptodate(Exception):
|
||||||
'''TBD
|
'''
|
||||||
|
Thrown when the zone is already up to date, so there's no need to download
|
||||||
|
the zone. This is not really an error case (but it's still an exceptional
|
||||||
|
condition and the control flow is different than usual).
|
||||||
'''
|
'''
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@@ -427,10 +430,10 @@ class XfrinIXFRAdd(XfrinState):
|
|||||||
conn.get_transfer_stats().ixfr_changeset_count += 1
|
conn.get_transfer_stats().ixfr_changeset_count += 1
|
||||||
soa_serial = get_soa_serial(rr.get_rdata()[0])
|
soa_serial = get_soa_serial(rr.get_rdata()[0])
|
||||||
if soa_serial == conn._end_serial:
|
if soa_serial == conn._end_serial:
|
||||||
# The final part is there. Check all was signed
|
# The final part is there. Finish the transfer by
|
||||||
# and commit it to the database.
|
# checking the last TSIG (if required), the zone data and
|
||||||
conn._check_response_tsig_last()
|
# commiting.
|
||||||
conn._diff.commit()
|
conn.finish_transfer()
|
||||||
self.set_xfrstate(conn, XfrinIXFREnd())
|
self.set_xfrstate(conn, XfrinIXFREnd())
|
||||||
return True
|
return True
|
||||||
elif soa_serial != conn._current_serial:
|
elif soa_serial != conn._current_serial:
|
||||||
@@ -500,15 +503,11 @@ class XfrinAXFREnd(XfrinState):
|
|||||||
"""
|
"""
|
||||||
Final processing after processing an entire AXFR session.
|
Final processing after processing an entire AXFR session.
|
||||||
|
|
||||||
In this process all the AXFR changes are committed to the
|
This simply calls the finish_transfer method of the connection
|
||||||
data source.
|
that ensures it is signed by TSIG (if required), the zone data
|
||||||
|
is valid and commits it.
|
||||||
There might be more actions here, but for now we simply return False,
|
|
||||||
indicating there will be no more message to receive.
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
conn._check_response_tsig_last()
|
conn.finish_transfer()
|
||||||
conn._diff.commit()
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
class XfrinTransferStats:
|
class XfrinTransferStats:
|
||||||
@@ -805,6 +804,31 @@ class XfrinConnection(asyncore.dispatcher):
|
|||||||
raise XfrinProtocolError('TSIG verify fail: no TSIG on last '+
|
raise XfrinProtocolError('TSIG verify fail: no TSIG on last '+
|
||||||
'message')
|
'message')
|
||||||
|
|
||||||
|
def __validate_error(self, reason):
|
||||||
|
'''
|
||||||
|
Used as error callback below.
|
||||||
|
'''
|
||||||
|
logger.error(XFRIN_ZONE_INVALID, self._zone_name, self._rrclass,
|
||||||
|
reason)
|
||||||
|
|
||||||
|
def __validate_warning(self, reason):
|
||||||
|
'''
|
||||||
|
Used as warning callback below.
|
||||||
|
'''
|
||||||
|
logger.warn(XFRIN_ZONE_WARN, self._zone_name, self._rrclass, reason)
|
||||||
|
|
||||||
|
def finish_transfer(self):
|
||||||
|
"""
|
||||||
|
Perform any necessary checks after a transfer. Then complete the
|
||||||
|
transfer by commiting the transaction into the data source.
|
||||||
|
"""
|
||||||
|
self._check_response_tsig_last()
|
||||||
|
if not check_zone(self._zone_name, self._rrclass,
|
||||||
|
self._diff.get_rrset_collection(),
|
||||||
|
(self.__validate_error, self.__validate_warning)):
|
||||||
|
raise XfrinZoneError('Validation of the new zone failed')
|
||||||
|
self._diff.commit()
|
||||||
|
|
||||||
def __parse_soa_response(self, msg, response_data):
|
def __parse_soa_response(self, msg, response_data):
|
||||||
'''Parse a response to SOA query and extract the SOA from answer.
|
'''Parse a response to SOA query and extract the SOA from answer.
|
||||||
|
|
||||||
@@ -950,8 +974,16 @@ class XfrinConnection(asyncore.dispatcher):
|
|||||||
# of trying another primary server, etc, but for now we treat it
|
# of trying another primary server, etc, but for now we treat it
|
||||||
# as "success".
|
# as "success".
|
||||||
pass
|
pass
|
||||||
|
except XfrinZoneError:
|
||||||
|
# The log message doesn't contain the exception text, since there's
|
||||||
|
# only one place where the exception is thrown now and it'd be the
|
||||||
|
# same generic message every time.
|
||||||
|
logger.error(XFRIN_INVALID_ZONE_DATA, self.zone_str(),
|
||||||
|
format_addrinfo(self._master_addrinfo))
|
||||||
|
ret = XFRIN_FAIL
|
||||||
except XfrinProtocolError as e:
|
except XfrinProtocolError as e:
|
||||||
logger.info(XFRIN_XFR_TRANSFER_PROTOCOL_ERROR, req_str,
|
# FIXME: Why is this .info? Even the messageID contains "ERROR".
|
||||||
|
logger.info(XFRIN_XFR_TRANSFER_PROTOCOL_VIOLATION, req_str,
|
||||||
self.zone_str(),
|
self.zone_str(),
|
||||||
format_addrinfo(self._master_addrinfo), str(e))
|
format_addrinfo(self._master_addrinfo), str(e))
|
||||||
ret = XFRIN_FAIL
|
ret = XFRIN_FAIL
|
||||||
|
@@ -77,6 +77,11 @@ is not equal to the requested SOA serial.
|
|||||||
There was an error importing the python DNS module pydnspp. The most
|
There was an error importing the python DNS module pydnspp. The most
|
||||||
likely cause is a PYTHONPATH problem.
|
likely cause is a PYTHONPATH problem.
|
||||||
|
|
||||||
|
% XFRIN_INVALID_ZONE_DATA zone %1 received from %2 is broken and unusable
|
||||||
|
The zone was received, but it failed sanity validation. The previous version
|
||||||
|
of zone (if any is available) will be used. Look for previous
|
||||||
|
XFRIN_ZONE_INVALID messages to see the exact problem(s).
|
||||||
|
|
||||||
% XFRIN_IXFR_TRANSFER_SUCCESS incremental IXFR transfer of zone %1 succeeded (messages: %2, changesets: %3, deletions: %4, additions: %5, bytes: %6, run time: %7 seconds, %8 bytes/second)
|
% XFRIN_IXFR_TRANSFER_SUCCESS incremental IXFR transfer of zone %1 succeeded (messages: %2, changesets: %3, deletions: %4, additions: %5, bytes: %6, run time: %7 seconds, %8 bytes/second)
|
||||||
The IXFR transfer for the given zone was successful.
|
The IXFR transfer for the given zone was successful.
|
||||||
The provided information contains the following values:
|
The provided information contains the following values:
|
||||||
@@ -205,7 +210,7 @@ such that the remote server doesn't support IXFR, we don't have the SOA record
|
|||||||
(or the zone at all), we are out of sync, etc. In many of these situations,
|
(or the zone at all), we are out of sync, etc. In many of these situations,
|
||||||
AXFR could still work. Therefore we try that one in case it helps.
|
AXFR could still work. Therefore we try that one in case it helps.
|
||||||
|
|
||||||
% XFRIN_XFR_TRANSFER_PROTOCOL_ERROR %1 transfer of zone %2 with %3 failed: %4
|
% XFRIN_XFR_TRANSFER_PROTOCOL_VIOLATION %1 transfer of zone %2 with %3 failed: %4
|
||||||
The XFR transfer for the given zone has failed due to a protocol
|
The XFR transfer for the given zone has failed due to a protocol
|
||||||
error, such as an unexpected response from the primary server. The
|
error, such as an unexpected response from the primary server. The
|
||||||
error is shown in the log message. It may be because the primary
|
error is shown in the log message. It may be because the primary
|
||||||
@@ -232,6 +237,12 @@ zones at a higher level. In future it is more likely that a separate
|
|||||||
zone management framework is provided, and the situation where the
|
zone management framework is provided, and the situation where the
|
||||||
given zone isn't found in xfrout will be treated as an error.
|
given zone isn't found in xfrout will be treated as an error.
|
||||||
|
|
||||||
|
% XFRIN_ZONE_INVALID Newly received zone %1/%2 fails validation: %3
|
||||||
|
The zone was received successfully, but it failed validation. The problem
|
||||||
|
is severe enough that the new version of zone is discarded and the old version,
|
||||||
|
if any, will stay in use. New transfer will be attempted after some time.
|
||||||
|
The problem needs to be fixed in the zone data on the remote server.
|
||||||
|
|
||||||
% XFRIN_ZONE_MULTIPLE_SOA Zone %1 has %2 SOA RRs
|
% XFRIN_ZONE_MULTIPLE_SOA Zone %1 has %2 SOA RRs
|
||||||
On starting an xfrin session, it is identified that the zone to be
|
On starting an xfrin session, it is identified that the zone to be
|
||||||
transferred has multiple SOA RRs. Such a zone is broken, but could be
|
transferred has multiple SOA RRs. Such a zone is broken, but could be
|
||||||
@@ -258,3 +269,9 @@ 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
|
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
|
happened, and you may want to take a closer look. In this case xfrin
|
||||||
doesn't perform subsequent zone transfer.
|
doesn't perform subsequent zone transfer.
|
||||||
|
|
||||||
|
% XFRIN_ZONE_WARN Newly received zone %1/%2 has a problem: %3
|
||||||
|
The zone was received successfully, but when checking it, it was discovered
|
||||||
|
there's some issue with it. It might be correct, but it should be checked
|
||||||
|
and possibly fixed on the remote server. The problem is described in the
|
||||||
|
message. The problem does not stop the zone from being used.
|
||||||
|
@@ -197,6 +197,16 @@ modify the database). This is what the client would do when such RRs
|
|||||||
were given in a DNS response according to RFC2181. The data in
|
were given in a DNS response according to RFC2181. The data in
|
||||||
database should be checked and fixed.
|
database should be checked and fixed.
|
||||||
|
|
||||||
|
% DATASRC_DATABASE_JOURNALREADER_BADDATA failed to convert a diff to RRset in %1/%2 on %3 between %4 and %5: %6
|
||||||
|
This is an error message indicating that a zone's diff is broken and
|
||||||
|
the data source library failed to convert it to a valid RRset. The
|
||||||
|
most likely cause of this is that someone has manually modified the
|
||||||
|
zone's diff in the database and inserted invalid data as a result.
|
||||||
|
The zone's name and class, database name, and the start and end
|
||||||
|
serials, and an additional detail of the error are shown in the
|
||||||
|
message. The administrator should examine the diff in the database
|
||||||
|
to find any invalid data and fix it.
|
||||||
|
|
||||||
% DATASRC_DATABASE_JOURNALREADER_END %1/%2 on %3 from %4 to %5
|
% DATASRC_DATABASE_JOURNALREADER_END %1/%2 on %3 from %4 to %5
|
||||||
This is a debug message indicating that the program (successfully)
|
This is a debug message indicating that the program (successfully)
|
||||||
reaches the end of sequences of a zone's differences. The zone's name
|
reaches the end of sequences of a zone's differences. The zone's name
|
||||||
@@ -215,16 +225,6 @@ a zone's difference sequences from a database-based data source. The
|
|||||||
zone's name and class, database name, and the start and end serials
|
zone's name and class, database name, and the start and end serials
|
||||||
are shown in the message.
|
are shown in the message.
|
||||||
|
|
||||||
% DATASRC_DATABASE_JOURNALREADER_BADDATA failed to convert a diff to RRset in %1/%2 on %3 between %4 and %5: %6
|
|
||||||
This is an error message indicating that a zone's diff is broken and
|
|
||||||
the data source library failed to convert it to a valid RRset. The
|
|
||||||
most likely cause of this is that someone has manually modified the
|
|
||||||
zone's diff in the database and inserted invalid data as a result.
|
|
||||||
The zone's name and class, database name, and the start and end
|
|
||||||
serials, and an additional detail of the error are shown in the
|
|
||||||
message. The administrator should examine the diff in the database
|
|
||||||
to find any invalid data and fix it.
|
|
||||||
|
|
||||||
% DATASRC_DATABASE_NO_MATCH not match for %2/%3/%4 in %1
|
% DATASRC_DATABASE_NO_MATCH not match for %2/%3/%4 in %1
|
||||||
No match (not even a wildcard) was found in the named data source for the given
|
No match (not even a wildcard) was found in the named data source for the given
|
||||||
name/type/class in the data source.
|
name/type/class in the data source.
|
||||||
@@ -442,6 +442,10 @@ shown name, the search tries the superdomain name that share the shown
|
|||||||
www.example.com. with shown label count of 3, example.com. is being
|
www.example.com. with shown label count of 3, example.com. is being
|
||||||
tried).
|
tried).
|
||||||
|
|
||||||
|
% DATASRC_MEM_FIND_TYPE_AT_ORIGIN origin query for type %1 in in-memory zone %2/%3 successful
|
||||||
|
Debug information. A specific type RRset is requested at a zone origin
|
||||||
|
of an in-memory zone and it is found.
|
||||||
|
|
||||||
% DATASRC_MEM_FIND_ZONE looking for zone '%1'
|
% DATASRC_MEM_FIND_ZONE looking for zone '%1'
|
||||||
Debug information. A zone object for this zone is being searched for in the
|
Debug information. A zone object for this zone is being searched for in the
|
||||||
in-memory data source.
|
in-memory data source.
|
||||||
|
@@ -59,8 +59,7 @@ TreeNodeRRset::getName() const {
|
|||||||
const RRTTL&
|
const RRTTL&
|
||||||
TreeNodeRRset::getTTL() const {
|
TreeNodeRRset::getTTL() const {
|
||||||
if (ttl_ == NULL) {
|
if (ttl_ == NULL) {
|
||||||
util::InputBuffer ttl_buffer(rdataset_->getTTLData(),
|
util::InputBuffer ttl_buffer(ttl_data_, sizeof(uint32_t));
|
||||||
sizeof(uint32_t));
|
|
||||||
ttl_ = new RRTTL(ttl_buffer);
|
ttl_ = new RRTTL(ttl_buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -169,7 +168,7 @@ TreeNodeRRset::toWire(AbstractMessageRenderer& renderer) const {
|
|||||||
// Render the main (non RRSIG) RRs
|
// Render the main (non RRSIG) RRs
|
||||||
const size_t rendered_rdata_count =
|
const size_t rendered_rdata_count =
|
||||||
writeRRs(renderer, rdataset_->getRdataCount(), name_labels,
|
writeRRs(renderer, rdataset_->getRdataCount(), name_labels,
|
||||||
rdataset_->type, rrclass_, rdataset_->getTTLData(), reader,
|
rdataset_->type, rrclass_, ttl_data_, reader,
|
||||||
&RdataReader::iterateRdata);
|
&RdataReader::iterateRdata);
|
||||||
if (renderer.isTruncated()) {
|
if (renderer.isTruncated()) {
|
||||||
return (rendered_rdata_count);
|
return (rendered_rdata_count);
|
||||||
@@ -180,7 +179,7 @@ TreeNodeRRset::toWire(AbstractMessageRenderer& renderer) const {
|
|||||||
// Render any RRSIGs, if we supposed to do so
|
// Render any RRSIGs, if we supposed to do so
|
||||||
const size_t rendered_rrsig_count = dnssec_ok_ ?
|
const size_t rendered_rrsig_count = dnssec_ok_ ?
|
||||||
writeRRs(renderer, rrsig_count_, name_labels, RRType::RRSIG(),
|
writeRRs(renderer, rrsig_count_, name_labels, RRType::RRSIG(),
|
||||||
rrclass_, rdataset_->getTTLData(), reader,
|
rrclass_, ttl_data_, reader,
|
||||||
&RdataReader::iterateSingleSig) : 0;
|
&RdataReader::iterateSingleSig) : 0;
|
||||||
|
|
||||||
return (rendered_rdata_count + rendered_rrsig_count);
|
return (rendered_rdata_count + rendered_rrsig_count);
|
||||||
|
@@ -112,12 +112,34 @@ public:
|
|||||||
const RdataSet* rdataset, bool dnssec_ok) :
|
const RdataSet* rdataset, bool dnssec_ok) :
|
||||||
node_(node), rdataset_(rdataset),
|
node_(node), rdataset_(rdataset),
|
||||||
rrsig_count_(rdataset_->getSigRdataCount()), rrclass_(rrclass),
|
rrsig_count_(rdataset_->getSigRdataCount()), rrclass_(rrclass),
|
||||||
dnssec_ok_(dnssec_ok), name_(NULL), realname_(NULL), ttl_(NULL)
|
dnssec_ok_(dnssec_ok), name_(NULL), realname_(NULL),
|
||||||
|
ttl_data_(rdataset->getTTLData()), ttl_(NULL)
|
||||||
|
{}
|
||||||
|
|
||||||
|
/// \brief Constructor with a specific TTL.
|
||||||
|
///
|
||||||
|
/// This constructor is mostly the same as the normal version, but takes
|
||||||
|
/// an extra parameter, \c ttl_data. It's expected to point to a memory
|
||||||
|
/// region at least for 32 bits, and the corresponding 32-bit data will
|
||||||
|
/// be used as wire-format TTL value of the RRset, instead of the TTL
|
||||||
|
/// associated with \c rdataset.
|
||||||
|
///
|
||||||
|
/// It's the caller's responsibility to guarantee the memory region is
|
||||||
|
/// valid and intact throughout the lifetime of the RRset.
|
||||||
|
///
|
||||||
|
/// \throw None
|
||||||
|
TreeNodeRRset(const dns::RRClass& rrclass, const ZoneNode* node,
|
||||||
|
const RdataSet* rdataset, bool dnssec_ok,
|
||||||
|
const void* ttl_data) :
|
||||||
|
node_(node), rdataset_(rdataset),
|
||||||
|
rrsig_count_(rdataset_->getSigRdataCount()), rrclass_(rrclass),
|
||||||
|
dnssec_ok_(dnssec_ok), name_(NULL), realname_(NULL),
|
||||||
|
ttl_data_(ttl_data), ttl_(NULL)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
/// \brief Constructor for wildcard-expanded owner name.
|
/// \brief Constructor for wildcard-expanded owner name.
|
||||||
///
|
///
|
||||||
/// This constructor is mostly the same as the other version, but takes
|
/// This constructor is mostly the same as the normal version, but takes
|
||||||
/// an extra parameter, \c realname. It effectively overrides the owner
|
/// an extra parameter, \c realname. It effectively overrides the owner
|
||||||
/// name of the RRset; wherever the owner name is used (e.g., in the
|
/// name of the RRset; wherever the owner name is used (e.g., in the
|
||||||
/// \c toWire() method), the specified name will be used instead of
|
/// \c toWire() method), the specified name will be used instead of
|
||||||
@@ -133,7 +155,7 @@ public:
|
|||||||
node_(node), rdataset_(rdataset),
|
node_(node), rdataset_(rdataset),
|
||||||
rrsig_count_(rdataset_->getSigRdataCount()), rrclass_(rrclass),
|
rrsig_count_(rdataset_->getSigRdataCount()), rrclass_(rrclass),
|
||||||
dnssec_ok_(dnssec_ok), name_(NULL), realname_(new dns::Name(realname)),
|
dnssec_ok_(dnssec_ok), name_(NULL), realname_(new dns::Name(realname)),
|
||||||
ttl_(NULL)
|
ttl_data_(rdataset->getTTLData()), ttl_(NULL)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
virtual ~TreeNodeRRset() {
|
virtual ~TreeNodeRRset() {
|
||||||
@@ -255,6 +277,7 @@ private:
|
|||||||
const bool dnssec_ok_;
|
const bool dnssec_ok_;
|
||||||
mutable dns::Name* name_;
|
mutable dns::Name* name_;
|
||||||
const dns::Name* const realname_;
|
const dns::Name* const realname_;
|
||||||
|
const void* const ttl_data_;
|
||||||
mutable dns::RRTTL* ttl_;
|
mutable dns::RRTTL* ttl_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -134,6 +134,31 @@ NSEC3Data::insertName(util::MemorySegment& mem_sgmt, const Name& name,
|
|||||||
result == ZoneTree::ALREADYEXISTS) && node != NULL);
|
result == ZoneTree::ALREADYEXISTS) && node != NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
// A helper to convert a TTL value in network byte order and set it in
|
||||||
|
// ZoneData::min_ttl_. We can use util::OutputBuffer, but copy the logic
|
||||||
|
// here to guarantee it is exception free.
|
||||||
|
// Note: essentially this function is a local (re)implementation of the
|
||||||
|
// standard htonl() library function, but we avoid relying on it in case it's
|
||||||
|
// not available (it's not in the C++ standard library).
|
||||||
|
void
|
||||||
|
setTTLInNetOrder(uint32_t val, uint32_t* result) {
|
||||||
|
uint8_t buf[4];
|
||||||
|
buf[0] = static_cast<uint8_t>((val & 0xff000000) >> 24);
|
||||||
|
buf[1] = static_cast<uint8_t>((val & 0x00ff0000) >> 16);
|
||||||
|
buf[2] = static_cast<uint8_t>((val & 0x0000ff00) >> 8);
|
||||||
|
buf[3] = static_cast<uint8_t>(val & 0x000000ff);
|
||||||
|
std::memcpy(result, buf, sizeof(*result));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ZoneData::ZoneData(ZoneTree* zone_tree, ZoneNode* origin_node) :
|
||||||
|
zone_tree_(zone_tree), origin_node_(origin_node),
|
||||||
|
min_ttl_(0) // tentatively set to silence static checkers
|
||||||
|
{
|
||||||
|
setTTLInNetOrder(RRTTL::MAX_TTL().getValue(), &min_ttl_);
|
||||||
|
}
|
||||||
|
|
||||||
ZoneData*
|
ZoneData*
|
||||||
ZoneData::create(util::MemorySegment& mem_sgmt, const Name& zone_origin) {
|
ZoneData::create(util::MemorySegment& mem_sgmt, const Name& zone_origin) {
|
||||||
// ZoneTree::insert() and ZoneData allocation can throw. See also
|
// ZoneTree::insert() and ZoneData allocation can throw. See also
|
||||||
@@ -178,6 +203,11 @@ ZoneData::insertName(util::MemorySegment& mem_sgmt, const Name& name,
|
|||||||
result == ZoneTree::ALREADYEXISTS) && node != NULL);
|
result == ZoneTree::ALREADYEXISTS) && node != NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ZoneData::setMinTTL(uint32_t min_ttl_val) {
|
||||||
|
setTTLInNetOrder(min_ttl_val, &min_ttl_);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace memory
|
} // namespace memory
|
||||||
} // namespace datasrc
|
} // namespace datasrc
|
||||||
} // datasrc isc
|
} // datasrc isc
|
||||||
|
@@ -287,7 +287,7 @@ private:
|
|||||||
/// from NSEC to NSEC3 or vice versa, support incremental signing, or support
|
/// from NSEC to NSEC3 or vice versa, support incremental signing, or support
|
||||||
/// multiple sets of NSEC3 parameters.
|
/// multiple sets of NSEC3 parameters.
|
||||||
///
|
///
|
||||||
/// One last type of meta data is the status of the zone in terms of DNSSEC
|
/// One other type of meta data is the status of the zone in terms of DNSSEC
|
||||||
/// signing. This class supports the following concepts:
|
/// signing. This class supports the following concepts:
|
||||||
/// - Whether the zone is signed or not, either with NSEC records or NSEC3
|
/// - Whether the zone is signed or not, either with NSEC records or NSEC3
|
||||||
/// records.
|
/// records.
|
||||||
@@ -315,6 +315,15 @@ private:
|
|||||||
/// because we won't have to change the application code when we implement
|
/// because we won't have to change the application code when we implement
|
||||||
/// the future separation.
|
/// the future separation.
|
||||||
///
|
///
|
||||||
|
/// One last type of meta data is the zone's "minimum" TTL. It's expected
|
||||||
|
/// to be a shortcut copy of the minimum field of the zone's SOA RDATA,
|
||||||
|
/// and is expected to be used to create an SOA RR for a negative response,
|
||||||
|
/// whose RR TTL may have to be set to this value according to RFC2308.
|
||||||
|
/// This class is not aware of such usage, however, and only provides a
|
||||||
|
/// simple getter and setter method for this value: \c getMinTTLData() and
|
||||||
|
/// \c setMinTTL(). The user of this class is responsible for setting the
|
||||||
|
/// value with \c setMinTTL() when it loads or updates the SOA RR.
|
||||||
|
///
|
||||||
/// The intended usage of these two status concepts is to implement the
|
/// The intended usage of these two status concepts is to implement the
|
||||||
/// \c ZoneFinder::Context::isNSECSigned() and
|
/// \c ZoneFinder::Context::isNSECSigned() and
|
||||||
/// \c ZoneFinder::Context::isNSEC3Signed() methods. A possible implementation
|
/// \c ZoneFinder::Context::isNSEC3Signed() methods. A possible implementation
|
||||||
@@ -349,9 +358,7 @@ private:
|
|||||||
/// allocator (\c create()), so the constructor is hidden as private.
|
/// allocator (\c create()), so the constructor is hidden as private.
|
||||||
///
|
///
|
||||||
/// It never throws an exception.
|
/// It never throws an exception.
|
||||||
ZoneData(ZoneTree* zone_tree, ZoneNode* origin_node) :
|
ZoneData(ZoneTree* zone_tree, ZoneNode* origin_node);
|
||||||
zone_tree_(zone_tree), origin_node_(origin_node)
|
|
||||||
{}
|
|
||||||
|
|
||||||
// Zone node flags.
|
// Zone node flags.
|
||||||
private:
|
private:
|
||||||
@@ -413,7 +420,7 @@ public:
|
|||||||
///
|
///
|
||||||
/// The class encapsulation ensures that the origin node always exists at
|
/// The class encapsulation ensures that the origin node always exists at
|
||||||
/// the same address, so this method always returns a non-NULL valid
|
/// the same address, so this method always returns a non-NULL valid
|
||||||
/// valid pointer.
|
/// pointer.
|
||||||
///
|
///
|
||||||
/// \throw none
|
/// \throw none
|
||||||
const ZoneNode* getOriginNode() const {
|
const ZoneNode* getOriginNode() const {
|
||||||
@@ -456,6 +463,26 @@ public:
|
|||||||
///
|
///
|
||||||
/// \throw none
|
/// \throw none
|
||||||
const NSEC3Data* getNSEC3Data() const { return (nsec3_data_.get()); }
|
const NSEC3Data* getNSEC3Data() const { return (nsec3_data_.get()); }
|
||||||
|
|
||||||
|
/// \brief Return a pointer to the zone's minimum TTL data.
|
||||||
|
///
|
||||||
|
/// The returned pointer points to a memory region that is valid at least
|
||||||
|
/// for 32 bits, storing the zone's minimum TTL in the network byte
|
||||||
|
/// order. The corresponding 32-bit value as an integer is initially
|
||||||
|
/// set to the value of \c dns::RRTTL::MAX_TTL(), and, once
|
||||||
|
/// \c setMinTTL() is called, set to the value specified at the latest
|
||||||
|
/// call to \c setMinTTL().
|
||||||
|
///
|
||||||
|
/// It returns opaque data to make it clear that unless the wire
|
||||||
|
/// format data is necessary (e.g., when rendering it in a DNS message),
|
||||||
|
/// it should be converted to, e.g., an \c RRTTL object explicitly.
|
||||||
|
///
|
||||||
|
/// The returned pointer is valid as long as the \c ZoneData is valid,
|
||||||
|
/// and the corresponding 32-bit data are the same until \c setMinTTL()
|
||||||
|
/// is called.
|
||||||
|
///
|
||||||
|
/// \throw none
|
||||||
|
const void* getMinTTLData() const { return (&min_ttl_); }
|
||||||
//@}
|
//@}
|
||||||
|
|
||||||
///
|
///
|
||||||
@@ -552,12 +579,32 @@ public:
|
|||||||
nsec3_data_ = nsec3_data;
|
nsec3_data_ = nsec3_data;
|
||||||
return (old);
|
return (old);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// \brief Set the zone's "minimum" TTL.
|
||||||
|
///
|
||||||
|
/// This method updates the recorded minimum TTL of the zone data.
|
||||||
|
/// It's expected to be identical to the value of the Minimum field
|
||||||
|
/// of the SOA RR at the zone apex, but this method does not check the
|
||||||
|
/// consistency; it's the caller's responsibility.
|
||||||
|
///
|
||||||
|
/// While RFC2181 specifies the max TTL value to be 2^31-1, this method
|
||||||
|
/// does not check the range; it accepts any unsigned 32-bit integer
|
||||||
|
/// value. In practice, this shouldn't cause a problem, however, because
|
||||||
|
/// the only expected usage of this value is to use the minimum of this
|
||||||
|
/// value and SOA RR's TTL, and the latter is expected to be in the
|
||||||
|
/// valid range.
|
||||||
|
///
|
||||||
|
/// \throw None
|
||||||
|
/// \param min_ttl_val The minimum TTL value as unsigned 32-bit integer
|
||||||
|
/// in the host byte order.
|
||||||
|
void setMinTTL(uint32_t min_ttl_val);
|
||||||
//@}
|
//@}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const boost::interprocess::offset_ptr<ZoneTree> zone_tree_;
|
const boost::interprocess::offset_ptr<ZoneTree> zone_tree_;
|
||||||
const boost::interprocess::offset_ptr<ZoneNode> origin_node_;
|
const boost::interprocess::offset_ptr<ZoneNode> origin_node_;
|
||||||
boost::interprocess::offset_ptr<NSEC3Data> nsec3_data_;
|
boost::interprocess::offset_ptr<NSEC3Data> nsec3_data_;
|
||||||
|
uint32_t min_ttl_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace memory
|
} // namespace memory
|
||||||
|
@@ -336,6 +336,17 @@ ZoneDataUpdater::addRdataSet(const Name& name, const RRType& rrtype,
|
|||||||
// "NSEC signed")
|
// "NSEC signed")
|
||||||
zone_data_.setSigned(true);
|
zone_data_.setSigned(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If we are adding a new SOA at the origin, update zone's min TTL.
|
||||||
|
// Note: if the input is broken and contains multiple SOAs, the load
|
||||||
|
// or update will be rejected higher level. We just always (though
|
||||||
|
// this should be only once in normal cases) update the TTL.
|
||||||
|
if (rrset && rrtype == RRType::SOA() && is_origin) {
|
||||||
|
// Our own validation ensures the RRset is not empty.
|
||||||
|
zone_data_.setMinTTL(
|
||||||
|
dynamic_cast<const generic::SOA&>(
|
||||||
|
rrset->getRdataIterator()->getCurrent()).getMinimum());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -23,10 +23,13 @@
|
|||||||
#include <dns/name.h>
|
#include <dns/name.h>
|
||||||
#include <dns/rrset.h>
|
#include <dns/rrset.h>
|
||||||
#include <dns/rrtype.h>
|
#include <dns/rrtype.h>
|
||||||
|
#include <dns/rrttl.h>
|
||||||
#include <dns/nsec3hash.h>
|
#include <dns/nsec3hash.h>
|
||||||
|
|
||||||
#include <datasrc/logger.h>
|
#include <datasrc/logger.h>
|
||||||
|
|
||||||
|
#include <util/buffer.h>
|
||||||
|
|
||||||
#include <boost/scoped_ptr.hpp>
|
#include <boost/scoped_ptr.hpp>
|
||||||
#include <boost/bind.hpp>
|
#include <boost/bind.hpp>
|
||||||
|
|
||||||
@@ -104,14 +107,19 @@ createTreeNodeRRset(const ZoneNode* node,
|
|||||||
const RdataSet* rdataset,
|
const RdataSet* rdataset,
|
||||||
const RRClass& rrclass,
|
const RRClass& rrclass,
|
||||||
ZoneFinder::FindOptions options,
|
ZoneFinder::FindOptions options,
|
||||||
const Name* realname = NULL)
|
const Name* realname = NULL,
|
||||||
|
const void* ttl_data = NULL)
|
||||||
{
|
{
|
||||||
const bool dnssec = ((options & ZoneFinder::FIND_DNSSEC) != 0);
|
const bool dnssec = ((options & ZoneFinder::FIND_DNSSEC) != 0);
|
||||||
if (node != NULL && rdataset != NULL) {
|
if (node && rdataset) {
|
||||||
if (realname != NULL) {
|
if (realname) {
|
||||||
return (TreeNodeRRsetPtr(new TreeNodeRRset(*realname, rrclass,
|
return (TreeNodeRRsetPtr(new TreeNodeRRset(*realname, rrclass,
|
||||||
node, rdataset,
|
node, rdataset,
|
||||||
dnssec)));
|
dnssec)));
|
||||||
|
} else if (ttl_data) {
|
||||||
|
assert(!realname); // these two cases should be mixed in our use
|
||||||
|
return (TreeNodeRRsetPtr(new TreeNodeRRset(rrclass, node, rdataset,
|
||||||
|
dnssec, ttl_data)));
|
||||||
} else {
|
} else {
|
||||||
return (TreeNodeRRsetPtr(new TreeNodeRRset(rrclass, node, rdataset,
|
return (TreeNodeRRsetPtr(new TreeNodeRRset(rrclass, node, rdataset,
|
||||||
dnssec)));
|
dnssec)));
|
||||||
@@ -229,6 +237,12 @@ createNSEC3RRset(const ZoneNode* node, const RRClass& rrclass) {
|
|||||||
ZoneFinder::FIND_DNSSEC));
|
ZoneFinder::FIND_DNSSEC));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline RRTTL
|
||||||
|
createTTLFromData(const void* ttl_data) {
|
||||||
|
util::InputBuffer b(ttl_data, sizeof(uint32_t));
|
||||||
|
return (RRTTL(b));
|
||||||
|
}
|
||||||
|
|
||||||
// convenience function to fill in the final details
|
// convenience function to fill in the final details
|
||||||
//
|
//
|
||||||
// Set up ZoneFinderResultContext object as a return value of find(),
|
// Set up ZoneFinderResultContext object as a return value of find(),
|
||||||
@@ -250,7 +264,8 @@ createFindResult(const RRClass& rrclass,
|
|||||||
const RdataSet* rdataset,
|
const RdataSet* rdataset,
|
||||||
ZoneFinder::FindOptions options,
|
ZoneFinder::FindOptions options,
|
||||||
bool wild = false,
|
bool wild = false,
|
||||||
const Name* qname = NULL)
|
const Name* qname = NULL,
|
||||||
|
bool use_minttl = false)
|
||||||
{
|
{
|
||||||
ZoneFinder::FindResultFlags flags = ZoneFinder::RESULT_DEFAULT;
|
ZoneFinder::FindResultFlags flags = ZoneFinder::RESULT_DEFAULT;
|
||||||
const Name* rename = NULL;
|
const Name* rename = NULL;
|
||||||
@@ -268,6 +283,15 @@ createFindResult(const RRClass& rrclass,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (use_minttl && rdataset &&
|
||||||
|
createTTLFromData(zone_data.getMinTTLData()) <
|
||||||
|
createTTLFromData(rdataset->getTTLData())) {
|
||||||
|
return (ZoneFinderResultContext(
|
||||||
|
code,
|
||||||
|
createTreeNodeRRset(node, rdataset, rrclass, options,
|
||||||
|
rename, zone_data.getMinTTLData()),
|
||||||
|
flags, zone_data, node, rdataset));
|
||||||
|
}
|
||||||
return (ZoneFinderResultContext(code, createTreeNodeRRset(node, rdataset,
|
return (ZoneFinderResultContext(code, createTreeNodeRRset(node, rdataset,
|
||||||
rrclass, options,
|
rrclass, options,
|
||||||
rename),
|
rename),
|
||||||
@@ -741,6 +765,44 @@ InMemoryZoneFinder::findAll(const isc::dns::Name& name,
|
|||||||
options))));
|
options))));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The implementation is a special case of the generic findInternal: we know
|
||||||
|
// the qname should have an "exact match" and its node is accessible via
|
||||||
|
// getOriginNode(); and, since there should be at least SOA RR at the origin
|
||||||
|
// the case of CNAME can be eliminated (these should be guaranteed at the load
|
||||||
|
// or update time, but even if they miss a corner case and allows a CNAME to
|
||||||
|
// be added at origin, the zone is broken anyway, so we'd just let this
|
||||||
|
// method return garbage, too). As a result, there can be only too cases
|
||||||
|
// for the result codes: SUCCESS if the requested type of RR exists; NXRRSET
|
||||||
|
// otherwise. Due to its simplicity we implement it separately, rather than
|
||||||
|
// sharing the code with findInternal.
|
||||||
|
boost::shared_ptr<ZoneFinder::Context>
|
||||||
|
InMemoryZoneFinder::findAtOrigin(const isc::dns::RRType& type,
|
||||||
|
bool use_minttl,
|
||||||
|
const FindOptions options)
|
||||||
|
{
|
||||||
|
const ZoneNode* const node = zone_data_.getOriginNode();
|
||||||
|
const RdataSet* const found = RdataSet::find(node->getData(), type);
|
||||||
|
|
||||||
|
if (found != NULL) {
|
||||||
|
LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_FIND_TYPE_AT_ORIGIN).
|
||||||
|
arg(type).arg(getOrigin()).arg(rrclass_);
|
||||||
|
return (ZoneFinderContextPtr(
|
||||||
|
new Context(*this, options, rrclass_,
|
||||||
|
createFindResult(rrclass_, zone_data_, SUCCESS,
|
||||||
|
node, found, options, false,
|
||||||
|
NULL, use_minttl))));
|
||||||
|
}
|
||||||
|
return (ZoneFinderContextPtr(
|
||||||
|
new Context(*this, options, rrclass_,
|
||||||
|
createFindResult(rrclass_, zone_data_, NXRRSET,
|
||||||
|
node,
|
||||||
|
getNSECForNXRRSET(zone_data_,
|
||||||
|
options,
|
||||||
|
node),
|
||||||
|
options, false, NULL,
|
||||||
|
use_minttl))));
|
||||||
|
}
|
||||||
|
|
||||||
ZoneFinderResultContext
|
ZoneFinderResultContext
|
||||||
InMemoryZoneFinder::findInternal(const isc::dns::Name& name,
|
InMemoryZoneFinder::findInternal(const isc::dns::Name& name,
|
||||||
const isc::dns::RRType& type,
|
const isc::dns::RRType& type,
|
||||||
|
@@ -60,6 +60,16 @@ public:
|
|||||||
const isc::dns::RRType& type,
|
const isc::dns::RRType& type,
|
||||||
const FindOptions options = FIND_DEFAULT);
|
const FindOptions options = FIND_DEFAULT);
|
||||||
|
|
||||||
|
/// \brief Search for an RRset of given RR type at the zone origin
|
||||||
|
/// specialized for in-memory data source.
|
||||||
|
///
|
||||||
|
/// This specialized version exploits internal data structure to find
|
||||||
|
/// RRsets at the zone origin and (if \c use_minttl is true) extract
|
||||||
|
/// the SOA Minimum TTL much more efficiently.
|
||||||
|
virtual boost::shared_ptr<ZoneFinder::Context> findAtOrigin(
|
||||||
|
const isc::dns::RRType& type, bool use_minttl,
|
||||||
|
FindOptions options);
|
||||||
|
|
||||||
/// \brief Version of find that returns all types at once
|
/// \brief Version of find that returns all types at once
|
||||||
///
|
///
|
||||||
/// It acts the same as find, just that when the correct node is found,
|
/// It acts the same as find, just that when the correct node is found,
|
||||||
@@ -108,3 +118,7 @@ private:
|
|||||||
} // namespace isc
|
} // namespace isc
|
||||||
|
|
||||||
#endif // DATASRC_MEMORY_ZONE_FINDER_H
|
#endif // DATASRC_MEMORY_ZONE_FINDER_H
|
||||||
|
|
||||||
|
// Local Variables:
|
||||||
|
// mode: c++
|
||||||
|
// End:
|
||||||
|
@@ -194,8 +194,14 @@ checkBasicFields(const AbstractRRset& actual_rrset, const RdataSet* rdataset,
|
|||||||
// a temporary non-copyable object.
|
// a temporary non-copyable object.
|
||||||
boost::shared_ptr<TreeNodeRRset>
|
boost::shared_ptr<TreeNodeRRset>
|
||||||
createRRset(const RRClass& rrclass, const ZoneNode* node,
|
createRRset(const RRClass& rrclass, const ZoneNode* node,
|
||||||
const RdataSet* rdataset, bool dnssec_ok)
|
const RdataSet* rdataset, bool dnssec_ok,
|
||||||
|
const void* ttl_data = NULL)
|
||||||
{
|
{
|
||||||
|
if (ttl_data) {
|
||||||
|
return (boost::shared_ptr<TreeNodeRRset>(
|
||||||
|
new TreeNodeRRset(rrclass, node, rdataset, dnssec_ok,
|
||||||
|
ttl_data)));
|
||||||
|
}
|
||||||
return (boost::shared_ptr<TreeNodeRRset>(
|
return (boost::shared_ptr<TreeNodeRRset>(
|
||||||
new TreeNodeRRset(rrclass, node, rdataset, dnssec_ok)));
|
new TreeNodeRRset(rrclass, node, rdataset, dnssec_ok)));
|
||||||
}
|
}
|
||||||
@@ -243,6 +249,13 @@ TEST_F(TreeNodeRRsetTest, create) {
|
|||||||
true),
|
true),
|
||||||
wildcard_rdataset_, match_name_, rrclass_, RRType::A(),
|
wildcard_rdataset_, match_name_, rrclass_, RRType::A(),
|
||||||
3600, 2, 1);
|
3600, 2, 1);
|
||||||
|
|
||||||
|
// Constructed with explicit TTL
|
||||||
|
const uint32_t ttl = 0; // use 0 to avoid byte-order conversion
|
||||||
|
checkBasicFields(*createRRset(rrclass_, www_node_, a_rdataset_, true,
|
||||||
|
&ttl),
|
||||||
|
a_rdataset_, www_name_, rrclass_, RRType::A(), 0, 2,
|
||||||
|
1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// The following two templated functions are helper to encapsulate the
|
// The following two templated functions are helper to encapsulate the
|
||||||
@@ -336,6 +349,21 @@ TEST_F(TreeNodeRRsetTest, toWire) {
|
|||||||
isc::Unexpected);
|
isc::Unexpected);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
SCOPED_TRACE("with RRSIG, DNSSEC OK, explicit TTL");
|
||||||
|
const uint32_t ttl = 0;
|
||||||
|
const TreeNodeRRset rrset(rrclass_, www_node_, a_rdataset_, true,
|
||||||
|
&ttl);
|
||||||
|
checkToWireResult(expected_renderer, actual_renderer, rrset,
|
||||||
|
www_name_,
|
||||||
|
textToRRset("www.example.com. 0 IN A 192.0.2.1\n"
|
||||||
|
"www.example.com. 0 IN A 192.0.2.2"),
|
||||||
|
textToRRset("www.example.com. 0 IN RRSIG "
|
||||||
|
"A 5 2 3600 20120814220826 "
|
||||||
|
"20120715220826 1234 example.com. FAKE"),
|
||||||
|
true);
|
||||||
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
SCOPED_TRACE("with RRSIG, DNSSEC not OK");
|
SCOPED_TRACE("with RRSIG, DNSSEC not OK");
|
||||||
const TreeNodeRRset rrset(rrclass_, www_node_, a_rdataset_, false);
|
const TreeNodeRRset rrset(rrclass_, www_node_, a_rdataset_, false);
|
||||||
@@ -522,6 +550,14 @@ TEST_F(TreeNodeRRsetTest, toText) {
|
|||||||
// Constructed with RRSIG, and it should be visible.
|
// Constructed with RRSIG, and it should be visible.
|
||||||
checkToText(*createRRset(rrclass_, www_node_, a_rdataset_, true),
|
checkToText(*createRRset(rrclass_, www_node_, a_rdataset_, true),
|
||||||
a_rrset_, a_rrsig_rrset_);
|
a_rrset_, a_rrsig_rrset_);
|
||||||
|
// Same as the previous, but with explicit TTL.
|
||||||
|
const uint32_t ttl = 0;
|
||||||
|
checkToText(*createRRset(rrclass_, www_node_, a_rdataset_, true, &ttl),
|
||||||
|
textToRRset("www.example.com. 0 IN A 192.0.2.1\n"
|
||||||
|
"www.example.com. 0 IN A 192.0.2.2"),
|
||||||
|
textToRRset("www.example.com. 0 IN RRSIG A 5 2 3600 "
|
||||||
|
"20120814220826 20120715220826 1234 example.com. "
|
||||||
|
"FAKE"));
|
||||||
// Constructed with RRSIG, and it should be invisible.
|
// Constructed with RRSIG, and it should be invisible.
|
||||||
checkToText(*createRRset(rrclass_, www_node_, a_rdataset_, false),
|
checkToText(*createRRset(rrclass_, www_node_, a_rdataset_, false),
|
||||||
a_rrset_, ConstRRsetPtr());
|
a_rrset_, ConstRRsetPtr());
|
||||||
@@ -556,6 +592,11 @@ TEST_F(TreeNodeRRsetTest, isSameKind) {
|
|||||||
EXPECT_TRUE(rrset.isSameKind(*createRRset(rrclass_, www_node_,
|
EXPECT_TRUE(rrset.isSameKind(*createRRset(rrclass_, www_node_,
|
||||||
a_rdataset_, true)));
|
a_rdataset_, true)));
|
||||||
|
|
||||||
|
// Similar to the previous, but with explicit (different TTL) => still same
|
||||||
|
const uint32_t ttl = 0;
|
||||||
|
EXPECT_TRUE(rrset.isSameKind(*createRRset(rrclass_, www_node_,
|
||||||
|
a_rdataset_, true, &ttl)));
|
||||||
|
|
||||||
// Same name (node), different type (rdataset) => not same kind
|
// Same name (node), different type (rdataset) => not same kind
|
||||||
EXPECT_FALSE(rrset.isSameKind(*createRRset(rrclass_, www_node_,
|
EXPECT_FALSE(rrset.isSameKind(*createRRset(rrclass_, www_node_,
|
||||||
aaaa_rdataset_, true)));
|
aaaa_rdataset_, true)));
|
||||||
|
@@ -12,13 +12,15 @@
|
|||||||
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||||
// PERFORMANCE OF THIS SOFTWARE.
|
// PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
|
||||||
#include <dns/name.h>
|
#include <datasrc/memory/zone_data_loader.h>
|
||||||
#include <dns/rrclass.h>
|
|
||||||
|
|
||||||
#include <datasrc/memory/rdataset.h>
|
#include <datasrc/memory/rdataset.h>
|
||||||
#include <datasrc/memory/zone_data.h>
|
#include <datasrc/memory/zone_data.h>
|
||||||
#include <datasrc/memory/zone_data_updater.h>
|
#include <datasrc/memory/zone_data_updater.h>
|
||||||
#include <datasrc/memory/zone_data_loader.h>
|
|
||||||
|
#include <util/buffer.h>
|
||||||
|
|
||||||
|
#include <dns/name.h>
|
||||||
|
#include <dns/rrclass.h>
|
||||||
|
|
||||||
#include "memory_segment_test.h"
|
#include "memory_segment_test.h"
|
||||||
|
|
||||||
@@ -62,4 +64,13 @@ TEST_F(ZoneDataLoaderTest, loadRRSIGFollowsNothing) {
|
|||||||
// Teardown checks for memory segment leaks
|
// Teardown checks for memory segment leaks
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(ZoneDataLoaderTest, zoneMinTTL) {
|
||||||
|
// This should hold outside of the loader class, but we do double check.
|
||||||
|
zone_data_ = loadZoneData(mem_sgmt_, zclass_, Name("example.org"),
|
||||||
|
TEST_DATA_DIR
|
||||||
|
"/example.org-nsec3-signed.zone");
|
||||||
|
isc::util::InputBuffer b(zone_data_->getMinTTLData(), sizeof(uint32_t));
|
||||||
|
EXPECT_EQ(RRTTL(1200), RRTTL(b));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -12,19 +12,22 @@
|
|||||||
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||||
// PERFORMANCE OF THIS SOFTWARE.
|
// PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
|
||||||
|
#include <datasrc/memory/zone_data.h>
|
||||||
|
#include <datasrc/memory/rdata_serialization.h>
|
||||||
|
#include <datasrc/memory/rdataset.h>
|
||||||
|
|
||||||
#include "memory_segment_test.h"
|
#include "memory_segment_test.h"
|
||||||
|
|
||||||
#include <dns/rdataclass.h>
|
#include <dns/rdataclass.h>
|
||||||
|
|
||||||
#include <exceptions/exceptions.h>
|
#include <exceptions/exceptions.h>
|
||||||
|
|
||||||
|
#include <util/buffer.h>
|
||||||
|
|
||||||
#include <dns/name.h>
|
#include <dns/name.h>
|
||||||
#include <dns/labelsequence.h>
|
#include <dns/labelsequence.h>
|
||||||
#include <dns/rrclass.h>
|
#include <dns/rrclass.h>
|
||||||
|
#include <dns/rrttl.h>
|
||||||
#include <datasrc/memory/rdata_serialization.h>
|
|
||||||
#include <datasrc/memory/rdataset.h>
|
|
||||||
#include <datasrc/memory/zone_data.h>
|
|
||||||
|
|
||||||
#include <testutils/dnsmessage_test.h>
|
#include <testutils/dnsmessage_test.h>
|
||||||
|
|
||||||
@@ -258,4 +261,21 @@ TEST_F(ZoneDataTest, isSigned) {
|
|||||||
zone_data_->setSigned(false);
|
zone_data_->setSigned(false);
|
||||||
EXPECT_FALSE(zone_data_->isSigned());
|
EXPECT_FALSE(zone_data_->isSigned());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// A simple wrapper to reconstruct an RRTTL object from wire-format TTL
|
||||||
|
// data (32 bits)
|
||||||
|
RRTTL
|
||||||
|
createRRTTL(const void* ttl_data) {
|
||||||
|
isc::util::InputBuffer b(ttl_data, sizeof(uint32_t));
|
||||||
|
return (RRTTL(b));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ZoneDataTest, minTTL) {
|
||||||
|
// By default it's tentatively set to "max TTL"
|
||||||
|
EXPECT_EQ(RRTTL::MAX_TTL(), createRRTTL(zone_data_->getMinTTLData()));
|
||||||
|
|
||||||
|
// Explicitly set, then retrieve it.
|
||||||
|
zone_data_->setMinTTL(1200);
|
||||||
|
EXPECT_EQ(RRTTL(1200), createRRTTL(zone_data_->getMinTTLData()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -12,6 +12,10 @@
|
|||||||
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||||
// PERFORMANCE OF THIS SOFTWARE.
|
// PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
|
||||||
|
#include <datasrc/memory/zone_data_updater.h>
|
||||||
|
#include <datasrc/memory/rdataset.h>
|
||||||
|
#include <datasrc/memory/zone_data.h>
|
||||||
|
|
||||||
#include <testutils/dnsmessage_test.h>
|
#include <testutils/dnsmessage_test.h>
|
||||||
|
|
||||||
#include <exceptions/exceptions.h>
|
#include <exceptions/exceptions.h>
|
||||||
@@ -19,10 +23,7 @@
|
|||||||
#include <dns/name.h>
|
#include <dns/name.h>
|
||||||
#include <dns/rrclass.h>
|
#include <dns/rrclass.h>
|
||||||
#include <dns/rrset.h>
|
#include <dns/rrset.h>
|
||||||
|
#include <dns/rrttl.h>
|
||||||
#include <datasrc/memory/rdataset.h>
|
|
||||||
#include <datasrc/memory/zone_data.h>
|
|
||||||
#include <datasrc/memory/zone_data_updater.h>
|
|
||||||
|
|
||||||
#include "memory_segment_test.h"
|
#include "memory_segment_test.h"
|
||||||
|
|
||||||
@@ -86,6 +87,16 @@ getNode(isc::util::MemorySegment& mem_sgmt, const Name& name,
|
|||||||
return (node);
|
return (node);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(ZoneDataUpdaterTest, zoneMinTTL) {
|
||||||
|
// If we add SOA, zone's min TTL will be updated.
|
||||||
|
updater_->add(textToRRset(
|
||||||
|
"example.org. 3600 IN SOA . . 0 0 0 0 1200",
|
||||||
|
zclass_, zname_),
|
||||||
|
ConstRRsetPtr());
|
||||||
|
isc::util::InputBuffer b(zone_data_->getMinTTLData(), sizeof(uint32_t));
|
||||||
|
EXPECT_EQ(RRTTL(1200), RRTTL(b));
|
||||||
|
}
|
||||||
|
|
||||||
TEST_F(ZoneDataUpdaterTest, rrsigOnly) {
|
TEST_F(ZoneDataUpdaterTest, rrsigOnly) {
|
||||||
// RRSIG that doesn't have covered RRset can be added. The resulting
|
// RRSIG that doesn't have covered RRset can be added. The resulting
|
||||||
// rdataset won't have "normal" RDATA but sig RDATA.
|
// rdataset won't have "normal" RDATA but sig RDATA.
|
||||||
|
@@ -53,19 +53,6 @@ namespace {
|
|||||||
using result::SUCCESS;
|
using result::SUCCESS;
|
||||||
using result::EXIST;
|
using result::EXIST;
|
||||||
|
|
||||||
/// \brief expensive rrset converter
|
|
||||||
///
|
|
||||||
/// converts any specialized rrset (which may not have implemented some
|
|
||||||
/// methods for efficiency) into a 'full' RRsetPtr, for easy use in test
|
|
||||||
/// checks.
|
|
||||||
///
|
|
||||||
/// Done very inefficiently through text representation, speed should not
|
|
||||||
/// be a concern here.
|
|
||||||
ConstRRsetPtr
|
|
||||||
convertRRset(ConstRRsetPtr src) {
|
|
||||||
return (textToRRset(src->toText()));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// \brief Test fixture for the InMemoryZoneFinder class
|
/// \brief Test fixture for the InMemoryZoneFinder class
|
||||||
class InMemoryZoneFinderTest : public ::testing::Test {
|
class InMemoryZoneFinderTest : public ::testing::Test {
|
||||||
// A straightforward pair of textual RR(set) and a RRsetPtr variable
|
// A straightforward pair of textual RR(set) and a RRsetPtr variable
|
||||||
@@ -105,7 +92,6 @@ protected:
|
|||||||
ZoneFinder::FindResultFlags expected_flags =
|
ZoneFinder::FindResultFlags expected_flags =
|
||||||
ZoneFinder::RESULT_DEFAULT);
|
ZoneFinder::RESULT_DEFAULT);
|
||||||
|
|
||||||
public:
|
|
||||||
InMemoryZoneFinderTest() :
|
InMemoryZoneFinderTest() :
|
||||||
class_(RRClass::IN()),
|
class_(RRClass::IN()),
|
||||||
origin_("example.org"),
|
origin_("example.org"),
|
||||||
@@ -119,6 +105,7 @@ public:
|
|||||||
// Note that this contains an out-of-zone RR, and due to the
|
// Note that this contains an out-of-zone RR, and due to the
|
||||||
// validation check of masterLoad() used below, we cannot add SOA.
|
// validation check of masterLoad() used below, we cannot add SOA.
|
||||||
const RRsetData zone_data[] = {
|
const RRsetData zone_data[] = {
|
||||||
|
{"example.org. 300 IN SOA . . 0 0 0 0 100", &rr_soa_},
|
||||||
{"example.org. 300 IN NS ns.example.org.", &rr_ns_},
|
{"example.org. 300 IN NS ns.example.org.", &rr_ns_},
|
||||||
{"example.org. 300 IN A 192.0.2.1", &rr_a_},
|
{"example.org. 300 IN A 192.0.2.1", &rr_a_},
|
||||||
{"ns.example.org. 300 IN A 192.0.2.2", &rr_ns_a_},
|
{"ns.example.org. 300 IN A 192.0.2.2", &rr_ns_a_},
|
||||||
@@ -185,8 +172,19 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
for (unsigned int i = 0; zone_data[i].text != NULL; ++i) {
|
for (unsigned int i = 0; zone_data[i].text != NULL; ++i) {
|
||||||
|
if (zone_data[i].rrset == &rr_soa_) {
|
||||||
|
// This is zone's SOA. We need to specify the origin for
|
||||||
|
// textToRRset; otherwise it would throw.
|
||||||
|
*zone_data[i].rrset = textToRRset(zone_data[i].text, class_,
|
||||||
|
origin_);
|
||||||
|
} else {
|
||||||
|
// For other data, we should rather omit the origin (the root
|
||||||
|
// name will be used by default); there's some out-of-zone
|
||||||
|
// name, which would trigger an exception if we specified
|
||||||
|
// origin_.
|
||||||
*zone_data[i].rrset = textToRRset(zone_data[i].text);
|
*zone_data[i].rrset = textToRRset(zone_data[i].text);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -200,6 +198,24 @@ public:
|
|||||||
updater_.add(rrset, rrset->getRRsig());
|
updater_.add(rrset, rrset->getRRsig());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// \brief expensive rrset converter
|
||||||
|
///
|
||||||
|
/// converts any specialized rrset (which may not have implemented some
|
||||||
|
/// methods for efficiency) into a 'full' RRsetPtr, for easy use in test
|
||||||
|
/// checks.
|
||||||
|
///
|
||||||
|
/// Done very inefficiently through text representation, speed should not
|
||||||
|
/// be a concern here.
|
||||||
|
ConstRRsetPtr
|
||||||
|
convertRRset(ConstRRsetPtr src) {
|
||||||
|
// If the type is SOA, textToRRset performs a stricter check, so we
|
||||||
|
// should specify the origin. For now we don't use out-of-zone
|
||||||
|
// owner names (e.g. for pathological cases) with this method, so it
|
||||||
|
// works for all test data. If future changes break this assumption
|
||||||
|
// we should adjust it.
|
||||||
|
return (textToRRset(src->toText(), class_, origin_));
|
||||||
|
}
|
||||||
|
|
||||||
// Some data to test with
|
// Some data to test with
|
||||||
const RRClass class_;
|
const RRClass class_;
|
||||||
const Name origin_;
|
const Name origin_;
|
||||||
@@ -218,6 +234,8 @@ public:
|
|||||||
RRsetPtr
|
RRsetPtr
|
||||||
// Out of zone RRset
|
// Out of zone RRset
|
||||||
rr_out_,
|
rr_out_,
|
||||||
|
// SOA of example.org
|
||||||
|
rr_soa_,
|
||||||
// NS of example.org
|
// NS of example.org
|
||||||
rr_ns_,
|
rr_ns_,
|
||||||
// A of ns.example.org
|
// A of ns.example.org
|
||||||
@@ -293,31 +311,67 @@ public:
|
|||||||
if (zone_finder == NULL) {
|
if (zone_finder == NULL) {
|
||||||
zone_finder = &zone_finder_;
|
zone_finder = &zone_finder_;
|
||||||
}
|
}
|
||||||
const ConstRRsetPtr answer_sig = answer ? answer->getRRsig() :
|
|
||||||
RRsetPtr(); // note we use the same type as of retval of getRRsig()
|
|
||||||
// The whole block is inside, because we need to check the result and
|
// The whole block is inside, because we need to check the result and
|
||||||
// we can't assign to FindResult
|
// we can't assign to FindResult
|
||||||
EXPECT_NO_THROW({
|
EXPECT_NO_THROW({
|
||||||
ZoneFinderContextPtr find_result(zone_finder->find(
|
ZoneFinderContextPtr find_result(zone_finder->find(
|
||||||
name, rrtype, options));
|
name, rrtype, options));
|
||||||
|
findTestCommon(name, result, find_result, check_answer,
|
||||||
|
answer, expected_flags, options,
|
||||||
|
check_wild_answer);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void findAtOriginTest(const RRType& rrtype,
|
||||||
|
ZoneFinder::Result result,
|
||||||
|
bool check_answer = true,
|
||||||
|
const ConstRRsetPtr& answer = ConstRRsetPtr(),
|
||||||
|
ZoneFinder::FindResultFlags expected_flags =
|
||||||
|
ZoneFinder::RESULT_DEFAULT,
|
||||||
|
memory::InMemoryZoneFinder* zone_finder = NULL,
|
||||||
|
ZoneFinder::FindOptions options =
|
||||||
|
ZoneFinder::FIND_DEFAULT,
|
||||||
|
bool use_minttl = false)
|
||||||
|
{
|
||||||
|
SCOPED_TRACE("findAtOriginTest for " + rrtype.toText());
|
||||||
|
|
||||||
|
if (zone_finder == NULL) {
|
||||||
|
zone_finder = &zone_finder_;
|
||||||
|
}
|
||||||
|
ZoneFinderContextPtr find_result(zone_finder->findAtOrigin(
|
||||||
|
rrtype, use_minttl, options));
|
||||||
|
findTestCommon(origin_, result, find_result, check_answer, answer,
|
||||||
|
expected_flags, options, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
void findTestCommon(const Name& name, ZoneFinder::Result result,
|
||||||
|
ZoneFinderContextPtr find_result,
|
||||||
|
bool check_answer,
|
||||||
|
const ConstRRsetPtr& answer,
|
||||||
|
ZoneFinder::FindResultFlags expected_flags,
|
||||||
|
ZoneFinder::FindOptions options,
|
||||||
|
bool check_wild_answer)
|
||||||
|
{
|
||||||
|
const ConstRRsetPtr answer_sig = answer ? answer->getRRsig() :
|
||||||
|
RRsetPtr(); // note we use the same type as of retval of getRRsig()
|
||||||
|
|
||||||
// Check it returns correct answers
|
// Check it returns correct answers
|
||||||
EXPECT_EQ(result, find_result->code);
|
EXPECT_EQ(result, find_result->code);
|
||||||
EXPECT_EQ((expected_flags & ZoneFinder::RESULT_WILDCARD) != 0,
|
EXPECT_EQ((expected_flags & ZoneFinder::RESULT_WILDCARD) != 0,
|
||||||
find_result->isWildcard());
|
find_result->isWildcard());
|
||||||
EXPECT_EQ((expected_flags & ZoneFinder::RESULT_NSEC_SIGNED)
|
EXPECT_EQ((expected_flags & ZoneFinder::RESULT_NSEC_SIGNED) != 0,
|
||||||
!= 0, find_result->isNSECSigned());
|
find_result->isNSECSigned());
|
||||||
EXPECT_EQ((expected_flags & ZoneFinder::RESULT_NSEC3_SIGNED)
|
EXPECT_EQ((expected_flags & ZoneFinder::RESULT_NSEC3_SIGNED) != 0,
|
||||||
!= 0, find_result->isNSEC3Signed());
|
find_result->isNSEC3Signed());
|
||||||
if (check_answer) {
|
if (check_answer) {
|
||||||
if (!answer) {
|
if (!answer) {
|
||||||
ASSERT_FALSE(find_result->rrset);
|
ASSERT_FALSE(find_result->rrset);
|
||||||
} else {
|
} else {
|
||||||
ASSERT_TRUE(find_result->rrset);
|
ASSERT_TRUE(find_result->rrset);
|
||||||
ConstRRsetPtr result_rrset(
|
ConstRRsetPtr result_rrset(convertRRset(find_result->rrset));
|
||||||
convertRRset(find_result->rrset));
|
|
||||||
rrsetCheck(answer, result_rrset);
|
rrsetCheck(answer, result_rrset);
|
||||||
if (answer_sig &&
|
if (answer_sig && (options & ZoneFinder::FIND_DNSSEC) != 0) {
|
||||||
(options & ZoneFinder::FIND_DNSSEC) != 0) {
|
|
||||||
ASSERT_TRUE(result_rrset->getRRsig());
|
ASSERT_TRUE(result_rrset->getRRsig());
|
||||||
rrsetCheck(answer_sig, result_rrset->getRRsig());
|
rrsetCheck(answer_sig, result_rrset->getRRsig());
|
||||||
} else {
|
} else {
|
||||||
@@ -340,16 +394,14 @@ public:
|
|||||||
wildanswer->addRdata(expectedIt->getCurrent());
|
wildanswer->addRdata(expectedIt->getCurrent());
|
||||||
}
|
}
|
||||||
|
|
||||||
ConstRRsetPtr result_rrset(
|
ConstRRsetPtr result_rrset(convertRRset(find_result->rrset));
|
||||||
convertRRset(find_result->rrset));
|
|
||||||
rrsetCheck(wildanswer, result_rrset);
|
rrsetCheck(wildanswer, result_rrset);
|
||||||
|
|
||||||
// Same for the RRSIG, if any.
|
// Same for the RRSIG, if any.
|
||||||
if (answer_sig) {
|
if (answer_sig) {
|
||||||
ASSERT_TRUE(result_rrset->getRRsig());
|
ASSERT_TRUE(result_rrset->getRRsig());
|
||||||
|
|
||||||
RRsetPtr wildsig(new RRset(name,
|
RRsetPtr wildsig(new RRset(name, answer_sig->getClass(),
|
||||||
answer_sig->getClass(),
|
|
||||||
RRType::RRSIG(),
|
RRType::RRSIG(),
|
||||||
answer_sig->getTTL()));
|
answer_sig->getTTL()));
|
||||||
RdataIteratorPtr expectedIt(
|
RdataIteratorPtr expectedIt(
|
||||||
@@ -360,8 +412,9 @@ public:
|
|||||||
rrsetCheck(wildsig, result_rrset->getRRsig());
|
rrsetCheck(wildsig, result_rrset->getRRsig());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
/**
|
/**
|
||||||
* \brief Calls the findAll on the finder and checks the result.
|
* \brief Calls the findAll on the finder and checks the result.
|
||||||
*/
|
*/
|
||||||
@@ -583,7 +636,6 @@ TEST_F(InMemoryZoneFinderTest, glue) {
|
|||||||
findTest(rr_child_glue_->getName(), RRType::A(), ZoneFinder::DELEGATION,
|
findTest(rr_child_glue_->getName(), RRType::A(), ZoneFinder::DELEGATION,
|
||||||
true, rr_child_ns_);
|
true, rr_child_ns_);
|
||||||
|
|
||||||
|
|
||||||
// If we do it in the "glue OK" mode, we should find the exact match.
|
// If we do it in the "glue OK" mode, we should find the exact match.
|
||||||
findTest(rr_child_glue_->getName(), RRType::A(), ZoneFinder::SUCCESS, true,
|
findTest(rr_child_glue_->getName(), RRType::A(), ZoneFinder::SUCCESS, true,
|
||||||
rr_child_glue_, ZoneFinder::RESULT_DEFAULT, NULL,
|
rr_child_glue_, ZoneFinder::RESULT_DEFAULT, NULL,
|
||||||
@@ -619,6 +671,101 @@ TEST_F(InMemoryZoneFinderTest, glue) {
|
|||||||
NULL, ZoneFinder::FIND_GLUE_OK);
|
NULL, ZoneFinder::FIND_GLUE_OK);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(InMemoryZoneFinderTest, findAtOrigin) {
|
||||||
|
// Add origin NS.
|
||||||
|
rr_ns_->addRRsig(createRdata(RRType::RRSIG(), RRClass::IN(),
|
||||||
|
"NS 5 3 3600 20120814220826 20120715220826 "
|
||||||
|
"1234 example.org. FAKE"));
|
||||||
|
addToZoneData(rr_ns_);
|
||||||
|
|
||||||
|
// Specified type of RR exists, no DNSSEC
|
||||||
|
findAtOriginTest(RRType::NS(), ZoneFinder::SUCCESS, true, rr_ns_);
|
||||||
|
|
||||||
|
// Specified type of RR exists, with DNSSEC
|
||||||
|
findAtOriginTest(RRType::NS(), ZoneFinder::SUCCESS, true, rr_ns_,
|
||||||
|
ZoneFinder::RESULT_DEFAULT, NULL,
|
||||||
|
ZoneFinder::FIND_DNSSEC);
|
||||||
|
|
||||||
|
// Specified type of RR doesn't exist, no DNSSEC
|
||||||
|
findAtOriginTest(RRType::TXT(), ZoneFinder::NXRRSET);
|
||||||
|
|
||||||
|
// Specified type of RR doesn't exist, with DNSSEC. First, make the
|
||||||
|
// zone "NSEC-signed", then check.
|
||||||
|
rr_nsec_->addRRsig(createRdata(RRType::RRSIG(), RRClass::IN(),
|
||||||
|
"NSEC 5 3 3600 20120814220826 "
|
||||||
|
"20120715220826 1234 example.org. FAKE"));
|
||||||
|
addToZoneData(rr_nsec_);
|
||||||
|
findAtOriginTest(RRType::TXT(), ZoneFinder::NXRRSET, true, rr_nsec_,
|
||||||
|
ZoneFinder::RESULT_NSEC_SIGNED, NULL,
|
||||||
|
ZoneFinder::FIND_DNSSEC);
|
||||||
|
|
||||||
|
// Specified type of RR doesn't exist, with DNSSEC, enabling NSEC3. First,
|
||||||
|
// make the zone "NSEC3-signed" (by just installing NSEC3PARAM; we don't
|
||||||
|
// need to add NSEC3s for the purpose of this test), then check.
|
||||||
|
addToZoneData(textToRRset("example.org. 300 IN NSEC3PARAM "
|
||||||
|
"1 0 12 aabbccdd"));
|
||||||
|
findAtOriginTest(RRType::TXT(), ZoneFinder::NXRRSET, true, ConstRRsetPtr(),
|
||||||
|
ZoneFinder::RESULT_NSEC3_SIGNED, NULL,
|
||||||
|
ZoneFinder::FIND_DNSSEC);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(InMemoryZoneFinderTest, findAtOriginWithMinTTL) {
|
||||||
|
// Install zone's SOA. This also sets internal zone data min TTL field.
|
||||||
|
addToZoneData(rr_soa_);
|
||||||
|
|
||||||
|
// Specify the use of min TTL, then the resulting TTL should be derived
|
||||||
|
// from the SOA MINTTL (which is smaller).
|
||||||
|
findAtOriginTest(RRType::SOA(), ZoneFinder::SUCCESS, true,
|
||||||
|
textToRRset("example.org. 100 IN SOA . . 0 0 0 0 100",
|
||||||
|
class_, origin_),
|
||||||
|
ZoneFinder::RESULT_DEFAULT, NULL,
|
||||||
|
ZoneFinder::FIND_DEFAULT, true);
|
||||||
|
|
||||||
|
// Add signed NS for the following test.
|
||||||
|
RRsetPtr ns_rrset(textToRRset("example.org. 300 IN NS ns.example.org."));
|
||||||
|
ns_rrset->addRRsig(createRdata(RRType::RRSIG(), RRClass::IN(),
|
||||||
|
"NS 5 3 3600 20120814220826 20120715220826 "
|
||||||
|
"1234 example.org. FAKE"));
|
||||||
|
addToZoneData(ns_rrset);
|
||||||
|
|
||||||
|
// If DNSSEC is requested, TTL of the RRSIG should also be the min.
|
||||||
|
ns_rrset->setTTL(RRTTL(100)); // reset TTL to the expected one
|
||||||
|
findAtOriginTest(RRType::NS(), ZoneFinder::SUCCESS, true, ns_rrset,
|
||||||
|
ZoneFinder::RESULT_DEFAULT, NULL,
|
||||||
|
ZoneFinder::FIND_DEFAULT, true);
|
||||||
|
|
||||||
|
// If we don't request the use of min TTL, the original TTL will be used.
|
||||||
|
findAtOriginTest(RRType::SOA(), ZoneFinder::SUCCESS, true, rr_soa_,
|
||||||
|
ZoneFinder::RESULT_DEFAULT, NULL,
|
||||||
|
ZoneFinder::FIND_DEFAULT, false);
|
||||||
|
|
||||||
|
// If the found RRset has a smaller TTL than SOA, the original TTL should
|
||||||
|
// win.
|
||||||
|
rr_a_->setTTL(RRTTL(10));
|
||||||
|
addToZoneData(rr_a_);
|
||||||
|
findAtOriginTest(RRType::A(), ZoneFinder::SUCCESS, true, rr_a_,
|
||||||
|
ZoneFinder::RESULT_DEFAULT, NULL,
|
||||||
|
ZoneFinder::FIND_DEFAULT, true);
|
||||||
|
|
||||||
|
// If no RRset is returned, use_minttl doesn't matter (it shouldn't cause
|
||||||
|
// disruption)
|
||||||
|
findAtOriginTest(RRType::TXT(), ZoneFinder::NXRRSET, true, ConstRRsetPtr(),
|
||||||
|
ZoneFinder::RESULT_DEFAULT, NULL,
|
||||||
|
ZoneFinder::FIND_DEFAULT, true);
|
||||||
|
|
||||||
|
// If it results in NXRRSET with NSEC, and if we specify the use of min
|
||||||
|
// TTL, the NSEC and RRSIG should have the min TTL (again, though, this
|
||||||
|
// use case is not really the intended one)
|
||||||
|
rr_nsec_->addRRsig(createRdata(RRType::RRSIG(), RRClass::IN(),
|
||||||
|
"NSEC 5 3 3600 20120814220826 "
|
||||||
|
"20120715220826 1234 example.org. FAKE"));
|
||||||
|
addToZoneData(rr_nsec_);
|
||||||
|
rr_nsec_->setTTL(RRTTL(100)); // reset it to the expected one
|
||||||
|
findAtOriginTest(RRType::TXT(), ZoneFinder::NXRRSET, true, rr_nsec_,
|
||||||
|
ZoneFinder::RESULT_NSEC_SIGNED, NULL,
|
||||||
|
ZoneFinder::FIND_DNSSEC, true);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief Test searching.
|
* \brief Test searching.
|
||||||
*
|
*
|
||||||
|
@@ -584,3 +584,16 @@ class Diff:
|
|||||||
if rr.get_name() == name:
|
if rr.get_name() == name:
|
||||||
new_rrsets.append(rr)
|
new_rrsets.append(rr)
|
||||||
return result, new_rrsets, flags
|
return result, new_rrsets, flags
|
||||||
|
|
||||||
|
def get_rrset_collection(self):
|
||||||
|
'''
|
||||||
|
This first applies all changes to the data source. Then it creates
|
||||||
|
and returns an RRsetCollection on top of the corresponding zone
|
||||||
|
updater. Notice it might be impossible to apply more changes after
|
||||||
|
that.
|
||||||
|
|
||||||
|
This must not be called after a commit, or it'd throw ValueError.
|
||||||
|
'''
|
||||||
|
# Apply itself will check it is not yet commited.
|
||||||
|
self.apply()
|
||||||
|
return self.__updater.get_rrset_collection()
|
||||||
|
@@ -16,7 +16,8 @@
|
|||||||
import isc.log
|
import isc.log
|
||||||
import unittest
|
import unittest
|
||||||
from isc.datasrc import ZoneFinder
|
from isc.datasrc import ZoneFinder
|
||||||
from isc.dns import Name, RRset, RRClass, RRType, RRTTL, Rdata
|
from isc.dns import Name, RRset, RRClass, RRType, RRTTL, Rdata, \
|
||||||
|
RRsetCollectionBase
|
||||||
from isc.xfrin.diff import Diff, NoSuchZone
|
from isc.xfrin.diff import Diff, NoSuchZone
|
||||||
|
|
||||||
class TestError(Exception):
|
class TestError(Exception):
|
||||||
@@ -1087,6 +1088,52 @@ class DiffTest(unittest.TestCase):
|
|||||||
self.__check_find_all_call(diff.find_all, self.__rrset3,
|
self.__check_find_all_call(diff.find_all, self.__rrset3,
|
||||||
rcode)
|
rcode)
|
||||||
|
|
||||||
|
class Collection(isc.dns.RRsetCollectionBase):
|
||||||
|
'''
|
||||||
|
Our own mock RRsetCollection. We only pass it through, but we
|
||||||
|
still define an (mostly empty) find method to satisfy the
|
||||||
|
expectations.
|
||||||
|
'''
|
||||||
|
def __init__(self):
|
||||||
|
'''
|
||||||
|
Empty init. The base class's __init__ can't be called,
|
||||||
|
so we need to provide our own to shadow it -- and make sure
|
||||||
|
not to call the super().__init__().
|
||||||
|
'''
|
||||||
|
pass
|
||||||
|
|
||||||
|
def find(self, name, rrclass, rrtype):
|
||||||
|
'''
|
||||||
|
Empty find method. Returns None to each query (pretends
|
||||||
|
the collection is empty. Present mostly for completeness.
|
||||||
|
'''
|
||||||
|
return None
|
||||||
|
|
||||||
|
def get_rrset_collection(self):
|
||||||
|
'''
|
||||||
|
Part of pretending to be the zone updater. This returns the rrset
|
||||||
|
collection (a mock one, unuseable) for the updater.
|
||||||
|
'''
|
||||||
|
return self.Collection()
|
||||||
|
|
||||||
|
def test_get_rrset_collection(self):
|
||||||
|
'''
|
||||||
|
Test the diff can return corresponding rrset collection. Test
|
||||||
|
it applies the data first.
|
||||||
|
'''
|
||||||
|
diff = Diff(self, Name('example.org'), single_update_mode=True)
|
||||||
|
diff.add_data(self.__rrset_soa)
|
||||||
|
collection = diff.get_rrset_collection()
|
||||||
|
# Check it is applied
|
||||||
|
self.assertEqual(1, len(self.__data_operations))
|
||||||
|
self.assertEqual('add', self.__data_operations[0][0])
|
||||||
|
# Check the returned one is actually RRsetCollection
|
||||||
|
self.assertTrue(isinstance(collection, self.Collection))
|
||||||
|
# The collection is just the mock from above, so this doesn't do much
|
||||||
|
# testing, but we check that the mock got through and didn't get hurt.
|
||||||
|
self.assertIsNone(collection.find(Name('example.org'), RRClass.IN(),
|
||||||
|
RRType.SOA()))
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
isc.log.init("bind10")
|
isc.log.init("bind10")
|
||||||
isc.log.resetUnitTestRootLogger()
|
isc.log.resetUnitTestRootLogger()
|
||||||
|
@@ -0,0 +1,48 @@
|
|||||||
|
{
|
||||||
|
"version": 2,
|
||||||
|
"Logging": {
|
||||||
|
"loggers": [ {
|
||||||
|
"debuglevel": 99,
|
||||||
|
"severity": "DEBUG",
|
||||||
|
"name": "*"
|
||||||
|
} ]
|
||||||
|
},
|
||||||
|
"Auth": {
|
||||||
|
"database_file": "data/example.org-nons.sqlite3",
|
||||||
|
"listen_on": [ {
|
||||||
|
"address": "::1",
|
||||||
|
"port": 47807
|
||||||
|
} ]
|
||||||
|
},
|
||||||
|
"data_sources": {
|
||||||
|
"classes": {
|
||||||
|
"IN": [{
|
||||||
|
"type": "sqlite3",
|
||||||
|
"params": {
|
||||||
|
"database_file": "data/example.org-nons.sqlite3"
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Xfrout": {
|
||||||
|
"zone_config": [ {
|
||||||
|
"origin": "example.org"
|
||||||
|
} ],
|
||||||
|
"also_notify": [ {
|
||||||
|
"address": "::1",
|
||||||
|
"port": 47806
|
||||||
|
} ]
|
||||||
|
},
|
||||||
|
"Stats": {
|
||||||
|
"poll-interval": 1
|
||||||
|
},
|
||||||
|
"Boss": {
|
||||||
|
"components": {
|
||||||
|
"b10-auth": { "kind": "needed", "special": "auth" },
|
||||||
|
"b10-xfrout": { "address": "Xfrout", "kind": "dispensable" },
|
||||||
|
"b10-zonemgr": { "address": "Zonemgr", "kind": "dispensable" },
|
||||||
|
"b10-stats": { "address": "Stats", "kind": "dispensable" },
|
||||||
|
"b10-cmdctl": { "special": "cmdctl", "kind": "needed" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
BIN
tests/lettuce/data/example.org-nons.sqlite3
Normal file
BIN
tests/lettuce/data/example.org-nons.sqlite3
Normal file
Binary file not shown.
@@ -30,7 +30,7 @@ def stop_a_named_process(step, process_name):
|
|||||||
"""
|
"""
|
||||||
world.processes.stop_process(process_name)
|
world.processes.stop_process(process_name)
|
||||||
|
|
||||||
@step('wait (?:(\d+) times )?for (new )?(\w+) stderr message (.+)(?: not (.+))?')
|
@step('wait (?:(\d+) times )?for (new )?(\w+) stderr message (\S+)(?: not (\S+))?')
|
||||||
def wait_for_stderr_message(step, times, new, process_name, message, not_message):
|
def wait_for_stderr_message(step, times, new, process_name, message, not_message):
|
||||||
"""
|
"""
|
||||||
Block until the given message is printed to the given process's stderr
|
Block until the given message is printed to the given process's stderr
|
||||||
|
@@ -64,6 +64,8 @@ copylist = [
|
|||||||
"configurations/ddns/noddns.config"],
|
"configurations/ddns/noddns.config"],
|
||||||
["configurations/xfrin/retransfer_master.conf.orig",
|
["configurations/xfrin/retransfer_master.conf.orig",
|
||||||
"configurations/xfrin/retransfer_master.conf"],
|
"configurations/xfrin/retransfer_master.conf"],
|
||||||
|
["configurations/xfrin/retransfer_master_nons.conf.orig",
|
||||||
|
"configurations/xfrin/retransfer_master_nons.conf"],
|
||||||
["configurations/xfrin/retransfer_slave.conf.orig",
|
["configurations/xfrin/retransfer_slave.conf.orig",
|
||||||
"configurations/xfrin/retransfer_slave.conf"],
|
"configurations/xfrin/retransfer_slave.conf"],
|
||||||
["data/inmem-xfrin.sqlite3.orig",
|
["data/inmem-xfrin.sqlite3.orig",
|
||||||
@@ -84,7 +86,7 @@ removelist = [
|
|||||||
# If we have waited OUTPUT_WAIT_MAX_INTERVALS times, we will abort with an
|
# If we have waited OUTPUT_WAIT_MAX_INTERVALS times, we will abort with an
|
||||||
# error (so as not to hang indefinitely)
|
# error (so as not to hang indefinitely)
|
||||||
OUTPUT_WAIT_INTERVAL = 0.5
|
OUTPUT_WAIT_INTERVAL = 0.5
|
||||||
OUTPUT_WAIT_MAX_INTERVALS = 20
|
OUTPUT_WAIT_MAX_INTERVALS = 120
|
||||||
|
|
||||||
# class that keeps track of one running process and the files
|
# class that keeps track of one running process and the files
|
||||||
# we created for it.
|
# we created for it.
|
||||||
|
@@ -25,6 +25,13 @@ Feature: Xfrin
|
|||||||
|
|
||||||
A query for www.example.org to [::1]:47806 should have rcode REFUSED
|
A query for www.example.org to [::1]:47806 should have rcode REFUSED
|
||||||
When I send bind10 the command Xfrin retransfer example.org IN ::1 47807
|
When I send bind10 the command Xfrin retransfer example.org IN ::1 47807
|
||||||
|
# The data we receive contain a NS RRset that refers to three names in the
|
||||||
|
# example.org. zone. All these three are nonexistent in the data, producing
|
||||||
|
# 3 separate warning messages in the log.
|
||||||
|
And wait for new bind10 stderr message XFRIN_ZONE_WARN
|
||||||
|
And wait for new bind10 stderr message XFRIN_ZONE_WARN
|
||||||
|
And wait for new bind10 stderr message XFRIN_ZONE_WARN
|
||||||
|
# But after complaining, the zone data should be accepted.
|
||||||
Then wait for new bind10 stderr message XFRIN_TRANSFER_SUCCESS not XFRIN_XFR_PROCESS_FAILURE
|
Then wait for new bind10 stderr message XFRIN_TRANSFER_SUCCESS not XFRIN_XFR_PROCESS_FAILURE
|
||||||
Then wait for new bind10 stderr message ZONEMGR_RECEIVE_XFRIN_SUCCESS
|
Then wait for new bind10 stderr message ZONEMGR_RECEIVE_XFRIN_SUCCESS
|
||||||
A query for www.example.org to [::1]:47806 should have rcode NOERROR
|
A query for www.example.org to [::1]:47806 should have rcode NOERROR
|
||||||
@@ -38,7 +45,20 @@ Feature: Xfrin
|
|||||||
When I do an AXFR transfer of example.org
|
When I do an AXFR transfer of example.org
|
||||||
Then transfer result should have 13 rrs
|
Then transfer result should have 13 rrs
|
||||||
|
|
||||||
|
# Now try to offer another update. However, the validation of
|
||||||
|
# data should fail. The old version shoud still be available.
|
||||||
|
When I send bind10 the following commands with cmdctl port 47804:
|
||||||
|
"""
|
||||||
|
config set data_sources/classes/IN[0]/params/database_file data/example.org-nons.sqlite3
|
||||||
|
config set Auth/database_file data/example.org-nons.sqlite3
|
||||||
|
config commit
|
||||||
|
"""
|
||||||
|
Then I send bind10 the command Xfrin retransfer example.org IN ::1 47807
|
||||||
|
And wait for new bind10 stderr message XFRIN_ZONE_INVALID
|
||||||
|
And wait for new bind10 stderr message XFRIN_INVALID_ZONE_DATA
|
||||||
|
Then wait for new bind10 stderr message ZONEMGR_RECEIVE_XFRIN_FAILED
|
||||||
|
A query for example.org type NS to [::1]:47806 should have rcode NOERROR
|
||||||
|
And transfer result should have 13 rrs
|
||||||
|
|
||||||
Scenario: Transfer with TSIG
|
Scenario: Transfer with TSIG
|
||||||
# Similar setup to the test above, but this time, we add TSIG configuration
|
# Similar setup to the test above, but this time, we add TSIG configuration
|
||||||
@@ -74,7 +94,7 @@ Feature: Xfrin
|
|||||||
|
|
||||||
# Transfer should fail
|
# Transfer should fail
|
||||||
When I send bind10 the command Xfrin retransfer example.org
|
When I send bind10 the command Xfrin retransfer example.org
|
||||||
Then wait for new bind10 stderr message XFRIN_XFR_TRANSFER_PROTOCOL_ERROR not XFRIN_TRANSFER_SUCCESS
|
Then wait for new bind10 stderr message XFRIN_XFR_TRANSFER_PROTOCOL_VIOLATION not XFRIN_TRANSFER_SUCCESS
|
||||||
# Set client to use TSIG as well
|
# Set client to use TSIG as well
|
||||||
When I send bind10 the following commands:
|
When I send bind10 the following commands:
|
||||||
"""
|
"""
|
||||||
@@ -86,3 +106,41 @@ Feature: Xfrin
|
|||||||
# Transwer should succeed now
|
# Transwer should succeed now
|
||||||
When I send bind10 the command Xfrin retransfer example.org
|
When I send bind10 the command Xfrin retransfer example.org
|
||||||
Then wait for new bind10 stderr message XFRIN_TRANSFER_SUCCESS not XFRIN_XFR_PROCESS_FAILURE
|
Then wait for new bind10 stderr message XFRIN_TRANSFER_SUCCESS not XFRIN_XFR_PROCESS_FAILURE
|
||||||
|
|
||||||
|
Scenario: Validation fails
|
||||||
|
# In this test, the source data of the XFR is invalid (missing NS record
|
||||||
|
# at the origin). We check it is rejected after the transfer.
|
||||||
|
#
|
||||||
|
# We use abuse the fact that we do not check data when we read it from
|
||||||
|
# the sqlite3 database (unless we load into in-memory, which we don't
|
||||||
|
# do here).
|
||||||
|
The file data/test_nonexistent_db.sqlite3 should not exist
|
||||||
|
|
||||||
|
Given I have bind10 running with configuration xfrin/retransfer_master_nons.conf with cmdctl port 47804 as master
|
||||||
|
And wait for master stderr message BIND10_STARTED_CC
|
||||||
|
And wait for master stderr message CMDCTL_STARTED
|
||||||
|
And wait for master stderr message AUTH_SERVER_STARTED
|
||||||
|
And wait for master stderr message XFROUT_STARTED
|
||||||
|
And wait for master stderr message ZONEMGR_STARTED
|
||||||
|
|
||||||
|
And I have bind10 running with configuration xfrin/retransfer_slave.conf
|
||||||
|
And wait for bind10 stderr message BIND10_STARTED_CC
|
||||||
|
And wait for bind10 stderr message CMDCTL_STARTED
|
||||||
|
And wait for bind10 stderr message AUTH_SERVER_STARTED
|
||||||
|
And wait for bind10 stderr message XFRIN_STARTED
|
||||||
|
And wait for bind10 stderr message ZONEMGR_STARTED
|
||||||
|
|
||||||
|
# Now we use the first step again to see if the file has been created
|
||||||
|
The file data/test_nonexistent_db.sqlite3 should exist
|
||||||
|
|
||||||
|
A query for www.example.org to [::1]:47806 should have rcode REFUSED
|
||||||
|
When I send bind10 the command Xfrin retransfer example.org IN ::1 47807
|
||||||
|
# It should complain once about invalid data, then again that the whole
|
||||||
|
# zone is invalid and then reject it.
|
||||||
|
And wait for new bind10 stderr message XFRIN_ZONE_INVALID
|
||||||
|
And wait for new bind10 stderr message XFRIN_INVALID_ZONE_DATA
|
||||||
|
Then wait for new bind10 stderr message ZONEMGR_RECEIVE_XFRIN_FAILED
|
||||||
|
# The zone still doesn't exist as it is rejected.
|
||||||
|
# FIXME: This step fails. Probably an empty zone is created in the data
|
||||||
|
# source :-|. This should be REFUSED, not SERVFAIL.
|
||||||
|
A query for www.example.org to [::1]:47806 should have rcode SERVFAIL
|
||||||
|
@@ -123,7 +123,7 @@ Feature: Xfrin incoming notify handling
|
|||||||
Then wait for new bind10 stderr message AUTH_RECEIVED_NOTIFY
|
Then wait for new bind10 stderr message AUTH_RECEIVED_NOTIFY
|
||||||
Then wait for new bind10 stderr message ZONEMGR_RECEIVE_NOTIFY
|
Then wait for new bind10 stderr message ZONEMGR_RECEIVE_NOTIFY
|
||||||
Then wait for new bind10 stderr message XFRIN_XFR_TRANSFER_STARTED
|
Then wait for new bind10 stderr message XFRIN_XFR_TRANSFER_STARTED
|
||||||
Then wait for new bind10 stderr message XFRIN_XFR_TRANSFER_PROTOCOL_ERROR not XFRIN_XFR_TRANSFER_STARTED
|
Then wait for new bind10 stderr message XFRIN_XFR_TRANSFER_PROTOCOL_VIOLATION not XFRIN_XFR_TRANSFER_STARTED
|
||||||
Then wait for new bind10 stderr message ZONEMGR_RECEIVE_XFRIN_FAILED not ZONEMGR_RECEIVE_XFRIN_SUCCESS
|
Then wait for new bind10 stderr message ZONEMGR_RECEIVE_XFRIN_FAILED not ZONEMGR_RECEIVE_XFRIN_SUCCESS
|
||||||
Then wait 5 times for new master stderr message NOTIFY_OUT_SENDING_NOTIFY
|
Then wait 5 times for new master stderr message NOTIFY_OUT_SENDING_NOTIFY
|
||||||
Then wait for new master stderr message NOTIFY_OUT_RETRY_EXCEEDED
|
Then wait for new master stderr message NOTIFY_OUT_RETRY_EXCEEDED
|
||||||
|
Reference in New Issue
Block a user