Commit 2a81a3ad authored by Michal 'vorner' Vaner's avatar Michal 'vorner' Vaner
Browse files

Merge branch 'master' into work/pylog

parents c55ce183 9c2b48c4
249. [func] jerry
256. [bug] jerry
src/bin/xfrin: update xfrin to check TSIG before other part of
incoming message.
(Trac955, git 261450e93af0b0406178e9ef121f81e721e0855c)
255. [func] zhang likun
src/lib/cache: remove empty code in lib/cache and the corresponding
suppression rule in src/cppcheck-suppress.lst.
(Trac639, git 4f714bac4547d0a025afd314c309ca5cb603e212)
254. [bug] jinmei
b10-xfrout: failed to send notifies over IPv6 correctly.
(Trac964, git 3255c92714737bb461fb67012376788530f16e40)
253. [func] jelte
Add configuration options for logging through the virtual module
Logging.
(Trac 736, git 9fa2a95177265905408c51d13c96e752b14a0824)
252. [func] stephen
Add syslog as destination for logging.
(Trac976, git 31a30f5485859fd3df2839fc309d836e3206546e)
251. [bug]* jinmei
Make sure bindctl private files are non readable to anyone except
the owner or users in the same group. Note that if BIND 10 is run
with changing the user, this change means that the file owner or
group will have to be adjusted. Also note that this change is
only effective for a fresh install; if these files already exist,
their permissions must be adjusted by hand (if necessary).
(Trac870, git 461fc3cb6ebabc9f3fa5213749956467a14ebfd4)
250. [bug] ocean
src/lib/util/encode, in some conditions, the DecodeNormalizer's
iterator may reach the end() and when later being dereferenced
it will cause crash on some platform.
(Trac838, git 83e33ec80c0c6485d8b116b13045b3488071770f)
249. [func] jerry
xfrout: add support for TSIG verification.
(Trac816, git 3b2040e2af2f8139c1c319a2cbc429035d93f217)
248. [func] stephen
248. [func] stephen
Add file and stderr as destinations for logging.
(Trac555, git 38b3546867425bd64dbc5920111a843a3330646b)
247. [func] jelte
247. [func] jelte
Upstream queries from the resolver now set EDNS0 buffer size.
(Trac834, git 48e10c2530fe52c9bde6197db07674a851aa0f5d)
246. [func] stephen
246. [func] stephen
Implement logging using log4cplus (http://log4cplus.sourceforge.net)
(Trac899, git 31d3f525dc01638aecae460cb4bc2040c9e4df10)
245. [func] vorner
245. [func] vorner
Authoritative server can now sign the answers using TSIG
(configured in tsig_keys/keys, list of strings like
"name:<base64-secret>:sha1-hmac"). It doesn't use them for
......
......@@ -827,6 +827,8 @@ AC_CONFIG_FILES([Makefile
src/lib/util/unittests/Makefile
src/lib/util/pyunittests/Makefile
src/lib/util/tests/Makefile
src/lib/acl/Makefile
src/lib/acl/tests/Makefile
tests/Makefile
tests/system/Makefile
tests/tools/Makefile
......@@ -891,6 +893,7 @@ AC_OUTPUT([doc/version.ent
src/lib/cc/session_config.h.pre
src/lib/cc/tests/session_unittests_config.h
src/lib/log/tests/console_test.sh
src/lib/log/tests/destination_test.sh
src/lib/log/tests/local_file_test.sh
src/lib/log/tests/severity_test.sh
src/lib/log/tests/tempdir.h
......@@ -922,6 +925,7 @@ AC_OUTPUT([doc/version.ent
chmod +x src/lib/dns/tests/testdata/gen-wiredata.py
chmod +x src/lib/log/tests/local_file_test.sh
chmod +x src/lib/log/tests/console_test.sh
chmod +x src/lib/log/tests/destination_test.sh
chmod +x src/lib/log/tests/severity_test.sh
chmod +x src/lib/util/python/mkpywrapper.py
chmod +x src/lib/python/isc/log/tests/log_console.py
......
......@@ -573,8 +573,8 @@ INPUT = ../src/lib/cc ../src/lib/config \
../src/bin/auth ../src/bin/resolver ../src/lib/bench \
../src/lib/log ../src/lib/asiolink/ ../src/lib/nsas \
../src/lib/testutils ../src/lib/cache ../src/lib/server_common/ \
../src/bin/sockcreator/ ../src/lib/util/
../src/lib/resolve
../src/bin/sockcreator/ ../src/lib/util/ \
../src/lib/resolve ../src/lib/acl
# This tag can be used to specify the character encoding of the source files
# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is
......
......@@ -13,6 +13,7 @@
// PERFORMANCE OF THIS SOFTWARE.
#include <gtest/gtest.h>
#include <log/logger_support.h>
#include <util/unittests/run_all.h>
#include <dns/tests/unittest_util.h>
......@@ -22,6 +23,7 @@ main(int argc, char* argv[]) {
::testing::InitGoogleTest(&argc, argv);
isc::UnitTestUtil::addDataPath(TEST_DATA_DIR);
isc::UnitTestUtil::addDataPath(TEST_DATA_BUILDDIR);
isc::log::initLogger();
return (isc::util::unittests::run_all());
}
SUBDIRS = tests
EXTRA_DIST = README tsig_keys.py tsig_keys.spec
EXTRA_DIST += logging.spec b10logging.py
config_plugindir = @prefix@/share/@PACKAGE@/config_plugins
config_plugin_DATA = tsig_keys.py tsig_keys.spec
# Copyright (C) 2011 Internet Systems Consortium.
#
# Permission to use, copy, modify, and distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
# copyright notice and this permission notice appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SYSTEMS CONSORTIUM
# DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
# INTERNET SYSTEMS CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
# FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
# This is the configuration plugin for logging options
# The name is 'b10logging' because logging.py is an existing module
#
# For a technical background, see
# http://bind10.isc.org/wiki/LoggingCppApiDesign
#
from isc.config.module_spec import module_spec_from_file
from isc.util.file import path_search
from bind10_config import PLUGIN_PATHS
spec = module_spec_from_file(path_search('logging.spec', PLUGIN_PATHS))
ALLOWED_SEVERITIES = [ 'default',
'debug',
'info',
'warn',
'error',
'fatal',
'none' ]
ALLOWED_DESTINATIONS = [ 'console',
'file',
'syslog' ]
ALLOWED_STREAMS = [ 'stdout',
'stderr' ]
def check(config):
# Check the data layout first
errors=[]
if not spec.validate_config(False, config, errors):
return ' '.join(errors)
# The 'layout' is ok, now check for specific values
if 'loggers' in config:
for logger in config['loggers']:
# name should always be present
name = logger['name']
if 'severity' in logger and\
logger['severity'].lower() not in ALLOWED_SEVERITIES:
errors.append("bad severity value for logger " + name +
": " + logger['severity'])
if 'output_options' in logger:
for output_option in logger['output_options']:
if 'destination' in output_option:
destination = output_option['destination'].lower()
if destination not in ALLOWED_DESTINATIONS:
errors.append("bad destination for logger " +
name + ": " + output_option['destination'])
else:
# if left to default, output is stdout, and
# it will not show in the updated config,
# so 1. we only need to check it if present,
# and 2. if destination is changed, so should
# output. So first check checks 'in', and the
# others 'not in' for 'output'
if destination == "console" and\
'output' in output_option and\
output_option['output'] not in ALLOWED_STREAMS:
errors.append("bad output for logger " + name +
": " + output_option['stream'] +
", must be stdout or stderr")
elif destination == "file" and\
'output' not in output_option or\
output_option['output'] == "":
errors.append("destination set to file but "
"output not set to any "
"filename for logger "
+ name)
elif destination == "syslog" and\
'output' not in output_option or\
output_option['output'] == "":
errors.append("destination set to syslog but "
"output not set to any facility"
" for logger " + name)
if errors:
return ', '.join(errors)
return None
def load():
return (spec, check)
{
"module_spec": {
"module_name": "Logging",
"module_description": "Logging options",
"config_data": [
{
"item_name": "loggers",
"item_type": "list",
"item_optional": false,
"item_default": [],
"list_item_spec": {
"item_name": "logger",
"item_type": "map",
"item_optional": false,
"item_default": {},
"map_item_spec": [
{ "item_name": "name",
"item_type": "string",
"item_optional": false,
"item_default": ""
},
{ "item_name": "severity",
"item_type": "string",
"item_optional": false,
"item_default": "INFO"
},
{ "item_name": "debuglevel",
"item_type": "integer",
"item_optional": false,
"item_default": 0
},
{ "item_name": "additive",
"item_type": "boolean",
"item_optional": false,
"item_default": false
},
{ "item_name": "output_options",
"item_type": "list",
"item_optional": false,
"item_default": [],
"list_item_spec": {
"item_name": "output_option",
"item_type": "map",
"item_optional": false,
"item_default": {},
"map_item_spec": [
{ "item_name": "destination",
"item_type": "string",
"item_optional": false,
"item_default": "console"
},
{ "item_name": "output",
"item_type": "string",
"item_optional": false,
"item_default": "stdout"
},
{ "item_name": "flush",
"item_type": "boolean",
"item_optional": false,
"item_default": false
},
{ "item_name": "maxsize",
"item_type": "integer",
"item_optional": false,
"item_default": 0
},
{ "item_name": "maxver",
"item_type": "integer",
"item_optional": false,
"item_default": 0
}
]
}
}
]
}
}
],
"commands": []
}
}
......@@ -86,7 +86,7 @@ class TSigKeysTest(unittest.TestCase):
self.assertEqual("TSIG: Invalid TSIG key string: invalid.key",
tsig_keys.check({'keys': ['invalid.key']}))
self.assertEqual(
"TSIG: attempt to decode a value not in base64 char set",
"TSIG: Unexpected end of input in BASE decoder",
tsig_keys.check({'keys': ['invalid.key:123']}))
def test_bad_format(self):
......
......@@ -40,12 +40,13 @@ b10-cmdctl: cmdctl.py
if INSTALL_CONFIGURATIONS
# TODO: permissions handled later
# Below we intentionally use ${INSTALL} -m 640 instead of $(INSTALL_DATA)
# because these file will contain sensitive information.
install-data-local:
$(mkinstalldirs) $(DESTDIR)/@sysconfdir@/@PACKAGE@
for f in $(CMDCTL_CONFIGURATIONS) ; do \
if test ! -f $(DESTDIR)$(sysconfdir)/@PACKAGE@/$$f; then \
$(INSTALL_DATA) $(srcdir)/$$f $(DESTDIR)$(sysconfdir)/@PACKAGE@/ ; \
${INSTALL} -m 640 $(srcdir)/$$f $(DESTDIR)$(sysconfdir)/@PACKAGE@/ ; \
fi ; \
done
......
......@@ -208,7 +208,8 @@ main(int argc, char* argv[]) {
cc_session = new Session(io_service.get_io_service());
config_session = new ModuleCCSession(specfile, *cc_session,
my_config_handler,
my_command_handler);
my_command_handler,
true, true);
LOG_DEBUG(resolver_logger, RESOLVER_DBG_INIT, RESOLVER_CONFIGCHAN);
// FIXME: This does not belong here, but inside Boss
......
......@@ -34,9 +34,7 @@ run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
run_unittests_LDADD = $(GTEST_LDADD)
run_unittests_LDADD += $(SQLITE_LIBS)
run_unittests_LDADD += $(top_builddir)/src/lib/testutils/libtestutils.la
run_unittests_LDADD += $(top_builddir)/src/lib/datasrc/libdatasrc.la
run_unittests_LDADD += $(top_builddir)/src/lib/dns/libdns++.la
run_unittests_LDADD += $(top_builddir)/src/lib/asiodns/libasiodns.la
run_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libasiolink.la
......
......@@ -13,6 +13,7 @@
// PERFORMANCE OF THIS SOFTWARE.
#include <gtest/gtest.h>
#include <log/logger_support.h>
#include <util/unittests/run_all.h>
#include <dns/tests/unittest_util.h>
......@@ -22,6 +23,7 @@ main(int argc, char* argv[]) {
::testing::InitGoogleTest(&argc, argv);
isc::UnitTestUtil::addDataPath(TEST_DATA_DIR);
isc::UnitTestUtil::addDataPath(TEST_DATA_BUILDDIR);
isc::log::initLogger();
return (isc::util::unittests::run_all());
}
......@@ -15,6 +15,7 @@
import unittest
import socket
import io
from isc.testutils.tsigctx_mock import MockTSIGContext
from xfrin import *
......@@ -78,7 +79,7 @@ class MockXfrin(Xfrin):
def _get_db_file(self):
pass
def _cc_check_command(self):
self._shutdown_event.set()
if MockXfrin.check_command_hook:
......@@ -207,6 +208,18 @@ class TestXfrinConnection(unittest.TestCase):
mock_ctx.error = error
return mock_ctx
def __match_exception(self, expected_exception, expected_msg, expression):
# This helper method is a higher-granularity version of assertRaises().
# If it's not sufficient to check the exception class (e.g., when
# the same type of exceptions can be thrown from many places), this
# method can be used to check it with the exception argument.
try:
expression()
except expected_exception as ex:
self.assertEqual(str(ex), expected_msg)
else:
self.assertFalse('exception is expected, but not raised')
def test_close(self):
# we shouldn't be using the global asyncore map.
self.assertEqual(len(asyncore.socket_map), 0)
......@@ -293,6 +306,31 @@ class TestXfrinConnection(unittest.TestCase):
self.conn.reply_data = self.conn.create_response_data(bad_qid = True)
self.assertRaises(XfrinException, self._handle_xfrin_response)
def test_response_error_code_bad_sig(self):
self.conn._tsig_key = TSIG_KEY
self.conn._tsig_ctx_creator = \
lambda key: self.__create_mock_tsig(key, TSIGError.BAD_SIG)
self.conn._send_query(RRType.AXFR())
self.conn.reply_data = self.conn.create_response_data(
rcode=Rcode.SERVFAIL())
# xfrin should check TSIG before other part of incoming message
# validate log message for XfrinException
self.__match_exception(XfrinException,
"TSIG verify fail: BADSIG",
self._handle_xfrin_response)
def test_response_bad_qid_bad_key(self):
self.conn._tsig_key = TSIG_KEY
self.conn._tsig_ctx_creator = \
lambda key: self.__create_mock_tsig(key, TSIGError.BAD_KEY)
self.conn._send_query(RRType.AXFR())
self.conn.reply_data = self.conn.create_response_data(bad_qid=True)
# xfrin should check TSIG before other part of incoming message
# validate log message for XfrinException
self.__match_exception(XfrinException,
"TSIG verify fail: BADKEY",
self._handle_xfrin_response)
def test_response_non_response(self):
self.conn._send_query(RRType.AXFR())
self.conn.reply_data = self.conn.create_response_data(response = False)
......@@ -337,6 +375,18 @@ class TestXfrinConnection(unittest.TestCase):
self.conn.response_generator = self._create_soa_response_data
self.assertRaises(XfrinException, self.conn._check_soa_serial)
def test_soacheck_bad_qid_bad_sig(self):
self.conn._tsig_key = TSIG_KEY
self.conn._tsig_ctx_creator = \
lambda key: self.__create_mock_tsig(key, TSIGError.BAD_SIG)
self.soa_response_params['bad_qid'] = True
self.conn.response_generator = self._create_soa_response_data
# xfrin should check TSIG before other part of incoming message
# validate log message for XfrinException
self.__match_exception(XfrinException,
"TSIG verify fail: BADSIG",
self.conn._check_soa_serial)
def test_soacheck_non_response(self):
self.soa_response_params['response'] = False
self.conn.response_generator = self._create_soa_response_data
......
......@@ -243,13 +243,13 @@ class XfrinConnection(asyncore.dispatcher):
msg = Message(Message.PARSE)
msg.from_wire(soa_response)
# TSIG related checks, including an unexpected signed response
self._check_response_tsig(msg, soa_response)
# perform some minimal level validation. It's an open issue how
# strict we should be (see the comment in _check_response_header())
self._check_response_header(msg)
# TSIG related checks, including an unexpected signed response
self._check_response_tsig(msg, soa_response)
# TODO, need select soa record from data source then compare the two
# serial, current just return OK, since this function hasn't been used
# now.
......@@ -311,7 +311,7 @@ class XfrinConnection(asyncore.dispatcher):
raise XfrinException('error response: %s' % msg_rcode.to_text())
if not msg.get_header_flag(Message.HEADERFLAG_QR):
raise XfrinException('response is not a response ')
raise XfrinException('response is not a response')
if msg.get_qid() != self._query_id:
raise XfrinException('bad query id')
......@@ -362,11 +362,13 @@ class XfrinConnection(asyncore.dispatcher):
recvdata = self._get_request_response(msg_len)
msg = Message(Message.PARSE)
msg.from_wire(recvdata)
self._check_response_status(msg)
# TSIG related checks, including an unexpected signed response
self._check_response_tsig(msg, recvdata)
# Perform response status validation
self._check_response_status(msg)
answer_section = msg.get_section(Message.SECTION_ANSWER)
for rr in self._handle_answer_section(answer_section):
yield rr
......
......@@ -4,11 +4,6 @@ debug
missingInclude
// This is a template, and should be excluded from the check
unreadVariable:src/lib/dns/rdata/template.cc:60
// These three trigger warnings due to the incomplete implementation. This is
// our problem, but we need to suppress the warnings for now.
functionConst:src/lib/cache/resolver_cache.h
functionConst:src/lib/cache/message_cache.h
functionConst:src/lib/cache/rrset_cache.h
// Intentional self assignment tests. Suppress warning about them.
selfAssignment:src/lib/dns/tests/name_unittest.cc:293
selfAssignment:src/lib/dns/tests/rdata_unittest.cc:228
......
SUBDIRS = exceptions util log cryptolink dns cc config python xfr \
bench asiolink asiodns nsas cache resolve testutils datasrc \
server_common
server_common acl
SUBDIRS = tests
# TODO: Once we have some cc file we are able to compile, create the library.
# For now, we have only header files, not creating empty library.
// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
#ifndef ACL_CHECK_H
#define ACL_CHECK_H
#include <vector>
#include <typeinfo>
#include <sstream>
namespace isc {
namespace acl {
/**
* \brief ACL check base class.
*
* It is intended that all ACL checks are inherited (maybe indirectly) from
* this base class. This will allow us to define new types of checks without
* changing any of the code that is using it and with the correct
* implementation even without changing the thing that parses configuration
* and creates instances of the checks.
*
* It is implemented as a template. This allows easy reuse of the code for
* checking of different types of things (packets of different protocols, etc).
* We'll implement the loader and compound checks as templates as well (
* and just make sure they are instantiated for each type of thing we want
* to check). While most of concrete checks will be specific for one protocol
* (or whatever the entity we check is), it makes sense to implement some of
* these as templates as well (for example the IP address check, for whatever
* context that contains member called ip and has the right methods).
*
* The Context carries whatever information might be checked for that protocol
* (eg. the packet, information where it came from, to what port, ...).
*/
template<typename Context> class Check {
protected:
/// \brief Constructor.
///
/// Just to make sure this thing is not directly instantiated.
Check() { }
public:
/**
* \brief The check itself.
*
* The actual check will be performed here. Every concrete child class
* will reimplement it and decide based on the context passed if it
* matches.
*
* The caller should expect this method can throw. The list of exceptions
* isn't restricted, as we don't know what kind of checks will be needed.
* An exception should be considered as it is impossible to check the
* condition. It should lead to either blackholing the packet or returning
* some 500-like error (ServFail).
*
* \param context The thing we are trying to match against this check.
* \return true if the context satisfies the check, false otherwise.
*/
virtual bool matches(const Context& context) const = 0;
/**
* \brief Cost for unknown cost estimate.
*
* This indicates that the estimate for cost is not provided. This
* is arbitrary large value, meaning "somehow longish time". To be
* on the safe side, we guess more and be just happily suprirised
* if it turns out to run faster.
*/
static const unsigned UNKNOWN_COST;
/**
* \brief The expected cost of single match.
*
* This is here to provide some kind of cost information to optimising
* routines. It is in units without any real size, just bigger number
* means the check takes longer time. It is expected to be linear scale.
* It doesn't need to be exact, but better accuracy might lead to better
* optimisations. As of writing this, no optimisations exist yet, but
* are expected to exist in future.
*
* The default is UNKNOWN_COST.
*/
virtual unsigned cost() const {
return (UNKNOWN_COST);
}
/// \brief Virtual destructor, as we're virtual
virtual ~ Check() { }
/**
* \brief Conversion to text.
*
* This is meant for debugging purposes, it doesn't have to
* serialise the whole information stored in this Check.
*
* If the check is compound, it should not include the subexpressions
* (while we're able to build whatever treeish representation using
* CompoundCheck::subexpressions, we're not able to separate them
* automatically, as this may produce any kind of free-form string).