BIND 10 trac739, updated. e57fcdbaee0fe7562168a363c1f3273a92fa595a Merge branch 'master' into trac739
BIND 10 source code commits
bind10-changes at lists.isc.org
Wed May 25 14:42:40 UTC 2011
The branch, trac739 has been updated
via e57fcdbaee0fe7562168a363c1f3273a92fa595a (commit)
via 14a1dcf8c6d299b7b7d91b27c4bd73b06e1fd61d (commit)
via ce11fa4222196986805f54ab5b67b77dd5fe8e47 (commit)
via b8855cafa94a12af7e087a4866367f2336e10bdb (commit)
via 682108d6e371706f340cfe8575d8e00d1dab09cc (commit)
via 6ea285d4f7cf6a9fd18bd9a3811944fc02d0e34c (commit)
via 1aa773d84cd6431aa1483eb34a7f4204949a610f (commit)
via 5519b6cb34e655c96dca4ec8be2a3eabda941f3c (commit)
via de552586600ecd4123b904e65d09c3ef0717fc2a (commit)
via 6a9f0125c633e6203e5fda37e6220ea862038df6 (commit)
via e66cc9c2e0eca69c62a3a61133fc4ba220274bc7 (commit)
via c37ebedf94c5dbbed3c685272a0cdc4bea67fb04 (commit)
via 24fd003fcc458c673803b953ca857a96bfa5183c (commit)
via a9d323bf4df6aeb6333b057514b748d05febc1a9 (commit)
via e1df41b8ba21d04560ad4b65837bf7bb32fbdf34 (commit)
via 2af8dc8ec9301a935228568f80f8ba18531a3ffc (commit)
via b0dfec1a5000ad22ef4dfa9c21c89e90b0e68673 (commit)
via 496a4378cfcc8430dd1155bb3ae5448f27ebfdfa (commit)
via 6e6eda44e9b1b7236e3baccc25e008eb674440aa (commit)
via 27e122bfdd9edcc04e39cbe8a033d89ef36207d3 (commit)
via d04505cee4d9df2cb9cf9a2909a948589be4ff09 (commit)
via 77d792c9d7c1a3f95d3e6a8b721ac79002cd7db1 (commit)
via 82ea1075a4ae1ef8f6c0ba3cca5d6a1968a77f3f (commit)
via 358e2303c565cb387aca660c2e55967b683792ff (commit)
via 2ddb97d633c1721d74615b84d2c6ebd360b9bda3 (commit)
via 74567fe1e92c8f10da3a639d235cdbc4c4d5cc9d (commit)
via cc3ccc1f474407507118ae0c53af307d0dd7bdf2 (commit)
via 41521402ab646288ff1a4a12c5110d52dddeb4b1 (commit)
via 5cb80378e05cc0ec208adfbe09e15efd04431267 (commit)
via 0c7670c04135ba2ade5ffe241b66094aeb891f16 (commit)
via f0645fdb5d7be3c7d7090159847853f5a06ed7e4 (commit)
via df354bdb4031c1f67c03d71527105da4e35d1a6e (commit)
via b30c6c0843ae18a275f65e7ad5ab86bd78c82678 (commit)
via 30d350e8cae28631a083c04a9ccf8d5bcaf69adb (commit)
via 6c86102687c491bdac96577cabf98a0640ad8a60 (commit)
via 37035a073be57c9f6d00f2558f74ce69b7067295 (commit)
via 765bfaf3ccf1a96e981210c429d5beacb229b4ce (commit)
via 938eb7ec8ac6bc670fcdc95a2189f4b13390e9cb (commit)
via 50c678a05ecc181f4a63fd930bd345bce72dfc68 (commit)
via b5dc1d86b2a763f98d8414589a545fcbda09a36c (commit)
via bf4d941c69ed13ec03a2e179009780eb1ad11359 (commit)
via 71ff1ab09693f1a90e46ba74cd5bbadf449161ea (commit)
via d2ee006cd5d5f3cf5ff56594fbf3d8955685d8a1 (commit)
via 74a60943da1619dc598bbf4b440b78914ebe98a9 (commit)
via 71d57323f77270278f75a57f63499c82f8f4ddd0 (commit)
via 6f8d74e83a1c8073dada7288be92c2976c638e27 (commit)
via 0034051f94a5e0e91e2574ec07f9b5374d39d6cd (commit)
via 0346449357648e45f5b68f792cbf6dc914b16d9c (commit)
via 89e3ffaa1fb56bcc76f626a64afcc25e506d8b54 (commit)
via fc97ca7de4ecd731aa6e470a7c68f5b26cbda7b0 (commit)
via 78502c021478d97672232015b7df06a7d52e531b (commit)
via 71eb80242fd144bddc06c98b3bdaa91341a65f26 (commit)
via 0555ab65d0e43d03b2d40c95d833dd050eea6c23 (commit)
via 23d63caf0cc09f19a01794458bf2457a67caac05 (commit)
via 031eda633fe45496f47fad9d4b656a0100144f75 (commit)
via 77def12e8d0d957d9fe587816b27b6df4f88ce90 (commit)
via 080db7e6245ebf63f0b3104e92b99142c87fe291 (commit)
via b50e03318eeb6464f7143eb524eb7b8ed2a5d02a (commit)
via 88504d121c5e08fff947b92e698a54d24d14c375 (commit)
via ede93598806f8ddb21d193eb002fad8218416e40 (commit)
via 35ac625edfbc78dde6ccf78f8d577f3d8edadbce (commit)
via 4bd053d4e1a4165c7b4b5d91a6f674e40250301a (commit)
via a2936cc155f8b5ce2afaa82820fa377a037f2be3 (commit)
via 94cfeebfec6574350fb4980c2b0cc6a7d84ba4f7 (commit)
via 47e5578077fa332f8476a13aa4ad6ba29e003a1a (commit)
via 830c0ba6c96b009d1c9c4fa31dea7cf4f6de4e37 (commit)
via 9e84764e4911a4ecf9b82df3beb6cd289eb68ada (commit)
via 28465ec39050a2779fe98503c690ed4df2711e98 (commit)
via bcea2b3da4bca69194a2154b92f9f734edbe8322 (commit)
via a251c4f239e7b42856314412142cd9777f91dbf1 (commit)
via 2360fc72c24eb39d32b0afd6a389cfe8f10cb2f9 (commit)
via fc9c42d22de8c2c5555573a1a3e29b2d30146a29 (commit)
via 3303478b6f9943fd5514268bb1c0c42a638d107a (commit)
via d158640b970e5d8f0e5d4f8c6c278f03ee0e47e7 (commit)
via d11b5e89203a5340d4e5ca51c4c02db17c33dc1f (commit)
via 7a1c4cc8e1baaf51a2058edd0a4179bd586345f3 (commit)
via de0694d2f4c7dba909eb922e3fa1a1269d3cbc78 (commit)
via bc0688b0e7846ec9bb38e4e014ed23be84876948 (commit)
via b8a7bf58c974c9ba4518fd894963cd66a19baf7e (commit)
via 3a54f64afc94e9394a527872b957609d14002ff1 (commit)
via 7c390c1149baab7bd33e741afcdefb827275d5cd (commit)
via b6982ea32af206b6ef661b492eea7b274af97bd2 (commit)
via 62168918aceee04418765089cdc5fdfb34bf66a3 (commit)
via 929a9cae2b351e67ac1953514a63b0c54095361c (commit)
via 04ea273e2988cc405ae3ee7555a0f028258c17a5 (commit)
via 58a5aabf65705e4107cc59ad25c76bbbcedc52bf (commit)
via 43f5888605c770c012421a420766b01e1ad8a96b (commit)
via 5b495e880e559c413a80d0dcc741a5076f3f7eb4 (commit)
via 141bf465a7b13f1e4c92a76ca2df208c8d375385 (commit)
via 7a87432e627715d9062367e2321427a4510d5822 (commit)
via ac2e283bd92dcc5af494938d6cfed82e4074abde (commit)
via 03fd43339b3ffd2537a1621d628d504cee13c9d5 (commit)
via 6ed8870ee7ac32e786030403de4423b8d7647679 (commit)
via 7a6f36fc9073def2a531a4090b97d223d5a5c69d (commit)
via 523ab3a9278385abc26e1438803499a66e260f43 (commit)
via 8e4fcdf22e1b5d14f740276e3a6c34a90bf3117c (commit)
via b02c482b25d489b723927f45c4cfe3fedd89f5b7 (commit)
via 99240dbca4848d3aa76b34f5ff0d6da4d6bf911c (commit)
via cd79c2fae07f7b1a8d2e2f501488de7a2d11eac5 (commit)
via 76039741c19fa58e404879b334475b9ae01cd8dd (commit)
via dc19181b46dec42bc5db83861731656a5b45b899 (commit)
via 94d43a69237b5d2bf671e384ff8b2b9a5ce445b4 (commit)
via c3e1e45419104bdb01dd385b22eae85bb8799611 (commit)
via 946b527467c19236814cae6e35191ce19db3284a (commit)
via 02e2f3a3c2ae669824d595bc9b42f37d9624b22c (commit)
via 54210ba456b4e0822c5e333fd1f996bb35c6bee9 (commit)
via 7a52a9a3618fd19ea9779eb0cef1a3e4f1c3d444 (commit)
via 75340e4d2906762ecd088180087c9229a253e4ed (commit)
via 71315bc901882a8bbb35a95c19781528560fcb82 (commit)
via 3f27e2dc3c0ce961a95e4791604e3cb12fd43dfa (commit)
via 50ca3176a95f7ca760c0749d7a92634e2526369d (commit)
via 08878f6bfb271301564ad307339d2599bc6d951c (commit)
via 3bb68553bb39502b749265542c5b6d36ad80e32d (commit)
via ade0b1a890f1fe21c075d4fef332e3e35407f086 (commit)
via ff20711233b8bd6a5df5a896c0d2855222291a9e (commit)
via 3e8ef1595dd2c7095540b22cca77e69991ad8ee1 (commit)
via c6827475447b07375cfdd2902c08519cd1cc9dde (commit)
via a1420dabc6ace739cb21044184654717a32604a5 (commit)
via cd689c463a59fa5fee72d3f977835e0369eb3650 (commit)
via c3f769ce6f0e3be367f7e0079a97a11e3f344761 (commit)
via fd9855894957b318876a9cb9a0dbe2b4cbbdd4b6 (commit)
via 0f07b7c15231aa778e693e0f2b36d32e1023c431 (commit)
via a28db69a6e8e3548e8e41f62999fff18dbd33bec (commit)
via e3ab39868457157166a8b7b2f1753555409426b9 (commit)
via cba6db1c8d894a7f30ae820a49aea3d0ff8c18be (commit)
via d4a2c864bf1952f1bcccead59159462519c58e10 (commit)
via ab5868a8dc4c6859f772219daddb7775848d3dc6 (commit)
via fd51a1883a332c305fa4015a6210971d3956fc12 (commit)
via 04611df9f5e34b2a8d6949b5b00d25a065c7c920 (commit)
via 76a1d7e547e9eecdc4aecfa3cf6cc4ba940ad725 (commit)
via 82ead9ea724a5482c44e8a6235bcd4634eacce2c (commit)
via 4a78ffbf8a2b9d1171415d7f0af1b7adc4e53481 (commit)
via fe59cd140491dbc685932bd22440e28c703c1053 (commit)
via 9c3195a542df8d7e747e28d49d7abdc707781632 (commit)
from f80bba65ac947cdc2f99253623e976c815b63140 (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 e57fcdbaee0fe7562168a363c1f3273a92fa595a
Merge: f80bba65ac947cdc2f99253623e976c815b63140 14a1dcf8c6d299b7b7d91b27c4bd73b06e1fd61d
Author: Stephen Morris <stephen at isc.org>
Date: Wed May 25 15:19:52 2011 +0100
Merge branch 'master' into trac739
-----------------------------------------------------------------------
Summary of changes:
ChangeLog | 64 ++-
README | 4 +-
configure.ac | 5 +-
doc/guide/bind10-guide.html | 44 +-
doc/guide/bind10-guide.xml | 16 +-
src/bin/auth/tests/Makefile.am | 1 +
src/bin/auth/tests/run_unittests.cc | 3 +-
src/bin/msgq/tests/msgq_test.py | 2 +-
src/bin/resolver/tests/Makefile.am | 1 +
src/bin/resolver/tests/run_unittests.cc | 3 +-
src/bin/sockcreator/tests/Makefile.am | 5 +-
src/bin/sockcreator/tests/run_unittests.cc | 3 +-
src/bin/xfrin/b10-xfrin.8 | 43 ++-
src/bin/xfrin/b10-xfrin.xml | 46 ++-
src/bin/xfrin/tests/xfrin_test.py | 532 ++++++++++++++++++--
src/bin/xfrin/xfrin.py.in | 352 ++++++++++---
src/bin/xfrin/xfrin.spec | 50 ++-
src/bin/zonemgr/b10-zonemgr.8 | 37 +-
src/bin/zonemgr/b10-zonemgr.xml | 66 ++-
src/bin/zonemgr/tests/zonemgr_test.py | 55 ++
src/bin/zonemgr/zonemgr.py.in | 89 +++-
src/cppcheck-suppress.lst | 2 +-
src/lib/asiodns/tests/Makefile.am | 1 +
src/lib/asiodns/tests/run_unittests.cc | 3 +-
src/lib/asiolink/tests/Makefile.am | 8 +-
src/lib/asiolink/tests/run_unittests.cc | 3 +-
src/lib/bench/tests/Makefile.am | 6 +-
src/lib/bench/tests/run_unittests.cc | 3 +-
src/lib/cache/tests/Makefile.am | 2 +-
src/lib/cache/tests/run_unittests.cc | 3 +-
src/lib/cc/tests/Makefile.am | 1 +
src/lib/cc/tests/run_unittests.cc | 3 +-
src/lib/config/ccsession.cc | 51 ++-
src/lib/config/ccsession.h | 41 ++-
src/lib/config/tests/Makefile.am | 5 +-
src/lib/config/tests/ccsession_unittests.cc | 97 ++++
src/lib/config/tests/run_unittests.cc | 10 +-
src/lib/cryptolink/crypto_hmac.cc | 41 ++-
src/lib/cryptolink/cryptolink.h | 12 +-
src/lib/cryptolink/tests/Makefile.am | 2 +-
src/lib/cryptolink/tests/crypto_unittests.cc | 297 ++++++++++--
src/lib/cryptolink/tests/run_unittests.cc | 3 +-
src/lib/datasrc/tests/Makefile.am | 1 +
src/lib/datasrc/tests/run_unittests.cc | 3 +-
src/lib/dns/python/Makefile.am | 16 +-
src/lib/dns/python/edns_python.cc | 6 +-
src/lib/dns/python/message_python.cc | 42 ++-
src/lib/dns/python/messagerenderer_python.cc | 189 ++++----
...tsigerror_python.h => messagerenderer_python.h} | 28 +-
src/lib/dns/python/name_python.cc | 479 +++++++++---------
src/lib/dns/python/name_python.h | 84 +++
src/lib/dns/python/pydnspp.cc | 24 +-
src/lib/dns/python/pydnspp_towire.h | 127 +++++
src/lib/dns/python/question_python.cc | 4 +-
src/lib/dns/python/rrset_python.cc | 8 +-
src/lib/dns/python/tests/Makefile.am | 4 +-
src/lib/dns/python/tests/message_python_test.py | 49 ++-
src/lib/dns/python/tests/tsig_python_test.py | 535 +++++++++++++++++++-
...ig_python_test.py => tsig_rdata_python_test.py} | 19 +-
src/lib/dns/python/tests/tsigkey_python_test.py | 3 +
src/lib/dns/python/tests/tsigrecord_python_test.py | 44 ++
src/lib/dns/python/tsig_python.cc | 310 ++++++++++--
.../python/{tsigerror_python.h => tsig_python.h} | 21 +-
.../python/tsig_rdata_python.cc} | 262 ++++++----
.../{tsigerror_python.h => tsig_rdata_python.h} | 31 +-
src/lib/dns/python/tsigerror_python.cc | 10 +-
src/lib/dns/python/tsigerror_python.h | 8 +
src/lib/dns/python/tsigkey_python.cc | 384 ++++++++-------
.../{tsigerror_python.h => tsigkey_python.h} | 27 +-
.../python/tsigrecord_python.cc} | 218 +++++----
.../{tsigerror_python.h => tsigrecord_python.h} | 27 +-
src/lib/dns/rdata/any_255/tsig_250.cc | 2 +-
src/lib/dns/tests/Makefile.am | 1 -
src/lib/dns/tests/run_unittests.cc | 3 +-
src/lib/dns/tests/tsigkey_unittest.cc | 12 +
src/lib/dns/tsigkey.cc | 28 +
src/lib/dns/tsigkey.h | 3 +
src/lib/exceptions/tests/run_unittests.cc | 3 +
src/lib/log/tests/Makefile.am | 2 +-
src/lib/log/tests/run_unittests.cc | 3 +-
src/lib/nsas/tests/Makefile.am | 1 +
src/lib/nsas/tests/run_unittests.cc | 15 +-
src/lib/python/Makefile.am | 1 +
src/lib/python/isc/config/config_data.py | 9 +
.../python/isc/config/tests/config_data_test.py | 21 +
src/lib/python/isc/notify/notify_out.py | 8 +-
src/lib/python/isc/notify/tests/notify_out_test.py | 50 ++-
src/lib/python/isc/testutils/Makefile.am | 2 +-
src/lib/python/isc/testutils/tsigctx_mock.py | 53 ++
src/lib/resolve/Makefile.am | 22 +-
src/lib/resolve/recursive_query.cc | 172 +++++--
.../{nsas/nsas_log.cc => resolve/resolve_log.cc} | 8 +-
.../resolver_log.h => lib/resolve/resolve_log.h} | 44 +-
src/lib/resolve/resolvedef.mes | 155 ++++++
src/lib/resolve/tests/Makefile.am | 3 +-
src/lib/resolve/tests/run_unittests.cc | 5 +-
src/lib/server_common/Makefile.am | 3 +
src/lib/server_common/keyring.cc | 61 +++
src/lib/server_common/keyring.h | 96 ++++
src/lib/server_common/tests/Makefile.am | 10 +-
.../tests/data_path.h.in} | 13 +-
src/lib/server_common/tests/keyring_test.cc | 132 +++++
src/lib/server_common/tests/run_unittests.cc | 3 +-
src/lib/server_common/tests/testdata/spec.spec | 6 +
src/lib/util/Makefile.am | 4 +-
src/lib/util/io/Makefile.am | 2 -
src/lib/util/io/tests/Makefile.am | 25 -
src/lib/util/io/tests/run_unittests.cc | 22 -
src/lib/util/python/wrapper_template.cc | 18 +-
src/lib/util/python/wrapper_template.h | 15 +
src/lib/util/pyunittests/Makefile.am | 14 +
src/lib/util/pyunittests/pyunittests_util.cc | 84 +++
src/lib/util/tests/Makefile.am | 24 +-
src/lib/util/{io => }/tests/fd_share_tests.cc | 4 +-
src/lib/util/{io => }/tests/fd_tests.cc | 2 +-
src/lib/util/tests/run_unittests.cc | 4 +-
src/lib/util/unittests/Makefile.am | 12 +
src/lib/util/unittests/run_all.cc | 95 ++++
src/lib/util/unittests/run_all.h | 52 ++
tests/tools/badpacket/tests/Makefile.am | 6 +-
tests/tools/badpacket/tests/run_unittests.cc | 3 +-
121 files changed, 4953 insertions(+), 1361 deletions(-)
copy src/lib/dns/python/{tsigerror_python.h => messagerenderer_python.h} (59%)
create mode 100644 src/lib/dns/python/name_python.h
create mode 100644 src/lib/dns/python/pydnspp_towire.h
copy src/lib/dns/python/tests/{tsig_python_test.py => tsig_rdata_python_test.py} (63%)
create mode 100644 src/lib/dns/python/tests/tsigrecord_python_test.py
copy src/lib/dns/python/{tsigerror_python.h => tsig_python.h} (74%)
copy src/lib/{util/python/wrapper_template.cc => dns/python/tsig_rdata_python.cc} (52%)
copy src/lib/dns/python/{tsigerror_python.h => tsig_rdata_python.h} (57%)
copy src/lib/dns/python/{tsigerror_python.h => tsigkey_python.h} (69%)
copy src/lib/{util/python/wrapper_template.cc => dns/python/tsigrecord_python.cc} (57%)
copy src/lib/dns/python/{tsigerror_python.h => tsigrecord_python.h} (57%)
create mode 100644 src/lib/python/isc/testutils/tsigctx_mock.py
copy src/lib/{nsas/nsas_log.cc => resolve/resolve_log.cc} (86%)
copy src/{bin/resolver/resolver_log.h => lib/resolve/resolve_log.h} (59%)
create mode 100644 src/lib/resolve/resolvedef.mes
create mode 100644 src/lib/server_common/keyring.cc
create mode 100644 src/lib/server_common/keyring.h
copy src/lib/{datasrc/logger.cc => server_common/tests/data_path.h.in} (87%)
create mode 100644 src/lib/server_common/tests/keyring_test.cc
create mode 100644 src/lib/server_common/tests/testdata/spec.spec
delete mode 100644 src/lib/util/io/tests/Makefile.am
delete mode 100644 src/lib/util/io/tests/run_unittests.cc
create mode 100644 src/lib/util/pyunittests/Makefile.am
create mode 100644 src/lib/util/pyunittests/pyunittests_util.cc
rename src/lib/util/{io => }/tests/fd_share_tests.cc (97%)
rename src/lib/util/{io => }/tests/fd_tests.cc (98%)
create mode 100644 src/lib/util/unittests/run_all.cc
create mode 100644 src/lib/util/unittests/run_all.h
-----------------------------------------------------------------------
diff --git a/ChangeLog b/ChangeLog
index 679542c..4e953be 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,45 @@
+244. [func] stephen
+ In unit tests, allow the choice of whether unhandled exceptions are
+ caught in the unit test program (and details printed) or allowed to
+ propagate to the default exception handler. See the bind10-dev thread
+ https://lists.isc.org/pipermail/bind10-dev/2011-January/001867.html
+ for more details.
+ (Trac #542, git 1aa773d84cd6431aa1483eb34a7f4204949a610f)
+
+243. [func]* feng
+ Add optional hmac algorithm SHA224/384/812.
+ (Trac#782, git 77d792c9d7c1a3f95d3e6a8b721ac79002cd7db1)
+
+bind10-devel-20110519 released on May 19, 2011
+
+242. [func] jinmei
+ xfrin: added support for TSIG verify. This change completes TSIG
+ support in b10-xfrin.
+ (Trac #914, git 78502c021478d97672232015b7df06a7d52e531b)
+
+241. [func] jinmei
+ pydnspp: added python extension for the TSIG API introduced in
+ change 235.
+ (Trac #905, git 081891b38f05f9a186814ab7d1cd5c572b8f777f)
+ (Trac #915, git 0555ab65d0e43d03b2d40c95d833dd050eea6c23)
+
+240. [func]* jelte
+ Updated configuration options to Xfrin, so that you can specify
+ a master address, port, and TSIG key per zone. Still only one per
+ zone at this point, and TSIG keys are (currently) only specified
+ by their full string representation. This replaces the
+ Xfrin/master_addr, Xfrin/master_port, and short-lived
+ Xfrin/tsig_key configurations with a Xfrin/zones list.
+ (Trac #811, git 88504d121c5e08fff947b92e698a54d24d14c375)
+
+239. [bug] jerry
+ src/bin/xfrout: If a zone doesn't have notify slaves (only has
+ one apex ns record - the primary master name server) will cause
+ b10-xfrout uses 100% of CPU.
+ (Trac #684, git d11b5e89203a5340d4e5ca51c4c02db17c33dc1f)
+
238. [func] zhang likun
- Implement the simplest forwarder, which pass everything throught
+ Implement the simplest forwarder, which pass everything through
except QID, port number. The response will not be cached.
(Trac #598_new, git 8e28187a582820857ef2dae9b13637a3881f13ba)
@@ -52,7 +92,7 @@
(Trac #900, git b395258c708b49a5da8d0cffcb48d83294354ba3)
231. [func]* vorner
- The logging interface changed slightly. We use
+ 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
@@ -94,14 +134,14 @@
(Trac #781, git 9df42279a47eb617f586144dce8cce680598558a)
225. [func] naokikambe
- Added the HTTP/XML interface(b10-stats-httpd) to the
+ 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
+ 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,
+ bindctl. Since it also can send XSL (Extensible Stylesheet
+ 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)
@@ -120,11 +160,13 @@
reconfigure them.
(Trac #775, git 572ac2cf62e18f7eb69d670b890e2a3443bfd6e7)
-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.
- (Trac #387, svn 9140fab9bab5f6502bd15d391fd51ac078b0b89b)
+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. This
+ removed the Zonemgr/jitter_scope setting and introduced
+ Zonemgr/refresh_jitter and Zonemgr/reload_jitter.
+ (Trac #387, git 1241ddcffa16285d0a7bb01d6a8526e19fbb70cb)
221. [func]* jerry
src/lib/util: Create C++ utility library.
diff --git a/README b/README
index 5320a6e..a6509da 100644
--- a/README
+++ b/README
@@ -19,7 +19,9 @@ backends), b10-resolver recursive or forwarding DNS server, b10-cmdctl
remote control daemon, b10-cfgmgr configuration manager, b10-xfrin
AXFR inbound service, b10-xfrout outgoing AXFR service, b10-zonemgr
secondary manager, b10-stats statistics collection and reporting
-daemon, and a new libdns++ library for C++ with a python wrapper.
+daemon, b10-stats-httpd for HTTP access to XML-formatted stats,
+b10-host DNS lookup utility, and a new libdns++ library for C++
+with a python wrapper.
Documentation is included and also available via the BIND 10
website at http://bind10.isc.org/
diff --git a/configure.ac b/configure.ac
index c95396a..93b9304 100644
--- a/configure.ac
+++ b/configure.ac
@@ -2,7 +2,7 @@
# Process this file with autoconf to produce a configure script.
AC_PREREQ([2.59])
-AC_INIT(bind10-devel, 20110322, bind10-dev at isc.org)
+AC_INIT(bind10-devel, 20110519, bind10-dev at isc.org)
AC_CONFIG_SRCDIR(README)
AM_INIT_AUTOMAKE
AC_CONFIG_HEADERS([config.h])
@@ -775,8 +775,8 @@ AC_CONFIG_FILES([Makefile
src/lib/server_common/tests/Makefile
src/lib/util/Makefile
src/lib/util/io/Makefile
- src/lib/util/io/tests/Makefile
src/lib/util/unittests/Makefile
+ src/lib/util/pyunittests/Makefile
src/lib/util/tests/Makefile
tests/Makefile
tests/system/Makefile
@@ -843,6 +843,7 @@ AC_OUTPUT([doc/version.ent
src/lib/cc/tests/session_unittests_config.h
src/lib/log/tests/run_time_init_test.sh
src/lib/util/python/mkpywrapper.py
+ src/lib/server_common/tests/data_path.h
tests/system/conf.sh
tests/system/glue/setup.sh
tests/system/glue/nsx1/b10-config.db
diff --git a/doc/guide/bind10-guide.html b/doc/guide/bind10-guide.html
index a631a9c..069f508 100644
--- a/doc/guide/bind10-guide.html
+++ b/doc/guide/bind10-guide.html
@@ -1,19 +1,19 @@
-<html><head><meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"><title>BIND 10 Guide</title><link rel="stylesheet" href="./bind10-guide.css" type="text/css"><meta name="generator" content="DocBook XSL Stylesheets V1.75.2"><meta name="description" content="BIND 10 is a Domain Name System (DNS) suite managed by Internet Systems Consortium (ISC). It includes DNS libraries and modular components for controlling authoritative and recursive DNS servers. This is the reference guide for BIND 10 version 20110322. The most up-to-date version of this document, along with other documents for BIND 10, can be found at ."></head><body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div class="book" title="BIND 10 Guide"><div class="titlepage"><div><div><h1 class="title"><a name="id1168230298903"></a>BIND 10 Guide</h1></div><div><h2 class="subtitle">Administrator Reference for BIND 10</h2></div><div><p class="releaseinfo">This is the referenc
e guide for BIND 10 version
- 20110322.</p></div><div><p class="copyright">Copyright © 2010 Internet Systems Consortium, Inc.</p></div><div><div class="abstract" title="Abstract"><p class="title"><b>Abstract</b></p><p>BIND 10 is a Domain Name System (DNS) suite managed by
+<html><head><meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"><title>BIND 10 Guide</title><link rel="stylesheet" href="./bind10-guide.css" type="text/css"><meta name="generator" content="DocBook XSL Stylesheets V1.75.2"><meta name="description" content="BIND 10 is a Domain Name System (DNS) suite managed by Internet Systems Consortium (ISC). It includes DNS libraries and modular components for controlling authoritative and recursive DNS servers. This is the reference guide for BIND 10 version 20110519. The most up-to-date version of this document, along with other documents for BIND 10, can be found at ."></head><body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div class="book" title="BIND 10 Guide"><div class="titlepage"><div><div><h1 class="title"><a name="id1168230298903"></a>BIND 10 Guide</h1></div><div><h2 class="subtitle">Administrator Reference for BIND 10</h2></div><div><p class="releaseinfo">This is the referenc
e guide for BIND 10 version
+ 20110519.</p></div><div><p class="copyright">Copyright © 2010 Internet Systems Consortium, Inc.</p></div><div><div class="abstract" title="Abstract"><p class="title"><b>Abstract</b></p><p>BIND 10 is a Domain Name System (DNS) suite managed by
Internet Systems Consortium (ISC). It includes DNS libraries
and modular components for controlling authoritative and
recursive DNS servers.
</p><p>
- This is the reference guide for BIND 10 version 20110322.
+ This is the reference guide for BIND 10 version 20110519.
The most up-to-date version of this document, along with
- other documents for BIND 10, can be found at <a class="ulink" href="http://bind10.isc.org/docs" target="_top">http://bind10.isc.org/docs</a>. </p></div></div></div><hr></div><div class="toc"><p><b>Table of Contents</b></p><dl><dt><span class="chapter"><a href="#intro">1. Introduction</a></span></dt><dd><dl><dt><span class="section"><a href="#id1168230299038">Supported Platforms</a></span></dt><dt><span class="section"><a href="#id1168230299065">Required Software</a></span></dt><dt><span class="section"><a href="#starting_stopping">Starting and Stopping the Server</a></span></dt><dt><span class="section"><a href="#managing_once_running">Managing BIND 10</a></span></dt></dl></dd><dt><span class="chapter"><a href="#installation">2. Installation</a></span></dt><dd><dl><dt><span class="section"><a href="#id1168230284842">Building Requirements</a></span></dt><dt><span class="section"><a href="#quickstart">Quick start</a></span></dt><dt><span class="section"><a href="#install">In
stallation from source</a></span></dt><dd><dl><dt><span class="section"><a href="#id1168230285021">Download Tar File</a></span></dt><dt><span class="section"><a href="#id1168230285041">Retrieve from Git</a></span></dt><dt><span class="section"><a href="#id1168230285101">Configure before the build</a></span></dt><dt><span class="section"><a href="#id1168230285198">Build</a></span></dt><dt><span class="section"><a href="#id1168230285214">Install</a></span></dt><dt><span class="section"><a href="#id1168230285238">Install Hierarchy</a></span></dt></dl></dd></dl></dd><dt><span class="chapter"><a href="#bind10">3. Starting BIND10 with <span class="command"><strong>bind10</strong></span></a></span></dt><dd><dl><dt><span class="section"><a href="#start">Starting BIND 10</a></span></dt></dl></dd><dt><span class="chapter"><a href="#msgq">4. Command channel</a></span></dt><dt><span class="chapter"><a href="#cfgmgr">5. Configuration manager</a></span></dt><dt><span class="chapter"><a hr
ef="#cmdctl">6. Remote control daemon</a></span></dt><dd><dl><dt><span class="section"><a href="#cmdctl.spec">Configuration specification for b10-cmdctl</a></span></dt></dl></dd><dt><span class="chapter"><a href="#bindctl">7. Control and configure user interface</a></span></dt><dt><span class="chapter"><a href="#authserver">8. Authoritative Server</a></span></dt><dd><dl><dt><span class="section"><a href="#id1168230285812">Server Configurations</a></span></dt><dt><span class="section"><a href="#id1168230285877">Data Source Backends</a></span></dt><dt><span class="section"><a href="#id1168230285908">Loading Master Zones Files</a></span></dt></dl></dd><dt><span class="chapter"><a href="#xfrin">9. Incoming Zone Transfers</a></span></dt><dt><span class="chapter"><a href="#xfrout">10. Outbound Zone Transfers</a></span></dt><dt><span class="chapter"><a href="#zonemgr">11. Secondary Manager</a></span></dt><dt><span class="chapter"><a href="#resolverserver">12. Recursive Name Server<
/a></span></dt><dd><dl><dt><span class="section"><a href="#id1168230286296">Forwarding</a></span></dt></dl></dd><dt><span class="chapter"><a href="#statistics">13. Statistics</a></span></dt></dl></div><div class="chapter" title="Chapter 1. Introduction"><div class="titlepage"><div><div><h2 class="title"><a name="intro"></a>Chapter 1. Introduction</h2></div></div></div><div class="toc"><p><b>Table of Contents</b></p><dl><dt><span class="section"><a href="#id1168230299038">Supported Platforms</a></span></dt><dt><span class="section"><a href="#id1168230299065">Required Software</a></span></dt><dt><span class="section"><a href="#starting_stopping">Starting and Stopping the Server</a></span></dt><dt><span class="section"><a href="#managing_once_running">Managing BIND 10</a></span></dt></dl></div><p>
+ other documents for BIND 10, can be found at <a class="ulink" href="http://bind10.isc.org/docs" target="_top">http://bind10.isc.org/docs</a>. </p></div></div></div><hr></div><div class="toc"><p><b>Table of Contents</b></p><dl><dt><span class="chapter"><a href="#intro">1. Introduction</a></span></dt><dd><dl><dt><span class="section"><a href="#id1168230299038">Supported Platforms</a></span></dt><dt><span class="section"><a href="#id1168230299065">Required Software</a></span></dt><dt><span class="section"><a href="#starting_stopping">Starting and Stopping the Server</a></span></dt><dt><span class="section"><a href="#managing_once_running">Managing BIND 10</a></span></dt></dl></dd><dt><span class="chapter"><a href="#installation">2. Installation</a></span></dt><dd><dl><dt><span class="section"><a href="#id1168230284846">Building Requirements</a></span></dt><dt><span class="section"><a href="#quickstart">Quick start</a></span></dt><dt><span class="section"><a href="#install">In
stallation from source</a></span></dt><dd><dl><dt><span class="section"><a href="#id1168230285026">Download Tar File</a></span></dt><dt><span class="section"><a href="#id1168230285045">Retrieve from Git</a></span></dt><dt><span class="section"><a href="#id1168230285106">Configure before the build</a></span></dt><dt><span class="section"><a href="#id1168230285203">Build</a></span></dt><dt><span class="section"><a href="#id1168230285219">Install</a></span></dt><dt><span class="section"><a href="#id1168230285242">Install Hierarchy</a></span></dt></dl></dd></dl></dd><dt><span class="chapter"><a href="#bind10">3. Starting BIND10 with <span class="command"><strong>bind10</strong></span></a></span></dt><dd><dl><dt><span class="section"><a href="#start">Starting BIND 10</a></span></dt></dl></dd><dt><span class="chapter"><a href="#msgq">4. Command channel</a></span></dt><dt><span class="chapter"><a href="#cfgmgr">5. Configuration manager</a></span></dt><dt><span class="chapter"><a hr
ef="#cmdctl">6. Remote control daemon</a></span></dt><dd><dl><dt><span class="section"><a href="#cmdctl.spec">Configuration specification for b10-cmdctl</a></span></dt></dl></dd><dt><span class="chapter"><a href="#bindctl">7. Control and configure user interface</a></span></dt><dt><span class="chapter"><a href="#authserver">8. Authoritative Server</a></span></dt><dd><dl><dt><span class="section"><a href="#id1168230285816">Server Configurations</a></span></dt><dt><span class="section"><a href="#id1168230285881">Data Source Backends</a></span></dt><dt><span class="section"><a href="#id1168230285912">Loading Master Zones Files</a></span></dt></dl></dd><dt><span class="chapter"><a href="#xfrin">9. Incoming Zone Transfers</a></span></dt><dt><span class="chapter"><a href="#xfrout">10. Outbound Zone Transfers</a></span></dt><dt><span class="chapter"><a href="#zonemgr">11. Secondary Manager</a></span></dt><dt><span class="chapter"><a href="#resolverserver">12. Recursive Name Server<
/a></span></dt><dd><dl><dt><span class="section"><a href="#id1168230286300">Forwarding</a></span></dt></dl></dd><dt><span class="chapter"><a href="#statistics">13. Statistics</a></span></dt></dl></div><div class="chapter" title="Chapter 1. Introduction"><div class="titlepage"><div><div><h2 class="title"><a name="intro"></a>Chapter 1. Introduction</h2></div></div></div><div class="toc"><p><b>Table of Contents</b></p><dl><dt><span class="section"><a href="#id1168230299038">Supported Platforms</a></span></dt><dt><span class="section"><a href="#id1168230299065">Required Software</a></span></dt><dt><span class="section"><a href="#starting_stopping">Starting and Stopping the Server</a></span></dt><dt><span class="section"><a href="#managing_once_running">Managing BIND 10</a></span></dt></dl></div><p>
BIND is the popular implementation of a DNS server, developer
interfaces, and DNS tools.
BIND 10 is a rewrite of BIND 9. BIND 10 is written in C++ and Python
and provides a modular environment for serving and maintaining DNS.
</p><div class="note" title="Note" style="margin-left: 0.5in; margin-right: 0.5in;"><h3 class="title">Note</h3><p>
This guide covers the experimental prototype of
- BIND 10 version 20110322.
+ BIND 10 version 20110519.
</p></div><div class="note" title="Note" style="margin-left: 0.5in; margin-right: 0.5in;"><h3 class="title">Note</h3><p>
BIND 10 provides a EDNS0- and DNSSEC-capable
authoritative DNS server and a caching recursive name server
@@ -31,12 +31,16 @@
</p></div><div class="section" title="Required Software"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="id1168230299065"></a>Required Software</h2></div></div></div><p>
BIND 10 requires Python 3.1. Later versions may work, but Python
3.1 is the minimum version which will work.
- </p><div class="note" title="Note" style="margin-left: 0.5in; margin-right: 0.5in;"><h3 class="title">Note</h3><p>
+ </p><p>
+ BIND 10 uses the Botan crypto library for C++. It requires
+ at least Botan version 1.8. To build BIND 10, install the
+ Botan libraries and development include headers.
+ </p><p>
The authoritative server requires SQLite 3.3.9 or newer.
The <span class="command"><strong>b10-xfrin</strong></span>, <span class="command"><strong>b10-xfrout</strong></span>,
and <span class="command"><strong>b10-zonemgr</strong></span> modules require the
libpython3 library and the Python _sqlite3.so module.
- </p></div><div class="note" title="Note" style="margin-left: 0.5in; margin-right: 0.5in;"><h3 class="title">Note</h3><p>
+ </p><div class="note" title="Note" style="margin-left: 0.5in; margin-right: 0.5in;"><h3 class="title">Note</h3><p>
Some operating systems do not provide these dependencies
in their default installation nor standard packages
collections.
@@ -132,7 +136,7 @@
and, of course, DNS. These include detailed developer
documentation and code examples.
- </p></div><div class="chapter" title="Chapter 2. Installation"><div class="titlepage"><div><div><h2 class="title"><a name="installation"></a>Chapter 2. Installation</h2></div></div></div><div class="toc"><p><b>Table of Contents</b></p><dl><dt><span class="section"><a href="#id1168230284842">Building Requirements</a></span></dt><dt><span class="section"><a href="#quickstart">Quick start</a></span></dt><dt><span class="section"><a href="#install">Installation from source</a></span></dt><dd><dl><dt><span class="section"><a href="#id1168230285021">Download Tar File</a></span></dt><dt><span class="section"><a href="#id1168230285041">Retrieve from Git</a></span></dt><dt><span class="section"><a href="#id1168230285101">Configure before the build</a></span></dt><dt><span class="section"><a href="#id1168230285198">Build</a></span></dt><dt><span class="section"><a href="#id1168230285214">Install</a></span></dt><dt><span class="section"><a href="#id1168230285238">Install Hierarchy<
/a></span></dt></dl></dd></dl></div><div class="section" title="Building Requirements"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="id1168230284842"></a>Building Requirements</h2></div></div></div><div class="note" title="Note" style="margin-left: 0.5in; margin-right: 0.5in;"><h3 class="title">Note</h3><p>
+ </p></div><div class="chapter" title="Chapter 2. Installation"><div class="titlepage"><div><div><h2 class="title"><a name="installation"></a>Chapter 2. Installation</h2></div></div></div><div class="toc"><p><b>Table of Contents</b></p><dl><dt><span class="section"><a href="#id1168230284846">Building Requirements</a></span></dt><dt><span class="section"><a href="#quickstart">Quick start</a></span></dt><dt><span class="section"><a href="#install">Installation from source</a></span></dt><dd><dl><dt><span class="section"><a href="#id1168230285026">Download Tar File</a></span></dt><dt><span class="section"><a href="#id1168230285045">Retrieve from Git</a></span></dt><dt><span class="section"><a href="#id1168230285106">Configure before the build</a></span></dt><dt><span class="section"><a href="#id1168230285203">Build</a></span></dt><dt><span class="section"><a href="#id1168230285219">Install</a></span></dt><dt><span class="section"><a href="#id1168230285242">Install Hierarchy<
/a></span></dt></dl></dd></dl></div><div class="section" title="Building Requirements"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="id1168230284846"></a>Building Requirements</h2></div></div></div><div class="note" title="Note" style="margin-left: 0.5in; margin-right: 0.5in;"><h3 class="title">Note</h3><p>
Some operating systems have split their distribution packages into
a run-time and a development package. You will need to install
the development package versions, which include header files and
@@ -188,14 +192,14 @@
the Git code revision control system or as a downloadable
tar file. It may also be available in pre-compiled ready-to-use
packages from operating system vendors.
- </p><div class="section" title="Download Tar File"><div class="titlepage"><div><div><h3 class="title"><a name="id1168230285021"></a>Download Tar File</h3></div></div></div><p>
+ </p><div class="section" title="Download Tar File"><div class="titlepage"><div><div><h3 class="title"><a name="id1168230285026"></a>Download Tar File</h3></div></div></div><p>
Downloading a release tar file is the recommended method to
obtain the source code.
</p><p>
The BIND 10 releases are available as tar file downloads from
<a class="ulink" href="ftp://ftp.isc.org/isc/bind10/" target="_top">ftp://ftp.isc.org/isc/bind10/</a>.
Periodic development snapshots may also be available.
- </p></div><div class="section" title="Retrieve from Git"><div class="titlepage"><div><div><h3 class="title"><a name="id1168230285041"></a>Retrieve from Git</h3></div></div></div><p>
+ </p></div><div class="section" title="Retrieve from Git"><div class="titlepage"><div><div><h3 class="title"><a name="id1168230285045"></a>Retrieve from Git</h3></div></div></div><p>
Downloading this "bleeding edge" code is recommended only for
developers or advanced users. Using development code in a production
environment is not recommended.
@@ -229,7 +233,7 @@
<span class="command"><strong>autoheader</strong></span>,
<span class="command"><strong>automake</strong></span>,
and related commands.
- </p></div><div class="section" title="Configure before the build"><div class="titlepage"><div><div><h3 class="title"><a name="id1168230285101"></a>Configure before the build</h3></div></div></div><p>
+ </p></div><div class="section" title="Configure before the build"><div class="titlepage"><div><div><h3 class="title"><a name="id1168230285106"></a>Configure before the build</h3></div></div></div><p>
BIND 10 uses the GNU Build System to discover build environment
details.
To generate the makefiles using the defaults, simply run:
@@ -260,16 +264,16 @@
</p><p>
If the configure fails, it may be due to missing or old
dependencies.
- </p></div><div class="section" title="Build"><div class="titlepage"><div><div><h3 class="title"><a name="id1168230285198"></a>Build</h3></div></div></div><p>
+ </p></div><div class="section" title="Build"><div class="titlepage"><div><div><h3 class="title"><a name="id1168230285203"></a>Build</h3></div></div></div><p>
After the configure step is complete, to build the executables
from the C++ code and prepare the Python scripts, run:
</p><pre class="screen">$ <strong class="userinput"><code>make</code></strong></pre><p>
- </p></div><div class="section" title="Install"><div class="titlepage"><div><div><h3 class="title"><a name="id1168230285214"></a>Install</h3></div></div></div><p>
+ </p></div><div class="section" title="Install"><div class="titlepage"><div><div><h3 class="title"><a name="id1168230285219"></a>Install</h3></div></div></div><p>
To install the BIND 10 executables, support files,
and documentation, run:
</p><pre class="screen">$ <strong class="userinput"><code>make install</code></strong></pre><p>
- </p><div class="note" title="Note" style="margin-left: 0.5in; margin-right: 0.5in;"><h3 class="title">Note</h3><p>The install step may require superuser privileges.</p></div></div><div class="section" title="Install Hierarchy"><div class="titlepage"><div><div><h3 class="title"><a name="id1168230285238"></a>Install Hierarchy</h3></div></div></div><p>
+ </p><div class="note" title="Note" style="margin-left: 0.5in; margin-right: 0.5in;"><h3 class="title">Note</h3><p>The install step may require superuser privileges.</p></div></div><div class="section" title="Install Hierarchy"><div class="titlepage"><div><div><h3 class="title"><a name="id1168230285242"></a>Install Hierarchy</h3></div></div></div><p>
The following is the layout of the complete BIND 10 installation:
</p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem">
<code class="filename">bin/</code> —
@@ -486,12 +490,12 @@ shutdown
the details and relays (over a <span class="command"><strong>b10-msgq</strong></span> command
channel) the configuration on to the specified module.
</p><p>
- </p></div><div class="chapter" title="Chapter 8. Authoritative Server"><div class="titlepage"><div><div><h2 class="title"><a name="authserver"></a>Chapter 8. Authoritative Server</h2></div></div></div><div class="toc"><p><b>Table of Contents</b></p><dl><dt><span class="section"><a href="#id1168230285812">Server Configurations</a></span></dt><dt><span class="section"><a href="#id1168230285877">Data Source Backends</a></span></dt><dt><span class="section"><a href="#id1168230285908">Loading Master Zones Files</a></span></dt></dl></div><p>
+ </p></div><div class="chapter" title="Chapter 8. Authoritative Server"><div class="titlepage"><div><div><h2 class="title"><a name="authserver"></a>Chapter 8. Authoritative Server</h2></div></div></div><div class="toc"><p><b>Table of Contents</b></p><dl><dt><span class="section"><a href="#id1168230285816">Server Configurations</a></span></dt><dt><span class="section"><a href="#id1168230285881">Data Source Backends</a></span></dt><dt><span class="section"><a href="#id1168230285912">Loading Master Zones Files</a></span></dt></dl></div><p>
The <span class="command"><strong>b10-auth</strong></span> is the authoritative DNS server.
It supports EDNS0 and DNSSEC. It supports IPv6.
Normally it is started by the <span class="command"><strong>bind10</strong></span> master
process.
- </p><div class="section" title="Server Configurations"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="id1168230285812"></a>Server Configurations</h2></div></div></div><p>
+ </p><div class="section" title="Server Configurations"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="id1168230285816"></a>Server Configurations</h2></div></div></div><p>
<span class="command"><strong>b10-auth</strong></span> is configured via the
<span class="command"><strong>b10-cfgmgr</strong></span> configuration manager.
The module name is <span class="quote">“<span class="quote">Auth</span>”</span>.
@@ -511,7 +515,7 @@ This may be a temporary setting until then.
</p><div class="variablelist"><dl><dt><span class="term">shutdown</span></dt><dd>Stop the authoritative DNS server.
</dd></dl></div><p>
- </p></div><div class="section" title="Data Source Backends"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="id1168230285877"></a>Data Source Backends</h2></div></div></div><div class="note" title="Note" style="margin-left: 0.5in; margin-right: 0.5in;"><h3 class="title">Note</h3><p>
+ </p></div><div class="section" title="Data Source Backends"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="id1168230285881"></a>Data Source Backends</h2></div></div></div><div class="note" title="Note" style="margin-left: 0.5in; margin-right: 0.5in;"><h3 class="title">Note</h3><p>
For the development prototype release, <span class="command"><strong>b10-auth</strong></span>
supports a SQLite3 data source backend and in-memory data source
backend.
@@ -525,7 +529,7 @@ This may be a temporary setting until then.
The default is <code class="filename">/usr/local/var/</code>.)
This data file location may be changed by defining the
<span class="quote">“<span class="quote">database_file</span>”</span> configuration.
- </p></div><div class="section" title="Loading Master Zones Files"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="id1168230285908"></a>Loading Master Zones Files</h2></div></div></div><p>
+ </p></div><div class="section" title="Loading Master Zones Files"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="id1168230285912"></a>Loading Master Zones Files</h2></div></div></div><p>
RFC 1035 style DNS master zone files may imported
into a BIND 10 data source by using the
<span class="command"><strong>b10-loadzone</strong></span> utility.
@@ -603,7 +607,7 @@ This may be a temporary setting until then.
</p><div class="note" title="Note" style="margin-left: 0.5in; margin-right: 0.5in;"><h3 class="title">Note</h3><p>
Access control (such as allowing notifies) is not yet provided.
The primary/secondary service is not yet complete.
- </p></div></div><div class="chapter" title="Chapter 12. Recursive Name Server"><div class="titlepage"><div><div><h2 class="title"><a name="resolverserver"></a>Chapter 12. Recursive Name Server</h2></div></div></div><div class="toc"><p><b>Table of Contents</b></p><dl><dt><span class="section"><a href="#id1168230286296">Forwarding</a></span></dt></dl></div><p>
+ </p></div></div><div class="chapter" title="Chapter 12. Recursive Name Server"><div class="titlepage"><div><div><h2 class="title"><a name="resolverserver"></a>Chapter 12. Recursive Name Server</h2></div></div></div><div class="toc"><p><b>Table of Contents</b></p><dl><dt><span class="section"><a href="#id1168230286300">Forwarding</a></span></dt></dl></div><p>
The <span class="command"><strong>b10-resolver</strong></span> process is started by
<span class="command"><strong>bind10</strong></span>.
@@ -632,7 +636,7 @@ This may be a temporary setting until then.
> <strong class="userinput"><code>config set Resolver/listen_on [{ "address": "127.0.0.1", "port": 53 }]</code></strong>
> <strong class="userinput"><code>config commit</code></strong>
</pre><p>
- </p><div class="section" title="Forwarding"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="id1168230286296"></a>Forwarding</h2></div></div></div><p>
+ </p><div class="section" title="Forwarding"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="id1168230286300"></a>Forwarding</h2></div></div></div><p>
To enable forwarding, the upstream address and port must be
configured to forward queries to, such as:
diff --git a/doc/guide/bind10-guide.xml b/doc/guide/bind10-guide.xml
index c020f11..5eb4dc7 100644
--- a/doc/guide/bind10-guide.xml
+++ b/doc/guide/bind10-guide.xml
@@ -79,12 +79,24 @@
3.1 is the minimum version which will work.
</para>
- <note><para>
+ <para>
+ BIND 10 uses the Botan crypto library for C++. It requires
+ at least Botan version 1.8. To build BIND 10, install the
+ Botan libraries and development include headers.
+ </para>
+
+<!--
+TODO
+Debian and Ubuntu:
+ libgmp3-dev and libbz2-dev required for botan too
+-->
+
+ <para>
The authoritative server requires SQLite 3.3.9 or newer.
The <command>b10-xfrin</command>, <command>b10-xfrout</command>,
and <command>b10-zonemgr</command> modules require the
libpython3 library and the Python _sqlite3.so module.
- </para></note>
+ </para>
<!-- TODO: this will change ... -->
<!-- TODO: list where to get these from -->
diff --git a/src/bin/auth/tests/Makefile.am b/src/bin/auth/tests/Makefile.am
index 050373a..a4620f5 100644
--- a/src/bin/auth/tests/Makefile.am
+++ b/src/bin/auth/tests/Makefile.am
@@ -52,6 +52,7 @@ run_unittests_LDADD += $(top_builddir)/src/lib/xfr/libxfr.la
run_unittests_LDADD += $(top_builddir)/src/lib/log/liblog.la
run_unittests_LDADD += $(top_builddir)/src/lib/server_common/libserver_common.la
run_unittests_LDADD += $(top_builddir)/src/lib/nsas/libnsas.la
+run_unittests_LDADD += $(top_builddir)/src/lib/util/unittests/libutil_unittests.la
endif
noinst_PROGRAMS = $(TESTS)
diff --git a/src/bin/auth/tests/run_unittests.cc b/src/bin/auth/tests/run_unittests.cc
index 6ae848d..d4fc6fd 100644
--- a/src/bin/auth/tests/run_unittests.cc
+++ b/src/bin/auth/tests/run_unittests.cc
@@ -13,6 +13,7 @@
// PERFORMANCE OF THIS SOFTWARE.
#include <gtest/gtest.h>
+#include <util/unittests/run_all.h>
#include <dns/tests/unittest_util.h>
@@ -22,5 +23,5 @@ main(int argc, char* argv[]) {
isc::UnitTestUtil::addDataPath(TEST_DATA_DIR);
isc::UnitTestUtil::addDataPath(TEST_DATA_BUILDDIR);
- return (RUN_ALL_TESTS());
+ return (isc::util::unittests::run_all());
}
diff --git a/src/bin/msgq/tests/msgq_test.py b/src/bin/msgq/tests/msgq_test.py
index 26878f7..fe4f7d4 100644
--- a/src/bin/msgq/tests/msgq_test.py
+++ b/src/bin/msgq/tests/msgq_test.py
@@ -202,7 +202,7 @@ class SendNonblock(unittest.TestCase):
try:
def killall(signum, frame):
os.kill(queue_pid, signal.SIGTERM)
- sys.exit(1)
+ os._exit(1)
signal.signal(signal.SIGALRM, killall)
msg = msgq.preparemsg({"type" : "ping"}, data)
now = time.clock()
diff --git a/src/bin/resolver/tests/Makefile.am b/src/bin/resolver/tests/Makefile.am
index 22da0d2..3b5c197 100644
--- a/src/bin/resolver/tests/Makefile.am
+++ b/src/bin/resolver/tests/Makefile.am
@@ -47,6 +47,7 @@ run_unittests_LDADD += $(top_builddir)/src/lib/server_common/libserver_common.la
run_unittests_LDADD += $(top_builddir)/src/lib/cache/libcache.la
run_unittests_LDADD += $(top_builddir)/src/lib/nsas/libnsas.la
run_unittests_LDADD += $(top_builddir)/src/lib/resolve/libresolve.la
+run_unittests_LDADD += $(top_builddir)/src/lib/util/unittests/libutil_unittests.la
# Note the ordering matters: -Wno-... must follow -Wextra (defined in
# B10_CXXFLAGS
diff --git a/src/bin/resolver/tests/run_unittests.cc b/src/bin/resolver/tests/run_unittests.cc
index 6ae848d..d4fc6fd 100644
--- a/src/bin/resolver/tests/run_unittests.cc
+++ b/src/bin/resolver/tests/run_unittests.cc
@@ -13,6 +13,7 @@
// PERFORMANCE OF THIS SOFTWARE.
#include <gtest/gtest.h>
+#include <util/unittests/run_all.h>
#include <dns/tests/unittest_util.h>
@@ -22,5 +23,5 @@ main(int argc, char* argv[]) {
isc::UnitTestUtil::addDataPath(TEST_DATA_DIR);
isc::UnitTestUtil::addDataPath(TEST_DATA_BUILDDIR);
- return (RUN_ALL_TESTS());
+ return (isc::util::unittests::run_all());
}
diff --git a/src/bin/sockcreator/tests/Makefile.am b/src/bin/sockcreator/tests/Makefile.am
index 2e1307a..223e761 100644
--- a/src/bin/sockcreator/tests/Makefile.am
+++ b/src/bin/sockcreator/tests/Makefile.am
@@ -16,10 +16,9 @@ run_unittests_SOURCES += run_unittests.cc
run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
-run_unittests_LDADD = $(GTEST_LDADD)
+run_unittests_LDADD = $(GTEST_LDADD)
+run_unittests_LDADD += $(top_builddir)/src/lib/util/unittests/libutil_unittests.la
run_unittests_LDADD += $(top_builddir)/src/lib/util/io/libutil_io.la
-run_unittests_LDADD += \
- $(top_builddir)/src/lib/util/unittests/libutil_unittests.la
endif
noinst_PROGRAMS = $(TESTS)
diff --git a/src/bin/sockcreator/tests/run_unittests.cc b/src/bin/sockcreator/tests/run_unittests.cc
index e787ab1..1287164 100644
--- a/src/bin/sockcreator/tests/run_unittests.cc
+++ b/src/bin/sockcreator/tests/run_unittests.cc
@@ -13,10 +13,11 @@
// PERFORMANCE OF THIS SOFTWARE.
#include <gtest/gtest.h>
+#include <util/unittests/run_all.h>
int
main(int argc, char *argv[]) {
::testing::InitGoogleTest(&argc, argv);
- return RUN_ALL_TESTS();
+ return isc::util::unittests::run_all();
}
diff --git a/src/bin/xfrin/b10-xfrin.8 b/src/bin/xfrin/b10-xfrin.8
index d0723b5..3ea2293 100644
--- a/src/bin/xfrin/b10-xfrin.8
+++ b/src/bin/xfrin/b10-xfrin.8
@@ -2,12 +2,12 @@
.\" Title: b10-xfrin
.\" Author: [FIXME: author] [see http://docbook.sf.net/el/author]
.\" Generator: DocBook XSL Stylesheets v1.75.2 <http://docbook.sf.net/>
-.\" Date: September 8, 2010
+.\" Date: May 19, 2011
.\" Manual: BIND10
.\" Source: BIND10
.\" Language: English
.\"
-.TH "B10\-XFRIN" "8" "September 8, 2010" "BIND10" "BIND10"
+.TH "B10\-XFRIN" "8" "May 19, 2011" "BIND10" "BIND10"
.\" -----------------------------------------------------------------
.\" * set default formatting
.\" -----------------------------------------------------------------
@@ -43,7 +43,7 @@ boss process\&. When triggered it can request and receive a zone transfer and st
.ps -1
.br
.sp
-The Y1 prototype release only supports AXFR\&. IXFR is not implemented\&.
+This prototype release only supports AXFR\&. IXFR is not implemented\&.
.sp .5v
.RE
.PP
@@ -61,15 +61,34 @@ receives its configurations from
.PP
The configurable settings are:
.PP
-\fImaster_addr\fR
-The default is 127\&.0\&.0\&.1\&.
-.PP
-\fImaster_port\fR
-The default is 53\&.
-.PP
\fItransfers\-in\fR
defines the maximum number of inbound zone transfers that can run concurrently\&. The default is 10\&.
.PP
+
+\fIzones\fR
+is a list of zones known to the
+\fBb10\-xfrin\fR
+daemon\&. The list items are:
+\fIname\fR
+(the zone name),
+\fImaster_addr\fR
+(the zone master to transfer from),
+\fImaster_port\fR
+(defaults to 53), and
+\fItsig_key\fR
+(optional TSIG key to use)\&. The
+\fItsig_key\fR
+is specified using a full string colon\-delimited name:key:algorithm representation (e\&.g\&.
+\(lqfoo\&.example\&.org:EvABsfU2h7uofnmqaRCrhHunGsd=:hmac\-sha1\(rq)\&.
+.PP
+(The site\-wide
+\fImaster_addr\fR
+and
+\fImaster_port\fR
+configurations are deprecated; use the
+\fIzones\fR
+list configuration instead\&.)
+.PP
The configuration commands are:
.PP
@@ -106,7 +125,9 @@ to define the class (defaults to
\fImaster\fR
to define the IP address of the authoritative server to transfer from, and
\fIport\fR
-to define the port number on the authoritative server (defaults to 53)\&.
+to define the port number on the authoritative server (defaults to 53)\&. If the address or port is not specified, it will use the values previously defined in the
+\fIzones\fR
+configuration\&.
.PP
\fBshutdown\fR
@@ -143,5 +164,5 @@ The
daemon was implemented in March 2010 by Zhang Likun of CNNIC for the ISC BIND 10 project\&.
.SH "COPYRIGHT"
.br
-Copyright \(co 2010 Internet Systems Consortium, Inc. ("ISC")
+Copyright \(co 2010-2011 Internet Systems Consortium, Inc. ("ISC")
.br
diff --git a/src/bin/xfrin/b10-xfrin.xml b/src/bin/xfrin/b10-xfrin.xml
index fdfe1ef..ea4c724 100644
--- a/src/bin/xfrin/b10-xfrin.xml
+++ b/src/bin/xfrin/b10-xfrin.xml
@@ -2,7 +2,7 @@
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd"
[<!ENTITY mdash "—">]>
<!--
- - 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
@@ -20,7 +20,7 @@
<refentry>
<refentryinfo>
- <date>September 8, 2010</date>
+ <date>May 19, 2011</date>
</refentryinfo>
<refmeta>
@@ -36,7 +36,7 @@
<docinfo>
<copyright>
- <year>2010</year>
+ <year>2010-2011</year>
<holder>Internet Systems Consortium, Inc. ("ISC")</holder>
</copyright>
</docinfo>
@@ -62,6 +62,12 @@
the zone in a BIND 10 zone data store.
</para>
+<!-- TODO:
+xfrin only does the transfer to make it as simple as possible.
+The logic for handling transfer triggers or zone management is handled
+in separate zonemgr process.
+-->
+
<note><simpara>
This prototype release only supports AXFR. IXFR is not implemented.
</simpara></note>
@@ -86,20 +92,33 @@
The configurable settings are:
</para>
- <para><varname>master_addr</varname>
-<!-- TODO: how can there be a single setting for this? -->
- The default is 127.0.0.1.
+ <para><varname>transfers-in</varname>
+ defines the maximum number of inbound zone transfers
+ that can run concurrently. The default is 10.
</para>
- <para><varname>master_port</varname>
-<!-- TODO: what if custom is needed per zone? -->
- The default is 53.
+<!-- TODO: is name okay for master_addr or just IP? -->
+ <para>
+ <varname>zones</varname> is a list of zones known to the
+ <command>b10-xfrin</command> daemon.
+ The list items are:
+ <varname>name</varname> (the zone name),
+ <varname>master_addr</varname> (the zone master to transfer from),
+ <varname>master_port</varname> (defaults to 53), and
+ <varname>tsig_key</varname> (optional TSIG key to use).
+ The <varname>tsig_key</varname> is specified using a full string
+ colon-delimited name:key:algorithm representation (e.g.
+ <quote>foo.example.org:EvABsfU2h7uofnmqaRCrhHunGsd=:hmac-sha1</quote>).
</para>
+<!-- TODO: document this better -->
+<!-- TODO: the tsig_key format may change -->
- <para><varname>transfers-in</varname>
- defines the maximum number of inbound zone transfers
- that can run concurrently. The default is 10.
+ <para>
+ (The site-wide <varname>master_addr</varname> and
+ <varname>master_port</varname> configurations are deprecated;
+ use the <varname>zones</varname> list configuration instead.)
</para>
+<!-- NOTE: also tsig_key but not mentioning since so short lived. -->
<!-- TODO: formating -->
<para>
@@ -148,6 +167,9 @@
the authoritative server to transfer from,
and <varname>port</varname> to define the port number on the
authoritative server (defaults to 53).
+ If the address or port is not specified, it will use the
+ values previously defined in the <varname>zones</varname>
+ configuration.
</para>
<!-- TODO: later hostname for master? -->
diff --git a/src/bin/xfrin/tests/xfrin_test.py b/src/bin/xfrin/tests/xfrin_test.py
index 0ccbbb8..f3e2ee4 100644
--- a/src/bin/xfrin/tests/xfrin_test.py
+++ b/src/bin/xfrin/tests/xfrin_test.py
@@ -1,4 +1,4 @@
-# Copyright (C) 2009 Internet Systems Consortium.
+# Copyright (C) 2009-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
@@ -15,13 +15,16 @@
import unittest
import socket
+from isc.testutils.tsigctx_mock import MockTSIGContext
from xfrin import *
#
# Commonly used (mostly constant) test parameters
#
-TEST_ZONE_NAME = "example.com"
+TEST_ZONE_NAME_STR = "example.com."
+TEST_ZONE_NAME = Name(TEST_ZONE_NAME_STR)
TEST_RRCLASS = RRClass.IN()
+TEST_RRCLASS_STR = 'IN'
TEST_DB_FILE = 'db_file'
TEST_MASTER_IPV4_ADDRESS = '127.0.0.1'
TEST_MASTER_IPV4_ADDRINFO = (socket.AF_INET, socket.SOCK_STREAM,
@@ -40,12 +43,12 @@ TSIG_KEY = TSIGKey("example.com:SFuWd/q99SzF8Yzd1QbB9g==")
soa_rdata = Rdata(RRType.SOA(), TEST_RRCLASS,
'master.example.com. admin.example.com ' +
'1234 3600 1800 2419200 7200')
-soa_rrset = RRset(Name(TEST_ZONE_NAME), TEST_RRCLASS, RRType.SOA(),
+soa_rrset = RRset(TEST_ZONE_NAME, TEST_RRCLASS, RRType.SOA(),
RRTTL(3600))
soa_rrset.add_rdata(soa_rdata)
-example_axfr_question = Question(Name(TEST_ZONE_NAME), TEST_RRCLASS,
+example_axfr_question = Question(TEST_ZONE_NAME, TEST_RRCLASS,
RRType.AXFR())
-example_soa_question = Question(Name(TEST_ZONE_NAME), TEST_RRCLASS,
+example_soa_question = Question(TEST_ZONE_NAME, TEST_RRCLASS,
RRType.SOA())
default_questions = [example_axfr_question]
default_answers = [soa_rrset]
@@ -53,12 +56,12 @@ default_answers = [soa_rrset]
class XfrinTestException(Exception):
pass
-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 MockCC():
+ def get_default_value(self, identifier):
+ if identifier == "zones/master_port":
+ return TEST_MASTER_PORT
+ if identifier == "zones/class":
+ return TEST_RRCLASS_STR
class MockXfrin(Xfrin):
# This is a class attribute of a callable object that specifies a non
@@ -69,7 +72,8 @@ class MockXfrin(Xfrin):
check_command_hook = None
def _cc_setup(self):
- self._tsig_key_str = None
+ self._tsig_key = None
+ self._module_cc = MockCC()
pass
def _get_db_file(self):
@@ -80,6 +84,16 @@ class MockXfrin(Xfrin):
if MockXfrin.check_command_hook:
MockXfrin.check_command_hook()
+ def xfrin_start(self, zone_name, rrclass, db_file, master_addrinfo,
+ tsig_key, check_soa=True):
+ # store some of the arguments for verification, then call this
+ # method in the superclass
+ self.xfrin_started_master_addr = master_addrinfo[2][0]
+ self.xfrin_started_master_port = master_addrinfo[2][1]
+ return Xfrin.xfrin_start(self, zone_name, rrclass, db_file,
+ master_addrinfo, tsig_key,
+ check_soa)
+
class MockXfrinConnection(XfrinConnection):
def __init__(self, sock_map, zone_name, rrclass, db_file, shutdown_event,
master_addr):
@@ -131,10 +145,11 @@ class MockXfrinConnection(XfrinConnection):
self.response_generator()
return len(data)
- def create_response_data(self, response = True, bad_qid = False,
- rcode = Rcode.NOERROR(),
- questions = default_questions,
- answers = default_answers):
+ def create_response_data(self, response=True, bad_qid=False,
+ rcode=Rcode.NOERROR(),
+ questions=default_questions,
+ answers=default_answers,
+ tsig_ctx=None):
resp = Message(Message.RENDER)
qid = self.qid
if bad_qid:
@@ -148,7 +163,10 @@ class MockXfrinConnection(XfrinConnection):
[resp.add_rrset(Message.SECTION_ANSWER, a) for a in answers]
renderer = MessageRenderer()
- resp.to_wire(renderer)
+ if tsig_ctx is not None:
+ resp.to_wire(renderer, tsig_ctx)
+ else:
+ resp.to_wire(renderer)
reply_data = struct.pack('H', socket.htons(renderer.get_length()))
reply_data += renderer.get_data()
@@ -163,20 +181,32 @@ class TestXfrinConnection(unittest.TestCase):
TEST_RRCLASS, TEST_DB_FILE,
threading.Event(),
TEST_MASTER_IPV4_ADDRINFO)
- self.axfr_after_soa = False
self.soa_response_params = {
'questions': [example_soa_question],
'bad_qid': False,
'response': True,
'rcode': Rcode.NOERROR(),
+ 'tsig': False,
'axfr_after_soa': self._create_normal_response_data
}
+ self.axfr_response_params = {
+ 'tsig_1st': None,
+ 'tsig_2nd': None
+ }
def tearDown(self):
self.conn.close()
if os.path.exists(TEST_DB_FILE):
os.remove(TEST_DB_FILE)
+ def __create_mock_tsig(self, key, error):
+ # This helper function creates a MockTSIGContext for a given key
+ # and TSIG error to be used as a result of verify (normally faked
+ # one)
+ mock_ctx = MockTSIGContext(key)
+ mock_ctx.error = error
+ return mock_ctx
+
def test_close(self):
# we shouldn't be using the global asyncore map.
self.assertEqual(len(asyncore.socket_map), 0)
@@ -216,6 +246,15 @@ class TestXfrinConnection(unittest.TestCase):
query_question = Question(Name("example.com."), RRClass.IN(), query_type)
msg.add_question(query_question)
return msg
+
+ def message_has_tsig(data):
+ # a simple check if the actual data contains a TSIG RR.
+ # At our level this simple check should suffice; other detailed
+ # tests regarding the TSIG protocol are done in pydnspp.
+ msg = Message(Message.PARSE)
+ msg.from_wire(data)
+ return msg.get_tsig_record() is not None
+
self.conn._create_query = create_msg
# soa request
self.conn._send_query(RRType.SOA())
@@ -225,22 +264,20 @@ class TestXfrinConnection(unittest.TestCase):
self.assertEqual(self.conn.query_data, b'\x00\x1d\x105\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x07example\x03com\x00\x00\xfc\x00\x01')
# soa request with tsig
- self.conn._tsig_ctx = TSIGContext(TSIG_KEY)
+ self.conn._tsig_key = TSIG_KEY
self.conn._send_query(RRType.SOA())
- tsig_soa_data = strip_mutable_tsig_data(self.conn.query_data)
- self.assertEqual(tsig_soa_data, b'\x00n\x105\x00\x00\x00\x01\x00\x00\x00\x00\x00\x01\x07example\x03com\x00\x00\x06\x00\x01\x07example\x03com\x00\x00\xfa\x00\xff\x00\x00\x00\x00\x00:\x08hmac-md5\x07sig-alg\x03reg\x03int\x00\x01,\x00\x10\x105\x00\x00\x00\x00')
+ self.assertTrue(message_has_tsig(self.conn.query_data[2:]))
# axfr request with tsig
self.conn._send_query(RRType.AXFR())
- tsig_axfr_data = strip_mutable_tsig_data(self.conn.query_data)
- self.assertEqual(tsig_axfr_data, b'\x00n\x105\x00\x00\x00\x01\x00\x00\x00\x00\x00\x01\x07example\x03com\x00\x00\xfc\x00\x01\x07example\x03com\x00\x00\xfa\x00\xff\x00\x00\x00\x00\x00:\x08hmac-md5\x07sig-alg\x03reg\x03int\x00\x01,\x00\x10\x105\x00\x00\x00\x00')
+ self.assertTrue(message_has_tsig(self.conn.query_data[2:]))
def test_response_with_invalid_msg(self):
self.conn.reply_data = b'aaaxxxx'
self.assertRaises(XfrinTestException, self._handle_xfrin_response)
- def test_response_with_tsig(self):
- self.conn._tsig_ctx = TSIGContext(TSIG_KEY)
+ def test_response_with_tsigfail(self):
+ self.conn._tsig_key = TSIG_KEY
# server tsig check fail, return with RCODE 9 (NOTAUTH)
self.conn._send_query(RRType.SOA())
self.conn.reply_data = self.conn.create_response_data(rcode=Rcode.NOTAUTH())
@@ -310,6 +347,54 @@ class TestXfrinConnection(unittest.TestCase):
self.conn.response_generator = self._create_soa_response_data
self.assertRaises(XfrinException, self.conn._check_soa_serial)
+ def test_soacheck_with_tsig(self):
+ # Use a mock tsig context emulating a validly signed response
+ self.conn._tsig_key = TSIG_KEY
+ self.conn._tsig_ctx_creator = \
+ lambda key: self.__create_mock_tsig(key, TSIGError.NOERROR)
+ self.conn.response_generator = self._create_soa_response_data
+ self.assertEqual(self.conn._check_soa_serial(), XFRIN_OK)
+ self.assertEqual(self.conn._tsig_ctx.get_error(), TSIGError.NOERROR)
+
+ def test_soacheck_with_tsig_notauth(self):
+ # emulate a valid error response
+ self.conn._tsig_key = TSIG_KEY
+ self.conn._tsig_ctx_creator = \
+ lambda key: self.__create_mock_tsig(key, TSIGError.BAD_SIG)
+ self.soa_response_params['rcode'] = Rcode.NOTAUTH()
+ self.conn.response_generator = self._create_soa_response_data
+
+ self.assertRaises(XfrinException, self.conn._check_soa_serial)
+
+ def test_soacheck_with_tsig_noerror_badsig(self):
+ self.conn._tsig_key = TSIG_KEY
+ self.conn._tsig_ctx_creator = \
+ lambda key: self.__create_mock_tsig(key, TSIGError.BAD_SIG)
+
+ # emulate a normal response bad verification failure due to BADSIG.
+ # According RFC2845, in this case we should ignore it and keep
+ # waiting for a valid response until a timeout. But we immediately
+ # treat this as a final failure (just as BIND 9 does).
+ self.conn.response_generator = self._create_soa_response_data
+
+ self.assertRaises(XfrinException, self.conn._check_soa_serial)
+
+ def test_soacheck_with_tsig_unsigned_response(self):
+ # we can use a real TSIGContext for this. the response doesn't
+ # contain a TSIG while we sent a signed query. RFC2845 states
+ # we should wait for a valid response in this case, but we treat
+ # it as a fatal transaction failure, too.
+ self.conn._tsig_key = TSIG_KEY
+ self.conn.response_generator = self._create_soa_response_data
+ self.assertRaises(XfrinException, self.conn._check_soa_serial)
+
+ def test_soacheck_with_unexpected_tsig_response(self):
+ # we reject unexpected TSIG in responses (following BIND 9's
+ # behavior)
+ self.soa_response_params['tsig'] = True
+ self.conn.response_generator = self._create_soa_response_data
+ self.assertRaises(XfrinException, self.conn._check_soa_serial)
+
def test_response_shutdown(self):
self.conn.response_generator = self._create_normal_response_data
self.conn._shutdown_event.set()
@@ -343,6 +428,88 @@ class TestXfrinConnection(unittest.TestCase):
self.conn.response_generator = self._create_normal_response_data
self.assertEqual(self.conn.do_xfrin(False), XFRIN_OK)
+ def test_do_xfrin_with_tsig(self):
+ # use TSIG with a mock context. we fake all verify results to
+ # emulate successful verification.
+ self.conn._tsig_key = TSIG_KEY
+ self.conn._tsig_ctx_creator = \
+ lambda key: self.__create_mock_tsig(key, TSIGError.NOERROR)
+ self.conn.response_generator = self._create_normal_response_data
+ self.assertEqual(self.conn.do_xfrin(False), XFRIN_OK)
+ # We use two messages in the tests. The same context should have been
+ # usef for both.
+ self.assertEqual(2, self.conn._tsig_ctx.verify_called)
+
+ def test_do_xfrin_with_tsig_fail(self):
+ # TSIG verify will fail for the first message. xfrin should fail
+ # immediately.
+ self.conn._tsig_key = TSIG_KEY
+ self.conn._tsig_ctx_creator = \
+ lambda key: self.__create_mock_tsig(key, TSIGError.BAD_SIG)
+ self.conn.response_generator = self._create_normal_response_data
+ self.assertEqual(self.conn.do_xfrin(False), XFRIN_FAIL)
+ self.assertEqual(1, self.conn._tsig_ctx.verify_called)
+
+ def test_do_xfrin_with_tsig_fail_for_second_message(self):
+ # Similar to the previous test, but first verify succeeds. There
+ # should be a second verify attempt, which will fail, which should
+ # make xfrin fail.
+ def fake_tsig_error(ctx):
+ if self.conn._tsig_ctx.verify_called == 1:
+ return TSIGError.NOERROR
+ return TSIGError.BAD_SIG
+ self.conn._tsig_key = TSIG_KEY
+ self.conn._tsig_ctx_creator = \
+ lambda key: self.__create_mock_tsig(key, fake_tsig_error)
+ self.conn.response_generator = self._create_normal_response_data
+ self.assertEqual(self.conn.do_xfrin(False), XFRIN_FAIL)
+ self.assertEqual(2, self.conn._tsig_ctx.verify_called)
+
+ def test_do_xfrin_with_missing_tsig(self):
+ # XFR request sent with TSIG, but the response doesn't have TSIG.
+ # xfr should fail.
+ self.conn._tsig_key = TSIG_KEY
+ self.conn._tsig_ctx_creator = \
+ lambda key: self.__create_mock_tsig(key, None)
+ self.conn._tsig_ctx = MockTSIGContext(TSIG_KEY)
+ self.conn.response_generator = self._create_normal_response_data
+ self.assertEqual(self.conn.do_xfrin(False), XFRIN_FAIL)
+ self.assertEqual(1, self.conn._tsig_ctx.verify_called)
+
+ def test_do_xfrin_with_missing_tsig_for_second_message(self):
+ # Similar to the previous test, but firt one contains TSIG and verify
+ # succeeds (due to fake). The second message lacks TSIG.
+ #
+ # Note: this test case is actually not that trivial: Skipping
+ # intermediate TSIG is allowed. In this case, however, the second
+ # message is the last one, which must contain TSIG anyway, so the
+ # expected result is correct. If/when we support skipping
+ # intermediate TSIGs, we'll need additional test cases.
+ def fake_tsig_error(ctx):
+ if self.conn._tsig_ctx.verify_called == 1:
+ return TSIGError.NOERROR
+ return TSIGError.FORMERR
+ self.conn._tsig_key = TSIG_KEY
+ self.conn._tsig_ctx_creator = \
+ lambda key: self.__create_mock_tsig(key, fake_tsig_error)
+ self.conn.response_generator = self._create_normal_response_data
+ self.assertEqual(self.conn.do_xfrin(False), XFRIN_FAIL)
+ self.assertEqual(2, self.conn._tsig_ctx.verify_called)
+
+ def test_do_xfrin_with_unexpected_tsig(self):
+ # XFR request wasn't signed, but response includes TSIG. Like BIND 9,
+ # we reject that.
+ self.axfr_response_params['tsig_1st'] = TSIGContext(TSIG_KEY)
+ self.conn.response_generator = self._create_normal_response_data
+ self.assertEqual(self.conn.do_xfrin(False), XFRIN_FAIL)
+
+ def test_do_xfrin_with_unexpected_tsig_for_second_message(self):
+ # similar to the previous test, but the first message is normal.
+ # the second one contains an unexpected TSIG. should be rejected.
+ self.axfr_response_params['tsig_2nd'] = TSIGContext(TSIG_KEY)
+ self.conn.response_generator = self._create_normal_response_data
+ self.assertEqual(self.conn.do_xfrin(False), XFRIN_FAIL)
+
def test_do_xfrin_empty_response(self):
# skipping the creation of response data, so the transfer will fail.
self.assertEqual(self.conn.do_xfrin(False), XFRIN_FAIL)
@@ -361,6 +528,23 @@ class TestXfrinConnection(unittest.TestCase):
self.conn.response_generator = self._create_soa_response_data
self.assertEqual(self.conn.do_xfrin(True), XFRIN_OK)
+ def test_do_soacheck_and_xfrin_with_tsig(self):
+ # We are going to have a SOA query/response transaction, followed by
+ # AXFR, all TSIG signed. xfrin should use a new TSIG context for
+ # AXFR. We are not interested in whether verify works correctly in
+ # this test, so we simply fake the results (they need to succeed for
+ # this test)
+ self.conn._tsig_key = TSIG_KEY
+ self.conn._tsig_ctx_creator = \
+ lambda key: self.__create_mock_tsig(key, TSIGError.NOERROR)
+ self.soa_response_params['tsig'] = True
+ self.conn.response_generator = self._create_soa_response_data
+ self.assertEqual(self.conn.do_xfrin(True), XFRIN_OK)
+ # We should've got 3 response messages: 1 SOA and two AXFR, but
+ # the context should be replaced for AXFR, so verify() should be
+ # called only twice for the latest context.
+ self.assertEqual(2, self.conn._tsig_ctx.verify_called)
+
def test_do_soacheck_broken_response(self):
self.conn.response_generator = self._create_broken_response_data
# XXX: TODO: this test failed here, should xfr not raise an
@@ -388,21 +572,39 @@ class TestXfrinConnection(unittest.TestCase):
# This helper method creates a simple sequence of DNS messages that
# forms a valid XFR transaction. It consists of two messages, each
# containing just a single SOA RR.
- self.conn.reply_data = self.conn.create_response_data()
- self.conn.reply_data += self.conn.create_response_data()
+ tsig_1st = self.axfr_response_params['tsig_1st']
+ tsig_2nd = self.axfr_response_params['tsig_2nd']
+ self.conn.reply_data = self.conn.create_response_data(tsig_ctx=tsig_1st)
+ self.conn.reply_data += \
+ self.conn.create_response_data(tsig_ctx=tsig_2nd)
def _create_soa_response_data(self):
# This helper method creates a DNS message that is supposed to be
# used a valid response to SOA queries prior to XFR.
+ # If tsig is True, it tries to verify the query with a locally
+ # created TSIG context (which may or may not succeed) so that the
+ # response will include a TSIG.
# If axfr_after_soa is True, it resets the response_generator so that
# a valid XFR messages will follow.
+
+ verify_ctx = None
+ if self.soa_response_params['tsig']:
+ # xfrin (curreently) always uses TCP. strip off the length field.
+ query_data = self.conn.query_data[2:]
+ query_message = Message(Message.PARSE)
+ query_message.from_wire(query_data)
+ verify_ctx = TSIGContext(TSIG_KEY)
+ verify_ctx.verify(query_message.get_tsig_record(), query_data)
+
self.conn.reply_data = self.conn.create_response_data(
bad_qid=self.soa_response_params['bad_qid'],
response=self.soa_response_params['response'],
rcode=self.soa_response_params['rcode'],
- questions=self.soa_response_params['questions'])
+ questions=self.soa_response_params['questions'],
+ tsig_ctx=verify_ctx)
if self.soa_response_params['axfr_after_soa'] != None:
- self.conn.response_generator = self.soa_response_params['axfr_after_soa']
+ self.conn.response_generator = \
+ self.soa_response_params['axfr_after_soa']
def _create_broken_response_data(self):
# This helper method creates a bogus "DNS message" that only contains
@@ -450,7 +652,8 @@ class TestXfrin(unittest.TestCase):
sys.stderr = open(os.devnull, 'w')
self.xfr = MockXfrin()
self.args = {}
- self.args['zone_name'] = TEST_ZONE_NAME
+ self.args['zone_name'] = TEST_ZONE_NAME_STR
+ self.args['class'] = TEST_RRCLASS_STR
self.args['port'] = TEST_MASTER_PORT
self.args['master'] = TEST_MASTER_IPV4_ADDRESS
self.args['db_file'] = TEST_DB_FILE
@@ -464,7 +667,8 @@ class TestXfrin(unittest.TestCase):
return self.xfr._parse_zone_name_and_class(self.args)
def _do_parse_master_port(self):
- return self.xfr._parse_master_and_port(self.args)
+ name, rrclass = self._do_parse_zone_name_class()
+ return self.xfr._parse_master_and_port(self.args, name, rrclass)
def test_parse_cmd_params(self):
name, rrclass = self._do_parse_zone_name_class()
@@ -492,7 +696,7 @@ class TestXfrin(unittest.TestCase):
def test_parse_cmd_params_bogusclass(self):
self.args['zone_class'] = 'XXX'
- self.assertRaises(XfrinException, self._do_parse_zone_name_class)
+ self.assertRaises(XfrinZoneInfoException, self._do_parse_zone_name_class)
def test_parse_cmd_params_nozone(self):
# zone name is mandatory.
@@ -502,8 +706,7 @@ class TestXfrin(unittest.TestCase):
def test_parse_cmd_params_nomaster(self):
# master address is mandatory.
del self.args['master']
- master_addrinfo = self._do_parse_master_port()
- self.assertEqual(master_addrinfo[2][0], DEFAULT_MASTER)
+ self.assertRaises(XfrinException, self._do_parse_master_port)
def test_parse_cmd_params_bad_ip4(self):
self.args['master'] = '3.3.3.3.3'
@@ -533,6 +736,77 @@ class TestXfrin(unittest.TestCase):
def test_command_handler_retransfer(self):
self.assertEqual(self.xfr.command_handler("retransfer",
self.args)['result'][0], 0)
+ self.assertEqual(self.args['master'], self.xfr.xfrin_started_master_addr)
+ self.assertEqual(int(self.args['port']), self.xfr.xfrin_started_master_port)
+
+ def test_command_handler_retransfer_short_command1(self):
+ # try it when only specifying the zone name (of unknown zone)
+ # this should fail because master address is not specified.
+ short_args = {}
+ short_args['zone_name'] = TEST_ZONE_NAME_STR
+ self.assertEqual(self.xfr.command_handler("retransfer",
+ short_args)['result'][0], 1)
+
+ def test_command_handler_retransfer_short_command2(self):
+ # try it when only specifying the zone name (of known zone)
+ short_args = {}
+ short_args['zone_name'] = TEST_ZONE_NAME_STR
+
+ zones = { 'zones': [
+ { 'name': TEST_ZONE_NAME_STR,
+ 'master_addr': TEST_MASTER_IPV4_ADDRESS,
+ 'master_port': TEST_MASTER_PORT
+ }
+ ]}
+ self.xfr.config_handler(zones)
+ self.assertEqual(self.xfr.command_handler("retransfer",
+ short_args)['result'][0], 0)
+ self.assertEqual(TEST_MASTER_IPV4_ADDRESS,
+ self.xfr.xfrin_started_master_addr)
+ self.assertEqual(int(TEST_MASTER_PORT),
+ self.xfr.xfrin_started_master_port)
+
+ def test_command_handler_retransfer_short_command3(self):
+ # try it when only specifying the zone name (of known zone)
+ short_args = {}
+ # test it without the trailing root dot
+ short_args['zone_name'] = TEST_ZONE_NAME_STR[:-1]
+
+ zones = { 'zones': [
+ { 'name': TEST_ZONE_NAME_STR,
+ 'master_addr': TEST_MASTER_IPV4_ADDRESS,
+ 'master_port': TEST_MASTER_PORT
+ }
+ ]}
+ self.xfr.config_handler(zones)
+ self.assertEqual(self.xfr.command_handler("retransfer",
+ short_args)['result'][0], 0)
+ self.assertEqual(TEST_MASTER_IPV4_ADDRESS,
+ self.xfr.xfrin_started_master_addr)
+ self.assertEqual(int(TEST_MASTER_PORT),
+ self.xfr.xfrin_started_master_port)
+
+ def test_command_handler_retransfer_short_command4(self):
+ # try it when only specifying the zone name (of known zone, with
+ # different case)
+ short_args = {}
+
+ # swap the case of the zone name in our command
+ short_args['zone_name'] = TEST_ZONE_NAME_STR.swapcase()
+
+ zones = { 'zones': [
+ { 'name': TEST_ZONE_NAME_STR,
+ 'master_addr': TEST_MASTER_IPV4_ADDRESS,
+ 'master_port': TEST_MASTER_PORT
+ }
+ ]}
+ self.xfr.config_handler(zones)
+ self.assertEqual(self.xfr.command_handler("retransfer",
+ short_args)['result'][0], 0)
+ self.assertEqual(TEST_MASTER_IPV4_ADDRESS,
+ self.xfr.xfrin_started_master_addr)
+ self.assertEqual(int(TEST_MASTER_PORT),
+ self.xfr.xfrin_started_master_port)
def test_command_handler_retransfer_badcommand(self):
self.args['master'] = 'invalid'
@@ -540,13 +814,15 @@ class TestXfrin(unittest.TestCase):
self.args)['result'][0], 1)
def test_command_handler_retransfer_quota(self):
+ self.args['master'] = TEST_MASTER_IPV4_ADDRESS
+
for i in range(self.xfr._max_transfers_in - 1):
- self.xfr.recorder.increment(str(i) + TEST_ZONE_NAME)
+ self.xfr.recorder.increment(Name(str(i) + TEST_ZONE_NAME_STR))
# there can be one more outstanding transfer.
self.assertEqual(self.xfr.command_handler("retransfer",
self.args)['result'][0], 0)
# make sure the # xfrs would excceed the quota
- self.xfr.recorder.increment(str(self.xfr._max_transfers_in) + TEST_ZONE_NAME)
+ self.xfr.recorder.increment(Name(str(self.xfr._max_transfers_in) + TEST_ZONE_NAME_STR))
# this one should fail
self.assertEqual(self.xfr.command_handler("retransfer",
self.args)['result'][0], 1)
@@ -570,14 +846,43 @@ class TestXfrin(unittest.TestCase):
self.args['master'] = TEST_MASTER_IPV6_ADDRESS
self.assertEqual(self.xfr.command_handler("refresh",
self.args)['result'][0], 0)
+ self.assertEqual(TEST_MASTER_IPV6_ADDRESS,
+ self.xfr.xfrin_started_master_addr)
+ self.assertEqual(int(TEST_MASTER_PORT),
+ self.xfr.xfrin_started_master_port)
def test_command_handler_notify(self):
# at this level, refresh is no different than retransfer.
self.args['master'] = TEST_MASTER_IPV6_ADDRESS
- # ...but right now we disable the feature due to security concerns.
+ # ...but the zone is unknown so this would return an error
+ self.assertEqual(self.xfr.command_handler("notify",
+ self.args)['result'][0], 1)
+
+ def test_command_handler_notify_known_zone(self):
+ # try it with a known zone
+ self.args['master'] = TEST_MASTER_IPV6_ADDRESS
+
+ # but use a different address in the actual command
+ zones = { 'zones': [
+ { 'name': TEST_ZONE_NAME_STR,
+ 'master_addr': TEST_MASTER_IPV4_ADDRESS,
+ 'master_port': TEST_MASTER_PORT
+ }
+ ]}
+ self.xfr.config_handler(zones)
self.assertEqual(self.xfr.command_handler("notify",
self.args)['result'][0], 0)
+ # and see if we used the address from the command, and not from
+ # the config
+ # This is actually NOT the address given in the command, which
+ # would at this point not make sense, see the TODO in
+ # xfrin.py.in Xfrin.command_handler())
+ self.assertEqual(TEST_MASTER_IPV4_ADDRESS,
+ self.xfr.xfrin_started_master_addr)
+ self.assertEqual(int(TEST_MASTER_PORT),
+ self.xfr.xfrin_started_master_port)
+
def test_command_handler_unknown(self):
self.assertEqual(self.xfr.command_handler("xxx", None)['result'][0], 1)
@@ -586,20 +891,145 @@ class TestXfrin(unittest.TestCase):
self.assertEqual(self.xfr.config_handler({'transfers_in': 3})['result'][0], 0)
self.assertEqual(self.xfr._max_transfers_in, 3)
- def test_command_handler_masters(self):
- master_info = {'master_addr': '1.1.1.1', 'master_port':53}
- self.assertEqual(self.xfr.config_handler(master_info)['result'][0], 0)
-
- master_info = {'master_addr': '1111.1.1.1', 'master_port':53 }
- self.assertEqual(self.xfr.config_handler(master_info)['result'][0], 1)
-
- master_info = {'master_addr': '2.2.2.2', 'master_port':530000 }
- self.assertEqual(self.xfr.config_handler(master_info)['result'][0], 1)
-
- master_info = {'master_addr': '2.2.2.2', 'master_port':53 }
- self.xfr.config_handler(master_info)
- self.assertEqual(self.xfr._master_addr, '2.2.2.2')
- self.assertEqual(self.xfr._master_port, 53)
+ def _check_zones_config(self, config_given):
+ if 'transfers_in' in config_given:
+ self.assertEqual(config_given['transfers_in'],
+ self.xfr._max_transfers_in)
+ for zone_config in config_given['zones']:
+ zone_name = zone_config['name']
+ zone_info = self.xfr._get_zone_info(Name(zone_name), RRClass.IN())
+ self.assertEqual(str(zone_info.master_addr), zone_config['master_addr'])
+ self.assertEqual(zone_info.master_port, zone_config['master_port'])
+ if 'tsig_key' in zone_config:
+ self.assertEqual(zone_info.tsig_key.to_text(), TSIGKey(zone_config['tsig_key']).to_text())
+ else:
+ self.assertIsNone(zone_info.tsig_key)
+
+ def test_command_handler_zones(self):
+ config1 = { 'transfers_in': 3,
+ 'zones': [
+ { 'name': 'test.example.',
+ 'master_addr': '192.0.2.1',
+ 'master_port': 53
+ }
+ ]}
+ self.assertEqual(self.xfr.config_handler(config1)['result'][0], 0)
+ self._check_zones_config(config1)
+
+ config2 = { 'transfers_in': 4,
+ 'zones': [
+ { 'name': 'test.example.',
+ 'master_addr': '192.0.2.2',
+ 'master_port': 53,
+ 'tsig_key': "example.com:SFuWd/q99SzF8Yzd1QbB9g=="
+ }
+ ]}
+ self.assertEqual(self.xfr.config_handler(config2)['result'][0], 0)
+ self._check_zones_config(config2)
+
+ # test that configuring the zone multiple times fails
+ zones = { 'transfers_in': 5,
+ 'zones': [
+ { 'name': 'test.example.',
+ 'master_addr': '192.0.2.1',
+ 'master_port': 53
+ },
+ { 'name': 'test.example.',
+ 'master_addr': '192.0.2.2',
+ 'master_port': 53
+ }
+ ]}
+ self.assertEqual(self.xfr.config_handler(zones)['result'][0], 1)
+ # since this has failed, we should still have the previous config
+ self._check_zones_config(config2)
+
+ zones = { 'zones': [
+ { 'name': 'test.example.',
+ 'master_addr': '192.0.2.3',
+ 'master_port': 53,
+ 'class': 'BADCLASS'
+ }
+ ]}
+ self.assertEqual(self.xfr.config_handler(zones)['result'][0], 1)
+ self._check_zones_config(config2)
+
+ zones = { 'zones': [
+ { 'master_addr': '192.0.2.4',
+ 'master_port': 53
+ }
+ ]}
+ self.assertEqual(self.xfr.config_handler(zones)['result'][0], 1)
+ # since this has failed, we should still have the previous config
+ self._check_zones_config(config2)
+
+ zones = { 'zones': [
+ { 'name': 'bad..zone.',
+ 'master_addr': '192.0.2.5',
+ 'master_port': 53
+ }
+ ]}
+ self.assertEqual(self.xfr.config_handler(zones)['result'][0], 1)
+ # since this has failed, we should still have the previous config
+ self._check_zones_config(config2)
+
+ zones = { 'zones': [
+ { 'name': '',
+ 'master_addr': '192.0.2.6',
+ 'master_port': 53
+ }
+ ]}
+ self.assertEqual(self.xfr.config_handler(zones)['result'][0], 1)
+ # since this has failed, we should still have the previous config
+ self._check_zones_config(config2)
+
+ zones = { 'zones': [
+ { 'name': 'test.example',
+ 'master_addr': 'badaddress',
+ 'master_port': 53
+ }
+ ]}
+ self.assertEqual(self.xfr.config_handler(zones)['result'][0], 1)
+ # since this has failed, we should still have the previous config
+ self._check_zones_config(config2)
+
+ zones = { 'zones': [
+ { 'name': 'test.example',
+ 'master_addr': '192.0.2.7',
+ 'master_port': 'bad_port'
+ }
+ ]}
+ self.assertEqual(self.xfr.config_handler(zones)['result'][0], 1)
+ # since this has failed, we should still have the previous config
+ self._check_zones_config(config2)
+
+ zones = { 'zones': [
+ { 'name': 'test.example',
+ 'master_addr': '192.0.2.7',
+ 'master_port': 53,
+ # using a bad TSIG key spec
+ 'tsig_key': "bad..example.com:SFuWd/q99SzF8Yzd1QbB9g=="
+ }
+ ]}
+ self.assertEqual(self.xfr.config_handler(zones)['result'][0], 1)
+ # since this has failed, we should still have the previous config
+ self._check_zones_config(config2)
+
+ # let's also add a zone that is correct too, and make sure
+ # that the new config is not partially taken
+ zones = { 'zones': [
+ { 'name': 'test.example.',
+ 'master_addr': '192.0.2.8',
+ 'master_port': 53
+ },
+ { 'name': 'test2.example.',
+ 'master_addr': '192.0.2.9',
+ 'master_port': 53,
+ 'tsig_key': 'badkey'
+ }
+ ]}
+ self.assertEqual(self.xfr.config_handler(zones)['result'][0], 1)
+ # since this has failed, we should still have the previous config
+ self._check_zones_config(config2)
def raise_interrupt():
diff --git a/src/bin/xfrin/xfrin.py.in b/src/bin/xfrin/xfrin.py.in
index 1bf46c1..7758a37 100755
--- a/src/bin/xfrin/xfrin.py.in
+++ b/src/bin/xfrin/xfrin.py.in
@@ -1,6 +1,6 @@
#!@PYTHON@
-# Copyright (C) 2010 Internet Systems Consortium.
+# Copyright (C) 2009-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
@@ -56,26 +56,66 @@ XFROUT_MODULE_NAME = 'Xfrout'
ZONE_MANAGER_MODULE_NAME = 'Zonemgr'
REFRESH_FROM_ZONEMGR = 'refresh_from_zonemgr'
ZONE_XFRIN_FAILED = 'zone_xfrin_failed'
+
+# These two default are currently hard-coded. For config this isn't
+# necessary, but we need these defaults for optional command arguments
+# (TODO: have similar support to get default values for command
+# arguments as we do for config options)
+DEFAULT_MASTER_PORT = 53
+DEFAULT_ZONE_CLASS = RRClass.IN()
+
__version__ = 'BIND10'
# define xfrin rcode
XFRIN_OK = 0
XFRIN_FAIL = 1
-DEFAULT_MASTER_PORT = '53'
-DEFAULT_MASTER = '127.0.0.1'
-
def log_error(msg):
sys.stderr.write("[b10-xfrin] %s\n" % str(msg))
class XfrinException(Exception):
pass
+class XfrinZoneInfoException(Exception):
+ """This exception is raised if there is an error in the given
+ configuration (part), or when a command does not have a required
+ argument or has bad arguments, for instance when the zone's master
+ address is not a valid IP address, when the zone does not
+ have a name, or when multiple settings are given for the same
+ zone."""
+ pass
+
+def _check_zone_name(zone_name_str):
+ """Checks if the given zone name is a valid domain name, and returns
+ it as a Name object. Raises an XfrinException if it is not."""
+ try:
+ # In the _zones dict, part of the key is the zone name,
+ # but due to a limitation in the Name class, we
+ # cannot directly use it as a dict key, and we use to_text()
+ #
+ # Downcase the name here for that reason.
+ return Name(zone_name_str, True)
+ except (EmptyLabel, TooLongLabel, BadLabelType, BadEscape,
+ TooLongName, IncompleteName) as ne:
+ raise XfrinZoneInfoException("bad zone name: " + zone_name_str + " (" + str(ne) + ")")
+
+def _check_zone_class(zone_class_str):
+ """If the given argument is a string: checks if the given class is
+ a valid one, and returns an RRClass object if so.
+ Raises XfrinZoneInfoException if not.
+ If it is None, this function returns the default RRClass.IN()"""
+ if zone_class_str is None:
+ return DEFAULT_ZONE_CLASS
+ try:
+ return RRClass(zone_class_str)
+ except InvalidRRClass as irce:
+ raise XfrinZoneInfoException("bad zone class: " + zone_class_str + " (" + str(irce) + ")")
+
class XfrinConnection(asyncore.dispatcher):
'''Do xfrin in this class. '''
def __init__(self,
sock_map, zone_name, rrclass, db_file, shutdown_event,
- master_addrinfo, tsig_key_str = None, verbose = False,
+ master_addrinfo, tsig_key = None, verbose = False,
idle_timeout = 60):
''' idle_timeout: max idle time for read data from socket.
db_file: specify the data source file.
@@ -94,9 +134,14 @@ class XfrinConnection(asyncore.dispatcher):
self._shutdown_event = shutdown_event
self._verbose = verbose
self._master_address = master_addrinfo[2]
+ self._tsig_key = tsig_key
self._tsig_ctx = None
- if tsig_key_str is not None:
- self._tsig_ctx = TSIGContext(TSIGKey(tsig_key_str))
+ # tsig_ctx_creator is introduced to allow tests to use a mock class for
+ # easier tests (in normal case we always use the default)
+ self._tsig_ctx_creator = self.__create_tsig_ctx
+
+ def __create_tsig_ctx(self, key):
+ return TSIGContext(key)
def connect_to_master(self):
'''Connect to master in TCP.'''
@@ -136,7 +181,8 @@ class XfrinConnection(asyncore.dispatcher):
render = MessageRenderer()
# XXX Currently, python wrapper doesn't accept 'None' parameter in this case,
# we should remove the if statement and use a universal interface later.
- if self._tsig_ctx is not None:
+ if self._tsig_key is not None:
+ self._tsig_ctx = self._tsig_ctx_creator(self._tsig_key)
msg.to_wire(render, self._tsig_ctx)
else:
msg.to_wire(render)
@@ -167,6 +213,22 @@ class XfrinConnection(asyncore.dispatcher):
return data
+ def _check_response_tsig(self, msg, response_data):
+ tsig_record = msg.get_tsig_record()
+ if self._tsig_ctx is not None:
+ tsig_error = self._tsig_ctx.verify(tsig_record, response_data)
+ if tsig_error != TSIGError.NOERROR:
+ raise XfrinException('TSIG verify fail: %s' % str(tsig_error))
+ elif tsig_record is not None:
+ # If the response includes a TSIG while we didn't sign the query,
+ # we treat it as an error. RFC doesn't say anything about this
+ # case, but it clearly states the server must not sign a response
+ # to an unsigned request. Although we could be flexible, no sane
+ # implementation would return such a response, and since this is
+ # part of security mechanism, it's probably better to be more
+ # strict.
+ raise XfrinException('Unexpected TSIG in response')
+
def _check_soa_serial(self):
''' Compare the soa serial, if soa serial in master is less than
the soa serial in local, Finish xfrin.
@@ -174,7 +236,7 @@ class XfrinConnection(asyncore.dispatcher):
True: soa serial in master is bigger
'''
- self._send_query(RRType("SOA"))
+ self._send_query(RRType.SOA())
data_len = self._get_request_response(2)
msg_len = socket.htons(struct.unpack('H', data_len)[0])
soa_response = self._get_request_response(msg_len)
@@ -185,6 +247,9 @@ class XfrinConnection(asyncore.dispatcher):
# strict we should be (see the comment in _check_response_header())
self._check_response_header(msg)
+ # TSIG related checks, including an unexpected signed response
+ self._check_response_tsig(msg, soa_response)
+
# TODO, need select soa record from data source then compare the two
# serial, current just return OK, since this function hasn't been used
# now.
@@ -202,8 +267,7 @@ class XfrinConnection(asyncore.dispatcher):
logstr = 'transfer of \'%s\': AXFR ' % self._zone_name
if ret == XFRIN_OK:
self.log_msg(logstr + 'started')
- # TODO: .AXFR() RRType.AXFR()
- self._send_query(RRType(252))
+ self._send_query(RRType.AXFR())
isc.datasrc.sqlite3_ds.load(self._db_file, self._zone_name,
self._handle_xfrin_response)
@@ -274,7 +338,7 @@ class XfrinConnection(asyncore.dispatcher):
for rdata in rrset.get_rdata():
# Count the soa record count
- if rrset.get_type() == RRType("SOA"):
+ if rrset.get_type() == RRType.SOA():
self._soa_rr_count += 1
# XXX: the current DNS message parser can't preserve the
@@ -300,6 +364,9 @@ class XfrinConnection(asyncore.dispatcher):
msg.from_wire(recvdata)
self._check_response_status(msg)
+ # TSIG related checks, including an unexpected signed response
+ self._check_response_tsig(msg, recvdata)
+
answer_section = msg.get_section(Message.SECTION_ANSWER)
for rr in self._handle_answer_section(answer_section):
yield rr
@@ -333,12 +400,12 @@ class XfrinConnection(asyncore.dispatcher):
def process_xfrin(server, xfrin_recorder, zone_name, rrclass, db_file,
shutdown_event, master_addrinfo, check_soa, verbose,
- tsig_key_str):
+ tsig_key):
xfrin_recorder.increment(zone_name)
sock_map = {}
conn = XfrinConnection(sock_map, zone_name, rrclass, db_file,
shutdown_event, master_addrinfo,
- tsig_key_str, verbose)
+ tsig_key, verbose)
ret = XFRIN_FAIL
if conn.connect_to_master():
ret = conn.do_xfrin(check_soa)
@@ -378,12 +445,100 @@ class XfrinRecorder:
self._lock.release()
return ret
+class ZoneInfo:
+ def __init__(self, config_data, module_cc):
+ """Creates a zone_info with the config data element as
+ specified by the 'zones' list in xfrin.spec. Module_cc is
+ needed to get the defaults from the specification"""
+ self._module_cc = module_cc
+ self.set_name(config_data.get('name'))
+ self.set_master_addr(config_data.get('master_addr'))
+
+ self.set_master_port(config_data.get('master_port'))
+ self.set_zone_class(config_data.get('class'))
+ self.set_tsig_key(config_data.get('tsig_key'))
+
+ def set_name(self, name_str):
+ """Set the name for this zone given a name string.
+ Raises XfrinZoneInfoException if name_str is None or if it
+ cannot be parsed."""
+ if name_str is None:
+ raise XfrinZoneInfoException("Configuration zones list "
+ "element does not contain "
+ "'name' attribute")
+ else:
+ self.name = _check_zone_name(name_str)
+
+ def set_master_addr(self, master_addr_str):
+ """Set the master address for this zone given an IP address
+ string. Raises XfrinZoneInfoException if master_addr_str is
+ None or if it cannot be parsed."""
+ if master_addr_str is None:
+ raise XfrinZoneInfoException("master address missing from config data")
+ else:
+ try:
+ self.master_addr = isc.net.parse.addr_parse(master_addr_str)
+ except ValueError:
+ errmsg = "bad format for zone's master: " + master_addr_str
+ log_error(errmsg)
+ raise XfrinZoneInfoException(errmsg)
+
+ def set_master_port(self, master_port_str):
+ """Set the master port given a port number string. If
+ master_port_str is None, the default from the specification
+ for this module will be used. Raises XfrinZoneInfoException if
+ the string contains an invalid port number"""
+ if master_port_str is None:
+ self.master_port = self._module_cc.get_default_value("zones/master_port")
+ else:
+ try:
+ self.master_port = isc.net.parse.port_parse(master_port_str)
+ except ValueError:
+ errmsg = "bad format for zone's master port: " + master_port_str
+ log_error(errmsg)
+ raise XfrinZoneInfoException(errmsg)
+
+ def set_zone_class(self, zone_class_str):
+ """Set the zone class given an RR class str (e.g. "IN"). If
+ zone_class_str is None, it will default to what is specified
+ in the specification file for this module. Raises
+ XfrinZoneInfoException if the string cannot be parsed."""
+ # TODO: remove _str
+ self.class_str = zone_class_str or self._module_cc.get_default_value("zones/class")
+ if zone_class_str == None:
+ #TODO rrclass->zone_class
+ self.rrclass = RRClass(self._module_cc.get_default_value("zones/class"))
+ else:
+ try:
+ self.rrclass = RRClass(zone_class_str)
+ except InvalidRRClass:
+ errmsg = "invalid zone class: " + zone_class_str
+ log_error(errmsg)
+ raise XfrinZoneInfoException(errmsg)
+
+ def set_tsig_key(self, tsig_key_str):
+ """Set the tsig_key for this zone, given a TSIG key string
+ representation. If tsig_key_str is None, no TSIG key will
+ be set. Raises XfrinZoneInfoException if tsig_key_str cannot
+ be parsed."""
+ if tsig_key_str is None:
+ self.tsig_key = None
+ else:
+ try:
+ self.tsig_key = TSIGKey(tsig_key_str)
+ except InvalidParameter as ipe:
+ errmsg = "bad TSIG key string: " + tsig_key_str
+ log_error(errmsg)
+ raise XfrinZoneInfoException(errmsg)
+
+ def get_master_addr_info(self):
+ return (self.master_addr.family, socket.SOCK_STREAM,
+ (str(self.master_addr), self.master_port))
+
class Xfrin:
def __init__(self, verbose = False):
self._max_transfers_in = 10
- #TODO, this is the temp way to set the zone's master.
- self._master_addr = DEFAULT_MASTER
- self._master_port = DEFAULT_MASTER_PORT
+ self._zones = {}
self._cc_setup()
self.recorder = XfrinRecorder()
self._shutdown_event = threading.Event()
@@ -402,10 +557,7 @@ class Xfrin:
self.command_handler)
self._module_cc.start()
config_data = self._module_cc.get_full_config()
- self._max_transfers_in = config_data.get("transfers_in")
- self._master_addr = config_data.get('master_addr') or self._master_addr
- self._master_port = config_data.get('master_port') or self._master_port
- self._tsig_key_str = config_data.get('tsig_key') or None
+ self.config_handler(config_data)
def _cc_check_command(self):
'''This is a straightforward wrapper for cc.check_command,
@@ -413,22 +565,42 @@ class Xfrin:
of unit tests.'''
self._module_cc.check_command(False)
+ def _get_zone_info(self, name, rrclass):
+ """Returns the ZoneInfo object containing the configured data
+ for the given zone name. If the zone name did not have any
+ data, returns None"""
+ return self._zones.get((name.to_text(), rrclass.to_text()))
+
+ def _add_zone_info(self, zone_info):
+ """Add the zone info. Raises a XfrinZoneInfoException if a zone
+ with the same name and class is already configured"""
+ key = (zone_info.name.to_text(), zone_info.class_str)
+ if key in self._zones:
+ raise XfrinZoneInfoException("zone " + str(key) +
+ " configured multiple times")
+ self._zones[key] = zone_info
+
+ def _clear_zone_info(self):
+ self._zones = {}
+
def config_handler(self, new_config):
+ # backup all config data (should there be a problem in the new
+ # data)
+ old_max_transfers_in = self._max_transfers_in
+ old_zones = self._zones
+
self._max_transfers_in = new_config.get("transfers_in") or self._max_transfers_in
- self._tsig_key_str = new_config.get('tsig_key') or None
- if ('master_addr' in new_config) or ('master_port' in new_config):
- # User should change the port and address together.
- try:
- addr = new_config.get('master_addr') or self._master_addr
- port = new_config.get('master_port') or self._master_port
- isc.net.parse.addr_parse(addr)
- isc.net.parse.port_parse(port)
- self._master_addr = addr
- self._master_port = port
- except ValueError:
- errmsg = "bad format for zone's master: " + str(new_config)
- log_error(errmsg)
- return create_answer(1, errmsg)
+
+ if 'zones' in new_config:
+ self._clear_zone_info()
+ for zone_config in new_config.get('zones'):
+ try:
+ zone_info = ZoneInfo(zone_config, self._module_cc)
+ self._add_zone_info(zone_info)
+ except XfrinZoneInfoException as xce:
+ self._zones = old_zones
+ self._max_transfers_in = old_max_transfers_in
+ return create_answer(1, str(xce))
return create_answer(0)
@@ -453,28 +625,43 @@ class Xfrin:
# notify command maybe has the parameters which
# specify the notifyfrom address and port, according the RFC1996, zone
# transfer should starts first from the notifyfrom, but now, let 'TODO' it.
+ # (using the value now, while we can only set one master address, would be
+ # a security hole. Once we add the ability to have multiple master addresses,
+ # we should check if it matches one of them, and then use it.)
(zone_name, rrclass) = self._parse_zone_name_and_class(args)
- (master_addr) = build_addr_info(self._master_addr, self._master_port)
- ret = self.xfrin_start(zone_name,
- rrclass,
- self._get_db_file(),
- master_addr,
- self._tsig_key_str,
- True)
- answer = create_answer(ret[0], ret[1])
+ zone_info = self._get_zone_info(zone_name, rrclass)
+ if zone_info is None:
+ # TODO what to do? no info known about zone. defaults?
+ errmsg = "Got notification to retransfer unknown zone " + zone_name.to_text()
+ log_error(errmsg)
+ answer = create_answer(1, errmsg)
+ else:
+ master_addr = zone_info.get_master_addr_info()
+ ret = self.xfrin_start(zone_name,
+ rrclass,
+ self._get_db_file(),
+ master_addr,
+ zone_info.tsig_key,
+ True)
+ answer = create_answer(ret[0], ret[1])
elif command == 'retransfer' or command == 'refresh':
# Xfrin receives the retransfer/refresh from cmdctl(sent by bindctl).
# If the command has specified master address, do transfer from the
# master address, or else do transfer from the configured masters.
(zone_name, rrclass) = self._parse_zone_name_and_class(args)
- master_addr = self._parse_master_and_port(args)
+ master_addr = self._parse_master_and_port(args, zone_name,
+ rrclass)
+ zone_info = self._get_zone_info(zone_name, rrclass)
+ tsig_key = None
+ if zone_info:
+ tsig_key = zone_info.tsig_key
db_file = args.get('db_file') or self._get_db_file()
ret = self.xfrin_start(zone_name,
rrclass,
db_file,
master_addr,
- self._tsig_key_str,
+ tsig_key,
(False if command == 'retransfer' else True))
answer = create_answer(ret[0], ret[1])
@@ -486,25 +673,51 @@ class Xfrin:
return answer
def _parse_zone_name_and_class(self, args):
- zone_name = args.get('zone_name')
- if not zone_name:
+ zone_name_str = args.get('zone_name')
+ if zone_name_str is None:
raise XfrinException('zone name should be provided')
- rrclass = args.get('zone_class')
- if not rrclass:
- rrclass = RRClass.IN()
+ return (_check_zone_name(zone_name_str), _check_zone_class(args.get('zone_class')))
+
+ def _parse_master_and_port(self, args, zone_name, zone_class):
+ """
+ Return tuple (family, socktype, sockaddr) for address and port in given
+ args dict.
+ IPv4 and IPv6 are the only supported addresses now, so sockaddr will be
+ (address, port). The socktype is socket.SOCK_STREAM for now.
+ """
+ # check if we have configured info about this zone, in case
+ # port or master are not specified
+ zone_info = self._get_zone_info(zone_name, zone_class)
+
+ addr_str = args.get('master')
+ if addr_str is None:
+ if zone_info is not None:
+ addr = zone_info.master_addr
+ else:
+ raise XfrinException("Master address not given or "
+ "configured for " + zone_name.to_text())
else:
try:
- rrclass = RRClass(rrclass)
- except InvalidRRClass as e:
- raise XfrinException('invalid RRClass: ' + rrclass)
-
- return zone_name, rrclass
+ addr = isc.net.parse.addr_parse(addr_str)
+ except ValueError as err:
+ raise XfrinException("failed to resolve master address %s: %s" %
+ (addr_str, str(err)))
+
+ port_str = args.get('port')
+ if port_str is None:
+ if zone_info is not None:
+ port = zone_info.master_port
+ else:
+ port = DEFAULT_MASTER_PORT
+ else:
+ try:
+ port = isc.net.parse.port_parse(port_str)
+ except ValueError as err:
+ raise XfrinException("failed to parse port=%s: %s" %
+ (port_str, str(err)))
- def _parse_master_and_port(self, args):
- port = args.get('port') or self._master_port
- master = args.get('master') or self._master_addr
- return build_addr_info(master, port)
+ return (addr.family, socket.SOCK_STREAM, (str(addr), port))
def _get_db_file(self):
#TODO, the db file path should be got in auth server's configuration
@@ -567,7 +780,7 @@ class Xfrin:
while not self._shutdown_event.is_set():
self._cc_check_command()
- def xfrin_start(self, zone_name, rrclass, db_file, master_addrinfo, tsig_key_str,
+ def xfrin_start(self, zone_name, rrclass, db_file, master_addrinfo, tsig_key,
check_soa = True):
if "pydnspp" not in sys.modules:
return (1, "xfrin failed, can't load dns message python library: 'pydnspp'")
@@ -582,12 +795,13 @@ class Xfrin:
xfrin_thread = threading.Thread(target = process_xfrin,
args = (self,
self.recorder,
- zone_name, rrclass,
+ zone_name.to_text(),
+ rrclass,
db_file,
self._shutdown_event,
master_addrinfo, check_soa,
self._verbose,
- tsig_key_str))
+ tsig_key))
xfrin_thread.start()
return (0, 'zone xfrin is started')
@@ -604,20 +818,6 @@ def set_signal_handler():
signal.signal(signal.SIGTERM, signal_handler)
signal.signal(signal.SIGINT, signal_handler)
-def build_addr_info(addrstr, portstr):
- """
- Return tuple (family, socktype, sockaddr) for given address and port.
- IPv4 and IPv6 are the only supported addresses now, so sockaddr will be
- (address, port). The socktype is socket.SOCK_STREAM for now.
- """
- try:
- port = isc.net.parse.port_parse(portstr)
- addr = isc.net.parse.addr_parse(addrstr)
- return (addr.family, socket.SOCK_STREAM, (addrstr, port))
- except ValueError as err:
- raise XfrinException("failed to resolve master address/port=%s/%s: %s" %
- (addrstr, portstr, str(err)))
-
def set_cmd_options(parser):
parser.add_option("-v", "--verbose", dest="verbose", action="store_true",
help="display more about what is going on")
diff --git a/src/bin/xfrin/xfrin.spec b/src/bin/xfrin/xfrin.spec
index 46bad69..a3e62ce 100644
--- a/src/bin/xfrin/xfrin.spec
+++ b/src/bin/xfrin/xfrin.spec
@@ -9,21 +9,43 @@
"item_optional": false,
"item_default": 10
},
- {
- "item_name": "master_addr",
- "item_type": "string",
- "item_optional": false,
- "item_default": ""
- },
- { "item_name": "master_port",
- "item_type": "integer",
+ { "item_name": "zones",
+ "item_type": "list",
"item_optional": false,
- "item_default": 53
- },
- { "item_name": "tsig_key",
- "item_type": "string",
- "item_optional": true,
- "item_default": ""
+ "item_default": [],
+ "list_item_spec":
+ { "item_type": "map",
+ "item_name": "zone_info",
+ "item_optional": false,
+ "item_default": {},
+ "map_item_spec": [
+ { "item_name": "name",
+ "item_type": "string",
+ "item_optional": false,
+ "item_default": ""
+ },
+ { "item_name": "class",
+ "item_type": "string",
+ "item_optional": false,
+ "item_default": "IN"
+ },
+ {
+ "item_name": "master_addr",
+ "item_type": "string",
+ "item_optional": false,
+ "item_default": ""
+ },
+ { "item_name": "master_port",
+ "item_type": "integer",
+ "item_optional": false,
+ "item_default": 53
+ },
+ { "item_name": "tsig_key",
+ "item_type": "string",
+ "item_optional": true
+ }
+ ]
+ }
}
],
"commands": [
diff --git a/src/bin/zonemgr/b10-zonemgr.8 b/src/bin/zonemgr/b10-zonemgr.8
index fbd0602..bfc0a7b 100644
--- a/src/bin/zonemgr/b10-zonemgr.8
+++ b/src/bin/zonemgr/b10-zonemgr.8
@@ -2,12 +2,12 @@
.\" Title: b10-zonemgr
.\" Author: [FIXME: author] [see http://docbook.sf.net/el/author]
.\" Generator: DocBook XSL Stylesheets v1.75.2 <http://docbook.sf.net/>
-.\" Date: October 18, 2010
+.\" Date: May 19, 2011
.\" Manual: BIND10
.\" Source: BIND10
.\" Language: English
.\"
-.TH "B10\-ZONEMGR" "8" "October 18, 2010" "BIND10" "BIND10"
+.TH "B10\-ZONEMGR" "8" "May 19, 2011" "BIND10" "BIND10"
.\" -----------------------------------------------------------------
.\" * set default formatting
.\" -----------------------------------------------------------------
@@ -46,11 +46,6 @@ receives its configurations from
The configurable settings are:
.PP
-\fIjitter_scope\fR
-defines the random jitter range subtracted from the refresh and retry timers to avoid many zones from refreshing at the same time\&. The refresh or retry time actually used is a random time between the defined refresh or retry time and it multiplied by the
-\fIjitter_scope\fR\&. This is re\-evaluated after each refresh or retry\&. This value is a real number and the maximum is 0\&.5 (half of the refresh or retry time)\&. The default is 0\&.25\&. Set to 0 to disable the jitter\&.
-.PP
-
\fIlowerbound_refresh\fR
defines the minimum SOA REFRESH time in seconds\&. The default is 10\&.
.PP
@@ -59,10 +54,36 @@ defines the minimum SOA REFRESH time in seconds\&. The default is 10\&.
defines the minimum SOA RETRY time in seconds\&. The default is 5\&.
.PP
+\fIrefresh_jitter\fR
+This value is a real number\&. The maximum amount is 0\&.5\&. The default is 0\&.25\&.
+.PP
+
+\fIreload_jitter\fR
+This value is a real number\&. The default is 0\&.75\&.
+.PP
+
\fImax_transfer_timeout\fR
defines the maximum amount of time in seconds for a transfer\&.
The default is 14400 (4 hours)\&.
.PP
+
+\fIsecondary_zones\fR
+is a list of slave zones that the
+\fBb10\-zonemgr\fR
+should keep timers for\&. The list items include the
+\fIname\fR
+(which defines the zone name) and the
+\fIclass\fR
+(which defaults to
+\(lqIN\(rq)\&.
+.PP
+(A deprecated configuration is
+\fIjitter_scope\fR
+which is superceded by
+\fIrefresh_jitter\fR
+and
+\fIreload_jitter\fR\&.)
+.PP
The configuration commands are:
.PP
@@ -107,5 +128,5 @@ The
daemon was designed in July 2010 by CNNIC for the ISC BIND 10 project\&.
.SH "COPYRIGHT"
.br
-Copyright \(co 2010 Internet Systems Consortium, Inc. ("ISC")
+Copyright \(co 2010-2011 Internet Systems Consortium, Inc. ("ISC")
.br
diff --git a/src/bin/zonemgr/b10-zonemgr.xml b/src/bin/zonemgr/b10-zonemgr.xml
index 4d796ee..00f5d04 100644
--- a/src/bin/zonemgr/b10-zonemgr.xml
+++ b/src/bin/zonemgr/b10-zonemgr.xml
@@ -2,7 +2,7 @@
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd"
[<!ENTITY mdash "—">]>
<!--
- - 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
@@ -20,7 +20,7 @@
<refentry>
<refentryinfo>
- <date>October 18, 2010</date>
+ <date>May 19, 2011</date>
</refentryinfo>
<refmeta>
@@ -36,7 +36,7 @@
<docinfo>
<copyright>
- <year>2010</year>
+ <year>2010-2011</year>
<holder>Internet Systems Consortium, Inc. ("ISC")</holder>
</copyright>
</docinfo>
@@ -92,6 +92,39 @@
<para>
The configurable settings are:
</para>
+
+ <para>
+ <varname>lowerbound_refresh</varname>
+ defines the minimum SOA REFRESH time in seconds.
+ The default is 10.
+ </para>
+
+ <para>
+ <varname>lowerbound_retry</varname>
+ defines the minimum SOA RETRY time in seconds.
+ The default is 5.
+ </para>
+
+ <para>
+ <varname>refresh_jitter</varname>
+ This value is a real number.
+ The maximum amount is 0.5.
+ The default is 0.25.
+ </para>
+<!-- TODO: needs to be documented -->
+<!-- TODO: Set to 0 to disable the jitter. -->
+
+ <para>
+ <varname>reload_jitter</varname>
+ This value is a real number.
+ The default is 0.75.
+ </para>
+<!-- TODO: needs to be documented -->
+<!-- TODO: Set to 0 to disable the jitter. -->
+<!-- what does 0 do? -->
+<!-- TODO: no max? -->
+
+<!-- TODO: remove this. This is old removed config
<para>
<varname>jitter_scope</varname>
defines the random jitter range subtracted from the refresh
@@ -106,16 +139,8 @@
The default is 0.25.
Set to 0 to disable the jitter.
</para>
- <para>
- <varname>lowerbound_refresh</varname>
- defines the minimum SOA REFRESH time in seconds.
- The default is 10.
- </para>
- <para>
- <varname>lowerbound_retry</varname>
- defines the minimum SOA RETRY time in seconds.
- The default is 5.
- </para>
+-->
+
<para>
<varname>max_transfer_timeout</varname>
defines the maximum amount of time in seconds for a transfer.
@@ -123,6 +148,21 @@
The default is 14400 (4 hours).
</para>
+<!-- TODO: this duplicates list in Xfrin too -->
+ <para>
+ <varname>secondary_zones</varname> is a list of slave zones
+ that the <command>b10-zonemgr</command> should keep timers for.
+ The list items include the <varname>name</varname> (which
+ defines the zone name) and the <varname>class</varname>
+ (which defaults to <quote>IN</quote>).
+ </para>
+
+ <para>
+ (A deprecated configuration is <varname>jitter_scope</varname>
+ which is superceded by <varname>refresh_jitter</varname>
+ and <varname>reload_jitter</varname>.)
+ </para>
+
<!-- TODO: formating -->
<para>
The configuration commands are:
diff --git a/src/bin/zonemgr/tests/zonemgr_test.py b/src/bin/zonemgr/tests/zonemgr_test.py
index 479ca68..496ce6b 100644
--- a/src/bin/zonemgr/tests/zonemgr_test.py
+++ b/src/bin/zonemgr/tests/zonemgr_test.py
@@ -434,6 +434,14 @@ class TestZonemgrRefresh(unittest.TestCase):
self.assertTrue(zone_state == ZONE_REFRESHING)
def test_update_config_data(self):
+ # make sure it doesn't fail if we only provide secondary zones
+ config_data = {
+ "secondary_zones": [ { "name": "example.net.",
+ "class": "IN" } ]
+ }
+ self.zone_refresh.update_config_data(config_data)
+
+ # update all values
config_data = {
"lowerbound_refresh" : 60,
"lowerbound_retry" : 30,
@@ -449,6 +457,53 @@ class TestZonemgrRefresh(unittest.TestCase):
self.assertEqual(0.25, self.zone_refresh._refresh_jitter)
self.assertEqual(0.75, self.zone_refresh._reload_jitter)
+ # make sure they are not reset when we only update one
+ config_data = {
+ "reload_jitter" : 0.35,
+ }
+ self.zone_refresh.update_config_data(config_data)
+ self.assertEqual(60, self.zone_refresh._lowerbound_refresh)
+ self.assertEqual(30, self.zone_refresh._lowerbound_retry)
+ self.assertEqual(19800, self.zone_refresh._max_transfer_timeout)
+ self.assertEqual(0.25, self.zone_refresh._refresh_jitter)
+ self.assertEqual(0.35, self.zone_refresh._reload_jitter)
+
+ # and make sure we restore the previous config if something
+ # goes wrong
+ config_data = {
+ "lowerbound_refresh" : 61,
+ "lowerbound_retry" : 31,
+ "max_transfer_timeout" : 19801,
+ "refresh_jitter" : 0.21,
+ "reload_jitter" : 0.71,
+ "secondary_zones": [ { "name": "doesnotexist",
+ "class": "IN" } ]
+ }
+ self.assertRaises(ZonemgrException,
+ self.zone_refresh.update_config_data,
+ config_data)
+ self.assertEqual(60, self.zone_refresh._lowerbound_refresh)
+ self.assertEqual(30, self.zone_refresh._lowerbound_retry)
+ self.assertEqual(19800, self.zone_refresh._max_transfer_timeout)
+ self.assertEqual(0.25, self.zone_refresh._refresh_jitter)
+ self.assertEqual(0.35, self.zone_refresh._reload_jitter)
+
+ # Make sure we accept 0 as a value
+ config_data = {
+ "lowerbound_refresh" : 60,
+ "lowerbound_retry" : 30,
+ "max_transfer_timeout" : 19800,
+ "refresh_jitter" : 0,
+ "reload_jitter" : 0.75,
+ "secondary_zones": []
+ }
+ self.zone_refresh.update_config_data(config_data)
+ self.assertEqual(60, self.zone_refresh._lowerbound_refresh)
+ self.assertEqual(30, self.zone_refresh._lowerbound_retry)
+ self.assertEqual(19800, self.zone_refresh._max_transfer_timeout)
+ self.assertEqual(0, self.zone_refresh._refresh_jitter)
+ self.assertEqual(0.75, self.zone_refresh._reload_jitter)
+
def test_shutdown(self):
self.zone_refresh._check_sock = self.zone_refresh._master_socket
listener = self.zone_refresh.run_timer()
diff --git a/src/bin/zonemgr/zonemgr.py.in b/src/bin/zonemgr/zonemgr.py.in
index cc6d7b9..c6e3163 100755
--- a/src/bin/zonemgr/zonemgr.py.in
+++ b/src/bin/zonemgr/zonemgr.py.in
@@ -101,6 +101,11 @@ class ZonemgrRefresh:
self._check_sock = slave_socket
self._db_file = db_file
self._zonemgr_refresh_info = {}
+ self._lowerbound_refresh = None
+ self._lowerbound_retry = None
+ self._max_transfer_timeout = None
+ self._refresh_jitter = None
+ self._reload_jitter = None
self.update_config_data(config_data)
self._running = False
@@ -404,37 +409,73 @@ class ZonemgrRefresh:
def update_config_data(self, new_config):
""" update ZonemgrRefresh config """
+ # TODO: we probably want to store all this info in a nice
+ # class, so that we don't have to backup and restore every
+ # single value.
+ # TODO2: We also don't use get_default_value yet
backup = self._zonemgr_refresh_info.copy()
+
+ # Get a new value, but only if it is defined (commonly used below)
+ # We don't use "value or default", because if value would be
+ # 0, we would take default
+ def val_or_default(value, default):
+ if value is not None:
+ return value
+ else:
+ return default
+
+ # store the values so we can restore them if there is a problem
+ lowerbound_refresh_backup = self._lowerbound_refresh
+ self._lowerbound_refresh = val_or_default(
+ new_config.get('lowerbound_refresh'), self._lowerbound_refresh)
+
+ lowerbound_retry_backup = self._lowerbound_retry
+ self._lowerbound_retry = val_or_default(
+ new_config.get('lowerbound_retry'), self._lowerbound_retry)
+
+ max_transfer_timeout_backup = self._max_transfer_timeout
+ self._max_transfer_timeout = val_or_default(
+ new_config.get('max_transfer_timeout'), self._max_transfer_timeout)
+
+ refresh_jitter_backup = self._refresh_jitter
+ self._refresh_jitter = val_or_default(
+ new_config.get('refresh_jitter'), self._refresh_jitter)
+
+ reload_jitter_backup = self._reload_jitter
+ self._reload_jitter = val_or_default(
+ new_config.get('reload_jitter'), self._reload_jitter)
try:
required = {}
- # Add new zones
- for secondary_zone in new_config.get('secondary_zones'):
- name = secondary_zone['name']
- # Be tolerant to sclerotic users who forget the final dot
- if name[-1] != '.':
- name = name + '.'
- name_class = (name, secondary_zone['class'])
- required[name_class] = True
- # Add it only if it isn't there already
- if not name_class in self._zonemgr_refresh_info:
- self.zonemgr_add_zone(name_class)
- # Drop the zones that are no longer there
- # Do it in two phases, python doesn't like deleting while iterating
- to_drop = []
- for old_zone in self._zonemgr_refresh_info:
- if not old_zone in required:
- to_drop.append(old_zone)
- for drop in to_drop:
- del self._zonemgr_refresh_info[drop]
+ secondary_zones = new_config.get('secondary_zones')
+ if secondary_zones is not None:
+ # Add new zones
+ for secondary_zone in new_config.get('secondary_zones'):
+ name = secondary_zone['name']
+ # Be tolerant to sclerotic users who forget the final dot
+ if name[-1] != '.':
+ name = name + '.'
+ name_class = (name, secondary_zone['class'])
+ required[name_class] = True
+ # Add it only if it isn't there already
+ if not name_class in self._zonemgr_refresh_info:
+ self.zonemgr_add_zone(name_class)
+ # Drop the zones that are no longer there
+ # Do it in two phases, python doesn't like deleting while iterating
+ to_drop = []
+ for old_zone in self._zonemgr_refresh_info:
+ if not old_zone in required:
+ to_drop.append(old_zone)
+ for drop in to_drop:
+ del self._zonemgr_refresh_info[drop]
# If we are not able to find it in database, restore the original
except:
self._zonemgr_refresh_info = backup
+ self._lowerbound_refresh = lowerbound_refresh_backup
+ self._lowerbound_retry = lowerbound_retry_backup
+ self._max_transfer_timeout = max_transfer_timeout_backup
+ self._refresh_jitter = refresh_jitter_backup
+ self._reload_jitter = reload_jitter_backup
raise
- self._lowerbound_refresh = new_config.get('lowerbound_refresh')
- self._lowerbound_retry = new_config.get('lowerbound_retry')
- self._max_transfer_timeout = new_config.get('max_transfer_timeout')
- self._refresh_jitter = new_config.get('refresh_jitter')
- self._reload_jitter = new_config.get('reload_jitter')
class Zonemgr:
"""Zone manager class."""
diff --git a/src/cppcheck-suppress.lst b/src/cppcheck-suppress.lst
index 4659dc4..36b8e4c 100644
--- a/src/cppcheck-suppress.lst
+++ b/src/cppcheck-suppress.lst
@@ -12,4 +12,4 @@ functionConst:src/lib/cache/rrset_cache.h
// Intentional self assignment tests. Suppress warning about them.
selfAssignment:src/lib/dns/tests/name_unittest.cc:293
selfAssignment:src/lib/dns/tests/rdata_unittest.cc:228
-selfAssignment:src/lib/dns/tests/tsigkey_unittest.cc:125
+selfAssignment:src/lib/dns/tests/tsigkey_unittest.cc:137
diff --git a/src/lib/asiodns/tests/Makefile.am b/src/lib/asiodns/tests/Makefile.am
index fd65d0b..7e05c18 100644
--- a/src/lib/asiodns/tests/Makefile.am
+++ b/src/lib/asiodns/tests/Makefile.am
@@ -27,6 +27,7 @@ run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
run_unittests_LDADD = $(GTEST_LDADD)
run_unittests_LDADD += $(SQLITE_LIBS)
run_unittests_LDADD += $(top_builddir)/src/lib/dns/libdns++.la
+run_unittests_LDADD += $(top_builddir)/src/lib/util/unittests/libutil_unittests.la
run_unittests_LDADD += $(top_builddir)/src/lib/util/libutil.la
run_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libasiolink.la
run_unittests_LDADD += $(top_builddir)/src/lib/log/liblog.la
diff --git a/src/lib/asiodns/tests/run_unittests.cc b/src/lib/asiodns/tests/run_unittests.cc
index c285f9e..d3c63b6 100644
--- a/src/lib/asiodns/tests/run_unittests.cc
+++ b/src/lib/asiodns/tests/run_unittests.cc
@@ -13,6 +13,7 @@
// PERFORMANCE OF THIS SOFTWARE.
#include <gtest/gtest.h>
+#include <util/unittests/run_all.h>
#include <log/root_logger_name.h>
#include <dns/tests/unittest_util.h>
@@ -24,5 +25,5 @@ main(int argc, char* argv[])
isc::log::setRootLoggerName("unittest"); // Set a root logger name
isc::UnitTestUtil::addDataPath(TEST_DATA_DIR); // Add location of test data
- return (RUN_ALL_TESTS());
+ return (isc::util::unittests::run_all());
}
diff --git a/src/lib/asiolink/tests/Makefile.am b/src/lib/asiolink/tests/Makefile.am
index bfdf7c1..af4f679 100644
--- a/src/lib/asiolink/tests/Makefile.am
+++ b/src/lib/asiolink/tests/Makefile.am
@@ -10,6 +10,12 @@ if USE_STATIC_LINK
AM_LDFLAGS = -static
endif
+# Some versions of GCC warn about some versions of Boost regarding
+# missing initializer for members in its posix_time.
+# https://svn.boost.org/trac/boost/ticket/3477
+# But older GCC compilers don't have the flag.
+AM_CXXFLAGS += $(WARNING_NO_MISSING_FIELD_INITIALIZERS_CFLAG)
+
CLEANFILES = *.gcno *.gcda
TESTS =
@@ -30,8 +36,8 @@ run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
run_unittests_LDADD = $(GTEST_LDADD)
run_unittests_LDADD += $(SQLITE_LIBS)
run_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libasiolink.la
-run_unittests_LDADD += $(top_builddir)/src/lib/util/libutil.la
run_unittests_LDADD += $(top_builddir)/src/lib/log/liblog.la
+run_unittests_LDADD += $(top_builddir)/src/lib/util/unittests/libutil_unittests.la
run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
diff --git a/src/lib/asiolink/tests/run_unittests.cc b/src/lib/asiolink/tests/run_unittests.cc
index 97bcb65..8d18d62 100644
--- a/src/lib/asiolink/tests/run_unittests.cc
+++ b/src/lib/asiolink/tests/run_unittests.cc
@@ -13,6 +13,7 @@
// PERFORMANCE OF THIS SOFTWARE.
#include <gtest/gtest.h>
+#include <util/unittests/run_all.h>
#include <log/root_logger_name.h>
#include <dns/tests/unittest_util.h>
@@ -23,5 +24,5 @@ main(int argc, char* argv[])
::testing::InitGoogleTest(&argc, argv); // Initialize Google test
isc::log::setRootLoggerName("unittest"); // Set a root logger name
- return (RUN_ALL_TESTS());
+ return (isc::util::unittests::run_all());
}
diff --git a/src/lib/bench/tests/Makefile.am b/src/lib/bench/tests/Makefile.am
index 4259b0e..3ebdf29 100644
--- a/src/lib/bench/tests/Makefile.am
+++ b/src/lib/bench/tests/Makefile.am
@@ -14,10 +14,10 @@ run_unittests_SOURCES += loadquery_unittest.cc
run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
-run_unittests_LDADD = $(top_builddir)/src/lib/exceptions/libexceptions.la
+run_unittests_LDADD = $(top_builddir)/src/lib/bench/libbench.la
run_unittests_LDADD += $(top_builddir)/src/lib/dns/libdns++.la
-run_unittests_LDADD += $(top_builddir)/src/lib/util/libutil.la
-run_unittests_LDADD += $(top_builddir)/src/lib/bench/libbench.la
+run_unittests_LDADD += $(top_builddir)/src/lib/util/unittests/libutil_unittests.la
+run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
run_unittests_LDADD += $(GTEST_LDADD)
endif
diff --git a/src/lib/bench/tests/run_unittests.cc b/src/lib/bench/tests/run_unittests.cc
index 85d4548..450f5dc 100644
--- a/src/lib/bench/tests/run_unittests.cc
+++ b/src/lib/bench/tests/run_unittests.cc
@@ -13,10 +13,11 @@
// PERFORMANCE OF THIS SOFTWARE.
#include <gtest/gtest.h>
+#include <util/unittests/run_all.h>
int
main(int argc, char* argv[]) {
::testing::InitGoogleTest(&argc, argv);
- return (RUN_ALL_TESTS());
+ return (isc::util::unittests::run_all());
}
diff --git a/src/lib/cache/tests/Makefile.am b/src/lib/cache/tests/Makefile.am
index 68a8425..014ad43 100644
--- a/src/lib/cache/tests/Makefile.am
+++ b/src/lib/cache/tests/Makefile.am
@@ -55,8 +55,8 @@ endif
run_unittests_LDADD += $(top_builddir)/src/lib/cache/libcache.la
run_unittests_LDADD += $(top_builddir)/src/lib/nsas/libnsas.la
run_unittests_LDADD += $(top_builddir)/src/lib/dns/libdns++.la
-run_unittests_LDADD += $(top_builddir)/src/lib/util/libutil.la
run_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libasiolink.la
+run_unittests_LDADD += $(top_builddir)/src/lib/util/unittests/libutil_unittests.la
run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
endif
diff --git a/src/lib/cache/tests/run_unittests.cc b/src/lib/cache/tests/run_unittests.cc
index 2c86581..b75fc06 100644
--- a/src/lib/cache/tests/run_unittests.cc
+++ b/src/lib/cache/tests/run_unittests.cc
@@ -15,6 +15,7 @@
#include <config.h>
#include <gtest/gtest.h>
+#include <util/unittests/run_all.h>
#include <dns/tests/unittest_util.h>
@@ -24,5 +25,5 @@ main(int argc, char* argv[]) {
isc::UnitTestUtil::addDataPath(TEST_DATA_SRCDIR);
isc::UnitTestUtil::addDataPath(TEST_DATA_BUILDDIR);
- return (RUN_ALL_TESTS());
+ return (isc::util::unittests::run_all());
}
diff --git a/src/lib/cc/tests/Makefile.am b/src/lib/cc/tests/Makefile.am
index 71e6988..ebfd856 100644
--- a/src/lib/cc/tests/Makefile.am
+++ b/src/lib/cc/tests/Makefile.am
@@ -26,6 +26,7 @@ run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
run_unittests_LDADD = $(GTEST_LDADD)
run_unittests_LDADD += $(top_builddir)/src/lib/cc/libcc.la
+run_unittests_LDADD += $(top_builddir)/src/lib/util/unittests/libutil_unittests.la
run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
endif
diff --git a/src/lib/cc/tests/run_unittests.cc b/src/lib/cc/tests/run_unittests.cc
index 0908071..eeef955 100644
--- a/src/lib/cc/tests/run_unittests.cc
+++ b/src/lib/cc/tests/run_unittests.cc
@@ -13,9 +13,10 @@
// PERFORMANCE OF THIS SOFTWARE.
#include <gtest/gtest.h>
+#include <util/unittests/run_all.h>
int
main(int argc, char* argv[]) {
::testing::InitGoogleTest(&argc, argv);
- return (RUN_ALL_TESTS());
+ return (isc::util::unittests::run_all());
}
diff --git a/src/lib/config/ccsession.cc b/src/lib/config/ccsession.cc
index 1b5e47d..45710e3 100644
--- a/src/lib/config/ccsession.cc
+++ b/src/lib/config/ccsession.cc
@@ -356,11 +356,40 @@ ModuleCCSession::checkCommand() {
}
std::string
-ModuleCCSession::addRemoteConfig(const std::string& spec_file_name) {
- ModuleSpec rmod_spec = readModuleSpecification(spec_file_name);
- std::string module_name = rmod_spec.getFullSpec()->get("module_name")->stringValue();
+ModuleCCSession::addRemoteConfig(const std::string& spec_name,
+ void (*handler)(const std::string& module,
+ ConstElementPtr),
+ bool spec_is_filename)
+{
+ std::string module_name;
+ ModuleSpec rmod_spec;
+ if (spec_is_filename) {
+ // It's a file name, so load it
+ rmod_spec = readModuleSpecification(spec_name);
+ module_name =
+ rmod_spec.getFullSpec()->get("module_name")->stringValue();
+ } else {
+ // It's module name, request it from config manager
+ ConstElementPtr cmd = Element::fromJSON("{ \"command\": ["
+ "\"get_module_spec\","
+ "{\"module_name\": \"" +
+ module_name + "\"} ] }");
+ unsigned int seq = session_.group_sendmsg(cmd, "ConfigManager");
+ ConstElementPtr env, answer;
+ session_.group_recvmsg(env, answer, false, seq);
+ int rcode;
+ ConstElementPtr spec_data = parseAnswer(rcode, answer);
+ if (rcode == 0 && spec_data) {
+ rmod_spec = ModuleSpec(spec_data);
+ module_name = spec_name;
+ if (module_name != rmod_spec.getModuleName()) {
+ isc_throw(CCSessionError, "Module name mismatch");
+ }
+ } else {
+ isc_throw(CCSessionError, "Error getting config for " + module_name + ": " + answer->str());
+ }
+ }
ConfigData rmod_config = ConfigData(rmod_spec);
- session_.subscribe(module_name);
// Get the current configuration values for that module
ConstElementPtr cmd = Element::fromJSON("{ \"command\": [\"get_config\", {\"module_name\":\"" + module_name + "\"} ] }");
@@ -370,8 +399,9 @@ ModuleCCSession::addRemoteConfig(const std::string& spec_file_name) {
session_.group_recvmsg(env, answer, false, seq);
int rcode;
ConstElementPtr new_config = parseAnswer(rcode, answer);
+ ElementPtr local_config;
if (rcode == 0 && new_config) {
- ElementPtr local_config = rmod_config.getLocalConfig();
+ local_config = rmod_config.getLocalConfig();
isc::data::merge(local_config, new_config);
rmod_config.setLocalConfig(local_config);
} else {
@@ -380,6 +410,11 @@ ModuleCCSession::addRemoteConfig(const std::string& spec_file_name) {
// all ok, add it
remote_module_configs_[module_name] = rmod_config;
+ if (handler) {
+ remote_module_handlers_[module_name] = handler;
+ handler(module_name, local_config);
+ }
+ session_.subscribe(module_name);
return (module_name);
}
@@ -390,6 +425,7 @@ ModuleCCSession::removeRemoteConfig(const std::string& module_name) {
it = remote_module_configs_.find(module_name);
if (it != remote_module_configs_.end()) {
remote_module_configs_.erase(it);
+ remote_module_handlers_.erase(module_name);
session_.unsubscribe(module_name);
}
}
@@ -419,6 +455,11 @@ ModuleCCSession::updateRemoteConfig(const std::string& module_name,
if (it != remote_module_configs_.end()) {
ElementPtr rconf = (*it).second.getLocalConfig();
isc::data::merge(rconf, new_config);
+ std::map<std::string, RemoteHandler>::iterator hit =
+ remote_module_handlers_.find(module_name);
+ if (hit != remote_module_handlers_.end()) {
+ hit->second(module_name, new_config);
+ }
}
}
diff --git a/src/lib/config/ccsession.h b/src/lib/config/ccsession.h
index 7364876..c845b8f 100644
--- a/src/lib/config/ccsession.h
+++ b/src/lib/config/ccsession.h
@@ -234,24 +234,43 @@ public:
/**
* Gives access to the configuration values of a different module
* Once this function has been called with the name of the specification
- * file of the module you want the configuration of, you can use
+ * file or the module you want the configuration of, you can use
* \c getRemoteConfigValue() to get a specific setting.
- * Changes are automatically updated, but you cannot specify handlers
- * for those changes, must use \c getRemoteConfigValue() to get a value
- * This function will subscribe to the relevant module channel.
+ * Changes are automatically updated, and you can specify handlers
+ * for those changes. This function will subscribe to the relevant module
+ * channel.
*
- * \param spec_file_name The path to the specification file of
- * the module we want to have configuration
- * values from
+ * \param spec_name This specifies the module to add. It is either a
+ * filename of the spec file to use or a name of module
+ * (in case it's a module name, the spec data is
+ * downloaded from the configuration manager, therefore
+ * the configuration manager must know it). If
+ * spec_is_filenabe is true (the default), then a
+ * filename is assumed, otherwise a module name.
+ * \param handler The handler function called whenever there's a change.
+ * Called once initally from this function. May be NULL
+ * if you don't want any handler to be called and you're
+ * fine with requesting the data through
+ * getRemoteConfigValue() each time.
+ *
+ * The handler should not throw, or it'll fall trough and
+ * the exception will get into strange places, probably
+ * aborting the application.
+ * \param spec_is_filename Says if spec_name is filename or module name.
* \return The name of the module specified in the given specification
* file
*/
- std::string addRemoteConfig(const std::string& spec_file_name);
+ std::string addRemoteConfig(const std::string& spec_name,
+ void (*handler)(const std::string& module_name,
+ isc::data::ConstElementPtr
+ update) = NULL,
+ bool spec_is_filename = true);
/**
* Removes the module with the given name from the remote config
* settings. If the module was not added with \c addRemoteConfig(),
- * nothing happens.
+ * nothing happens. If there was a handler for this config, it is
+ * removed as well.
*/
void removeRemoteConfig(const std::string& module_name);
@@ -296,7 +315,11 @@ private:
const std::string& command,
isc::data::ConstElementPtr args);
+ typedef void (*RemoteHandler)(const std::string&,
+ isc::data::ConstElementPtr);
std::map<std::string, ConfigData> remote_module_configs_;
+ std::map<std::string, RemoteHandler> remote_module_handlers_;
+
void updateRemoteConfig(const std::string& module_name,
isc::data::ConstElementPtr new_config);
};
diff --git a/src/lib/config/tests/Makefile.am b/src/lib/config/tests/Makefile.am
index 0d2c29b..7153e09 100644
--- a/src/lib/config/tests/Makefile.am
+++ b/src/lib/config/tests/Makefile.am
@@ -22,11 +22,12 @@ run_unittests_SOURCES = ccsession_unittests.cc module_spec_unittests.cc config_d
run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
run_unittests_LDADD = $(GTEST_LDADD)
+run_unittests_LDADD += libfake_session.la
run_unittests_LDADD += $(top_builddir)/src/lib/cc/libcc.la
+run_unittests_LDADD += $(top_builddir)/src/lib/config/libcfgclient.la
run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
run_unittests_LDADD += $(top_builddir)/src/lib/log/liblog.la
-run_unittests_LDADD += libfake_session.la
-run_unittests_LDADD += $(top_builddir)/src/lib/config/libcfgclient.la
+run_unittests_LDADD += $(top_builddir)/src/lib/util/unittests/libutil_unittests.la
endif
diff --git a/src/lib/config/tests/ccsession_unittests.cc b/src/lib/config/tests/ccsession_unittests.cc
index f566949..3564d4b 100644
--- a/src/lib/config/tests/ccsession_unittests.cc
+++ b/src/lib/config/tests/ccsession_unittests.cc
@@ -346,6 +346,18 @@ TEST_F(CCSessionTest, checkCommand2) {
EXPECT_EQ(2, mccs.getValue("item1")->intValue());
}
+std::string remote_module_name;
+int remote_item1(0);
+ConstElementPtr remote_config;
+ModuleCCSession *remote_mccs(NULL);
+
+void remoteHandler(const std::string& module_name, ConstElementPtr config) {
+ remote_module_name = module_name;
+ remote_item1 = remote_mccs->getRemoteConfigValue("Spec2", "item1")->
+ intValue();
+ remote_config = config;
+}
+
TEST_F(CCSessionTest, remoteConfig) {
std::string module_name;
int item1;
@@ -392,6 +404,91 @@ TEST_F(CCSessionTest, remoteConfig) {
session.getMessages()->add(createAnswer());
EXPECT_THROW(mccs.addRemoteConfig(ccspecfile("spec2.spec")), CCSessionError);
+
+ {
+ SCOPED_TRACE("With module name");
+ // Try adding it with downloading the spec from config manager
+ ModuleSpec spec(moduleSpecFromFile(ccspecfile("spec2.spec")));
+ session.getMessages()->add(createAnswer(0, spec.getFullSpec()));
+ session.getMessages()->add(createAnswer(0, el("{}")));
+
+ EXPECT_NO_THROW(module_name = mccs.addRemoteConfig("Spec2", NULL,
+ false));
+
+ EXPECT_EQ("Spec2", module_name);
+ EXPECT_NO_THROW(item1 =
+ mccs.getRemoteConfigValue(module_name,
+ "item1")->intValue());
+ EXPECT_EQ(1, item1);
+
+ mccs.removeRemoteConfig(module_name);
+ }
+
+ {
+ // Try adding it with a handler.
+ // Pass non-default value to see the handler is called after
+ // downloading the configuration, not too soon.
+ SCOPED_TRACE("With handler");
+ session.getMessages()->add(createAnswer(0, el("{ \"item1\": 2 }")));
+ remote_mccs = &mccs;
+ module_name = mccs.addRemoteConfig(ccspecfile("spec2.spec"),
+ remoteHandler);
+ {
+ SCOPED_TRACE("Before update");
+ EXPECT_EQ("Spec2", module_name);
+ EXPECT_TRUE(session.haveSubscription("Spec2", "*"));
+ // Now check the parameters the remote handler stored
+ // This also checks it was called
+ EXPECT_EQ("Spec2", remote_module_name);
+ remote_module_name = "";
+ EXPECT_EQ(2, remote_item1);
+ remote_item1 = 0;
+ if (remote_config) {
+ EXPECT_EQ(2, remote_config->get("item1")->intValue());
+ } else {
+ ADD_FAILURE() << "Remote config not set";
+ }
+ remote_config.reset();
+ // Make sure normal way still works
+ item1 = mccs.getRemoteConfigValue(module_name,
+ "item1")->intValue();
+ EXPECT_EQ(2, item1);
+ }
+
+ {
+ SCOPED_TRACE("After update");
+ session.addMessage(el("{ \"command\": [ \"config_update\", "
+ "{ \"item1\": 3 } ] }"), module_name, "*");
+ mccs.checkCommand();
+ EXPECT_EQ("Spec2", remote_module_name);
+ remote_module_name = "";
+ EXPECT_EQ(3, remote_item1);
+ remote_item1 = 0;
+ if (remote_config) {
+ EXPECT_EQ(3, remote_config->get("item1")->intValue());
+ } else {
+ ADD_FAILURE() << "Remote config not set";
+ }
+ remote_config.reset();
+ // Make sure normal way still works
+ item1 = mccs.getRemoteConfigValue(module_name,
+ "item1")->intValue();
+ EXPECT_EQ(3, item1);
+ }
+
+ remote_mccs = NULL;
+ mccs.removeRemoteConfig(module_name);
+
+ {
+ SCOPED_TRACE("When removed");
+ // Make sure nothing is called any more
+ session.addMessage(el("{ \"command\": [ \"config_update\", "
+ "{ \"item1\": 4 } ] }"), module_name, "*");
+ EXPECT_EQ("", remote_module_name);
+ EXPECT_EQ(0, remote_item1);
+ EXPECT_FALSE(remote_config);
+ }
+ }
}
TEST_F(CCSessionTest, ignoreRemoteConfigCommands) {
diff --git a/src/lib/config/tests/run_unittests.cc b/src/lib/config/tests/run_unittests.cc
index fab90f5..19d2be1 100644
--- a/src/lib/config/tests/run_unittests.cc
+++ b/src/lib/config/tests/run_unittests.cc
@@ -13,16 +13,12 @@
// PERFORMANCE OF THIS SOFTWARE.
#include <gtest/gtest.h>
+#include <util/unittests/run_all.h>
#include <log/logger_support.h>
int
main(int argc, char* argv[]) {
::testing::InitGoogleTest(&argc, argv);
-
- // TODO: UNCOMMENT ON MERGE
- // (this is the call we want in master, but branch point does not
- // have this yet)
- //isc::log::initLogger();
-
- return (RUN_ALL_TESTS());
+ isc::log::initLogger();
+ return (isc::util::unittests::run_all());
}
diff --git a/src/lib/cryptolink/crypto_hmac.cc b/src/lib/cryptolink/crypto_hmac.cc
index d20c85b..af0c18e 100644
--- a/src/lib/cryptolink/crypto_hmac.cc
+++ b/src/lib/cryptolink/crypto_hmac.cc
@@ -17,6 +17,7 @@
#include <boost/scoped_ptr.hpp>
+#include <botan/version.h>
#include <botan/botan.h>
#include <botan/hmac.h>
#include <botan/hash.h>
@@ -35,6 +36,15 @@ getBotanHashAlgorithmName(isc::cryptolink::HashAlgorithm algorithm) {
case isc::cryptolink::SHA256:
return ("SHA-256");
break;
+ case isc::cryptolink::SHA224:
+ return ("SHA-224");
+ break;
+ case isc::cryptolink::SHA384:
+ return ("SHA-384");
+ break;
+ case isc::cryptolink::SHA512:
+ return ("SHA-512");
+ break;
case isc::cryptolink::UNKNOWN_HASH:
return ("Unknown");
break;
@@ -60,7 +70,8 @@ public:
getBotanHashAlgorithmName(hash_algorithm));
} catch (const Botan::Algorithm_Not_Found&) {
isc_throw(isc::cryptolink::UnsupportedAlgorithm,
- "Unknown hash algorithm: " + hash_algorithm);
+ "Unknown hash algorithm: " <<
+ static_cast<int>(hash_algorithm));
} catch (const Botan::Exception& exc) {
isc_throw(isc::cryptolink::LibraryError, exc.what());
}
@@ -70,12 +81,28 @@ public:
// If the key length is larger than the block size, we hash the
// key itself first.
try {
- if (secret_len > hash->HASH_BLOCK_SIZE) {
+ // use a temp var so we don't have blocks spanning
+ // preprocessor directives
+#if BOTAN_VERSION_CODE >= BOTAN_VERSION_CODE_FOR(1,9,0)
+ size_t block_length = hash->hash_block_size();
+#elif BOTAN_VERSION_CODE >= BOTAN_VERSION_CODE_FOR(1,8,0)
+ size_t block_length = hash->HASH_BLOCK_SIZE;
+#else
+#error "Unsupported Botan version (need 1.8 or higher)"
+ // added to suppress irrelevant compiler errors
+ size_t block_length = 0;
+#endif
+ if (secret_len > block_length) {
Botan::SecureVector<Botan::byte> hashed_key =
hash->process(static_cast<const Botan::byte*>(secret),
secret_len);
hmac_->set_key(hashed_key.begin(), hashed_key.size());
} else {
+ // Botan 1.8 considers len 0 a bad key. 1.9 does not,
+ // but we won't accept it anyway, and fail early
+ if (secret_len == 0) {
+ isc_throw(BadKey, "Bad HMAC secret length: 0");
+ }
hmac_->set_key(static_cast<const Botan::byte*>(secret),
secret_len);
}
@@ -89,7 +116,15 @@ public:
~HMACImpl() { }
size_t getOutputLength() const {
+#if BOTAN_VERSION_CODE >= BOTAN_VERSION_CODE_FOR(1,9,0)
+ return (hmac_->output_length());
+#elif BOTAN_VERSION_CODE >= BOTAN_VERSION_CODE_FOR(1,8,0)
return (hmac_->OUTPUT_LENGTH);
+#else
+#error "Unsupported Botan version (need 1.8 or higher)"
+ // added to suppress irrelevant compiler errors
+ return 0;
+#endif
}
void update(const void* data, const size_t len) {
@@ -211,7 +246,7 @@ HMAC::verify(const void* sig, const size_t len) {
}
void
-signHMAC(const void* data, size_t data_len, const void* secret,
+signHMAC(const void* data, const size_t data_len, const void* secret,
size_t secret_len, const HashAlgorithm hash_algorithm,
isc::util::OutputBuffer& result, size_t len)
{
diff --git a/src/lib/cryptolink/cryptolink.h b/src/lib/cryptolink/cryptolink.h
index 1583136..d0f7d38 100644
--- a/src/lib/cryptolink/cryptolink.h
+++ b/src/lib/cryptolink/cryptolink.h
@@ -29,15 +29,19 @@ namespace cryptolink {
/// \brief Hash algorithm identifiers
enum HashAlgorithm {
- MD5 = 0, ///< MD5
- SHA1 = 1, ///< SHA-1
- SHA256 = 2, ///< SHA-256
- UNKNOWN_HASH = 3 ///< This value can be used in conversion
+ UNKNOWN_HASH = 0, ///< This value can be used in conversion
/// functions, to be returned when the
/// input is unknown (but a value MUST be
/// returned), for instance when the input
/// is a Name or a string, and the return
/// value is a HashAlgorithm.
+ MD5 = 1, ///< MD5
+ SHA1 = 2, ///< SHA-1
+ SHA256 = 3, ///< SHA-256
+ SHA224 = 4, ///< SHA-224
+ SHA384 = 5, ///< SHA-384
+ SHA512 = 6 ///< SHA-512
+
};
// Forward declaration for createHMAC()
diff --git a/src/lib/cryptolink/tests/Makefile.am b/src/lib/cryptolink/tests/Makefile.am
index c8b5e26..1b029d5 100644
--- a/src/lib/cryptolink/tests/Makefile.am
+++ b/src/lib/cryptolink/tests/Makefile.am
@@ -19,7 +19,7 @@ run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
run_unittests_LDADD = $(GTEST_LDADD)
run_unittests_LDADD += $(top_builddir)/src/lib/cryptolink/libcryptolink.la
-run_unittests_LDADD += $(top_builddir)/src/lib/util/libutil.la
+run_unittests_LDADD += $(top_builddir)/src/lib/util/unittests/libutil_unittests.la
run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
endif
diff --git a/src/lib/cryptolink/tests/crypto_unittests.cc b/src/lib/cryptolink/tests/crypto_unittests.cc
index a1ffaab..339eb1b 100644
--- a/src/lib/cryptolink/tests/crypto_unittests.cc
+++ b/src/lib/cryptolink/tests/crypto_unittests.cc
@@ -340,19 +340,84 @@ TEST(CryptoLinkTest, DISABLED_HMAC_SHA1_RFC2202_SIGN_TRUNCATED) {
//
// Test values taken from RFC 4231
//
-TEST(CryptoLinkTest, HMAC_SHA256_RFC2202_SIGN) {
- const uint8_t secret[] = { 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
- 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
- 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b };
- const uint8_t hmac_expected[] = { 0xb0, 0x34, 0x4c, 0x61, 0xd8,
+//
+// Test data from RFC4231, including secret key
+// and source data, they are common for sha224/256/384/512
+// so put them together in seperate space
+namespace {
+ static const uint8_t secret1[] = {
+ 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+ 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+ 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b
+ };
+
+ static const uint8_t secret2[] = "Jefe";
+ static const uint8_t secret3[] = {
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa
+ };
+ static const uint8_t secret4[] = {
+ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
+ 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c,
+ 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12,
+ 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
+ 0x19
+ };
+ static const uint8_t secret5[] = {
+ 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+ 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+ 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+ 0x0c, 0x0c
+ };
+
+ static uint8_t secret6[131];
+ static uint8_t secret7[131];
+
+ static const std::string data1("Hi There");
+ static const std::string data2("what do ya want for nothing?");
+ static const std::string data3(std::string(50, 0xdd));
+ static const std::string data4(std::string(50, 0xcd));
+ static const std::string data5("Test With Truncation");
+ static const std::string data6("Test Using Larger Than Block-Size Key - Hash Key First");
+ static const std::string data7("This is a test using a larger than block-size key and a"
+ " larger than block-size data. The key needs to be hashe"
+ "d before being used by the HMAC algorithm.");
+#define SECRECT(n) secret##n
+#define DATA(n) data##n
+#define HMAC_EXPECTED(n) hmac_expected##n
+
+#define RUN_NTH_TEST_CASE_FOR_ALG(index, hash_algorithm) do {\
+ doHMACTest(DATA(index), \
+ SECRECT(index), sizeof(SECRECT(index)), \
+ (hash_algorithm), HMAC_EXPECTED(index), \
+ sizeof(HMAC_EXPECTED(index)));\
+ }while(0)
+
+#define RUN_TEST_CASES_FOR_ALG(alg) do {\
+ memcpy(secret6, std::string(131, 0xaa).c_str(), 131); \
+ memcpy(secret7, std::string(131, 0xaa).c_str(), 131); \
+ RUN_NTH_TEST_CASE_FOR_ALG(1, alg); \
+ RUN_NTH_TEST_CASE_FOR_ALG(2, alg); \
+ RUN_NTH_TEST_CASE_FOR_ALG(3, alg); \
+ RUN_NTH_TEST_CASE_FOR_ALG(4, alg); \
+ RUN_NTH_TEST_CASE_FOR_ALG(5, alg); \
+ RUN_NTH_TEST_CASE_FOR_ALG(6, alg); \
+ RUN_NTH_TEST_CASE_FOR_ALG(7, alg); \
+ }while(0)
+
+
+};
+
+TEST(CryptoLinkTest, HMAC_SHA256_RFC4231_SIGN) {
+ const uint8_t hmac_expected1[] = { 0xb0, 0x34, 0x4c, 0x61, 0xd8,
0xdb, 0x38, 0x53, 0x5c, 0xa8,
0xaf, 0xce, 0xaf, 0x0b, 0xf1,
0x2b, 0x88, 0x1d, 0xc2, 0x00,
0xc9, 0x83, 0x3d, 0xa7, 0x26,
0xe9, 0x37, 0x6c, 0x2e, 0x32,
0xcf, 0xf7 };
- doHMACTest("Hi There", secret, 20, SHA256, hmac_expected, 32);
-
const uint8_t hmac_expected2[] = { 0x5b, 0xdc, 0xc1, 0x46, 0xbf,
0x60, 0x75, 0x4e, 0x6a, 0x04,
0x24, 0x26, 0x08, 0x95, 0x75,
@@ -360,13 +425,6 @@ TEST(CryptoLinkTest, HMAC_SHA256_RFC2202_SIGN) {
0x9d, 0x27, 0x39, 0x83, 0x9d,
0xec, 0x58, 0xb9, 0x64, 0xec,
0x38, 0x43 };
- doHMACTest("what do ya want for nothing?", "Jefe", 4, SHA256,
- hmac_expected2, 32);
-
- const uint8_t secret3[] = { 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
- 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
- 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
- 0xaa, 0xaa };
const uint8_t hmac_expected3[] = { 0x77, 0x3e, 0xa9, 0x1e, 0x36,
0x80, 0x0e, 0x46, 0x85, 0x4d,
0xb8, 0xeb, 0xd0, 0x91, 0x81,
@@ -374,13 +432,6 @@ TEST(CryptoLinkTest, HMAC_SHA256_RFC2202_SIGN) {
0x3e, 0xf8, 0xc1, 0x22, 0xd9,
0x63, 0x55, 0x14, 0xce, 0xd5,
0x65, 0xfe };
- doHMACTest(std::string(50, 0xdd), secret3, 20, SHA256, hmac_expected3, 32);
-
- const uint8_t secret4[] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
- 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c,
- 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12,
- 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
- 0x19 };
const uint8_t hmac_expected4[] = { 0x82, 0x55, 0x8a, 0x38, 0x9a,
0x44, 0x3c, 0x0e, 0xa4, 0xcc,
0x81, 0x98, 0x99, 0xf2, 0x08,
@@ -388,8 +439,10 @@ TEST(CryptoLinkTest, HMAC_SHA256_RFC2202_SIGN) {
0xe5, 0x78, 0xf8, 0x07, 0x7a,
0x2e, 0x3f, 0xf4, 0x67, 0x29,
0x66, 0x5b };
- doHMACTest(std::string(50, 0xcd), secret4, 25, SHA256, hmac_expected4, 32);
-
+// const uint8_t hmac_expected5[] = { 0xa3, 0xb6, 0x16, 0x74, 0x73,
+// 0x10, 0x0e, 0xe0, 0x6e, 0x0c,
+// 0x79, 0x6c, 0x29, 0x55, 0x55,
+// 0x2b };
const uint8_t hmac_expected6[] = { 0x60, 0xe4, 0x31, 0x59, 0x1e,
0xe0, 0xb6, 0x7f, 0x0d, 0x8a,
0x26, 0xaa, 0xcb, 0xf5, 0xb7,
@@ -397,9 +450,6 @@ TEST(CryptoLinkTest, HMAC_SHA256_RFC2202_SIGN) {
0x37, 0x28, 0xc5, 0x14, 0x05,
0x46, 0x04, 0x0f, 0x0e, 0xe3,
0x7f, 0x54 };
- doHMACTest("Test Using Larger Than Block-Size Key - Hash Key First",
- std::string(131, 0xaa).c_str(), 131, SHA256, hmac_expected6, 32);
-
const uint8_t hmac_expected7[] = { 0x9b, 0x09, 0xff, 0xa7, 0x1b,
0x94, 0x2f, 0xcb, 0x27, 0x63,
0x5f, 0xbc, 0xd5, 0xb0, 0xe9,
@@ -407,10 +457,197 @@ TEST(CryptoLinkTest, HMAC_SHA256_RFC2202_SIGN) {
0x4f, 0x07, 0x13, 0x93, 0x8a,
0x7f, 0x51, 0x53, 0x5c, 0x3a,
0x35, 0xe2 };
- doHMACTest("This is a test using a larger than block-size key and a"
- " larger than block-size data. The key needs to be hashe"
- "d before being used by the HMAC algorithm.",
- std::string(131, 0xaa).c_str(), 131, SHA256, hmac_expected7, 32);
+
+ memcpy(secret6, std::string(131, 0xaa).c_str(), 131);
+ memcpy(secret7, std::string(131, 0xaa).c_str(), 131);
+ RUN_NTH_TEST_CASE_FOR_ALG(1, SHA256);
+ RUN_NTH_TEST_CASE_FOR_ALG(2, SHA256);
+ RUN_NTH_TEST_CASE_FOR_ALG(3, SHA256);
+ RUN_NTH_TEST_CASE_FOR_ALG(4, SHA256);
+ RUN_NTH_TEST_CASE_FOR_ALG(6, SHA256);
+ RUN_NTH_TEST_CASE_FOR_ALG(7, SHA256);
+
+}
+
+
+//
+// Test values taken from RFC 4231, test optional algorithm 224,384,512
+//
+TEST(CryptoLinkTest, DISABLED_HMAC_SHA224_RFC4231_SIGN) {
+ const uint8_t hmac_expected1[] = {
+ 0x89,0x6f,0xb1,0x12,0x8a,0xbb,0xdf,0x19,0x68,0x32,0x10,0x7c,
+ 0xd4,0x9d,0xf3,0x3f,0x47,0xb4,0xb1,0x16,0x99,0x12,0xba,0x4f,
+ 0x53,0x68,0x4b,0x22
+ };
+ const uint8_t hmac_expected2[] = {
+ 0xa3,0x0e,0x01,0x09,0x8b,0xc6,0xdb,0xbf,0x45,0x69,0x0f,0x3a,
+ 0x7e,0x9e,0x6d,0x0f,0x8b,0xbe,0xa2,0xa3,0x9e,0x61,0x48,0x00,
+ 0x8f,0xd0,0x5e,0x44
+ };
+
+ const uint8_t hmac_expected3[] = {
+ 0x7f,0xb3,0xcb,0x35,0x88,0xc6,0xc1,0xf6,0xff,0xa9,0x69,0x4d,
+ 0x7d,0x6a,0xd2,0x64,0x93,0x65,0xb0,0xc1,0xf6,0x5d,0x69,0xd1,
+ 0xec,0x83,0x33,0xea
+ };
+
+ const uint8_t hmac_expected4[] = {
+ 0x6c,0x11,0x50,0x68,0x74,0x01,0x3c,0xac,0x6a,0x2a,0xbc,0x1b,
+ 0xb3,0x82,0x62,0x7c,0xec,0x6a,0x90,0xd8,0x6e,0xfc,0x01,0x2d,
+ 0xe7,0xaf,0xec,0x5a
+ };
+
+// const uint8_t hmac_expected5[] = {
+// 0x0e,0x2a,0xea,0x68,0xa9,0x0c,0x8d,0x37,0xc9,0x88,0xbc,0xdb,0x9f,
+// 0xca,0x6f,0xa8
+// };
+
+ const uint8_t hmac_expected6[] = {
+ 0x95,0xe9,0xa0,0xdb,0x96,0x20,0x95,0xad,0xae,0xbe,0x9b,0x2d,0x6f,
+ 0x0d,0xbc,0xe2,0xd4,0x99,0xf1,0x12,0xf2,0xd2,0xb7,0x27,0x3f,0xa6,
+ 0x87,0x0e
+ };
+
+ const uint8_t hmac_expected7[] = {
+ 0x3a,0x85,0x41,0x66,0xac,0x5d,0x9f,0x02,0x3f,0x54,0xd5,0x17,0xd0,
+ 0xb3,0x9d,0xbd,0x94,0x67,0x70,0xdb,0x9c,0x2b,0x95,0xc9,0xf6,0xf5,
+ 0x65,0xd1
+ };
+
+ memcpy(secret6, std::string(131, 0xaa).c_str(), 131);
+ memcpy(secret7, std::string(131, 0xaa).c_str(), 131);
+ RUN_NTH_TEST_CASE_FOR_ALG(1, SHA224);
+ RUN_NTH_TEST_CASE_FOR_ALG(2, SHA224);
+ RUN_NTH_TEST_CASE_FOR_ALG(3, SHA224);
+ RUN_NTH_TEST_CASE_FOR_ALG(4, SHA224);
+ RUN_NTH_TEST_CASE_FOR_ALG(6, SHA224);
+ RUN_NTH_TEST_CASE_FOR_ALG(7, SHA224);
+}
+
+
+TEST(CryptoLinkTest, HMAC_SHA384_RFC4231_SIGN) {
+ const uint8_t hmac_expected1[] = {
+ 0xaf,0xd0,0x39,0x44,0xd8,0x48,0x95,0x62,0x6b,0x08,0x25,0xf4,
+ 0xab,0x46,0x90,0x7f,0x15,0xf9,0xda,0xdb,0xe4,0x10,0x1e,0xc6,
+ 0x82,0xaa,0x03,0x4c,0x7c,0xeb,0xc5,0x9c,0xfa,0xea,0x9e,0xa9,
+ 0x07,0x6e,0xde,0x7f,0x4a,0xf1,0x52,0xe8,0xb2,0xfa,0x9c,0xb6
+ };
+
+ const uint8_t hmac_expected2[] = {
+ 0xaf,0x45,0xd2,0xe3,0x76,0x48,0x40,0x31,0x61,0x7f,0x78,0xd2,
+ 0xb5,0x8a,0x6b,0x1b,0x9c,0x7e,0xf4,0x64,0xf5,0xa0,0x1b,0x47,
+ 0xe4,0x2e,0xc3,0x73,0x63,0x22,0x44,0x5e,0x8e,0x22,0x40,0xca,
+ 0x5e,0x69,0xe2,0xc7,0x8b,0x32,0x39,0xec,0xfa,0xb2,0x16,0x49
+ };
+
+ const uint8_t hmac_expected3[] = {
+ 0x88,0x06,0x26,0x08,0xd3,0xe6,0xad,0x8a,0x0a,0xa2,0xac,0xe0,
+ 0x14,0xc8,0xa8,0x6f,0x0a,0xa6,0x35,0xd9,0x47,0xac,0x9f,0xeb,
+ 0xe8,0x3e,0xf4,0xe5,0x59,0x66,0x14,0x4b,0x2a,0x5a,0xb3,0x9d,
+ 0xc1,0x38,0x14,0xb9,0x4e,0x3a,0xb6,0xe1,0x01,0xa3,0x4f,0x27
+ };
+
+ const uint8_t hmac_expected4[] = {
+ 0x3e,0x8a,0x69,0xb7,0x78,0x3c,0x25,0x85,0x19,0x33,0xab,0x62,
+ 0x90,0xaf,0x6c,0xa7,0x7a,0x99,0x81,0x48,0x08,0x50,0x00,0x9c,
+ 0xc5,0x57,0x7c,0x6e,0x1f,0x57,0x3b,0x4e,0x68,0x01,0xdd,0x23,
+ 0xc4,0xa7,0xd6,0x79,0xcc,0xf8,0xa3,0x86,0xc6,0x74,0xcf,0xfb,
+ };
+
+// const uint8_t hmac_expected5[] = {
+// 0x3a,0xbf,0x34,0xc3,0x50,0x3b,0x2a,0x23,0xa4,0x6e,0xfc,0x61,0x9b,
+// 0xae,0xf8,0x97,
+// };
+
+ const uint8_t hmac_expected6[] = {
+ 0x4e,0xce,0x08,0x44,0x85,0x81,0x3e,0x90,0x88,0xd2,0xc6,0x3a,0x04,
+ 0x1b,0xc5,0xb4,0x4f,0x9e,0xf1,0x01,0x2a,0x2b,0x58,0x8f,0x3c,0xd1,
+ 0x1f,0x05,0x03,0x3a,0xc4,0xc6,0x0c,0x2e,0xf6,0xab,0x40,0x30,0xfe,
+ 0x82,0x96,0x24,0x8d,0xf1,0x63,0xf4,0x49,0x52
+ };
+
+ const uint8_t hmac_expected7[] = {
+ 0x66,0x17,0x17,0x8e,0x94,0x1f,0x02,0x0d,0x35,0x1e,0x2f,0x25,0x4e,
+ 0x8f,0xd3,0x2c,0x60,0x24,0x20,0xfe,0xb0,0xb8,0xfb,0x9a,0xdc,0xce,
+ 0xbb,0x82,0x46,0x1e,0x99,0xc5,0xa6,0x78,0xcc,0x31,0xe7,0x99,0x17,
+ 0x6d,0x38,0x60,0xe6,0x11,0x0c,0x46,0x52,0x3e
+ };
+
+ memcpy(secret6, std::string(131, 0xaa).c_str(), 131);
+ memcpy(secret7, std::string(131, 0xaa).c_str(), 131);
+ RUN_NTH_TEST_CASE_FOR_ALG(1, SHA384);
+ RUN_NTH_TEST_CASE_FOR_ALG(2, SHA384);
+ RUN_NTH_TEST_CASE_FOR_ALG(3, SHA384);
+ RUN_NTH_TEST_CASE_FOR_ALG(4, SHA384);
+ RUN_NTH_TEST_CASE_FOR_ALG(6, SHA384);
+ RUN_NTH_TEST_CASE_FOR_ALG(7, SHA384);
+}
+
+TEST(CryptoLinkTest, HMAC_SHA512_RFC4231_SIGN) {
+ const uint8_t hmac_expected1[] = {
+ 0x87,0xaa,0x7c,0xde,0xa5,0xef,0x61,0x9d,0x4f,0xf0,0xb4,0x24,
+ 0x1a,0x1d,0x6c,0xb0,0x23,0x79,0xf4,0xe2,0xce,0x4e,0xc2,0x78,
+ 0x7a,0xd0,0xb3,0x05,0x45,0xe1,0x7c,0xde,0xda,0xa8,0x33,0xb7,
+ 0xd6,0xb8,0xa7,0x02,0x03,0x8b,0x27,0x4e,0xae,0xa3,0xf4,0xe4,
+ 0xbe,0x9d,0x91,0x4e,0xeb,0x61,0xf1,0x70,0x2e,0x69,0x6c,0x20,
+ 0x3a,0x12,0x68,0x54
+ };
+ const uint8_t hmac_expected2[] = {
+ 0x16,0x4b,0x7a,0x7b,0xfc,0xf8,0x19,0xe2,0xe3,0x95,0xfb,0xe7,
+ 0x3b,0x56,0xe0,0xa3,0x87,0xbd,0x64,0x22,0x2e,0x83,0x1f,0xd6,
+ 0x10,0x27,0x0c,0xd7,0xea,0x25,0x05,0x54,0x97,0x58,0xbf,0x75,
+ 0xc0,0x5a,0x99,0x4a,0x6d,0x03,0x4f,0x65,0xf8,0xf0,0xe6,0xfd,
+ 0xca,0xea,0xb1,0xa3,0x4d,0x4a,0x6b,0x4b,0x63,0x6e,0x07,0x0a,
+ 0x38,0xbc,0xe7,0x37
+ };
+
+ const uint8_t hmac_expected3[] = {
+ 0xfa,0x73,0xb0,0x08,0x9d,0x56,0xa2,0x84,0xef,0xb0,0xf0,0x75,
+ 0x6c,0x89,0x0b,0xe9,0xb1,0xb5,0xdb,0xdd,0x8e,0xe8,0x1a,0x36,
+ 0x55,0xf8,0x3e,0x33,0xb2,0x27,0x9d,0x39,0xbf,0x3e,0x84,0x82,
+ 0x79,0xa7,0x22,0xc8,0x06,0xb4,0x85,0xa4,0x7e,0x67,0xc8,0x07,
+ 0xb9,0x46,0xa3,0x37,0xbe,0xe8,0x94,0x26,0x74,0x27,0x88,0x59,
+ 0xe1,0x32,0x92,0xfb
+ };
+
+ const uint8_t hmac_expected4[] = {
+ 0xb0,0xba,0x46,0x56,0x37,0x45,0x8c,0x69,0x90,0xe5,0xa8,0xc5,
+ 0xf6,0x1d,0x4a,0xf7,0xe5,0x76,0xd9,0x7f,0xf9,0x4b,0x87,0x2d,
+ 0xe7,0x6f,0x80,0x50,0x36,0x1e,0xe3,0xdb,0xa9,0x1c,0xa5,0xc1,
+ 0x1a,0xa2,0x5e,0xb4,0xd6,0x79,0x27,0x5c,0xc5,0x78,0x80,0x63,
+ 0xa5,0xf1,0x97,0x41,0x12,0x0c,0x4f,0x2d,0xe2,0xad,0xeb,0xeb,
+ 0x10,0xa2,0x98,0xdd
+ };
+
+// const uint8_t hmac_expected5[] = {
+// 0x41,0x5f,0xad,0x62,0x71,0x58,0x0a,0x53,0x1d,0x41,0x79,0xbc,0x89,
+// 0x1d,0x87,0xa6,
+// };
+
+ const uint8_t hmac_expected6[] = {
+ 0x80,0xb2,0x42,0x63,0xc7,0xc1,0xa3,0xeb,0xb7,0x14,0x93,0xc1,0xdd,
+ 0x7b,0xe8,0xb4,0x9b,0x46,0xd1,0xf4,0x1b,0x4a,0xee,0xc1,0x12,0x1b,
+ 0x01,0x37,0x83,0xf8,0xf3,0x52,0x6b,0x56,0xd0,0x37,0xe0,0x5f,0x25,
+ 0x98,0xbd,0x0f,0xd2,0x21,0x5d,0x6a,0x1e,0x52,0x95,0xe6,0x4f,0x73,
+ 0xf6,0x3f,0x0a,0xec,0x8b,0x91,0x5a,0x98,0x5d,0x78,0x65,0x98
+ };
+
+ const uint8_t hmac_expected7[] = {
+ 0xe3,0x7b,0x6a,0x77,0x5d,0xc8,0x7d,0xba,0xa4,0xdf,0xa9,0xf9,0x6e,
+ 0x5e,0x3f,0xfd,0xde,0xbd,0x71,0xf8,0x86,0x72,0x89,0x86,0x5d,0xf5,
+ 0xa3,0x2d,0x20,0xcd,0xc9,0x44,0xb6,0x02,0x2c,0xac,0x3c,0x49,0x82,
+ 0xb1,0x0d,0x5e,0xeb,0x55,0xc3,0xe4,0xde,0x15,0x13,0x46,0x76,0xfb,
+ 0x6d,0xe0,0x44,0x60,0x65,0xc9,0x74,0x40,0xfa,0x8c,0x6a,0x58
+ };
+
+ memcpy(secret6, std::string(131, 0xaa).c_str(), 131);
+ memcpy(secret7, std::string(131, 0xaa).c_str(), 131);
+ RUN_NTH_TEST_CASE_FOR_ALG(1, SHA512);
+ RUN_NTH_TEST_CASE_FOR_ALG(2, SHA512);
+ RUN_NTH_TEST_CASE_FOR_ALG(3, SHA512);
+ RUN_NTH_TEST_CASE_FOR_ALG(4, SHA512);
+ RUN_NTH_TEST_CASE_FOR_ALG(6, SHA512);
+ RUN_NTH_TEST_CASE_FOR_ALG(7, SHA512);
}
TEST(CryptoLinkTest, DISABLED_HMAC_SHA256_RFC2202_SIGN_TRUNCATED) {
diff --git a/src/lib/cryptolink/tests/run_unittests.cc b/src/lib/cryptolink/tests/run_unittests.cc
index d16327e..a2181cf 100644
--- a/src/lib/cryptolink/tests/run_unittests.cc
+++ b/src/lib/cryptolink/tests/run_unittests.cc
@@ -13,10 +13,11 @@
// PERFORMANCE OF THIS SOFTWARE.
#include <gtest/gtest.h>
+#include <util/unittests/run_all.h>
int
main(int argc, char* argv[]) {
::testing::InitGoogleTest(&argc, argv);
- return (RUN_ALL_TESTS());
+ return (isc::util::unittests::run_all());
}
diff --git a/src/lib/datasrc/tests/Makefile.am b/src/lib/datasrc/tests/Makefile.am
index ad4374a..471d802 100644
--- a/src/lib/datasrc/tests/Makefile.am
+++ b/src/lib/datasrc/tests/Makefile.am
@@ -38,6 +38,7 @@ run_unittests_LDADD += $(top_builddir)/src/lib/log/liblog.la
run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
run_unittests_LDADD += $(top_builddir)/src/lib/cc/libcc.la
run_unittests_LDADD += $(top_builddir)/src/lib/testutils/libtestutils.la
+run_unittests_LDADD += $(top_builddir)/src/lib/util/unittests/libutil_unittests.la
endif
noinst_PROGRAMS = $(TESTS)
diff --git a/src/lib/datasrc/tests/run_unittests.cc b/src/lib/datasrc/tests/run_unittests.cc
index a35a646..d7a1ab8 100644
--- a/src/lib/datasrc/tests/run_unittests.cc
+++ b/src/lib/datasrc/tests/run_unittests.cc
@@ -13,6 +13,7 @@
// PERFORMANCE OF THIS SOFTWARE.
#include <gtest/gtest.h>
+#include <util/unittests/run_all.h>
#include <dns/tests/unittest_util.h>
@@ -21,5 +22,5 @@ main(int argc, char* argv[]) {
::testing::InitGoogleTest(&argc, argv);
isc::UnitTestUtil::addDataPath(TEST_DATA_DIR);
- return (RUN_ALL_TESTS());
+ return (isc::util::unittests::run_all());
}
diff --git a/src/lib/dns/python/Makefile.am b/src/lib/dns/python/Makefile.am
index 1c9afc7..aa9d062 100644
--- a/src/lib/dns/python/Makefile.am
+++ b/src/lib/dns/python/Makefile.am
@@ -5,10 +5,16 @@ AM_CPPFLAGS += $(BOOST_INCLUDES)
AM_CXXFLAGS = $(B10_CXXFLAGS)
pyexec_LTLIBRARIES = pydnspp.la
-pydnspp_la_SOURCES = pydnspp.cc pydnspp_common.cc
+pydnspp_la_SOURCES = pydnspp.cc pydnspp_common.cc pydnspp_towire.h
+pydnspp_la_SOURCES += name_python.cc name_python.h
+pydnspp_la_SOURCES += messagerenderer_python.cc messagerenderer_python.h
pydnspp_la_SOURCES += rcode_python.cc rcode_python.h
+pydnspp_la_SOURCES += tsigkey_python.cc tsigkey_python.h
pydnspp_la_SOURCES += tsigerror_python.cc tsigerror_python.h
-pydnspp_la_SOURCES += tsigerror_python_inc.cc
+pydnspp_la_SOURCES += tsig_rdata_python.cc tsig_rdata_python.h
+pydnspp_la_SOURCES += tsigrecord_python.cc tsigrecord_python.h
+pydnspp_la_SOURCES += tsig_python.cc tsig_python.h
+
pydnspp_la_CPPFLAGS = $(AM_CPPFLAGS) $(PYTHON_INCLUDES)
pydnspp_la_LDFLAGS = $(PYTHON_LDFLAGS)
@@ -16,19 +22,15 @@ pydnspp_la_LDFLAGS = $(PYTHON_LDFLAGS)
# rules
EXTRA_DIST = pydnspp_common.h
EXTRA_DIST += edns_python.cc
-EXTRA_DIST += messagerenderer_python.cc
EXTRA_DIST += message_python.cc
EXTRA_DIST += rrclass_python.cc
-EXTRA_DIST += name_python.cc
EXTRA_DIST += opcode_python.cc
-EXTRA_DIST += rcode_python.cc
EXTRA_DIST += rrset_python.cc
EXTRA_DIST += question_python.cc
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
+EXTRA_DIST += tsigerror_python_inc.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/edns_python.cc b/src/lib/dns/python/edns_python.cc
index d781e89..83c3bfa 100644
--- a/src/lib/dns/python/edns_python.cc
+++ b/src/lib/dns/python/edns_python.cc
@@ -108,7 +108,7 @@ PyMethodDef EDNS_methods[] = {
// Most of the functions are not actually implemented and NULL here.
PyTypeObject edns_type = {
PyVarObject_HEAD_INIT(NULL, 0)
- "libdns_python.EDNS",
+ "pydnspp.EDNS",
sizeof(s_EDNS), // tp_basicsize
0, // tp_itemsize
(destructor)EDNS_destroy, // tp_dealloc
@@ -203,7 +203,7 @@ EDNS_init(s_EDNS* self, PyObject* args) {
// in this context so that we can share the try-catch logic with
// EDNS_createFromRR() (see below).
uint8_t extended_rcode;
- self->edns = createFromRR(*name->name, *rrclass->rrclass,
+ self->edns = createFromRR(*name->cppobj, *rrclass->rrclass,
*rrtype->rrtype, *rrttl->rrttl,
*rdata->rdata, extended_rcode);
return (self->edns != NULL ? 0 : -1);
@@ -334,7 +334,7 @@ EDNS_createFromRR(const s_EDNS* null_self, PyObject* args) {
return (NULL);
}
- edns_obj->edns = createFromRR(*name->name, *rrclass->rrclass,
+ edns_obj->edns = createFromRR(*name->cppobj, *rrclass->rrclass,
*rrtype->rrtype, *rrttl->rrttl,
*rdata->rdata, extended_rcode);
if (edns_obj->edns != NULL) {
diff --git a/src/lib/dns/python/message_python.cc b/src/lib/dns/python/message_python.cc
index 381b7bc..2842588 100644
--- a/src/lib/dns/python/message_python.cc
+++ b/src/lib/dns/python/message_python.cc
@@ -14,6 +14,9 @@
#include <exceptions/exceptions.h>
#include <dns/message.h>
+#include <dns/rcode.h>
+#include <dns/tsig.h>
+
using namespace isc::dns;
using namespace isc::util;
@@ -65,6 +68,7 @@ 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_getTSIGRecord(s_Message* self);
PyObject* Message_getRRCount(s_Message* self, PyObject* args);
// use direct iterators for these? (or simply lists for now?)
PyObject* Message_getQuestion(s_Message* self);
@@ -123,6 +127,11 @@ PyMethodDef Message_methods[] = {
{ "set_edns", reinterpret_cast<PyCFunction>(Message_setEDNS), METH_VARARGS,
"Set EDNS for the message."
},
+ { "get_tsig_record",
+ reinterpret_cast<PyCFunction>(Message_getTSIGRecord), METH_NOARGS,
+ "Return, if any, the TSIG record contained in the received message. "
+ "If no TSIG RR is set in the message, None will be returned."
+ },
{ "get_rr_count", reinterpret_cast<PyCFunction>(Message_getRRCount), METH_VARARGS,
"Returns the number of RRs contained in the given section." },
{ "get_question", reinterpret_cast<PyCFunction>(Message_getQuestion), METH_NOARGS,
@@ -442,6 +451,29 @@ Message_setEDNS(s_Message* self, PyObject* args) {
}
PyObject*
+Message_getTSIGRecord(s_Message* self) {
+ try {
+ const TSIGRecord* tsig_record = self->message->getTSIGRecord();
+
+ if (tsig_record == NULL) {
+ Py_RETURN_NONE;
+ }
+ return (createTSIGRecordObject(*tsig_record));
+ } catch (const InvalidMessageOperation& ex) {
+ PyErr_SetString(po_InvalidMessageOperation, ex.what());
+ } catch (const exception& ex) {
+ const string ex_what =
+ "Unexpected failure in getting TSIGRecord from message: " +
+ string(ex.what());
+ PyErr_SetString(po_IscException, ex_what.c_str());
+ } catch (...) {
+ PyErr_SetString(PyExc_SystemError, "Unexpected failure in "
+ "getting TSIGRecord from message");
+ }
+ return (NULL);
+}
+
+PyObject*
Message_getRRCount(s_Message* self, PyObject* args) {
unsigned int section;
if (!PyArg_ParseTuple(args, "I", §ion)) {
@@ -652,13 +684,12 @@ Message_toWire(s_Message* self, PyObject* args) {
s_TSIGContext* tsig_ctx = NULL;
if (PyArg_ParseTuple(args, "O!|O!", &messagerenderer_type, &mr,
- &tsig_context_type, &tsig_ctx)) {
+ &tsigcontext_type, &tsig_ctx)) {
try {
if (tsig_ctx == NULL) {
self->message->toWire(*mr->messagerenderer);
} else {
- self->message->toWire(*mr->messagerenderer,
- *tsig_ctx->tsig_ctx);
+ self->message->toWire(*mr->messagerenderer, *tsig_ctx->cppobj);
}
// If we return NULL it is seen as an error, so use this for
// None returns
@@ -667,6 +698,11 @@ Message_toWire(s_Message* self, PyObject* args) {
PyErr_Clear();
PyErr_SetString(po_InvalidMessageOperation, imo.what());
return (NULL);
+ } catch (const TSIGContextError& ex) {
+ // toWire() with a TSIG context can fail due to this if the
+ // python program has a bug.
+ PyErr_SetString(po_TSIGContextError, ex.what());
+ return (NULL);
}
}
PyErr_Clear();
diff --git a/src/lib/dns/python/messagerenderer_python.cc b/src/lib/dns/python/messagerenderer_python.cc
index 85a4f17..e6f5d3e 100644
--- a/src/lib/dns/python/messagerenderer_python.cc
+++ b/src/lib/dns/python/messagerenderer_python.cc
@@ -12,39 +12,41 @@
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
+#include <Python.h>
+
+#include <util/buffer.h>
+
#include <dns/messagerenderer.h>
-// 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
+#include "pydnspp_common.h"
+#include "messagerenderer_python.h"
+
using namespace isc::dns;
+using namespace isc::dns::python;
using namespace isc::util;
// MessageRenderer
-// since we don't use *Buffer in the python version (but work with
-// the already existing bytearray type where we use these custom buffers
-// in c++, we need to keep track of one here.
-class s_MessageRenderer : public PyObject {
-public:
- OutputBuffer* outputbuffer;
- MessageRenderer* messagerenderer;
-};
+s_MessageRenderer::s_MessageRenderer() : outputbuffer(NULL),
+ messagerenderer(NULL)
+{
+}
-static int MessageRenderer_init(s_MessageRenderer* self);
-static void MessageRenderer_destroy(s_MessageRenderer* self);
+namespace {
+int MessageRenderer_init(s_MessageRenderer* self);
+void MessageRenderer_destroy(s_MessageRenderer* self);
-static PyObject* MessageRenderer_getData(s_MessageRenderer* self);
-static PyObject* MessageRenderer_getLength(s_MessageRenderer* self);
-static PyObject* MessageRenderer_isTruncated(s_MessageRenderer* self);
-static PyObject* MessageRenderer_getLengthLimit(s_MessageRenderer* self);
-static PyObject* MessageRenderer_getCompressMode(s_MessageRenderer* self);
-static PyObject* MessageRenderer_setTruncated(s_MessageRenderer* self);
-static PyObject* MessageRenderer_setLengthLimit(s_MessageRenderer* self, PyObject* args);
-static PyObject* MessageRenderer_setCompressMode(s_MessageRenderer* self, PyObject* args);
-static PyObject* MessageRenderer_clear(s_MessageRenderer* self);
+PyObject* MessageRenderer_getData(s_MessageRenderer* self);
+PyObject* MessageRenderer_getLength(s_MessageRenderer* self);
+PyObject* MessageRenderer_isTruncated(s_MessageRenderer* self);
+PyObject* MessageRenderer_getLengthLimit(s_MessageRenderer* self);
+PyObject* MessageRenderer_getCompressMode(s_MessageRenderer* self);
+PyObject* MessageRenderer_setTruncated(s_MessageRenderer* self);
+PyObject* MessageRenderer_setLengthLimit(s_MessageRenderer* self, PyObject* args);
+PyObject* MessageRenderer_setCompressMode(s_MessageRenderer* self, PyObject* args);
+PyObject* MessageRenderer_clear(s_MessageRenderer* self);
-static PyMethodDef MessageRenderer_methods[] = {
+PyMethodDef MessageRenderer_methods[] = {
{ "get_data", reinterpret_cast<PyCFunction>(MessageRenderer_getData), METH_NOARGS,
"Returns the data as a bytes() object" },
{ "get_length", reinterpret_cast<PyCFunction>(MessageRenderer_getLength), METH_NOARGS,
@@ -67,69 +69,14 @@ static PyMethodDef MessageRenderer_methods[] = {
{ NULL, NULL, 0, NULL }
};
-static PyTypeObject messagerenderer_type = {
- PyVarObject_HEAD_INIT(NULL, 0)
- "pydnspp.MessageRenderer",
- sizeof(s_MessageRenderer), // tp_basicsize
- 0, // tp_itemsize
- (destructor)MessageRenderer_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 MessageRenderer class encapsulates implementation details "
- "of rendering a DNS message into a buffer in wire format. "
- "In effect, it's simply responsible for name compression at least in the "
- "current implementation. A MessageRenderer class object manages the "
- "positions of names rendered in a buffer and uses that information to render "
- "subsequent names with compression.",
- NULL, // tp_traverse
- NULL, // tp_clear
- NULL, // tp_richcompare
- 0, // tp_weaklistoffset
- NULL, // tp_iter
- NULL, // tp_iternext
- MessageRenderer_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)MessageRenderer_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
-};
-
-static int
+int
MessageRenderer_init(s_MessageRenderer* self) {
self->outputbuffer = new OutputBuffer(4096);
self->messagerenderer = new MessageRenderer(*self->outputbuffer);
return (0);
}
-static void
+void
MessageRenderer_destroy(s_MessageRenderer* self) {
delete self->messagerenderer;
delete self->outputbuffer;
@@ -138,19 +85,19 @@ MessageRenderer_destroy(s_MessageRenderer* self) {
Py_TYPE(self)->tp_free(self);
}
-static PyObject*
+PyObject*
MessageRenderer_getData(s_MessageRenderer* self) {
return (Py_BuildValue("y#",
self->messagerenderer->getData(),
self->messagerenderer->getLength()));
}
-static PyObject*
+PyObject*
MessageRenderer_getLength(s_MessageRenderer* self) {
return (Py_BuildValue("I", self->messagerenderer->getLength()));
}
-static PyObject*
+PyObject*
MessageRenderer_isTruncated(s_MessageRenderer* self) {
if (self->messagerenderer->isTruncated()) {
Py_RETURN_TRUE;
@@ -159,23 +106,23 @@ MessageRenderer_isTruncated(s_MessageRenderer* self) {
}
}
-static PyObject*
+PyObject*
MessageRenderer_getLengthLimit(s_MessageRenderer* self) {
return (Py_BuildValue("I", self->messagerenderer->getLengthLimit()));
}
-static PyObject*
+PyObject*
MessageRenderer_getCompressMode(s_MessageRenderer* self) {
return (Py_BuildValue("I", self->messagerenderer->getCompressMode()));
}
-static PyObject*
+PyObject*
MessageRenderer_setTruncated(s_MessageRenderer* self) {
self->messagerenderer->setTruncated();
Py_RETURN_NONE;
}
-static PyObject*
+PyObject*
MessageRenderer_setLengthLimit(s_MessageRenderer* self,
PyObject* args)
{
@@ -195,7 +142,7 @@ MessageRenderer_setLengthLimit(s_MessageRenderer* self,
Py_RETURN_NONE;
}
-static PyObject*
+PyObject*
MessageRenderer_setCompressMode(s_MessageRenderer* self,
PyObject* args)
{
@@ -220,14 +167,71 @@ MessageRenderer_setCompressMode(s_MessageRenderer* self,
}
}
-static PyObject*
+PyObject*
MessageRenderer_clear(s_MessageRenderer* self) {
self->messagerenderer->clear();
Py_RETURN_NONE;
}
+} // end of unnamed namespace
// end of MessageRenderer
-
+namespace isc {
+namespace dns {
+namespace python {
+PyTypeObject messagerenderer_type = {
+ PyVarObject_HEAD_INIT(NULL, 0)
+ "pydnspp.MessageRenderer",
+ sizeof(s_MessageRenderer), // tp_basicsize
+ 0, // tp_itemsize
+ (destructor)MessageRenderer_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 MessageRenderer class encapsulates implementation details "
+ "of rendering a DNS message into a buffer in wire format. "
+ "In effect, it's simply responsible for name compression at least in the "
+ "current implementation. A MessageRenderer class object manages the "
+ "positions of names rendered in a buffer and uses that information to render "
+ "subsequent names with compression.",
+ NULL, // tp_traverse
+ NULL, // tp_clear
+ NULL, // tp_richcompare
+ 0, // tp_weaklistoffset
+ NULL, // tp_iter
+ NULL, // tp_iternext
+ MessageRenderer_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)MessageRenderer_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
+};
// Module Initialization, all statics are initialized here
bool
@@ -260,5 +264,6 @@ initModulePart_MessageRenderer(PyObject* mod) {
return (true);
}
-
-
+} // namespace python
+} // namespace dns
+} // namespace isc
diff --git a/src/lib/dns/python/messagerenderer_python.h b/src/lib/dns/python/messagerenderer_python.h
new file mode 100644
index 0000000..3bb096e
--- /dev/null
+++ b/src/lib/dns/python/messagerenderer_python.h
@@ -0,0 +1,52 @@
+// 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 __PYTHON_MESSAGERENDERER_H
+#define __PYTHON_MESSAGERENDERER_H 1
+
+#include <Python.h>
+
+namespace isc {
+namespace util {
+class OutputBuffer;
+}
+namespace dns {
+class MessageRenderer;
+
+namespace python {
+
+// The s_* Class simply covers one instantiation of the object.
+//
+// since we don't use *Buffer in the python version (but work with
+// the already existing bytearray type where we use these custom buffers
+// in C++, we need to keep track of one here.
+class s_MessageRenderer : public PyObject {
+public:
+ s_MessageRenderer();
+ isc::util::OutputBuffer* outputbuffer;
+ MessageRenderer* messagerenderer;
+};
+
+extern PyTypeObject messagerenderer_type;
+
+bool initModulePart_MessageRenderer(PyObject* mod);
+
+} // namespace python
+} // namespace dns
+} // namespace isc
+#endif // __PYTHON_MESSAGERENDERER_H
+
+// Local Variables:
+// mode: c++
+// End:
diff --git a/src/lib/dns/python/name_python.cc b/src/lib/dns/python/name_python.cc
index b030ba1..d00c6f7 100644
--- a/src/lib/dns/python/name_python.cc
+++ b/src/lib/dns/python/name_python.cc
@@ -12,26 +12,18 @@
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
-//
-// Declaration of the custom exceptions
-// Initialization and addition of these go in the module init at the
-// end
-//
-static PyObject* po_EmptyLabel;
-static PyObject* po_TooLongName;
-static PyObject* po_TooLongLabel;
-static PyObject* po_BadLabelType;
-static PyObject* po_BadEscape;
-static PyObject* po_IncompleteName;
-static PyObject* po_InvalidBufferPosition;
-static PyObject* po_DNSMessageFORMERR;
+#include <Python.h>
-//
-// Declaration of enums
-// Initialization and addition of these go in the module init at the
-// end
-//
-static PyObject* po_NameRelation;
+#include <util/buffer.h>
+#include <util/python/pycppwrapper_util.h>
+
+#include <dns/exceptions.h>
+#include <dns/messagerenderer.h>
+#include <dns/name.h>
+
+#include "pydnspp_common.h"
+#include "messagerenderer_python.h"
+#include "name_python.h"
//
// Definition of the classes
@@ -41,21 +33,20 @@ static PyObject* po_NameRelation;
// and static wrappers around the methods we export), a list of methods,
// and a type description
using namespace isc::dns;
+using namespace isc::dns::python;
using namespace isc::util;
+using namespace isc::util::python;
+namespace {
// NameComparisonResult
-class s_NameComparisonResult : public PyObject {
-public:
- isc::dns::NameComparisonResult* ncr;
-};
-static int NameComparisonResult_init(s_NameComparisonResult*, PyObject*);
-static void NameComparisonResult_destroy(s_NameComparisonResult* self);
-static PyObject* NameComparisonResult_getOrder(s_NameComparisonResult* self);
-static PyObject* NameComparisonResult_getCommonLabels(s_NameComparisonResult* self);
-static PyObject* NameComparisonResult_getRelation(s_NameComparisonResult* self);
+int NameComparisonResult_init(s_NameComparisonResult*, PyObject*);
+void NameComparisonResult_destroy(s_NameComparisonResult* self);
+PyObject* NameComparisonResult_getOrder(s_NameComparisonResult* self);
+PyObject* NameComparisonResult_getCommonLabels(s_NameComparisonResult* self);
+PyObject* NameComparisonResult_getRelation(s_NameComparisonResult* self);
-static PyMethodDef NameComparisonResult_methods[] = {
+PyMethodDef NameComparisonResult_methods[] = {
{ "get_order", reinterpret_cast<PyCFunction>(NameComparisonResult_getOrder), METH_NOARGS,
"Returns the order" },
{ "get_common_labels", reinterpret_cast<PyCFunction>(NameComparisonResult_getCommonLabels), METH_NOARGS,
@@ -65,122 +56,61 @@ static PyMethodDef NameComparisonResult_methods[] = {
{ NULL, NULL, 0, NULL }
};
-static PyTypeObject name_comparison_result_type = {
- PyVarObject_HEAD_INIT(NULL, 0)
- "pydnspp.NameComparisonResult",
- sizeof(s_NameComparisonResult), // tp_basicsize
- 0, // tp_itemsize
- (destructor)NameComparisonResult_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
- "This is a supplemental class used only as a return value of Name.compare(). "
- "It encapsulate a tuple of the comparison: ordering, number of common labels, "
- "and relationship as follows:\n"
- "- ordering: relative ordering under the DNSSEC order relation\n"
- "- labels: the number of common significant labels of the two names being"
- " compared\n"
- "- relationship: see NameComparisonResult.NameRelation\n",
- NULL, // tp_traverse
- NULL, // tp_clear
- NULL, // tp_richcompare
- 0, // tp_weaklistoffset
- NULL, // tp_iter
- NULL, // tp_iternext
- NameComparisonResult_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)NameComparisonResult_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
-};
-
-static int
+int
NameComparisonResult_init(s_NameComparisonResult*, PyObject*) {
PyErr_SetString(PyExc_NotImplementedError,
"NameComparisonResult can't be built directly");
return (-1);
}
-static void
+void
NameComparisonResult_destroy(s_NameComparisonResult* self) {
- delete self->ncr;
- self->ncr = NULL;
+ delete self->cppobj;
+ self->cppobj = NULL;
Py_TYPE(self)->tp_free(self);
}
-static PyObject*
+PyObject*
NameComparisonResult_getOrder(s_NameComparisonResult* self) {
- return (Py_BuildValue("i", self->ncr->getOrder()));
+ return (Py_BuildValue("i", self->cppobj->getOrder()));
}
-static PyObject*
+PyObject*
NameComparisonResult_getCommonLabels(s_NameComparisonResult* self) {
- return (Py_BuildValue("I", self->ncr->getCommonLabels()));
+ return (Py_BuildValue("I", self->cppobj->getCommonLabels()));
}
-static PyObject*
+PyObject*
NameComparisonResult_getRelation(s_NameComparisonResult* self) {
- return (Py_BuildValue("I", self->ncr->getRelation()));
+ return (Py_BuildValue("I", self->cppobj->getRelation()));
}
-
// end of NameComparisonResult
// Name
-
-class s_Name : public PyObject {
-public:
- isc::dns::Name* name;
- size_t position;
-};
-
-static int Name_init(s_Name* self, PyObject* args);
-static void Name_destroy(s_Name* self);
-
-static PyObject* Name_toWire(s_Name* self, PyObject* args);
-static PyObject* Name_toText(s_Name* self);
-static PyObject* Name_str(PyObject* self);
-static PyObject* Name_getLabelCount(s_Name* self);
-static PyObject* Name_at(s_Name* self, PyObject* args);
-static PyObject* Name_getLength(s_Name* self);
-
-static PyObject* Name_compare(s_Name* self, PyObject* args);
-static PyObject* Name_equals(s_Name* self, PyObject* args);
-
-static PyObject* Name_richcmp(s_Name* self, s_Name* other, int op);
-static PyObject* Name_split(s_Name* self, PyObject* args);
-static PyObject* Name_reverse(s_Name* self);
-static PyObject* Name_concatenate(s_Name* self, PyObject* args);
-static PyObject* Name_downcase(s_Name* self);
-static PyObject* Name_isWildCard(s_Name* self);
-
-static PyMethodDef Name_methods[] = {
+// Shortcut type which would be convenient for adding class variables safely.
+typedef CPPPyObjectContainer<s_Name, Name> NameContainer;
+
+int Name_init(s_Name* self, PyObject* args);
+void Name_destroy(s_Name* self);
+
+PyObject* Name_toWire(s_Name* self, PyObject* args);
+PyObject* Name_toText(s_Name* self);
+PyObject* Name_str(PyObject* self);
+PyObject* Name_getLabelCount(s_Name* self);
+PyObject* Name_at(s_Name* self, PyObject* args);
+PyObject* Name_getLength(s_Name* self);
+
+PyObject* Name_compare(s_Name* self, PyObject* args);
+PyObject* Name_equals(s_Name* self, PyObject* args);
+
+PyObject* Name_richcmp(s_Name* self, s_Name* other, int op);
+PyObject* Name_split(s_Name* self, PyObject* args);
+PyObject* Name_reverse(s_Name* self);
+PyObject* Name_concatenate(s_Name* self, PyObject* args);
+PyObject* Name_downcase(s_Name* self);
+PyObject* Name_isWildCard(s_Name* self);
+
+PyMethodDef Name_methods[] = {
{ "at", reinterpret_cast<PyCFunction>(Name_at), METH_VARARGS,
"Returns the integer value of the name data at the specified position" },
{ "get_length", reinterpret_cast<PyCFunction>(Name_getLength), METH_NOARGS,
@@ -217,63 +147,7 @@ static PyMethodDef Name_methods[] = {
{ NULL, NULL, 0, NULL }
};
-static PyTypeObject name_type = {
- PyVarObject_HEAD_INIT(NULL, 0)
- "pydnspp.Name",
- sizeof(s_Name), // tp_basicsize
- 0, // tp_itemsize
- (destructor)Name_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
- Name_str, // tp_str
- NULL, // tp_getattro
- NULL, // tp_setattro
- NULL, // tp_as_buffer
- Py_TPFLAGS_DEFAULT, // tp_flags
- "The Name class encapsulates DNS names.\n"
- "It provides interfaces to construct a name from string or wire-format data, "
- "transform a name into a string or wire-format data, compare two names, get "
- "access to various properties of a name, etc.",
- NULL, // tp_traverse
- NULL, // tp_clear
- (richcmpfunc)Name_richcmp, // tp_richcompare
- 0, // tp_weaklistoffset
- NULL, // tp_iter
- NULL, // tp_iternext
- Name_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)Name_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
- // Note: not sure if the following are correct. Added them just to
- // make the compiler happy.
- NULL, // tp_del
- 0 // tp_version_tag
-};
-
-
-static int
+int
Name_init(s_Name* self, PyObject* args) {
const char* s;
PyObject* downcase = Py_False;
@@ -286,7 +160,7 @@ Name_init(s_Name* self, PyObject* args) {
try {
const std::string n(s);
- self->name = new Name(n, downcase == Py_True);
+ self->cppobj = new Name(n, downcase == Py_True);
self->position = 0;
} catch (const EmptyLabel&) {
PyErr_SetString(po_EmptyLabel, "EmptyLabel");
@@ -339,7 +213,7 @@ Name_init(s_Name* self, PyObject* args) {
InputBuffer buffer(bytes, len);
buffer.setPosition(position);
- self->name = new Name(buffer, downcase == Py_True);
+ self->cppobj = new Name(buffer, downcase == Py_True);
self->position = buffer.getPosition();
} catch (const InvalidBufferPosition&) {
PyErr_SetString(po_InvalidBufferPosition,
@@ -361,14 +235,14 @@ Name_init(s_Name* self, PyObject* args) {
return (-1);
}
-static void
+void
Name_destroy(s_Name* self) {
- delete self->name;
- self->name = NULL;
+ delete self->cppobj;
+ self->cppobj = NULL;
Py_TYPE(self)->tp_free(self);
}
-static PyObject*
+PyObject*
Name_at(s_Name* self, PyObject* args) {
int pos;
if (!PyArg_ParseTuple(args, "i", &pos)) {
@@ -382,7 +256,7 @@ Name_at(s_Name* self, PyObject* args) {
}
try {
- return (Py_BuildValue("I", self->name->at(pos)));
+ return (Py_BuildValue("I", self->cppobj->at(pos)));
} catch (const isc::OutOfRange&) {
PyErr_SetString(PyExc_IndexError,
"name index out of range");
@@ -390,22 +264,22 @@ Name_at(s_Name* self, PyObject* args) {
}
}
-static PyObject*
+PyObject*
Name_getLength(s_Name* self) {
- return (Py_BuildValue("i", self->name->getLength()));
+ return (Py_BuildValue("i", self->cppobj->getLength()));
}
-static PyObject*
+PyObject*
Name_getLabelCount(s_Name* self) {
- return (Py_BuildValue("i", self->name->getLabelCount()));
+ return (Py_BuildValue("i", self->cppobj->getLabelCount()));
}
-static PyObject*
+PyObject*
Name_toText(s_Name* self) {
- return (Py_BuildValue("s", self->name->toText().c_str()));
+ return (Py_BuildValue("s", self->cppobj->toText().c_str()));
}
-static PyObject*
+PyObject*
Name_str(PyObject* self) {
// Simply call the to_text method we already defined
// str() is not defined in the c++ version, only to_text
@@ -415,7 +289,7 @@ Name_str(PyObject* self) {
const_cast<char*>("")));
}
-static PyObject*
+PyObject*
Name_toWire(s_Name* self, PyObject* args) {
PyObject* bytes;
s_MessageRenderer* mr;
@@ -424,7 +298,7 @@ Name_toWire(s_Name* self, PyObject* args) {
PyObject* bytes_o = bytes;
OutputBuffer buffer(Name::MAX_WIRE);
- self->name->toWire(buffer);
+ self->cppobj->toWire(buffer);
PyObject* name_bytes = PyBytes_FromStringAndSize(static_cast<const char*>(buffer.getData()), buffer.getLength());
PyObject* result = PySequence_InPlaceConcat(bytes_o, name_bytes);
// We need to release the object we temporarily created here
@@ -432,7 +306,7 @@ Name_toWire(s_Name* self, PyObject* args) {
Py_DECREF(name_bytes);
return (result);
} else if (PyArg_ParseTuple(args, "O!", &messagerenderer_type, &mr)) {
- self->name->toWire(*mr->messagerenderer);
+ self->cppobj->toWire(*mr->messagerenderer);
// If we return NULL it is seen as an error, so use this for
// None returns
Py_RETURN_NONE;
@@ -443,7 +317,7 @@ Name_toWire(s_Name* self, PyObject* args) {
return (NULL);
}
-static PyObject*
+PyObject*
Name_compare(s_Name* self, PyObject* args) {
s_Name* other;
@@ -452,26 +326,26 @@ Name_compare(s_Name* self, PyObject* args) {
s_NameComparisonResult* ret = PyObject_New(s_NameComparisonResult, &name_comparison_result_type);
if (ret != NULL) {
- ret->ncr = new NameComparisonResult(
- self->name->compare(*other->name));
+ ret->cppobj = new NameComparisonResult(
+ self->cppobj->compare(*other->cppobj));
}
return (ret);
}
-static PyObject*
+PyObject*
Name_equals(s_Name* self, PyObject* args) {
s_Name* other;
if (!PyArg_ParseTuple(args, "O!", &name_type, &other))
return (NULL);
- if (self->name->equals(*other->name))
+ if (self->cppobj->equals(*other->cppobj))
Py_RETURN_TRUE;
else
Py_RETURN_FALSE;
}
-static PyObject*
+PyObject*
Name_split(s_Name* self, PyObject* args) {
int first, n;
s_Name* ret = NULL;
@@ -485,14 +359,14 @@ Name_split(s_Name* self, PyObject* args) {
}
ret = PyObject_New(s_Name, &name_type);
if (ret != NULL) {
- ret->name = NULL;
+ ret->cppobj = NULL;
try {
- ret->name = new Name(self->name->split(first, n));
+ ret->cppobj = new Name(self->cppobj->split(first, n));
} catch(const isc::OutOfRange& oor) {
PyErr_SetString(PyExc_IndexError, oor.what());
- ret->name = NULL;
+ ret->cppobj = NULL;
}
- if (ret->name == NULL) {
+ if (ret->cppobj == NULL) {
Py_DECREF(ret);
return (NULL);
}
@@ -507,14 +381,14 @@ Name_split(s_Name* self, PyObject* args) {
}
ret = PyObject_New(s_Name, &name_type);
if (ret != NULL) {
- ret->name = NULL;
+ ret->cppobj = NULL;
try {
- ret->name = new Name(self->name->split(n));
+ ret->cppobj = new Name(self->cppobj->split(n));
} catch(const isc::OutOfRange& oor) {
PyErr_SetString(PyExc_IndexError, oor.what());
- ret->name = NULL;
+ ret->cppobj = NULL;
}
- if (ret->name == NULL) {
+ if (ret->cppobj == NULL) {
Py_DECREF(ret);
return (NULL);
}
@@ -526,14 +400,13 @@ Name_split(s_Name* self, PyObject* args) {
"No valid type in split argument");
return (ret);
}
-#include <iostream>
//
// richcmp defines the ==, !=, >, <, >= and <= operators in python
// It is translated to a function that gets 3 arguments, an object,
// an object to compare to, and an operator.
//
-static PyObject*
+PyObject*
Name_richcmp(s_Name* self, s_Name* other, int op) {
bool c;
@@ -545,22 +418,22 @@ Name_richcmp(s_Name* self, s_Name* other, int op) {
switch (op) {
case Py_LT:
- c = *self->name < *other->name;
+ c = *self->cppobj < *other->cppobj;
break;
case Py_LE:
- c = *self->name <= *other->name;
+ c = *self->cppobj <= *other->cppobj;
break;
case Py_EQ:
- c = *self->name == *other->name;
+ c = *self->cppobj == *other->cppobj;
break;
case Py_NE:
- c = *self->name != *other->name;
+ c = *self->cppobj != *other->cppobj;
break;
case Py_GT:
- c = *self->name > *other->name;
+ c = *self->cppobj > *other->cppobj;
break;
case Py_GE:
- c = *self->name >= *other->name;
+ c = *self->cppobj >= *other->cppobj;
break;
default:
PyErr_SetString(PyExc_IndexError,
@@ -574,13 +447,13 @@ Name_richcmp(s_Name* self, s_Name* other, int op) {
}
}
-static PyObject*
+PyObject*
Name_reverse(s_Name* self) {
s_Name* ret = PyObject_New(s_Name, &name_type);
if (ret != NULL) {
- ret->name = new Name(self->name->reverse());
- if (ret->name == NULL) {
+ ret->cppobj = new Name(self->cppobj->reverse());
+ if (ret->cppobj == NULL) {
Py_DECREF(ret);
return (NULL);
}
@@ -588,7 +461,7 @@ Name_reverse(s_Name* self) {
return (ret);
}
-static PyObject*
+PyObject*
Name_concatenate(s_Name* self, PyObject* args) {
s_Name* other;
@@ -598,7 +471,7 @@ Name_concatenate(s_Name* self, PyObject* args) {
s_Name* ret = PyObject_New(s_Name, &name_type);
if (ret != NULL) {
try {
- ret->name = new Name(self->name->concatenate(*other->name));
+ ret->cppobj = new Name(self->cppobj->concatenate(*other->cppobj));
} catch (const TooLongName& tln) {
PyErr_SetString(po_TooLongName, tln.what());
return (NULL);
@@ -607,23 +480,159 @@ Name_concatenate(s_Name* self, PyObject* args) {
return (ret);
}
-static PyObject*
+PyObject*
Name_downcase(s_Name* self) {
- self->name->downcase();
+ self->cppobj->downcase();
Py_INCREF(self);
return (self);
}
-static PyObject*
+PyObject*
Name_isWildCard(s_Name* self) {
- if (self->name->isWildcard()) {
+ if (self->cppobj->isWildcard()) {
Py_RETURN_TRUE;
} else {
Py_RETURN_FALSE;
}
}
// end of Name
+} // end of unnamed namespace
+
+namespace isc {
+namespace dns {
+namespace python {
+
+//
+// Definition of the custom exceptions
+// Initialization and addition of these go in the module init at the
+// end
+//
+PyObject* po_EmptyLabel;
+PyObject* po_TooLongName;
+PyObject* po_TooLongLabel;
+PyObject* po_BadLabelType;
+PyObject* po_BadEscape;
+PyObject* po_IncompleteName;
+PyObject* po_InvalidBufferPosition;
+PyObject* po_DNSMessageFORMERR;
+//
+// Definition of enums
+// Initialization and addition of these go in the module init at the
+// end
+//
+PyObject* po_NameRelation;
+
+PyTypeObject name_comparison_result_type = {
+ PyVarObject_HEAD_INIT(NULL, 0)
+ "pydnspp.NameComparisonResult",
+ sizeof(s_NameComparisonResult), // tp_basicsize
+ 0, // tp_itemsize
+ (destructor)NameComparisonResult_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
+ "This is a supplemental class used only as a return value of Name.compare(). "
+ "It encapsulate a tuple of the comparison: ordering, number of common labels, "
+ "and relationship as follows:\n"
+ "- ordering: relative ordering under the DNSSEC order relation\n"
+ "- labels: the number of common significant labels of the two names being"
+ " compared\n"
+ "- relationship: see NameComparisonResult.NameRelation\n",
+ NULL, // tp_traverse
+ NULL, // tp_clear
+ NULL, // tp_richcompare
+ 0, // tp_weaklistoffset
+ NULL, // tp_iter
+ NULL, // tp_iternext
+ NameComparisonResult_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)NameComparisonResult_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
+};
+
+PyTypeObject name_type = {
+ PyVarObject_HEAD_INIT(NULL, 0)
+ "pydnspp.Name",
+ sizeof(s_Name), // tp_basicsize
+ 0, // tp_itemsize
+ (destructor)Name_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
+ Name_str, // tp_str
+ NULL, // tp_getattro
+ NULL, // tp_setattro
+ NULL, // tp_as_buffer
+ Py_TPFLAGS_DEFAULT, // tp_flags
+ "The Name class encapsulates DNS names.\n"
+ "It provides interfaces to construct a name from string or wire-format data, "
+ "transform a name into a string or wire-format data, compare two names, get "
+ "access to various properties of a name, etc.",
+ NULL, // tp_traverse
+ NULL, // tp_clear
+ (richcmpfunc)Name_richcmp, // tp_richcompare
+ 0, // tp_weaklistoffset
+ NULL, // tp_iter
+ NULL, // tp_iternext
+ Name_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)Name_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
+ // Note: not sure if the following are correct. Added them just to
+ // make the compiler happy.
+ NULL, // tp_del
+ 0 // tp_version_tag
+};
// Module Initialization, all statics are initialized here
bool
@@ -669,7 +678,7 @@ initModulePart_Name(PyObject* mod) {
addClassVariable(name_type, "COMPRESS_POINTER_MARK16", Py_BuildValue("I", Name::COMPRESS_POINTER_MARK16));
s_Name* root_name = PyObject_New(s_Name, &name_type);
- root_name->name = new Name(Name::ROOT_NAME());
+ root_name->cppobj = new Name(Name::ROOT_NAME());
PyObject* po_ROOT_NAME = root_name;
addClassVariable(name_type, "ROOT_NAME", po_ROOT_NAME);
@@ -706,3 +715,13 @@ initModulePart_Name(PyObject* mod) {
return (true);
}
+
+PyObject*
+createNameObject(const Name& source) {
+ NameContainer container = PyObject_New(s_Name, &name_type);
+ container.set(new Name(source));
+ return (container.release());
+}
+} // namespace python
+} // namespace dns
+} // namespace isc
diff --git a/src/lib/dns/python/name_python.h b/src/lib/dns/python/name_python.h
new file mode 100644
index 0000000..f8e793d
--- /dev/null
+++ b/src/lib/dns/python/name_python.h
@@ -0,0 +1,84 @@
+// 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 __PYTHON_NAME_H
+#define __PYTHON_NAME_H 1
+
+#include <Python.h>
+
+#include <util/python/pycppwrapper_util.h>
+
+namespace isc {
+namespace dns {
+class NameComparisonResult;
+class Name;
+
+namespace python {
+
+//
+// Declaration of the custom exceptions
+// Initialization and addition of these go in the module init at the
+// end
+//
+extern PyObject* po_EmptyLabel;
+extern PyObject* po_TooLongName;
+extern PyObject* po_TooLongLabel;
+extern PyObject* po_BadLabelType;
+extern PyObject* po_BadEscape;
+extern PyObject* po_IncompleteName;
+extern PyObject* po_InvalidBufferPosition;
+extern PyObject* po_DNSMessageFORMERR;
+
+//
+// Declaration of enums
+// Initialization and addition of these go in the module init at the
+// end
+//
+extern PyObject* po_NameRelation;
+
+// The s_* Class simply covers one instantiation of the object.
+class s_NameComparisonResult : public PyObject {
+public:
+ s_NameComparisonResult() : cppobj(NULL) {}
+ NameComparisonResult* cppobj;
+};
+
+class s_Name : public PyObject {
+public:
+ s_Name() : cppobj(NULL), position(0) {}
+ Name* cppobj;
+ size_t position;
+};
+
+extern PyTypeObject name_comparison_result_type;
+extern PyTypeObject name_type;
+
+bool initModulePart_Name(PyObject* mod);
+
+/// This is A simple shortcut to create a python Name object (in the
+/// form of a pointer to PyObject) with minimal exception safety.
+/// On success, it returns a valid pointer to PyObject with a reference
+/// counter of 1; if something goes wrong it throws an exception (it never
+/// returns a NULL pointer).
+/// This function is expected to be called with in a try block
+/// followed by necessary setup for python exception.
+PyObject* createNameObject(const Name& source);
+} // namespace python
+} // namespace dns
+} // namespace isc
+#endif // __PYTHON_NAME_H
+
+// Local Variables:
+// mode: c++
+// End:
diff --git a/src/lib/dns/python/pydnspp.cc b/src/lib/dns/python/pydnspp.cc
index 0be346e..07abf71 100644
--- a/src/lib/dns/python/pydnspp.cc
+++ b/src/lib/dns/python/pydnspp.cc
@@ -38,6 +38,14 @@
#include <dns/messagerenderer.h>
#include "pydnspp_common.h"
+#include "messagerenderer_python.h"
+#include "name_python.h"
+#include "rcode_python.h"
+#include "tsigkey_python.h"
+#include "tsig_rdata_python.h"
+#include "tsigerror_python.h"
+#include "tsigrecord_python.h"
+#include "tsig_python.h"
namespace isc {
namespace dns {
@@ -52,14 +60,9 @@ PyObject* po_DNSMessageBADVERS;
}
}
-#include "rcode_python.h"
-#include "tsigerror_python.h"
-
// order is important here!
using namespace isc::dns::python;
-#include <dns/python/messagerenderer_python.cc>
-#include <dns/python/name_python.cc> // needs Messagerenderer
#include <dns/python/rrclass_python.cc> // needs Messagerenderer
#include <dns/python/rrtype_python.cc> // needs Messagerenderer
#include <dns/python/rrttl_python.cc> // needs Messagerenderer
@@ -67,8 +70,6 @@ using namespace isc::dns::python;
#include <dns/python/rrset_python.cc> // needs Rdata, RRTTL
#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/edns_python.cc> // needs Messagerenderer, Rcode
#include <dns/python/message_python.cc> // needs RRset, Question
@@ -167,14 +168,21 @@ PyInit_pydnspp(void) {
return (NULL);
}
+ if (!initModulePart_TSIG(mod)) {
+ return (NULL);
+ }
+
if (!initModulePart_TSIGError(mod)) {
return (NULL);
}
+ if (!initModulePart_TSIGRecord(mod)) {
+ return (NULL);
+ }
+
if (!initModulePart_TSIGContext(mod)) {
return (NULL);
}
return (mod);
}
-
diff --git a/src/lib/dns/python/pydnspp_towire.h b/src/lib/dns/python/pydnspp_towire.h
new file mode 100644
index 0000000..66362a0
--- /dev/null
+++ b/src/lib/dns/python/pydnspp_towire.h
@@ -0,0 +1,127 @@
+// 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 __LIBDNS_PYTHON_TOWIRE_H
+#define __LIBDNS_PYTHON_TOWIRE_H 1
+
+#include <Python.h>
+
+#include <stdexcept>
+#include <string>
+
+#include <dns/messagerenderer.h>
+
+#include <util/buffer.h>
+#include <util/python/pycppwrapper_util.h>
+
+#include "messagerenderer_python.h"
+
+namespace isc {
+namespace dns {
+namespace python {
+
+// The following two templated structures are a helper to use the same
+// toWire() template implementation for two types of toWire() methods:
+// return an integer or have no return value.
+template <typename CPPCLASS>
+struct ToWireCallVoid {
+ ToWireCallVoid(CPPCLASS& cppobj) : cppobj_(cppobj) {}
+ int operator()(AbstractMessageRenderer& renderer) const {
+ cppobj_.toWire(renderer);
+ return (0);
+ }
+ const CPPCLASS& cppobj_;
+};
+
+template <typename CPPCLASS>
+struct ToWireCallInt {
+ ToWireCallInt(CPPCLASS& cppobj) : cppobj_(cppobj) {}
+ int operator()(AbstractMessageRenderer& renderer) const {
+ return (cppobj_.toWire(renderer));
+ }
+ const CPPCLASS& cppobj_;
+};
+
+// This templated function gives a common implementation of the toWire()
+// wrapper for various libdns++ classes. PYSTRUCT and CPPCLASS are
+// (C++ binding of) python and (pure) C++ classes (e.g., s_Name and Name),
+// and TOWIRECALLER is either ToWireCallVoid<CPPCLASS> or
+// ToWireCallInt<CPPCLASS>, depending on the toWire() method of the class
+// returns a value or not.
+//
+// See, e.g., tsigrecord_python.cc for how to use it.
+//
+// This should be able to be used without modification for most classes that
+// have toWire(). But if the underlying toWire() has an extra argument, the
+// definition will need to be adjusted accordingly.
+template <typename PYSTRUCT, typename CPPCLASS, typename TOWIRECALLER>
+PyObject*
+toWireWrapper(const PYSTRUCT* const self, PyObject* args) {
+ try {
+ // To OutputBuffer version
+ PyObject* bytes; // this won't have own reference, no risk of leak.
+ if (PyArg_ParseTuple(args, "O", &bytes) && PySequence_Check(bytes)) {
+ // render the object into a buffer (this can throw)
+ isc::util::OutputBuffer buffer(0);
+ self->cppobj->toWire(buffer);
+
+ // convert the rendered data into PyObject. This could leak later,
+ // so we need to store it in a container.
+ PyObject* rd_bytes = PyBytes_FromStringAndSize(
+ static_cast<const char*>(buffer.getData()),
+ buffer.getLength());
+ isc::util::python::PyObjectContainer rd_bytes_container(rd_bytes);
+
+ // concat the latest data to the given existing sequence. concat
+ // operation could fail, so we use a container to clean it up
+ // safely should that happen.
+ PyObject* result = PySequence_InPlaceConcat(bytes, rd_bytes);
+ isc::util::python::PyObjectContainer result_container(result);
+
+ return (result_container.release());
+ }
+
+ // To MessageRenderer version
+ s_MessageRenderer* renderer;
+ if (PyArg_ParseTuple(args, "O!", &messagerenderer_type, &renderer)) {
+ const unsigned int n = TOWIRECALLER(*self->cppobj)(
+ *renderer->messagerenderer);
+
+ return (Py_BuildValue("I", n));
+ }
+ } catch (const std::exception& ex) {
+ const std::string ex_what =
+ "Failed to render an libdns++ object wire-format: "
+ + std::string(ex.what());
+ PyErr_SetString(po_IscException, ex_what.c_str());
+ return (NULL);
+ } catch (...) {
+ PyErr_SetString(po_IscException, "Unexpectedly failed to render an "
+ "libdns++ object wire-format.");
+ return (NULL);
+ }
+
+ PyErr_Clear();
+ PyErr_SetString(PyExc_TypeError,
+ "Incorrect arguments for a to_wire() method");
+ return (NULL);
+}
+} // namespace python
+} // namespace dns
+} // namespace isc
+#endif // __LIBDNS_PYTHON_TOWIRE_H
+
+// Local Variables:
+// mode: c++
+// End:
diff --git a/src/lib/dns/python/question_python.cc b/src/lib/dns/python/question_python.cc
index 2889350..c702f85 100644
--- a/src/lib/dns/python/question_python.cc
+++ b/src/lib/dns/python/question_python.cc
@@ -144,7 +144,7 @@ Question_init(s_Question* self, PyObject* args) {
&rrclass_type, &rrclass,
&rrtype_type, &rrtype
)) {
- self->question = QuestionPtr(new Question(*name->name, *rrclass->rrclass,
+ self->question = QuestionPtr(new Question(*name->cppobj, *rrclass->rrclass,
*rrtype->rrtype));
return (0);
} else if (PyArg_ParseTuple(args, "y#|I", &b, &len, &position)) {
@@ -189,7 +189,7 @@ Question_getName(s_Question* self) {
// is this the best way to do this?
name = static_cast<s_Name*>(name_type.tp_alloc(&name_type, 0));
if (name != NULL) {
- name->name = new Name(self->question->getName());
+ name->cppobj = new Name(self->question->getName());
}
return (name);
diff --git a/src/lib/dns/python/rrset_python.cc b/src/lib/dns/python/rrset_python.cc
index c7d05d1..71a0710 100644
--- a/src/lib/dns/python/rrset_python.cc
+++ b/src/lib/dns/python/rrset_python.cc
@@ -168,7 +168,7 @@ RRset_init(s_RRset* self, PyObject* args) {
&rrtype_type, &rrtype,
&rrttl_type, &rrttl
)) {
- self->rrset = RRsetPtr(new RRset(*name->name, *rrclass->rrclass,
+ self->rrset = RRsetPtr(new RRset(*name->cppobj, *rrclass->rrclass,
*rrtype->rrtype, *rrttl->rrttl));
return (0);
}
@@ -197,8 +197,8 @@ RRset_getName(s_RRset* self) {
// is this the best way to do this?
name = static_cast<s_Name*>(name_type.tp_alloc(&name_type, 0));
if (name != NULL) {
- name->name = new Name(self->rrset->getName());
- if (name->name == NULL)
+ name->cppobj = new Name(self->rrset->getName());
+ if (name->cppobj == NULL)
{
Py_DECREF(name);
return (NULL);
@@ -265,7 +265,7 @@ RRset_setName(s_RRset* self, PyObject* args) {
if (!PyArg_ParseTuple(args, "O!", &name_type, &name)) {
return (NULL);
}
- self->rrset->setName(*name->name);
+ self->rrset->setName(*name->cppobj);
Py_RETURN_NONE;
}
diff --git a/src/lib/dns/python/tests/Makefile.am b/src/lib/dns/python/tests/Makefile.am
index 1149a47..eb0c136 100644
--- a/src/lib/dns/python/tests/Makefile.am
+++ b/src/lib/dns/python/tests/Makefile.am
@@ -12,8 +12,10 @@ PYTESTS += rrset_python_test.py
PYTESTS += rrttl_python_test.py
PYTESTS += rrtype_python_test.py
PYTESTS += tsig_python_test.py
+PYTESTS += tsig_rdata_python_test.py
PYTESTS += tsigerror_python_test.py
PYTESTS += tsigkey_python_test.py
+PYTESTS += tsigrecord_python_test.py
EXTRA_DIST = $(PYTESTS)
EXTRA_DIST += testutil.py
@@ -34,7 +36,7 @@ if ENABLE_PYTHON_COVERAGE
endif
for pytest in $(PYTESTS) ; do \
echo Running test: $$pytest ; \
- env PYTHONPATH=$(abs_top_srcdir)/src/lib/dns/.libs:$(abs_top_srcdir)/src/lib/python:$(abs_top_builddir)/src/lib/python:$(abs_top_builddir)/src/lib/dns/python/.libs \
+ env PYTHONPATH=$(abs_top_builddir)/src/lib/util/pyunittests/.libs:$(abs_top_srcdir)/src/lib/dns/.libs:$(abs_top_srcdir)/src/lib/python:$(abs_top_builddir)/src/lib/python:$(abs_top_builddir)/src/lib/dns/python/.libs \
TESTDATA_PATH=$(abs_top_srcdir)/src/lib/dns/tests/testdata:$(abs_top_builddir)/src/lib/dns/tests/testdata \
$(LIBRARY_PATH_PLACEHOLDER) \
$(PYCOVERAGE_RUN) $(abs_srcdir)/$$pytest || exit ; \
diff --git a/src/lib/dns/python/tests/message_python_test.py b/src/lib/dns/python/tests/message_python_test.py
index 72807cc..41b9a67 100644
--- a/src/lib/dns/python/tests/message_python_test.py
+++ b/src/lib/dns/python/tests/message_python_test.py
@@ -422,7 +422,54 @@ test.example.com. 3600 IN A 192.0.2.2
factoryFromFile,
message_parse,
"message_fromWire9")
-
+
+ def test_from_wire_with_tsig(self):
+ # Initially there should be no TSIG
+ self.assertEqual(None, self.p.get_tsig_record())
+
+ # getTSIGRecord() is only valid in the parse mode.
+ self.assertRaises(InvalidMessageOperation, self.r.get_tsig_record)
+
+ factoryFromFile(self.p, "message_toWire2.wire")
+ tsig_rr = self.p.get_tsig_record()
+ self.assertEqual(Name("www.example.com"), tsig_rr.get_name())
+ self.assertEqual(85, tsig_rr.get_length())
+ self.assertEqual(TSIGKey.HMACMD5_NAME,
+ tsig_rr.get_rdata().get_algorithm())
+
+ # If we clear the message for reuse, the recorded TSIG will be cleared.
+ self.p.clear(Message.PARSE)
+ self.assertEqual(None, self.p.get_tsig_record())
+
+ def test_from_wire_with_tsigcompressed(self):
+ # Mostly same as fromWireWithTSIG, but the TSIG owner name is
+ # compressed.
+ factoryFromFile(self.p, "message_fromWire12.wire");
+ tsig_rr = self.p.get_tsig_record()
+ self.assertEqual(Name("www.example.com"), tsig_rr.get_name())
+ # 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.
+ self.assertEqual(70, tsig_rr.get_length())
+
+ def test_from_wire_with_badtsig(self):
+ # Multiple TSIG RRs
+ self.assertRaises(DNSMessageFORMERR, factoryFromFile,
+ self.p, "message_fromWire13.wire")
+ self.p.clear(Message.PARSE)
+
+ # TSIG in the answer section (must be in additional)
+ self.assertRaises(DNSMessageFORMERR, factoryFromFile,
+ self.p, "message_fromWire14.wire")
+ self.p.clear(Message.PARSE)
+
+ # TSIG is not the last record.
+ self.assertRaises(DNSMessageFORMERR, factoryFromFile,
+ self.p, "message_fromWire15.wire")
+ self.p.clear(Message.PARSE)
+
+ # Unexpected RR Class (this will fail in constructing TSIGRecord)
+ self.assertRaises(DNSMessageFORMERR, factoryFromFile,
+ self.p, "message_fromWire16.wire")
if __name__ == '__main__':
unittest.main()
diff --git a/src/lib/dns/python/tests/tsig_python_test.py b/src/lib/dns/python/tests/tsig_python_test.py
index bffa0cf..7e5515d 100644
--- a/src/lib/dns/python/tests/tsig_python_test.py
+++ b/src/lib/dns/python/tests/tsig_python_test.py
@@ -1,4 +1,4 @@
-# Copyright (C) 2010 Internet Systems Consortium.
+# 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
@@ -13,17 +13,542 @@
# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-import unittest
+import base64, sys, time, unittest
from pydnspp import *
+from testutil import *
+from pyunittests_util import fix_current_time
+
+# bit-wise constant flags to configure DNS header flags for test
+# messages.
+QR_FLAG = 0x1
+AA_FLAG = 0x2
+RD_FLAG = 0x4
+
+COMMON_EXPECTED_MAC = b"\x22\x70\x26\xad\x29\x7b\xee\xe7\x21\xce\x6c\x6f\xff\x1e\x9e\xf3"
+DUMMY_DATA = b"\xdd" * 100
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.
+ # make sure we don't use faked time unless explicitly do so in tests
+ fix_current_time(None)
+ self.qid = 0x2d65
+ self.test_name = Name("www.example.com")
self.tsig_ctx = TSIGContext(self.tsig_key)
+ self.tsig_verify_ctx = TSIGContext(self.tsig_key)
+ self.keyring = TSIGKeyRing()
+ self.message = Message(Message.RENDER)
+ self.renderer = MessageRenderer()
+ self.test_class = RRClass.IN()
+ self.test_ttl = RRTTL(86400)
+ self.secret = base64.b64decode(b"SFuWd/q99SzF8Yzd1QbB9g==")
+ self.tsig_ctx = TSIGContext(TSIGKey(self.test_name,
+ TSIGKey.HMACMD5_NAME,
+ self.secret))
+ self.badkey_name = Name("badkey.example.com")
+ self.dummy_record = TSIGRecord(self.badkey_name,
+ TSIG("hmac-md5.sig-alg.reg.int. " + \
+ "1302890362 300 0 11621 " + \
+ "0 0"))
+
+ def tearDown(self):
+ # reset any faked current time setting (it would affect other tests)
+ fix_current_time(None)
+
+ # Note: intentionally use camelCase so that we can easily copy-paste
+ # corresponding C++ tests.
+ def createMessageAndSign(self, id, qname, ctx, message_flags=RD_FLAG,
+ qtype=RRType.A(), answer_data=None,
+ answer_type=None, add_question=True,
+ rcode=Rcode.NOERROR()):
+ self.message.clear(Message.RENDER)
+ self.message.set_qid(id)
+ self.message.set_opcode(Opcode.QUERY())
+ self.message.set_rcode(rcode)
+ if (message_flags & QR_FLAG) != 0:
+ self.message.set_header_flag(Message.HEADERFLAG_QR)
+ if (message_flags & AA_FLAG) != 0:
+ self.message.set_header_flag(Message.HEADERFLAG_AA)
+ if (message_flags & RD_FLAG) != 0:
+ self.message.set_header_flag(Message.HEADERFLAG_RD)
+ if add_question:
+ self.message.add_question(Question(qname, self.test_class, qtype))
+ if answer_data is not None:
+ if answer_type is None:
+ answer_type = qtype
+ answer_rrset = RRset(qname, self.test_class, answer_type,
+ self.test_ttl)
+ answer_rrset.add_rdata(Rdata(answer_type, self.test_class,
+ answer_data))
+ self.message.add_rrset(Message.SECTION_ANSWER, answer_rrset)
+ self.renderer.clear()
+ self.message.to_wire(self.renderer)
+
+ if ctx.get_state() == TSIGContext.STATE_INIT:
+ expected_new_state = TSIGContext.STATE_SENT_REQUEST
+ else:
+ expected_new_state = TSIGContext.STATE_SENT_RESPONSE
+ tsig = ctx.sign(id, self.renderer.get_data())
+
+ return tsig
+
+ # Note: intentionally use camelCase so that we can easily copy-paste
+ # corresponding C++ tests.
+ def createMessageFromFile(self, file):
+ self.message.clear(Message.PARSE)
+ self.received_data = read_wire_data(file)
+ self.message.from_wire(self.received_data)
+
+ # Note: intentionally use camelCase so that we can easily copy-paste
+ # corresponding C++ tests.
+ def commonSignChecks(self, tsig, expected_qid, expected_timesigned,
+ expected_mac, expected_error=0,
+ expected_otherdata=None,
+ expected_algorithm=TSIGKey.HMACMD5_NAME):
+ tsig_rdata = tsig.get_rdata()
+ self.assertEqual(expected_algorithm, tsig_rdata.get_algorithm())
+ self.assertEqual(expected_timesigned, tsig_rdata.get_timesigned())
+ self.assertEqual(300, tsig_rdata.get_fudge())
+ self.assertEqual(expected_mac, tsig_rdata.get_mac())
+ self.assertEqual(expected_qid, tsig_rdata.get_original_id())
+ self.assertEqual(expected_error, tsig_rdata.get_error())
+ self.assertEqual(expected_otherdata, tsig_rdata.get_other_data())
+
+ def test_initial_state(self):
+ # Until signing or verifying, the state should be INIT
+ self.assertEqual(TSIGContext.STATE_INIT, self.tsig_ctx.get_state())
+
+ # And there should be no error code.
+ self.assertEqual(TSIGError(Rcode.NOERROR()), self.tsig_ctx.get_error())
+
+ # Note: intentionally use camelCase so that we can easily copy-paste
+ # corresponding C++ tests.
+ def commonVerifyChecks(self, ctx, record, data, expected_error,
+ expected_new_state=\
+ TSIGContext.STATE_VERIFIED_RESPONSE):
+ self.assertEqual(expected_error, ctx.verify(record, data))
+ self.assertEqual(expected_error, ctx.get_error())
+ self.assertEqual(expected_new_state, ctx.get_state())
+
+ def test_from_keyring(self):
+ # Construct a TSIG context with an empty key ring. Key shouldn't be
+ # found, and the BAD_KEY error should be recorded.
+ ctx = TSIGContext(self.test_name, TSIGKey.HMACMD5_NAME, self.keyring)
+ self.assertEqual(TSIGContext.STATE_INIT, ctx.get_state())
+ self.assertEqual(TSIGError.BAD_KEY, ctx.get_error())
+ # check get_error() doesn't cause ref leak. Note: we can't
+ # realiably do this check for get_state(), as it returns an integer
+ # object, which could have many references
+ self.assertEqual(1, sys.getrefcount(ctx.get_error()))
+
+ # Add a matching key (we don't use the secret so leave it empty), and
+ # construct it again. This time it should be constructed with a valid
+ # key.
+ self.keyring.add(TSIGKey(self.test_name, TSIGKey.HMACMD5_NAME, b""))
+ ctx = TSIGContext(self.test_name, TSIGKey.HMACMD5_NAME, self.keyring)
+ self.assertEqual(TSIGContext.STATE_INIT, ctx.get_state())
+ self.assertEqual(TSIGError.NOERROR, ctx.get_error())
+
+ # Similar to the first case except that the key ring isn't empty but
+ # it doesn't contain a matching key.
+ ctx = TSIGContext(self.test_name, TSIGKey.HMACSHA1_NAME, self.keyring)
+ self.assertEqual(TSIGContext.STATE_INIT, ctx.get_state())
+ self.assertEqual(TSIGError.BAD_KEY, ctx.get_error())
+
+ ctx = TSIGContext(Name("different-key.example"),
+ TSIGKey.HMACMD5_NAME, self.keyring)
+ self.assertEqual(TSIGContext.STATE_INIT, ctx.get_state())
+ self.assertEqual(TSIGError.BAD_KEY, ctx.get_error())
+
+ # "Unknown" algorithm name will result in BADKEY, too.
+ ctx = TSIGContext(self.test_name, Name("unknown.algorithm"),
+ self.keyring)
+ self.assertEqual(TSIGContext.STATE_INIT, ctx.get_state())
+ self.assertEqual(TSIGError.BAD_KEY, ctx.get_error())
+
+ def test_sign(self):
+ fix_current_time(0x4da8877a)
+ tsig = self.createMessageAndSign(self.qid, self.test_name,
+ self.tsig_ctx)
+ self.commonSignChecks(tsig, self.qid, 0x4da8877a, COMMON_EXPECTED_MAC)
+
+ # Same test as sign, but specifying the key name with upper-case (i.e.
+ # 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.
+ def test_sign_using_uppercase_keyname(self):
+ fix_current_time(0x4da8877a)
+ cap_ctx = TSIGContext(TSIGKey(Name("WWW.EXAMPLE.COM"),
+ TSIGKey.HMACMD5_NAME, self.secret))
+ tsig = self.createMessageAndSign(self.qid, self.test_name, cap_ctx)
+ self.commonSignChecks(tsig, self.qid, 0x4da8877a, COMMON_EXPECTED_MAC)
+
+ # Same as the previous test, but for the algorithm name.
+ def test_sign_using_uppercase_algorithm_name(self):
+ fix_current_time(0x4da8877a)
+ cap_ctx = TSIGContext(TSIGKey(self.test_name,
+ Name("HMAC-md5.SIG-alg.REG.int"),
+ self.secret))
+ tsig = self.createMessageAndSign(self.qid, self.test_name, cap_ctx)
+ self.commonSignChecks(tsig, self.qid, 0x4da8877a, COMMON_EXPECTED_MAC)
+
+ # Sign the message using the actual time, and check the accuracy of it.
+ # We cannot reasonably predict the expected MAC, so don't bother to
+ # check it.
+ def test_sign_at_actual_time(self):
+ now = int(time.time())
+ tsig = self.createMessageAndSign(self.qid, self.test_name,
+ self.tsig_ctx)
+ tsig_rdata = tsig.get_rdata()
+
+ # Check the resulted time signed is in the range of [now, now + 5]
+ self.assertTrue(now <= tsig_rdata.get_timesigned())
+ self.assertTrue(now + 5 >= tsig_rdata.get_timesigned())
+
+ def test_bad_data(self):
+ self.assertRaises(TypeError, self.tsig_ctx.sign, None, 10)
+
+ def test_verify_bad_data(self):
+ # the data must at least hold the DNS message header and the specified
+ # TSIG.
+ bad_len = 12 + self.dummy_record.get_length() - 1
+ self.assertRaises(InvalidParameter, self.tsig_ctx.verify,
+ self.dummy_record, DUMMY_DATA[:bad_len])
+
+ def test_sign_using_hmacsha1(self):
+ fix_current_time(0x4dae7d5f)
+
+ secret = base64.b64decode(b"MA+QDhXbyqUak+qnMFyTyEirzng=")
+ sha1_ctx = TSIGContext(TSIGKey(self.test_name, TSIGKey.HMACSHA1_NAME,
+ secret))
+ qid = 0x0967
+ expected_mac = b"\x41\x53\x40\xc7\xda\xf8\x24\xed\x68\x4e\xe5\x86" + \
+ b"\xf7\xb5\xa6\x7a\x2f\xeb\xc0\xd3"
+ tsig = self.createMessageAndSign(qid, self.test_name, sha1_ctx)
+ self.commonSignChecks(tsig, qid, 0x4dae7d5f, expected_mac,
+ 0, None, TSIGKey.HMACSHA1_NAME)
+
+ def test_verify_then_sign_response(self):
+ fix_current_time(0x4da8877a)
+
+ self.createMessageFromFile("message_toWire2.wire")
+ self.commonVerifyChecks(self.tsig_verify_ctx,
+ self.message.get_tsig_record(),
+ self.received_data, TSIGError.NOERROR,
+ TSIGContext.STATE_RECEIVED_REQUEST)
+
+ tsig = self.createMessageAndSign(self.qid, self.test_name,
+ self.tsig_verify_ctx,
+ QR_FLAG|AA_FLAG|RD_FLAG,
+ RRType.A(), "192.0.2.1")
+
+ expected_mac = b"\x8f\xcd\xa6\x6a\x7c\xd1\xa3\xb9\x94\x8e\xb1\x86" + \
+ b"\x9d\x38\x4a\x9f"
+ self.commonSignChecks(tsig, self.qid, 0x4da8877a, expected_mac)
+
+ def test_verify_uppercase_names(self):
+ fix_current_time(0x4da8877a)
+
+ self.createMessageFromFile("tsig_verify9.wire")
+ self.commonVerifyChecks(self.tsig_verify_ctx,
+ self.message.get_tsig_record(),
+ self.received_data, TSIGError.NOERROR,
+ TSIGContext.STATE_RECEIVED_REQUEST)
+
+ def test_verify_forward_message(self):
+ fix_current_time(0x4da8877a)
+
+ self.createMessageFromFile("tsig_verify6.wire")
+ self.commonVerifyChecks(self.tsig_verify_ctx,
+ self.message.get_tsig_record(),
+ self.received_data, TSIGError.NOERROR,
+ TSIGContext.STATE_RECEIVED_REQUEST)
+
+ def test_sign_continuation(self):
+ fix_current_time(0x4da8e951)
+
+ axfr_qid = 0x3410
+ zone_name = Name("example.com")
+
+ tsig = self.createMessageAndSign(axfr_qid, zone_name, self.tsig_ctx,
+ 0, RRType.AXFR())
+
+ received_data = read_wire_data("tsig_verify1.wire")
+ self.commonVerifyChecks(self.tsig_verify_ctx, tsig, received_data,
+ TSIGError.NOERROR,
+ TSIGContext.STATE_RECEIVED_REQUEST)
+
+ tsig = self.createMessageAndSign(axfr_qid, zone_name,
+ self.tsig_verify_ctx,
+ AA_FLAG|QR_FLAG, RRType.AXFR(),
+ "ns.example.com. root.example.com." +\
+ " 2011041503 7200 3600 2592000 1200",
+ RRType.SOA())
+
+ received_data = read_wire_data("tsig_verify2.wire")
+ self.commonVerifyChecks(self.tsig_ctx, tsig, received_data,
+ TSIGError.NOERROR)
+
+ expected_mac = b"\x10\x24\x58\xf7\xf6\x2d\xdd\x7d\x63\x8d\x74" +\
+ b"\x60\x34\x13\x09\x68"
+ tsig = self.createMessageAndSign(axfr_qid, zone_name,
+ self.tsig_verify_ctx,
+ AA_FLAG|QR_FLAG, RRType.AXFR(),
+ "ns.example.com.", RRType.NS(),
+ False)
+ self.commonSignChecks(tsig, axfr_qid, 0x4da8e951, expected_mac)
+
+ received_data = read_wire_data("tsig_verify3.wire")
+ self.commonVerifyChecks(self.tsig_ctx, tsig, received_data,
+ TSIGError.NOERROR)
+
+ def test_badtime_response(self):
+ fix_current_time(0x4da8b9d6)
+
+ test_qid = 0x7fc4
+ tsig = self.createMessageAndSign(test_qid, self.test_name,
+ self.tsig_ctx, 0, RRType.SOA())
+
+ # "advance the clock" and try validating, which should fail due to
+ # BADTIME
+ fix_current_time(0x4da8be86)
+ self.commonVerifyChecks(self.tsig_verify_ctx, tsig, DUMMY_DATA,
+ TSIGError.BAD_TIME,
+ TSIGContext.STATE_RECEIVED_REQUEST)
+
+ # make and sign a response in the context of TSIG error.
+ tsig = self.createMessageAndSign(test_qid, self.test_name,
+ self.tsig_verify_ctx,
+ QR_FLAG, RRType.SOA(), None, None,
+ True, Rcode.NOTAUTH())
+
+ expected_otherdata = b"\x00\x00\x4d\xa8\xbe\x86"
+ expected_mac = b"\xd4\xb0\x43\xf6\xf4\x44\x95\xec\x8a\x01\x26" +\
+ b"\x0e\x39\x15\x9d\x76"
+
+ self.commonSignChecks(tsig, self.message.get_qid(), 0x4da8b9d6,
+ expected_mac,
+ 18, # error: BADTIME
+ expected_otherdata)
+
+ def test_badtime_response2(self):
+ fix_current_time(0x4da8b9d6)
+
+ tsig = self.createMessageAndSign(self.qid, self.test_name,
+ self.tsig_ctx, 0, RRType.SOA())
+
+ # "rewind the clock" and try validating, which should fail due to
+ # BADTIME
+ fix_current_time(0x4da8b9d6 - 600)
+ self.commonVerifyChecks(self.tsig_verify_ctx, tsig, DUMMY_DATA,
+ TSIGError.BAD_TIME,
+ TSIGContext.STATE_RECEIVED_REQUEST)
+
+ # Test various boundary conditions. We intentionally use the magic
+ # number of 300 instead of the constant variable for testing.
+ # In the okay cases, signature is not correct, but it's sufficient to
+ # check the error code isn't BADTIME for the purpose of this test.
+ def test_badtime_boundaries(self):
+ fix_current_time(0x4da8b9d6)
+
+ tsig = self.createMessageAndSign(self.qid, self.test_name,
+ self.tsig_ctx, 0, RRType.SOA())
+
+ fix_current_time(0x4da8b9d6 + 301)
+ self.assertEqual(TSIGError.BAD_TIME,
+ self.tsig_verify_ctx.verify(tsig, DUMMY_DATA))
+
+ fix_current_time(0x4da8b9d6 + 300)
+ self.assertNotEqual(TSIGError.BAD_TIME,
+ self.tsig_verify_ctx.verify(tsig, DUMMY_DATA))
+
+ fix_current_time(0x4da8b9d6 - 301)
+ self.assertEqual(TSIGError.BAD_TIME,
+ self.tsig_verify_ctx.verify(tsig, DUMMY_DATA))
+
+ fix_current_time(0x4da8b9d6 - 300)
+ self.assertNotEqual(TSIGError.BAD_TIME,
+ self.tsig_verify_ctx.verify(tsig, DUMMY_DATA))
+
+ def test_badtime_overflow(self):
+ fix_current_time(200)
+ tsig = self.createMessageAndSign(self.qid, self.test_name,
+ self.tsig_ctx, 0, RRType.SOA())
+
+ # This should be in the okay range, but since "200 - fudge" overflows
+ # and we compare them as 64-bit unsigned integers, it results in a
+ # false positive (we intentionally accept that).
+ fix_current_time(100)
+ self.assertEqual(TSIGError.BAD_TIME,
+ self.tsig_verify_ctx.verify(tsig, DUMMY_DATA))
+
+ def test_badsig_response(self):
+ fix_current_time(0x4da8877a)
+
+ # Try to sign a simple message with bogus secret. It should fail
+ # with BADSIG.
+ self.createMessageFromFile("message_toWire2.wire")
+ bad_ctx = TSIGContext(TSIGKey(self.test_name, TSIGKey.HMACMD5_NAME,
+ DUMMY_DATA))
+ self.commonVerifyChecks(bad_ctx, self.message.get_tsig_record(),
+ self.received_data, TSIGError.BAD_SIG,
+ TSIGContext.STATE_RECEIVED_REQUEST)
+
+ # Sign the same message (which doesn't matter for this test) with the
+ # context of "checked state".
+ tsig = self.createMessageAndSign(self.qid, self.test_name, bad_ctx)
+ self.commonSignChecks(tsig, self.message.get_qid(), 0x4da8877a, None,
+ 16) # 16: BADSIG
+
+ def test_badkey_response(self):
+ # A similar test as badsigResponse but for BADKEY
+ fix_current_time(0x4da8877a)
+ tsig_ctx = TSIGContext(self.badkey_name, TSIGKey.HMACMD5_NAME,
+ self.keyring)
+ self.commonVerifyChecks(tsig_ctx, self.dummy_record, DUMMY_DATA,
+ TSIGError.BAD_KEY,
+ TSIGContext.STATE_RECEIVED_REQUEST)
+
+ sig = self.createMessageAndSign(self.qid, self.test_name, tsig_ctx)
+ self.assertEqual(self.badkey_name, sig.get_name())
+ self.commonSignChecks(sig, self.qid, 0x4da8877a, None, 17) # 17: BADKEY
+
+ def test_badkey_for_response(self):
+ # "BADKEY" case for a response to a signed message
+ self.createMessageAndSign(self.qid, self.test_name, self.tsig_ctx)
+ self.commonVerifyChecks(self.tsig_ctx, self.dummy_record, DUMMY_DATA,
+ TSIGError.BAD_KEY,
+ TSIGContext.STATE_SENT_REQUEST)
+
+ # A similar case with a different algorithm
+ dummy_record = TSIGRecord(self.test_name,
+ TSIG("hmac-sha1. 1302890362 300 0 "
+ "11621 0 0"))
+ self.commonVerifyChecks(self.tsig_ctx, dummy_record, DUMMY_DATA,
+ TSIGError.BAD_KEY,
+ TSIGContext.STATE_SENT_REQUEST)
+
+ # According to RFC2845 4.6, if TSIG verification fails the client
+ # should discard that message and wait for another signed response.
+ # This test emulates that situation.
+ def test_badsig_then_validate(self):
+ fix_current_time(0x4da8877a)
+
+ self.createMessageAndSign(self.qid, self.test_name, self.tsig_ctx)
+ self.createMessageFromFile("tsig_verify4.wire")
+
+ self.commonVerifyChecks(self.tsig_ctx, self.message.get_tsig_record(),
+ self.received_data, TSIGError.BAD_SIG,
+ TSIGContext.STATE_SENT_REQUEST)
+
+ self.createMessageFromFile("tsig_verify5.wire")
+ self.commonVerifyChecks(self.tsig_ctx, self.message.get_tsig_record(),
+ self.received_data, TSIGError.NOERROR,
+ TSIGContext.STATE_VERIFIED_RESPONSE)
+
+ # Similar to the previous test, but the first response doesn't contain
+ # TSIG.
+ def test_nosig_then_validate(self):
+ fix_current_time(0x4da8877a)
+ self.createMessageAndSign(self.qid, self.test_name, self.tsig_ctx)
+
+ self.commonVerifyChecks(self.tsig_ctx, None, DUMMY_DATA,
+ TSIGError.FORMERR, TSIGContext.STATE_SENT_REQUEST)
+
+ self.createMessageFromFile("tsig_verify5.wire")
+ self.commonVerifyChecks(self.tsig_ctx, self.message.get_tsig_record(),
+ self.received_data, TSIGError.NOERROR,
+ TSIGContext.STATE_VERIFIED_RESPONSE)
+
+ # Similar to the previous test, but the first response results in BADTIME.
+ def test_badtime_then_validate(self):
+ fix_current_time(0x4da8877a)
+ tsig = self.createMessageAndSign(self.qid, self.test_name,
+ self.tsig_ctx)
+
+ # "advance the clock" and try validating, which should fail due to
+ # BADTIME
+ fix_current_time(0x4da8877a + 600)
+ self.commonVerifyChecks(self.tsig_ctx, tsig, DUMMY_DATA,
+ TSIGError.BAD_TIME, TSIGContext.STATE_SENT_REQUEST)
+
+ # revert the clock again.
+ fix_current_time(0x4da8877a)
+ self.createMessageFromFile("tsig_verify5.wire")
+ self.commonVerifyChecks(self.tsig_ctx, self.message.get_tsig_record(),
+ self.received_data, TSIGError.NOERROR,
+ TSIGContext.STATE_VERIFIED_RESPONSE)
+
+ # We don't allow empty MAC unless the TSIG error is BADSIG or BADKEY.
+ def test_empty_mac(self):
+ fix_current_time(0x4da8877a)
+
+ self.createMessageFromFile("tsig_verify7.wire")
+
+ self.commonVerifyChecks(self.tsig_verify_ctx,
+ self.message.get_tsig_record(),
+ self.received_data,
+ TSIGError.BAD_SIG,
+ TSIGContext.STATE_RECEIVED_REQUEST)
+
+ # If the empty MAC comes with a BADKEY error, the error is passed
+ # transparently.
+ self.createMessageFromFile("tsig_verify8.wire")
+ self.commonVerifyChecks(self.tsig_verify_ctx,
+ self.message.get_tsig_record(),
+ self.received_data,
+ TSIGError.BAD_KEY,
+ TSIGContext.STATE_RECEIVED_REQUEST)
+
+ # Once the context is used for sending a signed response, it shouldn't
+ # be used for further verification.
+ def test_verify_after_sendresponse(self):
+ fix_current_time(0x4da8877a)
+
+ self.createMessageFromFile("message_toWire2.wire")
+ self.tsig_verify_ctx.verify(self.message.get_tsig_record(),
+ self.received_data)
+ self.assertEqual(TSIGContext.STATE_RECEIVED_REQUEST,
+ self.tsig_verify_ctx.get_state())
+ self.createMessageAndSign(self.qid, self.test_name,
+ self.tsig_verify_ctx,
+ QR_FLAG|AA_FLAG|RD_FLAG, RRType.A(),
+ "192.0.2.1")
+ self.assertEqual(TSIGContext.STATE_SENT_RESPONSE,
+ self.tsig_verify_ctx.get_state())
+
+ # Now trying further verification.
+ self.createMessageFromFile("message_toWire2.wire")
+ self.assertRaises(TSIGContextError, self.tsig_verify_ctx.verify,
+ self.message.get_tsig_record(), self.received_data)
+
+ # Likewise, once the context verifies a response, it shouldn't for
+ # signing any more.
+ def test_sign_after_verified(self):
+ fix_current_time(0x4da8877a)
+
+ self.createMessageAndSign(self.qid, self.test_name, self.tsig_ctx)
+ self.createMessageFromFile("tsig_verify5.wire")
+ self.tsig_ctx.verify(self.message.get_tsig_record(),
+ self.received_data)
+ self.assertEqual(TSIGContext.STATE_VERIFIED_RESPONSE,
+ self.tsig_ctx.get_state())
+
+ # Now trying further signing.
+ self.assertRaises(TSIGContextError, self.createMessageAndSign,
+ self.qid, self.test_name, self.tsig_ctx)
+
+ # Too short MAC should be rejected.
+ # Note: when we implement RFC4635-based checks, the error code will
+ # (probably) be FORMERR.
+ def test_too_short_mac(self):
+ fix_current_time(0x4da8877a)
+ self.createMessageFromFile("tsig_verify10.wire")
+ self.commonVerifyChecks(self.tsig_verify_ctx,
+ self.message.get_tsig_record(),
+ self.received_data, TSIGError.BAD_SIG,
+ TSIGContext.STATE_RECEIVED_REQUEST)
if __name__ == '__main__':
unittest.main()
diff --git a/src/lib/dns/python/tests/tsig_rdata_python_test.py b/src/lib/dns/python/tests/tsig_rdata_python_test.py
new file mode 100644
index 0000000..7b861d6
--- /dev/null
+++ b/src/lib/dns/python/tests/tsig_rdata_python_test.py
@@ -0,0 +1,30 @@
+# 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.
+
+import unittest
+import sys
+from pydnspp import *
+
+class TSIGRdataTest(unittest.TestCase):
+ VALID_TEXT1 = "hmac-md5.sig-alg.reg.int. 1286779327 300 0 16020 BADKEY 0"
+ def test_from_string(self):
+ tsig = TSIG(self.VALID_TEXT1)
+ self.assertEqual(Name("hmac-md5.sig-alg.reg.int"),
+ tsig.get_algorithm())
+ # check there's no leak in creating the name object:
+ self.assertEqual(1, sys.getrefcount(tsig.get_algorithm()))
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/src/lib/dns/python/tests/tsigkey_python_test.py b/src/lib/dns/python/tests/tsigkey_python_test.py
index 305c8dd..516bea4 100644
--- a/src/lib/dns/python/tests/tsigkey_python_test.py
+++ b/src/lib/dns/python/tests/tsigkey_python_test.py
@@ -25,6 +25,9 @@ class TSIGKeyTest(unittest.TestCase):
TSIGKey.HMACMD5_NAME)
self.assertEqual(Name('hmac-sha1'), TSIGKey.HMACSHA1_NAME)
self.assertEqual(Name('hmac-sha256'), TSIGKey.HMACSHA256_NAME)
+ self.assertEqual(Name('hmac-sha224'), TSIGKey.HMACSHA224_NAME)
+ self.assertEqual(Name('hmac-sha384'), TSIGKey.HMACSHA384_NAME)
+ self.assertEqual(Name('hmac-sha512'), TSIGKey.HMACSHA512_NAME)
def test_init(self):
key = TSIGKey(self.key_name, TSIGKey.HMACMD5_NAME, self.secret)
diff --git a/src/lib/dns/python/tests/tsigrecord_python_test.py b/src/lib/dns/python/tests/tsigrecord_python_test.py
new file mode 100644
index 0000000..813a23b
--- /dev/null
+++ b/src/lib/dns/python/tests/tsigrecord_python_test.py
@@ -0,0 +1,44 @@
+# 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.
+
+import unittest
+import sys
+from pydnspp import *
+
+class TSIGRecordTest(unittest.TestCase):
+ def setUp(self):
+ self.test_name = Name("www.example.com")
+ self.test_rdata = TSIG("hmac-md5.sig-alg.reg.int. 1302890362 " + \
+ "300 16 2tra2tra2tra2tra2tra2g== " + \
+ "11621 0 0")
+ self.test_record = TSIGRecord(self.test_name, self.test_rdata)
+
+ def test_getname(self):
+ self.assertEqual(self.test_name, self.test_record.get_name())
+ self.assertEqual(1, sys.getrefcount(self.test_record.get_name()))
+
+ def test_get_length(self):
+ # see the C++ test for the magic number
+ self.assertEqual(85, self.test_record.get_length())
+
+ def test_to_text(self):
+ expected_text = "www.example.com. 0 ANY TSIG " + \
+ "hmac-md5.sig-alg.reg.int. 1302890362 300 16 " + \
+ "2tra2tra2tra2tra2tra2g== 11621 NOERROR 0\n"
+ self.assertEqual(expected_text, self.test_record.to_text())
+ self.assertEqual(expected_text, str(self.test_record))
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/src/lib/dns/python/tsig_python.cc b/src/lib/dns/python/tsig_python.cc
index 7b6ba01..db93a08 100644
--- a/src/lib/dns/python/tsig_python.cc
+++ b/src/lib/dns/python/tsig_python.cc
@@ -12,9 +12,30 @@
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
+#define PY_SSIZE_T_CLEAN // need for "y#" below
+#include <Python.h>
+
+#include <string>
+#include <stdexcept>
+
+#include <exceptions/exceptions.h>
+
+#include <util/python/pycppwrapper_util.h>
+
#include <dns/tsig.h>
+#include "pydnspp_common.h"
+#include "name_python.h"
+#include "tsigkey_python.h"
+#include "tsigerror_python.h"
+#include "tsigrecord_python.h"
+#include "tsig_python.h"
+
+using namespace std;
+using namespace isc;
+using namespace isc::util::python;
using namespace isc::dns;
+using namespace isc::dns::python;
//
// Definition of the classes
@@ -24,12 +45,17 @@ using namespace isc::dns;
// and static wrappers around the methods we export), a list of methods,
// and a type description
+//
+// TSIGContext
+//
+
+// Trivial constructor.
+s_TSIGContext::s_TSIGContext() : cppobj(NULL) {
+}
+
namespace {
-// The s_* Class simply covers one instantiation of the object
-class s_TSIGContext : public PyObject {
-public:
- TSIGContext* tsig_ctx;
-};
+// Shortcut type which would be convenient for adding class variables safely.
+typedef CPPPyObjectContainer<s_TSIGContext, TSIGContext> TSIGContextContainer;
//
// We declare the functions here, the definitions are below
@@ -40,6 +66,12 @@ public:
int TSIGContext_init(s_TSIGContext* self, PyObject* args);
void TSIGContext_destroy(s_TSIGContext* self);
+// Class specific methods
+PyObject* TSIGContext_getState(s_TSIGContext* self);
+PyObject* TSIGContext_getError(s_TSIGContext* self);
+PyObject* TSIGContext_sign(s_TSIGContext* self, PyObject* args);
+PyObject* TSIGContext_verify(s_TSIGContext* self, PyObject* args);
+
// These are the functions we export
// For a minimal support, we don't need them.
@@ -50,18 +82,180 @@ void TSIGContext_destroy(s_TSIGContext* self);
// 3. Argument type
// 4. Documentation
PyMethodDef TSIGContext_methods[] = {
+ { "get_state", reinterpret_cast<PyCFunction>(TSIGContext_getState),
+ METH_NOARGS,
+ "Return the current state of the context (mainly for tests)" },
+ { "get_error", reinterpret_cast<PyCFunction>(TSIGContext_getError),
+ METH_NOARGS,
+ "Return the TSIG error as a result of the latest verification" },
+ { "sign",
+ reinterpret_cast<PyCFunction>(TSIGContext_sign), METH_VARARGS,
+ "Sign a DNS message." },
+ { "verify",
+ reinterpret_cast<PyCFunction>(TSIGContext_verify), METH_VARARGS,
+ "Verify a DNS message." },
{ NULL, NULL, 0, NULL }
};
+int
+TSIGContext_init(s_TSIGContext* self, PyObject* args) {
+ try {
+ // "From key" constructor
+ const s_TSIGKey* tsigkey_obj;
+ if (PyArg_ParseTuple(args, "O!", &tsigkey_type, &tsigkey_obj)) {
+ self->cppobj = new TSIGContext(*tsigkey_obj->cppobj);
+ return (0);
+ }
+
+ // "From key param + keyring" constructor
+ PyErr_Clear();
+ const s_Name* keyname_obj;
+ const s_Name* algname_obj;
+ const s_TSIGKeyRing* keyring_obj;
+ if (PyArg_ParseTuple(args, "O!O!O!", &name_type, &keyname_obj,
+ &name_type, &algname_obj, &tsigkeyring_type,
+ &keyring_obj)) {
+ self->cppobj = new TSIGContext(*keyname_obj->cppobj,
+ *algname_obj->cppobj,
+ *keyring_obj->cppobj);
+ return (0);
+ }
+ } catch (const exception& ex) {
+ const string ex_what = "Failed to construct TSIGContext object: " +
+ string(ex.what());
+ PyErr_SetString(po_IscException, ex_what.c_str());
+ return (-1);
+ } catch (...) {
+ PyErr_SetString(po_IscException,
+ "Unexpected exception in constructing TSIGContext");
+ return (-1);
+ }
+
+ PyErr_SetString(PyExc_TypeError,
+ "Invalid arguments to TSIGContext constructor");
+
+ return (-1);
+}
+
+void
+TSIGContext_destroy(s_TSIGContext* const self) {
+ delete self->cppobj;
+ self->cppobj = NULL;
+ Py_TYPE(self)->tp_free(self);
+}
+
+PyObject*
+TSIGContext_getState(s_TSIGContext* self) {
+ return (Py_BuildValue("I", self->cppobj->getState()));
+}
+
+PyObject*
+TSIGContext_getError(s_TSIGContext* self) {
+ try {
+ PyObjectContainer container(createTSIGErrorObject(
+ self->cppobj->getError()));
+ return (Py_BuildValue("O", container.get()));
+ } catch (const exception& ex) {
+ const string ex_what =
+ "Unexpectedly failed to get TSIGContext error: " +
+ string(ex.what());
+ PyErr_SetString(po_IscException, ex_what.c_str());
+ } catch (...) {
+ PyErr_SetString(po_IscException,
+ "Unexpected exception in TSIGContext.get_error");
+ }
+ return (NULL);
+}
+
+PyObject*
+TSIGContext_sign(s_TSIGContext* self, PyObject* args) {
+ long qid = 0;
+ const char* mac;
+ Py_ssize_t mac_size;
+
+ if (PyArg_ParseTuple(args, "ly#", &qid, &mac, &mac_size)) {
+ if (qid < 0 || qid > 0xffff) {
+ PyErr_SetString(PyExc_ValueError,
+ "TSIGContext.sign: QID out of range");
+ return (NULL);
+ }
+
+ try {
+ ConstTSIGRecordPtr record = self->cppobj->sign(qid, mac, mac_size);
+ return (createTSIGRecordObject(*record));
+ } catch (const TSIGContextError& ex) {
+ PyErr_SetString(po_TSIGContextError, ex.what());
+ } catch (const exception& ex) {
+ const string ex_what = "Unexpected failure in TSIG sign: " +
+ string(ex.what());
+ PyErr_SetString(po_IscException, ex_what.c_str());
+ } catch (...) {
+ PyErr_SetString(PyExc_SystemError,
+ "Unexpected failure in TSIG sign");
+ }
+ } else {
+ PyErr_SetString(PyExc_TypeError,
+ "Invalid arguments to TSIGContext.sign");
+ }
+
+ return (NULL);
+}
+
+PyObject*
+TSIGContext_verify(s_TSIGContext* self, PyObject* args) {
+ const char* data;
+ Py_ssize_t data_len;
+ s_TSIGRecord* py_record;
+ PyObject* py_maybe_none;
+ TSIGRecord* record;
+
+ if (PyArg_ParseTuple(args, "O!y#", &tsigrecord_type, &py_record,
+ &data, &data_len)) {
+ record = py_record->cppobj;
+ } else if (PyArg_ParseTuple(args, "Oy#", &py_maybe_none, &data,
+ &data_len)) {
+ record = NULL;
+ } else {
+ PyErr_SetString(PyExc_TypeError,
+ "Invalid arguments to TSIGContext.verify");
+ return (NULL);
+ }
+ PyErr_Clear();
+
+ try {
+ const TSIGError error = self->cppobj->verify(record, data, data_len);
+ return (createTSIGErrorObject(error));
+ } catch (const TSIGContextError& ex) {
+ PyErr_SetString(po_TSIGContextError, ex.what());
+ } catch (const InvalidParameter& ex) {
+ PyErr_SetString(po_InvalidParameter, ex.what());
+ } catch (const exception& ex) {
+ const string ex_what = "Unexpected failure in TSIG verify: " +
+ string(ex.what());
+ PyErr_SetString(po_IscException, ex_what.c_str());
+ } catch (...) {
+ PyErr_SetString(PyExc_SystemError, "Unexpected failure in TSIG verify");
+ }
+
+ return (NULL);
+}
+} // end of unnamed namespace
+
+namespace isc {
+namespace dns {
+namespace python {
+// Definition of class specific exception(s)
+PyObject* po_TSIGContextError;
+
// This defines the complete type for reflection in python and
-// parsing of PyObject* to s_EDNS
+// parsing of PyObject* to s_TSIGContext
// Most of the functions are not actually implemented and NULL here.
-PyTypeObject tsig_context_type = {
+PyTypeObject tsigcontext_type = {
PyVarObject_HEAD_INIT(NULL, 0)
- "libdns_python.TSIGContext",
- sizeof(s_TSIGContext), // tp_basicsize
+ "pydnspp.TSIGContext",
+ sizeof(s_TSIGContext), // tp_basicsize
0, // tp_itemsize
- (destructor)TSIGContext_destroy, // tp_dealloc
+ reinterpret_cast<destructor>(TSIGContext_destroy), // tp_dealloc
NULL, // tp_print
NULL, // tp_getattr
NULL, // tp_setattr
@@ -76,16 +270,22 @@ PyTypeObject tsig_context_type = {
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.",
+
+ // We allow the python version of TSIGContext to act as a base class.
+ // From pure design point of view, this is wrong because it's not intended
+ // to be inherited. However, cryptographic operations are generally
+ // difficult to test, so it would be very advantageous if we can define
+ // a mock context class.
+ Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, // tp_flags
+
+ "The TSIGContext class objects is...(COMPLETE THIS)",
NULL, // tp_traverse
NULL, // tp_clear
- NULL, // tp_richcompare
+ NULL, // tp_richcompare
0, // tp_weaklistoffset
NULL, // tp_iter
NULL, // tp_iternext
- TSIGContext_methods, // tp_methods
+ TSIGContext_methods, // tp_methods
NULL, // tp_members
NULL, // tp_getset
NULL, // tp_base
@@ -93,7 +293,7 @@ PyTypeObject tsig_context_type = {
NULL, // tp_descr_get
NULL, // tp_descr_set
0, // tp_dictoffset
- (initproc)TSIGContext_init, // tp_init
+ reinterpret_cast<initproc>(TSIGContext_init), // tp_init
NULL, // tp_alloc
PyType_GenericNew, // tp_new
NULL, // tp_free
@@ -107,50 +307,58 @@ PyTypeObject tsig_context_type = {
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) {
+ if (PyType_Ready(&tsigcontext_type) < 0) {
return (false);
}
- Py_INCREF(&tsig_context_type);
- void* p = &tsig_context_type;
- PyModule_AddObject(mod, "TSIGContext", static_cast<PyObject*>(p));
+ void* p = &tsigcontext_type;
+ if (PyModule_AddObject(mod, "TSIGContext",
+ static_cast<PyObject*>(p)) < 0) {
+ return (false);
+ }
+ Py_INCREF(&tsigcontext_type);
+
+ try {
+ // Class specific exceptions
+ po_TSIGContextError = PyErr_NewException("pydnspp.TSIGContextError",
+ po_IscException, NULL);
+ PyObjectContainer(po_TSIGContextError).installToModule(
+ mod, "TSIGContextError");
+
+ // Constant class variables
+ installClassVariable(tsigcontext_type, "STATE_INIT",
+ Py_BuildValue("I", TSIGContext::INIT));
+ installClassVariable(tsigcontext_type, "STATE_SENT_REQUEST",
+ Py_BuildValue("I", TSIGContext::SENT_REQUEST));
+ installClassVariable(tsigcontext_type, "STATE_RECEIVED_REQUEST",
+ Py_BuildValue("I", TSIGContext::RECEIVED_REQUEST));
+ installClassVariable(tsigcontext_type, "STATE_SENT_RESPONSE",
+ Py_BuildValue("I", TSIGContext::SENT_RESPONSE));
+ installClassVariable(tsigcontext_type, "STATE_VERIFIED_RESPONSE",
+ Py_BuildValue("I",
+ TSIGContext::VERIFIED_RESPONSE));
- addClassVariable(tsig_context_type, "DEFAULT_FUDGE",
- Py_BuildValue("H", TSIGContext::DEFAULT_FUDGE));
+ installClassVariable(tsigcontext_type, "DEFAULT_FUDGE",
+ Py_BuildValue("H", TSIGContext::DEFAULT_FUDGE));
+ } catch (const exception& ex) {
+ const string ex_what =
+ "Unexpected failure in TSIGContext initialization: " +
+ string(ex.what());
+ PyErr_SetString(po_IscException, ex_what.c_str());
+ return (false);
+ } catch (...) {
+ PyErr_SetString(PyExc_SystemError,
+ "Unexpected failure in TSIGContext initialization");
+ return (false);
+ }
return (true);
}
-} // end of anonymous namespace
+} // namespace python
+} // namespace dns
+} // namespace isc
diff --git a/src/lib/dns/python/tsig_python.h b/src/lib/dns/python/tsig_python.h
new file mode 100644
index 0000000..f9b4f7b
--- /dev/null
+++ b/src/lib/dns/python/tsig_python.h
@@ -0,0 +1,47 @@
+// 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 __PYTHON_TSIGCONTEXT_H
+#define __PYTHON_TSIGCONTEXT_H 1
+
+#include <Python.h>
+
+namespace isc {
+namespace dns {
+class TSIGContext;
+
+namespace python {
+
+// The s_* Class simply covers one instantiation of the object
+class s_TSIGContext : public PyObject {
+public:
+ s_TSIGContext();
+ TSIGContext* cppobj;
+};
+
+extern PyTypeObject tsigcontext_type;
+
+// Class specific exceptions
+extern PyObject* po_TSIGContextError;
+
+bool initModulePart_TSIGContext(PyObject* mod);
+
+} // namespace python
+} // namespace dns
+} // namespace isc
+#endif // __PYTHON_TSIGCONTEXT_H
+
+// Local Variables:
+// mode: c++
+// End:
diff --git a/src/lib/dns/python/tsig_rdata_python.cc b/src/lib/dns/python/tsig_rdata_python.cc
new file mode 100644
index 0000000..4e4f287
--- /dev/null
+++ b/src/lib/dns/python/tsig_rdata_python.cc
@@ -0,0 +1,369 @@
+// 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 <Python.h>
+
+#include <string>
+#include <stdexcept>
+
+#include <util/python/pycppwrapper_util.h>
+
+#include <dns/rdataclass.h>
+
+#include "pydnspp_common.h"
+#include "pydnspp_towire.h"
+#include "name_python.h"
+#include "tsig_rdata_python.h"
+
+using namespace std;
+using namespace isc::util::python;
+using namespace isc::dns;
+using namespace isc::dns::rdata;
+using namespace isc::dns::python;
+
+//
+// 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
+
+//
+// TSIG RDATA
+//
+
+// Trivial constructor.
+s_TSIG::s_TSIG() : cppobj(NULL) {
+}
+
+namespace {
+// Shortcut type which would be convenient for adding class variables safely.
+typedef CPPPyObjectContainer<s_TSIG, any::TSIG> TSIGContainer;
+
+//
+// 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 TSIG_init(s_TSIG* self, PyObject* args);
+void TSIG_destroy(s_TSIG* self);
+
+// These are the functions we export
+// ADD/REMOVE/MODIFY THE FOLLOWING AS APPROPRIATE FOR THE ACTUAL CLASS.
+//
+PyObject* TSIG_toText(const s_TSIG* const self);
+PyObject* TSIG_getAlgorithm(const s_TSIG* const self);
+PyObject* TSIG_getTimeSigned(const s_TSIG* const self);
+PyObject* TSIG_getFudge(const s_TSIG* const self);
+PyObject* TSIG_getOriginalID(const s_TSIG* const self);
+PyObject* TSIG_getError(const s_TSIG* const self);
+PyObject* TSIG_getMAC(const s_TSIG* const self);
+PyObject* TSIG_getOtherData(const s_TSIG* const self);
+PyObject* TSIG_str(PyObject* self);
+PyObject* TSIG_richcmp(const s_TSIG* const self,
+ const s_TSIG* const other, int op);
+PyObject* TSIG_toWire(const s_TSIG* self, PyObject* args);
+
+// 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 TSIG_methods[] = {
+ { "get_algorithm", reinterpret_cast<PyCFunction>(TSIG_getAlgorithm),
+ METH_NOARGS,
+ "Return the algorithm name." },
+ { "get_timesigned", reinterpret_cast<PyCFunction>(TSIG_getTimeSigned),
+ METH_NOARGS,
+ "Return the value of the Time Signed field. "
+ "The returned value does not exceed 2^48-1."
+ },
+ { "get_fudge", reinterpret_cast<PyCFunction>(TSIG_getFudge),
+ METH_NOARGS,
+ "Return the value of the Fudge field." },
+ { "get_original_id", reinterpret_cast<PyCFunction>(TSIG_getOriginalID),
+ METH_NOARGS,
+ "Return the value of the Original ID field." },
+ { "get_error", reinterpret_cast<PyCFunction>(TSIG_getError),
+ METH_NOARGS,
+ "Return the value of the Error field." },
+ { "get_mac", reinterpret_cast<PyCFunction>(TSIG_getMAC),
+ METH_NOARGS,
+ "Return the value of the MAC field."
+ "If it's empty, return None." },
+ { "get_other_data", reinterpret_cast<PyCFunction>(TSIG_getOtherData),
+ METH_NOARGS,
+ "Return the value of the Other Data field."
+ "If it's empty, return None." },
+ { "to_text", reinterpret_cast<PyCFunction>(TSIG_toText), METH_NOARGS,
+ "Returns the text representation" },
+ { "to_wire", reinterpret_cast<PyCFunction>(TSIG_toWire), METH_VARARGS,
+ "Converts the TSIG object to wire format.\n"
+ "The argument can be either a MessageRenderer or an object that "
+ "implements the sequence interface. If the object is mutable "
+ "(for instance a bytearray()), the wire data is added in-place.\n"
+ "If it is not (for instance a bytes() object), a new object is "
+ "returned" },
+ { NULL, NULL, 0, NULL }
+};
+
+int
+TSIG_init(s_TSIG* self, PyObject* args) {
+ try {
+ // constructor from string
+ const char* rdata_str;
+ if (PyArg_ParseTuple(args, "s", &rdata_str)) {
+ self->cppobj = new any::TSIG(string(rdata_str));
+ return (0);
+ }
+ } catch (const exception& ex) {
+ const string ex_what = "Failed to construct TSIG object: " +
+ string(ex.what());
+ PyErr_SetString(po_IscException, ex_what.c_str());
+ return (-1);
+ } catch (...) {
+ PyErr_SetString(po_IscException,
+ "Unexpected exception in constructing TSIG");
+ return (-1);
+ }
+
+ PyErr_SetString(PyExc_TypeError,
+ "Invalid arguments to TSIG constructor");
+
+ return (-1);
+}
+
+void
+TSIG_destroy(s_TSIG* const self) {
+ delete self->cppobj;
+ self->cppobj = NULL;
+ Py_TYPE(self)->tp_free(self);
+}
+
+PyObject*
+TSIG_getAlgorithm(const s_TSIG* const self) {
+ try {
+ return (createNameObject(self->cppobj->getAlgorithm()));
+ } catch (const exception& ex) {
+ const string ex_what =
+ "Failed to get TSIG algorithm: " + string(ex.what());
+ PyErr_SetString(po_IscException, ex_what.c_str());
+ } catch (...) {
+ PyErr_SetString(PyExc_SystemError, "Unexpected failure in "
+ "getting TSIG algorithm");
+ }
+ return (NULL);
+}
+
+PyObject*
+TSIG_getTimeSigned(const s_TSIG* const self) {
+ return (Py_BuildValue("K", self->cppobj->getTimeSigned()));
+}
+
+PyObject*
+TSIG_getFudge(const s_TSIG* const self) {
+ return (Py_BuildValue("H", self->cppobj->getFudge()));
+}
+
+PyObject*
+TSIG_getOriginalID(const s_TSIG* const self) {
+ return (Py_BuildValue("H", self->cppobj->getOriginalID()));
+}
+
+PyObject*
+TSIG_getError(const s_TSIG* const self) {
+ return (Py_BuildValue("H", self->cppobj->getError()));
+}
+
+PyObject*
+TSIG_getMAC(const s_TSIG* const self) {
+ return (Py_BuildValue("y#", self->cppobj->getMAC(),
+ self->cppobj->getMACSize()));
+}
+
+PyObject*
+TSIG_getOtherData(const s_TSIG* const self) {
+ return (Py_BuildValue("y#", self->cppobj->getOtherData(),
+ self->cppobj->getOtherLen()));
+}
+
+PyObject*
+TSIG_toText(const s_TSIG* const self) {
+ try {
+ // toText() could throw, so we need to catch any exceptions below.
+ return (Py_BuildValue("s", self->cppobj->toText().c_str()));
+ } catch (const exception& ex) {
+ const string ex_what =
+ "Failed to convert TSIG object to text: " +
+ string(ex.what());
+ PyErr_SetString(po_IscException, ex_what.c_str());
+ } catch (...) {
+ PyErr_SetString(PyExc_SystemError, "Unexpected failure in "
+ "converting TSIG object to text");
+ }
+ return (NULL);
+}
+
+PyObject*
+TSIG_str(PyObject* self) {
+ // Simply call the to_text method we already defined
+ return (PyObject_CallMethod(self, const_cast<char*>("to_text"),
+ const_cast<char*>("")));
+}
+
+PyObject*
+TSIG_toWire(const s_TSIG* const self, PyObject* args) {
+ typedef any::TSIG TSIGRdata;
+ return (toWireWrapper<s_TSIG, TSIGRdata, ToWireCallVoid<const TSIGRdata> >(
+ self, args));
+}
+
+PyObject*
+TSIG_richcmp(const s_TSIG* const self,
+ const s_TSIG* const other,
+ const int op)
+{
+ bool c = false;
+
+ // Check for null and if the types match. If different type,
+ // simply return False
+ if (other == NULL || (self->ob_type != other->ob_type)) {
+ Py_RETURN_FALSE;
+ }
+
+ // Only equals and not equals here, unorderable type
+ const int cmp = self->cppobj->compare(*other->cppobj);
+ switch (op) {
+ case Py_EQ:
+ c = (cmp == 0);
+ break;
+ case Py_NE:
+ c = (cmp != 0);
+ break;
+ case Py_GT:
+ c = (cmp > 0);
+ break;
+ case Py_GE:
+ c = (cmp >= 0);
+ break;
+ case Py_LT:
+ c = (cmp < 0);
+ break;
+ case Py_LE:
+ c = (cmp <= 0);
+ break;
+ default:
+ PyErr_SetString(PyExc_IndexError,
+ "Unhandled rich comparison operator for TSIG");
+ return (NULL);
+ }
+ if (c) {
+ Py_RETURN_TRUE;
+ } else {
+ Py_RETURN_FALSE;
+ }
+}
+} // end of unnamed namespace
+
+namespace isc {
+namespace dns {
+namespace python {
+// This defines the complete type for reflection in python and
+// parsing of PyObject* to s_TSIG
+// Most of the functions are not actually implemented and NULL here.
+PyTypeObject tsig_type = {
+ PyVarObject_HEAD_INIT(NULL, 0)
+ "pydnspp.TSIG",
+ sizeof(s_TSIG), // tp_basicsize
+ 0, // tp_itemsize
+ reinterpret_cast<destructor>(TSIG_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
+ TSIG_str, // tp_str
+ NULL, // tp_getattro
+ NULL, // tp_setattro
+ NULL, // tp_as_buffer
+ Py_TPFLAGS_DEFAULT, // tp_flags
+ "The TSIG class objects represents the TSIG RDATA as defined in RFC2845.",
+ NULL, // tp_traverse
+ NULL, // tp_clear
+ reinterpret_cast<richcmpfunc>(TSIG_richcmp), // tp_richcompare
+ 0, // tp_weaklistoffset
+ NULL, // tp_iter
+ NULL, // tp_iternext
+ TSIG_methods, // tp_methods
+ NULL, // tp_members
+ NULL, // tp_getset
+ // At the moment, we leave tp_base NULL as we won't use this class
+ // in a polymorphic way for our immediate need.
+ NULL, // tp_base
+ NULL, // tp_dict
+ NULL, // tp_descr_get
+ NULL, // tp_descr_set
+ 0, // tp_dictoffset
+ reinterpret_cast<initproc>(TSIG_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
+};
+
+// Module Initialization, all statics are initialized here
+bool
+initModulePart_TSIG(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_type) < 0) {
+ return (false);
+ }
+ void* p = &tsig_type;
+ if (PyModule_AddObject(mod, "TSIG", static_cast<PyObject*>(p)) < 0) {
+ return (false);
+ }
+ Py_INCREF(&tsig_type);
+
+ return (true);
+}
+
+PyObject*
+createTSIGObject(const any::TSIG& source) {
+ TSIGContainer container = PyObject_New(s_TSIG, &tsig_type);
+ container.set(new any::TSIG(source));
+ return (container.release());
+}
+} // namespace python
+} // namespace dns
+} // namespace isc
diff --git a/src/lib/dns/python/tsig_rdata_python.h b/src/lib/dns/python/tsig_rdata_python.h
new file mode 100644
index 0000000..e5e0c6c
--- /dev/null
+++ b/src/lib/dns/python/tsig_rdata_python.h
@@ -0,0 +1,57 @@
+// 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 __PYTHON_TSIG_H
+#define __PYTHON_TSIG_H 1
+
+#include <Python.h>
+
+namespace isc {
+namespace dns {
+namespace rdata {
+namespace any {
+class TSIG;
+}
+}
+
+namespace python {
+
+// The s_* Class simply covers one instantiation of the object
+class s_TSIG : public PyObject {
+public:
+ s_TSIG();
+ const rdata::any::TSIG* cppobj;
+};
+
+extern PyTypeObject tsig_type;
+
+bool initModulePart_TSIG(PyObject* mod);
+
+/// This is A simple shortcut to create a python TSIG object (in the
+/// form of a pointer to PyObject) with minimal exception safety.
+/// On success, it returns a valid pointer to PyObject with a reference
+/// counter of 1; if something goes wrong it throws an exception (it never
+/// returns a NULL pointer).
+/// This function is expected to be called with in a try block
+/// followed by necessary setup for python exception.
+PyObject* createTSIGObject(const rdata::any::TSIG& source);
+
+} // namespace python
+} // namespace dns
+} // namespace isc
+#endif // __PYTHON_TSIG_H
+
+// Local Variables:
+// mode: c++
+// End:
diff --git a/src/lib/dns/python/tsigerror_python.cc b/src/lib/dns/python/tsigerror_python.cc
index e973772..0ad4716 100644
--- a/src/lib/dns/python/tsigerror_python.cc
+++ b/src/lib/dns/python/tsigerror_python.cc
@@ -106,6 +106,7 @@ TSIGError_init(s_TSIGError* self, PyObject* args) {
}
// Constructor from Rcode
+ PyErr_Clear();
s_Rcode* py_rcode;
if (PyArg_ParseTuple(args, "O!", &rcode_type, &py_rcode)) {
self->cppobj = new TSIGError(*py_rcode->cppobj);
@@ -239,7 +240,7 @@ namespace python {
// Most of the functions are not actually implemented and NULL here.
PyTypeObject tsigerror_type = {
PyVarObject_HEAD_INIT(NULL, 0)
- "libdns_python.TSIGError",
+ "pydnspp.TSIGError",
sizeof(s_TSIGError), // tp_basicsize
0, // tp_itemsize
reinterpret_cast<destructor>(TSIGError_destroy), // tp_dealloc
@@ -357,6 +358,13 @@ initModulePart_TSIGError(PyObject* mod) {
return (true);
}
+
+PyObject*
+createTSIGErrorObject(const TSIGError& source) {
+ TSIGErrorContainer container = PyObject_New(s_TSIGError, &tsigerror_type);
+ container.set(new TSIGError(source));
+ return (container.release());
+}
} // namespace python
} // namespace dns
} // namespace isc
diff --git a/src/lib/dns/python/tsigerror_python.h b/src/lib/dns/python/tsigerror_python.h
index f0c66cc..735a480 100644
--- a/src/lib/dns/python/tsigerror_python.h
+++ b/src/lib/dns/python/tsigerror_python.h
@@ -34,6 +34,14 @@ extern PyTypeObject tsigerror_type;
bool initModulePart_TSIGError(PyObject* mod);
+/// This is A simple shortcut to create a python TSIGError object (in the
+/// form of a pointer to PyObject) with minimal exception safety.
+/// On success, it returns a valid pointer to PyObject with a reference
+/// counter of 1; if something goes wrong it throws an exception (it never
+/// returns a NULL pointer).
+/// This function is expected to be called with in a try block
+/// followed by necessary setup for python exception.
+PyObject* createTSIGErrorObject(const TSIGError& source);
} // namespace python
} // namespace dns
} // namespace isc
diff --git a/src/lib/dns/python/tsigkey_python.cc b/src/lib/dns/python/tsigkey_python.cc
index 4ca7bcd..f0906cb 100644
--- a/src/lib/dns/python/tsigkey_python.cc
+++ b/src/lib/dns/python/tsigkey_python.cc
@@ -12,12 +12,24 @@
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
-#include <new>
+#include <Python.h>
+#include <stdexcept>
+
+#include <util/python/pycppwrapper_util.h>
+
+#include <dns/name.h>
#include <dns/tsigkey.h>
+#include <dns/rdata.h>
+#include "pydnspp_common.h"
+#include "name_python.h"
+#include "tsigkey_python.h"
+
+using namespace std;
+using namespace isc::util::python;
using namespace isc::dns;
-using namespace isc::dns::rdata;
+using namespace isc::dns::python;
//
// Definition of the classes
@@ -27,19 +39,15 @@ using namespace isc::dns::rdata;
// and static wrappers around the methods we export), a list of methods,
// and a type description
-namespace {
//
// TSIGKey
//
// The s_* Class simply covers one instantiation of the object
-class s_TSIGKey : public PyObject {
-public:
- s_TSIGKey() : tsigkey(NULL) {}
- TSIGKey* tsigkey;
-};
+s_TSIGKey::s_TSIGKey() : cppobj(NULL) {}
+namespace {
//
// We declare the functions here, the definitions are below
// the type definition of the object, since both can use the other
@@ -78,12 +86,105 @@ PyMethodDef TSIGKey_methods[] = {
{ NULL, NULL, 0, NULL }
};
+int
+TSIGKey_init(s_TSIGKey* self, PyObject* args) {
+ try {
+ const char* str;
+ if (PyArg_ParseTuple(args, "s", &str)) {
+ self->cppobj = new TSIGKey(str);
+ return (0);
+ }
+
+ PyErr_Clear();
+ const s_Name* key_name;
+ const s_Name* algorithm_name;
+ PyObject* bytes_obj;
+ const char* secret;
+ Py_ssize_t secret_len;
+ if (PyArg_ParseTuple(args, "O!O!O", &name_type, &key_name,
+ &name_type, &algorithm_name, &bytes_obj) &&
+ PyObject_AsCharBuffer(bytes_obj, &secret, &secret_len) == 0) {
+ if (secret_len == 0) {
+ secret = NULL;
+ }
+ self->cppobj = new TSIGKey(*key_name->cppobj,
+ *algorithm_name->cppobj,
+ secret, secret_len);
+ return (0);
+ }
+ } catch (const isc::InvalidParameter& ex) {
+ PyErr_SetString(po_InvalidParameter, ex.what());
+ return (-1);
+ } catch (...) {
+ PyErr_SetString(po_IscException, "Unexpected exception");
+ return (-1);
+ }
+
+ PyErr_Clear();
+ PyErr_SetString(PyExc_TypeError,
+ "Invalid arguments to TSIGKey constructor");
+
+ return (-1);
+}
+
+void
+TSIGKey_destroy(s_TSIGKey* const self) {
+ delete self->cppobj;
+ self->cppobj = NULL;
+ Py_TYPE(self)->tp_free(self);
+}
+
+PyObject*
+TSIGKey_getKeyName(const s_TSIGKey* const self) {
+ try {
+ return (createNameObject(self->cppobj->getKeyName()));
+ } catch (const exception& ex) {
+ const string ex_what =
+ "Failed to get key name of TSIGKey: " + string(ex.what());
+ PyErr_SetString(po_IscException, ex_what.c_str());
+ } catch (...) {
+ PyErr_SetString(PyExc_SystemError, "Unexpected failure in "
+ "getting key name of TSIGKey");
+ }
+ return (NULL);
+}
+
+PyObject*
+TSIGKey_getAlgorithmName(const s_TSIGKey* const self) {
+ try {
+ return (createNameObject(self->cppobj->getAlgorithmName()));
+ } catch (const exception& ex) {
+ const string ex_what =
+ "Failed to get algorithm name of TSIGKey: " + string(ex.what());
+ PyErr_SetString(po_IscException, ex_what.c_str());
+ } catch (...) {
+ PyErr_SetString(PyExc_SystemError, "Unexpected failure in "
+ "getting algorithm name of TSIGKey");
+ }
+ return (NULL);
+}
+
+PyObject*
+TSIGKey_getSecret(const s_TSIGKey* const self) {
+ return (Py_BuildValue("y#", self->cppobj->getSecret(),
+ self->cppobj->getSecretLength()));
+}
+
+PyObject*
+TSIGKey_toText(const s_TSIGKey* self) {
+ return (Py_BuildValue("s", self->cppobj->toText().c_str()));
+}
+} // end of unnamed namespace
+
+namespace isc {
+namespace dns {
+namespace python {
// 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 tsigkey_type = {
PyVarObject_HEAD_INIT(NULL, 0)
- "libdns_python.TSIGKey",
+ "pydnspp.TSIGKey",
sizeof(s_TSIGKey), // tp_basicsize
0, // tp_itemsize
(destructor)TSIGKey_destroy, // tp_dealloc
@@ -132,89 +233,6 @@ PyTypeObject tsigkey_type = {
0 // tp_version_tag
};
-// A helper function to build a python "Name" object with error handling
-// encapsulated.
-s_Name*
-createNameObject(const Name& source) {
- s_Name* name = PyObject_New(s_Name, &name_type);
- if (name == NULL) {
- return (NULL);
- }
- name->name = new(nothrow) Name(source);
- if (name->name == NULL) {
- Py_DECREF(name);
- PyErr_SetString(po_IscException, "Allocating Name object failed");
- return (NULL);
- }
- return (name);
-}
-
-int
-TSIGKey_init(s_TSIGKey* self, PyObject* args) {
- const char* str;
-
- const s_Name* key_name;
- const s_Name* algorithm_name;
- PyObject* bytes_obj;
- const char* secret;
- Py_ssize_t secret_len;
-
-
- try {
- if (PyArg_ParseTuple(args, "s", &str)) {
- self->tsigkey = new TSIGKey(str);
- return (0);
- } else if (PyArg_ParseTuple(args, "O!O!O", &name_type, &key_name,
- &name_type, &algorithm_name, &bytes_obj) &&
- PyObject_AsCharBuffer(bytes_obj, &secret, &secret_len) != -1) {
- self->tsigkey = new TSIGKey(*key_name->name,
- *algorithm_name->name,
- secret, secret_len);
- return (0);
- }
- } catch (const isc::InvalidParameter& ex) {
- PyErr_SetString(po_InvalidParameter, ex.what());
- return (-1);
- } catch (...) {
- PyErr_SetString(po_IscException, "Unexpected exception");
- return (-1);
- }
-
- PyErr_Clear();
- PyErr_SetString(PyExc_TypeError,
- "Invalid arguments to TSIGKey constructor");
-
- return (-1);
-}
-
-void
-TSIGKey_destroy(s_TSIGKey* const self) {
- delete self->tsigkey;
- self->tsigkey = NULL;
- Py_TYPE(self)->tp_free(self);
-}
-
-PyObject*
-TSIGKey_getKeyName(const s_TSIGKey* const self) {
- return (createNameObject(self->tsigkey->getKeyName()));
-}
-
-PyObject*
-TSIGKey_getAlgorithmName(const s_TSIGKey* const self) {
- return (createNameObject(self->tsigkey->getAlgorithmName()));
-}
-
-PyObject*
-TSIGKey_getSecret(const s_TSIGKey* const self) {
- return (Py_BuildValue("y#", self->tsigkey->getSecret(),
- self->tsigkey->getSecretLength()));
-}
-
-PyObject*
-TSIGKey_toText(const s_TSIGKey* self) {
- return (Py_BuildValue("s", self->tsigkey->toText().c_str()));
-}
-
// Module Initialization, all statics are initialized here
bool
initModulePart_TSIGKey(PyObject* mod) {
@@ -224,33 +242,43 @@ initModulePart_TSIGKey(PyObject* mod) {
if (PyType_Ready(&tsigkey_type) < 0) {
return (false);
}
- Py_INCREF(&tsigkey_type);
void* p = &tsigkey_type;
if (PyModule_AddObject(mod, "TSIGKey", static_cast<PyObject*>(p)) != 0) {
- Py_DECREF(&tsigkey_type);
return (false);
}
+ Py_INCREF(&tsigkey_type);
- s_Name* name;
- if ((name = createNameObject(TSIGKey::HMACMD5_NAME())) == NULL) {
- goto cleanup;
- }
- addClassVariable(tsigkey_type, "HMACMD5_NAME", name);
- if ((name = createNameObject(TSIGKey::HMACSHA1_NAME())) == NULL) {
- goto cleanup;
- }
- addClassVariable(tsigkey_type, "HMACSHA1_NAME", name);
- if ((name = createNameObject(TSIGKey::HMACSHA256_NAME())) == NULL) {
- goto cleanup;
+ try {
+ // Constant class variables
+ installClassVariable(tsigkey_type, "HMACMD5_NAME",
+ createNameObject(TSIGKey::HMACMD5_NAME()));
+ installClassVariable(tsigkey_type, "HMACSHA1_NAME",
+ createNameObject(TSIGKey::HMACSHA1_NAME()));
+ installClassVariable(tsigkey_type, "HMACSHA256_NAME",
+ createNameObject(TSIGKey::HMACSHA256_NAME()));
+ installClassVariable(tsigkey_type, "HMACSHA224_NAME",
+ createNameObject(TSIGKey::HMACSHA224_NAME()));
+ installClassVariable(tsigkey_type, "HMACSHA384_NAME",
+ createNameObject(TSIGKey::HMACSHA384_NAME()));
+ installClassVariable(tsigkey_type, "HMACSHA512_NAME",
+ createNameObject(TSIGKey::HMACSHA512_NAME()));
+ } catch (const exception& ex) {
+ const string ex_what =
+ "Unexpected failure in TSIGKey initialization: " +
+ string(ex.what());
+ PyErr_SetString(po_IscException, ex_what.c_str());
+ return (false);
+ } catch (...) {
+ PyErr_SetString(PyExc_SystemError,
+ "Unexpected failure in TSIGKey initialization");
+ return (false);
}
- addClassVariable(tsigkey_type, "HMACSHA256_NAME", name);
return (true);
-
- cleanup:
- Py_DECREF(&tsigkey_type);
- return (false);
}
+} // namespace python
+} // namespace dns
+} // namespace isc
//
// End of TSIGKey
//
@@ -263,12 +291,9 @@ initModulePart_TSIGKey(PyObject* mod) {
// The s_* Class simply covers one instantiation of the object
-class s_TSIGKeyRing : public PyObject {
-public:
- s_TSIGKeyRing() : keyring(NULL) {}
- TSIGKeyRing* keyring;
-};
+s_TSIGKeyRing::s_TSIGKeyRing() : cppobj(NULL) {}
+namespace {
//
// We declare the functions here, the definitions are below
// the type definition of the object, since both can use the other
@@ -296,56 +321,6 @@ PyMethodDef TSIGKeyRing_methods[] = {
{ NULL, NULL, 0, NULL }
};
-PyTypeObject tsigkeyring_type = {
- PyVarObject_HEAD_INIT(NULL, 0)
- "libdns_python.TSIGKeyRing",
- sizeof(s_TSIGKeyRing), // tp_basicsize
- 0, // tp_itemsize
- (destructor)TSIGKeyRing_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
- "A simple repository of a set of TSIGKey objects.",
- NULL, // tp_traverse
- NULL, // tp_clear
- NULL, // tp_richcompare
- 0, // tp_weaklistoffset
- NULL, // tp_iter
- NULL, // tp_iternext
- TSIGKeyRing_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)TSIGKeyRing_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
TSIGKeyRing_init(s_TSIGKeyRing* self, PyObject* args) {
if (!PyArg_ParseTuple(args, "")) {
@@ -355,8 +330,8 @@ TSIGKeyRing_init(s_TSIGKeyRing* self, PyObject* args) {
return (-1);
}
- self->keyring = new(nothrow) TSIGKeyRing();
- if (self->keyring == NULL) {
+ self->cppobj = new(nothrow) TSIGKeyRing();
+ if (self->cppobj == NULL) {
PyErr_SetString(po_IscException, "Allocating TSIGKeyRing failed");
return (-1);
}
@@ -366,14 +341,14 @@ TSIGKeyRing_init(s_TSIGKeyRing* self, PyObject* args) {
void
TSIGKeyRing_destroy(s_TSIGKeyRing* self) {
- delete self->keyring;
- self->keyring = NULL;
+ delete self->cppobj;
+ self->cppobj = NULL;
Py_TYPE(self)->tp_free(self);
}
PyObject*
TSIGKeyRing_size(const s_TSIGKeyRing* const self) {
- return (Py_BuildValue("I", self->keyring->size()));
+ return (Py_BuildValue("I", self->cppobj->size()));
}
PyObject*
@@ -383,7 +358,7 @@ TSIGKeyRing_add(const s_TSIGKeyRing* const self, PyObject* args) {
if (PyArg_ParseTuple(args, "O!", &tsigkey_type, &tsigkey)) {
try {
const TSIGKeyRing::Result result =
- self->keyring->add(*tsigkey->tsigkey);
+ self->cppobj->add(*tsigkey->cppobj);
return (Py_BuildValue("I", result));
} catch (...) {
PyErr_SetString(po_IscException, "Unexpected exception");
@@ -403,7 +378,7 @@ TSIGKeyRing_remove(const s_TSIGKeyRing* self, PyObject* args) {
if (PyArg_ParseTuple(args, "O!", &name_type, &key_name)) {
const TSIGKeyRing::Result result =
- self->keyring->remove(*key_name->name);
+ self->cppobj->remove(*key_name->cppobj);
return (Py_BuildValue("I", result));
}
@@ -421,14 +396,14 @@ TSIGKeyRing_find(const s_TSIGKeyRing* self, PyObject* args) {
if (PyArg_ParseTuple(args, "O!O!", &name_type, &key_name,
&name_type, &algorithm_name)) {
const TSIGKeyRing::FindResult result =
- self->keyring->find(*key_name->name, *algorithm_name->name);
+ self->cppobj->find(*key_name->cppobj, *algorithm_name->cppobj);
if (result.key != NULL) {
s_TSIGKey* key = PyObject_New(s_TSIGKey, &tsigkey_type);
if (key == NULL) {
return (NULL);
}
- key->tsigkey = new(nothrow) TSIGKey(*result.key);
- if (key->tsigkey == NULL) {
+ key->cppobj = new(nothrow) TSIGKey(*result.key);
+ if (key->cppobj == NULL) {
Py_DECREF(key);
PyErr_SetString(po_IscException,
"Allocating TSIGKey object failed");
@@ -442,6 +417,60 @@ TSIGKeyRing_find(const s_TSIGKeyRing* self, PyObject* args) {
return (NULL);
}
+} // end of unnamed namespace
+
+namespace isc {
+namespace dns {
+namespace python {
+PyTypeObject tsigkeyring_type = {
+ PyVarObject_HEAD_INIT(NULL, 0)
+ "pydnspp.TSIGKeyRing",
+ sizeof(s_TSIGKeyRing), // tp_basicsize
+ 0, // tp_itemsize
+ (destructor)TSIGKeyRing_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
+ "A simple repository of a set of TSIGKey objects.",
+ NULL, // tp_traverse
+ NULL, // tp_clear
+ NULL, // tp_richcompare
+ 0, // tp_weaklistoffset
+ NULL, // tp_iter
+ NULL, // tp_iternext
+ TSIGKeyRing_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)TSIGKeyRing_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
+};
bool
initModulePart_TSIGKeyRing(PyObject* mod) {
@@ -465,5 +494,6 @@ initModulePart_TSIGKeyRing(PyObject* mod) {
return (true);
}
-
-} // end of unnamed namespace
+} // namespace python
+} // namespace dns
+} // namespace isc
diff --git a/src/lib/dns/python/tsigkey_python.h b/src/lib/dns/python/tsigkey_python.h
new file mode 100644
index 0000000..51b3ae7
--- /dev/null
+++ b/src/lib/dns/python/tsigkey_python.h
@@ -0,0 +1,53 @@
+// 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 __PYTHON_TSIGKEY_H
+#define __PYTHON_TSIGKEY_H 1
+
+#include <Python.h>
+
+namespace isc {
+namespace dns {
+class TSIGKey;
+class TSIGKeyRing;
+
+namespace python {
+
+// The s_* Class simply covers one instantiation of the object
+class s_TSIGKey : public PyObject {
+public:
+ s_TSIGKey();
+ TSIGKey* cppobj;
+};
+
+class s_TSIGKeyRing : public PyObject {
+public:
+ s_TSIGKeyRing();
+ TSIGKeyRing* cppobj;
+};
+
+extern PyTypeObject tsigkey_type;
+extern PyTypeObject tsigkeyring_type;
+
+bool initModulePart_TSIGKey(PyObject* mod);
+bool initModulePart_TSIGKeyRing(PyObject* mod);
+
+} // namespace python
+} // namespace dns
+} // namespace isc
+#endif // __PYTHON_TSIGKEY_H
+
+// Local Variables:
+// mode: c++
+// End:
diff --git a/src/lib/dns/python/tsigrecord_python.cc b/src/lib/dns/python/tsigrecord_python.cc
new file mode 100644
index 0000000..8a78b5e
--- /dev/null
+++ b/src/lib/dns/python/tsigrecord_python.cc
@@ -0,0 +1,311 @@
+// 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 <Python.h>
+
+#include <string>
+#include <stdexcept>
+
+#include <util/python/pycppwrapper_util.h>
+
+#include <dns/tsigrecord.h>
+
+#include "pydnspp_common.h"
+#include "pydnspp_towire.h"
+#include "name_python.h"
+#include "tsig_rdata_python.h"
+#include "tsigrecord_python.h"
+
+using namespace std;
+using namespace isc::util::python;
+using namespace isc::dns;
+using namespace isc::dns::python;
+
+//
+// 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
+
+//
+// TSIGRecord
+//
+
+// Trivial constructor.
+s_TSIGRecord::s_TSIGRecord() : cppobj(NULL) {
+}
+
+namespace {
+// Shortcut type which would be convenient for adding class variables safely.
+typedef CPPPyObjectContainer<s_TSIGRecord, TSIGRecord> TSIGRecordContainer;
+
+//
+// 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 TSIGRecord_init(s_TSIGRecord* self, PyObject* args);
+void TSIGRecord_destroy(s_TSIGRecord* self);
+PyObject* TSIGRecord_toText(const s_TSIGRecord* const self);
+PyObject* TSIGRecord_str(PyObject* self);
+PyObject* TSIGRecord_toWire(const s_TSIGRecord* self, PyObject* args);
+PyObject* TSIGRecord_getName(const s_TSIGRecord* self);
+PyObject* TSIGRecord_getLength(const s_TSIGRecord* self);
+PyObject* TSIGRecord_getRdata(const s_TSIGRecord* 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 TSIGRecord_methods[] = {
+ { "get_name", reinterpret_cast<PyCFunction>(TSIGRecord_getName),
+ METH_NOARGS,
+ "Return the owner name of the TSIG RR, which is the TSIG key name" },
+ { "get_length", reinterpret_cast<PyCFunction>(TSIGRecord_getLength),
+ METH_NOARGS,
+ "Return the length of the TSIG record" },
+ { "get_rdata", reinterpret_cast<PyCFunction>(TSIGRecord_getRdata),
+ METH_NOARGS,
+ "Return the RDATA of the TSIG RR" },
+ { "to_text", reinterpret_cast<PyCFunction>(TSIGRecord_toText), METH_NOARGS,
+ "Returns the text representation" },
+ { "to_wire", reinterpret_cast<PyCFunction>(TSIGRecord_toWire),
+ METH_VARARGS,
+ "Converts the TSIGRecord object to wire format.\n"
+ "The argument can be either a MessageRenderer or an object that "
+ "implements the sequence interface. If the object is mutable "
+ "(for instance a bytearray()), the wire data is added in-place.\n"
+ "If it is not (for instance a bytes() object), a new object is "
+ "returned" },
+ { NULL, NULL, 0, NULL }
+};
+
+int
+TSIGRecord_init(s_TSIGRecord* self, PyObject* args) {
+ try {
+ const s_Name* py_name;
+ const s_TSIG* py_tsig;
+ if (PyArg_ParseTuple(args, "O!O!", &name_type, &py_name,
+ &tsig_type, &py_tsig)) {
+ self->cppobj = new TSIGRecord(*py_name->cppobj, *py_tsig->cppobj);
+ return (0);
+ }
+ } catch (const exception& ex) {
+ const string ex_what = "Failed to construct TSIGRecord object: " +
+ string(ex.what());
+ PyErr_SetString(po_IscException, ex_what.c_str());
+ return (-1);
+ } catch (...) {
+ PyErr_SetString(po_IscException,
+ "Unexpected exception in constructing TSIGRecord");
+ return (-1);
+ }
+
+ PyErr_SetString(PyExc_TypeError,
+ "Invalid arguments to TSIGRecord constructor");
+
+ return (-1);
+}
+
+// This is a template of typical code logic of python object destructor.
+// In many cases you can use it without modification, but check that carefully.
+void
+TSIGRecord_destroy(s_TSIGRecord* const self) {
+ delete self->cppobj;
+ self->cppobj = NULL;
+ Py_TYPE(self)->tp_free(self);
+}
+
+// This should be able to be used without modification as long as the
+// underlying C++ class has toText().
+PyObject*
+TSIGRecord_toText(const s_TSIGRecord* const self) {
+ try {
+ // toText() could throw, so we need to catch any exceptions below.
+ return (Py_BuildValue("s", self->cppobj->toText().c_str()));
+ } catch (const exception& ex) {
+ const string ex_what =
+ "Failed to convert TSIGRecord object to text: " +
+ string(ex.what());
+ PyErr_SetString(po_IscException, ex_what.c_str());
+ } catch (...) {
+ PyErr_SetString(PyExc_SystemError, "Unexpected failure in "
+ "converting TSIGRecord object to text");
+ }
+ return (NULL);
+}
+
+PyObject*
+TSIGRecord_str(PyObject* self) {
+ // Simply call the to_text method we already defined
+ return (PyObject_CallMethod(self, const_cast<char*>("to_text"),
+ const_cast<char*>("")));
+}
+
+PyObject*
+TSIGRecord_toWire(const s_TSIGRecord* const self, PyObject* args) {
+ typedef ToWireCallInt<const TSIGRecord> ToWireCall;
+ PyObject* (*towire_fn)(const s_TSIGRecord* const, PyObject*) =
+ toWireWrapper<s_TSIGRecord, TSIGRecord, ToWireCall>;
+ return (towire_fn(self, args));
+}
+
+PyObject*
+TSIGRecord_getName(const s_TSIGRecord* const self) {
+ try {
+ return (createNameObject(self->cppobj->getName()));
+ } catch (const exception& ex) {
+ const string ex_what =
+ "Failed to get TSIGRecord name: " + string(ex.what());
+ PyErr_SetString(po_IscException, ex_what.c_str());
+ } catch (...) {
+ PyErr_SetString(PyExc_SystemError, "Unexpected failure in "
+ "getting TSIGRecord name");
+ }
+ return (NULL);
+}
+
+PyObject*
+TSIGRecord_getLength(const s_TSIGRecord* const self) {
+ return (Py_BuildValue("H", self->cppobj->getLength()));
+}
+
+PyObject*
+TSIGRecord_getRdata(const s_TSIGRecord* const self) {
+ try {
+ return (createTSIGObject(self->cppobj->getRdata()));
+ } catch (const exception& ex) {
+ const string ex_what =
+ "Failed to get TSIGRecord RDATA: " + string(ex.what());
+ PyErr_SetString(po_IscException, ex_what.c_str());
+ } catch (...) {
+ PyErr_SetString(PyExc_SystemError, "Unexpected failure in "
+ "getting TSIGRecord RDATA");
+ }
+ return (NULL);
+}
+
+} // end of unnamed namespace
+
+namespace isc {
+namespace dns {
+namespace python {
+// This defines the complete type for reflection in python and
+// parsing of PyObject* to s_TSIGRecord
+// Most of the functions are not actually implemented and NULL here.
+PyTypeObject tsigrecord_type = {
+ PyVarObject_HEAD_INIT(NULL, 0)
+ "pydnspp.TSIGRecord",
+ sizeof(s_TSIGRecord), // tp_basicsize
+ 0, // tp_itemsize
+ reinterpret_cast<destructor>(TSIGRecord_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
+ TSIGRecord_str, // tp_str
+ NULL, // tp_getattro
+ NULL, // tp_setattro
+ NULL, // tp_as_buffer
+ Py_TPFLAGS_DEFAULT, // tp_flags
+ "The TSIGRecord class objects is...(COMPLETE THIS)",
+ NULL, // tp_traverse
+ NULL, // tp_clear
+ NULL, // tp_richcompare
+ 0, // tp_weaklistoffset
+ NULL, // tp_iter
+ NULL, // tp_iternext
+ TSIGRecord_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
+ reinterpret_cast<initproc>(TSIGRecord_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
+};
+
+// Module Initialization, all statics are initialized here
+bool
+initModulePart_TSIGRecord(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(&tsigrecord_type) < 0) {
+ return (false);
+ }
+ void* p = &tsigrecord_type;
+ if (PyModule_AddObject(mod, "TSIGRecord", static_cast<PyObject*>(p)) < 0) {
+ return (false);
+ }
+ Py_INCREF(&tsigrecord_type);
+
+ // The following template is the typical procedure for installing class
+ // variables. If the class doesn't have a class variable, remove the
+ // entire try-catch clauses.
+ try {
+ // Constant class variables
+ installClassVariable(tsigrecord_type, "TSIG_TTL",
+ Py_BuildValue("I", 0));
+ } catch (const exception& ex) {
+ const string ex_what =
+ "Unexpected failure in TSIGRecord initialization: " +
+ string(ex.what());
+ PyErr_SetString(po_IscException, ex_what.c_str());
+ return (false);
+ } catch (...) {
+ PyErr_SetString(PyExc_SystemError,
+ "Unexpected failure in TSIGRecord initialization");
+ return (false);
+ }
+
+ return (true);
+}
+
+PyObject*
+createTSIGRecordObject(const TSIGRecord& source) {
+ TSIGRecordContainer container = PyObject_New(s_TSIGRecord,
+ &tsigrecord_type);
+ container.set(new TSIGRecord(source));
+ return (container.release());
+}
+} // namespace python
+} // namespace dns
+} // namespace isc
diff --git a/src/lib/dns/python/tsigrecord_python.h b/src/lib/dns/python/tsigrecord_python.h
new file mode 100644
index 0000000..e0a3526
--- /dev/null
+++ b/src/lib/dns/python/tsigrecord_python.h
@@ -0,0 +1,53 @@
+// 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 __PYTHON_TSIGRECORD_H
+#define __PYTHON_TSIGRECORD_H 1
+
+#include <Python.h>
+
+namespace isc {
+namespace dns {
+class TSIGRecord;
+
+namespace python {
+
+// The s_* Class simply covers one instantiation of the object
+class s_TSIGRecord : public PyObject {
+public:
+ s_TSIGRecord();
+ TSIGRecord* cppobj;
+};
+
+extern PyTypeObject tsigrecord_type;
+
+bool initModulePart_TSIGRecord(PyObject* mod);
+
+/// This is A simple shortcut to create a python TSIGRecord object (in the
+/// form of a pointer to PyObject) with minimal exception safety.
+/// On success, it returns a valid pointer to PyObject with a reference
+/// counter of 1; if something goes wrong it throws an exception (it never
+/// returns a NULL pointer).
+/// This function is expected to be called with in a try block
+/// followed by necessary setup for python exception.
+PyObject* createTSIGRecordObject(const TSIGRecord& source);
+
+} // namespace python
+} // namespace dns
+} // namespace isc
+#endif // __PYTHON_TSIGRECORD_H
+
+// Local Variables:
+// mode: c++
+// End:
diff --git a/src/lib/dns/rdata/any_255/tsig_250.cc b/src/lib/dns/rdata/any_255/tsig_250.cc
index 8211e7f..2557965 100644
--- a/src/lib/dns/rdata/any_255/tsig_250.cc
+++ b/src/lib/dns/rdata/any_255/tsig_250.cc
@@ -71,7 +71,7 @@ getToken(istringstream& iss, const string& full_input) {
string token;
iss >> token;
if (iss.bad() || iss.fail()) {
- isc_throw(InvalidRdataText, "Invalid TSIG text: parse error" <<
+ isc_throw(InvalidRdataText, "Invalid TSIG text: parse error " <<
full_input);
}
return (token);
diff --git a/src/lib/dns/tests/Makefile.am b/src/lib/dns/tests/Makefile.am
index 9783beb..b0a9672 100644
--- a/src/lib/dns/tests/Makefile.am
+++ b/src/lib/dns/tests/Makefile.am
@@ -58,7 +58,6 @@ run_unittests_LDADD = $(GTEST_LDADD)
run_unittests_LDADD += $(top_builddir)/src/lib/dns/libdns++.la
run_unittests_LDADD += $(top_builddir)/src/lib/util/libutil.la
run_unittests_LDADD += $(top_builddir)/src/lib/util/unittests/libutil_unittests.la
-run_unittests_LDADD += $(top_builddir)/src/lib/util/io/libutil_io.la
run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
endif
diff --git a/src/lib/dns/tests/run_unittests.cc b/src/lib/dns/tests/run_unittests.cc
index 18eb0a5..7616202 100644
--- a/src/lib/dns/tests/run_unittests.cc
+++ b/src/lib/dns/tests/run_unittests.cc
@@ -13,6 +13,7 @@
// PERFORMANCE OF THIS SOFTWARE.
#include <gtest/gtest.h>
+#include <util/unittests/run_all.h>
#include <util/unittests/testdata.h>
#include <dns/tests/unittest_util.h>
@@ -25,5 +26,5 @@ main(int argc, char* argv[]) {
isc::UnitTestUtil::addDataPath(TEST_DATA_BUILDDIR);
isc::util::unittests::addTestDataPath(TEST_DATA_BUILDDIR);
- return (RUN_ALL_TESTS());
+ return (isc::util::unittests::run_all());
}
diff --git a/src/lib/dns/tests/tsigkey_unittest.cc b/src/lib/dns/tests/tsigkey_unittest.cc
index dac3c49..20ee802 100644
--- a/src/lib/dns/tests/tsigkey_unittest.cc
+++ b/src/lib/dns/tests/tsigkey_unittest.cc
@@ -40,6 +40,9 @@ TEST_F(TSIGKeyTest, algorithmNames) {
EXPECT_EQ(Name("hmac-md5.sig-alg.reg.int"), TSIGKey::HMACMD5_NAME());
EXPECT_EQ(Name("hmac-sha1"), TSIGKey::HMACSHA1_NAME());
EXPECT_EQ(Name("hmac-sha256"), TSIGKey::HMACSHA256_NAME());
+ EXPECT_EQ(Name("hmac-sha224"), TSIGKey::HMACSHA224_NAME());
+ EXPECT_EQ(Name("hmac-sha384"), TSIGKey::HMACSHA384_NAME());
+ EXPECT_EQ(Name("hmac-sha512"), TSIGKey::HMACSHA512_NAME());
// Also check conversion to cryptolink definitions
EXPECT_EQ(isc::cryptolink::MD5, TSIGKey(key_name, TSIGKey::HMACMD5_NAME(),
@@ -49,6 +52,15 @@ TEST_F(TSIGKeyTest, algorithmNames) {
EXPECT_EQ(isc::cryptolink::SHA256, TSIGKey(key_name,
TSIGKey::HMACSHA256_NAME(),
NULL, 0).getAlgorithm());
+ EXPECT_EQ(isc::cryptolink::SHA224, TSIGKey(key_name,
+ TSIGKey::HMACSHA224_NAME(),
+ NULL, 0).getAlgorithm());
+ EXPECT_EQ(isc::cryptolink::SHA384, TSIGKey(key_name,
+ TSIGKey::HMACSHA384_NAME(),
+ NULL, 0).getAlgorithm());
+ EXPECT_EQ(isc::cryptolink::SHA512, TSIGKey(key_name,
+ TSIGKey::HMACSHA512_NAME(),
+ NULL, 0).getAlgorithm());
}
TEST_F(TSIGKeyTest, construct) {
diff --git a/src/lib/dns/tsigkey.cc b/src/lib/dns/tsigkey.cc
index 4082fbe..d7d60eb 100644
--- a/src/lib/dns/tsigkey.cc
+++ b/src/lib/dns/tsigkey.cc
@@ -42,6 +42,16 @@ namespace {
if (name == TSIGKey::HMACSHA256_NAME()) {
return (isc::cryptolink::SHA256);
}
+ if (name == TSIGKey::HMACSHA224_NAME()) {
+ return (isc::cryptolink::SHA224);
+ }
+ if (name == TSIGKey::HMACSHA384_NAME()) {
+ return (isc::cryptolink::SHA384);
+ }
+ if (name == TSIGKey::HMACSHA512_NAME()) {
+ return (isc::cryptolink::SHA512);
+ }
+
return (isc::cryptolink::UNKNOWN_HASH);
}
}
@@ -207,6 +217,24 @@ Name& TSIGKey::HMACSHA256_NAME() {
return (alg_name);
}
+const
+Name& TSIGKey::HMACSHA224_NAME() {
+ static Name alg_name("hmac-sha224");
+ return (alg_name);
+}
+
+const
+Name& TSIGKey::HMACSHA384_NAME() {
+ static Name alg_name("hmac-sha384");
+ return (alg_name);
+}
+
+const
+Name& TSIGKey::HMACSHA512_NAME() {
+ static Name alg_name("hmac-sha512");
+ return (alg_name);
+}
+
struct TSIGKeyRing::TSIGKeyRingImpl {
typedef map<Name, TSIGKey> TSIGKeyMap;
typedef pair<Name, TSIGKey> NameAndKey;
diff --git a/src/lib/dns/tsigkey.h b/src/lib/dns/tsigkey.h
index f0df709..31211d1 100644
--- a/src/lib/dns/tsigkey.h
+++ b/src/lib/dns/tsigkey.h
@@ -206,6 +206,9 @@ public:
static const Name& HMACMD5_NAME(); ///< HMAC-MD5 (RFC2845)
static const Name& HMACSHA1_NAME(); ///< HMAC-SHA1 (RFC4635)
static const Name& HMACSHA256_NAME(); ///< HMAC-SHA256 (RFC4635)
+ static const Name& HMACSHA224_NAME(); ///< HMAC-SHA256 (RFC4635)
+ static const Name& HMACSHA384_NAME(); ///< HMAC-SHA256 (RFC4635)
+ static const Name& HMACSHA512_NAME(); ///< HMAC-SHA256 (RFC4635)
//@}
private:
diff --git a/src/lib/exceptions/tests/run_unittests.cc b/src/lib/exceptions/tests/run_unittests.cc
index 0908071..6a0de4f 100644
--- a/src/lib/exceptions/tests/run_unittests.cc
+++ b/src/lib/exceptions/tests/run_unittests.cc
@@ -17,5 +17,8 @@
int
main(int argc, char* argv[]) {
::testing::InitGoogleTest(&argc, argv);
+
+ // Unlike other tests we cannot use our wrapper for RUN_ALL_TESTS()
+ // due to dependency.
return (RUN_ALL_TESTS());
}
diff --git a/src/lib/log/tests/Makefile.am b/src/lib/log/tests/Makefile.am
index 46065e8..ac4b82a 100644
--- a/src/lib/log/tests/Makefile.am
+++ b/src/lib/log/tests/Makefile.am
@@ -29,7 +29,7 @@ run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
run_unittests_LDADD = $(GTEST_LDADD)
run_unittests_LDADD += $(top_builddir)/src/lib/log/liblog.la
-run_unittests_LDADD += $(top_builddir)/src/lib/util/libutil.la
+run_unittests_LDADD += $(top_builddir)/src/lib/util/unittests/libutil_unittests.la
endif
TESTS += logger_support_test
diff --git a/src/lib/log/tests/run_unittests.cc b/src/lib/log/tests/run_unittests.cc
index bd3c4c9..e84ae5f 100644
--- a/src/lib/log/tests/run_unittests.cc
+++ b/src/lib/log/tests/run_unittests.cc
@@ -13,9 +13,10 @@
// PERFORMANCE OF THIS SOFTWARE.
#include <gtest/gtest.h>
+#include <util/unittests/run_all.h>
int
main(int argc, char* argv[]) {
::testing::InitGoogleTest(&argc, argv);
- return (RUN_ALL_TESTS());
+ return (isc::util::unittests::run_all());
}
diff --git a/src/lib/nsas/tests/Makefile.am b/src/lib/nsas/tests/Makefile.am
index e9235ba..38c6d93 100644
--- a/src/lib/nsas/tests/Makefile.am
+++ b/src/lib/nsas/tests/Makefile.am
@@ -56,6 +56,7 @@ run_unittests_LDADD += $(top_builddir)/src/lib/util/libutil.la
run_unittests_LDADD += $(top_builddir)/src/lib/log/liblog.la
run_unittests_LDADD += $(top_builddir)/src/lib/dns/libdns++.la
run_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libasiolink.la
+run_unittests_LDADD += $(top_builddir)/src/lib/util/unittests/libutil_unittests.la
run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
endif
diff --git a/src/lib/nsas/tests/run_unittests.cc b/src/lib/nsas/tests/run_unittests.cc
index bc672d0..e469e03 100644
--- a/src/lib/nsas/tests/run_unittests.cc
+++ b/src/lib/nsas/tests/run_unittests.cc
@@ -12,24 +12,13 @@
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
-#include <config.h>
-#include <stdlib.h>
-
-#include <string>
-#include <boost/lexical_cast.hpp>
-
#include <gtest/gtest.h>
-
-#include <dns/tests/unittest_util.h>
#include <log/logger_support.h>
-
-using namespace std;
+#include <util/unittests/run_all.h>
int
main(int argc, char* argv[]) {
::testing::InitGoogleTest(&argc, argv);
-
isc::log::initLogger();
-
- return (RUN_ALL_TESTS());
+ return (isc::util::unittests::run_all());
}
diff --git a/src/lib/python/Makefile.am b/src/lib/python/Makefile.am
index f7eb333..75e5afb 100644
--- a/src/lib/python/Makefile.am
+++ b/src/lib/python/Makefile.am
@@ -1,6 +1,7 @@
SUBDIRS = isc
python_PYTHON = bind10_config.py
+pythondir = $(pyexecdir)
# Explicitly define DIST_COMMON so ${python_PYTHON} is not included
# as we don't want the generated file included in distributed tarfile.
diff --git a/src/lib/python/isc/config/config_data.py b/src/lib/python/isc/config/config_data.py
index cee1d34..cc3f484 100644
--- a/src/lib/python/isc/config/config_data.py
+++ b/src/lib/python/isc/config/config_data.py
@@ -213,6 +213,15 @@ class ConfigData:
return spec['item_default'], True
return None, False
+ def get_default_value(self, identifier):
+ """Returns the default from the specification, or None if there
+ is no default"""
+ spec = find_spec_part(self.specification.get_config_spec(), identifier)
+ if spec and 'item_default' in spec:
+ return spec['item_default']
+ else:
+ return None
+
def get_module_spec(self):
"""Returns the ModuleSpec object associated with this ConfigData"""
return self.specification
diff --git a/src/lib/python/isc/config/tests/config_data_test.py b/src/lib/python/isc/config/tests/config_data_test.py
index 923e0b6..48099bb 100644
--- a/src/lib/python/isc/config/tests/config_data_test.py
+++ b/src/lib/python/isc/config/tests/config_data_test.py
@@ -237,6 +237,27 @@ class TestConfigData(unittest.TestCase):
self.assertEqual(None, value)
self.assertEqual(False, default)
+ def test_get_default_value(self):
+ self.assertEqual(1, self.cd.get_default_value("item1"))
+ self.assertEqual('default', self.cd.get_default_value("item6/value1"))
+ self.assertEqual(None, self.cd.get_default_value("item6/value2"))
+
+ # set some local values to something else, and see if we
+ # still get the default
+ self.cd.set_local_config({"item1": 2, "item6": { "value1": "asdf" } })
+
+ self.assertEqual((2, False), self.cd.get_value("item1"))
+ self.assertEqual(1, self.cd.get_default_value("item1"))
+ self.assertEqual(('asdf', False), self.cd.get_value("item6/value1"))
+ self.assertEqual('default', self.cd.get_default_value("item6/value1"))
+
+ self.assertRaises(isc.cc.data.DataNotFoundError,
+ self.cd.get_default_value,
+ "does_not_exist/value1")
+ self.assertRaises(isc.cc.data.DataNotFoundError,
+ self.cd.get_default_value,
+ "item6/doesnotexist")
+
def test_set_local_config(self):
self.cd.set_local_config({"item1": 2})
value, default = self.cd.get_value("item1")
diff --git a/src/lib/python/isc/notify/notify_out.py b/src/lib/python/isc/notify/notify_out.py
index 43dc7af..178a983 100644
--- a/src/lib/python/isc/notify/notify_out.py
+++ b/src/lib/python/isc/notify/notify_out.py
@@ -142,13 +142,17 @@ class NotifyOut:
if zone_id not in self._notify_infos:
return
+ # Has no slave servers, skip it.
+ if (len(self._notify_infos[zone_id].notify_slaves) <= 0):
+ return
+
with self._lock:
if (self.notify_num >= _MAX_NOTIFY_NUM) or (zone_id in self._notifying_zones):
if zone_id not in self._waiting_zones:
self._waiting_zones.append(zone_id)
else:
self._notify_infos[zone_id].prepare_notify_out()
- self.notify_num += 1
+ self.notify_num += 1
self._notifying_zones.append(zone_id)
def _dispatcher(self, started_event):
@@ -300,7 +304,7 @@ class NotifyOut:
try:
r_fds, w, e = select.select(valid_socks, [], [], block_timeout)
except select.error as err:
- if err.args[0] != EINTR:
+ if err.args[0] != errno.EINTR:
return {}, {}
if self._read_sock in r_fds: # user has called shutdown()
diff --git a/src/lib/python/isc/notify/tests/notify_out_test.py b/src/lib/python/isc/notify/tests/notify_out_test.py
index c4c149c..44725d0 100644
--- a/src/lib/python/isc/notify/tests/notify_out_test.py
+++ b/src/lib/python/isc/notify/tests/notify_out_test.py
@@ -99,36 +99,51 @@ class TestNotifyOut(unittest.TestCase):
self._notify._notify_infos[('example.org.', 'IN')] = MockZoneNotifyInfo('example.org.', 'IN')
self._notify._notify_infos[('example.org.', 'CH')] = MockZoneNotifyInfo('example.org.', 'CH')
- info = self._notify._notify_infos[('example.net.', 'IN')]
- info.notify_slaves.append(('127.0.0.1', 53))
- info.notify_slaves.append(('1.1.1.1', 5353))
+ net_info = self._notify._notify_infos[('example.net.', 'IN')]
+ net_info.notify_slaves.append(('127.0.0.1', 53))
+ net_info.notify_slaves.append(('1.1.1.1', 5353))
+ com_info = self._notify._notify_infos[('example.com.', 'IN')]
+ com_info.notify_slaves.append(('1.1.1.1', 5353))
+ com_ch_info = self._notify._notify_infos[('example.com.', 'CH')]
+ com_ch_info.notify_slaves.append(('1.1.1.1', 5353))
def tearDown(self):
self._db_file.close()
os.unlink(self._db_file.name)
def test_send_notify(self):
+ notify_out._MAX_NOTIFY_NUM = 2
+
self._notify.send_notify('example.net')
self.assertEqual(self._notify.notify_num, 1)
- self.assertEqual(self._notify._notifying_zones[0], ('example.net.','IN'))
+ self.assertEqual(self._notify._notifying_zones[0], ('example.net.', 'IN'))
self._notify.send_notify('example.com')
self.assertEqual(self._notify.notify_num, 2)
- self.assertEqual(self._notify._notifying_zones[1], ('example.com.','IN'))
+ self.assertEqual(self._notify._notifying_zones[1], ('example.com.', 'IN'))
- notify_out._MAX_NOTIFY_NUM = 3
+ # notify_num is equal to MAX_NOTIFY_NUM, append it to waiting_zones list.
self._notify.send_notify('example.com', 'CH')
- self.assertEqual(self._notify.notify_num, 3)
- self.assertEqual(self._notify._notifying_zones[2], ('example.com.','CH'))
-
- self._notify.send_notify('example.org.')
- self.assertEqual(self._notify._waiting_zones[0], ('example.org.', 'IN'))
- self._notify.send_notify('example.org.')
+ self.assertEqual(self._notify.notify_num, 2)
self.assertEqual(1, len(self._notify._waiting_zones))
+ # zone_id is already in notifying_zones list, append it to waiting_zones list.
+ self._notify.send_notify('example.net')
+ self.assertEqual(2, len(self._notify._waiting_zones))
+ self.assertEqual(self._notify._waiting_zones[1], ('example.net.', 'IN'))
+
+ # zone_id is already in waiting_zones list, skip it.
+ self._notify.send_notify('example.net')
+ self.assertEqual(2, len(self._notify._waiting_zones))
+
+ # has no slave masters, skip it.
self._notify.send_notify('example.org.', 'CH')
+ self.assertEqual(self._notify.notify_num, 2)
+ self.assertEqual(2, len(self._notify._waiting_zones))
+
+ self._notify.send_notify('example.org.')
+ self.assertEqual(self._notify.notify_num, 2)
self.assertEqual(2, len(self._notify._waiting_zones))
- self.assertEqual(self._notify._waiting_zones[1], ('example.org.', 'CH'))
def test_wait_for_notify_reply(self):
self._notify.send_notify('example.net.')
@@ -171,6 +186,7 @@ class TestNotifyOut(unittest.TestCase):
self._notify.send_notify('example.net.')
self._notify.send_notify('example.com.')
notify_out._MAX_NOTIFY_NUM = 2
+ # zone example.org. has no slave servers.
self._notify.send_notify('example.org.')
self._notify.send_notify('example.com.', 'CH')
@@ -179,17 +195,19 @@ class TestNotifyOut(unittest.TestCase):
self.assertEqual(0, info.notify_try_num)
self.assertEqual(info.get_current_notify_target(), ('1.1.1.1', 5353))
self.assertEqual(2, self._notify.notify_num)
+ self.assertEqual(1, len(self._notify._waiting_zones))
self._notify._notify_next_target(info)
self.assertEqual(0, info.notify_try_num)
self.assertIsNone(info.get_current_notify_target())
self.assertEqual(2, self._notify.notify_num)
- self.assertEqual(1, len(self._notify._waiting_zones))
+ self.assertEqual(0, len(self._notify._waiting_zones))
example_com_info = self._notify._notify_infos[('example.com.', 'IN')]
self._notify._notify_next_target(example_com_info)
- self.assertEqual(2, self._notify.notify_num)
- self.assertEqual(2, len(self._notify._notifying_zones))
+ self.assertEqual(1, self._notify.notify_num)
+ self.assertEqual(1, len(self._notify._notifying_zones))
+ self.assertEqual(0, len(self._notify._waiting_zones))
def test_handle_notify_reply(self):
self.assertEqual(notify_out._BAD_REPLY_PACKET, self._notify._handle_notify_reply(None, b'badmsg'))
diff --git a/src/lib/python/isc/testutils/Makefile.am b/src/lib/python/isc/testutils/Makefile.am
index 8f00a9b..dd032fb 100644
--- a/src/lib/python/isc/testutils/Makefile.am
+++ b/src/lib/python/isc/testutils/Makefile.am
@@ -1 +1 @@
-EXTRA_DIST = __init__.py parse_args.py
+EXTRA_DIST = __init__.py parse_args.py tsigctx_mock.py
diff --git a/src/lib/python/isc/testutils/tsigctx_mock.py b/src/lib/python/isc/testutils/tsigctx_mock.py
new file mode 100644
index 0000000..a9af9b9
--- /dev/null
+++ b/src/lib/python/isc/testutils/tsigctx_mock.py
@@ -0,0 +1,53 @@
+# 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.
+
+from pydnspp import *
+
+class MockTSIGContext(TSIGContext):
+ """Tthis is a mock of TSIGContext class for testing.
+ Via its "error" attribute, you can fake the result of verify(), thereby
+ you can test many of TSIG related tests without requiring actual crypto
+ setups. "error" should be either a TSIGError type value or a callable
+ object (typically a function). In the latter case, the callable object
+ will take the context as a parameter, and is expected to return a
+ TSIGError object.
+ """
+
+ def __init__(self, tsig_key):
+ super().__init__(tsig_key)
+ self.error = None
+ self.verify_called = 0 # number of verify() called
+
+ def sign(self, qid, data):
+ """Transparently delegate the processing to the super class.
+ It doesn't matter much anyway because normal applications that would
+ be implemented in Python normally won't call TSIGContext.sign()
+ directly.
+ """
+ return super().sign(qid, data)
+
+ def verify(self, tsig_record, data):
+ self.verify_called += 1
+ # call real "verify" so that we can notice any misue (which would
+ # result in exception.
+ super().verify(tsig_record, data)
+ return self.get_error()
+
+ def get_error(self):
+ if self.error is None:
+ return super().get_error()
+ if hasattr(self.error, '__call__'):
+ return self.error(self)
+ return self.error
diff --git a/src/lib/resolve/Makefile.am b/src/lib/resolve/Makefile.am
index 0b29da4..94ad371 100644
--- a/src/lib/resolve/Makefile.am
+++ b/src/lib/resolve/Makefile.am
@@ -7,16 +7,36 @@ AM_CPPFLAGS += $(SQLITE_CFLAGS)
AM_CXXFLAGS = $(B10_CXXFLAGS)
-CLEANFILES = *.gcno *.gcda
+# Define rule to build logging source files from message file
+resolvedef.h resolvedef.cc: resolvedef.mes
+ $(top_builddir)/src/lib/log/compiler/message $(top_srcdir)/src/lib/resolve/resolvedef.mes
+
+# Tell Automake that the nsasdef.{cc,h} source files are created in the build
+# process, so it must create these before doing anything else. Although they
+# are a dependency of the library (so will be created from the message file
+# anyway), there is no guarantee as to exactly _when_ in the build they will be
+# created. As the .h file is included in other sources file (so must be
+# present when they are compiled), the safest option is to create it first.
+BUILT_SOURCES = resolvedef.h resolvedef.cc
+
+CLEANFILES = *.gcno *.gcda resolvedef.cc resolvedef.h
lib_LTLIBRARIES = libresolve.la
libresolve_la_SOURCES = resolve.h resolve.cc
+libresolve_la_SOURCES += resolve_log.h resolve_log.cc
libresolve_la_SOURCES += resolver_interface.h
libresolve_la_SOURCES += resolver_callback.h resolver_callback.cc
libresolve_la_SOURCES += response_classifier.cc response_classifier.h
libresolve_la_SOURCES += recursive_query.cc recursive_query.h
+
+nodist_libresolve_la_SOURCES = resolvedef.h resolvedef.cc
+
libresolve_la_LIBADD = $(top_builddir)/src/lib/dns/libdns++.la
libresolve_la_LIBADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
+libresolve_la_LIBADD += $(top_builddir)/src/lib/log/liblog.la
+
+# The message file should be in the distribution.
+EXTRA_DIST = resolvedef.mes
# Note: the ordering matters: -Wno-... must follow -Wextra (defined in
# B10_CXXFLAGS)
diff --git a/src/lib/resolve/recursive_query.cc b/src/lib/resolve/recursive_query.cc
index b753cc9..ad073ae 100644
--- a/src/lib/resolve/recursive_query.cc
+++ b/src/lib/resolve/recursive_query.cc
@@ -16,14 +16,13 @@
#include <stdlib.h>
#include <sys/socket.h>
#include <unistd.h> // for some IPC/network system calls
+#include <string>
#include <boost/lexical_cast.hpp>
#include <boost/bind.hpp>
#include <config.h>
-#include <log/dummylog.h>
-
#include <dns/question.h>
#include <dns/message.h>
#include <dns/opcode.h>
@@ -31,6 +30,7 @@
#include <dns/rdataclass.h>
#include <resolve/resolve.h>
+#include <resolve/resolve_log.h>
#include <cache/resolver_cache.h>
#include <nsas/address_request_callback.h>
#include <nsas/nameserver_address.h>
@@ -41,10 +41,10 @@
#include <asiolink/io_service.h>
#include <resolve/recursive_query.h>
-using isc::log::dlog;
using namespace isc::dns;
using namespace isc::util;
using namespace isc::asiolink;
+using namespace isc::resolve;
namespace isc {
namespace asiodns {
@@ -64,8 +64,20 @@ hasAddress(const Name& name, const RRClass& rrClass,
cache.lookup(name, RRType::AAAA(), rrClass) != RRsetPtr());
}
+// Convenience function for debug messages. Question::toText() includes
+// a trailing newline in its output, which makes it awkward to embed in a
+// message. This just strips that newline from it.
+std::string
+questionText(const isc::dns::Question& question) {
+ std::string text = question.toText();
+ if (!text.empty()) {
+ text.erase(text.size() - 1);
+ }
+ return (text);
}
+} // anonymous namespace
+
/// \brief Find deepest usable delegation in the cache
///
/// This finds the deepest delegation we have in cache and is safe to use.
@@ -135,8 +147,7 @@ RecursiveQuery::RecursiveQuery(DNSService& dns_service,
// Set the test server - only used for unit testing.
void
RecursiveQuery::setTestServer(const std::string& address, uint16_t port) {
- dlog("Setting test server to " + address + "(" +
- boost::lexical_cast<std::string>(port) + ")");
+ LOG_WARN(isc::resolve::logger, RESLIB_TESTSERV).arg(address).arg(port);
test_server_.first = address;
test_server_.second = port;
}
@@ -165,14 +176,16 @@ public:
ResolverNSASCallback(RunningQuery* rq) : rq_(rq) {}
void success(const isc::nsas::NameserverAddress& address) {
- dlog("Found a nameserver, sending query to " + address.getAddress().toText());
+ // Success callback, send query to found namesever
+ LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_CB, RESLIB_RUNQUSUCC)
+ .arg(address.getAddress().toText());
rq_->nsasCallbackCalled();
rq_->sendTo(address);
}
void unreachable() {
- dlog("Nameservers unreachable");
- // Drop query or send servfail?
+ // Nameservers unreachable: drop query or send servfail?
+ LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_CB, RESLIB_RUNQUFAIL);
rq_->nsasCallbackCalled();
rq_->makeSERVFAIL();
rq_->callCallback(true);
@@ -298,12 +311,16 @@ private:
// if we have a response for our query stored already. if
// so, call handlerecursiveresponse(), if not, we call send()
void doLookup() {
- dlog("doLookup: try cache");
+ LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_CACHE, RESLIB_RUNCALOOK)
+ .arg(questionText(question_));
+
Message cached_message(Message::RENDER);
isc::resolve::initResponseMessage(question_, cached_message);
if (cache_.lookup(question_.getName(), question_.getType(),
question_.getClass(), cached_message)) {
- dlog("Message found in cache, continuing with that");
+
+ LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_CACHE, RESLIB_RUNCAFND)
+ .arg(questionText(question_));
// Should these be set by the cache too?
cached_message.setOpcode(Opcode::QUERY());
cached_message.setRcode(Rcode::NOERROR());
@@ -313,9 +330,10 @@ private:
stop();
}
} else {
- dlog("doLookup: get lowest usable delegation from cache");
cur_zone_ = deepestDelegation(question_.getName(),
question_.getClass(), cache_);
+ LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_CACHE, RESLIB_DEEPEST)
+ .arg(questionText(question_)).arg(cur_zone_);
send();
}
@@ -347,8 +365,9 @@ private:
void send(IOFetch::Protocol protocol = IOFetch::UDP) {
protocol_ = protocol; // Store protocol being used for this
if (test_server_.second != 0) {
- dlog("Sending upstream query (" + question_.toText() +
- ") to test server at " + test_server_.first);
+ // Send query to test server
+ LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_TRACE, RESLIB_TESTUPSTR)
+ .arg(questionText(question_)).arg(test_server_.first);
gettimeofday(¤t_ns_qsent_time, NULL);
++outstanding_events_;
IOFetch query(protocol, io_, question_,
@@ -356,10 +375,13 @@ private:
test_server_.second, buffer_, this,
query_timeout_);
io_.get_io_service().post(query);
+
} else {
// Ask the NSAS for an address for the current zone,
// the callback will call the actual sendTo()
- dlog("Look up nameserver for " + cur_zone_ + " in NSAS");
+ LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_TRACE, RESLIB_NSASLOOK)
+ .arg(cur_zone_);
+
// Can we have multiple calls to nsas_out? Let's assume not
// for now
assert(!nsas_callback_out_);
@@ -387,7 +409,7 @@ private:
// error message)
// returns false if we are not done
bool handleRecursiveAnswer(const Message& incoming) {
- dlog("Handle response");
+
// In case we get a CNAME, we store the target
// here (classify() will set it when it walks through
// the cname chain to verify it).
@@ -402,46 +424,60 @@ private:
switch (category) {
case isc::resolve::ResponseClassifier::ANSWER:
case isc::resolve::ResponseClassifier::ANSWERCNAME:
- // Done. copy and return.
- dlog("Response is an answer");
+ // Answer received - copy and return.
+ LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_RESULTS, RESLIB_ANSWER)
+ .arg(questionText(question_));
isc::resolve::copyResponseMessage(incoming, answer_message_);
cache_.update(*answer_message_);
return true;
break;
+
case isc::resolve::ResponseClassifier::CNAME:
- dlog("Response is CNAME!");
+ // CNAME received.
+
// (unfinished) CNAME. We set our question_ to the CNAME
// target, then start over at the beginning (for now, that
// is, we reset our 'current servers' to the root servers).
if (cname_count_ >= RESOLVER_MAX_CNAME_CHAIN) {
- // just give up
- dlog("CNAME chain too long");
+ // CNAME chain too long - just give up
+ LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_RESULTS, RESLIB_LONGCHAIN)
+ .arg(questionText(question_));
makeSERVFAIL();
return true;
}
+ LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_RESULTS, RESLIB_CNAME)
+ .arg(questionText(question_));
+
answer_message_->appendSection(Message::SECTION_ANSWER,
incoming);
question_ = Question(cname_target, question_.getClass(),
question_.getType());
- dlog("Following CNAME chain to " + question_.toText());
+ // Follow CNAME chain.
+ LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_RESULTS, RESLIB_FOLLOWCNAME)
+ .arg(questionText(question_));
doLookup();
return false;
break;
+
case isc::resolve::ResponseClassifier::NXDOMAIN:
case isc::resolve::ResponseClassifier::NXRRSET:
- dlog("Response is NXDOMAIN or NXRRSET");
- // NXDOMAIN, just copy and return.
- dlog(incoming.toText());
+ // Received NXDOMAIN or NXRRSET, just copy and return
+ LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_RESULTS, RESLIB_NXDOMRR)
+ .arg(questionText(question_));
isc::resolve::copyResponseMessage(incoming, answer_message_);
// no negcache yet
//cache_.update(*answer_message_);
return true;
break;
+
case isc::resolve::ResponseClassifier::REFERRAL:
- dlog("Response is referral");
+ // Response is a referral
+ LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_RESULTS, RESLIB_REFERRAL)
+ .arg(questionText(question_));
+
cache_.update(incoming);
// Referral. For now we just take the first glue address
// we find and continue with that
@@ -460,7 +496,8 @@ private:
// (this requires a few API changes in related
// libraries, so as not to need many conversions)
cur_zone_ = rrs->getName().toText();
- dlog("Referred to zone " + cur_zone_);
+ LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_RESULTS, RESLIB_REFERZONE)
+ .arg(cur_zone_);
found_ns = true;
break;
}
@@ -484,7 +521,10 @@ private:
nsas_callback_, ANY_OK, glue_hints);
return false;
} else {
- dlog("No NS RRset in referral?");
+ // Referral was received but did not contain an NS RRset.
+ LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_RESULTS, RESLIB_NONSRRSET)
+ .arg(questionText(question_));
+
// TODO this will result in answering with the delegation. oh well
isc::resolve::copyResponseMessage(incoming, answer_message_);
return true;
@@ -494,7 +534,8 @@ private:
// Truncated packet. If the protocol we used for the last one is
// UDP, re-query using TCP. Otherwise regard it as an error.
if (protocol_ == IOFetch::UDP) {
- dlog("Response truncated, re-querying over TCP");
+ LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_RESULTS, RESLIB_TRUNCATED)
+ .arg(questionText(question_));
send(IOFetch::TCP);
return false;
}
@@ -513,6 +554,8 @@ private:
case isc::resolve::ResponseClassifier::NOTSINGLE:
case isc::resolve::ResponseClassifier::OPCODE:
case isc::resolve::ResponseClassifier::RCODE:
+ LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_RESULTS, RESLIB_RCODERR)
+ .arg(questionText(question_));
// Should we try a different server rather than SERVFAIL?
makeSERVFAIL();
return true;
@@ -677,7 +720,7 @@ public:
rtt = 1000 * (cur_time.tv_sec - current_ns_qsent_time.tv_sec);
rtt += (cur_time.tv_usec - current_ns_qsent_time.tv_usec) / 1000;
}
- dlog("RTT: " + boost::lexical_cast<std::string>(rtt));
+ LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_RESULTS, RESLIB_RTT).arg(rtt);
current_ns_address.updateRTT(rtt);
if (rtt_recorder_) {
rtt_recorder_->addRtt(rtt);
@@ -701,19 +744,22 @@ public:
stop();
}
} catch (const isc::dns::DNSProtocolError& dpe) {
- dlog("DNS Protocol error in answer for " +
- question_.toText() + " " +
- question_.getType().toText() + ": " +
- dpe.what());
// Right now, we treat this similar to timeouts
// (except we don't store RTT)
// We probably want to make this an integral part
// of the fetch data process. (TODO)
if (retries_--) {
- dlog("Retrying");
+ // Retry
+ LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_RESULTS,
+ RESLIB_PROTOCOLRTRY)
+ .arg(questionText(question_)).arg(dpe.what())
+ .arg(retries_);
send();
} else {
- dlog("Giving up");
+ // Give up
+ LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_RESULTS,
+ RESLIB_PROTOCOL)
+ .arg(questionText(question_)).arg(dpe.what());
if (!callback_called_) {
makeSERVFAIL();
callCallback(true);
@@ -723,13 +769,17 @@ public:
}
} else if (!done_ && retries_--) {
// Query timed out, but we have some retries, so send again
- dlog("Timeout for " + question_.toText() + " to " + current_ns_address.getAddress().toText() + ", resending query");
+ LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_RESULTS, RESLIB_TIMEOUTRTRY)
+ .arg(questionText(question_))
+ .arg(current_ns_address.getAddress().toText()).arg(retries_);
current_ns_address.updateRTT(isc::nsas::AddressEntry::UNREACHABLE);
send();
} else {
// We are either already done, or out of retries
if (result == IOFetch::TIME_OUT) {
- dlog("Timeout for " + question_.toText() + " to " + current_ns_address.getAddress().toText() + ", giving up");
+ LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_RESULTS, RESLIB_TIMEOUT)
+ .arg(questionText(question_))
+ .arg(current_ns_address.getAddress().toText());
current_ns_address.updateRTT(isc::nsas::AddressEntry::UNREACHABLE);
}
if (!callback_called_) {
@@ -793,8 +843,10 @@ private:
buffer_->clear();
int serverIndex = rand() % uc;
ConstQuestionPtr question = *(query_message_->beginQuestion());
- dlog("Sending upstream query (" + question->toText() +
- ") to " + upstream_->at(serverIndex).first);
+ LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_TRACE, RESLIB_UPSTREAM)
+ .arg(questionText(*question))
+ .arg(upstream_->at(serverIndex).first);
+
++outstanding_events_;
// Forward the query, create the IOFetch with
// query message, so that query flags can be forwarded
@@ -934,14 +986,16 @@ RecursiveQuery::resolve(const QuestionPtr& question,
OutputBufferPtr buffer(new OutputBuffer(0));
- dlog("Asked to resolve: " + question->toText());
-
- dlog("Try out cache first (direct call to resolve)");
// First try to see if we have something cached in the messagecache
+ LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_TRACE, RESLIB_RESOLVE)
+ .arg(questionText(*question)).arg(1);
if (cache_.lookup(question->getName(), question->getType(),
question->getClass(), *answer_message) &&
answer_message->getRRCount(Message::SECTION_ANSWER) > 0) {
- dlog("Message found in cache, returning that");
+ // Message found, return that
+ LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_CACHE, RESLIB_RESCAFND)
+ .arg(questionText(*question)).arg(1);
+
// TODO: err, should cache set rcode as well?
answer_message->setRcode(Rcode::NOERROR());
callback->success(answer_message);
@@ -952,14 +1006,18 @@ RecursiveQuery::resolve(const QuestionPtr& question,
question->getType(),
question->getClass());
if (cached_rrset) {
- dlog("Found single RRset in cache");
+ // Found single RRset in cache
+ LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_CACHE, RESLIB_RRSETFND)
+ .arg(questionText(*question)).arg(1);
answer_message->addRRset(Message::SECTION_ANSWER,
cached_rrset);
answer_message->setRcode(Rcode::NOERROR());
callback->success(answer_message);
} else {
- dlog("Message not found in cache, starting recursive query");
- // It will delete itself when it is done
+ // Message not found in cache, start recursive query. It will
+ // delete itself when it is done
+ LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_TRACE, RESLIB_RESCANOTFND)
+ .arg(questionText(*question)).arg(1);
new RunningQuery(io, *question, answer_message,
test_server_, buffer, callback,
query_timeout_, client_timeout_,
@@ -988,14 +1046,17 @@ RecursiveQuery::resolve(const Question& question,
answer_message->setOpcode(isc::dns::Opcode::QUERY());
answer_message->addQuestion(question);
- dlog("Asked to resolve: " + question.toText());
-
// First try to see if we have something cached in the messagecache
- dlog("Try out cache first (started by incoming event)");
+ LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_TRACE, RESLIB_RESOLVE)
+ .arg(questionText(question)).arg(2);
+
if (cache_.lookup(question.getName(), question.getType(),
question.getClass(), *answer_message) &&
answer_message->getRRCount(Message::SECTION_ANSWER) > 0) {
- dlog("Message found in cache, returning that");
+
+ // Message found, return that
+ LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_CACHE, RESLIB_RESCAFND)
+ .arg(questionText(question)).arg(2);
// TODO: err, should cache set rcode as well?
answer_message->setRcode(Rcode::NOERROR());
crs->success(answer_message);
@@ -1006,14 +1067,19 @@ RecursiveQuery::resolve(const Question& question,
question.getType(),
question.getClass());
if (cached_rrset) {
- dlog("Found single RRset in cache");
+ // Found single RRset in cache
+ LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_CACHE, RESLIB_RRSETFND)
+ .arg(questionText(question)).arg(2);
answer_message->addRRset(Message::SECTION_ANSWER,
cached_rrset);
answer_message->setRcode(Rcode::NOERROR());
crs->success(answer_message);
+
} else {
- dlog("Message not found in cache, starting recursive query");
- // It will delete itself when it is done
+ // Message not found in cache, start recursive query. It will
+ // delete itself when it is done
+ LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_TRACE, RESLIB_RESCANOTFND)
+ .arg(questionText(question)).arg(2);
new RunningQuery(io, question, answer_message,
test_server_, buffer, crs, query_timeout_,
client_timeout_, lookup_timeout_, retries_,
diff --git a/src/lib/resolve/resolve_log.cc b/src/lib/resolve/resolve_log.cc
new file mode 100644
index 0000000..e41d8d2
--- /dev/null
+++ b/src/lib/resolve/resolve_log.cc
@@ -0,0 +1,26 @@
+// 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.
+
+/// Defines the logger used by the NSAS
+
+#include <resolve/resolve_log.h>
+
+namespace isc {
+namespace resolve {
+
+isc::log::Logger logger("reslib"); // Distinct from "resolver"
+
+} // namespace resolve
+} // namespace isc
+
diff --git a/src/lib/resolve/resolve_log.h b/src/lib/resolve/resolve_log.h
new file mode 100644
index 0000000..89d23c6
--- /dev/null
+++ b/src/lib/resolve/resolve_log.h
@@ -0,0 +1,53 @@
+// 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 __RESOLVE_LOG__H
+#define __RESOLVE_LOG__H
+
+#include <log/macros.h>
+#include "resolvedef.h"
+
+namespace isc {
+namespace resolve {
+
+/// \brief Resolver Library Logging
+///
+/// Defines the levels used to output debug messages in the resolver library.
+/// Note that higher numbers equate to more verbose (and detailed) output.
+
+// The first level traces normal operations
+const int RESLIB_DBG_TRACE = 10;
+
+// The next level extends the normal operations and records the results of the
+// lookups.
+const int RESLIB_DBG_RESULTS = 20;
+
+// Report cache lookups and results
+const int RESLIB_DBG_CACHE = 40;
+
+// Indicate when callbacks are called
+const int RESLIB_DBG_CB = 50;
+
+
+/// \brief Resolver Library Logger
+///
+/// Define the logger used to log messages. We could define it in multiple
+/// modules, but defining in a single module and linking to it saves time and
+/// space.
+extern isc::log::Logger logger;
+
+} // namespace resolve
+} // namespace isc
+
+#endif // __RESOLVE_LOG__H
diff --git a/src/lib/resolve/resolvedef.mes b/src/lib/resolve/resolvedef.mes
new file mode 100644
index 0000000..61870a6
--- /dev/null
+++ b/src/lib/resolve/resolvedef.mes
@@ -0,0 +1,155 @@
+# 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 RESLIB_
+$NAMESPACE isc::resolve
+
+% ANSWER answer received in response to query for <%1>
+A debug message recording that an answer has been received to an upstream
+query for the specified question. Previous debug messages will have indicated
+the server to which the question was sent.
+
+% CNAME CNAME received in response to query for <%1>
+A debug message recording that CNAME response has been received to an upstream
+query for the specified question. Previous debug messages will have indicated
+the server to which the question was sent.
+
+% DEEPEST did not find <%1> in cache, deepest delegation found is %2
+A debug message, a cache lookup did not find the specified <name, class,
+type> tuple in the cache; instead, the deepest delegation found is indicated.
+
+% FOLLOWCNAME following CNAME chain to <%1>
+A debug message, a CNAME response was received and another query is being issued
+for the <name, class, type> tuple.
+
+% LONGCHAIN CNAME received in response to query for <%1>: CNAME chain length exceeded
+A debug message recording that a CNAME response has been received to an upstream
+query for the specified question (Previous debug messages will have indicated
+the server to which the question was sent). However, receipt of this CNAME
+has meant that the resolver has exceeded the CNAME chain limit (a CNAME chain
+is where on CNAME points to another) and so an error is being returned.
+
+% NONSRRSET no NS RRSet in referral response received to query for <%1>
+A debug message, this indicates that a response was received for the specified
+query and was categorised as a referral. However, the received message did
+not contain any NS RRsets. This may indicate a programming error in the
+response classification code.
+
+% NSASLOOK looking up nameserver for zone %1 in the NSAS
+A debug message, the RunningQuery object is querying the NSAS for the
+nameservers for the specified zone.
+
+% NXDOMRR NXDOMAIN/NXRRSET received in response to query for <%1>
+A debug message recording that either a NXDOMAIN or an NXRRSET response has
+been received to an upstream query for the specified question. Previous debug
+messages will have indicated the server to which the question was sent.
+
+% PROTOCOL protocol error in answer for %1: %3
+A debug message indicating that a protocol error was received. As there
+are no retries left, an error will be reported.
+
+% PROTOCOLRTRY protocol error in answer for %1: %2 (retries left: %3)
+A debug message indicating that a protocol error was received and that
+the resolver is repeating the query to the same nameserver. After this
+repeated query, there will be the indicated number of retries left.
+
+% RCODERR RCODE indicates error in response to query for <%1>
+A debug message, the response to the specified query indicated an error
+that is not covered by a specific code path. A SERVFAIL will be returned.
+
+% REFERRAL referral received in response to query for <%1>
+A debug message recording that a referral response has been received to an
+upstream query for the specified question. Previous debug messages will
+have indicated the server to which the question was sent.
+
+% REFERZONE referred to zone %1
+A debug message indicating that the last referral message was to the specified
+zone.
+
+% RESCAFND found <%1> in the cache (resolve() instance %2)
+This is a debug message and indicates that a RecursiveQuery object found the
+the specified <name, class, type> tuple in the cache. The instance number
+at the end of the message indicates which of the two resolve() methods has
+been called.
+
+% RESCANOTFND did not find <%1> in the cache, starting RunningQuery (resolve() instance %2)
+This is a debug message and indicates that the look in the cache made by the
+RecursiveQuery::resolve() method did not find an answer, so a new RunningQuery
+object has been created to resolve the question. The instance number at
+the end of the message indicates which of the two resolve() methods has
+been called.
+
+% RESOLVE asked to resolve <%1> (resolve() instance %2)
+A debug message, the RecursiveQuery::resolve method has been called to resolve
+the specified <name, class, type> tuple. The first action will be to lookup
+the specified tuple in the cache. The instance number at the end of the
+message indicates which of the two resolve() methods has been called.
+
+% RRSETFND found single RRset in the cache when querying for <%1> (resolve() instance %2)
+A debug message, indicating that when RecursiveQuery::resolve queried the
+cache, a single RRset was found which was put in the answer. The instance
+number at the end of the message indicates which of the two resolve()
+methods has been called.
+
+% RTT round-trip time of last query calculated as %1 ms
+A debug message giving the round-trip time of the last query and response.
+
+% RUNCAFND found <%1> in the cache
+This is a debug message and indicates that a RunningQuery object found
+the specified <name, class, type> tuple in the cache.
+
+% RUNCALOOK looking up up <%1> in the cache
+This is a debug message and indicates that a RunningQuery object has made
+a call to its doLookup() method to look up the specified <name, class, type>
+tuple, the first action of which will be to examine the cache.
+
+% RUNQUFAIL failure callback - nameservers are unreachable
+A debug message indicating that a RunningQuery's failure callback has been
+called because all nameservers for the zone in question are unreachable.
+
+% RUNQUSUCC success callback - sending query to %1
+A debug message indicating that a RunningQuery's success callback has been
+called because a nameserver has been found, and that a query is being sent
+to the specified nameserver.
+
+% TESTSERV setting test server to %1(%2)
+This is an internal debugging message and is only generated in unit tests.
+It indicates that all upstream queries from the resolver are being routed to
+the specified server, regardless of the address of the nameserver to which
+the query would normally be routed. As it should never be seen in normal
+operation, it is a warning message instead of a debug message.
+
+% TESTUPSTR sending upstream query for <%1> to test server at %2
+This is a debug message and should only be seen in unit tests. A query for
+the specified <name, class, type> tuple is being sent to a test nameserver
+whose address is given in the message.
+
+% TIMEOUT query <%1> to %2 timed out
+A debug message indicating that the specified query has timed out and as
+there are no retries left, an error will be reported.
+
+% TIMEOUTRTRY query <%1> to %2 timed out, re-trying (retries left: %3)
+A debug message indicating that the specified query has timed out and that
+the resolver is repeating the query to the same nameserver. After this
+repeated query, there will be the indicated number of retries left.
+
+% TRUNCATED response to query for <%1> was truncated, re-querying over TCP
+A debug message, this indicates that the response to the specified query was
+truncated and that the resolver will be re-querying over TCP. There are
+various reasons why responses may be truncated, so this message is normal and
+gives no cause for concern.
+
+% UPSTREAM sending upstream query for <%1> to %2
+A debug message indicating that a query for the specified <name, class, type>
+tuple is being sent to a nameserver whose address is given in the message.
diff --git a/src/lib/resolve/tests/Makefile.am b/src/lib/resolve/tests/Makefile.am
index edea7cd..3cb0e3e 100644
--- a/src/lib/resolve/tests/Makefile.am
+++ b/src/lib/resolve/tests/Makefile.am
@@ -23,7 +23,6 @@ run_unittests_SOURCES += recursive_query_unittest.cc
run_unittests_SOURCES += recursive_query_unittest_2.cc
run_unittests_LDADD = $(GTEST_LDADD)
-run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
run_unittests_LDADD += $(top_builddir)/src/lib/nsas/libnsas.la
run_unittests_LDADD += $(top_builddir)/src/lib/cache/libcache.la
run_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libasiolink.la
@@ -31,6 +30,8 @@ run_unittests_LDADD += $(top_builddir)/src/lib/asiodns/libasiodns.la
run_unittests_LDADD += $(top_builddir)/src/lib/resolve/libresolve.la
run_unittests_LDADD += $(top_builddir)/src/lib/dns/libdns++.la
run_unittests_LDADD += $(top_builddir)/src/lib/log/liblog.la
+run_unittests_LDADD += $(top_builddir)/src/lib/util/unittests/libutil_unittests.la
+run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
endif
diff --git a/src/lib/resolve/tests/run_unittests.cc b/src/lib/resolve/tests/run_unittests.cc
index f80e167..fe8124e 100644
--- a/src/lib/resolve/tests/run_unittests.cc
+++ b/src/lib/resolve/tests/run_unittests.cc
@@ -13,12 +13,15 @@
// PERFORMANCE OF THIS SOFTWARE.
#include <gtest/gtest.h>
+#include <util/unittests/run_all.h>
#include <dns/tests/unittest_util.h>
+#include <log/logger_support.h>
int
main(int argc, char* argv[]) {
::testing::InitGoogleTest(&argc, argv);
+ isc::log::initLogger();
- return (RUN_ALL_TESTS());
+ return (isc::util::unittests::run_all());
}
diff --git a/src/lib/server_common/Makefile.am b/src/lib/server_common/Makefile.am
index dfb3014..a3063ba 100644
--- a/src/lib/server_common/Makefile.am
+++ b/src/lib/server_common/Makefile.am
@@ -18,9 +18,12 @@ endif
lib_LTLIBRARIES = libserver_common.la
libserver_common_la_SOURCES = portconfig.h portconfig.cc
+libserver_common_la_SOURCES += keyring.h keyring.cc
libserver_common_la_LIBADD = $(top_builddir)/src/lib/exceptions/libexceptions.la
libserver_common_la_LIBADD += $(top_builddir)/src/lib/asiolink/libasiolink.la
libserver_common_la_LIBADD += $(top_builddir)/src/lib/cc/libcc.la
+libserver_common_la_LIBADD += $(top_builddir)/src/lib/config/libcfgclient.la
libserver_common_la_LIBADD += $(top_builddir)/src/lib/log/liblog.la
+libserver_common_la_LIBADD += $(top_builddir)/src/lib/dns/libdns++.la
CLEANFILES = *.gcno *.gcda
diff --git a/src/lib/server_common/keyring.cc b/src/lib/server_common/keyring.cc
new file mode 100644
index 0000000..f68db70
--- /dev/null
+++ b/src/lib/server_common/keyring.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 <server_common/keyring.h>
+
+using namespace isc::dns;
+using namespace isc::data;
+
+namespace isc {
+namespace server_common {
+
+typedef boost::shared_ptr<TSIGKeyRing> KeyringPtr;
+
+KeyringPtr keyring;
+
+namespace {
+
+void
+updateKeyring(const std::string&, ConstElementPtr data) {
+ ConstElementPtr list(data->get("keys"));
+ KeyringPtr load(new TSIGKeyRing);
+ for (size_t i(0); i < list->size(); ++ i) {
+ load->add(TSIGKey(list->get(i)->stringValue()));
+ }
+ keyring.swap(load);
+}
+
+}
+
+void
+initKeyring(config::ModuleCCSession& session) {
+ if (keyring) {
+ // We are already initialized
+ return;
+ }
+ session.addRemoteConfig("tsig_keys", updateKeyring, false);
+}
+
+void
+deinitKeyring(config::ModuleCCSession& session) {
+ if (!keyring) {
+ // Not initialized, ignore it
+ return;
+ }
+ keyring.reset();
+ session.removeRemoteConfig("tsig_keys");
+}
+
+}
+}
diff --git a/src/lib/server_common/keyring.h b/src/lib/server_common/keyring.h
new file mode 100644
index 0000000..8832095
--- /dev/null
+++ b/src/lib/server_common/keyring.h
@@ -0,0 +1,96 @@
+// 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 ISC_SERVER_COMMON_KEYRING_H
+#define ISC_SERVER_COMMON_KEYRING_H
+
+#include <boost/shared_ptr.hpp>
+#include <dns/tsigkey.h>
+#include <config/ccsession.h>
+
+/**
+ * \file keyring.h
+ * \brief TSIG keyring loaded from configuration.
+ *
+ * This file contains routines for loading a TSIG key ring from
+ * the tsig_keys configuration section and keeping them up to date
+ * on updates.
+ *
+ * You simply initialize/load the keyring with isc::server_common::initKeyring
+ * and then just use the key ring in in isc::server_common::keyring. It is
+ * automatically reloaded, when the configuration updates, so you no longer
+ * needs to care about it.
+ *
+ * If you want to keep a key (or session) for longer time or your application
+ * is multithreaded, you might want to have a copy of the shared pointer.
+ * Otherwise an update might replace the keyring and delete the keys in the
+ * old one.
+ */
+
+namespace isc {
+
+namespace server_common {
+
+/**
+ * \brief The key ring itself
+ *
+ * This is where the key ring is stored. You can directly use it to your needs,
+ * but you need to call initKeyring first, otherwise you'll find a NULL pointer
+ * here only.
+ */
+extern boost::shared_ptr<dns::TSIGKeyRing> keyring;
+
+/**
+ * \brief Load the key ring for the first time
+ *
+ * This loads the key ring from configuration to keyring. It also registers for
+ * config updates, so from now on, it'll be kept up to date.
+ *
+ * You can unload the key ring with deinitKeyring.
+ *
+ * If it is already loaded, this function does nothing. So, if more than one
+ * part of an application needs to use the key ring, they all can just call
+ * this independently to ensure the keyring is loaded.
+ *
+ * \param session The configuration session used to talk to the config manager.
+ */
+void
+initKeyring(config::ModuleCCSession& session);
+
+/**
+ * \brief Unload the key ring
+ *
+ * This can be used to unload the key ring. It will reset the keyring to NULL
+ * and stop receiving updates of the configuration.
+ *
+ * The need for this function should be quite rare, as it isn't required to be
+ * called before application shutdown. And not calling it has only small
+ * performance penalty -- the keyring will be kept in memory and updated when
+ * the user changes configuration.
+ *
+ * This does nothing if the key ring is not loaded currently.
+ *
+ * \param session The configuration session used to talk to the config manager.
+ *
+ * \todo What do we do when the data that come are invalid? Should we ignore it,
+ * as walidity should have been checked already in the config manager, or
+ * throw? What about when we get an update and it's invalid?
+ */
+void
+deinitKeyring(config::ModuleCCSession& session);
+
+}
+}
+
+#endif
diff --git a/src/lib/server_common/tests/Makefile.am b/src/lib/server_common/tests/Makefile.am
index a04a884..ecdb2d9 100644
--- a/src/lib/server_common/tests/Makefile.am
+++ b/src/lib/server_common/tests/Makefile.am
@@ -27,17 +27,23 @@ if HAVE_GTEST
TESTS += run_unittests
run_unittests_SOURCES = run_unittests.cc
run_unittests_SOURCES += portconfig_unittest.cc
+run_unittests_SOURCES += keyring_test.cc
+nodist_run_unittests_SOURCES = data_path.h
run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
run_unittests_LDADD = $(GTEST_LDADD)
-
run_unittests_LDADD += $(top_builddir)/src/lib/server_common/libserver_common.la
-run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
run_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libasiolink.la
run_unittests_LDADD += $(top_builddir)/src/lib/asiodns/libasiodns.la
run_unittests_LDADD += $(top_builddir)/src/lib/cc/libcc.la
run_unittests_LDADD += $(top_builddir)/src/lib/dns/libdns++.la
+run_unittests_LDADD += $(top_builddir)/src/lib/util/unittests/libutil_unittests.la
+run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
+run_unittests_LDADD += $(top_builddir)/src/lib/config/libcfgclient.la
+run_unittests_LDADD += $(top_builddir)/src/lib/config/tests/libfake_session.la
endif
noinst_PROGRAMS = $(TESTS)
+
+EXTRA_DIST = testdata/spec.spec
diff --git a/src/lib/server_common/tests/data_path.h.in b/src/lib/server_common/tests/data_path.h.in
new file mode 100644
index 0000000..8ac0380
--- /dev/null
+++ b/src/lib/server_common/tests/data_path.h.in
@@ -0,0 +1,16 @@
+// 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.
+
+#define TEST_DATA_PATH "@abs_srcdir@/testdata"
+#define PLUGIN_DATA_PATH "@top_srcdir@/src/bin/cfgmgr/plugins"
diff --git a/src/lib/server_common/tests/keyring_test.cc b/src/lib/server_common/tests/keyring_test.cc
new file mode 100644
index 0000000..6d2f226
--- /dev/null
+++ b/src/lib/server_common/tests/keyring_test.cc
@@ -0,0 +1,132 @@
+// 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 <server_common/keyring.h>
+#include <server_common/tests/data_path.h>
+
+#include <config/tests/fake_session.h>
+#include <config/ccsession.h>
+#include <dns/name.h>
+
+#include <gtest/gtest.h>
+#include <memory>
+#include <string>
+
+using namespace isc::data;
+using namespace isc::config;
+using namespace isc::server_common;
+using namespace isc::dns;
+
+namespace {
+
+class KeyringTest : public ::testing::Test {
+public:
+ KeyringTest() :
+ session(ElementPtr(new ListElement), ElementPtr(new ListElement),
+ ElementPtr(new ListElement)),
+ specfile(std::string(TEST_DATA_PATH) + "/spec.spec")
+ {
+ session.getMessages()->add(createAnswer());
+ mccs.reset(new ModuleCCSession(specfile, session, NULL, NULL));
+ }
+ isc::cc::FakeSession session;
+ std::auto_ptr<ModuleCCSession> mccs;
+ std::string specfile;
+ void doInit() {
+ // Prepare the module specification for it and the config
+ session.getMessages()->
+ add(createAnswer(0,
+ moduleSpecFromFile(std::string(PLUGIN_DATA_PATH) +
+ "/tsig_keys.spec").
+ getFullSpec()));
+ session.getMessages()->add(createAnswer(0, Element::fromJSON(
+ "{\"keys\": [\"key:MTIzNAo=:hmac-sha1\"]}")));
+ // Now load it
+ EXPECT_NO_THROW(initKeyring(*mccs));
+ EXPECT_NE(keyring, boost::shared_ptr<TSIGKeyRing>()) <<
+ "No keyring even after init";
+ }
+};
+
+// Test usual use - init, using the keyring, update, deinit
+TEST_F(KeyringTest, keyring) {
+ // First, initialize it
+ {
+ SCOPED_TRACE("Init");
+ doInit();
+
+ // Make sure it contains the correct key
+ TSIGKeyRing::FindResult result(keyring->find(Name("key"),
+ TSIGKey::HMACSHA1_NAME()));
+ EXPECT_EQ(TSIGKeyRing::SUCCESS, result.code);
+ }
+
+ {
+ SCOPED_TRACE("Update");
+ session.addMessage(createCommand("config_update", Element::fromJSON(
+ "{\"keys\": [\"another:MTIzNAo=:hmac-sha256\"]}")),
+ "tsig_keys", "*");
+ mccs->checkCommand();
+
+ // Make sure it no longer contains the original key
+ TSIGKeyRing::FindResult result(keyring->find(Name("key"),
+ TSIGKey::HMACSHA1_NAME()));
+ EXPECT_EQ(TSIGKeyRing::NOTFOUND, result.code);
+ // but it does contain the new one
+ TSIGKeyRing::FindResult result2 = keyring->find(Name("another"),
+ TSIGKey::HMACSHA256_NAME());
+ EXPECT_EQ(TSIGKeyRing::SUCCESS, result2.code);
+ }
+
+ {
+ SCOPED_TRACE("Deinit");
+ deinitKeyring(*mccs);
+ EXPECT_EQ(keyring, boost::shared_ptr<TSIGKeyRing>()) <<
+ "The keyring didn't disappear";
+ }
+}
+
+// Init twice
+TEST_F(KeyringTest, initTwice) {
+ // It is NULL before
+ EXPECT_EQ(keyring, boost::shared_ptr<TSIGKeyRing>()) <<
+ "Someone forgot to deinit it before";
+ {
+ SCOPED_TRACE("First init");
+ doInit();
+ }
+ boost::shared_ptr<TSIGKeyRing> backup(keyring);
+ {
+ SCOPED_TRACE("Second init");
+ EXPECT_NO_THROW(initKeyring(*mccs)) <<
+ "It not only does something when it is already initialized, "
+ "it even throws at it";
+ }
+ EXPECT_EQ(backup, keyring) << "The second init replaced the data";
+ deinitKeyring(*mccs);
+}
+
+// deinit when not initialized
+TEST_F(KeyringTest, extraDeinit) {
+ // It is NULL before
+ EXPECT_EQ(boost::shared_ptr<TSIGKeyRing>(), keyring) <<
+ "Someone forgot to deinit it before";
+ // Check that it doesn't get confused when we do not have it initialized
+ EXPECT_NO_THROW(deinitKeyring(*mccs));
+ // It is still NULL
+ EXPECT_EQ(keyring, boost::shared_ptr<TSIGKeyRing>()) <<
+ "Where did it get something after deinit?";
+}
+
+}
diff --git a/src/lib/server_common/tests/run_unittests.cc b/src/lib/server_common/tests/run_unittests.cc
index 7ebc985..b982ef3 100644
--- a/src/lib/server_common/tests/run_unittests.cc
+++ b/src/lib/server_common/tests/run_unittests.cc
@@ -15,6 +15,7 @@
#include <config.h>
#include <gtest/gtest.h>
+#include <util/unittests/run_all.h>
#include <dns/tests/unittest_util.h>
@@ -22,5 +23,5 @@ int
main(int argc, char* argv[]) {
::testing::InitGoogleTest(&argc, argv);
- return (RUN_ALL_TESTS());
+ return (isc::util::unittests::run_all());
}
diff --git a/src/lib/server_common/tests/testdata/spec.spec b/src/lib/server_common/tests/testdata/spec.spec
new file mode 100644
index 0000000..3e0a822
--- /dev/null
+++ b/src/lib/server_common/tests/testdata/spec.spec
@@ -0,0 +1,6 @@
+{
+ "module_spec": {
+ "module_name": "test"
+ }
+}
+
diff --git a/src/lib/util/Makefile.am b/src/lib/util/Makefile.am
index 6deec27..3db9ac4 100644
--- a/src/lib/util/Makefile.am
+++ b/src/lib/util/Makefile.am
@@ -1,6 +1,4 @@
-SUBDIRS = . tests unittests io
-# The io/tests is hack, because otherwise we can not order these directories
-# properly. Unittests use io and io/tests use unittest.
+SUBDIRS = . io unittests tests pyunittests
AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
AM_CPPFLAGS += -I$(top_srcdir)/src/lib/util -I$(top_builddir)/src/lib/util
diff --git a/src/lib/util/io/Makefile.am b/src/lib/util/io/Makefile.am
index 9f06ef9..b2653d8 100644
--- a/src/lib/util/io/Makefile.am
+++ b/src/lib/util/io/Makefile.am
@@ -1,5 +1,3 @@
-SUBDIRS = . tests
-
AM_CXXFLAGS = $(B10_CXXFLAGS)
lib_LTLIBRARIES = libutil_io.la
diff --git a/src/lib/util/io/tests/Makefile.am b/src/lib/util/io/tests/Makefile.am
deleted file mode 100644
index 56d50cf..0000000
--- a/src/lib/util/io/tests/Makefile.am
+++ /dev/null
@@ -1,25 +0,0 @@
-CLEANFILES = *.gcno *.gcda
-
-AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
-AM_CXXFLAGS = $(B10_CXXFLAGS)
-
-if USE_STATIC_LINK
-AM_LDFLAGS = -static
-endif
-
-TESTS =
-if HAVE_GTEST
-TESTS += run_unittests
-run_unittests_SOURCES = run_unittests.cc
-run_unittests_SOURCES += fd_tests.cc
-run_unittests_SOURCES += fd_share_tests.cc
-
-run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
-run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
-run_unittests_LDADD = $(GTEST_LDADD)
-run_unittests_LDADD += $(top_builddir)/src/lib/util/io/libutil_io.la
-run_unittests_LDADD += \
- $(top_builddir)/src/lib/util/unittests/libutil_unittests.la
-endif
-
-noinst_PROGRAMS = $(TESTS)
diff --git a/src/lib/util/io/tests/fd_share_tests.cc b/src/lib/util/io/tests/fd_share_tests.cc
deleted file mode 100644
index 0902ce0..0000000
--- a/src/lib/util/io/tests/fd_share_tests.cc
+++ /dev/null
@@ -1,74 +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.
-
-#include "../fd.h"
-#include "../fd_share.h"
-
-#include <util/unittests/fork.h>
-
-#include <gtest/gtest.h>
-#include <unistd.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <cstdio>
-
-using namespace isc::util::io;
-using namespace isc::util::unittests;
-
-namespace {
-
-// We test that we can transfer a pipe over other pipe
-TEST(FDShare, transfer) {
- // Get a pipe and fork
- int pipes[2];
- ASSERT_NE(-1, socketpair(AF_UNIX, SOCK_STREAM, 0, pipes));
- pid_t sender(fork());
- ASSERT_NE(-1, sender);
- if(sender) { // We are in parent
- // Close the other side of pipe, we want only writible one
- EXPECT_NE(-1, close(pipes[0]));
- // Get a process to check data
- int fd(0);
- pid_t checker(check_output(&fd, "data", 4));
- ASSERT_NE(-1, checker);
- // Now, send the file descriptor, close it and close the pipe
- EXPECT_NE(-1, send_fd(pipes[1], fd));
- EXPECT_NE(-1, close(pipes[1]));
- EXPECT_NE(-1, close(fd));
- // Check both subprocesses ended well
- EXPECT_TRUE(process_ok(sender));
- EXPECT_TRUE(process_ok(checker));
- } else { // We are in child. We do not use ASSERT here
- // Close the write end, we only read
- if(close(pipes[1])) {
- exit(1);
- }
- // Get the file descriptor
- int fd(recv_fd(pipes[0]));
- if(fd == -1) {
- exit(1);
- }
- // This pipe is not needed
- if(close(pipes[0])) {
- exit(1);
- }
- // Send "data" trough the received fd, close it and be done
- if(!write_data(fd, "data", 4) || close(fd) == -1) {
- exit(1);
- }
- exit(0);
- }
-}
-
-}
diff --git a/src/lib/util/io/tests/fd_tests.cc b/src/lib/util/io/tests/fd_tests.cc
deleted file mode 100644
index 12b70d8..0000000
--- a/src/lib/util/io/tests/fd_tests.cc
+++ /dev/null
@@ -1,66 +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.
-
-#include "../fd.h"
-
-#include <util/unittests/fork.h>
-
-#include <gtest/gtest.h>
-
-using namespace isc::util::io;
-using namespace isc::util::unittests;
-
-namespace {
-
-// Make sure the test is large enough and does not fit into one
-// read or write request
-const size_t TEST_DATA_SIZE = 8 * 1024 * 1024;
-
-class FDTest : public ::testing::Test {
- public:
- unsigned char *data, *buffer;
- FDTest() :
- // We do not care what is inside, we just need it to be the same
- data(new unsigned char[TEST_DATA_SIZE]),
- buffer(NULL)
- { }
- ~ FDTest() {
- delete[] data;
- delete[] buffer;
- }
-};
-
-// Test we read what was sent
-TEST_F(FDTest, read) {
- int read_pipe(0);
- buffer = new unsigned char[TEST_DATA_SIZE];
- pid_t feeder(provide_input(&read_pipe, data, TEST_DATA_SIZE));
- ASSERT_GE(feeder, 0);
- ssize_t received(read_data(read_pipe, buffer, TEST_DATA_SIZE));
- EXPECT_TRUE(process_ok(feeder));
- EXPECT_EQ(TEST_DATA_SIZE, received);
- EXPECT_EQ(0, memcmp(data, buffer, received));
-}
-
-// Test we write the correct thing
-TEST_F(FDTest, write) {
- int write_pipe(0);
- pid_t checker(check_output(&write_pipe, data, TEST_DATA_SIZE));
- ASSERT_GE(checker, 0);
- EXPECT_TRUE(write_data(write_pipe, data, TEST_DATA_SIZE));
- EXPECT_EQ(0, close(write_pipe));
- EXPECT_TRUE(process_ok(checker));
-}
-
-}
diff --git a/src/lib/util/io/tests/run_unittests.cc b/src/lib/util/io/tests/run_unittests.cc
deleted file mode 100644
index e787ab1..0000000
--- a/src/lib/util/io/tests/run_unittests.cc
+++ /dev/null
@@ -1,22 +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.
-
-#include <gtest/gtest.h>
-
-int
-main(int argc, char *argv[]) {
- ::testing::InitGoogleTest(&argc, argv);
-
- return RUN_ALL_TESTS();
-}
diff --git a/src/lib/util/python/wrapper_template.cc b/src/lib/util/python/wrapper_template.cc
index 2fe7567..691e4bf 100644
--- a/src/lib/util/python/wrapper_template.cc
+++ b/src/lib/util/python/wrapper_template.cc
@@ -12,6 +12,14 @@
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
+// Enable this if you use s# variants with PyArg_ParseTuple(), see
+// http://docs.python.org/py3k/c-api/arg.html#strings-and-buffers
+//#define PY_SSIZE_T_CLEAN
+
+// Python.h needs to be placed at the head of the program file, see:
+// http://docs.python.org/py3k/extending/extending.html#a-simple-example
+#include <Python.h>
+
#include <string>
#include <stdexcept>
@@ -202,7 +210,7 @@ namespace python {
// Most of the functions are not actually implemented and NULL here.
PyTypeObject @cppclass at _type = {
PyVarObject_HEAD_INIT(NULL, 0)
- "libdns_python. at CPPCLASS@",
+ "pydnspp. at CPPCLASS@",
sizeof(s_ at CPPCLASS@), // tp_basicsize
0, // tp_itemsize
reinterpret_cast<destructor>(@CPPCLASS at _destroy), // tp_dealloc
@@ -288,6 +296,14 @@ initModulePart_ at CPPCLASS@(PyObject* mod) {
return (true);
}
+
+PyObject*
+create at CPPCLASS@Object(const @CPPCLASS@& source) {
+ @CPPCLASS at Container container =
+ PyObject_New(s_ at CPPCLASS@, &@cppclass at _type);
+ container.set(new @CPPCLASS@(source));
+ return (container.release());
+}
} // namespace python
} // namespace @MODULE@
} // namespace isc
diff --git a/src/lib/util/python/wrapper_template.h b/src/lib/util/python/wrapper_template.h
index 1f983e4..d68a658 100644
--- a/src/lib/util/python/wrapper_template.h
+++ b/src/lib/util/python/wrapper_template.h
@@ -34,6 +34,21 @@ extern PyTypeObject @cppclass at _type;
bool initModulePart_ at CPPCLASS@(PyObject* mod);
+// Note: this utility function works only when @CPPCLASS@ is a copy
+// constructable.
+// And, it would only be useful when python binding needs to create this
+// object frequently. Otherwise, it would (or should) probably better to
+// remove the declaration and definition of this function.
+//
+/// This is A simple shortcut to create a python @CPPCLASS@ object (in the
+/// form of a pointer to PyObject) with minimal exception safety.
+/// On success, it returns a valid pointer to PyObject with a reference
+/// counter of 1; if something goes wrong it throws an exception (it never
+/// returns a NULL pointer).
+/// This function is expected to be called with in a try block
+/// followed by necessary setup for python exception.
+PyObject* create at CPPCLASS@Object(const @CPPCLASS@& source);
+
} // namespace python
} // namespace @MODULE@
} // namespace isc
diff --git a/src/lib/util/pyunittests/Makefile.am b/src/lib/util/pyunittests/Makefile.am
new file mode 100644
index 0000000..e8fefbd
--- /dev/null
+++ b/src/lib/util/pyunittests/Makefile.am
@@ -0,0 +1,14 @@
+AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
+AM_CPPFLAGS += $(BOOST_INCLUDES)
+AM_CXXFLAGS = $(B10_CXXFLAGS)
+
+pyexec_LTLIBRARIES = pyunittests_util.la
+pyunittests_util_la_SOURCES = pyunittests_util.cc
+pyunittests_util_la_CPPFLAGS = $(AM_CPPFLAGS) $(PYTHON_INCLUDES)
+pyunittests_util_la_LDFLAGS = $(PYTHON_LDFLAGS)
+
+# Python prefers .so, while some OSes (specifically MacOS) use a different
+# suffix for dynamic objects. -module is necessary to work this around.
+pyunittests_util_la_LDFLAGS += -module
+pyunittests_util_la_LIBADD = $(top_builddir)/src/lib/util/libutil.la
+pyunittests_util_la_LIBADD += $(PYTHON_LIB)
diff --git a/src/lib/util/pyunittests/pyunittests_util.cc b/src/lib/util/pyunittests/pyunittests_util.cc
new file mode 100644
index 0000000..d266c84
--- /dev/null
+++ b/src/lib/util/pyunittests/pyunittests_util.cc
@@ -0,0 +1,84 @@
+// 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 <Python.h>
+
+#include <stdint.h>
+
+// see util/time_utilities.h
+namespace isc {
+namespace util {
+namespace detail {
+extern int64_t (*gettimeFunction)();
+}
+}
+}
+
+namespace {
+int64_t fake_current_time;
+
+int64_t
+getFakeTime() {
+ return (fake_current_time);
+}
+
+PyObject*
+fixCurrentTime(PyObject*, PyObject* args) {
+ PyObject* maybe_none;
+ if (PyArg_ParseTuple(args, "L", &fake_current_time)) {
+ isc::util::detail::gettimeFunction = getFakeTime;
+ } else if (PyArg_ParseTuple(args, "O", &maybe_none) &&
+ maybe_none == Py_None) {
+ isc::util::detail::gettimeFunction = NULL;
+ } else {
+ PyErr_SetString(PyExc_TypeError, "Invalid arguments to "
+ "pyunittests_util.fix_current_time");
+ return (NULL);
+ }
+
+ PyErr_Clear();
+ Py_RETURN_NONE;
+}
+
+PyMethodDef PyUnittestsUtilMethods[] = {
+ { "fix_current_time", fixCurrentTime, METH_VARARGS,
+ "Fix the current system time at the specified (fake) value.\n\n"
+ "This is useful for testing modules that depend on the current time.\n"
+ "Note that it only affects C++ modules that use gettimeWrapper() "
+ "defined in libutil, which allows a hook for testing.\n"
+ "If an integer (signed 64bit) is given, the current time will be fixed "
+ "to that value; if None is specified (which is the default) the use of "
+ "faked time will be canceled."
+ },
+ { NULL, NULL, 0, NULL}
+};
+
+PyModuleDef pyunittests_util = {
+ { PyObject_HEAD_INIT(NULL) NULL, 0, NULL},
+ "pyunittests_util",
+ "This module is a collection of utilities useful for testing "
+ "the BIND 10 C++ binding modules.",
+ -1,
+ PyUnittestsUtilMethods,
+ NULL,
+ NULL,
+ NULL,
+ NULL
+};
+} // end of unnamed namespace
+
+PyMODINIT_FUNC
+PyInit_pyunittests_util(void) {
+ return (PyModule_Create(&pyunittests_util));
+}
diff --git a/src/lib/util/tests/Makefile.am b/src/lib/util/tests/Makefile.am
index 7b97202..47243f8 100644
--- a/src/lib/util/tests/Makefile.am
+++ b/src/lib/util/tests/Makefile.am
@@ -1,8 +1,6 @@
SUBDIRS = .
AM_CPPFLAGS = -I$(top_builddir)/src/lib -I$(top_srcdir)/src/lib
-AM_CPPFLAGS += -I$(top_srcdir)/src/lib/util -I$(top_builddir)/src/lib/util
-AM_CPPFLAGS += -I$(top_srcdir)/src/lib/exceptions -I$(top_builddir)/src/lib/exceptions
AM_CPPFLAGS += $(BOOST_INCLUDES)
AM_CXXFLAGS = $(B10_CXXFLAGS)
@@ -15,26 +13,30 @@ CLEANFILES = *.gcno *.gcda
TESTS =
if HAVE_GTEST
TESTS += run_unittests
-run_unittests_SOURCES =
-run_unittests_SOURCES += filename_unittest.cc
-run_unittests_SOURCES += strutil_unittest.cc
-run_unittests_SOURCES += run_unittests.cc
+run_unittests_SOURCES = run_unittests.cc
run_unittests_SOURCES += base32hex_unittest.cc
run_unittests_SOURCES += base64_unittest.cc
-run_unittests_SOURCES += hex_unittest.cc
-run_unittests_SOURCES += sha1_unittest.cc
run_unittests_SOURCES += buffer_unittest.cc
-run_unittests_SOURCES += time_utilities_unittest.cc
-run_unittests_SOURCES += random_number_generator_unittest.cc
-run_unittests_SOURCES += lru_list_unittest.cc
+run_unittests_SOURCES += fd_share_tests.cc
+run_unittests_SOURCES += fd_tests.cc
+run_unittests_SOURCES += filename_unittest.cc
+run_unittests_SOURCES += hex_unittest.cc
run_unittests_SOURCES += io_utilities_unittest.cc
+run_unittests_SOURCES += lru_list_unittest.cc
run_unittests_SOURCES += qid_gen_unittest.cc
+run_unittests_SOURCES += random_number_generator_unittest.cc
+run_unittests_SOURCES += sha1_unittest.cc
+run_unittests_SOURCES += strutil_unittest.cc
+run_unittests_SOURCES += time_utilities_unittest.cc
run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
run_unittests_LDADD = $(GTEST_LDADD)
run_unittests_LDADD += $(top_builddir)/src/lib/util/libutil.la
+run_unittests_LDADD += $(top_builddir)/src/lib/util/io/libutil_io.la
+run_unittests_LDADD += \
+ $(top_builddir)/src/lib/util/unittests/libutil_unittests.la
run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
endif
diff --git a/src/lib/util/tests/fd_share_tests.cc b/src/lib/util/tests/fd_share_tests.cc
new file mode 100644
index 0000000..cc92e47
--- /dev/null
+++ b/src/lib/util/tests/fd_share_tests.cc
@@ -0,0 +1,74 @@
+// 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 <util/io/fd.h>
+#include <util/io/fd_share.h>
+
+#include <util/unittests/fork.h>
+
+#include <gtest/gtest.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <cstdio>
+
+using namespace isc::util::io;
+using namespace isc::util::unittests;
+
+namespace {
+
+// We test that we can transfer a pipe over other pipe
+TEST(FDShare, transfer) {
+ // Get a pipe and fork
+ int pipes[2];
+ ASSERT_NE(-1, socketpair(AF_UNIX, SOCK_STREAM, 0, pipes));
+ pid_t sender(fork());
+ ASSERT_NE(-1, sender);
+ if(sender) { // We are in parent
+ // Close the other side of pipe, we want only writible one
+ EXPECT_NE(-1, close(pipes[0]));
+ // Get a process to check data
+ int fd(0);
+ pid_t checker(check_output(&fd, "data", 4));
+ ASSERT_NE(-1, checker);
+ // Now, send the file descriptor, close it and close the pipe
+ EXPECT_NE(-1, send_fd(pipes[1], fd));
+ EXPECT_NE(-1, close(pipes[1]));
+ EXPECT_NE(-1, close(fd));
+ // Check both subprocesses ended well
+ EXPECT_TRUE(process_ok(sender));
+ EXPECT_TRUE(process_ok(checker));
+ } else { // We are in child. We do not use ASSERT here
+ // Close the write end, we only read
+ if(close(pipes[1])) {
+ exit(1);
+ }
+ // Get the file descriptor
+ int fd(recv_fd(pipes[0]));
+ if(fd == -1) {
+ exit(1);
+ }
+ // This pipe is not needed
+ if(close(pipes[0])) {
+ exit(1);
+ }
+ // Send "data" trough the received fd, close it and be done
+ if(!write_data(fd, "data", 4) || close(fd) == -1) {
+ exit(1);
+ }
+ exit(0);
+ }
+}
+
+}
diff --git a/src/lib/util/tests/fd_tests.cc b/src/lib/util/tests/fd_tests.cc
new file mode 100644
index 0000000..6ba2766
--- /dev/null
+++ b/src/lib/util/tests/fd_tests.cc
@@ -0,0 +1,66 @@
+// 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 <util/io/fd.h>
+
+#include <util/unittests/fork.h>
+
+#include <gtest/gtest.h>
+
+using namespace isc::util::io;
+using namespace isc::util::unittests;
+
+namespace {
+
+// Make sure the test is large enough and does not fit into one
+// read or write request
+const size_t TEST_DATA_SIZE = 8 * 1024 * 1024;
+
+class FDTest : public ::testing::Test {
+ public:
+ unsigned char *data, *buffer;
+ FDTest() :
+ // We do not care what is inside, we just need it to be the same
+ data(new unsigned char[TEST_DATA_SIZE]),
+ buffer(NULL)
+ { }
+ ~ FDTest() {
+ delete[] data;
+ delete[] buffer;
+ }
+};
+
+// Test we read what was sent
+TEST_F(FDTest, read) {
+ int read_pipe(0);
+ buffer = new unsigned char[TEST_DATA_SIZE];
+ pid_t feeder(provide_input(&read_pipe, data, TEST_DATA_SIZE));
+ ASSERT_GE(feeder, 0);
+ ssize_t received(read_data(read_pipe, buffer, TEST_DATA_SIZE));
+ EXPECT_TRUE(process_ok(feeder));
+ EXPECT_EQ(TEST_DATA_SIZE, received);
+ EXPECT_EQ(0, memcmp(data, buffer, received));
+}
+
+// Test we write the correct thing
+TEST_F(FDTest, write) {
+ int write_pipe(0);
+ pid_t checker(check_output(&write_pipe, data, TEST_DATA_SIZE));
+ ASSERT_GE(checker, 0);
+ EXPECT_TRUE(write_data(write_pipe, data, TEST_DATA_SIZE));
+ EXPECT_EQ(0, close(write_pipe));
+ EXPECT_TRUE(process_ok(checker));
+}
+
+}
diff --git a/src/lib/util/tests/run_unittests.cc b/src/lib/util/tests/run_unittests.cc
index bd3c4c9..a2181cf 100644
--- a/src/lib/util/tests/run_unittests.cc
+++ b/src/lib/util/tests/run_unittests.cc
@@ -13,9 +13,11 @@
// PERFORMANCE OF THIS SOFTWARE.
#include <gtest/gtest.h>
+#include <util/unittests/run_all.h>
int
main(int argc, char* argv[]) {
::testing::InitGoogleTest(&argc, argv);
- return (RUN_ALL_TESTS());
+
+ return (isc::util::unittests::run_all());
}
diff --git a/src/lib/util/unittests/Makefile.am b/src/lib/util/unittests/Makefile.am
index 340cd1f..83235f2 100644
--- a/src/lib/util/unittests/Makefile.am
+++ b/src/lib/util/unittests/Makefile.am
@@ -5,6 +5,18 @@ 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
+if HAVE_GTEST
+libutil_unittests_la_SOURCES += run_all.h run_all.cc
libutil_unittests_la_SOURCES += textdata.h
+endif
+
+libutil_unittests_la_CPPFLAGS = $(AM_CPPFLAGS)
+if HAVE_GTEST
+libutil_unittests_la_CPPFLAGS += $(GTEST_INCLUDES)
+endif
+
+libutil_unittests_la_LIBADD = $(top_builddir)/src/lib/util/libutil.la
+libutil_unittests_la_LIBADD += $(top_builddir)/src/lib/util/io/libutil_io.la
+libutil_unittests_la_LIBADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
CLEANFILES = *.gcno *.gcda
diff --git a/src/lib/util/unittests/run_all.cc b/src/lib/util/unittests/run_all.cc
new file mode 100644
index 0000000..5f50f77
--- /dev/null
+++ b/src/lib/util/unittests/run_all.cc
@@ -0,0 +1,95 @@
+// 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 <stdlib.h>
+
+#include <iostream>
+#include <iomanip>
+
+#include <gtest/gtest.h>
+#include <exceptions/exceptions.h>
+#include <util/unittests/run_all.h>
+
+namespace isc {
+namespace util {
+namespace unittests {
+
+int
+run_all() {
+ int ret = 0;
+
+ // The catching of exceptions generated in tests is controlled by the
+ // B10TEST_CATCH_EXCEPTION environment variable. Setting this to
+ // 1 enables the cacthing of exceptions; setting it to 0 disables it.
+ // Anything else causes a message to be printed to stderr and the default
+ // taken. (The default is to catch exceptions if compiling with clang
+ // and false if not.)
+#ifdef __clang__
+ bool catch_exception = true;
+#else
+ bool catch_exception = false;
+#endif
+
+ const char* b10test_catch_exception = getenv("B10TEST_CATCH_EXCEPTION");
+ if (b10test_catch_exception != NULL) {
+ if (strcmp(b10test_catch_exception, "1") == 0) {
+ catch_exception = true;
+ } else if (strcmp(b10test_catch_exception, "0") == 0) {
+ catch_exception = false;
+ } else {
+ std::cerr << "***ERROR: B10TEST_CATCH_EXCEPTION is '"
+ << b10test_catch_exception
+ << "': allowed values are '1' or '0'.\n"
+ << " The default value of "
+ << (catch_exception ?
+ "1 (exception catching enabled)":
+ "0 (exception catching disabled)")
+ << " will be used.\n";
+ }
+ }
+
+ // Actually run the code
+ if (catch_exception) {
+ try {
+ ret = RUN_ALL_TESTS();
+ } catch (const isc::Exception& ex) {
+ // Could output more information with typeid(), but there is no
+ // guarantee that all compilers will support it without an explicit
+ // flag on the command line.
+ std::cerr << "*** Exception derived from isc::exception thrown:\n"
+ << " file: " << ex.getFile() << "\n"
+ << " line: " << ex.getLine() << "\n"
+ << " what: " << ex.what() << std::endl;
+ throw;
+ } catch (const std::exception& ex) {
+ std::cerr << "*** Exception derived from std::exception thrown:\n"
+ << " what: " << ex.what() << std::endl;
+ throw;
+ }
+ } else {
+ // This is a separate path for the case where the exception is not
+ // being caught. Although the other code path re-throws the exception
+ // after catching it, there is no guarantee that the state of the
+ // stack is preserved - a compiler might have unwound the stack to
+ // the point at which the exception is caught. This would prove
+ // awkward if trying to debug the program using a debugger.
+ ret = RUN_ALL_TESTS();
+ }
+
+ return (ret);
+}
+
+} // namespace unittests
+} // namespace util
+} // namespace isc
diff --git a/src/lib/util/unittests/run_all.h b/src/lib/util/unittests/run_all.h
new file mode 100644
index 0000000..94c7cb0
--- /dev/null
+++ b/src/lib/util/unittests/run_all.h
@@ -0,0 +1,52 @@
+// 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 __RUN_ALL_H
+#define __RUN_ALL_H
+
+// Avoid need for user to include this header file.
+
+#include <gtest/gtest.h>
+
+namespace isc {
+namespace util {
+namespace unittests {
+
+/// \brief Run All Tests
+///
+/// A wrapper for the Google Test RUN_ALL_TESTS() macro, this calls the macro
+/// but wraps the call in a try...catch block if the environment variable
+/// B10TEST_CATCH_EXCEPTION is defined, and calls the macro directly if not.
+///
+/// The catch block catches exceptions of types isc::Exception and
+/// std::exception and prints some information about them to stderr. (In the
+/// case of isc::Exception, this includes the file and line number from which
+/// the exception was raised.) It then re-throws the exception.
+///
+/// See: https://lists.isc.org/pipermail/bind10-dev/2011-January/001867.html
+/// for some context.
+///
+/// \return Return value from RUN_ALL_TESTS().
+
+int run_all();
+
+} // namespace unittests
+} // namespace util
+} // namespace isc
+
+
+
+#endif // __RUN_ALL_H
diff --git a/tests/tools/badpacket/tests/Makefile.am b/tests/tools/badpacket/tests/Makefile.am
index e83c3b6..6d0d4e1 100644
--- a/tests/tools/badpacket/tests/Makefile.am
+++ b/tests/tools/badpacket/tests/Makefile.am
@@ -22,11 +22,11 @@ run_unittests_SOURCES += $(top_builddir)/tests/tools/badpacket/option_info.cc
run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
-run_unittests_LDFLAGS += $(top_builddir)/src/lib/log/liblog.la
-run_unittests_LDFLAGS += $(top_builddir)/src/lib/exceptions/libexceptions.la
-run_unittests_LDFLAGS += $(top_builddir)/src/lib/util/libutil.la
run_unittests_LDADD = $(GTEST_LDADD)
+run_unittests_LDFLAGS += $(top_builddir)/src/lib/exceptions/libexceptions.la
+run_unittests_LDFLAGS += $(top_builddir)/src/lib/util/libutil.la
+run_unittests_LDADD += $(top_builddir)/src/lib/util/unittests/libutil_unittests.la
endif
noinst_PROGRAMS = $(TESTS)
diff --git a/tests/tools/badpacket/tests/run_unittests.cc b/tests/tools/badpacket/tests/run_unittests.cc
index 624cf6f..6eeca75 100644
--- a/tests/tools/badpacket/tests/run_unittests.cc
+++ b/tests/tools/badpacket/tests/run_unittests.cc
@@ -15,10 +15,11 @@
#include <config.h>
#include <gtest/gtest.h>
+#include <util/unittests/run_all.h>
int
main(int argc, char* argv[]) {
::testing::InitGoogleTest(&argc, argv);
- return (RUN_ALL_TESTS());
+ return (isc::util::unittests::run_all());
}
More information about the bind10-changes
mailing list