BIND 10 trac2225, updated. 6ad0b6d3c74a5dff49d8237597a5cdb03701d78c [2225] added a new scenario which a slave isn't started
BIND 10 source code commits
bind10-changes at lists.isc.org
Fri Sep 28 08:34:56 UTC 2012
The branch, trac2225 has been updated
discards fc68b5e633fb4631fc9f81204c783cdc4007eb6c (commit)
discards e45e236a21be35f31fec539331877094f3adb0b9 (commit)
discards e0e690cc3c4194678ca55b5e10cc4e50e9185f48 (commit)
discards 9e54085ec92bd8a8af3fd1042887a973d70b2d79 (commit)
discards 01ef4b9e28eba5fb58ab15383066ac2c253bfc81 (commit)
discards 36d6278052e653036c101e196af3186801913447 (commit)
discards 38d6d1bbf565025d372017c76b2e50f75bdb9185 (commit)
discards 36d15da8ffb637b0e81fed8e11760a6d22f68883 (commit)
via 6ad0b6d3c74a5dff49d8237597a5cdb03701d78c (commit)
via 1bb92b49f494992fd835d48c55a405cc086c761d (commit)
via e9e42f5f58667995c28056ebfd57bf8330675a73 (commit)
via f16d66dc5594b3434cd1f6f43f62b385e5ca1bed (commit)
via 9a930c4a3519216fbb0cfb0424c8339ec7c261f8 (commit)
via 35ad6100102a882ea9fbf960ad05cc3b93a51cdd (commit)
via 6e2a7dfe6f743de63afba18592b2e5240bb89dff (commit)
via feb8d3d646f841537e7b546daabb7b49e468bdad (commit)
via 7f2bb474f70587ee0de39d5bfb7fb2ddc8281b26 (commit)
via 29fa8bf0dc4870c92abb497e6928f008220e596c (commit)
via 6179d997ee8c0ace36592f496e0a0b2a28c54148 (commit)
via e5bf564f734555173b5cae04757bc3f870f3463a (commit)
via 6fad0e38082ed1078acb7add13321212dccdcf88 (commit)
via ac07f31fa2182962f4e958330fc460abeac350d5 (commit)
via 34a2c9541c16578175eb928b9ea3f3c7755965d3 (commit)
via 73b258b1b4284e1017aedacee9ed75301be23e79 (commit)
via ae65cad3fbe923329f674694614e0af560c37018 (commit)
via 6de9ca4708fe4e56e17aedab38b89d7f76bf2515 (commit)
via 354ae8184c3927c7afa0aef59297d1d03561d640 (commit)
via f3ab6e90b9c941913a2241c6ad78d943471a33d6 (commit)
via 40c6c15c3f5a820c8d304d4a2b3529645d2cb368 (commit)
via 07d2d30929b0734618e7c41ca27c8074cde51770 (commit)
via 60b9a13fc0a03e38d198bf4e5491075792528aff (commit)
via 8e0d333e76d72abef126f197a6caa00d6b7a10e3 (commit)
via c0672d18dc8709e15a175e781a5d690a281f6d19 (commit)
via 28c929309ce6200a388e97b5cb89e81f2e061a6f (commit)
via a3a63ba5ca940d188dc28a4cd2db63d908ec3e1b (commit)
via 7298821ace8f33cd683dc52856e7272e52eef589 (commit)
via 8660a77ac739583b013c502a903ea9cdd979ae7e (commit)
via 7e2522e4177ca26bd511395be02f35c96335bcd0 (commit)
via 59c096cda4f3fbe9f3744c6cbe01497c909b4f06 (commit)
via d0a83e52167113c55010a44d69e8b540d9bbd00b (commit)
via 6a0721cb9647ea09a7ca40174dee6de870829cc5 (commit)
via 4735853efc9554536e607a3431631348ea2d0c49 (commit)
via f9a376ab22abb84377f24846f66b49c58469fe17 (commit)
via f569d3269831e8ad74e8f5c4d2e84d38e524c979 (commit)
via 30ab0e64b78a7e634d037c7a885546ebce010da1 (commit)
via 77b10c708212584290b7505d90cdcd110fc1637d (commit)
via 46109eae81cb9768ea339ebd3bd53006ad276d56 (commit)
via f142802acfd65bb9cd993a254a99ff34ef731573 (commit)
via 48b6e91386b46eed383126ad98dddfafc9f7e75e (commit)
via bfdb265f3ac48ec543de081bb02b79e78b8d7f9e (commit)
via 2adcf03734b609514a665ce58315e71ddb110eb5 (commit)
via 1edc3624622f3ca9f88c8b09e11d257e769b3172 (commit)
via c2a303e6d227217d56d249e132b873b37c36daa7 (commit)
via 52177bb31f5fb8e134aecb9fd039c368684ad2df (commit)
via dfc79b64ed8fdf77c46ebea1e025e229b7497a60 (commit)
via c714ebe32337bd89ba5209547fc759810c15dd19 (commit)
via c7060ccf5e4ec41ee12e08e66f410fe14199c545 (commit)
via 10da91d4dcb3baf1b040864bf60cd89d82a80e8f (commit)
via 730fd2f2de14300227720977b581c421088dfec7 (commit)
via 55f7ce059f925a531c9f0fe9adad18c1a941dec6 (commit)
via 64e40094a730e99da4b24bbe97cb346af7b8fbcd (commit)
via 42dc5ad3e7eed5f53d2d38b6683b7a9b427c1a1d (commit)
via 09ccd7c1ec01fd8d30270b2d8ee37a4e5e4962b1 (commit)
via 050a4704b903ba8e7d0b2e4736f4d1972f0319b4 (commit)
via 8ad666fca65695efbcb90d656e089aa3ae314590 (commit)
via e5222941c0cc25a5580eab58390dcf62ea2278a8 (commit)
via 51cbca9e606137fcdad96a99824f27809701afcc (commit)
via 12d5f67f3e58ab09f8369b4218baa2c8a40e2b9a (commit)
via 1830f45cda873932566b1eacfb15f5f6170cf916 (commit)
via 25aae9d23312a6c808bbbbfcefbafe3dc31cba3f (commit)
via 593cc7dda0802634b12373f0215b6033e1791118 (commit)
via 736bcacc37bde8182160fbf5b4c8191a1846373e (commit)
via e7d7b0180edf1cf28316bfb0b2e2ccb8e8593eb9 (commit)
via 06651f1b45157e097a20de4ad5139652d6f388c9 (commit)
via 8c2ea7566498c8b3f826a85316ae63f0d57e80b0 (commit)
via babcf09173d27ad28f53d911d1908c7375fd4e22 (commit)
via ab51eb2f65a390b98b5601f8e7bf5bd6634e4ad6 (commit)
via 493a2b3d0b3c494aced54e2778595618ec6f5100 (commit)
via 02dd6ed0dd458e7627e06bcab6a5168354e929d6 (commit)
via bbf34f3ba16d91fabcfa53a5b989e999d9c19875 (commit)
via 237def8395cbe9f2d1193e804443c403294b8c6f (commit)
via 1a7e6f8b2d18a98bfac5f4a47f55f8f288e4d0ab (commit)
via 79848f3dcca94147df133b8969c5c1aa0fddc299 (commit)
via 6c192e5c0903f349b4d80cf2bb6cd964040ae7da (commit)
via 89476cd22065c79ba1c7dd48a29f0fc958917b39 (commit)
via 00bbd50bb77e798708891f177003ad1d01bf9dbe (commit)
via e016cdf94cb3377f4e186e4f175ef89eb382089f (commit)
via 4a8a4d185ddee401aeed95f9f51d443d725c4d5f (commit)
via 117a2ea8230671c17c8011cdd2f0b747eadbec01 (commit)
via c60790ce9bd68bb155ea77f5f6629f9f754ef9c7 (commit)
via 07b53c2297ed4bbb167fda55b43b70575208f0d3 (commit)
via e13c2404bea730239b16f5b74142e7364fa739aa (commit)
via 360b20fadb0530d2cb069f428c09fa6320d941ad (commit)
via 6c447559f19adc0e040a6a6f44c06dffcf3ce0ff (commit)
via d570aa48262648cfccdf96e01b1c7c66a9d0839d (commit)
via 85c7fb2e4627e84c2d17c59432509a221034e6ea (commit)
via 40d4887c7d706c0a27b1aef720f2a8870d26d5bb (commit)
via 58388f2e53b8428f0ecb5936d9d6c076b2003203 (commit)
via 208dbbaf77e781a19d6efdaca32d423ea266a6f1 (commit)
via 26f07019946b185e191ce7490f4c398b830f8420 (commit)
via 5633225648b2a4a5582327131f9ca7a7d83b340f (commit)
via 8a1be35939e817930570e4afc76af240e78c2602 (commit)
via d98f7060780fc89f05c1610d3e88403969dfc819 (commit)
via d0a5d03e219ccb645894c8afe41e1bd548ff83e1 (commit)
via 08162d5b7ef6b101030b71ac0553b237a7c64bd9 (commit)
via 1f486ccd690d8267b0a4410d8163298c5f5cf1ef (commit)
via de626b3c2aec2bd7896e75c5ae14337b03342729 (commit)
via 56aa815ad1200fb8b2901634b6004bbcd0eb5331 (commit)
via 825d75f5c66467173d0c0f7333bed1ba47857823 (commit)
via d863d2f26c9e2fb9a866d56de15723cfd5a9e58b (commit)
via efeb8e81af78b9bca08785646948da46aec3d1c8 (commit)
via eb5ace77a9c1ee6ce93d827080624d566eaf572c (commit)
via f6f8e26e272ee0d94c36bf2be23e005bd74c55ea (commit)
via 5b6cccfc2e34d0319e8c5358391de6fe9a5dcdf4 (commit)
via 56a18b6b8dc444efeec0052eefc4539633e9a762 (commit)
via aa53f0f7d8c06ea9dd7082971d080895a56d6c14 (commit)
via c13d8c76021cb27bf9d5f0bf899b267ca04caa21 (commit)
via 6a1a234386523e5be05adf0a6ac51df29bf3b7a6 (commit)
via d82d52518ab96e3c7aece6c2c4ebcb599b993770 (commit)
via 1841fbb5bc6e588c311f22360bb59462a244c644 (commit)
via 2538cdc2251cc27c38193680af90f2e7e94ffb57 (commit)
via 25a4afa2395d6d9263b385a73cec2919c4a21830 (commit)
via e5f7c1b89ed635155aa022f6a14285657b152a1c (commit)
via e05f1d87c3764033b920fa8a227cfd05dc3ca3b8 (commit)
via 5fe90d260706fdac0ed0678233e116213520c575 (commit)
via 559395e392e41dd929368d820f83f58555c5e319 (commit)
via 5f39840cf5f96ee91f826aa801d934864a55d444 (commit)
via 0fc1237c7ba1edd9128387ba1db1bae9e7aacc27 (commit)
via d1d08475aec2b0f3c45fa35db5ad629ab14dfb26 (commit)
via 45e74198ebeee9d0e2eee780185d91c20fa63513 (commit)
via c200bbcb68071fb2148344d629c5605498bf0656 (commit)
via 5b1952368e65ab130340c46d5e0f3bb72a343993 (commit)
via c33327c47f10065f09cc32bcd93b1e7f5612e5d8 (commit)
via e7bd7d6ceafd9ba44c953835b6bea33d5f54b32c (commit)
via 55eaa4303d7bc462286b4a6b7f262976f1a346bb (commit)
via f4663e8cab5485f19069ddb80fef073a6705c105 (commit)
via ca9f73e098180f9d5d188f1d1bd46031f9b81ce3 (commit)
via 055a8352580a73069b0745d3fbddec1c488f9e59 (commit)
via 3aeb78fd7a34c736a4ba3b985ef6a0f8432bfafa (commit)
via 01244a662e1ad9387e27c2f5e1723c954d47a12b (commit)
via 97e21bca2ddfc0ceb7350b741efae2c4199383ad (commit)
via 82225f72c3af83d8ca5e2b33b0e93d32b1e2fdbf (commit)
via a61af89b9799857a955e5e0b7b04b1f273dd63ab (commit)
via 6f25929f1cfee95e92142cdf8ce42fe5f2a8aae6 (commit)
via b4913f7bf1a1b4c11c0b614d9a7516d4ab629776 (commit)
via 6bf4ff4eb4a3a8095bdd5d622cab24f46a0d9834 (commit)
via 2fdacd86e25c075a059e81274a1531ed6f0b787c (commit)
via 4f202471b02965f0bd3c402d1bffa503c5ee6527 (commit)
via 7f5343e069bf811dc88bea5fad759bbe7fe4f78c (commit)
via 6ec1f3e61319b6da0ed8567bc53c1c5cf7455c9a (commit)
via f4c07bf162d7ed4495496f7574242ccd2add5926 (commit)
via 40ee3e7167ec6660de797955af57ec9f5ec67c79 (commit)
via 4de75117f84ad23f96392677338e733d6e1e87bd (commit)
via 280d0604dbf569d0aeb3065cae12cd38876e3e5c (commit)
via 5b3b1ee480cb8d47477972118f7ce03751699359 (commit)
via ee538e573d559fc8912465fa8161cd975287e807 (commit)
via 6419f929bd1e7eb951fe1c0c0debc7e8ae30a59a (commit)
via d26a99607b8d1e1b861de188c321e869ebb92cd1 (commit)
via 163a1a31471bea170a85b2e158f66c9f439bb2e4 (commit)
via f802f3ba317e49c1c1d16134855eb1b3c1aaeec8 (commit)
via 1b10623a2bdc55e002defbd36eeba3f0d6f9d656 (commit)
via fa04b8777b196e3094f8b351c7f3d1ebf56fdd7e (commit)
via 3ef4699944709ab91f856396c76b90f3c97df3ac (commit)
via 950c72ccede294b7692bb7bdb20ef1bc28c3cc76 (commit)
via eee7c05de1c33b0d932ebbaca4370129d315e0ba (commit)
via 5ba139c6882f7399449eafdefb2202578fbe2903 (commit)
via e5ad751d8372bb0306d4eb5b8311a7b9cb7c7c32 (commit)
via a9fd37df7cba2f48a2f4e42cc0d1f26b991b5636 (commit)
via fd8264e864303a947addc8f788b31ae7c7ea4c12 (commit)
via 65253b21b0d15b2608655e90a129efbe1de24d07 (commit)
via 5261f939f525186b89099aae541f7042e42c54ba (commit)
via a16ecb53a5c69269e749845e708215d5d3c75c31 (commit)
via c2a477d54ed891c84fb2218831d0d7950a91fdb4 (commit)
via 56df3cb883631fc189fde26781f5d01695fcd386 (commit)
via 6b8528fd0d34b49c44f9da2b2ce2127399fcca34 (commit)
via cef74673072e543a06bdc487afcad8787a811908 (commit)
via 1b35c2aba35663034479b293e238deeff068dced (commit)
via 8b356c0c7202c02151a882cc7676803bcaf90b75 (commit)
via f985b514e1b9a338b3940928415396c6a87dbce1 (commit)
via e1ae6846339d8ce57f61a56d1fccf00d92ef910b (commit)
via ceeaa8ae96db786b65715e0cb440a411fbab8b2c (commit)
via 8d093113af5d33b4c7841ef7bc003d29c5d1268e (commit)
via 94f1fcdbcfb0cfceaff0e850df3bdd190efb73ad (commit)
via 8240c04e9bf91ce4d26f8f55d23216f7e48b3fd8 (commit)
via 40fe836dcfb4fb1b37152bf9078be0351e6eea21 (commit)
via a4eaf309a64c5eb108653efe6796af54f4da7337 (commit)
via 328b5fc4dbbafe7a5286d857581505a7cf55ea0e (commit)
via 10018747ad68736c31da97ec1ad7c5eef94489a0 (commit)
via d0a412aeddad9a64649eb28a3a5ad1003aa97d94 (commit)
via 9ec629f3465d8651ceaf5b766fc91598e26f7023 (commit)
via d06e139eb2d03ef254ab8b2f3f56083addcfe756 (commit)
via b169803e955bf7c5647f4c67bd851b2a9a03630c (commit)
via d047c08a96270d4d757dc4c0bcd4dfece3bf586a (commit)
via 60c7cde711da05825672e5b8601ff596993a030b (commit)
via 88871270308be3aaaeb8918c29c2b2c3ca4286e5 (commit)
via 436f507ae4e387f7fe5e242864500a6839bfc32b (commit)
via 054b2a99a4e527a15739c0e6a9a0247bb23a5d1a (commit)
via 38081a3eb213dad9b2233f37e8c8f499a9e50f1a (commit)
via 350fa8a59935fac90f02e86a28d81b4941cc7ee3 (commit)
via 06bf41c4a9c79e450e78f3eecac362dd0b18d06c (commit)
via 467078e47174e5a3d20caa8b1a1c8c0b35b0876f (commit)
via faa94e73318b57601aa9bf857387863c9be72a6b (commit)
via f8b99e43fcc928eccd9b30fd187d91e92782ee02 (commit)
via 4c9e89ec998fb375031f14929775074aa1d9c903 (commit)
via e7ecb231083afee21757b37c4128fd222a6e93a5 (commit)
via a96a4dc64fe455a2f2e8a1fd2f3a66e251d6c1a3 (commit)
via 5da8f0ce275404a225759f9673daffa4d9da386a (commit)
via b1b7f7957f6ae64bf1621368548127a4ccf5acc3 (commit)
via 9967f37261be7acaecff0dc8d3fe6b1fc49fc4d0 (commit)
via 7ece465effe5156585f24bb60e836211c8aacab0 (commit)
via 6517287182efdd74a30184c4806634b0de5ef049 (commit)
via f13266d9802fa12a8bf223d94b6fe97923ca384a (commit)
via 861b38913123f90f4ab68d105d1111b2e42a6481 (commit)
via f07fdbfa1a7d0f592c0b3d9e1a10e287dc608dd1 (commit)
via a897e5cf38d32a41107f537bc8dace96813b06d8 (commit)
This update added new revisions after undoing existing revisions. That is
to say, the old revision is not a strict subset of the new revision. This
situation occurs when you --force push a change and generate a repository
containing something like this:
* -- * -- B -- O -- O -- O (fc68b5e633fb4631fc9f81204c783cdc4007eb6c)
\
N -- N -- N (6ad0b6d3c74a5dff49d8237597a5cdb03701d78c)
When this happens we assume that you've already had alert emails for all
of the O revisions, and so we here report only the revisions in the N
branch from the common base, B.
Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.
- Log -----------------------------------------------------------------
commit 6ad0b6d3c74a5dff49d8237597a5cdb03701d78c
Author: Naoki Kambe <kambe at jprs.co.jp>
Date: Fri Sep 28 14:19:52 2012 +0900
[2225] added a new scenario which a slave isn't started
commit 1bb92b49f494992fd835d48c55a405cc086c761d
Author: Naoki Kambe <kambe at jprs.co.jp>
Date: Fri Sep 28 13:17:00 2012 +0900
[2225] added same tests as the above tests
commit e9e42f5f58667995c28056ebfd57bf8330675a73
Author: Naoki Kambe <kambe at jprs.co.jp>
Date: Fri Sep 28 11:13:49 2012 +0900
[2225] removed duplicate tests
commit f16d66dc5594b3434cd1f6f43f62b385e5ca1bed
Author: Naoki Kambe <kambe at jprs.co.jp>
Date: Thu Sep 13 14:25:11 2012 +0900
[2225] updated docstrings for counter.py
commit 9a930c4a3519216fbb0cfb0424c8339ec7c261f8
Author: Naoki Kambe <kambe at jprs.co.jp>
Date: Mon Sep 3 18:58:47 2012 +0900
[2225] corrected the wrong place to count receive errors of a unix socket
moved the counter from handle_request() to _select_loop() and revised the
related unit test
commit 35ad6100102a882ea9fbf960ad05cc3b93a51cdd
Author: Naoki Kambe <kambe at jprs.co.jp>
Date: Mon Sep 3 10:35:20 2012 +0900
[2225] changed the behavior of the counter class
It doesn't create a counter object twice if it has been already created. It
just returns created object in that case. The global functors aren't set inside
of the counter classes, but they are set in the init method after the counter
object is firstly created. It is intended at this change that the init method
is responsible for creation of a counter method and setting of the global
functor.
commit 6e2a7dfe6f743de63afba18592b2e5240bb89dff
Author: Naoki Kambe <kambe at jprs.co.jp>
Date: Fri Aug 31 14:48:52 2012 +0900
[2225] added descriptions about unixsocket counters into the manpage
commit feb8d3d646f841537e7b546daabb7b49e468bdad
Author: Naoki Kambe <kambe at jprs.co.jp>
Date: Fri Aug 31 12:16:17 2012 +0900
[2225] added checks for statistics unixsocket counters
commit 7f2bb474f70587ee0de39d5bfb7fb2ddc8281b26
Author: Naoki Kambe <kambe at jprs.co.jp>
Date: Fri Aug 31 14:31:31 2012 +0900
[2225] changed according the changes of the counter class
- implemented a dummy counter class inside of the notifyout test and made it
use it.
commit 29fa8bf0dc4870c92abb497e6928f008220e596c
Author: Naoki Kambe <kambe at jprs.co.jp>
Date: Fri Aug 31 12:13:10 2012 +0900
[2225] changed the format of the debug when revised getstats command
- output content of answer to the stats module with the debug message
commit 6179d997ee8c0ace36592f496e0a0b2a28c54148
Author: Naoki Kambe <kambe at jprs.co.jp>
Date: Wed Sep 26 18:30:47 2012 +0900
[2225] introduced new counter classes and implemented unixsocket counters
- implemented a base class Counter and a concrete XfroutCounter as an external
module under isc.statistics. Because it is easy to implement another
concrete Counter class for other module in future. The caller module can
statically import it. The new counter class provides a getter method for
each statistics item. It is intended for making loose relationship between
the counter class and the caller module.
- added implementation of unixsocket counter into the existing UnixSockServer
Class.
- added new tests for checking counters implemented in UnixSockServer are
working properly into xfrout_test.py.
- removed obsoleted XfroutCounter class and TestXfroutCounter from xfrout.py
and xfrout_test.py.
- implemented enabling/disabling counting in the new counter class.
-----------------------------------------------------------------------
Summary of changes:
ChangeLog | 61 +-
configure.ac | 18 +-
doc/guide/bind10-guide.xml | 112 +-
src/bin/auth/Makefile.am | 6 -
src/bin/auth/auth_srv.cc | 3 -
src/bin/auth/benchmarks/Makefile.am | 6 -
src/bin/auth/tests/Makefile.am | 7 -
src/bin/auth/tests/auth_srv_unittest.cc | 10 +-
src/bin/dhcp4/dhcp4_messages.mes | 10 +
src/bin/dhcp4/dhcp4_srv.cc | 14 +-
src/bin/dhcp6/dhcp6_messages.mes | 10 +
src/bin/dhcp6/dhcp6_srv.cc | 14 +-
src/lib/acl/dns.h | 4 -
src/lib/acl/tests/dns_test.cc | 14 +-
src/lib/datasrc/Makefile.am | 13 +-
src/lib/datasrc/cache.cc | 396 ---
src/lib/datasrc/cache.h | 223 --
src/lib/datasrc/client_list.cc | 67 +-
src/lib/datasrc/client_list.h | 58 +-
src/lib/datasrc/data_source.cc | 1434 --------
src/lib/datasrc/data_source.h | 362 --
src/lib/datasrc/memory/Makefile.am | 2 +-
src/lib/datasrc/memory/domaintree.h | 118 +-
src/lib/datasrc/memory/memory_client.cc | 58 +-
src/lib/datasrc/memory/memory_client.h | 23 +-
src/lib/datasrc/memory/treenode_rrset.cc | 39 +-
src/lib/datasrc/memory/treenode_rrset.h | 2 -
src/lib/datasrc/memory/zone_finder.cc | 481 ++-
src/lib/datasrc/memory/zone_finder.h | 38 +-
src/lib/datasrc/query.cc | 116 -
src/lib/datasrc/query.h | 255 --
src/lib/datasrc/sqlite3_datasrc.cc | 917 -----
src/lib/datasrc/sqlite3_datasrc.h | 130 -
src/lib/datasrc/static_datasrc.cc | 275 --
src/lib/datasrc/static_datasrc.h | 95 -
src/lib/datasrc/tests/Makefile.am | 19 +-
src/lib/datasrc/tests/cache_unittest.cc | 340 --
src/lib/datasrc/tests/client_list_unittest.cc | 151 +-
src/lib/datasrc/tests/datasrc_unittest.cc | 1209 -------
src/lib/datasrc/tests/faked_nsec3.cc | 17 +
src/lib/datasrc/tests/faked_nsec3.h | 4 +
.../{memory/tests => tests/memory}/.gitignore | 0
.../{memory/tests => tests/memory}/Makefile.am | 3 +-
.../tests => tests/memory}/domaintree_unittest.cc | 62 +
.../memory}/memory_client_unittest.cc | 76 +-
.../tests => tests/memory}/memory_segment_test.h | 0
.../memory}/rdata_serialization_unittest.cc | 2 +-
.../tests => tests/memory}/rdataset_unittest.cc | 0
.../tests => tests/memory}/run_unittests.cc | 0
.../memory}/segment_object_holder_unittest.cc | 0
.../tests => tests/memory}/testdata/Makefile.am | 1 +
.../tests => tests/memory}/testdata/empty.zone | 0
.../memory}/testdata/example.org-broken1.zone | 0
.../memory}/testdata/example.org-broken2.zone | 0
.../testdata/example.org-cname-and-not-nsec-1.zone | 0
.../testdata/example.org-cname-and-not-nsec-2.zone | 0
.../testdata/example.org-dname-ns-apex-1.zone | 0
.../testdata/example.org-dname-ns-apex-2.zone | 0
.../testdata/example.org-dname-ns-nonapex-1.zone | 0
.../testdata/example.org-dname-ns-nonapex-2.zone | 0
.../testdata/example.org-duplicate-type-bad.zone | 0
.../testdata/example.org-duplicate-type.zone | 0
.../memory}/testdata/example.org-empty.zone | 0
.../testdata/example.org-multiple-cname.zone | 0
.../testdata/example.org-multiple-dname.zone | 0
.../testdata/example.org-multiple-nsec3.zone | 0
.../testdata/example.org-multiple-nsec3param.zone | 0
.../memory}/testdata/example.org-multiple.zone | 0
.../testdata/example.org-nsec3-empty-salt.zone} | 8 +-
.../testdata/example.org-nsec3-fewer-labels.zone | 0
.../testdata/example.org-nsec3-more-labels.zone | 0
.../example.org-nsec3-signed-no-param.zone | 0
.../memory}/testdata/example.org-nsec3-signed.zone | 0
.../memory}/testdata/example.org-out-of-zone.zone | 0
.../example.org-rrsig-follows-nothing.zone | 0
.../memory}/testdata/example.org-rrsigs.zone | 0
.../testdata/example.org-wildcard-dname.zone | 0
.../memory}/testdata/example.org-wildcard-ns.zone | 0
.../testdata/example.org-wildcard-nsec3.zone | 0
.../memory}/testdata/example.org.zone | 0
.../memory}/treenode_rrset_unittest.cc | 58 +-
.../tests => tests/memory}/zone_data_unittest.cc | 0
.../tests => tests/memory}/zone_finder_unittest.cc | 351 +-
.../tests => tests/memory}/zone_table_unittest.cc | 0
src/lib/datasrc/tests/memory_datasrc_unittest.cc | 66 -
src/lib/datasrc/tests/query_unittest.cc | 71 -
src/lib/datasrc/tests/sqlite3_unittest.cc | 950 ------
src/lib/datasrc/tests/static_unittest.cc | 422 ---
src/lib/datasrc/tests/test_datasrc.cc | 657 ----
src/lib/datasrc/tests/test_datasrc.h | 111 -
src/lib/datasrc/tests/testdata/contexttest.zone | 5 +-
.../datasrc/tests/zone_finder_context_unittest.cc | 39 +-
src/lib/dhcp/dhcp4.h | 3 +
src/lib/dhcp/iface_mgr.cc | 236 +-
src/lib/dhcp/iface_mgr.h | 40 +
src/lib/dhcp/iface_mgr_linux.cc | 9 +-
src/lib/dhcp/libdhcp++.cc | 17 +-
src/lib/dhcp/pkt4.cc | 2 -
src/lib/dhcp/pkt6.cc | 10 +-
src/lib/dhcp/tests/iface_mgr_unittest.cc | 85 +-
src/lib/dns/labelsequence.cc | 3 +-
src/lib/dns/nsec3hash.cc | 58 +-
src/lib/dns/nsec3hash.h | 34 +-
src/lib/dns/tests/labelsequence_unittest.cc | 33 +
src/lib/dns/tests/nsec3hash_unittest.cc | 24 +-
src/lib/python/isc/acl/tests/dns_test.py | 4 -
.../python/isc/datasrc/tests/clientlist_test.py | 93 +-
src/lib/python/isc/ddns/session.py | 2 -
.../lettuce/features/xfrin_notify_handling.feature | 77 +-
tests/tools/dhcp-ubench/dhcp-perf-guide.xml | 18 +-
tests/tools/perfdhcp/Makefile.am | 35 +-
tests/tools/perfdhcp/command_options.cc | 454 +--
tests/tools/perfdhcp/command_options.h | 160 +-
tests/tools/perfdhcp/main.cc | 28 +-
tests/tools/perfdhcp/perfdhcp.c | 3559 --------------------
tests/tools/perfdhcp/stats_mgr.h | 136 +-
tests/tools/perfdhcp/templates/Makefile.am | 4 +-
tests/tools/perfdhcp/test_control.cc | 513 ++-
tests/tools/perfdhcp/test_control.h | 255 +-
.../tools/perfdhcp/tests/command_options_helper.h | 5 +-
.../perfdhcp/tests/command_options_unittest.cc | 84 +-
tests/tools/perfdhcp/tests/stats_mgr_unittest.cc | 3 +-
.../tools/perfdhcp/tests/test_control_unittest.cc | 64 +-
123 files changed, 3074 insertions(+), 12987 deletions(-)
delete mode 100644 src/lib/datasrc/cache.cc
delete mode 100644 src/lib/datasrc/cache.h
delete mode 100644 src/lib/datasrc/data_source.cc
delete mode 100644 src/lib/datasrc/query.cc
delete mode 100644 src/lib/datasrc/query.h
delete mode 100644 src/lib/datasrc/sqlite3_datasrc.cc
delete mode 100644 src/lib/datasrc/sqlite3_datasrc.h
delete mode 100644 src/lib/datasrc/static_datasrc.cc
delete mode 100644 src/lib/datasrc/static_datasrc.h
delete mode 100644 src/lib/datasrc/tests/cache_unittest.cc
delete mode 100644 src/lib/datasrc/tests/datasrc_unittest.cc
rename src/lib/datasrc/{memory/tests => tests/memory}/.gitignore (100%)
rename src/lib/datasrc/{memory/tests => tests/memory}/Makefile.am (92%)
rename src/lib/datasrc/{memory/tests => tests/memory}/domaintree_unittest.cc (95%)
rename src/lib/datasrc/{memory/tests => tests/memory}/memory_client_unittest.cc (92%)
rename src/lib/datasrc/{memory/tests => tests/memory}/memory_segment_test.h (100%)
rename src/lib/datasrc/{memory/tests => tests/memory}/rdata_serialization_unittest.cc (99%)
rename src/lib/datasrc/{memory/tests => tests/memory}/rdataset_unittest.cc (100%)
rename src/lib/datasrc/{memory/tests => tests/memory}/run_unittests.cc (100%)
rename src/lib/datasrc/{memory/tests => tests/memory}/segment_object_holder_unittest.cc (100%)
rename src/lib/datasrc/{memory/tests => tests/memory}/testdata/Makefile.am (96%)
rename src/lib/datasrc/{memory/tests => tests/memory}/testdata/empty.zone (100%)
rename src/lib/datasrc/{memory/tests => tests/memory}/testdata/example.org-broken1.zone (100%)
rename src/lib/datasrc/{memory/tests => tests/memory}/testdata/example.org-broken2.zone (100%)
rename src/lib/datasrc/{memory/tests => tests/memory}/testdata/example.org-cname-and-not-nsec-1.zone (100%)
rename src/lib/datasrc/{memory/tests => tests/memory}/testdata/example.org-cname-and-not-nsec-2.zone (100%)
rename src/lib/datasrc/{memory/tests => tests/memory}/testdata/example.org-dname-ns-apex-1.zone (100%)
rename src/lib/datasrc/{memory/tests => tests/memory}/testdata/example.org-dname-ns-apex-2.zone (100%)
rename src/lib/datasrc/{memory/tests => tests/memory}/testdata/example.org-dname-ns-nonapex-1.zone (100%)
rename src/lib/datasrc/{memory/tests => tests/memory}/testdata/example.org-dname-ns-nonapex-2.zone (100%)
rename src/lib/datasrc/{memory/tests => tests/memory}/testdata/example.org-duplicate-type-bad.zone (100%)
rename src/lib/datasrc/{memory/tests => tests/memory}/testdata/example.org-duplicate-type.zone (100%)
rename src/lib/datasrc/{memory/tests => tests/memory}/testdata/example.org-empty.zone (100%)
rename src/lib/datasrc/{memory/tests => tests/memory}/testdata/example.org-multiple-cname.zone (100%)
rename src/lib/datasrc/{memory/tests => tests/memory}/testdata/example.org-multiple-dname.zone (100%)
rename src/lib/datasrc/{memory/tests => tests/memory}/testdata/example.org-multiple-nsec3.zone (100%)
rename src/lib/datasrc/{memory/tests => tests/memory}/testdata/example.org-multiple-nsec3param.zone (100%)
rename src/lib/datasrc/{memory/tests => tests/memory}/testdata/example.org-multiple.zone (100%)
copy src/lib/datasrc/{memory/tests/testdata/example.org-nsec3-signed.zone => tests/memory/testdata/example.org-nsec3-empty-salt.zone} (91%)
rename src/lib/datasrc/{memory/tests => tests/memory}/testdata/example.org-nsec3-fewer-labels.zone (100%)
rename src/lib/datasrc/{memory/tests => tests/memory}/testdata/example.org-nsec3-more-labels.zone (100%)
rename src/lib/datasrc/{memory/tests => tests/memory}/testdata/example.org-nsec3-signed-no-param.zone (100%)
rename src/lib/datasrc/{memory/tests => tests/memory}/testdata/example.org-nsec3-signed.zone (100%)
rename src/lib/datasrc/{memory/tests => tests/memory}/testdata/example.org-out-of-zone.zone (100%)
rename src/lib/datasrc/{memory/tests => tests/memory}/testdata/example.org-rrsig-follows-nothing.zone (100%)
rename src/lib/datasrc/{memory/tests => tests/memory}/testdata/example.org-rrsigs.zone (100%)
rename src/lib/datasrc/{memory/tests => tests/memory}/testdata/example.org-wildcard-dname.zone (100%)
rename src/lib/datasrc/{memory/tests => tests/memory}/testdata/example.org-wildcard-ns.zone (100%)
rename src/lib/datasrc/{memory/tests => tests/memory}/testdata/example.org-wildcard-nsec3.zone (100%)
rename src/lib/datasrc/{memory/tests => tests/memory}/testdata/example.org.zone (100%)
rename src/lib/datasrc/{memory/tests => tests/memory}/treenode_rrset_unittest.cc (91%)
rename src/lib/datasrc/{memory/tests => tests/memory}/zone_data_unittest.cc (100%)
rename src/lib/datasrc/{memory/tests => tests/memory}/zone_finder_unittest.cc (83%)
rename src/lib/datasrc/{memory/tests => tests/memory}/zone_table_unittest.cc (100%)
delete mode 100644 src/lib/datasrc/tests/query_unittest.cc
delete mode 100644 src/lib/datasrc/tests/sqlite3_unittest.cc
delete mode 100644 src/lib/datasrc/tests/static_unittest.cc
delete mode 100644 src/lib/datasrc/tests/test_datasrc.cc
delete mode 100644 src/lib/datasrc/tests/test_datasrc.h
delete mode 100644 tests/tools/perfdhcp/perfdhcp.c
-----------------------------------------------------------------------
diff --git a/ChangeLog b/ChangeLog
index e266ba9..9c5f9ba 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,48 @@
+bind10-devel-20120927 released on September 27, 2012
+
+483. [func] marcin
+ libdhcp++: Added new parameter to define sub-second timeout
+ for DHCP packet reception. The total timeout is now specified
+ by two parameters: first specifies integral number of
+ seconds, second (which defaults to 0) specifies fractional
+ seconds with microsecond resolution.
+ (Trac #2231, git 15560cac16e4c52129322e3cb1787e0f47cf7850)
+
+482. [func] team
+ Memory footprint of the in-memory data source has been
+ substantially improved. For example, b10-auth now requires much
+ less memory than BIND 9 named for loading and serving the same
+ zone in-memory. This is a transparent change in terms of user
+ operation; there's no need to update or change the configuration
+ to enable this feature.
+ Notes: multiple instances of b10-auth still make separate copies
+ of the memory image. Also, loading zones in memory still suspends
+ query processing, so manual reloading or reloading after incoming
+ transfer may cause service disruption for huge zones.
+ (Multiple Trac tickets, Summarized in Trac #2101)
+
+481. [bug] vorner
+ The abbreviated form of IP addresses in ACLs is accepted
+ (eg. "from": ["127.0.0.1", "::1"] now works).
+ (Trac #2191, git 48b6e91386b46eed383126ad98dddfafc9f7e75e)
+
+480. [doc] vorner
+ Added documentation about global TSIG key ring to the Guide.
+ (Trac #2189, git 52177bb31f5fb8e134aecb9fd039c368684ad2df)
+
+479. [func] marcin
+ Refactored perfdhcp tool to C++, added missing unit tests and removed
+ the old code. The new code uses libdhcp++ (src/lib/dhcp) for DHCP
+ packet management, network interface management and packet
+ transmission.
+ (Trac #1954, git 8d56105742f3043ed4b561f26241f3e4331f51dc)
+ (Trac #1955, git 6f914bb2c388eb4dd3e5c55297f8988ab9529b3f)
+ (Trac #1956, git 6f914bb2c388eb4dd3e5c55297f8988ab9529b3f)
+ (Trac #1957, git 7fca81716ad3a755bf5744e88c3adeef15b04450)
+ (Trac #1958, git 94e17184270cda58f55e6da62e845695117fede3)
+ (Trac #1959, git a8cf043db8f44604c7773e047a9dc2861e58462a)
+ (Trac #1960, git 6c192e5c0903f349b4d80cf2bb6cd964040ae7da)
+
478. [func] naokikambe
New statistics items added into b10-xfrout: ixfr_running and
axfr_running. Their values can be obtained by invoking "Stats show
@@ -10,19 +55,19 @@
(Trac #2190, git e0ffa11d49ab949ee5a4ffe7682b0e6906667baa)
476. [bug] vorner
- The XfrIn now accepts transfers with some TSIG signatures omitted, as
+ The Xfrin now accepts transfers with some TSIG signatures omitted, as
allowed per RFC2845, section 4.4. This solves a compatibility
issues with Knot and NSD.
- (Trac #1375, git 7ca65cb9ec528118f370142d7e7b792fcc31c9cf)
+ (Trac #1357, git 7ca65cb9ec528118f370142d7e7b792fcc31c9cf)
475. [func] naokikambe
- Added Xfrout statistics counters: notifyoutv4, notifyoutv6, xfrrej, and
- xfrreqdone. These are per-zone type counters. The value of these
- counters can be seen with zone name by invoking "Stats show Xfrout" via
- bindctl.
+ Added Xfrout statistics counters: notifyoutv4, notifyoutv6,
+ xfrrej, and xfrreqdone. These are per-zone type counters.
+ The value of these counters can be seen with zone name by
+ invoking "Stats show Xfrout" via bindctl.
(Trac #2158, git e68c127fed52e6034ab5309ddd506da03c37a08a)
-474. [func] stephen
+474. [func] stephen
DHCP servers now use the BIND 10 logging system for messages.
(Trac #1545, git de69a92613b36bd3944cb061e1b7c611c3c85506)
@@ -41,7 +86,7 @@
contain the generated documentation.
(Trac #1687, git 2d4063b1a354f5048ca9dfb195e8e169650f43d0)
-471. [bug] vorner
+471. [bug] vorner
Fixed a problem when b10-loadzone tried to tread semicolon
in string data as start of comment, which caused invalid
data being loaded.
diff --git a/configure.ac b/configure.ac
index eb3f289..3360f78 100644
--- a/configure.ac
+++ b/configure.ac
@@ -990,20 +990,6 @@ CPPFLAGS="$CPPFLAGS -DASIO_DISABLE_THREADS=1"
# Check for functions that are not available on all platforms
AC_CHECK_FUNCS([pselect])
-# perfdhcp: If the clock_gettime() function does not exist on the system,
-# use an alternative supplied in the code based on gettimeofday().
-CLOCK_GETTIME_LDFLAGS=
-AC_CHECK_LIB([rt], [clock_gettime], [CLOCK_GETTIME_LDFLAGS=-lrt], [])
-AC_SUBST([CLOCK_GETTIME_LDFLAGS])
-
-# perfdhcp: if getifaddrs() does not exist, have the code output a message
-# that it can't be run on this version of the operating system. For the
-# systems on which BIND 10 is built, this means Solaris 10. (Replacements
-# for this function are long and involved, and the function is reported present
-# on Solaris 11 and later, either in the libsocket or libnsl libraries.)
-AC_SEARCH_LIBS([getifaddrs], [socket nsl],
- [AC_DEFINE([HAVE_GETIFADDRS], [1], [getifaddrs() present])])
-
# /dev/poll issue: ASIO uses /dev/poll by default if it's available (generally
# the case with Solaris). Unfortunately its /dev/poll specific code would
# trigger the gcc's "missing-field-initializers" warning, which would
@@ -1209,11 +1195,11 @@ AC_CONFIG_FILES([Makefile
src/lib/exceptions/tests/Makefile
src/lib/datasrc/Makefile
src/lib/datasrc/memory/Makefile
- src/lib/datasrc/memory/tests/Makefile
- src/lib/datasrc/memory/tests/testdata/Makefile
src/lib/datasrc/memory/benchmarks/Makefile
src/lib/datasrc/tests/Makefile
src/lib/datasrc/tests/testdata/Makefile
+ src/lib/datasrc/tests/memory/Makefile
+ src/lib/datasrc/tests/memory/testdata/Makefile
src/lib/xfr/Makefile
src/lib/xfr/tests/Makefile
src/lib/log/Makefile
diff --git a/doc/guide/bind10-guide.xml b/doc/guide/bind10-guide.xml
index a95b0f5..2e66ad5 100644
--- a/doc/guide/bind10-guide.xml
+++ b/doc/guide/bind10-guide.xml
@@ -1310,6 +1310,89 @@ TODO
many modules. So we show them here in one place.
</para>
+ <section id='common-tsig'>
+ <title>TSIG keys</title>
+
+ <para>
+ TSIG is a way to sign requests and responses in DNS. It is defined in
+ RFC 2845 and uses symmetric cryptography to sign the DNS messages. If
+ you want to make any use of TSIG (to authenticate transfers or DDNS,
+ for example), you need to set up shared secrets between the endpoints.
+ </para>
+
+ <para>
+ BIND 10 uses a global key ring for the secrets. It doesn't currently
+ mean they would be stored differently, they are just in one place of
+ the configuration.
+ </para>
+
+ <section id='tsig-key-syntax'>
+ <title>Key anatomy and syntax</title>
+
+ <para>
+ Each key has three attributes. One is a name by which it is referred
+ both in DNS packets and the rest of the configuration. Another is the
+ algorithm used to compute the signature. And the last part is a
+ base64 encoded secret, which might be any blob of data.
+ </para>
+
+ <para>
+ The parts are written into a string, concatenated together by colons.
+ So if you wanted to have a key called "example.key", used as a
+ HMAC-MD5 key with secret "secret", you'd write it as:
+<screen>"example.key.:c2VjcmV0:hmac-md5"</screen>
+ </para>
+
+ <para>
+ The HMAC-MD5 algorithm is the default, so you can omit it. You could
+ write the same key as:
+<screen>"example.key.:c2VjcmV0"</screen>
+ </para>
+
+ <para>
+ You can also use these algorithms (which may not be omitted from the
+ key definition if used):
+ <itemizedlist>
+ <listitem>hmac-sha1</listitem>
+ <listitem>hmac-sha224</listitem>
+ <listitem>hmac-sha256</listitem>
+ <listitem>hmac-sha384</listitem>
+ <listitem>hmac-sha512</listitem>
+ </itemizedlist>
+ </para>
+
+ <para>
+ The name of the key must be a valid DNS name.
+ </para>
+ </section>
+
+ <section id='tsig-key-ring'>
+ <title>Key ring</title>
+ <para>
+ The key ring lives in the configuration in "tsig_keys/keys". Most of
+ the system uses the keys from there — ACLs, authoritative server to
+ sign responses to signed queries, and <command>b10-xfrout</command>
+ to sign transfers. The <command>b10-xfrin</command> uses its own
+ configuration for keys, but that will be fixed in Trac ticket
+ <ulink url="http://bind10.isc.org/ticket/1351">#1351</ulink>.
+ </para>
+
+ <para>
+ The key ring is just a list of strings, each describing one key. So,
+ to add a new key, you can do this:
+ <screen>> <userinput>config add tsig_keys/keys "example.key.:c2VjcmV0"</userinput>
+> <userinput>config show tsig_keys/keys</userinput>
+tsig_keys/keys[0] "example.key.:c2VjcmV0" string (modified)
+> <userinput>config commit</userinput></screen>
+ </para>
+
+ <para>
+ You can keep as many keys as you want in the key ring, but each must
+ have a different name.
+ </para>
+ </section>
+ </section>
+
<section id='common-acl'>
<title>ACLs</title>
@@ -1375,9 +1458,9 @@ AND_MATCH := "ALL": [ RULE_RAW, RULE_RAW, ... ]
<para>
The other is TSIG key by which the message was signed. The ACL
contains only the name (under the name "key"), the key itself
- must be stored in the global keyring. This property is applicable only
- to the DNS context.
-<!-- TODO: Section for the keyring and link to it.-->
+ must be stored in the global key ring (see <xref
+ linkend="tsig-key-ring"/>).
+ This property is applicable only to the DNS context.
</para>
<para>
@@ -1403,18 +1486,6 @@ AND_MATCH := "ALL": [ RULE_RAW, RULE_RAW, ... ]
will work in a similar way.
</para>
- <note>
- <simpara>
- The list form is currently rejected due to an
- implementation bug. There is a plan to fix it relatively
- soon, so the syntax is kept here, but note that it won't
- work until the bug is fixed. To keep track of the status
- of the issue, see
- <ulink url="http://bind10.isc.org/ticket/2191">Trac #2191</ulink>.
- Until then, the value must be a single string.
- </simpara>
- </note>
-
<para>
If that is not enough, you can compose the matching conditions
to logical expressions. They are called "ANY", "ALL" and "NOT".
@@ -1827,7 +1898,7 @@ can use various data source backends.
operator, the authoritative server needs to be told to reload it, by
<screen>> <userinput>Auth loadzone example.org</userinput></screen>
You don't need to do this when the zone is modified by
- XfrIn, it does so automatically.
+ <command>b10-xfrin</command>; it does so automatically.
</para>
<para>
@@ -2151,7 +2222,7 @@ Xfrout/transfer_acl[0] {"action": "ACCEPT"} any (default)</screen>
<para>
If you want to require TSIG in access control, a system wide TSIG
- "key ring" must be configured.
+ key ring must be configured (see <xref linkend="tsig-key-ring"/>).
In this example, we allow client matching both the IP address
and key.
</para>
@@ -2161,7 +2232,7 @@ Xfrout/transfer_acl[0] {"action": "ACCEPT"} any (default)</screen>
> <userinput>config commit</userinput></screen>
<para>Both <command>b10-xfrout</command> and <command>b10-auth</command>
- will use the system wide keyring to check
+ will use the system wide key ring to check
TSIGs in the incoming messages and to sign responses.</para>
<para>
@@ -2371,11 +2442,12 @@ what is XfroutClient xfr_client??
> <userinput>config commit</userinput>
</screen>
The TSIG key must be configured system wide
- (see <xref linkend="xfrout"/>.)
+ (see <xref linkend="common-tsig"/>).
</para>
<para>
- Full description of ACLs can be found in <xref linkend="common-acl" />.
+ The full description of ACLs can be found in <xref
+ linkend="common-acl" />.
</para>
<note><simpara>
diff --git a/src/bin/auth/Makefile.am b/src/bin/auth/Makefile.am
index 9e24433..3efa9d3 100644
--- a/src/bin/auth/Makefile.am
+++ b/src/bin/auth/Makefile.am
@@ -57,12 +57,6 @@ b10_auth_SOURCES += common.h common.cc
b10_auth_SOURCES += statistics.cc statistics.h
b10_auth_SOURCES += datasrc_configurator.h
b10_auth_SOURCES += main.cc
-# This is a temporary workaround for #1206, where the InMemoryClient has been
-# moved to an ldopened library. We could add that library to LDADD, but that
-# is nonportable. This should've been moot after #1207, but there is still
-# one dependency; the in-memory-specific zone loader call is still in
-# auth.
-b10_auth_SOURCES += ${top_srcdir}/src/lib/datasrc/memory_datasrc.cc
nodist_b10_auth_SOURCES = auth_messages.h auth_messages.cc
EXTRA_DIST += auth_messages.mes
diff --git a/src/bin/auth/auth_srv.cc b/src/bin/auth/auth_srv.cc
index ddb7466..82a98a4 100644
--- a/src/bin/auth/auth_srv.cc
+++ b/src/bin/auth/auth_srv.cc
@@ -41,10 +41,7 @@
#include <asiodns/dns_service.h>
-#include <datasrc/query.h>
#include <datasrc/data_source.h>
-#include <datasrc/static_datasrc.h>
-#include <datasrc/sqlite3_datasrc.h>
#include <datasrc/client_list.h>
#include <xfr/xfrout_client.h>
diff --git a/src/bin/auth/benchmarks/Makefile.am b/src/bin/auth/benchmarks/Makefile.am
index c09230a..948e718 100644
--- a/src/bin/auth/benchmarks/Makefile.am
+++ b/src/bin/auth/benchmarks/Makefile.am
@@ -17,12 +17,6 @@ query_bench_SOURCES += ../auth_srv.h ../auth_srv.cc
query_bench_SOURCES += ../auth_config.h ../auth_config.cc
query_bench_SOURCES += ../statistics.h ../statistics.cc
query_bench_SOURCES += ../auth_log.h ../auth_log.cc
-# This is a temporary workaround for #1206, where the InMemoryClient has been
-# moved to an ldopened library. We could add that library to LDADD, but that
-# is nonportable. When #1207 is done this becomes moot anyway, and the
-# specific workaround is not needed anymore, so we can then remove this
-# line again.
-query_bench_SOURCES += ${top_srcdir}/src/lib/datasrc/memory_datasrc.cc
nodist_query_bench_SOURCES = ../auth_messages.h ../auth_messages.cc
diff --git a/src/bin/auth/tests/Makefile.am b/src/bin/auth/tests/Makefile.am
index f87ed4c..75e4a27 100644
--- a/src/bin/auth/tests/Makefile.am
+++ b/src/bin/auth/tests/Makefile.am
@@ -52,13 +52,6 @@ run_unittests_SOURCES += query_unittest.cc
run_unittests_SOURCES += statistics_unittest.cc
run_unittests_SOURCES += datasrc_configurator_unittest.cc
run_unittests_SOURCES += run_unittests.cc
-# This is a temporary workaround for #1206, where the InMemoryClient has been
-# moved to an ldopened library. We could add that library to LDADD, but that
-# is nonportable. This should've been moot after #1207, but there is still
-# one dependency; the in-memory-specific zone loader call is still in
-# auth.
-run_unittests_SOURCES += ${top_srcdir}/src/lib/datasrc/memory_datasrc.cc
-
nodist_run_unittests_SOURCES = ../auth_messages.h ../auth_messages.cc
diff --git a/src/bin/auth/tests/auth_srv_unittest.cc b/src/bin/auth/tests/auth_srv_unittest.cc
index e86cca4..e7b4ca7 100644
--- a/src/bin/auth/tests/auth_srv_unittest.cc
+++ b/src/bin/auth/tests/auth_srv_unittest.cc
@@ -15,6 +15,7 @@
#include <config.h>
#include <util/io/sockaddr_util.h>
+#include <util/memory_segment_local.h>
#include <dns/message.h>
#include <dns/messagerenderer.h>
@@ -1393,16 +1394,19 @@ public:
const isc::datasrc::DataSourceClientPtr
client(new FakeClient(info.data_src_client_ != NULL ?
info.data_src_client_ :
- info.cache_.get(),
+ info.getCacheClient(),
throw_when, isc_exception, fake_rrset));
clients_.push_back(client);
- data_sources_.push_back(DataSourceInfo(client.get(),
- isc::datasrc::DataSourceClientContainerPtr(), false));
+ data_sources_.push_back(
+ DataSourceInfo(client.get(),
+ isc::datasrc::DataSourceClientContainerPtr(),
+ false, RRClass::IN(), mem_sgmt_));
}
}
private:
const boost::shared_ptr<isc::datasrc::ConfigurableClientList> real_;
vector<isc::datasrc::DataSourceClientPtr> clients_;
+ MemorySegmentLocal mem_sgmt_;
};
} // end anonymous namespace for throwing proxy classes
diff --git a/src/bin/dhcp4/dhcp4_messages.mes b/src/bin/dhcp4/dhcp4_messages.mes
index 98e402d..63ddbfc 100644
--- a/src/bin/dhcp4/dhcp4_messages.mes
+++ b/src/bin/dhcp4/dhcp4_messages.mes
@@ -42,12 +42,22 @@ server is about to open sockets on the specified port.
The IPv4 DHCP server has received a packet that it is unable to
interpret. The reason why the packet is invalid is included in the message.
+% DHCP4_PACKET_RECEIVE_FAIL error on attempt to receive packet: %1
+The IPv4 DHCP server tried to receive a packet but an error
+occured during this attempt. The reason for the error is included in
+the message.
+
% DHCP4_PACKET_RECEIVED %1 (type %2) packet received on interface %3
A debug message noting that the server has received the specified type of
packet on the specified interface. Note that a packet marked as UNKNOWN
may well be a valid DHCP packet, just a type not expected by the server
(e.g. it will report a received OFFER packet as UNKNOWN).
+% DHCP4_PACKET_SEND_FAIL failed to send DHCPv4 packet: %1
+This error is output if the IPv4 DHCP server fails to send an assembled
+DHCP message to a client. The reason for the error is included in the
+message.
+
% DHCP4_PACK_FAIL failed to assemble response correctly
This error is output if the server failed to assemble the data to be
returned to the client into a valid packet. The cause is most likely
diff --git a/src/bin/dhcp4/dhcp4_srv.cc b/src/bin/dhcp4/dhcp4_srv.cc
index 2bbc075..67943c5 100644
--- a/src/bin/dhcp4/dhcp4_srv.cc
+++ b/src/bin/dhcp4/dhcp4_srv.cc
@@ -73,9 +73,15 @@ Dhcpv4Srv::run() {
int timeout = 1000;
// client's message and server's response
- Pkt4Ptr query = IfaceMgr::instance().receive4(timeout);
+ Pkt4Ptr query;
Pkt4Ptr rsp;
+ try {
+ query = IfaceMgr::instance().receive4(timeout);
+ } catch (const std::exception& e) {
+ LOG_ERROR(dhcp4_logger, DHCP4_PACKET_RECEIVE_FAIL).arg(e.what());
+ }
+
if (query) {
try {
query->unpack();
@@ -141,7 +147,11 @@ Dhcpv4Srv::run() {
.arg(rsp->getType()).arg(rsp->toText());
if (rsp->pack()) {
- IfaceMgr::instance().send(rsp);
+ try {
+ IfaceMgr::instance().send(rsp);
+ } catch (const std::exception& e) {
+ LOG_ERROR(dhcp4_logger, DHCP4_PACKET_SEND_FAIL).arg(e.what());
+ }
} else {
LOG_ERROR(dhcp4_logger, DHCP4_PACK_FAIL);
}
diff --git a/src/bin/dhcp6/dhcp6_messages.mes b/src/bin/dhcp6/dhcp6_messages.mes
index a361531..1564940 100644
--- a/src/bin/dhcp6/dhcp6_messages.mes
+++ b/src/bin/dhcp6/dhcp6_messages.mes
@@ -45,12 +45,22 @@ server is about to open sockets on the specified port.
% DHCP6_PACKET_PARSE_FAIL failed to parse incoming packet
The IPv6 DHCP server has received a packet that it is unable to interpret.
+% DHCP6_PACKET_RECEIVE_FAIL error on attempt to receive packet: %1
+The IPv6 DHCP server tried to receive a packet but an error
+occured during this attempt. The reason for the error is included in
+the message.
+
% DHCP6_PACKET_RECEIVED %1 (type %2) packet received
A debug message noting that the server has received the specified type
of packet. Note that a packet marked as UNKNOWN may well be a valid
DHCP packet, just a type not expected by the server (e.g. it will report
a received OFFER packet as UNKNOWN).
+% DHCP6_PACKET_SEND_FAIL failed to send DHCPv6 packet: %1
+This error is output if the IPv6 DHCP server fails to send an assembled
+DHCP message to a client. The reason for the error is included in the
+message.
+
% DHCP6_PACK_FAIL failed to assemble response correctly
This error is output if the server failed to assemble the data to be
returned to the client into a valid packet. The reason is most likely
diff --git a/src/bin/dhcp6/dhcp6_srv.cc b/src/bin/dhcp6/dhcp6_srv.cc
index 7c21941..54fa2b5 100644
--- a/src/bin/dhcp6/dhcp6_srv.cc
+++ b/src/bin/dhcp6/dhcp6_srv.cc
@@ -84,9 +84,15 @@ bool Dhcpv6Srv::run() {
int timeout = 1000;
// client's message and server's response
- Pkt6Ptr query = IfaceMgr::instance().receive6(timeout);
+ Pkt6Ptr query;
Pkt6Ptr rsp;
+ try {
+ query = IfaceMgr::instance().receive6(timeout);
+ } catch (const std::exception& e) {
+ LOG_ERROR(dhcp6_logger, DHCP6_PACKET_RECEIVE_FAIL).arg(e.what());
+ }
+
if (query) {
if (!query->unpack()) {
LOG_DEBUG(dhcp6_logger, DBG_DHCP6_DETAIL,
@@ -154,7 +160,11 @@ bool Dhcpv6Srv::run() {
.arg(rsp->getType()).arg(rsp->toText());
if (rsp->pack()) {
- IfaceMgr::instance().send(rsp);
+ try {
+ IfaceMgr::instance().send(rsp);
+ } catch (const std::exception& e) {
+ LOG_ERROR(dhcp6_logger, DHCP6_PACKET_SEND_FAIL).arg(e.what());
+ }
} else {
LOG_ERROR(dhcp6_logger, DHCP6_PACK_FAIL);
}
diff --git a/src/lib/acl/dns.h b/src/lib/acl/dns.h
index d08fcf3..5ea0b13 100644
--- a/src/lib/acl/dns.h
+++ b/src/lib/acl/dns.h
@@ -136,10 +136,6 @@ public:
virtual boost::shared_ptr<RequestCheck>
create(const std::string& name, isc::data::ConstElementPtr definition,
const acl::Loader<RequestContext>& loader);
-
- /// Until we are sure how the various rules work for this case, we won't
- /// allow unexpected special interpretation for list definitions.
- virtual bool allowListAbbreviation() const { return (false); }
};
} // end of namespace "internal"
diff --git a/src/lib/acl/tests/dns_test.cc b/src/lib/acl/tests/dns_test.cc
index b3ddbf4..8d6ee70 100644
--- a/src/lib/acl/tests/dns_test.cc
+++ b/src/lib/acl/tests/dns_test.cc
@@ -62,6 +62,18 @@ TEST(DNSACL, getRequestLoader) {
" \"from\": \"192.0.2.1\"}]")));
}
+// Check we can abbreviate the IP address lists and TSIG keys
+TEST(DNSACL, abbreviated) {
+ dns::RequestLoader* l(&getRequestLoader());
+
+ EXPECT_NO_THROW(l->load(Element::fromJSON("[{\"action\": \"DROP\","
+ " \"from\": [\"127.0.0.1\","
+ " \"::1\"]}]")));
+ EXPECT_NO_THROW(l->load(Element::fromJSON("[{\"action\": \"DROP\","
+ " \"key\": [\"key.example.\","
+ " \"other.\"]}]")));
+}
+
class RequestCheckCreatorTest : public ::testing::Test {
protected:
dns::internal::RequestCheckCreator creator_;
@@ -78,7 +90,7 @@ TEST_F(RequestCheckCreatorTest, names) {
}
TEST_F(RequestCheckCreatorTest, allowListAbbreviation) {
- EXPECT_FALSE(creator_.allowListAbbreviation());
+ EXPECT_TRUE(creator_.allowListAbbreviation());
}
// The following two tests check the creator for the form of
diff --git a/src/lib/datasrc/Makefile.am b/src/lib/datasrc/Makefile.am
index bd96838..eccc147 100644
--- a/src/lib/datasrc/Makefile.am
+++ b/src/lib/datasrc/Makefile.am
@@ -1,4 +1,4 @@
-SUBDIRS = . memory tests
+SUBDIRS = memory . tests
AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
AM_CPPFLAGS += -I$(top_srcdir)/src/lib/dns -I$(top_builddir)/src/lib/dns
@@ -12,7 +12,9 @@ pkglibdir = $(libexecdir)/@PACKAGE@/backends
datasrc_config.h: datasrc_config.h.pre
$(SED) -e "s|@@PKGLIBDIR@@|$(pkglibdir)|" datasrc_config.h.pre >$@
-static.zone: static.zone.pre
+# The top config.h defines "PACKAGE_STRING". When it's changed we neeed to
+# regenerate this zone file.
+static.zone: static.zone.pre $(top_builddir)/config.h $(top_srcdir)/AUTHORS
$(SED) -e "s|@@VERSION_STRING@@|$(PACKAGE_STRING)|" $(srcdir)/static.zone.pre >$@
$(SED) -e 's/\(.*\)/AUTHORS.BIND. 0 CH TXT "\1"/' $(top_srcdir)/AUTHORS >>$@
@@ -21,11 +23,7 @@ CLEANFILES += datasrc_config.h
CLEANFILES += static.zone
lib_LTLIBRARIES = libb10-datasrc.la
-libb10_datasrc_la_SOURCES = data_source.h data_source.cc
-libb10_datasrc_la_SOURCES += static_datasrc.h static_datasrc.cc
-libb10_datasrc_la_SOURCES += sqlite3_datasrc.h sqlite3_datasrc.cc
-libb10_datasrc_la_SOURCES += query.h query.cc
-libb10_datasrc_la_SOURCES += cache.h cache.cc
+libb10_datasrc_la_SOURCES = data_source.h
libb10_datasrc_la_SOURCES += rbnode_rrset.h
libb10_datasrc_la_SOURCES += rbtree.h
libb10_datasrc_la_SOURCES += zonetable.h zonetable.cc
@@ -64,6 +62,7 @@ libb10_datasrc_la_LIBADD = $(top_builddir)/src/lib/exceptions/libb10-exceptions.
libb10_datasrc_la_LIBADD += $(top_builddir)/src/lib/dns/libb10-dns++.la
libb10_datasrc_la_LIBADD += $(top_builddir)/src/lib/log/libb10-log.la
libb10_datasrc_la_LIBADD += $(top_builddir)/src/lib/cc/libb10-cc.la
+libb10_datasrc_la_LIBADD += $(builddir)/memory/libdatasrc_memory.la
libb10_datasrc_la_LIBADD += $(SQLITE_LIBS)
BUILT_SOURCES = datasrc_config.h datasrc_messages.h datasrc_messages.cc
diff --git a/src/lib/datasrc/cache.cc b/src/lib/datasrc/cache.cc
deleted file mode 100644
index d88e649..0000000
--- a/src/lib/datasrc/cache.cc
+++ /dev/null
@@ -1,396 +0,0 @@
-// Copyright (C) 2010 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 <stdint.h>
-
-#include <map>
-
-#include <dns/question.h>
-#include <dns/rrclass.h>
-#include <dns/rrset.h>
-#include <dns/rrtype.h>
-
-#include <list>
-
-#include <datasrc/cache.h>
-#include <datasrc/logger.h>
-
-using namespace std;
-using namespace isc::dns;
-
-namespace isc {
-namespace datasrc {
-
-/// \brief A \c CacheEntry object contains the data stored with
-/// each \c CacheNode: a pointer to the cached RRset (empty in
-/// the case of a negative cache entry), and a copy of the
-/// query-response flags that were returned when the RRset
-/// was originally looked up in the low-level data source.
-class CacheEntry {
-private:
- /// The copy constructor and the assignment operator are intentionally
- /// defined as private.
- CacheEntry(const CacheEntry& source);
- CacheEntry& operator=(const CacheEntry& source);
-
-public:
- CacheEntry(RRsetPtr r, uint32_t f) : rrset(r), flags(f) {};
-
- RRsetPtr rrset;
- uint32_t flags;
-};
-
-typedef boost::shared_ptr<CacheEntry> CacheEntryPtr;
-
-/// \brief A \c CacheNode is a node in the \c HotCache LRU queue. It
-/// contains a pointer to a \c CacheEntry, a reference to the \c Question
-/// that we are answering, a lifespan during which this entry remains
-/// valid, and pointers to the next and previous entries in the list.
-class CacheNode {
-private:
- /// \name Constructors and Assignment Operator
- ///
- /// Note: The copy constructor and the assignment operator are intentionally
- /// defined as private.
- //@{
- CacheNode(const CacheNode& source);
- CacheNode& operator=(const CacheNode& source);
-
-public:
- /// \brief Constructor for positive cache entry.
- ///
- /// \param rrset The \c RRset to cache.
- /// \param flags The query response flags returned from the low-level
- /// data source when this \c RRset was looked up.
- /// \param lifespan How long the cache node is to be considered valid.
- CacheNode(const RRsetPtr rrset, uint32_t flags, time_t lifespan);
-
- /// \brief Constructor for negative cache entry.
- ///
- /// \param name Query name
- /// \param rrclass Query class
- /// \param rrtype Query type
- /// \param flags Query response flags returned from the low-level
- /// data source, indicating why this lookup failed (name not found,
- /// type not found, etc).
- /// \param lifespan How long the cache node is to be considered valid.
- CacheNode(const Name& name,
- const RRClass& rrclass,
- const RRType& rrtype,
- uint32_t flags,
- time_t lifespan);
- //@}
-
- /// \name Getter and Setter Methods
- //@{
- /// \brief Returns a pointer to the cached RRset (or an empty
- /// RRsetPtr for negative cache entries).
-
- /// \return \c RRsetPtr
- RRsetPtr getRRset() const { return (entry->rrset); }
-
- /// \brief Returns name associated with cached node
- ///
- /// This is the name associated with the RRset if it is a positive
- /// entry, and the associated question name if the RRSet is NULL
- /// and this is a negative entry (together with an indication that
- /// this is a negative entry).
- string getNodeName() const {
- if (getRRset()) {
- return (getRRset()->getName().toText());
- }
- return (std::string("negative entry for ") + question.toText());
- }
-
- /// \brief Returns the query response flags associated with the data.
- ///
- /// \return \c uint32_t
- uint32_t getFlags() const { return (entry->flags); }
-
- /// \brief Is this record still valid?
- ///
- /// \return True if the expiration time has not yet passed,
- /// or false if it has.
- bool isValid() const;
- //@}
-
- // An iterator referencing this entry in the LRU list. This
- // permits unit-time removal using list::erase().
- list<CacheNodePtr>::iterator lru_entry_;
-
- /// The \c Question (name/rrclass/rrtype) answered by this cache node
- const isc::dns::Question question;
-
-private:
- // The cached RRset data
- CacheEntryPtr entry;
-
- // When this record should be discarded
- time_t expiry;
-};
-
-// CacheNode constructor for a positive cache entry
-CacheNode::CacheNode(const RRsetPtr rrset, const uint32_t flags,
- const time_t lifespan) :
- question(Question(rrset->getName(), rrset->getClass(), rrset->getType()))
-{
- const time_t now = time(NULL);
- expiry = now + lifespan;
-
- entry = CacheEntryPtr(new CacheEntry(rrset, flags));
-}
-
-// CacheNode constructor for a negative cache entry
-CacheNode::CacheNode(const Name& name,
- const RRClass& rrclass,
- const RRType& rrtype,
- const uint32_t flags,
- const time_t lifespan) :
- question(Question(name, rrclass, rrtype))
-{
- const time_t now = time(NULL);
- expiry = now + lifespan;
-
- entry = CacheEntryPtr(new CacheEntry(RRsetPtr(), flags));
-}
-// Returns true if the node has not yet expired.
-bool
-CacheNode::isValid() const {
- const time_t now = time(NULL);
- return (now < expiry);
-}
-
-/// This class abstracts the implementation details for \c HotCache.
-///
-/// Each node inserted into the cache is placed at the head of a
-/// doubly-linked list. Whenever that node is retrieved from the cache,
-/// it is again moved to the head of the list. When the configured
-/// number of slots in the cache has been exceeded, the least recently
-/// used nodes will be removed from the tail of the list.
-///
-/// A pointer to each cache node is also placed in a \c std::map object,
-/// keyed by \c isc::dns::Question. This allows retrieval of data in
-/// (usually) logarithmic time. (Possible TODO item: replace this with a
-/// hash table instead.)
-class HotCacheImpl {
-public:
- HotCacheImpl(int slots, bool enabled);
-
- // The LRU list
- list<CacheNodePtr> lru_;
-
- // Flag to indicate whether the cache is enabled
- bool enabled_;
-
- // The number of available slots in the LRU list. (If zero,
- // then the list is unbounded; otherwise, items are removed
- // from the tail of the list whenever it grows past slots_.)
- int slots_;
-
- // The number of items currently in the list.
- int count_;
-
- // Map from query tuple to cache node pointer, allowing fast retrieval
- // of data without a linear search of the LRU list
- std::map<Question, CacheNodePtr> map_;
-
- // Move a node to the front of the LRU list.
- void promote(CacheNodePtr node);
-
- // Remove a node from the cache.
- void remove(ConstCacheNodePtr node);
-
- // Insert a node into the cache (called by both cache() and ncache())
- void insert(CacheNodePtr node);
-};
-
-// HotCacheImpl constructor
-HotCacheImpl::HotCacheImpl(int slots, bool enabled) :
- enabled_(enabled), slots_(slots), count_(0)
-{
- LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_CACHE_CREATE);
-}
-
-// Insert a cache node into the cache
-inline void
-HotCacheImpl::insert(const CacheNodePtr node) {
- LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_CACHE_INSERT).
- arg(node->getNodeName());
- std::map<Question, CacheNodePtr>::const_iterator iter;
- iter = map_.find(node->question);
- if (iter != map_.end()) {
- CacheNodePtr old = iter->second;
- if (old && old->isValid()) {
- LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_CACHE_OLD_FOUND)
- .arg(node->getNodeName());
- remove(old);
- }
- }
-
- lru_.push_front(node);
- node->lru_entry_ = lru_.begin();
-
- map_[node->question] = node;
- ++count_;
-
- if (slots_ != 0 && count_ > slots_) {
- LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_CACHE_FULL);
- remove(lru_.back());
- }
-}
-
-// Promote a node to the head of the LRU list
-void
-HotCacheImpl::promote(CacheNodePtr node) {
- if (!node) {
- return;
- }
- if (node->lru_entry_ == lru_.begin()) {
- return;
- }
- lru_.splice(lru_.begin(), lru_, node->lru_entry_); // move node to front
- node->lru_entry_ = lru_.begin();
-}
-
-// Remove a node from the LRU list and the map
-void
-HotCacheImpl::remove(ConstCacheNodePtr node) {
- LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_CACHE_REMOVE).
- arg(node->getNodeName());
- lru_.erase(node->lru_entry_);
- map_.erase(node->question);
- --count_;
-}
-
-// HotCache constructor
-HotCache::HotCache(const int slots) {
- impl_ = new HotCacheImpl(slots, true);
-}
-
-// HotCache destructor
-HotCache::~HotCache() {
- LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_CACHE_DESTROY);
- delete impl_;
-}
-
-// Add a positive entry to the cache
-void
-HotCache::addPositive(RRsetPtr rrset, const uint32_t flags,
- const time_t lifespan)
-{
- if (!impl_->enabled_) {
- return;
- }
-
- impl_->insert(CacheNodePtr(new CacheNode(rrset, flags, lifespan)));
-}
-
-// Add a negative entry to the cache
-void
-HotCache::addNegative(const Name& name, const RRClass &rrclass,
- const RRType& rrtype, const uint32_t flags,
- const time_t lifespan)
-{
- if (!impl_->enabled_) {
- return;
- }
-
- if (rrtype == RRType::ANY() || rrclass == RRClass::ANY()) {
- return;
- }
-
- impl_->insert(CacheNodePtr(new CacheNode(name, rrclass, rrtype,
- flags, lifespan)));
-}
-
-// Try to retrieve an entry from the cache, returning true if
-// it was found and valid.
-bool
-HotCache::retrieve(const Name& n, const RRClass& c, const RRType& t,
- RRsetPtr& rrset, uint32_t& flags)
-{
- if (!impl_->enabled_) {
- return (false);
- }
-
- std::map<Question, CacheNodePtr>::const_iterator iter;
- iter = impl_->map_.find(Question(n, c, t));
- if (iter == impl_->map_.end()) {
- LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_CACHE_NOT_FOUND).arg(n);
- return (false);
- }
-
- CacheNodePtr node = iter->second;
-
- if (node->isValid()) {
- LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_CACHE_FOUND).arg(n);
- impl_->promote(node);
- rrset = node->getRRset();
- flags = node->getFlags();
- return (true);
- }
-
- LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_CACHE_EXPIRED).arg(n);
- impl_->remove(node);
- return (false);
-}
-
-// Set the number of slots in the cache.
-void
-HotCache::setSlots(const int slots) {
- impl_->slots_ = slots;
-
- if (!impl_->enabled_) {
- return;
- }
-
- logger.info(DATASRC_CACHE_SLOTS).arg(slots).arg(max(0, impl_->count_ -
- slots));
-
- while (impl_->slots_ != 0 && impl_->count_ > impl_->slots_) {
- impl_->remove(impl_->lru_.back());
- }
-}
-
-// Return the number of slots in the cache
-int
-HotCache::getSlots() const {
- return (impl_->slots_);
-}
-
-/// Enable or disable the cache
-void
-HotCache::setEnabled(const bool e) {
- impl_->enabled_ = e;
- if (e) {
- logger.info(DATASRC_CACHE_ENABLE);
- } else {
- logger.info(DATASRC_CACHE_DISABLE);
- }
-}
-
-/// Indicate whether the cache is enabled
-bool
-HotCache::getEnabled() const {
- return (impl_->enabled_);
-}
-
-// Return the number of entries in the cache
-int
-HotCache::getCount() const {
- return (impl_->count_);
-}
-
-}
-}
diff --git a/src/lib/datasrc/cache.h b/src/lib/datasrc/cache.h
deleted file mode 100644
index 054912c..0000000
--- a/src/lib/datasrc/cache.h
+++ /dev/null
@@ -1,223 +0,0 @@
-// Copyright (C) 2010 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 __CACHE_H
-#define __CACHE_H
-
-#include <time.h>
-
-#include <boost/shared_ptr.hpp>
-
-#include <dns/rrset.h>
-
-namespace isc {
-namespace dns {
-class Name;
-class RRClass;
-class RRType;
-}
-
-namespace datasrc {
-
-class CacheNode;
-typedef boost::shared_ptr<CacheNode> CacheNodePtr;
-typedef boost::shared_ptr<const CacheNode> ConstCacheNodePtr;
-
-class HotCacheImpl;
-
-/// \brief A \c HotCache is a hot-spot cache for one or more data sources.
-///
-/// A \c HotCache must be instantiated prior to creating a \c Query.
-/// The same instance should be passed to the constructor for all queries,
-/// so that all of them will be using the same cache.
-///
-/// The cache may contain positive or negative entries, indicating
-/// whether the data does or does not exist in the underlying data
-/// source. Entries have a fixed and limited lifespan (currently
-/// set to 30 seconds, see LIFESPAN_ below). If a cache entry is
-/// found which has exceeded its lifespan, it will not be returned
-/// to the caller--exactly as if it had not been found.
-///
-/// The current 30 second cache entry lifespan is experimental. A longer
-/// lifespan would improve performance somewhat; however, longer-lived
-/// cache entries are more likely to be incorrect in the event that
-/// the underlying data source had been updated. Depending on the
-/// frequency of queries and the frequency of updates, longer or
-/// shorter lifespans may be desirable -- it's even possible
-/// we may want the lifespan to be set differently depending on
-/// the zone or the data source (i.e., with an infinite lifespan
-/// for cached data from a static data source). Additional benchmarking
-/// and analysis will be needed for this.
-///
-/// The cache may be configured with a number of available slots for
-/// for entries. When set to a nonzero value, no more than that number
-/// of entries can exist in the cache. If more entries are inserted,
-/// old entries will be dropped in "least recently used" order. If
-/// set to zero, the cache size is unlimited. The current default is
-/// based on one thousand queries per second, times the number of seconds
-/// in the cache lifespan: 30,000 slots.
-///
-/// Notes to developers: The current implementation of HotCache uses
-/// a std::map (keyed by isc::dns::Question) to locate nodes, so access
-/// will generally be in O(log n) time. (XXX: This might be faster if a
-/// hash table were used instead.)
-///
-/// A linked list is also maintained to keep track of recent accesses
-/// to cache entries; each time an entry is accessed, it is moved to the
-/// head of the list; when entries need to be removed, they are taken
-/// from the tail of the list. This operation is not locked. BIND 10
-/// does not currently use threads, but if it ever does (or if libdatasrc
-/// is ever used by a threaded application), this will need to be
-/// revisited.
-class HotCache {
-private:
- /// \name Static definitions
- //@{
- /// \brief Default validity period for cache entries
- static const int LIFESPAN_ = 30;
-
- /// \brief Default number of slots in cache
- static const int SLOTS_ = 1000 * LIFESPAN_;
- //@}
-
- /// \name Constructors, Assignment Operator and Destructor.
- ///
- /// Note: The copy constructor and the assignment operator are intentionally
- /// defined as private.
- //@{
- HotCache(const HotCache& source);
- HotCache& operator=(const HotCache& source);
-
-public:
- /// \brief Constructor for HotCache
- ///
- /// \param slots The number of slots available in the cache.
- HotCache(const int slots = SLOTS_);
-
- /// \brief Destructor for HotCache
- ~HotCache();
- //@}
-
- /// \name Cache Manipulation Methods
- //@{
- /// \brief Enter a positive cache entry.
- ///
- /// If an entry already exists in the cache which matches the
- /// name/class/type of the RRset being cached, then the old entry
- /// is removed before the the new one is inserted. (XXX: This is
- /// currently slightly inefficient; it would be quicker to keep the
- /// existing node and simply update the rrset, flags, and lifespan.)
- ///
- /// \param rrset The \c RRset to cache.
- /// \param flags The query response flags returned from the low-level
- /// data source when this \c RRset was looked up.
- /// \param lifespan How long the cache node is to be considered valid;
- /// defaulting to 30 seconds.
- void addPositive(isc::dns::RRsetPtr rrset,
- uint32_t flags,
- time_t lifespan = LIFESPAN_);
-
- /// \brief Enter a negative cache entry.
- ///
- /// In the case of a negative cache entry there is no \c RRset to
- /// cache, so instead a null \c RRsetPtr will be stored. Since the
- /// name, class, and type cannot be retrieved from an \c RRset, they
- /// must be specified in the parameters.
- ///
- /// If an entry already exists in the cache which matches the
- /// specified name/class/type, then the old entry is removed
- /// before the the new one is inserted. (XXX: As noted in the comments
- /// for addPositive(), this is currently slightly inefficient.)
- ///
- /// \param name Query name
- /// \param rrclass Query class
- /// \param rrtype Query type
- /// \param flags Query response flags returned from the low-level
- /// data source, indicating why this lookup failed (name not found,
- /// type not found, etc).
- /// \param lifespan How long the cache node is to be considered valid;
- /// defaulting to 30 seconds.
- ///
- /// Note: 'rrclass' and 'rrtype' must refer to a specific class and
- /// type; it is not meaningful to cache type or class ANY. Currently,
- /// this condition is silently ignored.
- void addNegative(const isc::dns::Name& name,
- const isc::dns::RRClass& rrclass,
- const isc::dns::RRType& rrtype,
- uint32_t flags,
- time_t lifespan = LIFESPAN_);
-
- /// \brief Retrieve (and promote) a record from the cache
- ///
- /// Retrieves a record from the cache matching the given
- /// query-tuple. Returns true if one is found. If it is a
- /// positive cache entry, then 'rrset' is set to the cached
- /// RRset. For both positive and negative cache entries, 'flags'
- /// is set to the query response flags. The cache entry is
- /// then promoted to the head of the LRU queue. (NOTE: Because
- /// of this, "retrieve" cannot be implemented as a const method.)
- ///
- /// \param qname The query name
- /// \param qclass The query class
- /// \param qtype The query type
- /// \param rrset Returns the RRset found, if any, to the caller
- /// \param flags Returns the flags, if any, to the caller
- ///
- /// \return \c bool, true if data was found in the cache, false if not.
- bool retrieve(const isc::dns::Name& qname,
- const isc::dns::RRClass& qclass,
- const isc::dns::RRType& qtype,
- isc::dns::RRsetPtr& rrset,
- uint32_t& flags);
- //@}
-
- /// \name Getter and Setter Methods
- //@{
- /// \brief Sets the number of slots in the cache.
- ///
- /// If slots is set to zero, the cache can grow without any imposed
- /// limit. If slots is to set a lower number than the cache currently
- /// contains, then the least recently used records will be purged from
- /// the cache until the total number of items in the cache equals slots.
- void setSlots(int slots);
-
- /// \brief Returns the number of slots in the cache.
- int getSlots() const;
-
- /// \brief Enable or disable the cache
- void setEnabled(bool e);
-
- /// \brief Indicate whether the cache is enabled
- bool getEnabled() const;
-
- /// \brief Returns the number of nodes currently stored in the cache.
- ///
- /// Note that this doesn't indicate how many nodes are still valid;
- /// some may have expired.
- int getCount() const;
- //@}
-
-private:
- /// \brief Hidden implementation details
- HotCacheImpl* impl_;
-};
-
-}
-}
-
-#endif
-
-// Local Variables:
-// mode: c++
-// End:
diff --git a/src/lib/datasrc/client_list.cc b/src/lib/datasrc/client_list.cc
index 3c0ea19..865a1ce 100644
--- a/src/lib/datasrc/client_list.cc
+++ b/src/lib/datasrc/client_list.cc
@@ -12,10 +12,12 @@
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
+#include <util/memory_segment_local.h>
+
#include "client_list.h"
#include "client.h"
#include "factory.h"
-#include "memory_datasrc.h"
+#include "memory/memory_client.h"
#include "logger.h"
#include <dns/masterload.h>
@@ -25,32 +27,58 @@
using namespace isc::data;
using namespace isc::dns;
using namespace std;
+using isc::util::MemorySegment;
using boost::lexical_cast;
using boost::shared_ptr;
using boost::dynamic_pointer_cast;
+using isc::datasrc::memory::InMemoryClient;
namespace isc {
namespace datasrc {
ConfigurableClientList::DataSourceInfo::DataSourceInfo(
DataSourceClient* data_src_client,
- const DataSourceClientContainerPtr& container, bool has_cache) :
+ const DataSourceClientContainerPtr& container, bool has_cache,
+ const RRClass& rrclass, MemorySegment& mem_sgmt) :
data_src_client_(data_src_client),
container_(container)
{
if (has_cache) {
- cache_.reset(new InMemoryClient);
+ cache_.reset(new InMemoryClient(mem_sgmt, rrclass));
}
}
-ConfigurableClientList::DataSourceInfo::DataSourceInfo(bool has_cache) :
+ConfigurableClientList::DataSourceInfo::DataSourceInfo(
+ const RRClass& rrclass, MemorySegment& mem_sgmt, bool has_cache) :
data_src_client_(NULL)
{
if (has_cache) {
- cache_.reset(new InMemoryClient);
+ cache_.reset(new InMemoryClient(mem_sgmt, rrclass));
}
}
+const DataSourceClient*
+ConfigurableClientList::DataSourceInfo::getCacheClient() const {
+ return (cache_.get());
+}
+
+ConfigurableClientList::ConfigurableClientList(const RRClass& rrclass) :
+ rrclass_(rrclass),
+ mem_sgmt_(new util::MemorySegmentLocal),
+ configuration_(new isc::data::ListElement),
+ allow_cache_(false)
+{}
+
+ConfigurableClientList::~ConfigurableClientList() {
+ // Explicitly clear the contained data source clients, and check memory
+ // leak. assert() (with abort on failure) may be too harsh, but
+ // it's probably better to find more leaks initially. Once it's stabilized
+ // we should probably revisit it.
+
+ data_sources_.clear();
+ assert(mem_sgmt_->allMemoryDeallocated());
+}
+
void
ConfigurableClientList::configure(const ConstElementPtr& config,
bool allow_cache)
@@ -98,14 +126,16 @@ ConfigurableClientList::configure(const ConstElementPtr& config,
isc_throw(ConfigurationError, "The cache must be enabled "
"for the MasterFiles type");
}
- new_data_sources.push_back(DataSourceInfo(true));
+ new_data_sources.push_back(DataSourceInfo(rrclass_, *mem_sgmt_,
+ true));
} else {
// Ask the factory to create the data source for us
const DataSourcePair ds(this->getDataSourceClient(type,
paramConf));
// And put it into the vector
new_data_sources.push_back(DataSourceInfo(ds.first, ds.second,
- want_cache));
+ want_cache, rrclass_,
+ *mem_sgmt_));
}
if (want_cache) {
@@ -141,13 +171,10 @@ ConfigurableClientList::configure(const ConstElementPtr& config,
for (vector<string>::const_iterator it(zones_origins.begin());
it != zones_origins.end(); ++it) {
const Name origin(*it);
- shared_ptr<InMemoryZoneFinder>
- finder(new
- InMemoryZoneFinder(rrclass_, origin));
if (type == "MasterFiles") {
try {
- finder->load(paramConf->get(*it)->stringValue());
- cache->addZone(finder);
+ cache->load(origin,
+ paramConf->get(*it)->stringValue());
} catch (const isc::dns::MasterLoadError& mle) {
LOG_ERROR(logger, DATASRC_MASTERLOAD_ERROR)
.arg(mle.what());
@@ -165,8 +192,7 @@ ConfigurableClientList::configure(const ConstElementPtr& config,
isc_throw(isc::Unexpected, "Got NULL iterator "
"for zone " << origin);
}
- finder->load(*iterator);
- cache->addZone(finder);
+ cache->load(origin, *iterator);
}
}
}
@@ -324,14 +350,11 @@ ConfigurableClientList::reload(const Name& name) {
}
// Try to convert the finder to in-memory one. If it is the cache,
// it should work.
- shared_ptr<InMemoryZoneFinder>
- finder(dynamic_pointer_cast<InMemoryZoneFinder>(result.finder));
- const DataSourceInfo* info(result.info);
// It is of a different type or there's no cache.
- if (!info->cache_ || !finder) {
+ if (!result.info->cache_) {
return (ZONE_NOT_CACHED);
}
- DataSourceClient* client(info->data_src_client_);
+ DataSourceClient* client(result.info->data_src_client_);
if (client) {
// Now do the final reload. If it does not exist in client,
// DataSourceError is thrown, which is exactly the result what we
@@ -340,15 +363,15 @@ ConfigurableClientList::reload(const Name& name) {
if (!iterator) {
isc_throw(isc::Unexpected, "Null iterator from " << name);
}
- finder->load(*iterator);
+ result.info->cache_->load(name, *iterator);
} else {
// The MasterFiles special case
- const string filename(finder->getFileName());
+ const string filename(result.info->cache_->getFileName(name));
if (filename.empty()) {
isc_throw(isc::Unexpected, "Confused about missing both filename "
"and data source");
}
- finder->load(filename);
+ result.info->cache_->load(name, filename);
}
return (ZONE_RELOADED);
}
diff --git a/src/lib/datasrc/client_list.h b/src/lib/datasrc/client_list.h
index 0dd522f..61544ef 100644
--- a/src/lib/datasrc/client_list.h
+++ b/src/lib/datasrc/client_list.h
@@ -15,6 +15,8 @@
#ifndef DATASRC_CONTAINER_H
#define DATASRC_CONTAINER_H
+#include <util/memory_segment.h>
+
#include <dns/name.h>
#include <dns/rrclass.h>
#include <cc/data.h>
@@ -22,6 +24,7 @@
#include <vector>
#include <boost/shared_ptr.hpp>
+#include <boost/scoped_ptr.hpp>
#include <boost/noncopyable.hpp>
namespace isc {
@@ -34,7 +37,13 @@ typedef boost::shared_ptr<DataSourceClient> DataSourceClientPtr;
class DataSourceClientContainer;
typedef boost::shared_ptr<DataSourceClientContainer>
DataSourceClientContainerPtr;
+
+// XXX: it's better to even hide the existence of the "memory" namespace.
+// We should probably consider pimpl for details of ConfigurableClientList
+// and hide real definitions except for itself and tests.
+namespace memory {
class InMemoryClient;
+}
/// \brief The list of data source clients.
///
@@ -209,11 +218,11 @@ public:
/// \brief Constructor
///
/// \param rrclass For which class the list should work.
- ConfigurableClientList(const isc::dns::RRClass &rrclass) :
- rrclass_(rrclass),
- configuration_(new isc::data::ListElement),
- allow_cache_(false)
- {}
+ ConfigurableClientList(const isc::dns::RRClass& rrclass);
+
+ /// \brief Destructor
+ virtual ~ConfigurableClientList();
+
/// \brief Exception thrown when there's an error in configuration.
class ConfigurationError : public Exception {
public:
@@ -290,24 +299,27 @@ public:
/// \todo The content yet to be defined.
struct DataSourceInfo {
// Plays a role of default constructor too (for vector)
- DataSourceInfo(bool has_cache = false);
+ DataSourceInfo(const dns::RRClass& rrclass,
+ util::MemorySegment& mem_sgmt,
+ bool has_cache = false);
DataSourceInfo(DataSourceClient* data_src_client,
const DataSourceClientContainerPtr& container,
- bool has_cache);
+ bool has_cache, const dns::RRClass& rrclass,
+ util::MemorySegment& mem_sgmt);
DataSourceClient* data_src_client_;
DataSourceClientContainerPtr container_;
- boost::shared_ptr<InMemoryClient> cache_;
+
+ // Accessor to cache_ in the form of DataSourceClient, hiding
+ // the existence of InMemoryClient as much as possible. We should
+ // really consider cleaner abstraction, but for now it works.
+ // This is also only intended to be used in auth unit tests right now.
+ // No other applications or tests may use it.
+ const DataSourceClient* getCacheClient() const;
+ boost::shared_ptr<memory::InMemoryClient> cache_;
};
/// \brief The collection of data sources.
typedef std::vector<DataSourceInfo> DataSources;
-protected:
- /// \brief The data sources held here.
- ///
- /// All our data sources are stored here. It is protected to let the
- /// tests in. You should consider it private if you ever want to
- /// derive this class (which is not really recommended anyway).
- DataSources data_sources_;
/// \brief Convenience type alias.
///
@@ -357,10 +369,26 @@ private:
void findInternal(MutableResult& result, const dns::Name& name,
bool want_exact_match, bool want_finder) const;
const isc::dns::RRClass rrclass_;
+
+ /// \brief Memory segment for in-memory cache.
+ ///
+ /// Note that this must be placed before data_sources_ so it won't be
+ /// destroyed before the built objects in the destructor.
+ boost::scoped_ptr<util::MemorySegment> mem_sgmt_;
+
/// \brief Currently active configuration.
isc::data::ConstElementPtr configuration_;
+
/// \brief The last set value of allow_cache.
bool allow_cache_;
+
+protected:
+ /// \brief The data sources held here.
+ ///
+ /// All our data sources are stored here. It is protected to let the
+ /// tests in. You should consider it private if you ever want to
+ /// derive this class (which is not really recommended anyway).
+ DataSources data_sources_;
};
} // namespace datasrc
diff --git a/src/lib/datasrc/data_source.cc b/src/lib/datasrc/data_source.cc
deleted file mode 100644
index 0e5b3a3..0000000
--- a/src/lib/datasrc/data_source.cc
+++ /dev/null
@@ -1,1434 +0,0 @@
-// Copyright (C) 2010 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 <cassert>
-#include <iomanip>
-#include <iostream>
-#include <vector>
-
-#include <boost/shared_ptr.hpp>
-#include <boost/foreach.hpp>
-
-#include <datasrc/cache.h>
-#include <datasrc/data_source.h>
-#include <datasrc/query.h>
-#include <datasrc/logger.h>
-
-#include <util/encode/base32hex.h>
-#include <util/hash/sha1.h>
-#include <util/buffer.h>
-
-#include <dns/message.h>
-#include <dns/name.h>
-#include <dns/rcode.h>
-#include <dns/rdataclass.h>
-#include <dns/rrset.h>
-#include <dns/rrsetlist.h>
-
-#include <cc/data.h>
-
-#define RETERR(x) do { \
- DataSrc::Result r = (x); \
- if (r != DataSrc::SUCCESS) \
- return (r); \
- } while (0)
-
-using namespace std;
-using namespace isc::util;
-using namespace isc::util::encode;
-using namespace isc::util::hash;
-using namespace isc::dns;
-using namespace isc::dns::rdata;
-
-namespace {
-
-struct MatchRRsetForType {
- MatchRRsetForType(const RRType rrtype) : rrtype_(rrtype) {}
- bool operator()(RRsetPtr rrset) {
- return (rrset->getType() == rrtype_);
- }
- const RRType rrtype_;
-};
-
-// This is a helper to retrieve a specified RR type of RRset from RRsetList.
-// In our case the data source search logic should ensure that the class is
-// valid. We use this find logic of our own so that we can support both
-// specific RR class queries (normal case) and class ANY queries.
-RRsetPtr
-findRRsetFromList(RRsetList& list, const RRType rrtype) {
- RRsetList::iterator it(find_if(list.begin(), list.end(),
- MatchRRsetForType(rrtype)));
- return (it != list.end() ? *it : RRsetPtr());
-}
-}
-
-namespace isc {
-namespace datasrc {
-
-typedef boost::shared_ptr<const Nsec3Param> ConstNsec3ParamPtr;
-
-class ZoneInfo {
-public:
- ZoneInfo(DataSrc* ts,
- const isc::dns::Name& n,
- const isc::dns::RRClass& c,
- const isc::dns::RRType& t = isc::dns::RRType::ANY()) :
- top_source_(ts),
- dsm_(((t == RRType::DS() && n.getLabelCount() != 1)
- ? n.split(1, n.getLabelCount() - 1) : n),
- c)
- {}
-
- const Name* getEnclosingZone() {
- if (dsm_.getEnclosingZone() == NULL) {
- top_source_->findClosestEnclosure(dsm_);
- }
- return (dsm_.getEnclosingZone());
- }
-
- const DataSrc* getDataSource() {
- if (dsm_.getDataSource() == NULL) {
- top_source_->findClosestEnclosure(dsm_);
- }
- return (dsm_.getDataSource());
- }
-
-private:
- const DataSrc* top_source_;
- DataSrcMatch dsm_;
-};
-
-// Add a task to the query task queue to look up additional data
-// (i.e., address records for the names included in NS or MX records)
-void
-getAdditional(Query& q, ConstRRsetPtr rrset) {
- if (!q.wantAdditional()) {
- return;
- }
-
- RdataIteratorPtr it = rrset->getRdataIterator();
- for (; !it->isLast(); it->next()) {
- const Rdata& rd(it->getCurrent());
- if (rrset->getType() == RRType::NS()) {
- const generic::NS& ns = dynamic_cast<const generic::NS&>(rd);
- LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_QUERY_GET_NS_ADDITIONAL).
- arg(ns.getNSName()).arg(rrset->getName());
- q.tasks().push(QueryTaskPtr(
- new QueryTask(q, ns.getNSName(),
- Message::SECTION_ADDITIONAL,
- QueryTask::GLUE_QUERY,
- QueryTask::GETADDITIONAL)));
- } else if (rrset->getType() == RRType::MX()) {
- const generic::MX& mx = dynamic_cast<const generic::MX&>(rd);
- LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_QUERY_GET_MX_ADDITIONAL).
- arg(mx.getMXName()).arg(rrset->getName());
- q.tasks().push(QueryTaskPtr(
- new QueryTask(q, mx.getMXName(),
- Message::SECTION_ADDITIONAL,
- QueryTask::NOGLUE_QUERY,
- QueryTask::GETADDITIONAL)));
- }
- }
-}
-
-// Synthesize a CNAME answer, for the benefit of clients that don't
-// understand DNAME
-void
-synthesizeCname(QueryTaskPtr task, RRsetPtr rrset, RRsetList& target) {
- LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_QUERY_SYNTH_CNAME).
- arg(rrset->getName());
- RdataIteratorPtr it = rrset->getRdataIterator();
-
- // More than one DNAME RR in the RRset is illegal, so we only have
- // to process the first one.
- if (it->isLast()) {
- logger.error(DATASRC_QUERY_EMPTY_DNAME).arg(rrset->getName());
- return;
- }
-
- const Rdata& rd(it->getCurrent());
- const generic::DNAME& dname = dynamic_cast<const generic::DNAME&>(rd);
- const Name& dname_target(dname.getDname());
-
- RRsetPtr cname(new RRset(task->qname, rrset->getClass(), RRType::CNAME(),
- rrset->getTTL()));
-
- const int qnlen = task->qname.getLabelCount();
- const int dnlen = rrset->getName().getLabelCount();
- assert(qnlen > dnlen);
- const Name& prefix(task->qname.split(0, qnlen - dnlen));
- cname->addRdata(generic::CNAME(prefix.concatenate(dname_target)));
-
- target.addRRset(cname);
-}
-
-// Add a task to the query task queue to look up the data pointed
-// to by a CNAME record
-void
-chaseCname(Query& q, QueryTaskPtr task, RRsetPtr rrset) {
- LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_QUERY_FOLLOW_CNAME).
- arg(rrset->getName());
- RdataIteratorPtr it = rrset->getRdataIterator();
-
- // More than one CNAME RR in the RRset is illegal, so we only have
- // to process the first one.
- if (it->isLast()) {
- logger.error(DATASRC_QUERY_EMPTY_CNAME).arg(rrset->getName());
- return;
- }
-
- // Stop chasing CNAMES after 16 lookups, to prevent loops
- if (q.tooMany()) {
- logger.error(DATASRC_QUERY_TOO_MANY_CNAMES).arg(rrset->getName());
- return;
- }
-
- q.tasks().push(QueryTaskPtr(
- new QueryTask(q, dynamic_cast<const generic::CNAME&>
- (it->getCurrent()).getCname(),
- task->qtype, Message::SECTION_ANSWER,
- QueryTask::FOLLOWCNAME)));
-}
-
-// Check the cache for data which can answer the current query task.
-bool
-checkCache(QueryTask& task, RRsetList& target) {
- LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_QUERY_CHECK_CACHE).
- arg(task.qname).arg(task.qtype);
- HotCache& cache = task.q.getCache();
- RRsetList rrsets;
- RRsetPtr rrset;
- int count = 0;
- uint32_t flags = 0, cflags = 0;
- bool hit = false, found = false;
-
- switch (task.op) {
- case QueryTask::SIMPLE_QUERY: // Find exact RRset
- // ANY queries must be handled by the low-level data source,
- // or the results won't be guaranteed to be complete
- if (task.qtype == RRType::ANY() || task.qclass == RRClass::ANY()) {
- LOG_DEBUG(logger, DBG_TRACE_DATA,
- DATASRC_QUERY_NO_CACHE_ANY_SIMPLE).arg(task.qname).
- arg(task.qtype).arg(task.qclass);
- break;
- }
-
- hit = cache.retrieve(task.qname, task.qclass, task.qtype, rrset, flags);
- if (hit) {
- if (rrset) {
- rrsets.addRRset(rrset);
- target.append(rrsets);
- }
-
- // Reset the referral flag and treat CNAME as "not found".
- // This emulates the behavior of the sqlite3 data source.
- // XXX: this is not ideal in that the responsibility for handling
- // operation specific cases is spread over various classes at
- // different abstraction levels. For longer terms we should
- // revisit the whole datasource/query design, and clarify this
- // point better.
- flags &= ~DataSrc::REFERRAL;
- if ((flags & DataSrc::CNAME_FOUND) != 0) {
- flags &= ~DataSrc::CNAME_FOUND;
- flags |= DataSrc::TYPE_NOT_FOUND;
- }
- task.flags = flags;
- return (true);
- }
- break;
-
- case QueryTask::AUTH_QUERY: // Find exact RRset or CNAME
- if (task.qtype == RRType::ANY() || task.qclass == RRClass::ANY()) {
- LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_QUERY_NO_CACHE_ANY_AUTH).
- arg(task.qname).arg(task.qtype).arg(task.qclass);
- break;
- }
-
- hit = cache.retrieve(task.qname, task.qclass, task.qtype, rrset, flags);
- if (!hit || !rrset || (flags & DataSrc::CNAME_FOUND) != 0) {
- hit = cache.retrieve(task.qname, task.qclass, RRType::CNAME(),
- rrset, flags);
- if (!rrset) {
- // If we don't have a positive cache, forget it; otherwise the
- // intermediate result may confuse the subsequent processing.
- hit = false;
- }
- }
-
- if (hit) {
- if (rrset) {
- rrsets.addRRset(rrset);
- target.append(rrsets);
- }
- task.flags = flags;
- return (true);
- }
- break;
-
- case QueryTask::GLUE_QUERY: // Find addresses
- case QueryTask::NOGLUE_QUERY:
- // (XXX: need to figure out how to deal with noglue case)
- flags = 0;
-
- hit = cache.retrieve(task.qname, task.qclass, RRType::A(),
- rrset, cflags);
- if (hit) {
- flags |= cflags;
- ++count;
- if (rrset) {
- rrsets.addRRset(rrset);
- found = true;
- }
- }
-
- hit = cache.retrieve(task.qname, task.qclass, RRType::AAAA(),
- rrset, flags);
- if (hit) {
- flags |= cflags;
- ++count;
- if (rrset) {
- rrsets.addRRset(rrset);
- found = true;
- }
- }
-
- if (count == 2) {
- if (found) {
- flags &= ~DataSrc::TYPE_NOT_FOUND;
- target.append(rrsets);
- }
- task.flags = flags;
- return (true);
- }
- break;
-
-
- case QueryTask::REF_QUERY: // Find NS, DS and/or DNAME
- flags = count = 0;
-
- hit = cache.retrieve(task.qname, task.qclass, RRType::NS(),
- rrset, cflags);
- if (hit) {
- flags |= cflags;
- ++count;
- if (rrset) {
- rrsets.addRRset(rrset);
- found = true;
- }
- }
-
- hit = cache.retrieve(task.qname, task.qclass, RRType::DS(),
- rrset, flags);
- if (hit) {
- flags |= cflags;
- ++count;
- if (rrset) {
- rrsets.addRRset(rrset);
- found = true;
- }
- }
-
- hit = cache.retrieve(task.qname, task.qclass, RRType::DNAME(),
- rrset, flags);
- if (hit) {
- flags |= cflags;
- ++count;
- if (rrset) {
- rrsets.addRRset(rrset);
- found = true;
- }
- }
-
- if (count == 3) {
- if (found) {
- flags &= ~DataSrc::TYPE_NOT_FOUND;
- flags &= DataSrc::REFERRAL;
- target.append(rrsets);
- }
- task.flags = flags;
- return (true);
- }
- break;
- }
-
- return (false);
-}
-
-// Carry out the query specified in a QueryTask object
-DataSrc::Result
-doQueryTask(QueryTask& task, ZoneInfo& zoneinfo, RRsetList& target) {
- HotCache& cache = task.q.getCache();
- RRsetPtr rrset;
- LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_DO_QUERY).arg(task.qname).
- arg(task.qtype);
-
- // First off, make sure at least we have a matching zone in some data
- // source. We must do this before checking the cache, because it can
- // happen that the matching zone has been removed after an RRset of that
- // zone is cached. Such inconsistency will cause various problems,
- // including a crash.
- const DataSrc* ds = zoneinfo.getDataSource();
- const Name* const zonename = zoneinfo.getEnclosingZone();
- if (ds == NULL) {
- task.flags |= DataSrc::NO_SUCH_ZONE;
- LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_QUERY_NO_ZONE).
- arg(task.qname).arg(task.qclass);
- return (DataSrc::SUCCESS);
- }
-
- // Then check the cache for matching data
- if (checkCache(task, target)) {
- LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_QUERY_CACHED).
- arg(task.qname).arg(task.qtype);
- return (DataSrc::SUCCESS);
- }
-
- // Requested data weren't in the cache (or were, but had expired),
- // so now we proceed with the low-level data source lookup, and cache
- // whatever we find.
-
- DataSrc::Result result;
- switch (task.op) {
- case QueryTask::SIMPLE_QUERY:
- LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_QUERY_IS_SIMPLE).
- arg(task.qname).arg(task.qtype);
- result = ds->findExactRRset(task.qname, task.qclass, task.qtype,
- target, task.flags, zonename);
-
- if (result != DataSrc::SUCCESS) {
- logger.error(DATASRC_QUERY_SIMPLE_FAIL).arg(result);
- return (result);
- }
-
- if (task.qclass == RRClass::ANY()) {
- // XXX: Currently, RRsetList::findRRset() doesn't handle
- // ANY queries, and without that we can't cache the results,
- // so we just return in that case.
- return (result);
- }
-
- if (task.flags == 0) {
- rrset = target.findRRset(task.qtype, task.qclass);
- assert(rrset);
- cache.addPositive(rrset, task.flags);
- } else {
- cache.addNegative(task.qname, task.qclass, task.qtype, task.flags);
- }
-
- return (result);
-
- case QueryTask::AUTH_QUERY:
- LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_QUERY_IS_AUTH).
- arg(task.qname).arg(task.qtype);
- result = ds->findRRset(task.qname, task.qclass, task.qtype,
- target, task.flags, zonename);
-
- if (result != DataSrc::SUCCESS) {
- logger.error(DATASRC_QUERY_AUTH_FAIL).arg(result);
- return (result);
- }
-
- if (task.qclass == RRClass::ANY()) {
- return (result);
- }
-
- if (task.qtype == RRType::ANY()) {
- BOOST_FOREACH(RRsetPtr rr, target) {
- cache.addPositive(rr, task.flags);
- }
- } else if ((task.flags & DataSrc::CNAME_FOUND) != 0) {
- cache.addNegative(task.qname, task.qclass, task.qtype, task.flags);
- rrset = target.findRRset(RRType::CNAME(), task.qclass);
- assert(rrset);
- cache.addPositive(rrset, task.flags);
- } else if ((task.flags & DataSrc::DATA_NOT_FOUND) == 0) {
- if (task.qtype != RRType::CNAME()) {
- cache.addNegative(task.qname, task.qclass, RRType::CNAME(),
- task.flags);
- }
- rrset = target.findRRset(task.qtype, task.qclass);
- assert(rrset);
- cache.addPositive(rrset, task.flags);
- } else {
- cache.addNegative(task.qname, task.qclass, task.qtype, task.flags);
- }
-
- return (result);
-
- case QueryTask::GLUE_QUERY:
- case QueryTask::NOGLUE_QUERY:
- LOG_DEBUG(logger, DBG_TRACE_DATA, task.op == QueryTask::GLUE_QUERY ?
- DATASRC_QUERY_IS_GLUE : DATASRC_QUERY_IS_NOGLUE).
- arg(task.qname).arg(task.qtype);
- result = ds->findAddrs(task.qname, task.qclass, target,
- task.flags, zonename);
-
- if (result != DataSrc::SUCCESS) {
- logger.error(task.op == QueryTask::GLUE_QUERY ?
- DATASRC_QUERY_GLUE_FAIL : DATASRC_QUERY_NOGLUE_FAIL).
- arg(result);
- return (result);
- }
-
- if (task.qclass == RRClass::ANY()) {
- return (result);
- }
-
- rrset = target.findRRset(RRType::A(), task.qclass);
- if (rrset) {
- cache.addPositive(rrset, task.flags);
- } else {
- cache.addNegative(task.qname, task.qclass, RRType::A(), task.flags);
- }
-
- rrset = target.findRRset(RRType::AAAA(), task.qclass);
- if (rrset) {
- cache.addPositive(rrset, task.flags);
- } else {
- cache.addNegative(task.qname, task.qclass, RRType::AAAA(),
- task.flags);
- }
-
- return (result);
-
- case QueryTask::REF_QUERY:
- LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_QUERY_IS_REF).
- arg(task.qname).arg(task.qtype);
- result = ds->findReferral(task.qname, task.qclass, target,
- task.flags, zonename);
-
- if (result != DataSrc::SUCCESS) {
- logger.error(DATASRC_QUERY_REF_FAIL).arg(result);
- return (result);
- }
-
- if (task.qclass == RRClass::ANY()) {
- return (result);
- }
-
- rrset = target.findRRset(RRType::NS(), task.qclass);
- if (rrset) {
- cache.addPositive(rrset, task.flags);
- } else {
- cache.addNegative(task.qname, task.qclass, RRType::NS(),
- task.flags);
- }
- rrset = target.findRRset(RRType::DS(), task.qclass);
- if (rrset) {
- cache.addPositive(rrset, task.flags);
- } else {
- cache.addNegative(task.qname, task.qclass, RRType::DS(),
- task.flags);
- }
- rrset = target.findRRset(RRType::DNAME(), task.qclass);
- if (rrset) {
- cache.addPositive(rrset, task.flags);
- } else {
- cache.addNegative(task.qname, task.qclass, RRType::DNAME(),
- task.flags);
- }
-
- return (result);
- }
-
- // Not reached
- logger.error(DATASRC_QUERY_INVALID_OP);
- return (DataSrc::ERROR);
-}
-
-
-// Add an RRset (and its associated RRSIG) to a message section,
-// checking first to ensure that there isn't already an RRset with
-// the same name and type.
-inline void
-addToMessage(Query& q, const Message::Section sect, RRsetPtr rrset,
- bool no_dnssec = false)
-{
- LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_QUERY_ADD_RRSET).
- arg(rrset->getName()).arg(rrset->getType());
- Message& m = q.message();
- if (no_dnssec) {
- if (rrset->getType() == RRType::RRSIG() ||
- !m.hasRRset(sect, rrset->getName(), rrset->getClass(),
- rrset->getType())) {
- m.addRRset(sect, rrset);
- }
- } else {
- if (!m.hasRRset(sect, rrset->getName(), rrset->getClass(),
- rrset->getType())) {
- m.addRRset(sect, rrset);
- }
- }
-}
-
-// Copy referral information into the authority section of a message
-inline void
-copyAuth(Query& q, RRsetList& auth) {
- LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_QUERY_COPY_AUTH);
- BOOST_FOREACH(RRsetPtr rrset, auth) {
- if (rrset->getType() == RRType::DNAME()) {
- continue;
- }
- if (rrset->getType() == RRType::DS() && !q.wantDnssec()) {
- continue;
- }
- addToMessage(q, Message::SECTION_AUTHORITY, rrset);
- getAdditional(q, rrset);
- }
-}
-
-// Query for referrals (i.e., NS/DS or DNAME) at a given name
-inline bool
-refQuery(const Query& q, const Name& name, ZoneInfo& zoneinfo,
- RRsetList& target)
-{
- QueryTask newtask(q, name, QueryTask::REF_QUERY);
-
- if (doQueryTask(newtask, zoneinfo, target) != DataSrc::SUCCESS) {
- // Lookup failed
- return (false);
- }
-
- // Referral bit is expected, so clear it when checking flags
- if ((newtask.flags & ~DataSrc::REFERRAL) != 0) {
- return (false);
- }
-
- return (true);
-}
-
-// Match downward, from the zone apex to the query name, looking for
-// referrals. Note that we exclude the apex name and query name themselves;
-// they'll be handled in a normal lookup in the zone.
-inline bool
-hasDelegation(Query& q, QueryTaskPtr task, ZoneInfo& zoneinfo) {
- LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_QUERY_DELEGATION).
- arg(task->qname);
-
- const Name* const zonename = zoneinfo.getEnclosingZone();
- if (zonename == NULL) {
- if (task->state == QueryTask::GETANSWER) {
- q.message().setRcode(Rcode::REFUSED());
- }
- return (false);
- }
-
- const int diff = task->qname.getLabelCount() - zonename->getLabelCount();
- if (diff > 1) {
- bool found = false;
- RRsetList ref;
- for (int i = diff - 1; i > 0; --i) {
- const Name sub(task->qname.split(i));
- if (refQuery(q, sub, zoneinfo, ref)) {
- found = true;
- break;
- }
- }
-
- // Found a referral while getting additional data
- // for something other than NS; we skip it.
- if (found && task->op == QueryTask::NOGLUE_QUERY) {
- return (true);
- }
-
- // Found a referral while getting answer data;
- // send a delegation.
- if (found) {
- RRsetPtr r = findRRsetFromList(ref, RRType::DNAME());
- if (r != NULL) {
- RRsetList syn;
- addToMessage(q, Message::SECTION_ANSWER, r);
- q.message().setHeaderFlag(Message::HEADERFLAG_AA);
- synthesizeCname(task, r, syn);
- if (syn.size() == 1) {
- RRsetPtr cname_rrset = findRRsetFromList(syn,
- RRType::CNAME());
- addToMessage(q, Message::SECTION_ANSWER, cname_rrset);
- chaseCname(q, task, cname_rrset);
- return (true);
- }
- }
-
- copyAuth(q, ref);
- return (true);
- }
- }
-
- // We appear to have authoritative data; set the header
- // flag. (We may clear it later if we find a referral
- // at the actual qname node.)
- if (task->op == QueryTask::AUTH_QUERY &&
- task->state == QueryTask::GETANSWER) {
- q.message().setHeaderFlag(Message::HEADERFLAG_AA);
- }
-
- return (false);
-}
-
-inline DataSrc::Result
-addSOA(Query& q, ZoneInfo& zoneinfo) {
- RRsetList soa;
-
- const Name* const zonename = zoneinfo.getEnclosingZone();
- LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_QUERY_ADD_SOA).arg(*zonename);
- QueryTask newtask(q, *zonename, RRType::SOA(), QueryTask::SIMPLE_QUERY);
- RETERR(doQueryTask(newtask, zoneinfo, soa));
- if (newtask.flags != 0) {
- return (DataSrc::ERROR);
- }
-
- addToMessage(q, Message::SECTION_AUTHORITY,
- findRRsetFromList(soa, RRType::SOA()));
- return (DataSrc::SUCCESS);
-}
-
-inline DataSrc::Result
-addNSEC(Query& q, const Name& name, ZoneInfo& zoneinfo) {
- LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_QUERY_ADD_NSEC).arg(name);
- RRsetList nsec;
-
- QueryTask newtask(q, name, RRType::NSEC(), QueryTask::SIMPLE_QUERY);
- RETERR(doQueryTask(newtask, zoneinfo, nsec));
- if (newtask.flags == 0) {
- addToMessage(q, Message::SECTION_AUTHORITY,
- findRRsetFromList(nsec, RRType::NSEC()));
- }
-
- return (DataSrc::SUCCESS);
-}
-
-inline DataSrc::Result
-getNsec3(Query& q, ZoneInfo& zoneinfo, string& hash, RRsetPtr& target) {
- const DataSrc* ds = zoneinfo.getDataSource();
- const Name* const zonename = zoneinfo.getEnclosingZone();
- LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_QUERY_ADD_NSEC3).arg(*zonename);
-
- if (ds == NULL) {
- q.message().setRcode(Rcode::SERVFAIL());
- logger.error(DATASRC_QUERY_NO_DS_NSEC3).arg(*zonename);
- return (DataSrc::ERROR);
- }
-
- RRsetList rl;
- RETERR(ds->findCoveringNSEC3(*zonename, hash, rl));
- target = rl.findRRset(RRType::NSEC3(), q.qclass());
-
- return (DataSrc::SUCCESS);
-}
-
-ConstNsec3ParamPtr
-getNsec3Param(Query& q, ZoneInfo& zoneinfo) {
- DataSrc::Result result;
- RRsetList nsec3param;
-
- const Name* const zonename = zoneinfo.getEnclosingZone();
- QueryTask newtask(q, *zonename, RRType::NSEC3PARAM(),
- QueryTask::SIMPLE_QUERY);
- result = doQueryTask(newtask, zoneinfo, nsec3param);
- newtask.flags &= ~DataSrc::REFERRAL;
- if (result != DataSrc::SUCCESS || newtask.flags != 0) {
- return (ConstNsec3ParamPtr());
- }
-
- RRsetPtr rrset = nsec3param.findRRset(RRType::NSEC3PARAM(), q.qclass());
- if (!rrset) {
- return (ConstNsec3ParamPtr());
- }
-
- // XXX: currently only one NSEC3 chain per zone is supported;
- // we will need to revisit this.
- RdataIteratorPtr it = rrset->getRdataIterator();
- if (it->isLast()) {
- return (ConstNsec3ParamPtr());
- }
-
- const generic::NSEC3PARAM& np =
- dynamic_cast<const generic::NSEC3PARAM&>(it->getCurrent());
- return (ConstNsec3ParamPtr(new Nsec3Param(np.getHashalg(), np.getFlags(),
- np.getIterations(),
- np.getSalt())));
-}
-
-inline DataSrc::Result
-proveNX(Query& q, QueryTaskPtr task, ZoneInfo& zoneinfo, const bool wildcard) {
- Message& m = q.message();
- const Name* const zonename = zoneinfo.getEnclosingZone();
- ConstNsec3ParamPtr nsec3 = getNsec3Param(q, zoneinfo);
-
- if (nsec3 != NULL) {
- // Attach the NSEC3 record covering the QNAME
- RRsetPtr rrset;
- string hash1(nsec3->getHash(task->qname));
- RETERR(getNsec3(q, zoneinfo, hash1, rrset));
- addToMessage(q, Message::SECTION_AUTHORITY, rrset);
-
- // If this is an NXRRSET or NOERROR/NODATA, we're done
- if ((task->flags & DataSrc::TYPE_NOT_FOUND) != 0) {
- return (DataSrc::SUCCESS);
- }
-
- // Find the closest provable enclosing name for QNAME
- Name enclosure(*zonename);
- const int diff = task->qname.getLabelCount() -
- enclosure.getLabelCount();
- string hash2;
- for (int i = 1; i <= diff; ++i) {
- enclosure = task->qname.split(i);
- string nodehash(nsec3->getHash(enclosure));
- if (nodehash == hash1) {
- break;
- }
- hash2 = nodehash;
- RRsetList rl;
-
- // hash2 will be overwritten with the actual hash found;
- // we don't want to use one until we find an exact match
- RETERR(getNsec3(q, zoneinfo, hash2, rrset));
- if (hash2 == nodehash) {
- addToMessage(q, Message::SECTION_AUTHORITY, rrset);
- break;
- }
- }
-
- // If we are processing a wildcard answer, we're done.
- if (wildcard) {
- return (DataSrc::SUCCESS);
- }
-
- // Otherwise, there is no wildcard record, so we must add a
- // covering NSEC3 to prove that it doesn't exist.
- string hash3(nsec3->getHash(Name("*").concatenate(enclosure)));
- RETERR(getNsec3(q, zoneinfo, hash3, rrset));
- if (hash3 != hash1 && hash3 != hash2) {
- addToMessage(q, Message::SECTION_AUTHORITY, rrset);
- }
- } else {
- Name nsecname(task->qname);
- if ((task->flags & DataSrc::NAME_NOT_FOUND) != 0 || wildcard) {
- const DataSrc* ds = zoneinfo.getDataSource();
- if (ds == NULL) {
- m.setRcode(Rcode::SERVFAIL());
- logger.error(DATASRC_QUERY_NO_DS_NSEC).arg(*zonename);
- return (DataSrc::ERROR);
- }
- ds->findPreviousName(task->qname, nsecname, zonename);
- }
-
- RETERR(addNSEC(q, nsecname, zoneinfo));
- if ((task->flags & DataSrc::TYPE_NOT_FOUND) != 0 ||
- nsecname == *zonename)
- {
- return (DataSrc::SUCCESS);
- }
-
- // If we are processing a wildcard answer, we're done.
- if (wildcard) {
- return (DataSrc::SUCCESS);
- }
-
- // Otherwise, there is no wildcard record, so we must add an
- // NSEC for the zone to prove the wildcard doesn't exist.
- RETERR(addNSEC(q, *zonename, zoneinfo));
- }
-
- return (DataSrc::SUCCESS);
-}
-
-// Attempt a wildcard lookup
-inline DataSrc::Result
-tryWildcard(Query& q, QueryTaskPtr task, ZoneInfo& zoneinfo, bool& found) {
- LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_QUERY_WILDCARD).arg(task->qname);
- Message& m = q.message();
- DataSrc::Result result;
- found = false;
-
- if ((task->flags & DataSrc::NAME_NOT_FOUND) == 0 ||
- (task->state != QueryTask::GETANSWER &&
- task->state != QueryTask::FOLLOWCNAME)) {
- return (DataSrc::SUCCESS);
- }
-
- const Name* const zonename = zoneinfo.getEnclosingZone();
- const int diff = task->qname.getLabelCount() - zonename->getLabelCount();
- if (diff < 1) {
- return (DataSrc::SUCCESS);
- }
-
- RRsetList wild;
- const Name star("*");
- bool cname = false;
-
- for (int i = 1; i <= diff; ++i) {
- const Name& wname(star.concatenate(task->qname.split(i)));
- QueryTask newtask(q, wname, task->qtype, Message::SECTION_ANSWER,
- QueryTask::AUTH_QUERY);
- result = doQueryTask(newtask, zoneinfo, wild);
- if (result == DataSrc::SUCCESS) {
- if (newtask.flags == 0) {
- task->flags &= ~DataSrc::NAME_NOT_FOUND;
- task->flags &= ~DataSrc::TYPE_NOT_FOUND;
- found = true;
- break;
- } else if ((newtask.flags & DataSrc::CNAME_FOUND) != 0) {
- task->flags &= ~DataSrc::NAME_NOT_FOUND;
- task->flags &= ~DataSrc::TYPE_NOT_FOUND;
- task->flags |= DataSrc::CNAME_FOUND;
- found = true;
- cname = true;
- break;
- } else if ((newtask.flags & DataSrc::TYPE_NOT_FOUND) != 0) {
- task->flags &= ~DataSrc::NAME_NOT_FOUND;
- task->flags |= DataSrc::TYPE_NOT_FOUND;
- break;
- }
- }
- }
-
- // A wildcard was found.
- if (found) {
- // Prove the nonexistence of the name we were looking for
- if (q.wantDnssec()) {
- result = proveNX(q, task, zoneinfo, true);
- if (result != DataSrc::SUCCESS) {
- m.setRcode(Rcode::SERVFAIL());
- logger.error(DATASRC_QUERY_WILDCARD_PROVE_NX_FAIL).
- arg(task->qname).arg(result);
- return (DataSrc::ERROR);
- }
- }
-
- // Add the data to the answer section (but with the name changed to
- // match the qname), and then continue as if this were a normal
- // answer: if a CNAME, chase the target, otherwise add authority.
- if (cname) {
- RRsetPtr rrset = findRRsetFromList(wild, RRType::CNAME());
- if (rrset != NULL) {
- rrset->setName(task->qname);
- addToMessage(q, Message::SECTION_ANSWER, rrset);
- chaseCname(q, task, rrset);
- }
- } else {
- BOOST_FOREACH (RRsetPtr rrset, wild) {
- rrset->setName(task->qname);
- addToMessage(q, Message::SECTION_ANSWER, rrset);
- }
-
- RRsetList auth;
- if (!refQuery(q, *zonename, zoneinfo, auth)) {
- logger.error(DATASRC_QUERY_WILDCARD_REFERRAL).arg(task->qname).
- arg(result);
- return (DataSrc::ERROR);
- }
-
- copyAuth(q, auth);
- }
- }
-
- return (DataSrc::SUCCESS);
-}
-
-//
-// doQuery: Processes a query.
-//
-void
-DataSrc::doQuery(Query& q) {
- LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_QUERY_PROCESS).arg(q.qname()).
- arg(q.qtype()).arg(q.qclass());
- Message& m = q.message();
- vector<RRsetPtr> additional;
-
- // Record the fact that the query is being processed by the
- // current data source.
- q.setDatasrc(this);
-
- // Process the query task queue. (The queue is initialized
- // and the first task placed on it by the Query constructor.)
- m.setHeaderFlag(Message::HEADERFLAG_AA, false);
- while (!q.tasks().empty()) {
- QueryTaskPtr task = q.tasks().front();
- q.tasks().pop();
-
- // Can't query directly for RRSIG.
- if (task->qtype == RRType::RRSIG()) {
- m.setRcode(Rcode::REFUSED());
- logger.warn(DATASRC_QUERY_RRSIG).arg(task->qname);
- return;
- }
-
- // These task types should never be on the task queue.
- if (task->op == QueryTask::SIMPLE_QUERY ||
- task->op == QueryTask::REF_QUERY) {
- m.setRcode(Rcode::SERVFAIL());
- logger.error(DATASRC_QUERY_MISPLACED_TASK);
- return;
- }
-
- ZoneInfo zoneinfo(this, task->qname, task->qclass, task->qtype);
- RRsetList data;
- Result result = SUCCESS;
-
- // For these query task types, if there is more than
- // one level between the zone name and qname, we need to
- // check the intermediate nodes for referrals.
- if ((task->op == QueryTask::AUTH_QUERY ||
- task->op == QueryTask::NOGLUE_QUERY) &&
- hasDelegation(q, task, zoneinfo)) {
- continue;
- }
-
- result = doQueryTask(*task, zoneinfo, data);
- if (result != SUCCESS) {
- m.setRcode(Rcode::SERVFAIL());
- logger.error(DATASRC_QUERY_TASK_FAIL).arg(result);
- return;
- }
-
- // No such zone. If we're chasing cnames or adding additional
- // data, that's okay, but if doing an original query, return
- // REFUSED.
- if (task->flags == NO_SUCH_ZONE) {
- if (task->state == QueryTask::GETANSWER) {
- m.setRcode(Rcode::REFUSED());
- // No need to log it here, it was already logged in doQueryTask
- return;
- }
- continue;
- }
-
- // Query found a referral; let's find out if that was expected--
- // i.e., if an NS was at the zone apex, or if we were querying
- // specifically for, and found, a DS, NSEC, or DNAME record.
- const Name* const zonename = zoneinfo.getEnclosingZone();
- if ((task->flags & REFERRAL) != 0 &&
- (zonename->getLabelCount() == task->qname.getLabelCount() ||
- ((task->qtype == RRType::NSEC() ||
- task->qtype == RRType::DS() ||
- task->qtype == RRType::DNAME()) &&
- findRRsetFromList(data, task->qtype)))) {
- task->flags &= ~REFERRAL;
- }
-
- if (result == SUCCESS && task->flags == 0) {
- bool have_ns = false, need_auth = false;
- switch (task->state) {
- case QueryTask::GETANSWER:
- case QueryTask::FOLLOWCNAME:
- BOOST_FOREACH(RRsetPtr rrset, data) {
- addToMessage(q, task->section, rrset);
- if (q.tasks().empty()) {
- need_auth = true;
- }
- getAdditional(q, rrset);
- if (rrset->getType() == RRType::NS()) {
- have_ns = true;
- }
- }
- q.setStatus(Query::ANSWERED);
- if (need_auth && !have_ns) {
- // Data found, no additional processing needed.
- // Add the NS records for the enclosing zone to
- // the authority section.
- RRsetList auth;
- if (!refQuery(q, Name(*zonename), zoneinfo, auth) ||
- !findRRsetFromList(auth, RRType::NS())) {
- logger.error(DATASRC_QUERY_MISSING_NS).arg(*zonename);
- isc_throw(DataSourceError,
- "NS RR not found in " << *zonename << "/" <<
- q.qclass());
- }
-
- copyAuth(q, auth);
- }
- continue;
-
- case QueryTask::GETADDITIONAL:
- // Got additional data. Do not add it to the message
- // yet; instead store it and copy it in at the end
- // (this allow RRSIGs to be omitted if necessary).
- BOOST_FOREACH(RRsetPtr rrset, data) {
- if (q.status() == Query::ANSWERED &&
- rrset->getName() == q.qname() &&
- rrset->getType() == q.qtype()) {
- continue;
- }
- additional.push_back(rrset);
- }
- continue;
-
- default:
- logger.error(DATASRC_UNEXPECTED_QUERY_STATE);
- isc_throw (Unexpected, "unexpected query state");
- }
- } else if (result == ERROR || result == NOT_IMPLEMENTED) {
- m.setRcode(Rcode::SERVFAIL());
- logger.error(DATASRC_QUERY_FAIL);
- return;
- } else if ((task->flags & CNAME_FOUND) != 0) {
- // The qname node contains a CNAME. Add a new task to the
- // queue to look up its target.
- RRsetPtr rrset = findRRsetFromList(data, RRType::CNAME());
- if (rrset != NULL) {
- addToMessage(q, task->section, rrset);
- chaseCname(q, task, rrset);
- }
- continue;
- } else if ((task->flags & REFERRAL) != 0) {
- // The qname node contains an out-of-zone referral.
- if (task->state == QueryTask::GETANSWER) {
- RRsetList auth;
- m.setHeaderFlag(Message::HEADERFLAG_AA, false);
- if (!refQuery(q, task->qname, zoneinfo, auth)) {
- m.setRcode(Rcode::SERVFAIL());
- logger.error(DATASRC_QUERY_BAD_REFERRAL).arg(task->qname);
- return;
- }
- BOOST_FOREACH (RRsetPtr rrset, auth) {
- if (rrset->getType() == RRType::NS()) {
- addToMessage(q, Message::SECTION_AUTHORITY, rrset);
- } else if (rrset->getType() == task->qtype) {
- addToMessage(q, Message::SECTION_ANSWER, rrset);
- } else if (rrset->getType() == RRType::DS() &&
- q.wantDnssec()) {
- addToMessage(q, Message::SECTION_AUTHORITY, rrset);
- }
- getAdditional(q, rrset);
- }
- }
- continue;
- } else if ((task->flags & (NAME_NOT_FOUND|TYPE_NOT_FOUND)) != 0) {
- // No data found at this qname/qtype.
-
- // If we were looking for additional data, we should simply
- // ignore this result.
- if (task->state == QueryTask::GETADDITIONAL) {
- continue;
- }
-
- // If we were looking for answer data, not additional,
- // and the name was not found, we need to find out whether
- // there are any relevant wildcards.
- bool wildcard_found = false;
- result = tryWildcard(q, task, zoneinfo, wildcard_found);
- if (result != SUCCESS) {
- m.setRcode(Rcode::SERVFAIL());
- logger.error(DATASRC_QUERY_WILDCARD_FAIL).arg(task->qname);
- return;
- }
-
- if (wildcard_found) {
- continue;
- }
-
- // If we've reached this point, there is definitely no answer.
- // If we were chasing cnames or adding additional data, that's
- // okay, but if we were doing an original query, reply with the
- // SOA in the authority section. For NAME_NOT_FOUND, set
- // NXDOMAIN, and also add the previous NSEC to the authority
- // section. For TYPE_NOT_FOUND, do not set an error rcode,
- // and send the current NSEC in the authority section.
- if (task->state == QueryTask::GETANSWER) {
- if ((task->flags & NAME_NOT_FOUND) != 0) {
- m.setRcode(Rcode::NXDOMAIN());
- }
-
- result = addSOA(q, zoneinfo);
- if (result != SUCCESS) {
- logger.error(DATASRC_QUERY_MISSING_SOA).arg(*zonename);
- isc_throw(DataSourceError,
- "SOA RR not found in " << *zonename <<
- "/" << q.qclass());
- }
- }
-
- Name nsecname(task->qname);
- if ((task->flags & NAME_NOT_FOUND) != 0) {
- const DataSrc* ds = zoneinfo.getDataSource();
- ds->findPreviousName(task->qname, nsecname, zonename);
- }
-
- if (q.wantDnssec()) {
- result = proveNX(q, task, zoneinfo, false);
- if (result != DataSrc::SUCCESS) {
- m.setRcode(Rcode::SERVFAIL());
- logger.error(DATASRC_QUERY_PROVE_NX_FAIL).arg(task->qname);
- return;
- }
- }
-
- return;
- } else {
- // Should never be reached!
- m.setRcode(Rcode::SERVFAIL());
- logger.error(DATASRC_QUERY_UNKNOWN_RESULT);
- return;
- }
- }
-
- // We're done, so now copy in the additional data:
- // data first, then signatures. (If we run out of
- // space, signatures in additional section are
- // optional.)
- BOOST_FOREACH(RRsetPtr rrset, additional) {
- addToMessage(q, Message::SECTION_ADDITIONAL, rrset, true);
- }
-
- if (q.wantDnssec()) {
- BOOST_FOREACH(RRsetPtr rrset, additional) {
- if (rrset->getRRsig()) {
- addToMessage(q, Message::SECTION_ADDITIONAL, rrset->getRRsig(),
- true);
- }
- }
- }
-}
-
-DataSrc::Result
-DataSrc::findAddrs(const Name& qname, const RRClass& qclass,
- RRsetList& target, uint32_t& flags,
- const Name* zonename) const
-{
- Result r;
- bool a = false, aaaa = false;
-
- flags = 0;
- r = findExactRRset(qname, qclass, RRType::A(), target, flags, zonename);
- if (r == SUCCESS && flags == 0) {
- a = true;
- }
-
- flags = 0;
- r = findExactRRset(qname, qclass, RRType::AAAA(), target, flags,
- zonename);
- if (r == SUCCESS && flags == 0) {
- aaaa = true;
- }
-
- if (!a && !aaaa) {
- flags = TYPE_NOT_FOUND;
- } else {
- flags = 0;
- }
-
- return (SUCCESS);
-}
-
-DataSrc::Result
-DataSrc::findReferral(const Name& qname, const RRClass& qclass,
- RRsetList& target, uint32_t& flags,
- const Name* zonename) const
-{
- Result r;
- bool ns = false, ds = false, dname = false;
-
- flags = 0;
- r = findExactRRset(qname, qclass, RRType::NS(), target, flags, zonename);
- if (r == SUCCESS && flags == 0) {
- ns = true;
- } else if ((flags & (NO_SUCH_ZONE|NAME_NOT_FOUND))) {
- return (SUCCESS);
- }
-
- flags = 0;
- r = findExactRRset(qname, qclass, RRType::DS(), target, flags, zonename);
- if (r == SUCCESS && flags == 0) {
- ds = true;
- } else if ((flags & (NO_SUCH_ZONE|NAME_NOT_FOUND))) {
- return (SUCCESS);
- }
-
- flags = 0;
- r = findExactRRset(qname, qclass, RRType::DNAME(), target, flags, zonename);
- if (r == SUCCESS && flags == 0) {
- dname = true;
- } else if ((flags & (NO_SUCH_ZONE|NAME_NOT_FOUND))) {
- return (SUCCESS);
- }
-
- if (!ns && !dname && !ds) {
- flags = TYPE_NOT_FOUND;
- } else {
- flags = 0;
- }
-
- return (SUCCESS);
-}
-
-void
-MetaDataSrc::addDataSrc(ConstDataSrcPtr data_src) {
- LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_META_ADD);
- if (getClass() != RRClass::ANY() && data_src->getClass() != getClass()) {
- logger.error(DATASRC_META_ADD_CLASS_MISMATCH).
- arg(data_src->getClass()).arg(getClass());
- isc_throw(Unexpected, "class mismatch");
- }
-
- data_sources.push_back(data_src);
-}
-
-void
-MetaDataSrc::removeDataSrc(ConstDataSrcPtr data_src) {
- LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_META_REMOVE);
- std::vector<ConstDataSrcPtr>::iterator it, itr;
- for (it = data_sources.begin(); it != data_sources.end(); ++it) {
- if (*it == data_src) {
- itr = it;
- }
- }
-
- data_sources.erase(itr);
-}
-
-void
-MetaDataSrc::findClosestEnclosure(DataSrcMatch& match) const {
- if (getClass() != match.getClass() &&
- getClass() != RRClass::ANY() && match.getClass() != RRClass::ANY()) {
- return;
- }
-
- BOOST_FOREACH (ConstDataSrcPtr data_src, data_sources) {
- data_src->findClosestEnclosure(match);
- }
-}
-
-DataSrcMatch::~DataSrcMatch() {
- delete closest_name_;
-}
-
-void
-DataSrcMatch::update(const DataSrc& new_source, const Name& container) {
- if (getClass() != new_source.getClass() && getClass() != RRClass::ANY() &&
- new_source.getClass() != RRClass::ANY())
- {
- return;
- }
-
- if (closest_name_ == NULL) {
- const NameComparisonResult::NameRelation cmp =
- getName().compare(container).getRelation();
- if (cmp != NameComparisonResult::EQUAL &&
- cmp != NameComparisonResult::SUBDOMAIN)
- {
- return;
- }
-
- closest_name_ = new Name(container);
- best_source_ = &new_source;
- return;
- }
-
- if (container.compare(*closest_name_).getRelation() ==
- NameComparisonResult::SUBDOMAIN) {
- Name* newname = new Name(container);
- delete closest_name_;
- closest_name_ = newname;
- best_source_ = &new_source;
- }
-}
-
-Nsec3Param::Nsec3Param(const uint8_t a, const uint8_t f, const uint16_t i,
- const std::vector<uint8_t>& s) :
- algorithm_(a), flags_(f), iterations_(i), salt_(s)
-{}
-
-string
-Nsec3Param::getHash(const Name& name) const {
- OutputBuffer buf(0);
- name.toWire(buf);
-
- uint8_t digest[SHA1_HASHSIZE];
- const uint8_t* input = static_cast<const uint8_t*>(buf.getData());
- size_t inlength = buf.getLength();
- const uint8_t saltlen = salt_.size();
-
- int n = 0;
- SHA1Context sha;
- do {
- SHA1Reset(&sha);
- SHA1Input(&sha, input, inlength);
- SHA1Input(&sha, &salt_[0], saltlen);
- SHA1Result(&sha, digest);
- input = digest;
- inlength = SHA1_HASHSIZE;
- } while (n++ < iterations_);
-
- return (encodeBase32Hex(vector<uint8_t>(digest, digest + SHA1_HASHSIZE)));
-}
-
-DataSrc::Result
-DataSrc::init(isc::data::ConstElementPtr) {
- return (NOT_IMPLEMENTED);
-}
-
-DataSrc::Result
-MetaDataSrc::findRRset(const isc::dns::Name&,
- const isc::dns::RRClass&,
- const isc::dns::RRType&,
- isc::dns::RRsetList&,
- uint32_t&,
- const isc::dns::Name*) const
-{
- return (NOT_IMPLEMENTED);
-}
-
-DataSrc::Result
-MetaDataSrc::findExactRRset(const isc::dns::Name&,
- const isc::dns::RRClass&,
- const isc::dns::RRType&,
- isc::dns::RRsetList&,
- uint32_t&,
- const isc::dns::Name*) const
-{
- return (NOT_IMPLEMENTED);
-}
-
-DataSrc::Result
-MetaDataSrc::findAddrs(const isc::dns::Name&,
- const isc::dns::RRClass&,
- isc::dns::RRsetList&,
- uint32_t&,
- const isc::dns::Name*) const
-{
- return (NOT_IMPLEMENTED);
-}
-
-DataSrc::Result
-MetaDataSrc::findReferral(const isc::dns::Name&,
- const isc::dns::RRClass&,
- isc::dns::RRsetList&,
- uint32_t&,
- const isc::dns::Name*) const
-{
- return (NOT_IMPLEMENTED);
-}
-
-DataSrc::Result
-MetaDataSrc::findPreviousName(const isc::dns::Name&,
- isc::dns::Name&,
- const isc::dns::Name*) const
-{
- return (NOT_IMPLEMENTED);
-}
-
-DataSrc::Result
-MetaDataSrc::findCoveringNSEC3(const isc::dns::Name&,
- std::string&,
- isc::dns::RRsetList&) const
-{
- return (NOT_IMPLEMENTED);
-}
-
-}
-}
diff --git a/src/lib/datasrc/data_source.h b/src/lib/datasrc/data_source.h
index c35f0d3..37c536e 100644
--- a/src/lib/datasrc/data_source.h
+++ b/src/lib/datasrc/data_source.h
@@ -38,13 +38,6 @@ class RRsetList;
namespace datasrc {
-class DataSrcMatch;
-class Query;
-
-class DataSrc;
-typedef boost::shared_ptr<DataSrc> DataSrcPtr;
-typedef boost::shared_ptr<const DataSrc> ConstDataSrcPtr;
-
/// This exception represents Backend-independent errors relating to
/// data source operations.
class DataSourceError : public Exception {
@@ -65,361 +58,6 @@ public:
DataSourceError(file, line, what) {}
};
-
-class AbstractDataSrc {
- ///
- /// \name Constructors, Assignment Operator and Destructor.
- ///
- /// Note: The copy constructor and the assignment operator are intentionally
- /// defined as private to make it explicit that this is a pure base class.
-private:
- AbstractDataSrc(const AbstractDataSrc& source);
- AbstractDataSrc& operator=(const AbstractDataSrc& source);
-protected:
- /// \brief The default constructor.
- ///
- /// This is intentionally defined as \c protected as this base class should
- /// never be instantiated (except as part of a derived class).
- AbstractDataSrc() {}
-public:
- /// \brief The destructor.
- virtual ~AbstractDataSrc() {};
- //@}
-
- enum Result {
- SUCCESS,
- ERROR,
- NOT_IMPLEMENTED
- };
-
- // These flags indicate conditions encountered while processing a query.
- //
- // REFERRAL: The node contains an NS record
- // CNAME_FOUND: The node contains a CNAME record
- // NAME_NOT_FOUND: The node does not exist in the data source.
- // TYPE_NOT_FOUND: The node does not contain the requested RRType
- // NO_SUCH_ZONE: The zone does not exist in this data source.
- //
- // DATA_NOT_FOUND: A combination of the last three, for coding convenience
- enum QueryResponseFlags {
- REFERRAL = 0x01,
- CNAME_FOUND = 0x02,
- NAME_NOT_FOUND = 0x04,
- TYPE_NOT_FOUND = 0x08,
- NO_SUCH_ZONE = 0x10,
- DATA_NOT_FOUND = (NAME_NOT_FOUND|TYPE_NOT_FOUND|NO_SUCH_ZONE)
- };
-
- // 'High-level' methods. These will be implemented by the
- // general DataSrc class, and SHOULD NOT be overwritten by subclasses.
- virtual void doQuery(Query& query) = 0;
-
- // XXX: High-level methods to be implemented later:
- // virtual void doUpdate(Update update) = 0;
- // virtual void doXfr(Query query) = 0;
-
- // 'Medium-level' methods. This will be implemented by the general
- // DataSrc class but MAY be overwritten by subclasses.
- virtual void findClosestEnclosure(DataSrcMatch& match) const = 0;
-
- // Optional 'low-level' methods. These will have stub implementations
- // in the general DataSrc class but MAY be overwritten by subclasses
- virtual Result init() = 0;
- virtual Result init(isc::data::ConstElementPtr config) = 0;
- virtual Result close() = 0;
-
- // Mandatory 'low-level' methods: These will NOT be implemented by
- // the general DataSrc class; subclasses MUST implement them.
- virtual Result findRRset(const isc::dns::Name& qname,
- const isc::dns::RRClass& qclass,
- const isc::dns::RRType& qtype,
- isc::dns::RRsetList& target,
- uint32_t& flags,
- const isc::dns::Name* zonename) const = 0;
-
- virtual Result findExactRRset(const isc::dns::Name& qname,
- const isc::dns::RRClass& qclass,
- const isc::dns::RRType& qtype,
- isc::dns::RRsetList& target,
- uint32_t& flags,
- const isc::dns::Name* zonename) const = 0;
-
- // These will have dumb implementations in the general DataSrc
- // class, and SHOULD be overwritten by subclasses.
- virtual Result findAddrs(const isc::dns::Name& qname,
- const isc::dns::RRClass& qclass,
- isc::dns::RRsetList& target,
- uint32_t& flags,
- const isc::dns::Name* zonename) const = 0;
-
- virtual Result findReferral(const isc::dns::Name& qname,
- const isc::dns::RRClass& qclass,
- isc::dns::RRsetList& target,
- uint32_t& flags,
- const isc::dns::Name* zonename) const = 0;
-
- // This MUST be implemented by concrete data sources which support
- // DNSSEC, but is optional for others (e.g., the static data source).
- virtual Result findPreviousName(const isc::dns::Name& qname,
- isc::dns::Name& target,
- const isc::dns::Name* zonename) const = 0;
-
- // This MUST be implemented by concrete data sources which support
- // NSEC3, but is optional for others
- virtual Result findCoveringNSEC3(const isc::dns::Name& zonename,
- std::string& hash,
- isc::dns::RRsetList& target) const = 0;
-};
-
-// Base class for a DNS Data Source
-class DataSrc : public AbstractDataSrc {
- ///
- /// \name Constructors, Assignment Operator and Destructor.
- ///
- /// Note: The copy constructor and the assignment operator are intentionally
- /// defined as private.
-private:
- DataSrc(const DataSrc& source);
- DataSrc& operator=(const DataSrc& source);
-public:
- DataSrc() : rrclass(isc::dns::RRClass::IN()) {}
- DataSrc(const isc::dns::RRClass& c) : rrclass(c) {}
- /// \brief The destructor.
- virtual ~DataSrc() {};
- //@}
-
- virtual void doQuery(Query& q);
-
- virtual void findClosestEnclosure(DataSrcMatch& match) const = 0;
-
- const isc::dns::RRClass& getClass() const { return (rrclass); }
- void setClass(isc::dns::RRClass& c) { rrclass = c; }
- void setClass(const isc::dns::RRClass& c) { rrclass = c; }
-
- virtual Result init() { return (NOT_IMPLEMENTED); }
- virtual Result init(isc::data::ConstElementPtr config);
- virtual Result close() { return (NOT_IMPLEMENTED); }
-
- virtual Result findRRset(const isc::dns::Name& qname,
- const isc::dns::RRClass& qclass,
- const isc::dns::RRType& qtype,
- isc::dns::RRsetList& target,
- uint32_t& flags,
- const isc::dns::Name* zonename) const = 0;
-
- virtual Result findExactRRset(const isc::dns::Name& qname,
- const isc::dns::RRClass& qclass,
- const isc::dns::RRType& qtype,
- isc::dns::RRsetList& target,
- uint32_t& flags,
- const isc::dns::Name* zonename) const = 0;
-
- virtual Result findAddrs(const isc::dns::Name& qname,
- const isc::dns::RRClass& qclass,
- isc::dns::RRsetList& target,
- uint32_t& flags,
- const isc::dns::Name* zonename) const;
-
- virtual Result findReferral(const isc::dns::Name& qname,
- const isc::dns::RRClass& qclass,
- isc::dns::RRsetList& target,
- uint32_t& flags,
- const isc::dns::Name* zonename) const;
-
- virtual Result findPreviousName(const isc::dns::Name& qname,
- isc::dns::Name& target,
- const isc::dns::Name* zonename) const = 0;
-
- virtual Result findCoveringNSEC3(const isc::dns::Name& zonename,
- std::string& hash,
- isc::dns::RRsetList& target) const = 0;
-
-private:
- isc::dns::RRClass rrclass;
-};
-
-class MetaDataSrc : public DataSrc {
- ///
- /// \name Constructors, Assignment Operator and Destructor.
- ///
- /// Note: The copy constructor and the assignment operator are intentionally
- /// defined as private.
- //@{
-private:
- MetaDataSrc(const MetaDataSrc& source);
- MetaDataSrc& operator=(const MetaDataSrc& source);
-public:
- MetaDataSrc() : DataSrc(isc::dns::RRClass::ANY()) {}
- MetaDataSrc(const isc::dns::RRClass& c) : DataSrc(c) {}
- /// \brief The destructor.
- virtual ~MetaDataSrc() {}
- //@}
-
- void addDataSrc(ConstDataSrcPtr data_src);
- void removeDataSrc(ConstDataSrcPtr data_src);
- size_t dataSrcCount() { return (data_sources.size()); }
-
- void findClosestEnclosure(DataSrcMatch& match) const;
-
- // Actual queries for data should not be sent to a MetaDataSrc object,
- // so we return NOT_IMPLEMENTED if we receive any.
- //
- // The proper way to use the MetaDataSrc is to run findClosestEnclosure()
- // to get a pointer to the best concrete data source for the specified
- // zone, then send all queries directly to that data source.
-
- Result findRRset(const isc::dns::Name& qname,
- const isc::dns::RRClass& qclass,
- const isc::dns::RRType& qtype,
- isc::dns::RRsetList& target,
- uint32_t& flags,
- const isc::dns::Name* zonename) const;
-
- Result findExactRRset(const isc::dns::Name& qname,
- const isc::dns::RRClass& qclass,
- const isc::dns::RRType& qtype,
- isc::dns::RRsetList& target,
- uint32_t& flags,
- const isc::dns::Name* zonename) const;
-
- Result findAddrs(const isc::dns::Name& qname,
- const isc::dns::RRClass& qclass,
- isc::dns::RRsetList& target,
- uint32_t& flags,
- const isc::dns::Name* zonename) const;
-
- Result findReferral(const isc::dns::Name& qname,
- const isc::dns::RRClass& qclass,
- isc::dns::RRsetList& target,
- uint32_t& flags,
- const isc::dns::Name* zonename) const;
-
- virtual Result findPreviousName(const isc::dns::Name& qname,
- isc::dns::Name& target,
- const isc::dns::Name* zonename) const;
-
- virtual Result findCoveringNSEC3(const isc::dns::Name& zonename,
- std::string& hash,
- isc::dns::RRsetList& target) const;
-
-private:
- std::vector<ConstDataSrcPtr> data_sources;
-};
-
-/// \brief Information about the zone along with the %data source that best
-/// matches a give name and RR class.
-///
-/// A \c DataSrcMatch object is created with a domain name and RR class to
-/// hold the search state of looking for the zone and the %data source that
-/// stores the zone that best match the given name and RR class.
-/// The application of this class passes an object of \c DataSrcMatch to
-/// one or more ^data sources via their \c findClosestEnclosure() method.
-/// The %data source searches its content for the given key, and update
-/// the state if it finds a better zone than the currently recorded one.
-///
-/// The state of a \c DataSrcMatch object should be updated if and only if:
-/// - The specified RR class and the RR class of the %data source are the
-// same, or the specified RR class is ANY; and
-/// - There is no matching %data source and name found (which is probably
-/// wrong, see below), or the given enclosing name gives a longer match
-/// than the currently stored enclosing name against the specified name.
-class DataSrcMatch {
- ///
- /// \name Constructors, Assignment Operator and Destructor.
- ///
- /// Note: The copy constructor and the assignment operator are
- /// intentionally defined as private.
- //@{
-private:
- DataSrcMatch(const DataSrcMatch& source);
- DataSrcMatch& operator=(const DataSrcMatch& source);
-public:
- /// \brief The constructor.
- ///
- /// This constructor normally doesn't throw an exception. However,
- /// it creates a copy of the given name object, which may require memory
- /// allocation, and if it fails the corresponding standard exception will
- /// be thrown.
- ///
- /// \param name The domain name to be matched.
- /// \param rrclass The RR class to be matched
- DataSrcMatch(const isc::dns::Name& name,
- const isc::dns::RRClass& rrclass) :
- closest_name_(NULL), best_source_(NULL),
- name_(name), rrclass_(rrclass)
- {}
- ~DataSrcMatch();
- //@}
-
- /// \name Getter and Setter Methods
- //@{
- /// \brief Returns the name to be matched.
- const isc::dns::Name& getName() const { return (name_); }
-
- /// \brief Returns the RR class to be matched.
- ///
- /// This method never throws an exception.
- const isc::dns::RRClass& getClass() const { return (rrclass_); }
-
- /// \brief Returns the best enclosing zone name found for the given
- // name and RR class so far.
- ///
- /// \return A pointer to the zone apex \c Name, NULL if none found yet.
- ///
- /// This method never throws an exception.
- const isc::dns::Name* getEnclosingZone() const { return (closest_name_); }
-
- /// \brief Returns the best %data source found for the given name and
- /// RR class so far.
- ///
- /// This method never throws an exception.
- ///
- /// \return A pointer to a concrete %data source, NULL if none found yet.
- const DataSrc* getDataSource() const { return (best_source_); }
- //@}
-
- /// \brief Update the object state with better information if possible.
- ///
- /// This method is intended to be called by a concrete %data source's
- /// \c findClosestEnclosure() method to store the best match for
- /// the given name and class that has been found so far.
- ///
- /// It compares the best name (if found) and \c container, and if the
- /// latter gives a longer match, it will install the given %data source
- /// and the enclosing name as the best match;
- /// if there is no known pair of %data source and enclosing name,
- /// this method will install the given pair unconditionally.
- /// (which is probably BAD);
- /// otherwise this method does nothing.
- ///
- /// In any case, if a new pair of %data source and enclosing name are
- /// installed, a new name object will be internally allocated.
- /// And, if memory allocation fails the corresponding standard exception
- /// will be thrown.
- ///
- /// \param new_source A candidate %data source that gives a better match.
- /// \param container The enclosing name of the matching zone in
- /// \c new_source.
- void update(const DataSrc& new_source, const isc::dns::Name& container);
-
-private:
- isc::dns::Name* closest_name_;
- const DataSrc* best_source_;
- const isc::dns::Name name_;
- const isc::dns::RRClass& rrclass_;
-};
-
-class Nsec3Param {
-public:
- Nsec3Param(uint8_t a, uint8_t f, uint16_t i, const std::vector<uint8_t>& s);
- std::string getHash(const isc::dns::Name& name) const;
-private:
- const uint8_t algorithm_;
- const uint8_t flags_;
- const uint16_t iterations_;
- const std::vector<uint8_t> salt_;
-};
-
}
}
diff --git a/src/lib/datasrc/memory/Makefile.am b/src/lib/datasrc/memory/Makefile.am
index eea9c0b..3bdec0d 100644
--- a/src/lib/datasrc/memory/Makefile.am
+++ b/src/lib/datasrc/memory/Makefile.am
@@ -1,4 +1,4 @@
-SUBDIRS = . tests benchmarks
+SUBDIRS = . benchmarks
AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
AM_CPPFLAGS += -I$(top_srcdir)/src/lib/dns -I$(top_builddir)/src/lib/dns
diff --git a/src/lib/datasrc/memory/domaintree.h b/src/lib/datasrc/memory/domaintree.h
index b29f10f..20f4693 100644
--- a/src/lib/datasrc/memory/domaintree.h
+++ b/src/lib/datasrc/memory/domaintree.h
@@ -373,10 +373,23 @@ private:
}
}
+ /// \brief returns if the node is a subtree's root node
+ ///
+ /// This method takes a node and returns \c true if it is the root
+ /// node of the subtree it belongs to.
+ ///
+ /// This method never throws an exception.
bool isSubTreeRoot() const {
return ((flags_ & FLAG_SUBTREE_ROOT) != 0);
}
+ /// \brief returns the root of its subtree
+ ///
+ /// This method takes a node and returns the root of its subtree.
+ ///
+ /// This method never throws an exception.
+ const DomainTreeNode<T>* getSubTreeRoot() const;
+
public:
/// \brief returns the parent of the root of its subtree
///
@@ -388,7 +401,6 @@ public:
/// This method never throws an exception.
const DomainTreeNode<T>* getUpperNode() const;
-private:
/// \brief return the next node which is bigger than current node
/// in the same subtree
///
@@ -423,6 +435,7 @@ private:
/// This method never throws an exception.
const DomainTreeNode<T>* predecessor() const;
+private:
/// \brief private shared implementation of successor and predecessor
///
/// As the two mentioned functions are merely mirror images of each other,
@@ -538,7 +551,7 @@ DomainTreeNode<T>::~DomainTreeNode() {
template <typename T>
const DomainTreeNode<T>*
-DomainTreeNode<T>::getUpperNode() const {
+DomainTreeNode<T>::getSubTreeRoot() const {
const DomainTreeNode<T>* current = this;
// current would never be equal to NULL here (in a correct tree
@@ -547,7 +560,13 @@ DomainTreeNode<T>::getUpperNode() const {
current = current->getParent();
}
- return (current->getParent());
+ return (current);
+}
+
+template <typename T>
+const DomainTreeNode<T>*
+DomainTreeNode<T>::getUpperNode() const {
+ return (getSubTreeRoot()->getParent());
}
template <typename T>
@@ -555,7 +574,17 @@ isc::dns::LabelSequence
DomainTreeNode<T>::getAbsoluteLabels(
uint8_t buf[isc::dns::LabelSequence::MAX_SERIALIZED_LENGTH]) const
{
- isc::dns::LabelSequence result(getLabels(), buf);
+ // If the current node already has absolute labels, just return it.
+ // This should normally be the case for the origin node if this tree
+ // is used to represent a single DNS zone.
+ const isc::dns::LabelSequence cur_labels(getLabels());
+ if (cur_labels.isAbsolute()) {
+ return (cur_labels);
+ }
+
+ // Otherwise, build the absolute sequence traversing the tree of tree
+ // toward the top root.
+ isc::dns::LabelSequence result(cur_labels, buf);
const DomainTreeNode<T>* upper = getUpperNode();
while (upper != NULL) {
result.extend(upper->getLabels(), buf);
@@ -672,14 +701,26 @@ public:
/// The default constructor.
///
/// \exception None
- DomainTreeNodeChain() : node_count_(0), last_compared_(NULL),
+ DomainTreeNodeChain() : level_count_(0), last_compared_(NULL),
// XXX: meaningless initial values:
last_comparison_(0, 0,
isc::dns::NameComparisonResult::EQUAL)
{}
+ /// \brief Copy constructor.
+ ///
+ /// \exception None
+ DomainTreeNodeChain(const DomainTreeNodeChain<T>& other) :
+ level_count_(other.level_count_),
+ last_compared_(other.last_compared_),
+ last_comparison_(other.last_comparison_)
+ {
+ for (size_t i = 0; i < level_count_; i++) {
+ nodes_[i] = other.nodes_[i];
+ }
+ }
+
private:
- DomainTreeNodeChain(const DomainTreeNodeChain<T>&);
DomainTreeNodeChain<T>& operator=(const DomainTreeNodeChain<T>&);
//@}
@@ -691,7 +732,7 @@ public:
///
/// \exception None
void clear() {
- node_count_ = 0;
+ level_count_ = 0;
last_compared_ = NULL;
}
@@ -732,7 +773,7 @@ public:
/// chain, 0 will be returned.
///
/// \exception None
- unsigned int getLevelCount() const { return (node_count_); }
+ size_t getLevelCount() const { return (level_count_); }
/// \brief return the absolute name for the node which this
/// \c DomainTreeNodeChain currently refers to.
@@ -750,11 +791,11 @@ public:
const DomainTreeNode<T>* top_node = top();
isc::dns::Name absolute_name = top_node->getName();
- int node_count = node_count_ - 1;
- while (node_count > 0) {
- top_node = nodes_[node_count - 1];
+ size_t level = level_count_ - 1;
+ while (level > 0) {
+ top_node = nodes_[level - 1];
absolute_name = absolute_name.concatenate(top_node->getName());
- --node_count;
+ --level;
}
return (absolute_name);
}
@@ -768,7 +809,7 @@ private:
/// \brief return whether node chain has node in it.
///
/// \exception None
- bool isEmpty() const { return (node_count_ == 0); }
+ bool isEmpty() const { return (level_count_ == 0); }
/// \brief return the top node for the node chain
///
@@ -778,7 +819,7 @@ private:
/// \exception None
const DomainTreeNode<T>* top() const {
assert(!isEmpty());
- return (nodes_[node_count_ - 1]);
+ return (nodes_[level_count_ - 1]);
}
/// \brief pop the top node from the node chain
@@ -789,7 +830,7 @@ private:
/// \exception None
void pop() {
assert(!isEmpty());
- --node_count_;
+ --level_count_;
}
/// \brief add the node into the node chain
@@ -800,8 +841,8 @@ private:
///
/// \exception None
void push(const DomainTreeNode<T>* node) {
- assert(node_count_ < RBT_MAX_LEVEL);
- nodes_[node_count_++] = node;
+ assert(level_count_ < RBT_MAX_LEVEL);
+ nodes_[level_count_++] = node;
}
private:
@@ -810,7 +851,7 @@ private:
// it's also equal to the possible maximum level.
const static int RBT_MAX_LEVEL = isc::dns::Name::MAX_LABELS;
- int node_count_;
+ size_t level_count_;
const DomainTreeNode<T>* nodes_[RBT_MAX_LEVEL];
const DomainTreeNode<T>* last_compared_;
isc::dns::NameComparisonResult last_comparison_;
@@ -1265,12 +1306,20 @@ public:
const DomainTreeNode<T>*
previousNode(DomainTreeNodeChain<T>& node_path) const;
+ /// \brief return the largest node in the tree of trees.
+ ///
+ /// \throw none
+ ///
+ /// \return A \c DomainTreeNode that is the largest node in the
+ /// tree. If there are no nodes, then \c NULL is returned.
+ const DomainTreeNode<T>* largestNode() const;
+
/// \brief Get the total number of nodes in the tree
///
/// It includes nodes internally created as a result of adding a domain
/// name that is a subdomain of an existing node of the tree.
/// This function is mainly intended to be used for debugging.
- int getNodeCount() const { return (node_count_); }
+ uint32_t getNodeCount() const { return (node_count_); }
/// \name Debug function
//@{
@@ -1402,8 +1451,15 @@ private:
//@}
typename DomainTreeNode<T>::DomainTreeNodePtr root_;
- /// the node count of current tree
- unsigned int node_count_;
+
+ /// the node count of current tree.
+ ///
+ /// Note: uint32_t may look awkward, but we intentionally choose it so
+ /// that needsReturnEmptyNode_ below won't make cause extra padding
+ /// in 64-bit machines (and we can minimize the total size of this class).
+ /// 2^32 - 1 should be a reasonable max of possible number of nodes.
+ uint32_t node_count_;
+
/// search policy for domaintree
const bool needsReturnEmptyNode_;
};
@@ -1710,6 +1766,24 @@ DomainTree<T>::previousNode(DomainTreeNodeChain<T>& node_path) const {
}
template <typename T>
+const DomainTreeNode<T>*
+DomainTree<T>::largestNode() const {
+ const DomainTreeNode<T>* node = root_.get();
+ while (node != NULL) {
+ // We go right first, then down.
+ if (node->getRight() != NULL) {
+ node = node->getRight();
+ } else if (node->getDown() != NULL) {
+ node = node->getDown();
+ } else {
+ break;
+ }
+ }
+
+ return (node);
+}
+
+template <typename T>
typename DomainTree<T>::Result
DomainTree<T>::insert(util::MemorySegment& mem_sgmt,
const isc::dns::Name& target_name,
@@ -1826,7 +1900,7 @@ DomainTree<T>::nodeFission(util::MemorySegment& mem_sgmt,
node.getParent()->down_ = up_node;
}
} else {
- this->root_ = up_node;
+ root_ = up_node;
}
up_node->down_ = &node;
diff --git a/src/lib/datasrc/memory/memory_client.cc b/src/lib/datasrc/memory/memory_client.cc
index 66822d2..5f6f510 100644
--- a/src/lib/datasrc/memory/memory_client.cc
+++ b/src/lib/datasrc/memory/memory_client.cc
@@ -22,6 +22,7 @@
#include <datasrc/memory/domaintree.h>
#include <datasrc/memory/segment_object_holder.h>
#include <datasrc/memory/treenode_rrset.h>
+#include <datasrc/memory/zone_finder.h>
#include <util/memory_segment_local.h>
@@ -100,9 +101,6 @@ public:
FileNameTree::destroy(mem_sgmt_, file_name_tree_, deleter);
ZoneTable::destroy(mem_sgmt_, zone_table_, rrclass_);
-
- // see above for the assert().
- assert(mem_sgmt_.allMemoryDeallocated());
}
util::MemorySegment& mem_sgmt_;
@@ -127,7 +125,7 @@ public:
//
// In order for wildcard matching to work correctly in the zone finder,
// we must ensure that a node for the wildcarding level exists in the
- // backend RBTree.
+ // backend ZoneTree.
// E.g. if the wildcard name is "*.sub.example." then we must ensure
// that "sub.example." exists and is marked as a wildcard level.
// Note: the "wildcarding level" is for the parent name of the wildcard
@@ -326,6 +324,7 @@ public:
if (nsec3_data == NULL) {
nsec3_data = NSEC3Data::create(mem_sgmt_, nsec3_rdata);
zone_data.setNSEC3Data(nsec3_data);
+ zone_data.setSigned(true);
} else {
size_t salt_len = nsec3_data->getSaltLen();
const uint8_t* salt_data = nsec3_data->getSaltData();
@@ -333,7 +332,12 @@ public:
if ((nsec3_rdata.getHashalg() != nsec3_data->hashalg) ||
(nsec3_rdata.getIterations() != nsec3_data->iterations) ||
- (salt_data_2.size() != salt_len) ||
+ (salt_data_2.size() != salt_len)) {
+ isc_throw(AddError,
+ "NSEC3 with inconsistent parameters: " <<
+ rrset->toText());
+ }
+ if ((salt_len > 0) &&
(std::memcmp(&salt_data_2[0], salt_data, salt_len) != 0)) {
isc_throw(AddError,
"NSEC3 with inconsistent parameters: " <<
@@ -341,17 +345,10 @@ public:
}
}
- string fst_label = rrset->getName().split(0, 1).toText(true);
- transform(fst_label.begin(), fst_label.end(), fst_label.begin(),
- ::toupper);
-
ZoneNode* node;
- nsec3_data->insertName(mem_sgmt_, Name(fst_label), &node);
+ nsec3_data->insertName(mem_sgmt_, rrset->getName(), &node);
RdataEncoder encoder;
-
- // We assume that rrsig has already been checked to match rrset
- // by the caller.
RdataSet* set = RdataSet::create(mem_sgmt_, encoder, rrset, rrsig);
RdataSet* old_set = node->setData(set);
if (old_set != NULL) {
@@ -416,6 +413,7 @@ public:
if (nsec3_data == NULL) {
nsec3_data = NSEC3Data::create(mem_sgmt_, param);
zone_data.setNSEC3Data(nsec3_data);
+ zone_data.setSigned(true);
} else {
size_t salt_len = nsec3_data->getSaltLen();
const uint8_t* salt_data = nsec3_data->getSaltData();
@@ -423,7 +421,13 @@ public:
if ((param.getHashalg() != nsec3_data->hashalg) ||
(param.getIterations() != nsec3_data->iterations) ||
- (salt_data_2.size() != salt_len) ||
+ (salt_data_2.size() != salt_len)) {
+ isc_throw(AddError,
+ "NSEC3PARAM with inconsistent parameters: "
+ << rrset->toText());
+ }
+
+ if ((salt_len > 0) &&
(std::memcmp(&salt_data_2[0],
salt_data, salt_len) != 0)) {
isc_throw(AddError,
@@ -606,7 +610,7 @@ InMemoryClient::InMemoryClientImpl::load(
}
LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_MEMORY_MEM_ADD_ZONE).
- arg(zone_name).arg(rrclass_.toText());
+ arg(zone_name).arg(rrclass_);
// Set the filename in file_name_tree_ now, so that getFileName()
// can use it (during zone reloading).
@@ -686,21 +690,25 @@ InMemoryClient::getZoneCount() const {
return (impl_->zone_count_);
}
-isc::datasrc::memory::ZoneTable::FindResult
-InMemoryClient::findZone2(const isc::dns::Name& zone_name) const {
+isc::datasrc::DataSourceClient::FindResult
+InMemoryClient::findZone(const isc::dns::Name& zone_name) const {
LOG_DEBUG(logger, DBG_TRACE_DATA,
DATASRC_MEMORY_MEM_FIND_ZONE).arg(zone_name);
+
ZoneTable::FindResult result(impl_->zone_table_->findZone(zone_name));
- return (result);
+
+ ZoneFinderPtr finder;
+ if (result.code != result::NOTFOUND) {
+ finder.reset(new InMemoryZoneFinder(*result.zone_data, getClass()));
+ }
+
+ return (DataSourceClient::FindResult(result.code, finder));
}
-isc::datasrc::DataSourceClient::FindResult
-InMemoryClient::findZone(const isc::dns::Name&) const {
- // This variant of findZone() is not implemented and should be
- // removed eventually. It currently throws an exception. It is
- // required right now to derive from DataSourceClient.
- isc_throw(isc::NotImplemented,
- "This variant of findZone() is not implemented.");
+const ZoneData*
+InMemoryClient::findZoneData(const isc::dns::Name& zone_name) {
+ ZoneTable::FindResult result(impl_->zone_table_->findZone(zone_name));
+ return (result.zone_data);
}
result::Result
diff --git a/src/lib/datasrc/memory/memory_client.h b/src/lib/datasrc/memory/memory_client.h
index 4c3ad27..330d62e 100644
--- a/src/lib/datasrc/memory/memory_client.h
+++ b/src/lib/datasrc/memory/memory_client.h
@@ -20,11 +20,7 @@
#include <datasrc/iterator.h>
#include <datasrc/client.h>
#include <datasrc/memory/zone_table.h>
-
-// for isc::datasrc::ZoneTable::FindResult returned by findZone(). This
-// variant of findZone() is not implemented and should be removed
-// eventually.
-#include <datasrc/zonetable.h>
+#include <datasrc/memory/zone_data.h>
#include <string>
@@ -206,19 +202,22 @@ public:
{ }
};
- /// Returns a \c ZoneTable result that best matches the given name.
+ /// Returns a \c ZoneFinder result that best matches the given name.
///
/// This derived version of the method never throws an exception.
/// For other details see \c DataSourceClient::findZone().
- virtual isc::datasrc::memory::ZoneTable::FindResult
- findZone2(const isc::dns::Name& name) const;
-
- // This variant of findZone() is not implemented and should be
- // removed eventually. It currently throws an exception. It is
- // required right now to derive from DataSourceClient.
virtual isc::datasrc::DataSourceClient::FindResult
findZone(const isc::dns::Name& name) const;
+ /// Returns a \c ZoneData in the result that best matches the given
+ /// name.
+ ///
+ /// This is mainly intended for use in unit tests and should not be
+ /// used in other code.
+ ///
+ /// \throws none
+ const ZoneData* findZoneData(const isc::dns::Name& name);
+
/// \brief Implementation of the getIterator method
virtual isc::datasrc::ZoneIteratorPtr
getIterator(const isc::dns::Name& name, bool separate_rrs = false) const;
diff --git a/src/lib/datasrc/memory/tests/.gitignore b/src/lib/datasrc/memory/tests/.gitignore
deleted file mode 100644
index d6d1ec8..0000000
--- a/src/lib/datasrc/memory/tests/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-/run_unittests
diff --git a/src/lib/datasrc/memory/tests/Makefile.am b/src/lib/datasrc/memory/tests/Makefile.am
deleted file mode 100644
index 74b1a3d..0000000
--- a/src/lib/datasrc/memory/tests/Makefile.am
+++ /dev/null
@@ -1,50 +0,0 @@
-SUBDIRS = testdata .
-
-AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
-AM_CPPFLAGS += -I$(top_builddir)/src/lib/dns -I$(top_srcdir)/src/lib/dns
-AM_CPPFLAGS += $(BOOST_INCLUDES)
-AM_CPPFLAGS += -DTEST_DATA_DIR=\"$(abs_srcdir)/testdata\"
-
-AM_CXXFLAGS = $(B10_CXXFLAGS)
-
-if USE_STATIC_LINK
-AM_LDFLAGS = -static
-endif
-
-CLEANFILES = *.gcno *.gcda
-
-TESTS_ENVIRONMENT = \
- $(LIBTOOL) --mode=execute $(VALGRIND_COMMAND)
-
-TESTS =
-if HAVE_GTEST
-TESTS += run_unittests
-
-run_unittests_SOURCES = run_unittests.cc
-run_unittests_SOURCES += rdata_serialization_unittest.cc
-run_unittests_SOURCES += rdataset_unittest.cc
-run_unittests_SOURCES += domaintree_unittest.cc
-run_unittests_SOURCES += treenode_rrset_unittest.cc
-run_unittests_SOURCES += zone_table_unittest.cc
-run_unittests_SOURCES += zone_data_unittest.cc
-run_unittests_SOURCES += zone_finder_unittest.cc
-run_unittests_SOURCES += ../../tests/faked_nsec3.h ../../tests/faked_nsec3.cc
-run_unittests_SOURCES += memory_segment_test.h
-run_unittests_SOURCES += segment_object_holder_unittest.cc
-run_unittests_SOURCES += memory_client_unittest.cc
-
-run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
-run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
-
-run_unittests_LDADD = $(builddir)/../libdatasrc_memory.la
-run_unittests_LDADD += $(top_builddir)/src/lib/datasrc/libb10-datasrc.la
-run_unittests_LDADD += $(top_builddir)/src/lib/dns/libb10-dns++.la
-run_unittests_LDADD += $(top_builddir)/src/lib/util/unittests/libutil_unittests.la
-run_unittests_LDADD += $(top_builddir)/src/lib/util/libb10-util.la
-run_unittests_LDADD += $(top_builddir)/src/lib/testutils/libb10-testutils.la
-run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libb10-exceptions.la
-run_unittests_LDADD += $(top_builddir)/src/lib/log/libb10-log.la
-run_unittests_LDADD += $(GTEST_LDADD)
-endif
-
-noinst_PROGRAMS = $(TESTS)
diff --git a/src/lib/datasrc/memory/tests/domaintree_unittest.cc b/src/lib/datasrc/memory/tests/domaintree_unittest.cc
deleted file mode 100644
index ef90050..0000000
--- a/src/lib/datasrc/memory/tests/domaintree_unittest.cc
+++ /dev/null
@@ -1,1230 +0,0 @@
-// Copyright (C) 2010 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 <exceptions/exceptions.h>
-
-#include <util/memory_segment_local.h>
-
-#include <dns/name.h>
-#include <dns/rrclass.h>
-#include <dns/rrset.h>
-#include <dns/rrtype.h>
-#include <dns/rrttl.h>
-
-#include <datasrc/memory/domaintree.h>
-
-#include <dns/tests/unittest_util.h>
-
-using namespace std;
-using namespace isc;
-using namespace isc::dns;
-using isc::UnitTestUtil;
-using namespace isc::datasrc::memory;
-
-// XXX: some compilers cannot find class static constants used in
-// EXPECT_xxx macros, for which we need an explicit empty definition.
-const size_t Name::MAX_LABELS;
-
-/* The initial structure of dtree
- *
- * .
- * |
- * b
- * / \
- * a d.e.f
- * / | \
- * c | g.h
- * | |
- * w.y i
- * / | \ \
- * x | z k
- * | |
- * p j
- * / \
- * o q
- */
-
-namespace {
-
-void deleteData(int* i) {
- delete i;
-}
-
-typedef DomainTree<int> TestDomainTree;
-typedef DomainTreeNode<int> TestDomainTreeNode;
-typedef DomainTreeNodeChain<int> TestDomainTreeNodeChain;
-
-class TreeHolder {
-public:
- TreeHolder(util::MemorySegment& mem_sgmt, TestDomainTree* tree) :
- mem_sgmt_(mem_sgmt), tree_(tree)
- {}
- ~TreeHolder() {
- TestDomainTree::destroy(mem_sgmt_, tree_, deleteData);
- }
- TestDomainTree* get() { return (tree_); }
-private:
- util::MemorySegment& mem_sgmt_;
- TestDomainTree* tree_;
-};
-
-class DomainTreeTest : public::testing::Test {
-protected:
- DomainTreeTest() :
- dtree_holder_(mem_sgmt_, TestDomainTree::create(mem_sgmt_)),
- dtree_expose_empty_node_holder_(mem_sgmt_,
- TestDomainTree::create(mem_sgmt_, true)),
- dtree(*dtree_holder_.get()),
- dtree_expose_empty_node(*dtree_expose_empty_node_holder_.get()),
- cdtnode(NULL)
- {
- const char* const domain_names[] = {
- "c", "b", "a", "x.d.e.f", "z.d.e.f", "g.h", "i.g.h", "o.w.y.d.e.f",
- "j.z.d.e.f", "p.w.y.d.e.f", "q.w.y.d.e.f", "k.g.h"};
- int name_count = sizeof(domain_names) / sizeof(domain_names[0]);
- for (int i = 0; i < name_count; ++i) {
- dtree.insert(mem_sgmt_, Name(domain_names[i]), &dtnode);
- // Check the node doesn't have any data initially.
- EXPECT_EQ(static_cast<int*>(NULL),
- dtnode->setData(new int(i + 1)));
-
- dtree_expose_empty_node.insert(mem_sgmt_, Name(domain_names[i]),
- &dtnode);
- EXPECT_EQ(static_cast<int*>(NULL), dtnode->setData(new int(i + 1)));
- }
- }
-
- util::MemorySegmentLocal mem_sgmt_;
- TreeHolder dtree_holder_;
- TreeHolder dtree_expose_empty_node_holder_;
- TestDomainTree& dtree;
- TestDomainTree& dtree_expose_empty_node;
- TestDomainTreeNode* dtnode;
- const TestDomainTreeNode* cdtnode;
- uint8_t buf[LabelSequence::MAX_SERIALIZED_LENGTH];
-};
-
-TEST_F(DomainTreeTest, nodeCount) {
- EXPECT_EQ(15, dtree.getNodeCount());
-
- // Delete all nodes, then the count should be set to 0. This also tests
- // the behavior of deleteAllNodes().
- dtree.deleteAllNodes(mem_sgmt_, deleteData);
- EXPECT_EQ(0, dtree.getNodeCount());
-}
-
-TEST_F(DomainTreeTest, setGetData) {
- // set new data to an existing node. It should have some data.
- int* newdata = new int(11);
- int* olddata = dtnode->setData(newdata);
- EXPECT_NE(static_cast<int*>(NULL), olddata);
- deleteData(olddata);
- EXPECT_EQ(11, *(dtnode->getData()));
-
- // clear the node. we should get the new data back we just passed.
- olddata = dtnode->setData(NULL);
- EXPECT_EQ(newdata, olddata);
- deleteData(olddata);
-}
-
-TEST_F(DomainTreeTest, insertNames) {
- EXPECT_EQ(TestDomainTree::ALREADYEXISTS, dtree.insert(mem_sgmt_,
- Name("d.e.f"),
- &dtnode));
- EXPECT_EQ(Name("d.e.f"), dtnode->getName());
- EXPECT_EQ(15, dtree.getNodeCount());
-
- // insert not exist node
- EXPECT_EQ(TestDomainTree::SUCCESS, dtree.insert(mem_sgmt_, Name("0"),
- &dtnode));
- EXPECT_EQ(Name("0"), dtnode->getName());
- EXPECT_EQ(16, dtree.getNodeCount());
-
- EXPECT_EQ(TestDomainTree::SUCCESS, dtree.insert(mem_sgmt_,
- Name("example.com"),
- &dtnode));
- EXPECT_EQ(17, dtree.getNodeCount());
- // add data to it; also make sure it doesn't have data right now
- // (otherwise it would leak)
- EXPECT_EQ(static_cast<int*>(NULL), dtnode->setData(new int(12)));
-
- // return ALREADYEXISTS, since node "example.com" already has
- // been explicitly inserted
- EXPECT_EQ(TestDomainTree::ALREADYEXISTS, dtree.insert(mem_sgmt_,
- Name("example.com"),
- &dtnode));
- EXPECT_EQ(17, dtree.getNodeCount());
-
- // split the node "d.e.f"
- EXPECT_EQ(TestDomainTree::SUCCESS, dtree.insert(mem_sgmt_, Name("k.e.f"),
- &dtnode));
- EXPECT_EQ(Name("k"), dtnode->getName());
- EXPECT_EQ(19, dtree.getNodeCount());
-
- // split the node "g.h"
- EXPECT_EQ(TestDomainTree::ALREADYEXISTS, dtree.insert(mem_sgmt_, Name("h"),
- &dtnode));
- EXPECT_EQ(Name("h"), dtnode->getName());
- EXPECT_EQ(20, dtree.getNodeCount());
-
- // add child domain
- EXPECT_EQ(TestDomainTree::SUCCESS, dtree.insert(mem_sgmt_,
- Name("m.p.w.y.d.e.f"),
- &dtnode));
- EXPECT_EQ(Name("m"), dtnode->getName());
- EXPECT_EQ(21, dtree.getNodeCount());
- EXPECT_EQ(TestDomainTree::SUCCESS, dtree.insert(mem_sgmt_,
- Name("n.p.w.y.d.e.f"),
- &dtnode));
- EXPECT_EQ(Name("n"), dtnode->getName());
- EXPECT_EQ(22, dtree.getNodeCount());
-
- EXPECT_EQ(TestDomainTree::SUCCESS, dtree.insert(mem_sgmt_, Name("l.a"),
- &dtnode));
- EXPECT_EQ(Name("l"), dtnode->getName());
- EXPECT_EQ(23, dtree.getNodeCount());
-
- EXPECT_EQ(TestDomainTree::SUCCESS, dtree.insert(mem_sgmt_, Name("r.d.e.f"),
- &dtnode));
- EXPECT_EQ(TestDomainTree::SUCCESS, dtree.insert(mem_sgmt_, Name("s.d.e.f"),
- &dtnode));
- EXPECT_EQ(25, dtree.getNodeCount());
-
- EXPECT_EQ(TestDomainTree::SUCCESS, dtree.insert(mem_sgmt_,
- Name("h.w.y.d.e.f"),
- &dtnode));
-
- // add more nodes one by one to cover leftRotate and rightRotate
- EXPECT_EQ(TestDomainTree::ALREADYEXISTS, dtree.insert(mem_sgmt_, Name("f"),
- &dtnode));
- EXPECT_EQ(TestDomainTree::SUCCESS, dtree.insert(mem_sgmt_, Name("m"),
- &dtnode));
- EXPECT_EQ(TestDomainTree::SUCCESS, dtree.insert(mem_sgmt_, Name("nm"),
- &dtnode));
- EXPECT_EQ(TestDomainTree::SUCCESS, dtree.insert(mem_sgmt_, Name("om"),
- &dtnode));
- EXPECT_EQ(TestDomainTree::SUCCESS, dtree.insert(mem_sgmt_, Name("k"),
- &dtnode));
- EXPECT_EQ(TestDomainTree::SUCCESS, dtree.insert(mem_sgmt_, Name("l"),
- &dtnode));
- EXPECT_EQ(TestDomainTree::SUCCESS, dtree.insert(mem_sgmt_, Name("fe"),
- &dtnode));
- EXPECT_EQ(TestDomainTree::SUCCESS, dtree.insert(mem_sgmt_, Name("ge"),
- &dtnode));
- EXPECT_EQ(TestDomainTree::SUCCESS, dtree.insert(mem_sgmt_, Name("i"),
- &dtnode));
- EXPECT_EQ(TestDomainTree::SUCCESS, dtree.insert(mem_sgmt_, Name("ae"),
- &dtnode));
- EXPECT_EQ(TestDomainTree::SUCCESS, dtree.insert(mem_sgmt_, Name("n"),
- &dtnode));
-}
-
-TEST_F(DomainTreeTest, subTreeRoot) {
- // This is a testcase for a particular issue that went unchecked in
- // #2089's implementation, but was fixed in #2092. The issue was
- // that when a node was fissioned, FLAG_SUBTREE_ROOT was not being
- // copied correctly.
-
- EXPECT_EQ(TestDomainTree::ALREADYEXISTS,
- dtree_expose_empty_node.insert(mem_sgmt_, Name("d.e.f"),
- &dtnode));
- EXPECT_EQ(TestDomainTree::SUCCESS,
- dtree_expose_empty_node.insert(mem_sgmt_, Name("0"),
- &dtnode));
- EXPECT_EQ(TestDomainTree::SUCCESS,
- dtree_expose_empty_node.insert(mem_sgmt_, Name("example.com"),
- &dtnode));
- EXPECT_EQ(TestDomainTree::ALREADYEXISTS,
- dtree_expose_empty_node.insert(mem_sgmt_, Name("example.com"),
- &dtnode));
- EXPECT_EQ(TestDomainTree::SUCCESS,
- dtree_expose_empty_node.insert(mem_sgmt_, Name("k.e.f"),
- &dtnode));
-
- // "g.h" is not a subtree root
- EXPECT_EQ(TestDomainTree::EXACTMATCH,
- dtree_expose_empty_node.find(Name("g.h"), &dtnode));
- EXPECT_FALSE(dtnode->getFlag(TestDomainTreeNode::FLAG_SUBTREE_ROOT));
-
- // fission the node "g.h"
- EXPECT_EQ(TestDomainTree::ALREADYEXISTS,
- dtree_expose_empty_node.insert(mem_sgmt_, Name("h"),
- &dtnode));
-
- // the node "h" (h.down_ -> "g") should not be a subtree root. "g"
- // should be a subtree root.
- EXPECT_FALSE(dtnode->getFlag(TestDomainTreeNode::FLAG_SUBTREE_ROOT));
-
- // "g.h" should be a subtree root now.
- EXPECT_EQ(TestDomainTree::EXACTMATCH,
- dtree_expose_empty_node.find(Name("g.h"), &dtnode));
- EXPECT_TRUE(dtnode->getFlag(TestDomainTreeNode::FLAG_SUBTREE_ROOT));
-}
-
-TEST_F(DomainTreeTest, additionalNodeFission) {
- // These are additional nodeFission tests added by #2054's rewrite
- // of DomainTree::nodeFission(). These test specific corner cases that
- // are not covered by other tests.
-
- // Insert "t.0" (which becomes the left child of its parent)
- EXPECT_EQ(TestDomainTree::SUCCESS,
- dtree_expose_empty_node.insert(mem_sgmt_, Name("t.0"),
- &dtnode));
-
- // "t.0" is not a subtree root
- EXPECT_EQ(TestDomainTree::EXACTMATCH,
- dtree_expose_empty_node.find(Name("t.0"), &dtnode));
- EXPECT_FALSE(dtnode->getFlag(TestDomainTreeNode::FLAG_SUBTREE_ROOT));
-
- // fission the node "t.0"
- EXPECT_EQ(TestDomainTree::ALREADYEXISTS,
- dtree_expose_empty_node.insert(mem_sgmt_, Name("0"),
- &dtnode));
-
- // the node "0" ("0".down_ -> "t") should not be a subtree root. "t"
- // should be a subtree root.
- EXPECT_FALSE(dtnode->getFlag(TestDomainTreeNode::FLAG_SUBTREE_ROOT));
-
- // "t.0" should be a subtree root now.
- EXPECT_EQ(TestDomainTree::EXACTMATCH,
- dtree_expose_empty_node.find(Name("t.0"), &dtnode));
- EXPECT_TRUE(dtnode->getFlag(TestDomainTreeNode::FLAG_SUBTREE_ROOT));
-}
-
-TEST_F(DomainTreeTest, findName) {
- // find const dtnode
- // exact match
- EXPECT_EQ(TestDomainTree::EXACTMATCH, dtree.find(Name("a"), &cdtnode));
- EXPECT_EQ(Name("a"), cdtnode->getName());
-
- // not found
- EXPECT_EQ(TestDomainTree::NOTFOUND, dtree.find(Name("d.e.f"), &cdtnode));
- EXPECT_EQ(TestDomainTree::NOTFOUND, dtree.find(Name("y.d.e.f"), &cdtnode));
- EXPECT_EQ(TestDomainTree::NOTFOUND, dtree.find(Name("x"), &cdtnode));
- EXPECT_EQ(TestDomainTree::NOTFOUND, dtree.find(Name("m.n"), &cdtnode));
-
- // if we expose empty node, we can get the empty node created during insert
- EXPECT_EQ(TestDomainTree::EXACTMATCH,
- dtree_expose_empty_node.find(Name("d.e.f"), &cdtnode));
- EXPECT_EQ(TestDomainTree::EXACTMATCH,
- dtree_expose_empty_node.find(Name("w.y.d.e.f"), &cdtnode));
-
- // partial match
- EXPECT_EQ(TestDomainTree::PARTIALMATCH, dtree.find(Name("m.b"), &cdtnode));
- EXPECT_EQ(Name("b"), cdtnode->getName());
- EXPECT_EQ(TestDomainTree::PARTIALMATCH,
- dtree_expose_empty_node.find(Name("m.d.e.f"), &cdtnode));
-
- // find dtnode
- EXPECT_EQ(TestDomainTree::EXACTMATCH, dtree.find(Name("q.w.y.d.e.f"),
- &dtnode));
- EXPECT_EQ(Name("q"), dtnode->getName());
-}
-
-TEST_F(DomainTreeTest, findError) {
- // For the version that takes a node chain, the chain must be empty.
- TestDomainTreeNodeChain chain;
- EXPECT_EQ(TestDomainTree::EXACTMATCH, dtree.find(Name("a"), &cdtnode,
- chain));
- // trying to reuse the same chain. it should result in an exception.
- EXPECT_THROW(dtree.find(Name("a"), &cdtnode, chain),
- BadValue);
-}
-
-TEST_F(DomainTreeTest, flags) {
- EXPECT_EQ(TestDomainTree::SUCCESS, dtree.insert(mem_sgmt_,
- Name("flags.example"),
- &dtnode));
-
- // by default, flags are all off
- EXPECT_FALSE(dtnode->getFlag(TestDomainTreeNode::FLAG_CALLBACK));
-
- // set operation, by default it enables the flag
- dtnode->setFlag(TestDomainTreeNode::FLAG_CALLBACK);
- EXPECT_TRUE(dtnode->getFlag(TestDomainTreeNode::FLAG_CALLBACK));
-
- // try disable the flag explicitly
- dtnode->setFlag(TestDomainTreeNode::FLAG_CALLBACK, false);
- EXPECT_FALSE(dtnode->getFlag(TestDomainTreeNode::FLAG_CALLBACK));
-
- // try enable the flag explicitly
- dtnode->setFlag(TestDomainTreeNode::FLAG_CALLBACK, true);
- EXPECT_TRUE(dtnode->getFlag(TestDomainTreeNode::FLAG_CALLBACK));
-
- // setting an unknown flag will trigger an exception
- EXPECT_THROW(dtnode->setFlag(static_cast<TestDomainTreeNode::Flags>(2), true),
- isc::InvalidParameter);
-}
-
-bool
-testCallback(const TestDomainTreeNode&, bool* callback_checker) {
- *callback_checker = true;
- return (false);
-}
-
-template <typename T>
-void
-performCallbackTest(TestDomainTree& dtree,
- util::MemorySegmentLocal& mem_sgmt,
- const T& name_called,
- const T& name_not_called)
-{
- TestDomainTreeNode* dtnode;
- const TestDomainTreeNode* cdtnode;
-
- // by default callback isn't enabled
- EXPECT_EQ(TestDomainTree::SUCCESS, dtree.insert(mem_sgmt,
- Name("callback.example"),
- &dtnode));
- EXPECT_EQ(static_cast<int*>(NULL), dtnode->setData(new int(1)));
- EXPECT_FALSE(dtnode->getFlag(TestDomainTreeNode::FLAG_CALLBACK));
-
- // enable/re-disable callback
- dtnode->setFlag(TestDomainTreeNode::FLAG_CALLBACK);
- EXPECT_TRUE(dtnode->getFlag(TestDomainTreeNode::FLAG_CALLBACK));
- dtnode->setFlag(TestDomainTreeNode::FLAG_CALLBACK, false);
- EXPECT_FALSE(dtnode->getFlag(TestDomainTreeNode::FLAG_CALLBACK));
-
- // enable again for subsequent tests
- dtnode->setFlag(TestDomainTreeNode::FLAG_CALLBACK);
- // add more levels below and above the callback node for partial match.
- TestDomainTreeNode* subdtnode;
- EXPECT_EQ(TestDomainTree::SUCCESS, dtree.insert(mem_sgmt,
- Name("sub.callback.example"),
- &subdtnode));
- EXPECT_EQ(static_cast<int*>(NULL), subdtnode->setData(new int(2)));
- TestDomainTreeNode* parentdtnode;
- EXPECT_EQ(TestDomainTree::ALREADYEXISTS, dtree.insert(mem_sgmt,
- Name("example"),
- &parentdtnode));
- // the child/parent nodes shouldn't "inherit" the callback flag.
- // "dtnode" may be invalid due to the insertion, so we need to re-find
- // it.
- EXPECT_EQ(TestDomainTree::EXACTMATCH, dtree.find(Name("callback.example"),
- &dtnode));
- EXPECT_TRUE(dtnode->getFlag(TestDomainTreeNode::FLAG_CALLBACK));
- EXPECT_FALSE(subdtnode->getFlag(TestDomainTreeNode::FLAG_CALLBACK));
- EXPECT_FALSE(parentdtnode->getFlag(TestDomainTreeNode::FLAG_CALLBACK));
-
- // check if the callback is called from find()
- TestDomainTreeNodeChain node_path1;
- bool callback_called = false;
- EXPECT_EQ(TestDomainTree::EXACTMATCH,
- dtree.find(name_called, &cdtnode, node_path1,
- testCallback, &callback_called));
- EXPECT_TRUE(callback_called);
-
- // enable callback at the parent node, but it doesn't have data so
- // the callback shouldn't be called.
- TestDomainTreeNodeChain node_path2;
- parentdtnode->setFlag(TestDomainTreeNode::FLAG_CALLBACK);
- callback_called = false;
- EXPECT_EQ(TestDomainTree::EXACTMATCH,
- dtree.find(name_not_called, &cdtnode, node_path2,
- testCallback, &callback_called));
- EXPECT_FALSE(callback_called);
-}
-
-TEST_F(DomainTreeTest, callbackName) {
- const Name n1("sub.callback.example");
- const Name n2("callback.example");
-
- performCallbackTest(dtree, mem_sgmt_, n1, n2);
-}
-
-TEST_F(DomainTreeTest, callbackLabelSequence) {
- const Name n1("sub.callback.example");
- const Name n2("callback.example");
- const LabelSequence ls1(n1);
- const LabelSequence ls2(n2);
-
- performCallbackTest(dtree, mem_sgmt_, ls1, ls2);
-}
-
-TEST_F(DomainTreeTest, findInSubTree) {
- // For the version that takes a node chain, the chain must be empty.
- DomainTreeNodeChain<int> chain;
- bool flag;
-
- // Searching for a non-absolute (right-stripped) label sequence when
- // chain is empty should throw.
- const Name n0("w.y.d.e.f");
- LabelSequence ls0(n0);
- ls0.stripRight(1);
- EXPECT_THROW(dtree_expose_empty_node.find(ls0, &cdtnode, chain,
- testCallback, &flag),
- isc::BadValue);
-
- // First, find a sub-tree node
- chain.clear();
- const LabelSequence ls1(n0);
- DomainTree<int>::Result result =
- dtree_expose_empty_node.find(ls1, &cdtnode, chain,
- testCallback, &flag);
- EXPECT_EQ(DomainTree<int>::EXACTMATCH, result);
- EXPECT_EQ(n0, chain.getAbsoluteName());
-
- // Searching for an absolute label sequence when chain is already
- // populated should throw.
- const Name n2a("o");
- const LabelSequence ls2a(n2a);
- EXPECT_THROW(dtree_expose_empty_node.find(ls2a, &cdtnode, chain,
- testCallback, &flag),
- isc::BadValue);
-
- // Now, find "o.w.y.d.e.f." by right-stripping the "w.y.d.e.f."
- // suffix to "o" (non-absolute).
- const Name n2("o.w.y.d.e.f");
- LabelSequence ls2(n2);
- ls2.stripRight(6);
- EXPECT_EQ("o", ls2.toText());
-
- result = dtree_expose_empty_node.find(ls2, &cdtnode, chain,
- testCallback, &flag);
- EXPECT_EQ(DomainTree<int>::EXACTMATCH, result);
- EXPECT_EQ(n2, chain.getAbsoluteName());
-
- // Another test. Start with "d.e.f." node.
- chain.clear();
- const Name n3("d.e.f");
- const LabelSequence ls3(n3);
- result =
- dtree_expose_empty_node.find(ls3, &cdtnode, chain,
- testCallback, &flag);
- EXPECT_EQ(DomainTree<int>::EXACTMATCH, result);
- EXPECT_EQ(n3, chain.getAbsoluteName());
-
- // Now, find "o.w.y.d.e.f." by right-stripping the "w.y.d.e.f."
- // suffix to "o.w.y" (non-absolute).
- const Name n4("o.w.y.d.e.f");
- LabelSequence ls4(n2);
- ls4.stripRight(4);
- EXPECT_EQ("o.w.y", ls4.toText());
-
- result = dtree_expose_empty_node.find(ls4, &cdtnode, chain,
- testCallback, &flag);
- EXPECT_EQ(DomainTree<int>::EXACTMATCH, result);
- EXPECT_EQ(n4, chain.getAbsoluteName());
-}
-
-TEST_F(DomainTreeTest, findInSubTreeSameLabelSequence) {
- // For the version that takes a node chain, the chain must be empty.
- DomainTreeNodeChain<int> chain;
- bool flag;
-
- const Name n1("c.g.h");
-
- // First insert a "c.g.h." node.
- dtree_expose_empty_node.insert(mem_sgmt_, n1, &dtnode);
-
- /* Now, the tree looks like:
- *
- * .
- * |
- * b
- * / \
- * a d.e.f
- * / | \____
- * c | \
- * | g.h
- * | |
- * w.y i
- * / | \ / \
- * x | z c k
- * | |
- * p j
- * / \
- * o q
- */
-
- // Make a non-absolute label sequence. We will search for this same
- // sequence in two places in the tree.
- LabelSequence ls1(n1);
- ls1.stripRight(3);
- EXPECT_EQ("c", ls1.toText());
-
- // First, find "g.h."
- const Name n2("g.h");
- const LabelSequence ls2(n2);
- DomainTree<int>::Result result =
- dtree_expose_empty_node.find(ls2, &cdtnode, chain,
- testCallback, &flag);
- EXPECT_EQ(DomainTree<int>::EXACTMATCH, result);
- EXPECT_EQ(n2, chain.getAbsoluteName());
-
- // Now, find "c.g.h." by searching just the non-absolute ls1 label
- // sequence.
- result = dtree_expose_empty_node.find(ls1, &cdtnode, chain,
- testCallback, &flag);
- EXPECT_EQ(DomainTree<int>::EXACTMATCH, result);
- EXPECT_EQ(n1, chain.getAbsoluteName());
-
- // Now, find "." (the root node)
- chain.clear();
- const Name n3(".");
- const LabelSequence ls3(n3);
- result =
- dtree_expose_empty_node.find(ls3, &cdtnode, chain,
- testCallback, &flag);
- EXPECT_EQ(DomainTree<int>::EXACTMATCH, result);
- EXPECT_EQ(n3, chain.getAbsoluteName());
-
- // Now, find "c." by searching just the non-absolute ls1 label
- // sequence.
- result = dtree_expose_empty_node.find(ls1, &cdtnode, chain,
- testCallback, &flag);
- EXPECT_EQ(DomainTree<int>::EXACTMATCH, result);
- EXPECT_EQ(Name("c."), chain.getAbsoluteName());
-}
-
-TEST_F(DomainTreeTest, chainLevel) {
- TestDomainTreeNodeChain chain;
-
- // by default there should be no level in the chain.
- EXPECT_EQ(0, chain.getLevelCount());
-
- // insert one node to the tree and find it. there should be exactly
- // one level in the chain.
- TreeHolder tree_holder(mem_sgmt_, TestDomainTree::create(mem_sgmt_, true));
- TestDomainTree& tree(*tree_holder.get());
- Name node_name(Name::ROOT_NAME());
- EXPECT_EQ(TestDomainTree::SUCCESS, tree.insert(mem_sgmt_, node_name,
- &dtnode));
- EXPECT_EQ(TestDomainTree::EXACTMATCH,
- tree.find(node_name, &cdtnode, chain));
- EXPECT_EQ(1, chain.getLevelCount());
-
- // Check the name of the found node (should have '.' as both non-absolute
- // and absolute name
- EXPECT_EQ(".", cdtnode->getLabels().toText());
- EXPECT_EQ(".", cdtnode->getAbsoluteLabels(buf).toText());
-
- /*
- * Now creating a possibly deepest tree with MAX_LABELS levels.
- * it should look like:
- * (.)
- * |
- * a
- * |
- * a
- * : (MAX_LABELS - 1) "a"'s
- *
- * then confirm that find() for the deepest name succeeds without any
- * disruption, and the resulting chain has the expected level.
- * Note that the root name (".") solely belongs to a single level,
- * so the levels begin with 2.
- */
- for (unsigned int i = 2; i <= Name::MAX_LABELS; ++i) {
- node_name = Name("a.").concatenate(node_name);
- EXPECT_EQ(TestDomainTree::SUCCESS, tree.insert(mem_sgmt_, node_name,
- &dtnode));
- TestDomainTreeNodeChain found_chain;
- EXPECT_EQ(TestDomainTree::EXACTMATCH,
- tree.find(node_name, &cdtnode, found_chain));
- EXPECT_EQ(i, found_chain.getLevelCount());
-
- // The non-absolute name should only have the first label
- EXPECT_EQ("a", cdtnode->getLabels().toText());
- // But the absolute name should have all labels
- EXPECT_EQ(node_name.toText(),
- cdtnode->getAbsoluteLabels(buf).toText());
- }
-
- // Confirm the last inserted name has the possible maximum length with
- // maximum label count. This confirms the dtree and chain level cannot
- // be larger.
- EXPECT_EQ(Name::MAX_LABELS, node_name.getLabelCount());
- EXPECT_THROW(node_name.concatenate(Name("a.")), TooLongName);
-}
-
-TEST_F(DomainTreeTest, getAbsoluteNameError) {
- // an empty chain isn't allowed.
- TestDomainTreeNodeChain chain;
- EXPECT_THROW(chain.getAbsoluteName(), BadValue);
-}
-
-/*
- * The domain order should be:
- * ., a, b, c, d.e.f, x.d.e.f, w.y.d.e.f, o.w.y.d.e.f, p.w.y.d.e.f,
- * q.w.y.d.e.f, z.d.e.f, j.z.d.e.f, g.h, i.g.h, k.g.h
- * . (no data, can't be found)
- * |
- * b
- * / \
- * a d.e.f
- * / | \
- * c | g.h
- * | |
- * w.y i
- * / | \ \
- * x | z k
- * | |
- * p j
- * / \
- * o q
- */
-const char* const names[] = {
- "a", "b", "c", "d.e.f", "x.d.e.f", "w.y.d.e.f", "o.w.y.d.e.f",
- "p.w.y.d.e.f", "q.w.y.d.e.f", "z.d.e.f", "j.z.d.e.f",
- "g.h", "i.g.h", "k.g.h"};
-const size_t name_count(sizeof(names) / sizeof(*names));
-
-const char* const upper_node_names[] = {
- ".", ".", ".", ".", "d.e.f", "d.e.f", "w.y.d.e.f",
- "w.y.d.e.f", "w.y.d.e.f", "d.e.f", "z.d.e.f",
- ".", "g.h", "g.h"};
-
-TEST_F(DomainTreeTest, getUpperNode) {
- TestDomainTreeNodeChain node_path;
- const TestDomainTreeNode* node = NULL;
- EXPECT_EQ(TestDomainTree::EXACTMATCH,
- dtree_expose_empty_node.find(Name(names[0]),
- &node,
- node_path));
- for (int i = 0; i < name_count; ++i) {
- EXPECT_NE(static_cast<void*>(NULL), node);
-
- const TestDomainTreeNode* upper_node = node->getUpperNode();
- if (upper_node_names[i] != NULL) {
- const TestDomainTreeNode* upper_node2 = NULL;
- EXPECT_EQ(TestDomainTree::EXACTMATCH,
- dtree_expose_empty_node.find(Name(upper_node_names[i]),
- &upper_node2));
- EXPECT_NE(static_cast<void*>(NULL), upper_node2);
- EXPECT_EQ(upper_node, upper_node2);
- } else {
- EXPECT_EQ(static_cast<void*>(NULL), upper_node);
- }
-
- node = dtree_expose_empty_node.nextNode(node_path);
- }
-
- // We should have reached the end of the tree.
- EXPECT_EQ(static_cast<void*>(NULL), node);
-}
-
-TEST_F(DomainTreeTest, nextNode) {
- TestDomainTreeNodeChain node_path;
- const TestDomainTreeNode* node = NULL;
- EXPECT_EQ(TestDomainTree::EXACTMATCH,
- dtree.find(Name(names[0]), &node, node_path));
- for (int i = 0; i < name_count; ++i) {
- EXPECT_NE(static_cast<void*>(NULL), node);
- EXPECT_EQ(Name(names[i]), node_path.getAbsoluteName());
- node = dtree.nextNode(node_path);
- }
-
- // We should have reached the end of the tree.
- EXPECT_EQ(static_cast<void*>(NULL), node);
-}
-
-// Just walk using previousNode until the beginning of the tree and check it is
-// OK
-//
-// dtree - the tree to walk
-// node - result of previous call to find(), starting position of the walk
-// node_path - the path from the previous call to find(), will be modified
-// chain_length - the number of names that should be in the chain to be walked
-// (0 means it should be empty, 3 means 'a', 'b' and 'c' should be there -
-// this is always from the beginning of the names[] list).
-// skip_first - if this is false, the node should already contain the node with
-// the first name of the chain. If it is true, the node should be NULL
-// (true is for finds that return no match, false for the ones that return
-// match)
-void
-previousWalk(TestDomainTree& dtree, const TestDomainTreeNode* node,
- TestDomainTreeNodeChain& node_path, size_t chain_length,
- bool skip_first)
-{
- if (skip_first) {
- // If the first is not found, this is supposed to be NULL and we skip
- // it in our checks.
- EXPECT_EQ(static_cast<void*>(NULL), node);
- node = dtree.previousNode(node_path);
- }
- for (size_t i(chain_length); i > 0; --i) {
- EXPECT_NE(static_cast<void*>(NULL), node);
- EXPECT_EQ(Name(names[i - 1]), node_path.getAbsoluteName());
- // Find the node at the path and check the value is the same
- // (that it really returns the correct corresponding node)
- //
- // The "empty" nodes can not be found
- if (node->getData()) {
- const TestDomainTreeNode* node2(NULL);
- TestDomainTreeNodeChain node_path2;
- EXPECT_EQ(TestDomainTree::EXACTMATCH,
- dtree.find(Name(names[i - 1]), &node2, node_path2));
- EXPECT_EQ(node, node2);
- }
- node = dtree.previousNode(node_path);
- }
-
- // We should have reached the start of the tree.
- ASSERT_NE(static_cast<void*>(NULL), node);
- EXPECT_EQ(".", node->getLabels().toText());
-
- // With one more call it results in NULL
- node = dtree.previousNode(node_path);
- EXPECT_EQ(static_cast<void*>(NULL), node);
-
- // Calling previousNode() yet again should still return NULL without
- // fail.
- node = dtree.previousNode(node_path);
- EXPECT_EQ(static_cast<void*>(NULL), node);
-}
-
-// Check the previousNode
-TEST_F(DomainTreeTest, previousNode) {
- // First, iterate the whole tree from the end to the beginning.
- TestDomainTreeNodeChain node_path;
- EXPECT_THROW(dtree.previousNode(node_path), isc::BadValue) <<
- "Throw before a search was done on the path";
- const TestDomainTreeNode* node(NULL);
- {
- SCOPED_TRACE("Iterate through");
- EXPECT_EQ(TestDomainTree::EXACTMATCH,
- dtree.find(Name(names[name_count - 1]), &node, node_path));
- previousWalk(dtree, node, node_path, name_count, false);
- node = NULL;
- node_path.clear();
- }
-
- {
- SCOPED_TRACE("Iterate from the middle");
- // Now, start somewhere in the middle, but within the real node.
- EXPECT_EQ(TestDomainTree::EXACTMATCH,
- dtree.find(Name(names[4]), &node, node_path));
- previousWalk(dtree, node, node_path, 5, false);
- node = NULL;
- node_path.clear();
- }
-
- {
- SCOPED_TRACE("Start at the first");
- // If we start at the lowest (which is "a"), we get to the beginning
- // right away.
- EXPECT_EQ(TestDomainTree::EXACTMATCH,
- dtree.find(Name(names[0]), &node, node_path));
- EXPECT_NE(static_cast<void*>(NULL), node);
- node = dtree.previousNode(node_path);
- ASSERT_NE(static_cast<void*>(NULL), node);
- EXPECT_EQ(".", node->getLabels().toText());
- node = NULL;
- node_path.clear();
- }
-
- {
- SCOPED_TRACE("Start before the first");
- // If we start before the lowest (. < 0. < a.), we should not get a
- // node. Its previous node should be the root.
- EXPECT_EQ(TestDomainTree::NOTFOUND,
- dtree.find<void*>(Name("0"), &node, node_path, NULL, NULL));
- EXPECT_EQ(static_cast<void*>(NULL), node);
- node = dtree.previousNode(node_path);
- ASSERT_NE(static_cast<void*>(NULL), node);
- EXPECT_EQ(".", node->getLabels().toText());
- node = NULL;
- node_path.clear();
- }
-
- {
- SCOPED_TRACE("Start after the last");
- EXPECT_EQ(TestDomainTree::NOTFOUND,
- dtree.find(Name("z"), &node, node_path));
- previousWalk(dtree, node, node_path, name_count, true);
- node = NULL;
- node_path.clear();
- }
-
- {
- SCOPED_TRACE("Start below a leaf");
- // We exit a leaf by going down. We should start by the one
- // we exited - 'c' (actually, we should get it by the find, as partial
- // match).
- EXPECT_EQ(TestDomainTree::PARTIALMATCH,
- dtree.find(Name("b.c"), &node, node_path));
- previousWalk(dtree, node, node_path, 3, false);
- node = NULL;
- node_path.clear();
- }
-
- {
- SCOPED_TRACE("Start to the right of a leaf");
- // When searching for this, we exit the 'x' node to the right side,
- // so we should go x afterwards.
-
- // The d.e.f is empty node, so it is hidden by find. Therefore NOTFOUND
- // and not PARTIALMATCH.
- EXPECT_EQ(TestDomainTree::NOTFOUND,
- dtree.find(Name("xy.d.e.f"), &node, node_path));
- previousWalk(dtree, node, node_path, 5, true);
- node = NULL;
- node_path.clear();
- }
-
- {
- SCOPED_TRACE("Start to the left of a leaf");
- // This is similar to the previous, but we exit the 'z' leaf to the
- // left side, so should not visit z at all then.
-
- // The d.e.f is empty node, so it is hidden by find. Therefore NOTFOUND
- // and not PARTIALMATCH.
- EXPECT_EQ(TestDomainTree::NOTFOUND,
- dtree.find(Name("yz.d.e.f"), &node, node_path));
- previousWalk(dtree, node, node_path, 9, true);
- node = NULL;
- node_path.clear();
- }
-
- {
- SCOPED_TRACE("Start to the right of a parent");
- // When searching for this, we exit the 'g.h' node to the right
- // side, so we should go to g.h's children afterwards.
-
- // 'g.h' is an empty node, so we get a NOTFOUND and not
- // PARTIALMATCH.
- EXPECT_EQ(TestDomainTree::NOTFOUND,
- dtree.find(Name("x.h"), &node, node_path));
- // 'g.h' is the COMMONANCESTOR.
- EXPECT_EQ(node_path.getLastComparedNode()->getName(), Name("g.h"));
- EXPECT_EQ(NameComparisonResult::COMMONANCESTOR,
- node_path.getLastComparisonResult().getRelation());
- // find() exits to the right of 'g.h'
- EXPECT_GT(node_path.getLastComparisonResult().getOrder(), 0);
- // We then descend into 'i.g.h' and walk all the nodes in the
- // tree.
- previousWalk(dtree, node, node_path, name_count, true);
- node = NULL;
- node_path.clear();
- }
-
- {
- SCOPED_TRACE("Start inside a wrong node");
- // The d.e.f is a single node, but we want only part of it. We
- // should start iterating before it.
- EXPECT_EQ(TestDomainTree::NOTFOUND,
- dtree.find(Name("e.f"), &node, node_path));
- previousWalk(dtree, node, node_path, 3, true);
- node = NULL;
- node_path.clear();
- }
-
- {
- SCOPED_TRACE("Lookup in empty tree");
- // Just check it doesn't crash, etc.
- TreeHolder tree_holder(mem_sgmt_, TestDomainTree::create(mem_sgmt_));
- TestDomainTree& empty_tree(*tree_holder.get());
- EXPECT_EQ(TestDomainTree::NOTFOUND,
- empty_tree.find(Name("x"), &node, node_path));
- EXPECT_EQ(static_cast<void*>(NULL), node);
- EXPECT_EQ(static_cast<void*>(NULL),
- empty_tree.previousNode(node_path));
- node = NULL;
- node_path.clear();
- }
-}
-
-TEST_F(DomainTreeTest, nextNodeError) {
- // Empty chain for nextNode() is invalid.
- TestDomainTreeNodeChain chain;
- EXPECT_THROW(dtree.nextNode(chain), BadValue);
-}
-
-// A helper function for getLastComparedNode() below.
-void
-comparisonChecks(const TestDomainTreeNodeChain& chain,
- int expected_order, int expected_common_labels,
- NameComparisonResult::NameRelation expected_reln)
-{
- if (expected_order > 0) {
- EXPECT_LT(0, chain.getLastComparisonResult().getOrder());
- } else if (expected_order < 0) {
- EXPECT_GT(0, chain.getLastComparisonResult().getOrder());
- } else {
- EXPECT_EQ(0, chain.getLastComparisonResult().getOrder());
- }
- EXPECT_EQ(expected_common_labels,
- chain.getLastComparisonResult().getCommonLabels());
- EXPECT_EQ(expected_reln,
- chain.getLastComparisonResult().getRelation());
-}
-
-TEST_F(DomainTreeTest, getLastComparedNode) {
- TestDomainTree& tree = dtree_expose_empty_node; // use the "empty OK" mode
- TestDomainTreeNodeChain chain;
-
- // initially there should be no 'last compared'.
- EXPECT_EQ(static_cast<void*>(NULL), chain.getLastComparedNode());
-
- // A search for an empty tree should result in no 'last compared', too.
- TreeHolder tree_holder(mem_sgmt_, TestDomainTree::create(mem_sgmt_));
- TestDomainTree& empty_tree(*tree_holder.get());
- EXPECT_EQ(TestDomainTree::NOTFOUND,
- empty_tree.find(Name("a"), &cdtnode, chain));
- EXPECT_EQ(static_cast<void*>(NULL), chain.getLastComparedNode());
- chain.clear();
-
- const TestDomainTreeNode* expected_node = NULL;
-
- // Exact match case. The returned node should be last compared.
- EXPECT_EQ(TestDomainTree::EXACTMATCH,
- tree.find(Name("x.d.e.f"), &expected_node, chain));
- EXPECT_EQ(expected_node, chain.getLastComparedNode());
- // 1 = # labels of "x" (note: excluding ".")
- comparisonChecks(chain, 0, 1, NameComparisonResult::EQUAL);
- chain.clear();
-
- // Partial match, search stopped at the matching node, which should be
- // the last compared node.
- EXPECT_EQ(TestDomainTree::EXACTMATCH,
- tree.find(Name("k.g.h"), &expected_node));
- EXPECT_EQ(TestDomainTree::PARTIALMATCH,
- tree.find(Name("x.k.g.h"), &cdtnode, chain));
- EXPECT_EQ(expected_node, chain.getLastComparedNode());
- // k.g.h < x.k.g.h, 1 = # labels of "k"
- comparisonChecks(chain, 1, 1, NameComparisonResult::SUBDOMAIN);
- chain.clear();
-
- // Partial match, search stopped in the subtree below the matching node
- // after following a left branch.
- EXPECT_EQ(TestDomainTree::EXACTMATCH,
- tree.find(Name("x.d.e.f"), &expected_node));
- EXPECT_EQ(TestDomainTree::PARTIALMATCH,
- tree.find(Name("a.d.e.f"), &cdtnode, chain));
- EXPECT_EQ(expected_node, chain.getLastComparedNode());
- // a < x, no common labels
- comparisonChecks(chain, -1, 0, NameComparisonResult::NONE);
- chain.clear();
-
- // Partial match, search stopped in the subtree below the matching node
- // after following a right branch.
- EXPECT_EQ(TestDomainTree::EXACTMATCH,
- tree.find(Name("z.d.e.f"), &expected_node));
- EXPECT_EQ(TestDomainTree::PARTIALMATCH,
- tree.find(Name("zz.d.e.f"), &cdtnode, chain));
- EXPECT_EQ(expected_node, chain.getLastComparedNode());
- // zz > z, no common label
- comparisonChecks(chain, 1, 0, NameComparisonResult::NONE);
- chain.clear();
-
- // Partial match, search stopped at a node for a super domain of the
- // search name in the subtree below the matching node.
- EXPECT_EQ(TestDomainTree::EXACTMATCH,
- tree.find(Name("w.y.d.e.f"), &expected_node));
- EXPECT_EQ(TestDomainTree::PARTIALMATCH,
- tree.find(Name("y.d.e.f"), &cdtnode, chain));
- EXPECT_EQ(expected_node, chain.getLastComparedNode());
- // y < w.y, 1 = # labels of "y"
- comparisonChecks(chain, -1, 1, NameComparisonResult::SUPERDOMAIN);
- chain.clear();
-
- // Partial match, search stopped at a node that share a common ancestor
- // with the search name in the subtree below the matching node.
- // (the expected node is the same as the previous case)
- EXPECT_EQ(TestDomainTree::PARTIALMATCH,
- tree.find(Name("z.y.d.e.f"), &cdtnode, chain));
- EXPECT_EQ(expected_node, chain.getLastComparedNode());
- // z.y > w.y, 1 = # labels of "y"
- comparisonChecks(chain, 1, 1, NameComparisonResult::COMMONANCESTOR);
- chain.clear();
-
- // Search stops in the highest level (under ".") after following a left
- // branch. (find() still returns PARTIALMATCH due to the top level ".")
- EXPECT_EQ(TestDomainTree::EXACTMATCH, tree.find(Name("c"), &expected_node));
- EXPECT_EQ(TestDomainTree::PARTIALMATCH,
- tree.find(Name("bb"), &cdtnode, chain));
- EXPECT_EQ(expected_node, chain.getLastComparedNode());
- // bb < c, no common label
- comparisonChecks(chain, -1, 0, NameComparisonResult::NONE);
- chain.clear();
-
- // Search stops in the highest level (under ".") after following a right
- // branch. (the expected node is the same as the previous case)
- EXPECT_EQ(TestDomainTree::PARTIALMATCH,
- tree.find(Name("d"), &cdtnode, chain));
- EXPECT_EQ(expected_node, chain.getLastComparedNode());
- // d > c, no common label
- comparisonChecks(chain, 1, 0, NameComparisonResult::NONE);
- chain.clear();
-}
-
-TEST_F(DomainTreeTest, dumpTree) {
- std::ostringstream str;
- std::ostringstream str2;
- dtree.dumpTree(str);
- str2 << "tree has 15 node(s)\n"
- ". (black) [invisible] [subtreeroot]\n"
- " begin down from .\n"
- " b (black) [subtreeroot]\n"
- " a (black)\n"
- " NULL\n"
- " NULL\n"
- " d.e.f (black) [invisible]\n"
- " begin down from d.e.f\n"
- " w.y (black) [invisible] [subtreeroot]\n"
- " begin down from w.y\n"
- " p (black) [subtreeroot]\n"
- " o (red)\n"
- " NULL\n"
- " NULL\n"
- " q (red)\n"
- " NULL\n"
- " NULL\n"
- " end down from w.y\n"
- " x (red)\n"
- " NULL\n"
- " NULL\n"
- " z (red)\n"
- " begin down from z\n"
- " j (black) [subtreeroot]\n"
- " NULL\n"
- " NULL\n"
- " end down from z\n"
- " NULL\n"
- " NULL\n"
- " end down from d.e.f\n"
- " c (red)\n"
- " NULL\n"
- " NULL\n"
- " g.h (red)\n"
- " begin down from g.h\n"
- " i (black) [subtreeroot]\n"
- " NULL\n"
- " k (red)\n"
- " NULL\n"
- " NULL\n"
- " end down from g.h\n"
- " NULL\n"
- " NULL\n"
- " end down from .\n"
- " NULL\n"
- " NULL\n";
- EXPECT_EQ(str2.str(), str.str());
-}
-
-TEST_F(DomainTreeTest, swap) {
- // Store info about the first tree
- std::ostringstream str1;
- dtree.dumpTree(str1);
- size_t count1(dtree.getNodeCount());
-
- // Create second one and store state
- TreeHolder tree_holder(mem_sgmt_, TestDomainTree::create(mem_sgmt_));
- TestDomainTree& tree2(*tree_holder.get());
- TestDomainTreeNode* node;
- tree2.insert(mem_sgmt_, Name("second"), &node);
- std::ostringstream str2;
- tree2.dumpTree(str2);
-
- // Swap them
- ASSERT_NO_THROW(tree2.swap(dtree));
-
- // Check their sizes
- ASSERT_EQ(1, dtree.getNodeCount());
- ASSERT_EQ(count1, tree2.getNodeCount());
-
- // And content
- std::ostringstream out;
- dtree.dumpTree(out);
- ASSERT_EQ(str2.str(), out.str());
- out.str("");
- tree2.dumpTree(out);
- ASSERT_EQ(str1.str(), out.str());
-}
-
-// Matching in the "root zone" may be special (e.g. there's no parent,
-// any domain names should be considered a subdomain of it), so it makes
-// sense to test cases with the root zone explicitly.
-TEST_F(DomainTreeTest, root) {
- TreeHolder tree_holder(mem_sgmt_, TestDomainTree::create(mem_sgmt_));
- TestDomainTree& root(*tree_holder.get());
- root.insert(mem_sgmt_, Name::ROOT_NAME(), &dtnode);
- EXPECT_EQ(static_cast<int*>(NULL), dtnode->setData(new int(1)));
-
- EXPECT_EQ(TestDomainTree::EXACTMATCH,
- root.find(Name::ROOT_NAME(), &cdtnode));
- EXPECT_EQ(dtnode, cdtnode);
- EXPECT_EQ(TestDomainTree::PARTIALMATCH,
- root.find(Name("example.com"), &cdtnode));
- EXPECT_EQ(dtnode, cdtnode);
-
- // Insert a new name that better matches the query name. find() should
- // find the better one.
- root.insert(mem_sgmt_, Name("com"), &dtnode);
- EXPECT_EQ(static_cast<int*>(NULL), dtnode->setData(new int(2)));
- EXPECT_EQ(TestDomainTree::PARTIALMATCH,
- root.find(Name("example.com"), &cdtnode));
- EXPECT_EQ(dtnode, cdtnode);
-
- // Perform the same tests for the tree that allows matching against empty
- // nodes.
- TreeHolder tree_holder_emptyok(mem_sgmt_,
- TestDomainTree::create(mem_sgmt_, true));
- TestDomainTree& root_emptyok(*tree_holder_emptyok.get());
- root_emptyok.insert(mem_sgmt_, Name::ROOT_NAME(), &dtnode);
- EXPECT_EQ(TestDomainTree::EXACTMATCH,
- root_emptyok.find(Name::ROOT_NAME(), &cdtnode));
- EXPECT_EQ(dtnode, cdtnode);
- EXPECT_EQ(TestDomainTree::PARTIALMATCH,
- root_emptyok.find(Name("example.com"), &cdtnode));
- EXPECT_EQ(dtnode, cdtnode);
-
- root.insert(mem_sgmt_, Name("com"), &dtnode);
- EXPECT_EQ(TestDomainTree::PARTIALMATCH,
- root.find(Name("example.com"), &cdtnode));
- EXPECT_EQ(dtnode, cdtnode);
-}
-
-TEST_F(DomainTreeTest, getAbsoluteLabels) {
- // The full absolute names of the nodes in the tree
- // with the addition of the explicit root node
- const char* const domain_names[] = {
- "c", "b", "a", "x.d.e.f", "z.d.e.f", "g.h", "i.g.h", "o.w.y.d.e.f",
- "j.z.d.e.f", "p.w.y.d.e.f", "q.w.y.d.e.f", "k.g.h"};
- // The names of the nodes themselves, as they end up in the tree
- const char* const first_labels[] = {
- "c", "b", "a", "x", "z", "g.h", "i", "o",
- "j", "p", "q", "k"};
-
- const int name_count = sizeof(domain_names) / sizeof(domain_names[0]);
- for (int i = 0; i < name_count; ++i) {
- EXPECT_EQ(TestDomainTree::EXACTMATCH, dtree.find(Name(domain_names[i]),
- &cdtnode));
-
- // First make sure the names themselves are not absolute
- const LabelSequence ls(cdtnode->getLabels());
- EXPECT_EQ(first_labels[i], ls.toText());
- EXPECT_FALSE(ls.isAbsolute());
-
- // Now check the absolute names
- const LabelSequence abs_ls(cdtnode->getAbsoluteLabels(buf));
- EXPECT_EQ(Name(domain_names[i]).toText(), abs_ls.toText());
- EXPECT_TRUE(abs_ls.isAbsolute());
- }
-
- // Explicitly add and find a root node, to see that getAbsoluteLabels
- // also works when getLabels() already returns an absolute LabelSequence
- dtree.insert(mem_sgmt_, Name("."), &dtnode);
- dtnode->setData(new int(1));
-
- EXPECT_EQ(TestDomainTree::EXACTMATCH, dtree.find(Name("."), &cdtnode));
-
- EXPECT_TRUE(cdtnode->getLabels().isAbsolute());
- EXPECT_EQ(".", cdtnode->getLabels().toText());
- EXPECT_TRUE(cdtnode->getAbsoluteLabels(buf).isAbsolute());
- EXPECT_EQ(".", cdtnode->getAbsoluteLabels(buf).toText());
-}
-}
diff --git a/src/lib/datasrc/memory/tests/memory_client_unittest.cc b/src/lib/datasrc/memory/tests/memory_client_unittest.cc
deleted file mode 100644
index fcee0be..0000000
--- a/src/lib/datasrc/memory/tests/memory_client_unittest.cc
+++ /dev/null
@@ -1,767 +0,0 @@
-// 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 <exceptions/exceptions.h>
-
-#include <util/memory_segment_local.h>
-
-#include <dns/name.h>
-#include <dns/rrclass.h>
-#include <dns/masterload.h>
-#include <dns/nsec3hash.h>
-#include <dns/rdata.h>
-#include <dns/rdataclass.h>
-#include <dns/rrsetlist.h>
-#include <dns/rrttl.h>
-#include <dns/masterload.h>
-
-#include <datasrc/result.h>
-#include <datasrc/data_source.h>
-#include <datasrc/memory/zone_data.h>
-#include <datasrc/memory/zone_table.h>
-#include <datasrc/memory/memory_client.h>
-
-#include <testutils/dnsmessage_test.h>
-
-#include "memory_segment_test.h"
-
-#include <gtest/gtest.h>
-
-#include <new> // for bad_alloc
-
-using namespace isc::dns;
-using namespace isc::dns::rdata;
-using namespace isc::datasrc;
-using namespace isc::datasrc::memory;
-using namespace isc::testutils;
-
-namespace {
-
-const char* rrset_data[] = {
- "example.org. 3600 IN SOA ns1.example.org. bugs.x.w.example.org. "
- "68 3600 300 3600000 3600",
- "a.example.org. 3600 IN A 192.168.0.1\n" // RRset containing 2 RRs
- "a.example.org. 3600 IN A 192.168.0.2",
- "a.example.org. 3600 IN RRSIG A 5 3 3600 20150420235959 20051021000000 "
- "40430 example.org. FAKEFAKE",
- "a.example.org. 3600 IN MX 10 mail.example.org.",
- "a.example.org. 3600 IN RRSIG MX 5 3 3600 20150420235959 20051021000000 "
- "40430 example.org. FAKEFAKEFAKE",
- NULL
-};
-
-// RRsets that emulate the "separate RRs" mode.
-const char* rrset_data_separated[] = {
- "example.org. 3600 IN SOA ns1.example.org. bugs.x.w.example.org. "
- "68 3600 300 3600000 3600",
- "a.example.org. 3600 IN A 192.168.0.1", // these two belong to the same
- "a.example.org. 3600 IN A 192.168.0.2", // RRset, but are separated.
- NULL
-};
-
-// Similar to the previous one, but with separated RRSIGs
-const char* rrset_data_sigseparated[] = {
- "example.org. 3600 IN SOA ns1.example.org. bugs.x.w.example.org. "
- "68 3600 300 3600000 3600",
- "a.example.org. 3600 IN A 192.168.0.1",
- "a.example.org. 3600 IN RRSIG A 5 3 3600 20150420235959 20051021000000 "
- "40430 example.org. FAKEFAKE",
- "a.example.org. 3600 IN RRSIG A 5 3 3600 20150420235959 20051021000000 "
- "53535 example.org. FAKEFAKE",
- NULL
-};
-
-class MockIterator : public ZoneIterator {
-private:
- MockIterator(const char** rrset_data_ptr, bool pass_empty_rrsig) :
- rrset_data_ptr_(rrset_data_ptr),
- pass_empty_rrsig_(pass_empty_rrsig)
- {
- }
-
- const char** rrset_data_ptr_;
- // If true, emulate an unexpected bogus case where an RRSIG RRset is
- // returned without the RDATA. For brevity allow tests tweak it directly.
- bool pass_empty_rrsig_;
-
-public:
- virtual ConstRRsetPtr getNextRRset() {
- if (*rrset_data_ptr_ == NULL) {
- return (ConstRRsetPtr());
- }
-
- ConstRRsetPtr result(textToRRset(*rrset_data_ptr_,
- RRClass::IN(), Name("example.org")));
- if (pass_empty_rrsig_ && result->getType() == RRType::RRSIG()) {
- result.reset(new RRset(result->getName(), result->getClass(),
- result->getType(), result->getTTL()));
- }
- ++rrset_data_ptr_;
-
- return (result);
- }
-
- virtual ConstRRsetPtr getSOA() const {
- isc_throw(isc::NotImplemented, "Not implemented");
- }
-
- static ZoneIteratorPtr makeIterator(const char** rrset_data_ptr,
- bool pass_empty_rrsig = false)
- {
- return (ZoneIteratorPtr(new MockIterator(rrset_data_ptr,
- pass_empty_rrsig)));
- }
-};
-
-class MemoryClientTest : public ::testing::Test {
-protected:
- MemoryClientTest() : zclass_(RRClass::IN()),
- client_(new InMemoryClient(mem_sgmt_, zclass_))
- {}
- ~MemoryClientTest() {
- if (client_ != NULL) {
- delete client_;
- }
- }
- void TearDown() {
- delete client_;
- client_ = NULL;
- EXPECT_TRUE(mem_sgmt_.allMemoryDeallocated()); // catch any leak here.
- }
- const RRClass zclass_;
- test::MemorySegmentTest mem_sgmt_;
- InMemoryClient* client_;
-};
-
-TEST_F(MemoryClientTest, loadRRsetDoesntMatchOrigin) {
- // Attempting to load example.org to example.com zone should result
- // in an exception.
- EXPECT_THROW(client_->load(Name("example.com"),
- TEST_DATA_DIR "/example.org-empty.zone"),
- MasterLoadError);
-}
-
-TEST_F(MemoryClientTest, loadErrorsInParsingZoneMustNotLeak1) {
- // Attempting to load broken example.org zone should result in an
- // exception. This should not leak ZoneData and other such
- // allocations.
- EXPECT_THROW(client_->load(Name("example.org"),
- TEST_DATA_DIR "/example.org-broken1.zone"),
- MasterLoadError);
- // Teardown checks for memory segment leaks
-}
-
-TEST_F(MemoryClientTest, loadErrorsInParsingZoneMustNotLeak2) {
- // Attempting to load broken example.org zone should result in an
- // exception. This should not leak ZoneData and other such
- // allocations.
- EXPECT_THROW(client_->load(Name("example.org"),
- TEST_DATA_DIR "/example.org-broken2.zone"),
- MasterLoadError);
- // Teardown checks for memory segment leaks
-}
-
-TEST_F(MemoryClientTest, loadNonExistentZoneFile) {
- EXPECT_THROW(client_->load(Name("example.org"),
- TEST_DATA_DIR "/somerandomfilename"),
- MasterLoadError);
- // Teardown checks for memory segment leaks
-}
-
-TEST_F(MemoryClientTest, loadEmptyZoneFileThrows) {
- // When an empty zone file is loaded, the origin doesn't even have
- // an SOA RR. This condition should be avoided, and hence load()
- // should throw when an empty zone is loaded.
-
- EXPECT_EQ(0, client_->getZoneCount());
-
- EXPECT_THROW(client_->load(Name("."),
- TEST_DATA_DIR "/empty.zone"),
- InMemoryClient::EmptyZone);
-
- EXPECT_EQ(0, client_->getZoneCount());
-
- // Teardown checks for memory segment leaks
-}
-
-TEST_F(MemoryClientTest, load) {
- // This is a simple load check for a "full" and correct zone that
- // should not result in any exceptions.
- client_->load(Name("example.org"),
- TEST_DATA_DIR "/example.org.zone");
-}
-
-TEST_F(MemoryClientTest, loadFromIterator) {
- client_->load(Name("example.org"),
- *MockIterator::makeIterator(rrset_data));
-
- ZoneIteratorPtr iterator(client_->getIterator(Name("example.org")));
-
- // First we have the SOA
- ConstRRsetPtr rrset(iterator->getNextRRset());
- EXPECT_TRUE(rrset);
- EXPECT_EQ(RRType::SOA(), rrset->getType());
-
- // RRType::MX() RRset
- rrset = iterator->getNextRRset();
- EXPECT_TRUE(rrset);
- EXPECT_EQ(RRType::MX(), rrset->getType());
- EXPECT_EQ(1, rrset->getRRsigDataCount()); // this RRset is signed
-
- // RRType::A() RRset
- rrset = iterator->getNextRRset();
- EXPECT_TRUE(rrset);
- EXPECT_EQ(RRType::A(), rrset->getType());
- EXPECT_EQ(1, rrset->getRRsigDataCount()); // also signed
-
- // There's nothing else in this iterator
- EXPECT_EQ(ConstRRsetPtr(), iterator->getNextRRset());
-
- // Iterating past the end should result in an exception
- EXPECT_THROW(iterator->getNextRRset(), isc::Unexpected);
-
- // Loading the zone with an iterator separating RRs of the same RRset
- // will fail because the resulting sequence doesn't meet assumptions of
- // the (current) in-memory implementation.
- EXPECT_THROW(client_->load(Name("example.org"),
- *MockIterator::makeIterator(
- rrset_data_separated)),
- InMemoryClient::AddError);
-
- // Similar to the previous case, but with separated RRSIGs.
- EXPECT_THROW(client_->load(Name("example.org"),
- *MockIterator::makeIterator(
- rrset_data_sigseparated)),
- InMemoryClient::AddError);
-
- // Emulating bogus iterator implementation that passes empty RRSIGs.
- EXPECT_THROW(client_->load(Name("example.org"),
- *MockIterator::makeIterator(rrset_data, true)),
- isc::Unexpected);
-}
-
-TEST_F(MemoryClientTest, loadMemoryAllocationFailures) {
- // Just to check that things get cleaned up
-
- for (int i = 1; i < 16; i++) {
- mem_sgmt_.setThrowCount(i);
- EXPECT_THROW(client_->load(Name("example.org"),
- TEST_DATA_DIR "/example.org.zone"),
- std::bad_alloc);
- }
- // Teardown checks for memory segment leaks
-}
-
-TEST_F(MemoryClientTest, loadNSEC3Signed) {
- client_->load(Name("example.org"),
- TEST_DATA_DIR "/example.org-nsec3-signed.zone");
-}
-
-TEST_F(MemoryClientTest, loadNSEC3SignedNoParam) {
- client_->load(Name("example.org"),
- TEST_DATA_DIR "/example.org-nsec3-signed-no-param.zone");
-}
-
-TEST_F(MemoryClientTest, loadReloadZone) {
- // Because we reload the same zone, also check that the zone count
- // doesn't increase.
- EXPECT_EQ(0, client_->getZoneCount());
-
- client_->load(Name("example.org"),
- TEST_DATA_DIR "/example.org-empty.zone");
- EXPECT_EQ(1, client_->getZoneCount());
-
- // Reload zone with same data
-
- client_->load(Name("example.org"),
- client_->getFileName(Name("example.org")));
- EXPECT_EQ(1, client_->getZoneCount());
-
- isc::datasrc::memory::ZoneTable::FindResult
- result(client_->findZone2(Name("example.org")));
- EXPECT_EQ(result::SUCCESS, result.code);
- EXPECT_NE(static_cast<ZoneData*>(NULL),
- result.zone_data);
-
- /* Check SOA */
- const ZoneNode* node = result.zone_data->getOriginNode();
- EXPECT_NE(static_cast<const ZoneNode*>(NULL), node);
-
- const RdataSet* set = node->getData();
- EXPECT_NE(static_cast<const RdataSet*>(NULL), set);
- EXPECT_EQ(RRType::SOA(), set->type);
-
- set = set->getNext();
- EXPECT_EQ(static_cast<const RdataSet*>(NULL), set);
-
- /* Check ns1.example.org */
- const ZoneTree& tree = result.zone_data->getZoneTree();
- ZoneTree::Result zresult(tree.find(Name("ns1.example.org"), &node));
- EXPECT_NE(ZoneTree::EXACTMATCH, zresult);
-
- // Reload zone with different data
-
- client_->load(Name("example.org"),
- TEST_DATA_DIR "/example.org-rrsigs.zone");
- EXPECT_EQ(1, client_->getZoneCount());
-
- isc::datasrc::memory::ZoneTable::FindResult
- result2(client_->findZone2(Name("example.org")));
- EXPECT_EQ(result::SUCCESS, result2.code);
- EXPECT_NE(static_cast<ZoneData*>(NULL),
- result2.zone_data);
-
- /* Check SOA */
- node = result2.zone_data->getOriginNode();
- EXPECT_NE(static_cast<const ZoneNode*>(NULL), node);
-
- set = node->getData();
- EXPECT_NE(static_cast<const RdataSet*>(NULL), set);
- EXPECT_EQ(RRType::SOA(), set->type);
-
- set = set->getNext();
- EXPECT_EQ(static_cast<const RdataSet*>(NULL), set);
-
- /* Check ns1.example.org */
- const ZoneTree& tree2 = result2.zone_data->getZoneTree();
- ZoneTree::Result zresult2(tree2.find(Name("ns1.example.org"), &node));
- EXPECT_EQ(ZoneTree::EXACTMATCH, zresult2);
- EXPECT_NE(static_cast<const ZoneNode*>(NULL), node);
-
- set = node->getData();
- EXPECT_NE(static_cast<const RdataSet*>(NULL), set);
- EXPECT_EQ(RRType::AAAA(), set->type);
-
- set = set->getNext();
- EXPECT_NE(static_cast<const RdataSet*>(NULL), set);
- EXPECT_EQ(RRType::A(), set->type);
-
- set = set->getNext();
- EXPECT_EQ(static_cast<const RdataSet*>(NULL), set);
-
- // Teardown checks for memory segment leaks
-}
-
-TEST_F(MemoryClientTest, loadDuplicateType) {
- // This should not result in any exceptions:
- client_->load(Name("example.org"),
- TEST_DATA_DIR "/example.org-duplicate-type.zone");
-
- // This should throw:
- EXPECT_THROW(client_->load(Name("example.org"),
- TEST_DATA_DIR
- "/example.org-duplicate-type-bad.zone"),
- InMemoryClient::AddError);
- // Teardown checks for memory segment leaks
-}
-
-TEST_F(MemoryClientTest, loadMultipleCNAMEThrows) {
- // Multiple CNAME RRs should throw.
- EXPECT_THROW(client_->load(Name("example.org"),
- TEST_DATA_DIR
- "/example.org-multiple-cname.zone"),
- InMemoryClient::AddError);
- // Teardown checks for memory segment leaks
-}
-
-TEST_F(MemoryClientTest, loadMultipleDNAMEThrows) {
- // Multiple DNAME RRs should throw.
- EXPECT_THROW(client_->load(Name("example.org"),
- TEST_DATA_DIR
- "/example.org-multiple-dname.zone"),
- InMemoryClient::AddError);
- // Teardown checks for memory segment leaks
-}
-
-TEST_F(MemoryClientTest, loadMultipleNSEC3Throws) {
- // Multiple NSEC3 RRs should throw.
- EXPECT_THROW(client_->load(Name("example.org"),
- TEST_DATA_DIR
- "/example.org-multiple-nsec3.zone"),
- InMemoryClient::AddError);
- // Teardown checks for memory segment leaks
-}
-
-TEST_F(MemoryClientTest, loadMultipleNSEC3PARAMThrows) {
- // Multiple NSEC3PARAM RRs should throw.
- EXPECT_THROW(client_->load(Name("example.org"),
- TEST_DATA_DIR
- "/example.org-multiple-nsec3param.zone"),
- InMemoryClient::AddError);
- // Teardown checks for memory segment leaks
-}
-
-TEST_F(MemoryClientTest, loadOutOfZoneThrows) {
- // Out of zone names should throw.
- EXPECT_THROW(client_->load(Name("example.org"),
- TEST_DATA_DIR
- "/example.org-out-of-zone.zone"),
- MasterLoadError);
- // Teardown checks for memory segment leaks
-}
-
-TEST_F(MemoryClientTest, loadWildcardNSThrows) {
- // Wildcard NS names should throw
- EXPECT_THROW(client_->load(Name("example.org"),
- TEST_DATA_DIR
- "/example.org-wildcard-ns.zone"),
- InMemoryClient::AddError);
- // Teardown checks for memory segment leaks
-}
-
-TEST_F(MemoryClientTest, loadWildcardDNAMEThrows) {
- // Wildcard DNAME names should throw
- EXPECT_THROW(client_->load(Name("example.org"),
- TEST_DATA_DIR
- "/example.org-wildcard-dname.zone"),
- InMemoryClient::AddError);
- // Teardown checks for memory segment leaks
-}
-
-TEST_F(MemoryClientTest, loadWildcardNSEC3Throws) {
- // Wildcard NSEC3 names should throw
- EXPECT_THROW(client_->load(Name("example.org"),
- TEST_DATA_DIR
- "/example.org-wildcard-nsec3.zone"),
- InMemoryClient::AddError);
- // Teardown checks for memory segment leaks
-}
-
-TEST_F(MemoryClientTest, loadNSEC3WithFewerLabelsThrows) {
- // NSEC3 names with labels != (origin_labels + 1) should throw
- EXPECT_THROW(client_->load(Name("example.org"),
- TEST_DATA_DIR
- "/example.org-nsec3-fewer-labels.zone"),
- InMemoryClient::AddError);
- // Teardown checks for memory segment leaks
-}
-
-TEST_F(MemoryClientTest, loadNSEC3WithMoreLabelsThrows) {
- // NSEC3 names with labels != (origin_labels + 1) should throw
- EXPECT_THROW(client_->load(Name("example.org"),
- TEST_DATA_DIR
- "/example.org-nsec3-more-labels.zone"),
- InMemoryClient::AddError);
- // Teardown checks for memory segment leaks
-}
-
-TEST_F(MemoryClientTest, loadCNAMEAndNotNSECThrows) {
- // CNAME and not NSEC should throw
- EXPECT_THROW(client_->load(Name("example.org"),
- TEST_DATA_DIR
- "/example.org-cname-and-not-nsec-1.zone"),
- InMemoryClient::AddError);
-
- EXPECT_THROW(client_->load(Name("example.org"),
- TEST_DATA_DIR
- "/example.org-cname-and-not-nsec-2.zone"),
- InMemoryClient::AddError);
-
- // Teardown checks for memory segment leaks
-}
-
-TEST_F(MemoryClientTest, loadDNAMEAndNSApex1) {
- // DNAME + NS (apex) is OK
- client_->load(Name("example.org"),
- TEST_DATA_DIR
- "/example.org-dname-ns-apex-1.zone");
- // Teardown checks for memory segment leaks
-}
-
-TEST_F(MemoryClientTest, loadDNAMEAndNSApex2) {
- // DNAME + NS (apex) is OK (reverse order)
- client_->load(Name("example.org"),
- TEST_DATA_DIR
- "/example.org-dname-ns-apex-2.zone");
- // Teardown checks for memory segment leaks
-}
-
-TEST_F(MemoryClientTest, loadDNAMEAndNSNonApex1) {
- // DNAME + NS (non-apex) must throw
- EXPECT_THROW(client_->load(Name("example.org"),
- TEST_DATA_DIR
- "/example.org-dname-ns-nonapex-1.zone"),
- InMemoryClient::AddError);
- // Teardown checks for memory segment leaks
-}
-
-TEST_F(MemoryClientTest, loadDNAMEAndNSNonApex2) {
- // DNAME + NS (non-apex) must throw (reverse order)
- EXPECT_THROW(client_->load(Name("example.org"),
- TEST_DATA_DIR
- "/example.org-dname-ns-nonapex-2.zone"),
- InMemoryClient::AddError);
- // Teardown checks for memory segment leaks
-}
-
-TEST_F(MemoryClientTest, loadRRSIGFollowsNothing) {
- // This causes the situation where an RRSIG is added without a covered
- // RRset. Such cases are currently rejected.
- EXPECT_THROW(client_->load(Name("example.org"),
- TEST_DATA_DIR
- "/example.org-rrsig-follows-nothing.zone"),
- InMemoryClient::AddError);
- // Teardown checks for memory segment leaks
-}
-
-TEST_F(MemoryClientTest, loadRRSIGs) {
- client_->load(Name("example.org"),
- TEST_DATA_DIR "/example.org-rrsigs.zone");
- EXPECT_EQ(1, client_->getZoneCount());
-}
-
-TEST_F(MemoryClientTest, loadRRSIGsRdataMixedCoveredTypes) {
- client_->load(Name("example.org"),
- TEST_DATA_DIR "/example.org-rrsigs.zone");
-
- RRsetPtr rrset(new RRset(Name("example.org"),
- RRClass::IN(), RRType::A(), RRTTL(3600)));
- rrset->addRdata(in::A("192.0.2.1"));
- rrset->addRdata(in::A("192.0.2.2"));
-
- RRsetPtr rrsig(new RRset(Name("example.org"), zclass_,
- RRType::RRSIG(), RRTTL(300)));
- rrsig->addRdata(generic::RRSIG("A 5 3 3600 20000101000000 20000201000000 "
- "12345 example.org. FAKEFAKEFAKE"));
- rrsig->addRdata(generic::RRSIG("NS 5 3 3600 20000101000000 20000201000000 "
- "54321 example.org. FAKEFAKEFAKEFAKE"));
- rrset->addRRsig(rrsig);
-
- EXPECT_THROW(client_->add(Name("example.org"), rrset),
- InMemoryClient::AddError);
-
- // Teardown checks for memory segment leaks
-}
-
-TEST_F(MemoryClientTest, getZoneCount) {
- EXPECT_EQ(0, client_->getZoneCount());
- client_->load(Name("example.org"), TEST_DATA_DIR "/example.org-empty.zone");
- EXPECT_EQ(1, client_->getZoneCount());
-}
-
-TEST_F(MemoryClientTest, getFileNameForNonExistentZone) {
- // Zone "example.org." doesn't exist
- EXPECT_TRUE(client_->getFileName(Name("example.org.")).empty());
-}
-
-TEST_F(MemoryClientTest, getFileName) {
- client_->load(Name("example.org"), TEST_DATA_DIR "/example.org-empty.zone");
- EXPECT_EQ(TEST_DATA_DIR "/example.org-empty.zone",
- client_->getFileName(Name("example.org")));
-}
-
-TEST_F(MemoryClientTest, getIteratorForNonExistentZone) {
- // Zone "." doesn't exist
- EXPECT_THROW(client_->getIterator(Name(".")), DataSourceError);
-}
-
-TEST_F(MemoryClientTest, getIterator) {
- client_->load(Name("example.org"), TEST_DATA_DIR "/example.org-empty.zone");
- ZoneIteratorPtr iterator(client_->getIterator(Name("example.org")));
-
- // First we have the SOA
- ConstRRsetPtr rrset_soa(iterator->getNextRRset());
- EXPECT_TRUE(rrset_soa);
- EXPECT_EQ(RRType::SOA(), rrset_soa->getType());
-
- // There's nothing else in this iterator
- EXPECT_EQ(ConstRRsetPtr(), iterator->getNextRRset());
-
- // Iterating past the end should result in an exception
- EXPECT_THROW(iterator->getNextRRset(), isc::Unexpected);
-}
-
-TEST_F(MemoryClientTest, getIteratorSeparateRRs) {
- client_->load(Name("example.org"),
- TEST_DATA_DIR "/example.org-multiple.zone");
-
- // separate_rrs = false
- ZoneIteratorPtr iterator(client_->getIterator(Name("example.org")));
-
- // First we have the SOA
- ConstRRsetPtr rrset(iterator->getNextRRset());
- EXPECT_TRUE(rrset);
- EXPECT_EQ(RRType::SOA(), rrset->getType());
-
- // Only one RRType::A() RRset
- rrset = iterator->getNextRRset();
- EXPECT_TRUE(rrset);
- EXPECT_EQ(RRType::A(), rrset->getType());
-
- // There's nothing else in this zone
- EXPECT_EQ(ConstRRsetPtr(), iterator->getNextRRset());
-
-
- // separate_rrs = true
- ZoneIteratorPtr iterator2(client_->getIterator(Name("example.org"), true));
-
- // First we have the SOA
- rrset = iterator2->getNextRRset();
- EXPECT_TRUE(rrset);
- EXPECT_EQ(RRType::SOA(), rrset->getType());
-
- // First RRType::A() RRset
- rrset = iterator2->getNextRRset();
- EXPECT_TRUE(rrset);
- EXPECT_EQ(RRType::A(), rrset->getType());
-
- // Second RRType::A() RRset
- rrset = iterator2->getNextRRset();
- EXPECT_TRUE(rrset);
- EXPECT_EQ(RRType::A(), rrset->getType());
-
- // There's nothing else in this iterator
- EXPECT_EQ(ConstRRsetPtr(), iterator2->getNextRRset());
-}
-
-TEST_F(MemoryClientTest, getIteratorGetSOAThrowsNotImplemented) {
- client_->load(Name("example.org"), TEST_DATA_DIR "/example.org-empty.zone");
- ZoneIteratorPtr iterator(client_->getIterator(Name("example.org")));
-
- // This method is not implemented.
- EXPECT_THROW(iterator->getSOA(), isc::NotImplemented);
-}
-
-TEST_F(MemoryClientTest, addRRsetToNonExistentZoneThrows) {
- // The zone "example.org" doesn't exist, so we can't add an RRset to
- // it.
- RRsetPtr rrset_a(new RRset(Name("example.org"), RRClass::IN(), RRType::A(),
- RRTTL(300)));
- rrset_a->addRdata(rdata::in::A("192.0.2.1"));
- EXPECT_THROW(client_->add(Name("example.org"), rrset_a), DataSourceError);
-}
-
-TEST_F(MemoryClientTest, addOutOfZoneThrows) {
- // Out of zone names should throw.
- client_->load(Name("example.org"),
- TEST_DATA_DIR "/example.org-empty.zone");
-
- RRsetPtr rrset_a(new RRset(Name("a.example.com"),
- RRClass::IN(), RRType::A(), RRTTL(300)));
- rrset_a->addRdata(rdata::in::A("192.0.2.1"));
-
- EXPECT_THROW(client_->add(Name("example.org"), rrset_a),
- OutOfZone);
- // Teardown checks for memory segment leaks
-}
-
-TEST_F(MemoryClientTest, addNullRRsetThrows) {
- client_->load(Name("example.org"),
- TEST_DATA_DIR "/example.org-rrsigs.zone");
-
- EXPECT_THROW(client_->add(Name("example.org"), ConstRRsetPtr()),
- InMemoryClient::NullRRset);
-
- // Teardown checks for memory segment leaks
-}
-
-TEST_F(MemoryClientTest, addEmptyRRsetThrows) {
- client_->load(Name("example.org"),
- TEST_DATA_DIR "/example.org-rrsigs.zone");
-
- RRsetPtr rrset_a(new RRset(Name("example.org"), RRClass::IN(), RRType::A(),
- RRTTL(300)));
- EXPECT_THROW(client_->add(Name("example.org"), rrset_a),
- InMemoryClient::AddError);
-
- // Teardown checks for memory segment leaks
-}
-
-TEST_F(MemoryClientTest, add) {
- client_->load(Name("example.org"), TEST_DATA_DIR "/example.org-empty.zone");
-
- // Add another RRset
- RRsetPtr rrset_a(new RRset(Name("example.org"), RRClass::IN(), RRType::A(),
- RRTTL(300)));
- rrset_a->addRdata(rdata::in::A("192.0.2.1"));
- client_->add(Name("example.org"), rrset_a);
-
- ZoneIteratorPtr iterator(client_->getIterator(Name("example.org")));
-
- // First we have the SOA
- ConstRRsetPtr rrset(iterator->getNextRRset());
- EXPECT_TRUE(rrset);
- EXPECT_EQ(RRType::A(), rrset->getType());
-
- rrset = iterator->getNextRRset();
- EXPECT_TRUE(rrset);
- EXPECT_EQ(RRType::SOA(), rrset->getType());
-
- // There's nothing else in this zone
- EXPECT_EQ(ConstRRsetPtr(), iterator->getNextRRset());
-}
-
-TEST_F(MemoryClientTest, findZoneThrowsNotImplemented) {
- // This method is not implemented.
- EXPECT_THROW(client_->findZone(Name(".")),
- isc::NotImplemented);
-}
-
-TEST_F(MemoryClientTest, findZone2) {
- client_->load(Name("example.org"),
- TEST_DATA_DIR "/example.org-rrsigs.zone");
-
- isc::datasrc::memory::ZoneTable::FindResult
- result(client_->findZone2(Name("example.com")));
- EXPECT_EQ(result::NOTFOUND, result.code);
- EXPECT_EQ(static_cast<ZoneData*>(NULL),
- result.zone_data);
-
- isc::datasrc::memory::ZoneTable::FindResult
- result2(client_->findZone2(Name("example.org")));
- EXPECT_EQ(result::SUCCESS, result2.code);
- EXPECT_NE(static_cast<ZoneData*>(NULL),
- result2.zone_data);
-
- /* Check SOA */
- const ZoneNode* node = result2.zone_data->getOriginNode();
- EXPECT_NE(static_cast<const ZoneNode*>(NULL), node);
-
- const RdataSet* set = node->getData();
- EXPECT_NE(static_cast<const RdataSet*>(NULL), set);
- EXPECT_EQ(RRType::SOA(), set->type);
-
- set = set->getNext();
- EXPECT_EQ(static_cast<const RdataSet*>(NULL), set);
-
- /* Check ns1.example.org */
- const ZoneTree& tree = result2.zone_data->getZoneTree();
- ZoneTree::Result result3(tree.find(Name("ns1.example.org"), &node));
- EXPECT_EQ(ZoneTree::EXACTMATCH, result3);
- EXPECT_NE(static_cast<const ZoneNode*>(NULL), node);
-
- set = node->getData();
- EXPECT_NE(static_cast<const RdataSet*>(NULL), set);
- EXPECT_EQ(RRType::AAAA(), set->type);
-
- set = set->getNext();
- EXPECT_NE(static_cast<const RdataSet*>(NULL), set);
- EXPECT_EQ(RRType::A(), set->type);
-
- set = set->getNext();
- EXPECT_EQ(static_cast<const RdataSet*>(NULL), set);
-}
-
-TEST_F(MemoryClientTest, getUpdaterThrowsNotImplemented) {
- // This method is not implemented.
- EXPECT_THROW(client_->getUpdater(Name("."), false, false),
- isc::NotImplemented);
-}
-
-TEST_F(MemoryClientTest, getJournalReaderNotImplemented) {
- // This method is not implemented.
- EXPECT_THROW(client_->getJournalReader(Name("."), 0, 0),
- isc::NotImplemented);
-}
-}
diff --git a/src/lib/datasrc/memory/tests/memory_segment_test.h b/src/lib/datasrc/memory/tests/memory_segment_test.h
deleted file mode 100644
index 3195a9b..0000000
--- a/src/lib/datasrc/memory/tests/memory_segment_test.h
+++ /dev/null
@@ -1,62 +0,0 @@
-// 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 DATASRC_MEMORY_SEGMENT_TEST_H
-#define DATASRC_MEMORY_SEGMENT_TEST_H 1
-
-#include <util/memory_segment_local.h>
-
-#include <cstddef> // for size_t
-#include <new> // for bad_alloc
-
-namespace isc {
-namespace datasrc {
-namespace memory {
-namespace test {
-
-// A special memory segment that can be used for tests. It normally behaves
-// like a "local" memory segment. If "throw count" is set to non 0 via
-// setThrowCount(), it continues the normal behavior until the specified
-// number of calls to allocate(), exclusive, and throws an exception at the
-// next call. For example, if count is set to 3, the next two calls to
-// allocate() will succeed, and the 3rd call will fail with an exception.
-// This segment object can be used after the exception is thrown, and the
-// count is internally reset to 0.
-class MemorySegmentTest : public isc::util::MemorySegmentLocal {
-public:
- MemorySegmentTest() : throw_count_(0) {}
- virtual void* allocate(std::size_t size) {
- if (throw_count_ > 0) {
- if (--throw_count_ == 0) {
- throw std::bad_alloc();
- }
- }
- return (isc::util::MemorySegmentLocal::allocate(size));
- }
- void setThrowCount(std::size_t count) { throw_count_ = count; }
-
-private:
- std::size_t throw_count_;
-};
-
-} // namespace test
-} // namespace memory
-} // namespace datasrc
-} // namespace isc
-
-#endif // DATASRC_MEMORY_SEGMENT_TEST_H
-
-// Local Variables:
-// mode: c++
-// End:
diff --git a/src/lib/datasrc/memory/tests/rdata_serialization_unittest.cc b/src/lib/datasrc/memory/tests/rdata_serialization_unittest.cc
deleted file mode 100644
index 12b613a..0000000
--- a/src/lib/datasrc/memory/tests/rdata_serialization_unittest.cc
+++ /dev/null
@@ -1,839 +0,0 @@
-// 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 <exceptions/exceptions.h>
-
-#include <util/buffer.h>
-
-#include <dns/name.h>
-#include <dns/labelsequence.h>
-#include <dns/messagerenderer.h>
-#include <dns/rdata.h>
-#include <dns/rdataclass.h>
-#include <dns/rrclass.h>
-#include <dns/rrtype.h>
-
-#include <datasrc/memory/rdata_serialization.h>
-
-#include <util/unittests/wiredata.h>
-
-#include <gtest/gtest.h>
-
-#include <boost/bind.hpp>
-#include <boost/foreach.hpp>
-
-#include <cstring>
-#include <set>
-#include <string>
-#include <vector>
-
-using namespace isc::dns;
-using namespace isc::dns::rdata;
-using namespace isc::datasrc::memory;
-
-using isc::util::unittests::matchWireData;
-using std::string;
-using std::vector;
-
-// A trick to steal some private definitions of the implementation we use here
-
-namespace isc {
-namespace datasrc{
-namespace memory {
-
-#include "../rdata_serialization_priv.cc"
-
-}
-}
-}
-
-namespace {
-// This defines a tuple of test data used in test_rdata_list below.
-struct TestRdata {
- const char* const rrclass; // RR class, textual form
- const char* const rrtype; // RR type, textual form
- const char* const rdata; // textual RDATA
- const size_t n_varlen_fields; // expected # of variable-len fields
-};
-
-// This test data consist of (almost) all supported types of RDATA (+ some
-// unusual and corner cases).
-const TestRdata test_rdata_list[] = {
- {"IN", "A", "192.0.2.1", 0},
- {"IN", "NS", "ns.example.com", 0},
- {"IN", "CNAME", "cname.example.com", 0},
- {"IN", "SOA", "ns.example.com root.example.com 0 0 0 0 0", 0},
- {"IN", "PTR", "reverse.example.com", 0},
- {"IN", "HINFO", "\"cpu-info\" \"OS-info\"", 1},
- {"IN", "MINFO", "root.example.com mbox.example.com", 0},
- {"IN", "MX", "10 mx.example.com", 0},
- {"IN", "TXT", "\"test1\" \"test 2\"", 1},
- {"IN", "RP", "root.example.com. rp-text.example.com", 0},
- {"IN", "AFSDB", "1 afsdb.example.com", 0},
- {"IN", "AAAA", "2001:db8::1", 0},
- {"IN", "SRV", "1 0 10 target.example.com", 0},
- {"IN", "NAPTR", "100 50 \"s\" \"http\" \"\" _http._tcp.example.com", 1},
- {"IN", "DNAME", "dname.example.com", 0},
- {"IN", "DS", "12892 5 2 5F0EB5C777586DE18DA6B5", 1},
- {"IN", "SSHFP", "1 1 dd465c09cfa51fb45020cc83316fff", 1},
- // We handle RRSIG separately, so it's excluded from the list
- {"IN", "NSEC", "next.example.com. A AAAA NSEC RRSIG", 1},
- {"IN", "DNSKEY", "256 3 5 FAKEFAKE", 1},
- {"IN", "DHCID", "FAKEFAKE", 1},
- {"IN", "NSEC3", "1 1 12 AABBCCDD FAKEFAKE A RRSIG", 1},
- {"IN", "NSEC3PARAM", "1 0 12 AABBCCDD", 1},
- {"IN", "SPF", "v=spf1 +mx a:colo.example.com/28 -all", 1},
- {"IN", "DLV", "12892 5 2 5F0EB5C777586DE18DA6B5", 1},
- {"IN", "TYPE65000", "\\# 3 010203", 1}, // some "custom" type
- {"IN", "TYPE65535", "\\# 0", 1}, // max RR type, 0-length RDATA
- {"CH", "A", "\\# 2 0102", 1}, // A RR for non-IN class; varlen data
- {"CH", "NS", "ns.example.com", 0}, // class CH, generic data
- {"CH", "TXT", "BIND10", 1}, // ditto
- {"HS", "A", "\\# 5 0102030405", 1}, // A RR for non-IN class; varlen data
- {NULL, NULL, NULL, 0}
-};
-
-// The following two functions will be used to generate wire format data
-// from encoded representation of each RDATA.
-void
-renderNameField(MessageRenderer* renderer, bool additional_required,
- const LabelSequence& labels, RdataNameAttributes attributes)
-{
- EXPECT_EQ(additional_required, (attributes & NAMEATTR_ADDITIONAL) != 0);
- renderer->writeName(labels, (attributes & NAMEATTR_COMPRESSIBLE) != 0);
-}
-
-void
-renderDataField(MessageRenderer* renderer, const void* data, size_t data_len) {
- renderer->writeData(data, data_len);
-}
-
-class RdataSerializationTest : public ::testing::Test {
-protected:
- RdataSerializationTest() : a_rdata_(createRdata(RRType::A(), RRClass::IN(),
- "192.0.2.53")),
- aaaa_rdata_(createRdata(RRType::AAAA(), RRClass::IN(),
- "2001:db8::53")),
- rrsig_rdata_(createRdata(
- RRType::RRSIG(), RRClass::IN(),
- "A 5 2 3600 20120814220826 "
- "20120715220826 12345 com. FAKE"))
- {}
-
- // A wraper for RdataEncoder::encode() with buffer overrun check.
- void encodeWrapper(size_t data_len);
-
- // Some commonly used RDATA
- const ConstRdataPtr a_rdata_;
- const ConstRdataPtr aaaa_rdata_;
- const ConstRdataPtr rrsig_rdata_;
-
- RdataEncoder encoder_;
- vector<uint8_t> encoded_data_;
- MessageRenderer expected_renderer_;
- MessageRenderer actual_renderer_;
- vector<ConstRdataPtr> rdata_list_;
-};
-
-// There are several ways to decode the data. For one, there are
-// more interfaces uses for RdataReader, and we use our own decoder,
-// to check the actual encoded data.
-//
-// These decoding ways are provided by the template parameter.
-template<class DecoderStyle>
-class RdataEncodeDecodeTest : public RdataSerializationTest {
-public:
- // This helper test method encodes the given list of RDATAs
- // (in rdata_list), and then iterates over the data, rendering the fields
- // in the wire format. It then compares the wire data with the one
- // generated by the normal libdns++ interface to see the encoding/decoding
- // works as intended.
- void checkEncode(RRClass rrclass, RRType rrtype,
- const vector<ConstRdataPtr>& rdata_list,
- size_t expected_varlen_fields,
- const vector<ConstRdataPtr>& rrsig_list =
- vector<ConstRdataPtr>());
-
- void addRdataCommon(const vector<ConstRdataPtr>& rrsigs);
- void addRdataMultiCommon(const vector<ConstRdataPtr>& rrsigs);
-};
-
-// Used across more classes and scopes. But it's just uninteresting
-// constant.
-const Name& dummyName2() {
- static const Name result("example.com");
- return (result);
-}
-
-bool
-additionalRequired(const RRType& type) {
- // The set of RR types that require additional section processing.
- // We'll use it to determine what value should the renderNameField get
- // and, if the stored attributes are as expected.
- static std::set<RRType> need_additionals;
- if (need_additionals.empty()) {
- need_additionals.insert(RRType::NS());
- need_additionals.insert(RRType::MX());
- need_additionals.insert(RRType::SRV());
- }
-
- return (need_additionals.find(type) != need_additionals.end());
-}
-
-// A decoder that does not use RdataReader. Not recommended for use,
-// but it allows the tests to check the internals of the data.
-class ManualDecoderStyle {
-public:
- static void foreachRdataField(RRClass rrclass, RRType rrtype,
- size_t rdata_count,
- const vector<uint8_t>& encoded_data,
- const vector<uint16_t>& varlen_list,
- RdataReader::NameAction name_callback,
- RdataReader::DataAction data_callback)
- {
- const RdataEncodeSpec& encode_spec = getRdataEncodeSpec(rrclass,
- rrtype);
-
- size_t off = 0;
- size_t varlen_count = 0;
- size_t name_count = 0;
- for (size_t count = 0; count < rdata_count; ++count) {
- for (size_t i = 0; i < encode_spec.field_count; ++i) {
- const RdataFieldSpec& field_spec = encode_spec.fields[i];
- switch (field_spec.type) {
- case RdataFieldSpec::FIXEDLEN_DATA:
- if (data_callback) {
- data_callback(&encoded_data.at(off),
- field_spec.fixeddata_len);
- }
- off += field_spec.fixeddata_len;
- break;
- case RdataFieldSpec::VARLEN_DATA:
- {
- const size_t varlen = varlen_list.at(varlen_count);
- if (data_callback && varlen > 0) {
- data_callback(&encoded_data.at(off), varlen);
- }
- off += varlen;
- ++varlen_count;
- break;
- }
- case RdataFieldSpec::DOMAIN_NAME:
- {
- ++name_count;
- const LabelSequence labels(&encoded_data.at(off));
- if (name_callback) {
- name_callback(labels,
- field_spec.name_attributes);
- }
- off += labels.getSerializedLength();
- break;
- }
- }
- }
- }
- assert(name_count == encode_spec.name_count * rdata_count);
- assert(varlen_count == encode_spec.varlen_count * rdata_count);
- }
-
- static void foreachRRSig(const vector<uint8_t>& encoded_data,
- const vector<uint16_t>& rrsiglen_list,
- RdataReader::DataAction data_callback)
- {
- size_t rrsig_totallen = 0;
- for (vector<uint16_t>::const_iterator it = rrsiglen_list.begin();
- it != rrsiglen_list.end();
- ++it) {
- rrsig_totallen += *it;
- }
- assert(encoded_data.size() >= rrsig_totallen);
-
- const uint8_t* dp = &encoded_data[encoded_data.size() -
- rrsig_totallen];
- for (size_t i = 0; i < rrsiglen_list.size(); ++i) {
- data_callback(dp, rrsiglen_list[i]);
- dp += rrsiglen_list[i];
- }
- }
-
- static void decode(const isc::dns::RRClass& rrclass,
- const isc::dns::RRType& rrtype,
- size_t rdata_count,
- size_t rrsig_count,
- size_t expected_varlen_fields,
- // Warning: this test actualy might change the
- // encoded_data !
- vector<uint8_t>& encoded_data, size_t,
- MessageRenderer& renderer)
- {
- // If this type of RDATA is expected to contain variable-length fields,
- // we brute force the encoded data, exploiting our knowledge of actual
- // encoding, then adjust the encoded data excluding the list of length
- // fields. This is ugly, but for tests only.
- vector<uint16_t> varlen_list;
- if (expected_varlen_fields > 0) {
- const size_t varlen_list_size =
- rdata_count * expected_varlen_fields * sizeof(uint16_t);
- ASSERT_LE(varlen_list_size, encoded_data.size());
- varlen_list.resize(rdata_count * expected_varlen_fields);
- std::memcpy(&varlen_list[0], &encoded_data[0], varlen_list_size);
- encoded_data.assign(encoded_data.begin() + varlen_list_size,
- encoded_data.end());
- }
-
- // If RRSIGs are given, we need to extract the list of the RRSIG
- // lengths and adjust encoded_data_ further.
- vector<uint16_t> rrsiglen_list;
- if (rrsig_count > 0) {
- const size_t rrsig_len_size = rrsig_count * sizeof(uint16_t);
- ASSERT_LE(rrsig_len_size, encoded_data.size());
- rrsiglen_list.resize(rrsig_count * rrsig_len_size);
- std::memcpy(&rrsiglen_list[0], &encoded_data[0], rrsig_len_size);
- encoded_data.assign(encoded_data.begin() + rrsig_len_size,
- encoded_data.end());
- }
-
- // Create wire-format data from the encoded data
- foreachRdataField(rrclass, rrtype, rdata_count, encoded_data,
- varlen_list,
- boost::bind(renderNameField, &renderer,
- additionalRequired(rrtype), _1, _2),
- boost::bind(renderDataField, &renderer, _1, _2));
-
- // 2nd dummy name
- renderer.writeName(dummyName2());
- // Finally, dump any RRSIGs in wire format.
- foreachRRSig(encoded_data, rrsiglen_list,
- boost::bind(renderDataField, &renderer, _1, _2));
- }
-};
-
-// Check using callbacks and calling next until the end.
-class CallbackDecoder {
-public:
- static void decode(const isc::dns::RRClass& rrclass,
- const isc::dns::RRType& rrtype,
- size_t rdata_count, size_t sig_count, size_t,
- const vector<uint8_t>& encoded_data, size_t,
- MessageRenderer& renderer)
- {
- RdataReader reader(rrclass, rrtype, &encoded_data[0], rdata_count,
- sig_count,
- boost::bind(renderNameField, &renderer,
- additionalRequired(rrtype), _1, _2),
- boost::bind(renderDataField, &renderer, _1, _2));
- while (reader.next() != RdataReader::RRSET_BOUNDARY) {}
- renderer.writeName(dummyName2());
- while (reader.nextSig() != RdataReader::RRSET_BOUNDARY) {}
- }
-};
-
-// Check using callbacks and calling iterate.
-class IterateDecoder {
-public:
- static void decode(const isc::dns::RRClass& rrclass,
- const isc::dns::RRType& rrtype,
- size_t rdata_count, size_t sig_count, size_t,
- const vector<uint8_t>& encoded_data, size_t,
- MessageRenderer& renderer)
- {
- RdataReader reader(rrclass, rrtype, &encoded_data[0],
- rdata_count, sig_count,
- boost::bind(renderNameField, &renderer,
- additionalRequired(rrtype), _1, _2),
- boost::bind(renderDataField, &renderer, _1, _2));
- reader.iterate();
- renderer.writeName(dummyName2());
- reader.iterateAllSigs();
- }
-};
-
-namespace {
-
-// Render the data to renderer, if one is set, or put it inside
-// a data buffer.
-void
-appendOrRenderData(vector<uint8_t>* where, MessageRenderer** renderer,
- const void* data, size_t size)
-{
- if (*renderer != NULL) {
- (*renderer)->writeData(data, size);
- } else {
- where->insert(where->end(), reinterpret_cast<const uint8_t*>(data),
- reinterpret_cast<const uint8_t*>(data) + size);
- }
-}
-
-}
-
-// Similar to IterateDecoder, but it first iterates a little and rewinds
-// before actual rendering.
-class RewindAndDecode {
-private:
- static void writeName(MessageRenderer** renderer,
- const LabelSequence& labels,
- RdataNameAttributes attributes)
- {
- (*renderer)->writeName(labels,
- (attributes & NAMEATTR_COMPRESSIBLE) != 0);
- }
-public:
- static void decode(const isc::dns::RRClass& rrclass,
- const isc::dns::RRType& rrtype,
- size_t rdata_count, size_t sig_count, size_t,
- const vector<uint8_t>& encoded_data, size_t,
- MessageRenderer& renderer)
- {
- MessageRenderer dump; // A place to dump the extra data from before
- // actual rendering.
- MessageRenderer* current = &dump;
- vector<uint8_t> placeholder; // boost::bind does not like NULL
- RdataReader reader(rrclass, rrtype, &encoded_data[0],
- rdata_count, sig_count,
- boost::bind(writeName, ¤t, _1, _2),
- boost::bind(appendOrRenderData, &placeholder,
- ¤t, _1, _2));
- // Iterate a little and rewind
- reader.next();
- reader.nextSig();
- reader.rewind();
- // Do the actual rendering
- current = &renderer;
- reader.iterate();
- renderer.writeName(dummyName2());
- reader.iterateAllSigs();
- }
-};
-
-// Decode using the iteration over one rdata each time.
-// We also count there's the correct count of Rdatas.
-class SingleIterateDecoder {
-public:
- static void decode(const isc::dns::RRClass& rrclass,
- const isc::dns::RRType& rrtype,
- size_t rdata_count, size_t sig_count, size_t,
- const vector<uint8_t>& encoded_data, size_t,
- MessageRenderer& renderer)
- {
- RdataReader reader(rrclass, rrtype, &encoded_data[0],
- rdata_count, sig_count,
- boost::bind(renderNameField, &renderer,
- additionalRequired(rrtype), _1, _2),
- boost::bind(renderDataField, &renderer, _1, _2));
- size_t actual_count = 0;
- while (reader.iterateRdata()) {
- ++actual_count;
- }
- EXPECT_EQ(rdata_count, actual_count);
- actual_count = 0;
- renderer.writeName(dummyName2());
- while (reader.iterateSingleSig()) {
- ++actual_count;
- }
- EXPECT_EQ(sig_count, actual_count);
- }
-};
-
-// This one does not adhere to the usual way the reader is used, trying
-// to confuse it. It iterates part of the data manually and then reads
-// the rest through iterate. It also reads the signatures in the middle
-// of rendering.
-template<bool start_data, bool start_sig>
-class HybridDecoder {
-public:
- static void decode(const isc::dns::RRClass& rrclass,
- const isc::dns::RRType& rrtype,
- size_t rdata_count, size_t sig_count, size_t,
- const vector<uint8_t>& encoded_data,
- size_t encoded_data_len,
- MessageRenderer& renderer)
- {
- vector<uint8_t> data;
- MessageRenderer* current;
- RdataReader reader(rrclass, rrtype, &encoded_data[0],
- rdata_count, sig_count,
- boost::bind(renderNameField, &renderer,
- additionalRequired(rrtype), _1, _2),
- boost::bind(appendOrRenderData, &data, ¤t, _1,
- _2));
- // The size matches
- EXPECT_EQ(encoded_data_len, reader.getSize());
- if (start_sig) {
- current = NULL;
- reader.nextSig();
- }
- // Render first part of data. If there's none, return empty Result and
- // do nothing.
- if (start_data) {
- current = &renderer;
- reader.next();
- }
- // Now, we let all sigs to be copied to data. We disable the
- // renderer for this.
- current = NULL;
- reader.iterateAllSigs();
- // Now return the renderer and render the rest of the data
- current = &renderer;
- reader.iterate();
- // Now, this should not break anything and should be valid, but should
- // return ends.
- EXPECT_EQ(RdataReader::RRSET_BOUNDARY, reader.next());
- EXPECT_EQ(RdataReader::RRSET_BOUNDARY, reader.nextSig());
- // Render the name and the sigs
- renderer.writeName(dummyName2());
- renderer.writeData(&data[0], data.size());
- // The size matches even after use
- EXPECT_EQ(encoded_data_len, reader.getSize());
- }
-};
-
-typedef ::testing::Types<ManualDecoderStyle,
- CallbackDecoder, IterateDecoder, SingleIterateDecoder,
- HybridDecoder<true, true>, HybridDecoder<true, false>,
- HybridDecoder<false, true>,
- HybridDecoder<false, false> >
- DecoderStyles;
-// Each decoder style must contain a decode() method. Such method is expected
-// to decode the passed data, first render the Rdata into the passed renderer,
-// then write the dummyName2() there and write the RRSig data after that.
-// It may do other checks too.
-//
-// There are some slight differences to how to do the decoding, that's why we
-// have the typed test.
-TYPED_TEST_CASE(RdataEncodeDecodeTest, DecoderStyles);
-
-void
-RdataSerializationTest::encodeWrapper(size_t data_len) {
- // make sure the data buffer is large enough for the canary
- encoded_data_.resize(data_len + 2);
- // set the canary data
- encoded_data_.at(data_len) = 0xde;
- encoded_data_.at(data_len + 1) = 0xad;
- // encode, then check the canary is intact
- encoder_.encode(&encoded_data_[0], data_len);
- EXPECT_EQ(0xde, encoded_data_.at(data_len));
- EXPECT_EQ(0xad, encoded_data_.at(data_len + 1));
- // shrink the data buffer to the originally expected size (some tests
- // expect that). the actual encoded data should be intact.
- encoded_data_.resize(data_len);
-}
-
-template<class DecoderStyle>
-void
-RdataEncodeDecodeTest<DecoderStyle>::
-checkEncode(RRClass rrclass, RRType rrtype,
- const vector<ConstRdataPtr>& rdata_list,
- size_t expected_varlen_fields,
- const vector<ConstRdataPtr>& rrsig_list)
-{
- // These two names will be rendered before and after the test RDATA,
- // to check in case the RDATA contain a domain name whether it's
- // compressed or not correctly. The names in the RDATA should basically
- // a subdomain of example.com, so it can be compressed due to dummyName2().
- // Likewise, dummyName2() should be able to be fully compressed due to
- // the name in the RDATA.
- const Name dummy_name("com");
-
- expected_renderer_.clear();
- actual_renderer_.clear();
- encoded_data_.clear();
-
- // Build expected wire-format data
- expected_renderer_.writeName(dummy_name);
- BOOST_FOREACH(const ConstRdataPtr& rdata, rdata_list) {
- rdata->toWire(expected_renderer_);
- }
- expected_renderer_.writeName(dummyName2());
- BOOST_FOREACH(const ConstRdataPtr& rdata, rrsig_list) {
- rdata->toWire(expected_renderer_);
- }
-
- // Then build wire format data using the encoded data.
- // 1st dummy name
- actual_renderer_.writeName(dummy_name);
-
- // Create encoded data
- encoder_.start(rrclass, rrtype);
- BOOST_FOREACH(const ConstRdataPtr& rdata, rdata_list) {
- encoder_.addRdata(*rdata);
- }
- BOOST_FOREACH(const ConstRdataPtr& rdata, rrsig_list) {
- encoder_.addSIGRdata(*rdata);
- }
- const size_t storage_len = encoder_.getStorageLength();
- encodeWrapper(storage_len);
-
- DecoderStyle::decode(rrclass, rrtype, rdata_list.size(), rrsig_list.size(),
- expected_varlen_fields, encoded_data_, storage_len,
- actual_renderer_);
-
- // Two sets of wire-format data should be identical.
- matchWireData(expected_renderer_.getData(), expected_renderer_.getLength(),
- actual_renderer_.getData(), actual_renderer_.getLength());
-}
-
-template<class DecoderStyle>
-void
-RdataEncodeDecodeTest<DecoderStyle>::
-addRdataCommon(const vector<ConstRdataPtr>& rrsigs) {
- // Basic check on the encoded data for (most of) all supported RR types,
- // in a comprehensive manner.
- for (size_t i = 0; test_rdata_list[i].rrclass != NULL; ++i) {
- SCOPED_TRACE(string(test_rdata_list[i].rrclass) + "/" +
- test_rdata_list[i].rrtype);
- const RRClass rrclass(test_rdata_list[i].rrclass);
- const RRType rrtype(test_rdata_list[i].rrtype);
- const ConstRdataPtr rdata = createRdata(rrtype, rrclass,
- test_rdata_list[i].rdata);
- rdata_list_.clear();
- rdata_list_.push_back(rdata);
- checkEncode(rrclass, rrtype, rdata_list_,
- test_rdata_list[i].n_varlen_fields, rrsigs);
- }
-}
-
-TYPED_TEST(RdataEncodeDecodeTest, addRdata) {
- vector<ConstRdataPtr> rrsigs;
- this->addRdataCommon(rrsigs); // basic tests without RRSIGs (empty vector)
-
- // Test with RRSIGs (covered type doesn't always match, but the encoder
- // doesn't check that)
- rrsigs.push_back(this->rrsig_rdata_);
- this->addRdataCommon(rrsigs);
-}
-
-template<class DecoderStyle>
-void
-RdataEncodeDecodeTest<DecoderStyle>::
-addRdataMultiCommon(const vector<ConstRdataPtr>& rrsigs) {
- // Similar to addRdata(), but test with multiple RDATAs.
- // Four different cases are tested: a single fixed-len RDATA (A),
- // fixed-len data + domain name (MX), variable-len data only (TXT),
- // variable-len data + domain name (NAPTR).
- ConstRdataPtr a_rdata2 = createRdata(RRType::A(), RRClass::IN(),
- "192.0.2.54");
- rdata_list_.clear();
- rdata_list_.push_back(a_rdata_);
- rdata_list_.push_back(a_rdata2);
- checkEncode(RRClass::IN(), RRType::A(), rdata_list_, 0, rrsigs);
-
- ConstRdataPtr mx_rdata1 = createRdata(RRType::MX(), RRClass::IN(),
- "5 mx1.example.com");
- ConstRdataPtr mx_rdata2 = createRdata(RRType::MX(), RRClass::IN(),
- "10 mx2.example.com");
- rdata_list_.clear();
- rdata_list_.push_back(mx_rdata1);
- rdata_list_.push_back(mx_rdata2);
- checkEncode(RRClass::IN(), RRType::MX(), rdata_list_, 0, rrsigs);
-
- ConstRdataPtr txt_rdata1 = createRdata(RRType::TXT(), RRClass::IN(),
- "foo bar baz");
- ConstRdataPtr txt_rdata2 = createRdata(RRType::TXT(), RRClass::IN(),
- "another text data");
- rdata_list_.clear();
- rdata_list_.push_back(txt_rdata1);
- rdata_list_.push_back(txt_rdata2);
- checkEncode(RRClass::IN(), RRType::TXT(), rdata_list_, 1, rrsigs);
-
- ConstRdataPtr naptr_rdata1 =
- createRdata(RRType::NAPTR(), RRClass::IN(),
- "100 50 \"s\" \"http\" \"\" _http._tcp.example.com");
- ConstRdataPtr naptr_rdata2 =
- createRdata(RRType::NAPTR(), RRClass::IN(),
- "200 100 \"s\" \"http\" \"\" _http._tcp.example.com");
- rdata_list_.clear();
- rdata_list_.push_back(naptr_rdata1);
- rdata_list_.push_back(naptr_rdata2);
- checkEncode(RRClass::IN(), RRType::NAPTR(), rdata_list_, 1, rrsigs);
-}
-
-void ignoreName(const LabelSequence&, unsigned) {
-}
-
-void
-checkLargeData(const in::DHCID* decoded, bool* called, const void* encoded,
- size_t length)
-{
- EXPECT_FALSE(*called); // Called exactly once
- *called = true;
-
- // Reconstruct the Rdata and check it.
- isc::util::InputBuffer ib(encoded, length);
- const in::DHCID reconstructed(ib, ib.getLength());
- EXPECT_EQ(0, reconstructed.compare(*decoded));
-}
-
-TEST_F(RdataSerializationTest, encodeLargeRdata) {
- // There should be no reason for a large RDATA to fail in encoding,
- // but we check such a case explicitly.
-
- encoded_data_.resize(65535); // max unsigned 16-bit int
- isc::util::InputBuffer buffer(&encoded_data_[0], encoded_data_.size());
- const in::DHCID large_dhcid(buffer, encoded_data_.size());
-
- encoder_.start(RRClass::IN(), RRType::DHCID());
- encoder_.addRdata(large_dhcid);
- encodeWrapper(encoder_.getStorageLength());
-
- // The encoded data should be identical to the original one.
- bool called = false;
- RdataReader reader(RRClass::IN(), RRType::DHCID(), &encoded_data_[0], 1, 0,
- ignoreName, boost::bind(checkLargeData, &large_dhcid,
- &called, _1, _2));
- reader.iterate();
- EXPECT_TRUE(called);
- called = false;
- reader.iterateAllSigs();
- EXPECT_FALSE(called);
-}
-
-TYPED_TEST(RdataEncodeDecodeTest, addRdataMulti) {
- vector<ConstRdataPtr> rrsigs;
- this->addRdataMultiCommon(rrsigs); // test without RRSIGs (empty vector)
-
- // Tests with two RRSIGs
- rrsigs.push_back(this->rrsig_rdata_);
- rrsigs.push_back(createRdata(RRType::RRSIG(), RRClass::IN(),
- "A 5 2 3600 20120814220826 "
- "20120715220826 54321 com. FAKE"));
- this->addRdataMultiCommon(rrsigs);
-}
-
-TEST_F(RdataSerializationTest, badAddRdata) {
- // Some operations must follow start().
- EXPECT_THROW(encoder_.addRdata(*a_rdata_), isc::InvalidOperation);
- EXPECT_THROW(encoder_.getStorageLength(), isc::InvalidOperation);
- // will allocate space of some arbitrary size (256 bytes)
- EXPECT_THROW(encodeWrapper(256), isc::InvalidOperation);
-
- // Bad buffer for encode
- encoder_.start(RRClass::IN(), RRType::A());
- encoder_.addRdata(*a_rdata_);
- const size_t buf_len = encoder_.getStorageLength();
- // NULL buffer for encode
- EXPECT_THROW(encoder_.encode(NULL, buf_len), isc::BadValue);
- // buffer length is too short (we don't use the wrraper because we don't
- // like to tweak the length arg to encode()).
- encoded_data_.resize(buf_len - 1);
- EXPECT_THROW(encoder_.encode(&encoded_data_[0], buf_len - 1),
- isc::BadValue);
-
- // Type of RDATA and the specified RR type don't match. addRdata() should
- // detect this inconsistency.
- encoder_.start(RRClass::IN(), RRType::AAAA());
- EXPECT_THROW(encoder_.addRdata(*a_rdata_), isc::BadValue);
-
- // Likewise.
- encoder_.start(RRClass::IN(), RRType::A());
- EXPECT_THROW(encoder_.addRdata(*aaaa_rdata_), isc::BadValue);
-
- // Likewise. The encoder expects the first name completes the data, and
- // throws on the second due as an unexpected name field.
- const ConstRdataPtr rp_rdata =
- createRdata(RRType::RP(), RRClass::IN(), "a.example. b.example");
- encoder_.start(RRClass::IN(), RRType::NS());
- EXPECT_THROW(encoder_.addRdata(*rp_rdata), isc::BadValue);
-
- // Likewise. The encoder considers the name data a variable length data
- // field, and throws on the first name.
- encoder_.start(RRClass::IN(), RRType::DHCID());
- EXPECT_THROW(encoder_.addRdata(*rp_rdata), isc::BadValue);
-
- // Likewise. The text RDATA (2 bytes) will be treated as MX preference,
- // and the encoder will still expect to see a domain name.
- const ConstRdataPtr txt_rdata = createRdata(RRType::TXT(), RRClass::IN(),
- "a");
- encoder_.start(RRClass::IN(), RRType::MX());
- EXPECT_THROW(encoder_.addRdata(*txt_rdata), isc::BadValue);
-
- // Similar to the previous one, but in this case there's no data field
- // in the spec.
- encoder_.start(RRClass::IN(), RRType::NS());
- EXPECT_THROW(encoder_.addRdata(*txt_rdata), isc::BadValue);
-
- // Likewise. Inconsistent name compression policy.
- const ConstRdataPtr ns_rdata =
- createRdata(RRType::NS(), RRClass::IN(), "ns.example");
- encoder_.start(RRClass::IN(), RRType::DNAME());
- EXPECT_THROW(encoder_.addRdata(*ns_rdata), isc::BadValue);
-
- // Same as the previous one, opposite inconsistency.
- const ConstRdataPtr dname_rdata =
- createRdata(RRType::DNAME(), RRClass::IN(), "dname.example");
- encoder_.start(RRClass::IN(), RRType::NS());
- EXPECT_THROW(encoder_.addRdata(*dname_rdata), isc::BadValue);
-
- // RDATA len exceeds the 16-bit range. Technically not invalid, but
- // we don't support that (and it's practically useless anyway).
- encoded_data_.resize(65536); // use encoded_data_ for placeholder
- isc::util::InputBuffer buffer(&encoded_data_[0], encoded_data_.size());
- encoder_.start(RRClass::IN(), RRType::DHCID());
- EXPECT_THROW(encoder_.addRdata(in::DHCID(buffer, encoded_data_.size())),
- RdataEncodingError);
-
- // RRSIG cannot be used as the main RDATA type (can only be added as
- // a signature for some other type of RDATAs).
- EXPECT_THROW(encoder_.start(RRClass::IN(), RRType::RRSIG()),
- isc::BadValue);
-}
-
-void
-checkSigData(const ConstRdataPtr& decoded, bool* called, const void* encoded,
- size_t length)
-{
- EXPECT_FALSE(*called); // Called exactly once
- *called = true;
-
- // Reconstruct the RRSig and check it.
- isc::util::InputBuffer ib(encoded, length);
- const generic::RRSIG reconstructed(ib, ib.getLength());
- EXPECT_EQ(0, reconstructed.compare(*decoded));
-}
-
-TEST_F(RdataSerializationTest, addSIGRdataOnly) {
- // Encoded data that only contain RRSIGs. Mostly useless, but can happen
- // (in a partially broken zone) and it's accepted.
- encoder_.start(RRClass::IN(), RRType::A());
- encoder_.addSIGRdata(*rrsig_rdata_);
- encodeWrapper(encoder_.getStorageLength());
- ASSERT_LT(sizeof(uint16_t), encoder_.getStorageLength());
-
- bool called = false;
- RdataReader reader(RRClass::IN(), RRType::A(), &encoded_data_[0], 0, 1,
- ignoreName, boost::bind(checkSigData, rrsig_rdata_,
- &called, _1, _2));
- reader.iterate();
- EXPECT_FALSE(called);
- reader.iterateAllSigs();
- EXPECT_TRUE(called);
-}
-
-TEST_F(RdataSerializationTest, badAddSIGRdata) {
- // try adding SIG before start
- EXPECT_THROW(encoder_.addSIGRdata(*rrsig_rdata_), isc::InvalidOperation);
-
- // Very big RRSIG. This implementation rejects it.
- isc::util::OutputBuffer ob(0);
- rrsig_rdata_->toWire(ob);
- // append dummy trailing signature to make it too big
- vector<uint8_t> dummy_sig(65536 - ob.getLength());
- ob.writeData(&dummy_sig[0], dummy_sig.size());
- ASSERT_EQ(65536, ob.getLength());
-
- isc::util::InputBuffer ib(ob.getData(), ob.getLength());
- const generic::RRSIG big_sigrdata(ib, ob.getLength());
- encoder_.start(RRClass::IN(), RRType::A());
- EXPECT_THROW(encoder_.addSIGRdata(big_sigrdata), RdataEncodingError);
-}
-}
diff --git a/src/lib/datasrc/memory/tests/rdataset_unittest.cc b/src/lib/datasrc/memory/tests/rdataset_unittest.cc
deleted file mode 100644
index 897e53c..0000000
--- a/src/lib/datasrc/memory/tests/rdataset_unittest.cc
+++ /dev/null
@@ -1,298 +0,0 @@
-// 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 <exceptions/exceptions.h>
-
-#include <util/buffer.h>
-#include <util/memory_segment_local.h>
-
-#include <dns/rdata.h>
-#include <dns/rdataclass.h>
-#include <dns/rrset.h>
-#include <dns/rrclass.h>
-#include <dns/rrtype.h>
-#include <dns/rrttl.h>
-
-#include <datasrc/memory/rdata_serialization.h>
-#include <datasrc/memory/rdataset.h>
-
-#include <testutils/dnsmessage_test.h>
-
-#include <gtest/gtest.h>
-
-#include <boost/lexical_cast.hpp>
-
-#include <string>
-
-using namespace isc::dns;
-using namespace isc::dns::rdata;
-using namespace isc::datasrc::memory;
-using namespace isc::testutils;
-using boost::lexical_cast;
-
-namespace {
-
-class RdataSetTest : public ::testing::Test {
-protected:
- RdataSetTest() :
- // 1076895760 = 0x40302010. Use this so we fill in all 8-bit "field"
- // of the 32-bit TTL
- a_rrset_(textToRRset("www.example.com. 1076895760 IN A 192.0.2.1")),
- rrsig_rrset_(textToRRset("www.example.com. 1076895760 IN RRSIG "
- "A 5 2 3600 20120814220826 20120715220826 "
- "1234 example.com. FAKE"))
- {}
- void TearDown() {
- EXPECT_TRUE(mem_sgmt_.allMemoryDeallocated());
- }
-
- ConstRRsetPtr a_rrset_, rrsig_rrset_;
- isc::util::MemorySegmentLocal mem_sgmt_;
- RdataEncoder encoder_;
-};
-
-// Convert the given 32-bit integer (network byte order) to the corresponding
-// RRTTL object.
-RRTTL
-restoreTTL(const void* ttl_data) {
- isc::util::InputBuffer b(ttl_data, sizeof(uint32_t));
- return (RRTTL(b));
-}
-
-// A helper callback for checkRdataSet. This confirms the given data
-// is the expected in::A RDATA (the value is taken from the RdataSetTest
-// constructor).
-void
-checkData(const void* data, size_t size) {
- isc::util::InputBuffer b(data, size);
- EXPECT_EQ(0, in::A(b, size).compare(in::A("192.0.2.1")));
-}
-
-// This is a set of checks for an RdataSet created with some simple
-// conditions. with_rrset/with_rrsig is true iff the RdataSet is supposed to
-// contain normal/RRSIG RDATA.
-void
-checkRdataSet(const RdataSet& rdataset, bool with_rrset, bool with_rrsig) {
- EXPECT_FALSE(rdataset.next); // by default the next pointer should be NULL
- EXPECT_EQ(RRType::A(), rdataset.type);
- // See the RdataSetTest constructor for the magic number.
- EXPECT_EQ(RRTTL(1076895760), restoreTTL(rdataset.getTTLData()));
- EXPECT_EQ(with_rrset ? 1 : 0, rdataset.getRdataCount());
- EXPECT_EQ(with_rrsig ? 1 : 0, rdataset.getSigRdataCount());
-
- // A simple test for the data content. Details tests for the encoder/
- // reader should be basically sufficient for various cases of the data,
- // and the fact that this test doesn't detect memory leak should be
- // reasonably sufficient that the implementation handles the data region
- // correctly. Here we check one simple case for a simple form of RDATA,
- // mainly for checking the behavior of getDataBuf().
- RdataReader reader(RRClass::IN(), RRType::A(),
- reinterpret_cast<const uint8_t*>(
- rdataset.getDataBuf()),
- rdataset.getRdataCount(), rdataset.getSigRdataCount(),
- &RdataReader::emptyNameAction, checkData);
- reader.iterate();
-}
-
-TEST_F(RdataSetTest, create) {
- // A simple case of creating an RdataSet. Confirming the resulting
- // fields have the expected values, and then destroying it (TearDown()
- // would detect any memory leak)
- RdataSet* rdataset = RdataSet::create(mem_sgmt_, encoder_, a_rrset_,
- ConstRRsetPtr());
- checkRdataSet(*rdataset, true, false);
- RdataSet::destroy(mem_sgmt_, RRClass::IN(), rdataset);
-}
-
-TEST_F(RdataSetTest, getNext) {
- RdataSet* rdataset = RdataSet::create(mem_sgmt_, encoder_, a_rrset_,
- ConstRRsetPtr());
-
- // By default, the next pointer should be NULL (already tested in other
- // test cases), which should be the case with getNext(). We test both
- // mutable and immutable versions of getNext().
- EXPECT_EQ(static_cast<RdataSet*>(NULL), rdataset->getNext());
- EXPECT_EQ(static_cast<const RdataSet*>(NULL),
- static_cast<const RdataSet*>(rdataset)->getNext());
-
- // making a link (it would form an infinite loop, but it doesn't matter
- // in this test), and check the pointer returned by getNext().
- rdataset->next = rdataset;
- EXPECT_EQ(rdataset, static_cast<const RdataSet*>(rdataset)->getNext());
-
- RdataSet::destroy(mem_sgmt_, RRClass::IN(), rdataset);
-}
-
-// A helper function to create an RRset containing the given number of
-// unique RDATAs.
-ConstRRsetPtr
-getRRsetWithRdataCount(size_t rdata_count) {
- RRsetPtr rrset(new RRset(Name("example.com"), RRClass::IN(), RRType::TXT(),
- RRTTL(3600)));
- for (size_t i = 0; i < rdata_count; ++i) {
- rrset->addRdata(rdata::createRdata(RRType::TXT(), RRClass::IN(),
- lexical_cast<std::string>(i)));
- }
- return (rrset);
-}
-
-TEST_F(RdataSetTest, createManyRRs) {
- // RRset with possible maximum number of RDATAs
- RdataSet* rdataset = RdataSet::create(mem_sgmt_, encoder_,
- getRRsetWithRdataCount(8191),
- ConstRRsetPtr());
- EXPECT_EQ(8191, rdataset->getRdataCount());
- EXPECT_EQ(0, rdataset->getSigRdataCount());
- RdataSet::destroy(mem_sgmt_, RRClass::IN(), rdataset);
-
- // Exceeding that will result in an exception.
- EXPECT_THROW(RdataSet::create(mem_sgmt_, encoder_,
- getRRsetWithRdataCount(8192),
- ConstRRsetPtr()),
- RdataSetError);
- // To be very sure even try larger number than the threshold
- EXPECT_THROW(RdataSet::create(mem_sgmt_, encoder_,
- getRRsetWithRdataCount(65535),
- ConstRRsetPtr()),
- RdataSetError);
-}
-
-TEST_F(RdataSetTest, createWithRRSIG) {
- // Normal case.
- RdataSet* rdataset = RdataSet::create(mem_sgmt_, encoder_, a_rrset_,
- rrsig_rrset_);
- checkRdataSet(*rdataset, true, true);
- RdataSet::destroy(mem_sgmt_, RRClass::IN(), rdataset);
-
- // Unusual case: TTL doesn't match. This implementation accepts that,
- // using the TTL of the covered RRset.
- ConstRRsetPtr rrsig_badttl(textToRRset(
- "www.example.com. 3600 IN RRSIG "
- "A 5 2 3600 20120814220826 "
- "20120715220826 1234 example.com. FAKE"));
- rdataset = RdataSet::create(mem_sgmt_, encoder_, a_rrset_, rrsig_badttl);
- checkRdataSet(*rdataset, true, true);
- RdataSet::destroy(mem_sgmt_, RRClass::IN(), rdataset);
-}
-
-// A helper function to create an RRSIG RRset containing the given number of
-// unique RDATAs.
-ConstRRsetPtr
-getRRSIGWithRdataCount(size_t sig_count) {
- RRsetPtr rrset(new RRset(Name("example.com"), RRClass::IN(),
- RRType::RRSIG(), RRTTL(3600)));
- // We use a base wire-format image and tweak the original TTL field to
- // generate unique RDATAs in the loop. (Creating them from corresponding
- // text is simpler, but doing so for a large number of RRSIGs is
- // relatively heavy and could be too long for unittests).
- ConstRdataPtr rrsig_base =
- rdata::createRdata(RRType::RRSIG(), RRClass::IN(),
- "A 5 2 3600 20120814220826 20120715220826 1234 "
- "example.com. FAKE");
- isc::util::OutputBuffer ob(0);
- rrsig_base->toWire(ob);
- for (size_t i = 0; i < sig_count; ++i) {
- ob.writeUint16At((i >> 16) & 0xffff, 4);
- ob.writeUint16At(i & 0xffff, 6);
- isc::util::InputBuffer ib(ob.getData(), ob.getLength());
- rrset->addRdata(rdata::createRdata(RRType::RRSIG(), RRClass::IN(),
- ib, ib.getLength()));
- }
- return (rrset);
-}
-
-TEST_F(RdataSetTest, createManyRRSIGs) {
- // 7 has a special meaning in the implementation: if the number of the
- // RRSIGs reaches this value, an extra 'sig count' field will be created.
- RdataSet* rdataset = RdataSet::create(mem_sgmt_, encoder_, a_rrset_,
- getRRSIGWithRdataCount(7));
- EXPECT_EQ(7, rdataset->getSigRdataCount());
- RdataSet::destroy(mem_sgmt_, RRClass::IN(), rdataset);
-
- // 8 would cause overflow in the normal 3-bit field if there were no extra
- // count field.
- rdataset = RdataSet::create(mem_sgmt_, encoder_, a_rrset_,
- getRRSIGWithRdataCount(8));
- EXPECT_EQ(8, rdataset->getSigRdataCount());
- RdataSet::destroy(mem_sgmt_, RRClass::IN(), rdataset);
-
- // Up to 2^16-1 RRSIGs are allowed (although that would be useless
- // in practice)
- rdataset = RdataSet::create(mem_sgmt_, encoder_, a_rrset_,
- getRRSIGWithRdataCount(65535));
- EXPECT_EQ(65535, rdataset->getSigRdataCount());
- RdataSet::destroy(mem_sgmt_, RRClass::IN(), rdataset);
-
- // Exceeding this limit will result in an exception.
- EXPECT_THROW(RdataSet::create(mem_sgmt_, encoder_, a_rrset_,
- getRRSIGWithRdataCount(65536)),
- RdataSetError);
- // To be very sure even try larger number than the threshold
- EXPECT_THROW(RdataSet::create(mem_sgmt_, encoder_, a_rrset_,
- getRRSIGWithRdataCount(70000)),
- RdataSetError);
-}
-
-TEST_F(RdataSetTest, createWithRRSIGOnly) {
- // A rare, but allowed, case: RdataSet without the main RRset but with
- // RRSIG.
- RdataSet* rdataset = RdataSet::create(mem_sgmt_, encoder_, ConstRRsetPtr(),
- rrsig_rrset_);
- checkRdataSet(*rdataset, false, true);
- RdataSet::destroy(mem_sgmt_, RRClass::IN(), rdataset);
-}
-
-TEST_F(RdataSetTest, badCeate) {
- // Neither the RRset nor RRSIG RRset is given
- EXPECT_THROW(RdataSet::create(mem_sgmt_, encoder_, ConstRRsetPtr(),
- ConstRRsetPtr()), isc::BadValue);
-
- // Empty RRset (An RRset without RDATA)
- ConstRRsetPtr empty_rrset(new RRset(Name("example.com"), RRClass::IN(),
- RRType::A(), RRTTL(3600)));
- EXPECT_THROW(RdataSet::create(mem_sgmt_, encoder_, empty_rrset,
- ConstRRsetPtr()), isc::BadValue);
- ConstRRsetPtr empty_rrsig(new RRset(Name("example.com"), RRClass::IN(),
- RRType::RRSIG(), RRTTL(3600)));
- EXPECT_THROW(RdataSet::create(mem_sgmt_, encoder_, ConstRRsetPtr(),
- empty_rrsig), isc::BadValue);
-
- // The RRset type and RRSIG's type covered don't match
- ConstRRsetPtr bad_rrsig(textToRRset(
- "www.example.com. 1076895760 IN RRSIG "
- "NS 5 2 3600 20120814220826 20120715220826 "
- "1234 example.com. FAKE"));
- EXPECT_THROW(RdataSet::create(mem_sgmt_, encoder_, a_rrset_, bad_rrsig),
- isc::BadValue);
-
- // Pass non RRSIG for the sig parameter
- EXPECT_THROW(RdataSet::create(mem_sgmt_, encoder_, a_rrset_, a_rrset_),
- isc::BadValue);
-
- // Pass RRSIG for normal RRset (the RdataEncoder will catch this and throw)
- EXPECT_THROW(RdataSet::create(mem_sgmt_, encoder_, rrsig_rrset_,
- rrsig_rrset_),
- isc::BadValue);
-
- // RR class doesn't match between RRset and RRSIG
- ConstRRsetPtr badclass_rrsig(textToRRset(
- "www.example.com. 1076895760 CH RRSIG "
- "A 5 2 3600 20120814220826 "
- "20120715220826 1234 example.com. FAKE",
- RRClass::CH()));
- EXPECT_THROW(RdataSet::create(mem_sgmt_, encoder_, a_rrset_,
- badclass_rrsig),
- isc::BadValue);
-}
-}
diff --git a/src/lib/datasrc/memory/tests/run_unittests.cc b/src/lib/datasrc/memory/tests/run_unittests.cc
deleted file mode 100644
index 6321976..0000000
--- a/src/lib/datasrc/memory/tests/run_unittests.cc
+++ /dev/null
@@ -1,26 +0,0 @@
-// 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 <gtest/gtest.h>
-#include <util/unittests/run_all.h>
-#include <log/logger_support.h>
-
-int
-main(int argc, char* argv[]) {
- ::testing::InitGoogleTest(&argc, argv);
-
- isc::log::initLogger();
-
- return (isc::util::unittests::run_all());
-}
diff --git a/src/lib/datasrc/memory/tests/segment_object_holder_unittest.cc b/src/lib/datasrc/memory/tests/segment_object_holder_unittest.cc
deleted file mode 100644
index d27e364..0000000
--- a/src/lib/datasrc/memory/tests/segment_object_holder_unittest.cc
+++ /dev/null
@@ -1,67 +0,0 @@
-// 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 <util/memory_segment_local.h>
-
-#include <datasrc/memory/segment_object_holder.h>
-
-#include <gtest/gtest.h>
-
-using namespace isc::util;
-using namespace isc::datasrc::memory;
-using namespace isc::datasrc::memory::detail;
-
-namespace {
-const int TEST_ARG_VAL = 42; // arbitrary chosen magic number
-
-class TestObject {
-public:
- static void destroy(MemorySegment& sgmt, TestObject* obj, int arg) {
- sgmt.deallocate(obj, sizeof(*obj));
- EXPECT_EQ(TEST_ARG_VAL, arg);
- }
-};
-
-void
-useHolder(MemorySegment& sgmt, TestObject* obj, bool release) {
- // Create a holder object, check the return value of get(), and,
- // if requested, release the held object. At the end of function
- // the holder is destructed, and if the object hasn't been released by
- // then, it should be deallocated. Passed argument is checked in its
- // deallocate().
-
- typedef SegmentObjectHolder<TestObject, int> HolderType;
- HolderType holder(sgmt, obj, TEST_ARG_VAL);
- EXPECT_EQ(obj, holder.get());
- if (release) {
- EXPECT_EQ(obj, holder.release());
- }
-}
-
-TEST(SegmentObjectHolderTest, foo) {
- MemorySegmentLocal sgmt;
- void* p = sgmt.allocate(sizeof(TestObject));
- TestObject* obj = new(p) TestObject;
-
- // Use holder, and release the content. The memory shouldn't be
- // deallocated.
- useHolder(sgmt, obj, true);
- EXPECT_FALSE(sgmt.allMemoryDeallocated());
-
- // Use holder, and let it deallocate the object. The memory segment
- // should now be empty.
- useHolder(sgmt, obj, false);
- EXPECT_TRUE(sgmt.allMemoryDeallocated());
-}
-}
diff --git a/src/lib/datasrc/memory/tests/testdata/Makefile.am b/src/lib/datasrc/memory/tests/testdata/Makefile.am
deleted file mode 100644
index 2d92266..0000000
--- a/src/lib/datasrc/memory/tests/testdata/Makefile.am
+++ /dev/null
@@ -1,30 +0,0 @@
-CLEANFILES = *.copied
-
-EXTRA_DIST = empty.zone
-EXTRA_DIST += example.org.zone
-EXTRA_DIST += example.org-empty.zone
-
-EXTRA_DIST += example.org-broken1.zone
-EXTRA_DIST += example.org-broken2.zone
-EXTRA_DIST += example.org-cname-and-not-nsec-1.zone
-EXTRA_DIST += example.org-cname-and-not-nsec-2.zone
-EXTRA_DIST += example.org-dname-ns-apex-1.zone
-EXTRA_DIST += example.org-dname-ns-apex-2.zone
-EXTRA_DIST += example.org-dname-ns-nonapex-1.zone
-EXTRA_DIST += example.org-dname-ns-nonapex-2.zone
-EXTRA_DIST += example.org-duplicate-type-bad.zone
-EXTRA_DIST += example.org-duplicate-type.zone
-EXTRA_DIST += example.org-multiple-cname.zone
-EXTRA_DIST += example.org-multiple-dname.zone
-EXTRA_DIST += example.org-multiple-nsec3.zone
-EXTRA_DIST += example.org-multiple-nsec3param.zone
-EXTRA_DIST += example.org-multiple.zone
-EXTRA_DIST += example.org-nsec3-fewer-labels.zone example.org-nsec3-more-labels.zone
-EXTRA_DIST += example.org-nsec3-signed-no-param.zone
-EXTRA_DIST += example.org-nsec3-signed.zone
-EXTRA_DIST += example.org-out-of-zone.zone
-EXTRA_DIST += example.org-rrsig-follows-nothing.zone
-EXTRA_DIST += example.org-rrsigs.zone
-EXTRA_DIST += example.org-wildcard-dname.zone
-EXTRA_DIST += example.org-wildcard-ns.zone
-EXTRA_DIST += example.org-wildcard-nsec3.zone
diff --git a/src/lib/datasrc/memory/tests/testdata/empty.zone b/src/lib/datasrc/memory/tests/testdata/empty.zone
deleted file mode 100644
index e69de29..0000000
diff --git a/src/lib/datasrc/memory/tests/testdata/example.org-broken1.zone b/src/lib/datasrc/memory/tests/testdata/example.org-broken1.zone
deleted file mode 100644
index 317095d..0000000
--- a/src/lib/datasrc/memory/tests/testdata/example.org-broken1.zone
+++ /dev/null
@@ -1 +0,0 @@
-This is a broken zone that should not parse.
diff --git a/src/lib/datasrc/memory/tests/testdata/example.org-broken2.zone b/src/lib/datasrc/memory/tests/testdata/example.org-broken2.zone
deleted file mode 100644
index 2f9fd98..0000000
--- a/src/lib/datasrc/memory/tests/testdata/example.org-broken2.zone
+++ /dev/null
@@ -1,5 +0,0 @@
-;; broken example.org zone, where some RRs are OK, but others aren't
-example.org. 3600 IN SOA ns1.example.org. bugs.x.w.example.org. 73 3600 300 3600000 3600
-ns1.example.org. 3600 IN A 192.0.2.1
-ns2.example.org. 3600 IN A 192.0.2.2
-ns2.a.example.com. 3600 IN AAAA
diff --git a/src/lib/datasrc/memory/tests/testdata/example.org-cname-and-not-nsec-1.zone b/src/lib/datasrc/memory/tests/testdata/example.org-cname-and-not-nsec-1.zone
deleted file mode 100644
index 5533663..0000000
--- a/src/lib/datasrc/memory/tests/testdata/example.org-cname-and-not-nsec-1.zone
+++ /dev/null
@@ -1,4 +0,0 @@
-;; CNAME + other is an error
-example.org. 86400 IN SOA ns.example.org. ns.example.org. 2012091009 7200 3600 2592000 1200
-a.example.org. 7200 IN A 192.168.0.1
-a.example.org. 3600 IN CNAME foo.example.com.
diff --git a/src/lib/datasrc/memory/tests/testdata/example.org-cname-and-not-nsec-2.zone b/src/lib/datasrc/memory/tests/testdata/example.org-cname-and-not-nsec-2.zone
deleted file mode 100644
index 966aeeb..0000000
--- a/src/lib/datasrc/memory/tests/testdata/example.org-cname-and-not-nsec-2.zone
+++ /dev/null
@@ -1,4 +0,0 @@
-;; CNAME + other is an error
-example.org. 86400 IN SOA ns.example.org. ns.example.org. 2012091007 7200 3600 2592000 1200
-a.example.org. 3600 IN CNAME foo.example.com.
-a.example.org. 7200 IN A 192.168.0.1
diff --git a/src/lib/datasrc/memory/tests/testdata/example.org-dname-ns-apex-1.zone b/src/lib/datasrc/memory/tests/testdata/example.org-dname-ns-apex-1.zone
deleted file mode 100644
index f57c25d..0000000
--- a/src/lib/datasrc/memory/tests/testdata/example.org-dname-ns-apex-1.zone
+++ /dev/null
@@ -1,4 +0,0 @@
-;; DNAME + NS (apex)
-example.org. 86400 IN SOA ns.example.org. ns.example.org. 2012091015 7200 3600 2592000 1200
-example.org. 3600 IN DNAME foo.example.com.
-example.org. 3600 IN NS bar.example.com.
diff --git a/src/lib/datasrc/memory/tests/testdata/example.org-dname-ns-apex-2.zone b/src/lib/datasrc/memory/tests/testdata/example.org-dname-ns-apex-2.zone
deleted file mode 100644
index bb3f191..0000000
--- a/src/lib/datasrc/memory/tests/testdata/example.org-dname-ns-apex-2.zone
+++ /dev/null
@@ -1,4 +0,0 @@
-;; DNAME + NS (apex)
-example.org. 86400 IN SOA ns.example.org. ns.example.org. 2012091016 7200 3600 2592000 1200
-example.org. 3600 IN NS bar.example.com.
-example.org. 3600 IN DNAME foo.example.com.
diff --git a/src/lib/datasrc/memory/tests/testdata/example.org-dname-ns-nonapex-1.zone b/src/lib/datasrc/memory/tests/testdata/example.org-dname-ns-nonapex-1.zone
deleted file mode 100644
index 68a9805..0000000
--- a/src/lib/datasrc/memory/tests/testdata/example.org-dname-ns-nonapex-1.zone
+++ /dev/null
@@ -1,4 +0,0 @@
-;; DNAME + NS (non-apex)
-example.org. 86400 IN SOA ns.example.org. ns.example.org. 2012091014 7200 3600 2592000 1200
-ns1.example.org. 3600 IN DNAME foo.example.com.
-ns1.example.org. 3600 IN NS bar.example.com.
diff --git a/src/lib/datasrc/memory/tests/testdata/example.org-dname-ns-nonapex-2.zone b/src/lib/datasrc/memory/tests/testdata/example.org-dname-ns-nonapex-2.zone
deleted file mode 100644
index 1b671dd..0000000
--- a/src/lib/datasrc/memory/tests/testdata/example.org-dname-ns-nonapex-2.zone
+++ /dev/null
@@ -1,4 +0,0 @@
-;; DNAME + NS (non-apex)
-example.org. 86400 IN SOA ns.example.org. ns.example.org. 2012091015 7200 3600 2592000 1200
-ns1.example.org. 3600 IN NS bar.example.com.
-ns1.example.org. 3600 IN DNAME foo.example.com.
diff --git a/src/lib/datasrc/memory/tests/testdata/example.org-duplicate-type-bad.zone b/src/lib/datasrc/memory/tests/testdata/example.org-duplicate-type-bad.zone
deleted file mode 100644
index 06c7dff..0000000
--- a/src/lib/datasrc/memory/tests/testdata/example.org-duplicate-type-bad.zone
+++ /dev/null
@@ -1,4 +0,0 @@
-example.org. 3600 IN SOA ns1.example.org. bugs.x.w.example.org. 77 3600 300 3600000 3600
-ns1.example.org. 3600 IN A 192.168.0.1
-ns1.example.org. 3600 IN AAAA ::1
-ns1.example.org. 3600 IN A 192.168.0.2
diff --git a/src/lib/datasrc/memory/tests/testdata/example.org-duplicate-type.zone b/src/lib/datasrc/memory/tests/testdata/example.org-duplicate-type.zone
deleted file mode 100644
index 6e5c1b8..0000000
--- a/src/lib/datasrc/memory/tests/testdata/example.org-duplicate-type.zone
+++ /dev/null
@@ -1,4 +0,0 @@
-example.org. 3600 IN SOA ns1.example.org. bugs.x.w.example.org. 76 3600 300 3600000 3600
-ns1.example.org. 3600 IN A 192.168.0.1
-ns1.example.org. 3600 IN A 192.168.0.2
-ns1.example.org. 3600 IN AAAA ::1
diff --git a/src/lib/datasrc/memory/tests/testdata/example.org-empty.zone b/src/lib/datasrc/memory/tests/testdata/example.org-empty.zone
deleted file mode 100644
index f11b9b8..0000000
--- a/src/lib/datasrc/memory/tests/testdata/example.org-empty.zone
+++ /dev/null
@@ -1,2 +0,0 @@
-;; empty example.org zone
-example.org. 3600 IN SOA ns1.example.org. bugs.x.w.example.org. 68 3600 300 3600000 3600
diff --git a/src/lib/datasrc/memory/tests/testdata/example.org-multiple-cname.zone b/src/lib/datasrc/memory/tests/testdata/example.org-multiple-cname.zone
deleted file mode 100644
index 0a0c983..0000000
--- a/src/lib/datasrc/memory/tests/testdata/example.org-multiple-cname.zone
+++ /dev/null
@@ -1,3 +0,0 @@
-example.org. 3600 IN SOA ns1.example.org. bugs.x.w.example.org. 78 3600 300 3600000 3600
-ns1.example.org. 3600 IN CNAME foo.example.com.
-ns1.example.org. 3600 IN CNAME bar.example.com.
diff --git a/src/lib/datasrc/memory/tests/testdata/example.org-multiple-dname.zone b/src/lib/datasrc/memory/tests/testdata/example.org-multiple-dname.zone
deleted file mode 100644
index 3d581d0..0000000
--- a/src/lib/datasrc/memory/tests/testdata/example.org-multiple-dname.zone
+++ /dev/null
@@ -1,3 +0,0 @@
-example.org. 3600 IN SOA ns1.example.org. bugs.x.w.example.org. 79 3600 300 3600000 3600
-ns1.example.org. 3600 IN DNAME foo.example.com.
-ns1.example.org. 3600 IN DNAME bar.example.com.
diff --git a/src/lib/datasrc/memory/tests/testdata/example.org-multiple-nsec3.zone b/src/lib/datasrc/memory/tests/testdata/example.org-multiple-nsec3.zone
deleted file mode 100644
index 874023a..0000000
--- a/src/lib/datasrc/memory/tests/testdata/example.org-multiple-nsec3.zone
+++ /dev/null
@@ -1,3 +0,0 @@
-example.org. 86400 IN SOA ns.example.org. ns.example.org. 2012090702 7200 3600 2592000 1200
-09GM5T42SMIMT7R8DF6RTG80SFMS1NLU.example.org. 1200 IN NSEC3 1 0 10 AABBCCDD RKOF8QMFRB5F2V9EJHFBVB2JPVSA0DJD A RRSIG
-09GM5T42SMIMT7R8DF6RTG80SFMS1NLU.example.org. 1200 IN NSEC3 1 0 10 AABBCCDD 09GM5T42SMIMT7R8DF6RTG80SFMS1NLU NS SOA RRSIG DNSKEY NSEC3PARAM
diff --git a/src/lib/datasrc/memory/tests/testdata/example.org-multiple-nsec3param.zone b/src/lib/datasrc/memory/tests/testdata/example.org-multiple-nsec3param.zone
deleted file mode 100644
index 5e69518..0000000
--- a/src/lib/datasrc/memory/tests/testdata/example.org-multiple-nsec3param.zone
+++ /dev/null
@@ -1,3 +0,0 @@
-example.org. 86400 IN SOA ns.example.org. ns.example.org. 2012090700 7200 3600 2592000 1200
-example.org. 0 IN NSEC3PARAM 1 0 10 AABBCCDD
-example.org. 0 IN NSEC3PARAM 1 0 10 AABBCCDD
diff --git a/src/lib/datasrc/memory/tests/testdata/example.org-multiple.zone b/src/lib/datasrc/memory/tests/testdata/example.org-multiple.zone
deleted file mode 100644
index f473ae6..0000000
--- a/src/lib/datasrc/memory/tests/testdata/example.org-multiple.zone
+++ /dev/null
@@ -1,4 +0,0 @@
-;; Multiple RDATA for testing separate RRs iterator
-example.org. 3600 IN SOA ns1.example.org. bugs.x.w.example.org. 78 3600 300 3600000 3600
-a.example.org. 3600 IN A 192.168.0.1
-a.example.org. 3600 IN A 192.168.0.2
diff --git a/src/lib/datasrc/memory/tests/testdata/example.org-nsec3-fewer-labels.zone b/src/lib/datasrc/memory/tests/testdata/example.org-nsec3-fewer-labels.zone
deleted file mode 100644
index 0221269..0000000
--- a/src/lib/datasrc/memory/tests/testdata/example.org-nsec3-fewer-labels.zone
+++ /dev/null
@@ -1,3 +0,0 @@
-;; NSEC3 names with labels != (origin_labels + 1)
-example.org. 86400 IN SOA ns.example.org. ns.example.org. 2012091001 7200 3600 2592000 1200
-example.org. 1200 IN NSEC3 1 0 10 AABBCCDD RKOF8QMFRB5F2V9EJHFBVB2JPVSA0DJD A RRSIG
diff --git a/src/lib/datasrc/memory/tests/testdata/example.org-nsec3-more-labels.zone b/src/lib/datasrc/memory/tests/testdata/example.org-nsec3-more-labels.zone
deleted file mode 100644
index efebcfb..0000000
--- a/src/lib/datasrc/memory/tests/testdata/example.org-nsec3-more-labels.zone
+++ /dev/null
@@ -1,3 +0,0 @@
-;; NSEC3 names with labels != (origin_labels + 1)
-example.org. 86400 IN SOA ns.example.org. ns.example.org. 2012091002 7200 3600 2592000 1200
-a.b.example.org. 1200 IN NSEC3 1 0 10 AABBCCDD RKOF8QMFRB5F2V9EJHFBVB2JPVSA0DJD A RRSIG
diff --git a/src/lib/datasrc/memory/tests/testdata/example.org-nsec3-signed-no-param.zone b/src/lib/datasrc/memory/tests/testdata/example.org-nsec3-signed-no-param.zone
deleted file mode 100644
index 5caa308..0000000
--- a/src/lib/datasrc/memory/tests/testdata/example.org-nsec3-signed-no-param.zone
+++ /dev/null
@@ -1,15 +0,0 @@
-;; This file intentionally removes NSEC3PARAM from example.org.nsec3-signed
-example.org. 86400 IN SOA ns.example.org. ns.example.org. 2012013000 7200 3600 2592000 1200
-example.org. 86400 IN RRSIG SOA 7 2 86400 20120301040838 20120131040838 19562 example.org. Jt9wCRLS5TQxZH0IBqrM9uMGD453rIoxYopfM9AjjRZfEx+HGlBpOZeR pGN7yLcN+URnicOD0ydLHiakaBODiZyNoYCKYG5d2ZOhL+026REnDKNM 0m5T3X3sczP+l55An/GITheTdrKt3Y1Ouc2yKI8ro8JjOxV/a4nGDWjK x9A=
-example.org. 86400 IN NS ns.example.org.
-example.org. 86400 IN RRSIG NS 7 2 86400 20120301040838 20120131040838 19562 example.org. gYXL3xK4IFdJU6TtiVuzqDBb2MeA8xB3AKtHlJGFTfTRNHyuej0ZGovx TeUYsLYmoiGYaJG66iD1tYYFq0qdj0xWq+LEa53ACtKvYf9IIwK4ijJs k0g6xCNavc6/qPppymDhN7MvoFVkW59uJa0HPWlsIIuRlEAr7xyt85vq yoA=
-example.org. 86400 IN DNSKEY 256 3 7 AwEAAbrBkKf2gmGtG4aogZY4svIZCrOLLZlQzVHwz7WxJdTR8iEnvz/x Q/jReDroS5CBZWvzwLlhPIpsJAojx0oj0RvfJNsz3+6LN8q7x9u6+86B 85CYjTk3dcFOebgkF4fXr7/kkOX+ZY94Zk0Z1+pUC3eY4gkKcyME/Uxm O18PBTeB
-example.org. 86400 IN RRSIG DNSKEY 7 2 86400 20120301040838 20120131040838 19562 example.org. d0eLF8JqNHaGuBSX0ashU5c1O/wyWU43UUsKGrMQIoBDiJ588MWQOnas rwvW6vdkLNqRqCsP/B4epV/EtLL0tBsk5SHkTYbNo80gGrBufQ6YrWRr Ile8Z+h+MR4y9DybbjmuNKqaO4uQMg/X6+4HqRAKx1lmZMTcrcVeOwDM ZA4=
-;; example.org. 0 IN NSEC3PARAM 1 0 10 AABBCCDD
-;; example.org. 0 IN RRSIG NSEC3PARAM 7 2 0 20120301040838 20120131040838 19562 example.org. Ggs5MiQDlXXt22Fz9DNg3Ujc0T6MBfumlRkd8/enBbJwLmqw2QXAzDEk pjUeGstCEHKzxJDJstavGoCpTDJgoV4Fd9szooMx69rzPrq9tdoM46jG xZHqw+Pv2fkRGC6aP7ZX1r3Qnpwpk47AQgATftbO4G6KcMcO8JoKE47x XLM=
-ns.example.org. 86400 IN A 192.0.2.1
-ns.example.org. 86400 IN RRSIG A 7 3 86400 20120301040838 20120131040838 19562 example.org. dOH+Dxib8VcGnjLrKILsqDhS1wki6BWk1dZwpOGUGHyLWcLNW8ygWY2o r29jPhHtaFCNWpn46JebgnXDPRiQjaY3dQqL8rcf2QX1e3+Cicw1OSrs S0sUDE5DmMNEac+ZCUQ0quCStZTCldl05hlspV2RS92TpApnoOK0nXMp Uak=
-09GM5T42SMIMT7R8DF6RTG80SFMS1NLU.example.org. 1200 IN NSEC3 1 0 10 AABBCCDD RKOF8QMFRB5F2V9EJHFBVB2JPVSA0DJD A RRSIG
-09GM5T42SMIMT7R8DF6RTG80SFMS1NLU.example.org. 1200 IN RRSIG NSEC3 7 3 1200 20120301040838 20120131040838 19562 example.org. EdwMeepLf//lV+KpCAN+213Scv1rrZyj4i2OwoCP4XxxS3CWGSuvYuKO yfZc8wKRcrD/4YG6nZVXE0s5O8NahjBJmDIyVt4WkfZ6QthxGg8ggLVv cD3dFksPyiKHf/WrTOZPSsxvN5m/i1Ey6+YWS01Gf3WDCMWDauC7Nmh3 CTM=
-RKOF8QMFRB5F2V9EJHFBVB2JPVSA0DJD.example.org. 1200 IN NSEC3 1 0 10 AABBCCDD 09GM5T42SMIMT7R8DF6RTG80SFMS1NLU NS SOA RRSIG DNSKEY NSEC3PARAM
-RKOF8QMFRB5F2V9EJHFBVB2JPVSA0DJD.example.org. 1200 IN RRSIG NSEC3 7 3 1200 20120301040838 20120131040838 19562 example.org. j7d8GL4YqX035FBcPPsEcSWHjWcKdlQMHLL4TB67xVNFnl4SEFQCp4OO AtPap5tkKakwgWxoQVN9XjnqrBz+oQhofDkB3aTatAjIIkcwcnrm3AYQ rTI3E03ySiRwuCPKVmHOLUV2cG6O4xzcmP+MYZcvPTS8V3F5LlaU22i7 A3E=
diff --git a/src/lib/datasrc/memory/tests/testdata/example.org-nsec3-signed.zone b/src/lib/datasrc/memory/tests/testdata/example.org-nsec3-signed.zone
deleted file mode 100644
index 9c1195f..0000000
--- a/src/lib/datasrc/memory/tests/testdata/example.org-nsec3-signed.zone
+++ /dev/null
@@ -1,14 +0,0 @@
-example.org. 86400 IN SOA ns.example.org. ns.example.org. 2012013000 7200 3600 2592000 1200
-example.org. 86400 IN RRSIG SOA 7 2 86400 20120301040838 20120131040838 19562 example.org. Jt9wCRLS5TQxZH0IBqrM9uMGD453rIoxYopfM9AjjRZfEx+HGlBpOZeR pGN7yLcN+URnicOD0ydLHiakaBODiZyNoYCKYG5d2ZOhL+026REnDKNM 0m5T3X3sczP+l55An/GITheTdrKt3Y1Ouc2yKI8ro8JjOxV/a4nGDWjK x9A=
-example.org. 86400 IN NS ns.example.org.
-example.org. 86400 IN RRSIG NS 7 2 86400 20120301040838 20120131040838 19562 example.org. gYXL3xK4IFdJU6TtiVuzqDBb2MeA8xB3AKtHlJGFTfTRNHyuej0ZGovx TeUYsLYmoiGYaJG66iD1tYYFq0qdj0xWq+LEa53ACtKvYf9IIwK4ijJs k0g6xCNavc6/qPppymDhN7MvoFVkW59uJa0HPWlsIIuRlEAr7xyt85vq yoA=
-example.org. 86400 IN DNSKEY 256 3 7 AwEAAbrBkKf2gmGtG4aogZY4svIZCrOLLZlQzVHwz7WxJdTR8iEnvz/x Q/jReDroS5CBZWvzwLlhPIpsJAojx0oj0RvfJNsz3+6LN8q7x9u6+86B 85CYjTk3dcFOebgkF4fXr7/kkOX+ZY94Zk0Z1+pUC3eY4gkKcyME/Uxm O18PBTeB
-example.org. 86400 IN RRSIG DNSKEY 7 2 86400 20120301040838 20120131040838 19562 example.org. d0eLF8JqNHaGuBSX0ashU5c1O/wyWU43UUsKGrMQIoBDiJ588MWQOnas rwvW6vdkLNqRqCsP/B4epV/EtLL0tBsk5SHkTYbNo80gGrBufQ6YrWRr Ile8Z+h+MR4y9DybbjmuNKqaO4uQMg/X6+4HqRAKx1lmZMTcrcVeOwDM ZA4=
-example.org. 0 IN NSEC3PARAM 1 0 10 AABBCCDD
-example.org. 0 IN RRSIG NSEC3PARAM 7 2 0 20120301040838 20120131040838 19562 example.org. Ggs5MiQDlXXt22Fz9DNg3Ujc0T6MBfumlRkd8/enBbJwLmqw2QXAzDEk pjUeGstCEHKzxJDJstavGoCpTDJgoV4Fd9szooMx69rzPrq9tdoM46jG xZHqw+Pv2fkRGC6aP7ZX1r3Qnpwpk47AQgATftbO4G6KcMcO8JoKE47x XLM=
-ns.example.org. 86400 IN A 192.0.2.1
-ns.example.org. 86400 IN RRSIG A 7 3 86400 20120301040838 20120131040838 19562 example.org. dOH+Dxib8VcGnjLrKILsqDhS1wki6BWk1dZwpOGUGHyLWcLNW8ygWY2o r29jPhHtaFCNWpn46JebgnXDPRiQjaY3dQqL8rcf2QX1e3+Cicw1OSrs S0sUDE5DmMNEac+ZCUQ0quCStZTCldl05hlspV2RS92TpApnoOK0nXMp Uak=
-09GM5T42SMIMT7R8DF6RTG80SFMS1NLU.example.org. 1200 IN NSEC3 1 0 10 AABBCCDD RKOF8QMFRB5F2V9EJHFBVB2JPVSA0DJD A RRSIG
-09GM5T42SMIMT7R8DF6RTG80SFMS1NLU.example.org. 1200 IN RRSIG NSEC3 7 3 1200 20120301040838 20120131040838 19562 example.org. EdwMeepLf//lV+KpCAN+213Scv1rrZyj4i2OwoCP4XxxS3CWGSuvYuKO yfZc8wKRcrD/4YG6nZVXE0s5O8NahjBJmDIyVt4WkfZ6QthxGg8ggLVv cD3dFksPyiKHf/WrTOZPSsxvN5m/i1Ey6+YWS01Gf3WDCMWDauC7Nmh3 CTM=
-RKOF8QMFRB5F2V9EJHFBVB2JPVSA0DJD.example.org. 1200 IN NSEC3 1 0 10 AABBCCDD 09GM5T42SMIMT7R8DF6RTG80SFMS1NLU NS SOA RRSIG DNSKEY NSEC3PARAM
-RKOF8QMFRB5F2V9EJHFBVB2JPVSA0DJD.example.org. 1200 IN RRSIG NSEC3 7 3 1200 20120301040838 20120131040838 19562 example.org. j7d8GL4YqX035FBcPPsEcSWHjWcKdlQMHLL4TB67xVNFnl4SEFQCp4OO AtPap5tkKakwgWxoQVN9XjnqrBz+oQhofDkB3aTatAjIIkcwcnrm3AYQ rTI3E03ySiRwuCPKVmHOLUV2cG6O4xzcmP+MYZcvPTS8V3F5LlaU22i7 A3E=
diff --git a/src/lib/datasrc/memory/tests/testdata/example.org-out-of-zone.zone b/src/lib/datasrc/memory/tests/testdata/example.org-out-of-zone.zone
deleted file mode 100644
index e3afb74..0000000
--- a/src/lib/datasrc/memory/tests/testdata/example.org-out-of-zone.zone
+++ /dev/null
@@ -1,5 +0,0 @@
-;; 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. 75 3600 300 3600000 3600
-a.example.com. 3600 IN A 192.168.0.1
diff --git a/src/lib/datasrc/memory/tests/testdata/example.org-rrsig-follows-nothing.zone b/src/lib/datasrc/memory/tests/testdata/example.org-rrsig-follows-nothing.zone
deleted file mode 100644
index ef5f887..0000000
--- a/src/lib/datasrc/memory/tests/testdata/example.org-rrsig-follows-nothing.zone
+++ /dev/null
@@ -1,5 +0,0 @@
-;; 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. 68 3600 300 3600000 3600
-ns1.example.org. 3600 IN RRSIG A 7 3 3600 20150420235959 20051021000000 40430 example.org. FAKEFAKE
diff --git a/src/lib/datasrc/memory/tests/testdata/example.org-rrsigs.zone b/src/lib/datasrc/memory/tests/testdata/example.org-rrsigs.zone
deleted file mode 100644
index 1c780b1..0000000
--- a/src/lib/datasrc/memory/tests/testdata/example.org-rrsigs.zone
+++ /dev/null
@@ -1,8 +0,0 @@
-;; 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. 74 3600 300 3600000 3600
-ns1.example.org. 3600 IN A 192.168.0.1
-ns1.example.org. 3600 IN RRSIG A 7 3 3600 20150420235959 20051021000000 40430 example.org. FAKEFAKE
-ns1.example.org. 3600 IN RRSIG A 7 2 3600 20150420235959 20051021000000 40430 example.org. FAKEFAKE
-ns1.example.org. 3600 IN AAAA ::1
diff --git a/src/lib/datasrc/memory/tests/testdata/example.org-wildcard-dname.zone b/src/lib/datasrc/memory/tests/testdata/example.org-wildcard-dname.zone
deleted file mode 100644
index 0d03b0d..0000000
--- a/src/lib/datasrc/memory/tests/testdata/example.org-wildcard-dname.zone
+++ /dev/null
@@ -1,4 +0,0 @@
-;; test zone file with wildcard DNAME names
-
-example.org. 3600 IN SOA ns1.example.org. bugs.x.w.example.org. 79 3600 300 3600000 3600
-*.example.org. 3600 IN DNAME dname.example.com.
diff --git a/src/lib/datasrc/memory/tests/testdata/example.org-wildcard-ns.zone b/src/lib/datasrc/memory/tests/testdata/example.org-wildcard-ns.zone
deleted file mode 100644
index 2933515..0000000
--- a/src/lib/datasrc/memory/tests/testdata/example.org-wildcard-ns.zone
+++ /dev/null
@@ -1,4 +0,0 @@
-;; test zone file with wildcard NS names
-
-example.org. 3600 IN SOA ns1.example.org. bugs.x.w.example.org. 78 3600 300 3600000 3600
-*.example.org. 3600 IN NS ns1.example.org.
diff --git a/src/lib/datasrc/memory/tests/testdata/example.org-wildcard-nsec3.zone b/src/lib/datasrc/memory/tests/testdata/example.org-wildcard-nsec3.zone
deleted file mode 100644
index feee116..0000000
--- a/src/lib/datasrc/memory/tests/testdata/example.org-wildcard-nsec3.zone
+++ /dev/null
@@ -1,4 +0,0 @@
-;; test zone file with wildcard NS names
-
-example.org. 3600 IN SOA ns1.example.org. bugs.x.w.example.org. 79 3600 300 3600000 3600
-*.example.org. 1200 IN NSEC3 1 0 10 AABBCCDD RKOF8QMFRB5F2V9EJHFBVB2JPVSA0DJD A RRSIG
diff --git a/src/lib/datasrc/memory/tests/testdata/example.org.zone b/src/lib/datasrc/memory/tests/testdata/example.org.zone
deleted file mode 100644
index e499e0d..0000000
--- a/src/lib/datasrc/memory/tests/testdata/example.org.zone
+++ /dev/null
@@ -1,81 +0,0 @@
-;; 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. 67 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). One of the NS names matches an empty wildcard node, for
-;; which no additional record should be provided (or any other
-;; disruption should happen).
-e.example.org. 3600 IN NS ns.wild.example.org.
-e.example.org. 3600 IN NS ns.emptywild.example.org.
-e.example.org. 3600 IN NS ns2.example.org.
-*.wild.example.org. 3600 IN A 192.0.2.15
-a.*.emptywild.example.org. 3600 IN AAAA 2001:db8::2
-
-;; 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/memory/tests/treenode_rrset_unittest.cc b/src/lib/datasrc/memory/tests/treenode_rrset_unittest.cc
deleted file mode 100644
index 182cca6..0000000
--- a/src/lib/datasrc/memory/tests/treenode_rrset_unittest.cc
+++ /dev/null
@@ -1,591 +0,0 @@
-// 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 <util/buffer.h>
-#include <util/memory_segment_local.h>
-
-#include <datasrc/memory/treenode_rrset.h>
-#include <datasrc/memory/rdataset.h>
-#include <datasrc/memory/rdata_serialization.h>
-#include <datasrc/memory/zone_data.h>
-
-#include <util/unittests/wiredata.h>
-#include <testutils/dnsmessage_test.h>
-
-#include <gtest/gtest.h>
-
-#include <boost/shared_ptr.hpp>
-
-#include <string>
-#include <vector>
-
-using std::vector;
-using std::string;
-using namespace isc::dns;
-using namespace isc::dns::rdata;
-using namespace isc::datasrc::memory;
-using namespace isc::testutils;
-using isc::util::unittests::matchWireData;
-using isc::util::OutputBuffer;
-
-namespace {
-
-class TreeNodeRRsetTest : public ::testing::Test {
-protected:
- TreeNodeRRsetTest() :
- rrclass_(RRClass::IN()),
- origin_name_("example.com"), www_name_("www.example.com"),
- wildcard_name_("*.example.com"), match_name_("match.example.com"),
- ns_rrset_(textToRRset("example.com. 3600 IN NS ns.example.com.")),
- a_rrset_(textToRRset("www.example.com. 3600 IN A 192.0.2.1\n"
- "www.example.com. 3600 IN A 192.0.2.2")),
- aaaa_rrset_(textToRRset("www.example.com. 3600 IN AAAA "
- "2001:db8::1\n")),
- dname_rrset_(textToRRset("example.com. 3600 IN DNAME d.example.org.")),
- a_rrsig_rrset_(textToRRset("www.example.com. 3600 IN RRSIG "
- "A 5 2 3600 20120814220826 20120715220826 "
- "1234 example.com. FAKE")),
- aaaa_rrsig_rrset_(textToRRset("www.example.com. 3600 IN RRSIG AAAA 5 2"
- " 3600 20120814220826 20120715220826 "
- "1234 example.com. FAKE\n"
- "www.example.com. 3600 IN RRSIG AAAA 5 2"
- " 3600 20120814220826 20120715220826 "
- "4321 example.com. FAKE\n")),
- txt_rrsig_rrset_(textToRRset("www.example.com. 3600 IN RRSIG TXT 5 2"
- " 3600 20120814220826 20120715220826 "
- "1234 example.com. FAKE\n")),
- wildmatch_rrset_(textToRRset(
- "match.example.com. 3600 IN A 192.0.2.1\n"
- "match.example.com. 3600 IN A 192.0.2.2")),
- wildmatch_rrsig_rrset_(textToRRset(
- "match.example.com. 3600 IN RRSIG "
- "A 5 2 3600 20120814220826 20120715220826 "
- "1234 example.com. FAKE")),
- zone_data_(NULL), origin_node_(NULL), www_node_(NULL),
- wildcard_node_(NULL), ns_rdataset_(NULL), dname_rdataset_(NULL),
- a_rdataset_(NULL), aaaa_rdataset_(NULL), rrsig_only_rdataset_(NULL),
- wildcard_rdataset_(NULL)
- {}
- void SetUp() {
- // We create some common test data here in SetUp() so it will be
- // as exception safe as possible.
-
- zone_data_ = ZoneData::create(mem_sgmt_, origin_name_);
-
- zone_data_->insertName(mem_sgmt_, origin_name_, &origin_node_);
- ns_rdataset_ = RdataSet::create(mem_sgmt_, encoder_, ns_rrset_,
- ConstRRsetPtr());
- origin_node_->setData(ns_rdataset_);
- dname_rdataset_ = RdataSet::create(mem_sgmt_, encoder_, dname_rrset_,
- ConstRRsetPtr());
- ns_rdataset_->next = dname_rdataset_;
-
- zone_data_->insertName(mem_sgmt_, www_name_, &www_node_);
- a_rdataset_ = RdataSet::create(mem_sgmt_, encoder_, a_rrset_,
- a_rrsig_rrset_);
- www_node_->setData(a_rdataset_);
-
- aaaa_rdataset_ = RdataSet::create(mem_sgmt_, encoder_, aaaa_rrset_,
- aaaa_rrsig_rrset_);
- a_rdataset_->next = aaaa_rdataset_;
-
- // A rare (half broken) case of RRSIG-only set
- rrsig_only_rdataset_ = RdataSet::create(mem_sgmt_, encoder_,
- ConstRRsetPtr(),
- txt_rrsig_rrset_);
- aaaa_rdataset_->next = rrsig_only_rdataset_;
-
- zone_data_->insertName(mem_sgmt_, wildcard_name_, &wildcard_node_);
- wildcard_rdataset_ = RdataSet::create(mem_sgmt_, encoder_, a_rrset_,
- a_rrsig_rrset_);
- wildcard_node_->setData(wildcard_rdataset_);
- }
- void TearDown() {
- ZoneData::destroy(mem_sgmt_, zone_data_, rrclass_);
- // detect any memory leak
- EXPECT_TRUE(mem_sgmt_.allMemoryDeallocated());
- }
-
- const RRClass rrclass_;
- const Name origin_name_, www_name_, wildcard_name_, match_name_;
- isc::util::MemorySegmentLocal mem_sgmt_;
- RdataEncoder encoder_;
- MessageRenderer renderer_, renderer_expected_;
- ConstRRsetPtr ns_rrset_, a_rrset_, aaaa_rrset_, dname_rrset_,
- a_rrsig_rrset_, aaaa_rrsig_rrset_, txt_rrsig_rrset_,
- wildmatch_rrset_, wildmatch_rrsig_rrset_;
- ZoneData* zone_data_;
- ZoneNode* origin_node_;
- ZoneNode* www_node_;
- ZoneNode* wildcard_node_;
- RdataSet* ns_rdataset_;
- RdataSet* dname_rdataset_;
- RdataSet* a_rdataset_;
- RdataSet* aaaa_rdataset_;
- RdataSet* rrsig_only_rdataset_;
- RdataSet* wildcard_rdataset_; // for wildcard (type doesn't matter much)
-};
-
-// Check some trivial fields of a constructed TreeNodeRRset (passed as
-// AbstractRRset as we'd normally use it in polymorphic way).
-// Other complicated fields are checked through rendering tests.
-void
-checkBasicFields(const AbstractRRset& actual_rrset, const Name& expected_name,
- const RRClass& expected_class, const RRType& expected_type,
- const uint32_t expected_ttl,
- size_t expected_rdatacount, size_t expected_sigcount)
-{
- EXPECT_EQ(expected_name, actual_rrset.getName());
- EXPECT_EQ(expected_class, actual_rrset.getClass());
- EXPECT_EQ(expected_type, actual_rrset.getType());
- EXPECT_EQ(RRTTL(expected_ttl), actual_rrset.getTTL());
- EXPECT_EQ(expected_rdatacount, actual_rrset.getRdataCount());
- EXPECT_EQ(expected_sigcount, actual_rrset.getRRsigDataCount());
-}
-
-// The following two are trivial wrapper to create a shared pointer
-// version of TreeNodeRRset object in order to work around dubious
-// behavior of some C++ compiler: they reject getting a const reference to
-// a temporary non-copyable object.
-boost::shared_ptr<TreeNodeRRset>
-createRRset(const RRClass& rrclass, const ZoneNode* node,
- const RdataSet* rdataset, bool dnssec_ok)
-{
- return (boost::shared_ptr<TreeNodeRRset>(
- new TreeNodeRRset(rrclass, node, rdataset, dnssec_ok)));
-}
-
-boost::shared_ptr<TreeNodeRRset>
-createRRset(const Name& realname, const RRClass& rrclass, const ZoneNode* node,
- const RdataSet* rdataset, bool dnssec_ok)
-{
- return (boost::shared_ptr<TreeNodeRRset>(
- new TreeNodeRRset(realname, rrclass, node, rdataset,
- dnssec_ok)));
-}
-
-TEST_F(TreeNodeRRsetTest, create) {
- // Constructed with RRSIG, and it should be visible.
- checkBasicFields(*createRRset(rrclass_, www_node_, a_rdataset_, true),
- www_name_, rrclass_, RRType::A(), 3600, 2, 1);
- // Constructed with RRSIG, and it should be invisible.
- checkBasicFields(*createRRset(rrclass_, www_node_, a_rdataset_, false),
- www_name_, rrclass_, RRType::A(), 3600, 2, 0);
- // Constructed without RRSIG, and it would be visible (but of course won't)
- checkBasicFields(*createRRset(rrclass_, origin_node_, ns_rdataset_, true),
- origin_name_, rrclass_, RRType::NS(), 3600, 1, 0);
- // Constructed without RRSIG, and it should be visible
- checkBasicFields(*createRRset(rrclass_, origin_node_, ns_rdataset_, false),
- origin_name_, rrclass_, RRType::NS(), 3600, 1, 0);
- // RRSIG-only case (note the RRset's type is covered type)
- checkBasicFields(*createRRset(rrclass_, www_node_, rrsig_only_rdataset_,
- true),
- www_name_, rrclass_, RRType::TXT(), 3600, 0, 1);
- // RRSIG-only case (note the RRset's type is covered type), but it's
- // invisible
- checkBasicFields(*createRRset(rrclass_, www_node_, rrsig_only_rdataset_,
- false),
- www_name_, rrclass_, RRType::TXT(), 3600, 0, 0);
- // Wildcard substitution
- checkBasicFields(*createRRset(match_name_, rrclass_,
- wildcard_node_, wildcard_rdataset_,
- true),
- match_name_, rrclass_, RRType::A(), 3600, 2, 1);
-}
-
-// The following two templated functions are helper to encapsulate the
-// concept truncation and handle MessageRenderer and OutputBuffer transparently
-// in templated test cases.
-template <typename OutputType>
-void
-setOutputLengthLimit(OutputType& output, size_t len_limit) {
- output.setLengthLimit(len_limit);
-}
-template <>
-void
-setOutputLengthLimit<OutputBuffer>(OutputBuffer&, size_t) {
-}
-
-template <typename OutputType>
-bool
-isOutputTruncated(OutputType& output) {
- return (output.isTruncated());
-}
-template <>
-bool
-isOutputTruncated<OutputBuffer>(OutputBuffer&) {
- return (false);
-}
-
-// Templated so we so can support OutputBuffer version of toWire().
-// We use the above helper templated functions for some renderer only methods.
-// We test two sets of cases: normal rendering case and case when truncation
-// is expected. The latter is effectively for MessageRenderer only.
-// If len_limit == 0, we consider it the normal case; otherwise it's for
-// truncation. prepended_name isn't used for the truncation case.
-template <typename OutputType>
-void
-checkToWireResult(OutputType& expected_output, OutputType& actual_output,
- const AbstractRRset& actual_rrset,
- const Name& prepended_name,
- ConstRRsetPtr rrset, ConstRRsetPtr rrsig_rrset,
- bool dnssec_ok,
- size_t len_limit = 0,
- size_t expected_result = 0)
-{
- expected_output.clear();
- actual_output.clear();
-
- if (len_limit == 0) { // normal rendering
- // Prepare "actual" rendered data. We prepend a name to confirm the
- // owner name should be compressed in both cases.
- prepended_name.toWire(actual_output);
- const size_t rdata_count = rrset ? rrset->getRdataCount() : 0;
- const int expected_ret = (dnssec_ok && rrsig_rrset) ?
- rdata_count + rrsig_rrset->getRdataCount() : rdata_count;
- EXPECT_EQ(expected_ret, actual_rrset.toWire(actual_output));
- } else { // truncation
- setOutputLengthLimit(actual_output, len_limit);
- EXPECT_EQ(expected_result, actual_rrset.toWire(actual_output));
- EXPECT_TRUE(isOutputTruncated(actual_output)); // always true here
- }
-
- // Prepare "expected" data.
- if (len_limit == 0) { // normal rendering
- prepended_name.toWire(expected_output);
- } else { // truncation
- setOutputLengthLimit(expected_output, len_limit);
- }
- if (rrset) {
- rrset->toWire(expected_output);
- }
- if (!isOutputTruncated(expected_output) && dnssec_ok && rrsig_rrset) {
- rrsig_rrset->toWire(expected_output);
- }
-
- // Compare the two.
- matchWireData(expected_output.getData(), expected_output.getLength(),
- actual_output.getData(), actual_output.getLength());
-}
-
-TEST_F(TreeNodeRRsetTest, toWire) {
- MessageRenderer expected_renderer, actual_renderer;
- OutputBuffer expected_buffer(0), actual_buffer(0);
-
- {
- SCOPED_TRACE("with RRSIG, DNSSEC OK");
- const TreeNodeRRset rrset(rrclass_, www_node_, a_rdataset_, true);
- checkToWireResult(expected_renderer, actual_renderer, rrset,
- www_name_, a_rrset_, a_rrsig_rrset_, true);
- // Currently the buffer version throws
- EXPECT_THROW(
- checkToWireResult(expected_buffer, actual_buffer, rrset,
- www_name_, a_rrset_, a_rrsig_rrset_, true),
- isc::Unexpected);
- }
-
- {
- SCOPED_TRACE("with RRSIG, DNSSEC not OK");
- const TreeNodeRRset rrset(rrclass_, www_node_, a_rdataset_, false);
- checkToWireResult(expected_renderer, actual_renderer, rrset,
- www_name_, a_rrset_, a_rrsig_rrset_, false);
- }
-
- {
- SCOPED_TRACE("without RRSIG, DNSSEC OK");
- const TreeNodeRRset rrset(rrclass_, origin_node_, ns_rdataset_, true);
- checkToWireResult(expected_renderer, actual_renderer, rrset,
- origin_name_, ns_rrset_, ConstRRsetPtr(), true);
- }
-
- {
- SCOPED_TRACE("without RRSIG, DNSSEC not OK");
- const TreeNodeRRset rrset(rrclass_, origin_node_, ns_rdataset_,
- false);
- checkToWireResult(expected_renderer, actual_renderer, rrset,
- origin_name_, ns_rrset_, ConstRRsetPtr(), false);
- }
-
- {
- // RDATA of DNAME DR shouldn't be compressed. Prepending "example.org"
- // will check that.
- SCOPED_TRACE("uncompressed RDATA");
- const TreeNodeRRset rrset(rrclass_, origin_node_, dname_rdataset_,
- false);
- checkToWireResult(expected_renderer, actual_renderer, rrset,
- Name("example.org"), dname_rrset_, ConstRRsetPtr(),
- false);
- }
-
- {
- SCOPED_TRACE("wildcard with RRSIG");
- checkToWireResult(expected_renderer, actual_renderer,
- *createRRset(match_name_, rrclass_, wildcard_node_,
- wildcard_rdataset_, true),
- origin_name_, wildmatch_rrset_,
- wildmatch_rrsig_rrset_, true);
- }
-
- {
- SCOPED_TRACE("wildcard without RRSIG");
- checkToWireResult(expected_renderer, actual_renderer,
- *createRRset(match_name_, rrclass_, wildcard_node_,
- wildcard_rdataset_, false),
- origin_name_, wildmatch_rrset_,
- wildmatch_rrsig_rrset_, false);
- }
-
- {
- // Very unusual case: the set only contains RRSIG (already rare)
- // and it's requested to be dumped to wire (can only happen in
- // ANY or type-RRSIG queries, which are rare also). But can still
- // happen.
- SCOPED_TRACE("RRSIG only, DNSSEC OK");
- const TreeNodeRRset rrset(rrclass_, www_node_, rrsig_only_rdataset_,
- true);
- checkToWireResult(expected_renderer, actual_renderer, rrset,
- www_name_, ConstRRsetPtr(), txt_rrsig_rrset_,true);
- }
-
- {
- // Similar to the previous case, but DNSSEC records aren't requested.
- // In practice this case wouldn't happen, but API-wise possible, so
- // we test it explicitly.
- SCOPED_TRACE("RRSIG only, DNSSEC not OK");
- const TreeNodeRRset rrset(rrclass_, www_node_, rrsig_only_rdataset_,
- false);
- checkToWireResult(expected_renderer, actual_renderer, rrset,
- www_name_, ConstRRsetPtr(), txt_rrsig_rrset_,false);
- }
-}
-
-TEST_F(TreeNodeRRsetTest, toWireTruncated) {
- MessageRenderer expected_renderer, actual_renderer;
- // dummy parameter to checkToWireResult (unused for the this test case)
- const Name& name = Name::ROOT_NAME();
-
- // Set the truncation limit to name len + 14 bytes of fixed data for A RR
- // (type, class, TTL, rdlen, and 4-byte IPv4 address). Then we can only
- // render just one RR, without any garbage trailing data.
- checkToWireResult(expected_renderer, actual_renderer,
- *createRRset(rrclass_, www_node_, a_rdataset_, true),
- name, a_rrset_, a_rrsig_rrset_, true,
- www_name_.getLength() + 14,
- 1); // 1 main RR, no RRSIG
-
- // The first main RRs should fit in the renderer (the name will be
- // fully compressed, so its size is 2 bytes), but the RRSIG doesn't.
- checkToWireResult(expected_renderer, actual_renderer,
- *createRRset(rrclass_, www_node_, a_rdataset_, true),
- name, a_rrset_, a_rrsig_rrset_, true,
- www_name_.getLength() + 14 + 2 + 14,
- 2); // 2 main RR, no RRSIG
-
- // This RRset has one main RR and two RRSIGs. Rendering the second RRSIG
- // causes truncation.
- // First, compute the rendered length for the main RR and a single RRSIG.
- // The length of the RRSIG should be the same if we "accidentally"
- // rendered the RRSIG for the A RR (which only contains one RRSIG).
- expected_renderer.clear();
- aaaa_rrset_->toWire(expected_renderer);
- a_rrsig_rrset_->toWire(expected_renderer);
- const size_t limit_len = expected_renderer.getLength();
- // Then perform the test
- checkToWireResult(expected_renderer, actual_renderer,
- *createRRset(rrclass_, www_node_, aaaa_rdataset_, true),
- name, aaaa_rrset_, aaaa_rrsig_rrset_, true, limit_len,
- 2); // 1 main RR, 1 RRSIG
-
- // RRSIG only case. Render length limit being 1, so it won't fit,
- // and will cause truncation.
- checkToWireResult(expected_renderer, actual_renderer,
- *createRRset(rrclass_, www_node_, rrsig_only_rdataset_,
- true),
- name, ConstRRsetPtr(), txt_rrsig_rrset_, true, 1,
- 0); // no RR
-}
-
-void
-checkRdataIterator(const vector<string>& expected, RdataIteratorPtr rit) {
- for (vector<string>::const_iterator it = expected.begin();
- it != expected.end();
- ++it)
- {
- ASSERT_FALSE(rit->isLast());
- EXPECT_EQ(*it, rit->getCurrent().toText());
- rit->next();
- }
- // We should have reached the end of RDATA
- EXPECT_TRUE(rit->isLast());
-
- // move to the first RDATA again, and check the value.
- rit->first();
- if (!expected.empty()) {
- EXPECT_EQ(expected[0], rit->getCurrent().toText());
- } else {
- EXPECT_TRUE(rit->isLast());
- }
-}
-
-TEST_F(TreeNodeRRsetTest, getRdataIterator) {
- // This RRset should have 2 A RDATAs
- vector<string> expected;
- expected.push_back("192.0.2.1");
- expected.push_back("192.0.2.2");
- checkRdataIterator(expected,
- TreeNodeRRset(rrclass_, www_node_, a_rdataset_, true).
- getRdataIterator());
-
- // The iterator shouldn't work different with or without RRSIG
- checkRdataIterator(expected,
- TreeNodeRRset(rrclass_, www_node_, a_rdataset_, false).
- getRdataIterator());
-
- // This RRset should have 1 NS RDATA (containing name field)
- expected.clear();
- expected.push_back("ns.example.com.");
- checkRdataIterator(expected,
- TreeNodeRRset(rrclass_, origin_node_, ns_rdataset_,
- false).getRdataIterator());
-
- // RRSIG only. Iterator will be empty and shouldn't cause any disruption.
- expected.clear();
- checkRdataIterator(expected,
- TreeNodeRRset(rrclass_, www_node_, rrsig_only_rdataset_,
- true).getRdataIterator());
-}
-
-void
-checkToText(const AbstractRRset& actual_rrset,
- ConstRRsetPtr expected_rrset, ConstRRsetPtr expected_sig_rrset)
-{
- const string actual_text = actual_rrset.toText();
- const string expected_text =
- (expected_rrset ? expected_rrset->toText() : "") +
- (expected_sig_rrset ? expected_sig_rrset->toText() : "");
- EXPECT_EQ(expected_text, actual_text);
-}
-
-TEST_F(TreeNodeRRsetTest, toText) {
- // Constructed with RRSIG, and it should be visible.
- checkToText(*createRRset(rrclass_, www_node_, a_rdataset_, true),
- a_rrset_, a_rrsig_rrset_);
- // Constructed with RRSIG, and it should be invisible.
- checkToText(*createRRset(rrclass_, www_node_, a_rdataset_, false),
- a_rrset_, ConstRRsetPtr());
- // Constructed without RRSIG, and it would be visible (but of course won't)
- checkToText(*createRRset(rrclass_, origin_node_, ns_rdataset_, true),
- ns_rrset_, ConstRRsetPtr());
- // Constructed without RRSIG, and it should be visible
- checkToText(*createRRset(rrclass_, origin_node_, ns_rdataset_, false),
- ns_rrset_, ConstRRsetPtr());
- // Wildcard expanded name with RRSIG
- checkToText(*createRRset(match_name_, rrclass_, wildcard_node_,
- wildcard_rdataset_, true),
- wildmatch_rrset_, wildmatch_rrsig_rrset_);
- // Wildcard expanded name without RRSIG
- checkToText(*createRRset(match_name_, rrclass_, wildcard_node_,
- wildcard_rdataset_, false),
- wildmatch_rrset_, ConstRRsetPtr());
- // RRSIG case
- checkToText(*createRRset(rrclass_, www_node_, rrsig_only_rdataset_,
- true),
- ConstRRsetPtr(), txt_rrsig_rrset_);
- // Similar to the previous case, but completely empty.
- checkToText(*createRRset(rrclass_, www_node_, rrsig_only_rdataset_,
- false),
- ConstRRsetPtr(), ConstRRsetPtr());
-}
-
-TEST_F(TreeNodeRRsetTest, isSameKind) {
- const TreeNodeRRset rrset(rrclass_, www_node_, a_rdataset_, true);
-
- // Same name (node), same type (rdataset) => same kind
- EXPECT_TRUE(rrset.isSameKind(*createRRset(rrclass_, www_node_,
- a_rdataset_, true)));
-
- // Same name (node), different type (rdataset) => not same kind
- EXPECT_FALSE(rrset.isSameKind(*createRRset(rrclass_, www_node_,
- aaaa_rdataset_, true)));
-
- // Different name, different type => not same kind
- EXPECT_FALSE(rrset.isSameKind(*createRRset(rrclass_, origin_node_,
- ns_rdataset_, true)));
-
- // Different name, same type => not same kind.
- // Note: this shouldn't happen in our in-memory data source implementation,
- // but API doesn't prohibit it.
- EXPECT_FALSE(rrset.isSameKind(*createRRset(rrclass_, origin_node_,
- a_rdataset_, true)));
-
- // Wildcard and expanded RRset
- const TreeNodeRRset wildcard_rrset(rrclass_, wildcard_node_,
- wildcard_rdataset_, true);
- const TreeNodeRRset match_rrset(match_name_, rrclass_, wildcard_node_,
- wildcard_rdataset_, true);
- EXPECT_FALSE(wildcard_rrset.isSameKind(match_rrset));
- EXPECT_FALSE(match_rrset.isSameKind(wildcard_rrset));
-
- // Both are wildcard expanded, and have different names
- const TreeNodeRRset match2_rrset(Name("match2.example.com"), rrclass_,
- wildcard_node_, wildcard_rdataset_, true);
- EXPECT_FALSE(match_rrset.isSameKind(match2_rrset));
- EXPECT_FALSE(match2_rrset.isSameKind(match_rrset));
-
- // Pathological case. "badwild" is constructed as if expanded due to
- // a wildcard, but has the same owner name of the wildcard itself.
- // Technically, they should be considered of the same kind, but this
- // implementation considers they are not. But this case shouldn't happen
- // as long as the RRsets are only constructed inside the in-memory
- // zone finder implementation.
- const TreeNodeRRset badwild_rrset(wildcard_name_, rrclass_, wildcard_node_,
- wildcard_rdataset_, true);
- EXPECT_FALSE(wildcard_rrset.isSameKind(badwild_rrset));
- EXPECT_EQ(wildcard_rrset.toText(), badwild_rrset.toText());
-
- // Pathological case: Same name, same type, but different class.
- // This case should be impossible because if the RRsets share the same
- // tree node, they must belong to the same RR class. This case is
- // a caller's bug, and the isSameKind() implementation returns the
- // "wrong" (= true) answer.
- EXPECT_TRUE(rrset.isSameKind(*createRRset(RRClass::CH(), www_node_,
- a_rdataset_, true)));
-
- // Same kind of different RRset class
- EXPECT_TRUE(rrset.isSameKind(*a_rrset_));
-
- // Different kind of different RRset class
- EXPECT_FALSE(rrset.isSameKind(*aaaa_rrset_));
-}
-
-TEST_F(TreeNodeRRsetTest, unexpectedMethods) {
- // Note: buffer version of toWire() is checked in the toWire test.
-
- TreeNodeRRset rrset(rrclass_, www_node_, a_rdataset_, true);
-
- EXPECT_THROW(rrset.setTTL(RRTTL(0)), isc::Unexpected);
- EXPECT_THROW(rrset.setName(Name("example")), isc::Unexpected);
- EXPECT_THROW(rrset.addRdata(createRdata(RRType::A(), rrclass_, "0.0.0.0")),
- isc::Unexpected);
- EXPECT_THROW(rrset.getRRsig(), isc::Unexpected);
- RdataPtr sig_rdata = createRdata(
- RRType::RRSIG(), rrclass_,
- "A 5 2 3600 20120814220826 20120715220826 5300 example.com. FAKE");
- EXPECT_THROW(rrset.addRRsig(sig_rdata), isc::Unexpected);
- EXPECT_THROW(rrset.addRRsig(*a_rrsig_rrset_), isc::Unexpected);
- EXPECT_THROW(rrset.addRRsig(a_rrsig_rrset_), isc::Unexpected);
- EXPECT_THROW(rrset.addRRsig(RRsetPtr()), isc::Unexpected);
- EXPECT_THROW(rrset.removeRRsig(), isc::Unexpected);
-}
-}
diff --git a/src/lib/datasrc/memory/tests/zone_data_unittest.cc b/src/lib/datasrc/memory/tests/zone_data_unittest.cc
deleted file mode 100644
index d15fe8b..0000000
--- a/src/lib/datasrc/memory/tests/zone_data_unittest.cc
+++ /dev/null
@@ -1,255 +0,0 @@
-// 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 "memory_segment_test.h"
-
-#include <dns/rdataclass.h>
-
-#include <exceptions/exceptions.h>
-
-#include <dns/name.h>
-#include <dns/labelsequence.h>
-#include <dns/rrclass.h>
-
-#include <datasrc/memory/rdata_serialization.h>
-#include <datasrc/memory/rdataset.h>
-#include <datasrc/memory/zone_data.h>
-
-#include <testutils/dnsmessage_test.h>
-
-#include <gtest/gtest.h>
-
-#include <new> // for bad_alloc
-#include <string>
-
-using namespace isc::dns;
-using namespace isc::dns::rdata;
-using namespace isc::datasrc::memory;
-using namespace isc::datasrc::memory::test;
-using namespace isc::testutils;
-
-namespace {
-
-// With this single fixture we'll test both NSEC3Data and ZoneData
-class ZoneDataTest : public ::testing::Test {
-protected:
- ZoneDataTest() :
- nsec3_data_(NULL), param_rdata_("1 0 12 aabbccdd"),
- param_rdata_nosalt_("1 1 10 -"),
- param_rdata_largesalt_("2 0 5 " + std::string(255 * 2, 'a')),
- nsec3_rdata_("1 0 12 aabbccdd TDK23RP6 SOA"),
- nsec3_rdata_nosalt_("1 1 10 - TDK23RP6 SOA"),
- nsec3_rdata_largesalt_("2 0 5 " + std::string(255 * 2, 'a') +
- " TDK23RP6 SOA"),
- zname_("example.com"),
- zone_data_(ZoneData::create(mem_sgmt_, zname_)),
- a_rrset_(textToRRset("www.example.com. 3600 IN A 192.0.2.1")),
- aaaa_rrset_(textToRRset("www.example.com. 3600 IN AAAA 2001:db8::1")),
- nsec3_rrset_(textToRRset("TDK23RP6.example.com. 3600 IN NSEC3 "
- "1 0 12 aabbccdd TDK23RP6 SOA"))
- {}
- void TearDown() {
- if (nsec3_data_ != NULL) {
- NSEC3Data::destroy(mem_sgmt_, nsec3_data_, RRClass::IN());
- }
- if (zone_data_ != NULL) {
- ZoneData::destroy(mem_sgmt_, zone_data_, RRClass::IN());
- }
- // detect any memory leak in the test memory segment
- EXPECT_TRUE(mem_sgmt_.allMemoryDeallocated());
- }
-
- MemorySegmentTest mem_sgmt_;
- NSEC3Data* nsec3_data_;
- const generic::NSEC3PARAM param_rdata_, param_rdata_nosalt_,
- param_rdata_largesalt_;
- const generic::NSEC3 nsec3_rdata_, nsec3_rdata_nosalt_,
- nsec3_rdata_largesalt_;
- const Name zname_;
- ZoneData* zone_data_;
- const ConstRRsetPtr a_rrset_, aaaa_rrset_, nsec3_rrset_;
- RdataEncoder encoder_;
-};
-
-// Shared by both test cases using NSEC3 and NSEC3PARAM Rdata
-template <typename RdataType>
-void
-checkNSEC3Data(MemorySegmentTest& mem_sgmt, const RdataType& expect_rdata) {
- NSEC3Data* nsec3_data = NSEC3Data::create(mem_sgmt, expect_rdata);
-
- // Internal tree should be created and empty.
- EXPECT_EQ(0, nsec3_data->getNSEC3Tree().getNodeCount());
-
- EXPECT_EQ(expect_rdata.getHashalg(), nsec3_data->hashalg);
- EXPECT_EQ(expect_rdata.getFlags(), nsec3_data->flags);
- EXPECT_EQ(expect_rdata.getIterations(), nsec3_data->iterations);
- EXPECT_EQ(expect_rdata.getSalt().size(), nsec3_data->getSaltLen());
- if (expect_rdata.getSalt().size() > 0) {
- EXPECT_EQ(0, memcmp(&expect_rdata.getSalt()[0],
- nsec3_data->getSaltData(),
- expect_rdata.getSalt().size()));
- }
-
- NSEC3Data::destroy(mem_sgmt, nsec3_data, RRClass::IN());
-}
-
-void
-checkFindRdataSet(const ZoneTree& tree, const Name& name, RRType type,
- const RdataSet* expected_set)
-{
- ZoneNode* node = NULL;
- tree.find(name, &node);
- ASSERT_NE(static_cast<ZoneNode*>(NULL), node);
- EXPECT_EQ(expected_set, RdataSet::find(node->getData(), type));
-}
-
-TEST_F(ZoneDataTest, createNSEC3Data) {
- // Create an NSEC3Data object from various types of RDATA (of NSEC3PARAM
- // and of NSEC3), check if the resulting parameters match.
- checkNSEC3Data(mem_sgmt_, param_rdata_); // one 'usual' form of params
- checkNSEC3Data(mem_sgmt_, param_rdata_nosalt_); // empty salt
- checkNSEC3Data(mem_sgmt_, param_rdata_largesalt_); // max-len salt
-
- // Same concepts of the tests, using NSEC3 RDATA.
- checkNSEC3Data(mem_sgmt_, nsec3_rdata_);
- checkNSEC3Data(mem_sgmt_, nsec3_rdata_nosalt_);
- checkNSEC3Data(mem_sgmt_, nsec3_rdata_largesalt_);
-}
-
-TEST_F(ZoneDataTest, addNSEC3) {
- nsec3_data_ = NSEC3Data::create(mem_sgmt_, param_rdata_);
-
- ZoneNode* node = NULL;
- nsec3_data_->insertName(mem_sgmt_, nsec3_rrset_->getName(), &node);
- ASSERT_NE(static_cast<ZoneNode*>(NULL), node);
- EXPECT_TRUE(node->isEmpty()); // initially it should be empty
-
- RdataSet* rdataset_nsec3 =
- RdataSet::create(mem_sgmt_, encoder_, nsec3_rrset_, ConstRRsetPtr());
- node->setData(rdataset_nsec3);
-
- // Confirm we can find the added ones from the zone data.
- checkFindRdataSet(nsec3_data_->getNSEC3Tree(), nsec3_rrset_->getName(),
- RRType::NSEC3(), rdataset_nsec3);
-
- // TearDown() will confirm there's no leak on destroy
-}
-
-TEST_F(ZoneDataTest, getOriginNode) {
- EXPECT_EQ(LabelSequence(zname_), zone_data_->getOriginNode()->getLabels());
-}
-
-TEST_F(ZoneDataTest, exceptionSafetyOnCreate) {
- // Note: below, we use our knowledge of how memory allocation happens
- // within the NSEC3Data, the zone data and the underlying domain tree
- // implementation. We'll emulate rare situations where allocate() fails
- // with an exception, and confirm it doesn't cause any harsh disruption
- // or leak.
-
- // Creating internal NSEC3 tree will succeed, but allocation of NSEC3Data
- // will fail due to bad_alloc. It shouldn't cause memory leak
- // (that would be caught in TearDown()).
- mem_sgmt_.setThrowCount(2);
- EXPECT_THROW(NSEC3Data::create(mem_sgmt_, param_rdata_), std::bad_alloc);
-
- // allocate() will throw on the insertion of the origin node.
- mem_sgmt_.setThrowCount(2);
- EXPECT_THROW(ZoneData::create(mem_sgmt_, zname_), std::bad_alloc);
-
- // allocate() will throw on creating the zone data.
- mem_sgmt_.setThrowCount(3);
- EXPECT_THROW(ZoneData::create(mem_sgmt_, zname_), std::bad_alloc);
-
- // These incomplete create() attempts shouldn't cause memory leak
- // (that would be caught in TearDown()).
-}
-
-TEST_F(ZoneDataTest, addRdataSets) {
- // Insert a name to the zone, and add a couple the data (RdataSet) objects
- // to the corresponding node.
-
- ZoneNode* node = NULL;
- zone_data_->insertName(mem_sgmt_, a_rrset_->getName(), &node);
- ASSERT_NE(static_cast<ZoneNode*>(NULL), node);
- EXPECT_TRUE(node->isEmpty()); // initially it should be empty
-
- RdataSet* rdataset_a =
- RdataSet::create(mem_sgmt_, encoder_, a_rrset_, ConstRRsetPtr());
- node->setData(rdataset_a);
-
- RdataSet* rdataset_aaaa =
- RdataSet::create(mem_sgmt_, encoder_, aaaa_rrset_, ConstRRsetPtr());
- // make a linked list and replace the list head
- rdataset_aaaa->next = rdataset_a;
- node->setData(rdataset_aaaa);
-
- // Confirm we can find the added ones from the zone data.
- checkFindRdataSet(zone_data_->getZoneTree(), a_rrset_->getName(),
- RRType::A(), rdataset_a);
- checkFindRdataSet(zone_data_->getZoneTree(), a_rrset_->getName(),
- RRType::AAAA(), rdataset_aaaa);
- // There's no NS (or anything other than AAAA or A) RdataSet in the list
- checkFindRdataSet(zone_data_->getZoneTree(), a_rrset_->getName(),
- RRType::NS(), NULL);
-
- // TearDown() will confirm there's no leak on destroy
-}
-
-TEST_F(ZoneDataTest, getSetNSEC3Data) {
- // Initially there's no NSEC3 data
- EXPECT_EQ(static_cast<NSEC3Data*>(NULL), zone_data_->getNSEC3Data());
- // isNSEC3Signed is true iff zone data has non NULL NSEC3 data
- EXPECT_FALSE(zone_data_->isNSEC3Signed());
-
- // Set a new one. The set method should return NULL. The get method
- // should return the new one.
- NSEC3Data* nsec3_data = NSEC3Data::create(mem_sgmt_, param_rdata_);
- NSEC3Data* old_nsec3_data = zone_data_->setNSEC3Data(nsec3_data);
- EXPECT_EQ(static_cast<NSEC3Data*>(NULL), old_nsec3_data);
- EXPECT_EQ(nsec3_data, zone_data_->getNSEC3Data());
- EXPECT_TRUE(zone_data_->isNSEC3Signed());
-
- // Replace an existing one with a yet another one.
- // We're responsible for destroying the old one.
- NSEC3Data* nsec3_data2 = NSEC3Data::create(mem_sgmt_, nsec3_rdata_);
- old_nsec3_data = zone_data_->setNSEC3Data(nsec3_data2);
- EXPECT_EQ(nsec3_data, old_nsec3_data);
- EXPECT_EQ(nsec3_data2, zone_data_->getNSEC3Data());
- EXPECT_TRUE(zone_data_->isNSEC3Signed());
- NSEC3Data::destroy(mem_sgmt_, old_nsec3_data, RRClass::IN());
-
- // Setting NULL clears any existing one.
- old_nsec3_data = zone_data_->setNSEC3Data(NULL);
- EXPECT_EQ(nsec3_data2, old_nsec3_data);
- EXPECT_EQ(static_cast<NSEC3Data*>(NULL), zone_data_->getNSEC3Data());
- EXPECT_FALSE(zone_data_->isNSEC3Signed());
-
- // Then set it again. The zone data should destroy it on its own
- // destruction.
- zone_data_->setNSEC3Data(old_nsec3_data);
-}
-
-TEST_F(ZoneDataTest, isSigned) {
- // By default it's considered unsigned
- EXPECT_FALSE(zone_data_->isSigned());
-
- // declare it's signed, the isSigned() says so too
- zone_data_->setSigned(true);
- EXPECT_TRUE(zone_data_->isSigned());
-
- // change it to unsigned again
- zone_data_->setSigned(false);
- EXPECT_FALSE(zone_data_->isSigned());
-}
-}
diff --git a/src/lib/datasrc/memory/tests/zone_finder_unittest.cc b/src/lib/datasrc/memory/tests/zone_finder_unittest.cc
deleted file mode 100644
index 94ebe5e..0000000
--- a/src/lib/datasrc/memory/tests/zone_finder_unittest.cc
+++ /dev/null
@@ -1,1481 +0,0 @@
-// 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 "memory_segment_test.h"
-
-// NOTE: this faked_nsec3 inclusion (and all related code below)
-// was ported during #2109 for the convenience of implementing #2218
-// In #2218 the NSEC3 test code in this file is expected to be finalized.
-// In #2219 the original is expected to be removed, and this file should
-// probably be moved here (and any leftover code not handled in #2218 should
-// be cleaned up)
-#include "../../tests/faked_nsec3.h"
-
-#include <datasrc/memory/zone_finder.h>
-#include <datasrc/memory/rdata_serialization.h>
-#include <datasrc/data_source.h>
-#include <testutils/dnsmessage_test.h>
-
-#include <boost/foreach.hpp>
-
-#include <gtest/gtest.h>
-
-using namespace std;
-using namespace isc::dns;
-using namespace isc::dns::rdata;
-using namespace isc::datasrc;
-using namespace isc::testutils;
-using boost::shared_ptr;
-using namespace isc::datasrc::test;
-using namespace isc::datasrc::memory::test;
-using namespace isc::datasrc::memory;
-
-namespace {
-// Commonly used result codes (Who should write the prefix all the time)
-using result::SUCCESS;
-using result::EXIST;
-
-// Some faked NSEC3 hash values commonly used in tests and the faked NSEC3Hash
-// object.
-//
-// For apex (example.org)
-const char* const apex_hash = "0P9MHAVEQVM6T7VBL5LOP2U3T2RP3TOM";
-const char* const apex_hash_lower = "0p9mhaveqvm6t7vbl5lop2u3t2rp3tom";
-// For ns1.example.org
-const char* const ns1_hash = "2T7B4G4VSA5SMI47K61MV5BV1A22BOJR";
-// For w.example.org
-const char* const w_hash = "01UDEMVP1J2F7EG6JEBPS17VP3N8I58H";
-// For x.y.w.example.org (lower-cased)
-const char* const xyw_hash = "2vptu5timamqttgl4luu9kg21e0aor3s";
-// For zzz.example.org.
-const char* const zzz_hash = "R53BQ7CC2UVMUBFU5OCMM6PERS9TK9EN";
-
-// A simple faked NSEC3 hash calculator with a dedicated creator for it.
-//
-// This is used in some NSEC3-related tests below.
-// Also see NOTE at inclusion of "../../tests/faked_nsec3.h"
-class TestNSEC3HashCreator : public NSEC3HashCreator {
- class TestNSEC3Hash : public NSEC3Hash {
- private:
- typedef map<Name, string> NSEC3HashMap;
- typedef NSEC3HashMap::value_type NSEC3HashPair;
- NSEC3HashMap map_;
- public:
- TestNSEC3Hash() {
- // Build pre-defined hash
- map_[Name("example.org")] = apex_hash;
- map_[Name("www.example.org")] = "2S9MHAVEQVM6T7VBL5LOP2U3T2RP3TOM";
- map_[Name("xxx.example.org")] = "Q09MHAVEQVM6T7VBL5LOP2U3T2RP3TOM";
- map_[Name("yyy.example.org")] = "0A9MHAVEQVM6T7VBL5LOP2U3T2RP3TOM";
- map_[Name("x.y.w.example.org")] =
- "2VPTU5TIMAMQTTGL4LUU9KG21E0AOR3S";
- map_[Name("y.w.example.org")] = "K8UDEMVP1J2F7EG6JEBPS17VP3N8I58H";
- map_[Name("w.example.org")] = w_hash;
- map_[Name("zzz.example.org")] = zzz_hash;
- map_[Name("smallest.example.org")] =
- "00000000000000000000000000000000";
- map_[Name("largest.example.org")] =
- "UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU";
- }
- virtual string calculate(const Name& name) const {
- const NSEC3HashMap::const_iterator found = map_.find(name);
- if (found != map_.end()) {
- return (found->second);
- }
- isc_throw(isc::Unexpected, "unexpected name for NSEC3 test: "
- << name);
- }
- virtual bool match(const generic::NSEC3PARAM&) const {
- return (true);
- }
- virtual bool match(const generic::NSEC3&) const {
- return (true);
- }
- };
-
-public:
- virtual NSEC3Hash* create(const generic::NSEC3PARAM&) const {
- return (new TestNSEC3Hash);
- }
- virtual NSEC3Hash* create(const generic::NSEC3&) const {
- return (new TestNSEC3Hash);
- }
-};
-
-
-/// \brief expensive rrset converter
-///
-/// converts any specialized rrset (which may not have implemented some
-/// methods for efficiency) into a 'full' RRsetPtr, for easy use in test
-/// checks.
-///
-/// Done very inefficiently through text representation, speed should not
-/// be a concern here.
-ConstRRsetPtr
-convertRRset(ConstRRsetPtr src) {
- return (textToRRset(src->toText()));
-}
-
-/// \brief Test fixture for the InMemoryZoneFinder class
-class InMemoryZoneFinderTest : public ::testing::Test {
- // A straightforward pair of textual RR(set) and a RRsetPtr variable
- // to store the RRset. Used to build test data below.
- struct RRsetData {
- const char* const text; // textual representation of an RRset
- RRsetPtr* rrset;
- };
-protected:
- // The following sub tests are shared by multiple test cases, changing
- // the zone's DNSSEC status (unsigned, NSEC-signed or NSEC3-signed).
- // expected_flags is set to either RESULT_NSEC_SIGNED or
- // RESULT_NSEC3_SIGNED when it's NSEC/NSEC3 signed respectively and
- // find() is expected to set the corresponding flags.
- // find_options should be set to FIND_DNSSEC for NSEC-signed case when
- // NSEC is expected to be returned.
- void findCheck(ZoneFinder::FindResultFlags expected_flags =
- ZoneFinder::RESULT_DEFAULT,
- ZoneFinder::FindOptions find_options =
- ZoneFinder::FIND_DEFAULT);
- void emptyNodeCheck(ZoneFinder::FindResultFlags expected_flags =
- ZoneFinder::RESULT_DEFAULT);
- void wildcardCheck(ZoneFinder::FindResultFlags expected_flags =
- ZoneFinder::RESULT_DEFAULT,
- ZoneFinder::FindOptions find_options =
- ZoneFinder::FIND_DEFAULT);
- void doCancelWildcardCheck(ZoneFinder::FindResultFlags expected_flags =
- ZoneFinder::RESULT_DEFAULT,
- ZoneFinder::FindOptions find_options =
- ZoneFinder::FIND_DEFAULT);
- void anyWildcardCheck(ZoneFinder::FindResultFlags expected_flags =
- ZoneFinder::RESULT_DEFAULT);
- void emptyWildcardCheck(ZoneFinder::FindResultFlags expected_flags =
- ZoneFinder::RESULT_DEFAULT);
- void findNSECENTCheck(const Name& ent_name,
- ConstRRsetPtr expected_nsec,
- ZoneFinder::FindResultFlags expected_flags =
- ZoneFinder::RESULT_DEFAULT);
-
-public:
- InMemoryZoneFinderTest() :
- class_(RRClass::IN()),
- origin_("example.org"),
- zone_data_(ZoneData::create(mem_sgmt_, origin_)),
- zone_finder_(*zone_data_, class_)
- {
- // Build test RRsets. Below, we construct an RRset for
- // each textual RR(s) of zone_data, and assign it to the corresponding
- // rr_xxx.
- // Note that this contains an out-of-zone RR, and due to the
- // validation check of masterLoad() used below, we cannot add SOA.
- const RRsetData zone_data[] = {
- {"example.org. 300 IN NS ns.example.org.", &rr_ns_},
- {"example.org. 300 IN A 192.0.2.1", &rr_a_},
- {"ns.example.org. 300 IN A 192.0.2.2", &rr_ns_a_},
- {"ns.example.org. 300 IN AAAA 2001:db8::2", &rr_ns_aaaa_},
- {"cname.example.org. 300 IN CNAME canonical.example.org",
- &rr_cname_},
- {"cname.example.org. 300 IN A 192.0.2.3", &rr_cname_a_},
- {"dname.example.org. 300 IN DNAME target.example.org.",
- &rr_dname_},
- {"dname.example.org. 300 IN A 192.0.2.39", &rr_dname_a_},
- {"dname.example.org. 300 IN NS ns.dname.example.org.",
- &rr_dname_ns_},
- {"example.org. 300 IN DNAME example.com.", &rr_dname_apex_},
- {"child.example.org. 300 IN NS ns.child.example.org.",
- &rr_child_ns_},
- {"child.example.org. 300 IN DS 12345 5 1 DEADBEEF",
- &rr_child_ds_},
- {"ns.child.example.org. 300 IN A 192.0.2.153",
- &rr_child_glue_},
- {"grand.child.example.org. 300 IN NS ns.grand.child.example.org.",
- &rr_grandchild_ns_},
- {"ns.grand.child.example.org. 300 IN AAAA 2001:db8::253",
- &rr_grandchild_glue_},
- {"dname.child.example.org. 300 IN DNAME example.com.",
- &rr_child_dname_},
- {"example.com. 300 IN A 192.0.2.10", &rr_out_},
- {"*.wild.example.org. 300 IN A 192.0.2.1", &rr_wild_},
- {"*.cnamewild.example.org. 300 IN CNAME canonial.example.org.",
- &rr_cnamewild_},
- {"foo.wild.example.org. 300 IN A 192.0.2.3", &rr_under_wild_},
- {"wild.*.foo.example.org. 300 IN A 192.0.2.1", &rr_emptywild_},
- {"wild.*.foo.*.bar.example.org. 300 IN A 192.0.2.1",
- &rr_nested_emptywild_},
- {"*.nswild.example.org. 300 IN NS nswild.example.", &rr_nswild_},
- {"*.dnamewild.example.org. 300 IN DNAME dnamewild.example.",
- &rr_dnamewild_},
- {"*.child.example.org. 300 IN A 192.0.2.1", &rr_child_wild_},
- {"bar.foo.wild.example.org. 300 IN A 192.0.2.2", &rr_not_wild_},
- {"baz.foo.wild.example.org. 300 IN A 192.0.2.3",
- &rr_not_wild_another_},
- {"0P9MHAVEQVM6T7VBL5LOP2U3T2RP3TOM.example.org. 300 IN "
- "NSEC3 1 1 12 aabbccdd 2T7B4G4VSA5SMI47K61MV5BV1A22BOJR A RRSIG",
- &rr_nsec3_},
- {"example.org. 300 IN NSEC wild.*.foo.example.org. "
- "NS SOA RRSIG NSEC DNSKEY", &rr_nsec_},
- // Together with the apex NSEC, these next NSECs make a complete
- // chain in the case of the zone for the emptyNonterminal tests
- // (We may want to clean up this generator code and/or masterLoad
- // so that we can prepare conflicting datasets better)
- {"wild.*.foo.example.org. 3600 IN NSEC ns.example.org. "
- "A RRSIG NSEC", &rr_ent_nsec2_},
- {"ns.example.org. 3600 IN NSEC foo.wild.example.org. A RRSIG NSEC",
- &rr_ent_nsec3_},
- {"foo.wild.example.org. 3600 IN NSEC example.org. A RRSIG NSEC",
- &rr_ent_nsec4_},
- // And these are NSECs used in different tests
- {"ns.example.org. 300 IN NSEC *.nswild.example.org. A AAAA NSEC",
- &rr_ns_nsec_},
- {"*.wild.example.org. 300 IN NSEC foo.wild.example.org. A NSEC",
- &rr_wild_nsec_},
- {NULL, NULL}
- };
-
- for (unsigned int i = 0; zone_data[i].text != NULL; ++i) {
- *zone_data[i].rrset = textToRRset(zone_data[i].text);
- }
-
- }
-
- ~InMemoryZoneFinderTest() {
- // Make sure we reset the hash creator to the default
- setNSEC3HashCreator(NULL);
- ZoneData::destroy(mem_sgmt_, zone_data_, RRClass::IN());
- }
-
- // NSEC3-specific call for 'loading' data
- // This needs to be updated and checked when implementing #2118
- void addZoneDataNSEC3(const ConstRRsetPtr rrset) {
- assert(rrset->getType() == RRType::NSEC3());
-
- const Rdata* rdata = &rrset->getRdataIterator()->getCurrent();
- const generic::NSEC3* nsec3_rdata =
- dynamic_cast<const generic::NSEC3*>(rdata);
- NSEC3Data* nsec3_data = NSEC3Data::create(mem_sgmt_, *nsec3_rdata);
- // in case we happen to be replacing, destroy old
- NSEC3Data* old_data = zone_data_->setNSEC3Data(nsec3_data);
- if (old_data != NULL) {
- NSEC3Data::destroy(mem_sgmt_, old_data, rrset->getClass());
- }
- zone_data_->setSigned(true);
- }
-
- // simplified version of 'loading' data
- void addZoneData(const ConstRRsetPtr rrset) {
- ZoneNode* node = NULL;
-
- if (rrset->getType() == RRType::NSEC3()) {
- return (addZoneDataNSEC3(rrset));
- } else if (rrset->getType() == RRType::NSEC()) {
- zone_data_->setSigned(true);
- }
-
- zone_data_->insertName(mem_sgmt_, rrset->getName(), &node);
-
- if (rrset->getType() == RRType::NS() &&
- rrset->getName() != zone_data_->getOriginNode()->getName()) {
- node->setFlag(DomainTreeNode<RdataSet>::FLAG_CALLBACK);
- } else if (rrset->getType() == RRType::DNAME()) {
- node->setFlag(DomainTreeNode<RdataSet>::FLAG_CALLBACK);
- }
-
- RdataSet* next_rds = node->getData();
- RdataSet* rdataset =
- RdataSet::create(mem_sgmt_, encoder_, rrset, rrset->getRRsig());
- rdataset->next = next_rds;
- node->setData(rdataset);
-
- // find wildcard nodes in name (go through all of them in case there
- // is a nonterminal one)
- // Note that this method is pretty much equal to the 'real' loader;
- // but less efficient
- Name name(rrset->getName());
- while (name.getLabelCount() > 1) {
- if (name.isWildcard()) {
- ZoneNode* wnode = NULL;
- // add Wild node
- zone_data_->insertName(mem_sgmt_, name.split(1), &wnode);
- wnode->setFlag(ZoneData::WILDCARD_NODE);
- // add wildcard name itself too
- zone_data_->insertName(mem_sgmt_, name, &wnode);
- }
- name = name.split(1);
- }
- }
-
- // Some data to test with
- const RRClass class_;
- const Name origin_;
- // The zone finder to torture by tests
- MemorySegmentTest mem_sgmt_;
- memory::ZoneData* zone_data_;
- memory::InMemoryZoneFinder zone_finder_;
- isc::datasrc::memory::RdataEncoder encoder_;
-
- // Placeholder for storing RRsets to be checked with rrsetsCheck()
- vector<ConstRRsetPtr> actual_rrsets_;
-
- /*
- * Some RRsets to put inside the zone.
- */
- RRsetPtr
- // Out of zone RRset
- rr_out_,
- // NS of example.org
- rr_ns_,
- // A of ns.example.org
- rr_ns_a_,
- // AAAA of ns.example.org
- rr_ns_aaaa_,
- // A of example.org
- rr_a_;
- RRsetPtr rr_cname_; // CNAME in example.org (RDATA will be added)
- RRsetPtr rr_cname_a_; // for mixed CNAME + A case
- RRsetPtr rr_dname_; // DNAME in example.org (RDATA will be added)
- RRsetPtr rr_dname_a_; // for mixed DNAME + A case
- RRsetPtr rr_dname_ns_; // for mixed DNAME + NS case
- RRsetPtr rr_dname_apex_; // for mixed DNAME + NS case in the apex
- RRsetPtr rr_child_ns_; // NS of a child domain (for delegation)
- RRsetPtr rr_child_ds_; // DS of a child domain (for delegation, auth data)
- RRsetPtr rr_child_glue_; // glue RR of the child domain
- RRsetPtr rr_grandchild_ns_; // NS below a zone cut (unusual)
- RRsetPtr rr_grandchild_glue_; // glue RR below a deeper zone cut
- RRsetPtr rr_child_dname_; // A DNAME under NS
- RRsetPtr rr_wild_; // Wildcard record
- RRsetPtr rr_cnamewild_; // CNAME at a wildcard
- RRsetPtr rr_emptywild_;
- RRsetPtr rr_nested_emptywild_;
- RRsetPtr rr_nswild_, rr_dnamewild_;
- RRsetPtr rr_child_wild_;
- RRsetPtr rr_under_wild_;
- RRsetPtr rr_not_wild_;
- RRsetPtr rr_not_wild_another_;
- RRsetPtr rr_nsec3_;
- RRsetPtr rr_nsec_;
- RRsetPtr rr_ent_nsec2_;
- RRsetPtr rr_ent_nsec3_;
- RRsetPtr rr_ent_nsec4_;
- RRsetPtr rr_ns_nsec_;
- RRsetPtr rr_wild_nsec_;
-
- // A faked NSEC3 hash calculator for convenience.
- // Tests that need to use the faked hashed values should call
- // setNSEC3HashCreator() with a pointer to this variable at the beginning
- // of the test (at least before adding any NSEC3/NSEC3PARAM RR).
- TestNSEC3HashCreator nsec3_hash_creator_;
-
- /**
- * \brief Test one find query to the zone finder.
- *
- * Asks a query to the zone finder and checks it does not throw and returns
- * expected results. It returns nothing, it just signals failures
- * to GTEST.
- *
- * \param name The name to ask for.
- * \param rrtype The RRType to ask of.
- * \param result The expected code of the result.
- * \param check_answer Should a check against equality of the answer be
- * done?
- * \param answer The expected rrset, if any should be returned.
- * \param expected_flags The expected result flags returned via find().
- * These can be tested using isWildcard() etc.
- * \param zone_finder Check different InMemoryZoneFinder object than
- * zone_finder_ (if NULL, uses zone_finder_)
- * \param check_wild_answer Checks that the answer has the same RRs, type
- * class and TTL as the eqxpected answer and that the name corresponds
- * to the one searched. It is meant for checking answers for wildcard
- * queries.
- */
- void findTest(const Name& name, const RRType& rrtype,
- ZoneFinder::Result result,
- bool check_answer = true,
- const ConstRRsetPtr& answer = ConstRRsetPtr(),
- ZoneFinder::FindResultFlags expected_flags =
- ZoneFinder::RESULT_DEFAULT,
- memory::InMemoryZoneFinder* zone_finder = NULL,
- ZoneFinder::FindOptions options = ZoneFinder::FIND_DEFAULT,
- bool check_wild_answer = false)
- {
- SCOPED_TRACE("findTest for " + name.toText() + "/" + rrtype.toText());
-
- if (zone_finder == NULL) {
- zone_finder = &zone_finder_;
- }
- const ConstRRsetPtr answer_sig = answer ? answer->getRRsig() :
- RRsetPtr(); // note we use the same type as of retval of getRRsig()
- // The whole block is inside, because we need to check the result and
- // we can't assign to FindResult
- EXPECT_NO_THROW({
- ZoneFinderContextPtr find_result(zone_finder->find(
- name, rrtype, options));
- // Check it returns correct answers
- EXPECT_EQ(result, find_result->code);
- EXPECT_EQ((expected_flags & ZoneFinder::RESULT_WILDCARD) != 0,
- find_result->isWildcard());
- EXPECT_EQ((expected_flags & ZoneFinder::RESULT_NSEC_SIGNED)
- != 0, find_result->isNSECSigned());
- EXPECT_EQ((expected_flags & ZoneFinder::RESULT_NSEC3_SIGNED)
- != 0, find_result->isNSEC3Signed());
- if (check_answer) {
- if (!answer) {
- ASSERT_FALSE(find_result->rrset);
- } else {
- ASSERT_TRUE(find_result->rrset);
- ConstRRsetPtr result_rrset(
- convertRRset(find_result->rrset));
- rrsetCheck(answer, result_rrset);
- if (answer_sig) {
- ASSERT_TRUE(result_rrset->getRRsig());
- rrsetCheck(answer_sig, 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) <<
- "No answer found";
- // Build the expected answer using the given name and
- // other parameter of the base wildcard RRset.
- RRsetPtr wildanswer(new RRset(name, answer->getClass(),
- answer->getType(),
- answer->getTTL()));
- RdataIteratorPtr expectedIt(answer->getRdataIterator());
- for (; !expectedIt->isLast(); expectedIt->next()) {
- wildanswer->addRdata(expectedIt->getCurrent());
- }
-
- ConstRRsetPtr result_rrset(
- convertRRset(find_result->rrset));
- rrsetCheck(wildanswer, result_rrset);
-
- // Same for the RRSIG, if any.
- if (answer_sig) {
- ASSERT_TRUE(result_rrset->getRRsig());
-
- RRsetPtr wildsig(new RRset(name,
- answer_sig->getClass(),
- RRType::RRSIG(),
- answer_sig->getTTL()));
- RdataIteratorPtr expectedIt(
- answer_sig->getRdataIterator());
- for (; !expectedIt->isLast(); expectedIt->next()) {
- wildsig->addRdata(expectedIt->getCurrent());
- }
- rrsetCheck(wildsig, result_rrset->getRRsig());
- }
- }
- });
- }
- /**
- * \brief Calls the findAll on the finder and checks the result.
- */
- void findAllTest(const Name& name, ZoneFinder::Result result,
- const vector<ConstRRsetPtr>& expected_rrsets,
- ZoneFinder::FindResultFlags expected_flags =
- ZoneFinder::RESULT_DEFAULT,
- memory::InMemoryZoneFinder* finder = NULL,
- const ConstRRsetPtr &rrset_result = ConstRRsetPtr(),
- ZoneFinder::FindOptions options =
- ZoneFinder::FIND_DEFAULT)
- {
- if (finder == NULL) {
- finder = &zone_finder_;
- }
- std::vector<ConstRRsetPtr> target;
- ZoneFinderContextPtr find_result(finder->findAll(name, target,
- options));
- EXPECT_EQ(result, find_result->code);
- if (!rrset_result) {
- EXPECT_FALSE(find_result->rrset);
- } else {
- ASSERT_TRUE(find_result->rrset);
- rrsetCheck(rrset_result, convertRRset(find_result->rrset));
- }
- EXPECT_EQ((expected_flags & ZoneFinder::RESULT_WILDCARD) != 0,
- find_result->isWildcard());
- EXPECT_EQ((expected_flags & ZoneFinder::RESULT_NSEC_SIGNED)
- != 0, find_result->isNSECSigned());
- EXPECT_EQ((expected_flags & ZoneFinder::RESULT_NSEC3_SIGNED)
- != 0, find_result->isNSEC3Signed());
- // Convert all rrsets to 'full' ones before checking
- std::vector<ConstRRsetPtr> converted_rrsets;
- BOOST_FOREACH(ConstRRsetPtr cur_rrset, target) {
- converted_rrsets.push_back(convertRRset(cur_rrset));
- }
- rrsetsCheck(expected_rrsets.begin(), expected_rrsets.end(),
- converted_rrsets.begin(), converted_rrsets.end());
- }
-};
-
-/**
- * \brief Test InMemoryZoneFinder::InMemoryZoneFinder constructor.
- *
- * Takes the created zone finder and checks its properties they are the same
- * as passed parameters.
- */
-TEST_F(InMemoryZoneFinderTest, constructor) {
- ASSERT_EQ(origin_, zone_finder_.getOrigin());
-}
-
-TEST_F(InMemoryZoneFinderTest, findCNAME) {
- // install CNAME RR
- addZoneData(rr_cname_);
-
- // Find A RR of the same. Should match the CNAME
- findTest(rr_cname_->getName(), RRType::NS(), ZoneFinder::CNAME, true,
- rr_cname_);
-
- // Find the CNAME itself. Should result in normal SUCCESS
- findTest(rr_cname_->getName(), RRType::CNAME(), ZoneFinder::SUCCESS, true,
- rr_cname_);
-}
-
-TEST_F(InMemoryZoneFinderTest, findCNAMEUnderZoneCut) {
- // There's nothing special when we find a CNAME under a zone cut
- // (with FIND_GLUE_OK). The behavior is different from BIND 9,
- // so we test this case explicitly.
- addZoneData(rr_child_ns_);
- ConstRRsetPtr rr_cname_under_cut_ = textToRRset(
- "cname.child.example.org. 300 IN CNAME target.child.example.org.");
- addZoneData(rr_cname_under_cut_);
- findTest(Name("cname.child.example.org"), RRType::AAAA(),
- ZoneFinder::CNAME, true, rr_cname_under_cut_,
- ZoneFinder::RESULT_DEFAULT, NULL, ZoneFinder::FIND_GLUE_OK);
-}
-
-// Search under a DNAME record. It should return the DNAME
-TEST_F(InMemoryZoneFinderTest, findBelowDNAME) {
- EXPECT_NO_THROW(addZoneData(rr_dname_));
- findTest(Name("below.dname.example.org"), RRType::A(), ZoneFinder::DNAME,
- true, rr_dname_);
-}
-
-// Search at the domain with DNAME. It should act as DNAME isn't there, DNAME
-// influences only the data below (see RFC 2672, section 3)
-TEST_F(InMemoryZoneFinderTest, findAtDNAME) {
- EXPECT_NO_THROW(addZoneData(rr_dname_));
- EXPECT_NO_THROW(addZoneData(rr_dname_a_));
-
- const Name dname_name(rr_dname_->getName());
- findTest(dname_name, RRType::A(), ZoneFinder::SUCCESS, true, rr_dname_a_);
- findTest(dname_name, RRType::DNAME(), ZoneFinder::SUCCESS, true,
- rr_dname_);
- findTest(dname_name, RRType::TXT(), ZoneFinder::NXRRSET, true);
-}
-
-// Try searching something that is both under NS and DNAME, without and with
-// GLUE_OK mode (it should stop at the NS and DNAME respectively).
-TEST_F(InMemoryZoneFinderTest, DNAMEUnderNS) {
- addZoneData(rr_child_ns_);
- addZoneData(rr_child_dname_);
-
- Name lowName("below.dname.child.example.org.");
-
- findTest(lowName, RRType::A(), ZoneFinder::DELEGATION, true, rr_child_ns_);
- findTest(lowName, RRType::A(), ZoneFinder::DNAME, true, rr_child_dname_,
- ZoneFinder::RESULT_DEFAULT, NULL, ZoneFinder::FIND_GLUE_OK);
-}
-
-// Test adding child zones and zone cut handling
-TEST_F(InMemoryZoneFinderTest, delegationNS) {
- // add in-zone data
- EXPECT_NO_THROW(addZoneData(rr_ns_));
-
- // install a zone cut
- EXPECT_NO_THROW(addZoneData(rr_child_ns_));
-
- // below the zone cut
- findTest(Name("www.child.example.org"), RRType::A(),
- ZoneFinder::DELEGATION, true, rr_child_ns_);
-
- // at the zone cut
- findTest(Name("child.example.org"), RRType::A(), ZoneFinder::DELEGATION,
- true, rr_child_ns_);
- findTest(Name("child.example.org"), RRType::NS(), ZoneFinder::DELEGATION,
- true, rr_child_ns_);
-
- // finding NS for the apex (origin) node. This must not be confused
- // with delegation due to the existence of an NS RR.
- findTest(origin_, RRType::NS(), ZoneFinder::SUCCESS, true, rr_ns_);
-
- // unusual case of "nested delegation": the highest cut should be used.
- EXPECT_NO_THROW(addZoneData(rr_grandchild_ns_));
- findTest(Name("www.grand.child.example.org"), RRType::A(),
- // note: !rr_grandchild_ns_
- ZoneFinder::DELEGATION, true, rr_child_ns_);
-}
-
-TEST_F(InMemoryZoneFinderTest, delegationWithDS) {
- // Similar setup to the previous one, but with DS RR at the delegation
- // point.
- addZoneData(rr_ns_);
- addZoneData(rr_child_ns_);
- addZoneData(rr_child_ds_);
-
- // Normal types of query should result in delegation, but DS query
- // should be considered in-zone (but only exactly at the delegation point).
- findTest(Name("child.example.org"), RRType::A(), ZoneFinder::DELEGATION,
- true, rr_child_ns_);
- findTest(Name("child.example.org"), RRType::DS(), ZoneFinder::SUCCESS,
- true, rr_child_ds_);
- findTest(Name("grand.child.example.org"), RRType::DS(),
- ZoneFinder::DELEGATION, true, rr_child_ns_);
-
- // There's nothing special for DS query at the zone apex. It would
- // normally result in NXRRSET.
- findTest(Name("example.org"), RRType::DS(), ZoneFinder::NXRRSET,
- true, ConstRRsetPtr());
-}
-
-TEST_F(InMemoryZoneFinderTest, findAny) {
- EXPECT_NO_THROW(addZoneData(rr_a_));
- EXPECT_NO_THROW(addZoneData(rr_ns_));
- EXPECT_NO_THROW(addZoneData(rr_child_glue_));
-
- vector<ConstRRsetPtr> expected_sets;
-
- // origin
- expected_sets.push_back(rr_a_);
- expected_sets.push_back(rr_ns_);
- findAllTest(origin_, ZoneFinder::SUCCESS, expected_sets);
-
- // out zone name
- EXPECT_THROW(findAllTest(Name("example.com"), ZoneFinder::NXDOMAIN,
- vector<ConstRRsetPtr>()),
- OutOfZone);
-
- expected_sets.clear();
- expected_sets.push_back(rr_child_glue_);
- findAllTest(rr_child_glue_->getName(), ZoneFinder::SUCCESS, expected_sets);
-
- // add zone cut
- EXPECT_NO_THROW(addZoneData(rr_child_ns_));
-
- // zone cut
- findAllTest(rr_child_ns_->getName(), ZoneFinder::DELEGATION,
- vector<ConstRRsetPtr>(), ZoneFinder::RESULT_DEFAULT,
- NULL, rr_child_ns_);
-
- // glue for this zone cut
- findAllTest(rr_child_glue_->getName(),ZoneFinder::DELEGATION,
- vector<ConstRRsetPtr>(), ZoneFinder::RESULT_DEFAULT,
- NULL, rr_child_ns_);
-}
-
-TEST_F(InMemoryZoneFinderTest, glue) {
- // install zone data:
- // a zone cut
- EXPECT_NO_THROW(addZoneData(rr_child_ns_));
- // glue for this cut
- EXPECT_NO_THROW(addZoneData(rr_child_glue_));
- // a nested zone cut (unusual)
- EXPECT_NO_THROW(addZoneData(rr_grandchild_ns_));
- // glue under the deeper zone cut
- EXPECT_NO_THROW(addZoneData(rr_grandchild_glue_));
-
- // by default glue is hidden due to the zone cut
- findTest(rr_child_glue_->getName(), RRType::A(), ZoneFinder::DELEGATION,
- true, rr_child_ns_);
-
-
- // If we do it in the "glue OK" mode, we should find the exact match.
- findTest(rr_child_glue_->getName(), RRType::A(), ZoneFinder::SUCCESS, true,
- rr_child_glue_, ZoneFinder::RESULT_DEFAULT, NULL,
- ZoneFinder::FIND_GLUE_OK);
-
- // glue OK + NXRRSET case
- findTest(rr_child_glue_->getName(), RRType::AAAA(), ZoneFinder::NXRRSET,
- true, ConstRRsetPtr(), ZoneFinder::RESULT_DEFAULT, NULL,
- ZoneFinder::FIND_GLUE_OK);
-
- // glue OK + NXDOMAIN case
- findTest(Name("www.child.example.org"), RRType::A(),
- ZoneFinder::DELEGATION, true, rr_child_ns_,
- ZoneFinder::RESULT_DEFAULT, NULL, ZoneFinder::FIND_GLUE_OK);
-
- // nested cut case. The glue should be found.
- findTest(rr_grandchild_glue_->getName(), RRType::AAAA(),
- ZoneFinder::SUCCESS,
- true, rr_grandchild_glue_, ZoneFinder::RESULT_DEFAULT, NULL,
- ZoneFinder::FIND_GLUE_OK);
-
- // A non-existent name in nested cut. This should result in delegation
- // at the highest zone cut.
- findTest(Name("www.grand.child.example.org"), RRType::TXT(),
- ZoneFinder::DELEGATION, true, rr_child_ns_,
- ZoneFinder::RESULT_DEFAULT, NULL, ZoneFinder::FIND_GLUE_OK);
-}
-
-/**
- * \brief Test searching.
- *
- * Check it finds or does not find correctly and does not throw exceptions.
- */
-void
-InMemoryZoneFinderTest::findCheck(ZoneFinder::FindResultFlags expected_flags,
- ZoneFinder::FindOptions find_options)
-{
- // Fill some data inside
- // Now put all the data we have there. It should throw nothing
- EXPECT_NO_THROW(addZoneData(rr_ns_));
- EXPECT_NO_THROW(addZoneData(rr_ns_a_));
- EXPECT_NO_THROW(addZoneData(rr_ns_aaaa_));
- EXPECT_NO_THROW(addZoneData(rr_a_));
- if ((expected_flags & ZoneFinder::RESULT_NSEC3_SIGNED) != 0) {
- addZoneData(rr_nsec3_);
- zone_data_->setSigned(true);
- }
- if ((expected_flags & ZoneFinder::RESULT_NSEC_SIGNED) != 0) {
- addZoneData(rr_nsec_);
- zone_data_->setSigned(true);
- }
-
- // These two should be successful
- findTest(origin_, RRType::NS(), ZoneFinder::SUCCESS, true, rr_ns_);
- findTest(rr_ns_a_->getName(), RRType::A(), ZoneFinder::SUCCESS, true,
- rr_ns_a_);
-
- // These domains don't exist. (and one is out of the zone). In an
- // NSEC-signed zone with DNSSEC records requested, it should return the
- // covering NSEC for the query name (the actual NSEC in the test data may
- // not really "cover" it, but for the purpose of this test it's okay).
- ConstRRsetPtr expected_nsec; // by default it's NULL
- if ((expected_flags & ZoneFinder::RESULT_NSEC_SIGNED) != 0 &&
- (find_options & ZoneFinder::FIND_DNSSEC) != 0) {
- expected_nsec = rr_nsec_;
- }
-
- // There's no other name between this one and the origin, so when NSEC
- // is to be returned it should be the origin NSEC.
- findTest(Name("nothere.example.org"), RRType::A(),
- ZoneFinder::NXDOMAIN, true, expected_nsec, expected_flags,
- NULL, find_options);
-
- // The previous name in the zone is "ns.example.org", but it doesn't
- // have an NSEC. It should be skipped and the origin NSEC will be
- // returned as the "closest NSEC".
- findTest(Name("nxdomain.example.org"), RRType::A(),
- ZoneFinder::NXDOMAIN, true, expected_nsec, expected_flags,
- NULL, find_options);
- EXPECT_THROW(zone_finder_.find(Name("example.net"), RRType::A()),
- OutOfZone);
-
- // These domain exist but don't have the provided RRType. For the latter
- // one we now add its NSEC (which was delayed so that it wouldn't break
- // other cases above).
- findTest(origin_, RRType::AAAA(), ZoneFinder::NXRRSET, true,
- expected_nsec, expected_flags, NULL, find_options);
-
- if ((expected_flags & ZoneFinder::RESULT_NSEC_SIGNED) != 0) {
- addZoneData(rr_ns_nsec_);
- zone_data_->setSigned(true);
- if ((find_options & ZoneFinder::FIND_DNSSEC) != 0) {
- expected_nsec = rr_ns_nsec_;
- }
- }
- findTest(rr_ns_a_->getName(), RRType::NS(), ZoneFinder::NXRRSET, true,
- expected_nsec, expected_flags, NULL, find_options);
-}
-
-TEST_F(InMemoryZoneFinderTest, find) {
- findCheck();
-}
-
-TEST_F(InMemoryZoneFinderTest, findNSEC3Signed) {
- findCheck(ZoneFinder::RESULT_NSEC3_SIGNED);
-}
-
-TEST_F(InMemoryZoneFinderTest, findNSEC3SignedWithDNSSEC) {
- // For NSEC3-signed zones, specifying the DNSSEC option shouldn't change
- // anything (the NSEC3_SIGNED flag is always set, and no records are
- // returned for negative cases regardless).
- findCheck(ZoneFinder::RESULT_NSEC3_SIGNED, ZoneFinder::FIND_DNSSEC);
-}
-
-TEST_F(InMemoryZoneFinderTest, findNSECSigned) {
- // NSEC-signed zone, without requesting DNSSEC (no NSEC should be provided)
- findCheck(ZoneFinder::RESULT_NSEC_SIGNED);
-}
-
-// Generalized test for Empty Nonterminal (ENT) cases with NSEC
-void
-InMemoryZoneFinderTest::findNSECENTCheck(const Name& ent_name,
- ConstRRsetPtr expected_nsec,
- ZoneFinder::FindResultFlags expected_flags)
-{
- addZoneData(rr_emptywild_);
- addZoneData(rr_under_wild_);
-
- // Sanity check: Should result in NXRRSET
- findTest(ent_name, RRType::A(), ZoneFinder::NXRRSET, true,
- ConstRRsetPtr(), expected_flags);
- // Sanity check: No NSEC added yet
- findTest(ent_name, RRType::A(), ZoneFinder::NXRRSET, true,
- ConstRRsetPtr(), expected_flags,
- NULL, ZoneFinder::FIND_DNSSEC);
-
- // Now add the NSEC rrs making it a 'complete' zone (in terms of NSEC,
- // there are no sigs)
- addZoneData(rr_nsec_);
- addZoneData(rr_ent_nsec2_);
- addZoneData(rr_ent_nsec3_);
- addZoneData(rr_ent_nsec4_);
- zone_data_->setSigned(true);
-
- // Should result in NXRRSET, and RESULT_NSEC_SIGNED
- findTest(ent_name, RRType::A(), ZoneFinder::NXRRSET, true,
- ConstRRsetPtr(),
- expected_flags | ZoneFinder::RESULT_NSEC_SIGNED);
-
- // And check for the NSEC if DNSSEC_OK
- findTest(ent_name, RRType::A(), ZoneFinder::NXRRSET, true,
- expected_nsec, expected_flags | ZoneFinder::RESULT_NSEC_SIGNED,
- NULL, ZoneFinder::FIND_DNSSEC);
-}
-
-TEST_F(InMemoryZoneFinderTest,findNSECEmptyNonterminal) {
- // Non-wildcard case
- findNSECENTCheck(Name("wild.example.org"), rr_ent_nsec3_);
-}
-
-TEST_F(InMemoryZoneFinderTest, findNSECEmptyNonterminalWildcard) {
- // Wildcard case, above actual wildcard
- findNSECENTCheck(Name("foo.example.org"), rr_nsec_);
-}
-
-TEST_F(InMemoryZoneFinderTest, findNSECEmptyNonterminalAtWildcard) {
- // Wildcard case, at actual wildcard
- findNSECENTCheck(Name("bar.foo.example.org"), rr_nsec_,
- ZoneFinder::RESULT_WILDCARD);
-}
-
-TEST_F(InMemoryZoneFinderTest, findNSECSignedWithDNSSEC) {
- // NSEC-signed zone, requesting DNSSEC (NSEC should be provided)
- findCheck(ZoneFinder::RESULT_NSEC_SIGNED, ZoneFinder::FIND_DNSSEC);
-}
-
-void
-InMemoryZoneFinderTest::emptyNodeCheck(
- ZoneFinder::FindResultFlags expected_flags)
-{
- /*
- * The backend RBTree for this test should look like as follows:
- * example.org
- * |
- * baz (empty; easy case)
- * / | \
- * bar | x.foo ('foo' part is empty; a bit trickier)
- * bbb
- * /
- * aaa
- */
-
- // Construct the test zone
- const char* const names[] = {
- "bar.example.org.", "x.foo.example.org.", "aaa.baz.example.org.",
- "bbb.baz.example.org.", NULL};
- for (int i = 0; names[i] != NULL; ++i) {
- ConstRRsetPtr rrset = textToRRset(string(names[i]) +
- " 300 IN A 192.0.2.1");
- addZoneData(rrset);
- }
- if ((expected_flags & ZoneFinder::RESULT_NSEC3_SIGNED) != 0) {
- addZoneData(rr_nsec3_);
- zone_data_->setSigned(true);
- }
- if ((expected_flags & ZoneFinder::RESULT_NSEC_SIGNED) != 0) {
- addZoneData(rr_nsec_);
- zone_data_->setSigned(true);
- }
-
- // empty node matching, easy case: the node for 'baz' exists with
- // no data.
- findTest(Name("baz.example.org"), RRType::A(), ZoneFinder::NXRRSET, true,
- ConstRRsetPtr(), expected_flags);
-
- // empty node matching, a trickier case: the node for 'foo' is part of
- // "x.foo", which should be considered an empty node.
- findTest(Name("foo.example.org"), RRType::A(), ZoneFinder::NXRRSET, true,
- ConstRRsetPtr(), expected_flags);
-
- // "org" is contained in "example.org", but it shouldn't be treated as
- // NXRRSET because it's out of zone.
- // Note: basically we don't expect such a query to be performed (the common
- // operation is to identify the best matching zone first then perform
- // search it), but we shouldn't be confused even in the unexpected case.
- EXPECT_THROW(zone_finder_.find(Name("org"), RRType::A()), OutOfZone);
-}
-
-TEST_F(InMemoryZoneFinderTest, emptyNode) {
- emptyNodeCheck();
-}
-
-TEST_F(InMemoryZoneFinderTest, emptyNodeNSEC3) {
- emptyNodeCheck(ZoneFinder::RESULT_NSEC3_SIGNED);
-}
-
-TEST_F(InMemoryZoneFinderTest, emptyNodeNSEC) {
- emptyNodeCheck(ZoneFinder::RESULT_NSEC_SIGNED);
-}
-
-/*
- * Test that puts a (simple) wildcard into the zone and checks we can
- * correctly find the data.
- */
-void
-InMemoryZoneFinderTest::wildcardCheck(
- ZoneFinder::FindResultFlags expected_flags,
- ZoneFinder::FindOptions find_options)
-{
- /*
- * example.org.
- * |
- * [cname]wild (not *.wild, should have wild mark)
- * |
- * *
- */
-
- // If the zone is "signed" (detecting it by the NSEC/NSEC3 signed flags),
- // add RRSIGs to the records.
- if ((expected_flags & ZoneFinder::RESULT_NSEC_SIGNED) != 0 ||
- (expected_flags & ZoneFinder::RESULT_NSEC3_SIGNED) != 0) {
- // Convenience shortcut. The RDATA is not really validatable, but
- // it doesn't matter for our tests.
- const char* const rrsig_common = "5 3 3600 "
- "20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE";
-
- find_options = find_options | ZoneFinder::FIND_DNSSEC;
- rr_wild_->addRRsig(textToRRset("*.wild.example.org. 300 IN RRSIG A " +
- string(rrsig_common)));
- rr_cnamewild_->addRRsig(textToRRset("*.cnamewild.example.org. 300 IN "
- "RRSIG CNAME " +
- string(rrsig_common)));
- }
- addZoneData(rr_wild_);
- addZoneData(rr_cnamewild_);
- // If the zone is expected to be "signed" with NSEC3, add an NSEC3.
- // (the content of the NSEC3 shouldn't matter)
- if ((expected_flags & ZoneFinder::RESULT_NSEC3_SIGNED) != 0) {
- addZoneData(rr_nsec3_);
- }
- if ((expected_flags & ZoneFinder::RESULT_NSEC_SIGNED) != 0) {
- addZoneData(rr_nsec_);
- }
-
- // Search at the parent. The parent will not have the A, but it will
- // be in the wildcard (so check the wildcard isn't matched at the parent)
- {
- SCOPED_TRACE("Search at parent");
- if ((expected_flags & ZoneFinder::RESULT_NSEC_SIGNED) != 0) {
- findTest(Name("wild.example.org"), RRType::A(),
- ZoneFinder::NXRRSET, true, rr_nsec_, expected_flags,
- NULL, find_options);
- } else {
- findTest(Name("wild.example.org"), RRType::A(),
- ZoneFinder::NXRRSET, true, ConstRRsetPtr(),
- expected_flags, NULL, find_options);
- }
- }
-
- // For the test setup of "NSEC-signed" zone, we might expect it will
- // be returned with a negative result, either because wildcard match is
- // disabled by the search option or because wildcard match is canceled
- // per protocol.
- ConstRRsetPtr expected_nsec; // by default it's NULL
- if ((expected_flags & ZoneFinder::RESULT_NSEC_SIGNED) != 0 &&
- (find_options & ZoneFinder::FIND_DNSSEC) != 0) {
- expected_nsec = rr_nsec_;
- }
- // Explicitly converting the following to const pointers; some compilers
- // would complain about mixed use of const and non const in ?: below.
- const ConstRRsetPtr rr_wild = rr_wild_;
- const ConstRRsetPtr rr_cnamewild = rr_cnamewild_;
-
- // Search the original name of wildcard
- {
- SCOPED_TRACE("Search directly at *");
- findTest(Name("*.wild.example.org"), RRType::A(), ZoneFinder::SUCCESS,
- true, rr_wild_, ZoneFinder::RESULT_DEFAULT, NULL,
- find_options);
- }
-
- // Below some of the test cases will normally result in a wildcard match;
- // if NO_WILDCARD is specified, it should result in NXDOMAIN instead,
- // and, when available and requested, the covering NSEC will be returned.
- // The following are shortcut parameters to unify these cases.
- const bool wild_ok = ((find_options & ZoneFinder::NO_WILDCARD) == 0);
- const ZoneFinder::FindResultFlags wild_expected_flags =
- wild_ok ? (ZoneFinder::RESULT_WILDCARD | expected_flags) :
- expected_flags;
-
- // Search "created" name.
- {
- SCOPED_TRACE("Search at created child");
- findTest(Name("a.wild.example.org"), RRType::A(),
- wild_ok ? ZoneFinder::SUCCESS : ZoneFinder::NXDOMAIN, false,
- wild_ok ? rr_wild : expected_nsec,
- wild_expected_flags, NULL, find_options, wild_ok);
- }
-
- // Search name that has CNAME.
- {
- SCOPED_TRACE("Matching CNAME");
- findTest(Name("a.cnamewild.example.org"), RRType::A(),
- wild_ok ? ZoneFinder::CNAME : ZoneFinder::NXDOMAIN, false,
- wild_ok ? rr_cnamewild : expected_nsec,
- wild_expected_flags, NULL, find_options, wild_ok);
- }
-
- // Search another created name, this time little bit lower
- {
- SCOPED_TRACE("Search at created grand-child");
- findTest(Name("a.b.wild.example.org"), RRType::A(),
- wild_ok ? ZoneFinder::SUCCESS : ZoneFinder::NXDOMAIN, false,
- wild_ok ? rr_wild : expected_nsec,
- wild_expected_flags, NULL, find_options, wild_ok);
- }
-
- addZoneData(rr_under_wild_);
- {
- SCOPED_TRACE("Search under non-wildcard");
- findTest(Name("bar.foo.wild.example.org"), RRType::A(),
- ZoneFinder::NXDOMAIN, true, expected_nsec, expected_flags,
- NULL, find_options);
- }
-
- // Wildcard match, but no data. We add the additional NSEC at the wildcard
- // at this point so that it wouldn't break other tests above. Note also
- // that in the NO_WILDCARD case the resulting NSEC is the same. Ideally
- // we could use a more tricky setup so we can distinguish these cases,
- // but for this purpose it's not bad; what we'd like to test here is that
- // wildcard substitution doesn't happen for either case, and the
- // NO_WILDCARD effect itself can be checked by the result code (NXDOMAIN).
- ConstRRsetPtr expected_wild_nsec; // by default it's NULL
- if ((expected_flags & ZoneFinder::RESULT_NSEC_SIGNED) != 0) {
- addZoneData(rr_wild_nsec_);
- expected_wild_nsec = rr_wild_nsec_;
- }
- {
- SCOPED_TRACE("Search at wildcard, no data");
- findTest(Name("a.wild.example.org"), RRType::AAAA(),
- wild_ok ? ZoneFinder::NXRRSET : ZoneFinder::NXDOMAIN, true,
- wild_ok ? expected_wild_nsec : expected_wild_nsec,
- wild_expected_flags, NULL, find_options);
- }
-}
-
-TEST_F(InMemoryZoneFinderTest, wildcard) {
- // Normal case
- wildcardCheck();
-}
-
-TEST_F(InMemoryZoneFinderTest, wildcardDisabledWithNSEC) {
- // Wildcard is disabled. In practice, this is used as part of query
- // processing for an NSEC-signed zone, so we test that case specifically.
- wildcardCheck(ZoneFinder::RESULT_NSEC_SIGNED, ZoneFinder::NO_WILDCARD);
-}
-
-TEST_F(InMemoryZoneFinderTest, wildcardDisabledWithoutNSEC) {
- // Similar to the previous once, but check the behavior for a non signed
- // zone just in case.
- wildcardCheck(ZoneFinder::RESULT_DEFAULT, ZoneFinder::NO_WILDCARD);
-}
-
-/*
- * Test that we don't match a wildcard if we get under delegation.
- * By 4.3.3 of RFC1034:
- * "Wildcard RRs do not apply:
- * - When the query is in another zone. That is, delegation cancels
- * the wildcard defaults."
- */
-TEST_F(InMemoryZoneFinderTest, delegatedWildcard) {
- addZoneData(rr_child_wild_);
- addZoneData(rr_child_ns_);
-
- {
- SCOPED_TRACE("Looking under delegation point");
- findTest(Name("a.child.example.org"), RRType::A(),
- ZoneFinder::DELEGATION, true, rr_child_ns_);
- }
-
- {
- SCOPED_TRACE("Looking under delegation point in GLUE_OK mode");
- findTest(Name("a.child.example.org"), RRType::A(),
- ZoneFinder::DELEGATION, true, rr_child_ns_,
- ZoneFinder::RESULT_DEFAULT, NULL, ZoneFinder::FIND_GLUE_OK);
- }
-}
-
-// Tests combination of wildcard and ANY.
-void
-InMemoryZoneFinderTest::anyWildcardCheck(
- ZoneFinder::FindResultFlags expected_flags)
-{
- addZoneData(rr_wild_);
- if ((expected_flags & ZoneFinder::RESULT_NSEC3_SIGNED) != 0) {
- addZoneData(rr_nsec3_);
- }
- if ((expected_flags & ZoneFinder::RESULT_NSEC_SIGNED) != 0) {
- addZoneData(rr_nsec_);
- }
-
- vector<ConstRRsetPtr> expected_sets;
-
- // First try directly the name (normal match)
- {
- SCOPED_TRACE("Asking directly for *");
- expected_sets.push_back(rr_wild_);
- findAllTest(Name("*.wild.example.org"), ZoneFinder::SUCCESS,
- expected_sets);
- }
-
- // Then a wildcard match
- {
- SCOPED_TRACE("Asking in the wild way");
- expected_sets.clear();
- RRsetPtr expected(new RRset(Name("a.wild.example.org"),
- rr_wild_->getClass(), rr_wild_->getType(),
- rr_wild_->getTTL()));
- expected->addRdata(rr_wild_->getRdataIterator()->getCurrent());
- expected_sets.push_back(expected);
- findAllTest(Name("a.wild.example.org"), ZoneFinder::SUCCESS,
- expected_sets,
- ZoneFinder::RESULT_WILDCARD | expected_flags);
- }
-}
-
-TEST_F(InMemoryZoneFinderTest, anyWildcard) {
- anyWildcardCheck();
-}
-
-TEST_F(InMemoryZoneFinderTest, anyWildcardNSEC3) {
- anyWildcardCheck(ZoneFinder::RESULT_NSEC3_SIGNED);
-}
-
-TEST_F(InMemoryZoneFinderTest, anyWildcardNSEC) {
- anyWildcardCheck(ZoneFinder::RESULT_NSEC_SIGNED);
-}
-
-// Test there's nothing in the wildcard in the middle if we load
-// wild.*.foo.example.org.
-void
-InMemoryZoneFinderTest::emptyWildcardCheck(
- ZoneFinder::FindResultFlags expected_flags)
-{
- /*
- * example.org.
- * foo
- * *
- * wild
- */
- addZoneData(rr_emptywild_);
- if ((expected_flags & ZoneFinder::RESULT_NSEC3_SIGNED) != 0) {
- addZoneData(rr_nsec3_);
- }
- if ((expected_flags & ZoneFinder::RESULT_NSEC_SIGNED) != 0) {
- addZoneData(rr_nsec_);
- }
-
- {
- SCOPED_TRACE("Asking for the original record under wildcard");
- findTest(Name("wild.*.foo.example.org"), RRType::A(),
- ZoneFinder::SUCCESS, true, rr_emptywild_);
- }
-
- {
- SCOPED_TRACE("Asking for A record");
- findTest(Name("a.foo.example.org"), RRType::A(), ZoneFinder::NXRRSET,
- true, ConstRRsetPtr(),
- ZoneFinder::RESULT_WILDCARD | expected_flags);
- findTest(Name("*.foo.example.org"), RRType::A(), ZoneFinder::NXRRSET,
- true, ConstRRsetPtr(), expected_flags);
- findTest(Name("foo.example.org"), RRType::A(), ZoneFinder::NXRRSET,
- true, ConstRRsetPtr(), expected_flags);
- }
-
- {
- SCOPED_TRACE("Asking for ANY record");
- findAllTest(Name("*.foo.example.org"), ZoneFinder::NXRRSET,
- vector<ConstRRsetPtr>(), expected_flags);
-
- findAllTest(Name("a.foo.example.org"), ZoneFinder::NXRRSET,
- vector<ConstRRsetPtr>(),
- ZoneFinder::RESULT_WILDCARD | expected_flags);
- }
-
- {
- SCOPED_TRACE("Asking on the non-terminal");
- findTest(Name("wild.bar.foo.example.org"), RRType::A(),
- ZoneFinder::NXRRSET, true, ConstRRsetPtr(),
- ZoneFinder::RESULT_WILDCARD | expected_flags);
- }
-}
-
-TEST_F(InMemoryZoneFinderTest, emptyWildcard) {
- emptyWildcardCheck();
-}
-
-TEST_F(InMemoryZoneFinderTest, emptyWildcardNSEC3) {
- emptyWildcardCheck(ZoneFinder::RESULT_NSEC3_SIGNED);
-}
-
-TEST_F(InMemoryZoneFinderTest, emptyWildcardNSEC) {
- emptyWildcardCheck(ZoneFinder::RESULT_NSEC_SIGNED);
-}
-
-// Same as emptyWildcard, but with multiple * in the path.
-TEST_F(InMemoryZoneFinderTest, nestedEmptyWildcard) {
- addZoneData(rr_nested_emptywild_);
-
- {
- SCOPED_TRACE("Asking for the original record under wildcards");
- findTest(Name("wild.*.foo.*.bar.example.org"), RRType::A(),
- ZoneFinder::SUCCESS, true, rr_nested_emptywild_);
- }
-
- {
- SCOPED_TRACE("Matching wildcard against empty nonterminal");
-
- const char* names[] = {
- "baz.foo.*.bar.example.org",
- "baz.foo.baz.bar.example.org",
- "*.foo.baz.bar.example.org",
- NULL
- };
-
- for (const char** name = names; *name != NULL; ++ name) {
- SCOPED_TRACE(string("Node ") + *name);
- findTest(Name(*name), RRType::A(), ZoneFinder::NXRRSET, true,
- ConstRRsetPtr(), ZoneFinder::RESULT_WILDCARD);
- }
- }
-
- // Domains to test
- const char* names[] = {
- "*.foo.*.bar.example.org",
- "foo.*.bar.example.org",
- "*.bar.example.org",
- "bar.example.org",
- NULL
- };
-
- {
- SCOPED_TRACE("Asking directly for A on parent nodes");
-
- for (const char** name = names; *name != NULL; ++ name) {
- SCOPED_TRACE(string("Node ") + *name);
- findTest(Name(*name), RRType::A(), ZoneFinder::NXRRSET);
- }
- }
-
- {
- SCOPED_TRACE("Asking for ANY on parent nodes");
-
- for (const char** name = names; *name != NULL; ++ name) {
- SCOPED_TRACE(string("Node ") + *name);
-
- findAllTest(Name(*name), ZoneFinder::NXRRSET,
- vector<ConstRRsetPtr>());
- }
- }
-}
-
-// We run this part twice from the below test, in two slightly different
-// situations
-void
-InMemoryZoneFinderTest::doCancelWildcardCheck(
- ZoneFinder::FindResultFlags expected_flags,
- ZoneFinder::FindOptions find_options)
-{
- // These should be canceled
- {
- SCOPED_TRACE("Canceled under foo.wild.example.org");
-
- // For an NSEC-signed zone with DNSSEC requested, the covering NSEC
- // should be returned. The expected NSEC is actually just the only
- // NSEC in the test data, but in this context it doesn't matter;
- // it's sufficient just to check any NSEC is returned (or not).
- ConstRRsetPtr expected_nsec; // by default it's NULL
- if ((expected_flags & ZoneFinder::RESULT_NSEC_SIGNED) != 0 &&
- (find_options & ZoneFinder::FIND_DNSSEC)) {
- expected_nsec = rr_nsec_;
- }
-
- findTest(Name("aaa.foo.wild.example.org"), RRType::A(),
- ZoneFinder::NXDOMAIN, true, expected_nsec, expected_flags,
- NULL, find_options);
- findTest(Name("zzz.foo.wild.example.org"), RRType::A(),
- ZoneFinder::NXDOMAIN, true, expected_nsec, expected_flags,
- NULL, find_options);
- }
-
- // This is existing, non-wildcard domain, shouldn't wildcard at all
- {
- SCOPED_TRACE("Existing domain under foo.wild.example.org");
- findTest(Name("bar.foo.wild.example.org"), RRType::A(),
- ZoneFinder::SUCCESS, true, rr_not_wild_);
- }
-
- // These should be caught by the wildcard
- {
- SCOPED_TRACE("Neighbor wildcards to foo.wild.example.org");
-
- const char* names[] = {
- "aaa.bbb.wild.example.org",
- "aaa.zzz.wild.example.org",
- "zzz.wild.example.org",
- NULL
- };
-
- for (const char** name = names; *name != NULL; ++ name) {
- SCOPED_TRACE(string("Node ") + *name);
-
- findTest(Name(*name), RRType::A(), ZoneFinder::SUCCESS, false,
- rr_wild_,
- ZoneFinder::RESULT_WILDCARD | expected_flags, NULL,
- ZoneFinder::FIND_DEFAULT, true);
- }
- }
-
- // This shouldn't be wildcarded, it's an existing domain
- {
- SCOPED_TRACE("The foo.wild.example.org itself");
- findTest(Name("foo.wild.example.org"), RRType::A(),
- ZoneFinder::NXRRSET, true, ConstRRsetPtr(), expected_flags);
- }
-}
-
-/*
- * This tests that if there's a name between the wildcard domain and the
- * searched one, it will not trigger wildcard, for example, if we have
- * *.wild.example.org and bar.foo.wild.example.org, then we know
- * foo.wild.example.org exists and is not wildcard. Therefore, search for
- * aaa.foo.wild.example.org should return NXDOMAIN.
- *
- * Tests few cases "around" the canceled wildcard match, to see something that
- * shouldn't be canceled isn't.
- */
-TEST_F(InMemoryZoneFinderTest, cancelWildcard) {
- addZoneData(rr_wild_);
- addZoneData(rr_not_wild_);
-
- {
- SCOPED_TRACE("Runnig with single entry under foo.wild.example.org");
- doCancelWildcardCheck();
- }
-
- // Try putting another one under foo.wild....
- // The result should be the same but it will be done in another way in the
- // code, because the foo.wild.example.org will exist in the tree.
- addZoneData(rr_not_wild_another_);
- {
- SCOPED_TRACE("Runnig with two entries under foo.wild.example.org");
- doCancelWildcardCheck();
- }
-}
-
-// Same tests as cancelWildcard for NSEC3-signed zone
-TEST_F(InMemoryZoneFinderTest, cancelWildcardNSEC3) {
- addZoneData(rr_wild_);
- addZoneData(rr_not_wild_);
- addZoneData(rr_nsec3_);
-
- {
- SCOPED_TRACE("Runnig with single entry under foo.wild.example.org");
- doCancelWildcardCheck(ZoneFinder::RESULT_NSEC3_SIGNED);
- }
- addZoneData(rr_not_wild_another_);
- {
- SCOPED_TRACE("Runnig with two entries under foo.wild.example.org");
- doCancelWildcardCheck(ZoneFinder::RESULT_NSEC3_SIGNED);
- }
-}
-
-// Same tests as cancelWildcard for NSEC-signed zone. Check both cases with
-// or without FIND_DNSSEC option. NSEC should be returned only when the option
-// is given.
-TEST_F(InMemoryZoneFinderTest, cancelWildcardNSEC) {
- addZoneData(rr_wild_);
- addZoneData(rr_not_wild_);
- addZoneData(rr_nsec_);
-
- {
- SCOPED_TRACE("Runnig with single entry under foo.wild.example.org");
- doCancelWildcardCheck(ZoneFinder::RESULT_NSEC_SIGNED,
- ZoneFinder::FIND_DNSSEC);
- doCancelWildcardCheck(ZoneFinder::RESULT_NSEC_SIGNED);
- }
- addZoneData(rr_not_wild_another_);
- {
- SCOPED_TRACE("Runnig with two entries under foo.wild.example.org");
- doCancelWildcardCheck(ZoneFinder::RESULT_NSEC_SIGNED,
- ZoneFinder::FIND_DNSSEC);
- doCancelWildcardCheck(ZoneFinder::RESULT_NSEC_SIGNED);
- }
-}
-
-
-// DISABLED: nsec3 will be re-added in #2118
-TEST_F(InMemoryZoneFinderTest, DISABLED_findNSEC3) {
- // Set up the faked hash calculator.
- setNSEC3HashCreator(&nsec3_hash_creator_);
-
- // Add a few NSEC3 records:
- // apex (example.org.): hash=0P..
- // ns1.example.org: hash=2T..
- // w.example.org: hash=01..
- // zzz.example.org: hash=R5..
- const string apex_nsec3_text = string(apex_hash) + ".example.org." +
- string(nsec3_common);
- addZoneData(textToRRset(apex_nsec3_text));
- const string ns1_nsec3_text = string(ns1_hash) + ".example.org." +
- string(nsec3_common);
- addZoneData(textToRRset(ns1_nsec3_text));
- const string w_nsec3_text = string(w_hash) + ".example.org." +
- string(nsec3_common);
- addZoneData(textToRRset(w_nsec3_text));
- const string zzz_nsec3_text = string(zzz_hash) + ".example.org." +
- string(nsec3_common);
- addZoneData(textToRRset(zzz_nsec3_text));
-
- performNSEC3Test(zone_finder_);
-}
-
-// DISABLED: NSEC3 will be re-added in #2218
-TEST_F(InMemoryZoneFinderTest, DISABLED_findNSEC3ForBadZone) {
- // Set up the faked hash calculator.
- setNSEC3HashCreator(&nsec3_hash_creator_);
-
- // If the zone has nothing about NSEC3 (neither NSEC3 or NSEC3PARAM),
- // findNSEC3() should be rejected.
- EXPECT_THROW(zone_finder_.findNSEC3(Name("www.example.org"), true),
- DataSourceError);
-
- // Only having NSEC3PARAM isn't enough
- addZoneData(textToRRset("example.org. 300 IN NSEC3PARAM "
- "1 0 12 aabbccdd"));
- EXPECT_THROW(zone_finder_.findNSEC3(Name("www.example.org"), true),
- DataSourceError);
-
- // Unless NSEC3 for apex is added the result in the recursive mode
- // is guaranteed.
- const string ns1_nsec3_text = string(ns1_hash) + ".example.org." +
- string(nsec3_common);
- addZoneData(textToRRset(ns1_nsec3_text));
- EXPECT_THROW(zone_finder_.findNSEC3(Name("www.example.org"), true),
- DataSourceError);
-}
-
-}
diff --git a/src/lib/datasrc/memory/tests/zone_table_unittest.cc b/src/lib/datasrc/memory/tests/zone_table_unittest.cc
deleted file mode 100644
index 359df49..0000000
--- a/src/lib/datasrc/memory/tests/zone_table_unittest.cc
+++ /dev/null
@@ -1,150 +0,0 @@
-// 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 <exceptions/exceptions.h>
-
-#include <util/memory_segment_local.h>
-
-#include <dns/name.h>
-#include <dns/rrclass.h>
-
-#include <datasrc/result.h>
-#include <datasrc/memory/zone_data.h>
-#include <datasrc/memory/zone_table.h>
-
-#include <gtest/gtest.h>
-
-#include <new> // for bad_alloc
-
-using namespace isc::dns;
-using namespace isc::datasrc;
-using namespace isc::datasrc::memory;
-
-namespace {
-// Memory segment specified for tests. It normally behaves like a "local"
-// memory segment. If "throw count" is set to non 0 via setThrowCount(),
-// it continues the normal behavior up to the specified number of calls to
-// allocate(), and throws an exception at the next call.
-class TestMemorySegment : public isc::util::MemorySegmentLocal {
-public:
- TestMemorySegment() : throw_count_(0) {}
- virtual void* allocate(size_t size) {
- if (throw_count_ > 0) {
- if (--throw_count_ == 0) {
- throw std::bad_alloc();
- }
- }
- return (isc::util::MemorySegmentLocal::allocate(size));
- }
- void setThrowCount(size_t count) { throw_count_ = count; }
-
-private:
- size_t throw_count_;
-};
-
-class ZoneTableTest : public ::testing::Test {
-protected:
- ZoneTableTest() : zclass_(RRClass::IN()),
- zname1(Name("example.com")),
- zname2(Name("example.net")),
- zname3(Name("example")),
- zone_table(ZoneTable::create(mem_sgmt_, zclass_))
- {}
- ~ZoneTableTest() {
- if (zone_table != NULL) {
- ZoneTable::destroy(mem_sgmt_, zone_table, zclass_);
- }
- }
- void TearDown() {
- ZoneTable::destroy(mem_sgmt_, zone_table, zclass_);
- zone_table = NULL;
- EXPECT_TRUE(mem_sgmt_.allMemoryDeallocated()); // catch any leak here.
- }
- const RRClass zclass_;
- const Name zname1, zname2, zname3;
- TestMemorySegment mem_sgmt_;
- ZoneTable* zone_table;
-};
-
-TEST_F(ZoneTableTest, create) {
- // Test about creating a zone table. Normal case covers through other
- // tests. We only check exception safety by letting the test memory
- // segment throw.
- mem_sgmt_.setThrowCount(2);
- EXPECT_THROW(ZoneTable::create(mem_sgmt_, zclass_), std::bad_alloc);
- // This shouldn't cause memory leak (that would be caught in TearDown()).
-}
-
-TEST_F(ZoneTableTest, addZone) {
- // Normal successful case.
- const ZoneTable::AddResult result1 =
- zone_table->addZone(mem_sgmt_, zclass_, zname1);
- EXPECT_EQ(result::SUCCESS, result1.code);
-
- // Duplicate add doesn't replace the existing data.
- EXPECT_EQ(result::EXIST, zone_table->addZone(mem_sgmt_, zclass_,
- zname1).code);
- EXPECT_EQ(result1.zone_data,
- zone_table->addZone(mem_sgmt_, zclass_, zname1).zone_data);
- // names are compared in a case insensitive manner.
- EXPECT_EQ(result::EXIST, zone_table->addZone(mem_sgmt_, zclass_,
- Name("EXAMPLE.COM")).code);
- // Add some more different ones. Should just succeed.
- EXPECT_EQ(result::SUCCESS,
- zone_table->addZone(mem_sgmt_, zclass_, zname2).code);
- EXPECT_EQ(result::SUCCESS,
- zone_table->addZone(mem_sgmt_, zclass_, zname3).code);
-
- // Have the memory segment throw an exception in extending the internal
- // tree. It still shouldn't cause memory leak (which would be detected
- // in TearDown()).
- mem_sgmt_.setThrowCount(2);
- EXPECT_THROW(zone_table->addZone(mem_sgmt_, zclass_, Name("example.org")),
- std::bad_alloc);
-}
-
-TEST_F(ZoneTableTest, findZone) {
- const ZoneTable::AddResult add_result1 =
- zone_table->addZone(mem_sgmt_, zclass_, zname1);
- EXPECT_EQ(result::SUCCESS, add_result1.code);
- EXPECT_EQ(result::SUCCESS,
- zone_table->addZone(mem_sgmt_, zclass_, zname2).code);
- EXPECT_EQ(result::SUCCESS,
- zone_table->addZone(mem_sgmt_, zclass_, zname3).code);
-
- const ZoneTable::FindResult find_result1 =
- zone_table->findZone(Name("example.com"));
- EXPECT_EQ(result::SUCCESS, find_result1.code);
- EXPECT_EQ(add_result1.zone_data, find_result1.zone_data);
-
- EXPECT_EQ(result::NOTFOUND,
- zone_table->findZone(Name("example.org")).code);
- EXPECT_EQ(static_cast<ZoneData*>(NULL),
- zone_table->findZone(Name("example.org")).zone_data);
-
- // there's no exact match. the result should be the longest match,
- // and the code should be PARTIALMATCH.
- EXPECT_EQ(result::PARTIALMATCH,
- zone_table->findZone(Name("www.example.com")).code);
- EXPECT_EQ(add_result1.zone_data,
- zone_table->findZone(Name("www.example.com")).zone_data);
-
- // make sure the partial match is indeed the longest match by adding
- // a zone with a shorter origin and query again.
- EXPECT_EQ(result::SUCCESS, zone_table->addZone(mem_sgmt_, zclass_,
- Name("com")).code);
- EXPECT_EQ(add_result1.zone_data,
- zone_table->findZone(Name("www.example.com")).zone_data);
-}
-}
diff --git a/src/lib/datasrc/memory/treenode_rrset.cc b/src/lib/datasrc/memory/treenode_rrset.cc
index fb7cafc..f51b2f9 100644
--- a/src/lib/datasrc/memory/treenode_rrset.cc
+++ b/src/lib/datasrc/memory/treenode_rrset.cc
@@ -79,10 +79,6 @@ TreeNodeRRset::setTTL(const RRTTL&) {
std::string
TreeNodeRRset::toText() const {
- // Create TTL from internal data
- util::InputBuffer ttl_buffer(rdataset_->getTTLData(), sizeof(uint32_t));
- const RRTTL ttl(ttl_buffer);
-
// Dump the main RRset, if not empty
std::string ret;
RRsetPtr tmp_rrset;
@@ -92,7 +88,7 @@ TreeNodeRRset::toText() const {
{
if (!tmp_rrset) {
tmp_rrset = RRsetPtr(new RRset(getName(), rrclass_, getType(),
- ttl));
+ getTTL()));
}
tmp_rrset->addRdata(rit->getCurrent());
}
@@ -101,17 +97,7 @@ TreeNodeRRset::toText() const {
}
// Dump any RRSIGs
- tmp_rrset.reset();
- for (RdataIteratorPtr rit = getSigRdataIterator();
- !rit->isLast();
- rit->next())
- {
- if (!tmp_rrset) {
- tmp_rrset = RRsetPtr(new RRset(getName(), rrclass_,
- RRType::RRSIG(), ttl));
- }
- tmp_rrset->addRdata(rit->getCurrent());
- }
+ tmp_rrset = getRRsig();
if (tmp_rrset) {
ret += tmp_rrset->toText();
}
@@ -292,7 +278,26 @@ TreeNodeRRset::getSigRdataIterator() const {
RRsetPtr
TreeNodeRRset::getRRsig() const {
- isc_throw(Unexpected, "unexpected method called on TreeNodeRRset");
+ // Shortcut: if DNSSEC is disabled for the RRset, simply return NULL.
+ // The generic code below provides the same behavior, but with much more
+ // overhead.
+ if (!dnssec_ok_) {
+ return (RRsetPtr());
+ }
+
+ RRsetPtr tmp_rrset;
+ for (RdataIteratorPtr rit = getSigRdataIterator();
+ !rit->isLast();
+ rit->next())
+ {
+ if (!tmp_rrset) {
+ tmp_rrset = RRsetPtr(new RRset(getName(), rrclass_,
+ RRType::RRSIG(), getTTL()));
+ }
+ tmp_rrset->addRdata(rit->getCurrent());
+ }
+
+ return (tmp_rrset);
}
void
diff --git a/src/lib/datasrc/memory/treenode_rrset.h b/src/lib/datasrc/memory/treenode_rrset.h
index a5f48bf..460a704 100644
--- a/src/lib/datasrc/memory/treenode_rrset.h
+++ b/src/lib/datasrc/memory/treenode_rrset.h
@@ -190,8 +190,6 @@ public:
virtual dns::RdataIteratorPtr getRdataIterator() const;
/// \brief Specialized version of \c getRRsig() for \c TreeNodeRRset.
- ///
- /// It throws \c isc::Unexpected unconditionally.
virtual dns::RRsetPtr getRRsig() const;
virtual unsigned int getRRsigDataCount() const {
diff --git a/src/lib/datasrc/memory/zone_finder.cc b/src/lib/datasrc/memory/zone_finder.cc
index cb56b8f..9403c4f 100644
--- a/src/lib/datasrc/memory/zone_finder.cc
+++ b/src/lib/datasrc/memory/zone_finder.cc
@@ -15,6 +15,7 @@
#include <datasrc/memory/zone_finder.h>
#include <datasrc/memory/domaintree.h>
#include <datasrc/memory/treenode_rrset.h>
+#include <datasrc/memory/rdata_serialization.h>
#include <datasrc/zone.h>
#include <datasrc/data_source.h>
@@ -22,9 +23,16 @@
#include <dns/name.h>
#include <dns/rrset.h>
#include <dns/rrtype.h>
+#include <dns/nsec3hash.h>
#include <datasrc/logger.h>
+#include <boost/scoped_ptr.hpp>
+#include <boost/bind.hpp>
+
+#include <algorithm>
+#include <vector>
+
using namespace isc::dns;
using namespace isc::datasrc::memory;
using namespace isc::datasrc;
@@ -33,6 +41,37 @@ namespace isc {
namespace datasrc {
namespace memory {
+namespace internal {
+
+// Specialized version of ZoneFinder::ResultContext, which holds objects
+// related to find() results using internal representations of the in-memory
+// data source implementation.
+class ZoneFinderResultContext {
+public:
+ /// \brief Constructor
+ ///
+ /// The first three parameters correspond to those of
+ /// ZoneFinder::ResultContext. If node is non NULL, it specifies the
+ /// found ZoneNode in the search.
+ ZoneFinderResultContext(ZoneFinder::Result code_param,
+ TreeNodeRRsetPtr rrset_param,
+ ZoneFinder::FindResultFlags flags_param,
+ const ZoneData& zone_data_param,
+ const ZoneNode* node, const RdataSet* rdset) :
+ code(code_param), rrset(rrset_param), flags(flags_param),
+ zone_data(&zone_data_param), found_node(node), found_rdset(rdset)
+ {}
+
+ const ZoneFinder::Result code;
+ const TreeNodeRRsetPtr rrset;
+ const ZoneFinder::FindResultFlags flags;
+ const ZoneData* const zone_data;
+ const ZoneNode* const found_node;
+ const RdataSet* const found_rdset;
+};
+}
+using internal::ZoneFinderResultContext;
+
namespace {
/// Creates a TreeNodeRRsetPtr for the given RdataSet at the given Node, for
/// the given RRClass
@@ -51,18 +90,21 @@ TreeNodeRRsetPtr
createTreeNodeRRset(const ZoneNode* node,
const RdataSet* rdataset,
const RRClass& rrclass,
+ ZoneFinder::FindOptions options,
const Name* realname = NULL)
{
+ const bool dnssec = ((options & ZoneFinder::FIND_DNSSEC) != 0);
if (node != NULL && rdataset != NULL) {
if (realname != NULL) {
- return TreeNodeRRsetPtr(new TreeNodeRRset(*realname, rrclass, node,
- rdataset, true));
+ return (TreeNodeRRsetPtr(new TreeNodeRRset(*realname, rrclass,
+ node, rdataset,
+ dnssec)));
} else {
- return TreeNodeRRsetPtr(new TreeNodeRRset(rrclass, node,
- rdataset, true));
+ return (TreeNodeRRsetPtr(new TreeNodeRRset(rrclass, node, rdataset,
+ dnssec)));
}
} else {
- return TreeNodeRRsetPtr();
+ return (TreeNodeRRsetPtr());
}
}
@@ -95,7 +137,7 @@ struct FindState {
};
// A callback called from possible zone cut nodes and nodes with DNAME.
-// This will be passed from findNode() to \c RBTree::find().
+// This will be passed from findNode() to \c ZoneTree::find().
bool cutCallback(const ZoneNode& node, FindState* state) {
// We need to look for DNAME first, there's allowed case where
// DNAME and NS coexist in the apex. DNAME is the one to notice,
@@ -145,6 +187,27 @@ bool cutCallback(const ZoneNode& node, FindState* state) {
return (false);
}
+/// Creates a NSEC3 ConstRRsetPtr for the given ZoneNode inside the
+/// NSEC3 tree, for the given RRClass.
+///
+/// It asserts that the node contains data (RdataSet) and is of type
+/// NSEC3.
+///
+/// \param node The ZoneNode inside the NSEC3 tree
+/// \param rrclass The RRClass as passed by the client
+ConstRRsetPtr
+createNSEC3RRset(const ZoneNode* node, const RRClass& rrclass) {
+ const RdataSet* rdataset = node->getData();
+ // Only NSEC3 ZoneNodes are allowed to be passed to this method. We
+ // assert that these have data, and also are of type NSEC3.
+ assert(rdataset != NULL);
+ assert(rdataset->type == RRType::NSEC3());
+
+ // Create the RRset. Note the DNSSEC flag: NSEC3 implies DNSSEC.
+ return (createTreeNodeRRset(node, rdataset, rrclass,
+ ZoneFinder::FIND_DNSSEC));
+}
+
// convenience function to fill in the final details
//
// Set up ZoneFinderResultContext object as a return value of find(),
@@ -158,14 +221,16 @@ bool cutCallback(const ZoneNode& node, FindState* state) {
// if wild is true, the RESULT_WILDCARD flag will be set.
// If qname is not NULL, this is the query name, to be used in wildcard
// substitution instead of the Node's name).
-isc::datasrc::memory::ZoneFinderResultContext
+ZoneFinderResultContext
createFindResult(const RRClass& rrclass,
const ZoneData& zone_data,
ZoneFinder::Result code,
- const RdataSet* rrset,
+ const RdataSet* rdset,
const ZoneNode* node,
+ ZoneFinder::FindOptions options,
bool wild = false,
- const Name* qname = NULL) {
+ const Name* qname = NULL)
+{
ZoneFinder::FindResultFlags flags = ZoneFinder::RESULT_DEFAULT;
const Name* rename = NULL;
@@ -182,9 +247,10 @@ createFindResult(const RRClass& rrclass,
}
}
- return (ZoneFinderResultContext(code, createTreeNodeRRset(node, rrset,
- rrclass, rename),
- flags, node));
+ return (ZoneFinderResultContext(code, createTreeNodeRRset(node, rdset,
+ rrclass, options,
+ rename),
+ flags, zone_data, node, rdset));
}
// A helper function for NSEC-signed zones. It searches the zone for
@@ -198,7 +264,7 @@ createFindResult(const RRClass& rrclass,
// it should always succeed.
//
// node_path must store valid search context (in practice, it's expected
-// to be set by findNode()); otherwise the underlying RBTree implementation
+// to be set by findNode()); otherwise the underlying ZoneTree implementation
// throws.
//
// If the zone is not considered NSEC-signed or DNSSEC records were not
@@ -341,19 +407,22 @@ public:
// caught above.
//
// If none of the above succeeds, we conclude the name doesn't exist in
-// the zone, and throw an OutOfZone exception.
+// the zone, and throw an OutOfZone exception by default. If the optional
+// out_of_zone_ok is true, it returns an NXDOMAIN result with NULL data so
+// the caller can take an action to it (technically it's not "NXDOMAIN",
+// but the caller is assumed not to rely on the difference.)
FindNodeResult findNode(const ZoneData& zone_data,
- const Name& name,
+ const LabelSequence& name_labels,
ZoneChain& node_path,
- ZoneFinder::FindOptions options)
+ ZoneFinder::FindOptions options,
+ bool out_of_zone_ok = false)
{
ZoneNode* node = NULL;
FindState state((options & ZoneFinder::FIND_GLUE_OK) != 0);
const ZoneTree& tree(zone_data.getZoneTree());
- ZoneTree::Result result = tree.find(isc::dns::LabelSequence(name),
- &node, node_path, cutCallback,
- &state);
+ const ZoneTree::Result result = tree.find(name_labels, &node, node_path,
+ cutCallback, &state);
const unsigned int zonecut_flag =
(state.zonecut_node_ != NULL) ? FindNodeResult::FIND_ZONECUT : 0;
if (result == ZoneTree::EXACTMATCH) {
@@ -377,7 +446,7 @@ FindNodeResult findNode(const ZoneData& zone_data,
if (node_path.getLastComparisonResult().getRelation() ==
NameComparisonResult::SUPERDOMAIN) { // empty node, so NXRRSET
LOG_DEBUG(logger, DBG_TRACE_DATA,
- DATASRC_MEM_SUPER_STOP).arg(name);
+ DATASRC_MEM_SUPER_STOP).arg(name_labels);
const ZoneNode* nsec_node;
const RdataSet* nsec_rds = getClosestNSEC(zone_data,
node_path,
@@ -398,7 +467,7 @@ FindNodeResult findNode(const ZoneData& zone_data,
// baz.foo.wild.example. The common ancestor, foo.wild.example,
// should cancel wildcard. Treat it as NXDOMAIN.
LOG_DEBUG(logger, DBG_TRACE_DATA,
- DATASRC_MEM_WILDCARD_CANCEL).arg(name);
+ DATASRC_MEM_WILDCARD_CANCEL).arg(name_labels);
const ZoneNode* nsec_node;
const RdataSet* nsec_rds = getClosestNSEC(zone_data,
node_path,
@@ -421,9 +490,8 @@ FindNodeResult findNode(const ZoneData& zone_data,
// Clear the node_path so that we don't keep incorrect (NSEC)
// context
node_path.clear();
- ZoneTree::Result result = tree.find(LabelSequence(wildcard_ls),
- &node, node_path, cutCallback,
- &state);
+ ZoneTree::Result result = tree.find(wildcard_ls, &node, node_path,
+ cutCallback, &state);
// Otherwise, why would the domain_flag::WILD be there if
// there was no wildcard under it?
assert(result == ZoneTree::EXACTMATCH);
@@ -431,53 +499,198 @@ FindNodeResult findNode(const ZoneData& zone_data,
FindNodeResult::FIND_WILDCARD | zonecut_flag));
}
- LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_NOT_FOUND).arg(name);
+ LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_NOT_FOUND).
+ arg(name_labels);
const ZoneNode* nsec_node;
const RdataSet* nsec_rds = getClosestNSEC(zone_data, node_path,
&nsec_node, options);
return (FindNodeResult(ZoneFinder::NXDOMAIN, nsec_node, nsec_rds));
} else {
// If the name is neither an exact or partial match, it is
- // out of bailiwick, which is considered an error.
- isc_throw(OutOfZone, name.toText() << " not in " <<
+ // out of bailiwick, which is considered an error, unless the caller
+ // is willing to accept it.
+ if (out_of_zone_ok) {
+ return (FindNodeResult(ZoneFinder::NXDOMAIN, NULL, NULL));
+ }
+ isc_throw(OutOfZone, name_labels << " not in " <<
zone_data.getOriginNode()->getName());
}
}
} // end anonymous namespace
-// Specialization of the ZoneFinder::Context for the in-memory finder.
+
+/// \brief Specialization of the ZoneFinder::Context for the in-memory finder.
+///
+/// Note that we don't have a specific constructor for the findAll() case.
+/// For (successful) type ANY query, found_node points to the
+/// corresponding zone node, which is recorded within this specialized
+/// context.
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,
- ZoneFinderResultContext result) :
+ const RRClass& rrclass, const ZoneFinderResultContext& result) :
ZoneFinder::Context(finder, options,
ResultContext(result.code, result.rrset,
result.flags)),
- rrset_(result.rrset), found_node_(result.found_node)
+ rrclass_(rrclass), zone_data_(result.zone_data),
+ found_node_(result.found_node),
+ found_rdset_(result.found_rdset)
{}
+protected:
+ virtual void getAdditionalImpl(const std::vector<RRType>& requested_types,
+ std::vector<ConstRRsetPtr>& result)
+ {
+ if (found_rdset_ != NULL) {
+ // Normal query with successful result.
+ getAdditionalForRdataset(found_rdset_, requested_types, result,
+ options_);
+ } else if (found_node_ != NULL) {
+ // Successful type ANY query result. Call
+ // getAdditionalForRdataset for each RdataSet of the node.
+ for (const RdataSet* rdset = found_node_->getData();
+ rdset != NULL;
+ rdset = rdset->getNext())
+ {
+ getAdditionalForRdataset(rdset, requested_types, result,
+ options_);
+ }
+ }
+ }
+
private:
- const TreeNodeRRsetPtr rrset_;
+ // Main subroutine of getAdditionalImpl, iterate over Rdata fields
+ // find, create, and insert necessary additional RRsets.
+ void
+ getAdditionalForRdataset(const RdataSet* rdset,
+ const std::vector<RRType>& requested_types,
+ std::vector<ConstRRsetPtr>& result,
+ ZoneFinder::FindOptions orig_options) const
+ {
+ ZoneFinder::FindOptions options = ZoneFinder::FIND_DEFAULT;
+ if ((orig_options & ZoneFinder::FIND_DNSSEC) != 0) {
+ options = options | ZoneFinder::FIND_DNSSEC;
+ }
+ if (rdset->type == RRType::NS()) {
+ options = options | ZoneFinder::FIND_GLUE_OK;
+ }
+
+ RdataReader(rrclass_, rdset->type, rdset->getDataBuf(),
+ rdset->getRdataCount(), rdset->getSigRdataCount(),
+ boost::bind(&Context::findAdditional, this,
+ &requested_types, &result, options, _1, _2),
+ &RdataReader::emptyDataAction).iterate();
+ }
+
+ // RdataReader callback for additional section processing.
+ void
+ findAdditional(const std::vector<RRType>* requested_types,
+ std::vector<ConstRRsetPtr>* result,
+ ZoneFinder::FindOptions options,
+ const LabelSequence& name_labels,
+ RdataNameAttributes attr) const;
+
+ // Subroutine for findAdditional() to unify the normal and wildcard match
+ // cases.
+ void
+ findAdditionalHelper(const std::vector<RRType>* requested_types,
+ std::vector<ConstRRsetPtr>* result,
+ const ZoneNode* node,
+ ZoneFinder::FindOptions options,
+ const Name* real_name) const
+ {
+ const std::vector<RRType>::const_iterator type_beg =
+ requested_types->begin();
+ const std::vector<RRType>::const_iterator type_end =
+ requested_types->end();
+ for (const RdataSet* rdset = node->getData();
+ rdset != NULL;
+ rdset = rdset->getNext())
+ {
+ // Checking all types for all RdataSets could be suboptimal.
+ // This can be a bit more optimized, but unless we have many
+ // requested types the effect is probably marginal. For now we
+ // keep it simple.
+ if (std::find(type_beg, type_end, rdset->type) != type_end) {
+ result->push_back(createTreeNodeRRset(node, rdset, rrclass_,
+ options, real_name));
+ }
+ }
+ }
+
+private:
+ const RRClass rrclass_;
+ const ZoneData* const zone_data_;
const ZoneNode* const found_node_;
+ const RdataSet* const found_rdset_;
};
+void
+InMemoryZoneFinder::Context::findAdditional(
+ const std::vector<RRType>* requested_types,
+ std::vector<ConstRRsetPtr>* result,
+ ZoneFinder::FindOptions options,
+ const LabelSequence& name_labels,
+ RdataNameAttributes attr) const
+{
+ // Ignore name data that don't need additional processing.
+ if ((attr & NAMEATTR_ADDITIONAL) == 0) {
+ return;
+ }
+
+ // Find the zone node for the additional name. By passing true as the
+ // last parameter of findNode() we ignore out-of-zone names.
+ ZoneChain node_path;
+ const FindNodeResult node_result =
+ findNode(*zone_data_, name_labels, node_path, options, true);
+ // we only need non-empty exact match
+ if (node_result.code != SUCCESS) {
+ return;
+ }
+
+ // Ignore data at a zone cut (due to subdomain delegation) unless glue is
+ // allowed. Checking the node callback flag is a cheap way to detect
+ // zone cuts, but it includes DNAME delegation, in which case we should
+ // keep finding the additional records regardless of the 'GLUE_OK' flag.
+ // The last two conditions limit the case to delegation NS, i.e, the node
+ // has an NS and it's not the zone origin.
+ const ZoneNode* node = node_result.node;
+ if ((options & ZoneFinder::FIND_GLUE_OK) == 0 &&
+ node->getFlag(ZoneNode::FLAG_CALLBACK) &&
+ node != zone_data_->getOriginNode() &&
+ RdataSet::find(node->getData(), RRType::NS()) != NULL) {
+ return;
+ }
+
+ // Examine RdataSets of the node, and create and insert requested types
+ // of RRsets as we find them.
+ if ((node_result.flags & FindNodeResult::FIND_WILDCARD) == 0) {
+ // normal case
+ findAdditionalHelper(requested_types, result, node, options, NULL);
+ } else {
+ // if the additional name is subject to wildcard substitution, we need
+ // to create a name object for the "real" (after substitution) name.
+ // This is expensive, but in the additional processing this should be
+ // very rare cases and acceptable.
+ size_t data_len;
+ const uint8_t* data;
+ data = name_labels.getData(&data_len);
+ util::InputBuffer buffer(data, data_len);
+ const Name real_name(buffer);
+ findAdditionalHelper(requested_types, result, node, options,
+ &real_name);
+ }
+}
+
boost::shared_ptr<ZoneFinder::Context>
InMemoryZoneFinder::find(const isc::dns::Name& name,
const isc::dns::RRType& type,
const FindOptions options)
{
- return ZoneFinderContextPtr(new Context(*this, options,
- find_internal(name,
- type,
- NULL,
- options)));
+ return (ZoneFinderContextPtr(new Context(*this, options, rrclass_,
+ find_internal(name, type,
+ NULL, options))));
}
boost::shared_ptr<ZoneFinder::Context>
@@ -485,11 +698,11 @@ InMemoryZoneFinder::findAll(const isc::dns::Name& name,
std::vector<isc::dns::ConstRRsetPtr>& target,
const FindOptions options)
{
- return ZoneFinderContextPtr(new Context(*this, options,
- find_internal(name,
- RRType::ANY(),
- &target,
- options)));
+ return (ZoneFinderContextPtr(new Context(*this, options, rrclass_,
+ find_internal(name,
+ RRType::ANY(),
+ &target,
+ options))));
}
ZoneFinderResultContext
@@ -502,10 +715,11 @@ InMemoryZoneFinder::find_internal(const isc::dns::Name& name,
// in findNode(). We simply construct a result structure and return.
ZoneChain node_path;
const FindNodeResult node_result =
- findNode(zone_data_, name, node_path, options);
+ findNode(zone_data_, LabelSequence(name), node_path, options);
if (node_result.code != SUCCESS) {
return (createFindResult(rrclass_, zone_data_, node_result.code,
- node_result.rrset, node_result.node));
+ node_result.rrset, node_result.node,
+ options));
}
const ZoneNode* node = node_result.node;
@@ -524,25 +738,26 @@ InMemoryZoneFinder::find_internal(const isc::dns::Name& name,
const RdataSet* nsec_rds = getClosestNSEC(zone_data_, node_path,
&nsec_node, options);
return (createFindResult(rrclass_, zone_data_, NXRRSET,
- nsec_rds,
- nsec_node,
- wild));
+ nsec_rds, nsec_node, options, wild));
}
const RdataSet* found;
// If the node callback is enabled, this may be a zone cut. If it
// has a NS RR, we should return a delegation, but not in the apex.
- // There is one exception: the case for DS query, which should always
- // be considered in-zone lookup.
+ // There are two exceptions:
+ // - the case for DS query, which should always be considered in-zone
+ // lookup.
+ // - when we are looking for glue records (FIND_GLUE_OK)
if (node->getFlag(ZoneNode::FLAG_CALLBACK) &&
- node != zone_data_.getOriginNode() && type != RRType::DS()) {
+ (options & FIND_GLUE_OK) == 0 &&
+ node != zone_data_.getOriginNode() && type != RRType::DS()) {
found = RdataSet::find(node->getData(), RRType::NS());
if (found != NULL) {
LOG_DEBUG(logger, DBG_TRACE_DATA,
DATASRC_MEM_EXACT_DELEGATION).arg(name);
return (createFindResult(rrclass_, zone_data_, DELEGATION,
- found, node, wild, &name));
+ found, node, options, wild, &name));
}
}
@@ -552,13 +767,13 @@ InMemoryZoneFinder::find_internal(const isc::dns::Name& name,
const RdataSet* cur_rds = node->getData();
while (cur_rds != NULL) {
target->push_back(createTreeNodeRRset(node, cur_rds, rrclass_,
- &name));
+ options, &name));
cur_rds = cur_rds->getNext();
}
LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_ANY_SUCCESS).
arg(name);
return (createFindResult(rrclass_, zone_data_, SUCCESS, NULL, node,
- wild, &name));
+ options, wild, &name));
}
found = RdataSet::find(node->getData(), type);
@@ -567,7 +782,7 @@ InMemoryZoneFinder::find_internal(const isc::dns::Name& name,
LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_SUCCESS).arg(name).
arg(type);
return (createFindResult(rrclass_, zone_data_, SUCCESS, found, node,
- wild, &name));
+ options, wild, &name));
} else {
// Next, try CNAME.
found = RdataSet::find(node->getData(), RRType::CNAME());
@@ -575,20 +790,158 @@ InMemoryZoneFinder::find_internal(const isc::dns::Name& name,
LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_CNAME).arg(name);
return (createFindResult(rrclass_, zone_data_, CNAME, found, node,
- wild, &name));
+ options, wild, &name));
}
}
// No exact match or CNAME. Get NSEC if necessary and return NXRRSET.
return (createFindResult(rrclass_, zone_data_, NXRRSET,
getNSECForNXRRSET(zone_data_, options, node),
- node, wild, &name));
+ node, options, wild, &name));
}
isc::datasrc::ZoneFinder::FindNSEC3Result
InMemoryZoneFinder::findNSEC3(const isc::dns::Name& name, bool recursive) {
- (void)name;
- (void)recursive;
- isc_throw(isc::NotImplemented, "not completed yet! please implement me");
+ LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_MEM_FINDNSEC3).arg(name).
+ arg(recursive ? "recursive" : "non-recursive");
+
+ uint8_t labels_buf[LabelSequence::MAX_SERIALIZED_LENGTH];
+ const LabelSequence origin_ls(zone_data_.getOriginNode()->
+ getAbsoluteLabels(labels_buf));
+ const LabelSequence name_ls(name);
+
+ if (!zone_data_.isNSEC3Signed()) {
+ isc_throw(DataSourceError,
+ "findNSEC3 attempt for non NSEC3 signed zone: " <<
+ origin_ls << "/" << getClass());
+ }
+
+ const NSEC3Data* nsec3_data = zone_data_.getNSEC3Data();
+ // This would be a programming mistake, as ZoneData::isNSEC3Signed()
+ // should check this.
+ assert(nsec3_data != NULL);
+
+ const ZoneTree& tree = nsec3_data->getNSEC3Tree();
+ if (tree.getNodeCount() == 0) {
+ isc_throw(DataSourceError,
+ "findNSEC3 attempt but zone has no NSEC3 RRs: " <<
+ origin_ls << "/" << getClass());
+ }
+
+ const NameComparisonResult cmp_result = name_ls.compare(origin_ls);
+ if (cmp_result.getRelation() != NameComparisonResult::EQUAL &&
+ cmp_result.getRelation() != NameComparisonResult::SUBDOMAIN) {
+ isc_throw(OutOfZone, "findNSEC3 attempt for out-of-zone name: "
+ << name_ls << ", zone: " << origin_ls << "/"
+ << getClass());
+ }
+
+ // Convenient shortcuts
+ const unsigned int olabels = origin_ls.getLabelCount();
+ const unsigned int qlabels = name.getLabelCount();
+ // placeholder of the next closer proof
+ const ZoneNode* covering_node(NULL);
+
+ // Now we'll first look up the origin node and initialize orig_chain
+ // with it.
+ ZoneChain orig_chain;
+ const ZoneNode* node(NULL);
+ ZoneTree::Result result =
+ tree.find<void*>(origin_ls, &node, orig_chain, NULL, NULL);
+ if (result != ZoneTree::EXACTMATCH) {
+ // If the origin node doesn't exist, simply fail.
+ isc_throw(DataSourceError,
+ "findNSEC3 attempt but zone has no NSEC3 RRs: " <<
+ origin_ls << "/" << getClass());
+ }
+
+ const boost::scoped_ptr<NSEC3Hash> hash
+ (NSEC3Hash::create(nsec3_data->hashalg,
+ nsec3_data->iterations,
+ nsec3_data->getSaltData(),
+ nsec3_data->getSaltLen()));
+
+ // 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.
+ for (unsigned int labels = qlabels; labels >= olabels; --labels) {
+ const Name& hname = (labels == qlabels ?
+ name : name.split(qlabels - labels, labels));
+ const std::string hlabel = hash->calculate(hname);
+
+ LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_MEM_FINDNSEC3_TRYHASH).
+ arg(name).arg(labels).arg(hlabel);
+
+ node = NULL;
+ ZoneChain chain(orig_chain);
+
+ // Now, make a label sequence relative to the origin.
+ const Name hlabel_name(hlabel);
+ LabelSequence hlabel_ls(hlabel_name);
+ // Remove trailing '.' making it relative
+ hlabel_ls.stripRight(1);
+
+ // Find hlabel relative to the orig_chain.
+ result = tree.find<void*>(hlabel_ls, &node, chain, NULL, NULL);
+ if (result == ZoneTree::EXACTMATCH) {
+ // We found an exact match.
+ ConstRRsetPtr closest = createNSEC3RRset(node, getClass());
+ ConstRRsetPtr next;
+ if (covering_node != NULL) {
+ next = createNSEC3RRset(covering_node, getClass());
+ }
+
+ LOG_DEBUG(logger, DBG_TRACE_BASIC,
+ DATASRC_MEM_FINDNSEC3_MATCH).arg(name).arg(labels).
+ arg(*closest);
+
+ return (FindNSEC3Result(true, labels, closest, next));
+ } else {
+ while ((covering_node = tree.previousNode(chain)) != NULL &&
+ covering_node->isEmpty()) {
+ ;
+ }
+ if (covering_node == NULL) {
+ covering_node = tree.largestNode();
+ }
+
+ if (!recursive) { // in non recursive mode, we are done.
+ ConstRRsetPtr closest;
+ if (covering_node != NULL) {
+ closest = createNSEC3RRset(covering_node, getClass());
+
+ LOG_DEBUG(logger, DBG_TRACE_BASIC,
+ DATASRC_MEM_FINDNSEC3_COVER).
+ arg(name).arg(*closest);
+ }
+
+ return (FindNSEC3Result(false, labels,
+ closest, ConstRRsetPtr()));
+ }
+ }
+ }
+
+ isc_throw(DataSourceError, "recursive findNSEC3 mode didn't stop, likely "
+ "a broken NSEC3 zone: " << getOrigin() << "/"
+ << getClass());
+}
+
+Name
+InMemoryZoneFinder::getOrigin() const {
+ // In future we may allow adding out-of-zone names in the zone tree.
+ // For example, to hold out-of-zone NS names so we can establish a
+ // shortcut link to them as an optimization. If and when that happens
+ // the origin node may not have an absolute label (consider the zone
+ // is example.org and we add ns.noexample.org). Even in such cases,
+ // DomainTreeNode::getAbsoluteLabels() returns the correct absolute
+ // label sequence.
+ uint8_t labels_buf[LabelSequence::MAX_SERIALIZED_LENGTH];
+ const LabelSequence name_labels =
+ zone_data_.getOriginNode()->getAbsoluteLabels(labels_buf);
+ size_t data_len;
+ const uint8_t* data = name_labels.getData(&data_len);
+
+ util::InputBuffer buffer(data, data_len);
+ return (Name(buffer));
}
} // namespace memory
diff --git a/src/lib/datasrc/memory/zone_finder.h b/src/lib/datasrc/memory/zone_finder.h
index 8f2c687..85e21c3 100644
--- a/src/lib/datasrc/memory/zone_finder.h
+++ b/src/lib/datasrc/memory/zone_finder.h
@@ -23,30 +23,15 @@
#include <dns/rrset.h>
#include <dns/rrtype.h>
+#include <string>
+
namespace isc {
namespace datasrc {
namespace memory {
-
-class ZoneFinderResultContext {
-public:
- /// \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.
- ZoneFinderResultContext(ZoneFinder::Result code_param,
- TreeNodeRRsetPtr rrset_param,
- ZoneFinder::FindResultFlags flags_param,
- const ZoneNode* node) :
- code(code_param), rrset(rrset_param), flags(flags_param),
- found_node(node)
- {}
-
- const ZoneFinder::Result code;
- const TreeNodeRRsetPtr rrset;
- const ZoneFinder::FindResultFlags flags;
- const ZoneNode* const found_node;
-};
+namespace internal {
+// intermediate result context, only used in the zone finder implementation.
+class ZoneFinderResultContext;
+}
/// A derived zone finder class intended to be used with the memory data
/// source, using ZoneData for its contents.
@@ -92,16 +77,13 @@ public:
findNSEC3(const isc::dns::Name& name, bool recursive);
/// \brief Returns the origin of the zone.
- virtual isc::dns::Name getOrigin() const {
- return zone_data_.getOriginNode()->getName();
- }
+ virtual isc::dns::Name getOrigin() const;
/// \brief Returns the RR class of the zone.
virtual isc::dns::RRClass getClass() const {
- return rrclass_;
+ return (rrclass_);
}
-
private:
/// \brief In-memory version of finder context.
///
@@ -110,7 +92,7 @@ private:
class Context;
/// Actual implementation for both find() and findAll()
- ZoneFinderResultContext find_internal(
+ internal::ZoneFinderResultContext find_internal(
const isc::dns::Name& name,
const isc::dns::RRType& type,
std::vector<isc::dns::ConstRRsetPtr>* target,
@@ -118,7 +100,7 @@ private:
FIND_DEFAULT);
const ZoneData& zone_data_;
- const isc::dns::RRClass& rrclass_;
+ const isc::dns::RRClass rrclass_;
};
} // namespace memory
diff --git a/src/lib/datasrc/query.cc b/src/lib/datasrc/query.cc
deleted file mode 100644
index a8d675a..0000000
--- a/src/lib/datasrc/query.cc
+++ /dev/null
@@ -1,116 +0,0 @@
-// Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC")
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
-
-#include <util/buffer.h>
-#include <dns/name.h>
-#include <dns/rrset.h>
-#include <dns/message.h>
-
-#include <cc/data.h>
-
-#include <datasrc/query.h>
-
-using namespace isc::dns;
-
-namespace isc {
-namespace datasrc {
-
-QueryTask::QueryTask(const Query& qry, const isc::dns::Name& n,
- const isc::dns::RRType& t,
- const isc::dns::Message::Section sect) :
- q(qry), qname(n), qclass(qry.qclass()), qtype(t), section(sect),
- op(AUTH_QUERY), state(GETANSWER), flags(0)
-{}
-
-QueryTask::QueryTask(const Query& qry, const isc::dns::Name& n,
- const isc::dns::RRType& t,
- const isc::dns::Message::Section sect,
- const Op o) :
- q(qry), qname(n), qclass(qry.qclass()), qtype(t), section(sect), op(o),
- state(GETANSWER), flags(0)
-{}
-
-QueryTask::QueryTask(const Query& qry, const isc::dns::Name& n,
- const isc::dns::RRType& t,
- const isc::dns::Message::Section sect,
- const State st) :
- q(qry), qname(n), qclass(qry.qclass()), qtype(t), section(sect),
- op(AUTH_QUERY), state(st), flags(0)
-{}
-
-QueryTask::QueryTask(const Query& qry, const isc::dns::Name& n,
- const isc::dns::RRType& t,
- const isc::dns::Message::Section sect,
- const Op o, const State st) :
- q(qry), qname(n), qclass(qry.qclass()), qtype(t), section(sect), op(o),
- state(st), flags(0)
-{}
-
-QueryTask::QueryTask(const Query& qry, const isc::dns::Name& n,
- const isc::dns::RRType& t, const Op o) :
- q(qry), qname(n), qclass(qry.qclass()), qtype(t),
- section(Message::SECTION_ANSWER), op(o), state(GETANSWER), flags(0)
-{
- if (op != SIMPLE_QUERY) {
- isc_throw(Unexpected, "invalid constructor for this task operation");
- }
-}
-
-// A referral query doesn't need to specify section, state, or type.
-QueryTask::QueryTask(const Query& qry, const isc::dns::Name& n, const Op o) :
- q(qry), qname(n), qclass(qry.qclass()), qtype(RRType::ANY()),
- section(Message::SECTION_ANSWER), op(o), state(GETANSWER), flags(0)
-{
- if (op != REF_QUERY) {
- isc_throw(Unexpected, "invalid constructor for this task operation");
- }
-}
-
-QueryTask::QueryTask(const Query& qry, const isc::dns::Name& n,
- const isc::dns::Message::Section sect, const Op o,
- const State st) :
- q(qry), qname(n), qclass(qry.qclass()), qtype(RRType::ANY()),
- section(sect), op(o), state(st), flags(0)
-{
- if (op != GLUE_QUERY && op != NOGLUE_QUERY) {
- isc_throw(Unexpected, "invalid constructor for this task operation");
- }
-}
-
-QueryTask::~QueryTask() {}
-
-Query::Query(Message& m, HotCache& c, bool dnssec) :
- status_(PENDING), qname_(NULL), qclass_(NULL), qtype_(NULL),
- cache_(&c), message_(&m), want_additional_(true), want_dnssec_(dnssec)
-{
- // Check message formatting
- if (message_->getRRCount(Message::SECTION_QUESTION) != 1) {
- isc_throw(Unexpected, "malformed message: too many questions");
- }
-
- // Populate the query task queue with the initial question
- QuestionPtr question = *message_->beginQuestion();
- qname_ = &question->getName();
- qclass_ = &question->getClass();
- qtype_ = &question->getType();
- restarts_ = 0;
-
- querytasks_.push(QueryTaskPtr(new QueryTask(*this, *qname_, *qtype_,
- Message::SECTION_ANSWER)));
-}
-
-Query::~Query() {}
-
-}
-}
diff --git a/src/lib/datasrc/query.h b/src/lib/datasrc/query.h
deleted file mode 100644
index 43b62cc..0000000
--- a/src/lib/datasrc/query.h
+++ /dev/null
@@ -1,255 +0,0 @@
-// Copyright (C) 2010 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 __QUERY_H
-#define __QUERY_H
-
-#include <boost/shared_ptr.hpp>
-
-#include <datasrc/cache.h>
-#include <datasrc/data_source.h>
-
-#include <dns/name.h>
-#include <dns/message.h>
-#include <dns/rrtype.h>
-#include <dns/rrclass.h>
-
-#include <queue>
-
-namespace isc {
-namespace datasrc {
-
-class Query;
-typedef boost::shared_ptr<Query> QueryPtr;
-
-// An individual task to be carried out by the query logic
-class QueryTask {
-private:
- /// Note: The copy constructor and the assignment operator are intentionally
- /// defined as private.
- QueryTask(const QueryTask& source);
- QueryTask& operator=(const QueryTask& source);
-public:
- // XXX: Members are currently public, but should probably be
- // moved to private and wrapped in get() functions later.
-
- // The \c Query that this \c QueryTask was created to service.
- const Query& q;
-
- // The standard query tuple: qname/qclass/qtype.
- // Note that qtype is ignored in the GLUE_QUERY/NOGLUE_QUERY case.
- const isc::dns::Name qname;
- const isc::dns::RRClass qclass;
- const isc::dns::RRType qtype;
-
- // The section of the reply into which the data should be
- // written after it has been fetched from the data source.
- const isc::dns::Message::Section section;
-
- // The op field indicates the operation to be carried out by
- // this query task:
- //
- // - SIMPLE_QUERY: look for a match for qname/qclass/qtype
- // in local data (regardless of whether it is above or below
- // a zone cut).
- //
- // - AUTH_QUERY: look for a match for qname/qclass/qtype, or
- // for qname/qclass/CNAME, or for a referral.
- //
- // - GLUE_QUERY: look for matches with qname/qclass/A
- // OR qname/class/AAAA in local data, regardless of
- // authority, for use in glue. (This can be implemented
- // as two successive SIMPLE_QUERY tasks, but might be
- // optimized by the concrete data source implementation
- // by turning it into a single database lookup.)
- //
- // - NOGLUE_QUERY: same as GLUE_QUERY except that answers
- // are rejected if they are below a zone cut.
- //
- // - REF_QUERY: look for matches for qname/qclass/NS,
- // qname/qclass/DS, and qname/qclass/DNAME. Used
- // to search for a zone cut.
-
- const enum Op {
- SIMPLE_QUERY,
- AUTH_QUERY,
- GLUE_QUERY,
- NOGLUE_QUERY,
- REF_QUERY
- } op;
-
- // The state field indicates the state of the query; it controls
- // the next step after processing each query task.
- //
- // - GETANSWER: We are looking for the answer to a primary query.
- // (The qname of the task should exactly match the qname of the
- // query.) If we have no match, the query has failed.
- //
- // - GETADDITIONAL: We are filling in additional data, either
- // as a result of finding NS or MX records via a GETANSWER
- // query task, or as a result of finding NS records when
- // getting authority-section data.
- //
- // - FOLLOWCNAME: We are looking for the target of a CNAME RR that
- // was found via a previous GETANSWER query task. If we have no
- // match, the query is still successful.
- //
- // (NOTE: It is only necessary to set a task state when pushing
- // tasks onto the query task queue, which in turn is only necessary
- // when it's uncertain which data source will be authoritative for the
- // data. That's why there is no GETAUTHORITY task state; when
- // processing an answer, either positive or negative, the authoritative
- // data source will already have been discovered, and can be queried
- // directly.)
-
- enum State {
- GETANSWER,
- GETADDITIONAL,
- FOLLOWCNAME
- } state;
-
- // Response flags to indicate conditions encountered while
- // processing this task.
- uint32_t flags;
-
- // Constructors
- QueryTask(const Query& q, const isc::dns::Name& n,
- const isc::dns::RRType& t,
- const isc::dns::Message::Section sect);
- QueryTask(const Query& q, const isc::dns::Name& n,
- const isc::dns::RRType& t,
- const isc::dns::Message::Section sect, Op o);
- QueryTask(const Query& q, const isc::dns::Name& n,
- const isc::dns::RRType& t,
- const isc::dns::Message::Section sect,
- const State st);
- QueryTask(const Query& q, const isc::dns::Name& n,
- const isc::dns::RRType& t,
- const isc::dns::Message::Section sect,
- Op o, State st);
-
- // These are special constructors for particular query task types,
- // to simplify the code.
- //
- // A simple query doesn't need to specify section or state.
- QueryTask(const Query& q, const isc::dns::Name& n,
- const isc::dns::RRType& t, Op o);
- // A referral query doesn't need to specify section, state, or type.
- QueryTask(const Query& q, const isc::dns::Name& n, Op o);
- // A glue (or noglue) query doesn't need to specify type.
- QueryTask(const Query& q, const isc::dns::Name& n,
- const isc::dns::Message::Section sect, Op o, State st);
-
- ~QueryTask();
-};
-
-typedef boost::shared_ptr<QueryTask> QueryTaskPtr;
-typedef std::queue<QueryTaskPtr> QueryTaskQueue;
-
-// Data Source query
-class Query {
-public:
- // The state of a query: pending or answered.
- enum Status {
- PENDING,
- ANSWERED
- };
-
- ///
- /// \name Constructors, Assignment Operator and Destructor.
- ///
- /// Note: The copy constructor and the assignment operator are intentionally
- /// defined as private.
- //@{
-private:
- Query(const Query& source);
- Query& operator=(const Query& source);
-public:
- // Query constructor
- Query(isc::dns::Message& m, HotCache& c, bool dnssec);
- /// \brief The destructor.
- virtual ~Query();
- //@}
-
- // wantAdditional() == true indicates that additional-section data
- // should be looked up while processing this query. false indicates
- // that we're only interested in answer-section data
- bool wantAdditional() { return (want_additional_); }
- void setWantAdditional(bool d) { want_additional_ = d; }
-
- // wantDnssec() == true indicates that DNSSEC data should be retrieved
- // from the data source when this query is being processed
- bool wantDnssec() const { return (want_dnssec_); }
- void setWantDnssec(bool d) { want_dnssec_ = d; }
-
- const isc::dns::Name& qname() const { return (*qname_); }
- const isc::dns::RRClass& qclass() const { return (*qclass_); }
- const isc::dns::RRType& qtype() const { return (*qtype_); }
-
- // Note: these can't be constant member functions because they expose
- // writable 'handles' of internal member variables. It's questionable
- // whether we need these accessors in the first place because the
- // corresponding members are public (which itself is not a good practice
- // but it's a different topic), but at the moment we keep them.
- // We should definitely revisit the design later.
- isc::dns::Message& message() { return (*message_); }
- QueryTaskQueue& tasks() { return (querytasks_); }
-
- Status status() const { return (status_); }
- void setStatus(Status s) { status_ = s; }
-
- // Limit CNAME chains to 16 per query, to avoid loops
- inline bool tooMany() {
- if (++restarts_ > MAX_RESTARTS) {
- return (true);
- }
- return (false);
- }
-
- void setDatasrc(DataSrc* ds) { datasrc_ = ds; }
- DataSrc* datasrc() const { return (datasrc_); }
-
- // \brief The query cache. This is a static member of class \c Query;
- // the same cache will be used by all instances.
- HotCache& getCache() const { return (*cache_); }
-
-private:
- Status status_;
-
- const isc::dns::Name* qname_;
- const isc::dns::RRClass* qclass_;
- const isc::dns::RRType* qtype_;
-
- HotCache* cache_;
- DataSrc* datasrc_;
-
- isc::dns::Message* message_;
- QueryTaskQueue querytasks_;
-
- bool want_additional_;
- bool want_dnssec_;
-
- static const int MAX_RESTARTS = 16;
- int restarts_;
-};
-
-}
-}
-
-
-#endif
-
-// Local Variables:
-// mode: c++
-// End:
diff --git a/src/lib/datasrc/sqlite3_datasrc.cc b/src/lib/datasrc/sqlite3_datasrc.cc
deleted file mode 100644
index bced1ae..0000000
--- a/src/lib/datasrc/sqlite3_datasrc.cc
+++ /dev/null
@@ -1,917 +0,0 @@
-// Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC")
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
-
-#include <string>
-#include <sstream>
-#include <utility>
-
-#include <sqlite3.h>
-
-#include <datasrc/sqlite3_datasrc.h>
-#include <datasrc/logger.h>
-#include <exceptions/exceptions.h>
-#include <dns/rrttl.h>
-#include <dns/rdata.h>
-#include <dns/rdataclass.h>
-#include <dns/rrset.h>
-#include <dns/rrsetlist.h>
-
-namespace {
-// Expected schema. The major version must match else there is an error. If
-// the minor version of the database is less than this, a warning is output.
-//
-// It is assumed that a program written to run on m.n of the database will run
-// with a database version m.p, where p is any number. However, if p < n,
-// we assume that the database structure was upgraded for some reason, and that
-// some advantage may result if the database is upgraded. Conversely, if p > n,
-// The database is at a later version than the program was written for and the
-// program may not be taking advantage of features (possibly performance
-// improvements) added to the database.
-const int SQLITE_SCHEMA_MAJOR_VERSION = 2;
-const int SQLITE_SCHEMA_MINOR_VERSION = 0;
-}
-
-using namespace std;
-using namespace isc::dns;
-using namespace isc::dns::rdata;
-
-namespace isc {
-namespace datasrc {
-
-struct Sqlite3Parameters {
- Sqlite3Parameters() : db_(NULL), major_version_(-1), minor_version_(-1),
- q_zone_(NULL), q_record_(NULL), q_addrs_(NULL), q_referral_(NULL),
- q_any_(NULL), q_count_(NULL), q_previous_(NULL), q_nsec3_(NULL),
- q_prevnsec3_(NULL)
- {}
- sqlite3* db_;
- int major_version_;
- int minor_version_;
- sqlite3_stmt* q_zone_;
- sqlite3_stmt* q_record_;
- sqlite3_stmt* q_addrs_;
- sqlite3_stmt* q_referral_;
- sqlite3_stmt* q_any_;
- sqlite3_stmt* q_count_;
- sqlite3_stmt* q_previous_;
- sqlite3_stmt* q_nsec3_;
- sqlite3_stmt* q_prevnsec3_;
-};
-
-namespace {
-const char* const SCHEMA_LIST[] = {
- "CREATE TABLE schema_version (version INTEGER NOT NULL, "
- "minor INTEGER NOT NULL DEFAULT 0)",
- "INSERT INTO schema_version VALUES (2, 0)",
- "CREATE TABLE zones (id INTEGER PRIMARY KEY, "
- "name TEXT NOT NULL COLLATE NOCASE, "
- "rdclass TEXT NOT NULL COLLATE NOCASE DEFAULT 'IN', "
- "dnssec BOOLEAN NOT NULL DEFAULT 0)",
- "CREATE INDEX zones_byname ON zones (name)",
- "CREATE TABLE records (id INTEGER PRIMARY KEY, "
- "zone_id INTEGER NOT NULL, name TEXT NOT NULL COLLATE NOCASE, "
- "rname TEXT NOT NULL COLLATE NOCASE, ttl INTEGER NOT NULL, "
- "rdtype TEXT NOT NULL COLLATE NOCASE, sigtype TEXT COLLATE NOCASE, "
- "rdata TEXT NOT NULL)",
- "CREATE INDEX records_byname ON records (name)",
- "CREATE INDEX records_byrname ON records (rname)",
- "CREATE INDEX records_bytype_and_rname ON records (rdtype, rname)",
- "CREATE TABLE nsec3 (id INTEGER PRIMARY KEY, zone_id INTEGER NOT NULL, "
- "hash TEXT NOT NULL COLLATE NOCASE, "
- "owner TEXT NOT NULL COLLATE NOCASE, "
- "ttl INTEGER NOT NULL, rdtype TEXT NOT NULL COLLATE NOCASE, "
- "rdata TEXT NOT NULL)",
- "CREATE INDEX nsec3_byhash ON nsec3 (hash)",
- "CREATE TABLE diffs (id INTEGER PRIMARY KEY, "
- "zone_id INTEGER NOT NULL, "
- "version INTEGER NOT NULL, "
- "operation INTEGER NOT NULL, "
- "name TEXT NOT NULL COLLATE NOCASE, "
- "rrtype TEXT NOT NULL COLLATE NOCASE, "
- "ttl INTEGER NOT NULL, "
- "rdata TEXT NOT NULL)",
- NULL
-};
-
-const char* const q_version_str = "SELECT version FROM schema_version";
-const char* const q_minor_str = "SELECT minor FROM schema_version";
-
-const char* const q_zone_str = "SELECT id FROM zones WHERE name=?1";
-
-const char* const q_record_str = "SELECT rdtype, ttl, sigtype, rdata "
- "FROM records WHERE zone_id=?1 AND name=?2 AND "
- "((rdtype=?3 OR sigtype=?3) OR "
- "(rdtype='CNAME' OR sigtype='CNAME') OR "
- "(rdtype='NS' OR sigtype='NS'))";
-
-const char* const q_addrs_str = "SELECT rdtype, ttl, sigtype, rdata "
- "FROM records WHERE zone_id=?1 AND name=?2 AND "
- "(rdtype='A' OR sigtype='A' OR rdtype='AAAA' OR sigtype='AAAA')";
-
-const char* const q_referral_str = "SELECT rdtype, ttl, sigtype, rdata FROM "
- "records WHERE zone_id=?1 AND name=?2 AND"
- "(rdtype='NS' OR sigtype='NS' OR rdtype='DS' OR sigtype='DS' OR "
- "rdtype='DNAME' OR sigtype='DNAME')";
-
-const char* const q_any_str = "SELECT rdtype, ttl, sigtype, rdata "
- "FROM records WHERE zone_id=?1 AND name=?2";
-
-// Note: the wildcard symbol '%' is expected to be added to the text
-// for the placeholder for LIKE given via sqlite3_bind_text(). We don't
-// use the expression such as (?2 || '%') because it would disable the use
-// of indices and could result in terrible performance.
-const char* const q_count_str = "SELECT COUNT(*) FROM records "
- "WHERE zone_id=?1 AND rname LIKE ?2;";
-
-const char* const q_previous_str = "SELECT name FROM records "
- "WHERE rname < ?2 AND zone_id=?1 AND rdtype = 'NSEC' "
- "ORDER BY rname DESC LIMIT 1";
-
-const char* const q_nsec3_str = "SELECT rdtype, ttl, rdata FROM nsec3 "
- "WHERE zone_id = ?1 AND hash = $2";
-
-const char* const q_prevnsec3_str = "SELECT hash FROM nsec3 "
- "WHERE zone_id = ?1 AND hash <= $2 ORDER BY hash DESC LIMIT 1";
-
-}
-
-//
-// Find the exact zone match. Return -1 if not found, or the zone's
-// ID if found. This will always be >= 0 if found.
-//
-int
-Sqlite3DataSrc::hasExactZone(const char* const name) const {
- int rc;
-
- sqlite3_reset(dbparameters->q_zone_);
- rc = sqlite3_bind_text(dbparameters->q_zone_, 1, name, -1, SQLITE_STATIC);
- if (rc != SQLITE_OK) {
- isc_throw(Sqlite3Error, "Could not bind " << name <<
- " to SQL statement (zone)");
- }
-
- rc = sqlite3_step(dbparameters->q_zone_);
- const int i = (rc == SQLITE_ROW) ?
- sqlite3_column_int(dbparameters->q_zone_, 0) : -1;
- sqlite3_reset(dbparameters->q_zone_);
- return (i);
-}
-
-namespace {
-int
-importSqlite3Rows(sqlite3_stmt* query, const Name& qname, const RRClass& qclass,
- const RRType& qtype, const bool nsec3_tree,
- RRsetList& result_sets, uint32_t& flags)
-{
- int rows = 0;
- int rc = sqlite3_step(query);
- const bool qtype_is_any = (qtype == RRType::ANY());
-
- while (rc == SQLITE_ROW) {
- const char* type = (const char*)sqlite3_column_text(query, 0);
- int ttl = sqlite3_column_int(query, 1);
- const char* sigtype = NULL;
- const char* rdata;
-
- if (nsec3_tree) {
- rdata = (const char*)sqlite3_column_text(query, 2);
- if (RRType(type) == RRType::RRSIG()) {
- sigtype = "NSEC3";
- }
- } else {
- sigtype = (const char*)sqlite3_column_text(query, 2);
- rdata = (const char*)sqlite3_column_text(query, 3);
- }
-
- const RRType base_rrtype(sigtype != NULL ? sigtype : type);
-
- // found an NS; we need to inform the caller that this might be a
- // referral, but we do not return the NS RRset to the caller
- // unless asked for it.
- if (base_rrtype == RRType::NS()) {
- flags |= DataSrc::REFERRAL;
- if (!qtype_is_any && qtype != RRType::NS()) {
- rc = sqlite3_step(query);
- continue;
- }
- }
-
- ++rows;
-
- // Looking for something else but found CNAME
- if (base_rrtype == RRType::CNAME() && qtype != RRType::CNAME()) {
- if (qtype == RRType::NSEC()) {
- // NSEC query, just skip the CNAME
- rc = sqlite3_step(query);
- continue;
- } else if (!qtype_is_any) {
- // include the CNAME, but don't flag it for chasing if
- // this is an ANY query
- flags |= DataSrc::CNAME_FOUND;
- }
- }
-
- RRsetPtr rrset = result_sets.findRRset(base_rrtype, qclass);
- if (rrset == NULL) {
- rrset = RRsetPtr(new RRset(qname, qclass, base_rrtype, RRTTL(ttl)));
- result_sets.addRRset(rrset);
- }
-
- if (sigtype == NULL && base_rrtype == rrset->getType()) {
- rrset->addRdata(createRdata(rrset->getType(), qclass, rdata));
- if (ttl > rrset->getTTL().getValue()) {
- rrset->setTTL(RRTTL(ttl));
- }
- } else if (sigtype != NULL && base_rrtype == rrset->getType()) {
- RdataPtr rrsig = createRdata(RRType::RRSIG(), qclass, rdata);
- if (rrset->getRRsig()) {
- rrset->getRRsig()->addRdata(rrsig);
- } else {
- RRsetPtr sigs = RRsetPtr(new RRset(qname, qclass,
- RRType::RRSIG(),
- RRTTL(ttl)));
- sigs->addRdata(rrsig);
- rrset->addRRsig(sigs);
- }
-
- if (ttl > rrset->getRRsig()->getTTL().getValue()) {
- rrset->getRRsig()->setTTL(RRTTL(ttl));
- }
- }
-
- rc = sqlite3_step(query);
- }
-
- return (rows);
-}
-}
-
-int
-Sqlite3DataSrc::findRecords(const Name& name, const RRType& rdtype,
- RRsetList& target, const Name* zonename,
- const Mode mode, uint32_t& flags) const
-{
- LOG_DEBUG(logger, DBG_TRACE_DETAILED, DATASRC_SQLITE_FINDREC).arg(name).
- arg(rdtype);
- flags = 0;
- int zone_id = (zonename == NULL) ? findClosest(name, NULL) :
- findClosest(*zonename, NULL);
- if (zone_id < 0) {
- flags = NO_SUCH_ZONE;
- return (0);
- }
-
- sqlite3_stmt* query;
- switch (mode) {
- case ADDRESS:
- query = dbparameters->q_addrs_;
- break;
- case DELEGATION:
- query = dbparameters->q_referral_;
- break;
- default:
- if (rdtype == RRType::ANY()) {
- query = dbparameters->q_any_;
- } else {
- query = dbparameters->q_record_;
- }
- break;
- }
-
- sqlite3_reset(query);
- sqlite3_clear_bindings(query);
-
- int rc;
- rc = sqlite3_bind_int(query, 1, zone_id);
- if (rc != SQLITE_OK) {
- isc_throw(Sqlite3Error, "Could not bind zone ID " << zone_id <<
- " to SQL statement (query)");
- }
- const string name_text = name.toText();
- rc = sqlite3_bind_text(query, 2, name_text.c_str(), -1, SQLITE_STATIC);
- if (rc != SQLITE_OK) {
- isc_throw(Sqlite3Error, "Could not bind name " << name_text <<
- " to SQL statement (query)");
- }
-
- const string rdtype_text = rdtype.toText();
- if (query == dbparameters->q_record_) {
- rc = sqlite3_bind_text(query, 3, rdtype_text.c_str(), -1,
- SQLITE_STATIC);
- if (rc != SQLITE_OK) {
- isc_throw(Sqlite3Error, "Could not bind RR type " <<
- rdtype.toText() << " to SQL statement (query)");
- }
- }
-
- const int rows = importSqlite3Rows(query, name, getClass(), rdtype, false,
- target, flags);
- sqlite3_reset(query);
- if (rows > 0) {
- return (rows);
- }
-
- //
- // No rows were found. We need to find out whether there are
- // any RRs with that name to determine whether this is NXDOMAIN or
- // NXRRSET
- //
- sqlite3_reset(dbparameters->q_count_);
- sqlite3_clear_bindings(dbparameters->q_count_);
-
- rc = sqlite3_bind_int(dbparameters->q_count_, 1, zone_id);
- if (rc != SQLITE_OK) {
- isc_throw(Sqlite3Error, "Could not bind zone ID " << zone_id <<
- " to SQL statement (qcount)");
- }
-
- const string revname_text = name.reverse().toText() + "%";
- rc = sqlite3_bind_text(dbparameters->q_count_, 2,
- revname_text.c_str(),
- -1, SQLITE_STATIC);
- if (rc != SQLITE_OK) {
- isc_throw(Sqlite3Error, "Could not bind name " << name.reverse() <<
- " to SQL statement (qcount)");
- }
-
- rc = sqlite3_step(dbparameters->q_count_);
- if (rc == SQLITE_ROW) {
- if (sqlite3_column_int(dbparameters->q_count_, 0) != 0) {
- flags |= TYPE_NOT_FOUND;
- sqlite3_reset(dbparameters->q_count_);
- return (0);
- }
- }
-
- flags |= NAME_NOT_FOUND;
- sqlite3_reset(dbparameters->q_count_);
- return (0);
-}
-
-//
-// Search for the closest enclosing zone. Will return -1 if not found,
-// >= 0 if found. If position is not NULL, it will be filled in with the
-// longest match found.
-//
-int
-Sqlite3DataSrc::findClosest(const Name& name, unsigned int* position) const {
- const unsigned int nlabels = name.getLabelCount();
- for (unsigned int i = 0; i < nlabels; ++i) {
- const Name matchname(name.split(i));
- const int rc = hasExactZone(matchname.toText().c_str());
- if (rc >= 0) {
- if (position != NULL) {
- *position = i;
- }
- return (rc);
- }
- }
-
- return (-1);
-}
-
-void
-Sqlite3DataSrc::findClosestEnclosure(DataSrcMatch& match) const {
- LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_SQLITE_ENCLOSURE).
- arg(match.getName());
- if (match.getClass() != getClass() && match.getClass() != RRClass::ANY()) {
- return;
- }
-
- unsigned int position;
- if (findClosest(match.getName(), &position) == -1) {
- LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_SQLITE_ENCLOSURE_NOT_FOUND)
- .arg(match.getName());
- return;
- }
-
- match.update(*this, match.getName().split(position));
-}
-
-DataSrc::Result
-Sqlite3DataSrc::findPreviousName(const Name& qname,
- Name& target,
- const Name* zonename) const
-{
- LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_SQLITE_PREVIOUS).arg(qname);
- const int zone_id = (zonename == NULL) ?
- findClosest(qname, NULL) : findClosest(*zonename, NULL);
- if (zone_id < 0) {
- LOG_ERROR(logger, DATASRC_SQLITE_PREVIOUS_NO_ZONE).arg(qname.toText());
- return (ERROR);
- }
-
- sqlite3_reset(dbparameters->q_previous_);
- sqlite3_clear_bindings(dbparameters->q_previous_);
-
- int rc = sqlite3_bind_int(dbparameters->q_previous_, 1, zone_id);
- if (rc != SQLITE_OK) {
- isc_throw(Sqlite3Error, "Could not bind zone ID " << zone_id <<
- " to SQL statement (qprevious)");
- }
- const string revname_text = qname.reverse().toText();
- rc = sqlite3_bind_text(dbparameters->q_previous_, 2,
- revname_text.c_str(), -1, SQLITE_STATIC);
- if (rc != SQLITE_OK) {
- isc_throw(Sqlite3Error, "Could not bind name " << qname <<
- " to SQL statement (qprevious)");
- }
-
- rc = sqlite3_step(dbparameters->q_previous_);
- if (rc != SQLITE_ROW) {
- sqlite3_reset(dbparameters->q_previous_);
- return (ERROR);
- }
-
- // XXX: bad cast. we should revisit this.
- target = Name((const char*)sqlite3_column_text(dbparameters->q_previous_,
- 0));
- sqlite3_reset(dbparameters->q_previous_);
- return (SUCCESS);
-}
-
-DataSrc::Result
-Sqlite3DataSrc::findCoveringNSEC3(const Name& zonename,
- string& hashstr,
- RRsetList& target) const
-{
- LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_SQLITE_FIND_NSEC3).
- arg(zonename).arg(hashstr);
- const int zone_id = findClosest(zonename, NULL);
- if (zone_id < 0) {
- LOG_ERROR(logger, DATASRC_SQLITE_FIND_NSEC3_NO_ZONE).arg(zonename);
- return (ERROR);
- }
-
- sqlite3_reset(dbparameters->q_prevnsec3_);
- sqlite3_clear_bindings(dbparameters->q_prevnsec3_);
-
- int rc = sqlite3_bind_int(dbparameters->q_prevnsec3_, 1, zone_id);
- if (rc != SQLITE_OK) {
- isc_throw(Sqlite3Error, "Could not bind zone ID " << zone_id <<
- " to SQL statement (previous NSEC3)");
- }
-
- rc = sqlite3_bind_text(dbparameters->q_prevnsec3_, 2, hashstr.c_str(),
- -1, SQLITE_STATIC);
- if (rc != SQLITE_OK) {
- isc_throw(Sqlite3Error, "Could not bind hash " << hashstr <<
- " to SQL statement (previous NSEC3)");
- }
-
- rc = sqlite3_step(dbparameters->q_prevnsec3_);
- const char* hash;
- if (rc == SQLITE_ROW) {
- hash = (const char*) sqlite3_column_text(dbparameters->q_prevnsec3_, 0);
- } else {
- // We need to find the final NSEC3 in the chain.
- // A valid NSEC3 hash is in base32, which contains no
- // letters higher than V, so a search for the previous
- // NSEC3 from "w" will always find it.
- sqlite3_reset(dbparameters->q_prevnsec3_);
- rc = sqlite3_bind_text(dbparameters->q_prevnsec3_, 2, "w", -1,
- SQLITE_STATIC);
- if (rc != SQLITE_OK) {
- isc_throw(Sqlite3Error, "Could not bind \"w\""
- " to SQL statement (previous NSEC3)");
- }
-
- rc = sqlite3_step(dbparameters->q_prevnsec3_);
- if (rc != SQLITE_ROW) {
- return (ERROR);
- }
-
- hash = (const char*) sqlite3_column_text(dbparameters->q_prevnsec3_, 0);
- }
-
- sqlite3_reset(dbparameters->q_nsec3_);
- sqlite3_clear_bindings(dbparameters->q_nsec3_);
-
- rc = sqlite3_bind_int(dbparameters->q_nsec3_, 1, zone_id);
- if (rc != SQLITE_OK) {
- isc_throw(Sqlite3Error, "Could not bind zone ID " << zone_id <<
- " to SQL statement (NSEC3)");
- }
-
- rc = sqlite3_bind_text(dbparameters->q_nsec3_, 2, hash, -1, SQLITE_STATIC);
- if (rc != SQLITE_OK) {
- isc_throw(Sqlite3Error, "Could not bind hash " << hash <<
- " to SQL statement (NSEC3)");
- }
-
- DataSrc::Result result = SUCCESS;
- uint32_t flags = 0;
- if (importSqlite3Rows(dbparameters->q_nsec3_,
- Name(hash).concatenate(zonename),
- getClass(), RRType::NSEC3(), true, target,
- flags) == 0 || flags != 0) {
- result = ERROR;
- }
- hashstr = string(hash);
- sqlite3_reset(dbparameters->q_nsec3_);
- return (result);
-}
-
-DataSrc::Result
-Sqlite3DataSrc::findRRset(const Name& qname,
- const RRClass& qclass,
- const RRType& qtype,
- RRsetList& target,
- uint32_t& flags,
- const Name* zonename) const
-{
- LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_SQLITE_FIND).arg(qname).
- arg(qtype);
- if (qclass != getClass() && qclass != RRClass::ANY()) {
- LOG_ERROR(logger, DATASRC_SQLITE_FIND_BAD_CLASS).arg(getClass()).
- arg(qclass);
- return (ERROR);
- }
- findRecords(qname, qtype, target, zonename, NORMAL, flags);
- return (SUCCESS);
-}
-
-DataSrc::Result
-Sqlite3DataSrc::findExactRRset(const Name& qname,
- const RRClass& qclass,
- const RRType& qtype,
- RRsetList& target,
- uint32_t& flags,
- const Name* zonename) const
-{
- LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_SQLITE_FINDEXACT).arg(qname).
- arg(qtype);
- if (qclass != getClass() && qclass != RRClass::ANY()) {
- LOG_ERROR(logger, DATASRC_SQLITE_FINDEXACT_BAD_CLASS).arg(getClass()).
- arg(qclass);
- return (ERROR);
- }
- findRecords(qname, qtype, target, zonename, NORMAL, flags);
-
- // Ignore referrals in this case
- flags &= ~REFERRAL;
-
- // CNAMEs don't count in this case
- if (flags & CNAME_FOUND) {
- flags &= ~CNAME_FOUND;
- flags |= TYPE_NOT_FOUND;
- }
-
- return (SUCCESS);
-}
-
-DataSrc::Result
-Sqlite3DataSrc::findAddrs(const Name& qname,
- const RRClass& qclass,
- RRsetList& target,
- uint32_t& flags,
- const Name* zonename) const
-{
- LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_SQLITE_FINDADDRS).arg(qname);
- if (qclass != getClass() && qclass != RRClass::ANY()) {
- LOG_ERROR(logger, DATASRC_SQLITE_FINDADDRS_BAD_CLASS).arg(getClass()).
- arg(qclass);
- return (ERROR);
- }
- findRecords(qname, RRType::ANY(), target, zonename, ADDRESS, flags);
- return (SUCCESS);
-}
-
-DataSrc::Result
-Sqlite3DataSrc::findReferral(const Name& qname,
- const RRClass& qclass,
- RRsetList& target,
- uint32_t& flags,
- const Name* zonename) const
-{
- LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_SQLITE_FINDREF).arg(qname);
- if (qclass != getClass() && qclass != RRClass::ANY()) {
- LOG_ERROR(logger, DATASRC_SQLITE_FINDREF_BAD_CLASS).arg(getClass()).
- arg(qclass);
- return (ERROR);
- }
- findRecords(qname, RRType::ANY(), target, zonename, DELEGATION, flags);
- return (SUCCESS);
-}
-
-Sqlite3DataSrc::Sqlite3DataSrc() :
- dbparameters(new Sqlite3Parameters)
-{
- LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_SQLITE_CREATE);
-}
-
-Sqlite3DataSrc::~Sqlite3DataSrc() {
- LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_SQLITE_DESTROY);
- if (dbparameters->db_ != NULL) {
- close();
- }
- delete dbparameters;
-}
-
-DataSrc::Result
-Sqlite3DataSrc::init(isc::data::ConstElementPtr config) {
- if (config && config->contains("database_file")) {
- open(config->get("database_file")->stringValue());
- } else {
- isc_throw(DataSourceError, "No SQLite database file specified");
- }
- return (SUCCESS);
-}
-
-namespace {
-// This is a helper class to initialize a Sqlite3 DB safely. An object of
-// this class encapsulates all temporary resources that are necessary for
-// the initialization, and release them in the destructor. Once everything
-// is properly initialized, the move() method moves the allocated resources
-// to the main object in an exception free manner. This way, the main code
-// for the initialization can be exception safe, and can provide the strong
-// exception guarantee.
-class Sqlite3Initializer {
-public:
- ~Sqlite3Initializer() {
- if (params_.q_zone_ != NULL) {
- sqlite3_finalize(params_.q_zone_);
- }
- if (params_.q_record_ != NULL) {
- sqlite3_finalize(params_.q_record_);
- }
- if (params_.q_addrs_ != NULL) {
- sqlite3_finalize(params_.q_addrs_);
- }
- if (params_.q_referral_ != NULL) {
- sqlite3_finalize(params_.q_referral_);
- }
- if (params_.q_any_ != NULL) {
- sqlite3_finalize(params_.q_any_);
- }
- if (params_.q_count_ != NULL) {
- sqlite3_finalize(params_.q_count_);
- }
- if (params_.q_previous_ != NULL) {
- sqlite3_finalize(params_.q_previous_);
- }
- if (params_.q_nsec3_ != NULL) {
- sqlite3_finalize(params_.q_nsec3_);
- }
- if (params_.q_prevnsec3_ != NULL) {
- sqlite3_finalize(params_.q_prevnsec3_);
- }
- if (params_.db_ != NULL) {
- sqlite3_close(params_.db_);
- }
- }
- void move(Sqlite3Parameters* dst) {
- *dst = params_;
- params_ = Sqlite3Parameters(); // clear everything
- }
- Sqlite3Parameters params_;
-};
-
-sqlite3_stmt*
-prepare(sqlite3* const db, const char* const statement) {
- sqlite3_stmt* prepared = NULL;
- if (sqlite3_prepare_v2(db, statement, -1, &prepared, NULL) != SQLITE_OK) {
- isc_throw(Sqlite3Error, "Could not prepare SQLite statement: " <<
- statement);
- }
- return (prepared);
-}
-
-// small function to sleep for 0.1 seconds, needed when waiting for
-// exclusive database locks (which should only occur on startup, and only
-// when the database has not been created yet)
-void do_sleep() {
- struct timespec req;
- req.tv_sec = 0;
- req.tv_nsec = 100000000;
- nanosleep(&req, NULL);
-}
-
-// returns the schema version element if the schema version table exists
-// returns -1 if it does not
-int check_schema_version_element(sqlite3* db, const char* const version_query) {
- sqlite3_stmt* prepared = NULL;
- // At this point in time, the database might be exclusively locked, in
- // which case even prepare() will return BUSY, so we may need to try a
- // few times
- for (size_t i = 0; i < 50; ++i) {
- int rc = sqlite3_prepare_v2(db, version_query, -1, &prepared, NULL);
- if (rc == SQLITE_ERROR) {
- // this is the error that is returned when the table does not
- // exist
- return (-1);
- } else if (rc == SQLITE_OK) {
- break;
- } else if (rc != SQLITE_BUSY || i == 50) {
- isc_throw(Sqlite3Error, "Unable to prepare version query: "
- << rc << " " << sqlite3_errmsg(db));
- }
- do_sleep();
- }
- if (sqlite3_step(prepared) != SQLITE_ROW) {
- isc_throw(Sqlite3Error,
- "Unable to query version: " << sqlite3_errmsg(db));
- }
- int version = sqlite3_column_int(prepared, 0);
- sqlite3_finalize(prepared);
- return (version);
-}
-
-// Returns the schema major and minor version numbers in a pair.
-// Returns (-1, -1) if the table does not exist, (1, 0) for a V1
-// database, and (n, m) for any other.
-pair<int, int> check_schema_version(sqlite3* db) {
- int major = check_schema_version_element(db, q_version_str);
- if (major == -1) {
- return (make_pair(-1, -1));
- } else if (major == 1) {
- return (make_pair(1, 0));
- } else {
- int minor = check_schema_version_element(db, q_minor_str);
- return (make_pair(major, minor));
- }
-}
-
-// A helper class used in create_database() below so we manage the one shot
-// transaction safely.
-class ScopedTransaction {
-public:
- ScopedTransaction(sqlite3* db) : db_(NULL) {
- // try for 5 secs (50*0.1)
- for (size_t i = 0; i < 50; ++i) {
- const int rc = sqlite3_exec(db, "BEGIN EXCLUSIVE TRANSACTION",
- NULL, NULL, NULL);
- if (rc == SQLITE_OK) {
- break;
- } else if (rc != SQLITE_BUSY || i == 50) {
- isc_throw(Sqlite3Error, "Unable to acquire exclusive lock "
- "for database creation: " << sqlite3_errmsg(db));
- }
- do_sleep();
- }
- // Hold the DB pointer once we have successfully acquired the lock.
- db_ = db;
- }
- ~ScopedTransaction() {
- if (db_ != NULL) {
- // Note: even rollback could fail in theory, but in that case
- // we cannot do much for safe recovery anyway. We could at least
- // log the event, but for now don't even bother to do that, with
- // the expectation that we'll soon stop creating the schema in this
- // module.
- sqlite3_exec(db_, "ROLLBACK", NULL, NULL, NULL);
- }
- }
- void commit() {
- if (sqlite3_exec(db_, "COMMIT TRANSACTION", NULL, NULL, NULL) !=
- SQLITE_OK) {
- isc_throw(Sqlite3Error, "Unable to commit newly created database "
- "schema: " << sqlite3_errmsg(db_));
- }
- db_ = NULL;
- }
-
-private:
- sqlite3* db_;
-};
-
-// return db version
-pair<int, int> create_database(sqlite3* db) {
- logger.info(DATASRC_SQLITE_SETUP_OLD_API);
-
- // try to get an exclusive lock. Once that is obtained, do the version
- // check *again*, just in case this process was racing another
- ScopedTransaction transaction(db);
- pair<int, int> schema_version = check_schema_version(db);
- if (schema_version.first == -1) {
- for (int i = 0; SCHEMA_LIST[i] != NULL; ++i) {
- if (sqlite3_exec(db, SCHEMA_LIST[i], NULL, NULL, NULL) !=
- SQLITE_OK) {
- isc_throw(Sqlite3Error,
- "Failed to set up schema " << SCHEMA_LIST[i]);
- }
- }
- transaction.commit();
-
- // Return the version. We query again to ensure that the only point
- // in which the current schema version is defined is in the
- // CREATE statements.
- schema_version = check_schema_version(db);
- }
- return (schema_version);
-}
-
-void
-checkAndSetupSchema(Sqlite3Initializer* initializer) {
- sqlite3* const db = initializer->params_.db_;
-
- // Note: we use the same SCHEMA_xxx_VERSION log IDs here and in
- // sqlite3_accessor.cc, which is against our policy of ID uniqueness.
- // The assumption is that this file will soon be deprecated, and we don't
- // bother to define separate IDs for the short period.
- pair<int, int> schema_version = check_schema_version(db);
- if (schema_version.first == -1) {
- schema_version = create_database(db);
- } else if (schema_version.first != SQLITE_SCHEMA_MAJOR_VERSION) {
- LOG_ERROR(logger, DATASRC_SQLITE_INCOMPATIBLE_VERSION)
- .arg(schema_version.first).arg(schema_version.second)
- .arg(SQLITE_SCHEMA_MAJOR_VERSION).arg(SQLITE_SCHEMA_MINOR_VERSION);
- isc_throw(IncompatibleDbVersion, "Incompatible database version");
- } else if (schema_version.second < SQLITE_SCHEMA_MINOR_VERSION) {
- LOG_WARN(logger, DATASRC_SQLITE_COMPATIBLE_VERSION)
- .arg(schema_version.first).arg(schema_version.second)
- .arg(SQLITE_SCHEMA_MAJOR_VERSION).arg(SQLITE_SCHEMA_MINOR_VERSION);
- }
-
- initializer->params_.major_version_ = schema_version.first;
- initializer->params_.minor_version_ = schema_version.second;
- initializer->params_.q_zone_ = prepare(db, q_zone_str);
- initializer->params_.q_record_ = prepare(db, q_record_str);
- initializer->params_.q_addrs_ = prepare(db, q_addrs_str);
- initializer->params_.q_referral_ = prepare(db, q_referral_str);
- initializer->params_.q_any_ = prepare(db, q_any_str);
- initializer->params_.q_count_ = prepare(db, q_count_str);
- initializer->params_.q_previous_ = prepare(db, q_previous_str);
- initializer->params_.q_nsec3_ = prepare(db, q_nsec3_str);
- initializer->params_.q_prevnsec3_ = prepare(db, q_prevnsec3_str);
-}
-}
-
-//
-// Open the database.
-//
-void
-Sqlite3DataSrc::open(const string& name) {
- LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_SQLITE_OPEN).arg(name);
- if (dbparameters->db_ != NULL) {
- isc_throw(DataSourceError, "Duplicate SQLite open with " << name);
- }
-
- Sqlite3Initializer initializer;
-
- if (sqlite3_open(name.c_str(), &initializer.params_.db_) != 0) {
- isc_throw(Sqlite3Error, "Cannot open SQLite database file: " << name);
- }
-
- checkAndSetupSchema(&initializer);
- initializer.move(dbparameters);
-}
-
-//
-// Close the database.
-//
-DataSrc::Result
-Sqlite3DataSrc::close(void) {
- LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_SQLITE_CLOSE);
- if (dbparameters->db_ == NULL) {
- isc_throw(DataSourceError,
- "SQLite data source is being closed before open");
- }
-
- // XXX: sqlite3_finalize() could fail. What should we do in that case?
- sqlite3_finalize(dbparameters->q_zone_);
- dbparameters->q_zone_ = NULL;
-
- sqlite3_finalize(dbparameters->q_record_);
- dbparameters->q_record_ = NULL;
-
- sqlite3_finalize(dbparameters->q_addrs_);
- dbparameters->q_addrs_ = NULL;
-
- sqlite3_finalize(dbparameters->q_referral_);
- dbparameters->q_referral_ = NULL;
-
- sqlite3_finalize(dbparameters->q_any_);
- dbparameters->q_any_ = NULL;
-
- sqlite3_finalize(dbparameters->q_count_);
- dbparameters->q_count_ = NULL;
-
- sqlite3_finalize(dbparameters->q_previous_);
- dbparameters->q_previous_ = NULL;
-
- sqlite3_finalize(dbparameters->q_prevnsec3_);
- dbparameters->q_prevnsec3_ = NULL;
-
- sqlite3_finalize(dbparameters->q_nsec3_);
- dbparameters->q_nsec3_ = NULL;
-
- sqlite3_close(dbparameters->db_);
- dbparameters->db_ = NULL;
-
- return (SUCCESS);
-}
-
-}
-}
diff --git a/src/lib/datasrc/sqlite3_datasrc.h b/src/lib/datasrc/sqlite3_datasrc.h
deleted file mode 100644
index 8ee042f..0000000
--- a/src/lib/datasrc/sqlite3_datasrc.h
+++ /dev/null
@@ -1,130 +0,0 @@
-// Copyright (C) 2010 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 __DATA_SOURCE_SQLITE3_H
-#define __DATA_SOURCE_SQLITE3_H
-
-#include <string>
-
-#include <exceptions/exceptions.h>
-
-#include <datasrc/data_source.h>
-
-namespace isc {
-
-namespace dns {
-class Name;
-class RRClass;
-class RRType;
-class RRsetList;
-}
-
-namespace datasrc {
-
-class Query;
-struct Sqlite3Parameters;
-
-class Sqlite3Error : public Exception {
-public:
- Sqlite3Error(const char* file, size_t line, const char* what) :
- isc::Exception(file, line, what) {}
-};
-
-class IncompatibleDbVersion : public Exception {
-public:
- IncompatibleDbVersion(const char* file, size_t line, const char* what) :
- isc::Exception(file, line, what) {}
-};
-
-class Sqlite3DataSrc : public DataSrc {
- ///
- /// \name Constructors, Assignment Operator and Destructor.
- ///
- /// Note: The copy constructor and the assignment operator are intentionally
- /// defined as private.
- //@{
-private:
- Sqlite3DataSrc(const Sqlite3DataSrc& source);
- Sqlite3DataSrc& operator=(const Sqlite3DataSrc& source);
-public:
- Sqlite3DataSrc();
- ~Sqlite3DataSrc();
- //@}
-
- void findClosestEnclosure(DataSrcMatch& match) const;
-
- Result findRRset(const isc::dns::Name& qname,
- const isc::dns::RRClass& qclass,
- const isc::dns::RRType& qtype,
- isc::dns::RRsetList& target,
- uint32_t& flags,
- const isc::dns::Name* zonename) const;
-
- Result findExactRRset(const isc::dns::Name& qname,
- const isc::dns::RRClass& qclass,
- const isc::dns::RRType& qtype,
- isc::dns::RRsetList& target,
- uint32_t& flags,
- const isc::dns::Name* zonename) const;
-
- Result findAddrs(const isc::dns::Name& qname,
- const isc::dns::RRClass& qclass,
- isc::dns::RRsetList& target,
- uint32_t& flags,
- const isc::dns::Name* zonename) const;
-
- Result findReferral(const isc::dns::Name& qname,
- const isc::dns::RRClass& qclass,
- isc::dns::RRsetList& target,
- uint32_t& flags,
- const isc::dns::Name* zonename) const;
-
- DataSrc::Result findPreviousName(const isc::dns::Name& qname,
- isc::dns::Name& target,
- const isc::dns::Name* zonename) const;
-
- Result findCoveringNSEC3(const isc::dns::Name& zonename,
- std::string& hash,
- isc::dns::RRsetList& target) const;
-
- Result init() { return (init(isc::data::ElementPtr())); }
- Result init(const isc::data::ConstElementPtr config);
- Result close();
-
-private:
- enum Mode {
- NORMAL,
- ADDRESS,
- DELEGATION
- };
-
- void open(const std::string& name);
- int hasExactZone(const char *name) const;
- int findRecords(const isc::dns::Name& name, const isc::dns::RRType& rdtype,
- isc::dns::RRsetList& target, const isc::dns::Name* zonename,
- const Mode mode, uint32_t& flags) const;
- int findClosest(const isc::dns::Name& name, unsigned int* position) const;
-
-private:
- Sqlite3Parameters* dbparameters;
-};
-
-}
-}
-
-#endif // __DATA_SOURCE_SQLITE3_H
-
-// Local Variables:
-// mode: c++
-// End:
diff --git a/src/lib/datasrc/static_datasrc.cc b/src/lib/datasrc/static_datasrc.cc
deleted file mode 100644
index 77d7a1d..0000000
--- a/src/lib/datasrc/static_datasrc.cc
+++ /dev/null
@@ -1,275 +0,0 @@
-// Copyright (C) 2010 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 <cassert>
-
-#include <dns/name.h>
-#include <dns/rdataclass.h>
-#include <dns/rrclass.h>
-#include <dns/rrset.h>
-#include <dns/rrsetlist.h>
-#include <dns/rrtype.h>
-#include <dns/rrttl.h>
-
-#include <datasrc/data_source.h>
-#include <datasrc/static_datasrc.h>
-#include <datasrc/logger.h>
-
-using namespace std;
-using namespace isc::dns;
-using namespace isc::dns::rdata;
-
-namespace isc {
-namespace datasrc {
-
-// This class stores the "static" data for the built-in static data source.
-// Since it's static, it could be literally static, i.e, defined as static
-// objects. But to avoid the static initialization order fiasco, which would
-// be unlikely to happen for this class in practice but is still possible,
-// we took a safer approach. A downside of this approach is that redundant
-// copies of exactly the same content of these objects can be created.
-// In practice, however, there's normally at most one StaticDataSrc object,
-// so this should be acceptable (if this turns out to be a real concern we
-// might consider making this class a singleton).
-// We use the "pimpl" idiom for this class. Operations for this class is
-// not expected to be performance sensitive, so the overhead due to the pimpl
-// should be okay, and it's more beneficial to hide details and minimize
-// inter module dependencies in header files.
-struct StaticDataSrcImpl {
-public:
- StaticDataSrcImpl();
- const Name authors_name;
- const Name version_name;
- // XXX: unfortunately these can't be ConstRRsetPtr because they'll be
- // passed to RRsetList::addRRset(), which expect non const RRsetPtr.
- // We should revisit this design later.
- RRsetPtr authors;
- RRsetPtr authors_ns;
- RRsetPtr authors_soa;
- RRsetPtr version;
- RRsetPtr version_ns;
- RRsetPtr version_soa;
-};
-
-StaticDataSrcImpl::StaticDataSrcImpl() :
- authors_name("authors.bind"), version_name("version.bind")
-{
- authors = RRsetPtr(new RRset(authors_name, RRClass::CH(),
- RRType::TXT(), RRTTL(0)));
- authors->addRdata(generic::TXT("Chen Zhengzhang")); // Jerry
- authors->addRdata(generic::TXT("Dmitriy Volodin"));
- authors->addRdata(generic::TXT("Evan Hunt"));
- authors->addRdata(generic::TXT("Haidong Wang")); // Ocean
- authors->addRdata(generic::TXT("Haikuo Zhang"));
- authors->addRdata(generic::TXT("Han Feng"));
- authors->addRdata(generic::TXT("Jelte Jansen"));
- authors->addRdata(generic::TXT("Jeremy C. Reed"));
- authors->addRdata(generic::TXT("Xie Jiagui")); // Kevin Tes
- authors->addRdata(generic::TXT("Jin Jian"));
- authors->addRdata(generic::TXT("JINMEI Tatuya"));
- authors->addRdata(generic::TXT("Kazunori Fujiwara"));
- authors->addRdata(generic::TXT("Michael Graff"));
- authors->addRdata(generic::TXT("Michal Vaner"));
- authors->addRdata(generic::TXT("Mukund Sivaraman"));
- authors->addRdata(generic::TXT("Naoki Kambe"));
- authors->addRdata(generic::TXT("Shane Kerr"));
- authors->addRdata(generic::TXT("Shen Tingting"));
- authors->addRdata(generic::TXT("Stephen Morris"));
- authors->addRdata(generic::TXT("Yoshitaka Aharen"));
- authors->addRdata(generic::TXT("Zhang Likun"));
-
- authors_ns = RRsetPtr(new RRset(authors_name, RRClass::CH(),
- RRType::NS(), RRTTL(0)));
- authors_ns->addRdata(generic::NS(authors_name));
-
- authors_soa = RRsetPtr(new RRset(authors_name, RRClass::CH(),
- RRType::SOA(), RRTTL(0)));
- authors_soa->addRdata(generic::SOA(
- "authors.bind. hostmaster.authors.bind. "
- "0 28800 7200 604800 86400"));
-
- version = RRsetPtr(new RRset(version_name, RRClass::CH(),
- RRType::TXT(), RRTTL(0)));
- version->addRdata(generic::TXT(PACKAGE_STRING));
-
- version_ns = RRsetPtr(new RRset(version_name, RRClass::CH(),
- RRType::NS(), RRTTL(0)));
- version_ns->addRdata(generic::NS(version_name));
-
- version_soa = RRsetPtr(new RRset(version_name, RRClass::CH(),
- RRType::SOA(), RRTTL(0)));
- version_soa->addRdata(generic::SOA(
- "version.bind. hostmaster.version.bind. "
- "0 28800 7200 604800 86400"));
-}
-
-StaticDataSrc::StaticDataSrc() {
- LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_STATIC_CREATE);
- setClass(RRClass::CH());
- impl_ = new StaticDataSrcImpl;
-}
-
-StaticDataSrc::~StaticDataSrc() {
- delete impl_;
-}
-
-namespace {
-bool
-isSubdomain(const Name& qname, const Name& zone) {
- const NameComparisonResult::NameRelation cmp =
- qname.compare(zone).getRelation();
- return (cmp == NameComparisonResult::EQUAL ||
- cmp == NameComparisonResult::SUBDOMAIN);
-}
-}
-
-void
-StaticDataSrc::findClosestEnclosure(DataSrcMatch& match) const {
- const Name& qname = match.getName();
-
- if (match.getClass() != getClass() && match.getClass() != RRClass::ANY()) {
- return;
- }
-
- if (isSubdomain(qname, impl_->version_name)) {
- match.update(*this, impl_->version_name);
- return;
- }
-
- if (isSubdomain(qname, impl_->authors_name)) {
- match.update(*this, impl_->authors_name);
- return;
- }
-}
-
-DataSrc::Result
-StaticDataSrc::findRRset(const Name& qname,
- const RRClass& qclass, const RRType& qtype,
- RRsetList& target, uint32_t& flags,
- const Name* const zonename) const
-{
- LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_STATIC_FIND).arg(qname).
- arg(qtype);
- flags = 0;
- if (qclass != getClass() && qclass != RRClass::ANY()) {
- LOG_ERROR(logger, DATASRC_STATIC_CLASS_NOT_CH);
- return (ERROR);
- }
-
- // Identify the appropriate zone.
- bool is_versionname = false, is_authorsname = false;
- if (zonename != NULL) {
- if (*zonename == impl_->version_name &&
- isSubdomain(qname, impl_->version_name)) {
- is_versionname = true;
- } else if (*zonename == impl_->authors_name &&
- isSubdomain(qname, impl_->authors_name)) {
- is_authorsname = true;
- } else {
- flags = NO_SUCH_ZONE;
- return (SUCCESS);
- }
- } else {
- if (isSubdomain(qname, impl_->version_name)) {
- is_versionname = true;
- } else if (isSubdomain(qname, impl_->authors_name)) {
- is_authorsname = true;
- } else {
- flags = NO_SUCH_ZONE;
- return (SUCCESS);
- }
- }
-
- const bool any = (qtype == RRType::ANY());
-
- if (is_versionname) {
- if (qname == impl_->version_name) {
- if (qtype == RRType::TXT() || any) {
- target.addRRset(impl_->version);
- }
- if (qtype == RRType::NS() || any) {
- target.addRRset(impl_->version_ns);
- }
- if (qtype == RRType::SOA() || any) {
- target.addRRset(impl_->version_soa);
- }
- if (target.size() == 0) {
- flags = TYPE_NOT_FOUND;
- }
- } else {
- flags = NAME_NOT_FOUND;
- }
- } else {
- assert(is_authorsname);
- if (qname == impl_->authors_name) {
- if (qtype == RRType::TXT() || any) {
- target.addRRset(impl_->authors);
- }
- if (qtype == RRType::NS() || any) {
- target.addRRset(impl_->authors_ns);
- }
- if (qtype == RRType::SOA() || any) {
- target.addRRset(impl_->authors_soa);
- }
- if (target.size() == 0 ) {
- flags = TYPE_NOT_FOUND;
- }
- } else {
- flags = NAME_NOT_FOUND;
- }
- }
-
- return (SUCCESS);
-}
-
-DataSrc::Result
-StaticDataSrc::findExactRRset(const Name& qname,
- const RRClass& qclass, const RRType& qtype,
- RRsetList& target, uint32_t& flags,
- const Name* zonename) const
-{
- return (findRRset(qname, qclass, qtype, target, flags, zonename));
-}
-
-DataSrc::Result
-StaticDataSrc::findPreviousName(const Name&, Name&, const Name*) const {
- return (NOT_IMPLEMENTED);
-}
-
-DataSrc::Result
-StaticDataSrc::findCoveringNSEC3(const Name&, string&, RRsetList&) const {
- return (NOT_IMPLEMENTED);
-}
-
-DataSrc::Result
-StaticDataSrc::init() {
- return (SUCCESS);
-}
-
-// Static data source is "configuration less", so the \c config parameter
-// is intentionally ignored.
-DataSrc::Result
-StaticDataSrc::init(isc::data::ConstElementPtr) {
- return (init());
-}
-
-DataSrc::Result
-StaticDataSrc::close() {
- return (SUCCESS);
-}
-
-}
-}
diff --git a/src/lib/datasrc/static_datasrc.h b/src/lib/datasrc/static_datasrc.h
deleted file mode 100644
index 4d212fe..0000000
--- a/src/lib/datasrc/static_datasrc.h
+++ /dev/null
@@ -1,95 +0,0 @@
-// 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.
-
-//
-// Sample Datasource implementation; this datasource only returns
-// static content for the queries
-// CH TXT version.bind
-// and
-// CH TXT authors.bind
-//
-
-#ifndef __STATIC_DATA_SOURCE_H
-#define __STATIC_DATA_SOURCE_H
-
-#include <datasrc/data_source.h>
-
-namespace isc {
-
-namespace dns {
-class Name;
-class RRClass;
-class RRType;
-class RRType;
-class RRsetList;
-}
-
-namespace datasrc {
-
-struct StaticDataSrcImpl;
-
-class StaticDataSrc : public DataSrc {
-private:
- ///
- /// \name Constructors, Assignment Operator and Destructor.
- ///
- /// Note: The copy constructor and the assignment operator are intentionally
- /// defined as private.
- //@{
- StaticDataSrc(const StaticDataSrc& source);
- StaticDataSrc& operator=(const StaticDataSrc& source);
-public:
- StaticDataSrc();
- ~StaticDataSrc();
- //@}
-
- void findClosestEnclosure(DataSrcMatch& match) const;
-
- Result findRRset(const isc::dns::Name& qname,
- const isc::dns::RRClass& qclass,
- const isc::dns::RRType& qtype,
- isc::dns::RRsetList& target,
- uint32_t& flags,
- const isc::dns::Name* zonename) const;
-
- Result findExactRRset(const isc::dns::Name& qname,
- const isc::dns::RRClass& qclass,
- const isc::dns::RRType& qtype,
- isc::dns::RRsetList& target,
- uint32_t& flags,
- const isc::dns::Name* zonename) const;
-
- Result findPreviousName(const isc::dns::Name& qname,
- isc::dns::Name& target,
- const isc::dns::Name* zonename) const;
-
- Result findCoveringNSEC3(const isc::dns::Name& zonename,
- std::string& hash,
- isc::dns::RRsetList& target) const;
-
- Result init();
- Result init(isc::data::ConstElementPtr config);
- Result close();
-private:
- StaticDataSrcImpl* impl_;
-};
-
-}
-}
-
-#endif
-
-// Local Variables:
-// mode: c++
-// End:
diff --git a/src/lib/datasrc/tests/Makefile.am b/src/lib/datasrc/tests/Makefile.am
index b73a64a..d2049f1 100644
--- a/src/lib/datasrc/tests/Makefile.am
+++ b/src/lib/datasrc/tests/Makefile.am
@@ -1,4 +1,4 @@
-SUBDIRS = testdata
+SUBDIRS = . memory testdata
AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
AM_CPPFLAGS += -I$(top_builddir)/src/lib/dns -I$(top_srcdir)/src/lib/dns
@@ -47,15 +47,6 @@ common_ldadd += $(GTEST_LDADD) $(SQLITE_LIBS)
# The general tests
run_unittests_SOURCES = $(common_sources)
-# Commented out by ticket #2165. If you re-enable these, please modify
-# EXTRA_DIST at the bottom of this file.
-#run_unittests_SOURCES += datasrc_unittest.cc
-#run_unittests_SOURCES += static_unittest.cc
-#run_unittests_SOURCES += query_unittest.cc
-#run_unittests_SOURCES += cache_unittest.cc
-#run_unittests_SOURCES += sqlite3_unittest.cc
-#run_unittests_SOURCES += test_datasrc.h test_datasrc.cc
-
run_unittests_SOURCES += test_client.h test_client.cc
run_unittests_SOURCES += rbtree_unittest.cc
run_unittests_SOURCES += logger_unittest.cc
@@ -124,11 +115,3 @@ EXTRA_DIST += testdata/new_minor_schema.sqlite3
EXTRA_DIST += testdata/newschema.sqlite3
EXTRA_DIST += testdata/oldschema.sqlite3
EXTRA_DIST += testdata/static.zone
-
-# Added by ticket #2165
-EXTRA_DIST += datasrc_unittest.cc
-EXTRA_DIST += static_unittest.cc
-EXTRA_DIST += query_unittest.cc
-EXTRA_DIST += cache_unittest.cc
-EXTRA_DIST += sqlite3_unittest.cc
-EXTRA_DIST += test_datasrc.h test_datasrc.cc
diff --git a/src/lib/datasrc/tests/cache_unittest.cc b/src/lib/datasrc/tests/cache_unittest.cc
deleted file mode 100644
index 1325f64..0000000
--- a/src/lib/datasrc/tests/cache_unittest.cc
+++ /dev/null
@@ -1,340 +0,0 @@
-// Copyright (C) 2010 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 <stdexcept>
-
-#include <dns/name.h>
-#include <dns/rdata.h>
-#include <dns/rdataclass.h>
-#include <dns/rrclass.h>
-#include <dns/rrtype.h>
-#include <dns/rrttl.h>
-#include <dns/rrset.h>
-
-#include <datasrc/cache.h>
-#include <datasrc/data_source.h>
-
-#include <gtest/gtest.h>
-
-using namespace std;
-using namespace isc::dns;
-using namespace isc::dns::rdata;
-using namespace isc::datasrc;
-
-namespace {
-class CacheTest : public ::testing::Test {
-protected:
- CacheTest() : test_name("test.example.com"),
- test_nsname("ns.example.com"),
- test_ch("example.com")
- {
- RRsetPtr a(new RRset(test_name, RRClass::IN(), RRType::A(),
- RRTTL(3600)));
- a->addRdata(in::A("192.0.2.1"));
- a->addRdata(in::A("192.0.2.2"));
-
- RRsetPtr b(new RRset(test_nsname, RRClass::IN(), RRType::NS(),
- RRTTL(86400)));
- RRsetPtr c(new RRset(test_ch, RRClass::CH(), RRType::TXT(),
- RRTTL(0)));
- c->addRdata(generic::TXT("Text record"));
-
- cache.setSlots(5);
-
- cache.addPositive(a, 1, 30);
- cache.addPositive(b, 2, 30);
- cache.addPositive(c, 4, 30);
- }
-
- Name test_name;
- Name test_nsname;
- Name test_ch;
-
- HotCache cache;
-};
-
-class TestRRset : public RRset {
-public:
- TestRRset(const Name& name, int& counter) :
- RRset(name, RRClass::IN(), RRType::A(), RRTTL(3600)),
- counter_(counter)
- {
- ++counter_;
- }
- ~TestRRset() {
- --counter_;
- }
- int& counter_;
-};
-
-// make sure any remaining cache entries are purged on destruction of the
-// cache.
-TEST_F(CacheTest, cleanup) {
- HotCache* local_cache(new HotCache);
- int num_rrsets = 0;
-
- local_cache->addPositive(RRsetPtr(new TestRRset(Name("example.com"),
- num_rrsets)), 0, 10);
- local_cache->addPositive(RRsetPtr(new TestRRset(Name("example.org"),
- num_rrsets)), 0, 10);
-
- EXPECT_EQ(2, num_rrsets);
- delete local_cache;
- EXPECT_EQ(0, num_rrsets);
-}
-
-TEST_F(CacheTest, slots) {
- EXPECT_EQ(5, cache.getSlots());
- EXPECT_EQ(3, cache.getCount());
-}
-
-TEST_F(CacheTest, insert) {
- RRsetPtr aaaa(new RRset(Name("foo"), RRClass::IN(), RRType::AAAA(),
- RRTTL(0)));
- aaaa->addRdata(in::AAAA("2001:db8:3:bb::5"));
- cache.addPositive(aaaa, 0, 4);
-
- EXPECT_EQ(4, cache.getCount());
-
- RRsetPtr r;
- uint32_t f;
- bool hit = cache.retrieve(Name("foo"), RRClass::IN(),
- RRType::AAAA(), r, f);
- EXPECT_TRUE(hit);
- EXPECT_EQ(aaaa, r);
-}
-
-TEST_F(CacheTest, retrieveOK) {
- bool hit;
- RRsetPtr r;
- uint32_t f;
-
- // Test repeatedly to ensure that all records remain accessible
- // even after being promoted to the top of the cache
- hit = cache.retrieve(test_name, RRClass::IN(), RRType::A(), r, f);
- EXPECT_TRUE(hit);
- EXPECT_EQ(test_name, r->getName());
- EXPECT_EQ(RRClass::IN(), r->getClass());
- EXPECT_EQ(RRType::A(), r->getType());
-
- hit = cache.retrieve(test_nsname, RRClass::IN(), RRType::NS(), r, f);
- EXPECT_TRUE(hit);
- EXPECT_EQ(test_nsname, r->getName());
- EXPECT_EQ(RRClass::IN(), r->getClass());
- EXPECT_EQ(RRType::NS(), r->getType());
-
- hit = cache.retrieve(test_ch, RRClass::CH(), RRType::TXT(), r, f);
- EXPECT_TRUE(hit);
- EXPECT_EQ(test_ch, r->getName());
- EXPECT_EQ(RRClass::CH(), r->getClass());
- EXPECT_EQ(RRType::TXT(), r->getType());
-
- hit = cache.retrieve(test_nsname, RRClass::IN(), RRType::NS(), r, f);
- EXPECT_TRUE(hit);
- EXPECT_EQ(test_nsname, r->getName());
- EXPECT_EQ(RRClass::IN(), r->getClass());
- EXPECT_EQ(RRType::NS(), r->getType());
-
- hit = cache.retrieve(test_ch, RRClass::CH(), RRType::TXT(), r, f);
- EXPECT_TRUE(hit);
- EXPECT_EQ(test_ch, r->getName());
- EXPECT_EQ(RRClass::CH(), r->getClass());
- EXPECT_EQ(RRType::TXT(), r->getType());
-
- hit = cache.retrieve(test_name, RRClass::IN(), RRType::A(), r, f);
- EXPECT_TRUE(hit);
- EXPECT_EQ(test_name, r->getName());
- EXPECT_EQ(RRClass::IN(), r->getClass());
- EXPECT_EQ(RRType::A(), r->getType());
-
- hit = cache.retrieve(test_name, RRClass::IN(), RRType::A(), r, f);
- EXPECT_TRUE(hit);
- EXPECT_EQ(test_name, r->getName());
- EXPECT_EQ(RRClass::IN(), r->getClass());
- EXPECT_EQ(RRType::A(), r->getType());
-};
-
-TEST_F(CacheTest, flags) {
- bool hit;
- RRsetPtr r;
- uint32_t f;
-
- hit = cache.retrieve(test_name, RRClass::IN(), RRType::A(), r, f);
- EXPECT_TRUE(hit);
- EXPECT_TRUE(r);
- EXPECT_EQ(DataSrc::REFERRAL, f);
-
- hit = cache.retrieve(test_nsname, RRClass::IN(), RRType::NS(), r, f);
- EXPECT_TRUE(hit);
- EXPECT_TRUE(r);
- EXPECT_EQ(DataSrc::CNAME_FOUND, f);
-
- hit = cache.retrieve(test_ch, RRClass::CH(), RRType::TXT(), r, f);
- EXPECT_TRUE(hit);
- EXPECT_TRUE(r);
- EXPECT_EQ(DataSrc::NAME_NOT_FOUND, f);
-}
-
-TEST_F(CacheTest, retrieveFail) {
- bool hit;
- RRsetPtr r;
- uint32_t f;
-
- hit = cache.retrieve(Name("fake"), RRClass::IN(), RRType::A(), r, f);
- EXPECT_FALSE(hit);
-
- hit = cache.retrieve(test_name, RRClass::CH(), RRType::A(), r, f);
- EXPECT_FALSE(hit);
-
- hit = cache.retrieve(test_name, RRClass::IN(), RRType::DNSKEY(), r, f);
- EXPECT_FALSE(hit);
-}
-
-TEST_F(CacheTest, expire) {
- // Insert "foo" with a duration of 1 seconds; sleep 2. The
- // record should not be returned from the cache even though it's
- // at the top of the cache.
- RRsetPtr aaaa(new RRset(Name("foo"), RRClass::IN(), RRType::AAAA(),
- RRTTL(0)));
- aaaa->addRdata(in::AAAA("2001:db8:3:bb::5"));
- cache.addPositive(aaaa, 0, 1);
-
- sleep(2);
-
- RRsetPtr r;
- uint32_t f;
- bool hit = cache.retrieve(Name("foo"), RRClass::IN(), RRType::AAAA(), r, f);
- EXPECT_FALSE(hit);
-};
-
-TEST_F(CacheTest, LRU) {
- // Retrieve a record, cache four new records; with five slots
- // in the LRU queue this should remove all the previous records
- // except the last one retreived.
- bool hit;
- RRsetPtr r;
- uint32_t f;
-
- hit = cache.retrieve(test_nsname, RRClass::IN(), RRType::NS(), r, f);
- EXPECT_TRUE(hit);
- EXPECT_EQ(3, cache.getCount());
-
- RRsetPtr one(new RRset(Name("one"), RRClass::IN(), RRType::TXT(),
- RRTTL(0)));
- one->addRdata(generic::TXT("one"));
- cache.addPositive(one, 0, 30);
- EXPECT_EQ(4, cache.getCount());
-
- RRsetPtr two(new RRset(Name("two"), RRClass::IN(), RRType::TXT(),
- RRTTL(0)));
- two->addRdata(generic::TXT("two"));
- cache.addPositive(two, 0, 30);
- EXPECT_EQ(5, cache.getCount());
-
- RRsetPtr three(new RRset(Name("three"), RRClass::IN(), RRType::TXT(),
- RRTTL(0)));
- three->addRdata(generic::TXT("three"));
- cache.addPositive(three, 0, 30);
- EXPECT_EQ(5, cache.getCount());
-
- RRsetPtr four(new RRset(Name("four"), RRClass::IN(), RRType::TXT(),
- RRTTL(0)));
- four->addRdata(generic::TXT("four"));
- cache.addPositive(four, 0, 30);
- EXPECT_EQ(5, cache.getCount());
-
- hit = cache.retrieve(test_name, RRClass::IN(), RRType::A(), r, f);
- EXPECT_FALSE(hit);
-
- hit = cache.retrieve(test_nsname, RRClass::IN(), RRType::NS(), r, f);
- EXPECT_TRUE(hit);
-
- hit = cache.retrieve(test_ch, RRClass::CH(), RRType::TXT(), r, f);
- EXPECT_FALSE(hit);
-}
-
-TEST_F(CacheTest, ncache) {
- Name missing("missing.example.com");
- cache.addNegative(missing, RRClass::IN(), RRType::DNSKEY(), 8, 30);
-
- RRsetPtr r;
- uint32_t f;
- bool hit = cache.retrieve(missing, RRClass::IN(), RRType::DNSKEY(), r, f);
-
- EXPECT_TRUE(hit);
- EXPECT_FALSE(r);
- EXPECT_EQ(DataSrc::TYPE_NOT_FOUND, f);
-}
-
-TEST_F(CacheTest, overwrite) {
- EXPECT_EQ(3, cache.getCount());
-
- RRsetPtr a(new RRset(test_name, RRClass::IN(), RRType::A(), RRTTL(300)));
- a->addRdata(in::A("192.0.2.100"));
-
- EXPECT_NO_THROW(cache.addPositive(a, 16, 30));
- EXPECT_EQ(3, cache.getCount());
-
- RRsetPtr r;
- uint32_t f;
- bool hit = cache.retrieve(test_name, RRClass::IN(), RRType::A(), r, f);
- EXPECT_TRUE(hit);
- EXPECT_TRUE(r);
- EXPECT_EQ(16, f);
-
- EXPECT_NO_THROW(cache.addNegative(test_name, RRClass::IN(), RRType::A(), 1, 30));
- EXPECT_EQ(3, cache.getCount());
-
- hit = cache.retrieve(test_name, RRClass::IN(), RRType::A(), r, f);
- EXPECT_TRUE(hit);
- EXPECT_FALSE(r);
- EXPECT_EQ(DataSrc::REFERRAL, f);
-}
-
-TEST_F(CacheTest, reduceSlots) {
- EXPECT_EQ(3, cache.getCount());
- cache.setSlots(2);
- EXPECT_EQ(2, cache.getCount());
- cache.setSlots(1);
- EXPECT_EQ(1, cache.getCount());
- cache.setSlots(0);
- EXPECT_EQ(1, cache.getCount());
-}
-
-TEST_F(CacheTest, setEnabled) {
- cache.setEnabled(false);
- EXPECT_FALSE(cache.getEnabled());
- cache.setEnabled(true);
- EXPECT_TRUE(cache.getEnabled());
-}
-
-TEST_F(CacheTest, disabled) {
- bool hit;
- RRsetPtr r;
- uint32_t f;
-
- cache.setEnabled(false);
- hit = cache.retrieve(test_name, RRClass::IN(), RRType::A(), r, f);
- EXPECT_FALSE(hit);
-
- cache.setEnabled(true);
- hit = cache.retrieve(test_name, RRClass::IN(), RRType::A(), r, f);
- EXPECT_TRUE(hit);
-
- EXPECT_EQ(test_name, r->getName());
- EXPECT_EQ(RRClass::IN(), r->getClass());
- EXPECT_EQ(RRType::A(), r->getType());
-}
-
-}
diff --git a/src/lib/datasrc/tests/client_list_unittest.cc b/src/lib/datasrc/tests/client_list_unittest.cc
index 3ca906b..d995d5c 100644
--- a/src/lib/datasrc/tests/client_list_unittest.cc
+++ b/src/lib/datasrc/tests/client_list_unittest.cc
@@ -12,11 +12,14 @@
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
+#include <util/memory_segment_local.h>
+
#include <datasrc/client_list.h>
#include <datasrc/client.h>
#include <datasrc/iterator.h>
#include <datasrc/data_source.h>
-#include <datasrc/memory_datasrc.h>
+#include <datasrc/memory/memory_client.h>
+#include <datasrc/memory/zone_finder.h>
#include <dns/rrclass.h>
#include <dns/rrttl.h>
@@ -28,6 +31,8 @@
#include <fstream>
using namespace isc::datasrc;
+using isc::datasrc::memory::InMemoryClient;
+using isc::datasrc::memory::InMemoryZoneFinder;
using namespace isc::data;
using namespace isc::dns;
using namespace boost;
@@ -67,35 +72,47 @@ public:
};
class Iterator : public ZoneIterator {
public:
- Iterator(const Name& origin) :
+ Iterator(const Name& origin, bool include_ns) :
origin_(origin),
- finished_(false),
- soa_(new RRset(origin_, RRClass::IN(), RRType::SOA(), RRTTL(3600)))
+ soa_(new RRset(origin_, RRClass::IN(), RRType::SOA(),
+ RRTTL(3600)))
{
// The RData here is bogus, but it is not used to anything. There
// just needs to be some.
soa_->addRdata(rdata::generic::SOA(Name::ROOT_NAME(),
Name::ROOT_NAME(),
0, 0, 0, 0, 0));
+ rrsets_.push_back(soa_);
+
+ if (include_ns) {
+ ns_.reset(new RRset(origin_, RRClass::IN(), RRType::NS(),
+ RRTTL(3600)));
+ ns_->addRdata(rdata::generic::NS(Name::ROOT_NAME()));
+ rrsets_.push_back(ns_);
+ }
+ rrsets_.push_back(ConstRRsetPtr());
+
+ it_ = rrsets_.begin();
}
virtual isc::dns::ConstRRsetPtr getNextRRset() {
- if (finished_) {
- return (ConstRRsetPtr());
- } else {
- finished_ = true;
- return (soa_);
- }
+ ConstRRsetPtr result = *it_;
+ ++it_;
+ return (result);
}
virtual isc::dns::ConstRRsetPtr getSOA() const {
return (soa_);
}
private:
const Name origin_;
- bool finished_;
- const isc::dns::RRsetPtr soa_;
+ const RRsetPtr soa_;
+ RRsetPtr ns_;
+ std::vector<ConstRRsetPtr> rrsets_;
+ std::vector<ConstRRsetPtr>::const_iterator it_;
};
// Constructor from a list of zones.
- MockDataSourceClient(const char* zone_names[]) {
+ MockDataSourceClient(const char* zone_names[]) :
+ have_ns_(true), use_baditerator_(true)
+ {
for (const char** zone(zone_names); *zone; ++zone) {
zones.insert(Name(*zone));
}
@@ -105,7 +122,8 @@ public:
MockDataSourceClient(const string& type,
const ConstElementPtr& configuration) :
type_(type),
- configuration_(configuration)
+ configuration_(configuration),
+ have_ns_(true), use_baditerator_(true)
{
EXPECT_NE("MasterFiles", type) << "MasterFiles is a special case "
"and it never should be created as a data source client";
@@ -146,23 +164,27 @@ public:
isc_throw(isc::NotImplemented, "Not implemented");
}
virtual ZoneIteratorPtr getIterator(const Name& name, bool) const {
- if (name == Name("noiter.org")) {
+ if (use_baditerator_ && name == Name("noiter.org")) {
isc_throw(isc::NotImplemented, "Asked not to be implemented");
- } else if (name == Name("null.org")) {
+ } else if (use_baditerator_ && name == Name("null.org")) {
return (ZoneIteratorPtr());
} else {
FindResult result(findZone(name));
if (result.code == isc::datasrc::result::SUCCESS) {
- return (ZoneIteratorPtr(new Iterator(name)));
+ return (ZoneIteratorPtr(new Iterator(name, have_ns_)));
} else {
isc_throw(DataSourceError, "No such zone");
}
}
}
+ void disableNS() { have_ns_ = false; }
+ void disableBadIterator() { use_baditerator_ = false; }
const string type_;
const ConstElementPtr configuration_;
private:
set<Name> zones;
+ bool have_ns_; // control the iterator behavior wrt whether to include NS
+ bool use_baditerator_; // whether to use bogus zone iterators for tests
};
@@ -220,8 +242,9 @@ const size_t ds_count = (sizeof(ds_zones) / sizeof(*ds_zones));
class ListTest : public ::testing::Test {
public:
ListTest() :
+ rrclass_(RRClass::IN()),
// The empty list corresponds to a list with no elements inside
- list_(new TestedList(RRClass::IN())),
+ list_(new TestedList(rrclass_)),
config_elem_(Element::fromJSON("["
"{"
" \"type\": \"test_type\","
@@ -238,28 +261,35 @@ public:
shared_ptr<MockDataSourceClient>
ds(new MockDataSourceClient(ds_zones[i]));
ds_.push_back(ds);
- ds_info_.push_back(ConfigurableClientList::DataSourceInfo(ds.get(),
- DataSourceClientContainerPtr(), false));
+ ds_info_.push_back(ConfigurableClientList::DataSourceInfo(
+ ds.get(), DataSourceClientContainerPtr(),
+ false, rrclass_, mem_sgmt_));
}
}
- void prepareCache(size_t index, const Name& zone, bool prefill = false) {
- const shared_ptr<InMemoryClient> cache(new InMemoryClient());
- const shared_ptr<InMemoryZoneFinder>
- finder(new InMemoryZoneFinder(RRClass::IN(), zone));
- if (prefill) {
- RRsetPtr soa(new RRset(zone, RRClass::IN(), RRType::SOA(),
- RRTTL(3600)));
- // The RData here is bogus, but it is not used to anything. There
- // just needs to be some.
- soa->addRdata(rdata::generic::SOA(Name::ROOT_NAME(),
- Name::ROOT_NAME(),
- 0, 0, 0, 0, 0));
- finder->add(soa);
- }
- // If we don't do prefill, we leave the zone empty. This way,
- // we can check when it was reloaded.
- cache->addZone(finder);
- list_->getDataSources()[index].cache_ = cache;
+
+ // Install a "fake" cached zone using a temporary underlying data source
+ // client.
+ void prepareCache(size_t index, const Name& zone) {
+ // Prepare the temporary data source client
+ const char* zones[2];
+ const std::string zonename_txt = zone.toText();
+ zones[0] = zonename_txt.c_str();
+ zones[1] = NULL;
+ MockDataSourceClient mock_client(zones);
+ // Disable some default features of the mock to distinguish the
+ // temporary case from normal case.
+ mock_client.disableNS();
+ mock_client.disableBadIterator();
+
+ // Create cache from the temporary data source, and push it to the
+ // client list.
+ const shared_ptr<InMemoryClient> cache(new InMemoryClient(mem_sgmt_,
+ rrclass_));
+ cache->load(zone, *mock_client.getIterator(zone, false));
+
+ ConfigurableClientList::DataSourceInfo& dsrc_info =
+ list_->getDataSources()[index];
+ dsrc_info.cache_ = cache;
}
// Check the positive result is as we expect it.
void positiveResult(const ClientList::FindResult& result,
@@ -331,6 +361,8 @@ public:
EXPECT_EQ(cache, list_->getDataSources()[index].cache_ !=
shared_ptr<InMemoryClient>());
}
+ const RRClass rrclass_;
+ isc::util::MemorySegmentLocal mem_sgmt_;
shared_ptr<TestedList> list_;
const ClientList::FindResult negative_result_;
vector<shared_ptr<MockDataSourceClient> > ds_;
@@ -815,39 +847,38 @@ TEST_F(ListTest, BadMasterFile) {
// Test we can reload a zone
TEST_F(ListTest, reloadSuccess) {
list_->configure(config_elem_zones_, true);
- Name name("example.org");
+ const Name name("example.org");
prepareCache(0, name);
- // Not there yet. It would be NXDOMAIN, but it is in apex and
- // it returns NXRRSET instead.
+ // The cache currently contains a tweaked version of zone, which doesn't
+ // have apex NS. So the lookup should result in NXRRSET.
EXPECT_EQ(ZoneFinder::NXRRSET,
- list_->find(name).finder_->find(name, RRType::SOA())->code);
- // Now reload. It should be there now.
+ list_->find(name).finder_->find(name, RRType::NS())->code);
+ // Now reload the full zone. It should be there now.
EXPECT_EQ(ConfigurableClientList::ZONE_RELOADED, list_->reload(name));
EXPECT_EQ(ZoneFinder::SUCCESS,
- list_->find(name).finder_->find(name, RRType::SOA())->code);
+ list_->find(name).finder_->find(name, RRType::NS())->code);
}
// The cache is not enabled. The load should be rejected.
TEST_F(ListTest, reloadNotEnabled) {
list_->configure(config_elem_zones_, false);
- Name name("example.org");
+ const Name name("example.org");
// We put the cache in even when not enabled. This won't confuse the thing.
prepareCache(0, name);
- // Not there yet. It would be NXDOMAIN, but it is in apex and
- // it returns NXRRSET instead.
+ // See the reloadSuccess test. This should result in NXRRSET.
EXPECT_EQ(ZoneFinder::NXRRSET,
- list_->find(name).finder_->find(name, RRType::SOA())->code);
+ list_->find(name).finder_->find(name, RRType::NS())->code);
// Now reload. It should reject it.
EXPECT_EQ(ConfigurableClientList::CACHE_DISABLED, list_->reload(name));
// Nothing changed here
EXPECT_EQ(ZoneFinder::NXRRSET,
- list_->find(name).finder_->find(name, RRType::SOA())->code);
+ list_->find(name).finder_->find(name, RRType::NS())->code);
}
// Test several cases when the zone does not exist
TEST_F(ListTest, reloadNoSuchZone) {
list_->configure(config_elem_zones_, true);
- Name name("example.org");
+ const Name name("example.org");
// We put the cache in even when not enabled. This won't confuse the
// reload method, as that one looks at the real state of things, not
// at the configuration.
@@ -867,27 +898,27 @@ TEST_F(ListTest, reloadNoSuchZone) {
list_->find(Name("example.cz")).dsrc_client_);
EXPECT_EQ(static_cast<isc::datasrc::DataSourceClient*>(NULL),
list_->find(Name("sub.example.com"), true).dsrc_client_);
- // Not reloaded
+ // Not reloaded, so NS shouldn't be visible yet.
EXPECT_EQ(ZoneFinder::NXRRSET,
list_->find(Name("example.com")).finder_->
- find(Name("example.com"), RRType::SOA())->code);
+ find(Name("example.com"), RRType::NS())->code);
}
// Check we gracefuly throw an exception when a zone disappeared in
// the underlying data source when we want to reload it
TEST_F(ListTest, reloadZoneGone) {
list_->configure(config_elem_, true);
- Name name("example.org");
+ const Name name("example.org");
// We put in a cache for non-existant zone. This emulates being loaded
// and then the zone disappearing. We prefill the cache, so we can check
// it.
- prepareCache(0, name, true);
- // The zone contains something
+ prepareCache(0, name);
+ // The (cached) zone contains zone's SOA
EXPECT_EQ(ZoneFinder::SUCCESS,
list_->find(name).finder_->find(name, RRType::SOA())->code);
// The zone is not there, so abort the reload.
EXPECT_THROW(list_->reload(name), DataSourceError);
- // The zone is not hurt.
+ // The (cached) zone is not hurt.
EXPECT_EQ(ZoneFinder::SUCCESS,
list_->find(name).finder_->find(name, RRType::SOA())->code);
}
@@ -895,8 +926,8 @@ TEST_F(ListTest, reloadZoneGone) {
// The underlying data source throws. Check we don't modify the state.
TEST_F(ListTest, reloadZoneThrow) {
list_->configure(config_elem_zones_, true);
- Name name("noiter.org");
- prepareCache(0, name, true);
+ const Name name("noiter.org");
+ prepareCache(0, name);
// The zone contains stuff now
EXPECT_EQ(ZoneFinder::SUCCESS,
list_->find(name).finder_->find(name, RRType::SOA())->code);
@@ -909,8 +940,8 @@ TEST_F(ListTest, reloadZoneThrow) {
TEST_F(ListTest, reloadNullIterator) {
list_->configure(config_elem_zones_, true);
- Name name("null.org");
- prepareCache(0, name, true);
+ const Name name("null.org");
+ prepareCache(0, name);
// The zone contains stuff now
EXPECT_EQ(ZoneFinder::SUCCESS,
list_->find(name).finder_->find(name, RRType::SOA())->code);
diff --git a/src/lib/datasrc/tests/datasrc_unittest.cc b/src/lib/datasrc/tests/datasrc_unittest.cc
deleted file mode 100644
index 36bed1d..0000000
--- a/src/lib/datasrc/tests/datasrc_unittest.cc
+++ /dev/null
@@ -1,1209 +0,0 @@
-// Copyright (C) 2010 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 <stdint.h>
-
-#include <iostream>
-#include <vector>
-#include <string>
-
-#include <gtest/gtest.h>
-
-#include <util/buffer.h>
-
-#include <dns/message.h>
-#include <dns/messagerenderer.h>
-#include <dns/question.h>
-#include <dns/opcode.h>
-#include <dns/rcode.h>
-#include <dns/rdata.h>
-#include <dns/rdataclass.h>
-#include <dns/rrclass.h>
-#include <dns/rrttl.h>
-#include <dns/rrtype.h>
-
-#include <cc/data.h>
-
-#include <datasrc/query.h>
-#include <datasrc/sqlite3_datasrc.h>
-#include <datasrc/static_datasrc.h>
-
-#include <testutils/dnsmessage_test.h>
-#include <dns/tests/unittest_util.h>
-#include <datasrc/tests/test_datasrc.h>
-
-using isc::UnitTestUtil;
-using namespace std;
-using namespace isc::util;
-using namespace isc::dns;
-using namespace isc::dns::rdata;
-using namespace isc::datasrc;
-using namespace isc::data;
-using namespace isc::testutils;
-
-namespace {
-ConstElementPtr SQLITE_DBFILE_EXAMPLE = Element::fromJSON(
- "{ \"database_file\": \"" TEST_DATA_DIR "/example.org.sqlite3\"}");
-
-class DataSrcTest : public ::testing::Test {
-protected:
- DataSrcTest() : msg(Message::PARSE),
- opcodeval(Opcode::QUERY().getCode()), qid(0)
- {
- DataSrcPtr sql3_source = DataSrcPtr(new Sqlite3DataSrc);
- sql3_source->init(SQLITE_DBFILE_EXAMPLE);
- DataSrcPtr test_source = DataSrcPtr(new TestDataSrc);
- test_source->init();
- DataSrcPtr static_source = DataSrcPtr(new StaticDataSrc);
- meta_source.addDataSrc(test_source);
- meta_source.addDataSrc(sql3_source);
- meta_source.addDataSrc(static_source);
- }
- void QueryCommon(const RRClass& qclass);
- void createAndProcessQuery(const Name& qname, const RRClass& qclass,
- const RRType& qtype, bool need_dnssec);
-
- HotCache cache;
- MetaDataSrc meta_source;
- MessageRenderer renderer;
- Message msg;
- const uint16_t opcodeval;
- qid_t qid;
-};
-
-void
-performQuery(DataSrc& data_source, HotCache& cache, Message& message,
- bool need_dnssec = true)
-{
- message.setHeaderFlag(Message::HEADERFLAG_AA);
- message.setRcode(Rcode::NOERROR());
- Query q(message, cache, need_dnssec);
- data_source.doQuery(q);
-}
-
-void
-DataSrcTest::createAndProcessQuery(const Name& qname, const RRClass& qclass,
- const RRType& qtype,
- bool need_dnssec = true)
-{
- msg.makeResponse();
- msg.setOpcode(Opcode::QUERY());
- msg.addQuestion(Question(qname, qclass, qtype));
- msg.setHeaderFlag(Message::HEADERFLAG_RD);
- qid = msg.getQid();
- performQuery(meta_source, cache, msg, need_dnssec);
-}
-
-void
-DataSrcTest::QueryCommon(const RRClass& qclass) {
- createAndProcessQuery(Name("www.example.com"), qclass, RRType::A());
-
- headerCheck(msg, qid, Rcode::NOERROR(), opcodeval,
- QR_FLAG | AA_FLAG | RD_FLAG, 1, 2, 4, 6);
-
- RRsetIterator rit = msg.beginSection(Message::SECTION_ANSWER);
- RRsetPtr rrset = *rit;
- EXPECT_EQ(Name("www.example.com"), rrset->getName());
- EXPECT_EQ(RRType::A(), rrset->getType());
- EXPECT_EQ(RRClass::IN(), rrset->getClass());
-
- RdataIteratorPtr it = rrset->getRdataIterator();
- EXPECT_EQ("192.0.2.1", it->getCurrent().toText());
- it->next();
- EXPECT_TRUE(it->isLast());
-
- // XXX: also check ANSWER RRSIG
-
- rit = msg.beginSection(Message::SECTION_AUTHORITY);
- rrset = *rit;
- EXPECT_EQ(Name("example.com"), rrset->getName());
- EXPECT_EQ(RRType::NS(), rrset->getType());
- EXPECT_EQ(RRClass::IN(), rrset->getClass());
-
- it = rrset->getRdataIterator();
- EXPECT_EQ("dns01.example.com.", it->getCurrent().toText());
- it->next();
- EXPECT_EQ("dns02.example.com.", it->getCurrent().toText());
- it->next();
- EXPECT_EQ("dns03.example.com.", it->getCurrent().toText());
- it->next();
- EXPECT_TRUE(it->isLast());
-
- rit = msg.beginSection(Message::SECTION_ADDITIONAL);
- rrset = *rit;
- EXPECT_EQ(Name("dns01.example.com"), rrset->getName());
- EXPECT_EQ(RRType::A(), rrset->getType());
- EXPECT_EQ(RRClass::IN(), rrset->getClass());
-
- it = rrset->getRdataIterator();
- EXPECT_EQ("192.0.2.1", it->getCurrent().toText());
- it->next();
- EXPECT_TRUE(it->isLast());
-}
-
-TEST_F(DataSrcTest, Query) {
- QueryCommon(RRClass::IN());
-}
-
-// Query class doesn't match any of the data source classes. The result
-// should be the same as "NxZone".
-TEST_F(DataSrcTest, QueryClassMismatch) {
- createAndProcessQuery(Name("www.example.com"), RRClass::CH(), RRType::A());
- headerCheck(msg, qid, Rcode::REFUSED(), opcodeval, QR_FLAG | RD_FLAG,
- 1, 0, 0, 0);
-}
-
-// Query class of any should match the first data source.
-TEST_F(DataSrcTest, QueryClassAny) {
- QueryCommon(RRClass::ANY());
-}
-
-TEST_F(DataSrcTest, queryClassAnyNegative) {
- // There was a bug where Class ANY query triggered a crash due to NULL
- // pointer dereference. This test checks that condition.
-
- // NXDOMAIN case
- createAndProcessQuery(Name("notexistent.example.com"), RRClass::ANY(),
- RRType::A());
- headerCheck(msg, qid, Rcode::NXDOMAIN(), opcodeval,
- QR_FLAG | AA_FLAG | RD_FLAG, 1, 0, 6, 0);
-
- // NXRRSET case
- msg.clear(Message::PARSE);
- createAndProcessQuery(Name("www.example.com"), RRClass::ANY(),
- RRType::TXT());
- headerCheck(msg, qid, Rcode::NOERROR(), opcodeval,
- QR_FLAG | AA_FLAG | RD_FLAG, 1, 0, 4, 0);
-}
-
-TEST_F(DataSrcTest, queryClassAnyDNAME) {
- // Class ANY query that would match a DNAME. Everything including the
- // synthesized CNAME should be the same as the response to class IN query.
- createAndProcessQuery(Name("www.dname.example.com"), RRClass::ANY(),
- RRType::A(), false);
- headerCheck(msg, qid, Rcode::NOERROR(), opcodeval,
- QR_FLAG | AA_FLAG | RD_FLAG, 1, 3, 3, 3);
- rrsetsCheck("dname.example.com. 3600 IN DNAME sql1.example.com.\n"
- "www.dname.example.com. 3600 IN CNAME www.sql1.example.com.\n"
- "www.sql1.example.com. 3600 IN A 192.0.2.2\n",
- msg.beginSection(Message::SECTION_ANSWER),
- msg.endSection(Message::SECTION_ANSWER));
-
- // Also check the case of explicit DNAME query.
- msg.clear(Message::PARSE);
- createAndProcessQuery(Name("dname.example.com"), RRClass::ANY(),
- RRType::DNAME(), false);
- headerCheck(msg, qid, Rcode::NOERROR(), opcodeval,
- QR_FLAG | AA_FLAG | RD_FLAG, 1, 1, 3, 3);
- rrsetsCheck("dname.example.com. 3600 IN DNAME sql1.example.com.\n",
- msg.beginSection(Message::SECTION_ANSWER),
- msg.endSection(Message::SECTION_ANSWER));
-}
-
-TEST_F(DataSrcTest, queryClassAnyCNAME) {
- // Similar test for CNAME
- createAndProcessQuery(Name("foo.example.com"), RRClass::ANY(),
- RRType::A(), false);
- headerCheck(msg, qid, Rcode::NOERROR(), opcodeval,
- QR_FLAG | AA_FLAG | RD_FLAG, 1, 1, 0, 0);
- rrsetsCheck("foo.example.com. 3600 IN CNAME cnametest.example.net.\n",
- msg.beginSection(Message::SECTION_ANSWER),
- msg.endSection(Message::SECTION_ANSWER));
-}
-
-TEST_F(DataSrcTest, NSQuery) {
- createAndProcessQuery(Name("example.com"), RRClass::IN(),
- RRType::NS());
- headerCheck(msg, qid, Rcode::NOERROR(), opcodeval,
- QR_FLAG | AA_FLAG | RD_FLAG, 1, 4, 0, 6);
-
- RRsetIterator rit = msg.beginSection(Message::SECTION_ANSWER);
- RRsetPtr rrset = *rit;
- EXPECT_EQ(Name("example.com"), rrset->getName());
- EXPECT_EQ(RRType::NS(), rrset->getType());
- EXPECT_EQ(RRClass::IN(), rrset->getClass());
-
- RdataIteratorPtr it = rrset->getRdataIterator();
- EXPECT_EQ("dns01.example.com.", it->getCurrent().toText());
- it->next();
- EXPECT_EQ("dns02.example.com.", it->getCurrent().toText());
- it->next();
- EXPECT_EQ("dns03.example.com.", it->getCurrent().toText());
- it->next();
- EXPECT_TRUE(it->isLast());
-}
-
-// Make sure two successive queries have the same result
-TEST_F(DataSrcTest, DuplicateQuery) {
- createAndProcessQuery(Name("example.com"), RRClass::IN(),
- RRType::NS());
- headerCheck(msg, qid, Rcode::NOERROR(), opcodeval,
- QR_FLAG | AA_FLAG | RD_FLAG, 1, 4, 0, 6);
-
- RRsetIterator rit = msg.beginSection(Message::SECTION_ANSWER);
- RRsetPtr rrset = *rit;
- EXPECT_EQ(Name("example.com"), rrset->getName());
- EXPECT_EQ(RRType::NS(), rrset->getType());
- EXPECT_EQ(RRClass::IN(), rrset->getClass());
-
- RdataIteratorPtr it = rrset->getRdataIterator();
- EXPECT_EQ("dns01.example.com.", it->getCurrent().toText());
- it->next();
- EXPECT_EQ("dns02.example.com.", it->getCurrent().toText());
- it->next();
- EXPECT_EQ("dns03.example.com.", it->getCurrent().toText());
- it->next();
- EXPECT_TRUE(it->isLast());
-
- msg.clear(Message::PARSE);
- createAndProcessQuery(Name("example.com"), RRClass::IN(),
- RRType::NS());
- headerCheck(msg, qid, Rcode::NOERROR(), opcodeval,
- QR_FLAG | AA_FLAG | RD_FLAG, 1, 4, 0, 6);
-
- rit = msg.beginSection(Message::SECTION_ANSWER);
- rrset = *rit;
- EXPECT_EQ(Name("example.com"), rrset->getName());
- EXPECT_EQ(RRType::NS(), rrset->getType());
- EXPECT_EQ(RRClass::IN(), rrset->getClass());
-
- it = rrset->getRdataIterator();
- EXPECT_EQ("dns01.example.com.", it->getCurrent().toText());
- it->next();
- EXPECT_EQ("dns02.example.com.", it->getCurrent().toText());
- it->next();
- EXPECT_EQ("dns03.example.com.", it->getCurrent().toText());
- it->next();
- EXPECT_TRUE(it->isLast());
-}
-
-TEST_F(DataSrcTest, DNSKEYQuery) {
- createAndProcessQuery(Name("example.com"), RRClass::IN(),
- RRType::DNSKEY());
- headerCheck(msg, qid, Rcode::NOERROR(), opcodeval,
- QR_FLAG | AA_FLAG | RD_FLAG, 1, 4, 4, 6);
-
- RRsetIterator rit = msg.beginSection(Message::SECTION_ANSWER);
- RRsetPtr rrset = *rit;
- EXPECT_EQ(Name("example.com"), rrset->getName());
- EXPECT_EQ(RRType::DNSKEY(), rrset->getType());
- EXPECT_EQ(RRClass::IN(), rrset->getClass());
-}
-
-// Repeat the previous query to check that cache is working correctly.
-// We query for a record at a zone cut to ensure the REFERRAL flag doesn't
-// cause incorrect behavior.
-TEST_F(DataSrcTest, DNSKEYDuplicateQuery) {
- createAndProcessQuery(Name("example.com"), RRClass::IN(),
- RRType::DNSKEY());
- headerCheck(msg, qid, Rcode::NOERROR(), opcodeval,
- QR_FLAG | AA_FLAG | RD_FLAG, 1, 4, 4, 6);
-
- RRsetIterator rit = msg.beginSection(Message::SECTION_ANSWER);
- RRsetPtr rrset = *rit;
- EXPECT_EQ(Name("example.com"), rrset->getName());
- EXPECT_EQ(RRType::DNSKEY(), rrset->getType());
- EXPECT_EQ(RRClass::IN(), rrset->getClass());
-
- msg.clear(Message::PARSE);
- createAndProcessQuery(Name("example.com"), RRClass::IN(),
- RRType::DNSKEY());
- rit = msg.beginSection(Message::SECTION_ANSWER);
- rrset = *rit;
- EXPECT_EQ(Name("example.com"), rrset->getName());
- EXPECT_EQ(RRType::DNSKEY(), rrset->getType());
- EXPECT_EQ(RRClass::IN(), rrset->getClass());
-}
-
-TEST_F(DataSrcTest, NxRRset) {
- createAndProcessQuery(Name("example.com"), RRClass::IN(),
- RRType::PTR());
-
- headerCheck(msg, qid, Rcode::NOERROR(), opcodeval,
- QR_FLAG | AA_FLAG | RD_FLAG, 1, 0, 4, 0);
-
- RRsetIterator rit = msg.beginSection(Message::SECTION_AUTHORITY);
- RRsetPtr rrset = *rit;
- EXPECT_EQ(Name("example.com"), rrset->getName());
- EXPECT_EQ(RRType::SOA(), rrset->getType());
-}
-
-TEST_F(DataSrcTest, Nxdomain) {
- createAndProcessQuery(Name("glork.example.com"), RRClass::IN(),
- RRType::A());
-
- headerCheck(msg, qid, Rcode::NXDOMAIN(), opcodeval,
- QR_FLAG | AA_FLAG | RD_FLAG, 1, 0, 6, 0);
-
- RRsetIterator rit = msg.beginSection(Message::SECTION_AUTHORITY);
- RRsetPtr rrset = *rit;
- EXPECT_EQ(Name("example.com"), rrset->getName());
- EXPECT_EQ(RRType::SOA(), rrset->getType());
- EXPECT_EQ(RRClass::IN(), rrset->getClass());
- // XXX: check for other authority section answers
-}
-
-TEST_F(DataSrcTest, NxdomainAfterSOAQuery) {
- // There was a bug where once SOA RR is stored in the hot spot cache
- // subsequent negative search fails due to "missing SOA". This test
- // checks that situation.
-
- // First, run the scenario with disabling the cache.
- cache.setEnabled(false);
- createAndProcessQuery(Name("example.com"), RRClass::IN(),
- RRType::SOA());
- msg.clear(Message::PARSE);
- createAndProcessQuery(Name("notexistent.example.com"), RRClass::IN(),
- RRType::A());
- {
- SCOPED_TRACE("NXDOMAIN after SOA, without hot spot cache");
- headerCheck(msg, qid, Rcode::NXDOMAIN(), opcodeval,
- QR_FLAG | AA_FLAG | RD_FLAG, 1, 0, 6, 0);
- }
-
- // Then enable the cache and perform the same queries. This should
- // produce the same result.
- cache.setEnabled(true);
- msg.clear(Message::PARSE);
- createAndProcessQuery(Name("example.com"), RRClass::IN(),
- RRType::SOA());
- msg.clear(Message::PARSE);
- createAndProcessQuery(Name("notexistent.example.com"), RRClass::IN(),
- RRType::A());
- {
- SCOPED_TRACE("NXDOMAIN after SOA, without hot spot cache");
- headerCheck(msg, qid, Rcode::NXDOMAIN(), opcodeval,
- QR_FLAG | AA_FLAG | RD_FLAG, 1, 0, 6, 0);
- }
-}
-
-TEST_F(DataSrcTest, NxZone) {
- createAndProcessQuery(Name("spork.example"), RRClass::IN(),
- RRType::A());
-
- headerCheck(msg, qid, Rcode::REFUSED(), opcodeval,
- QR_FLAG | RD_FLAG, 1, 0, 0, 0);
-
- EXPECT_EQ(Rcode::REFUSED(), msg.getRcode());
- EXPECT_TRUE(msg.getHeaderFlag(Message::HEADERFLAG_QR));
- EXPECT_FALSE(msg.getHeaderFlag(Message::HEADERFLAG_AA));
- EXPECT_TRUE(msg.getHeaderFlag(Message::HEADERFLAG_RD));
-}
-
-TEST_F(DataSrcTest, Wildcard) {
- createAndProcessQuery(Name("www.wild.example.com"), RRClass::IN(),
- RRType::A());
-
- headerCheck(msg, qid, Rcode::NOERROR(), opcodeval,
- QR_FLAG | AA_FLAG | RD_FLAG, 1, 2, 6, 6);
-
- RRsetIterator rit = msg.beginSection(Message::SECTION_ANSWER);
- RRsetPtr rrset = *rit;
- EXPECT_EQ(Name("www.wild.example.com"), rrset->getName());
- EXPECT_EQ(RRType::A(), rrset->getType());
- EXPECT_EQ(RRClass::IN(), rrset->getClass());
-
- RdataIteratorPtr it = rrset->getRdataIterator();
- EXPECT_EQ("192.0.2.2", it->getCurrent().toText());
- it->next();
- EXPECT_TRUE(it->isLast());
-
- rit = msg.beginSection(Message::SECTION_AUTHORITY);
- rrset = *rit;
- EXPECT_EQ(Name("*.wild.example.com"), rrset->getName());
- EXPECT_EQ(RRType::NSEC(), rrset->getType());
- EXPECT_EQ(RRClass::IN(), rrset->getClass());
- ++rit;
- ++rit;
-
- rrset = *rit;
- EXPECT_EQ(Name("example.com"), rrset->getName());
- EXPECT_EQ(RRType::NS(), rrset->getType());
- EXPECT_EQ(RRClass::IN(), rrset->getClass());
-
- it = rrset->getRdataIterator();
- EXPECT_EQ("dns01.example.com.", it->getCurrent().toText());
- it->next();
- EXPECT_EQ("dns02.example.com.", it->getCurrent().toText());
- it->next();
- EXPECT_EQ("dns03.example.com.", it->getCurrent().toText());
- it->next();
- EXPECT_TRUE(it->isLast());
-
- rit = msg.beginSection(Message::SECTION_ADDITIONAL);
- rrset = *rit;
- EXPECT_EQ(Name("dns01.example.com"), rrset->getName());
- EXPECT_EQ(RRType::A(), rrset->getType());
- EXPECT_EQ(RRClass::IN(), rrset->getClass());
-
- it = rrset->getRdataIterator();
- EXPECT_EQ("192.0.2.1", it->getCurrent().toText());
- it->next();
- EXPECT_TRUE(it->isLast());
-}
-
-TEST_F(DataSrcTest, WildcardNodata) {
- // Check that a query for a data type not covered by the wildcard
- // returns NOERROR
- createAndProcessQuery(Name("www.wild.example.com"), RRClass::IN(),
- RRType::AAAA());
- headerCheck(msg, qid, Rcode::NOERROR(), opcodeval,
- QR_FLAG | AA_FLAG | RD_FLAG, 1, 0, 2, 0);
-}
-
-TEST_F(DataSrcTest, DISABLED_WildcardAgainstMultiLabel) {
- // this qname shouldn't match *.wild.com.com (because * can only match
- // a single label), and it should result in NXDOMAIN.
- createAndProcessQuery(Name("www.xxx.wild.example.com"), RRClass::IN(),
- RRType::A());
- headerCheck(msg, qid, Rcode::NXDOMAIN(), opcodeval,
- QR_FLAG | AA_FLAG | RD_FLAG, 1, 0, 1, 0);
-}
-
-TEST_F(DataSrcTest, WildcardCname) {
- // Check that wildcard answers containing CNAMES are followed
- // correctly. It should result in the same response for both
- // class IN and ANY queries.
- const RRClass classes[2] = { RRClass::IN(), RRClass::ANY() };
-
- for (int i = 0; i < sizeof(classes) / sizeof(classes[0]); ++i) {
- SCOPED_TRACE("Wildcard + CNAME test for class " + classes[i].toText());
-
- msg.clear(Message::PARSE);
-
- createAndProcessQuery(Name("www.wild2.example.com"), classes[i],
- RRType::A(), false);
-
- headerCheck(msg, qid, Rcode::NOERROR(), opcodeval,
- QR_FLAG | AA_FLAG | RD_FLAG, 1, 2, 3, 3);
-
- rrsetsCheck("www.wild2.example.com. 3600 IN CNAME www.example.com\n"
- "www.example.com. 3600 IN A 192.0.2.1\n",
- msg.beginSection(Message::SECTION_ANSWER),
- msg.endSection(Message::SECTION_ANSWER));
- rrsetsCheck("example.com. 3600 IN NS dns01.example.com.\n"
- "example.com. 3600 IN NS dns02.example.com.\n"
- "example.com. 3600 IN NS dns03.example.com.",
- msg.beginSection(Message::SECTION_AUTHORITY),
- msg.endSection(Message::SECTION_AUTHORITY));
- rrsetsCheck("dns01.example.com. 3600 IN A 192.0.2.1\n"
- "dns02.example.com. 3600 IN A 192.0.2.2\n"
- "dns03.example.com. 3600 IN A 192.0.2.3",
- msg.beginSection(Message::SECTION_ADDITIONAL),
- msg.endSection(Message::SECTION_ADDITIONAL));
- }
-}
-
-TEST_F(DataSrcTest, WildcardCnameNodata) {
- // A wildcard containing a CNAME whose target does not include
- // data of this type.
- createAndProcessQuery(Name("www.wild2.example.com"), RRClass::IN(),
- RRType::AAAA());
- headerCheck(msg, qid, Rcode::NOERROR(), opcodeval,
- QR_FLAG | AA_FLAG | RD_FLAG, 1, 2, 4, 0);
-
- RRsetIterator rit = msg.beginSection(Message::SECTION_ANSWER);
- RRsetPtr rrset = *rit;
- EXPECT_EQ(Name("www.wild2.example.com"), rrset->getName());
- EXPECT_EQ(RRType::CNAME(), rrset->getType());
- EXPECT_EQ(RRClass::IN(), rrset->getClass());
-
- RdataIteratorPtr it = rrset->getRdataIterator();
- EXPECT_EQ("www.example.com.", it->getCurrent().toText());
- it->next();
- EXPECT_TRUE(it->isLast());
-
- rit = msg.beginSection(Message::SECTION_AUTHORITY);
- rrset = *rit;
- EXPECT_EQ(Name("*.wild2.example.com"), rrset->getName());
- EXPECT_EQ(RRType::NSEC(), rrset->getType());
- EXPECT_EQ(RRClass::IN(), rrset->getClass());
- ++rit;
- ++rit;
-
- rrset = *rit;
- EXPECT_EQ(Name("www.example.com"), rrset->getName());
- EXPECT_EQ(RRType::NSEC(), rrset->getType());
- EXPECT_EQ(RRClass::IN(), rrset->getClass());
-}
-
-TEST_F(DataSrcTest, WildcardCnameNxdomain) {
- // A wildcard containing a CNAME whose target does not exist
- createAndProcessQuery(Name("www.wild3.example.com"), RRClass::IN(),
- RRType::A());
- headerCheck(msg, qid, Rcode::NOERROR(), opcodeval,
- QR_FLAG | AA_FLAG | RD_FLAG, 1, 2, 6, 0);
-
- RRsetIterator rit = msg.beginSection(Message::SECTION_ANSWER);
- RRsetPtr rrset = *rit;
- EXPECT_EQ(Name("www.wild3.example.com"), rrset->getName());
- EXPECT_EQ(RRType::CNAME(), rrset->getType());
- EXPECT_EQ(RRClass::IN(), rrset->getClass());
-
- RdataIteratorPtr it = rrset->getRdataIterator();
- EXPECT_EQ("spork.example.com.", it->getCurrent().toText());
- it->next();
- EXPECT_TRUE(it->isLast());
-
- rit = msg.beginSection(Message::SECTION_AUTHORITY);
- rrset = *rit;
- EXPECT_EQ(Name("*.wild3.example.com"), rrset->getName());
- EXPECT_EQ(RRType::NSEC(), rrset->getType());
- EXPECT_EQ(RRClass::IN(), rrset->getClass());
- ++rit;
- ++rit;
-
- rrset = *rit;
- EXPECT_EQ(Name("foo.example.com"), rrset->getName());
- EXPECT_EQ(RRType::NSEC(), rrset->getType());
- EXPECT_EQ(RRClass::IN(), rrset->getClass());
- ++rit;
- ++rit;
-
- rrset = *rit;
- EXPECT_EQ(Name("example.com"), rrset->getName());
- EXPECT_EQ(RRType::NSEC(), rrset->getType());
- EXPECT_EQ(RRClass::IN(), rrset->getClass());
-}
-TEST_F(DataSrcTest, AuthDelegation) {
- createAndProcessQuery(Name("www.sql1.example.com"), RRClass::IN(),
- RRType::A());
-
- headerCheck(msg, qid, Rcode::NOERROR(), opcodeval,
- QR_FLAG | AA_FLAG | RD_FLAG, 1, 2, 4, 6);
-
- RRsetIterator rit = msg.beginSection(Message::SECTION_ANSWER);
- RRsetPtr rrset = *rit;
- EXPECT_EQ(Name("www.sql1.example.com"), rrset->getName());
- EXPECT_EQ(RRType::A(), rrset->getType());
- EXPECT_EQ(RRClass::IN(), rrset->getClass());
-
- RdataIteratorPtr it = rrset->getRdataIterator();
- EXPECT_EQ("192.0.2.2", it->getCurrent().toText());
- it->next();
- EXPECT_TRUE(it->isLast());
-
- rit = msg.beginSection(Message::SECTION_AUTHORITY);
- rrset = *rit;
- EXPECT_EQ(Name("sql1.example.com"), rrset->getName());
- EXPECT_EQ(RRType::NS(), rrset->getType());
- EXPECT_EQ(RRClass::IN(), rrset->getClass());
-
- it = rrset->getRdataIterator();
- EXPECT_EQ("dns01.example.com.", it->getCurrent().toText());
- it->next();
- EXPECT_EQ("dns02.example.com.", it->getCurrent().toText());
- it->next();
- EXPECT_EQ("dns03.example.com.", it->getCurrent().toText());
- it->next();
- EXPECT_TRUE(it->isLast());
-
- rit = msg.beginSection(Message::SECTION_ADDITIONAL);
- rrset = *rit;
- EXPECT_EQ(Name("dns01.example.com"), rrset->getName());
- EXPECT_EQ(RRType::A(), rrset->getType());
- EXPECT_EQ(RRClass::IN(), rrset->getClass());
-
- it = rrset->getRdataIterator();
- EXPECT_EQ("192.0.2.1", it->getCurrent().toText());
- it->next();
- EXPECT_TRUE(it->isLast());
-}
-
-TEST_F(DataSrcTest, Dname) {
- createAndProcessQuery(Name("www.dname.example.com"), RRClass::IN(),
- RRType::A());
-
- headerCheck(msg, qid, Rcode::NOERROR(), opcodeval,
- QR_FLAG | AA_FLAG | RD_FLAG, 1, 5, 4, 6);
-
- RRsetIterator rit = msg.beginSection(Message::SECTION_ANSWER);
- RRsetPtr rrset = *rit;
- EXPECT_EQ(Name("dname.example.com"), rrset->getName());
- EXPECT_EQ(RRType::DNAME(), rrset->getType());
- EXPECT_EQ(RRClass::IN(), rrset->getClass());
-
- RdataIteratorPtr it = rrset->getRdataIterator();
- EXPECT_EQ("sql1.example.com.", it->getCurrent().toText());
- it->next();
- EXPECT_TRUE(it->isLast());
-
- // XXX: check CNAME and A record too
-
- rit = msg.beginSection(Message::SECTION_AUTHORITY);
- rrset = *rit;
- EXPECT_EQ(Name("sql1.example.com"), rrset->getName());
- EXPECT_EQ(RRType::NS(), rrset->getType());
- EXPECT_EQ(RRClass::IN(), rrset->getClass());
-
- it = rrset->getRdataIterator();
- EXPECT_EQ("dns01.example.com.", it->getCurrent().toText());
- it->next();
- EXPECT_EQ("dns02.example.com.", it->getCurrent().toText());
- it->next();
- EXPECT_EQ("dns03.example.com.", it->getCurrent().toText());
- it->next();
- EXPECT_TRUE(it->isLast());
-
- rit = msg.beginSection(Message::SECTION_ADDITIONAL);
- rrset = *rit;
- EXPECT_EQ(Name("dns01.example.com"), rrset->getName());
- EXPECT_EQ(RRType::A(), rrset->getType());
- EXPECT_EQ(RRClass::IN(), rrset->getClass());
-
- it = rrset->getRdataIterator();
- EXPECT_EQ("192.0.2.1", it->getCurrent().toText());
- it->next();
- EXPECT_TRUE(it->isLast());
-}
-
-TEST_F(DataSrcTest, DnameExact) {
- // The example.org test zone has a DNAME RR for dname2.foo.example.org.
- // A query for that name with a different RR type than DNAME shouldn't
- // confuse delegation processing.
- createAndProcessQuery(Name("dname2.foo.example.org"), RRClass::IN(),
- RRType::A());
- headerCheck(msg, qid, Rcode::NOERROR(), opcodeval,
- QR_FLAG | AA_FLAG | RD_FLAG, 1, 0, 1, 0);
-}
-
-TEST_F(DataSrcTest, Cname) {
- createAndProcessQuery(Name("foo.example.com"), RRClass::IN(),
- RRType::A());
-
- headerCheck(msg, qid, Rcode::NOERROR(), opcodeval,
- QR_FLAG | AA_FLAG | RD_FLAG, 1, 2, 0, 0);
-
- RRsetIterator rit = msg.beginSection(Message::SECTION_ANSWER);
- RRsetPtr rrset = *rit;
- EXPECT_EQ(Name("foo.example.com"), rrset->getName());
- EXPECT_EQ(RRType::CNAME(), rrset->getType());
- EXPECT_EQ(RRClass::IN(), rrset->getClass());
-
- RdataIteratorPtr it = rrset->getRdataIterator();
- EXPECT_EQ("cnametest.example.net.", it->getCurrent().toText());
- it->next();
- EXPECT_TRUE(it->isLast());
-}
-
-TEST_F(DataSrcTest, CnameInt) {
- createAndProcessQuery(Name("cname-int.example.com"), RRClass::IN(),
- RRType::A());
-
- headerCheck(msg, qid, Rcode::NOERROR(), opcodeval,
- QR_FLAG | AA_FLAG | RD_FLAG, 1, 4, 4, 6);
-
- RRsetIterator rit = msg.beginSection(Message::SECTION_ANSWER);
- RRsetPtr rrset = *rit;
- EXPECT_EQ(Name("cname-int.example.com"), rrset->getName());
- EXPECT_EQ(RRType::CNAME(), rrset->getType());
- EXPECT_EQ(RRClass::IN(), rrset->getClass());
-
- RdataIteratorPtr it = rrset->getRdataIterator();
- EXPECT_EQ("www.example.com.", it->getCurrent().toText());
- it->next();
- EXPECT_TRUE(it->isLast());
-
- // XXX: check a record as well
-
- rit = msg.beginSection(Message::SECTION_AUTHORITY);
- rrset = *rit;
- EXPECT_EQ(Name("example.com"), rrset->getName());
- EXPECT_EQ(RRType::NS(), rrset->getType());
- EXPECT_EQ(RRClass::IN(), rrset->getClass());
-}
-
-TEST_F(DataSrcTest, CnameExt) {
- createAndProcessQuery(Name("cname-ext.example.com"), RRClass::IN(),
- RRType::A());
-
- headerCheck(msg, qid, Rcode::NOERROR(), opcodeval,
- QR_FLAG | AA_FLAG | RD_FLAG, 1, 4, 4, 6);
-
- RRsetIterator rit = msg.beginSection(Message::SECTION_ANSWER);
- RRsetPtr rrset = *rit;
- EXPECT_EQ(Name("cname-ext.example.com"), rrset->getName());
- EXPECT_EQ(RRType::CNAME(), rrset->getType());
- EXPECT_EQ(RRClass::IN(), rrset->getClass());
-
- RdataIteratorPtr it = rrset->getRdataIterator();
- EXPECT_EQ("www.sql1.example.com.", it->getCurrent().toText());
- it->next();
- EXPECT_TRUE(it->isLast());
-
- rit = msg.beginSection(Message::SECTION_AUTHORITY);
- rrset = *rit;
- EXPECT_EQ(Name("sql1.example.com"), rrset->getName());
- EXPECT_EQ(RRType::NS(), rrset->getType());
- EXPECT_EQ(RRClass::IN(), rrset->getClass());
-}
-
-TEST_F(DataSrcTest, Delegation) {
- createAndProcessQuery(Name("www.subzone.example.com"), RRClass::IN(),
- RRType::A());
-
- headerCheck(msg, qid, Rcode::NOERROR(), opcodeval,
- QR_FLAG | RD_FLAG, 1, 0, 5, 2);
-
- RRsetIterator rit = msg.beginSection(Message::SECTION_AUTHORITY);
- RRsetPtr rrset = *rit;
- EXPECT_EQ(Name("subzone.example.com."), rrset->getName());
- EXPECT_EQ(RRType::NS(), rrset->getType());
- EXPECT_EQ(RRClass::IN(), rrset->getClass());
-
- RdataIteratorPtr it = rrset->getRdataIterator();
- EXPECT_EQ("ns1.subzone.example.com.", it->getCurrent().toText());
- it->next();
- EXPECT_FALSE(it->isLast());
-
- rit = msg.beginSection(Message::SECTION_ADDITIONAL);
- rrset = *rit;
- EXPECT_EQ(Name("ns1.subzone.example.com"), rrset->getName());
- EXPECT_EQ(RRType::A(), rrset->getType());
- EXPECT_EQ(RRClass::IN(), rrset->getClass());
-
- it = rrset->getRdataIterator();
- EXPECT_EQ("192.0.2.1", it->getCurrent().toText());
- it->next();
- EXPECT_TRUE(it->isLast());
-}
-
-TEST_F(DataSrcTest, NSDelegation) {
- createAndProcessQuery(Name("subzone.example.com"), RRClass::IN(),
- RRType::NS());
-
- headerCheck(msg, qid, Rcode::NOERROR(), opcodeval,
- QR_FLAG | RD_FLAG, 1, 0, 5, 2);
-
- RRsetIterator rit = msg.beginSection(Message::SECTION_AUTHORITY);
- RRsetPtr rrset = *rit;
- EXPECT_EQ(Name("subzone.example.com."), rrset->getName());
- EXPECT_EQ(RRType::NS(), rrset->getType());
- EXPECT_EQ(RRClass::IN(), rrset->getClass());
-
- RdataIteratorPtr it = rrset->getRdataIterator();
- EXPECT_EQ("ns1.subzone.example.com.", it->getCurrent().toText());
- it->next();
- EXPECT_FALSE(it->isLast());
-
- rit = msg.beginSection(Message::SECTION_ADDITIONAL);
- rrset = *rit;
- EXPECT_EQ(Name("ns1.subzone.example.com"), rrset->getName());
- EXPECT_EQ(RRType::A(), rrset->getType());
- EXPECT_EQ(RRClass::IN(), rrset->getClass());
-
- it = rrset->getRdataIterator();
- EXPECT_EQ("192.0.2.1", it->getCurrent().toText());
- it->next();
- EXPECT_TRUE(it->isLast());
-}
-
-TEST_F(DataSrcTest, ANYZonecut) {
- // An ANY query at a zone cut should behave the same as any other
- // delegation
- createAndProcessQuery(Name("subzone.example.com"), RRClass::IN(),
- RRType::ANY());
-}
-
-TEST_F(DataSrcTest, NSECZonecut) {
- createAndProcessQuery(Name("subzone.example.com"), RRClass::IN(),
- RRType::NSEC());
-
- headerCheck(msg, qid, Rcode::NOERROR(), opcodeval,
- QR_FLAG | AA_FLAG | RD_FLAG, 1, 2, 4, 6);
-
- RRsetIterator rit = msg.beginSection(Message::SECTION_ANSWER);
- RRsetPtr rrset = *rit;
- EXPECT_EQ(Name("subzone.example.com."), rrset->getName());
- EXPECT_EQ(RRType::NSEC(), rrset->getType());
- EXPECT_EQ(RRClass::IN(), rrset->getClass());
-
- rit = msg.beginSection(Message::SECTION_AUTHORITY);
- rrset = *rit;
- EXPECT_EQ(Name("example.com"), rrset->getName());
- EXPECT_EQ(RRType::NS(), rrset->getType());
- EXPECT_EQ(RRClass::IN(), rrset->getClass());
-
- RdataIteratorPtr it = rrset->getRdataIterator();
- EXPECT_EQ("dns01.example.com.", it->getCurrent().toText());
- it->next();
- EXPECT_EQ("dns02.example.com.", it->getCurrent().toText());
- it->next();
- EXPECT_EQ("dns03.example.com.", it->getCurrent().toText());
- it->next();
- EXPECT_TRUE(it->isLast());
-}
-
-TEST_F(DataSrcTest, DNAMEZonecut) {
- createAndProcessQuery(Name("subzone.example.com"), RRClass::IN(),
- RRType::DNAME());
-
- headerCheck(msg, qid, Rcode::NOERROR(), opcodeval,
- QR_FLAG | RD_FLAG, 1, 0, 5, 2);
- RRsetIterator rit = msg.beginSection(Message::SECTION_AUTHORITY);
- RRsetPtr rrset = *rit;
- EXPECT_EQ(Name("subzone.example.com."), rrset->getName());
- EXPECT_EQ(RRType::NS(), rrset->getType());
- EXPECT_EQ(RRClass::IN(), rrset->getClass());
-
- RdataIteratorPtr it = rrset->getRdataIterator();
- EXPECT_EQ("ns1.subzone.example.com.", it->getCurrent().toText());
- it->next();
- EXPECT_FALSE(it->isLast());
-
- rit = msg.beginSection(Message::SECTION_ADDITIONAL);
- rrset = *rit;
- EXPECT_EQ(Name("ns1.subzone.example.com"), rrset->getName());
- EXPECT_EQ(RRType::A(), rrset->getType());
- EXPECT_EQ(RRClass::IN(), rrset->getClass());
-
- it = rrset->getRdataIterator();
- EXPECT_EQ("192.0.2.1", it->getCurrent().toText());
- it->next();
- EXPECT_TRUE(it->isLast());
-}
-
-TEST_F(DataSrcTest, DS) {
- createAndProcessQuery(Name("subzone.example.com"), RRClass::IN(),
- RRType::DS());
-
- headerCheck(msg, qid, Rcode::NOERROR(), opcodeval,
- QR_FLAG | AA_FLAG | RD_FLAG, 1, 3, 4, 6);
-
- RRsetIterator rit = msg.beginSection(Message::SECTION_ANSWER);
- RRsetPtr rrset = *rit;
- EXPECT_EQ(Name("subzone.example.com."), rrset->getName());
- EXPECT_EQ(RRType::DS(), rrset->getType());
- EXPECT_EQ(RRClass::IN(), rrset->getClass());
-
- rit = msg.beginSection(Message::SECTION_AUTHORITY);
- rrset = *rit;
- EXPECT_EQ(Name("example.com"), rrset->getName());
- EXPECT_EQ(RRType::NS(), rrset->getType());
- EXPECT_EQ(RRClass::IN(), rrset->getClass());
-
- RdataIteratorPtr it = rrset->getRdataIterator();
- EXPECT_EQ("dns01.example.com.", it->getCurrent().toText());
- it->next();
- EXPECT_EQ("dns02.example.com.", it->getCurrent().toText());
- it->next();
- EXPECT_EQ("dns03.example.com.", it->getCurrent().toText());
- it->next();
- EXPECT_TRUE(it->isLast());
-}
-
-TEST_F(DataSrcTest, CNAMELoop) {
- createAndProcessQuery(Name("one.loop.example"), RRClass::IN(),
- RRType::A());
- EXPECT_EQ(Rcode::NOERROR(), msg.getRcode());
-
- // one.loop.example points to two.loop.example, which points back
- // to one.loop.example, so there should be exactly two CNAME records
- // in the answer.
- EXPECT_EQ(2, msg.getRRCount(Message::SECTION_ANSWER));
-}
-
-// NSEC query for the name of a zone cut for non-secure delegation.
-// Should return normal referral.
-TEST_F(DataSrcTest, NSECZonecutOfNonsecureZone) {
- createAndProcessQuery(Name("sub.example.org"), RRClass::IN(),
- RRType::NSEC());
-
- headerCheck(msg, qid, Rcode::NOERROR(), opcodeval,
- QR_FLAG | RD_FLAG, 1, 0, 1, 1);
-
- RRsetIterator rit = msg.beginSection(Message::SECTION_AUTHORITY);
- ConstRRsetPtr rrset = *rit;
- EXPECT_EQ(Name("sub.example.org."), rrset->getName());
- EXPECT_EQ(RRType::NS(), rrset->getType());
- EXPECT_EQ(RRClass::IN(), rrset->getClass());
-
- RdataIteratorPtr it = rrset->getRdataIterator();
- EXPECT_EQ(createRdata(RRType::NS(), RRClass::IN(),
- "ns.sub.example.org.")->toText(),
- it->getCurrent().toText());
- it->next();
- EXPECT_TRUE(it->isLast());
-
- rit = msg.beginSection(Message::SECTION_ADDITIONAL);
- rrset = *rit;
- EXPECT_EQ(Name("ns.sub.example.org."), rrset->getName());
- EXPECT_EQ(RRType::A(), rrset->getType());
- EXPECT_EQ(RRClass::IN(), rrset->getClass());
-
- it = rrset->getRdataIterator();
- EXPECT_EQ(createRdata(RRType::A(), RRClass::IN(), "192.0.2.101")->toText(),
- it->getCurrent().toText());
- it->next();
- EXPECT_TRUE(it->isLast());
-}
-
-// Test sending a DS query to root (nonsense, but it should survive)
-TEST_F(DataSrcTest, RootDSQuery1) {
- EXPECT_NO_THROW(createAndProcessQuery(Name("."), RRClass::IN(),
- RRType::DS()));
- headerCheck(msg, qid, Rcode::REFUSED(), opcodeval,
- QR_FLAG | RD_FLAG, 1, 0, 0, 0);
-}
-
-// The same, but when we have the root zone
-// (which triggers rfc4035 section 3.1.4.1)
-TEST_F(DataSrcTest, RootDSQuery2) {
- // The message
- msg.makeResponse();
- msg.setOpcode(Opcode::QUERY());
- msg.addQuestion(Question(Name("."), RRClass::IN(), RRType::DS()));
- msg.setHeaderFlag(Message::HEADERFLAG_RD);
- // Prepare the source
- DataSrcPtr sql3_source = DataSrcPtr(new Sqlite3DataSrc);
- ConstElementPtr sqlite_root = Element::fromJSON(
- "{ \"database_file\": \"" TEST_DATA_DIR "/test-root.sqlite3\"}");
- EXPECT_NO_THROW(sql3_source->init(sqlite_root));
- // Make the query
- EXPECT_NO_THROW(performQuery(*sql3_source, cache, msg));
-
- headerCheck(msg, qid, Rcode::NOERROR(), opcodeval,
- QR_FLAG | AA_FLAG | RD_FLAG, 1, 0, 1, 0);
-}
-
-TEST_F(DataSrcTest, DSQueryFromCache) {
- // explicitly enable hot spot cache
- cache.setEnabled(true);
-
- // The first query will create a negative cache for example.org/CNAME
- createAndProcessQuery(Name("example.org"), RRClass::IN(), RRType::SOA());
-
- // the cached CNAME shouldn't confuse subsequent query.
- // there may be several different possible cases that could trigger a bug,
- // but DS query is the only known example.
- msg.clear(Message::PARSE);
- createAndProcessQuery(Name("example.org"), RRClass::IN(), RRType::DS());
-
- // returning refused is probably a bad behavior, but it's a different
- // issue -- see Trac Ticket #306.
- headerCheck(msg, qid, Rcode::REFUSED(), opcodeval,
- QR_FLAG | RD_FLAG, 1, 0, 0, 0);
-}
-
-// Non-existent name in the "static" data source. The purpose of this test
-// is to check a corner case behavior when atypical RRClass (CH in this case)
-// is specified.
-TEST_F(DataSrcTest, StaticNxDomain) {
- createAndProcessQuery(Name("www.version.bind"), RRClass::CH(),
- RRType::TXT());
- headerCheck(msg, qid, Rcode::NXDOMAIN(), opcodeval,
- QR_FLAG | AA_FLAG | RD_FLAG, 1, 0, 1, 0);
- RRsetIterator rit = msg.beginSection(Message::SECTION_AUTHORITY);
- RRsetPtr rrset = *rit;
- EXPECT_EQ(Name("version.bind"), rrset->getName());
- EXPECT_EQ(RRType::SOA(), rrset->getType());
- EXPECT_EQ(RRClass::CH(), rrset->getClass());
-}
-
-TEST_F(DataSrcTest, Nsec3Hash) {
- vector<uint8_t> salt;
- salt.push_back(0xfe);
- salt.push_back(0xed);
- salt.push_back(0xab);
- salt.push_back(0xee);
- Nsec3Param nsec3(1, 0, 10, salt);
- EXPECT_EQ("VIR9KJAPN2FHRLS6EP0JBQ89MBLUE296", nsec3.getHash(Name("test1")));
- EXPECT_EQ("FHA27EURONFH5640SFJQ8MJAKMCVB7UJ", nsec3.getHash(Name("test2")));
- EXPECT_EQ("A4M93LR7A60IDDQMO6TCVUPCC60CU38A", nsec3.getHash(Name("test3")));
-}
-
-TEST_F(DataSrcTest, AddRemoveDataSrc) {
- MetaDataSrc ds;
- ConstDataSrcPtr tsp = ConstDataSrcPtr(new TestDataSrc);
- EXPECT_EQ(0, ds.dataSrcCount());
- ds.addDataSrc(tsp);
- EXPECT_EQ(1, ds.dataSrcCount());
- ds.removeDataSrc(tsp);
- EXPECT_EQ(0, ds.dataSrcCount());
-}
-
-TEST_F(DataSrcTest, noNSZone) {
- EXPECT_THROW(createAndProcessQuery(Name("www.nons.example"),
- RRClass::IN(), RRType::A()),
- DataSourceError);
-}
-
-TEST_F(DataSrcTest, noNSButDnameZone) {
- EXPECT_THROW(createAndProcessQuery(Name("www.nons-dname.example"),
- RRClass::IN(), RRType::A()),
- DataSourceError);
-}
-
-TEST_F(DataSrcTest, noSOAZone) {
- EXPECT_THROW(createAndProcessQuery(Name("notexist.nosoa.example"),
- RRClass::IN(), RRType::A()),
- DataSourceError);
-}
-
-TEST_F(DataSrcTest, apexCNAMEZone) {
- // The query name doesn't exist in the best matching zone,
- // and there's a CNAME at the apex (which is bogus), so query handling
- // will fail due to missing SOA.
- EXPECT_THROW(createAndProcessQuery(Name("notexist.apexcname.example"),
- RRClass::IN(), RRType::A()),
- DataSourceError);
-}
-
-TEST_F(DataSrcTest, incompleteGlue) {
- // One of the NS names belong to a different zone (which is still
- // authoritative), and the glue is missing in that zone. We should
- // still return the existent glue.
- // (nons.example is also broken in that it doesn't have apex NS, but
- // that doesn't matter for this test)
- createAndProcessQuery(Name("www.incompletechild.nons.example"),
- RRClass::IN(), RRType::A());
- headerCheck(msg, qid, Rcode::NOERROR(), opcodeval,
- QR_FLAG | RD_FLAG, 1, 0, 2, 1);
- rrsetsCheck("incompletechild.nons.example. 3600 IN NS ns.incompletechild.nons.example.\n"
- "incompletechild.nons.example. 3600 IN NS nx.nosoa.example.",
- msg.beginSection(Message::SECTION_AUTHORITY),
- msg.endSection(Message::SECTION_AUTHORITY));
- rrsetsCheck("ns.incompletechild.nons.example. 3600 IN A 192.0.2.1",
- msg.beginSection(Message::SECTION_ADDITIONAL),
- msg.endSection(Message::SECTION_ADDITIONAL));
-}
-
-// currently fails
-TEST_F(DataSrcTest, DISABLED_synthesizedCnameTooLong) {
- // qname has the possible max length (255 octets). it matches a DNAME,
- // and the synthesized CNAME would exceed the valid length.
- createAndProcessQuery(
- Name("0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcde."
- "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcde."
- "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcde."
- "0123456789abcdef0123456789abcdef0123456789a.dname.example.org."),
- RRClass::IN(), RRType::A());
-}
-
-TEST_F(DataSrcTest, cacheDataInNonexistentZone) {
- // This test emulates the situation where an RRset in some zone of some
- // data source is cached and then the zone is removed from the data source.
- // When there is such a substantial inconsistency between the cache and
- // the real data source, we should honor the latter. More important,
- // the inconsistency shouldn't cause any disruption such as a crash.
-
- const Name qname("nosuchzone.example");
- RRsetPtr rrset(new RRset(qname, RRClass::IN(), RRType::A(), RRTTL(0)));
- cache.addPositive(rrset, DataSrc::REFERRAL);
-
- createAndProcessQuery(qname, RRClass::IN(), RRType::A(), false);
- headerCheck(msg, qid, Rcode::REFUSED(), opcodeval, QR_FLAG | RD_FLAG,
- 1, 0, 0, 0);
-}
-
-// Tests of the DataSrcMatch class start here
-class DataSrcMatchTest : public ::testing::Test {
-protected:
- DataSrcMatchTest() {
- datasrc1.init();
- }
- // test data source serves example.com/IN.
- TestDataSrc datasrc1;
- // this data source is dummy. Its content doesn't matter in the tests.
- TestDataSrc datasrc2;
-};
-
-TEST_F(DataSrcMatchTest, match) {
- DataSrcMatch match(Name("very.very.long.example.com"), RRClass::IN());
- datasrc1.findClosestEnclosure(match);
- EXPECT_EQ(Name("example.com"), *match.getEnclosingZone());
- EXPECT_EQ(&datasrc1, match.getDataSource());
-}
-
-TEST_F(DataSrcMatchTest, matchWithWrongClass) {
- DataSrcMatch match(Name("very.very.long.example.com"), RRClass::CH());
- datasrc1.findClosestEnclosure(match);
- // XXX: some deviant compilers seem to fail to recognize a NULL as a
- // pointer type. This explicit cast works around such compilers.
- EXPECT_EQ(static_cast<void*>(NULL), match.getEnclosingZone());
- EXPECT_EQ(static_cast<void*>(NULL), match.getDataSource());
-}
-
-TEST_F(DataSrcMatchTest, matchWithAnyClass) {
- DataSrcMatch match(Name("very.very.long.example.com"), RRClass::ANY());
- datasrc1.findClosestEnclosure(match);
- EXPECT_EQ(Name("example.com"), *match.getEnclosingZone());
- EXPECT_EQ(&datasrc1, match.getDataSource());
-}
-
-TEST_F(DataSrcMatchTest, updateWithWrongClass) {
- DataSrcMatch match(Name("www.example.com"), RRClass::CH());
-
- EXPECT_EQ(RRClass::IN(), datasrc2.getClass());
- match.update(datasrc2, Name("com"));
- EXPECT_EQ(static_cast<void*>(NULL), match.getEnclosingZone());
- EXPECT_EQ(static_cast<void*>(NULL), match.getDataSource());
-
- EXPECT_EQ(RRClass::IN(), datasrc1.getClass());
- match.update(datasrc1, Name("example.com"));
- EXPECT_EQ(static_cast<void*>(NULL), match.getEnclosingZone());
- EXPECT_EQ(static_cast<void*>(NULL), match.getDataSource());
-}
-
-TEST_F(DataSrcMatchTest, updateAgainstAnyClass) {
- DataSrcMatch match(Name("www.example.com"), RRClass::ANY());
- match.update(datasrc2, Name("com"));
- EXPECT_EQ(Name("com"), *match.getEnclosingZone());
- EXPECT_EQ(&datasrc2, match.getDataSource());
-
- // the given class for search is ANY, so update should be okay.
- EXPECT_EQ(RRClass::IN(), datasrc1.getClass());
- match.update(datasrc1, Name("example.com"));
- EXPECT_EQ(Name("example.com"), *match.getEnclosingZone());
- EXPECT_EQ(&datasrc1, match.getDataSource());
-}
-
-TEST_F(DataSrcMatchTest, updateWithNoMatch) {
- DataSrcMatch match(Name("www.example.com"), RRClass::IN());
- match.update(datasrc1, Name("com"));
- EXPECT_EQ(Name("com"), *match.getEnclosingZone());
- EXPECT_EQ(&datasrc1, match.getDataSource());
-
- // An attempt of update with a name that doesn't match. This attempt
- // should be ignored.
- match.update(datasrc2, Name("example.org"));
- EXPECT_EQ(Name("com"), *match.getEnclosingZone());
- EXPECT_EQ(&datasrc1, match.getDataSource());
-}
-
-TEST_F(DataSrcMatchTest, initialUpdateWithNoMatch) {
- DataSrcMatch match(Name("www.example.com"), RRClass::IN());
-
- // An initial attempt of update with a name that doesn't match.
- // Should be ignored.
- match.update(datasrc1, Name("example.org"));
- EXPECT_EQ(static_cast<void*>(NULL), match.getEnclosingZone());
- EXPECT_EQ(static_cast<void*>(NULL), match.getDataSource());
-}
-
-TEST_F(DataSrcMatchTest, updateWithShorterMatch) {
- DataSrcMatch match(Name("www.example.com"), RRClass::IN());
-
- match.update(datasrc1, Name("example.com"));
- EXPECT_EQ(Name("example.com"), *match.getEnclosingZone());
- EXPECT_EQ(&datasrc1, match.getDataSource());
-
- // An attempt of update with a name that gives a shorter match.
- // This attempt should be ignored.
- match.update(datasrc2, Name("com"));
- EXPECT_EQ(Name("example.com"), *match.getEnclosingZone());
- EXPECT_EQ(&datasrc1, match.getDataSource());
-}
-
-}
diff --git a/src/lib/datasrc/tests/faked_nsec3.cc b/src/lib/datasrc/tests/faked_nsec3.cc
index b702d28..f53fbd7 100644
--- a/src/lib/datasrc/tests/faked_nsec3.cc
+++ b/src/lib/datasrc/tests/faked_nsec3.cc
@@ -67,6 +67,17 @@ public:
"00000000000000000000000000000000";
map_[Name("largest.example.org")] =
"UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU";
+
+ // These are used by the findNSEC3Walk test.
+ map_[Name("n0.example.org")] = "00000000000000000000000000000000";
+ map_[Name("n1.example.org")] = "01UDEMVP1J2F7EG6JEBPS17VP3N8I58H";
+ map_[Name("n2.example.org")] = "02UDEMVP1J2F7EG6JEBPS17VP3N8I58H";
+ map_[Name("n3.example.org")] = "0P9MHAVEQVM6T7VBL5LOP2U3T2RP3TOM";
+ map_[Name("n4.example.org")] = "11111111111111111111111111111111";
+ map_[Name("n5.example.org")] = "2T7B4G4VSA5SMI47K61MV5BV1A22BOJR";
+ map_[Name("n6.example.org")] = "44444444444444444444444444444444";
+ map_[Name("n7.example.org")] = "R53BQ7CC2UVMUBFU5OCMM6PERS9TK9EN";
+ map_[Name("n8.example.org")] = "ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ";
}
virtual string calculate(const Name& name) const {
const NSEC3HashMap::const_iterator found = map_.find(name);
@@ -94,6 +105,12 @@ NSEC3Hash* TestNSEC3HashCreator::create(const rdata::generic::NSEC3&) const {
return (new TestNSEC3Hash);
}
+NSEC3Hash* TestNSEC3HashCreator::create(uint8_t, uint16_t,
+ const uint8_t*, size_t) const
+{
+ return (new TestNSEC3Hash);
+}
+
void
findNSEC3Check(bool expected_matched, uint8_t expected_labels,
const string& expected_closest,
diff --git a/src/lib/datasrc/tests/faked_nsec3.h b/src/lib/datasrc/tests/faked_nsec3.h
index 8c1857c..26a7b8d 100644
--- a/src/lib/datasrc/tests/faked_nsec3.h
+++ b/src/lib/datasrc/tests/faked_nsec3.h
@@ -57,11 +57,15 @@ class TestNSEC3HashCreator : public isc::dns::NSEC3HashCreator {
private:
class TestNSEC3Hash;
public:
+ TestNSEC3HashCreator() {}
virtual isc::dns::NSEC3Hash* create(const
isc::dns::rdata::generic::NSEC3PARAM&)
const;
virtual isc::dns::NSEC3Hash* create(const isc::dns::rdata::generic::NSEC3&)
const;
+ virtual isc::dns::NSEC3Hash* create(uint8_t, uint16_t,
+ const uint8_t*, size_t)
+ const;
};
// Check the result against expected values. It directly calls EXPECT_ macros
diff --git a/src/lib/datasrc/tests/memory/.gitignore b/src/lib/datasrc/tests/memory/.gitignore
new file mode 100644
index 0000000..d6d1ec8
--- /dev/null
+++ b/src/lib/datasrc/tests/memory/.gitignore
@@ -0,0 +1 @@
+/run_unittests
diff --git a/src/lib/datasrc/tests/memory/Makefile.am b/src/lib/datasrc/tests/memory/Makefile.am
new file mode 100644
index 0000000..00d5594
--- /dev/null
+++ b/src/lib/datasrc/tests/memory/Makefile.am
@@ -0,0 +1,49 @@
+SUBDIRS = testdata .
+
+AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
+AM_CPPFLAGS += -I$(top_builddir)/src/lib/dns -I$(top_srcdir)/src/lib/dns
+AM_CPPFLAGS += $(BOOST_INCLUDES)
+AM_CPPFLAGS += -DTEST_DATA_DIR=\"$(abs_srcdir)/testdata\"
+
+AM_CXXFLAGS = $(B10_CXXFLAGS)
+
+if USE_STATIC_LINK
+AM_LDFLAGS = -static
+endif
+
+CLEANFILES = *.gcno *.gcda
+
+TESTS_ENVIRONMENT = \
+ $(LIBTOOL) --mode=execute $(VALGRIND_COMMAND)
+
+TESTS =
+if HAVE_GTEST
+TESTS += run_unittests
+
+run_unittests_SOURCES = run_unittests.cc
+run_unittests_SOURCES += rdata_serialization_unittest.cc
+run_unittests_SOURCES += rdataset_unittest.cc
+run_unittests_SOURCES += domaintree_unittest.cc
+run_unittests_SOURCES += treenode_rrset_unittest.cc
+run_unittests_SOURCES += zone_table_unittest.cc
+run_unittests_SOURCES += zone_data_unittest.cc
+run_unittests_SOURCES += zone_finder_unittest.cc
+run_unittests_SOURCES += ../../tests/faked_nsec3.h ../../tests/faked_nsec3.cc
+run_unittests_SOURCES += memory_segment_test.h
+run_unittests_SOURCES += segment_object_holder_unittest.cc
+run_unittests_SOURCES += memory_client_unittest.cc
+
+run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
+run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
+
+run_unittests_LDADD = $(top_builddir)/src/lib/datasrc/libb10-datasrc.la
+run_unittests_LDADD += $(top_builddir)/src/lib/dns/libb10-dns++.la
+run_unittests_LDADD += $(top_builddir)/src/lib/util/unittests/libutil_unittests.la
+run_unittests_LDADD += $(top_builddir)/src/lib/util/libb10-util.la
+run_unittests_LDADD += $(top_builddir)/src/lib/testutils/libb10-testutils.la
+run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libb10-exceptions.la
+run_unittests_LDADD += $(top_builddir)/src/lib/log/libb10-log.la
+run_unittests_LDADD += $(GTEST_LDADD)
+endif
+
+noinst_PROGRAMS = $(TESTS)
diff --git a/src/lib/datasrc/tests/memory/domaintree_unittest.cc b/src/lib/datasrc/tests/memory/domaintree_unittest.cc
new file mode 100644
index 0000000..cb16e02
--- /dev/null
+++ b/src/lib/datasrc/tests/memory/domaintree_unittest.cc
@@ -0,0 +1,1292 @@
+// Copyright (C) 2010 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 <exceptions/exceptions.h>
+
+#include <util/memory_segment_local.h>
+
+#include <dns/name.h>
+#include <dns/rrclass.h>
+#include <dns/rrset.h>
+#include <dns/rrtype.h>
+#include <dns/rrttl.h>
+
+#include <datasrc/memory/domaintree.h>
+
+#include <dns/tests/unittest_util.h>
+
+using namespace std;
+using namespace isc;
+using namespace isc::dns;
+using isc::UnitTestUtil;
+using namespace isc::datasrc::memory;
+
+// XXX: some compilers cannot find class static constants used in
+// EXPECT_xxx macros, for which we need an explicit empty definition.
+const size_t Name::MAX_LABELS;
+
+/* The initial structure of dtree
+ *
+ * .
+ * |
+ * b
+ * / \
+ * a d.e.f
+ * / | \
+ * c | g.h
+ * | |
+ * w.y i
+ * / | \ \
+ * x | z k
+ * | |
+ * p j
+ * / \
+ * o q
+ */
+
+namespace {
+
+void deleteData(int* i) {
+ delete i;
+}
+
+typedef DomainTree<int> TestDomainTree;
+typedef DomainTreeNode<int> TestDomainTreeNode;
+typedef DomainTreeNodeChain<int> TestDomainTreeNodeChain;
+
+class TreeHolder {
+public:
+ TreeHolder(util::MemorySegment& mem_sgmt, TestDomainTree* tree) :
+ mem_sgmt_(mem_sgmt), tree_(tree)
+ {}
+ ~TreeHolder() {
+ TestDomainTree::destroy(mem_sgmt_, tree_, deleteData);
+ }
+ TestDomainTree* get() { return (tree_); }
+private:
+ util::MemorySegment& mem_sgmt_;
+ TestDomainTree* tree_;
+};
+
+class DomainTreeTest : public::testing::Test {
+protected:
+ DomainTreeTest() :
+ dtree_holder_(mem_sgmt_, TestDomainTree::create(mem_sgmt_)),
+ dtree_expose_empty_node_holder_(mem_sgmt_,
+ TestDomainTree::create(mem_sgmt_, true)),
+ dtree(*dtree_holder_.get()),
+ dtree_expose_empty_node(*dtree_expose_empty_node_holder_.get()),
+ cdtnode(NULL)
+ {
+ const char* const domain_names[] = {
+ "c", "b", "a", "x.d.e.f", "z.d.e.f", "g.h", "i.g.h", "o.w.y.d.e.f",
+ "j.z.d.e.f", "p.w.y.d.e.f", "q.w.y.d.e.f", "k.g.h"};
+ int name_count = sizeof(domain_names) / sizeof(domain_names[0]);
+ for (int i = 0; i < name_count; ++i) {
+ dtree.insert(mem_sgmt_, Name(domain_names[i]), &dtnode);
+ // Check the node doesn't have any data initially.
+ EXPECT_EQ(static_cast<int*>(NULL),
+ dtnode->setData(new int(i + 1)));
+
+ dtree_expose_empty_node.insert(mem_sgmt_, Name(domain_names[i]),
+ &dtnode);
+ EXPECT_EQ(static_cast<int*>(NULL), dtnode->setData(new int(i + 1)));
+ }
+ }
+
+ util::MemorySegmentLocal mem_sgmt_;
+ TreeHolder dtree_holder_;
+ TreeHolder dtree_expose_empty_node_holder_;
+ TestDomainTree& dtree;
+ TestDomainTree& dtree_expose_empty_node;
+ TestDomainTreeNode* dtnode;
+ const TestDomainTreeNode* cdtnode;
+ uint8_t buf[LabelSequence::MAX_SERIALIZED_LENGTH];
+};
+
+TEST_F(DomainTreeTest, nodeCount) {
+ EXPECT_EQ(15, dtree.getNodeCount());
+
+ // Delete all nodes, then the count should be set to 0. This also tests
+ // the behavior of deleteAllNodes().
+ dtree.deleteAllNodes(mem_sgmt_, deleteData);
+ EXPECT_EQ(0, dtree.getNodeCount());
+}
+
+TEST_F(DomainTreeTest, setGetData) {
+ // set new data to an existing node. It should have some data.
+ int* newdata = new int(11);
+ int* olddata = dtnode->setData(newdata);
+ EXPECT_NE(static_cast<int*>(NULL), olddata);
+ deleteData(olddata);
+ EXPECT_EQ(11, *(dtnode->getData()));
+
+ // clear the node. we should get the new data back we just passed.
+ olddata = dtnode->setData(NULL);
+ EXPECT_EQ(newdata, olddata);
+ deleteData(olddata);
+}
+
+TEST_F(DomainTreeTest, insertNames) {
+ EXPECT_EQ(TestDomainTree::ALREADYEXISTS, dtree.insert(mem_sgmt_,
+ Name("d.e.f"),
+ &dtnode));
+ EXPECT_EQ(Name("d.e.f"), dtnode->getName());
+ EXPECT_EQ(15, dtree.getNodeCount());
+
+ // insert not exist node
+ EXPECT_EQ(TestDomainTree::SUCCESS, dtree.insert(mem_sgmt_, Name("0"),
+ &dtnode));
+ EXPECT_EQ(Name("0"), dtnode->getName());
+ EXPECT_EQ(16, dtree.getNodeCount());
+
+ EXPECT_EQ(TestDomainTree::SUCCESS, dtree.insert(mem_sgmt_,
+ Name("example.com"),
+ &dtnode));
+ EXPECT_EQ(17, dtree.getNodeCount());
+ // add data to it; also make sure it doesn't have data right now
+ // (otherwise it would leak)
+ EXPECT_EQ(static_cast<int*>(NULL), dtnode->setData(new int(12)));
+
+ // return ALREADYEXISTS, since node "example.com" already has
+ // been explicitly inserted
+ EXPECT_EQ(TestDomainTree::ALREADYEXISTS, dtree.insert(mem_sgmt_,
+ Name("example.com"),
+ &dtnode));
+ EXPECT_EQ(17, dtree.getNodeCount());
+
+ // split the node "d.e.f"
+ EXPECT_EQ(TestDomainTree::SUCCESS, dtree.insert(mem_sgmt_, Name("k.e.f"),
+ &dtnode));
+ EXPECT_EQ(Name("k"), dtnode->getName());
+ EXPECT_EQ(19, dtree.getNodeCount());
+
+ // split the node "g.h"
+ EXPECT_EQ(TestDomainTree::ALREADYEXISTS, dtree.insert(mem_sgmt_, Name("h"),
+ &dtnode));
+ EXPECT_EQ(Name("h"), dtnode->getName());
+ EXPECT_EQ(20, dtree.getNodeCount());
+
+ // add child domain
+ EXPECT_EQ(TestDomainTree::SUCCESS, dtree.insert(mem_sgmt_,
+ Name("m.p.w.y.d.e.f"),
+ &dtnode));
+ EXPECT_EQ(Name("m"), dtnode->getName());
+ EXPECT_EQ(21, dtree.getNodeCount());
+ EXPECT_EQ(TestDomainTree::SUCCESS, dtree.insert(mem_sgmt_,
+ Name("n.p.w.y.d.e.f"),
+ &dtnode));
+ EXPECT_EQ(Name("n"), dtnode->getName());
+ EXPECT_EQ(22, dtree.getNodeCount());
+
+ EXPECT_EQ(TestDomainTree::SUCCESS, dtree.insert(mem_sgmt_, Name("l.a"),
+ &dtnode));
+ EXPECT_EQ(Name("l"), dtnode->getName());
+ EXPECT_EQ(23, dtree.getNodeCount());
+
+ EXPECT_EQ(TestDomainTree::SUCCESS, dtree.insert(mem_sgmt_, Name("r.d.e.f"),
+ &dtnode));
+ EXPECT_EQ(TestDomainTree::SUCCESS, dtree.insert(mem_sgmt_, Name("s.d.e.f"),
+ &dtnode));
+ EXPECT_EQ(25, dtree.getNodeCount());
+
+ EXPECT_EQ(TestDomainTree::SUCCESS, dtree.insert(mem_sgmt_,
+ Name("h.w.y.d.e.f"),
+ &dtnode));
+
+ // add more nodes one by one to cover leftRotate and rightRotate
+ EXPECT_EQ(TestDomainTree::ALREADYEXISTS, dtree.insert(mem_sgmt_, Name("f"),
+ &dtnode));
+ EXPECT_EQ(TestDomainTree::SUCCESS, dtree.insert(mem_sgmt_, Name("m"),
+ &dtnode));
+ EXPECT_EQ(TestDomainTree::SUCCESS, dtree.insert(mem_sgmt_, Name("nm"),
+ &dtnode));
+ EXPECT_EQ(TestDomainTree::SUCCESS, dtree.insert(mem_sgmt_, Name("om"),
+ &dtnode));
+ EXPECT_EQ(TestDomainTree::SUCCESS, dtree.insert(mem_sgmt_, Name("k"),
+ &dtnode));
+ EXPECT_EQ(TestDomainTree::SUCCESS, dtree.insert(mem_sgmt_, Name("l"),
+ &dtnode));
+ EXPECT_EQ(TestDomainTree::SUCCESS, dtree.insert(mem_sgmt_, Name("fe"),
+ &dtnode));
+ EXPECT_EQ(TestDomainTree::SUCCESS, dtree.insert(mem_sgmt_, Name("ge"),
+ &dtnode));
+ EXPECT_EQ(TestDomainTree::SUCCESS, dtree.insert(mem_sgmt_, Name("i"),
+ &dtnode));
+ EXPECT_EQ(TestDomainTree::SUCCESS, dtree.insert(mem_sgmt_, Name("ae"),
+ &dtnode));
+ EXPECT_EQ(TestDomainTree::SUCCESS, dtree.insert(mem_sgmt_, Name("n"),
+ &dtnode));
+}
+
+TEST_F(DomainTreeTest, subTreeRoot) {
+ // This is a testcase for a particular issue that went unchecked in
+ // #2089's implementation, but was fixed in #2092. The issue was
+ // that when a node was fissioned, FLAG_SUBTREE_ROOT was not being
+ // copied correctly.
+
+ EXPECT_EQ(TestDomainTree::ALREADYEXISTS,
+ dtree_expose_empty_node.insert(mem_sgmt_, Name("d.e.f"),
+ &dtnode));
+ EXPECT_EQ(TestDomainTree::SUCCESS,
+ dtree_expose_empty_node.insert(mem_sgmt_, Name("0"),
+ &dtnode));
+ EXPECT_EQ(TestDomainTree::SUCCESS,
+ dtree_expose_empty_node.insert(mem_sgmt_, Name("example.com"),
+ &dtnode));
+ EXPECT_EQ(TestDomainTree::ALREADYEXISTS,
+ dtree_expose_empty_node.insert(mem_sgmt_, Name("example.com"),
+ &dtnode));
+ EXPECT_EQ(TestDomainTree::SUCCESS,
+ dtree_expose_empty_node.insert(mem_sgmt_, Name("k.e.f"),
+ &dtnode));
+
+ // "g.h" is not a subtree root
+ EXPECT_EQ(TestDomainTree::EXACTMATCH,
+ dtree_expose_empty_node.find(Name("g.h"), &dtnode));
+ EXPECT_FALSE(dtnode->getFlag(TestDomainTreeNode::FLAG_SUBTREE_ROOT));
+
+ // fission the node "g.h"
+ EXPECT_EQ(TestDomainTree::ALREADYEXISTS,
+ dtree_expose_empty_node.insert(mem_sgmt_, Name("h"),
+ &dtnode));
+
+ // the node "h" (h.down_ -> "g") should not be a subtree root. "g"
+ // should be a subtree root.
+ EXPECT_FALSE(dtnode->getFlag(TestDomainTreeNode::FLAG_SUBTREE_ROOT));
+
+ // "g.h" should be a subtree root now.
+ EXPECT_EQ(TestDomainTree::EXACTMATCH,
+ dtree_expose_empty_node.find(Name("g.h"), &dtnode));
+ EXPECT_TRUE(dtnode->getFlag(TestDomainTreeNode::FLAG_SUBTREE_ROOT));
+}
+
+TEST_F(DomainTreeTest, additionalNodeFission) {
+ // These are additional nodeFission tests added by #2054's rewrite
+ // of DomainTree::nodeFission(). These test specific corner cases that
+ // are not covered by other tests.
+
+ // Insert "t.0" (which becomes the left child of its parent)
+ EXPECT_EQ(TestDomainTree::SUCCESS,
+ dtree_expose_empty_node.insert(mem_sgmt_, Name("t.0"),
+ &dtnode));
+
+ // "t.0" is not a subtree root
+ EXPECT_EQ(TestDomainTree::EXACTMATCH,
+ dtree_expose_empty_node.find(Name("t.0"), &dtnode));
+ EXPECT_FALSE(dtnode->getFlag(TestDomainTreeNode::FLAG_SUBTREE_ROOT));
+
+ // fission the node "t.0"
+ EXPECT_EQ(TestDomainTree::ALREADYEXISTS,
+ dtree_expose_empty_node.insert(mem_sgmt_, Name("0"),
+ &dtnode));
+
+ // the node "0" ("0".down_ -> "t") should not be a subtree root. "t"
+ // should be a subtree root.
+ EXPECT_FALSE(dtnode->getFlag(TestDomainTreeNode::FLAG_SUBTREE_ROOT));
+
+ // "t.0" should be a subtree root now.
+ EXPECT_EQ(TestDomainTree::EXACTMATCH,
+ dtree_expose_empty_node.find(Name("t.0"), &dtnode));
+ EXPECT_TRUE(dtnode->getFlag(TestDomainTreeNode::FLAG_SUBTREE_ROOT));
+}
+
+TEST_F(DomainTreeTest, findName) {
+ // find const dtnode
+ // exact match
+ EXPECT_EQ(TestDomainTree::EXACTMATCH, dtree.find(Name("a"), &cdtnode));
+ EXPECT_EQ(Name("a"), cdtnode->getName());
+
+ // not found
+ EXPECT_EQ(TestDomainTree::NOTFOUND, dtree.find(Name("d.e.f"), &cdtnode));
+ EXPECT_EQ(TestDomainTree::NOTFOUND, dtree.find(Name("y.d.e.f"), &cdtnode));
+ EXPECT_EQ(TestDomainTree::NOTFOUND, dtree.find(Name("x"), &cdtnode));
+ EXPECT_EQ(TestDomainTree::NOTFOUND, dtree.find(Name("m.n"), &cdtnode));
+
+ // if we expose empty node, we can get the empty node created during insert
+ EXPECT_EQ(TestDomainTree::EXACTMATCH,
+ dtree_expose_empty_node.find(Name("d.e.f"), &cdtnode));
+ EXPECT_EQ(TestDomainTree::EXACTMATCH,
+ dtree_expose_empty_node.find(Name("w.y.d.e.f"), &cdtnode));
+
+ // partial match
+ EXPECT_EQ(TestDomainTree::PARTIALMATCH, dtree.find(Name("m.b"), &cdtnode));
+ EXPECT_EQ(Name("b"), cdtnode->getName());
+ EXPECT_EQ(TestDomainTree::PARTIALMATCH,
+ dtree_expose_empty_node.find(Name("m.d.e.f"), &cdtnode));
+
+ // find dtnode
+ EXPECT_EQ(TestDomainTree::EXACTMATCH, dtree.find(Name("q.w.y.d.e.f"),
+ &dtnode));
+ EXPECT_EQ(Name("q"), dtnode->getName());
+}
+
+TEST_F(DomainTreeTest, findError) {
+ // For the version that takes a node chain, the chain must be empty.
+ TestDomainTreeNodeChain chain;
+ EXPECT_EQ(TestDomainTree::EXACTMATCH, dtree.find(Name("a"), &cdtnode,
+ chain));
+ // trying to reuse the same chain. it should result in an exception.
+ EXPECT_THROW(dtree.find(Name("a"), &cdtnode, chain),
+ BadValue);
+}
+
+TEST_F(DomainTreeTest, flags) {
+ EXPECT_EQ(TestDomainTree::SUCCESS, dtree.insert(mem_sgmt_,
+ Name("flags.example"),
+ &dtnode));
+
+ // by default, flags are all off
+ EXPECT_FALSE(dtnode->getFlag(TestDomainTreeNode::FLAG_CALLBACK));
+
+ // set operation, by default it enables the flag
+ dtnode->setFlag(TestDomainTreeNode::FLAG_CALLBACK);
+ EXPECT_TRUE(dtnode->getFlag(TestDomainTreeNode::FLAG_CALLBACK));
+
+ // try disable the flag explicitly
+ dtnode->setFlag(TestDomainTreeNode::FLAG_CALLBACK, false);
+ EXPECT_FALSE(dtnode->getFlag(TestDomainTreeNode::FLAG_CALLBACK));
+
+ // try enable the flag explicitly
+ dtnode->setFlag(TestDomainTreeNode::FLAG_CALLBACK, true);
+ EXPECT_TRUE(dtnode->getFlag(TestDomainTreeNode::FLAG_CALLBACK));
+
+ // setting an unknown flag will trigger an exception
+ EXPECT_THROW(dtnode->setFlag(static_cast<TestDomainTreeNode::Flags>(2), true),
+ isc::InvalidParameter);
+}
+
+bool
+testCallback(const TestDomainTreeNode&, bool* callback_checker) {
+ *callback_checker = true;
+ return (false);
+}
+
+template <typename T>
+void
+performCallbackTest(TestDomainTree& dtree,
+ util::MemorySegmentLocal& mem_sgmt,
+ const T& name_called,
+ const T& name_not_called)
+{
+ TestDomainTreeNode* dtnode;
+ const TestDomainTreeNode* cdtnode;
+
+ // by default callback isn't enabled
+ EXPECT_EQ(TestDomainTree::SUCCESS, dtree.insert(mem_sgmt,
+ Name("callback.example"),
+ &dtnode));
+ EXPECT_EQ(static_cast<int*>(NULL), dtnode->setData(new int(1)));
+ EXPECT_FALSE(dtnode->getFlag(TestDomainTreeNode::FLAG_CALLBACK));
+
+ // enable/re-disable callback
+ dtnode->setFlag(TestDomainTreeNode::FLAG_CALLBACK);
+ EXPECT_TRUE(dtnode->getFlag(TestDomainTreeNode::FLAG_CALLBACK));
+ dtnode->setFlag(TestDomainTreeNode::FLAG_CALLBACK, false);
+ EXPECT_FALSE(dtnode->getFlag(TestDomainTreeNode::FLAG_CALLBACK));
+
+ // enable again for subsequent tests
+ dtnode->setFlag(TestDomainTreeNode::FLAG_CALLBACK);
+ // add more levels below and above the callback node for partial match.
+ TestDomainTreeNode* subdtnode;
+ EXPECT_EQ(TestDomainTree::SUCCESS, dtree.insert(mem_sgmt,
+ Name("sub.callback.example"),
+ &subdtnode));
+ EXPECT_EQ(static_cast<int*>(NULL), subdtnode->setData(new int(2)));
+ TestDomainTreeNode* parentdtnode;
+ EXPECT_EQ(TestDomainTree::ALREADYEXISTS, dtree.insert(mem_sgmt,
+ Name("example"),
+ &parentdtnode));
+ // the child/parent nodes shouldn't "inherit" the callback flag.
+ // "dtnode" may be invalid due to the insertion, so we need to re-find
+ // it.
+ EXPECT_EQ(TestDomainTree::EXACTMATCH, dtree.find(Name("callback.example"),
+ &dtnode));
+ EXPECT_TRUE(dtnode->getFlag(TestDomainTreeNode::FLAG_CALLBACK));
+ EXPECT_FALSE(subdtnode->getFlag(TestDomainTreeNode::FLAG_CALLBACK));
+ EXPECT_FALSE(parentdtnode->getFlag(TestDomainTreeNode::FLAG_CALLBACK));
+
+ // check if the callback is called from find()
+ TestDomainTreeNodeChain node_path1;
+ bool callback_called = false;
+ EXPECT_EQ(TestDomainTree::EXACTMATCH,
+ dtree.find(name_called, &cdtnode, node_path1,
+ testCallback, &callback_called));
+ EXPECT_TRUE(callback_called);
+
+ // enable callback at the parent node, but it doesn't have data so
+ // the callback shouldn't be called.
+ TestDomainTreeNodeChain node_path2;
+ parentdtnode->setFlag(TestDomainTreeNode::FLAG_CALLBACK);
+ callback_called = false;
+ EXPECT_EQ(TestDomainTree::EXACTMATCH,
+ dtree.find(name_not_called, &cdtnode, node_path2,
+ testCallback, &callback_called));
+ EXPECT_FALSE(callback_called);
+}
+
+TEST_F(DomainTreeTest, callbackName) {
+ const Name n1("sub.callback.example");
+ const Name n2("callback.example");
+
+ performCallbackTest(dtree, mem_sgmt_, n1, n2);
+}
+
+TEST_F(DomainTreeTest, callbackLabelSequence) {
+ const Name n1("sub.callback.example");
+ const Name n2("callback.example");
+ const LabelSequence ls1(n1);
+ const LabelSequence ls2(n2);
+
+ performCallbackTest(dtree, mem_sgmt_, ls1, ls2);
+}
+
+TEST_F(DomainTreeTest, findInSubTree) {
+ // For the version that takes a node chain, the chain must be empty.
+ DomainTreeNodeChain<int> chain;
+ bool flag;
+
+ // Searching for a non-absolute (right-stripped) label sequence when
+ // chain is empty should throw.
+ const Name n0("w.y.d.e.f");
+ LabelSequence ls0(n0);
+ ls0.stripRight(1);
+ EXPECT_THROW(dtree_expose_empty_node.find(ls0, &cdtnode, chain,
+ testCallback, &flag),
+ isc::BadValue);
+
+ // First, find a sub-tree node
+ chain.clear();
+ const LabelSequence ls1(n0);
+ DomainTree<int>::Result result =
+ dtree_expose_empty_node.find(ls1, &cdtnode, chain,
+ testCallback, &flag);
+ EXPECT_EQ(DomainTree<int>::EXACTMATCH, result);
+ EXPECT_EQ(n0, chain.getAbsoluteName());
+
+ // Searching for an absolute label sequence when chain is already
+ // populated should throw.
+ const Name n2a("o");
+ const LabelSequence ls2a(n2a);
+ EXPECT_THROW(dtree_expose_empty_node.find(ls2a, &cdtnode, chain,
+ testCallback, &flag),
+ isc::BadValue);
+
+ // Now, find "o.w.y.d.e.f." by right-stripping the "w.y.d.e.f."
+ // suffix to "o" (non-absolute).
+ const Name n2("o.w.y.d.e.f");
+ LabelSequence ls2(n2);
+ ls2.stripRight(6);
+ EXPECT_EQ("o", ls2.toText());
+
+ result = dtree_expose_empty_node.find(ls2, &cdtnode, chain,
+ testCallback, &flag);
+ EXPECT_EQ(DomainTree<int>::EXACTMATCH, result);
+ EXPECT_EQ(n2, chain.getAbsoluteName());
+
+ // Another test. Start with "d.e.f." node.
+ chain.clear();
+ const Name n3("d.e.f");
+ const LabelSequence ls3(n3);
+ result =
+ dtree_expose_empty_node.find(ls3, &cdtnode, chain,
+ testCallback, &flag);
+ EXPECT_EQ(DomainTree<int>::EXACTMATCH, result);
+ EXPECT_EQ(n3, chain.getAbsoluteName());
+
+ // Now, find "o.w.y.d.e.f." by right-stripping the "w.y.d.e.f."
+ // suffix to "o.w.y" (non-absolute).
+ const Name n4("o.w.y.d.e.f");
+ LabelSequence ls4(n2);
+ ls4.stripRight(4);
+ EXPECT_EQ("o.w.y", ls4.toText());
+
+ result = dtree_expose_empty_node.find(ls4, &cdtnode, chain,
+ testCallback, &flag);
+ EXPECT_EQ(DomainTree<int>::EXACTMATCH, result);
+ EXPECT_EQ(n4, chain.getAbsoluteName());
+}
+
+TEST_F(DomainTreeTest, findInSubTreeSameLabelSequence) {
+ // For the version that takes a node chain, the chain must be empty.
+ DomainTreeNodeChain<int> chain;
+ bool flag;
+
+ const Name n1("c.g.h");
+
+ // First insert a "c.g.h." node.
+ dtree_expose_empty_node.insert(mem_sgmt_, n1, &dtnode);
+
+ /* Now, the tree looks like:
+ *
+ * .
+ * |
+ * b
+ * / \
+ * a d.e.f
+ * / | \____
+ * c | \
+ * | g.h
+ * | |
+ * w.y i
+ * / | \ / \
+ * x | z c k
+ * | |
+ * p j
+ * / \
+ * o q
+ */
+
+ // Make a non-absolute label sequence. We will search for this same
+ // sequence in two places in the tree.
+ LabelSequence ls1(n1);
+ ls1.stripRight(3);
+ EXPECT_EQ("c", ls1.toText());
+
+ // First, find "g.h."
+ const Name n2("g.h");
+ const LabelSequence ls2(n2);
+ DomainTree<int>::Result result =
+ dtree_expose_empty_node.find(ls2, &cdtnode, chain,
+ testCallback, &flag);
+ EXPECT_EQ(DomainTree<int>::EXACTMATCH, result);
+ EXPECT_EQ(n2, chain.getAbsoluteName());
+
+ // Now, find "c.g.h." by searching just the non-absolute ls1 label
+ // sequence.
+ result = dtree_expose_empty_node.find(ls1, &cdtnode, chain,
+ testCallback, &flag);
+ EXPECT_EQ(DomainTree<int>::EXACTMATCH, result);
+ EXPECT_EQ(n1, chain.getAbsoluteName());
+
+ // Now, find "." (the root node)
+ chain.clear();
+ const Name n3(".");
+ const LabelSequence ls3(n3);
+ result =
+ dtree_expose_empty_node.find(ls3, &cdtnode, chain,
+ testCallback, &flag);
+ EXPECT_EQ(DomainTree<int>::EXACTMATCH, result);
+ EXPECT_EQ(n3, chain.getAbsoluteName());
+
+ // Now, find "c." by searching just the non-absolute ls1 label
+ // sequence.
+ result = dtree_expose_empty_node.find(ls1, &cdtnode, chain,
+ testCallback, &flag);
+ EXPECT_EQ(DomainTree<int>::EXACTMATCH, result);
+ EXPECT_EQ(Name("c."), chain.getAbsoluteName());
+}
+
+TEST_F(DomainTreeTest, chainLevel) {
+ TestDomainTreeNodeChain chain;
+
+ // by default there should be no level in the chain.
+ EXPECT_EQ(0, chain.getLevelCount());
+
+ // Copy should be consistent
+ TestDomainTreeNodeChain chain2(chain);
+ EXPECT_EQ(chain.getLevelCount(), chain2.getLevelCount());
+
+ // insert one node to the tree and find it. there should be exactly
+ // one level in the chain.
+ TreeHolder tree_holder(mem_sgmt_, TestDomainTree::create(mem_sgmt_, true));
+ TestDomainTree& tree(*tree_holder.get());
+ Name node_name(Name::ROOT_NAME());
+ EXPECT_EQ(TestDomainTree::SUCCESS, tree.insert(mem_sgmt_, node_name,
+ &dtnode));
+ EXPECT_EQ(TestDomainTree::EXACTMATCH,
+ tree.find(node_name, &cdtnode, chain));
+ EXPECT_EQ(1, chain.getLevelCount());
+
+ // Copy should be consistent
+ TestDomainTreeNodeChain chain3(chain);
+ EXPECT_EQ(chain.getLevelCount(), chain3.getLevelCount());
+ EXPECT_EQ(chain.getAbsoluteName(), chain3.getAbsoluteName());
+
+ // Check the name of the found node (should have '.' as both non-absolute
+ // and absolute name
+ EXPECT_EQ(".", cdtnode->getLabels().toText());
+ EXPECT_EQ(".", cdtnode->getAbsoluteLabels(buf).toText());
+
+ /*
+ * Now creating a possibly deepest tree with MAX_LABELS levels.
+ * it should look like:
+ * (.)
+ * |
+ * a
+ * |
+ * a
+ * : (MAX_LABELS - 1) "a"'s
+ *
+ * then confirm that find() for the deepest name succeeds without any
+ * disruption, and the resulting chain has the expected level.
+ * Note that the root name (".") solely belongs to a single level,
+ * so the levels begin with 2.
+ */
+ for (unsigned int i = 2; i <= Name::MAX_LABELS; ++i) {
+ node_name = Name("a.").concatenate(node_name);
+ EXPECT_EQ(TestDomainTree::SUCCESS, tree.insert(mem_sgmt_, node_name,
+ &dtnode));
+ TestDomainTreeNodeChain found_chain;
+ EXPECT_EQ(TestDomainTree::EXACTMATCH,
+ tree.find(node_name, &cdtnode, found_chain));
+ EXPECT_EQ(i, found_chain.getLevelCount());
+
+ // The non-absolute name should only have the first label
+ EXPECT_EQ("a", cdtnode->getLabels().toText());
+ // But the absolute name should have all labels
+ EXPECT_EQ(node_name.toText(),
+ cdtnode->getAbsoluteLabels(buf).toText());
+ }
+
+ // Confirm the last inserted name has the possible maximum length with
+ // maximum label count. This confirms the dtree and chain level cannot
+ // be larger.
+ EXPECT_EQ(Name::MAX_LABELS, node_name.getLabelCount());
+ EXPECT_THROW(node_name.concatenate(Name("a.")), TooLongName);
+}
+
+TEST_F(DomainTreeTest, getAbsoluteNameError) {
+ // an empty chain isn't allowed.
+ TestDomainTreeNodeChain chain;
+ EXPECT_THROW(chain.getAbsoluteName(), BadValue);
+}
+
+/*
+ * The domain order should be:
+ * ., a, b, c, d.e.f, x.d.e.f, w.y.d.e.f, o.w.y.d.e.f, p.w.y.d.e.f,
+ * q.w.y.d.e.f, z.d.e.f, j.z.d.e.f, g.h, i.g.h, k.g.h
+ * . (no data, can't be found)
+ * |
+ * b
+ * / \
+ * a d.e.f
+ * / | \
+ * c | g.h
+ * | |
+ * w.y i
+ * / | \ \
+ * x | z k
+ * | |
+ * p j
+ * / \
+ * o q
+ */
+const char* const names[] = {
+ "a", "b", "c", "d.e.f", "x.d.e.f", "w.y.d.e.f", "o.w.y.d.e.f",
+ "p.w.y.d.e.f", "q.w.y.d.e.f", "z.d.e.f", "j.z.d.e.f",
+ "g.h", "i.g.h", "k.g.h"};
+const size_t name_count(sizeof(names) / sizeof(*names));
+
+const char* const upper_node_names[] = {
+ ".", ".", ".", ".", "d.e.f", "d.e.f", "w.y.d.e.f",
+ "w.y.d.e.f", "w.y.d.e.f", "d.e.f", "z.d.e.f",
+ ".", "g.h", "g.h"};
+
+TEST_F(DomainTreeTest, getUpperNode) {
+ TestDomainTreeNodeChain node_path;
+ const TestDomainTreeNode* node = NULL;
+ EXPECT_EQ(TestDomainTree::EXACTMATCH,
+ dtree_expose_empty_node.find(Name(names[0]),
+ &node,
+ node_path));
+ for (int i = 0; i < name_count; ++i) {
+ EXPECT_NE(static_cast<void*>(NULL), node);
+
+ const TestDomainTreeNode* upper_node = node->getUpperNode();
+ if (upper_node_names[i] != NULL) {
+ const TestDomainTreeNode* upper_node2 = NULL;
+ EXPECT_EQ(TestDomainTree::EXACTMATCH,
+ dtree_expose_empty_node.find(Name(upper_node_names[i]),
+ &upper_node2));
+ EXPECT_NE(static_cast<void*>(NULL), upper_node2);
+ EXPECT_EQ(upper_node, upper_node2);
+ } else {
+ EXPECT_EQ(static_cast<void*>(NULL), upper_node);
+ }
+
+ node = dtree_expose_empty_node.nextNode(node_path);
+ }
+
+ // We should have reached the end of the tree.
+ EXPECT_EQ(static_cast<void*>(NULL), node);
+}
+
+
+#if 0
+// Disabled and kept still, for use in case we make getSubTreeRoot() a
+// public function again.
+
+const char* const subtree_root_node_names[] = {
+ "b", "b", "b", "b", "w.y.d.e.f", "w.y.d.e.f", "p.w.y.d.e.f",
+ "p.w.y.d.e.f", "p.w.y.d.e.f", "w.y.d.e.f", "j.z.d.e.f",
+ "b", "i.g.h", "i.g.h"};
+
+TEST_F(DomainTreeTest, getSubTreeRoot) {
+ TestDomainTreeNodeChain node_path;
+ const TestDomainTreeNode* node = NULL;
+ EXPECT_EQ(TestDomainTree::EXACTMATCH,
+ dtree_expose_empty_node.find(Name(names[0]),
+ &node,
+ node_path));
+ for (int i = 0; i < name_count; ++i) {
+ EXPECT_NE(static_cast<void*>(NULL), node);
+
+ const TestDomainTreeNode* sr_node = node->getSubTreeRoot();
+ if (subtree_root_node_names[i] != NULL) {
+ const TestDomainTreeNode* sr_node2 = NULL;
+ EXPECT_EQ(TestDomainTree::EXACTMATCH,
+ dtree_expose_empty_node.find(Name(subtree_root_node_names[i]),
+ &sr_node2));
+ EXPECT_NE(static_cast<void*>(NULL), sr_node2);
+ EXPECT_EQ(sr_node, sr_node2);
+ } else {
+ EXPECT_EQ(static_cast<void*>(NULL), sr_node);
+ }
+
+ node = dtree_expose_empty_node.nextNode(node_path);
+ }
+
+ // We should have reached the end of the tree.
+ EXPECT_EQ(static_cast<void*>(NULL), node);
+}
+
+#endif // disabled getSubTreeRoot()
+
+
+TEST_F(DomainTreeTest, nextNode) {
+ TestDomainTreeNodeChain node_path;
+ const TestDomainTreeNode* node = NULL;
+ EXPECT_EQ(TestDomainTree::EXACTMATCH,
+ dtree.find(Name(names[0]), &node, node_path));
+ for (int i = 0; i < name_count; ++i) {
+ EXPECT_NE(static_cast<void*>(NULL), node);
+ EXPECT_EQ(Name(names[i]), node_path.getAbsoluteName());
+ node = dtree.nextNode(node_path);
+ }
+
+ // We should have reached the end of the tree.
+ EXPECT_EQ(static_cast<void*>(NULL), node);
+}
+
+// Just walk using previousNode until the beginning of the tree and check it is
+// OK
+//
+// dtree - the tree to walk
+// node - result of previous call to find(), starting position of the walk
+// node_path - the path from the previous call to find(), will be modified
+// chain_length - the number of names that should be in the chain to be walked
+// (0 means it should be empty, 3 means 'a', 'b' and 'c' should be there -
+// this is always from the beginning of the names[] list).
+// skip_first - if this is false, the node should already contain the node with
+// the first name of the chain. If it is true, the node should be NULL
+// (true is for finds that return no match, false for the ones that return
+// match)
+void
+previousWalk(TestDomainTree& dtree, const TestDomainTreeNode* node,
+ TestDomainTreeNodeChain& node_path, size_t chain_length,
+ bool skip_first)
+{
+ if (skip_first) {
+ // If the first is not found, this is supposed to be NULL and we skip
+ // it in our checks.
+ EXPECT_EQ(static_cast<void*>(NULL), node);
+ node = dtree.previousNode(node_path);
+ }
+ for (size_t i(chain_length); i > 0; --i) {
+ EXPECT_NE(static_cast<void*>(NULL), node);
+ EXPECT_EQ(Name(names[i - 1]), node_path.getAbsoluteName());
+ // Find the node at the path and check the value is the same
+ // (that it really returns the correct corresponding node)
+ //
+ // The "empty" nodes can not be found
+ if (node->getData()) {
+ const TestDomainTreeNode* node2(NULL);
+ TestDomainTreeNodeChain node_path2;
+ EXPECT_EQ(TestDomainTree::EXACTMATCH,
+ dtree.find(Name(names[i - 1]), &node2, node_path2));
+ EXPECT_EQ(node, node2);
+ }
+ node = dtree.previousNode(node_path);
+ }
+
+ // We should have reached the start of the tree.
+ ASSERT_NE(static_cast<void*>(NULL), node);
+ EXPECT_EQ(".", node->getLabels().toText());
+
+ // With one more call it results in NULL
+ node = dtree.previousNode(node_path);
+ EXPECT_EQ(static_cast<void*>(NULL), node);
+
+ // Calling previousNode() yet again should still return NULL without
+ // fail.
+ node = dtree.previousNode(node_path);
+ EXPECT_EQ(static_cast<void*>(NULL), node);
+}
+
+// Check the previousNode
+TEST_F(DomainTreeTest, previousNode) {
+ // First, iterate the whole tree from the end to the beginning.
+ TestDomainTreeNodeChain node_path;
+ EXPECT_THROW(dtree.previousNode(node_path), isc::BadValue) <<
+ "Throw before a search was done on the path";
+ const TestDomainTreeNode* node(NULL);
+ {
+ SCOPED_TRACE("Iterate through");
+ EXPECT_EQ(TestDomainTree::EXACTMATCH,
+ dtree.find(Name(names[name_count - 1]), &node, node_path));
+ previousWalk(dtree, node, node_path, name_count, false);
+ node = NULL;
+ node_path.clear();
+ }
+
+ {
+ SCOPED_TRACE("Iterate from the middle");
+ // Now, start somewhere in the middle, but within the real node.
+ EXPECT_EQ(TestDomainTree::EXACTMATCH,
+ dtree.find(Name(names[4]), &node, node_path));
+ previousWalk(dtree, node, node_path, 5, false);
+ node = NULL;
+ node_path.clear();
+ }
+
+ {
+ SCOPED_TRACE("Start at the first");
+ // If we start at the lowest (which is "a"), we get to the beginning
+ // right away.
+ EXPECT_EQ(TestDomainTree::EXACTMATCH,
+ dtree.find(Name(names[0]), &node, node_path));
+ EXPECT_NE(static_cast<void*>(NULL), node);
+ node = dtree.previousNode(node_path);
+ ASSERT_NE(static_cast<void*>(NULL), node);
+ EXPECT_EQ(".", node->getLabels().toText());
+ node = NULL;
+ node_path.clear();
+ }
+
+ {
+ SCOPED_TRACE("Start before the first");
+ // If we start before the lowest (. < 0. < a.), we should not get a
+ // node. Its previous node should be the root.
+ EXPECT_EQ(TestDomainTree::NOTFOUND,
+ dtree.find<void*>(Name("0"), &node, node_path, NULL, NULL));
+ EXPECT_EQ(static_cast<void*>(NULL), node);
+ node = dtree.previousNode(node_path);
+ ASSERT_NE(static_cast<void*>(NULL), node);
+ EXPECT_EQ(".", node->getLabels().toText());
+ node = NULL;
+ node_path.clear();
+ }
+
+ {
+ SCOPED_TRACE("Start after the last");
+ EXPECT_EQ(TestDomainTree::NOTFOUND,
+ dtree.find(Name("z"), &node, node_path));
+ previousWalk(dtree, node, node_path, name_count, true);
+ node = NULL;
+ node_path.clear();
+ }
+
+ {
+ SCOPED_TRACE("Start below a leaf");
+ // We exit a leaf by going down. We should start by the one
+ // we exited - 'c' (actually, we should get it by the find, as partial
+ // match).
+ EXPECT_EQ(TestDomainTree::PARTIALMATCH,
+ dtree.find(Name("b.c"), &node, node_path));
+ previousWalk(dtree, node, node_path, 3, false);
+ node = NULL;
+ node_path.clear();
+ }
+
+ {
+ SCOPED_TRACE("Start to the right of a leaf");
+ // When searching for this, we exit the 'x' node to the right side,
+ // so we should go x afterwards.
+
+ // The d.e.f is empty node, so it is hidden by find. Therefore NOTFOUND
+ // and not PARTIALMATCH.
+ EXPECT_EQ(TestDomainTree::NOTFOUND,
+ dtree.find(Name("xy.d.e.f"), &node, node_path));
+ previousWalk(dtree, node, node_path, 5, true);
+ node = NULL;
+ node_path.clear();
+ }
+
+ {
+ SCOPED_TRACE("Start to the left of a leaf");
+ // This is similar to the previous, but we exit the 'z' leaf to the
+ // left side, so should not visit z at all then.
+
+ // The d.e.f is empty node, so it is hidden by find. Therefore NOTFOUND
+ // and not PARTIALMATCH.
+ EXPECT_EQ(TestDomainTree::NOTFOUND,
+ dtree.find(Name("yz.d.e.f"), &node, node_path));
+ previousWalk(dtree, node, node_path, 9, true);
+ node = NULL;
+ node_path.clear();
+ }
+
+ {
+ SCOPED_TRACE("Start to the right of a parent");
+ // When searching for this, we exit the 'g.h' node to the right
+ // side, so we should go to g.h's children afterwards.
+
+ // 'g.h' is an empty node, so we get a NOTFOUND and not
+ // PARTIALMATCH.
+ EXPECT_EQ(TestDomainTree::NOTFOUND,
+ dtree.find(Name("x.h"), &node, node_path));
+ // 'g.h' is the COMMONANCESTOR.
+ EXPECT_EQ(node_path.getLastComparedNode()->getName(), Name("g.h"));
+ EXPECT_EQ(NameComparisonResult::COMMONANCESTOR,
+ node_path.getLastComparisonResult().getRelation());
+ // find() exits to the right of 'g.h'
+ EXPECT_GT(node_path.getLastComparisonResult().getOrder(), 0);
+ // We then descend into 'i.g.h' and walk all the nodes in the
+ // tree.
+ previousWalk(dtree, node, node_path, name_count, true);
+ node = NULL;
+ node_path.clear();
+ }
+
+ {
+ SCOPED_TRACE("Start inside a wrong node");
+ // The d.e.f is a single node, but we want only part of it. We
+ // should start iterating before it.
+ EXPECT_EQ(TestDomainTree::NOTFOUND,
+ dtree.find(Name("e.f"), &node, node_path));
+ previousWalk(dtree, node, node_path, 3, true);
+ node = NULL;
+ node_path.clear();
+ }
+
+ {
+ SCOPED_TRACE("Lookup in empty tree");
+ // Just check it doesn't crash, etc.
+ TreeHolder tree_holder(mem_sgmt_, TestDomainTree::create(mem_sgmt_));
+ TestDomainTree& empty_tree(*tree_holder.get());
+ EXPECT_EQ(TestDomainTree::NOTFOUND,
+ empty_tree.find(Name("x"), &node, node_path));
+ EXPECT_EQ(static_cast<void*>(NULL), node);
+ EXPECT_EQ(static_cast<void*>(NULL),
+ empty_tree.previousNode(node_path));
+ node = NULL;
+ node_path.clear();
+ }
+}
+
+TEST_F(DomainTreeTest, largestNode) {
+ cdtnode = dtree.largestNode();
+ EXPECT_EQ(Name("k"), cdtnode->getName());
+
+ // Check for largest node in an empty tree.
+ TreeHolder empty_tree_holder
+ (mem_sgmt_, TestDomainTree::create(mem_sgmt_));
+ TestDomainTree& empty_tree(*empty_tree_holder.get());
+ EXPECT_EQ(static_cast<void*>(NULL), empty_tree.largestNode());
+}
+
+TEST_F(DomainTreeTest, nextNodeError) {
+ // Empty chain for nextNode() is invalid.
+ TestDomainTreeNodeChain chain;
+ EXPECT_THROW(dtree.nextNode(chain), BadValue);
+}
+
+// A helper function for getLastComparedNode() below.
+void
+comparisonChecks(const TestDomainTreeNodeChain& chain,
+ int expected_order, int expected_common_labels,
+ NameComparisonResult::NameRelation expected_reln)
+{
+ if (expected_order > 0) {
+ EXPECT_LT(0, chain.getLastComparisonResult().getOrder());
+ } else if (expected_order < 0) {
+ EXPECT_GT(0, chain.getLastComparisonResult().getOrder());
+ } else {
+ EXPECT_EQ(0, chain.getLastComparisonResult().getOrder());
+ }
+ EXPECT_EQ(expected_common_labels,
+ chain.getLastComparisonResult().getCommonLabels());
+ EXPECT_EQ(expected_reln,
+ chain.getLastComparisonResult().getRelation());
+}
+
+TEST_F(DomainTreeTest, getLastComparedNode) {
+ TestDomainTree& tree = dtree_expose_empty_node; // use the "empty OK" mode
+ TestDomainTreeNodeChain chain;
+
+ // initially there should be no 'last compared'.
+ EXPECT_EQ(static_cast<void*>(NULL), chain.getLastComparedNode());
+
+ // A search for an empty tree should result in no 'last compared', too.
+ TreeHolder tree_holder(mem_sgmt_, TestDomainTree::create(mem_sgmt_));
+ TestDomainTree& empty_tree(*tree_holder.get());
+ EXPECT_EQ(TestDomainTree::NOTFOUND,
+ empty_tree.find(Name("a"), &cdtnode, chain));
+ EXPECT_EQ(static_cast<void*>(NULL), chain.getLastComparedNode());
+ chain.clear();
+
+ const TestDomainTreeNode* expected_node = NULL;
+
+ // Exact match case. The returned node should be last compared.
+ EXPECT_EQ(TestDomainTree::EXACTMATCH,
+ tree.find(Name("x.d.e.f"), &expected_node, chain));
+ EXPECT_EQ(expected_node, chain.getLastComparedNode());
+ // 1 = # labels of "x" (note: excluding ".")
+ comparisonChecks(chain, 0, 1, NameComparisonResult::EQUAL);
+ chain.clear();
+
+ // Partial match, search stopped at the matching node, which should be
+ // the last compared node.
+ EXPECT_EQ(TestDomainTree::EXACTMATCH,
+ tree.find(Name("k.g.h"), &expected_node));
+ EXPECT_EQ(TestDomainTree::PARTIALMATCH,
+ tree.find(Name("x.k.g.h"), &cdtnode, chain));
+ EXPECT_EQ(expected_node, chain.getLastComparedNode());
+ // k.g.h < x.k.g.h, 1 = # labels of "k"
+ comparisonChecks(chain, 1, 1, NameComparisonResult::SUBDOMAIN);
+ chain.clear();
+
+ // Partial match, search stopped in the subtree below the matching node
+ // after following a left branch.
+ EXPECT_EQ(TestDomainTree::EXACTMATCH,
+ tree.find(Name("x.d.e.f"), &expected_node));
+ EXPECT_EQ(TestDomainTree::PARTIALMATCH,
+ tree.find(Name("a.d.e.f"), &cdtnode, chain));
+ EXPECT_EQ(expected_node, chain.getLastComparedNode());
+ // a < x, no common labels
+ comparisonChecks(chain, -1, 0, NameComparisonResult::NONE);
+ chain.clear();
+
+ // Partial match, search stopped in the subtree below the matching node
+ // after following a right branch.
+ EXPECT_EQ(TestDomainTree::EXACTMATCH,
+ tree.find(Name("z.d.e.f"), &expected_node));
+ EXPECT_EQ(TestDomainTree::PARTIALMATCH,
+ tree.find(Name("zz.d.e.f"), &cdtnode, chain));
+ EXPECT_EQ(expected_node, chain.getLastComparedNode());
+ // zz > z, no common label
+ comparisonChecks(chain, 1, 0, NameComparisonResult::NONE);
+ chain.clear();
+
+ // Partial match, search stopped at a node for a super domain of the
+ // search name in the subtree below the matching node.
+ EXPECT_EQ(TestDomainTree::EXACTMATCH,
+ tree.find(Name("w.y.d.e.f"), &expected_node));
+ EXPECT_EQ(TestDomainTree::PARTIALMATCH,
+ tree.find(Name("y.d.e.f"), &cdtnode, chain));
+ EXPECT_EQ(expected_node, chain.getLastComparedNode());
+ // y < w.y, 1 = # labels of "y"
+ comparisonChecks(chain, -1, 1, NameComparisonResult::SUPERDOMAIN);
+ chain.clear();
+
+ // Partial match, search stopped at a node that share a common ancestor
+ // with the search name in the subtree below the matching node.
+ // (the expected node is the same as the previous case)
+ EXPECT_EQ(TestDomainTree::PARTIALMATCH,
+ tree.find(Name("z.y.d.e.f"), &cdtnode, chain));
+ EXPECT_EQ(expected_node, chain.getLastComparedNode());
+ // z.y > w.y, 1 = # labels of "y"
+ comparisonChecks(chain, 1, 1, NameComparisonResult::COMMONANCESTOR);
+ chain.clear();
+
+ // Search stops in the highest level (under ".") after following a left
+ // branch. (find() still returns PARTIALMATCH due to the top level ".")
+ EXPECT_EQ(TestDomainTree::EXACTMATCH, tree.find(Name("c"), &expected_node));
+ EXPECT_EQ(TestDomainTree::PARTIALMATCH,
+ tree.find(Name("bb"), &cdtnode, chain));
+ EXPECT_EQ(expected_node, chain.getLastComparedNode());
+ // bb < c, no common label
+ comparisonChecks(chain, -1, 0, NameComparisonResult::NONE);
+ chain.clear();
+
+ // Search stops in the highest level (under ".") after following a right
+ // branch. (the expected node is the same as the previous case)
+ EXPECT_EQ(TestDomainTree::PARTIALMATCH,
+ tree.find(Name("d"), &cdtnode, chain));
+ EXPECT_EQ(expected_node, chain.getLastComparedNode());
+ // d > c, no common label
+ comparisonChecks(chain, 1, 0, NameComparisonResult::NONE);
+ chain.clear();
+}
+
+TEST_F(DomainTreeTest, dumpTree) {
+ std::ostringstream str;
+ std::ostringstream str2;
+ dtree.dumpTree(str);
+ str2 << "tree has 15 node(s)\n"
+ ". (black) [invisible] [subtreeroot]\n"
+ " begin down from .\n"
+ " b (black) [subtreeroot]\n"
+ " a (black)\n"
+ " NULL\n"
+ " NULL\n"
+ " d.e.f (black) [invisible]\n"
+ " begin down from d.e.f\n"
+ " w.y (black) [invisible] [subtreeroot]\n"
+ " begin down from w.y\n"
+ " p (black) [subtreeroot]\n"
+ " o (red)\n"
+ " NULL\n"
+ " NULL\n"
+ " q (red)\n"
+ " NULL\n"
+ " NULL\n"
+ " end down from w.y\n"
+ " x (red)\n"
+ " NULL\n"
+ " NULL\n"
+ " z (red)\n"
+ " begin down from z\n"
+ " j (black) [subtreeroot]\n"
+ " NULL\n"
+ " NULL\n"
+ " end down from z\n"
+ " NULL\n"
+ " NULL\n"
+ " end down from d.e.f\n"
+ " c (red)\n"
+ " NULL\n"
+ " NULL\n"
+ " g.h (red)\n"
+ " begin down from g.h\n"
+ " i (black) [subtreeroot]\n"
+ " NULL\n"
+ " k (red)\n"
+ " NULL\n"
+ " NULL\n"
+ " end down from g.h\n"
+ " NULL\n"
+ " NULL\n"
+ " end down from .\n"
+ " NULL\n"
+ " NULL\n";
+ EXPECT_EQ(str2.str(), str.str());
+}
+
+TEST_F(DomainTreeTest, swap) {
+ // Store info about the first tree
+ std::ostringstream str1;
+ dtree.dumpTree(str1);
+ size_t count1(dtree.getNodeCount());
+
+ // Create second one and store state
+ TreeHolder tree_holder(mem_sgmt_, TestDomainTree::create(mem_sgmt_));
+ TestDomainTree& tree2(*tree_holder.get());
+ TestDomainTreeNode* node;
+ tree2.insert(mem_sgmt_, Name("second"), &node);
+ std::ostringstream str2;
+ tree2.dumpTree(str2);
+
+ // Swap them
+ ASSERT_NO_THROW(tree2.swap(dtree));
+
+ // Check their sizes
+ ASSERT_EQ(1, dtree.getNodeCount());
+ ASSERT_EQ(count1, tree2.getNodeCount());
+
+ // And content
+ std::ostringstream out;
+ dtree.dumpTree(out);
+ ASSERT_EQ(str2.str(), out.str());
+ out.str("");
+ tree2.dumpTree(out);
+ ASSERT_EQ(str1.str(), out.str());
+}
+
+// Matching in the "root zone" may be special (e.g. there's no parent,
+// any domain names should be considered a subdomain of it), so it makes
+// sense to test cases with the root zone explicitly.
+TEST_F(DomainTreeTest, root) {
+ TreeHolder tree_holder(mem_sgmt_, TestDomainTree::create(mem_sgmt_));
+ TestDomainTree& root(*tree_holder.get());
+ root.insert(mem_sgmt_, Name::ROOT_NAME(), &dtnode);
+ EXPECT_EQ(static_cast<int*>(NULL), dtnode->setData(new int(1)));
+
+ EXPECT_EQ(TestDomainTree::EXACTMATCH,
+ root.find(Name::ROOT_NAME(), &cdtnode));
+ EXPECT_EQ(dtnode, cdtnode);
+ EXPECT_EQ(TestDomainTree::PARTIALMATCH,
+ root.find(Name("example.com"), &cdtnode));
+ EXPECT_EQ(dtnode, cdtnode);
+
+ // Insert a new name that better matches the query name. find() should
+ // find the better one.
+ root.insert(mem_sgmt_, Name("com"), &dtnode);
+ EXPECT_EQ(static_cast<int*>(NULL), dtnode->setData(new int(2)));
+ EXPECT_EQ(TestDomainTree::PARTIALMATCH,
+ root.find(Name("example.com"), &cdtnode));
+ EXPECT_EQ(dtnode, cdtnode);
+
+ // Perform the same tests for the tree that allows matching against empty
+ // nodes.
+ TreeHolder tree_holder_emptyok(mem_sgmt_,
+ TestDomainTree::create(mem_sgmt_, true));
+ TestDomainTree& root_emptyok(*tree_holder_emptyok.get());
+ root_emptyok.insert(mem_sgmt_, Name::ROOT_NAME(), &dtnode);
+ EXPECT_EQ(TestDomainTree::EXACTMATCH,
+ root_emptyok.find(Name::ROOT_NAME(), &cdtnode));
+ EXPECT_EQ(dtnode, cdtnode);
+ EXPECT_EQ(TestDomainTree::PARTIALMATCH,
+ root_emptyok.find(Name("example.com"), &cdtnode));
+ EXPECT_EQ(dtnode, cdtnode);
+
+ root.insert(mem_sgmt_, Name("com"), &dtnode);
+ EXPECT_EQ(TestDomainTree::PARTIALMATCH,
+ root.find(Name("example.com"), &cdtnode));
+ EXPECT_EQ(dtnode, cdtnode);
+}
+
+TEST_F(DomainTreeTest, getAbsoluteLabels) {
+ // The full absolute names of the nodes in the tree
+ // with the addition of the explicit root node
+ const char* const domain_names[] = {
+ "c", "b", "a", "x.d.e.f", "z.d.e.f", "g.h", "i.g.h", "o.w.y.d.e.f",
+ "j.z.d.e.f", "p.w.y.d.e.f", "q.w.y.d.e.f", "k.g.h"};
+ // The names of the nodes themselves, as they end up in the tree
+ const char* const first_labels[] = {
+ "c", "b", "a", "x", "z", "g.h", "i", "o",
+ "j", "p", "q", "k"};
+
+ const int name_count = sizeof(domain_names) / sizeof(domain_names[0]);
+ for (int i = 0; i < name_count; ++i) {
+ EXPECT_EQ(TestDomainTree::EXACTMATCH, dtree.find(Name(domain_names[i]),
+ &cdtnode));
+
+ // First make sure the names themselves are not absolute
+ const LabelSequence ls(cdtnode->getLabels());
+ EXPECT_EQ(first_labels[i], ls.toText());
+ EXPECT_FALSE(ls.isAbsolute());
+
+ // Now check the absolute names
+ const LabelSequence abs_ls(cdtnode->getAbsoluteLabels(buf));
+ EXPECT_EQ(Name(domain_names[i]).toText(), abs_ls.toText());
+ EXPECT_TRUE(abs_ls.isAbsolute());
+ }
+
+ // Explicitly add and find a root node, to see that getAbsoluteLabels
+ // also works when getLabels() already returns an absolute LabelSequence
+ dtree.insert(mem_sgmt_, Name("."), &dtnode);
+ dtnode->setData(new int(1));
+
+ EXPECT_EQ(TestDomainTree::EXACTMATCH, dtree.find(Name("."), &cdtnode));
+
+ EXPECT_TRUE(cdtnode->getLabels().isAbsolute());
+ EXPECT_EQ(".", cdtnode->getLabels().toText());
+ EXPECT_TRUE(cdtnode->getAbsoluteLabels(buf).isAbsolute());
+ EXPECT_EQ(".", cdtnode->getAbsoluteLabels(buf).toText());
+}
+}
diff --git a/src/lib/datasrc/tests/memory/memory_client_unittest.cc b/src/lib/datasrc/tests/memory/memory_client_unittest.cc
new file mode 100644
index 0000000..58979a4
--- /dev/null
+++ b/src/lib/datasrc/tests/memory/memory_client_unittest.cc
@@ -0,0 +1,777 @@
+// 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 <exceptions/exceptions.h>
+
+#include <util/memory_segment_local.h>
+
+#include <dns/name.h>
+#include <dns/rrclass.h>
+#include <dns/masterload.h>
+#include <dns/nsec3hash.h>
+#include <dns/rdata.h>
+#include <dns/rdataclass.h>
+#include <dns/rrsetlist.h>
+#include <dns/rrttl.h>
+#include <dns/masterload.h>
+
+#include <datasrc/result.h>
+#include <datasrc/data_source.h>
+#include <datasrc/memory/zone_data.h>
+#include <datasrc/memory/zone_table.h>
+#include <datasrc/memory/memory_client.h>
+
+#include <testutils/dnsmessage_test.h>
+
+#include "memory_segment_test.h"
+
+#include <gtest/gtest.h>
+
+#include <new> // for bad_alloc
+
+using namespace isc::dns;
+using namespace isc::dns::rdata;
+using namespace isc::datasrc;
+using namespace isc::datasrc::memory;
+using namespace isc::testutils;
+
+namespace {
+
+const char* rrset_data[] = {
+ "example.org. 3600 IN SOA ns1.example.org. bugs.x.w.example.org. "
+ "68 3600 300 3600000 3600",
+ "a.example.org. 3600 IN A 192.168.0.1\n" // RRset containing 2 RRs
+ "a.example.org. 3600 IN A 192.168.0.2",
+ "a.example.org. 3600 IN RRSIG A 5 3 3600 20150420235959 20051021000000 "
+ "40430 example.org. FAKEFAKE",
+ "a.example.org. 3600 IN MX 10 mail.example.org.",
+ "a.example.org. 3600 IN RRSIG MX 5 3 3600 20150420235959 20051021000000 "
+ "40430 example.org. FAKEFAKEFAKE",
+ NULL
+};
+
+// RRsets that emulate the "separate RRs" mode.
+const char* rrset_data_separated[] = {
+ "example.org. 3600 IN SOA ns1.example.org. bugs.x.w.example.org. "
+ "68 3600 300 3600000 3600",
+ "a.example.org. 3600 IN A 192.168.0.1", // these two belong to the same
+ "a.example.org. 3600 IN A 192.168.0.2", // RRset, but are separated.
+ NULL
+};
+
+// Similar to the previous one, but with separated RRSIGs
+const char* rrset_data_sigseparated[] = {
+ "example.org. 3600 IN SOA ns1.example.org. bugs.x.w.example.org. "
+ "68 3600 300 3600000 3600",
+ "a.example.org. 3600 IN A 192.168.0.1",
+ "a.example.org. 3600 IN RRSIG A 5 3 3600 20150420235959 20051021000000 "
+ "40430 example.org. FAKEFAKE",
+ "a.example.org. 3600 IN RRSIG A 5 3 3600 20150420235959 20051021000000 "
+ "53535 example.org. FAKEFAKE",
+ NULL
+};
+
+class MockIterator : public ZoneIterator {
+private:
+ MockIterator(const char** rrset_data_ptr, bool pass_empty_rrsig) :
+ rrset_data_ptr_(rrset_data_ptr),
+ pass_empty_rrsig_(pass_empty_rrsig)
+ {
+ }
+
+ const char** rrset_data_ptr_;
+ // If true, emulate an unexpected bogus case where an RRSIG RRset is
+ // returned without the RDATA. For brevity allow tests tweak it directly.
+ bool pass_empty_rrsig_;
+
+public:
+ virtual ConstRRsetPtr getNextRRset() {
+ if (*rrset_data_ptr_ == NULL) {
+ return (ConstRRsetPtr());
+ }
+
+ ConstRRsetPtr result(textToRRset(*rrset_data_ptr_,
+ RRClass::IN(), Name("example.org")));
+ if (pass_empty_rrsig_ && result->getType() == RRType::RRSIG()) {
+ result.reset(new RRset(result->getName(), result->getClass(),
+ result->getType(), result->getTTL()));
+ }
+ ++rrset_data_ptr_;
+
+ return (result);
+ }
+
+ virtual ConstRRsetPtr getSOA() const {
+ isc_throw(isc::NotImplemented, "Not implemented");
+ }
+
+ static ZoneIteratorPtr makeIterator(const char** rrset_data_ptr,
+ bool pass_empty_rrsig = false)
+ {
+ return (ZoneIteratorPtr(new MockIterator(rrset_data_ptr,
+ pass_empty_rrsig)));
+ }
+};
+
+class MemoryClientTest : public ::testing::Test {
+protected:
+ MemoryClientTest() : zclass_(RRClass::IN()),
+ client_(new InMemoryClient(mem_sgmt_, zclass_))
+ {}
+ ~MemoryClientTest() {
+ if (client_ != NULL) {
+ delete client_;
+ }
+ }
+ void TearDown() {
+ delete client_;
+ client_ = NULL;
+ EXPECT_TRUE(mem_sgmt_.allMemoryDeallocated()); // catch any leak here.
+ }
+ const RRClass zclass_;
+ test::MemorySegmentTest mem_sgmt_;
+ InMemoryClient* client_;
+};
+
+TEST_F(MemoryClientTest, loadRRsetDoesntMatchOrigin) {
+ // Attempting to load example.org to example.com zone should result
+ // in an exception.
+ EXPECT_THROW(client_->load(Name("example.com"),
+ TEST_DATA_DIR "/example.org-empty.zone"),
+ MasterLoadError);
+}
+
+TEST_F(MemoryClientTest, loadErrorsInParsingZoneMustNotLeak1) {
+ // Attempting to load broken example.org zone should result in an
+ // exception. This should not leak ZoneData and other such
+ // allocations.
+ EXPECT_THROW(client_->load(Name("example.org"),
+ TEST_DATA_DIR "/example.org-broken1.zone"),
+ MasterLoadError);
+ // Teardown checks for memory segment leaks
+}
+
+TEST_F(MemoryClientTest, loadErrorsInParsingZoneMustNotLeak2) {
+ // Attempting to load broken example.org zone should result in an
+ // exception. This should not leak ZoneData and other such
+ // allocations.
+ EXPECT_THROW(client_->load(Name("example.org"),
+ TEST_DATA_DIR "/example.org-broken2.zone"),
+ MasterLoadError);
+ // Teardown checks for memory segment leaks
+}
+
+TEST_F(MemoryClientTest, loadNonExistentZoneFile) {
+ EXPECT_THROW(client_->load(Name("example.org"),
+ TEST_DATA_DIR "/somerandomfilename"),
+ MasterLoadError);
+ // Teardown checks for memory segment leaks
+}
+
+TEST_F(MemoryClientTest, loadEmptyZoneFileThrows) {
+ // When an empty zone file is loaded, the origin doesn't even have
+ // an SOA RR. This condition should be avoided, and hence load()
+ // should throw when an empty zone is loaded.
+
+ EXPECT_EQ(0, client_->getZoneCount());
+
+ EXPECT_THROW(client_->load(Name("."),
+ TEST_DATA_DIR "/empty.zone"),
+ InMemoryClient::EmptyZone);
+
+ EXPECT_EQ(0, client_->getZoneCount());
+
+ // Teardown checks for memory segment leaks
+}
+
+TEST_F(MemoryClientTest, load) {
+ // This is a simple load check for a "full" and correct zone that
+ // should not result in any exceptions.
+ client_->load(Name("example.org"),
+ TEST_DATA_DIR "/example.org.zone");
+ const ZoneData* zone_data =
+ client_->findZoneData(Name("example.org"));
+ ASSERT_NE(static_cast<const ZoneData*>(NULL), zone_data);
+ EXPECT_FALSE(zone_data->isSigned());
+ EXPECT_FALSE(zone_data->isNSEC3Signed());
+}
+
+TEST_F(MemoryClientTest, loadFromIterator) {
+ client_->load(Name("example.org"),
+ *MockIterator::makeIterator(rrset_data));
+
+ ZoneIteratorPtr iterator(client_->getIterator(Name("example.org")));
+
+ // First we have the SOA
+ ConstRRsetPtr rrset(iterator->getNextRRset());
+ EXPECT_TRUE(rrset);
+ EXPECT_EQ(RRType::SOA(), rrset->getType());
+
+ // RRType::MX() RRset
+ rrset = iterator->getNextRRset();
+ EXPECT_TRUE(rrset);
+ EXPECT_EQ(RRType::MX(), rrset->getType());
+ EXPECT_EQ(1, rrset->getRRsigDataCount()); // this RRset is signed
+
+ // RRType::A() RRset
+ rrset = iterator->getNextRRset();
+ EXPECT_TRUE(rrset);
+ EXPECT_EQ(RRType::A(), rrset->getType());
+ EXPECT_EQ(1, rrset->getRRsigDataCount()); // also signed
+
+ // There's nothing else in this iterator
+ EXPECT_EQ(ConstRRsetPtr(), iterator->getNextRRset());
+
+ // Iterating past the end should result in an exception
+ EXPECT_THROW(iterator->getNextRRset(), isc::Unexpected);
+
+ // Loading the zone with an iterator separating RRs of the same RRset
+ // will fail because the resulting sequence doesn't meet assumptions of
+ // the (current) in-memory implementation.
+ EXPECT_THROW(client_->load(Name("example.org"),
+ *MockIterator::makeIterator(
+ rrset_data_separated)),
+ InMemoryClient::AddError);
+
+ // Similar to the previous case, but with separated RRSIGs.
+ EXPECT_THROW(client_->load(Name("example.org"),
+ *MockIterator::makeIterator(
+ rrset_data_sigseparated)),
+ InMemoryClient::AddError);
+
+ // Emulating bogus iterator implementation that passes empty RRSIGs.
+ EXPECT_THROW(client_->load(Name("example.org"),
+ *MockIterator::makeIterator(rrset_data, true)),
+ isc::Unexpected);
+}
+
+TEST_F(MemoryClientTest, loadMemoryAllocationFailures) {
+ // Just to check that things get cleaned up
+
+ for (int i = 1; i < 16; i++) {
+ mem_sgmt_.setThrowCount(i);
+ EXPECT_THROW(client_->load(Name("example.org"),
+ TEST_DATA_DIR "/example.org.zone"),
+ std::bad_alloc);
+ }
+ // Teardown checks for memory segment leaks
+}
+
+TEST_F(MemoryClientTest, loadNSEC3Signed) {
+ client_->load(Name("example.org"),
+ TEST_DATA_DIR "/example.org-nsec3-signed.zone");
+ const ZoneData* zone_data =
+ client_->findZoneData(Name("example.org"));
+ ASSERT_NE(static_cast<const ZoneData*>(NULL), zone_data);
+ EXPECT_TRUE(zone_data->isSigned());
+ EXPECT_TRUE(zone_data->isNSEC3Signed());
+}
+
+TEST_F(MemoryClientTest, loadNSEC3EmptySalt) {
+ // Load NSEC3 with empty ("-") salt. This should not throw or crash
+ // or anything.
+ client_->load(Name("example.org"),
+ TEST_DATA_DIR "/example.org-nsec3-empty-salt.zone");
+ const ZoneData* zone_data =
+ client_->findZoneData(Name("example.org"));
+ ASSERT_NE(static_cast<const ZoneData*>(NULL), zone_data);
+ EXPECT_TRUE(zone_data->isSigned());
+ EXPECT_TRUE(zone_data->isNSEC3Signed());
+}
+
+TEST_F(MemoryClientTest, loadNSEC3SignedNoParam) {
+ client_->load(Name("example.org"),
+ TEST_DATA_DIR "/example.org-nsec3-signed-no-param.zone");
+ const ZoneData* zone_data =
+ client_->findZoneData(Name("example.org"));
+ ASSERT_NE(static_cast<const ZoneData*>(NULL), zone_data);
+ EXPECT_TRUE(zone_data->isSigned());
+ EXPECT_TRUE(zone_data->isNSEC3Signed());
+}
+
+TEST_F(MemoryClientTest, loadReloadZone) {
+ // Because we reload the same zone, also check that the zone count
+ // doesn't increase.
+ EXPECT_EQ(0, client_->getZoneCount());
+
+ client_->load(Name("example.org"),
+ TEST_DATA_DIR "/example.org-empty.zone");
+ EXPECT_EQ(1, client_->getZoneCount());
+
+ // Reload zone with same data
+
+ client_->load(Name("example.org"),
+ client_->getFileName(Name("example.org")));
+ EXPECT_EQ(1, client_->getZoneCount());
+
+ const ZoneData* zone_data =
+ client_->findZoneData(Name("example.org"));
+ EXPECT_NE(static_cast<const ZoneData*>(NULL), zone_data);
+
+ /* Check SOA */
+ const ZoneNode* node = zone_data->getOriginNode();
+ EXPECT_NE(static_cast<const ZoneNode*>(NULL), node);
+
+ const RdataSet* set = node->getData();
+ EXPECT_NE(static_cast<const RdataSet*>(NULL), set);
+ EXPECT_EQ(RRType::SOA(), set->type);
+
+ set = set->getNext();
+ EXPECT_EQ(static_cast<const RdataSet*>(NULL), set);
+
+ /* Check ns1.example.org */
+ const ZoneTree& tree = zone_data->getZoneTree();
+ ZoneTree::Result zresult(tree.find(Name("ns1.example.org"), &node));
+ EXPECT_NE(ZoneTree::EXACTMATCH, zresult);
+
+ // Reload zone with different data
+
+ client_->load(Name("example.org"),
+ TEST_DATA_DIR "/example.org-rrsigs.zone");
+ EXPECT_EQ(1, client_->getZoneCount());
+
+ zone_data = client_->findZoneData(Name("example.org"));
+ EXPECT_NE(static_cast<const ZoneData*>(NULL), zone_data);
+
+ /* Check SOA */
+ node = zone_data->getOriginNode();
+ EXPECT_NE(static_cast<const ZoneNode*>(NULL), node);
+
+ set = node->getData();
+ EXPECT_NE(static_cast<const RdataSet*>(NULL), set);
+ EXPECT_EQ(RRType::SOA(), set->type);
+
+ set = set->getNext();
+ EXPECT_EQ(static_cast<const RdataSet*>(NULL), set);
+
+ /* Check ns1.example.org */
+ const ZoneTree& tree2 = zone_data->getZoneTree();
+ ZoneTree::Result zresult2(tree2.find(Name("ns1.example.org"), &node));
+ EXPECT_EQ(ZoneTree::EXACTMATCH, zresult2);
+ EXPECT_NE(static_cast<const ZoneNode*>(NULL), node);
+
+ set = node->getData();
+ EXPECT_NE(static_cast<const RdataSet*>(NULL), set);
+ EXPECT_EQ(RRType::AAAA(), set->type);
+
+ set = set->getNext();
+ EXPECT_NE(static_cast<const RdataSet*>(NULL), set);
+ EXPECT_EQ(RRType::A(), set->type);
+
+ set = set->getNext();
+ EXPECT_EQ(static_cast<const RdataSet*>(NULL), set);
+
+ // Teardown checks for memory segment leaks
+}
+
+TEST_F(MemoryClientTest, loadDuplicateType) {
+ // This should not result in any exceptions:
+ client_->load(Name("example.org"),
+ TEST_DATA_DIR "/example.org-duplicate-type.zone");
+
+ // This should throw:
+ EXPECT_THROW(client_->load(Name("example.org"),
+ TEST_DATA_DIR
+ "/example.org-duplicate-type-bad.zone"),
+ InMemoryClient::AddError);
+ // Teardown checks for memory segment leaks
+}
+
+TEST_F(MemoryClientTest, loadMultipleCNAMEThrows) {
+ // Multiple CNAME RRs should throw.
+ EXPECT_THROW(client_->load(Name("example.org"),
+ TEST_DATA_DIR
+ "/example.org-multiple-cname.zone"),
+ InMemoryClient::AddError);
+ // Teardown checks for memory segment leaks
+}
+
+TEST_F(MemoryClientTest, loadMultipleDNAMEThrows) {
+ // Multiple DNAME RRs should throw.
+ EXPECT_THROW(client_->load(Name("example.org"),
+ TEST_DATA_DIR
+ "/example.org-multiple-dname.zone"),
+ InMemoryClient::AddError);
+ // Teardown checks for memory segment leaks
+}
+
+TEST_F(MemoryClientTest, loadMultipleNSEC3Throws) {
+ // Multiple NSEC3 RRs should throw.
+ EXPECT_THROW(client_->load(Name("example.org"),
+ TEST_DATA_DIR
+ "/example.org-multiple-nsec3.zone"),
+ InMemoryClient::AddError);
+ // Teardown checks for memory segment leaks
+}
+
+TEST_F(MemoryClientTest, loadMultipleNSEC3PARAMThrows) {
+ // Multiple NSEC3PARAM RRs should throw.
+ EXPECT_THROW(client_->load(Name("example.org"),
+ TEST_DATA_DIR
+ "/example.org-multiple-nsec3param.zone"),
+ InMemoryClient::AddError);
+ // Teardown checks for memory segment leaks
+}
+
+TEST_F(MemoryClientTest, loadOutOfZoneThrows) {
+ // Out of zone names should throw.
+ EXPECT_THROW(client_->load(Name("example.org"),
+ TEST_DATA_DIR
+ "/example.org-out-of-zone.zone"),
+ MasterLoadError);
+ // Teardown checks for memory segment leaks
+}
+
+TEST_F(MemoryClientTest, loadWildcardNSThrows) {
+ // Wildcard NS names should throw
+ EXPECT_THROW(client_->load(Name("example.org"),
+ TEST_DATA_DIR
+ "/example.org-wildcard-ns.zone"),
+ InMemoryClient::AddError);
+ // Teardown checks for memory segment leaks
+}
+
+TEST_F(MemoryClientTest, loadWildcardDNAMEThrows) {
+ // Wildcard DNAME names should throw
+ EXPECT_THROW(client_->load(Name("example.org"),
+ TEST_DATA_DIR
+ "/example.org-wildcard-dname.zone"),
+ InMemoryClient::AddError);
+ // Teardown checks for memory segment leaks
+}
+
+TEST_F(MemoryClientTest, loadWildcardNSEC3Throws) {
+ // Wildcard NSEC3 names should throw
+ EXPECT_THROW(client_->load(Name("example.org"),
+ TEST_DATA_DIR
+ "/example.org-wildcard-nsec3.zone"),
+ InMemoryClient::AddError);
+ // Teardown checks for memory segment leaks
+}
+
+TEST_F(MemoryClientTest, loadNSEC3WithFewerLabelsThrows) {
+ // NSEC3 names with labels != (origin_labels + 1) should throw
+ EXPECT_THROW(client_->load(Name("example.org"),
+ TEST_DATA_DIR
+ "/example.org-nsec3-fewer-labels.zone"),
+ InMemoryClient::AddError);
+ // Teardown checks for memory segment leaks
+}
+
+TEST_F(MemoryClientTest, loadNSEC3WithMoreLabelsThrows) {
+ // NSEC3 names with labels != (origin_labels + 1) should throw
+ EXPECT_THROW(client_->load(Name("example.org"),
+ TEST_DATA_DIR
+ "/example.org-nsec3-more-labels.zone"),
+ InMemoryClient::AddError);
+ // Teardown checks for memory segment leaks
+}
+
+TEST_F(MemoryClientTest, loadCNAMEAndNotNSECThrows) {
+ // CNAME and not NSEC should throw
+ EXPECT_THROW(client_->load(Name("example.org"),
+ TEST_DATA_DIR
+ "/example.org-cname-and-not-nsec-1.zone"),
+ InMemoryClient::AddError);
+
+ EXPECT_THROW(client_->load(Name("example.org"),
+ TEST_DATA_DIR
+ "/example.org-cname-and-not-nsec-2.zone"),
+ InMemoryClient::AddError);
+
+ // Teardown checks for memory segment leaks
+}
+
+TEST_F(MemoryClientTest, loadDNAMEAndNSApex1) {
+ // DNAME + NS (apex) is OK
+ client_->load(Name("example.org"),
+ TEST_DATA_DIR
+ "/example.org-dname-ns-apex-1.zone");
+ // Teardown checks for memory segment leaks
+}
+
+TEST_F(MemoryClientTest, loadDNAMEAndNSApex2) {
+ // DNAME + NS (apex) is OK (reverse order)
+ client_->load(Name("example.org"),
+ TEST_DATA_DIR
+ "/example.org-dname-ns-apex-2.zone");
+ // Teardown checks for memory segment leaks
+}
+
+TEST_F(MemoryClientTest, loadDNAMEAndNSNonApex1) {
+ // DNAME + NS (non-apex) must throw
+ EXPECT_THROW(client_->load(Name("example.org"),
+ TEST_DATA_DIR
+ "/example.org-dname-ns-nonapex-1.zone"),
+ InMemoryClient::AddError);
+ // Teardown checks for memory segment leaks
+}
+
+TEST_F(MemoryClientTest, loadDNAMEAndNSNonApex2) {
+ // DNAME + NS (non-apex) must throw (reverse order)
+ EXPECT_THROW(client_->load(Name("example.org"),
+ TEST_DATA_DIR
+ "/example.org-dname-ns-nonapex-2.zone"),
+ InMemoryClient::AddError);
+ // Teardown checks for memory segment leaks
+}
+
+TEST_F(MemoryClientTest, loadRRSIGFollowsNothing) {
+ // This causes the situation where an RRSIG is added without a covered
+ // RRset. Such cases are currently rejected.
+ EXPECT_THROW(client_->load(Name("example.org"),
+ TEST_DATA_DIR
+ "/example.org-rrsig-follows-nothing.zone"),
+ InMemoryClient::AddError);
+ // Teardown checks for memory segment leaks
+}
+
+TEST_F(MemoryClientTest, loadRRSIGs) {
+ client_->load(Name("example.org"),
+ TEST_DATA_DIR "/example.org-rrsigs.zone");
+ EXPECT_EQ(1, client_->getZoneCount());
+}
+
+TEST_F(MemoryClientTest, loadRRSIGsRdataMixedCoveredTypes) {
+ client_->load(Name("example.org"),
+ TEST_DATA_DIR "/example.org-rrsigs.zone");
+
+ RRsetPtr rrset(new RRset(Name("example.org"),
+ RRClass::IN(), RRType::A(), RRTTL(3600)));
+ rrset->addRdata(in::A("192.0.2.1"));
+ rrset->addRdata(in::A("192.0.2.2"));
+
+ RRsetPtr rrsig(new RRset(Name("example.org"), zclass_,
+ RRType::RRSIG(), RRTTL(300)));
+ rrsig->addRdata(generic::RRSIG("A 5 3 3600 20000101000000 20000201000000 "
+ "12345 example.org. FAKEFAKEFAKE"));
+ rrsig->addRdata(generic::RRSIG("NS 5 3 3600 20000101000000 20000201000000 "
+ "54321 example.org. FAKEFAKEFAKEFAKE"));
+ rrset->addRRsig(rrsig);
+
+ EXPECT_THROW(client_->add(Name("example.org"), rrset),
+ InMemoryClient::AddError);
+
+ // Teardown checks for memory segment leaks
+}
+
+TEST_F(MemoryClientTest, getZoneCount) {
+ EXPECT_EQ(0, client_->getZoneCount());
+ client_->load(Name("example.org"), TEST_DATA_DIR "/example.org-empty.zone");
+ EXPECT_EQ(1, client_->getZoneCount());
+}
+
+TEST_F(MemoryClientTest, getFileNameForNonExistentZone) {
+ // Zone "example.org." doesn't exist
+ EXPECT_TRUE(client_->getFileName(Name("example.org.")).empty());
+}
+
+TEST_F(MemoryClientTest, getFileName) {
+ client_->load(Name("example.org"), TEST_DATA_DIR "/example.org-empty.zone");
+ EXPECT_EQ(TEST_DATA_DIR "/example.org-empty.zone",
+ client_->getFileName(Name("example.org")));
+}
+
+TEST_F(MemoryClientTest, getIteratorForNonExistentZone) {
+ // Zone "." doesn't exist
+ EXPECT_THROW(client_->getIterator(Name(".")), DataSourceError);
+}
+
+TEST_F(MemoryClientTest, getIterator) {
+ client_->load(Name("example.org"), TEST_DATA_DIR "/example.org-empty.zone");
+ ZoneIteratorPtr iterator(client_->getIterator(Name("example.org")));
+
+ // First we have the SOA
+ ConstRRsetPtr rrset_soa(iterator->getNextRRset());
+ EXPECT_TRUE(rrset_soa);
+ EXPECT_EQ(RRType::SOA(), rrset_soa->getType());
+
+ // There's nothing else in this iterator
+ EXPECT_EQ(ConstRRsetPtr(), iterator->getNextRRset());
+
+ // Iterating past the end should result in an exception
+ EXPECT_THROW(iterator->getNextRRset(), isc::Unexpected);
+}
+
+TEST_F(MemoryClientTest, getIteratorSeparateRRs) {
+ client_->load(Name("example.org"),
+ TEST_DATA_DIR "/example.org-multiple.zone");
+
+ // separate_rrs = false
+ ZoneIteratorPtr iterator(client_->getIterator(Name("example.org")));
+
+ // First we have the SOA
+ ConstRRsetPtr rrset(iterator->getNextRRset());
+ EXPECT_TRUE(rrset);
+ EXPECT_EQ(RRType::SOA(), rrset->getType());
+
+ // Only one RRType::A() RRset
+ rrset = iterator->getNextRRset();
+ EXPECT_TRUE(rrset);
+ EXPECT_EQ(RRType::A(), rrset->getType());
+
+ // There's nothing else in this zone
+ EXPECT_EQ(ConstRRsetPtr(), iterator->getNextRRset());
+
+
+ // separate_rrs = true
+ ZoneIteratorPtr iterator2(client_->getIterator(Name("example.org"), true));
+
+ // First we have the SOA
+ rrset = iterator2->getNextRRset();
+ EXPECT_TRUE(rrset);
+ EXPECT_EQ(RRType::SOA(), rrset->getType());
+
+ // First RRType::A() RRset
+ rrset = iterator2->getNextRRset();
+ EXPECT_TRUE(rrset);
+ EXPECT_EQ(RRType::A(), rrset->getType());
+
+ // Second RRType::A() RRset
+ rrset = iterator2->getNextRRset();
+ EXPECT_TRUE(rrset);
+ EXPECT_EQ(RRType::A(), rrset->getType());
+
+ // There's nothing else in this iterator
+ EXPECT_EQ(ConstRRsetPtr(), iterator2->getNextRRset());
+}
+
+TEST_F(MemoryClientTest, getIteratorGetSOAThrowsNotImplemented) {
+ client_->load(Name("example.org"), TEST_DATA_DIR "/example.org-empty.zone");
+ ZoneIteratorPtr iterator(client_->getIterator(Name("example.org")));
+
+ // This method is not implemented.
+ EXPECT_THROW(iterator->getSOA(), isc::NotImplemented);
+}
+
+TEST_F(MemoryClientTest, addRRsetToNonExistentZoneThrows) {
+ // The zone "example.org" doesn't exist, so we can't add an RRset to
+ // it.
+ RRsetPtr rrset_a(new RRset(Name("example.org"), RRClass::IN(), RRType::A(),
+ RRTTL(300)));
+ rrset_a->addRdata(rdata::in::A("192.0.2.1"));
+ EXPECT_THROW(client_->add(Name("example.org"), rrset_a), DataSourceError);
+}
+
+TEST_F(MemoryClientTest, addOutOfZoneThrows) {
+ // Out of zone names should throw.
+ client_->load(Name("example.org"),
+ TEST_DATA_DIR "/example.org-empty.zone");
+
+ RRsetPtr rrset_a(new RRset(Name("a.example.com"),
+ RRClass::IN(), RRType::A(), RRTTL(300)));
+ rrset_a->addRdata(rdata::in::A("192.0.2.1"));
+
+ EXPECT_THROW(client_->add(Name("example.org"), rrset_a),
+ OutOfZone);
+ // Teardown checks for memory segment leaks
+}
+
+TEST_F(MemoryClientTest, addNullRRsetThrows) {
+ client_->load(Name("example.org"),
+ TEST_DATA_DIR "/example.org-rrsigs.zone");
+
+ EXPECT_THROW(client_->add(Name("example.org"), ConstRRsetPtr()),
+ InMemoryClient::NullRRset);
+
+ // Teardown checks for memory segment leaks
+}
+
+TEST_F(MemoryClientTest, addEmptyRRsetThrows) {
+ client_->load(Name("example.org"),
+ TEST_DATA_DIR "/example.org-rrsigs.zone");
+
+ RRsetPtr rrset_a(new RRset(Name("example.org"), RRClass::IN(), RRType::A(),
+ RRTTL(300)));
+ EXPECT_THROW(client_->add(Name("example.org"), rrset_a),
+ InMemoryClient::AddError);
+
+ // Teardown checks for memory segment leaks
+}
+
+TEST_F(MemoryClientTest, add) {
+ client_->load(Name("example.org"), TEST_DATA_DIR "/example.org-empty.zone");
+
+ // Add another RRset
+ RRsetPtr rrset_a(new RRset(Name("example.org"), RRClass::IN(), RRType::A(),
+ RRTTL(300)));
+ rrset_a->addRdata(rdata::in::A("192.0.2.1"));
+ client_->add(Name("example.org"), rrset_a);
+
+ ZoneIteratorPtr iterator(client_->getIterator(Name("example.org")));
+
+ // First we have the SOA
+ ConstRRsetPtr rrset(iterator->getNextRRset());
+ EXPECT_TRUE(rrset);
+ EXPECT_EQ(RRType::A(), rrset->getType());
+
+ rrset = iterator->getNextRRset();
+ EXPECT_TRUE(rrset);
+ EXPECT_EQ(RRType::SOA(), rrset->getType());
+
+ // There's nothing else in this zone
+ EXPECT_EQ(ConstRRsetPtr(), iterator->getNextRRset());
+}
+
+TEST_F(MemoryClientTest, findZoneData) {
+ client_->load(Name("example.org"),
+ TEST_DATA_DIR "/example.org-rrsigs.zone");
+
+ const ZoneData* zone_data = client_->findZoneData(Name("example.com"));
+ EXPECT_EQ(static_cast<const ZoneData*>(NULL), zone_data);
+
+ zone_data = client_->findZoneData(Name("example.org"));
+ EXPECT_NE(static_cast<const ZoneData*>(NULL), zone_data);
+
+ /* Check SOA */
+ const ZoneNode* node = zone_data->getOriginNode();
+ EXPECT_NE(static_cast<const ZoneNode*>(NULL), node);
+
+ const RdataSet* set = node->getData();
+ EXPECT_NE(static_cast<const RdataSet*>(NULL), set);
+ EXPECT_EQ(RRType::SOA(), set->type);
+
+ set = set->getNext();
+ EXPECT_EQ(static_cast<const RdataSet*>(NULL), set);
+
+ /* Check ns1.example.org */
+ const ZoneTree& tree = zone_data->getZoneTree();
+ ZoneTree::Result result3(tree.find(Name("ns1.example.org"), &node));
+ EXPECT_EQ(ZoneTree::EXACTMATCH, result3);
+ EXPECT_NE(static_cast<const ZoneNode*>(NULL), node);
+
+ set = node->getData();
+ EXPECT_NE(static_cast<const RdataSet*>(NULL), set);
+ EXPECT_EQ(RRType::AAAA(), set->type);
+
+ set = set->getNext();
+ EXPECT_NE(static_cast<const RdataSet*>(NULL), set);
+ EXPECT_EQ(RRType::A(), set->type);
+
+ set = set->getNext();
+ EXPECT_EQ(static_cast<const RdataSet*>(NULL), set);
+}
+
+TEST_F(MemoryClientTest, getUpdaterThrowsNotImplemented) {
+ // This method is not implemented.
+ EXPECT_THROW(client_->getUpdater(Name("."), false, false),
+ isc::NotImplemented);
+}
+
+TEST_F(MemoryClientTest, getJournalReaderNotImplemented) {
+ // This method is not implemented.
+ EXPECT_THROW(client_->getJournalReader(Name("."), 0, 0),
+ isc::NotImplemented);
+}
+}
diff --git a/src/lib/datasrc/tests/memory/memory_segment_test.h b/src/lib/datasrc/tests/memory/memory_segment_test.h
new file mode 100644
index 0000000..3195a9b
--- /dev/null
+++ b/src/lib/datasrc/tests/memory/memory_segment_test.h
@@ -0,0 +1,62 @@
+// 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 DATASRC_MEMORY_SEGMENT_TEST_H
+#define DATASRC_MEMORY_SEGMENT_TEST_H 1
+
+#include <util/memory_segment_local.h>
+
+#include <cstddef> // for size_t
+#include <new> // for bad_alloc
+
+namespace isc {
+namespace datasrc {
+namespace memory {
+namespace test {
+
+// A special memory segment that can be used for tests. It normally behaves
+// like a "local" memory segment. If "throw count" is set to non 0 via
+// setThrowCount(), it continues the normal behavior until the specified
+// number of calls to allocate(), exclusive, and throws an exception at the
+// next call. For example, if count is set to 3, the next two calls to
+// allocate() will succeed, and the 3rd call will fail with an exception.
+// This segment object can be used after the exception is thrown, and the
+// count is internally reset to 0.
+class MemorySegmentTest : public isc::util::MemorySegmentLocal {
+public:
+ MemorySegmentTest() : throw_count_(0) {}
+ virtual void* allocate(std::size_t size) {
+ if (throw_count_ > 0) {
+ if (--throw_count_ == 0) {
+ throw std::bad_alloc();
+ }
+ }
+ return (isc::util::MemorySegmentLocal::allocate(size));
+ }
+ void setThrowCount(std::size_t count) { throw_count_ = count; }
+
+private:
+ std::size_t throw_count_;
+};
+
+} // namespace test
+} // namespace memory
+} // namespace datasrc
+} // namespace isc
+
+#endif // DATASRC_MEMORY_SEGMENT_TEST_H
+
+// Local Variables:
+// mode: c++
+// End:
diff --git a/src/lib/datasrc/tests/memory/rdata_serialization_unittest.cc b/src/lib/datasrc/tests/memory/rdata_serialization_unittest.cc
new file mode 100644
index 0000000..58c6cb1
--- /dev/null
+++ b/src/lib/datasrc/tests/memory/rdata_serialization_unittest.cc
@@ -0,0 +1,839 @@
+// 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 <exceptions/exceptions.h>
+
+#include <util/buffer.h>
+
+#include <dns/name.h>
+#include <dns/labelsequence.h>
+#include <dns/messagerenderer.h>
+#include <dns/rdata.h>
+#include <dns/rdataclass.h>
+#include <dns/rrclass.h>
+#include <dns/rrtype.h>
+
+#include <datasrc/memory/rdata_serialization.h>
+
+#include <util/unittests/wiredata.h>
+
+#include <gtest/gtest.h>
+
+#include <boost/bind.hpp>
+#include <boost/foreach.hpp>
+
+#include <cstring>
+#include <set>
+#include <string>
+#include <vector>
+
+using namespace isc::dns;
+using namespace isc::dns::rdata;
+using namespace isc::datasrc::memory;
+
+using isc::util::unittests::matchWireData;
+using std::string;
+using std::vector;
+
+// A trick to steal some private definitions of the implementation we use here
+
+namespace isc {
+namespace datasrc{
+namespace memory {
+
+#include <datasrc/memory/rdata_serialization_priv.cc>
+
+}
+}
+}
+
+namespace {
+// This defines a tuple of test data used in test_rdata_list below.
+struct TestRdata {
+ const char* const rrclass; // RR class, textual form
+ const char* const rrtype; // RR type, textual form
+ const char* const rdata; // textual RDATA
+ const size_t n_varlen_fields; // expected # of variable-len fields
+};
+
+// This test data consist of (almost) all supported types of RDATA (+ some
+// unusual and corner cases).
+const TestRdata test_rdata_list[] = {
+ {"IN", "A", "192.0.2.1", 0},
+ {"IN", "NS", "ns.example.com", 0},
+ {"IN", "CNAME", "cname.example.com", 0},
+ {"IN", "SOA", "ns.example.com root.example.com 0 0 0 0 0", 0},
+ {"IN", "PTR", "reverse.example.com", 0},
+ {"IN", "HINFO", "\"cpu-info\" \"OS-info\"", 1},
+ {"IN", "MINFO", "root.example.com mbox.example.com", 0},
+ {"IN", "MX", "10 mx.example.com", 0},
+ {"IN", "TXT", "\"test1\" \"test 2\"", 1},
+ {"IN", "RP", "root.example.com. rp-text.example.com", 0},
+ {"IN", "AFSDB", "1 afsdb.example.com", 0},
+ {"IN", "AAAA", "2001:db8::1", 0},
+ {"IN", "SRV", "1 0 10 target.example.com", 0},
+ {"IN", "NAPTR", "100 50 \"s\" \"http\" \"\" _http._tcp.example.com", 1},
+ {"IN", "DNAME", "dname.example.com", 0},
+ {"IN", "DS", "12892 5 2 5F0EB5C777586DE18DA6B5", 1},
+ {"IN", "SSHFP", "1 1 dd465c09cfa51fb45020cc83316fff", 1},
+ // We handle RRSIG separately, so it's excluded from the list
+ {"IN", "NSEC", "next.example.com. A AAAA NSEC RRSIG", 1},
+ {"IN", "DNSKEY", "256 3 5 FAKEFAKE", 1},
+ {"IN", "DHCID", "FAKEFAKE", 1},
+ {"IN", "NSEC3", "1 1 12 AABBCCDD FAKEFAKE A RRSIG", 1},
+ {"IN", "NSEC3PARAM", "1 0 12 AABBCCDD", 1},
+ {"IN", "SPF", "v=spf1 +mx a:colo.example.com/28 -all", 1},
+ {"IN", "DLV", "12892 5 2 5F0EB5C777586DE18DA6B5", 1},
+ {"IN", "TYPE65000", "\\# 3 010203", 1}, // some "custom" type
+ {"IN", "TYPE65535", "\\# 0", 1}, // max RR type, 0-length RDATA
+ {"CH", "A", "\\# 2 0102", 1}, // A RR for non-IN class; varlen data
+ {"CH", "NS", "ns.example.com", 0}, // class CH, generic data
+ {"CH", "TXT", "BIND10", 1}, // ditto
+ {"HS", "A", "\\# 5 0102030405", 1}, // A RR for non-IN class; varlen data
+ {NULL, NULL, NULL, 0}
+};
+
+// The following two functions will be used to generate wire format data
+// from encoded representation of each RDATA.
+void
+renderNameField(MessageRenderer* renderer, bool additional_required,
+ const LabelSequence& labels, RdataNameAttributes attributes)
+{
+ EXPECT_EQ(additional_required, (attributes & NAMEATTR_ADDITIONAL) != 0);
+ renderer->writeName(labels, (attributes & NAMEATTR_COMPRESSIBLE) != 0);
+}
+
+void
+renderDataField(MessageRenderer* renderer, const void* data, size_t data_len) {
+ renderer->writeData(data, data_len);
+}
+
+class RdataSerializationTest : public ::testing::Test {
+protected:
+ RdataSerializationTest() : a_rdata_(createRdata(RRType::A(), RRClass::IN(),
+ "192.0.2.53")),
+ aaaa_rdata_(createRdata(RRType::AAAA(), RRClass::IN(),
+ "2001:db8::53")),
+ rrsig_rdata_(createRdata(
+ RRType::RRSIG(), RRClass::IN(),
+ "A 5 2 3600 20120814220826 "
+ "20120715220826 12345 com. FAKE"))
+ {}
+
+ // A wraper for RdataEncoder::encode() with buffer overrun check.
+ void encodeWrapper(size_t data_len);
+
+ // Some commonly used RDATA
+ const ConstRdataPtr a_rdata_;
+ const ConstRdataPtr aaaa_rdata_;
+ const ConstRdataPtr rrsig_rdata_;
+
+ RdataEncoder encoder_;
+ vector<uint8_t> encoded_data_;
+ MessageRenderer expected_renderer_;
+ MessageRenderer actual_renderer_;
+ vector<ConstRdataPtr> rdata_list_;
+};
+
+// There are several ways to decode the data. For one, there are
+// more interfaces uses for RdataReader, and we use our own decoder,
+// to check the actual encoded data.
+//
+// These decoding ways are provided by the template parameter.
+template<class DecoderStyle>
+class RdataEncodeDecodeTest : public RdataSerializationTest {
+public:
+ // This helper test method encodes the given list of RDATAs
+ // (in rdata_list), and then iterates over the data, rendering the fields
+ // in the wire format. It then compares the wire data with the one
+ // generated by the normal libdns++ interface to see the encoding/decoding
+ // works as intended.
+ void checkEncode(RRClass rrclass, RRType rrtype,
+ const vector<ConstRdataPtr>& rdata_list,
+ size_t expected_varlen_fields,
+ const vector<ConstRdataPtr>& rrsig_list =
+ vector<ConstRdataPtr>());
+
+ void addRdataCommon(const vector<ConstRdataPtr>& rrsigs);
+ void addRdataMultiCommon(const vector<ConstRdataPtr>& rrsigs);
+};
+
+// Used across more classes and scopes. But it's just uninteresting
+// constant.
+const Name& dummyName2() {
+ static const Name result("example.com");
+ return (result);
+}
+
+bool
+additionalRequired(const RRType& type) {
+ // The set of RR types that require additional section processing.
+ // We'll use it to determine what value should the renderNameField get
+ // and, if the stored attributes are as expected.
+ static std::set<RRType> need_additionals;
+ if (need_additionals.empty()) {
+ need_additionals.insert(RRType::NS());
+ need_additionals.insert(RRType::MX());
+ need_additionals.insert(RRType::SRV());
+ }
+
+ return (need_additionals.find(type) != need_additionals.end());
+}
+
+// A decoder that does not use RdataReader. Not recommended for use,
+// but it allows the tests to check the internals of the data.
+class ManualDecoderStyle {
+public:
+ static void foreachRdataField(RRClass rrclass, RRType rrtype,
+ size_t rdata_count,
+ const vector<uint8_t>& encoded_data,
+ const vector<uint16_t>& varlen_list,
+ RdataReader::NameAction name_callback,
+ RdataReader::DataAction data_callback)
+ {
+ const RdataEncodeSpec& encode_spec = getRdataEncodeSpec(rrclass,
+ rrtype);
+
+ size_t off = 0;
+ size_t varlen_count = 0;
+ size_t name_count = 0;
+ for (size_t count = 0; count < rdata_count; ++count) {
+ for (size_t i = 0; i < encode_spec.field_count; ++i) {
+ const RdataFieldSpec& field_spec = encode_spec.fields[i];
+ switch (field_spec.type) {
+ case RdataFieldSpec::FIXEDLEN_DATA:
+ if (data_callback) {
+ data_callback(&encoded_data.at(off),
+ field_spec.fixeddata_len);
+ }
+ off += field_spec.fixeddata_len;
+ break;
+ case RdataFieldSpec::VARLEN_DATA:
+ {
+ const size_t varlen = varlen_list.at(varlen_count);
+ if (data_callback && varlen > 0) {
+ data_callback(&encoded_data.at(off), varlen);
+ }
+ off += varlen;
+ ++varlen_count;
+ break;
+ }
+ case RdataFieldSpec::DOMAIN_NAME:
+ {
+ ++name_count;
+ const LabelSequence labels(&encoded_data.at(off));
+ if (name_callback) {
+ name_callback(labels,
+ field_spec.name_attributes);
+ }
+ off += labels.getSerializedLength();
+ break;
+ }
+ }
+ }
+ }
+ assert(name_count == encode_spec.name_count * rdata_count);
+ assert(varlen_count == encode_spec.varlen_count * rdata_count);
+ }
+
+ static void foreachRRSig(const vector<uint8_t>& encoded_data,
+ const vector<uint16_t>& rrsiglen_list,
+ RdataReader::DataAction data_callback)
+ {
+ size_t rrsig_totallen = 0;
+ for (vector<uint16_t>::const_iterator it = rrsiglen_list.begin();
+ it != rrsiglen_list.end();
+ ++it) {
+ rrsig_totallen += *it;
+ }
+ assert(encoded_data.size() >= rrsig_totallen);
+
+ const uint8_t* dp = &encoded_data[encoded_data.size() -
+ rrsig_totallen];
+ for (size_t i = 0; i < rrsiglen_list.size(); ++i) {
+ data_callback(dp, rrsiglen_list[i]);
+ dp += rrsiglen_list[i];
+ }
+ }
+
+ static void decode(const isc::dns::RRClass& rrclass,
+ const isc::dns::RRType& rrtype,
+ size_t rdata_count,
+ size_t rrsig_count,
+ size_t expected_varlen_fields,
+ // Warning: this test actualy might change the
+ // encoded_data !
+ vector<uint8_t>& encoded_data, size_t,
+ MessageRenderer& renderer)
+ {
+ // If this type of RDATA is expected to contain variable-length fields,
+ // we brute force the encoded data, exploiting our knowledge of actual
+ // encoding, then adjust the encoded data excluding the list of length
+ // fields. This is ugly, but for tests only.
+ vector<uint16_t> varlen_list;
+ if (expected_varlen_fields > 0) {
+ const size_t varlen_list_size =
+ rdata_count * expected_varlen_fields * sizeof(uint16_t);
+ ASSERT_LE(varlen_list_size, encoded_data.size());
+ varlen_list.resize(rdata_count * expected_varlen_fields);
+ std::memcpy(&varlen_list[0], &encoded_data[0], varlen_list_size);
+ encoded_data.assign(encoded_data.begin() + varlen_list_size,
+ encoded_data.end());
+ }
+
+ // If RRSIGs are given, we need to extract the list of the RRSIG
+ // lengths and adjust encoded_data_ further.
+ vector<uint16_t> rrsiglen_list;
+ if (rrsig_count > 0) {
+ const size_t rrsig_len_size = rrsig_count * sizeof(uint16_t);
+ ASSERT_LE(rrsig_len_size, encoded_data.size());
+ rrsiglen_list.resize(rrsig_count * rrsig_len_size);
+ std::memcpy(&rrsiglen_list[0], &encoded_data[0], rrsig_len_size);
+ encoded_data.assign(encoded_data.begin() + rrsig_len_size,
+ encoded_data.end());
+ }
+
+ // Create wire-format data from the encoded data
+ foreachRdataField(rrclass, rrtype, rdata_count, encoded_data,
+ varlen_list,
+ boost::bind(renderNameField, &renderer,
+ additionalRequired(rrtype), _1, _2),
+ boost::bind(renderDataField, &renderer, _1, _2));
+
+ // 2nd dummy name
+ renderer.writeName(dummyName2());
+ // Finally, dump any RRSIGs in wire format.
+ foreachRRSig(encoded_data, rrsiglen_list,
+ boost::bind(renderDataField, &renderer, _1, _2));
+ }
+};
+
+// Check using callbacks and calling next until the end.
+class CallbackDecoder {
+public:
+ static void decode(const isc::dns::RRClass& rrclass,
+ const isc::dns::RRType& rrtype,
+ size_t rdata_count, size_t sig_count, size_t,
+ const vector<uint8_t>& encoded_data, size_t,
+ MessageRenderer& renderer)
+ {
+ RdataReader reader(rrclass, rrtype, &encoded_data[0], rdata_count,
+ sig_count,
+ boost::bind(renderNameField, &renderer,
+ additionalRequired(rrtype), _1, _2),
+ boost::bind(renderDataField, &renderer, _1, _2));
+ while (reader.next() != RdataReader::RRSET_BOUNDARY) {}
+ renderer.writeName(dummyName2());
+ while (reader.nextSig() != RdataReader::RRSET_BOUNDARY) {}
+ }
+};
+
+// Check using callbacks and calling iterate.
+class IterateDecoder {
+public:
+ static void decode(const isc::dns::RRClass& rrclass,
+ const isc::dns::RRType& rrtype,
+ size_t rdata_count, size_t sig_count, size_t,
+ const vector<uint8_t>& encoded_data, size_t,
+ MessageRenderer& renderer)
+ {
+ RdataReader reader(rrclass, rrtype, &encoded_data[0],
+ rdata_count, sig_count,
+ boost::bind(renderNameField, &renderer,
+ additionalRequired(rrtype), _1, _2),
+ boost::bind(renderDataField, &renderer, _1, _2));
+ reader.iterate();
+ renderer.writeName(dummyName2());
+ reader.iterateAllSigs();
+ }
+};
+
+namespace {
+
+// Render the data to renderer, if one is set, or put it inside
+// a data buffer.
+void
+appendOrRenderData(vector<uint8_t>* where, MessageRenderer** renderer,
+ const void* data, size_t size)
+{
+ if (*renderer != NULL) {
+ (*renderer)->writeData(data, size);
+ } else {
+ where->insert(where->end(), reinterpret_cast<const uint8_t*>(data),
+ reinterpret_cast<const uint8_t*>(data) + size);
+ }
+}
+
+}
+
+// Similar to IterateDecoder, but it first iterates a little and rewinds
+// before actual rendering.
+class RewindAndDecode {
+private:
+ static void writeName(MessageRenderer** renderer,
+ const LabelSequence& labels,
+ RdataNameAttributes attributes)
+ {
+ (*renderer)->writeName(labels,
+ (attributes & NAMEATTR_COMPRESSIBLE) != 0);
+ }
+public:
+ static void decode(const isc::dns::RRClass& rrclass,
+ const isc::dns::RRType& rrtype,
+ size_t rdata_count, size_t sig_count, size_t,
+ const vector<uint8_t>& encoded_data, size_t,
+ MessageRenderer& renderer)
+ {
+ MessageRenderer dump; // A place to dump the extra data from before
+ // actual rendering.
+ MessageRenderer* current = &dump;
+ vector<uint8_t> placeholder; // boost::bind does not like NULL
+ RdataReader reader(rrclass, rrtype, &encoded_data[0],
+ rdata_count, sig_count,
+ boost::bind(writeName, ¤t, _1, _2),
+ boost::bind(appendOrRenderData, &placeholder,
+ ¤t, _1, _2));
+ // Iterate a little and rewind
+ reader.next();
+ reader.nextSig();
+ reader.rewind();
+ // Do the actual rendering
+ current = &renderer;
+ reader.iterate();
+ renderer.writeName(dummyName2());
+ reader.iterateAllSigs();
+ }
+};
+
+// Decode using the iteration over one rdata each time.
+// We also count there's the correct count of Rdatas.
+class SingleIterateDecoder {
+public:
+ static void decode(const isc::dns::RRClass& rrclass,
+ const isc::dns::RRType& rrtype,
+ size_t rdata_count, size_t sig_count, size_t,
+ const vector<uint8_t>& encoded_data, size_t,
+ MessageRenderer& renderer)
+ {
+ RdataReader reader(rrclass, rrtype, &encoded_data[0],
+ rdata_count, sig_count,
+ boost::bind(renderNameField, &renderer,
+ additionalRequired(rrtype), _1, _2),
+ boost::bind(renderDataField, &renderer, _1, _2));
+ size_t actual_count = 0;
+ while (reader.iterateRdata()) {
+ ++actual_count;
+ }
+ EXPECT_EQ(rdata_count, actual_count);
+ actual_count = 0;
+ renderer.writeName(dummyName2());
+ while (reader.iterateSingleSig()) {
+ ++actual_count;
+ }
+ EXPECT_EQ(sig_count, actual_count);
+ }
+};
+
+// This one does not adhere to the usual way the reader is used, trying
+// to confuse it. It iterates part of the data manually and then reads
+// the rest through iterate. It also reads the signatures in the middle
+// of rendering.
+template<bool start_data, bool start_sig>
+class HybridDecoder {
+public:
+ static void decode(const isc::dns::RRClass& rrclass,
+ const isc::dns::RRType& rrtype,
+ size_t rdata_count, size_t sig_count, size_t,
+ const vector<uint8_t>& encoded_data,
+ size_t encoded_data_len,
+ MessageRenderer& renderer)
+ {
+ vector<uint8_t> data;
+ MessageRenderer* current;
+ RdataReader reader(rrclass, rrtype, &encoded_data[0],
+ rdata_count, sig_count,
+ boost::bind(renderNameField, &renderer,
+ additionalRequired(rrtype), _1, _2),
+ boost::bind(appendOrRenderData, &data, ¤t, _1,
+ _2));
+ // The size matches
+ EXPECT_EQ(encoded_data_len, reader.getSize());
+ if (start_sig) {
+ current = NULL;
+ reader.nextSig();
+ }
+ // Render first part of data. If there's none, return empty Result and
+ // do nothing.
+ if (start_data) {
+ current = &renderer;
+ reader.next();
+ }
+ // Now, we let all sigs to be copied to data. We disable the
+ // renderer for this.
+ current = NULL;
+ reader.iterateAllSigs();
+ // Now return the renderer and render the rest of the data
+ current = &renderer;
+ reader.iterate();
+ // Now, this should not break anything and should be valid, but should
+ // return ends.
+ EXPECT_EQ(RdataReader::RRSET_BOUNDARY, reader.next());
+ EXPECT_EQ(RdataReader::RRSET_BOUNDARY, reader.nextSig());
+ // Render the name and the sigs
+ renderer.writeName(dummyName2());
+ renderer.writeData(&data[0], data.size());
+ // The size matches even after use
+ EXPECT_EQ(encoded_data_len, reader.getSize());
+ }
+};
+
+typedef ::testing::Types<ManualDecoderStyle,
+ CallbackDecoder, IterateDecoder, SingleIterateDecoder,
+ HybridDecoder<true, true>, HybridDecoder<true, false>,
+ HybridDecoder<false, true>,
+ HybridDecoder<false, false> >
+ DecoderStyles;
+// Each decoder style must contain a decode() method. Such method is expected
+// to decode the passed data, first render the Rdata into the passed renderer,
+// then write the dummyName2() there and write the RRSig data after that.
+// It may do other checks too.
+//
+// There are some slight differences to how to do the decoding, that's why we
+// have the typed test.
+TYPED_TEST_CASE(RdataEncodeDecodeTest, DecoderStyles);
+
+void
+RdataSerializationTest::encodeWrapper(size_t data_len) {
+ // make sure the data buffer is large enough for the canary
+ encoded_data_.resize(data_len + 2);
+ // set the canary data
+ encoded_data_.at(data_len) = 0xde;
+ encoded_data_.at(data_len + 1) = 0xad;
+ // encode, then check the canary is intact
+ encoder_.encode(&encoded_data_[0], data_len);
+ EXPECT_EQ(0xde, encoded_data_.at(data_len));
+ EXPECT_EQ(0xad, encoded_data_.at(data_len + 1));
+ // shrink the data buffer to the originally expected size (some tests
+ // expect that). the actual encoded data should be intact.
+ encoded_data_.resize(data_len);
+}
+
+template<class DecoderStyle>
+void
+RdataEncodeDecodeTest<DecoderStyle>::
+checkEncode(RRClass rrclass, RRType rrtype,
+ const vector<ConstRdataPtr>& rdata_list,
+ size_t expected_varlen_fields,
+ const vector<ConstRdataPtr>& rrsig_list)
+{
+ // These two names will be rendered before and after the test RDATA,
+ // to check in case the RDATA contain a domain name whether it's
+ // compressed or not correctly. The names in the RDATA should basically
+ // a subdomain of example.com, so it can be compressed due to dummyName2().
+ // Likewise, dummyName2() should be able to be fully compressed due to
+ // the name in the RDATA.
+ const Name dummy_name("com");
+
+ expected_renderer_.clear();
+ actual_renderer_.clear();
+ encoded_data_.clear();
+
+ // Build expected wire-format data
+ expected_renderer_.writeName(dummy_name);
+ BOOST_FOREACH(const ConstRdataPtr& rdata, rdata_list) {
+ rdata->toWire(expected_renderer_);
+ }
+ expected_renderer_.writeName(dummyName2());
+ BOOST_FOREACH(const ConstRdataPtr& rdata, rrsig_list) {
+ rdata->toWire(expected_renderer_);
+ }
+
+ // Then build wire format data using the encoded data.
+ // 1st dummy name
+ actual_renderer_.writeName(dummy_name);
+
+ // Create encoded data
+ encoder_.start(rrclass, rrtype);
+ BOOST_FOREACH(const ConstRdataPtr& rdata, rdata_list) {
+ encoder_.addRdata(*rdata);
+ }
+ BOOST_FOREACH(const ConstRdataPtr& rdata, rrsig_list) {
+ encoder_.addSIGRdata(*rdata);
+ }
+ const size_t storage_len = encoder_.getStorageLength();
+ encodeWrapper(storage_len);
+
+ DecoderStyle::decode(rrclass, rrtype, rdata_list.size(), rrsig_list.size(),
+ expected_varlen_fields, encoded_data_, storage_len,
+ actual_renderer_);
+
+ // Two sets of wire-format data should be identical.
+ matchWireData(expected_renderer_.getData(), expected_renderer_.getLength(),
+ actual_renderer_.getData(), actual_renderer_.getLength());
+}
+
+template<class DecoderStyle>
+void
+RdataEncodeDecodeTest<DecoderStyle>::
+addRdataCommon(const vector<ConstRdataPtr>& rrsigs) {
+ // Basic check on the encoded data for (most of) all supported RR types,
+ // in a comprehensive manner.
+ for (size_t i = 0; test_rdata_list[i].rrclass != NULL; ++i) {
+ SCOPED_TRACE(string(test_rdata_list[i].rrclass) + "/" +
+ test_rdata_list[i].rrtype);
+ const RRClass rrclass(test_rdata_list[i].rrclass);
+ const RRType rrtype(test_rdata_list[i].rrtype);
+ const ConstRdataPtr rdata = createRdata(rrtype, rrclass,
+ test_rdata_list[i].rdata);
+ rdata_list_.clear();
+ rdata_list_.push_back(rdata);
+ checkEncode(rrclass, rrtype, rdata_list_,
+ test_rdata_list[i].n_varlen_fields, rrsigs);
+ }
+}
+
+TYPED_TEST(RdataEncodeDecodeTest, addRdata) {
+ vector<ConstRdataPtr> rrsigs;
+ this->addRdataCommon(rrsigs); // basic tests without RRSIGs (empty vector)
+
+ // Test with RRSIGs (covered type doesn't always match, but the encoder
+ // doesn't check that)
+ rrsigs.push_back(this->rrsig_rdata_);
+ this->addRdataCommon(rrsigs);
+}
+
+template<class DecoderStyle>
+void
+RdataEncodeDecodeTest<DecoderStyle>::
+addRdataMultiCommon(const vector<ConstRdataPtr>& rrsigs) {
+ // Similar to addRdata(), but test with multiple RDATAs.
+ // Four different cases are tested: a single fixed-len RDATA (A),
+ // fixed-len data + domain name (MX), variable-len data only (TXT),
+ // variable-len data + domain name (NAPTR).
+ ConstRdataPtr a_rdata2 = createRdata(RRType::A(), RRClass::IN(),
+ "192.0.2.54");
+ rdata_list_.clear();
+ rdata_list_.push_back(a_rdata_);
+ rdata_list_.push_back(a_rdata2);
+ checkEncode(RRClass::IN(), RRType::A(), rdata_list_, 0, rrsigs);
+
+ ConstRdataPtr mx_rdata1 = createRdata(RRType::MX(), RRClass::IN(),
+ "5 mx1.example.com");
+ ConstRdataPtr mx_rdata2 = createRdata(RRType::MX(), RRClass::IN(),
+ "10 mx2.example.com");
+ rdata_list_.clear();
+ rdata_list_.push_back(mx_rdata1);
+ rdata_list_.push_back(mx_rdata2);
+ checkEncode(RRClass::IN(), RRType::MX(), rdata_list_, 0, rrsigs);
+
+ ConstRdataPtr txt_rdata1 = createRdata(RRType::TXT(), RRClass::IN(),
+ "foo bar baz");
+ ConstRdataPtr txt_rdata2 = createRdata(RRType::TXT(), RRClass::IN(),
+ "another text data");
+ rdata_list_.clear();
+ rdata_list_.push_back(txt_rdata1);
+ rdata_list_.push_back(txt_rdata2);
+ checkEncode(RRClass::IN(), RRType::TXT(), rdata_list_, 1, rrsigs);
+
+ ConstRdataPtr naptr_rdata1 =
+ createRdata(RRType::NAPTR(), RRClass::IN(),
+ "100 50 \"s\" \"http\" \"\" _http._tcp.example.com");
+ ConstRdataPtr naptr_rdata2 =
+ createRdata(RRType::NAPTR(), RRClass::IN(),
+ "200 100 \"s\" \"http\" \"\" _http._tcp.example.com");
+ rdata_list_.clear();
+ rdata_list_.push_back(naptr_rdata1);
+ rdata_list_.push_back(naptr_rdata2);
+ checkEncode(RRClass::IN(), RRType::NAPTR(), rdata_list_, 1, rrsigs);
+}
+
+void ignoreName(const LabelSequence&, unsigned) {
+}
+
+void
+checkLargeData(const in::DHCID* decoded, bool* called, const void* encoded,
+ size_t length)
+{
+ EXPECT_FALSE(*called); // Called exactly once
+ *called = true;
+
+ // Reconstruct the Rdata and check it.
+ isc::util::InputBuffer ib(encoded, length);
+ const in::DHCID reconstructed(ib, ib.getLength());
+ EXPECT_EQ(0, reconstructed.compare(*decoded));
+}
+
+TEST_F(RdataSerializationTest, encodeLargeRdata) {
+ // There should be no reason for a large RDATA to fail in encoding,
+ // but we check such a case explicitly.
+
+ encoded_data_.resize(65535); // max unsigned 16-bit int
+ isc::util::InputBuffer buffer(&encoded_data_[0], encoded_data_.size());
+ const in::DHCID large_dhcid(buffer, encoded_data_.size());
+
+ encoder_.start(RRClass::IN(), RRType::DHCID());
+ encoder_.addRdata(large_dhcid);
+ encodeWrapper(encoder_.getStorageLength());
+
+ // The encoded data should be identical to the original one.
+ bool called = false;
+ RdataReader reader(RRClass::IN(), RRType::DHCID(), &encoded_data_[0], 1, 0,
+ ignoreName, boost::bind(checkLargeData, &large_dhcid,
+ &called, _1, _2));
+ reader.iterate();
+ EXPECT_TRUE(called);
+ called = false;
+ reader.iterateAllSigs();
+ EXPECT_FALSE(called);
+}
+
+TYPED_TEST(RdataEncodeDecodeTest, addRdataMulti) {
+ vector<ConstRdataPtr> rrsigs;
+ this->addRdataMultiCommon(rrsigs); // test without RRSIGs (empty vector)
+
+ // Tests with two RRSIGs
+ rrsigs.push_back(this->rrsig_rdata_);
+ rrsigs.push_back(createRdata(RRType::RRSIG(), RRClass::IN(),
+ "A 5 2 3600 20120814220826 "
+ "20120715220826 54321 com. FAKE"));
+ this->addRdataMultiCommon(rrsigs);
+}
+
+TEST_F(RdataSerializationTest, badAddRdata) {
+ // Some operations must follow start().
+ EXPECT_THROW(encoder_.addRdata(*a_rdata_), isc::InvalidOperation);
+ EXPECT_THROW(encoder_.getStorageLength(), isc::InvalidOperation);
+ // will allocate space of some arbitrary size (256 bytes)
+ EXPECT_THROW(encodeWrapper(256), isc::InvalidOperation);
+
+ // Bad buffer for encode
+ encoder_.start(RRClass::IN(), RRType::A());
+ encoder_.addRdata(*a_rdata_);
+ const size_t buf_len = encoder_.getStorageLength();
+ // NULL buffer for encode
+ EXPECT_THROW(encoder_.encode(NULL, buf_len), isc::BadValue);
+ // buffer length is too short (we don't use the wrraper because we don't
+ // like to tweak the length arg to encode()).
+ encoded_data_.resize(buf_len - 1);
+ EXPECT_THROW(encoder_.encode(&encoded_data_[0], buf_len - 1),
+ isc::BadValue);
+
+ // Type of RDATA and the specified RR type don't match. addRdata() should
+ // detect this inconsistency.
+ encoder_.start(RRClass::IN(), RRType::AAAA());
+ EXPECT_THROW(encoder_.addRdata(*a_rdata_), isc::BadValue);
+
+ // Likewise.
+ encoder_.start(RRClass::IN(), RRType::A());
+ EXPECT_THROW(encoder_.addRdata(*aaaa_rdata_), isc::BadValue);
+
+ // Likewise. The encoder expects the first name completes the data, and
+ // throws on the second due as an unexpected name field.
+ const ConstRdataPtr rp_rdata =
+ createRdata(RRType::RP(), RRClass::IN(), "a.example. b.example");
+ encoder_.start(RRClass::IN(), RRType::NS());
+ EXPECT_THROW(encoder_.addRdata(*rp_rdata), isc::BadValue);
+
+ // Likewise. The encoder considers the name data a variable length data
+ // field, and throws on the first name.
+ encoder_.start(RRClass::IN(), RRType::DHCID());
+ EXPECT_THROW(encoder_.addRdata(*rp_rdata), isc::BadValue);
+
+ // Likewise. The text RDATA (2 bytes) will be treated as MX preference,
+ // and the encoder will still expect to see a domain name.
+ const ConstRdataPtr txt_rdata = createRdata(RRType::TXT(), RRClass::IN(),
+ "a");
+ encoder_.start(RRClass::IN(), RRType::MX());
+ EXPECT_THROW(encoder_.addRdata(*txt_rdata), isc::BadValue);
+
+ // Similar to the previous one, but in this case there's no data field
+ // in the spec.
+ encoder_.start(RRClass::IN(), RRType::NS());
+ EXPECT_THROW(encoder_.addRdata(*txt_rdata), isc::BadValue);
+
+ // Likewise. Inconsistent name compression policy.
+ const ConstRdataPtr ns_rdata =
+ createRdata(RRType::NS(), RRClass::IN(), "ns.example");
+ encoder_.start(RRClass::IN(), RRType::DNAME());
+ EXPECT_THROW(encoder_.addRdata(*ns_rdata), isc::BadValue);
+
+ // Same as the previous one, opposite inconsistency.
+ const ConstRdataPtr dname_rdata =
+ createRdata(RRType::DNAME(), RRClass::IN(), "dname.example");
+ encoder_.start(RRClass::IN(), RRType::NS());
+ EXPECT_THROW(encoder_.addRdata(*dname_rdata), isc::BadValue);
+
+ // RDATA len exceeds the 16-bit range. Technically not invalid, but
+ // we don't support that (and it's practically useless anyway).
+ encoded_data_.resize(65536); // use encoded_data_ for placeholder
+ isc::util::InputBuffer buffer(&encoded_data_[0], encoded_data_.size());
+ encoder_.start(RRClass::IN(), RRType::DHCID());
+ EXPECT_THROW(encoder_.addRdata(in::DHCID(buffer, encoded_data_.size())),
+ RdataEncodingError);
+
+ // RRSIG cannot be used as the main RDATA type (can only be added as
+ // a signature for some other type of RDATAs).
+ EXPECT_THROW(encoder_.start(RRClass::IN(), RRType::RRSIG()),
+ isc::BadValue);
+}
+
+void
+checkSigData(const ConstRdataPtr& decoded, bool* called, const void* encoded,
+ size_t length)
+{
+ EXPECT_FALSE(*called); // Called exactly once
+ *called = true;
+
+ // Reconstruct the RRSig and check it.
+ isc::util::InputBuffer ib(encoded, length);
+ const generic::RRSIG reconstructed(ib, ib.getLength());
+ EXPECT_EQ(0, reconstructed.compare(*decoded));
+}
+
+TEST_F(RdataSerializationTest, addSIGRdataOnly) {
+ // Encoded data that only contain RRSIGs. Mostly useless, but can happen
+ // (in a partially broken zone) and it's accepted.
+ encoder_.start(RRClass::IN(), RRType::A());
+ encoder_.addSIGRdata(*rrsig_rdata_);
+ encodeWrapper(encoder_.getStorageLength());
+ ASSERT_LT(sizeof(uint16_t), encoder_.getStorageLength());
+
+ bool called = false;
+ RdataReader reader(RRClass::IN(), RRType::A(), &encoded_data_[0], 0, 1,
+ ignoreName, boost::bind(checkSigData, rrsig_rdata_,
+ &called, _1, _2));
+ reader.iterate();
+ EXPECT_FALSE(called);
+ reader.iterateAllSigs();
+ EXPECT_TRUE(called);
+}
+
+TEST_F(RdataSerializationTest, badAddSIGRdata) {
+ // try adding SIG before start
+ EXPECT_THROW(encoder_.addSIGRdata(*rrsig_rdata_), isc::InvalidOperation);
+
+ // Very big RRSIG. This implementation rejects it.
+ isc::util::OutputBuffer ob(0);
+ rrsig_rdata_->toWire(ob);
+ // append dummy trailing signature to make it too big
+ vector<uint8_t> dummy_sig(65536 - ob.getLength());
+ ob.writeData(&dummy_sig[0], dummy_sig.size());
+ ASSERT_EQ(65536, ob.getLength());
+
+ isc::util::InputBuffer ib(ob.getData(), ob.getLength());
+ const generic::RRSIG big_sigrdata(ib, ob.getLength());
+ encoder_.start(RRClass::IN(), RRType::A());
+ EXPECT_THROW(encoder_.addSIGRdata(big_sigrdata), RdataEncodingError);
+}
+}
diff --git a/src/lib/datasrc/tests/memory/rdataset_unittest.cc b/src/lib/datasrc/tests/memory/rdataset_unittest.cc
new file mode 100644
index 0000000..897e53c
--- /dev/null
+++ b/src/lib/datasrc/tests/memory/rdataset_unittest.cc
@@ -0,0 +1,298 @@
+// 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 <exceptions/exceptions.h>
+
+#include <util/buffer.h>
+#include <util/memory_segment_local.h>
+
+#include <dns/rdata.h>
+#include <dns/rdataclass.h>
+#include <dns/rrset.h>
+#include <dns/rrclass.h>
+#include <dns/rrtype.h>
+#include <dns/rrttl.h>
+
+#include <datasrc/memory/rdata_serialization.h>
+#include <datasrc/memory/rdataset.h>
+
+#include <testutils/dnsmessage_test.h>
+
+#include <gtest/gtest.h>
+
+#include <boost/lexical_cast.hpp>
+
+#include <string>
+
+using namespace isc::dns;
+using namespace isc::dns::rdata;
+using namespace isc::datasrc::memory;
+using namespace isc::testutils;
+using boost::lexical_cast;
+
+namespace {
+
+class RdataSetTest : public ::testing::Test {
+protected:
+ RdataSetTest() :
+ // 1076895760 = 0x40302010. Use this so we fill in all 8-bit "field"
+ // of the 32-bit TTL
+ a_rrset_(textToRRset("www.example.com. 1076895760 IN A 192.0.2.1")),
+ rrsig_rrset_(textToRRset("www.example.com. 1076895760 IN RRSIG "
+ "A 5 2 3600 20120814220826 20120715220826 "
+ "1234 example.com. FAKE"))
+ {}
+ void TearDown() {
+ EXPECT_TRUE(mem_sgmt_.allMemoryDeallocated());
+ }
+
+ ConstRRsetPtr a_rrset_, rrsig_rrset_;
+ isc::util::MemorySegmentLocal mem_sgmt_;
+ RdataEncoder encoder_;
+};
+
+// Convert the given 32-bit integer (network byte order) to the corresponding
+// RRTTL object.
+RRTTL
+restoreTTL(const void* ttl_data) {
+ isc::util::InputBuffer b(ttl_data, sizeof(uint32_t));
+ return (RRTTL(b));
+}
+
+// A helper callback for checkRdataSet. This confirms the given data
+// is the expected in::A RDATA (the value is taken from the RdataSetTest
+// constructor).
+void
+checkData(const void* data, size_t size) {
+ isc::util::InputBuffer b(data, size);
+ EXPECT_EQ(0, in::A(b, size).compare(in::A("192.0.2.1")));
+}
+
+// This is a set of checks for an RdataSet created with some simple
+// conditions. with_rrset/with_rrsig is true iff the RdataSet is supposed to
+// contain normal/RRSIG RDATA.
+void
+checkRdataSet(const RdataSet& rdataset, bool with_rrset, bool with_rrsig) {
+ EXPECT_FALSE(rdataset.next); // by default the next pointer should be NULL
+ EXPECT_EQ(RRType::A(), rdataset.type);
+ // See the RdataSetTest constructor for the magic number.
+ EXPECT_EQ(RRTTL(1076895760), restoreTTL(rdataset.getTTLData()));
+ EXPECT_EQ(with_rrset ? 1 : 0, rdataset.getRdataCount());
+ EXPECT_EQ(with_rrsig ? 1 : 0, rdataset.getSigRdataCount());
+
+ // A simple test for the data content. Details tests for the encoder/
+ // reader should be basically sufficient for various cases of the data,
+ // and the fact that this test doesn't detect memory leak should be
+ // reasonably sufficient that the implementation handles the data region
+ // correctly. Here we check one simple case for a simple form of RDATA,
+ // mainly for checking the behavior of getDataBuf().
+ RdataReader reader(RRClass::IN(), RRType::A(),
+ reinterpret_cast<const uint8_t*>(
+ rdataset.getDataBuf()),
+ rdataset.getRdataCount(), rdataset.getSigRdataCount(),
+ &RdataReader::emptyNameAction, checkData);
+ reader.iterate();
+}
+
+TEST_F(RdataSetTest, create) {
+ // A simple case of creating an RdataSet. Confirming the resulting
+ // fields have the expected values, and then destroying it (TearDown()
+ // would detect any memory leak)
+ RdataSet* rdataset = RdataSet::create(mem_sgmt_, encoder_, a_rrset_,
+ ConstRRsetPtr());
+ checkRdataSet(*rdataset, true, false);
+ RdataSet::destroy(mem_sgmt_, RRClass::IN(), rdataset);
+}
+
+TEST_F(RdataSetTest, getNext) {
+ RdataSet* rdataset = RdataSet::create(mem_sgmt_, encoder_, a_rrset_,
+ ConstRRsetPtr());
+
+ // By default, the next pointer should be NULL (already tested in other
+ // test cases), which should be the case with getNext(). We test both
+ // mutable and immutable versions of getNext().
+ EXPECT_EQ(static_cast<RdataSet*>(NULL), rdataset->getNext());
+ EXPECT_EQ(static_cast<const RdataSet*>(NULL),
+ static_cast<const RdataSet*>(rdataset)->getNext());
+
+ // making a link (it would form an infinite loop, but it doesn't matter
+ // in this test), and check the pointer returned by getNext().
+ rdataset->next = rdataset;
+ EXPECT_EQ(rdataset, static_cast<const RdataSet*>(rdataset)->getNext());
+
+ RdataSet::destroy(mem_sgmt_, RRClass::IN(), rdataset);
+}
+
+// A helper function to create an RRset containing the given number of
+// unique RDATAs.
+ConstRRsetPtr
+getRRsetWithRdataCount(size_t rdata_count) {
+ RRsetPtr rrset(new RRset(Name("example.com"), RRClass::IN(), RRType::TXT(),
+ RRTTL(3600)));
+ for (size_t i = 0; i < rdata_count; ++i) {
+ rrset->addRdata(rdata::createRdata(RRType::TXT(), RRClass::IN(),
+ lexical_cast<std::string>(i)));
+ }
+ return (rrset);
+}
+
+TEST_F(RdataSetTest, createManyRRs) {
+ // RRset with possible maximum number of RDATAs
+ RdataSet* rdataset = RdataSet::create(mem_sgmt_, encoder_,
+ getRRsetWithRdataCount(8191),
+ ConstRRsetPtr());
+ EXPECT_EQ(8191, rdataset->getRdataCount());
+ EXPECT_EQ(0, rdataset->getSigRdataCount());
+ RdataSet::destroy(mem_sgmt_, RRClass::IN(), rdataset);
+
+ // Exceeding that will result in an exception.
+ EXPECT_THROW(RdataSet::create(mem_sgmt_, encoder_,
+ getRRsetWithRdataCount(8192),
+ ConstRRsetPtr()),
+ RdataSetError);
+ // To be very sure even try larger number than the threshold
+ EXPECT_THROW(RdataSet::create(mem_sgmt_, encoder_,
+ getRRsetWithRdataCount(65535),
+ ConstRRsetPtr()),
+ RdataSetError);
+}
+
+TEST_F(RdataSetTest, createWithRRSIG) {
+ // Normal case.
+ RdataSet* rdataset = RdataSet::create(mem_sgmt_, encoder_, a_rrset_,
+ rrsig_rrset_);
+ checkRdataSet(*rdataset, true, true);
+ RdataSet::destroy(mem_sgmt_, RRClass::IN(), rdataset);
+
+ // Unusual case: TTL doesn't match. This implementation accepts that,
+ // using the TTL of the covered RRset.
+ ConstRRsetPtr rrsig_badttl(textToRRset(
+ "www.example.com. 3600 IN RRSIG "
+ "A 5 2 3600 20120814220826 "
+ "20120715220826 1234 example.com. FAKE"));
+ rdataset = RdataSet::create(mem_sgmt_, encoder_, a_rrset_, rrsig_badttl);
+ checkRdataSet(*rdataset, true, true);
+ RdataSet::destroy(mem_sgmt_, RRClass::IN(), rdataset);
+}
+
+// A helper function to create an RRSIG RRset containing the given number of
+// unique RDATAs.
+ConstRRsetPtr
+getRRSIGWithRdataCount(size_t sig_count) {
+ RRsetPtr rrset(new RRset(Name("example.com"), RRClass::IN(),
+ RRType::RRSIG(), RRTTL(3600)));
+ // We use a base wire-format image and tweak the original TTL field to
+ // generate unique RDATAs in the loop. (Creating them from corresponding
+ // text is simpler, but doing so for a large number of RRSIGs is
+ // relatively heavy and could be too long for unittests).
+ ConstRdataPtr rrsig_base =
+ rdata::createRdata(RRType::RRSIG(), RRClass::IN(),
+ "A 5 2 3600 20120814220826 20120715220826 1234 "
+ "example.com. FAKE");
+ isc::util::OutputBuffer ob(0);
+ rrsig_base->toWire(ob);
+ for (size_t i = 0; i < sig_count; ++i) {
+ ob.writeUint16At((i >> 16) & 0xffff, 4);
+ ob.writeUint16At(i & 0xffff, 6);
+ isc::util::InputBuffer ib(ob.getData(), ob.getLength());
+ rrset->addRdata(rdata::createRdata(RRType::RRSIG(), RRClass::IN(),
+ ib, ib.getLength()));
+ }
+ return (rrset);
+}
+
+TEST_F(RdataSetTest, createManyRRSIGs) {
+ // 7 has a special meaning in the implementation: if the number of the
+ // RRSIGs reaches this value, an extra 'sig count' field will be created.
+ RdataSet* rdataset = RdataSet::create(mem_sgmt_, encoder_, a_rrset_,
+ getRRSIGWithRdataCount(7));
+ EXPECT_EQ(7, rdataset->getSigRdataCount());
+ RdataSet::destroy(mem_sgmt_, RRClass::IN(), rdataset);
+
+ // 8 would cause overflow in the normal 3-bit field if there were no extra
+ // count field.
+ rdataset = RdataSet::create(mem_sgmt_, encoder_, a_rrset_,
+ getRRSIGWithRdataCount(8));
+ EXPECT_EQ(8, rdataset->getSigRdataCount());
+ RdataSet::destroy(mem_sgmt_, RRClass::IN(), rdataset);
+
+ // Up to 2^16-1 RRSIGs are allowed (although that would be useless
+ // in practice)
+ rdataset = RdataSet::create(mem_sgmt_, encoder_, a_rrset_,
+ getRRSIGWithRdataCount(65535));
+ EXPECT_EQ(65535, rdataset->getSigRdataCount());
+ RdataSet::destroy(mem_sgmt_, RRClass::IN(), rdataset);
+
+ // Exceeding this limit will result in an exception.
+ EXPECT_THROW(RdataSet::create(mem_sgmt_, encoder_, a_rrset_,
+ getRRSIGWithRdataCount(65536)),
+ RdataSetError);
+ // To be very sure even try larger number than the threshold
+ EXPECT_THROW(RdataSet::create(mem_sgmt_, encoder_, a_rrset_,
+ getRRSIGWithRdataCount(70000)),
+ RdataSetError);
+}
+
+TEST_F(RdataSetTest, createWithRRSIGOnly) {
+ // A rare, but allowed, case: RdataSet without the main RRset but with
+ // RRSIG.
+ RdataSet* rdataset = RdataSet::create(mem_sgmt_, encoder_, ConstRRsetPtr(),
+ rrsig_rrset_);
+ checkRdataSet(*rdataset, false, true);
+ RdataSet::destroy(mem_sgmt_, RRClass::IN(), rdataset);
+}
+
+TEST_F(RdataSetTest, badCeate) {
+ // Neither the RRset nor RRSIG RRset is given
+ EXPECT_THROW(RdataSet::create(mem_sgmt_, encoder_, ConstRRsetPtr(),
+ ConstRRsetPtr()), isc::BadValue);
+
+ // Empty RRset (An RRset without RDATA)
+ ConstRRsetPtr empty_rrset(new RRset(Name("example.com"), RRClass::IN(),
+ RRType::A(), RRTTL(3600)));
+ EXPECT_THROW(RdataSet::create(mem_sgmt_, encoder_, empty_rrset,
+ ConstRRsetPtr()), isc::BadValue);
+ ConstRRsetPtr empty_rrsig(new RRset(Name("example.com"), RRClass::IN(),
+ RRType::RRSIG(), RRTTL(3600)));
+ EXPECT_THROW(RdataSet::create(mem_sgmt_, encoder_, ConstRRsetPtr(),
+ empty_rrsig), isc::BadValue);
+
+ // The RRset type and RRSIG's type covered don't match
+ ConstRRsetPtr bad_rrsig(textToRRset(
+ "www.example.com. 1076895760 IN RRSIG "
+ "NS 5 2 3600 20120814220826 20120715220826 "
+ "1234 example.com. FAKE"));
+ EXPECT_THROW(RdataSet::create(mem_sgmt_, encoder_, a_rrset_, bad_rrsig),
+ isc::BadValue);
+
+ // Pass non RRSIG for the sig parameter
+ EXPECT_THROW(RdataSet::create(mem_sgmt_, encoder_, a_rrset_, a_rrset_),
+ isc::BadValue);
+
+ // Pass RRSIG for normal RRset (the RdataEncoder will catch this and throw)
+ EXPECT_THROW(RdataSet::create(mem_sgmt_, encoder_, rrsig_rrset_,
+ rrsig_rrset_),
+ isc::BadValue);
+
+ // RR class doesn't match between RRset and RRSIG
+ ConstRRsetPtr badclass_rrsig(textToRRset(
+ "www.example.com. 1076895760 CH RRSIG "
+ "A 5 2 3600 20120814220826 "
+ "20120715220826 1234 example.com. FAKE",
+ RRClass::CH()));
+ EXPECT_THROW(RdataSet::create(mem_sgmt_, encoder_, a_rrset_,
+ badclass_rrsig),
+ isc::BadValue);
+}
+}
diff --git a/src/lib/datasrc/tests/memory/run_unittests.cc b/src/lib/datasrc/tests/memory/run_unittests.cc
new file mode 100644
index 0000000..6321976
--- /dev/null
+++ b/src/lib/datasrc/tests/memory/run_unittests.cc
@@ -0,0 +1,26 @@
+// 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 <gtest/gtest.h>
+#include <util/unittests/run_all.h>
+#include <log/logger_support.h>
+
+int
+main(int argc, char* argv[]) {
+ ::testing::InitGoogleTest(&argc, argv);
+
+ isc::log::initLogger();
+
+ return (isc::util::unittests::run_all());
+}
diff --git a/src/lib/datasrc/tests/memory/segment_object_holder_unittest.cc b/src/lib/datasrc/tests/memory/segment_object_holder_unittest.cc
new file mode 100644
index 0000000..d27e364
--- /dev/null
+++ b/src/lib/datasrc/tests/memory/segment_object_holder_unittest.cc
@@ -0,0 +1,67 @@
+// 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 <util/memory_segment_local.h>
+
+#include <datasrc/memory/segment_object_holder.h>
+
+#include <gtest/gtest.h>
+
+using namespace isc::util;
+using namespace isc::datasrc::memory;
+using namespace isc::datasrc::memory::detail;
+
+namespace {
+const int TEST_ARG_VAL = 42; // arbitrary chosen magic number
+
+class TestObject {
+public:
+ static void destroy(MemorySegment& sgmt, TestObject* obj, int arg) {
+ sgmt.deallocate(obj, sizeof(*obj));
+ EXPECT_EQ(TEST_ARG_VAL, arg);
+ }
+};
+
+void
+useHolder(MemorySegment& sgmt, TestObject* obj, bool release) {
+ // Create a holder object, check the return value of get(), and,
+ // if requested, release the held object. At the end of function
+ // the holder is destructed, and if the object hasn't been released by
+ // then, it should be deallocated. Passed argument is checked in its
+ // deallocate().
+
+ typedef SegmentObjectHolder<TestObject, int> HolderType;
+ HolderType holder(sgmt, obj, TEST_ARG_VAL);
+ EXPECT_EQ(obj, holder.get());
+ if (release) {
+ EXPECT_EQ(obj, holder.release());
+ }
+}
+
+TEST(SegmentObjectHolderTest, foo) {
+ MemorySegmentLocal sgmt;
+ void* p = sgmt.allocate(sizeof(TestObject));
+ TestObject* obj = new(p) TestObject;
+
+ // Use holder, and release the content. The memory shouldn't be
+ // deallocated.
+ useHolder(sgmt, obj, true);
+ EXPECT_FALSE(sgmt.allMemoryDeallocated());
+
+ // Use holder, and let it deallocate the object. The memory segment
+ // should now be empty.
+ useHolder(sgmt, obj, false);
+ EXPECT_TRUE(sgmt.allMemoryDeallocated());
+}
+}
diff --git a/src/lib/datasrc/tests/memory/testdata/Makefile.am b/src/lib/datasrc/tests/memory/testdata/Makefile.am
new file mode 100644
index 0000000..dddbf0a
--- /dev/null
+++ b/src/lib/datasrc/tests/memory/testdata/Makefile.am
@@ -0,0 +1,31 @@
+CLEANFILES = *.copied
+
+EXTRA_DIST = empty.zone
+EXTRA_DIST += example.org.zone
+EXTRA_DIST += example.org-empty.zone
+
+EXTRA_DIST += example.org-broken1.zone
+EXTRA_DIST += example.org-broken2.zone
+EXTRA_DIST += example.org-cname-and-not-nsec-1.zone
+EXTRA_DIST += example.org-cname-and-not-nsec-2.zone
+EXTRA_DIST += example.org-dname-ns-apex-1.zone
+EXTRA_DIST += example.org-dname-ns-apex-2.zone
+EXTRA_DIST += example.org-dname-ns-nonapex-1.zone
+EXTRA_DIST += example.org-dname-ns-nonapex-2.zone
+EXTRA_DIST += example.org-duplicate-type-bad.zone
+EXTRA_DIST += example.org-duplicate-type.zone
+EXTRA_DIST += example.org-multiple-cname.zone
+EXTRA_DIST += example.org-multiple-dname.zone
+EXTRA_DIST += example.org-multiple-nsec3.zone
+EXTRA_DIST += example.org-multiple-nsec3param.zone
+EXTRA_DIST += example.org-multiple.zone
+EXTRA_DIST += example.org-nsec3-empty-salt.zone
+EXTRA_DIST += example.org-nsec3-fewer-labels.zone example.org-nsec3-more-labels.zone
+EXTRA_DIST += example.org-nsec3-signed-no-param.zone
+EXTRA_DIST += example.org-nsec3-signed.zone
+EXTRA_DIST += example.org-out-of-zone.zone
+EXTRA_DIST += example.org-rrsig-follows-nothing.zone
+EXTRA_DIST += example.org-rrsigs.zone
+EXTRA_DIST += example.org-wildcard-dname.zone
+EXTRA_DIST += example.org-wildcard-ns.zone
+EXTRA_DIST += example.org-wildcard-nsec3.zone
diff --git a/src/lib/datasrc/tests/memory/testdata/empty.zone b/src/lib/datasrc/tests/memory/testdata/empty.zone
new file mode 100644
index 0000000..e69de29
diff --git a/src/lib/datasrc/tests/memory/testdata/example.org-broken1.zone b/src/lib/datasrc/tests/memory/testdata/example.org-broken1.zone
new file mode 100644
index 0000000..317095d
--- /dev/null
+++ b/src/lib/datasrc/tests/memory/testdata/example.org-broken1.zone
@@ -0,0 +1 @@
+This is a broken zone that should not parse.
diff --git a/src/lib/datasrc/tests/memory/testdata/example.org-broken2.zone b/src/lib/datasrc/tests/memory/testdata/example.org-broken2.zone
new file mode 100644
index 0000000..2f9fd98
--- /dev/null
+++ b/src/lib/datasrc/tests/memory/testdata/example.org-broken2.zone
@@ -0,0 +1,5 @@
+;; broken example.org zone, where some RRs are OK, but others aren't
+example.org. 3600 IN SOA ns1.example.org. bugs.x.w.example.org. 73 3600 300 3600000 3600
+ns1.example.org. 3600 IN A 192.0.2.1
+ns2.example.org. 3600 IN A 192.0.2.2
+ns2.a.example.com. 3600 IN AAAA
diff --git a/src/lib/datasrc/tests/memory/testdata/example.org-cname-and-not-nsec-1.zone b/src/lib/datasrc/tests/memory/testdata/example.org-cname-and-not-nsec-1.zone
new file mode 100644
index 0000000..5533663
--- /dev/null
+++ b/src/lib/datasrc/tests/memory/testdata/example.org-cname-and-not-nsec-1.zone
@@ -0,0 +1,4 @@
+;; CNAME + other is an error
+example.org. 86400 IN SOA ns.example.org. ns.example.org. 2012091009 7200 3600 2592000 1200
+a.example.org. 7200 IN A 192.168.0.1
+a.example.org. 3600 IN CNAME foo.example.com.
diff --git a/src/lib/datasrc/tests/memory/testdata/example.org-cname-and-not-nsec-2.zone b/src/lib/datasrc/tests/memory/testdata/example.org-cname-and-not-nsec-2.zone
new file mode 100644
index 0000000..966aeeb
--- /dev/null
+++ b/src/lib/datasrc/tests/memory/testdata/example.org-cname-and-not-nsec-2.zone
@@ -0,0 +1,4 @@
+;; CNAME + other is an error
+example.org. 86400 IN SOA ns.example.org. ns.example.org. 2012091007 7200 3600 2592000 1200
+a.example.org. 3600 IN CNAME foo.example.com.
+a.example.org. 7200 IN A 192.168.0.1
diff --git a/src/lib/datasrc/tests/memory/testdata/example.org-dname-ns-apex-1.zone b/src/lib/datasrc/tests/memory/testdata/example.org-dname-ns-apex-1.zone
new file mode 100644
index 0000000..f57c25d
--- /dev/null
+++ b/src/lib/datasrc/tests/memory/testdata/example.org-dname-ns-apex-1.zone
@@ -0,0 +1,4 @@
+;; DNAME + NS (apex)
+example.org. 86400 IN SOA ns.example.org. ns.example.org. 2012091015 7200 3600 2592000 1200
+example.org. 3600 IN DNAME foo.example.com.
+example.org. 3600 IN NS bar.example.com.
diff --git a/src/lib/datasrc/tests/memory/testdata/example.org-dname-ns-apex-2.zone b/src/lib/datasrc/tests/memory/testdata/example.org-dname-ns-apex-2.zone
new file mode 100644
index 0000000..bb3f191
--- /dev/null
+++ b/src/lib/datasrc/tests/memory/testdata/example.org-dname-ns-apex-2.zone
@@ -0,0 +1,4 @@
+;; DNAME + NS (apex)
+example.org. 86400 IN SOA ns.example.org. ns.example.org. 2012091016 7200 3600 2592000 1200
+example.org. 3600 IN NS bar.example.com.
+example.org. 3600 IN DNAME foo.example.com.
diff --git a/src/lib/datasrc/tests/memory/testdata/example.org-dname-ns-nonapex-1.zone b/src/lib/datasrc/tests/memory/testdata/example.org-dname-ns-nonapex-1.zone
new file mode 100644
index 0000000..68a9805
--- /dev/null
+++ b/src/lib/datasrc/tests/memory/testdata/example.org-dname-ns-nonapex-1.zone
@@ -0,0 +1,4 @@
+;; DNAME + NS (non-apex)
+example.org. 86400 IN SOA ns.example.org. ns.example.org. 2012091014 7200 3600 2592000 1200
+ns1.example.org. 3600 IN DNAME foo.example.com.
+ns1.example.org. 3600 IN NS bar.example.com.
diff --git a/src/lib/datasrc/tests/memory/testdata/example.org-dname-ns-nonapex-2.zone b/src/lib/datasrc/tests/memory/testdata/example.org-dname-ns-nonapex-2.zone
new file mode 100644
index 0000000..1b671dd
--- /dev/null
+++ b/src/lib/datasrc/tests/memory/testdata/example.org-dname-ns-nonapex-2.zone
@@ -0,0 +1,4 @@
+;; DNAME + NS (non-apex)
+example.org. 86400 IN SOA ns.example.org. ns.example.org. 2012091015 7200 3600 2592000 1200
+ns1.example.org. 3600 IN NS bar.example.com.
+ns1.example.org. 3600 IN DNAME foo.example.com.
diff --git a/src/lib/datasrc/tests/memory/testdata/example.org-duplicate-type-bad.zone b/src/lib/datasrc/tests/memory/testdata/example.org-duplicate-type-bad.zone
new file mode 100644
index 0000000..06c7dff
--- /dev/null
+++ b/src/lib/datasrc/tests/memory/testdata/example.org-duplicate-type-bad.zone
@@ -0,0 +1,4 @@
+example.org. 3600 IN SOA ns1.example.org. bugs.x.w.example.org. 77 3600 300 3600000 3600
+ns1.example.org. 3600 IN A 192.168.0.1
+ns1.example.org. 3600 IN AAAA ::1
+ns1.example.org. 3600 IN A 192.168.0.2
diff --git a/src/lib/datasrc/tests/memory/testdata/example.org-duplicate-type.zone b/src/lib/datasrc/tests/memory/testdata/example.org-duplicate-type.zone
new file mode 100644
index 0000000..6e5c1b8
--- /dev/null
+++ b/src/lib/datasrc/tests/memory/testdata/example.org-duplicate-type.zone
@@ -0,0 +1,4 @@
+example.org. 3600 IN SOA ns1.example.org. bugs.x.w.example.org. 76 3600 300 3600000 3600
+ns1.example.org. 3600 IN A 192.168.0.1
+ns1.example.org. 3600 IN A 192.168.0.2
+ns1.example.org. 3600 IN AAAA ::1
diff --git a/src/lib/datasrc/tests/memory/testdata/example.org-empty.zone b/src/lib/datasrc/tests/memory/testdata/example.org-empty.zone
new file mode 100644
index 0000000..f11b9b8
--- /dev/null
+++ b/src/lib/datasrc/tests/memory/testdata/example.org-empty.zone
@@ -0,0 +1,2 @@
+;; empty example.org zone
+example.org. 3600 IN SOA ns1.example.org. bugs.x.w.example.org. 68 3600 300 3600000 3600
diff --git a/src/lib/datasrc/tests/memory/testdata/example.org-multiple-cname.zone b/src/lib/datasrc/tests/memory/testdata/example.org-multiple-cname.zone
new file mode 100644
index 0000000..0a0c983
--- /dev/null
+++ b/src/lib/datasrc/tests/memory/testdata/example.org-multiple-cname.zone
@@ -0,0 +1,3 @@
+example.org. 3600 IN SOA ns1.example.org. bugs.x.w.example.org. 78 3600 300 3600000 3600
+ns1.example.org. 3600 IN CNAME foo.example.com.
+ns1.example.org. 3600 IN CNAME bar.example.com.
diff --git a/src/lib/datasrc/tests/memory/testdata/example.org-multiple-dname.zone b/src/lib/datasrc/tests/memory/testdata/example.org-multiple-dname.zone
new file mode 100644
index 0000000..3d581d0
--- /dev/null
+++ b/src/lib/datasrc/tests/memory/testdata/example.org-multiple-dname.zone
@@ -0,0 +1,3 @@
+example.org. 3600 IN SOA ns1.example.org. bugs.x.w.example.org. 79 3600 300 3600000 3600
+ns1.example.org. 3600 IN DNAME foo.example.com.
+ns1.example.org. 3600 IN DNAME bar.example.com.
diff --git a/src/lib/datasrc/tests/memory/testdata/example.org-multiple-nsec3.zone b/src/lib/datasrc/tests/memory/testdata/example.org-multiple-nsec3.zone
new file mode 100644
index 0000000..874023a
--- /dev/null
+++ b/src/lib/datasrc/tests/memory/testdata/example.org-multiple-nsec3.zone
@@ -0,0 +1,3 @@
+example.org. 86400 IN SOA ns.example.org. ns.example.org. 2012090702 7200 3600 2592000 1200
+09GM5T42SMIMT7R8DF6RTG80SFMS1NLU.example.org. 1200 IN NSEC3 1 0 10 AABBCCDD RKOF8QMFRB5F2V9EJHFBVB2JPVSA0DJD A RRSIG
+09GM5T42SMIMT7R8DF6RTG80SFMS1NLU.example.org. 1200 IN NSEC3 1 0 10 AABBCCDD 09GM5T42SMIMT7R8DF6RTG80SFMS1NLU NS SOA RRSIG DNSKEY NSEC3PARAM
diff --git a/src/lib/datasrc/tests/memory/testdata/example.org-multiple-nsec3param.zone b/src/lib/datasrc/tests/memory/testdata/example.org-multiple-nsec3param.zone
new file mode 100644
index 0000000..5e69518
--- /dev/null
+++ b/src/lib/datasrc/tests/memory/testdata/example.org-multiple-nsec3param.zone
@@ -0,0 +1,3 @@
+example.org. 86400 IN SOA ns.example.org. ns.example.org. 2012090700 7200 3600 2592000 1200
+example.org. 0 IN NSEC3PARAM 1 0 10 AABBCCDD
+example.org. 0 IN NSEC3PARAM 1 0 10 AABBCCDD
diff --git a/src/lib/datasrc/tests/memory/testdata/example.org-multiple.zone b/src/lib/datasrc/tests/memory/testdata/example.org-multiple.zone
new file mode 100644
index 0000000..f473ae6
--- /dev/null
+++ b/src/lib/datasrc/tests/memory/testdata/example.org-multiple.zone
@@ -0,0 +1,4 @@
+;; Multiple RDATA for testing separate RRs iterator
+example.org. 3600 IN SOA ns1.example.org. bugs.x.w.example.org. 78 3600 300 3600000 3600
+a.example.org. 3600 IN A 192.168.0.1
+a.example.org. 3600 IN A 192.168.0.2
diff --git a/src/lib/datasrc/tests/memory/testdata/example.org-nsec3-empty-salt.zone b/src/lib/datasrc/tests/memory/testdata/example.org-nsec3-empty-salt.zone
new file mode 100644
index 0000000..6c20ee9
--- /dev/null
+++ b/src/lib/datasrc/tests/memory/testdata/example.org-nsec3-empty-salt.zone
@@ -0,0 +1,14 @@
+example.org. 86400 IN SOA ns.example.org. ns.example.org. 2012092602 7200 3600 2592000 1200
+example.org. 86400 IN RRSIG SOA 7 2 86400 20120301040838 20120131040838 19562 example.org. Jt9wCRLS5TQxZH0IBqrM9uMGD453rIoxYopfM9AjjRZfEx+HGlBpOZeR pGN7yLcN+URnicOD0ydLHiakaBODiZyNoYCKYG5d2ZOhL+026REnDKNM 0m5T3X3sczP+l55An/GITheTdrKt3Y1Ouc2yKI8ro8JjOxV/a4nGDWjK x9A=
+example.org. 86400 IN NS ns.example.org.
+example.org. 86400 IN RRSIG NS 7 2 86400 20120301040838 20120131040838 19562 example.org. gYXL3xK4IFdJU6TtiVuzqDBb2MeA8xB3AKtHlJGFTfTRNHyuej0ZGovx TeUYsLYmoiGYaJG66iD1tYYFq0qdj0xWq+LEa53ACtKvYf9IIwK4ijJs k0g6xCNavc6/qPppymDhN7MvoFVkW59uJa0HPWlsIIuRlEAr7xyt85vq yoA=
+example.org. 86400 IN DNSKEY 256 3 7 AwEAAbrBkKf2gmGtG4aogZY4svIZCrOLLZlQzVHwz7WxJdTR8iEnvz/x Q/jReDroS5CBZWvzwLlhPIpsJAojx0oj0RvfJNsz3+6LN8q7x9u6+86B 85CYjTk3dcFOebgkF4fXr7/kkOX+ZY94Zk0Z1+pUC3eY4gkKcyME/Uxm O18PBTeB
+example.org. 86400 IN RRSIG DNSKEY 7 2 86400 20120301040838 20120131040838 19562 example.org. d0eLF8JqNHaGuBSX0ashU5c1O/wyWU43UUsKGrMQIoBDiJ588MWQOnas rwvW6vdkLNqRqCsP/B4epV/EtLL0tBsk5SHkTYbNo80gGrBufQ6YrWRr Ile8Z+h+MR4y9DybbjmuNKqaO4uQMg/X6+4HqRAKx1lmZMTcrcVeOwDM ZA4=
+example.org. 0 IN NSEC3PARAM 1 0 10 -
+example.org. 0 IN RRSIG NSEC3PARAM 7 2 0 20120301040838 20120131040838 19562 example.org. Ggs5MiQDlXXt22Fz9DNg3Ujc0T6MBfumlRkd8/enBbJwLmqw2QXAzDEk pjUeGstCEHKzxJDJstavGoCpTDJgoV4Fd9szooMx69rzPrq9tdoM46jG xZHqw+Pv2fkRGC6aP7ZX1r3Qnpwpk47AQgATftbO4G6KcMcO8JoKE47x XLM=
+ns.example.org. 86400 IN A 192.0.2.1
+ns.example.org. 86400 IN RRSIG A 7 3 86400 20120301040838 20120131040838 19562 example.org. dOH+Dxib8VcGnjLrKILsqDhS1wki6BWk1dZwpOGUGHyLWcLNW8ygWY2o r29jPhHtaFCNWpn46JebgnXDPRiQjaY3dQqL8rcf2QX1e3+Cicw1OSrs S0sUDE5DmMNEac+ZCUQ0quCStZTCldl05hlspV2RS92TpApnoOK0nXMp Uak=
+09GM5T42SMIMT7R8DF6RTG80SFMS1NLU.example.org. 1200 IN NSEC3 1 0 10 - RKOF8QMFRB5F2V9EJHFBVB2JPVSA0DJD A RRSIG
+09GM5T42SMIMT7R8DF6RTG80SFMS1NLU.example.org. 1200 IN RRSIG NSEC3 7 3 1200 20120301040838 20120131040838 19562 example.org. EdwMeepLf//lV+KpCAN+213Scv1rrZyj4i2OwoCP4XxxS3CWGSuvYuKO yfZc8wKRcrD/4YG6nZVXE0s5O8NahjBJmDIyVt4WkfZ6QthxGg8ggLVv cD3dFksPyiKHf/WrTOZPSsxvN5m/i1Ey6+YWS01Gf3WDCMWDauC7Nmh3 CTM=
+RKOF8QMFRB5F2V9EJHFBVB2JPVSA0DJD.example.org. 1200 IN NSEC3 1 0 10 - 09GM5T42SMIMT7R8DF6RTG80SFMS1NLU NS SOA RRSIG DNSKEY NSEC3PARAM
+RKOF8QMFRB5F2V9EJHFBVB2JPVSA0DJD.example.org. 1200 IN RRSIG NSEC3 7 3 1200 20120301040838 20120131040838 19562 example.org. j7d8GL4YqX035FBcPPsEcSWHjWcKdlQMHLL4TB67xVNFnl4SEFQCp4OO AtPap5tkKakwgWxoQVN9XjnqrBz+oQhofDkB3aTatAjIIkcwcnrm3AYQ rTI3E03ySiRwuCPKVmHOLUV2cG6O4xzcmP+MYZcvPTS8V3F5LlaU22i7 A3E=
diff --git a/src/lib/datasrc/tests/memory/testdata/example.org-nsec3-fewer-labels.zone b/src/lib/datasrc/tests/memory/testdata/example.org-nsec3-fewer-labels.zone
new file mode 100644
index 0000000..0221269
--- /dev/null
+++ b/src/lib/datasrc/tests/memory/testdata/example.org-nsec3-fewer-labels.zone
@@ -0,0 +1,3 @@
+;; NSEC3 names with labels != (origin_labels + 1)
+example.org. 86400 IN SOA ns.example.org. ns.example.org. 2012091001 7200 3600 2592000 1200
+example.org. 1200 IN NSEC3 1 0 10 AABBCCDD RKOF8QMFRB5F2V9EJHFBVB2JPVSA0DJD A RRSIG
diff --git a/src/lib/datasrc/tests/memory/testdata/example.org-nsec3-more-labels.zone b/src/lib/datasrc/tests/memory/testdata/example.org-nsec3-more-labels.zone
new file mode 100644
index 0000000..efebcfb
--- /dev/null
+++ b/src/lib/datasrc/tests/memory/testdata/example.org-nsec3-more-labels.zone
@@ -0,0 +1,3 @@
+;; NSEC3 names with labels != (origin_labels + 1)
+example.org. 86400 IN SOA ns.example.org. ns.example.org. 2012091002 7200 3600 2592000 1200
+a.b.example.org. 1200 IN NSEC3 1 0 10 AABBCCDD RKOF8QMFRB5F2V9EJHFBVB2JPVSA0DJD A RRSIG
diff --git a/src/lib/datasrc/tests/memory/testdata/example.org-nsec3-signed-no-param.zone b/src/lib/datasrc/tests/memory/testdata/example.org-nsec3-signed-no-param.zone
new file mode 100644
index 0000000..5caa308
--- /dev/null
+++ b/src/lib/datasrc/tests/memory/testdata/example.org-nsec3-signed-no-param.zone
@@ -0,0 +1,15 @@
+;; This file intentionally removes NSEC3PARAM from example.org.nsec3-signed
+example.org. 86400 IN SOA ns.example.org. ns.example.org. 2012013000 7200 3600 2592000 1200
+example.org. 86400 IN RRSIG SOA 7 2 86400 20120301040838 20120131040838 19562 example.org. Jt9wCRLS5TQxZH0IBqrM9uMGD453rIoxYopfM9AjjRZfEx+HGlBpOZeR pGN7yLcN+URnicOD0ydLHiakaBODiZyNoYCKYG5d2ZOhL+026REnDKNM 0m5T3X3sczP+l55An/GITheTdrKt3Y1Ouc2yKI8ro8JjOxV/a4nGDWjK x9A=
+example.org. 86400 IN NS ns.example.org.
+example.org. 86400 IN RRSIG NS 7 2 86400 20120301040838 20120131040838 19562 example.org. gYXL3xK4IFdJU6TtiVuzqDBb2MeA8xB3AKtHlJGFTfTRNHyuej0ZGovx TeUYsLYmoiGYaJG66iD1tYYFq0qdj0xWq+LEa53ACtKvYf9IIwK4ijJs k0g6xCNavc6/qPppymDhN7MvoFVkW59uJa0HPWlsIIuRlEAr7xyt85vq yoA=
+example.org. 86400 IN DNSKEY 256 3 7 AwEAAbrBkKf2gmGtG4aogZY4svIZCrOLLZlQzVHwz7WxJdTR8iEnvz/x Q/jReDroS5CBZWvzwLlhPIpsJAojx0oj0RvfJNsz3+6LN8q7x9u6+86B 85CYjTk3dcFOebgkF4fXr7/kkOX+ZY94Zk0Z1+pUC3eY4gkKcyME/Uxm O18PBTeB
+example.org. 86400 IN RRSIG DNSKEY 7 2 86400 20120301040838 20120131040838 19562 example.org. d0eLF8JqNHaGuBSX0ashU5c1O/wyWU43UUsKGrMQIoBDiJ588MWQOnas rwvW6vdkLNqRqCsP/B4epV/EtLL0tBsk5SHkTYbNo80gGrBufQ6YrWRr Ile8Z+h+MR4y9DybbjmuNKqaO4uQMg/X6+4HqRAKx1lmZMTcrcVeOwDM ZA4=
+;; example.org. 0 IN NSEC3PARAM 1 0 10 AABBCCDD
+;; example.org. 0 IN RRSIG NSEC3PARAM 7 2 0 20120301040838 20120131040838 19562 example.org. Ggs5MiQDlXXt22Fz9DNg3Ujc0T6MBfumlRkd8/enBbJwLmqw2QXAzDEk pjUeGstCEHKzxJDJstavGoCpTDJgoV4Fd9szooMx69rzPrq9tdoM46jG xZHqw+Pv2fkRGC6aP7ZX1r3Qnpwpk47AQgATftbO4G6KcMcO8JoKE47x XLM=
+ns.example.org. 86400 IN A 192.0.2.1
+ns.example.org. 86400 IN RRSIG A 7 3 86400 20120301040838 20120131040838 19562 example.org. dOH+Dxib8VcGnjLrKILsqDhS1wki6BWk1dZwpOGUGHyLWcLNW8ygWY2o r29jPhHtaFCNWpn46JebgnXDPRiQjaY3dQqL8rcf2QX1e3+Cicw1OSrs S0sUDE5DmMNEac+ZCUQ0quCStZTCldl05hlspV2RS92TpApnoOK0nXMp Uak=
+09GM5T42SMIMT7R8DF6RTG80SFMS1NLU.example.org. 1200 IN NSEC3 1 0 10 AABBCCDD RKOF8QMFRB5F2V9EJHFBVB2JPVSA0DJD A RRSIG
+09GM5T42SMIMT7R8DF6RTG80SFMS1NLU.example.org. 1200 IN RRSIG NSEC3 7 3 1200 20120301040838 20120131040838 19562 example.org. EdwMeepLf//lV+KpCAN+213Scv1rrZyj4i2OwoCP4XxxS3CWGSuvYuKO yfZc8wKRcrD/4YG6nZVXE0s5O8NahjBJmDIyVt4WkfZ6QthxGg8ggLVv cD3dFksPyiKHf/WrTOZPSsxvN5m/i1Ey6+YWS01Gf3WDCMWDauC7Nmh3 CTM=
+RKOF8QMFRB5F2V9EJHFBVB2JPVSA0DJD.example.org. 1200 IN NSEC3 1 0 10 AABBCCDD 09GM5T42SMIMT7R8DF6RTG80SFMS1NLU NS SOA RRSIG DNSKEY NSEC3PARAM
+RKOF8QMFRB5F2V9EJHFBVB2JPVSA0DJD.example.org. 1200 IN RRSIG NSEC3 7 3 1200 20120301040838 20120131040838 19562 example.org. j7d8GL4YqX035FBcPPsEcSWHjWcKdlQMHLL4TB67xVNFnl4SEFQCp4OO AtPap5tkKakwgWxoQVN9XjnqrBz+oQhofDkB3aTatAjIIkcwcnrm3AYQ rTI3E03ySiRwuCPKVmHOLUV2cG6O4xzcmP+MYZcvPTS8V3F5LlaU22i7 A3E=
diff --git a/src/lib/datasrc/tests/memory/testdata/example.org-nsec3-signed.zone b/src/lib/datasrc/tests/memory/testdata/example.org-nsec3-signed.zone
new file mode 100644
index 0000000..9c1195f
--- /dev/null
+++ b/src/lib/datasrc/tests/memory/testdata/example.org-nsec3-signed.zone
@@ -0,0 +1,14 @@
+example.org. 86400 IN SOA ns.example.org. ns.example.org. 2012013000 7200 3600 2592000 1200
+example.org. 86400 IN RRSIG SOA 7 2 86400 20120301040838 20120131040838 19562 example.org. Jt9wCRLS5TQxZH0IBqrM9uMGD453rIoxYopfM9AjjRZfEx+HGlBpOZeR pGN7yLcN+URnicOD0ydLHiakaBODiZyNoYCKYG5d2ZOhL+026REnDKNM 0m5T3X3sczP+l55An/GITheTdrKt3Y1Ouc2yKI8ro8JjOxV/a4nGDWjK x9A=
+example.org. 86400 IN NS ns.example.org.
+example.org. 86400 IN RRSIG NS 7 2 86400 20120301040838 20120131040838 19562 example.org. gYXL3xK4IFdJU6TtiVuzqDBb2MeA8xB3AKtHlJGFTfTRNHyuej0ZGovx TeUYsLYmoiGYaJG66iD1tYYFq0qdj0xWq+LEa53ACtKvYf9IIwK4ijJs k0g6xCNavc6/qPppymDhN7MvoFVkW59uJa0HPWlsIIuRlEAr7xyt85vq yoA=
+example.org. 86400 IN DNSKEY 256 3 7 AwEAAbrBkKf2gmGtG4aogZY4svIZCrOLLZlQzVHwz7WxJdTR8iEnvz/x Q/jReDroS5CBZWvzwLlhPIpsJAojx0oj0RvfJNsz3+6LN8q7x9u6+86B 85CYjTk3dcFOebgkF4fXr7/kkOX+ZY94Zk0Z1+pUC3eY4gkKcyME/Uxm O18PBTeB
+example.org. 86400 IN RRSIG DNSKEY 7 2 86400 20120301040838 20120131040838 19562 example.org. d0eLF8JqNHaGuBSX0ashU5c1O/wyWU43UUsKGrMQIoBDiJ588MWQOnas rwvW6vdkLNqRqCsP/B4epV/EtLL0tBsk5SHkTYbNo80gGrBufQ6YrWRr Ile8Z+h+MR4y9DybbjmuNKqaO4uQMg/X6+4HqRAKx1lmZMTcrcVeOwDM ZA4=
+example.org. 0 IN NSEC3PARAM 1 0 10 AABBCCDD
+example.org. 0 IN RRSIG NSEC3PARAM 7 2 0 20120301040838 20120131040838 19562 example.org. Ggs5MiQDlXXt22Fz9DNg3Ujc0T6MBfumlRkd8/enBbJwLmqw2QXAzDEk pjUeGstCEHKzxJDJstavGoCpTDJgoV4Fd9szooMx69rzPrq9tdoM46jG xZHqw+Pv2fkRGC6aP7ZX1r3Qnpwpk47AQgATftbO4G6KcMcO8JoKE47x XLM=
+ns.example.org. 86400 IN A 192.0.2.1
+ns.example.org. 86400 IN RRSIG A 7 3 86400 20120301040838 20120131040838 19562 example.org. dOH+Dxib8VcGnjLrKILsqDhS1wki6BWk1dZwpOGUGHyLWcLNW8ygWY2o r29jPhHtaFCNWpn46JebgnXDPRiQjaY3dQqL8rcf2QX1e3+Cicw1OSrs S0sUDE5DmMNEac+ZCUQ0quCStZTCldl05hlspV2RS92TpApnoOK0nXMp Uak=
+09GM5T42SMIMT7R8DF6RTG80SFMS1NLU.example.org. 1200 IN NSEC3 1 0 10 AABBCCDD RKOF8QMFRB5F2V9EJHFBVB2JPVSA0DJD A RRSIG
+09GM5T42SMIMT7R8DF6RTG80SFMS1NLU.example.org. 1200 IN RRSIG NSEC3 7 3 1200 20120301040838 20120131040838 19562 example.org. EdwMeepLf//lV+KpCAN+213Scv1rrZyj4i2OwoCP4XxxS3CWGSuvYuKO yfZc8wKRcrD/4YG6nZVXE0s5O8NahjBJmDIyVt4WkfZ6QthxGg8ggLVv cD3dFksPyiKHf/WrTOZPSsxvN5m/i1Ey6+YWS01Gf3WDCMWDauC7Nmh3 CTM=
+RKOF8QMFRB5F2V9EJHFBVB2JPVSA0DJD.example.org. 1200 IN NSEC3 1 0 10 AABBCCDD 09GM5T42SMIMT7R8DF6RTG80SFMS1NLU NS SOA RRSIG DNSKEY NSEC3PARAM
+RKOF8QMFRB5F2V9EJHFBVB2JPVSA0DJD.example.org. 1200 IN RRSIG NSEC3 7 3 1200 20120301040838 20120131040838 19562 example.org. j7d8GL4YqX035FBcPPsEcSWHjWcKdlQMHLL4TB67xVNFnl4SEFQCp4OO AtPap5tkKakwgWxoQVN9XjnqrBz+oQhofDkB3aTatAjIIkcwcnrm3AYQ rTI3E03ySiRwuCPKVmHOLUV2cG6O4xzcmP+MYZcvPTS8V3F5LlaU22i7 A3E=
diff --git a/src/lib/datasrc/tests/memory/testdata/example.org-out-of-zone.zone b/src/lib/datasrc/tests/memory/testdata/example.org-out-of-zone.zone
new file mode 100644
index 0000000..e3afb74
--- /dev/null
+++ b/src/lib/datasrc/tests/memory/testdata/example.org-out-of-zone.zone
@@ -0,0 +1,5 @@
+;; 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. 75 3600 300 3600000 3600
+a.example.com. 3600 IN A 192.168.0.1
diff --git a/src/lib/datasrc/tests/memory/testdata/example.org-rrsig-follows-nothing.zone b/src/lib/datasrc/tests/memory/testdata/example.org-rrsig-follows-nothing.zone
new file mode 100644
index 0000000..ef5f887
--- /dev/null
+++ b/src/lib/datasrc/tests/memory/testdata/example.org-rrsig-follows-nothing.zone
@@ -0,0 +1,5 @@
+;; 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. 68 3600 300 3600000 3600
+ns1.example.org. 3600 IN RRSIG A 7 3 3600 20150420235959 20051021000000 40430 example.org. FAKEFAKE
diff --git a/src/lib/datasrc/tests/memory/testdata/example.org-rrsigs.zone b/src/lib/datasrc/tests/memory/testdata/example.org-rrsigs.zone
new file mode 100644
index 0000000..1c780b1
--- /dev/null
+++ b/src/lib/datasrc/tests/memory/testdata/example.org-rrsigs.zone
@@ -0,0 +1,8 @@
+;; 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. 74 3600 300 3600000 3600
+ns1.example.org. 3600 IN A 192.168.0.1
+ns1.example.org. 3600 IN RRSIG A 7 3 3600 20150420235959 20051021000000 40430 example.org. FAKEFAKE
+ns1.example.org. 3600 IN RRSIG A 7 2 3600 20150420235959 20051021000000 40430 example.org. FAKEFAKE
+ns1.example.org. 3600 IN AAAA ::1
diff --git a/src/lib/datasrc/tests/memory/testdata/example.org-wildcard-dname.zone b/src/lib/datasrc/tests/memory/testdata/example.org-wildcard-dname.zone
new file mode 100644
index 0000000..0d03b0d
--- /dev/null
+++ b/src/lib/datasrc/tests/memory/testdata/example.org-wildcard-dname.zone
@@ -0,0 +1,4 @@
+;; test zone file with wildcard DNAME names
+
+example.org. 3600 IN SOA ns1.example.org. bugs.x.w.example.org. 79 3600 300 3600000 3600
+*.example.org. 3600 IN DNAME dname.example.com.
diff --git a/src/lib/datasrc/tests/memory/testdata/example.org-wildcard-ns.zone b/src/lib/datasrc/tests/memory/testdata/example.org-wildcard-ns.zone
new file mode 100644
index 0000000..2933515
--- /dev/null
+++ b/src/lib/datasrc/tests/memory/testdata/example.org-wildcard-ns.zone
@@ -0,0 +1,4 @@
+;; test zone file with wildcard NS names
+
+example.org. 3600 IN SOA ns1.example.org. bugs.x.w.example.org. 78 3600 300 3600000 3600
+*.example.org. 3600 IN NS ns1.example.org.
diff --git a/src/lib/datasrc/tests/memory/testdata/example.org-wildcard-nsec3.zone b/src/lib/datasrc/tests/memory/testdata/example.org-wildcard-nsec3.zone
new file mode 100644
index 0000000..feee116
--- /dev/null
+++ b/src/lib/datasrc/tests/memory/testdata/example.org-wildcard-nsec3.zone
@@ -0,0 +1,4 @@
+;; test zone file with wildcard NS names
+
+example.org. 3600 IN SOA ns1.example.org. bugs.x.w.example.org. 79 3600 300 3600000 3600
+*.example.org. 1200 IN NSEC3 1 0 10 AABBCCDD RKOF8QMFRB5F2V9EJHFBVB2JPVSA0DJD A RRSIG
diff --git a/src/lib/datasrc/tests/memory/testdata/example.org.zone b/src/lib/datasrc/tests/memory/testdata/example.org.zone
new file mode 100644
index 0000000..e499e0d
--- /dev/null
+++ b/src/lib/datasrc/tests/memory/testdata/example.org.zone
@@ -0,0 +1,81 @@
+;; 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. 67 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). One of the NS names matches an empty wildcard node, for
+;; which no additional record should be provided (or any other
+;; disruption should happen).
+e.example.org. 3600 IN NS ns.wild.example.org.
+e.example.org. 3600 IN NS ns.emptywild.example.org.
+e.example.org. 3600 IN NS ns2.example.org.
+*.wild.example.org. 3600 IN A 192.0.2.15
+a.*.emptywild.example.org. 3600 IN AAAA 2001:db8::2
+
+;; 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/memory/treenode_rrset_unittest.cc b/src/lib/datasrc/tests/memory/treenode_rrset_unittest.cc
new file mode 100644
index 0000000..02ad2bd
--- /dev/null
+++ b/src/lib/datasrc/tests/memory/treenode_rrset_unittest.cc
@@ -0,0 +1,631 @@
+// 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 <util/buffer.h>
+#include <util/memory_segment_local.h>
+
+#include <datasrc/memory/treenode_rrset.h>
+#include <datasrc/memory/rdataset.h>
+#include <datasrc/memory/rdata_serialization.h>
+#include <datasrc/memory/zone_data.h>
+
+#include <util/unittests/wiredata.h>
+#include <testutils/dnsmessage_test.h>
+
+#include <gtest/gtest.h>
+
+#include <boost/bind.hpp>
+#include <boost/shared_ptr.hpp>
+
+#include <string>
+#include <vector>
+
+using std::vector;
+using std::string;
+using namespace isc::dns;
+using namespace isc::dns::rdata;
+using namespace isc::datasrc::memory;
+using namespace isc::testutils;
+using isc::util::unittests::matchWireData;
+using isc::util::OutputBuffer;
+
+namespace {
+
+class TreeNodeRRsetTest : public ::testing::Test {
+protected:
+ TreeNodeRRsetTest() :
+ rrclass_(RRClass::IN()),
+ origin_name_("example.com"), www_name_("www.example.com"),
+ wildcard_name_("*.example.com"), match_name_("match.example.com"),
+ ns_rrset_(textToRRset("example.com. 3600 IN NS ns.example.com.")),
+ a_rrset_(textToRRset("www.example.com. 3600 IN A 192.0.2.1\n"
+ "www.example.com. 3600 IN A 192.0.2.2")),
+ aaaa_rrset_(textToRRset("www.example.com. 3600 IN AAAA "
+ "2001:db8::1\n")),
+ dname_rrset_(textToRRset("example.com. 3600 IN DNAME d.example.org.")),
+ a_rrsig_rrset_(textToRRset("www.example.com. 3600 IN RRSIG "
+ "A 5 2 3600 20120814220826 20120715220826 "
+ "1234 example.com. FAKE")),
+ aaaa_rrsig_rrset_(textToRRset("www.example.com. 3600 IN RRSIG AAAA 5 2"
+ " 3600 20120814220826 20120715220826 "
+ "1234 example.com. FAKE\n"
+ "www.example.com. 3600 IN RRSIG AAAA 5 2"
+ " 3600 20120814220826 20120715220826 "
+ "4321 example.com. FAKE\n")),
+ txt_rrsig_rrset_(textToRRset("www.example.com. 3600 IN RRSIG TXT 5 2"
+ " 3600 20120814220826 20120715220826 "
+ "1234 example.com. FAKE\n")),
+ wildmatch_rrset_(textToRRset(
+ "match.example.com. 3600 IN A 192.0.2.1\n"
+ "match.example.com. 3600 IN A 192.0.2.2")),
+ wildmatch_rrsig_rrset_(textToRRset(
+ "match.example.com. 3600 IN RRSIG "
+ "A 5 2 3600 20120814220826 20120715220826 "
+ "1234 example.com. FAKE")),
+ zone_data_(NULL), origin_node_(NULL), www_node_(NULL),
+ wildcard_node_(NULL), ns_rdataset_(NULL), dname_rdataset_(NULL),
+ a_rdataset_(NULL), aaaa_rdataset_(NULL), rrsig_only_rdataset_(NULL),
+ wildcard_rdataset_(NULL)
+ {}
+ void SetUp() {
+ // We create some common test data here in SetUp() so it will be
+ // as exception safe as possible.
+
+ zone_data_ = ZoneData::create(mem_sgmt_, origin_name_);
+
+ zone_data_->insertName(mem_sgmt_, origin_name_, &origin_node_);
+ ns_rdataset_ = RdataSet::create(mem_sgmt_, encoder_, ns_rrset_,
+ ConstRRsetPtr());
+ origin_node_->setData(ns_rdataset_);
+ dname_rdataset_ = RdataSet::create(mem_sgmt_, encoder_, dname_rrset_,
+ ConstRRsetPtr());
+ ns_rdataset_->next = dname_rdataset_;
+
+ zone_data_->insertName(mem_sgmt_, www_name_, &www_node_);
+ a_rdataset_ = RdataSet::create(mem_sgmt_, encoder_, a_rrset_,
+ a_rrsig_rrset_);
+ www_node_->setData(a_rdataset_);
+
+ aaaa_rdataset_ = RdataSet::create(mem_sgmt_, encoder_, aaaa_rrset_,
+ aaaa_rrsig_rrset_);
+ a_rdataset_->next = aaaa_rdataset_;
+
+ // A rare (half broken) case of RRSIG-only set
+ rrsig_only_rdataset_ = RdataSet::create(mem_sgmt_, encoder_,
+ ConstRRsetPtr(),
+ txt_rrsig_rrset_);
+ aaaa_rdataset_->next = rrsig_only_rdataset_;
+
+ zone_data_->insertName(mem_sgmt_, wildcard_name_, &wildcard_node_);
+ wildcard_rdataset_ = RdataSet::create(mem_sgmt_, encoder_, a_rrset_,
+ a_rrsig_rrset_);
+ wildcard_node_->setData(wildcard_rdataset_);
+ }
+ void TearDown() {
+ ZoneData::destroy(mem_sgmt_, zone_data_, rrclass_);
+ // detect any memory leak
+ EXPECT_TRUE(mem_sgmt_.allMemoryDeallocated());
+ }
+
+ const RRClass rrclass_;
+ const Name origin_name_, www_name_, wildcard_name_, match_name_;
+ isc::util::MemorySegmentLocal mem_sgmt_;
+ RdataEncoder encoder_;
+ MessageRenderer renderer_, renderer_expected_;
+ ConstRRsetPtr ns_rrset_, a_rrset_, aaaa_rrset_, dname_rrset_,
+ a_rrsig_rrset_, aaaa_rrsig_rrset_, txt_rrsig_rrset_,
+ wildmatch_rrset_, wildmatch_rrsig_rrset_;
+ ZoneData* zone_data_;
+ ZoneNode* origin_node_;
+ ZoneNode* www_node_;
+ ZoneNode* wildcard_node_;
+ RdataSet* ns_rdataset_;
+ RdataSet* dname_rdataset_;
+ RdataSet* a_rdataset_;
+ RdataSet* aaaa_rdataset_;
+ RdataSet* rrsig_only_rdataset_;
+ RdataSet* wildcard_rdataset_; // for wildcard (type doesn't matter much)
+};
+
+void
+compareRRSIGData(RdataIteratorPtr rit, const void* data, size_t data_len) {
+ ASSERT_FALSE(rit->isLast());
+
+ OutputBuffer buffer(0);
+ rit->getCurrent().toWire(buffer);
+ matchWireData(data, data_len, buffer.getData(), buffer.getLength());
+ rit->next();
+}
+
+// Check some trivial fields of a constructed TreeNodeRRset (passed as
+// AbstractRRset as we'd normally use it in polymorphic way).
+// Other complicated fields are checked through rendering tests.
+void
+checkBasicFields(const AbstractRRset& actual_rrset, const RdataSet* rdataset,
+ const Name& expected_name,
+ const RRClass& expected_class, const RRType& expected_type,
+ const uint32_t expected_ttl,
+ size_t expected_rdatacount, size_t expected_sigcount)
+{
+ EXPECT_EQ(expected_name, actual_rrset.getName());
+ EXPECT_EQ(expected_class, actual_rrset.getClass());
+ EXPECT_EQ(expected_type, actual_rrset.getType());
+ EXPECT_EQ(RRTTL(expected_ttl), actual_rrset.getTTL());
+ EXPECT_EQ(expected_rdatacount, actual_rrset.getRdataCount());
+ EXPECT_EQ(expected_sigcount, actual_rrset.getRRsigDataCount());
+
+ // getRRsig() should return non NULL iff the RRset is expected to be signed
+ if (expected_sigcount == 0) {
+ EXPECT_FALSE(actual_rrset.getRRsig());
+ } else {
+ ConstRRsetPtr actual_sigrrset = actual_rrset.getRRsig();
+ ASSERT_TRUE(actual_sigrrset);
+ EXPECT_EQ(expected_name, actual_sigrrset->getName());
+ EXPECT_EQ(expected_class, actual_sigrrset->getClass());
+ EXPECT_EQ(RRType::RRSIG(), actual_sigrrset->getType());
+ EXPECT_EQ(RRTTL(expected_ttl), actual_sigrrset->getTTL());
+ EXPECT_EQ(expected_sigcount, actual_sigrrset->getRdataCount());
+
+ // Compare each RRSIG RDATA
+ RdataIteratorPtr rit = actual_sigrrset->getRdataIterator();
+ RdataReader reader(expected_class, expected_type,
+ rdataset->getDataBuf(), expected_rdatacount,
+ expected_sigcount, &RdataReader::emptyNameAction,
+ boost::bind(compareRRSIGData, rit, _1, _2));
+ while (reader.nextSig() != RdataReader::RRSET_BOUNDARY) {}
+ EXPECT_TRUE(rit->isLast()); // should check all RDATAs
+ }
+}
+
+// The following two are trivial wrapper to create a shared pointer
+// version of TreeNodeRRset object in order to work around dubious
+// behavior of some C++ compiler: they reject getting a const reference to
+// a temporary non-copyable object.
+boost::shared_ptr<TreeNodeRRset>
+createRRset(const RRClass& rrclass, const ZoneNode* node,
+ const RdataSet* rdataset, bool dnssec_ok)
+{
+ return (boost::shared_ptr<TreeNodeRRset>(
+ new TreeNodeRRset(rrclass, node, rdataset, dnssec_ok)));
+}
+
+boost::shared_ptr<TreeNodeRRset>
+createRRset(const Name& realname, const RRClass& rrclass, const ZoneNode* node,
+ const RdataSet* rdataset, bool dnssec_ok)
+{
+ return (boost::shared_ptr<TreeNodeRRset>(
+ new TreeNodeRRset(realname, rrclass, node, rdataset,
+ dnssec_ok)));
+}
+
+TEST_F(TreeNodeRRsetTest, create) {
+ // Constructed with RRSIG, and it should be visible.
+ checkBasicFields(*createRRset(rrclass_, www_node_, a_rdataset_, true),
+ a_rdataset_, www_name_, rrclass_, RRType::A(), 3600, 2,
+ 1);
+ // Constructed with RRSIG, and it should be invisible.
+ checkBasicFields(*createRRset(rrclass_, www_node_, a_rdataset_, false),
+ a_rdataset_, www_name_, rrclass_, RRType::A(), 3600, 2,
+ 0);
+ // Constructed without RRSIG, and it would be visible (but of course won't)
+ checkBasicFields(*createRRset(rrclass_, origin_node_, ns_rdataset_, true),
+ ns_rdataset_, origin_name_, rrclass_, RRType::NS(), 3600,
+ 1, 0);
+ // Constructed without RRSIG, and it should be visible
+ checkBasicFields(*createRRset(rrclass_, origin_node_, ns_rdataset_, false),
+ ns_rdataset_, origin_name_, rrclass_, RRType::NS(), 3600,
+ 1, 0);
+ // RRSIG-only case (note the RRset's type is covered type)
+ checkBasicFields(*createRRset(rrclass_, www_node_, rrsig_only_rdataset_,
+ true),
+ rrsig_only_rdataset_, www_name_, rrclass_, RRType::TXT(),
+ 3600, 0, 1);
+ // RRSIG-only case (note the RRset's type is covered type), but it's
+ // invisible
+ checkBasicFields(*createRRset(rrclass_, www_node_, rrsig_only_rdataset_,
+ false),
+ rrsig_only_rdataset_, www_name_, rrclass_, RRType::TXT(),
+ 3600, 0, 0);
+ // Wildcard substitution
+ checkBasicFields(*createRRset(match_name_, rrclass_,
+ wildcard_node_, wildcard_rdataset_,
+ true),
+ wildcard_rdataset_, match_name_, rrclass_, RRType::A(),
+ 3600, 2, 1);
+}
+
+// The following two templated functions are helper to encapsulate the
+// concept truncation and handle MessageRenderer and OutputBuffer transparently
+// in templated test cases.
+template <typename OutputType>
+void
+setOutputLengthLimit(OutputType& output, size_t len_limit) {
+ output.setLengthLimit(len_limit);
+}
+template <>
+void
+setOutputLengthLimit<OutputBuffer>(OutputBuffer&, size_t) {
+}
+
+template <typename OutputType>
+bool
+isOutputTruncated(OutputType& output) {
+ return (output.isTruncated());
+}
+template <>
+bool
+isOutputTruncated<OutputBuffer>(OutputBuffer&) {
+ return (false);
+}
+
+// Templated so we so can support OutputBuffer version of toWire().
+// We use the above helper templated functions for some renderer only methods.
+// We test two sets of cases: normal rendering case and case when truncation
+// is expected. The latter is effectively for MessageRenderer only.
+// If len_limit == 0, we consider it the normal case; otherwise it's for
+// truncation. prepended_name isn't used for the truncation case.
+template <typename OutputType>
+void
+checkToWireResult(OutputType& expected_output, OutputType& actual_output,
+ const AbstractRRset& actual_rrset,
+ const Name& prepended_name,
+ ConstRRsetPtr rrset, ConstRRsetPtr rrsig_rrset,
+ bool dnssec_ok,
+ size_t len_limit = 0,
+ size_t expected_result = 0)
+{
+ expected_output.clear();
+ actual_output.clear();
+
+ if (len_limit == 0) { // normal rendering
+ // Prepare "actual" rendered data. We prepend a name to confirm the
+ // owner name should be compressed in both cases.
+ prepended_name.toWire(actual_output);
+ const size_t rdata_count = rrset ? rrset->getRdataCount() : 0;
+ const int expected_ret = (dnssec_ok && rrsig_rrset) ?
+ rdata_count + rrsig_rrset->getRdataCount() : rdata_count;
+ EXPECT_EQ(expected_ret, actual_rrset.toWire(actual_output));
+ } else { // truncation
+ setOutputLengthLimit(actual_output, len_limit);
+ EXPECT_EQ(expected_result, actual_rrset.toWire(actual_output));
+ EXPECT_TRUE(isOutputTruncated(actual_output)); // always true here
+ }
+
+ // Prepare "expected" data.
+ if (len_limit == 0) { // normal rendering
+ prepended_name.toWire(expected_output);
+ } else { // truncation
+ setOutputLengthLimit(expected_output, len_limit);
+ }
+ if (rrset) {
+ rrset->toWire(expected_output);
+ }
+ if (!isOutputTruncated(expected_output) && dnssec_ok && rrsig_rrset) {
+ rrsig_rrset->toWire(expected_output);
+ }
+
+ // Compare the two.
+ matchWireData(expected_output.getData(), expected_output.getLength(),
+ actual_output.getData(), actual_output.getLength());
+}
+
+TEST_F(TreeNodeRRsetTest, toWire) {
+ MessageRenderer expected_renderer, actual_renderer;
+ OutputBuffer expected_buffer(0), actual_buffer(0);
+
+ {
+ SCOPED_TRACE("with RRSIG, DNSSEC OK");
+ const TreeNodeRRset rrset(rrclass_, www_node_, a_rdataset_, true);
+ checkToWireResult(expected_renderer, actual_renderer, rrset,
+ www_name_, a_rrset_, a_rrsig_rrset_, true);
+ // Currently the buffer version throws
+ EXPECT_THROW(
+ checkToWireResult(expected_buffer, actual_buffer, rrset,
+ www_name_, a_rrset_, a_rrsig_rrset_, true),
+ isc::Unexpected);
+ }
+
+ {
+ SCOPED_TRACE("with RRSIG, DNSSEC not OK");
+ const TreeNodeRRset rrset(rrclass_, www_node_, a_rdataset_, false);
+ checkToWireResult(expected_renderer, actual_renderer, rrset,
+ www_name_, a_rrset_, a_rrsig_rrset_, false);
+ }
+
+ {
+ SCOPED_TRACE("without RRSIG, DNSSEC OK");
+ const TreeNodeRRset rrset(rrclass_, origin_node_, ns_rdataset_, true);
+ checkToWireResult(expected_renderer, actual_renderer, rrset,
+ origin_name_, ns_rrset_, ConstRRsetPtr(), true);
+ }
+
+ {
+ SCOPED_TRACE("without RRSIG, DNSSEC not OK");
+ const TreeNodeRRset rrset(rrclass_, origin_node_, ns_rdataset_,
+ false);
+ checkToWireResult(expected_renderer, actual_renderer, rrset,
+ origin_name_, ns_rrset_, ConstRRsetPtr(), false);
+ }
+
+ {
+ // RDATA of DNAME DR shouldn't be compressed. Prepending "example.org"
+ // will check that.
+ SCOPED_TRACE("uncompressed RDATA");
+ const TreeNodeRRset rrset(rrclass_, origin_node_, dname_rdataset_,
+ false);
+ checkToWireResult(expected_renderer, actual_renderer, rrset,
+ Name("example.org"), dname_rrset_, ConstRRsetPtr(),
+ false);
+ }
+
+ {
+ SCOPED_TRACE("wildcard with RRSIG");
+ checkToWireResult(expected_renderer, actual_renderer,
+ *createRRset(match_name_, rrclass_, wildcard_node_,
+ wildcard_rdataset_, true),
+ origin_name_, wildmatch_rrset_,
+ wildmatch_rrsig_rrset_, true);
+ }
+
+ {
+ SCOPED_TRACE("wildcard without RRSIG");
+ checkToWireResult(expected_renderer, actual_renderer,
+ *createRRset(match_name_, rrclass_, wildcard_node_,
+ wildcard_rdataset_, false),
+ origin_name_, wildmatch_rrset_,
+ wildmatch_rrsig_rrset_, false);
+ }
+
+ {
+ // Very unusual case: the set only contains RRSIG (already rare)
+ // and it's requested to be dumped to wire (can only happen in
+ // ANY or type-RRSIG queries, which are rare also). But can still
+ // happen.
+ SCOPED_TRACE("RRSIG only, DNSSEC OK");
+ const TreeNodeRRset rrset(rrclass_, www_node_, rrsig_only_rdataset_,
+ true);
+ checkToWireResult(expected_renderer, actual_renderer, rrset,
+ www_name_, ConstRRsetPtr(), txt_rrsig_rrset_,true);
+ }
+
+ {
+ // Similar to the previous case, but DNSSEC records aren't requested.
+ // In practice this case wouldn't happen, but API-wise possible, so
+ // we test it explicitly.
+ SCOPED_TRACE("RRSIG only, DNSSEC not OK");
+ const TreeNodeRRset rrset(rrclass_, www_node_, rrsig_only_rdataset_,
+ false);
+ checkToWireResult(expected_renderer, actual_renderer, rrset,
+ www_name_, ConstRRsetPtr(), txt_rrsig_rrset_,false);
+ }
+}
+
+TEST_F(TreeNodeRRsetTest, toWireTruncated) {
+ MessageRenderer expected_renderer, actual_renderer;
+ // dummy parameter to checkToWireResult (unused for the this test case)
+ const Name& name = Name::ROOT_NAME();
+
+ // Set the truncation limit to name len + 14 bytes of fixed data for A RR
+ // (type, class, TTL, rdlen, and 4-byte IPv4 address). Then we can only
+ // render just one RR, without any garbage trailing data.
+ checkToWireResult(expected_renderer, actual_renderer,
+ *createRRset(rrclass_, www_node_, a_rdataset_, true),
+ name, a_rrset_, a_rrsig_rrset_, true,
+ www_name_.getLength() + 14,
+ 1); // 1 main RR, no RRSIG
+
+ // The first main RRs should fit in the renderer (the name will be
+ // fully compressed, so its size is 2 bytes), but the RRSIG doesn't.
+ checkToWireResult(expected_renderer, actual_renderer,
+ *createRRset(rrclass_, www_node_, a_rdataset_, true),
+ name, a_rrset_, a_rrsig_rrset_, true,
+ www_name_.getLength() + 14 + 2 + 14,
+ 2); // 2 main RR, no RRSIG
+
+ // This RRset has one main RR and two RRSIGs. Rendering the second RRSIG
+ // causes truncation.
+ // First, compute the rendered length for the main RR and a single RRSIG.
+ // The length of the RRSIG should be the same if we "accidentally"
+ // rendered the RRSIG for the A RR (which only contains one RRSIG).
+ expected_renderer.clear();
+ aaaa_rrset_->toWire(expected_renderer);
+ a_rrsig_rrset_->toWire(expected_renderer);
+ const size_t limit_len = expected_renderer.getLength();
+ // Then perform the test
+ checkToWireResult(expected_renderer, actual_renderer,
+ *createRRset(rrclass_, www_node_, aaaa_rdataset_, true),
+ name, aaaa_rrset_, aaaa_rrsig_rrset_, true, limit_len,
+ 2); // 1 main RR, 1 RRSIG
+
+ // RRSIG only case. Render length limit being 1, so it won't fit,
+ // and will cause truncation.
+ checkToWireResult(expected_renderer, actual_renderer,
+ *createRRset(rrclass_, www_node_, rrsig_only_rdataset_,
+ true),
+ name, ConstRRsetPtr(), txt_rrsig_rrset_, true, 1,
+ 0); // no RR
+}
+
+void
+checkRdataIterator(const vector<string>& expected, RdataIteratorPtr rit) {
+ for (vector<string>::const_iterator it = expected.begin();
+ it != expected.end();
+ ++it)
+ {
+ ASSERT_FALSE(rit->isLast());
+ EXPECT_EQ(*it, rit->getCurrent().toText());
+ rit->next();
+ }
+ // We should have reached the end of RDATA
+ EXPECT_TRUE(rit->isLast());
+
+ // move to the first RDATA again, and check the value.
+ rit->first();
+ if (!expected.empty()) {
+ EXPECT_EQ(expected[0], rit->getCurrent().toText());
+ } else {
+ EXPECT_TRUE(rit->isLast());
+ }
+}
+
+TEST_F(TreeNodeRRsetTest, getRdataIterator) {
+ // This RRset should have 2 A RDATAs
+ vector<string> expected;
+ expected.push_back("192.0.2.1");
+ expected.push_back("192.0.2.2");
+ checkRdataIterator(expected,
+ TreeNodeRRset(rrclass_, www_node_, a_rdataset_, true).
+ getRdataIterator());
+
+ // The iterator shouldn't work different with or without RRSIG
+ checkRdataIterator(expected,
+ TreeNodeRRset(rrclass_, www_node_, a_rdataset_, false).
+ getRdataIterator());
+
+ // This RRset should have 1 NS RDATA (containing name field)
+ expected.clear();
+ expected.push_back("ns.example.com.");
+ checkRdataIterator(expected,
+ TreeNodeRRset(rrclass_, origin_node_, ns_rdataset_,
+ false).getRdataIterator());
+
+ // RRSIG only. Iterator will be empty and shouldn't cause any disruption.
+ expected.clear();
+ checkRdataIterator(expected,
+ TreeNodeRRset(rrclass_, www_node_, rrsig_only_rdataset_,
+ true).getRdataIterator());
+}
+
+void
+checkToText(const AbstractRRset& actual_rrset,
+ ConstRRsetPtr expected_rrset, ConstRRsetPtr expected_sig_rrset)
+{
+ const string actual_text = actual_rrset.toText();
+ const string expected_text =
+ (expected_rrset ? expected_rrset->toText() : "") +
+ (expected_sig_rrset ? expected_sig_rrset->toText() : "");
+ EXPECT_EQ(expected_text, actual_text);
+}
+
+TEST_F(TreeNodeRRsetTest, toText) {
+ // Constructed with RRSIG, and it should be visible.
+ checkToText(*createRRset(rrclass_, www_node_, a_rdataset_, true),
+ a_rrset_, a_rrsig_rrset_);
+ // Constructed with RRSIG, and it should be invisible.
+ checkToText(*createRRset(rrclass_, www_node_, a_rdataset_, false),
+ a_rrset_, ConstRRsetPtr());
+ // Constructed without RRSIG, and it would be visible (but of course won't)
+ checkToText(*createRRset(rrclass_, origin_node_, ns_rdataset_, true),
+ ns_rrset_, ConstRRsetPtr());
+ // Constructed without RRSIG, and it should be visible
+ checkToText(*createRRset(rrclass_, origin_node_, ns_rdataset_, false),
+ ns_rrset_, ConstRRsetPtr());
+ // Wildcard expanded name with RRSIG
+ checkToText(*createRRset(match_name_, rrclass_, wildcard_node_,
+ wildcard_rdataset_, true),
+ wildmatch_rrset_, wildmatch_rrsig_rrset_);
+ // Wildcard expanded name without RRSIG
+ checkToText(*createRRset(match_name_, rrclass_, wildcard_node_,
+ wildcard_rdataset_, false),
+ wildmatch_rrset_, ConstRRsetPtr());
+ // RRSIG case
+ checkToText(*createRRset(rrclass_, www_node_, rrsig_only_rdataset_,
+ true),
+ ConstRRsetPtr(), txt_rrsig_rrset_);
+ // Similar to the previous case, but completely empty.
+ checkToText(*createRRset(rrclass_, www_node_, rrsig_only_rdataset_,
+ false),
+ ConstRRsetPtr(), ConstRRsetPtr());
+}
+
+TEST_F(TreeNodeRRsetTest, isSameKind) {
+ const TreeNodeRRset rrset(rrclass_, www_node_, a_rdataset_, true);
+
+ // Same name (node), same type (rdataset) => same kind
+ EXPECT_TRUE(rrset.isSameKind(*createRRset(rrclass_, www_node_,
+ a_rdataset_, true)));
+
+ // Same name (node), different type (rdataset) => not same kind
+ EXPECT_FALSE(rrset.isSameKind(*createRRset(rrclass_, www_node_,
+ aaaa_rdataset_, true)));
+
+ // Different name, different type => not same kind
+ EXPECT_FALSE(rrset.isSameKind(*createRRset(rrclass_, origin_node_,
+ ns_rdataset_, true)));
+
+ // Different name, same type => not same kind.
+ // Note: this shouldn't happen in our in-memory data source implementation,
+ // but API doesn't prohibit it.
+ EXPECT_FALSE(rrset.isSameKind(*createRRset(rrclass_, origin_node_,
+ a_rdataset_, true)));
+
+ // Wildcard and expanded RRset
+ const TreeNodeRRset wildcard_rrset(rrclass_, wildcard_node_,
+ wildcard_rdataset_, true);
+ const TreeNodeRRset match_rrset(match_name_, rrclass_, wildcard_node_,
+ wildcard_rdataset_, true);
+ EXPECT_FALSE(wildcard_rrset.isSameKind(match_rrset));
+ EXPECT_FALSE(match_rrset.isSameKind(wildcard_rrset));
+
+ // Both are wildcard expanded, and have different names
+ const TreeNodeRRset match2_rrset(Name("match2.example.com"), rrclass_,
+ wildcard_node_, wildcard_rdataset_, true);
+ EXPECT_FALSE(match_rrset.isSameKind(match2_rrset));
+ EXPECT_FALSE(match2_rrset.isSameKind(match_rrset));
+
+ // Pathological case. "badwild" is constructed as if expanded due to
+ // a wildcard, but has the same owner name of the wildcard itself.
+ // Technically, they should be considered of the same kind, but this
+ // implementation considers they are not. But this case shouldn't happen
+ // as long as the RRsets are only constructed inside the in-memory
+ // zone finder implementation.
+ const TreeNodeRRset badwild_rrset(wildcard_name_, rrclass_, wildcard_node_,
+ wildcard_rdataset_, true);
+ EXPECT_FALSE(wildcard_rrset.isSameKind(badwild_rrset));
+ EXPECT_EQ(wildcard_rrset.toText(), badwild_rrset.toText());
+
+ // Pathological case: Same name, same type, but different class.
+ // This case should be impossible because if the RRsets share the same
+ // tree node, they must belong to the same RR class. This case is
+ // a caller's bug, and the isSameKind() implementation returns the
+ // "wrong" (= true) answer.
+ EXPECT_TRUE(rrset.isSameKind(*createRRset(RRClass::CH(), www_node_,
+ a_rdataset_, true)));
+
+ // Same kind of different RRset class
+ EXPECT_TRUE(rrset.isSameKind(*a_rrset_));
+
+ // Different kind of different RRset class
+ EXPECT_FALSE(rrset.isSameKind(*aaaa_rrset_));
+}
+
+TEST_F(TreeNodeRRsetTest, unexpectedMethods) {
+ // Note: buffer version of toWire() is checked in the toWire test.
+
+ TreeNodeRRset rrset(rrclass_, www_node_, a_rdataset_, true);
+
+ EXPECT_THROW(rrset.setTTL(RRTTL(0)), isc::Unexpected);
+ EXPECT_THROW(rrset.setName(Name("example")), isc::Unexpected);
+ EXPECT_THROW(rrset.addRdata(createRdata(RRType::A(), rrclass_, "0.0.0.0")),
+ isc::Unexpected);
+ RdataPtr sig_rdata = createRdata(
+ RRType::RRSIG(), rrclass_,
+ "A 5 2 3600 20120814220826 20120715220826 5300 example.com. FAKE");
+ EXPECT_THROW(rrset.addRRsig(sig_rdata), isc::Unexpected);
+ EXPECT_THROW(rrset.addRRsig(*a_rrsig_rrset_), isc::Unexpected);
+ EXPECT_THROW(rrset.addRRsig(a_rrsig_rrset_), isc::Unexpected);
+ EXPECT_THROW(rrset.addRRsig(RRsetPtr()), isc::Unexpected);
+ EXPECT_THROW(rrset.removeRRsig(), isc::Unexpected);
+}
+}
diff --git a/src/lib/datasrc/tests/memory/zone_data_unittest.cc b/src/lib/datasrc/tests/memory/zone_data_unittest.cc
new file mode 100644
index 0000000..d15fe8b
--- /dev/null
+++ b/src/lib/datasrc/tests/memory/zone_data_unittest.cc
@@ -0,0 +1,255 @@
+// 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 "memory_segment_test.h"
+
+#include <dns/rdataclass.h>
+
+#include <exceptions/exceptions.h>
+
+#include <dns/name.h>
+#include <dns/labelsequence.h>
+#include <dns/rrclass.h>
+
+#include <datasrc/memory/rdata_serialization.h>
+#include <datasrc/memory/rdataset.h>
+#include <datasrc/memory/zone_data.h>
+
+#include <testutils/dnsmessage_test.h>
+
+#include <gtest/gtest.h>
+
+#include <new> // for bad_alloc
+#include <string>
+
+using namespace isc::dns;
+using namespace isc::dns::rdata;
+using namespace isc::datasrc::memory;
+using namespace isc::datasrc::memory::test;
+using namespace isc::testutils;
+
+namespace {
+
+// With this single fixture we'll test both NSEC3Data and ZoneData
+class ZoneDataTest : public ::testing::Test {
+protected:
+ ZoneDataTest() :
+ nsec3_data_(NULL), param_rdata_("1 0 12 aabbccdd"),
+ param_rdata_nosalt_("1 1 10 -"),
+ param_rdata_largesalt_("2 0 5 " + std::string(255 * 2, 'a')),
+ nsec3_rdata_("1 0 12 aabbccdd TDK23RP6 SOA"),
+ nsec3_rdata_nosalt_("1 1 10 - TDK23RP6 SOA"),
+ nsec3_rdata_largesalt_("2 0 5 " + std::string(255 * 2, 'a') +
+ " TDK23RP6 SOA"),
+ zname_("example.com"),
+ zone_data_(ZoneData::create(mem_sgmt_, zname_)),
+ a_rrset_(textToRRset("www.example.com. 3600 IN A 192.0.2.1")),
+ aaaa_rrset_(textToRRset("www.example.com. 3600 IN AAAA 2001:db8::1")),
+ nsec3_rrset_(textToRRset("TDK23RP6.example.com. 3600 IN NSEC3 "
+ "1 0 12 aabbccdd TDK23RP6 SOA"))
+ {}
+ void TearDown() {
+ if (nsec3_data_ != NULL) {
+ NSEC3Data::destroy(mem_sgmt_, nsec3_data_, RRClass::IN());
+ }
+ if (zone_data_ != NULL) {
+ ZoneData::destroy(mem_sgmt_, zone_data_, RRClass::IN());
+ }
+ // detect any memory leak in the test memory segment
+ EXPECT_TRUE(mem_sgmt_.allMemoryDeallocated());
+ }
+
+ MemorySegmentTest mem_sgmt_;
+ NSEC3Data* nsec3_data_;
+ const generic::NSEC3PARAM param_rdata_, param_rdata_nosalt_,
+ param_rdata_largesalt_;
+ const generic::NSEC3 nsec3_rdata_, nsec3_rdata_nosalt_,
+ nsec3_rdata_largesalt_;
+ const Name zname_;
+ ZoneData* zone_data_;
+ const ConstRRsetPtr a_rrset_, aaaa_rrset_, nsec3_rrset_;
+ RdataEncoder encoder_;
+};
+
+// Shared by both test cases using NSEC3 and NSEC3PARAM Rdata
+template <typename RdataType>
+void
+checkNSEC3Data(MemorySegmentTest& mem_sgmt, const RdataType& expect_rdata) {
+ NSEC3Data* nsec3_data = NSEC3Data::create(mem_sgmt, expect_rdata);
+
+ // Internal tree should be created and empty.
+ EXPECT_EQ(0, nsec3_data->getNSEC3Tree().getNodeCount());
+
+ EXPECT_EQ(expect_rdata.getHashalg(), nsec3_data->hashalg);
+ EXPECT_EQ(expect_rdata.getFlags(), nsec3_data->flags);
+ EXPECT_EQ(expect_rdata.getIterations(), nsec3_data->iterations);
+ EXPECT_EQ(expect_rdata.getSalt().size(), nsec3_data->getSaltLen());
+ if (expect_rdata.getSalt().size() > 0) {
+ EXPECT_EQ(0, memcmp(&expect_rdata.getSalt()[0],
+ nsec3_data->getSaltData(),
+ expect_rdata.getSalt().size()));
+ }
+
+ NSEC3Data::destroy(mem_sgmt, nsec3_data, RRClass::IN());
+}
+
+void
+checkFindRdataSet(const ZoneTree& tree, const Name& name, RRType type,
+ const RdataSet* expected_set)
+{
+ ZoneNode* node = NULL;
+ tree.find(name, &node);
+ ASSERT_NE(static_cast<ZoneNode*>(NULL), node);
+ EXPECT_EQ(expected_set, RdataSet::find(node->getData(), type));
+}
+
+TEST_F(ZoneDataTest, createNSEC3Data) {
+ // Create an NSEC3Data object from various types of RDATA (of NSEC3PARAM
+ // and of NSEC3), check if the resulting parameters match.
+ checkNSEC3Data(mem_sgmt_, param_rdata_); // one 'usual' form of params
+ checkNSEC3Data(mem_sgmt_, param_rdata_nosalt_); // empty salt
+ checkNSEC3Data(mem_sgmt_, param_rdata_largesalt_); // max-len salt
+
+ // Same concepts of the tests, using NSEC3 RDATA.
+ checkNSEC3Data(mem_sgmt_, nsec3_rdata_);
+ checkNSEC3Data(mem_sgmt_, nsec3_rdata_nosalt_);
+ checkNSEC3Data(mem_sgmt_, nsec3_rdata_largesalt_);
+}
+
+TEST_F(ZoneDataTest, addNSEC3) {
+ nsec3_data_ = NSEC3Data::create(mem_sgmt_, param_rdata_);
+
+ ZoneNode* node = NULL;
+ nsec3_data_->insertName(mem_sgmt_, nsec3_rrset_->getName(), &node);
+ ASSERT_NE(static_cast<ZoneNode*>(NULL), node);
+ EXPECT_TRUE(node->isEmpty()); // initially it should be empty
+
+ RdataSet* rdataset_nsec3 =
+ RdataSet::create(mem_sgmt_, encoder_, nsec3_rrset_, ConstRRsetPtr());
+ node->setData(rdataset_nsec3);
+
+ // Confirm we can find the added ones from the zone data.
+ checkFindRdataSet(nsec3_data_->getNSEC3Tree(), nsec3_rrset_->getName(),
+ RRType::NSEC3(), rdataset_nsec3);
+
+ // TearDown() will confirm there's no leak on destroy
+}
+
+TEST_F(ZoneDataTest, getOriginNode) {
+ EXPECT_EQ(LabelSequence(zname_), zone_data_->getOriginNode()->getLabels());
+}
+
+TEST_F(ZoneDataTest, exceptionSafetyOnCreate) {
+ // Note: below, we use our knowledge of how memory allocation happens
+ // within the NSEC3Data, the zone data and the underlying domain tree
+ // implementation. We'll emulate rare situations where allocate() fails
+ // with an exception, and confirm it doesn't cause any harsh disruption
+ // or leak.
+
+ // Creating internal NSEC3 tree will succeed, but allocation of NSEC3Data
+ // will fail due to bad_alloc. It shouldn't cause memory leak
+ // (that would be caught in TearDown()).
+ mem_sgmt_.setThrowCount(2);
+ EXPECT_THROW(NSEC3Data::create(mem_sgmt_, param_rdata_), std::bad_alloc);
+
+ // allocate() will throw on the insertion of the origin node.
+ mem_sgmt_.setThrowCount(2);
+ EXPECT_THROW(ZoneData::create(mem_sgmt_, zname_), std::bad_alloc);
+
+ // allocate() will throw on creating the zone data.
+ mem_sgmt_.setThrowCount(3);
+ EXPECT_THROW(ZoneData::create(mem_sgmt_, zname_), std::bad_alloc);
+
+ // These incomplete create() attempts shouldn't cause memory leak
+ // (that would be caught in TearDown()).
+}
+
+TEST_F(ZoneDataTest, addRdataSets) {
+ // Insert a name to the zone, and add a couple the data (RdataSet) objects
+ // to the corresponding node.
+
+ ZoneNode* node = NULL;
+ zone_data_->insertName(mem_sgmt_, a_rrset_->getName(), &node);
+ ASSERT_NE(static_cast<ZoneNode*>(NULL), node);
+ EXPECT_TRUE(node->isEmpty()); // initially it should be empty
+
+ RdataSet* rdataset_a =
+ RdataSet::create(mem_sgmt_, encoder_, a_rrset_, ConstRRsetPtr());
+ node->setData(rdataset_a);
+
+ RdataSet* rdataset_aaaa =
+ RdataSet::create(mem_sgmt_, encoder_, aaaa_rrset_, ConstRRsetPtr());
+ // make a linked list and replace the list head
+ rdataset_aaaa->next = rdataset_a;
+ node->setData(rdataset_aaaa);
+
+ // Confirm we can find the added ones from the zone data.
+ checkFindRdataSet(zone_data_->getZoneTree(), a_rrset_->getName(),
+ RRType::A(), rdataset_a);
+ checkFindRdataSet(zone_data_->getZoneTree(), a_rrset_->getName(),
+ RRType::AAAA(), rdataset_aaaa);
+ // There's no NS (or anything other than AAAA or A) RdataSet in the list
+ checkFindRdataSet(zone_data_->getZoneTree(), a_rrset_->getName(),
+ RRType::NS(), NULL);
+
+ // TearDown() will confirm there's no leak on destroy
+}
+
+TEST_F(ZoneDataTest, getSetNSEC3Data) {
+ // Initially there's no NSEC3 data
+ EXPECT_EQ(static_cast<NSEC3Data*>(NULL), zone_data_->getNSEC3Data());
+ // isNSEC3Signed is true iff zone data has non NULL NSEC3 data
+ EXPECT_FALSE(zone_data_->isNSEC3Signed());
+
+ // Set a new one. The set method should return NULL. The get method
+ // should return the new one.
+ NSEC3Data* nsec3_data = NSEC3Data::create(mem_sgmt_, param_rdata_);
+ NSEC3Data* old_nsec3_data = zone_data_->setNSEC3Data(nsec3_data);
+ EXPECT_EQ(static_cast<NSEC3Data*>(NULL), old_nsec3_data);
+ EXPECT_EQ(nsec3_data, zone_data_->getNSEC3Data());
+ EXPECT_TRUE(zone_data_->isNSEC3Signed());
+
+ // Replace an existing one with a yet another one.
+ // We're responsible for destroying the old one.
+ NSEC3Data* nsec3_data2 = NSEC3Data::create(mem_sgmt_, nsec3_rdata_);
+ old_nsec3_data = zone_data_->setNSEC3Data(nsec3_data2);
+ EXPECT_EQ(nsec3_data, old_nsec3_data);
+ EXPECT_EQ(nsec3_data2, zone_data_->getNSEC3Data());
+ EXPECT_TRUE(zone_data_->isNSEC3Signed());
+ NSEC3Data::destroy(mem_sgmt_, old_nsec3_data, RRClass::IN());
+
+ // Setting NULL clears any existing one.
+ old_nsec3_data = zone_data_->setNSEC3Data(NULL);
+ EXPECT_EQ(nsec3_data2, old_nsec3_data);
+ EXPECT_EQ(static_cast<NSEC3Data*>(NULL), zone_data_->getNSEC3Data());
+ EXPECT_FALSE(zone_data_->isNSEC3Signed());
+
+ // Then set it again. The zone data should destroy it on its own
+ // destruction.
+ zone_data_->setNSEC3Data(old_nsec3_data);
+}
+
+TEST_F(ZoneDataTest, isSigned) {
+ // By default it's considered unsigned
+ EXPECT_FALSE(zone_data_->isSigned());
+
+ // declare it's signed, the isSigned() says so too
+ zone_data_->setSigned(true);
+ EXPECT_TRUE(zone_data_->isSigned());
+
+ // change it to unsigned again
+ zone_data_->setSigned(false);
+ EXPECT_FALSE(zone_data_->isSigned());
+}
+}
diff --git a/src/lib/datasrc/tests/memory/zone_finder_unittest.cc b/src/lib/datasrc/tests/memory/zone_finder_unittest.cc
new file mode 100644
index 0000000..a536bf5
--- /dev/null
+++ b/src/lib/datasrc/tests/memory/zone_finder_unittest.cc
@@ -0,0 +1,1604 @@
+// 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 "memory_segment_test.h"
+
+// NOTE: this faked_nsec3 inclusion (and all related code below)
+// was ported during #2109 for the convenience of implementing #2218
+// In #2218 the NSEC3 test code in this file is expected to be finalized.
+// In #2219 the original is expected to be removed, and this file should
+// probably be moved here (and any leftover code not handled in #2218 should
+// be cleaned up)
+#include "../../tests/faked_nsec3.h"
+
+#include <datasrc/memory/zone_finder.h>
+#include <datasrc/memory/rdata_serialization.h>
+#include <datasrc/data_source.h>
+#include <testutils/dnsmessage_test.h>
+
+#include <boost/foreach.hpp>
+
+#include <gtest/gtest.h>
+
+#include <string>
+
+using namespace std;
+using namespace isc::dns;
+using namespace isc::dns::rdata;
+using namespace isc::datasrc;
+using namespace isc::testutils;
+using boost::shared_ptr;
+using namespace isc::datasrc::test;
+using namespace isc::datasrc::memory::test;
+using namespace isc::datasrc::memory;
+
+namespace {
+// Commonly used result codes (Who should write the prefix all the time)
+using result::SUCCESS;
+using result::EXIST;
+
+/// \brief expensive rrset converter
+///
+/// converts any specialized rrset (which may not have implemented some
+/// methods for efficiency) into a 'full' RRsetPtr, for easy use in test
+/// checks.
+///
+/// Done very inefficiently through text representation, speed should not
+/// be a concern here.
+ConstRRsetPtr
+convertRRset(ConstRRsetPtr src) {
+ return (textToRRset(src->toText()));
+}
+
+/// \brief Test fixture for the InMemoryZoneFinder class
+class InMemoryZoneFinderTest : public ::testing::Test {
+ // A straightforward pair of textual RR(set) and a RRsetPtr variable
+ // to store the RRset. Used to build test data below.
+ struct RRsetData {
+ const char* const text; // textual representation of an RRset
+ RRsetPtr* rrset;
+ };
+protected:
+ // The following sub tests are shared by multiple test cases, changing
+ // the zone's DNSSEC status (unsigned, NSEC-signed or NSEC3-signed).
+ // expected_flags is set to either RESULT_NSEC_SIGNED or
+ // RESULT_NSEC3_SIGNED when it's NSEC/NSEC3 signed respectively and
+ // find() is expected to set the corresponding flags.
+ // find_options should be set to FIND_DNSSEC for NSEC-signed case when
+ // NSEC is expected to be returned.
+ void findCheck(ZoneFinder::FindResultFlags expected_flags =
+ ZoneFinder::RESULT_DEFAULT,
+ ZoneFinder::FindOptions find_options =
+ ZoneFinder::FIND_DEFAULT);
+ void emptyNodeCheck(ZoneFinder::FindResultFlags expected_flags =
+ ZoneFinder::RESULT_DEFAULT);
+ void wildcardCheck(ZoneFinder::FindResultFlags expected_flags =
+ ZoneFinder::RESULT_DEFAULT,
+ ZoneFinder::FindOptions find_options =
+ ZoneFinder::FIND_DEFAULT);
+ void doCancelWildcardCheck(ZoneFinder::FindResultFlags expected_flags =
+ ZoneFinder::RESULT_DEFAULT,
+ ZoneFinder::FindOptions find_options =
+ ZoneFinder::FIND_DEFAULT);
+ void anyWildcardCheck(ZoneFinder::FindResultFlags expected_flags =
+ ZoneFinder::RESULT_DEFAULT);
+ void emptyWildcardCheck(ZoneFinder::FindResultFlags expected_flags =
+ ZoneFinder::RESULT_DEFAULT);
+ void findNSECENTCheck(const Name& ent_name,
+ ConstRRsetPtr expected_nsec,
+ ZoneFinder::FindResultFlags expected_flags =
+ ZoneFinder::RESULT_DEFAULT);
+
+public:
+ InMemoryZoneFinderTest() :
+ class_(RRClass::IN()),
+ origin_("example.org"),
+ zone_data_(ZoneData::create(mem_sgmt_, origin_)),
+ zone_finder_(*zone_data_, class_)
+ {
+ // Build test RRsets. Below, we construct an RRset for
+ // each textual RR(s) of zone_data, and assign it to the corresponding
+ // rr_xxx.
+ // Note that this contains an out-of-zone RR, and due to the
+ // validation check of masterLoad() used below, we cannot add SOA.
+ const RRsetData zone_data[] = {
+ {"example.org. 300 IN NS ns.example.org.", &rr_ns_},
+ {"example.org. 300 IN A 192.0.2.1", &rr_a_},
+ {"ns.example.org. 300 IN A 192.0.2.2", &rr_ns_a_},
+ // This one will place rr_ns_a_ at a zone cut, making it a glue:
+ {"ns.example.org. 300 IN NS 192.0.2.2", &rr_ns_ns_},
+ {"ns.example.org. 300 IN AAAA 2001:db8::2", &rr_ns_aaaa_},
+ {"cname.example.org. 300 IN CNAME canonical.example.org",
+ &rr_cname_},
+ {"cname.example.org. 300 IN A 192.0.2.3", &rr_cname_a_},
+ {"dname.example.org. 300 IN DNAME target.example.org.",
+ &rr_dname_},
+ {"dname.example.org. 300 IN A 192.0.2.39", &rr_dname_a_},
+ {"dname.example.org. 300 IN NS ns.dname.example.org.",
+ &rr_dname_ns_},
+ {"example.org. 300 IN DNAME example.com.", &rr_dname_apex_},
+ {"child.example.org. 300 IN NS ns.child.example.org.",
+ &rr_child_ns_},
+ {"child.example.org. 300 IN DS 12345 5 1 DEADBEEF",
+ &rr_child_ds_},
+ {"ns.child.example.org. 300 IN A 192.0.2.153",
+ &rr_child_glue_},
+ {"grand.child.example.org. 300 IN NS ns.grand.child.example.org.",
+ &rr_grandchild_ns_},
+ {"ns.grand.child.example.org. 300 IN AAAA 2001:db8::253",
+ &rr_grandchild_glue_},
+ {"dname.child.example.org. 300 IN DNAME example.com.",
+ &rr_child_dname_},
+ {"example.com. 300 IN A 192.0.2.10", &rr_out_},
+ {"*.wild.example.org. 300 IN A 192.0.2.1", &rr_wild_},
+ {"*.cnamewild.example.org. 300 IN CNAME canonial.example.org.",
+ &rr_cnamewild_},
+ {"foo.wild.example.org. 300 IN A 192.0.2.3", &rr_under_wild_},
+ {"wild.*.foo.example.org. 300 IN A 192.0.2.1", &rr_emptywild_},
+ {"wild.*.foo.*.bar.example.org. 300 IN A 192.0.2.1",
+ &rr_nested_emptywild_},
+ {"*.nswild.example.org. 300 IN NS nswild.example.", &rr_nswild_},
+ {"*.dnamewild.example.org. 300 IN DNAME dnamewild.example.",
+ &rr_dnamewild_},
+ {"*.child.example.org. 300 IN A 192.0.2.1", &rr_child_wild_},
+ {"bar.foo.wild.example.org. 300 IN A 192.0.2.2", &rr_not_wild_},
+ {"baz.foo.wild.example.org. 300 IN A 192.0.2.3",
+ &rr_not_wild_another_},
+ {"0P9MHAVEQVM6T7VBL5LOP2U3T2RP3TOM.example.org. 300 IN "
+ "NSEC3 1 1 12 aabbccdd 2T7B4G4VSA5SMI47K61MV5BV1A22BOJR A RRSIG",
+ &rr_nsec3_},
+ {"example.org. 300 IN NSEC wild.*.foo.example.org. "
+ "NS SOA RRSIG NSEC DNSKEY", &rr_nsec_},
+ // Together with the apex NSEC, these next NSECs make a complete
+ // chain in the case of the zone for the emptyNonterminal tests
+ // (We may want to clean up this generator code and/or masterLoad
+ // so that we can prepare conflicting datasets better)
+ {"wild.*.foo.example.org. 3600 IN NSEC ns.example.org. "
+ "A RRSIG NSEC", &rr_ent_nsec2_},
+ {"ns.example.org. 3600 IN NSEC foo.wild.example.org. A RRSIG NSEC",
+ &rr_ent_nsec3_},
+ {"foo.wild.example.org. 3600 IN NSEC example.org. A RRSIG NSEC",
+ &rr_ent_nsec4_},
+ // And these are NSECs used in different tests
+ {"ns.example.org. 300 IN NSEC *.nswild.example.org. A AAAA NSEC",
+ &rr_ns_nsec_},
+ {"*.wild.example.org. 300 IN NSEC foo.wild.example.org. A NSEC",
+ &rr_wild_nsec_},
+ {NULL, NULL}
+ };
+
+ for (unsigned int i = 0; zone_data[i].text != NULL; ++i) {
+ *zone_data[i].rrset = textToRRset(zone_data[i].text);
+ }
+
+ }
+
+ ~InMemoryZoneFinderTest() {
+ // Make sure we reset the hash creator to the default
+ setNSEC3HashCreator(NULL);
+ ZoneData::destroy(mem_sgmt_, zone_data_, RRClass::IN());
+ }
+
+ // NSEC3-specific call for 'loading' data
+ void addZoneDataNSEC3(const ConstRRsetPtr rrset) {
+ assert(rrset->getType() == RRType::NSEC3());
+
+ const generic::NSEC3& nsec3_rdata =
+ dynamic_cast<const generic::NSEC3&>(
+ rrset->getRdataIterator()->getCurrent());
+
+ NSEC3Data* nsec3_data = zone_data_->getNSEC3Data();
+ if (nsec3_data == NULL) {
+ nsec3_data = NSEC3Data::create(mem_sgmt_, nsec3_rdata);
+ zone_data_->setNSEC3Data(nsec3_data);
+ } else {
+ const size_t salt_len = nsec3_data->getSaltLen();
+ const uint8_t* salt_data = nsec3_data->getSaltData();
+ const vector<uint8_t>& salt_data_2 = nsec3_rdata.getSalt();
+
+ if ((nsec3_rdata.getHashalg() != nsec3_data->hashalg) ||
+ (nsec3_rdata.getIterations() != nsec3_data->iterations) ||
+ (salt_data_2.size() != salt_len)) {
+ isc_throw(isc::Unexpected,
+ "NSEC3 with inconsistent parameters: " <<
+ rrset->toText());
+ }
+
+ if ((salt_len > 0) &&
+ (std::memcmp(&salt_data_2[0], salt_data, salt_len) != 0)) {
+ isc_throw(isc::Unexpected,
+ "NSEC3 with inconsistent parameters: " <<
+ rrset->toText());
+ }
+ }
+
+ ZoneNode* node;
+ nsec3_data->insertName(mem_sgmt_, rrset->getName(), &node);
+
+ RdataSet* rdset = RdataSet::create(mem_sgmt_, encoder_,
+ rrset, ConstRRsetPtr());
+ RdataSet* old_rdset = node->setData(rdset);
+ if (old_rdset != NULL) {
+ RdataSet::destroy(mem_sgmt_, class_, old_rdset);
+ }
+ zone_data_->setSigned(true);
+ }
+
+ // simplified version of 'loading' data
+ void addZoneData(const ConstRRsetPtr rrset) {
+ ZoneNode* node = NULL;
+
+ if (rrset->getType() == RRType::NSEC3()) {
+ return (addZoneDataNSEC3(rrset));
+ } else if (rrset->getType() == RRType::NSEC()) {
+ zone_data_->setSigned(true);
+ }
+
+ zone_data_->insertName(mem_sgmt_, rrset->getName(), &node);
+
+ if (rrset->getType() == RRType::NS() &&
+ rrset->getName() != zone_data_->getOriginNode()->getName()) {
+ node->setFlag(DomainTreeNode<RdataSet>::FLAG_CALLBACK);
+ } else if (rrset->getType() == RRType::DNAME()) {
+ node->setFlag(DomainTreeNode<RdataSet>::FLAG_CALLBACK);
+ }
+
+ RdataSet* next_rds = node->getData();
+ RdataSet* rdataset =
+ RdataSet::create(mem_sgmt_, encoder_, rrset, rrset->getRRsig());
+ rdataset->next = next_rds;
+ node->setData(rdataset);
+
+ // find wildcard nodes in name (go through all of them in case there
+ // is a nonterminal one)
+ // Note that this method is pretty much equal to the 'real' loader;
+ // but less efficient
+ Name name(rrset->getName());
+ while (name.getLabelCount() > 1) {
+ if (name.isWildcard()) {
+ ZoneNode* wnode = NULL;
+ // add Wild node
+ zone_data_->insertName(mem_sgmt_, name.split(1), &wnode);
+ wnode->setFlag(ZoneData::WILDCARD_NODE);
+ // add wildcard name itself too
+ zone_data_->insertName(mem_sgmt_, name, &wnode);
+ }
+ name = name.split(1);
+ }
+
+ // 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() &&
+ rrset->getName() == origin_) {
+ // We know rrset has exactly one RDATA
+ const generic::NSEC3PARAM& param =
+ dynamic_cast<const generic::NSEC3PARAM&>
+ (rrset->getRdataIterator()->getCurrent());
+
+ NSEC3Data* nsec3_data = zone_data_->getNSEC3Data();
+ if (nsec3_data == NULL) {
+ nsec3_data = NSEC3Data::create(mem_sgmt_, param);
+ zone_data_->setNSEC3Data(nsec3_data);
+ zone_data_->setSigned(true);
+ } else {
+ size_t salt_len = nsec3_data->getSaltLen();
+ const uint8_t* salt_data = nsec3_data->getSaltData();
+ const vector<uint8_t>& salt_data_2 = param.getSalt();
+
+ if ((param.getHashalg() != nsec3_data->hashalg) ||
+ (param.getIterations() != nsec3_data->iterations) ||
+ (salt_data_2.size() != salt_len)) {
+ isc_throw(isc::Unexpected,
+ "NSEC3PARAM with inconsistent parameters: "
+ << rrset->toText());
+ }
+
+ if ((salt_len > 0) &&
+ (std::memcmp(&salt_data_2[0],
+ salt_data, salt_len) != 0)) {
+ isc_throw(isc::Unexpected,
+ "NSEC3PARAM with inconsistent parameters: "
+ << rrset->toText());
+ }
+ }
+ }
+ }
+
+ // Some data to test with
+ const RRClass class_;
+ const Name origin_;
+ // The zone finder to torture by tests
+ MemorySegmentTest mem_sgmt_;
+ memory::ZoneData* zone_data_;
+ memory::InMemoryZoneFinder zone_finder_;
+ isc::datasrc::memory::RdataEncoder encoder_;
+
+ // Placeholder for storing RRsets to be checked with rrsetsCheck()
+ vector<ConstRRsetPtr> actual_rrsets_;
+
+ /*
+ * Some RRsets to put inside the zone.
+ */
+ RRsetPtr
+ // Out of zone RRset
+ rr_out_,
+ // NS of example.org
+ rr_ns_,
+ // A of ns.example.org
+ rr_ns_a_,
+ // AAAA of ns.example.org
+ rr_ns_aaaa_,
+ // A of example.org
+ rr_a_;
+ RRsetPtr rr_ns_ns_; // used to make rr_ns_a_ a glue.
+ RRsetPtr rr_cname_; // CNAME in example.org (RDATA will be added)
+ RRsetPtr rr_cname_a_; // for mixed CNAME + A case
+ RRsetPtr rr_dname_; // DNAME in example.org (RDATA will be added)
+ RRsetPtr rr_dname_a_; // for mixed DNAME + A case
+ RRsetPtr rr_dname_ns_; // for mixed DNAME + NS case
+ RRsetPtr rr_dname_apex_; // for mixed DNAME + NS case in the apex
+ RRsetPtr rr_child_ns_; // NS of a child domain (for delegation)
+ RRsetPtr rr_child_ds_; // DS of a child domain (for delegation, auth data)
+ RRsetPtr rr_child_glue_; // glue RR of the child domain
+ RRsetPtr rr_grandchild_ns_; // NS below a zone cut (unusual)
+ RRsetPtr rr_grandchild_glue_; // glue RR below a deeper zone cut
+ RRsetPtr rr_child_dname_; // A DNAME under NS
+ RRsetPtr rr_wild_; // Wildcard record
+ RRsetPtr rr_cnamewild_; // CNAME at a wildcard
+ RRsetPtr rr_emptywild_;
+ RRsetPtr rr_nested_emptywild_;
+ RRsetPtr rr_nswild_, rr_dnamewild_;
+ RRsetPtr rr_child_wild_;
+ RRsetPtr rr_under_wild_;
+ RRsetPtr rr_not_wild_;
+ RRsetPtr rr_not_wild_another_;
+ RRsetPtr rr_nsec3_;
+ RRsetPtr rr_nsec_;
+ RRsetPtr rr_ent_nsec2_;
+ RRsetPtr rr_ent_nsec3_;
+ RRsetPtr rr_ent_nsec4_;
+ RRsetPtr rr_ns_nsec_;
+ RRsetPtr rr_wild_nsec_;
+
+ /**
+ * \brief Test one find query to the zone finder.
+ *
+ * Asks a query to the zone finder and checks it does not throw and returns
+ * expected results. It returns nothing, it just signals failures
+ * to GTEST.
+ *
+ * \param name The name to ask for.
+ * \param rrtype The RRType to ask of.
+ * \param result The expected code of the result.
+ * \param check_answer Should a check against equality of the answer be
+ * done?
+ * \param answer The expected rrset, if any should be returned.
+ * \param expected_flags The expected result flags returned via find().
+ * These can be tested using isWildcard() etc.
+ * \param zone_finder Check different InMemoryZoneFinder object than
+ * zone_finder_ (if NULL, uses zone_finder_)
+ * \param check_wild_answer Checks that the answer has the same RRs, type
+ * class and TTL as the eqxpected answer and that the name corresponds
+ * to the one searched. It is meant for checking answers for wildcard
+ * queries.
+ */
+ void findTest(const Name& name, const RRType& rrtype,
+ ZoneFinder::Result result,
+ bool check_answer = true,
+ const ConstRRsetPtr& answer = ConstRRsetPtr(),
+ ZoneFinder::FindResultFlags expected_flags =
+ ZoneFinder::RESULT_DEFAULT,
+ memory::InMemoryZoneFinder* zone_finder = NULL,
+ ZoneFinder::FindOptions options = ZoneFinder::FIND_DEFAULT,
+ bool check_wild_answer = false)
+ {
+ SCOPED_TRACE("findTest for " + name.toText() + "/" + rrtype.toText());
+
+ if (zone_finder == NULL) {
+ zone_finder = &zone_finder_;
+ }
+ const ConstRRsetPtr answer_sig = answer ? answer->getRRsig() :
+ RRsetPtr(); // note we use the same type as of retval of getRRsig()
+ // The whole block is inside, because we need to check the result and
+ // we can't assign to FindResult
+ EXPECT_NO_THROW({
+ ZoneFinderContextPtr find_result(zone_finder->find(
+ name, rrtype, options));
+ // Check it returns correct answers
+ EXPECT_EQ(result, find_result->code);
+ EXPECT_EQ((expected_flags & ZoneFinder::RESULT_WILDCARD) != 0,
+ find_result->isWildcard());
+ EXPECT_EQ((expected_flags & ZoneFinder::RESULT_NSEC_SIGNED)
+ != 0, find_result->isNSECSigned());
+ EXPECT_EQ((expected_flags & ZoneFinder::RESULT_NSEC3_SIGNED)
+ != 0, find_result->isNSEC3Signed());
+ if (check_answer) {
+ if (!answer) {
+ ASSERT_FALSE(find_result->rrset);
+ } else {
+ ASSERT_TRUE(find_result->rrset);
+ ConstRRsetPtr result_rrset(
+ convertRRset(find_result->rrset));
+ rrsetCheck(answer, result_rrset);
+ if (answer_sig &&
+ (options & ZoneFinder::FIND_DNSSEC) != 0) {
+ ASSERT_TRUE(result_rrset->getRRsig());
+ rrsetCheck(answer_sig, result_rrset->getRRsig());
+ } else {
+ EXPECT_FALSE(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) <<
+ "No answer found";
+ // Build the expected answer using the given name and
+ // other parameter of the base wildcard RRset.
+ RRsetPtr wildanswer(new RRset(name, answer->getClass(),
+ answer->getType(),
+ answer->getTTL()));
+ RdataIteratorPtr expectedIt(answer->getRdataIterator());
+ for (; !expectedIt->isLast(); expectedIt->next()) {
+ wildanswer->addRdata(expectedIt->getCurrent());
+ }
+
+ ConstRRsetPtr result_rrset(
+ convertRRset(find_result->rrset));
+ rrsetCheck(wildanswer, result_rrset);
+
+ // Same for the RRSIG, if any.
+ if (answer_sig) {
+ ASSERT_TRUE(result_rrset->getRRsig());
+
+ RRsetPtr wildsig(new RRset(name,
+ answer_sig->getClass(),
+ RRType::RRSIG(),
+ answer_sig->getTTL()));
+ RdataIteratorPtr expectedIt(
+ answer_sig->getRdataIterator());
+ for (; !expectedIt->isLast(); expectedIt->next()) {
+ wildsig->addRdata(expectedIt->getCurrent());
+ }
+ rrsetCheck(wildsig, result_rrset->getRRsig());
+ }
+ }
+ });
+ }
+ /**
+ * \brief Calls the findAll on the finder and checks the result.
+ */
+ void findAllTest(const Name& name, ZoneFinder::Result result,
+ const vector<ConstRRsetPtr>& expected_rrsets,
+ ZoneFinder::FindResultFlags expected_flags =
+ ZoneFinder::RESULT_DEFAULT,
+ memory::InMemoryZoneFinder* finder = NULL,
+ const ConstRRsetPtr &rrset_result = ConstRRsetPtr(),
+ ZoneFinder::FindOptions options =
+ ZoneFinder::FIND_DEFAULT)
+ {
+ if (finder == NULL) {
+ finder = &zone_finder_;
+ }
+ std::vector<ConstRRsetPtr> target;
+ ZoneFinderContextPtr find_result(finder->findAll(name, target,
+ options));
+ EXPECT_EQ(result, find_result->code);
+ if (!rrset_result) {
+ EXPECT_FALSE(find_result->rrset);
+ } else {
+ ASSERT_TRUE(find_result->rrset);
+ rrsetCheck(rrset_result, convertRRset(find_result->rrset));
+ }
+ EXPECT_EQ((expected_flags & ZoneFinder::RESULT_WILDCARD) != 0,
+ find_result->isWildcard());
+ EXPECT_EQ((expected_flags & ZoneFinder::RESULT_NSEC_SIGNED)
+ != 0, find_result->isNSECSigned());
+ EXPECT_EQ((expected_flags & ZoneFinder::RESULT_NSEC3_SIGNED)
+ != 0, find_result->isNSEC3Signed());
+ // Convert all rrsets to 'full' ones before checking
+ std::vector<ConstRRsetPtr> converted_rrsets;
+ BOOST_FOREACH(ConstRRsetPtr cur_rrset, target) {
+ converted_rrsets.push_back(convertRRset(cur_rrset));
+ }
+ rrsetsCheck(expected_rrsets.begin(), expected_rrsets.end(),
+ converted_rrsets.begin(), converted_rrsets.end());
+ }
+};
+
+/**
+ * \brief Test InMemoryZoneFinder::InMemoryZoneFinder constructor.
+ *
+ * Takes the created zone finder and checks its properties they are the same
+ * as passed parameters.
+ */
+TEST_F(InMemoryZoneFinderTest, constructor) {
+ ASSERT_EQ(origin_, zone_finder_.getOrigin());
+
+ // Some unusual (abnormal case): if we add a super domain name of the
+ // zone somehow, the label of the origin node won't be absolute.
+ // getOrigin() should still be the correct one.
+ ZoneNode *node;
+ zone_data_->insertName(mem_sgmt_, Name("org"), &node);
+ ASSERT_EQ(origin_, zone_finder_.getOrigin());
+}
+
+TEST_F(InMemoryZoneFinderTest, findCNAME) {
+ // install CNAME RR
+ addZoneData(rr_cname_);
+
+ // Find A RR of the same. Should match the CNAME
+ findTest(rr_cname_->getName(), RRType::NS(), ZoneFinder::CNAME, true,
+ rr_cname_);
+
+ // Find the CNAME itself. Should result in normal SUCCESS
+ findTest(rr_cname_->getName(), RRType::CNAME(), ZoneFinder::SUCCESS, true,
+ rr_cname_);
+}
+
+TEST_F(InMemoryZoneFinderTest, findCNAMEUnderZoneCut) {
+ // There's nothing special when we find a CNAME under a zone cut
+ // (with FIND_GLUE_OK). The behavior is different from BIND 9,
+ // so we test this case explicitly.
+ addZoneData(rr_child_ns_);
+ ConstRRsetPtr rr_cname_under_cut_ = textToRRset(
+ "cname.child.example.org. 300 IN CNAME target.child.example.org.");
+ addZoneData(rr_cname_under_cut_);
+ findTest(Name("cname.child.example.org"), RRType::AAAA(),
+ ZoneFinder::CNAME, true, rr_cname_under_cut_,
+ ZoneFinder::RESULT_DEFAULT, NULL, ZoneFinder::FIND_GLUE_OK);
+}
+
+// Search under a DNAME record. It should return the DNAME
+TEST_F(InMemoryZoneFinderTest, findBelowDNAME) {
+ EXPECT_NO_THROW(addZoneData(rr_dname_));
+ findTest(Name("below.dname.example.org"), RRType::A(), ZoneFinder::DNAME,
+ true, rr_dname_);
+}
+
+// Search at the domain with DNAME. It should act as DNAME isn't there, DNAME
+// influences only the data below (see RFC 2672, section 3)
+TEST_F(InMemoryZoneFinderTest, findAtDNAME) {
+ EXPECT_NO_THROW(addZoneData(rr_dname_));
+ EXPECT_NO_THROW(addZoneData(rr_dname_a_));
+
+ const Name dname_name(rr_dname_->getName());
+ findTest(dname_name, RRType::A(), ZoneFinder::SUCCESS, true, rr_dname_a_);
+ findTest(dname_name, RRType::DNAME(), ZoneFinder::SUCCESS, true,
+ rr_dname_);
+ findTest(dname_name, RRType::TXT(), ZoneFinder::NXRRSET, true);
+}
+
+// Try searching something that is both under NS and DNAME, without and with
+// GLUE_OK mode (it should stop at the NS and DNAME respectively).
+TEST_F(InMemoryZoneFinderTest, DNAMEUnderNS) {
+ addZoneData(rr_child_ns_);
+ addZoneData(rr_child_dname_);
+
+ Name lowName("below.dname.child.example.org.");
+
+ findTest(lowName, RRType::A(), ZoneFinder::DELEGATION, true, rr_child_ns_);
+ findTest(lowName, RRType::A(), ZoneFinder::DNAME, true, rr_child_dname_,
+ ZoneFinder::RESULT_DEFAULT, NULL, ZoneFinder::FIND_GLUE_OK);
+}
+
+// Test adding child zones and zone cut handling
+TEST_F(InMemoryZoneFinderTest, delegationNS) {
+ // add in-zone data
+ EXPECT_NO_THROW(addZoneData(rr_ns_));
+
+ // install a zone cut
+ EXPECT_NO_THROW(addZoneData(rr_child_ns_));
+
+ // below the zone cut
+ findTest(Name("www.child.example.org"), RRType::A(),
+ ZoneFinder::DELEGATION, true, rr_child_ns_);
+
+ // at the zone cut
+ findTest(Name("child.example.org"), RRType::A(), ZoneFinder::DELEGATION,
+ true, rr_child_ns_);
+ findTest(Name("child.example.org"), RRType::NS(), ZoneFinder::DELEGATION,
+ true, rr_child_ns_);
+
+ // finding NS for the apex (origin) node. This must not be confused
+ // with delegation due to the existence of an NS RR.
+ findTest(origin_, RRType::NS(), ZoneFinder::SUCCESS, true, rr_ns_);
+
+ // unusual case of "nested delegation": the highest cut should be used.
+ EXPECT_NO_THROW(addZoneData(rr_grandchild_ns_));
+ findTest(Name("www.grand.child.example.org"), RRType::A(),
+ // note: !rr_grandchild_ns_
+ ZoneFinder::DELEGATION, true, rr_child_ns_);
+}
+
+TEST_F(InMemoryZoneFinderTest, delegationWithDS) {
+ // Similar setup to the previous one, but with DS RR at the delegation
+ // point.
+ addZoneData(rr_ns_);
+ addZoneData(rr_child_ns_);
+ addZoneData(rr_child_ds_);
+
+ // Normal types of query should result in delegation, but DS query
+ // should be considered in-zone (but only exactly at the delegation point).
+ findTest(Name("child.example.org"), RRType::A(), ZoneFinder::DELEGATION,
+ true, rr_child_ns_);
+ findTest(Name("child.example.org"), RRType::DS(), ZoneFinder::SUCCESS,
+ true, rr_child_ds_);
+ findTest(Name("grand.child.example.org"), RRType::DS(),
+ ZoneFinder::DELEGATION, true, rr_child_ns_);
+
+ // There's nothing special for DS query at the zone apex. It would
+ // normally result in NXRRSET.
+ findTest(Name("example.org"), RRType::DS(), ZoneFinder::NXRRSET,
+ true, ConstRRsetPtr());
+}
+
+TEST_F(InMemoryZoneFinderTest, findAny) {
+ EXPECT_NO_THROW(addZoneData(rr_a_));
+ EXPECT_NO_THROW(addZoneData(rr_ns_));
+ EXPECT_NO_THROW(addZoneData(rr_child_glue_));
+
+ vector<ConstRRsetPtr> expected_sets;
+
+ // origin
+ expected_sets.push_back(rr_a_);
+ expected_sets.push_back(rr_ns_);
+ findAllTest(origin_, ZoneFinder::SUCCESS, expected_sets);
+
+ // out zone name
+ EXPECT_THROW(findAllTest(Name("example.com"), ZoneFinder::NXDOMAIN,
+ vector<ConstRRsetPtr>()),
+ OutOfZone);
+
+ expected_sets.clear();
+ expected_sets.push_back(rr_child_glue_);
+ findAllTest(rr_child_glue_->getName(), ZoneFinder::SUCCESS, expected_sets);
+
+ // add zone cut
+ EXPECT_NO_THROW(addZoneData(rr_child_ns_));
+
+ // zone cut
+ findAllTest(rr_child_ns_->getName(), ZoneFinder::DELEGATION,
+ vector<ConstRRsetPtr>(), ZoneFinder::RESULT_DEFAULT,
+ NULL, rr_child_ns_);
+
+ // glue for this zone cut
+ findAllTest(rr_child_glue_->getName(),ZoneFinder::DELEGATION,
+ vector<ConstRRsetPtr>(), ZoneFinder::RESULT_DEFAULT,
+ NULL, rr_child_ns_);
+}
+
+TEST_F(InMemoryZoneFinderTest, glue) {
+ // install zone data:
+ // a zone cut
+ EXPECT_NO_THROW(addZoneData(rr_child_ns_));
+ // glue for this cut
+ EXPECT_NO_THROW(addZoneData(rr_child_glue_));
+ // a nested zone cut (unusual)
+ EXPECT_NO_THROW(addZoneData(rr_grandchild_ns_));
+ // glue under the deeper zone cut
+ EXPECT_NO_THROW(addZoneData(rr_grandchild_glue_));
+ // glue 'at the' zone cut
+ EXPECT_NO_THROW(addZoneData(rr_ns_a_));
+ EXPECT_NO_THROW(addZoneData(rr_ns_ns_));
+
+ // by default glue is hidden due to the zone cut
+ findTest(rr_child_glue_->getName(), RRType::A(), ZoneFinder::DELEGATION,
+ true, rr_child_ns_);
+
+
+ // If we do it in the "glue OK" mode, we should find the exact match.
+ findTest(rr_child_glue_->getName(), RRType::A(), ZoneFinder::SUCCESS, true,
+ rr_child_glue_, ZoneFinder::RESULT_DEFAULT, NULL,
+ ZoneFinder::FIND_GLUE_OK);
+
+ // glue OK + NXRRSET case
+ findTest(rr_child_glue_->getName(), RRType::AAAA(), ZoneFinder::NXRRSET,
+ true, ConstRRsetPtr(), ZoneFinder::RESULT_DEFAULT, NULL,
+ ZoneFinder::FIND_GLUE_OK);
+
+ // glue OK + NXDOMAIN case
+ findTest(Name("www.child.example.org"), RRType::A(),
+ ZoneFinder::DELEGATION, true, rr_child_ns_,
+ ZoneFinder::RESULT_DEFAULT, NULL, ZoneFinder::FIND_GLUE_OK);
+
+ // nested cut case. The glue should be found.
+ findTest(rr_grandchild_glue_->getName(), RRType::AAAA(),
+ ZoneFinder::SUCCESS,
+ true, rr_grandchild_glue_, ZoneFinder::RESULT_DEFAULT, NULL,
+ ZoneFinder::FIND_GLUE_OK);
+
+ // A non-existent name in nested cut. This should result in delegation
+ // at the highest zone cut.
+ findTest(Name("www.grand.child.example.org"), RRType::TXT(),
+ ZoneFinder::DELEGATION, true, rr_child_ns_,
+ ZoneFinder::RESULT_DEFAULT, NULL, ZoneFinder::FIND_GLUE_OK);
+
+ // Glue at a zone cut
+ findTest(Name("ns.example.org"), RRType::A(),
+ ZoneFinder::DELEGATION, true, rr_ns_ns_);
+ findTest(Name("ns.example.org"), RRType::A(), ZoneFinder::SUCCESS,
+ true, rr_ns_a_, ZoneFinder::RESULT_DEFAULT,
+ NULL, ZoneFinder::FIND_GLUE_OK);
+}
+
+/**
+ * \brief Test searching.
+ *
+ * Check it finds or does not find correctly and does not throw exceptions.
+ */
+void
+InMemoryZoneFinderTest::findCheck(ZoneFinder::FindResultFlags expected_flags,
+ ZoneFinder::FindOptions find_options)
+{
+ // Fill some data inside
+ // Now put all the data we have there. It should throw nothing
+ rr_a_->addRRsig(createRdata(RRType::RRSIG(), RRClass::IN(),
+ "A 5 3 3600 20120814220826 20120715220826 "
+ "1234 example.com. FAKE"));
+ EXPECT_NO_THROW(addZoneData(rr_ns_));
+ EXPECT_NO_THROW(addZoneData(rr_ns_a_));
+ EXPECT_NO_THROW(addZoneData(rr_ns_aaaa_));
+ EXPECT_NO_THROW(addZoneData(rr_a_));
+ if ((expected_flags & ZoneFinder::RESULT_NSEC3_SIGNED) != 0) {
+ addZoneData(rr_nsec3_);
+ zone_data_->setSigned(true);
+ }
+ if ((expected_flags & ZoneFinder::RESULT_NSEC_SIGNED) != 0) {
+ addZoneData(rr_nsec_);
+ zone_data_->setSigned(true);
+ }
+
+ // These two should be successful
+ findTest(origin_, RRType::NS(), ZoneFinder::SUCCESS, true, rr_ns_);
+ findTest(rr_ns_a_->getName(), RRType::A(), ZoneFinder::SUCCESS, true,
+ rr_ns_a_);
+
+ // Similar test for a signed RRset. We should see the RRSIG iff
+ // FIND_DNSSEC option is specified.
+ findTest(rr_a_->getName(), RRType::A(), ZoneFinder::SUCCESS, true, rr_a_);
+ findTest(rr_a_->getName(), RRType::A(), ZoneFinder::SUCCESS, true,
+ rr_a_, ZoneFinder::RESULT_DEFAULT, NULL, ZoneFinder::FIND_DNSSEC);
+
+ // These domains don't exist. (and one is out of the zone). In an
+ // NSEC-signed zone with DNSSEC records requested, it should return the
+ // covering NSEC for the query name (the actual NSEC in the test data may
+ // not really "cover" it, but for the purpose of this test it's okay).
+ ConstRRsetPtr expected_nsec; // by default it's NULL
+ if ((expected_flags & ZoneFinder::RESULT_NSEC_SIGNED) != 0 &&
+ (find_options & ZoneFinder::FIND_DNSSEC) != 0) {
+ expected_nsec = rr_nsec_;
+ }
+
+ // There's no other name between this one and the origin, so when NSEC
+ // is to be returned it should be the origin NSEC.
+ findTest(Name("nothere.example.org"), RRType::A(),
+ ZoneFinder::NXDOMAIN, true, expected_nsec, expected_flags,
+ NULL, find_options);
+
+ // The previous name in the zone is "ns.example.org", but it doesn't
+ // have an NSEC. It should be skipped and the origin NSEC will be
+ // returned as the "closest NSEC".
+ findTest(Name("nxdomain.example.org"), RRType::A(),
+ ZoneFinder::NXDOMAIN, true, expected_nsec, expected_flags,
+ NULL, find_options);
+ EXPECT_THROW(zone_finder_.find(Name("example.net"), RRType::A()),
+ OutOfZone);
+
+ // These domain exist but don't have the provided RRType. For the latter
+ // one we now add its NSEC (which was delayed so that it wouldn't break
+ // other cases above).
+ findTest(origin_, RRType::AAAA(), ZoneFinder::NXRRSET, true,
+ expected_nsec, expected_flags, NULL, find_options);
+
+ if ((expected_flags & ZoneFinder::RESULT_NSEC_SIGNED) != 0) {
+ addZoneData(rr_ns_nsec_);
+ zone_data_->setSigned(true);
+ if ((find_options & ZoneFinder::FIND_DNSSEC) != 0) {
+ expected_nsec = rr_ns_nsec_;
+ }
+ }
+ findTest(rr_ns_a_->getName(), RRType::NS(), ZoneFinder::NXRRSET, true,
+ expected_nsec, expected_flags, NULL, find_options);
+}
+
+TEST_F(InMemoryZoneFinderTest, find) {
+ findCheck();
+}
+
+TEST_F(InMemoryZoneFinderTest, findNSEC3Signed) {
+ findCheck(ZoneFinder::RESULT_NSEC3_SIGNED);
+}
+
+TEST_F(InMemoryZoneFinderTest, findNSEC3SignedWithDNSSEC) {
+ // For NSEC3-signed zones, specifying the DNSSEC option shouldn't change
+ // anything (the NSEC3_SIGNED flag is always set, and no records are
+ // returned for negative cases regardless).
+ findCheck(ZoneFinder::RESULT_NSEC3_SIGNED, ZoneFinder::FIND_DNSSEC);
+}
+
+TEST_F(InMemoryZoneFinderTest, findNSECSigned) {
+ // NSEC-signed zone, without requesting DNSSEC (no NSEC should be provided)
+ findCheck(ZoneFinder::RESULT_NSEC_SIGNED);
+}
+
+// Generalized test for Empty Nonterminal (ENT) cases with NSEC
+void
+InMemoryZoneFinderTest::findNSECENTCheck(const Name& ent_name,
+ ConstRRsetPtr expected_nsec,
+ ZoneFinder::FindResultFlags expected_flags)
+{
+ addZoneData(rr_emptywild_);
+ addZoneData(rr_under_wild_);
+
+ // Sanity check: Should result in NXRRSET
+ findTest(ent_name, RRType::A(), ZoneFinder::NXRRSET, true,
+ ConstRRsetPtr(), expected_flags);
+ // Sanity check: No NSEC added yet
+ findTest(ent_name, RRType::A(), ZoneFinder::NXRRSET, true,
+ ConstRRsetPtr(), expected_flags,
+ NULL, ZoneFinder::FIND_DNSSEC);
+
+ // Now add the NSEC rrs making it a 'complete' zone (in terms of NSEC,
+ // there are no sigs)
+ addZoneData(rr_nsec_);
+ addZoneData(rr_ent_nsec2_);
+ addZoneData(rr_ent_nsec3_);
+ addZoneData(rr_ent_nsec4_);
+ zone_data_->setSigned(true);
+
+ // Should result in NXRRSET, and RESULT_NSEC_SIGNED
+ findTest(ent_name, RRType::A(), ZoneFinder::NXRRSET, true,
+ ConstRRsetPtr(),
+ expected_flags | ZoneFinder::RESULT_NSEC_SIGNED);
+
+ // And check for the NSEC if DNSSEC_OK
+ findTest(ent_name, RRType::A(), ZoneFinder::NXRRSET, true,
+ expected_nsec, expected_flags | ZoneFinder::RESULT_NSEC_SIGNED,
+ NULL, ZoneFinder::FIND_DNSSEC);
+}
+
+TEST_F(InMemoryZoneFinderTest,findNSECEmptyNonterminal) {
+ // Non-wildcard case
+ findNSECENTCheck(Name("wild.example.org"), rr_ent_nsec3_);
+}
+
+TEST_F(InMemoryZoneFinderTest, findNSECEmptyNonterminalWildcard) {
+ // Wildcard case, above actual wildcard
+ findNSECENTCheck(Name("foo.example.org"), rr_nsec_);
+}
+
+TEST_F(InMemoryZoneFinderTest, findNSECEmptyNonterminalAtWildcard) {
+ // Wildcard case, at actual wildcard
+ findNSECENTCheck(Name("bar.foo.example.org"), rr_nsec_,
+ ZoneFinder::RESULT_WILDCARD);
+}
+
+TEST_F(InMemoryZoneFinderTest, findNSECSignedWithDNSSEC) {
+ // NSEC-signed zone, requesting DNSSEC (NSEC should be provided)
+ findCheck(ZoneFinder::RESULT_NSEC_SIGNED, ZoneFinder::FIND_DNSSEC);
+}
+
+void
+InMemoryZoneFinderTest::emptyNodeCheck(
+ ZoneFinder::FindResultFlags expected_flags)
+{
+ /*
+ * The backend ZoneTree for this test should look like as follows:
+ * example.org
+ * |
+ * baz (empty; easy case)
+ * / | \
+ * bar | x.foo ('foo' part is empty; a bit trickier)
+ * bbb
+ * /
+ * aaa
+ */
+
+ // Construct the test zone
+ const char* const names[] = {
+ "bar.example.org.", "x.foo.example.org.", "aaa.baz.example.org.",
+ "bbb.baz.example.org.", NULL};
+ for (int i = 0; names[i] != NULL; ++i) {
+ ConstRRsetPtr rrset = textToRRset(string(names[i]) +
+ " 300 IN A 192.0.2.1");
+ addZoneData(rrset);
+ }
+ if ((expected_flags & ZoneFinder::RESULT_NSEC3_SIGNED) != 0) {
+ addZoneData(rr_nsec3_);
+ zone_data_->setSigned(true);
+ }
+ if ((expected_flags & ZoneFinder::RESULT_NSEC_SIGNED) != 0) {
+ addZoneData(rr_nsec_);
+ zone_data_->setSigned(true);
+ }
+
+ // empty node matching, easy case: the node for 'baz' exists with
+ // no data.
+ findTest(Name("baz.example.org"), RRType::A(), ZoneFinder::NXRRSET, true,
+ ConstRRsetPtr(), expected_flags);
+
+ // empty node matching, a trickier case: the node for 'foo' is part of
+ // "x.foo", which should be considered an empty node.
+ findTest(Name("foo.example.org"), RRType::A(), ZoneFinder::NXRRSET, true,
+ ConstRRsetPtr(), expected_flags);
+
+ // "org" is contained in "example.org", but it shouldn't be treated as
+ // NXRRSET because it's out of zone.
+ // Note: basically we don't expect such a query to be performed (the common
+ // operation is to identify the best matching zone first then perform
+ // search it), but we shouldn't be confused even in the unexpected case.
+ EXPECT_THROW(zone_finder_.find(Name("org"), RRType::A()), OutOfZone);
+}
+
+TEST_F(InMemoryZoneFinderTest, emptyNode) {
+ emptyNodeCheck();
+}
+
+TEST_F(InMemoryZoneFinderTest, emptyNodeNSEC3) {
+ emptyNodeCheck(ZoneFinder::RESULT_NSEC3_SIGNED);
+}
+
+TEST_F(InMemoryZoneFinderTest, emptyNodeNSEC) {
+ emptyNodeCheck(ZoneFinder::RESULT_NSEC_SIGNED);
+}
+
+/*
+ * Test that puts a (simple) wildcard into the zone and checks we can
+ * correctly find the data.
+ */
+void
+InMemoryZoneFinderTest::wildcardCheck(
+ ZoneFinder::FindResultFlags expected_flags,
+ ZoneFinder::FindOptions find_options)
+{
+ /*
+ * example.org.
+ * |
+ * [cname]wild (not *.wild, should have wild mark)
+ * |
+ * *
+ */
+
+ // If the zone is "signed" (detecting it by the NSEC/NSEC3 signed flags),
+ // add RRSIGs to the records.
+ if ((expected_flags & ZoneFinder::RESULT_NSEC_SIGNED) != 0 ||
+ (expected_flags & ZoneFinder::RESULT_NSEC3_SIGNED) != 0) {
+ // Convenience shortcut. The RDATA is not really validatable, but
+ // it doesn't matter for our tests.
+ const char* const rrsig_common = "5 3 3600 "
+ "20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE";
+
+ find_options = find_options | ZoneFinder::FIND_DNSSEC;
+ rr_wild_->addRRsig(textToRRset("*.wild.example.org. 300 IN RRSIG A " +
+ string(rrsig_common)));
+ rr_cnamewild_->addRRsig(textToRRset("*.cnamewild.example.org. 300 IN "
+ "RRSIG CNAME " +
+ string(rrsig_common)));
+ }
+ addZoneData(rr_wild_);
+ addZoneData(rr_cnamewild_);
+ // If the zone is expected to be "signed" with NSEC3, add an NSEC3.
+ // (the content of the NSEC3 shouldn't matter)
+ if ((expected_flags & ZoneFinder::RESULT_NSEC3_SIGNED) != 0) {
+ addZoneData(rr_nsec3_);
+ }
+ if ((expected_flags & ZoneFinder::RESULT_NSEC_SIGNED) != 0) {
+ addZoneData(rr_nsec_);
+ }
+
+ // Search at the parent. The parent will not have the A, but it will
+ // be in the wildcard (so check the wildcard isn't matched at the parent)
+ {
+ SCOPED_TRACE("Search at parent");
+ if ((expected_flags & ZoneFinder::RESULT_NSEC_SIGNED) != 0) {
+ findTest(Name("wild.example.org"), RRType::A(),
+ ZoneFinder::NXRRSET, true, rr_nsec_, expected_flags,
+ NULL, find_options);
+ } else {
+ findTest(Name("wild.example.org"), RRType::A(),
+ ZoneFinder::NXRRSET, true, ConstRRsetPtr(),
+ expected_flags, NULL, find_options);
+ }
+ }
+
+ // For the test setup of "NSEC-signed" zone, we might expect it will
+ // be returned with a negative result, either because wildcard match is
+ // disabled by the search option or because wildcard match is canceled
+ // per protocol.
+ ConstRRsetPtr expected_nsec; // by default it's NULL
+ if ((expected_flags & ZoneFinder::RESULT_NSEC_SIGNED) != 0 &&
+ (find_options & ZoneFinder::FIND_DNSSEC) != 0) {
+ expected_nsec = rr_nsec_;
+ }
+ // Explicitly converting the following to const pointers; some compilers
+ // would complain about mixed use of const and non const in ?: below.
+ const ConstRRsetPtr rr_wild = rr_wild_;
+ const ConstRRsetPtr rr_cnamewild = rr_cnamewild_;
+
+ // Search the original name of wildcard
+ {
+ SCOPED_TRACE("Search directly at *");
+ findTest(Name("*.wild.example.org"), RRType::A(), ZoneFinder::SUCCESS,
+ true, rr_wild_, ZoneFinder::RESULT_DEFAULT, NULL,
+ find_options);
+ }
+
+ // Below some of the test cases will normally result in a wildcard match;
+ // if NO_WILDCARD is specified, it should result in NXDOMAIN instead,
+ // and, when available and requested, the covering NSEC will be returned.
+ // The following are shortcut parameters to unify these cases.
+ const bool wild_ok = ((find_options & ZoneFinder::NO_WILDCARD) == 0);
+ const ZoneFinder::FindResultFlags wild_expected_flags =
+ wild_ok ? (ZoneFinder::RESULT_WILDCARD | expected_flags) :
+ expected_flags;
+
+ // Search "created" name.
+ {
+ SCOPED_TRACE("Search at created child");
+ findTest(Name("a.wild.example.org"), RRType::A(),
+ wild_ok ? ZoneFinder::SUCCESS : ZoneFinder::NXDOMAIN, false,
+ wild_ok ? rr_wild : expected_nsec,
+ wild_expected_flags, NULL, find_options, wild_ok);
+ }
+
+ // Search name that has CNAME.
+ {
+ SCOPED_TRACE("Matching CNAME");
+ findTest(Name("a.cnamewild.example.org"), RRType::A(),
+ wild_ok ? ZoneFinder::CNAME : ZoneFinder::NXDOMAIN, false,
+ wild_ok ? rr_cnamewild : expected_nsec,
+ wild_expected_flags, NULL, find_options, wild_ok);
+ }
+
+ // Search another created name, this time little bit lower
+ {
+ SCOPED_TRACE("Search at created grand-child");
+ findTest(Name("a.b.wild.example.org"), RRType::A(),
+ wild_ok ? ZoneFinder::SUCCESS : ZoneFinder::NXDOMAIN, false,
+ wild_ok ? rr_wild : expected_nsec,
+ wild_expected_flags, NULL, find_options, wild_ok);
+ }
+
+ addZoneData(rr_under_wild_);
+ {
+ SCOPED_TRACE("Search under non-wildcard");
+ findTest(Name("bar.foo.wild.example.org"), RRType::A(),
+ ZoneFinder::NXDOMAIN, true, expected_nsec, expected_flags,
+ NULL, find_options);
+ }
+
+ // Wildcard match, but no data. We add the additional NSEC at the wildcard
+ // at this point so that it wouldn't break other tests above. Note also
+ // that in the NO_WILDCARD case the resulting NSEC is the same. Ideally
+ // we could use a more tricky setup so we can distinguish these cases,
+ // but for this purpose it's not bad; what we'd like to test here is that
+ // wildcard substitution doesn't happen for either case, and the
+ // NO_WILDCARD effect itself can be checked by the result code (NXDOMAIN).
+ ConstRRsetPtr expected_wild_nsec; // by default it's NULL
+ if ((expected_flags & ZoneFinder::RESULT_NSEC_SIGNED) != 0) {
+ addZoneData(rr_wild_nsec_);
+ expected_wild_nsec = rr_wild_nsec_;
+ }
+ {
+ SCOPED_TRACE("Search at wildcard, no data");
+ findTest(Name("a.wild.example.org"), RRType::AAAA(),
+ wild_ok ? ZoneFinder::NXRRSET : ZoneFinder::NXDOMAIN, true,
+ wild_ok ? expected_wild_nsec : expected_wild_nsec,
+ wild_expected_flags, NULL, find_options);
+ }
+}
+
+TEST_F(InMemoryZoneFinderTest, wildcard) {
+ // Normal case
+ wildcardCheck();
+}
+
+TEST_F(InMemoryZoneFinderTest, wildcardDisabledWithNSEC) {
+ // Wildcard is disabled. In practice, this is used as part of query
+ // processing for an NSEC-signed zone, so we test that case specifically.
+ wildcardCheck(ZoneFinder::RESULT_NSEC_SIGNED, ZoneFinder::NO_WILDCARD);
+}
+
+TEST_F(InMemoryZoneFinderTest, wildcardDisabledWithoutNSEC) {
+ // Similar to the previous once, but check the behavior for a non signed
+ // zone just in case.
+ wildcardCheck(ZoneFinder::RESULT_DEFAULT, ZoneFinder::NO_WILDCARD);
+}
+
+/*
+ * Test that we don't match a wildcard if we get under delegation.
+ * By 4.3.3 of RFC1034:
+ * "Wildcard RRs do not apply:
+ * - When the query is in another zone. That is, delegation cancels
+ * the wildcard defaults."
+ */
+TEST_F(InMemoryZoneFinderTest, delegatedWildcard) {
+ addZoneData(rr_child_wild_);
+ addZoneData(rr_child_ns_);
+
+ {
+ SCOPED_TRACE("Looking under delegation point");
+ findTest(Name("a.child.example.org"), RRType::A(),
+ ZoneFinder::DELEGATION, true, rr_child_ns_);
+ }
+
+ {
+ SCOPED_TRACE("Looking under delegation point in GLUE_OK mode");
+ findTest(Name("a.child.example.org"), RRType::A(),
+ ZoneFinder::DELEGATION, true, rr_child_ns_,
+ ZoneFinder::RESULT_DEFAULT, NULL, ZoneFinder::FIND_GLUE_OK);
+ }
+}
+
+// Tests combination of wildcard and ANY.
+void
+InMemoryZoneFinderTest::anyWildcardCheck(
+ ZoneFinder::FindResultFlags expected_flags)
+{
+ addZoneData(rr_wild_);
+ if ((expected_flags & ZoneFinder::RESULT_NSEC3_SIGNED) != 0) {
+ addZoneData(rr_nsec3_);
+ }
+ if ((expected_flags & ZoneFinder::RESULT_NSEC_SIGNED) != 0) {
+ addZoneData(rr_nsec_);
+ }
+
+ vector<ConstRRsetPtr> expected_sets;
+
+ // First try directly the name (normal match)
+ {
+ SCOPED_TRACE("Asking directly for *");
+ expected_sets.push_back(rr_wild_);
+ findAllTest(Name("*.wild.example.org"), ZoneFinder::SUCCESS,
+ expected_sets);
+ }
+
+ // Then a wildcard match
+ {
+ SCOPED_TRACE("Asking in the wild way");
+ expected_sets.clear();
+ RRsetPtr expected(new RRset(Name("a.wild.example.org"),
+ rr_wild_->getClass(), rr_wild_->getType(),
+ rr_wild_->getTTL()));
+ expected->addRdata(rr_wild_->getRdataIterator()->getCurrent());
+ expected_sets.push_back(expected);
+ findAllTest(Name("a.wild.example.org"), ZoneFinder::SUCCESS,
+ expected_sets,
+ ZoneFinder::RESULT_WILDCARD | expected_flags);
+ }
+}
+
+TEST_F(InMemoryZoneFinderTest, anyWildcard) {
+ anyWildcardCheck();
+}
+
+TEST_F(InMemoryZoneFinderTest, anyWildcardNSEC3) {
+ anyWildcardCheck(ZoneFinder::RESULT_NSEC3_SIGNED);
+}
+
+TEST_F(InMemoryZoneFinderTest, anyWildcardNSEC) {
+ anyWildcardCheck(ZoneFinder::RESULT_NSEC_SIGNED);
+}
+
+// Test there's nothing in the wildcard in the middle if we load
+// wild.*.foo.example.org.
+void
+InMemoryZoneFinderTest::emptyWildcardCheck(
+ ZoneFinder::FindResultFlags expected_flags)
+{
+ /*
+ * example.org.
+ * foo
+ * *
+ * wild
+ */
+ addZoneData(rr_emptywild_);
+ if ((expected_flags & ZoneFinder::RESULT_NSEC3_SIGNED) != 0) {
+ addZoneData(rr_nsec3_);
+ }
+ if ((expected_flags & ZoneFinder::RESULT_NSEC_SIGNED) != 0) {
+ addZoneData(rr_nsec_);
+ }
+
+ {
+ SCOPED_TRACE("Asking for the original record under wildcard");
+ findTest(Name("wild.*.foo.example.org"), RRType::A(),
+ ZoneFinder::SUCCESS, true, rr_emptywild_);
+ }
+
+ {
+ SCOPED_TRACE("Asking for A record");
+ findTest(Name("a.foo.example.org"), RRType::A(), ZoneFinder::NXRRSET,
+ true, ConstRRsetPtr(),
+ ZoneFinder::RESULT_WILDCARD | expected_flags);
+ findTest(Name("*.foo.example.org"), RRType::A(), ZoneFinder::NXRRSET,
+ true, ConstRRsetPtr(), expected_flags);
+ findTest(Name("foo.example.org"), RRType::A(), ZoneFinder::NXRRSET,
+ true, ConstRRsetPtr(), expected_flags);
+ }
+
+ {
+ SCOPED_TRACE("Asking for ANY record");
+ findAllTest(Name("*.foo.example.org"), ZoneFinder::NXRRSET,
+ vector<ConstRRsetPtr>(), expected_flags);
+
+ findAllTest(Name("a.foo.example.org"), ZoneFinder::NXRRSET,
+ vector<ConstRRsetPtr>(),
+ ZoneFinder::RESULT_WILDCARD | expected_flags);
+ }
+
+ {
+ SCOPED_TRACE("Asking on the non-terminal");
+ findTest(Name("wild.bar.foo.example.org"), RRType::A(),
+ ZoneFinder::NXRRSET, true, ConstRRsetPtr(),
+ ZoneFinder::RESULT_WILDCARD | expected_flags);
+ }
+}
+
+TEST_F(InMemoryZoneFinderTest, emptyWildcard) {
+ emptyWildcardCheck();
+}
+
+TEST_F(InMemoryZoneFinderTest, emptyWildcardNSEC3) {
+ emptyWildcardCheck(ZoneFinder::RESULT_NSEC3_SIGNED);
+}
+
+TEST_F(InMemoryZoneFinderTest, emptyWildcardNSEC) {
+ emptyWildcardCheck(ZoneFinder::RESULT_NSEC_SIGNED);
+}
+
+// Same as emptyWildcard, but with multiple * in the path.
+TEST_F(InMemoryZoneFinderTest, nestedEmptyWildcard) {
+ addZoneData(rr_nested_emptywild_);
+
+ {
+ SCOPED_TRACE("Asking for the original record under wildcards");
+ findTest(Name("wild.*.foo.*.bar.example.org"), RRType::A(),
+ ZoneFinder::SUCCESS, true, rr_nested_emptywild_);
+ }
+
+ {
+ SCOPED_TRACE("Matching wildcard against empty nonterminal");
+
+ const char* names[] = {
+ "baz.foo.*.bar.example.org",
+ "baz.foo.baz.bar.example.org",
+ "*.foo.baz.bar.example.org",
+ NULL
+ };
+
+ for (const char** name = names; *name != NULL; ++ name) {
+ SCOPED_TRACE(string("Node ") + *name);
+ findTest(Name(*name), RRType::A(), ZoneFinder::NXRRSET, true,
+ ConstRRsetPtr(), ZoneFinder::RESULT_WILDCARD);
+ }
+ }
+
+ // Domains to test
+ const char* names[] = {
+ "*.foo.*.bar.example.org",
+ "foo.*.bar.example.org",
+ "*.bar.example.org",
+ "bar.example.org",
+ NULL
+ };
+
+ {
+ SCOPED_TRACE("Asking directly for A on parent nodes");
+
+ for (const char** name = names; *name != NULL; ++ name) {
+ SCOPED_TRACE(string("Node ") + *name);
+ findTest(Name(*name), RRType::A(), ZoneFinder::NXRRSET);
+ }
+ }
+
+ {
+ SCOPED_TRACE("Asking for ANY on parent nodes");
+
+ for (const char** name = names; *name != NULL; ++ name) {
+ SCOPED_TRACE(string("Node ") + *name);
+
+ findAllTest(Name(*name), ZoneFinder::NXRRSET,
+ vector<ConstRRsetPtr>());
+ }
+ }
+}
+
+// We run this part twice from the below test, in two slightly different
+// situations
+void
+InMemoryZoneFinderTest::doCancelWildcardCheck(
+ ZoneFinder::FindResultFlags expected_flags,
+ ZoneFinder::FindOptions find_options)
+{
+ // These should be canceled
+ {
+ SCOPED_TRACE("Canceled under foo.wild.example.org");
+
+ // For an NSEC-signed zone with DNSSEC requested, the covering NSEC
+ // should be returned. The expected NSEC is actually just the only
+ // NSEC in the test data, but in this context it doesn't matter;
+ // it's sufficient just to check any NSEC is returned (or not).
+ ConstRRsetPtr expected_nsec; // by default it's NULL
+ if ((expected_flags & ZoneFinder::RESULT_NSEC_SIGNED) != 0 &&
+ (find_options & ZoneFinder::FIND_DNSSEC)) {
+ expected_nsec = rr_nsec_;
+ }
+
+ findTest(Name("aaa.foo.wild.example.org"), RRType::A(),
+ ZoneFinder::NXDOMAIN, true, expected_nsec, expected_flags,
+ NULL, find_options);
+ findTest(Name("zzz.foo.wild.example.org"), RRType::A(),
+ ZoneFinder::NXDOMAIN, true, expected_nsec, expected_flags,
+ NULL, find_options);
+ }
+
+ // This is existing, non-wildcard domain, shouldn't wildcard at all
+ {
+ SCOPED_TRACE("Existing domain under foo.wild.example.org");
+ findTest(Name("bar.foo.wild.example.org"), RRType::A(),
+ ZoneFinder::SUCCESS, true, rr_not_wild_);
+ }
+
+ // These should be caught by the wildcard
+ {
+ SCOPED_TRACE("Neighbor wildcards to foo.wild.example.org");
+
+ const char* names[] = {
+ "aaa.bbb.wild.example.org",
+ "aaa.zzz.wild.example.org",
+ "zzz.wild.example.org",
+ NULL
+ };
+
+ for (const char** name = names; *name != NULL; ++ name) {
+ SCOPED_TRACE(string("Node ") + *name);
+
+ findTest(Name(*name), RRType::A(), ZoneFinder::SUCCESS, false,
+ rr_wild_,
+ ZoneFinder::RESULT_WILDCARD | expected_flags, NULL,
+ ZoneFinder::FIND_DEFAULT, true);
+ }
+ }
+
+ // This shouldn't be wildcarded, it's an existing domain
+ {
+ SCOPED_TRACE("The foo.wild.example.org itself");
+ findTest(Name("foo.wild.example.org"), RRType::A(),
+ ZoneFinder::NXRRSET, true, ConstRRsetPtr(), expected_flags);
+ }
+}
+
+/*
+ * This tests that if there's a name between the wildcard domain and the
+ * searched one, it will not trigger wildcard, for example, if we have
+ * *.wild.example.org and bar.foo.wild.example.org, then we know
+ * foo.wild.example.org exists and is not wildcard. Therefore, search for
+ * aaa.foo.wild.example.org should return NXDOMAIN.
+ *
+ * Tests few cases "around" the canceled wildcard match, to see something that
+ * shouldn't be canceled isn't.
+ */
+TEST_F(InMemoryZoneFinderTest, cancelWildcard) {
+ addZoneData(rr_wild_);
+ addZoneData(rr_not_wild_);
+
+ {
+ SCOPED_TRACE("Runnig with single entry under foo.wild.example.org");
+ doCancelWildcardCheck();
+ }
+
+ // Try putting another one under foo.wild....
+ // The result should be the same but it will be done in another way in the
+ // code, because the foo.wild.example.org will exist in the tree.
+ addZoneData(rr_not_wild_another_);
+ {
+ SCOPED_TRACE("Runnig with two entries under foo.wild.example.org");
+ doCancelWildcardCheck();
+ }
+}
+
+// Same tests as cancelWildcard for NSEC3-signed zone
+TEST_F(InMemoryZoneFinderTest, cancelWildcardNSEC3) {
+ addZoneData(rr_wild_);
+ addZoneData(rr_not_wild_);
+ addZoneData(rr_nsec3_);
+
+ {
+ SCOPED_TRACE("Runnig with single entry under foo.wild.example.org");
+ doCancelWildcardCheck(ZoneFinder::RESULT_NSEC3_SIGNED);
+ }
+ addZoneData(rr_not_wild_another_);
+ {
+ SCOPED_TRACE("Runnig with two entries under foo.wild.example.org");
+ doCancelWildcardCheck(ZoneFinder::RESULT_NSEC3_SIGNED);
+ }
+}
+
+// Same tests as cancelWildcard for NSEC-signed zone. Check both cases with
+// or without FIND_DNSSEC option. NSEC should be returned only when the option
+// is given.
+TEST_F(InMemoryZoneFinderTest, cancelWildcardNSEC) {
+ addZoneData(rr_wild_);
+ addZoneData(rr_not_wild_);
+ addZoneData(rr_nsec_);
+
+ {
+ SCOPED_TRACE("Runnig with single entry under foo.wild.example.org");
+ doCancelWildcardCheck(ZoneFinder::RESULT_NSEC_SIGNED,
+ ZoneFinder::FIND_DNSSEC);
+ doCancelWildcardCheck(ZoneFinder::RESULT_NSEC_SIGNED);
+ }
+ addZoneData(rr_not_wild_another_);
+ {
+ SCOPED_TRACE("Runnig with two entries under foo.wild.example.org");
+ doCancelWildcardCheck(ZoneFinder::RESULT_NSEC_SIGNED,
+ ZoneFinder::FIND_DNSSEC);
+ doCancelWildcardCheck(ZoneFinder::RESULT_NSEC_SIGNED);
+ }
+}
+
+
+TEST_F(InMemoryZoneFinderTest, findNSEC3ForBadZone) {
+ // Set up the faked hash calculator.
+ const TestNSEC3HashCreator creator;
+ setNSEC3HashCreator(&creator);
+
+ // If the zone has nothing about NSEC3 (neither NSEC3 or NSEC3PARAM),
+ // findNSEC3() should be rejected.
+ EXPECT_THROW(zone_finder_.findNSEC3(Name("www.example.org"), true),
+ DataSourceError);
+
+ // Only having NSEC3PARAM isn't enough
+ addZoneData(textToRRset("example.org. 300 IN NSEC3PARAM "
+ "1 0 12 aabbccdd"));
+ EXPECT_THROW(zone_finder_.findNSEC3(Name("www.example.org"), true),
+ DataSourceError);
+
+ // Unless NSEC3 for apex is added the result in the recursive mode
+ // is guaranteed.
+ const string ns1_nsec3_text = string(ns1_hash) + ".example.org." +
+ string(nsec3_common);
+ addZoneData(textToRRset(ns1_nsec3_text));
+ EXPECT_THROW(zone_finder_.findNSEC3(Name("www.example.org"), true),
+ DataSourceError);
+}
+
+/// \brief NSEC3 specific tests fixture for the InMemoryZoneFinder class
+class InMemoryZoneFinderNSEC3Test : public InMemoryZoneFinderTest {
+public:
+ InMemoryZoneFinderNSEC3Test() {
+ // Set up the faked hash calculator.
+ setNSEC3HashCreator(&creator_);
+
+ // Add a few NSEC3 records:
+ // apex (example.org.): hash=0P..
+ // ns1.example.org: hash=2T..
+ // w.example.org: hash=01..
+ // zzz.example.org: hash=R5..
+ const string apex_nsec3_text = string(apex_hash) + ".example.org." +
+ string(nsec3_common);
+ addZoneData(textToRRset(apex_nsec3_text));
+ const string ns1_nsec3_text = string(ns1_hash) + ".example.org." +
+ string(nsec3_common);
+ addZoneData(textToRRset(ns1_nsec3_text));
+ const string w_nsec3_text = string(w_hash) + ".example.org." +
+ string(nsec3_common);
+ addZoneData(textToRRset(w_nsec3_text));
+ const string zzz_nsec3_text = string(zzz_hash) + ".example.org." +
+ string(nsec3_common);
+ addZoneData(textToRRset(zzz_nsec3_text));
+ }
+
+private:
+ const TestNSEC3HashCreator creator_;
+};
+
+TEST_F(InMemoryZoneFinderNSEC3Test, findNSEC3) {
+ performNSEC3Test(zone_finder_);
+}
+
+struct TestData {
+ // String for the name passed to findNSEC3() (concatenated with
+ // "example.org.")
+ const char* const name;
+ // Should recursive findNSEC3() be performed?
+ const bool recursive;
+ // The following are members of the FindNSEC3Result returned by
+ // findNSEC3(). The proofs are given as char*, which are converted
+ // to Name objects and checked against getName() on the returned
+ // ConstRRsetPtr. If any of these is NULL, then it's expected that
+ // ConstRRsetPtr() will be returned.
+ const bool matched;
+ const uint8_t closest_labels;
+ const char* const closest_proof;
+ const char* const next_proof;
+};
+
+const TestData nsec3_data[] = {
+ // ==== These are non-recursive tests.
+ {"n0", false, false, 4, "R53BQ7CC2UVMUBFU5OCMM6PERS9TK9EN", NULL},
+ {"n1", false, true, 4, "01UDEMVP1J2F7EG6JEBPS17VP3N8I58H", NULL},
+ {"n2", false, false, 4, "01UDEMVP1J2F7EG6JEBPS17VP3N8I58H", NULL},
+ {"n3", false, true, 4, "0P9MHAVEQVM6T7VBL5LOP2U3T2RP3TOM", NULL},
+ {"n4", false, false, 4, "0P9MHAVEQVM6T7VBL5LOP2U3T2RP3TOM", NULL},
+ {"n5", false, true, 4, "2T7B4G4VSA5SMI47K61MV5BV1A22BOJR", NULL},
+ {"n6", false, false, 4, "2T7B4G4VSA5SMI47K61MV5BV1A22BOJR", NULL},
+ {"n7", false, true, 4, "R53BQ7CC2UVMUBFU5OCMM6PERS9TK9EN", NULL},
+ {"n8", false, false, 4, "R53BQ7CC2UVMUBFU5OCMM6PERS9TK9EN", NULL},
+
+ // ==== These are recursive tests.
+ {"n0", true, true, 3, "0P9MHAVEQVM6T7VBL5LOP2U3T2RP3TOM",
+ "R53BQ7CC2UVMUBFU5OCMM6PERS9TK9EN"},
+ {"n1", true, true, 4, "01UDEMVP1J2F7EG6JEBPS17VP3N8I58H", NULL},
+ {"n2", true, true, 3, "0P9MHAVEQVM6T7VBL5LOP2U3T2RP3TOM",
+ "01UDEMVP1J2F7EG6JEBPS17VP3N8I58H"},
+ {"n3", true, true, 4, "0P9MHAVEQVM6T7VBL5LOP2U3T2RP3TOM", NULL},
+ {"n4", true, true, 3, "0P9MHAVEQVM6T7VBL5LOP2U3T2RP3TOM",
+ "0P9MHAVEQVM6T7VBL5LOP2U3T2RP3TOM"},
+ {"n5", true, true, 4, "2T7B4G4VSA5SMI47K61MV5BV1A22BOJR", NULL},
+ {"n6", true, true, 3, "0P9MHAVEQVM6T7VBL5LOP2U3T2RP3TOM",
+ "2T7B4G4VSA5SMI47K61MV5BV1A22BOJR"},
+ {"n7", true, true, 4, "R53BQ7CC2UVMUBFU5OCMM6PERS9TK9EN", NULL},
+ {"n8", true, true, 3, "0P9MHAVEQVM6T7VBL5LOP2U3T2RP3TOM",
+ "R53BQ7CC2UVMUBFU5OCMM6PERS9TK9EN"}
+};
+
+const size_t data_count(sizeof(nsec3_data) / sizeof(*nsec3_data));
+
+TEST_F(InMemoryZoneFinderNSEC3Test, findNSEC3Walk) {
+ // This test basically uses nsec3_data[] declared above along with
+ // the fake hash setup to walk the NSEC3 tree. The names and fake
+ // hash calculation is specially setup so that the tree search
+ // terminates at specific locations in the tree. We findNSEC3() on
+ // each of the nsec3_data[], which is setup such that the hash
+ // results in the search terminating on either side of each node of
+ // the NSEC3 tree. This way, we check what result is returned in
+ // every search termination case in the NSEC3 tree.
+
+ const Name origin("example.org");
+ for (size_t i = 0; i < data_count; ++i) {
+ const Name name = Name(nsec3_data[i].name).concatenate(origin);
+
+ SCOPED_TRACE(name.toText() + (nsec3_data[i].recursive ?
+ ", recursive" :
+ ", non-recursive"));
+
+ const ZoneFinder::FindNSEC3Result result =
+ zone_finder_.findNSEC3(name, nsec3_data[i].recursive);
+
+ EXPECT_EQ(nsec3_data[i].matched, result.matched);
+ EXPECT_EQ(nsec3_data[i].closest_labels, result.closest_labels);
+
+ if (nsec3_data[i].closest_proof != NULL) {
+ ASSERT_TRUE(result.closest_proof);
+ EXPECT_EQ(Name(nsec3_data[i].closest_proof).concatenate(origin),
+ result.closest_proof->getName());
+ } else {
+ EXPECT_FALSE(result.closest_proof);
+ }
+
+ if (nsec3_data[i].next_proof != NULL) {
+ ASSERT_TRUE(result.next_proof);
+ EXPECT_EQ(Name(nsec3_data[i].next_proof).concatenate(origin),
+ result.next_proof->getName());
+ } else {
+ EXPECT_FALSE(result.next_proof);
+ }
+ }
+}
+}
diff --git a/src/lib/datasrc/tests/memory/zone_table_unittest.cc b/src/lib/datasrc/tests/memory/zone_table_unittest.cc
new file mode 100644
index 0000000..359df49
--- /dev/null
+++ b/src/lib/datasrc/tests/memory/zone_table_unittest.cc
@@ -0,0 +1,150 @@
+// 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 <exceptions/exceptions.h>
+
+#include <util/memory_segment_local.h>
+
+#include <dns/name.h>
+#include <dns/rrclass.h>
+
+#include <datasrc/result.h>
+#include <datasrc/memory/zone_data.h>
+#include <datasrc/memory/zone_table.h>
+
+#include <gtest/gtest.h>
+
+#include <new> // for bad_alloc
+
+using namespace isc::dns;
+using namespace isc::datasrc;
+using namespace isc::datasrc::memory;
+
+namespace {
+// Memory segment specified for tests. It normally behaves like a "local"
+// memory segment. If "throw count" is set to non 0 via setThrowCount(),
+// it continues the normal behavior up to the specified number of calls to
+// allocate(), and throws an exception at the next call.
+class TestMemorySegment : public isc::util::MemorySegmentLocal {
+public:
+ TestMemorySegment() : throw_count_(0) {}
+ virtual void* allocate(size_t size) {
+ if (throw_count_ > 0) {
+ if (--throw_count_ == 0) {
+ throw std::bad_alloc();
+ }
+ }
+ return (isc::util::MemorySegmentLocal::allocate(size));
+ }
+ void setThrowCount(size_t count) { throw_count_ = count; }
+
+private:
+ size_t throw_count_;
+};
+
+class ZoneTableTest : public ::testing::Test {
+protected:
+ ZoneTableTest() : zclass_(RRClass::IN()),
+ zname1(Name("example.com")),
+ zname2(Name("example.net")),
+ zname3(Name("example")),
+ zone_table(ZoneTable::create(mem_sgmt_, zclass_))
+ {}
+ ~ZoneTableTest() {
+ if (zone_table != NULL) {
+ ZoneTable::destroy(mem_sgmt_, zone_table, zclass_);
+ }
+ }
+ void TearDown() {
+ ZoneTable::destroy(mem_sgmt_, zone_table, zclass_);
+ zone_table = NULL;
+ EXPECT_TRUE(mem_sgmt_.allMemoryDeallocated()); // catch any leak here.
+ }
+ const RRClass zclass_;
+ const Name zname1, zname2, zname3;
+ TestMemorySegment mem_sgmt_;
+ ZoneTable* zone_table;
+};
+
+TEST_F(ZoneTableTest, create) {
+ // Test about creating a zone table. Normal case covers through other
+ // tests. We only check exception safety by letting the test memory
+ // segment throw.
+ mem_sgmt_.setThrowCount(2);
+ EXPECT_THROW(ZoneTable::create(mem_sgmt_, zclass_), std::bad_alloc);
+ // This shouldn't cause memory leak (that would be caught in TearDown()).
+}
+
+TEST_F(ZoneTableTest, addZone) {
+ // Normal successful case.
+ const ZoneTable::AddResult result1 =
+ zone_table->addZone(mem_sgmt_, zclass_, zname1);
+ EXPECT_EQ(result::SUCCESS, result1.code);
+
+ // Duplicate add doesn't replace the existing data.
+ EXPECT_EQ(result::EXIST, zone_table->addZone(mem_sgmt_, zclass_,
+ zname1).code);
+ EXPECT_EQ(result1.zone_data,
+ zone_table->addZone(mem_sgmt_, zclass_, zname1).zone_data);
+ // names are compared in a case insensitive manner.
+ EXPECT_EQ(result::EXIST, zone_table->addZone(mem_sgmt_, zclass_,
+ Name("EXAMPLE.COM")).code);
+ // Add some more different ones. Should just succeed.
+ EXPECT_EQ(result::SUCCESS,
+ zone_table->addZone(mem_sgmt_, zclass_, zname2).code);
+ EXPECT_EQ(result::SUCCESS,
+ zone_table->addZone(mem_sgmt_, zclass_, zname3).code);
+
+ // Have the memory segment throw an exception in extending the internal
+ // tree. It still shouldn't cause memory leak (which would be detected
+ // in TearDown()).
+ mem_sgmt_.setThrowCount(2);
+ EXPECT_THROW(zone_table->addZone(mem_sgmt_, zclass_, Name("example.org")),
+ std::bad_alloc);
+}
+
+TEST_F(ZoneTableTest, findZone) {
+ const ZoneTable::AddResult add_result1 =
+ zone_table->addZone(mem_sgmt_, zclass_, zname1);
+ EXPECT_EQ(result::SUCCESS, add_result1.code);
+ EXPECT_EQ(result::SUCCESS,
+ zone_table->addZone(mem_sgmt_, zclass_, zname2).code);
+ EXPECT_EQ(result::SUCCESS,
+ zone_table->addZone(mem_sgmt_, zclass_, zname3).code);
+
+ const ZoneTable::FindResult find_result1 =
+ zone_table->findZone(Name("example.com"));
+ EXPECT_EQ(result::SUCCESS, find_result1.code);
+ EXPECT_EQ(add_result1.zone_data, find_result1.zone_data);
+
+ EXPECT_EQ(result::NOTFOUND,
+ zone_table->findZone(Name("example.org")).code);
+ EXPECT_EQ(static_cast<ZoneData*>(NULL),
+ zone_table->findZone(Name("example.org")).zone_data);
+
+ // there's no exact match. the result should be the longest match,
+ // and the code should be PARTIALMATCH.
+ EXPECT_EQ(result::PARTIALMATCH,
+ zone_table->findZone(Name("www.example.com")).code);
+ EXPECT_EQ(add_result1.zone_data,
+ zone_table->findZone(Name("www.example.com")).zone_data);
+
+ // make sure the partial match is indeed the longest match by adding
+ // a zone with a shorter origin and query again.
+ EXPECT_EQ(result::SUCCESS, zone_table->addZone(mem_sgmt_, zclass_,
+ Name("com")).code);
+ EXPECT_EQ(add_result1.zone_data,
+ zone_table->findZone(Name("www.example.com")).zone_data);
+}
+}
diff --git a/src/lib/datasrc/tests/memory_datasrc_unittest.cc b/src/lib/datasrc/tests/memory_datasrc_unittest.cc
index fcdca16..958c9e1 100644
--- a/src/lib/datasrc/tests/memory_datasrc_unittest.cc
+++ b/src/lib/datasrc/tests/memory_datasrc_unittest.cc
@@ -293,72 +293,6 @@ setRRset(RRsetPtr rrset, vector<RRsetPtr*>::iterator& it) {
++it;
}
-// Some faked NSEC3 hash values commonly used in tests and the faked NSEC3Hash
-// object.
-//
-// For apex (example.org)
-const char* const apex_hash = "0P9MHAVEQVM6T7VBL5LOP2U3T2RP3TOM";
-const char* const apex_hash_lower = "0p9mhaveqvm6t7vbl5lop2u3t2rp3tom";
-// For ns1.example.org
-const char* const ns1_hash = "2T7B4G4VSA5SMI47K61MV5BV1A22BOJR";
-// For w.example.org
-const char* const w_hash = "01UDEMVP1J2F7EG6JEBPS17VP3N8I58H";
-// For x.y.w.example.org (lower-cased)
-const char* const xyw_hash = "2vptu5timamqttgl4luu9kg21e0aor3s";
-// For zzz.example.org.
-const char* const zzz_hash = "R53BQ7CC2UVMUBFU5OCMM6PERS9TK9EN";
-
-// A simple faked NSEC3 hash calculator with a dedicated creator for it.
-//
-// This is used in some NSEC3-related tests below.
-class TestNSEC3HashCreator : public NSEC3HashCreator {
- class TestNSEC3Hash : public NSEC3Hash {
- private:
- typedef map<Name, string> NSEC3HashMap;
- typedef NSEC3HashMap::value_type NSEC3HashPair;
- NSEC3HashMap map_;
- public:
- TestNSEC3Hash() {
- // Build pre-defined hash
- map_[Name("example.org")] = apex_hash;
- map_[Name("www.example.org")] = "2S9MHAVEQVM6T7VBL5LOP2U3T2RP3TOM";
- map_[Name("xxx.example.org")] = "Q09MHAVEQVM6T7VBL5LOP2U3T2RP3TOM";
- map_[Name("yyy.example.org")] = "0A9MHAVEQVM6T7VBL5LOP2U3T2RP3TOM";
- map_[Name("x.y.w.example.org")] =
- "2VPTU5TIMAMQTTGL4LUU9KG21E0AOR3S";
- map_[Name("y.w.example.org")] = "K8UDEMVP1J2F7EG6JEBPS17VP3N8I58H";
- map_[Name("w.example.org")] = w_hash;
- map_[Name("zzz.example.org")] = zzz_hash;
- map_[Name("smallest.example.org")] =
- "00000000000000000000000000000000";
- map_[Name("largest.example.org")] =
- "UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU";
- }
- virtual string calculate(const Name& name) const {
- const NSEC3HashMap::const_iterator found = map_.find(name);
- if (found != map_.end()) {
- return (found->second);
- }
- isc_throw(isc::Unexpected, "unexpected name for NSEC3 test: "
- << name);
- }
- virtual bool match(const generic::NSEC3PARAM&) const {
- return (true);
- }
- virtual bool match(const generic::NSEC3&) const {
- return (true);
- }
- };
-
-public:
- virtual NSEC3Hash* create(const generic::NSEC3PARAM&) const {
- return (new TestNSEC3Hash);
- }
- virtual NSEC3Hash* create(const generic::NSEC3&) const {
- return (new TestNSEC3Hash);
- }
-};
-
/// \brief Test fixture for the InMemoryZoneFinder class
class InMemoryZoneFinderTest : public ::testing::Test {
// A straightforward pair of textual RR(set) and a RRsetPtr variable
diff --git a/src/lib/datasrc/tests/query_unittest.cc b/src/lib/datasrc/tests/query_unittest.cc
deleted file mode 100644
index 7a20b86..0000000
--- a/src/lib/datasrc/tests/query_unittest.cc
+++ /dev/null
@@ -1,71 +0,0 @@
-// Copyright (C) 2010 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/buffer.h>
-#include <dns/message.h>
-#include <dns/name.h>
-#include <dns/opcode.h>
-#include <dns/rrtype.h>
-#include <dns/rrclass.h>
-
-#include <datasrc/query.h>
-
-#include <dns/tests/unittest_util.h>
-
-using isc::UnitTestUtil;
-using namespace isc::dns;
-using namespace isc::datasrc;
-
-namespace {
-
-void
-createQuery(Message& m, const Name& qname, const RRClass& qclass,
- const RRType& qtype)
-{
- m.setOpcode(Opcode::QUERY());
- m.setHeaderFlag(Message::HEADERFLAG_RD);
- m.addQuestion(Question(qname, qclass, qtype));
-}
-
-QueryTaskPtr
-createTask(Message& m, const Name& name, const RRType& rrtype0, HotCache& c) {
- RRType rrtype(rrtype0);
- Query q(m, c, true);
- return (QueryTaskPtr(new QueryTask(q, name, rrtype,
- QueryTask::SIMPLE_QUERY)));
-}
-
-// Check the QueryTask created using a temporary RRType object will remain
-// valid.
-TEST(QueryTest, constructWithTemporary) {
- HotCache cache;
-
- Message m1(Message::RENDER);
- createQuery(m1, Name("www.wild.example.com"), RRClass::IN(), RRType::A());
- QueryTaskPtr task_a = createTask(m1, Name("www.wild.example.com"),
- RRType::A(), cache);
- EXPECT_EQ(RRType::A(), task_a->qtype);
-
- Message m2(Message::RENDER);
- createQuery(m2, Name("www.wild.example.com"), RRClass::IN(),
- RRType::AAAA());
- QueryTaskPtr task_aaaa = createTask(m2, Name("www.wild.example.com"),
- RRType::AAAA(), cache);
- EXPECT_EQ(RRType::AAAA(), task_aaaa->qtype);
-
-}
-
-}
diff --git a/src/lib/datasrc/tests/sqlite3_unittest.cc b/src/lib/datasrc/tests/sqlite3_unittest.cc
deleted file mode 100644
index ac1211b..0000000
--- a/src/lib/datasrc/tests/sqlite3_unittest.cc
+++ /dev/null
@@ -1,950 +0,0 @@
-// Copyright (C) 2010 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 <stdint.h>
-
-#include <algorithm>
-#include <string>
-#include <vector>
-
-#include <sqlite3.h>
-#include <gtest/gtest.h>
-
-#include <dns/name.h>
-#include <dns/message.h>
-#include <dns/rdata.h>
-#include <dns/rrclass.h>
-#include <dns/rrtype.h>
-#include <dns/rdataclass.h>
-#include <dns/rrsetlist.h>
-#include <cc/data.h>
-
-#include <datasrc/query.h>
-#include <datasrc/data_source.h>
-#include <datasrc/sqlite3_datasrc.h>
-
-using namespace std;
-using namespace isc::dns;
-using namespace isc::dns::rdata;
-using namespace isc::datasrc;
-using namespace isc::data;
-
-namespace {
-ConstElementPtr SQLITE_DBFILE_EXAMPLE = Element::fromJSON(
- "{ \"database_file\": \"" TEST_DATA_DIR "/test.sqlite3\"}");
-ConstElementPtr SQLITE_DBFILE_EXAMPLE2 = Element::fromJSON(
- "{ \"database_file\": \"" TEST_DATA_DIR "/example2.com.sqlite3\"}");
-ConstElementPtr SQLITE_DBFILE_EXAMPLE_ROOT = Element::fromJSON(
- "{ \"database_file\": \"" TEST_DATA_DIR "/test-root.sqlite3\"}");
-ConstElementPtr SQLITE_DBFILE_BROKENDB = Element::fromJSON(
- "{ \"database_file\": \"" TEST_DATA_DIR "/brokendb.sqlite3\"}");
-ConstElementPtr SQLITE_DBFILE_MEMORY = Element::fromJSON(
- "{ \"database_file\": \":memory:\"}");
-ConstElementPtr SQLITE_DBFILE_NEWSCHEMA = Element::fromJSON(
- "{ \"database_file\": \"" TEST_DATA_DIR "/newschema.sqlite3\"}");
-ConstElementPtr SQLITE_DBFILE_OLDSCHEMA = Element::fromJSON(
- "{ \"database_file\": \"" TEST_DATA_DIR "/oldschema.sqlite3\"}");
-
-// The following file must be non existent and must be non"creatable";
-// the sqlite3 library will try to create a new DB file if it doesn't exist,
-// so to test a failure case the create operation should also fail.
-// The "nodir", a non existent directory, is inserted for this purpose.
-ConstElementPtr SQLITE_DBFILE_NOTEXIST = Element::fromJSON(
- "{ \"database_file\": \"" TEST_DATA_DIR "/nodir/notexist\"}");
-
-const string sigdata_common(" 20100322084538 20100220084538 "
- "33495 example.com. FAKEFAKEFAKEFAKE");
-const string dnskey1_data(" AwEAAcOUBllYc1hf7ND9uDy+Yz1BF3sI0m4q"
- "NGV7WcTD0WEiuV7IjXgHE36fCmS9QsUxSSOV"
- "o1I/FMxI2PJVqTYHkXFBS7AzLGsQYMU7UjBZ"
- "SotBJ6Imt5pXMu+lEDNy8TOUzG3xm7g0qcbW"
- "YF6qCEfvZoBtAqi5Rk7Mlrqs8agxYyMx");
-const string dnskey2_data(" AwEAAe5WFbxdCPq2jZrZhlMj7oJdff3W7syJ"
- "tbvzg62tRx0gkoCDoBI9DPjlOQG0UAbj+xUV"
- "4HQZJStJaZ+fHU5AwVNT+bBZdtV+NujSikhd"
- "THb4FYLg2b3Cx9NyJvAVukHp/91HnWuG4T36"
- "CzAFrfPwsHIrBz9BsaIQ21VRkcmj7DswfI/i"
- "DGd8j6bqiODyNZYQ+ZrLmF0KIJ2yPN3iO6Zq"
- "23TaOrVTjB7d1a/h31ODfiHAxFHrkY3t3D5J"
- "R9Nsl/7fdRmSznwtcSDgLXBoFEYmw6p86Acv"
- "RyoYNcL1SXjaKVLG5jyU3UR+LcGZT5t/0xGf"
- "oIK/aKwENrsjcKZZj660b1M=");
-const string nsec3_signature("gNIVj4T8t51fEU6kOPpvK7HOGBFZGbalN5ZK"
- "mInyrww6UWZsUNdw07ge6/U6HfG+/s61RZ/L"
- "is2M6yUWHyXbNbj/QqwqgadG5dhxTArfuR02"
- "xP600x0fWX8LXzW4yLMdKVxGbzYT+vvGz71o"
- "8gHSY5vYTtothcZQa4BMKhmGQEk=");
-
-const Name zone_name("example.com");
-const Name nomatch_name("example.org");
-const Name child_name("sql1.example.com");
-const Name www_name("www.example.com");
-const Name www_upper_name("WWW.EXAMPLE.COM");
-
-typedef enum {
- NORMAL,
- EXACT,
- ADDRESS,
- REFERRAL
-} FindMode;
-
-class Sqlite3DataSourceTest : public ::testing::Test {
-protected:
- Sqlite3DataSourceTest() : rrclass(RRClass::IN()),
- rrclass_notmatch(RRClass::CH()),
- rrtype(RRType::A()), rrttl(RRTTL(3600)),
- find_flags(0), rrset_matched(0), rrset_count(0)
- {
- data_source.init(SQLITE_DBFILE_EXAMPLE);
-
- common_a_data.push_back("192.0.2.1");
- common_sig_data.push_back("A 5 3 3600" + sigdata_common);
- common_aaaa_data.push_back("2001:db8::1234");
- common_aaaa_sig_data.push_back("AAAA 5 3 3600" + sigdata_common);
-
- www_nsec_data.push_back("example.com. A RRSIG NSEC");
- www_nsec_sig_data.push_back("NSEC 5 3 7200" + sigdata_common);
-
- mix_a_data.push_back("192.0.2.1");
- mix_a_data.push_back("192.0.2.2");
- mix_aaaa_data.push_back("2001:db8::1");
- mix_aaaa_data.push_back("2001:db8::2");
-
- apex_soa_data.push_back("master.example.com. admin.example.com. "
- "1234 3600 1800 2419200 7200");
- apex_soa_sig_data.push_back("SOA 5 2 3600" + sigdata_common);
- apex_ns_data.push_back("dns01.example.com.");
- apex_ns_data.push_back("dns02.example.com.");
- apex_ns_data.push_back("dns03.example.com.");
- apex_ns_sig_data.push_back("NS 5 2 3600" + sigdata_common);
- apex_mx_data.push_back("10 mail.example.com.");
- apex_mx_data.push_back("20 mail.subzone.example.com.");
- apex_mx_sig_data.push_back("MX 5 2 3600" + sigdata_common);
- apex_nsec_data.push_back("cname-ext.example.com. "
- "NS SOA MX RRSIG NSEC DNSKEY");
- apex_nsec_sig_data.push_back("NSEC 5 2 7200" + sigdata_common);
- apex_dnskey_data.push_back("256 3 5" + dnskey1_data);
- apex_dnskey_data.push_back("257 3 5" + dnskey2_data);
- // this one is special (using different key):
- apex_dnskey_sig_data.push_back("DNSKEY 5 2 3600 20100322084538 "
- "20100220084538 4456 example.com. "
- "FAKEFAKEFAKEFAKE");
- apex_dnskey_sig_data.push_back("DNSKEY 5 2 3600" + sigdata_common);
-
- wild_a_data.push_back("192.0.2.255");
- dname_data.push_back("sql1.example.com.");
- dname_sig_data.push_back("DNAME 5 3 3600" + sigdata_common);
- cname_data.push_back("cnametest.example.org.");
- cname_sig_data.push_back("CNAME 5 3 3600" + sigdata_common);
- cname_nsec_data.push_back("mail.example.com. CNAME RRSIG NSEC");
- cname_nsec_sig_data.push_back("NSEC 5 3 7200" + sigdata_common);
- delegation_ns_data.push_back("ns1.subzone.example.com.");
- delegation_ns_data.push_back("ns2.subzone.example.com.");
- delegation_ds_data.push_back("40633 5 1 "
- "3E56C0EA92CF529E005A4B62979533350492 "
- "F105");
- delegation_ds_data.push_back("40633 5 2 "
- "AA8D4BD330C68BFB4D785894DDCF6B689CE9"
- "873C4A3801F57A5AA3FE17925B8C");
- delegation_ds_sig_data.push_back("DS 5 3 3600" + sigdata_common);
- child_ds_data.push_back("33313 5 1 "
- "FDD7A2C11AA7F55D50FBF9B7EDDA2322C541A8DE");
- child_ds_data.push_back("33313 5 2 "
- "0B99B7006F496D135B01AB17EDB469B4BE9E"
- "1973884DEA757BC4E3015A8C3AB3");
- child_ds_sig_data.push_back("DS 5 3 3600" + sigdata_common);
- delegation_nsec_data.push_back("*.wild.example.com. NS DS RRSIG NSEC");
- delegation_nsec_sig_data.push_back("NSEC 5 3 7200" + sigdata_common);
- child_a_data.push_back("192.0.2.100");
- child_sig_data.push_back("A 5 4 3600 20100322084536 "
- "20100220084536 12447 sql1.example.com. "
- "FAKEFAKEFAKEFAKE");
- nsec3_data.push_back("1 0 10 FEEDABEE 4KLSVDE8KH8G95VU68R7AHBE1CPQN38J");
- nsec3_sig_data.push_back("NSEC3 5 4 7200 20100410172647 "
- "20100311172647 63192 sql2.example.com. "
- + nsec3_signature);
- }
- Sqlite3DataSrc data_source;
- // we allow these to be modified in the test
- RRClass rrclass;
- RRClass rrclass_notmatch;
- RRType rrtype;
- RRTTL rrttl;
- RRsetList result_sets;
- uint32_t find_flags;
- unsigned rrset_matched;
- unsigned rrset_count;
-
- vector<RRType> types;
- vector<RRTTL> ttls;
- vector<const vector<string>* > answers;
- vector<const vector<string>* > signatures;
-
- vector<RRType> expected_types;
- vector<string> common_a_data;
- vector<string> common_sig_data;
- vector<string> common_aaaa_data;
- vector<string> common_aaaa_sig_data;
- vector<string> www_nsec_data;
- vector<string> www_nsec_sig_data;
- vector<string> mix_a_data;
- vector<string> mix_aaaa_data;
- vector<string> apex_soa_data;
- vector<string> apex_soa_sig_data;
- vector<string> apex_ns_data;
- vector<string> apex_ns_sig_data;
- vector<string> apex_mx_data;
- vector<string> apex_mx_sig_data;
- vector<string> apex_nsec_data;
- vector<string> apex_nsec_sig_data;
- vector<string> apex_dnskey_data;
- vector<string> apex_dnskey_sig_data;
- vector<string> wild_a_data;
- vector<string> dname_data;
- vector<string> dname_sig_data;
- vector<string> cname_data;
- vector<string> cname_sig_data;
- vector<string> cname_nsec_data;
- vector<string> cname_nsec_sig_data;
- vector<string> delegation_ns_data;
- vector<string> delegation_ns_sig_data;
- vector<string> delegation_ds_data;
- vector<string> delegation_ds_sig_data;
- vector<string> child_ds_data;
- vector<string> child_ds_sig_data;
- vector<string> delegation_nsec_data;
- vector<string> delegation_nsec_sig_data;
- vector<string> child_a_data;
- vector<string> child_sig_data;
- vector<string> nsec3_data;
- vector<string> nsec3_sig_data;
-
- void findReferralRRsetCommon(const Name& qname, const RRClass& qclass);
- void findAddressRRsetCommon(const RRClass& qclass);
-};
-
-void
-checkRRset(RRsetPtr rrset, const Name& expected_name,
- const RRClass& expected_class, const RRType& expected_type,
- const RRTTL& expected_rrttl, const vector<string>& expected_data,
- const vector<string>* expected_sig_data)
-{
- EXPECT_EQ(expected_name, rrset->getName());
- EXPECT_EQ(expected_class, rrset->getClass());
- EXPECT_EQ(expected_type, rrset->getType());
- EXPECT_EQ(expected_rrttl, rrset->getTTL());
-
- RdataIteratorPtr rdata_iterator = rrset->getRdataIterator();
- vector<string>::const_iterator data_it = expected_data.begin();
- for (; data_it != expected_data.end(); ++data_it) {
- EXPECT_FALSE(rdata_iterator->isLast());
- if (rdata_iterator->isLast()) {
- // buggy case, should stop here
- break;
- }
-
- // We use text-based comparison so that we can easily identify which
- // data causes the error. RDATA::compare() is the most strict
- // comparison method, but in this case text-based comparison should
- // be okay because we generate the text data from Rdata objects
- // rather than hand-write the expected text.
- EXPECT_EQ(createRdata(expected_type, expected_class,
- *data_it)->toText(),
- rdata_iterator->getCurrent().toText());
- rdata_iterator->next();
- }
-
- if (expected_sig_data != NULL) {
- RRsetPtr sig_rrset = rrset->getRRsig();
- EXPECT_FALSE(sig_rrset == NULL);
- if (sig_rrset != NULL) { // check this to avoid possible bug.
- // Note: we assume the TTL for RRSIG is the same as that of the
- // RRSIG target.
- checkRRset(sig_rrset, expected_name, expected_class,
- RRType::RRSIG(), expected_rrttl, *expected_sig_data,
- NULL);
- }
- } else {
- EXPECT_TRUE(NULL == rrset->getRRsig());
- }
-
- EXPECT_TRUE(rdata_iterator->isLast());
-}
-
-void
-checkFind(FindMode mode, const Sqlite3DataSrc& data_source,
- const Name& expected_name, const Name* zone_name,
- const RRClass& qclass, const RRClass& expected_class,
- const RRType& expected_type, const vector<RRTTL>& expected_ttls,
- const uint32_t expected_flags, const vector<RRType>& expected_types,
- const vector<const vector<string>* >& expected_answers,
- const vector<const vector<string>* >& expected_signatures)
-{
- RRsetList result_sets;
- uint32_t find_flags;
- unsigned int rrset_matched = 0;
- unsigned int rrset_count = 0;
-
- switch (mode) {
- case NORMAL:
- EXPECT_EQ(DataSrc::SUCCESS,
- data_source.findRRset(expected_name, qclass,
- expected_type, result_sets, find_flags,
- zone_name));
- break;
- case EXACT:
- EXPECT_EQ(DataSrc::SUCCESS,
- data_source.findExactRRset(expected_name, qclass,
- expected_type, result_sets,
- find_flags, zone_name));
- break;
- case ADDRESS:
- EXPECT_EQ(DataSrc::SUCCESS,
- data_source.findAddrs(expected_name, qclass, result_sets,
- find_flags, zone_name));
- break;
- case REFERRAL:
- EXPECT_EQ(DataSrc::SUCCESS,
- data_source.findReferral(expected_name, qclass, result_sets,
- find_flags, zone_name));
- break;
- }
- EXPECT_EQ(expected_flags, find_flags);
- RRsetList::iterator it = result_sets.begin();
- for (; it != result_sets.end(); ++it) {
- vector<RRType>::const_iterator found_type =
- find(expected_types.begin(), expected_types.end(),
- (*it)->getType());
- // there should be a match
- EXPECT_TRUE(found_type != expected_types.end());
- if (found_type != expected_types.end()) {
- unsigned int index = distance(expected_types.begin(), found_type);
- checkRRset(*it, expected_name, expected_class, (*it)->getType(),
- expected_ttls[index], *expected_answers[index],
- expected_signatures[index]);
- ++rrset_matched;
- }
- ++rrset_count;
- }
- EXPECT_EQ(expected_types.size(), rrset_count);
- EXPECT_EQ(expected_types.size(), rrset_matched);
-}
-
-void
-checkFind(FindMode mode, const Sqlite3DataSrc& data_source,
- const Name& expected_name, const Name* zone_name,
- const RRClass& qclass, const RRClass& expected_class,
- const RRType& expected_type, const RRTTL& expected_rrttl,
- const uint32_t expected_flags,
- const vector<string>& expected_data,
- const vector<string>* expected_sig_data)
-{
- vector<RRType> types;
- vector<RRTTL> ttls;
- vector<const vector<string>* > answers;
- vector<const vector<string>* > signatures;
-
- types.push_back(expected_type);
- ttls.push_back(expected_rrttl);
- answers.push_back(&expected_data);
- signatures.push_back(expected_sig_data);
-
- checkFind(mode, data_source, expected_name, zone_name, qclass,
- expected_class, expected_type, ttls, expected_flags, types,
- answers, signatures);
-}
-
-TEST_F(Sqlite3DataSourceTest, close) {
- EXPECT_EQ(DataSrc::SUCCESS, data_source.close());
-}
-
-TEST_F(Sqlite3DataSourceTest, reOpen) {
- // Replace the data with a totally different zone. This should succeed,
- // and shouldn't match any names in the previously managed domains.
- EXPECT_EQ(DataSrc::SUCCESS, data_source.close());
- EXPECT_EQ(DataSrc::SUCCESS, data_source.init(SQLITE_DBFILE_EXAMPLE2));
-
- DataSrcMatch match(www_name, rrclass);
- data_source.findClosestEnclosure(match);
- EXPECT_EQ(static_cast<void*>(NULL), match.getEnclosingZone());
- EXPECT_EQ(static_cast<void*>(NULL), match.getDataSource());
-}
-
-TEST_F(Sqlite3DataSourceTest, openFail) {
- EXPECT_EQ(DataSrc::SUCCESS, data_source.close());
- EXPECT_THROW(data_source.init(SQLITE_DBFILE_NOTEXIST), Sqlite3Error);
-}
-
-TEST_F(Sqlite3DataSourceTest, doubleOpen) {
- // An attempt of duplicate open should trigger an exception.
- EXPECT_THROW(data_source.init(SQLITE_DBFILE_EXAMPLE), DataSourceError);
-}
-
-TEST_F(Sqlite3DataSourceTest, doubleClose) {
- // An attempt of duplicate close should trigger an exception.
- EXPECT_EQ(DataSrc::SUCCESS, data_source.close());
- EXPECT_THROW(data_source.close(), DataSourceError);
-}
-
-TEST_F(Sqlite3DataSourceTest, openBrokenDB) {
- EXPECT_EQ(DataSrc::SUCCESS, data_source.close());
- // The database exists but is broken. An exception will be thrown
- // in the middle of the initialization.
- EXPECT_THROW(data_source.init(SQLITE_DBFILE_BROKENDB), Sqlite3Error);
- // Confirming the strong exception guarantee: the data source must be
- // in the closed state.
- EXPECT_EQ(DataSrc::SUCCESS, data_source.init(SQLITE_DBFILE_EXAMPLE));
-}
-
-// Different schema versions, see sqlite3_accessor_unittest.
-TEST_F(Sqlite3DataSourceTest, differentSchemaVersions) {
- EXPECT_EQ(DataSrc::SUCCESS, data_source.close());
- EXPECT_THROW(data_source.init(SQLITE_DBFILE_NEWSCHEMA),
- IncompatibleDbVersion);
- EXPECT_THROW(data_source.init(SQLITE_DBFILE_OLDSCHEMA),
- IncompatibleDbVersion);
- // Don't bother to test the new_minor case; we should retire this stuff
- // before it really happens.
-}
-
-// This test only confirms that on-the-fly schema creation works.
-TEST_F(Sqlite3DataSourceTest, memoryDB) {
- EXPECT_EQ(DataSrc::SUCCESS, data_source.close());
- EXPECT_EQ(DataSrc::SUCCESS, data_source.init(SQLITE_DBFILE_MEMORY));
-}
-
-TEST_F(Sqlite3DataSourceTest, findClosestEnclosure) {
- DataSrcMatch match(www_name, rrclass);
- data_source.findClosestEnclosure(match);
- EXPECT_EQ(zone_name, *match.getEnclosingZone());
- EXPECT_EQ(&data_source, match.getDataSource());
-}
-
-TEST_F(Sqlite3DataSourceTest, findClosestEnclosureMatchRoot) {
- EXPECT_EQ(DataSrc::SUCCESS, data_source.close());
- EXPECT_EQ(DataSrc::SUCCESS, data_source.init(SQLITE_DBFILE_EXAMPLE_ROOT));
-
- DataSrcMatch match(Name("org."), rrclass);
- data_source.findClosestEnclosure(match);
- EXPECT_EQ(Name("."), *match.getEnclosingZone());
- EXPECT_EQ(&data_source, match.getDataSource());
-}
-
-TEST_F(Sqlite3DataSourceTest, findClosestEnclosureAtDelegation) {
- // The search name exists both in the parent and child zones, but
- // child has a better match.
- DataSrcMatch match(child_name, rrclass);
- data_source.findClosestEnclosure(match);
- EXPECT_EQ(child_name, *match.getEnclosingZone());
- EXPECT_EQ(&data_source, match.getDataSource());
-}
-
-TEST_F(Sqlite3DataSourceTest, findClosestEnclosureNoMatch) {
- DataSrcMatch match(nomatch_name, rrclass);
- data_source.findClosestEnclosure(match);
- EXPECT_EQ(static_cast<void*>(NULL), match.getEnclosingZone());
- EXPECT_EQ(static_cast<void*>(NULL), match.getDataSource());
-}
-
-TEST_F(Sqlite3DataSourceTest, findClosestClassMismatch) {
- DataSrcMatch match(nomatch_name, rrclass);
- data_source.findClosestEnclosure(match);
- EXPECT_EQ(static_cast<void*>(NULL), match.getEnclosingZone());
- EXPECT_EQ(static_cast<void*>(NULL), match.getDataSource());
-}
-
-// If the query class is ANY, the result should be the same as the case where
-// the class exactly matches.
-TEST_F(Sqlite3DataSourceTest, findClosestClassAny) {
- DataSrcMatch match(www_name, RRClass::ANY());
- data_source.findClosestEnclosure(match);
- EXPECT_EQ(zone_name, *match.getEnclosingZone());
- EXPECT_EQ(&data_source, match.getDataSource());
-}
-
-TEST_F(Sqlite3DataSourceTest, findRRsetNormal) {
- // Without specifying the zone name, and then with the zone name
- checkFind(NORMAL, data_source, www_name, NULL, rrclass, rrclass, rrtype,
- rrttl, 0, common_a_data, &common_sig_data);
-
- checkFind(NORMAL, data_source, www_name, &zone_name, rrclass, rrclass,
- rrtype, rrttl, 0, common_a_data, &common_sig_data);
-
- // With a zone name that doesn't match
- EXPECT_EQ(DataSrc::SUCCESS,
- data_source.findRRset(www_name, rrclass, rrtype,
- result_sets, find_flags, &nomatch_name));
- EXPECT_EQ(DataSrc::NO_SUCH_ZONE, find_flags);
- EXPECT_TRUE(result_sets.begin() == result_sets.end()); // should be empty
-}
-
-TEST_F(Sqlite3DataSourceTest, findRRsetClassMismatch) {
- EXPECT_EQ(DataSrc::ERROR,
- data_source.findRRset(www_name, rrclass_notmatch, rrtype,
- result_sets, find_flags, NULL));
-}
-
-TEST_F(Sqlite3DataSourceTest, findRRsetClassAny) {
- checkFind(NORMAL, data_source, www_name, NULL, RRClass::ANY(), rrclass,
- rrtype, rrttl, 0, common_a_data, &common_sig_data);
-}
-
-TEST_F(Sqlite3DataSourceTest, findRRsetClassClassAny) {
- checkFind(NORMAL, data_source, www_name, NULL, RRClass::ANY(), rrclass,
- rrtype, rrttl, 0, common_a_data, &common_sig_data);
-}
-
-TEST_F(Sqlite3DataSourceTest, findRRsetNormalANY) {
- types.push_back(RRType::A());
- types.push_back(RRType::NSEC());
- ttls.push_back(RRTTL(3600));
- ttls.push_back(RRTTL(7200));
- answers.push_back(&common_a_data);
- answers.push_back(&www_nsec_data);
- signatures.push_back(&common_sig_data);
- signatures.push_back(&www_nsec_sig_data);
-
- rrtype = RRType::ANY();
- checkFind(NORMAL, data_source, www_name, NULL, rrclass, rrclass, rrtype,
- ttls, 0, types, answers, signatures);
-
- checkFind(NORMAL, data_source, www_name, &zone_name, rrclass, rrclass,
- rrtype, ttls, 0, types, answers, signatures);
-}
-
-// Case insensitive lookup
-TEST_F(Sqlite3DataSourceTest, findRRsetNormalCase) {
- checkFind(NORMAL, data_source, www_upper_name, NULL, rrclass, rrclass,
- rrtype, rrttl, 0, common_a_data, &common_sig_data);
-
- checkFind(NORMAL, data_source, www_upper_name, &zone_name, rrclass, rrclass,
- rrtype, rrttl, 0, common_a_data, &common_sig_data);
-
- EXPECT_EQ(DataSrc::SUCCESS,
- data_source.findRRset(www_upper_name, rrclass, rrtype,
- result_sets, find_flags, &nomatch_name));
- EXPECT_EQ(DataSrc::NO_SUCH_ZONE, find_flags);
- EXPECT_TRUE(result_sets.begin() == result_sets.end()); // should be empty
-}
-
-TEST_F(Sqlite3DataSourceTest, findRRsetApexNS) {
- rrtype = RRType::NS();
- checkFind(NORMAL, data_source, zone_name, NULL, rrclass, rrclass, rrtype,
- rrttl, DataSrc::REFERRAL, apex_ns_data, &apex_ns_sig_data);
-
- checkFind(NORMAL, data_source, zone_name, &zone_name, rrclass, rrclass,
- rrtype, rrttl, DataSrc::REFERRAL, apex_ns_data,
- &apex_ns_sig_data);
-
- EXPECT_EQ(DataSrc::SUCCESS,
- data_source.findRRset(zone_name, rrclass, rrtype,
- result_sets, find_flags, &nomatch_name));
- EXPECT_EQ(DataSrc::NO_SUCH_ZONE, find_flags);
- EXPECT_TRUE(result_sets.begin() == result_sets.end()); // should be empty
-}
-
-TEST_F(Sqlite3DataSourceTest, findRRsetApexANY) {
- types.push_back(RRType::SOA());
- types.push_back(RRType::NS());
- types.push_back(RRType::MX());
- types.push_back(RRType::NSEC());
- types.push_back(RRType::DNSKEY());
- ttls.push_back(rrttl); // SOA TTL
- ttls.push_back(rrttl); // NS TTL
- ttls.push_back(rrttl); // MX TTL
- ttls.push_back(RRTTL(7200)); // NSEC TTL
- ttls.push_back(rrttl); // DNSKEY TTL
- answers.push_back(&apex_soa_data);
- answers.push_back(&apex_ns_data);
- answers.push_back(&apex_mx_data);
- answers.push_back(&apex_nsec_data);
- answers.push_back(&apex_dnskey_data);
- signatures.push_back(&apex_soa_sig_data);
- signatures.push_back(&apex_ns_sig_data);
- signatures.push_back(&apex_mx_sig_data);
- signatures.push_back(&apex_nsec_sig_data);
- signatures.push_back(&apex_dnskey_sig_data);
-
- rrtype = RRType::ANY();
-
- // there is an NS at the zone apex, so the REFERRAL flag should
- // be set, but will ordinarily be ignored by the caller
- checkFind(NORMAL, data_source, zone_name, NULL, rrclass, rrclass, rrtype,
- ttls, DataSrc::REFERRAL, types, answers, signatures);
-
- checkFind(NORMAL, data_source, zone_name, &zone_name, rrclass, rrclass,
- rrtype, ttls, DataSrc::REFERRAL, types, answers, signatures);
-}
-
-TEST_F(Sqlite3DataSourceTest, findRRsetMixedANY) {
- // ANY query for mixed order RRs
- const Name qname("mix.example.com");
-
- types.push_back(RRType::A());
- types.push_back(RRType::AAAA());
- ttls.push_back(rrttl);
- ttls.push_back(rrttl);
- answers.push_back(&mix_a_data);
- answers.push_back(&mix_aaaa_data);
- signatures.push_back(NULL);
- signatures.push_back(NULL);
-
- rrtype = RRType::ANY();
- checkFind(NORMAL, data_source, qname, &zone_name, rrclass, rrclass,
- rrtype, ttls, 0, types, answers, signatures);
-}
-
-TEST_F(Sqlite3DataSourceTest, findRRsetApexNXRRSET) {
- rrtype = RRType::AAAA();
- EXPECT_EQ(DataSrc::SUCCESS,
- data_source.findRRset(zone_name, rrclass, rrtype,
- result_sets, find_flags, &zone_name));
- // there's an NS RRset at the apex name, so the REFERRAL flag should be
- // set, too.
- EXPECT_EQ(DataSrc::TYPE_NOT_FOUND | DataSrc::REFERRAL, find_flags);
- EXPECT_TRUE(result_sets.begin() == result_sets.end());
-
- // Same test, without specifying the zone name
- EXPECT_EQ(DataSrc::SUCCESS,
- data_source.findRRset(zone_name, rrclass, rrtype,
- result_sets, find_flags, NULL));
- // there's an NS RRset at the apex name, so the REFERRAL flag should be
- // set, too.
- EXPECT_EQ(DataSrc::TYPE_NOT_FOUND | DataSrc::REFERRAL, find_flags);
- EXPECT_TRUE(result_sets.begin() == result_sets.end());
-}
-
-// Matching a wildcard node. There's nothing special for the data source API
-// point of view, but perform minimal tests anyway.
-TEST_F(Sqlite3DataSourceTest, findRRsetWildcard) {
- const Name qname("*.wild.example.com");
- checkFind(NORMAL, data_source, qname, NULL, rrclass, rrclass,
- rrtype, rrttl, 0, wild_a_data, &common_sig_data);
- checkFind(NORMAL, data_source, qname, &zone_name, rrclass, rrclass,
- rrtype, rrttl, 0, wild_a_data, &common_sig_data);
-}
-
-TEST_F(Sqlite3DataSourceTest, findRRsetEmptyNode) {
- // foo.bar.example.com exists, but bar.example.com doesn't have any data.
- const Name qname("bar.example.com");
-
- EXPECT_EQ(DataSrc::SUCCESS,
- data_source.findRRset(qname, rrclass, rrtype,
- result_sets, find_flags, NULL));
- EXPECT_EQ(DataSrc::TYPE_NOT_FOUND, find_flags);
- EXPECT_TRUE(result_sets.begin() == result_sets.end());
-
- EXPECT_EQ(DataSrc::SUCCESS,
- data_source.findRRset(qname, rrclass, rrtype,
- result_sets, find_flags, &zone_name));
- EXPECT_EQ(DataSrc::TYPE_NOT_FOUND, find_flags);
- EXPECT_TRUE(result_sets.begin() == result_sets.end());
-}
-
-// There's nothing special about DNAME lookup for the data source API
-// point of view, but perform minimal tests anyway.
-TEST_F(Sqlite3DataSourceTest, findRRsetDNAME) {
- const Name qname("dname.example.com");
-
- rrtype = RRType::DNAME();
- checkFind(NORMAL, data_source, qname, NULL, rrclass, rrclass,
- rrtype, rrttl, 0, dname_data, &dname_sig_data);
- checkFind(NORMAL, data_source, qname, &zone_name, rrclass, rrclass,
- rrtype, rrttl, 0, dname_data, &dname_sig_data);
-}
-
-TEST_F(Sqlite3DataSourceTest, findRRsetCNAME) {
- const Name qname("foo.example.com");
-
- // This qname only has the CNAME (+ sigs). CNAME query is not different
- // from ordinary queries.
- rrtype = RRType::CNAME();
- checkFind(NORMAL, data_source, qname, NULL, rrclass, rrclass,
- rrtype, rrttl, 0, cname_data, &cname_sig_data);
- checkFind(NORMAL, data_source, qname, &zone_name, rrclass, rrclass,
- rrtype, rrttl, 0, cname_data, &cname_sig_data);
-
- // queries for (ordinary) different RR types that match the CNAME.
- // CNAME_FOUND flag is set, and the CNAME RR is returned instead of A
- rrtype = RRType::A();
- types.push_back(RRType::CNAME());
- ttls.push_back(rrttl);
- answers.push_back(&cname_data);
- signatures.push_back(&cname_sig_data);
- checkFind(NORMAL, data_source, qname, NULL, rrclass, rrclass,
- rrtype, ttls, DataSrc::CNAME_FOUND, types, answers, signatures);
- checkFind(NORMAL, data_source, qname, &zone_name, rrclass, rrclass,
- rrtype, ttls, DataSrc::CNAME_FOUND, types, answers, signatures);
-
- // NSEC query that match the CNAME.
- // CNAME_FOUND flag is NOT set, and the NSEC RR is returned instead of
- // CNAME.
- rrtype = RRType::NSEC();
- checkFind(NORMAL, data_source, qname, NULL, rrclass, rrclass,
- rrtype, RRTTL(7200), 0, cname_nsec_data, &cname_nsec_sig_data);
- checkFind(NORMAL, data_source, qname, &zone_name, rrclass, rrclass,
- rrtype, RRTTL(7200), 0, cname_nsec_data, &cname_nsec_sig_data);
-}
-
-TEST_F(Sqlite3DataSourceTest, findRRsetDelegation) {
- const Name qname("www.subzone.example.com");
-
- // query for a name under a zone cut. From the data source API point
- // of view this is no different than "NXDOMAIN".
- EXPECT_EQ(DataSrc::SUCCESS,
- data_source.findRRset(qname, rrclass, rrtype,
- result_sets, find_flags, NULL));
- EXPECT_EQ(DataSrc::NAME_NOT_FOUND, find_flags);
- EXPECT_TRUE(result_sets.begin() == result_sets.end());
-}
-
-TEST_F(Sqlite3DataSourceTest, findRRsetDelegationAtZoneCut) {
- const Name qname("subzone.example.com");
-
- // query for a name *at* a zone cut. It matches the NS RRset for the
- // delegation.
-
- // For non-NS ordinary queries, "no type" should be set too, and no RRset is
- // returned.
- EXPECT_EQ(DataSrc::SUCCESS,
- data_source.findRRset(qname, rrclass, rrtype,
- result_sets, find_flags, NULL));
- EXPECT_EQ(DataSrc::TYPE_NOT_FOUND | DataSrc::REFERRAL, find_flags);
- EXPECT_TRUE(result_sets.begin() == result_sets.end());
-
- EXPECT_EQ(DataSrc::SUCCESS,
- data_source.findRRset(qname, rrclass, rrtype,
- result_sets, find_flags, &zone_name));
- EXPECT_EQ(DataSrc::TYPE_NOT_FOUND | DataSrc::REFERRAL, find_flags);
- EXPECT_TRUE(result_sets.begin() == result_sets.end());
-
- // For NS query, RRset is returned with the REFERRAL flag. No RRSIG should
- // be provided.
- rrtype = RRType::NS();
- checkFind(NORMAL, data_source, qname, NULL, rrclass, rrclass,
- rrtype, rrttl, DataSrc::REFERRAL, delegation_ns_data, NULL);
- checkFind(NORMAL, data_source, qname, &zone_name, rrclass, rrclass,
- rrtype, rrttl, DataSrc::REFERRAL, delegation_ns_data, NULL);
-
- // For ANY query. At the backend data source level, it returns all
- // existent records with their RRSIGs, setting the referral flag.
- rrtype = RRType::ANY();
- types.push_back(RRType::NS());
- types.push_back(RRType::NSEC());
- types.push_back(RRType::DS());
- ttls.push_back(rrttl);
- ttls.push_back(RRTTL(7200));
- ttls.push_back(rrttl);
- answers.push_back(&delegation_ns_data);
- answers.push_back(&delegation_nsec_data);
- answers.push_back(&delegation_ds_data);
- signatures.push_back(NULL);
- signatures.push_back(&delegation_nsec_sig_data);
- signatures.push_back(&delegation_ds_sig_data);
-
- checkFind(NORMAL, data_source, qname, &zone_name, rrclass, rrclass,
- rrtype, ttls, DataSrc::REFERRAL, types, answers,
- signatures);
- checkFind(NORMAL, data_source, qname, NULL, rrclass, rrclass,
- rrtype, ttls, DataSrc::REFERRAL, types, answers,
- signatures);
-
- // For NSEC query. At the backend data source level, it returns NSEC+
- // RRSIG with the referral flag.
- rrtype = RRType::NSEC();
- checkFind(NORMAL, data_source, qname, NULL, rrclass, rrclass,
- rrtype, RRTTL(7200), DataSrc::REFERRAL, delegation_nsec_data,
- &delegation_nsec_sig_data);
- checkFind(NORMAL, data_source, qname, &zone_name, rrclass, rrclass,
- rrtype, RRTTL(7200), DataSrc::REFERRAL, delegation_nsec_data,
- &delegation_nsec_sig_data);
-}
-
-TEST_F(Sqlite3DataSourceTest, findRRsetInChildZone) {
- const Name qname("www.sql1.example.com");
- const Name child_zone_name("sql1.example.com");
-
- // If we don't specify the zone, the data source should identify the
- // best matching zone.
- checkFind(NORMAL, data_source, qname, NULL, rrclass, rrclass,
- rrtype, rrttl, 0, child_a_data, &child_sig_data);
-
- // If we specify the parent zone, it's treated as NXDOMAIN because it's
- // under a zone cut.
- EXPECT_EQ(DataSrc::SUCCESS,
- data_source.findRRset(qname, rrclass, rrtype,
- result_sets, find_flags, &zone_name));
- EXPECT_EQ(DataSrc::NAME_NOT_FOUND, find_flags);
- EXPECT_TRUE(result_sets.begin() == result_sets.end());
-
- // If we specify the child zone, it should be the same as the first case.
- checkFind(NORMAL, data_source, qname, &child_zone_name, rrclass, rrclass,
- rrtype, rrttl, 0, child_a_data, &child_sig_data);
-}
-
-TEST_F(Sqlite3DataSourceTest, findExactRRset) {
- // Normal case. No different than findRRset.
- checkFind(EXACT, data_source, www_name, &zone_name, rrclass, rrclass,
- rrtype, rrttl, 0, common_a_data, &common_sig_data);
-}
-
-TEST_F(Sqlite3DataSourceTest, findExactRRsetClassMismatch) {
- EXPECT_EQ(DataSrc::ERROR,
- data_source.findExactRRset(www_name, rrclass_notmatch, rrtype,
- result_sets, find_flags, NULL));
-}
-
-TEST_F(Sqlite3DataSourceTest, findExactRRsetClassAny) {
- checkFind(EXACT, data_source, www_name, &zone_name, RRClass::ANY(), rrclass,
- rrtype, rrttl, 0, common_a_data, &common_sig_data);
-}
-
-TEST_F(Sqlite3DataSourceTest, findRRsetNSEC3) {
- // Simple NSEC3 tests (more should be added)
- string hashstr("1BB7SO0452U1QHL98UISNDD9218GELR5");
-
- const Name nsec3_zonename("sql2.example.com");
- EXPECT_EQ(DataSrc::SUCCESS,
- data_source.findCoveringNSEC3(nsec3_zonename,
- hashstr, result_sets));
- RRsetList::iterator it = result_sets.begin();
- checkRRset(*it, Name(hashstr).concatenate(nsec3_zonename), rrclass,
- RRType::NSEC3(), RRTTL(7200), nsec3_data, &nsec3_sig_data);
- ++it;
- EXPECT_TRUE(it == result_sets.end());
-
-}
-
-TEST_F(Sqlite3DataSourceTest, findExactRRsetCNAME) {
- const Name qname("foo.example.com");
-
- // This qname only has the CNAME (+ sigs). In this case it should be
- // no different than findRRset for CNAME query.
- rrtype = RRType::CNAME();
- checkFind(NORMAL, data_source, qname, &zone_name, rrclass, rrclass,
- rrtype, rrttl, 0, cname_data, &cname_sig_data);
-
- // queries for (ordinary) different RR types that match the CNAME.
- // "type not found" set, but CNAME and its sig are returned (awkward,
- // but that's how it currently works).
- rrtype = RRType::A();
- types.push_back(RRType::CNAME());
- ttls.push_back(rrttl);
- answers.push_back(&cname_data);
- signatures.push_back(&cname_sig_data);
- checkFind(EXACT, data_source, qname, &zone_name, rrclass, rrclass,
- rrtype, ttls, DataSrc::TYPE_NOT_FOUND, types, answers,
- signatures);
-}
-
-void
-Sqlite3DataSourceTest::findReferralRRsetCommon(const Name& qname,
- const RRClass& qclass)
-{
- types.push_back(RRType::NS());
- types.push_back(RRType::DS());
- ttls.push_back(rrttl);
- ttls.push_back(rrttl);
- answers.push_back(&apex_ns_data);
- answers.push_back(&child_ds_data);
- signatures.push_back(NULL);
- signatures.push_back(&child_ds_sig_data);
- // Note: zone_name matters here because we need to perform the search
- // in the parent zone.
- checkFind(REFERRAL, data_source, qname, &zone_name, qclass, rrclass,
- rrtype, ttls, DataSrc::REFERRAL, types, answers, signatures);
-}
-
-
-TEST_F(Sqlite3DataSourceTest, findReferralRRset) {
- // A referral lookup searches for NS, DS, or DNAME, or their sigs.
- const Name qname("sql1.example.com");
- findReferralRRsetCommon(qname, rrclass);
-}
-
-TEST_F(Sqlite3DataSourceTest, findReferralRRsetClassMismatch) {
- EXPECT_EQ(DataSrc::ERROR,
- data_source.findReferral(www_name, rrclass_notmatch, result_sets,
- find_flags, NULL));
-}
-
-TEST_F(Sqlite3DataSourceTest, findReferralRRsetClassAny) {
- const Name qname("sql1.example.com");
- findReferralRRsetCommon(qname, RRClass::ANY());
-}
-
-TEST_F(Sqlite3DataSourceTest, findReferralRRsetDNAME) {
- // same as above. the DNAME case.
- const Name qname("dname.example.com");
- checkFind(REFERRAL, data_source, qname, &zone_name, rrclass, rrclass,
- RRType::DNAME(), rrttl, 0, dname_data, &dname_sig_data);
-}
-
-TEST_F(Sqlite3DataSourceTest, findReferralRRsetFail) {
- // qname has not "referral" records. the result should be "empty".
- EXPECT_EQ(DataSrc::SUCCESS,
- data_source.findReferral(www_name, rrclass,
- result_sets, find_flags, &zone_name));
- EXPECT_EQ(DataSrc::TYPE_NOT_FOUND, find_flags);
- EXPECT_TRUE(result_sets.begin() == result_sets.end());
-}
-
-void
-Sqlite3DataSourceTest::findAddressRRsetCommon(const RRClass& qclass) {
- // A referral lookup searches for A or AAAA, or their sigs.
-
- // A-only case
- checkFind(ADDRESS, data_source, www_name, &zone_name, qclass, rrclass,
- rrtype, rrttl, 0, common_a_data, &common_sig_data);
-
- // AAAA-only case
- checkFind(ADDRESS, data_source, Name("ip6.example.com"), &zone_name,
- qclass, rrclass, RRType::AAAA(), rrttl, 0, common_aaaa_data,
- &common_aaaa_sig_data);
-
- // Dual-stack
- types.push_back(RRType::A());
- ttls.push_back(rrttl);
- answers.push_back(&common_a_data);
- signatures.push_back(&common_sig_data);
- types.push_back(RRType::AAAA());
- ttls.push_back(rrttl);
- answers.push_back(&common_aaaa_data);
- signatures.push_back(&common_aaaa_sig_data);
- checkFind(ADDRESS, data_source, Name("ip46.example.com"), &zone_name,
- rrclass, rrclass, rrtype, ttls, 0, types, answers, signatures);
-
- // The qname doesn't have no address records.
- EXPECT_EQ(DataSrc::SUCCESS,
- data_source.findAddrs(Name("text.example.com"), qclass,
- result_sets, find_flags, &zone_name));
- EXPECT_EQ(DataSrc::TYPE_NOT_FOUND, find_flags);
- EXPECT_TRUE(result_sets.begin() == result_sets.end());
-}
-
-TEST_F(Sqlite3DataSourceTest, findAddressRRset) {
- findAddressRRsetCommon(rrclass);
-}
-
-TEST_F(Sqlite3DataSourceTest, findAddressRRsetClassMismatch) {
- EXPECT_EQ(DataSrc::ERROR, data_source.findAddrs(www_name, rrclass_notmatch,
- result_sets, find_flags,
- NULL));
-}
-
-TEST_F(Sqlite3DataSourceTest, findAddressRRsetClassAny) {
- findAddressRRsetCommon(RRClass::ANY());
-}
-
-}
diff --git a/src/lib/datasrc/tests/static_unittest.cc b/src/lib/datasrc/tests/static_unittest.cc
deleted file mode 100644
index 2a19ecb..0000000
--- a/src/lib/datasrc/tests/static_unittest.cc
+++ /dev/null
@@ -1,422 +0,0 @@
-// Copyright (C) 2010 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 <stdint.h>
-#include <string>
-#include <vector>
-
-#include <config.h>
-
-#include <gtest/gtest.h>
-
-#include <dns/name.h>
-#include <dns/message.h>
-#include <dns/rdata.h>
-#include <dns/rrclass.h>
-#include <dns/rrtype.h>
-#include <dns/rdataclass.h>
-#include <dns/rrsetlist.h>
-#include <cc/data.h>
-
-#include <datasrc/query.h>
-#include <datasrc/data_source.h>
-#include <datasrc/static_datasrc.h>
-
-using namespace std;
-using namespace isc::dns;
-using namespace isc::dns::rdata;
-using namespace isc::datasrc;
-
-namespace {
-class StaticDataSourceTest : public ::testing::Test {
-protected:
- StaticDataSourceTest() : version_name("version.bind"),
- authors_name("authors.bind"),
- nomatch_name("example.com"),
- rrclass(RRClass::CH()), rrtype(RRType::TXT()),
- rrttl(RRTTL(0)), find_flags(0), matched_rdata(0)
- {
- // version.bind is answered with package name+version
- // (defined as PACKAGE_STRING in config.h)
- version_data.push_back(PACKAGE_STRING);
-
- // NOTE: in addition, the order of the following items matter.
- authors_data.push_back("Chen Zhengzhang");
- authors_data.push_back("Dmitriy Volodin");
- authors_data.push_back("Evan Hunt");
- authors_data.push_back("Haidong Wang");
- authors_data.push_back("Haikuo Zhang");
- authors_data.push_back("Han Feng");
- authors_data.push_back("Jelte Jansen");
- authors_data.push_back("Jeremy C. Reed");
- authors_data.push_back("Xie Jiagui");
- authors_data.push_back("Jin Jian");
- authors_data.push_back("JINMEI Tatuya");
- authors_data.push_back("Kazunori Fujiwara");
- authors_data.push_back("Michael Graff");
- authors_data.push_back("Michal Vaner");
- authors_data.push_back("Mukund Sivaraman");
- authors_data.push_back("Naoki Kambe");
- authors_data.push_back("Shane Kerr");
- authors_data.push_back("Shen Tingting");
- authors_data.push_back("Stephen Morris");
- authors_data.push_back("Yoshitaka Aharen");
- authors_data.push_back("Zhang Likun");
-
- version_ns_data.push_back("version.bind.");
- authors_ns_data.push_back("authors.bind.");
-
- version_soa_data.push_back("version.bind. hostmaster.version.bind. "
- "0 28800 7200 604800 86400");
- authors_soa_data.push_back("authors.bind. hostmaster.authors.bind. "
- "0 28800 7200 604800 86400");
- }
- StaticDataSrc data_source;
- const Name version_name;
- const Name authors_name;
- const Name nomatch_name;
- const RRClass rrclass;
- RRType rrtype; // we allow this to be modified in the test
- RRTTL rrttl;
- RRsetList result_sets;
- uint32_t find_flags;
- unsigned matched_rdata;
- vector<RRType> types;
- vector<RRTTL> ttls;
- vector<const vector<string>* > answers;
- vector<string> version_data;
- vector<string> authors_data;
- vector<string> version_ns_data;
- vector<string> authors_ns_data;
- vector<string> version_soa_data;
- vector<string> authors_soa_data;
-};
-
-void
-checkRRset(ConstRRsetPtr rrset, const Name& expected_name,
- const RRClass& expected_class, const RRType& expected_type,
- const RRTTL& rrttl, const vector<string>& expected_data)
-{
- EXPECT_EQ(expected_name, rrset->getName());
- EXPECT_EQ(expected_class, rrset->getClass());
- EXPECT_EQ(expected_type, rrset->getType());
- EXPECT_EQ(rrttl, rrset->getTTL());
-
- RdataIteratorPtr rdata_iterator = rrset->getRdataIterator();
- vector<string>::const_iterator data_it = expected_data.begin();
- for (; data_it != expected_data.end(); ++data_it) {
- EXPECT_FALSE(rdata_iterator->isLast());
- if (rdata_iterator->isLast()) {
- // buggy case, should stop here
- break;
- }
- EXPECT_EQ(0, (rdata_iterator->getCurrent()).compare(
- *createRdata(expected_type,
- expected_class,
- *data_it)));
- rdata_iterator->next();
- }
-
- EXPECT_TRUE(rdata_iterator->isLast());
-}
-
-void
-checkFind(const DataSrc& data_source,
- const Name& qname, const Name* zone_name,
- const RRClass& qclass, const RRClass& expected_class,
- const RRType& qtype, const vector<RRTTL>& expected_ttls,
- const uint32_t expected_flags, const vector<RRType>& expected_types,
- const vector<const vector<string>* >& expected_answers)
-{
- RRsetList result_sets;
- uint32_t find_flags;
- unsigned int rrset_matched = 0;
- unsigned int rrset_count = 0;
-
- EXPECT_EQ(DataSrc::SUCCESS,
- data_source.findRRset(qname, qclass, qtype, result_sets,
- find_flags, zone_name));
- EXPECT_EQ(expected_flags, find_flags);
-
- if ((find_flags & (DataSrc::NO_SUCH_ZONE | DataSrc::NAME_NOT_FOUND |
- DataSrc::TYPE_NOT_FOUND)) != 0) {
- EXPECT_TRUE(result_sets.begin() == result_sets.end());
- return;
- }
-
- RRsetList::iterator it = result_sets.begin();
- for (; it != result_sets.end(); ++it) {
- vector<RRType>::const_iterator found_type =
- find(expected_types.begin(), expected_types.end(),
- (*it)->getType());
- // there should be a match
- EXPECT_TRUE(found_type != expected_types.end());
- if (found_type != expected_types.end()) {
- unsigned int index = distance(expected_types.begin(), found_type);
- checkRRset(*it, qname, expected_class, (*it)->getType(),
- expected_ttls[index], *expected_answers[index]);
- ++rrset_matched;
- }
- ++rrset_count;
- }
- EXPECT_EQ(expected_types.size(), rrset_count);
- EXPECT_EQ(expected_types.size(), rrset_matched);
-}
-
-void
-checkFind(const DataSrc& data_source,
- const Name& qname, const Name* zone_name,
- const RRClass& qclass, const RRClass& expected_class,
- const RRType& qtype, // == expected RRType
- const RRTTL& expected_ttl, const uint32_t expected_flags,
- const vector<string>& expected_answers)
-{
- vector<RRType> types;
- vector<RRTTL> ttls;
- vector<const vector<string>* > answers;
-
- types.push_back(qtype);
- ttls.push_back(expected_ttl);
- answers.push_back(&expected_answers);
-
- checkFind(data_source, qname, zone_name, qclass, expected_class, qtype,
- ttls, expected_flags, types, answers);
-}
-
-TEST_F(StaticDataSourceTest, init) {
- EXPECT_EQ(DataSrc::SUCCESS, data_source.init());
-}
-
-TEST_F(StaticDataSourceTest, close) {
- EXPECT_EQ(DataSrc::SUCCESS, data_source.init());
-}
-
-TEST_F(StaticDataSourceTest, findClosestEnclosureForVersion) {
- DataSrcMatch match(version_name, rrclass);
- data_source.findClosestEnclosure(match);
- EXPECT_EQ(version_name, *match.getEnclosingZone());
- EXPECT_EQ(&data_source, match.getDataSource());
-}
-
-// Class Any query should result in the same answer.
-TEST_F(StaticDataSourceTest, findClosestEnclosureForVersionClassAny) {
- DataSrcMatch match(version_name, RRClass::ANY());
- data_source.findClosestEnclosure(match);
- EXPECT_EQ(version_name, *match.getEnclosingZone());
- EXPECT_EQ(&data_source, match.getDataSource());
-}
-
-// If class doesn't match the lookup should fail.
-TEST_F(StaticDataSourceTest, findClosestEnclosureForVersionClassMismatch) {
- DataSrcMatch match(version_name, RRClass::IN());
- data_source.findClosestEnclosure(match);
- EXPECT_EQ(static_cast<void*>(NULL), match.getEnclosingZone());
- EXPECT_EQ(static_cast<void*>(NULL), match.getDataSource());
-}
-
-TEST_F(StaticDataSourceTest, findClosestEnclosureForVersionPartial) {
- DataSrcMatch match(Name("foo").concatenate(version_name), rrclass);
- data_source.findClosestEnclosure(match);
- EXPECT_EQ(version_name, *match.getEnclosingZone());
- EXPECT_EQ(&data_source, match.getDataSource());
-}
-
-TEST_F(StaticDataSourceTest, findClosestEnclosureForAuthors) {
- DataSrcMatch match(authors_name, rrclass);
- data_source.findClosestEnclosure(match);
- EXPECT_EQ(authors_name, *match.getEnclosingZone());
- EXPECT_EQ(&data_source, match.getDataSource());
-}
-
-TEST_F(StaticDataSourceTest, findClosestEnclosureForAuthorsPartial) {
- DataSrcMatch match(Name("foo").concatenate(authors_name), rrclass);
- data_source.findClosestEnclosure(match);
- EXPECT_EQ(authors_name, *match.getEnclosingZone());
- EXPECT_EQ(&data_source, match.getDataSource());
-}
-
-TEST_F(StaticDataSourceTest, findClosestEnclosureNoMatch) {
- DataSrcMatch match(nomatch_name, rrclass);
- data_source.findClosestEnclosure(match);
- EXPECT_EQ(static_cast<void*>(NULL), match.getEnclosingZone());
- EXPECT_EQ(static_cast<void*>(NULL), match.getDataSource());
-}
-
-TEST_F(StaticDataSourceTest, findRRsetVersionTXT) {
- checkFind(data_source, version_name, NULL, rrclass, rrclass,
- rrtype, rrttl, 0, version_data);
- checkFind(data_source, version_name, &version_name, rrclass, rrclass,
- rrtype, rrttl, 0, version_data);
-}
-
-TEST_F(StaticDataSourceTest, findRRsetVersionNS) {
- rrtype = RRType::NS();
- checkFind(data_source, version_name, NULL, rrclass, rrclass,
- rrtype, rrttl, 0, version_ns_data);
- checkFind(data_source, version_name, &version_name, rrclass, rrclass,
- rrtype, rrttl, 0, version_ns_data);
-}
-
-TEST_F(StaticDataSourceTest, findRRsetVersionSOA) {
- rrtype = RRType::SOA();
-
- checkFind(data_source, version_name, NULL, rrclass, rrclass,
- rrtype, rrttl, 0, version_soa_data);
- checkFind(data_source, version_name, &version_name, rrclass, rrclass,
- rrtype, rrttl, 0, version_soa_data);
-}
-
-TEST_F(StaticDataSourceTest, findRRsetVersionANY) {
- rrtype = RRType::ANY();
-
- types.push_back(RRType::TXT());
- types.push_back(RRType::NS());
- types.push_back(RRType::SOA());
- ttls.push_back(rrttl);
- ttls.push_back(rrttl);
- ttls.push_back(rrttl);
- answers.push_back(&version_data);
- answers.push_back(&version_ns_data);
- answers.push_back(&version_soa_data);
-
- checkFind(data_source, version_name, NULL, rrclass, rrclass, rrtype, ttls,
- 0, types, answers);
- checkFind(data_source, version_name, &version_name, rrclass, rrclass,
- rrtype, ttls, 0, types, answers);
-}
-
-TEST_F(StaticDataSourceTest, findRRsetAuthorsTXT) {
- checkFind(data_source, authors_name, NULL, rrclass, rrclass,
- rrtype, rrttl, 0, authors_data);
- checkFind(data_source, authors_name, &authors_name, rrclass, rrclass,
- rrtype, rrttl, 0, authors_data);
-}
-
-TEST_F(StaticDataSourceTest, findRRsetAuthorsNS) {
- rrtype = RRType::NS();
- checkFind(data_source, authors_name, NULL, rrclass, rrclass,
- rrtype, rrttl, 0, authors_ns_data);
- checkFind(data_source, authors_name, &authors_name, rrclass, rrclass,
- rrtype, rrttl, 0, authors_ns_data);
-}
-
-TEST_F(StaticDataSourceTest, findRRsetAuthorsSOA) {
- rrtype = RRType::SOA();
- checkFind(data_source, authors_name, NULL, rrclass, rrclass,
- rrtype, rrttl, 0, authors_soa_data);
- checkFind(data_source, authors_name, &authors_name, rrclass, rrclass,
- rrtype, rrttl, 0, authors_soa_data);
-}
-
-TEST_F(StaticDataSourceTest, findRRsetAuthorsANY) {
- rrtype = RRType::ANY();
-
- types.push_back(RRType::TXT());
- types.push_back(RRType::NS());
- types.push_back(RRType::SOA());
- ttls.push_back(rrttl);
- ttls.push_back(rrttl);
- ttls.push_back(rrttl);
- answers.push_back(&authors_data);
- answers.push_back(&authors_ns_data);
- answers.push_back(&authors_soa_data);
-
- checkFind(data_source, authors_name, NULL, rrclass, rrclass, rrtype, ttls,
- 0, types, answers);
- checkFind(data_source, authors_name, &authors_name, rrclass, rrclass,
- rrtype, ttls, 0, types, answers);
-}
-// Class ANY lookup should result in the same answer.
-TEST_F(StaticDataSourceTest, findRRsetVersionClassAny) {
- checkFind(data_source, version_name, NULL, RRClass::ANY(), rrclass,
- rrtype, rrttl, 0, version_data);
- checkFind(data_source, version_name, &version_name, RRClass::ANY(), rrclass,
- rrtype, rrttl, 0, version_data);
-}
-
-// If the class doesn't match, it should simply fail.
-TEST_F(StaticDataSourceTest, findRRsetVersionClassMismatch) {
- EXPECT_EQ(DataSrc::ERROR,
- data_source.findRRset(version_name, RRClass::IN(), rrtype,
- result_sets, find_flags, &version_name));
-}
-
-TEST_F(StaticDataSourceTest, findRRsetOutOfZone) {
- // If the qname doesn't match any of the static zones, the result should
- // be "no such zone", regardless of whether the zone is explicitly
- // specified. Other "expected" result parameters will be ignored.
- checkFind(data_source, nomatch_name, NULL, rrclass, rrclass,
- rrtype, rrttl, DataSrc::NO_SUCH_ZONE, authors_ns_data);
- checkFind(data_source, nomatch_name, &version_name, rrclass, rrclass,
- rrtype, rrttl, DataSrc::NO_SUCH_ZONE, authors_ns_data);
- checkFind(data_source, nomatch_name, &authors_name, rrclass, rrclass,
- rrtype, rrttl, DataSrc::NO_SUCH_ZONE, authors_ns_data);
-}
-
-// If a zone name is given but doesn't match any of the static zones,
-// the result should be "no such zone"
-TEST_F(StaticDataSourceTest, findRRsetZoneMismatch) {
- const Name& short_zonename(Name("bind"));
- checkFind(data_source, version_name, &short_zonename, rrclass, rrclass,
- rrtype, rrttl, DataSrc::NO_SUCH_ZONE, authors_ns_data);
- checkFind(data_source, authors_name, &short_zonename, rrclass, rrclass,
- rrtype, rrttl, DataSrc::NO_SUCH_ZONE, authors_ns_data);
-}
-
-// Zone matches, but name doesn't exist in the zone
-TEST_F(StaticDataSourceTest, findRRsetNoName) {
- checkFind(data_source, Name("foo").concatenate(version_name), NULL, rrclass,
- rrclass, rrtype, rrttl, DataSrc::NAME_NOT_FOUND, authors_ns_data);
- checkFind(data_source, Name("foo").concatenate(version_name), &version_name,
- rrclass, rrclass, rrtype, rrttl, DataSrc::NAME_NOT_FOUND,
- authors_ns_data);
- checkFind(data_source, Name("foo").concatenate(authors_name), NULL, rrclass,
- rrclass, rrtype, rrttl, DataSrc::NAME_NOT_FOUND, authors_ns_data);
- checkFind(data_source, Name("foo").concatenate(authors_name), &authors_name,
- rrclass, rrclass, rrtype, rrttl, DataSrc::NAME_NOT_FOUND,
- authors_ns_data);
-}
-
-// Zone matches and qname exists, but type doesn't exist for the name.
-TEST_F(StaticDataSourceTest, findRRsetNoType) {
- const RRType& nomatch_type = RRType::A();
-
- checkFind(data_source, version_name, NULL, rrclass,
- rrclass, nomatch_type, rrttl, DataSrc::TYPE_NOT_FOUND,
- authors_ns_data);
- checkFind(data_source, version_name, &version_name, rrclass,
- rrclass, nomatch_type, rrttl, DataSrc::TYPE_NOT_FOUND,
- authors_ns_data);
- checkFind(data_source, authors_name, NULL, rrclass,
- rrclass, nomatch_type, rrttl, DataSrc::TYPE_NOT_FOUND,
- authors_ns_data);
- checkFind(data_source, authors_name, &authors_name, rrclass,
- rrclass, nomatch_type, rrttl, DataSrc::TYPE_NOT_FOUND,
- authors_ns_data);
-}
-
-// Simple tests for "unsupported" tests.
-TEST_F(StaticDataSourceTest, notImplemented) {
- Name target_name(version_name);
- EXPECT_EQ(DataSrc::NOT_IMPLEMENTED,
- data_source.findPreviousName(version_name, target_name,
- &version_name));
-
- string target_hash;
- EXPECT_EQ(DataSrc::NOT_IMPLEMENTED,
- data_source.findCoveringNSEC3(version_name, target_hash,
- result_sets));
-}
-
-}
diff --git a/src/lib/datasrc/tests/test_datasrc.cc b/src/lib/datasrc/tests/test_datasrc.cc
deleted file mode 100644
index d78e7db..0000000
--- a/src/lib/datasrc/tests/test_datasrc.cc
+++ /dev/null
@@ -1,657 +0,0 @@
-// Copyright (C) 2010 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 <cassert>
-
-#include <algorithm>
-
-#include <dns/tests/unittest_util.h>
-#include <datasrc/tests/test_datasrc.h>
-
-#include <datasrc/data_source.h>
-
-#include <util/buffer.h>
-#include <dns/messagerenderer.h>
-#include <dns/name.h>
-#include <dns/rdata.h>
-#include <dns/rdataclass.h>
-#include <dns/rrclass.h>
-#include <dns/rrset.h>
-#include <dns/rrsetlist.h>
-#include <dns/rrtype.h>
-#include <dns/rrttl.h>
-
-#include <iostream>
-
-using isc::UnitTestUtil;
-using namespace std;
-using namespace isc::dns;
-using namespace isc::dns::rdata;
-
-namespace isc {
-namespace datasrc {
-
-namespace {
-
-// This is a mock data source for testing. It can contain multiple zones.
-// The content of each zone should be configured in the form of RRData{}.
-// Each RRData element is a tuple of char strings, representing
-// "name, RRtype, RDATA". For simplicity we use the same single TTL for
-// RRs (TEST_TTL) defined below.
-// Multiple RRs of the same pair of (name, RRtype) can be defined, but
-// they must not be interleaved with other types of pair. For example,
-// This is okay:
-// {"example.com", "AAAA", "2001:db8::1"},
-// {"example.com", "AAAA", "2001:db8::2"},
-// ...
-// but this is invalid:
-// {"example.com", "AAAA", "2001:db8::1"},
-// {"example.com", "A", "192.0.2.1"},
-// {"example.com", "AAAA", "2001:db8::2"},
-// ...
-// If an RRset is associated with an RRSIG, the RRSIG must immediately follow
-// the RRset to be signed. Multiple RRSIGs can follow the RRset. RRSIG
-// records will always be attached to the most recent non-RRSIG RRset;
-// consequently, the first RR listed must not be an RRSIG record.
-//
-// Names are sorted internally, and don't have to be sorted in the data.
-//
-// A zone is defined in the form of ZoneData{}, which contains:
-// zone name (character string)
-// RRclass (character string)
-// A pointer to in-zone RRs in the form of RRData{}
-// A pointer to glue RRs in the form of RRData{}
-// Glues can be omitted, in which case a convenient constant "empty_records"
-// can be specified.
-
-// For simplicity we use the same single TTL for all test RRs.
-const uint32_t TEST_TTL = 3600;
-
-struct RRData {
- const char* const name;
- const char* const rrtype;
- const char* const rdata;
-};
-
-struct ZoneData {
- const char* const zone_name;
- const char* const rrclass;
- const struct RRData* records;
- const struct RRData* glue_records;
-};
-
-//
-// zone data for example.com
-//
-const struct RRData example_com_records[] = {
- // example.com
- {"example.com", "NS", "dns01.example.com"},
- {"example.com", "NS", "dns02.example.com"},
- {"example.com", "NS", "dns03.example.com"},
- {"example.com", "RRSIG", "NS 5 2 3600 20100322084538 20100220084538 33495 example.com. ClcrfjkQZUY5L6ZlCkU3cJHzcrEGrofKSVeeoeZ+w6yeEowFNVXs2YBo3tom53DiCrdD9rs3feVSLGW5rjsz/O6lDuomgQG+EVSnWa7GTIPBXj1BmDXXp3XxeldYmhf4UzaN5BA+RUA5E8NChNKuNNof76j2S9tilfN/kvpy4fw="},
- {"example.com", "SOA", "master.example.com. admin.example.com. 1234 3600 1800 2419200 7200"},
- {"example.com", "RRSIG", "SOA 5 2 3600 20100322084538 20100220084538 33495 example.com. KUun66Qaw36osk2BJS6U1fAy3PPDkNo2QK4meGNbDBY8q8b+f2o+IXJ14YCvssGl1ORW0CcLnDRxssnk8V/Svmj5iFhO+8HC2hnVBdi2zewvdVtwRb+lWwKN7pkXXwuy6g1t9WCd/j5FCc/wgxqtZUTPb6XgZcnHrORDMOTqLs4="},
- {"example.com", "NSEC", "cname-ext.example.com. NS SOA MX RRSIG NSEC DNSKEY"},
- {"example.com", "RRSIG", "NSEC 5 2 7200 20100322084538 20100220084538 33495 example.com. KxuVaPPKNPJzr/q+cJPiNlkHVTQK0LVsgTbSqruXQc25lAd0wn5oKUtxL1bEAchHkfA8eLzcYCj2ZqqAv9OJubw53mfskTad7UHs4Uj2RTrIsNGMCiZGgOpvNb9JcWpQtoyXVT1uNse+Qsbeir0eyeYIufUynFU041jtNrlJMio="},
- {"example.com", "DNSKEY", "257 3 5 AwEAAe5WFbxdCPq2jZrZhlMj7oJdff3W7syJtbvzg62tRx0gkoCDoBI9DPjlOQG0UAbj+xUV4HQZJStJaZ+fHU5AwVNT+bBZdtV+NujSikhdTHb4FYLg2b3Cx9NyJvAVukHp/91HnWuG4T36CzAFrfPwsHIrBz9BsaIQ21VRkcmj7DswfI/iDGd8j6bqiODyNZYQ+ZrLmF0KIJ2yPN3iO6Zq23TaOrVTjB7d1a/h31ODfiHAxFHrkY3t3D5JR9Nsl/7fdRmSznwtcSDgLXBoFEYmw6p86AcvRyoYNcL1SXjaKVLG5jyU3UR+LcGZT5t/0xGfoIK/aKwENrsjcKZZj660b1M="},
- {"example.com", "DNSKEY", "256 3 5 AwEAAcOUBllYc1hf7ND9uDy+Yz1BF3sI0m4qNGV7WcTD0WEiuV7IjXgHE36fCmS9QsUxSSOVo1I/FMxI2PJVqTYHkXFBS7AzLGsQYMU7UjBZSotBJ6Imt5pXMu+lEDNy8TOUzG3xm7g0qcbWYF6qCEfvZoBtAqi5Rk7Mlrqs8agxYyMx"},
- {"example.com", "RRSIG", "DNSKEY 5 2 3600 20100416210049 20100317210049 4456 example.com. 37FC0rcwOZVarTMjft0BMbvv8hbJU7OHNsvO7R1q6OgsLTj7QGMX3sC42JGbwUrYI/OwnZblNcv1eim0g0jX5k+sVr2OJsEubngRjVqLo54qV8rBC14tLk9PGKxxjQG0IBJU866uHxzXYBO2a1r2g93/qyTtrT7iPLu/2Ce1WRKMBPK0yf4nW2usFU/PXesXFWpZ7HLGZL73/NWv8wcezBDuU0B2PlHLjSu7k6poq6JWDC02o5SYnEBwsJ5Chi+3/NZmzKTiNP7g0H4t6QhunkEXxL3z0617mwwQt00ypXsNunnPy4Ub5Kllk1SKJl8ZkEDKkJtSvuXJhcAZsLyMQw=="},
- {"example.com", "RRSIG", "DNSKEY 5 2 3600 20100416210049 20100317210049 33495 example.com. h3OM5r3roBsgnEQk9fcjTg5L7p3yDptDpVzDN/lgjqpaWxtlz5LsulBH3YzwYyXzT7pG7L0/qT6dcuRECc/rniECviWvmJMJZzEAMry0Of/pk/8ekuGTxABpqwAoCwM5as30sc0cfMJTS7umpJVDA4lRB2zoKGefWnJ3+pREDiY="},
-
- // dns01.example.com
- {"dns01.example.com", "A", "192.0.2.1"},
- {"dns01.example.com", "RRSIG", "A 5 3 3600 20100322084538 20100220084538 33495 example.com. NIawlZLk8WZAjNux7oQM2mslfW52OZFFkWt++7FHu2SU98XqEeKfCMnpgtWe5T8Nr9cS8df901iEOJoWQzGTEaHYUBtEhsSjBVn7mKp3fz6473a2xxy75SUKZ0rxjNXSZ8Q5rnFmkX0HTH2Sg51mtjH6aC2pfheQnA2t193BnSg="},
- {"dns01.example.com", "NSEC", "dns02.example.com. A RRSIG NSEC"},
- {"dns01.example.com", "RRSIG", "NSEC 5 3 7200 20100322084538 20100220084538 33495 example.com. EkyeshmMNP9xiAz6mDFDIwksTdmkF9zsFzLuVKAgK6eUk7St6tp5PSvjA8nWol0vdvvz4LK85a4ffTFEiNRyvWeYP2vOhEkyDcrwuCd8Vc3jh/8Sm1Js+nX7hJStrZGFvp2TWPpt9nKH5p3MxXvTb/YVurnue0xSeFAE17O3+I0="},
-
- // dns02.example.com
- {"dns02.example.com", "A", "192.0.2.2"},
- {"dns02.example.com", "RRSIG", "A 5 3 3600 20100322084538 20100220084538 33495 example.com. XJtVMbUIRE0mk6Hn/Nx6k36jaxaBDPK2/IYB6vCQjJETz6gW4T6q/H/eY9/Lsw5iYPFhoBRDxT4XFj575t98kELXnJe1WhuMbRPlOhyOjxkLECaUne/sbFPOtbGFx9ohuojI0RgxxZiCFaO8wJuv6nfPuzmlLajWS6z9NZeOMIk="},
- {"dns02.example.com", "NSEC", "dns03.example.com. A RRSIG NSEC"},
- {"dns02.example.com", "RRSIG", "NSEC 5 3 7200 20100322084538 20100220084538 33495 example.com. imBNTMB3sPU4kblcaAH6V7lCVt5xgtAybi3DA/SbLEulLaV2NE6vcoEn/AieaM4mOJicQnUDj/H+1hSEhzxU2tRM8zfVlvztxQWn6eh7ZR4mKfNDSvRUGU9ykhpwMyC7wjOt1j5bcSA/OTnLRAilslnJyOM4bSaxVEFo8YPjncY="},
-
- // dns03.example.com
- {"dns03.example.com", "A", "192.0.2.3"},
- {"dns03.example.com", "RRSIG", "A 5 3 3600 20100322084538 20100220084538 33495 example.com. Ubrcm1H+F6m8khle7P9zU8eO+Jtuj+1Vx1MM5KAkmZPJwQe9uTcoCpQa6DXOGG9kajDTnNN1Be1gkZuJDTZJG4SmJLXLbNY3RDnxpGmWta3qs/VgDq78/YM8ropt1/s7YKyrCfGE2ff+FUB0mLObiG01ZV2gu5HJzgE7SEWLEiI="},
- {"dns03.example.com", "NSEC", "foo.example.com. A RRSIG NSEC"},
- {"dns03.example.com", "RRSIG", "NSEC 5 3 7200 20100322084538 20100220084538 33495 example.com. nn829Xw5CJFnPHwI9WHeT5epQv+odtCkHnjlPFGoPTLOyiks+041UmMqtq3uiSp4d2meMSe9UuDvoROT0L6NTtQQvVqiDhTn0irTFw1uw7fO8ZTG7eyu6Ypfz0+HvfbNvd4kMoD2OTgADRXPVsCTwK+PBOIIG9YTEQfl8pCqW5g="},
-
- // www.example.com
- {"www.example.com", "A", "192.0.2.1"},
- {"www.example.com", "RRSIG", "A 5 3 3600 20100322084538 20100220084538 33495 example.com. qyFyyV/mE8x4pdhudr5iycwhDsva31MzwO1kBR+bDKvzJg8mN8KxlPZrOlNNUhd3YRXQVwieMyxOTWRPXoxrNEDkNwimXkfe3rrHY7ibV9eNS4OIBUjb44VjCNr9CmQSzfuQ2yxO2r+YIuPYHRCjieD4xh6t9ay4IaCN/tDAJ+Q="},
- {"www.example.com", "NSEC", "example.com. A RRSIG NSEC"},
- {"www.example.com", "RRSIG", "NSEC 5 3 7200 20100322084538 20100220084538 33495 example.com. ZLZlSVBa2oe4U+7SZASnypP2VkI5gg1/1cVGqYUvfYNIUkcVMWDgn7DZCfpmo+2vdlV/4VhAc+sjDd+X+e57XGnW8+lqZHvG6NMMhmSGmeATD3D+8lEJJGo0dxoN4rHJQyp/eT2S4nChz+D/ze+YRagYxGF7pXm9zcrw3kKZGTs="},
-
- // *.wild.example.com
- {"*.wild.example.com", "A", "192.0.2.2"},
- {"*.wild.example.com", "RRSIG", "A 5 3 3600 20100322084538 20100220084538 33495 example.com. FdO+UWONgtLKFxUzzygGunw67F9y8SzsP7yOLEYVJclRR8X3Ii62L0gtQHq2y0TcKsXttRsD6XY+tM5P/pgXlTNi7Bk4Fgb0PIDPjOsfT4DrS80kWn0YbinM/4/FA1j5ru5sTTboOY5UGhvDnoA9ogNuQQYb2/3wkoH0PrA2Q/0="},
- {"*.wild.example.com", "NSEC", "*.wild2.example.com. A RRSIG NSEC"},
- {"*.wild.example.com", "RRSIG", "NSEC 5 3 7200 20100322084538 20100220084538 33495 example.com. OoGYslRj4xjZnBuzgOqsrvkDAHWycmQzbUxCRmgWnCbXiobJK7/ynONH3jm8G3vGlU0lwpHkhNs6cUK+6Nu8W49X3MT0Xksl/brroLcXYLi3vfxnYUNMMpXdeFl6WNNfoJRo90F/f/TWXAClRrDS29qiG3G1PEJZikIxZsZ0tyM="},
-
- // *.wild2.example.com
- {"*.wild2.example.com", "CNAME", "www.example.com"},
- {"*.wild2.example.com", "RRSIG", "CNAME 5 3 3600 20100410212307 20100311212307 33495 example.com. pGHtGdRBi4GKFSKszi6SsKvuBLDX8dFhZubU0tMojQ9SJuiFNF+WtxvdAYuUaoWP/9VLUaYmiw5u7JnzmR84DiXZPEs6DtD+UJdOZhaS7V7RTpE+tMOfVQBLpUnRWYtlTTmiBpFquzf3DdIxgUFhEPEuJJyp3LFRxJObCaq9 nvI="},
- {"*.wild2.example.com", "NSEC", "*.wild3.example.com. CNAME RRSIG NSEC"},
- {"*.wild2.example.com", "RRSIG", "NSEC 5 3 7200 20100410212307 20100311212307 33495 example.com. EuSzh6or8mbvwru2H7fyYeMpW6J8YZ528rabU38V/lMN0TdamghIuCneAvSNaZgwk2MSN1bWpZqB2kAipaM/ZI9/piLlTvVjjOQ8pjk0auwCEqT7Z7Qng3E92O9yVzO+WHT9QZn/fR6t60392In4IvcBGjZyjzQk8njIwbui xGA="},
-
- // *.wild3.example.com -- a wildcard record with a lame CNAME
- {"*.wild3.example.com", "CNAME", "spork.example.com"},
- {"*.wild3.example.com", "RRSIG", "CNAME 5 3 3600 20100410212307 20100311212307 33495 example.com. pGHtGdRBi4GKFSKszi6SsKvuBLDX8dFhZubU0tMojQ9SJuiFNF+WtxvdAYuUaoWP/9VLUaYmiw5u7JnzmR84DiXZPEs6DtD+UJdOZhaS7V7RTpE+tMOfVQBLpUnRWYtlTTmiBpFquzf3DdIxgUFhEPEuJJyp3LFRxJObCaq9 nvI="},
- {"*.wild3.example.com", "NSEC", "www.example.com. CNAME RRSIG NSEC"},
- {"*.wild3.example.com", "RRSIG", "NSEC 5 3 7200 20100410212307 20100311212307 33495 example.com. EuSzh6or8mbvwru2H7fyYeMpW6J8YZ528rabU38V/lMN0TdamghIuCneAvSNaZgwk2MSN1bWpZqB2kAipaM/ZI9/piLlTvVjjOQ8pjk0auwCEqT7Z7Qng3E92O9yVzO+WHT9QZn/fR6t60392In4IvcBGjZyjzQk8njIwbui xGA="},
-
- // foo.example.com
- {"foo.example.com", "CNAME", "cnametest.example.net"},
- {"foo.example.com", "RRSIG", "CNAME 5 3 3600 20100322084538 20100220084538 33495 example.com. DSqkLnsh0gCeCPVW/Q8viy9GNP+KHmFGfWqyVG1S6koBtGN/VQQ16M4PHZ9Zssmf/JcDVJNIhAChHPE2WJiaPCNGTprsaUshf1Q2vMPVnkrJKgDY8SVRYMptmT8eaT0gGri4KhqRoFpMT5OYfesybwDgfhFSQQAh6ps3bIUsy4o="},
- {"foo.example.com", "NSEC", "mail.example.com. CNAME RRSIG NSEC"},
- {"foo.example.com", "RRSIG", "NSEC 5 3 7200 20100322084538 20100220084538 33495 example.com. RTQwlSqui6StUYye1KCSOEr1d3irndWFqHBpwP7g7n+w8EDXJ8I7lYgwzHvlQt6BLAxe5fUDi7ct8M5hXvsm7FoWPZ5wXH+2/eJUCYxIw4vezKMkMwBP6M/YkJ2CMqY8DppYf60QaLDONQAr7AcK/naSyioeI5h6eaoVitUDMso="},
-
- // cname-int.example.com
- {"cname-int.example.com", "CNAME", "www.example.com."},
- {"cname-int.example.com", "RRSIG", "CNAME 5 3 3600 20100322084538 20100220084538 33495 example.com. U1wjt0XY9xjTwvUmWSUcfLGMhCjfX2ylWfHrycy50x2oxcK9z94E1ejen9wDTIEBSGYgi6wpZ8RK0+02N1DWTGpDqNXd7aFRfDrWQJ/q/XJHDx0vlcmhkWhrT82LBfKxkrptOzchuSo/c0mpK+mpiIMc1VOwY+yuQ2ALfcD6EHw="},
- {"cname-int.example.com", "NSEC", "dname.example.com. CNAME RRSIG NSEC"},
- {"cname-int.example.com", "RRSIG", "NSEC 5 3 7200 20100322084538 20100220084538 33495 example.com. rbV+gaxfrsoha59NOLF4EFyWQ+GuFCVK/8D77x1atan3HNlXBlZ1smgudKTaJ3CtlobIDt0MEdPxY1yn2Tskw/5mlP1PWf8oaP3BwGSQdn4gLI8+sMpNOPFEdXpxqxngm2F6/7fqniL1QuSAQBEdO+5UiCAgnncPmAsSJg3u1zg="},
-
- // cname-ext.example.com
- {"cname-ext.example.com", "CNAME", "www.sql1.example.com"},
- {"cname-ext.example.com", "RRSIG", "CNAME 5 3 3600 20100322084538 20100220084538 33495 example.com. bGPIuZilyygvTThK4BrdECuaBcnZUgW/0d09iN2CrNjckchQl3dtbnMNirFsVs9hShDSldRNlQpiAVMpnPgXHhReNum7jmX6yqIH6s8GKIo91zr3VL/ramlezie5w4MilDHrxXLK2pb8IHmP+ZHivQ2EtdYQZgETWBWxr5FDfwk="},
- {"cname-ext.example.com", "NSEC", "cname-int.example.com. CNAME RRSIG NSEC"},
- {"cname-ext.example.com", "RRSIG", "NSEC 5 3 7200 20100322084538 20100220084538 33495 example.com. inWsFwSDWG7TakjwbUTzTRpXz0WifelA5Kn3ABk6BVirIPmd+yQoNj2QZBDFAQwhnLPlNws2Oo4vgMsBMyx1Fv5eHgMUuCN3DUDaLlzlPtUb42CjOUa+jZBeTV/Hd7WZrirluASE1QFDprLdSSqoPPfAKvN3pORtW7y580dMOIM="},
-
- // dname.example.com
- {"dname.example.com", "DNAME", "sql1.example.com."},
- {"dname.example.com", "RRSIG", "DNAME 5 3 3600 20100322084538 20100220084538 33495 example.com. ae8U47oaiwWdurkSyzcsCAF6DxBqjukizwF7K7U6lQVMtfoUE14oiAqfj1fjH8YLDOO/Hd1twrd/u0vgjnI1Gg32YTi7cYOzwE912SV1u2B/y0awaQKWPBwOW0aI7vxelt1vMUF81xosiQD04gOIdDBTqbHKcDxum87iWbhk4Ug="},
- {"dname.example.com", "NSEC", "dns01.example.com. DNAME RRSIG NSEC"},
- {"dname.example.com", "RRSIG", "NSEC 5 3 7200 20100322084538 20100220084538 33495 example.com. c21Fff2D8vBrLzohBnUeflkaRdUAnUxAFGp+UQ0miACDCMOFBlCS9v9g/2+orOnKfd3l4vyz55C310t8JXgXb119ofaZWj2zkdUe+X8Bax+sMS0Y5K/sUhSNvbJbozr9UYPdvjSVBiWgh3s9fsb+etKq9uFukAzGU/FuGYpO0r0="},
-
- // subzone.example.com
- {"subzone.example.com", "NS", "ns1.subzone.example.com"},
- {"subzone.example.com", "NS", "ns2.subzone.example.com"},
- {"subzone.example.com", "NSEC", "*.wild.example.com. NS DS RRSIG NSEC"},
- {"subzone.example.com", "RRSIG", "NSEC 5 3 7200 20100322084538 20100220084538 33495 example.com. Oe2kgIhsLtPJ4+lDZDxznV8/vEVoXKOBFN9lwWyebaKa19BaSXlQ+YVejmulmKDDjEucMvEfuItfn6w7bnU+DzOLk5D1lJCjwDlKz8u3xOAx16TiuQn4bgQAOiFtBQygmGGqO3BVpX+jxsmw7eH3emofy8uUqr/C4aopnwuf28g="},
- {"subzone.example.com", "DS", "33313 5 1 0FDD7A2C11AA7F55D50FBF9B7EDDA2322C541A8D"},
- {"subzone.example.com", "DS", "33313 5 2 00B99B7006F496D135B01AB17EDB469B4BE9E1973884DEA757BC4E3015A8C3AB"},
- {"subzone.example.com", "RRSIG", "DS 5 3 3600 20100322084538 20100220084538 33495 example.com. dIqZKvpkJN1l92SOiWgJh3KbjErIN+EfojMsm4pEdV5xQdZwj6DNNEu6Kw4rRwdvrZIu0TyqPr3jSJb7o6R7vZgZzmLfVV/ojQah7rwuYHCFcfyZ4JyK2311fMhRR1QAvMsdcjdyA1XC140Cm6AnL3cH5rh/KUks/0ec3Ca7GNQ="},
-
- // subset of child zone: sql1
- {"sql1.example.com", "NS", "dns01.example.com"},
- {"sql1.example.com", "NS", "dns02.example.com"},
- {"sql1.example.com", "NS", "dns03.example.com"},
-
- {"sql1.example.com", "DS", "33313 5 1 0FDD7A2C11AA7F55D50FBF9B7EDDA2322C541A8D"},
- {"sql1.example.com", "DS", "33313 5 2 00B99B7006F496D135B01AB17EDB469B4BE9E1973884DEA757BC4E3015A8C3AB"},
- {"sql1.example.com", "RRSIG", "DS 5 3 3600 20100322084538 20100220084538 33495 example.com. dIqZKvpkJN1l92SOiWgJh3KbjErIN+EfojMsm4pEdV5xQdZwj6DNNEu6Kw4rRwdvrZIu0TyqPr3jSJb7o6R7vZgZzmLfVV/ojQah7rwuYHCFcfyZ4JyK2311fMhRR1QAvMsdcjdyA1XC140Cm6AnL3cH5rh/KUks/0ec3Ca7GNQ="},
- {"sql1.example.com", "NSEC", "subzone.example.com. NS DS RRSIG NSEC"},
- {"sql1.example.com", "RRSIG", "NSEC 5 3 7200 20100322084538 20100220084538 33495 example.com. k9FRdFyk/cPdkmmaoZbGZPpzIzfbFWQ3QCHd2qhJa0xAXaEOT/GBL6aFqx9SlunDu2wgES+To5fWPZGi4NzWpp6c5t27rnATN/oCEQ/UYIJKmWbqrXdst0Ps5boznk7suK2Y+km31KxaIf3fDd/T3kZCVsR0aWKRRRatPb7GfLw="},
-
- {NULL, NULL, NULL}
-};
-
-const struct RRData example_com_glue_records[] = {
- {"ns1.subzone.example.com", "A", "192.0.2.1"},
- {"ns2.subzone.example.com", "A", "192.0.2.2"},
- {NULL, NULL, NULL}
-};
-
-//
-// zone data for sql1.example.com
-//
-const struct RRData sql1_example_com_records[] = {
- {"sql1.example.com", "NS", "dns01.example.com"},
- {"sql1.example.com", "NS", "dns02.example.com"},
- {"sql1.example.com", "NS", "dns03.example.com"},
- {"sql1.example.com", "RRSIG", "NS 5 3 3600 20100322084536 20100220084536 12447 sql1.example.com. 0CL8noy0NSgoWwuKd+Dc6vyIIw2BrAEBx0IJzcSB6GlB25x/zjEd6AJG0be13HN6jOaTX8iWTuCVrEYuXg76V+M4EvTZHjEScj0az74TrDv4Vdo459paGKCX9B8NLJW1mW4fzZrrXQ8jmBEZeS91Q5rJrO+UKJEuUz3LYdTPvao="},
- {"sql1.example.com", "SOA", "master.example.com. admin.example.com. 678 3600 1800 2419200 7200"},
- {"sql1.example.com", "RRSIG", "SOA 5 3 3600 20100322084536 20100220084536 12447 sql1.example.com. oakulfyljL/RAKgCKXEZ3KsG8BJj5WG4JK4moWFB6c9OKem6jIk8hKP2XlUVXFuOYJlRdIM4KicmR2GAK+5jJp6z5ShssstYTXo3QosVm6oCKumuFeLFHzcjfqP1D+F9NsvHldJIBnS/4ebPkmR5OENyCZXQF5HmN2awIj4CLjE="},
- {"sql1.example.com", "NSEC", "www.sql1.example.com. NS SOA RRSIG NSEC DNSKEY"},
- {"sql1.example.com", "RRSIG", "NSEC 5 3 7200 20100322084536 20100220084536 12447 sql1.example.com. v71CgdTYccCiTqfRcn6HsvISQa8ruvUfCKtpwym0RW/G27xlZn8otj2IMtWwkLxti8Rqqu+PTViLaOIbeVfHBcqzAd7U59cAOYoq3ODZx6auiE3C23HAKqUavKcP7Esaajm1cbcWy6Kyie4CAZc8M7EeKxgkXMKJGqBQzF+/FOo="},
-
- // www.sql1.example.com
- {"www.sql1.example.com", "A", "192.0.2.2"},
- {"www.sql1.example.com", "RRSIG", "A 5 4 3600 20100322084536 20100220084536 12447 sql1.example.com. DNdVKxB3oBsB14NPoV9WG14Y/g4zMcIXLYnFjj9vRZRZJpAvbTEipiXlayuhOxnqU827OipETQyeULZmLsqIQ1wK4Fgf+9b5aJ8D85/o4wBka00X4hZ3MwDPRb4mjuogwBTBg5NRpNSzUfbkPGiav08BFwgg+Efm9veSB05arS0="},
- {"www.sql1.example.com", "NSEC", "sql1.example.com. A RRSIG NSEC"},
- {"www.sql1.example.com", "RRSIG", "NSEC 5 4 7200 20100322084536 20100220084536 12447 sql1.example.com. cJMJhDx/ND7/9j3zhyXe+6eaSsU7ByYpXhJzbe+OhjFgH0VasQXq7o1QB3I293UZ+yhkjgXap+9QtPlraaNaYyTyOMQ42OoxSefJpYz9CME/FI2tsUfyrCnLFxYRNet7sMS0q+hLqxRayuEHDFDp72hHPGLJQ8a7jq4SrIonT50="},
-
- {NULL, NULL, NULL}
-};
-
-//
-// zone data for loop.example
-//
-const struct RRData loop_example_records[] = {
- {"loop.example", "SOA", "master.loop.example admin.loop.example. "
- "1234 3600 1800 2419200 7200"},
- {"loop.example", "NS", "ns.loop.example"},
- {"one.loop.example", "CNAME", "two.loop.example"},
- {"two.loop.example", "CNAME", "one.loop.example"},
- {NULL, NULL, NULL}
-};
-
-//
-// zone data for nons.example
-//
-const struct RRData nons_example_records[] = {
- {"nons.example", "SOA", "master.nons.example admin.nons.example. "
- "1234 3600 1800 2419200 7200"},
- {"www.nons.example", "A", "192.0.2.1"},
- {"ns.nons.example", "A", "192.0.2.2"},
-
- // One of the NS names is intentionally non existent in the zone it belongs
- // to. This delegation is used to see if we still return the NS and the
- // existent glue.
- // (These are not relevant to test the case for the "no NS" case. We use
- // this zone to minimize the number of test zones)
- {"incompletechild.nons.example", "NS", "ns.incompletechild.nons.example"},
- {"incompletechild.nons.example", "NS", "nx.nosoa.example"},
-
- {NULL, NULL, NULL}
-};
-
-const struct RRData nons_example_glue_records[] = {
- {"ns.incompletechild.nons.example", "A", "192.0.2.1"},
- {NULL, NULL, NULL}
-};
-
-//
-// zone data for nons-dname.example
-//
-const struct RRData nonsdname_example_records[] = {
- {"nons-dname.example", "SOA", "master.nons-dname.example "
- "admin.nons-dname.example. 1234 3600 1800 2419200 7200"},
- {"nons-dname.example", "DNAME", "example.org"},
- {"www.nons-dname.example", "A", "192.0.2.1"},
- {"ns.nons-dname.example", "A", "192.0.2.2"},
- {NULL, NULL, NULL}
-};
-
-//
-// zone data for nosoa.example
-//
-const struct RRData nosoa_example_records[] = {
- {"nosoa.example", "NS", "ns.nosoa.example"},
- {"www.nosoa.example", "A", "192.0.2.1"},
- {"ns.nosoa.example", "A", "192.0.2.2"},
- {NULL, NULL, NULL}
-};
-
-//
-// zone data for apexcname.example.
-//
-const struct RRData apexcname_example_records[] = {
- {"apexcname.example", "CNAME", "canonical.apexcname.example"},
- {"canonical.apexcname.example", "SOA",
- "master.apexcname.example "
- "admin.apexcname.example. 1234 3600 1800 2419200 7200"},
- {NULL, NULL, NULL}
-};
-
-
-//
-// empty data set, for convenience.
-//
-const struct RRData empty_records[] = {
- {NULL, NULL, NULL}
-};
-
-//
-// test zones
-//
-const struct ZoneData zone_data[] = {
- { "example.com", "IN", example_com_records, example_com_glue_records },
- { "sql1.example.com", "IN", sql1_example_com_records, empty_records },
- { "loop.example", "IN", loop_example_records, empty_records },
- { "nons.example", "IN", nons_example_records, nons_example_glue_records },
- { "nons-dname.example", "IN", nonsdname_example_records, empty_records },
- { "nosoa.example", "IN", nosoa_example_records, empty_records },
- { "apexcname.example", "IN", nosoa_example_records, empty_records }
-};
-const size_t NUM_ZONES = sizeof(zone_data) / sizeof(zone_data[0]);
-
-struct Zone {
- Zone(const char* const name, const char* const class_txt) :
- zone_name(Name(name)), rrclass(class_txt)
- {}
- Name zone_name;
- RRClass rrclass;
- vector<Name> names;
- vector<RRsetPtr> rrsets;
-};
-vector<Zone> zones;
-}
-
-DataSrc::Result
-TestDataSrc::init(isc::data::ConstElementPtr) {
- return (init());
-}
-
-void
-buildZone(Zone& zone, const RRData* records, const bool is_glue) {
- RRsetPtr prev_rrset;
- for (int i = 0; records[i].name != NULL; ++i) {
- Name name(records[i].name);
- RRType rrtype(records[i].rrtype);
- RRsetPtr rrset;
- bool new_name = false;
-
- if (!prev_rrset || prev_rrset->getName() != name) {
- if (!is_glue) {
- zone.names.push_back(name);
- }
- new_name = true;
- }
-
- if (new_name || prev_rrset->getType() != rrtype) {
- rrset = RRsetPtr(new RRset(name, zone.rrclass, rrtype,
- RRTTL(TEST_TTL)));
- if (rrtype != RRType::RRSIG()) {
- zone.rrsets.push_back(rrset);
- }
- } else {
- rrset = prev_rrset;
- }
- rrset->addRdata(createRdata(rrtype, zone.rrclass, records[i].rdata));
- if (rrtype == RRType::RRSIG()) {
- prev_rrset->addRRsig(rrset);
- } else {
- prev_rrset = rrset;
- }
- }
-}
-
-DataSrc::Result
-TestDataSrc::init() {
- if (initialized) {
- return (SUCCESS);
- }
-
- if (zones.empty()) {
- for (int i = 0; i < NUM_ZONES; ++i) {
- Zone zone(zone_data[i].zone_name, zone_data[i].rrclass);
- buildZone(zone, zone_data[i].records, false);
- buildZone(zone, zone_data[i].glue_records, true);
- sort(zone.names.begin(), zone.names.end());
- zones.push_back(zone);
- }
- }
-
- initialized = true;
- return (SUCCESS);
-}
-
-void
-TestDataSrc::findClosestEnclosure(DataSrcMatch& match) const {
- const Name& qname = match.getName();
-
- if (match.getClass() != getClass() && match.getClass() != RRClass::ANY()) {
- return;
- }
-
- vector<Zone>::const_iterator it;
- vector<Zone>::const_iterator best_it = zones.end();
- unsigned int best_common_labels = 0;
- for (it = zones.begin(); it != zones.end(); ++it) {
- const NameComparisonResult cmp = qname.compare(it->zone_name);
- const NameComparisonResult::NameRelation reln = cmp.getRelation();
-
- if ((reln == NameComparisonResult::EQUAL ||
- reln == NameComparisonResult::SUBDOMAIN) &&
- cmp.getCommonLabels() > best_common_labels) {
- best_it = it;
- best_common_labels = cmp.getCommonLabels();
- }
- }
-
- if (best_it != zones.end()) {
- match.update(*this, best_it->zone_name);
- }
-}
-
-struct ZoneNameMatch : public unary_function<Name, bool> {
- ZoneNameMatch(const Name& name) : name_(name) {}
- bool operator()(const Zone& zone) const {
- return (zone.zone_name == name_);
- }
- const Name& name_;
-};
-
-// XXX: the main data source module can override the returned RRset.
-// That's bad and should be fixed (Trac #254), but for now we work around it.
-RRsetPtr
-copyRRset(RRsetPtr const source) {
- RRsetPtr rrset = RRsetPtr(new RRset(source->getName(), source->getClass(),
- source->getType(), source->getTTL()));
- RdataIteratorPtr it = source->getRdataIterator();
- for (; !it->isLast(); it->next()) {
- rrset->addRdata(it->getCurrent());
- }
- if (source->getRRsig()) {
- rrset->addRRsig(copyRRset(source->getRRsig()));
- }
-
- return (rrset);
-}
-
-class TestDataSrc::RRsetMatch {
-public:
- struct MatchResult {
- MatchResult(const bool name_found, const bool has_delegation) :
- name_found_(name_found), has_delegation_(has_delegation)
- {}
- bool name_found_;
- bool has_delegation_;
- };
- RRsetMatch(const Name& name, const RRType& rrtype, const Mode mode,
- RRsetList& target, uint32_t& flags) :
- name_(name), rrtype_(rrtype), mode_(mode), target_(target),
- flags_(flags), name_found_(false), has_delegation_(false)
- {}
- void operator()(const RRsetPtr& rrset) {
- if (rrset->getName() != name_) {
- return;
- }
- name_found_ = true;
-
- if (rrset->getType() == RRType::NS() ||
- rrset->getType() == RRType::DNAME()) {
- has_delegation_ = true;
- }
-
- if (mode_ == DELEGATION) {
- if (rrset->getType() == RRType::NS() ||
- rrset->getType() == RRType::DNAME() ||
- rrset->getType() == RRType::DS()) {
- target_.addRRset(copyRRset(rrset));
- }
- } else if (mode_ == ADDRESS) {
- if (rrset->getType() == RRType::A() ||
- rrset->getType() == RRType::AAAA()) {
- target_.addRRset(copyRRset(rrset));
- }
- } else {
- if (rrtype_ == RRType::NSEC() &&
- rrset->getType() == RRType::CNAME()) {
- // XXX: ignore CNAME if the qtype is NSEC.
- // tricky, but necessary.
- return;
- }
- if (rrtype_ == RRType::ANY() || rrtype_ == rrset->getType() ||
- rrset->getType() == RRType::CNAME() ||
- rrset->getType() == RRType::DNAME()) {
- target_.addRRset(copyRRset(rrset));
- if (rrset->getType() == RRType::CNAME()) {
- flags_ |= CNAME_FOUND;
- }
- if (rrset->getType() == RRType::DNAME()) {
- flags_ |= REFERRAL;
- }
- }
- }
-
- }
- MatchResult getResult() { return (MatchResult(name_found_,
- has_delegation_)); }
- const Name& name_;
- const RRType& rrtype_;
- const Mode mode_;
- RRsetList& target_;
- uint32_t& flags_;
- bool name_found_;
- bool has_delegation_;
-};
-
-void
-TestDataSrc::findRecords(const Name& name, const RRType& rdtype,
- RRsetList& target, const Name* zonename,
- const Mode mode, uint32_t& flags) const
-{
- flags = 0;
-
- assert(zonename != NULL);
-
- vector<Zone>::const_iterator zone = find_if(zones.begin(), zones.end(),
- ZoneNameMatch(*zonename));
- if (zone == zones.end()) {
- return;
- }
-
- const RRsetMatch::MatchResult match_result =
- for_each(zone->rrsets.begin(), zone->rrsets.end(),
- RRsetMatch(name, rdtype, mode, target, flags)).getResult();
- if (match_result.has_delegation_) {
- flags |= REFERRAL;
- }
- if (target.size() == 0) {
- if (match_result.name_found_) {
- flags |= TYPE_NOT_FOUND;
- } else {
- flags |= NAME_NOT_FOUND;
- }
- }
-}
-
-DataSrc::Result
-TestDataSrc::findRRset(const Name& qname,
- const RRClass& qclass,
- const RRType& qtype,
- RRsetList& target,
- uint32_t& flags,
- const Name* zonename) const
-{
- if (qclass != getClass() && qclass != RRClass::ANY()) {
- return (ERROR);
- }
-
- findRecords(qname, qtype, target, zonename, NORMAL, flags);
- return (SUCCESS);
-}
-
-DataSrc::Result
-TestDataSrc::findExactRRset(const Name& qname,
- const RRClass& qclass,
- const RRType& qtype,
- RRsetList& target,
- uint32_t& flags,
- const Name* zonename) const
-{
- if (qclass != getClass() && qclass != RRClass::ANY()) {
- return (ERROR);
- }
-
- findRecords(qname, qtype, target, zonename, NORMAL, flags);
- // Ignore referrals in this case
- flags &= ~REFERRAL;
-
- // CNAMEs don't count in this case
- if ((flags & CNAME_FOUND) != 0) {
- flags &= ~CNAME_FOUND;
- flags |= TYPE_NOT_FOUND;
- }
-
- return (SUCCESS);
-}
-
-DataSrc::Result
-TestDataSrc::findAddrs(const Name& qname,
- const RRClass& qclass,
- RRsetList& target,
- uint32_t& flags,
- const Name* zonename) const
-{
- if (qclass != getClass() && qclass != RRClass::ANY()) {
- return (ERROR);
- }
-
- findRecords(qname, RRType::ANY(), target, zonename, ADDRESS, flags);
- return (SUCCESS);
-}
-
-DataSrc::Result
-TestDataSrc::findReferral(const Name& qname,
- const RRClass& qclass,
- RRsetList& target,
- uint32_t& flags,
- const Name* zonename) const
-{
- if (qclass != getClass() && qclass != RRClass::ANY()) {
- return (ERROR);
- }
-
- findRecords(qname, RRType::ANY(), target, zonename, DELEGATION, flags);
- return (SUCCESS);
-}
-
-DataSrc::Result
-TestDataSrc::findPreviousName(const Name& qname,
- Name& target,
- const Name* zonename) const
-{
- assert(zonename != NULL);
-
- vector<Zone>::const_iterator zone = find_if(zones.begin(), zones.end(),
- ZoneNameMatch(*zonename));
- if (zone == zones.end()) {
- return (ERROR);
- }
-
- if (zone->names.empty()) {
- return (ERROR);
- }
-
- // if found, next_name >= qname.
- vector<Name>::const_iterator next_name =
- lower_bound(zone->names.begin(), zone->names.end(), qname);
- if (next_name == zone->names.end()) {
- // if no such name was found, the previous name is the last name.
- target = zone->names.back();
- } else if (*next_name == qname) {
- target = *next_name;
- } else if (next_name == zone->names.begin()) {
- // if qname < first_name, the "previous name" is the last name.
- target = zone->names.back();
- } else {
- // otherwise, qname and next_name share the same previous name.
- target = *(next_name - 1);
- }
- return (SUCCESS);
-}
-
-DataSrc::Result
-TestDataSrc::findCoveringNSEC3(const Name&, string&, RRsetList&) const {
- return (NOT_IMPLEMENTED);
-}
-
-}
-}
diff --git a/src/lib/datasrc/tests/test_datasrc.h b/src/lib/datasrc/tests/test_datasrc.h
deleted file mode 100644
index d0d67fc..0000000
--- a/src/lib/datasrc/tests/test_datasrc.h
+++ /dev/null
@@ -1,111 +0,0 @@
-// Copyright (C) 2010 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 __TEST_DATA_SOURCE_H
-#define __TEST_DATA_SOURCE_H
-
-#include <gtest/gtest.h>
-
-#include <datasrc/data_source.h>
-
-namespace isc {
-
-namespace dns {
-class Name;
-class RRClass;
-class RRType;
-class RRsetList;
-}
-
-namespace datasrc {
-class Query;
-
-class TestDataSrc : public DataSrc {
- ///
- /// \name Constructors, Assignment Operator and Destructor.
- ///
- /// Note: The copy constructor and the assignment operator are intentionally
- /// defined as private.
- //@{
-private:
- TestDataSrc(const TestDataSrc& source);
- TestDataSrc operator=(const TestDataSrc& source);
-public:
- TestDataSrc() : initialized(false) {}
- ~TestDataSrc() {}
- //@}
-
- void findClosestEnclosure(DataSrcMatch& match) const;
-
- Result findRRset(const isc::dns::Name& qname,
- const isc::dns::RRClass& qclass,
- const isc::dns::RRType& qtype,
- isc::dns::RRsetList& target,
- uint32_t& flags,
- const isc::dns::Name* zonename) const;
-
- Result findExactRRset(const isc::dns::Name& qname,
- const isc::dns::RRClass& qclass,
- const isc::dns::RRType& qtype,
- isc::dns::RRsetList& target,
- uint32_t& flags,
- const isc::dns::Name* zonename) const;
-
- Result findAddrs(const isc::dns::Name& qname,
- const isc::dns::RRClass& qclass,
- isc::dns::RRsetList& target,
- uint32_t& flags,
- const isc::dns::Name* zonename) const;
-
- Result findReferral(const isc::dns::Name& qname,
- const isc::dns::RRClass& qclass,
- isc::dns::RRsetList& target,
- uint32_t& flags,
- const isc::dns::Name* zonename) const;
-
- Result findPreviousName(const isc::dns::Name& qname,
- isc::dns::Name& target,
- const isc::dns::Name* zonename) const;
-
- Result findCoveringNSEC3(const isc::dns::Name& zonename,
- std::string& hash,
- isc::dns::RRsetList& target) const;
-
- Result init();
- Result init(isc::data::ConstElementPtr config);
- Result close() { return (SUCCESS); }
-
-private:
- bool initialized;
- enum Mode {
- NORMAL,
- ADDRESS,
- DELEGATION
- };
- class RRsetMatch;
-
- void findRecords(const isc::dns::Name& name, const isc::dns::RRType& rdtype,
- isc::dns::RRsetList& target,
- const isc::dns::Name* zonename, const Mode mode,
- uint32_t& flags) const;
-};
-
-}
-}
-
-#endif
-
-// Local Variables:
-// mode: c++
-// End:
diff --git a/src/lib/datasrc/tests/testdata/contexttest.zone b/src/lib/datasrc/tests/testdata/contexttest.zone
index e499e0d..a39649d 100644
--- a/src/lib/datasrc/tests/testdata/contexttest.zone
+++ b/src/lib/datasrc/tests/testdata/contexttest.zone
@@ -1,7 +1,7 @@
;; 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. 67 3600 300 3600000 3600
+example.org. 3600 IN SOA ns1.example.org. bugs.x.w.example.org. 71 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.
@@ -71,6 +71,9 @@ a.*.emptywild.example.org. 3600 IN AAAA 2001:db8::2
;; expansion
*.wildmx.example.org. 3600 IN MX 1 mx1.example.org.
+;; the owner name of additional for an answer RRset (MX) has DNAME
+dnamemx.example.org. 3600 IN MX 1 dname.example.org.
+
;; CNAME
alias.example.org. 3600 IN CNAME cname.example.org.
diff --git a/src/lib/datasrc/tests/zone_finder_context_unittest.cc b/src/lib/datasrc/tests/zone_finder_context_unittest.cc
index ea02543..14429ae 100644
--- a/src/lib/datasrc/tests/zone_finder_context_unittest.cc
+++ b/src/lib/datasrc/tests/zone_finder_context_unittest.cc
@@ -14,12 +14,14 @@
#include <exceptions/exceptions.h>
+#include <util/memory_segment_local.h>
+
#include <dns/masterload.h>
#include <dns/name.h>
#include <dns/rrclass.h>
#include <datasrc/zone.h>
-#include <datasrc/memory_datasrc.h>
+#include <datasrc/memory/memory_client.h>
#include <datasrc/database.h>
#include <datasrc/sqlite3_accessor.h>
@@ -39,8 +41,10 @@
using namespace std;
using boost::shared_ptr;
+using namespace isc::util;
using namespace isc::dns;
using namespace isc::datasrc;
+using isc::datasrc::memory::InMemoryClient;
using namespace isc::testutils;
namespace {
@@ -54,18 +58,16 @@ 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&);
+typedef DataSourceClientPtr (*ClientCreator)(MemorySegment&, 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);
+createInMemoryClient(MemorySegment& mem_sgmt, RRClass zclass,
+ const Name& zname)
+{
+ shared_ptr<InMemoryClient> client(new InMemoryClient(mem_sgmt, zclass));
+ client->load(zname, TEST_ZONE_FILE);
return (client);
}
@@ -76,7 +78,7 @@ addRRset(ZoneUpdaterPtr updater, ConstRRsetPtr rrset) {
}
DataSourceClientPtr
-createSQLite3Client(RRClass zclass, const Name& zname) {
+createSQLite3Client(MemorySegment&, 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.
@@ -103,7 +105,7 @@ class ZoneFinderContextTest :
{
protected:
ZoneFinderContextTest() : qclass_(RRClass::IN()), qzone_("example.org") {
- client_ = (*GetParam())(qclass_, qzone_);
+ client_ = (*GetParam())(mem_sgmt_, qclass_, qzone_);
REQUESTED_A.push_back(RRType::A());
REQUESTED_AAAA.push_back(RRType::AAAA());
REQUESTED_BOTH.push_back(RRType::A());
@@ -114,6 +116,7 @@ protected:
ASSERT_TRUE(finder_);
}
+ MemorySegmentLocal mem_sgmt_;
const RRClass qclass_;
const Name qzone_;
DataSourceClientPtr client_;
@@ -232,6 +235,18 @@ TEST_P(ZoneFinderContextTest, getAdditionalDelegationWithDname) {
result_sets_.begin(), result_sets_.end());
}
+TEST_P(ZoneFinderContextTest, getAdditionalOnDname) {
+ // The additional name has a DNAME as well as the additional record.
+ // The existence of DNAME shouldn't hide the additional record.
+ ZoneFinderContextPtr ctx = finder_->find(Name("dnamemx.example.org"),
+ RRType::MX());
+ EXPECT_EQ(ZoneFinder::SUCCESS, ctx->code);
+
+ ctx->getAdditional(REQUESTED_BOTH, result_sets_);
+ rrsetsCheck("dname.example.org. 3600 IN A 192.0.2.12\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.
diff --git a/src/lib/dhcp/dhcp4.h b/src/lib/dhcp/dhcp4.h
index e058960..002e74f 100644
--- a/src/lib/dhcp/dhcp4.h
+++ b/src/lib/dhcp/dhcp4.h
@@ -39,6 +39,9 @@
namespace isc {
namespace dhcp {
+/* IPv4 Broadcast address */
+#define DHCP_IPV4_BROADCAST_ADDRESS "255.255.255.255"
+
/* BOOTP (rfc951) message types */
enum BOOTPTypes {
BOOTREQUEST = 1,
diff --git a/src/lib/dhcp/iface_mgr.cc b/src/lib/dhcp/iface_mgr.cc
index a2f807e..ecaa652 100644
--- a/src/lib/dhcp/iface_mgr.cc
+++ b/src/lib/dhcp/iface_mgr.cc
@@ -122,8 +122,6 @@ IfaceMgr::IfaceMgr()
session_socket_(INVALID_SOCKET), session_callback_(NULL)
{
- cout << "IfaceMgr initialization." << endl;
-
try {
// required for sending/receiving packets
// let's keep it in front, just in case someone
@@ -134,13 +132,7 @@ IfaceMgr::IfaceMgr()
detectIfaces();
} catch (const std::exception& ex) {
- cout << "IfaceMgr creation failed:" << ex.what() << endl;
-
- // TODO Uncomment this (or call LOG_FATAL) once
- // interface detection is implemented. Otherwise
- // it is not possible to run tests in a portable
- // way (see detectIfaces() method).
- throw;
+ isc_throw(IfaceDetectError, ex.what());
}
}
@@ -166,51 +158,34 @@ void IfaceMgr::stubDetectIfaces() {
// is faked by detecting loopback interface (lo or lo0). It will eventually
// be removed once we have actual implementations for all supported systems.
- cout << "Interface detection is not implemented on this Operating System yet. "
- << endl;
-
- try {
- if (if_nametoindex("lo") > 0) {
- ifaceName = "lo";
- // this is Linux-like OS
- } else if (if_nametoindex("lo0") > 0) {
- ifaceName = "lo0";
- // this is BSD-like OS
- } else {
- // we give up. What OS is this, anyway? Solaris? Hurd?
- isc_throw(NotImplemented,
- "Interface detection on this OS is not supported.");
- }
-
- Iface iface(ifaceName, if_nametoindex(ifaceName.c_str()));
- iface.flag_up_ = true;
- iface.flag_running_ = true;
-
- // Note that we claim that this is not a loopback. iface_mgr tries to open a
- // socket on all interaces that are up, running and not loopback. As this is
- // the only interface we were able to detect, let's pretend this is a normal
- // interface.
- iface.flag_loopback_ = false;
- iface.flag_multicast_ = true;
- iface.flag_broadcast_ = true;
- iface.setHWType(HWTYPE_ETHERNET);
-
- iface.addAddress(IOAddress(v4addr));
- iface.addAddress(IOAddress(v6addr));
- addInterface(iface);
-
- cout << "Detected interface " << ifaceName << "/" << v4addr << "/"
- << v6addr << endl;
- } catch (const std::exception& ex) {
- // TODO: deallocate whatever memory we used
- // not that important, since this function is going to be
- // thrown away as soon as we get proper interface detection
- // implemented
-
- // TODO Do LOG_FATAL here
- std::cerr << "Interface detection failed." << std::endl;
- throw;
+ if (if_nametoindex("lo") > 0) {
+ ifaceName = "lo";
+ // this is Linux-like OS
+ } else if (if_nametoindex("lo0") > 0) {
+ ifaceName = "lo0";
+ // this is BSD-like OS
+ } else {
+ // we give up. What OS is this, anyway? Solaris? Hurd?
+ isc_throw(NotImplemented,
+ "Interface detection on this OS is not supported.");
}
+
+ Iface iface(ifaceName, if_nametoindex(ifaceName.c_str()));
+ iface.flag_up_ = true;
+ iface.flag_running_ = true;
+
+ // Note that we claim that this is not a loopback. iface_mgr tries to open a
+ // socket on all interaces that are up, running and not loopback. As this is
+ // the only interface we were able to detect, let's pretend this is a normal
+ // interface.
+ iface.flag_loopback_ = false;
+ iface.flag_multicast_ = true;
+ iface.flag_broadcast_ = true;
+ iface.setHWType(HWTYPE_ETHERNET);
+
+ iface.addAddress(IOAddress(v4addr));
+ iface.addAddress(IOAddress(v6addr));
+ addInterface(iface);
}
bool IfaceMgr::openSockets4(const uint16_t port) {
@@ -221,13 +196,9 @@ bool IfaceMgr::openSockets4(const uint16_t port) {
iface != ifaces_.end();
++iface) {
- cout << "Trying opening socket on interface " << iface->getFullName() << endl;
-
if (iface->flag_loopback_ ||
!iface->flag_up_ ||
!iface->flag_running_) {
- cout << "Interface " << iface->getFullName()
- << " not suitable: is loopback, is down or not running" << endl;
continue;
}
@@ -243,15 +214,13 @@ bool IfaceMgr::openSockets4(const uint16_t port) {
sock = openSocket(iface->getName(), *addr, port);
if (sock < 0) {
- cout << "Failed to open unicast socket." << endl;
- return (false);
+ isc_throw(SocketConfigError, "failed to open unicast socket");
}
count++;
}
}
return (count > 0);
-
}
bool IfaceMgr::openSockets6(const uint16_t port) {
@@ -280,8 +249,7 @@ bool IfaceMgr::openSockets6(const uint16_t port) {
sock = openSocket(iface->getName(), *addr, port);
if (sock < 0) {
- cout << "Failed to open unicast socket." << endl;
- return (false);
+ isc_throw(SocketConfigError, "failed to open unicast socket");
}
// Binding socket to unicast address and then joining multicast group
@@ -290,7 +258,8 @@ bool IfaceMgr::openSockets6(const uint16_t port) {
if ( !joinMulticast(sock, iface->getName(),
string(ALL_DHCP_RELAY_AGENTS_AND_SERVERS))) {
close(sock);
- isc_throw(Unexpected, "Failed to join " << ALL_DHCP_RELAY_AGENTS_AND_SERVERS
+ isc_throw(SocketConfigError, "Failed to join "
+ << ALL_DHCP_RELAY_AGENTS_AND_SERVERS
<< " multicast group.");
}
@@ -305,7 +274,7 @@ bool IfaceMgr::openSockets6(const uint16_t port) {
IOAddress(ALL_DHCP_RELAY_AGENTS_AND_SERVERS),
port);
if (sock2 < 0) {
- isc_throw(Unexpected, "Failed to open multicast socket on "
+ isc_throw(SocketConfigError, "Failed to open multicast socket on "
<< " interface " << iface->getFullName());
iface->delSocket(sock); // delete previously opened socket
}
@@ -418,7 +387,7 @@ int IfaceMgr::openSocketFromIface(const std::string& ifname,
family_name = "AF_INET6";
}
// We did not find address on the interface.
- isc_throw(BadValue, "There is no address for interface: "
+ isc_throw(SocketConfigError, "There is no address for interface: "
<< ifname << ", port: " << port << ", address "
" family: " << family_name);
}
@@ -460,9 +429,13 @@ int IfaceMgr::openSocketFromAddress(const IOAddress& addr,
int IfaceMgr::openSocketFromRemoteAddress(const IOAddress& remote_addr,
const uint16_t port) {
- // Get local address to be used to connect to remote location.
- IOAddress local_address(getLocalAddress(remote_addr, port).getAddress());
- return openSocketFromAddress(local_address, port);
+ try {
+ // Get local address to be used to connect to remote location.
+ IOAddress local_address(getLocalAddress(remote_addr, port).getAddress());
+ return openSocketFromAddress(local_address, port);
+ } catch (const Exception& e) {
+ isc_throw(SocketConfigError, e.what());
+ }
}
isc::asiolink::IOAddress
@@ -483,7 +456,7 @@ IfaceMgr::getLocalAddress(const IOAddress& remote_addr, const uint16_t port) {
// If remote address is broadcast address we have to
// allow this on the socket.
if (remote_addr.getAddress().is_v4() &&
- (remote_addr == IOAddress("255.255.255.255"))) {
+ (remote_addr == IOAddress(DHCP_IPV4_BROADCAST_ADDRESS))) {
// Socket has to be open prior to setting the broadcast
// option. Otherwise set_option will complain about
// bad file descriptor.
@@ -506,7 +479,7 @@ IfaceMgr::getLocalAddress(const IOAddress& remote_addr, const uint16_t port) {
sock.connect(remote_endpoint->getASIOEndpoint(), err_code);
if (err_code) {
sock.close();
- isc_throw(Unexpected,"failed to connect to remote endpoint.");
+ isc_throw(Unexpected, "failed to connect to remote endpoint.");
}
// Once we are connected socket object holds local endpoint.
@@ -523,9 +496,6 @@ IfaceMgr::getLocalAddress(const IOAddress& remote_addr, const uint16_t port) {
int IfaceMgr::openSocket4(Iface& iface, const IOAddress& addr, uint16_t port) {
- cout << "Creating UDP4 socket on " << iface.getFullName()
- << " " << addr.toText() << "/port=" << port << endl;
-
struct sockaddr_in addr4;
memset(&addr4, 0, sizeof(sockaddr));
addr4.sin_family = AF_INET;
@@ -537,12 +507,12 @@ int IfaceMgr::openSocket4(Iface& iface, const IOAddress& addr, uint16_t port) {
int sock = socket(AF_INET, SOCK_DGRAM, 0);
if (sock < 0) {
- isc_throw(Unexpected, "Failed to create UDP6 socket.");
+ isc_throw(SocketConfigError, "Failed to create UDP6 socket.");
}
if (bind(sock, (struct sockaddr *)&addr4, sizeof(addr4)) < 0) {
close(sock);
- isc_throw(Unexpected, "Failed to bind socket " << sock << " to " << addr.toText()
+ isc_throw(SocketConfigError, "Failed to bind socket " << sock << " to " << addr.toText()
<< "/port=" << port);
}
@@ -552,13 +522,10 @@ int IfaceMgr::openSocket4(Iface& iface, const IOAddress& addr, uint16_t port) {
int flag = 1;
if (setsockopt(sock, IPPROTO_IP, IP_PKTINFO, &flag, sizeof(flag)) != 0) {
close(sock);
- isc_throw(Unexpected, "setsockopt: IP_PKTINFO: failed.");
+ isc_throw(SocketConfigError, "setsockopt: IP_PKTINFO: failed.");
}
#endif
- cout << "Created socket " << sock << " on " << iface.getName() << "/" <<
- addr.toText() << "/port=" << port << endl;
-
SocketInfo info(sock, addr, port);
iface.addSocket(info);
@@ -567,9 +534,6 @@ int IfaceMgr::openSocket4(Iface& iface, const IOAddress& addr, uint16_t port) {
int IfaceMgr::openSocket6(Iface& iface, const IOAddress& addr, uint16_t port) {
- cout << "Creating UDP6 socket on " << iface.getFullName()
- << " " << addr.toText() << "/port=" << port << endl;
-
struct sockaddr_in6 addr6;
memset(&addr6, 0, sizeof(addr6));
addr6.sin6_family = AF_INET6;
@@ -590,7 +554,7 @@ int IfaceMgr::openSocket6(Iface& iface, const IOAddress& addr, uint16_t port) {
// make a socket
int sock = socket(AF_INET6, SOCK_DGRAM, 0);
if (sock < 0) {
- isc_throw(Unexpected, "Failed to create UDP6 socket.");
+ isc_throw(SocketConfigError, "Failed to create UDP6 socket.");
}
// Set the REUSEADDR option so that we don't fail to start if
@@ -599,12 +563,12 @@ int IfaceMgr::openSocket6(Iface& iface, const IOAddress& addr, uint16_t port) {
if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
(char *)&flag, sizeof(flag)) < 0) {
close(sock);
- isc_throw(Unexpected, "Can't set SO_REUSEADDR option on dhcpv6 socket.");
+ isc_throw(SocketConfigError, "Can't set SO_REUSEADDR option on dhcpv6 socket.");
}
if (bind(sock, (struct sockaddr *)&addr6, sizeof(addr6)) < 0) {
close(sock);
- isc_throw(Unexpected, "Failed to bind socket " << sock << " to " << addr.toText()
+ isc_throw(SocketConfigError, "Failed to bind socket " << sock << " to " << addr.toText()
<< "/port=" << port);
}
#ifdef IPV6_RECVPKTINFO
@@ -612,14 +576,14 @@ int IfaceMgr::openSocket6(Iface& iface, const IOAddress& addr, uint16_t port) {
if (setsockopt(sock, IPPROTO_IPV6, IPV6_RECVPKTINFO,
&flag, sizeof(flag)) != 0) {
close(sock);
- isc_throw(Unexpected, "setsockopt: IPV6_RECVPKTINFO failed.");
+ isc_throw(SocketConfigError, "setsockopt: IPV6_RECVPKTINFO failed.");
}
#else
// RFC2292 - an old way
if (setsockopt(sock, IPPROTO_IPV6, IPV6_PKTINFO,
&flag, sizeof(flag)) != 0) {
close(sock);
- isc_throw(Unexpected, "setsockopt: IPV6_PKTINFO: failed.");
+ isc_throw(SocketConfigError, "setsockopt: IPV6_PKTINFO: failed.");
}
#endif
@@ -632,14 +596,11 @@ int IfaceMgr::openSocket6(Iface& iface, const IOAddress& addr, uint16_t port) {
if ( !joinMulticast( sock, iface.getName(),
string(ALL_DHCP_RELAY_AGENTS_AND_SERVERS) ) ) {
close(sock);
- isc_throw(Unexpected, "Failed to join " << ALL_DHCP_RELAY_AGENTS_AND_SERVERS
+ isc_throw(SocketConfigError, "Failed to join " << ALL_DHCP_RELAY_AGENTS_AND_SERVERS
<< " multicast group.");
}
}
- cout << "Created socket " << sock << " on " << iface.getName() << "/" <<
- addr.toText() << "/port=" << port << endl;
-
SocketInfo info(sock, addr, port);
iface.addSocket(info);
@@ -654,20 +615,15 @@ const std::string & mcast) {
if (inet_pton(AF_INET6, mcast.c_str(),
&mreq.ipv6mr_multiaddr) <= 0) {
- cout << "Failed to convert " << ifname
- << " to IPv6 multicast address." << endl;
return (false);
}
mreq.ipv6mr_interface = if_nametoindex(ifname.c_str());
if (setsockopt(sock, IPPROTO_IPV6, IPV6_JOIN_GROUP,
&mreq, sizeof(mreq)) < 0) {
- cout << "Failed to join " << mcast << " multicast group." << endl;
return (false);
}
- cout << "Joined multicast " << mcast << " group." << endl;
-
return (true);
}
@@ -746,13 +702,8 @@ IfaceMgr::send(const Pkt6Ptr& pkt) {
result = sendmsg(getSocket(*pkt), &m, 0);
if (result < 0) {
- isc_throw(Unexpected, "Pkt6 send failed: sendmsg() returned " << result);
+ isc_throw(SocketWriteError, "Pkt6 send failed: sendmsg() returned " << result);
}
- cout << "Sent " << pkt->getBuffer().getLength() << " bytes over socket " << getSocket(*pkt)
- << " on " << iface->getFullName() << " interface: "
- << " dst=[" << pkt->getRemoteAddr().toText() << "]:" << pkt->getRemotePort()
- << ", src=" << pkt->getLocalAddr().toText() << "]:" << pkt->getLocalPort()
- << endl;
return (result);
}
@@ -797,24 +748,13 @@ IfaceMgr::send(const Pkt4Ptr& pkt)
// call OS-specific routines (like setting interface index)
os_send4(m, control_buf_, control_buf_len_, pkt);
- cout << "Trying to send " << pkt->getBuffer().getLength() << " bytes to "
- << pkt->getRemoteAddr().toText() << ":" << pkt->getRemotePort()
- << " over socket " << getSocket(*pkt) << " on interface "
- << getIface(pkt->getIface())->getFullName() << endl;
-
pkt->updateTimestamp();
int result = sendmsg(getSocket(*pkt), &m, 0);
if (result < 0) {
- isc_throw(Unexpected, "Pkt4 send failed.");
+ isc_throw(SocketWriteError, "pkt4 send failed");
}
- cout << "Sent " << pkt->getBuffer().getLength() << " bytes over socket " << getSocket(*pkt)
- << " on " << iface->getFullName() << " interface: "
- << " dst=" << pkt->getRemoteAddr().toText() << ":" << pkt->getRemotePort()
- << ", src=" << pkt->getLocalAddr().toText() << ":" << pkt->getLocalPort()
- << endl;
-
return (result);
}
@@ -869,27 +809,18 @@ IfaceMgr::receive4(uint32_t timeout_sec, uint32_t timeout_usec /* = 0 */) {
select_timeout.tv_sec = timeout_sec;
select_timeout.tv_usec = timeout_usec;
- cout << "Trying to receive data on sockets: " << names.str()
- << ". Timeout is " << timeout_sec << "." << setw(6) << setfill('0')
- << timeout_usec << " seconds." << endl;
int result = select(maxfd + 1, &sockets, NULL, NULL, &select_timeout);
- cout << "select returned " << result << endl;
if (result == 0) {
// nothing received and timeout has been reached
return (Pkt4Ptr()); // NULL
} else if (result < 0) {
- cout << "Socket read error: " << strerror(errno) << endl;
-
- /// @todo: perhaps throw here?
- return (Pkt4Ptr()); // NULL
+ isc_throw(SocketReadError, strerror(errno));
}
// Let's find out which socket has the data
if ((session_socket_ != INVALID_SOCKET) && (FD_ISSET(session_socket_, &sockets))) {
// something received over session socket
- cout << "BIND10 command or config available over session socket." << endl;
-
if (session_callback_) {
// in theory we could call io_service.run_one() here, instead of
// implementing callback mechanism, but that would introduce
@@ -918,14 +849,9 @@ IfaceMgr::receive4(uint32_t timeout_sec, uint32_t timeout_usec /* = 0 */) {
}
if (!candidate) {
- cout << "Received data over unknown socket." << endl;
- return (Pkt4Ptr()); // NULL
+ isc_throw(SocketReadError, "received data over unknown socket");
}
- cout << "Trying to receive over UDP4 socket " << candidate->sockfd_ << " bound to "
- << candidate->addr_.toText() << "/port=" << candidate->port_ << " on "
- << iface->getFullName() << endl;
-
// Now we have a socket, let's get some data from it!
struct sockaddr_in from_addr;
uint8_t buf[RCVBUFSIZE];
@@ -958,8 +884,7 @@ IfaceMgr::receive4(uint32_t timeout_sec, uint32_t timeout_usec /* = 0 */) {
result = recvmsg(candidate->sockfd_, &m, 0);
if (result < 0) {
- cout << "Failed to receive UDP4 data." << endl;
- return (Pkt4Ptr()); // NULL
+ isc_throw(SocketReadError, "failed to receive UDP4 data");
}
// We have all data let's create Pkt4 object.
@@ -982,15 +907,9 @@ IfaceMgr::receive4(uint32_t timeout_sec, uint32_t timeout_usec /* = 0 */) {
pkt->setLocalPort(candidate->port_);
if (!os_receive4(m, pkt)) {
- cout << "Unable to find pktinfo" << endl;
- return (boost::shared_ptr<Pkt4>()); // NULL
+ isc_throw(SocketReadError, "unable to find pktinfo");
}
- cout << "Received " << result << " bytes from " << from.toText()
- << "/port=" << from_port
- << " sent to " << pkt->getLocalAddr().toText() << " over interface "
- << iface->getFullName() << endl;
-
return (pkt);
}
@@ -1039,10 +958,6 @@ Pkt6Ptr IfaceMgr::receive6(uint32_t timeout_sec, uint32_t timeout_usec /* = 0 */
names << session_socket_ << "(session)";
}
- cout << "Trying to receive data on sockets: " << names.str()
- << ". Timeout is " << timeout_sec << "." << setw(6) << setfill('0')
- << timeout_usec << " seconds." << endl;
-
struct timeval select_timeout;
select_timeout.tv_sec = timeout_sec;
select_timeout.tv_usec = timeout_usec;
@@ -1053,17 +968,12 @@ Pkt6Ptr IfaceMgr::receive6(uint32_t timeout_sec, uint32_t timeout_usec /* = 0 */
// nothing received and timeout has been reached
return (Pkt6Ptr()); // NULL
} else if (result < 0) {
- cout << "Socket read error: " << strerror(errno) << endl;
-
- /// @todo: perhaps throw here?
- return (Pkt6Ptr()); // NULL
+ isc_throw(SocketReadError, strerror(errno));
}
// Let's find out which socket has the data
if ((session_socket_ != INVALID_SOCKET) && (FD_ISSET(session_socket_, &sockets))) {
// something received over session socket
- cout << "BIND10 command or config available over session socket." << endl;
-
if (session_callback_) {
// in theory we could call io_service.run_one() here, instead of
// implementing callback mechanism, but that would introduce
@@ -1092,14 +1002,9 @@ Pkt6Ptr IfaceMgr::receive6(uint32_t timeout_sec, uint32_t timeout_usec /* = 0 */
}
if (!candidate) {
- cout << "Received data over unknown socket." << endl;
- return (Pkt6Ptr()); // NULL
+ isc_throw(SocketReadError, "received data over unknown socket");
}
- cout << "Trying to receive over UDP6 socket " << candidate->sockfd_ << " bound to "
- << candidate->addr_.toText() << "/port=" << candidate->port_ << " on "
- << iface->getFullName() << endl;
-
// Now we have a socket, let's get some data from it!
uint8_t buf[RCVBUFSIZE];
memset(&control_buf_[0], 0, control_buf_len_);
@@ -1163,12 +1068,10 @@ Pkt6Ptr IfaceMgr::receive6(uint32_t timeout_sec, uint32_t timeout_usec /* = 0 */
cmsg = CMSG_NXTHDR(&m, cmsg);
}
if (!found_pktinfo) {
- cout << "Unable to find pktinfo" << endl;
- return (Pkt6Ptr()); // NULL
+ isc_throw(SocketReadError, "unable to find pktinfo");
}
} else {
- cout << "Failed to receive data." << endl;
- return (Pkt6Ptr()); // NULL
+ isc_throw(SocketReadError, "failed to receive data");
}
// Let's create a packet.
@@ -1176,8 +1079,7 @@ Pkt6Ptr IfaceMgr::receive6(uint32_t timeout_sec, uint32_t timeout_usec /* = 0 */
try {
pkt = Pkt6Ptr(new Pkt6(buf, result));
} catch (const std::exception& ex) {
- cout << "Failed to create new packet." << endl;
- return (Pkt6Ptr()); // NULL
+ isc_throw(SocketReadError, "failed to create new packet");
}
pkt->updateTimestamp();
@@ -1193,18 +1095,10 @@ Pkt6Ptr IfaceMgr::receive6(uint32_t timeout_sec, uint32_t timeout_usec /* = 0 */
if (received) {
pkt->setIface(received->getName());
} else {
- cout << "Received packet over unknown interface (ifindex="
- << pkt->getIndex() << ")." << endl;
- return (boost::shared_ptr<Pkt6>()); // NULL
+ isc_throw(SocketReadError, "received packet over unknown interface"
+ << "(ifindex=" << pkt->getIndex() << ")");
}
- /// @todo: Move this to LOG_DEBUG
- cout << "Received " << pkt->getBuffer().getLength() << " bytes over "
- << pkt->getIface() << "/" << pkt->getIndex() << " interface: "
- << " src=" << pkt->getRemoteAddr().toText()
- << ", dst=" << pkt->getLocalAddr().toText()
- << endl;
-
return (pkt);
}
diff --git a/src/lib/dhcp/iface_mgr.h b/src/lib/dhcp/iface_mgr.h
index 98ce672..a7b9a78 100644
--- a/src/lib/dhcp/iface_mgr.h
+++ b/src/lib/dhcp/iface_mgr.h
@@ -28,6 +28,38 @@
namespace isc {
namespace dhcp {
+
+/// @brief IfaceMgr exception thrown thrown when interface detection fails.
+class IfaceDetectError : public Exception {
+public:
+ IfaceDetectError(const char* file, size_t line, const char* what) :
+ isc::Exception(file, line, what) { };
+};
+
+/// @brief IfaceMgr exception thrown thrown when socket opening
+/// or configuration failed.
+class SocketConfigError : public Exception {
+public:
+ SocketConfigError(const char* file, size_t line, const char* what) :
+ isc::Exception(file, line, what) { };
+};
+
+/// @brief IfaceMgr exception thrown thrown when error occured during
+/// reading data from socket.
+class SocketReadError : public Exception {
+public:
+ SocketReadError(const char* file, size_t line, const char* what) :
+ isc::Exception(file, line, what) { };
+};
+
+/// @brief IfaceMgr exception thrown thrown when error occured during
+/// sedning data through socket.
+class SocketWriteError : public Exception {
+public:
+ SocketWriteError(const char* file, size_t line, const char* what) :
+ isc::Exception(file, line, what) { };
+};
+
/// @brief handles network interfaces, transmission and reception
///
/// IfaceMgr is an interface manager class that detects available network
@@ -340,6 +372,8 @@ public:
///
/// @param pkt packet to be sent
///
+ /// @throw isc::BadValue if invalid interface specified in the packet.
+ /// @throw isc::dhcp::SocketWriteError if sendmsg() failed to send packet.
/// @return true if sending was successful
bool send(const Pkt6Ptr& pkt);
@@ -351,6 +385,8 @@ public:
///
/// @param pkt a packet to be sent
///
+ /// @throw isc::BadValue if invalid interface specified in the packet.
+ /// @throw isc::dhcp::SocketWriteError if sendmsg() failed to send packet.
/// @return true if sending was successful
bool send(const Pkt4Ptr& pkt);
@@ -369,6 +405,7 @@ public:
/// (in microseconds)
///
/// @throw isc::BadValue if timeout_usec is greater than one million
+ /// @throw isc::dhcp::SocketReadError if error occured when receiving a packet.
/// @return Pkt6 object representing received packet (or NULL)
Pkt6Ptr receive6(uint32_t timeout_sec, uint32_t timeout_usec = 0);
@@ -383,6 +420,7 @@ public:
/// (in microseconds)
///
/// @throw isc::BadValue if timeout_usec is greater than one million
+ /// @throw isc::dhcp::SocketReadError if error occured when receiving a packet.
/// @return Pkt4 object representing received packet (or NULL)
Pkt4Ptr receive4(uint32_t timeout_sec, uint32_t timeout_usec = 0);
@@ -460,6 +498,7 @@ public:
///
/// @param port specifies port number (usually DHCP6_SERVER_PORT)
///
+ /// @throw SocketOpenFailure if tried and failed to open socket.
/// @return true if any sockets were open
bool openSockets6(const uint16_t port = DHCP6_SERVER_PORT);
@@ -472,6 +511,7 @@ public:
///
/// @param port specifies port number (usually DHCP4_SERVER_PORT)
///
+ /// @throw SocketOpenFailure if tried and failed to open socket.
/// @return true if any sockets were open
bool openSockets4(const uint16_t port = DHCP4_SERVER_PORT);
diff --git a/src/lib/dhcp/iface_mgr_linux.cc b/src/lib/dhcp/iface_mgr_linux.cc
index 3c62d72..189fe90 100644
--- a/src/lib/dhcp/iface_mgr_linux.cc
+++ b/src/lib/dhcp/iface_mgr_linux.cc
@@ -417,8 +417,6 @@ namespace dhcp {
/// Uses the socket-based netlink protocol to retrieve the list of interfaces
/// from the Linux kernel.
void IfaceMgr::detectIfaces() {
- cout << "Linux: detecting interfaces." << endl;
-
// Copies of netlink messages about links will be stored here.
Netlink::NetlinkMessages link_info;
@@ -495,8 +493,6 @@ void IfaceMgr::detectIfaces() {
nl.release_list(link_info);
nl.release_list(addr_info);
-
- printIfaces();
}
/// @brief sets flag_*_ fields.
@@ -558,10 +554,7 @@ bool IfaceMgr::os_receive4(struct msghdr& m, Pkt4Ptr& pkt) {
// broadcast. This will return broadcast address, not
// the address we are bound to.
- // IOAddress tmp(htonl(pktinfo->ipi_spec_dst.s_addr));
- // cout << "The other addr is: " << tmp.toText() << endl;
-
- // Perhaps we should uncomment this:
+ // XXX: Perhaps we should uncomment this:
// to_addr = pktinfo->ipi_spec_dst;
}
cmsg = CMSG_NXTHDR(&m, cmsg);
diff --git a/src/lib/dhcp/libdhcp++.cc b/src/lib/dhcp/libdhcp++.cc
index 22cd47b..12e1bbd 100644
--- a/src/lib/dhcp/libdhcp++.cc
+++ b/src/lib/dhcp/libdhcp++.cc
@@ -72,26 +72,23 @@ size_t LibDHCP::unpackOptions6(const OptionBuffer& buf,
offset += 2;
if (offset + opt_len > end) {
- cout << "Option " << opt_type << " truncated." << endl;
+ // @todo: consider throwing exception here.
return (offset);
}
OptionPtr opt;
switch (opt_type) {
case D6O_IA_NA:
case D6O_IA_PD:
- // cout << "Creating Option6IA" << endl;
opt = OptionPtr(new Option6IA(opt_type,
buf.begin() + offset,
buf.begin() + offset + opt_len));
break;
case D6O_IAADDR:
- // cout << "Creating Option6IAAddr" << endl;
opt = OptionPtr(new Option6IAAddr(opt_type,
buf.begin() + offset,
buf.begin() + offset + opt_len));
break;
default:
- // cout << "Creating Option" << endl;
opt = OptionPtr(new Option(Option::V6,
opt_type,
buf.begin() + offset,
@@ -151,15 +148,9 @@ size_t LibDHCP::unpackOptions4(const OptionBuffer& buf,
void LibDHCP::packOptions6(isc::util::OutputBuffer &buf,
const isc::dhcp::Option::OptionCollection& options) {
- try {
- for (Option::OptionCollection::const_iterator it = options.begin();
- it != options.end(); ++it) {
- it->second->pack(buf);
- }
- }
- catch (const Exception&) {
- cout << "Packet build failed (Option build failed)." << endl;
- throw;
+ for (Option::OptionCollection::const_iterator it = options.begin();
+ it != options.end(); ++it) {
+ it->second->pack(buf);
}
}
diff --git a/src/lib/dhcp/pkt4.cc b/src/lib/dhcp/pkt4.cc
index 2c3f1eb..405277d 100644
--- a/src/lib/dhcp/pkt4.cc
+++ b/src/lib/dhcp/pkt4.cc
@@ -197,8 +197,6 @@ void Pkt4::check() {
}
void Pkt4::repack() {
- cout << "Convering RX packet to TX packet: " << data_.size() << " bytes." << endl;
-
bufferOut_.writeData(&data_[0], data_.size());
}
diff --git a/src/lib/dhcp/pkt6.cc b/src/lib/dhcp/pkt6.cc
index e869c7b..330c97f 100644
--- a/src/lib/dhcp/pkt6.cc
+++ b/src/lib/dhcp/pkt6.cc
@@ -100,7 +100,7 @@ Pkt6::packUDP() {
LibDHCP::packOptions6(bufferOut_, options_);
}
catch (const Exception& e) {
- cout << "Packet build failed:" << e.what() << endl;
+ /// @todo: throw exception here once we turn this function to void.
return (false);
}
return (true);
@@ -129,8 +129,8 @@ Pkt6::unpack() {
bool
Pkt6::unpackUDP() {
if (data_.size() < 4) {
- std::cout << "DHCPv6 packet truncated. Only " << data_.size()
- << " bytes. Need at least 4." << std::endl;
+ // @todo: throw exception here informing that packet is truncated
+ // once we turn this function to void.
return (false);
}
msg_type_ = data_[0];
@@ -143,7 +143,7 @@ Pkt6::unpackUDP() {
LibDHCP::unpackOptions6(opt_buffer, options_);
} catch (const Exception& e) {
- cout << "Packet parsing failed:" << e.what() << endl;
+ // @todo: throw exception here once we turn this function to void.
return (false);
}
return (true);
@@ -197,8 +197,6 @@ Pkt6::delOption(uint16_t type) {
}
void Pkt6::repack() {
- cout << "Convering RX packet to TX packet: " << data_.size() << " bytes." << endl;
-
bufferOut_.writeData(&data_[0], data_.size());
}
diff --git a/src/lib/dhcp/tests/iface_mgr_unittest.cc b/src/lib/dhcp/tests/iface_mgr_unittest.cc
index 4fe7e23..2c7b86b 100644
--- a/src/lib/dhcp/tests/iface_mgr_unittest.cc
+++ b/src/lib/dhcp/tests/iface_mgr_unittest.cc
@@ -82,10 +82,8 @@ TEST_F(IfaceMgrTest, loDetect) {
// it will go away as soon as proper interface detection
// is implemented
if (if_nametoindex("lo") > 0) {
- cout << "This is Linux, using lo as loopback." << endl;
snprintf(LOOPBACK, BUF_SIZE - 1, "lo");
} else if (if_nametoindex("lo0") > 0) {
- cout << "This is BSD, using lo0 as loopback." << endl;
snprintf(LOOPBACK, BUF_SIZE - 1, "lo0");
} else {
cout << "Failed to detect loopback interface. Neither "
@@ -421,7 +419,7 @@ TEST_F(IfaceMgrTest, sockets6) {
// testing socket operation in a portable way is tricky
// without interface detection implemented
- NakedIfaceMgr* ifacemgr = new NakedIfaceMgr();
+ boost::scoped_ptr<NakedIfaceMgr> ifacemgr(new NakedIfaceMgr());
IOAddress loAddr("::1");
@@ -441,10 +439,25 @@ TEST_F(IfaceMgrTest, sockets6) {
// removed code for binding socket twice to the same address/port
// as it caused problems on some platforms (e.g. Mac OS X)
- close(socket1);
- close(socket2);
+ // Close sockets here because the following tests will want to
+ // open sockets on the same ports.
+ ifacemgr->closeSockets();
- delete ifacemgr;
+ // Use address that is not assigned to LOOPBACK iface.
+ IOAddress invalidAddr("::2");
+ EXPECT_THROW(
+ ifacemgr->openSocket(LOOPBACK, invalidAddr, 10547),
+ SocketConfigError
+ );
+
+ // Use non-existing interface name.
+ EXPECT_THROW(
+ ifacemgr->openSocket("non_existing_interface", loAddr, 10548),
+ BadValue
+ );
+
+ // Do not call closeSockets() because it is called by IfaceMgr's
+ // virtual destructor.
}
TEST_F(IfaceMgrTest, socketsFromIface) {
@@ -468,6 +481,18 @@ TEST_F(IfaceMgrTest, socketsFromIface) {
EXPECT_GT(socket2, 0);
close(socket2);
+ // Close sockets here because the following tests will want to
+ // open sockets on the same ports.
+ ifacemgr->closeSockets();
+
+ // Use invalid interface name.
+ EXPECT_THROW(
+ ifacemgr->openSocketFromIface("non_existing_interface", PORT1, AF_INET),
+ BadValue
+ );
+
+ // Do not call closeSockets() because it is called by IfaceMgr's
+ // virtual destructor.
}
@@ -482,7 +507,6 @@ TEST_F(IfaceMgrTest, socketsFromAddress) {
);
// socket descriptor must be positive integer
EXPECT_GT(socket1, 0);
- close(socket1);
// Open v4 socket on loopback interface and bind to different port
int socket2 = 0;
@@ -492,7 +516,19 @@ TEST_F(IfaceMgrTest, socketsFromAddress) {
);
// socket descriptor must be positive integer
EXPECT_GT(socket2, 0);
- close(socket2);
+
+ // Close sockets here because the following tests will want to
+ // open sockets on the same ports.
+ ifacemgr->closeSockets();
+
+ // Use non-existing address.
+ IOAddress invalidAddr("1.2.3.4");
+ EXPECT_THROW(
+ ifacemgr->openSocketFromAddress(invalidAddr, PORT1), BadValue
+ );
+
+ // Do not call closeSockets() because it is called by IfaceMgr's
+ // virtual destructor.
}
TEST_F(IfaceMgrTest, socketsFromRemoteAddress) {
@@ -507,7 +543,6 @@ TEST_F(IfaceMgrTest, socketsFromRemoteAddress) {
socket1 = ifacemgr->openSocketFromRemoteAddress(loAddr6, PORT1);
);
EXPECT_GT(socket1, 0);
- close(socket1);
// Open v4 socket to connect to remote address.
int socket2 = 0;
@@ -516,7 +551,10 @@ TEST_F(IfaceMgrTest, socketsFromRemoteAddress) {
socket2 = ifacemgr->openSocketFromRemoteAddress(loAddr, PORT2);
);
EXPECT_GT(socket2, 0);
- close(socket2);
+
+ // Close sockets here because the following tests will want to
+ // open sockets on the same ports.
+ ifacemgr->closeSockets();
// The following test is currently disabled for OSes other than
// Linux because interface detection is not implemented on them.
@@ -530,8 +568,10 @@ TEST_F(IfaceMgrTest, socketsFromRemoteAddress) {
socket3 = ifacemgr->openSocketFromRemoteAddress(bcastAddr, PORT2);
);
EXPECT_GT(socket3, 0);
- close(socket3);
#endif
+
+ // Do not call closeSockets() because it is called by IfaceMgr's
+ // virtual destructor.
}
// TODO: disabled due to other naming on various systems
@@ -570,7 +610,7 @@ TEST_F(IfaceMgrTest, sendReceive6) {
// testing socket operation in a portable way is tricky
// without interface detection implemented
- NakedIfaceMgr* ifacemgr = new NakedIfaceMgr();
+ boost::scoped_ptr<NakedIfaceMgr> ifacemgr(new NakedIfaceMgr());
// let's assume that every supported OS have lo interface
IOAddress loAddr("::1");
@@ -619,7 +659,11 @@ TEST_F(IfaceMgrTest, sendReceive6) {
// we should accept both values as source ports.
EXPECT_TRUE((rcvPkt->getRemotePort() == 10546) || (rcvPkt->getRemotePort() == 10547));
- delete ifacemgr;
+ // try to send/receive data over the closed socket. Closed socket's descriptor is
+ // still being hold by IfaceMgr which will try to use it to receive data.
+ close(socket1);
+ EXPECT_THROW(ifacemgr->receive6(10), SocketReadError);
+ EXPECT_THROW(ifacemgr->send(sendPkt), SocketWriteError);
}
TEST_F(IfaceMgrTest, sendReceive4) {
@@ -627,7 +671,7 @@ TEST_F(IfaceMgrTest, sendReceive4) {
// testing socket operation in a portable way is tricky
// without interface detection implemented
- NakedIfaceMgr* ifacemgr = new NakedIfaceMgr();
+ boost::scoped_ptr<NakedIfaceMgr> ifacemgr(new NakedIfaceMgr());
// let's assume that every supported OS have lo interface
IOAddress loAddr("127.0.0.1");
@@ -675,8 +719,7 @@ TEST_F(IfaceMgrTest, sendReceive4) {
EXPECT_EQ(true, ifacemgr->send(sendPkt));
- rcvPkt = ifacemgr->receive4(10);
-
+ ASSERT_NO_THROW(rcvPkt = ifacemgr->receive4(10));
ASSERT_TRUE(rcvPkt); // received our own packet
ASSERT_NO_THROW(
@@ -710,7 +753,11 @@ TEST_F(IfaceMgrTest, sendReceive4) {
// assume the one or the other will always be choosen for sending data. We should
// skip checking source port of sent address.
- delete ifacemgr;
+ // try to receive data over the closed socket. Closed socket's descriptor is
+ // still being hold by IfaceMgr which will try to use it to receive data.
+ close(socket1);
+ EXPECT_THROW(ifacemgr->receive4(10), SocketReadError);
+ EXPECT_THROW(ifacemgr->send(sendPkt), SocketWriteError);
}
@@ -1214,7 +1261,7 @@ TEST_F(IfaceMgrTest, controlSession) {
EXPECT_NO_THROW(ifacemgr->set_session_socket(pipefd[0], my_callback));
Pkt4Ptr pkt4;
- pkt4 = ifacemgr->receive4(1);
+ ASSERT_NO_THROW(pkt4 = ifacemgr->receive4(1));
// Our callback should not be called this time (there was no data)
EXPECT_FALSE(callback_ok);
@@ -1226,7 +1273,7 @@ TEST_F(IfaceMgrTest, controlSession) {
EXPECT_EQ(38, write(pipefd[1], "Hi, this is a message sent over a pipe", 38));
// ... and repeat
- pkt4 = ifacemgr->receive4(1);
+ ASSERT_NO_THROW(pkt4 = ifacemgr->receive4(1));
// IfaceMgr should not process control socket data as incoming packets
EXPECT_FALSE(pkt4);
diff --git a/src/lib/dns/labelsequence.cc b/src/lib/dns/labelsequence.cc
index ed23f26..f5f6a95 100644
--- a/src/lib/dns/labelsequence.cc
+++ b/src/lib/dns/labelsequence.cc
@@ -45,8 +45,7 @@ LabelSequence::LabelSequence(const void* buf) {
// Check the integrity on the offsets and the name data
const uint8_t* dp = data_;
for (size_t cur_offset = 0; cur_offset < offsets_len; ++cur_offset) {
- if (offsets_[cur_offset] > Name::MAX_LABELLEN ||
- dp - data_ != offsets_[cur_offset]) {
+ if (dp - data_ != offsets_[cur_offset] || *dp > Name::MAX_LABELLEN) {
isc_throw(BadValue,
"Broken offset or name data in serialized "
"LabelSequence data");
diff --git a/src/lib/dns/nsec3hash.cc b/src/lib/dns/nsec3hash.cc
index 159dff3..8fe3cf1 100644
--- a/src/lib/dns/nsec3hash.cc
+++ b/src/lib/dns/nsec3hash.cc
@@ -16,6 +16,7 @@
#include <cassert>
#include <cstring>
+#include <cstdlib>
#include <string>
#include <vector>
@@ -57,17 +58,31 @@ private:
public:
NSEC3HashRFC5155(uint8_t algorithm, uint16_t iterations,
- const vector<uint8_t>& salt) :
+ const uint8_t* salt_data, size_t salt_length) :
algorithm_(algorithm), iterations_(iterations),
- salt_(salt), digest_(SHA1_HASHSIZE), obuf_(Name::MAX_WIRE)
+ salt_data_(NULL), salt_length_(salt_length),
+ digest_(SHA1_HASHSIZE), obuf_(Name::MAX_WIRE)
{
if (algorithm_ != NSEC3_HASH_SHA1) {
isc_throw(UnknownNSEC3HashAlgorithm, "Unknown NSEC3 algorithm: " <<
static_cast<unsigned int>(algorithm_));
}
+
+ if (salt_length > 0) {
+ salt_data_ = static_cast<uint8_t*>(std::malloc(salt_length));
+ if (salt_data_ == NULL) {
+ throw std::bad_alloc();
+ }
+ std::memcpy(salt_data_, salt_data, salt_length);
+ }
+
SHA1Reset(&sha1_ctx_);
}
+ virtual ~NSEC3HashRFC5155() {
+ std::free(salt_data_);
+ }
+
virtual std::string calculate(const Name& name) const;
virtual bool match(const generic::NSEC3& nsec3) const;
@@ -78,7 +93,8 @@ public:
private:
const uint8_t algorithm_;
const uint16_t iterations_;
- const vector<uint8_t> salt_;
+ uint8_t* salt_data_;
+ const size_t salt_length_;
// The following members are placeholder of work place and don't hold
// any state over multiple calls so can be mutable without breaking
@@ -108,15 +124,14 @@ NSEC3HashRFC5155::calculate(const Name& name) const {
name_copy.downcase();
name_copy.toWire(obuf_);
- const uint8_t saltlen = salt_.size();
- const uint8_t* const salt = (saltlen > 0) ? &salt_[0] : NULL;
uint8_t* const digest = &digest_[0];
assert(digest_.size() == SHA1_HASHSIZE);
iterateSHA1(&sha1_ctx_, static_cast<const uint8_t*>(obuf_.getData()),
- obuf_.getLength(), salt, saltlen, digest);
+ obuf_.getLength(), salt_data_, salt_length_, digest);
for (unsigned int n = 0; n < iterations_; ++n) {
- iterateSHA1(&sha1_ctx_, digest, SHA1_HASHSIZE, salt, saltlen, digest);
+ iterateSHA1(&sha1_ctx_, digest, SHA1_HASHSIZE,
+ salt_data_, salt_length_, digest);
}
return (encodeBase32Hex(digest_));
@@ -127,8 +142,9 @@ NSEC3HashRFC5155::match(uint8_t algorithm, uint16_t iterations,
const vector<uint8_t>& salt) const
{
return (algorithm_ == algorithm && iterations_ == iterations &&
- salt_.size() == salt.size() &&
- (salt_.empty() || memcmp(&salt_[0], &salt[0], salt_.size()) == 0));
+ salt_length_ == salt.size() &&
+ ((salt_length_ == 0) ||
+ memcmp(salt_data_, &salt[0], salt_length_) == 0));
}
bool
@@ -175,15 +191,35 @@ NSEC3Hash::create(const generic::NSEC3& nsec3) {
}
NSEC3Hash*
+NSEC3Hash::create(uint8_t algorithm, uint16_t iterations,
+ const uint8_t* salt_data, size_t salt_length) {
+ return (getNSEC3HashCreator()->create(algorithm, iterations,
+ salt_data, salt_length));
+}
+
+NSEC3Hash*
DefaultNSEC3HashCreator::create(const generic::NSEC3PARAM& param) const {
+ const vector<uint8_t>& salt = param.getSalt();
return (new NSEC3HashRFC5155(param.getHashalg(), param.getIterations(),
- param.getSalt()));
+ salt.empty() ? NULL : &salt[0],
+ salt.size()));
}
NSEC3Hash*
DefaultNSEC3HashCreator::create(const generic::NSEC3& nsec3) const {
+ const vector<uint8_t>& salt = nsec3.getSalt();
return (new NSEC3HashRFC5155(nsec3.getHashalg(), nsec3.getIterations(),
- nsec3.getSalt()));
+ salt.empty() ? NULL : &salt[0],
+ salt.size()));
+}
+
+NSEC3Hash*
+DefaultNSEC3HashCreator::create(uint8_t algorithm, uint16_t iterations,
+ const uint8_t* salt_data,
+ size_t salt_length) const
+{
+ return (new NSEC3HashRFC5155(algorithm, iterations,
+ salt_data, salt_length));
}
void
diff --git a/src/lib/dns/nsec3hash.h b/src/lib/dns/nsec3hash.h
index 2056708..e082ee8 100644
--- a/src/lib/dns/nsec3hash.h
+++ b/src/lib/dns/nsec3hash.h
@@ -16,7 +16,8 @@
#define __NSEC3HASH_H 1
#include <string>
-
+#include <vector>
+#include <stdint.h>
#include <exceptions/exceptions.h>
namespace isc {
@@ -115,6 +116,16 @@ public:
/// for hash calculation from an NSEC3 RDATA object.
static NSEC3Hash* create(const rdata::generic::NSEC3& nsec3);
+ /// \brief Factory method of NSECHash from args.
+ ///
+ /// \param algorithm the NSEC3 algorithm to use; currently only 1
+ /// (SHA-1) is supported
+ /// \param iterations the number of iterations
+ /// \param salt_data the salt data as a byte array
+ /// \param salt_data_length the length of the salt data
+ static NSEC3Hash* create(uint8_t algorithm, uint16_t iterations,
+ const uint8_t* salt_data, size_t salt_length);
+
/// \brief The destructor.
virtual ~NSEC3Hash() {}
@@ -167,7 +178,7 @@ public:
/// would be an experimental extension for a newer hash algorithm or
/// implementation.
///
-/// The two main methods named \c create() correspond to the static factory
+/// The three main methods named \c create() correspond to the static factory
/// methods of \c NSEC3Hash of the same name.
///
/// By default, the library uses the \c DefaultNSEC3HashCreator creator.
@@ -210,6 +221,22 @@ public:
/// <code>NSEC3Hash::create(const rdata::generic::NSEC3& param)</code>
virtual NSEC3Hash* create(const rdata::generic::NSEC3& nsec3)
const = 0;
+
+ /// \brief Factory method of NSECHash from args.
+ ///
+ /// See
+ /// <code>NSEC3Hash::create(uint8_t algorithm, uint16_t iterations,
+ /// const uint8_t* salt_data,
+ /// size_t salt_length)</code>
+ ///
+ /// \param algorithm the NSEC3 algorithm to use; currently only 1
+ /// (SHA-1) is supported
+ /// \param iterations the number of iterations
+ /// \param salt_data the salt data as a byte array
+ /// \param salt_data_length the length of the salt data
+ virtual NSEC3Hash* create(uint8_t algorithm, uint16_t iterations,
+ const uint8_t* salt_data, size_t salt_length)
+ const = 0;
};
/// \brief The default NSEC3Hash creator.
@@ -225,6 +252,9 @@ class DefaultNSEC3HashCreator : public NSEC3HashCreator {
public:
virtual NSEC3Hash* create(const rdata::generic::NSEC3PARAM& param) const;
virtual NSEC3Hash* create(const rdata::generic::NSEC3& nsec3) const;
+ virtual NSEC3Hash* create(uint8_t algorithm, uint16_t iterations,
+ const uint8_t* salt_data,
+ size_t salt_length) const;
};
/// \brief The registrar of \c NSEC3HashCreator.
diff --git a/src/lib/dns/tests/labelsequence_unittest.cc b/src/lib/dns/tests/labelsequence_unittest.cc
index b7463b2..90d470d 100644
--- a/src/lib/dns/tests/labelsequence_unittest.cc
+++ b/src/lib/dns/tests/labelsequence_unittest.cc
@@ -12,6 +12,8 @@
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
+#include <util/buffer.h>
+
#include <dns/labelsequence.h>
#include <dns/name.h>
#include <exceptions/exceptions.h>
@@ -772,6 +774,37 @@ TEST_F(LabelSequenceTest, serialize) {
1, 0, 7, 'e', 'x', 'a', 'm', 'p', 'l', 'e' };
expected.push_back(DataPair(sizeof(expected_data5), expected_data5));
+ // Labels containing a longest possible label
+ const Name name_longlabel(std::string(63, 'x')); // 63 'x's
+ LabelSequence ls_longlabel(name_longlabel);
+ actual_labelseqs.push_back(ls_longlabel);
+ vector<uint8_t> expected_data6;
+ expected_data6.push_back(2); // 2 labels
+ expected_data6.push_back(0); // 1st offset
+ expected_data6.push_back(64); // 2nd offset
+ expected_data6.push_back(63); // 1st label length
+ expected_data6.insert(expected_data6.end(), 63, 'x'); // 1st label: 63 'x's
+ expected_data6.push_back(0); // 2nd label: trailing 0
+ expected.push_back(DataPair(expected_data6.size(), &expected_data6[0]));
+
+ // Max number of labels and longest possible name
+ EXPECT_EQ(Name::MAX_WIRE, n_maxlabel.getLength());
+ LabelSequence ls_maxlabel(n_maxlabel);
+ actual_labelseqs.push_back(ls_maxlabel);
+ vector<uint8_t> expected_data7;
+ expected_data7.push_back(Name::MAX_LABELS); // number of labels
+ for (size_t i = 0; i < Name::MAX_LABELS; ++i) {
+ expected_data7.push_back(i * 2); // each label has length and 1 byte
+ }
+ // Copy wire data of the name
+ isc::util::OutputBuffer ob(0);
+ n_maxlabel.toWire(ob);
+ expected_data7.insert(expected_data7.end(),
+ static_cast<const uint8_t*>(ob.getData()),
+ static_cast<const uint8_t*>(ob.getData()) +
+ ob.getLength());
+ expected.push_back(DataPair(expected_data7.size(), &expected_data7[0]));
+
// For each data set, serialize the labels and compare the data to the
// expected one.
vector<DataPair>::const_iterator it = expected.begin();
diff --git a/src/lib/dns/tests/nsec3hash_unittest.cc b/src/lib/dns/tests/nsec3hash_unittest.cc
index e607c74..4ef0c7b 100644
--- a/src/lib/dns/tests/nsec3hash_unittest.cc
+++ b/src/lib/dns/tests/nsec3hash_unittest.cc
@@ -20,11 +20,14 @@
#include <dns/nsec3hash.h>
#include <dns/rdataclass.h>
+#include <util/encode/hex.h>
using boost::scoped_ptr;
using namespace std;
using namespace isc::dns;
using namespace isc::dns::rdata;
+using namespace isc::util;
+using namespace isc::util::encode;
namespace {
typedef scoped_ptr<NSEC3Hash> NSEC3HashPtr;
@@ -39,7 +42,10 @@ protected:
test_hash_nsec3(NSEC3Hash::create(generic::NSEC3
("1 0 12 aabbccdd " +
string(nsec3_common))))
- {}
+ {
+ const uint8_t salt[] = {0xaa, 0xbb, 0xcc, 0xdd};
+ test_hash_args.reset(NSEC3Hash::create(1, 12, salt, sizeof(salt)));
+ }
~NSEC3HashTest() {
// Make sure we reset the hash creator to the default
@@ -53,6 +59,9 @@ protected:
// Similar to test_hash, but created from NSEC3 RR.
NSEC3HashPtr test_hash_nsec3;
+
+ // Similar to test_hash, but created from passed args.
+ NSEC3HashPtr test_hash_args;
};
TEST_F(NSEC3HashTest, unknownAlgorithm) {
@@ -65,6 +74,10 @@ TEST_F(NSEC3HashTest, unknownAlgorithm) {
generic::NSEC3("2 0 12 aabbccdd " +
string(nsec3_common)))),
UnknownNSEC3HashAlgorithm);
+
+ const uint8_t salt[] = {0xaa, 0xbb, 0xcc, 0xdd};
+ EXPECT_THROW(NSEC3HashPtr(NSEC3Hash::create(2, 12, salt, sizeof(salt))),
+ UnknownNSEC3HashAlgorithm);
}
// Common checks for NSEC3 hash calculation
@@ -90,6 +103,10 @@ TEST_F(NSEC3HashTest, calculate) {
SCOPED_TRACE("calculate check with NSEC3 based hash");
calculateCheck(*test_hash_nsec3);
}
+ {
+ SCOPED_TRACE("calculate check with args based hash");
+ calculateCheck(*test_hash_args);
+ }
// Some boundary cases: 0-iteration and empty salt. Borrowed from the
// .com zone data.
@@ -177,6 +194,11 @@ public:
}
return (new TestNSEC3Hash);
}
+ virtual NSEC3Hash* create(uint8_t, uint16_t,
+ const uint8_t*, size_t) const {
+ isc_throw(isc::Unexpected,
+ "This method is not implemented here.");
+ }
private:
DefaultNSEC3HashCreator default_creator_;
};
diff --git a/src/lib/python/isc/acl/tests/dns_test.py b/src/lib/python/isc/acl/tests/dns_test.py
index d225bee..84fead2 100644
--- a/src/lib/python/isc/acl/tests/dns_test.py
+++ b/src/lib/python/isc/acl/tests/dns_test.py
@@ -198,10 +198,6 @@ class RequestACLTest(unittest.TestCase):
self.assertRaises(LoaderError, REQUEST_LOADER.load,
[{"action": "ACCEPT", "from": 4}])
self.assertRaises(LoaderError, REQUEST_LOADER.load,
- '[{"action": "ACCEPT", "from": []}]')
- self.assertRaises(LoaderError, REQUEST_LOADER.load,
- [{"action": "ACCEPT", "from": []}])
- self.assertRaises(LoaderError, REQUEST_LOADER.load,
'[{"action": "ACCEPT", "key": 1}]')
self.assertRaises(LoaderError, REQUEST_LOADER.load,
[{"action": "ACCEPT", "key": 1}])
diff --git a/src/lib/python/isc/datasrc/tests/clientlist_test.py b/src/lib/python/isc/datasrc/tests/clientlist_test.py
index 54b20e1..ea39d4e 100644
--- a/src/lib/python/isc/datasrc/tests/clientlist_test.py
+++ b/src/lib/python/isc/datasrc/tests/clientlist_test.py
@@ -28,6 +28,16 @@ class ClientListTest(unittest.TestCase):
contain the ConfigurableClientList only.
"""
+ def tearDown(self):
+ # The unit test module could keep internal objects alive longer than
+ # we expect. But cached zone finder and cache client cannot stay
+ # longer than the originating client list. So we explicitly clean
+ # them up here. The ordering is important: clist must be destroyed
+ # last.
+ self.dsrc = None
+ self.finder = None
+ self.clist = None
+
def test_constructors(self):
"""
Test the constructor. It should accept an RRClass. Check it
@@ -50,16 +60,16 @@ class ClientListTest(unittest.TestCase):
ones are acceptend and invalid rejected. We check the changes
have effect.
"""
- clist = isc.datasrc.ConfigurableClientList(isc.dns.RRClass.IN())
+ self.clist = isc.datasrc.ConfigurableClientList(isc.dns.RRClass.IN())
# This should be NOP now
- clist.configure("[]", True)
+ self.clist.configure("[]", True)
# Check the zone is not there yet
- dsrc, finder, exact = clist.find(isc.dns.Name("example.org"))
+ dsrc, finder, exact = self.clist.find(isc.dns.Name("example.org"))
self.assertIsNone(dsrc)
self.assertIsNone(finder)
self.assertFalse(exact)
# We can use this type, as it is not loaded dynamically.
- clist.configure('''[{
+ self.clist.configure('''[{
"type": "MasterFiles",
"params": {
"example.org": "''' + TESTDATA_PATH + '''example.org.zone"
@@ -68,38 +78,39 @@ class ClientListTest(unittest.TestCase):
}]''', True)
# Check the zone is there now. Proper tests of find are in other
# test methods.
- dsrc, finder, exact = clist.find(isc.dns.Name("example.org"))
- self.assertIsNotNone(dsrc)
- self.assertTrue(isinstance(dsrc, isc.datasrc.DataSourceClient))
- self.assertIsNotNone(finder)
- self.assertTrue(isinstance(finder, isc.datasrc.ZoneFinder))
+ self.dsrc, self.finder, exact = \
+ self.clist.find(isc.dns.Name("example.org"))
+ self.assertIsNotNone(self.dsrc)
+ self.assertTrue(isinstance(self.dsrc, isc.datasrc.DataSourceClient))
+ self.assertIsNotNone(self.finder)
+ self.assertTrue(isinstance(self.finder, isc.datasrc.ZoneFinder))
self.assertTrue(exact)
- self.assertRaises(isc.datasrc.Error, clist.configure, '"bad type"',
- True)
- self.assertRaises(isc.datasrc.Error, clist.configure, '''[{
+ self.assertRaises(isc.datasrc.Error, self.clist.configure,
+ '"bad type"', True)
+ self.assertRaises(isc.datasrc.Error, self.clist.configure, '''[{
"type": "bad type"
}]''', True)
- self.assertRaises(isc.datasrc.Error, clist.configure, '''[{
+ self.assertRaises(isc.datasrc.Error, self.clist.configure, '''[{
bad JSON,
}]''', True)
- self.assertRaises(TypeError, clist.configure, [], True)
- self.assertRaises(TypeError, clist.configure, "[]")
- self.assertRaises(TypeError, clist.configure, "[]", "true")
+ self.assertRaises(TypeError, self.clist.configure, [], True)
+ self.assertRaises(TypeError, self.clist.configure, "[]")
+ self.assertRaises(TypeError, self.clist.configure, "[]", "true")
def test_find(self):
"""
Test the find accepts the right arguments, some of them can be omitted,
etc.
"""
- clist = isc.datasrc.ConfigurableClientList(isc.dns.RRClass.IN())
- clist.configure('''[{
+ self.clist = isc.datasrc.ConfigurableClientList(isc.dns.RRClass.IN())
+ self.clist.configure('''[{
"type": "MasterFiles",
"params": {
"example.org": "''' + TESTDATA_PATH + '''example.org.zone"
},
"cache-enable": true
}]''', True)
- dsrc, finder, exact = clist.find(isc.dns.Name("sub.example.org"))
+ dsrc, finder, exact = self.clist.find(isc.dns.Name("sub.example.org"))
self.assertIsNotNone(dsrc)
self.assertTrue(isinstance(dsrc, isc.datasrc.DataSourceClient))
self.assertIsNotNone(finder)
@@ -112,33 +123,33 @@ class ClientListTest(unittest.TestCase):
self.assertEqual(2, sys.getrefcount(dsrc))
# We check an exact match in test_configure already
self.assertFalse(exact)
- dsrc, finder, exact = clist.find(isc.dns.Name("sub.example.org"),
- False)
- self.assertIsNotNone(dsrc)
- self.assertTrue(isinstance(dsrc, isc.datasrc.DataSourceClient))
- self.assertIsNotNone(finder)
- self.assertTrue(isinstance(finder, isc.datasrc.ZoneFinder))
+ self.dsrc, self.finder, exact = \
+ self.clist.find(isc.dns.Name("sub.example.org"), False)
+ self.assertIsNotNone(self.dsrc)
+ self.assertTrue(isinstance(self.dsrc, isc.datasrc.DataSourceClient))
+ self.assertIsNotNone(self.finder)
+ self.assertTrue(isinstance(self.finder, isc.datasrc.ZoneFinder))
self.assertFalse(exact)
- dsrc, finder, exact = clist.find(isc.dns.Name("sub.example.org"),
- True)
- self.assertIsNone(dsrc)
- self.assertIsNone(finder)
+ self.dsrc, self.finder, exact = \
+ self.clist.find(isc.dns.Name("sub.example.org"), True)
+ self.assertIsNone(self.dsrc)
+ self.assertIsNone(self.finder)
self.assertFalse(exact)
- dsrc, finder, exact = clist.find(isc.dns.Name("sub.example.org"),
- False, False)
- self.assertIsNotNone(dsrc)
- self.assertTrue(isinstance(dsrc, isc.datasrc.DataSourceClient))
- self.assertIsNotNone(finder)
- self.assertTrue(isinstance(finder, isc.datasrc.ZoneFinder))
+ self.dsrc, self.finder, exact = \
+ self.clist.find(isc.dns.Name("sub.example.org"), False, False)
+ self.assertIsNotNone(self.dsrc)
+ self.assertTrue(isinstance(self.dsrc, isc.datasrc.DataSourceClient))
+ self.assertIsNotNone(self.finder)
+ self.assertTrue(isinstance(self.finder, isc.datasrc.ZoneFinder))
self.assertFalse(exact)
- dsrc, finder, exact = clist.find(isc.dns.Name("sub.example.org"),
- True, False)
- self.assertIsNone(dsrc)
- self.assertIsNone(finder)
+ self.dsrc, self.finder, exact = \
+ self.clist.find(isc.dns.Name("sub.example.org"), True, False)
+ self.assertIsNone(self.dsrc)
+ self.assertIsNone(self.finder)
self.assertFalse(exact)
# Some invalid inputs
- self.assertRaises(TypeError, clist.find, "example.org")
- self.assertRaises(TypeError, clist.find)
+ self.assertRaises(TypeError, self.clist.find, "example.org")
+ self.assertRaises(TypeError, self.clist.find)
if __name__ == "__main__":
isc.log.init("bind10")
diff --git a/src/lib/python/isc/ddns/session.py b/src/lib/python/isc/ddns/session.py
index 366bc8b..60834fb 100644
--- a/src/lib/python/isc/ddns/session.py
+++ b/src/lib/python/isc/ddns/session.py
@@ -199,8 +199,6 @@ class UpdateSession:
logging and access control.
- zone_config (ZoneConfig) A tentative container that encapsulates
the server's zone configuration. See zone_config.py.
- - req_data (binary) Wire format data of the request message.
- It will be used for TSIG verification if necessary.
'''
self.__message = req_message
diff --git a/tests/lettuce/features/xfrin_notify_handling.feature b/tests/lettuce/features/xfrin_notify_handling.feature
index 8a21688..c7cdd4b 100644
--- a/tests/lettuce/features/xfrin_notify_handling.feature
+++ b/tests/lettuce/features/xfrin_notify_handling.feature
@@ -34,11 +34,9 @@ Feature: Xfrin incoming notify handling
When I query statistics ixfr_running of bind10 module Xfrout with cmdctl port 47804
Then the statistics counter ixfr_running should be 0
- Then the statistics counter ixfr_running should be 0
When I query statistics axfr_running of bind10 module Xfrout with cmdctl port 47804
Then the statistics counter axfr_running should be 0
- Then the statistics counter axfr_running should be 0
When I query statistics socket of bind10 module Xfrout with cmdctl port 47804
Then the statistics counter open should be 0
@@ -127,6 +125,22 @@ Feature: Xfrin incoming notify handling
Then the statistics counter xfrrej for the zone _SERVER_ should be 0
Then the statistics counter xfrreqdone for the zone _SERVER_ should be 0
+ When I query statistics ixfr_running of bind10 module Xfrout with cmdctl port 47804
+ Then the statistics counter ixfr_running should be 0
+
+ When I query statistics axfr_running of bind10 module Xfrout with cmdctl port 47804
+ Then the statistics counter axfr_running should be 0
+
+ When I query statistics socket of bind10 module Xfrout with cmdctl port 47804
+ Then the statistics counter open should be between 0 and 1
+ Then the statistics counter openfail should be 0
+ Then the statistics counter close should be 0
+ Then the statistics counter bindfail should be 0
+ Then the statistics counter acceptfail should be 0
+ Then the statistics counter accept should be 0
+ Then the statistics counter senderr should be 0
+ Then the statistics counter recverr should be 0
+
#
# set transfer_acl rejection
# Local xfr requests from Xfrin module would be rejected here.
@@ -172,3 +186,62 @@ Feature: Xfrin incoming notify handling
Then the statistics counter xfrrej for the zone example.org. should be greater than 0
Then the statistics counter xfrreqdone for the zone _SERVER_ should be 0
Then the statistics counter xfrreqdone for the zone example.org. should be 0
+
+ When I query statistics socket of bind10 module Xfrout with cmdctl port 47804
+ Then the statistics counter open should be 1
+ Then the statistics counter openfail should be 0
+ Then the statistics counter close should be 0
+ Then the statistics counter bindfail should be 0
+ Then the statistics counter acceptfail should be 0
+ Then the statistics counter accept should be 1
+ Then the statistics counter senderr should be 0
+ Then the statistics counter recverr should be 0
+
+ #
+ # Test for unreachable slave
+ #
+ Scenario: Handle incoming notify (unreachable slave)
+ Given I have bind10 running with configuration xfrin/retransfer_master.conf with cmdctl port 47804 as master
+ And wait for master stderr message BIND10_STARTED_CC
+ And wait for master stderr message CMDCTL_STARTED
+ And wait for master stderr message AUTH_SERVER_STARTED
+ And wait for master stderr message XFROUT_STARTED
+ And wait for master stderr message ZONEMGR_STARTED
+ And wait for master stderr message STATS_STARTING
+
+ When I send bind10 with cmdctl port 47804 the command Xfrout notify example.org IN
+ Then wait for new master stderr message XFROUT_NOTIFY_COMMAND
+ Then wait for new master stderr message NOTIFY_OUT_SENDING_NOTIFY
+ Then wait for new master stderr message NOTIFY_OUT_TIMEOUT
+
+ #
+ # Test1 for Xfrout statistics
+ #
+ # check statistics change
+ #
+
+ # wait until the last stats requesting is finished
+ wait for new master stderr message STATS_SEND_STATISTICS_REQUEST
+ wait for new master stderr message XFROUT_RECEIVED_GETSTATS_COMMAND
+
+ When I query statistics zones of bind10 module Xfrout with cmdctl port 47804
+ last bindctl output should not contain "error"
+ Then the statistics counter notifyoutv4 for the zone _SERVER_ should be 0
+ Then the statistics counter notifyoutv4 for the zone example.org. should be 0
+ Then the statistics counter notifyoutv6 for the zone _SERVER_ should be greater than 0
+ Then the statistics counter notifyoutv6 for the zone example.org. should be greater than 0
+ Then the statistics counter xfrrej for the zone _SERVER_ should be 0
+ Then the statistics counter xfrrej for the zone example.org. should be 0
+ Then the statistics counter xfrreqdone for the zone _SERVER_ should be 0
+ Then the statistics counter xfrreqdone for the zone example.org. should be 0
+
+ When I query statistics socket of bind10 module Xfrout with cmdctl port 47804
+ Then the statistics counter open should be 1
+ Then the statistics counter openfail should be 0
+ Then the statistics counter close should be 0
+ Then the statistics counter bindfail should be 0
+ Then the statistics counter acceptfail should be 0
+ Then the statistics counter accept should be 0
+ Then the statistics counter senderr should be 0
+ Then the statistics counter recverr should be 0
+
diff --git a/tests/tools/dhcp-ubench/dhcp-perf-guide.xml b/tests/tools/dhcp-ubench/dhcp-perf-guide.xml
index f878af3..583b155 100644
--- a/tests/tools/dhcp-ubench/dhcp-perf-guide.xml
+++ b/tests/tools/dhcp-ubench/dhcp-perf-guide.xml
@@ -1020,7 +1020,7 @@ SQLite version: 3.7.9sourceid version is 2011-11-01 00:52:41 c7c6050ef060877ebe7
command line switches. This summary also lists the tool's
possible exit codes as well as describing the
error counters printed when the test is complete:
- <screen>$ <userinput>./perfdhcp -h</userinput>
+ <screen>$ <userinput>perfdhcp -h</userinput>
<![CDATA[perfdhcp [-hv] [-4|-6] [-r<rate>] [-t<report>] [-R<range>] [-b<base>]
[-n<num-request>] [-p<test-period>] [-d<drop-time>] [-D<max-drop>]
[-l<local-addr|interface>] [-P<preload>] [-a<aggressivity>]
@@ -1172,12 +1172,12 @@ The exit status is:
As DHCP uses low port numbers (67 for DHCPv4 relays and
547 for DHCPv6), running perfdhcp with non-root privileges will
usually result in the error message similar to this:
- <screen><userinput>$./perfdhcp -4 -l eth3 -r 100 all</userinput>
+ <screen><userinput>$ perfdhcp -4 -l eth3 -r 100 all</userinput>
Error running perfdhcp: Failed to bind socket 3 to 172.16.1.2/port=67
</screen>
The '-L' command line switch allows the use of a custom local port.
However, although the following command line will work:
- <screen><userinput>$./perfdhcp -4 -l eth3 -r 100 -L 10067 all</userinput></screen>
+ <screen><userinput>$ perfdhcp -4 -l eth3 -r 100 -L 10067 all</userinput></screen>
in the standard configuration no responses will be received
from the DHCP server because the server responds to default relay
port 67. A way to overcome this issue is to run
@@ -1198,7 +1198,7 @@ Error running perfdhcp: Failed to bind socket 3 to 172.16.1.2/port=67
<para>
If server is listening on interface with IPv4 address 172.16.1.1,
the simplest perfdhcp command line will look like:
- <screen><userinput>#./perfdhcp 172.16.1.1</userinput>
+ <screen><userinput># perfdhcp 172.16.1.1</userinput>
***Rate statistics***
Rate: 206.345
@@ -1281,9 +1281,9 @@ collected packets: 0
Note: should multiple interfaces on the system running perfdhcp be
connected to the same subnet, the interface to be used for the test
can be specified using either the interface name:
- <screen><userinput>#./perfdhcp -l eth3</userinput></screen>
+ <screen><userinput># perfdhcp -l eth3</userinput></screen>
or a local address assigned to it:
- <screen><userinput>#./perfdhcp -l 172.16.1.2</userinput></screen>
+ <screen><userinput># perfdhcp -l 172.16.1.2</userinput></screen>
</para>
</section>
<section id="perfdhcp-rate-control">
@@ -1296,7 +1296,7 @@ collected packets: 0
without dropping any packets. The following command is an example of such
a test: it causes perfdhcp to initiate 300 four-way exchanges
per second, and runs the test for 60 seconds:
- <screen><userinput>#./perfdhcp -l eth3 -p 60 -r 300</userinput>
+ <screen><userinput># perfdhcp -l eth3 -p 60 -r 300</userinput>
***Rate statistics***
Rate: 256.683 exchanges/second, expected rate: 300 exchanges/second
@@ -1331,7 +1331,7 @@ collected packets: 0
rate (256/s) indicate that server's measured performance is lower than 300 leases
per second. A further rate decrease should eliminate most of the packet
drops and bring the achieved rate close to expected rate:
- <screen><userinput>#./perfdhcp -l eth3 -p 60 -r 100 -R 30</userinput>
+ <screen><userinput># perfdhcp -l eth3 -p 60 -r 100 -R 30</userinput>
***Rate statistics***
Rate: 99.8164 exchanges/second, expected rate: 100 exchanges/second
@@ -1393,7 +1393,7 @@ collected packets: 0
specify offsets for particular options and fields. With the following
command line the DHCPv6 SOLICIT and REQUEST packets will be formed from
solicit.hex and request6.hex packets:
- <screen><userinput>#./perfdhcp -6 -l eth3 -r 100 -R 20 -T solicit.hex -T request6.hex -O 21 -E 84 -S 22 -I 40 servers</userinput>
+ <screen><userinput># perfdhcp -6 -l eth3 -r 100 -R 20 -T solicit.hex -T request6.hex -O 21 -E 84 -S 22 -I 40 servers</userinput>
***Rate statistics***
Rate: 99.5398 exchanges/second, expected rate: 100 exchanges/second
diff --git a/tests/tools/perfdhcp/Makefile.am b/tests/tools/perfdhcp/Makefile.am
index 816eeeb..08a21a4 100644
--- a/tests/tools/perfdhcp/Makefile.am
+++ b/tests/tools/perfdhcp/Makefile.am
@@ -12,36 +12,33 @@ AM_CXXFLAGS = $(B10_CXXFLAGS)
# But older GCC compilers don't have the flag.
AM_CXXFLAGS += $(WARNING_NO_MISSING_FIELD_INITIALIZERS_CFLAG)
-AM_LDFLAGS = $(CLOCK_GETTIME_LDFLAGS)
-AM_LDFLAGS += -lm
+AM_LDFLAGS = -lm
if USE_STATIC_LINK
AM_LDFLAGS += -static
endif
-pkglibexec_PROGRAMS = perfdhcp2
-perfdhcp2_SOURCES = main.cc
-perfdhcp2_SOURCES += command_options.cc command_options.h
-perfdhcp2_SOURCES += localized_option.h
-perfdhcp2_SOURCES += perf_pkt6.cc perf_pkt6.h
-perfdhcp2_SOURCES += perf_pkt4.cc perf_pkt4.h
-perfdhcp2_SOURCES += pkt_transform.cc pkt_transform.h
-perfdhcp2_SOURCES += stats_mgr.h
-perfdhcp2_SOURCES += test_control.cc test_control.h
+bin_PROGRAMS = perfdhcp
+perfdhcp_SOURCES = main.cc
+perfdhcp_SOURCES += command_options.cc command_options.h
+perfdhcp_SOURCES += localized_option.h
+perfdhcp_SOURCES += perf_pkt6.cc perf_pkt6.h
+perfdhcp_SOURCES += perf_pkt4.cc perf_pkt4.h
+perfdhcp_SOURCES += pkt_transform.cc pkt_transform.h
+perfdhcp_SOURCES += stats_mgr.h
+perfdhcp_SOURCES += test_control.cc test_control.h
libb10_perfdhcp___la_CXXFLAGS = $(AM_CXXFLAGS)
-perfdhcp2_CXXFLAGS = $(AM_CXXFLAGS)
+perfdhcp_CXXFLAGS = $(AM_CXXFLAGS)
if USE_CLANGPP
# Disable unused parameter warning caused by some of the
# Boost headers when compiling with clang.
-perfdhcp2_CXXFLAGS += -Wno-unused-parameter
+perfdhcp_CXXFLAGS += -Wno-unused-parameter
endif
-perfdhcp2_LDADD = $(top_builddir)/src/lib/exceptions/libb10-exceptions.la
-perfdhcp2_LDADD += $(top_builddir)/src/lib/dhcp/libb10-dhcp++.la
-perfdhcp2_LDADD += $(top_builddir)/src/lib/asiolink/libb10-asiolink.la
+perfdhcp_LDADD = $(top_builddir)/src/lib/exceptions/libb10-exceptions.la
+perfdhcp_LDADD += $(top_builddir)/src/lib/dhcp/libb10-dhcp++.la
+perfdhcp_LDADD += $(top_builddir)/src/lib/asiolink/libb10-asiolink.la
-#pkglibexec_PROGRAMS = perfdhcp
-#perfdhcp_SOURCES = perfdhcp.c
# ... and the documentation
-EXTRA_DIST = perfdhcp_internals.dox
+EXTRA_DIST = perfdhcp_internals.dox
\ No newline at end of file
diff --git a/tests/tools/perfdhcp/command_options.cc b/tests/tools/perfdhcp/command_options.cc
index 91c5819..9aa6abf 100644
--- a/tests/tools/perfdhcp/command_options.cc
+++ b/tests/tools/perfdhcp/command_options.cc
@@ -18,13 +18,10 @@
#include <stdint.h>
#include <unistd.h>
-#include <boost/algorithm/string.hpp>
-#include <boost/foreach.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <exceptions/exceptions.h>
-#include <dhcp/dhcp6.h>
#include <dhcp/iface_mgr.h>
#include "command_options.h"
@@ -86,10 +83,9 @@ CommandOptions::reset() {
wrapped_.clear();
server_name_.clear();
generateDuidTemplate();
- commandline_.clear();
}
-void
+bool
CommandOptions::parse(int argc, char** const argv) {
// Reset internal variables used by getopt
// to eliminate undefined behavior when
@@ -127,18 +123,23 @@ CommandOptions::parse(int argc, char** const argv) {
// Reset values of class members
reset();
- initialize(argc, argv);
- validate();
+ // Informs if program has been run with 'h' or 'v' option.
+ bool help_or_version_mode = initialize(argc, argv);
+ if (!help_or_version_mode) {
+ validate();
+ }
+ return (help_or_version_mode);
}
-void
+bool
CommandOptions::initialize(int argc, char** argv) {
char opt = 0; // Subsequent options returned by getopt()
std::string drop_arg; // Value of -D<value>argument
size_t percent_loc = 0; // Location of % sign in -D<value>
double drop_percent = 0; // % value (1..100) in -D<value%>
int num_drops = 0; // Max number of drops specified in -D<value>
- int num_req = 0; // Max number of dropped requests in -n<max-drops>
+ int num_req = 0; // Max number of dropped
+ // requests in -n<max-drops>
int offset_arg = 0; // Temporary variable holding offset arguments
std::string sarg; // Temporary variable for string args
@@ -147,16 +148,13 @@ CommandOptions::initialize(int argc, char** argv) {
// In this section we collect argument values from command line
// they will be tuned and validated elsewhere
- while((opt = getopt(argc, argv, "hv46r:t:R:b:n:p:d:D:l:P:a:L:s:iBc1T:X:O:E:S:I:x:w:")) != -1) {
+ while((opt = getopt(argc, argv, "hv46r:t:R:b:n:p:d:D:l:P:a:L:"
+ "s:iBc1T:X:O:E:S:I:x:w:")) != -1) {
stream << " -" << opt;
if (optarg) {
stream << " " << optarg;
}
switch (opt) {
- case 'v':
- version();
- return;
-
case '1':
use_first_ = true;
break;
@@ -172,11 +170,13 @@ CommandOptions::initialize(int argc, char** argv) {
break;
case 'a':
- aggressivity_ = positiveInteger("value of aggressivity: -a<value> must be a positive integer");
+ aggressivity_ = positiveInteger("value of aggressivity: -a<value>"
+ " must be a positive integer");
break;
case 'b':
- check(base_.size() > 3, "-b<value> already specified, unexpected occurence of 5th -b<value>");
+ check(base_.size() > 3, "-b<value> already specified,"
+ " unexpected occurence of 5th -b<value>");
base_.push_back(optarg);
decodeBase(base_.back());
break;
@@ -190,53 +190,64 @@ CommandOptions::initialize(int argc, char** argv) {
break;
case 'd':
- check(drop_time_set_ > 1, "maximum number of drops already specified, "
+ check(drop_time_set_ > 1,
+ "maximum number of drops already specified, "
"unexpected 3rd occurence of -d<value>");
try {
- drop_time_[drop_time_set_] = boost::lexical_cast<double>(optarg);
+ drop_time_[drop_time_set_] =
+ boost::lexical_cast<double>(optarg);
} catch (boost::bad_lexical_cast&) {
isc_throw(isc::InvalidParameter,
- "value of drop time: -d<value> must be positive number");
+ "value of drop time: -d<value>"
+ " must be positive number");
}
- check(drop_time_[drop_time_set_] <= 0., "drop-time must be a positive number");
+ check(drop_time_[drop_time_set_] <= 0.,
+ "drop-time must be a positive number");
drop_time_set_ = true;
break;
case 'D':
drop_arg = std::string(optarg);
percent_loc = drop_arg.find('%');
- check(max_pdrop_.size() > 1 || max_drop_.size() > 1, "values of maximum drops: -D<value> already "
+ check(max_pdrop_.size() > 1 || max_drop_.size() > 1,
+ "values of maximum drops: -D<value> already "
"specified, unexpected 3rd occurence of -D,value>");
if ((percent_loc) != std::string::npos) {
try {
- drop_percent = boost::lexical_cast<double>(drop_arg.substr(0, percent_loc));
+ drop_percent =
+ boost::lexical_cast<double>(drop_arg.substr(0, percent_loc));
} catch (boost::bad_lexical_cast&) {
isc_throw(isc::InvalidParameter,
- "value of drop percentage: -D<value%> must be 0..100");
+ "value of drop percentage: -D<value%>"
+ " must be 0..100");
}
check((drop_percent <= 0) || (drop_percent >= 100),
"value of drop percentage: -D<value%> must be 0..100");
max_pdrop_.push_back(drop_percent);
} else {
- num_drops = positiveInteger("value of max drops number: -d<value> must be a positive integer");
+ num_drops = positiveInteger("value of max drops number:"
+ " -d<value> must be a positive integer");
max_drop_.push_back(num_drops);
}
break;
case 'E':
- elp_offset_ = nonNegativeInteger("value of time-offset: -E<value> must not be a negative integer");
+ elp_offset_ = nonNegativeInteger("value of time-offset: -E<value>"
+ " must not be a negative integer");
break;
case 'h':
usage();
- return;
+ return (true);
case 'i':
exchange_mode_ = DO_SA;
break;
case 'I':
- rip_offset_ = positiveInteger("value of ip address offset: -I<value> must be a positive integer");
+ rip_offset_ = positiveInteger("value of ip address offset:"
+ " -I<value> must be a"
+ " positive integer");
break;
case 'l':
@@ -245,43 +256,55 @@ CommandOptions::initialize(int argc, char** argv) {
break;
case 'L':
- local_port_ = nonNegativeInteger("value of local port: -L<value> must not be a negative integer");
- check(local_port_ > static_cast<int>(std::numeric_limits<uint16_t>::max()),
+ local_port_ = nonNegativeInteger("value of local port:"
+ " -L<value> must not be a"
+ " negative integer");
+ check(local_port_ >
+ static_cast<int>(std::numeric_limits<uint16_t>::max()),
"local-port must be lower than " +
boost::lexical_cast<std::string>(std::numeric_limits<uint16_t>::max()));
break;
case 'n':
- num_req = positiveInteger("value of num-request: -n<value> must be a positive integer");
+ num_req = positiveInteger("value of num-request:"
+ " -n<value> must be a positive integer");
if (num_request_.size() >= 2) {
- isc_throw(isc::InvalidParameter,"value of maximum number of requests: -n<value> "
- "already specified, unexpected 3rd occurence of -n<value>");
+ isc_throw(isc::InvalidParameter,
+ "value of maximum number of requests: -n<value> "
+ "already specified, unexpected 3rd occurence"
+ " of -n<value>");
}
num_request_.push_back(num_req);
break;
case 'O':
if (rnd_offset_.size() < 2) {
- offset_arg = positiveInteger("value of random offset: -O<value> must be greater than 3");
+ offset_arg = positiveInteger("value of random offset: "
+ "-O<value> must be greater than 3");
} else {
isc_throw(isc::InvalidParameter,
- "random offsets already specified, unexpected 3rd occurence of -O<value>");
+ "random offsets already specified,"
+ " unexpected 3rd occurence of -O<value>");
}
- check(offset_arg < 3, "value of random random-offset: -O<value> must be greater than 3 ");
+ check(offset_arg < 3, "value of random random-offset:"
+ " -O<value> must be greater than 3 ");
rnd_offset_.push_back(offset_arg);
break;
case 'p':
- period_ = positiveInteger("value of test period: -p<value> must be a positive integer");
+ period_ = positiveInteger("value of test period:"
+ " -p<value> must be a positive integer");
break;
case 'P':
- preload_ = nonNegativeInteger("number of preload packets: -P<value> must not be "
+ preload_ = nonNegativeInteger("number of preload packets:"
+ " -P<value> must not be "
"a negative integer");
break;
case 'r':
- rate_ = positiveInteger("value of rate: -r<value> must be a positive integer");
+ rate_ = positiveInteger("value of rate:"
+ " -r<value> must be a positive integer");
break;
case 'R':
@@ -290,42 +313,58 @@ CommandOptions::initialize(int argc, char** argv) {
case 's':
seed_ = static_cast<unsigned int>
- (nonNegativeInteger("value of seed: -s <seed> must be non-negative integer"));
+ (nonNegativeInteger("value of seed:"
+ " -s <seed> must be non-negative integer"));
seeded_ = seed_ > 0 ? true : false;
break;
case 'S':
- sid_offset_ = positiveInteger("value of server id offset: -S<value> must be a positive integer");
+ sid_offset_ = positiveInteger("value of server id offset:"
+ " -S<value> must be a"
+ " positive integer");
break;
case 't':
- report_delay_ = positiveInteger("value of report delay: -t<value> must be a positive integer");
+ report_delay_ = positiveInteger("value of report delay:"
+ " -t<value> must be a"
+ " positive integer");
break;
case 'T':
if (template_file_.size() < 2) {
- sarg = nonEmptyString("template file name not specified, expected -T<filename>");
+ sarg = nonEmptyString("template file name not specified,"
+ " expected -T<filename>");
template_file_.push_back(sarg);
} else {
isc_throw(isc::InvalidParameter,
- "template files are already specified, unexpected 3rd -T<filename> occurence");
+ "template files are already specified,"
+ " unexpected 3rd -T<filename> occurence");
}
break;
+ case 'v':
+ version();
+ return (true);
+
case 'w':
- wrapped_ = nonEmptyString("command for wrapped mode: -w<command> must be specified");
+ wrapped_ = nonEmptyString("command for wrapped mode:"
+ " -w<command> must be specified");
break;
case 'x':
- diags_ = nonEmptyString("value of diagnostics selectors: -x<value> must be specified");
+ diags_ = nonEmptyString("value of diagnostics selectors:"
+ " -x<value> must be specified");
break;
case 'X':
if (xid_offset_.size() < 2) {
- offset_arg = positiveInteger("value of transaction id: -X<value> must be a positive integer");
+ offset_arg = positiveInteger("value of transaction id:"
+ " -X<value> must be a"
+ " positive integer");
} else {
isc_throw(isc::InvalidParameter,
- "transaction ids already specified, unexpected 3rd -X<value> occurence");
+ "transaction ids already specified,"
+ " unexpected 3rd -X<value> occurence");
}
xid_offset_.push_back(offset_arg);
break;
@@ -335,8 +374,6 @@ CommandOptions::initialize(int argc, char** argv) {
}
}
- std::cout << "Running: " << stream.str() << std::endl;
-
// If the IP version was not specified in the
// command line, assume IPv4.
if (ipversion_ == 0) {
@@ -364,26 +401,30 @@ CommandOptions::initialize(int argc, char** argv) {
check(optind < argc -1, "extra arguments?");
if (optind == argc - 1) {
server_name_ = argv[optind];
+ stream << " " << server_name_;
// Decode special cases
if ((ipversion_ == 4) && (server_name_.compare("all") == 0)) {
- broadcast_ = 1;
- // 255.255.255.255 is IPv4 broadcast address
- server_name_ = "255.255.255.255";
+ broadcast_ = true;
+ // Use broadcast address as server name.
+ server_name_ = DHCP_IPV4_BROADCAST_ADDRESS;
} else if ((ipversion_ == 6) && (server_name_.compare("all") == 0)) {
- server_name_ = "FF02::1:2";
- } else if ((ipversion_ == 6) && (server_name_.compare("servers") == 0)) {
- server_name_ = "FF05::1:3";
+ server_name_ = ALL_DHCP_RELAY_AGENTS_AND_SERVERS;
+ } else if ((ipversion_ == 6) &&
+ (server_name_.compare("servers") == 0)) {
+ server_name_ = ALL_DHCP_SERVERS;
}
}
+ std::cout << "Running: " << stream.str() << std::endl;
+
// Handle the local '-l' address/interface
if (!localname_.empty()) {
if (server_name_.empty()) {
if (is_interface_ && (ipversion_ == 4)) {
- broadcast_ = 1;
- server_name_ = "255.255.255.255";
+ broadcast_ = true;
+ server_name_ = DHCP_IPV4_BROADCAST_ADDRESS;
} else if (is_interface_ && (ipversion_ == 6)) {
- server_name_ = "FF02::1:2";
+ server_name_ = ALL_DHCP_RELAY_AGENTS_AND_SERVERS;
}
}
}
@@ -397,11 +438,13 @@ CommandOptions::initialize(int argc, char** argv) {
if (duid_template_.size() == 0) {
generateDuidTemplate();
}
+ return (false);
}
void
CommandOptions::initClientsNum() {
- const std::string errmsg = "value of -R <value> must be non-negative integer";
+ const std::string errmsg =
+ "value of -R <value> must be non-negative integer";
// Declare clients_num as as 64-bit signed value to
// be able to detect negative values provided
@@ -410,11 +453,7 @@ CommandOptions::initClientsNum() {
long long clients_num = 0;
try {
clients_num = boost::lexical_cast<long long>(optarg);
- } catch (boost::bad_lexical_cast&) {
- isc_throw(isc::InvalidParameter, errmsg.c_str());
- }
- check(clients_num < 0, errmsg);
- try {
+ check(clients_num < 0, errmsg);
clients_num_ = boost::lexical_cast<uint32_t>(optarg);
} catch (boost::bad_lexical_cast&) {
isc_throw(isc::InvalidParameter, errmsg);
@@ -444,7 +483,8 @@ CommandOptions::decodeBase(const std::string& base) {
decodeDuid(b);
} else {
isc_throw(isc::InvalidParameter,
- "base value not provided as -b<value>, expected -b mac=<mac> or -b duid=<duid>");
+ "base value not provided as -b<value>,"
+ " expected -b mac=<mac> or -b duid=<duid>");
}
}
@@ -452,7 +492,9 @@ void
CommandOptions::decodeMac(const std::string& base) {
// Strip string from mac=
size_t found = base.find('=');
- static const char* errmsg = "expected -b<base> format for mac address is -b mac=00::0C::01::02::03::04";
+ static const char* errmsg = "expected -b<base> format for"
+ " mac address is -b mac=00::0C::01::02::03::04 or"
+ " -b mac=00:0C:01:02:03:04";
check(found == std::string::npos, errmsg);
// Decode mac address to vector of uint8_t
@@ -485,7 +527,8 @@ CommandOptions::decodeDuid(const std::string& base) {
// Strip argument from duid=
std::vector<uint8_t> duid_template;
size_t found = base.find('=');
- check(found == std::string::npos, "expected -b<base> format for duid is -b duid=<duid>");
+ check(found == std::string::npos, "expected -b<base>"
+ " format for duid is -b duid=<duid>");
std::string b = base.substr(found + 1);
// DUID must have even number of digits and must not be longer than 64 bytes
@@ -501,7 +544,8 @@ CommandOptions::decodeDuid(const std::string& base) {
ui = convertHexString(b.substr(i, 2));
} catch (isc::InvalidParameter&) {
isc_throw(isc::InvalidParameter,
- "invalid characters in DUID provided, exepected hex digits");
+ "invalid characters in DUID provided,"
+ " exepected hex digits");
}
duid_template.push_back(static_cast<uint8_t>(ui));
}
@@ -527,7 +571,7 @@ CommandOptions::generateDuidTemplate() {
duid_template_[1] = DUID_LLT & 0xff;
duid_template_[2] = HWTYPE_ETHERNET >> 8;
duid_template_[3] = HWTYPE_ETHERNET & 0xff;
-
+
// As described in RFC3315: 'the time value is the time
// that the DUID is generated represented in seconds
// since midnight (UTC), January 1, 2000, modulo 2^32.'
@@ -559,7 +603,8 @@ CommandOptions::convertHexString(const std::string& text) const {
text_stream >> std::hex >> ui >> std::dec;
// Check if for some reason we have overflow - this should never happen!
if (ui > 0xFF) {
- isc_throw(isc::InvalidParameter, "Can't convert more than two hex digits to byte");
+ isc_throw(isc::InvalidParameter, "Can't convert more than"
+ " two hex digits to byte");
}
return ui;
}
@@ -591,29 +636,29 @@ CommandOptions::validate() const {
"-S<srvid-offset> is not compatible with -i\n");
check((getExchangeMode() == DO_SA) && (getRequestedIpOffset() >= 0),
"-I<ip-offset> is not compatible with -i\n");
- check((getExchangeMode() != DO_SA) && (isRapidCommit() != 0),
+ check((getExchangeMode() != DO_SA) && (isRapidCommit() != 0),
"-i must be set to use -c\n");
- check((getRate() == 0) && (getReportDelay() != 0),
+ check((getRate() == 0) && (getReportDelay() != 0),
"-r<rate> must be set to use -t<report>\n");
- check((getRate() == 0) && (getNumRequests().size() > 0),
+ check((getRate() == 0) && (getNumRequests().size() > 0),
"-r<rate> must be set to use -n<num-request>\n");
- check((getRate() == 0) && (getPeriod() != 0),
+ check((getRate() == 0) && (getPeriod() != 0),
"-r<rate> must be set to use -p<test-period>\n");
- check((getRate() == 0) &&
+ check((getRate() == 0) &&
((getMaxDrop().size() > 0) || getMaxDropPercentage().size() > 0),
"-r<rate> must be set to use -D<max-drop>\n");
- check((getTemplateFiles().size() < getTransactionIdOffset().size()),
+ check((getTemplateFiles().size() < getTransactionIdOffset().size()),
"-T<template-file> must be set to use -X<xid-offset>\n");
- check((getTemplateFiles().size() < getRandomOffset().size()),
+ check((getTemplateFiles().size() < getRandomOffset().size()),
"-T<template-file> must be set to use -O<random-offset>\n");
- check((getTemplateFiles().size() < 2) && (getElapsedTimeOffset() >= 0),
+ check((getTemplateFiles().size() < 2) && (getElapsedTimeOffset() >= 0),
"second/request -T<template-file> must be set to use -E<time-offset>\n");
- check((getTemplateFiles().size() < 2) && (getServerIdOffset() >= 0),
+ check((getTemplateFiles().size() < 2) && (getServerIdOffset() >= 0),
"second/request -T<template-file> must be set to "
"use -S<srvid-offset>\n");
- check((getTemplateFiles().size() < 2) && (getRequestedIpOffset() >= 0),
- "second/request -T<template-file> must be set to "
- "use -I<ip-offset>\n");
+ check((getTemplateFiles().size() < 2) && (getRequestedIpOffset() >= 0),
+ "second/request -T<template-file> must be set to "
+ "use -I<ip-offset>\n");
}
@@ -666,7 +711,7 @@ CommandOptions::printCommandLine() const {
} else {
std::cout << "SOLICIT-ADVERETISE only" << std::endl;
}
- }
+ }
if (rate_ != 0) {
std::cout << "rate[1/s]=" << rate_ << std::endl;
}
@@ -675,7 +720,7 @@ CommandOptions::printCommandLine() const {
}
if (clients_num_ != 0) {
std::cout << "clients=" << clients_num_ << std::endl;
- }
+ }
for (int i = 0; i < base_.size(); ++i) {
std::cout << "base[" << i << "]=" << base_[i] << std::endl;
}
@@ -751,127 +796,126 @@ CommandOptions::printCommandLine() const {
void
CommandOptions::usage() const {
- fprintf(stdout, "%s",
-"perfdhcp [-hv] [-4|-6] [-r<rate>] [-t<report>] [-R<range>] [-b<base>]\n"
-" [-n<num-request>] [-p<test-period>] [-d<drop-time>] [-D<max-drop>]\n"
-" [-l<local-addr|interface>] [-P<preload>] [-a<aggressivity>]\n"
-" [-L<local-port>] [-s<seed>] [-i] [-B] [-c] [-1]\n"
-" [-T<template-file>] [-X<xid-offset>] [-O<random-offset]\n"
-" [-E<time-offset>] [-S<srvid-offset>] [-I<ip-offset>]\n"
-" [-x<diagnostic-selector>] [-w<wrapped>] [server]\n"
-"\n"
-"The [server] argument is the name/address of the DHCP server to\n"
-"contact. For DHCPv4 operation, exchanges are initiated by\n"
-"transmitting a DHCP DISCOVER to this address.\n"
-"\n"
-"For DHCPv6 operation, exchanges are initiated by transmitting a DHCP\n"
-"SOLICIT to this address. In the DHCPv6 case, the special name 'all'\n"
-"can be used to refer to All_DHCP_Relay_Agents_and_Servers (the\n"
-"multicast address FF02::1:2), or the special name 'servers' to refer\n"
-"to All_DHCP_Servers (the multicast address FF05::1:3). The [server]\n"
-"argument is optional only in the case that -l is used to specify an\n"
-"interface, in which case [server] defaults to 'all'.\n"
-"\n"
-"The default is to perform a single 4-way exchange, effectively pinging\n"
-"the server.\n"
-"The -r option is used to set up a performance test, without\n"
-"it exchanges are initiated as fast as possible.\n"
-"\n"
-"Options:\n"
-"-1: Take the server-ID option from the first received message.\n"
-"-4: DHCPv4 operation (default). This is incompatible with the -6 option.\n"
-"-6: DHCPv6 operation. This is incompatible with the -4 option.\n"
-"-a<aggressivity>: When the target sending rate is not yet reached,\n"
-" control how many exchanges are initiated before the next pause.\n"
-"-b<base>: The base mac, duid, IP, etc, used to simulate different\n"
-" clients. This can be specified multiple times, each instance is\n"
-" in the <type>=<value> form, for instance:\n"
-" (and default) mac=00:0c:01:02:03:04.\n"
-"-d<drop-time>: Specify the time after which a request is treated as\n"
-" having been lost. The value is given in seconds and may contain a\n"
-" fractional component. The default is 1 second.\n"
-"-E<time-offset>: Offset of the (DHCPv4) secs field / (DHCPv6)\n"
-" elapsed-time option in the (second/request) template.\n"
-" The value 0 disables it.\n"
-"-h: Print this help.\n"
-"-i: Do only the initial part of an exchange: DO or SA, depending on\n"
-" whether -6 is given.\n"
-"-I<ip-offset>: Offset of the (DHCPv4) IP address in the requested-IP\n"
-" option / (DHCPv6) IA_NA option in the (second/request) template.\n"
-"-l<local-addr|interface>: For DHCPv4 operation, specify the local\n"
-" hostname/address to use when communicating with the server. By\n"
-" default, the interface address through which traffic would\n"
-" normally be routed to the server is used.\n"
-" For DHCPv6 operation, specify the name of the network interface\n"
-" via which exchanges are initiated.\n"
-"-L<local-port>: Specify the local port to use\n"
-" (the value 0 means to use the default).\n"
-"-O<random-offset>: Offset of the last octet to randomize in the template.\n"
-"-P<preload>: Initiate first <preload> exchanges back to back at startup.\n"
-"-r<rate>: Initiate <rate> DORA/SARR (or if -i is given, DO/SA)\n"
-" exchanges per second. A periodic report is generated showing the\n"
-" number of exchanges which were not completed, as well as the\n"
-" average response latency. The program continues until\n"
-" interrupted, at which point a final report is generated.\n"
-"-R<range>: Specify how many different clients are used. With 1\n"
-" (the default), all requests seem to come from the same client.\n"
-"-s<seed>: Specify the seed for randomization, making it repeatable.\n"
-"-S<srvid-offset>: Offset of the server-ID option in the\n"
-" (second/request) template.\n"
-"-T<template-file>: The name of a file containing the template to use\n"
-" as a stream of hexadecimal digits.\n"
-"-v: Report the version number of this program.\n"
-"-w<wrapped>: Command to call with start/stop at the beginning/end of\n"
-" the program.\n"
-"-x<diagnostic-selector>: Include extended diagnostics in the output.\n"
-" <diagnostic-selector> is a string of single-keywords specifying\n"
-" the operations for which verbose output is desired. The selector\n"
-" keyletters are:\n"
-" * 'a': print the decoded command line arguments\n"
-" * 'e': print the exit reason\n"
-" * 'i': print rate processing details\n"
-" * 'r': print randomization details\n"
-" * 's': print first server-id\n"
-" * 't': when finished, print timers of all successful exchanges\n"
-" * 'T': when finished, print templates\n"
-"-X<xid-offset>: Transaction ID (aka. xid) offset in the template.\n"
-"\n"
-"DHCPv4 only options:\n"
-"-B: Force broadcast handling.\n"
-"\n"
-"DHCPv6 only options:\n"
-"-c: Add a rapid commit option (exchanges will be SA).\n"
-"\n"
-"The remaining options are used only in conjunction with -r:\n"
-"\n"
-"-D<max-drop>: Abort the test if more than <max-drop> requests have\n"
-" been dropped. Use -D0 to abort if even a single request has been\n"
-" dropped. If <max-drop> includes the suffix '%', it specifies a\n"
-" maximum percentage of requests that may be dropped before abort.\n"
-" In this case, testing of the threshold begins after 10 requests\n"
-" have been expected to be received.\n"
-"-n<num-request>: Initiate <num-request> transactions. No report is\n"
-" generated until all transactions have been initiated/waited-for,\n"
-" after which a report is generated and the program terminates.\n"
-"-p<test-period>: Send requests for the given test period, which is\n"
-" specified in the same manner as -d. This can be used as an\n"
-" alternative to -n, or both options can be given, in which case the\n"
-" testing is completed when either limit is reached.\n"
-"-t<report>: Delay in seconds between two periodic reports.\n"
-"\n"
-"Errors:\n"
-"- tooshort: received a too short message\n"
-"- orphans: received a message which doesn't match an exchange\n"
-" (duplicate, late or not related)\n"
-"- locallimit: reached to local system limits when sending a message.\n"
-"\n"
-"Exit status:\n"
-"The exit status is:\n"
-"0 on complete success.\n"
-"1 for a general error.\n"
-"2 if an error is found in the command line arguments.\n"
-"3 if there are no general failures in operation, but one or more\n"
-" exchanges are not successfully completed.\n");
+ std::cout <<
+ "perfdhcp [-hv] [-4|-6] [-r<rate>] [-t<report>] [-R<range>] [-b<base>]\n"
+ " [-n<num-request>] [-p<test-period>] [-d<drop-time>] [-D<max-drop>]\n"
+ " [-l<local-addr|interface>] [-P<preload>] [-a<aggressivity>]\n"
+ " [-L<local-port>] [-s<seed>] [-i] [-B] [-c] [-1]\n"
+ " [-T<template-file>] [-X<xid-offset>] [-O<random-offset]\n"
+ " [-E<time-offset>] [-S<srvid-offset>] [-I<ip-offset>]\n"
+ " [-x<diagnostic-selector>] [-w<wrapped>] [server]\n"
+ "\n"
+ "The [server] argument is the name/address of the DHCP server to\n"
+ "contact. For DHCPv4 operation, exchanges are initiated by\n"
+ "transmitting a DHCP DISCOVER to this address.\n"
+ "\n"
+ "For DHCPv6 operation, exchanges are initiated by transmitting a DHCP\n"
+ "SOLICIT to this address. In the DHCPv6 case, the special name 'all'\n"
+ "can be used to refer to All_DHCP_Relay_Agents_and_Servers (the\n"
+ "multicast address FF02::1:2), or the special name 'servers' to refer\n"
+ "to All_DHCP_Servers (the multicast address FF05::1:3). The [server]\n"
+ "argument is optional only in the case that -l is used to specify an\n"
+ "interface, in which case [server] defaults to 'all'.\n"
+ "\n"
+ "The default is to perform a single 4-way exchange, effectively pinging\n"
+ "the server.\n"
+ "The -r option is used to set up a performance test, without\n"
+ "it exchanges are initiated as fast as possible.\n"
+ "\n"
+ "Options:\n"
+ "-1: Take the server-ID option from the first received message.\n"
+ "-4: DHCPv4 operation (default). This is incompatible with the -6 option.\n"
+ "-6: DHCPv6 operation. This is incompatible with the -4 option.\n"
+ "-a<aggressivity>: When the target sending rate is not yet reached,\n"
+ " control how many exchanges are initiated before the next pause.\n"
+ "-b<base>: The base mac, duid, IP, etc, used to simulate different\n"
+ " clients. This can be specified multiple times, each instance is\n"
+ " in the <type>=<value> form, for instance:\n"
+ " (and default) mac=00:0c:01:02:03:04.\n"
+ "-d<drop-time>: Specify the time after which a requeqst is treated as\n"
+ " having been lost. The value is given in seconds and may contain a\n"
+ " fractional component. The default is 1 second.\n"
+ "-E<time-offset>: Offset of the (DHCPv4) secs field / (DHCPv6)\n"
+ " elapsed-time option in the (second/request) template.\n"
+ " The value 0 disables it.\n"
+ "-h: Print this help.\n"
+ "-i: Do only the initial part of an exchange: DO or SA, depending on\n"
+ " whether -6 is given.\n"
+ "-I<ip-offset>: Offset of the (DHCPv4) IP address in the requested-IP\n"
+ " option / (DHCPv6) IA_NA option in the (second/request) template.\n"
+ "-l<local-addr|interface>: For DHCPv4 operation, specify the local\n"
+ " hostname/address to use when communicating with the server. By\n"
+ " default, the interface address through which traffic would\n"
+ " normally be routed to the server is used.\n"
+ " For DHCPv6 operation, specify the name of the network interface\n"
+ " via which exchanges are initiated.\n"
+ "-L<local-port>: Specify the local port to use\n"
+ " (the value 0 means to use the default).\n"
+ "-O<random-offset>: Offset of the last octet to randomize in the template.\n"
+ "-P<preload>: Initiate first <preload> exchanges back to back at startup.\n"
+ "-r<rate>: Initiate <rate> DORA/SARR (or if -i is given, DO/SA)\n"
+ " exchanges per second. A periodic report is generated showing the\n"
+ " number of exchanges which were not completed, as well as the\n"
+ " average response latency. The program continues until\n"
+ " interrupted, at which point a final report is generated.\n"
+ "-R<range>: Specify how many different clients are used. With 1\n"
+ " (the default), all requests seem to come from the same client.\n"
+ "-s<seed>: Specify the seed for randomization, making it repeatable.\n"
+ "-S<srvid-offset>: Offset of the server-ID option in the\n"
+ " (second/request) template.\n"
+ "-T<template-file>: The name of a file containing the template to use\n"
+ " as a stream of hexadecimal digits.\n"
+ "-v: Report the version number of this program.\n"
+ "-w<wrapped>: Command to call with start/stop at the beginning/end of\n"
+ " the program.\n"
+ "-x<diagnostic-selector>: Include extended diagnostics in the output.\n"
+ " <diagnostic-selector> is a string of single-keywords specifying\n"
+ " the operations for which verbose output is desired. The selector\n"
+ " keyletters are:\n"
+ " * 'a': print the decoded command line arguments\n"
+ " * 'e': print the exit reason\n"
+ " * 'i': print rate processing details\n"
+ " * 's': print first server-id\n"
+ " * 't': when finished, print timers of all successful exchanges\n"
+ " * 'T': when finished, print templates\n"
+ "-X<xid-offset>: Transaction ID (aka. xid) offset in the template.\n"
+ "\n"
+ "DHCPv4 only options:\n"
+ "-B: Force broadcast handling.\n"
+ "\n"
+ "DHCPv6 only options:\n"
+ "-c: Add a rapid commit option (exchanges will be SA).\n"
+ "\n"
+ "The remaining options are used only in conjunction with -r:\n"
+ "\n"
+ "-D<max-drop>: Abort the test if more than <max-drop> requests have\n"
+ " been dropped. Use -D0 to abort if even a single request has been\n"
+ " dropped. If <max-drop> includes the suffix '%', it specifies a\n"
+ " maximum percentage of requests that may be dropped before abort.\n"
+ " In this case, testing of the threshold begins after 10 requests\n"
+ " have been expected to be received.\n"
+ "-n<num-request>: Initiate <num-request> transactions. No report is\n"
+ " generated until all transactions have been initiated/waited-for,\n"
+ " after which a report is generated and the program terminates.\n"
+ "-p<test-period>: Send requests for the given test period, which is\n"
+ " specified in the same manner as -d. This can be used as an\n"
+ " alternative to -n, or both options can be given, in which case the\n"
+ " testing is completed when either limit is reached.\n"
+ "-t<report>: Delay in seconds between two periodic reports.\n"
+ "\n"
+ "Errors:\n"
+ "- tooshort: received a too short message\n"
+ "- orphans: received a message which doesn't match an exchange\n"
+ " (duplicate, late or not related)\n"
+ "- locallimit: reached to local system limits when sending a message.\n"
+ "\n"
+ "Exit status:\n"
+ "The exit status is:\n"
+ "0 on complete success.\n"
+ "1 for a general error.\n"
+ "2 if an error is found in the command line arguments.\n"
+ "3 if there are no general failures in operation, but one or more\n"
+ " exchanges are not successfully completed.\n";
}
void
diff --git a/tests/tools/perfdhcp/command_options.h b/tests/tools/perfdhcp/command_options.h
index b9e2b9e..4668f73 100644
--- a/tests/tools/perfdhcp/command_options.h
+++ b/tests/tools/perfdhcp/command_options.h
@@ -57,7 +57,8 @@ public:
/// \param argc Argument count passed to main().
/// \param argv Argument value array passed to main().
/// \throws isc::InvalidParameter if parse fails.
- void parse(int argc, char** const argv);
+ /// \return true if program has been run in help or version mode ('h' or 'v' flag).
+ bool parse(int argc, char** const argv);
/// \brief Returns IP version.
///
@@ -230,7 +231,7 @@ public:
///
/// \return server name.
std::string getServerName() const { return server_name_; }
-
+
/// \brief Print command line arguments.
void printCommandLine() const;
@@ -261,7 +262,8 @@ private:
/// \param argc Argument count passed to main().
/// \param argv Argument value array passed to main().
/// \throws isc::InvalidParameter if command line options initialization fails.
- void initialize(int argc, char** argv);
+ /// \return true if program has been run in help or version mode ('h' or 'v' flag).
+ bool initialize(int argc, char** argv);
/// \brief Validates initialized options.
///
@@ -349,7 +351,7 @@ private:
/// \param base Base string given as -b duid=0F1234.
/// \throws isc::InvalidParameter if DUID is invalid.
void decodeDuid(const std::string& base);
-
+
/// \brief Generates DUID-LLT (based on link layer address).
///
/// Function generates DUID based on link layer address and
@@ -364,65 +366,97 @@ private:
/// \throw isc::InvalidParameter if string does not represent hex byte.
uint8_t convertHexString(const std::string& hex_text) const;
- uint8_t ipversion_; ///< IP protocol version to be used, expected values are:
- ///< 4 for IPv4 and 6 for IPv6, default value 0 means "not set"
- ExchangeMode exchange_mode_; ///< Packet exchange mode (e.g. DORA/SARR)
- int rate_; ///< Rate in exchange per second
- int report_delay_; ///< Delay between generation of two consecutive
- ///< performance reports
- uint32_t clients_num_; ///< Number of simulated clients (aka randomization range).
- std::vector<uint8_t> mac_template_; ///< MAC address template used to generate unique DUIDs
- ///< for simulated clients.
- std::vector<uint8_t> duid_template_; ///< DUID template used to generate unique DUIDs for
- ///< simulated clients
- std::vector<std::string> base_; ///< Collection of base values specified with -b<value>
- ///< options. Supported "bases" are mac=<mac> and duid=<duid>
- std::vector<int> num_request_; ///< Number of 2 or 4-way exchanges to perform
- int period_; ///< Test period in seconds
- uint8_t drop_time_set_; ///< Indicates number of -d<value> parameters specified by user.
- ///< If this value goes above 2, command line parsing fails.
- std::vector<double> drop_time_; ///< Time to elapse before request is lost. The fisrt value of
- ///< two-element vector refers to DO/SA exchanges,
- ///< second value refers to RA/RR. Default values are { 1, 1 }
- std::vector<int> max_drop_; ///< Maximum number of drops request before aborting test.
- ///< First value of two-element vector specifies maximum
- ///< number of drops for DO/SA exchange, second value
- ///< specifies maximum number of drops for RA/RR.
- std::vector<double> max_pdrop_; ///< Maximal percentage of lost requests before aborting test.
- ///< First value of two-element vector specifies percentage for
- ///< DO/SA exchanges, second value for RA/RR.
- std::string localname_; ///< Local address or interface specified with -l<value> option.
- bool is_interface_; ///< Indicates that specified value with -l<value> is
- ///< rather interface (not address)
- int preload_; ///< Number of preload packets. Preload packets are used to
- ///< initiate communication with server before doing performance
- ///< measurements.
- int aggressivity_; ///< Number of exchanges sent before next pause.
- int local_port_; ///< Local port number (host endian)
- bool seeded_; ///< Indicates that randomization seed was provided.
- uint32_t seed_; ///< Randomization seed.
- bool broadcast_; ///< Indicates that we use broadcast address.
- bool rapid_commit_; ///< Indicates that we do rapid commit option.
- bool use_first_; ///< Indicates that we take server id from first received packet.
- std::vector<std::string> template_file_; ///< Packet template file names. These files store template packets
- ///< that are used for initiating echanges. Template packets
- ///< read from files are later tuned with variable data.
- std::vector<int> xid_offset_; ///< Offset of transaction id in template files. First vector
- ///< element points to offset for DISCOVER/SOLICIT messages,
- ///< second element points to trasaction id offset for
- ///< REQUEST messages
- std::vector<int> rnd_offset_; ///< Random value offset in templates. Random value offset
- ///< points to last octet of DUID. Up to 4 last octets of
- ///< DUID are randomized to simulate differnt clients.
- int elp_offset_; ///< Offset of elapsed time option in template packet.
- int sid_offset_; ///< Offset of server id option in template packet.
- int rip_offset_; ///< Offset of requested ip data in template packet/
- std::string diags_; ///< String representing diagnostic selectors specified
- ///< by user with -x<value>.
- std::string wrapped_; ///< Wrapped command specified as -w<value>. Expected
- ///< values are start and stop.
- std::string server_name_; ///< Server name specified as last argument of command line.
- std::string commandline_; ///< Entire command line as typed in by the user.
+ /// IP protocol version to be used, expected values are:
+ /// 4 for IPv4 and 6 for IPv6, default value 0 means "not set"
+ uint8_t ipversion_;
+ /// Packet exchange mode (e.g. DORA/SARR)
+ ExchangeMode exchange_mode_;
+ /// Rate in exchange per second
+ int rate_;
+ /// Delay between generation of two consecutive
+ /// performance reports
+ int report_delay_;
+ /// Number of simulated clients (aka randomization range).
+ uint32_t clients_num_;
+ /// MAC address template used to generate unique MAC
+ /// addresses for simulated clients.
+ std::vector<uint8_t> mac_template_;
+ /// DUID template used to generate unique DUIDs for
+ /// simulated clients
+ std::vector<uint8_t> duid_template_;
+ /// Collection of base values specified with -b<value>
+ /// options. Supported "bases" are mac=<mac> and duid=<duid>
+ std::vector<std::string> base_;
+ /// Number of 2 or 4-way exchanges to perform.
+ std::vector<int> num_request_;
+ /// Test period in seconds
+ int period_;
+ /// Indicates number of -d<value> parameters specified by user.
+ /// If this value goes above 2, command line parsing fails.
+ uint8_t drop_time_set_;
+ /// Time to elapse before request is lost. The fisrt value of
+ /// two-element vector refers to DO/SA exchanges,
+ /// second value refers to RA/RR. Default values are { 1, 1 }
+ std::vector<double> drop_time_;
+ /// Maximum number of drops request before aborting test.
+ /// First value of two-element vector specifies maximum
+ /// number of drops for DO/SA exchange, second value
+ /// specifies maximum number of drops for RA/RR.
+ std::vector<int> max_drop_;
+ /// Maximal percentage of lost requests before aborting test.
+ /// First value of two-element vector specifies percentage for
+ /// DO/SA exchanges, second value for RA/RR.
+ std::vector<double> max_pdrop_;
+ /// Local address or interface specified with -l<value> option.
+ std::string localname_;
+ /// Indicates that specified value with -l<value> is
+ /// rather interface (not address)
+ bool is_interface_;
+ /// Number of preload packets. Preload packets are used to
+ /// initiate communication with server before doing performance
+ /// measurements.
+ int preload_;
+ /// Number of exchanges sent before next pause.
+ int aggressivity_;
+ /// Local port number (host endian)
+ int local_port_;
+ /// Randomization seed.
+ uint32_t seed_;
+ /// Indicates that randomization seed was provided.
+ bool seeded_;
+ /// Indicates that we use broadcast address.
+ bool broadcast_;
+ /// Indicates that we do rapid commit option.
+ bool rapid_commit_;
+ /// Indicates that we take server id from first received packet.
+ bool use_first_;
+ /// Packet template file names. These files store template packets
+ /// that are used for initiating echanges. Template packets
+ /// read from files are later tuned with variable data.
+ std::vector<std::string> template_file_;
+ /// Offset of transaction id in template files. First vector
+ /// element points to offset for DISCOVER/SOLICIT messages,
+ /// second element points to trasaction id offset for
+ /// REQUEST messages
+ std::vector<int> xid_offset_;
+ /// Random value offset in templates. Random value offset
+ /// points to last octet of DUID. Up to 4 last octets of
+ /// DUID are randomized to simulate differnt clients.
+ std::vector<int> rnd_offset_;
+ /// Offset of elapsed time option in template packet.
+ int elp_offset_;
+ /// Offset of server id option in template packet.
+ int sid_offset_;
+ /// Offset of requested ip data in template packet
+ int rip_offset_;
+ /// String representing diagnostic selectors specified
+ /// by user with -x<value>.
+ std::string diags_;
+ /// Command to be executed at the beginning/end of the test.
+ /// This command is expected to expose start and stop argument.
+ std::string wrapped_;
+ /// Server name specified as last argument of command line.
+ std::string server_name_;
};
} // namespace perfdhcp
diff --git a/tests/tools/perfdhcp/main.cc b/tests/tools/perfdhcp/main.cc
index 0c706a2..aa202f1 100644
--- a/tests/tools/perfdhcp/main.cc
+++ b/tests/tools/perfdhcp/main.cc
@@ -26,25 +26,35 @@ using namespace isc::perfdhcp;
int
main(int argc, char* argv[]) {
CommandOptions& command_options = CommandOptions::instance();
+ std::string diags(command_options.getDiags());
+ int ret_code = 0;
try {
- command_options.parse(argc, argv);
+ // If parser returns true it means that user specified
+ // 'h' or 'v' command line option. Program shows the
+ // help or version message and exits here.
+ if (command_options.parse(argc, argv)) {
+ return (ret_code);
+ }
} catch(isc::Exception& e) {
- std::cout << "Error parsing command line options: "
+ ret_code = 1;
+ std::cerr << "Error parsing command line options: "
<< e.what() << std::endl;
command_options.usage();
- return(1);
+ if (diags.find('e') != std::string::npos) {
+ std::cerr << "Fatal error" << std::endl;
+ }
+ return (ret_code);
}
try{
TestControl& test_control = TestControl::instance();
- test_control.run();
+ ret_code = test_control.run();
} catch (isc::Exception& e) {
- std::cout << "Error running perfdhcp: " << e.what() << std::endl;
- std::string diags(command_options.getDiags());
+ ret_code = 1;
+ std::cerr << "Error running perfdhcp: " << e.what() << std::endl;
if (diags.find('e') != std::string::npos) {
- std::cout << "Fatal error" << std::endl;
+ std::cerr << "Fatal error" << std::endl;
}
- return(1);
}
- return(0);
+ return (ret_code);
}
diff --git a/tests/tools/perfdhcp/perfdhcp.c b/tests/tools/perfdhcp/perfdhcp.c
deleted file mode 100644
index 3ab9a2e..0000000
--- a/tests/tools/perfdhcp/perfdhcp.c
+++ /dev/null
@@ -1,3559 +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 <config.h>
-
-#ifndef HAVE_GETIFADDRS
-
-/*
- * Solaris 10 does not have the getifaddrs() function available (although it
- * is present on Solaris 11 and later). For the present we will not implement
- * a replacement (as we do with clock_gettime) as the implementation is
- * relatively complex. Just output a message saying that the utility is not
- * supported on this operating system.
- */
-
-#include <stdio.h>
-
-int
-main(const int argc, char* const argv[])
-{
- fprintf(stderr, "perfdhcp is not supported on this version of the operating system\n");
- return (1);
-}
-
-#else
-
-/* getifaddrs() present, so the code should compile */
-
-#ifdef __linux__
-#define _GNU_SOURCE
-#endif
-
-#include <sys/types.h>
-#include <sys/select.h>
-#include <sys/socket.h>
-#include <sys/wait.h>
-
-#include <net/if.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
-
-#include <ctype.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <ifaddrs.h>
-#include <math.h>
-#include <netdb.h>
-#include <signal.h>
-#include <stdint.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <time.h>
-#include <unistd.h>
-
-#ifndef HAVE_PSELECT
-
-#include <assert.h>
-
-/* Platforms such as OpenBSD don't provide a pselect(), so we use our
- own implementation for this testcase, which wraps around select() and
- hence doesn't implement the high precision timer. This implementation
- is fine for our purpose. */
-
-static int
-pselect (int nfds, fd_set *readfds, fd_set *writefds,
- fd_set *exceptfds, const struct timespec *timeout,
- const sigset_t *sigmask)
-{
- struct timeval my_timeout;
-
- /* Our particular usage of pselect() doesn't use these fields. */
- assert(writefds == NULL);
- assert(exceptfds == NULL);
- assert(sigmask == NULL);
-
- my_timeout.tv_sec = timeout->tv_sec;
- my_timeout.tv_usec = timeout->tv_nsec / 1000;
-
- return (select(nfds, readfds, writefds, exceptfds, &my_timeout));
-}
-
-#endif /* !HAVE_PSELECT */
-
-/* DHCPv4 defines (to be moved/shared) */
-
-#define DHCP_OFF_OPCODE 0
-#define DHCP_OFF_HTYPE 1
-#define DHCP_OFF_HLEN 2
-#define DHCP_OFF_HOPS 3
-#define DHCP_OFF_XID 4
-#define DHCP_OFF_SECS 8
-#define DHCP_OFF_FLAGS 10
-#define DHCP_OFF_CIADDR 12
-#define DHCP_OFF_YIADDR 16
-#define DHCP_OFF_SADDR 20
-#define DHCP_OFF_GIADDR 24
-#define DHCP_OFF_CHADDR 28
-#define DHCP_OFF_SNAME 44
-#define DHCP_OFF_FILE 108
-#define DHCP_OFF_COOKIE 236
-#define DHCP_OFF_OPTIONS 240
-
-#define BOOTP_OP_REQUEST 1
-#define BOOTP_OP_REPLY 2
-#define BOOTP_MIN_LEN 300
-
-#define DHCP_OP_DISCOVER 1
-#define DHCP_OP_OFFER 2
-#define DHCP_OP_REQUEST 3
-#define DHCP_OP_DECLINE 4
-#define DHCP_OP_ACK 5
-#define DHCP_OP_NAK 6
-#define DHCP_OP_RELEASE 7
-#define DHCP_OP_INFORM 8
-
-#define DHCP_HTYPE_ETHER 1
-
-#define DHCP_OPT_PAD 0
-#define DHCP_OPT_SUBNET_MASK 1
-#define DHCP_OPT_TIME_OFFSET 2
-#define DHCP_OPT_ROUTERS 3
-#define DHCP_OPT_DNS_SERVERS 6
-#define DHCP_OPT_HOST_NAME 12
-#define DHCP_OPT_DOMAIN_NAME 15
-#define DHCP_OPT_BROADCAST 28
-#define DHCP_OPT_DHCP_ADDRESS 50
-#define DHCP_OPT_DHCP_LEASE 51
-#define DHCP_OPT_DHCP_MSGTYPE 53
-#define DHCP_OPT_DHCP_SRVID 54
-#define DHCP_OPT_DHCP_PRL 55
-#define DHCP_OPT_END 255
-
-#define DHCP_OPTLEN_SRVID 6
-
-/* DHCPv6 defines (to be moved/shared) */
-
-#define DHCP6_OFF_MSGTYP 0
-#define DHCP6_OFF_XID 1
-#define DHCP6_OFF_OPTIONS 4
-
-#define DHCP6_OP_SOLICIT 1
-#define DHCP6_OP_ADVERTISE 2
-#define DHCP6_OP_REQUEST 3
-#define DHCP6_OP_REPLY 7
-
-#define DHCP6_OPT_CLIENTID 1
-#define DHCP6_OPT_SERVERID 2
-#define DHCP6_OPT_IA_NA 3
-#define DHCP6_OPT_ORO 6
-#define DHCP6_OPT_ELAPSED_TIME 8
-#define DHCP6_OPT_STATUS_CODE 13
-#define DHCP6_OPT_RAPID_COMMIT 14
-#define DHCP6_OPT_NAME_SERVERS 23
-#define DHCP6_OPT_DOMAIN_SEARCH 24
-
-#define DHCP6_ST_SUCCESS 0
-#define DHCP6_ST_NOADDRSAVAIL 2
-
-#define DHCP6_DUID_LLT 1
-#define DHCP6_DUID_EPOCH 946684800
-
-/* tail queue macros (from FreeBSD 8.2 /sys/sys/queue.h, to be moved/shared) */
-
-#define ISC_TAILQ_HEAD(name, type) \
-struct name { \
- struct type *tqh_first; \
- struct type **tqh_last; \
-}
-
-#define ISC_TAILQ_ENTRY(type) \
-struct { \
- struct type *tqe_next; \
- struct type **tqe_prev; \
-}
-
-#define ISC_TAILQ_EMPTY(head) ((head)->tqh_first == NULL)
-
-#define ISC_TAILQ_FIRST(head) ((head)->tqh_first)
-
-#define ISC_TAILQ_LAST(head, headname) \
- (*(((struct headname *)((head)->tqh_last))->tqh_last))
-
-#define ISC_TAILQ_NEXT(elm, field) ((elm)->field.tqe_next)
-
-#define ISC_TAILQ_PREV(elm, headname, field) \
- (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last))
-
-#define ISC_TAILQ_INIT(head) do { \
- ISC_TAILQ_FIRST((head)) = NULL; \
- (head)->tqh_last = &ISC_TAILQ_FIRST((head)); \
-} while (0)
-
-#define ISC_TAILQ_INSERT_HEAD(head, elm, field) do { \
- ISC_TAILQ_NEXT((elm), field) = ISC_TAILQ_FIRST((head)); \
- if (!ISC_TAILQ_EMPTY((head))) \
- ISC_TAILQ_FIRST((head))->field.tqe_prev = \
- &ISC_TAILQ_NEXT((elm), field); \
- else \
- (head)->tqh_last = &ISC_TAILQ_NEXT((elm), field); \
- ISC_TAILQ_FIRST((head)) = (elm); \
- (elm)->field.tqe_prev = &ISC_TAILQ_FIRST((head)); \
-} while (0)
-
-#define ISC_TAILQ_INSERT_TAIL(head, elm, field) do { \
- ISC_TAILQ_NEXT((elm), field) = NULL; \
- (elm)->field.tqe_prev = (head)->tqh_last; \
- *(head)->tqh_last = (elm); \
- (head)->tqh_last = &ISC_TAILQ_NEXT((elm), field); \
-} while (0)
-
-#define ISC_TAILQ_REMOVE(head, elm, field) do { \
- if ((ISC_TAILQ_NEXT((elm), field)) != NULL) \
- ISC_TAILQ_NEXT((elm), field)->field.tqe_prev = \
- (elm)->field.tqe_prev; \
- else \
- (head)->tqh_last = (elm)->field.tqe_prev; \
- *(elm)->field.tqe_prev = ISC_TAILQ_NEXT((elm), field); \
-} while (0)
-
-#define ISC_TAILQ_FOREACH(var, head, field) \
- for ((var) = ISC_TAILQ_FIRST((head)); \
- (var); \
- (var) = ISC_TAILQ_NEXT((var), field))
-
-#define ISC_TAILQ_FOREACH_SAFE(var, head, field, tvar) \
- for ((var) = ISC_TAILQ_FIRST((head)); \
- (var) && ((tvar) = ISC_TAILQ_NEXT((var), field), 1); \
- (var) = (tvar))
-
-/*
- * Data structures
- */
-
-/*
- * exchange:
- * - per exchange values:
- * * order (for debugging)
- * * xid (even/odd for 4 packet exchanges)
- * * random (for debugging)
- * * time-stamps
- * * server ID (for 3rd packet)
- * * IA_NA (for IPv6 3rd packet)
- *
- * sent/rcvd global chains, "next to be received" on entry cache,
- * and hash table for xid -> data structure fast matching
- * (using the assumption collisions are unlikely, cf birthday problem)
- */
-
-struct exchange { /* per exchange structure */
- ISC_TAILQ_ENTRY(exchange) gchain; /* global chaining */
- ISC_TAILQ_ENTRY(exchange) hchain; /* hash table chaining */
- uint64_t order0, order2; /* number of this exchange */
- uint32_t xid; /* transaction ID */
- uint32_t rnd; /* random part */
- struct timespec ts0, ts1, ts2, ts3; /* timespecs */
- uint8_t *sid; /* server ID */
- size_t sidlen; /* server ID length */
- uint8_t *iana; /* (IPv6) IA_NA */
- size_t ianalen; /* (IPv6) IA_NA length */
-};
-struct exchange *xnext0, *xnext2; /* next to be received */
-ISC_TAILQ_HEAD(xlist, exchange); /* exchange list */
-struct xlist xsent0, xsent2, xrcvd0, xrcvd2; /* sent and received lists */
-uint64_t xscount0, xscount2; /* sent counters */
-uint64_t xrcount0, xrcount2; /* received counters */
-caddr_t exchanges0, exchanges2; /* hash tables */
-uint32_t hashsize0, hashsize2; /* hash table sizes */
-
-/*
- * statictics counters and accumulators
- */
-
-uint64_t tooshort, orphans, locallimit; /* error counters */
-uint64_t latesent, compsend, latercvd; /* rate stats */
-uint64_t multrcvd, shortwait, collected[2]; /* rate stats (cont) */
-double dmin0 = 999999999., dmin2 = 999999999.; /* minimum delays */
-double dmax0 = 0., dmax2 = 0.; /* maximum delays */
-double dsum0 = 0., dsum2 = 0.; /* delay sums */
-double dsumsq0 = 0., dsumsq2 = 0.; /* square delay sums */
-
-/*
- * command line parameters
- */
-
-int ipversion = 0; /* IP version */
-int simple; /* DO/SA in place of DORR/SARR */
-int rate; /* rate in exchange per second */
-int report; /* delay between two reports */
-uint32_t range; /* randomization range */
-uint32_t maxrandom; /* maximum random value */
-int basecnt; /* base count */
-char *base[4]; /* bases */
-int gotnumreq; /* numreq[0] was set */
-int numreq[2]; /* number of exchange */
-int period; /* test period */
-int gotlosttime; /* losttime[0] was set */
-double losttime[2] = {1., 1.}; /* time after a request is lost */
-int gotmaxdrop; /* max{p}drop[0] was set */
-int maxdrop[2]; /* maximum number of lost requests */
-double maxpdrop[2] = { 0., 0.}; /* maximum percentage */
-char *localname; /* local address or interface */
-int isinterface; /* interface vs local address */
-int preload; /* preload exchanges */
-int aggressivity = 1; /* back to back exchanges */
-int localport; /* local port number (host endian) */
-int seeded; /* is a seed provided */
-unsigned int seed; /* randomization seed */
-int isbroadcast; /* use broadcast */
-int rapidcommit; /* add rapid commit option */
-int usefirst; /* where to take the server-ID */
-char *templatefile[2]; /* template file name */
-int xidoffset[2] = {-1, -1}; /* template offsets (xid)*/
-int rndoffset[2] = {-1, -1}; /* template offsets (random) */
-int elpoffset = -1; /* template offset (elapsed time) */
-int sidoffset = -1; /* template offset (server ID) */
-int ripoffset = -1; /* template offset (requested IP) */
-char *diags; /* diagnostic selectors */
-char *wrapped; /* wrapped command */
-char *servername; /* server */
-
-/*
- * global variables
- */
-
-struct sockaddr_storage localaddr; /* local socket address */
-struct sockaddr_storage serveraddr; /* server socket address */
-
-int sock; /* socket descriptor */
-int interrupted, fatal; /* to finish flags */
-
-uint8_t obuf[4096], ibuf[4096]; /* I/O buffers */
-char tbuf[8200]; /* template buffer */
-
-struct timespec boot; /* the date of boot */
-struct timespec last; /* the date of last send */
-struct timespec due; /* the date of next send */
-struct timespec dreport; /* the date of next reporting */
-struct timespec finished; /* the date of finish */
-
-uint8_t *gsrvid; /* global server id */
-size_t gsrvidlen; /* and its length */
-uint8_t gsrvidbuf[64]; /* and its storage */
-
-/* MAC address */
-uint8_t mac_prefix[6] = { 0x00, 0x0c, 0x01, 0x02, 0x03, 0x04 };
-
-/* DUID prefix */
-uint8_t *duid_prefix;
-int duid_length;
-
-/* magic cookie for BOOTP/DHCPv4 */
-uint8_t dhcp_cookie[4] = { 0x63, 0x82, 0x53, 0x63 };
-
-/*
- * templates
- *
- * note: the only hard point is what are the offsets:
- * - xid_discover4 and xid_request4: first of the 4 octet long
- * transaction ID (default DHCP_OFF_XID = 4)
- * - random_discover4 and random_request4: last of the 6 octet long
- * MAC address (default DHCP_OFF_CHADDR + 6 = 28 + 6)
- * - elapsed_request4: first of the 2 octet long secs field
- * (default DHCP_OFF_SECS = 8, 0 means disabled)
- * - serverid_request4: first of the 6 octet long server ID option
- * (no default, required)
- * - reqaddr_request4: first of the 4 octet long requested IP address
- * option content (i.e., the address itself, btw OFFER yiaddr)
- * (no default, required)
- * - xid_solicit6 and xid_request6: first of the 3 octet long
- * transaction ID (default DHCP6_OFF_XID = 1)
- * - random_solicit6 and random_request6: last of the DUID in the
- * client ID option (no default, required when rate is set)
- * - elapsed_request6: first of the 2 octet long content of
- * the option elapsed time option (no default, 0 means disabled)
- * - serverid_request6: position where the variable length server ID
- * option is inserted (no default, required, set to length means append)
- * - reqaddr_request6: position where of the variable length requested
- * IP address option is inserted (no default, required, set to
- * length means append)
- */
-
-size_t length_discover4;
-uint8_t template_discover4[4096];
-size_t xid_discover4;
-size_t random_discover4;
-size_t length_request4;
-uint8_t template_request4[4096];
-size_t xid_request4;
-size_t elapsed_request4;
-size_t random_request4;
-size_t serverid_request4;
-size_t reqaddr_request4;
-size_t length_solicit6;
-uint8_t template_solicit6[4096];
-size_t xid_solicit6;
-size_t random_solicit6;
-size_t length_request6;
-uint8_t template_request6[4096];
-size_t xid_request6;
-size_t elapsed_request6;
-size_t random_request6;
-size_t serverid_request6;
-size_t reqaddr_request6;
-
-
-// use definition of CLOCK_REALTIME (or lack of thereof) as an indicator
-// if the code is being compiled or Linux (or somewhere else)
-// Perhaps this should be based on OS_LINUX define?
-
-#if !defined (CLOCK_REALTIME)
-#define CLOCK_REALTIME 0
-
-/// @brief clock_gettime implementation for non-Linux systems
-///
-/// This implementation lacks nanosecond resolution. It is intended
-/// to be used on non-Linux systems that does not provide clock_gettime
-/// implementation.
-///
-/// @param clockid ignored (kept for Linux prototype compatibility)
-/// @param tp timespec structure
-///
-/// @return always zero (kept for compatibility reasons)
-int clock_gettime(int clockid, struct timespec *tp) {
-
- struct timeval tv;
- gettimeofday(&tv, NULL);
- tp->tv_sec = tv.tv_sec;
- tp->tv_nsec = tv.tv_usec*1000;
-
- return (0);
-}
-
-#endif
-
-/*
- * initialize data structures handling exchanges
- */
-
-void
-inits(void)
-{
- struct xlist *bucket;
- caddr_t p;
- size_t len, i;
-
- ISC_TAILQ_INIT(&xsent0);
- ISC_TAILQ_INIT(&xsent2);
- ISC_TAILQ_INIT(&xrcvd0);
- ISC_TAILQ_INIT(&xrcvd2);
-
- /// compute hashsizes
- hashsize0 = 1024;
- len = sizeof(*bucket) * hashsize0;
- exchanges0 = malloc(len);
- if (exchanges0 == NULL) {
- perror("malloc(exchanges0)");
- exit(1);
- }
- for (i = 0, p = exchanges0; i < hashsize0; i++, p += sizeof(*bucket)) {
- bucket = (struct xlist *) p;
- ISC_TAILQ_INIT(bucket);
- }
- if (simple != 0)
- return;
- hashsize2 = 1024;
- len = sizeof(*bucket) * hashsize2;
- exchanges2 = malloc(len);
- if (exchanges2 == NULL) {
- perror("malloc(exchanges2)");
- exit(1);
- }
- for (i = 0, p = exchanges2; i < hashsize2; i++, p += sizeof(*bucket)) {
- bucket = (struct xlist *) p;
- ISC_TAILQ_INIT(bucket);
- }
-}
-
-/*
- * randomize the value of the given field:
- * - offset of the field
- * - random seed (used as it when suitable)
- * - returns the random value which was used
- */
-
-uint32_t
-randomize(size_t offset, uint32_t r)
-{
- uint32_t v;
-
- if (range == 0)
- return 0;
- if (range == UINT32_MAX)
- return r;
- if (maxrandom != 0)
- while (r >= maxrandom)
- r = (uint32_t) random();
- r %= range + 1;
- v = r;
- v += obuf[offset];
- obuf[offset] = v;
- if (v < 256)
- return r;
- v >>= 8;
- v += obuf[offset - 1];
- obuf[offset - 1] = v;
- if (v < 256)
- return r;
- v >>= 8;
- v += obuf[offset - 2];
- obuf[offset - 2] = v;
- if (v < 256)
- return r;
- v >>= 8;
- v += obuf[offset - 3];
- obuf[offset - 3] = v;
- return r;
-}
-
-/*
- * receive a reply (4th packet), shared between IPv4 and IPv6:
- * - transaction ID xid
- * - receiving time-stamp now
- * called from receive[46]() when the xid is odd
- */
-
-void
-receive_reply(uint32_t xid, struct timespec *now)
-{
- struct exchange *x, *t;
- struct xlist *bucket;
- uint32_t hash;
- int checklost;
- double delta;
-
- /* bucket is needed even when the next cache matches */
- hash = (xid >> 1) & (hashsize2 - 1);
- bucket = (struct xlist *) (exchanges2 + hash * sizeof(*bucket));
- /* try the 'next to be received' cache */
- if ((xnext2 != NULL) && (xnext2->xid == xid)) {
- x = xnext2;
- goto found;
- }
- /* usually the lost probability is low for request/reply */
- checklost = 1;
- /* look for the exchange */
- ISC_TAILQ_FOREACH_SAFE(x, bucket, hchain, t) {
- double waited;
-
- if (x->xid == xid)
- goto found;
- if (checklost <= 0)
- continue;
- checklost = 0;
- /* check for a timed-out exchange */
- waited = now->tv_sec - x->ts2.tv_sec;
- waited += (now->tv_nsec - x->ts2.tv_nsec) / 1e9;
- if (waited < losttime[1])
- continue;
- /* garbage collect timed-out exchange */
- ISC_TAILQ_REMOVE(bucket, x, hchain);
- ISC_TAILQ_REMOVE(&xsent2, x, gchain);
- free(x);
- collected[1] += 1;
- }
- /* no match? very late or not for us */
- orphans++;
- return;
-
- /* got it: update stats and move to the received queue */
- found:
- xrcount2++;
- x->ts3 = *now;
- delta = x->ts3.tv_sec - x->ts2.tv_sec;
- delta += (x->ts3.tv_nsec - x->ts2.tv_nsec) / 1e9;
- if (delta < dmin2)
- dmin2 = delta;
- if (delta > dmax2)
- dmax2 = delta;
- dsum2 += delta;
- dsumsq2 += delta * delta;
- xnext2 = ISC_TAILQ_NEXT(x, gchain);
- ISC_TAILQ_REMOVE(bucket, x, hchain);
- ISC_TAILQ_REMOVE(&xsent2, x, gchain);
- ISC_TAILQ_INSERT_TAIL(&xrcvd2, x, gchain);
-}
-
-/*
- * get the DHCPv4 socket descriptor
- * (the only complexity is broadcast enabling: there is no easy way to
- * recognize broadcast addresses, so the command line -B flag)
- */
-
-void
-getsock4(void)
-{
- int ret;
-
- /* update local port */
- if (localport != 0) {
- uint16_t lp = htons((uint16_t) localport);
-
- ((struct sockaddr_in *) &localaddr)->sin_port = lp;
- }
- sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
- if (sock < 0) {
- perror("socket");
- exit(1);
- }
- ret = bind(sock,
- (struct sockaddr *) &localaddr,
- sizeof(struct sockaddr_in));
- if (ret < 0) {
- perror("bind");
- exit(1);
- }
- /* enable broadcast if needed or required */
- if (isbroadcast != 0) {
- int on = 1;
-
- ret = setsockopt(sock,
- SOL_SOCKET, SO_BROADCAST,
- &on, sizeof(on));
- if (ret < 0) {
- perror("setsockopt(SO_BROADCAST)");
- exit(1);
- }
- }
-}
-
-/*
- * build a DHCPv4 DISCOVER from a relay template
- * (implicit parameters are the local (giaddr) and MAC addresses (chaddr))
- * (assume the link is Ethernet)
- */
-
-void
-build_template_discover4(void)
-{
- uint8_t *p = template_discover4;
-
- length_discover4 = BOOTP_MIN_LEN;
- xid_discover4 = DHCP_OFF_XID;
- random_discover4 = DHCP_OFF_CHADDR + 6;
- /* opcode */
- p[DHCP_OFF_OPCODE] = BOOTP_OP_REQUEST;
- /* hardware address type */
- p[DHCP_OFF_HTYPE] = DHCP_HTYPE_ETHER;
- /* hardware address length */
- p[DHCP_OFF_HLEN] = 6;
- /* hops */
- p[DHCP_OFF_HOPS] = 1;
- /* gateway address */
- memcpy(p + DHCP_OFF_GIADDR,
- &((struct sockaddr_in *) &localaddr)->sin_addr,
- 4);
- /* hardware address */
- memcpy(p + DHCP_OFF_CHADDR, mac_prefix, 6);
- /* cookie */
- memcpy(p + DHCP_OFF_COOKIE, dhcp_cookie, 4);
- /* options */
- p += DHCP_OFF_OPTIONS;
- /* inline DHCP message type */
- *p++ = DHCP_OPT_DHCP_MSGTYPE;
- *p++ = 1;
- *p++ = DHCP_OP_DISCOVER;
- /* inline DHCP parameter request list (default) */
- *p++ = DHCP_OPT_DHCP_PRL;
- *p++ = 7;
- *p++ = DHCP_OPT_SUBNET_MASK;
- *p++ = DHCP_OPT_BROADCAST;
- *p++ = DHCP_OPT_TIME_OFFSET;
- *p++ = DHCP_OPT_ROUTERS;
- *p++ = DHCP_OPT_DOMAIN_NAME;
- *p++ = DHCP_OPT_DNS_SERVERS;
- *p++ = DHCP_OPT_HOST_NAME;
- /* end */
- *p = DHCP_OPT_END;
-}
-
-/*
- * get a DHCPv4 client/relay first packet (usually a DISCOVER) template
- * from the file given in the command line (-T<template-file>)
- * and xid/rnd offsets (-X<xid-offset> and -O<random-offset>)
- */
-
-void
-get_template_discover4(void)
-{
- uint8_t *p = template_discover4;
- int fd, cc, i, j;
-
- fd = open(templatefile[0], O_RDONLY);
- if (fd < 0) {
- fprintf(stderr, "open(%s): %s\n",
- templatefile[0], strerror(errno));
- exit(2);
- }
- cc = read(fd, tbuf, sizeof(tbuf));
- (void) close(fd);
- if (cc < 0) {
- fprintf(stderr, "read(%s): %s\n",
- templatefile[0], strerror(errno));
- exit(1);
- }
- if (cc < 100) {
- fprintf(stderr, "file '%s' too short\n", templatefile[0]);
- exit(2);
- }
- if (cc > 8193) {
- fprintf(stderr,"file '%s' too large\n", templatefile[0]);
- exit(2);
- }
- j = 0;
- for (i = 0; i < cc; i++) {
- if (isspace((int) tbuf[i]))
- continue;
- if (!isxdigit((int) tbuf[i])) {
- fprintf(stderr,
- "illegal char[%d]='%c' in file '%s'\n",
- i, (int) tbuf[i], templatefile[0]);
- exit(2);
- }
- tbuf[j] = tbuf[i];
- j++;
- }
- cc = j;
- if ((cc & 1) != 0) {
- fprintf(stderr,
- "odd number of hexadecimal digits in file '%s'\n",
- templatefile[0]);
- exit(2);
- }
- length_discover4 = cc >> 1;
- for (i = 0; i < cc; i += 2)
- (void) sscanf(tbuf + i, "%02hhx", &p[i >> 1]);
- if (xidoffset[0] >= 0)
- xid_discover4 = (size_t) xidoffset[0];
- else
- xid_discover4 = DHCP_OFF_XID;
- if (xid_discover4 + 4 > length_discover4) {
- fprintf(stderr,
- "xid (at %zu) outside the template (length %zu)?\n",
- xid_discover4, length_discover4);
- exit(2);
- }
- if (rndoffset[0] >= 0)
- random_discover4 = (size_t) rndoffset[0];
- else
- random_discover4 = DHCP_OFF_CHADDR + 6;
- if (random_discover4 > length_discover4) {
- fprintf(stderr,
- "random (at %zu) outside the template (length %zu)?\n",
- random_discover4, length_discover4);
- exit(2);
- }
-}
-
-/*
- * build a DHCPv4 REQUEST from a relay template
- * (implicit parameters are the local (giaddr) and MAC addresses (chaddr))
- * (assume the link is Ethernet)
- */
-
-void
-build_template_request4(void)
-{
- uint8_t *p = template_request4;
-
- length_request4 = BOOTP_MIN_LEN;
- xid_request4 = DHCP_OFF_XID;
- elapsed_request4 = DHCP_OFF_SECS;
- random_request4 = DHCP_OFF_CHADDR + 6;
- /* opcode */
- p[DHCP_OFF_OPCODE] = BOOTP_OP_REQUEST;
- /* hardware address type */
- p[DHCP_OFF_HTYPE] = DHCP_HTYPE_ETHER;
- /* hardware address length */
- p[DHCP_OFF_HLEN] = 6;
- /* hops */
- p[DHCP_OFF_HOPS] = 1;
- /* gateway address */
- memcpy(p + DHCP_OFF_GIADDR,
- &((struct sockaddr_in *) &localaddr)->sin_addr,
- 4);
- /* hardware address */
- memcpy(p + DHCP_OFF_CHADDR, mac_prefix, 6);
- /* cookie */
- memcpy(p + DHCP_OFF_COOKIE, dhcp_cookie, 4);
- /* options */
- p += DHCP_OFF_OPTIONS;
- /* inline DHCP message type */
- *p++ = DHCP_OPT_DHCP_MSGTYPE;
- *p++ = 1;
- *p++ = DHCP_OP_REQUEST;
- /* place for DHCP server id (option) */
- serverid_request4 = p - template_request4;
- p += DHCP_OPTLEN_SRVID;
- /* place for DHCP requested IP address (address) */
- *p++ = DHCP_OPT_DHCP_ADDRESS;
- *p++ = 4;
- reqaddr_request4 = p - template_request4;
- p += 4;
- /* inline DHCP parameter request list (default) */
- *p++ = DHCP_OPT_DHCP_PRL;
- *p++ = 7;
- *p++ = DHCP_OPT_SUBNET_MASK;
- *p++ = DHCP_OPT_BROADCAST;
- *p++ = DHCP_OPT_TIME_OFFSET;
- *p++ = DHCP_OPT_ROUTERS;
- *p++ = DHCP_OPT_DOMAIN_NAME;
- *p++ = DHCP_OPT_DNS_SERVERS;
- *p++ = DHCP_OPT_HOST_NAME;
- /* end */
- *p = DHCP_OPT_END;
-}
-
-/*
- * get a DHCPv4 client/relay third packet (usually a REQUEST) template
- * from the file given in the command line (-T<template-file>)
- * and offsets (-X,-O,-E,-S,-I).
- */
-
-void
-get_template_request4(void)
-{
- uint8_t *p = template_request4;
- int fd, cc, i, j;
-
- fd = open(templatefile[1], O_RDONLY);
- if (fd < 0) {
- fprintf(stderr, "open(%s): %s\n",
- templatefile[1], strerror(errno));
- exit(2);
- }
- cc = read(fd, tbuf, sizeof(tbuf));
- (void) close(fd);
- if (cc < 0) {
- fprintf(stderr, "read(%s): %s\n",
- templatefile[1], strerror(errno));
- exit(1);
- }
- if (cc < 100) {
- fprintf(stderr, "file '%s' too short\n", templatefile[1]);
- exit(2);
- }
- if (cc > 8193) {
- fprintf(stderr,"file '%s' too large\n", templatefile[1]);
- exit(2);
- }
- j = 0;
- for (i = 0; i < cc; i++) {
- if (isspace((int) tbuf[i]))
- continue;
- if (!isxdigit((int) tbuf[i])) {
- fprintf(stderr,
- "illegal char[%d]='%c' in file '%s'\n",
- i, (int) tbuf[i], templatefile[1]);
- exit(2);
- }
- tbuf[j] = tbuf[i];
- j++;
- }
- cc = j;
- if ((cc & 1) != 0) {
- fprintf(stderr,
- "odd number of hexadecimal digits in file '%s'\n",
- templatefile[1]);
- exit(2);
- }
- length_request4 = cc >> 1;
- for (i = 0; i < cc; i += 2)
- (void) sscanf(tbuf + i, "%02hhx", &p[i >> 1]);
- if (xidoffset[1] >= 0)
- xid_request4 = (size_t) xidoffset[1];
- else
- xid_request4 = DHCP_OFF_XID;
- if (xid_request4 + 4 > length_request4) {
- fprintf(stderr,
- "xid (at %zu) outside the template (length %zu)?\n",
- xid_request4, length_request4);
- exit(2);
- }
- if (rndoffset[1] >= 0)
- random_request4 = (size_t) rndoffset[1];
- else
- random_request4 = DHCP_OFF_CHADDR + 6;
- if (random_request4 > length_request4) {
- fprintf(stderr,
- "random (at %zu) outside the template (length %zu)?\n",
- random_request4, length_request4);
- exit(2);
- }
- if (elpoffset >= 0)
- elapsed_request4 = (size_t) elpoffset;
- else
- elapsed_request4 = DHCP_OFF_SECS;
- if (elapsed_request4 + 2 > length_request4) {
- fprintf(stderr,
- "secs (at %zu) outside the template (length %zu)?\n",
- elapsed_request4, length_request4);
- exit(2);
- }
- serverid_request4 = (size_t) sidoffset;
- if (serverid_request4 + 6 > length_request4) {
- fprintf(stderr,
- "server-id option (at %zu) outside the template "
- "(length %zu)?\n",
- serverid_request4, length_request4);
- exit(2);
- }
- reqaddr_request4 = (size_t) ripoffset;
- if (reqaddr_request4 + 4 > length_request4) {
- fprintf(stderr,
- "requested-ip-address option (at %zu) outside "
- "the template (length %zu)?\n",
- reqaddr_request4, length_request4);
- exit(2);
- }
-}
-
-/*
- * send the DHCPv4 REQUEST third packet
- * (the transaction ID is odd)
- * (TODO: check for errors in the OFFER)
- */
-
-void
-send_request4(struct exchange *x0)
-{
- struct exchange *x;
- struct xlist *bucket;
- uint32_t hash;
- ssize_t ret;
-
- x = (struct exchange *) malloc(sizeof(*x));
- if (x == NULL) {
- locallimit++;
- perror("send2");
- return;
- }
-
- memcpy(x, x0, sizeof(*x));
- x->order2 = xscount2++;
- x->xid |= 1;
- hash = x->xid >> 1;
-
- ISC_TAILQ_INSERT_TAIL(&xsent2, x, gchain);
- hash &= hashsize2 - 1;
- bucket = (struct xlist *) (exchanges2 + hash * sizeof(*bucket));
- ISC_TAILQ_INSERT_TAIL(bucket, x, hchain);
-
- memcpy(obuf, template_request4, length_request4);
- /* xid */
- memcpy(obuf + xid_request4, &x->xid, 4);
- /* random */
- randomize(random_request4, x->rnd);
- /* secs */
- if (elapsed_request4 > 0) {
- int secs;
-
- secs = x->ts1.tv_sec - x->ts0.tv_sec;
- if (x->ts1.tv_nsec < x->ts0.tv_nsec)
- secs += 1;
- if (secs > 0) {
- obuf[elapsed_request4] = secs >> 8;
- obuf[elapsed_request4 + 1] = secs & 0xff;
- }
- }
- /* server ID */
- memcpy(obuf + serverid_request4, x->sid, x->sidlen);
- /* requested IP address */
- memcpy(obuf + reqaddr_request4, ibuf + DHCP_OFF_YIADDR, 4);
-
- /* timestamp */
- ret = clock_gettime(CLOCK_REALTIME, &x->ts2);
- if (ret < 0) {
- perror("clock_gettime(send2)");
- fatal = 1;
- return;
- }
- ret = sendto(sock, obuf, length_request4, 0,
- (struct sockaddr *) &serveraddr,
- sizeof(struct sockaddr_in));
- if (ret >= 0)
- return;
- if ((errno == EAGAIN) || (errno == EWOULDBLOCK) ||
- (errno == ENOBUFS) || (errno == ENOMEM))
- locallimit++;
- perror("send2");
-}
-
-/*
- * send the DHCPv4 DISCOVER first packet
- * (for 4-exchange, the transaction ID xid is even)
- */
-
-int
-send4(void)
-{
- struct exchange *x;
- struct xlist *bucket;
- uint32_t hash;
- ssize_t ret;
-
- x = (struct exchange *) malloc(sizeof(*x));
- if (x == NULL)
- return -ENOMEM;
-
- memset(x, 0, sizeof(*x));
- x->order0 = xscount0++;
- hash = x->rnd = (uint32_t) random();
- if (simple == 0)
- x->xid = hash << 1;
- else
- x->xid = hash;
-
- ISC_TAILQ_INSERT_TAIL(&xsent0, x, gchain);
- hash &= hashsize0 - 1;
- bucket = (struct xlist *) (exchanges0 + hash * sizeof(*bucket));
- ISC_TAILQ_INSERT_TAIL(bucket, x, hchain);
-
- memcpy(obuf, template_discover4, length_discover4);
- /* xid */
- memcpy(obuf + xid_discover4, &x->xid, 4);
- /* random */
- x->rnd = randomize(random_discover4, x->rnd);
- /* timestamp */
- ret = clock_gettime(CLOCK_REALTIME, &last);
- if (ret < 0) {
- perror("clock_gettime(send)");
- fatal = 1;
- return -errno;
- }
- x->ts0 = last;
- errno = 0;
- ret = sendto(sock, obuf, length_discover4, 0,
- (struct sockaddr *) &serveraddr,
- sizeof(struct sockaddr_in));
- if (ret == (ssize_t) length_discover4)
- return 0;
- return -errno;
-}
-
-/*
- * scan a DHCPv4 OFFER to get its server-id option
- */
-
-int
-scan_for_srvid4(struct exchange *x, size_t cc)
-{
- size_t off = DHCP_OFF_OPTIONS;
-
- for (;;) {
- if (off + DHCP_OPTLEN_SRVID > cc) {
- fprintf(stderr, "truncated\n");
- return -1;
- }
- if (ibuf[off] == DHCP_OPT_DHCP_SRVID)
- break;
- if (ibuf[off] == DHCP_OPT_END) {
- fprintf(stderr, "server-id not found\n");
- return -1;
- }
- if (ibuf[off] == DHCP_OPT_PAD) {
- off++;
- continue;
- }
- off += 2 + ibuf[off + 1];
- }
- /* check length */
- if (ibuf[off + 1] != DHCP_OPTLEN_SRVID - 2) {
- fprintf(stderr,
- "bad server-id length (%hhu)\n",
- ibuf[off + 1]);
- return -1;
- }
- /* cache it in the global variables when required and not yet done */
- if ((usefirst != 0) && (gsrvid == NULL)) {
- memcpy(gsrvidbuf, ibuf + off, DHCP_OPTLEN_SRVID);
- gsrvid = gsrvidbuf;
- gsrvidlen = DHCP_OPTLEN_SRVID;
- }
- x->sid = ibuf + off;
- x->sidlen = DHCP_OPTLEN_SRVID;
- return 0;
-}
-
-/*
- * receive a DHCPv4 packet
- */
-
-void
-receive4(void)
-{
- struct exchange *x, *t;
- struct xlist *bucket;
- struct timespec now;
- ssize_t cc;
- uint32_t xid, hash;
- int checklost = 0;
- double delta;
-
- cc = recv(sock, ibuf, sizeof(ibuf), 0);
- if (cc < 0) {
- if ((errno == EAGAIN) ||
- (errno == EWOULDBLOCK) ||
- (errno == EINTR))
- return;
- perror("recv");
- fatal = 1;
- return;
- }
- /* enforce a reasonable length */
- if (cc < BOOTP_MIN_LEN) {
- tooshort++;
- return;
- }
- /* must be a BOOTP REPLY */
- if (ibuf[DHCP_OFF_OPCODE] != BOOTP_OP_REPLY)
- return;
- if (clock_gettime(CLOCK_REALTIME, &now) < 0) {
- perror("clock_gettime(receive)");
- fatal = 1;
- return;
- }
- memcpy(&xid, ibuf + xid_discover4, 4);
- /* 4-packet exchange even/odd xid */
- if (simple == 0) {
- if ((xid & 1) != 0) {
- receive_reply(xid, &now);
- return;
- }
- hash = (xid >> 1) & (hashsize0 - 1);
- } else
- hash = xid & (hashsize0 - 1);
- /* now it is the second packet, get the bucket which is needed */
- bucket = (struct xlist *) (exchanges0 + hash * sizeof(*bucket));
- /* try the 'next to be received' cache */
- if ((xnext0 != NULL) && (xnext0->xid == xid)) {
- x = xnext0;
- goto found;
- }
- /* if the rate is not illimited, garbage collect up to 3
- timed-out exchanges */
- if (rate != 0)
- checklost = 3;
- /* look for the exchange */
- ISC_TAILQ_FOREACH_SAFE(x, bucket, hchain, t) {
- double waited;
-
- if (x->xid == xid)
- goto found;
- if (checklost <= 0)
- continue;
- /* check for a timed-out exchange */
- waited = now.tv_sec - x->ts0.tv_sec;
- waited += (now.tv_nsec - x->ts0.tv_nsec) / 1e9;
- if (waited < losttime[0]) {
- checklost = 0;
- continue;
- }
- /* garbage collect timed-out exchange */
- checklost--;
- ISC_TAILQ_REMOVE(bucket, x, hchain);
- ISC_TAILQ_REMOVE(&xsent0, x, gchain);
- free(x);
- collected[0] += 1;
- }
- /* no match? very late or not for us */
- orphans++;
- return;
-
- /* got it: update stats and move to the received queue */
- found:
- xrcount0++;
- x->ts1 = now;
- delta = x->ts1.tv_sec - x->ts0.tv_sec;
- delta += (x->ts1.tv_nsec - x->ts0.tv_nsec) / 1e9;
- if (delta < dmin0)
- dmin0 = delta;
- if (delta > dmax0)
- dmax0 = delta;
- dsum0 += delta;
- dsumsq0 += delta * delta;
- xnext0 = ISC_TAILQ_NEXT(x, gchain);
- ISC_TAILQ_REMOVE(bucket, x, hchain);
- ISC_TAILQ_REMOVE(&xsent0, x, gchain);
- ISC_TAILQ_INSERT_TAIL(&xrcvd0, x, gchain);
- /* if the exchange is not finished, go to the second part */
- if (simple == 0) {
- int ret = 0;
-
- /* the server-ID option is needed */
- if ((usefirst != 0) && (gsrvid != NULL)) {
- x->sid = gsrvid;
- x->sidlen = gsrvidlen;
- } else
- ret = scan_for_srvid4(x, cc);
- if (ret >= 0)
- send_request4(x);
- }
-}
-
-/*
- * get the DHCPv6 socket descriptor
- */
-
-void
-getsock6(void)
-{
- struct sockaddr_in6 *s6 = (struct sockaddr_in6 *) &serveraddr;
- int ret;
-
- /* update local port */
- if (localport != 0) {
- uint16_t lp = htons((uint16_t) localport);
-
- ((struct sockaddr_in6 *) &localaddr)->sin6_port = lp;
- }
- sock = socket(PF_INET6, SOCK_DGRAM, IPPROTO_UDP);
- if (sock < 0) {
- perror("socket");
- exit(1);
- }
- ret = bind(sock,
- (struct sockaddr *) &localaddr,
- sizeof(struct sockaddr_in6));
- if (ret < 0) {
- perror("Failed to bind v6 socket to local-link address");
- exit(1);
- }
- /* perform the multicast stuff when the destination is multicast */
- if (IN6_IS_ADDR_MULTICAST(&s6->sin6_addr)) {
- int hops = 1;
-
- ret = setsockopt(sock,
- IPPROTO_IPV6, IPV6_MULTICAST_HOPS,
- &hops, sizeof(hops));
- if (ret < 0) {
- perror("setsockopt(IPV6_MULTICAST_HOPS)");
- exit(1);
- }
- }
- if (isinterface && IN6_IS_ADDR_MULTICAST(&s6->sin6_addr)) {
- unsigned idx = if_nametoindex(localname);
-
- if (idx == 0) {
- fprintf(stderr,
- "if_nametoindex(%s) failed\n",
- localname);
- exit(1);
- }
- ret = setsockopt(sock,
- IPPROTO_IPV6, IPV6_MULTICAST_IF,
- &idx, sizeof(idx));
- if (ret < 0) {
- perror("setsockopt(IPV6_MULTICAST_IF)");
- exit(1);
- }
- }
-}
-
-/*
- * build a DHCPv6 SOLICIT template
- * (implicit parameter is the DUID, don't assume an Ethernet link)
- */
-
-void
-build_template_solicit6(void)
-{
- uint8_t *p = template_solicit6;
-
- xid_solicit6 = DHCP6_OFF_XID;
- /* message type */
- p[DHCP6_OFF_MSGTYP] = DHCP6_OP_SOLICIT;
- /* options */
- p += DHCP6_OFF_OPTIONS;
- /* elapsed time */
- p[1] = DHCP6_OPT_ELAPSED_TIME;
- p[3] = 2;
- p += 6;
- /* rapid commit */
- if (rapidcommit != 0) {
- p[1] = DHCP6_OPT_RAPID_COMMIT;
- p += 4;
- }
- /* client ID */
- p[1] = DHCP6_OPT_CLIENTID;
- p[3] = duid_length;
- memcpy(p + 4, duid_prefix, duid_length);
- p += 4 + duid_length;
- random_solicit6 = p - template_solicit6;
- /* option request option */
- p[1] = DHCP6_OPT_ORO;
- p[3] = 4;
- p[5] = DHCP6_OPT_NAME_SERVERS;
- p[7] = DHCP6_OPT_DOMAIN_SEARCH;
- p += 8;
- /* IA_NA (IAID = 1, T1 = 3600, T2 = 5400) */
- p[1] = DHCP6_OPT_IA_NA;
- p[3] = 12;
- p[7] = 1;
- p[10] = 3600 >> 8;
- p[11] = 3600 & 0xff;
- p[14] = 5400 >> 8;
- p[15] = 5400 & 0xff;
- p += 16;
- /* set length */
- length_solicit6 = p - template_solicit6;
-}
-
-/*
- * get a DHCPv6 first packet (usually a SOLICIT) template
- * from the file given in the command line (-T<template-file>)
- * and xid/rnd offsets (-X<xid-offset> and -O<random-offset>)
- */
-
-void
-get_template_solicit6(void)
-{
- uint8_t *p = template_solicit6;
- int fd, cc, i, j;
-
- fd = open(templatefile[0], O_RDONLY);
- if (fd < 0) {
- fprintf(stderr, "open(%s): %s\n",
- templatefile[0], strerror(errno));
- exit(2);
- }
- cc = read(fd, tbuf, sizeof(tbuf));
- (void) close(fd);
- if (cc < 0) {
- fprintf(stderr, "read(%s): %s\n",
- templatefile[0], strerror(errno));
- exit(1);
- }
- if (cc < 10) {
- fprintf(stderr, "file '%s' too short\n", templatefile[0]);
- exit(2);
- }
- if (cc > 8193) {
- fprintf(stderr,"file '%s' too large\n", templatefile[0]);
- exit(2);
- }
- j = 0;
- for (i = 0; i < cc; i++) {
- if (isspace((int) tbuf[i]))
- continue;
- if (!isxdigit((int) tbuf[i])) {
- fprintf(stderr,
- "illegal char[%d]='%c' in file '%s'\n",
- i, (int) tbuf[i], templatefile[0]);
- exit(2);
- }
- tbuf[j] = tbuf[i];
- j++;
- }
- cc = j;
- if ((cc & 1) != 0) {
- fprintf(stderr,
- "odd number of hexadecimal digits in file '%s'\n",
- templatefile[0]);
- exit(2);
- }
- length_solicit6 = cc >> 1;
- for (i = 0; i < cc; i += 2)
- (void) sscanf(tbuf + i, "%02hhx", &p[i >> 1]);
- if (xidoffset[0] >= 0)
- xid_solicit6 = (size_t) xidoffset[0];
- else
- xid_solicit6 = DHCP6_OFF_XID;
- if (xid_solicit6 + 3 > length_solicit6) {
- fprintf(stderr,
- "xid (at %zu) is outside the template (length %zu)?\n",
- xid_solicit6, length_solicit6);
- exit(2);
- }
- if (rndoffset[0] >= 0)
- random_solicit6 = (size_t) rndoffset[0];
- else
- random_solicit6 = 0;
- if (random_solicit6 > length_solicit6) {
- fprintf(stderr,
- "random (at %zu) outside the template (length %zu)?\n",
- random_solicit6, length_solicit6);
- exit(2);
- }
-}
-
-/*
- * build a DHCPv6 REQUEST template
- * (implicit parameter is the DUID, don't assume an Ethernet link)
- */
-
-void
-build_template_request6(void)
-{
- uint8_t *p = template_request6;
-
- xid_request6 = DHCP6_OFF_XID;
- /* message type */
- p[DHCP6_OFF_MSGTYP] = DHCP6_OP_REQUEST;
- /* options */
- p += DHCP6_OFF_OPTIONS;
- /* elapsed time */
- p[1] = DHCP6_OPT_ELAPSED_TIME;
- p[3] = 2;
- p += 4;
- elapsed_request6 = p - template_request6;
- p += 2;
- /* client ID */
- p[1] = DHCP6_OPT_CLIENTID;
- p[3] = duid_length;
- memcpy(p + 4, duid_prefix, duid_length);
- p += 4 + duid_length;
- random_request6 = p - template_request6;
- /* option request option */
- p[1] = DHCP6_OPT_ORO;
- p[3] = 4;
- p[5] = DHCP6_OPT_NAME_SERVERS;
- p[7] = DHCP6_OPT_DOMAIN_SEARCH;
- p += 8;
- /* server ID and IA_NA */
- serverid_request6 = p - template_request6;
- reqaddr_request6 = p - template_request6;
- /* set length */
- length_request6 = p - template_request6;
-}
-
-/*
- * get a DHCPv6 third packet (usually a REQUEST) template
- * from the file given in the command line (-T<template-file>)
- * and offsets (-X,-O,-E,-S,-I).
- */
-
-void
-get_template_request6(void)
-{
- uint8_t *p = template_request6;
- int fd, cc, i, j;
-
- fd = open(templatefile[1], O_RDONLY);
- if (fd < 0) {
- fprintf(stderr, "open(%s): %s\n",
- templatefile[1], strerror(errno));
- exit(2);
- }
- cc = read(fd, tbuf, sizeof(tbuf));
- (void) close(fd);
- if (cc < 0) {
- fprintf(stderr, "read(%s): %s\n",
- templatefile[1], strerror(errno));
- exit(1);
- }
- if (cc < 10) {
- fprintf(stderr, "file '%s' too short\n", templatefile[1]);
- exit(2);
- }
- if (cc > 8193) {
- fprintf(stderr,"file '%s' too large\n", templatefile[1]);
- exit(2);
- }
- j = 0;
- for (i = 0; i < cc; i++) {
- if (isspace((int) tbuf[i]))
- continue;
- if (!isxdigit((int) tbuf[i])) {
- fprintf(stderr,
- "illegal char[%d]='%c' in file '%s'\n",
- i, (int) tbuf[i], templatefile[1]);
- exit(2);
- }
- tbuf[j] = tbuf[i];
- j++;
- }
- cc = j;
- if ((cc & 1) != 0) {
- fprintf(stderr,
- "odd number of hexadecimal digits in file '%s'\n",
- templatefile[1]);
- exit(2);
- }
- length_request6 = cc >> 1;
- for (i = 0; i < cc; i += 2)
- (void) sscanf(tbuf + i, "%02hhx", &p[i >> 1]);
- if (xidoffset[1] >= 0)
- xid_request6 = (size_t) xidoffset[1];
- else
- xid_request6 = DHCP6_OFF_XID;
- if (xid_request6 + 3 > length_request6) {
- fprintf(stderr,
- "xid (at %zu) is outside the template (length %zu)?\n",
- xid_request6, length_request6);
- exit(2);
- }
- if (rndoffset[1] >= 0)
- random_request6 = (size_t) rndoffset[1];
- else
- random_request6 = 0;
- if (random_request6 > length_request6) {
- fprintf(stderr,
- "random (at %zu) outside the template (length %zu)?\n",
- random_request6, length_request6);
- exit(2);
- }
- if (elpoffset >= 0)
- elapsed_request6 = (size_t) elpoffset;
- if (elapsed_request6 + 2 > length_request6) {
- fprintf(stderr,
- "secs (at %zu) outside the template (length %zu)?\n",
- elapsed_request6, length_request6);
- exit(2);
- }
- serverid_request6 = (size_t) sidoffset;
- if (serverid_request6 > length_request6) {
- fprintf(stderr,
- "server-id option (at %zu) outside the template "
- "(length %zu)?\n",
- serverid_request6, length_request6);
- exit(2);
- }
- reqaddr_request6 = (size_t) ripoffset;
- if (reqaddr_request6 > length_request6) {
- fprintf(stderr,
- "requested-ip-address option (at %zu) outside "
- "the template (length %zu)?\n",
- reqaddr_request6, length_request6);
- exit(2);
- }
-}
-
-/*
- * send the DHCPv6 REQUEST third packet
- * (the transaction ID is odd)
- * (TODO: check for errors in the ADVERTISE)
- */
-
-void
-send_request6(struct exchange *x0)
-{
- struct exchange *x;
- struct xlist *bucket;
- size_t len;
- uint32_t hash;
- ssize_t ret;
-
- x = (struct exchange *) malloc(sizeof(*x));
- if (x == NULL) {
- locallimit++;
- perror("send2");
- return;
- }
-
- memcpy(x, x0, sizeof(*x));
- x->order2 = xscount2++;
- x->xid |= 1;
- hash = x->xid >> 1;
-
- ISC_TAILQ_INSERT_TAIL(&xsent2, x, gchain);
- hash &= hashsize2 - 1;
- bucket = (struct xlist *) (exchanges2 + hash * sizeof(*bucket));
- ISC_TAILQ_INSERT_TAIL(bucket, x, hchain);
-
- len = length_request6;
- memcpy(obuf, template_request6, len);
- /* xid */
- obuf[xid_request6] = x->xid >> 16;
- obuf[xid_request6 + 1] = x->xid >> 8;
- obuf[xid_request6 + 2] = x->xid;
- /* random */
- randomize(random_request6, x->rnd);
- /* elapsed time */
- if (elapsed_request6 > 0) {
- int et;
-
- et = (x->ts1.tv_sec - x->ts0.tv_sec) * 100;
- et += (x->ts1.tv_nsec - x->ts0.tv_nsec) / 10000000;
- if (et > 65535) {
- obuf[elapsed_request6] = 0xff;
- obuf[elapsed_request6 + 1] = 0xff;
- } else if (et > 0) {
- obuf[elapsed_request6] = et >> 8;
- obuf[elapsed_request6 + 1] = et & 0xff;
- }
- }
- /* server ID */
- if (serverid_request6 < length_request6)
- memmove(obuf + serverid_request6 + x->sidlen,
- obuf + serverid_request6,
- x->sidlen);
- memcpy(obuf + serverid_request6, x->sid, x->sidlen);
- len += x->sidlen;
- /* IA_NA */
- if (reqaddr_request6 < serverid_request6) {
- memmove(obuf + reqaddr_request6 + x->ianalen,
- obuf + reqaddr_request6,
- x->ianalen);
- memcpy(obuf + reqaddr_request6, x->iana, x->ianalen);
- } else if (reqaddr_request6 < length_request6) {
- memmove(obuf + reqaddr_request6 + x->sidlen + x->ianalen,
- obuf + reqaddr_request6 + x->sidlen,
- x->ianalen);
- memcpy(obuf + reqaddr_request6+ x->sidlen,
- x->iana,
- x->ianalen);
- } else
- memcpy(obuf + len, x->iana, x->ianalen);
- len += x->ianalen;
-
- /* timestamp */
- ret = clock_gettime(CLOCK_REALTIME, &x->ts2);
- if (ret < 0) {
- perror("clock_gettime(send2)");
- fatal = 1;
- return;
- }
- ret = sendto(sock, obuf, len, 0,
- (struct sockaddr *) &serveraddr,
- sizeof(struct sockaddr_in6));
- if (ret >= 0)
- return;
- if ((errno == EAGAIN) || (errno == EWOULDBLOCK) ||
- (errno == ENOBUFS) || (errno == ENOMEM))
- locallimit++;
- perror("send2");
-}
-
-/*
- * send the DHCPv6 SOLICIT first packet
- * (for 4-exchange, the transaction ID xid is even)
- */
-
-int
-send6(void)
-{
- struct exchange *x;
- struct xlist *bucket;
- uint32_t hash;
- ssize_t ret;
-
- x = (struct exchange *) malloc(sizeof(*x));
- if (x == NULL)
- return -ENOMEM;
-
- memset(x, 0, sizeof(*x));
- x->order0 = xscount0++;
- hash = x->rnd = (uint32_t) random();
- if (simple == 0)
- x->xid = (hash << 1) & 0x00ffffff;
- else
- x->xid = hash & 0x00ffffff;
-
- ISC_TAILQ_INSERT_TAIL(&xsent0, x, gchain);
- hash &= hashsize0 - 1;
- bucket = (struct xlist *) (exchanges0 + hash * sizeof(*bucket));
- ISC_TAILQ_INSERT_TAIL(bucket, x, hchain);
-
- memcpy(obuf, template_solicit6, length_solicit6);
- /* xid */
- obuf[xid_solicit6] = x->xid >> 16;
- obuf[xid_solicit6 + 1] = x->xid >> 8;
- obuf[xid_solicit6 + 2] = x->xid;
- /* random */
- x->rnd = randomize(random_solicit6, x->rnd);
-
- /* timestamp */
- ret = clock_gettime(CLOCK_REALTIME, &last);
- if (ret < 0) {
- perror("clock_gettime(send)");
- fatal = 1;
- return -errno;
- }
- x->ts0 = last;
- errno = 0;
- ret = sendto(sock, obuf, length_solicit6, 0,
- (struct sockaddr *) &serveraddr,
- sizeof(struct sockaddr_in6));
- if (ret == (ssize_t) length_solicit6)
- return 0;
- return -errno;
-}
-
-/*
- * scan a DHCPv6 ADVERTISE to get its server-id option
- */
-
-int
-scan_for_srvid6(struct exchange *x, size_t cc)
-{
- size_t off = DHCP6_OFF_OPTIONS, len;
-
- for (;;) {
- if (off + 4 > cc) {
- fprintf(stderr, "server-id not found\n");
- return -1;
- }
- if ((ibuf[off] == 0) && (ibuf[off + 1] == DHCP6_OPT_SERVERID))
- break;
- off += 4 + (ibuf[off + 2] << 8) + ibuf[off + 3];
- }
- len = 4 + (ibuf[off + 2] << 8) + ibuf[off + 3];
- /* cache it in the global variables when required and not yet done */
- if ((usefirst != 0) && (gsrvid == NULL)) {
- if (len <= sizeof(gsrvidbuf)) {
- memcpy(gsrvidbuf, ibuf + off, len);
- gsrvid = gsrvidbuf;
- gsrvidlen = len;
- } else {
- gsrvid = (uint8_t *) malloc(len);
- if (gsrvid == NULL) {
- perror("malloc(gsrvid");
- return -1;
- }
- memcpy(gsrvid, ibuf + off, len);
- gsrvidlen = len;
- }
- }
- x->sid = ibuf + off;
- x->sidlen = len;
- return 0;
-}
-
-/*
- * scan a DHCPv6 ADVERTISE to get its IA_NA option
- * (TODO: check for errors)
- */
-
-int
-scan_for_ia_na(struct exchange *x, size_t cc)
-{
- size_t off = DHCP6_OFF_OPTIONS, len;
-
- for (;;) {
- if (off + 4 > cc) {
- fprintf(stderr, "ia-na not found\n");
- return -1;
- }
- if ((ibuf[off] == 0) && (ibuf[off + 1] == DHCP6_OPT_IA_NA))
- break;
- off += 4 + (ibuf[off + 2] << 8) + ibuf[off + 3];
- }
- len = 4 + (ibuf[off + 2] << 8) + ibuf[off + 3];
- x->iana = ibuf + off;
- x->ianalen = len;
- return 0;
-}
-
-/*
- * receive a DHCPv6 packet
- */
-
-void
-receive6(void)
-{
- struct exchange *x, *t;
- struct xlist *bucket;
- struct timespec now;
- ssize_t cc;
- uint32_t xid, hash;
- int checklost = 0;
- double delta;
-
- cc = recv(sock, ibuf, sizeof(ibuf), 0);
- if (cc < 0) {
- if ((errno == EAGAIN) ||
- (errno == EWOULDBLOCK) ||
- (errno == EINTR))
- return;
- perror("recv");
- fatal = 1;
- return;
- }
- /* enforce a reasonable length */
- if (cc < 22) {
- tooshort++;
- return;
- }
- if (clock_gettime(CLOCK_REALTIME, &now) < 0) {
- perror("clock_gettime(receive)");
- fatal = 1;
- return;
- }
- xid = ibuf[xid_solicit6] << 16;
- xid |= ibuf[xid_solicit6 + 1] << 8;
- xid |= ibuf[xid_solicit6 + 2];
- /* 4-packet exchange even/odd xid */
- if (simple == 0) {
- if ((xid & 1) != 0) {
- receive_reply(xid, &now);
- return;
- }
- hash = (xid >> 1) & (hashsize0 - 1);
- } else
- hash = xid & (hashsize0 - 1);
- /* now it is the second packet, get the bucket which is needed */
- bucket = (struct xlist *) (exchanges0 + hash * sizeof(*bucket));
- /* try the 'next to be received' cache */
- if ((xnext0 != NULL) && (xnext0->xid == xid)) {
- x = xnext0;
- goto found;
- }
- /* if the rate is not illimited, garbage collect up to 3
- timed-out exchanges */
- if (rate != 0)
- checklost = 3;
- /* look for the exchange */
- ISC_TAILQ_FOREACH_SAFE(x, bucket, hchain, t) {
- double waited;
-
- if (x->xid == xid)
- goto found;
- if (checklost <= 0)
- continue;
- /* check for a timed-out exchange */
- waited = now.tv_sec - x->ts0.tv_sec;
- waited += (now.tv_nsec - x->ts0.tv_nsec) / 1e9;
- if (waited < losttime[0]) {
- checklost = 0;
- continue;
- }
- /* garbage collect timed-out exchange */
- checklost--;
- ISC_TAILQ_REMOVE(bucket, x, hchain);
- ISC_TAILQ_REMOVE(&xsent0, x, gchain);
- free(x);
- collected[0] += 1;
- }
- /* no match? very late or not for us */
- orphans++;
- return;
-
- /* got it: update stats and move to the received queue */
- found:
- xrcount0++;
- x->ts1 = now;
- delta = x->ts1.tv_sec - x->ts0.tv_sec;
- delta += (x->ts1.tv_nsec - x->ts0.tv_nsec) / 1e9;
- if (delta < dmin0)
- dmin0 = delta;
- if (delta > dmax0)
- dmax0 = delta;
- dsum0 += delta;
- dsumsq0 += delta * delta;
- xnext0 = ISC_TAILQ_NEXT(x, gchain);
- ISC_TAILQ_REMOVE(bucket, x, hchain);
- ISC_TAILQ_REMOVE(&xsent0, x, gchain);
- ISC_TAILQ_INSERT_TAIL(&xrcvd0, x, gchain);
- /* if the exchange is not finished, go to the second part */
- if (simple == 0) {
- int ret = 0;
-
- /* the server-ID option is needed */
- if ((usefirst != 0) && (gsrvid != NULL)) {
- x->sid = gsrvid;
- x->sidlen = gsrvidlen;
- } else
- ret = scan_for_srvid6(x, cc);
- /* and the IA_NA option too */
- if (ret >= 0)
- ret = scan_for_ia_na(x, cc);
- if (ret >= 0)
- send_request6(x);
- }
-}
-
-/*
- * decode a base command line parameter
- * (currently only MAC address and DUID are supported)
- */
-
-void
-decodebase(void)
-{
- char *b0 = base[basecnt];
-
- /* MAC address (alias Ethernet address) */
- if ((strncasecmp(b0, "mac=", 4) == 0) ||
- (strncasecmp(b0, "ether=", 6) == 0)) {
- char *b;
-
- b = strchr(b0, '=') + 1;
- if (sscanf(b, "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx",
- &mac_prefix[0], &mac_prefix[1],
- &mac_prefix[2], &mac_prefix[3],
- &mac_prefix[4], &mac_prefix[5]) == 6) {
- if ((diags != NULL) && (strchr(diags, 'a') != NULL))
- printf("set MAC to %s\n", b);
- return;
- }
- fprintf(stderr,
- "expected base in '%*s=xx:xx:xx:xx:xx:xx' format\n",
- (int) (b - b0 - 1), b0);
- exit(2);
- }
- /* DUID */
- if (strncasecmp(b0, "duid=", 5) == 0) {
- char *b;
- size_t i, l;
-
- if (duid_prefix != NULL) {
- fprintf(stderr, "duid was already set?\n");
- exit(2);
- }
- b = strchr(b0, '=') + 1;
- l = 0;
- while (*b != '\0') {
- if (!isxdigit((int) *b)) {
- fprintf(stderr,
- "illegal char '%c' in duid\n",
- (int) *b);
- exit(2);
- }
- b++;
- l++;
- }
- if ((l & 1) != 0) {
- fprintf(stderr,
- "odd number of hexadecimal digits in duid\n");
- exit(2);
- }
- l /= 2;
- if (l > 64) {
- fprintf(stderr, "duid too large\n");
- exit(2);
- }
- duid_prefix = (uint8_t *) malloc(l);
- if (duid_prefix == NULL) {
- perror("malloc(duid)");
- exit(1);
- }
- b = strchr(b0, '=') + 1;
- for (i = 0; i < l; i++, b += 2)
- (void) sscanf(b, "%02hhx", &duid_prefix[i]);
- duid_length = l;
- if ((diags != NULL) && (strchr(diags, 'a') != NULL)) {
- b = strchr(b0, '=') + 1;
- printf("set DUID[%d] to %s\n",
- duid_length, b);
- }
- return;
- }
- /* other */
- fprintf(stderr, "not yet supported base '%s'\n", b0);
- exit(2);
-}
-
-/*
- * get the server socket address from the command line:
- * - flags: inherited from main, 0 or AI_NUMERICHOST (for literals)
- */
-
-void
-getserveraddr(const int flags)
-{
- struct addrinfo hints, *res;
- char *service;
- int ret;
-
- memset(&hints, 0, sizeof(hints));
- if (ipversion == 4) {
- hints.ai_family = AF_INET;
- service = "67";
- } else {
- hints.ai_family = AF_INET6;
- service = "547";
- }
- hints.ai_socktype = SOCK_DGRAM;
-
- hints.ai_flags = AI_NUMERICSERV | flags;
-#if defined(AI_ADDRCONFIG)
- hints.ai_flags |= AI_ADDRCONFIG;
-#endif
- hints.ai_protocol = IPPROTO_UDP;
-
- ret = getaddrinfo(servername, service, &hints, &res);
- if (ret != 0) {
- fprintf(stderr, "bad server=%s: %s\n",
- servername, gai_strerror(ret));
- exit(2);
- }
- if (res->ai_next != NULL) {
- fprintf(stderr, "ambiguous server=%s\n", servername);
- exit(2);
- }
- memcpy(&serveraddr, res->ai_addr, res->ai_addrlen);
- freeaddrinfo(res);
-}
-
-/*
- * get the interface from the command line:
- * - name of the interface
- * - socket address to fill
- * (in IPv6, get the first link-local address)
- */
-
-void
-getinterface(const char *name, struct sockaddr_storage *ss)
-{
- struct ifaddrs *ifaddr, *ifa;
- int ret;
- size_t len;
-
- ret = getifaddrs(&ifaddr);
- if (ret < 0) {
- perror("getifaddrs");
- exit(1);
- }
-
- for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) {
- if (ifa->ifa_name == NULL)
- continue;
- if (strcmp(ifa->ifa_name, name) != 0)
- continue;
- if ((ifa->ifa_flags & IFF_UP) == 0)
- continue;
- if ((ifa->ifa_flags & IFF_MULTICAST) == 0)
- continue;
- if (ifa->ifa_addr == NULL)
- continue;
- if ((ipversion == 4) &&
- (ifa->ifa_addr->sa_family != AF_INET))
- continue;
- if (ipversion == 6) {
- struct sockaddr_in6 *a6;
-
- a6 = (struct sockaddr_in6 *) ifa->ifa_addr;
- if (a6->sin6_family != AF_INET6)
- continue;
- if (!IN6_IS_ADDR_LINKLOCAL(&a6->sin6_addr))
- continue;
- }
- if (ipversion == 4)
- len = sizeof(struct sockaddr_in);
- else
- len = sizeof(struct sockaddr_in6);
- memcpy(ss, ifa->ifa_addr, len);
- /* fill the server port */
- if (ipversion == 4)
- ((struct sockaddr_in *) ss)->sin_port = htons(67);
- else
- ((struct sockaddr_in6 *) ss)->sin6_port = htons(546);
- return;
- }
- fprintf(stderr, "can't find interface %s\n", name);
- exit(1);
-}
-
-/*
- * get the local socket address from the command line
- * (if it doesn't work, try an interface name)
- */
-
-void
-getlocaladdr(void)
-{
- struct addrinfo hints, *res;
- char *service;
- int ret;
-
- memset(&hints, 0, sizeof(hints));
- if (ipversion == 4) {
- hints.ai_family = AF_INET;
- service = "67";
- } else {
- hints.ai_family = AF_INET6;
- service = "546";
- }
- hints.ai_socktype = SOCK_DGRAM;
- hints.ai_flags = AI_NUMERICSERV;
-#if defined(AI_ADDRCONFIG)
- hints.ai_flags |= AI_ADDRCONFIG;
-#endif
- hints.ai_protocol = IPPROTO_UDP;
-
- ret = getaddrinfo(localname, service, &hints, &res);
- if ((ret == EAI_NONAME)
-#ifdef EAI_NODATA
- || (ret == EAI_NODATA)
-#endif
- ) {
- isinterface = 1;
- getinterface(localname, &localaddr);
- return;
- }
- if (ret != 0) {
- fprintf(stderr,
- "bad -l<local-addr=%s>: %s\n",
- localname,
- gai_strerror(ret));
- exit(2);
- }
- /* refuse multiple addresses */
- if (res->ai_next != NULL) {
- fprintf(stderr,
- "ambiguous -l<local-addr=%s>\n",
- localname);
- exit(2);
- }
- memcpy(&localaddr, res->ai_addr, res->ai_addrlen);
- freeaddrinfo(res);
- return;
-}
-
-/*
- * get the local socket address from the server one
- */
-
-void
-getlocal(void)
-{
- int ret, s;
- socklen_t l;
-
- if (ipversion == 4) {
- l = sizeof(struct sockaddr_in);
- s = socket(PF_INET, SOCK_DGRAM, 0);
- } else {
- l = sizeof(struct sockaddr_in6);
- s = socket(PF_INET6, SOCK_DGRAM, 0);
- }
- if (s < 0) {
- perror("socket");
- exit(1);
- }
- if ((ipversion == 4) && (isbroadcast != 0)) {
- int on = 1;
-
- ret = setsockopt(s, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on));
- if (ret < 0) {
- perror("setsockopt(SO_BROADCAST)");
- exit(1);
- }
- }
- ret = connect(s, (struct sockaddr *) &serveraddr, l);
- if (ret < 0) {
- perror("connect");
- exit(1);
- }
- l = sizeof(localaddr);
- ret = getsockname(s, (struct sockaddr *) &localaddr, &l);
- if (ret < 0) {
- perror("getsockname");
- exit(1);
- }
- (void) close(s);
- /* fill the local port */
- if (ipversion == 4)
- ((struct sockaddr_in *) &localaddr)->sin_port = htons(67);
- else
- ((struct sockaddr_in6 *) &localaddr)->sin6_port = htons(546);
-}
-
-/*
- * intermediate reporting
- * (note: an in-transit packet can be reported as dropped)
- */
-
-void
-reporting(void)
-{
- dreport.tv_sec += report;
-
- if (xscount2 == 0) {
- printf("sent: %llu, received: %llu (drops: %lld)",
- (unsigned long long) xscount0,
- (unsigned long long) xrcount0,
- (long long) (xscount0 - xrcount0));
- if (!ISC_TAILQ_EMPTY(&xrcvd0)) {
- double avg;
-
- avg = dsum0 / xrcount0;
- printf(" average: %.3f ms", avg * 1e3);
- }
- } else {
- printf("sent: %llu/%llu received: %llu/%llu "
- "(drops: %lld/%lld)",
- (unsigned long long) xscount0,
- (unsigned long long) xscount2,
- (unsigned long long) xrcount0,
- (unsigned long long) xrcount2,
- (long long) (xscount0 - xrcount0),
- (long long) (xscount2 - xrcount2));
- if (!ISC_TAILQ_EMPTY(&xrcvd0)) {
- double avg0, avg2;
-
- avg0 = dsum0 / xrcount0;
- if (xrcount2 != 0)
- avg2 = dsum2 / xrcount2;
- else
- avg2 = 0.;
- printf(" average: %.3f/%.3f ms",
- avg0 * 1e3, avg2 * 1e3);
- }
- }
- printf("\n");
-}
-
-/*
- * SIGCHLD handler
- */
-
-void
-reapchild(int sig)
-{
- int status;
-
- sig = sig;
- while (wait3(&status, WNOHANG, NULL) > 0)
- /* continue */;
-}
-
-/*
- * SIGINT handler
- */
-
-void
-interrupt(int sig)
-{
- sig = sig;
- interrupted = 1;
-}
-
-/*
- * '-v' handler
- */
-
-void
-version(void)
-{
- fprintf(stderr, "version 0.01\n");
-}
-
-/*
- * usage (from the wiki)
- */
-
-void
-usage(void)
-{
- fprintf(stderr, "%s",
-"perfdhcp [-hv] [-4|-6] [-r<rate>] [-t<report>] [-R<range>] [-b<base>]\n"
-" [-n<num-request>] [-p<test-period>] [-d<drop-time>] [-D<max-drop>]\n"
-" [-l<local-addr|interface>] [-P<preload>] [-a<aggressivity>]\n"
-" [-L<local-port>] [-s<seed>] [-i] [-B] [-c] [-1]\n"
-" [-T<template-file>] [-X<xid-offset>] [-O<random-offset]\n"
-" [-E<time-offset>] [-S<srvid-offset>] [-I<ip-offset>]\n"
-" [-x<diagnostic-selector>] [-w<wrapped>] [server]\n"
-"\f\n"
-"The [server] argument is the name/address of the DHCP server to\n"
-"contact. For DHCPv4 operation, exchanges are initiated by\n"
-"transmitting a DHCP DISCOVER to this address.\n"
-"\n"
-"For DHCPv6 operation, exchanges are initiated by transmitting a DHCP\n"
-"SOLICIT to this address. In the DHCPv6 case, the special name 'all'\n"
-"can be used to refer to All_DHCP_Relay_Agents_and_Servers (the\n"
-"multicast address FF02::1:2), or the special name 'servers' to refer\n"
-"to All_DHCP_Servers (the multicast address FF05::1:3). The [server]\n"
-"argument is optional only in the case that -l is used to specify an\n"
-"interface, in which case [server] defaults to 'all'.\n"
-"\n"
-"The default is to perform a single 4-way exchange, effectively pinging\n"
-"the server.\n"
-"The -r option is used to set up a performance test, without\n"
-"it exchanges are initiated as fast as possible.\n"
-"\n"
-"Options:\n"
-"-1: Take the server-ID option from the first received message.\n"
-"-4: DHCPv4 operation (default). This is incompatible with the -6 option.\n"
-"-6: DHCPv6 operation. This is incompatible with the -4 option.\n"
-"-a<aggressivity>: When the target sending rate is not yet reached,\n"
-" control how many exchanges are initiated before the next pause.\n"
-"-b<base>: The base MAC, DUID, IP, etc, used to simulate different\n"
-" clients. This can be specified multiple times, each instance is\n"
-" in the <type>=<value> form, for instance:\n"
-" (and default) MAC=00:0c:01:02:03:04.\n"
-"-d<drop-time>: Specify the time after which a request is treated as\n"
-" having been lost. The value is given in seconds and may contain a\n"
-" fractional component. The default is 1 second.\n"
-"-E<time-offset>: Offset of the (DHCPv4) secs field / (DHCPv6)\n"
-" elapsed-time option in the (second/request) template.\n"
-" The value 0 disables it.\n"
-"-h: Print this help.\n"
-"-i: Do only the initial part of an exchange: DO or SA, depending on\n"
-" whether -6 is given.\n"
-"-I<ip-offset>: Offset of the (DHCPv4) IP address in the requested-IP\n"
-" option / (DHCPv6) IA_NA option in the (second/request) template.\n"
-"-l<local-addr|interface>: For DHCPv4 operation, specify the local\n"
-" hostname/address to use when communicating with the server. By\n"
-" default, the interface address through which traffic would\n"
-" normally be routed to the server is used.\n"
-" For DHCPv6 operation, specify the name of the network interface\n"
-" via which exchanges are initiated.\n"
-"-L<local-port>: Specify the local port to use\n"
-" (the value 0 means to use the default).\n"
-"-O<random-offset>: Offset of the last octet to randomize in the template.\n"
-"-P<preload>: Initiate first <preload> exchanges back to back at startup.\n"
-"-r<rate>: Initiate <rate> DORA/SARR (or if -i is given, DO/SA)\n"
-" exchanges per second. A periodic report is generated showing the\n"
-" number of exchanges which were not completed, as well as the\n"
-" average response latency. The program continues until\n"
-" interrupted, at which point a final report is generated.\n"
-"-R<range>: Specify how many different clients are used. With 1\n"
-" (the default), all requests seem to come from the same client.\n"
-"-s<seed>: Specify the seed for randomization, making it repeatable.\n"
-"-S<srvid-offset>: Offset of the server-ID option in the\n"
-" (second/request) template.\n"
-"-T<template-file>: The name of a file containing the template to use\n"
-" as a stream of hexadecimal digits.\n"
-"-v: Report the version number of this program.\n"
-"-w<wrapped>: Command to call with start/stop at the beginning/end of\n"
-" the program.\n"
-"-x<diagnostic-selector>: Include extended diagnostics in the output.\n"
-" <diagnostic-selector> is a string of single-keywords specifying\n"
-" the operations for which verbose output is desired. The selector\n"
-" keyletters are:\n"
-" * 'a': print the decoded command line arguments\n"
-" * 'e': print the exit reason\n"
-" * 'i': print rate processing details\n"
-" * 'r': print randomization details\n"
-" * 's': print first server-id\n"
-" * 't': when finished, print timers of all successful exchanges\n"
-" * 'T': when finished, print templates\n"
-"-X<xid-offset>: Transaction ID (aka. xid) offset in the template.\n"
-"\n"
-"DHCPv4 only options:\n"
-"-B: Force broadcast handling.\n"
-"\n"
-"DHCPv6 only options:\n"
-"-c: Add a rapid commit option (exchanges will be SA).\n"
-"\n"
-"The remaining options are used only in conjunction with -r:\n"
-"\n"
-"-D<max-drop>: Abort the test if more than <max-drop> requests have\n"
-" been dropped. Use -D0 to abort if even a single request has been\n"
-" dropped. If <max-drop> includes the suffix '%', it specifies a\n"
-" maximum percentage of requests that may be dropped before abort.\n"
-" In this case, testing of the threshold begins after 10 requests\n"
-" have been expected to be received.\n"
-"-n<num-request>: Initiate <num-request> transactions. No report is\n"
-" generated until all transactions have been initiated/waited-for,\n"
-" after which a report is generated and the program terminates.\n"
-"-p<test-period>: Send requests for the given test period, which is\n"
-" specified in the same manner as -d. This can be used as an\n"
-" alternative to -n, or both options can be given, in which case the\n"
-" testing is completed when either limit is reached.\n"
-"-t<report>: Delay in seconds between two periodic reports.\n"
-"\n"
-"Errors:\n"
-"- tooshort: received a too short message\n"
-"- orphans: received a message which doesn't match an exchange\n"
-" (duplicate, late or not related)\n"
-"- locallimit: reached to local system limits when sending a message.\n"
-"\n"
-"Exit status:\n"
-"The exit status is:\n"
-"0 on complete success.\n"
-"1 for a general error.\n"
-"2 if an error is found in the command line arguments.\n"
-"3 if there are no general failures in operation, but one or more\n"
-" exchanges are not successfully completed.\n");
-}
-
-/*
- * main function / entry point
- */
-
-int
-main(const int argc, char * const argv[])
-{
- int opt, flags = 0, ret, i;
- long long r;
- char *pc;
- extern char *optarg;
- extern int optind;
-
-#define OPTIONS "hv46r:t:R:b:n:p:d:D:l:P:a:L:s:iBc1T:X:O:E:S:I:x:w:"
-
- /* decode options */
- while ((opt = getopt(argc, argv, OPTIONS)) != -1)
- switch (opt) {
- case 'h':
- usage();
- exit(0);
-
- case 'v':
- version();
- exit(0);
-
- case '4':
- if (ipversion == 6) {
- fprintf(stderr, "IP version already set to 6\n");
- usage();
- exit(2);
- }
- ipversion = 4;
- break;
-
- case '6':
- if (ipversion == 4) {
- fprintf(stderr, "IP version already set to 4\n");
- usage();
- exit(2);
- }
- ipversion = 6;
- break;
-
- case 'r':
- rate = atoi(optarg);
- if (rate <= 0) {
- fprintf(stderr, "rate must be a positive integer\n");
- usage();
- exit(2);
- }
- break;
-
- case 't':
- report = atoi(optarg);
- if (report <= 0) {
- fprintf(stderr, "report must be a positive integer\n");
- usage();
- exit(2);
- }
- break;
-
- case 'R':
- r = atoll(optarg);
- if (r < 0) {
- fprintf(stderr,
- "range must not be a negative integer\n");
- usage();
- exit(2);
- }
- range = (uint32_t) r;
- if ((range != 0) && (range != UINT32_MAX)) {
- uint32_t s = range + 1;
- uint64_t b = UINT32_MAX + 1, m;
-
- m = (b / s) * s;
- if (m == b)
- maxrandom = 0;
- else
- maxrandom = (uint32_t) m;
- }
- break;
-
- case 'b':
- if (basecnt > 3) {
- fprintf(stderr, "too many bases\n");
- usage();
- exit(2);
- }
- base[basecnt] = optarg;
- decodebase();
- basecnt++;
- break;
-
- case 'n':
- numreq[gotnumreq] = atoi(optarg);
- if (numreq[gotnumreq] <= 0) {
- fprintf(stderr,
- "num-request must be a positive integer\n");
- usage();
- exit(2);
- }
- gotnumreq = 1;
- break;
-
- case 'p':
- period = atoi(optarg);
- if (period <= 0) {
- fprintf(stderr,
- "test-period must be a positive integer\n");
- usage();
- exit(2);
- }
- break;
-
- case 'd':
- losttime[gotlosttime] = atof(optarg);
- if (losttime[gotlosttime] <= 0.) {
- fprintf(stderr,
- "drop-time must be a positive number\n");
- usage();
- exit(2);
- }
- gotlosttime = 1;
- break;
-
- case 'D':
- pc = strchr(optarg, '%');
- if (pc != NULL) {
- *pc = '\0';
- maxpdrop[gotmaxdrop] = atof(optarg);
- if ((maxpdrop[gotmaxdrop] <= 0) ||
- (maxpdrop[gotmaxdrop] >= 100)) {
- fprintf(stderr,
- "invalid drop-time percentage\n");
- usage();
- exit(2);
- }
- gotmaxdrop = 1;
- break;
- }
- maxdrop[gotmaxdrop] = atoi(optarg);
- if (maxdrop[gotmaxdrop] <= 0) {
- fprintf(stderr,
- "max-drop must be a positive integer\n");
- usage();
- exit(2);
- }
- gotmaxdrop = 1;
- break;
-
- case 'l':
- localname = optarg;
- break;
-
- case 'P':
- preload = atoi(optarg);
- if (preload < 0) {
- fprintf(stderr,
- "preload must not be a negative integer\n");
- usage();
- exit(2);
- }
- break;
-
- case 'a':
- aggressivity = atoi(optarg);
- if (aggressivity <= 0) {
- fprintf(stderr,
- "aggressivity must be a positive integer\n");
- usage();
- exit(2);
- }
- break;
-
- case 'L':
- localport = atoi(optarg);
- if (localport < 0) {
- fprintf(stderr,
- "local-port must not be a negative integer\n");
- usage();
- exit(2);
- }
- if (localport > (int) UINT16_MAX) {
- fprintf(stderr,
- "local-port must be lower than %d\n",
- (int) UINT16_MAX);
- usage();
- exit(2);
- }
- break;
-
- case 's':
- seeded = 1;
- seed = (unsigned int) atol(optarg);
- break;
-
- case 'i':
- simple = 1;
- break;
-
- case 'B':
- isbroadcast = 1;
- break;
-
- case 'c':
- rapidcommit = 1;
- break;
-
- case '1':
- usefirst = 1;
- break;
-
- case 'T':
- if (templatefile[0] != NULL) {
- if (templatefile[1] != NULL) {
- fprintf(stderr,
- "template-files are already set\n");
- usage();
- exit(2);
- }
- templatefile[1] = optarg;
- } else
- templatefile[0] = optarg;
- break;
-
- case 'X':
- if (xidoffset[0] >= 0)
- i = 1;
- else
- i = 0;
- xidoffset[i] = atoi(optarg);
- if (xidoffset[i] <= 0) {
- fprintf(stderr,
- "xid-offset must be a positive integer\n");
- usage();
- exit(2);
- }
- break;
-
- case 'O':
- if (rndoffset[0] >= 0)
- i = 1;
- else
- i = 0;
- rndoffset[i] = atoi(optarg);
- if (rndoffset[i] < 3) {
- fprintf(stderr,
- "random-offset must be greater than 3\n");
- usage();
- exit(2);
- }
- break;
-
- case 'E':
- elpoffset = atoi(optarg);
- if (elpoffset < 0) {
- fprintf(stderr,
- "time-offset must not be a "
- "negative integer\n");
- usage();
- exit(2);
- }
- break;
-
- case 'S':
- sidoffset = atoi(optarg);
- if (sidoffset <= 0) {
- fprintf(stderr,
- "srvid-offset must be a positive integer\n");
- usage();
- exit(2);
- }
- break;
-
- case 'I':
- ripoffset = atoi(optarg);
- if (ripoffset <= 0) {
- fprintf(stderr,
- "ip-offset must be a positive integer\n");
- usage();
- exit(2);
- }
- break;
-
- case 'x':
- diags = optarg;
- break;
-
- case 'w':
- wrapped = optarg;
- break;
-
- default:
- usage();
- exit(2);
- }
-
- /* adjust some global variables */
- if (ipversion == 0)
- ipversion = 4;
- if (templatefile[1] != NULL) {
- if (xidoffset[1] < 0)
- xidoffset[1] = xidoffset[0];
- if (rndoffset[1] < 0)
- rndoffset[1] = rndoffset[0];
- }
-
- /* when required, print the internal view of the command line */
- if ((diags != NULL) && (strchr(diags, 'a') != NULL)) {
- printf("IPv%d", ipversion);
- if (simple != 0) {
- if (ipversion == 4)
- printf(" DO only");
- else
- printf(" SA only");
- }
- if (rate != 0)
- printf(" rate=%d", rate);
- if (report != 0)
- printf(" report=%d", report);
- if (range != 0) {
- if (strchr(diags, 'r') != NULL)
- printf(" range=0..%d [0x%x]",
- range,
- (unsigned int) maxrandom);
- else
- printf(" range=0..%d", range);
- }
- if (basecnt != 0)
- for (i = 0; i < basecnt; i++)
- printf(" base[%d]='%s'", i, base[i]);
- if (gotnumreq != 0)
- printf(" num-request=%d,%d", numreq[0], numreq[1]);
- if (period != 0)
- printf(" test-period=%d", period);
- printf(" drop-time=%g,%g", losttime[0], losttime[1]);
- if ((maxdrop[0] != 0) || (maxdrop[1] != 0))
- printf(" max-drop=%d,%d", maxdrop[0], maxdrop[1]);
- if ((maxpdrop[0] != 0.) || (maxpdrop[1] != 0.))
- printf(" max-drop=%2.2f%%,%2.2f%%",
- maxpdrop[0], maxpdrop[1]);
- if (preload != 0)
- printf(" preload=%d", preload);
- printf(" aggressivity=%d", aggressivity);
- if (localport != 0)
- printf(" local-port=%d", localport);
- if (seeded)
- printf(" seed=%u", seed);
- if (isbroadcast != 0)
- printf(" broadcast");
- if (rapidcommit != 0)
- printf(" rapid-commit");
- if (usefirst != 0)
- printf(" use-first");
- if ((templatefile[0] != NULL) && (templatefile[1] == NULL))
- printf(" template-file='%s'", templatefile[0]);
- else if (templatefile[1] != NULL)
- printf(" template-file='%s','%s'",
- templatefile[0], templatefile[1]);
- if ((xidoffset[0] >= 0) && (xidoffset[1] < 0))
- printf(" xid-offset=%d", xidoffset[0]);
- else if (xidoffset[1] >= 0)
- printf(" xid-offset=%d,%d",
- xidoffset[0], xidoffset[1]);
- if ((rndoffset[0] >= 0) && (rndoffset[1] < 0))
- printf(" xid-offset=%d", rndoffset[0]);
- else if (rndoffset[1] >= 0)
- printf(" xid-offset=%d,%d",
- rndoffset[0], rndoffset[1]);
- if (elpoffset >= 0)
- printf(" time-offset=%d", elpoffset);
- if (sidoffset >= 0)
- printf(" srvid-offset=%d", sidoffset);
- if (ripoffset >= 0)
- printf(" ip-offset=%d", ripoffset);
- printf(" diagnotic-selectors='%s'", diags);
- if (wrapped != NULL)
- printf(" wrapped='%s'", wrapped);
- printf("\n");
- }
-
- /* check DHCPv4 only options */
- if ((ipversion != 4) && (isbroadcast != 0)) {
- fprintf(stderr, "-b is not compatible with IPv6 (-6)\n");
- usage();
- exit(2);
- }
-
- /* check DHCPv6 only options */
- if ((ipversion != 6) && (rapidcommit != 0)) {
- fprintf(stderr, "-6 (IPv6) must be set to use -c\n");
- usage();
- exit(2);
- }
-
- /* check 4-packet (aka not simple) mode options */
- if ((simple != 0) && (numreq[1] != 0)) {
- fprintf(stderr,
- "second -n<num-request> is not compatible with -i\n");
- usage();
- exit(2);
- }
- if ((simple != 0) && (losttime[1] != 1.)) {
- fprintf(stderr,
- "second -d<drop-time> is not compatible with -i\n");
- usage();
- exit(2);
- }
- if ((simple != 0) &&
- ((maxdrop[1] != 0) || (maxpdrop[1] != 0.))) {
- fprintf(stderr,
- "second -D<max-drop> is not compatible with -i\n");
- usage();
- exit(2);
- }
- if ((simple != 0) && (usefirst != 0)) {
- fprintf(stderr,
- "-1 is not compatible with -i\n");
- usage();
- exit(2);
- }
- if ((simple != 0) && (templatefile[1] != NULL)) {
- fprintf(stderr,
- "second -T<template-file> is not "
- "compatible with -i\n");
- usage();
- exit(2);
- }
- if ((simple != 0) && (xidoffset[1] >= 0)) {
- fprintf(stderr,
- "second -X<xid-offset> is not compatible with -i\n");
- usage();
- exit(2);
- }
- if ((simple != 0) && (rndoffset[1] >= 0)) {
- fprintf(stderr,
- "second -O<random-offset is not compatible with -i\n");
- usage();
- exit(2);
- }
- if ((simple != 0) && (elpoffset >= 0)) {
- fprintf(stderr,
- "-E<time-offset> is not compatible with -i\n");
- usage();
- exit(2);
- }
- if ((simple != 0) && (sidoffset >= 0)) {
- fprintf(stderr,
- "-S<srvid-offset> is not compatible with -i\n");
- usage();
- exit(2);
- }
- if ((simple != 0) && (ripoffset >= 0)) {
- fprintf(stderr,
- "-I<ip-offset> is not compatible with -i\n");
- usage();
- exit(2);
- }
-
-
- /* check simple mode options */
- if ((simple == 0) && (rapidcommit != 0)) {
- fprintf(stderr, "-i must be set to use -c\n");
- usage();
- exit(2);
- }
-
- /* check rate '-r' options */
- if ((rate == 0) && (report != 0)) {
- fprintf(stderr,
- "-r<rate> must be set to use -t<report>\n");
- usage();
- exit(2);
- }
- if ((rate == 0) && ((numreq[0] != 0) || (numreq[1] != 0))) {
- fprintf(stderr,
- "-r<rate> must be set to use -n<num-request>\n");
- usage();
- exit(2);
- }
- if ((rate == 0) && (period != 0)) {
- fprintf(stderr,
- "-r<rate> must be set to use -p<test-period>\n");
- usage();
- exit(2);
- }
- if ((rate == 0) &&
- ((maxdrop[0] != 0) || (maxdrop[1] != 0) ||
- (maxpdrop[0] != 0.) || (maxpdrop[1] != 0.))) {
- fprintf(stderr,
- "-r<rate> must be set to use -D<max-drop>\n");
- usage();
- exit(2);
- }
-
- /* check (first) template file options */
- if ((templatefile[0] == NULL) && (xidoffset[0] >= 0)) {
- fprintf(stderr,
- "-T<template-file> must be set to "
- "use -X<xid-offset>\n");
- usage();
- exit(2);
- }
- if ((templatefile[0] == NULL) && (rndoffset[0] >= 0)) {
- fprintf(stderr,
- "-T<template-file> must be set to "
- "use -O<random-offset>\n");
- usage();
- exit(2);
- }
-
- /* check (second) template file options */
- if ((templatefile[1] == NULL) && (elpoffset >= 0)) {
- fprintf(stderr,
- "second/request -T<template-file> must be set to "
- "use -E<time-offset>\n");
- usage();
- exit(2);
- }
- if ((templatefile[1] == NULL) && (sidoffset >= 0)) {
- fprintf(stderr,
- "second/request -T<template-file> must be set to "
- "use -S<srvid-offset>\n");
- usage();
- exit(2);
- }
- if ((templatefile[1] == NULL) && (ripoffset >= 0)) {
- fprintf(stderr,
- "second/request -T<template-file> must be set to "
- "use -I<ip-offset>\n");
- usage();
- exit(2);
- }
-
- /* check various template file(s) and other condition(s) options */
- if ((templatefile[0] != NULL) && (range > 0) && (rndoffset[0] < 0)) {
- fprintf(stderr,
- "-O<random-offset> must be set when "
- "-T<template-file> and -R<range> are used\n");
- usage();
- exit(2);
- }
- if ((templatefile[1] != NULL) && (sidoffset < 0)) {
- fprintf(stderr,
- "-S<srvid-offset> must be set when second "
- "-T<template-file> is used\n");
- usage();
- exit(2);
- }
- if ((templatefile[1] != NULL) && (ripoffset < 0)) {
- fprintf(stderr,
- "-I<ip-offset> must be set when second "
- "-T<template-file> is used\n");
- usage();
- exit(2);
- }
-
- /* get the server argument */
- if (optind < argc - 1) {
- fprintf(stderr, "extra arguments?\n");
- usage();
- exit(2);
- }
- if (optind == argc - 1) {
- servername = argv[optind];
- /* decode special cases */
- if ((ipversion == 4) &&
- (strcmp(servername, "all") == 0)) {
- flags = AI_NUMERICHOST;
- isbroadcast = 1;
- servername = "255.255.255.255";
- } else if ((ipversion == 6) &&
- (strcmp(servername, "all") == 0)) {
- flags = AI_NUMERICHOST;
- servername = "FF02::1:2";
- } else if ((ipversion == 6) &&
- (strcmp(servername, "servers") == 0)) {
- flags = AI_NUMERICHOST;
- servername = "FF05::1:3";
- }
- }
-
- /* handle the local '-l' address/interface */
- if (localname != NULL) {
- /* given */
- getlocaladdr();
- if ((diags != NULL) && (strchr(diags, 'a') != NULL)) {
- if (isinterface)
- printf("interface='%s'\n", localname);
- else
- printf("local-addr='%s'\n", localname);
- }
- /* get the not given server from it */
- if (servername == NULL) {
- if (isinterface && (ipversion == 4)) {
- flags = AI_NUMERICHOST;
- isbroadcast = 1;
- servername = "255.255.255.255";
- } else if (isinterface && (ipversion == 6)) {
- flags = AI_NUMERICHOST;
- servername = "FF02::1:2";
- } else {
- fprintf(stderr,
- "without an interface "
- "server is required\n");
- usage();
- exit(2);
- }
- }
- } else if (servername == NULL) {
- fprintf(stderr, "without an interface server is required\n");
- usage();
- exit(2);
- }
- /* get the server socket address */
- getserveraddr(flags);
- /* finish local/server socket address stuff and print it */
- if ((diags != NULL) && (strchr(diags, 'a') != NULL))
- printf("server='%s'\n", servername);
- if (localname == NULL)
- getlocal();
- if ((diags != NULL) && (strchr(diags, 'a') != NULL)) {
- char addr[NI_MAXHOST];
-
- ret = getnameinfo((struct sockaddr *) &localaddr,
- sizeof(localaddr),
- addr,
- NI_MAXHOST,
- NULL,
- 0,
- NI_NUMERICHOST);
- if (ret != 0) {
- fprintf(stderr,
- "can't get the local address: %s\n",
- gai_strerror(ret));
- exit(1);
- }
- printf("local address='%s'\n", addr);
- }
-
- /* initialize exchange structures */
- inits();
-
- /* get the socket descriptor and template(s) */
- if (ipversion == 4) {
- getsock4();
- if (templatefile[0] == NULL)
- build_template_discover4();
- else
- get_template_discover4();
- if (simple == 0) {
- if (templatefile[1] == NULL)
- build_template_request4();
- else
- get_template_request4();
- }
- } else {
- getsock6();
- if (duid_prefix != NULL) {
- if (templatefile[0] == NULL)
- build_template_solicit6();
- else
- get_template_solicit6();
- if (simple == 0) {
- if (templatefile[1] == NULL)
- build_template_request6();
- else
- get_template_request6();
- }
- }
- }
- /* sanity check */
- if ((unsigned) sock > FD_SETSIZE) {
- fprintf(stderr, "socket descriptor (%d) too large?!\n", sock);
- exit(1);
- }
- /* make the socket descriptor not blocking */
- flags = fcntl(sock, F_GETFL, 0);
- if (flags < 0) {
- perror("fcntl(F_GETFL)");
- exit(1);
- }
- if (fcntl(sock, F_SETFL, flags | O_NONBLOCK) < 0) {
- perror("fcntl(F_SETFL)");
- exit(1);
- }
-
- /* wrapped start */
- if (wrapped != NULL) {
- pid_t pid;
-
- (void) signal(SIGCHLD, reapchild);
- pid = fork();
- if (pid < 0) {
- perror("fork");
- exit(1);
- } else if (pid == 0)
- (void) execlp(wrapped, "start", (char *) NULL);
- }
-
- /* boot is done! */
- if (clock_gettime(CLOCK_REALTIME, &boot) < 0) {
- perror("clock_gettime(boot)");
- exit(1);
- }
-
- /* compute the next intermediate reporting date */
- if (report != 0) {
- dreport.tv_sec = boot.tv_sec + report;
- dreport.tv_nsec = boot.tv_nsec;
- }
-
- /* compute the DUID (the current date is needed) */
- if ((ipversion == 6) && (duid_prefix == NULL)) {
- uint32_t curdate;
-
- duid_length = 14;
- duid_prefix = (uint8_t *) malloc(duid_length);
- if (duid_prefix == NULL) {
- perror("malloc(duid)");
- exit(1);
- }
- duid_prefix[0] = DHCP6_DUID_LLT >> 8;
- duid_prefix[1] = DHCP6_DUID_LLT;
- duid_prefix[2] = DHCP_HTYPE_ETHER >> 8;
- duid_prefix[3] = DHCP_HTYPE_ETHER;
- curdate = htonl(boot.tv_sec - DHCP6_DUID_EPOCH);
- memcpy(duid_prefix + 4, &curdate, 4);
- memcpy(duid_prefix + 8, mac_prefix, 6);
- /* the DUID is in template(s) */
- if (templatefile[0] == NULL)
- build_template_solicit6();
- else
- get_template_solicit6();
- if (simple == 0) {
- if (templatefile[1] == NULL)
- build_template_request6();
- else
- get_template_request6();
- }
- }
-
- /* seed the random generator */
- if (seeded == 0)
- seed = (unsigned int) (boot.tv_sec + boot.tv_nsec);
- srandom(seed);
-
- /* preload the server with at least one packet */
- compsend = preload + 1;
- for (i = 0; i <= preload; i++) {
- if (ipversion == 4)
- ret = send4();
- else
- ret = send6();
- if (ret < 0) {
- /* failure at the first packet is fatal */
- if (i == 0) {
- fprintf(stderr,
- "initial send failed: %s\n",
- strerror(-ret));
- exit(1);
- }
- if ((errno == EAGAIN) ||
- (errno == EWOULDBLOCK) ||
- (errno == ENOBUFS) ||
- (errno == ENOMEM))
- locallimit++;
- fprintf(stderr, "preload send: %s\n", strerror(-ret));
- break;
- }
- }
-
- /* required only before the interrupted flag check */
- (void) signal(SIGINT, interrupt);
-
- /* main loop */
- for (;;) {
- struct timespec now, ts;
- fd_set rfds;
-
- /* immediate loop exit conditions */
- if (interrupted) {
- if ((diags != NULL) && (strchr(diags, 'e') != NULL))
- printf("interrupted\n");
- break;
- }
- if (fatal) {
- if ((diags != NULL) && (strchr(diags, 'e') != NULL))
- printf("got a fatal error\n");
- break;
- }
-
- /* get the date and use it */
- if (clock_gettime(CLOCK_REALTIME, &now) < 0) {
- perror("clock_gettime(now)");
- fatal = 1;
- continue;
- }
- if ((period != 0) &&
- ((boot.tv_sec + period < now.tv_sec) ||
- ((boot.tv_sec + period == now.tv_sec) &&
- (boot.tv_nsec < now.tv_nsec)))) {
- if ((diags != NULL) && (strchr(diags, 'e') != NULL))
- printf("reached test-period\n");
- break;
- }
- if ((report != 0) &&
- ((dreport.tv_sec < now.tv_sec) ||
- ((dreport.tv_sec == now.tv_sec) &&
- (dreport.tv_nsec < now.tv_nsec))))
- reporting();
-
- /* compute the delay for the next send */
- due = last;
- if (rate == 1)
- due.tv_sec += 1;
- else if (rate != 0)
- due.tv_nsec += 1010000000 / rate;
- else
- due.tv_nsec += 1;
- while (due.tv_nsec >= 1000000000) {
- due.tv_sec += 1;
- due.tv_nsec -= 1000000000;
- }
- ts = due;
- ts.tv_sec -= now.tv_sec;
- ts.tv_nsec -= now.tv_nsec;
- while (ts.tv_nsec < 0) {
- ts.tv_sec -= 1;
- ts.tv_nsec += 1000000000;
- }
- /* the send was already due? */
- if (ts.tv_sec < 0) {
- ts.tv_sec = ts.tv_nsec = 0;
- latesent++;
- }
-
- /* pselect() */
- FD_ZERO(&rfds);
- FD_SET(sock, &rfds);
- ret = pselect(sock + 1, &rfds, NULL, NULL, &ts, NULL);
- if (ret < 0) {
- if (errno == EINTR)
- continue;
- perror("pselect");
- fatal = 1;
- continue;
- }
-
- /* packet(s) to receive */
- while (ret == 1) {
- if (ipversion == 4)
- receive4();
- else
- receive6();
- if (recv(sock, ibuf, sizeof(ibuf), MSG_PEEK) <= 0)
- ret = 0;
- else
- multrcvd++;
- }
- if (fatal)
- continue;
-
- /* check receive loop exit conditions */
- if ((numreq[0] != 0) && ((int) xscount0 >= numreq[0])) {
- if ((diags != NULL) && (strchr(diags, 'e') != NULL))
- printf("reached num-request0\n");
- break;
- }
- if ((numreq[1] != 0) && ((int) xscount2 >= numreq[1])) {
- if ((diags != NULL) && (strchr(diags, 'e') != NULL))
- printf("reached num-request2\n");
- break;
- }
- if ((maxdrop[0] != 0) &&
- ((int) (xscount0 - xrcount0) > maxdrop[0])) {
- if ((diags != NULL) && (strchr(diags, 'e') != NULL))
- printf("reached max-drop%s (absolute)\n",
- simple != 0 ? "" : "0");
- break;
- }
- if ((maxdrop[1] != 0) &&
- ((int) (xscount2 - xrcount2) > maxdrop[1])) {
- if ((diags != NULL) && (strchr(diags, 'e') != NULL))
- printf("reached max-drop2 (absolute)\n");
- break;
- }
- if ((maxpdrop[0] != 0.) &&
- (xscount0 > 10) &&
- (((100. * (xscount0 - xrcount0)) / xscount0)
- > maxpdrop[0])) {
- if ((diags != NULL) && (strchr(diags, 'e') != NULL))
- printf("reached max-drop%s (percent)\n",
- simple != 0 ? "" : "0");
- break;
- }
- if ((maxpdrop[1] != 0.) &&
- (xscount2 > 10) &&
- (((100. * (xscount2 - xrcount2)) / xscount2)
- > maxpdrop[1])) {
- if ((diags != NULL) && (strchr(diags, 'e') != NULL))
- printf("reached max-drop2 (percent)\n");
- break;
- }
-
- /* compute how many packets to send */
- if (clock_gettime(CLOCK_REALTIME, &now) < 0) {
- perror("clock_gettime(now2)");
- fatal = 1;
- continue;
- }
- if ((now.tv_sec > due.tv_sec) ||
- ((now.tv_sec == due.tv_sec) &&
- (now.tv_nsec >= due.tv_nsec))) {
- double tosend;
-
- if (rate != 0) {
- tosend = (now.tv_nsec - due.tv_nsec) / 1e9;
- tosend += now.tv_sec - due.tv_sec;
- tosend *= rate;
- tosend += 1;
- if (tosend > (double) aggressivity)
- i = aggressivity;
- else
- i = (int) tosend;
- } else
- i = aggressivity;
- compsend += i;
- /* send packets */
- for (;;) {
- if (ipversion == 4)
- ret = send4();
- else
- ret = send6();
- if (ret < 0) {
- if ((errno == EAGAIN) ||
- (errno == EWOULDBLOCK) ||
- (errno == ENOBUFS) ||
- (errno == ENOMEM))
- locallimit++;
- fprintf(stderr,
- "send: %s\n", strerror(-ret));
- break;
- }
- i--;
- if (i == 0)
- break;
- /* check for late packets to receive */
- if (recv(sock, ibuf, sizeof(ibuf),
- MSG_PEEK) > 0) {
- latercvd++;
- if (ipversion == 4)
- receive4();
- else
- receive6();
- }
- }
- } else
- /* there was no packet to send */
- shortwait++;
- }
-
- /* after main loop: finished */
- if (clock_gettime(CLOCK_REALTIME, &finished) < 0)
- perror("clock_gettime(finished)");
-
- /* wrapped stop */
- if (wrapped != NULL) {
- pid_t pid;
-
- pid = fork();
- if (pid == 0)
- (void) execlp(wrapped, "stop", (char *) NULL);
- }
-
- /* main statictics */
- if (xscount2 == 0)
- printf("sent: %llu, received: %llu (drops: %lld)\n",
- (unsigned long long) xscount0,
- (unsigned long long) xrcount0,
- (long long) (xscount0 - xrcount0));
- else
- printf("sent: %llu/%llu, received: %llu/%llu "
- "(drops: %lld/%lld)\n",
- (unsigned long long) xscount0,
- (unsigned long long) xscount2,
- (unsigned long long) xrcount0,
- (unsigned long long) xrcount2,
- (long long) (xscount0 - xrcount0),
- (long long) (xscount2 - xrcount2));
- printf("tooshort: %llu, orphans: %llu, local limits: %llu\n",
- (unsigned long long) tooshort,
- (unsigned long long) orphans,
- (unsigned long long) locallimit);
-
- /* print the rate */
- if (finished.tv_sec != 0) {
- double dall, erate;
-
- dall = (finished.tv_nsec - boot.tv_nsec) / 1e9;
- dall += finished.tv_sec - boot.tv_sec;
- erate = xrcount0 / dall;
- if (rate != 0)
- printf("rate: %f (expected %d)\n", erate, rate);
- else
- printf("rate: %f\n", erate);
- }
-
- /* rate processing instrumentation */
- if ((diags != NULL) && (strchr(diags, 'i') != NULL)) {
- printf("latesent: %llu, compsend: %llu, shortwait: %llu\n"
- "multrcvd: %llu, latercvd: %llu, collected:%llu/%llu\n",
- (unsigned long long) latesent,
- (unsigned long long) compsend,
- (unsigned long long) shortwait,
- (unsigned long long) multrcvd,
- (unsigned long long) latercvd,
- (unsigned long long) collected[0],
- (unsigned long long) collected[1]);
- }
-
- /* round-time trip statistics */
- if (xrcount2 != 0) {
- double avg0, avg2, stddev0, stddev2;
-
- avg0 = dsum0 / xrcount0;
- avg2 = dsum2 / xrcount2;
- stddev0 = sqrt(dsumsq0 / xrcount0 - avg0 * avg0);
- stddev2 = sqrt(dsumsq2 / xrcount2 - avg2 * avg2);
- printf("RTT0: min/avg/max/stddev: %.3f/%.3f/%.3f/%.3f ms\n",
- dmin0 * 1e3, avg0 * 1e3, dmax0 * 1e3, stddev0 * 1e3);
- printf("RTT2: min/avg/max/stddev: %.3f/%.3f/%.3f/%.3f ms\n",
- dmin2 * 1e3, avg2 * 1e3, dmax2 * 1e3, stddev2 * 1e3);
- } else if (xrcount0 != 0) {
- double avg, stddev;
-
- avg = dsum0 / xrcount0;
- stddev = sqrt(dsumsq0 / xrcount0 - avg * avg);
- printf("RTT%s: min/avg/max/stddev: %.3f/%.3f/%.3f/%.3f ms\n",
- simple != 0 ? "" : "0",
- dmin0 * 1e3, avg * 1e3, dmax0 * 1e3, stddev * 1e3);
- }
-
- /* (first) server-ID option content */
- if ((diags != NULL) && (strchr(diags, 's') != NULL) &&
- !ISC_TAILQ_EMPTY(&xrcvd0)) {
- struct exchange *x;
- size_t n;
-
- printf("server-id: ");
- x = ISC_TAILQ_FIRST(&xrcvd0);
- if (ipversion == 4)
- n = 2;
- else
- n = 4;
- for (; n < x->sidlen; n++)
- printf("%02hhx", x->sid[n]);
- printf("\n");
- }
-
- /* all time-stamps */
- if ((diags != NULL) && (strchr(diags, 't') != NULL) &&
- !ISC_TAILQ_EMPTY(&xrcvd0)) {
- struct exchange *x;
-
- printf("\n\n");
- ISC_TAILQ_FOREACH(x, &xrcvd0, gchain)
- printf("%ld.%09ld %ld.%09ld\n",
- (long) x->ts0.tv_sec, x->ts0.tv_nsec,
- (long) x->ts1.tv_sec, x->ts1.tv_nsec);
-
- }
- if ((diags != NULL) && (strchr(diags, 't') != NULL) &&
- !ISC_TAILQ_EMPTY(&xrcvd2)) {
- struct exchange *x;
-
- printf("--\n");
- ISC_TAILQ_FOREACH(x, &xrcvd2, gchain)
- printf("%ld.%09ld %ld.%09ld %ld.%09ld %ld.%09ld\n",
- (long) x->ts0.tv_sec, x->ts0.tv_nsec,
- (long) x->ts1.tv_sec, x->ts1.tv_nsec,
- (long) x->ts2.tv_sec, x->ts2.tv_nsec,
- (long) x->ts3.tv_sec, x->ts3.tv_nsec);
-
- }
- if ((diags != NULL) && (strchr(diags, 't') != NULL) &&
- !ISC_TAILQ_EMPTY(&xrcvd0))
- printf("\n\n");
-
- /* template(s) */
- if ((diags != NULL) && (strchr(diags, 'T') != NULL)) {
- size_t n;
-
- if (ipversion == 4) {
- printf("length = %zu\n", length_discover4);
- printf("xid offset = %d\n", DHCP_OFF_XID);
- printf("xid length = 4\n");
- printf("random offset = %zu\n", random_discover4);
- printf("content:\n");
- for (n = 0; n < length_discover4; n++) {
- printf("%s%02hhx",
- (n & 15) == 0 ? "" : " ",
- template_discover4[n]);
- if ((n & 15) == 15)
- printf("\n");
- }
- if ((n & 15) != 15)
- printf("\n");
- if (simple != 0)
- goto doneT;
- printf("--\n");
- printf("length = %zu\n", length_request4);
- printf("xid offset = %d\n", DHCP_OFF_XID);
- printf("xid length = 4\n");
- printf("random offset = %zu\n", random_request4);
- if (elapsed_request4 > 0)
- printf("secs offset = %zu\n",
- elapsed_request4);
- printf("server-id offset = %zu\n", serverid_request4);
- printf("server-id length = %d\n", DHCP_OPTLEN_SRVID);
- printf("content:\n");
- printf("requested-ip-address offset = %zu\n",
- reqaddr_request4);
- printf("requested-ip-address length = %d\n", 4);
- for (n = 0; n < length_request4; n++) {
- printf("%s%02hhx",
- (n & 15) == 0 ? "" : " ",
- template_request4[n]);
- if ((n & 15) == 15)
- printf("\n");
- }
- printf("\n");
- } else {
- printf("length = %zu\n", length_solicit6);
- printf("xid offset = %d\n", DHCP6_OFF_XID);
- printf("xid length = 3\n");
- printf("random offset = %zu\n", random_solicit6);
- for (n = 0; n < length_solicit6; n++) {
- printf("%s%02hhx",
- (n & 15) == 0 ? "" : " ",
- template_solicit6[n]);
- if ((n & 15) == 15)
- printf("\n");
- }
- if ((n & 15) != 15)
- printf("\n");
- if (simple != 0)
- goto doneT;
- printf("--\n");
- printf("length = %zu\n", length_request6);
- printf("xid offset = %d\n", DHCP_OFF_XID);
- printf("xid length = 4\n");
- printf("random offset = %zu\n", random_request6);
- if (elapsed_request6 > 0)
- printf("secs offset = %zu\n",
- elapsed_request6);
- printf("server-id offset = %zu\n", serverid_request6);
- printf("content:\n");
- printf("requested-ip-address offset = %zu\n",
- reqaddr_request6);
- for (n = 0; n < length_request6; n++) {
- printf("%s%02hhx",
- (n & 15) == 0 ? "" : " ",
- template_request6[n]);
- if ((n & 15) == 15)
- printf("\n");
- }
- printf("\n");
- }
- }
- doneT:
-
- /* compute the exit code (and exit) */
- if (fatal)
- exit(1);
- else if ((xscount0 == xrcount0) && (xscount2 == xrcount2))
- exit(0);
- else
- exit(3);
-}
-
-#endif /* HAVE_GETIFADDRS */
diff --git a/tests/tools/perfdhcp/stats_mgr.h b/tests/tools/perfdhcp/stats_mgr.h
index a8dfa8b..0c2b68c 100644
--- a/tests/tools/perfdhcp/stats_mgr.h
+++ b/tests/tools/perfdhcp/stats_mgr.h
@@ -73,14 +73,19 @@ public:
/// \brief Increment operator.
const CustomCounter& operator++() {
++counter_;
- return(*this);
+ return (*this);
}
/// \brief Increment operator.
const CustomCounter& operator++(int) {
CustomCounter& this_counter(*this);
operator++();
- return(this_counter);
+ return (this_counter);
+ }
+
+ const CustomCounter& operator+=(int val) {
+ counter_ += val;
+ return (*this);
}
/// \brief Return counter value.
@@ -251,24 +256,30 @@ public:
/// \brief Constructor
///
/// \param xchg_type exchange type
+ /// \param drop_time maximum time elapsed before packet is
+ /// assumed dropped. Negative value disables it.
/// \param archive_enabled if true packets archive mode is enabled.
/// In this mode all packets are stored throughout the test execution.
- ExchangeStats(const ExchangeType xchg_type, const bool archive_enabled)
+ ExchangeStats(const ExchangeType xchg_type,
+ const double drop_time,
+ const bool archive_enabled)
: xchg_type_(xchg_type),
- sent_packets_(),
- rcvd_packets_(),
- archived_packets_(),
- archive_enabled_(archive_enabled),
- min_delay_(std::numeric_limits<double>::max()),
- max_delay_(0.),
- sum_delay_(0.),
- sum_delay_squared_(0.),
- orphans_(0),
- unordered_lookup_size_sum_(0),
- unordered_lookups_(0),
- ordered_lookups_(0),
- sent_packets_num_(0),
- rcvd_packets_num_(0)
+ sent_packets_(),
+ rcvd_packets_(),
+ archived_packets_(),
+ archive_enabled_(archive_enabled),
+ drop_time_(drop_time),
+ min_delay_(std::numeric_limits<double>::max()),
+ max_delay_(0.),
+ sum_delay_(0.),
+ sum_delay_squared_(0.),
+ orphans_(0),
+ collected_(0),
+ unordered_lookup_size_sum_(0),
+ unordered_lookups_(0),
+ ordered_lookups_(0),
+ sent_packets_num_(0),
+ rcvd_packets_num_(0)
{
next_sent_ = sent_packets_.begin();
}
@@ -370,6 +381,8 @@ public:
/// not found
boost::shared_ptr<T>
matchPackets(const boost::shared_ptr<T>& rcvd_packet) {
+ using namespace boost::posix_time;
+
if (!rcvd_packet) {
isc_throw(BadValue, "Received packet is null");
}
@@ -432,6 +445,20 @@ public:
sent_packets_.template project<0>(it);
break;
}
+ ptime now = microsec_clock::universal_time();
+ ptime packet_time = (*it)->getTimestamp();
+ time_period packet_period(packet_time, now);
+ if (!packet_period.is_null()) {
+ double period_fractional =
+ packet_period.length().total_seconds() +
+ (static_cast<double>(packet_period.length().fractional_seconds())
+ / packet_period.length().ticks_per_second());
+ if (drop_time_ > 0 &&
+ (period_fractional > drop_time_)) {
+ eraseSent(sent_packets_.template project<0>(it));
+ ++collected_;
+ }
+ }
}
}
@@ -510,6 +537,17 @@ public:
/// \return number of orphant received packets.
uint64_t getOrphans() const { return(orphans_); }
+ /// \brief Return number of garbage collected packets.
+ ///
+ /// Method returns number of garbage collected timed out
+ /// packets. Packet is assumed timed out when duration
+ /// between sending it to server and receiving server's
+ /// response is greater than value specified with -d<value>
+ /// command line argument.
+ ///
+ /// \return number of garbage collected packets.
+ uint64_t getCollectedNum() const { return(collected_); }
+
/// \brief Return average unordered lookup set size.
///
/// Method returns average unordered lookup set size.
@@ -603,9 +641,10 @@ public:
<< "avg delay: " << getAvgDelay() * 1e3 << " ms" << endl
<< "max delay: " << getMaxDelay() * 1e3 << " ms" << endl
<< "std deviation: " << getStdDevDelay() * 1e3 << " ms"
- << endl;
+ << endl
+ << "collected packets: " << getCollectedNum() << endl;
} catch (const Exception& e) {
- cout << "Unavailable! No packets received." << endl;
+ cout << "Delay summary unavailable! No packets received." << endl;
}
}
@@ -644,7 +683,7 @@ public:
idx.equal_range(hashTransid(rcvd_packet));
for (PktListTransidHashIterator it_archived = p.first;
it_archived != p.second;
- ++it) {
+ ++it_archived) {
if ((*it_archived)->getTransid() ==
rcvd_packet->getTransid()) {
boost::shared_ptr<T> sent_packet = *it_archived;
@@ -733,6 +772,10 @@ public:
/// to keep all packets archived throughout the test.
bool archive_enabled_;
+ /// Maxmimum time elapsed between sending and receiving packet
+ /// before packet is assumed dropped.
+ double drop_time_;
+
double min_delay_; ///< Minimum delay between sent
///< and received packets.
double max_delay_; ///< Maximum delay between sent
@@ -744,6 +787,8 @@ public:
uint64_t orphans_; ///< Number of orphant received packets.
+ uint64_t collected_; ///< Number of garbage collected packets.
+
/// Sum of unordered lookup sets. Needed to calculate mean size of
/// lookup set. It is desired that number of unordered lookups is
/// minimal for performance reasons. Tracking number of lookups and
@@ -786,7 +831,6 @@ public:
/// archive mode is enabled.
StatsMgr(const bool archive_enabled = false) :
exchanges_(),
- custom_counters_(),
archive_enabled_(archive_enabled),
boot_time_(boost::posix_time::microsec_clock::universal_time()) {
}
@@ -798,13 +842,18 @@ public:
/// type.
///
/// \param xchg_type exchange type.
+ /// \param drop_time maximum time elapsed before packet is
+ /// assumed dropped. Negative value disables it.
/// \throw isc::BadValue if exchange of specified type exists.
- void addExchangeStats(const ExchangeType xchg_type) {
+ void addExchangeStats(const ExchangeType xchg_type,
+ const double drop_time = -1) {
if (exchanges_.find(xchg_type) != exchanges_.end()) {
isc_throw(BadValue, "Exchange of specified type already added.");
}
exchanges_[xchg_type] =
- ExchangeStatsPtr(new ExchangeStats(xchg_type, archive_enabled_));
+ ExchangeStatsPtr(new ExchangeStats(xchg_type,
+ drop_time,
+ archive_enabled_));
}
/// \brief Add named custom uint64 counter.
@@ -824,6 +873,20 @@ public:
CustomCounterPtr(new CustomCounter(long_name));
}
+ /// \brief Check if any packet drops occured.
+ ///
+ // \return true, if packet drops occured.
+ bool droppedPackets() const {
+ for (ExchangesMapIterator it = exchanges_.begin();
+ it != exchanges_.end();
+ ++it) {
+ if (it->second->getDroppedPacketsNum() > 0) {
+ return (true);
+ }
+ }
+ return (false);
+ }
+
/// \brief Return specified counter.
///
/// Method returns specified counter.
@@ -844,11 +907,14 @@ public:
///
/// Increement counter value by one.
///
- /// \param counter_key key poitinh to the counter in the counters map.
+ /// \param counter_key key poiting to the counter in the counters map.
+ /// \param value value to increment counter by.
/// \return pointer to specified counter after incrementation.
- const CustomCounter& incrementCounter(const std::string& counter_key) {
+ const CustomCounter& incrementCounter(const std::string& counter_key,
+ const uint64_t value = 1) {
CustomCounterPtr counter = getCounter(counter_key);
- return(++(*counter));
+ *counter += value;
+ return (*counter);
}
/// \brief Adds new packet to the sent packets list.
@@ -1041,6 +1107,22 @@ public:
return(xchg_stats->getDroppedPacketsNum());
}
+ /// \brief Return number of garbage collected packets.
+ ///
+ /// Method returns number of garbage collected timed out
+ /// packets. Packet is assumed timed out when duration
+ /// between sending it to server and receiving server's
+ /// response is greater than value specified with -d<value>
+ /// command line argument.
+ ///
+ /// \throw isc::BadValue if invalid exchange type specified.
+ /// \return number of garbage collected packets.
+ uint64_t getCollectedNum(const ExchangeType xchg_type) const {
+ ExchangeStatsPtr xchg_stats = getExchangeStats(xchg_type);
+ return(xchg_stats->getCollectedNum());
+ }
+
+
/// \brief Get time period since the start of test.
///
/// Calculate dna return period since the test start. This
@@ -1128,7 +1210,7 @@ public:
stream_rcvd << sep << it->second->getRcvdPacketsNum();
stream_drops << sep << it->second->getDroppedPacketsNum();
}
- std::cout << "sent: " << stream_sent.str()
+ std::cout << "sent: " << stream_sent.str()
<< "; received: " << stream_rcvd.str()
<< "; drops: " << stream_drops.str()
<< std::endl;
diff --git a/tests/tools/perfdhcp/templates/Makefile.am b/tests/tools/perfdhcp/templates/Makefile.am
index 1eee19c..33930a7 100644
--- a/tests/tools/perfdhcp/templates/Makefile.am
+++ b/tests/tools/perfdhcp/templates/Makefile.am
@@ -1,8 +1,8 @@
SUBDIRS = .
-# The test1.hex and test2.hex are created by the TestControl.PacketTemplates
+# The test[1-5].hex are created by the TestControl.PacketTemplates
# unit tests and have to be removed.
-CLEANFILES = test1.hex test2.hex
+CLEANFILES = test1.hex test2.hex test3.hex test4.hex test5.hex
perfdhcpdir = $(pkgdatadir)
perfdhcp_DATA = discover-example.hex request4-example.hex
diff --git a/tests/tools/perfdhcp/test_control.cc b/tests/tools/perfdhcp/test_control.cc
index c154cf9..9376972 100644
--- a/tests/tools/perfdhcp/test_control.cc
+++ b/tests/tools/perfdhcp/test_control.cc
@@ -19,6 +19,7 @@
#include <stdint.h>
#include <unistd.h>
#include <signal.h>
+#include <sys/wait.h>
#include <boost/date_time/posix_time/posix_time.hpp>
@@ -378,6 +379,16 @@ TestControl::generateDuid(uint8_t& randomized) const {
return (duid);
}
+int
+TestControl::getElapsedTimeOffset() const {
+ int elp_offset = CommandOptions::instance().getIpVersion() == 4 ?
+ DHCPV4_ELAPSED_TIME_OFFSET : DHCPV6_ELAPSED_TIME_OFFSET;
+ if (CommandOptions::instance().getElapsedTimeOffset() > 0) {
+ elp_offset = CommandOptions::instance().getElapsedTimeOffset();
+ }
+ return (elp_offset);
+}
+
template<class T>
uint32_t
TestControl::getElapsedTime(const T& pkt1, const T& pkt2) {
@@ -438,6 +449,26 @@ TestControl::getNextExchangesNum() const {
return (0);
}
+int
+TestControl::getRandomOffset(const int arg_idx) const {
+ int rand_offset = CommandOptions::instance().getIpVersion() == 4 ?
+ DHCPV4_RANDOMIZATION_OFFSET : DHCPV6_RANDOMIZATION_OFFSET;
+ if (CommandOptions::instance().getRandomOffset().size() > arg_idx) {
+ rand_offset = CommandOptions::instance().getRandomOffset()[arg_idx];
+ }
+ return (rand_offset);
+}
+
+int
+TestControl::getRequestedIpOffset() const {
+ int rip_offset = CommandOptions::instance().getIpVersion() == 4 ?
+ DHCPV4_REQUESTED_IP_OFFSET : DHCPV6_IA_NA_OFFSET;
+ if (CommandOptions::instance().getRequestedIpOffset() > 0) {
+ rip_offset = CommandOptions::instance().getRequestedIpOffset();
+ }
+ return (rip_offset);
+}
+
uint64_t
TestControl::getRcvdPacketsNum(const ExchangeType xchg_type) const {
uint8_t ip_version = CommandOptions::instance().getIpVersion();
@@ -458,6 +489,16 @@ TestControl::getSentPacketsNum(const ExchangeType xchg_type) const {
getSentPacketsNum(static_cast<StatsMgr6::ExchangeType>(xchg_type)));
}
+int
+TestControl::getServerIdOffset() const {
+ int srvid_offset = CommandOptions::instance().getIpVersion() == 4 ?
+ DHCPV4_SERVERID_OFFSET : DHCPV6_SERVERID_OFFSET;
+ if (CommandOptions::instance().getServerIdOffset() > 0) {
+ srvid_offset = CommandOptions::instance().getServerIdOffset();
+ }
+ return (srvid_offset);
+}
+
TestControl::TemplateBuffer
TestControl::getTemplateBuffer(const size_t idx) const {
if (template_buffers_.size() > idx) {
@@ -466,6 +507,24 @@ TestControl::getTemplateBuffer(const size_t idx) const {
isc_throw(OutOfRange, "invalid buffer index");
}
+int
+TestControl::getTransactionIdOffset(const int arg_idx) const {
+ int xid_offset = CommandOptions::instance().getIpVersion() == 4 ?
+ DHCPV4_TRANSID_OFFSET : DHCPV6_TRANSID_OFFSET;
+ if (CommandOptions::instance().getTransactionIdOffset().size() > arg_idx) {
+ xid_offset = CommandOptions::instance().getTransactionIdOffset()[arg_idx];
+ }
+ return (xid_offset);
+}
+
+void
+TestControl::handleChild(int) {
+ int status = 0;
+ while (wait3(&status, WNOHANG, NULL) > 0) {
+ // continue
+ }
+}
+
void
TestControl::handleInterrupt(int) {
interrupted_ = true;
@@ -473,6 +532,8 @@ TestControl::handleInterrupt(int) {
void
TestControl::initPacketTemplates() {
+ template_packets_v4_.clear();
+ template_packets_v6_.clear();
template_buffers_.clear();
CommandOptions& options = CommandOptions::instance();
std::vector<std::string> template_files = options.getTemplateFiles();
@@ -485,20 +546,28 @@ TestControl::initPacketTemplates() {
void
TestControl::initializeStatsMgr() {
CommandOptions& options = CommandOptions::instance();
+ // Check if packet archive mode is required. If user
+ // requested diagnostics option -x t we have to enable
+ // it so as StatsMgr preserves all packets.
+ const bool archive_mode = testDiags('t') ? true : false;
if (options.getIpVersion() == 4) {
stats_mgr4_.reset();
- stats_mgr4_ = StatsMgr4Ptr(new StatsMgr4());
- stats_mgr4_->addExchangeStats(StatsMgr4::XCHG_DO);
+ stats_mgr4_ = StatsMgr4Ptr(new StatsMgr4(archive_mode));
+ stats_mgr4_->addExchangeStats(StatsMgr4::XCHG_DO,
+ options.getDropTime()[0]);
if (options.getExchangeMode() == CommandOptions::DORA_SARR) {
- stats_mgr4_->addExchangeStats(StatsMgr4::XCHG_RA);
+ stats_mgr4_->addExchangeStats(StatsMgr4::XCHG_RA,
+ options.getDropTime()[1]);
}
} else if (options.getIpVersion() == 6) {
stats_mgr6_.reset();
- stats_mgr6_ = StatsMgr6Ptr(new StatsMgr6());
- stats_mgr6_->addExchangeStats(StatsMgr6::XCHG_SA);
+ stats_mgr6_ = StatsMgr6Ptr(new StatsMgr6(archive_mode));
+ stats_mgr6_->addExchangeStats(StatsMgr6::XCHG_SA,
+ options.getDropTime()[0]);
if (options.getExchangeMode() == CommandOptions::DORA_SARR) {
- stats_mgr6_->addExchangeStats(StatsMgr6::XCHG_RR);
+ stats_mgr6_->addExchangeStats(StatsMgr6::XCHG_RR,
+ options.getDropTime()[1]);
}
}
if (testDiags('i')) {
@@ -506,12 +575,12 @@ TestControl::initializeStatsMgr() {
stats_mgr4_->addCustomCounter("latesend", "Late sent packets");
stats_mgr4_->addCustomCounter("shortwait", "Short waits for packets");
stats_mgr4_->addCustomCounter("multircvd", "Multiple packets receives");
- // stats_mgr4_->addCustomCounter("latercvd", "Late received packets");
+ stats_mgr4_->addCustomCounter("latercvd", "Late received packets");
} else if (options.getIpVersion() == 6) {
stats_mgr6_->addCustomCounter("latesend", "Late sent packets");
stats_mgr6_->addCustomCounter("shortwait", "Short waits for packets");
stats_mgr6_->addCustomCounter("multircvd", "Multiple packets receives");
- // stats_mgr6_->addCustomCounter("latercvd", "Late received packets");
+ stats_mgr6_->addCustomCounter("latercvd", "Late received packets");
}
}
}
@@ -605,6 +674,47 @@ TestControl::openSocket() const {
}
void
+TestControl::sendPackets(const TestControlSocket& socket,
+ const uint64_t packets_num,
+ const bool preload /* = false */) {
+ CommandOptions& options = CommandOptions::instance();
+ for (uint64_t i = packets_num; i > 0; --i) {
+ if (options.getIpVersion() == 4) {
+ // No template packets means that no -T option was specified.
+ // We have to build packets ourselfs.
+ if (template_buffers_.size() == 0) {
+ sendDiscover4(socket, preload);
+ } else {
+ // @todo add defines for packet type index that can be
+ // used to access template_buffers_.
+ sendDiscover4(socket, template_buffers_[0], preload);
+ }
+ } else {
+ // No template packets means that no -T option was specified.
+ // We have to build packets ourselfs.
+ if (template_buffers_.size() == 0) {
+ sendSolicit6(socket, preload);
+ } else {
+ // @todo add defines for packet type index that can be
+ // used to access template_buffers_.
+ sendSolicit6(socket, template_buffers_[0], preload);
+ }
+ }
+ // If we preload server we don't want to receive any packets.
+ if (!preload) {
+ uint64_t latercvd = receivePackets(socket);
+ if (testDiags('i')) {
+ if (options.getIpVersion() == 4) {
+ stats_mgr4_->incrementCounter("latercvd", latercvd);
+ } else if (options.getIpVersion() == 6) {
+ stats_mgr6_->incrementCounter("latercvd", latercvd);
+ }
+ }
+ }
+ }
+}
+
+void
TestControl::printDiagnostics() const {
CommandOptions& options = CommandOptions::instance();
if (testDiags('a')) {
@@ -620,6 +730,75 @@ TestControl::printDiagnostics() const {
}
void
+TestControl::printTemplate(const uint8_t packet_type) const {
+ std::string hex_buf;
+ int arg_idx = 0;
+ if (CommandOptions::instance().getIpVersion() == 4) {
+ if (packet_type == DHCPREQUEST) {
+ arg_idx = 1;
+ }
+ std::map<uint8_t, dhcp::Pkt4Ptr>::const_iterator pkt_it =
+ template_packets_v4_.find(packet_type);
+ if ((pkt_it != template_packets_v4_.end()) &&
+ pkt_it->second) {
+ const util::OutputBuffer& out_buf(pkt_it->second->getBuffer());
+ const char* out_buf_data =
+ static_cast<const char*>(out_buf.getData());
+ std::vector<uint8_t> buf(out_buf_data, out_buf_data + out_buf.getLength());
+ hex_buf = vector2Hex(buf);
+ }
+ } else if (CommandOptions::instance().getIpVersion() == 6) {
+ if (packet_type == DHCPV6_REQUEST) {
+ arg_idx = 1;
+ }
+ std::map<uint8_t, dhcp::Pkt6Ptr>::const_iterator pkt_it =
+ template_packets_v6_.find(packet_type);
+ if (pkt_it != template_packets_v6_.end() &&
+ pkt_it->second) {
+ const util::OutputBuffer& out_buf(pkt_it->second->getBuffer());
+ const char* out_buf_data =
+ static_cast<const char*>(out_buf.getData());
+ std::vector<uint8_t> buf(out_buf_data, out_buf_data + out_buf.getLength());
+ hex_buf = vector2Hex(buf);
+ }
+ }
+ std::cout << "xid-offset=" << getTransactionIdOffset(arg_idx) << std::endl;
+ std::cout << "random-offset=" << getRandomOffset(arg_idx) << std::endl;
+ if (arg_idx > 0) {
+ std::cout << "srvid-offset=" << getServerIdOffset() << std::endl;
+ std::cout << "time-offset=" << getElapsedTimeOffset() << std::endl;
+ std::cout << "ip-offset=" << getRequestedIpOffset() << std::endl;
+ }
+
+ std::cout << "contents: " << std::endl;
+ int line_len = 32;
+ int i = 0;
+ while (line_len == 32) {
+ if (hex_buf.length() - i < 32) {
+ line_len = hex_buf.length() - i;
+ };
+ if (line_len > 0) {
+ std::cout << setfill('0') << setw(4) << std::hex << i << std::dec
+ << " " << hex_buf.substr(i, line_len) << std::endl;
+ }
+ i += 32;
+ }
+ std::cout << std::endl;
+}
+
+void
+TestControl::printTemplates() const {
+ CommandOptions& options = CommandOptions::instance();
+ if (options.getIpVersion() == 4) {
+ printTemplate(DHCPDISCOVER);
+ printTemplate(DHCPREQUEST);
+ } else if (options.getIpVersion() == 6) {
+ printTemplate(DHCPV6_SOLICIT);
+ printTemplate(DHCPV6_REQUEST);
+ }
+}
+
+void
TestControl::printRate() const {
double rate = 0;
CommandOptions& options = CommandOptions::instance();
@@ -634,8 +813,8 @@ TestControl::printRate() const {
}
std::cout << "***Rate statistics***" << std::endl;
if (options.getRate() > 0) {
- std::cout << "Rate: " << rate << ", expected rate: "
- << options.getRate() << std::endl << std::endl;
+ std::cout << "Rate: " << rate << " exchanges/second, expected rate: "
+ << options.getRate() << " exchanges/second" << std::endl << std::endl;
} else {
std::cout << "Rate: " << rate << std::endl << std::endl;
}
@@ -705,21 +884,38 @@ TestControl::readPacketTemplate(const std::string& file_name) {
if (!temp_file.is_open()) {
isc_throw(BadValue, "unable to open template file " << file_name);
}
- std::ifstream::pos_type temp_size = temp_file.tellg();
- if (temp_size % 2 != 0) {
+ // Read template file contents.
+ std::streampos temp_size = temp_file.tellg();
+ if (temp_size == std::streampos(0)) {
temp_file.close();
- isc_throw(BadValue, "odd number of digits in template file");
+ isc_throw(OutOfRange, "the template file " << file_name << " is empty");
}
temp_file.seekg(0, ios::beg);
- std::vector<char> hex_digits(temp_size);
- std::vector<uint8_t> binary_stream;
- temp_file.read(&hex_digits[0], temp_size);
+ std::vector<char> file_contents(temp_size);
+ temp_file.read(&file_contents[0], temp_size);
temp_file.close();
- for (int i = 0; i < hex_digits.size(); i += 2) {
- if (!isxdigit(hex_digits[i]) || !isxdigit(hex_digits[i+1])) {
- isc_throw(BadValue, "the '" << hex_digits[i] << hex_digits[i+1]
- << "' is not hexadecimal digit");
+ // Spaces are allowed so we have to strip the contents
+ // from them. In the same time we want to make sure that
+ // apart from spaces the file contains hexadecimal digits
+ // only.
+ std::vector<char> hex_digits;
+ for (int i = 0; i < file_contents.size(); ++i) {
+ if (isxdigit(file_contents[i])) {
+ hex_digits.push_back(file_contents[i]);
+ } else if (!isxdigit(file_contents[i]) &&
+ !isspace(file_contents[i])) {
+ isc_throw(BadValue, "the '" << file_contents[i] << "' is not a"
+ " heaxadecimal digit");
}
+ }
+ // Expect even number of digits.
+ if (hex_digits.size() % 2 != 0) {
+ isc_throw(OutOfRange, "odd number of digits in template file");
+ } else if (hex_digits.size() == 0) {
+ isc_throw(OutOfRange, "template file " << file_name << " is empty");
+ }
+ std::vector<uint8_t> binary_stream;
+ for (int i = 0; i < hex_digits.size(); i += 2) {
stringstream s;
s << "0x" << hex_digits[i] << hex_digits[i+1];
int b;
@@ -777,14 +973,20 @@ TestControl::processReceivedPacket6(const TestControlSocket& socket,
}
}
-void
+uint64_t
TestControl::receivePackets(const TestControlSocket& socket) {
int timeout = 0;
bool receiving = true;
uint64_t received = 0;
while (receiving) {
if (CommandOptions::instance().getIpVersion() == 4) {
- Pkt4Ptr pkt4 = IfaceMgr::instance().receive4(timeout);
+ Pkt4Ptr pkt4;
+ try {
+ pkt4 = IfaceMgr::instance().receive4(timeout);
+ } catch (const Exception& e) {
+ std::cerr << "Failed to receive DHCPv4 packet: "
+ << e.what() << std::endl;
+ }
if (!pkt4) {
receiving = false;
} else {
@@ -796,7 +998,13 @@ TestControl::receivePackets(const TestControlSocket& socket) {
processReceivedPacket4(socket, pkt4);
}
} else if (CommandOptions::instance().getIpVersion() == 6) {
- Pkt6Ptr pkt6 = IfaceMgr::instance().receive6(timeout);
+ Pkt6Ptr pkt6;
+ try {
+ pkt6 = IfaceMgr::instance().receive6(timeout);
+ } catch (const Exception& e) {
+ std::cerr << "Failed to receive DHCPv6 packet: "
+ << e.what() << std::endl;
+ }
if (!pkt6) {
receiving = false;
} else {
@@ -810,6 +1018,7 @@ TestControl::receivePackets(const TestControlSocket& socket) {
}
}
}
+ return (received);
}
void
@@ -896,7 +1105,7 @@ TestControl::reset() {
interrupted_ = false;
}
-void
+int
TestControl::run() {
// Reset singleton state before test starts.
reset();
@@ -910,14 +1119,14 @@ TestControl::run() {
isc_throw(InvalidOperation,
"command options must be parsed before running a test");
} else if (options.getIpVersion() == 4) {
- setTransidGenerator(NumberGeneratorPtr(new SequencialGenerator()));
+ setTransidGenerator(NumberGeneratorPtr(new SequentialGenerator()));
} else {
- setTransidGenerator(NumberGeneratorPtr(new SequencialGenerator(0x00FFFFFF)));
+ setTransidGenerator(NumberGeneratorPtr(new SequentialGenerator(0x00FFFFFF)));
}
uint32_t clients_num = options.getClientsNum() == 0 ?
1 : options.getClientsNum();
- setMacAddrGenerator(NumberGeneratorPtr(new SequencialGenerator(clients_num)));
+ setMacAddrGenerator(NumberGeneratorPtr(new SequentialGenerator(clients_num)));
// Diagnostics are command line options mainly.
printDiagnostics();
@@ -941,48 +1150,20 @@ TestControl::run() {
}
// If user interrupts the program we will exit gracefully.
signal(SIGINT, TestControl::handleInterrupt);
- // Preload server with number of packets.
- const bool do_preload = true;
- for (int i = 0; i < options.getPreload(); ++i) {
- if (options.getIpVersion() == 4) {
- // No template buffer means no -T option specified.
- // We will build packet ourselves.
- if (template_buffers_.size() == 0) {
- sendDiscover4(socket, do_preload);
- } else {
- // Pick template #0 if Discover is being sent.
- // For Request it would be #1.
- // @todo add defines for packet type index that can be
- // used to access template_buffers_.
- sendDiscover4(socket, template_buffers_[0],
- do_preload);
- }
- } else if (options.getIpVersion() == 6) {
- // No template buffer means no -T option specified.
- // We will build packet ourselfs.
- if (template_buffers_.size() == 0) {
- sendSolicit6(socket, do_preload);
- } else {
- // Pick template #0 if Solicit is being sent.
- // For Request it would be #1.
- // @todo add defines for packet type index that can be
- // used to access template_buffers_.
- sendSolicit6(socket, template_buffers_[0],
- do_preload);
- }
- }
+
+ // Preload server with the number of packets.
+ sendPackets(socket, options.getPreload(), true);
+
+ // Fork and run command specified with -w<wrapped-command>
+ if (!options.getWrapped().empty()) {
+ runWrapped();
}
+
// Initialize Statistics Manager. Release previous if any.
initializeStatsMgr();
for (;;) {
// Calculate send due based on when last exchange was initiated.
updateSendDue();
- // If test period finished, maximum number of packet drops
- // has been reached or test has been interrupted we have to
- // finish the test.
- if (checkExitConditions()) {
- break;
- }
// Calculate number of packets to be sent to stay
// catch up with rate.
uint64_t packets_due = getNextExchangesNum();
@@ -997,30 +1178,17 @@ TestControl::run() {
// @todo: set non-zero timeout for packets once we implement
// microseconds timeout in IfaceMgr.
receivePackets(socket);
- // Send packets.
- for (uint64_t i = packets_due; i > 0; --i) {
- if (options.getIpVersion() == 4) {
- // No template packets means that no -T option was specified.
- // We have to build packets ourselfs.
- if (template_buffers_.size() == 0) {
- sendDiscover4(socket);
- } else {
- // @todo add defines for packet type index that can be
- // used to access template_buffers_.
- sendDiscover4(socket, template_buffers_[0]);
- }
- } else {
- // No template packets means that no -T option was specified.
- // We have to build packets ourselfs.
- if (template_buffers_.size() == 0) {
- sendSolicit6(socket);
- } else {
- // @todo add defines for packet type index that can be
- // used to access template_buffers_.
- sendSolicit6(socket, template_buffers_[0]);
- }
- }
+
+ // If test period finished, maximum number of packet drops
+ // has been reached or test has been interrupted we have to
+ // finish the test.
+ if (checkExitConditions()) {
+ break;
}
+
+ // Initiate new DHCP packet exchanges.
+ sendPackets(socket, packets_due);
+
// Report delay means that user requested printing number
// of sent/received/dropped packets repeatedly.
if (options.getReportDelay() > 0) {
@@ -1028,14 +1196,79 @@ TestControl::run() {
}
}
printStats();
+
+ if (!options.getWrapped().empty()) {
+ // true means that we execute wrapped command with 'stop' argument.
+ runWrapped(true);
+ }
+
+ // Print packet timestamps
+ if (testDiags('t')) {
+ if (options.getIpVersion() == 4) {
+ stats_mgr4_->printTimestamps();
+ } else if (options.getIpVersion() == 6) {
+ stats_mgr6_->printTimestamps();
+ }
+ }
+
// Print server id.
if (testDiags('s') && (first_packet_serverid_.size() > 0)) {
std::cout << "Server id: " << vector2Hex(first_packet_serverid_) << std::endl;
}
+
// Diagnostics flag 'e' means show exit reason.
if (testDiags('e')) {
std::cout << "Interrupted" << std::endl;
}
+ // Print packet templates. Even if -T options have not been specified the
+ // dynamically build packet will be printed if at least one has been sent.
+ if (testDiags('T')) {
+ printTemplates();
+ }
+
+ int ret_code = 0;
+ // Check if any packet drops occured.
+ if (options.getIpVersion() == 4) {
+ ret_code = stats_mgr4_->droppedPackets() ? 3 : 0;
+ } else if (options.getIpVersion() == 6) {
+ ret_code = stats_mgr6_->droppedPackets() ? 3 : 0;
+ }
+ return (ret_code);
+}
+
+void
+TestControl::runWrapped(bool do_stop /*= false */) const {
+ CommandOptions& options = CommandOptions::instance();
+ if (!options.getWrapped().empty()) {
+ pid_t pid = 0;
+ signal(SIGCHLD, handleChild);
+ pid = fork();
+ if (pid < 0) {
+ isc_throw(Unexpected, "unable to fork");
+ } else if (pid == 0) {
+ execlp(options.getWrapped().c_str(),
+ do_stop ? "stop" : "start",
+ NULL);
+ }
+ }
+}
+
+void
+TestControl::saveFirstPacket(const Pkt4Ptr& pkt) {
+ if (testDiags('T')) {
+ if (template_packets_v4_.find(pkt->getType()) == template_packets_v4_.end()) {
+ template_packets_v4_[pkt->getType()] = pkt;
+ }
+ }
+}
+
+void
+TestControl::saveFirstPacket(const Pkt6Ptr& pkt) {
+ if (testDiags('T')) {
+ if (template_packets_v6_.find(pkt->getType()) == template_packets_v6_.end()) {
+ template_packets_v6_[pkt->getType()] = pkt;
+ }
+ }
}
void
@@ -1075,6 +1308,7 @@ TestControl::sendDiscover4(const TestControlSocket& socket,
}
stats_mgr4_->passSentPacket(StatsMgr4::XCHG_DO, pkt4);
}
+ saveFirstPacket(pkt4);
}
void
@@ -1084,7 +1318,6 @@ TestControl::sendDiscover4(const TestControlSocket& socket,
// last_sent_ has to be updated for each function that initiates
// new transaction. The packet exchange synchronization relies on this.
last_sent_ = microsec_clock::universal_time();
- CommandOptions& options = CommandOptions::instance();
// Get the first argument if mulitple the same arguments specified
// in the command line. First one refers to DISCOVER packets.
const uint8_t arg_idx = 0;
@@ -1094,18 +1327,11 @@ TestControl::sendDiscover4(const TestControlSocket& socket,
// Generate trasnaction id to be set for the new exchange.
const uint32_t transid = generateTransid();
// Get transaction id offset.
- size_t transid_offset = DHCPV4_TRANSID_OFFSET;
- if (options.getTransactionIdOffset().size() > arg_idx) {
- transid_offset = options.getTransactionIdOffset()[arg_idx];
- }
- // Calculate randomization offset.
- size_t rand_offset = DHCPV4_RANDOMIZATION_OFFSET;
- if (options.getRandomOffset().size() > arg_idx) {
- rand_offset = options.getRandomOffset()[arg_idx];
- }
+ size_t transid_offset = getTransactionIdOffset(arg_idx);
+ // Get randomization offset.
// We need to go back by HW_ETHER_LEN (MAC address length)
// because this offset points to last octet of MAC address.
- rand_offset -= HW_ETHER_LEN + 1;
+ size_t rand_offset = getRandomOffset(arg_idx) - HW_ETHER_LEN + 1;
// Create temporary buffer with template contents. We will
// modify this temporary buffer but we don't want to modify
// the original template.
@@ -1136,6 +1362,7 @@ TestControl::sendDiscover4(const TestControlSocket& socket,
stats_mgr4_->passSentPacket(StatsMgr4::XCHG_DO,
boost::static_pointer_cast<Pkt4>(pkt4));
}
+ saveFirstPacket(pkt4);
}
void
@@ -1149,6 +1376,8 @@ TestControl::sendRequest4(const TestControlSocket& socket,
OptionPtr opt_msg_type = Option::factory(Option::V4, DHO_DHCP_MESSAGE_TYPE,
buf_msg_type);
pkt4->addOption(opt_msg_type);
+ // Use first flags indicates that we want to use the server
+ // id captured in first packet.
if (CommandOptions::instance().isUseFirst() &&
(first_packet_serverid_.size() > 0)) {
pkt4->addOption(Option::factory(Option::V4, DHO_DHCP_SERVER_IDENTIFIER,
@@ -1199,6 +1428,7 @@ TestControl::sendRequest4(const TestControlSocket& socket,
"hasn't been initialized");
}
stats_mgr4_->passSentPacket(StatsMgr4::XCHG_RA, pkt4);
+ saveFirstPacket(pkt4);
}
void
@@ -1206,25 +1436,17 @@ TestControl::sendRequest4(const TestControlSocket& socket,
const std::vector<uint8_t>& template_buf,
const dhcp::Pkt4Ptr& discover_pkt4,
const dhcp::Pkt4Ptr& offer_pkt4) {
- CommandOptions& options = CommandOptions::instance();
// Get the second argument if multiple the same arguments specified
// in the command line. Second one refers to REQUEST packets.
const uint8_t arg_idx = 1;
// Generate new transaction id.
const uint32_t transid = generateTransid();
// Get transaction id offset.
- size_t transid_offset = DHCPV4_TRANSID_OFFSET;
- if (options.getTransactionIdOffset().size() > arg_idx) {
- transid_offset = options.getTransactionIdOffset()[arg_idx];
- }
+ size_t transid_offset = getTransactionIdOffset(arg_idx);
// Get the offset of MAC's last octet.
- size_t rand_offset = DHCPV4_RANDOMIZATION_OFFSET;
- if (options.getRandomOffset().size() > arg_idx) {
- rand_offset = options.getRandomOffset()[arg_idx];
- }
// We need to go back by HW_ETHER_LEN (MAC address length)
// because this offset points to last octet of MAC address.
- rand_offset -= HW_ETHER_LEN + 1;
+ size_t rand_offset = getRandomOffset(arg_idx) - HW_ETHER_LEN + 1;
// Create temporaru buffer from the template.
std::vector<uint8_t> in_buf(template_buf.begin(),
template_buf.end());
@@ -1244,19 +1466,15 @@ TestControl::sendRequest4(const TestControlSocket& socket,
pkt4->writeAt(rand_offset, mac_address.begin(), mac_address.end());
// Set elapsed time.
- size_t elp_offset = 0;
- if (options.getElapsedTimeOffset() > 0) {
- elp_offset = options.getElapsedTimeOffset();
- }
+ size_t elp_offset = getElapsedTimeOffset();
uint32_t elapsed_time = getElapsedTime<Pkt4Ptr>(discover_pkt4, offer_pkt4);
pkt4->writeValueAt<uint16_t>(elp_offset,
static_cast<uint16_t>(elapsed_time / 1000));
// Get the actual server id offset.
- size_t sid_offset = DHCPV4_SERVERID_OFFSET;
- if (options.getServerIdOffset() > 0) {
- sid_offset = options.getServerIdOffset();
- }
+ size_t sid_offset = getServerIdOffset();
+ // Use first flags indicates that we want to use the server
+ // id captured in first packet.
if (CommandOptions::instance().isUseFirst() &&
(first_packet_serverid_.size() > 0)) {
boost::shared_ptr<LocalizedOption>
@@ -1293,10 +1511,7 @@ TestControl::sendRequest4(const TestControlSocket& socket,
}
// Get the actual offset of requested ip.
- size_t rip_offset = DHCPV4_REQUESTED_IP_OFFSET;
- if (options.getRequestedIpOffset() > 0) {
- rip_offset = options.getRequestedIpOffset();
- }
+ size_t rip_offset = getRequestedIpOffset();
// Place requested IP option at specified position (rip_offset).
boost::shared_ptr<LocalizedOption>
opt_requested_ip(new LocalizedOption(Option::V4,
@@ -1318,6 +1533,7 @@ TestControl::sendRequest4(const TestControlSocket& socket,
// Update packet stats.
stats_mgr4_->passSentPacket(StatsMgr4::XCHG_RA,
boost::static_pointer_cast<Pkt4>(pkt4));
+ saveFirstPacket(pkt4);
}
void
@@ -1337,7 +1553,7 @@ TestControl::sendRequest6(const TestControlSocket& socket,
pkt6->addOption(opt_clientid);
// Use first flags indicates that we want to use the server
- // id captured in fisrt packet.
+ // id captured in first packet.
if (CommandOptions::instance().isUseFirst() &&
(first_packet_serverid_.size() > 0)) {
pkt6->addOption(Option::factory(Option::V6, D6O_SERVERID,
@@ -1370,40 +1586,33 @@ TestControl::sendRequest6(const TestControlSocket& socket,
"hasn't been initialized");
}
stats_mgr6_->passSentPacket(StatsMgr6::XCHG_RR, pkt6);
+ saveFirstPacket(pkt6);
}
void
TestControl::sendRequest6(const TestControlSocket& socket,
const std::vector<uint8_t>& template_buf,
const Pkt6Ptr& advertise_pkt6) {
- CommandOptions& options = CommandOptions::instance();
// Get the second argument if multiple the same arguments specified
// in the command line. Second one refers to REQUEST packets.
const uint8_t arg_idx = 1;
// Generate transaction id.
const uint32_t transid = generateTransid();
// Get transaction id offset.
- size_t transid_offset = DHCPV6_TRANSID_OFFSET;
- if (options.getTransactionIdOffset().size() > arg_idx) {
- transid_offset = options.getTransactionIdOffset()[arg_idx];
- }
+ size_t transid_offset = getTransactionIdOffset(arg_idx);
PerfPkt6Ptr pkt6(new PerfPkt6(&template_buf[0], template_buf.size(),
transid_offset, transid));
// Set elapsed time.
- size_t elp_offset = DHCPV6_ELAPSED_TIME_OFFSET;
- if (options.getElapsedTimeOffset() > 0) {
- elp_offset = options.getElapsedTimeOffset();
- }
+ size_t elp_offset = getElapsedTimeOffset();
boost::shared_ptr<LocalizedOption>
opt_elapsed_time(new LocalizedOption(Option::V6, D6O_ELAPSED_TIME,
OptionBuffer(), elp_offset));
pkt6->addOption(opt_elapsed_time);
// Get the actual server id offset.
- size_t sid_offset = DHCPV6_SERVERID_OFFSET;
- if (options.getServerIdOffset() > 0) {
- sid_offset = options.getServerIdOffset();
- }
+ size_t sid_offset = getServerIdOffset();
+ // Use first flags indicates that we want to use the server
+ // id captured in first packet.
if (CommandOptions::instance().isUseFirst() &&
(first_packet_serverid_.size() > 0)) {
boost::shared_ptr<LocalizedOption>
@@ -1439,10 +1648,7 @@ TestControl::sendRequest6(const TestControlSocket& socket,
isc_throw(Unexpected, "DHCPv6 IA_NA option not found in received "
"packet");
}
- size_t addr_offset = DHCPV6_IA_NA_OFFSET;
- if (options.getRequestedIpOffset() > 0) {
- addr_offset = options.getRequestedIpOffset();
- }
+ size_t addr_offset = getRequestedIpOffset();
boost::shared_ptr<LocalizedOption>
opt_ia_na(new LocalizedOption(opt_ia_na_advertise, addr_offset));
if (!opt_ia_na->valid()) {
@@ -1455,20 +1661,14 @@ TestControl::sendRequest6(const TestControlSocket& socket,
isc_throw(Unexpected, "DHCPV6 SERVERID option not found in received "
"packet");
}
- size_t srvid_offset = DHCPV6_SERVERID_OFFSET;
- if (options.getServerIdOffset() > 0) {
- srvid_offset = options.getServerIdOffset();
- }
+ size_t srvid_offset = getServerIdOffset();
boost::shared_ptr<LocalizedOption>
opt_serverid(new LocalizedOption(Option::V6, D6O_SERVERID,
opt_serverid_advertise->getData(),
srvid_offset));
pkt6->addOption(opt_serverid);
// Get randomization offset.
- size_t rand_offset = DHCPV6_RANDOMIZATION_OFFSET;
- if (options.getRandomOffset().size() > arg_idx) {
- rand_offset = options.getRandomOffset()[arg_idx];
- }
+ size_t rand_offset = getRandomOffset(arg_idx);
OptionPtr opt_clientid_advertise = advertise_pkt6->getOption(D6O_CLIENTID);
if (!opt_clientid_advertise) {
isc_throw(Unexpected, "DHCPV6 CLIENTID option not found in received packet");
@@ -1493,6 +1693,15 @@ TestControl::sendRequest6(const TestControlSocket& socket,
// Update packet stats.
stats_mgr6_->passSentPacket(StatsMgr6::XCHG_RR, pkt6);
+ // When 'T' diagnostics flag is specified it means that user requested
+ // printing packet contents. It will be just one (first) packet which
+ // contents will be printed. Here we check if this packet has been already
+ // collected. If it hasn't we save this packet so as we can print its
+ // contents when test is finished.
+ if (testDiags('T') &&
+ (template_packets_v6_.find(DHCPV6_REQUEST) == template_packets_v6_.end())) {
+ template_packets_v6_[DHCPV6_REQUEST] = pkt6;
+ }
}
void
@@ -1526,6 +1735,8 @@ TestControl::sendSolicit6(const TestControlSocket& socket,
}
stats_mgr6_->passSentPacket(StatsMgr6::XCHG_SA, pkt6);
}
+
+ saveFirstPacket(pkt6);
}
void
@@ -1533,13 +1744,9 @@ TestControl::sendSolicit6(const TestControlSocket& socket,
const std::vector<uint8_t>& template_buf,
const bool preload /*= false*/) {
last_sent_ = microsec_clock::universal_time();
- CommandOptions& options = CommandOptions::instance();
const int arg_idx = 0;
// Get transaction id offset.
- size_t transid_offset = DHCPV6_TRANSID_OFFSET;
- if (options.getTransactionIdOffset().size() > arg_idx) {
- transid_offset = options.getTransactionIdOffset()[arg_idx];
- }
+ size_t transid_offset = getTransactionIdOffset(arg_idx);
// Generate trasnaction id to be set for the new exchange.
const uint32_t transid = generateTransid();
// Create packet.
@@ -1548,10 +1755,7 @@ TestControl::sendSolicit6(const TestControlSocket& socket,
if (!pkt6) {
isc_throw(Unexpected, "failed to create SOLICIT packet");
}
- size_t rand_offset = DHCPV6_RANDOMIZATION_OFFSET;
- if (options.getRandomOffset().size() > arg_idx) {
- rand_offset = options.getRandomOffset()[arg_idx];
- }
+ size_t rand_offset = getRandomOffset(arg_idx);
// randomized will pick number of bytes randomized so we can
// just use part of the generated duid and substitude a few bytes
/// in template.
@@ -1577,6 +1781,7 @@ TestControl::sendSolicit6(const TestControlSocket& socket,
// Update packet stats.
stats_mgr6_->passSentPacket(StatsMgr6::XCHG_SA, pkt6);
}
+ saveFirstPacket(pkt6);
}
diff --git a/tests/tools/perfdhcp/test_control.h b/tests/tools/perfdhcp/test_control.h
index f89a7be..2bd7871 100644
--- a/tests/tools/perfdhcp/test_control.h
+++ b/tests/tools/perfdhcp/test_control.h
@@ -33,15 +33,79 @@
namespace isc {
namespace perfdhcp {
+/// Default transaction id offset in the packet template.
+static const size_t DHCPV4_TRANSID_OFFSET = 4;
+/// Default offset of MAC's last octet in the packet template..
+static const size_t DHCPV4_RANDOMIZATION_OFFSET = 35;
+/// Default elapsed time offset in the packet template.
+static const size_t DHCPV4_ELAPSED_TIME_OFFSET = 8;
+/// Default server id offset in the packet template.
+static const size_t DHCPV4_SERVERID_OFFSET = 54;
+/// Default requested ip offset in the packet template.
+static const size_t DHCPV4_REQUESTED_IP_OFFSET = 240;
+/// Default DHCPV6 transaction id offset in t the packet template.
+static const size_t DHCPV6_TRANSID_OFFSET = 1;
+/// Default DHCPV6 randomization offset (last octet of DUID)
+/// in the packet template.
+static const size_t DHCPV6_RANDOMIZATION_OFFSET = 21;
+/// Default DHCPV6 elapsed time offset in the packet template.
+static const size_t DHCPV6_ELAPSED_TIME_OFFSET = 84;
+/// Default DHCPV6 server id offset in the packet template.
+static const size_t DHCPV6_SERVERID_OFFSET = 22;
+/// Default DHCPV6 IA_NA offset in the packet template.
+static const size_t DHCPV6_IA_NA_OFFSET = 40;
+
/// \brief Test Control class.
///
-/// This class is responsible for executing DHCP performance
-/// test end to end.
+/// This singleton class is used to run the performance test with
+/// with \ref TestControl::run function. This function can be executed
+/// multiple times if desired because it resets TestControl's internal
+/// state every time it is executed. Prior to running \ref TestControl::run,
+/// one must make sure to parse command line options by calling
+/// \ref CommandOptions::parse. Failing to do this will result in an exception.
+///
+/// The following major stages of the test are performed by this class:
+/// - set default transaction id and MAC address generators - the generator
+/// is an object of \ref TestControl::NumberGenerator type and it provides
+/// the custom randomization algorithms,
+/// - print command line arguments,
+/// - register option factory functions which are used to generate DHCP options
+/// being sent to a server,
+/// - create the socket for communication with a server,
+/// - read packet templates if user specified template files with '-T' command
+/// line option,
+/// - set the interrupt handler (invoked when ^C is pressed) which makes
+/// perfdhcp stop gracefully and print the test results before exiting,
+/// - executes an external command (if specified '-w' option), e.g. if user
+/// specified -w ./foo in the command line then program will execute
+/// "./foo start" at the beginning of the test and "./foo stop" when the test
+/// ends,
+/// - initialize the Statistics Manager,
+/// - executes the main loop:
+/// - calculate how many packets must be send to satisfy desired rate,
+/// - receive incoming packets from the server,
+/// - check the exit conditions - terminate the program if the exit criteria
+/// are fulfiled, e.g. reached maximum number of packet drops,
+/// - send the number of packets appropriate to satisfy the desired rate,
+/// - optionally print intermediate reports,
+/// - print statistics, e.g. achieved rate,
+/// - optionally print some diagnostics.
+///
+/// With the '-w' command line option user may specify the external application
+/// or script to be executed. This is executed twice, first when the test starts
+/// and second time when the test ends. This external script or application must
+/// accept 'start' and 'stop' arguments. The first time it is called, it is
+/// called with the argument 'start' and the second time with the argument
+/// 'stop'.
+///
+/// The application is executed by calling fork() to fork the current perfdhcp
+/// process and then call execlp() to replace the current process image with
+/// the new one.
///
/// Option factory functions are registered using
/// \ref dhcp::LibDHCP::OptionFactoryRegister. Registered factory functions
/// provide a way to create options of the same type in the same way.
-/// When new option instance is needed the corresponding factory
+/// When a new option instance is needed, the corresponding factory
/// function is called to create it. This is done by calling
/// \ref dhcp::Option::factory with DHCP message type specified as one of
/// parameters. Some of the parameters passed to factory function
@@ -55,27 +119,6 @@ namespace perfdhcp {
class TestControl : public boost::noncopyable {
public:
- /// Default transaction id offset.
- static const size_t DHCPV4_TRANSID_OFFSET = 4;
- /// Default offset of MAC's last octet.
- static const size_t DHCPV4_RANDOMIZATION_OFFSET = 35;
- /// Default elapsed time offset.
- static const size_t DHCPV4_ELAPSED_TIME_OFFSET = 8;
- /// Default server id offset.
- static const size_t DHCPV4_SERVERID_OFFSET = 54;
- /// Default requested ip offset.
- static const size_t DHCPV4_REQUESTED_IP_OFFSET = 240;
- /// Default DHCPV6 transaction id offset.
- static const size_t DHCPV6_TRANSID_OFFSET = 1;
- /// Default DHCPV6 randomization offset (last octet of DUID)
- static const size_t DHCPV6_RANDOMIZATION_OFFSET = 21;
- /// Default DHCPV6 elapsed time offset.
- static const size_t DHCPV6_ELAPSED_TIME_OFFSET = 84;
- /// Default DHCPV6 server id offset.
- static const size_t DHCPV6_SERVERID_OFFSET = 22;
- /// Default DHCPV6 IA_NA offset.
- static const size_t DHCPV6_IA_NA_OFFSET = 40;
-
/// Statistics Manager for DHCPv4.
typedef StatsMgr<dhcp::Pkt4> StatsMgr4;
/// Pointer to Statistics Manager for DHCPv4;
@@ -138,7 +181,7 @@ public:
/// This is default numbers generator class. The member function is
/// used to generate uint32_t values. Other generator classes should
/// derive from this one to implement generation algorithms
- /// (e.g. sequencial or based on random function).
+ /// (e.g. sequential or based on random function).
class NumberGenerator {
public:
@@ -154,14 +197,14 @@ public:
/// The default generator pointer.
typedef boost::shared_ptr<NumberGenerator> NumberGeneratorPtr;
- /// \brief Sequencial numbers generatorc class.
- class SequencialGenerator : public NumberGenerator {
+ /// \brief Sequential numbers generatorc class.
+ class SequentialGenerator : public NumberGenerator {
public:
/// \brief Constructor.
///
/// \param range maximum number generated. If 0 is given then
- /// range defaults to maximym uint32_t value.
- SequencialGenerator(uint32_t range = 0xFFFFFFFF) :
+ /// range defaults to maximum uint32_t value.
+ SequentialGenerator(uint32_t range = 0xFFFFFFFF) :
NumberGenerator(),
num_(0),
range_(range) {
@@ -170,7 +213,7 @@ public:
}
}
- /// \brief Generate number sequencialy.
+ /// \brief Generate number sequentialy.
///
/// \return generated number.
virtual uint32_t generate() {
@@ -180,7 +223,7 @@ public:
}
private:
uint32_t num_; ///< Current number.
- uint32_t range_; ///< Maximum number generated.
+ uint32_t range_; ///< Number of unique numbers generated.
};
/// \brief Length of the Ethernet HW address (MAC) in bytes.
@@ -203,7 +246,9 @@ public:
///
/// \throw isc::InvalidOperation if command line options are not parsed.
/// \throw isc::Unexpected if internal Test Controler error occured.
- void run();
+ /// \return error_code, 3 if number of received packets is not equal
+ /// to number of sent packets, 0 if everything is ok.
+ int run();
/// \brief Set new transaction id generator.
///
@@ -412,7 +457,10 @@ protected:
/// their content and stores it in class internal buffers. Template
/// file names are specified from the command line with -T option.
///
- /// \throw isc::BadValue if any of the template files does not exist
+ /// \throw isc::BadValue if any of the template files does not exist,
+ /// contains characters other than hexadecimal digits or spaces.
+ /// \throw OutOfRange if any of the template files is empty or has
+ /// odd number of hexadecimal digits.
void initPacketTemplates();
/// \brief Initializes Statistics Manager.
@@ -496,8 +544,8 @@ protected:
/// \brief Receive DHCPv4 or DHCPv6 packets from the server.
///
/// Method receives DHCPv4 or DHCPv6 packets from the server.
- /// This function will call \ref receivePacket4 or
- /// \ref receivePacket6 depending if DHCPv4 or DHCPv6 packet
+ /// This function will call \ref processReceivedPacket4 or
+ /// \ref processReceivedPacket6 depending if DHCPv4 or DHCPv6 packet
/// has arrived.
///
/// \warning this method does not check if provided socket is
@@ -506,7 +554,8 @@ protected:
/// \param socket socket to be used.
/// \throw isc::BadValue if unknown message type received.
/// \throw isc::Unexpected if unexpected error occured.
- void receivePackets(const TestControlSocket& socket);
+ /// \return number of received packets.
+ uint64_t receivePackets(const TestControlSocket& socket);
/// \brief Register option factory functions for DHCPv4
///
@@ -539,6 +588,36 @@ protected:
/// called before new test is started.
void reset();
+ /// \brief Save the first DHCPv4 sent packet of the specified type.
+ ///
+ /// This method saves first packet of the specified being sent
+ /// to the server if user requested diagnostics flag 'T'. In
+ /// such case program has to print contents of selected packets
+ /// being sent to the server. It collects first packets of each
+ /// type and keeps them around until test finishes. Then they
+ /// are printed to the user. If packet of specified type has
+ /// been already stored this function perfroms no operation.
+ /// This function does not perform sanity check if packet
+ /// pointer is valid. Make sure it is before calling it.
+ ///
+ /// \param pkt packet to be stored.
+ inline void saveFirstPacket(const dhcp::Pkt4Ptr& pkt);
+
+ /// \brief Save the first DHCPv6 sent packet of the specified type.
+ ///
+ /// This method saves first packet of the specified being sent
+ /// to the server if user requested diagnostics flag 'T'. In
+ /// such case program has to print contents of selected packets
+ /// being sent to the server. It collects first packets of each
+ /// type and keeps them around until test finishes. Then they
+ /// are printed to the user. If packet of specified type has
+ /// been already stored this function perfroms no operation.
+ /// This function does not perform sainty check if packet
+ /// pointer is valid. Make sure it is before calling it.
+ ///
+ /// \param pkt packet to be stored.
+ inline void saveFirstPacket(const dhcp::Pkt6Ptr& pkt);
+
/// \brief Send DHCPv4 DISCOVER message.
///
/// Method creates and sends DHCPv4 DISCOVER message to the server
@@ -554,8 +633,10 @@ protected:
///
/// \param socket socket to be used to send the message.
/// \param preload preload mode, packets not included in statistics.
+ ///
/// \throw isc::Unexpected if failed to create new packet instance.
/// \throw isc::BadValue if MAC address has invalid length.
+ /// \throw isc::dhcp::SocketWriteError if failed to send the packet.
void sendDiscover4(const TestControlSocket& socket,
const bool preload = false);
@@ -571,11 +652,38 @@ protected:
/// \param socket socket to be used to send the message.
/// \param template_buf buffer holding template packet.
/// \param preload preload mode, packets not included in statistics.
+ ///
/// \throw isc::OutOfRange if randomization offset is out of bounds.
+ /// \throw isc::dhcp::SocketWriteError if failed to send the packet.
void sendDiscover4(const TestControlSocket& socket,
const std::vector<uint8_t>& template_buf,
const bool preload = false);
+ /// \brief Send number of packets to initiate new exchanges.
+ ///
+ /// Method initiates the new DHCP exchanges by sending number
+ /// of DISCOVER (DHCPv4) or SOLICIT (DHCPv6) packets. If preload
+ /// mode was requested sent packets will not be counted in
+ /// the statistics. The responses from the server will be
+ /// received and counted as orphans because corresponding sent
+ /// packets are not included in StatsMgr for match.
+ /// When preload mode is disabled and diagnostics flag 'i' is
+ /// specified then function will be trying to receive late packets
+ /// before new packets are sent to the server. Statistics of
+ /// late received packets is updated accordingly.
+ ///
+ /// \todo do not count responses in preload mode as orphans.
+ ///
+ /// \param socket socket to be used to send packets.
+ /// \param packets_num number of packets to be sent.
+ /// \param preload preload mode, packets not included in statistics.
+ /// \throw isc::Unexpected if thrown by packet sending method.
+ /// \throw isc::InvalidOperation if thrown by packet sending method.
+ /// \throw isc::OutOfRange if thrown by packet sending method.
+ void sendPackets(const TestControlSocket &socket,
+ const uint64_t packets_num,
+ const bool preload = false);
+
/// \brief Send DHCPv4 REQUEST message.
///
/// Method creates and sends DHCPv4 REQUEST message to the server.
@@ -585,9 +693,11 @@ protected:
/// \param socket socket to be used to send message.
/// \param discover_pkt4 DISCOVER packet sent.
/// \param offer_pkt4 OFFER packet object.
+ ///
/// \throw isc::Unexpected if unexpected error occured.
/// \throw isc::InvalidOperation if Statistics Manager has not been
/// initialized.
+ /// \throw isc::dhcp::SocketWriteError if failed to send the packet.
void sendRequest4(const TestControlSocket& socket,
const dhcp::Pkt4Ptr& discover_pkt4,
const dhcp::Pkt4Ptr& offer_pkt4);
@@ -602,6 +712,8 @@ protected:
/// \param template_buf buffer holding template packet.
/// \param discover_pkt4 DISCOVER packet sent.
/// \param offer_pkt4 OFFER packet received.
+ ///
+ /// \throw isc::dhcp::SocketWriteError if failed to send the packet.
void sendRequest4(const TestControlSocket& socket,
const std::vector<uint8_t>& template_buf,
const dhcp::Pkt4Ptr& discover_pkt4,
@@ -622,6 +734,8 @@ protected:
/// \throw isc::Unexpected if unexpected error occured.
/// \throw isc::InvalidOperation if Statistics Manager has not been
/// initialized.
+ ///
+ /// \throw isc::dhcp::SocketWriteError if failed to send the packet.
void sendRequest6(const TestControlSocket& socket,
const dhcp::Pkt6Ptr& advertise_pkt6);
@@ -634,6 +748,8 @@ protected:
/// \param socket socket to be used to send message.
/// \param template_buf packet template buffer.
/// \param advertise_pkt6 ADVERTISE packet object.
+ ///
+ /// \throw isc::dhcp::SocketWriteError if failed to send the packet.
void sendRequest6(const TestControlSocket& socket,
const std::vector<uint8_t>& template_buf,
const dhcp::Pkt6Ptr& advertise_pkt6);
@@ -652,7 +768,9 @@ protected:
///
/// \param socket socket to be used to send the message.
/// \param preload mode, packets not included in statistics.
+ ///
/// \throw isc::Unexpected if failed to create new packet instance.
+ /// \throw isc::dhcp::SocketWriteError if failed to send the packet.
void sendSolicit6(const TestControlSocket& socket,
const bool preload = false);
@@ -665,6 +783,8 @@ protected:
/// \param socket socket to be used to send the message.
/// \param template_buf packet template buffer.
/// \param preload mode, packets not included in statistics.
+ ///
+ /// \throw isc::dhcp::SocketWriteError if failed to send the packet.
void sendSolicit6(const TestControlSocket& socket,
const std::vector<uint8_t>& template_buf,
const bool preload = false);
@@ -732,6 +852,34 @@ private:
template<class T>
uint32_t getElapsedTime(const T& pkt1, const T& pkt2);
+ /// \brief Return elapsed time offset in a packet.
+ ///
+ /// \return elapsed time offset in packet.
+ int getElapsedTimeOffset() const;
+
+ /// \brief Return randomization offset in a packet.
+ ///
+ /// \return randomization offset in packet.
+ int getRandomOffset(const int arg_idx) const;
+
+ /// \brief Return requested ip offset in a packet.
+ ///
+ /// \return randomization offset in a packet.
+ int getRequestedIpOffset() const;
+
+ /// \brief Return server id offset in a packet.
+ ///
+ /// \return server id offset in packet.
+ int getServerIdOffset() const;
+
+ /// \brief Return transaction id offset in a packet.
+ ///
+ /// \param arg_idx command line argument index to be used.
+ /// If multiple -X parameters specifed it points to the
+ /// one to be used.
+ /// \return transaction id offset in packet.
+ int getTransactionIdOffset(const int arg_idx) const;
+
/// \brief Get number of received packets.
///
/// Get the number of received packets from the Statistics Manager.
@@ -750,6 +898,14 @@ private:
/// \return number of sent packets.
uint64_t getSentPacketsNum(const ExchangeType xchg_type) const;
+ /// \brief Handle child signal.
+ ///
+ /// Function handles child signal by waiting for
+ /// the process to complete.
+ ///
+ /// \param sig signal (ignored)
+ static void handleChild(int sig);
+
/// \brief Handle interrupt signal.
///
/// Function sets flag indicating that program has been
@@ -763,13 +919,35 @@ private:
/// Method prints main diagnostics data.
void printDiagnostics() const;
+ /// \brief Print template information
+ ///
+ /// \param packet_type packet type.
+ void printTemplate(const uint8_t packet_type) const;
+
+ /// \brief Print templates information.
+ ///
+ /// Method prints information about data offsets
+ /// in packet templates and their contents.
+ void printTemplates() const;
+
/// \brief Read DHCP message template from file.
///
/// Method reads DHCP message template from file and
/// converts it to binary format. Read data is appended
/// to template_buffers_ vector.
+ ///
+ /// \param file_name name of the packet template file.
+ /// \throw isc::OutOfRange if file is empty or has odd number
+ /// of hexadecimal digits.
+ /// \throw isc::BadValue if file contains characters other than
+ /// spaces or hexadecimal digits.
void readPacketTemplate(const std::string& file_name);
+ /// \brief Run wrapped command.
+ ///
+ /// \param do_stop execute wrapped command with "stop" argument.
+ void runWrapped(bool do_stop = false) const;
+
/// \brief Convert vector in hexadecimal string.
///
/// \todo Consider moving this function to src/lib/util.
@@ -798,6 +976,11 @@ private:
/// Packet template buffers.
TemplateBufferCollection template_buffers_;
+ /// First packets send. They are used at the end of the test
+ /// to print packet templates when diagnostics flag T is specifed.
+ std::map<uint8_t, dhcp::Pkt4Ptr> template_packets_v4_;
+ std::map<uint8_t, dhcp::Pkt6Ptr> template_packets_v6_;
+
static bool interrupted_; ///< Is program interrupted.
};
diff --git a/tests/tools/perfdhcp/tests/command_options_helper.h b/tests/tools/perfdhcp/tests/command_options_helper.h
index 2fc70ea..f9f6d36 100644
--- a/tests/tools/perfdhcp/tests/command_options_helper.h
+++ b/tests/tools/perfdhcp/tests/command_options_helper.h
@@ -87,13 +87,14 @@ public:
/// parsing.
///
/// \param cmdline command line provided as single string.
- static void process(const std::string& cmdline) {
+ /// \return true if program has been run in help or version mode ('h' or 'v' flag).
+ static bool process(const std::string& cmdline) {
CommandOptions& opt = CommandOptions::instance();
int argc = 0;
char** argv = tokenizeString(cmdline, argc);
ArgvPtr args(argv, argc);
opt.reset();
- opt.parse(args.getArgc(), args.getArgv());
+ return (opt.parse(args.getArgc(), args.getArgv()));
}
private:
diff --git a/tests/tools/perfdhcp/tests/command_options_unittest.cc b/tests/tools/perfdhcp/tests/command_options_unittest.cc
index 801b02d..360178f 100644
--- a/tests/tools/perfdhcp/tests/command_options_unittest.cc
+++ b/tests/tools/perfdhcp/tests/command_options_unittest.cc
@@ -45,10 +45,11 @@ protected:
/// parses arguments using CommandOptions class to set
/// its data members and de-allocates array of C-strings.
///
- /// \param cmdline Command line to parse
- /// \throws std::bad allocation if tokenization failed
- void process(const std::string& cmdline) {
- CommandOptionsHelper::process(cmdline);
+ /// \param cmdline Command line to parse.
+ /// \throws std::bad allocation if tokenization failed.
+ /// \return true if program has been run in help or version mode ('h' or 'v' flag).
+ bool process(const std::string& cmdline) {
+ return (CommandOptionsHelper::process(cmdline));
}
/// \brief Check default initialized values
@@ -56,7 +57,7 @@ protected:
/// Check if initialized values are correct
void checkDefaults() {
CommandOptions& opt = CommandOptions::instance();
- process("perfdhcp 192.168.0.1");
+ EXPECT_NO_THROW(process("perfdhcp 192.168.0.1"));
EXPECT_EQ(4, opt.getIpVersion());
EXPECT_EQ(CommandOptions::DORA_SARR, opt.getExchangeMode());
EXPECT_EQ(0, opt.getRate());
@@ -137,18 +138,31 @@ protected:
};
TEST_F(CommandOptionsTest, Defaults) {
- process("perfdhcp all");
+ EXPECT_NO_THROW(process("perfdhcp all"));
checkDefaults();
}
+TEST_F(CommandOptionsTest, HelpVersion) {
+ // The parser is supposed to return true if 'h' or 'v' options
+ // are specified.
+ EXPECT_TRUE(process("perfdhcp -h"));
+ EXPECT_TRUE(process("perfdhcp -v"));
+ EXPECT_TRUE(process("perfdhcp -h -v"));
+ EXPECT_TRUE(process("perfdhcp -6 -l ethx -h all"));
+ EXPECT_TRUE(process("perfdhcp -l ethx -v all"));
+ // No 'h' or 'v' option specified. The false value
+ // should be returned.
+ EXPECT_FALSE(process("perfdhcp -l ethx all"));
+}
+
TEST_F(CommandOptionsTest, UseFirst) {
CommandOptions& opt = CommandOptions::instance();
- process("perfdhcp -1 -B -l ethx all");
+ EXPECT_NO_THROW(process("perfdhcp -1 -B -l ethx all"));
EXPECT_TRUE(opt.isUseFirst());
}
TEST_F(CommandOptionsTest, IpVersion) {
CommandOptions& opt = CommandOptions::instance();
- process("perfdhcp -6 -l ethx -c -i all");
+ EXPECT_NO_THROW(process("perfdhcp -6 -l ethx -c -i all"));
EXPECT_EQ(6, opt.getIpVersion());
EXPECT_EQ("ethx", opt.getLocalName());
EXPECT_TRUE(opt.isRapidCommit());
@@ -169,7 +183,7 @@ TEST_F(CommandOptionsTest, IpVersion) {
TEST_F(CommandOptionsTest, Rate) {
CommandOptions& opt = CommandOptions::instance();
- process("perfdhcp -4 -r 10 -l ethx all");
+ EXPECT_NO_THROW(process("perfdhcp -4 -r 10 -l ethx all"));
EXPECT_EQ(10, opt.getRate());
// Negative test cases
@@ -189,7 +203,7 @@ TEST_F(CommandOptionsTest, Rate) {
TEST_F(CommandOptionsTest, ReportDelay) {
CommandOptions& opt = CommandOptions::instance();
- process("perfdhcp -r 100 -t 17 -l ethx all");
+ EXPECT_NO_THROW(process("perfdhcp -r 100 -t 17 -l ethx all"));
EXPECT_EQ(17, opt.getReportDelay());
// Negative test cases
@@ -204,7 +218,7 @@ TEST_F(CommandOptionsTest, ReportDelay) {
TEST_F(CommandOptionsTest, ClientsNum) {
CommandOptions& opt = CommandOptions::instance();
- process("perfdhcp -R 200 -l ethx all");
+ EXPECT_NO_THROW(process("perfdhcp -R 200 -l ethx all"));
EXPECT_EQ(200, opt.getClientsNum());
process("perfdhcp -R 0 -l ethx all");
EXPECT_EQ(0, opt.getClientsNum());
@@ -282,12 +296,12 @@ TEST_F(CommandOptionsTest, Base) {
TEST_F(CommandOptionsTest, DropTime) {
CommandOptions& opt = CommandOptions::instance();
- process("perfdhcp -l ethx -d 12 all");
+ EXPECT_NO_THROW(process("perfdhcp -l ethx -d 12 all"));
ASSERT_EQ(2, opt.getDropTime().size());
EXPECT_DOUBLE_EQ(12, opt.getDropTime()[0]);
EXPECT_DOUBLE_EQ(1, opt.getDropTime()[1]);
- process("perfdhcp -l ethx -d 2 -d 4.7 all");
+ EXPECT_NO_THROW(process("perfdhcp -l ethx -d 2 -d 4.7 all"));
ASSERT_EQ(2, opt.getDropTime().size());
EXPECT_DOUBLE_EQ(2, opt.getDropTime()[0]);
EXPECT_DOUBLE_EQ(4.7, opt.getDropTime()[1]);
@@ -302,7 +316,7 @@ TEST_F(CommandOptionsTest, DropTime) {
TEST_F(CommandOptionsTest, TimeOffset) {
CommandOptions& opt = CommandOptions::instance();
- process("perfdhcp -l ethx -T file1.x -T file2.x -E 4 all");
+ EXPECT_NO_THROW(process("perfdhcp -l ethx -T file1.x -T file2.x -E 4 all"));
EXPECT_EQ(4, opt.getElapsedTimeOffset());
// Negative test cases
@@ -339,8 +353,8 @@ TEST_F(CommandOptionsTest, ExchangeMode) {
TEST_F(CommandOptionsTest, Offsets) {
CommandOptions& opt = CommandOptions::instance();
- process("perfdhcp -E5 -4 -I 2 -S3 -O 30 -X7 -l ethx "
- "-X3 -T file1.x -T file2.x all");
+ EXPECT_NO_THROW(process("perfdhcp -E5 -4 -I 2 -S3 -O 30 -X7 -l ethx "
+ "-X3 -T file1.x -T file2.x all"));
EXPECT_EQ(2, opt.getRequestedIpOffset());
EXPECT_EQ(5, opt.getElapsedTimeOffset());
EXPECT_EQ(3, opt.getServerIdOffset());
@@ -363,7 +377,7 @@ TEST_F(CommandOptionsTest, Offsets) {
TEST_F(CommandOptionsTest, LocalPort) {
CommandOptions& opt = CommandOptions::instance();
- process("perfdhcp -l ethx -L 2000 all");
+ EXPECT_NO_THROW(process("perfdhcp -l ethx -L 2000 all"));
EXPECT_EQ(2000, opt.getLocalPort());
// Negative test cases
@@ -378,7 +392,7 @@ TEST_F(CommandOptionsTest, LocalPort) {
TEST_F(CommandOptionsTest, Preload) {
CommandOptions& opt = CommandOptions::instance();
- process("perfdhcp -1 -P 3 -l ethx all");
+ EXPECT_NO_THROW(process("perfdhcp -1 -P 3 -l ethx all"));
EXPECT_EQ(3, opt.getPreload());
// Negative test cases
@@ -391,11 +405,11 @@ TEST_F(CommandOptionsTest, Preload) {
TEST_F(CommandOptionsTest, Seed) {
CommandOptions& opt = CommandOptions::instance();
- process("perfdhcp -6 -P 2 -s 23 -l ethx all");
+ EXPECT_NO_THROW(process("perfdhcp -6 -P 2 -s 23 -l ethx all"));
EXPECT_EQ(23, opt.getSeed());
EXPECT_TRUE(opt.isSeeded());
- process("perfdhcp -6 -P 2 -s 0 -l ethx all");
+ EXPECT_NO_THROW(process("perfdhcp -6 -P 2 -s 0 -l ethx all"));
EXPECT_EQ(0, opt.getSeed());
EXPECT_FALSE(opt.isSeeded());
@@ -409,11 +423,11 @@ TEST_F(CommandOptionsTest, Seed) {
TEST_F(CommandOptionsTest, TemplateFiles) {
CommandOptions& opt = CommandOptions::instance();
- process("perfdhcp -T file1.x -l ethx all");
+ EXPECT_NO_THROW(process("perfdhcp -T file1.x -l ethx all"));
ASSERT_EQ(1, opt.getTemplateFiles().size());
EXPECT_EQ("file1.x", opt.getTemplateFiles()[0]);
- process("perfdhcp -T file1.x -s 12 -w start -T file2.x -4 -l ethx all");
+ EXPECT_NO_THROW(process("perfdhcp -T file1.x -s 12 -w start -T file2.x -4 -l ethx all"));
ASSERT_EQ(2, opt.getTemplateFiles().size());
EXPECT_EQ("file1.x", opt.getTemplateFiles()[0]);
EXPECT_EQ("file2.x", opt.getTemplateFiles()[1]);
@@ -430,7 +444,7 @@ TEST_F(CommandOptionsTest, TemplateFiles) {
TEST_F(CommandOptionsTest, Wrapped) {
CommandOptions& opt = CommandOptions::instance();
- process("perfdhcp -B -w start -i -l ethx all");
+ EXPECT_NO_THROW(process("perfdhcp -B -w start -i -l ethx all"));
EXPECT_EQ("start", opt.getWrapped());
// Negative test cases
@@ -441,7 +455,7 @@ TEST_F(CommandOptionsTest, Wrapped) {
TEST_F(CommandOptionsTest, Diagnostics) {
CommandOptions& opt = CommandOptions::instance();
- process("perfdhcp -l ethx -i -x asTe all");
+ EXPECT_NO_THROW(process("perfdhcp -l ethx -i -x asTe all"));
EXPECT_EQ("asTe", opt.getDiags());
// Negative test cases
@@ -467,18 +481,18 @@ TEST_F(CommandOptionsTest, Aggressivity) {
TEST_F(CommandOptionsTest, MaxDrop) {
CommandOptions& opt = CommandOptions::instance();
- process("perfdhcp -D 25 -l ethx -r 10 all");
+ EXPECT_NO_THROW(process("perfdhcp -D 25 -l ethx -r 10 all"));
EXPECT_EQ(25, opt.getMaxDrop()[0]);
- process("perfdhcp -D 25 -l ethx -D 15 -r 10 all");
+ EXPECT_NO_THROW(process("perfdhcp -D 25 -l ethx -D 15 -r 10 all"));
EXPECT_EQ(25, opt.getMaxDrop()[0]);
EXPECT_EQ(15, opt.getMaxDrop()[1]);
- process("perfdhcp -D 15% -l ethx -r 10 all");
+ EXPECT_NO_THROW(process("perfdhcp -D 15% -l ethx -r 10 all"));
EXPECT_EQ(15, opt.getMaxDropPercentage()[0]);
- process("perfdhcp -D 15% -D25% -l ethx -r 10 all");
+ EXPECT_NO_THROW(process("perfdhcp -D 15% -D25% -l ethx -r 10 all"));
EXPECT_EQ(15, opt.getMaxDropPercentage()[0]);
EXPECT_EQ(25, opt.getMaxDropPercentage()[1]);
- process("perfdhcp -D 1% -D 99% -l ethx -r 10 all");
+ EXPECT_NO_THROW(process("perfdhcp -D 1% -D 99% -l ethx -r 10 all"));
EXPECT_EQ(1, opt.getMaxDropPercentage()[0]);
EXPECT_EQ(99, opt.getMaxDropPercentage()[1]);
@@ -498,9 +512,9 @@ TEST_F(CommandOptionsTest, MaxDrop) {
TEST_F(CommandOptionsTest, NumRequest) {
CommandOptions& opt = CommandOptions::instance();
- process("perfdhcp -n 1000 -r 10 -l ethx all");
+ EXPECT_NO_THROW(process("perfdhcp -n 1000 -r 10 -l ethx all"));
EXPECT_EQ(1000, opt.getNumRequests()[0]);
- process("perfdhcp -n 5 -r 10 -n 500 -l ethx all");
+ EXPECT_NO_THROW(process("perfdhcp -n 5 -r 10 -n 500 -l ethx all"));
EXPECT_EQ(5, opt.getNumRequests()[0]);
EXPECT_EQ(500, opt.getNumRequests()[1]);
@@ -517,7 +531,7 @@ TEST_F(CommandOptionsTest, NumRequest) {
TEST_F(CommandOptionsTest, Period) {
CommandOptions& opt = CommandOptions::instance();
- process("perfdhcp -p 120 -l ethx -r 100 all");
+ EXPECT_NO_THROW(process("perfdhcp -p 120 -l ethx -r 100 all"));
EXPECT_EQ(120, opt.getPeriod());
// Negative test cases
@@ -567,19 +581,19 @@ TEST_F(CommandOptionsTest, Server) {
// set to broadcast address because 'all' was specified.
EXPECT_TRUE(opt.isBroadcast());
// The broadcast address is 255.255.255.255.
- EXPECT_EQ("255.255.255.255", opt.getServerName());
+ EXPECT_EQ(DHCP_IPV4_BROADCAST_ADDRESS, opt.getServerName());
// When all is specified for DHCPv6 mode we expect
// FF02::1:2 as a server name which means All DHCP
// servers and relay agents in local network segment
ASSERT_NO_THROW(process("perfdhcp -6 all"));
- EXPECT_EQ("FF02::1:2", opt.getServerName());
+ EXPECT_EQ(ALL_DHCP_RELAY_AGENTS_AND_SERVERS, opt.getServerName());
// When server='servers' in DHCPv6 mode we expect
// FF05::1:3 as server name which means All DHCP
// servers in local network.
ASSERT_NO_THROW(process("perfdhcp -6 servers"));
- EXPECT_EQ("FF05::1:3", opt.getServerName());
+ EXPECT_EQ(ALL_DHCP_SERVERS, opt.getServerName());
// If server name is neither 'all' nor 'servers'
// the given argument value is expected to be
diff --git a/tests/tools/perfdhcp/tests/stats_mgr_unittest.cc b/tests/tools/perfdhcp/tests/stats_mgr_unittest.cc
index d6b3aef..8a83cac 100644
--- a/tests/tools/perfdhcp/tests/stats_mgr_unittest.cc
+++ b/tests/tools/perfdhcp/tests/stats_mgr_unittest.cc
@@ -166,6 +166,7 @@ TEST_F(StatsMgrTest, Constructor) {
EXPECT_EQ(0, stats_mgr->getUnorderedLookups(StatsMgr4::XCHG_DO));
EXPECT_EQ(0, stats_mgr->getSentPacketsNum(StatsMgr4::XCHG_DO));
EXPECT_EQ(0, stats_mgr->getRcvdPacketsNum(StatsMgr4::XCHG_DO));
+ EXPECT_EQ(0, stats_mgr->getCollectedNum(StatsMgr4::XCHG_DO));
EXPECT_THROW(stats_mgr->getAvgDelay(StatsMgr4::XCHG_DO), InvalidOperation);
EXPECT_THROW(stats_mgr->getStdDevDelay(StatsMgr4::XCHG_DO),
@@ -342,7 +343,7 @@ TEST_F(StatsMgrTest, Orphans) {
TEST_F(StatsMgrTest, Delays) {
boost::shared_ptr<StatsMgr4> stats_mgr(new StatsMgr4());
- stats_mgr->addExchangeStats(StatsMgr4::XCHG_DO);
+ stats_mgr->addExchangeStats(StatsMgr4::XCHG_DO, 5);
// Send DISCOVER, wait 2s and receive OFFER. This will affect
// counters in Stats Manager.
diff --git a/tests/tools/perfdhcp/tests/test_control_unittest.cc b/tests/tools/perfdhcp/tests/test_control_unittest.cc
index a4cde00..66f85fe 100644
--- a/tests/tools/perfdhcp/tests/test_control_unittest.cc
+++ b/tests/tools/perfdhcp/tests/test_control_unittest.cc
@@ -92,7 +92,7 @@ public:
NakedTestControl() : TestControl() {
uint32_t clients_num = CommandOptions::instance().getClientsNum() == 0 ?
1 : CommandOptions::instance().getClientsNum();
- setMacAddrGenerator(NumberGeneratorPtr(new TestControl::SequencialGenerator(clients_num)));
+ setMacAddrGenerator(NumberGeneratorPtr(new TestControl::SequentialGenerator(clients_num)));
};
};
@@ -117,12 +117,20 @@ public:
/// \brief Create packet template file from binary data.
///
/// Function creates file containing data from the provided buffer
- /// in hexadecimal format.
+ /// in hexadecimal format. The size parameter specifies the maximum
+ /// size of the file. If total number of hexadecimal digits resulting
+ /// from buffer size is greater than maximum file size the file is
+ /// truncated.
+ ///
/// \param filename template file to be created.
/// \param buffer with binary datato be stored in file.
+ /// \param size target size of the file.
+ /// \param invalid_chars inject invalid chars to the template file.
/// \return true if file creation successful.
bool createTemplateFile(const std::string& filename,
- const std::vector<uint8_t>& buf) const {
+ const std::vector<uint8_t>& buf,
+ const size_t size,
+ const bool invalid_chars = false) const {
std::ofstream temp_file;
temp_file.open(filename.c_str(), ios::out | ios::trunc);
if (!temp_file.is_open()) {
@@ -131,7 +139,24 @@ public:
for (int i = 0; i < buf.size(); ++i) {
int first_digit = buf[i] / 16;
int second_digit = buf[i] % 16;
- temp_file << std::hex << first_digit << second_digit << std::dec;
+ // Insert two spaces between two hexadecimal digits.
+ // Spaces are allowed in template files.
+ temp_file << std::string(2, ' ');
+ if (2 * i + 1 < size) {
+ if (!invalid_chars) {
+ temp_file << std::hex << first_digit << second_digit << std::dec;
+ } else {
+ temp_file << "XY";
+ }
+ } else if (2 * i < size) {
+ if (!invalid_chars) {
+ temp_file << std::hex << first_digit;
+ } else {
+ temp_file << "X";
+ }
+ } else {
+ break;
+ }
}
temp_file.close();
return (true);
@@ -965,8 +990,9 @@ TEST_F(TestControlTest, PacketTemplates) {
for (int i = 0; i < template2.size(); ++i) {
template2[i] = static_cast<uint8_t>(random() % 256);
}
- ASSERT_TRUE(createTemplateFile(file1, template1));
- ASSERT_TRUE(createTemplateFile(file2, template2));
+ // Size of the file is 2 times larger than binary data size.
+ ASSERT_TRUE(createTemplateFile(file1, template1, template1.size() * 2));
+ ASSERT_TRUE(createTemplateFile(file2, template2, template2.size() * 2));
CommandOptions& options = CommandOptions::instance();
NakedTestControl tc;
@@ -983,6 +1009,32 @@ TEST_F(TestControlTest, PacketTemplates) {
ASSERT_EQ(template2.size(), buf2.size());
EXPECT_TRUE(std::equal(template1.begin(), template1.end(), buf1.begin()));
EXPECT_TRUE(std::equal(template2.begin(), template2.end(), buf2.begin()));
+
+ // Try to read template file with odd number of digits.
+ std::string file3("../templates/test3.hex");
+ // Size of the file is 2 times larger than binary data size and it is always
+ // even number. Substracting 1 makes file size odd.
+ ASSERT_TRUE(createTemplateFile(file3, template1, template1.size() * 2 - 1));
+ ASSERT_NO_THROW(
+ processCmdLine("perfdhcp -l 127.0.0.1 -T " + file3 + " all")
+ );
+ EXPECT_THROW(tc.initPacketTemplates(), isc::OutOfRange);
+
+ // Try to read empty file.
+ std::string file4("../templates/test4.hex");
+ ASSERT_TRUE(createTemplateFile(file4, template2, 0));
+ ASSERT_NO_THROW(
+ processCmdLine("perfdhcp -l 127.0.0.1 -T " + file4 + " all")
+ );
+ EXPECT_THROW(tc.initPacketTemplates(), isc::OutOfRange);
+
+ // Try reading file with non hexadecimal characters.
+ std::string file5("../templates/test5.hex");
+ ASSERT_TRUE(createTemplateFile(file5, template1, template1.size() * 2, true));
+ ASSERT_NO_THROW(
+ processCmdLine("perfdhcp -l 127.0.0.1 -T " + file5 + " all")
+ );
+ EXPECT_THROW(tc.initPacketTemplates(), isc::BadValue);
}
TEST_F(TestControlTest, RateControl) {
More information about the bind10-changes
mailing list