BIND 10 trac744, updated. d65686925bbd04cbbfa3ca2705f54696b76beb45 [trac744] Update message file format
BIND 10 source code commits
bind10-changes at lists.isc.org
Mon May 9 17:44:42 UTC 2011
The branch, trac744 has been updated
via d65686925bbd04cbbfa3ca2705f54696b76beb45 (commit)
via 4f6b51bc00d3f37b8938951217cd1f3564bedcd8 (commit)
via b395258c708b49a5da8d0cffcb48d83294354ba3 (commit)
via 569b2b6e5d84277316bce84c0e118ffd116ffe5e (commit)
via e5687a75c06966ab68ad453093fb6c7b9d8fe761 (commit)
via 119678f768f09b13d6ecb715ad731d1dca8f05b0 (commit)
via 4903410e45670b30d7283f5d69dc28c2069237d6 (commit)
via 27a4c936d87d31952f6a8377e04cae1fb90edffd (commit)
via 9eb7b41d5253c44e59987170a74e345b651c96bc (commit)
via 4ff5a9f61d9ad1c96468556771562f1dbad7ec98 (commit)
via c16b3ab523860ccca5e8e9648b58801c3eefae29 (commit)
via 8bfccfb1fead0448e959126e07dad1a800880575 (commit)
via 9a8870c515be763e8be63cdc28231883601867d4 (commit)
via fb1d629419b6c58b9c9f410e6c027c8769e0c9a3 (commit)
via 0709f8b56df68cfe0c79ed98bfe7e9dd25ce0e1e (commit)
via 40e06e655ce78197f0b52d8c5dc9c7516795362f (commit)
via 434719dc9b7eeee0d0901fa6f5e1847bc40691c2 (commit)
via 0ecb807011196eac01f281d40bc7c9d44565b364 (commit)
via fa321eca22c746101dd9cf67c7bc9da6de30483a (commit)
via ee2664ecf4fda60a78e97d39c4f69b4f22d5aa24 (commit)
via fb283c1a0e6f8cdacc9c326f00caef141b058f1f (commit)
via 4c178871eba133fbbf56c2dac1dde1a28ad20477 (commit)
via ffa2f0672084c1f16e5784cdcdd55822f119feaa (commit)
via 9a2ca8e5de8a23018ebb0ac7fee819c065542ff1 (commit)
via 284c8b457d8a8940265da1e4d0c7b1050806afbb (commit)
via 7c54055c0e47c7a0e36fcfab4b47ff180c0ca8c8 (commit)
via 5c497dfce7d23487c2aefe9907f942c39c4c5846 (commit)
via 83717978e3941e4eb5d7dc90d26a88256efd3175 (commit)
via df0c3599c1f3ad957df20eb8cc04c33d1c958e4a (commit)
via 40870d717a8017379cdb68db9ed15ca71faddd5f (commit)
via d846851699d5c76937533adf9ff9d948dfd593ca (commit)
via 372a2f033edf044d3f515f3b5009d0c8fce3ce16 (commit)
via 89fcb0f002447e2cc3148e212f1ecd4f6d1bd821 (commit)
via a437d4e26b81bb07181ff35a625c540703eee845 (commit)
via 0338dd8c3ef74395235bc37533269fb252142263 (commit)
via 28c50de2e9ef07250b54a3d99850ee353f511ffe (commit)
via 297955d6928e3202c25d0f1fa17708c032c46a40 (commit)
via 2979d921553eaaf59cfbe6b78bd726e09006ca9e (commit)
via 7f0a7a5ff8d2452c4b48575a725f97e5981aff66 (commit)
via 25bc145bb597683411ece6ad7cb72fc0fabedeae (commit)
via 4a2c0e7a8852496e9b8f9a56f5058cb1f55d3f55 (commit)
via 25301e86fb8aa4cb8d98933a0c3b7dcf46f8c4be (commit)
via bb568b0fbb855dcf79681de5024c09b91251ccda (commit)
via f1dfe74d6b9b2b8c4dc85dd39a43084dc2b73aa9 (commit)
via 6daf339b7a83843a12d4938424063d1b96929924 (commit)
via 3ce5e0a2b96c84ae003ea372bb84ebb19ca355e8 (commit)
via 9e8b5ec4233217fba5c7e9b17afd6c01e4fc7b79 (commit)
via cca71e70d0667d066ba5ab63c65b21d83cbb896c (commit)
via 2d514a764119f29d3e2fbb04366d880df1866928 (commit)
via e22700fc2021180a0dab3f312df5fdabe9bc47e2 (commit)
via 98d147ea68fc1e62b6e7e3bc74c80e9457b5249f (commit)
via 5a4d69114ef60bbb86c4fc2190b78761ac4ccc3f (commit)
via 776f5ebd79615b0c05f2009c09bf61c44c59343a (commit)
via 314e299226288d66efe5e65d8e00fc041b688f35 (commit)
via 0e3b55edc75e91ea11df51ecffae0ae4ff07fdac (commit)
via 0261bbec3a5cd8770aee567aaebab2a4f93c2303 (commit)
via e997ff18a6bf1eeb23391e0957db0defdecd0213 (commit)
via 31a6f34026c83594bd9f205df18eb290114d9ea1 (commit)
via 8320ec6eb230fe761ae4769367f10383e967655c (commit)
via b8422db8e0120d3998b15581dddcbfe4efe91a68 (commit)
via da16cc776443e1414f779d7402996c6949934765 (commit)
via d6db622c5649e745fb3c64218e36c41545c47aed (commit)
via cce60befee1e0c25e20449b9242112cc0a777089 (commit)
via 89dd4548179485bdf76a84d289ab88ba65bd07bc (commit)
via 364be72653933235d362a57c9342bae1c6e17b3a (commit)
via 28143beb0e57bbd4d62432b81edc8e179b95959f (commit)
via 76705c8fc54809ece24609deda2d156a844d2bf5 (commit)
via a42b39784b65894e7dc164f1b7f2f887715f0c45 (commit)
via 29170e28d19f35a874d84b88f7c0873c51e28f89 (commit)
via 53d9f46923a22920c04d5bc4fc706ec202686b1b (commit)
via 2842dda9275c1a471d70d3a571d810c8d715bf3a (commit)
via daa24430a497d145849a687f51252bfed16a2caf (commit)
via cc788af2c45043fbb98365d5fb85c1d16307dda2 (commit)
via 21385dbdb72fe02275c859a88674a9d2166a0ed0 (commit)
via 158af09a0032d47503a9e52102bc495b8d5bba46 (commit)
via e9f3843c953ce9a3e05c3dcc4b8df2a67a9d9e6d (commit)
via 9f729b786ad2d12fde55cbaad5de72918fbe209e (commit)
from 80c0a2c3eec3ac5d3ac862f9b4c5926f0eece6ed (commit)
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 d65686925bbd04cbbfa3ca2705f54696b76beb45
Author: Michal 'vorner' Vaner <michal.vaner at nic.cz>
Date: Mon May 9 19:42:17 2011 +0200
[trac744] Update message file format
commit 4f6b51bc00d3f37b8938951217cd1f3564bedcd8
Merge: 80c0a2c3eec3ac5d3ac862f9b4c5926f0eece6ed b395258c708b49a5da8d0cffcb48d83294354ba3
Author: Michal 'vorner' Vaner <michal.vaner at nic.cz>
Date: Mon May 9 19:18:58 2011 +0200
Merge remote-tracking branch 'origin/trac900' into work/log/datasrc
Conflicts:
src/lib/log/log_formatter.h
Not updated the message file yet, only merged.
-----------------------------------------------------------------------
Summary of changes:
ChangeLog | 64 +-
configure.ac | 6 -
src/bin/auth/statistics.cc | 4 -
src/bin/host/Makefile.am | 31 +-
src/bin/host/README | 16 +-
src/bin/host/b10-host.1 | 122 +++
src/bin/host/b10-host.xml | 201 ++++
src/bin/host/host.cc | 73 ++-
src/bin/msgq/tests/msgq_test.py | 2 +-
src/bin/stats/run_b10-stats-httpd.sh.in | 33 -
src/bin/stats/run_b10-stats.sh.in | 33 -
src/bin/stats/stats.py.in | 3 -
src/bin/stats/tests/stats_test.in | 31 -
src/lib/asiodns/Makefile.am | 6 +-
src/lib/asiodns/asiodef.mes | 56 ++
src/lib/asiodns/asiodef.msg | 56 --
src/lib/asiodns/io_fetch.cc | 20 +-
src/lib/datasrc/messagedef.mes | 962 ++++++++++----------
src/lib/dns/Makefile.am | 1 +
src/lib/dns/edns.cc | 29 +-
src/lib/dns/edns.h | 10 +-
src/lib/dns/message.cc | 419 ++++++----
src/lib/dns/message.h | 55 ++-
src/lib/dns/python/Makefile.am | 1 +
src/lib/dns/python/message_python.cc | 123 ++--
src/lib/dns/python/pydnspp.cc | 5 +
src/lib/dns/python/tests/Makefile.am | 1 +
src/lib/dns/python/tests/message_python_test.py | 37 +-
.../dns/python/tests/tsig_python_test.py} | 22 +-
src/lib/dns/python/tsig_python.cc | 157 ++++
src/lib/dns/question.cc | 2 +-
src/lib/dns/question.h | 6 +-
src/lib/dns/rdata/any_255/tsig_250.cc | 12 +-
src/lib/dns/rrclass-placeholder.h | 4 +-
src/lib/dns/rrclass.cc | 2 +-
src/lib/dns/rrset.cc | 6 +-
src/lib/dns/rrset.h | 6 +-
src/lib/dns/rrttl.cc | 2 +-
src/lib/dns/rrttl.h | 4 +-
src/lib/dns/tests/Makefile.am | 1 +
src/lib/dns/tests/message_unittest.cc | 192 ++++-
src/lib/dns/tests/run_unittests.cc | 3 +
src/lib/dns/tests/testdata/Makefile.am | 21 +-
src/lib/dns/tests/testdata/gen-wiredata.py.in | 92 ++-
src/lib/dns/tests/testdata/message_fromWire12.spec | 21 +
src/lib/dns/tests/testdata/message_fromWire13.spec | 20 +
src/lib/dns/tests/testdata/message_fromWire14.spec | 21 +
src/lib/dns/tests/testdata/message_fromWire15.spec | 22 +
src/lib/dns/tests/testdata/message_fromWire16.spec | 21 +
src/lib/dns/tests/testdata/message_toText1.spec | 24 +
src/lib/dns/tests/testdata/message_toText1.txt | 14 +
src/lib/dns/tests/testdata/message_toText2.spec | 14 +
src/lib/dns/tests/testdata/message_toText2.txt | 8 +
src/lib/dns/tests/testdata/message_toText3.spec | 31 +
src/lib/dns/tests/testdata/message_toText3.txt | 17 +
src/lib/dns/tests/testdata/message_toWire2.spec | 21 +
src/lib/dns/tests/testdata/message_toWire3.spec | 22 +
src/lib/dns/tests/testdata/tsigrecord_toWire1.spec | 16 +
src/lib/dns/tests/testdata/tsigrecord_toWire2.spec | 19 +
src/lib/dns/tests/tsig_unittest.cc | 30 +-
src/lib/dns/tests/tsigrecord_unittest.cc | 160 ++++
src/lib/dns/tsig.cc | 33 +-
src/lib/dns/tsig.h | 72 +--
src/lib/dns/tsigrecord.cc | 145 +++
src/lib/dns/tsigrecord.h | 285 ++++++
src/lib/log/Makefile.am | 2 +-
src/lib/log/README | 115 ++-
src/lib/log/compiler/message.cc | 36 +-
src/lib/log/log_formatter.cc | 5 +-
src/lib/log/log_formatter.h | 103 +--
src/lib/log/logger.cc | 20 +-
src/lib/log/logger_impl.cc | 1 +
src/lib/log/logger_support.cc | 7 +-
src/lib/log/macros.h | 2 +
src/lib/log/message_exception.cc | 26 -
src/lib/log/message_exception.h | 37 +-
src/lib/log/message_reader.cc | 182 +++--
src/lib/log/message_reader.h | 18 +-
src/lib/log/messagedef.cc | 70 +-
src/lib/log/messagedef.h | 14 +-
src/lib/log/messagedef.mes | 202 ++--
src/lib/log/tests/log_formatter_unittest.cc | 49 +-
src/lib/log/tests/logger_support_test.cc | 17 +-
src/lib/log/tests/message_dictionary_unittest.cc | 4 +-
src/lib/log/tests/message_reader_unittest.cc | 47 +-
src/lib/log/tests/run_time_init_test.sh.in | 41 +-
src/lib/util/encode/base_n.cc | 2 +-
src/lib/util/time_utilities.cc | 7 +-
src/lib/util/time_utilities.h | 28 +
src/lib/util/unittests/Makefile.am | 2 +
src/lib/util/unittests/{newhook.cc => testdata.cc} | 60 +-
src/lib/util/unittests/testdata.h | 54 ++
src/lib/util/unittests/textdata.h | 103 +++
93 files changed, 3588 insertions(+), 1614 deletions(-)
create mode 100644 src/bin/host/b10-host.1
create mode 100644 src/bin/host/b10-host.xml
delete mode 100755 src/bin/stats/run_b10-stats-httpd.sh.in
delete mode 100755 src/bin/stats/run_b10-stats.sh.in
delete mode 100755 src/bin/stats/tests/stats_test.in
create mode 100644 src/lib/asiodns/asiodef.mes
delete mode 100644 src/lib/asiodns/asiodef.msg
copy src/{bin/cmdctl/run_b10-cmdctl.sh.in => lib/dns/python/tests/tsig_python_test.py} (65%)
create mode 100644 src/lib/dns/python/tsig_python.cc
create mode 100644 src/lib/dns/tests/testdata/message_fromWire12.spec
create mode 100644 src/lib/dns/tests/testdata/message_fromWire13.spec
create mode 100644 src/lib/dns/tests/testdata/message_fromWire14.spec
create mode 100644 src/lib/dns/tests/testdata/message_fromWire15.spec
create mode 100644 src/lib/dns/tests/testdata/message_fromWire16.spec
create mode 100644 src/lib/dns/tests/testdata/message_toText1.spec
create mode 100644 src/lib/dns/tests/testdata/message_toText1.txt
create mode 100644 src/lib/dns/tests/testdata/message_toText2.spec
create mode 100644 src/lib/dns/tests/testdata/message_toText2.txt
create mode 100644 src/lib/dns/tests/testdata/message_toText3.spec
create mode 100644 src/lib/dns/tests/testdata/message_toText3.txt
create mode 100644 src/lib/dns/tests/testdata/message_toWire2.spec
create mode 100644 src/lib/dns/tests/testdata/message_toWire3.spec
create mode 100644 src/lib/dns/tests/testdata/tsigrecord_toWire1.spec
create mode 100644 src/lib/dns/tests/testdata/tsigrecord_toWire2.spec
create mode 100644 src/lib/dns/tests/tsigrecord_unittest.cc
create mode 100644 src/lib/dns/tsigrecord.cc
create mode 100644 src/lib/dns/tsigrecord.h
delete mode 100644 src/lib/log/message_exception.cc
copy src/lib/util/unittests/{newhook.cc => testdata.cc} (51%)
create mode 100644 src/lib/util/unittests/testdata.h
create mode 100644 src/lib/util/unittests/textdata.h
-----------------------------------------------------------------------
diff --git a/ChangeLog b/ChangeLog
index ae1b5e8..d5de903 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,30 @@
+231. [func]*
+ The logging interface changed slightly. We use
+ logger.foo(MESSAGE_ID).arg(bar); instead of logger.foo(MESSAGE_ID, bar);
+ internally. The message definitions use '%1,%2,...' instead of '%s,%d',
+ which allows us to cope better with mismatched placeholders and allows
+ reordering of them in case of translation.
+ (Trac901, git 4903410e45670b30d7283f5d69dc28c2069237d6)
+
+230. [bug] naokikambe
+ Removed too repeated verbose messages in two cases of:
+ - when auth sends statistics data to stats
+ - when stats receives statistics data from other modules
+ (Trac#620, git 0ecb807011196eac01f281d40bc7c9d44565b364)
+
+229. [doc] jreed
+ Add manual page for b10-host.
+ (git a437d4e26b81bb07181ff35a625c540703eee845)
+
+228. [func]* jreed
+ The host tool is renamed to b10-host. While the utility is
+ a work in progress, it is expected to now be shipped with
+ tarballs. Its initial goal was to be a host(1) clone,
+ rewritten in C++ from scratch and using BIND 10's libdns++.
+ It now supports the -a (any), -c class, -d (verbose) switches
+ and has improved output.
+ (Trac #872, git d846851699d5c76937533adf9ff9d948dfd593ca)
+
227. [build] jreed
Add missing libdns++ rdata files for the distribution (this
fixes distcheck error). Change three generated libdns++
@@ -10,17 +37,19 @@
implementation uses Botan as the backend library.
This introduces a new dependency, on Botan. Currently only Botan
1.8.x works; older or newer versions don't.
- (Trac#781, git 9df42279a47eb617f586144dce8cce680598558a)
+ (Trac #781, git 9df42279a47eb617f586144dce8cce680598558a)
225. [func] naokikambe
- Added the HTTP/XML interface(b10-stats-httpd) to the statistics feature
- in BIND 10. b10-stats-httpd is a standalone HTTP server and it requests
- statistics data to the stats daemon(b10-stats) and sends it to HTTP
- clients in XML format. Items of the data collected via b10-stats-httpd
- are almost equivalent to ones which are collected via bindctl. Since it
- also can send XSL(Extensible Stylessheet Language) document and XSD(XML
- Schema definition) document, XML document is human-friendly to view
- through web browsers and its data types are strictly defined.
+ Added the HTTP/XML interface(b10-stats-httpd) to the
+ statistics feature in BIND 10. b10-stats-httpd is a standalone
+ HTTP server and it requests statistics data to the stats
+ daemon(b10-stats) and sends it to HTTP clients in XML
+ format. Items of the data collected via b10-stats-httpd
+ are almost equivalent to ones which are collected via
+ bindctl. Since it also can send XSL(Extensible Stylessheet
+ Language) document and XSD(XML Schema definition) document,
+ XML document is human-friendly to view through web browsers
+ and its data types are strictly defined.
(Trac #547, git 1cbd51919237a6e65983be46e4f5a63d1877b1d3)
224. [bug] jinmei
@@ -32,11 +61,12 @@
(Trac #851, git 2463b96680bb3e9a76e50c38a4d7f1d38d810643)
223. [bug] feng
- If ip address or port isn't usable for name server, name server process
- won't exist and give end user chance to reconfigure them.
+ If ip address or port isn't usable for name server, name
+ server process won't exist and give end user chance to
+ reconfigure them.
(Trac #775, git 572ac2cf62e18f7eb69d670b890e2a3443bfd6e7)
-222. [bug] jerry
+222. [bug] jerry
src/lib/zonemgr: Fix a bug that xfrin not checking for new copy of
zone on startup. Imposes some random jitters to avoid many zones
need to do refresh at the same time.
@@ -51,7 +81,7 @@
(potentially) bad packets to a nameserver and prints the responses.
(Trac #703, git 1b666838b6c0fe265522b30971e878d9f0d21fde)
-219. [func] ocean
+219. [func] ocean
src/lib: move some dns related code out of asiolink library to
asiodns library
(Trac #751, git 262ac6c6fc61224d54705ed4c700dadb606fcb1c)
@@ -61,9 +91,9 @@
(Trac #806, git 4e47d5f6b692c63c907af6681a75024450884a88)
217. [bug] jerry
- src/lib/dns/python: Use a signed version of larger size of integer and
- perform more strict range checks with PyArg_ParseTuple() in case of
- overflows.
+ src/lib/dns/python: Use a signed version of larger size of
+ integer and perform more strict range checks with
+ PyArg_ParseTuple() in case of overflows.
(Trac #363, git ce281e646be9f0f273229d94ccd75bf7e08d17cf)
216. [func] vorner
@@ -1085,7 +1115,7 @@ bind10-devel-20100701 released on July 1, 2010
55. [bug] shane
bin/xfrout: xfrout exception on Ctrl-C now no longer generates
exception for 'Interrupted system call'
- (Track #136, svn r2147)
+ (Trac #136, svn r2147)
54. [bug] zhanglikun
bin/xfrout: Enable b10-xfrout can be launched in source
diff --git a/configure.ac b/configure.ac
index acd8c93..d76500d 100644
--- a/configure.ac
+++ b/configure.ac
@@ -811,9 +811,6 @@ AC_OUTPUT([doc/version.ent
src/bin/stats/stats-httpd-xml.tpl
src/bin/stats/stats-httpd-xsd.tpl
src/bin/stats/stats-httpd-xsl.tpl
- src/bin/stats/run_b10-stats.sh
- src/bin/stats/run_b10-stats-httpd.sh
- src/bin/stats/tests/stats_test
src/bin/bind10/bind10.py
src/bin/bind10/run_bind10.sh
src/bin/bind10/tests/bind10_test.py
@@ -852,9 +849,6 @@ AC_OUTPUT([doc/version.ent
chmod +x src/bin/xfrin/run_b10-xfrin.sh
chmod +x src/bin/xfrout/run_b10-xfrout.sh
chmod +x src/bin/zonemgr/run_b10-zonemgr.sh
- chmod +x src/bin/stats/tests/stats_test
- chmod +x src/bin/stats/run_b10-stats.sh
- chmod +x src/bin/stats/run_b10-stats-httpd.sh
chmod +x src/bin/bind10/run_bind10.sh
chmod +x src/bin/cmdctl/tests/cmdctl_test
chmod +x src/bin/xfrin/tests/xfrin_test
diff --git a/src/bin/auth/statistics.cc b/src/bin/auth/statistics.cc
index e68793c..415aa14 100644
--- a/src/bin/auth/statistics.cc
+++ b/src/bin/auth/statistics.cc
@@ -91,10 +91,6 @@ AuthCountersImpl::submitStatistics() const {
const int seq =
statistics_session_->group_sendmsg(statistics_element, "Stats");
isc::data::ConstElementPtr env, answer;
- if (verbose_mode_) {
- std::cerr << "[b10-auth] "
- << "send statistics data" << std::endl;
- }
// TODO: parse and check response from statistics module
// currently it just returns empty message
statistics_session_->group_recvmsg(env, answer, false, seq);
diff --git a/src/bin/host/Makefile.am b/src/bin/host/Makefile.am
index 0758cb9..ec34ce7 100644
--- a/src/bin/host/Makefile.am
+++ b/src/bin/host/Makefile.am
@@ -10,17 +10,20 @@ endif
CLEANFILES = *.gcno *.gcda
-bin_PROGRAMS = host
-host_SOURCES = host.cc
-host_LDADD = $(top_builddir)/src/lib/dns/libdns++.la
-host_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
-
-#man_MANS = host.1
-#EXTRA_DIST = $(man_MANS) host.xml
-#
-#if ENABLE_MAN
-#
-#host.1: host.xml
-# xsltproc --novalid --xinclude --nonet -o $@ http://docbook.sourceforge.net/release/xsl/current/manpages/docbook.xsl $(srcdir)/host.xml
-#
-#endif
+bin_PROGRAMS = b10-host
+b10_host_SOURCES = host.cc
+b10_host_LDADD = $(top_builddir)/src/lib/dns/libdns++.la
+b10_host_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
+
+man_MANS = b10-host.1
+EXTRA_DIST = $(man_MANS) b10-host.xml
+
+.PHONY: man
+if ENABLE_MAN
+
+man: b10-host.1
+
+b10-host.1: b10-host.xml
+ xsltproc --novalid --xinclude --nonet -o $@ http://docbook.sourceforge.net/release/xsl/current/manpages/docbook.xsl $(srcdir)/b10-host.xml
+
+endif
diff --git a/src/bin/host/README b/src/bin/host/README
index d493a95..5cc4068 100644
--- a/src/bin/host/README
+++ b/src/bin/host/README
@@ -1,14 +1,4 @@
-Rewriting host(1) in C++ from scratch using BIND 10's libdns.
+Rewriting host(1) in C++ from scratch using BIND 10's libdns++.
-Initial functionality:
-
- host _hostname_ [server]
-
-By default, it looks up the A, AAAA, and MX record sets.
-
-Note it doesn't use /etc/resolv.conf at this time.
-The default name server used is 127.0.0.1.
-
- -r disable recursive processing
- -t _type_ specific query type
- -v enable verbose output mode, including elapsed time
+The bugs and incompatibilities are listed in the manual page
+and in the source code.
diff --git a/src/bin/host/b10-host.1 b/src/bin/host/b10-host.1
new file mode 100644
index 0000000..ed0068b
--- /dev/null
+++ b/src/bin/host/b10-host.1
@@ -0,0 +1,122 @@
+'\" t
+.\" Title: b10-host
+.\" Author: [FIXME: author] [see http://docbook.sf.net/el/author]
+.\" Generator: DocBook XSL Stylesheets v1.75.2 <http://docbook.sf.net/>
+.\" Date: May 4, 2011
+.\" Manual: BIND10
+.\" Source: BIND10
+.\" Language: English
+.\"
+.TH "B10\-HOST" "1" "May 4, 2011" "BIND10" "BIND10"
+.\" -----------------------------------------------------------------
+.\" * set default formatting
+.\" -----------------------------------------------------------------
+.\" disable hyphenation
+.nh
+.\" disable justification (adjust text to left margin only)
+.ad l
+.\" -----------------------------------------------------------------
+.\" * MAIN CONTENT STARTS HERE *
+.\" -----------------------------------------------------------------
+.SH "NAME"
+b10-host \- DNS lookup utility
+.SH "SYNOPSIS"
+.HP \w'\fBb10\-host\fR\ 'u
+\fBb10\-host\fR [\fB\-a\fR] [\fB\-c\ \fR\fB\fIclass\fR\fR] [\fB\-d\fR] [\fB\-p\ \fR\fB\fIport\fR\fR] [\fB\-r\fR] [\fB\-t\ \fR\fB\fItype\fR\fR] [\fB\-v\fR] [\fIname\fR] [\fB\fIserver\fR\fR]
+.SH "DESCRIPTION"
+.PP
+The
+\fBb10\-host\fR
+utility does DNS lookups\&. Its initial goal is to be a
+\fBhost\fR(1)
+clone, but also add a few features useful for BIND 10 development testing\&.
+.PP
+By default, it looks up the A, AAAA, and MX record sets for the
+\fIname\fR\&. Optionally, you may select a name server to query against by adding the
+\fIserver\fR
+argument\&.
+.SH "OPTIONS"
+.PP
+The arguments are as follows:
+.PP
+\fB\-a\fR
+.RS 4
+Enable verbose mode and do a query for type ANY\&. (If the
+\fB\-t\fR
+option is also set, then the ANY query is not done, but it still uses verbose mode\&.)
+.RE
+.PP
+\fB\-c \fR\fB\fIclass\fR\fR
+.RS 4
+Define the class for the query\&. The default is IN (Internet)\&.
+.RE
+.PP
+\fB\-d\fR
+.RS 4
+Enable verbose output mode, including elapsed time in milliseconds\&. Verbose mode shows the header, question, answer, authority, and additional sections (if provided)\&. (Same as
+\fB\-v\fR\&.)
+.RE
+.PP
+\fB\-p \fR\fB\fIport\fR\fR
+.RS 4
+Select an alternative port for the query\&. This may be a number or a service name\&. The default is 53 (domain)\&. This is not a standard feature of
+\fBhost\fR(1)\&.
+.RE
+.PP
+\fB\-r\fR
+.RS 4
+Disable recursive processing by not setting the Recursion Desired flag in the query\&.
+.RE
+.PP
+\fB\-t \fR\fB\fItype\fR\fR
+.RS 4
+Select a specific resource record type for the query\&. By default, it looks up the A, AAAA, and MX record sets\&.
+(This overrides the
+\fB\-a\fR
+option\&.)
+.RE
+.PP
+\fB\-v\fR
+.RS 4
+Same as
+\fB\-d\fR
+option\&.
+.RE
+.SH "COMPATIBILITY / BUGS"
+.PP
+
+\fBb10\-host\fR
+does not do reverse lookups by default yet (by detecting if name is a IPv4 or IPv6 address)\&.
+.PP
+Unknown
+\fB\-c\fR
+class or
+\fB\-t\fR
+type causes
+\fBb10\-host\fR
+to Abort\&.
+.PP
+Not all types are supported yet for formatting\&. Not all switches are supported yet\&.
+.PP
+It doesn\'t use
+/etc/resolv\&.conf
+at this time\&. The default name server used is 127\&.0\&.0\&.1\&.
+.PP
+
+\fBb10\-host\fR
+does not do reverse lookups by default yet (by detecting if name is a IPv4 or IPv6 address)\&.
+.PP
+
+\fB\-p\fR
+is not a standard feature\&.
+.SH "HISTORY"
+.PP
+The C++ version of
+\fBb10\-host\fR
+was started in October 2009 by Jeremy C\&. Reed of ISC\&. Its usage and output were based on the standard
+\fBhost\fR
+command\&.
+.SH "COPYRIGHT"
+.br
+Copyright \(co 2011 Internet Systems Consortium, Inc. ("ISC")
+.br
diff --git a/src/bin/host/b10-host.xml b/src/bin/host/b10-host.xml
new file mode 100644
index 0000000..7da07dd
--- /dev/null
+++ b/src/bin/host/b10-host.xml
@@ -0,0 +1,201 @@
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
+ "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd"
+ [<!ENTITY mdash "—">]>
+<!--
+ - 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.
+-->
+
+<!-- $Id$ -->
+<refentry>
+
+ <refentryinfo>
+ <date>May 4, 2011</date>
+ </refentryinfo>
+
+ <refmeta>
+ <refentrytitle>b10-host</refentrytitle>
+ <manvolnum>1</manvolnum>
+ <refmiscinfo>BIND10</refmiscinfo>
+ </refmeta>
+
+ <refnamediv>
+ <refname>b10-host</refname>
+ <refpurpose>DNS lookup utility</refpurpose>
+ </refnamediv>
+
+ <docinfo>
+ <copyright>
+ <year>2011</year>
+ <holder>Internet Systems Consortium, Inc. ("ISC")</holder>
+ </copyright>
+ </docinfo>
+
+ <refsynopsisdiv>
+ <cmdsynopsis>
+ <command>b10-host</command>
+ <arg><option>-a</option></arg>
+ <arg><option>-c <replaceable>class</replaceable></option></arg>
+ <arg><option>-d</option></arg>
+ <arg><option>-p <replaceable>port</replaceable></option></arg>
+ <arg><option>-r</option></arg>
+ <arg><option>-t <replaceable>type</replaceable></option></arg>
+ <arg><option>-v</option></arg>
+ <arg><replaceable>name</replaceable></arg>
+ <arg><option><replaceable>server</replaceable></option></arg>
+ </cmdsynopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>DESCRIPTION</title>
+ <para>
+ The <command>b10-host</command> utility does DNS lookups.
+ Its initial goal is to be a
+ <citerefentry><refentrytitle>host</refentrytitle>
+ <manvolnum>1</manvolnum></citerefentry>
+ clone, but also add a few features useful for BIND 10 development
+ testing.
+ </para>
+
+ <para>
+ By default, it looks up the A, AAAA, and MX record sets for the
+ <replaceable>name</replaceable>.
+ Optionally, you may select a name server to query against by adding
+ the <replaceable>server</replaceable> argument.
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>OPTIONS</title>
+
+ <para>The arguments are as follows:</para>
+
+ <variablelist>
+
+ <varlistentry>
+ <term><option>-a</option></term>
+ <listitem><para>
+ Enable verbose mode and do a query for type ANY.
+ (If the <option>-t</option> option is also set, then the
+ ANY query is not done, but it still uses verbose mode.)
+ </para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>-c <replaceable>class</replaceable></option></term>
+ <listitem><para>
+ Define the class for the query.
+ The default is IN (Internet).
+<!-- TODO: bug if class is unknown causes seg fault and possible core dump -->
+ </para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>-d</option></term>
+ <listitem><para>
+ Enable verbose output mode, including elapsed time in
+ milliseconds.
+ Verbose mode shows the header, question, answer, authority,
+ and additional sections (if provided).
+ (Same as <option>-v</option>.)
+ </para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>-p <replaceable>port</replaceable></option></term>
+ <listitem><para>
+ Select an alternative port for the query.
+ This may be a number or a service name.
+ The default is 53 (domain).
+ This is not a standard feature of
+ <citerefentry><refentrytitle>host</refentrytitle>
+ <manvolnum>1</manvolnum></citerefentry>.
+ </para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>-r</option></term>
+ <listitem><para>
+ Disable recursive processing by not setting the
+ Recursion Desired flag in the query.
+ </para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>-t <replaceable>type</replaceable></option></term>
+ <listitem><para>
+ Select a specific resource record type for the query.
+ By default, it looks up the A, AAAA, and MX record sets.
+<!-- TODO: bug if class is unknown causes seg fault and possible core dump -->
+ (This overrides the <option>-a</option> option.)
+ </para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>-v</option></term>
+ <listitem><para>
+ Same as <option>-d</option> option.
+ </para></listitem>
+ </varlistentry>
+
+ </variablelist>
+
+ </refsect1>
+
+ <refsect1>
+ <title>COMPATIBILITY / BUGS</title>
+ <para>
+ <command>b10-host</command> does not do reverse lookups by
+ default yet (by detecting if name is a IPv4 or IPv6 address).
+ </para>
+
+ <para>
+ Unknown <option>-c</option> class or <option>-t</option> type
+ causes <command>b10-host</command> to Abort.
+ </para>
+
+ <para>
+ Not all types are supported yet for formatting.
+ Not all switches are supported yet.
+ </para>
+
+ <para>
+ It doesn't use <filename>/etc/resolv.conf</filename> at this time.
+ The default name server used is 127.0.0.1.
+ </para>
+
+ <para>
+ <command>b10-host</command> does not do reverse lookups by
+ default yet (by detecting if name is a IPv4 or IPv6 address).
+ </para>
+
+ <para>
+ <option>-p</option> is not a standard feature.
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>HISTORY</title>
+ <para>
+ The C++ version of <command>b10-host</command> was started in
+ October 2009 by Jeremy C. Reed of ISC.
+ Its usage and output were based on the standard <command>host</command>
+ command.
+ </para>
+ </refsect1>
+</refentry><!--
+ - Local variables:
+ - mode: sgml
+ - End:
+-->
diff --git a/src/bin/host/host.cc b/src/bin/host/host.cc
index 973509e..f0df0c8 100644
--- a/src/bin/host/host.cc
+++ b/src/bin/host/host.cc
@@ -1,4 +1,4 @@
-// Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2010-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
@@ -44,13 +44,16 @@ namespace {
char* dns_type = NULL; // not set, so A, AAAA, MX
const char* server = "127.0.0.1";
const char* server_port = "53";
-int verbose = 0;
-int first_time = 1;
-bool recursive_bit = true;
+const char* dns_class = "IN";
+bool verbose = false;
+bool dns_any = false;
+int first_time = 1;
+bool recursive_bit = true;
struct timeval before_time, after_time;
int
-host_lookup(const char* const name, const char* const type) {
+host_lookup(const char* const name, const char* const dns_class,
+ const char* const type, bool any) {
Message msg(Message::RENDER);
@@ -64,8 +67,8 @@ host_lookup(const char* const name, const char* const type) {
}
msg.addQuestion(Question(Name(name),
- RRClass::IN(), // IN class only for now
- RRType(type))); // if NULL then:
+ RRClass(dns_class),
+ any ? RRType::ANY() : RRType(type))); // if NULL then:
OutputBuffer obuffer(512);
MessageRenderer renderer(obuffer);
@@ -127,18 +130,29 @@ host_lookup(const char* const name, const char* const type) {
rmsg.fromWire(ibuffer);
if (!verbose) {
+ string description = "";
for (RRsetIterator it =
rmsg.beginSection(Message::SECTION_ANSWER);
it != rmsg.endSection(Message::SECTION_ANSWER);
++it) {
- if ((*it)->getType() != RRType::A()) {
- continue;
+
+ if ((*it)->getType() == RRType::A()) {
+ description = "has address";
+ }
+ else if ((*it)->getType() == RRType::AAAA()) {
+ description = "has IPv6 address";
+ }
+ else if ((*it)->getType() == RRType::MX()) {
+ description = "mail is handled by";
+ }
+ else if ((*it)->getType() == RRType::TXT()) {
+ description = "descriptive text";
}
RdataIteratorPtr rit = (*it)->getRdataIterator();
for (; !rit->isLast(); rit->next()) {
// instead of using my name, maybe use returned label?
- cout << name << " has address " <<
+ cout << name << " " << description << " " <<
(*rit).getCurrent().toText() << endl;
}
}
@@ -159,13 +173,19 @@ host_lookup(const char* const name, const char* const type) {
// TODO: if NXDOMAIN, host(1) doesn't show HEADER
// Host hsdjkfhksjhdfkj not found: 3(NXDOMAIN)
- // TODO: figure out the new libdns way to test if NXDOMAIN
+ // TODO: test if NXDOMAIN
std::cout << "Received " << cc <<
" bytes in " << elapsed_time << " ms\n";
// TODO: " bytes from 127.0.0.1#53 in 0 ms
} //verbose
+/*
+TODO: handle InvalidRRClass
+TODO: handle invalid type exception
+ } catch (InvalidType ivt) {
+ std::cerr << "invalid type:" << ivt.what();
+*/
} catch (const exception& ex) {
std::cerr << "parse failed for " <<
string(name) << "/" << type << ": " << ex.what() << std::endl;
@@ -184,26 +204,36 @@ int
main(int argc, char* argv[]) {
int c;
- while ((c = getopt(argc, argv, "p:rt:v")) != -1)
+ while ((c = getopt(argc, argv, "ac:dp:rt:v")) != -1)
switch (c) {
+ case 'a':
+ dns_any = true;
+ verbose = true;
+ break;
+ case 'c':
+ dns_class = optarg;
+ break;
+ // p for port is a non-standard switch
+ case 'p':
+ server_port = optarg;
+ break;
case 'r':
recursive_bit = false;
break;
case 't':
dns_type = optarg;
break;
- case 'p':
- server_port = optarg;
- break;
+ case 'd':
+ // drop through to v, because debug and verbose are equivalent
case 'v':
- verbose = 1;
+ verbose = true;
break;
}
argc -= optind;
argv += optind;
if (argc < 1) {
- cout << "Usage: host [-vr] [-t type] hostname [server]\n";
+ cout << "Usage: host [-adprv] [-c class] [-t type] hostname [server]\n";
exit(1);
}
@@ -212,12 +242,13 @@ main(int argc, char* argv[]) {
}
if (dns_type == NULL) {
- host_lookup(argv[0], "A");
+ host_lookup(argv[0], dns_class, "A", dns_any);
// TODO: don't do next if A doesn't exist
- host_lookup(argv[0], "AAAA");
- host_lookup(argv[0], "MX");
+ host_lookup(argv[0], dns_class, "AAAA", dns_any);
+ host_lookup(argv[0], dns_class, "MX", dns_any);
} else {
- host_lookup(argv[0], dns_type);
+ // -t overrides -a, regardless of order
+ host_lookup(argv[0], dns_class, dns_type, false);
}
return (0);
}
diff --git a/src/bin/msgq/tests/msgq_test.py b/src/bin/msgq/tests/msgq_test.py
index f926845..26878f7 100644
--- a/src/bin/msgq/tests/msgq_test.py
+++ b/src/bin/msgq/tests/msgq_test.py
@@ -132,7 +132,7 @@ class SendNonblock(unittest.TestCase):
task()
# If we got here, then everything worked well and in time
# In that case, we terminate successfully
- sys.exit(0) # needs exit code
+ os._exit(0) # needs exit code
else:
(pid, status) = os.waitpid(task_pid, 0)
self.assertEqual(0, status,
diff --git a/src/bin/stats/run_b10-stats-httpd.sh.in b/src/bin/stats/run_b10-stats-httpd.sh.in
deleted file mode 100755
index 67c93f0..0000000
--- a/src/bin/stats/run_b10-stats-httpd.sh.in
+++ /dev/null
@@ -1,33 +0,0 @@
-#! /bin/sh
-
-# Copyright (C) 2011 Internet Systems Consortium.
-#
-# Permission to use, copy, modify, and distribute this software for any
-# purpose with or without fee is hereby granted, provided that the above
-# copyright notice and this permission notice appear in all copies.
-#
-# THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SYSTEMS CONSORTIUM
-# DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
-# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
-# INTERNET SYSTEMS CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
-# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
-# FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
-# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
-# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-
-PYTHON_EXEC=${PYTHON_EXEC:- at PYTHON@}
-export PYTHON_EXEC
-
-PYTHONPATH=@abs_top_builddir@/src/lib/python
-export PYTHONPATH
-
-BIND10_MSGQ_SOCKET_FILE=@abs_top_builddir@/msgq_socket
-export BIND10_MSGQ_SOCKET_FILE
-
-STATS_PATH=@abs_top_builddir@/src/bin/stats
-
-B10_FROM_SOURCE=@abs_top_srcdir@
-export B10_FROM_SOURCE
-
-cd ${STATS_PATH}
-exec ${PYTHON_EXEC} -O b10-stats-httpd "$@"
diff --git a/src/bin/stats/run_b10-stats.sh.in b/src/bin/stats/run_b10-stats.sh.in
deleted file mode 100755
index b9007c8..0000000
--- a/src/bin/stats/run_b10-stats.sh.in
+++ /dev/null
@@ -1,33 +0,0 @@
-#! /bin/sh
-
-# Copyright (C) 2010, 2011 Internet Systems Consortium.
-#
-# Permission to use, copy, modify, and distribute this software for any
-# purpose with or without fee is hereby granted, provided that the above
-# copyright notice and this permission notice appear in all copies.
-#
-# THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SYSTEMS CONSORTIUM
-# DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
-# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
-# INTERNET SYSTEMS CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
-# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
-# FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
-# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
-# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-
-PYTHON_EXEC=${PYTHON_EXEC:- at PYTHON@}
-export PYTHON_EXEC
-
-PYTHONPATH=@abs_top_builddir@/src/lib/python
-export PYTHONPATH
-
-BIND10_MSGQ_SOCKET_FILE=@abs_top_builddir@/msgq_socket
-export BIND10_MSGQ_SOCKET_FILE
-
-B10_FROM_SOURCE=@abs_top_srcdir@
-export B10_FROM_SOURCE
-
-STATS_PATH=@abs_top_builddir@/src/bin/stats
-
-cd ${STATS_PATH}
-exec ${PYTHON_EXEC} -O b10-stats "$@"
diff --git a/src/bin/stats/stats.py.in b/src/bin/stats/stats.py.in
index dd617f8..821a719 100644
--- a/src/bin/stats/stats.py.in
+++ b/src/bin/stats/stats.py.in
@@ -276,9 +276,6 @@ class CCSessionListener(Listener):
"""
handle set command
"""
- if self.verbose:
- sys.stdout.write("[b10-stats] 'set' command received, args: "+str(args)+"\n")
-
# 'args' must be dictionary type
self.stats_data.update(args['stats_data'])
diff --git a/src/bin/stats/tests/stats_test.in b/src/bin/stats/tests/stats_test.in
deleted file mode 100755
index 9a95c5b..0000000
--- a/src/bin/stats/tests/stats_test.in
+++ /dev/null
@@ -1,31 +0,0 @@
-#! /bin/sh
-
-# Copyright (C) 2010, 2011 Internet Systems Consortium.
-#
-# Permission to use, copy, modify, and distribute this software for any
-# purpose with or without fee is hereby granted, provided that the above
-# copyright notice and this permission notice appear in all copies.
-#
-# THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SYSTEMS CONSORTIUM
-# DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
-# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
-# INTERNET SYSTEMS CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
-# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
-# FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
-# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
-# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-
-PYTHON_EXEC=${PYTHON_EXEC:- at PYTHON@}
-export PYTHON_EXEC
-
-PYTHONPATH=@abs_top_builddir@/src/lib/python:@abs_top_srcdir@/src/bin/stats:@abs_top_srcdir@/src/bin/stats/tests
-export PYTHONPATH
-
-B10_FROM_SOURCE=@abs_top_srcdir@
-export B10_FROM_SOURCE
-
-TEST_PATH=@abs_top_srcdir@/src/bin/stats/tests
-
-cd ${TEST_PATH}
-${PYTHON_EXEC} -O b10-stats_test.py $*
-${PYTHON_EXEC} -O b10-stats-httpd_test.py $*
diff --git a/src/lib/asiodns/Makefile.am b/src/lib/asiodns/Makefile.am
index 7beaaa3..2a6c3ac 100644
--- a/src/lib/asiodns/Makefile.am
+++ b/src/lib/asiodns/Makefile.am
@@ -11,8 +11,8 @@ AM_CXXFLAGS = $(B10_CXXFLAGS)
CLEANFILES = *.gcno *.gcda asiodef.h asiodef.cc
# Define rule to build logging source files from message file
-asiodef.h asiodef.cc: asiodef.msg
- $(top_builddir)/src/lib/log/compiler/message $(top_srcdir)/src/lib/asiodns/asiodef.msg
+asiodef.h asiodef.cc: asiodef.mes
+ $(top_builddir)/src/lib/log/compiler/message $(top_srcdir)/src/lib/asiodns/asiodef.mes
BUILT_SOURCES = asiodef.h asiodef.cc
@@ -28,7 +28,7 @@ libasiodns_la_SOURCES += io_fetch.cc io_fetch.h
nodist_libasiodns_la_SOURCES = asiodef.cc asiodef.h
-EXTRA_DIST = asiodef.msg
+EXTRA_DIST = asiodef.mes
# Note: the ordering matters: -Wno-... must follow -Wextra (defined in
# B10_CXXFLAGS)
diff --git a/src/lib/asiodns/asiodef.mes b/src/lib/asiodns/asiodef.mes
new file mode 100644
index 0000000..3f2e80c
--- /dev/null
+++ b/src/lib/asiodns/asiodef.mes
@@ -0,0 +1,56 @@
+# 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.
+
+$PREFIX ASIODNS_
+$NAMESPACE isc::asiodns
+
+% FETCHCOMP upstream fetch to %1(%2) has now completed
+A debug message, this records the the upstream fetch (a query made by the
+resolver on behalf of its client) to the specified address has completed.
+
+% FETCHSTOP upstream fetch to %1(%2) has been stopped
+An external component has requested the halting of an upstream fetch. This
+is an allowed operation, and the message should only appear if debug is
+enabled.
+
+% OPENSOCK error %1 opening %2 socket to %3(%4)
+The asynchronous I/O code encountered an error when trying to open a socket
+of the specified protocol in order to send a message to the target address.
+The the number of the system error that cause the problem is given in the
+message.
+
+% RECVSOCK error %1 reading %2 data from %3(%4)
+The asynchronous I/O code encountered an error when trying read data from
+the specified address on the given protocol. The the number of the system
+error that cause the problem is given in the message.
+
+% SENDSOCK error %1 sending data using %2 to %3(%4)
+The asynchronous I/O code encountered an error when trying send data to
+the specified address on the given protocol. The the number of the system
+error that cause the problem is given in the message.
+
+% RECVTMO receive timeout while waiting for data from %1(%2)
+An upstream fetch from the specified address timed out. This may happen for
+any number of reasons and is most probably a problem at the remote server
+or a problem on the network. The message will only appear if debug is
+enabled.
+
+% UNKORIGIN unknown origin for ASIO error code %1 (protocol: %2, address %3)
+This message should not appear and indicates an internal error if it does.
+Please enter a bug report.
+
+% UNKRESULT unknown result (%1) when IOFetch::stop() was executed for I/O to %2(%3)
+The termination method of the resolver's upstream fetch class was called with
+an unknown result code (which is given in the message). This message should
+not appear and may indicate an internal error. Please enter a bug report.
diff --git a/src/lib/asiodns/asiodef.msg b/src/lib/asiodns/asiodef.msg
deleted file mode 100644
index ea3ec2f..0000000
--- a/src/lib/asiodns/asiodef.msg
+++ /dev/null
@@ -1,56 +0,0 @@
-# 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.
-
-$PREFIX ASIODNS_
-$NAMESPACE isc::asiodns
-
-FETCHCOMP upstream fetch to %1(%2) has now completed
-+ A debug message, this records the the upstream fetch (a query made by the
-+ resolver on behalf of its client) to the specified address has completed.
-
-FETCHSTOP upstream fetch to %1(%2) has been stopped
-+ An external component has requested the halting of an upstream fetch. This
-+ is an allowed operation, and the message should only appear if debug is
-+ enabled.
-
-OPENSOCK error %1 opening %2 socket to %3(%4)
-+ The asynchronous I/O code encountered an error when trying to open a socket
-+ of the specified protocol in order to send a message to the target address.
-+ The the number of the system error that cause the problem is given in the
-+ message.
-
-RECVSOCK error %1 reading %2 data from %3(%4)
-+ The asynchronous I/O code encountered an error when trying read data from
-+ the specified address on the given protocol. The the number of the system
-+ error that cause the problem is given in the message.
-
-SENDSOCK error %1 sending data using %2 to %3(%4)
-+ The asynchronous I/O code encountered an error when trying send data to
-+ the specified address on the given protocol. The the number of the system
-+ error that cause the problem is given in the message.
-
-RECVTMO receive timeout while waiting for data from %1(%2)
-+ An upstream fetch from the specified address timed out. This may happen for
-+ any number of reasons and is most probably a problem at the remote server
-+ or a problem on the network. The message will only appear if debug is
-+ enabled.
-
-UNKORIGIN unknown origin for ASIO error code %1 (protocol: %2, address %3)
-+ This message should not appear and indicates an internal error if it does.
-+ Please enter a bug report.
-
-UNKRESULT unknown result (%1) when IOFetch::stop() was executed for I/O to %2(%3)
-+ The termination method of the resolver's upstream fetch class was called with
-+ an unknown result code (which is given in the message). This message should
-+ not appear and may indicate an internal error. Please enter a bug report.
diff --git a/src/lib/asiodns/io_fetch.cc b/src/lib/asiodns/io_fetch.cc
index dd1af75..cc8bd11 100644
--- a/src/lib/asiodns/io_fetch.cc
+++ b/src/lib/asiodns/io_fetch.cc
@@ -62,7 +62,17 @@ namespace asiodns {
/// Use the ASIO logger
+namespace {
+
isc::log::Logger logger("asiolink");
+// Log debug verbosity
+enum {
+ DBG_IMPORTANT = 1,
+ DBG_COMMON = 20,
+ DBG_ALL = 50
+};
+
+}
/// \brief IOFetch Data
///
@@ -332,21 +342,17 @@ IOFetch::stop(Result result) {
// numbers indicating the most important information. The relative
// values are somewhat arbitrary.
//
- // Although Logger::debug checks the debug flag internally, doing it
- // below before calling Logger::debug avoids the overhead of a string
- // conversion in the common case when debug is not enabled.
- //
// TODO: Update testing of stopped_ if threads are used.
data_->stopped = true;
switch (result) {
case TIME_OUT:
- LOG_DEBUG(logger, 20, ASIODNS_RECVTMO).
+ LOG_DEBUG(logger, DBG_COMMON, ASIODNS_RECVTMO).
arg(data_->remote_snd->getAddress().toText()).
arg(data_->remote_snd->getPort());
break;
case SUCCESS:
- LOG_DEBUG(logger, 50, ASIODNS_FETCHCOMP).
+ LOG_DEBUG(logger, DBG_ALL, ASIODNS_FETCHCOMP).
arg(data_->remote_rcv->getAddress().toText()).
arg(data_->remote_rcv->getPort());
break;
@@ -355,7 +361,7 @@ IOFetch::stop(Result result) {
// Fetch has been stopped for some other reason. This is
// allowed but as it is unusual it is logged, but with a lower
// debug level than a timeout (which is totally normal).
- LOG_DEBUG(logger, 1, ASIODNS_FETCHSTOP).
+ LOG_DEBUG(logger, DBG_IMPORTANT, ASIODNS_FETCHSTOP).
arg(data_->remote_snd->getAddress().toText()).
arg(data_->remote_snd->getPort());
break;
diff --git a/src/lib/datasrc/messagedef.mes b/src/lib/datasrc/messagedef.mes
index e4add48..c436912 100644
--- a/src/lib/datasrc/messagedef.mes
+++ b/src/lib/datasrc/messagedef.mes
@@ -17,484 +17,484 @@ $NAMESPACE isc::datasrc
# \brief Messages for the data source library
-CACHE_CREATE creating the hotspot cache
-+ Debug information that the hotspot cache was created at startup.
-
-CACHE_DESTROY destroying the hotspot cache
-+ Debug information. The hotspot cache is being destroyed.
-
-CACHE_INSERT inserting item '%1' into the cache
-+ Debug information. It means a new item is being inserted into the hotspot
-+ cache.
-
-CACHE_OLD_FOUND older instance of cache item 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.
-
-CACHE_FULL 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.
-
-CACHE_REMOVE removing '%1' from the cache
-+ Debug information. An item is being removed from the hotspot cache.
-
-CACHE_LOOKUP looking up '%1' in the cache
-+ Debug information. We are trying to look up an item in the hotspot cache.
-+ Further progress and result will follow.
-
-CACHE_NOT_FOUND the item '%1' was not found
-+ Debug information. We tried to look up an item in the hotspot cache, but
-+ it is not there.
-
-CACHE_FOUND the item '%1' was found
-+ Debug information. We successfully looked up an item in the hotspot cache.
-
-CACHE_EXPIRED the item '%1' is expired
-+ Debug information. We tried to find an item in the hotspot cache and in fact
-+ we did, but it was too old. So we pretend we didn't find it at all (the
-+ external effect is the same as CACHE_NOT_FOUND).
-
-CACHE_SLOTS setting the 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, we're going to drop them right now. The size
-+ of 0 means no limit.
-
-CACHE_ENABLE enabling the cache
-+ The hotspot cache is enabled from now on.
-
-CACHE_DISABLE disabling the cache
-+ The hotspot cache is disabled from now on. It is not going to store
-+ information or return anything.
-
-QUERY_SYNTH_CNAME synthesizing CNAME from DNAME on '%1'
-+ Debug information. While answering a query, we met a DNAME. We'll return the
-+ DNAME, but we're creating a CNAME for clients that don't understand DNAMEs.
-
-QUERY_EMPTY_DNAME the DNAME on '%1' is empty
-+ We tried to synthesize a CNAME from this DNAME, but it contains no records.
-+ This indicates problem with supplied data.
-
-QUERY_GET_NS_ADDITIONAL addition of A/AAAA for '%1' requested by NS '%2'
-+ Debug information. While processing a query, we met a NS record. It
-+ references the mentioned address, so we want to look up A/AAAA records for it
-+ and put it into the additional section.
-
-QUERY_GET_MX_ADDITIONAL addition of A/AAAA for '%1' requested by MX '%2'
-+ Debug information. While processing a query, we met a MX record. It
-+ references the mentioned address, so we want to look up A/AAAA records for it
-+ and put it into the additional section.
-
-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 we're following it.
-
-QUERY_EMPTY_CNAME cNAME at '%1' is empty
-+ We tried to follow a CNAME, but contains no records. We have nothing to
-+ follow, so we will have nothing in the answer. This indicates a problem with
-+ supplied data.
-
-QUERY_TOO_MANY_CNAMES cNAME chain limit exceeded at '%1'
-+ While answering a query, we followed a CNAME. Then another one. And after 16
-+ CNAMEs we decided it's enough and we won't follow more. Long CNAME chains
-+ are discouraged, and this might be a loop as well. Note that some of the
-+ CNAMEs might have been synthesised from DNAMEs internally. This indicates
-+ a problem with supplied data.
-
-QUERY_CHECK_CACHE checking cache for '%1/%2'
-+ Debug information. While processing a query we're looking into the hotspot
-+ cache.
-
-QUERY_NO_CACHE_ANY_SIMPLE ignoring cache for ANY query
-+ Debug information. We don't really want to use cache for simple ANY query
-+ (ANY as the type or class).
-
-QUERY_NO_CACHE_ANY_AUTH ignoring cache for ANY query
-+ Debug information. We don't really want to use cache for authoritative ANY
-+ query (ANY as the type or class).
-
-DO_QUERY handling query for '%1/%2'
-+ Debug information. We're processing some internal query for given name and
-+ type.
-
-QUERY_NO_ZONE no zone containing '%1' in class '%2'
-+ We tried to get the domain but there's no zone in our data that encloses
-+ the name. Maybe someone sent a query to wrong server for some reason.
-
-QUERY_CACHED data found in cache
-+ Debug information. We found the requested data in cache, so we're not
-+ querying the real data source.
-
-QUERY_IS_SIMPLE simple query
-+ Debug information. The last DO_QUERY is a simple query.
-
-QUERY_IS_AUTH auth query
-+ Debug information. The last DO_QUERY is an auth query.
-
-QUERY_IS_GLUE glue query
-+ Debug information. The last DO_QUERY is query for glue addresses.
-
-QUERY_IS_NOGLUE query for non-glue addresses
-+ Debug information. The last DO_QUERY is query for addresses that are not
-+ glue.
-
-QUERY_IS_REF query for referral
-+ Debug information. The last DO_QUERY is query for referral information.
-
-QUERY_SIMPLE_FAIL the underlying data source failed with %1
-+ We queried the data source to answer a simple query and it returned error
-+ (1 is some error, 2 is not implemented). The data source should have logged
-+ the specific error already.
-
-QUERY_AUTH_FAIL the underlying data source failed with %1
-+ We queried the data source to answer authoritative query and it returned
-+ error (1 is some error, 2 is not implemented). The data source should have
-+ log the specific error already.
-
-QUERY_GLUE_FAIL the underlying data source failed with %1
-+ We queried the data source to answer query for glue addresses and it returned
-+ error (1 is some error, 2 is not implemented). The data source should have
-+ log the specific error already.
-
-QUERY_NOGLUE_FAIL the underlying data source failed with %1
-+ We queried the data source to answer query for non-glue addresses and it
-+ returned error (1 is some error, 2 is not implemented). The data source
-+ should have log the specific error already.
-
-QUERY_REF_FAIL the underlying data source failed with %1
-+ We queried the data source to answer query for referral and it
-+ returned error (1 is some error, 2 is not implemented). The data source
-+ should have log the specific error already.
-
-QUERY_INVALID_OP invalid query operation requested
-+ This indicates a programmer error. The DO_QUERY was called with unknown
-+ operation code.
-
-QUERY_ADD_RRSET adding RRset '%1/%2' to message
-+ Debug information. We're adding the RRset to answer message.
-
-QUERY_COPY_AUTH copying authoritative section into message
-+ Debug information. We're copying referral information into authoritative
-+ section of the response message.
-
-QUERY_DELEGATION looking for delegation on the path to '%1'
-+ Debug information. We're looking if there's a delegation point on the way
-+ down to the given domain.
-
-QUERY_ADD_SOA adding SOA of '%1'
-+ Debug information. We're adding a SOA record for the given zone into the
-+ authority section.
-
-QUERY_ADD_NSEC adding NSEC record for '%1'
-+ Debug information. We're adding NSEC record for this domain.
-
-QUERY_ADD_NSEC3 adding NSEC3 record of zone '%1'
-+ Debug information. We're adding an NSEC3 record for this zone.
-
-QUERY_NO_DS_NSEC3 there's no DS record in the '%1' zone
-+ We tried to insert a NSEC3 record into the message. But we didn't find a DS
-+ record for this zone.
-
-QUERY_NO_DS_NSEC there's no DS record in the '%1' zone
-+ We tried to insert a NSEC record into the message. But we didn't find a DS
-+ record for this zone.
-
-QUERY_WILDCARD looking for a wildcard covering '%1'
-+ Debug information. We didn't find a direct match, so we're trying to find if
-+ there's a wildcard we could use to answer the query.
-
-QUERY_WILDCARD_PROVENX_FAIL unable to prove nonexistence of '%1' (%2)
-+ While processing a wildcard, we tried to prove nonexistence of the given
-+ domain or record. The code is 1 for error and 2 for not implemented.
-
-QUERY_WILDCARD_REFERRAL unable to find referral info for '%1' (%2)
-+ While processing a wildcard we met a referral. But we were not able to get
-+ information for it. The code is 1 for error, 2 for not implemented.
-
-QUERY_PROCESS processing query '%1/%2' in the '%3' class
-+ Debug information. We're starting to process a user query.
-
-QUERY_RRSIG unable to answer RRSIG query
-+ The server is unable to answer a direct query for RRSIG type, but was asked
-+ to do so.
-
-QUERY_MISPLACED_TASK task of this type should not be here
-+ This indicates a programming error. We found a task in the internal task
-+ queue which wasn't supposed to ever be put into the queue, but handled
-+ directly.
-
-QUERY_TASK_FAIL task failed with %1
-+ The query subtask failed. The reason should have been reported by the subtask
-+ already. The code is 1 for error, 2 for not implemented.
-
-QUERY_MISSING_NS missing NS records for '%1'
-+ We wanted to put the nameserver records into the authority section, but we
-+ discovered the zone doesn't have them. This indicates problem with provided
-+ data.
-
-UNEXPECTED_QUERY_STATE unexpected query state
-+ This indicates a programming error. We generated an internal task of type
-+ unknown to us.
-
-QUERY_FAIL query failed
-+ Some subtask of query processing failed. The reason should have been reported
-+ already. We are returning SERVFAIL.
-
-QUERY_BAD_REFERRAL bad referral to '%1'
-+ We discovered that the domain lives in another zone. But we are not able to
-+ generate referral information to it.
-
-QUERY_WILDCARD_FAIL error processing wildcard for '%1'
-+ We tried to find a wildcard to cover the domain, but there happened to be
-+ some (hopefully already reported) error for it.
-
-QUERY_MISSING_SOA the zone '%1' has no SOA
-+ We tried to answer negatively, but there's no SOA record in the zone.
-
-QUERY_PROVENX_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.
-
-QUERY_UNKNOWN_RESULT unknown result of subtask
-+ This indicates a programmer error. The answer of subtask doesn't look like
-+ anything we would know.
-
-META_ADD adding a data source into meta data source
-+ Debug information. We add yet another data source into the meta data source
-+ (probably at startup or reconfiguration).
-
-META_ADD_CLASS_MISMATCH mismatch between classes '%1' and '%2'
-+ We tried to add a data source of one class into a meta data source of a
-+ different type. The types must be the same.
-
-META_REMOVE removing data source from meta data source
-+ Debug information. We take a data source out of meta data source (probably
-+ at shutdown or reconfiguration).
-
-MEM_ADD_WILDCARD adding wildcards for '%1'
-+ Debug information. We need some special marks above each * in wildcard name
-+ in the in-memory data source. We are adding the marks for this name now.
-
-MEM_CNAME_TO_NONEMPTY can't add CNAME to domain with other data in '%1'
-+ Someone or something tried to add a CNAME into a domain that already contains
-+ some other data. But the protocol forbids coexistence of CNAME with anything
-+ (RFC 1034, section 3.6.2). This indicates a problem with provided data.
-
-MEM_CNAME_COEXIST can't add data to CNAME in domain '%1'
-+ This is the same problem as in MEM_CNAME_TO_NONEMPTY, but it happened the
-+ other way around -- adding some outher data to CNAME.
-
-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.
-
-MEM_SINGLETON trying to add multiple RRs for domain '%1' and type '%2'
-+ Some resource types are singletons -- only one is allowed in a domain
-+ (for example CNAME or SOA). This indicates a problem with provided data.
-
-MEM_OUT_OF_ZONE domain '%1' doesn't belong to zone '%2'
-+ It was attempted to add the domain into a zone that shouldn't have it
-+ (eg. the domain is not subdomain of the zone origin). This indicates a
-+ problem with provided data.
-
-MEM_WILDCARD_NS nS record in wildcard domain '%1'
-+ We refuse to load NS record into a wildcard domain. It is'n explicitly
-+ forbidden, but the protocol is ambiguous about how this should behave and
-+ BIND 9 refuses that as well. We don't like your zone, please describe it
-+ using different tools.
-
-MEM_WILDCARD_DNAME dNAME record in wildcard domain '%1'
-+ We refuse to load DNAME record into a wildcard domain. It is'n explicitly
-+ forbidden, but the protocol is ambiguous about how this should behave and
-+ BIND 9 refuses that as well. We don't like your zone, please describe it
-+ using different tools.
-
-MEM_ADD_RRSET adding RRset '%1/%2' into zone '%3'
-+ Debug information. We're adding an RRset to the zone of in-memory data
-+ source.
-
-MEM_DUP_RRSET duplicate RRset '%1/%2'
-+ An RRset is being inserted into in-memory data source for a second time.
-+ The original version must be removed first. Note that we don't support
-+ loading master files where an RRset is split into multiple locations yet.
-
-MEM_DNAME_ENCOUNTERED encountered a DNAME
-+ Debug information. While searching for the requested domain, we encountered
-+ a DNAME on the way. This may lead to redirection to a different domain and
-+ stop the search.
-
-MEM_NS_ENCOUNTERED encountered a NS
-+ Debug information. While searching for the requested domain, we encountered
-+ a NS on the way (a delegation). This may lead to stop of the search.
-
-MEM_RENAME renaming RRset from '%1' to '%2'
-+ Debug information. We generate an RRset from a different RRset (most probably
-+ a wildcard). So we need to rename it to whatever the user asked for. In fact,
-+ we can't rename RRset (it's not possible with our libraries), so we create
-+ a new one and copy everything.
-
-MEM_FIND find '%1/%2'
-+ Debug information. We're going to search the in-memory data source to find
-+ requestet RRset.
-
-MEM_DNAME_FOUND dNAME found at '%1'
-+ Debug information. We found a DNAME instead of the requested record.
-
-MEM_DELEG_FOUND delegation found at '%1'
-+ Debug information. We found a delegation point above the requested record.
-
-MEM_SUPER_STOP stopped at superdomain, domain is empty
-+ Debug information. The search stopped at a superdomain of the requested
-+ domain. The domain is a empty nonterminal, therefore we treat it as NXRRSET
-+ case (eg. the domain exists, but it doesn't have the requested record type).
-
-MEM_WILDCARD_CANCEL wildcard match canceled
-+ Debug information. We reached a domain above wildcard, but there's something
-+ below the requested domain. Therefore the wildcard doesn't apply here.
-+ This behaviour is specified by RFC 1034, section 4.3.3
-
-MEM_NOTFOUND requested domain not found
-+ Debug information. The requested domain does not exist.
-
-MEM_DOMAIN_EMPTY requested domain is empty
-+ Debug information. The requested domain exists in the tree of domains, but
-+ it is empty. Therefore it doesn't contain the requested resource type.
-
-MEM_EXACT_DELEGATION delegation at the exact domain
-+ Debug information. There's a NS record at the requested domain. This means
-+ this zone is not authoritative for the requested domain, but a delegation
-+ should be followed. The requested domain is an apex of some zone.
-
-MEM_ANY_SUCCESS aNY query successful
-+ Debug information. The domain was found and we answer an ANY type query by
-+ providing everything we found inside the domain.
-
-MEM_SUCCESS query successful
-+ Debug information. We found the record we searched for.
-
-MEM_CNAME cNAME at the domain
-+ Debug information. The requested domain is an alias to a different domain,
-+ returning the CNAME instead.
-
-MEM_NXRRSET no such type
-+ Debug information. The domain exists, but it doesn't hold any record of the
-+ requested type.
-
-MEM_CREATE creating zone '%1' in '%2' class
-+ Debug information. We're creating representation of a zone for the in-memory
-+ data source.
-
-MEM_DESTROY destroying zone '%1' in '%2' class
-+ Debug information. We're destroying the representation of zone in the
-+ in-memory data source.
-
-MEM_LOAD loading zone '%1' from file '%2'
-+ Debug information. We're loading the content of zone from a master file.
-
-MEM_SWAP swapping contents of two zone representations ('%1' and '%2')
-+ Debug information. We exchange contents of the zones. This is usual practice
-+ to do some manipulation in exception-safe manner -- we prepare the new data
-+ in a different zone object and when it works, we swap it with the old, then
-+ we can safely destroy the old one.
-
-MEM_ADD_ZONE adding zone '%1/%2'
-+ Debug information. We're adding this zone into the in-memory data source.
-
-MEM_FIND_ZONE looking for zone '%1'
-+ Debug information. We're looking for a zone in the in-memory data source.
-
-STATIC_CREATE creating the static datasource
-+ Debug information. We're creating the static data source (the one holding
-+ stuff like version.bind).
-
-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.
-
-STATIC_FIND looking for '%1/%2'
-+ Debug information. We're looking for this resource record set in the static
-+ data source.
-
-SQLITE_FINDREC looking for record '%1/%2'
-+ Debug information. The SQLite data source is looking up records of given name
-+ and type in the database.
-
-SQLITE_ENCLOSURE looking for zone containing '%1'
-+ Debug information. The SQLite data source is trying to identify, which zone
-+ should hold this domain.
-
-SQLITE_ENCLOSURE_BAD_CLASS class mismatch looking for a zone ('%1' and '%2')
-+ The SQLite data source can handle only one class at a time and it was asked
-+ to identify which zone is holding data of a different class.
-
-SQLITE_ENCLOSURE_NOTFOUND no zone contains it
-+ Debug information. The last SQLITE_ENCLOSURE query was unsuccessful, there's
-+ no such zone in our data.
-
-SQLITE_PREVIOUS looking for name previous to '%1'
-+ Debug information. We're trying to look up name preceding the supplied one.
-
-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.
-
-SQLITE_FIND_NSEC3 looking for NSEC3 in zone '%1' for hash '%2'
-+ Debug information. We're trying to look up a NSEC3 record in the SQLite data
-+ source.
-
-SQLITE_FIND_NSEC3_NO_ZONE no such zone '%1'
-+ The SQLite data source was asked to provide a NSEC3 record for given zone.
-+ But it doesn't contain that zone.
-
-SQLITE_FIND looking for RRset '%1/%2'
-+ Debug information. The SQLite data source is looking up a resource record
-+ set.
-
-SQLITE_FIND_BAD_CLASS class mismatch looking for an RRset ('%1' and '%2')
-+ The SQLite data source was looking up an RRset, but the data source contains
-+ different class than the query was for.
-
-SQLITE_FINDEXACT looking for exact RRset '%1/%2'
-+ Debug information. The SQLite data source is looking up an exact resource
-+ record.
-
-SQLITE_FINDEXACT_BAD_CLASS class mismatch looking for an RRset ('%1' and '%2')
-+ The SQLite data source was looking up an exact RRset, but the data source
-+ contains different class than the query was for.
-
-SQLITE_FINDADDRS looking for A/AAAA addresses for '%1'
-+ Debug information. The data source is looking up the addresses for given
-+ domain name.
-
-SQLITE_FINDADDRS_BAD_CLASS class mismatch looking for addresses ('%1' and '%2')
-+ The SQLite data source was looking up A/AAAA addresses, but the data source
-+ contains different class than the query was for.
-
-SQLITE_FINDREF looking for referral at '%1'
-+ Debug information. The SQLite data source is identifying if this domain is
-+ a referral and where it goes.
-
-SQLITE_FINDREF_BAD_CLASS class mismatch looking for referral ('%1' and '%2')
-+ The SQLite data source was trying to identify, if there's a referral. But the
-+ but it contains different class than the query was for.
-
-SQLITE_CREATE sQLite data source created
-+ Debug information. We're creating an instance of the SQLite data source.
-
-SQLITE_DESTROY sQLite data source destroyed
-+ Debug information. We're destroying an instance of SQLite data source.
-
-SQLITE_SETUP setting up SQLite database
-+ The database for SQLite data source was found empty. So we're assuming this
-+ is the first run and we initialize it with current schema. It'll still
-+ contain no data, but it will be ready for use.
-
-SQLITE_OPEN opening SQLite database '%1'
-+ Debug information. The SQLite data source is loading an SQLite database in
-+ the provide file.
-
-SQLITE_CLOSE closing SQLite database
-+ Debug information. The SQLite data source is closing the database file.
+% CACHE_CREATE creating the hotspot cache
+Debug information that the hotspot cache was created at startup.
+
+% CACHE_DESTROY destroying the hotspot cache
+Debug information. The hotspot cache is being destroyed.
+
+% CACHE_INSERT inserting item '%1' into the cache
+Debug information. It means a new item is being inserted into the hotspot
+cache.
+
+% CACHE_OLD_FOUND older instance of cache item 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.
+
+% CACHE_FULL 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.
+
+% CACHE_REMOVE removing '%1' from the cache
+Debug information. An item is being removed from the hotspot cache.
+
+% CACHE_LOOKUP looking up '%1' in the cache
+Debug information. We are trying to look up an item in the hotspot cache.
+Further progress and result will follow.
+
+% CACHE_NOT_FOUND the item '%1' was not found
+Debug information. We tried to look up an item in the hotspot cache, but
+it is not there.
+
+% CACHE_FOUND the item '%1' was found
+Debug information. We successfully looked up an item in the hotspot cache.
+
+% CACHE_EXPIRED the item '%1' is expired
+Debug information. We tried to find an item in the hotspot cache and in fact
+we did, but it was too old. So we pretend we didn't find it at all (the
+external effect is the same as CACHE_NOT_FOUND).
+
+% CACHE_SLOTS setting the 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, we're going to drop them right now. The size
+of 0 means no limit.
+
+% CACHE_ENABLE enabling the cache
+The hotspot cache is enabled from now on.
+
+% CACHE_DISABLE disabling the cache
+The hotspot cache is disabled from now on. It is not going to store
+information or return anything.
+
+% QUERY_SYNTH_CNAME synthesizing CNAME from DNAME on '%1'
+Debug information. While answering a query, we met a DNAME. We'll return the
+DNAME, but we're creating a CNAME for clients that don't understand DNAMEs.
+
+% QUERY_EMPTY_DNAME the DNAME on '%1' is empty
+We tried to synthesize a CNAME from this DNAME, but it contains no records.
+This indicates problem with supplied data.
+
+% QUERY_GET_NS_ADDITIONAL addition of A/AAAA for '%1' requested by NS '%2'
+Debug information. While processing a query, we met a NS record. It
+references the mentioned address, so we want to look up A/AAAA records for it
+and put it into the additional section.
+
+% QUERY_GET_MX_ADDITIONAL addition of A/AAAA for '%1' requested by MX '%2'
+Debug information. While processing a query, we met a MX record. It
+references the mentioned address, so we want to look up A/AAAA records for it
+and put it into the additional section.
+
+% 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 we're following it.
+
+% QUERY_EMPTY_CNAME cNAME at '%1' is empty
+We tried to follow a CNAME, but contains no records. We have nothing to
+follow, so we will have nothing in the answer. This indicates a problem with
+supplied data.
+
+% QUERY_TOO_MANY_CNAMES cNAME chain limit exceeded at '%1'
+While answering a query, we followed a CNAME. Then another one. And after 16
+CNAMEs we decided it's enough and we won't follow more. Long CNAME chains
+are discouraged, and this might be a loop as well. Note that some of the
+CNAMEs might have been synthesised from DNAMEs internally. This indicates
+a problem with supplied data.
+
+% QUERY_CHECK_CACHE checking cache for '%1/%2'
+Debug information. While processing a query we're looking into the hotspot
+cache.
+
+% QUERY_NO_CACHE_ANY_SIMPLE ignoring cache for ANY query
+Debug information. We don't really want to use cache for simple ANY query
+(ANY as the type or class).
+
+% QUERY_NO_CACHE_ANY_AUTH ignoring cache for ANY query
+Debug information. We don't really want to use cache for authoritative ANY
+query (ANY as the type or class).
+
+% DO_QUERY handling query for '%1/%2'
+Debug information. We're processing some internal query for given name and
+type.
+
+% QUERY_NO_ZONE no zone containing '%1' in class '%2'
+We tried to get the domain but there's no zone in our data that encloses
+the name. Maybe someone sent a query to wrong server for some reason.
+
+% QUERY_CACHED data found in cache
+Debug information. We found the requested data in cache, so we're not
+querying the real data source.
+
+% QUERY_IS_SIMPLE simple query
+Debug information. The last DO_QUERY is a simple query.
+
+% QUERY_IS_AUTH auth query
+Debug information. The last DO_QUERY is an auth query.
+
+% QUERY_IS_GLUE glue query
+Debug information. The last DO_QUERY is query for glue addresses.
+
+% QUERY_IS_NOGLUE query for non-glue addresses
+Debug information. The last DO_QUERY is query for addresses that are not
+glue.
+
+% QUERY_IS_REF query for referral
+Debug information. The last DO_QUERY is query for referral information.
+
+% QUERY_SIMPLE_FAIL the underlying data source failed with %1
+We queried the data source to answer a simple query and it returned error
+(1 is some error, 2 is not implemented). The data source should have logged
+the specific error already.
+
+% QUERY_AUTH_FAIL the underlying data source failed with %1
+We queried the data source to answer authoritative query and it returned
+error (1 is some error, 2 is not implemented). The data source should have
+log the specific error already.
+
+% QUERY_GLUE_FAIL the underlying data source failed with %1
+We queried the data source to answer query for glue addresses and it returned
+error (1 is some error, 2 is not implemented). The data source should have
+log the specific error already.
+
+% QUERY_NOGLUE_FAIL the underlying data source failed with %1
+We queried the data source to answer query for non-glue addresses and it
+returned error (1 is some error, 2 is not implemented). The data source
+should have log the specific error already.
+
+% QUERY_REF_FAIL the underlying data source failed with %1
+We queried the data source to answer query for referral and it
+returned error (1 is some error, 2 is not implemented). The data source
+should have log the specific error already.
+
+% QUERY_INVALID_OP invalid query operation requested
+This indicates a programmer error. The DO_QUERY was called with unknown
+operation code.
+
+% QUERY_ADD_RRSET adding RRset '%1/%2' to message
+Debug information. We're adding the RRset to answer message.
+
+% QUERY_COPY_AUTH copying authoritative section into message
+Debug information. We're copying referral information into authoritative
+section of the response message.
+
+% QUERY_DELEGATION looking for delegation on the path to '%1'
+Debug information. We're looking if there's a delegation point on the way
+down to the given domain.
+
+% QUERY_ADD_SOA adding SOA of '%1'
+Debug information. We're adding a SOA record for the given zone into the
+authority section.
+
+% QUERY_ADD_NSEC adding NSEC record for '%1'
+Debug information. We're adding NSEC record for this domain.
+
+% QUERY_ADD_NSEC3 adding NSEC3 record of zone '%1'
+Debug information. We're adding an NSEC3 record for this zone.
+
+% QUERY_NO_DS_NSEC3 there's no DS record in the '%1' zone
+We tried to insert a NSEC3 record into the message. But we didn't find a DS
+record for this zone.
+
+% QUERY_NO_DS_NSEC there's no DS record in the '%1' zone
+We tried to insert a NSEC record into the message. But we didn't find a DS
+record for this zone.
+
+% QUERY_WILDCARD looking for a wildcard covering '%1'
+Debug information. We didn't find a direct match, so we're trying to find if
+there's a wildcard we could use to answer the query.
+
+% QUERY_WILDCARD_PROVENX_FAIL unable to prove nonexistence of '%1' (%2)
+While processing a wildcard, we tried to prove nonexistence of the given
+domain or record. The code is 1 for error and 2 for not implemented.
+
+% QUERY_WILDCARD_REFERRAL unable to find referral info for '%1' (%2)
+While processing a wildcard we met a referral. But we were not able to get
+information for it. The code is 1 for error, 2 for not implemented.
+
+% QUERY_PROCESS processing query '%1/%2' in the '%3' class
+Debug information. We're starting to process a user query.
+
+% QUERY_RRSIG unable to answer RRSIG query
+The server is unable to answer a direct query for RRSIG type, but was asked
+to do so.
+
+% QUERY_MISPLACED_TASK task of this type should not be here
+This indicates a programming error. We found a task in the internal task
+queue which wasn't supposed to ever be put into the queue, but handled
+directly.
+
+% QUERY_TASK_FAIL task failed with %1
+The query subtask failed. The reason should have been reported by the subtask
+already. The code is 1 for error, 2 for not implemented.
+
+% QUERY_MISSING_NS missing NS records for '%1'
+We wanted to put the nameserver records into the authority section, but we
+discovered the zone doesn't have them. This indicates problem with provided
+data.
+
+% UNEXPECTED_QUERY_STATE unexpected query state
+This indicates a programming error. We generated an internal task of type
+unknown to us.
+
+% QUERY_FAIL query failed
+Some subtask of query processing failed. The reason should have been reported
+already. We are returning SERVFAIL.
+
+% QUERY_BAD_REFERRAL bad referral to '%1'
+We discovered that the domain lives in another zone. But we are not able to
+generate referral information to it.
+
+% QUERY_WILDCARD_FAIL error processing wildcard for '%1'
+We tried to find a wildcard to cover the domain, but there happened to be
+some (hopefully already reported) error for it.
+
+% QUERY_MISSING_SOA the zone '%1' has no SOA
+We tried to answer negatively, but there's no SOA record in the zone.
+
+% QUERY_PROVENX_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.
+
+% QUERY_UNKNOWN_RESULT unknown result of subtask
+This indicates a programmer error. The answer of subtask doesn't look like
+anything we would know.
+
+% META_ADD adding a data source into meta data source
+Debug information. We add yet another data source into the meta data source
+(probably at startup or reconfiguration).
+
+% META_ADD_CLASS_MISMATCH mismatch between classes '%1' and '%2'
+We tried to add a data source of one class into a meta data source of a
+different type. The types must be the same.
+
+% META_REMOVE removing data source from meta data source
+Debug information. We take a data source out of meta data source (probably
+at shutdown or reconfiguration).
+
+% MEM_ADD_WILDCARD adding wildcards for '%1'
+Debug information. We need some special marks above each * in wildcard name
+in the in-memory data source. We are adding the marks for this name now.
+
+% MEM_CNAME_TO_NONEMPTY can't add CNAME to domain with other data in '%1'
+Someone or something tried to add a CNAME into a domain that already contains
+some other data. But the protocol forbids coexistence of CNAME with anything
+(RFC 1034, section 3.6.2). This indicates a problem with provided data.
+
+% MEM_CNAME_COEXIST can't add data to CNAME in domain '%1'
+This is the same problem as in MEM_CNAME_TO_NONEMPTY, but it happened the
+other way around -- adding some outher data to CNAME.
+
+% 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.
+
+% MEM_SINGLETON trying to add multiple RRs for domain '%1' and type '%2'
+Some resource types are singletons -- only one is allowed in a domain
+(for example CNAME or SOA). This indicates a problem with provided data.
+
+% MEM_OUT_OF_ZONE domain '%1' doesn't belong to zone '%2'
+It was attempted to add the domain into a zone that shouldn't have it
+(eg. the domain is not subdomain of the zone origin). This indicates a
+problem with provided data.
+
+% MEM_WILDCARD_NS nS record in wildcard domain '%1'
+We refuse to load NS record into a wildcard domain. It is'n explicitly
+forbidden, but the protocol is ambiguous about how this should behave and
+BIND 9 refuses that as well. We don't like your zone, please describe it
+using different tools.
+
+% MEM_WILDCARD_DNAME dNAME record in wildcard domain '%1'
+We refuse to load DNAME record into a wildcard domain. It is'n explicitly
+forbidden, but the protocol is ambiguous about how this should behave and
+BIND 9 refuses that as well. We don't like your zone, please describe it
+using different tools.
+
+% MEM_ADD_RRSET adding RRset '%1/%2' into zone '%3'
+Debug information. We're adding an RRset to the zone of in-memory data
+source.
+
+% MEM_DUP_RRSET duplicate RRset '%1/%2'
+An RRset is being inserted into in-memory data source for a second time.
+The original version must be removed first. Note that we don't support
+loading master files where an RRset is split into multiple locations yet.
+
+% MEM_DNAME_ENCOUNTERED encountered a DNAME
+Debug information. While searching for the requested domain, we encountered
+a DNAME on the way. This may lead to redirection to a different domain and
+stop the search.
+
+% MEM_NS_ENCOUNTERED encountered a NS
+Debug information. While searching for the requested domain, we encountered
+a NS on the way (a delegation). This may lead to stop of the search.
+
+% MEM_RENAME renaming RRset from '%1' to '%2'
+Debug information. We generate an RRset from a different RRset (most probably
+a wildcard). So we need to rename it to whatever the user asked for. In fact,
+we can't rename RRset (it's not possible with our libraries), so we create
+a new one and copy everything.
+
+% MEM_FIND find '%1/%2'
+Debug information. We're going to search the in-memory data source to find
+requestet RRset.
+
+% MEM_DNAME_FOUND dNAME found at '%1'
+Debug information. We found a DNAME instead of the requested record.
+
+% MEM_DELEG_FOUND delegation found at '%1'
+Debug information. We found a delegation point above the requested record.
+
+% MEM_SUPER_STOP stopped at superdomain, domain is empty
+Debug information. The search stopped at a superdomain of the requested
+domain. The domain is a empty nonterminal, therefore we treat it as NXRRSET
+case (eg. the domain exists, but it doesn't have the requested record type).
+
+% MEM_WILDCARD_CANCEL wildcard match canceled
+Debug information. We reached a domain above wildcard, but there's something
+below the requested domain. Therefore the wildcard doesn't apply here.
+This behaviour is specified by RFC 1034, section 4.3.3
+
+% MEM_NOTFOUND requested domain not found
+Debug information. The requested domain does not exist.
+
+% MEM_DOMAIN_EMPTY requested domain is empty
+Debug information. The requested domain exists in the tree of domains, but
+it is empty. Therefore it doesn't contain the requested resource type.
+
+% MEM_EXACT_DELEGATION delegation at the exact domain
+Debug information. There's a NS record at the requested domain. This means
+this zone is not authoritative for the requested domain, but a delegation
+should be followed. The requested domain is an apex of some zone.
+
+% MEM_ANY_SUCCESS aNY query successful
+Debug information. The domain was found and we answer an ANY type query by
+providing everything we found inside the domain.
+
+% MEM_SUCCESS query successful
+Debug information. We found the record we searched for.
+
+% MEM_CNAME cNAME at the domain
+Debug information. The requested domain is an alias to a different domain,
+returning the CNAME instead.
+
+% MEM_NXRRSET no such type
+Debug information. The domain exists, but it doesn't hold any record of the
+requested type.
+
+% MEM_CREATE creating zone '%1' in '%2' class
+Debug information. We're creating representation of a zone for the in-memory
+data source.
+
+% MEM_DESTROY destroying zone '%1' in '%2' class
+Debug information. We're destroying the representation of zone in the
+in-memory data source.
+
+% MEM_LOAD loading zone '%1' from file '%2'
+Debug information. We're loading the content of zone from a master file.
+
+% MEM_SWAP swapping contents of two zone representations ('%1' and '%2')
+Debug information. We exchange contents of the zones. This is usual practice
+to do some manipulation in exception-safe manner -- we prepare the new data
+in a different zone object and when it works, we swap it with the old, then
+we can safely destroy the old one.
+
+% MEM_ADD_ZONE adding zone '%1/%2'
+Debug information. We're adding this zone into the in-memory data source.
+
+% MEM_FIND_ZONE looking for zone '%1'
+Debug information. We're looking for a zone in the in-memory data source.
+
+% STATIC_CREATE creating the static datasource
+Debug information. We're creating the static data source (the one holding
+stuff like version.bind).
+
+% 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.
+
+% STATIC_FIND looking for '%1/%2'
+Debug information. We're looking for this resource record set in the static
+data source.
+
+% SQLITE_FINDREC looking for record '%1/%2'
+Debug information. The SQLite data source is looking up records of given name
+and type in the database.
+
+% SQLITE_ENCLOSURE looking for zone containing '%1'
+Debug information. The SQLite data source is trying to identify, which zone
+should hold this domain.
+
+% SQLITE_ENCLOSURE_BAD_CLASS class mismatch looking for a zone ('%1' and '%2')
+The SQLite data source can handle only one class at a time and it was asked
+to identify which zone is holding data of a different class.
+
+% SQLITE_ENCLOSURE_NOTFOUND no zone contains it
+Debug information. The last SQLITE_ENCLOSURE query was unsuccessful, there's
+no such zone in our data.
+
+% SQLITE_PREVIOUS looking for name previous to '%1'
+Debug information. We're trying to look up name preceding the supplied one.
+
+% 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.
+
+% SQLITE_FIND_NSEC3 looking for NSEC3 in zone '%1' for hash '%2'
+Debug information. We're trying to look up a NSEC3 record in the SQLite data
+source.
+
+% SQLITE_FIND_NSEC3_NO_ZONE no such zone '%1'
+The SQLite data source was asked to provide a NSEC3 record for given zone.
+But it doesn't contain that zone.
+
+% SQLITE_FIND looking for RRset '%1/%2'
+Debug information. The SQLite data source is looking up a resource record
+set.
+
+% SQLITE_FIND_BAD_CLASS class mismatch looking for an RRset ('%1' and '%2')
+The SQLite data source was looking up an RRset, but the data source contains
+different class than the query was for.
+
+% SQLITE_FINDEXACT looking for exact RRset '%1/%2'
+Debug information. The SQLite data source is looking up an exact resource
+record.
+
+% SQLITE_FINDEXACT_BAD_CLASS class mismatch looking for an RRset ('%1' and '%2')
+The SQLite data source was looking up an exact RRset, but the data source
+contains different class than the query was for.
+
+% SQLITE_FINDADDRS looking for A/AAAA addresses for '%1'
+Debug information. The data source is looking up the addresses for given
+domain name.
+
+% SQLITE_FINDADDRS_BAD_CLASS class mismatch looking for addresses ('%1' and '%2')
+The SQLite data source was looking up A/AAAA addresses, but the data source
+contains different class than the query was for.
+
+% SQLITE_FINDREF looking for referral at '%1'
+Debug information. The SQLite data source is identifying if this domain is
+a referral and where it goes.
+
+% SQLITE_FINDREF_BAD_CLASS class mismatch looking for referral ('%1' and '%2')
+The SQLite data source was trying to identify, if there's a referral. But the
+but it contains different class than the query was for.
+
+% SQLITE_CREATE sQLite data source created
+Debug information. We're creating an instance of the SQLite data source.
+
+% SQLITE_DESTROY sQLite data source destroyed
+Debug information. We're destroying an instance of SQLite data source.
+
+% SQLITE_SETUP setting up SQLite database
+The database for SQLite data source was found empty. So we're assuming this
+is the first run and we initialize it with current schema. It'll still
+contain no data, but it will be ready for use.
+
+% SQLITE_OPEN opening SQLite database '%1'
+Debug information. The SQLite data source is loading an SQLite database in
+the provide file.
+
+% SQLITE_CLOSE closing SQLite database
+Debug information. The SQLite data source is closing the database file.
diff --git a/src/lib/dns/Makefile.am b/src/lib/dns/Makefile.am
index 300cd92..887ac09 100644
--- a/src/lib/dns/Makefile.am
+++ b/src/lib/dns/Makefile.am
@@ -87,6 +87,7 @@ libdns___la_SOURCES += question.h question.cc
libdns___la_SOURCES += tsig.h tsig.cc
libdns___la_SOURCES += tsigerror.h tsigerror.cc
libdns___la_SOURCES += tsigkey.h tsigkey.cc
+libdns___la_SOURCES += tsigrecord.h tsigrecord.cc
libdns___la_SOURCES += rdata/generic/detail/nsec_bitmap.h
libdns___la_SOURCES += rdata/generic/detail/nsec_bitmap.cc
diff --git a/src/lib/dns/edns.cc b/src/lib/dns/edns.cc
index 5405dab..447b479 100644
--- a/src/lib/dns/edns.cc
+++ b/src/lib/dns/edns.cc
@@ -110,19 +110,25 @@ EDNS::toText() const {
return (ret);
}
+namespace {
+/// Helper function to define unified implementation for the public versions
+/// of toWire().
template <typename Output>
int
-EDNS::toWire(Output& output, const uint8_t extended_rcode) const {
+toWireCommon(Output& output, const uint8_t version,
+ const uint16_t udp_size, const bool dnssec_aware,
+ const uint8_t extended_rcode)
+{
// Render EDNS OPT RR
uint32_t extrcode_flags = extended_rcode << EXTRCODE_SHIFT;
- extrcode_flags |= (version_ << VERSION_SHIFT) & VERSION_MASK;
- if (dnssec_aware_) {
+ extrcode_flags |= (version << VERSION_SHIFT) & VERSION_MASK;
+ if (dnssec_aware) {
extrcode_flags |= EXTFLAG_DO;
}
// Construct an RRset corresponding to the EDNS.
// We don't support any options for now, so the OPT RR can be empty.
- RRsetPtr edns_rrset(new RRset(Name::ROOT_NAME(), RRClass(udp_size_),
+ RRsetPtr edns_rrset(new RRset(Name::ROOT_NAME(), RRClass(udp_size),
RRType::OPT(), RRTTL(extrcode_flags)));
edns_rrset->addRdata(ConstRdataPtr(new generic::OPT()));
@@ -130,9 +136,12 @@ EDNS::toWire(Output& output, const uint8_t extended_rcode) const {
return (1);
}
+}
unsigned int
-EDNS::toWire(MessageRenderer& renderer, const uint8_t extended_rcode) const {
+EDNS::toWire(AbstractMessageRenderer& renderer,
+ const uint8_t extended_rcode) const
+{
// If adding the OPT RR would exceed the size limit, don't do it.
// 11 = len(".") + type(2byte) + class(2byte) + TTL(4byte) + RDLEN(2byte)
// (RDATA is empty in this simple implementation)
@@ -140,12 +149,16 @@ EDNS::toWire(MessageRenderer& renderer, const uint8_t extended_rcode) const {
return (0);
}
- return (toWire<MessageRenderer>(renderer, extended_rcode));
+ return (toWireCommon(renderer, version_, udp_size_, dnssec_aware_,
+ extended_rcode));
}
unsigned int
-EDNS::toWire(isc::util::OutputBuffer& buffer, const uint8_t extended_rcode) const {
- return (toWire<isc::util::OutputBuffer>(buffer, extended_rcode));
+EDNS::toWire(isc::util::OutputBuffer& buffer,
+ const uint8_t extended_rcode) const
+{
+ return (toWireCommon(buffer, version_, udp_size_, dnssec_aware_,
+ extended_rcode));
}
EDNS*
diff --git a/src/lib/dns/edns.h b/src/lib/dns/edns.h
index 5731b95..a7bc4c4 100644
--- a/src/lib/dns/edns.h
+++ b/src/lib/dns/edns.h
@@ -32,7 +32,7 @@ namespace dns {
class EDNS;
class Name;
-class MessageRenderer;
+class AbstractMessageRenderer;
class RRClass;
class RRTTL;
class RRType;
@@ -314,7 +314,7 @@ public:
/// \param extended_rcode Upper 8 bits of extended RCODE to be rendered as
/// part of the EDNS OPT RR.
/// \return 1 if the OPT RR fits in the message size limit; otherwise 0.
- unsigned int toWire(MessageRenderer& renderer,
+ unsigned int toWire(AbstractMessageRenderer& renderer,
const uint8_t extended_rcode) const;
/// \brief Render the \c EDNS in the wire format.
@@ -354,12 +354,6 @@ public:
// something like this.
//void addOption();
-private:
- /// Helper method to define unified implementation for the public versions
- /// of toWire().
- template <typename Output>
- int toWire(Output& output, const uint8_t extended_rcode) const;
-
public:
/// \brief The highest EDNS version this implementation supports.
static const uint8_t SUPPORTED_VERSION = 0;
diff --git a/src/lib/dns/message.cc b/src/lib/dns/message.cc
index d1025d1..bf7ccd5 100644
--- a/src/lib/dns/message.cc
+++ b/src/lib/dns/message.cc
@@ -15,6 +15,7 @@
#include <stdint.h>
#include <algorithm>
+#include <cassert>
#include <string>
#include <sstream>
#include <vector>
@@ -40,6 +41,7 @@
#include <dns/rrtype.h>
#include <dns/rrttl.h>
#include <dns/rrset.h>
+#include <dns/tsig.h>
using namespace std;
using namespace boost;
@@ -81,7 +83,7 @@ const unsigned int HEADERFLAG_MASK = 0x87b0;
const uint16_t MESSAGE_REPLYPRESERVE = (Message::HEADERFLAG_RD |
Message::HEADERFLAG_CD);
-const char *sectiontext[] = {
+const char* const sectiontext[] = {
"QUESTION",
"ANSWER",
"AUTHORITY",
@@ -114,8 +116,8 @@ public:
vector<QuestionPtr> questions_;
vector<RRsetPtr> rrsets_[NUM_SECTIONS];
ConstEDNSPtr edns_;
+ ConstTSIGRecordPtr tsig_rr_;
- // tsig/sig0: TODO
// RRsetsSorter* sorter_; : TODO
void init();
@@ -123,6 +125,17 @@ public:
void setRcode(const Rcode& rcode);
int parseQuestion(InputBuffer& buffer);
int parseSection(const Message::Section section, InputBuffer& buffer);
+ void addRR(Message::Section section, const Name& name,
+ const RRClass& rrclass, const RRType& rrtype,
+ const RRTTL& ttl, ConstRdataPtr rdata);
+ void addEDNS(Message::Section section, const Name& name,
+ const RRClass& rrclass, const RRType& rrtype,
+ const RRTTL& ttl, const Rdata& rdata);
+ void addTSIG(Message::Section section, unsigned int count,
+ const InputBuffer& buffer, size_t start_position,
+ const Name& name, const RRClass& rrclass,
+ const RRTTL& ttl, const Rdata& rdata);
+ void toWire(AbstractMessageRenderer& renderer, TSIGContext* tsig_ctx);
};
MessageImpl::MessageImpl(Message::Mode mode) :
@@ -140,6 +153,7 @@ MessageImpl::init() {
rcode_ = NULL;
opcode_ = NULL;
edns_ = EDNSPtr();
+ tsig_rr_ = ConstTSIGRecordPtr();
for (int i = 0; i < NUM_SECTIONS; ++i) {
counts_[i] = 0;
@@ -164,6 +178,154 @@ MessageImpl::setRcode(const Rcode& rcode) {
rcode_ = &rcode_placeholder_;
}
+namespace {
+// This helper class is used by MessageImpl::toWire() to render a set of
+// RRsets of a specific section of message to a given MessageRenderer.
+//
+// A RenderSection object is expected to be used with a QuestionIterator or
+// SectionIterator. Its operator() is called for each RRset as the iterator
+// iterates over the corresponding section, and it renders the RRset to
+// the given MessageRenderer, while counting the number of RRs (note: not
+// RRsets) successfully rendered. If the MessageRenderer reports the need
+// for truncation (via its isTruncated() method), the RenderSection object
+// stops rendering further RRsets. In addition, unless partial_ok (given on
+// construction) is true, it removes any RRs that are partially rendered
+// from the MessageRenderer.
+//
+// On the completion of rendering the entire section, the owner of the
+// RenderSection object can get the number of rendered RRs via the
+// getTotalCount() method.
+template <typename T>
+struct RenderSection {
+ RenderSection(AbstractMessageRenderer& renderer, const bool partial_ok) :
+ counter_(0), renderer_(renderer), partial_ok_(partial_ok),
+ truncated_(false)
+ {}
+ void operator()(const T& entry) {
+ // If it's already truncated, ignore the rest of the section.
+ if (truncated_) {
+ return;
+ }
+ const size_t pos0 = renderer_.getLength();
+ counter_ += entry->toWire(renderer_);
+ if (renderer_.isTruncated()) {
+ truncated_ = true;
+ if (!partial_ok_) {
+ // roll back to the end of the previous RRset.
+ renderer_.trim(renderer_.getLength() - pos0);
+ }
+ }
+ }
+ unsigned int getTotalCount() { return (counter_); }
+ unsigned int counter_;
+ AbstractMessageRenderer& renderer_;
+ const bool partial_ok_;
+ bool truncated_;
+};
+}
+
+void
+MessageImpl::toWire(AbstractMessageRenderer& renderer, TSIGContext* tsig_ctx) {
+ if (mode_ != Message::RENDER) {
+ isc_throw(InvalidMessageOperation,
+ "Message rendering attempted in non render mode");
+ }
+ if (rcode_ == NULL) {
+ isc_throw(InvalidMessageOperation,
+ "Message rendering attempted without Rcode set");
+ }
+ if (opcode_ == NULL) {
+ isc_throw(InvalidMessageOperation,
+ "Message rendering attempted without Opcode set");
+ }
+
+ // reserve room for the header
+ renderer.skip(HEADERLEN);
+
+ uint16_t qdcount =
+ for_each(questions_.begin(), questions_.end(),
+ RenderSection<QuestionPtr>(renderer, false)).getTotalCount();
+
+ // TODO: sort RRsets in each section based on configuration policy.
+ uint16_t ancount = 0;
+ if (!renderer.isTruncated()) {
+ ancount =
+ for_each(rrsets_[Message::SECTION_ANSWER].begin(),
+ rrsets_[Message::SECTION_ANSWER].end(),
+ RenderSection<RRsetPtr>(renderer, true)).getTotalCount();
+ }
+ uint16_t nscount = 0;
+ if (!renderer.isTruncated()) {
+ nscount =
+ for_each(rrsets_[Message::SECTION_AUTHORITY].begin(),
+ rrsets_[Message::SECTION_AUTHORITY].end(),
+ RenderSection<RRsetPtr>(renderer, true)).getTotalCount();
+ }
+ uint16_t arcount = 0;
+ if (renderer.isTruncated()) {
+ flags_ |= Message::HEADERFLAG_TC;
+ } else {
+ arcount =
+ for_each(rrsets_[Message::SECTION_ADDITIONAL].begin(),
+ rrsets_[Message::SECTION_ADDITIONAL].end(),
+ RenderSection<RRsetPtr>(renderer, false)).getTotalCount();
+ }
+
+ // Add EDNS OPT RR if necessary. Basically, we add it only when EDNS
+ // has been explicitly set. However, if the RCODE would require it and
+ // no EDNS has been set we generate a temporary local EDNS and use it.
+ if (!renderer.isTruncated()) {
+ ConstEDNSPtr local_edns = edns_;
+ if (!local_edns && rcode_->getExtendedCode() != 0) {
+ local_edns = ConstEDNSPtr(new EDNS());
+ }
+ if (local_edns) {
+ arcount += local_edns->toWire(renderer, rcode_->getExtendedCode());
+ }
+ }
+
+ // 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
+ // was inserted. This is not good, and we should revisit the entire
+ // design.
+ counts_[Message::SECTION_QUESTION] = qdcount;
+ counts_[Message::SECTION_ANSWER] = ancount;
+ counts_[Message::SECTION_AUTHORITY] = nscount;
+ counts_[Message::SECTION_ADDITIONAL] = arcount;
+
+ // fill in the header
+ size_t header_pos = 0;
+ renderer.writeUint16At(qid_, header_pos);
+ header_pos += sizeof(uint16_t);
+
+ uint16_t codes_and_flags =
+ (opcode_->getCode() << OPCODE_SHIFT) & OPCODE_MASK;
+ codes_and_flags |= (rcode_->getCode() & RCODE_MASK);
+ codes_and_flags |= (flags_ & HEADERFLAG_MASK);
+ renderer.writeUint16At(codes_and_flags, header_pos);
+ header_pos += sizeof(uint16_t);
+ // TODO: should avoid repeated pattern
+ renderer.writeUint16At(qdcount, header_pos);
+ header_pos += sizeof(uint16_t);
+ renderer.writeUint16At(ancount, header_pos);
+ header_pos += sizeof(uint16_t);
+ renderer.writeUint16At(nscount, header_pos);
+ header_pos += sizeof(uint16_t);
+ 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);
+
+ // update the ARCOUNT for the TSIG RR. Note that for a sane DNS
+ // message arcount should never overflow to 0.
+ renderer.writeUint16At(++arcount, header_pos);
+ }
+}
+
Message::Message(Mode mode) :
impl_(new MessageImpl(mode))
{}
@@ -262,6 +424,16 @@ Message::setEDNS(ConstEDNSPtr edns) {
impl_->edns_ = edns;
}
+const TSIGRecord*
+Message::getTSIGRecord() const {
+ if (impl_->mode_ != Message::PARSE) {
+ isc_throw(InvalidMessageOperation,
+ "getTSIGRecord performed in non-parse mode");
+ }
+
+ return (impl_->tsig_rr_.get());
+}
+
unsigned int
Message::getRRCount(const Section section) const {
if (section >= MessageImpl::NUM_SECTIONS) {
@@ -363,129 +535,14 @@ Message::addQuestion(const Question& question) {
addQuestion(QuestionPtr(new Question(question)));
}
-namespace {
-template <typename T>
-struct RenderSection {
- RenderSection(MessageRenderer& renderer, const bool partial_ok) :
- counter_(0), renderer_(renderer), partial_ok_(partial_ok),
- truncated_(false)
- {}
- void operator()(const T& entry) {
- // If it's already truncated, ignore the rest of the section.
- if (truncated_) {
- return;
- }
- const size_t pos0 = renderer_.getLength();
- counter_ += entry->toWire(renderer_);
- if (renderer_.isTruncated()) {
- truncated_ = true;
- if (!partial_ok_) {
- // roll back to the end of the previous RRset.
- renderer_.trim(renderer_.getLength() - pos0);
- }
- }
- }
- unsigned int getTotalCount() { return (counter_); }
- unsigned int counter_;
- MessageRenderer& renderer_;
- const bool partial_ok_;
- bool truncated_;
-};
+void
+Message::toWire(AbstractMessageRenderer& renderer) {
+ impl_->toWire(renderer, NULL);
}
void
-Message::toWire(MessageRenderer& renderer) {
- if (impl_->mode_ != Message::RENDER) {
- isc_throw(InvalidMessageOperation,
- "Message rendering attempted in non render mode");
- }
- if (impl_->rcode_ == NULL) {
- isc_throw(InvalidMessageOperation,
- "Message rendering attempted without Rcode set");
- }
- if (impl_->opcode_ == NULL) {
- isc_throw(InvalidMessageOperation,
- "Message rendering attempted without Opcode set");
- }
-
- // reserve room for the header
- renderer.skip(HEADERLEN);
-
- uint16_t qdcount =
- for_each(impl_->questions_.begin(), impl_->questions_.end(),
- RenderSection<QuestionPtr>(renderer, false)).getTotalCount();
-
- // TBD: sort RRsets in each section based on configuration policy.
- uint16_t ancount = 0;
- if (!renderer.isTruncated()) {
- ancount =
- for_each(impl_->rrsets_[SECTION_ANSWER].begin(),
- impl_->rrsets_[SECTION_ANSWER].end(),
- RenderSection<RRsetPtr>(renderer, true)).getTotalCount();
- }
- uint16_t nscount = 0;
- if (!renderer.isTruncated()) {
- nscount =
- for_each(impl_->rrsets_[SECTION_AUTHORITY].begin(),
- impl_->rrsets_[SECTION_AUTHORITY].end(),
- RenderSection<RRsetPtr>(renderer, true)).getTotalCount();
- }
- uint16_t arcount = 0;
- if (renderer.isTruncated()) {
- setHeaderFlag(HEADERFLAG_TC, true);
- } else {
- arcount =
- for_each(impl_->rrsets_[SECTION_ADDITIONAL].begin(),
- impl_->rrsets_[SECTION_ADDITIONAL].end(),
- RenderSection<RRsetPtr>(renderer, false)).getTotalCount();
- }
-
- // Add EDNS OPT RR if necessary. Basically, we add it only when EDNS
- // has been explicitly set. However, if the RCODE would require it and
- // no EDNS has been set we generate a temporary local EDNS and use it.
- if (!renderer.isTruncated()) {
- ConstEDNSPtr local_edns = impl_->edns_;
- if (!local_edns && impl_->rcode_->getExtendedCode() != 0) {
- local_edns = ConstEDNSPtr(new EDNS());
- }
- if (local_edns) {
- arcount += local_edns->toWire(renderer,
- impl_->rcode_->getExtendedCode());
- }
- }
-
- // 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
- // was inserted. This is not good, and we should revisit the entire
- // design.
- impl_->counts_[SECTION_QUESTION] = qdcount;
- impl_->counts_[SECTION_ANSWER] = ancount;
- impl_->counts_[SECTION_AUTHORITY] = nscount;
- impl_->counts_[SECTION_ADDITIONAL] = arcount;
-
- // TBD: TSIG, SIG(0) etc.
-
- // fill in the header
- size_t header_pos = 0;
- renderer.writeUint16At(impl_->qid_, header_pos);
- header_pos += sizeof(uint16_t);
-
- uint16_t codes_and_flags =
- (impl_->opcode_->getCode() << OPCODE_SHIFT) & OPCODE_MASK;
- codes_and_flags |= (impl_->rcode_->getCode() & RCODE_MASK);
- codes_and_flags |= (impl_->flags_ & HEADERFLAG_MASK);
- renderer.writeUint16At(codes_and_flags, header_pos);
- header_pos += sizeof(uint16_t);
- // XXX: should avoid repeated pattern (TODO)
- renderer.writeUint16At(qdcount, header_pos);
- header_pos += sizeof(uint16_t);
- renderer.writeUint16At(ancount, header_pos);
- header_pos += sizeof(uint16_t);
- renderer.writeUint16At(nscount, header_pos);
- header_pos += sizeof(uint16_t);
- renderer.writeUint16At(arcount, header_pos);
- header_pos += sizeof(uint16_t);
+Message::toWire(AbstractMessageRenderer& renderer, TSIGContext& tsig_ctx) {
+ impl_->toWire(renderer, &tsig_ctx);
}
void
@@ -613,6 +670,9 @@ MessageImpl::parseSection(const Message::Section section,
unsigned int added = 0;
for (unsigned int count = 0; count < counts_[section]; ++count) {
+ // We need to remember the start position for TSIG processing
+ const size_t start_position = buffer.getPosition();
+
const Name name(buffer);
// buffer must store at least RR TYPE, RR CLASS, TTL, and RDLEN.
@@ -630,32 +690,12 @@ MessageImpl::parseSection(const Message::Section section,
ConstRdataPtr rdata = createRdata(rrtype, rrclass, buffer, rdlen);
if (rrtype == RRType::OPT()) {
- if (section != Message::SECTION_ADDITIONAL) {
- isc_throw(DNSMessageFORMERR,
- "EDNS OPT RR found in an invalid section");
- }
- if (edns_) {
- isc_throw(DNSMessageFORMERR, "multiple EDNS OPT RR found");
- }
-
- uint8_t extended_rcode;
- edns_ = ConstEDNSPtr(createEDNSFromRR(name, rrclass, rrtype, ttl,
- *rdata, extended_rcode));
- setRcode(Rcode(rcode_->getCode(), extended_rcode));
- continue;
+ addEDNS(section, name, rrclass, rrtype, ttl, *rdata);
+ } else if (rrtype == RRType::TSIG()) {
+ addTSIG(section, count, buffer, start_position, name, rrclass, ttl,
+ *rdata);
} else {
- vector<RRsetPtr>::iterator it =
- find_if(rrsets_[section].begin(), rrsets_[section].end(),
- MatchRR(name, rrtype, rrclass));
- if (it != rrsets_[section].end()) {
- (*it)->setTTL(min((*it)->getTTL(), ttl));
- (*it)->addRdata(rdata);
- } else {
- RRsetPtr rrset =
- RRsetPtr(new RRset(name, rrclass, rrtype, ttl));
- rrset->addRdata(rdata);
- rrsets_[section].push_back(rrset);
- }
+ addRR(section, name, rrclass, rrtype, ttl, rdata);
++added;
}
}
@@ -663,6 +703,65 @@ MessageImpl::parseSection(const Message::Section section,
return (added);
}
+void
+MessageImpl::addRR(Message::Section section, const Name& name,
+ const RRClass& rrclass, const RRType& rrtype,
+ const RRTTL& ttl, ConstRdataPtr rdata)
+{
+ vector<RRsetPtr>::iterator it =
+ find_if(rrsets_[section].begin(), rrsets_[section].end(),
+ MatchRR(name, rrtype, rrclass));
+ if (it != rrsets_[section].end()) {
+ (*it)->setTTL(min((*it)->getTTL(), ttl));
+ (*it)->addRdata(rdata);
+ } else {
+ RRsetPtr rrset(new RRset(name, rrclass, rrtype, ttl));
+ rrset->addRdata(rdata);
+ rrsets_[section].push_back(rrset);
+ }
+}
+
+void
+MessageImpl::addEDNS(Message::Section section, const Name& name,
+ const RRClass& rrclass, const RRType& rrtype,
+ const RRTTL& ttl, const Rdata& rdata)
+{
+ if (section != Message::SECTION_ADDITIONAL) {
+ isc_throw(DNSMessageFORMERR,
+ "EDNS OPT RR found in an invalid section");
+ }
+ if (edns_) {
+ isc_throw(DNSMessageFORMERR, "multiple EDNS OPT RR found");
+ }
+
+ uint8_t extended_rcode;
+ edns_ = ConstEDNSPtr(createEDNSFromRR(name, rrclass, rrtype, ttl, rdata,
+ extended_rcode));
+ setRcode(Rcode(rcode_->getCode(), extended_rcode));
+}
+
+void
+MessageImpl::addTSIG(Message::Section section, unsigned int count,
+ const InputBuffer& buffer, size_t start_position,
+ const Name& name, const RRClass& rrclass,
+ const RRTTL& ttl, const Rdata& rdata)
+{
+ if (section != Message::SECTION_ADDITIONAL) {
+ isc_throw(DNSMessageFORMERR,
+ "TSIG RR found in an invalid section");
+ }
+ if (count != counts_[section] - 1) {
+ isc_throw(DNSMessageFORMERR, "TSIG RR is not the last record");
+ }
+ if (tsig_rr_) {
+ isc_throw(DNSMessageFORMERR, "multiple TSIG RRs found");
+ }
+ tsig_rr_ = ConstTSIGRecordPtr(new TSIGRecord(name, rrclass,
+ ttl, rdata,
+ buffer.getPosition() -
+ start_position));
+}
+
namespace {
template <typename T>
struct SectionFormatter {
@@ -696,31 +795,31 @@ Message::toText() const {
// for simplicity we don't consider extended rcode (unlike BIND9)
s += ", status: " + impl_->rcode_->toText();
s += ", id: " + boost::lexical_cast<string>(impl_->qid_);
- s += "\n;; flags: ";
+ s += "\n;; flags:";
if (getHeaderFlag(HEADERFLAG_QR)) {
- s += "qr ";
+ s += " qr";
}
if (getHeaderFlag(HEADERFLAG_AA)) {
- s += "aa ";
+ s += " aa";
}
if (getHeaderFlag(HEADERFLAG_TC)) {
- s += "tc ";
+ s += " tc";
}
if (getHeaderFlag(HEADERFLAG_RD)) {
- s += "rd ";
+ s += " rd";
}
if (getHeaderFlag(HEADERFLAG_RA)) {
- s += "ra ";
+ s += " ra";
}
if (getHeaderFlag(HEADERFLAG_AD)) {
- s += "ad ";
+ s += " ad";
}
if (getHeaderFlag(HEADERFLAG_CD)) {
- s += "cd ";
+ s += " cd";
}
// for simplicity, don't consider the update case for now
- s += "; QUESTION: " +
+ s += "; QUERY: " + // note: not "QUESTION" to be compatible with BIND 9 dig
lexical_cast<string>(impl_->counts_[SECTION_QUESTION]);
s += ", ANSWER: " +
lexical_cast<string>(impl_->counts_[SECTION_ANSWER]);
@@ -731,6 +830,9 @@ Message::toText() const {
if (impl_->edns_ != NULL) {
++arcount;
}
+ if (impl_->tsig_rr_ != NULL) {
+ ++arcount;
+ }
s += ", ADDITIONAL: " + lexical_cast<string>(arcount) + "\n";
if (impl_->edns_ != NULL) {
@@ -767,6 +869,11 @@ Message::toText() const {
SectionFormatter<RRsetPtr>(SECTION_ADDITIONAL, s));
}
+ if (impl_->tsig_rr_ != NULL) {
+ s += "\n;; TSIG PSEUDOSECTION:\n";
+ s += impl_->tsig_rr_->toText();
+ }
+
return (s);
}
diff --git a/src/lib/dns/message.h b/src/lib/dns/message.h
index 5601299..8a657da 100644
--- a/src/lib/dns/message.h
+++ b/src/lib/dns/message.h
@@ -33,6 +33,8 @@ class InputBuffer;
}
namespace dns {
+class TSIGContext;
+class TSIGRecord;
///
/// \brief A standard DNS module exception that is thrown if a wire format
@@ -80,7 +82,7 @@ public:
typedef uint16_t qid_t;
-class MessageRenderer;
+class AbstractMessageRenderer;
class Message;
class MessageImpl;
class Opcode;
@@ -368,6 +370,25 @@ public:
/// \c Message.
void setEDNS(ConstEDNSPtr edns);
+ /// \brief Return, if any, the TSIG record contained in the received
+ /// message.
+ ///
+ /// Currently, this method is only intended to return a TSIG record
+ /// for an incoming message built via the \c fromWire() method in the
+ /// PARSE mode. A call to this method in the RENDER mode is invalid and
+ /// result in an exception. Also, calling this method is meaningless
+ /// unless \c fromWire() is performed.
+ ///
+ /// The returned pointer is valid only during the lifetime of the
+ /// \c Message object and until \c clear() is called. The \c Message
+ /// object retains the ownership of \c TSIGRecord; the caller must not
+ /// try to delete it.
+ ///
+ /// \exception InvalidMessageOperation Message is not in the PARSE mode.
+ ///
+ /// \return A pointer to the stored \c TSIGRecord or \c NULL.
+ const TSIGRecord* getTSIGRecord() const;
+
/// \brief Returns the number of RRs contained in the given section.
///
/// In the \c PARSE mode, the returned value may not be identical to
@@ -523,13 +544,31 @@ public:
/// class \c InvalidMessageOperation will be thrown.
std::string toText() const;
- /// \brief Render the message in wire formant into a \c MessageRenderer
+ /// \brief Render the message in wire formant into a message renderer
/// object.
///
/// This \c Message must be in the \c RENDER mode and both \c Opcode and
/// \c Rcode must have been set beforehand; otherwise, an exception of
/// class \c InvalidMessageOperation will be thrown.
- void toWire(MessageRenderer& renderer);
+ ///
+ /// \param renderer DNS message rendering context that encapsulates the
+ /// output buffer and name compression information.
+ void toWire(AbstractMessageRenderer& renderer);
+
+ /// \brief Render the message in wire formant into a message renderer
+ /// object with TSIG.
+ ///
+ /// This method is similar to the other version of \c toWire(), but
+ /// it will also add a TSIG RR with (in many cases) the TSIG MAC for
+ /// the message along with the given TSIG context (\c tsig_ctx).
+ /// The TSIG RR will be placed at the end of \c renderer.
+ /// \c tsig_ctx will be updated based on the fact it was used for signing
+ /// and with the latest MAC.
+ ///
+ /// \param renderer See the other version
+ /// \param tsig_ctx A TSIG context that is to be used for signing the
+ /// message
+ void toWire(AbstractMessageRenderer& renderer, TSIGContext& tsig_ctx);
/// \brief Parse the header section of the \c Message.
void parseHeader(isc::util::InputBuffer& buffer);
@@ -563,6 +602,16 @@ private:
/// that originated the asynchronous call falls out of scope.
typedef boost::shared_ptr<Message> MessagePtr;
+/// Insert the \c Message as a string into stream.
+///
+/// This method convert \c message into a string and inserts it into the
+/// output stream \c os.
+///
+/// \param os A \c std::ostream object on which the insertion operation is
+/// performed.
+/// \param record A \c Message object output by the operation.
+/// \return A reference to the same \c std::ostream object referenced by
+/// parameter \c os after the insertion operation.
std::ostream& operator<<(std::ostream& os, const Message& message);
}
}
diff --git a/src/lib/dns/python/Makefile.am b/src/lib/dns/python/Makefile.am
index 9d171cd..9162f4e 100644
--- a/src/lib/dns/python/Makefile.am
+++ b/src/lib/dns/python/Makefile.am
@@ -25,6 +25,7 @@ EXTRA_DIST += rrttl_python.cc
EXTRA_DIST += rdata_python.cc
EXTRA_DIST += rrtype_python.cc
EXTRA_DIST += tsigkey_python.cc
+EXTRA_DIST += tsig_python.cc
# Python prefers .so, while some OSes (specifically MacOS) use a different
# suffix for dynamic objects. -module is necessary to work this around.
diff --git a/src/lib/dns/python/message_python.cc b/src/lib/dns/python/message_python.cc
index 058312e..e3cc53f 100644
--- a/src/lib/dns/python/message_python.cc
+++ b/src/lib/dns/python/message_python.cc
@@ -17,15 +17,16 @@
using namespace isc::dns;
using namespace isc::util;
+namespace {
//
// Declaration of the custom exceptions
// Initialization and addition of these go in the initModulePart
// function at the end of this file
//
-static PyObject* po_MessageTooShort;
-static PyObject* po_InvalidMessageSection;
-static PyObject* po_InvalidMessageOperation;
-static PyObject* po_InvalidMessageUDPSize;
+PyObject* po_MessageTooShort;
+PyObject* po_InvalidMessageSection;
+PyObject* po_InvalidMessageOperation;
+PyObject* po_InvalidMessageUDPSize;
//
// Definition of the classes
@@ -36,10 +37,6 @@ static PyObject* po_InvalidMessageUDPSize;
// and a type description
//
-// Section
-//
-
-//
// Message
//
@@ -55,36 +52,36 @@ public:
//
// General creation and destruction
-static int Message_init(s_Message* self, PyObject* args);
-static void Message_destroy(s_Message* self);
-
-static PyObject* Message_getHeaderFlag(s_Message* self, PyObject* args);
-static PyObject* Message_setHeaderFlag(s_Message* self, PyObject* args);
-static PyObject* Message_getQid(s_Message* self);
-static PyObject* Message_setQid(s_Message* self, PyObject* args);
-static PyObject* Message_getRcode(s_Message* self);
-static PyObject* Message_setRcode(s_Message* self, PyObject* args);
-static PyObject* Message_getOpcode(s_Message* self);
-static PyObject* Message_setOpcode(s_Message* self, PyObject* args);
-static PyObject* Message_getEDNS(s_Message* self);
-static PyObject* Message_setEDNS(s_Message* self, PyObject* args);
-static PyObject* Message_getRRCount(s_Message* self, PyObject* args);
+int Message_init(s_Message* self, PyObject* args);
+void Message_destroy(s_Message* self);
+
+PyObject* Message_getHeaderFlag(s_Message* self, PyObject* args);
+PyObject* Message_setHeaderFlag(s_Message* self, PyObject* args);
+PyObject* Message_getQid(s_Message* self);
+PyObject* Message_setQid(s_Message* self, PyObject* args);
+PyObject* Message_getRcode(s_Message* self);
+PyObject* Message_setRcode(s_Message* self, PyObject* args);
+PyObject* Message_getOpcode(s_Message* self);
+PyObject* Message_setOpcode(s_Message* self, PyObject* args);
+PyObject* Message_getEDNS(s_Message* self);
+PyObject* Message_setEDNS(s_Message* self, PyObject* args);
+PyObject* Message_getRRCount(s_Message* self, PyObject* args);
// use direct iterators for these? (or simply lists for now?)
-static PyObject* Message_getQuestion(s_Message* self);
-static PyObject* Message_getSection(s_Message* self, PyObject* args);
+PyObject* Message_getQuestion(s_Message* self);
+PyObject* Message_getSection(s_Message* self, PyObject* args);
//static PyObject* Message_beginQuestion(s_Message* self, PyObject* args);
//static PyObject* Message_endQuestion(s_Message* self, PyObject* args);
//static PyObject* Message_beginSection(s_Message* self, PyObject* args);
//static PyObject* Message_endSection(s_Message* self, PyObject* args);
-static PyObject* Message_addQuestion(s_Message* self, PyObject* args);
-static PyObject* Message_addRRset(s_Message* self, PyObject* args);
-static PyObject* Message_clear(s_Message* self, PyObject* args);
-static PyObject* Message_makeResponse(s_Message* self);
-static PyObject* Message_toText(s_Message* self);
-static PyObject* Message_str(PyObject* self);
-static PyObject* Message_toWire(s_Message* self, PyObject* args);
-static PyObject* Message_fromWire(s_Message* self, PyObject* args);
+PyObject* Message_addQuestion(s_Message* self, PyObject* args);
+PyObject* Message_addRRset(s_Message* self, PyObject* args);
+PyObject* Message_clear(s_Message* self, PyObject* args);
+PyObject* Message_makeResponse(s_Message* self);
+PyObject* Message_toText(s_Message* self);
+PyObject* Message_str(PyObject* self);
+PyObject* Message_toWire(s_Message* self, PyObject* args);
+PyObject* Message_fromWire(s_Message* self, PyObject* args);
// This list contains the actual set of functions we have in
// python. Each entry has
@@ -92,7 +89,7 @@ static PyObject* Message_fromWire(s_Message* self, PyObject* args);
// 2. Our static function here
// 3. Argument type
// 4. Documentation
-static PyMethodDef Message_methods[] = {
+PyMethodDef Message_methods[] = {
{ "get_header_flag", reinterpret_cast<PyCFunction>(Message_getHeaderFlag),
METH_VARARGS,
"Return whether the specified header flag bit is set in the "
@@ -175,7 +172,7 @@ static PyMethodDef Message_methods[] = {
// This defines the complete type for reflection in python and
// parsing of PyObject* to s_Message
// Most of the functions are not actually implemented and NULL here.
-static PyTypeObject message_type = {
+PyTypeObject message_type = {
PyVarObject_HEAD_INIT(NULL, 0)
"pydnspp.Message",
sizeof(s_Message), // tp_basicsize
@@ -225,7 +222,7 @@ static PyTypeObject message_type = {
0 // tp_version_tag
};
-static int
+int
Message_init(s_Message* self, PyObject* args) {
int i;
@@ -248,14 +245,14 @@ Message_init(s_Message* self, PyObject* args) {
return (-1);
}
-static void
+void
Message_destroy(s_Message* self) {
delete self->message;
self->message = NULL;
Py_TYPE(self)->tp_free(self);
}
-static PyObject*
+PyObject*
Message_getHeaderFlag(s_Message* self, PyObject* args) {
unsigned int messageflag;
if (!PyArg_ParseTuple(args, "I", &messageflag)) {
@@ -273,7 +270,7 @@ Message_getHeaderFlag(s_Message* self, PyObject* args) {
}
}
-static PyObject*
+PyObject*
Message_setHeaderFlag(s_Message* self, PyObject* args) {
long messageflag;
PyObject *on = Py_True;
@@ -304,12 +301,12 @@ Message_setHeaderFlag(s_Message* self, PyObject* args) {
}
}
-static PyObject*
+PyObject*
Message_getQid(s_Message* self) {
return (Py_BuildValue("I", self->message->getQid()));
}
-static PyObject*
+PyObject*
Message_setQid(s_Message* self, PyObject* args) {
long id;
if (!PyArg_ParseTuple(args, "l", &id)) {
@@ -333,7 +330,7 @@ Message_setQid(s_Message* self, PyObject* args) {
}
}
-static PyObject*
+PyObject*
Message_getRcode(s_Message* self) {
s_Rcode* rcode;
@@ -356,7 +353,7 @@ Message_getRcode(s_Message* self) {
return (rcode);
}
-static PyObject*
+PyObject*
Message_setRcode(s_Message* self, PyObject* args) {
s_Rcode* rcode;
if (!PyArg_ParseTuple(args, "O!", &rcode_type, &rcode)) {
@@ -371,7 +368,7 @@ Message_setRcode(s_Message* self, PyObject* args) {
}
}
-static PyObject*
+PyObject*
Message_getOpcode(s_Message* self) {
s_Opcode* opcode;
@@ -394,7 +391,7 @@ Message_getOpcode(s_Message* self) {
return (opcode);
}
-static PyObject*
+PyObject*
Message_setOpcode(s_Message* self, PyObject* args) {
s_Opcode* opcode;
if (!PyArg_ParseTuple(args, "O!", &opcode_type, &opcode)) {
@@ -409,7 +406,7 @@ Message_setOpcode(s_Message* self, PyObject* args) {
}
}
-static PyObject*
+PyObject*
Message_getEDNS(s_Message* self) {
s_EDNS* edns;
EDNS* edns_body;
@@ -429,7 +426,7 @@ Message_getEDNS(s_Message* self) {
return (edns);
}
-static PyObject*
+PyObject*
Message_setEDNS(s_Message* self, PyObject* args) {
s_EDNS* edns;
if (!PyArg_ParseTuple(args, "O!", &edns_type, &edns)) {
@@ -444,7 +441,7 @@ Message_setEDNS(s_Message* self, PyObject* args) {
}
}
-static PyObject*
+PyObject*
Message_getRRCount(s_Message* self, PyObject* args) {
unsigned int section;
if (!PyArg_ParseTuple(args, "I", §ion)) {
@@ -463,7 +460,7 @@ Message_getRRCount(s_Message* self, PyObject* args) {
}
// TODO use direct iterators for these? (or simply lists for now?)
-static PyObject*
+PyObject*
Message_getQuestion(s_Message* self) {
QuestionIterator qi, qi_end;
try {
@@ -502,7 +499,7 @@ Message_getQuestion(s_Message* self) {
return (list);
}
-static PyObject*
+PyObject*
Message_getSection(s_Message* self, PyObject* args) {
unsigned int section;
if (!PyArg_ParseTuple(args, "I", §ion)) {
@@ -559,7 +556,7 @@ Message_getSection(s_Message* self, PyObject* args) {
//static PyObject* Message_beginSection(s_Message* self, PyObject* args);
//static PyObject* Message_endSection(s_Message* self, PyObject* args);
//static PyObject* Message_addQuestion(s_Message* self, PyObject* args);
-static PyObject*
+PyObject*
Message_addQuestion(s_Message* self, PyObject* args) {
s_Question *question;
@@ -572,7 +569,7 @@ Message_addQuestion(s_Message* self, PyObject* args) {
Py_RETURN_NONE;
}
-static PyObject*
+PyObject*
Message_addRRset(s_Message* self, PyObject* args) {
PyObject *sign = Py_False;
int section;
@@ -599,7 +596,7 @@ Message_addRRset(s_Message* self, PyObject* args) {
}
}
-static PyObject*
+PyObject*
Message_clear(s_Message* self, PyObject* args) {
int i;
if (PyArg_ParseTuple(args, "i", &i)) {
@@ -620,13 +617,13 @@ Message_clear(s_Message* self, PyObject* args) {
}
}
-static PyObject*
+PyObject*
Message_makeResponse(s_Message* self) {
self->message->makeResponse();
Py_RETURN_NONE;
}
-static PyObject*
+PyObject*
Message_toText(s_Message* self) {
// Py_BuildValue makes python objects from native data
try {
@@ -641,7 +638,7 @@ Message_toText(s_Message* self) {
}
}
-static PyObject*
+PyObject*
Message_str(PyObject* self) {
// Simply call the to_text method we already defined
return (PyObject_CallMethod(self,
@@ -649,13 +646,20 @@ Message_str(PyObject* self) {
const_cast<char*>("")));
}
-static PyObject*
+PyObject*
Message_toWire(s_Message* self, PyObject* args) {
s_MessageRenderer* mr;
+ s_TSIGContext* tsig_ctx = NULL;
- if (PyArg_ParseTuple(args, "O!", &messagerenderer_type, &mr)) {
+ if (PyArg_ParseTuple(args, "O!|O!", &messagerenderer_type, &mr,
+ &tsig_context_type, &tsig_ctx)) {
try {
- self->message->toWire(*mr->messagerenderer);
+ if (tsig_ctx == NULL) {
+ self->message->toWire(*mr->messagerenderer);
+ } else {
+ self->message->toWire(*mr->messagerenderer,
+ *tsig_ctx->tsig_ctx);
+ }
// If we return NULL it is seen as an error, so use this for
// None returns
Py_RETURN_NONE;
@@ -671,7 +675,7 @@ Message_toWire(s_Message* self, PyObject* args) {
return (NULL);
}
-static PyObject*
+PyObject*
Message_fromWire(s_Message* self, PyObject* args) {
const char* b;
Py_ssize_t len;
@@ -765,3 +769,4 @@ initModulePart_Message(PyObject* mod) {
return (true);
}
+} // end of unnamed namespace
diff --git a/src/lib/dns/python/pydnspp.cc b/src/lib/dns/python/pydnspp.cc
index 27dbae6..2138198 100644
--- a/src/lib/dns/python/pydnspp.cc
+++ b/src/lib/dns/python/pydnspp.cc
@@ -56,6 +56,7 @@ static PyObject* po_DNSMessageBADVERS;
#include <dns/python/question_python.cc> // needs RRClass, RRType, RRTTL,
// Name
#include <dns/python/tsigkey_python.cc> // needs Name
+#include <dns/python/tsig_python.cc> // needs tsigkey
#include <dns/python/opcode_python.cc>
#include <dns/python/rcode_python.cc>
#include <dns/python/edns_python.cc> // needs Messagerenderer, Rcode
@@ -153,6 +154,10 @@ PyInit_pydnspp(void) {
return (NULL);
}
+ if (!initModulePart_TSIGContext(mod)) {
+ return (NULL);
+ }
+
return (mod);
}
diff --git a/src/lib/dns/python/tests/Makefile.am b/src/lib/dns/python/tests/Makefile.am
index 184f06d..9ee98c7 100644
--- a/src/lib/dns/python/tests/Makefile.am
+++ b/src/lib/dns/python/tests/Makefile.am
@@ -11,6 +11,7 @@ PYTESTS += rrclass_python_test.py
PYTESTS += rrset_python_test.py
PYTESTS += rrttl_python_test.py
PYTESTS += rrtype_python_test.py
+PYTESTS += tsig_python_test.py
PYTESTS += tsigkey_python_test.py
EXTRA_DIST = $(PYTESTS)
diff --git a/src/lib/dns/python/tests/message_python_test.py b/src/lib/dns/python/tests/message_python_test.py
index bccc596..72807cc 100644
--- a/src/lib/dns/python/tests/message_python_test.py
+++ b/src/lib/dns/python/tests/message_python_test.py
@@ -62,6 +62,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):
@@ -81,6 +87,8 @@ class MessageTest(unittest.TestCase):
self.bogus_section = Message.SECTION_ADDITIONAL + 1
self.bogus_below_section = Message.SECTION_QUESTION - 1
+ self.tsig_key = TSIGKey("www.example.com:SFuWd/q99SzF8Yzd1QbB9g==")
+ self.tsig_ctx = TSIGContext(self.tsig_key)
def test_init(self):
self.assertRaises(TypeError, Message, -1)
@@ -277,12 +285,39 @@ class MessageTest(unittest.TestCase):
self.assertRaises(InvalidMessageOperation, self.r.to_wire,
MessageRenderer())
+ def __common_tsigquery_setup(self):
+ self.r.set_opcode(Opcode.QUERY())
+ self.r.set_rcode(Rcode.NOERROR())
+ self.r.set_header_flag(Message.HEADERFLAG_RD)
+ self.r.add_question(Question(Name("www.example.com"),
+ RRClass("IN"), RRType("A")))
+
+ 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)
+
+ def test_to_wire_with_tsig(self):
+ self.r.set_qid(0x2d65)
+ self.__common_tsigquery_setup()
+ self.__common_tsig_checks("message_toWire2.wire")
+
+ def test_to_wire_with_edns_tsig(self):
+ self.r.set_qid(0x6cd)
+ self.__common_tsigquery_setup()
+ edns = EDNS()
+ edns.set_udp_size(4096)
+ self.r.set_edns(edns)
+ self.__common_tsig_checks("message_toWire3.wire")
+
def test_to_text(self):
message_render = create_message()
msg_str =\
""";; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 4149
-;; flags: qr aa rd ; QUESTION: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 0
+;; flags: qr aa rd; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 0
;; QUESTION SECTION:
;test.example.com. IN A
diff --git a/src/lib/dns/python/tests/tsig_python_test.py b/src/lib/dns/python/tests/tsig_python_test.py
new file mode 100644
index 0000000..bffa0cf
--- /dev/null
+++ b/src/lib/dns/python/tests/tsig_python_test.py
@@ -0,0 +1,29 @@
+# Copyright (C) 2010 Internet Systems Consortium.
+#
+# Permission to use, copy, modify, and distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SYSTEMS CONSORTIUM
+# DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+# INTERNET SYSTEMS CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
+# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
+# FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+import unittest
+from pydnspp import *
+
+class TSIGContextTest(unittest.TestCase):
+ tsig_key = TSIGKey('www.example.com:SFuWd/q99SzF8Yzd1QbB9g==')
+
+ def setUp(self):
+ # In the minimal implementation, we simply check constructing a
+ # TSIGContext doesn't cause any disruption. We can add more tests
+ # later.
+ self.tsig_ctx = TSIGContext(self.tsig_key)
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/src/lib/dns/python/tsig_python.cc b/src/lib/dns/python/tsig_python.cc
new file mode 100644
index 0000000..2e6d986
--- /dev/null
+++ b/src/lib/dns/python/tsig_python.cc
@@ -0,0 +1,157 @@
+// 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 <dns/tsig.h>
+
+using namespace isc::dns;
+
+//
+// Definition of the classes
+//
+
+// For each class, we need a struct, a helper functions (init, destroy,
+// and static wrappers around the methods we export), a list of methods,
+// and a type description
+
+namespace {
+// The s_* Class simply covers one instantiation of the object
+
+class s_TSIGContext : public PyObject {
+public:
+ TSIGContext* tsig_ctx;
+};
+
+//
+// We declare the functions here, the definitions are below
+// the type definition of the object, since both can use the other
+//
+
+// General creation and destruction
+int TSIGContext_init(s_TSIGContext* self, PyObject* args);
+void TSIGContext_destroy(s_TSIGContext* self);
+
+// These are the functions we export
+// For a minimal support, we don't need them.
+
+// This list contains the actual set of functions we have in
+// python. Each entry has
+// 1. Python method name
+// 2. Our static function here
+// 3. Argument type
+// 4. Documentation
+PyMethodDef TSIGContext_methods[] = {
+ { NULL, NULL, 0, NULL }
+};
+
+// This defines the complete type for reflection in python and
+// parsing of PyObject* to s_EDNS
+// Most of the functions are not actually implemented and NULL here.
+PyTypeObject tsig_context_type = {
+ PyVarObject_HEAD_INIT(NULL, 0)
+ "libdns_python.TSIGContext",
+ sizeof(s_TSIGContext), // tp_basicsize
+ 0, // tp_itemsize
+ (destructor)TSIGContext_destroy, // tp_dealloc
+ NULL, // tp_print
+ NULL, // tp_getattr
+ NULL, // tp_setattr
+ NULL, // tp_reserved
+ NULL, // tp_repr
+ NULL, // tp_as_number
+ NULL, // tp_as_sequence
+ NULL, // tp_as_mapping
+ NULL, // tp_hash
+ NULL, // tp_call
+ NULL, // tp_str
+ NULL, // tp_getattro
+ NULL, // tp_setattro
+ NULL, // tp_as_buffer
+ Py_TPFLAGS_DEFAULT, // tp_flags
+ "The TSIGContext class maintains a context of a signed session of "
+ "DNS transactions by TSIG.",
+ NULL, // tp_traverse
+ NULL, // tp_clear
+ NULL, // tp_richcompare
+ 0, // tp_weaklistoffset
+ NULL, // tp_iter
+ NULL, // tp_iternext
+ TSIGContext_methods, // tp_methods
+ NULL, // tp_members
+ NULL, // tp_getset
+ NULL, // tp_base
+ NULL, // tp_dict
+ NULL, // tp_descr_get
+ NULL, // tp_descr_set
+ 0, // tp_dictoffset
+ (initproc)TSIGContext_init, // tp_init
+ NULL, // tp_alloc
+ PyType_GenericNew, // tp_new
+ NULL, // tp_free
+ NULL, // tp_is_gc
+ NULL, // tp_bases
+ NULL, // tp_mro
+ NULL, // tp_cache
+ NULL, // tp_subclasses
+ NULL, // tp_weaklist
+ NULL, // tp_del
+ 0 // tp_version_tag
+};
+
+int
+TSIGContext_init(s_TSIGContext* self, PyObject* args) {
+ const s_TSIGKey* tsigkey_obj;
+
+ try {
+ if (PyArg_ParseTuple(args, "O!", &tsigkey_type, &tsigkey_obj)) {
+ self->tsig_ctx = new TSIGContext(*tsigkey_obj->tsigkey);
+ return (0);
+ }
+ } catch (...) {
+ PyErr_SetString(po_IscException, "Unexpected exception");
+ return (-1);
+ }
+
+ PyErr_Clear();
+ PyErr_SetString(PyExc_TypeError,
+ "Invalid arguments to TSIGContext constructor");
+
+ return (-1);
+}
+
+void
+TSIGContext_destroy(s_TSIGContext* const self) {
+ delete self->tsig_ctx;
+ self->tsig_ctx = NULL;
+ Py_TYPE(self)->tp_free(self);
+}
+
+// Module Initialization, all statics are initialized here
+bool
+initModulePart_TSIGContext(PyObject* mod) {
+ // We initialize the static description object with PyType_Ready(),
+ // then add it to the module. This is not just a check! (leaving
+ // this out results in segmentation faults)
+ if (PyType_Ready(&tsig_context_type) < 0) {
+ return (false);
+ }
+ Py_INCREF(&tsig_context_type);
+ void* p = &tsig_context_type;
+ PyModule_AddObject(mod, "TSIGContext", static_cast<PyObject*>(p));
+
+ addClassVariable(tsig_context_type, "DEFAULT_FUDGE",
+ Py_BuildValue("H", TSIGContext::DEFAULT_FUDGE));
+
+ return (true);
+}
+} // end of anonymous namespace
diff --git a/src/lib/dns/question.cc b/src/lib/dns/question.cc
index 723fc88..96e2a9c 100644
--- a/src/lib/dns/question.cc
+++ b/src/lib/dns/question.cc
@@ -56,7 +56,7 @@ Question::toWire(OutputBuffer& buffer) const {
}
unsigned int
-Question::toWire(MessageRenderer& renderer) const {
+Question::toWire(AbstractMessageRenderer& renderer) const {
renderer.writeName(name_);
rrtype_.toWire(renderer);
rrclass_.toWire(renderer);
diff --git a/src/lib/dns/question.h b/src/lib/dns/question.h
index 3c038b3..b3f3d98 100644
--- a/src/lib/dns/question.h
+++ b/src/lib/dns/question.h
@@ -32,7 +32,7 @@ class OutputBuffer;
namespace dns {
-class MessageRenderer;
+class AbstractMessageRenderer;
class Question;
/// \brief A pointer-like type pointing to an \c Question object.
@@ -218,13 +218,13 @@ public:
/// \param renderer DNS message rendering context that encapsulates the
/// output buffer and name compression information.
/// \return 1
- unsigned int toWire(MessageRenderer& renderer) const;
+ unsigned int toWire(AbstractMessageRenderer& renderer) const;
/// \brief Render the Question in the wire format without name compression.
///
/// This method behaves like the render version except it doesn't compress
/// the owner name.
- /// See \c toWire(MessageRenderer& renderer)const.
+ /// See \c toWire(AbstractMessageRenderer& renderer)const.
///
/// \param buffer An output buffer to store the wire data.
/// \return 1
diff --git a/src/lib/dns/rdata/any_255/tsig_250.cc b/src/lib/dns/rdata/any_255/tsig_250.cc
index f2bd7ad..8211e7f 100644
--- a/src/lib/dns/rdata/any_255/tsig_250.cc
+++ b/src/lib/dns/rdata/any_255/tsig_250.cc
@@ -24,7 +24,7 @@
#include <dns/messagerenderer.h>
#include <dns/rdata.h>
#include <dns/rdataclass.h>
-
+#include <dns/tsigerror.h>
using namespace std;
using namespace boost;
@@ -313,15 +313,7 @@ TSIG::toText() const {
result += encodeBase64(impl_->mac_) + " ";
}
result += lexical_cast<string>(impl_->original_id_) + " ";
- if (impl_->error_ == 16) { // XXX: we'll soon introduce generic converter.
- result += "BADSIG ";
- } else if (impl_->error_ == 17) {
- result += "BADKEY ";
- } else if (impl_->error_ == 18) {
- result += "BADTIME ";
- } else {
- result += lexical_cast<string>(impl_->error_) + " ";
- }
+ result += TSIGError(impl_->error_).toText() + " ";
result += lexical_cast<string>(impl_->other_data_.size());
if (impl_->other_data_.size() > 0) {
result += " " + encodeBase64(impl_->other_data_);
diff --git a/src/lib/dns/rrclass-placeholder.h b/src/lib/dns/rrclass-placeholder.h
index ae3ff6f..80035d8 100644
--- a/src/lib/dns/rrclass-placeholder.h
+++ b/src/lib/dns/rrclass-placeholder.h
@@ -31,7 +31,7 @@ class OutputBuffer;
namespace dns {
// forward declarations
-class MessageRenderer;
+class AbstractMessageRenderer;
///
/// \brief A standard DNS module exception that is thrown if an RRClass object
@@ -169,7 +169,7 @@ public:
/// standard exception will be thrown.
///
/// \param buffer An output buffer to store the wire data.
- void toWire(MessageRenderer& renderer) const;
+ void toWire(AbstractMessageRenderer& renderer) const;
/// \brief Render the \c RRClass in the wire format.
///
/// This method renders the class code in network byte order into the
diff --git a/src/lib/dns/rrclass.cc b/src/lib/dns/rrclass.cc
index f1bb8d7..a28e5cf 100644
--- a/src/lib/dns/rrclass.cc
+++ b/src/lib/dns/rrclass.cc
@@ -52,7 +52,7 @@ RRClass::toWire(OutputBuffer& buffer) const {
}
void
-RRClass::toWire(MessageRenderer& renderer) const {
+RRClass::toWire(AbstractMessageRenderer& renderer) const {
renderer.writeUint16(classcode_);
}
diff --git a/src/lib/dns/rrset.cc b/src/lib/dns/rrset.cc
index e80afdb..776d49f 100644
--- a/src/lib/dns/rrset.cc
+++ b/src/lib/dns/rrset.cc
@@ -104,8 +104,8 @@ AbstractRRset::toWire(OutputBuffer& buffer) const {
}
unsigned int
-AbstractRRset::toWire(MessageRenderer& renderer) const {
- const unsigned int rrs_written = rrsetToWire<MessageRenderer>(
+AbstractRRset::toWire(AbstractMessageRenderer& renderer) const {
+ const unsigned int rrs_written = rrsetToWire<AbstractMessageRenderer>(
*this, renderer, renderer.getLengthLimit());
if (getRdataCount() > rrs_written) {
renderer.setTruncated();
@@ -202,7 +202,7 @@ BasicRRset::toWire(OutputBuffer& buffer) const {
}
unsigned int
-BasicRRset::toWire(MessageRenderer& renderer) const {
+BasicRRset::toWire(AbstractMessageRenderer& renderer) const {
return (AbstractRRset::toWire(renderer));
}
diff --git a/src/lib/dns/rrset.h b/src/lib/dns/rrset.h
index bf7adc0..6c15b53 100644
--- a/src/lib/dns/rrset.h
+++ b/src/lib/dns/rrset.h
@@ -47,7 +47,7 @@ class Name;
class RRType;
class RRClass;
class RRTTL;
-class MessageRenderer;
+class AbstractMessageRenderer;
class AbstractRRset;
class BasicRRset;
class RdataIterator;
@@ -311,7 +311,7 @@ public:
/// \return The number of RRs rendered. If the truncation is necessary
/// this value may be different from the number of RDATA objects contained
/// in the RRset.
- virtual unsigned int toWire(MessageRenderer& renderer) const = 0;
+ virtual unsigned int toWire(AbstractMessageRenderer& renderer) const = 0;
/// \brief Render the RRset in the wire format without any compression.
///
@@ -617,7 +617,7 @@ public:
///
/// This method simply uses the default implementation.
/// See \c AbstractRRset::toWire(MessageRenderer&)const.
- virtual unsigned int toWire(MessageRenderer& renderer) const;
+ virtual unsigned int toWire(AbstractMessageRenderer& renderer) const;
/// \brief Render the RRset in the wire format without any compression.
///
diff --git a/src/lib/dns/rrttl.cc b/src/lib/dns/rrttl.cc
index a4c4f83..ecd8cc6 100644
--- a/src/lib/dns/rrttl.cc
+++ b/src/lib/dns/rrttl.cc
@@ -63,7 +63,7 @@ RRTTL::toWire(OutputBuffer& buffer) const {
}
void
-RRTTL::toWire(MessageRenderer& renderer) const {
+RRTTL::toWire(AbstractMessageRenderer& renderer) const {
renderer.writeUint32(ttlval_);
}
diff --git a/src/lib/dns/rrttl.h b/src/lib/dns/rrttl.h
index 17ec891..bf23295 100644
--- a/src/lib/dns/rrttl.h
+++ b/src/lib/dns/rrttl.h
@@ -28,7 +28,7 @@ class OutputBuffer;
namespace dns {
// forward declarations
-class MessageRenderer;
+class AbstractMessageRenderer;
///
/// \brief A standard DNS module exception that is thrown if an RRTTL object
@@ -123,7 +123,7 @@ public:
///
/// \param renderer DNS message rendering context that encapsulates the
/// output buffer in which the RRTTL is to be stored.
- void toWire(MessageRenderer& renderer) const;
+ void toWire(AbstractMessageRenderer& renderer) const;
/// \brief Render the \c RRTTL in the wire format.
///
/// This method renders the TTL value in network byte order into the
diff --git a/src/lib/dns/tests/Makefile.am b/src/lib/dns/tests/Makefile.am
index 7a94653..9783beb 100644
--- a/src/lib/dns/tests/Makefile.am
+++ b/src/lib/dns/tests/Makefile.am
@@ -50,6 +50,7 @@ run_unittests_SOURCES += message_unittest.cc
run_unittests_SOURCES += tsig_unittest.cc
run_unittests_SOURCES += tsigerror_unittest.cc
run_unittests_SOURCES += tsigkey_unittest.cc
+run_unittests_SOURCES += tsigrecord_unittest.cc
run_unittests_SOURCES += run_unittests.cc
run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
diff --git a/src/lib/dns/tests/message_unittest.cc b/src/lib/dns/tests/message_unittest.cc
index 4031e4f..c79ea2c 100644
--- a/src/lib/dns/tests/message_unittest.cc
+++ b/src/lib/dns/tests/message_unittest.cc
@@ -12,9 +12,18 @@
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
+#include <fstream>
+
+#include <boost/scoped_ptr.hpp>
+
#include <exceptions/exceptions.h>
#include <util/buffer.h>
+#include <util/time_utilities.h>
+
+#include <util/unittests/testdata.h>
+#include <util/unittests/textdata.h>
+
#include <dns/edns.h>
#include <dns/exceptions.h>
#include <dns/message.h>
@@ -26,6 +35,8 @@
#include <dns/rrclass.h>
#include <dns/rrttl.h>
#include <dns/rrtype.h>
+#include <dns/tsig.h>
+#include <dns/tsigkey.h>
#include <gtest/gtest.h>
@@ -53,6 +64,18 @@ using namespace isc::dns::rdata;
const uint16_t Message::DEFAULT_MAX_UDPSIZE;
const Name test_name("test.example.com");
+namespace isc {
+namespace util {
+namespace detail {
+extern int64_t (*gettimeFunction)();
+}
+}
+}
+
+// XXX: this is defined as class static constants, but some compilers
+// seemingly cannot find the symbol when used in the EXPECT_xxx macros.
+const uint16_t TSIGContext::DEFAULT_FUDGE;
+
namespace {
class MessageTest : public ::testing::Test {
protected:
@@ -60,7 +83,9 @@ protected:
message_parse(Message::PARSE),
message_render(Message::RENDER),
bogus_section(static_cast<Message::Section>(
- Message::SECTION_ADDITIONAL + 1))
+ Message::SECTION_ADDITIONAL + 1)),
+ tsig_ctx(TSIGKey("www.example.com:"
+ "SFuWd/q99SzF8Yzd1QbB9g=="))
{
rrset_a = RRsetPtr(new RRset(test_name, RRClass::IN(),
RRType::A(), RRTTL(3600)));
@@ -88,6 +113,9 @@ protected:
RRsetPtr rrset_a; // A RRset with two RDATAs
RRsetPtr rrset_aaaa; // AAAA RRset with one RDATA with RRSIG
RRsetPtr rrset_rrsig; // RRSIG for the AAAA RRset
+ TSIGContext tsig_ctx;
+ vector<unsigned char> expected_data;
+
static void factoryFromFile(Message& message, const char* datafile);
};
@@ -166,6 +194,70 @@ TEST_F(MessageTest, setEDNS) {
EXPECT_EQ(edns, message_render.getEDNS());
}
+TEST_F(MessageTest, fromWireWithTSIG) {
+ // Initially there should be no TSIG
+ EXPECT_EQ(static_cast<void*>(NULL), message_parse.getTSIGRecord());
+
+ // getTSIGRecord() is only valid in the parse mode.
+ EXPECT_THROW(message_render.getTSIGRecord(), InvalidMessageOperation);
+
+ factoryFromFile(message_parse, "message_toWire2.wire");
+ const char expected_mac[] = {
+ 0x22, 0x70, 0x26, 0xad, 0x29, 0x7b, 0xee, 0xe7,
+ 0x21, 0xce, 0x6c, 0x6f, 0xff, 0x1e, 0x9e, 0xf3
+ };
+ const TSIGRecord* tsig_rr = message_parse.getTSIGRecord();
+ ASSERT_NE(static_cast<void*>(NULL), tsig_rr);
+ EXPECT_EQ(Name("www.example.com"), tsig_rr->getName());
+ EXPECT_EQ(85, tsig_rr->getLength()); // see TSIGRecordTest.getLength
+ EXPECT_EQ(TSIGKey::HMACMD5_NAME(), tsig_rr->getRdata().getAlgorithm());
+ EXPECT_EQ(0x4da8877a, tsig_rr->getRdata().getTimeSigned());
+ EXPECT_EQ(TSIGContext::DEFAULT_FUDGE, tsig_rr->getRdata().getFudge());
+ EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
+ tsig_rr->getRdata().getMAC(),
+ tsig_rr->getRdata().getMACSize(),
+ expected_mac, sizeof(expected_mac));
+ EXPECT_EQ(0, tsig_rr->getRdata().getError());
+ EXPECT_EQ(0, tsig_rr->getRdata().getOtherLen());
+ EXPECT_EQ(static_cast<void*>(NULL), tsig_rr->getRdata().getOtherData());
+
+ // If we clear the message for reuse, the recorded TSIG will be cleared.
+ message_parse.clear(Message::PARSE);
+ EXPECT_EQ(static_cast<void*>(NULL), message_parse.getTSIGRecord());
+}
+
+TEST_F(MessageTest, fromWireWithTSIGCompressed) {
+ // Mostly same as fromWireWithTSIG, but the TSIG owner name is compressed.
+ factoryFromFile(message_parse, "message_fromWire12.wire");
+ const TSIGRecord* tsig_rr = message_parse.getTSIGRecord();
+ ASSERT_NE(static_cast<void*>(NULL), tsig_rr);
+ EXPECT_EQ(Name("www.example.com"), tsig_rr->getName());
+ // len(www.example.com) = 17, but when fully compressed, the length is
+ // 2 bytes. So the length of the record should be 15 bytes shorter.
+ EXPECT_EQ(70, tsig_rr->getLength());
+}
+
+TEST_F(MessageTest, fromWireWithBadTSIG) {
+ // Multiple TSIG RRs
+ EXPECT_THROW(factoryFromFile(message_parse, "message_fromWire13.wire"),
+ DNSMessageFORMERR);
+ message_parse.clear(Message::PARSE);
+
+ // TSIG in the answer section (must be in additional)
+ EXPECT_THROW(factoryFromFile(message_parse, "message_fromWire14.wire"),
+ DNSMessageFORMERR);
+ message_parse.clear(Message::PARSE);
+
+ // TSIG is not the last record.
+ EXPECT_THROW(factoryFromFile(message_parse, "message_fromWire15.wire"),
+ DNSMessageFORMERR);
+ message_parse.clear(Message::PARSE);
+
+ // Unexpected RR Class (this will fail in constructing TSIGRecord)
+ EXPECT_THROW(factoryFromFile(message_parse, "message_fromWire16.wire"),
+ DNSMessageFORMERR);
+}
+
TEST_F(MessageTest, getRRCount) {
// by default all counters should be 0
EXPECT_EQ(0, message_render.getRRCount(Message::SECTION_QUESTION));
@@ -519,6 +611,65 @@ TEST_F(MessageTest, toWireInParseMode) {
EXPECT_THROW(message_parse.toWire(renderer), InvalidMessageOperation);
}
+// See dnssectime_unittest.cc
+template <int64_t NOW>
+int64_t
+testGetTime() {
+ return (NOW);
+}
+
+void
+commonTSIGToWireCheck(Message& message, MessageRenderer& renderer,
+ TSIGContext& tsig_ctx, const char* const expected_file)
+{
+ message.setOpcode(Opcode::QUERY());
+ message.setRcode(Rcode::NOERROR());
+ message.setHeaderFlag(Message::HEADERFLAG_RD, true);
+ message.addQuestion(Question(Name("www.example.com"), RRClass::IN(),
+ RRType::A()));
+
+ message.toWire(renderer, tsig_ctx);
+ vector<unsigned char> expected_data;
+ UnitTestUtil::readWireData(expected_file, expected_data);
+ EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, renderer.getData(),
+ renderer.getLength(),
+ &expected_data[0], expected_data.size());
+}
+
+TEST_F(MessageTest, toWireWithTSIG) {
+ // Rendering a message with TSIG. Various special cases specific to
+ // TSIG are tested in the tsig tests. We only check the message contains
+ // a TSIG at the end and the ARCOUNT of the header is updated.
+
+ isc::util::detail::gettimeFunction = testGetTime<0x4da8877a>;
+
+ message_render.setQid(0x2d65);
+
+ {
+ SCOPED_TRACE("Message sign with TSIG");
+ commonTSIGToWireCheck(message_render, renderer, tsig_ctx,
+ "message_toWire2.wire");
+ }
+}
+
+TEST_F(MessageTest, toWireWithEDNSAndTSIG) {
+ // Similar to the previous test, but with an EDNS before TSIG.
+ // The wire data check will confirm the ordering.
+ isc::util::detail::gettimeFunction = testGetTime<0x4db60d1f>;
+
+ message_render.setQid(0x6cd);
+
+ EDNSPtr edns(new EDNS());
+ edns->setUDPSize(4096);
+ message_render.setEDNS(edns);
+
+ {
+ SCOPED_TRACE("Message sign with TSIG and EDNS");
+ commonTSIGToWireCheck(message_render, renderer, tsig_ctx,
+ "message_toWire3.wire");
+ }
+}
+
TEST_F(MessageTest, toWireWithoutOpcode) {
message_render.setRcode(Rcode::NOERROR());
EXPECT_THROW(message_render.toWire(renderer), InvalidMessageOperation);
@@ -529,6 +680,45 @@ TEST_F(MessageTest, toWireWithoutRcode) {
EXPECT_THROW(message_render.toWire(renderer), InvalidMessageOperation);
}
+TEST_F(MessageTest, toText) {
+ // Check toText() output for a typical DNS response with records in
+ // all sections
+
+ factoryFromFile(message_parse, "message_toText1.wire");
+ {
+ SCOPED_TRACE("Message toText test (basic case)");
+ ifstream ifs;
+ unittests::openTestData("message_toText1.txt", ifs);
+ unittests::matchTextData(ifs, message_parse.toText());
+ }
+
+ // Another example with EDNS. The expected data was slightly modified
+ // from the dig output (other than replacing tabs with a space): adding
+ // a newline after the "OPT PSEUDOSECTION". This is an intentional change
+ // in our version for better readability.
+ message_parse.clear(Message::PARSE);
+ factoryFromFile(message_parse, "message_toText2.wire");
+ {
+ SCOPED_TRACE("Message toText test with EDNS");
+ ifstream ifs;
+ unittests::openTestData("message_toText2.txt", ifs);
+ unittests::matchTextData(ifs, message_parse.toText());
+ }
+
+ // Another example with TSIG. The expected data was slightly modified
+ // from the dig output (other than replacing tabs with a space): removing
+ // a redundant white space at the end of TSIG RDATA. We'd rather consider
+ // it a dig's defect than a feature.
+ message_parse.clear(Message::PARSE);
+ factoryFromFile(message_parse, "message_toText3.wire");
+ {
+ SCOPED_TRACE("Message toText test with TSIG");
+ ifstream ifs;
+ unittests::openTestData("message_toText3.txt", ifs);
+ unittests::matchTextData(ifs, message_parse.toText());
+ }
+}
+
TEST_F(MessageTest, toTextWithoutOpcode) {
message_render.setRcode(Rcode::NOERROR());
EXPECT_THROW(message_render.toText(), InvalidMessageOperation);
diff --git a/src/lib/dns/tests/run_unittests.cc b/src/lib/dns/tests/run_unittests.cc
index 3cdc61d..18eb0a5 100644
--- a/src/lib/dns/tests/run_unittests.cc
+++ b/src/lib/dns/tests/run_unittests.cc
@@ -14,13 +14,16 @@
#include <gtest/gtest.h>
+#include <util/unittests/testdata.h>
#include <dns/tests/unittest_util.h>
int
main(int argc, char* argv[]) {
::testing::InitGoogleTest(&argc, argv);
isc::UnitTestUtil::addDataPath(TEST_DATA_SRCDIR);
+ isc::util::unittests::addTestDataPath(TEST_DATA_SRCDIR);
isc::UnitTestUtil::addDataPath(TEST_DATA_BUILDDIR);
+ isc::util::unittests::addTestDataPath(TEST_DATA_BUILDDIR);
return (RUN_ALL_TESTS());
}
diff --git a/src/lib/dns/tests/testdata/Makefile.am b/src/lib/dns/tests/testdata/Makefile.am
index 31771bb..8a83aae 100644
--- a/src/lib/dns/tests/testdata/Makefile.am
+++ b/src/lib/dns/tests/testdata/Makefile.am
@@ -3,6 +3,12 @@ CLEANFILES = *.wire
BUILT_SOURCES = edns_toWire1.wire edns_toWire2.wire edns_toWire3.wire
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_toWire2.wire message_toWire3.wire
+BUILT_SOURCES += message_toText1.wire message_toText2.wire
+BUILT_SOURCES += message_toText3.wire
BUILT_SOURCES += name_toWire5.wire name_toWire6.wire
BUILT_SOURCES += rdatafields1.wire rdatafields2.wire rdatafields3.wire
BUILT_SOURCES += rdatafields4.wire rdatafields5.wire rdatafields6.wire
@@ -33,6 +39,7 @@ BUILT_SOURCES += rdata_tsig_fromWire9.wire
BUILT_SOURCES += rdata_tsig_toWire1.wire rdata_tsig_toWire2.wire
BUILT_SOURCES += rdata_tsig_toWire3.wire rdata_tsig_toWire4.wire
BUILT_SOURCES += rdata_tsig_toWire5.wire
+BUILT_SOURCES += tsigrecord_toWire1.wire tsigrecord_toWire2.wire
# NOTE: keep this in sync with real file listing
# so is included in tarball
@@ -45,8 +52,13 @@ EXTRA_DIST += message_fromWire3 message_fromWire4
EXTRA_DIST += message_fromWire5 message_fromWire6
EXTRA_DIST += message_fromWire7 message_fromWire8
EXTRA_DIST += message_fromWire9 message_fromWire10.spec
-EXTRA_DIST += message_fromWire11.spec
-EXTRA_DIST += message_toWire1
+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_toWire1 message_toWire2.spec message_toWire3.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
EXTRA_DIST += name_fromWire1 name_fromWire2 name_fromWire3_1 name_fromWire3_2
EXTRA_DIST += name_fromWire4 name_fromWire6 name_fromWire7 name_fromWire8
EXTRA_DIST += name_fromWire9 name_fromWire10 name_fromWire11 name_fromWire12
@@ -66,7 +78,8 @@ EXTRA_DIST += rdata_nsec_fromWire6.spec rdata_nsec_fromWire7.spec
EXTRA_DIST += rdata_nsec_fromWire8.spec rdata_nsec_fromWire9.spec
EXTRA_DIST += rdata_nsec_fromWire10.spec
EXTRA_DIST += rdata_nsec3param_fromWire1
-EXTRA_DIST += rdata_nsec3_fromWire1 rdata_nsec3_fromWire3
+EXTRA_DIST += rdata_nsec3_fromWire1
+EXTRA_DIST += rdata_nsec3_fromWire2.spec rdata_nsec3_fromWire3
EXTRA_DIST += rdata_nsec3_fromWire4.spec rdata_nsec3_fromWire5.spec
EXTRA_DIST += rdata_nsec3_fromWire6.spec rdata_nsec3_fromWire7.spec
EXTRA_DIST += rdata_nsec3_fromWire8.spec rdata_nsec3_fromWire9.spec
@@ -94,7 +107,7 @@ EXTRA_DIST += rdata_tsig_fromWire9.spec
EXTRA_DIST += rdata_tsig_toWire1.spec rdata_tsig_toWire2.spec
EXTRA_DIST += rdata_tsig_toWire3.spec rdata_tsig_toWire4.spec
EXTRA_DIST += rdata_tsig_toWire5.spec
-EXTRA_DIST += rdata_nsec3_fromWire2.spec
+EXTRA_DIST += tsigrecord_toWire1.spec tsigrecord_toWire2.spec
.spec.wire:
./gen-wiredata.py -o $@ $<
diff --git a/src/lib/dns/tests/testdata/gen-wiredata.py.in b/src/lib/dns/tests/testdata/gen-wiredata.py.in
index 72cbdaa..7a82bfd 100755
--- a/src/lib/dns/tests/testdata/gen-wiredata.py.in
+++ b/src/lib/dns/tests/testdata/gen-wiredata.py.in
@@ -15,7 +15,7 @@
# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-import configparser, re, time, sys
+import configparser, re, time, socket, sys
from datetime import datetime
from optparse import OptionParser
@@ -215,6 +215,74 @@ class EDNS:
f.write('# RDLEN=%d\n' % self.rdlen)
f.write('%04x\n' % self.rdlen)
+class RR:
+ '''This is a base class for various types of RR test data.
+ For each RR type (A, AAAA, NS, etc), we define a derived class of RR
+ to dump type specific RDATA parameters. This class defines parameters
+ common to all types of RDATA, namely the owner name, RR class and TTL.
+ The dump() method of derived classes are expected to call dump_header(),
+ whose default implementation is provided in this class. This method
+ decides whether to dump the test data as an RR (with name, type, class)
+ or only as RDATA (with its length), and dumps the corresponding data
+ via the specified file object.
+
+ By convention we assume derived classes are named after the common
+ standard mnemonic of the corresponding RR types. For example, the
+ derived class for the RR type SOA should be named "SOA".
+
+ Configurable parameters are as follows:
+ - as_rr (bool): Whether or not the data is to be dumped as an RR. False
+ by default.
+ - rr_class (string): The RR class of the data. Only meaningful when the
+ data is dumped as an RR. Default is 'IN'.
+ - rr_ttl (integer): The TTL value of the RR. Only meaningful when the
+ data is dumped as an RR. Default is 86400 (1 day).
+ '''
+
+ def __init__(self):
+ self.as_rr = False
+ # only when as_rr is True, same for class/TTL:
+ self.rr_name = 'example.com'
+ self.rr_class = 'IN'
+ self.rr_ttl = 86400
+ def dump_header(self, f, rdlen):
+ type_txt = self.__class__.__name__
+ type_code = parse_value(type_txt, dict_rrtype)
+ if self.as_rr:
+ rrclass = parse_value(self.rr_class, dict_rrclass)
+ f.write('\n# %s RR (QNAME=%s Class=%s TTL=%d RDLEN=%d)\n' %
+ (type_txt, self.rr_name,
+ code_totext(rrclass, rdict_rrclass), self.rr_ttl, rdlen))
+ f.write('%s %04x %04x %08x %04x\n' %
+ (encode_name(self.rr_name), type_code, rrclass,
+ self.rr_ttl, rdlen))
+ else:
+ f.write('\n# %s RDATA (RDLEN=%d)\n' % (type_txt, rdlen))
+ f.write('%04x\n' % rdlen)
+
+class A(RR):
+ rdlen = 4 # fixed by default
+ address = '192.0.2.1'
+
+ def dump(self, f):
+ self.dump_header(f, self.rdlen)
+ f.write('# Address=%s\n' % (self.address))
+ bin_address = socket.inet_aton(self.address)
+ f.write('%02x%02x%02x%02x\n' % (bin_address[0], bin_address[1],
+ bin_address[2], bin_address[3]))
+
+class NS(RR):
+ rdlen = None # auto calculate
+ nsname = 'ns.example.com'
+
+ def dump(self, f):
+ nsname_wire = encode_name(self.nsname)
+ if self.rdlen is None:
+ self.rdlen = len(nsname_wire) / 2
+ self.dump_header(f, self.rdlen)
+ f.write('# NS name=%s\n' % (self.nsname))
+ f.write('%s\n' % nsname_wire)
+
class SOA:
# this currently doesn't support name compression within the RDATA.
rdlen = -1 # auto-calculate
@@ -432,7 +500,7 @@ class RRSIG:
f.write('# Tag=%d Signer=%s and Signature\n' % (self.tag, self.signer))
f.write('%04x %s %s\n' % (self.tag, name_wire, sig_wire))
-class TSIG:
+class TSIG(RR):
rdlen = None # auto-calculate
algorithm = 'hmac-sha256'
time_signed = 1286978795 # arbitrarily chosen default
@@ -444,12 +512,18 @@ class TSIG:
other_len = None # 6 if error is BADTIME; otherwise 0
other_data = None # use time_signed + fudge + 1 for BADTIME
dict_macsize = { 'hmac-md5' : 16, 'hmac-sha1' : 20, 'hmac-sha256' : 32 }
+
+ # TSIG has some special defaults
+ def __init__(self):
+ super().__init__()
+ self.rr_class = 'ANY'
+ self.rr_ttl = 0
+
def dump(self, f):
if str(self.algorithm) == 'hmac-md5':
name_wire = encode_name('hmac-md5.sig-alg.reg.int')
else:
name_wire = encode_name(self.algorithm)
- rdlen = self.rdlen
mac_size = self.mac_size
if mac_size is None:
if self.algorithm in self.dict_macsize.keys():
@@ -468,11 +542,10 @@ class TSIG:
if self.error == 18 else ''
else:
other_data = encode_string(self.other_data, other_len)
- if rdlen is None:
- rdlen = int(len(name_wire) / 2 + 16 + len(mac) / 2 + \
- len(other_data) / 2)
- f.write('\n# TSIG RDATA (RDLEN=%d)\n' % rdlen)
- f.write('%04x\n' % rdlen);
+ if self.rdlen is None:
+ self.rdlen = int(len(name_wire) / 2 + 16 + len(mac) / 2 + \
+ len(other_data) / 2)
+ self.dump_header(f, self.rdlen)
f.write('# Algorithm=%s Time-Signed=%d Fudge=%d\n' %
(self.algorithm, self.time_signed, self.fudge))
f.write('%s %012x %04x\n' % (name_wire, self.time_signed, self.fudge))
@@ -488,7 +561,8 @@ def get_config_param(section):
config_param = {'name' : (Name, {}),
'header' : (DNSHeader, header_xtables),
'question' : (DNSQuestion, question_xtables),
- 'edns' : (EDNS, {}), 'soa' : (SOA, {}), 'txt' : (TXT, {}),
+ 'edns' : (EDNS, {}), 'a' : (A, {}), 'ns' : (NS, {}),
+ 'soa' : (SOA, {}), 'txt' : (TXT, {}),
'rp' : (RP, {}), 'rrsig' : (RRSIG, {}),
'nsec' : (NSEC, {}), 'nsec3' : (NSEC3, {}),
'tsig' : (TSIG, {}) }
diff --git a/src/lib/dns/tests/testdata/message_fromWire12.spec b/src/lib/dns/tests/testdata/message_fromWire12.spec
new file mode 100644
index 0000000..4eadeed
--- /dev/null
+++ b/src/lib/dns/tests/testdata/message_fromWire12.spec
@@ -0,0 +1,21 @@
+#
+# A simple DNS response message with TSIG signed, but the owner name of TSIG
+# is compressed
+#
+
+[custom]
+sections: header:question:tsig
+[header]
+id: 0x2d65
+rd: 1
+arcount: 1
+[question]
+name: www.example.com
+[tsig]
+as_rr: True
+rr_name: ptr=12
+algorithm: hmac-md5
+time_signed: 0x4da8877a
+mac_size: 16
+mac: 0x227026ad297beee721ce6c6fff1e9ef3
+original_id: 0x2d65
diff --git a/src/lib/dns/tests/testdata/message_fromWire13.spec b/src/lib/dns/tests/testdata/message_fromWire13.spec
new file mode 100644
index 0000000..e81ec4c
--- /dev/null
+++ b/src/lib/dns/tests/testdata/message_fromWire13.spec
@@ -0,0 +1,20 @@
+#
+# Invalid TSIG: containing 2 TSIG RRs.
+#
+
+[custom]
+sections: header:question:tsig:tsig
+[header]
+id: 0x2d65
+rd: 1
+arcount: 2
+[question]
+name: www.example.com
+[tsig]
+as_rr: True
+rr_name: www.example.com
+algorithm: hmac-md5
+time_signed: 0x4da8877a
+mac_size: 16
+mac: 0x227026ad297beee721ce6c6fff1e9ef3
+original_id: 0x2d65
diff --git a/src/lib/dns/tests/testdata/message_fromWire14.spec b/src/lib/dns/tests/testdata/message_fromWire14.spec
new file mode 100644
index 0000000..bf68a93
--- /dev/null
+++ b/src/lib/dns/tests/testdata/message_fromWire14.spec
@@ -0,0 +1,21 @@
+#
+# Invalid TSIG: not in the additional section.
+#
+
+[custom]
+sections: header:question:tsig
+[header]
+id: 0x2d65
+rd: 1
+# TSIG goes to the answer section
+ancount: 1
+[question]
+name: www.example.com
+[tsig]
+as_rr: True
+rr_name: www.example.com
+algorithm: hmac-md5
+time_signed: 0x4da8877a
+mac_size: 16
+mac: 0x227026ad297beee721ce6c6fff1e9ef3
+original_id: 0x2d65
diff --git a/src/lib/dns/tests/testdata/message_fromWire15.spec b/src/lib/dns/tests/testdata/message_fromWire15.spec
new file mode 100644
index 0000000..25d810f
--- /dev/null
+++ b/src/lib/dns/tests/testdata/message_fromWire15.spec
@@ -0,0 +1,22 @@
+#
+# Invalid TSIG: not at the end of the message
+#
+
+[custom]
+sections: header:question:tsig:edns
+[header]
+id: 0x2d65
+rd: 1
+arcount: 2
+[question]
+name: www.example.com
+[tsig]
+as_rr: True
+rr_name: www.example.com
+algorithm: hmac-md5
+time_signed: 0x4da8877a
+mac_size: 16
+mac: 0x227026ad297beee721ce6c6fff1e9ef3
+original_id: 0x2d65
+[edns]
+# (all default)
diff --git a/src/lib/dns/tests/testdata/message_fromWire16.spec b/src/lib/dns/tests/testdata/message_fromWire16.spec
new file mode 100644
index 0000000..be0abc3
--- /dev/null
+++ b/src/lib/dns/tests/testdata/message_fromWire16.spec
@@ -0,0 +1,21 @@
+#
+# Invalid TSIG: not in the additional section.
+#
+
+[custom]
+sections: header:question:tsig
+[header]
+id: 0x2d65
+rd: 1
+arcount: 1
+[question]
+name: www.example.com
+[tsig]
+as_rr: True
+rr_class: IN
+rr_name: www.example.com
+algorithm: hmac-md5
+time_signed: 0x4da8877a
+mac_size: 16
+mac: 0x227026ad297beee721ce6c6fff1e9ef3
+original_id: 0x2d65
diff --git a/src/lib/dns/tests/testdata/message_toText1.spec b/src/lib/dns/tests/testdata/message_toText1.spec
new file mode 100644
index 0000000..b31310e
--- /dev/null
+++ b/src/lib/dns/tests/testdata/message_toText1.spec
@@ -0,0 +1,24 @@
+#
+# A standard DNS message (taken from an invocation of dig)
+#
+
+[custom]
+sections: header:question:a/1:ns:a/2
+[header]
+id: 29174
+qr: 1
+aa: 1
+ancount: 1
+nscount: 1
+arcount: 1
+[question]
+name: www.example.com
+[a/1]
+as_rr: True
+rr_name: www.example.com
+address: 192.0.2.80
+[ns]
+as_rr: True
+[a/2]
+as_rr: True
+rr_name: ns.example.com
diff --git a/src/lib/dns/tests/testdata/message_toText1.txt b/src/lib/dns/tests/testdata/message_toText1.txt
new file mode 100644
index 0000000..58c7239
--- /dev/null
+++ b/src/lib/dns/tests/testdata/message_toText1.txt
@@ -0,0 +1,14 @@
+;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 29174
+;; flags: qr aa; QUERY: 1, ANSWER: 1, AUTHORITY: 1, ADDITIONAL: 1
+
+;; QUESTION SECTION:
+;www.example.com. IN A
+
+;; ANSWER SECTION:
+www.example.com. 86400 IN A 192.0.2.80
+
+;; AUTHORITY SECTION:
+example.com. 86400 IN NS ns.example.com.
+
+;; ADDITIONAL SECTION:
+ns.example.com. 86400 IN A 192.0.2.1
diff --git a/src/lib/dns/tests/testdata/message_toText2.spec b/src/lib/dns/tests/testdata/message_toText2.spec
new file mode 100644
index 0000000..978aab3
--- /dev/null
+++ b/src/lib/dns/tests/testdata/message_toText2.spec
@@ -0,0 +1,14 @@
+#
+# A standard DNS message with EDNS (taken from an invocation of dig)
+#
+
+[custom]
+sections: header:question:edns
+[header]
+id: 45981
+qr: 1
+rcode: refused
+arcount: 1
+[question]
+[edns]
+do: 1
diff --git a/src/lib/dns/tests/testdata/message_toText2.txt b/src/lib/dns/tests/testdata/message_toText2.txt
new file mode 100644
index 0000000..42cc2c1
--- /dev/null
+++ b/src/lib/dns/tests/testdata/message_toText2.txt
@@ -0,0 +1,8 @@
+;; ->>HEADER<<- opcode: QUERY, status: REFUSED, id: 45981
+;; flags: qr; QUERY: 1, ANSWER: 0, AUTHORITY: 0, ADDITIONAL: 1
+
+;; OPT PSEUDOSECTION:
+; EDNS: version: 0, flags: do; udp: 4096
+
+;; QUESTION SECTION:
+;example.com. IN A
diff --git a/src/lib/dns/tests/testdata/message_toText3.spec b/src/lib/dns/tests/testdata/message_toText3.spec
new file mode 100644
index 0000000..a74ea1b
--- /dev/null
+++ b/src/lib/dns/tests/testdata/message_toText3.spec
@@ -0,0 +1,31 @@
+#
+# A standard DNS message with TSIG (taken from an invocation of dig)
+#
+
+[custom]
+sections: header:question:a/1:ns:a/2:tsig
+[header]
+id: 10140
+qr: 1
+aa: 1
+ancount: 1
+nscount: 1
+arcount: 2
+[question]
+name: www.example.com
+[a/1]
+as_rr: True
+rr_name: www.example.com
+address: 192.0.2.80
+[ns]
+as_rr: True
+[a/2]
+as_rr: True
+rr_name: ns.example.com
+[tsig]
+as_rr: True
+rr_name: www.example.com
+algorithm: hmac-md5
+time_signed: 1304384318
+original_id: 10140
+mac: 0x5257c80396f2fa95b20c77ae9a652fb2
diff --git a/src/lib/dns/tests/testdata/message_toText3.txt b/src/lib/dns/tests/testdata/message_toText3.txt
new file mode 100644
index 0000000..359b9c5
--- /dev/null
+++ b/src/lib/dns/tests/testdata/message_toText3.txt
@@ -0,0 +1,17 @@
+;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 10140
+;; flags: qr aa; QUERY: 1, ANSWER: 1, AUTHORITY: 1, ADDITIONAL: 2
+
+;; QUESTION SECTION:
+;www.example.com. IN A
+
+;; ANSWER SECTION:
+www.example.com. 86400 IN A 192.0.2.80
+
+;; AUTHORITY SECTION:
+example.com. 86400 IN NS ns.example.com.
+
+;; ADDITIONAL SECTION:
+ns.example.com. 86400 IN A 192.0.2.1
+
+;; TSIG PSEUDOSECTION:
+www.example.com. 0 ANY TSIG hmac-md5.sig-alg.reg.int. 1304384318 300 16 UlfIA5by+pWyDHeummUvsg== 10140 NOERROR 0
diff --git a/src/lib/dns/tests/testdata/message_toWire2.spec b/src/lib/dns/tests/testdata/message_toWire2.spec
new file mode 100644
index 0000000..d256052
--- /dev/null
+++ b/src/lib/dns/tests/testdata/message_toWire2.spec
@@ -0,0 +1,21 @@
+#
+# A simple DNS query message with TSIG signed
+#
+
+[custom]
+sections: header:question:tsig
+[header]
+id: 0x2d65
+rd: 1
+arcount: 1
+[question]
+name: www.example.com
+[tsig]
+as_rr: True
+# TSIG QNAME won't be compressed
+rr_name: www.example.com
+algorithm: hmac-md5
+time_signed: 0x4da8877a
+mac_size: 16
+mac: 0x227026ad297beee721ce6c6fff1e9ef3
+original_id: 0x2d65
diff --git a/src/lib/dns/tests/testdata/message_toWire3.spec b/src/lib/dns/tests/testdata/message_toWire3.spec
new file mode 100644
index 0000000..c8e9453
--- /dev/null
+++ b/src/lib/dns/tests/testdata/message_toWire3.spec
@@ -0,0 +1,22 @@
+#
+# A simple DNS query message with EDNS and TSIG
+#
+
+[custom]
+sections: header:question:edns:tsig
+[header]
+id: 0x06cd
+rd: 1
+arcount: 2
+[question]
+name: www.example.com
+[edns]
+[tsig]
+as_rr: True
+# TSIG QNAME won't be compressed
+rr_name: www.example.com
+algorithm: hmac-md5
+time_signed: 0x4db60d1f
+mac_size: 16
+mac: 0x93444053881c83d7eb120e86f25b369e
+original_id: 0x06cd
diff --git a/src/lib/dns/tests/testdata/tsigrecord_toWire1.spec b/src/lib/dns/tests/testdata/tsigrecord_toWire1.spec
new file mode 100644
index 0000000..a25dc46
--- /dev/null
+++ b/src/lib/dns/tests/testdata/tsigrecord_toWire1.spec
@@ -0,0 +1,16 @@
+#
+# A simple TSIG RR (some of the parameters are taken from a live example
+# and don't have a specific meaning)
+#
+
+[custom]
+sections: tsig
+[tsig]
+as_rr: True
+# TSIG QNAME won't be compressed
+rr_name: www.example.com
+algorithm: hmac-md5
+time_signed: 0x4da8877a
+mac_size: 16
+mac: 0xdadadadadadadadadadadadadadadada
+original_id: 0x2d65
diff --git a/src/lib/dns/tests/testdata/tsigrecord_toWire2.spec b/src/lib/dns/tests/testdata/tsigrecord_toWire2.spec
new file mode 100644
index 0000000..f667e4c
--- /dev/null
+++ b/src/lib/dns/tests/testdata/tsigrecord_toWire2.spec
@@ -0,0 +1,19 @@
+#
+# TSIG RR after some names that could (unexpectedly) cause name compression
+#
+
+[custom]
+sections: name/1:name/2:tsig
+[name/1]
+name: hmac-md5.sig-alg.reg.int
+[name/2]
+name: foo.example.com
+[tsig]
+as_rr: True
+# TSIG QNAME won't be compressed
+rr_name: www.example.com
+algorithm: hmac-md5
+time_signed: 0x4da8877a
+mac_size: 16
+mac: 0xdadadadadadadadadadadadadadadada
+original_id: 0x2d65
diff --git a/src/lib/dns/tests/tsig_unittest.cc b/src/lib/dns/tests/tsig_unittest.cc
index 28189cc..efa0490 100644
--- a/src/lib/dns/tests/tsig_unittest.cc
+++ b/src/lib/dns/tests/tsig_unittest.cc
@@ -26,6 +26,7 @@
#include <util/buffer.h>
#include <util/encode/base64.h>
#include <util/unittests/newhook.h>
+#include <util/time_utilities.h>
#include <dns/message.h>
#include <dns/messagerenderer.h>
@@ -36,6 +37,7 @@
#include <dns/rrtype.h>
#include <dns/tsig.h>
#include <dns/tsigkey.h>
+#include <dns/tsigrecord.h>
#include <dns/tests/unittest_util.h>
@@ -49,14 +51,12 @@ using isc::UnitTestUtil;
// See dnssectime.cc
namespace isc {
-namespace dns {
-namespace tsig {
+namespace util {
namespace detail {
extern int64_t (*gettimeFunction)();
}
}
}
-}
namespace {
// See dnssectime_unittest.cc
@@ -75,7 +75,7 @@ protected:
{
// Make sure we use the system time by default so that we won't be
// confused due to other tests that tweak the time.
- tsig::detail::gettimeFunction = NULL;
+ isc::util::detail::gettimeFunction = NULL;
decodeBase64("SFuWd/q99SzF8Yzd1QbB9g==", secret);
tsig_ctx.reset(new TSIGContext(TSIGKey(test_name,
@@ -87,7 +87,7 @@ protected:
secret.size())));
}
~TSIGTest() {
- tsig::detail::gettimeFunction = NULL;
+ isc::util::detail::gettimeFunction = NULL;
}
// Many of the tests below create some DNS message and sign it under
@@ -209,7 +209,7 @@ const uint8_t common_expected_mac[] = {
0x21, 0xce, 0x6c, 0x6f, 0xff, 0x1e, 0x9e, 0xf3
};
TEST_F(TSIGTest, sign) {
- tsig::detail::gettimeFunction = testGetTime<0x4da8877a>;
+ isc::util::detail::gettimeFunction = testGetTime<0x4da8877a>;
{
SCOPED_TRACE("Sign test for query");
@@ -223,7 +223,7 @@ TEST_F(TSIGTest, sign) {
// non canonical) characters. The digest must be the same. It should actually
// be ensured at the level of TSIGKey, but we confirm that at this level, too.
TEST_F(TSIGTest, signUsingUpperCasedKeyName) {
- tsig::detail::gettimeFunction = testGetTime<0x4da8877a>;
+ isc::util::detail::gettimeFunction = testGetTime<0x4da8877a>;
TSIGContext cap_ctx(TSIGKey(Name("WWW.EXAMPLE.COM"),
TSIGKey::HMACMD5_NAME(),
@@ -239,7 +239,7 @@ TEST_F(TSIGTest, signUsingUpperCasedKeyName) {
// Same as the previous test, but for the algorithm name.
TEST_F(TSIGTest, signUsingUpperCasedAlgorithmName) {
- tsig::detail::gettimeFunction = testGetTime<0x4da8877a>;
+ isc::util::detail::gettimeFunction = testGetTime<0x4da8877a>;
TSIGContext cap_ctx(TSIGKey(test_name,
Name("HMAC-md5.SIG-alg.REG.int"),
@@ -325,7 +325,7 @@ TEST_F(TSIGTest, signExceptionSafety) {
// HMAC Size: 20
// HMAC: 415340c7daf824ed684ee586f7b5a67a2febc0d3
TEST_F(TSIGTest, signUsingHMACSHA1) {
- tsig::detail::gettimeFunction = testGetTime<0x4dae7d5f>;
+ isc::util::detail::gettimeFunction = testGetTime<0x4dae7d5f>;
secret.clear();
decodeBase64("MA+QDhXbyqUak+qnMFyTyEirzng=", secret);
@@ -350,7 +350,7 @@ TEST_F(TSIGTest, signUsingHMACSHA1) {
// Answer: www.example.com. 86400 IN A 192.0.2.1
// MAC: 8fcda66a7cd1a3b9948eb1869d384a9f
TEST_F(TSIGTest, signResponse) {
- tsig::detail::gettimeFunction = testGetTime<0x4da8877a>;
+ isc::util::detail::gettimeFunction = testGetTime<0x4da8877a>;
ConstTSIGRecordPtr tsig = createMessageAndSign(qid, test_name,
tsig_ctx.get());
@@ -385,7 +385,7 @@ TEST_F(TSIGTest, signResponse) {
// Answer: example.com. 86400 IN NS ns.example.com.
// MAC: 102458f7f62ddd7d638d746034130968
TEST_F(TSIGTest, signContinuation) {
- tsig::detail::gettimeFunction = testGetTime<0x4da8e951>;
+ isc::util::detail::gettimeFunction = testGetTime<0x4da8e951>;
const uint16_t axfr_qid = 0x3410;
const Name zone_name("example.com");
@@ -435,7 +435,7 @@ TEST_F(TSIGTest, signContinuation) {
// Error: 0x12 (BADTIME), Other Len: 6
// Other data: 00004da8be86
TEST_F(TSIGTest, badtimeResponse) {
- tsig::detail::gettimeFunction = testGetTime<0x4da8b9d6>;
+ isc::util::detail::gettimeFunction = testGetTime<0x4da8b9d6>;
const uint16_t test_qid = 0x7fc4;
ConstTSIGRecordPtr tsig = createMessageAndSign(test_qid, test_name,
@@ -444,7 +444,7 @@ TEST_F(TSIGTest, badtimeResponse) {
// "advance the clock" and try validating, which should fail due to BADTIME
// (verifyTentative actually doesn't check the time, though)
- tsig::detail::gettimeFunction = testGetTime<0x4da8be86>;
+ isc::util::detail::gettimeFunction = testGetTime<0x4da8be86>;
tsig_verify_ctx->verifyTentative(tsig, TSIGError::BAD_TIME());
EXPECT_EQ(TSIGError::BAD_TIME(), tsig_verify_ctx->getError());
@@ -468,7 +468,7 @@ TEST_F(TSIGTest, badtimeResponse) {
}
TEST_F(TSIGTest, badsigResponse) {
- tsig::detail::gettimeFunction = testGetTime<0x4da8877a>;
+ isc::util::detail::gettimeFunction = testGetTime<0x4da8877a>;
// Sign a simple message, and force the verification to fail with
// BADSIG.
@@ -489,7 +489,7 @@ TEST_F(TSIGTest, badsigResponse) {
TEST_F(TSIGTest, badkeyResponse) {
// A similar test as badsigResponse but for BADKEY
- tsig::detail::gettimeFunction = testGetTime<0x4da8877a>;
+ isc::util::detail::gettimeFunction = testGetTime<0x4da8877a>;
tsig_verify_ctx->verifyTentative(createMessageAndSign(qid, test_name,
tsig_ctx.get()),
TSIGError::BAD_KEY());
diff --git a/src/lib/dns/tests/tsigrecord_unittest.cc b/src/lib/dns/tests/tsigrecord_unittest.cc
new file mode 100644
index 0000000..a932b7f
--- /dev/null
+++ b/src/lib/dns/tests/tsigrecord_unittest.cc
@@ -0,0 +1,160 @@
+// 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 <vector>
+#include <sstream>
+
+#include <gtest/gtest.h>
+
+#include <util/buffer.h>
+
+#include <dns/exceptions.h>
+#include <dns/messagerenderer.h>
+#include <dns/name.h>
+#include <dns/rdata.h>
+#include <dns/rdataclass.h>
+#include <dns/tsig.h>
+#include <dns/tsigkey.h>
+#include <dns/tsigrecord.h>
+
+#include <dns/tests/unittest_util.h>
+
+using namespace std;
+using namespace isc::util;
+using namespace isc::dns;
+using namespace isc::dns::rdata;
+using isc::UnitTestUtil;
+
+namespace {
+class TSIGRecordTest : public ::testing::Test {
+protected:
+ TSIGRecordTest() :
+ test_name("www.example.com"), test_mac(16, 0xda),
+ test_rdata(any::TSIG(TSIGKey::HMACMD5_NAME(), 0x4da8877a,
+ TSIGContext::DEFAULT_FUDGE,
+ test_mac.size(), &test_mac[0],
+ 0x2d65, 0, 0, NULL)),
+ test_record(test_name, test_rdata),
+ buffer(0), renderer(buffer)
+ {}
+ const Name test_name;
+ vector<unsigned char> test_mac;
+ const any::TSIG test_rdata;
+ const TSIGRecord test_record;
+ OutputBuffer buffer;
+ MessageRenderer renderer;
+ vector<unsigned char> data;
+};
+
+TEST_F(TSIGRecordTest, getName) {
+ EXPECT_EQ(test_name, test_record.getName());
+}
+
+TEST_F(TSIGRecordTest, getLength) {
+ // 85 = 17 + 26 + 16 + 26
+ // len(www.example.com) = 17
+ // len(hmac-md5.sig-alg.reg.int) = 26
+ // len(MAC) = 16
+ // the rest are fixed length fields (26 in total)
+ EXPECT_EQ(85, test_record.getLength());
+}
+
+TEST_F(TSIGRecordTest, fromParams) {
+ // Construct the same TSIG RR as test_record from parameters.
+ // See the getLength test for the magic number of 85 (although it
+ // actually doesn't matter)
+ const TSIGRecord record(test_name, TSIGRecord::getClass(),
+ TSIGRecord::getTTL(), test_rdata, 85);
+ // Perform straight sanity checks
+ EXPECT_EQ(test_name, record.getName());
+ EXPECT_EQ(85, record.getLength());
+ EXPECT_EQ(0, test_rdata.compare(record.getRdata()));
+
+ // The constructor doesn't check the length...
+ EXPECT_NO_THROW(TSIGRecord(test_name, TSIGRecord::getClass(),
+ TSIGRecord::getTTL(), test_rdata, 82));
+ // ...even for impossibly small values...
+ EXPECT_NO_THROW(TSIGRecord(test_name, TSIGRecord::getClass(),
+ TSIGRecord::getTTL(), test_rdata, 1));
+ // ...or too large values.
+ EXPECT_NO_THROW(TSIGRecord(test_name, TSIGRecord::getClass(),
+ TSIGRecord::getTTL(), test_rdata, 65536));
+
+ // RDATA must indeed be TSIG
+ EXPECT_THROW(TSIGRecord(test_name, TSIGRecord::getClass(),
+ TSIGRecord::getTTL(), in::A("192.0.2.1"), 85),
+ DNSMessageFORMERR);
+
+ // Unexpected class
+ EXPECT_THROW(TSIGRecord(test_name, RRClass::IN(), TSIGRecord::getTTL(),
+ test_rdata, 85),
+ DNSMessageFORMERR);
+
+ // Unexpected TTL (simply ignored)
+ EXPECT_NO_THROW(TSIGRecord(test_name, TSIGRecord::getClass(),
+ RRTTL(3600), test_rdata, 85));
+}
+
+TEST_F(TSIGRecordTest, recordToWire) {
+ UnitTestUtil::readWireData("tsigrecord_toWire1.wire", data);
+ EXPECT_EQ(1, test_record.toWire(renderer));
+ EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
+ renderer.getData(), renderer.getLength(),
+ &data[0], data.size());
+
+ // Same test for a dumb buffer
+ buffer.clear();
+ EXPECT_EQ(1, test_record.toWire(buffer));
+ EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
+ buffer.getData(), buffer.getLength(),
+ &data[0], data.size());
+}
+
+TEST_F(TSIGRecordTest, recordToOLongToWire) {
+ // By setting the limit to "record length - 1", it will fail, and the
+ // renderer will be marked as "truncated".
+ renderer.setLengthLimit(test_record.getLength() - 1);
+ EXPECT_FALSE(renderer.isTruncated()); // not marked before render attempt
+ EXPECT_EQ(0, test_record.toWire(renderer));
+ EXPECT_TRUE(renderer.isTruncated());
+}
+
+TEST_F(TSIGRecordTest, recordToWireAfterNames) {
+ // A similar test but the TSIG RR follows some domain names that could
+ // cause name compression inside TSIG. Our implementation shouldn't
+ // compress either owner (key) name or the algorithm name. This test
+ // confirms that.
+
+ UnitTestUtil::readWireData("tsigrecord_toWire2.wire", data);
+ renderer.writeName(TSIGKey::HMACMD5_NAME());
+ renderer.writeName(Name("foo.example.com"));
+ EXPECT_EQ(1, test_record.toWire(renderer));
+ EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
+ renderer.getData(), renderer.getLength(),
+ &data[0], data.size());
+}
+
+TEST_F(TSIGRecordTest, toText) {
+ EXPECT_EQ("www.example.com. 0 ANY TSIG hmac-md5.sig-alg.reg.int. "
+ "1302890362 300 16 2tra2tra2tra2tra2tra2g== 11621 NOERROR 0\n",
+ test_record.toText());
+}
+
+// test operator<<. We simply confirm it appends the result of toText().
+TEST_F(TSIGRecordTest, LeftShiftOperator) {
+ ostringstream oss;
+ oss << test_record;
+ EXPECT_EQ(test_record.toText(), oss.str());
+}
+} // end namespace
diff --git a/src/lib/dns/tsig.cc b/src/lib/dns/tsig.cc
index 48ac5e0..8e8301d 100644
--- a/src/lib/dns/tsig.cc
+++ b/src/lib/dns/tsig.cc
@@ -24,6 +24,7 @@
#include <exceptions/exceptions.h>
#include <util/buffer.h>
+#include <util/time_utilities.h>
#include <dns/rdataclass.h>
#include <dns/rrclass.h>
@@ -41,38 +42,10 @@ using namespace isc::dns::rdata;
namespace isc {
namespace dns {
-
-// Borrowed from dnssectime.cc. This trick should be unified somewhere.
-namespace tsig {
-namespace detail {
-int64_t (*gettimeFunction)() = NULL;
-}
-}
-
-namespace {
-int64_t
-gettimeofdayWrapper() {
- using namespace tsig::detail;
- if (gettimeFunction != NULL) {
- return (gettimeFunction());
- }
-
- struct timeval now;
- gettimeofday(&now, NULL);
-
- return (static_cast<int64_t>(now.tv_sec));
-}
-}
-
namespace {
typedef boost::shared_ptr<HMAC> HMACPtr;
}
-const RRClass&
-TSIGRecord::getClass() {
- return (RRClass::ANY());
-}
-
struct TSIGContext::TSIGContextImpl {
TSIGContextImpl(const TSIGKey& key) :
state_(INIT), key_(key), error_(Rcode::NOERROR()),
@@ -118,7 +91,7 @@ TSIGContext::sign(const uint16_t qid, const void* const data,
// represents a value in the expected range. (In reality, however,
// gettimeofdayWrapper() will return a positive integer that will fit
// in 48 bits)
- const uint64_t now = (gettimeofdayWrapper() & 0x0000ffffffffffffULL);
+ const uint64_t now = (detail::gettimeWrapper() & 0x0000ffffffffffffULL);
// For responses adjust the error code.
if (impl_->state_ == CHECKED) {
@@ -129,6 +102,7 @@ TSIGContext::sign(const uint16_t qid, const void* const data,
// specified in Section 4.3 of RFC2845.
if (error == TSIGError::BAD_SIG() || error == TSIGError::BAD_KEY()) {
ConstTSIGRecordPtr tsig(new TSIGRecord(
+ impl_->key_.getKeyName(),
any::TSIG(impl_->key_.getAlgorithmName(),
now, DEFAULT_FUDGE, 0, NULL,
qid, error.getCode(), 0, NULL)));
@@ -198,6 +172,7 @@ TSIGContext::sign(const uint16_t qid, const void* const data,
// Get the final digest, update internal state, then finish.
vector<uint8_t> digest = hmac->sign();
ConstTSIGRecordPtr tsig(new TSIGRecord(
+ impl_->key_.getKeyName(),
any::TSIG(impl_->key_.getAlgorithmName(),
time_signed, DEFAULT_FUDGE,
digest.size(), &digest[0],
diff --git a/src/lib/dns/tsig.h b/src/lib/dns/tsig.h
index 55ab41e..fbcb1bb 100644
--- a/src/lib/dns/tsig.h
+++ b/src/lib/dns/tsig.h
@@ -15,84 +15,14 @@
#ifndef __TSIG_H
#define __TSIG_H 1
-#include <boost/shared_ptr.hpp>
#include <boost/noncopyable.hpp>
-#include <dns/rdataclass.h>
#include <dns/tsigerror.h>
#include <dns/tsigkey.h>
+#include <dns/tsigrecord.h>
namespace isc {
namespace dns {
-/// TSIG resource record.
-///
-/// A \c TSIGRecord class object represents a TSIG resource record and is
-/// responsible for conversion to and from wire format TSIG record based on
-/// the protocol specification (RFC2845).
-/// This class is provided so that other classes and applications can handle
-/// TSIG without knowing protocol details of TSIG, such as that it uses a
-/// fixed constant of TTL.
-///
-/// \note So the plan is to eventually provide a \c toWire() method and
-/// the "from wire" constructor. They are not yet provided in this initial
-/// step.
-///
-/// \note
-/// This class could be a derived class of \c AbstractRRset. That way
-/// it would be able to be used in a polymorphic way; for example,
-/// an application can construct a TSIG RR by itself and insert it to a
-/// \c Message object as a generic RRset. On the other hand, it would mean
-/// this class would have to implement an \c RdataIterator (even though it
-/// can be done via straightforward forwarding) while the iterator is mostly
-/// redundant since there should be one and only one RDATA for a valid TSIG
-/// RR. Likewise, some methods such as \c setTTL() method wouldn't be well
-/// defined due to such special rules for TSIG as using a fixed TTL.
-/// Overall, TSIG is a very special RR type that simply uses the compatible
-/// resource record format, and it will be unlikely that a user wants to
-/// handle it through a generic interface in a polymorphic way.
-/// We therefore chose to define it as a separate class. This is also
-/// similar to why \c EDNS is a separate class.
-class TSIGRecord {
-public:
- /// Constructor from TSIG RDATA
- ///
- /// \exception std::bad_alloc Resource allocation for copying the RDATA
- /// fails
- explicit TSIGRecord(const rdata::any::TSIG& tsig_rdata) :
- rdata_(tsig_rdata)
- {}
-
- /// Return the RDATA of the TSIG RR
- ///
- /// \exception None
- const rdata::any::TSIG& getRdata() const { return (rdata_); }
-
- /// \name Protocol constants and defaults
- ///
- //@{
- /// Return the RR class of TSIG
- ///
- /// TSIG always uses the ANY RR class. This static method returns it,
- /// when, though unlikely, an application wants to know which class TSIG
- /// is supposed to use.
- ///
- /// \exception None
- static const RRClass& getClass();
-
- /// The TTL value to be used in TSIG RRs.
- static const uint32_t TSIG_TTL = 0;
- //@}
-
-private:
- const rdata::any::TSIG rdata_;
-};
-
-/// A pointer-like type pointing to a \c TSIGRecord object.
-typedef boost::shared_ptr<TSIGRecord> TSIGRecordPtr;
-
-/// A pointer-like type pointing to an immutable \c TSIGRecord object.
-typedef boost::shared_ptr<const TSIGRecord> ConstTSIGRecordPtr;
-
/// TSIG session context.
///
/// The \c TSIGContext class maintains a context of a signed session of
diff --git a/src/lib/dns/tsigrecord.cc b/src/lib/dns/tsigrecord.cc
new file mode 100644
index 0000000..40ea6c2
--- /dev/null
+++ b/src/lib/dns/tsigrecord.cc
@@ -0,0 +1,145 @@
+// 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 <ostream>
+#include <string>
+
+#include <util/buffer.h>
+
+#include <dns/exceptions.h>
+#include <dns/messagerenderer.h>
+#include <dns/rrclass.h>
+#include <dns/rrttl.h>
+#include <dns/tsigrecord.h>
+
+using namespace isc::util;
+using namespace isc::dns::rdata;
+
+namespace {
+// Internally used constants:
+
+// Size in octets for the RR type, class TTL, RDLEN fields.
+const size_t RR_COMMON_LEN = 10;
+
+// Size in octets for the fixed part of TSIG RDATAs.
+// - Time Signed (6)
+// - Fudge (2)
+// - MAC Size (2)
+// - Original ID (2)
+// - Error (2)
+// - Other Len (2)
+const size_t RDATA_COMMON_LEN = 16;
+}
+
+namespace isc {
+namespace dns {
+TSIGRecord::TSIGRecord(const Name& key_name,
+ const rdata::any::TSIG& tsig_rdata) :
+ key_name_(key_name), rdata_(tsig_rdata),
+ length_(RR_COMMON_LEN + RDATA_COMMON_LEN + key_name_.getLength() +
+ rdata_.getAlgorithm().getLength() +
+ rdata_.getMACSize() + rdata_.getOtherLen())
+{}
+
+namespace {
+// This is a straightforward wrapper of dynamic_cast<const any::TSIG&>.
+// We use this so that we can throw the DNSMessageFORMERR exception when
+// unexpected type of RDATA is detected in the member initialization list
+// of the constructor below.
+const any::TSIG&
+castToTSIGRdata(const rdata::Rdata& rdata) {
+ try {
+ return (dynamic_cast<const any::TSIG&>(rdata));
+ } catch (std::bad_cast&) {
+ isc_throw(DNSMessageFORMERR,
+ "TSIG record is being constructed from "
+ "incompatible RDATA:" << rdata.toText());
+ }
+}
+}
+
+TSIGRecord::TSIGRecord(const Name& name, const RRClass& rrclass,
+ const RRTTL&, // we ignore TTL
+ const rdata::Rdata& rdata,
+ size_t length) :
+ key_name_(name), rdata_(castToTSIGRdata(rdata)), length_(length)
+{
+ if (rrclass != getClass()) {
+ isc_throw(DNSMessageFORMERR, "Unexpected TSIG RR class: " << rrclass);
+ }
+}
+
+const RRClass&
+TSIGRecord::getClass() {
+ return (RRClass::ANY());
+}
+
+const RRTTL&
+TSIGRecord::getTTL() {
+ static RRTTL ttl(TSIG_TTL);
+ return (ttl);
+}
+
+namespace {
+template <typename OUTPUT>
+void
+toWireCommon(OUTPUT& output, const rdata::any::TSIG& rdata) {
+ // RR type, class, TTL are fixed constants.
+ RRType::TSIG().toWire(output);
+ TSIGRecord::getClass().toWire(output);
+ output.writeUint32(TSIGRecord::TSIG_TTL);
+
+ // RDLEN
+ output.writeUint16(RDATA_COMMON_LEN + rdata.getAlgorithm().getLength() +
+ rdata.getMACSize() + rdata.getOtherLen());
+
+ // TSIG RDATA
+ rdata.toWire(output);
+}
+}
+
+int
+TSIGRecord::toWire(AbstractMessageRenderer& renderer) const {
+ // If adding the TSIG would exceed the size limit, don't do it.
+ if (renderer.getLength() + length_ > renderer.getLengthLimit()) {
+ renderer.setTruncated();
+ return (0);
+ }
+
+ // key name = owner. note that we disable compression.
+ renderer.writeName(key_name_, false);
+ toWireCommon(renderer, rdata_);
+ return (1);
+}
+
+int
+TSIGRecord::toWire(OutputBuffer& buffer) const {
+ key_name_.toWire(buffer);
+ toWireCommon(buffer, rdata_);
+ return (1);
+}
+
+std::string
+TSIGRecord::toText() const {
+ return (key_name_.toText() + " " + RRTTL(TSIG_TTL).toText() + " " +
+ getClass().toText() + " " + RRType::TSIG().toText() + " " +
+ rdata_.toText() + "\n");
+}
+
+std::ostream&
+operator<<(std::ostream& os, const TSIGRecord& record) {
+ return (os << record.toText());
+}
+} // namespace dns
+} // namespace isc
diff --git a/src/lib/dns/tsigrecord.h b/src/lib/dns/tsigrecord.h
new file mode 100644
index 0000000..e42edf1
--- /dev/null
+++ b/src/lib/dns/tsigrecord.h
@@ -0,0 +1,285 @@
+// 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 __TSIGRECORD_H
+#define __TSIGRECORD_H 1
+
+#include <ostream>
+#include <string>
+
+#include <boost/shared_ptr.hpp>
+
+#include <util/buffer.h>
+
+#include <dns/name.h>
+#include <dns/rdataclass.h>
+
+namespace isc {
+namespace util {
+class OutputBuffer;
+}
+namespace dns {
+class AbstractMessageRenderer;
+
+/// TSIG resource record.
+///
+/// A \c TSIGRecord class object represents a TSIG resource record and is
+/// responsible for conversion to and from wire format TSIG record based on
+/// the protocol specification (RFC2845).
+/// This class is provided so that other classes and applications can handle
+/// TSIG without knowing protocol details of TSIG, such as that it uses a
+/// fixed constant of TTL.
+///
+/// \todo So the plan is to eventually provide the "from wire" constructor.
+/// It's not yet provided in the current phase of development.
+///
+/// \note
+/// This class could be a derived class of \c AbstractRRset. That way
+/// it would be able to be used in a polymorphic way; for example,
+/// an application can construct a TSIG RR by itself and insert it to a
+/// \c Message object as a generic RRset. On the other hand, it would mean
+/// this class would have to implement an \c RdataIterator (even though it
+/// can be done via straightforward forwarding) while the iterator is mostly
+/// redundant since there should be one and only one RDATA for a valid TSIG
+/// RR. Likewise, some methods such as \c setTTL() method wouldn't be well
+/// defined due to such special rules for TSIG as using a fixed TTL.
+/// Overall, TSIG is a very special RR type that simply uses the compatible
+/// resource record format, and it will be unlikely that a user wants to
+/// handle it through a generic interface in a polymorphic way.
+/// We therefore chose to define it as a separate class. This is also
+/// similar to why \c EDNS is a separate class.
+class TSIGRecord {
+public:
+ ///
+ /// \name Constructors
+ ///
+ /// We use the default copy constructor, default copy assignment operator,
+ /// (and default destructor) intentionally.
+ //@{
+ /// Constructor from TSIG key name and RDATA
+ ///
+ /// \exception std::bad_alloc Resource allocation for copying the name or
+ /// RDATA fails
+ TSIGRecord(const Name& key_name, const rdata::any::TSIG& tsig_rdata);
+
+ /// Constructor from resource record (RR) parameters.
+ ///
+ /// This constructor is intended to be used in the context of parsing
+ /// an incoming DNS message that contains a TSIG. The parser would
+ /// first extract the owner name, RR type (which is TSIG) class, TTL and
+ /// the TSIG RDATA from the message. This constructor is expected to
+ /// be given these RR parameters (except the RR type, because it must be
+ /// TSIG).
+ ///
+ /// According to RFC2845, a TSIG RR uses fixed RR class (ANY) and TTL (0).
+ /// If the RR class is different from the expected one, this
+ /// implementation considers it an invalid record and throws an exception
+ /// of class \c DNSMessageFORMERR. On the other hand, the TTL is simply
+ /// ignored (in that sense we could even omit that parameter, but it's
+ /// still included if and when we want to change the policy). RFC2845
+ /// is silent about what the receiver should do if it sees an unexpected
+ /// RR class or TTL in a TSIG RR. This implementation simply follows what
+ /// BIND 9 does (it is not clear why BIND 9 employs the "inconsistent"
+ /// policy).
+ ///
+ /// Likewise, if \c rdata is not of type \c any::TSIG, an exception of
+ /// class DNSMessageFORMERR will be thrown. When the caller is a
+ /// DNS message parser and builds \c rdata from incoming wire format
+ /// data as described above, this case happens when the RR class is
+ /// different from ANY (in the implementation, the type check takes place
+ /// before the explicit check against the RR class explained in the
+ /// previous paragraph).
+ ///
+ /// The \c length parameter is intended to be the length of the TSIG RR
+ /// (from the beginning of the owner name to the end of the RDATA) when
+ /// the caller is a DNS message parser. Note that it is the actual length
+ /// for the RR in the format; if the owner name or the algorithm name
+ /// (in the RDATA) is compressed (although the latter should not be
+ /// compressed according to RFC3597), the length must be the size of the
+ /// compressed data. The length is recorded inside the class and will
+ /// be returned via subsequent calls to \c getLength(). It's intended to
+ /// be used in the context TSIG verification; in the verify process
+ /// the MAC computation must be performed for the original data without
+ /// TSIG, so, to avoid parsing the entire data in the verify process
+ /// again, it's necessary to record information that can identify the
+ /// length to be digested for the MAC. This parameter serves for that
+ /// purpose.
+ ///
+ /// \note Since the constructor doesn't take the wire format data per se,
+ /// it doesn't (and cannot) check the validity of \c length, and simply
+ /// accepts any given value. It even accepts obviously invalid values
+ /// such as 0. It's caller's responsibility to provide a valid value of
+ /// length, and, the verifier's responsibility to use the length safely.
+ ///
+ /// <b>DISCUSSION:</b> this design is fragile in that it introduces
+ /// a tight coupling between message parsing and TSIG verification via
+ /// the \c TSIGRecord class. In terms of responsibility decoupling,
+ /// the ideal way to have \c TSIGRecord remember the entire wire data
+ /// along with the length of the TSIG. Then in the TSIG verification
+ /// we could refer to the necessary potion of data solely from a
+ /// \c TSIGRecord object. However, this approach would require expensive
+ /// heavy copy of the original data or introduce another kind of coupling
+ /// between the data holder and this class (if the original data is freed
+ /// while a \c TSIGRecord object referencing the data still exists, the
+ /// result will be catastrophic). As a "best current compromise", we
+ /// use the current design. We may reconsider it if it turns out to
+ /// cause a big problem or we come up with a better idea.
+ ///
+ /// \exception DNSMessageFORMERR Given RR parameters are invalid for TSIG.
+ /// \exception std::bad_alloc Internal resource allocation fails.
+ ///
+ /// \param name The owner name of the TSIG RR
+ /// \param rrclass The RR class of the RR. Must be \c RRClass::ANY()
+ /// (see above)
+ /// \param ttl The TTL of the RR. Expected to be a zero TTL, but
+ /// actually ignored in this implementation.
+ /// \param rdata The RDATA of the RR. Must be of type \c any::TSIG.
+ /// \param length The size of the RR (see above)
+ TSIGRecord(const Name& name, const RRClass& rrclass, const RRTTL& ttl,
+ const rdata::Rdata& rdata, size_t length);
+ //@}
+
+ /// Return the owner name of the TSIG RR, which is the TSIG key name
+ ///
+ /// \exception None
+ const Name& getName() const { return (key_name_); }
+
+ /// Return the RDATA of the TSIG RR
+ ///
+ /// \exception None
+ const rdata::any::TSIG& getRdata() const { return (rdata_); }
+
+ /// \name Protocol constants and defaults
+ ///
+ //@{
+ /// Return the RR class of TSIG
+ ///
+ /// TSIG always uses the ANY RR class. This static method returns it,
+ /// when, though unlikely, an application wants to know which class TSIG
+ /// is supposed to use.
+ ///
+ /// \exception None
+ static const RRClass& getClass();
+
+ /// Return the TTL value of TSIG
+ ///
+ /// TSIG always uses 0 TTL. This static method returns it,
+ /// when, though unlikely, an application wants to know the TTL TSIG
+ /// is supposed to use.
+ ///
+ /// \exception None
+ static const RRTTL& getTTL();
+ //@}
+
+ /// Return the length of the TSIG record
+ ///
+ /// When constructed from the key name and RDATA, it is the length of
+ /// the record when it is rendered by the \c toWire() method.
+ ///
+ /// \note When constructed "from wire", that will mean the length of
+ /// the wire format data for the TSIG RR. The length will be necessary
+ /// to verify the message once parse is completed.
+ ///
+ /// \exception None
+ size_t getLength() const { return (length_); }
+
+ /// \brief Render the \c TSIG RR in the wire format.
+ ///
+ /// This method renders the TSIG record as a form of a DNS TSIG RR
+ /// via \c renderer, which encapsulates output buffer and other rendering
+ /// contexts.
+ ///
+ /// Normally this version of \c toWire() method tries to compress the
+ /// owner name of the RR whenever possible, but this method intentionally
+ /// skips owner name compression. This is due to a report that some
+ /// Windows clients refuse a TSIG if its owner name is compressed
+ /// (See http://marc.info/?l=bind-workers&m=126637138430819&w=2).
+ /// Reportedly this seemed to be specific to GSS-TSIG, but this
+ /// implementation skip compression regardless of the algorithm.
+ ///
+ /// If by adding the TSIG RR the message size would exceed the limit
+ /// maintained in \c renderer, this method skips rendering the RR
+ /// and returns 0 and mark \c renderer as "truncated" (so that a
+ /// subsequent call to \c isTruncated() on \c renderer will result in
+ /// \c true); otherwise it returns 1, which is the number of RR
+ /// rendered.
+ ///
+ /// \note If the caller follows the specification of adding TSIG
+ /// as described in RFC2845, this should not happen; the caller is
+ /// generally expected to leave a sufficient room in the message for
+ /// the TSIG. But this method checks the unexpected case nevertheless.
+ ///
+ /// \exception std::bad_alloc Internal resource allocation fails (this
+ /// should be rare).
+ ///
+ /// \param renderer DNS message rendering context that encapsulates the
+ /// output buffer and name compression information.
+ /// \return 1 if the TSIG RR fits in the message size limit; otherwise 0.
+ int toWire(AbstractMessageRenderer& renderer) const;
+
+ /// \brief Render the \c TSIG RR in the wire format.
+ ///
+ /// This method is same as \c toWire(AbstractMessageRenderer&)const
+ /// except it renders the RR in an \c OutputBuffer and therefore
+ /// does not care about message size limit.
+ /// As a consequence it always returns 1.
+ int toWire(isc::util::OutputBuffer& buffer) const;
+
+ /// Convert the TSIG record to a string.
+ ///
+ /// The output format is the same as the result of \c toText() for
+ /// other normal types of RRsets (with always using the same RR class
+ /// and TTL). It also ends with a newline.
+ ///
+ /// \exception std::bad_alloc Internal resource allocation fails (this
+ /// should be rare).
+ ///
+ /// \return A string representation of \c TSIG record
+ std::string toText() const;
+
+ /// The TTL value to be used in TSIG RRs.
+ static const uint32_t TSIG_TTL = 0;
+ //@}
+
+private:
+ const Name key_name_;
+ const rdata::any::TSIG rdata_;
+ const size_t length_;
+};
+
+/// A pointer-like type pointing to a \c TSIGRecord object.
+typedef boost::shared_ptr<TSIGRecord> TSIGRecordPtr;
+
+/// A pointer-like type pointing to an immutable \c TSIGRecord object.
+typedef boost::shared_ptr<const TSIGRecord> ConstTSIGRecordPtr;
+
+/// Insert the \c TSIGRecord as a string into stream.
+///
+/// This method convert \c record into a string and inserts it into the
+/// output stream \c os.
+///
+/// \param os A \c std::ostream object on which the insertion operation is
+/// performed.
+/// \param record A \c TSIGRecord object output by the operation.
+/// \return A reference to the same \c std::ostream object referenced by
+/// parameter \c os after the insertion operation.
+std::ostream& operator<<(std::ostream& os, const TSIGRecord& record);
+}
+}
+
+#endif // __TSIGRECORD_H
+
+// Local Variables:
+// mode: c++
+// End:
diff --git a/src/lib/log/Makefile.am b/src/lib/log/Makefile.am
index 537d08f..c27b3e4 100644
--- a/src/lib/log/Makefile.am
+++ b/src/lib/log/Makefile.am
@@ -16,7 +16,7 @@ liblog_la_SOURCES += logger_impl.cc logger_impl.h
liblog_la_SOURCES += logger_support.cc logger_support.h
liblog_la_SOURCES += messagedef.cc messagedef.h
liblog_la_SOURCES += message_dictionary.cc message_dictionary.h
-liblog_la_SOURCES += message_exception.h message_exception.cc
+liblog_la_SOURCES += message_exception.h
liblog_la_SOURCES += message_initializer.cc message_initializer.h
liblog_la_SOURCES += message_reader.cc message_reader.h
liblog_la_SOURCES += message_types.h
diff --git a/src/lib/log/README b/src/lib/log/README
index d01b12f..529eefc 100644
--- a/src/lib/log/README
+++ b/src/lib/log/README
@@ -44,7 +44,7 @@ stored definitions; this updated text is logged. However, to aid support,
the message identifier so in the example above, the message finally logged
would be something like:
- OPENIN, unable to open a.txt for input
+ FAC_OPENIN, unable to open a.txt for input
Using The System
@@ -55,17 +55,15 @@ The steps in using the system are:
mnemonic for the message, typically 6-12 characters long - and a message.
The file is described in more detail below.
- Ideally the file should have a file type of ".msg".
+ Ideally the file should have a file type of ".mes".
-2. Run it through the message compiler to produce the .h and .cc files. It
- is intended that this step be included in the build process. However,
- for now run the compiler (found in the "compiler" subdirectory) manually.
- The only argument is the name of the message file: it will produce as
- output two files, having the same name as the input file but with file
- types of ".h" and ".cc".
-
- The compiler is built in the "compiler" subdirectory of the "src/lib/log"
- directory.
+2. Run it through the message compiler to produce the .h and .cc files. This
+ step should be included in the build process. (For an example of how to
+ do this, see src/lib/nsas/Makefile.am.) During development though, the
+ message compiler (found in the "src/lib/log/compiler" directory) will need
+ to be run manually. The only argument is the name of the message file: it
+ will produce as output two files in the default directory, having the same
+ name as the input file but with file types of ".h" and ".cc".
3. Include the .h file in your source code to define message symbols, and
make sure that the .cc file is compiled and linked into your program -
@@ -96,11 +94,12 @@ An example file could be:
$PREFIX TEST_
$NAMESPACE isc::log
-TEST1 message %1 is much too large
-+ This message is a test for the general message code
-UNKNOWN unknown message
-+ Issued when the message is unknown.
+% TEST1 message %1 is much too large
+This message is a test for the general message code
+
+% UNKNOWN unknown message
+Issued when the message is unknown.
-- END --
@@ -117,23 +116,31 @@ Points to note:
* Lines starting $ are directives. At present, two directives are recognised:
- * $PREFIX, which has one argument: the string used to prefix symbols. If
- absent, there is no prefix to the symbols. (Prefixes are explained below.)
+ * $PREFIX, which has one optional argument: the string used to prefix symbols.
+ If absent, there is no prefix to the symbols. (Prefixes are explained below.)
+
* $NAMESPACE, which has one argument: the namespace in which the symbols are
- created. In the absence of a $NAMESPACE directive, symbols will be put
- in the global namespace.
+ created. In the absence of a $NAMESPACE directive, symbols will be put in
+ the anonymous namespace.
-* Lines starting + indicate an explanation for the preceding message. These
- are intended to be processed by a separate program and used to generate
- an error messages manual. However they are treated like comments by the
- message compiler. As with comments, these must be on a line by themselves;
- if inline, the text (including the leading "+") will be interpreted as
- part of the line.
+* Message lines. These start with a "%" and are followed by the message
+ identification and the message text, the latter including zero or more
+ replacement tokens, e.g.
-* Message lines. These comprise a symbol name and a message, which may
- include zero or more %1, %2... placeholder tokens. Symbol names will be
- upper-cased by the compiler.
+ % TEST message %1 is larger than the permitted length of %2
+ * There may be zero or more spaces between the leading "%" and the message
+ identification (which, in the example above, is the word "TEST").
+
+ * The replacement tokens are the strings "%1", "%2" etc. When a message
+ is logged, these are replaced with the arguments passed to the logging
+ call: %1 refers to the first argument, %2 to the second etc. Within the
+ message text, the placeholders can appear in any order, and placeholders
+ can be repeated.
+
+* Remaining lines indicate an explanation for the preceding message. These
+ are intended to be processed by a separate program and used to generate
+ an error messages manual. They are ignored by the message compiler.
Message Compiler
----------------
@@ -153,9 +160,8 @@ the form:
:
}
-The symbols define the keys in the global message dictionary.
-
-The namespace enclosing the symbols is set by the $NAMESPACE directive.
+The symbols define the keys in the global message dictionary, with the
+namespace enclosing the symbols set by the $NAMESPACE directive.
The "PREFIX_" part of the symbol name is the string defined in the $PREFIX
the argument to the directive. So "$PREFIX MSG_" would prefix the identifier
@@ -163,12 +169,20 @@ ABC with "MSG_" to give the symbol MSG_ABC. Similarly "$PREFIX E" would
prefix it with "E" to give the symbol EABC. If no $PREFIX is given, no
prefix appears (so the symbol in this example would be ABC).
+The prefix is "syntactic sugar". Generally all symbols in a given message file
+will be prefixed with the same set of letters. By extracting these into
+a separate prefix, it becomes easier to disambiguate the different symbols.
+
+There may be multiple $PREFIX directives in a file. A $PREFIX directive applies
+to all message definitions between it an the next $PREFIX directive. A $PREFIX
+directive with no arguments clears the current prefix.
+
2) A C++ source file (called <message-file-name>.cc) that holds the definitions
of the global symbols and code to insert the symbols and messages into the map.
Symbols are defined to be equal to strings holding the identifier, e.g.
- extern const isc::log::MessageID MSG_DUPLNS = "DUPLNS";
+ extern const isc::log::MessageID MSG_DUPLNS = "MSG_DUPLNS";
(The implementation allows symbols to be compared. However, use of strings
should not be assumed - a future implementation may change this.)
@@ -243,12 +257,10 @@ To use the current version of the logging:
highest-level debug messages and 99 for the lowest-level (and typically
more verbose) messages.
- c) Name of an external message file. This is the same as a standard message
- file, although it should not include any directives. (A single directive
- of a particular type will be ignored; multiple directives will cause the
- read of the file to fail with an error.) If a message is replaced, the
- message should include the same printf-format directives in the same order
- as the original message.
+ c) The external message file. If present, this is the same as a standard
+ message file, although it should not include any directives. (A single
+ directive of a particular type will be ignored; multiple directives will
+ cause the read of the file to fail with an error.)
4. Issue logging calls using methods on logger, e.g.
@@ -264,6 +276,30 @@ To use the current version of the logging:
At present, the only logging is to the console.
+Efficiency Considerations
+-------------------------
+A common pattern in logging is a debug call of the form:
+
+ logger.debug(dbglevel, MSGID).arg(expensive_call()).arg(...
+
+... where "expensive_call()" is a function call to obtain logging information
+that may be computationally intensive. Although the cost may be justified
+when debugging is enabled, the cost is still incurred even if debugging is
+disabled and the debug() method returns without outputting anything. (The
+same may be true of other logging levels, although there are likely to be
+fewer calls to logger.info(), logger.error() etc. throughout the code and
+they are less likely to be disabled.)
+
+For this reason, a set of macros is provided and are called using the
+construct:
+
+ LOG_DEBUG(logger, dbglevel, MSGID).arg(expensive_call()).arg(...
+ LOG_INFO(logger, MSGID).arg(expensive_call()...)
+
+If these are used, the arguments passed to the arg() method are not evaluated
+if the relevant logging level is disabled.
+
+
Severity Guidelines
===================
When using logging, the question arises, what severity should a message be
@@ -361,9 +397,6 @@ is only one piece of code that does this functionality.
Outstanding Issues
==================
* Ability to configure system according to configuration database.
-* Update the build procedure to create .cc and .h files from the .msg file
- during the build process. (Requires that the message compiler is built
- first.)
log4cxx Issues
diff --git a/src/lib/log/compiler/message.cc b/src/lib/log/compiler/message.cc
index eb9ddca..457a62e 100644
--- a/src/lib/log/compiler/message.cc
+++ b/src/lib/log/compiler/message.cc
@@ -50,17 +50,13 @@ static const char* VERSION = "1.0-0";
/// \li A .cc file containing code that adds the messages to the program's
/// message dictionary at start-up time.
///
-/// Alternatively, the program can produce a .py file that contains the
-/// message definitions.
-///
-
/// \b Invocation<BR>
/// The program is invoked with the command:
///
/// <tt>message [-v | -h | \<message-file\>]</tt>
///
-/// It reads the message file and writes out two files of the same name but with
-/// extensions of .h and .cc.
+/// It reads the message file and writes out two files of the same name in the
+/// default directory but with extensions of .h and .cc.
///
/// \-v causes it to print the version number and exit. \-h prints a help
/// message (and exits).
@@ -251,14 +247,13 @@ writeClosingNamespace(ostream& output, const vector<string>& ns) {
///
/// \param file Name of the message file. The header file is written to a
/// file of the same name but with a .h suffix.
-/// \param prefix Prefix string to use in symbols
/// \param ns Namespace in which the definitions are to be placed. An empty
/// string indicates no namespace.
/// \param dictionary Dictionary holding the message definitions.
void
-writeHeaderFile(const string& file, const string& prefix,
- const vector<string>& ns_components, MessageDictionary& dictionary)
+writeHeaderFile(const string& file, const vector<string>& ns_components,
+ MessageDictionary& dictionary)
{
Filename message_file(file);
Filename header_file(Filename(message_file.name()).useAsDefault(".h"));
@@ -271,7 +266,7 @@ writeHeaderFile(const string& file, const string& prefix,
try {
if (hfile.fail()) {
- throw MessageException(MSG_OPNMSGOUT, header_file.fullName(),
+ throw MessageException(MSG_OPENOUT, header_file.fullName(),
strerror(errno));
}
@@ -294,7 +289,7 @@ writeHeaderFile(const string& file, const string& prefix,
vector<string> idents = sortedIdentifiers(dictionary);
for (vector<string>::const_iterator j = idents.begin();
j != idents.end(); ++j) {
- hfile << "extern const isc::log::MessageID " << prefix << *j << ";\n";
+ hfile << "extern const isc::log::MessageID " << *j << ";\n";
}
hfile << "\n";
@@ -305,7 +300,7 @@ writeHeaderFile(const string& file, const string& prefix,
// Report errors (if any) and exit
if (hfile.fail()) {
- throw MessageException(MSG_MSGWRTERR, header_file.fullName(),
+ throw MessageException(MSG_WRITERR, header_file.fullName(),
strerror(errno));
}
@@ -354,8 +349,8 @@ replaceNonAlphaNum(char c) {
/// to it. But until BIND-10 is ported to Windows, we won't know.
void
-writeProgramFile(const string& file, const string& prefix,
- const vector<string>& ns_components, MessageDictionary& dictionary)
+writeProgramFile(const string& file, const vector<string>& ns_components,
+ MessageDictionary& dictionary)
{
Filename message_file(file);
Filename program_file(Filename(message_file.name()).useAsDefault(".cc"));
@@ -364,7 +359,7 @@ writeProgramFile(const string& file, const string& prefix,
ofstream ccfile(program_file.fullName().c_str());
try {
if (ccfile.fail()) {
- throw MessageException(MSG_OPNMSGOUT, program_file.fullName(),
+ throw MessageException(MSG_OPENOUT, program_file.fullName(),
strerror(errno));
}
@@ -387,7 +382,7 @@ writeProgramFile(const string& file, const string& prefix,
vector<string> idents = sortedIdentifiers(dictionary);
for (vector<string>::const_iterator j = idents.begin();
j != idents.end(); ++j) {
- ccfile << "extern const isc::log::MessageID " << prefix << *j <<
+ ccfile << "extern const isc::log::MessageID " << *j <<
" = \"" << *j << "\";\n";
}
ccfile << "\n";
@@ -422,7 +417,7 @@ writeProgramFile(const string& file, const string& prefix,
// Report errors (if any) and exit
if (ccfile.fail()) {
- throw MessageException(MSG_MSGWRTERR, program_file.fullName(),
+ throw MessageException(MSG_WRITERR, program_file.fullName(),
strerror(errno));
}
@@ -518,13 +513,10 @@ main(int argc, char* argv[]) {
vector<string> ns_components = splitNamespace(reader.getNamespace());
// Write the header file.
- writeHeaderFile(message_file, reader.getPrefix(), ns_components,
- dictionary);
+ writeHeaderFile(message_file, ns_components, dictionary);
// Write the file that defines the message symbols and text
- writeProgramFile(message_file, reader.getPrefix(), ns_components,
- dictionary);
-
+ writeProgramFile(message_file, ns_components, dictionary);
// Finally, warn of any duplicates encountered.
warnDuplicates(reader);
diff --git a/src/lib/log/log_formatter.cc b/src/lib/log/log_formatter.cc
index 920d7dd..18c4741 100644
--- a/src/lib/log/log_formatter.cc
+++ b/src/lib/log/log_formatter.cc
@@ -27,7 +27,10 @@ replacePlaceholder(string* message, const string& arg,
string mark("%" + lexical_cast<string>(placeholder));
size_t pos(message->find(mark));
if (pos != string::npos) {
- message->replace(pos, mark.size(), arg);
+ do {
+ message->replace(pos, mark.size(), arg);
+ pos = message->find(mark, pos + arg.size());
+ } while (pos != string::npos);
} else {
// We're missing the placeholder, so add some complain
message->append(" @@Missing placeholder " + mark + " for '" + arg +
diff --git a/src/lib/log/log_formatter.h b/src/lib/log/log_formatter.h
index 3977eba..cda1d96 100644
--- a/src/lib/log/log_formatter.h
+++ b/src/lib/log/log_formatter.h
@@ -45,16 +45,12 @@ replacePlaceholder(std::string* message, const std::string& replacement,
/// no .arg call on the object, it is destroyed right away and we use the
/// destructor to output the text (but only in case we should output anything).
///
-/// If there's an .arg call, it replaces a placeholder with the argument
-/// converted to string and produces another Formatter. We mark the current
-/// Formatter so it won't output anything in it's destructor and the task
-/// to do the output is moved onto the new object. Again, the last one in
-/// the chain is destroyed without any modification and does the real output.
+/// If there's an .arg call, we return reference to the same object, so another
+/// .arg can be called on it. After the last .arg call is done, the object is
+/// destroyed and, again, we can produce the output.
///
/// Of course, if the logging is turned off, we don't bother with any replacing
-/// and just return new empty Formatter. As everything here is in the header
-/// file, compiler should be able to easily optimise most of the work with
-/// creating and destroying objects and simply do the replacing only.
+/// and just return.
///
/// User of logging code should not really care much about this class, only
/// call the .arg method to generate the correct output.
@@ -62,68 +58,64 @@ replacePlaceholder(std::string* message, const std::string& replacement,
/// The class is a template to allow easy testing. Also, we want everything
/// here in the header anyway and it doesn't depend on the details of what
/// Logger really is, so it doesn't hurt anything.
+///
+/// Also, if you are interested in the internals, you might find the copy
+/// constructor a bit strange. It deactivates the original formatter. We don't
+/// really want to support copying of the Formatter by user, but C++ needs a
+/// copy constructor when returning from the logging functions, so we need one.
+/// And if we did not deactivate the original Formatter, that one would get
+/// destroyed before any call to .arg, producing an output, and then the one
+/// the .arg calls are called on would get destroyed as well, producing output
+/// again. So, think of this behaviour as soul moving from one to another.
template<class Logger> class Formatter {
private:
- /// \brief The logger we will use to output the final message
- Logger* logger_;
+ /// \brief The logger we will use to output the final message.
+ ///
+ /// If NULL, we are not active and should not produce anything.
+ mutable Logger* logger_;
/// \brief Prefix (eg. "ERROR", "DEBUG" or like that)
const char* prefix_;
/// \brief The messages with %1, %2... placeholders
std::string* message_;
/// \brief Which will be the next placeholder to replace
- const unsigned nextPlaceholder_;
- /// \brief Should we do output?
- mutable bool active_;
+ unsigned nextPlaceholder_;
Formatter& operator =(const Formatter& other);
public:
/// \brief Constructor of "active" formatter
///
- /// This will create a formatter in active mode -- the one when it
- /// will generate output.
+ /// This will create a formatter. If the arguments are set, it
+ /// will be active (will produce output). If you leave them all as NULL,
+ /// it will create an inactive Formatter -- one that'll produce no output.
///
/// It is not expected to be called by user of logging system directly.
///
- /// \param prefix The prefix, like "ERROR" or "DEBUG"
+ /// \param prefix The severity prefix, like "ERROR" or "DEBUG"
/// \param message The message with placeholders. We take ownership of
- /// it and we will modify the string. Must not be NULL and it's
- /// not checked.
- /// \param nextPlaceholder It is the number of next placeholder which
- /// should be replaced. It should be called with 1, higher numbers
- /// are used internally in the chain.
- /// \param logger The logger where the final output will go.
- Formatter(const char* prefix, std::string* message,
- const unsigned nextPlaceholder, Logger& logger) :
- logger_(&logger), prefix_(prefix), message_(message),
- nextPlaceholder_(nextPlaceholder), active_(true)
- {
- }
- /// \brief Constructor of "inactive" formatter
- ///
- /// It is not expected to be called by user of the logging system directly.
- ///
- /// This will create a formatter that produces no output.
- Formatter() :
- message_(NULL),
- nextPlaceholder_(0),
- active_(false)
+ /// it and we will modify the string. Must not be NULL unless
+ /// logger is also NULL, but it's not checked.
+ /// \param logger The logger where the final output will go, or NULL
+ /// if no output is wanted.
+ Formatter(const char* prefix = NULL, std::string* message = NULL,
+ Logger* logger = NULL) :
+ logger_(logger), prefix_(prefix), message_(message),
+ nextPlaceholder_(1)
{
}
Formatter(const Formatter& other) :
logger_(other.logger_), prefix_(other.prefix_),
- message_(other.message_), nextPlaceholder_(other.nextPlaceholder_),
- active_(other.active_)
+ message_(other.message_), nextPlaceholder_(other.nextPlaceholder_)
{
- other.active_ = false;
+ other.logger_ = false;
}
/// \brief Destructor.
//
/// This is the place where output happens if the formatter is active.
~ Formatter() {
- if (active_) {
+ if (logger_) {
logger_->output(prefix_, *message_);
+ delete message_;
}
- delete message_;
}
/// \brief Replaces another placeholder
///
@@ -132,36 +124,23 @@ public:
/// only produces another inactive formatter.
///
/// \param arg The argument to place into the placeholder.
- template<class Arg> Formatter arg(const Arg& value) {
- if (active_) {
+ template<class Arg> Formatter& arg(const Arg& value) {
+ if (logger_) {
return (arg(boost::lexical_cast<std::string>(value)));
} else {
- return (Formatter<Logger>());
+ return (*this);
}
}
/// \brief String version of arg.
- Formatter arg(const std::string& arg) {
- if (active_) {
+ Formatter& arg(const std::string& arg) {
+ if (logger_) {
// FIXME: This logic has a problem. If we had a message like
// "%1 %2" and called .arg("%2").arg(42), we would get "42 %2".
// But we consider this to be rare enough not to complicate
// matters.
- active_ = false;
- replacePlaceholder(message_, arg, nextPlaceholder_);
- std::string *message(message_);
- message_ = NULL;
- try {
- return (Formatter<Logger>(prefix_, message,
- nextPlaceholder_ + 1, *logger_));
- }
- // Make sure the memory is not leaked on stuff like bad_alloc
- catch (...) {
- delete message;
- throw;
- }
- } else {
- return (Formatter<Logger>());
+ replacePlaceholder(message_, arg, nextPlaceholder_ ++);
}
+ return (*this);
}
};
diff --git a/src/lib/log/logger.cc b/src/lib/log/logger.cc
index 3131c0b..c340315 100644
--- a/src/lib/log/logger.cc
+++ b/src/lib/log/logger.cc
@@ -119,8 +119,8 @@ Logger::output(const char* sevText, const string& message) {
Logger::Formatter
Logger::debug(int dbglevel, const isc::log::MessageID& ident) {
if (isDebugEnabled(dbglevel)) {
- return (Formatter("DEBUG", getLoggerPtr()->lookupMessage(ident), 1,
- *this));
+ return (Formatter("DEBUG", getLoggerPtr()->lookupMessage(ident),
+ this));
} else {
return (Formatter());
}
@@ -129,8 +129,8 @@ Logger::debug(int dbglevel, const isc::log::MessageID& ident) {
Logger::Formatter
Logger::info(const isc::log::MessageID& ident) {
if (isInfoEnabled()) {
- return (Formatter("INFO ", getLoggerPtr()->lookupMessage(ident), 1,
- *this));
+ return (Formatter("INFO ", getLoggerPtr()->lookupMessage(ident),
+ this));
} else {
return (Formatter());
}
@@ -139,8 +139,8 @@ Logger::info(const isc::log::MessageID& ident) {
Logger::Formatter
Logger::warn(const isc::log::MessageID& ident) {
if (isWarnEnabled()) {
- return (Formatter("WARN ", getLoggerPtr()->lookupMessage(ident), 1,
- *this));
+ return (Formatter("WARN ", getLoggerPtr()->lookupMessage(ident),
+ this));
} else {
return (Formatter());
}
@@ -149,8 +149,8 @@ Logger::warn(const isc::log::MessageID& ident) {
Logger::Formatter
Logger::error(const isc::log::MessageID& ident) {
if (isErrorEnabled()) {
- return (Formatter("ERROR", getLoggerPtr()->lookupMessage(ident), 1,
- *this));
+ return (Formatter("ERROR", getLoggerPtr()->lookupMessage(ident),
+ this));
} else {
return (Formatter());
}
@@ -159,8 +159,8 @@ Logger::error(const isc::log::MessageID& ident) {
Logger::Formatter
Logger::fatal(const isc::log::MessageID& ident) {
if (isFatalEnabled()) {
- return (Formatter("FATAL", getLoggerPtr()->lookupMessage(ident), 1,
- *this));
+ return (Formatter("FATAL", getLoggerPtr()->lookupMessage(ident),
+ this));
} else {
return (Formatter());
}
diff --git a/src/lib/log/logger_impl.cc b/src/lib/log/logger_impl.cc
index a678735..b30f835 100644
--- a/src/lib/log/logger_impl.cc
+++ b/src/lib/log/logger_impl.cc
@@ -13,6 +13,7 @@
// PERFORMANCE OF THIS SOFTWARE
#include <iostream>
+#include <iomanip>
#include <algorithm>
#include <stdarg.h>
diff --git a/src/lib/log/logger_support.cc b/src/lib/log/logger_support.cc
index 83147aa..b744440 100644
--- a/src/lib/log/logger_support.cc
+++ b/src/lib/log/logger_support.cc
@@ -24,6 +24,7 @@
/// 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 <string>
#include <vector>
@@ -85,8 +86,12 @@ readLocalMessageFile(const char* file) {
logger.error(ident).arg(args[0]);
break;
- default: // 2 or more (2 should be the maximum)
+ case 2:
logger.error(ident).arg(args[0]).arg(args[1]);
+ break;
+
+ default: // 3 or more (3 should be the maximum)
+ logger.error(ident).arg(args[0]).arg(args[1]).arg(args[2]);
}
}
}
diff --git a/src/lib/log/macros.h b/src/lib/log/macros.h
index 4a5ac19..3128131 100644
--- a/src/lib/log/macros.h
+++ b/src/lib/log/macros.h
@@ -15,6 +15,8 @@
#ifndef __LOG_MACROS_H
#define __LOG_MACROS_H
+#include <log/logger.h>
+
/// \brief Macro to conveniently test debug output and log it
#define LOG_DEBUG(LOGGER, LEVEL, MESSAGE) \
if (!(LOGGER).isDebugEnabled((LEVEL))) { \
diff --git a/src/lib/log/message_exception.cc b/src/lib/log/message_exception.cc
deleted file mode 100644
index 1a69ca5..0000000
--- a/src/lib/log/message_exception.cc
+++ /dev/null
@@ -1,26 +0,0 @@
-// 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.
-
-/// \brief Body of Virtual Destructor
-
-#include <log/message_exception.h>
-
-namespace isc {
-namespace log {
-
-MessageException::~MessageException() throw() {
-}
-
-} // namespace log
-} // namespace isc
diff --git a/src/lib/log/message_exception.h b/src/lib/log/message_exception.h
index 30c6618..eebee89 100644
--- a/src/lib/log/message_exception.h
+++ b/src/lib/log/message_exception.h
@@ -19,6 +19,7 @@
#include <string>
#include <vector>
+#include <boost/lexical_cast.hpp>
#include <log/message_types.h>
namespace isc {
@@ -35,33 +36,47 @@ public:
/// \brief Constructor
///
- /// \param id Message identification
- MessageException(MessageID id) : id_(id)
- {}
+ /// \param id Message identification.
+ /// \param lineno Line number on which error occurred (if > 0).
+ MessageException(MessageID id, int lineno = 0) : id_(id)
+ {
+ if (lineno > 0) {
+ args_.push_back(boost::lexical_cast<std::string>(lineno));
+ }
+ }
/// \brief Constructor
///
- /// \param id Message identification
- /// \param arg1 First message argument
- MessageException(MessageID id, const std::string& arg1) : id_(id)
+ /// \param id Message identification.
+ /// \param arg1 First message argument.
+ /// \param lineno Line number on which error occurred (if > 0).
+ MessageException(MessageID id, const std::string& arg1, int lineno = 0)
+ : id_(id)
{
+ if (lineno > 0) {
+ args_.push_back(boost::lexical_cast<std::string>(lineno));
+ }
args_.push_back(arg1);
}
/// \brief Constructor
///
- /// \param id Message identification
- /// \param arg1 First message argument
- /// \param arg2 Second message argument
+ /// \param id Message identification.
+ /// \param arg1 First message argument.
+ /// \param arg2 Second message argument.
+ /// \param lineno Line number on which error occurred (if > 0).
MessageException(MessageID id, const std::string& arg1,
- const std::string& arg2) : id_(id)
+ const std::string& arg2, int lineno = 0) : id_(id)
{
+ if (lineno > 0) {
+ args_.push_back(boost::lexical_cast<std::string>(lineno));
+ }
args_.push_back(arg1);
args_.push_back(arg2);
}
/// \brief Destructor
- virtual ~MessageException() throw();
+ ~MessageException() throw() {}
/// \brief Return Message ID
///
diff --git a/src/lib/log/message_reader.cc b/src/lib/log/message_reader.cc
index 7281346..1a0b242 100644
--- a/src/lib/log/message_reader.cc
+++ b/src/lib/log/message_reader.cc
@@ -12,8 +12,10 @@
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
+#include <cassert>
#include <errno.h>
#include <string.h>
+#include <iostream>
#include <iostream>
#include <fstream>
@@ -25,45 +27,50 @@
using namespace std;
-namespace isc {
-namespace log {
-
-// Virtual destructor.
-MessageReader::~MessageReader() {
+namespace {
+const char DIRECTIVE_FLAG = '$'; // Starts each directive
+const char MESSAGE_FLAG = '%'; // Starts each message
}
+namespace isc {
+namespace log {
+
// Read the file.
void
MessageReader::readFile(const string& file, MessageReader::Mode mode) {
- // Ensure the non-added collection is empty: this object might be
- // being reused.
+ // Ensure the non-added collection is empty: we could be re-using this
+ // object.
not_added_.clear();
- // Open the file
+ // Open the file.
ifstream infile(file.c_str());
if (infile.fail()) {
- throw MessageException(MSG_OPNMSGIN, file, strerror(errno));
+ throw MessageException(MSG_OPENIN, file, strerror(errno));
}
- // Loop round reading it.
+ // Loop round reading it. As we process the file one line at a time,
+ // keep a track of line number of aid diagnosis of problems.
string line;
getline(infile, line);
+ lineno_ = 0;
+
while (infile.good()) {
+ ++lineno_;
processLine(line, mode);
getline(infile, line);
}
// Why did the loop terminate?
if (!infile.eof()) {
- throw MessageException(MSG_MSGRDERR, file, strerror(errno));
+ throw MessageException(MSG_READERR, file, strerror(errno));
}
infile.close();
}
-// Parse a line of the file
+// Parse a line of the file.
void
MessageReader::processLine(const string& line, MessageReader::Mode mode) {
@@ -74,15 +81,16 @@ MessageReader::processLine(const string& line, MessageReader::Mode mode) {
if (text.empty()) {
; // Ignore blank lines
- } else if ((text[0] == '#') || (text[0] == '+')) {
- ; // Ignore comments or descriptions
-
- } else if (text[0] == '$') {
+ } else if (text[0] == DIRECTIVE_FLAG) {
parseDirective(text); // Process directives
- } else {
- parseMessage(text, mode); // Process other lines
+ } else if (text[0] == MESSAGE_FLAG) {
+ parseMessage(text, mode); // Process message definition line
+
+ } else {
+ ; // Other lines are extended message
+ // description so are ignored
}
}
@@ -99,130 +107,162 @@ MessageReader::parseDirective(const std::string& text) {
isc::util::str::uppercase(tokens[0]);
if (tokens[0] == string("$PREFIX")) {
parsePrefix(tokens);
+
} else if (tokens[0] == string("$NAMESPACE")) {
parseNamespace(tokens);
+
} else {
- throw MessageException(MSG_UNRECDIR, tokens[0]);
+
+ // Unrecognised directive
+ throw MessageException(MSG_UNRECDIR, tokens[0], lineno_);
}
}
// Process $PREFIX
-
void
MessageReader::parsePrefix(const vector<string>& tokens) {
- // Check argument count
-
- static string valid = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_";
- if (tokens.size() < 2) {
- throw MessageException(MSG_PRFNOARG);
- } else if (tokens.size() > 2) {
- throw MessageException(MSG_PRFEXTRARG);
-
- }
+ // Should not get here unless there is something in the tokens array.
+ assert(tokens.size() > 0);
- // As a style, we are going to have the symbols in uppercase
- string prefix = tokens[1];
- isc::util::str::uppercase(prefix);
+ // Process $PREFIX. With no arguments, the prefix is set to the empty
+ // string. One argument sets the prefix to the to its value and more than
+ // one argument is invalid.
+ if (tokens.size() == 1) {
+ prefix_ = "";
- // Token is potentially valid providing it only contains alphabetic
- // and numeric characters (and underscores) and does not start with a
- // digit.
- if ((prefix.find_first_not_of(valid) != string::npos) ||
- (std::isdigit(prefix[0]))) {
+ } else if (tokens.size() == 2) {
+ prefix_ = tokens[1];
- // Invalid character in string or it starts with a digit.
- throw MessageException(MSG_PRFINVARG, tokens[1]);
- }
+ // Token is potentially valid providing it only contains alphabetic
+ // and numeric characters (and underscores) and does not start with a
+ // digit.
+ if (invalidSymbol(prefix_)) {
+ throw MessageException(MSG_PRFINVARG, prefix_, lineno_);
+ }
- // All OK - unless the prefix has already been set.
+ } else {
- if (prefix_.size() != 0) {
- throw MessageException(MSG_DUPLPRFX);
+ // Too many arguments
+ throw MessageException(MSG_PRFEXTRARG, lineno_);
}
+}
- // Prefix has not been set, so set it and return success.
-
- prefix_ = prefix;
+// Check if string is an invalid C++ symbol. It is valid if comprises only
+// alphanumeric characters and underscores, and does not start with a digit.
+// (Owing to the logic of the rest of the code, we check for its invalidity,
+// not its validity.)
+bool
+MessageReader::invalidSymbol(const string& symbol) {
+ static const string valid_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ "abcdefghijklmnopqrstuvwxyz"
+ "0123456789_";
+ return ( symbol.empty() ||
+ (symbol.find_first_not_of(valid_chars) != string::npos) ||
+ (std::isdigit(symbol[0])));
}
// Process $NAMESPACE. A lot of the processing is similar to that of $PREFIX,
// except that only limited checks will be done on the namespace (to avoid a
-// lot of parsing and separating out of the namespace components.)
+// lot of parsing and separating out of the namespace components.) Also, unlike
+// $PREFIX, there can only be one $NAMESPACE in a file.
void
MessageReader::parseNamespace(const vector<string>& tokens) {
// Check argument count
-
- static string valid = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_:"
- "abcdefghijklmnopqrstuvwxyz";
-
if (tokens.size() < 2) {
- throw MessageException(MSG_NSNOARG);
+ throw MessageException(MSG_NSNOARG, lineno_);
} else if (tokens.size() > 2) {
- throw MessageException(MSG_NSEXTRARG);
+ throw MessageException(MSG_NSEXTRARG, lineno_);
}
// Token is potentially valid providing it only contains alphabetic
- // and numeric characters (and underscores and colons).
- if (tokens[1].find_first_not_of(valid) != string::npos) {
-
- // Invalid character in string or it starts with a digit.
- throw MessageException(MSG_NSINVARG, tokens[1]);
+ // and numeric characters (and underscores and colons). As noted above,
+ // we won't be exhaustive - after all, and code containing the resultant
+ // namespace will have to be compiled, and the compiler will catch errors.
+ static const string valid_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ "abcdefghijklmnopqrstuvwxyz"
+ "0123456789_:";
+ if (tokens[1].find_first_not_of(valid_chars) != string::npos) {
+ throw MessageException(MSG_NSINVARG, tokens[1], lineno_);
}
// All OK - unless the namespace has already been set.
if (ns_.size() != 0) {
- throw MessageException(MSG_DUPLNS);
+ throw MessageException(MSG_DUPLNS, lineno_);
}
// Prefix has not been set, so set it and return success.
-
ns_ = tokens[1];
}
// Process message. By the time this method is called, the line has been
-// stripped of leading and trailing spaces, and we believe that it is a line
-// defining a message. The first token on the line is converted to uppercase
-// and becomes the message ID; the rest of the line is the message text.
+// stripped of leading and trailing spaces. The first character of the string
+// is the message introducer, so we can get rid of that. The remainder is
+// a line defining a message.
+//
+// The first token on the line, when concatenated to the prefix and converted to
+// upper-case, is the message ID. The first of the line from the next token
+// on is the message text.
void
MessageReader::parseMessage(const std::string& text, MessageReader::Mode mode) {
static string delimiters("\t\n "); // Delimiters
+ // The line passed should be at least one character long and start with the
+ // message introducer (else we should not have got here).
+ assert((text.size() >= 1) && (text[0] == MESSAGE_FLAG));
+
+ // A line comprising just the message introducer is not valid.
+ if (text.size() == 1) {
+ throw MessageException(MSG_NOMSGID, text, lineno_);
+ }
+
+ // Strip off the introducer and any leading space after that.
+ string message_line = isc::util::str::trim(text.substr(1));
+
// Look for the first delimiter.
- size_t first_delim = text.find_first_of(delimiters);
+ size_t first_delim = message_line.find_first_of(delimiters);
if (first_delim == string::npos) {
// Just a single token in the line - this is not valid
- throw MessageException(MSG_NOMSGTXT, text);
+ throw MessageException(MSG_NOMSGTXT, message_line, lineno_);
}
- // Extract the first token into the message ID
- string ident = text.substr(0, first_delim);
+ // Extract the first token into the message ID, preceding it with the
+ // current prefix, then convert to upper-case. If the prefix is not set,
+ // perform the valid character check now - the string will become a C++
+ // symbol so we may as well identify problems early.
+ string ident = prefix_ + message_line.substr(0, first_delim);
+ if (prefix_.empty()) {
+ if (invalidSymbol(ident)) {
+ throw MessageException(MSG_INVMSGID, ident, lineno_);
+ }
+ }
+ isc::util::str::uppercase(ident);
// Locate the start of the message text
- size_t first_text = text.find_first_not_of(delimiters, first_delim);
+ size_t first_text = message_line.find_first_not_of(delimiters, first_delim);
if (first_text == string::npos) {
// ?? This happens if there are trailing delimiters, which should not
// occur as we have stripped trailing spaces off the line. Just treat
// this as a single-token error for simplicity's sake.
- throw MessageException(MSG_NOMSGTXT, text);
+ throw MessageException(MSG_NOMSGTXT, message_line, lineno_);
}
// Add the result to the dictionary and to the non-added list if the add to
// the dictionary fails.
bool added;
if (mode == ADD) {
- added = dictionary_->add(ident, text.substr(first_text));
+ added = dictionary_->add(ident, message_line.substr(first_text));
}
else {
- added = dictionary_->replace(ident, text.substr(first_text));
+ added = dictionary_->replace(ident, message_line.substr(first_text));
}
if (!added) {
not_added_.push_back(ident);
diff --git a/src/lib/log/message_reader.h b/src/lib/log/message_reader.h
index d07c7f2..82a1b3a 100644
--- a/src/lib/log/message_reader.h
+++ b/src/lib/log/message_reader.h
@@ -65,10 +65,6 @@ public:
{}
- /// \brief Virtual Destructor
- virtual ~MessageReader();
-
-
/// \brief Get Dictionary
///
/// Returns the pointer to the dictionary object. Note that ownership is
@@ -188,10 +184,24 @@ private:
/// \param tokens $NAMESPACE line split into tokens
void parseNamespace(const std::vector<std::string>& tokens);
+ /// \brief Check for invalid C++ symbol name
+ ///
+ /// The message ID (or concatenation of prefix and message ID) will be used
+ /// as the name of a symbol in C++ code. This function checks if the name
+ /// is invalid (contains anything other than alphanumeric characters or
+ /// underscores, or starts with a digit).
+ ///
+ /// \param symbol name to check to see if it is an invalid C++ symbol.
+ ///
+ /// \return true if the name is invalid, false if it is valid.
+ bool invalidSymbol(const std::string& symbol);
+
+
/// Attributes
MessageDictionary* dictionary_; ///< Dictionary to add messages to
MessageIDCollection not_added_; ///< List of IDs not added
+ int lineno_; ///< Number of last line read
std::string prefix_; ///< Argument of $PREFIX statement
std::string ns_; ///< Argument of $NAMESPACE statement
};
diff --git a/src/lib/log/messagedef.cc b/src/lib/log/messagedef.cc
index 46086d0..5cc89b3 100644
--- a/src/lib/log/messagedef.cc
+++ b/src/lib/log/messagedef.cc
@@ -1,4 +1,4 @@
-// File created from messagedef.mes on Thu May 5 16:57:11 2011
+// File created from messagedef.mes on Mon May 9 13:52:54 2011
#include <cstddef>
#include <log/message_types.h>
@@ -7,23 +7,23 @@
namespace isc {
namespace log {
-extern const isc::log::MessageID MSG_DUPLNS = "DUPLNS";
-extern const isc::log::MessageID MSG_DUPLPRFX = "DUPLPRFX";
-extern const isc::log::MessageID MSG_DUPMSGID = "DUPMSGID";
-extern const isc::log::MessageID MSG_IDNOTFND = "IDNOTFND";
-extern const isc::log::MessageID MSG_MSGRDERR = "MSGRDERR";
-extern const isc::log::MessageID MSG_MSGWRTERR = "MSGWRTERR";
-extern const isc::log::MessageID MSG_NOMSGTXT = "NOMSGTXT";
-extern const isc::log::MessageID MSG_NSEXTRARG = "NSEXTRARG";
-extern const isc::log::MessageID MSG_NSINVARG = "NSINVARG";
-extern const isc::log::MessageID MSG_NSNOARG = "NSNOARG";
-extern const isc::log::MessageID MSG_OPNMSGIN = "OPNMSGIN";
-extern const isc::log::MessageID MSG_OPNMSGOUT = "OPNMSGOUT";
-extern const isc::log::MessageID MSG_PRFEXTRARG = "PRFEXTRARG";
-extern const isc::log::MessageID MSG_PRFINVARG = "PRFINVARG";
-extern const isc::log::MessageID MSG_PRFNOARG = "PRFNOARG";
-extern const isc::log::MessageID MSG_RDLOCMES = "RDLOCMES";
-extern const isc::log::MessageID MSG_UNRECDIR = "UNRECDIR";
+extern const isc::log::MessageID MSG_DUPLNS = "MSG_DUPLNS";
+extern const isc::log::MessageID MSG_DUPMSGID = "MSG_DUPMSGID";
+extern const isc::log::MessageID MSG_IDNOTFND = "MSG_IDNOTFND";
+extern const isc::log::MessageID MSG_INVMSGID = "MSG_INVMSGID";
+extern const isc::log::MessageID MSG_NOMSGID = "MSG_NOMSGID";
+extern const isc::log::MessageID MSG_NOMSGTXT = "MSG_NOMSGTXT";
+extern const isc::log::MessageID MSG_NSEXTRARG = "MSG_NSEXTRARG";
+extern const isc::log::MessageID MSG_NSINVARG = "MSG_NSINVARG";
+extern const isc::log::MessageID MSG_NSNOARG = "MSG_NSNOARG";
+extern const isc::log::MessageID MSG_OPENIN = "MSG_OPENIN";
+extern const isc::log::MessageID MSG_OPENOUT = "MSG_OPENOUT";
+extern const isc::log::MessageID MSG_PRFEXTRARG = "MSG_PRFEXTRARG";
+extern const isc::log::MessageID MSG_PRFINVARG = "MSG_PRFINVARG";
+extern const isc::log::MessageID MSG_RDLOCMES = "MSG_RDLOCMES";
+extern const isc::log::MessageID MSG_READERR = "MSG_READERR";
+extern const isc::log::MessageID MSG_UNRECDIR = "MSG_UNRECDIR";
+extern const isc::log::MessageID MSG_WRITERR = "MSG_WRITERR";
} // namespace log
} // namespace isc
@@ -31,23 +31,23 @@ extern const isc::log::MessageID MSG_UNRECDIR = "UNRECDIR";
namespace {
const char* values[] = {
- "DUPLNS", "duplicate $NAMESPACE directive found",
- "DUPLPRFX", "duplicate $PREFIX directive found",
- "DUPMSGID", "duplicate message ID (%1) in compiled code",
- "IDNOTFND", "could not replace message for '%1': no such message identification",
- "MSGRDERR", "error reading from message file %1: %2",
- "MSGWRTERR", "error writing to %1: %2",
- "NOMSGTXT", "a line containing a message ID ('%1') and nothing else was found",
- "NSEXTRARG", "$NAMESPACE directive has too many arguments",
- "NSINVARG", "$NAMESPACE directive has an invalid argument ('%1')",
- "NSNOARG", "no arguments were given to the $NAMESPACE directive",
- "OPNMSGIN", "unable to open message file %1 for input: %2",
- "OPNMSGOUT", "unable to open %1 for output: %2",
- "PRFEXTRARG", "$PREFIX directive has too many arguments",
- "PRFINVARG", "$PREFIX directive has an invalid argument ('%1')",
- "PRFNOARG", "no arguments were given to the $PREFIX directive",
- "RDLOCMES", "reading local message file %1",
- "UNRECDIR", "unrecognised directive '%1'",
+ "MSG_DUPLNS", "line %1: duplicate $NAMESPACE directive found",
+ "MSG_DUPMSGID", "duplicate message ID (%1) in compiled code",
+ "MSG_IDNOTFND", "could not replace message text for '%1': no such message",
+ "MSG_INVMSGID", "line %1: invalid message identification '%2'",
+ "MSG_NOMSGID", "line %1: message definition line found without a message ID",
+ "MSG_NOMSGTXT", "line %1: line found containing a message ID ('%2') and no text",
+ "MSG_NSEXTRARG", "line %1: $NAMESPACE directive has too many arguments",
+ "MSG_NSINVARG", "line %1: $NAMESPACE directive has an invalid argument ('%2')",
+ "MSG_NSNOARG", "line %1: no arguments were given to the $NAMESPACE directive",
+ "MSG_OPENIN", "unable to open message file %1 for input: %2",
+ "MSG_OPENOUT", "unable to open %1 for output: %2",
+ "MSG_PRFEXTRARG", "line %1: $PREFIX directive has too many arguments",
+ "MSG_PRFINVARG", "line %1: $PREFIX directive has an invalid argument ('%2')",
+ "MSG_RDLOCMES", "reading local message file %1",
+ "MSG_READERR", "error reading from message file %1: %2",
+ "MSG_UNRECDIR", "line %1: unrecognised directive '%2'",
+ "MSG_WRITERR", "error writing to %1: %2",
NULL
};
diff --git a/src/lib/log/messagedef.h b/src/lib/log/messagedef.h
index 47867e6..79c8bab 100644
--- a/src/lib/log/messagedef.h
+++ b/src/lib/log/messagedef.h
@@ -1,4 +1,4 @@
-// File created from messagedef.mes on Thu May 5 16:57:11 2011
+// File created from messagedef.mes on Mon May 9 13:52:54 2011
#ifndef __MESSAGEDEF_H
#define __MESSAGEDEF_H
@@ -9,22 +9,22 @@ namespace isc {
namespace log {
extern const isc::log::MessageID MSG_DUPLNS;
-extern const isc::log::MessageID MSG_DUPLPRFX;
extern const isc::log::MessageID MSG_DUPMSGID;
extern const isc::log::MessageID MSG_IDNOTFND;
-extern const isc::log::MessageID MSG_MSGRDERR;
-extern const isc::log::MessageID MSG_MSGWRTERR;
+extern const isc::log::MessageID MSG_INVMSGID;
+extern const isc::log::MessageID MSG_NOMSGID;
extern const isc::log::MessageID MSG_NOMSGTXT;
extern const isc::log::MessageID MSG_NSEXTRARG;
extern const isc::log::MessageID MSG_NSINVARG;
extern const isc::log::MessageID MSG_NSNOARG;
-extern const isc::log::MessageID MSG_OPNMSGIN;
-extern const isc::log::MessageID MSG_OPNMSGOUT;
+extern const isc::log::MessageID MSG_OPENIN;
+extern const isc::log::MessageID MSG_OPENOUT;
extern const isc::log::MessageID MSG_PRFEXTRARG;
extern const isc::log::MessageID MSG_PRFINVARG;
-extern const isc::log::MessageID MSG_PRFNOARG;
extern const isc::log::MessageID MSG_RDLOCMES;
+extern const isc::log::MessageID MSG_READERR;
extern const isc::log::MessageID MSG_UNRECDIR;
+extern const isc::log::MessageID MSG_WRITERR;
} // namespace log
} // namespace isc
diff --git a/src/lib/log/messagedef.mes b/src/lib/log/messagedef.mes
index 077830c..51c04fa 100644
--- a/src/lib/log/messagedef.mes
+++ b/src/lib/log/messagedef.mes
@@ -12,108 +12,108 @@
# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
# PERFORMANCE OF THIS SOFTWARE.
-$PREFIX MSG_
-$NAMESPACE isc::log
-
# \brief Message Utility Message File
#
-# This is the source of the set of messages generated by the message and logging
-# components. The associated .h and .cc files are created by hand from this
-# file though and are not built during the build process; this is to avoid the
-# chicken-and-egg situation where we need the files to build the message
+# This is the source of the set of messages generated by the message and
+# logging components. The associated .h and .cc files are created by hand from
+# this file though and are not built during the build process; this is to avoid
+# the chicken-and-egg situation where we need the files to build the message
# compiler, yet we need the compiler to build the files.
-DUPMSGID duplicate message ID (%1) in compiled code
-+ Indicative of a programming error, when it started up, BIND10 detected that
-+ the given message ID had been registered by one or more modules. (All message
-+ IDs should be unique throughout BIND10.) This has no impact on the operation
-+ of the server other that erroneous messages may be logged. (When BIND10 loads
-+ the message IDs (and their associated text), if a duplicate ID is found it is
-+ discarded. However, when the module that supplied the duplicate ID logs that
-+ particular message, the text supplied by the module that added the original
-+ ID will be output - something that may bear no relation to the condition being
-+ logged.
-
-DUPLNS duplicate $NAMESPACE directive found
-+ When reading a message file, more than one $NAMESPACE directive was found. In
-+ this version of the code, such a condition is regarded as an error and the
-+ read will be abandoned.
-
-DUPLPRFX duplicate $PREFIX directive found
-+ When reading a message file, more than one $PREFIX directive was found. In
-+ this version of the code, such a condition is regarded as an error and the
-+ read will be abandoned.
-
-IDNOTFND could not replace message for '%1': no such message identification
-+ During start-up a local message file was read. A line with the listed
-+ message identification was found in the file, but the identification is not
-+ one contained in the compiled-in message dictionary. Either the message
-+ identification has been mis-spelled in the file, or the local file was used
-+ for an earlier version of the software and the message with that
-+ identification has been removed.
-+
-+ This message may appear a number of times in the file, once for every such
-+ unknown message identification.
-
-MSGRDERR error reading from message file %1: %2
-+ The specified error was encountered reading from the named message file.
-
-MSGWRTERR error writing to %1: %2
-+ The specified error was encountered by the message compiler when writing to
-+ the named output file.
-
-NSEXTRARG $NAMESPACE directive has too many arguments
-+ The $NAMESPACE directive takes a single argument, a namespace in which all the
-+ generated symbol names are placed. This error is generated when the
-+ compiler finds a $NAMESPACE directive with more than one argument.
-
-NSINVARG $NAMESPACE directive has an invalid argument ('%1')
-+ The $NAMESPACE argument should be a valid C++ namespace. The reader does a
-+ cursory check on its validity, checking that the characters in the namespace
-+ are correct. The error is generated when the reader finds an invalid
-+ character. (Valid are alphanumeric characters, underscores and colons.)
-
-NOMSGTXT a line containing a message ID ('%1') and nothing else was found
-+ Message definitions comprise lines starting with a message identification (a
-+ symbolic name for the message) and followed by the text of the message. This
-+ error is generated when a line is found in the message file that contains just
-+ the message identification and no text.
-
-NSNOARG no arguments were given to the $NAMESPACE directive
-+ The $NAMESPACE directive takes a single argument, a namespace in which all the
-+ generated symbol names are placed. This error is generated when the
-+ compiler finds a $NAMESPACE directive with no arguments.
-
-OPNMSGIN unable to open message file %1 for input: %2
-+ The program was not able to open the specified input message file for the
-+ reason given.
-
-OPNMSGOUT unable to open %1 for output: %2
-+ The program was not able to open the specified output file for the reason
-+ given.
-
-PRFEXTRARG $PREFIX directive has too many arguments
-+ The $PREFIX directive takes a single argument, a prefix to be added to the
-+ symbol names when a C++ .h file is created. This error is generated when the
-+ compiler finds a $PREFIX directive with more than one argument.
-
-PRFINVARG $PREFIX directive has an invalid argument ('%1')
-+ The $PREFIX argument is used in a symbol name in a C++ header file. As such,
-+ it must adhere to restrictions on C++ symbol names (e.g. may only contain
-+ alphanumeric characters or underscores, and may nor start with a digit). A
-+ $PREFIX directive was found with an argument (given in the message) that
-+ violates those restictions.
-
-PRFNOARG no arguments were given to the $PREFIX directive
-+ The $PREFIX directive takes a single argument, a prefix to be added to the
-+ symbol names when a C++ .h file is created. This error is generated when the
-+ compiler finds a $PREFIX directive with no arguments.
-
-RDLOCMES reading local message file %1
-+ This is an informational message output by BIND10 when it starts to read a
-+ local message file. (A local message file may replace the text of one of more
-+ messages; the ID of the message will not be changed though.)
-
-UNRECDIR unrecognised directive '%1'
-+ A line starting with a dollar symbol was found, but the first word on the line
-+ (shown in the message) was not a recognised message compiler directive.
+$PREFIX MSG_
+$NAMESPACE isc::log
+
+% DUPMSGID duplicate message ID (%1) in compiled code
+Indicative of a programming error, when it started up, BIND10 detected that
+the given message ID had been registered by one or more modules. (All message
+IDs should be unique throughout BIND10.) This has no impact on the operation
+of the server other that erroneous messages may be logged. (When BIND10 loads
+the message IDs (and their associated text), if a duplicate ID is found it is
+discarded. However, when the module that supplied the duplicate ID logs that
+particular message, the text supplied by the module that added the original
+ID will be output - something that may bear no relation to the condition being
+logged.
+
+% DUPLNS line %1: duplicate $NAMESPACE directive found
+When reading a message file, more than one $NAMESPACE directive was found. In
+this version of the code, such a condition is regarded as an error and the
+read will be abandoned.
+
+% IDNOTFND could not replace message text for '%1': no such message
+During start-up a local message file was read. A line with the listed
+message identification was found in the file, but the identification is not
+one contained in the compiled-in message dictionary. Either the message
+identification has been mis-spelled in the file, or the local file was used
+for an earlier version of the software and the message with that
+identification has been removed.
+
+This message may appear a number of times in the file, once for every such
+unknown message identification.
+
+% INVMSGID line %1: invalid message identification '%2'
+The concatenation of the prefix and the message identification is used as
+a symbol in the C++ module; as such it may only contain
+
+% NOMSGID line %1: message definition line found without a message ID
+Message definition lines are lines starting with a "%". The rest of the line
+should comprise the message ID and text describing the message. This error
+indicates the message compiler found a line in the message file comprising
+just the "%" and nothing else.
+
+% NOMSGTXT line %1: line found containing a message ID ('%2') and no text
+Message definition lines are lines starting with a "%". The rest of the line
+should comprise the message ID and text describing the message. This error
+is generated when a line is found in the message file that contains the
+leading "%" and the message identification but no text.
+
+% NSEXTRARG line %1: $NAMESPACE directive has too many arguments
+The $NAMESPACE directive takes a single argument, a namespace in which all the
+generated symbol names are placed. This error is generated when the
+compiler finds a $NAMESPACE directive with more than one argument.
+
+% NSINVARG line %1: $NAMESPACE directive has an invalid argument ('%2')
+The $NAMESPACE argument should be a valid C++ namespace. The reader does a
+cursory check on its validity, checking that the characters in the namespace
+are correct. The error is generated when the reader finds an invalid
+character. (Valid are alphanumeric characters, underscores and colons.)
+
+% NSNOARG line %1: no arguments were given to the $NAMESPACE directive
+The $NAMESPACE directive takes a single argument, a namespace in which all the
+generated symbol names are placed. This error is generated when the
+compiler finds a $NAMESPACE directive with no arguments.
+
+% OPENIN unable to open message file %1 for input: %2
+The program was not able to open the specified input message file for the
+reason given.
+
+% OPENOUT unable to open %1 for output: %2
+The program was not able to open the specified output file for the reason
+given.
+
+% PRFEXTRARG line %1: $PREFIX directive has too many arguments
+The $PREFIX directive takes a single argument, a prefix to be added to the
+symbol names when a C++ .h file is created. This error is generated when the
+compiler finds a $PREFIX directive with more than one argument.
+
+% PRFINVARG line %1: $PREFIX directive has an invalid argument ('%2')
+The $PREFIX argument is used in a symbol name in a C++ header file. As such,
+it must adhere to restrictions on C++ symbol names (e.g. may only contain
+alphanumeric characters or underscores, and may nor start with a digit).
+A $PREFIX directive was found with an argument (given in the message) that
+violates those restictions.
+
+% RDLOCMES reading local message file %1
+This is an informational message output by BIND10 when it starts to read a
+local message file. (A local message file may replace the text of one of more
+messages; the ID of the message will not be changed though.)
+
+% READERR error reading from message file %1: %2
+The specified error was encountered reading from the named message file.
+
+% WRITERR error writing to %1: %2
+The specified error was encountered by the message compiler when writing to
+the named output file.
+
+% UNRECDIR line %1: unrecognised directive '%2'
+A line starting with a dollar symbol was found, but the first word on the line
+(shown in the message) was not a recognised message compiler directive.
diff --git a/src/lib/log/tests/log_formatter_unittest.cc b/src/lib/log/tests/log_formatter_unittest.cc
index 59157db..b67831a 100644
--- a/src/lib/log/tests/log_formatter_unittest.cc
+++ b/src/lib/log/tests/log_formatter_unittest.cc
@@ -46,9 +46,8 @@ TEST_F(FormatterTest, inactive) {
// Create an active formatter and check it produces output. Does no arg
// substitution yet
TEST_F(FormatterTest, active) {
- Formatter("TEST", s("Text of message"), 1, *this);
- ASSERT_LE(1, outputs.size());
- EXPECT_EQ(1, outputs.size());
+ Formatter("TEST", s("Text of message"), this);
+ ASSERT_EQ(1, outputs.size());
EXPECT_STREQ("TEST", outputs[0].first);
EXPECT_EQ("Text of message", outputs[0].second);
}
@@ -63,17 +62,15 @@ TEST_F(FormatterTest, inactiveArg) {
TEST_F(FormatterTest, stringArg) {
{
SCOPED_TRACE("C++ string");
- Formatter("TEST", s("Hello %1"), 1, *this).arg(string("World"));
- ASSERT_LE(1, outputs.size());
- EXPECT_EQ(1, outputs.size());
+ Formatter("TEST", s("Hello %1"), this).arg(string("World"));
+ ASSERT_EQ(1, outputs.size());
EXPECT_STREQ("TEST", outputs[0].first);
EXPECT_EQ("Hello World", outputs[0].second);
}
{
SCOPED_TRACE("C++ string");
- Formatter("TEST", s("Hello %1"), 1, *this).arg(string("Internet"));
- ASSERT_LE(2, outputs.size());
- EXPECT_EQ(2, outputs.size());
+ Formatter("TEST", s("Hello %1"), this).arg(string("Internet"));
+ ASSERT_EQ(2, outputs.size());
EXPECT_STREQ("TEST", outputs[1].first);
EXPECT_EQ("Hello Internet", outputs[1].second);
}
@@ -81,32 +78,48 @@ TEST_F(FormatterTest, stringArg) {
// Can convert to string
TEST_F(FormatterTest, intArg) {
- Formatter("TEST", s("The answer is %1"), 1, *this).arg(42);
- ASSERT_LE(1, outputs.size());
- EXPECT_EQ(1, outputs.size());
+ Formatter("TEST", s("The answer is %1"), this).arg(42);
+ ASSERT_EQ(1, outputs.size());
EXPECT_STREQ("TEST", outputs[0].first);
EXPECT_EQ("The answer is 42", outputs[0].second);
}
// Can use multiple arguments at different places
TEST_F(FormatterTest, multiArg) {
- Formatter("TEST", s("The %2 are %1"), 1, *this).arg("switched").
+ Formatter("TEST", s("The %2 are %1"), this).arg("switched").
arg("arguments");
- ASSERT_LE(1, outputs.size());
- EXPECT_EQ(1, outputs.size());
+ ASSERT_EQ(1, outputs.size());
EXPECT_STREQ("TEST", outputs[0].first);
EXPECT_EQ("The arguments are switched", outputs[0].second);
}
// Can survive and complains if placeholder is missing
TEST_F(FormatterTest, missingPlace) {
- EXPECT_NO_THROW(Formatter("TEST", s("Missing the first %2"), 1, *this).
+ EXPECT_NO_THROW(Formatter("TEST", s("Missing the first %2"), this).
arg("missing").arg("argument"));
- ASSERT_LE(1, outputs.size());
- EXPECT_EQ(1, outputs.size());
+ ASSERT_EQ(1, outputs.size());
EXPECT_STREQ("TEST", outputs[0].first);
EXPECT_EQ("Missing the first argument "
"@@Missing placeholder %1 for 'missing'@@", outputs[0].second);
}
+// Can replace multiple placeholders
+TEST_F(FormatterTest, multiPlaceholder) {
+ Formatter("TEST", s("The %1 is the %1"), this).
+ arg("first rule of tautology club");
+ ASSERT_EQ(1, outputs.size());
+ EXPECT_STREQ("TEST", outputs[0].first);
+ EXPECT_EQ("The first rule of tautology club is "
+ "the first rule of tautology club", outputs[0].second);
+}
+
+// Test we can cope with replacement containing the placeholder
+TEST_F(FormatterTest, noRecurse) {
+ // If we recurse, this will probably eat all the memory and crash
+ Formatter("TEST", s("%1"), this).arg("%1 %1");
+ ASSERT_EQ(1, outputs.size());
+ EXPECT_STREQ("TEST", outputs[0].first);
+ EXPECT_EQ("%1 %1", outputs[0].second);
+}
+
}
diff --git a/src/lib/log/tests/logger_support_test.cc b/src/lib/log/tests/logger_support_test.cc
index d0d5c53..0a2338b 100644
--- a/src/lib/log/tests/logger_support_test.cc
+++ b/src/lib/log/tests/logger_support_test.cc
@@ -93,13 +93,14 @@ int main(int argc, char** argv) {
initLogger("alpha", severity, dbglevel, localfile);
// Log a few messages
- LOG_FATAL(logger_ex, MSG_MSGWRTERR).arg("test1").arg("42");
- LOG_ERROR(logger_ex, MSG_UNRECDIR).arg("false");
- LOG_WARN(logger_dlm, MSG_MSGRDERR).arg("a.txt").arg("dummy test");
- LOG_INFO(logger_dlm, MSG_OPNMSGIN).arg("example.msg").arg("dummy test");
- LOG_DEBUG(logger_ex, 0, MSG_UNRECDIR).arg("[abc]");
- LOG_DEBUG(logger_ex, 24, MSG_UNRECDIR).arg("[24]");
- LOG_DEBUG(logger_ex, 25, MSG_UNRECDIR).arg("[25]");
- LOG_DEBUG(logger_ex, 26, MSG_UNRECDIR).arg("[26]");
+ LOG_FATAL(logger_ex, MSG_WRITERR).arg("test1").arg("42");
+ LOG_ERROR(logger_ex, MSG_RDLOCMES).arg("dummy/file");
+ LOG_WARN(logger_dlm, MSG_READERR).arg("a.txt").arg("dummy reason");
+ LOG_INFO(logger_dlm, MSG_OPENIN).arg("example.msg").arg("dummy reason");
+ LOG_DEBUG(logger_ex, 0, MSG_RDLOCMES).arg("dummy/0");
+ LOG_DEBUG(logger_ex, 24, MSG_RDLOCMES).arg("dummy/24");
+ LOG_DEBUG(logger_ex, 25, MSG_RDLOCMES).arg("dummy/25");
+ LOG_DEBUG(logger_ex, 26, MSG_RDLOCMES).arg("dummy/26");
+
return (0);
}
diff --git a/src/lib/log/tests/message_dictionary_unittest.cc b/src/lib/log/tests/message_dictionary_unittest.cc
index a92585c..ba33820 100644
--- a/src/lib/log/tests/message_dictionary_unittest.cc
+++ b/src/lib/log/tests/message_dictionary_unittest.cc
@@ -29,7 +29,7 @@ using namespace std;
// and the latter should be present.
static const char* values[] = {
- "DUPLNS", "duplicate $NAMESPACE directive found",
+ "MSG_DUPLNS", "duplicate $NAMESPACE directive found",
"NEWSYM", "new symbol added",
NULL
};
@@ -190,7 +190,7 @@ TEST_F(MessageDictionaryTest, GlobalTest) {
TEST_F(MessageDictionaryTest, GlobalLoadTest) {
vector<string>& duplicates = MessageInitializer::getDuplicates();
ASSERT_EQ(1, duplicates.size());
- EXPECT_EQ(string("DUPLNS"), duplicates[0]);
+ EXPECT_EQ(string("MSG_DUPLNS"), duplicates[0]);
string text = MessageDictionary::globalDictionary().getText("NEWSYM");
EXPECT_EQ(string("new symbol added"), text);
diff --git a/src/lib/log/tests/message_reader_unittest.cc b/src/lib/log/tests/message_reader_unittest.cc
index 36288f2..7b3ba5f 100644
--- a/src/lib/log/tests/message_reader_unittest.cc
+++ b/src/lib/log/tests/message_reader_unittest.cc
@@ -68,8 +68,8 @@ TEST_F(MessageReaderTest, BlanksAndComments) {
EXPECT_NO_THROW(reader_.processLine(" \n "));
EXPECT_NO_THROW(reader_.processLine("# This is a comment"));
EXPECT_NO_THROW(reader_.processLine("\t\t # Another comment"));
- EXPECT_NO_THROW(reader_.processLine(" + A description line"));
- EXPECT_NO_THROW(reader_.processLine("#+ A comment"));
+ EXPECT_NO_THROW(reader_.processLine(" A description line"));
+ EXPECT_NO_THROW(reader_.processLine("# A comment"));
EXPECT_NO_THROW(reader_.processLine(" +# A description line"));
// ... and (b) nothing gets added to either the map or the not-added section.
@@ -97,6 +97,15 @@ processLineException(MessageReader& reader, const char* what,
}
}
+// Check that it recognises invalid directives
+
+TEST_F(MessageReaderTest, InvalidDirectives) {
+
+ // Check that a "$" with nothing else generates an error
+ processLineException(reader_, "$", MSG_UNRECDIR);
+ processLineException(reader_, "$xyz", MSG_UNRECDIR);
+}
+
// Check that it can parse a prefix
TEST_F(MessageReaderTest, Prefix) {
@@ -104,8 +113,8 @@ TEST_F(MessageReaderTest, Prefix) {
// Check that no $PREFIX is present
EXPECT_EQ(string(""), reader_.getPrefix());
- // Check that a $PREFIX directive with no argument generates an error.
- processLineException(reader_, "$PREFIX", MSG_PRFNOARG);
+ // Check that a $PREFIX directive with no argument is OK
+ EXPECT_NO_THROW(reader_.processLine("$PREFIX"));
// Check a $PREFIX with multiple arguments is invalid
processLineException(reader_, "$prefix A B", MSG_PRFEXTRARG);
@@ -118,17 +127,19 @@ TEST_F(MessageReaderTest, Prefix) {
// A valid prefix should be accepted
EXPECT_NO_THROW(reader_.processLine("$PREFIX dlm__"));
- EXPECT_EQ(string("DLM__"), reader_.getPrefix());
+ EXPECT_EQ(string("dlm__"), reader_.getPrefix());
// And check that the parser fails on invalid prefixes...
processLineException(reader_, "$prefix 1ABC", MSG_PRFINVARG);
- // ... and rejects another valid one
- processLineException(reader_, "$PREFIX ABC", MSG_DUPLPRFX);
-
// Check that we can clear the prefix as well
reader_.clearPrefix();
EXPECT_EQ(string(""), reader_.getPrefix());
+
+ EXPECT_NO_THROW(reader_.processLine("$PREFIX dlm__"));
+ EXPECT_EQ(string("dlm__"), reader_.getPrefix());
+ EXPECT_NO_THROW(reader_.processLine("$PREFIX"));
+ EXPECT_EQ(string(""), reader_.getPrefix());
}
// Check that it can parse a namespace
@@ -173,8 +184,8 @@ TEST_F(MessageReaderTest, Namespace) {
TEST_F(MessageReaderTest, ValidMessageAddDefault) {
// Add a couple of valid messages
- reader_.processLine("GLOBAL1\t\tthis is message global one\n");
- reader_.processLine("GLOBAL2 this is message global two");
+ reader_.processLine("% GLOBAL1\t\tthis is message global one\n");
+ reader_.processLine("%GLOBAL2 this is message global two");
// ... and check them
EXPECT_EQ(string("this is message global one"),
@@ -191,9 +202,9 @@ TEST_F(MessageReaderTest, ValidMessageAddDefault) {
TEST_F(MessageReaderTest, ValidMessageAdd) {
// Add a couple of valid messages
- reader_.processLine("GLOBAL1\t\tthis is message global one\n",
+ reader_.processLine("%GLOBAL1\t\tthis is message global one\n",
MessageReader::ADD);
- reader_.processLine("GLOBAL2 this is message global two",
+ reader_.processLine("% GLOBAL2 this is message global two",
MessageReader::ADD);
// ... and check them
@@ -214,9 +225,9 @@ TEST_F(MessageReaderTest, ValidMessageReplace) {
dictionary_->add("GLOBAL2", "original global2 message");
// Replace a couple of valid messages
- reader_.processLine("GLOBAL1\t\tthis is message global one\n",
+ reader_.processLine("% GLOBAL1\t\tthis is message global one\n",
MessageReader::REPLACE);
- reader_.processLine("GLOBAL2 this is message global two",
+ reader_.processLine("% GLOBAL2 this is message global two",
MessageReader::REPLACE);
// ... and check them
@@ -237,14 +248,14 @@ TEST_F(MessageReaderTest, ValidMessageReplace) {
TEST_F(MessageReaderTest, Overflows) {
// Add a couple of valid messages
- reader_.processLine("GLOBAL1\t\tthis is message global one\n");
- reader_.processLine("GLOBAL2 this is message global two");
+ reader_.processLine("% GLOBAL1\t\tthis is message global one\n");
+ reader_.processLine("% GLOBAL2 this is message global two");
// Add a duplicate in ADD mode.
- reader_.processLine("GLOBAL1\t\tthis is a replacement for global one");
+ reader_.processLine("% GLOBAL1\t\tthis is a replacement for global one");
// Replace a non-existent one in REPLACE mode
- reader_.processLine("LOCAL\t\tthis is a new message",
+ reader_.processLine("% LOCAL\t\tthis is a new message",
MessageReader::REPLACE);
// Check what is in the dictionary.
diff --git a/src/lib/log/tests/run_time_init_test.sh.in b/src/lib/log/tests/run_time_init_test.sh.in
index 1010566..e48a781 100755
--- a/src/lib/log/tests/run_time_init_test.sh.in
+++ b/src/lib/log/tests/run_time_init_test.sh.in
@@ -29,48 +29,49 @@ passfail() {
# Create the local message file for testing
cat > $localmes << .
-NOTHERE this message is not in the global dictionary
-MSGRDERR replacement read error, parameters: '%1' and '%2'
-UNRECDIR replacement unrecognised directive message, parameter is '%1'
+\$PREFIX MSG_
+% NOTHERE this message is not in the global dictionary
+% READERR replacement read error, parameters: '%1' and '%2'
+% RDLOCMES replacement read local message file, parameter is '%1'
.
echo -n "1. runInitTest default parameters: "
cat > $tempfile << .
-FATAL [alpha.example] MSGWRTERR, error writing to test1: 42
-ERROR [alpha.example] UNRECDIR, unrecognised directive 'false'
-WARN [alpha.dlm] MSGRDERR, error reading from message file a.txt: dummy test
-INFO [alpha.dlm] OPNMSGIN, unable to open message file example.msg for input: dummy test
+FATAL [alpha.example] MSG_WRITERR, error writing to test1: 42
+ERROR [alpha.example] MSG_RDLOCMES, reading local message file dummy/file
+WARN [alpha.dlm] MSG_READERR, error reading from message file a.txt: dummy reason
+INFO [alpha.dlm] MSG_OPENIN, unable to open message file example.msg for input: dummy reason
.
./logger_support_test | cut -d' ' -f3- | diff $tempfile -
passfail $?
echo -n "2. Severity filter: "
cat > $tempfile << .
-FATAL [alpha.example] MSGWRTERR, error writing to test1: 42
-ERROR [alpha.example] UNRECDIR, unrecognised directive 'false'
+FATAL [alpha.example] MSG_WRITERR, error writing to test1: 42
+ERROR [alpha.example] MSG_RDLOCMES, reading local message file dummy/file
.
./logger_support_test -s error | cut -d' ' -f3- | diff $tempfile -
passfail $?
echo -n "3. Debug level: "
cat > $tempfile << .
-FATAL [alpha.example] MSGWRTERR, error writing to test1: 42
-ERROR [alpha.example] UNRECDIR, unrecognised directive 'false'
-WARN [alpha.dlm] MSGRDERR, error reading from message file a.txt: dummy test
-INFO [alpha.dlm] OPNMSGIN, unable to open message file example.msg for input: dummy test
-DEBUG [alpha.example] UNRECDIR, unrecognised directive '[abc]'
-DEBUG [alpha.example] UNRECDIR, unrecognised directive '[24]'
-DEBUG [alpha.example] UNRECDIR, unrecognised directive '[25]'
+FATAL [alpha.example] MSG_WRITERR, error writing to test1: 42
+ERROR [alpha.example] MSG_RDLOCMES, reading local message file dummy/file
+WARN [alpha.dlm] MSG_READERR, error reading from message file a.txt: dummy reason
+INFO [alpha.dlm] MSG_OPENIN, unable to open message file example.msg for input: dummy reason
+DEBUG [alpha.example] MSG_RDLOCMES, reading local message file dummy/0
+DEBUG [alpha.example] MSG_RDLOCMES, reading local message file dummy/24
+DEBUG [alpha.example] MSG_RDLOCMES, reading local message file dummy/25
.
./logger_support_test -s debug -d 25 | cut -d' ' -f3- | diff $tempfile -
passfail $?
echo -n "4. Local message replacement: "
cat > $tempfile << .
-WARN [alpha.log] IDNOTFND, could not replace message for 'NOTHERE': no such message identification
-FATAL [alpha.example] MSGWRTERR, error writing to test1: 42
-ERROR [alpha.example] UNRECDIR, replacement unrecognised directive message, parameter is 'false'
-WARN [alpha.dlm] MSGRDERR, replacement read error, parameters: 'a.txt' and 'dummy test'
+WARN [alpha.log] MSG_IDNOTFND, could not replace message text for 'MSG_NOTHERE': no such message
+FATAL [alpha.example] MSG_WRITERR, error writing to test1: 42
+ERROR [alpha.example] MSG_RDLOCMES, replacement read local message file, parameter is 'dummy/file'
+WARN [alpha.dlm] MSG_READERR, replacement read error, parameters: 'a.txt' and 'dummy reason'
.
./logger_support_test -s warn $localmes | cut -d' ' -f3- | diff $tempfile -
passfail $?
diff --git a/src/lib/util/encode/base_n.cc b/src/lib/util/encode/base_n.cc
index e79f11d..406dc77 100644
--- a/src/lib/util/encode/base_n.cc
+++ b/src/lib/util/encode/base_n.cc
@@ -138,7 +138,7 @@ private:
// DecodeNormalizer is an input iterator intended to be used as a filter
// between the encoded baseX stream and binary_from_baseXX.
// A DecodeNormalizer object is configured with three string iterators
-// (base, base_beinpad, and base_beginpad), specifying the head of the string,
+// (base, base_beginpad, and base_end), specifying the head of the string,
// the beginning position of baseX padding (when there's padding), and
// end of the string, respectively. It internally iterators over the original
// stream, and return each character of the encoded string via its dereference
diff --git a/src/lib/util/time_utilities.cc b/src/lib/util/time_utilities.cc
index 229800d..9303ab5 100644
--- a/src/lib/util/time_utilities.cc
+++ b/src/lib/util/time_utilities.cc
@@ -110,12 +110,9 @@ timeToText64(uint64_t value) {
// library, it's not even declared in a header file.
namespace detail {
int64_t (*gettimeFunction)() = NULL;
-}
-namespace {
int64_t
-gettimeofdayWrapper() {
- using namespace detail;
+gettimeWrapper() {
if (gettimeFunction != NULL) {
return (gettimeFunction());
}
@@ -132,7 +129,7 @@ timeToText32(const uint32_t value) {
// We first adjust the time to the closest epoch based on the current time.
// Note that the following variables must be signed in order to handle
// time until year 2038 correctly.
- const int64_t start = gettimeofdayWrapper() - 0x7fffffff;
+ const int64_t start = detail::gettimeWrapper() - 0x7fffffff;
int64_t base = 0;
int64_t t;
while ((t = (base + value)) < start) {
diff --git a/src/lib/util/time_utilities.h b/src/lib/util/time_utilities.h
index 0558f16..a53089d 100644
--- a/src/lib/util/time_utilities.h
+++ b/src/lib/util/time_utilities.h
@@ -15,6 +15,8 @@
#ifndef __TIME_UTILITIES_H
#define __TIME_UTILITIES_H 1
+#include <string>
+
#include <sys/types.h>
#include <stdint.h>
@@ -39,6 +41,32 @@ public:
isc::Exception(file, line, what) {}
};
+namespace detail {
+/// Return the current time in seconds
+///
+/// This function returns the "current" time in seconds from epoch
+/// (00:00:00 January 1, 1970) as a 64-bit signed integer. The return
+/// value can represent a point of time before epoch as a negative number.
+///
+/// This function is provided to help test time conscious implementations
+/// such as DNSSEC and TSIG signatures. It is difficult to test them with
+/// an unusual or a specifically chosen "current" via system-provided
+/// library functions to get time. This function acts as a straightforward
+/// wrapper of such a library function, but provides test code with a hook
+/// to return an arbitrary time value: if \c isc::util::detail::gettimeFunction
+/// is set to a pointer of function that returns 64-bit signed integer,
+/// \c gettimeWrapper() calls that function instead of the system library.
+///
+/// This hook variable is specifically intended for testing purposes, so,
+/// even if it's visible outside of this library, it's not even declared in a
+/// header file.
+///
+/// If the implementation doesn't need to be tested with faked current time,
+/// it should simply use the system supplied library function instead of
+/// this one.
+int64_t gettimeWrapper();
+}
+
///
/// \name DNSSEC time conversion functions.
///
diff --git a/src/lib/util/unittests/Makefile.am b/src/lib/util/unittests/Makefile.am
index 84d7d21..340cd1f 100644
--- a/src/lib/util/unittests/Makefile.am
+++ b/src/lib/util/unittests/Makefile.am
@@ -4,5 +4,7 @@ AM_CXXFLAGS = $(B10_CXXFLAGS)
lib_LTLIBRARIES = libutil_unittests.la
libutil_unittests_la_SOURCES = fork.h fork.cc resolver.h
libutil_unittests_la_SOURCES += newhook.h newhook.cc
+libutil_unittests_la_SOURCES += testdata.h testdata.cc
+libutil_unittests_la_SOURCES += textdata.h
CLEANFILES = *.gcno *.gcda
diff --git a/src/lib/util/unittests/testdata.cc b/src/lib/util/unittests/testdata.cc
new file mode 100644
index 0000000..2148d31
--- /dev/null
+++ b/src/lib/util/unittests/testdata.cc
@@ -0,0 +1,61 @@
+// 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 <string>
+#include <stdexcept>
+#include <fstream>
+#include <vector>
+
+#include "testdata.h"
+
+using namespace std;
+
+namespace {
+vector<string>&
+getDataPaths() {
+ static vector<string> data_path;
+ return (data_path);
+}
+}
+
+namespace isc {
+namespace util {
+namespace unittests {
+
+void
+addTestDataPath(const string& path) {
+ getDataPaths().push_back(path);
+}
+
+void
+openTestData(const char* const datafile, ifstream& ifs) {
+ vector<string>::const_iterator it = getDataPaths().begin();
+ for (; it != getDataPaths().end(); ++it) {
+ string data_path = *it;
+ if (data_path.empty() || *data_path.rbegin() != '/') {
+ data_path.push_back('/');
+ }
+ ifs.open((data_path + datafile).c_str(), ios_base::in);
+ if (!ifs.fail()) {
+ return;
+ }
+ }
+
+ throw runtime_error("failed to open data file in data paths: " +
+ string(datafile));
+}
+
+}
+}
+}
diff --git a/src/lib/util/unittests/testdata.h b/src/lib/util/unittests/testdata.h
new file mode 100644
index 0000000..03bd83a
--- /dev/null
+++ b/src/lib/util/unittests/testdata.h
@@ -0,0 +1,54 @@
+// 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 __UTIL_UNITTESTS_TESTDATA_H
+#define __UTIL_UNITTESTS_TESTDATA_H 1
+
+/**
+ * @file testdata.h
+ * @short Manipulating test data files.
+ *
+ * This utility defines functions that help test case handle test data
+ * stored in a file.
+ */
+
+namespace isc {
+namespace util {
+namespace unittests {
+/// Add a path (directory) that \c openTestData() will search for test data
+/// files.
+void addTestDataPath(const std::string& path);
+
+/// Open a file specified by 'datafile' using the data paths registered via
+/// addTestDataPath(). On success, ifs will be ready for reading the data
+/// stored in 'datafile'. If the data file cannot be open with any of the
+/// registered paths, a runtime_error exception will be thrown.
+///
+/// \note Care should be taken if you want to reuse the same single \c ifs
+/// for multiple input data. Some standard C++ library implementations retain
+/// the failure bit if the first stream reaches the end of the first file,
+/// and make the second call to \c ifstream::open() fail. The safest way
+/// is to use a different \c ifstream object for a new call to this function;
+/// alternatively make sure you explicitly clear the error bit by calling
+/// \c ifstream::clear() on \c ifs.
+void openTestData(const char* const datafile, std::ifstream& ifs);
+}
+}
+}
+
+#endif // __UTIL_UNITTESTS_TESTDATA_H
+
+// Local Variables:
+// mode: c++
+// End:
diff --git a/src/lib/util/unittests/textdata.h b/src/lib/util/unittests/textdata.h
new file mode 100644
index 0000000..3e9b1aa
--- /dev/null
+++ b/src/lib/util/unittests/textdata.h
@@ -0,0 +1,103 @@
+// 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 <istream>
+#include <string>
+#include <sstream>
+
+#include <gtest/gtest.h>
+
+#ifndef __UTIL_UNITTESTS_TEXTDATA_H
+#define __UTIL_UNITTESTS_TEXTDATA_H 1
+
+/**
+ * @file textdata.h
+ * @short Utilities for tests with text data.
+ *
+ * This utility provides convenient helper functions for unit tests using
+ * textual data.
+ */
+
+namespace isc {
+namespace util {
+namespace unittests {
+
+/// Line-by-line text comparison.
+///
+/// This templated function takes two standard input streams, extracts
+/// strings from them, and compares the two sets of strings line by line.
+template <typename EXPECTED_STREAM, typename ACTUAL_STREAM>
+void
+matchTextData(EXPECTED_STREAM& expected, ACTUAL_STREAM& actual) {
+ std::string actual_line;
+ std::string expected_line;
+ while (std::getline(actual, actual_line), !actual.eof()) {
+ std::getline(expected, expected_line);
+ if (expected.eof()) {
+ FAIL() << "Redundant line in actual output: " << actual_line;
+ break;
+ }
+ if (actual.bad() || actual.fail() ||
+ expected.bad() || expected.fail()) {
+ throw std::runtime_error("Unexpected error in data streams");
+ }
+ EXPECT_EQ(expected_line, actual_line);
+ }
+ while (std::getline(expected, expected_line), !expected.eof()) {
+ ADD_FAILURE() << "Missing line in actual output: " << expected_line;
+ }
+}
+
+/// Similar to the fully templated version, but takes string for the second
+/// (actual) data.
+///
+/// Due to the nature of textual data, it will often be the case that test
+/// data is given as a string object. This shortcut version helps such cases
+/// so that the test code doesn't have to create a string stream with the
+/// string data just for testing.
+template <typename EXPECTED_STREAM>
+void
+matchTextData(EXPECTED_STREAM& expected, const std::string& actual_text) {
+ std::istringstream iss(actual_text);
+ matchTextData(expected, iss);
+}
+
+/// Same for the previous version, but the first argument is string.
+template <typename ACTUAL_STREAM>
+void
+matchTextData(const std::string& expected_text, ACTUAL_STREAM& actual) {
+ std::istringstream iss(expected_text);
+ matchTextData(iss, actual);
+}
+
+/// Same for the previous two, but takes strings for both expected and
+/// actual data.
+void
+matchTextData(const std::string& expected_text,
+ const std::string& actual_text)
+{
+ std::istringstream expected_is(expected_text);
+ std::istringstream actual_is(actual_text);
+ matchTextData(expected_is, actual_is);
+}
+
+}
+}
+}
+
+#endif // __UTIL_UNITTESTS_TEXTDATA_H
+
+// Local Variables:
+// mode: c++
+// End:
More information about the bind10-changes
mailing list