BIND 10 trac929, updated. 2bbe9d72f0f2a8652873a2600a510cc23cc3990e [trac929] add unittest of "get_statistics_spec"
BIND 10 source code commits
bind10-changes at lists.isc.org
Fri Jul 15 04:31:59 UTC 2011
The branch, trac929 has been updated
discards 5986abec35906a9f600826e1efbc0a029bc45e41 (commit)
discards 246ab4e56025059a9754cfeb34035d5df0a9ac10 (commit)
discards c47a61ae51bc0958280210aef375906fc9f15e09 (commit)
discards 0554c222f958a300d0ab7e254a1c46a0b5fa6b80 (commit)
discards af3bcf68cf8ca9be04e2047cf5f7e4439e1940e2 (commit)
discards 09885f38f8450ae7d0c350ca2c3764f448a88bec (commit)
discards 1a73ddb22b4821c7fcf2b54e49bde65ea0147d32 (commit)
discards 385d103dcd281bdc2a99681f9d4440cc92cda2dc (commit)
via 2bbe9d72f0f2a8652873a2600a510cc23cc3990e (commit)
via 4089a03170200a230f71555885adf6beb4121a72 (commit)
via ac74183b3cef60df0b9a83724ce4e0335305c86a (commit)
via 144588b693f243388fedb35636706c1ce7f26fb3 (commit)
via 610e2676a76e03d36d7fe8ea68b5afeffb79f45e (commit)
via bf2bb0534e3b0914820469799d9d2491f0ba1694 (commit)
via 2af93ce75f5c24ce2cab601a4aa102dedad69626 (commit)
via a385d63aa6e6c32eaeb7253f9fa2a8ac76eaabdd (commit)
via a365c21da34b70f50459137ae242767cc336f191 (commit)
via d13509441ce77077ccf21b9442458b0fb52b1c07 (commit)
via 8e00f359e81c3cb03c5075710ead0f87f87e3220 (commit)
via f52ff519388e7f3ab4e903695b731a2a7000fcf5 (commit)
via f63ff922e713a04b3f4391d509c2206ac32edbb5 (commit)
via 72a0beb8dfe85b303f546d09986461886fe7a3d8 (commit)
via aa4405d57bec097972c4d5b60d1cfd6a06f84bf1 (commit)
via 4d17de950b96631d01c7928b9cab24860b2e29e5 (commit)
via dfd8332b1a958ed9aeb6ae423ea937b5e08024f8 (commit)
via 54c3708f45c72065cefd4d6013be5467bee65f85 (commit)
via 146c48357b32d26019675834eda1daddde95302c (commit)
via 62f912bd96a5fefeb0eb8b017ff12335810483b0 (commit)
via a7047de1ec7aece83271cc28605ea1d790afee67 (commit)
via 71eee6e279d7527adbc1e325b0cca49d824b67ee (commit)
via 0958095d36903cd821afc57be0c038896dd1acdb (commit)
via 8e66cc336b29bd5acc1f764f26cb0b116db4dc87 (commit)
via e540aaf2cedae6cfeb4c0ea063f8693cf5999822 (commit)
via aac974498b0a9513f3caf341e1eecbe4adbcff0a (commit)
via e7cf8992bed2ef0be2843da6f0eedf9fa6d5f66b (commit)
via 963e72656e6a5d8303034f9085c87834a75c44ce (commit)
via 4d685db094731fccfa684f5c0b26ebfc1c28ca2c (commit)
via 829edd5488aa90324ddc4036dbaf4f2578be9e76 (commit)
via d81a47d3366b6c6ed14edff69188b60ed3655f28 (commit)
via a29b113e5b418921dffaf9b4cfc562ae887a7960 (commit)
via 5024b68a04ecc7ff1c73299fa986cac740cb3e8b (commit)
via 56b188c6e4e36a28b54cab442677e2fa748f0bae (commit)
via d7d60797272f02e6f3f09b659922c71f2c49ffec (commit)
via 570bbcef51aa6a5bc920faabd850cd6a86c0d421 (commit)
via e8e411dd27068279b58bc3527d1b60878ed19d0b (commit)
via 0d68ac445710fdb4d9d89ca2055b206c9a06dc94 (commit)
via b5e49faa3340628865ea28a60d3dc36d3e08511d (commit)
via 99c025349129904b864806049ea8761940ba0ecc (commit)
This update added new revisions after undoing existing revisions. That is
to say, the old revision is not a strict subset of the new revision. This
situation occurs when you --force push a change and generate a repository
containing something like this:
* -- * -- B -- O -- O -- O (5986abec35906a9f600826e1efbc0a029bc45e41)
\
N -- N -- N (2bbe9d72f0f2a8652873a2600a510cc23cc3990e)
When this happens we assume that you've already had alert emails for all
of the O revisions, and so we here report only the revisions in the N
branch from the common base, B.
Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.
- Log -----------------------------------------------------------------
commit 2bbe9d72f0f2a8652873a2600a510cc23cc3990e
Author: Naoki Kambe <kambe at jprs.co.jp>
Date: Fri Jul 8 15:47:09 2011 +0900
[trac929] add unittest of "get_statistics_spec"
commit 4089a03170200a230f71555885adf6beb4121a72
Author: Naoki Kambe <kambe at jprs.co.jp>
Date: Fri Jul 8 15:46:57 2011 +0900
[trac929] add unittests for the functions:
- validate_format
- check_format
- validate_format
commit ac74183b3cef60df0b9a83724ce4e0335305c86a
Author: Naoki Kambe <kambe at jprs.co.jp>
Date: Fri Jul 8 15:46:46 2011 +0900
[trac929] add "validate_statistics" which validates statistics specification in the spec file
It checks data types and data format of statistics specification
commit 144588b693f243388fedb35636706c1ce7f26fb3
Author: Naoki Kambe <kambe at jprs.co.jp>
Date: Fri Jul 8 15:46:27 2011 +0900
[trac929] add "get_statistics_spec" into cfgmgr.py
it pushes contents in statistics category of each spec file.
commit 610e2676a76e03d36d7fe8ea68b5afeffb79f45e
Author: Naoki Kambe <kambe at jprs.co.jp>
Date: Fri Jul 8 15:45:28 2011 +0900
[trac929] add COMMAND_GET_STATISTICS_SPEC for "get_statistics_spec"
commit bf2bb0534e3b0914820469799d9d2491f0ba1694
Author: Naoki Kambe <kambe at jprs.co.jp>
Date: Fri Jul 8 15:45:15 2011 +0900
[trac929] add a statistics category into "spec2.spec"
and modify message string to be compared with in EXPECT_EQ
commit 2af93ce75f5c24ce2cab601a4aa102dedad69626
Author: Naoki Kambe <kambe at jprs.co.jp>
Date: Fri Jul 8 15:44:40 2011 +0900
[trac929] add some spec files for unittest of statistics category
commit a385d63aa6e6c32eaeb7253f9fa2a8ac76eaabdd
Author: Naoki Kambe <kambe at jprs.co.jp>
Date: Fri Jul 8 21:00:23 2011 +0900
[trac928] add statistics category in each spec file and add statistics items in
some spec files (bob.spec, auth.spec, stats.spec)
-----------------------------------------------------------------------
Summary of changes:
ChangeLog | 13 ++
src/bin/bind10/tests/bind10_test.py.in | 2 +
src/bin/cmdctl/tests/cmdctl_test.py | 2 +
src/bin/xfrin/tests/xfrin_test.py | 2 +
src/bin/xfrout/tests/xfrout_test.py.in | 2 +
src/lib/asiodns/tests/run_unittests.cc | 4 +-
src/lib/datasrc/cache.cc | 3 +-
src/lib/datasrc/data_source.cc | 4 +-
src/lib/datasrc/datasrc_messages.mes | 126 ++++++-----
src/lib/datasrc/memory_datasrc.cc | 4 +-
src/lib/datasrc/sqlite3_datasrc.cc | 3 +-
src/lib/datasrc/static_datasrc.cc | 2 +-
src/lib/dns/message.cc | 49 ++++-
src/lib/dns/message.h | 11 +
src/lib/dns/python/message_python.cc | 9 +
src/lib/dns/python/tests/message_python_test.py | 121 +++++++++--
src/lib/dns/python/tests/question_python_test.py | 10 +-
src/lib/dns/question.cc | 9 +
src/lib/dns/question.h | 16 +-
src/lib/dns/tests/message_unittest.cc | 226 +++++++++++++++++++-
src/lib/dns/tests/question_unittest.cc | 16 ++
src/lib/dns/tests/testdata/Makefile.am | 6 +-
src/lib/dns/tests/testdata/gen-wiredata.py.in | 12 +-
...essage_toWire2.spec => message_fromWire17.spec} | 9 +-
src/lib/dns/tests/testdata/message_fromWire18.spec | 23 ++
src/lib/dns/tests/testdata/message_toWire4.spec | 27 +++
src/lib/dns/tests/testdata/message_toWire5.spec | 36 +++
src/lib/dns/tests/tsig_unittest.cc | 72 ++++++
src/lib/dns/tsig.cc | 103 ++++++++--
src/lib/dns/tsig.h | 21 ++
src/lib/log/Makefile.am | 1 +
src/lib/log/logger_support.cc | 150 +-------------
src/lib/log/logger_support.h | 62 +-----
src/lib/log/logger_unittest_support.cc | 175 +++++++++++++++
...{logger_support.h => logger_unittest_support.h} | 111 ++++++-----
src/lib/log/tests/init_logger_test.sh.in | 6 +-
src/lib/log/tests/logger_level_impl_unittest.cc | 7 +-
src/lib/log/tests/logger_level_unittest.cc | 8 +-
src/lib/log/tests/logger_support_unittest.cc | 15 ++-
src/lib/python/isc/log/log.cc | 30 +++-
40 files changed, 1109 insertions(+), 399 deletions(-)
copy src/lib/dns/tests/testdata/{message_toWire2.spec => message_fromWire17.spec} (70%)
create mode 100644 src/lib/dns/tests/testdata/message_fromWire18.spec
create mode 100644 src/lib/dns/tests/testdata/message_toWire4.spec
create mode 100644 src/lib/dns/tests/testdata/message_toWire5.spec
create mode 100644 src/lib/log/logger_unittest_support.cc
copy src/lib/log/{logger_support.h => logger_unittest_support.h} (52%)
-----------------------------------------------------------------------
diff --git a/ChangeLog b/ChangeLog
index 5d27f6e..fc9e8b4 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,14 @@
+272. [func] jinmei
+ libdns++/pydnspp: TSIG signing now handles truncated DNS messages
+ (i.e. with TC bit on) with TSIG correctly.
+ (Trac #910, 8e00f359e81c3cb03c5075710ead0f87f87e3220)
+
+271. [func] stephen
+ Default logging for unit tests changed to severity DEBUG (level 99)
+ with the output routed to /dev/null. This can be altered by setting
+ the B10_LOGGER_XXX environment variables.
+ (Trac #1024, git 72a0beb8dfe85b303f546d09986461886fe7a3d8)
+
270. [func] jinmei
Added python bindings for ACLs using the DNS request as the
context. They are accessible via the isc.acl.dns module.
@@ -13,6 +24,8 @@
unit tests.
(Trac #1071, git 05164f9d61006869233b498d248486b4307ea8b6)
+bind10-devel-20110705 released on July 05, 2011
+
267. [func] tomek
Added a dummy module for DHCP6. This module does not actually
do anything at this point, and BIND 10 has no option for
diff --git a/src/bin/bind10/tests/bind10_test.py.in b/src/bin/bind10/tests/bind10_test.py.in
index 91d326c..9d794a6 100644
--- a/src/bin/bind10/tests/bind10_test.py.in
+++ b/src/bin/bind10/tests/bind10_test.py.in
@@ -26,6 +26,7 @@ import socket
from isc.net.addr import IPAddr
import time
import isc
+import isc.log
from isc.testutils.parse_args import TestOptParser, OptsError
@@ -764,4 +765,5 @@ class TestBrittle(unittest.TestCase):
self.assertFalse(bob.runnable)
if __name__ == '__main__':
+ isc.log.resetUnitTestRootLogger()
unittest.main()
diff --git a/src/bin/cmdctl/tests/cmdctl_test.py b/src/bin/cmdctl/tests/cmdctl_test.py
index e77c529..3103f47 100644
--- a/src/bin/cmdctl/tests/cmdctl_test.py
+++ b/src/bin/cmdctl/tests/cmdctl_test.py
@@ -19,6 +19,7 @@ import socket
import tempfile
import sys
from cmdctl import *
+import isc.log
SPEC_FILE_PATH = '..' + os.sep
if 'CMDCTL_SPEC_PATH' in os.environ:
@@ -447,6 +448,7 @@ class TestFuncNotInClass(unittest.TestCase):
if __name__== "__main__":
+ isc.log.resetUnitTestRootLogger()
unittest.main()
diff --git a/src/bin/xfrin/tests/xfrin_test.py b/src/bin/xfrin/tests/xfrin_test.py
index 2acd9d6..92bf1b0 100644
--- a/src/bin/xfrin/tests/xfrin_test.py
+++ b/src/bin/xfrin/tests/xfrin_test.py
@@ -18,6 +18,7 @@ import socket
import io
from isc.testutils.tsigctx_mock import MockTSIGContext
from xfrin import *
+import isc.log
#
# Commonly used (mostly constant) test parameters
@@ -1115,6 +1116,7 @@ class TestMain(unittest.TestCase):
if __name__== "__main__":
try:
+ isc.log.resetUnitTestRootLogger()
unittest.main()
except KeyboardInterrupt as e:
print(e)
diff --git a/src/bin/xfrout/tests/xfrout_test.py.in b/src/bin/xfrout/tests/xfrout_test.py.in
index adabf48..7ab4a58 100644
--- a/src/bin/xfrout/tests/xfrout_test.py.in
+++ b/src/bin/xfrout/tests/xfrout_test.py.in
@@ -23,6 +23,7 @@ from isc.cc.session import *
from pydnspp import *
from xfrout import *
import xfrout
+import isc.log
TSIG_KEY = TSIGKey("example.com:SFuWd/q99SzF8Yzd1QbB9g==")
@@ -670,4 +671,5 @@ class TestInitialization(unittest.TestCase):
self.assertEqual(xfrout.UNIX_SOCKET_FILE, "The/Socket/File")
if __name__== "__main__":
+ isc.log.resetUnitTestRootLogger()
unittest.main()
diff --git a/src/lib/asiodns/tests/run_unittests.cc b/src/lib/asiodns/tests/run_unittests.cc
index df77368..5cacdaf 100644
--- a/src/lib/asiodns/tests/run_unittests.cc
+++ b/src/lib/asiodns/tests/run_unittests.cc
@@ -15,14 +15,14 @@
#include <gtest/gtest.h>
#include <util/unittests/run_all.h>
-#include <log/logger_manager.h>
+#include <log/logger_support.h>
#include <dns/tests/unittest_util.h>
int
main(int argc, char* argv[])
{
::testing::InitGoogleTest(&argc, argv); // Initialize Google test
- isc::log::LoggerManager::init("unittest"); // Set a root logger name
+ isc::log::initLogger(); // Initialize logging
isc::UnitTestUtil::addDataPath(TEST_DATA_DIR); // Add location of test data
return (isc::util::unittests::run_all());
diff --git a/src/lib/datasrc/cache.cc b/src/lib/datasrc/cache.cc
index 9082a6b..d88e649 100644
--- a/src/lib/datasrc/cache.cc
+++ b/src/lib/datasrc/cache.cc
@@ -232,7 +232,8 @@ HotCacheImpl::insert(const CacheNodePtr node) {
if (iter != map_.end()) {
CacheNodePtr old = iter->second;
if (old && old->isValid()) {
- LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_CACHE_OLD_FOUND);
+ LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_CACHE_OLD_FOUND)
+ .arg(node->getNodeName());
remove(old);
}
}
diff --git a/src/lib/datasrc/data_source.cc b/src/lib/datasrc/data_source.cc
index b57a967..94dec89 100644
--- a/src/lib/datasrc/data_source.cc
+++ b/src/lib/datasrc/data_source.cc
@@ -903,7 +903,7 @@ tryWildcard(Query& q, QueryTaskPtr task, ZoneInfo& zoneinfo, bool& found) {
result = proveNX(q, task, zoneinfo, true);
if (result != DataSrc::SUCCESS) {
m.setRcode(Rcode::SERVFAIL());
- logger.error(DATASRC_QUERY_WILDCARD_PROVENX_FAIL).
+ logger.error(DATASRC_QUERY_WILDCARD_PROVE_NX_FAIL).
arg(task->qname).arg(result);
return (DataSrc::ERROR);
}
@@ -1162,7 +1162,7 @@ DataSrc::doQuery(Query& q) {
result = proveNX(q, task, zoneinfo, false);
if (result != DataSrc::SUCCESS) {
m.setRcode(Rcode::SERVFAIL());
- logger.error(DATASRC_QUERY_PROVENX_FAIL).arg(task->qname);
+ logger.error(DATASRC_QUERY_PROVE_NX_FAIL).arg(task->qname);
return;
}
}
diff --git a/src/lib/datasrc/datasrc_messages.mes b/src/lib/datasrc/datasrc_messages.mes
index c692364..3dc69e0 100644
--- a/src/lib/datasrc/datasrc_messages.mes
+++ b/src/lib/datasrc/datasrc_messages.mes
@@ -17,63 +17,63 @@ $NAMESPACE isc::datasrc
# \brief Messages for the data source library
% DATASRC_CACHE_CREATE creating the hotspot cache
-Debug information that the hotspot cache was created at startup.
+This is a debug message issued during startup when the hotspot cache
+is created.
% DATASRC_CACHE_DESTROY destroying the hotspot cache
Debug information. The hotspot cache is being destroyed.
-% DATASRC_CACHE_DISABLE disabling the cache
-The hotspot cache is disabled from now on. It is not going to store
-information or return anything.
+% DATASRC_CACHE_DISABLE disabling the hotspot cache
+A debug message issued when the hotspot cache is disabled.
-% DATASRC_CACHE_ENABLE enabling the cache
-The hotspot cache is enabled from now on.
+% DATASRC_CACHE_ENABLE enabling the hotspot cache
+A debug message issued when the hotspot cache is enabled.
-% DATASRC_CACHE_EXPIRED the item '%1' is expired
-Debug information. There was an attempt to look up an item in the hotspot
-cache. And the item was actually there, but it was too old, so it was removed
-instead and nothing is reported (the external behaviour is the same as with
-CACHE_NOT_FOUND).
+% DATASRC_CACHE_EXPIRED item '%1' in the hotspot cache has expired
+A debug message issued when a hotspot cache lookup located the item but it
+had expired. The item was removed and the program proceeded as if the item
+had not been found.
% DATASRC_CACHE_FOUND the item '%1' was found
-Debug information. An item was successfully looked up in the hotspot cache.
+Debug information. An item was successfully located in the hotspot cache.
-% DATASRC_CACHE_FULL cache is full, dropping oldest
+% DATASRC_CACHE_FULL hotspot cache is full, dropping oldest
Debug information. After inserting an item into the hotspot cache, the
maximum number of items was exceeded, so the least recently used item will
be dropped. This should be directly followed by CACHE_REMOVE.
-% DATASRC_CACHE_INSERT inserting item '%1' into the cache
-Debug information. It means a new item is being inserted into the hotspot
+% DATASRC_CACHE_INSERT inserting item '%1' into the hotspot cache
+A debug message indicating that a new item is being inserted into the hotspot
cache.
-% DATASRC_CACHE_NOT_FOUND the item '%1' was not found
-Debug information. It was attempted to look up an item in the hotspot cache,
-but it is not there.
+% DATASRC_CACHE_NOT_FOUND the item '%1' was not found in the hotspot cache
+A debug message issued when hotspot cache was searched for the specified
+item but it was not found.
-% DATASRC_CACHE_OLD_FOUND older instance of cache item found, replacing
+% DATASRC_CACHE_OLD_FOUND older instance of hotspot cache item '%1' found, replacing
Debug information. While inserting an item into the hotspot cache, an older
-instance of an item with the same name was found. The old instance will be
-removed. This should be directly followed by CACHE_REMOVE.
+instance of an item with the same name was found; the old instance will be
+removed. This will be directly followed by CACHE_REMOVE.
-% DATASRC_CACHE_REMOVE removing '%1' from the cache
+% DATASRC_CACHE_REMOVE removing '%1' from the hotspot cache
Debug information. An item is being removed from the hotspot cache.
-% DATASRC_CACHE_SLOTS setting the cache size to '%1', dropping '%2' items
+% DATASRC_CACHE_SLOTS setting the hotspot cache size to '%1', dropping '%2' items
The maximum allowed number of items of the hotspot cache is set to the given
number. If there are too many, some of them will be dropped. The size of 0
means no limit.
% DATASRC_DO_QUERY handling query for '%1/%2'
-Debug information. We're processing some internal query for given name and
-type.
+A debug message indicating that a query for the given name and RR type is being
+processed.
% DATASRC_MEM_ADD_RRSET adding RRset '%1/%2' into zone '%3'
Debug information. An RRset is being added to the in-memory data source.
% DATASRC_MEM_ADD_WILDCARD adding wildcards for '%1'
-Debug information. Some special marks above each * in wildcard name are needed.
-They are being added now for this name.
+This is a debug message issued during the processing of a wildcard
+name. The internal domain name tree is scanned and some nodes are
+specially marked to allow the wildcard lookup to succeed.
% DATASRC_MEM_ADD_ZONE adding zone '%1/%2'
Debug information. A zone is being added into the in-memory data source.
@@ -114,9 +114,9 @@ stop the search.
Debug information. A DNAME was found instead of the requested information.
% DATASRC_MEM_DNAME_NS DNAME and NS can't coexist in non-apex domain '%1'
-It was requested for DNAME and NS records to be put into the same domain
-which is not the apex (the top of the zone). This is forbidden by RFC
-2672, section 3. This indicates a problem with provided data.
+A request was made for DNAME and NS records to be put into the same
+domain which is not the apex (the top of the zone). This is forbidden
+by RFC 2672 (section 3) and indicates a problem with provided data.
% DATASRC_MEM_DOMAIN_EMPTY requested domain '%1' is empty
Debug information. The requested domain exists in the tree of domains, but
@@ -142,7 +142,7 @@ in-memory data source.
% DATASRC_MEM_LOAD loading zone '%1' from file '%2'
Debug information. The content of master file is being loaded into the memory.
-% DATASRC_MEM_NOTFOUND requested domain '%1' not found
+% DATASRC_MEM_NOT_FOUND requested domain '%1' not found
Debug information. The requested domain does not exist.
% DATASRC_MEM_NS_ENCOUNTERED encountered a NS
@@ -201,11 +201,11 @@ behave and BIND 9 refuses that as well. Please describe your intention using
different tools.
% DATASRC_META_ADD adding a data source into meta data source
-Debug information. Yet another data source is being added into the meta data
-source. (probably at startup or reconfiguration)
+This is a debug message issued during startup or reconfiguration.
+Another data source is being added into the meta data source.
% DATASRC_META_ADD_CLASS_MISMATCH mismatch between classes '%1' and '%2'
-It was attempted to add a data source into a meta data source. But their
+It was attempted to add a data source into a meta data source, but their
classes do not match.
% DATASRC_META_REMOVE removing data source from meta data source
@@ -234,11 +234,11 @@ specific error already.
The domain lives in another zone. But it is not possible to generate referral
information for it.
-% DATASRC_QUERY_CACHED data for %1/%2 found in cache
+% DATASRC_QUERY_CACHED data for %1/%2 found in hotspot cache
Debug information. The requested data were found in the hotspot cache, so
no query is sent to the real data source.
-% DATASRC_QUERY_CHECK_CACHE checking cache for '%1/%2'
+% DATASRC_QUERY_CHECK_CACHE checking hotspot cache for '%1/%2'
Debug information. While processing a query, lookup to the hotspot cache
is being made.
@@ -251,10 +251,9 @@ Debug information. The software is trying to identify delegation points on the
way down to the given domain.
% DATASRC_QUERY_EMPTY_CNAME CNAME at '%1' is empty
-There was an CNAME and it was being followed. But it contains no records,
-so there's nowhere to go. There will be no answer. This indicates a problem
-with supplied data.
-We tried to follow
+A CNAME chain was being followed and an entry was found that pointed
+to a domain name that had no RRsets associated with it. As a result,
+the query cannot be answered. This indicates a problem with supplied data.
% DATASRC_QUERY_EMPTY_DNAME the DNAME on '%1' is empty
During an attempt to synthesize CNAME from this DNAME it was discovered the
@@ -262,11 +261,11 @@ DNAME is empty (it has no records). This indicates problem with supplied data.
% DATASRC_QUERY_FAIL query failed
Some subtask of query processing failed. The reason should have been reported
-already. We are returning SERVFAIL.
+already and a SERVFAIL will be returned to the querying system.
% DATASRC_QUERY_FOLLOW_CNAME following CNAME at '%1'
-Debug information. The domain is a CNAME (or a DNAME and we created a CNAME
-for it already), so it's being followed.
+Debug information. The domain is a CNAME (or a DNAME and a CNAME for it
+has already been created) and the search is following this chain.
% DATASRC_QUERY_GET_MX_ADDITIONAL addition of A/AAAA for '%1' requested by MX '%2'
Debug information. While processing a query, a MX record was met. It
@@ -291,14 +290,14 @@ operation code.
Debug information. The last DO_QUERY is an auth query.
% DATASRC_QUERY_IS_GLUE glue query (%1/%2)
-Debug information. The last DO_QUERY is query for glue addresses.
+Debug information. The last DO_QUERY is a query for glue addresses.
% DATASRC_QUERY_IS_NOGLUE query for non-glue addresses (%1/%2)
-Debug information. The last DO_QUERY is query for addresses that are not
+Debug information. The last DO_QUERY is a query for addresses that are not
glue.
% DATASRC_QUERY_IS_REF query for referral (%1/%2)
-Debug information. The last DO_QUERY is query for referral information.
+Debug information. The last DO_QUERY is a query for referral information.
% DATASRC_QUERY_IS_SIMPLE simple query (%1/%2)
Debug information. The last DO_QUERY is a simple query.
@@ -322,11 +321,11 @@ The underlying data source failed to answer the no-glue query. 1 means some
error, 2 is not implemented. The data source should have logged the specific
error already.
-% DATASRC_QUERY_NO_CACHE_ANY_AUTH ignoring cache for ANY query (%1/%2 in %3 class)
+% DATASRC_QUERY_NO_CACHE_ANY_AUTH ignoring hotspot cache for ANY query (%1/%2 in %3 class)
Debug information. The hotspot cache is ignored for authoritative ANY queries
for consistency reasons.
-% DATASRC_QUERY_NO_CACHE_ANY_SIMPLE ignoring cache for ANY query (%1/%2 in %3 class)
+% DATASRC_QUERY_NO_CACHE_ANY_SIMPLE ignoring hotspot cache for ANY query (%1/%2 in %3 class)
Debug information. The hotspot cache is ignored for ANY queries for consistency
reasons.
@@ -345,7 +344,7 @@ domain. Maybe someone sent a query to the wrong server for some reason.
% DATASRC_QUERY_PROCESS processing query '%1/%2' in the '%3' class
Debug information. A sure query is being processed now.
-% DATASRC_QUERY_PROVENX_FAIL unable to prove nonexistence of '%1'
+% DATASRC_QUERY_PROVE_NX_FAIL unable to prove nonexistence of '%1'
The user wants DNSSEC and we discovered the entity doesn't exist (either
domain or the record). But there was an error getting NSEC/NSEC3 record
to prove the nonexistence.
@@ -365,9 +364,9 @@ error, 2 is not implemented. The data source should have logged the specific
error already.
% DATASRC_QUERY_SYNTH_CNAME synthesizing CNAME from DNAME on '%1'
-Debug information. While answering a query, a DNAME was met. The DNAME itself
-will be returned, but along with it a CNAME for clients which don't understand
-DNAMEs will be synthesized.
+This is a debug message. While answering a query, a DNAME was encountered. The
+DNAME itself will be returned, along with a synthesized CNAME for clients that
+do not understand the DNAME RR.
% DATASRC_QUERY_TASK_FAIL task failed with %1
The query subtask failed. The reason should have been reported by the subtask
@@ -391,7 +390,7 @@ domain is being looked for now.
During an attempt to cover the domain by a wildcard an error happened. The
exact kind was hopefully already reported.
-% DATASRC_QUERY_WILDCARD_PROVENX_FAIL unable to prove nonexistence of '%1' (%2)
+% DATASRC_QUERY_WILDCARD_PROVE_NX_FAIL unable to prove nonexistence of '%1' (%2)
While processing a wildcard, it wasn't possible to prove nonexistence of the
given domain or record. The code is 1 for error and 2 for not implemented.
@@ -411,7 +410,7 @@ Debug information. An instance of SQLite data source is being destroyed.
Debug information. The SQLite data source is trying to identify which zone
should hold this domain.
-% DATASRC_SQLITE_ENCLOSURE_NOTFOUND no zone contains it
+% DATASRC_SQLITE_ENCLOSURE_NOT_FOUND no zone contains '%1'
Debug information. The last SQLITE_ENCLOSURE query was unsuccessful; there's
no such zone in our data.
@@ -464,20 +463,27 @@ Debug information. The SQLite data source is loading an SQLite database in
the provided file.
% DATASRC_SQLITE_PREVIOUS looking for name previous to '%1'
-Debug information. We're trying to look up name preceding the supplied one.
+This is a debug message. The name given was not found, so the program
+is searching for the next name higher up the hierarchy (e.g. if
+www.example.com were queried for and not found, the software searches
+for the "previous" name, example.com).
% DATASRC_SQLITE_PREVIOUS_NO_ZONE no zone containing '%1'
-The SQLite data source tried to identify name preceding this one. But this
-one is not contained in any zone in the data source.
+The name given was not found, so the program is searching for the next
+name higher up the hierarchy (e.g. if www.example.com were queried
+for and not found, the software searches for the "previous" name,
+example.com). However, this name is not contained in any zone in the
+data source. This is an error since it indicates a problem in the earlier
+processing of the query.
% DATASRC_SQLITE_SETUP setting up SQLite database
The database for SQLite data source was found empty. It is assumed this is the
first run and it is being initialized with current schema. It'll still contain
no data, but it will be ready for use.
-% DATASRC_STATIC_BAD_CLASS static data source can handle CH only
-For some reason, someone asked the static data source a query that is not in
-the CH class.
+% DATASRC_STATIC_CLASS_NOT_CH static data source can handle CH class only
+An error message indicating that a query requesting a RR for a class other
+that CH was sent to the static data source (which only handles CH queries).
% DATASRC_STATIC_CREATE creating the static datasource
Debug information. The static data source (the one holding stuff like
diff --git a/src/lib/datasrc/memory_datasrc.cc b/src/lib/datasrc/memory_datasrc.cc
index b8019a2..6565000 100644
--- a/src/lib/datasrc/memory_datasrc.cc
+++ b/src/lib/datasrc/memory_datasrc.cc
@@ -129,7 +129,7 @@ struct MemoryZone::MemoryZoneImpl {
// Ensure CNAME and other type of RR don't coexist for the same
// owner name.
if (rrset->getType() == RRType::CNAME()) {
- // XXX: this check will become incorrect when we support DNSSEC
+ // TODO: this check will become incorrect when we support DNSSEC
// (depending on how we support DNSSEC). We should revisit it
// at that point.
if (!domain->empty()) {
@@ -523,7 +523,7 @@ struct MemoryZone::MemoryZoneImpl {
// fall through
case DomainTree::NOTFOUND:
- LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_NOTFOUND).
+ LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_NOT_FOUND).
arg(name);
return (FindResult(NXDOMAIN, ConstRRsetPtr()));
case DomainTree::EXACTMATCH: // This one is OK, handle it
diff --git a/src/lib/datasrc/sqlite3_datasrc.cc b/src/lib/datasrc/sqlite3_datasrc.cc
index 13d98ed..18ee929 100644
--- a/src/lib/datasrc/sqlite3_datasrc.cc
+++ b/src/lib/datasrc/sqlite3_datasrc.cc
@@ -356,7 +356,8 @@ Sqlite3DataSrc::findClosestEnclosure(DataSrcMatch& match) const {
unsigned int position;
if (findClosest(match.getName(), &position) == -1) {
- LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_SQLITE_ENCLOSURE_NOTFOUND);
+ LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_SQLITE_ENCLOSURE_NOT_FOUND)
+ .arg(match.getName());
return;
}
diff --git a/src/lib/datasrc/static_datasrc.cc b/src/lib/datasrc/static_datasrc.cc
index dee14b9..65229a0 100644
--- a/src/lib/datasrc/static_datasrc.cc
+++ b/src/lib/datasrc/static_datasrc.cc
@@ -161,7 +161,7 @@ StaticDataSrc::findRRset(const Name& qname,
arg(qtype);
flags = 0;
if (qclass != getClass() && qclass != RRClass::ANY()) {
- LOG_ERROR(logger, DATASRC_STATIC_BAD_CLASS);
+ LOG_ERROR(logger, DATASRC_STATIC_CLASS_NOT_CH);
return (ERROR);
}
diff --git a/src/lib/dns/message.cc b/src/lib/dns/message.cc
index bf7ccd5..c5ba4e1 100644
--- a/src/lib/dns/message.cc
+++ b/src/lib/dns/message.cc
@@ -239,7 +239,28 @@ MessageImpl::toWire(AbstractMessageRenderer& renderer, TSIGContext* tsig_ctx) {
"Message rendering attempted without Opcode set");
}
+ // Reserve the space for TSIG (if needed) so that we can handle truncation
+ // case correctly later when that happens. orig_xxx variables remember
+ // some configured parameters of renderer in case they are needed in
+ // truncation processing below.
+ const size_t tsig_len = (tsig_ctx != NULL) ? tsig_ctx->getTSIGLength() : 0;
+ const size_t orig_msg_len_limit = renderer.getLengthLimit();
+ const AbstractMessageRenderer::CompressMode orig_compress_mode =
+ renderer.getCompressMode();
+ if (tsig_len > 0) {
+ if (tsig_len > orig_msg_len_limit) {
+ isc_throw(InvalidParameter, "Failed to render DNS message: "
+ "too small limit for a TSIG (" <<
+ orig_msg_len_limit << ")");
+ }
+ renderer.setLengthLimit(orig_msg_len_limit - tsig_len);
+ }
+
// reserve room for the header
+ if (renderer.getLengthLimit() < HEADERLEN) {
+ isc_throw(InvalidParameter, "Failed to render DNS message: "
+ "too small limit for a Header");
+ }
renderer.skip(HEADERLEN);
uint16_t qdcount =
@@ -284,6 +305,22 @@ MessageImpl::toWire(AbstractMessageRenderer& renderer, TSIGContext* tsig_ctx) {
}
}
+ // If we're adding a TSIG to a truncated message, clear all RRsets
+ // from the message except for the question before adding the TSIG.
+ // If even (some of) the question doesn't fit, don't include it.
+ if (tsig_ctx != NULL && renderer.isTruncated()) {
+ renderer.clear();
+ renderer.setLengthLimit(orig_msg_len_limit - tsig_len);
+ renderer.setCompressMode(orig_compress_mode);
+ renderer.skip(HEADERLEN);
+ qdcount = for_each(questions_.begin(), questions_.end(),
+ RenderSection<QuestionPtr>(renderer,
+ false)).getTotalCount();
+ ancount = 0;
+ nscount = 0;
+ arcount = 0;
+ }
+
// Adjust the counter buffer.
// XXX: these may not be equal to the number of corresponding entries
// in rrsets_[] or questions_ if truncation occurred or an EDNS OPT RR
@@ -315,10 +352,16 @@ MessageImpl::toWire(AbstractMessageRenderer& renderer, TSIGContext* tsig_ctx) {
renderer.writeUint16At(arcount, header_pos);
// Add TSIG, if necessary, at the end of the message.
- // TODO: truncate case consideration
if (tsig_ctx != NULL) {
- tsig_ctx->sign(qid_, renderer.getData(),
- renderer.getLength())->toWire(renderer);
+ // Release the reserved space in the renderer.
+ renderer.setLengthLimit(orig_msg_len_limit);
+
+ const int tsig_count =
+ tsig_ctx->sign(qid_, renderer.getData(),
+ renderer.getLength())->toWire(renderer);
+ if (tsig_count != 1) {
+ isc_throw(Unexpected, "Failed to render a TSIG RR");
+ }
// update the ARCOUNT for the TSIG RR. Note that for a sane DNS
// message arcount should never overflow to 0.
diff --git a/src/lib/dns/message.h b/src/lib/dns/message.h
index fcc53e9..6a8bf9f 100644
--- a/src/lib/dns/message.h
+++ b/src/lib/dns/message.h
@@ -565,6 +565,17 @@ public:
/// \c tsig_ctx will be updated based on the fact it was used for signing
/// and with the latest MAC.
///
+ /// \exception InvalidMessageOperation The message is not in the Render
+ /// mode, or either Rcode or Opcode is not set.
+ /// \exception InvalidParameter The allowable limit of \c renderer is too
+ /// small for a TSIG or the Header section. Note that this shouldn't
+ /// happen with parameters as defined in the standard protocols,
+ /// so it's more likely a program bug.
+ /// \exception Unexpected Rendering the TSIG RR fails. The implementation
+ /// internally makes sure this doesn't happen, so if that ever occurs
+ /// it should mean a bug either in the TSIG context or in the renderer
+ /// implementation.
+ ///
/// \param renderer See the other version
/// \param tsig_ctx A TSIG context that is to be used for signing the
/// message
diff --git a/src/lib/dns/python/message_python.cc b/src/lib/dns/python/message_python.cc
index 2842588..00596f8 100644
--- a/src/lib/dns/python/message_python.cc
+++ b/src/lib/dns/python/message_python.cc
@@ -703,6 +703,15 @@ Message_toWire(s_Message* self, PyObject* args) {
// python program has a bug.
PyErr_SetString(po_TSIGContextError, ex.what());
return (NULL);
+ } catch (const std::exception& ex) {
+ // Other exceptions should be rare (most likely an implementation
+ // bug)
+ PyErr_SetString(po_TSIGContextError, ex.what());
+ return (NULL);
+ } catch (...) {
+ PyErr_SetString(PyExc_RuntimeError,
+ "Unexpected C++ exception in Message.to_wire");
+ return (NULL);
}
}
PyErr_Clear();
diff --git a/src/lib/dns/python/tests/message_python_test.py b/src/lib/dns/python/tests/message_python_test.py
index 41b9a67..c731253 100644
--- a/src/lib/dns/python/tests/message_python_test.py
+++ b/src/lib/dns/python/tests/message_python_test.py
@@ -21,6 +21,7 @@ import unittest
import os
from pydnspp import *
from testutil import *
+from pyunittests_util import fix_current_time
# helper functions for tests taken from c++ unittests
if "TESTDATA_PATH" in os.environ:
@@ -31,7 +32,7 @@ else:
def factoryFromFile(message, file):
data = read_wire_data(file)
message.from_wire(data)
- pass
+ return data
# we don't have direct comparison for rrsets right now (should we?
# should go in the cpp version first then), so also no direct list
@@ -44,6 +45,15 @@ def compare_rrset_list(list1, list2):
return False
return True
+# These are used for TSIG + TC tests
+LONG_TXT1 = "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcde";
+
+LONG_TXT2 = "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456";
+
+LONG_TXT3 = "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef01";
+
+LONG_TXT4 = "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0";
+
# a complete message taken from cpp tests, for testing towire and totext
def create_message():
message_render = Message(Message.RENDER)
@@ -62,16 +72,12 @@ def create_message():
message_render.add_rrset(Message.SECTION_ANSWER, rrset)
return message_render
-def strip_mutable_tsig_data(data):
- # Unfortunately we cannot easily compare TSIG RR because we can't tweak
- # current time. As a work around this helper function strips off the time
- # dependent part of TSIG RDATA, i.e., the MAC (assuming HMAC-MD5) and
- # Time Signed.
- return data[0:-32] + data[-26:-22] + data[-6:]
-
class MessageTest(unittest.TestCase):
def setUp(self):
+ # make sure we don't use faked time unless explicitly do so in tests
+ fix_current_time(None)
+
self.p = Message(Message.PARSE)
self.r = Message(Message.RENDER)
@@ -90,6 +96,10 @@ class MessageTest(unittest.TestCase):
self.tsig_key = TSIGKey("www.example.com:SFuWd/q99SzF8Yzd1QbB9g==")
self.tsig_ctx = TSIGContext(self.tsig_key)
+ def tearDown(self):
+ # reset any faked current time setting (it would affect other tests)
+ fix_current_time(None)
+
def test_init(self):
self.assertRaises(TypeError, Message, -1)
self.assertRaises(TypeError, Message, 3)
@@ -285,33 +295,112 @@ class MessageTest(unittest.TestCase):
self.assertRaises(InvalidMessageOperation, self.r.to_wire,
MessageRenderer())
- def __common_tsigquery_setup(self):
+ def __common_tsigmessage_setup(self, flags=[Message.HEADERFLAG_RD],
+ rrtype=RRType("A"), answer_data=None):
self.r.set_opcode(Opcode.QUERY())
self.r.set_rcode(Rcode.NOERROR())
- self.r.set_header_flag(Message.HEADERFLAG_RD)
+ for flag in flags:
+ self.r.set_header_flag(flag)
+ if answer_data is not None:
+ rrset = RRset(Name("www.example.com"), RRClass("IN"),
+ rrtype, RRTTL(86400))
+ for rdata in answer_data:
+ rrset.add_rdata(Rdata(rrtype, RRClass("IN"), rdata))
+ self.r.add_rrset(Message.SECTION_ANSWER, rrset)
self.r.add_question(Question(Name("www.example.com"),
- RRClass("IN"), RRType("A")))
+ RRClass("IN"), rrtype))
def __common_tsig_checks(self, expected_file):
renderer = MessageRenderer()
self.r.to_wire(renderer, self.tsig_ctx)
- actual_wire = strip_mutable_tsig_data(renderer.get_data())
- expected_wire = strip_mutable_tsig_data(read_wire_data(expected_file))
- self.assertEqual(expected_wire, actual_wire)
+ self.assertEqual(read_wire_data(expected_file), renderer.get_data())
def test_to_wire_with_tsig(self):
+ fix_current_time(0x4da8877a)
self.r.set_qid(0x2d65)
- self.__common_tsigquery_setup()
+ self.__common_tsigmessage_setup()
self.__common_tsig_checks("message_toWire2.wire")
def test_to_wire_with_edns_tsig(self):
+ fix_current_time(0x4db60d1f)
self.r.set_qid(0x6cd)
- self.__common_tsigquery_setup()
+ self.__common_tsigmessage_setup()
edns = EDNS()
edns.set_udp_size(4096)
self.r.set_edns(edns)
self.__common_tsig_checks("message_toWire3.wire")
+ def test_to_wire_tsig_truncation(self):
+ fix_current_time(0x4e179212)
+ data = factoryFromFile(self.p, "message_fromWire17.wire")
+ self.assertEqual(TSIGError.NOERROR,
+ self.tsig_ctx.verify(self.p.get_tsig_record(), data))
+ self.r.set_qid(0x22c2)
+ self.__common_tsigmessage_setup([Message.HEADERFLAG_QR,
+ Message.HEADERFLAG_AA,
+ Message.HEADERFLAG_RD],
+ RRType("TXT"),
+ [LONG_TXT1, LONG_TXT2])
+ self.__common_tsig_checks("message_toWire4.wire")
+
+ def test_to_wire_tsig_truncation2(self):
+ fix_current_time(0x4e179212)
+ data = factoryFromFile(self.p, "message_fromWire17.wire")
+ self.assertEqual(TSIGError.NOERROR,
+ self.tsig_ctx.verify(self.p.get_tsig_record(), data))
+ self.r.set_qid(0x22c2)
+ self.__common_tsigmessage_setup([Message.HEADERFLAG_QR,
+ Message.HEADERFLAG_AA,
+ Message.HEADERFLAG_RD],
+ RRType("TXT"),
+ [LONG_TXT1, LONG_TXT3])
+ self.__common_tsig_checks("message_toWire4.wire")
+
+ def test_to_wire_tsig_truncation3(self):
+ self.r.set_opcode(Opcode.QUERY())
+ self.r.set_rcode(Rcode.NOERROR())
+ for i in range(1, 68):
+ self.r.add_question(Question(Name("www.example.com"),
+ RRClass("IN"), RRType(i)))
+ renderer = MessageRenderer()
+ self.r.to_wire(renderer, self.tsig_ctx)
+
+ self.p.from_wire(renderer.get_data())
+ self.assertTrue(self.p.get_header_flag(Message.HEADERFLAG_TC))
+ self.assertEqual(66, self.p.get_rr_count(Message.SECTION_QUESTION))
+ self.assertNotEqual(None, self.p.get_tsig_record())
+
+ def test_to_wire_tsig_no_truncation(self):
+ fix_current_time(0x4e17b38d)
+ data = factoryFromFile(self.p, "message_fromWire18.wire")
+ self.assertEqual(TSIGError.NOERROR,
+ self.tsig_ctx.verify(self.p.get_tsig_record(), data))
+ self.r.set_qid(0xd6e2)
+ self.__common_tsigmessage_setup([Message.HEADERFLAG_QR,
+ Message.HEADERFLAG_AA,
+ Message.HEADERFLAG_RD],
+ RRType("TXT"),
+ [LONG_TXT1, LONG_TXT4])
+ self.__common_tsig_checks("message_toWire5.wire")
+
+ def test_to_wire_tsig_length_errors(self):
+ renderer = MessageRenderer()
+ renderer.set_length_limit(84) # 84 = expected TSIG length - 1
+ self.__common_tsigmessage_setup()
+ self.assertRaises(TSIGContextError,
+ self.r.to_wire, renderer, self.tsig_ctx)
+
+ renderer.clear()
+ self.r.clear(Message.RENDER)
+ renderer.set_length_limit(86) # 86 = expected TSIG length + 1
+ self.__common_tsigmessage_setup()
+ self.assertRaises(TSIGContextError,
+ self.r.to_wire, renderer, self.tsig_ctx)
+
+ # skip the last test of the corresponding C++ test: it requires
+ # subclassing MessageRenderer, which is (currently) not possible
+ # for python. In any case, it's very unlikely to happen in practice.
+
def test_to_text(self):
message_render = create_message()
diff --git a/src/lib/dns/python/tests/question_python_test.py b/src/lib/dns/python/tests/question_python_test.py
index 69e3051..8c8c815 100644
--- a/src/lib/dns/python/tests/question_python_test.py
+++ b/src/lib/dns/python/tests/question_python_test.py
@@ -74,7 +74,6 @@ class QuestionTest(unittest.TestCase):
self.assertEqual("foo.example.com. IN NS\n", str(self.test_question1))
self.assertEqual("bar.example.com. CH A\n", self.test_question2.to_text())
-
def test_to_wire_buffer(self):
obuffer = bytes()
obuffer = self.test_question1.to_wire(obuffer)
@@ -82,7 +81,6 @@ class QuestionTest(unittest.TestCase):
wiredata = read_wire_data("question_toWire1")
self.assertEqual(obuffer, wiredata)
-
def test_to_wire_renderer(self):
renderer = MessageRenderer()
self.test_question1.to_wire(renderer)
@@ -91,5 +89,13 @@ class QuestionTest(unittest.TestCase):
self.assertEqual(renderer.get_data(), wiredata)
self.assertRaises(TypeError, self.test_question1.to_wire, 1)
+ def test_to_wire_truncated(self):
+ renderer = MessageRenderer()
+ renderer.set_length_limit(self.example_name1.get_length())
+ self.assertFalse(renderer.is_truncated())
+ self.test_question1.to_wire(renderer)
+ self.assertTrue(renderer.is_truncated())
+ self.assertEqual(0, renderer.get_length())
+
if __name__ == '__main__':
unittest.main()
diff --git a/src/lib/dns/question.cc b/src/lib/dns/question.cc
index 96e2a9c..6ccb164 100644
--- a/src/lib/dns/question.cc
+++ b/src/lib/dns/question.cc
@@ -57,10 +57,19 @@ Question::toWire(OutputBuffer& buffer) const {
unsigned int
Question::toWire(AbstractMessageRenderer& renderer) const {
+ const size_t pos0 = renderer.getLength();
+
renderer.writeName(name_);
rrtype_.toWire(renderer);
rrclass_.toWire(renderer);
+ // Make sure the renderer has a room for the question
+ if (renderer.getLength() > renderer.getLengthLimit()) {
+ renderer.trim(renderer.getLength() - pos0);
+ renderer.setTruncated();
+ return (0);
+ }
+
return (1); // number of "entries"
}
diff --git a/src/lib/dns/question.h b/src/lib/dns/question.h
index b3f3d98..5d2783b 100644
--- a/src/lib/dns/question.h
+++ b/src/lib/dns/question.h
@@ -201,23 +201,23 @@ public:
/// class description).
///
/// The owner name will be compressed if possible, although it's an
- /// unlikely event in practice because the %Question section a DNS
+ /// unlikely event in practice because the Question section a DNS
/// message normally doesn't contain multiple question entries and
/// it's located right after the Header section.
/// Nevertheless, \c renderer records the information of the owner name
/// so that it can be pointed by other RRs in other sections (which is
/// more likely to happen).
///
- /// In theory, an attempt to render a Question may cause truncation
- /// (when the Question section contains a large number of entries),
- /// but this implementation doesn't catch that situation.
- /// It would make the code unnecessarily complicated (though perhaps
- /// slightly) for almost impossible case in practice.
- /// An upper layer will handle the pathological case as a general error.
+ /// It could be possible, though very rare in practice, that
+ /// an attempt to render a Question may cause truncation
+ /// (when the Question section contains a large number of entries).
+ /// In such a case this method avoid the rendering and indicate the
+ /// truncation in the \c renderer. This method returns 0 in this case.
///
/// \param renderer DNS message rendering context that encapsulates the
/// output buffer and name compression information.
- /// \return 1
+ ///
+ /// \return 1 on success; 0 if it causes truncation
unsigned int toWire(AbstractMessageRenderer& renderer) const;
/// \brief Render the Question in the wire format without name compression.
diff --git a/src/lib/dns/tests/message_unittest.cc b/src/lib/dns/tests/message_unittest.cc
index c79ea2c..6430626 100644
--- a/src/lib/dns/tests/message_unittest.cc
+++ b/src/lib/dns/tests/message_unittest.cc
@@ -62,7 +62,6 @@ using namespace isc::dns::rdata;
//
const uint16_t Message::DEFAULT_MAX_UDPSIZE;
-const Name test_name("test.example.com");
namespace isc {
namespace util {
@@ -79,7 +78,8 @@ const uint16_t TSIGContext::DEFAULT_FUDGE;
namespace {
class MessageTest : public ::testing::Test {
protected:
- MessageTest() : obuffer(0), renderer(obuffer),
+ MessageTest() : test_name("test.example.com"), obuffer(0),
+ renderer(obuffer),
message_parse(Message::PARSE),
message_render(Message::RENDER),
bogus_section(static_cast<Message::Section>(
@@ -103,8 +103,9 @@ protected:
"FAKEFAKEFAKEFAKE"));
rrset_aaaa->addRRsig(rrset_rrsig);
}
-
+
static Question factoryFromFile(const char* datafile);
+ const Name test_name;
OutputBuffer obuffer;
MessageRenderer renderer;
Message message_parse;
@@ -114,17 +115,18 @@ protected:
RRsetPtr rrset_aaaa; // AAAA RRset with one RDATA with RRSIG
RRsetPtr rrset_rrsig; // RRSIG for the AAAA RRset
TSIGContext tsig_ctx;
+ vector<unsigned char> received_data;
vector<unsigned char> expected_data;
- static void factoryFromFile(Message& message, const char* datafile);
+ void factoryFromFile(Message& message, const char* datafile);
};
void
MessageTest::factoryFromFile(Message& message, const char* datafile) {
- std::vector<unsigned char> data;
- UnitTestUtil::readWireData(datafile, data);
+ received_data.clear();
+ UnitTestUtil::readWireData(datafile, received_data);
- InputBuffer buffer(&data[0], data.size());
+ InputBuffer buffer(&received_data[0], received_data.size());
message.fromWire(buffer);
}
@@ -618,15 +620,43 @@ testGetTime() {
return (NOW);
}
+// bit-wise constant flags to configure DNS header flags for test
+// messages.
+const unsigned int QR_FLAG = 0x1;
+const unsigned int AA_FLAG = 0x2;
+const unsigned int RD_FLAG = 0x4;
+
void
commonTSIGToWireCheck(Message& message, MessageRenderer& renderer,
- TSIGContext& tsig_ctx, const char* const expected_file)
+ TSIGContext& tsig_ctx, const char* const expected_file,
+ unsigned int message_flags = RD_FLAG,
+ RRType qtype = RRType::A(),
+ const vector<const char*>* answer_data = NULL)
{
message.setOpcode(Opcode::QUERY());
message.setRcode(Rcode::NOERROR());
- message.setHeaderFlag(Message::HEADERFLAG_RD, true);
+ if ((message_flags & QR_FLAG) != 0) {
+ message.setHeaderFlag(Message::HEADERFLAG_QR);
+ }
+ if ((message_flags & AA_FLAG) != 0) {
+ message.setHeaderFlag(Message::HEADERFLAG_AA);
+ }
+ if ((message_flags & RD_FLAG) != 0) {
+ message.setHeaderFlag(Message::HEADERFLAG_RD);
+ }
message.addQuestion(Question(Name("www.example.com"), RRClass::IN(),
- RRType::A()));
+ qtype));
+
+ if (answer_data != NULL) {
+ RRsetPtr ans_rrset(new RRset(Name("www.example.com"), RRClass::IN(),
+ qtype, RRTTL(86400)));
+ for (vector<const char*>::const_iterator it = answer_data->begin();
+ it != answer_data->end();
+ ++it) {
+ ans_rrset->addRdata(createRdata(qtype, RRClass::IN(), *it));
+ }
+ message.addRRset(Message::SECTION_ANSWER, ans_rrset);
+ }
message.toWire(renderer, tsig_ctx);
vector<unsigned char> expected_data;
@@ -670,6 +700,182 @@ TEST_F(MessageTest, toWireWithEDNSAndTSIG) {
}
}
+// Some of the following tests involve truncation. We use the query name
+// "www.example.com" and some TXT question/answers. The length of the
+// header and question will be 33 bytes. If we also try to include a
+// TSIG of the same key name (not compressed) with HMAC-MD5, the TSIG RR
+// will be 85 bytes.
+
+// A long TXT RDATA. With a fully compressed owner name, the corresponding
+// RR will be 268 bytes.
+const char* const long_txt1 = "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcde";
+
+// With a fully compressed owner name, the corresponding RR will be 212 bytes.
+// It should result in truncation even without TSIG (33 + 268 + 212 = 513)
+const char* const long_txt2 = "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456";
+
+// With a fully compressed owner name, the corresponding RR will be 127 bytes.
+// So, it can fit in the standard 512 bytes with txt1 and without TSIG, but
+// adding a TSIG would result in truncation (33 + 268 + 127 + 85 = 513)
+const char* const long_txt3 = "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef01";
+
+// This is 1 byte shorter than txt3, which will result in a possible longest
+// message containing answer RRs and TSIG.
+const char* const long_txt4 = "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0";
+
+// Example output generated by
+// "dig -y www.example.com:SFuWd/q99SzF8Yzd1QbB9g== www.example.com txt
+// QID: 0x22c2
+// Time Signed: 0x00004e179212
+TEST_F(MessageTest, toWireTSIGTruncation) {
+ isc::util::detail::gettimeFunction = testGetTime<0x4e179212>;
+
+ // Verify a validly signed query so that we can use the TSIG context
+
+ factoryFromFile(message_parse, "message_fromWire17.wire");
+ EXPECT_EQ(TSIGError::NOERROR(),
+ tsig_ctx.verify(message_parse.getTSIGRecord(),
+ &received_data[0], received_data.size()));
+
+ message_render.setQid(0x22c2);
+ vector<const char*> answer_data;
+ answer_data.push_back(long_txt1);
+ answer_data.push_back(long_txt2);
+ {
+ SCOPED_TRACE("Message sign with TSIG and TC bit on");
+ commonTSIGToWireCheck(message_render, renderer, tsig_ctx,
+ "message_toWire4.wire",
+ QR_FLAG|AA_FLAG|RD_FLAG,
+ RRType::TXT(), &answer_data);
+ }
+}
+
+TEST_F(MessageTest, toWireTSIGTruncation2) {
+ // Similar to the previous test, but without TSIG it wouldn't cause
+ // truncation.
+ isc::util::detail::gettimeFunction = testGetTime<0x4e179212>;
+ factoryFromFile(message_parse, "message_fromWire17.wire");
+ EXPECT_EQ(TSIGError::NOERROR(),
+ tsig_ctx.verify(message_parse.getTSIGRecord(),
+ &received_data[0], received_data.size()));
+
+ message_render.setQid(0x22c2);
+ vector<const char*> answer_data;
+ answer_data.push_back(long_txt1);
+ answer_data.push_back(long_txt3);
+ {
+ SCOPED_TRACE("Message sign with TSIG and TC bit on (2)");
+ commonTSIGToWireCheck(message_render, renderer, tsig_ctx,
+ "message_toWire4.wire",
+ QR_FLAG|AA_FLAG|RD_FLAG,
+ RRType::TXT(), &answer_data);
+ }
+}
+
+TEST_F(MessageTest, toWireTSIGTruncation3) {
+ // Similar to previous ones, but truncation occurs due to too many
+ // Questions (very unusual, but not necessarily illegal).
+
+ // We are going to create a message starting with a standard
+ // header (12 bytes) and multiple questions in the Question
+ // section of the same owner name (changing the RRType, just so
+ // that it would be the form that would be accepted by the BIND 9
+ // parser). The first Question is 21 bytes in length, and the subsequent
+ // ones are 6 bytes. We'll also use a TSIG whose size is 85 bytes.
+ // Up to 66 questions can fit in the standard 512-byte buffer
+ // (12 + 21 + 6 * 65 + 85 = 508). If we try to add one more it would
+ // result in truncation.
+ message_render.setOpcode(Opcode::QUERY());
+ message_render.setRcode(Rcode::NOERROR());
+ for (int i = 1; i <= 67; ++i) {
+ message_render.addQuestion(Question(Name("www.example.com"),
+ RRClass::IN(), RRType(i)));
+ }
+ message_render.toWire(renderer, tsig_ctx);
+
+ // Check the rendered data by parsing it. We only check it has the
+ // TC bit on, has the correct number of questions, and has a TSIG RR.
+ // Checking the signature wouldn't be necessary for this rare case
+ // scenario.
+ InputBuffer buffer(renderer.getData(), renderer.getLength());
+ message_parse.fromWire(buffer);
+ EXPECT_TRUE(message_parse.getHeaderFlag(Message::HEADERFLAG_TC));
+ // Note that the number of questions are 66, not 67 as we tried to add.
+ EXPECT_EQ(66, message_parse.getRRCount(Message::SECTION_QUESTION));
+ EXPECT_TRUE(message_parse.getTSIGRecord() != NULL);
+}
+
+TEST_F(MessageTest, toWireTSIGNoTruncation) {
+ // A boundary case that shouldn't cause truncation: the resulting
+ // response message with a TSIG will be 512 bytes long.
+ isc::util::detail::gettimeFunction = testGetTime<0x4e17b38d>;
+ factoryFromFile(message_parse, "message_fromWire18.wire");
+ EXPECT_EQ(TSIGError::NOERROR(),
+ tsig_ctx.verify(message_parse.getTSIGRecord(),
+ &received_data[0], received_data.size()));
+
+ message_render.setQid(0xd6e2);
+ vector<const char*> answer_data;
+ answer_data.push_back(long_txt1);
+ answer_data.push_back(long_txt4);
+ {
+ SCOPED_TRACE("Message sign with TSIG, no truncation");
+ commonTSIGToWireCheck(message_render, renderer, tsig_ctx,
+ "message_toWire5.wire",
+ QR_FLAG|AA_FLAG|RD_FLAG,
+ RRType::TXT(), &answer_data);
+ }
+}
+
+// This is a buggy renderer for testing. It behaves like the straightforward
+// MessageRenderer, but once it has some data, its setLengthLimit() ignores
+// the given parameter and resets the limit to the current length, making
+// subsequent insertion result in truncation, which would make TSIG RR
+// rendering fail unexpectedly in the test that follows.
+class BadRenderer : public MessageRenderer {
+public:
+ BadRenderer(isc::util::OutputBuffer& buffer) :
+ MessageRenderer(buffer)
+ {}
+ virtual void setLengthLimit(size_t len) {
+ if (getLength() > 0) {
+ MessageRenderer::setLengthLimit(getLength());
+ } else {
+ MessageRenderer::setLengthLimit(len);
+ }
+ }
+};
+
+TEST_F(MessageTest, toWireTSIGLengthErrors) {
+ // specify an unusual short limit that wouldn't be able to hold
+ // the TSIG.
+ renderer.setLengthLimit(tsig_ctx.getTSIGLength() - 1);
+ // Use commonTSIGToWireCheck() only to call toWire() with otherwise valid
+ // conditions. The checks inside it don't matter because we expect an
+ // exception before any of the checks.
+ EXPECT_THROW(commonTSIGToWireCheck(message_render, renderer, tsig_ctx,
+ "message_toWire2.wire"),
+ InvalidParameter);
+
+ // This one is large enough for TSIG, but the remaining limit isn't
+ // even enough for the Header section.
+ renderer.clear();
+ message_render.clear(Message::RENDER);
+ renderer.setLengthLimit(tsig_ctx.getTSIGLength() + 1);
+ EXPECT_THROW(commonTSIGToWireCheck(message_render, renderer, tsig_ctx,
+ "message_toWire2.wire"),
+ InvalidParameter);
+
+ // Trying to render a message with TSIG using a buggy renderer.
+ obuffer.clear();
+ BadRenderer bad_renderer(obuffer);
+ bad_renderer.setLengthLimit(512);
+ message_render.clear(Message::RENDER);
+ EXPECT_THROW(commonTSIGToWireCheck(message_render, bad_renderer, tsig_ctx,
+ "message_toWire2.wire"),
+ Unexpected);
+}
+
TEST_F(MessageTest, toWireWithoutOpcode) {
message_render.setRcode(Rcode::NOERROR());
EXPECT_THROW(message_render.toWire(renderer), InvalidMessageOperation);
diff --git a/src/lib/dns/tests/question_unittest.cc b/src/lib/dns/tests/question_unittest.cc
index 25fd75b..1d483f2 100644
--- a/src/lib/dns/tests/question_unittest.cc
+++ b/src/lib/dns/tests/question_unittest.cc
@@ -106,6 +106,22 @@ TEST_F(QuestionTest, toWireRenderer) {
obuffer.getLength(), &wiredata[0], wiredata.size());
}
+TEST_F(QuestionTest, toWireTruncated) {
+ // If the available length in the renderer is too small, it would require
+ // truncation. This won't happen in normal cases, but protocol wise it
+ // could still happen if and when we support some (possibly future) opcode
+ // that allows multiple questions.
+
+ // Set the length limit to the qname length so that the whole question
+ // would request truncated
+ renderer.setLengthLimit(example_name1.getLength());
+
+ EXPECT_FALSE(renderer.isTruncated()); // check pre-render condition
+ EXPECT_EQ(0, test_question1.toWire(renderer));
+ EXPECT_TRUE(renderer.isTruncated());
+ EXPECT_EQ(0, renderer.getLength()); // renderer shouldn't have any data
+}
+
// test operator<<. We simply confirm it appends the result of toText().
TEST_F(QuestionTest, LeftShiftOperator) {
ostringstream oss;
diff --git a/src/lib/dns/tests/testdata/Makefile.am b/src/lib/dns/tests/testdata/Makefile.am
index cb1bb1c..257f2f3 100644
--- a/src/lib/dns/tests/testdata/Makefile.am
+++ b/src/lib/dns/tests/testdata/Makefile.am
@@ -5,8 +5,10 @@ BUILT_SOURCES += edns_toWire4.wire
BUILT_SOURCES += message_fromWire10.wire message_fromWire11.wire
BUILT_SOURCES += message_fromWire12.wire message_fromWire13.wire
BUILT_SOURCES += message_fromWire14.wire message_fromWire15.wire
-BUILT_SOURCES += message_fromWire16.wire
+BUILT_SOURCES += message_fromWire16.wire message_fromWire17.wire
+BUILT_SOURCES += message_fromWire18.wire
BUILT_SOURCES += message_toWire2.wire message_toWire3.wire
+BUILT_SOURCES += message_toWire4.wire message_toWire5.wire
BUILT_SOURCES += message_toText1.wire message_toText2.wire
BUILT_SOURCES += message_toText3.wire
BUILT_SOURCES += name_toWire5.wire name_toWire6.wire
@@ -59,7 +61,9 @@ EXTRA_DIST += message_fromWire9 message_fromWire10.spec
EXTRA_DIST += message_fromWire11.spec message_fromWire12.spec
EXTRA_DIST += message_fromWire13.spec message_fromWire14.spec
EXTRA_DIST += message_fromWire15.spec message_fromWire16.spec
+EXTRA_DIST += message_fromWire17.spec message_fromWire18.spec
EXTRA_DIST += message_toWire1 message_toWire2.spec message_toWire3.spec
+EXTRA_DIST += message_toWire4.spec message_toWire5.spec
EXTRA_DIST += message_toText1.txt message_toText1.spec
EXTRA_DIST += message_toText2.txt message_toText2.spec
EXTRA_DIST += message_toText3.txt message_toText3.spec
diff --git a/src/lib/dns/tests/testdata/gen-wiredata.py.in b/src/lib/dns/tests/testdata/gen-wiredata.py.in
index fd98c6e..818c6e9 100755
--- a/src/lib/dns/tests/testdata/gen-wiredata.py.in
+++ b/src/lib/dns/tests/testdata/gen-wiredata.py.in
@@ -307,8 +307,8 @@ class SOA(RR):
self.retry, self.expire,
self.minimum))
-class TXT:
- rdlen = -1 # auto-calculate
+class TXT(RR):
+ rdlen = None # auto-calculate
nstring = 1 # number of character-strings
stringlen = -1 # default string length, auto-calculate
string = 'Test String' # default string
@@ -330,11 +330,9 @@ class TXT:
stringlen_list.append(self.stringlen)
if stringlen_list[-1] < 0:
stringlen_list[-1] = int(len(wirestring_list[-1]) / 2)
- rdlen = self.rdlen
- if rdlen < 0:
- rdlen = int(len(''.join(wirestring_list)) / 2) + self.nstring
- f.write('\n# TXT RDATA (RDLEN=%d)\n' % rdlen)
- f.write('%04x\n' % rdlen);
+ if self.rdlen is None:
+ self.rdlen = int(len(''.join(wirestring_list)) / 2) + self.nstring
+ self.dump_header(f, self.rdlen)
for i in range(0, self.nstring):
f.write('# String Len=%d, String=\"%s\"\n' %
(stringlen_list[i], string_list[i]))
diff --git a/src/lib/dns/tests/testdata/message_fromWire17.spec b/src/lib/dns/tests/testdata/message_fromWire17.spec
new file mode 100644
index 0000000..366cf05
--- /dev/null
+++ b/src/lib/dns/tests/testdata/message_fromWire17.spec
@@ -0,0 +1,22 @@
+#
+# A simple DNS query message with TSIG signed
+#
+
+[custom]
+sections: header:question:tsig
+[header]
+id: 0x22c2
+rd: 1
+arcount: 1
+[question]
+name: www.example.com
+rrtype: TXT
+[tsig]
+as_rr: True
+# TSIG QNAME won't be compressed
+rr_name: www.example.com
+algorithm: hmac-md5
+time_signed: 0x4e179212
+mac_size: 16
+mac: 0x8214b04634e32323d651ac60b08e6388
+original_id: 0x22c2
diff --git a/src/lib/dns/tests/testdata/message_fromWire18.spec b/src/lib/dns/tests/testdata/message_fromWire18.spec
new file mode 100644
index 0000000..0b2592a
--- /dev/null
+++ b/src/lib/dns/tests/testdata/message_fromWire18.spec
@@ -0,0 +1,23 @@
+#
+# Another simple DNS query message with TSIG signed. Only ID and time signed
+# (and MAC as a result) are different.
+#
+
+[custom]
+sections: header:question:tsig
+[header]
+id: 0xd6e2
+rd: 1
+arcount: 1
+[question]
+name: www.example.com
+rrtype: TXT
+[tsig]
+as_rr: True
+# TSIG QNAME won't be compressed
+rr_name: www.example.com
+algorithm: hmac-md5
+time_signed: 0x4e17b38d
+mac_size: 16
+mac: 0x903b5b194a799b03a37718820c2404f2
+original_id: 0xd6e2
diff --git a/src/lib/dns/tests/testdata/message_toWire4.spec b/src/lib/dns/tests/testdata/message_toWire4.spec
new file mode 100644
index 0000000..aab7e10
--- /dev/null
+++ b/src/lib/dns/tests/testdata/message_toWire4.spec
@@ -0,0 +1,27 @@
+#
+# Truncated DNS response with TSIG signed
+# This is expected to be a response to "fromWire17"
+#
+
+[custom]
+sections: header:question:tsig
+[header]
+id: 0x22c2
+rd: 1
+qr: 1
+aa: 1
+# It's "truncated":
+tc: 1
+arcount: 1
+[question]
+name: www.example.com
+rrtype: TXT
+[tsig]
+as_rr: True
+# TSIG QNAME won't be compressed
+rr_name: www.example.com
+algorithm: hmac-md5
+time_signed: 0x4e179212
+mac_size: 16
+mac: 0x88adc3811d1d6bec7c684438906fc694
+original_id: 0x22c2
diff --git a/src/lib/dns/tests/testdata/message_toWire5.spec b/src/lib/dns/tests/testdata/message_toWire5.spec
new file mode 100644
index 0000000..e97fb43
--- /dev/null
+++ b/src/lib/dns/tests/testdata/message_toWire5.spec
@@ -0,0 +1,36 @@
+#
+# A longest possible (without EDNS) DNS response with TSIG, i.e. totatl
+# length should be 512 bytes.
+#
+
+[custom]
+sections: header:question:txt/1:txt/2:tsig
+[header]
+id: 0xd6e2
+rd: 1
+qr: 1
+aa: 1
+ancount: 2
+arcount: 1
+[question]
+name: www.example.com
+rrtype: TXT
+[txt/1]
+as_rr: True
+# QNAME is fully compressed
+rr_name: ptr=12
+string: 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcde
+[txt/2]
+as_rr: True
+# QNAME is fully compressed
+rr_name: ptr=12
+string: 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0
+[tsig]
+as_rr: True
+# TSIG QNAME won't be compressed
+rr_name: www.example.com
+algorithm: hmac-md5
+time_signed: 0x4e17b38d
+mac_size: 16
+mac: 0xbe2ba477373d2496891e2fda240ee4ec
+original_id: 0xd6e2
diff --git a/src/lib/dns/tests/tsig_unittest.cc b/src/lib/dns/tests/tsig_unittest.cc
index cbb1267..7944b29 100644
--- a/src/lib/dns/tests/tsig_unittest.cc
+++ b/src/lib/dns/tests/tsig_unittest.cc
@@ -927,4 +927,76 @@ TEST_F(TSIGTest, tooShortMAC) {
}
}
+TEST_F(TSIGTest, getTSIGLength) {
+ // Check for the most common case with various algorithms
+ // See the comment in TSIGContext::getTSIGLength() for calculation and
+ // parameter notation.
+ // The key name (www.example.com) is the same for most cases, where n1=17
+
+ // hmac-md5.sig-alg.reg.int.: n2=26, x=16
+ EXPECT_EQ(85, tsig_ctx->getTSIGLength());
+
+ // hmac-sha1: n2=11, x=20
+ tsig_ctx.reset(new TSIGContext(TSIGKey(test_name, TSIGKey::HMACSHA1_NAME(),
+ &dummy_data[0], 20)));
+ EXPECT_EQ(74, tsig_ctx->getTSIGLength());
+
+ // hmac-sha256: n2=13, x=32
+ tsig_ctx.reset(new TSIGContext(TSIGKey(test_name,
+ TSIGKey::HMACSHA256_NAME(),
+ &dummy_data[0], 32)));
+ EXPECT_EQ(88, tsig_ctx->getTSIGLength());
+
+ // hmac-sha224: n2=13, x=28
+ tsig_ctx.reset(new TSIGContext(TSIGKey(test_name,
+ TSIGKey::HMACSHA224_NAME(),
+ &dummy_data[0], 28)));
+ EXPECT_EQ(84, tsig_ctx->getTSIGLength());
+
+ // hmac-sha384: n2=13, x=48
+ tsig_ctx.reset(new TSIGContext(TSIGKey(test_name,
+ TSIGKey::HMACSHA384_NAME(),
+ &dummy_data[0], 48)));
+ EXPECT_EQ(104, tsig_ctx->getTSIGLength());
+
+ // hmac-sha512: n2=13, x=64
+ tsig_ctx.reset(new TSIGContext(TSIGKey(test_name,
+ TSIGKey::HMACSHA512_NAME(),
+ &dummy_data[0], 64)));
+ EXPECT_EQ(120, tsig_ctx->getTSIGLength());
+
+ // bad key case: n1=len(badkey.example.com)=20, n2=26, x=0
+ tsig_ctx.reset(new TSIGContext(badkey_name, TSIGKey::HMACMD5_NAME(),
+ keyring));
+ EXPECT_EQ(72, tsig_ctx->getTSIGLength());
+
+ // bad sig case: n1=17, n2=26, x=0
+ isc::util::detail::gettimeFunction = testGetTime<0x4da8877a>;
+ createMessageFromFile("message_toWire2.wire");
+ tsig_ctx.reset(new TSIGContext(TSIGKey(test_name, TSIGKey::HMACMD5_NAME(),
+ &dummy_data[0],
+ dummy_data.size())));
+ {
+ SCOPED_TRACE("Verify resulting in BADSIG");
+ commonVerifyChecks(*tsig_ctx, message.getTSIGRecord(),
+ &received_data[0], received_data.size(),
+ TSIGError::BAD_SIG(), TSIGContext::RECEIVED_REQUEST);
+ }
+ EXPECT_EQ(69, tsig_ctx->getTSIGLength());
+
+ // bad time case: n1=17, n2=26, x=16, y=6
+ isc::util::detail::gettimeFunction = testGetTime<0x4da8877a - 1000>;
+ tsig_ctx.reset(new TSIGContext(TSIGKey(test_name, TSIGKey::HMACMD5_NAME(),
+ &dummy_data[0],
+ dummy_data.size())));
+ {
+ SCOPED_TRACE("Verify resulting in BADTIME");
+ commonVerifyChecks(*tsig_ctx, message.getTSIGRecord(),
+ &received_data[0], received_data.size(),
+ TSIGError::BAD_TIME(),
+ TSIGContext::RECEIVED_REQUEST);
+ }
+ EXPECT_EQ(91, tsig_ctx->getTSIGLength());
+}
+
} // end namespace
diff --git a/src/lib/dns/tsig.cc b/src/lib/dns/tsig.cc
index 714b2a5..1bda021 100644
--- a/src/lib/dns/tsig.cc
+++ b/src/lib/dns/tsig.cc
@@ -58,10 +58,32 @@ getTSIGTime() {
}
struct TSIGContext::TSIGContextImpl {
- TSIGContextImpl(const TSIGKey& key) :
- state_(INIT), key_(key), error_(Rcode::NOERROR()),
- previous_timesigned_(0)
- {}
+ TSIGContextImpl(const TSIGKey& key,
+ TSIGError error = TSIGError::NOERROR()) :
+ state_(INIT), key_(key), error_(error),
+ previous_timesigned_(0), digest_len_(0)
+ {
+ if (error == TSIGError::NOERROR()) {
+ // In normal (NOERROR) case, the key should be valid, and we
+ // should be able to pre-create a corresponding HMAC object,
+ // which will be likely to be used for sign or verify later.
+ // We do this in the constructor so that we can know the expected
+ // digest length in advance. The creation should normally succeed,
+ // but the key information could be still broken, which could
+ // trigger an exception inside the cryptolink module. We ignore
+ // it at this moment; a subsequent sign/verify operation will try
+ // to create the HMAC, which would also fail.
+ try {
+ hmac_.reset(CryptoLink::getCryptoLink().createHMAC(
+ key_.getSecret(), key_.getSecretLength(),
+ key_.getAlgorithm()),
+ deleteHMAC);
+ } catch (const Exception&) {
+ return;
+ }
+ digest_len_ = hmac_->getOutputLength();
+ }
+ }
// This helper method is used from verify(). It's expected to be called
// just before verify() returns. It updates internal state based on
@@ -85,6 +107,23 @@ struct TSIGContext::TSIGContextImpl {
return (error);
}
+ // A shortcut method to create an HMAC object for sign/verify. If one
+ // has been successfully created in the constructor, return it; otherwise
+ // create a new one and return it. In the former case, the ownership is
+ // transferred to the caller; the stored HMAC will be reset after the
+ // call.
+ HMACPtr createHMAC() {
+ if (hmac_) {
+ HMACPtr ret = HMACPtr();
+ ret.swap(hmac_);
+ return (ret);
+ }
+ return (HMACPtr(CryptoLink::getCryptoLink().createHMAC(
+ key_.getSecret(), key_.getSecretLength(),
+ key_.getAlgorithm()),
+ deleteHMAC));
+ }
+
// The following three are helper methods to compute the digest for
// TSIG sign/verify in order to unify the common code logic for sign()
// and verify() and to keep these callers concise.
@@ -111,6 +150,8 @@ struct TSIGContext::TSIGContextImpl {
vector<uint8_t> previous_digest_;
TSIGError error_;
uint64_t previous_timesigned_; // only meaningful for response with BADTIME
+ size_t digest_len_;
+ HMACPtr hmac_;
};
void
@@ -221,8 +262,7 @@ TSIGContext::TSIGContext(const Name& key_name, const Name& algorithm_name,
// be used in subsequent response with a TSIG indicating a BADKEY
// error.
impl_ = new TSIGContextImpl(TSIGKey(key_name, algorithm_name,
- NULL, 0));
- impl_->error_ = TSIGError::BAD_KEY();
+ NULL, 0), TSIGError::BAD_KEY());
} else {
impl_ = new TSIGContextImpl(*result.key);
}
@@ -232,6 +272,45 @@ TSIGContext::~TSIGContext() {
delete impl_;
}
+size_t
+TSIGContext::getTSIGLength() const {
+ //
+ // The space required for an TSIG record is:
+ //
+ // n1 bytes for the (key) name
+ // 2 bytes for the type
+ // 2 bytes for the class
+ // 4 bytes for the ttl
+ // 2 bytes for the rdlength
+ // n2 bytes for the algorithm name
+ // 6 bytes for the time signed
+ // 2 bytes for the fudge
+ // 2 bytes for the MAC size
+ // x bytes for the MAC
+ // 2 bytes for the original id
+ // 2 bytes for the error
+ // 2 bytes for the other data length
+ // y bytes for the other data (at most)
+ // ---------------------------------
+ // 26 + n1 + n2 + x + y bytes
+ //
+
+ // Normally the digest length ("x") is the length of the underlying
+ // hash output. If a key related error occurred, however, the
+ // corresponding TSIG will be "unsigned", and the digest length will be 0.
+ const size_t digest_len =
+ (impl_->error_ == TSIGError::BAD_KEY() ||
+ impl_->error_ == TSIGError::BAD_SIG()) ? 0 : impl_->digest_len_;
+
+ // Other Len ("y") is normally 0; if BAD_TIME error occurred, the
+ // subsequent TSIG will contain 48 bits of the server current time.
+ const size_t other_len = (impl_->error_ == TSIGError::BAD_TIME()) ? 6 : 0;
+
+ return (26 + impl_->key_.getKeyName().getLength() +
+ impl_->key_.getAlgorithmName().getLength() +
+ digest_len + other_len);
+}
+
TSIGContext::State
TSIGContext::getState() const {
return (impl_->state_);
@@ -276,11 +355,7 @@ TSIGContext::sign(const uint16_t qid, const void* const data,
return (tsig);
}
- HMACPtr hmac(CryptoLink::getCryptoLink().createHMAC(
- impl_->key_.getSecret(),
- impl_->key_.getSecretLength(),
- impl_->key_.getAlgorithm()),
- deleteHMAC);
+ HMACPtr hmac(impl_->createHMAC());
// If the context has previous MAC (either the Request MAC or its own
// previous MAC), digest it.
@@ -406,11 +481,7 @@ TSIGContext::verify(const TSIGRecord* const record, const void* const data,
return (impl_->postVerifyUpdate(error, NULL, 0));
}
- HMACPtr hmac(CryptoLink::getCryptoLink().createHMAC(
- impl_->key_.getSecret(),
- impl_->key_.getSecretLength(),
- impl_->key_.getAlgorithm()),
- deleteHMAC);
+ HMACPtr hmac(impl_->createHMAC());
// If the context has previous MAC (either the Request MAC or its own
// previous MAC), digest it.
diff --git a/src/lib/dns/tsig.h b/src/lib/dns/tsig.h
index bceec25..028d295 100644
--- a/src/lib/dns/tsig.h
+++ b/src/lib/dns/tsig.h
@@ -353,6 +353,27 @@ public:
TSIGError verify(const TSIGRecord* const record, const void* const data,
const size_t data_len);
+ /// Return the expected length of TSIG RR after \c sign()
+ ///
+ /// This method returns the length of the TSIG RR that would be
+ /// produced as a result of \c sign() with the state of the context
+ /// at the time of the call. The expected length can be decided
+ /// from the key and the algorithm (which determines the MAC size if
+ /// included) and the recorded TSIG error. Specifically, if a key
+ /// related error has been identified, the MAC will be excluded; if
+ /// a time error has occurred, the TSIG will include "other data".
+ ///
+ /// This method is provided mainly for the convenience of the Message
+ /// class, which needs to know the expected TSIG length in rendering a
+ /// signed DNS message so that it can handle truncated messages with TSIG
+ /// correctly. Normal applications wouldn't need this method. The Python
+ /// binding for this method won't be provided for the same reason.
+ ///
+ /// \exception None
+ ///
+ /// \return The expected TISG RR length in bytes
+ size_t getTSIGLength() const;
+
/// Return the current state of the context
///
/// \note
diff --git a/src/lib/log/Makefile.am b/src/lib/log/Makefile.am
index 63b1dfb..9f52724 100644
--- a/src/lib/log/Makefile.am
+++ b/src/lib/log/Makefile.am
@@ -20,6 +20,7 @@ liblog_la_SOURCES += logger_manager_impl.cc logger_manager_impl.h
liblog_la_SOURCES += logger_name.cc logger_name.h
liblog_la_SOURCES += logger_specification.h
liblog_la_SOURCES += logger_support.cc logger_support.h
+liblog_la_SOURCES += logger_unittest_support.cc logger_unittest_support.h
liblog_la_SOURCES += macros.h
liblog_la_SOURCES += log_messages.cc log_messages.h
liblog_la_SOURCES += message_dictionary.cc message_dictionary.h
diff --git a/src/lib/log/logger_support.cc b/src/lib/log/logger_support.cc
index 2c5ab45..2097136 100644
--- a/src/lib/log/logger_support.cc
+++ b/src/lib/log/logger_support.cc
@@ -12,28 +12,9 @@
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE
-/// \brief Temporary Logger Support
-///
-/// Performs run-time initialization of the logger system. In particular, it
-/// is passed information from the command line and:
-///
-/// a) Sets the severity of the messages being logged (and debug level if
-/// appropriate).
-/// b) Reads in the local message file is one has been supplied.
-///
-/// These functions will be replaced once the code has been written to obtain
-/// the logging parameters from the configuration database.
-
-#include <iostream>
-#include <algorithm>
-#include <iostream>
#include <string>
-
-#include <log/logger_level.h>
-#include <log/logger_manager.h>
-#include <log/logger_specification.h>
#include <log/logger_support.h>
-#include <log/output_option.h>
+#include <log/logger_manager.h>
using namespace std;
@@ -42,77 +23,6 @@ namespace {
// Flag to hold logging initialization state.
bool logging_init_state = false;
-
-// Set logging destination according to the setting of B10_LOGGER_DESTINATION.
-// (See header for initLogger() for more details.) This is a no-op if the
-// environment variable is not defined.
-//
-// \param root Name of the root logger
-// \param severity Severity level to be assigned to the root logger
-// \param dbglevel Debug level
-
-void
-setDestination(const char* root, const isc::log::Severity severity,
- const int dbglevel) {
-
- using namespace isc::log;
-
- const char* destination = getenv("B10_LOGGER_DESTINATION");
- if (destination != NULL) {
-
- // Constants: not declared static as this is function is expected to be
- // called once only
- const string STDOUT = "stdout";
- const string STDERR = "stderr";
- const string SYSLOG = "syslog";
- const string SYSLOG_COLON = "syslog:";
-
- // Prepare the objects to define the logging specification
- LoggerSpecification spec(root, severity, dbglevel);
- OutputOption option;
-
- // Set up output option according to destination specification
- const string dest = destination;
- if (dest == STDOUT) {
- option.destination = OutputOption::DEST_CONSOLE;
- option.stream = OutputOption::STR_STDOUT;
-
- } else if (dest == STDERR) {
- option.destination = OutputOption::DEST_CONSOLE;
- option.stream = OutputOption::STR_STDERR;
-
- } else if (dest == SYSLOG) {
- option.destination = OutputOption::DEST_SYSLOG;
- // Use default specified in OutputOption constructor for the
- // syslog destination
-
- } else if (dest.find(SYSLOG_COLON) == 0) {
- option.destination = OutputOption::DEST_SYSLOG;
- // Must take account of the string actually being "syslog:"
- if (dest == SYSLOG_COLON) {
- cerr << "**ERROR** value for B10_LOGGER_DESTINATION of " <<
- SYSLOG_COLON << " is invalid, " << SYSLOG <<
- " will be used instead\n";
- // Use default for logging facility
-
- } else {
- // Everything else in the string is the facility name
- option.facility = dest.substr(SYSLOG_COLON.size());
- }
-
- } else {
- // Not a recognised destination, assume a file
- option.destination = OutputOption::DEST_FILE;
- option.filename = dest;
- }
-
- // ... and set the destination
- spec.addOutputOption(option);
- LoggerManager manager;
- manager.process(spec);
- }
-}
-
} // Anonymous namespace
namespace isc {
@@ -140,63 +50,5 @@ initLogger(const string& root, isc::log::Severity severity, int dbglevel,
LoggerManager::init(root, severity, dbglevel, file);
}
-// Logger Run-Time Initialization via Environment Variables
-void initLogger(isc::log::Severity severity, int dbglevel) {
-
- // Root logger name is defined by the environment variable B10_LOGGER_ROOT.
- // If not present, the name is "bind10".
- const char* DEFAULT_ROOT = "bind10";
- const char* root = getenv("B10_LOGGER_ROOT");
- if (! root) {
- root = DEFAULT_ROOT;
- }
-
- // Set the logging severity. The environment variable is
- // B10_LOGGER_SEVERITY, and can be one of "DEBUG", "INFO", "WARN", "ERROR"
- // of "FATAL". Note that the string must be in upper case with no leading
- // of trailing blanks.
- const char* sev_char = getenv("B10_LOGGER_SEVERITY");
- if (sev_char) {
- severity = isc::log::getSeverity(sev_char);
- }
-
- // If the severity is debug, get the debug level (environment variable
- // B10_LOGGER_DBGLEVEL), which should be in the range 0 to 99.
- if (severity == isc::log::DEBUG) {
- const char* dbg_char = getenv("B10_LOGGER_DBGLEVEL");
- if (dbg_char) {
- int level = 0;
- try {
- level = boost::lexical_cast<int>(dbg_char);
- if (level < MIN_DEBUG_LEVEL) {
- cerr << "**ERROR** debug level of " << level
- << " is invalid - a value of " << MIN_DEBUG_LEVEL
- << " will be used\n";
- level = MIN_DEBUG_LEVEL;
- } else if (level > MAX_DEBUG_LEVEL) {
- cerr << "**ERROR** debug level of " << level
- << " is invalid - a value of " << MAX_DEBUG_LEVEL
- << " will be used\n";
- level = MAX_DEBUG_LEVEL;
- }
- } catch (...) {
- // Error, but not fatal to the test
- cerr << "**ERROR** Unable to translate "
- "B10_LOGGER_DBGLEVEL - a value of 0 will be used\n";
- }
- dbglevel = level;
- }
- }
-
- // Set the local message file
- const char* localfile = getenv("B10_LOGGER_LOCALMSG");
-
- // Initialize logging
- initLogger(root, severity, dbglevel, localfile);
-
- // Now set the destination for logging output
- setDestination(root, severity, dbglevel);
-}
-
} // namespace log
} // namespace isc
diff --git a/src/lib/log/logger_support.h b/src/lib/log/logger_support.h
index cf83abc..4ce3ced 100644
--- a/src/lib/log/logger_support.h
+++ b/src/lib/log/logger_support.h
@@ -19,6 +19,13 @@
#include <string>
#include <log/logger.h>
+#include <log/logger_unittest_support.h>
+
+/// \file
+/// \brief Logging initialization functions
+///
+/// Contains a set of functions relating to logging initialization that are
+/// used by the production code.
namespace isc {
namespace log {
@@ -33,17 +40,13 @@ namespace log {
/// \return true if logging has been initialized, false if not
bool isLoggingInitialized();
-/// \brief Set "logging initialized" flag
-///
-/// Sets the state of the "logging initialized" flag.
+/// \brief Set state of "logging initialized" flag
///
/// \param state State to set the flag to. (This is expected to be "true" - the
/// default - for all code apart from specific unit tests.)
void setLoggingInitialized(bool state = true);
-
-
-/// \brief Run-Time Initialization
+/// \brief Run-time initialization
///
/// Performs run-time initialization of the logger in particular supplying:
///
@@ -62,54 +65,7 @@ void initLogger(const std::string& root,
isc::log::Severity severity = isc::log::INFO,
int dbglevel = 0, const char* file = NULL);
-
-/// \brief Run-Time Initialization from Environment
-///
-/// Performs run-time initialization of the logger via the setting of
-/// environment variables. These are:
-///
-/// - B10_LOGGER_ROOT\n
-/// Name of the root logger. If not given, the string "bind10" will be used.
-///
-/// - B10_LOGGER_SEVERITY\n
-/// Severity of messages that will be logged. This must be one of the strings
-/// "DEBUG", "INFO", "WARN", "ERROR", "FATAL" or "NONE". (Must be upper case
-/// and must not contain leading or trailing spaces.) If not specified (or if
-/// specified but incorrect), the default passed as argument to this function
-/// (currently INFO) will be used.
-///
-/// - B10_LOGGER_DBGLEVEL\n
-/// Ignored if the level is not DEBUG, this should be a number between 0 and
-/// 99 indicating the logging severity. The default is 0. If outside these
-/// limits or if not a number, The value passed to this function (default
-/// of 0) is used.
-///
-/// - B10_LOGGER_LOCALMSG\n
-/// If defined, the path specification of a file that contains message
-/// definitions replacing ones in the default dictionary.
-///
-/// - B10_LOGGER_DESTINATION\n
-/// If defined, the destination of the logging output. This can be one of:
-/// - \c stdout Send output to stdout.
-/// - \c stderr Send output to stderr
-/// - \c syslog Send output to syslog using the facility local0.
-/// - \c syslog:xxx Send output to syslog, using the facility xxx. ("xxx"
-/// should be one of the syslog facilities such as "local0".) There must
-/// be a colon between "syslog" and "xxx
-/// - \c other Anything else is interpreted as the name of a file to which
-/// output is appended. If the file does not exist, it is created.
-///
-/// Any errors in the settings cause messages to be output to stderr.
-///
-/// This function is aimed at test programs, allowing the default settings to
-/// be overridden by the tester. It is not intended for use in production
-/// code.
-
-void initLogger(isc::log::Severity severity = isc::log::INFO,
- int dbglevel = 0);
-
} // namespace log
} // namespace isc
-
#endif // __LOGGER_SUPPORT_H
diff --git a/src/lib/log/logger_unittest_support.cc b/src/lib/log/logger_unittest_support.cc
new file mode 100644
index 0000000..a0969be
--- /dev/null
+++ b/src/lib/log/logger_unittest_support.cc
@@ -0,0 +1,175 @@
+// 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.
+
+#include <iostream>
+#include <algorithm>
+#include <string>
+
+#include <log/logger_level.h>
+#include <log/logger_name.h>
+#include <log/logger_manager.h>
+#include <log/logger_specification.h>
+#include <log/logger_unittest_support.h>
+#include <log/logger_support.h>
+#include <log/output_option.h>
+
+using namespace std;
+
+namespace isc {
+namespace log {
+
+// Get the logging severity. This is defined by the environment variable
+// B10_LOGGER_SEVERITY, and can be one of "DEBUG", "INFO", "WARN", "ERROR"
+// of "FATAL". (Note that the string must be in upper case with no leading
+// of trailing blanks.) If not present, the default severity passed to the
+// function is returned.
+isc::log::Severity
+b10LoggerSeverity(isc::log::Severity defseverity) {
+ const char* sev_char = getenv("B10_LOGGER_SEVERITY");
+ if (sev_char) {
+ return (isc::log::getSeverity(sev_char));
+ }
+ return (defseverity);
+}
+
+// Get the debug level. This is defined by the envornment variable
+// B10_LOGGER_DBGLEVEL. If not defined, a default value passed to the function
+// is returned.
+int
+b10LoggerDbglevel(int defdbglevel) {
+ const char* dbg_char = getenv("B10_LOGGER_DBGLEVEL");
+ if (dbg_char) {
+ int level = 0;
+ try {
+ level = boost::lexical_cast<int>(dbg_char);
+ if (level < MIN_DEBUG_LEVEL) {
+ std::cerr << "**ERROR** debug level of " << level
+ << " is invalid - a value of " << MIN_DEBUG_LEVEL
+ << " will be used\n";
+ level = MIN_DEBUG_LEVEL;
+ } else if (level > MAX_DEBUG_LEVEL) {
+ std::cerr << "**ERROR** debug level of " << level
+ << " is invalid - a value of " << MAX_DEBUG_LEVEL
+ << " will be used\n";
+ level = MAX_DEBUG_LEVEL;
+ }
+ } catch (...) {
+ // Error, but not fatal to the test
+ std::cerr << "**ERROR** Unable to translate "
+ "B10_LOGGER_DBGLEVEL - a value of 0 will be used\n";
+ }
+ return (level);
+ }
+
+ return (defdbglevel);
+}
+
+
+// Reset characteristics of the root logger to that set by the environment
+// variables B10_LOGGER_SEVERITY, B10_LOGGER_DBGLEVEL and B10_LOGGER_DESTINATION.
+
+void
+resetUnitTestRootLogger() {
+
+ using namespace isc::log;
+
+ // Constants: not declared static as this is function is expected to be
+ // called once only
+ const string DEVNULL = "/dev/null";
+ const string STDOUT = "stdout";
+ const string STDERR = "stderr";
+ const string SYSLOG = "syslog";
+ const string SYSLOG_COLON = "syslog:";
+
+ // Get the destination. If not specified, assume /dev/null. (The default
+ // severity for unit tests is DEBUG, which generates a lot of output.
+ // Routing the logging to /dev/null will suppress that, whilst still
+ // ensuring that the code paths are tested.)
+ const char* destination = getenv("B10_LOGGER_DESTINATION");
+ const string dest((destination == NULL) ? DEVNULL : destination);
+
+ // Prepare the objects to define the logging specification
+ LoggerSpecification spec(getRootLoggerName(),
+ b10LoggerSeverity(isc::log::DEBUG),
+ b10LoggerDbglevel(isc::log::MAX_DEBUG_LEVEL));
+ OutputOption option;
+
+ // Set up output option according to destination specification
+ if (dest == STDOUT) {
+ option.destination = OutputOption::DEST_CONSOLE;
+ option.stream = OutputOption::STR_STDOUT;
+
+ } else if (dest == STDERR) {
+ option.destination = OutputOption::DEST_CONSOLE;
+ option.stream = OutputOption::STR_STDERR;
+
+ } else if (dest == SYSLOG) {
+ option.destination = OutputOption::DEST_SYSLOG;
+ // Use default specified in OutputOption constructor for the
+ // syslog destination
+
+ } else if (dest.find(SYSLOG_COLON) == 0) {
+ option.destination = OutputOption::DEST_SYSLOG;
+ // Must take account of the string actually being "syslog:"
+ if (dest == SYSLOG_COLON) {
+ cerr << "**ERROR** value for B10_LOGGER_DESTINATION of " <<
+ SYSLOG_COLON << " is invalid, " << SYSLOG <<
+ " will be used instead\n";
+ // Use default for logging facility
+
+ } else {
+ // Everything else in the string is the facility name
+ option.facility = dest.substr(SYSLOG_COLON.size());
+ }
+
+ } else {
+ // Not a recognised destination, assume a file.
+ option.destination = OutputOption::DEST_FILE;
+ option.filename = dest;
+ }
+
+ // ... and set the destination
+ spec.addOutputOption(option);
+ LoggerManager manager;
+ manager.process(spec);
+}
+
+
+// Logger Run-Time Initialization via Environment Variables
+void initLogger(isc::log::Severity severity, int dbglevel) {
+
+ // Root logger name is defined by the environment variable B10_LOGGER_ROOT.
+ // If not present, the name is "bind10".
+ const char* DEFAULT_ROOT = "bind10";
+ const char* root = getenv("B10_LOGGER_ROOT");
+ if (! root) {
+ root = DEFAULT_ROOT;
+ }
+
+ // Set the local message file
+ const char* localfile = getenv("B10_LOGGER_LOCALMSG");
+
+ // Initialize logging
+ initLogger(root, isc::log::DEBUG, isc::log::MAX_DEBUG_LEVEL, localfile);
+
+ // Now set reset the output destination of the root logger, overriding
+ // the default severity, debug level and destination with those specified
+ // in the environment variables. (The two-step approach is used as the
+ // setUnitTestRootLoggerCharacteristics() function is used in several
+ // places in the BIND 10 tests, and it avoid duplicating code.)
+ resetUnitTestRootLogger();
+}
+
+} // namespace log
+} // namespace isc
diff --git a/src/lib/log/logger_unittest_support.h b/src/lib/log/logger_unittest_support.h
new file mode 100644
index 0000000..ce9121b
--- /dev/null
+++ b/src/lib/log/logger_unittest_support.h
@@ -0,0 +1,126 @@
+// 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 __LOGGER_UNITTEST_SUPPORT_H
+#define __LOGGER_UNITTEST_SUPPORT_H
+
+#include <string>
+#include <log/logger.h>
+
+/// \file
+/// \brief Miscellaneous logging functions used by the unit tests.
+///
+/// As the configuration database is unsually unavailable during unit tests,
+/// the functions defined here allow a limited amount of logging configuration
+/// through the use of environment variables
+
+namespace isc {
+namespace log {
+
+/// \brief Run-Time Initialization for Unit Tests from Environment
+///
+/// Performs run-time initialization of the logger via the setting of
+/// environment variables. These are:
+///
+/// - B10_LOGGER_ROOT\n
+/// Name of the root logger. If not given, the string "bind10" will be used.
+///
+/// - B10_LOGGER_SEVERITY\n
+/// Severity of messages that will be logged. This must be one of the strings
+/// "DEBUG", "INFO", "WARN", "ERROR", "FATAL" or "NONE". (Must be upper case
+/// and must not contain leading or trailing spaces.) If not specified (or if
+/// specified but incorrect), the default passed as argument to this function
+/// (currently DEBUG) will be used.
+///
+/// - B10_LOGGER_DBGLEVEL\n
+/// Ignored if the level is not DEBUG, this should be a number between 0 and
+/// 99 indicating the logging severity. The default is 0. If outside these
+/// limits or if not a number, The value passed to this function (default
+/// of MAX_DEBUG_LEVEL) is used.
+///
+/// - B10_LOGGER_LOCALMSG\n
+/// If defined, the path specification of a file that contains message
+/// definitions replacing ones in the default dictionary.
+///
+/// - B10_LOGGER_DESTINATION\n
+/// If defined, the destination of the logging output. This can be one of:
+/// - \c stdout Send output to stdout.
+/// - \c stderr Send output to stderr
+/// - \c syslog Send output to syslog using the facility local0.
+/// - \c syslog:xxx Send output to syslog, using the facility xxx. ("xxx"
+/// should be one of the syslog facilities such as "local0".) There must
+/// be a colon between "syslog" and "xxx
+/// - \c other Anything else is interpreted as the name of a file to which
+/// output is appended. If the file does not exist, it is created.
+///
+/// Any errors in the settings cause messages to be output to stderr.
+///
+/// This function is aimed at test programs, allowing the default settings to
+/// be overridden by the tester. It is not intended for use in production
+/// code.
+///
+/// TODO: Rename. This function overloads the initLogger() function that can
+/// be used to initialize production programs. This may lead to confusion.
+void initLogger(isc::log::Severity severity = isc::log::DEBUG,
+ int dbglevel = isc::log::MAX_DEBUG_LEVEL);
+
+
+/// \brief Obtains logging severity from B10_LOGGER_SEVERITY
+///
+/// Support function called by the unit test logging initialization code.
+/// It returns the logging severity defined by B10_LOGGER_SEVERITY. If
+/// not defined it returns the default passed to it.
+///
+/// \param defseverity Default severity used if B10_LOGGER_SEVERITY is not
+// defined.
+///
+/// \return Severity to use for the logging.
+isc::log::Severity b10LoggerSeverity(isc::log::Severity defseverity);
+
+
+/// \brief Obtains logging debug level from B10_LOGGER_DBGLEVEL
+///
+/// Support function called by the unit test logging initialization code.
+/// It returns the logging debug level defined by B10_LOGGER_DBGLEVEL. If
+/// not defined, it returns the default passed to it.
+///
+/// N.B. If there is an error, a message is written to stderr and a value
+/// related to the error is used. (This is because (a) logging is not yet
+/// initialized, hence only the error stream is known to exist, and (b) this
+/// function is only used in unit test logging initialization, so incorrect
+/// selection of a level is not really an issue.)
+///
+/// \param defdbglevel Default debug level to be used if B10_LOGGER_DBGLEVEL
+/// is not defined.
+///
+/// \return Debug level to use.
+int b10LoggerDbglevel(int defdbglevel);
+
+
+/// \brief Reset root logger characteristics
+///
+/// This is a simplified interface into the resetting of the characteristics
+/// of the root logger. It is aimed for use in unit tests and resets the
+/// characteristics of the root logger to use a severity, debug level and
+/// destination set by the environment variables B10_LOGGER_SEVERITY,
+/// B10_LOGGER_DBGLEVEL and B10_LOGGER_DESTINATION.
+void
+resetUnitTestRootLogger();
+
+} // namespace log
+} // namespace isc
+
+
+
+#endif // __LOGGER_UNITTEST_SUPPORT_H
diff --git a/src/lib/log/tests/init_logger_test.sh.in b/src/lib/log/tests/init_logger_test.sh.in
index d26ca5d..795419b 100755
--- a/src/lib/log/tests/init_logger_test.sh.in
+++ b/src/lib/log/tests/init_logger_test.sh.in
@@ -44,7 +44,7 @@ WARN [bind10.log] LOG_BAD_STREAM bad log console output stream: warn
ERROR [bind10.log] LOG_DUPLICATE_MESSAGE_ID duplicate message ID (error) in compiled code
FATAL [bind10.log] LOG_NO_MESSAGE_ID line fatal: message definition line found without a message ID
.
-B10_LOGGER_SEVERITY=DEBUG B10_LOGGER_DBGLEVEL=99 ./init_logger_test 2>&1 | \
+B10_LOGGER_DESTINATION=stdout B10_LOGGER_SEVERITY=DEBUG B10_LOGGER_DBGLEVEL=99 ./init_logger_test | \
cut -d' ' -f3- | diff $tempfile -
passfail $?
@@ -57,7 +57,7 @@ WARN [bind10.log] LOG_BAD_STREAM bad log console output stream: warn
ERROR [bind10.log] LOG_DUPLICATE_MESSAGE_ID duplicate message ID (error) in compiled code
FATAL [bind10.log] LOG_NO_MESSAGE_ID line fatal: message definition line found without a message ID
.
-B10_LOGGER_SEVERITY=DEBUG B10_LOGGER_DBGLEVEL=50 ./init_logger_test 2>&1 | \
+B10_LOGGER_DESTINATION=stdout B10_LOGGER_SEVERITY=DEBUG B10_LOGGER_DBGLEVEL=50 ./init_logger_test | \
cut -d' ' -f3- | diff $tempfile -
passfail $?
@@ -67,7 +67,7 @@ WARN [bind10.log] LOG_BAD_STREAM bad log console output stream: warn
ERROR [bind10.log] LOG_DUPLICATE_MESSAGE_ID duplicate message ID (error) in compiled code
FATAL [bind10.log] LOG_NO_MESSAGE_ID line fatal: message definition line found without a message ID
.
-B10_LOGGER_SEVERITY=WARN ./init_logger_test 2>&1 | \
+B10_LOGGER_DESTINATION=stdout B10_LOGGER_SEVERITY=WARN ./init_logger_test | \
cut -d' ' -f3- | diff $tempfile -
passfail $?
diff --git a/src/lib/log/tests/logger_level_impl_unittest.cc b/src/lib/log/tests/logger_level_impl_unittest.cc
index 0ded7f9..dacd202 100644
--- a/src/lib/log/tests/logger_level_impl_unittest.cc
+++ b/src/lib/log/tests/logger_level_impl_unittest.cc
@@ -20,6 +20,7 @@
#include <boost/lexical_cast.hpp>
#include <log/logger_level_impl.h>
+#include <log/logger_support.h>
#include <log4cplus/logger.h>
using namespace isc::log;
@@ -27,8 +28,10 @@ using namespace std;
class LoggerLevelImplTest : public ::testing::Test {
protected:
- LoggerLevelImplTest()
- {}
+ LoggerLevelImplTest() {
+ // Ensure logging set to default for unit tests
+ resetUnitTestRootLogger();
+ }
~LoggerLevelImplTest()
{}
diff --git a/src/lib/log/tests/logger_level_unittest.cc b/src/lib/log/tests/logger_level_unittest.cc
index 8c98091..641a6cc 100644
--- a/src/lib/log/tests/logger_level_unittest.cc
+++ b/src/lib/log/tests/logger_level_unittest.cc
@@ -20,7 +20,7 @@
#include <log/logger.h>
#include <log/logger_manager.h>
#include <log/log_messages.h>
-#include <log/logger_name.h>
+#include <log/logger_support.h>
using namespace isc;
using namespace isc::log;
@@ -29,7 +29,9 @@ using namespace std;
class LoggerLevelTest : public ::testing::Test {
protected:
LoggerLevelTest() {
- // Logger initialization is done in main()
+ // Logger initialization is done in main(). As logging tests may
+ // alter the default logging output, it is reset here.
+ resetUnitTestRootLogger();
}
~LoggerLevelTest() {
LoggerManager::reset();
@@ -57,7 +59,7 @@ TEST_F(LoggerLevelTest, Creation) {
EXPECT_EQ(42, level3.dbglevel);
}
-TEST(LoggerLevel, getSeverity) {
+TEST_F(LoggerLevelTest, getSeverity) {
EXPECT_EQ(DEBUG, getSeverity("DEBUG"));
EXPECT_EQ(DEBUG, getSeverity("debug"));
EXPECT_EQ(DEBUG, getSeverity("DeBuG"));
diff --git a/src/lib/log/tests/logger_support_unittest.cc b/src/lib/log/tests/logger_support_unittest.cc
index 6a93652..b418906 100644
--- a/src/lib/log/tests/logger_support_unittest.cc
+++ b/src/lib/log/tests/logger_support_unittest.cc
@@ -18,12 +18,23 @@
using namespace isc::log;
+class LoggerSupportTest : public ::testing::Test {
+protected:
+ LoggerSupportTest() {
+ // Logger initialization is done in main(). As logging tests may
+ // alter the default logging output, it is reset here.
+ resetUnitTestRootLogger();
+ }
+ ~LoggerSupportTest() {
+ }
+};
+
// Check that the initialized flag can be manipulated. This is a bit chicken-
// -and-egg: we want to reset to the flag to the original value at the end
// of the test, so use the functions to do that. But we are trying to check
// that these functions in fact work.
-TEST(LoggerSupportTest, InitializedFlag) {
+TEST_F(LoggerSupportTest, InitializedFlag) {
bool current_flag = isLoggingInitialized();
// check we can flip the flag.
@@ -51,7 +62,7 @@ TEST(LoggerSupportTest, InitializedFlag) {
// Check that a logger will throw an exception if logging has not been
// initialized.
-TEST(LoggerSupportTest, LoggingInitializationCheck) {
+TEST_F(LoggerSupportTest, LoggingInitializationCheck) {
// Assert that logging has been initialized (it should be in main()).
bool current_flag = isLoggingInitialized();
diff --git a/src/lib/python/isc/log/log.cc b/src/lib/python/isc/log/log.cc
index 484151f..aa12664 100644
--- a/src/lib/python/isc/log/log.cc
+++ b/src/lib/python/isc/log/log.cc
@@ -20,6 +20,7 @@
#include <log/message_dictionary.h>
#include <log/logger_manager.h>
+#include <log/logger_support.h>
#include <log/logger.h>
#include <config/ccsession.h>
@@ -35,7 +36,7 @@ using boost::bind;
// (tags/RELEASE_28 115909)) on OSX, where unwinding the stack
// segfaults the moment this exception was thrown and caught.
//
-// Placing it in a named namespace instead of the original
+// Placing it in a named namespace instead of the originalRecommend
// unnamed namespace appears to solve this, so as a temporary
// workaround, we create a local randomly named namespace here
// to solve this issue.
@@ -184,6 +185,27 @@ init(PyObject*, PyObject* args) {
Py_RETURN_NONE;
}
+// This initialization is for unit tests. It allows message settings to be
+// be determined by a set of B10_xxx environment variables. (See the
+// description of initLogger() for more details.) The function has been named
+// resetUnitTestRootLogger() here as being more descriptive and
+// trying to avoid confusion.
+PyObject*
+resetUnitTestRootLogger(PyObject*, PyObject*) {
+ try {
+ isc::log::resetUnitTestRootLogger();
+ }
+ catch (const std::exception& e) {
+ PyErr_SetString(PyExc_RuntimeError, e.what());
+ return (NULL);
+ }
+ catch (...) {
+ PyErr_SetString(PyExc_RuntimeError, "Unknown C++ exception");
+ return (NULL);
+ }
+ Py_RETURN_NONE;
+}
+
PyObject*
logConfigUpdate(PyObject*, PyObject* args) {
// we have no wrappers for ElementPtr and ConfigData,
@@ -246,6 +268,12 @@ PyMethodDef methods[] = {
"logging severity (one of 'DEBUG', 'INFO', 'WARN', 'ERROR' or "
"'FATAL'), a debug level (integer in the range 0-99) and a file name "
"of a dictionary with message text translations."},
+ {"resetUnitTestRootLogger", resetUnitTestRootLogger, METH_VARARGS,
+ "Resets the configuration of the root logger to that set by the "
+ "B10_XXX environment variables. It is aimed at unit tests, where "
+ "the logging is initialized by the code under test; called before "
+ "the unit test starts, this function resets the logging configuration "
+ "to that in use for the C++ unit tests."},
{"log_config_update", logConfigUpdate, METH_VARARGS,
"Update logger settings. This method is automatically used when "
"ModuleCCSession is initialized with handle_logging_config set "
More information about the bind10-changes
mailing list