BIND 10 trac1600, updated. 1ddc5eccbe9fc1784a3447c38f708a389a45d83f Merge branch 'master' into trac1600
BIND 10 source code commits
bind10-changes at lists.isc.org
Thu Mar 15 01:34:45 UTC 2012
The branch, trac1600 has been updated
via 1ddc5eccbe9fc1784a3447c38f708a389a45d83f (commit)
via b35f3264ece9358619c3831aa6fe6bf74a14c8c9 (commit)
via 69aae7b1a831c5393785759efa3bcdcd238f599c (commit)
via 28ec5cffac56ead6c2c79ffd31427c46dde1b061 (commit)
via 06d963e3098ea6ead0eae1b54f178222b9fff479 (commit)
via ac2c63f07c78ba94fb8b76ce1051a5810b97a99a (commit)
via 02db7759b655b9eb3e5d4e750e5f5bf212e1157f (commit)
via 1007c99c026d5d7f51a95061d25f59d621441b39 (commit)
via 216b2e18c9811ec25dc99ae9b320140033e26a36 (commit)
via 64eef65d27bf5310afa53cac418c18bc8a989b31 (commit)
via 1af6565d9d5f789c82ececbf83ca725cf3208b3f (commit)
via 55d44fc94c8e0ddae7b9b383e7e29b9514ce23ff (commit)
via b8d64503f7fcbf832c4ca6bdd0dbd55678baf88c (commit)
via 9a2a86f3f47b60ff017ce1a040941d0c145cfe16 (commit)
via 896dd6912cefe2190fc12f58b2ce291f17599b8e (commit)
via a8c1ced88d2e49827f5082555e335e209fd32ef8 (commit)
via 2cc4d39e5c74092c2fda2d9be3682b432243099b (commit)
via ae873233be8b119b0495b98d843fada53949ab58 (commit)
via 62a3bb1472df7765c40525dfe9c07ab949fa4a72 (commit)
via fec011eb0defae05d4f0803467c841d2f27aee29 (commit)
via a8e53be7039ad50d8587c0972244029ff3533b6e (commit)
via a167e7085865d77a1a9311a5cfae067da988d5ac (commit)
via 292fc936f6bfbcf84e56e67b52a16368a726d32f (commit)
via 4dbcf3b666bf27ae2c3018007e163235e5c326e2 (commit)
via b469e6036af3a8d4b12dded6597ea08abb2196bb (commit)
via 869ed54d546cbc8d019993e3c3b81b8a14dd0128 (commit)
via 5cdde563b1ff647a83729c768f688188ac90bfd4 (commit)
via 5a045dfb928e3a2d5702bea2e803a6f6ac5b030d (commit)
via 3d8de45a64068a289bc8eebca3b21c308047cdc8 (commit)
via ec745dbffae8c60801738225ecb3c71b9d758091 (commit)
via cebaf1622cb9e73257b9876bfe863a3102803766 (commit)
via 9ef6f17654ebfc0f103f8563a21db07dd141806f (commit)
via 8cff3a658f4c21b8e83042e475491dbdcd404983 (commit)
via 8a0d8d026e4489913560819c438299560db77724 (commit)
via abca9b6b8de3fcb0c6d99df48793eb975aea0e5e (commit)
via ea5ac57d508a17611cfae9d9ea1c238f59d52c51 (commit)
via 75b062049db49cade952510e66324761a8ba09b6 (commit)
via 38278a2b5a74a76708d89862f8e9eee56bd84ab1 (commit)
via bf8e9e937a158253853013a6f316573d3b53e3ee (commit)
via a2d27b3d05763fa267d6f19cff7468ac86063a38 (commit)
via 0f0ab5b692e5670b0ff6774d0003a5f676a79dcc (commit)
via b87b288e07b97629fa80b36b0cfa0833727b785a (commit)
via a979801041c9a650f2b24a4c39498df80d72b75a (commit)
via 97bca3e1ce5cb69be9fb6fcbc9bf976fb05e3a2d (commit)
via 27ec89e237865a6f9d92639256291acade7af69f (commit)
via 004375eb0955a2eb1fbfa5d5988cdc5b10ae441f (commit)
via 6c506ce59394354abe02a650579ec0517867a3a4 (commit)
via 8efd8d022529b4d5178329bee08a6909d5823c04 (commit)
via f64dbc690b23b7354c8c67ca0add3b2ae07fcd0c (commit)
via d4935b95254646ab389e43517bf3cf61373d23e0 (commit)
via 08ac200f2f3e15548a81b4c252b72fc8ac72edbe (commit)
via 4bb8254e151217f9ac5c29743d57b239c8f11d1c (commit)
via 0b55e11e55f2422e2aa1f920f48debac92d826af (commit)
via c3962c4f4d5f08e3ff194132a6308dbede212997 (commit)
via 91a89cb8cac804441d957430593c43f40645a44d (commit)
via 8123c01dbd4f71cdff0ba73a45343ff51de0908e (commit)
via 914212e06d69ae4e24ac673b050dd60a7eadfbae (commit)
via 6b1caed1a4161932e68ef52f067d07d822d85c94 (commit)
via 2b01d944b6a137f95d47673ea8367315289c205d (commit)
via ee3042d8a998de0a2a272003f53957a1af500901 (commit)
via b1fb64710cddf53857625d7a871f82775e20d93b (commit)
via 6e7eee31fa6b4884490778ce4148eb346fa4bf28 (commit)
via cd8b0868f1b4bd63f4bc9b661f0afca448e57797 (commit)
via c3374ed01863bf7643cddd90f4ef47d338df6225 (commit)
via 1389eb8fa945be3ffd35d0daff3bcbf17da15549 (commit)
via 55fdccd716864e9f33675546d3b6fcce18027473 (commit)
via 5434c74a0bf1a4435af9879572143a7b9574f713 (commit)
via eabc47cf4ee5ff9e3cc92a2e2b7c5b9481fa7bca (commit)
via 2b13ceb6879f7c02662a0973d5c37333b3b3d660 (commit)
via 84a7f88f41fd49749deda05aaef7e978bd0fb90f (commit)
via dd95b7f18281fd4f06d5f13b5a99ca9e083f774e (commit)
via 62411740bf363a984e453df37b95bbbdebc443e4 (commit)
via 79b80617cf3f9c979b5f541d1cb82274631f5fdc (commit)
via ae7b2a915adb90a32070ab3cca4f589b449ed435 (commit)
via af7d66f37a3784fae54021afeba046383984ffc9 (commit)
via 71032a09e3cc7e34133378b426ad83d13ca3e0ac (commit)
via 42991798446e79e8e5e60641430d3d5a8cc0c1bd (commit)
via fe8e4b87d5b493fa2cf41f1e8dce1b5809c27679 (commit)
via 99c9c5e589d0359c115265864302b8dfefb210db (commit)
via ead42e5f5189b6fbd72823d6931eddff10409d9a (commit)
via 3adabc6a1ece55ee01bc5b0d6aee8aebca2510d2 (commit)
via 1cd0d0e4fc9324bbe7f8593478e2396d06337b1e (commit)
via 1030e6751920c754fa9d02360ef95342541b85b6 (commit)
via 638db2f91b681d050081a15e701011630c421f06 (commit)
via 14d4b5dd3ef4cb9cee473e70aaeaa5d63dac6113 (commit)
via 321b22346c57951b3e9758b471c4a1c95f5bb5be (commit)
via abfbe328e4a378f63a4eb5b3fb95949a38d21557 (commit)
via 2e940ea65d5b9f371c26352afd9e66719c38a6b9 (commit)
via 4a82ece4c4e353ab8e3ceb01fd8e0f4824ac6bbf (commit)
via b8960ab85c717fe70ad282e0052ac0858c5b57f7 (commit)
via f537c7e12fb7b25801408f93132ed33410edae76 (commit)
via 737c22c1d763e638bd958a5507bb2012267c8a22 (commit)
via bfe0f613e15f8c732462f43677f846610e496e08 (commit)
via 4e7ace34dbde7f41d474fc93a0bee27361b9651d (commit)
via 21a0d06de02275f51776d1177c7b64ababc2489a (commit)
via 81d738596851cc2f0d4e475c9c26e7e41c8bc1f7 (commit)
via d7fb4b7244e60a034e21873d5bd32f7148ccd973 (commit)
via d9ae23e5bc7ef47d29953bf54f2735d8f6f5f531 (commit)
via 5e17c750847213e462b9ca7537358d3d97975f61 (commit)
via 736fb87e53b0d57241c4e414ed5de2f3eacc6e6e (commit)
via 4cee65dd891d8dac00680084680fa57d89bea82d (commit)
via 7eda3c0ee0795ed21cb4fb44ff20f905de1cf800 (commit)
via beed5d700bd2ef38453f3a60e1bc2d4785c3bc68 (commit)
via f0b94ec2fb650ddf64d8d51dabb427056465747a (commit)
via 1296fe25ed708996f9aca9d3ae063dd80d444d9d (commit)
via ba84010ff254a3159eaac71db89a6c128d163736 (commit)
via d31372d7b6961c777c325350a4f415988a567715 (commit)
via 2903127df129af292b510a1c423e274ddc91ed2b (commit)
via 5a8474bf5e3b8e10959c76b573d3f0e1af03ebf1 (commit)
via cdbcf6b6134197bb28a3ee1c5460e78f9e89cdb5 (commit)
via fc609f2e7f47c724e2f7d713f4c3c41616751e76 (commit)
via 681c927f0d8150bbf3f71fa8c689b7712b834aa7 (commit)
via 03f1a5ea8f070b8b92704fc780cb858f6af04da8 (commit)
via bfb21aba64298f219218cf58ae9b6024c1ed7005 (commit)
via 516e15b88a729bc28ec04ee2713aa6b352f38875 (commit)
via 510292e639de813a6f8b02ee3e36726fae1da5d8 (commit)
via a0a39d4b6e67fe575206ae191ddadc2f6f29e35c (commit)
via cb40afcbf13f2f456f5e671c755db9457c04e012 (commit)
via 97fa3578b37fce3897f78bfd59990ec9928822b9 (commit)
via b5740c6b3962a55e46325b3c8b14c9d64cf0d845 (commit)
via bc65585535ce775f6a5cf8dae25dfbda98a489ae (commit)
via a2a9e24d6a5744ab6b916c8e94ab0b77137417f2 (commit)
via b3933db8909e3040dec00fffd670b0963e264506 (commit)
via dc0963ad902d475f08d4f1bebab4e5114c013922 (commit)
via d08e8c4fca2bdcbe3fd573c1872c8e9347a71fb0 (commit)
via 64611b969652b074976c72867bc78ca43f8e51be (commit)
via 6f31530ff7e74757980eb2d01b82852636991672 (commit)
via 29fab3811a8bbae6bc28198f7aec4378b338a03c (commit)
via 1633572f050616e4fc41502fa2bcbfd70ea594ef (commit)
via 184bd43c0451a18f9d7d792050c9089f8d329a1c (commit)
via b2e7f7e47b9ac2dc93ca57c63635255a9a50fbe7 (commit)
via de4b10ee8d53f5c9537ba98ad401f84d008efd69 (commit)
via 9a5e82f81a296fb59dfe75c7f47fe91471fcb13c (commit)
via 1d3d7c590668be4503e70c2d3f2ee1da955874ac (commit)
via 3e88cb09890b3d05c82c56be350b1b76325dff15 (commit)
via ac299353640a32e77ad0e3f630d1a6bdbfdbbb06 (commit)
via 118f1f9d41fed38bb12d46f41d50dd2fd7367a80 (commit)
via ff863e03a8cbdb0c971a101af01b604dc13d1457 (commit)
via 6b3dabe34a4fafac4a91fd0b953b49dfb846b713 (commit)
via 4bcec5ffb6b84ccc0e3b6598567d1e0b67b95976 (commit)
via eddbaf44858226fafadfad360eba55a79f69f085 (commit)
via 56f14dee951465a712f2fee1d4dcf36d7b87a4b7 (commit)
via d93639f9d862eee755aecbde69e37f40543f7109 (commit)
via 48b2af87d906f6280ab477950362e76f73b23482 (commit)
via 567c9411dc8574817c4155ac1215a2a8f08fd192 (commit)
via 9c4d0cadf4adc802cc41a2610dc2c30b25aad728 (commit)
via a74802e8424747421511511f78a344de22105c1d (commit)
via bb4cc9928f968cd9e72503116a83c1fbe8f07361 (commit)
via ac03fb060596dbebbb012d091292e4c9690f1c88 (commit)
from 8173e046080fe51963a37ba89107a8871a0b5353 (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 1ddc5eccbe9fc1784a3447c38f708a389a45d83f
Merge: 8173e046080fe51963a37ba89107a8871a0b5353 b35f3264ece9358619c3831aa6fe6bf74a14c8c9
Author: Xie Jiagui <xiejiagui at cnnic.cn>
Date: Thu Mar 15 09:33:56 2012 +0800
Merge branch 'master' into trac1600
Conflicts:
src/bin/auth/auth_srv.cc
src/bin/auth/tests/auth_srv_unittest.cc
-----------------------------------------------------------------------
Summary of changes:
.gitignore | 29 +
ChangeLog | 64 ++
compatcheck/.gitignore | 1 +
configure.ac | 67 --
doc/.gitignore | 1 +
doc/guide/bind10-guide.xml | 2 +-
doc/guide/bind10-messages.xml | 2 +-
src/bin/auth/.gitignore | 7 +
src/bin/auth/auth_messages.mes | 45 +-
src/bin/auth/auth_srv.cc | 180 +++---
src/bin/auth/auth_srv.h | 8 +-
src/bin/auth/benchmarks/.gitignore | 1 +
src/bin/auth/benchmarks/query_bench.cc | 24 +-
src/bin/auth/query.cc | 284 +++++----
src/bin/auth/query.h | 48 +--
src/bin/auth/tests/.gitignore | 1 +
src/bin/auth/tests/auth_srv_unittest.cc | 375 +++++++++--
src/bin/auth/tests/command_unittest.cc | 16 +-
src/bin/auth/tests/config_unittest.cc | 2 +-
src/bin/auth/tests/query_unittest.cc | 176 +++---
src/bin/bind10/.gitignore | 3 +
src/bin/bind10/bind10_messages.mes | 2 +-
src/bin/bind10/tests/.gitignore | 1 +
src/bin/bindctl/.gitignore | 3 +
src/bin/bindctl/tests/.gitignore | 1 +
src/bin/cfgmgr/.gitignore | 2 +
src/bin/cfgmgr/tests/.gitignore | 1 +
src/bin/cmdctl/.gitignore | 5 +
src/bin/cmdctl/tests/.gitignore | 1 +
src/bin/ddns/.gitignore | 2 +
src/bin/dhcp4/.gitignore | 3 +
src/bin/dhcp4/tests/.gitignore | 1 +
src/bin/host/.gitignore | 1 +
src/bin/loadzone/.gitignore | 3 +
src/bin/loadzone/tests/correct/.gitignore | 1 +
src/bin/loadzone/tests/error/.gitignore | 1 +
src/bin/msgq/.gitignore | 3 +
src/bin/msgq/tests/.gitignore | 1 +
src/bin/resolver/.gitignore | 7 +
src/bin/resolver/main.cc | 3 +-
src/bin/resolver/resolver_messages.mes | 2 +-
src/bin/resolver/tests/.gitignore | 1 +
src/bin/sockcreator/.gitignore | 1 +
src/bin/sockcreator/tests/.gitignore | 1 +
src/bin/stats/.gitignore | 4 +
src/bin/tests/.gitignore | 1 +
src/bin/usermgr/.gitignore | 3 +
src/bin/xfrin/.gitignore | 3 +
src/bin/xfrin/tests/.gitignore | 1 +
src/bin/xfrin/tests/xfrin_test.py | 81 +++
src/bin/xfrin/xfrin.py.in | 92 +++-
src/bin/xfrin/xfrin_messages.mes | 287 +++++----
src/bin/xfrout/.gitignore | 5 +
src/bin/xfrout/tests/.gitignore | 2 +
src/bin/zonemgr/.gitignore | 5 +
src/bin/zonemgr/tests/.gitignore | 1 +
src/lib/acl/tests/.gitignore | 1 +
src/lib/asiodns/.gitignore | 2 +
src/lib/asiodns/tests/.gitignore | 1 +
src/lib/asiolink/tests/.gitignore | 1 +
src/lib/bench/example/.gitignore | 1 +
src/lib/bench/tests/.gitignore | 1 +
src/lib/cache/.gitignore | 2 +
src/lib/cache/tests/.gitignore | 1 +
src/lib/cache/tests/Makefile.am | 5 -
src/lib/cc/.gitignore | 4 +
src/lib/cc/tests/.gitignore | 2 +
src/lib/config/.gitignore | 2 +
src/lib/config/tests/.gitignore | 2 +
src/lib/config/tests/testdata/.gitignore | 1 +
src/lib/cryptolink/tests/.gitignore | 1 +
src/lib/datasrc/.gitignore | 4 +
src/lib/datasrc/Makefile.am | 4 +-
src/lib/datasrc/database.cc | 59 +-
src/lib/datasrc/database.h | 77 ++-
src/lib/datasrc/memory_datasrc.cc | 677 ++++++++++++++------
src/lib/datasrc/memory_datasrc.h | 20 +-
src/lib/datasrc/memory_datasrc_link.cc | 173 +++++
src/lib/datasrc/rbnode_rrset.h | 167 +++---
src/lib/datasrc/rbtree.h | 6 +-
src/lib/datasrc/sqlite3_accessor.cc | 74 ---
src/lib/datasrc/sqlite3_accessor_link.cc | 105 +++
src/lib/datasrc/tests/.gitignore | 4 +
src/lib/datasrc/tests/Makefile.am | 51 +-
src/lib/datasrc/tests/database_unittest.cc | 74 ++--
src/lib/datasrc/tests/memory_datasrc_unittest.cc | 88 ++--
src/lib/datasrc/tests/rbnode_rrset_unittest.cc | 17 +
src/lib/datasrc/tests/testdata/.gitignore | 1 +
src/lib/datasrc/tests/testdata/contexttest.zone | 75 +++
.../datasrc/tests/zone_finder_context_unittest.cc | 412 ++++++++++++
src/lib/datasrc/zone.h | 268 ++++++--
src/lib/datasrc/zone_finder_context.cc | 102 +++
src/lib/dhcp/tests/.gitignore | 1 +
src/lib/dns/.gitignore | 6 +
src/lib/dns/Makefile.am | 5 +-
src/lib/dns/benchmarks/.gitignore | 2 +
src/lib/dns/benchmarks/Makefile.am | 10 +-
src/lib/dns/benchmarks/message_renderer_bench.cc | 176 +++++
src/lib/dns/benchmarks/oldmessagerenderer.cc | 278 ++++++++
src/lib/dns/benchmarks/oldmessagerenderer.h | 55 ++
src/lib/dns/labelsequence.cc | 49 ++-
src/lib/dns/labelsequence.h | 45 ++-
src/lib/dns/messagerenderer.cc | 317 ++++++----
src/lib/dns/messagerenderer.h | 10 +
src/lib/dns/name.cc | 22 +-
src/lib/dns/name_internal.h | 43 ++
src/lib/dns/rdata/generic/sshfp_44.cc | 164 +++++
src/lib/dns/rdata/generic/sshfp_44.h | 58 ++
src/lib/dns/rrset.cc | 10 +
src/lib/dns/rrset.h | 8 +
src/lib/dns/tests/.gitignore | 1 +
src/lib/dns/tests/Makefile.am | 1 +
src/lib/dns/tests/labelsequence_unittest.cc | 197 +++++--
src/lib/dns/tests/messagerenderer_unittest.cc | 36 +-
src/lib/dns/tests/rdata_sshfp_unittest.cc | 94 +++
src/lib/dns/tests/rrset_unittest.cc | 14 +
src/lib/dns/tests/testdata/.gitignore | 117 ++++
src/lib/dns/tests/testdata/Makefile.am | 3 +
src/lib/dns/tests/testdata/rdata_mx_fromWire | 2 +-
src/lib/dns/tests/testdata/rdata_sshfp_fromWire | 4 +
.../dns/tests/testdata/rdata_sshfp_fromWire1.spec | 6 +
src/lib/dns/tests/testdata/rdata_sshfp_fromWire2 | 4 +
.../dns/tests/testdata/rdata_sshfp_fromWire2.spec | 7 +
src/lib/dns/tests/unittest_util.cc | 18 +
src/lib/dns/tests/unittest_util.h | 16 +
src/lib/exceptions/tests/.gitignore | 1 +
src/lib/log/compiler/.gitignore | 1 +
src/lib/log/compiler/message.cc | 18 +-
src/lib/log/logger.h | 43 ++-
src/lib/log/logger_manager.cc | 4 +
src/lib/log/message_initializer.cc | 71 ++-
src/lib/log/message_initializer.h | 56 ++-
src/lib/log/tests/.gitignore | 11 +
src/lib/log/tests/Makefile.am | 99 ++-
src/lib/log/tests/logger_example.cc | 2 +-
src/lib/log/tests/logger_manager_unittest.cc | 8 +-
src/lib/log/tests/logger_unittest.cc | 31 +-
.../log/tests/message_initializer_1_unittest.cc | 79 +++
.../log/tests/message_initializer_1a_unittest.cc | 37 ++
.../log/tests/message_initializer_2_unittest.cc | 48 ++
src/lib/log/tests/message_initializer_unittest.cc | 70 --
.../log/tests/message_initializer_unittest_2.cc | 39 --
src/lib/log/tests/run_initializer_unittests.cc | 24 +
src/lib/nsas/.gitignore | 2 +
src/lib/nsas/tests/.gitignore | 1 +
src/lib/nsas/tests/Makefile.am | 5 -
src/lib/python/.gitignore | 1 +
src/lib/python/isc/bind10/component.py | 28 +-
src/lib/python/isc/cc/tests/.gitignore | 1 +
src/lib/python/isc/config/tests/.gitignore | 1 +
src/lib/python/isc/datasrc/finder_python.cc | 24 +-
src/lib/python/isc/log/tests/.gitignore | 1 +
src/lib/python/isc/log_messages/work/.gitignore | 2 +
src/lib/python/isc/notify/tests/.gitignore | 1 +
src/lib/resolve/.gitignore | 2 +
src/lib/resolve/tests/.gitignore | 1 +
src/lib/server_common/.gitignore | 2 +
src/lib/server_common/tests/.gitignore | 2 +
src/lib/statistics/tests/.gitignore | 1 +
src/lib/testutils/dnsmessage_test.cc | 3 +
src/lib/testutils/dnsmessage_test.h | 31 +-
src/lib/testutils/testdata/.gitignore | 12 +
src/lib/util/buffer.h | 42 +-
src/lib/util/locks.h | 58 +--
src/lib/util/python/.gitignore | 2 +
src/lib/util/python/gen_wiredata.py.in | 23 +
src/lib/util/tests/.gitignore | 1 +
src/lib/xfr/tests/.gitignore | 1 +
tests/lettuce/.gitignore | 1 +
tests/lettuce/features/xfrin_bind10.feature | 2 +-
tests/system/.gitignore | 2 +
tests/system/bindctl/nsx1/.gitignore | 1 +
tests/system/glue/.gitignore | 1 +
tests/system/glue/nsx1/.gitignore | 1 +
tests/system/ixfr/.gitignore | 8 +
tests/system/ixfr/in-1/.gitignore | 1 +
tests/system/ixfr/in-2/.gitignore | 1 +
tests/system/ixfr/in-2/tests.sh | 2 +-
tests/system/ixfr/in-3/.gitignore | 1 +
tests/system/ixfr/in-4/.gitignore | 1 +
tests/tools/badpacket/.gitignore | 1 +
tests/tools/badpacket/tests/.gitignore | 1 +
tests/tools/perfdhcp/.gitignore | 1 +
183 files changed, 5329 insertions(+), 1718 deletions(-)
create mode 100644 .gitignore
create mode 100644 compatcheck/.gitignore
create mode 100644 doc/.gitignore
create mode 100644 src/bin/auth/.gitignore
create mode 100644 src/bin/auth/benchmarks/.gitignore
create mode 100644 src/bin/auth/tests/.gitignore
create mode 100644 src/bin/bind10/.gitignore
create mode 100644 src/bin/bind10/tests/.gitignore
create mode 100644 src/bin/bindctl/.gitignore
create mode 100644 src/bin/bindctl/tests/.gitignore
create mode 100644 src/bin/cfgmgr/.gitignore
create mode 100644 src/bin/cfgmgr/tests/.gitignore
create mode 100644 src/bin/cmdctl/.gitignore
create mode 100644 src/bin/cmdctl/tests/.gitignore
create mode 100644 src/bin/ddns/.gitignore
create mode 100644 src/bin/dhcp4/.gitignore
create mode 100644 src/bin/dhcp4/tests/.gitignore
create mode 100644 src/bin/host/.gitignore
create mode 100644 src/bin/loadzone/.gitignore
create mode 100644 src/bin/loadzone/tests/correct/.gitignore
create mode 100644 src/bin/loadzone/tests/error/.gitignore
create mode 100644 src/bin/msgq/.gitignore
create mode 100644 src/bin/msgq/tests/.gitignore
create mode 100644 src/bin/resolver/.gitignore
create mode 100644 src/bin/resolver/tests/.gitignore
create mode 100644 src/bin/sockcreator/.gitignore
create mode 100644 src/bin/sockcreator/tests/.gitignore
create mode 100644 src/bin/stats/.gitignore
create mode 100644 src/bin/tests/.gitignore
create mode 100644 src/bin/usermgr/.gitignore
create mode 100644 src/bin/xfrin/.gitignore
create mode 100644 src/bin/xfrin/tests/.gitignore
create mode 100644 src/bin/xfrout/.gitignore
create mode 100644 src/bin/xfrout/tests/.gitignore
create mode 100644 src/bin/zonemgr/.gitignore
create mode 100644 src/bin/zonemgr/tests/.gitignore
create mode 100644 src/lib/acl/tests/.gitignore
create mode 100644 src/lib/asiodns/.gitignore
create mode 100644 src/lib/asiodns/tests/.gitignore
create mode 100644 src/lib/asiolink/tests/.gitignore
create mode 100644 src/lib/bench/example/.gitignore
create mode 100644 src/lib/bench/tests/.gitignore
create mode 100644 src/lib/cache/.gitignore
create mode 100644 src/lib/cache/tests/.gitignore
create mode 100644 src/lib/cc/.gitignore
create mode 100644 src/lib/cc/tests/.gitignore
create mode 100644 src/lib/config/.gitignore
create mode 100644 src/lib/config/tests/.gitignore
create mode 100644 src/lib/config/tests/testdata/.gitignore
create mode 100644 src/lib/cryptolink/tests/.gitignore
create mode 100644 src/lib/datasrc/.gitignore
create mode 100644 src/lib/datasrc/memory_datasrc_link.cc
create mode 100644 src/lib/datasrc/sqlite3_accessor_link.cc
create mode 100644 src/lib/datasrc/tests/.gitignore
create mode 100644 src/lib/datasrc/tests/testdata/.gitignore
create mode 100644 src/lib/datasrc/tests/testdata/contexttest.zone
create mode 100644 src/lib/datasrc/tests/zone_finder_context_unittest.cc
create mode 100644 src/lib/datasrc/zone_finder_context.cc
create mode 100644 src/lib/dhcp/tests/.gitignore
create mode 100644 src/lib/dns/.gitignore
create mode 100644 src/lib/dns/benchmarks/.gitignore
create mode 100644 src/lib/dns/benchmarks/message_renderer_bench.cc
create mode 100644 src/lib/dns/benchmarks/oldmessagerenderer.cc
create mode 100644 src/lib/dns/benchmarks/oldmessagerenderer.h
create mode 100644 src/lib/dns/name_internal.h
create mode 100644 src/lib/dns/rdata/generic/sshfp_44.cc
create mode 100644 src/lib/dns/rdata/generic/sshfp_44.h
create mode 100644 src/lib/dns/tests/.gitignore
create mode 100644 src/lib/dns/tests/rdata_sshfp_unittest.cc
create mode 100644 src/lib/dns/tests/testdata/.gitignore
create mode 100644 src/lib/dns/tests/testdata/rdata_sshfp_fromWire
create mode 100644 src/lib/dns/tests/testdata/rdata_sshfp_fromWire1.spec
create mode 100644 src/lib/dns/tests/testdata/rdata_sshfp_fromWire2
create mode 100644 src/lib/dns/tests/testdata/rdata_sshfp_fromWire2.spec
create mode 100644 src/lib/exceptions/tests/.gitignore
create mode 100644 src/lib/log/compiler/.gitignore
create mode 100644 src/lib/log/tests/.gitignore
create mode 100644 src/lib/log/tests/message_initializer_1_unittest.cc
create mode 100644 src/lib/log/tests/message_initializer_1a_unittest.cc
create mode 100644 src/lib/log/tests/message_initializer_2_unittest.cc
delete mode 100644 src/lib/log/tests/message_initializer_unittest.cc
delete mode 100644 src/lib/log/tests/message_initializer_unittest_2.cc
create mode 100644 src/lib/log/tests/run_initializer_unittests.cc
create mode 100644 src/lib/nsas/.gitignore
create mode 100644 src/lib/nsas/tests/.gitignore
create mode 100644 src/lib/python/.gitignore
create mode 100644 src/lib/python/isc/cc/tests/.gitignore
create mode 100644 src/lib/python/isc/config/tests/.gitignore
create mode 100644 src/lib/python/isc/log/tests/.gitignore
create mode 100644 src/lib/python/isc/log_messages/work/.gitignore
create mode 100644 src/lib/python/isc/notify/tests/.gitignore
create mode 100644 src/lib/resolve/.gitignore
create mode 100644 src/lib/resolve/tests/.gitignore
create mode 100644 src/lib/server_common/.gitignore
create mode 100644 src/lib/server_common/tests/.gitignore
create mode 100644 src/lib/statistics/tests/.gitignore
create mode 100644 src/lib/testutils/testdata/.gitignore
create mode 100644 src/lib/util/python/.gitignore
create mode 100644 src/lib/util/tests/.gitignore
create mode 100644 src/lib/xfr/tests/.gitignore
create mode 100644 tests/lettuce/.gitignore
create mode 100644 tests/system/.gitignore
create mode 100644 tests/system/bindctl/nsx1/.gitignore
create mode 100644 tests/system/glue/.gitignore
create mode 100644 tests/system/glue/nsx1/.gitignore
create mode 100644 tests/system/ixfr/.gitignore
create mode 100644 tests/system/ixfr/in-1/.gitignore
create mode 100644 tests/system/ixfr/in-2/.gitignore
create mode 100644 tests/system/ixfr/in-3/.gitignore
create mode 100644 tests/system/ixfr/in-4/.gitignore
create mode 100644 tests/tools/badpacket/.gitignore
create mode 100644 tests/tools/badpacket/tests/.gitignore
create mode 100644 tests/tools/perfdhcp/.gitignore
-----------------------------------------------------------------------
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..1dfbe9f
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,29 @@
+*.la
+*.lo
+*.o
+.deps/
+.libs/
+__pycache__/
+Makefile
+Makefile.in
+TAGS
+
+/aclocal.m4
+/autom4te.cache/
+/config.guess
+/config.h
+/config.h.in
+/config.log
+/config.report
+/config.status
+/config.sub
+/configure
+/cscope.files
+/cscope.out
+/depcomp
+/install-sh
+/libtool
+/ltmain.sh
+/missing
+/py-compile
+/stamp-h1
diff --git a/ChangeLog b/ChangeLog
index fd23fd0..c56c407 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,67 @@
+401. [func]* jinmei
+ libdns++: updated the internal implementation of the
+ MessageRenderer class. This is mostly a transparent change, but
+ the new version now doesn't allow changing compression mode in the
+ middle of rendering (which shouldn't be an issue in practice).
+ On the other hand, name compression performance was significantly
+ improved: depending on the number of names, micro benchmark tests
+ showed the new version is several times faster than the previous
+ version .
+ (Trac #1603, git 9a2a86f3f47b60ff017ce1a040941d0c145cfe16)
+
+400. [bug] stephen
+ Fix crash on Max OS X 10.7 by altering logging so as not to allocate
+ heap storage in the static initialization of logging objects.
+ (Trac #1698, git a8e53be7039ad50d8587c0972244029ff3533b6e)
+
+399. [func] muks
+ Add support for the SSHFP RR type (RFC 4255).
+ (Trac #1136, git ea5ac57d508a17611cfae9d9ea1c238f59d52c51)
+
+398. [func] jelte
+ The b10-xfrin module now logs more information on successful
+ incoming transfers. In the case of IXFR, it logs the number of
+ changesets, and the total number of added and deleted resource
+ records. For AXFR (or AXFR-style IXFR), it logs the number of
+ resource records. In both cases, the number of overhead DNS
+ messages, runtime, amount of wire data, and transfer speed are logged.
+ (Trac #1280, git 2b01d944b6a137f95d47673ea8367315289c205d)
+
+397. [func] muks
+ The boss process now gives more helpful description when a
+ sub-process exits due to a signal.
+ (Trac #1673, git 1cd0d0e4fc9324bbe7f8593478e2396d06337b1e)
+
+396. [func]* jinmei
+ libdatasrc: change the return type of ZoneFinder::find() so it can
+ contain more context of the search, which can be used for
+ optimizing post find() processing. A new method getAdditional()
+ is added to it for finding additional RRsets based on the result
+ of find(). External behavior shouldn't change. The query
+ handling code of b10-auth now uses the new interface.
+ (Trac #1607, git 2e940ea65d5b9f371c26352afd9e66719c38a6b9)
+
+395. [bug] jelte
+ The log message compiler now errors (resulting in build failures) if
+ duplicate log message identifiers are found in a single message file.
+ Renamed one duplicate that was found (RESOLVER_SHUTDOWN, renamed to
+ RESOLVER_SHUTDOWN_RECEIVED).
+ (Trac #1093, git f537c7e12fb7b25801408f93132ed33410edae76)
+ (Trac #1741, git b8960ab85c717fe70ad282e0052ac0858c5b57f7)
+
+394. [bug] jelte
+ b10-auth now catches any exceptions during response building; if any
+ datasource either throws an exception or causes an exception to be
+ thrown, the message processing code will now catch it, log a debug
+ message, and return a SERVFAIL response.
+ (Trac #1612, git b5740c6b3962a55e46325b3c8b14c9d64cf0d845)
+
+393. [func] jelte
+ Introduced a new class LabelSequence in libdns++, which provides
+ lightweight accessor functionality to the Name class, for more
+ efficient comparison of parts of names.
+ (Trac #1602, git b33929ed5df7c8f482d095e96e667d4a03180c78)
+
392. [func]* jinmei
libdns++: revised the (Abstract)MessageRenderer class so that it
has a default internal buffer and the buffer can be temporarily
diff --git a/compatcheck/.gitignore b/compatcheck/.gitignore
new file mode 100644
index 0000000..180a3ec
--- /dev/null
+++ b/compatcheck/.gitignore
@@ -0,0 +1 @@
+/sqlite3-difftbl-check.py
diff --git a/configure.ac b/configure.ac
index af8c2fe..72a462f 100644
--- a/configure.ac
+++ b/configure.ac
@@ -85,11 +85,6 @@ if test $enable_shared = no; then
AC_MSG_ERROR([BIND 10 requires shared libraries to be built])
fi
-AC_ARG_ENABLE(boost-threads,
-AC_HELP_STRING([--enable-boost-threads],
- [use boost threads. Currently this only means using its locks instead of dummy locks, in the cache and NSAS]),
- use_boost_threads=$enableval, use_boost_threads=no)
-
# allow configuring without setproctitle.
AC_ARG_ENABLE(setproctitle-check,
AC_HELP_STRING([--disable-setproctitle-check],
@@ -680,68 +675,6 @@ AC_CHECK_HEADERS([boost/shared_ptr.hpp boost/foreach.hpp boost/interprocess/sync
CPPFLAGS="$CPPFLAGS_SAVES"
AC_SUBST(BOOST_INCLUDES)
-
-if test "${use_boost_threads}" = "yes" ; then
- AC_DEFINE([USE_BOOST_THREADS], [], [Use boost threads])
-
- # Using boost::mutex can result in requiring libboost_thread with older
- # versions of Boost. We'd like to avoid relying on a compiled Boost library
- # whenever possible, so we need to check for it step by step.
- #
- # NOTE: another fix of this problem is to simply require newer versions of
- # boost. If we choose that solution we should simplify the following tricky
- # checks accordingly and all Makefile.am's that refer to NEED_LIBBOOST_THREAD.
- AC_MSG_CHECKING(for boost::mutex)
- CPPFLAGS_SAVES="$CPPFLAGS"
- LIBS_SAVES="$LIBS"
- CPPFLAGS="$BOOST_INCLUDES $CPPFLAGS $MULTITHREADING_FLAG"
- need_libboost_thread=0
- need_sunpro_workaround=0
- AC_TRY_LINK([
- #include <boost/thread.hpp>
- ],[
- boost::mutex m;
- ],
- [ AC_MSG_RESULT(yes (without libboost_thread)) ],
- # there is one specific problem with SunStudio 5.10
- # where including boost/thread causes a compilation failure
- # There is a workaround in boost but it checks the version not being 5.10
- # This will probably be fixed in the future, in which case this
- # is only a temporary workaround
- [ AC_TRY_LINK([
- #if defined(__SUNPRO_CC) && __SUNPRO_CC == 0x5100
- #undef __SUNPRO_CC
- #define __SUNPRO_CC 0x5090
- #endif
- #include <boost/thread.hpp>
- ],[
- boost::mutex m;
- ],
- [ AC_MSG_RESULT(yes (with SUNOS workaround))
- need_sunpro_workaround=1 ],
- [ LIBS=" $LIBS -lboost_thread"
- AC_TRY_LINK([
- #include <boost/thread.hpp>
- ],[
- boost::mutex m;
- ],
- [ AC_MSG_RESULT(yes (with libboost_thread))
- need_libboost_thread=1 ],
- [ AC_MSG_RESULT(no)
- AC_MSG_ERROR([boost::mutex cannot be linked in this build environment.
- Perhaps you are using an older version of Boost that requires libboost_thread for the mutex support, which does not appear to be available.
- You may want to check the availability of the library or to upgrade Boost.])
- ])])])
- CPPFLAGS="$CPPFLAGS_SAVES"
- LIBS="$LIBS_SAVES"
- AM_CONDITIONAL(NEED_LIBBOOST_THREAD, test $need_libboost_thread = 1)
- if test $need_sunpro_workaround = 1; then
- AC_DEFINE([NEED_SUNPRO_WORKAROUND], [], [Need boost sunstudio workaround])
- fi
-else
- AM_CONDITIONAL(NEED_LIBBOOST_THREAD, test "${use_boost_threads}" = "yes")
-fi
-
# I can't get some of the #include <asio.hpp> right without this
# TODO: find the real cause of asio/boost wanting pthreads
# (this currently only occurs for src/lib/cc/session_unittests)
diff --git a/doc/.gitignore b/doc/.gitignore
new file mode 100644
index 0000000..cf437ce
--- /dev/null
+++ b/doc/.gitignore
@@ -0,0 +1 @@
+/version.ent
diff --git a/doc/guide/bind10-guide.xml b/doc/guide/bind10-guide.xml
index 6b95c3e..6a68f82 100644
--- a/doc/guide/bind10-guide.xml
+++ b/doc/guide/bind10-guide.xml
@@ -1661,7 +1661,7 @@ Xfrout/transfer_acl[0] {"action": "ACCEPT"} any (default)</screen>
> <userinput>config commit</userinput></screen>
<para>Both Xfrout and Auth will use the system wide keyring to check
- TSIGs in the incomming messages and to sign responses.</para>
+ TSIGs in the incoming messages and to sign responses.</para>
<note><simpara>
The way to specify zone specific configuration (ACLs, etc) is
diff --git a/doc/guide/bind10-messages.xml b/doc/guide/bind10-messages.xml
index fecefd0..b0cbb26 100644
--- a/doc/guide/bind10-messages.xml
+++ b/doc/guide/bind10-messages.xml
@@ -598,7 +598,7 @@ needs a dedicated message bus.
</varlistentry>
<varlistentry id="BIND10_COMPONENT_FAILED">
-<term>BIND10_COMPONENT_FAILED component %1 (pid %2) failed with %3 exit status</term>
+<term>BIND10_COMPONENT_FAILED component %1 (pid %2) failed: %3</term>
<listitem><para>
The process terminated, but the bind10 boss didn't expect it to, which means
it must have failed.
diff --git a/src/bin/auth/.gitignore b/src/bin/auth/.gitignore
new file mode 100644
index 0000000..64c3fd7
--- /dev/null
+++ b/src/bin/auth/.gitignore
@@ -0,0 +1,7 @@
+/auth.spec
+/auth.spec.pre
+/auth_messages.cc
+/auth_messages.h
+/b10-auth
+/spec_config.h
+/spec_config.h.pre
diff --git a/src/bin/auth/auth_messages.mes b/src/bin/auth/auth_messages.mes
index 44f8d4b..b18feb1 100644
--- a/src/bin/auth/auth_messages.mes
+++ b/src/bin/auth/auth_messages.mes
@@ -73,6 +73,10 @@ attempt to parse the header of a received DNS packet has failed. (The
reason for the failure is given in the message.) The server will drop the
packet.
+% AUTH_INVALID_STATISTICS_DATA invalid specification of statistics data specified
+An error was encountered when the authoritiative server specified
+statistics data which is invalid for the auth specification file.
+
% AUTH_LOAD_TSIG loading TSIG keys
This is a debug message indicating that the authoritative server
has requested the keyring holding TSIG keys from the configuration
@@ -92,6 +96,18 @@ discovered that the memory data source is disabled for the given class.
This is a debug message reporting that the authoritative server has
discovered that the memory data source is enabled for the given class.
+% AUTH_NOTIFY_QUESTIONS invalid number of questions (%1) in incoming NOTIFY
+This debug message is logged by the authoritative server when it receives
+a NOTIFY packet that contains zero or more than one question. (A valid
+NOTIFY packet contains one question.) The server will return a FORMERR
+error to the sender.
+
+% AUTH_NOTIFY_RRTYPE invalid question RR type (%1) in incoming NOTIFY
+This debug message is logged by the authoritative server when it receives
+a NOTIFY packet that an RR type of something other than SOA in the
+question section. (The RR type received is included in the message.) The
+server will return a FORMERR error to the sender.
+
% AUTH_NO_STATS_SESSION session interface for statistics is not available
The authoritative server had no session with the statistics module at the
time it attempted to send it data: the attempt has been abandoned. This
@@ -102,18 +118,6 @@ This is a debug message produced by the authoritative server when it receives
a NOTIFY packet but the XFRIN process is not running. The packet will be
dropped and nothing returned to the sender.
-% AUTH_NOTIFY_RRTYPE invalid question RR type (%1) in incoming NOTIFY
-This debug message is logged by the authoritative server when it receives
-a NOTIFY packet that an RR type of something other than SOA in the
-question section. (The RR type received is included in the message.) The
-server will return a FORMERR error to the sender.
-
-% AUTH_NOTIFY_QUESTIONS invalid number of questions (%1) in incoming NOTIFY
-This debug message is logged by the authoritative server when it receives
-a NOTIFY packet that contains zero or more than one question. (A valid
-NOTIFY packet contains one question.) The server will return a FORMERR
-error to the sender.
-
% AUTH_PACKET_PARSE_ERROR unable to parse received DNS packet: %1
This is a debug message, generated by the authoritative server when an
attempt to parse a received DNS packet has failed due to something other
@@ -154,6 +158,19 @@ a command from the statistics module to send it data. The 'sendstats'
command is handled differently to other commands, which is why the debug
message associated with it has its own code.
+% AUTH_RESPONSE_FAILURE exception while building response to query: %1
+This is a debug message, generated by the authoritative server when an
+attempt to create a response to a received DNS packet has failed. The
+reason for the failure is given in the log message. A SERVFAIL response
+is sent back. The most likely cause of this is an error in the data
+source implementation; it is either creating bad responses or raising
+exceptions itself.
+
+% AUTH_RESPONSE_FAILURE_UNKNOWN unknown exception while building response to query
+This debug message is similar to AUTH_RESPONSE_FAILURE, but further
+details about the error are unknown, because it was signaled by something
+which is not an exception. This is definitely a bug.
+
% AUTH_RESPONSE_RECEIVED received response message, ignoring
This is a debug message, this is output if the authoritative server
receives a DNS packet with the QR bit set, i.e. a DNS response. The
@@ -260,7 +277,3 @@ This is a debug message output during the processing of a NOTIFY
request. The zone manager component has been informed of the request,
but has returned an error response (which is included in the message). The
NOTIFY request will not be honored.
-
-% AUTH_INVALID_STATISTICS_DATA invalid specification of statistics data specified
-An error was encountered when the authoritiative server specified
-statistics data which is invalid for the auth specification file.
diff --git a/src/bin/auth/auth_srv.cc b/src/bin/auth/auth_srv.cc
index cb74ccc..ff618a7 100644
--- a/src/bin/auth/auth_srv.cc
+++ b/src/bin/auth/auth_srv.cc
@@ -87,14 +87,14 @@ public:
~AuthSrvImpl();
isc::data::ConstElementPtr setDbFile(isc::data::ConstElementPtr config);
- bool processNormalQuery(const IOMessage& io_message, MessagePtr message,
- OutputBufferPtr buffer,
+ bool processNormalQuery(const IOMessage& io_message, Message& message,
+ OutputBuffer& buffer,
auto_ptr<TSIGContext> tsig_context);
- bool processXfrQuery(const IOMessage& io_message, MessagePtr message,
- OutputBufferPtr buffer,
+ bool processXfrQuery(const IOMessage& io_message, Message& message,
+ OutputBuffer& buffer,
auto_ptr<TSIGContext> tsig_context);
- bool processNotify(const IOMessage& io_message, MessagePtr message,
- OutputBufferPtr buffer,
+ bool processNotify(const IOMessage& io_message, Message& message,
+ OutputBuffer& buffer,
auto_ptr<TSIGContext> tsig_context);
IOService io_service_;
@@ -143,7 +143,7 @@ public:
/// \param done If true, the Rcode from the given message is counted,
/// this value is then passed to server->resume(bool)
void resumeServer(isc::asiodns::DNSServer* server,
- isc::dns::MessagePtr message,
+ isc::dns::Message& message,
bool done);
private:
std::string db_file_;
@@ -201,12 +201,11 @@ public:
MessageLookup(AuthSrv* srv) : server_(srv) {}
virtual void operator()(const IOMessage& io_message,
MessagePtr message,
- MessagePtr answer_message,
+ MessagePtr, // Not used here
OutputBufferPtr buffer,
DNSServer* server) const
{
- (void) answer_message;
- server_->processMessage(io_message, message, buffer, server);
+ server_->processMessage(io_message, *message, *buffer, server);
}
private:
AuthSrv* server_;
@@ -267,57 +266,58 @@ AuthSrv::~AuthSrv() {
namespace {
class QuestionInserter {
public:
- QuestionInserter(MessagePtr message) : message_(message) {}
+ QuestionInserter(Message& message) : message_(message) {}
void operator()(const QuestionPtr question) {
- message_->addQuestion(question);
+ message_.addQuestion(question);
}
- MessagePtr message_;
+ Message& message_;
};
void
-makeErrorMessage(MessagePtr message, OutputBufferPtr buffer,
- const Rcode& rcode,
+makeErrorMessage(Message& message, OutputBuffer& buffer,
+ const Rcode& rcode,
std::auto_ptr<TSIGContext> tsig_context =
std::auto_ptr<TSIGContext>())
{
// extract the parameters that should be kept.
// XXX: with the current implementation, it's not easy to set EDNS0
// depending on whether the query had it. So we'll simply omit it.
- const qid_t qid = message->getQid();
- const bool rd = message->getHeaderFlag(Message::HEADERFLAG_RD);
- const bool cd = message->getHeaderFlag(Message::HEADERFLAG_CD);
- const Opcode& opcode = message->getOpcode();
+ const qid_t qid = message.getQid();
+ const bool rd = message.getHeaderFlag(Message::HEADERFLAG_RD);
+ const bool cd = message.getHeaderFlag(Message::HEADERFLAG_CD);
+ const Opcode& opcode = message.getOpcode();
vector<QuestionPtr> questions;
// If this is an error to a query or notify, we should also copy the
// question section.
if (opcode == Opcode::QUERY() || opcode == Opcode::NOTIFY()) {
- questions.assign(message->beginQuestion(), message->endQuestion());
+ questions.assign(message.beginQuestion(), message.endQuestion());
}
- message->clear(Message::RENDER);
- message->setQid(qid);
- message->setOpcode(opcode);
- message->setHeaderFlag(Message::HEADERFLAG_QR);
+ message.clear(Message::RENDER);
+ message.setQid(qid);
+ message.setOpcode(opcode);
+ message.setHeaderFlag(Message::HEADERFLAG_QR);
if (rd) {
- message->setHeaderFlag(Message::HEADERFLAG_RD);
+ message.setHeaderFlag(Message::HEADERFLAG_RD);
}
if (cd) {
- message->setHeaderFlag(Message::HEADERFLAG_CD);
+ message.setHeaderFlag(Message::HEADERFLAG_CD);
}
for_each(questions.begin(), questions.end(), QuestionInserter(message));
- message->setRcode(rcode);
+
+ message.setRcode(rcode);
MessageRenderer renderer;
- renderer.setBuffer(buffer.get());
+ renderer.setBuffer(&buffer);
if (tsig_context.get() != NULL) {
- message->toWire(renderer, *tsig_context);
+ message.toWire(renderer, *tsig_context);
} else {
- message->toWire(renderer);
+ message.toWire(renderer);
}
renderer.setBuffer(NULL);
LOG_DEBUG(auth_logger, DBG_AUTH_MESSAGES, AUTH_SEND_ERROR_RESPONSE)
- .arg(renderer.getLength()).arg(*message);
+ .arg(renderer.getLength()).arg(message);
}
}
@@ -415,18 +415,18 @@ AuthSrv::setStatisticsTimerInterval(uint32_t interval) {
}
void
-AuthSrv::processMessage(const IOMessage& io_message, MessagePtr message,
- OutputBufferPtr buffer, DNSServer* server)
+AuthSrv::processMessage(const IOMessage& io_message, Message& message,
+ OutputBuffer& buffer, DNSServer* server)
{
InputBuffer request_buffer(io_message.getData(), io_message.getDataSize());
// First, check the header part. If we fail even for the base header,
// just drop the message.
try {
- message->parseHeader(request_buffer);
+ message.parseHeader(request_buffer);
// Ignore all responses.
- if (message->getHeaderFlag(Message::HEADERFLAG_QR)) {
+ if (message.getHeaderFlag(Message::HEADERFLAG_QR)) {
LOG_DEBUG(auth_logger, DBG_AUTH_DETAIL, AUTH_RESPONSE_RECEIVED);
impl_->resumeServer(server, message, false);
return;
@@ -440,7 +440,7 @@ AuthSrv::processMessage(const IOMessage& io_message, MessagePtr message,
try {
// Parse the message.
- message->fromWire(request_buffer);
+ message.fromWire(request_buffer);
} catch (const DNSProtocolError& error) {
LOG_DEBUG(auth_logger, DBG_AUTH_DETAIL, AUTH_PACKET_PROTOCOL_ERROR)
.arg(error.getRcode().toText()).arg(error.what());
@@ -456,13 +456,13 @@ AuthSrv::processMessage(const IOMessage& io_message, MessagePtr message,
} // other exceptions will be handled at a higher layer.
LOG_DEBUG(auth_logger, DBG_AUTH_MESSAGES, AUTH_PACKET_RECEIVED)
- .arg(message->toText());
+ .arg(message);
// Perform further protocol-level validation.
// TSIG first
// If this is set to something, we know we need to answer with TSIG as well
std::auto_ptr<TSIGContext> tsig_context;
- const TSIGRecord* tsig_record(message->getTSIGRecord());
+ const TSIGRecord* tsig_record(message.getTSIGRecord());
TSIGError tsig_error(TSIGError::NOERROR());
// Do we do TSIG?
@@ -482,51 +482,59 @@ AuthSrv::processMessage(const IOMessage& io_message, MessagePtr message,
return;
}
- // update per opcode statistics counter. This can only be reliable after
- // TSIG check succeeds.
- impl_->counters_.inc(message->getOpcode());
-
bool send_answer = true;
- if (message->getOpcode() == Opcode::NOTIFY()) {
- send_answer = impl_->processNotify(io_message, message, buffer,
- tsig_context);
- } else if (message->getOpcode() != Opcode::QUERY()) {
- LOG_DEBUG(auth_logger, DBG_AUTH_DETAIL, AUTH_UNSUPPORTED_OPCODE)
- .arg(message->getOpcode().toText());
- makeErrorMessage(message, buffer, Rcode::NOTIMP(), tsig_context);
- } else if (message->getRRCount(Message::SECTION_QUESTION) != 1) {
- makeErrorMessage(message, buffer, Rcode::FORMERR(), tsig_context);
- } else {
- ConstQuestionPtr question = *message->beginQuestion();
- const RRType &qtype = question->getType();
- if (qtype == RRType::AXFR()) {
- send_answer = impl_->processXfrQuery(io_message, message, buffer,
- tsig_context);
- } else if (qtype == RRType::IXFR()) {
- send_answer = impl_->processXfrQuery(io_message, message, buffer,
- tsig_context);
+ try {
+ // update per opcode statistics counter. This can only be reliable
+ // after TSIG check succeeds.
+ impl_->counters_.inc(message.getOpcode());
+
+ if (message.getOpcode() == Opcode::NOTIFY()) {
+ send_answer = impl_->processNotify(io_message, message, buffer,
+ tsig_context);
+ } else if (message.getOpcode() != Opcode::QUERY()) {
+ LOG_DEBUG(auth_logger, DBG_AUTH_DETAIL, AUTH_UNSUPPORTED_OPCODE)
+ .arg(message.getOpcode().toText());
+ makeErrorMessage(message, buffer, Rcode::NOTIMP(), tsig_context);
+ } else if (message.getRRCount(Message::SECTION_QUESTION) != 1) {
+ makeErrorMessage(message, buffer, Rcode::FORMERR(), tsig_context);
} else {
- send_answer = impl_->processNormalQuery(io_message, message,
- buffer, tsig_context);
+ ConstQuestionPtr question = *message.beginQuestion();
+ const RRType &qtype = question->getType();
+ if (qtype == RRType::AXFR()) {
+ send_answer = impl_->processXfrQuery(io_message, message,
+ buffer, tsig_context);
+ } else if (qtype == RRType::IXFR()) {
+ send_answer = impl_->processXfrQuery(io_message, message,
+ buffer, tsig_context);
+ } else {
+ send_answer = impl_->processNormalQuery(io_message, message,
+ buffer, tsig_context);
+ }
}
+ } catch (const std::exception& ex) {
+ LOG_DEBUG(auth_logger, DBG_AUTH_DETAIL, AUTH_RESPONSE_FAILURE)
+ .arg(ex.what());
+ makeErrorMessage(message, buffer, Rcode::SERVFAIL());
+ } catch (...) {
+ LOG_DEBUG(auth_logger, DBG_AUTH_DETAIL, AUTH_RESPONSE_FAILURE_UNKNOWN);
+ makeErrorMessage(message, buffer, Rcode::SERVFAIL());
}
-
impl_->resumeServer(server, message, send_answer);
}
bool
-AuthSrvImpl::processNormalQuery(const IOMessage& io_message, MessagePtr message,
- OutputBufferPtr buffer,
+AuthSrvImpl::processNormalQuery(const IOMessage& io_message, Message& message,
+ OutputBuffer& buffer,
auto_ptr<TSIGContext> tsig_context)
{
- ConstEDNSPtr remote_edns = message->getEDNS();
+ ConstEDNSPtr remote_edns = message.getEDNS();
const bool dnssec_ok = remote_edns && remote_edns->getDNSSECAwareness();
const uint16_t remote_bufsize = remote_edns ? remote_edns->getUDPSize() :
Message::DEFAULT_MAX_UDPSIZE;
- message->makeResponse();
- message->setHeaderFlag(Message::HEADERFLAG_AA);
- message->setRcode(Rcode::NOERROR());
+ message.makeResponse();
+ message.setHeaderFlag(Message::HEADERFLAG_AA);
+ message.setRcode(Rcode::NOERROR());
// Increment query counter.
incCounter(io_message.getSocket().getProtocol());
@@ -535,20 +543,20 @@ AuthSrvImpl::processNormalQuery(const IOMessage& io_message, MessagePtr message,
EDNSPtr local_edns = EDNSPtr(new EDNS());
local_edns->setDNSSECAwareness(dnssec_ok);
local_edns->setUDPSize(AuthSrvImpl::DEFAULT_LOCAL_UDPSIZE);
- message->setEDNS(local_edns);
+ message.setEDNS(local_edns);
}
try {
// If a memory data source is configured call the separate
// Query::process()
- const ConstQuestionPtr question = *message->beginQuestion();
+ const ConstQuestionPtr question = *message.beginQuestion();
if (memory_client_ && memory_client_class_ == question->getClass()) {
const RRType& qtype = question->getType();
const Name& qname = question->getName();
- auth::Query(*memory_client_, qname, qtype, *message,
+ auth::Query(*memory_client_, qname, qtype, message,
dnssec_ok).process();
} else {
- datasrc::Query query(*message, cache_, dnssec_ok);
+ datasrc::Query query(message, cache_, dnssec_ok);
data_sources_.doQuery(query);
}
} catch (const Exception& ex) {
@@ -559,6 +567,7 @@ AuthSrvImpl::processNormalQuery(const IOMessage& io_message, MessagePtr message,
renderer_.clear();
renderer_.setBuffer(buffer.get());
+
const bool udp_buffer =
(io_message.getSocket().getProtocol() == IPPROTO_UDP);
renderer_.setLengthLimit(udp_buffer ? remote_bufsize : 65535);
@@ -569,13 +578,12 @@ AuthSrvImpl::processNormalQuery(const IOMessage& io_message, MessagePtr message,
}
LOG_DEBUG(auth_logger, DBG_AUTH_MESSAGES, AUTH_SEND_NORMAL_RESPONSE)
.arg(renderer_.getLength()).arg(message->toText());
-
return (true);
}
bool
-AuthSrvImpl::processXfrQuery(const IOMessage& io_message, MessagePtr message,
- OutputBufferPtr buffer,
+AuthSrvImpl::processXfrQuery(const IOMessage& io_message, Message& message,
+ OutputBuffer& buffer,
auto_ptr<TSIGContext> tsig_context)
{
// Increment query counter.
@@ -616,19 +624,19 @@ AuthSrvImpl::processXfrQuery(const IOMessage& io_message, MessagePtr message,
}
bool
-AuthSrvImpl::processNotify(const IOMessage& io_message, MessagePtr message,
- OutputBufferPtr buffer,
+AuthSrvImpl::processNotify(const IOMessage& io_message, Message& message,
+ OutputBuffer& buffer,
std::auto_ptr<TSIGContext> tsig_context)
{
// The incoming notify must contain exactly one question for SOA of the
// zone name.
- if (message->getRRCount(Message::SECTION_QUESTION) != 1) {
+ if (message.getRRCount(Message::SECTION_QUESTION) != 1) {
LOG_DEBUG(auth_logger, DBG_AUTH_DETAIL, AUTH_NOTIFY_QUESTIONS)
- .arg(message->getRRCount(Message::SECTION_QUESTION));
+ .arg(message.getRRCount(Message::SECTION_QUESTION));
makeErrorMessage(message, buffer, Rcode::FORMERR(), tsig_context);
return (true);
}
- ConstQuestionPtr question = *message->beginQuestion();
+ ConstQuestionPtr question = *message.beginQuestion();
if (question->getType() != RRType::SOA()) {
LOG_DEBUG(auth_logger, DBG_AUTH_DETAIL, AUTH_NOTIFY_RRTYPE)
.arg(question->getType().toText());
@@ -683,9 +691,9 @@ AuthSrvImpl::processNotify(const IOMessage& io_message, MessagePtr message,
return (false);
}
- message->makeResponse();
- message->setHeaderFlag(Message::HEADERFLAG_AA);
- message->setRcode(Rcode::NOERROR());
+ message.makeResponse();
+ message.setHeaderFlag(Message::HEADERFLAG_AA);
+ message.setRcode(Rcode::NOERROR());
renderer_.clear();
renderer_.setBuffer(buffer.get());
@@ -777,9 +785,9 @@ AuthSrvImpl::setDbFile(ConstElementPtr config) {
}
void
-AuthSrvImpl::resumeServer(DNSServer* server, MessagePtr message, bool done) {
+AuthSrvImpl::resumeServer(DNSServer* server, Message& message, bool done) {
if (done) {
- counters_.inc(message->getRcode());
+ counters_.inc(message.getRcode());
}
server->resume(done);
}
diff --git a/src/bin/auth/auth_srv.h b/src/bin/auth/auth_srv.h
index c1a69f1..02a578d 100644
--- a/src/bin/auth/auth_srv.h
+++ b/src/bin/auth/auth_srv.h
@@ -115,14 +115,14 @@ public:
/// send the reply.
///
/// \param io_message The raw message received
- /// \param message Pointer to the \c Message object
- /// \param buffer Pointer to an \c OutputBuffer for the resposne
+ /// \param message the \c Message object
+ /// \param buffer an \c OutputBuffer for the resposne
/// \param server Pointer to the \c DNSServer
///
/// \throw isc::Unexpected Protocol type of \a message is unexpected
void processMessage(const isc::asiolink::IOMessage& io_message,
- isc::dns::MessagePtr message,
- isc::util::OutputBufferPtr buffer,
+ isc::dns::Message& message,
+ isc::util::OutputBuffer& buffer,
isc::asiodns::DNSServer* server);
/// \brief Updates the data source for the \c AuthSrv object.
diff --git a/src/bin/auth/benchmarks/.gitignore b/src/bin/auth/benchmarks/.gitignore
new file mode 100644
index 0000000..24e9944
--- /dev/null
+++ b/src/bin/auth/benchmarks/.gitignore
@@ -0,0 +1 @@
+/query_bench
diff --git a/src/bin/auth/benchmarks/query_bench.cc b/src/bin/auth/benchmarks/query_bench.cc
index a365085..aa238c0 100644
--- a/src/bin/auth/benchmarks/query_bench.cc
+++ b/src/bin/auth/benchmarks/query_bench.cc
@@ -76,8 +76,8 @@ private:
typedef boost::shared_ptr<const IOEndpoint> IOEndpointPtr;
protected:
QueryBenchMark(const bool enable_cache,
- const BenchQueries& queries, MessagePtr query_message,
- OutputBufferPtr buffer) :
+ const BenchQueries& queries, Message& query_message,
+ OutputBuffer& buffer) :
server_(new AuthSrv(enable_cache, xfrout_client)),
queries_(queries),
query_message_(query_message),
@@ -95,8 +95,8 @@ public:
for (query = queries_.begin(); query != query_end; ++query) {
IOMessage io_message(&(*query)[0], (*query).size(), dummy_socket,
*dummy_endpoint);
- query_message_->clear(Message::PARSE);
- buffer_->clear();
+ query_message_.clear(Message::PARSE);
+ buffer_.clear();
server_->processMessage(io_message, query_message_, buffer_,
&server);
}
@@ -107,8 +107,8 @@ protected:
AuthSrvPtr server_;
private:
const BenchQueries& queries_;
- MessagePtr query_message_;
- OutputBufferPtr buffer_;
+ Message& query_message_;
+ OutputBuffer& buffer_;
IOSocket& dummy_socket;
IOEndpointPtr dummy_endpoint;
};
@@ -118,8 +118,8 @@ public:
Sqlite3QueryBenchMark(const int cache_slots,
const char* const datasrc_file,
const BenchQueries& queries,
- MessagePtr query_message,
- OutputBufferPtr buffer) :
+ Message& query_message,
+ OutputBuffer& buffer) :
QueryBenchMark(cache_slots >= 0 ? true : false, queries,
query_message, buffer)
{
@@ -136,8 +136,8 @@ public:
MemoryQueryBenchMark(const char* const zone_file,
const char* const zone_origin,
const BenchQueries& queries,
- MessagePtr query_message,
- OutputBufferPtr buffer) :
+ Message& query_message,
+ OutputBuffer& buffer) :
QueryBenchMark(false, queries, query_message, buffer)
{
configureAuthServer(*server_,
@@ -255,8 +255,8 @@ main(int argc, char* argv[]) {
BenchQueries queries;
loadQueryData(query_data_file, queries, RRClass::IN());
- OutputBufferPtr buffer(new OutputBuffer(4096));
- MessagePtr message(new Message(Message::PARSE));
+ OutputBuffer buffer(4096);
+ Message message(Message::PARSE);
cout << "Parameters:" << endl;
cout << " Iterations: " << iteration << endl;
diff --git a/src/bin/auth/query.cc b/src/bin/auth/query.cc
index 3474430..7d50393 100644
--- a/src/bin/auth/query.cc
+++ b/src/bin/auth/query.cc
@@ -12,89 +12,89 @@
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
-#include <algorithm> // for std::max
-#include <vector>
-#include <boost/foreach.hpp>
-#include <boost/bind.hpp>
-#include <boost/function.hpp>
-
#include <dns/message.h>
#include <dns/rcode.h>
+#include <dns/rrtype.h>
#include <dns/rdataclass.h>
#include <datasrc/client.h>
#include <auth/query.h>
+#include <boost/foreach.hpp>
+#include <boost/bind.hpp>
+#include <boost/function.hpp>
+
+#include <algorithm> // for std::max
+#include <vector>
+
+using namespace std;
using namespace isc::dns;
using namespace isc::datasrc;
using namespace isc::dns::rdata;
-namespace isc {
-namespace auth {
+// Commonly used helper callback object for vector<ConstRRsetPtr> to
+// insert them to (the specified section of) a message.
+namespace {
+class RRsetInserter {
+public:
+ RRsetInserter(Message& msg, Message::Section section, bool dnssec) :
+ msg_(msg), section_(section), dnssec_(dnssec)
+ {}
+ void operator()(const ConstRRsetPtr& rrset) {
+ msg_.addRRset(section_,
+ boost::const_pointer_cast<AbstractRRset>(rrset),
+ dnssec_);
+ }
-void
-Query::addAdditional(ZoneFinder& zone, const AbstractRRset& rrset) {
- RdataIteratorPtr rdata_iterator(rrset.getRdataIterator());
- for (; !rdata_iterator->isLast(); rdata_iterator->next()) {
- const Rdata& rdata(rdata_iterator->getCurrent());
- if (rrset.getType() == RRType::NS()) {
- // Need to perform the search in the "GLUE OK" mode.
- const generic::NS& ns = dynamic_cast<const generic::NS&>(rdata);
- addAdditionalAddrs(zone, ns.getNSName(), ZoneFinder::FIND_GLUE_OK);
- } else if (rrset.getType() == RRType::MX()) {
- const generic::MX& mx(dynamic_cast<const generic::MX&>(rdata));
- addAdditionalAddrs(zone, mx.getMXName());
- }
+private:
+ Message& msg_;
+ const Message::Section section_;
+ const bool dnssec_;
+};
+
+// This is a "constant" vector storing desired RR types for the additional
+// section. The vector is filled first time it's used.
+const vector<RRType>&
+A_AND_AAAA() {
+ static vector<RRType> needed_types;
+ if (needed_types.empty()) {
+ needed_types.push_back(RRType::A());
+ needed_types.push_back(RRType::AAAA());
}
+ return (needed_types);
}
+// A wrapper for ZoneFinder::Context::getAdditional() so we don't include
+// duplicate RRs. This is not efficient, and we should actually unify
+// this at the end of the process() method. See also #1688.
void
-Query::addAdditionalAddrs(ZoneFinder& zone, const Name& qname,
- const ZoneFinder::FindOptions options)
+getAdditional(const Name& qname, RRType qtype,
+ ZoneFinder::Context& ctx, vector<ConstRRsetPtr>& results)
{
- // Out of zone name
- NameComparisonResult result = zone.getOrigin().compare(qname);
- if ((result.getRelation() != NameComparisonResult::SUPERDOMAIN) &&
- (result.getRelation() != NameComparisonResult::EQUAL))
- return;
-
- // Omit additional data which has already been provided in the answer
- // section from the additional.
- //
- // All the address rrset with the owner name of qname have been inserted
- // into ANSWER section.
- if (qname_ == qname && qtype_ == RRType::ANY())
- return;
-
- // Find A rrset
- if (qname_ != qname || qtype_ != RRType::A()) {
- ZoneFinder::FindResult a_result = zone.find(qname, RRType::A(),
- options | dnssec_opt_);
- if (a_result.code == ZoneFinder::SUCCESS) {
- response_.addRRset(Message::SECTION_ADDITIONAL,
- boost::const_pointer_cast<AbstractRRset>(a_result.rrset), dnssec_);
- }
- }
-
- // Find AAAA rrset
- if (qname_ != qname || qtype_ != RRType::AAAA()) {
- ZoneFinder::FindResult aaaa_result = zone.find(qname, RRType::AAAA(),
- options | dnssec_opt_);
- if (aaaa_result.code == ZoneFinder::SUCCESS) {
- response_.addRRset(Message::SECTION_ADDITIONAL,
- boost::const_pointer_cast<AbstractRRset>(aaaa_result.rrset),
- dnssec_);
+ vector<ConstRRsetPtr> additionals;
+ ctx.getAdditional(A_AND_AAAA(), additionals);
+
+ vector<ConstRRsetPtr>::const_iterator it = additionals.begin();
+ vector<ConstRRsetPtr>::const_iterator it_end = additionals.end();
+ for (; it != it_end; ++it) {
+ if ((qtype == (*it)->getType() || qtype == RRType::ANY()) &&
+ qname == (*it)->getName()) {
+ continue;
}
+ results.push_back(*it);
}
}
+}
+
+namespace isc {
+namespace auth {
void
Query::addSOA(ZoneFinder& finder) {
- ZoneFinder::FindResult soa_result = finder.find(finder.getOrigin(),
- RRType::SOA(),
- dnssec_opt_);
- if (soa_result.code != ZoneFinder::SUCCESS) {
+ ZoneFinderContextPtr soa_ctx = finder.find(finder.getOrigin(),
+ RRType::SOA(), dnssec_opt_);
+ if (soa_ctx->code != ZoneFinder::SUCCESS) {
isc_throw(NoSOA, "There's no SOA record in zone " <<
finder.getOrigin().toText());
} else {
@@ -104,7 +104,7 @@ Query::addSOA(ZoneFinder& finder) {
* to insist.
*/
response_.addRRset(Message::SECTION_AUTHORITY,
- boost::const_pointer_cast<AbstractRRset>(soa_result.rrset), dnssec_);
+ boost::const_pointer_cast<AbstractRRset>(soa_ctx->rrset), dnssec_);
}
}
@@ -148,10 +148,10 @@ Query::addNXDOMAINProofByNSEC(ZoneFinder& finder, ConstRRsetPtr nsec) {
// Confirm the wildcard doesn't exist (this should result in NXDOMAIN;
// otherwise we shouldn't have got NXDOMAIN for the original query in
// the first place).
- const ZoneFinder::FindResult fresult =
+ ConstZoneFinderContextPtr fcontext =
finder.find(wildname, RRType::NSEC(), dnssec_opt_);
- if (fresult.code != ZoneFinder::NXDOMAIN || !fresult.rrset ||
- fresult.rrset->getRdataCount() == 0) {
+ if (fcontext->code != ZoneFinder::NXDOMAIN || !fcontext->rrset ||
+ fcontext->rrset->getRdataCount() == 0) {
isc_throw(BadNSEC, "Unexpected result for wildcard NXDOMAIN proof");
}
@@ -160,9 +160,9 @@ Query::addNXDOMAINProofByNSEC(ZoneFinder& finder, ConstRRsetPtr nsec) {
// Note: name comparison is relatively expensive. When we are at the
// stage of performance optimization, we should consider optimizing this
// for some optimized data source implementations.
- if (nsec->getName() != fresult.rrset->getName()) {
+ if (nsec->getName() != fcontext->rrset->getName()) {
response_.addRRset(Message::SECTION_AUTHORITY,
- boost::const_pointer_cast<AbstractRRset>(fresult.rrset),
+ boost::const_pointer_cast<AbstractRRset>(fcontext->rrset),
dnssec_);
}
}
@@ -230,27 +230,27 @@ Query::addNXDOMAINProofByNSEC3(ZoneFinder& finder) {
void
Query::addWildcardProof(ZoneFinder& finder,
- const ZoneFinder::FindResult& db_result)
+ const ZoneFinder::Context& db_context)
{
- if (db_result.isNSECSigned()) {
+ if (db_context.isNSECSigned()) {
// Case for RFC4035 Section 3.1.3.3.
//
// The query name shouldn't exist in the zone if there were no wildcard
// substitution. Confirm that by specifying NO_WILDCARD. It should
// result in NXDOMAIN and an NSEC RR that proves it should be returned.
- const ZoneFinder::FindResult fresult =
+ ConstZoneFinderContextPtr fcontext =
finder.find(qname_, RRType::NSEC(),
dnssec_opt_ | ZoneFinder::NO_WILDCARD);
- if (fresult.code != ZoneFinder::NXDOMAIN || !fresult.rrset ||
- fresult.rrset->getRdataCount() == 0) {
+ if (fcontext->code != ZoneFinder::NXDOMAIN || !fcontext->rrset ||
+ fcontext->rrset->getRdataCount() == 0) {
isc_throw(BadNSEC,
"Unexpected NSEC result for wildcard proof");
}
response_.addRRset(Message::SECTION_AUTHORITY,
boost::const_pointer_cast<AbstractRRset>(
- fresult.rrset),
+ fcontext->rrset),
dnssec_);
- } else if (db_result.isNSEC3Signed()) {
+ } else if (db_context.isNSEC3Signed()) {
// Case for RFC 5155 Section 7.2.6.
//
// Note that the closest encloser must be the immediate ancestor
@@ -269,36 +269,36 @@ Query::addWildcardNXRRSETProof(ZoneFinder& finder, ConstRRsetPtr nsec) {
isc_throw(BadNSEC, "NSEC for WILDCARD_NXRRSET is empty");
}
- const ZoneFinder::FindResult fresult =
+ ConstZoneFinderContextPtr fcontext =
finder.find(qname_, RRType::NSEC(),
dnssec_opt_ | ZoneFinder::NO_WILDCARD);
- if (fresult.code != ZoneFinder::NXDOMAIN || !fresult.rrset ||
- fresult.rrset->getRdataCount() == 0) {
+ if (fcontext->code != ZoneFinder::NXDOMAIN || !fcontext->rrset ||
+ fcontext->rrset->getRdataCount() == 0) {
isc_throw(BadNSEC, "Unexpected result for no match QNAME proof");
}
- if (nsec->getName() != fresult.rrset->getName()) {
+ if (nsec->getName() != fcontext->rrset->getName()) {
// one NSEC RR proves wildcard_nxrrset that no matched QNAME.
response_.addRRset(Message::SECTION_AUTHORITY,
- boost::const_pointer_cast<AbstractRRset>(fresult.rrset),
+ boost::const_pointer_cast<AbstractRRset>(fcontext->rrset),
dnssec_);
}
}
void
Query::addDS(ZoneFinder& finder, const Name& dname) {
- ZoneFinder::FindResult ds_result =
+ ConstZoneFinderContextPtr ds_context =
finder.find(dname, RRType::DS(), dnssec_opt_);
- if (ds_result.code == ZoneFinder::SUCCESS) {
+ if (ds_context->code == ZoneFinder::SUCCESS) {
response_.addRRset(Message::SECTION_AUTHORITY,
boost::const_pointer_cast<AbstractRRset>(
- ds_result.rrset),
+ ds_context->rrset),
dnssec_);
- } else if (ds_result.code == ZoneFinder::NXRRSET &&
- ds_result.isNSECSigned()) {
- addNXRRsetProof(finder, ds_result);
- } else if (ds_result.code == ZoneFinder::NXRRSET &&
- ds_result.isNSEC3Signed()) {
+ } else if (ds_context->code == ZoneFinder::NXRRSET &&
+ ds_context->isNSECSigned()) {
+ addNXRRsetProof(finder, *ds_context);
+ } else if (ds_context->code == ZoneFinder::NXRRSET &&
+ ds_context->isNSEC3Signed()) {
// Add no DS proof with NSEC3 as specified in RFC 5155 Section 7.2.7.
addClosestEncloserProof(finder, dname, true);
} else {
@@ -309,17 +309,17 @@ Query::addDS(ZoneFinder& finder, const Name& dname) {
void
Query::addNXRRsetProof(ZoneFinder& finder,
- const ZoneFinder::FindResult& db_result)
+ const ZoneFinder::Context& db_context)
{
- if (db_result.isNSECSigned() && db_result.rrset) {
+ if (db_context.isNSECSigned() && db_context.rrset) {
response_.addRRset(Message::SECTION_AUTHORITY,
boost::const_pointer_cast<AbstractRRset>(
- db_result.rrset),
+ db_context.rrset),
dnssec_);
- if (db_result.isWildcard()) {
- addWildcardNXRRSETProof(finder, db_result.rrset);
+ if (db_context.isWildcard()) {
+ addWildcardNXRRSETProof(finder, db_context.rrset);
}
- } else if (db_result.isNSEC3Signed() && !db_result.isWildcard()) {
+ } else if (db_context.isNSEC3Signed() && !db_context.isWildcard()) {
if (qtype_ == RRType::DS()) {
// RFC 5155, Section 7.2.4. Add either NSEC3 for the qname or
// closest (provable) encloser proof in case of optout.
@@ -328,7 +328,7 @@ Query::addNXRRsetProof(ZoneFinder& finder,
// RFC 5155, Section 7.2.3. Just add NSEC3 for the qname.
addNSEC3ForName(finder, qname_, true);
}
- } else if (db_result.isNSEC3Signed() && db_result.isWildcard()) {
+ } else if (db_context.isNSEC3Signed() && db_context.isWildcard()) {
// Case for RFC 5155 Section 7.2.5: add closest encloser proof for the
// qname, construct the matched wildcard name and add NSEC3 for it.
const uint8_t closest_labels =
@@ -340,21 +340,24 @@ Query::addNXRRsetProof(ZoneFinder& finder,
}
void
-Query::addAuthAdditional(ZoneFinder& finder) {
+Query::addAuthAdditional(ZoneFinder& finder,
+ vector<ConstRRsetPtr>& additionals)
+{
+ const Name& origin = finder.getOrigin();
+
// Fill in authority and addtional sections.
- ZoneFinder::FindResult ns_result =
- finder.find(finder.getOrigin(), RRType::NS(), dnssec_opt_);
+ ConstZoneFinderContextPtr ns_context = finder.find(origin, RRType::NS(),
+ dnssec_opt_);
// zone origin name should have NS records
- if (ns_result.code != ZoneFinder::SUCCESS) {
+ if (ns_context->code != ZoneFinder::SUCCESS) {
isc_throw(NoApexNS, "There's no apex NS records in zone " <<
- finder.getOrigin().toText());
- } else {
- response_.addRRset(Message::SECTION_AUTHORITY,
- boost::const_pointer_cast<AbstractRRset>(ns_result.rrset), dnssec_);
- // Handle additional for authority section
- addAdditional(finder, *ns_result.rrset);
+ finder.getOrigin().toText());
}
+ response_.addRRset(Message::SECTION_AUTHORITY,
+ boost::const_pointer_cast<AbstractRRset>(
+ ns_context->rrset), dnssec_);
+ getAdditional(qname_, qtype_, *ns_context, additionals);
}
namespace {
@@ -403,8 +406,9 @@ Query::process() {
// indirectly via delegation). Look into the zone.
response_.setHeaderFlag(Message::HEADERFLAG_AA);
response_.setRcode(Rcode::NOERROR());
- std::vector<ConstRRsetPtr> target;
- boost::function0<ZoneFinder::FindResult> find;
+ vector<ConstRRsetPtr> target;
+ vector<ConstRRsetPtr> additionals;
+ boost::function0<ZoneFinderContextPtr> find;
const bool qtype_is_any = (qtype_ == RRType::ANY());
if (qtype_is_any) {
find = boost::bind(&ZoneFinder::findAll, &zfinder, qname_,
@@ -413,12 +417,12 @@ Query::process() {
find = boost::bind(&ZoneFinder::find, &zfinder, qname_, qtype_,
dnssec_opt_);
}
- ZoneFinder::FindResult db_result(find());
- switch (db_result.code) {
+ ZoneFinderContextPtr db_context(find());
+ switch (db_context->code) {
case ZoneFinder::DNAME: {
// First, put the dname into the answer
response_.addRRset(Message::SECTION_ANSWER,
- boost::const_pointer_cast<AbstractRRset>(db_result.rrset),
+ boost::const_pointer_cast<AbstractRRset>(db_context->rrset),
dnssec_);
/*
* Empty DNAME should never get in, as it is impossible to
@@ -426,14 +430,14 @@ Query::process() {
*
* FIXME: Other way to prevent this should be done
*/
- assert(db_result.rrset->getRdataCount() > 0);
+ assert(db_context->rrset->getRdataCount() > 0);
// Get the data of DNAME
const rdata::generic::DNAME& dname(
dynamic_cast<const rdata::generic::DNAME&>(
- db_result.rrset->getRdataIterator()->getCurrent()));
+ db_context->rrset->getRdataIterator()->getCurrent()));
// The yet unmatched prefix dname
const Name prefix(qname_.split(0, qname_.getLabelCount() -
- db_result.rrset->getName().getLabelCount()));
+ db_context->rrset->getName().getLabelCount()));
// If we put it together, will it be too long?
// (The prefix contains trailing ., which will be removed
if (prefix.getLength() - Name::ROOT_NAME().getLength() +
@@ -448,12 +452,12 @@ Query::process() {
// The new CNAME we are creating (it will be unsigned even
// with DNSSEC, the DNAME is signed and it can be validated
// by that)
- RRsetPtr cname(new RRset(qname_, db_result.rrset->getClass(),
- RRType::CNAME(), db_result.rrset->getTTL()));
+ RRsetPtr cname(new RRset(qname_, db_context->rrset->getClass(),
+ RRType::CNAME(), db_context->rrset->getTTL()));
// Construct the new target by replacing the end
cname->addRdata(rdata::generic::CNAME(qname_.split(0,
qname_.getLabelCount() -
- db_result.rrset->getName().getLabelCount()).
+ db_context->rrset->getName().getLabelCount()).
concatenate(dname.getDname())));
response_.addRRset(Message::SECTION_ANSWER, cname, dnssec_);
break;
@@ -469,13 +473,13 @@ Query::process() {
* So, just put it there.
*/
response_.addRRset(Message::SECTION_ANSWER,
- boost::const_pointer_cast<AbstractRRset>(db_result.rrset),
+ boost::const_pointer_cast<AbstractRRset>(db_context->rrset),
dnssec_);
// If the answer is a result of wildcard substitution,
// add a proof that there's no closer name.
- if (dnssec_ && db_result.isWildcard()) {
- addWildcardProof(*result.zone_finder,db_result);
+ if (dnssec_ && db_context->isWildcard()) {
+ addWildcardProof(*result.zone_finder, *db_context);
}
break;
case ZoneFinder::SUCCESS:
@@ -485,31 +489,33 @@ Query::process() {
BOOST_FOREACH(ConstRRsetPtr rrset, target) {
response_.addRRset(Message::SECTION_ANSWER,
boost::const_pointer_cast<AbstractRRset>(rrset), dnssec_);
- // Handle additional for answer section
- addAdditional(*result.zone_finder, *rrset.get());
}
} else {
response_.addRRset(Message::SECTION_ANSWER,
- boost::const_pointer_cast<AbstractRRset>(db_result.rrset),
+ boost::const_pointer_cast<AbstractRRset>(db_context->rrset),
dnssec_);
- // Handle additional for answer section
- addAdditional(*result.zone_finder, *db_result.rrset);
}
+
+ // Retrieve additional records for the answer
+ getAdditional(qname_, qtype_, *db_context, additionals);
+
// If apex NS records haven't been provided in the answer
// section, insert apex NS records into the authority section
// and AAAA/A RRS of each of the NS RDATA into the additional
// section.
- if (qname_ != result.zone_finder->getOrigin() ||
- db_result.code != ZoneFinder::SUCCESS ||
+ // Checking the findZone() is a lightweight check to see if
+ // qname is the zone origin.
+ if (result.code != result::SUCCESS ||
+ db_context->code != ZoneFinder::SUCCESS ||
(qtype_ != RRType::NS() && !qtype_is_any))
{
- addAuthAdditional(*result.zone_finder);
+ addAuthAdditional(*result.zone_finder, additionals);
}
// If the answer is a result of wildcard substitution,
// add a proof that there's no closer name.
- if (dnssec_ && db_result.isWildcard()) {
- addWildcardProof(*result.zone_finder,db_result);
+ if (dnssec_ && db_context->isWildcard()) {
+ addWildcardProof(*result.zone_finder, *db_context);
}
break;
case ZoneFinder::DELEGATION:
@@ -523,22 +529,24 @@ Query::process() {
response_.setHeaderFlag(Message::HEADERFLAG_AA, false);
response_.addRRset(Message::SECTION_AUTHORITY,
- boost::const_pointer_cast<AbstractRRset>(db_result.rrset),
+ boost::const_pointer_cast<AbstractRRset>(db_context->rrset),
dnssec_);
+ // Retrieve additional records for the name servers
+ db_context->getAdditional(A_AND_AAAA(), additionals);
+
// If DNSSEC is requested, see whether there is a DS
// record for this delegation.
if (dnssec_) {
- addDS(*result.zone_finder, db_result.rrset->getName());
+ addDS(*result.zone_finder, db_context->rrset->getName());
}
- addAdditional(*result.zone_finder, *db_result.rrset);
break;
case ZoneFinder::NXDOMAIN:
response_.setRcode(Rcode::NXDOMAIN());
addSOA(*result.zone_finder);
if (dnssec_) {
- if (db_result.isNSECSigned() && db_result.rrset) {
- addNXDOMAINProofByNSEC(zfinder, db_result.rrset);
- } else if (db_result.isNSEC3Signed()) {
+ if (db_context->isNSECSigned() && db_context->rrset) {
+ addNXDOMAINProofByNSEC(zfinder, db_context->rrset);
+ } else if (db_context->isNSEC3Signed()) {
addNXDOMAINProofByNSEC3(zfinder);
}
}
@@ -546,7 +554,7 @@ Query::process() {
case ZoneFinder::NXRRSET:
addSOA(*result.zone_finder);
if (dnssec_) {
- addNXRRsetProof(zfinder, db_result);
+ addNXRRsetProof(zfinder, *db_context);
}
break;
default:
@@ -556,6 +564,10 @@ Query::process() {
isc_throw(isc::NotImplemented, "Unknown result code");
break;
}
+
+ for_each(additionals.begin(), additionals.end(),
+ RRsetInserter(response_, Message::SECTION_ADDITIONAL,
+ dnssec_));
}
bool
@@ -579,11 +591,11 @@ Query::processDSAtChild() {
response_.setHeaderFlag(Message::HEADERFLAG_AA);
response_.setRcode(Rcode::NOERROR());
addSOA(*zresult.zone_finder);
- const ZoneFinder::FindResult ds_result =
+ ConstZoneFinderContextPtr ds_context =
zresult.zone_finder->find(qname_, RRType::DS(), dnssec_opt_);
- if (ds_result.code == ZoneFinder::NXRRSET) {
+ if (ds_context->code == ZoneFinder::NXRRSET) {
if (dnssec_) {
- addNXRRsetProof(*zresult.zone_finder, ds_result);
+ addNXRRsetProof(*zresult.zone_finder, *ds_context);
}
}
diff --git a/src/bin/auth/query.h b/src/bin/auth/query.h
index e8d3ba8..f8a8f02 100644
--- a/src/bin/auth/query.h
+++ b/src/bin/auth/query.h
@@ -15,8 +15,11 @@
*/
#include <exceptions/exceptions.h>
+#include <dns/rrset.h>
#include <datasrc/zone.h>
+#include <vector>
+
namespace isc {
namespace dns {
class Message;
@@ -96,7 +99,7 @@ private:
/// data
/// \param db_result The ZoneFinder::FindResult returned by find()
void addNXRRsetProof(isc::datasrc::ZoneFinder& finder,
- const isc::datasrc::ZoneFinder::FindResult& db_result);
+ const isc::datasrc::ZoneFinder::Context& db_context);
/// Add NSEC RRs that prove an NXDOMAIN result.
///
@@ -115,7 +118,7 @@ private:
/// of RFC5155.
void addWildcardProof(
isc::datasrc::ZoneFinder& finder,
- const isc::datasrc::ZoneFinder::FindResult& dbResult);
+ const isc::datasrc::ZoneFinder::Context& db_context);
/// \brief Adds one NSEC RR proved no matched QNAME,one NSEC RR proved no
/// matched <QNAME,QTYPE> through wildcard extension.
@@ -129,44 +132,6 @@ private:
void addWildcardNXRRSETProof(isc::datasrc::ZoneFinder& finder,
isc::dns::ConstRRsetPtr nsec);
- /// \brief Look up additional data (i.e., address records for the names
- /// included in NS or MX records) and add them to the additional section.
- ///
- /// Note: Any additional data which has already been provided in the
- /// answer section (i.e., if the original query happend to be for the
- /// address of the DNS server), it should be omitted from the additional.
- ///
- /// This method may throw a exception because its underlying methods may
- /// throw exceptions.
- ///
- /// \param zone The ZoneFinder through which the additional data for the
- /// query is to be found.
- /// \param rrset The RRset (i.e., NS or MX rrset) which require additional
- /// processing.
- void addAdditional(isc::datasrc::ZoneFinder& zone,
- const isc::dns::AbstractRRset& rrset);
-
- /// \brief Find address records for a specified name.
- ///
- /// Search the specified zone for AAAA/A RRs of each of the NS/MX RDATA
- /// (domain name), and insert the found ones into the additional section
- /// if address records are available. By default the search will stop
- /// once it encounters a zone cut.
- ///
- /// Note: we need to perform the search in the "GLUE OK" mode for NS RDATA,
- /// which means that we should include A/AAAA RRs under a zone cut.
- /// The glue records must exactly match the name in the NS RDATA, without
- /// CNAME or wildcard processing.
- ///
- /// \param zone The \c ZoneFinder through which the address records is to
- /// be found.
- /// \param qname The name in rrset RDATA.
- /// \param options The search options.
- void addAdditionalAddrs(isc::datasrc::ZoneFinder& zone,
- const isc::dns::Name& qname,
- const isc::datasrc::ZoneFinder::FindOptions options
- = isc::datasrc::ZoneFinder::FIND_DEFAULT);
-
/// \brief Look up a zone's NS RRset and their address records for an
/// authoritative answer, and add them to the additional section.
///
@@ -185,7 +150,8 @@ private:
///
/// \param finder The \c ZoneFinder through which the NS and additional
/// data for the query are to be found.
- void addAuthAdditional(isc::datasrc::ZoneFinder& finder);
+ void addAuthAdditional(isc::datasrc::ZoneFinder& finder,
+ std::vector<isc::dns::ConstRRsetPtr>& additionals);
/// \brief Process a DS query possible at the child side of zone cut.
///
diff --git a/src/bin/auth/tests/.gitignore b/src/bin/auth/tests/.gitignore
new file mode 100644
index 0000000..d6d1ec8
--- /dev/null
+++ b/src/bin/auth/tests/.gitignore
@@ -0,0 +1 @@
+/run_unittests
diff --git a/src/bin/auth/tests/auth_srv_unittest.cc b/src/bin/auth/tests/auth_srv_unittest.cc
index 3fc69bf..0790a87 100644
--- a/src/bin/auth/tests/auth_srv_unittest.cc
+++ b/src/bin/auth/tests/auth_srv_unittest.cc
@@ -87,8 +87,12 @@ protected:
server.setXfrinSession(¬ify_session);
server.setStatisticsSession(&statistics_session);
}
+
virtual void processMessage() {
- server.processMessage(*io_message, parse_message, response_obuffer,
+ // If processMessage has been called before, parse_message needs
+ // to be reset. If it hasn't, there's no harm in doing so
+ parse_message->clear(Message::PARSE);
+ server.processMessage(*io_message, *parse_message, *response_obuffer,
&dnsserv);
}
@@ -121,6 +125,19 @@ protected:
}
}
+<<<<<<< HEAD
+=======
+ // Convenience method for tests that expect to return SERVFAIL
+ // It calls processMessage, checks if there is an answer, and
+ // check the header for default SERVFAIL data
+ void processAndCheckSERVFAIL() {
+ processMessage();
+ EXPECT_TRUE(dnsserv.hasAnswer());
+ headerCheck(*parse_message, default_qid, Rcode::SERVFAIL(),
+ opcode.getCode(), QR_FLAG, 1, 0, 0, 0);
+ }
+
+>>>>>>> master
IOService ios_;
DNSService dnss_;
MockSession statistics_session;
@@ -174,7 +191,7 @@ TEST_F(AuthSrvTest, builtInQuery) {
default_qid, Name("version.bind"),
RRClass::CH(), RRType::TXT());
createRequestPacket(request_message, IPPROTO_UDP);
- server.processMessage(*io_message, parse_message, response_obuffer,
+ server.processMessage(*io_message, *parse_message, *response_obuffer,
&dnsserv);
createBuiltinVersionResponse(default_qid, response_data);
EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
@@ -286,7 +303,8 @@ TEST_F(AuthSrvTest, AXFRSuccess) {
createRequestPacket(request_message, IPPROTO_TCP);
// On success, the AXFR query has been passed to a separate process,
// so we shouldn't have to respond.
- server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
+ server.processMessage(*io_message, *parse_message, *response_obuffer,
+ &dnsserv);
EXPECT_FALSE(dnsserv.hasAnswer());
EXPECT_TRUE(xfrout.isConnected());
checkAllRcodeCountersZero();
@@ -307,7 +325,7 @@ TEST_F(AuthSrvTest, TSIGSigned) {
boost::shared_ptr<TSIGKeyRing> keyring(new TSIGKeyRing);
keyring->add(key);
server.setTSIGKeyRing(&keyring);
- server.processMessage(*io_message, parse_message, response_obuffer,
+ server.processMessage(*io_message, *parse_message, *response_obuffer,
&dnsserv);
// What did we get?
@@ -342,7 +360,7 @@ TEST_F(AuthSrvTest, TSIGSignedBadKey) {
// Process the message, but use a different key there
boost::shared_ptr<TSIGKeyRing> keyring(new TSIGKeyRing);
server.setTSIGKeyRing(&keyring);
- server.processMessage(*io_message, parse_message, response_obuffer,
+ server.processMessage(*io_message, *parse_message, *response_obuffer,
&dnsserv);
EXPECT_TRUE(dnsserv.hasAnswer());
@@ -377,7 +395,7 @@ TEST_F(AuthSrvTest, TSIGBadSig) {
boost::shared_ptr<TSIGKeyRing> keyring(new TSIGKeyRing);
keyring->add(TSIGKey("key:QkFECg==:hmac-sha1"));
server.setTSIGKeyRing(&keyring);
- server.processMessage(*io_message, parse_message, response_obuffer,
+ server.processMessage(*io_message, *parse_message, *response_obuffer,
&dnsserv);
EXPECT_TRUE(dnsserv.hasAnswer());
@@ -415,7 +433,7 @@ TEST_F(AuthSrvTest, TSIGCheckFirst) {
boost::shared_ptr<TSIGKeyRing> keyring(new TSIGKeyRing);
keyring->add(TSIGKey("key:QkFECg==:hmac-sha1"));
server.setTSIGKeyRing(&keyring);
- server.processMessage(*io_message, parse_message, response_obuffer,
+ server.processMessage(*io_message, *parse_message, *response_obuffer,
&dnsserv);
EXPECT_TRUE(dnsserv.hasAnswer());
@@ -446,7 +464,8 @@ TEST_F(AuthSrvTest, AXFRConnectFail) {
Name("example.com"), RRClass::IN(),
RRType::AXFR());
createRequestPacket(request_message, IPPROTO_TCP);
- server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
+ server.processMessage(*io_message, *parse_message, *response_obuffer,
+ &dnsserv);
EXPECT_TRUE(dnsserv.hasAnswer());
headerCheck(*parse_message, default_qid, Rcode::SERVFAIL(),
opcode.getCode(), QR_FLAG, 1, 0, 0, 0);
@@ -460,7 +479,8 @@ TEST_F(AuthSrvTest, AXFRSendFail) {
Name("example.com"), RRClass::IN(),
RRType::AXFR());
createRequestPacket(request_message, IPPROTO_TCP);
- server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
+ server.processMessage(*io_message, *parse_message, *response_obuffer,
+ &dnsserv);
EXPECT_TRUE(xfrout.isConnected());
xfrout.disableSend();
@@ -470,7 +490,8 @@ TEST_F(AuthSrvTest, AXFRSendFail) {
Name("example.com"), RRClass::IN(),
RRType::AXFR());
createRequestPacket(request_message, IPPROTO_TCP);
- server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
+ server.processMessage(*io_message, *parse_message, *response_obuffer,
+ &dnsserv);
EXPECT_TRUE(dnsserv.hasAnswer());
headerCheck(*parse_message, default_qid, Rcode::SERVFAIL(),
opcode.getCode(), QR_FLAG, 1, 0, 0, 0);
@@ -480,17 +501,17 @@ TEST_F(AuthSrvTest, AXFRSendFail) {
}
TEST_F(AuthSrvTest, AXFRDisconnectFail) {
- // In our usage disconnect() shouldn't fail. So we'll see the exception
- // should it be thrown.
+ // In our usage disconnect() shouldn't fail. But even if it does,
+ // it should not disrupt service (so processMessage should have caught it)
xfrout.disableSend();
xfrout.disableDisconnect();
UnitTestUtil::createRequestMessage(request_message, opcode, default_qid,
Name("example.com"), RRClass::IN(),
RRType::AXFR());
createRequestPacket(request_message, IPPROTO_TCP);
- EXPECT_THROW(server.processMessage(*io_message, parse_message,
- response_obuffer, &dnsserv),
- XfroutError);
+ EXPECT_NO_THROW(server.processMessage(*io_message, *parse_message,
+ *response_obuffer, &dnsserv));
+ // Since the disconnect failed, we should still be 'connected'
EXPECT_TRUE(xfrout.isConnected());
// XXX: we need to re-enable disconnect. otherwise an exception would be
// thrown via the destructor of the server.
@@ -504,7 +525,8 @@ TEST_F(AuthSrvTest, IXFRConnectFail) {
Name("example.com"), RRClass::IN(),
RRType::IXFR());
createRequestPacket(request_message, IPPROTO_TCP);
- server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
+ server.processMessage(*io_message, *parse_message, *response_obuffer,
+ &dnsserv);
EXPECT_TRUE(dnsserv.hasAnswer());
headerCheck(*parse_message, default_qid, Rcode::SERVFAIL(),
opcode.getCode(), QR_FLAG, 1, 0, 0, 0);
@@ -518,7 +540,8 @@ TEST_F(AuthSrvTest, IXFRSendFail) {
Name("example.com"), RRClass::IN(),
RRType::IXFR());
createRequestPacket(request_message, IPPROTO_TCP);
- server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
+ server.processMessage(*io_message, *parse_message, *response_obuffer,
+ &dnsserv);
EXPECT_TRUE(xfrout.isConnected());
xfrout.disableSend();
@@ -528,7 +551,8 @@ TEST_F(AuthSrvTest, IXFRSendFail) {
Name("example.com"), RRClass::IN(),
RRType::IXFR());
createRequestPacket(request_message, IPPROTO_TCP);
- server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
+ server.processMessage(*io_message, *parse_message, *response_obuffer,
+ &dnsserv);
EXPECT_TRUE(dnsserv.hasAnswer());
headerCheck(*parse_message, default_qid, Rcode::SERVFAIL(),
opcode.getCode(), QR_FLAG, 1, 0, 0, 0);
@@ -538,17 +562,16 @@ TEST_F(AuthSrvTest, IXFRSendFail) {
}
TEST_F(AuthSrvTest, IXFRDisconnectFail) {
- // In our usage disconnect() shouldn't fail. So we'll see the exception
- // should it be thrown.
+ // In our usage disconnect() shouldn't fail, but even if it does,
+ // procesMessage() should catch it.
xfrout.disableSend();
xfrout.disableDisconnect();
UnitTestUtil::createRequestMessage(request_message, opcode, default_qid,
Name("example.com"), RRClass::IN(),
RRType::IXFR());
createRequestPacket(request_message, IPPROTO_TCP);
- EXPECT_THROW(server.processMessage(*io_message, parse_message,
- response_obuffer, &dnsserv),
- XfroutError);
+ EXPECT_NO_THROW(server.processMessage(*io_message, *parse_message,
+ *response_obuffer, &dnsserv));
EXPECT_TRUE(xfrout.isConnected());
// XXX: we need to re-enable disconnect. otherwise an exception would be
// thrown via the destructor of the server.
@@ -561,7 +584,8 @@ TEST_F(AuthSrvTest, notify) {
RRClass::IN(), RRType::SOA());
request_message.setHeaderFlag(Message::HEADERFLAG_AA);
createRequestPacket(request_message, IPPROTO_UDP);
- server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
+ server.processMessage(*io_message, *parse_message, *response_obuffer,
+ &dnsserv);
EXPECT_TRUE(dnsserv.hasAnswer());
// An internal command message should have been created and sent to an
@@ -596,7 +620,8 @@ TEST_F(AuthSrvTest, notifyForCHClass) {
RRClass::CH(), RRType::SOA());
request_message.setHeaderFlag(Message::HEADERFLAG_AA);
createRequestPacket(request_message, IPPROTO_UDP);
- server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
+ server.processMessage(*io_message, *parse_message, *response_obuffer,
+ &dnsserv);
EXPECT_TRUE(dnsserv.hasAnswer());
// Other conditions should be the same, so simply confirm the RR class is
@@ -614,7 +639,8 @@ TEST_F(AuthSrvTest, notifyEmptyQuestion) {
request_message.setQid(default_qid);
request_message.toWire(request_renderer);
createRequestPacket(request_message, IPPROTO_UDP);
- server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
+ server.processMessage(*io_message, *parse_message, *response_obuffer,
+ &dnsserv);
EXPECT_TRUE(dnsserv.hasAnswer());
headerCheck(*parse_message, default_qid, Rcode::FORMERR(),
Opcode::NOTIFY().getCode(), QR_FLAG, 0, 0, 0, 0);
@@ -629,7 +655,8 @@ TEST_F(AuthSrvTest, notifyMultiQuestions) {
RRType::SOA()));
request_message.setHeaderFlag(Message::HEADERFLAG_AA);
createRequestPacket(request_message, IPPROTO_UDP);
- server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
+ server.processMessage(*io_message, *parse_message, *response_obuffer,
+ &dnsserv);
EXPECT_TRUE(dnsserv.hasAnswer());
headerCheck(*parse_message, default_qid, Rcode::FORMERR(),
Opcode::NOTIFY().getCode(), QR_FLAG, 2, 0, 0, 0);
@@ -641,7 +668,8 @@ TEST_F(AuthSrvTest, notifyNonSOAQuestion) {
RRClass::IN(), RRType::NS());
request_message.setHeaderFlag(Message::HEADERFLAG_AA);
createRequestPacket(request_message, IPPROTO_UDP);
- server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
+ server.processMessage(*io_message, *parse_message, *response_obuffer,
+ &dnsserv);
EXPECT_TRUE(dnsserv.hasAnswer());
headerCheck(*parse_message, default_qid, Rcode::FORMERR(),
Opcode::NOTIFY().getCode(), QR_FLAG, 1, 0, 0, 0);
@@ -653,7 +681,8 @@ TEST_F(AuthSrvTest, notifyWithoutAA) {
default_qid, Name("example.com"),
RRClass::IN(), RRType::SOA());
createRequestPacket(request_message, IPPROTO_UDP);
- server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
+ server.processMessage(*io_message, *parse_message, *response_obuffer,
+ &dnsserv);
EXPECT_TRUE(dnsserv.hasAnswer());
headerCheck(*parse_message, default_qid, Rcode::NOERROR(),
Opcode::NOTIFY().getCode(), QR_FLAG | AA_FLAG, 1, 0, 0, 0);
@@ -666,7 +695,8 @@ TEST_F(AuthSrvTest, notifyWithErrorRcode) {
request_message.setHeaderFlag(Message::HEADERFLAG_AA);
request_message.setRcode(Rcode::SERVFAIL());
createRequestPacket(request_message, IPPROTO_UDP);
- server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
+ server.processMessage(*io_message, *parse_message, *response_obuffer,
+ &dnsserv);
EXPECT_TRUE(dnsserv.hasAnswer());
headerCheck(*parse_message, default_qid, Rcode::NOERROR(),
Opcode::NOTIFY().getCode(), QR_FLAG | AA_FLAG, 1, 0, 0, 0);
@@ -683,7 +713,8 @@ TEST_F(AuthSrvTest, notifyWithoutSession) {
// we simply ignore the notify and let it be resent if an internal error
// happens.
- server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
+ server.processMessage(*io_message, *parse_message, *response_obuffer,
+ &dnsserv);
EXPECT_FALSE(dnsserv.hasAnswer());
}
@@ -696,7 +727,8 @@ TEST_F(AuthSrvTest, notifySendFail) {
request_message.setHeaderFlag(Message::HEADERFLAG_AA);
createRequestPacket(request_message, IPPROTO_UDP);
- server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
+ server.processMessage(*io_message, *parse_message, *response_obuffer,
+ &dnsserv);
EXPECT_FALSE(dnsserv.hasAnswer());
}
@@ -708,7 +740,8 @@ TEST_F(AuthSrvTest, notifyReceiveFail) {
RRClass::IN(), RRType::SOA());
request_message.setHeaderFlag(Message::HEADERFLAG_AA);
createRequestPacket(request_message, IPPROTO_UDP);
- server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
+ server.processMessage(*io_message, *parse_message, *response_obuffer,
+ &dnsserv);
EXPECT_FALSE(dnsserv.hasAnswer());
}
@@ -720,7 +753,8 @@ TEST_F(AuthSrvTest, notifyWithBogusSessionMessage) {
RRClass::IN(), RRType::SOA());
request_message.setHeaderFlag(Message::HEADERFLAG_AA);
createRequestPacket(request_message, IPPROTO_UDP);
- server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
+ server.processMessage(*io_message, *parse_message, *response_obuffer,
+ &dnsserv);
EXPECT_FALSE(dnsserv.hasAnswer());
}
@@ -733,7 +767,8 @@ TEST_F(AuthSrvTest, notifyWithSessionMessageError) {
RRClass::IN(), RRType::SOA());
request_message.setHeaderFlag(Message::HEADERFLAG_AA);
createRequestPacket(request_message, IPPROTO_UDP);
- server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
+ server.processMessage(*io_message, *parse_message, *response_obuffer,
+ &dnsserv);
EXPECT_FALSE(dnsserv.hasAnswer());
}
@@ -748,7 +783,8 @@ updateConfig(AuthSrv* server, const char* const config_data,
ConstElementPtr result = config_answer->get("result");
EXPECT_EQ(Element::list, result->getType());
- EXPECT_EQ(expect_success ? 0 : 1, result->get(0)->intValue());
+ EXPECT_EQ(expect_success ? 0 : 1, result->get(0)->intValue()) <<
+ "Bad result from updateConfig: " << result->str();
}
// Install a Sqlite3 data source with testing data.
@@ -759,7 +795,8 @@ TEST_F(AuthSrvTest, updateConfig) {
// response should have the AA flag on, and have an RR in each answer
// and authority section.
createDataFromFile("examplequery_fromWire.wire");
- server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
+ server.processMessage(*io_message, *parse_message, *response_obuffer,
+ &dnsserv);
EXPECT_TRUE(dnsserv.hasAnswer());
headerCheck(*parse_message, default_qid, Rcode::NOERROR(), opcode.getCode(),
QR_FLAG | AA_FLAG, 1, 1, 1, 0);
@@ -773,7 +810,8 @@ TEST_F(AuthSrvTest, datasourceFail) {
// in a SERVFAIL response, and the answer and authority sections should
// be empty.
createDataFromFile("badExampleQuery_fromWire.wire");
- server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
+ server.processMessage(*io_message, *parse_message, *response_obuffer,
+ &dnsserv);
EXPECT_TRUE(dnsserv.hasAnswer());
headerCheck(*parse_message, default_qid, Rcode::SERVFAIL(),
opcode.getCode(), QR_FLAG, 1, 0, 0, 0);
@@ -788,7 +826,8 @@ TEST_F(AuthSrvTest, updateConfigFail) {
// The original data source should still exist.
createDataFromFile("examplequery_fromWire.wire");
- server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
+ server.processMessage(*io_message, *parse_message, *response_obuffer,
+ &dnsserv);
EXPECT_TRUE(dnsserv.hasAnswer());
headerCheck(*parse_message, default_qid, Rcode::NOERROR(), opcode.getCode(),
QR_FLAG | AA_FLAG, 1, 1, 1, 0);
@@ -808,7 +847,7 @@ TEST_F(AuthSrvTest, updateWithInMemoryClient) {
// The memory data source is empty, should return REFUSED rcode.
createDataFromFile("examplequery_fromWire.wire");
- server.processMessage(*io_message, parse_message, response_obuffer,
+ server.processMessage(*io_message, *parse_message, *response_obuffer,
&dnsserv);
EXPECT_TRUE(dnsserv.hasAnswer());
headerCheck(*parse_message, default_qid, Rcode::REFUSED(),
@@ -825,7 +864,7 @@ TEST_F(AuthSrvTest, queryWithInMemoryClientNoDNSSEC) {
EXPECT_EQ(1, server.getInMemoryClient(rrclass)->getZoneCount());
createDataFromFile("nsec3query_nodnssec_fromWire.wire");
- server.processMessage(*io_message, parse_message, response_obuffer,
+ server.processMessage(*io_message, *parse_message, *response_obuffer,
&dnsserv);
EXPECT_TRUE(dnsserv.hasAnswer());
@@ -842,7 +881,7 @@ TEST_F(AuthSrvTest, queryWithInMemoryClientDNSSEC) {
EXPECT_EQ(1, server.getInMemoryClient(rrclass)->getZoneCount());
createDataFromFile("nsec3query_fromWire.wire");
- server.processMessage(*io_message, parse_message, response_obuffer,
+ server.processMessage(*io_message, *parse_message, *response_obuffer,
&dnsserv);
EXPECT_TRUE(dnsserv.hasAnswer());
@@ -860,7 +899,7 @@ TEST_F(AuthSrvTest, chQueryWithInMemoryClient) {
default_qid, Name("version.bind"),
RRClass::CH(), RRType::TXT());
createRequestPacket(request_message, IPPROTO_UDP);
- server.processMessage(*io_message, parse_message, response_obuffer,
+ server.processMessage(*io_message, *parse_message, *response_obuffer,
&dnsserv);
EXPECT_TRUE(dnsserv.hasAnswer());
headerCheck(*parse_message, default_qid, Rcode::NOERROR(),
@@ -886,7 +925,7 @@ TEST_F(AuthSrvTest, queryCounterUDPNormal) {
default_qid, Name("example.com"),
RRClass::IN(), RRType::NS());
createRequestPacket(request_message, IPPROTO_UDP);
- server.processMessage(*io_message, parse_message, response_obuffer,
+ server.processMessage(*io_message, *parse_message, *response_obuffer,
&dnsserv);
// After processing UDP query, the counter should be 1.
EXPECT_EQ(1, server.getCounter(AuthCounters::SERVER_UDP_QUERY));
@@ -905,7 +944,7 @@ TEST_F(AuthSrvTest, queryCounterTCPNormal) {
default_qid, Name("example.com"),
RRClass::IN(), RRType::NS());
createRequestPacket(request_message, IPPROTO_TCP);
- server.processMessage(*io_message, parse_message, response_obuffer,
+ server.processMessage(*io_message, *parse_message, *response_obuffer,
&dnsserv);
// After processing TCP query, the counter should be 1.
EXPECT_EQ(1, server.getCounter(AuthCounters::SERVER_TCP_QUERY));
@@ -924,7 +963,8 @@ TEST_F(AuthSrvTest, queryCounterTCPAXFR) {
createRequestPacket(request_message, IPPROTO_TCP);
// On success, the AXFR query has been passed to a separate process,
// so auth itself shouldn't respond.
- server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
+ server.processMessage(*io_message, *parse_message, *response_obuffer,
+ &dnsserv);
EXPECT_FALSE(dnsserv.hasAnswer());
// After processing TCP AXFR query, the counter should be 1.
EXPECT_EQ(1, server.getCounter(AuthCounters::SERVER_TCP_QUERY));
@@ -941,7 +981,8 @@ TEST_F(AuthSrvTest, queryCounterTCPIXFR) {
createRequestPacket(request_message, IPPROTO_TCP);
// On success, the IXFR query has been passed to a separate process,
// so auth itself shouldn't respond.
- server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
+ server.processMessage(*io_message, *parse_message, *response_obuffer,
+ &dnsserv);
EXPECT_FALSE(dnsserv.hasAnswer());
// After processing TCP IXFR query, the counter should be 1.
EXPECT_EQ(1, server.getCounter(AuthCounters::SERVER_TCP_QUERY));
@@ -962,7 +1003,8 @@ TEST_F(AuthSrvTest, queryCounterOpcodes) {
// we intentionally use different values for each code
for (int j = 0; j <= i; ++j) {
parse_message->clear(Message::PARSE);
- server.processMessage(*io_message, parse_message, response_obuffer,
+ server.processMessage(*io_message, *parse_message,
+ *response_obuffer,
&dnsserv);
}
@@ -988,11 +1030,10 @@ getDummyUnknownSocket() {
return (socket);
}
-// Submit unexpected type of query and check it throws isc::Unexpected
+// Submit unexpected type of query and check it is ignored
TEST_F(AuthSrvTest, queryCounterUnexpected) {
// This code isn't exception safe, but we'd rather keep the code
- // simpler and more readable as this is only for tests and if it throws
- // the program would immediately terminate anyway.
+ // simpler and more readable as this is only for tests
// Create UDP query packet.
UnitTestUtil::createRequestMessage(request_message, Opcode::QUERY(),
@@ -1008,9 +1049,7 @@ TEST_F(AuthSrvTest, queryCounterUnexpected) {
request_renderer.getLength(),
getDummyUnknownSocket(), *endpoint);
- EXPECT_THROW(server.processMessage(*io_message, parse_message,
- response_obuffer, &dnsserv),
- isc::Unexpected);
+ EXPECT_FALSE(dnsserv.hasAnswer());
}
TEST_F(AuthSrvTest, stop) {
@@ -1039,6 +1078,7 @@ TEST_F(AuthSrvTest, listenAddresses) {
"Released tokens");
}
+<<<<<<< HEAD
TEST_F(AuthSrvTest, processNormalQuery_reuseRenderer1) {
UnitTestUtil::createRequestMessage(request_message, Opcode::QUERY(),
default_qid, Name("example.com"),
@@ -1060,6 +1100,233 @@ TEST_F(AuthSrvTest, processNormalQuery_reuseRenderer2) {
server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
ConstQuestionPtr question = *parse_message->beginQuestion();
EXPECT_STRNE(question->getType().toText().c_str(),RRType::NS().toText().c_str());
+=======
+//
+// Tests for catching exceptions in various stages of the query processing
+//
+// These tests work by defining two proxy classes, that act as an in-memory
+// client by default, but can throw exceptions at various points.
+//
+namespace {
+
+/// A the possible methods to throw in, either in FakeInMemoryClient or
+/// FakeZoneFinder
+enum ThrowWhen {
+ THROW_NEVER,
+ THROW_AT_FIND_ZONE,
+ THROW_AT_GET_ORIGIN,
+ THROW_AT_GET_CLASS,
+ THROW_AT_FIND,
+ THROW_AT_FIND_ALL,
+ THROW_AT_FIND_NSEC3
+};
+
+/// convenience function to check whether and what to throw
+void
+checkThrow(ThrowWhen method, ThrowWhen throw_at, bool isc_exception) {
+ if (method == throw_at) {
+ if (isc_exception) {
+ isc_throw(isc::Exception, "foo");
+ } else {
+ throw std::exception();
+ }
+ }
+}
+
+/// \brief proxy class for the ZoneFinder returned by the InMemoryClient
+/// proxied by FakeInMemoryClient
+///
+/// See the documentation for FakeInMemoryClient for more information,
+/// all methods simply check whether they should throw, and if not, call
+/// their proxied equivalent.
+class FakeZoneFinder : public isc::datasrc::ZoneFinder {
+public:
+ FakeZoneFinder(isc::datasrc::ZoneFinderPtr zone_finder,
+ ThrowWhen throw_when,
+ bool isc_exception) :
+ real_zone_finder_(zone_finder),
+ throw_when_(throw_when),
+ isc_exception_(isc_exception)
+ {}
+
+ virtual isc::dns::Name
+ getOrigin() const {
+ checkThrow(THROW_AT_GET_ORIGIN, throw_when_, isc_exception_);
+ return (real_zone_finder_->getOrigin());
+ }
+
+ virtual isc::dns::RRClass
+ getClass() const {
+ checkThrow(THROW_AT_GET_CLASS, throw_when_, isc_exception_);
+ return (real_zone_finder_->getClass());
+ }
+
+ virtual isc::datasrc::ZoneFinderContextPtr
+ find(const isc::dns::Name& name,
+ const isc::dns::RRType& type,
+ isc::datasrc::ZoneFinder::FindOptions options)
+ {
+ checkThrow(THROW_AT_FIND, throw_when_, isc_exception_);
+ return (real_zone_finder_->find(name, type, options));
+ }
+
+ virtual isc::datasrc::ZoneFinderContextPtr
+ findAll(const isc::dns::Name& name,
+ std::vector<isc::dns::ConstRRsetPtr> &target,
+ const FindOptions options = FIND_DEFAULT)
+ {
+ checkThrow(THROW_AT_FIND_ALL, throw_when_, isc_exception_);
+ return (real_zone_finder_->findAll(name, target, options));
+ }
+
+ virtual FindNSEC3Result
+ findNSEC3(const isc::dns::Name& name, bool recursive) {
+ checkThrow(THROW_AT_FIND_NSEC3, throw_when_, isc_exception_);
+ return (real_zone_finder_->findNSEC3(name, recursive));
+ }
+
+ virtual isc::dns::Name
+ findPreviousName(const isc::dns::Name& query) const {
+ return (real_zone_finder_->findPreviousName(query));
+ }
+
+private:
+ isc::datasrc::ZoneFinderPtr real_zone_finder_;
+ ThrowWhen throw_when_;
+ bool isc_exception_;
+};
+
+/// \brief Proxy InMemoryClient that can throw exceptions at specified times
+///
+/// It is based on the memory client since that one is easy to override
+/// (with setInMemoryClient) with the current design of AuthSrv.
+class FakeInMemoryClient : public isc::datasrc::InMemoryClient {
+public:
+ /// \brief Create a proxy memory client
+ ///
+ /// \param real_client The real in-memory client to proxy
+ /// \param throw_when if set to any value other than never, that is
+ /// the method that will throw an exception (either in this
+ /// class or the related FakeZoneFinder)
+ /// \param isc_exception if true, throw isc::Exception, otherwise,
+ /// throw std::exception
+ FakeInMemoryClient(AuthSrv::InMemoryClientPtr real_client,
+ ThrowWhen throw_when,
+ bool isc_exception) :
+ real_client_(real_client),
+ throw_when_(throw_when),
+ isc_exception_(isc_exception)
+ {}
+
+ /// \brief proxy call for findZone
+ ///
+ /// if this instance was constructed with throw_when set to find_zone,
+ /// this method will throw. Otherwise, it will return a FakeZoneFinder
+ /// instance which will throw at the method specified at the
+ /// construction of this instance.
+ virtual FindResult
+ findZone(const isc::dns::Name& name) const {
+ checkThrow(THROW_AT_FIND_ZONE, throw_when_, isc_exception_);
+ const FindResult result = real_client_->findZone(name);
+ return (FindResult(result.code, isc::datasrc::ZoneFinderPtr(
+ new FakeZoneFinder(result.zone_finder,
+ throw_when_,
+ isc_exception_))));
+ }
+
+private:
+ AuthSrv::InMemoryClientPtr real_client_;
+ ThrowWhen throw_when_;
+ bool isc_exception_;
+};
+
+} // end anonymous namespace for throwing proxy classes
+
+// Test for the tests
+//
+// Set the proxies to never throw, this should have the same result as
+// queryWithInMemoryClientNoDNSSEC, and serves to test the two proxy classes
+TEST_F(AuthSrvTest, queryWithInMemoryClientProxy) {
+ // Set real inmem client to proxy
+ updateConfig(&server, CONFIG_INMEMORY_EXAMPLE, true);
+
+ AuthSrv::InMemoryClientPtr fake_client(
+ new FakeInMemoryClient(server.getInMemoryClient(rrclass),
+ THROW_NEVER,
+ false));
+
+ ASSERT_NE(AuthSrv::InMemoryClientPtr(), server.getInMemoryClient(rrclass));
+ server.setInMemoryClient(rrclass, fake_client);
+
+ createDataFromFile("nsec3query_nodnssec_fromWire.wire");
+ server.processMessage(*io_message, *parse_message, *response_obuffer,
+ &dnsserv);
+
+ EXPECT_TRUE(dnsserv.hasAnswer());
+ headerCheck(*parse_message, default_qid, Rcode::NOERROR(),
+ opcode.getCode(), QR_FLAG | AA_FLAG, 1, 1, 2, 1);
+}
+
+// Convenience function for the rest of the tests, set up a proxy
+// to throw in the given method
+// If isc_exception is true, it will throw isc::Exception, otherwise
+// it will throw std::exception
+void
+setupThrow(AuthSrv* server, const char *config, ThrowWhen throw_when,
+ bool isc_exception)
+{
+ // Set real inmem client to proxy
+ updateConfig(server, config, true);
+
+ // Set it to throw on findZone(), this should result in
+ // SERVFAIL on any exception
+ AuthSrv::InMemoryClientPtr fake_client(
+ new FakeInMemoryClient(
+ server->getInMemoryClient(isc::dns::RRClass::IN()),
+ throw_when,
+ isc_exception));
+
+ ASSERT_NE(AuthSrv::InMemoryClientPtr(),
+ server->getInMemoryClient(isc::dns::RRClass::IN()));
+ server->setInMemoryClient(isc::dns::RRClass::IN(), fake_client);
+}
+
+TEST_F(AuthSrvTest, queryWithThrowingProxyServfails) {
+ // Test the common cases, all of which should simply return SERVFAIL
+ // Use THROW_NEVER as end marker
+ ThrowWhen throws[] = { THROW_AT_FIND_ZONE,
+ THROW_AT_GET_ORIGIN,
+ THROW_AT_FIND,
+ THROW_AT_FIND_NSEC3,
+ THROW_NEVER };
+ UnitTestUtil::createDNSSECRequestMessage(request_message, opcode,
+ default_qid, Name("foo.example."),
+ RRClass::IN(), RRType::TXT());
+ for (ThrowWhen* when(throws); *when != THROW_NEVER; ++when) {
+ createRequestPacket(request_message, IPPROTO_UDP);
+ setupThrow(&server, CONFIG_INMEMORY_EXAMPLE, *when, true);
+ processAndCheckSERVFAIL();
+ // To be sure, check same for non-isc-exceptions
+ createRequestPacket(request_message, IPPROTO_UDP);
+ setupThrow(&server, CONFIG_INMEMORY_EXAMPLE, *when, false);
+ processAndCheckSERVFAIL();
+ }
+}
+
+// Throw isc::Exception in getClass(). (Currently?) getClass is not called
+// in the processMessage path, so this should result in a normal answer
+TEST_F(AuthSrvTest, queryWithInMemoryClientProxyGetClass) {
+ createDataFromFile("nsec3query_nodnssec_fromWire.wire");
+ setupThrow(&server, CONFIG_INMEMORY_EXAMPLE, THROW_AT_GET_CLASS, true);
+
+ // getClass is not called so it should just answer
+ server.processMessage(*io_message, *parse_message, *response_obuffer,
+ &dnsserv);
+
+ EXPECT_TRUE(dnsserv.hasAnswer());
+ headerCheck(*parse_message, default_qid, Rcode::NOERROR(),
+ opcode.getCode(), QR_FLAG | AA_FLAG, 1, 1, 2, 1);
+>>>>>>> master
}
}
diff --git a/src/bin/auth/tests/command_unittest.cc b/src/bin/auth/tests/command_unittest.cc
index 087ce81..093afda 100644
--- a/src/bin/auth/tests/command_unittest.cc
+++ b/src/bin/auth/tests/command_unittest.cc
@@ -176,16 +176,16 @@ zoneChecks(AuthSrv& server) {
EXPECT_TRUE(server.getInMemoryClient(RRClass::IN()));
EXPECT_EQ(ZoneFinder::SUCCESS, server.getInMemoryClient(RRClass::IN())->
findZone(Name("ns.test1.example")).zone_finder->
- find(Name("ns.test1.example"), RRType::A()).code);
+ find(Name("ns.test1.example"), RRType::A())->code);
EXPECT_EQ(ZoneFinder::NXRRSET, server.getInMemoryClient(RRClass::IN())->
findZone(Name("ns.test1.example")).zone_finder->
- find(Name("ns.test1.example"), RRType::AAAA()).code);
+ find(Name("ns.test1.example"), RRType::AAAA())->code);
EXPECT_EQ(ZoneFinder::SUCCESS, server.getInMemoryClient(RRClass::IN())->
findZone(Name("ns.test2.example")).zone_finder->
- find(Name("ns.test2.example"), RRType::A()).code);
+ find(Name("ns.test2.example"), RRType::A())->code);
EXPECT_EQ(ZoneFinder::NXRRSET, server.getInMemoryClient(RRClass::IN())->
findZone(Name("ns.test2.example")).zone_finder->
- find(Name("ns.test2.example"), RRType::AAAA()).code);
+ find(Name("ns.test2.example"), RRType::AAAA())->code);
}
void
@@ -213,19 +213,19 @@ newZoneChecks(AuthSrv& server) {
EXPECT_TRUE(server.getInMemoryClient(RRClass::IN()));
EXPECT_EQ(ZoneFinder::SUCCESS, server.getInMemoryClient(RRClass::IN())->
findZone(Name("ns.test1.example")).zone_finder->
- find(Name("ns.test1.example"), RRType::A()).code);
+ find(Name("ns.test1.example"), RRType::A())->code);
// now test1.example should have ns/AAAA
EXPECT_EQ(ZoneFinder::SUCCESS, server.getInMemoryClient(RRClass::IN())->
findZone(Name("ns.test1.example")).zone_finder->
- find(Name("ns.test1.example"), RRType::AAAA()).code);
+ find(Name("ns.test1.example"), RRType::AAAA())->code);
// test2.example shouldn't change
EXPECT_EQ(ZoneFinder::SUCCESS, server.getInMemoryClient(RRClass::IN())->
findZone(Name("ns.test2.example")).zone_finder->
- find(Name("ns.test2.example"), RRType::A()).code);
+ find(Name("ns.test2.example"), RRType::A())->code);
EXPECT_EQ(ZoneFinder::NXRRSET, server.getInMemoryClient(RRClass::IN())->
findZone(Name("ns.test2.example")).zone_finder->
- find(Name("ns.test2.example"), RRType::AAAA()).code);
+ find(Name("ns.test2.example"), RRType::AAAA())->code);
}
TEST_F(AuthCommandTest, loadZone) {
diff --git a/src/bin/auth/tests/config_unittest.cc b/src/bin/auth/tests/config_unittest.cc
index 973ab31..fcf88b1 100644
--- a/src/bin/auth/tests/config_unittest.cc
+++ b/src/bin/auth/tests/config_unittest.cc
@@ -191,7 +191,7 @@ TEST_F(MemoryDatasrcConfigTest, addOneZone) {
// Check it actually loaded something
EXPECT_EQ(ZoneFinder::SUCCESS, server.getInMemoryClient(rrclass)->findZone(
Name("ns.example.com.")).zone_finder->find(Name("ns.example.com."),
- RRType::A()).code);
+ RRType::A())->code);
}
TEST_F(MemoryDatasrcConfigTest, addMultiZones) {
diff --git a/src/bin/auth/tests/query_unittest.cc b/src/bin/auth/tests/query_unittest.cc
index 89f7d04..3c901aa 100644
--- a/src/bin/auth/tests/query_unittest.cc
+++ b/src/bin/auth/tests/query_unittest.cc
@@ -370,12 +370,14 @@ public:
}
virtual isc::dns::Name getOrigin() const { return (origin_); }
virtual isc::dns::RRClass getClass() const { return (rrclass_); }
- virtual FindResult find(const isc::dns::Name& name,
- const isc::dns::RRType& type,
- const FindOptions options = FIND_DEFAULT);
- virtual FindResult findAll(const isc::dns::Name& name,
- std::vector<ConstRRsetPtr>& target,
- const FindOptions options = FIND_DEFAULT);
+ virtual ZoneFinderContextPtr find(const isc::dns::Name& name,
+ const isc::dns::RRType& type,
+ const FindOptions options =
+ FIND_DEFAULT);
+ virtual ZoneFinderContextPtr findAll(const isc::dns::Name& name,
+ std::vector<ConstRRsetPtr>& target,
+ const FindOptions options =
+ FIND_DEFAULT);
virtual ZoneFinder::FindNSEC3Result
findNSEC3(const Name& name, bool recursive);
@@ -397,8 +399,10 @@ public:
ConstRRsetPtr rrset)
{
nsec_name_ = nsec_name;
- nsec_result_.reset(new ZoneFinder::FindResult(code, rrset,
- RESULT_NSEC_SIGNED));
+ nsec_context_.reset(new Context(*this,
+ FIND_DEFAULT, // a fake value
+ ResultContext(code, rrset,
+ RESULT_NSEC_SIGNED)));
}
// Once called, the findNSEC3 will return the provided result for the next
@@ -435,6 +439,18 @@ public:
ConstRRsetPtr dname_rrset_; // could be used as an arbitrary bogus RRset
ConstRRsetPtr empty_nsec_rrset_;
+protected:
+ // A convenient shortcut. Will also be used by further derived mocks.
+ ZoneFinderContextPtr createContext(FindOptions options,
+ Result code,
+ isc::dns::ConstRRsetPtr rrset,
+ FindResultFlags flags = RESULT_DEFAULT)
+ {
+ return (ZoneFinderContextPtr(
+ new Context(*this, options,
+ ResultContext(code, rrset, flags))));
+ }
+
private:
typedef map<RRType, ConstRRsetPtr> RRsetStore;
typedef map<Name, RRsetStore> Domains;
@@ -505,7 +521,7 @@ private:
bool use_nsec3_;
// The following two will be used for faked NSEC cases
Name nsec_name_;
- boost::scoped_ptr<ZoneFinder::FindResult> nsec_result_;
+ ZoneFinderContextPtr nsec_context_;
// The following two are for faking bad NSEC3 responses
// Enabled when not NULL
const FindNSEC3Result* nsec3_fake_;
@@ -533,12 +549,12 @@ substituteWild(const AbstractRRset& wild_rrset, const Name& real_name) {
return (rrset);
}
-ZoneFinder::FindResult
+ZoneFinderContextPtr
MockZoneFinder::findAll(const Name& name, std::vector<ConstRRsetPtr>& target,
const FindOptions options)
{
- ZoneFinder::FindResult result(find(name, RRType::ANY(), options));
- if (result.code == NXRRSET) {
+ ZoneFinderContextPtr result(find(name, RRType::ANY(), options));
+ if (result->code == NXRRSET) {
const Domains::const_iterator found_domain = domains_.find(name);
if (!found_domain->second.empty()) {
for (RRsetStore::const_iterator found_rrset =
@@ -547,7 +563,10 @@ MockZoneFinder::findAll(const Name& name, std::vector<ConstRRsetPtr>& target,
// Insert RRs under the domain name into target
target.push_back(found_rrset->second);
}
- return (FindResult(SUCCESS, RRsetPtr()));
+ return (ZoneFinderContextPtr(
+ new Context(*this, options,
+ ResultContext(SUCCESS, RRsetPtr()),
+ target)));
}
}
@@ -610,16 +629,16 @@ MockZoneFinder::findNSEC3(const Name& name, bool recursive) {
isc_throw(isc::Unexpected, "findNSEC3() isn't expected to fail");
}
-ZoneFinder::FindResult
+ZoneFinderContextPtr
MockZoneFinder::find(const Name& name, const RRType& type,
const FindOptions options)
{
// Emulating a broken zone: mandatory apex RRs are missing if specifically
// configured so (which are rare cases).
if (name == origin_ && type == RRType::SOA() && !has_SOA_) {
- return (FindResult(NXDOMAIN, RRsetPtr()));
+ return (createContext(options, NXDOMAIN, RRsetPtr()));
} else if (name == origin_ && type == RRType::NS() && !has_apex_NS_) {
- return (FindResult(NXDOMAIN, RRsetPtr()));
+ return (createContext(options, NXDOMAIN, RRsetPtr()));
}
// Special case for names on or under a zone cut and under DNAME
@@ -634,11 +653,11 @@ MockZoneFinder::find(const Name& name, const RRType& type,
// handled just like an in-zone case below. Others result in
// DELEGATION.
if (type != RRType::DS() || it->first != name) {
- return (FindResult(DELEGATION, delegation_ns));
+ return (createContext(options, DELEGATION, delegation_ns));
}
} else if (name.compare(dname_name_).getRelation() ==
NameComparisonResult::SUBDOMAIN) {
- return (FindResult(DNAME, dname_rrset_));
+ return (createContext(options, DNAME, dname_rrset_));
}
// normal cases. names are searched for only per exact-match basis
@@ -668,33 +687,35 @@ MockZoneFinder::find(const Name& name, const RRType& type,
}
rrset = noconst;
}
- return (FindResult(SUCCESS, rrset));
+ return (createContext(options, SUCCESS, rrset));
}
// Otherwise, if this domain name has CNAME, return it.
found_rrset = found_domain->second.find(RRType::CNAME());
if (found_rrset != found_domain->second.end()) {
- return (FindResult(CNAME, found_rrset->second));
+ return (createContext(options, CNAME, found_rrset->second));
}
// Otherwise it's NXRRSET case...
// ...but a special pathological case first:
if (found_domain->first == bad_signed_delegation_name_ &&
type == RRType::DS()) {
- return (FindResult(NXDOMAIN, RRsetPtr()));
+ return (createContext(options, NXDOMAIN, RRsetPtr()));
}
// normal cases follow.
if ((options & FIND_DNSSEC) != 0) {
if (use_nsec3_) {
- return (FindResult(NXRRSET, RRsetPtr(), RESULT_NSEC3_SIGNED));
+ return (createContext(options, NXRRSET, RRsetPtr(),
+ RESULT_NSEC3_SIGNED));
}
found_rrset = found_domain->second.find(RRType::NSEC());
if (found_rrset != found_domain->second.end()) {
- return (FindResult(NXRRSET, found_rrset->second,
- RESULT_NSEC_SIGNED));
+ return (createContext(options, NXRRSET, found_rrset->second,
+ RESULT_NSEC_SIGNED));
}
}
- return (FindResult(NXRRSET, RRsetPtr(), RESULT_NSEC_SIGNED));
+ return (createContext(options, NXRRSET, RRsetPtr(),
+ RESULT_NSEC_SIGNED));
}
// query name isn't found in our domains.
@@ -714,16 +735,17 @@ MockZoneFinder::find(const Name& name, const RRType& type,
--domain; // reset domain to the "previous name"
if ((options & FIND_DNSSEC) != 0) {
if (use_nsec3_) {
- return (FindResult(NXRRSET, RRsetPtr(), RESULT_NSEC3_SIGNED));
+ return (createContext(options, NXRRSET, RRsetPtr(),
+ RESULT_NSEC3_SIGNED));
}
RRsetStore::const_iterator found_rrset =
(*domain).second.find(RRType::NSEC());
if (found_rrset != (*domain).second.end()) {
- return (FindResult(NXRRSET, found_rrset->second,
- RESULT_NSEC_SIGNED));
+ return (createContext(options, NXRRSET, found_rrset->second,
+ RESULT_NSEC_SIGNED));
}
}
- return (FindResult(NXRRSET, RRsetPtr()));
+ return (createContext(options, NXRRSET, RRsetPtr()));
}
// Another possibility is wildcard. For simplicity we only check
@@ -746,39 +768,37 @@ MockZoneFinder::find(const Name& name, const RRType& type,
domain->second.find(type);
// Matched the QTYPE
if(found_rrset != domain->second.end()) {
- return (FindResult(SUCCESS,
- substituteWild(
- *found_rrset->second, name),
- RESULT_WILDCARD |
- (use_nsec3_ ?
- RESULT_NSEC3_SIGNED :
- RESULT_NSEC_SIGNED)));
+ return (createContext(options,SUCCESS, substituteWild(
+ *found_rrset->second, name),
+ RESULT_WILDCARD |
+ (use_nsec3_ ?
+ RESULT_NSEC3_SIGNED :
+ RESULT_NSEC_SIGNED)));
} else {
// No matched QTYPE, this case is for NXRRSET with
// WILDCARD
if (use_nsec3_) {
- return (FindResult(NXRRSET, RRsetPtr(),
- RESULT_WILDCARD |
- RESULT_NSEC3_SIGNED));
+ return (createContext(options, NXRRSET, RRsetPtr(),
+ RESULT_WILDCARD |
+ RESULT_NSEC3_SIGNED));
}
const Name new_name =
Name("*").concatenate(wild_suffix);
found_rrset = domain->second.find(RRType::NSEC());
assert(found_rrset != domain->second.end());
- return (FindResult(NXRRSET,
- substituteWild(
- *found_rrset->second,
- new_name),
- RESULT_WILDCARD |
- RESULT_NSEC_SIGNED));
+ return (createContext(options, NXRRSET, substituteWild(
+ *found_rrset->second,
+ new_name),
+ RESULT_WILDCARD |
+ RESULT_NSEC_SIGNED));
}
} else {
// This is empty non terminal name case on wildcard.
const Name empty_name = Name("*").concatenate(wild_suffix);
if (use_nsec3_) {
- return (FindResult(NXRRSET, RRsetPtr(),
- RESULT_WILDCARD |
- RESULT_NSEC3_SIGNED));
+ return (createContext(options, NXRRSET, RRsetPtr(),
+ RESULT_WILDCARD |
+ RESULT_NSEC3_SIGNED));
}
for (Domains::reverse_iterator it = domains_.rbegin();
it != domains_.rend();
@@ -787,13 +807,15 @@ MockZoneFinder::find(const Name& name, const RRType& type,
if ((*it).first < empty_name &&
(nsec_it = (*it).second.find(RRType::NSEC()))
!= (*it).second.end()) {
- return (FindResult(NXRRSET, (*nsec_it).second,
- RESULT_WILDCARD |
- RESULT_NSEC_SIGNED));
+ return (createContext(options, NXRRSET,
+ (*nsec_it).second,
+ RESULT_WILDCARD |
+ RESULT_NSEC_SIGNED));
}
}
}
- return (FindResult(NXRRSET, RRsetPtr(), RESULT_WILDCARD));
+ return (createContext(options, NXRRSET, RRsetPtr(),
+ RESULT_WILDCARD));
}
}
const Name cnamewild_suffix("cnamewild.example.com");
@@ -804,11 +826,11 @@ MockZoneFinder::find(const Name& name, const RRType& type,
RRsetStore::const_iterator found_rrset =
domain->second.find(RRType::CNAME());
assert(found_rrset != domain->second.end());
- return (FindResult(CNAME,
- substituteWild(*found_rrset->second, name),
- RESULT_WILDCARD |
- (use_nsec3_ ? RESULT_NSEC3_SIGNED :
- RESULT_NSEC_SIGNED)));
+ return (createContext(options, CNAME,
+ substituteWild(*found_rrset->second, name),
+ RESULT_WILDCARD |
+ (use_nsec3_ ? RESULT_NSEC3_SIGNED :
+ RESULT_NSEC_SIGNED)));
}
}
@@ -821,12 +843,13 @@ MockZoneFinder::find(const Name& name, const RRType& type,
// than the origin)
if ((options & FIND_DNSSEC) != 0) {
if (use_nsec3_) {
- return (FindResult(NXDOMAIN, RRsetPtr(), RESULT_NSEC3_SIGNED));
+ return (createContext(options, NXDOMAIN, RRsetPtr(),
+ RESULT_NSEC3_SIGNED));
}
// Emulate a broken DataSourceClient for some special names.
- if (nsec_result_ && nsec_name_ == name) {
- return (*nsec_result_);
+ if (nsec_context_ && nsec_name_ == name) {
+ return (nsec_context_);
}
// Normal case
@@ -839,12 +862,12 @@ MockZoneFinder::find(const Name& name, const RRType& type,
if ((*it).first < name &&
(nsec_it = (*it).second.find(RRType::NSEC()))
!= (*it).second.end()) {
- return (FindResult(NXDOMAIN, (*nsec_it).second,
- RESULT_NSEC_SIGNED));
+ return (createContext(options, NXDOMAIN, (*nsec_it).second,
+ RESULT_NSEC_SIGNED));
}
}
}
- return (FindResult(NXDOMAIN, RRsetPtr()));
+ return (createContext(options,NXDOMAIN, RRsetPtr()));
}
class QueryTest : public ::testing::Test {
@@ -1961,23 +1984,23 @@ public:
MockZoneFinder(), origin_(origin), have_ds_(have_ds)
{}
virtual isc::dns::Name getOrigin() const { return (origin_); }
- virtual FindResult find(const isc::dns::Name&,
- const isc::dns::RRType& type,
- const FindOptions)
+ virtual ZoneFinderContextPtr find(const isc::dns::Name&,
+ const isc::dns::RRType& type,
+ const FindOptions options)
{
if (type == RRType::SOA()) {
RRsetPtr soa = textToRRset(origin_.toText() + " 3600 IN SOA . . "
"0 0 0 0 0\n", origin_);
soa->addRRsig(RdataPtr(new generic::RRSIG(
getCommonRRSIGText("SOA"))));
- return (FindResult(SUCCESS, soa));
+ return (createContext(options, SUCCESS, soa));
}
if (type == RRType::NS()) {
RRsetPtr ns = textToRRset(origin_.toText() + " 3600 IN NS " +
Name("ns").concatenate(origin_).toText());
ns->addRRsig(RdataPtr(new generic::RRSIG(
getCommonRRSIGText("NS"))));
- return (FindResult(SUCCESS, ns));
+ return (createContext(options, SUCCESS, ns));
}
if (type == RRType::DS()) {
if (have_ds_) {
@@ -1987,7 +2010,7 @@ public:
"3CD34AC1AFE51DE");
ds->addRRsig(RdataPtr(new generic::RRSIG(
getCommonRRSIGText("DS"))));
- return (FindResult(SUCCESS, ds));
+ return (createContext(options, SUCCESS, ds));
} else {
RRsetPtr nsec = textToRRset(origin_.toText() +
" 3600 IN NSEC " +
@@ -1995,12 +2018,13 @@ public:
" SOA NSEC RRSIG");
nsec->addRRsig(RdataPtr(new generic::RRSIG(
getCommonRRSIGText("NSEC"))));
- return (FindResult(NXRRSET, nsec, RESULT_NSEC_SIGNED));
+ return (createContext(options, NXRRSET, nsec,
+ RESULT_NSEC_SIGNED));
}
}
// Returning NXDOMAIN is not correct, but doesn't matter for our tests.
- return (FindResult(NXDOMAIN, ConstRRsetPtr()));
+ return (createContext(options, NXDOMAIN, ConstRRsetPtr()));
}
private:
const Name origin_;
@@ -2310,11 +2334,11 @@ TEST_F(QueryTest, nxdomainWithBadWildcardNSEC3Proof) {
// clean them up.
TEST_F(QueryTest, emptyNameWithNSEC3) {
mock_finder->setNSEC3Flag(true);
- ZoneFinder::FindResult result = mock_finder->find(
+ ZoneFinderContextPtr result = mock_finder->find(
Name("no.example.com"), RRType::A(), ZoneFinder::FIND_DNSSEC);
- EXPECT_EQ(ZoneFinder::NXRRSET, result.code);
- EXPECT_FALSE(result.rrset);
- EXPECT_TRUE(result.isNSEC3Signed());
- EXPECT_FALSE(result.isWildcard());
+ EXPECT_EQ(ZoneFinder::NXRRSET, result->code);
+ EXPECT_FALSE(result->rrset);
+ EXPECT_TRUE(result->isNSEC3Signed());
+ EXPECT_FALSE(result->isWildcard());
}
}
diff --git a/src/bin/bind10/.gitignore b/src/bin/bind10/.gitignore
new file mode 100644
index 0000000..8dc8a04
--- /dev/null
+++ b/src/bin/bind10/.gitignore
@@ -0,0 +1,3 @@
+/bind10
+/bind10_src.py
+/run_bind10.sh
diff --git a/src/bin/bind10/bind10_messages.mes b/src/bin/bind10/bind10_messages.mes
index 79635fd..3dd938f 100644
--- a/src/bin/bind10/bind10_messages.mes
+++ b/src/bin/bind10/bind10_messages.mes
@@ -24,7 +24,7 @@ needs a dedicated message bus.
An error was encountered when the boss module specified
statistics data which is invalid for the boss specification file.
-% BIND10_COMPONENT_FAILED component %1 (pid %2) failed with %3 exit status
+% BIND10_COMPONENT_FAILED component %1 (pid %2) failed: %3
The process terminated, but the bind10 boss didn't expect it to, which means
it must have failed.
diff --git a/src/bin/bind10/tests/.gitignore b/src/bin/bind10/tests/.gitignore
new file mode 100644
index 0000000..5e54716
--- /dev/null
+++ b/src/bin/bind10/tests/.gitignore
@@ -0,0 +1 @@
+/bind10_test.py
diff --git a/src/bin/bindctl/.gitignore b/src/bin/bindctl/.gitignore
new file mode 100644
index 0000000..71fba03
--- /dev/null
+++ b/src/bin/bindctl/.gitignore
@@ -0,0 +1,3 @@
+/bindctl
+/bindctl_main.py
+/run_bindctl.sh
diff --git a/src/bin/bindctl/tests/.gitignore b/src/bin/bindctl/tests/.gitignore
new file mode 100644
index 0000000..284bacc
--- /dev/null
+++ b/src/bin/bindctl/tests/.gitignore
@@ -0,0 +1 @@
+/bindctl_test
diff --git a/src/bin/cfgmgr/.gitignore b/src/bin/cfgmgr/.gitignore
new file mode 100644
index 0000000..aad54f4
--- /dev/null
+++ b/src/bin/cfgmgr/.gitignore
@@ -0,0 +1,2 @@
+/b10-cfgmgr
+/b10-cfgmgr.py
diff --git a/src/bin/cfgmgr/tests/.gitignore b/src/bin/cfgmgr/tests/.gitignore
new file mode 100644
index 0000000..dcdab2d
--- /dev/null
+++ b/src/bin/cfgmgr/tests/.gitignore
@@ -0,0 +1 @@
+/b10-cfgmgr_test.py
diff --git a/src/bin/cmdctl/.gitignore b/src/bin/cmdctl/.gitignore
new file mode 100644
index 0000000..a194135
--- /dev/null
+++ b/src/bin/cmdctl/.gitignore
@@ -0,0 +1,5 @@
+/b10-cmdctl
+/cmdctl.py
+/cmdctl.spec
+/cmdctl.spec.pre
+/run_b10-cmdctl.sh
diff --git a/src/bin/cmdctl/tests/.gitignore b/src/bin/cmdctl/tests/.gitignore
new file mode 100644
index 0000000..ab9dfef
--- /dev/null
+++ b/src/bin/cmdctl/tests/.gitignore
@@ -0,0 +1 @@
+/cmdctl_test
diff --git a/src/bin/ddns/.gitignore b/src/bin/ddns/.gitignore
new file mode 100644
index 0000000..92b86f3
--- /dev/null
+++ b/src/bin/ddns/.gitignore
@@ -0,0 +1,2 @@
+/b10-ddns
+/ddns.py
diff --git a/src/bin/dhcp4/.gitignore b/src/bin/dhcp4/.gitignore
new file mode 100644
index 0000000..f7e9973
--- /dev/null
+++ b/src/bin/dhcp4/.gitignore
@@ -0,0 +1,3 @@
+/b10-dhcp4
+/spec_config.h
+/spec_config.h.pre
diff --git a/src/bin/dhcp4/tests/.gitignore b/src/bin/dhcp4/tests/.gitignore
new file mode 100644
index 0000000..5d14dac
--- /dev/null
+++ b/src/bin/dhcp4/tests/.gitignore
@@ -0,0 +1 @@
+/dhcp4_unittests
diff --git a/src/bin/host/.gitignore b/src/bin/host/.gitignore
new file mode 100644
index 0000000..0073523
--- /dev/null
+++ b/src/bin/host/.gitignore
@@ -0,0 +1 @@
+/b10-host
diff --git a/src/bin/loadzone/.gitignore b/src/bin/loadzone/.gitignore
new file mode 100644
index 0000000..86761ee
--- /dev/null
+++ b/src/bin/loadzone/.gitignore
@@ -0,0 +1,3 @@
+/b10-loadzone
+/b10-loadzone.py
+/run_loadzone.sh
diff --git a/src/bin/loadzone/tests/correct/.gitignore b/src/bin/loadzone/tests/correct/.gitignore
new file mode 100644
index 0000000..2d58698
--- /dev/null
+++ b/src/bin/loadzone/tests/correct/.gitignore
@@ -0,0 +1 @@
+/correct_test.sh
diff --git a/src/bin/loadzone/tests/error/.gitignore b/src/bin/loadzone/tests/error/.gitignore
new file mode 100644
index 0000000..5d20adb
--- /dev/null
+++ b/src/bin/loadzone/tests/error/.gitignore
@@ -0,0 +1 @@
+/error_test.sh
diff --git a/src/bin/msgq/.gitignore b/src/bin/msgq/.gitignore
new file mode 100644
index 0000000..ee1d942
--- /dev/null
+++ b/src/bin/msgq/.gitignore
@@ -0,0 +1,3 @@
+/b10-msgq
+/msgq.py
+/run_msgq.sh
diff --git a/src/bin/msgq/tests/.gitignore b/src/bin/msgq/tests/.gitignore
new file mode 100644
index 0000000..70acfff
--- /dev/null
+++ b/src/bin/msgq/tests/.gitignore
@@ -0,0 +1 @@
+/msgq_test
diff --git a/src/bin/resolver/.gitignore b/src/bin/resolver/.gitignore
new file mode 100644
index 0000000..95abd50
--- /dev/null
+++ b/src/bin/resolver/.gitignore
@@ -0,0 +1,7 @@
+/b10-resolver
+/resolver.spec
+/resolver.spec.pre
+/resolver_messages.cc
+/resolver_messages.h
+/spec_config.h
+/spec_config.h.pre
diff --git a/src/bin/resolver/main.cc b/src/bin/resolver/main.cc
index c56e6f3..d14fb0b 100644
--- a/src/bin/resolver/main.cc
+++ b/src/bin/resolver/main.cc
@@ -100,7 +100,8 @@ my_command_handler(const string& command, ConstElementPtr args) {
return (answer);
}
}
- LOG_DEBUG(resolver_logger, RESOLVER_DBG_INIT, RESOLVER_SHUTDOWN);
+ LOG_DEBUG(resolver_logger, RESOLVER_DBG_INIT,
+ RESOLVER_SHUTDOWN_RECEIVED);
io_service.stop();
}
diff --git a/src/bin/resolver/resolver_messages.mes b/src/bin/resolver/resolver_messages.mes
index bd9c818..4999dbe 100644
--- a/src/bin/resolver/resolver_messages.mes
+++ b/src/bin/resolver/resolver_messages.mes
@@ -247,6 +247,6 @@ The log message shows the query in the form of <query name>/<query
type>/<query class>, and the client that sends the query in the form of
<Source IP address>#<source port>.
-% RESOLVER_SHUTDOWN asked to shut down, doing so
+% RESOLVER_SHUTDOWN_RECEIVED received command to shut down
A debug message noting that the server was asked to terminate and is
complying to the request.
diff --git a/src/bin/resolver/tests/.gitignore b/src/bin/resolver/tests/.gitignore
new file mode 100644
index 0000000..d6d1ec8
--- /dev/null
+++ b/src/bin/resolver/tests/.gitignore
@@ -0,0 +1 @@
+/run_unittests
diff --git a/src/bin/sockcreator/.gitignore b/src/bin/sockcreator/.gitignore
new file mode 100644
index 0000000..2985184
--- /dev/null
+++ b/src/bin/sockcreator/.gitignore
@@ -0,0 +1 @@
+/b10-sockcreator
diff --git a/src/bin/sockcreator/tests/.gitignore b/src/bin/sockcreator/tests/.gitignore
new file mode 100644
index 0000000..d6d1ec8
--- /dev/null
+++ b/src/bin/sockcreator/tests/.gitignore
@@ -0,0 +1 @@
+/run_unittests
diff --git a/src/bin/stats/.gitignore b/src/bin/stats/.gitignore
new file mode 100644
index 0000000..7ff3797
--- /dev/null
+++ b/src/bin/stats/.gitignore
@@ -0,0 +1,4 @@
+/b10-stats
+/b10-stats-httpd
+/stats.py
+/stats_httpd.py
diff --git a/src/bin/tests/.gitignore b/src/bin/tests/.gitignore
new file mode 100644
index 0000000..b39aa86
--- /dev/null
+++ b/src/bin/tests/.gitignore
@@ -0,0 +1 @@
+/process_rename_test.py
diff --git a/src/bin/usermgr/.gitignore b/src/bin/usermgr/.gitignore
new file mode 100644
index 0000000..e116052
--- /dev/null
+++ b/src/bin/usermgr/.gitignore
@@ -0,0 +1,3 @@
+/b10-cmdctl-usermgr
+/b10-cmdctl-usermgr.py
+/run_b10-cmdctl-usermgr.sh
diff --git a/src/bin/xfrin/.gitignore b/src/bin/xfrin/.gitignore
new file mode 100644
index 0000000..5ac1942
--- /dev/null
+++ b/src/bin/xfrin/.gitignore
@@ -0,0 +1,3 @@
+/b10-xfrin
+/run_b10-xfrin.sh
+/xfrin.py
diff --git a/src/bin/xfrin/tests/.gitignore b/src/bin/xfrin/tests/.gitignore
new file mode 100644
index 0000000..4de3f47
--- /dev/null
+++ b/src/bin/xfrin/tests/.gitignore
@@ -0,0 +1 @@
+/xfrin_test
diff --git a/src/bin/xfrin/tests/xfrin_test.py b/src/bin/xfrin/tests/xfrin_test.py
index 89e4d96..8ce1ae0 100644
--- a/src/bin/xfrin/tests/xfrin_test.py
+++ b/src/bin/xfrin/tests/xfrin_test.py
@@ -1318,6 +1318,14 @@ class TestAXFR(TestXfrinConnection):
self.assertEqual(self.conn.do_xfrin(False), XFRIN_OK)
self.assertFalse(self.conn._datasrc_client._journaling_enabled)
+ self.assertEqual(2, self.conn._transfer_stats.message_count)
+ self.assertEqual(2, self.conn._transfer_stats.axfr_rr_count)
+ self.assertEqual(0, self.conn._transfer_stats.ixfr_changeset_count)
+ self.assertEqual(0, self.conn._transfer_stats.ixfr_deletion_count)
+ self.assertEqual(0, self.conn._transfer_stats.ixfr_addition_count)
+ self.assertEqual(177, self.conn._transfer_stats.byte_count)
+ self.assertGreater(self.conn._transfer_stats.get_running_time(), 0)
+
def test_do_xfrin_with_tsig(self):
# use TSIG with a mock context. we fake all verify results to
# emulate successful verification.
@@ -1687,6 +1695,14 @@ class TestIXFRSession(TestXfrinConnection):
self.assertEqual(TEST_ZONE_NAME, qmsg.get_question()[0].get_name())
self.assertEqual(RRType.IXFR(), qmsg.get_question()[0].get_type())
+ self.assertEqual(1, self.conn._transfer_stats.message_count)
+ self.assertEqual(0, self.conn._transfer_stats.axfr_rr_count)
+ self.assertEqual(1, self.conn._transfer_stats.ixfr_changeset_count)
+ self.assertEqual(1, self.conn._transfer_stats.ixfr_deletion_count)
+ self.assertEqual(1, self.conn._transfer_stats.ixfr_addition_count)
+ self.assertEqual(188, self.conn._transfer_stats.byte_count)
+ self.assertGreater(self.conn._transfer_stats.get_running_time(), 0)
+
def test_do_xfrin_fail(self):
'''IXFR fails due to a protocol error.
@@ -1719,6 +1735,14 @@ class TestIXFRSession(TestXfrinConnection):
self.conn.response_generator = create_response
self.assertEqual(XFRIN_OK, self.conn.do_xfrin(False, RRType.IXFR()))
+ self.assertEqual(1, self.conn._transfer_stats.message_count)
+ self.assertEqual(0, self.conn._transfer_stats.axfr_rr_count)
+ self.assertEqual(0, self.conn._transfer_stats.ixfr_changeset_count)
+ self.assertEqual(0, self.conn._transfer_stats.ixfr_deletion_count)
+ self.assertEqual(0, self.conn._transfer_stats.ixfr_addition_count)
+ self.assertEqual(80, self.conn._transfer_stats.byte_count)
+ self.assertGreater(self.conn._transfer_stats.get_running_time(), 0)
+
class TestXFRSessionWithSQLite3(TestXfrinConnection):
'''Tests for XFR sessions using an SQLite3 DB.
@@ -2715,6 +2739,63 @@ class TestFormatting(unittest.TestCase):
self.assertRaises(TypeError, format_addrinfo,
(socket.AF_INET, "asdf", ()))
+class TestXfrinTransferStats(unittest.TestCase):
+ def setUp(self):
+ self.stats = XfrinTransferStats()
+
+ def zero_check(self):
+ # Checks whether all counters are zero
+ self.assertEqual(0, self.stats.message_count)
+ self.assertEqual(0, self.stats.axfr_rr_count)
+ self.assertEqual(0, self.stats.byte_count)
+ self.assertEqual(0, self.stats.ixfr_changeset_count)
+ self.assertEqual(0, self.stats.ixfr_deletion_count)
+ self.assertEqual(0, self.stats.ixfr_addition_count)
+
+ def test_init(self):
+ self.zero_check()
+ self.assertIsNone(self.stats._end_time)
+
+ def test_get_running_time(self):
+ self.assertIsNone(self.stats._end_time)
+ runtime = self.stats.get_running_time()
+ self.assertIsNotNone(self.stats._end_time)
+ self.assertGreater(runtime, 0)
+ # make sure a second get does not change anything
+ runtime2 = self.stats.get_running_time()
+ self.assertEqual(runtime, runtime2)
+ # And that no counters have been modified
+ self.zero_check()
+
+ def test_bytes_per_second(self):
+ zbps = self.stats.get_bytes_per_second()
+ self.assertEqual(0, zbps)
+
+ self.stats._start_time = 1
+ self.stats._end_time = 2
+ self.stats.byte_count += 4
+ zbps = self.stats.get_bytes_per_second()
+ self.assertEqual(4, zbps)
+
+ self.stats._start_time = float(1)
+ self.stats._end_time = float(11)
+ self.assertEqual(10, self.stats.get_running_time())
+ self.stats.byte_count = 1234
+ zbps = self.stats.get_bytes_per_second()
+ self.assertEqual(123.4, zbps)
+
+ # if for some reason the runtime is 0, depending
+ # on whether bytes have actually been seen, bps is either
+ # 0 or 'infinite'
+ self.stats._end_time = self.stats._start_time
+ zbps = self.stats.get_bytes_per_second()
+ self.assertEqual(float("inf"), zbps)
+
+ self.stats.byte_count = 0
+ zbps = self.stats.get_bytes_per_second()
+ self.assertEqual(0, zbps)
+
+
if __name__== "__main__":
try:
isc.log.resetUnitTestRootLogger()
diff --git a/src/bin/xfrin/xfrin.py.in b/src/bin/xfrin/xfrin.py.in
index b59c2b6..863c5b9 100755
--- a/src/bin/xfrin/xfrin.py.in
+++ b/src/bin/xfrin/xfrin.py.in
@@ -24,6 +24,7 @@ import struct
import threading
import socket
import random
+import time
from functools import reduce
from optparse import OptionParser, OptionValueError
from isc.config.ccsession import *
@@ -381,6 +382,7 @@ class XfrinIXFRDeleteSOA(XfrinState):
conn._diff = Diff(conn._datasrc_client, conn._zone_name, False, True)
conn._diff.delete_data(rr)
self.set_xfrstate(conn, XfrinIXFRDelete())
+ conn.get_transfer_stats().ixfr_deletion_count += 1
return True
class XfrinIXFRDelete(XfrinState):
@@ -391,6 +393,7 @@ class XfrinIXFRDelete(XfrinState):
self.set_xfrstate(conn, XfrinIXFRAddSOA())
return False
conn._diff.delete_data(rr)
+ conn.get_transfer_stats().ixfr_deletion_count += 1
return True
class XfrinIXFRAddSOA(XfrinState):
@@ -402,11 +405,14 @@ class XfrinIXFRAddSOA(XfrinState):
' RR is given in IXFRAddSOA state')
conn._diff.add_data(rr)
self.set_xfrstate(conn, XfrinIXFRAdd())
+ conn.get_transfer_stats().ixfr_addition_count += 1
return True
class XfrinIXFRAdd(XfrinState):
def handle_rr(self, conn, rr):
if rr.get_type() == RRType.SOA():
+ # This SOA marks the end of a difference sequence
+ conn.get_transfer_stats().ixfr_changeset_count += 1
soa_serial = get_soa_serial(rr.get_rdata()[0])
if soa_serial == conn._end_serial:
conn._diff.commit()
@@ -422,6 +428,7 @@ class XfrinIXFRAdd(XfrinState):
self.set_xfrstate(conn, XfrinIXFRDeleteSOA())
return False
conn._diff.add_data(rr)
+ conn.get_transfer_stats().ixfr_addition_count += 1
return True
class XfrinIXFREnd(XfrinState):
@@ -462,6 +469,7 @@ class XfrinAXFR(XfrinState):
conn._end_serial, soa_serial)
self.set_xfrstate(conn, XfrinAXFREnd())
+ conn.get_transfer_stats().axfr_rr_count += 1
# Yes, we've eaten this RR.
return True
@@ -484,6 +492,55 @@ class XfrinAXFREnd(XfrinState):
conn._diff.commit()
return False
+class XfrinTransferStats:
+ """
+ This class keeps a record of transfer data for logging purposes.
+ It records number of messages, rrs, and bytes transfered, as well
+ as the start and end time. The start time is set upon instantiation of
+ this class. The end time is set the first time finalize(),
+ get_running_time(), or get_bytes_per_second() is called. The end time is
+ set only once; subsequent calls to any of these methods does not modify
+ it further.
+ All _count instance variables can be directly set as needed by the
+ class collecting these results.
+ """
+ def __init__(self):
+ self.message_count = 0
+ self.axfr_rr_count = 0
+ self.byte_count = 0
+ self.ixfr_changeset_count = 0;
+ self.ixfr_deletion_count = 0;
+ self.ixfr_addition_count = 0;
+ self._start_time = time.time()
+ self._end_time = None
+
+ def finalize(self):
+ """Sets the end time to time.time() if not done already."""
+ if self._end_time is None:
+ self._end_time = time.time()
+
+ def get_running_time(self):
+ """Calls finalize(), then returns the difference between creation
+ and finalization time"""
+ self.finalize()
+ return self._end_time - self._start_time
+
+ def get_bytes_per_second(self):
+ """Returns the number of bytes per second, based on the result of
+ get_running_time() and the value of bytes_count."""
+ runtime = self.get_running_time()
+ if runtime > 0.0:
+ return float(self.byte_count) / runtime
+ else:
+ # This should never happen, but if some clock is so
+ # off or reset in the meantime, we do need to return
+ # *something* (and not raise an error)
+ if self.byte_count == 0:
+ return 0.0
+ else:
+ return float("inf")
+
+
class XfrinConnection(asyncore.dispatcher):
'''Do xfrin in this class. '''
@@ -534,6 +591,10 @@ class XfrinConnection(asyncore.dispatcher):
# easier tests (in normal case we always use the default)
self._tsig_ctx_creator = lambda key : TSIGContext(key)
+ # keep a record of this specific transfer to log on success
+ # (time, rr/s, etc)
+ self._transfer_stats = XfrinTransferStats()
+
def init_socket(self):
'''Initialize the underlyig socket.
@@ -599,6 +660,11 @@ class XfrinConnection(asyncore.dispatcher):
def get_xfrstate(self):
return self.__state
+ def get_transfer_stats(self):
+ """Returns the transfer stats object, used to measure transfer time,
+ and number of messages/records/bytes transfered."""
+ return self._transfer_stats
+
def zone_str(self):
'''A convenience function for logging to include zone name and class'''
return format_zone_str(self._zone_name, self._rrclass)
@@ -823,7 +889,29 @@ class XfrinConnection(asyncore.dispatcher):
self._send_query(self._request_type)
self.__state = XfrinInitialSOA()
self._handle_xfrin_responses()
- logger.info(XFRIN_XFR_TRANSFER_SUCCESS, req_str, self.zone_str())
+ # Depending what data was found, we log different status reports
+ # (In case of an AXFR-style IXFR, print the 'AXFR' message)
+ if self._transfer_stats.axfr_rr_count == 0:
+ logger.info(XFRIN_IXFR_TRANSFER_SUCCESS,
+ self.zone_str(),
+ self._transfer_stats.message_count,
+ self._transfer_stats.ixfr_changeset_count,
+ self._transfer_stats.ixfr_deletion_count,
+ self._transfer_stats.ixfr_addition_count,
+ self._transfer_stats.byte_count,
+ "%.3f" % self._transfer_stats.get_running_time(),
+ "%.f" % self._transfer_stats.get_bytes_per_second()
+ )
+ else:
+ logger.info(XFRIN_TRANSFER_SUCCESS,
+ req_str,
+ self.zone_str(),
+ self._transfer_stats.message_count,
+ self._transfer_stats.axfr_rr_count,
+ self._transfer_stats.byte_count,
+ "%.3f" % self._transfer_stats.get_running_time(),
+ "%.f" % self._transfer_stats.get_bytes_per_second()
+ )
except XfrinZoneUptodate:
# Eventually we'll probably have to treat this case as a trigger
@@ -895,9 +983,11 @@ class XfrinConnection(asyncore.dispatcher):
while read_next_msg:
data_len = self._get_request_response(2)
msg_len = socket.htons(struct.unpack('H', data_len)[0])
+ self._transfer_stats.byte_count += msg_len + 2
recvdata = self._get_request_response(msg_len)
msg = Message(Message.PARSE)
msg.from_wire(recvdata, Message.PRESERVE_ORDER)
+ self._transfer_stats.message_count += 1
# TSIG related checks, including an unexpected signed response
self._check_response_tsig(msg, recvdata)
diff --git a/src/bin/xfrin/xfrin_messages.mes b/src/bin/xfrin/xfrin_messages.mes
index 5e182d8..eae1c69 100644
--- a/src/bin/xfrin/xfrin_messages.mes
+++ b/src/bin/xfrin/xfrin_messages.mes
@@ -15,92 +15,21 @@
# No namespace declaration - these constants go in the global namespace
# of the xfrin messages python module.
-% XFRIN_ZONE_CREATED Zone %1 not found in the given data source, newly created
-On starting an xfrin session, it is identified that the zone to be
-transferred is not found in the data source. This can happen if a
-secondary DNS server first tries to perform AXFR from a primary server
-without creating the zone image beforehand (e.g. by b10-loadzone). As
-of this writing the xfrin process provides backward compatible
-behavior to previous versions: creating a new one in the data source
-not to surprise existing users too much. This is probably not a good
-idea, however, in terms of who should be responsible for managing
-zones at a higher level. In future it is more likely that a separate
-zone management framework is provided, and the situation where the
-given zone isn't found in xfrout will be treated as an error.
-
-% XFRIN_ZONE_NO_SOA Zone %1 does not have SOA
-On starting an xfrin session, it is identified that the zone to be
-transferred does not have an SOA RR in the data source. This is not
-necessarily an error; if a secondary DNS server first tries to perform
-transfer from a primary server, the zone can be empty, and therefore
-doesn't have an SOA. Subsequent AXFR will fill in the zone; if the
-attempt is IXFR it will fail in query creation.
-
-% XFRIN_ZONE_MULTIPLE_SOA Zone %1 has %2 SOA RRs
-On starting an xfrin session, it is identified that the zone to be
-transferred has multiple SOA RRs. Such a zone is broken, but could be
-accidentally configured especially in a data source using "non
-captive" backend database. The implementation ignores entire SOA RRs
-and tries to continue processing as if the zone were empty. This
-means subsequent AXFR can succeed and possibly replace the zone with
-valid content, but an IXFR attempt will fail.
-
-% XFRIN_ZONE_SERIAL_AHEAD Serial number (%1) for %2 received from master %3 < ours (%4)
-The response to an SOA query prior to xfr indicated that the zone's
-SOA serial at the primary server is smaller than that of the xfrin
-client. This is not necessarily an error especially if that
-particular primary server is another secondary server which hasn't got
-the latest version of the zone. But if the primary server is known to
-be the real source of the zone, some unexpected inconsistency may have
-happened, and you may want to take a closer look. In this case xfrin
-doesn't perform subsequent zone transfer.
-
-% XFRIN_XFR_OTHER_FAILURE %1 transfer of zone %2 failed: %3
-The XFR transfer for the given zone has failed due to a problem outside
-of the xfrin module. Possible reasons are a broken DNS message or failure
-in database connection. The error is shown in the log message.
-
-% XFRIN_XFR_TRANSFER_PROTOCOL_ERROR %1 transfer of zone %2 with %3 failed: %4
-The XFR transfer for the given zone has failed due to a protocol
-error, such as an unexpected response from the primary server. The
-error is shown in the log message. It may be because the primary
-server implementation is broken or (although less likely) there was
-some attack attempt, but it can also happen due to configuration
-mismatch such as the remote server does not have authority for the
-zone any more but the local configuration hasn't been updated. So it
-is recommended to check the primary server configuration.
-
-% XFRIN_XFR_TRANSFER_FAILURE %1 transfer of zone %2 with %3 failed: %4
-The XFR transfer for the given zone has failed due to an internal error.
-The error is shown in the log message.
-
-% XFRIN_XFR_TRANSFER_FALLBACK falling back from IXFR to AXFR for %1
-The IXFR transfer of the given zone failed. This might happen in many cases,
-such that the remote server doesn't support IXFR, we don't have the SOA record
-(or the zone at all), we are out of sync, etc. In many of these situations,
-AXFR could still work. Therefore we try that one in case it helps.
-
-% XFRIN_XFR_PROCESS_FAILURE %1 transfer of zone %2/%3 failed: %4
-An XFR session failed outside the main protocol handling. This
-includes an error at the data source level at the initialization
-phase, unexpected failure in the network connection setup to the
-master server, or even more unexpected failure due to unlikely events
-such as memory allocation failure. Details of the error are shown in
-the log message. In general, these errors are not really expected
-ones, and indicate an installation error or a program bug. The
-session handler thread tries to clean up all intermediate resources
-even on these errors, but it may be incomplete. So, if this log
-message continuously appears, system resource consumption should be
-checked, and you may even want to disable the corresponding transfers.
-You may also want to file a bug report if this message appears so
-often.
-
-% XFRIN_XFR_TRANSFER_STARTED %1 transfer of zone %2 started
-A connection to the master server has been made, the serial value in
-the SOA record has been checked, and a zone transfer has been started.
-
-% XFRIN_XFR_TRANSFER_SUCCESS %1 transfer of zone %2 succeeded
-The XFR transfer of the given zone was successfully completed.
+% XFRIN_AXFR_INCONSISTENT_SOA AXFR SOAs are inconsistent for %1: %2 expected, %3 received
+The serial fields of the first and last SOAs of AXFR (including AXFR-style
+IXFR) are not the same. According to RFC 5936 these two SOAs must be the
+"same" (not only for the serial), but it is still not clear what the
+receiver should do if this condition does not hold. There was a discussion
+about this at the IETF dnsext wg:
+http://www.ietf.org/mail-archive/web/dnsext/current/msg07908.html
+and the general feeling seems that it would be better to reject the
+transfer if a mismatch is detected. On the other hand, also as noted
+in that email thread, neither BIND 9 nor NSD performs any comparison
+on the SOAs. For now, we only check the serials (ignoring other fields)
+and only leave a warning log message when a mismatch is found. If it
+turns out to happen with a real world primary server implementation
+and that server actually feeds broken data (e.g. mixed versions of
+zone), we can consider a stricter action.
% XFRIN_BAD_MASTER_ADDR_FORMAT bad format for master address: %1
The given master address is not a valid IP address.
@@ -127,6 +56,58 @@ error is given in the log message.
There was an error opening a connection to the master. The error is
shown in the log message.
+% XFRIN_GOT_INCREMENTAL_RESP got incremental response for %1
+In an attempt of IXFR processing, the begenning SOA of the first difference
+(following the initial SOA that specified the final SOA for all the
+differences) was found. This means a connection for xfrin tried IXFR
+and really aot a response for incremental updates.
+
+% XFRIN_GOT_NONINCREMENTAL_RESP got nonincremental response for %1
+Non incremental transfer was detected at the "first data" of a transfer,
+which is the RR following the initial SOA. Non incremental transfer is
+either AXFR or AXFR-style IXFR. In the latter case, it means that
+in a response to IXFR query the first data is not SOA or its SOA serial
+is not equal to the requested SOA serial.
+
+% XFRIN_IMPORT_DNS error importing python DNS module: %1
+There was an error importing the python DNS module pydnspp. The most
+likely cause is a PYTHONPATH problem.
+
+% XFRIN_IXFR_TRANSFER_SUCCESS incremental IXFR transfer of zone %1 succeeded (messages: %2, changesets: %3, deletions: %4, additions: %5, bytes: %6, run time: %7 seconds, %8 bytes/second)
+The IXFR transfer for the given zone was successful.
+The provided information contains the following values:
+
+messages: Number of overhead DNS messages in the transfer.
+
+changesets: Number of difference sequences.
+
+deletions: Number of Resource Records deleted by all the changesets combined,
+including the SOA records.
+
+additions: Number of Resource Records added by all the changesets combined,
+including the SOA records.
+
+bytes: Full size of the transfer data on the wire.
+
+run time: Time (in seconds) the complete ixfr took.
+
+bytes/second: Transfer speed.
+
+Note that there is no cross-checking of additions and deletions; if the same
+RR gets added and deleted in multiple changesets, it is counted each time;
+therefore, for each changeset, there should at least be 1 deletion and 1
+addition (the updated SOA record).
+
+% XFRIN_IXFR_UPTODATE IXFR requested serial for %1 is %2, master has %3, not updating
+The first SOA record in an IXFR response indicates the zone's serial
+at the primary server is not newer than the client's. This is
+basically unexpected event because normally the client first checks
+the SOA serial by an SOA query, but can still happen if the transfer
+is manually invoked or (although unlikely) there is a rapid change at
+the primary server between the SOA and IXFR queries. The client
+implementation confirms the whole response is this single SOA, and
+aborts the transfer just like a successful case.
+
% XFRIN_MSGQ_SEND_ERROR error while contacting %1 and %2
There was a problem sending a message to the xfrout module or the
zone manager. This most likely means that the msgq daemon has quit or
@@ -142,10 +123,6 @@ from does not match the master address in the Xfrin configuration. The notify
is ignored. This may indicate that the configuration for the master is wrong,
that a wrong machine is sending notifies, or that fake notifies are being sent.
-% XFRIN_IMPORT_DNS error importing python DNS module: %1
-There was an error importing the python DNS module pydnspp. The most
-likely cause is a PYTHONPATH problem.
-
% XFRIN_RETRANSFER_UNKNOWN_ZONE got notification to retransfer unknown zone %1
There was an internal command to retransfer the given zone, but the
zone is not known to the system. This may indicate that the configuration
@@ -159,45 +136,105 @@ An informational message, this is output when the resolver starts up.
There was a keyboard interrupt signal to stop the xfrin daemon. The
daemon will now shut down.
+% XFRIN_TRANSFER_SUCCESS full %1 transfer of zone %2 succeeded (messages: %3, records: %4, bytes: %5, run time: %6 seconds, %7 bytes/second)
+The AXFR transfer of the given zone was successful.
+The provided information contains the following values:
+
+messages: Number of overhead DNS messages in the transfer
+
+records: Number of Resource Records in the full transfer, excluding the
+final SOA record that marks the end of the AXFR.
+
+bytes: Full size of the transfer data on the wire.
+
+run time: Time (in seconds) the complete axfr took
+
+bytes/second: Transfer speed
+
% XFRIN_UNKNOWN_ERROR unknown error: %1
An uncaught exception was raised while running the xfrin daemon. The
exception message is printed in the log message.
-% XFRIN_IXFR_UPTODATE IXFR requested serial for %1 is %2, master has %3, not updating
-The first SOA record in an IXFR response indicates the zone's serial
-at the primary server is not newer than the client's. This is
-basically unexpected event because normally the client first checks
-the SOA serial by an SOA query, but can still happen if the transfer
-is manually invoked or (although unlikely) there is a rapid change at
-the primary server between the SOA and IXFR queries. The client
-implementation confirms the whole response is this single SOA, and
-aborts the transfer just like a successful case.
+% XFRIN_XFR_OTHER_FAILURE %1 transfer of zone %2 failed: %3
+The XFR transfer for the given zone has failed due to a problem outside
+of the xfrin module. Possible reasons are a broken DNS message or failure
+in database connection. The error is shown in the log message.
-% XFRIN_GOT_INCREMENTAL_RESP got incremental response for %1
-In an attempt of IXFR processing, the begenning SOA of the first difference
-(following the initial SOA that specified the final SOA for all the
-differences) was found. This means a connection for xfrin tried IXFR
-and really aot a response for incremental updates.
+% XFRIN_XFR_PROCESS_FAILURE %1 transfer of zone %2/%3 failed: %4
+An XFR session failed outside the main protocol handling. This
+includes an error at the data source level at the initialization
+phase, unexpected failure in the network connection setup to the
+master server, or even more unexpected failure due to unlikely events
+such as memory allocation failure. Details of the error are shown in
+the log message. In general, these errors are not really expected
+ones, and indicate an installation error or a program bug. The
+session handler thread tries to clean up all intermediate resources
+even on these errors, but it may be incomplete. So, if this log
+message continuously appears, system resource consumption should be
+checked, and you may even want to disable the corresponding transfers.
+You may also want to file a bug report if this message appears so
+often.
-% XFRIN_GOT_NONINCREMENTAL_RESP got nonincremental response for %1
-Non incremental transfer was detected at the "first data" of a transfer,
-which is the RR following the initial SOA. Non incremental transfer is
-either AXFR or AXFR-style IXFR. In the latter case, it means that
-in a response to IXFR query the first data is not SOA or its SOA serial
-is not equal to the requested SOA serial.
+% XFRIN_XFR_TRANSFER_FAILURE %1 transfer of zone %2 with %3 failed: %4
+The XFR transfer for the given zone has failed due to an internal error.
+The error is shown in the log message.
-% XFRIN_AXFR_INCONSISTENT_SOA AXFR SOAs are inconsistent for %1: %2 expected, %3 received
-The serial fields of the first and last SOAs of AXFR (including AXFR-style
-IXFR) are not the same. According to RFC 5936 these two SOAs must be the
-"same" (not only for the serial), but it is still not clear what the
-receiver should do if this condition does not hold. There was a discussion
-about this at the IETF dnsext wg:
-http://www.ietf.org/mail-archive/web/dnsext/current/msg07908.html
-and the general feeling seems that it would be better to reject the
-transfer if a mismatch is detected. On the other hand, also as noted
-in that email thread, neither BIND 9 nor NSD performs any comparison
-on the SOAs. For now, we only check the serials (ignoring other fields)
-and only leave a warning log message when a mismatch is found. If it
-turns out to happen with a real world primary server implementation
-and that server actually feeds broken data (e.g. mixed versions of
-zone), we can consider a stricter action.
+% XFRIN_XFR_TRANSFER_FALLBACK falling back from IXFR to AXFR for %1
+The IXFR transfer of the given zone failed. This might happen in many cases,
+such that the remote server doesn't support IXFR, we don't have the SOA record
+(or the zone at all), we are out of sync, etc. In many of these situations,
+AXFR could still work. Therefore we try that one in case it helps.
+
+% XFRIN_XFR_TRANSFER_PROTOCOL_ERROR %1 transfer of zone %2 with %3 failed: %4
+The XFR transfer for the given zone has failed due to a protocol
+error, such as an unexpected response from the primary server. The
+error is shown in the log message. It may be because the primary
+server implementation is broken or (although less likely) there was
+some attack attempt, but it can also happen due to configuration
+mismatch such as the remote server does not have authority for the
+zone any more but the local configuration hasn't been updated. So it
+is recommended to check the primary server configuration.
+
+% XFRIN_XFR_TRANSFER_STARTED %1 transfer of zone %2 started
+A connection to the master server has been made, the serial value in
+the SOA record has been checked, and a zone transfer has been started.
+
+% XFRIN_ZONE_CREATED Zone %1 not found in the given data source, newly created
+On starting an xfrin session, it is identified that the zone to be
+transferred is not found in the data source. This can happen if a
+secondary DNS server first tries to perform AXFR from a primary server
+without creating the zone image beforehand (e.g. by b10-loadzone). As
+of this writing the xfrin process provides backward compatible
+behavior to previous versions: creating a new one in the data source
+not to surprise existing users too much. This is probably not a good
+idea, however, in terms of who should be responsible for managing
+zones at a higher level. In future it is more likely that a separate
+zone management framework is provided, and the situation where the
+given zone isn't found in xfrout will be treated as an error.
+
+% XFRIN_ZONE_MULTIPLE_SOA Zone %1 has %2 SOA RRs
+On starting an xfrin session, it is identified that the zone to be
+transferred has multiple SOA RRs. Such a zone is broken, but could be
+accidentally configured especially in a data source using "non
+captive" backend database. The implementation ignores entire SOA RRs
+and tries to continue processing as if the zone were empty. This
+means subsequent AXFR can succeed and possibly replace the zone with
+valid content, but an IXFR attempt will fail.
+
+% XFRIN_ZONE_NO_SOA Zone %1 does not have SOA
+On starting an xfrin session, it is identified that the zone to be
+transferred does not have an SOA RR in the data source. This is not
+necessarily an error; if a secondary DNS server first tries to perform
+transfer from a primary server, the zone can be empty, and therefore
+doesn't have an SOA. Subsequent AXFR will fill in the zone; if the
+attempt is IXFR it will fail in query creation.
+
+% XFRIN_ZONE_SERIAL_AHEAD Serial number (%1) for %2 received from master %3 < ours (%4)
+The response to an SOA query prior to xfr indicated that the zone's
+SOA serial at the primary server is smaller than that of the xfrin
+client. This is not necessarily an error especially if that
+particular primary server is another secondary server which hasn't got
+the latest version of the zone. But if the primary server is known to
+be the real source of the zone, some unexpected inconsistency may have
+happened, and you may want to take a closer look. In this case xfrin
+doesn't perform subsequent zone transfer.
diff --git a/src/bin/xfrout/.gitignore b/src/bin/xfrout/.gitignore
new file mode 100644
index 0000000..2ace679
--- /dev/null
+++ b/src/bin/xfrout/.gitignore
@@ -0,0 +1,5 @@
+/b10-xfrout
+/run_b10-xfrout.sh
+/xfrout.py
+/xfrout.spec
+/xfrout.spec.pre
diff --git a/src/bin/xfrout/tests/.gitignore b/src/bin/xfrout/tests/.gitignore
new file mode 100644
index 0000000..92c94aa
--- /dev/null
+++ b/src/bin/xfrout/tests/.gitignore
@@ -0,0 +1,2 @@
+/xfrout_test
+/xfrout_test.py
diff --git a/src/bin/zonemgr/.gitignore b/src/bin/zonemgr/.gitignore
new file mode 100644
index 0000000..2d64f2d
--- /dev/null
+++ b/src/bin/zonemgr/.gitignore
@@ -0,0 +1,5 @@
+/b10-zonemgr
+/run_b10-zonemgr.sh
+/zonemgr.py
+/zonemgr.spec
+/zonemgr.spec.pre
diff --git a/src/bin/zonemgr/tests/.gitignore b/src/bin/zonemgr/tests/.gitignore
new file mode 100644
index 0000000..41465e0
--- /dev/null
+++ b/src/bin/zonemgr/tests/.gitignore
@@ -0,0 +1 @@
+/zonemgr_test
diff --git a/src/lib/acl/tests/.gitignore b/src/lib/acl/tests/.gitignore
new file mode 100644
index 0000000..d6d1ec8
--- /dev/null
+++ b/src/lib/acl/tests/.gitignore
@@ -0,0 +1 @@
+/run_unittests
diff --git a/src/lib/asiodns/.gitignore b/src/lib/asiodns/.gitignore
new file mode 100644
index 0000000..dedf17e
--- /dev/null
+++ b/src/lib/asiodns/.gitignore
@@ -0,0 +1,2 @@
+/asiodns_messages.cc
+/asiodns_messages.h
diff --git a/src/lib/asiodns/tests/.gitignore b/src/lib/asiodns/tests/.gitignore
new file mode 100644
index 0000000..d6d1ec8
--- /dev/null
+++ b/src/lib/asiodns/tests/.gitignore
@@ -0,0 +1 @@
+/run_unittests
diff --git a/src/lib/asiolink/tests/.gitignore b/src/lib/asiolink/tests/.gitignore
new file mode 100644
index 0000000..d6d1ec8
--- /dev/null
+++ b/src/lib/asiolink/tests/.gitignore
@@ -0,0 +1 @@
+/run_unittests
diff --git a/src/lib/bench/example/.gitignore b/src/lib/bench/example/.gitignore
new file mode 100644
index 0000000..eb3877d
--- /dev/null
+++ b/src/lib/bench/example/.gitignore
@@ -0,0 +1 @@
+/search_bench
diff --git a/src/lib/bench/tests/.gitignore b/src/lib/bench/tests/.gitignore
new file mode 100644
index 0000000..d6d1ec8
--- /dev/null
+++ b/src/lib/bench/tests/.gitignore
@@ -0,0 +1 @@
+/run_unittests
diff --git a/src/lib/cache/.gitignore b/src/lib/cache/.gitignore
new file mode 100644
index 0000000..a33f3f0
--- /dev/null
+++ b/src/lib/cache/.gitignore
@@ -0,0 +1,2 @@
+/cache_messages.cc
+/cache_messages.h
diff --git a/src/lib/cache/tests/.gitignore b/src/lib/cache/tests/.gitignore
new file mode 100644
index 0000000..d6d1ec8
--- /dev/null
+++ b/src/lib/cache/tests/.gitignore
@@ -0,0 +1 @@
+/run_unittests
diff --git a/src/lib/cache/tests/Makefile.am b/src/lib/cache/tests/Makefile.am
index a215c56..b638f55 100644
--- a/src/lib/cache/tests/Makefile.am
+++ b/src/lib/cache/tests/Makefile.am
@@ -47,11 +47,6 @@ run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
run_unittests_LDADD = $(GTEST_LDADD)
-# NOTE: we may have to clean up this hack later (see the note in configure.ac)
-if NEED_LIBBOOST_THREAD
-run_unittests_LDADD += -lboost_thread
-endif
-
run_unittests_LDADD += $(top_builddir)/src/lib/cache/libcache.la
run_unittests_LDADD += $(top_builddir)/src/lib/log/liblog.la
run_unittests_LDADD += $(top_builddir)/src/lib/nsas/libnsas.la
diff --git a/src/lib/cc/.gitignore b/src/lib/cc/.gitignore
new file mode 100644
index 0000000..cb2800f
--- /dev/null
+++ b/src/lib/cc/.gitignore
@@ -0,0 +1,4 @@
+/cc_messages.cc
+/cc_messages.h
+/session_config.h
+/session_config.h.pre
diff --git a/src/lib/cc/tests/.gitignore b/src/lib/cc/tests/.gitignore
new file mode 100644
index 0000000..f10451c
--- /dev/null
+++ b/src/lib/cc/tests/.gitignore
@@ -0,0 +1,2 @@
+/run_unittests
+/session_unittests_config.h
diff --git a/src/lib/config/.gitignore b/src/lib/config/.gitignore
new file mode 100644
index 0000000..c7ec9d3
--- /dev/null
+++ b/src/lib/config/.gitignore
@@ -0,0 +1,2 @@
+/config_messages.cc
+/config_messages.h
diff --git a/src/lib/config/tests/.gitignore b/src/lib/config/tests/.gitignore
new file mode 100644
index 0000000..abdfa8a
--- /dev/null
+++ b/src/lib/config/tests/.gitignore
@@ -0,0 +1,2 @@
+/data_def_unittests_config.h
+/run_unittests
diff --git a/src/lib/config/tests/testdata/.gitignore b/src/lib/config/tests/testdata/.gitignore
new file mode 100644
index 0000000..1c67281
--- /dev/null
+++ b/src/lib/config/tests/testdata/.gitignore
@@ -0,0 +1 @@
+/b10-config.db
diff --git a/src/lib/cryptolink/tests/.gitignore b/src/lib/cryptolink/tests/.gitignore
new file mode 100644
index 0000000..d6d1ec8
--- /dev/null
+++ b/src/lib/cryptolink/tests/.gitignore
@@ -0,0 +1 @@
+/run_unittests
diff --git a/src/lib/datasrc/.gitignore b/src/lib/datasrc/.gitignore
new file mode 100644
index 0000000..05c761e
--- /dev/null
+++ b/src/lib/datasrc/.gitignore
@@ -0,0 +1,4 @@
+/datasrc_messages.cc
+/datasrc_messages.h
+/datasrc_config.h
+/datasrc_config.h.pre
diff --git a/src/lib/datasrc/Makefile.am b/src/lib/datasrc/Makefile.am
index a058b33..dde3a82 100644
--- a/src/lib/datasrc/Makefile.am
+++ b/src/lib/datasrc/Makefile.am
@@ -24,7 +24,7 @@ libdatasrc_la_SOURCES += cache.h cache.cc
libdatasrc_la_SOURCES += rbnode_rrset.h
libdatasrc_la_SOURCES += rbtree.h
libdatasrc_la_SOURCES += zonetable.h zonetable.cc
-libdatasrc_la_SOURCES += zone.h
+libdatasrc_la_SOURCES += zone.h zone_finder_context.cc
libdatasrc_la_SOURCES += result.h
libdatasrc_la_SOURCES += logger.h logger.cc
libdatasrc_la_SOURCES += client.h iterator.h
@@ -35,6 +35,7 @@ nodist_libdatasrc_la_SOURCES = datasrc_messages.h datasrc_messages.cc
pkglib_LTLIBRARIES = sqlite3_ds.la memory_ds.la
sqlite3_ds_la_SOURCES = sqlite3_accessor.h sqlite3_accessor.cc
+sqlite3_ds_la_SOURCES += sqlite3_accessor_link.cc
sqlite3_ds_la_LDFLAGS = -module
sqlite3_ds_la_LDFLAGS += -no-undefined -version-info 1:0:0
sqlite3_ds_la_LIBADD = $(top_builddir)/src/lib/exceptions/libexceptions.la
@@ -42,6 +43,7 @@ sqlite3_ds_la_LIBADD += libdatasrc.la
sqlite3_ds_la_LIBADD += $(SQLITE_LIBS)
memory_ds_la_SOURCES = memory_datasrc.h memory_datasrc.cc
+memory_ds_la_SOURCES += memory_datasrc_link.cc
memory_ds_la_LDFLAGS = -module
memory_ds_la_LIBADD = $(top_builddir)/src/lib/exceptions/libexceptions.la
memory_ds_la_LIBADD += libdatasrc.la
diff --git a/src/lib/datasrc/database.cc b/src/lib/datasrc/database.cc
index 139576a..4e2fb15 100644
--- a/src/lib/datasrc/database.cc
+++ b/src/lib/datasrc/database.cc
@@ -396,15 +396,18 @@ DatabaseClient::Finder::findNSECCover(const Name& name) {
return (ConstRRsetPtr());
}
-ZoneFinder::FindResult
+ZoneFinderContextPtr
DatabaseClient::Finder::findAll(const isc::dns::Name& name,
std::vector<isc::dns::ConstRRsetPtr>& target,
const FindOptions options)
{
- return (findInternal(name, RRType::ANY(), &target, options));
+ return (ZoneFinderContextPtr(new Context(*this, options,
+ findInternal(name, RRType::ANY(),
+ &target, options),
+ target)));
}
-ZoneFinder::FindResult
+ZoneFinderContextPtr
DatabaseClient::Finder::find(const isc::dns::Name& name,
const isc::dns::RRType& type,
const FindOptions options)
@@ -412,7 +415,9 @@ DatabaseClient::Finder::find(const isc::dns::Name& name,
if (type == RRType::ANY()) {
isc_throw(isc::Unexpected, "Use findAll to answer ANY");
}
- return (findInternal(name, type, NULL, options));
+ return (ZoneFinderContextPtr(new Context(*this, options,
+ findInternal(name, type,
+ NULL, options))));
}
DatabaseClient::Finder::DelegationSearchResult
@@ -573,7 +578,7 @@ DatabaseClient::Finder::findDelegationPoint(const isc::dns::Name& name,
// covering NSEC record.
//
// If none of the above applies in any level, the search fails with NXDOMAIN.
-ZoneFinder::FindResult
+ZoneFinder::ResultContext
DatabaseClient::Finder::findWildcardMatch(
const isc::dns::Name& name, const isc::dns::RRType& type,
const FindOptions options, const DelegationSearchResult& dresult,
@@ -616,8 +621,7 @@ DatabaseClient::Finder::findWildcardMatch(
DATASRC_DATABASE_WILDCARD_CANCEL_NS).
arg(accessor_->getDBName()).arg(wildcard).
arg(dresult.first_ns->getName());
- return (FindResult(DELEGATION, dresult.first_ns));
-
+ return (ResultContext(DELEGATION, dresult.first_ns));
} else if (!hasSubdomains(name.split(i - 1).toText())) {
// The wildcard match is the best one, find the final result
// at it. Note that wildcard should never be the zone origin.
@@ -630,7 +634,7 @@ DatabaseClient::Finder::findWildcardMatch(
DATASRC_DATABASE_WILDCARD_CANCEL_SUB).
arg(accessor_->getDBName()).arg(wildcard).
arg(name).arg(superdomain);
- return (FindResult(NXDOMAIN, ConstRRsetPtr()));
+ return (ResultContext(NXDOMAIN, ConstRRsetPtr()));
}
} else if (hasSubdomains(wildcard)) {
@@ -641,19 +645,20 @@ DatabaseClient::Finder::findWildcardMatch(
if ((options & FIND_DNSSEC) != 0) {
ConstRRsetPtr nsec = findNSECCover(Name(wildcard));
if (nsec) {
- return (FindResult(NXRRSET, nsec,
- RESULT_WILDCARD | RESULT_NSEC_SIGNED));
+ return (ResultContext(NXRRSET, nsec,
+ RESULT_WILDCARD |
+ RESULT_NSEC_SIGNED));
}
}
- return (FindResult(NXRRSET, ConstRRsetPtr(), RESULT_WILDCARD));
+ return (ResultContext(NXRRSET, ConstRRsetPtr(), RESULT_WILDCARD));
}
}
// Nothing found at any level.
- return (FindResult(NXDOMAIN, ConstRRsetPtr()));
+ return (ResultContext(NXDOMAIN, ConstRRsetPtr()));
}
-ZoneFinder::FindResult
+ZoneFinder::ResultContext
DatabaseClient::Finder::logAndCreateResult(
const Name& name, const string* wildname, const RRType& type,
ZoneFinder::Result code, ConstRRsetPtr rrset,
@@ -680,10 +685,10 @@ DatabaseClient::Finder::logAndCreateResult(
arg(getClass()).arg(*wildname);
}
}
- return (ZoneFinder::FindResult(code, rrset, flags));
+ return (ResultContext(code, rrset, flags));
}
-ZoneFinder::FindResult
+ZoneFinder::ResultContext
DatabaseClient::Finder::findOnNameResult(const Name& name,
const RRType& type,
const FindOptions options,
@@ -799,7 +804,7 @@ DatabaseClient::Finder::findOnNameResult(const Name& name,
DATASRC_DATABASE_FOUND_NXRRSET, flags));
}
-ZoneFinder::FindResult
+ZoneFinder::ResultContext
DatabaseClient::Finder::findNoNameResult(const Name& name, const RRType& type,
FindOptions options,
const DelegationSearchResult& dresult,
@@ -821,17 +826,17 @@ DatabaseClient::Finder::findNoNameResult(const Name& name, const RRType& type,
arg(accessor_->getDBName()).arg(name);
const ConstRRsetPtr nsec = dnssec_data ? findNSECCover(name) :
ConstRRsetPtr();
- return (FindResult(NXRRSET, nsec,
- nsec ? RESULT_NSEC_SIGNED : RESULT_DEFAULT));
+ return (ResultContext(NXRRSET, nsec,
+ nsec ? RESULT_NSEC_SIGNED : RESULT_DEFAULT));
} else if ((options & NO_WILDCARD) == 0) {
// It's not an empty non-terminal and wildcard matching is not
// disabled, so check for wildcards. If there is a wildcard match
// (i.e. all results except NXDOMAIN) return it; otherwise fall
// through to the NXDOMAIN case below.
- const ZoneFinder::FindResult wresult =
+ const ResultContext wcontext =
findWildcardMatch(name, type, options, dresult, target);
- if (wresult.code != NXDOMAIN) {
- return (wresult);
+ if (wcontext.code != NXDOMAIN) {
+ return (wcontext);
}
}
@@ -841,11 +846,11 @@ DatabaseClient::Finder::findNoNameResult(const Name& name, const RRType& type,
arg(accessor_->getDBName()).arg(name).arg(type).arg(getClass());
const ConstRRsetPtr nsec = dnssec_data ? findNSECCover(name) :
ConstRRsetPtr();
- return (FindResult(NXDOMAIN, nsec,
- nsec ? RESULT_NSEC_SIGNED : RESULT_DEFAULT));
+ return (ResultContext(NXDOMAIN, nsec,
+ nsec ? RESULT_NSEC_SIGNED : RESULT_DEFAULT));
}
-ZoneFinder::FindResult
+ZoneFinder::ResultContext
DatabaseClient::Finder::findInternal(const Name& name, const RRType& type,
std::vector<ConstRRsetPtr>* target,
const FindOptions options)
@@ -860,7 +865,7 @@ DatabaseClient::Finder::findInternal(const Name& name, const RRType& type,
name.compare(getOrigin()).getRelation();
if (reln != NameComparisonResult::SUBDOMAIN &&
reln != NameComparisonResult::EQUAL) {
- return (FindResult(NXDOMAIN, ConstRRsetPtr()));
+ return (ResultContext(NXDOMAIN, ConstRRsetPtr()));
}
// First, go through all superdomains from the origin down, searching for
@@ -877,7 +882,7 @@ DatabaseClient::Finder::findInternal(const Name& name, const RRType& type,
const DelegationSearchResult dresult = findDelegationPoint(name, options);
if (dresult.rrset) {
// In this case no special flags are needed.
- return (FindResult(dresult.code, dresult.rrset));
+ return (ResultContext(dresult.code, dresult.rrset));
}
// If there is no delegation, look for the exact match to the request
@@ -975,7 +980,7 @@ public:
// Find the SOA of the zone (may or may not succeed). Note that
// this must be done before starting the iteration context.
soa_ = DatabaseClient::Finder(accessor_, zone.second, zone_name).
- find(zone_name, RRType::SOA()).rrset;
+ find(zone_name, RRType::SOA())->rrset;
// Request the context
context_ = accessor_->getAllRecords(zone.second);
diff --git a/src/lib/datasrc/database.h b/src/lib/datasrc/database.h
index 4d58401..afd3efb 100644
--- a/src/lib/datasrc/database.h
+++ b/src/lib/datasrc/database.h
@@ -26,7 +26,7 @@
#include <datasrc/data_source.h>
#include <datasrc/client.h>
-#include <datasrc/client.h>
+#include <datasrc/zone.h>
#include <datasrc/logger.h>
#include <dns/name.h>
@@ -738,17 +738,19 @@ public:
/// \param type The RRType to find
/// \param options Options about how to search.
/// See ZoneFinder::FindOptions.
- virtual FindResult find(const isc::dns::Name& name,
- const isc::dns::RRType& type,
- const FindOptions options = FIND_DEFAULT);
+ virtual ZoneFinderContextPtr find(const isc::dns::Name& name,
+ const isc::dns::RRType& type,
+ const FindOptions options =
+ FIND_DEFAULT);
/// \brief Implementation of the ZoneFinder::findAll method.
///
/// In short, it is mostly the same thing as find, but it returns all
/// RRsets in the named node through the target parameter in successful
/// case. It acts the same in the unsuccessful one.
- virtual FindResult findAll(const isc::dns::Name& name,
- std::vector<isc::dns::ConstRRsetPtr>& target,
- const FindOptions options = FIND_DEFAULT);
+ virtual ZoneFinderContextPtr findAll(
+ const isc::dns::Name& name,
+ std::vector<isc::dns::ConstRRsetPtr>& target,
+ const FindOptions options = FIND_DEFAULT);
/// \brief Implementation of ZoneFinder::findPreviousName method.
virtual isc::dns::Name findPreviousName(const isc::dns::Name& query)
@@ -785,6 +787,7 @@ public:
FoundRRsets;
/// \brief Just shortcut for set of types
typedef std::set<dns::RRType> WantedTypes;
+
/// \brief Internal logit of find and findAll methods.
///
/// Most of their handling is in the "error" cases and delegations
@@ -794,10 +797,12 @@ public:
/// Parameters and behaviour is like of those combined together.
/// Unexpected parameters, like type != ANY and having the target, are
/// just that - unexpected and not checked.
- FindResult findInternal(const isc::dns::Name& name,
- const isc::dns::RRType& type,
- std::vector<isc::dns::ConstRRsetPtr>* target,
- const FindOptions options = FIND_DEFAULT);
+ ResultContext findInternal(const isc::dns::Name& name,
+ const isc::dns::RRType& type,
+ std::vector<isc::dns::ConstRRsetPtr>*
+ target,
+ const FindOptions options = FIND_DEFAULT);
+
/// \brief Searches database for RRsets of one domain.
///
/// This method scans RRs of single domain specified by name and
@@ -942,9 +947,10 @@ public:
/// success due to an exact match). Also returned if there
/// is no match is an indication as to whether there was an
/// NXDOMAIN or an NXRRSET.
- FindResult findWildcardMatch(
+ ResultContext findWildcardMatch(
const isc::dns::Name& name,
- const isc::dns::RRType& type, const FindOptions options,
+ const isc::dns::RRType& type,
+ const FindOptions options,
const DelegationSearchResult& dresult,
std::vector<isc::dns::ConstRRsetPtr>* target);
@@ -986,13 +992,14 @@ public:
/// the above 4 cases). The return value is intended to be
/// usable as a return value of the caller of this helper
/// method.
- FindResult findOnNameResult(const isc::dns::Name& name,
- const isc::dns::RRType& type,
- const FindOptions options,
- const bool is_origin,
- const FoundRRsets& found,
- const std::string* wildname,
- std::vector<isc::dns::ConstRRsetPtr>* target);
+ ResultContext findOnNameResult(const isc::dns::Name& name,
+ const isc::dns::RRType& type,
+ const FindOptions options,
+ const bool is_origin,
+ const FoundRRsets& found,
+ const std::string* wildname,
+ std::vector<isc::dns::ConstRRsetPtr>*
+ target);
/// \brief Handle no match for name
///
@@ -1023,12 +1030,12 @@ public:
/// indicating the match type (e.g. CNAME at the wildcard
/// match, no RRs of the requested type at the wildcard,
/// success due to an exact match).
- FindResult findNoNameResult(const isc::dns::Name& name,
- const isc::dns::RRType& type,
- FindOptions options,
- const DelegationSearchResult& dresult,
- std::vector<isc::dns::ConstRRsetPtr>*
- target);
+ ResultContext findNoNameResult(const isc::dns::Name& name,
+ const isc::dns::RRType& type,
+ FindOptions options,
+ const DelegationSearchResult& dresult,
+ std::vector<isc::dns::ConstRRsetPtr>*
+ target);
/// Logs condition and creates result
///
@@ -1051,13 +1058,13 @@ public:
///
/// \return FindResult object constructed from the code and rrset
/// arguments.
- FindResult logAndCreateResult(const isc::dns::Name& name,
- const std::string* wildname,
- const isc::dns::RRType& type,
- ZoneFinder::Result code,
- isc::dns::ConstRRsetPtr rrset,
- const isc::log::MessageID& log_id,
- FindResultFlags flags) const;
+ ResultContext logAndCreateResult(const isc::dns::Name& name,
+ const std::string* wildname,
+ const isc::dns::RRType& type,
+ ZoneFinder::Result code,
+ isc::dns::ConstRRsetPtr rrset,
+ const isc::log::MessageID& log_id,
+ FindResultFlags flags) const;
/// \brief Checks if something lives below this domain.
///
@@ -1150,3 +1157,7 @@ private:
}
#endif // __DATABASE_DATASRC_H
+
+// Local Variables:
+// mode: c++
+// End:
diff --git a/src/lib/datasrc/memory_datasrc.cc b/src/lib/datasrc/memory_datasrc.cc
index e4700c2..eb6e2c5 100644
--- a/src/lib/datasrc/memory_datasrc.cc
+++ b/src/lib/datasrc/memory_datasrc.cc
@@ -12,17 +12,6 @@
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
-#include <algorithm>
-#include <map>
-#include <utility>
-#include <cctype>
-#include <cassert>
-
-#include <boost/shared_ptr.hpp>
-#include <boost/scoped_ptr.hpp>
-#include <boost/bind.hpp>
-#include <boost/foreach.hpp>
-
#include <exceptions/exceptions.h>
#include <dns/name.h>
@@ -40,19 +29,34 @@
#include <datasrc/data_source.h>
#include <datasrc/factory.h>
-#include <cc/data.h>
+#include <boost/shared_ptr.hpp>
+#include <boost/scoped_ptr.hpp>
+#include <boost/bind.hpp>
+#include <boost/foreach.hpp>
+
+#include <algorithm>
+#include <map>
+#include <utility>
+#include <cctype>
+#include <cassert>
using namespace std;
using namespace isc::dns;
using namespace isc::dns::rdata;
-using namespace isc::data;
using boost::scoped_ptr;
namespace isc {
namespace datasrc {
+using namespace internal;
+
namespace {
// Some type aliases
+
+// RRset specified for this implementation
+typedef boost::shared_ptr<internal::RBNodeRRset> RBNodeRRsetPtr;
+typedef boost::shared_ptr<const internal::RBNodeRRset> ConstRBNodeRRsetPtr;
+
/*
* Each domain consists of some RRsets. They will be looked up by the
* RRType.
@@ -64,19 +68,44 @@ namespace {
* critical place and map has better interface for the lookups, so we use
* that.
*/
-typedef map<RRType, ConstRRsetPtr> Domain;
+typedef map<RRType, ConstRBNodeRRsetPtr> Domain;
typedef Domain::value_type DomainPair;
typedef boost::shared_ptr<Domain> DomainPtr;
// The tree stores domains
typedef RBTree<Domain> DomainTree;
typedef RBNode<Domain> DomainNode;
+// In the following dedicated namespace we define a few application-specific
+// RBNode flags. We use a separate namespace so we can consolidate the
+// definition in a single place, which would hopefully reduce the risk of
+// collisions.
+// (Note: it's within an unnamed namespace, so effectively private.)
+namespace domain_flag {
+// This flag indicates the node is at a "wildcard level" (in short, it means
+// one of the node's immediate child is a wildcard). See addWildcards()
+// for more details.
+const DomainNode::Flags WILD = DomainNode::FLAG_USER1;
+
+// This flag is used for additional record shortcut. If a node has this
+// flag, it's under a zone cut for a delegation to a child zone.
+// Note: for a statically built zone this information is stable, but if we
+// change the implementation to be dynamically modifiable, it may not be
+// realistic to keep this flag update for all affected nodes, and we may
+// have to reconsider the mechanism.
+const DomainNode::Flags GLUE = DomainNode::FLAG_USER2;
+};
+
// Separate storage for NSEC3 RRs (and their RRSIGs). It's an STL map
// from string to the NSEC3 RRset. The map key is the first label
// (upper cased) of the owner name of the corresponding NSEC3 (i.e., map
// value). We can use the standard string comparison (if the comparison
// target is also upper cased) due to the nature of NSEC3 owner names.
-typedef map<string, ConstRRsetPtr> NSEC3Map;
+//
+// Note: We maintain the RRsets in the form of RBNodeRRset even if they are
+// not stored in the RB tree. The reason is because comparison can be
+// more efficient if we make sure all RRsets returned from this module are
+// of the same type.
+typedef map<string, ConstRBNodeRRsetPtr> NSEC3Map;
typedef NSEC3Map::value_type NSEC3Pair;
// Actual zone data: Essentially a set of zone's RRs. This is defined as
@@ -110,6 +139,275 @@ struct ZoneData {
};
}
+namespace internal {
+
+/// \brief An encapsulation type for a pointer of an additional node
+/// associated with an \c RBNodeRRset object.
+///
+/// Currently this is defined as a structure only so that it can declared
+/// in rbnode_rrset.h; this is essentially a pointer to \c DomainNode.
+/// In future, however, this structure may have other attributes.
+struct AdditionalNodeInfo {
+ AdditionalNodeInfo(DomainNode* node) : node_(node) {}
+ DomainNode* node_;
+};
+
+//
+// RBNodeRRset details
+//
+struct RBNodeRRsetImpl {
+public:
+ RBNodeRRsetImpl(const ConstRRsetPtr& rrset) : rrset_(rrset)
+ {}
+
+ ConstRRsetPtr rrset_; ///< Underlying RRset
+ scoped_ptr<vector<AdditionalNodeInfo> > additionals_;
+};
+
+RBNodeRRset::RBNodeRRset(const ConstRRsetPtr& rrset) :
+ impl_(new RBNodeRRsetImpl(rrset))
+{
+}
+
+RBNodeRRset::~RBNodeRRset() {
+ delete impl_;
+}
+
+unsigned int
+RBNodeRRset::getRdataCount() const {
+ return (impl_->rrset_->getRdataCount());
+}
+
+const Name&
+RBNodeRRset::getName() const {
+ return (impl_->rrset_->getName());
+}
+
+const RRClass&
+RBNodeRRset::getClass() const {
+ return (impl_->rrset_->getClass());
+}
+
+const RRType&
+RBNodeRRset::getType() const {
+ return (impl_->rrset_->getType());
+}
+
+const RRTTL&
+RBNodeRRset::getTTL() const {
+ return (impl_->rrset_->getTTL());
+}
+
+void
+RBNodeRRset::setName(const Name&) {
+ isc_throw(isc::NotImplemented, "RBNodeRRset::setName() not supported");
+}
+
+void
+RBNodeRRset::setTTL(const RRTTL&) {
+ isc_throw(isc::NotImplemented, "RBNodeRRset::setTTL() not supported");
+}
+
+string
+RBNodeRRset::toText() const {
+ return (impl_->rrset_->toText());
+}
+
+unsigned int
+RBNodeRRset::toWire(AbstractMessageRenderer& renderer) const {
+ return (impl_->rrset_->toWire(renderer));
+}
+
+unsigned int
+RBNodeRRset::toWire(isc::util::OutputBuffer& buffer) const {
+ return (impl_->rrset_->toWire(buffer));
+}
+
+void
+RBNodeRRset::addRdata(ConstRdataPtr) {
+ isc_throw(isc::NotImplemented, "RBNodeRRset::addRdata() not supported");
+}
+
+void
+RBNodeRRset::addRdata(const Rdata&) {
+ isc_throw(isc::NotImplemented, "RBNodeRRset::addRdata() not supported");
+}
+
+RdataIteratorPtr
+RBNodeRRset::getRdataIterator() const {
+ return (impl_->rrset_->getRdataIterator());
+}
+
+RRsetPtr
+RBNodeRRset::getRRsig() const {
+ return (impl_->rrset_->getRRsig());
+}
+
+void
+RBNodeRRset::addRRsig(const ConstRdataPtr& rdata) {
+ AbstractRRset* p = const_cast<AbstractRRset*>(impl_->rrset_.get());
+ p->addRRsig(rdata);
+}
+
+void
+RBNodeRRset::addRRsig(const RdataPtr& rdata) {
+ AbstractRRset* p = const_cast<AbstractRRset*>(impl_->rrset_.get());
+ p->addRRsig(rdata);
+}
+
+void
+RBNodeRRset::addRRsig(const AbstractRRset& sigs) {
+ AbstractRRset* p = const_cast<AbstractRRset*>(impl_->rrset_.get());
+ p->addRRsig(sigs);
+}
+
+void
+RBNodeRRset::addRRsig(const ConstRRsetPtr& sigs) {
+ AbstractRRset* p = const_cast<AbstractRRset*>(impl_->rrset_.get());
+ p->addRRsig(sigs);
+}
+
+void
+RBNodeRRset::addRRsig(const RRsetPtr& sigs) {
+ AbstractRRset* p = const_cast<AbstractRRset*>(impl_->rrset_.get());
+ p->addRRsig(sigs);
+}
+
+void
+RBNodeRRset::removeRRsig() {
+ AbstractRRset* p = const_cast<AbstractRRset*>(impl_->rrset_.get());
+ p->removeRRsig();
+}
+
+ConstRRsetPtr
+RBNodeRRset::getUnderlyingRRset() const {
+ return (impl_->rrset_);
+}
+
+void
+RBNodeRRset::addAdditionalNode(const AdditionalNodeInfo& additional) {
+ // Lazy initialization
+ if (!impl_->additionals_) {
+ impl_->additionals_.reset(new vector<AdditionalNodeInfo>);
+ }
+ impl_->additionals_->push_back(additional);
+}
+
+const vector<AdditionalNodeInfo>*
+RBNodeRRset::getAdditionalNodes() const {
+ return (impl_->additionals_.get());
+}
+
+void
+RBNodeRRset::copyAdditionalNodes(RBNodeRRset& dst) const {
+ if (impl_->additionals_) {
+ dst.impl_->additionals_.reset(
+ new vector<AdditionalNodeInfo>(impl_->additionals_->begin(),
+ impl_->additionals_->end()));
+ }
+}
+
+} // end of internal
+
+namespace {
+// Specialized version of ZoneFinder::ResultContext, which specifically
+// holds rrset in the form of RBNodeRRset.
+struct RBNodeResultContext {
+ /// \brief Constructor
+ ///
+ /// The first three parameters correspond to those of
+ /// ZoneFinder::ResultContext. If node is non NULL, it specifies the
+ /// found RBNode in the search.
+ RBNodeResultContext(ZoneFinder::Result code_param,
+ ConstRBNodeRRsetPtr rrset_param,
+ ZoneFinder::FindResultFlags flags_param,
+ const DomainNode* node) :
+ code(code_param), rrset(rrset_param), flags(flags_param),
+ found_node(node)
+ {}
+
+ const ZoneFinder::Result code;
+ const ConstRBNodeRRsetPtr rrset;
+ const ZoneFinder::FindResultFlags flags;
+ const DomainNode* const found_node;
+};
+}
+
+class InMemoryZoneFinder::Context : public ZoneFinder::Context {
+public:
+ /// \brief Constructor.
+ ///
+ /// Note that we don't have a specific constructor for the findAll() case.
+ /// For (successful) type ANY query, found_node points to the
+ /// corresponding RB node, which is recorded within this specialized
+ /// context.
+ Context(ZoneFinder& finder, ZoneFinder::FindOptions options,
+ const RBNodeResultContext& result) :
+ ZoneFinder::Context(finder, options,
+ ResultContext(result.code, result.rrset,
+ result.flags)),
+ rrset_(result.rrset), found_node_(result.found_node)
+ {}
+
+protected:
+ virtual void getAdditionalImpl(const vector<RRType>& requested_types,
+ vector<ConstRRsetPtr>& result)
+ {
+ if (!rrset_) {
+ // In this case this context should encapsulate the result of
+ // findAll() and found_node_ should point to a valid answer node.
+ if (found_node_ == NULL || found_node_->isEmpty()) {
+ isc_throw(isc::Unexpected,
+ "Invalid call to in-memory getAdditional: caller's "
+ "bug or broken zone");
+ }
+ BOOST_FOREACH(const DomainPair& dom_it, *found_node_->getData()) {
+ getAdditionalForRRset(*dom_it.second, requested_types,
+ result);
+ }
+ } else {
+ getAdditionalForRRset(*rrset_, requested_types, result);
+ }
+ }
+
+private:
+ // Retrieve additional RRsets for a given RRset associated in the context.
+ // The process is straightforward: it examines the link to
+ // AdditionalNodeInfo vector (if set), and find RRsets of the requested
+ // type for each node.
+ static void getAdditionalForRRset(const RBNodeRRset& rrset,
+ const vector<RRType>& requested_types,
+ vector<ConstRRsetPtr>& result)
+ {
+ const vector<AdditionalNodeInfo>* additionals_ =
+ rrset.getAdditionalNodes();
+ if (additionals_ == NULL) {
+ return;
+ }
+ const bool glue_ok = (rrset.getType() == RRType::NS());
+ BOOST_FOREACH(const AdditionalNodeInfo& additional, *additionals_) {
+ assert(additional.node_ != NULL);
+ if (additional.node_->isEmpty()) {
+ continue;
+ }
+ if (!glue_ok && additional.node_->getFlag(domain_flag::GLUE)) {
+ continue;
+ }
+ BOOST_FOREACH(const RRType& rrtype, requested_types) {
+ Domain::const_iterator found =
+ additional.node_->getData()->find(rrtype);
+ if (found != additional.node_->getData()->end()) {
+ // TODO: wildcard consideration
+ result.push_back(found->second);
+ }
+ }
+ }
+ }
+
+ const ConstRBNodeRRsetPtr rrset_;
+ const DomainNode* const found_node_;
+};
+
// Private data and hidden methods of InMemoryZoneFinder
struct InMemoryZoneFinder::InMemoryZoneFinderImpl {
// Constructor
@@ -118,8 +416,6 @@ struct InMemoryZoneFinder::InMemoryZoneFinderImpl {
zone_data_(new ZoneData(origin_))
{}
- static const DomainNode::Flags DOMAINFLAG_WILD = DomainNode::FLAG_USER1;
-
// Information about the zone
RRClass zone_class_;
Name origin_;
@@ -158,7 +454,7 @@ struct InMemoryZoneFinder::InMemoryZoneFinderImpl {
&node));
assert(result == DomainTree::SUCCESS ||
result == DomainTree::ALREADYEXISTS);
- node->setFlag(DOMAINFLAG_WILD);
+ node->setFlag(domain_flag::WILD);
// Ensure a separate level exists for the wildcard name.
// Note: for 'name' itself we do this later anyway, but the
@@ -411,7 +707,8 @@ struct InMemoryZoneFinder::InMemoryZoneFinderImpl {
return (result::EXIST);
}
- zone_data.nsec3_data_->map_.insert(NSEC3Pair(fst_label, rrset));
+ zone_data.nsec3_data_->map_.insert(
+ NSEC3Pair(fst_label, ConstRBNodeRRsetPtr(new RBNodeRRset(rrset))));
return (result::SUCCESS);
}
@@ -420,7 +717,9 @@ struct InMemoryZoneFinder::InMemoryZoneFinderImpl {
* access is without the impl_-> and it will get inlined anyway.
*/
// Implementation of InMemoryZoneFinder::add
- result::Result add(const ConstRRsetPtr& rawrrset, ZoneData& zone_data) {
+ result::Result add(const ConstRRsetPtr& rawrrset, ZoneData& zone_data,
+ vector<RBNodeRRset*>* need_additionals)
+ {
// Sanitize input. This will cause an exception to be thrown
// if the input RRset is empty.
addValidation(rawrrset);
@@ -432,7 +731,7 @@ struct InMemoryZoneFinder::InMemoryZoneFinderImpl {
// ... although instead of loading the RRset directly, we encapsulate
// it within an RBNodeRRset. This contains additional information that
// speeds up queries.
- ConstRRsetPtr rrset(new internal::RBNodeRRset(rawrrset));
+ RBNodeRRsetPtr rrset(new RBNodeRRset(rawrrset));
if (rrset->getType() == RRType::NSEC3()) {
return (addNSEC3(rrset, zone_data));
@@ -489,6 +788,12 @@ struct InMemoryZoneFinder::InMemoryZoneFinderImpl {
node->setFlag(DomainNode::FLAG_CALLBACK);
}
+ if (need_additionals != NULL &&
+ (rrset->getType() == RRType::NS() ||
+ rrset->getType() == RRType::MX())) {
+ need_additionals->push_back(rrset.get());
+ }
+
// If we've added NSEC3PARAM at zone origin, set up NSEC3 specific
// data or check consistency with already set up parameters.
if (rrset->getType() == RRType::NSEC3PARAM() &&
@@ -517,8 +822,10 @@ struct InMemoryZoneFinder::InMemoryZoneFinderImpl {
* Same as above, but it checks the return value and if it already exists,
* it throws.
*/
- void addFromLoad(const ConstRRsetPtr& set, ZoneData* zone_data) {
- switch (add(set, *zone_data)) {
+ void addFromLoad(const ConstRRsetPtr& set, ZoneData* zone_data,
+ vector<RBNodeRRset*>* need_additionals)
+ {
+ switch (add(set, *zone_data, need_additionals)) {
case result::EXIST:
LOG_ERROR(logger, DATASRC_MEM_DUP_RRSET).
arg(set->getName()).arg(set->getType());
@@ -544,7 +851,7 @@ struct InMemoryZoneFinder::InMemoryZoneFinderImpl {
{}
const DomainNode* zonecut_node_;
const DomainNode* dname_node_;
- ConstRRsetPtr rrset_;
+ ConstRBNodeRRsetPtr rrset_;
const FindOptions options_;
};
@@ -617,18 +924,19 @@ struct InMemoryZoneFinder::InMemoryZoneFinderImpl {
* It is designed for wildcard case, where we create the rrsets
* dynamically.
*/
- static ConstRRsetPtr prepareRRset(const Name& name,
- const ConstRRsetPtr& rrset,
- bool rename, FindOptions options)
+ static ConstRBNodeRRsetPtr prepareRRset(const Name& name,
+ const ConstRBNodeRRsetPtr& rrset,
+ bool rename, FindOptions options)
{
if (rename) {
LOG_DEBUG(logger, DBG_TRACE_DETAILED, DATASRC_MEM_RENAME).
arg(rrset->getName()).arg(name);
- RRsetPtr result(new RRset(name, rrset->getClass(),
- rrset->getType(), rrset->getTTL()));
+ RRsetPtr result_base(new RRset(name, rrset->getClass(),
+ rrset->getType(),
+ rrset->getTTL()));
for (RdataIteratorPtr i(rrset->getRdataIterator()); !i->isLast();
i->next()) {
- result->addRdata(i->getCurrent());
+ result_base->addRdata(i->getCurrent());
}
if ((options & FIND_DNSSEC) != 0) {
ConstRRsetPtr sig_rrset = rrset->getRRsig();
@@ -642,21 +950,28 @@ struct InMemoryZoneFinder::InMemoryZoneFinderImpl {
{
result_sig->addRdata(i->getCurrent());
}
- result->addRRsig(result_sig);
+ result_base->addRRsig(result_sig);
}
}
+ RBNodeRRsetPtr result(new RBNodeRRset(result_base));
+ rrset->copyAdditionalNodes(*result);
return (result);
} else {
return (rrset);
}
}
- // Set up FindResult object as a return value of find(), taking into
+ // Set up FindContext object as a return value of find(), taking into
// account wildcard matches and DNSSEC information. We set the NSEC/NSEC3
// flag when applicable regardless of the find option; the caller would
// simply ignore these when they didn't request DNSSEC related results.
- FindResult createFindResult(Result code, ConstRRsetPtr rrset,
- bool wild) const
+ // When the optional parameter 'node' is given (in which case it should be
+ // non NULL), it means it's a result of ANY query and the context should
+ // remember the matched node.
+ RBNodeResultContext createFindResult(Result code,
+ ConstRBNodeRRsetPtr rrset,
+ bool wild = false,
+ const DomainNode* node = NULL) const
{
FindResultFlags flags = RESULT_DEFAULT;
if (wild) {
@@ -666,13 +981,13 @@ struct InMemoryZoneFinder::InMemoryZoneFinderImpl {
zone_data_->nsec3_data_) {
flags = flags | RESULT_NSEC3_SIGNED;
}
- return (FindResult(code, rrset, flags));
+ return (RBNodeResultContext(code, rrset, flags, node));
}
// Implementation of InMemoryZoneFinder::find
- FindResult find(const Name& name, RRType type,
- std::vector<ConstRRsetPtr>* target,
- const FindOptions options) const
+ RBNodeResultContext find(const Name& name, RRType type,
+ std::vector<ConstRRsetPtr>* target,
+ const FindOptions options) const
{
LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_MEM_FIND).arg(name).
arg(type);
@@ -707,15 +1022,16 @@ struct InMemoryZoneFinder::InMemoryZoneFinderImpl {
arg(state.rrset_->getName());
// We were traversing a DNAME node (and wanted to go
// lower below it), so return the DNAME
- return (FindResult(DNAME, prepareRRset(name, state.rrset_,
- false, options)));
+ return (createFindResult(DNAME,
+ prepareRRset(name, state.rrset_,
+ false, options)));
}
if (state.zonecut_node_ != NULL) {
LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_DELEG_FOUND).
arg(state.rrset_->getName());
- return (FindResult(DELEGATION,
- prepareRRset(name, state.rrset_,
- false, options)));
+ return (createFindResult(DELEGATION,
+ prepareRRset(name, state.rrset_,
+ false, options)));
}
// If the RBTree search stopped at a node for a super domain
@@ -725,7 +1041,7 @@ struct InMemoryZoneFinder::InMemoryZoneFinderImpl {
NameComparisonResult::SUPERDOMAIN) {
LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_SUPER_STOP).
arg(name);
- return (createFindResult(NXRRSET, ConstRRsetPtr(), false));
+ return (createFindResult(NXRRSET, ConstRBNodeRRsetPtr()));
}
/*
@@ -736,7 +1052,7 @@ struct InMemoryZoneFinder::InMemoryZoneFinderImpl {
* not match according to 4.3.3 of RFC 1034 (the query name
* is known to exist).
*/
- if (node->getFlag(DOMAINFLAG_WILD)) {
+ if (node->getFlag(domain_flag::WILD)) {
/* Should we cancel this match?
*
* If we compare with some node and get a common ancestor,
@@ -764,7 +1080,8 @@ struct InMemoryZoneFinder::InMemoryZoneFinderImpl {
getLastComparisonResult().getCommonLabels() > 1) {
LOG_DEBUG(logger, DBG_TRACE_DATA,
DATASRC_MEM_WILDCARD_CANCEL).arg(name);
- return (createFindResult(NXDOMAIN, ConstRRsetPtr(),
+ return (createFindResult(NXDOMAIN,
+ ConstRBNodeRRsetPtr(),
false));
}
const Name wildcard(Name("*").concatenate(
@@ -772,7 +1089,7 @@ struct InMemoryZoneFinder::InMemoryZoneFinderImpl {
DomainTree::Result result =
zone_data_->domains_.find(wildcard, &node);
/*
- * Otherwise, why would the DOMAINFLAG_WILD be there if
+ * Otherwise, why would the domain_flag::WILD be there if
* there was no wildcard under it?
*/
assert(result == DomainTree::EXACTMATCH);
@@ -790,7 +1107,8 @@ struct InMemoryZoneFinder::InMemoryZoneFinderImpl {
case DomainTree::NOTFOUND:
LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_NOT_FOUND).
arg(name);
- return (createFindResult(NXDOMAIN, ConstRRsetPtr(), false));
+ return (createFindResult(NXDOMAIN, ConstRBNodeRRsetPtr(),
+ false));
case DomainTree::EXACTMATCH: // This one is OK, handle it
break;
default:
@@ -803,7 +1121,7 @@ struct InMemoryZoneFinder::InMemoryZoneFinderImpl {
if (node->isEmpty()) {
LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_DOMAIN_EMPTY).
arg(name);
- return (createFindResult(NXRRSET, ConstRRsetPtr(), rename));
+ return (createFindResult(NXRRSET, ConstRBNodeRRsetPtr(), rename));
}
Domain::const_iterator found;
@@ -818,9 +1136,9 @@ struct InMemoryZoneFinder::InMemoryZoneFinderImpl {
if (found != node->getData()->end()) {
LOG_DEBUG(logger, DBG_TRACE_DATA,
DATASRC_MEM_EXACT_DELEGATION).arg(name);
- return (FindResult(DELEGATION,
- prepareRRset(name, found->second, rename,
- options)));
+ return (createFindResult(DELEGATION,
+ prepareRRset(name, found->second,
+ rename, options)));
}
}
@@ -835,7 +1153,8 @@ struct InMemoryZoneFinder::InMemoryZoneFinderImpl {
}
LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_ANY_SUCCESS).
arg(name);
- return (createFindResult(SUCCESS, ConstRRsetPtr(), rename));
+ return (createFindResult(SUCCESS, ConstRBNodeRRsetPtr(), rename,
+ node));
}
found = node->getData()->find(type);
@@ -853,15 +1172,15 @@ struct InMemoryZoneFinder::InMemoryZoneFinderImpl {
if (found != node->getData()->end()) {
LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_CNAME).arg(name);
return (createFindResult(CNAME,
- prepareRRset(name, found->second,
- rename, options),
- rename));
+ prepareRRset(name, found->second,
+ rename, options),
+ rename));
}
}
// No exact match or CNAME. Return NXRRSET.
LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_NXRRSET).arg(type).
arg(name);
- return (createFindResult(NXRRSET, ConstRRsetPtr(), rename));
+ return (createFindResult(NXRRSET, ConstRBNodeRRsetPtr(), rename));
}
};
@@ -888,19 +1207,23 @@ InMemoryZoneFinder::getClass() const {
return (impl_->zone_class_);
}
-ZoneFinder::FindResult
+ZoneFinderContextPtr
InMemoryZoneFinder::find(const Name& name, const RRType& type,
- const FindOptions options)
+ const FindOptions options)
{
- return (impl_->find(name, type, NULL, options));
+ return (ZoneFinderContextPtr(
+ new Context(*this, options, impl_->find(name, type, NULL,
+ options))));
}
-ZoneFinder::FindResult
+ZoneFinderContextPtr
InMemoryZoneFinder::findAll(const Name& name,
std::vector<ConstRRsetPtr>& target,
const FindOptions options)
{
- return (impl_->find(name, RRType::ANY(), &target, options));
+ return (ZoneFinderContextPtr(
+ new Context(*this, options, impl_->find(name, RRType::ANY(),
+ &target, options))));
}
ZoneFinder::FindNSEC3Result
@@ -932,7 +1255,7 @@ InMemoryZoneFinder::findNSEC3(const Name& name, bool recursive) {
const unsigned int olabels = impl_->origin_.getLabelCount();
const unsigned int qlabels = name.getLabelCount();
- ConstRRsetPtr covering_proof; // placeholder of the next closer proof
+ ConstRBNodeRRsetPtr covering_proof; // placeholder of the next closer proof
// Examine all names from the query name to the origin name, stripping
// the deepest label one by one, until we find a name that has a matching
// NSEC3 hash.
@@ -982,20 +1305,101 @@ InMemoryZoneFinder::findNSEC3(const Name& name, bool recursive) {
result::Result
InMemoryZoneFinder::add(const ConstRRsetPtr& rrset) {
- return (impl_->add(rrset, *impl_->zone_data_));
+ return (impl_->add(rrset, *impl_->zone_data_, NULL));
}
+namespace {
+// This should eventually be more generalized.
+const Name
+getAdditionalName(RRType rrtype, const rdata::Rdata& rdata) {
+ if (rrtype == RRType::NS()) {
+ const generic::NS& ns = dynamic_cast<const generic::NS&>(rdata);
+ return (ns.getNSName());
+ } else {
+ // In our usage the only other possible case is MX.
+ assert(rrtype == RRType::MX());
+ const generic::MX& mx = dynamic_cast<const generic::MX&>(rdata);
+ return (mx.getMXName());
+ }
+}
+
+bool
+checkZoneCut(const DomainNode& node, pair<bool, bool>* arg) {
+ // We are only interested in the highest zone cut information.
+ // Ignore others and continue the search.
+ if (arg->first) {
+ return (false);
+ }
+ // Once we encounter a delegation point due to a DNAME, anything under it
+ // should be hidden.
+ if (node.getData()->find(RRType::DNAME()) != node.getData()->end()) {
+ return (true);
+ } else if (node.getData()->find(RRType::NS()) != node.getData()->end()) {
+ arg->first = true;
+ arg->second = true;
+ return (false);
+ }
+ return (false);
+}
+
+void
+addAdditional(RBNodeRRset* rrset, ZoneData* zone_data) {
+ RdataIteratorPtr rdata_iterator = rrset->getRdataIterator();
+ for (; !rdata_iterator->isLast(); rdata_iterator->next()) {
+ // For each domain name that requires additional section processing
+ // in each RDATA, search the tree for the name and remember it if
+ // found. If the name is under a zone cut (for a delegation to a
+ // child zone), mark the node as "GLUE", so we can selectively
+ // include/exclude them when we use it.
+
+ // TODO: wildcard
+ RBTreeNodeChain<Domain> node_path;
+ DomainNode* node = NULL;
+ // The callback argument is a pair of bools: the first is a flag to
+ // only check the highest cut; the second one records whether the
+ // search goes under a zone cut.
+ pair<bool, bool> callback_arg(false, false);
+ const DomainTree::Result result =
+ zone_data->domains_.find(
+ getAdditionalName(rrset->getType(),
+ rdata_iterator->getCurrent()),
+ &node, node_path, checkZoneCut, &callback_arg);
+ if (result == DomainTree::EXACTMATCH) {
+ assert(node != NULL);
+ if (callback_arg.second ||
+ (node->getFlag(DomainNode::FLAG_CALLBACK) &&
+ node->getData()->find(RRType::NS()) !=
+ node->getData()->end())) {
+ // The node is under or at a zone cut; mark it as a glue.
+ node->setFlag(domain_flag::GLUE);
+ }
+ // Note that node may be empty. We should keep it in the list
+ // in case we dynamically update the tree and it becomes non empty
+ // (which is not supported yet)
+ rrset->addAdditionalNode(node);
+ }
+ }
+}
+}
void
InMemoryZoneFinder::load(const string& filename) {
LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_MEM_LOAD).arg(getOrigin()).
arg(filename);
- // Load it into temporary zone data
+ // Load it into temporary zone data. As we build the zone, we record
+ // the (RBNode)RRsets that needs to be associated with additional
+ // information in 'need_additionals'.
+ vector<RBNodeRRset*> need_additionals;
scoped_ptr<ZoneData> tmp(new ZoneData(getOrigin()));
masterLoad(filename.c_str(), getOrigin(), getClass(),
boost::bind(&InMemoryZoneFinderImpl::addFromLoad, impl_,
- _1, tmp.get()));
+ _1, tmp.get(), &need_additionals));
+
+ // For each RRset in need_additionals, identify the corresponding
+ // RBnode for additional processing and associate it in the RRset.
+ for_each(need_additionals.begin(), need_additionals.end(),
+ boost::bind(addAdditional, _1, tmp.get()));
// If the zone is NSEC3-signed, check if it has NSEC3PARAM
if (tmp->nsec3_data_) {
@@ -1221,146 +1625,5 @@ InMemoryClient::getJournalReader(const isc::dns::Name&, uint32_t,
"in memory data source");
}
-namespace {
-// convencience function to add an error message to a list of those
-// (TODO: move functions like these to some util lib?)
-void
-addError(ElementPtr errors, const std::string& error) {
- if (errors != ElementPtr() && errors->getType() == Element::list) {
- errors->add(Element::create(error));
- }
-}
-
-/// Check if the given element exists in the map, and if it is a string
-bool
-checkConfigElementString(ConstElementPtr config, const std::string& name,
- ElementPtr errors)
-{
- if (!config->contains(name)) {
- addError(errors,
- "Config for memory backend does not contain a '"
- +name+
- "' value");
- return false;
- } else if (!config->get(name) ||
- config->get(name)->getType() != Element::string) {
- addError(errors, "value of " + name +
- " in memory backend config is not a string");
- return false;
- } else {
- return true;
- }
-}
-
-bool
-checkZoneConfig(ConstElementPtr config, ElementPtr errors) {
- bool result = true;
- if (!config || config->getType() != Element::map) {
- addError(errors, "Elements in memory backend's zone list must be maps");
- result = false;
- } else {
- if (!checkConfigElementString(config, "origin", errors)) {
- result = false;
- }
- if (!checkConfigElementString(config, "file", errors)) {
- result = false;
- }
- // we could add some existence/readabilty/parsability checks here
- // if we want
- }
- return result;
-}
-
-bool
-checkConfig(ConstElementPtr config, ElementPtr errors) {
- /* Specific configuration is under discussion, right now this accepts
- * the 'old' configuration, see [TODO]
- * So for memory datasource, we get a structure like this:
- * { "type": string ("memory"),
- * "class": string ("IN"/"CH"/etc),
- * "zones": list
- * }
- * Zones list is a list of maps:
- * { "origin": string,
- * "file": string
- * }
- *
- * At this moment we cannot be completely sure of the contents of the
- * structure, so we have to do some more extensive tests than should
- * strictly be necessary (e.g. existence and type of elements)
- */
- bool result = true;
-
- if (!config || config->getType() != Element::map) {
- addError(errors, "Base config for memory backend must be a map");
- result = false;
- } else {
- if (!checkConfigElementString(config, "type", errors)) {
- result = false;
- } else {
- if (config->get("type")->stringValue() != "memory") {
- addError(errors,
- "Config for memory backend is not of type \"memory\"");
- result = false;
- }
- }
- if (!checkConfigElementString(config, "class", errors)) {
- result = false;
- } else {
- try {
- RRClass rrc(config->get("class")->stringValue());
- } catch (const isc::Exception& rrce) {
- addError(errors,
- "Error parsing class config for memory backend: " +
- std::string(rrce.what()));
- result = false;
- }
- }
- if (!config->contains("zones")) {
- addError(errors, "No 'zones' element in memory backend config");
- result = false;
- } else if (!config->get("zones") ||
- config->get("zones")->getType() != Element::list) {
- addError(errors, "'zones' element in memory backend config is not a list");
- result = false;
- } else {
- BOOST_FOREACH(ConstElementPtr zone_config,
- config->get("zones")->listValue()) {
- if (!checkZoneConfig(zone_config, errors)) {
- result = false;
- }
- }
- }
- }
-
- return (result);
-}
-
-} // end anonymous namespace
-
-DataSourceClient *
-createInstance(isc::data::ConstElementPtr config, std::string& error) {
- ElementPtr errors(Element::createList());
- if (!checkConfig(config, errors)) {
- error = "Configuration error: " + errors->str();
- return (NULL);
- }
- try {
- return (new InMemoryClient());
- } catch (const std::exception& exc) {
- error = std::string("Error creating memory datasource: ") + exc.what();
- return (NULL);
- } catch (...) {
- error = std::string("Error creating memory datasource, "
- "unknown exception");
- return (NULL);
- }
-}
-
-void destroyInstance(DataSourceClient* instance) {
- delete instance;
-}
-
-
} // end of namespace datasrc
} // end of namespace isc
diff --git a/src/lib/datasrc/memory_datasrc.h b/src/lib/datasrc/memory_datasrc.h
index b960ab9..fbeb2c3 100644
--- a/src/lib/datasrc/memory_datasrc.h
+++ b/src/lib/datasrc/memory_datasrc.h
@@ -70,18 +70,20 @@ public:
/// See documentation in \c Zone.
///
/// It returns NULL pointer in case of NXDOMAIN and NXRRSET.
- virtual FindResult find(const isc::dns::Name& name,
- const isc::dns::RRType& type,
- const FindOptions options = FIND_DEFAULT);
+ virtual ZoneFinderContextPtr find(const isc::dns::Name& name,
+ const isc::dns::RRType& type,
+ const FindOptions options =
+ FIND_DEFAULT);
/// \brief Version of find that returns all types at once
///
/// It acts the same as find, just that when the correct node is found,
/// all the RRsets are filled into the target parameter instead of being
/// returned by the result.
- virtual FindResult findAll(const isc::dns::Name& name,
- std::vector<isc::dns::ConstRRsetPtr>& target,
- const FindOptions options = FIND_DEFAULT);
+ virtual ZoneFinderContextPtr findAll(
+ const isc::dns::Name& name,
+ std::vector<isc::dns::ConstRRsetPtr>& target,
+ const FindOptions options = FIND_DEFAULT);
/// Look for NSEC3 for proving (non)existence of given name.
///
@@ -216,6 +218,12 @@ private:
// extracts the pointer to data and puts it into the iterator.
// The access is read only.
friend class InMemoryClient;
+
+ /// \brief In-memory version of finder context.
+ ///
+ /// The implementation (and any specialized interface) is completely local
+ /// to the InMemoryZoneFinder class, so it's defined as private
+ class Context;
};
/// \brief A data source client that holds all necessary data in memory.
diff --git a/src/lib/datasrc/memory_datasrc_link.cc b/src/lib/datasrc/memory_datasrc_link.cc
new file mode 100644
index 0000000..a0b4bf6
--- /dev/null
+++ b/src/lib/datasrc/memory_datasrc_link.cc
@@ -0,0 +1,173 @@
+// Copyright (C) 2012 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 <cc/data.h>
+
+#include <dns/rrclass.h>
+
+#include <datasrc/client.h>
+#include <datasrc/memory_datasrc.h>
+
+#include <boost/foreach.hpp>
+
+#include <string>
+
+using namespace isc::dns;
+using namespace isc::data;
+
+namespace isc {
+namespace datasrc {
+
+namespace {
+// convencience function to add an error message to a list of those
+// (TODO: move functions like these to some util lib?)
+void
+addError(ElementPtr errors, const std::string& error) {
+ if (errors != ElementPtr() && errors->getType() == Element::list) {
+ errors->add(Element::create(error));
+ }
+}
+
+/// Check if the given element exists in the map, and if it is a string
+bool
+checkConfigElementString(ConstElementPtr config, const std::string& name,
+ ElementPtr errors)
+{
+ if (!config->contains(name)) {
+ addError(errors,
+ "Config for memory backend does not contain a '"
+ +name+
+ "' value");
+ return false;
+ } else if (!config->get(name) ||
+ config->get(name)->getType() != Element::string) {
+ addError(errors, "value of " + name +
+ " in memory backend config is not a string");
+ return false;
+ } else {
+ return true;
+ }
+}
+
+bool
+checkZoneConfig(ConstElementPtr config, ElementPtr errors) {
+ bool result = true;
+ if (!config || config->getType() != Element::map) {
+ addError(errors, "Elements in memory backend's zone list must be maps");
+ result = false;
+ } else {
+ if (!checkConfigElementString(config, "origin", errors)) {
+ result = false;
+ }
+ if (!checkConfigElementString(config, "file", errors)) {
+ result = false;
+ }
+ // we could add some existence/readabilty/parsability checks here
+ // if we want
+ }
+ return result;
+}
+
+bool
+checkConfig(ConstElementPtr config, ElementPtr errors) {
+ /* Specific configuration is under discussion, right now this accepts
+ * the 'old' configuration, see [TODO]
+ * So for memory datasource, we get a structure like this:
+ * { "type": string ("memory"),
+ * "class": string ("IN"/"CH"/etc),
+ * "zones": list
+ * }
+ * Zones list is a list of maps:
+ * { "origin": string,
+ * "file": string
+ * }
+ *
+ * At this moment we cannot be completely sure of the contents of the
+ * structure, so we have to do some more extensive tests than should
+ * strictly be necessary (e.g. existence and type of elements)
+ */
+ bool result = true;
+
+ if (!config || config->getType() != Element::map) {
+ addError(errors, "Base config for memory backend must be a map");
+ result = false;
+ } else {
+ if (!checkConfigElementString(config, "type", errors)) {
+ result = false;
+ } else {
+ if (config->get("type")->stringValue() != "memory") {
+ addError(errors,
+ "Config for memory backend is not of type \"memory\"");
+ result = false;
+ }
+ }
+ if (!checkConfigElementString(config, "class", errors)) {
+ result = false;
+ } else {
+ try {
+ RRClass rrc(config->get("class")->stringValue());
+ } catch (const isc::Exception& rrce) {
+ addError(errors,
+ "Error parsing class config for memory backend: " +
+ std::string(rrce.what()));
+ result = false;
+ }
+ }
+ if (!config->contains("zones")) {
+ addError(errors, "No 'zones' element in memory backend config");
+ result = false;
+ } else if (!config->get("zones") ||
+ config->get("zones")->getType() != Element::list) {
+ addError(errors, "'zones' element in memory backend config is not a list");
+ result = false;
+ } else {
+ BOOST_FOREACH(ConstElementPtr zone_config,
+ config->get("zones")->listValue()) {
+ if (!checkZoneConfig(zone_config, errors)) {
+ result = false;
+ }
+ }
+ }
+ }
+
+ return (result);
+}
+
+} // end unnamed namespace
+
+DataSourceClient *
+createInstance(isc::data::ConstElementPtr config, std::string& error) {
+ ElementPtr errors(Element::createList());
+ if (!checkConfig(config, errors)) {
+ error = "Configuration error: " + errors->str();
+ return (NULL);
+ }
+ try {
+ return (new isc::datasrc::InMemoryClient());
+ } catch (const std::exception& exc) {
+ error = std::string("Error creating memory datasource: ") + exc.what();
+ return (NULL);
+ } catch (...) {
+ error = std::string("Error creating memory datasource, "
+ "unknown exception");
+ return (NULL);
+ }
+}
+
+void destroyInstance(DataSourceClient* instance) {
+ delete instance;
+}
+
+} // end of namespace datasrc
+} // end of namespace isc
diff --git a/src/lib/datasrc/rbnode_rrset.h b/src/lib/datasrc/rbnode_rrset.h
index 968e7a8..3e5d20a 100644
--- a/src/lib/datasrc/rbnode_rrset.h
+++ b/src/lib/datasrc/rbnode_rrset.h
@@ -24,11 +24,27 @@
#include <util/buffer.h>
#include <string>
+#include <vector>
namespace isc {
namespace datasrc {
namespace internal {
+/// \brief The actual content of \c RBNodeRRset
+///
+/// This is defined in the namespace-scope (not hidden in the main class)
+/// so that the In-memory data source implementation can refer to it.
+struct RBNodeRRsetImpl;
+
+// Forward declaration of an opaque data type defined and used within the
+// implementation. This is public only because it needs to be used within
+// the in-memory data source implementation, but conceptually this is a
+// private type for the in-memory data source implementation.
+// Note that the definition of the structure is still hidden within the
+// implementation, so, basically, a normal application should never be able
+// to use it directly even if it peeks into the "internal" namespace.
+struct AdditionalNodeInfo;
+
/// \brief Special RRset for optimizing memory datasource requirement
///
/// To speed up the performance of the in-memory data source, at load time
@@ -84,11 +100,10 @@ public:
/// Creates an RBNodeRRset from the pointer to the RRset passed to it.
///
/// \param rrset Pointer to underlying RRset encapsulated by this object.
- explicit RBNodeRRset(const isc::dns::ConstRRsetPtr& rrset) : rrset_(rrset)
- {}
+ explicit RBNodeRRset(const isc::dns::ConstRRsetPtr& rrset);
/// \brief Destructor
- virtual ~RBNodeRRset() {}
+ virtual ~RBNodeRRset();
// Getter and Setter Methods
//
@@ -96,64 +111,48 @@ public:
// setter methods thrown an exception - this specialisation of the RRset
// object does not expect the underlying RRset to be modified.
- virtual unsigned int getRdataCount() const {
- return (rrset_->getRdataCount());
- }
+ virtual unsigned int getRdataCount() const;
- virtual const isc::dns::Name& getName() const {
- return (rrset_->getName());
- }
+ virtual const isc::dns::Name& getName() const;
- virtual const isc::dns::RRClass& getClass() const {
- return (rrset_->getClass());
- }
+ virtual const isc::dns::RRClass& getClass() const;
- virtual const isc::dns::RRType& getType() const {
- return (rrset_->getType());
- }
+ virtual const isc::dns::RRType& getType() const;
- virtual const isc::dns::RRTTL& getTTL() const {
- return (rrset_->getTTL());
- }
+ virtual const isc::dns::RRTTL& getTTL() const;
- virtual void setName(const isc::dns::Name&) {
- isc_throw(isc::NotImplemented, "RBNodeRRset::setName() not supported");
- }
+ virtual void setName(const isc::dns::Name&);
- virtual void setTTL(const isc::dns::RRTTL&) {
- isc_throw(isc::NotImplemented, "RBNodeRRset::setTTL() not supported");
- }
+ virtual void setTTL(const isc::dns::RRTTL&);
- virtual std::string toText() const {
- return (rrset_->toText());
+ virtual std::string toText() const;
+
+ virtual bool isSameKind(const AbstractRRset& other) const {
+ // This code is an optimisation for comparing
+ // RBNodeRRsets. However, in doing this optimisation,
+ // semantically the code is not "is same kind" but is instead
+ // "is identical object" in the case where RBNodeRRsets are compared.
+
+ const RBNodeRRset* rb = dynamic_cast<const RBNodeRRset*>(&other);
+ if (rb != NULL) {
+ return (this == rb);
+ } else {
+ return (AbstractRRset::isSameKind(other));
+ }
}
virtual unsigned int toWire(
- isc::dns::AbstractMessageRenderer& renderer) const {
- return (rrset_->toWire(renderer));
- }
+ isc::dns::AbstractMessageRenderer& renderer) const;
- virtual unsigned int toWire(isc::util::OutputBuffer& buffer) const {
- return (rrset_->toWire(buffer));
- }
+ virtual unsigned int toWire(isc::util::OutputBuffer& buffer) const;
- virtual void addRdata(isc::dns::rdata::ConstRdataPtr) {
- isc_throw(isc::NotImplemented,
- "RBNodeRRset::addRdata() not supported");
- }
+ virtual void addRdata(isc::dns::rdata::ConstRdataPtr);
- virtual void addRdata(const isc::dns::rdata::Rdata&) {
- isc_throw(isc::NotImplemented,
- "RBNodeRRset::addRdata() not supported");
- }
+ virtual void addRdata(const isc::dns::rdata::Rdata&);
- virtual isc::dns::RdataIteratorPtr getRdataIterator() const {
- return (rrset_->getRdataIterator());
- }
+ virtual isc::dns::RdataIteratorPtr getRdataIterator() const;
- virtual isc::dns::RRsetPtr getRRsig() const {
- return (rrset_->getRRsig());
- }
+ virtual isc::dns::RRsetPtr getRRsig() const;
// With all the RRsig methods, we have the problem that we store the
// underlying RRset using a ConstRRsetPtr - a pointer to a "const" RRset -
@@ -161,45 +160,65 @@ public:
// this by temporarily violating the "const" nature of the RRset to add the
// data.
- virtual void addRRsig(const isc::dns::rdata::ConstRdataPtr& rdata) {
- AbstractRRset* p = const_cast<AbstractRRset*>(rrset_.get());
- p->addRRsig(rdata);
- }
+ virtual void addRRsig(const isc::dns::rdata::ConstRdataPtr& rdata);
- virtual void addRRsig(const isc::dns::rdata::RdataPtr& rdata) {
- AbstractRRset* p = const_cast<AbstractRRset*>(rrset_.get());
- p->addRRsig(rdata);
- }
+ virtual void addRRsig(const isc::dns::rdata::RdataPtr& rdata);
- virtual void addRRsig(const AbstractRRset& sigs) {
- AbstractRRset* p = const_cast<AbstractRRset*>(rrset_.get());
- p->addRRsig(sigs);
- }
+ virtual void addRRsig(const AbstractRRset& sigs);
- virtual void addRRsig(const isc::dns::ConstRRsetPtr& sigs) {
- AbstractRRset* p = const_cast<AbstractRRset*>(rrset_.get());
- p->addRRsig(sigs);
- }
+ virtual void addRRsig(const isc::dns::ConstRRsetPtr& sigs);
- virtual void addRRsig(const isc::dns::RRsetPtr& sigs) {
- AbstractRRset* p = const_cast<AbstractRRset*>(rrset_.get());
- p->addRRsig(sigs);
- }
+ virtual void addRRsig(const isc::dns::RRsetPtr& sigs);
- virtual void removeRRsig() {
- AbstractRRset* p = const_cast<AbstractRRset*>(rrset_.get());
- p->removeRRsig();
- }
+ virtual void removeRRsig();
+
+ /// \brief Associate a link to an RB node of the additional record.
+ ///
+ /// This method adds a given opaque object that holds a link to an RB node
+ /// of the underlying in-memory data source that is corresponding to an
+ /// RDATA of this RRset.
+ ///
+ /// This method is exposed as public so it can be used within the in-memory
+ /// data source implementation, and only for that purpose.
+ ///
+ /// \param additional An opaque \c AdditionalNodeInfo object to be
+ /// associated with this RRset.
+ void addAdditionalNode(const AdditionalNodeInfo& additional);
+
+ /// \brief Return a pointer to the list (vector) of additional RB nodes.
+ ///
+ /// This method returns a pointer to a vector storing the opaque
+ /// \c AdditionalNodeInfo object that may be possibly set in this RRset.
+ /// Not all RRsets are associated with additional nodes; if no
+ /// such node is stored, this method returns NULL.
+ ///
+ /// Like \c addAdditionalNode(), this method is exposed as public only for
+ /// the in-memory data source implementation.
+ ///
+ /// \return A pointer to the associated vector of \c AdditionalNodeInfo;
+ /// NULL if no additional nodes are associated to this RRset.
+ const std::vector<AdditionalNodeInfo>* getAdditionalNodes() const;
+
+ /// \brief Copy the list of additional RB nodes to another RRset.
+ ///
+ /// This method copies the internal list (an STL vector in the actual
+ /// implementation) of additional RB nodes for this RRset to another
+ /// \c RBNodeRRset object. The copy destination is generally expected to
+ /// be newly created and have an empty list, but this method does not
+ /// check the condition. If the destination already has a non empty list,
+ /// the existing entries will be lost.
+ ///
+ /// \param dst The \c RBNodeRRset object to which the additional
+ /// RB node list is to be copied.
+ void copyAdditionalNodes(RBNodeRRset& dst) const;
/// \brief Return underlying RRset pointer
///
/// ... mainly for testing.
- isc::dns::ConstRRsetPtr getUnderlyingRRset() const {
- return (rrset_);
- }
+ isc::dns::ConstRRsetPtr getUnderlyingRRset() const;
private:
- isc::dns::ConstRRsetPtr rrset_; ///< Underlying RRset
+ RBNodeRRsetImpl* impl_;
};
} // namespace internal
diff --git a/src/lib/datasrc/rbtree.h b/src/lib/datasrc/rbtree.h
index 4757a45..161affb 100644
--- a/src/lib/datasrc/rbtree.h
+++ b/src/lib/datasrc/rbtree.h
@@ -123,7 +123,8 @@ public:
/// set to on by the \c setFlag() method.
enum Flags {
FLAG_CALLBACK = 1, ///< Callback enabled. See \ref callback
- FLAG_USER1 = 0x80000000U ///< Application specific flag
+ FLAG_USER1 = 0x80000000U, ///< Application specific flag
+ FLAG_USER2 = 0x40000000U ///< Application specific flag
};
private:
// Some flag values are expected to be used for internal purposes
@@ -131,7 +132,8 @@ private:
// limit the settable flags via the \c setFlag() method to those
// explicitly defined in \c Flags. This constant represents all
// such flags.
- static const uint32_t SETTABLE_FLAGS = (FLAG_CALLBACK | FLAG_USER1);
+ static const uint32_t SETTABLE_FLAGS = (FLAG_CALLBACK | FLAG_USER1 |
+ FLAG_USER2);
public:
diff --git a/src/lib/datasrc/sqlite3_accessor.cc b/src/lib/datasrc/sqlite3_accessor.cc
index fb2ffef..a0afb7f 100644
--- a/src/lib/datasrc/sqlite3_accessor.cc
+++ b/src/lib/datasrc/sqlite3_accessor.cc
@@ -17,8 +17,6 @@
#include <string>
#include <vector>
-#include <boost/foreach.hpp>
-
#include <datasrc/sqlite3_accessor.h>
#include <datasrc/logger.h>
#include <datasrc/data_source.h>
@@ -31,8 +29,6 @@ using namespace isc::data;
#define SQLITE_SCHEMA_VERSION 1
-#define CONFIG_ITEM_DATABASE_FILE "database_file"
-
namespace isc {
namespace datasrc {
@@ -1096,75 +1092,5 @@ SQLite3Accessor::findPreviousName(int zone_id, const std::string& rname)
return (result);
}
-namespace {
-void
-addError(ElementPtr errors, const std::string& error) {
- if (errors != ElementPtr() && errors->getType() == Element::list) {
- errors->add(Element::create(error));
- }
-}
-
-bool
-checkConfig(ConstElementPtr config, ElementPtr errors) {
- /* Specific configuration is under discussion, right now this accepts
- * the 'old' configuration, see header file
- */
- bool result = true;
-
- if (!config || config->getType() != Element::map) {
- addError(errors, "Base config for SQlite3 backend must be a map");
- result = false;
- } else {
- if (!config->contains(CONFIG_ITEM_DATABASE_FILE)) {
- addError(errors,
- "Config for SQlite3 backend does not contain a '"
- CONFIG_ITEM_DATABASE_FILE
- "' value");
- result = false;
- } else if (!config->get(CONFIG_ITEM_DATABASE_FILE) ||
- config->get(CONFIG_ITEM_DATABASE_FILE)->getType() !=
- Element::string) {
- addError(errors, "value of " CONFIG_ITEM_DATABASE_FILE
- " in SQLite3 backend is not a string");
- result = false;
- } else if (config->get(CONFIG_ITEM_DATABASE_FILE)->stringValue() ==
- "") {
- addError(errors, "value of " CONFIG_ITEM_DATABASE_FILE
- " in SQLite3 backend is empty");
- result = false;
- }
- }
-
- return (result);
-}
-
-} // end anonymous namespace
-
-DataSourceClient *
-createInstance(isc::data::ConstElementPtr config, std::string& error) {
- ElementPtr errors(Element::createList());
- if (!checkConfig(config, errors)) {
- error = "Configuration error: " + errors->str();
- return (NULL);
- }
- std::string dbfile = config->get(CONFIG_ITEM_DATABASE_FILE)->stringValue();
- try {
- boost::shared_ptr<DatabaseAccessor> sqlite3_accessor(
- new SQLite3Accessor(dbfile, "IN")); // XXX: avoid hardcode RR class
- return (new DatabaseClient(isc::dns::RRClass::IN(), sqlite3_accessor));
- } catch (const std::exception& exc) {
- error = std::string("Error creating sqlite3 datasource: ") + exc.what();
- return (NULL);
- } catch (...) {
- error = std::string("Error creating sqlite3 datasource, "
- "unknown exception");
- return (NULL);
- }
-}
-
-void destroyInstance(DataSourceClient* instance) {
- delete instance;
-}
-
} // end of namespace datasrc
} // end of namespace isc
diff --git a/src/lib/datasrc/sqlite3_accessor_link.cc b/src/lib/datasrc/sqlite3_accessor_link.cc
new file mode 100644
index 0000000..81ac6b5
--- /dev/null
+++ b/src/lib/datasrc/sqlite3_accessor_link.cc
@@ -0,0 +1,105 @@
+// Copyright (C) 2012 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 <cc/data.h>
+
+#include <dns/rrclass.h>
+
+#include <datasrc/sqlite3_accessor.h>
+#include <datasrc/database.h>
+
+#include <string>
+
+using namespace std;
+using namespace isc::dns;
+using namespace isc::data;
+
+namespace isc {
+namespace datasrc {
+
+namespace {
+
+const char* const CONFIG_ITEM_DATABASE_FILE = "database_file";
+
+void
+addError(ElementPtr errors, const std::string& error) {
+ if (errors != ElementPtr() && errors->getType() == Element::list) {
+ errors->add(Element::create(error));
+ }
+}
+
+bool
+checkConfig(ConstElementPtr config, ElementPtr errors) {
+ /* Specific configuration is under discussion, right now this accepts
+ * the 'old' configuration, see header file
+ */
+ bool result = true;
+
+ if (!config || config->getType() != Element::map) {
+ addError(errors, "Base config for SQlite3 backend must be a map");
+ result = false;
+ } else {
+ if (!config->contains(CONFIG_ITEM_DATABASE_FILE)) {
+ addError(errors,
+ "Config for SQlite3 backend does not contain a '" +
+ string(CONFIG_ITEM_DATABASE_FILE) +
+ "' value");
+ result = false;
+ } else if (!config->get(CONFIG_ITEM_DATABASE_FILE) ||
+ config->get(CONFIG_ITEM_DATABASE_FILE)->getType() !=
+ Element::string) {
+ addError(errors, "value of " + string(CONFIG_ITEM_DATABASE_FILE) +
+ " in SQLite3 backend is not a string");
+ result = false;
+ } else if (config->get(CONFIG_ITEM_DATABASE_FILE)->stringValue() ==
+ "") {
+ addError(errors, "value of " + string(CONFIG_ITEM_DATABASE_FILE) +
+ " in SQLite3 backend is empty");
+ result = false;
+ }
+ }
+
+ return (result);
+}
+
+} // end unnamed namespace
+
+DataSourceClient *
+createInstance(isc::data::ConstElementPtr config, std::string& error) {
+ ElementPtr errors(Element::createList());
+ if (!checkConfig(config, errors)) {
+ error = "Configuration error: " + errors->str();
+ return (NULL);
+ }
+ std::string dbfile = config->get(CONFIG_ITEM_DATABASE_FILE)->stringValue();
+ try {
+ boost::shared_ptr<DatabaseAccessor> sqlite3_accessor(
+ new SQLite3Accessor(dbfile, "IN")); // XXX: avoid hardcode RR class
+ return (new DatabaseClient(isc::dns::RRClass::IN(), sqlite3_accessor));
+ } catch (const std::exception& exc) {
+ error = std::string("Error creating sqlite3 datasource: ") + exc.what();
+ return (NULL);
+ } catch (...) {
+ error = std::string("Error creating sqlite3 datasource, "
+ "unknown exception");
+ return (NULL);
+ }
+}
+
+void destroyInstance(DataSourceClient* instance) {
+ delete instance;
+}
+
+} // end of namespace datasrc
+} // end of namespace isc
diff --git a/src/lib/datasrc/tests/.gitignore b/src/lib/datasrc/tests/.gitignore
new file mode 100644
index 0000000..bae5d90
--- /dev/null
+++ b/src/lib/datasrc/tests/.gitignore
@@ -0,0 +1,4 @@
+/run_unittests
+/run_unittests_factory
+/run_unittests_memory
+/run_unittests_sqlite3
diff --git a/src/lib/datasrc/tests/Makefile.am b/src/lib/datasrc/tests/Makefile.am
index 8eaf9ab..c8ffa58 100644
--- a/src/lib/datasrc/tests/Makefile.am
+++ b/src/lib/datasrc/tests/Makefile.am
@@ -20,17 +20,13 @@ CLEANFILES = *.gcno *.gcda
TESTS =
noinst_PROGRAMS =
if HAVE_GTEST
-TESTS += run_unittests run_unittests_sqlite3 run_unittests_memory
+TESTS += run_unittests
-#
-# For each specific datasource, there is a separate binary that includes
-# the code itself (we can't unittest through the public API). These need
-# to be separate because the included code, by design, contains conflicting
-# symbols.
-# We also have a 'general' run_unittests with non-datasource-specific tests
-#
+# We have two sets of tests: the general tests and factory tests (see below
+# for the latter). They are separate binary files sharing some program files
+# and libraries.
-# First define the parts shared by all
+# First define the parts shared by both
common_sources = run_unittests.cc
common_sources += $(top_srcdir)/src/lib/dns/tests/unittest_util.h
common_sources += $(top_srcdir)/src/lib/dns/tests/unittest_util.cc
@@ -46,7 +42,6 @@ common_ldadd += $(top_builddir)/src/lib/cc/libcc.la
common_ldadd += $(top_builddir)/src/lib/testutils/libtestutils.la
common_ldadd += $(top_builddir)/src/lib/util/unittests/libutil_unittests.la
-
# The general tests
run_unittests_SOURCES = $(common_sources)
run_unittests_SOURCES += datasrc_unittest.cc
@@ -57,36 +52,23 @@ run_unittests_SOURCES += test_datasrc.h test_datasrc.cc
run_unittests_SOURCES += rbtree_unittest.cc
run_unittests_SOURCES += logger_unittest.cc
run_unittests_SOURCES += client_unittest.cc
+run_unittests_SOURCES += database_unittest.cc
+run_unittests_SOURCES += sqlite3_unittest.cc
+run_unittests_SOURCES += sqlite3_accessor_unittest.cc
+run_unittests_SOURCES += memory_datasrc_unittest.cc
+run_unittests_SOURCES += rbnode_rrset_unittest.cc
+run_unittests_SOURCES += zone_finder_context_unittest.cc
+
+# We need the actual module implementation in the tests (they are not part
+# of libdatasrc)
+run_unittests_SOURCES += $(top_srcdir)/src/lib/datasrc/sqlite3_accessor.cc
+run_unittests_SOURCES += $(top_srcdir)/src/lib/datasrc/memory_datasrc.cc
run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
run_unittests_LDADD = $(common_ldadd)
-
-# SQlite3 datasource tests
-run_unittests_sqlite3_SOURCES = $(common_sources)
-run_unittests_sqlite3_SOURCES += database_unittest.cc
-run_unittests_sqlite3_SOURCES += sqlite3_unittest.cc
-run_unittests_sqlite3_SOURCES += sqlite3_accessor_unittest.cc
-run_unittests_sqlite3_SOURCES += $(top_srcdir)/src/lib/datasrc/sqlite3_accessor.cc
-
-run_unittests_sqlite3_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
-run_unittests_sqlite3_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
-
-run_unittests_sqlite3_LDADD = $(common_ldadd)
-
-# In-memory datasource tests
-run_unittests_memory_SOURCES = $(common_sources)
-run_unittests_memory_SOURCES += memory_datasrc_unittest.cc
-run_unittests_memory_SOURCES += rbnode_rrset_unittest.cc
-run_unittests_memory_SOURCES += $(top_srcdir)/src/lib/datasrc/memory_datasrc.cc
-
-run_unittests_memory_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
-run_unittests_memory_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
-
-run_unittests_memory_LDADD = $(common_ldadd)
-
noinst_PROGRAMS+= $(TESTS)
# For the factory unit tests, we need to specify that we want
@@ -110,6 +92,7 @@ endif
endif
EXTRA_DIST = testdata/brokendb.sqlite3
+EXTRA_DIST += testdata/contexttest.zone
EXTRA_DIST += testdata/diffs.sqlite3
EXTRA_DIST += testdata/example2.com
EXTRA_DIST += testdata/example2.com.sqlite3
diff --git a/src/lib/datasrc/tests/database_unittest.cc b/src/lib/datasrc/tests/database_unittest.cc
index 408913a..758095b 100644
--- a/src/lib/datasrc/tests/database_unittest.cc
+++ b/src/lib/datasrc/tests/database_unittest.cc
@@ -1364,7 +1364,7 @@ TYPED_TEST(DatabaseClientTest, iterateThenUpdate) {
// Confirm at least it doesn't contain any SOA
EXPECT_EQ(ZoneFinder::NXDOMAIN,
- this->getFinder()->find(this->zname_, RRType::SOA()).code);
+ this->getFinder()->find(this->zname_, RRType::SOA())->code);
} catch (const DataSourceError&) {}
ConstRRsetPtr rrset;
@@ -1422,31 +1422,31 @@ doFindTest(ZoneFinder& finder,
const ZoneFinder::FindOptions options = ZoneFinder::FIND_DEFAULT)
{
SCOPED_TRACE("doFindTest " + name.toText() + " " + type.toText());
- const ZoneFinder::FindResult result = finder.find(name, type, options);
- ASSERT_EQ(expected_result, result.code) << name << " " << type;
+ ConstZoneFinderContextPtr result = finder.find(name, type, options);
+ ASSERT_EQ(expected_result, result->code) << name << " " << type;
EXPECT_EQ((expected_flags & ZoneFinder::RESULT_WILDCARD) != 0,
- result.isWildcard());
+ result->isWildcard());
EXPECT_EQ((expected_flags & ZoneFinder::RESULT_NSEC_SIGNED) != 0,
- result.isNSECSigned());
+ result->isNSECSigned());
EXPECT_EQ((expected_flags & ZoneFinder::RESULT_NSEC3_SIGNED) != 0,
- result.isNSEC3Signed());
- if (!expected_rdatas.empty() && result.rrset) {
- checkRRset(result.rrset, expected_name != Name(".") ? expected_name :
+ result->isNSEC3Signed());
+ if (!expected_rdatas.empty() && result->rrset) {
+ checkRRset(result->rrset, expected_name != Name(".") ? expected_name :
name, finder.getClass(), expected_type, expected_ttl,
expected_rdatas);
- if (!expected_sig_rdatas.empty() && result.rrset->getRRsig()) {
- checkRRset(result.rrset->getRRsig(), expected_name != Name(".") ?
+ if (!expected_sig_rdatas.empty() && result->rrset->getRRsig()) {
+ checkRRset(result->rrset->getRRsig(), expected_name != Name(".") ?
expected_name : name, finder.getClass(),
isc::dns::RRType::RRSIG(), expected_ttl,
expected_sig_rdatas);
} else if (expected_sig_rdatas.empty()) {
- EXPECT_EQ(isc::dns::RRsetPtr(), result.rrset->getRRsig());
+ EXPECT_EQ(isc::dns::RRsetPtr(), result->rrset->getRRsig());
} else {
ADD_FAILURE() << "Missing RRSIG";
}
} else if (expected_rdatas.empty()) {
- EXPECT_EQ(isc::dns::RRsetPtr(), result.rrset);
+ EXPECT_EQ(isc::dns::RRsetPtr(), result->rrset);
} else {
ADD_FAILURE() << "Missing result";
}
@@ -1464,11 +1464,11 @@ doFindAllTestResult(ZoneFinder& finder, const isc::dns::Name& name,
{
SCOPED_TRACE("All test for " + name.toText());
std::vector<ConstRRsetPtr> target;
- ZoneFinder::FindResult result(finder.findAll(name, target, options));
+ ConstZoneFinderContextPtr result(finder.findAll(name, target, options));
EXPECT_TRUE(target.empty());
- EXPECT_EQ(expected_result, result.code);
- EXPECT_EQ(expected_type, result.rrset->getType());
- RdataIteratorPtr it(result.rrset->getRdataIterator());
+ EXPECT_EQ(expected_result, result->code);
+ EXPECT_EQ(expected_type, result->rrset->getType());
+ RdataIteratorPtr it(result->rrset->getRdataIterator());
std::vector<std::string> rdata;
while (!it->isLast()) {
rdata.push_back(it->getCurrent().toText());
@@ -1482,7 +1482,7 @@ doFindAllTestResult(ZoneFinder& finder, const isc::dns::Name& name,
}
EXPECT_TRUE(expected_rdata == rdata);
EXPECT_EQ(expected_name == isc::dns::Name::ROOT_NAME() ? name :
- expected_name, result.rrset->getName());
+ expected_name, result->rrset->getName());
}
// When asking for an RRset where RRs somehow have different TTLs, it should
@@ -1787,30 +1787,30 @@ TYPED_TEST(DatabaseClientTest, findOutOfZone) {
doFindTest(*finder, Name("org"), this->qtype_, this->qtype_,
this->rrttl_, ZoneFinder::NXDOMAIN,
this->empty_rdatas_, this->empty_rdatas_);
- EXPECT_EQ(ZoneFinder::NXDOMAIN, finder->findAll(Name("org"), target).code);
+ EXPECT_EQ(ZoneFinder::NXDOMAIN, finder->findAll(Name("org"), target)->code);
// sharing a common ancestor
doFindTest(*finder, Name("noexample.org"), this->qtype_, this->qtype_,
this->rrttl_, ZoneFinder::NXDOMAIN,
this->empty_rdatas_, this->empty_rdatas_);
EXPECT_EQ(ZoneFinder::NXDOMAIN, finder->findAll(Name("noexample.org"),
- target).code);
+ target)->code);
// totally unrelated domain, smaller number of labels
doFindTest(*finder, Name("com"), this->qtype_, this->qtype_,
this->rrttl_, ZoneFinder::NXDOMAIN,
this->empty_rdatas_, this->empty_rdatas_);
- EXPECT_EQ(ZoneFinder::NXDOMAIN, finder->findAll(Name("com"), target).code);
+ EXPECT_EQ(ZoneFinder::NXDOMAIN, finder->findAll(Name("com"), target)->code);
// totally unrelated domain, same number of labels
doFindTest(*finder, Name("example.com"), this->qtype_, this->qtype_,
this->rrttl_, ZoneFinder::NXDOMAIN,
this->empty_rdatas_, this->empty_rdatas_);
EXPECT_EQ(ZoneFinder::NXDOMAIN, finder->findAll(Name("example.com"),
- target).code);
+ target)->code);
// totally unrelated domain, larger number of labels
doFindTest(*finder, Name("more.example.com"), this->qtype_, this->qtype_,
this->rrttl_, ZoneFinder::NXDOMAIN,
this->empty_rdatas_, this->empty_rdatas_);
EXPECT_EQ(ZoneFinder::NXDOMAIN, finder->findAll(Name("more.example.com"),
- target).code);
+ target)->code);
}
TYPED_TEST(DatabaseClientTest, findDelegation) {
@@ -2363,11 +2363,11 @@ TYPED_TEST(DatabaseClientTest, getAll) {
std::vector<ConstRRsetPtr> target;
EXPECT_EQ(ZoneFinder::NXDOMAIN,
finder->findAll(isc::dns::Name("nothere.example.org."),
- target).code);
+ target)->code);
EXPECT_TRUE(target.empty());
EXPECT_EQ(ZoneFinder::NXRRSET,
finder->findAll(isc::dns::Name("here.wild.example.org."),
- target).code);
+ target)->code);
this->expected_rdatas_.push_back("ns.delegation.example.org.");
this->expected_rdatas_.push_back("ns.example.com.");
doFindAllTestResult(*finder, isc::dns::Name("xx.delegation.example.org."),
@@ -2388,7 +2388,7 @@ TYPED_TEST(DatabaseClientTest, getAll) {
// It should get the data on success
EXPECT_EQ(ZoneFinder::SUCCESS,
finder->findAll(isc::dns::Name("www2.example.org."),
- target).code);
+ target)->code);
ASSERT_EQ(2, target.size());
size_t a_idx(target[1]->getType() == RRType::A());
EXPECT_EQ(RRType::A(), target[a_idx]->getType());
@@ -2412,13 +2412,13 @@ TYPED_TEST(DatabaseClientTest, getAll) {
// And on wildcard. Check the signatures as well.
target.clear();
- const ZoneFinder::FindResult result =
+ ConstZoneFinderContextPtr result =
finder->findAll(Name("a.wild.example.org"), target,
ZoneFinder::FIND_DNSSEC);
- EXPECT_EQ(ZoneFinder::SUCCESS, result.code);
- EXPECT_TRUE(result.isWildcard());
- EXPECT_TRUE(result.isNSECSigned());
- EXPECT_FALSE(result.isNSEC3Signed());
+ EXPECT_EQ(ZoneFinder::SUCCESS, result->code);
+ EXPECT_TRUE(result->isWildcard());
+ EXPECT_TRUE(result->isNSECSigned());
+ EXPECT_FALSE(result->isNSEC3Signed());
ASSERT_EQ(2, target.size());
a_idx = target[1]->getType() == RRType::A();
EXPECT_EQ(RRType::A(), target[a_idx]->getType());
@@ -2496,22 +2496,22 @@ TYPED_TEST(DatabaseClientTest, flushZone) {
// Before update, the name exists.
EXPECT_EQ(ZoneFinder::SUCCESS, finder->find(this->qname_,
- this->qtype_).code);
+ this->qtype_)->code);
// start update in the replace mode. the normal finder should still
// be able to see the record, but the updater's finder shouldn't.
this->updater_ = this->client_->getUpdater(this->zname_, true);
this->setUpdateAccessor();
EXPECT_EQ(ZoneFinder::SUCCESS,
- finder->find(this->qname_, this->qtype_).code);
+ finder->find(this->qname_, this->qtype_)->code);
EXPECT_EQ(ZoneFinder::NXDOMAIN,
this->updater_->getFinder().find(this->qname_,
- this->qtype_).code);
+ this->qtype_)->code);
// commit the update. now the normal finder shouldn't see it.
this->updater_->commit();
EXPECT_EQ(ZoneFinder::NXDOMAIN, finder->find(this->qname_,
- this->qtype_).code);
+ this->qtype_)->code);
// Check rollback wasn't accidentally performed.
EXPECT_FALSE(this->isRollbacked());
@@ -2522,13 +2522,13 @@ TYPED_TEST(DatabaseClientTest, updateCancel) {
ZoneFinderPtr finder = this->client_->findZone(this->zname_).zone_finder;
EXPECT_EQ(ZoneFinder::SUCCESS, finder->find(this->qname_,
- this->qtype_).code);
+ this->qtype_)->code);
this->updater_ = this->client_->getUpdater(this->zname_, true);
this->setUpdateAccessor();
EXPECT_EQ(ZoneFinder::NXDOMAIN,
this->updater_->getFinder().find(this->qname_,
- this->qtype_).code);
+ this->qtype_)->code);
// DB should not have been rolled back yet.
EXPECT_FALSE(this->isRollbacked());
this->updater_.reset(); // destruct without commit
@@ -2538,7 +2538,7 @@ TYPED_TEST(DatabaseClientTest, updateCancel) {
// isRollbacked())
EXPECT_TRUE(this->isRollbacked(true));
EXPECT_EQ(ZoneFinder::SUCCESS, finder->find(this->qname_,
- this->qtype_).code);
+ this->qtype_)->code);
}
TYPED_TEST(DatabaseClientTest, exceptionFromRollback) {
diff --git a/src/lib/datasrc/tests/memory_datasrc_unittest.cc b/src/lib/datasrc/tests/memory_datasrc_unittest.cc
index 92d3a9a..9096a9e 100644
--- a/src/lib/datasrc/tests/memory_datasrc_unittest.cc
+++ b/src/lib/datasrc/tests/memory_datasrc_unittest.cc
@@ -553,33 +553,33 @@ public:
// The whole block is inside, because we need to check the result and
// we can't assign to FindResult
EXPECT_NO_THROW({
- ZoneFinder::FindResult find_result(zone_finder->find(
- name, rrtype, options));
+ ZoneFinderContextPtr find_result(zone_finder->find(
+ name, rrtype, options));
// Check it returns correct answers
- EXPECT_EQ(result, find_result.code);
+ EXPECT_EQ(result, find_result->code);
EXPECT_EQ((expected_flags & ZoneFinder::RESULT_WILDCARD) != 0,
- find_result.isWildcard());
+ find_result->isWildcard());
EXPECT_EQ((expected_flags & ZoneFinder::RESULT_NSEC_SIGNED)
- != 0, find_result.isNSECSigned());
+ != 0, find_result->isNSECSigned());
EXPECT_EQ((expected_flags & ZoneFinder::RESULT_NSEC3_SIGNED)
- != 0, find_result.isNSEC3Signed());
+ != 0, find_result->isNSEC3Signed());
if (check_answer) {
if (!answer) {
- ASSERT_FALSE(find_result.rrset);
+ ASSERT_FALSE(find_result->rrset);
} else {
- ASSERT_TRUE(find_result.rrset);
- rrsetCheck(answer, find_result.rrset);
+ ASSERT_TRUE(find_result->rrset);
+ rrsetCheck(answer, find_result->rrset);
if (answer_sig) {
- ASSERT_TRUE(find_result.rrset->getRRsig());
+ ASSERT_TRUE(find_result->rrset->getRRsig());
rrsetCheck(answer_sig,
- find_result.rrset->getRRsig());
+ find_result->rrset->getRRsig());
}
}
} else if (check_wild_answer) {
ASSERT_NE(ConstRRsetPtr(), answer) <<
"Wrong test, don't check for wild names if you expect "
"empty answer";
- ASSERT_NE(ConstRRsetPtr(), find_result.rrset) <<
+ ASSERT_NE(ConstRRsetPtr(), find_result->rrset) <<
"No answer found";
// Build the expected answer using the given name and
// other parameter of the base wildcard RRset.
@@ -590,11 +590,11 @@ public:
for (; !expectedIt->isLast(); expectedIt->next()) {
wildanswer->addRdata(expectedIt->getCurrent());
}
- rrsetCheck(wildanswer, find_result.rrset);
+ rrsetCheck(wildanswer, find_result->rrset);
// Same for the RRSIG, if any.
if (answer_sig) {
- ASSERT_TRUE(find_result.rrset->getRRsig());
+ ASSERT_TRUE(find_result->rrset->getRRsig());
RRsetPtr wildsig(new RRset(name,
answer_sig->getClass(),
@@ -605,7 +605,7 @@ public:
for (; !expectedIt->isLast(); expectedIt->next()) {
wildsig->addRdata(expectedIt->getCurrent());
}
- rrsetCheck(wildsig, find_result.rrset->getRRsig());
+ rrsetCheck(wildsig, find_result->rrset->getRRsig());
}
}
});
@@ -626,21 +626,21 @@ public:
finder = &zone_finder_;
}
std::vector<ConstRRsetPtr> target;
- ZoneFinder::FindResult find_result(finder->findAll(name, target,
- options));
- EXPECT_EQ(result, find_result.code);
+ ZoneFinderContextPtr find_result(finder->findAll(name, target,
+ options));
+ EXPECT_EQ(result, find_result->code);
if (!rrset_result) {
- EXPECT_FALSE(find_result.rrset);
+ EXPECT_FALSE(find_result->rrset);
} else {
- ASSERT_TRUE(find_result.rrset);
- rrsetCheck(rrset_result, find_result.rrset);
+ ASSERT_TRUE(find_result->rrset);
+ rrsetCheck(rrset_result, find_result->rrset);
}
EXPECT_EQ((expected_flags & ZoneFinder::RESULT_WILDCARD) != 0,
- find_result.isWildcard());
+ find_result->isWildcard());
EXPECT_EQ((expected_flags & ZoneFinder::RESULT_NSEC_SIGNED)
- != 0, find_result.isNSECSigned());
+ != 0, find_result->isNSECSigned());
EXPECT_EQ((expected_flags & ZoneFinder::RESULT_NSEC3_SIGNED)
- != 0, find_result.isNSEC3Signed());
+ != 0, find_result->isNSEC3Signed());
rrsetsCheck(expected_rrsets.begin(), expected_rrsets.end(),
target.begin(), target.end());
}
@@ -1552,37 +1552,35 @@ TEST_F(InMemoryZoneFinderTest, addRRsig) {
// that covers the first RRset
zone_finder_.add(rr_a_);
zone_finder_.add(textToRRset(rrsig_a_txt));
- ZoneFinder::FindResult result = zone_finder_.find(origin_, RRType::A(),
- ZoneFinder::FIND_DNSSEC);
- EXPECT_EQ(ZoneFinder::SUCCESS, result.code);
- ASSERT_TRUE(result.rrset);
- ASSERT_TRUE(result.rrset->getRRsig());
- actual_rrsets_.push_back(result.rrset->getRRsig());
+ ZoneFinderContextPtr result = zone_finder_.find(origin_, RRType::A(),
+ ZoneFinder::FIND_DNSSEC);
+ EXPECT_EQ(ZoneFinder::SUCCESS, result->code);
+ ASSERT_TRUE(result->rrset);
+ ASSERT_TRUE(result->rrset->getRRsig());
+ actual_rrsets_.push_back(result->rrset->getRRsig());
rrsetsCheck(rrsig_a_txt, actual_rrsets_.begin(), actual_rrsets_.end());
// Confirm a separate RRISG for a different type can be added
actual_rrsets_.clear();
zone_finder_.add(rr_ns_);
zone_finder_.add(textToRRset(rrsig_ns_txt));
- ZoneFinder::FindResult result2 =
- zone_finder_.find(origin_, RRType::NS(), ZoneFinder::FIND_DNSSEC);
- EXPECT_EQ(ZoneFinder::SUCCESS, result2.code);
- ASSERT_TRUE(result2.rrset);
- ASSERT_TRUE(result2.rrset->getRRsig());
- actual_rrsets_.push_back(result2.rrset->getRRsig());
+ result = zone_finder_.find(origin_, RRType::NS(), ZoneFinder::FIND_DNSSEC);
+ EXPECT_EQ(ZoneFinder::SUCCESS, result->code);
+ ASSERT_TRUE(result->rrset);
+ ASSERT_TRUE(result->rrset->getRRsig());
+ actual_rrsets_.push_back(result->rrset->getRRsig());
rrsetsCheck(rrsig_ns_txt, actual_rrsets_.begin(), actual_rrsets_.end());
// Check a case with multiple RRSIGs
actual_rrsets_.clear();
zone_finder_.add(rr_ns_aaaa_);
zone_finder_.add(textToRRset(rrsig_aaaa_txt));
- ZoneFinder::FindResult result3 =
- zone_finder_.find(Name("ns.example.org"), RRType::AAAA(),
- ZoneFinder::FIND_DNSSEC);
- EXPECT_EQ(ZoneFinder::SUCCESS, result3.code);
- ASSERT_TRUE(result3.rrset);
- ASSERT_TRUE(result3.rrset->getRRsig());
- actual_rrsets_.push_back(result3.rrset->getRRsig());
+ result = zone_finder_.find(Name("ns.example.org"), RRType::AAAA(),
+ ZoneFinder::FIND_DNSSEC);
+ EXPECT_EQ(ZoneFinder::SUCCESS, result->code);
+ ASSERT_TRUE(result->rrset);
+ ASSERT_TRUE(result->rrset->getRRsig());
+ actual_rrsets_.push_back(result->rrset->getRRsig());
rrsetsCheck(rrsig_aaaa_txt, actual_rrsets_.begin(), actual_rrsets_.end());
}
@@ -1694,7 +1692,7 @@ TEST_F(InMemoryZoneFinderTest, addNSEC3) {
EXPECT_EQ(result::SUCCESS, zone_finder_.add(textToRRset(nsec3_text)));
EXPECT_EQ(ZoneFinder::NXDOMAIN,
zone_finder_.find(Name(string(apex_hash) + ".example.org"),
- RRType::NSEC3()).code);
+ RRType::NSEC3())->code);
// Dedicated NSEC3 find should be able to find it.
findNSEC3Check(true, origin_.getLabelCount(), nsec3_text, "",
zone_finder_.findNSEC3(Name("example.org"), false));
@@ -1714,7 +1712,7 @@ TEST_F(InMemoryZoneFinderTest, addNSEC3) {
EXPECT_EQ(result::SUCCESS, zone_finder_.add(textToRRset(nonsec3_text)));
EXPECT_EQ(ZoneFinder::SUCCESS,
zone_finder_.find(Name(string(apex_hash) + ".example.org"),
- RRType::A()).code);
+ RRType::A())->code);
}
TEST_F(InMemoryZoneFinderTest, addNSEC3Lower) {
diff --git a/src/lib/datasrc/tests/rbnode_rrset_unittest.cc b/src/lib/datasrc/tests/rbnode_rrset_unittest.cc
index a46d9cf..c653f44 100644
--- a/src/lib/datasrc/tests/rbnode_rrset_unittest.cc
+++ b/src/lib/datasrc/tests/rbnode_rrset_unittest.cc
@@ -138,6 +138,23 @@ TEST_F(RBNodeRRsetTest, toText) {
EXPECT_THROW(rrset_a_empty.toText(), EmptyRRset);
}
+TEST_F(RBNodeRRsetTest, isSameKind) {
+ RBNodeRRset rrset_p(ConstRRsetPtr(new RRset(test_name, RRClass::IN(), RRType::A(), RRTTL(3600))));
+ RBNodeRRset rrset_q(ConstRRsetPtr(new RRset(test_name, RRClass::IN(), RRType::A(), RRTTL(3600))));
+ RRset rrset_w(test_name, RRClass::IN(), RRType::A(), RRTTL(3600));
+ RRset rrset_x(test_nsname, RRClass::IN(), RRType::A(), RRTTL(3600));
+ RRset rrset_y(test_name, RRClass::IN(), RRType::NS(), RRTTL(3600));
+ RRset rrset_z(test_name, RRClass::CH(), RRType::A(), RRTTL(3600));
+
+ EXPECT_TRUE(rrset_p.isSameKind(rrset_p));
+ EXPECT_FALSE(rrset_p.isSameKind(rrset_q));
+
+ EXPECT_TRUE(rrset_p.isSameKind(rrset_w));
+ EXPECT_FALSE(rrset_p.isSameKind(rrset_x));
+ EXPECT_FALSE(rrset_p.isSameKind(rrset_y));
+ EXPECT_FALSE(rrset_p.isSameKind(rrset_z));
+}
+
// Note: although the next two tests are essentially the same and used common
// test code, they use different test data: the MessageRenderer produces
// compressed wire data whereas the OutputBuffer does not.
diff --git a/src/lib/datasrc/tests/testdata/.gitignore b/src/lib/datasrc/tests/testdata/.gitignore
new file mode 100644
index 0000000..58ea8cd
--- /dev/null
+++ b/src/lib/datasrc/tests/testdata/.gitignore
@@ -0,0 +1 @@
+/*.sqlite3.copied
diff --git a/src/lib/datasrc/tests/testdata/contexttest.zone b/src/lib/datasrc/tests/testdata/contexttest.zone
new file mode 100644
index 0000000..425866a
--- /dev/null
+++ b/src/lib/datasrc/tests/testdata/contexttest.zone
@@ -0,0 +1,75 @@
+;; test zone file used for ZoneFinderContext tests.
+;; RRSIGs are (obviouslly) faked ones for testing.
+
+example.org. 3600 IN SOA ns1.example.org. bugs.x.w.example.org. 56 3600 300 3600000 3600
+example.org. 3600 IN NS ns1.example.org.
+example.org. 3600 IN NS ns2.example.org.
+example.org. 3600 IN MX 1 mx1.example.org.
+example.org. 3600 IN MX 2 mx2.example.org.
+example.org. 3600 IN MX 3 mx.a.example.org.
+
+ns1.example.org. 3600 IN A 192.0.2.1
+ns1.example.org. 3600 IN RRSIG A 7 3 3600 20150420235959 20051021000000 40430 example.org. FAKEFAKE
+ns1.example.org. 3600 IN AAAA 2001:db8::1
+ns1.example.org. 3600 IN RRSIG AAAA 7 3 3600 20150420235959 20051021000000 40430 example.org. FAKEFAKEFAKE
+ns2.example.org. 3600 IN A 192.0.2.2
+ns2.example.org. 3600 IN TXT "text data"
+
+mx1.example.org. 3600 IN A 192.0.2.10
+mx2.example.org. 3600 IN AAAA 2001:db8::10
+
+;; delegation
+a.example.org. 3600 IN NS ns1.a.example.org.
+a.example.org. 3600 IN NS ns2.a.example.org.
+a.example.org. 3600 IN NS ns.example.com.
+
+ns1.a.example.org. 3600 IN A 192.0.2.5
+ns2.a.example.org. 3600 IN A 192.0.2.6
+ns2.a.example.org. 3600 IN AAAA 2001:db8::6
+mx.a.example.org. 3600 IN A 192.0.2.7
+
+;; delegation, one of its NS names is at zone cut.
+b.example.org. 3600 IN NS ns.b.example.org.
+b.example.org. 3600 IN NS b.example.org.
+b.example.org. 3600 IN AAAA 2001:db8::8
+
+ns.b.example.org. 3600 IN A 192.0.2.9
+
+;; The MX name is at a zone cut. shouldn't be included in the
+;; additional section.
+mxatcut.example.org. 3600 IN MX 1 b.example.org.
+
+;; delegation, one of its NS names is under a DNAME delegation point;
+;; another is at that point; and yet another is under DNAME below a
+;; zone cut.
+c.example.org. 3600 IN NS ns.dname.example.org.
+c.example.org. 3600 IN NS dname.example.org.
+c.example.org. 3600 IN NS ns.deepdname.example.org.
+ns.dname.example.org. 3600 IN A 192.0.2.11
+dname.example.org. 3600 IN A 192.0.2.12
+ns.deepdname.example.org. 3600 IN AAAA 2001:db8::9
+
+;; delegation, one of its NS name is at an empty non terminal.
+d.example.org. 3600 IN NS ns.empty.example.org.
+d.example.org. 3600 IN NS ns1.example.org.
+;; by adding these two we can create an empty RB node for
+;; ns.empty.example.org in the in-memory zone
+foo.ns.empty.example.org. 3600 IN A 192.0.2.13
+bar.ns.empty.example.org. 3600 IN A 192.0.2.14
+
+;; delegation; the NS name matches a wildcard (and there's no exact match)
+e.example.org. 3600 IN NS ns.wild.example.org.
+*.wild.example.org. 3600 IN A 192.0.2.15
+
+;; additional for an answer RRset (MX) as a result of wildcard
+;; expansion
+*.wildmx.example.org. 3600 IN MX 1 mx1.example.org.
+
+;; CNAME
+alias.example.org. 3600 IN CNAME cname.example.org.
+
+;; DNAME
+dname.example.org. 3600 IN DNAME dname.example.com.
+
+;; DNAME under a NS (strange one)
+deepdname.c.example.org. 3600 IN DNAME deepdname.example.com.
diff --git a/src/lib/datasrc/tests/zone_finder_context_unittest.cc b/src/lib/datasrc/tests/zone_finder_context_unittest.cc
new file mode 100644
index 0000000..9134307
--- /dev/null
+++ b/src/lib/datasrc/tests/zone_finder_context_unittest.cc
@@ -0,0 +1,412 @@
+// Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <dns/masterload.h>
+#include <dns/name.h>
+#include <dns/rrclass.h>
+
+#include <datasrc/zone.h>
+#include <datasrc/memory_datasrc.h>
+#include <datasrc/database.h>
+#include <datasrc/sqlite3_accessor.h>
+
+#include <testutils/dnsmessage_test.h>
+
+#include <gtest/gtest.h>
+
+#include <boost/bind.hpp>
+#include <boost/foreach.hpp>
+#include <boost/shared_ptr.hpp>
+
+#include <cstdlib>
+#include <vector>
+
+using namespace std;
+using boost::shared_ptr;
+
+using namespace isc::dns;
+using namespace isc::datasrc;
+using namespace isc::testutils;
+
+namespace {
+
+// Commonly used test zone file.
+const char* const TEST_ZONE_FILE = TEST_DATA_DIR "/contexttest.zone";
+
+// Convenient shortcut
+typedef shared_ptr<DataSourceClient> DataSourceClientPtr;
+
+// This is the type used as the test parameter. Note that this is
+// intentionally a plain old type (i.e. a function pointer), not a class;
+// otherwise it could cause initialization fiasco at the instantiation time.
+typedef DataSourceClientPtr (*ClientCreator)(RRClass, const Name&);
+
+// Creator for the in-memory client to be tested
+DataSourceClientPtr
+createInMemoryClient(RRClass zclass, const Name& zname) {
+ shared_ptr<InMemoryClient> client(new InMemoryClient);
+
+ shared_ptr<InMemoryZoneFinder> finder(
+ new InMemoryZoneFinder(zclass, zname));
+ finder->load(TEST_ZONE_FILE);
+
+ client->addZone(finder);
+
+ return (client);
+}
+
+// Creator for the SQLite3 client to be tested. addRRset() is a helper
+// subroutine.
+void
+addRRset(ZoneUpdaterPtr updater, ConstRRsetPtr rrset) {
+ updater->addRRset(*rrset);
+}
+
+DataSourceClientPtr
+createSQLite3Client(RRClass zclass, const Name& zname) {
+ // We always begin with an empty template SQLite3 DB file and install
+ // the zone data from the zone file to ensure both cases have the
+ // same test data.
+
+ const char* const install_cmd = INSTALL_PROG " " TEST_DATA_DIR
+ "/rwtest.sqlite3 " TEST_DATA_BUILDDIR "/contexttest.sqlite3.copied";
+ if (system(install_cmd) != 0) {
+ isc_throw(isc::Unexpected,
+ "Error setting up; command failed: " << install_cmd);
+ }
+
+ shared_ptr<SQLite3Accessor> accessor(
+ new SQLite3Accessor(TEST_DATA_BUILDDIR "/contexttest.sqlite3.copied",
+ zclass.toText()));
+ shared_ptr<DatabaseClient> client(new DatabaseClient(zclass, accessor));
+
+ ZoneUpdaterPtr updater = client->getUpdater(zname, true);
+ masterLoad(TEST_ZONE_FILE, zname, zclass, boost::bind(addRRset, updater,
+ _1));
+ // Insert an out-of-zone name to test if it's incorrectly returned.
+ // Note that neither updater nor SQLite3 accessor checks this condition,
+ // so this should succeed.
+ stringstream ss("ns.example.com. 3600 IN A 192.0.2.7");
+ masterLoad(ss, Name::ROOT_NAME(), zclass,
+ boost::bind(addRRset, updater, _1));
+ updater->commit();
+
+ return (client);
+}
+
+// The test class. Its parameterized so we can share the test scnearios
+// for any concrete data source implementaitons.
+class ZoneFinderContextTest :
+ public ::testing::TestWithParam<ClientCreator>
+{
+protected:
+ ZoneFinderContextTest() : qclass_(RRClass::IN()), qzone_("example.org") {
+ client_ = (*GetParam())(qclass_, qzone_);
+ REQUESTED_A.push_back(RRType::A());
+ REQUESTED_AAAA.push_back(RRType::AAAA());
+ REQUESTED_BOTH.push_back(RRType::A());
+ REQUESTED_BOTH.push_back(RRType::AAAA());
+ }
+ void SetUp() {
+ finder_ = client_->findZone(qzone_).zone_finder;
+ ASSERT_TRUE(finder_);
+ }
+
+ const RRClass qclass_;
+ const Name qzone_;
+ DataSourceClientPtr client_;
+ ZoneFinderPtr finder_;
+
+ vector<RRType> requested_types_;
+ vector<RRType> REQUESTED_A;
+ vector<RRType> REQUESTED_AAAA;
+ vector<RRType> REQUESTED_BOTH;
+ vector<ConstRRsetPtr> result_sets_;
+};
+
+// We test the in-memory and SQLite3 data source implementations.
+INSTANTIATE_TEST_CASE_P(, ZoneFinderContextTest,
+ ::testing::Values(createInMemoryClient,
+ createSQLite3Client));
+
+TEST_P(ZoneFinderContextTest, getAdditionalAuthNS) {
+ ZoneFinderContextPtr ctx = finder_->find(qzone_, RRType::NS());
+ EXPECT_EQ(ZoneFinder::SUCCESS, ctx->code);
+
+ // Getting both A and AAAA NS addresses
+ ctx->getAdditional(REQUESTED_BOTH, result_sets_);
+ rrsetsCheck("ns1.example.org. 3600 IN A 192.0.2.1\n"
+ "ns1.example.org. 3600 IN AAAA 2001:db8::1\n"
+ "ns2.example.org. 3600 IN A 192.0.2.2\n",
+ result_sets_.begin(), result_sets_.end());
+
+ // Getting only A
+ result_sets_.clear();
+ ctx->getAdditional(REQUESTED_A, result_sets_);
+ rrsetsCheck("ns1.example.org. 3600 IN A 192.0.2.1\n"
+ "ns2.example.org. 3600 IN A 192.0.2.2\n",
+ result_sets_.begin(), result_sets_.end());
+
+ // Getting only AAAA
+ result_sets_.clear();
+ ctx->getAdditional(REQUESTED_AAAA, result_sets_);
+ rrsetsCheck("ns1.example.org. 3600 IN AAAA 2001:db8::1\n",
+ result_sets_.begin(), result_sets_.end());
+
+ // Getting A again, without clearing the result sets. This confirms
+ // getAdditional() doesn't change the existing vector content.
+ ctx->getAdditional(REQUESTED_A, result_sets_);
+ // The first element should be the existing AAAA RR, followed by the A's.
+ EXPECT_EQ(RRType::AAAA(), result_sets_[0]->getType());
+ rrsetsCheck("ns1.example.org. 3600 IN AAAA 2001:db8::1\n"
+ "ns1.example.org. 3600 IN A 192.0.2.1\n"
+ "ns2.example.org. 3600 IN A 192.0.2.2\n",
+ result_sets_.begin(), result_sets_.end());
+
+ // Normally expected type set contain only A and/or AAAA, but others aren't
+ // excluded.
+ result_sets_.clear();
+ requested_types_.push_back(RRType::TXT());
+ ctx->getAdditional(requested_types_, result_sets_);
+ rrsetsCheck("ns2.example.org. 3600 IN TXT \"text data\"",
+ result_sets_.begin(), result_sets_.end());
+
+ // Even empty set is okay. The result should also be empty.
+ result_sets_.clear();
+ ctx->getAdditional(vector<RRType>(), result_sets_);
+ EXPECT_TRUE(result_sets_.empty());
+}
+
+TEST_P(ZoneFinderContextTest, getAdditionalDelegation) {
+ // Basically similar to the AuthNS case, but NS names are glues.
+ // It contains an out-of-zone NS name. Its address (even if it's somehow
+ // inserted to the zone data) shouldn't be returned.
+ const Name qname("www.a.example.org");
+ ZoneFinderContextPtr ctx = finder_->find(qname, RRType::AAAA());
+ EXPECT_EQ(ZoneFinder::DELEGATION, ctx->code);
+
+ ctx->getAdditional(REQUESTED_BOTH, result_sets_);
+ rrsetsCheck("ns1.a.example.org. 3600 IN A 192.0.2.5\n"
+ "ns2.a.example.org. 3600 IN A 192.0.2.6\n"
+ "ns2.a.example.org. 3600 IN AAAA 2001:db8::6\n",
+ result_sets_.begin(), result_sets_.end());
+
+ result_sets_.clear();
+ ctx->getAdditional(REQUESTED_A, result_sets_);
+ rrsetsCheck("ns1.a.example.org. 3600 IN A 192.0.2.5\n"
+ "ns2.a.example.org. 3600 IN A 192.0.2.6\n",
+ result_sets_.begin(), result_sets_.end());
+
+ result_sets_.clear();
+ ctx->getAdditional(REQUESTED_AAAA, result_sets_);
+ rrsetsCheck("ns2.a.example.org. 3600 IN AAAA 2001:db8::6\n",
+ result_sets_.begin(), result_sets_.end());
+}
+
+TEST_P(ZoneFinderContextTest, getAdditionalDelegationAtZoneCut) {
+ // Similar to the previous case, but one of the NS addresses is at the
+ // zone cut.
+
+ // XXX: the current database-based data source incorrectly rejects this
+ // setup (see #1771)
+ if (GetParam() == createSQLite3Client) {
+ return;
+ }
+
+ ZoneFinderContextPtr ctx = finder_->find(Name("www.b.example.org"),
+ RRType::SOA());
+ EXPECT_EQ(ZoneFinder::DELEGATION, ctx->code);
+
+ ctx->getAdditional(REQUESTED_BOTH, result_sets_);
+ rrsetsCheck("b.example.org. 3600 IN AAAA 2001:db8::8\n"
+ "ns.b.example.org. 3600 IN A 192.0.2.9\n",
+ result_sets_.begin(), result_sets_.end());
+}
+
+TEST_P(ZoneFinderContextTest, getAdditionalDelegationWithDname) {
+ // Delegation: One of the NS names under a DNAME delegation; another
+ // is at the delegation point; yet another is under DNAME below a zone cut.
+ // The first should be hidden.
+ ZoneFinderContextPtr ctx = finder_->find(Name("www.c.example.org"),
+ RRType::TXT());
+ EXPECT_EQ(ZoneFinder::DELEGATION, ctx->code);
+
+ ctx->getAdditional(REQUESTED_BOTH, result_sets_);
+ rrsetsCheck("dname.example.org. 3600 IN A 192.0.2.12\n"
+ "ns.deepdname.example.org. 3600 IN AAAA 2001:db8::9\n",
+ result_sets_.begin(), result_sets_.end());
+}
+
+TEST_P(ZoneFinderContextTest, getAdditionalDelegationWithEmptyName) {
+ // One of NS names is at an empty non terminal node. It shouldn't cause
+ // any disruption.
+ ZoneFinderContextPtr ctx = finder_->find(Name("www.d.example.org"),
+ RRType::A());
+ EXPECT_EQ(ZoneFinder::DELEGATION, ctx->code);
+
+ ctx->getAdditional(REQUESTED_BOTH, result_sets_);
+ rrsetsCheck("ns1.example.org. 3600 IN A 192.0.2.1\n"
+ "ns1.example.org. 3600 IN AAAA 2001:db8::1\n",
+ result_sets_.begin(), result_sets_.end());
+}
+
+TEST_P(ZoneFinderContextTest, getAdditionalDelegationWithWild) {
+ // The NS name needs to be expanded by a wildcard. Currently it doesn't
+ // work for the optimized in-memory version.
+ if (GetParam() == createInMemoryClient) {
+ return;
+ }
+
+ ZoneFinderContextPtr ctx = finder_->find(Name("www.e.example.org"),
+ RRType::AAAA());
+ EXPECT_EQ(ZoneFinder::DELEGATION, ctx->code);
+ ctx->getAdditional(REQUESTED_BOTH, result_sets_);
+ rrsetsCheck("ns.wild.example.org. 3600 IN A 192.0.2.15\n",
+ result_sets_.begin(), result_sets_.end());
+}
+
+TEST_P(ZoneFinderContextTest, getAdditionalDelegationForWild) {
+ // additional for an answer RRset (MX) as a result of wildcard expansion.
+ // note the difference from the previous test. in this case wildcard
+ // applies to the owner name of the answer, not the owner name of the
+ // additional.
+ ZoneFinderContextPtr ctx = finder_->find(Name("mx.wildmx.example.org"),
+ RRType::MX());
+ EXPECT_EQ(ZoneFinder::SUCCESS, ctx->code);
+ ctx->getAdditional(REQUESTED_BOTH, result_sets_);
+ rrsetsCheck("mx1.example.org. 3600 IN A 192.0.2.10\n",
+ result_sets_.begin(), result_sets_.end());
+}
+
+TEST_P(ZoneFinderContextTest, getAdditionalMX) {
+ // Similar to the previous cases, but for MX addresses. The test zone
+ // contains MX name under a zone cut. Its address shouldn't be returned.
+ ZoneFinderContextPtr ctx = finder_->find(qzone_, RRType::MX());
+ EXPECT_EQ(ZoneFinder::SUCCESS, ctx->code);
+
+ // Getting both A and AAAA NS addresses
+ ctx->getAdditional(REQUESTED_BOTH, result_sets_);
+ rrsetsCheck("mx1.example.org. 3600 IN A 192.0.2.10\n"
+ "mx2.example.org. 3600 IN AAAA 2001:db8::10\n",
+ result_sets_.begin(), result_sets_.end());
+
+ // Getting only A
+ result_sets_.clear();
+ ctx->getAdditional(REQUESTED_A, result_sets_);
+ rrsetsCheck("mx1.example.org. 3600 IN A 192.0.2.10\n",
+ result_sets_.begin(), result_sets_.end());
+
+ // Getting only AAAA
+ result_sets_.clear();
+ ctx->getAdditional(REQUESTED_AAAA, result_sets_);
+ rrsetsCheck("mx2.example.org. 3600 IN AAAA 2001:db8::10\n",
+ result_sets_.begin(), result_sets_.end());
+}
+
+TEST_P(ZoneFinderContextTest, getAdditionalMXAtZoneCut) {
+ // XXX: the current database-based data source incorrectly rejects this
+ // setup (see #1771)
+ if (GetParam() == createSQLite3Client) {
+ return;
+ }
+
+ ZoneFinderContextPtr ctx = finder_->find(Name("mxatcut.example.org."),
+ RRType::MX());
+ EXPECT_EQ(ZoneFinder::SUCCESS, ctx->code);
+
+ ctx->getAdditional(REQUESTED_BOTH, result_sets_);
+ EXPECT_TRUE(result_sets_.empty());
+}
+
+TEST_P(ZoneFinderContextTest, getAdditionalWithSIG) {
+ // Similar to the AuthNS test, but the original find() requested DNSSEC
+ // RRSIGs. Then additional records will also have RRSIGs.
+ ZoneFinderContextPtr ctx = finder_->find(qzone_, RRType::NS(),
+ ZoneFinder::FIND_DNSSEC);
+ EXPECT_EQ(ZoneFinder::SUCCESS, ctx->code);
+
+ ctx->getAdditional(REQUESTED_BOTH, result_sets_);
+ rrsetsCheck("ns1.example.org. 3600 IN A 192.0.2.1\n"
+ "ns1.example.org. 3600 IN AAAA 2001:db8::1\n"
+ "ns2.example.org. 3600 IN A 192.0.2.2\n",
+ result_sets_.begin(), result_sets_.end());
+
+ vector<ConstRRsetPtr> sigresult_sets;
+ BOOST_FOREACH(ConstRRsetPtr rrset, result_sets_) {
+ ConstRRsetPtr sig_rrset = rrset->getRRsig();
+ if (sig_rrset) {
+ sigresult_sets.push_back(sig_rrset);
+ }
+ }
+ rrsetsCheck("ns1.example.org. 3600 IN RRSIG A 7 3 3600 20150420235959 "
+ "20051021000000 40430 example.org. FAKEFAKE\n"
+ "ns1.example.org. 3600 IN RRSIG AAAA 7 3 3600 20150420235959 "
+ "20051021000000 40430 example.org. FAKEFAKEFAKE\n",
+ sigresult_sets.begin(), sigresult_sets.end());
+}
+
+TEST_P(ZoneFinderContextTest, getAdditionalNoOP) {
+ // getAdditional() is only meaningful after SUCCESS or DELEGATION.
+
+ ZoneFinderContextPtr ctx = finder_->find(Name("nxdomain.example.org"),
+ RRType::NS());
+ EXPECT_EQ(ZoneFinder::NXDOMAIN, ctx->code);
+ ctx->getAdditional(REQUESTED_BOTH, result_sets_);
+ EXPECT_TRUE(result_sets_.empty());
+
+ ctx = finder_->find(qzone_, RRType::TXT());
+ EXPECT_EQ(ZoneFinder::NXRRSET, ctx->code);
+ ctx->getAdditional(REQUESTED_BOTH, result_sets_);
+ EXPECT_TRUE(result_sets_.empty());
+
+ ctx = finder_->find(Name("alias.example.org."), RRType::A());
+ EXPECT_EQ(ZoneFinder::CNAME, ctx->code);
+ ctx->getAdditional(REQUESTED_BOTH, result_sets_);
+ EXPECT_TRUE(result_sets_.empty());
+
+ ctx = finder_->find(Name("www.dname.example.org."), RRType::A());
+ EXPECT_EQ(ZoneFinder::DNAME, ctx->code);
+ ctx->getAdditional(REQUESTED_BOTH, result_sets_);
+ EXPECT_TRUE(result_sets_.empty());
+}
+
+TEST_P(ZoneFinderContextTest, getAdditionalForAny) {
+ // getAdditional() after successful type ANY query should return
+ // the additional records of all returned RRsets.
+ vector<ConstRRsetPtr> all_rrsets;
+ ZoneFinderContextPtr ctx = finder_->findAll(qzone_, all_rrsets);
+ EXPECT_EQ(ZoneFinder::SUCCESS, ctx->code);
+
+ ctx->getAdditional(REQUESTED_BOTH, result_sets_);
+ rrsetsCheck("ns1.example.org. 3600 IN A 192.0.2.1\n"
+ "ns1.example.org. 3600 IN AAAA 2001:db8::1\n"
+ "ns2.example.org. 3600 IN A 192.0.2.2\n"
+ "mx1.example.org. 3600 IN A 192.0.2.10\n"
+ "mx2.example.org. 3600 IN AAAA 2001:db8::10\n",
+ result_sets_.begin(), result_sets_.end());
+
+ // If the type ANY query results in DELEGATION, the result should be the
+ // same as normal query.
+ all_rrsets.clear();
+ result_sets_.clear();
+ ctx = finder_->findAll(Name("www.a.example.org"), all_rrsets);
+ EXPECT_EQ(ZoneFinder::DELEGATION, ctx->code);
+ ctx->getAdditional(REQUESTED_BOTH, result_sets_);
+ rrsetsCheck("ns1.a.example.org. 3600 IN A 192.0.2.5\n"
+ "ns2.a.example.org. 3600 IN A 192.0.2.6\n"
+ "ns2.a.example.org. 3600 IN AAAA 2001:db8::6\n",
+ result_sets_.begin(), result_sets_.end());
+}
+
+}
diff --git a/src/lib/datasrc/zone.h b/src/lib/datasrc/zone.h
index ff88746..c705279 100644
--- a/src/lib/datasrc/zone.h
+++ b/src/lib/datasrc/zone.h
@@ -15,14 +15,15 @@
#ifndef __ZONE_H
#define __ZONE_H 1
-#include <utility>
-#include <vector>
-
+#include <dns/name.h>
#include <dns/rrset.h>
-#include <dns/rrsetlist.h>
+#include <dns/rrtype.h>
#include <datasrc/result.h>
+#include <utility>
+#include <vector>
+
namespace isc {
namespace datasrc {
@@ -79,7 +80,7 @@ public:
/// proof of the result.
///
/// The caller is generally expected to get access to the information
- /// via read-only getter methods of \c FindResult so that it won't rely
+ /// via read-only getter methods of \c FindContext so that it won't rely
/// on specific details of the representation of the flags. So these
/// definitions are basically only meaningful for data source
/// implementations.
@@ -90,39 +91,120 @@ public:
RESULT_NSEC3_SIGNED = 4 ///< The zone is signed with NSEC3 RRs
};
- /// A helper structure to represent the search result of \c find().
- ///
- /// This is a straightforward tuple of the result code and a pointer
- /// (and optionally special flags) to the found RRset to represent the
- /// result of \c find() (there will be more members in the future -
- /// see the class description).
- /// We use this in order to avoid overloading the return value for both
- /// the result code ("success" or "not found") and the found object,
- /// i.e., avoid using \c NULL to mean "not found", etc.
- ///
- /// This is a simple value class whose internal state never changes,
- /// so for convenience we allow the applications to refer to some of the
- /// members directly. For others we provide read-only accessor methods
- /// to hide specific representation.
- ///
- /// Note: we should eventually include a notion of "zone node", which
- /// corresponds to a particular domain name of the zone, so that we can
- /// find RRsets of a different RR type for that name (e.g. for type ANY
- /// query or to include DS RRs with delegation).
- ///
- /// Note: we may also want to include the closest enclosure "node" to
- /// optimize including the NSEC for no-wildcard proof (FWIW NSD does that).
- struct FindResult {
- FindResult(Result param_code,
- const isc::dns::ConstRRsetPtr param_rrset,
- FindResultFlags param_flags = RESULT_DEFAULT) :
- code(param_code), rrset(param_rrset), flags(param_flags)
+ /// Find options.
+ ///
+ /// The option values are used as a parameter for \c find().
+ /// These are values of a bitmask type. Bitwise operations can be
+ /// performed on these values to express compound options.
+ enum FindOptions {
+ FIND_DEFAULT = 0, ///< The default options
+ FIND_GLUE_OK = 1, ///< Allow search under a zone cut
+ FIND_DNSSEC = 2, ///< Require DNSSEC data in the answer
+ ///< (RRSIG, NSEC, etc.). The implementation
+ ///< is allowed to include it even if it is
+ ///< not set.
+ NO_WILDCARD = 4 ///< Do not try wildcard matching.
+ };
+
+protected:
+ /// \brief A convenient tuple representing a set of find() results.
+ ///
+ /// This helper structure is specifically expected to be used as an input
+ /// for the construct of the \c Context class object used by derived
+ /// ZoneFinder implementations. This is therefore defined as protected.
+ struct ResultContext {
+ ResultContext(Result code_param,
+ isc::dns::ConstRRsetPtr rrset_param,
+ FindResultFlags flags_param = RESULT_DEFAULT) :
+ code(code_param), rrset(rrset_param), flags(flags_param)
+ {}
+ const Result code;
+ const isc::dns::ConstRRsetPtr rrset;
+ const FindResultFlags flags;
+ };
+
+public:
+ /// \brief Context of the result of a find() call.
+ ///
+ /// This class encapsulates results and (possibly) associated context
+ /// of a call to the \c find() method. The public member variables of
+ /// this class reprsent the result of the call. They are a
+ /// straightforward tuple of the result code and a pointer (and
+ /// optionally special flags) to the found RRset.
+ ///
+ /// These member variables will be initialized on construction and never
+ /// change, so for convenience we allow the applications to refer to some
+ /// of the members directly. For some others we provide read-only accessor
+ /// methods to hide specific representation.
+ ///
+ /// Another role of this class is to provide the interface to some common
+ /// processing logic that may be necessary using the result of \c find().
+ /// Specifically, it's expected to be used in the context of DNS query
+ /// handling, where the caller would need to look into the data source
+ /// again based on the \c find() result. For example, it would need to
+ /// get A and/or AAAA records for some of the answer or authority RRs.
+ ///
+ /// This class defines (a set of) method(s) that can be commonly used
+ /// for such purposes for any type of data source (as long as it conforms
+ /// to the public \c find() interface). In some cases, a specific data
+ /// source implementation may want to (and can) optimize the processing
+ /// exploiting its internal data structure and the knowledge of the context
+ /// of the precedent \c find() call. Such a data source implementation
+ /// can define a derived class of the base Context and override the
+ /// specific virtual method.
+ ///
+ /// This class object is generally expected to be associated with the
+ /// ZoneFinder that originally performed the \c find() call, and expects
+ /// the finder is valid throughout the lifetime of this object. It's
+ /// caller's responsibility to ensure that assumption.
+ class Context {
+ public:
+ /// \brief The constructor for the normal find call.
+ ///
+ /// This constructor is expected to be called from the \c find()
+ /// method when it constructs the return value.
+ ///
+ /// \param finder The ZoneFinder on which find() is called.
+ /// \param options The find options specified for the find() call.
+ /// \param result The result of the find() call.
+ Context(ZoneFinder& finder, FindOptions options,
+ const ResultContext& result) :
+ code(result.code), rrset(result.rrset),
+ finder_(finder), flags_(result.flags), options_(options)
+ {}
+
+ /// \brief The constructor for the normal findAll call.
+ ///
+ /// This constructor is expected to be called from the \c findAll()
+ /// method when it constructs the return value.
+ ///
+ /// It copies the vector that is to be returned to the caller of
+ /// \c findAll() for possible subsequent use. Note that it cannot
+ /// simply hold a reference to the vector because the caller may
+ /// alter it after the \c findAll() call.
+ ///
+ /// \param finder The ZoneFinder on which findAll() is called.
+ /// \param options The find options specified for the findAll() call.
+ /// \param result The result of the findAll() call (whose rrset is
+ /// expected to be NULL).
+ /// \param all_set Reference to the vector given by the caller of
+ /// \c findAll(), storing the RRsets to be returned.
+ Context(ZoneFinder& finder, FindOptions options,
+ const ResultContext& result,
+ const std::vector<isc::dns::ConstRRsetPtr> &all_set) :
+ code(result.code), rrset(result.rrset),
+ finder_(finder), flags_(result.flags), options_(options),
+ all_set_(all_set)
{}
+
+ /// \brief The destructor.
+ virtual ~Context() {}
+
const Result code;
const isc::dns::ConstRRsetPtr rrset;
/// Return true iff find() results in a wildcard match.
- bool isWildcard() const { return ((flags & RESULT_WILDCARD) != 0); }
+ bool isWildcard() const { return ((flags_ & RESULT_WILDCARD) != 0); }
/// Return true when the underlying zone is signed with NSEC.
///
@@ -134,7 +216,7 @@ public:
/// that \c rrset be a valid NSEC RRset as described in \c find()
/// documentation.
bool isNSECSigned() const {
- return ((flags & RESULT_NSEC_SIGNED) != 0);
+ return ((flags_ & RESULT_NSEC_SIGNED) != 0);
}
/// Return true when the underlying zone is signed with NSEC3.
@@ -143,25 +225,72 @@ public:
/// \c FIND_DNSSEC isn't specified regardless of whether the zone
/// is signed or which of NSEC/NSEC3 is used.
bool isNSEC3Signed() const {
- return ((flags & RESULT_NSEC3_SIGNED) != 0);
+ return ((flags_ & RESULT_NSEC3_SIGNED) != 0);
}
- private:
- FindResultFlags flags;
- };
- /// Find options.
- ///
- /// The option values are used as a parameter for \c find().
- /// These are values of a bitmask type. Bitwise operations can be
- /// performed on these values to express compound options.
- enum FindOptions {
- FIND_DEFAULT = 0, ///< The default options
- FIND_GLUE_OK = 1, ///< Allow search under a zone cut
- FIND_DNSSEC = 2, ///< Require DNSSEC data in the answer
- ///< (RRSIG, NSEC, etc.). The implementation
- ///< is allowed to include it even if it is
- ///< not set.
- NO_WILDCARD = 4 ///< Do not try wildcard matching.
+ /// \brief Find and return additional RRsets corresponding to the
+ /// result of \c find().
+ ///
+ /// If this context is based on a normal find() call that resulted
+ /// in SUCCESS or DELEGATION, it examines the returned RRset (in many
+ /// cases NS, sometimes MX or others), searches the data source for
+ /// specified type of additional RRs for each RDATA of the RRset
+ /// (e.g., A or AAAA for the name server addresses), and stores the
+ /// result in the given vector. The vector may not be empty; this
+ /// method appends any found RRsets to it, without touching existing
+ /// elements.
+ ///
+ /// If this context is based on a findAll() call that resulted in
+ /// SUCCESS, it performs the same process for each RRset returned in
+ /// the \c findAll() call.
+ ///
+ /// The caller specifies desired RR types of the additional RRsets
+ /// in \c requested_types. Normally it consists of A and/or AAAA
+ /// types, but other types can be specified.
+ ///
+ /// This method is meaningful only when the precedent find()/findAll()
+ /// call resulted in SUCCESS or DELEGATION. Otherwise this method
+ /// does nothing.
+ ///
+ /// \note The additional RRsets returned via method are limited to
+ /// ones contained in the zone which the corresponding find/findAll
+ /// call searched (possibly including glues under a zone cut where
+ /// they are applicable). If the caller needs to get out-of-zone
+ /// additional RRsets, it needs to explicitly finds them by
+ /// identifying the corresponding zone and calls \c find() for it.
+ ///
+ /// \param requested_types A vector of RR types for desired additional
+ /// RRsets.
+ /// \param result A vector to which any found additional RRsets are
+ /// to be inserted.
+ void getAdditional(
+ const std::vector<isc::dns::RRType>& requested_types,
+ std::vector<isc::dns::ConstRRsetPtr>& result)
+ {
+ // Perform common checks, and delegate the process to the default
+ // or specialized implementation.
+ if (code != SUCCESS && code != DELEGATION) {
+ return;
+ }
+
+ getAdditionalImpl(requested_types, result);
+ }
+
+ protected:
+ /// \brief Actual implementation of getAdditional().
+ ///
+ /// This base class defines a default implementation that can be
+ /// used for any type of data sources. A data source implementation
+ /// can override it.
+ virtual void getAdditionalImpl(
+ const std::vector<isc::dns::RRType>& requested_types,
+ std::vector<isc::dns::ConstRRsetPtr>& result);
+
+ private:
+ ZoneFinder& finder_;
+ const FindResultFlags flags_;
+ const FindOptions options_;
+ std::vector<isc::dns::ConstRRsetPtr> all_set_;
};
///
@@ -217,10 +346,10 @@ public:
/// the code of \c DNAME and that DNAME RR.
///
/// No RRset will be returned in the \c NXDOMAIN and \c NXRRSET cases
- /// (\c rrset member of \c FindResult will be NULL), unless DNSSEC data
+ /// (\c rrset member of \c FindContext will be NULL), unless DNSSEC data
/// are required. See below for the cases with DNSSEC.
///
- /// The returned \c FindResult object can also provide supplemental
+ /// The returned \c FindContext object can also provide supplemental
/// information about the search result via its methods returning a
/// boolean value. Such information may be useful for the caller if
/// the caller wants to collect additional DNSSEC proofs based on the
@@ -276,7 +405,7 @@ public:
/// returned from this method.
///
/// In case it's signed with NSEC, this method will possibly return
- /// a related NSEC RRset in the \c rrset member of \c FindResult.
+ /// a related NSEC RRset in the \c rrset member of \c FindContext.
/// What kind of NSEC is returned depends on the result code
/// (\c NXDOMAIN or \c NXRRSET) and on whether it's a wildcard match:
///
@@ -333,7 +462,7 @@ public:
/// \endcode
/// a call to \c find() for "y.b.example.org" with FIND_DNSSEC will
/// result in NXRRSET and this NSEC; \c isWildcard() on the returned
- /// \c FindResult object will return true.
+ /// \c FindContext object will return true.
///
/// \exception std::bad_alloc Memory allocation such as for constructing
/// the resulting RRset fails
@@ -348,11 +477,12 @@ public:
/// \param name The domain name to be searched for.
/// \param type The RR type to be searched for.
/// \param options The search options.
- /// \return A \c FindResult object enclosing the search result (see above).
- virtual FindResult find(const isc::dns::Name& name,
- const isc::dns::RRType& type,
- const FindOptions options
- = FIND_DEFAULT) = 0;
+ /// \return A \c FindContext object enclosing the search result
+ /// (see above).
+ virtual boost::shared_ptr<Context> find(const isc::dns::Name& name,
+ const isc::dns::RRType& type,
+ const FindOptions options
+ = FIND_DEFAULT) = 0;
///
/// \brief Finds all RRsets in the given name.
@@ -370,13 +500,14 @@ public:
/// \param target the successfull result is returned through this
/// \param options \see find, parameter options
/// \return \see find and it's result
- virtual FindResult findAll(const isc::dns::Name& name,
- std::vector<isc::dns::ConstRRsetPtr> &target,
- const FindOptions options = FIND_DEFAULT) = 0;
+ virtual boost::shared_ptr<Context> findAll(
+ const isc::dns::Name& name,
+ std::vector<isc::dns::ConstRRsetPtr> &target,
+ const FindOptions options = FIND_DEFAULT) = 0;
/// A helper structure to represent the search result of \c findNSEC3().
///
- /// The idea is similar to that of \c FindResult, but \c findNSEC3() has
+ /// The idea is similar to that of \c FindContext, but \c findNSEC3() has
/// special interface and semantics, we use a different structure to
/// represent the result.
struct FindNSEC3Result {
@@ -530,9 +661,16 @@ inline ZoneFinder::FindResultFlags operator |(
/// \brief A pointer-like type pointing to a \c ZoneFinder object.
typedef boost::shared_ptr<ZoneFinder> ZoneFinderPtr;
-/// \brief A pointer-like type pointing to a \c ZoneFinder object.
+/// \brief A pointer-like type pointing to an immutable \c ZoneFinder object.
typedef boost::shared_ptr<const ZoneFinder> ConstZoneFinderPtr;
+/// \brief A pointer-like type pointing to a \c ZoneFinder::Context object.
+typedef boost::shared_ptr<ZoneFinder::Context> ZoneFinderContextPtr;
+
+/// \brief A pointer-like type pointing to an immutable
+/// \c ZoneFinder::Context object.
+typedef boost::shared_ptr<ZoneFinder::Context> ConstZoneFinderContextPtr;
+
/// The base class to make updates to a single zone.
///
/// On construction, each derived class object will start a "transaction"
diff --git a/src/lib/datasrc/zone_finder_context.cc b/src/lib/datasrc/zone_finder_context.cc
new file mode 100644
index 0000000..7913d71
--- /dev/null
+++ b/src/lib/datasrc/zone_finder_context.cc
@@ -0,0 +1,102 @@
+// Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <dns/rdata.h>
+#include <dns/rrset.h>
+#include <dns/rrtype.h>
+#include <dns/rdataclass.h>
+
+#include <datasrc/zone.h>
+
+#include <boost/foreach.hpp>
+
+#include <vector>
+
+using namespace std;
+using namespace isc::dns;
+using namespace isc::dns::rdata;
+
+namespace isc {
+namespace datasrc {
+
+namespace {
+void
+getAdditionalAddrs(ZoneFinder& finder, const Name& name,
+ const vector<RRType>& requested_types,
+ vector<ConstRRsetPtr>& result_rrsets,
+ ZoneFinder::FindOptions options)
+{
+ // Ignore out-of-zone names
+ const NameComparisonResult cmp = finder.getOrigin().compare(name);
+ if ((cmp.getRelation() != NameComparisonResult::SUPERDOMAIN) &&
+ (cmp.getRelation() != NameComparisonResult::EQUAL)) {
+ return;
+ }
+
+ BOOST_FOREACH(RRType rrtype, requested_types) {
+ ConstZoneFinderContextPtr ctx = finder.find(name, rrtype, options);
+ if (ctx->code == ZoneFinder::SUCCESS) {
+ result_rrsets.push_back(ctx->rrset);
+ }
+ }
+}
+
+void
+getAdditionalForRRset(ZoneFinder& finder, const AbstractRRset& rrset,
+ const vector<RRType>& requested_types,
+ vector<ConstRRsetPtr>& result,
+ ZoneFinder::FindOptions orig_options)
+{
+ RdataIteratorPtr rdata_iterator(rrset.getRdataIterator());
+ ZoneFinder::FindOptions options = ZoneFinder::FIND_DEFAULT;
+ if ((orig_options & ZoneFinder::FIND_DNSSEC) != 0) {
+ options = options | ZoneFinder::FIND_DNSSEC;
+ }
+
+ for (; !rdata_iterator->isLast(); rdata_iterator->next()) {
+ const Rdata& rdata(rdata_iterator->getCurrent());
+
+ if (rrset.getType() == RRType::NS()) {
+ // Need to perform the search in the "GLUE OK" mode.
+ const generic::NS& ns = dynamic_cast<const generic::NS&>(rdata);
+ getAdditionalAddrs(finder, ns.getNSName(), requested_types,
+ result, options | ZoneFinder::FIND_GLUE_OK);
+ } else if (rrset.getType() == RRType::MX()) {
+ const generic::MX& mx = dynamic_cast<const generic::MX&>(rdata);
+ getAdditionalAddrs(finder, mx.getMXName(), requested_types,
+ result, options);
+ }
+ }
+}
+}
+
+void
+ZoneFinder::Context::getAdditionalImpl(const vector<RRType>& requested_types,
+ vector<ConstRRsetPtr>& result)
+{
+ // If rrset is non NULL, it should have been SUCCESS/DELEGATION; otherwise
+ // we should have responded to type ANY query.
+ if (rrset) {
+ getAdditionalForRRset(finder_, *rrset, requested_types, result,
+ options_);
+ return;
+ }
+ BOOST_FOREACH(ConstRRsetPtr rrset_in_set, all_set_) {
+ getAdditionalForRRset(finder_, *rrset_in_set, requested_types, result,
+ options_);
+ }
+}
+
+} // namespace datasrc
+} // datasrc isc
diff --git a/src/lib/dhcp/tests/.gitignore b/src/lib/dhcp/tests/.gitignore
new file mode 100644
index 0000000..313429d
--- /dev/null
+++ b/src/lib/dhcp/tests/.gitignore
@@ -0,0 +1 @@
+/libdhcp++_unittests
diff --git a/src/lib/dns/.gitignore b/src/lib/dns/.gitignore
new file mode 100644
index 0000000..cdf707c
--- /dev/null
+++ b/src/lib/dns/.gitignore
@@ -0,0 +1,6 @@
+/gen-rdatacode.py
+/rdataclass.cc
+/rdataclass.h
+/rrclass.h
+/rrparamregistry.cc
+/rrtype.h
diff --git a/src/lib/dns/Makefile.am b/src/lib/dns/Makefile.am
index 8471c2a..010c027 100644
--- a/src/lib/dns/Makefile.am
+++ b/src/lib/dns/Makefile.am
@@ -61,6 +61,8 @@ EXTRA_DIST += rdata/generic/soa_6.cc
EXTRA_DIST += rdata/generic/soa_6.h
EXTRA_DIST += rdata/generic/spf_99.cc
EXTRA_DIST += rdata/generic/spf_99.h
+EXTRA_DIST += rdata/generic/sshfp_44.cc
+EXTRA_DIST += rdata/generic/sshfp_44.h
EXTRA_DIST += rdata/generic/txt_16.cc
EXTRA_DIST += rdata/generic/txt_16.h
EXTRA_DIST += rdata/generic/minfo_14.cc
@@ -96,6 +98,7 @@ libdns___la_SOURCES += masterload.h masterload.cc
libdns___la_SOURCES += message.h message.cc
libdns___la_SOURCES += messagerenderer.h messagerenderer.cc
libdns___la_SOURCES += name.h name.cc
+libdns___la_SOURCES += name_internal.h
libdns___la_SOURCES += nsec3hash.h nsec3hash.cc
libdns___la_SOURCES += opcode.h opcode.cc
libdns___la_SOURCES += rcode.h rcode.cc
@@ -154,7 +157,7 @@ libdns___include_HEADERS = \
rrttl.h \
tsigkey.h
# Purposely not installing these headers:
-# util/*.h: used only internally, and not actually DNS specific
+# name_internal.h: used only internally, and not actually DNS specific
# rdata/*/detail/*.h: these are internal use only
# rrclass-placeholder.h
# rrtype-placeholder.h
diff --git a/src/lib/dns/benchmarks/.gitignore b/src/lib/dns/benchmarks/.gitignore
new file mode 100644
index 0000000..c1c840b
--- /dev/null
+++ b/src/lib/dns/benchmarks/.gitignore
@@ -0,0 +1,2 @@
+/message_renderer_bench
+/rdatarender_bench
diff --git a/src/lib/dns/benchmarks/Makefile.am b/src/lib/dns/benchmarks/Makefile.am
index 0d7856f..ff591cc 100644
--- a/src/lib/dns/benchmarks/Makefile.am
+++ b/src/lib/dns/benchmarks/Makefile.am
@@ -9,10 +9,16 @@ endif
CLEANFILES = *.gcno *.gcda
-noinst_PROGRAMS = rdatarender_bench
+noinst_PROGRAMS = rdatarender_bench message_renderer_bench
+
rdatarender_bench_SOURCES = rdatarender_bench.cc
rdatarender_bench_LDADD = $(top_builddir)/src/lib/dns/libdns++.la
rdatarender_bench_LDADD += $(top_builddir)/src/lib/util/libutil.la
rdatarender_bench_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
-rdatarender_bench_LDADD += $(SQLITE_LIBS)
+
+message_renderer_bench_SOURCES = message_renderer_bench.cc
+message_renderer_bench_SOURCES += oldmessagerenderer.h oldmessagerenderer.cc
+message_renderer_bench_LDADD = $(top_builddir)/src/lib/dns/libdns++.la
+message_renderer_bench_LDADD += $(top_builddir)/src/lib/util/libutil.la
+message_renderer_bench_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
diff --git a/src/lib/dns/benchmarks/message_renderer_bench.cc b/src/lib/dns/benchmarks/message_renderer_bench.cc
new file mode 100644
index 0000000..33cd65b
--- /dev/null
+++ b/src/lib/dns/benchmarks/message_renderer_bench.cc
@@ -0,0 +1,176 @@
+// Copyright (C) 2012 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 <bench/benchmark.h>
+
+#include <dns/name.h>
+#include <dns/messagerenderer.h>
+#include <oldmessagerenderer.h>
+
+#include <cassert>
+#include <vector>
+
+using namespace std;
+using namespace isc::util;
+using namespace isc::bench;
+using namespace isc::dns;
+
+namespace {
+// This templated test performs rendering given set of names using
+// a given (templated) MessageRenderer implementation. We can check the
+// performance when we modify the renderer implementation by comparing the
+// old and new implementation for the same data.
+template <typename T>
+class MessageRendererBenchMark {
+public:
+ MessageRendererBenchMark(const vector<Name>& names) :
+ names_(names)
+ {}
+ unsigned int run() {
+ renderer_.clear();
+ vector<Name>::const_iterator it = names_.begin();
+ const vector<Name>::const_iterator it_end = names_.end();
+ for (; it != it_end; ++it) {
+ renderer_.writeName(*it);
+ }
+ // Make sure truncation didn't accidentally happen.
+ assert(!renderer_.isTruncated());
+ return (names_.size());
+ }
+private:
+ T renderer_;
+ const vector<Name>& names_;
+};
+
+//
+// Builtin benchmark data.
+//
+// This consists of all names contained in a response from a root server for
+// the query for "www.example.com" (as of this implementing).
+const char* const root_to_com_names[] = {
+ // question section
+ "www.example.com",
+ // authority section
+ "com", "a.gtld-servers.net", "com", "b.gtld-servers.net",
+ "com", "c.gtld-servers.net", "com", "d.gtld-servers.net",
+ "com", "e.gtld-servers.net", "com", "f.gtld-servers.net",
+ "com", "g.gtld-servers.net", "com", "h.gtld-servers.net",
+ "com", "i.gtld-servers.net", "com", "j.gtld-servers.net",
+ "com", "k.gtld-servers.net", "com", "l.gtld-servers.net",
+ "com", // owner name of DS
+ "com", // owner name of RRSIG(DS)
+ // additional section. a and b has both AAAA and A; others have A only.
+ "a.gtld-servers.net", "a.gtld-servers.net",
+ "b.gtld-servers.net", "b.gtld-servers.net",
+ "c.gtld-servers.net", "d.gtld-servers.net", "e.gtld-servers.net",
+ "f.gtld-servers.net", "g.gtld-servers.net", "h.gtld-servers.net",
+ "i.gtld-servers.net", "j.gtld-servers.net", "k.gtld-servers.net",
+ "l.gtld-servers.net", "m.gtld-servers.net",
+ NULL
+};
+
+// Names contained a typical "NXDOMAIN" response: the question, the owner
+// name of SOA, and its MNAME and RNAME.
+const char* const example_nxdomain_names[] = {
+ "www.example.com", "example.com", "ns.example.com", "root.example.com",
+ NULL
+};
+
+// Names contained a typical "SERVFAIL" response: only the question.
+const char* const example_servfail_names[] = {
+ "www.example.com", NULL
+};
+
+// An experimental "dumb" renderer for comparison. It doesn't do any name
+// compression. It simply ignores all setter method, returns a dummy value
+// for getter methods, and write names to the internal buffer as plain binary
+// data.
+class DumbMessageRenderer : public AbstractMessageRenderer {
+public:
+ virtual void clear() {}
+ virtual size_t getLengthLimit() const { return (512); }
+ virtual void setLengthLimit(const size_t) {}
+ virtual bool isTruncated() const { return (false); }
+ virtual void setTruncated() {}
+ virtual CompressMode getCompressMode() const { return (CASE_INSENSITIVE); }
+ virtual void setCompressMode(const CompressMode) {}
+ virtual void writeName(const Name& name, const bool = false) {
+ name.toWire(getBuffer());
+ }
+};
+
+void
+usage() {
+ cerr << "Usage: message_renderer_bench [-n iterations]" << endl;
+ exit (1);
+}
+}
+
+int
+main(int argc, char* argv[]) {
+ int ch;
+ int iteration = 100000;
+ while ((ch = getopt(argc, argv, "n:")) != -1) {
+ switch (ch) {
+ case 'n':
+ iteration = atoi(optarg);
+ break;
+ case '?':
+ default:
+ usage();
+ }
+ }
+ argc -= optind;
+ argv += optind;
+ if (argc != 0) {
+ usage();
+ }
+
+ cout << "Parameters:" << endl;
+ cout << " Iterations: " << iteration << endl;
+
+ typedef pair<const char* const*, string> DataSpec;
+ vector<DataSpec> spec_list;
+ spec_list.push_back(DataSpec(root_to_com_names, "(positive response)"));
+ spec_list.push_back(DataSpec(example_nxdomain_names,
+ "(NXDOMAIN response)"));
+ spec_list.push_back(DataSpec(example_servfail_names,
+ "(SERVFAIL response)"));
+ for (vector<DataSpec>::const_iterator it = spec_list.begin();
+ it != spec_list.end();
+ ++it) {
+ vector<Name> names;
+ for (size_t i = 0; it->first[i] != NULL; ++i) {
+ names.push_back(Name(it->first[i]));
+ }
+
+ typedef MessageRendererBenchMark<OldMessageRenderer>
+ OldRendererBenchMark;
+ cout << "Benchmark for old MessageRenderer " << it->second << endl;
+ BenchMark<OldRendererBenchMark>(iteration,
+ OldRendererBenchMark(names));
+
+ typedef MessageRendererBenchMark<DumbMessageRenderer>
+ DumbRendererBenchMark;
+ cout << "Benchmark for dumb MessageRenderer " << it->second << endl;
+ BenchMark<DumbRendererBenchMark>(iteration,
+ DumbRendererBenchMark(names));
+
+ typedef MessageRendererBenchMark<MessageRenderer> RendererBenchMark;
+ cout << "Benchmark for new MessageRenderer " << it->second << endl;
+ BenchMark<RendererBenchMark>(iteration, RendererBenchMark(names));
+ }
+
+ return (0);
+}
diff --git a/src/lib/dns/benchmarks/oldmessagerenderer.cc b/src/lib/dns/benchmarks/oldmessagerenderer.cc
new file mode 100644
index 0000000..cd5c4e2
--- /dev/null
+++ b/src/lib/dns/benchmarks/oldmessagerenderer.cc
@@ -0,0 +1,278 @@
+// Copyright (C) 2009 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 <exceptions/exceptions.h>
+#include <util/buffer.h>
+#include <dns/name.h>
+#include <oldmessagerenderer.h>
+
+#include <cctype>
+#include <cassert>
+#include <set>
+
+using namespace isc::util;
+
+namespace isc {
+namespace dns {
+
+namespace { // hide internal-only names from the public namespaces
+///
+/// \brief The \c NameCompressNode class represents a pointer to a name
+/// rendered in the internal buffer for the \c MessageRendererImpl object.
+///
+/// A \c MessageRendererImpl object maintains a set of the \c NameCompressNode
+/// objects, and searches the set for the position of the longest match
+/// (ancestor) name against each new name to be rendered into the buffer.
+struct NameCompressNode {
+ NameCompressNode(const OldMessageRenderer& renderer,
+ const OutputBuffer& buffer, const size_t pos,
+ const size_t len) :
+ renderer_(renderer), buffer_(buffer), pos_(pos), len_(len) {}
+ /// The renderer that performs name compression using the node.
+ /// This is kept in each node to detect the compression mode
+ /// (case-sensitive or not) in the comparison functor (\c NameCompare).
+ const OldMessageRenderer& renderer_;
+ /// The buffer in which the corresponding name is rendered.
+ const OutputBuffer& buffer_;
+ /// The position (offset from the beginning) in the buffer where the
+ /// name starts.
+ uint16_t pos_;
+ /// The length of the corresponding name.
+ uint16_t len_;
+};
+
+///
+/// \brief The \c NameCompare class is a functor that gives ordering among
+/// \c NameCompressNode objects stored in \c MessageRendererImpl::nodeset_.
+///
+/// Its only public method as a functor, \c operator(), gives the ordering
+/// between two \c NameCompressNode objects in terms of equivalence, that is,
+/// returns whether one is "less than" the other.
+/// For our purpose we only need to distinguish two different names, so the
+/// ordering is different from the canonical DNS name order used in DNSSEC;
+/// basically, it gives the case-insensitive ordering of the two names as their
+/// textual representation.
+struct NameCompare : public std::binary_function<NameCompressNode,
+ NameCompressNode,
+ bool> {
+ ///
+ /// Returns true if n1 < n2 as a result of case-insensitive comparison;
+ /// otherwise return false.
+ ///
+ /// The name corresponding to \c n1 or \c n2 may be compressed, in which
+ /// case we must follow the compression pointer in the associated buffer.
+ /// The helper private method \c nextPosition() gives the position in the
+ /// buffer for the next character, taking into account compression.
+ ///
+ bool operator()(const NameCompressNode& n1,
+ const NameCompressNode& n2) const
+ {
+ if (n1.len_ < n2.len_) {
+ return (true);
+ } else if (n1.len_ > n2.len_) {
+ return (false);
+ }
+
+ const bool case_sensitive =
+ (n1.renderer_.getCompressMode() == OldMessageRenderer::CASE_SENSITIVE);
+
+ uint16_t pos1 = n1.pos_;
+ uint16_t pos2 = n2.pos_;
+ uint16_t l1 = 0;
+ uint16_t l2 = 0;
+ for (uint16_t i = 0; i < n1.len_; i++, pos1++, pos2++) {
+ pos1 = nextPosition(n1.buffer_, pos1, l1);
+ pos2 = nextPosition(n2.buffer_, pos2, l2);
+ if (case_sensitive) {
+ if (n1.buffer_[pos1] < n2.buffer_[pos2]) {
+ return (true);
+ } else if (n1.buffer_[pos1] > n2.buffer_[pos2]) {
+ return (false);
+ }
+ } else {
+ if (tolower(n1.buffer_[pos1]) < tolower(n2.buffer_[pos2])) {
+ return (true);
+ } else if (tolower(n1.buffer_[pos1]) >
+ tolower(n2.buffer_[pos2])) {
+ return (false);
+ }
+ }
+ }
+
+ return (false);
+ }
+
+private:
+ uint16_t nextPosition(const OutputBuffer& buffer,
+ uint16_t pos, uint16_t& llen) const
+ {
+ if (llen == 0) {
+ size_t i = 0;
+
+ while ((buffer[pos] & Name::COMPRESS_POINTER_MARK8) ==
+ Name::COMPRESS_POINTER_MARK8) {
+ pos = (buffer[pos] & ~Name::COMPRESS_POINTER_MARK8) *
+ 256 + buffer[pos + 1];
+
+ // This loop should stop as long as the buffer has been
+ // constructed validly and the search/insert argument is based
+ // on a valid name, which is an assumption for this class.
+ // But we'll abort if a bug could cause an infinite loop.
+ i += 2;
+ assert(i < Name::MAX_WIRE);
+ }
+ llen = buffer[pos];
+ } else {
+ --llen;
+ }
+ return (pos);
+ }
+};
+}
+
+///
+/// \brief The \c MessageRendererImpl class is the actual implementation of
+/// \c MessageRenderer.
+///
+/// The implementation is hidden from applications. We can refer to specific
+/// members of this class only within the implementation source file.
+///
+struct OldMessageRenderer::MessageRendererImpl {
+ /// \brief Constructor from an output buffer.
+ ///
+ MessageRendererImpl() :
+ nbuffer_(Name::MAX_WIRE), msglength_limit_(512),
+ truncated_(false), compress_mode_(OldMessageRenderer::CASE_INSENSITIVE)
+ {}
+ /// A local working buffer to convert each given name into wire format.
+ /// This could be a local variable of the \c writeName() method, but
+ /// we keep it in the class so that we can reuse it and avoid construction
+ /// overhead.
+ OutputBuffer nbuffer_;
+ /// A set of compression pointers.
+ std::set<NameCompressNode, NameCompare> nodeset_;
+ /// The maximum length of rendered data that can fit without
+ /// truncation.
+ uint16_t msglength_limit_;
+ /// A boolean flag that indicates truncation has occurred while rendering
+ /// the data.
+ bool truncated_;
+ /// The name compression mode.
+ CompressMode compress_mode_;
+};
+
+OldMessageRenderer::OldMessageRenderer() :
+ AbstractMessageRenderer(),
+ impl_(new MessageRendererImpl)
+{}
+
+OldMessageRenderer::~OldMessageRenderer() {
+ delete impl_;
+}
+
+void
+OldMessageRenderer::clear() {
+ AbstractMessageRenderer::clear();
+ impl_->nbuffer_.clear();
+ impl_->nodeset_.clear();
+ impl_->msglength_limit_ = 512;
+ impl_->truncated_ = false;
+ impl_->compress_mode_ = CASE_INSENSITIVE;
+}
+
+size_t
+OldMessageRenderer::getLengthLimit() const {
+ return (impl_->msglength_limit_);
+}
+
+void
+OldMessageRenderer::setLengthLimit(const size_t len) {
+ impl_->msglength_limit_ = len;
+}
+
+bool
+OldMessageRenderer::isTruncated() const {
+ return (impl_->truncated_);
+}
+
+void
+OldMessageRenderer::setTruncated() {
+ impl_->truncated_ = true;
+}
+
+OldMessageRenderer::CompressMode
+OldMessageRenderer::getCompressMode() const {
+ return (impl_->compress_mode_);
+}
+
+void
+OldMessageRenderer::setCompressMode(const CompressMode mode) {
+ impl_->compress_mode_ = mode;
+}
+
+void
+OldMessageRenderer::writeName(const Name& name, const bool compress) {
+ impl_->nbuffer_.clear();
+ name.toWire(impl_->nbuffer_);
+
+ unsigned int i;
+ std::set<NameCompressNode, NameCompare>::const_iterator notfound =
+ impl_->nodeset_.end();
+ std::set<NameCompressNode, NameCompare>::const_iterator n = notfound;
+
+ // Find the longest ancestor name in the rendered set that matches the
+ // given name.
+ for (i = 0; i < impl_->nbuffer_.getLength(); i += impl_->nbuffer_[i] + 1) {
+ // skip the trailing null label
+ if (impl_->nbuffer_[i] == 0) {
+ continue;
+ }
+ n = impl_->nodeset_.find(NameCompressNode(*this, impl_->nbuffer_, i,
+ impl_->nbuffer_.getLength() -
+ i));
+ if (n != notfound) {
+ break;
+ }
+ }
+
+ // Record the current offset before extending the buffer.
+ const size_t offset = getLength();
+ // Write uncompress part...
+ writeData(impl_->nbuffer_.getData(),
+ compress ? i : impl_->nbuffer_.getLength());
+ if (compress && n != notfound) {
+ // ...and compression pointer if available.
+ uint16_t pointer = (*n).pos_;
+ pointer |= Name::COMPRESS_POINTER_MARK16;
+ writeUint16(pointer);
+ }
+
+ // Finally, add to the set the newly rendered name and its ancestors that
+ // have not been in the set.
+ for (unsigned int j = 0; j < i; j += impl_->nbuffer_[j] + 1) {
+ if (impl_->nbuffer_[j] == 0) {
+ continue;
+ }
+ if (offset + j > Name::MAX_COMPRESS_POINTER) {
+ break;
+ }
+ impl_->nodeset_.insert(NameCompressNode(*this, getBuffer(),
+ offset + j,
+ impl_->nbuffer_.getLength() -
+ j));
+ }
+}
+
+}
+}
diff --git a/src/lib/dns/benchmarks/oldmessagerenderer.h b/src/lib/dns/benchmarks/oldmessagerenderer.h
new file mode 100644
index 0000000..14e7aee
--- /dev/null
+++ b/src/lib/dns/benchmarks/oldmessagerenderer.h
@@ -0,0 +1,55 @@
+// Copyright (C) 2009 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 __OLDMESSAGERENDERER_H
+#define __OLDMESSAGERENDERER_H 1
+
+//
+// This is a copy of an older version of MessageRenderer class. It is kept
+// here to provide a benchmark target.
+//
+
+#include <dns/messagerenderer.h>
+
+namespace isc {
+namespace dns {
+
+class OldMessageRenderer : public AbstractMessageRenderer {
+public:
+ using AbstractMessageRenderer::CASE_INSENSITIVE;
+ using AbstractMessageRenderer::CASE_SENSITIVE;
+
+ /// \brief Constructor from an output buffer.
+ OldMessageRenderer();
+
+ virtual ~OldMessageRenderer();
+ virtual bool isTruncated() const;
+ virtual size_t getLengthLimit() const;
+ virtual CompressMode getCompressMode() const;
+ virtual void setTruncated();
+ virtual void setLengthLimit(size_t len);
+ virtual void setCompressMode(CompressMode mode);
+ virtual void clear();
+ virtual void writeName(const Name& name, bool compress = true);
+private:
+ struct MessageRendererImpl;
+ MessageRendererImpl* impl_;
+};
+}
+}
+#endif // __OLDMESSAGERENDERER_H
+
+// Local Variables:
+// mode: c++
+// End:
diff --git a/src/lib/dns/labelsequence.cc b/src/lib/dns/labelsequence.cc
index 64cc519..0ec450f 100644
--- a/src/lib/dns/labelsequence.cc
+++ b/src/lib/dns/labelsequence.cc
@@ -13,25 +13,33 @@
// PERFORMANCE OF THIS SOFTWARE.
#include <dns/labelsequence.h>
+#include <dns/name_internal.h>
#include <exceptions/exceptions.h>
-#include <iostream>
+#include <boost/functional/hash.hpp>
+
namespace isc {
namespace dns {
const char*
LabelSequence::getData(size_t *len) const {
+ *len = getDataLength();
+ return (&name_.ndata_[name_.offsets_[first_label_]]);
+}
+
+size_t
+LabelSequence::getDataLength() const {
// If the labelsequence is absolute, the current last_label_ falls
// out of the vector (since it points to the 'label' after the
// root label, which doesn't exist; in that case, return
// the length for the 'previous' label (the root label) plus
// one (for the root label zero octet)
if (isAbsolute()) {
- *len = name_.offsets_[last_label_ - 1] - name_.offsets_[first_label_] + 1;
+ return (name_.offsets_[last_label_ - 1] -
+ name_.offsets_[first_label_] + 1);
} else {
- *len = name_.offsets_[last_label_] - name_.offsets_[first_label_];
+ return (name_.offsets_[last_label_] - name_.offsets_[first_label_]);
}
- return (&name_.ndata_[name_.offsets_[first_label_]]);
}
bool
@@ -44,10 +52,21 @@ LabelSequence::equals(const LabelSequence& other, bool case_sensitive) const {
return (false);
}
if (case_sensitive) {
- return (strncasecmp(data, other_data, len) == 0);
- } else {
return (strncmp(data, other_data, len) == 0);
}
+
+ // As long as the data was originally validated as (part of) a name,
+ // label length must never be a capital ascii character, so we can
+ // simply compare them after converting to lower characters.
+ for (size_t i = 0; i < len; ++i) {
+ const unsigned char ch = data[i];
+ const unsigned char other_ch = other_data[i];
+ if (isc::dns::name::internal::maptolower[ch] !=
+ isc::dns::name::internal::maptolower[other_ch]) {
+ return (false);
+ }
+ }
+ return (true);
}
void
@@ -73,5 +92,23 @@ LabelSequence::isAbsolute() const {
return (last_label_ == name_.offsets_.size());
}
+size_t
+LabelSequence::getHash(bool case_sensitive) const {
+ size_t length;
+ const char* s = getData(&length);
+ if (length > 16) {
+ length = 16;
+ }
+
+ size_t hash_val = 0;
+ while (length > 0) {
+ const unsigned char c = *s++;
+ boost::hash_combine(hash_val, case_sensitive ? c :
+ isc::dns::name::internal::maptolower[c]);
+ --length;
+ }
+ return (hash_val);
+}
+
} // end namespace dns
} // end namespace isc
diff --git a/src/lib/dns/labelsequence.h b/src/lib/dns/labelsequence.h
index 6fb1501..6b10b67 100644
--- a/src/lib/dns/labelsequence.h
+++ b/src/lib/dns/labelsequence.h
@@ -69,6 +69,22 @@ public:
/// \return Pointer to the wire-format data of this label sequence
const char* getData(size_t* len) const;
+ /// \brief Return the length of the wire-format data of this LabelSequence
+ ///
+ /// This method returns the number of octets for the data that would
+ /// be returned by the \c getData() method.
+ ///
+ /// Note that the return value of this method is always positive.
+ /// Note also that if the return value of this method is 1, it means the
+ /// sequence consists of the null label, i.e., a single "dot", and vice
+ /// versa.
+ ///
+ /// \note The data pointed to is only valid if the original Name
+ /// object is still in scope
+ ///
+ /// \return The length of the data of the label sequence in octets.
+ size_t getDataLength() const;
+
/// \brief Compares two label sequences.
///
/// Performs a (optionally case-insensitive) comparison between this
@@ -105,7 +121,7 @@ public:
/// \brief Returns the current number of labels for this LabelSequence
///
/// \return The number of labels
- size_t getLabelCount() const { return last_label_ - first_label_; }
+ size_t getLabelCount() const { return (last_label_ - first_label_); }
/// \brief Returns the original Name object associated with this
/// LabelSequence
@@ -116,7 +132,32 @@ public:
/// LabelSequence itself.
///
/// \return Reference to the original Name object
- const Name& getName() const { return name_; }
+ const Name& getName() const { return (name_); }
+
+ /// \brief Calculate a simple hash for the label sequence.
+ ///
+ /// This method calculates a hash value for the label sequence as binary
+ /// data. If \c case_sensitive is false, it ignores the case stored in
+ /// the labels; specifically, it normalizes the labels by converting all
+ /// upper case characters to lower case ones and calculates the hash value
+ /// for the result.
+ ///
+ /// This method is intended to provide a lightweight way to store a
+ /// relatively small number of label sequences in a hash table.
+ /// For this reason it only takes into account data up to 16 octets
+ /// (16 was derived from BIND 9's implementation). Also, the function does
+ /// not provide any unpredictability; a specific sequence will always have
+ /// the same hash value. It should therefore not be used in the context
+ /// where an untrusted third party can mount a denial of service attack by
+ /// forcing the application to create a very large number of label
+ /// sequences that have the same hash value and expected to be stored in
+ /// a hash table.
+ ///
+ /// \exception None
+ ///
+ /// \param case_sensitive
+ /// \return A hash value for this label sequence.
+ size_t getHash(bool case_sensitive) const;
/// \brief Checks whether the label sequence is absolute
///
diff --git a/src/lib/dns/messagerenderer.cc b/src/lib/dns/messagerenderer.cc
index bf4795a..d5c7c69 100644
--- a/src/lib/dns/messagerenderer.cc
+++ b/src/lib/dns/messagerenderer.cc
@@ -15,102 +15,105 @@
#include <exceptions/exceptions.h>
#include <util/buffer.h>
#include <dns/name.h>
+#include <dns/name_internal.h>
+#include <dns/labelsequence.h>
#include <dns/messagerenderer.h>
-#include <cctype>
+#include <boost/array.hpp>
+#include <boost/static_assert.hpp>
+
+#include <limits>
#include <cassert>
-#include <set>
+#include <vector>
+using namespace std;
using namespace isc::util;
+using isc::dns::name::internal::maptolower;
namespace isc {
namespace dns {
namespace { // hide internal-only names from the public namespaces
///
-/// \brief The \c NameCompressNode class represents a pointer to a name
+/// \brief The \c OffsetItem class represents a pointer to a name
/// rendered in the internal buffer for the \c MessageRendererImpl object.
///
-/// A \c MessageRendererImpl object maintains a set of the \c NameCompressNode
-/// objects, and searches the set for the position of the longest match
-/// (ancestor) name against each new name to be rendered into the buffer.
-struct NameCompressNode {
- NameCompressNode(const MessageRenderer& renderer,
- const OutputBuffer& buffer, const size_t pos,
- const size_t len) :
- renderer_(renderer), buffer_(buffer), pos_(pos), len_(len) {}
- /// The renderer that performs name compression using the node.
- /// This is kept in each node to detect the compression mode
- /// (case-sensitive or not) in the comparison functor (\c NameCompare).
- const MessageRenderer& renderer_;
- /// The buffer in which the corresponding name is rendered.
- const OutputBuffer& buffer_;
+/// A \c MessageRendererImpl object maintains a set of \c OffsetItem
+/// objects in a hash table, and searches the table for the position of the
+/// longest match (ancestor) name against each new name to be rendered into
+/// the buffer.
+struct OffsetItem {
+ OffsetItem(size_t hash, size_t pos, size_t len) :
+ hash_(hash), pos_(pos), len_(len)
+ {}
+
+ /// The hash value for the stored name calculated by LabelSequence.getHash.
+ /// This will help make name comparison in \c NameCompare more efficient.
+ size_t hash_;
+
/// The position (offset from the beginning) in the buffer where the
/// name starts.
uint16_t pos_;
- /// The length of the corresponding name.
+
+ /// The length of the corresponding sequence (which is a domain name).
uint16_t len_;
};
+/// \brief The \c NameCompare class is a functor that checks equality
+/// between the name corresponding to an \c OffsetItem object and the name
+/// consists of labels represented by a \c LabelSequence object.
///
-/// \brief The \c NameCompare class is a functor that gives ordering among
-/// \c NameCompressNode objects stored in \c MessageRendererImpl::nodeset_.
-///
-/// Its only public method as a functor, \c operator(), gives the ordering
-/// between two \c NameCompressNode objects in terms of equivalence, that is,
-/// returns whether one is "less than" the other.
-/// For our purpose we only need to distinguish two different names, so the
-/// ordering is different from the canonical DNS name order used in DNSSEC;
-/// basically, it gives the case-insensitive ordering of the two names as their
-/// textual representation.
-struct NameCompare : public std::binary_function<NameCompressNode,
- NameCompressNode,
- bool> {
- ///
- /// Returns true if n1 < n2 as a result of case-insensitive comparison;
- /// otherwise return false.
+/// Template parameter CASE_SENSITIVE determines whether to ignore the case
+/// of the names. This policy doesn't change throughout the lifetime of
+/// this object, so we separate these using template to avoid unnecessary
+/// condition check.
+template <bool CASE_SENSITIVE>
+struct NameCompare {
+ /// \brief Constructor
///
- /// The name corresponding to \c n1 or \c n2 may be compressed, in which
- /// case we must follow the compression pointer in the associated buffer.
- /// The helper private method \c nextPosition() gives the position in the
- /// buffer for the next character, taking into account compression.
- ///
- bool operator()(const NameCompressNode& n1,
- const NameCompressNode& n2) const
- {
- if (n1.len_ < n2.len_) {
- return (true);
- } else if (n1.len_ > n2.len_) {
+ /// \param buffer The buffer for rendering used in the caller renderer
+ /// \param name_buf An input buffer storing the wire-format data of the
+ /// name to be newly rendered (and only that data).
+ /// \param hash The hash value for the name.
+ NameCompare(const OutputBuffer& buffer, InputBuffer& name_buf,
+ size_t hash) :
+ buffer_(&buffer), name_buf_(&name_buf), hash_(hash)
+ {}
+
+ bool operator()(const OffsetItem& item) const {
+ // Trivial inequality check. If either the hash or the total length
+ // doesn't match, the names are obviously different.
+ if (item.hash_ != hash_ || item.len_ != name_buf_->getLength()) {
return (false);
}
- const bool case_sensitive =
- (n1.renderer_.getCompressMode() == MessageRenderer::CASE_SENSITIVE);
-
- uint16_t pos1 = n1.pos_;
- uint16_t pos2 = n2.pos_;
- uint16_t l1 = 0;
- uint16_t l2 = 0;
- for (uint16_t i = 0; i < n1.len_; i++, pos1++, pos2++) {
- pos1 = nextPosition(n1.buffer_, pos1, l1);
- pos2 = nextPosition(n2.buffer_, pos2, l2);
- if (case_sensitive) {
- if (n1.buffer_[pos1] < n2.buffer_[pos2]) {
- return (true);
- } else if (n1.buffer_[pos1] > n2.buffer_[pos2]) {
+ // Compare the name data, character-by-character.
+ // item_pos keeps track of the position in the buffer corresponding to
+ // the character to compare. item_label_len is the number of
+ // characters in the labels where the character pointed by item_pos
+ // belongs. When it reaches zero, nextPosition() identifies the
+ // position for the subsequent label, taking into account name
+ // compression, and resets item_label_len to the length of the new
+ // label.
+ name_buf_->setPosition(0); // buffer can be reused, so reset position
+ uint16_t item_pos = item.pos_;
+ uint16_t item_label_len = 0;
+ for (size_t i = 0; i < item.len_; ++i, ++item_pos) {
+ item_pos = nextPosition(*buffer_, item_pos, item_label_len);
+ const unsigned char ch1 = (*buffer_)[item_pos];
+ const unsigned char ch2 = name_buf_->readUint8();
+ if (CASE_SENSITIVE) {
+ if (ch1 != ch2) {
return (false);
}
} else {
- if (tolower(n1.buffer_[pos1]) < tolower(n2.buffer_[pos2])) {
- return (true);
- } else if (tolower(n1.buffer_[pos1]) >
- tolower(n2.buffer_[pos2])) {
+ if (maptolower[ch1] != maptolower[ch2]) {
return (false);
}
}
}
- return (false);
+ return (true);
}
private:
@@ -138,6 +141,10 @@ private:
}
return (pos);
}
+
+ const OutputBuffer* buffer_;
+ InputBuffer* name_buf_;
+ const size_t hash_;
};
}
@@ -148,20 +155,60 @@ private:
/// The implementation is hidden from applications. We can refer to specific
/// members of this class only within the implementation source file.
///
+/// It internally holds a hash table for OffsetItem objects corresponding
+/// to portions of names rendered in this renderer. The offset information
+/// is used to compress subsequent names to be rendered.
struct MessageRenderer::MessageRendererImpl {
- /// \brief Constructor from an output buffer.
- ///
+ // The size of hash buckets and number of hash entries per bucket for
+ // which space is preallocated and kept reserved for subsequent rendering
+ // to provide better performance. These values are derived from the
+ // BIND 9 implementation that uses a similar hash table.
+ static const size_t BUCKETS = 64;
+ static const size_t RESERVED_ITEMS = 16;
+ static const uint16_t NO_OFFSET = 65535; // used as a marker of 'not found'
+
+ /// \brief Constructor
MessageRendererImpl() :
- nbuffer_(Name::MAX_WIRE), msglength_limit_(512),
- truncated_(false), compress_mode_(MessageRenderer::CASE_INSENSITIVE)
- {}
- /// A local working buffer to convert each given name into wire format.
- /// This could be a local variable of the \c writeName() method, but
- /// we keep it in the class so that we can reuse it and avoid construction
- /// overhead.
- OutputBuffer nbuffer_;
- /// A set of compression pointers.
- std::set<NameCompressNode, NameCompare> nodeset_;
+ msglength_limit_(512), truncated_(false),
+ compress_mode_(MessageRenderer::CASE_INSENSITIVE)
+ {
+ // Reserve some spaces for hash table items.
+ for (size_t i = 0; i < BUCKETS; ++i) {
+ table_[i].reserve(RESERVED_ITEMS);
+ }
+ }
+
+ uint16_t findOffset(const OutputBuffer& buffer, InputBuffer& name_buf,
+ size_t hash, bool case_sensitive) const
+ {
+ // Find a matching entry, if any. We use some heuristics here: often
+ // the same name appers consecutively (like repeating the same owner
+ // name for a single RRset), so in case there's a collision in the
+ // bucket it will be more likely to find it in the tail side of the
+ // bucket.
+ const size_t bucket_id = hash % BUCKETS;
+ vector<OffsetItem>::const_reverse_iterator found;
+ if (case_sensitive) {
+ found = find_if(table_[bucket_id].rbegin(),
+ table_[bucket_id].rend(),
+ NameCompare<true>(buffer, name_buf, hash));
+ } else {
+ found = find_if(table_[bucket_id].rbegin(),
+ table_[bucket_id].rend(),
+ NameCompare<false>(buffer, name_buf, hash));
+ }
+ if (found != table_[bucket_id].rend()) {
+ return (found->pos_);
+ }
+ return (NO_OFFSET);
+ }
+
+ void addOffset(size_t hash, size_t offset, size_t len) {
+ table_[hash % BUCKETS].push_back(OffsetItem(hash, offset, len));
+ }
+
+ // The hash table for the (offset + position in the buffer) entries
+ vector<OffsetItem> table_[BUCKETS];
/// The maximum length of rendered data that can fit without
/// truncation.
uint16_t msglength_limit_;
@@ -170,6 +217,11 @@ struct MessageRenderer::MessageRendererImpl {
bool truncated_;
/// The name compression mode.
CompressMode compress_mode_;
+
+ // Placeholder for hash values as they are calculated in writeName().
+ // Note: we may want to make it a local variable of writeName() if it
+ // works more efficiently.
+ boost::array<size_t, Name::MAX_LABELS> seq_hashes_;
};
MessageRenderer::MessageRenderer() :
@@ -184,11 +236,22 @@ MessageRenderer::~MessageRenderer() {
void
MessageRenderer::clear() {
AbstractMessageRenderer::clear();
- impl_->nbuffer_.clear();
- impl_->nodeset_.clear();
impl_->msglength_limit_ = 512;
impl_->truncated_ = false;
impl_->compress_mode_ = CASE_INSENSITIVE;
+
+ // Clear the hash table. We reserve the minimum space for possible
+ // subsequent use of the renderer.
+ for (size_t i = 0; i < MessageRendererImpl::BUCKETS; ++i) {
+ if (impl_->table_[i].size() > MessageRendererImpl::RESERVED_ITEMS) {
+ // Trim excessive capacity: swap ensures the new capacity is only
+ // reasonably large for the reserved space.
+ vector<OffsetItem> new_table;
+ new_table.reserve(MessageRendererImpl::RESERVED_ITEMS);
+ new_table.swap(impl_->table_[i]);
+ }
+ impl_->table_[i].clear();
+ }
}
size_t
@@ -218,59 +281,81 @@ MessageRenderer::getCompressMode() const {
void
MessageRenderer::setCompressMode(const CompressMode mode) {
+ if (getLength() != 0) {
+ isc_throw(isc::InvalidParameter,
+ "compress mode cannot be changed during rendering");
+ }
impl_->compress_mode_ = mode;
}
void
MessageRenderer::writeName(const Name& name, const bool compress) {
- impl_->nbuffer_.clear();
- name.toWire(impl_->nbuffer_);
-
- unsigned int i;
- std::set<NameCompressNode, NameCompare>::const_iterator notfound =
- impl_->nodeset_.end();
- std::set<NameCompressNode, NameCompare>::const_iterator n = notfound;
-
- // Find the longest ancestor name in the rendered set that matches the
- // given name.
- for (i = 0; i < impl_->nbuffer_.getLength(); i += impl_->nbuffer_[i] + 1) {
- // skip the trailing null label
- if (impl_->nbuffer_[i] == 0) {
- continue;
+ LabelSequence sequence(name);
+ const size_t nlabels = sequence.getLabelCount();
+ size_t data_len;
+ const char* data;
+
+ // Find the offset in the offset table whose name gives the longest
+ // match against the name to be rendered.
+ size_t nlabels_uncomp;
+ uint16_t ptr_offset = MessageRendererImpl::NO_OFFSET;
+ const bool case_sensitive = (impl_->compress_mode_ ==
+ MessageRenderer::CASE_SENSITIVE);
+ for (nlabels_uncomp = 0; nlabels_uncomp < nlabels; ++nlabels_uncomp) {
+ data = sequence.getData(&data_len);
+ if (data_len == 1) { // trailing dot.
+ ++nlabels_uncomp;
+ break;
}
- n = impl_->nodeset_.find(NameCompressNode(*this, impl_->nbuffer_, i,
- impl_->nbuffer_.getLength() -
- i));
- if (n != notfound) {
+ // write with range check for safety
+ impl_->seq_hashes_.at(nlabels_uncomp) =
+ sequence.getHash(impl_->compress_mode_);
+ InputBuffer name_buf(data, data_len);
+ ptr_offset = impl_->findOffset(getBuffer(), name_buf,
+ impl_->seq_hashes_[nlabels_uncomp],
+ case_sensitive);
+ if (ptr_offset != MessageRendererImpl::NO_OFFSET) {
break;
}
+ sequence.stripLeft(1);
}
- // Record the current offset before extending the buffer.
- const size_t offset = getLength();
- // Write uncompress part...
- writeData(impl_->nbuffer_.getData(),
- compress ? i : impl_->nbuffer_.getLength());
- if (compress && n != notfound) {
- // ...and compression pointer if available.
- uint16_t pointer = (*n).pos_;
- pointer |= Name::COMPRESS_POINTER_MARK16;
- writeUint16(pointer);
+ // Record the current offset before updating the offset table
+ size_t offset = getLength();
+ // Write uncompress part:
+ if (nlabels_uncomp > 0 || !compress) {
+ LabelSequence uncomp_sequence(name);
+ if (compress && nlabels > nlabels_uncomp) {
+ // If there's compressed part, strip off that part.
+ uncomp_sequence.stripRight(nlabels - nlabels_uncomp);
+ }
+ data = uncomp_sequence.getData(&data_len);
+ writeData(data, data_len);
+ }
+ // And write compression pointer if available:
+ if (compress && ptr_offset != MessageRendererImpl::NO_OFFSET) {
+ ptr_offset |= Name::COMPRESS_POINTER_MARK16;
+ writeUint16(ptr_offset);
}
- // Finally, add to the set the newly rendered name and its ancestors that
- // have not been in the set.
- for (unsigned int j = 0; j < i; j += impl_->nbuffer_[j] + 1) {
- if (impl_->nbuffer_[j] == 0) {
- continue;
+ // Finally, record the offset and length for each uncompressed sequence
+ // in the hash table. The renderer's buffer has just stored the
+ // corresponding data, so we use the rendered data to get the length
+ // of each label of the names.
+ size_t seqlen = name.getLength();
+ for (size_t i = 0; i < nlabels_uncomp; ++i) {
+ const uint8_t label_len = getBuffer()[offset];
+ if (label_len == 0) { // offset for root doesn't need to be stored.
+ break;
}
- if (offset + j > Name::MAX_COMPRESS_POINTER) {
+ if (offset > Name::MAX_COMPRESS_POINTER) {
break;
}
- impl_->nodeset_.insert(NameCompressNode(*this, getBuffer(),
- offset + j,
- impl_->nbuffer_.getLength() -
- j));
+ // Store the tuple of <hash, offset, len> to the table. Note that we
+ // already know the hash value for each name.
+ impl_->addOffset(impl_->seq_hashes_[i], offset, seqlen);
+ offset += (label_len + 1);
+ seqlen -= (label_len + 1);
}
}
diff --git a/src/lib/dns/messagerenderer.h b/src/lib/dns/messagerenderer.h
index f7f2381..4c1c92a 100644
--- a/src/lib/dns/messagerenderer.h
+++ b/src/lib/dns/messagerenderer.h
@@ -359,7 +359,17 @@ public:
virtual CompressMode getCompressMode() const;
virtual void setTruncated();
virtual void setLengthLimit(size_t len);
+
+ /// This implementation does not allow this call in the middle of
+ /// rendering (i.e. after at least one name is rendered) due to
+ /// restriction specific to the internal implementation. Such attempts
+ /// will result in an \c isc::InvalidParameter exception.
+ ///
+ /// This shouldn't be too restrictive in practice; there's no known
+ /// practical case for such a mixed compression policy in a single
+ /// message.
virtual void setCompressMode(CompressMode mode);
+
virtual void clear();
virtual void writeName(const Name& name, bool compress = true);
private:
diff --git a/src/lib/dns/name.cc b/src/lib/dns/name.cc
index 772417f..b56efc4 100644
--- a/src/lib/dns/name.cc
+++ b/src/lib/dns/name.cc
@@ -23,11 +23,13 @@
#include <util/buffer.h>
#include <dns/exceptions.h>
#include <dns/name.h>
+#include <dns/name_internal.h>
#include <dns/messagerenderer.h>
using namespace std;
using namespace isc::util;
using isc::dns::NameComparisonResult;
+using namespace isc::dns::name::internal;
namespace isc {
namespace dns {
@@ -46,12 +48,12 @@ namespace {
/// we chose the naive but simple hardcoding approach.
///
/// These definitions are derived from BIND 9's libdns module.
-/// Note: it was not clear why the maptolower array was needed rather than
-/// using the standard tolower() function. It was perhaps due performance
-/// concern, but we were not sure. Even if it was performance reasons, we
-/// should carefully assess the effect via benchmarking to avoid the pitfall of
-/// premature optimization. We should revisit this point later.
-static const char digitvalue[256] = {
+/// Note: we could use the standard tolower() function instead of the
+/// maptolower array, but a benchmark indicated that the private array could
+/// improve the performance of message rendering (which internally uses the
+/// array heavily) about 27%. Since we want to achieve very good performance
+/// for message rendering in some cases, we'll keep using it.
+const char digitvalue[256] = {
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 16
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 32
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 48
@@ -69,8 +71,11 @@ static const char digitvalue[256] = {
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 256
};
+}
-static const unsigned char maptolower[] = {
+namespace name {
+namespace internal {
+const unsigned char maptolower[] = {
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
@@ -104,7 +109,8 @@ static const unsigned char maptolower[] = {
0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
};
-}
+} // end of internal
+} // end of name
namespace {
///
diff --git a/src/lib/dns/name_internal.h b/src/lib/dns/name_internal.h
new file mode 100644
index 0000000..e1eab8c
--- /dev/null
+++ b/src/lib/dns/name_internal.h
@@ -0,0 +1,43 @@
+// Copyright (C) 2012 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 __NAME_INTERNAL_H
+#define __NAME_INTERNAL_H 1
+
+// This is effectively a "private" namespace for the Name class implementation,
+// but exposed publicly so the definitions in it can be shared with other
+// modules of the library (as of its introduction, used by LabelSequence and
+// MessageRenderer). It's not expected to be used even by normal applications.
+// This header file is therefore not expected to be installed as part of the
+// library.
+//
+// Note: if it turns out that we need this shortcut for many other places
+// we may even want to make it expose to other BIND 10 modules, but for now
+// we'll keep it semi-private (note also that except for very performance
+// sensitive applications the standard std::tolower() function should be just
+// sufficient).
+namespace isc {
+namespace dns {
+namespace name {
+namespace internal {
+extern const unsigned char maptolower[];
+} // end of internal
+} // end of name
+} // end of dns
+} // end of isc
+#endif // __NAME_INTERNAL_H
+
+// Local Variables:
+// mode: c++
+// End:
diff --git a/src/lib/dns/rdata/generic/sshfp_44.cc b/src/lib/dns/rdata/generic/sshfp_44.cc
new file mode 100644
index 0000000..6320fd9
--- /dev/null
+++ b/src/lib/dns/rdata/generic/sshfp_44.cc
@@ -0,0 +1,164 @@
+// Copyright (C) 2012 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 <config.h>
+
+#include <string>
+
+#include <boost/lexical_cast.hpp>
+
+#include <exceptions/exceptions.h>
+
+#include <util/buffer.h>
+#include <util/encode/hex.h>
+#include <dns/name.h>
+#include <dns/messagerenderer.h>
+#include <dns/rdata.h>
+#include <dns/rdataclass.h>
+
+using namespace std;
+using namespace boost;
+using namespace isc::util;
+using namespace isc::util::encode;
+
+// BEGIN_ISC_NAMESPACE
+// BEGIN_RDATA_NAMESPACE
+
+SSHFP::SSHFP(InputBuffer& buffer, size_t rdata_len)
+{
+ if (rdata_len < 2) {
+ isc_throw(InvalidRdataLength, "SSHFP record too short");
+ }
+
+ algorithm_ = buffer.readUint8();
+ fingerprint_type_ = buffer.readUint8();
+
+ rdata_len -= 2;
+ fingerprint_.resize(rdata_len);
+ buffer.readData(&fingerprint_[0], rdata_len);
+}
+
+SSHFP::SSHFP(const std::string& sshfp_str)
+{
+ std::istringstream iss(sshfp_str);
+ // peekc should be of iss's char_type for isspace to work
+ std::istringstream::char_type peekc;
+ std::stringbuf fingerprintbuf;
+ uint32_t algorithm, fingerprint_type;
+
+ iss >> algorithm >> fingerprint_type;
+ if (iss.bad() || iss.fail()) {
+ isc_throw(InvalidRdataText, "Invalid SSHFP text");
+ }
+ if ((algorithm < 1) || (algorithm > 2)) {
+ isc_throw(InvalidRdataText, "SSHFP algorithm number out of range");
+ }
+ if (fingerprint_type != 1) {
+ isc_throw(InvalidRdataText, "SSHFP fingerprint type out of range");
+ }
+
+ iss.read(&peekc, 1);
+ if (!iss.good() || !isspace(peekc, iss.getloc())) {
+ isc_throw(InvalidRdataText, "SSHFP presentation format error");
+ }
+
+ iss >> &fingerprintbuf;
+
+ algorithm_ = algorithm;
+ fingerprint_type_ = fingerprint_type;
+ decodeHex(fingerprintbuf.str(), fingerprint_);
+}
+
+SSHFP::SSHFP(uint8_t algorithm, uint8_t fingerprint_type, const string& fingerprint)
+{
+ if ((algorithm < 1) || (algorithm > 2)) {
+ isc_throw(InvalidRdataText, "SSHFP algorithm number out of range");
+ }
+ if (fingerprint_type != 1) {
+ isc_throw(InvalidRdataText, "SSHFP fingerprint type out of range");
+ }
+
+ algorithm_ = algorithm;
+ fingerprint_type_ = fingerprint_type;
+ decodeHex(fingerprint, fingerprint_);
+}
+
+SSHFP::SSHFP(const SSHFP& other) :
+ Rdata(), algorithm_(other.algorithm_), fingerprint_type_(other.fingerprint_type_), fingerprint_(other.fingerprint_)
+{}
+
+void
+SSHFP::toWire(OutputBuffer& buffer) const {
+ buffer.writeUint8(algorithm_);
+ buffer.writeUint8(fingerprint_type_);
+ buffer.writeData(&fingerprint_[0], fingerprint_.size());
+}
+
+void
+SSHFP::toWire(AbstractMessageRenderer& renderer) const {
+ renderer.writeUint8(algorithm_);
+ renderer.writeUint8(fingerprint_type_);
+ renderer.writeData(&fingerprint_[0], fingerprint_.size());
+}
+
+string
+SSHFP::toText() const {
+ return (lexical_cast<string>(static_cast<int>(algorithm_)) +
+ " " + lexical_cast<string>(static_cast<int>(fingerprint_type_)) +
+ " " + encodeHex(fingerprint_));
+}
+
+int
+SSHFP::compare(const Rdata& other) const {
+ const SSHFP& other_sshfp = dynamic_cast<const SSHFP&>(other);
+
+ /* This doesn't really make any sort of sense, but in the name of
+ consistency... */
+
+ if (algorithm_ < other_sshfp.algorithm_) {
+ return (-1);
+ } else if (algorithm_ > other_sshfp.algorithm_) {
+ return (1);
+ }
+
+ if (fingerprint_type_ < other_sshfp.fingerprint_type_) {
+ return (-1);
+ } else if (fingerprint_type_ > other_sshfp.fingerprint_type_) {
+ return (1);
+ }
+
+ size_t this_len = fingerprint_.size();
+ size_t other_len = other_sshfp.fingerprint_.size();
+ size_t cmplen = min(this_len, other_len);
+ int cmp = memcmp(&fingerprint_[0], &other_sshfp.fingerprint_[0], cmplen);
+ if (cmp != 0) {
+ return (cmp);
+ } else {
+ return ((this_len == other_len)
+ ? 0 : (this_len < other_len) ? -1 : 1);
+ }
+}
+
+uint8_t
+SSHFP::getSSHFPAlgorithmNumber() const {
+ return (algorithm_);
+}
+
+uint8_t
+SSHFP::getSSHFPFingerprintType() const {
+ return (fingerprint_type_);
+}
+
+// END_RDATA_NAMESPACE
+// END_ISC_NAMESPACE
diff --git a/src/lib/dns/rdata/generic/sshfp_44.h b/src/lib/dns/rdata/generic/sshfp_44.h
new file mode 100644
index 0000000..c3ba944
--- /dev/null
+++ b/src/lib/dns/rdata/generic/sshfp_44.h
@@ -0,0 +1,58 @@
+// Copyright (C) 2012 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.
+
+// BEGIN_HEADER_GUARD
+
+#include <stdint.h>
+
+#include <string>
+
+#include <dns/name.h>
+#include <dns/rdata.h>
+
+// BEGIN_ISC_NAMESPACE
+
+// BEGIN_COMMON_DECLARATIONS
+// END_COMMON_DECLARATIONS
+
+// BEGIN_RDATA_NAMESPACE
+
+class SSHFP : public Rdata {
+public:
+ // BEGIN_COMMON_MEMBERS
+ // END_COMMON_MEMBERS
+
+ SSHFP(uint8_t algorithm, uint8_t fingerprint_type, const std::string& fingerprint);
+
+ ///
+ /// Specialized methods
+ ///
+ uint8_t getSSHFPAlgorithmNumber() const;
+ uint8_t getSSHFPFingerprintType() const;
+
+private:
+ /// Note: this is a prototype version; we may reconsider
+ /// this representation later.
+ uint8_t algorithm_;
+ uint8_t fingerprint_type_;
+ std::vector<uint8_t> fingerprint_;
+};
+
+// END_RDATA_NAMESPACE
+// END_ISC_NAMESPACE
+// END_HEADER_GUARD
+
+// Local Variables:
+// mode: c++
+// End:
diff --git a/src/lib/dns/rrset.cc b/src/lib/dns/rrset.cc
index 776d49f..9a55f5f 100644
--- a/src/lib/dns/rrset.cc
+++ b/src/lib/dns/rrset.cc
@@ -113,6 +113,16 @@ AbstractRRset::toWire(AbstractMessageRenderer& renderer) const {
return (rrs_written);
}
+bool
+AbstractRRset::isSameKind(const AbstractRRset& other) const {
+ // Compare classes last as they're likely to be identical. Compare
+ // names late in the list too, as these are expensive. So we compare
+ // types first, names second and classes last.
+ return (getType() == other.getType() &&
+ getName() == other.getName() &&
+ getClass() == other.getClass());
+}
+
ostream&
operator<<(ostream& os, const AbstractRRset& rrset) {
os << rrset.toText();
diff --git a/src/lib/dns/rrset.h b/src/lib/dns/rrset.h
index 5042a98..7ad555f 100644
--- a/src/lib/dns/rrset.h
+++ b/src/lib/dns/rrset.h
@@ -475,6 +475,14 @@ public:
/// \brief Clear the RRSIGs for this RRset
virtual void removeRRsig() = 0;
+ /// \brief Check whether two RRsets are of the same kind
+ ///
+ /// Checks if two RRsets have the same name, RR type, and RR class.
+ ///
+ /// \param other Pointer to another AbstractRRset to compare
+ /// against.
+ virtual bool isSameKind(const AbstractRRset& other) const;
+
//@}
};
diff --git a/src/lib/dns/tests/.gitignore b/src/lib/dns/tests/.gitignore
new file mode 100644
index 0000000..d6d1ec8
--- /dev/null
+++ b/src/lib/dns/tests/.gitignore
@@ -0,0 +1 @@
+/run_unittests
diff --git a/src/lib/dns/tests/Makefile.am b/src/lib/dns/tests/Makefile.am
index 7ba0314..26b4630 100644
--- a/src/lib/dns/tests/Makefile.am
+++ b/src/lib/dns/tests/Makefile.am
@@ -33,6 +33,7 @@ run_unittests_SOURCES += rdata_in_a_unittest.cc rdata_in_aaaa_unittest.cc
run_unittests_SOURCES += rdata_ns_unittest.cc rdata_soa_unittest.cc
run_unittests_SOURCES += rdata_txt_like_unittest.cc
run_unittests_SOURCES += rdata_mx_unittest.cc
+run_unittests_SOURCES += rdata_sshfp_unittest.cc
run_unittests_SOURCES += rdata_ptr_unittest.cc rdata_cname_unittest.cc
run_unittests_SOURCES += rdata_dname_unittest.cc
run_unittests_SOURCES += rdata_afsdb_unittest.cc
diff --git a/src/lib/dns/tests/labelsequence_unittest.cc b/src/lib/dns/tests/labelsequence_unittest.cc
index 72326be..98bb99c 100644
--- a/src/lib/dns/tests/labelsequence_unittest.cc
+++ b/src/lib/dns/tests/labelsequence_unittest.cc
@@ -13,11 +13,20 @@
// PERFORMANCE OF THIS SOFTWARE.
#include <dns/labelsequence.h>
+#include <dns/name.h>
#include <exceptions/exceptions.h>
#include <gtest/gtest.h>
+#include <boost/functional/hash.hpp>
+
+#include <string>
+#include <set>
+
using namespace isc::dns;
+using namespace std;
+
+namespace {
class LabelSequenceTest : public ::testing::Test {
public:
@@ -37,51 +46,14 @@ public:
// Basic equality tests
TEST_F(LabelSequenceTest, equals_sensitive) {
- EXPECT_TRUE(ls1.equals(ls1));
- EXPECT_FALSE(ls1.equals(ls2));
- EXPECT_TRUE(ls1.equals(ls3));
- EXPECT_FALSE(ls1.equals(ls4));
- EXPECT_FALSE(ls1.equals(ls5));
- EXPECT_FALSE(ls1.equals(ls6));
- EXPECT_FALSE(ls1.equals(ls7));
- EXPECT_FALSE(ls1.equals(ls8));
-
- EXPECT_FALSE(ls2.equals(ls1));
- EXPECT_TRUE(ls2.equals(ls2));
- EXPECT_FALSE(ls2.equals(ls3));
- EXPECT_FALSE(ls2.equals(ls4));
- EXPECT_FALSE(ls2.equals(ls5));
- EXPECT_FALSE(ls2.equals(ls6));
- EXPECT_FALSE(ls2.equals(ls7));
- EXPECT_FALSE(ls2.equals(ls8));
-
- EXPECT_FALSE(ls4.equals(ls1));
- EXPECT_FALSE(ls4.equals(ls2));
- EXPECT_FALSE(ls4.equals(ls3));
- EXPECT_TRUE(ls4.equals(ls4));
- EXPECT_FALSE(ls4.equals(ls5));
- EXPECT_FALSE(ls4.equals(ls6));
- EXPECT_FALSE(ls4.equals(ls7));
- EXPECT_FALSE(ls4.equals(ls8));
-
- EXPECT_FALSE(ls5.equals(ls1));
- EXPECT_FALSE(ls5.equals(ls2));
- EXPECT_FALSE(ls5.equals(ls3));
- EXPECT_FALSE(ls5.equals(ls4));
- EXPECT_TRUE(ls5.equals(ls5));
- EXPECT_FALSE(ls5.equals(ls6));
- EXPECT_FALSE(ls5.equals(ls7));
- EXPECT_FALSE(ls5.equals(ls8));
-}
-
-TEST_F(LabelSequenceTest, equals_insensitive) {
EXPECT_TRUE(ls1.equals(ls1, true));
EXPECT_FALSE(ls1.equals(ls2, true));
EXPECT_TRUE(ls1.equals(ls3, true));
EXPECT_FALSE(ls1.equals(ls4, true));
- EXPECT_TRUE(ls1.equals(ls5, true));
- EXPECT_TRUE(ls1.equals(ls6, true));
+ EXPECT_FALSE(ls1.equals(ls5, true));
+ EXPECT_FALSE(ls1.equals(ls6, true));
EXPECT_FALSE(ls1.equals(ls7, true));
+ EXPECT_FALSE(ls1.equals(ls8, true));
EXPECT_FALSE(ls2.equals(ls1, true));
EXPECT_TRUE(ls2.equals(ls2, true));
@@ -90,14 +62,7 @@ TEST_F(LabelSequenceTest, equals_insensitive) {
EXPECT_FALSE(ls2.equals(ls5, true));
EXPECT_FALSE(ls2.equals(ls6, true));
EXPECT_FALSE(ls2.equals(ls7, true));
-
- EXPECT_TRUE(ls3.equals(ls1, true));
- EXPECT_FALSE(ls3.equals(ls2, true));
- EXPECT_TRUE(ls3.equals(ls3, true));
- EXPECT_FALSE(ls3.equals(ls4, true));
- EXPECT_TRUE(ls3.equals(ls5, true));
- EXPECT_TRUE(ls3.equals(ls6, true));
- EXPECT_FALSE(ls3.equals(ls7, true));
+ EXPECT_FALSE(ls2.equals(ls8, true));
EXPECT_FALSE(ls4.equals(ls1, true));
EXPECT_FALSE(ls4.equals(ls2, true));
@@ -106,14 +71,58 @@ TEST_F(LabelSequenceTest, equals_insensitive) {
EXPECT_FALSE(ls4.equals(ls5, true));
EXPECT_FALSE(ls4.equals(ls6, true));
EXPECT_FALSE(ls4.equals(ls7, true));
+ EXPECT_FALSE(ls4.equals(ls8, true));
- EXPECT_TRUE(ls5.equals(ls1, true));
+ EXPECT_FALSE(ls5.equals(ls1, true));
EXPECT_FALSE(ls5.equals(ls2, true));
- EXPECT_TRUE(ls5.equals(ls3, true));
+ EXPECT_FALSE(ls5.equals(ls3, true));
EXPECT_FALSE(ls5.equals(ls4, true));
EXPECT_TRUE(ls5.equals(ls5, true));
- EXPECT_TRUE(ls5.equals(ls6, true));
+ EXPECT_FALSE(ls5.equals(ls6, true));
EXPECT_FALSE(ls5.equals(ls7, true));
+ EXPECT_FALSE(ls5.equals(ls8, true));
+}
+
+TEST_F(LabelSequenceTest, equals_insensitive) {
+ EXPECT_TRUE(ls1.equals(ls1));
+ EXPECT_FALSE(ls1.equals(ls2));
+ EXPECT_TRUE(ls1.equals(ls3));
+ EXPECT_FALSE(ls1.equals(ls4));
+ EXPECT_TRUE(ls1.equals(ls5));
+ EXPECT_TRUE(ls1.equals(ls6));
+ EXPECT_FALSE(ls1.equals(ls7));
+
+ EXPECT_FALSE(ls2.equals(ls1));
+ EXPECT_TRUE(ls2.equals(ls2));
+ EXPECT_FALSE(ls2.equals(ls3));
+ EXPECT_FALSE(ls2.equals(ls4));
+ EXPECT_FALSE(ls2.equals(ls5));
+ EXPECT_FALSE(ls2.equals(ls6));
+ EXPECT_FALSE(ls2.equals(ls7));
+
+ EXPECT_TRUE(ls3.equals(ls1));
+ EXPECT_FALSE(ls3.equals(ls2));
+ EXPECT_TRUE(ls3.equals(ls3));
+ EXPECT_FALSE(ls3.equals(ls4));
+ EXPECT_TRUE(ls3.equals(ls5));
+ EXPECT_TRUE(ls3.equals(ls6));
+ EXPECT_FALSE(ls3.equals(ls7));
+
+ EXPECT_FALSE(ls4.equals(ls1));
+ EXPECT_FALSE(ls4.equals(ls2));
+ EXPECT_FALSE(ls4.equals(ls3));
+ EXPECT_TRUE(ls4.equals(ls4));
+ EXPECT_FALSE(ls4.equals(ls5));
+ EXPECT_FALSE(ls4.equals(ls6));
+ EXPECT_FALSE(ls4.equals(ls7));
+
+ EXPECT_TRUE(ls5.equals(ls1));
+ EXPECT_FALSE(ls5.equals(ls2));
+ EXPECT_TRUE(ls5.equals(ls3));
+ EXPECT_FALSE(ls5.equals(ls4));
+ EXPECT_TRUE(ls5.equals(ls5));
+ EXPECT_TRUE(ls5.equals(ls6));
+ EXPECT_FALSE(ls5.equals(ls7));
}
void
@@ -124,6 +133,9 @@ getDataCheck(const char* expected_data, size_t expected_len,
const char* data = ls.getData(&len);
ASSERT_EQ(expected_len, len) << "Expected data: " << expected_data <<
" name: " << ls.getName().toText();
+ EXPECT_EQ(expected_len, ls.getDataLength()) <<
+ "Expected data: " << expected_data <<
+ " name: " << ls.getName().toText();
for (size_t i = 0; i < len; ++i) {
EXPECT_EQ(expected_data[i], data[i]) << "Difference at pos " << i <<
": Expected data: " <<
@@ -251,3 +263,86 @@ TEST_F(LabelSequenceTest, isAbsolute) {
ls3.stripLeft(2);
ASSERT_TRUE(ls3.isAbsolute());
}
+
+// The following are test data used in the getHash test below. Normally
+// we use example/documentation domain names for testing, but in this case
+// we'd specifically like to use more realistic data, and are intentionally
+// using real-world samples: They are the NS names of root and some top level
+// domains as of this test.
+const char* const root_servers[] = {
+ "a.root-servers.net", "b.root-servers.net", "c.root-servers.net",
+ "d.root-servers.net", "e.root-servers.net", "f.root-servers.net",
+ "g.root-servers.net", "h.root-servers.net", "i.root-servers.net",
+ "j.root-servers.net", "k.root-servers.net", "l.root-servers.net",
+ "m.root-servers.net", NULL
+};
+const char* const gtld_servers[] = {
+ "a.gtld-servers.net", "b.gtld-servers.net", "c.gtld-servers.net",
+ "d.gtld-servers.net", "e.gtld-servers.net", "f.gtld-servers.net",
+ "g.gtld-servers.net", "h.gtld-servers.net", "i.gtld-servers.net",
+ "j.gtld-servers.net", "k.gtld-servers.net", "l.gtld-servers.net",
+ "m.gtld-servers.net", NULL
+};
+const char* const jp_servers[] = {
+ "a.dns.jp", "b.dns.jp", "c.dns.jp", "d.dns.jp", "e.dns.jp",
+ "f.dns.jp", "g.dns.jp", NULL
+};
+const char* const cn_servers[] = {
+ "a.dns.cn", "b.dns.cn", "c.dns.cn", "d.dns.cn", "e.dns.cn",
+ "ns.cernet.net", NULL
+};
+const char* const ca_servers[] = {
+ "k.ca-servers.ca", "e.ca-servers.ca", "a.ca-servers.ca", "z.ca-servers.ca",
+ "tld.isc-sns.net", "c.ca-servers.ca", "j.ca-servers.ca", "l.ca-servers.ca",
+ "sns-pb.isc.org", "f.ca-servers.ca", NULL
+};
+
+// A helper function used in the getHash test below.
+void
+hashDistributionCheck(const char* const* servers) {
+ const size_t BUCKETS = 64; // constant used in the MessageRenderer
+ set<Name> names;
+ vector<size_t> hash_counts(BUCKETS);
+
+ // Store all test names and their super domain names (excluding the
+ // "root" label) in the set, calculates their hash values, and increments
+ // the counter for the corresponding hash "bucket".
+ for (size_t i = 0; servers[i] != NULL; ++i) {
+ const Name name(servers[i]);
+ for (size_t l = 0; l < name.getLabelCount() - 1; ++l) {
+ pair<set<Name>::const_iterator, bool> ret =
+ names.insert(name.split(l));
+ if (ret.second) {
+ hash_counts[LabelSequence((*ret.first)).getHash(false) %
+ BUCKETS]++;
+ }
+ }
+ }
+
+ // See how many conflicts we have in the buckets. For the testing purpose
+ // we expect there's at most 2 conflicts in each set, which is an
+ // arbitrary choice (it should happen to succeed with the hash function
+ // and data we are using; if it's not the case, maybe with an update to
+ // the hash implementation, we should revise the test).
+ for (size_t i = 0; i < BUCKETS; ++i) {
+ EXPECT_GE(3, hash_counts[i]);
+ }
+}
+
+TEST_F(LabelSequenceTest, getHash) {
+ // Trivial case. The same sequence should have the same hash.
+ EXPECT_EQ(ls1.getHash(true), ls1.getHash(true));
+
+ // Check the case-insensitive mode behavior.
+ EXPECT_EQ(ls1.getHash(false), ls5.getHash(false));
+
+ // Check that the distribution of hash values is "not too bad" (such as
+ // everything has the same hash value due to a stupid bug). It's
+ // difficult to check such things reliably. We do some ad hoc tests here.
+ hashDistributionCheck(root_servers);
+ hashDistributionCheck(jp_servers);
+ hashDistributionCheck(cn_servers);
+ hashDistributionCheck(ca_servers);
+}
+
+}
diff --git a/src/lib/dns/tests/messagerenderer_unittest.cc b/src/lib/dns/tests/messagerenderer_unittest.cc
index 2a1869f..bc526af 100644
--- a/src/lib/dns/tests/messagerenderer_unittest.cc
+++ b/src/lib/dns/tests/messagerenderer_unittest.cc
@@ -21,12 +21,16 @@
#include <gtest/gtest.h>
+#include <boost/lexical_cast.hpp>
+
+#include <string>
#include <vector>
using isc::UnitTestUtil;
using isc::dns::Name;
using isc::dns::MessageRenderer;
using isc::util::OutputBuffer;
+using boost::lexical_cast;
namespace {
class MessageRendererTest : public ::testing::Test {
@@ -142,13 +146,15 @@ TEST_F(MessageRendererTest, writeNameMixedCaseCompress) {
renderer.writeName(Name("a.example.com."));
renderer.writeName(Name("b.eXample.com."));
- // Change the compression mode in the middle of rendering. This is an
- // unusual operation and is unlikely to happen in practice, but is still
- // allowed in this API.
- renderer.setCompressMode(MessageRenderer::CASE_INSENSITIVE);
- renderer.writeName(Name("c.b.EXAMPLE.com."));
- EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, renderer.getData(),
- renderer.getLength(), &data[0], data.size());
+ // Change the compression mode in the middle of rendering. This is not
+ // allowed in this implementation.
+ EXPECT_THROW(renderer.setCompressMode(MessageRenderer::CASE_INSENSITIVE),
+ isc::InvalidParameter);
+
+ // Once the renderer is cleared, it's okay again.
+ renderer.clear();
+ EXPECT_NO_THROW(renderer.setCompressMode(
+ MessageRenderer::CASE_INSENSITIVE));
}
TEST_F(MessageRendererTest, writeRootName) {
@@ -211,4 +217,20 @@ TEST_F(MessageRendererTest, setBufferErrors) {
renderer.setBuffer(&new_buffer);
EXPECT_NO_THROW(renderer.setBuffer(NULL));
}
+
+TEST_F(MessageRendererTest, manyRRs) {
+ // Render a large number of names, and the confirm the resulting wire
+ // data store the expected names in the correct order (1000 is an
+ // arbitrary choice).
+ for (size_t i = 0; i < 1000; ++i) {
+ renderer.writeName(Name(lexical_cast<std::string>(i) + ".example"));
+ }
+ isc::util::InputBuffer b(renderer.getData(), renderer.getLength());
+ for (size_t i = 0; i < 1000; ++i) {
+ EXPECT_EQ(Name(lexical_cast<std::string>(i) + ".example"), Name(b));
+ }
+ // This will trigger trimming excessive hash items. It shouldn't cause
+ // any disruption.
+ EXPECT_NO_THROW(renderer.clear());
+}
}
diff --git a/src/lib/dns/tests/rdata_sshfp_unittest.cc b/src/lib/dns/tests/rdata_sshfp_unittest.cc
new file mode 100644
index 0000000..dd133ce
--- /dev/null
+++ b/src/lib/dns/tests/rdata_sshfp_unittest.cc
@@ -0,0 +1,94 @@
+// Copyright (C) 2012 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 <algorithm>
+#include <string>
+
+#include <util/buffer.h>
+#include <dns/messagerenderer.h>
+#include <dns/rdata.h>
+#include <dns/rdataclass.h>
+#include <dns/rrclass.h>
+#include <dns/rrtype.h>
+
+#include <gtest/gtest.h>
+
+#include <dns/tests/unittest_util.h>
+#include <dns/tests/rdata_unittest.h>
+#include <boost/algorithm/string.hpp>
+
+using isc::UnitTestUtil;
+using namespace std;
+using namespace isc::dns;
+using namespace isc::util;
+using namespace isc::dns::rdata;
+
+namespace {
+class Rdata_SSHFP_Test : public RdataTest {
+ // there's nothing to specialize
+};
+
+const string sshfp_txt("2 1 123456789abcdef67890123456789abcdef67890");
+const generic::SSHFP rdata_sshfp(2, 1, "123456789abcdef67890123456789abcdef67890");
+
+TEST_F(Rdata_SSHFP_Test, createFromText) {
+ // Basic test
+ const generic::SSHFP rdata_sshfp2(sshfp_txt);
+ EXPECT_EQ(0, rdata_sshfp2.compare(rdata_sshfp));
+
+ // With different spacing
+ const generic::SSHFP rdata_sshfp3("2 1 123456789abcdef67890123456789abcdef67890");
+ EXPECT_EQ(0, rdata_sshfp3.compare(rdata_sshfp));
+
+ // Combination of lowercase and uppercase
+ const generic::SSHFP rdata_sshfp4("2 1 123456789ABCDEF67890123456789abcdef67890");
+ EXPECT_EQ(0, rdata_sshfp4.compare(rdata_sshfp));
+}
+
+TEST_F(Rdata_SSHFP_Test, badText) {
+ EXPECT_THROW(const generic::SSHFP rdata_sshfp("1"), InvalidRdataText);
+ EXPECT_THROW(const generic::SSHFP rdata_sshfp("1 2"), InvalidRdataText);
+ EXPECT_THROW(const generic::SSHFP rdata_sshfp("BUCKLE MY SHOES"), InvalidRdataText);
+ EXPECT_THROW(const generic::SSHFP rdata_sshfp("1 2 foo bar"), InvalidRdataText);
+}
+
+TEST_F(Rdata_SSHFP_Test, copy) {
+ const generic::SSHFP rdata_sshfp2(rdata_sshfp);
+ EXPECT_EQ(0, rdata_sshfp.compare(rdata_sshfp2));
+}
+
+TEST_F(Rdata_SSHFP_Test, createFromWire) {
+ // Basic test
+ EXPECT_EQ(0, rdata_sshfp.compare(
+ *rdataFactoryFromFile(RRType("SSHFP"), RRClass("IN"),
+ "rdata_sshfp_fromWire")));
+ // Combination of lowercase and uppercase
+ EXPECT_EQ(0, rdata_sshfp.compare(
+ *rdataFactoryFromFile(RRType("SSHFP"), RRClass("IN"),
+ "rdata_sshfp_fromWire2")));
+ // TBD: more tests
+}
+
+TEST_F(Rdata_SSHFP_Test, toText) {
+ EXPECT_TRUE(boost::iequals(sshfp_txt, rdata_sshfp.toText()));
+}
+
+TEST_F(Rdata_SSHFP_Test, getSSHFPAlgorithmNumber) {
+ EXPECT_EQ(2, rdata_sshfp.getSSHFPAlgorithmNumber());
+}
+
+TEST_F(Rdata_SSHFP_Test, getSSHFPFingerprintType) {
+ EXPECT_EQ(1, rdata_sshfp.getSSHFPFingerprintType());
+}
+}
diff --git a/src/lib/dns/tests/rrset_unittest.cc b/src/lib/dns/tests/rrset_unittest.cc
index a6bfe56..a10b515 100644
--- a/src/lib/dns/tests/rrset_unittest.cc
+++ b/src/lib/dns/tests/rrset_unittest.cc
@@ -112,6 +112,20 @@ TEST_F(RRsetTest, setName) {
EXPECT_EQ(test_nsname, rrset_a.getName());
}
+TEST_F(RRsetTest, isSameKind) {
+ RRset rrset_w(test_name, RRClass::IN(), RRType::A(), RRTTL(3600));
+ RRset rrset_x(test_name, RRClass::IN(), RRType::A(), RRTTL(3600));
+ RRset rrset_y(test_name, RRClass::IN(), RRType::NS(), RRTTL(3600));
+ RRset rrset_z(test_name, RRClass::CH(), RRType::A(), RRTTL(3600));
+ RRset rrset_p(test_nsname, RRClass::IN(), RRType::A(), RRTTL(3600));
+
+ EXPECT_TRUE(rrset_w.isSameKind(rrset_w));
+ EXPECT_TRUE(rrset_w.isSameKind(rrset_x));
+ EXPECT_FALSE(rrset_w.isSameKind(rrset_y));
+ EXPECT_FALSE(rrset_w.isSameKind(rrset_z));
+ EXPECT_FALSE(rrset_w.isSameKind(rrset_p));
+}
+
void
addRdataTestCommon(const RRset& rrset) {
EXPECT_EQ(2, rrset.getRdataCount());
diff --git a/src/lib/dns/tests/testdata/.gitignore b/src/lib/dns/tests/testdata/.gitignore
new file mode 100644
index 0000000..e56355b
--- /dev/null
+++ b/src/lib/dns/tests/testdata/.gitignore
@@ -0,0 +1,117 @@
+/edns_toWire1.wire
+/edns_toWire2.wire
+/edns_toWire3.wire
+/edns_toWire4.wire
+/message_fromWire10.wire
+/message_fromWire11.wire
+/message_fromWire12.wire
+/message_fromWire13.wire
+/message_fromWire14.wire
+/message_fromWire15.wire
+/message_fromWire16.wire
+/message_fromWire17.wire
+/message_fromWire18.wire
+/message_fromWire19.wire
+/message_fromWire20.wire
+/message_fromWire21.wire
+/message_fromWire22.wire
+/message_toText1.wire
+/message_toText2.wire
+/message_toText3.wire
+/message_toWire2.wire
+/message_toWire3.wire
+/message_toWire4.wire
+/message_toWire5.wire
+/name_toWire5.wire
+/name_toWire6.wire
+/rdata_afsdb_fromWire1.wire
+/rdata_afsdb_fromWire2.wire
+/rdata_afsdb_fromWire3.wire
+/rdata_afsdb_fromWire4.wire
+/rdata_afsdb_fromWire5.wire
+/rdata_afsdb_toWire1.wire
+/rdata_afsdb_toWire2.wire
+/rdata_minfo_fromWire1.wire
+/rdata_minfo_fromWire2.wire
+/rdata_minfo_fromWire3.wire
+/rdata_minfo_fromWire4.wire
+/rdata_minfo_fromWire5.wire
+/rdata_minfo_fromWire6.wire
+/rdata_minfo_toWire1.wire
+/rdata_minfo_toWire2.wire
+/rdata_minfo_toWireUncompressed1.wire
+/rdata_minfo_toWireUncompressed2.wire
+/rdata_nsec3_fromWire10.wire
+/rdata_nsec3_fromWire11.wire
+/rdata_nsec3_fromWire12.wire
+/rdata_nsec3_fromWire13.wire
+/rdata_nsec3_fromWire14.wire
+/rdata_nsec3_fromWire15.wire
+/rdata_nsec3_fromWire16.wire
+/rdata_nsec3_fromWire17.wire
+/rdata_nsec3_fromWire2.wire
+/rdata_nsec3_fromWire4.wire
+/rdata_nsec3_fromWire5.wire
+/rdata_nsec3_fromWire6.wire
+/rdata_nsec3_fromWire7.wire
+/rdata_nsec3_fromWire8.wire
+/rdata_nsec3_fromWire9.wire
+/rdata_nsec3param_fromWire11.wire
+/rdata_nsec3param_fromWire13.wire
+/rdata_nsec3param_fromWire2.wire
+/rdata_nsec_fromWire10.wire
+/rdata_nsec_fromWire16.wire
+/rdata_nsec_fromWire4.wire
+/rdata_nsec_fromWire5.wire
+/rdata_nsec_fromWire6.wire
+/rdata_nsec_fromWire7.wire
+/rdata_nsec_fromWire8.wire
+/rdata_nsec_fromWire9.wire
+/rdata_rp_fromWire1.wire
+/rdata_rp_fromWire2.wire
+/rdata_rp_fromWire3.wire
+/rdata_rp_fromWire4.wire
+/rdata_rp_fromWire5.wire
+/rdata_rp_fromWire6.wire
+/rdata_rp_toWire1.wire
+/rdata_rp_toWire2.wire
+/rdata_rrsig_fromWire2.wire
+/rdata_soa_toWireUncompressed.wire
+/rdata_sshfp_fromWire1.wire
+/rdata_sshfp_fromWire2.wire
+/rdata_tsig_fromWire1.wire
+/rdata_tsig_fromWire2.wire
+/rdata_tsig_fromWire3.wire
+/rdata_tsig_fromWire4.wire
+/rdata_tsig_fromWire5.wire
+/rdata_tsig_fromWire6.wire
+/rdata_tsig_fromWire7.wire
+/rdata_tsig_fromWire8.wire
+/rdata_tsig_fromWire9.wire
+/rdata_tsig_toWire1.wire
+/rdata_tsig_toWire2.wire
+/rdata_tsig_toWire3.wire
+/rdata_tsig_toWire4.wire
+/rdata_tsig_toWire5.wire
+/rdata_txt_fromWire2.wire
+/rdata_txt_fromWire3.wire
+/rdata_txt_fromWire4.wire
+/rdata_txt_fromWire5.wire
+/rdatafields1.wire
+/rdatafields2.wire
+/rdatafields3.wire
+/rdatafields4.wire
+/rdatafields5.wire
+/rdatafields6.wire
+/tsig_verify1.wire
+/tsig_verify10.wire
+/tsig_verify2.wire
+/tsig_verify3.wire
+/tsig_verify4.wire
+/tsig_verify5.wire
+/tsig_verify6.wire
+/tsig_verify7.wire
+/tsig_verify8.wire
+/tsig_verify9.wire
+/tsigrecord_toWire1.wire
+/tsigrecord_toWire2.wire
diff --git a/src/lib/dns/tests/testdata/Makefile.am b/src/lib/dns/tests/testdata/Makefile.am
index fdf1025..f450a59 100644
--- a/src/lib/dns/tests/testdata/Makefile.am
+++ b/src/lib/dns/tests/testdata/Makefile.am
@@ -44,6 +44,7 @@ BUILT_SOURCES += rdata_rp_fromWire1.wire rdata_rp_fromWire2.wire
BUILT_SOURCES += rdata_rp_fromWire3.wire rdata_rp_fromWire4.wire
BUILT_SOURCES += rdata_rp_fromWire5.wire rdata_rp_fromWire6.wire
BUILT_SOURCES += rdata_rp_toWire1.wire rdata_rp_toWire2.wire
+BUILT_SOURCES += rdata_sshfp_fromWire1.wire rdata_sshfp_fromWire2.wire
BUILT_SOURCES += rdata_afsdb_fromWire1.wire rdata_afsdb_fromWire2.wire
BUILT_SOURCES += rdata_afsdb_fromWire3.wire rdata_afsdb_fromWire4.wire
BUILT_SOURCES += rdata_afsdb_fromWire5.wire
@@ -125,6 +126,8 @@ EXTRA_DIST += rdata_rp_fromWire1.spec rdata_rp_fromWire2.spec
EXTRA_DIST += rdata_rp_fromWire3.spec rdata_rp_fromWire4.spec
EXTRA_DIST += rdata_rp_fromWire5.spec rdata_rp_fromWire6.spec
EXTRA_DIST += rdata_rp_toWire1.spec rdata_rp_toWire2.spec
+EXTRA_DIST += rdata_sshfp_fromWire rdata_sshfp_fromWire2
+EXTRA_DIST += rdata_sshfp_fromWire1.spec rdata_sshfp_fromWire2.spec
EXTRA_DIST += rdata_afsdb_fromWire1.spec rdata_afsdb_fromWire2.spec
EXTRA_DIST += rdata_afsdb_fromWire3.spec rdata_afsdb_fromWire4.spec
EXTRA_DIST += rdata_afsdb_fromWire5.spec
diff --git a/src/lib/dns/tests/testdata/rdata_mx_fromWire b/src/lib/dns/tests/testdata/rdata_mx_fromWire
index f56387d..407350f 100644
--- a/src/lib/dns/tests/testdata/rdata_mx_fromWire
+++ b/src/lib/dns/tests/testdata/rdata_mx_fromWire
@@ -11,5 +11,5 @@
00 0a
# EXCHANGE: non compressed
# 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 (bytes)
-#(4) m x (7) e x a m p l e (3) c o m .
+#(2) m x (7) e x a m p l e (3) c o m .
02 6d 78 07 65 78 61 6d 70 6c 65 03 63 6f 6d 00
diff --git a/src/lib/dns/tests/testdata/rdata_sshfp_fromWire b/src/lib/dns/tests/testdata/rdata_sshfp_fromWire
new file mode 100644
index 0000000..added40
--- /dev/null
+++ b/src/lib/dns/tests/testdata/rdata_sshfp_fromWire
@@ -0,0 +1,4 @@
+# SSHFP RDATA, RDLEN=22
+0016
+# ALGORITHM=2 FINGERPRINT_TYPE=1 FINGERPRINT=123456789abcdef67890123456789abcdef67890
+02 01 123456789abcdef67890123456789abcdef67890
diff --git a/src/lib/dns/tests/testdata/rdata_sshfp_fromWire1.spec b/src/lib/dns/tests/testdata/rdata_sshfp_fromWire1.spec
new file mode 100644
index 0000000..e28a62f
--- /dev/null
+++ b/src/lib/dns/tests/testdata/rdata_sshfp_fromWire1.spec
@@ -0,0 +1,6 @@
+#
+# A simplest form of SSHFP: all default parameters
+#
+[custom]
+sections: sshfp
+[sshfp]
diff --git a/src/lib/dns/tests/testdata/rdata_sshfp_fromWire2 b/src/lib/dns/tests/testdata/rdata_sshfp_fromWire2
new file mode 100644
index 0000000..a695548
--- /dev/null
+++ b/src/lib/dns/tests/testdata/rdata_sshfp_fromWire2
@@ -0,0 +1,4 @@
+# SSHFP RDATA, RDLEN=22
+0016
+# ALGORITHM=2 FINGERPRINT_TYPE=1 FINGERPRINT=123456789ABCDEF67890123456789abcdef67890
+02 01 123456789ABCDEF67890123456789abcdef67890
diff --git a/src/lib/dns/tests/testdata/rdata_sshfp_fromWire2.spec b/src/lib/dns/tests/testdata/rdata_sshfp_fromWire2.spec
new file mode 100644
index 0000000..59a336e
--- /dev/null
+++ b/src/lib/dns/tests/testdata/rdata_sshfp_fromWire2.spec
@@ -0,0 +1,7 @@
+#
+# SSHFP RDATA
+#
+[custom]
+sections: sshfp
+[sshfp]
+fingerprint: 123456789abcdef67890123456789abcdef67890
diff --git a/src/lib/dns/tests/unittest_util.cc b/src/lib/dns/tests/unittest_util.cc
index ca9d347..66d49a8 100644
--- a/src/lib/dns/tests/unittest_util.cc
+++ b/src/lib/dns/tests/unittest_util.cc
@@ -191,3 +191,21 @@ UnitTestUtil::createRequestMessage(Message& message,
message.addQuestion(Question(name, rrclass, rrtype));
}
+void
+UnitTestUtil::createDNSSECRequestMessage(Message& message,
+ const Opcode& opcode,
+ const uint16_t qid,
+ const Name& name,
+ const RRClass& rrclass,
+ const RRType& rrtype)
+{
+ message.clear(Message::RENDER);
+ message.setOpcode(opcode);
+ message.setRcode(Rcode::NOERROR());
+ message.setQid(qid);
+ message.addQuestion(Question(name, rrclass, rrtype));
+ EDNSPtr edns(new EDNS());
+ edns->setUDPSize(4096);
+ edns->setDNSSECAwareness(true);
+ message.setEDNS(edns);
+}
diff --git a/src/lib/dns/tests/unittest_util.h b/src/lib/dns/tests/unittest_util.h
index f85a921..ebb514d 100644
--- a/src/lib/dns/tests/unittest_util.h
+++ b/src/lib/dns/tests/unittest_util.h
@@ -93,6 +93,22 @@ public:
const isc::dns::Name& name,
const isc::dns::RRClass& rrclass,
const isc::dns::RRType& rrtype);
+
+ ///
+ /// Populate a DNSSEC request message
+ ///
+ /// Create a request message in 'request_message' using the
+ /// opcode 'opcode' and the name/class/type query tuple specified in
+ /// 'name', 'rrclass' and 'rrtype.
+ /// EDNS will be added with DO=1 and bufsize 4096
+ static void
+ createDNSSECRequestMessage(isc::dns::Message& request_message,
+ const isc::dns::Opcode& opcode,
+ const uint16_t qid,
+ const isc::dns::Name& name,
+ const isc::dns::RRClass& rrclass,
+ const isc::dns::RRType& rrtype);
+
};
}
#endif // __UNITTEST_UTIL_H
diff --git a/src/lib/exceptions/tests/.gitignore b/src/lib/exceptions/tests/.gitignore
new file mode 100644
index 0000000..d6d1ec8
--- /dev/null
+++ b/src/lib/exceptions/tests/.gitignore
@@ -0,0 +1 @@
+/run_unittests
diff --git a/src/lib/log/compiler/.gitignore b/src/lib/log/compiler/.gitignore
new file mode 100644
index 0000000..b6afc24
--- /dev/null
+++ b/src/lib/log/compiler/.gitignore
@@ -0,0 +1 @@
+/message
diff --git a/src/lib/log/compiler/message.cc b/src/lib/log/compiler/message.cc
index 3c8a3f2..fb0f038 100644
--- a/src/lib/log/compiler/message.cc
+++ b/src/lib/log/compiler/message.cc
@@ -491,22 +491,22 @@ writeProgramFile(const string& file, const vector<string>& ns_components,
}
-/// \brief Warn of Duplicate Entries
+/// \brief Error and exit if there are duplicate entries
///
-/// If the input file contained duplicate message IDs, only the first will be
-/// processed. However, we should warn about it.
+/// If the input file contained duplicate message IDs, we print an
+/// error for each of them, then exit the program with a non-0 value.
///
/// \param reader Message Reader used to read the file
void
-warnDuplicates(MessageReader& reader) {
+errorDuplicates(MessageReader& reader) {
// Get the duplicates (the overflow) and, if present, sort them into some
// order and remove those which occur more than once (which mean that they
// occur more than twice in the input file).
MessageReader::MessageIDCollection duplicates = reader.getNotAdded();
- if (duplicates.size() > 0) {
- cout << "Warning: the following duplicate IDs were found:\n";
+ if (!duplicates.empty()) {
+ cout << "Error: the following duplicate IDs were found:\n";
sort(duplicates.begin(), duplicates.end());
MessageReader::MessageIDCollection::iterator new_end =
@@ -515,6 +515,7 @@ warnDuplicates(MessageReader& reader) {
i != new_end; ++i) {
cout << " " << *i << "\n";
}
+ exit(1);
}
}
@@ -580,6 +581,9 @@ main(int argc, char* argv[]) {
MessageReader reader(&dictionary);
reader.readFile(message_file);
+ // Error (and quit) if there are of any duplicates encountered.
+ errorDuplicates(reader);
+
if (doPython) {
// Warn in case of ignored directives
if (!reader.getNamespace().empty()) {
@@ -604,8 +608,6 @@ main(int argc, char* argv[]) {
output_directory);
}
- // Finally, warn of any duplicates encountered.
- warnDuplicates(reader);
}
catch (const MessageException& e) {
// Create an error message from the ID and the text
diff --git a/src/lib/log/logger.h b/src/lib/log/logger.h
index 96168c0..5715bc4 100644
--- a/src/lib/log/logger.h
+++ b/src/lib/log/logger.h
@@ -15,14 +15,17 @@
#ifndef __LOGGER_H
#define __LOGGER_H
+#include <cassert>
#include <cstdlib>
#include <string>
+#include <cstring>
#include <exceptions/exceptions.h>
#include <log/logger_level.h>
#include <log/message_types.h>
#include <log/log_formatter.h>
+
namespace isc {
namespace log {
@@ -68,9 +71,19 @@ namespace log {
/// is referred to using a symbolic name. The logging code uses this name as
/// a key in a dictionary from which the message text is obtained. Such a
/// system allows for the optional replacement of message text at run time.
-/// More details about the message disction (and the compiler used to create
+/// More details about the message dictionary (and the compiler used to create
/// the symbol definitions) can be found in other modules in the src/lib/log
/// directory.
+///
+/// \section LoggingApiImplementationIssues Implementation Issues
+/// Owing to the way that the logging is implemented, notably that loggers can
+/// be declared as static external objects, there is a restriction on the
+/// length of the name of a logger component (i.e. the length of
+/// the string passed to the Logger constructor) to a maximum of 31 characters.
+/// There is no reason for this particular value other than limiting the amount
+/// of memory used. It is defined by the constant Logger::MAX_LOGGER_NAME_SIZE,
+/// and can be made larger (or smaller) if so desired. Note however, using a
+/// logger name larger than this limit will cause an assertion failure.
class LoggerImpl; // Forward declaration of the implementation class
@@ -99,6 +112,8 @@ public:
class Logger {
public:
+ /// Maximum size of a logger name
+ static const size_t MAX_LOGGER_NAME_SIZE = 31;
/// \brief Constructor
///
@@ -107,8 +122,26 @@ public:
/// \param name Name of the logger. If the name is that of the root name,
/// this creates an instance of the root logger; otherwise it creates a
/// child of the root logger.
- Logger(const std::string& name) : loggerptr_(NULL), name_(name)
- {}
+ ///
+ /// \note The name of the logger may be no longer than MAX_LOGGER_NAME_SIZE
+ /// else the program will halt with an assertion failure. This restriction
+ /// allows loggers to be declared statically: the name is stored in a
+ /// fixed-size array to avoid the need to allocate heap storage during
+ /// program initialization (which causes problems on some operating
+ /// systems).
+ ///
+ /// \note Note also that there is no constructor taking a std::string. This
+ /// minimises the possibility of initializing a static logger with a
+ /// string, so leading to problems mentioned above.
+ Logger(const char* name) : loggerptr_(NULL) {
+ assert(std::strlen(name) < sizeof(name_));
+ // Do the copy. Note that the assertion above has checked that the
+ // contents of "name" and a trailing null will fit within the space
+ // allocated for name_, so we could use strcpy here and be safe.
+ // However, a bit of extra paranoia doesn't hurt.
+ std::strncpy(name_, name, sizeof(name_));
+ assert(name_[sizeof(name_) - 1] == '\0');
+ }
/// \brief Destructor
virtual ~Logger();
@@ -256,8 +289,8 @@ private:
/// \brief Initialize Underlying Implementation and Set loggerptr_
void initLoggerImpl();
- LoggerImpl* loggerptr_; ///< Pointer to the underlying logger
- std::string name_; ///< Copy of the logger name
+ LoggerImpl* loggerptr_; ///< Pointer to underlying logger
+ char name_[MAX_LOGGER_NAME_SIZE + 1]; ///< Copy of the logger name
};
} // namespace log
diff --git a/src/lib/log/logger_manager.cc b/src/lib/log/logger_manager.cc
index a04fffb..8a8a36b 100644
--- a/src/lib/log/logger_manager.cc
+++ b/src/lib/log/logger_manager.cc
@@ -95,6 +95,10 @@ void
LoggerManager::init(const std::string& root, isc::log::Severity severity,
int dbglevel, const char* file)
{
+ // Load in the messages declared in the program and registered by
+ // statically-declared MessageInitializer objects.
+ MessageInitializer::loadDictionary();
+
// Save name, severity and debug level for later. No need to save the
// file name as once the local message file is read the messages will
// not be lost.
diff --git a/src/lib/log/message_initializer.cc b/src/lib/log/message_initializer.cc
index 0113497..3dd5da7 100644
--- a/src/lib/log/message_initializer.cc
+++ b/src/lib/log/message_initializer.cc
@@ -12,25 +12,80 @@
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
+#include <cassert>
+#include <cstdlib>
#include <log/message_dictionary.h>
#include <log/message_initializer.h>
+
+// As explained in the header file, initialization of the message dictionary
+// is a two-stage process:
+// 1) In the MessageInitializer constructor, a pointer to the array of
+// messages is stored in a pre-defined array. Since the MessageInitializers
+// are declared statically outside a program unit, this takes place before
+// main() is called. As no heap storage is allocated in this process, we
+// should avoid the static initialization fiasco in cases where
+// initialization of system libraries is also carried out at the same time.
+// 2) After main() starts executing, loadDictionary() is called.
+//
+//
+
+namespace {
+
+// Declare the array of pointers to value arrays.
+const char** logger_values[isc::log::MessageInitializer::MAX_MESSAGE_ARRAYS];
+
+// Declare the index used to access the array. As this needs to be initialized
+// at first used, it is accessed it via a function.
+size_t& getIndex() {
+ static size_t index = 0;
+ return (index);
+}
+
+}
+
+
namespace isc {
namespace log {
-// Constructor. Just retrieve the global dictionary and load the IDs and
-// associated text into it.
+// Constructor. Add the pointer to the message array to the global array.
+// This method will trigger an assertion failure if the array overflows.
MessageInitializer::MessageInitializer(const char* values[]) {
+ assert(getIndex() < MAX_MESSAGE_ARRAYS);
+ logger_values[getIndex()++] = values;
+}
+
+// Return the number of arrays registered but not yet loaded.
+
+size_t
+MessageInitializer::getPendingCount() {
+ return (getIndex());
+}
+
+// Load the messages in the arrays registered in the logger_values array
+// into the global dictionary.
+
+void
+MessageInitializer::loadDictionary() {
MessageDictionary& global = MessageDictionary::globalDictionary();
- std::vector<std::string> repeats = global.load(values);
- // Append the IDs in the list just loaded (the "repeats") to the global list
- // of duplicate IDs.
- if (!repeats.empty()) {
- std::vector<std::string>& duplicates = getDuplicates();
- duplicates.insert(duplicates.end(), repeats.begin(), repeats.end());
+ for (size_t i = 0; i < getIndex(); ++i) {
+ std::vector<std::string> repeats = global.load(logger_values[i]);
+
+ // Append the IDs in the list just loaded (the "repeats") to the
+ // global list of duplicate IDs.
+ if (!repeats.empty()) {
+ std::vector<std::string>& duplicates = getDuplicates();
+ duplicates.insert(duplicates.end(), repeats.begin(),
+ repeats.end());
+ }
}
+
+ // ... and mark that the messages have been loaded. (This avoids a lot
+ // of "duplicate message ID" messages in some of the unit tests where the
+ // logging initialization code may be called multiple times.)
+ getIndex() = 0;
}
// Return reference to duplicate array
diff --git a/src/lib/log/message_initializer.h b/src/lib/log/message_initializer.h
index 7516823..28b0e61 100644
--- a/src/lib/log/message_initializer.h
+++ b/src/lib/log/message_initializer.h
@@ -15,6 +15,7 @@
#ifndef __MESSAGEINITIALIZER_H
#define __MESSAGEINITIALIZER_H
+#include <cstdlib>
#include <string>
#include <vector>
#include <log/message_dictionary.h>
@@ -37,28 +38,63 @@ namespace log {
/// :
/// NULL
/// };
-/// MessageDictionaryHelper xyz(values);
+/// MessageInitializer xyz(values);
///
-/// This will automatically add the message ID/text pairs to the global
-/// dictionary during initialization - all that is required is that the module
-/// containing the definition is included into the final executable.
+/// All that needed is for the module containing the definitions to be
+/// included in the execution unit.
///
-/// Messages are added via the MessageDictionary::add() method, so any
-/// duplicates are stored in the the global dictionary's overflow vector whence
-/// they can be retrieved at run-time.
+/// To avoid static initialization fiasco problems, the initialization is
+/// carried out in two stages:
+/// - The constructor adds a pointer to the values array to a pre-defined array
+/// of pointers.
+/// - During the run-time initialization of the logging system, the static
+/// method loadDictionary() is called to load the message dictionary.
+/// This way, no heap storage is allocated during the static initialization,
+/// something that may give problems on some operating systems.
+///
+/// \note The maximum number of message arrays that can be added to the
+/// dictionary in this way is defined by the constant
+/// MessageInitializer::MAX_MESSAGE_ARRAYS. This is set to 256 as a compromise
+/// between wasted space and allowing for future expansion, but can be
+/// changed (by editing the source file) to any value desired.
+///
+/// When messages are added to the dictionary, the are added via the
+/// MessageDictionary::add() method, so any duplicates are stored in the
+/// global dictionary's overflow vector whence they can be retrieved at
+/// run-time.
class MessageInitializer {
public:
+ /// Maximum number of message arrays that can be initialized in this way
+ static const size_t MAX_MESSAGE_ARRAYS = 256;
/// \brief Constructor
///
- /// Adds the array of values to the global dictionary, and notes any
- /// duplicates.
+ /// Adds a pointer to the array of messages to the global array of
+ /// pointers to message arrays.
///
/// \param values NULL-terminated array of alternating identifier strings
- /// and associated message text.
+ /// and associated message text. N.B. This object stores a pointer to the
+ /// passed array; the array MUST remain valid at least until
+ /// loadDictionary() has been called.
MessageInitializer(const char* values[]);
+ /// \brief Obtain pending load count
+ ///
+ /// Returns the number of message arrays that will be loaded by the next
+ /// call to loadDictionary().
+ ///
+ /// \return Number of registered message arrays. This is reset to zero
+ /// when loadDictionary() is called.
+ static size_t getPendingCount();
+
+ /// \brief Run-Time Initialization
+ ///
+ /// Loops through the internal array of pointers to message arrays
+ /// and adds the messages to the internal dictionary. This is called
+ /// during run-time initialization.
+ static void loadDictionary();
+
/// \brief Return Duplicates
///
/// When messages are added to the global dictionary, any duplicates are
diff --git a/src/lib/log/tests/.gitignore b/src/lib/log/tests/.gitignore
new file mode 100644
index 0000000..41b863b
--- /dev/null
+++ b/src/lib/log/tests/.gitignore
@@ -0,0 +1,11 @@
+/console_test.sh
+/destination_test.sh
+/init_logger_test
+/init_logger_test.sh
+/initializer_unittests_1
+/initializer_unittests_2
+/local_file_test.sh
+/logger_example
+/run_unittests
+/severity_test.sh
+/tempdir.h
diff --git a/src/lib/log/tests/Makefile.am b/src/lib/log/tests/Makefile.am
index 53e97a1..c07eb4a 100644
--- a/src/lib/log/tests/Makefile.am
+++ b/src/lib/log/tests/Makefile.am
@@ -3,15 +3,52 @@ SUBDIRS = .
AM_CPPFLAGS = -I$(top_builddir)/src/lib -I$(top_srcdir)/src/lib
AM_CPPFLAGS += $(BOOST_INCLUDES)
AM_CXXFLAGS = $(B10_CXXFLAGS)
+AM_LDADD =
+AM_LDFLAGS =
if USE_STATIC_LINK
-AM_LDFLAGS = -static
+AM_LDFLAGS += -static
endif
CLEANFILES = *.gcno *.gcda
-TESTS =
+noinst_PROGRAMS = logger_example
+logger_example_SOURCES = logger_example.cc
+logger_example_CPPFLAGS = $(AM_CPPFLAGS)
+logger_example_LDFLAGS = $(AM_LDFLAGS)
+logger_example_LDADD = $(AM_LDADD) $(LOG4CPLUS_LIBS)
+logger_example_LDADD += $(top_builddir)/src/lib/log/liblog.la
+logger_example_LDADD += $(top_builddir)/src/lib/util/libutil.la
+logger_example_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
+
+noinst_PROGRAMS += init_logger_test
+init_logger_test_SOURCES = init_logger_test.cc
+init_logger_test_CPPFLAGS = $(AM_CPPFLAGS)
+init_logger_test_LDFLAGS = $(AM_LDFLAGS)
+init_logger_test_LDADD = $(AM_LDADD) $(LOG4CPLUS_LIBS)
+init_logger_test_LDADD += $(top_builddir)/src/lib/log/liblog.la
+init_logger_test_LDADD += $(top_builddir)/src/lib/util/libutil.la
+init_logger_test_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
+
if HAVE_GTEST
+TESTS =
+
+# Define the flags used in each set of tests
+if USE_CLANGPP
+# Workaround unused variables tcout and tcerr in log4cplus's streams.h.
+AM_CXXFLAGS += -Wno-unused-variable
+endif
+AM_CPPFLAGS += $(GTEST_INCLUDES) $(LOG4CPLUS_INCLUDES)
+AM_LDFLAGS += $(GTEST_LDFLAGS)
+
+AM_LDADD += $(GTEST_LDADD)
+AM_LDADD += $(top_builddir)/src/lib/log/liblog.la
+AM_LDADD += $(top_builddir)/src/lib/util/unittests/libutil_unittests.la
+AM_LDADD += $(top_builddir)/src/lib/util/libutil.la
+AM_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
+AM_LDADD += $(top_builddir)/src/lib/util/unittests/libutil_unittests.la
+
+# Set of unit tests for the general logging classes
TESTS += run_unittests
run_unittests_SOURCES = run_unittests.cc
run_unittests_SOURCES += log_formatter_unittest.cc
@@ -23,47 +60,39 @@ run_unittests_SOURCES += logger_support_unittest.cc
run_unittests_SOURCES += logger_unittest.cc
run_unittests_SOURCES += logger_specification_unittest.cc
run_unittests_SOURCES += message_dictionary_unittest.cc
-run_unittests_SOURCES += message_initializer_unittest_2.cc
-run_unittests_SOURCES += message_initializer_unittest.cc
run_unittests_SOURCES += message_reader_unittest.cc
run_unittests_SOURCES += output_option_unittest.cc
-run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES) $(LOG4CPLUS_INCLUDES)
-run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
-
+run_unittests_CPPFLAGS = $(AM_CPPFLAGS)
run_unittests_CXXFLAGS = $(AM_CXXFLAGS)
-if USE_CLANGPP
-# This is to workaround unused variables tcout and tcerr in
-# log4cplus's streams.h.
-run_unittests_CXXFLAGS += -Wno-unused-variable
-endif
-run_unittests_LDADD = $(GTEST_LDADD)
-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/util/libutil.la
-run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
-run_unittests_LDADD += $(top_builddir)/src/lib/util/unittests/libutil_unittests.la
-endif
+run_unittests_LDADD = $(AM_LDADD)
+run_unittests_LDFLAGS = $(AM_LDFLAGS)
-noinst_PROGRAMS = logger_example
-logger_example_SOURCES = logger_example.cc
-logger_example_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
-logger_example_LDFLAGS = $(AM_LDFLAGS)
-logger_example_LDADD = $(LOG4CPLUS_LIBS)
-logger_example_LDADD += $(top_builddir)/src/lib/log/liblog.la
-logger_example_LDADD += $(top_builddir)/src/lib/util/libutil.la
-logger_example_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
+# logging initialization tests. These are put in separate programs to
+# ensure that the initialization status at the start of each test is known,
+# and to prevent circumstances where the execution of one test affects the
+# starting conditions of the next.
+TESTS += initializer_unittests_1
+initializer_unittests_1_SOURCES = run_initializer_unittests.cc
+initializer_unittests_1_SOURCES += message_initializer_1_unittest.cc
+initializer_unittests_1_SOURCES += message_initializer_1a_unittest.cc
-noinst_PROGRAMS += init_logger_test
-init_logger_test_SOURCES = init_logger_test.cc
-init_logger_test_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
-init_logger_test_LDFLAGS = $(AM_LDFLAGS)
-init_logger_test_LDADD = $(LOG4CPLUS_LIBS)
-init_logger_test_LDADD += $(top_builddir)/src/lib/log/liblog.la
-init_logger_test_LDADD += $(top_builddir)/src/lib/util/libutil.la
-init_logger_test_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
+initializer_unittests_1_CPPFLAGS = $(AM_CPPFLAGS)
+initializer_unittests_1_CXXFLAGS = $(AM_CXXFLAGS)
+initializer_unittests_1_LDADD = $(AM_LDADD)
+initializer_unittests_1_LDFLAGS = $(AM_LDFLAGS)
+
+TESTS += initializer_unittests_2
+initializer_unittests_2_SOURCES = run_initializer_unittests.cc
+initializer_unittests_2_SOURCES += message_initializer_2_unittest.cc
+
+initializer_unittests_2_CPPFLAGS = $(AM_CPPFLAGS)
+initializer_unittests_2_CXXFLAGS = $(AM_CXXFLAGS)
+initializer_unittests_2_LDADD = $(AM_LDADD)
+initializer_unittests_2_LDFLAGS = $(AM_LDFLAGS)
noinst_PROGRAMS += $(TESTS)
+endif
# Additional test using the shell. These are principally tests
# where the global logging environment is affected, and where the
diff --git a/src/lib/log/tests/logger_example.cc b/src/lib/log/tests/logger_example.cc
index 2170066..d3f08f3 100644
--- a/src/lib/log/tests/logger_example.cc
+++ b/src/lib/log/tests/logger_example.cc
@@ -105,7 +105,7 @@ void usage() {
// messages. Looking at the output determines whether the program worked.
int main(int argc, char** argv) {
- const string ROOT_NAME = "example";
+ const char* ROOT_NAME = "example";
bool sw_found = false; // Set true if switch found
bool c_found = false; // Set true if "-c" found
diff --git a/src/lib/log/tests/logger_manager_unittest.cc b/src/lib/log/tests/logger_manager_unittest.cc
index 9eb85ad..f2713b0 100644
--- a/src/lib/log/tests/logger_manager_unittest.cc
+++ b/src/lib/log/tests/logger_manager_unittest.cc
@@ -201,7 +201,7 @@ TEST_F(LoggerManagerTest, FileLogger) {
// Scope-limit the logger to ensure it is destroyed after the brief
// check. This adds weight to the idea that the logger will not
// keep the file open.
- Logger logger(file_spec.getLoggerName());
+ Logger logger(file_spec.getLoggerName().c_str());
LOG_FATAL(logger, LOG_DUPLICATE_MESSAGE_ID).arg("test");
ids.push_back(LOG_DUPLICATE_MESSAGE_ID);
@@ -223,7 +223,7 @@ TEST_F(LoggerManagerTest, FileLogger) {
manager.process(spec.begin(), spec.end());
// Create a new instance of the logger and log three more messages.
- Logger logger(file_spec.getLoggerName());
+ Logger logger(file_spec.getLoggerName().c_str());
LOG_FATAL(logger, LOG_NO_SUCH_MESSAGE).arg("test");
ids.push_back(LOG_NO_SUCH_MESSAGE);
@@ -275,7 +275,7 @@ TEST_F(LoggerManagerTest, FileSizeRollover) {
// three files as for the log4cplus implementation, the files appear to
// be rolled after the message is logged.
{
- Logger logger(file_spec.getLoggerName());
+ Logger logger(file_spec.getLoggerName().c_str());
LOG_FATAL(logger, LOG_NO_SUCH_MESSAGE).arg(big_arg);
LOG_FATAL(logger, LOG_DUPLICATE_NAMESPACE).arg(big_arg);
}
@@ -295,7 +295,7 @@ TEST_F(LoggerManagerTest, FileSizeRollover) {
// a .3 version does not exist.
manager.process(spec);
{
- Logger logger(file_spec.getLoggerName());
+ Logger logger(file_spec.getLoggerName().c_str());
LOG_FATAL(logger, LOG_NO_MESSAGE_TEXT).arg(big_arg);
}
diff --git a/src/lib/log/tests/logger_unittest.cc b/src/lib/log/tests/logger_unittest.cc
index edca9ce..d60bcb0 100644
--- a/src/lib/log/tests/logger_unittest.cc
+++ b/src/lib/log/tests/logger_unittest.cc
@@ -58,8 +58,8 @@ TEST_F(LoggerTest, Name) {
TEST_F(LoggerTest, GetLogger) {
- const string name1 = "alpha";
- const string name2 = "beta";
+ const char* name1 = "alpha";
+ const char* name2 = "beta";
// Instantiate two loggers that should be the same
Logger logger1(name1);
@@ -348,3 +348,30 @@ TEST_F(LoggerTest, IsDebugEnabledLevel) {
EXPECT_TRUE(logger.isDebugEnabled(MID_LEVEL));
EXPECT_TRUE(logger.isDebugEnabled(MAX_DEBUG_LEVEL));
}
+
+// Check that if a logger name is too long, it triggers the appropriate
+// assertion.
+
+TEST_F(LoggerTest, LoggerNameLength) {
+ // The following statements should just declare a logger and nothing
+ // should happen.
+ string ok1(Logger::MAX_LOGGER_NAME_SIZE - 1, 'x');
+ Logger l1(ok1.c_str());
+ EXPECT_EQ(getRootLoggerName() + "." + ok1, l1.getName());
+
+ string ok2(Logger::MAX_LOGGER_NAME_SIZE, 'x');
+ Logger l2(ok2.c_str());
+ EXPECT_EQ(getRootLoggerName() + "." + ok2, l2.getName());
+
+ // Note: Not all systems have EXPECT_DEATH. As it is a macro we can just
+ // test for its presence and bypass the test if not available.
+#ifdef EXPECT_DEATH
+ // Too long a logger name should trigger an assertion failure.
+ // Note that we just check that it dies - we don't check what message is
+ // output.
+ EXPECT_DEATH({
+ string ok3(Logger::MAX_LOGGER_NAME_SIZE + 1, 'x');
+ Logger l3(ok3.c_str());
+ }, ".*");
+#endif
+}
diff --git a/src/lib/log/tests/message_initializer_1_unittest.cc b/src/lib/log/tests/message_initializer_1_unittest.cc
new file mode 100644
index 0000000..994174c
--- /dev/null
+++ b/src/lib/log/tests/message_initializer_1_unittest.cc
@@ -0,0 +1,79 @@
+// Copyright (C) 2012 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 <log/message_dictionary.h>
+#include <log/message_initializer.h>
+#include <boost/lexical_cast.hpp>
+#include <gtest/gtest.h>
+#include <string>
+
+using namespace isc;
+using namespace isc::log;
+using namespace std;
+
+// Declare a set of messages to go into the global dictionary.
+
+namespace {
+const char* values1[] = {
+ "GLOBAL1", "global message one",
+ "GLOBAL2", "global message two",
+ NULL
+};
+
+const char* values2[] = {
+ "GLOBAL3", "global message three",
+ "GLOBAL4", "global message four",
+ NULL
+};
+
+}
+
+// Statically initialize the global dictionary with those messages. Three sets
+// are used to check that the declaration of separate initializer objects
+// really does combine the messages. (The third set - declaring message IDs
+// GLOBAL5 and GLOBAL6) is declared in the separately-compiled file,
+// message_identifier_initializer_1a_unittest.cc.)
+
+const MessageInitializer init_message_initializer_unittest_1(values1);
+const MessageInitializer init_message_initializer_unittest_2(values2);
+
+// Check that the global dictionary is initialized with the specified
+// messages.
+
+TEST(MessageInitializerTest1, MessageTest) {
+ MessageDictionary& global = MessageDictionary::globalDictionary();
+
+ // Pointers to the message arrays should have been stored, but none of the
+ // messages should yet be in the dictionary.
+ for (int i = 1; i <= 6; ++i) {
+ string symbol = string("GLOBAL") + boost::lexical_cast<std::string>(i);
+ EXPECT_EQ(string(""), global.getText(symbol));
+ }
+
+ // Load the dictionary - this should clear the message array pending count.
+ // (N.B. We do not check for a known value before the call, only that the
+ // value is not zero. This is because libraries against which the test
+ // is linked may have registered their own message arrays.)
+ EXPECT_NE(0, MessageInitializer::getPendingCount());
+ MessageInitializer::loadDictionary();
+ EXPECT_EQ(0, MessageInitializer::getPendingCount());
+
+ // ... and check the messages loaded.
+ EXPECT_EQ(string("global message one"), global.getText("GLOBAL1"));
+ EXPECT_EQ(string("global message two"), global.getText("GLOBAL2"));
+ EXPECT_EQ(string("global message three"), global.getText("GLOBAL3"));
+ EXPECT_EQ(string("global message four"), global.getText("GLOBAL4"));
+ EXPECT_EQ(string("global message five"), global.getText("GLOBAL5"));
+ EXPECT_EQ(string("global message six"), global.getText("GLOBAL6"));
+}
diff --git a/src/lib/log/tests/message_initializer_1a_unittest.cc b/src/lib/log/tests/message_initializer_1a_unittest.cc
new file mode 100644
index 0000000..3360167
--- /dev/null
+++ b/src/lib/log/tests/message_initializer_1a_unittest.cc
@@ -0,0 +1,37 @@
+// Copyright (C) 2012 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.
+
+// The sole purpose of this file is to provide a set of message definitions
+// in a separate compilation unit from the one in which their presence is
+// checked. This tests that merely declaring the MessageInitializer object
+// is enough to include the definitions in the global dictionary.
+
+#include <log/message_initializer.h>
+
+using namespace isc::log;
+
+// Declare a set of messages to go into the global dictionary.
+
+namespace {
+
+const char* values3[] = {
+ "GLOBAL5", "global message five",
+ "GLOBAL6", "global message six",
+ NULL
+};
+
+}
+
+// Register the messages for loading into the global dictionary
+const MessageInitializer init_message_initializer_unittest_3(values3);
diff --git a/src/lib/log/tests/message_initializer_2_unittest.cc b/src/lib/log/tests/message_initializer_2_unittest.cc
new file mode 100644
index 0000000..ca34b36
--- /dev/null
+++ b/src/lib/log/tests/message_initializer_2_unittest.cc
@@ -0,0 +1,48 @@
+// Copyright (C) 2012 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 <log/message_initializer.h>
+#include <gtest/gtest.h>
+
+using namespace isc::log;
+
+// Declare a set of messages to go into the global dictionary.
+
+namespace {
+const char* values[] = {
+ "GLOBAL1", "global message one",
+ "GLOBAL2", "global message two",
+ NULL
+};
+}
+
+TEST(MessageInitializerTest2, MessageLoadTest) {
+ // Load the maximum number of message arrays allowed. Some arrays may
+ // already have been loaded because of static initialization from modules
+ // in libraries linked against the test program, hence the reason for the
+ // loop starting from the value returned by getPendingCount() instead of 0.
+ for (size_t i = MessageInitializer::getPendingCount();
+ i < MessageInitializer::MAX_MESSAGE_ARRAYS; ++i) {
+ MessageInitializer initializer1(values);
+ }
+
+ // Note: Not all systems have EXPECT_DEATH. As it is a macro we can just
+ // test for its presence and bypass the test if not available.
+#ifdef EXPECT_DEATH
+ // Adding one more should take us over the limit.
+ EXPECT_DEATH({
+ MessageInitializer initializer2(values);
+ }, ".*");
+#endif
+}
diff --git a/src/lib/log/tests/message_initializer_unittest.cc b/src/lib/log/tests/message_initializer_unittest.cc
deleted file mode 100644
index 0cd1879..0000000
--- a/src/lib/log/tests/message_initializer_unittest.cc
+++ /dev/null
@@ -1,70 +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 <cstddef>
-#include <string>
-#include <gtest/gtest.h>
-#include <log/message_dictionary.h>
-#include <log/message_initializer.h>
-
-using namespace isc;
-using namespace isc::log;
-using namespace std;
-
-// Declare a set of messages to go into the global dictionary.
-
-namespace {
-const char* values1[] = {
- "GLOBAL1", "global message one",
- "GLOBAL2", "global message two",
- NULL
-};
-
-const char* values2[] = {
- "GLOBAL3", "global message three",
- "GLOBAL4", "global message four",
- NULL
-};
-
-}
-
-// Statically initialize the global dictionary with those messages. Three sets
-// are used to check that the declaration of separate initializer objects really// does combine the messages. (The third set is declared in the separately-
-// compiled file message_identifier_initializer_unittest_2.cc.)
-
-MessageInitializer init_message_initializer_unittest_1(values1);
-MessageInitializer init_message_initializer_unittest_2(values2);
-
-
-class MessageInitializerTest : public ::testing::Test {
-protected:
- MessageInitializerTest()
- {
- }
-};
-
-
-// Check that the global dictionary is initialized with the specified
-// messages.
-
-TEST_F(MessageInitializerTest, MessageTest) {
- MessageDictionary& global = MessageDictionary::globalDictionary();
-
- EXPECT_EQ(string("global message one"), global.getText("GLOBAL1"));
- EXPECT_EQ(string("global message two"), global.getText("GLOBAL2"));
- EXPECT_EQ(string("global message three"), global.getText("GLOBAL3"));
- EXPECT_EQ(string("global message four"), global.getText("GLOBAL4"));
- EXPECT_EQ(string("global message five"), global.getText("GLOBAL5"));
- EXPECT_EQ(string("global message six"), global.getText("GLOBAL6"));
-}
diff --git a/src/lib/log/tests/message_initializer_unittest_2.cc b/src/lib/log/tests/message_initializer_unittest_2.cc
deleted file mode 100644
index 94abb08..0000000
--- a/src/lib/log/tests/message_initializer_unittest_2.cc
+++ /dev/null
@@ -1,39 +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.
-
-// The sole purpose of this file is to provide a set of message definitions
-// in a separate compilation unit from the one in which their presence is
-// checked. This tests that merely declaring the MessageInitializer object
-// is enough to include the definitions in the global dictionary.
-
-#include <log/message_initializer.h>
-
-using namespace isc::log;
-
-// Declare a set of messages to go into the global dictionary.
-
-namespace {
-
-const char* values3[] = {
- "GLOBAL5", "global message five",
- "GLOBAL6", "global message six",
- NULL
-};
-
-}
-
-// Statically initialize the global dictionary with those messages.
-// Three sets are used to check that the declaration of separate
-// initializer objects really does combine the messages.
-MessageInitializer init_message_initializer_unittest_3(values3);
diff --git a/src/lib/log/tests/run_initializer_unittests.cc b/src/lib/log/tests/run_initializer_unittests.cc
new file mode 100644
index 0000000..54ee120
--- /dev/null
+++ b/src/lib/log/tests/run_initializer_unittests.cc
@@ -0,0 +1,24 @@
+// 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>
+#include <util/unittests/run_all.h>
+
+#include <log/logger_support.h>
+
+int
+main(int argc, char* argv[]) {
+ ::testing::InitGoogleTest(&argc, argv);
+ return (isc::util::unittests::run_all());
+}
diff --git a/src/lib/nsas/.gitignore b/src/lib/nsas/.gitignore
new file mode 100644
index 0000000..109ef04
--- /dev/null
+++ b/src/lib/nsas/.gitignore
@@ -0,0 +1,2 @@
+/nsas_messages.cc
+/nsas_messages.h
diff --git a/src/lib/nsas/tests/.gitignore b/src/lib/nsas/tests/.gitignore
new file mode 100644
index 0000000..d6d1ec8
--- /dev/null
+++ b/src/lib/nsas/tests/.gitignore
@@ -0,0 +1 @@
+/run_unittests
diff --git a/src/lib/nsas/tests/Makefile.am b/src/lib/nsas/tests/Makefile.am
index 420e897..afd91f6 100644
--- a/src/lib/nsas/tests/Makefile.am
+++ b/src/lib/nsas/tests/Makefile.am
@@ -46,11 +46,6 @@ run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
run_unittests_LDADD = $(GTEST_LDADD)
-# NOTE: we may have to clean up this hack later (see the note in configure.ac)
-if NEED_LIBBOOST_THREAD
-run_unittests_LDADD += -lboost_thread
-endif
-
run_unittests_LDADD += $(top_builddir)/src/lib/nsas/libnsas.la
run_unittests_LDADD += $(top_builddir)/src/lib/util/libutil.la
run_unittests_LDADD += $(top_builddir)/src/lib/log/liblog.la
diff --git a/src/lib/python/.gitignore b/src/lib/python/.gitignore
new file mode 100644
index 0000000..9252d05
--- /dev/null
+++ b/src/lib/python/.gitignore
@@ -0,0 +1 @@
+/bind10_config.py
diff --git a/src/lib/python/isc/bind10/component.py b/src/lib/python/isc/bind10/component.py
index 091bfee..da2730c 100644
--- a/src/lib/python/isc/bind10/component.py
+++ b/src/lib/python/isc/bind10/component.py
@@ -30,6 +30,8 @@ configuration). This is yet to be designed.
import isc.log
from isc.log_messages.bind10_messages import *
import time
+import os
+import signal
logger = isc.log.Logger("boss")
DBG_TRACE_DATA = 20
@@ -45,6 +47,14 @@ STATE_DEAD = 'dead'
STATE_STOPPED = 'stopped'
STATE_RUNNING = 'running'
+def get_signame(signal_number):
+ """Return the symbolic name for a signal."""
+ for sig in dir(signal):
+ if sig.startswith("SIG") and sig[3].isalnum():
+ if getattr(signal, sig) == signal_number:
+ return sig
+ return "unknown signal"
+
class BaseComponent:
"""
This represents a single component. This one is an abstract base class.
@@ -206,8 +216,24 @@ class BaseComponent:
it is considered a core or needed component, or because
the component is to be restarted later.
"""
+
+ if exit_code is not None:
+ if os.WIFEXITED(exit_code):
+ exit_str = "process exited normally with exit status %d" % (exit_code)
+ elif os.WIFSIGNALED(exit_code):
+ sig = os.WTERMSIG(exit_code)
+ signame = get_signame(sig)
+ if os.WCOREDUMP(exit_code):
+ exit_str = "process dumped core with exit status %d (killed by signal %d: %s)" % (exit_code, sig, signame)
+ else:
+ exit_str = "process terminated with exit status %d (killed by signal %d: %s)" % (exit_code, sig, signame)
+ else:
+ exit_str = "unknown condition with exit status %d" % (exit_code)
+ else:
+ exit_str = "unknown condition"
+
logger.error(BIND10_COMPONENT_FAILED, self.name(), self.pid(),
- exit_code if exit_code is not None else "unknown")
+ exit_str)
if not self.running():
raise ValueError("Can't fail component that isn't running")
self.__state = STATE_STOPPED
diff --git a/src/lib/python/isc/cc/tests/.gitignore b/src/lib/python/isc/cc/tests/.gitignore
new file mode 100644
index 0000000..b7ac2ae
--- /dev/null
+++ b/src/lib/python/isc/cc/tests/.gitignore
@@ -0,0 +1 @@
+/cc_test
diff --git a/src/lib/python/isc/config/tests/.gitignore b/src/lib/python/isc/config/tests/.gitignore
new file mode 100644
index 0000000..52a9c5e
--- /dev/null
+++ b/src/lib/python/isc/config/tests/.gitignore
@@ -0,0 +1 @@
+/config_test
diff --git a/src/lib/python/isc/datasrc/finder_python.cc b/src/lib/python/isc/datasrc/finder_python.cc
index f4b4ceb..33a503f 100644
--- a/src/lib/python/isc/datasrc/finder_python.cc
+++ b/src/lib/python/isc/datasrc/finder_python.cc
@@ -47,15 +47,15 @@ using namespace isc::datasrc::python;
namespace {
ZoneFinder::FindResultFlags
-getFindResultFlags(const ZoneFinder::FindResult& result) {
+getFindResultFlags(const ZoneFinder::Context& context) {
ZoneFinder::FindResultFlags result_flags = ZoneFinder::RESULT_DEFAULT;
- if (result.isWildcard()) {
+ if (context.isWildcard()) {
result_flags = result_flags | ZoneFinder::RESULT_WILDCARD;
}
- if (result.isNSECSigned()) {
+ if (context.isNSECSigned()) {
result_flags = result_flags | ZoneFinder::RESULT_NSEC_SIGNED;
}
- if (result.isNSEC3Signed()) {
+ if (context.isNSEC3Signed()) {
result_flags = result_flags | ZoneFinder::RESULT_NSEC3_SIGNED;
}
return (result_flags);
@@ -83,13 +83,13 @@ PyObject* ZoneFinder_helper(ZoneFinder* finder, PyObject* args) {
try {
ZoneFinder::FindOptions options =
static_cast<ZoneFinder::FindOptions>(options_int);
- const ZoneFinder::FindResult find_result(
+ ConstZoneFinderContextPtr find_ctx(
finder->find(PyName_ToName(name), PyRRType_ToRRType(rrtype),
options));
- const ZoneFinder::Result r = find_result.code;
- isc::dns::ConstRRsetPtr rrsp = find_result.rrset;
+ const ZoneFinder::Result r = find_ctx->code;
+ isc::dns::ConstRRsetPtr rrsp = find_ctx->rrset;
ZoneFinder::FindResultFlags result_flags =
- getFindResultFlags(find_result);
+ getFindResultFlags(*find_ctx);
if (rrsp) {
// Use N instead of O so the refcount isn't increased twice
return (Py_BuildValue("INI", r, createRRsetObject(*rrsp),
@@ -127,12 +127,12 @@ PyObject* ZoneFinder_helper_all(ZoneFinder* finder, PyObject* args) {
ZoneFinder::FindOptions options =
static_cast<ZoneFinder::FindOptions>(options_int);
std::vector<isc::dns::ConstRRsetPtr> target;
- const ZoneFinder::FindResult find_result(
+ ConstZoneFinderContextPtr find_ctx(
finder->findAll(PyName_ToName(name), target, options));
- const ZoneFinder::Result r = find_result.code;
- isc::dns::ConstRRsetPtr rrsp = find_result.rrset;
+ const ZoneFinder::Result r = find_ctx->code;
+ isc::dns::ConstRRsetPtr rrsp = find_ctx->rrset;
ZoneFinder::FindResultFlags result_flags =
- getFindResultFlags(find_result);
+ getFindResultFlags(*find_ctx);
if (r == ZoneFinder::SUCCESS) {
// Copy all the RRsets to the result list
PyObjectContainer list_container(PyList_New(target.size()));
diff --git a/src/lib/python/isc/log/tests/.gitignore b/src/lib/python/isc/log/tests/.gitignore
new file mode 100644
index 0000000..b9cf241
--- /dev/null
+++ b/src/lib/python/isc/log/tests/.gitignore
@@ -0,0 +1 @@
+/log_console.py
diff --git a/src/lib/python/isc/log_messages/work/.gitignore b/src/lib/python/isc/log_messages/work/.gitignore
new file mode 100644
index 0000000..05a7653
--- /dev/null
+++ b/src/lib/python/isc/log_messages/work/.gitignore
@@ -0,0 +1,2 @@
+/__init__.py
+/*_messages.py
diff --git a/src/lib/python/isc/notify/tests/.gitignore b/src/lib/python/isc/notify/tests/.gitignore
new file mode 100644
index 0000000..69f6d95
--- /dev/null
+++ b/src/lib/python/isc/notify/tests/.gitignore
@@ -0,0 +1 @@
+/notify_out_test
diff --git a/src/lib/resolve/.gitignore b/src/lib/resolve/.gitignore
new file mode 100644
index 0000000..292ed1a
--- /dev/null
+++ b/src/lib/resolve/.gitignore
@@ -0,0 +1,2 @@
+/resolve_messages.cc
+/resolve_messages.h
diff --git a/src/lib/resolve/tests/.gitignore b/src/lib/resolve/tests/.gitignore
new file mode 100644
index 0000000..d6d1ec8
--- /dev/null
+++ b/src/lib/resolve/tests/.gitignore
@@ -0,0 +1 @@
+/run_unittests
diff --git a/src/lib/server_common/.gitignore b/src/lib/server_common/.gitignore
new file mode 100644
index 0000000..e25a98f
--- /dev/null
+++ b/src/lib/server_common/.gitignore
@@ -0,0 +1,2 @@
+/server_common_messages.cc
+/server_common_messages.h
diff --git a/src/lib/server_common/tests/.gitignore b/src/lib/server_common/tests/.gitignore
new file mode 100644
index 0000000..0749d37
--- /dev/null
+++ b/src/lib/server_common/tests/.gitignore
@@ -0,0 +1,2 @@
+/data_path.h
+/run_unittests
diff --git a/src/lib/statistics/tests/.gitignore b/src/lib/statistics/tests/.gitignore
new file mode 100644
index 0000000..d6d1ec8
--- /dev/null
+++ b/src/lib/statistics/tests/.gitignore
@@ -0,0 +1 @@
+/run_unittests
diff --git a/src/lib/testutils/dnsmessage_test.cc b/src/lib/testutils/dnsmessage_test.cc
index af354d5..3d653c0 100644
--- a/src/lib/testutils/dnsmessage_test.cc
+++ b/src/lib/testutils/dnsmessage_test.cc
@@ -86,6 +86,9 @@ void
rrsetCheck(isc::dns::ConstRRsetPtr expected_rrset,
isc::dns::ConstRRsetPtr actual_rrset)
{
+ SCOPED_TRACE("Comparing RRsets\n"
+ " Actual: " + actual_rrset->toText() +
+ " Expected: " + expected_rrset->toText());
EXPECT_EQ(expected_rrset->getName(), actual_rrset->getName());
EXPECT_EQ(expected_rrset->getClass(), actual_rrset->getClass());
EXPECT_EQ(expected_rrset->getType(), actual_rrset->getType());
diff --git a/src/lib/testutils/dnsmessage_test.h b/src/lib/testutils/dnsmessage_test.h
index 1aba526..8a46e0e 100644
--- a/src/lib/testutils/dnsmessage_test.h
+++ b/src/lib/testutils/dnsmessage_test.h
@@ -157,6 +157,18 @@ public:
private:
std::vector<isc::dns::ConstRRsetPtr>& rrsets_;
};
+
+class RRsetDumper {
+public:
+ RRsetDumper(std::string& output) :
+ output_(output)
+ {}
+ void operator()(isc::dns::ConstRRsetPtr rrset) {
+ output_ += " " + rrset->toText();
+ }
+private:
+ std::string& output_;
+};
}
/// Set of unit tests to check if two sets of RRsets are identical.
@@ -195,6 +207,10 @@ rrsetsCheck(EXPECTED_ITERATOR expected_begin, EXPECTED_ITERATOR expected_end,
ACTUAL_ITERATOR actual_begin, ACTUAL_ITERATOR actual_end)
{
std::vector<isc::dns::ConstRRsetPtr> checked_rrsets; // for duplicate check
+ std::string expected_text, actual_text;
+ std::for_each(expected_begin, expected_end,
+ detail::RRsetDumper(expected_text));
+ std::for_each(actual_begin, actual_end, detail::RRsetDumper(actual_text));
unsigned int rrset_matched = 0;
ACTUAL_ITERATOR it;
for (it = actual_begin; it != actual_end; ++it) {
@@ -217,11 +233,16 @@ rrsetsCheck(EXPECTED_ITERATOR expected_begin, EXPECTED_ITERATOR expected_end,
}
}
- // make sure all expected RRsets are in actual sets
- EXPECT_EQ(std::distance(expected_begin, expected_end), rrset_matched);
- // make sure rrsets only contains expected RRsets
- EXPECT_EQ(std::distance(expected_begin, expected_end),
- std::distance(actual_begin, actual_end));
+ {
+ SCOPED_TRACE(std::string("Comparing two RRsets:\n") +
+ "Actual:\n" + actual_text +
+ "Expected:\n" + expected_text);
+ // make sure all expected RRsets are in actual sets
+ EXPECT_EQ(std::distance(expected_begin, expected_end), rrset_matched);
+ // make sure rrsets only contains expected RRsets
+ EXPECT_EQ(std::distance(expected_begin, expected_end),
+ std::distance(actual_begin, actual_end));
+ }
}
/// Set of unit tests to check if two sets of RRsets are identical using
diff --git a/src/lib/testutils/testdata/.gitignore b/src/lib/testutils/testdata/.gitignore
new file mode 100644
index 0000000..3c239fc
--- /dev/null
+++ b/src/lib/testutils/testdata/.gitignore
@@ -0,0 +1,12 @@
+/badExampleQuery_fromWire.wire
+/examplequery_fromWire.wire
+/iquery_fromWire.wire
+/iquery_response_fromWire.wire
+/iqueryresponse_fromWire.wire
+/multiquestion_fromWire.wire
+/nsec3query_fromWire.wire
+/nsec3query_nodnssec_fromWire.wire
+/queryBadEDNS_fromWire.wire
+/shortanswer_fromWire.wire
+/simplequery_fromWire.wire
+/simpleresponse_fromWire.wire
diff --git a/src/lib/util/buffer.h b/src/lib/util/buffer.h
index eb90d64..a885db1 100644
--- a/src/lib/util/buffer.h
+++ b/src/lib/util/buffer.h
@@ -122,10 +122,10 @@ public:
/// an exception of class \c isc::dns::InvalidBufferPosition will be thrown.
/// \param position The new position (offset from the beginning of the
/// buffer).
- void setPosition(size_t position)
- {
- if (position > len_)
- isc_throw(InvalidBufferPosition, "position is too large");
+ void setPosition(size_t position) {
+ if (position > len_) {
+ throwError("position is too large");
+ }
position_ = position;
}
//@}
@@ -137,10 +137,9 @@ public:
///
/// If the remaining length of the buffer is smaller than 8-bit, an
/// exception of class \c isc::dns::InvalidBufferPosition will be thrown.
- uint8_t readUint8()
- {
+ uint8_t readUint8() {
if (position_ + sizeof(uint8_t) > len_) {
- isc_throw(InvalidBufferPosition, "read beyond end of buffer");
+ throwError("read beyond end of buffer");
}
return (data_[position_++]);
@@ -150,13 +149,12 @@ public:
///
/// If the remaining length of the buffer is smaller than 16-bit, an
/// exception of class \c isc::dns::InvalidBufferPosition will be thrown.
- uint16_t readUint16()
- {
+ uint16_t readUint16() {
uint16_t data;
const uint8_t* cp;
if (position_ + sizeof(data) > len_) {
- isc_throw(InvalidBufferPosition, "read beyond end of buffer");
+ throwError("read beyond end of buffer");
}
cp = &data_[position_];
@@ -171,13 +169,12 @@ public:
///
/// If the remaining length of the buffer is smaller than 32-bit, an
/// exception of class \c isc::dns::InvalidBufferPosition will be thrown.
- uint32_t readUint32()
- {
+ uint32_t readUint32() {
uint32_t data;
const uint8_t* cp;
if (position_ + sizeof(data) > len_) {
- isc_throw(InvalidBufferPosition, "read beyond end of buffer");
+ throwError("read beyond end of buffer");
}
cp = &data_[position_];
@@ -196,10 +193,9 @@ public:
/// If the remaining length of the buffer is smaller than the specified
/// length, an exception of class \c isc::dns::InvalidBufferPosition will
/// be thrown.
- void readData(void* data, size_t len)
- {
+ void readData(void* data, size_t len) {
if (position_ + len > len_) {
- isc_throw(InvalidBufferPosition, "read beyond end of buffer");
+ throwError("read beyond end of buffer");
}
memcpy(data, &data_[position_], len);
@@ -215,10 +211,9 @@ public:
/// @param Reference to a buffer (data will be stored there).
/// @param Size specified number of bytes to read in a vector.
///
- void readVector(std::vector<uint8_t>& data, size_t len)
- {
+ void readVector(std::vector<uint8_t>& data, size_t len) {
if (position_ + len > len_) {
- isc_throw(InvalidBufferPosition, "read beyond end of buffer");
+ throwError("read beyond end of buffer");
}
data.resize(len);
@@ -226,6 +221,15 @@ public:
}
private:
+ /// \brief A common helper to throw an exception on invalid operation.
+ ///
+ /// Experiments showed that throwing from each method makes the buffer
+ /// operation slower, so we consolidate it here, and let the methods
+ /// call this.
+ static void throwError(const char* msg) {
+ isc_throw(InvalidBufferPosition, msg);
+ }
+
size_t position_;
// XXX: The following must be private, but for a short term workaround with
diff --git a/src/lib/util/locks.h b/src/lib/util/locks.h
index 971c413..daaf216 100644
--- a/src/lib/util/locks.h
+++ b/src/lib/util/locks.h
@@ -15,13 +15,9 @@
/// This file (right now) provides dummy locks
/// It also contains code to use boost/threads locks:
///
-/// if USE_BOOST_THREADS is defined, we typedef the relevant classes
-/// and derive from the relevant templates so our dummy locks are
-/// replaced by the boost locks (--enable-boost-threads)
///
-/// If USE_BOOST_THREADS is NOT defined, all locks are dummy classes
-/// that don't actually do anything. At this moment, only the very
-/// minimal set of methods that we actually use is defined.
+/// All locks are dummy classes that don't actually do anything. At this moment,
+/// only the very minimal set of methods that we actually use is defined.
///
/// Note that we need to include <config.h> in our .cc files for that
/// to be set. we might want to enfore this at compile time with a check
@@ -30,8 +26,6 @@
#ifndef __LOCKS_
#define __LOCKS_
-#ifndef USE_BOOST_THREADS
-
namespace isc {
namespace util {
namespace locks {
@@ -64,52 +58,4 @@ public:
} // namespace util
} // namespace isc
-#else // USE_BOOST_THREADS
-
-// Workaround for a problem with boost and sunstudio 5.10
-// There is a version check in there that appears wrong,
-// which makes including boost/thread.hpp fail
-// This will probably be fixed in a future version of boost,
-// in which case this part can be removed then
-#ifdef NEED_SUNPRO_WORKAROUND
-#if defined(__SUNPRO_CC) && __SUNPRO_CC == 0x5100
-#undef __SUNPRO_CC
-#define __SUNPRO_CC 0x5090
-#endif
-#endif // NEED_SUNPRO_WORKAROUND
-
-#include <boost/thread.hpp>
-#include <boost/interprocess/sync/sharable_lock.hpp>
-#include <boost/interprocess/sync/scoped_lock.hpp>
-#include <boost/interprocess/sync/interprocess_upgradable_mutex.hpp>
-#include <boost/interprocess/sync/interprocess_recursive_mutex.hpp>
-
-namespace isc {
-namespace util {
-namespace locks {
-
-typedef boost::mutex mutex;
-typedef boost::interprocess::interprocess_upgradable_mutex upgradable_mutex;
-typedef boost::interprocess::interprocess_recursive_mutex recursive_mutex;
-
-template <typename T>
-struct sharable_lock : public boost::interprocess::sharable_lock<T> {
-public:
- sharable_lock(T& mtype) : boost::interprocess::sharable_lock<T>(mtype) {}
-};
-
-
-template <class T>
-struct scoped_lock : public boost::interprocess::scoped_lock<T> {
-public:
- scoped_lock(T& mtype) : boost::interprocess::scoped_lock<T>(mtype) { }
-};
-
-} // namespace locks
-} // namespace util
-} // namespace isc
-
-
-#endif // USE_BOOST_THREADS
-
#endif // __LOCKS_
diff --git a/src/lib/util/python/.gitignore b/src/lib/util/python/.gitignore
new file mode 100644
index 0000000..c54df80
--- /dev/null
+++ b/src/lib/util/python/.gitignore
@@ -0,0 +1,2 @@
+/gen_wiredata.py
+/mkpywrapper.py
diff --git a/src/lib/util/python/gen_wiredata.py.in b/src/lib/util/python/gen_wiredata.py.in
index 8b3eac0..f997701 100755
--- a/src/lib/util/python/gen_wiredata.py.in
+++ b/src/lib/util/python/gen_wiredata.py.in
@@ -822,6 +822,29 @@ class RP(RR):
f.write('# MAILBOX=%s TEXT=%s\n' % (self.mailbox, self.text))
f.write('%s %s\n' % (mailbox_wire, text_wire))
+class SSHFP(RR):
+ '''Implements rendering SSHFP RDATA in the test data format.
+
+ Configurable parameters are as follows (see the description of the
+ same name of attribute for the default value):
+ - algorithm (int): The algorithm number.
+ - fingerprint_type (int): The fingerprint type.
+ - fingerprint (string): The fingerprint.
+ '''
+ algorithm = 2
+ fingerprint_type = 1
+ fingerprint = '123456789abcdef67890123456789abcdef67890'
+ def dump(self, f):
+ if self.rdlen is None:
+ self.rdlen = 2 + (len(self.fingerprint) / 2)
+ else:
+ self.rdlen = int(self.rdlen)
+ self.dump_header(f, self.rdlen)
+ f.write('# ALGORITHM=%d FINGERPRINT_TYPE=%d FINGERPRINT=%s\n' % (self.algorithm,
+ self.fingerprint_type,
+ self.fingerprint))
+ f.write('%02x %02x %s\n' % (self.algorithm, self.fingerprint_type, self.fingerprint))
+
class MINFO(RR):
'''Implements rendering MINFO RDATA in the test data format.
diff --git a/src/lib/util/tests/.gitignore b/src/lib/util/tests/.gitignore
new file mode 100644
index 0000000..d6d1ec8
--- /dev/null
+++ b/src/lib/util/tests/.gitignore
@@ -0,0 +1 @@
+/run_unittests
diff --git a/src/lib/xfr/tests/.gitignore b/src/lib/xfr/tests/.gitignore
new file mode 100644
index 0000000..d6d1ec8
--- /dev/null
+++ b/src/lib/xfr/tests/.gitignore
@@ -0,0 +1 @@
+/run_unittests
diff --git a/tests/lettuce/.gitignore b/tests/lettuce/.gitignore
new file mode 100644
index 0000000..e389f47
--- /dev/null
+++ b/tests/lettuce/.gitignore
@@ -0,0 +1 @@
+/setup_intree_bind10.sh
diff --git a/tests/lettuce/features/xfrin_bind10.feature b/tests/lettuce/features/xfrin_bind10.feature
index 70c3571..8bc6e5e 100644
--- a/tests/lettuce/features/xfrin_bind10.feature
+++ b/tests/lettuce/features/xfrin_bind10.feature
@@ -7,5 +7,5 @@ Feature: Xfrin
A query for www.example.org should have rcode REFUSED
Wait for bind10 stderr message CMDCTL_STARTED
When I send bind10 the command Xfrin retransfer example.org IN 127.0.0.1 47807
- Then wait for new bind10 stderr message XFRIN_XFR_TRANSFER_SUCCESS not XFRIN_XFR_PROCESS_FAILURE
+ Then wait for new bind10 stderr message XFRIN_TRANSFER_SUCCESS not XFRIN_XFR_PROCESS_FAILURE
A query for www.example.org should have rcode NOERROR
diff --git a/tests/system/.gitignore b/tests/system/.gitignore
new file mode 100644
index 0000000..76f87fe
--- /dev/null
+++ b/tests/system/.gitignore
@@ -0,0 +1,2 @@
+/conf.sh
+/run.sh
diff --git a/tests/system/bindctl/nsx1/.gitignore b/tests/system/bindctl/nsx1/.gitignore
new file mode 100644
index 0000000..84432f2
--- /dev/null
+++ b/tests/system/bindctl/nsx1/.gitignore
@@ -0,0 +1 @@
+/b10-config.db.template
diff --git a/tests/system/glue/.gitignore b/tests/system/glue/.gitignore
new file mode 100644
index 0000000..87e08bf
--- /dev/null
+++ b/tests/system/glue/.gitignore
@@ -0,0 +1 @@
+/setup.sh
diff --git a/tests/system/glue/nsx1/.gitignore b/tests/system/glue/nsx1/.gitignore
new file mode 100644
index 0000000..1c67281
--- /dev/null
+++ b/tests/system/glue/nsx1/.gitignore
@@ -0,0 +1 @@
+/b10-config.db
diff --git a/tests/system/ixfr/.gitignore b/tests/system/ixfr/.gitignore
new file mode 100644
index 0000000..027d45e
--- /dev/null
+++ b/tests/system/ixfr/.gitignore
@@ -0,0 +1,8 @@
+/b10-config.db
+/common_tests.sh
+/db.example.n0
+/db.example.n2
+/db.example.n2.refresh
+/db.example.n4
+/db.example.n6
+/ixfr_init.sh
diff --git a/tests/system/ixfr/in-1/.gitignore b/tests/system/ixfr/in-1/.gitignore
new file mode 100644
index 0000000..87e08bf
--- /dev/null
+++ b/tests/system/ixfr/in-1/.gitignore
@@ -0,0 +1 @@
+/setup.sh
diff --git a/tests/system/ixfr/in-2/.gitignore b/tests/system/ixfr/in-2/.gitignore
new file mode 100644
index 0000000..87e08bf
--- /dev/null
+++ b/tests/system/ixfr/in-2/.gitignore
@@ -0,0 +1 @@
+/setup.sh
diff --git a/tests/system/ixfr/in-2/tests.sh b/tests/system/ixfr/in-2/tests.sh
index 7b1e2a8..3050713 100644
--- a/tests/system/ixfr/in-2/tests.sh
+++ b/tests/system/ixfr/in-2/tests.sh
@@ -54,7 +54,7 @@ then
exit 1
fi
-grep XFRIN_XFR_TRANSFER_SUCCESS nsx2/bind10.run | grep IXFR > /dev/null
+grep XFRIN_IXFR_TRANSFER_SUCCESS nsx2/bind10.run | grep IXFR > /dev/null
if [ $? -ne 0 ];
then
echo "R:$CLIENT_NAME FAIL no 'IXFR successful' message in the BIND 10 log"
diff --git a/tests/system/ixfr/in-3/.gitignore b/tests/system/ixfr/in-3/.gitignore
new file mode 100644
index 0000000..87e08bf
--- /dev/null
+++ b/tests/system/ixfr/in-3/.gitignore
@@ -0,0 +1 @@
+/setup.sh
diff --git a/tests/system/ixfr/in-4/.gitignore b/tests/system/ixfr/in-4/.gitignore
new file mode 100644
index 0000000..87e08bf
--- /dev/null
+++ b/tests/system/ixfr/in-4/.gitignore
@@ -0,0 +1 @@
+/setup.sh
diff --git a/tests/tools/badpacket/.gitignore b/tests/tools/badpacket/.gitignore
new file mode 100644
index 0000000..ad6c1e6
--- /dev/null
+++ b/tests/tools/badpacket/.gitignore
@@ -0,0 +1 @@
+/badpacket
diff --git a/tests/tools/badpacket/tests/.gitignore b/tests/tools/badpacket/tests/.gitignore
new file mode 100644
index 0000000..d6d1ec8
--- /dev/null
+++ b/tests/tools/badpacket/tests/.gitignore
@@ -0,0 +1 @@
+/run_unittests
diff --git a/tests/tools/perfdhcp/.gitignore b/tests/tools/perfdhcp/.gitignore
new file mode 100644
index 0000000..1a8375a
--- /dev/null
+++ b/tests/tools/perfdhcp/.gitignore
@@ -0,0 +1 @@
+/perfdhcp
More information about the bind10-changes
mailing list