BIND 10 trac1175_reapplied, updated. 1006a9620414db12103f799466dde10b86fcbee6 [1175] remove the retrying part because there is no clear necessity
BIND 10 source code commits
bind10-changes at lists.isc.org
Fri Sep 30 01:31:56 UTC 2011
The branch, trac1175_reapplied has been updated
discards 4cc981dd8348e18f56c6eabfd2ec62ca888264fb (commit)
discards 05db5b8a87cbc6e9c4c8e686c3639ffb008bd795 (commit)
discards 00ef7b06259ca608c6e8d9e421137ba5b82b7f3a (commit)
discards 2dcae8e4ab1424bf72bc9e07ba75d7307da9327c (commit)
discards 6b5381b18740258fafb169acc071aaf8eeccb91c (commit)
discards 6d1a5d34b3e93934bd329ccd88c5d69571d5172e (commit)
discards 014a1174cfed568b54caa4edcacc2c109edc6038 (commit)
discards c01b95bbbd8dd42695ff7dd3f6576b9fd63be909 (commit)
discards 7670240e2e338fc0c5d4d1c869d6f6df35f97736 (commit)
discards 47b8f157b1a78b48ff1f671584f7b72527e3bc2d (commit)
discards d2c387636b375a75537774959b3fb32f415946bd (commit)
discards 54a6f9d12626355e9996062337406192f7129eb0 (commit)
discards 8fb1758311b0c17c6a7b50427a6dd57ee548915b (commit)
discards 19f02c8ae9f01f8093043a14949f3a016cbcfc9c (commit)
discards 79f1e1788cc5d70ebda0047cece816f5a98656a7 (commit)
discards 93e552a30631ef83b59a19526c64e919c5ed83e6 (commit)
discards 8b1640e4706d787dc568f0944e19afbc98644bf7 (commit)
discards 2802635d94d5cacbb7f9d41d1ee0ddd60695bd45 (commit)
discards e38a52d7f9bb779727edb66d8b01f95489be00ea (commit)
discards 198ab3a940305f2f93e5a32eefb45efcdc5f7949 (commit)
discards f9d0c4f689474149c476ae72d154f36163ffb886 (commit)
discards 48079e574d5c1b29e6affdcc86606e25a27c79ad (commit)
discards b73ca07b200f744f28f4e1f5d69a45e0d87dbb67 (commit)
discards 416f37522ad3fd0faf1867aec9ce5311e7025565 (commit)
discards 1f36d18f34f6e2ff0f6fe46a2879c80cab75fd0d (commit)
discards 85543666175c8be264747d992f0bf7a2614a1998 (commit)
discards d6e34898f7bba483766a603d1e3685706dc2183e (commit)
discards 9fb51bfc5f140e8dd54717ffdf5612950293e2d4 (commit)
discards ad12931f35e00137ce27bf4dc3cb5b5330c83cb0 (commit)
discards fc5a369dc627e9483b72cd7b5aac64965166fbc8 (commit)
discards 8381d954705ee95b76a07e372e9bf569cbab9c5d (commit)
discards d80ee0c91385e7d8f701e0ddcc9307346463f6a0 (commit)
discards 9fb29011e38ce708a44356bd2035f3cc3c9f5ee8 (commit)
discards dc9511274a054d2cbe0910e9a1523c472074c831 (commit)
discards 7d727fc559996f9cea92089834197d5e787ba851 (commit)
discards def6da447450a6578465b05a2d4fe191097b3ec6 (commit)
discards be82713a0375c6d054caa41b2cb8ffb9ba868a51 (commit)
discards e98f05903f818c3b9bbb2ac3d88ad7958a4e01c5 (commit)
discards fd52ce7c9559f988f02337ce3b29c9732703ef25 (commit)
discards 06be9d4343c2826e6d6823c2fb04dbfae32d3b91 (commit)
discards da66944b252d1d1ca5f617d9d6b3a4efe7d208ef (commit)
discards e7b8de7e275bfaa0cc1422972028a517099b0d94 (commit)
discards 7ed28efd266af1bc60139051273bc1302d5de314 (commit)
discards 3d210ea3529124eb85d86c8c5f24b37dbc7478d4 (commit)
discards 9d90b42489f84ecb3705cd75a7ae095551e6b074 (commit)
discards 8268ed44c570671c83e81a8622444003e91732ab (commit)
discards b3aafd78425b9e40a8751e9741c75e5ffb343361 (commit)
discards ce0cd9167bb2dddb4c3790f7506674a55207cdc4 (commit)
discards 71b7d0a8f30c7919ae33db990f46547a5263abf6 (commit)
discards a5d94ced1555dd0d1120f5507284703c180c0032 (commit)
discards 7854a68ea96f7d8a6882e2f88eb5d0b93c2e1838 (commit)
discards cb26f83fa6aa9015fe35873143ebff15cb562647 (commit)
discards 4d206ececbb6b5b17ebc33aacd69052e4440086f (commit)
discards 06e4e2306e583e164a62a99aeb2bb19f48b87681 (commit)
discards d546c5a4093a8f55d67b4a6e5a2e0f7f765fb188 (commit)
discards 57ab47653a29dacc3997424d0ea7a2bff3d019a2 (commit)
discards 96d91ef84b700d10fa3334e4943b15b3a344a40d (commit)
discards b97936f0e679f88174c7ae8033c6732327946c9f (commit)
discards 178e921a282cb14ed725630abd704c802b53a230 (commit)
discards 1d1767ad23047e5572df43dccce7b02988312162 (commit)
discards 7aebf9205317852dc2e65ffdc8669d1b0d464e8c (commit)
discards 428af4b27ba4d634735a1868318190848c0e77b9 (commit)
discards 8b831c7d6b41d7998162d04f934a6a1b1ee62e68 (commit)
discards b9626664d01a515afa0b9e5e64f13ac97f6e284c (commit)
discards 04c20971a768b542aa54237a2c5b8125ca42562d (commit)
discards eebcae5357f46c6e95ecee27ba6dba60d2e303fc (commit)
discards ecbf3bc2e7d6676109d60cf96eff0e08bfc94d3d (commit)
discards e2c0c2c4abfa2d00d491460bac2277dd9f390763 (commit)
discards 76360a50ff6819f86ede76824eafc2d9a820035e (commit)
discards ee92b92cd548918fdb013bffadcceb49c7129c61 (commit)
via 1006a9620414db12103f799466dde10b86fcbee6 (commit)
via b55e98a16cf7fd9db8af871b770b48f9c9146e05 (commit)
via 8fd664266678c3f10df02e229a55b428d7f0481a (commit)
via 5c334f5ca17c52d988db1e4d6f47d2b76a84be4a (commit)
via 010e6976f7961150586356ba9bb9e56b58ebf25f (commit)
via 86105ab0a1029a76ecf5a948d86efcfc87123cab (commit)
via b9bb528ab2141a8325877a1489dbb5928d58d404 (commit)
via 30659570e5ccbc8b0ab29e5d2139f4b6c54e55fb (commit)
via bc376ed5f17acd7b203ba36b2634363a3c9a12a1 (commit)
via 4c1c53b06a3b1d3b3b40698fd1cdc1aa3e356394 (commit)
via 0a0e98ffd310e33a1aa1bf353b09b2502f64d9be (commit)
via a48b3723667b0b87dbc70d4c7ee0b148331fd325 (commit)
via b2b0cc8260ae79e5406f0f206e805c85d88f0429 (commit)
via 7ebb37173dfec48ea13d513c6020a73183a6764d (commit)
via 885b69af7cabbfdff8e22255b9deb1705201da57 (commit)
via d6202285d5481b424eae9169aae0fa39cc34b271 (commit)
via 5a90a358b959edabf0c471bd84bac3255ca13596 (commit)
via 8e38c3cabdfd0d154678aa71712a52e08c516a2a (commit)
via b1168693dcdf910866fea348599d569b5ee33494 (commit)
via 56552bfe686c314a9a1a812e84b92aab6fdfb651 (commit)
via 2b78536997d1d17168d622f6a777cfdcd24882eb (commit)
via c72634c3bc4502890a57b7babbf534e9fe01eeaa (commit)
via c6370d65b8e2aad10be895cdba5677f6245f3144 (commit)
via adcc32e8f559f7525d709d801695bec1845065e6 (commit)
via 925e80e912191ce4bccf52d90ccaf4e665eb57e8 (commit)
via e6f9a14f57eb5f2dd1e2e622fa139ac7a6488d2d (commit)
via 0d943ba9d25211863c437ea1dd0d69d2bdde0067 (commit)
via 34af1d005f42009c31186c2dd6001a29c9773606 (commit)
via 47734cf9b10886fc8f49c3fe6e10ad3f24256d77 (commit)
via 093ef97943821e0423f060ae485b8bb798b351e6 (commit)
via 15623cb6fc32e4d303668c480c8eec5871dd2194 (commit)
via 45cab58eefc4c02a65803b08f1ded1e63f4b2c68 (commit)
via f9866a02cc7e3659d1b370ea6c9f5adc90560ba4 (commit)
via 16563433095c036dba4e2814838083a7ef89f752 (commit)
via 3cb838d07ed1bc285eeb255c36b32c8a76e35ffd (commit)
via 09d2b481e400a0fe519bd1227b0d7d29458c378f (commit)
via 08a3aa577ce47de0594264c267a38bceaf302061 (commit)
via f00403387266c310a2ea3618a1d4e336e75faae1 (commit)
via 546a7e4e387c85cfcf23dc476db3fb2244b84857 (commit)
via b2bac38d34242c760b0d396f943ffeda985fb640 (commit)
via 7fbe2a7eadcae6a8c4fbdb54de0786568de69122 (commit)
via 5aa4e96ef4f1c0a1f927924ab8ec1e1ff77d1794 (commit)
via 85cb5c5974c80333dd5c380a218cccc99a9d1947 (commit)
via b44392cdab8494212e2acdcf40de33e08e4b3698 (commit)
via 66d82fe96595d4331b4ec61c07f350ba0d0dd93b (commit)
via fe88e2b2d304fdc831b11c19aa85889318f15900 (commit)
via e8149ae19a25ec711945b7be484a154ebd5d592b (commit)
via 9d43ec08b73fddba0a020c177c6ddba710719bc3 (commit)
via 369761f549bbac00a287339e45f1e777841b940c (commit)
via d4536cbb5a10bebf7310c5e1d06f7d6627311617 (commit)
via 35fc22c9f60021360335f52d412eed636df35657 (commit)
via 97ed7e897f72492b911a571728c751d741d8c32c (commit)
via ca774237066b3ed998a5beb6d91d960758adf09a (commit)
via b82b2af6cbf716aae330c2a376cf21dc7a1805f4 (commit)
via bf4803b911a837b4bfd568aef94ffd17cdf5ca25 (commit)
via 06dbb588f199cad98d7feabfc63d14cffa58ebfc (commit)
via 6e3a3487b30699507e12554b15e9f052fe04c0c5 (commit)
via 4e9a7ed9bcb7b2ce8b6d2983e814f4b9d5fd87b2 (commit)
via 216d579298b7ac073c9cb9ed3757c602e3c251e8 (commit)
via e444604425665d42676728303c6e78e872682253 (commit)
via b7155e7e969e97806ea9416abb7fe20a54c16e6c (commit)
via 8ceafbdf99824fc917ecd573f7ba529c0a898c32 (commit)
via 3bddd9a844a7d89972b7f71b0015bda896633367 (commit)
via d1bfd53b673217da422975b3dc14c38f89094765 (commit)
via 4f8206ab0d632e993add8f29b90a515e213eda24 (commit)
via 03b79269e8d7d578587446c202c387413c47ffc8 (commit)
via 11c3c832347d09cd943970a1ddb72e6c2ac17fd9 (commit)
via 2dd40646cbf85f43a30cab21fab21759093f3d12 (commit)
via 2c72dc23bf3265aaab349471781a384b24873024 (commit)
via 2b148e41bc504de9df06b9cde7777f7113ea5978 (commit)
via abe73e885b980aace1fd0df492fa321bdd35f01f (commit)
via 53d45f54e33d23a5b4df42dc977a3a6ab597f5c5 (commit)
via 338b54ef4631f0d35601f174eabfa10f1541f46d (commit)
via 698176eccd5d55759fe9448b2c249717c932ac31 (commit)
via 41cbf5a91bdfa0b311aade6b05d2f51f59cce978 (commit)
via d845ae918fe8dce6806c3f927a7c101fc0e2173d (commit)
via 7bc93774a449b3f19748a37186db3efcd3d6c537 (commit)
via d5a58bbe641d32257035a6087f18655e7b66d8fd (commit)
via c64c4730852f74fff8ea75730e0b40cd3b23a85e (commit)
via c69a1675dd0434db0b99682d14fa7905fcd3af8f (commit)
via 4a605525cda67bea8c43ca8b3eae6e6749797450 (commit)
via b2d2acebebc66495b98eef634ce633eb70cc2411 (commit)
via acb299784ddbf280aac6ee5a78977c9acbf1fd32 (commit)
via 2418922a1389bbf265b02328f7c4f594257c4026 (commit)
via 44a44c0b568dc997e7522292212e0ef02b522f3d (commit)
via 250ce2abb3d6b48fce778b5e0c651d57582aff7c (commit)
via 99be45a44f97942f9327b16aff368f1650994e0e (commit)
via 7592596f7a9f8dce2e5e8d9311cc40c5199c66e3 (commit)
via c24c42a5e29444313efee6528f172ad66452050d (commit)
via 000164d51a974acf3846a6b0a7795f484e915161 (commit)
via f0ff0a2f69bcfae3e2a30a3bdeae37b475ae9106 (commit)
via bc03b37015ab6ea23cbec70dbd299c74fb001aba (commit)
via e56e0f7d1ad206f1ebc26e285d82a8e7ff6390e1 (commit)
via 40cd22fc64c7755efe60cd42cb12851cf3de55a4 (commit)
via ed8d686171f140fd12164d2d34f65b4ab3c97645 (commit)
via 1e32824c93dac7e406d1b35449b42700bf854679 (commit)
via c5d5522f83888a8b442aa7ff17738f3f688749fe (commit)
via 688867daa34ade5075443c77535f80e1d2d76743 (commit)
via d36ded7d95a695f0412f6ccdb59bf55fc600e9d3 (commit)
via b8e90124c19177e0b6b33bd624e244860e2424b3 (commit)
via 5cf1b7ab58c42675c1396fbbd5b1aaf037eb8d19 (commit)
via 17d9827aa40e363650d1698fddba9204f27b5171 (commit)
via 27f447c8b054b17d96abfba431568c1ffe017f0a (commit)
via 219818389cc848dc2d67aff732b9790968851b51 (commit)
via e602f86dae29c62619b0ea8bf2ca69e1ce1b8295 (commit)
via 57f7044d690d38cff90487b5883883a674d2589f (commit)
via 383b6b2891226228ddf3cfd4c3dd8b17ea186b8a (commit)
via 8cc8f4c008f640b7f13f8f1160261275ec14475b (commit)
via 70bba1b3f811261fcef30694568245e83cd64bc5 (commit)
via 06a24c688282b61dd2ce5b6c00608bee34ae3563 (commit)
via b902e70583a9dfb1ee410e297e2da4c8b944ba8d (commit)
via 09349cf206ee9e68618713b97e621b7ef2a6c0a9 (commit)
via ff1bd2a00278bc753a7d035fd5020ff936df1882 (commit)
via c89f3a2f43fd7fe70bcb199fad0ccf94364b1ebe (commit)
via 4c86025464db4603ec07490169aaf4b77868057b (commit)
via 7a061c2e82d62e2b275cb5a8d7460dce7d36f050 (commit)
via a6cbb14cc9c986d109983087313225829f1c91fe (commit)
via 7cc32b7915532354ed7e2fd15f7ca5a9b9b64610 (commit)
via dd340b32df88083fdc17f682094b451f7dcdf6d6 (commit)
via 30c277567f64d09c11cadcb173eef066efdaea07 (commit)
via ec2793914d1090db8c8d94a2f9b92ed97b1a6cba (commit)
via a59c7f28a458842b4edce2d6639639b17a85eb9f (commit)
via 2a5c5383e3df0e625367bf85b740f62bf777b211 (commit)
via f16de89251e4607eb413df666a64022c50478a4c (commit)
via 4e93ba217318854742144bf0b8e30f4c3614db92 (commit)
via 38d80ef7186ac2b18ed234a825894f5f78fc90b1 (commit)
via 88bee2515653d3b5481608bc92a1956c7ea7cf48 (commit)
via e9286ce511be095f2b16b1b7bc503b1e4377593d (commit)
via 723a6d1f333f1d513d5e4fe26a8ee7611767c9fc (commit)
via 88fe1bafce118f40d256097c2bfbdf9e53553784 (commit)
via 51c4b53945599a72d550d7380c7107e11b467d5c (commit)
via 84d7ae48d44e055cb16e3900cf2c4b2262f6a6da (commit)
via 61aaae27e12db2a00cfde674931e5080e733e6b3 (commit)
via 59e2ceaf7b75c38391c518436a70ac3d41b8c8be (commit)
via 4e3c6c5e5b19be3a0f970a06e3e135d1b2fae668 (commit)
via 03e9f45f8a6584a373f1bd15f01f56d9296c842a (commit)
via cb4d8443645a5c3e973b4e2477198686d8d8c507 (commit)
via f847a5e079ceae0346b84fb320ed06ce9b443a63 (commit)
via 05512e090c6c3cb852cebdb85ae7c12e8001603b (commit)
via c35f6b15bb6b703154e05399266dd2051ef9cfa9 (commit)
via 3f2864bf1271ca525858cf3e1fa641e3496eec59 (commit)
via f8720ba467d8e107c512160a5502caf9be58a425 (commit)
via 38af8a4225e8c82564758e8a5629da438220bc87 (commit)
via c5e0db2b7d8fbdb13548e01310f623f131ea0e9c (commit)
via 26c7bfe851f00422beb442a77d25cc0887557b79 (commit)
via f5239632a06383f2b4f6825cb6a006ceb8bea417 (commit)
via 680f05c35753bf1f70392d25b1e6310cf46476ce (commit)
via b12351c21ee92a13536aa89331cc73bd166dbe5f (commit)
via 2e1dceedf6a4f661a8d7e57757b28f9f6cb1a9b3 (commit)
via df69ad0d0231218610f68ecb2b1953ae7f28fa68 (commit)
via 5b713ea8e5fd35fdb1ab7ff953e010ef9b60f98c (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 (4cc981dd8348e18f56c6eabfd2ec62ca888264fb)
\
N -- N -- N (1006a9620414db12103f799466dde10b86fcbee6)
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 1006a9620414db12103f799466dde10b86fcbee6
Author: Naoki Kambe <kambe at jprs.co.jp>
Date: Wed Sep 28 13:53:21 2011 +0900
[1175] remove the retrying part because there is no clear necessity
commit b55e98a16cf7fd9db8af871b770b48f9c9146e05
Author: Naoki Kambe <kambe at jprs.co.jp>
Date: Wed Sep 28 13:45:49 2011 +0900
[1175] comment the reason why socket.has_ipv6 is not used
commit 8fd664266678c3f10df02e229a55b428d7f0481a
Author: Naoki Kambe <kambe at jprs.co.jp>
Date: Mon Sep 26 22:13:09 2011 +0900
[1175] correct the comment according to the purpose of the retry
commit 5c334f5ca17c52d988db1e4d6f47d2b76a84be4a
Author: Naoki Kambe <kambe at jprs.co.jp>
Date: Mon Sep 26 22:12:48 2011 +0900
[1175] correct the names and comments of the functions according to their
purposes
commit 010e6976f7961150586356ba9bb9e56b58ebf25f
Author: Naoki Kambe <kambe at jprs.co.jp>
Date: Mon Sep 26 18:35:22 2011 +0900
[1175] remove the two conditions because they do the same tests for IPv4
whether IPv6 is enabled or not
commit 86105ab0a1029a76ecf5a948d86efcfc87123cab
Author: Naoki Kambe <kambe at jprs.co.jp>
Date: Mon Sep 26 18:04:13 2011 +0900
[1175] modify b10-stats_test.py, b10-stats-httpd_test.py and test_utils.py
- exclude the setup of handler in SIGALRM and add it as an external
function in the SignalHandler class in test_utils.py
- define the function in that class to reset the handler in
test_utils.py, and add it in tearDown() in each testcase
commit b9bb528ab2141a8325877a1489dbb5928d58d404
Author: Naoki Kambe <kambe at jprs.co.jp>
Date: Mon Sep 26 15:46:30 2011 +0900
[1175] fix double hash signs
commit 30659570e5ccbc8b0ab29e5d2139f4b6c54e55fb
Author: Naoki Kambe <kambe at jprs.co.jp>
Date: Mon Sep 26 22:24:35 2011 +0900
[1175] correct the comment of the function according to its purpose
commit bc376ed5f17acd7b203ba36b2634363a3c9a12a1
Author: Naoki Kambe <kambe at jprs.co.jp>
Date: Tue Sep 13 18:22:23 2011 +0900
[1175] correct the logging id
commit 4c1c53b06a3b1d3b3b40698fd1cdc1aa3e356394
Author: Naoki Kambe <kambe at jprs.co.jp>
Date: Tue Sep 13 18:21:55 2011 +0900
[1175] add the package name for SessionError
commit 0a0e98ffd310e33a1aa1bf353b09b2502f64d9be
Author: Naoki Kambe <kambe at jprs.co.jp>
Date: Tue Sep 13 17:53:22 2011 +0900
[1175] modify test_utils.py
- explicitly shut down the socket of the msgq before shutting down
the msgq
- do nothing in the shutdown method of MockMsgq for avoiding shutting
down the msgq twice
- replace the stop method in the shutdown method of the MyStatsHttpd
with the shutdown command of stats_httpd
commit a48b3723667b0b87dbc70d4c7ee0b148331fd325
Author: Naoki Kambe <kambe at jprs.co.jp>
Date: Thu Sep 8 10:52:19 2011 +0900
[1175] modify test_utils.py
- move up an assignment of the BIND10_MSGQ_SOCKET_FILE environment
variable (BaseModules uses a constant file name during each
testcase)
- BaseModules checks whether msgq is ready after it started the msgq
object. A SessionTimeout is raised here if not.
commit b2b0cc8260ae79e5406f0f206e805c85d88f0429
Author: Naoki Kambe <kambe at jprs.co.jp>
Date: Tue Sep 6 20:27:44 2011 +0900
[1175] fix a typo
commit 7ebb37173dfec48ea13d513c6020a73183a6764d
Author: Naoki Kambe <kambe at jprs.co.jp>
Date: Tue Sep 6 20:17:57 2011 +0900
[1175] modify b10-stats-httpd_test.py
- The function get_availaddr uses socket.getaddrinfo for getting the
address family.
- The function is_ipv6_enabled uses 3 random ports for checking
whether IPv6 is enabled.
commit 885b69af7cabbfdff8e22255b9deb1705201da57
Author: Naoki Kambe <kambe at jprs.co.jp>
Date: Tue Sep 6 11:42:42 2011 +0900
[1175] send the command 'status' to the stats when it started, and
then send the command 'shutdown', and also check each value returned
by each invoked command
commit d6202285d5481b424eae9169aae0fa39cc34b271
Author: Naoki Kambe <kambe at jprs.co.jp>
Date: Mon Sep 5 17:14:11 2011 +0900
[1175] modify stats_httpd.py.in, b10-stats-httpd_test.py and
b10-stats_test.py
- The stats httpd doesn't need to return an argument when it's
shutting down.
- The testcase sends the 'status' command or the 'shutdown' command
to the stats or the stats httpd when they started, and then their
returned values are checked.
commit 5a90a358b959edabf0c471bd84bac3255ca13596
Author: Naoki Kambe <kambe at jprs.co.jp>
Date: Mon Sep 5 05:01:18 2011 +0000
[1175] modify test_utils.py
- add 3-time retry to creating the server object when it fails in the
ThreadingServerManager class
- suppress outputs by Msgq, and add dummy sys module and the output
methods
- pass Exceptions raised while it's running with a thread
commit 8e38c3cabdfd0d154678aa71712a52e08c516a2a
Author: Naoki Kambe <kambe at jprs.co.jp>
Date: Mon Sep 5 04:53:24 2011 +0000
[1175] remove a logging name from unittest
commit b1168693dcdf910866fea348599d569b5ee33494
Author: Naoki Kambe <kambe at jprs.co.jp>
Date: Mon Sep 5 04:53:11 2011 +0000
[1175] modify b10-stats-httpd_test.py
- remove a logging name from unittest
- do stats_httpd.stop() in teadDown() instead of each test case
- send 'shutdown' command to kill stats_httpd when testing address
already in use
commit 56552bfe686c314a9a1a812e84b92aab6fdfb651
Author: Naoki Kambe <kambe at jprs.co.jp>
Date: Mon Sep 5 04:46:08 2011 +0000
[1175] remove -v option from pycoverage
commit 2b78536997d1d17168d622f6a777cfdcd24882eb
Author: Naoki Kambe <kambe at jprs.co.jp>
Date: Thu Sep 1 11:34:28 2011 +0000
[1175] add -v option in pycoverage for debugging the failure in
buildbot. (unittest.main() with verbosity option is not supported
in Python3.1.)
commit c72634c3bc4502890a57b7babbf534e9fe01eeaa
Author: Naoki Kambe <kambe at jprs.co.jp>
Date: Thu Sep 1 04:19:53 2011 +0000
[1175] modify b10-stats-httpd_test.py, b10-stats_test.py and
test_utils.py
- more strictly close the io object whether it's successfully opened
or not
- add verbosity=2 in unittest.main for debugging the failure in the
buildbot
- don't redict sys.stderr in MockMsgq
- rename the function name to create_specfile
- switch the verbose in Msgq into True
commit c6370d65b8e2aad10be895cdba5677f6245f3144
Author: Naoki Kambe <kambe at jprs.co.jp>
Date: Fri Aug 26 12:01:47 2011 +0900
[1175] fix wrong list-type handling in the function
get_spec_defaults and add more tests into test_get_spec_defaults
commit adcc32e8f559f7525d709d801695bec1845065e6
Author: Naoki Kambe <kambe at jprs.co.jp>
Date: Wed Aug 24 17:28:29 2011 +0900
[1175] fix typo and correct changes from trac519
commit 925e80e912191ce4bccf52d90ccaf4e665eb57e8
Author: Naoki Kambe <kambe at jprs.co.jp>
Date: Wed Aug 24 16:51:11 2011 +0900
[1175] deadlock will be killed afer 20 secs
commit e6f9a14f57eb5f2dd1e2e622fa139ac7a6488d2d
Author: Naoki Kambe <kambe at jprs.co.jp>
Date: Wed Aug 24 15:55:21 2011 +0900
[1175] fix conflicts with trac519
commit 0d943ba9d25211863c437ea1dd0d69d2bdde0067
Author: Naoki Kambe <kambe at jprs.co.jp>
Date: Tue Aug 23 16:42:18 2011 +0900
[1175] modify stats_httpd.py.in and b10-stats-httpd_test.py
- A hostname (canonical name of host) is not acceptable in listen_on
configuration.
- A default port number(starting number for search) is added in args
of the function get_availaddr.
commit 34af1d005f42009c31186c2dd6001a29c9773606
Author: Naoki Kambe <kambe at jprs.co.jp>
Date: Tue Aug 23 11:22:03 2011 +0900
[1175] set msgq verbose off
commit 47734cf9b10886fc8f49c3fe6e10ad3f24256d77
Author: Naoki Kambe <kambe at jprs.co.jp>
Date: Mon Aug 22 18:20:39 2011 +0900
[1175] add #1175
commit 093ef97943821e0423f060ae485b8bb798b351e6
Author: Naoki Kambe <kambe at jprs.co.jp>
Date: Mon Aug 22 18:18:47 2011 +0900
[1175] modify test_utils.py
- don't use time.sleep for waiting threads are starting or finishing
- correct shutting down of mock modules
- use _started (threading.Event) where command_handler is invoked
- add implementation to changing contents of specfile of MyStatsHttpd
- set "BIND10_MSGQ_SOCKET_FILE" only when it's not set yet
commit 15623cb6fc32e4d303668c480c8eec5871dd2194
Author: Naoki Kambe <kambe at jprs.co.jp>
Date: Mon Aug 22 18:10:25 2011 +0900
[1175] modify b10-stats-httpd_test.py and b10-stats_test.py
- add function get_availaddr to get available address and port on the
platform
- add function is_ipv6enabled to check ipv6 enabled on the platform
- add miscellaneous changes to refactor unittest
commit 45cab58eefc4c02a65803b08f1ded1e63f4b2c68
Author: Naoki Kambe <kambe at jprs.co.jp>
Date: Mon Aug 22 18:05:57 2011 +0900
[1175] modify stats_httpd.py.in
- don't use DEFAULT_CONFIG
- move up mccs.start and open_httpd to __init__(). It takes time to
do these functions, and an extra sleep is needed in unittests.
- set running to False in http stopping
- use validate_config in module_spec class
- don't close/open http before it's opened
commit f9866a02cc7e3659d1b370ea6c9f5adc90560ba4
Author: Naoki Kambe <kambe at jprs.co.jp>
Date: Fri Aug 5 16:24:03 2011 +0900
[trac930] modify b10-stats-httpd_test.py and b10-stats_test.py
- revise header comments in each test script
- replace some hard-coded time strings with the constants defined in
the setUp function
- merged several checks about B10_FROM_SOURCE into the TestOSEnv
class
commit 16563433095c036dba4e2814838083a7ef89f752
Author: Naoki Kambe <kambe at jprs.co.jp>
Date: Fri Aug 5 14:48:27 2011 +0900
[trac930] modify b10-stats-httpd_test.py, b10-stats_test.py and
test_utils.py
- change address for test to 127.0.0.1 due to platform 127.0.0.2
can't be assigned
- remove unnecessary thread.Event.wait()
- add thread.Event.clear() after thread.Event.wait()
commit 3cb838d07ed1bc285eeb255c36b32c8a76e35ffd
Author: Naoki Kambe <kambe at jprs.co.jp>
Date: Wed Aug 3 11:41:05 2011 +0900
[trac930] refactor unittests
- remove time.sleep from various unittests and add in the "run"
method in ThreadingServerManager
- adjust the sleep time (TIMEOUT_SEC)
- join some small unittests
(test_start_with_err, test_command_status, test_command_shutdown)
commit 09d2b481e400a0fe519bd1227b0d7d29458c378f
Author: Naoki Kambe <kambe at jprs.co.jp>
Date: Tue Aug 2 22:00:11 2011 +0900
[trac930] add comments about abstracts of the test scripts in their
headers
commit 08a3aa577ce47de0594264c267a38bceaf302061
Author: Naoki Kambe <kambe at jprs.co.jp>
Date: Tue Aug 2 21:44:07 2011 +0900
[trac930] modify stats.py
- add more documentations into update_modules, get_statistics_data
and update_statistics_data methods
- modify two methods: "update_modules" and "get_statistics_data"
methods raise StatsError instead of just returning None, when
communication between stats module and config manager is failed or
when it can't find specified statistics data.
- also modify the unittest depending on the changes of these
behaviors.
commit f00403387266c310a2ea3618a1d4e336e75faae1
Author: Naoki Kambe <kambe at jprs.co.jp>
Date: Tue Aug 2 20:17:28 2011 +0900
[trac930] modify b10-stats_test.py
- set the constant variables in the setUp method in the TestUtilties
class, and compare values returned from the functions with these
constants in testing methods.
- remove the tearDown method which has no test case in the
TestCallback class
commit 546a7e4e387c85cfcf23dc476db3fb2244b84857
Author: Naoki Kambe <kambe at jprs.co.jp>
Date: Tue Aug 2 19:57:58 2011 +0900
[trac930] remove tailing whitespaces.
commit b2bac38d34242c760b0d396f943ffeda985fb640
Author: Naoki Kambe <kambe at jprs.co.jp>
Date: Mon Aug 1 18:38:35 2011 +0900
[trac930] raise StatsError including errors in the stats spec file
commit 7fbe2a7eadcae6a8c4fbdb54de0786568de69122
Author: Naoki Kambe <kambe at jprs.co.jp>
Date: Mon Aug 1 18:21:23 2011 +0900
[trac930] rename the function name
- rename the name of 'parse_spec' to 'get_spec_defaults' in the
result of consideration of what it is doing
- modify the description of the function as docstring
- fix unitttests for the stats module depending on the function name
commit 5aa4e96ef4f1c0a1f927924ab8ec1e1ff77d1794
Author: Naoki Kambe <kambe at jprs.co.jp>
Date: Fri Jul 29 22:11:38 2011 +0900
[trac930] remove a unnecessary x bit from stats_httpd.py.in
commit 85cb5c5974c80333dd5c380a218cccc99a9d1947
Author: Naoki Kambe <kambe at jprs.co.jp>
Date: Thu Jul 28 22:07:15 2011 +0900
[trac930] modify logging add loggings and new messages for logging
remove unused messages from the message file add test logging names
into unittest scripts
commit b44392cdab8494212e2acdcf40de33e08e4b3698
Author: Naoki Kambe <kambe at jprs.co.jp>
Date: Wed Jul 27 20:45:18 2011 +0900
[trac930] modify the update_modues function There is no part of
statistics category in the spec file of a module which has no
statistics data.
commit 66d82fe96595d4331b4ec61c07f350ba0d0dd93b
Author: Naoki Kambe <kambe at jprs.co.jp>
Date: Wed Jul 27 16:49:21 2011 +0900
[trac930] modify stats.py and b10-stats_test.py
- correct error messages in bindctl it prints together with
arguments.
- modify the command_show function it reports statistics data of the
module even if name is not specified.
- add/modify unittests depending on the changes of error messages
commit fe88e2b2d304fdc831b11c19aa85889318f15900
Author: Naoki Kambe <kambe at jprs.co.jp>
Date: Wed Jul 27 16:42:54 2011 +0900
[trac930] remove unnecessary a white space
commit e8149ae19a25ec711945b7be484a154ebd5d592b
Author: Naoki Kambe <kambe at jprs.co.jp>
Date: Wed Jul 27 10:18:07 2011 +0900
[trac930] add a test pattern which the set command with a
non-existent item name is sent
commit 9d43ec08b73fddba0a020c177c6ddba710719bc3
Author: Naoki Kambe <kambe at jprs.co.jp>
Date: Wed Jul 27 10:14:57 2011 +0900
[trac930] modify parse_spec function returns empty dict if list-type
is not specified in the argument
commit 369761f549bbac00a287339e45f1e777841b940c
Author: Naoki Kambe <kambe at jprs.co.jp>
Date: Fri Jul 22 21:40:07 2011 +0900
[trac930] fix conflicts with trac1021
commit d4536cbb5a10bebf7310c5e1d06f7d6627311617
Author: Naoki Kambe <kambe at jprs.co.jp>
Date: Fri Jul 22 18:50:41 2011 +0900
[trac930] add changes because query counter names described in the
specfile are changed.
commit 35fc22c9f60021360335f52d412eed636df35657
Author: Naoki Kambe <kambe at jprs.co.jp>
Date: Fri Jul 22 18:45:19 2011 +0900
[trac930] add the logging when the validation of statistics data
fails
commit 97ed7e897f72492b911a571728c751d741d8c32c
Author: Naoki Kambe <kambe at jprs.co.jp>
Date: Fri Jul 22 18:43:26 2011 +0900
[trac930] Add unittests to test sumitStatistics with the validation
of statistics data and add mock ModuleSpec class
commit ca774237066b3ed998a5beb6d91d960758adf09a
Author: Naoki Kambe <kambe at jprs.co.jp>
Date: Fri Jul 22 18:41:34 2011 +0900
[trac930] Add prototypes of validator_typea and
registerStatisticsValidator
- validator_type -- a type of statistics validation function
- registerStatisticsValidator -- the function to register the
validation function
commit b82b2af6cbf716aae330c2a376cf21dc7a1805f4
Author: Naoki Kambe <kambe at jprs.co.jp>
Date: Fri Jul 22 18:37:22 2011 +0900
[trac930] modify statistics.cc
- Add implementation to validate statistics data
-- When validation is success, it sends data to statistics
module. But when it fails, it doesn't send and logs the message.
- Add the function to register the validation function into the class
commit bf4803b911a837b4bfd568aef94ffd17cdf5ca25
Author: Naoki Kambe <kambe at jprs.co.jp>
Date: Fri Jul 22 18:32:22 2011 +0900
[trac930] add the helper functions which are used around the
registration of the function to validate the statistics data.
commit 06dbb588f199cad98d7feabfc63d14cffa58ebfc
Author: Naoki Kambe <kambe at jprs.co.jp>
Date: Fri Jul 22 18:28:40 2011 +0900
[trac930] add new messages into the message file of Auth and Boss
when validation of statistics data to send to statistics module is
failed.
commit 6e3a3487b30699507e12554b15e9f052fe04c0c5
Author: Naoki Kambe <kambe at jprs.co.jp>
Date: Wed Jul 20 10:00:29 2011 +0900
[trac930] add statistics validation for bob
commit 4e9a7ed9bcb7b2ce8b6d2983e814f4b9d5fd87b2
Author: Naoki Kambe <kambe at jprs.co.jp>
Date: Wed Jul 13 20:25:54 2011 +0900
[trac930] modify b10-stats-httpd_test.py
- increase seconds in sleep time which is before HTTP client connects
to the server
- delete 'test_log_message' because of the deletion of original
function
commit 216d579298b7ac073c9cb9ed3757c602e3c251e8
Author: Naoki Kambe <kambe at jprs.co.jp>
Date: Fri Jul 8 21:22:34 2011 +0900
[trac930] remove unneeded empty TODO comments
commit e444604425665d42676728303c6e78e872682253
Author: Naoki Kambe <kambe at jprs.co.jp>
Date: Fri Jul 8 20:08:22 2011 +0900
[trac930] refurbish the unittests for new stats module, new stats
httpd module and new mockups and utilities in test_utils.py
commit b7155e7e969e97806ea9416abb7fe20a54c16e6c
Author: Naoki Kambe <kambe at jprs.co.jp>
Date: Fri Jul 8 19:56:24 2011 +0900
[trac930] modify Stats
- remove unneeded subject and listener classes
- add StatsError for handling errors in Stats
- add some new methods (update_modules, update_statistics_data and
get_statistics_data)
- modify implementations of existent commands(show and set) according changes
stats.spec
- remove reset and remove command because stats module couldn't manage other
modules' statistics data schema
- add implementation of strict validation of each statistics data
(If the validation is failed, it puts out the error.)
- stats module shows its PID when status command invoked
- add new command showschema invokable via bindctl
- set command requires arguments of owner module name and statistics item name
- show and showschema commands accepts arguments of owner module name and
statistics item name
- exits at exit code 1 if got runtime errors
- has boot time in _BASETIME
commit 8ceafbdf99824fc917ecd573f7ba529c0a898c32
Author: Naoki Kambe <kambe at jprs.co.jp>
Date: Fri Jul 8 19:40:15 2011 +0900
[trac930] modify stats_httpd.py.in
- remove "stats-schema.spec" setting and getting statistics data
schema via this spec file
- add "version" item in DEFAULT_CONFIG
- get the address family by socket.getaddrinfo function with specified
server_address in advance, and create HttpServer object once, in stead of
creating double HttpServer objects for IPv6 and IPv4 in the prior code
(It is aimed for avoiding to fail to close the once opened sockets.)
- open HTTP port in start method
- avoid calling config_handler recursively in the except statement
- create XML, XSD, XSL documents after getting statistics data and schema from
remote stats module via CC session
- definitely close once opened template file object
commit 3bddd9a844a7d89972b7f71b0015bda896633367
Author: Naoki Kambe <kambe at jprs.co.jp>
Date: Fri Jul 8 16:33:59 2011 +0900
[trac930] update spec file of stats module
- update description of status command, shutdown command and show
command
- change argument of show command (Owner module name of statistics
data can be specified)
- change argument of set command (Owner module name of statistics
data is always required)
- add showschema command which shows statistics data schema of each
module specified)
- disabled reset command and remove command
commit d1bfd53b673217da422975b3dc14c38f89094765
Author: Naoki Kambe <kambe at jprs.co.jp>
Date: Fri Jul 8 16:21:49 2011 +0900
[trac930] update argument name and argument format of set command in
auth module and boss module and also update related unittests of
their modules
commit 4f8206ab0d632e993add8f29b90a515e213eda24
Author: Naoki Kambe <kambe at jprs.co.jp>
Date: Fri Jul 8 16:18:38 2011 +0900
[trac930] remove description about removing statistics data by stats
module update example format in bindctl when show command of stats
module is invoked
commit 03b79269e8d7d578587446c202c387413c47ffc8
Author: Naoki Kambe <kambe at jprs.co.jp>
Date: Fri Jul 8 16:13:17 2011 +0900
[trac930] add a column "Owner" in the table tag
commit 11c3c832347d09cd943970a1ddb72e6c2ac17fd9
Author: Naoki Kambe <kambe at jprs.co.jp>
Date: Fri Jul 8 16:12:09 2011 +0900
[trac930] remove descriptions about "stats-schema.spec" and add
description about new features because stats module can be
requested to show statistics data schema.
commit 2dd40646cbf85f43a30cab21fab21759093f3d12
Author: Naoki Kambe <kambe at jprs.co.jp>
Date: Fri Jul 8 16:00:30 2011 +0900
[trac930] add utilities and mock-up modules for unittests of
statistics modules and change some environ variables (PYTHONPATH,
CONFIG_TESTDATA_PATH) in Makefile
test_utilies.py internally calls msgq, cfgmgr and some mock modules
with threads for as real situation as possible.
commit 2c72dc23bf3265aaab349471781a384b24873024
Author: Naoki Kambe <kambe at jprs.co.jp>
Date: Fri Jul 8 15:57:41 2011 +0900
[trac930] remove unneeded mockups, fake modules and dummy data
commit 2b148e41bc504de9df06b9cde7777f7113ea5978
Author: Naoki Kambe <kambe at jprs.co.jp>
Date: Fri Jul 8 15:55:55 2011 +0900
[trac930] remove unneeded specfile "stats-schema.spec"
-----------------------------------------------------------------------
Summary of changes:
ChangeLog | 25 +
configure.ac | 4 +-
src/bin/auth/query.cc | 7 +
src/bin/auth/tests/query_unittest.cc | 4 +
src/bin/host/b10-host.1 | 4 -
src/bin/host/b10-host.xml | 5 -
src/bin/xfrin/tests/xfrin_test.py | 12 +-
src/bin/xfrin/xfrin.py.in | 11 +
src/bin/xfrin/xfrin.spec | 5 +
src/bin/xfrout/tests/Makefile.am | 3 +
src/bin/xfrout/tests/xfrout_test.py.in | 197 +++++++--
src/bin/xfrout/xfrout.py.in | 182 ++++++--
src/bin/xfrout/xfrout.spec.pre.in | 41 ++-
src/bin/xfrout/xfrout_messages.mes | 11 +
src/cppcheck-suppress.lst | 2 +-
src/lib/datasrc/Makefile.am | 1 +
src/lib/datasrc/database.cc | 469 ++++++++++++++------
src/lib/datasrc/database.h | 126 ++++--
src/lib/datasrc/datasrc_messages.mes | 5 +
src/lib/datasrc/memory_datasrc.cc | 6 +
src/lib/datasrc/memory_datasrc.h | 6 +
src/lib/datasrc/sqlite3_accessor.cc | 105 ++++--
src/lib/datasrc/sqlite3_accessor.h | 4 +
src/lib/datasrc/tests/database_unittest.cc | 289 ++++++++++++-
src/lib/datasrc/tests/memory_datasrc_unittest.cc | 8 +
src/lib/datasrc/tests/sqlite3_accessor_unittest.cc | 39 ++
src/lib/datasrc/zone.h | 73 +++-
src/lib/dns/gen-rdatacode.py.in | 17 +-
src/lib/dns/rdata/any_255/tsig_250.cc | 1 +
src/lib/dns/rdata/generic/afsdb_18.cc | 1 +
src/lib/dns/rdata/generic/minfo_14.cc | 1 +
src/lib/dns/rdata/generic/rp_17.cc | 1 +
src/lib/dns/rdata/template.cc | 1 +
src/lib/python/isc/__init__.py | 7 +-
src/lib/python/isc/datasrc/Makefile.am | 2 +-
src/lib/python/isc/datasrc/__init__.py | 7 +-
36 files changed, 1356 insertions(+), 326 deletions(-)
-----------------------------------------------------------------------
diff --git a/ChangeLog b/ChangeLog
index f0c1962..e629e3d 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -5,6 +5,31 @@ xxx. [func] naokikambe
schema by each module via both bindctl and HTTP/XML.
(Trac #928,#929,#930,#1175, git TBD)
+289. [func]* jinmei
+ b10-xfrout: ACLs for xfrout can now be configured per zone basis.
+ A per zone ACl is part of a more general zone configuration. A
+ quick example for configuring an ACL for zone "example.com" that
+ rejects any transfer request for that zone is as follows:
+ > config add Xfrout/zone_config
+ > config set Xfrout/zone_config[0]/origin "example.com"
+ > config add Xfrout/zone_config[0]/transfer_acl
+ > config set Xfrout/zone_config[0]/transfer_acl[0] {"action": "REJECT"}
+ The previous global ACL (query_acl) was renamed to transfer_acl,
+ which now works as the default ACL. Note: backward compatibility
+ is not provided, so an existing configuration using query_acl
+ needs to be updated by hand.
+ Note: the per zone configuration framework is a temporary
+ workaround. It will eventually be redesigned as a system wide
+ configuration.
+ (Trac #1165, git 698176eccd5d55759fe9448b2c249717c932ac31)
+
+288. [bug] stephen
+ Fixed problem whereby the order in which component files appeared in
+ rdataclass.cc was system dependent, leading to problems on some
+ systems where data types were used before the header file in which
+ they were declared was included.
+ (Trac #1202, git 4a605525cda67bea8c43ca8b3eae6e6749797450)
+
287. [bug]* jinmei
Python script files for log messages (xxx_messages.py) should have
been installed under the "isc" package. This fix itself should
diff --git a/configure.ac b/configure.ac
index 1bc075e..1cb0bb0 100644
--- a/configure.ac
+++ b/configure.ac
@@ -437,7 +437,7 @@ AC_ARG_WITH([botan],
AC_HELP_STRING([--with-botan=PATH],
[specify exact directory of Botan library]),
[botan_path="$withval"])
-if test "${botan_path}" == "no" ; then
+if test "${botan_path}" = "no" ; then
AC_MSG_ERROR([Need botan for libcryptolink])
fi
if test "${botan_path}" != "yes" ; then
@@ -510,7 +510,7 @@ AC_ARG_WITH([log4cplus],
AC_HELP_STRING([--with-log4cplus=PATH],
[specify exact directory of log4cplus library and headers]),
[log4cplus_path="$withval"])
-if test "${log4cplus_path}" == "no" ; then
+if test "${log4cplus_path}" = "no" ; then
AC_MSG_ERROR([Need log4cplus])
elif test "${log4cplus_path}" != "yes" ; then
LOG4CPLUS_INCLUDES="-I${log4cplus_path}/include"
diff --git a/src/bin/auth/query.cc b/src/bin/auth/query.cc
index 898fff7..ab6404e 100644
--- a/src/bin/auth/query.cc
+++ b/src/bin/auth/query.cc
@@ -253,6 +253,13 @@ Query::process() const {
// Just empty answer with SOA in authority section
putSOA(*result.zone_finder);
break;
+ default:
+ // These are new result codes (WILDCARD and WILDCARD_NXRRSET)
+ // They should not happen from the in-memory and the database
+ // backend isn't used yet.
+ // TODO: Implement before letting the database backends in
+ isc_throw(isc::NotImplemented, "Unknown result code");
+ break;
}
}
}
diff --git a/src/bin/auth/tests/query_unittest.cc b/src/bin/auth/tests/query_unittest.cc
index 4b8f013..b2d1094 100644
--- a/src/bin/auth/tests/query_unittest.cc
+++ b/src/bin/auth/tests/query_unittest.cc
@@ -141,6 +141,10 @@ public:
// Turn this on if you want it to return RRSIGs regardless of FIND_GLUE_OK
void setIncludeRRSIGAnyway(bool on) { include_rrsig_anyway_ = on; }
+ Name findPreviousName(const Name&) const {
+ isc_throw(isc::NotImplemented, "Mock doesn't support previous name");
+ }
+
private:
typedef map<RRType, ConstRRsetPtr> RRsetStore;
typedef map<Name, RRsetStore> Domains;
diff --git a/src/bin/host/b10-host.1 b/src/bin/host/b10-host.1
index ed0068b..050f6a3 100644
--- a/src/bin/host/b10-host.1
+++ b/src/bin/host/b10-host.1
@@ -103,10 +103,6 @@ It doesn\'t use
at this time\&. The default name server used is 127\&.0\&.0\&.1\&.
.PP
-\fBb10\-host\fR
-does not do reverse lookups by default yet (by detecting if name is a IPv4 or IPv6 address)\&.
-.PP
-
\fB\-p\fR
is not a standard feature\&.
.SH "HISTORY"
diff --git a/src/bin/host/b10-host.xml b/src/bin/host/b10-host.xml
index 7da07dd..a17ef67 100644
--- a/src/bin/host/b10-host.xml
+++ b/src/bin/host/b10-host.xml
@@ -176,11 +176,6 @@
</para>
<para>
- <command>b10-host</command> does not do reverse lookups by
- default yet (by detecting if name is a IPv4 or IPv6 address).
- </para>
-
- <para>
<option>-p</option> is not a standard feature.
</para>
</refsect1>
diff --git a/src/bin/xfrin/tests/xfrin_test.py b/src/bin/xfrin/tests/xfrin_test.py
index 92bf1b0..05cce98 100644
--- a/src/bin/xfrin/tests/xfrin_test.py
+++ b/src/bin/xfrin/tests/xfrin_test.py
@@ -955,13 +955,20 @@ class TestXfrin(unittest.TestCase):
self.assertEqual(zone_info.tsig_key.to_text(), TSIGKey(zone_config['tsig_key']).to_text())
else:
self.assertIsNone(zone_info.tsig_key)
+ if 'ixfr_disabled' in zone_config and\
+ zone_config.get('ixfr_disabled'):
+ self.assertTrue(zone_info.ixfr_disabled)
+ else:
+ # if not set, should default to False
+ self.assertFalse(zone_info.ixfr_disabled)
def test_command_handler_zones(self):
config1 = { 'transfers_in': 3,
'zones': [
{ 'name': 'test.example.',
'master_addr': '192.0.2.1',
- 'master_port': 53
+ 'master_port': 53,
+ 'ixfr_disabled': False
}
]}
self.assertEqual(self.xfr.config_handler(config1)['result'][0], 0)
@@ -972,7 +979,8 @@ class TestXfrin(unittest.TestCase):
{ 'name': 'test.example.',
'master_addr': '192.0.2.2',
'master_port': 53,
- 'tsig_key': "example.com:SFuWd/q99SzF8Yzd1QbB9g=="
+ 'tsig_key': "example.com:SFuWd/q99SzF8Yzd1QbB9g==",
+ 'ixfr_disabled': True
}
]}
self.assertEqual(self.xfr.config_handler(config2)['result'][0], 0)
diff --git a/src/bin/xfrin/xfrin.py.in b/src/bin/xfrin/xfrin.py.in
index 8845b42..a77a383 100755
--- a/src/bin/xfrin/xfrin.py.in
+++ b/src/bin/xfrin/xfrin.py.in
@@ -451,6 +451,7 @@ class ZoneInfo:
self.set_master_port(config_data.get('master_port'))
self.set_zone_class(config_data.get('class'))
self.set_tsig_key(config_data.get('tsig_key'))
+ self.set_ixfr_disabled(config_data.get('ixfr_disabled'))
def set_name(self, name_str):
"""Set the name for this zone given a name string.
@@ -525,6 +526,16 @@ class ZoneInfo:
errmsg = "bad TSIG key string: " + tsig_key_str
raise XfrinZoneInfoException(errmsg)
+ def set_ixfr_disabled(self, ixfr_disabled):
+ """Set ixfr_disabled. If set to False (the default), it will use
+ IXFR for incoming transfers. If set to True, it will use AXFR.
+ At this moment there is no automatic fallback"""
+ # don't care what type it is; if evaluates to true, set to True
+ if ixfr_disabled:
+ self.ixfr_disabled = True
+ else:
+ self.ixfr_disabled = False
+
def get_master_addr_info(self):
return (self.master_addr.family, socket.SOCK_STREAM,
(str(self.master_addr), self.master_port))
diff --git a/src/bin/xfrin/xfrin.spec b/src/bin/xfrin/xfrin.spec
index a3e62ce..bc93720 100644
--- a/src/bin/xfrin/xfrin.spec
+++ b/src/bin/xfrin/xfrin.spec
@@ -43,6 +43,11 @@
{ "item_name": "tsig_key",
"item_type": "string",
"item_optional": true
+ },
+ { "item_name": "ixfr_disabled",
+ "item_type": "boolean",
+ "item_optional": false,
+ "item_default": false
}
]
}
diff --git a/src/bin/xfrout/tests/Makefile.am b/src/bin/xfrout/tests/Makefile.am
index 2e22e64..ace8fc9 100644
--- a/src/bin/xfrout/tests/Makefile.am
+++ b/src/bin/xfrout/tests/Makefile.am
@@ -10,6 +10,8 @@ LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/cryp
endif
# test using command-line arguments, so use check-local target instead of TESTS
+# We set B10_FROM_BUILD below, so that the test can refer to the in-source
+# spec file.
check-local:
if ENABLE_PYTHON_COVERAGE
touch $(abs_top_srcdir)/.coverage
@@ -19,6 +21,7 @@ endif
for pytest in $(PYTESTS) ; do \
echo Running test: $$pytest ; \
chmod +x $(abs_builddir)/$$pytest ; \
+ B10_FROM_BUILD=$(abs_top_builddir) \
$(LIBRARY_PATH_PLACEHOLDER) \
PYTHONPATH=$(COMMON_PYTHON_PATH):$(abs_top_builddir)/src/bin/xfrout:$(abs_top_builddir)/src/lib/dns/python/.libs:$(abs_top_builddir)/src/lib/util/io/.libs \
$(PYCOVERAGE_RUN) $(abs_builddir)/$$pytest || exit ; \
diff --git a/src/bin/xfrout/tests/xfrout_test.py.in b/src/bin/xfrout/tests/xfrout_test.py.in
index 62c7708..85979a0 100644
--- a/src/bin/xfrout/tests/xfrout_test.py.in
+++ b/src/bin/xfrout/tests/xfrout_test.py.in
@@ -20,6 +20,7 @@ import unittest
import os
from isc.testutils.tsigctx_mock import MockTSIGContext
from isc.cc.session import *
+import isc.config
from pydnspp import *
from xfrout import *
import xfrout
@@ -101,20 +102,24 @@ class TestXfroutSession(unittest.TestCase):
def message_has_tsig(self, msg):
return msg.get_tsig_record() is not None
- def create_request_data_with_tsig(self):
+ def create_request_data(self, with_tsig=False):
msg = Message(Message.RENDER)
query_id = 0x1035
msg.set_qid(query_id)
msg.set_opcode(Opcode.QUERY())
msg.set_rcode(Rcode.NOERROR())
- query_question = Question(Name("example.com."), RRClass.IN(), RRType.AXFR())
+ query_question = Question(Name("example.com"), RRClass.IN(),
+ RRType.AXFR())
msg.add_question(query_question)
renderer = MessageRenderer()
- tsig_ctx = MockTSIGContext(TSIG_KEY)
- msg.to_wire(renderer, tsig_ctx)
- reply_data = renderer.get_data()
- return reply_data
+ if with_tsig:
+ tsig_ctx = MockTSIGContext(TSIG_KEY)
+ msg.to_wire(renderer, tsig_ctx)
+ else:
+ msg.to_wire(renderer)
+ request_data = renderer.get_data()
+ return request_data
def setUp(self):
self.sock = MySocket(socket.AF_INET,socket.SOCK_STREAM)
@@ -122,8 +127,9 @@ class TestXfroutSession(unittest.TestCase):
TSIGKeyRing(), ('127.0.0.1', 12345),
# When not testing ACLs, simply accept
isc.acl.dns.REQUEST_LOADER.load(
- [{"action": "ACCEPT"}]))
- self.mdata = bytes(b'\xd6=\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x07example\x03com\x00\x00\xfc\x00\x01')
+ [{"action": "ACCEPT"}]),
+ {})
+ self.mdata = self.create_request_data(False)
self.soa_record = (4, 3, 'example.com.', 'com.example.', 3600, 'SOA', None, 'master.example.com. admin.example.com. 1234 3600 1800 2419200 7200')
def test_parse_query_message(self):
@@ -131,7 +137,7 @@ class TestXfroutSession(unittest.TestCase):
self.assertEqual(get_rcode.to_text(), "NOERROR")
# tsig signed query message
- request_data = self.create_request_data_with_tsig()
+ request_data = self.create_request_data(True)
# BADKEY
[rcode, msg] = self.xfrsess._parse_query_message(request_data)
self.assertEqual(rcode.to_text(), "NOTAUTH")
@@ -143,8 +149,9 @@ class TestXfroutSession(unittest.TestCase):
self.assertEqual(rcode.to_text(), "NOERROR")
self.assertTrue(self.xfrsess._tsig_ctx is not None)
+ def check_transfer_acl(self, acl_setter):
# ACL checks, put some ACL inside
- self.xfrsess._acl = isc.acl.dns.REQUEST_LOADER.load([
+ acl_setter(isc.acl.dns.REQUEST_LOADER.load([
{
"from": "127.0.0.1",
"action": "ACCEPT"
@@ -153,7 +160,7 @@ class TestXfroutSession(unittest.TestCase):
"from": "192.0.2.1",
"action": "DROP"
}
- ])
+ ]))
# Localhost (the default in this test) is accepted
rcode, msg = self.xfrsess._parse_query_message(self.mdata)
self.assertEqual(rcode.to_text(), "NOERROR")
@@ -165,6 +172,10 @@ class TestXfroutSession(unittest.TestCase):
self.xfrsess._remote = ('192.0.2.2', 12345)
rcode, msg = self.xfrsess._parse_query_message(self.mdata)
self.assertEqual(rcode.to_text(), "REFUSED")
+
+ # TSIG signed request
+ request_data = self.create_request_data(True)
+
# If the TSIG check fails, it should not check ACL
# (If it checked ACL as well, it would just drop the request)
self.xfrsess._remote = ('192.0.2.1', 12345)
@@ -174,36 +185,36 @@ class TestXfroutSession(unittest.TestCase):
self.assertTrue(self.xfrsess._tsig_ctx is not None)
# ACL using TSIG: successful case
- self.xfrsess._acl = isc.acl.dns.REQUEST_LOADER.load([
+ acl_setter(isc.acl.dns.REQUEST_LOADER.load([
{"key": "example.com", "action": "ACCEPT"}, {"action": "REJECT"}
- ])
+ ]))
self.assertEqual(TSIGKeyRing.SUCCESS,
self.xfrsess._tsig_key_ring.add(TSIG_KEY))
[rcode, msg] = self.xfrsess._parse_query_message(request_data)
self.assertEqual(rcode.to_text(), "NOERROR")
# ACL using TSIG: key name doesn't match; should be rejected
- self.xfrsess._acl = isc.acl.dns.REQUEST_LOADER.load([
+ acl_setter(isc.acl.dns.REQUEST_LOADER.load([
{"key": "example.org", "action": "ACCEPT"}, {"action": "REJECT"}
- ])
+ ]))
[rcode, msg] = self.xfrsess._parse_query_message(request_data)
self.assertEqual(rcode.to_text(), "REFUSED")
# ACL using TSIG: no TSIG; should be rejected
- self.xfrsess._acl = isc.acl.dns.REQUEST_LOADER.load([
+ acl_setter(isc.acl.dns.REQUEST_LOADER.load([
{"key": "example.org", "action": "ACCEPT"}, {"action": "REJECT"}
- ])
+ ]))
[rcode, msg] = self.xfrsess._parse_query_message(self.mdata)
self.assertEqual(rcode.to_text(), "REFUSED")
#
# ACL using IP + TSIG: both should match
#
- self.xfrsess._acl = isc.acl.dns.REQUEST_LOADER.load([
+ acl_setter(isc.acl.dns.REQUEST_LOADER.load([
{"ALL": [{"key": "example.com"}, {"from": "192.0.2.1"}],
"action": "ACCEPT"},
{"action": "REJECT"}
- ])
+ ]))
# both matches
self.xfrsess._remote = ('192.0.2.1', 12345)
[rcode, msg] = self.xfrsess._parse_query_message(request_data)
@@ -221,6 +232,63 @@ class TestXfroutSession(unittest.TestCase):
[rcode, msg] = self.xfrsess._parse_query_message(self.mdata)
self.assertEqual(rcode.to_text(), "REFUSED")
+ def test_transfer_acl(self):
+ # ACL checks only with the default ACL
+ def acl_setter(acl):
+ self.xfrsess._acl = acl
+ self.check_transfer_acl(acl_setter)
+
+ def test_transfer_zoneacl(self):
+ # ACL check with a per zone ACL + default ACL. The per zone ACL
+ # should match the queryied zone, so it should be used.
+ def acl_setter(acl):
+ zone_key = ('IN', 'example.com.')
+ self.xfrsess._zone_config[zone_key] = {}
+ self.xfrsess._zone_config[zone_key]['transfer_acl'] = acl
+ self.xfrsess._acl = isc.acl.dns.REQUEST_LOADER.load([
+ {"from": "127.0.0.1", "action": "DROP"}])
+ self.check_transfer_acl(acl_setter)
+
+ def test_transfer_zoneacl_nomatch(self):
+ # similar to the previous one, but the per zone doesn't match the
+ # query. The default should be used.
+ def acl_setter(acl):
+ zone_key = ('IN', 'example.org.')
+ self.xfrsess._zone_config[zone_key] = {}
+ self.xfrsess._zone_config[zone_key]['transfer_acl'] = \
+ isc.acl.dns.REQUEST_LOADER.load([
+ {"from": "127.0.0.1", "action": "DROP"}])
+ self.xfrsess._acl = acl
+ self.check_transfer_acl(acl_setter)
+
+ def test_get_transfer_acl(self):
+ # set the default ACL. If there's no specific zone ACL, this one
+ # should be used.
+ self.xfrsess._acl = isc.acl.dns.REQUEST_LOADER.load([
+ {"from": "127.0.0.1", "action": "ACCEPT"}])
+ acl = self.xfrsess._get_transfer_acl(Name('example.com'), RRClass.IN())
+ self.assertEqual(acl, self.xfrsess._acl)
+
+ # install a per zone config with transfer ACL for example.com. Then
+ # that ACL will be used for example.com; for others the default ACL
+ # will still be used.
+ com_acl = isc.acl.dns.REQUEST_LOADER.load([
+ {"from": "127.0.0.1", "action": "REJECT"}])
+ self.xfrsess._zone_config[('IN', 'example.com.')] = {}
+ self.xfrsess._zone_config[('IN', 'example.com.')]['transfer_acl'] = \
+ com_acl
+ self.assertEqual(com_acl,
+ self.xfrsess._get_transfer_acl(Name('example.com'),
+ RRClass.IN()))
+ self.assertEqual(self.xfrsess._acl,
+ self.xfrsess._get_transfer_acl(Name('example.org'),
+ RRClass.IN()))
+
+ # Name matching should be case insensitive.
+ self.assertEqual(com_acl,
+ self.xfrsess._get_transfer_acl(Name('EXAMPLE.COM'),
+ RRClass.IN()))
+
def test_get_query_zone_name(self):
msg = self.getmsg()
self.assertEqual(self.xfrsess._get_query_zone_name(msg), "example.com.")
@@ -572,9 +640,11 @@ class TestXfroutSession(unittest.TestCase):
# and it should not have sent anything else
self.assertEqual(0, len(self.sock.sendqueue))
-class MyCCSession():
+class MyCCSession(isc.config.ConfigData):
def __init__(self):
- pass
+ module_spec = isc.config.module_spec_from_file(
+ xfrout.SPECFILE_LOCATION)
+ ConfigData.__init__(self, module_spec)
def get_remote_config_value(self, module_name, identifier):
if module_name == "Auth" and identifier == "database_file":
@@ -586,9 +656,9 @@ class MyCCSession():
class MyUnixSockServer(UnixSockServer):
def __init__(self):
self._shutdown_event = threading.Event()
- self._max_transfers_out = 10
- self._cc = MyCCSession()
self._common_init()
+ self._cc = MyCCSession()
+ self.update_config_data(self._cc.get_full_config())
class TestUnixSockServer(unittest.TestCase):
def setUp(self):
@@ -636,17 +706,17 @@ class TestUnixSockServer(unittest.TestCase):
socket.AI_NUMERICHOST)[0][4])
self.assertEqual(isc.acl.acl.ACCEPT, self.unix._acl.execute(context))
- def check_loaded_ACL(self):
+ def check_loaded_ACL(self, acl):
context = isc.acl.dns.RequestContext(socket.getaddrinfo("127.0.0.1",
1234, 0, socket.SOCK_DGRAM,
socket.IPPROTO_UDP,
socket.AI_NUMERICHOST)[0][4])
- self.assertEqual(isc.acl.acl.ACCEPT, self.unix._acl.execute(context))
+ self.assertEqual(isc.acl.acl.ACCEPT, acl.execute(context))
context = isc.acl.dns.RequestContext(socket.getaddrinfo("192.0.2.1",
1234, 0, socket.SOCK_DGRAM,
socket.IPPROTO_UDP,
socket.AI_NUMERICHOST)[0][4])
- self.assertEqual(isc.acl.acl.REJECT, self.unix._acl.execute(context))
+ self.assertEqual(isc.acl.acl.REJECT, acl.execute(context))
def test_update_config_data(self):
self.check_default_ACL()
@@ -671,14 +741,79 @@ class TestUnixSockServer(unittest.TestCase):
self.assertEqual(self.unix.tsig_key_ring.size(), 0)
# Load the ACL
- self.unix.update_config_data({'query_acl': [{'from': '127.0.0.1',
+ self.unix.update_config_data({'transfer_acl': [{'from': '127.0.0.1',
'action': 'ACCEPT'}]})
- self.check_loaded_ACL()
+ self.check_loaded_ACL(self.unix._acl)
# Pass a wrong data there and check it does not replace the old one
- self.assertRaises(isc.acl.acl.LoaderError,
+ self.assertRaises(XfroutConfigError,
+ self.unix.update_config_data,
+ {'transfer_acl': ['Something bad']})
+ self.check_loaded_ACL(self.unix._acl)
+
+ def test_zone_config_data(self):
+ # By default, there's no specific zone config
+ self.assertEqual({}, self.unix._zone_config)
+
+ # Adding config for a specific zone. The config is empty unless
+ # explicitly specified.
+ self.unix.update_config_data({'zone_config':
+ [{'origin': 'example.com',
+ 'class': 'IN'}]})
+ self.assertEqual({}, self.unix._zone_config[('IN', 'example.com.')])
+
+ # zone class can be omitted
+ self.unix.update_config_data({'zone_config':
+ [{'origin': 'example.com'}]})
+ self.assertEqual({}, self.unix._zone_config[('IN', 'example.com.')])
+
+ # zone class, name are stored in the "normalized" form. class
+ # strings are upper cased, names are down cased.
+ self.unix.update_config_data({'zone_config':
+ [{'origin': 'EXAMPLE.com'}]})
+ self.assertEqual({}, self.unix._zone_config[('IN', 'example.com.')])
+
+ # invalid zone class, name will result in exceptions
+ self.assertRaises(EmptyLabel,
+ self.unix.update_config_data,
+ {'zone_config': [{'origin': 'bad..example'}]})
+ self.assertRaises(InvalidRRClass,
+ self.unix.update_config_data,
+ {'zone_config': [{'origin': 'example.com',
+ 'class': 'badclass'}]})
+
+ # Configuring a couple of more zones
+ self.unix.update_config_data({'zone_config':
+ [{'origin': 'example.com'},
+ {'origin': 'example.com',
+ 'class': 'CH'},
+ {'origin': 'example.org'}]})
+ self.assertEqual({}, self.unix._zone_config[('IN', 'example.com.')])
+ self.assertEqual({}, self.unix._zone_config[('CH', 'example.com.')])
+ self.assertEqual({}, self.unix._zone_config[('IN', 'example.org.')])
+
+ # Duplicate data: should be rejected with an exception
+ self.assertRaises(XfroutConfigError,
+ self.unix.update_config_data,
+ {'zone_config': [{'origin': 'example.com'},
+ {'origin': 'example.org'},
+ {'origin': 'example.com'}]})
+
+ def test_zone_config_data_with_acl(self):
+ # Similar to the previous test, but with transfer_acl config
+ self.unix.update_config_data({'zone_config':
+ [{'origin': 'example.com',
+ 'transfer_acl':
+ [{'from': '127.0.0.1',
+ 'action': 'ACCEPT'}]}]})
+ acl = self.unix._zone_config[('IN', 'example.com.')]['transfer_acl']
+ self.check_loaded_ACL(acl)
+
+ # invalid ACL syntax will be rejected with exception
+ self.assertRaises(XfroutConfigError,
self.unix.update_config_data,
- {'query_acl': ['Something bad']})
- self.check_loaded_ACL()
+ {'zone_config': [{'origin': 'example.com',
+ 'transfer_acl':
+ [{'action': 'BADACTION'}]}]})
def test_get_db_file(self):
self.assertEqual(self.unix.get_db_file(), "initdb.file")
diff --git a/src/bin/xfrout/xfrout.py.in b/src/bin/xfrout/xfrout.py.in
index 144a1b8..8049e29 100755
--- a/src/bin/xfrout/xfrout.py.in
+++ b/src/bin/xfrout/xfrout.py.in
@@ -48,11 +48,23 @@ except ImportError as e:
# must keep running, so we warn about it and move forward.
log.error(XFROUT_IMPORT, str(e))
-from isc.acl.acl import ACCEPT, REJECT, DROP
+from isc.acl.acl import ACCEPT, REJECT, DROP, LoaderError
from isc.acl.dns import REQUEST_LOADER
isc.util.process.rename()
+class XfroutConfigError(Exception):
+ """An exception indicating an error in updating xfrout configuration.
+
+ This exception is raised when the xfrout process encouters an error in
+ handling configuration updates. Not all syntax error can be caught
+ at the module-CC layer, so xfrout needs to (explicitly or implicitly)
+ validate the given configuration data itself. When it finds an error
+ it raises this exception (either directly or by converting an exception
+ from other modules) as a unified error in configuration.
+ """
+ pass
+
def init_paths():
global SPECFILE_PATH
global AUTH_SPECFILE_PATH
@@ -79,14 +91,12 @@ init_paths()
SPECFILE_LOCATION = SPECFILE_PATH + "/xfrout.spec"
AUTH_SPECFILE_LOCATION = AUTH_SPECFILE_PATH + os.sep + "auth.spec"
-MAX_TRANSFERS_OUT = 10
VERBOSE_MODE = False
# tsig sign every N axfr packets.
TSIG_SIGN_EVERY_NTH = 96
XFROUT_MAX_MESSAGE_SIZE = 65535
-
def get_rrset_len(rrset):
"""Returns the wire length of the given RRset"""
bytes = bytearray()
@@ -96,7 +106,7 @@ def get_rrset_len(rrset):
class XfroutSession():
def __init__(self, sock_fd, request_data, server, tsig_key_ring, remote,
- acl):
+ default_acl, zone_config):
self._sock_fd = sock_fd
self._request_data = request_data
self._server = server
@@ -104,7 +114,8 @@ class XfroutSession():
self._tsig_ctx = None
self._tsig_len = 0
self._remote = remote
- self._acl = acl
+ self._acl = default_acl
+ self._zone_config = zone_config
self.handle()
def create_tsig_ctx(self, tsig_record, tsig_key_ring):
@@ -140,34 +151,50 @@ class XfroutSession():
try:
msg = Message(Message.PARSE)
Message.from_wire(msg, mdata)
-
- # TSIG related checks
- rcode = self._check_request_tsig(msg, mdata)
-
- if rcode == Rcode.NOERROR():
- # ACL checks
- acl_result = self._acl.execute(
- isc.acl.dns.RequestContext(self._remote,
- msg.get_tsig_record()))
- if acl_result == DROP:
- logger.info(XFROUT_QUERY_DROPPED,
- self._get_query_zone_name(msg),
- self._get_query_zone_class(msg),
- self._remote[0], self._remote[1])
- return None, None
- elif acl_result == REJECT:
- logger.info(XFROUT_QUERY_REJECTED,
- self._get_query_zone_name(msg),
- self._get_query_zone_class(msg),
- self._remote[0], self._remote[1])
- return Rcode.REFUSED(), msg
-
- except Exception as err:
+ except Exception as err: # Exception is too broad
logger.error(XFROUT_PARSE_QUERY_ERROR, err)
return Rcode.FORMERR(), None
+ # TSIG related checks
+ rcode = self._check_request_tsig(msg, mdata)
+
+ if rcode == Rcode.NOERROR():
+ # ACL checks
+ zone_name = msg.get_question()[0].get_name()
+ zone_class = msg.get_question()[0].get_class()
+ acl = self._get_transfer_acl(zone_name, zone_class)
+ acl_result = acl.execute(
+ isc.acl.dns.RequestContext(self._remote,
+ msg.get_tsig_record()))
+ if acl_result == DROP:
+ logger.info(XFROUT_QUERY_DROPPED, zone_name, zone_class,
+ self._remote[0], self._remote[1])
+ return None, None
+ elif acl_result == REJECT:
+ logger.info(XFROUT_QUERY_REJECTED, zone_name, zone_class,
+ self._remote[0], self._remote[1])
+ return Rcode.REFUSED(), msg
+
return rcode, msg
+ def _get_transfer_acl(self, zone_name, zone_class):
+ '''Return the ACL that should be applied for a given zone.
+
+ The zone is identified by a tuple of name and RR class.
+ If a per zone configuration for the zone exists and contains
+ transfer_acl, that ACL will be used; otherwise, the default
+ ACL will be used.
+
+ '''
+ # Internally zone names are managed in lower cased label characters,
+ # so we first need to convert the name.
+ zone_name_lower = Name(zone_name.to_text(), True)
+ config_key = (zone_class.to_text(), zone_name_lower.to_text())
+ if config_key in self._zone_config and \
+ 'transfer_acl' in self._zone_config[config_key]:
+ return self._zone_config[config_key]['transfer_acl']
+ return self._acl
+
def _get_query_zone_name(self, msg):
question = msg.get_question()[0]
return question.get_name().to_text()
@@ -384,10 +411,12 @@ class XfroutSession():
self._send_message_with_last_soa(msg, sock_fd, rrset_soa, message_upper_len,
count_since_last_tsig_sign)
-class UnixSockServer(socketserver_mixin.NoPollMixIn, ThreadingUnixStreamServer):
+class UnixSockServer(socketserver_mixin.NoPollMixIn,
+ ThreadingUnixStreamServer):
'''The unix domain socket server which accept xfr query sent from auth server.'''
- def __init__(self, sock_file, handle_class, shutdown_event, config_data, cc):
+ def __init__(self, sock_file, handle_class, shutdown_event, config_data,
+ cc):
self._remove_unused_sock_file(sock_file)
self._sock_file = sock_file
socketserver_mixin.NoPollMixIn.__init__(self)
@@ -395,16 +424,15 @@ class UnixSockServer(socketserver_mixin.NoPollMixIn, ThreadingUnixStreamServer):
self._shutdown_event = shutdown_event
self._write_sock, self._read_sock = socket.socketpair()
self._common_init()
- self.update_config_data(config_data)
self._cc = cc
+ self.update_config_data(config_data)
def _common_init(self):
+ '''Initialization shared with the mock server class used for tests'''
self._lock = threading.Lock()
self._transfers_counter = 0
- # This default value will probably get overwritten by the (same)
- # default value from the spec file. This is here just to make
- # sure and to make the default value in tests consistent.
- self._acl = REQUEST_LOADER.load('[{"action": "ACCEPT"}]')
+ self._zone_config = {}
+ self._acl = None # this will be initialized in update_config_data()
def _receive_query_message(self, sock):
''' receive request message from sock'''
@@ -482,7 +510,7 @@ class UnixSockServer(socketserver_mixin.NoPollMixIn, ThreadingUnixStreamServer):
if not request_data:
return
- t = threading.Thread(target = self.finish_request,
+ t = threading.Thread(target=self.finish_request,
args = (sock_fd, request_data))
if self.daemon_threads:
t.daemon = True
@@ -506,10 +534,17 @@ class UnixSockServer(socketserver_mixin.NoPollMixIn, ThreadingUnixStreamServer):
return sock.getpeername()
def finish_request(self, sock_fd, request_data):
- '''Finish one request by instantiating RequestHandlerClass.'''
+ '''Finish one request by instantiating RequestHandlerClass.
+
+ This method creates a XfroutSession object.
+ '''
+ self._lock.acquire()
+ acl = self._acl
+ zone_config = self._zone_config
+ self._lock.release()
self.RequestHandlerClass(sock_fd, request_data, self,
self.tsig_key_ring,
- self._guess_remote(sock_fd), self._acl)
+ self._guess_remote(sock_fd), acl, zone_config)
def _remove_unused_sock_file(self, sock_file):
'''Try to remove the socket file. If the file is being used
@@ -551,16 +586,65 @@ class UnixSockServer(socketserver_mixin.NoPollMixIn, ThreadingUnixStreamServer):
pass
def update_config_data(self, new_config):
- '''Apply the new config setting of xfrout module. '''
- logger.info(XFROUT_NEW_CONFIG)
- if 'query_acl' in new_config:
- self._acl = REQUEST_LOADER.load(new_config['query_acl'])
+ '''Apply the new config setting of xfrout module.
+
+ '''
self._lock.acquire()
- self._max_transfers_out = new_config.get('transfers_out')
- self.set_tsig_key_ring(new_config.get('tsig_key_ring'))
+ try:
+ logger.info(XFROUT_NEW_CONFIG)
+ new_acl = self._acl
+ if 'transfer_acl' in new_config:
+ try:
+ new_acl = REQUEST_LOADER.load(new_config['transfer_acl'])
+ except LoaderError as e:
+ raise XfroutConfigError('Failed to parse transfer_acl: ' +
+ str(e))
+
+ new_zone_config = self._zone_config
+ zconfig_data = new_config.get('zone_config')
+ if zconfig_data is not None:
+ new_zone_config = self.__create_zone_config(zconfig_data)
+
+ self._acl = new_acl
+ self._zone_config = new_zone_config
+ self._max_transfers_out = new_config.get('transfers_out')
+ self.set_tsig_key_ring(new_config.get('tsig_key_ring'))
+ except Exception as e:
+ self._lock.release()
+ raise e
self._lock.release()
logger.info(XFROUT_NEW_CONFIG_DONE)
+ def __create_zone_config(self, zone_config_list):
+ new_config = {}
+ for zconf in zone_config_list:
+ # convert the class, origin (name) pair. First build pydnspp
+ # object to reject invalid input.
+ zclass_str = zconf.get('class')
+ if zclass_str is None:
+ #zclass_str = 'IN' # temporary
+ zclass_str = self._cc.get_default_value('zone_config/class')
+ zclass = RRClass(zclass_str)
+ zorigin = Name(zconf['origin'], True)
+ config_key = (zclass.to_text(), zorigin.to_text())
+
+ # reject duplicate config
+ if config_key in new_config:
+ raise XfroutConfigError('Duplicate zone_config for ' +
+ str(zorigin) + '/' + str(zclass))
+
+ # create a new config entry, build any given (and known) config
+ new_config[config_key] = {}
+ if 'transfer_acl' in zconf:
+ try:
+ new_config[config_key]['transfer_acl'] = \
+ REQUEST_LOADER.load(zconf['transfer_acl'])
+ except LoaderError as e:
+ raise XfroutConfigError('Failed to parse transfer_acl ' +
+ 'for ' + zorigin.to_text() + '/' +
+ zclass_str + ': ' + str(e))
+ return new_config
+
def set_tsig_key_ring(self, key_list):
"""Set the tsig_key_ring , given a TSIG key string list representation. """
@@ -617,8 +701,10 @@ class XfroutServer:
def _start_xfr_query_listener(self):
'''Start a new thread to accept xfr query. '''
- self._unix_socket_server = UnixSockServer(self._listen_sock_file, XfroutSession,
- self._shutdown_event, self._config_data,
+ self._unix_socket_server = UnixSockServer(self._listen_sock_file,
+ XfroutSession,
+ self._shutdown_event,
+ self._config_data,
self._cc)
listener = threading.Thread(target=self._unix_socket_server.serve_forever)
listener.start()
@@ -726,6 +812,10 @@ if '__main__' == __name__:
logger.INFO(XFROUT_STOPPED_BY_KEYBOARD)
except SessionError as e:
logger.error(XFROUT_CC_SESSION_ERROR, str(e))
+ except ModuleCCSessionError as e:
+ logger.error(XFROUT_MODULECC_SESSION_ERROR, str(e))
+ except XfroutConfigError as e:
+ logger.error(XFROUT_CONFIG_ERROR, str(e))
except SessionTimeout as e:
logger.error(XFROUT_CC_SESSION_TIMEOUT_ERROR)
diff --git a/src/bin/xfrout/xfrout.spec.pre.in b/src/bin/xfrout/xfrout.spec.pre.in
index 8ecbb0b..0891a57 100644
--- a/src/bin/xfrout/xfrout.spec.pre.in
+++ b/src/bin/xfrout/xfrout.spec.pre.in
@@ -51,7 +51,7 @@
}
},
{
- "item_name": "query_acl",
+ "item_name": "transfer_acl",
"item_type": "list",
"item_optional": false,
"item_default": [{"action": "ACCEPT"}],
@@ -61,6 +61,45 @@
"item_type": "any",
"item_optional": true
}
+ },
+ {
+ "item_name": "zone_config",
+ "item_type": "list",
+ "item_optional": true,
+ "item_default": [],
+ "list_item_spec":
+ {
+ "item_name": "zone_config_element",
+ "item_type": "map",
+ "item_optional": true,
+ "item_default": { "origin": "" },
+ "map_item_spec": [
+ {
+ "item_name": "origin",
+ "item_type": "string",
+ "item_optional": false,
+ "item_default": ""
+ },
+ {
+ "item_name": "class",
+ "item_type": "string",
+ "item_optional": false,
+ "item_default": "IN"
+ },
+ {
+ "item_name": "transfer_acl",
+ "item_type": "list",
+ "item_optional": true,
+ "item_default": [{"action": "ACCEPT"}],
+ "list_item_spec":
+ {
+ "item_name": "acl_element",
+ "item_type": "any",
+ "item_optional": true
+ }
+ }
+ ]
+ }
}
],
"commands": [
diff --git a/src/bin/xfrout/xfrout_messages.mes b/src/bin/xfrout/xfrout_messages.mes
index 121b2ad..b2e432c 100644
--- a/src/bin/xfrout/xfrout_messages.mes
+++ b/src/bin/xfrout/xfrout_messages.mes
@@ -47,6 +47,17 @@ a valid TSIG key.
There was a problem reading from the command and control channel. The
most likely cause is that the msgq daemon is not running.
+% XFROUT_MODULECC_SESSION_ERROR error encountered by configuration/command module: %1
+There was a problem in the lower level module handling configuration and
+control commands. This could happen for various reasons, but the most likely
+cause is that the configuration database contains a syntax error and xfrout
+failed to start at initialization. A detailed error message from the module
+will also be displayed.
+
+% XFROUT_CONFIG_ERROR error found in configuration data: %1
+The xfrout process encountered an error when installing the configuration at
+startup time. Details of the error are included in the log message.
+
% XFROUT_CC_SESSION_TIMEOUT_ERROR timeout waiting for cc response
There was a problem reading a response from another module over the
command and control channel. The most likely cause is that the
diff --git a/src/cppcheck-suppress.lst b/src/cppcheck-suppress.lst
index a4fea30..8a4c7c1 100644
--- a/src/cppcheck-suppress.lst
+++ b/src/cppcheck-suppress.lst
@@ -3,7 +3,7 @@
debug
missingInclude
// This is a template, and should be excluded from the check
-unreadVariable:src/lib/dns/rdata/template.cc:60
+unreadVariable:src/lib/dns/rdata/template.cc:61
// Intentional self assignment tests. Suppress warning about them.
selfAssignment:src/lib/dns/tests/name_unittest.cc:293
selfAssignment:src/lib/dns/tests/rdata_unittest.cc:228
diff --git a/src/lib/datasrc/Makefile.am b/src/lib/datasrc/Makefile.am
index 8f8a5ce..6b71388 100644
--- a/src/lib/datasrc/Makefile.am
+++ b/src/lib/datasrc/Makefile.am
@@ -30,6 +30,7 @@ libdatasrc_la_LIBADD = $(top_builddir)/src/lib/exceptions/libexceptions.la
libdatasrc_la_LIBADD += $(top_builddir)/src/lib/dns/libdns++.la
libdatasrc_la_LIBADD += $(top_builddir)/src/lib/log/liblog.la
libdatasrc_la_LIBADD += $(top_builddir)/src/lib/cc/libcc.la
+libdatasrc_la_LIBADD += $(SQLITE_LIBS)
BUILT_SOURCES = datasrc_messages.h datasrc_messages.cc
datasrc_messages.h datasrc_messages.cc: Makefile datasrc_messages.mes
diff --git a/src/lib/datasrc/database.cc b/src/lib/datasrc/database.cc
index 2c5aaeb..e476297 100644
--- a/src/lib/datasrc/database.cc
+++ b/src/lib/datasrc/database.cc
@@ -174,105 +174,42 @@ private:
};
}
-std::pair<bool, isc::dns::RRsetPtr>
-DatabaseClient::Finder::getRRset(const isc::dns::Name& name,
- const isc::dns::RRType* type,
- bool want_cname, bool want_dname,
- bool want_ns,
- const isc::dns::Name* construct_name)
+DatabaseClient::Finder::FoundRRsets
+DatabaseClient::Finder::getRRsets(const string& name, const WantedTypes& types,
+ bool check_ns, const string* construct_name)
{
RRsigStore sig_store;
bool records_found = false;
- isc::dns::RRsetPtr result_rrset;
+ std::map<RRType, RRsetPtr> result;
// Request the context
DatabaseAccessor::IteratorContextPtr
- context(accessor_->getRecords(name.toText(), zone_id_));
+ context(accessor_->getRecords(name, zone_id_));
// It must not return NULL, that's a bug of the implementation
if (!context) {
- isc_throw(isc::Unexpected, "Iterator context null at " +
- name.toText());
+ isc_throw(isc::Unexpected, "Iterator context null at " + name);
}
std::string columns[DatabaseAccessor::COLUMN_COUNT];
if (construct_name == NULL) {
construct_name = &name;
}
+
+ const Name construct_name_object(*construct_name);
+
+ bool seen_cname(false);
+ bool seen_ds(false);
+ bool seen_other(false);
+ bool seen_ns(false);
+
while (context->getNext(columns)) {
- if (!records_found) {
- records_found = true;
- }
+ // The domain is not empty
+ records_found = true;
try {
- const isc::dns::RRType cur_type(columns[DatabaseAccessor::
- TYPE_COLUMN]);
- const isc::dns::RRTTL cur_ttl(columns[DatabaseAccessor::
- TTL_COLUMN]);
- // Ths sigtype column was an optimization for finding the
- // relevant RRSIG RRs for a lookup. Currently this column is
- // not used in this revised datasource implementation. We
- // should either start using it again, or remove it from use
- // completely (i.e. also remove it from the schema and the
- // backend implementation).
- // Note that because we don't use it now, we also won't notice
- // it if the value is wrong (i.e. if the sigtype column
- // contains an rrtype that is different from the actual value
- // of the 'type covered' field in the RRSIG Rdata).
- //cur_sigtype(columns[SIGTYPE_COLUMN]);
-
- // Check for delegations before checking for the right type.
- // This is needed to properly delegate request for the NS
- // record itself.
- //
- // This happens with NS only, CNAME must be alone and DNAME
- // is not checked in the exact queried domain.
- if (want_ns && cur_type == isc::dns::RRType::NS()) {
- if (result_rrset &&
- result_rrset->getType() != isc::dns::RRType::NS()) {
- isc_throw(DataSourceError, "NS found together with data"
- " in non-apex domain " + name.toText());
- }
- addOrCreate(result_rrset, *construct_name, getClass(),
- cur_type, cur_ttl,
- columns[DatabaseAccessor::RDATA_COLUMN],
- *accessor_);
- } else if (type != NULL && cur_type == *type) {
- if (result_rrset &&
- result_rrset->getType() == isc::dns::RRType::CNAME()) {
- isc_throw(DataSourceError, "CNAME found but it is not "
- "the only record for " + name.toText());
- } else if (result_rrset && want_ns &&
- result_rrset->getType() == isc::dns::RRType::NS()) {
- isc_throw(DataSourceError, "NS found together with data"
- " in non-apex domain " + name.toText());
- }
- addOrCreate(result_rrset, *construct_name, getClass(),
- cur_type, cur_ttl,
- columns[DatabaseAccessor::RDATA_COLUMN],
- *accessor_);
- } else if (want_cname && cur_type == isc::dns::RRType::CNAME()) {
- // There should be no other data, so result_rrset should
- // be empty.
- if (result_rrset) {
- isc_throw(DataSourceError, "CNAME found but it is not "
- "the only record for " + name.toText());
- }
- addOrCreate(result_rrset, *construct_name, getClass(),
- cur_type, cur_ttl,
- columns[DatabaseAccessor::RDATA_COLUMN],
- *accessor_);
- } else if (want_dname && cur_type == isc::dns::RRType::DNAME()) {
- // There should be max one RR of DNAME present
- if (result_rrset &&
- result_rrset->getType() == isc::dns::RRType::DNAME()) {
- isc_throw(DataSourceError, "DNAME with multiple RRs in " +
- name.toText());
- }
- addOrCreate(result_rrset, *construct_name, getClass(),
- cur_type, cur_ttl,
- columns[DatabaseAccessor::RDATA_COLUMN],
- *accessor_);
- } else if (cur_type == isc::dns::RRType::RRSIG()) {
+ const RRType cur_type(columns[DatabaseAccessor::TYPE_COLUMN]);
+
+ if (cur_type == RRType::RRSIG()) {
// If we get signatures before we get the actual data, we
// can't know which ones to keep and which to drop...
// So we keep a separate store of any signature that may be
@@ -280,27 +217,76 @@ DatabaseClient::Finder::getRRset(const isc::dns::Name& name,
// done.
// A possible optimization here is to not store them for
// types we are certain we don't need
- sig_store.addSig(isc::dns::rdata::createRdata(cur_type,
- getClass(), columns[DatabaseAccessor::RDATA_COLUMN]));
+ sig_store.addSig(rdata::createRdata(cur_type, getClass(),
+ columns[DatabaseAccessor::RDATA_COLUMN]));
+ }
+
+ if (types.find(cur_type) != types.end()) {
+ // This type is requested, so put it into result
+ const RRTTL cur_ttl(columns[DatabaseAccessor::TTL_COLUMN]);
+ // Ths sigtype column was an optimization for finding the
+ // relevant RRSIG RRs for a lookup. Currently this column is
+ // not used in this revised datasource implementation. We
+ // should either start using it again, or remove it from use
+ // completely (i.e. also remove it from the schema and the
+ // backend implementation).
+ // Note that because we don't use it now, we also won't notice
+ // it if the value is wrong (i.e. if the sigtype column
+ // contains an rrtype that is different from the actual value
+ // of the 'type covered' field in the RRSIG Rdata).
+ //cur_sigtype(columns[SIGTYPE_COLUMN]);
+ addOrCreate(result[cur_type], construct_name_object,
+ getClass(), cur_type, cur_ttl,
+ columns[DatabaseAccessor::RDATA_COLUMN],
+ *accessor_);
}
- } catch (const isc::dns::InvalidRRType& irt) {
+
+ if (cur_type == RRType::CNAME()) {
+ seen_cname = true;
+ } else if (cur_type == RRType::NS()) {
+ seen_ns = true;
+ } else if (cur_type == RRType::DS()) {
+ seen_ds = true;
+ } else if (cur_type != RRType::RRSIG() &&
+ cur_type != RRType::NSEC3() &&
+ cur_type != RRType::NSEC()) {
+ // NSEC and RRSIG can coexist with anything, otherwise
+ // we've seen something that can't live together with potential
+ // CNAME or NS
+ //
+ // NSEC3 lives in separate namespace from everything, therefore
+ // we just ignore it here for these checks as well.
+ seen_other = true;
+ }
+ } catch (const InvalidRRType&) {
isc_throw(DataSourceError, "Invalid RRType in database for " <<
name << ": " << columns[DatabaseAccessor::
TYPE_COLUMN]);
- } catch (const isc::dns::InvalidRRTTL& irttl) {
+ } catch (const InvalidRRTTL&) {
isc_throw(DataSourceError, "Invalid TTL in database for " <<
name << ": " << columns[DatabaseAccessor::
TTL_COLUMN]);
- } catch (const isc::dns::rdata::InvalidRdataText& ird) {
+ } catch (const rdata::InvalidRdataText&) {
isc_throw(DataSourceError, "Invalid rdata in database for " <<
name << ": " << columns[DatabaseAccessor::
RDATA_COLUMN]);
}
}
- if (result_rrset) {
- sig_store.appendSignatures(result_rrset);
+ if (seen_cname && (seen_other || seen_ns || seen_ds)) {
+ isc_throw(DataSourceError, "CNAME shares domain " << name <<
+ " with something else");
+ }
+ if (check_ns && seen_ns && seen_other) {
+ isc_throw(DataSourceError, "NS shares domain " << name <<
+ " with something else");
}
- return (std::pair<bool, isc::dns::RRsetPtr>(records_found, result_rrset));
+ // Add signatures to all found RRsets
+ for (std::map<RRType, RRsetPtr>::iterator i(result.begin());
+ i != result.end(); ++ i) {
+ sig_store.appendSignatures(i->second);
+ }
+
+ return (FoundRRsets(records_found, result));
}
bool
@@ -317,6 +303,92 @@ DatabaseClient::Finder::hasSubdomains(const std::string& name) {
return (context->getNext(columns));
}
+// Some manipulation with RRType sets
+namespace {
+
+// Bunch of functions to construct specific sets of RRTypes we will
+// ask from it.
+typedef std::set<RRType> WantedTypes;
+
+const WantedTypes&
+NSEC_TYPES() {
+ static bool initialized(false);
+ static WantedTypes result;
+
+ if (!initialized) {
+ result.insert(RRType::NSEC());
+ initialized = true;
+ }
+ return (result);
+}
+
+const WantedTypes&
+DELEGATION_TYPES() {
+ static bool initialized(false);
+ static WantedTypes result;
+
+ if (!initialized) {
+ result.insert(RRType::DNAME());
+ result.insert(RRType::NS());
+ initialized = true;
+ }
+ return (result);
+}
+
+const WantedTypes&
+FINAL_TYPES() {
+ static bool initialized(false);
+ static WantedTypes result;
+
+ if (!initialized) {
+ result.insert(RRType::CNAME());
+ result.insert(RRType::NS());
+ result.insert(RRType::NSEC());
+ initialized = true;
+ }
+ return (result);
+}
+
+}
+
+RRsetPtr
+DatabaseClient::Finder::findNSECCover(const Name& name) {
+ try {
+ // Which one should contain the NSEC record?
+ const Name coverName(findPreviousName(name));
+ // Get the record and copy it out
+ const FoundRRsets found = getRRsets(coverName.toText(), NSEC_TYPES(),
+ coverName != getOrigin());
+ const FoundIterator
+ nci(found.second.find(RRType::NSEC()));
+ if (nci != found.second.end()) {
+ return (nci->second);
+ } else {
+ // The previous doesn't contain NSEC.
+ // Badly signed zone or a bug?
+
+ // FIXME: Currently, if the zone is not signed, we could get
+ // here. In that case we can't really throw, but for now, we can't
+ // recognize it. So we don't throw at all, enable it once
+ // we have a is_signed flag or something.
+#if 0
+ isc_throw(DataSourceError, "No NSEC in " +
+ coverName.toText() + ", but it was "
+ "returned as previous - "
+ "accessor error? Badly signed zone?");
+#endif
+ }
+ }
+ catch (const isc::NotImplemented&) {
+ // Well, they want DNSSEC, but there is no available.
+ // So we don't provide anything.
+ LOG_INFO(logger, DATASRC_DATABASE_COVER_NSEC_UNSUPPORTED).
+ arg(accessor_->getDBName()).arg(name);
+ }
+ // We didn't find it, return nothing
+ return (RRsetPtr());
+}
+
ZoneFinder::FindResult
DatabaseClient::Finder::find(const isc::dns::Name& name,
const isc::dns::RRType& type,
@@ -326,10 +398,12 @@ DatabaseClient::Finder::find(const isc::dns::Name& name,
// This variable is used to determine the difference between
// NXDOMAIN and NXRRSET
bool records_found = false;
- bool glue_ok(options & FIND_GLUE_OK);
+ bool glue_ok((options & FIND_GLUE_OK) != 0);
+ const bool dnssec_data((options & FIND_DNSSEC) != 0);
+ bool get_cover(false);
isc::dns::RRsetPtr result_rrset;
ZoneFinder::Result result_status = SUCCESS;
- std::pair<bool, isc::dns::RRsetPtr> found;
+ FoundRRsets found;
logger.debug(DBG_TRACE_DETAILED, DATASRC_DATABASE_FIND_RECORDS)
.arg(accessor_->getDBName()).arg(name).arg(type);
// In case we are in GLUE_OK mode and start matching wildcards,
@@ -337,11 +411,11 @@ DatabaseClient::Finder::find(const isc::dns::Name& name,
isc::dns::RRsetPtr first_ns;
// First, do we have any kind of delegation (NS/DNAME) here?
- Name origin(getOrigin());
- size_t origin_label_count(origin.getLabelCount());
+ const Name origin(getOrigin());
+ const size_t origin_label_count(origin.getLabelCount());
// Number of labels in the last known non-empty domain
size_t last_known(origin_label_count);
- size_t current_label_count(name.getLabelCount());
+ const size_t current_label_count(name.getLabelCount());
// This is how many labels we remove to get origin
size_t remove_labels(current_label_count - origin_label_count);
@@ -349,35 +423,44 @@ DatabaseClient::Finder::find(const isc::dns::Name& name,
for (int i(remove_labels); i > 0; --i) {
Name superdomain(name.split(i));
// Look if there's NS or DNAME (but ignore the NS in origin)
- found = getRRset(superdomain, NULL, false, true,
- i != remove_labels && !glue_ok);
+ found = getRRsets(superdomain.toText(), DELEGATION_TYPES(),
+ i != remove_labels);
if (found.first) {
// It contains some RRs, so it exists.
last_known = superdomain.getLabelCount();
- // In case we are in GLUE_OK, we want to store the highest
- // encountered RRset.
- if (glue_ok && !first_ns && i != remove_labels) {
- first_ns = getRRset(superdomain, NULL, false, false,
- true).second;
- }
- }
- if (found.second) {
- // We found something redirecting somewhere else
- // (it can be only NS or DNAME here)
- result_rrset = found.second;
- if (result_rrset->getType() == isc::dns::RRType::NS()) {
+
+ const FoundIterator nsi(found.second.find(RRType::NS()));
+ const FoundIterator dni(found.second.find(RRType::DNAME()));
+ // In case we are in GLUE_OK mode, we want to store the
+ // highest encountered NS (but not apex)
+ if (glue_ok && !first_ns && i != remove_labels &&
+ nsi != found.second.end()) {
+ first_ns = nsi->second;
+ } else if (!glue_ok && i != remove_labels &&
+ nsi != found.second.end()) {
+ // Do a NS delegation, but ignore NS in glue_ok mode. Ignore
+ // delegation in apex
LOG_DEBUG(logger, DBG_TRACE_DETAILED,
DATASRC_DATABASE_FOUND_DELEGATION).
arg(accessor_->getDBName()).arg(superdomain);
+ result_rrset = nsi->second;
result_status = DELEGATION;
- } else {
+ // No need to go lower, found
+ break;
+ } else if (dni != found.second.end()) {
+ // Very similar with DNAME
LOG_DEBUG(logger, DBG_TRACE_DETAILED,
DATASRC_DATABASE_FOUND_DNAME).
arg(accessor_->getDBName()).arg(superdomain);
+ result_rrset = dni->second;
result_status = DNAME;
+ if (result_rrset->getRdataCount() != 1) {
+ isc_throw(DataSourceError, "DNAME at " << superdomain <<
+ " has " << result_rrset->getRdataCount() <<
+ " rdata, 1 expected");
+ }
+ break;
}
- // Don't search more
- break;
}
}
@@ -385,21 +468,37 @@ DatabaseClient::Finder::find(const isc::dns::Name& name,
// Try getting the final result and extract it
// It is special if there's a CNAME or NS, DNAME is ignored here
// And we don't consider the NS in origin
- found = getRRset(name, &type, true, false, name != origin && !glue_ok);
+
+ WantedTypes final_types(FINAL_TYPES());
+ final_types.insert(type);
+ found = getRRsets(name.toText(), final_types, name != origin);
records_found = found.first;
- result_rrset = found.second;
- if (result_rrset && name != origin && !glue_ok &&
- result_rrset->getType() == isc::dns::RRType::NS()) {
+
+ // NS records, CNAME record and Wanted Type records
+ const FoundIterator nsi(found.second.find(RRType::NS()));
+ const FoundIterator cni(found.second.find(RRType::CNAME()));
+ const FoundIterator wti(found.second.find(type));
+ if (name != origin && !glue_ok && nsi != found.second.end()) {
+ // There's a delegation at the exact node.
LOG_DEBUG(logger, DBG_TRACE_DETAILED,
DATASRC_DATABASE_FOUND_DELEGATION_EXACT).
arg(accessor_->getDBName()).arg(name);
result_status = DELEGATION;
- } else if (result_rrset && type != isc::dns::RRType::CNAME() &&
- result_rrset->getType() == isc::dns::RRType::CNAME()) {
+ result_rrset = nsi->second;
+ } else if (type != isc::dns::RRType::CNAME() &&
+ cni != found.second.end()) {
+ // A CNAME here
result_status = CNAME;
- }
-
- if (!result_rrset && !records_found) {
+ result_rrset = cni->second;
+ if (result_rrset->getRdataCount() != 1) {
+ isc_throw(DataSourceError, "CNAME with " <<
+ result_rrset->getRdataCount() <<
+ " rdata at " << name << ", expected 1");
+ }
+ } else if (wti != found.second.end()) {
+ // Just get the answer
+ result_rrset = wti->second;
+ } else if (!records_found) {
// Nothing lives here.
// But check if something lives below this
// domain and if so, pretend something is here as well.
@@ -408,23 +507,22 @@ DatabaseClient::Finder::find(const isc::dns::Name& name,
DATASRC_DATABASE_FOUND_EMPTY_NONTERMINAL).
arg(accessor_->getDBName()).arg(name);
records_found = true;
+ get_cover = dnssec_data;
} else {
// It's not empty non-terminal. So check for wildcards.
// We remove labels one by one and look for the wildcard there.
// Go up to first non-empty domain.
remove_labels = current_label_count - last_known;
- Name star("*");
for (size_t i(1); i <= remove_labels; ++ i) {
// Construct the name with *
- // TODO: Once the underlying DatabaseAccessor takes
- // string, do the concatenation on strings, not
- // Names
- Name superdomain(name.split(i));
- Name wildcard(star.concatenate(superdomain));
+ const Name superdomain(name.split(i));
+ const string wildcard("*." + superdomain.toText());
+ const string construct_name(name.toText());
// TODO What do we do about DNAME here?
- found = getRRset(wildcard, &type, true, false, true,
- &name);
+ // The types are the same as with original query
+ found = getRRsets(wildcard, final_types, true,
+ &construct_name);
if (found.first) {
if (first_ns) {
// In case we are under NS, we don't
@@ -445,7 +543,42 @@ DatabaseClient::Finder::find(const isc::dns::Name& name,
// domain, but it could be empty non-terminal. In
// that case, we need to cancel the match.
records_found = true;
- result_rrset = found.second;
+ const FoundIterator
+ cni(found.second.find(RRType::CNAME()));
+ const FoundIterator
+ nsi(found.second.find(RRType::NS()));
+ const FoundIterator
+ nci(found.second.find(RRType::NSEC()));
+ const FoundIterator wti(found.second.find(type));
+ if (cni != found.second.end() &&
+ type != RRType::CNAME()) {
+ result_rrset = cni->second;
+ result_status = CNAME;
+ } else if (nsi != found.second.end()) {
+ result_rrset = nsi->second;
+ result_status = DELEGATION;
+ } else if (wti != found.second.end()) {
+ result_rrset = wti->second;
+ result_status = WILDCARD;
+ } else {
+ // NXRRSET case in the wildcard
+ result_status = WILDCARD_NXRRSET;
+ if (dnssec_data &&
+ nci != found.second.end()) {
+ // User wants a proof the wildcard doesn't
+ // contain it
+ //
+ // However, we need to get the RRset in the
+ // name of the wildcard, not the constructed
+ // one, so we walk it again
+ found = getRRsets(wildcard, NSEC_TYPES(),
+ true);
+ result_rrset =
+ found.second.find(RRType::NSEC())->
+ second;
+ }
+ }
+
LOG_DEBUG(logger, DBG_TRACE_DETAILED,
DATASRC_DATABASE_WILDCARD).
arg(accessor_->getDBName()).arg(wildcard).
@@ -457,33 +590,63 @@ DatabaseClient::Finder::find(const isc::dns::Name& name,
arg(name).arg(superdomain);
}
break;
- } else if (hasSubdomains(wildcard.toText())) {
+ } else if (hasSubdomains(wildcard)) {
// Empty non-terminal asterisk
records_found = true;
LOG_DEBUG(logger, DBG_TRACE_DETAILED,
DATASRC_DATABASE_WILDCARD_EMPTY).
arg(accessor_->getDBName()).arg(wildcard).
arg(name);
+ if (dnssec_data) {
+ result_rrset = findNSECCover(Name(wildcard));
+ if (result_rrset) {
+ result_status = WILDCARD_NXRRSET;
+ }
+ }
break;
}
}
+ // This is the NXDOMAIN case (nothing found anywhere). If
+ // they want DNSSEC data, try getting the NSEC record
+ if (dnssec_data && !records_found) {
+ get_cover = true;
+ }
+ }
+ } else if (dnssec_data) {
+ // This is the "usual" NXRRSET case
+ // So in case they want DNSSEC, provide the NSEC
+ // (which should be available already here)
+ result_status = NXRRSET;
+ const FoundIterator nci(found.second.find(RRType::NSEC()));
+ if (nci != found.second.end()) {
+ result_rrset = nci->second;
}
}
}
if (!result_rrset) {
- if (records_found) {
- logger.debug(DBG_TRACE_DETAILED,
- DATASRC_DATABASE_FOUND_NXRRSET)
- .arg(accessor_->getDBName()).arg(name)
- .arg(getClass()).arg(type);
- result_status = NXRRSET;
- } else {
- logger.debug(DBG_TRACE_DETAILED,
- DATASRC_DATABASE_FOUND_NXDOMAIN)
- .arg(accessor_->getDBName()).arg(name)
- .arg(getClass()).arg(type);
- result_status = NXDOMAIN;
+ if (result_status == SUCCESS) {
+ // Should we look for NSEC covering the name?
+ if (get_cover) {
+ result_rrset = findNSECCover(name);
+ if (result_rrset) {
+ result_status = NXDOMAIN;
+ }
+ }
+ // Something is not here and we didn't decide yet what
+ if (records_found) {
+ logger.debug(DBG_TRACE_DETAILED,
+ DATASRC_DATABASE_FOUND_NXRRSET)
+ .arg(accessor_->getDBName()).arg(name)
+ .arg(getClass()).arg(type);
+ result_status = NXRRSET;
+ } else {
+ logger.debug(DBG_TRACE_DETAILED,
+ DATASRC_DATABASE_FOUND_NXDOMAIN)
+ .arg(accessor_->getDBName()).arg(name)
+ .arg(getClass()).arg(type);
+ result_status = NXDOMAIN;
+ }
}
} else {
logger.debug(DBG_TRACE_DETAILED,
@@ -494,6 +657,26 @@ DatabaseClient::Finder::find(const isc::dns::Name& name,
}
Name
+DatabaseClient::Finder::findPreviousName(const Name& name) const {
+ const string str(accessor_->findPreviousName(zone_id_,
+ name.reverse().toText()));
+ try {
+ return (Name(str));
+ }
+ /*
+ * To avoid having the same code many times, we just catch all the
+ * exceptions and handle them in a common code below
+ */
+ catch (const isc::dns::EmptyLabel&) {}
+ catch (const isc::dns::TooLongLabel&) {}
+ catch (const isc::dns::BadLabelType&) {}
+ catch (const isc::dns::BadEscape&) {}
+ catch (const isc::dns::TooLongName&) {}
+ catch (const isc::dns::IncompleteName&) {}
+ isc_throw(DataSourceError, "Bad name " + str + " from findPreviousName");
+}
+
+Name
DatabaseClient::Finder::getOrigin() const {
return (origin_);
}
diff --git a/src/lib/datasrc/database.h b/src/lib/datasrc/database.h
index 82918ac..8295779 100644
--- a/src/lib/datasrc/database.h
+++ b/src/lib/datasrc/database.h
@@ -28,6 +28,9 @@
#include <dns/name.h>
#include <exceptions/exceptions.h>
+#include <map>
+#include <set>
+
namespace isc {
namespace datasrc {
@@ -471,6 +474,34 @@ public:
* \return the name of the database
*/
virtual const std::string& getDBName() const = 0;
+
+ /**
+ * \brief It returns the previous name in DNSSEC order.
+ *
+ * This is used in DatabaseClient::findPreviousName and does more
+ * or less the real work, except for working on strings.
+ *
+ * \param rname The name to ask for previous of, in reversed form.
+ * We use the reversed form (see isc::dns::Name::reverse),
+ * because then the case insensitive order of string representation
+ * and the DNSSEC order correspond (eg. org.example.a is followed
+ * by org.example.a.b which is followed by org.example.b, etc).
+ * \param zone_id The zone to look through.
+ * \return The previous name.
+ * \note This function must return previous name even in case
+ * the queried rname does not exist in the zone.
+ * \note This method must skip under-the-zone-cut data (glue data).
+ * This might be implemented by looking for NSEC records (as glue
+ * data don't have them) in the zone or in some other way.
+ *
+ * \throw DataSourceError if there's a problem with the database.
+ * \throw NotImplemented if this database doesn't support DNSSEC
+ * or there's no previous name for the queried one (the NSECs
+ * might be missing or the queried name is less or equal the
+ * apex of the zone).
+ */
+ virtual std::string findPreviousName(int zone_id,
+ const std::string& rname) const = 0;
};
/**
@@ -587,6 +618,12 @@ public:
const FindOptions options = FIND_DEFAULT);
/**
+ * \brief Implementation of ZoneFinder::findPreviousName method.
+ */
+ virtual isc::dns::Name findPreviousName(const isc::dns::Name& query)
+ const;
+
+ /**
* \brief The zone ID
*
* This function provides the stored zone ID as passed to the
@@ -609,54 +646,42 @@ public:
boost::shared_ptr<DatabaseAccessor> accessor_;
const int zone_id_;
const isc::dns::Name origin_;
-
+ //
+ /// \brief Shortcut name for the result of getRRsets
+ typedef std::pair<bool, std::map<dns::RRType, dns::RRsetPtr> >
+ FoundRRsets;
+ /// \brief Just shortcut for set of types
+ typedef std::set<dns::RRType> WantedTypes;
/**
- * \brief Searches database for an RRset
+ * \brief Searches database for RRsets of one domain.
*
- * This method scans RRs of single domain specified by name and finds
- * RRset with given type or any of redirection RRsets that are
- * requested.
+ * This method scans RRs of single domain specified by name and
+ * extracts any RRsets found and requested by parameters.
*
- * This function is used internally by find(), because this part is
- * called multiple times with slightly different parameters.
+ * It is used internally by find(), because it is called multiple
+ * times (usually with different domains).
*
* \param name Which domain name should be scanned.
- * \param type The RRType which is requested. This can be NULL, in
- * which case the method will look for the redirections only.
- * \param want_cname If this is true, CNAME redirection may be returned
- * instead of the RRset with given type. If there's CNAME and
- * something else or the CNAME has multiple RRs, it throws
- * DataSourceError.
- * \param want_dname If this is true, DNAME redirection may be returned
- * instead. This is with type = NULL only and is not checked in
- * other circumstances. If the DNAME has multiple RRs, it throws
- * DataSourceError.
- * \param want_ns This allows redirection by NS to be returned. If
- * any other data is met as well, DataSourceError is thrown.
- * \param construct_name If set to non-NULL, the resulting RRset will
- * be constructed for this name instead of the queried one. This
- * is useful for wildcards.
- * \note It may happen that some of the above error conditions are not
- * detected in some circumstances. The goal here is not to validate
- * the domain in DB, but to avoid bad behaviour resulting from
- * broken data.
- * \return First part of the result tells if the domain contains any
- * RRs. This can be used to decide between NXDOMAIN and NXRRSET.
- * The second part is the RRset found (if any) with any relevant
- * signatures attached to it.
- * \todo This interface doesn't look very elegant. Any better idea
- * would be nice.
+ * \param types List of types the caller is interested in.
+ * \param check_ns If this is set to true, it checks nothing lives
+ * together with NS record (with few little exceptions, like RRSIG
+ * or NSEC). This check is meant for non-apex NS records.
+ * \param construct_name If this is NULL, the resulting RRsets have
+ * their name set to name. If it is not NULL, it overrides the name
+ * and uses this one (this can be used for wildcard synthesized
+ * records).
+ * \return A pair, where the first element indicates if the domain
+ * contains any RRs at all (not only the requested, it may happen
+ * this is set to true, but the second part is empty). The second
+ * part is map from RRtypes to RRsets of the corresponding types.
+ * If the RRset is not present in DB, the RRtype is not there at
+ * all (so you'll not find NULL pointer in the result).
+ * \throw DataSourceError If there's a low-level error with the
+ * database or the database contains bad data.
*/
- std::pair<bool, isc::dns::RRsetPtr> getRRset(const isc::dns::Name&
- name,
- const isc::dns::RRType*
- type,
- bool want_cname,
- bool want_dname,
- bool want_ns, const
- isc::dns::Name*
- construct_name = NULL);
-
+ FoundRRsets getRRsets(const std::string& name,
+ const WantedTypes& types, bool check_ns,
+ const std::string* construct_name = NULL);
/**
* \brief Checks if something lives below this domain.
*
@@ -666,6 +691,23 @@ public:
* \param name The domain to check.
*/
bool hasSubdomains(const std::string& name);
+
+ /**
+ * \brief Get the NSEC covering a name.
+ *
+ * This one calls findPreviousName on the given name and extracts an NSEC
+ * record on the result. It handles various error cases. The method exists
+ * to share code present at more than one location.
+ */
+ dns::RRsetPtr findNSECCover(const dns::Name& name);
+
+ /**
+ * \brief Convenience type shortcut.
+ *
+ * To find stuff in the result of getRRsets.
+ */
+ typedef std::map<dns::RRType, dns::RRsetPtr>::const_iterator
+ FoundIterator;
};
/**
diff --git a/src/lib/datasrc/datasrc_messages.mes b/src/lib/datasrc/datasrc_messages.mes
index efb88fd..04ad610 100644
--- a/src/lib/datasrc/datasrc_messages.mes
+++ b/src/lib/datasrc/datasrc_messages.mes
@@ -63,6 +63,11 @@ The maximum allowed number of items of the hotspot cache is set to the given
number. If there are too many, some of them will be dropped. The size of 0
means no limit.
+% DATASRC_DATABASE_COVER_NSEC_UNSUPPORTED %1 doesn't support DNSSEC when asked for NSEC data covering %2
+The datasource tried to provide an NSEC proof that the named domain does not
+exist, but the database backend doesn't support DNSSEC. No proof is included
+in the answer as a result.
+
% DATASRC_DATABASE_FIND_RECORDS looking in datasource %1 for record %2/%3
Debug information. The database data source is looking up records with the given
name and type in the database.
diff --git a/src/lib/datasrc/memory_datasrc.cc b/src/lib/datasrc/memory_datasrc.cc
index 630b1c0..2e94b67 100644
--- a/src/lib/datasrc/memory_datasrc.cc
+++ b/src/lib/datasrc/memory_datasrc.cc
@@ -661,6 +661,12 @@ InMemoryZoneFinder::getFileName() const {
return (impl_->file_name_);
}
+isc::dns::Name
+InMemoryZoneFinder::findPreviousName(const isc::dns::Name&) const {
+ isc_throw(NotImplemented, "InMemory data source doesn't support DNSSEC "
+ "yet, can't find previous name");
+}
+
/// Implementation details for \c InMemoryClient hidden from the public
/// interface.
///
diff --git a/src/lib/datasrc/memory_datasrc.h b/src/lib/datasrc/memory_datasrc.h
index c569548..95f589a 100644
--- a/src/lib/datasrc/memory_datasrc.h
+++ b/src/lib/datasrc/memory_datasrc.h
@@ -75,6 +75,12 @@ public:
isc::dns::RRsetList* target = NULL,
const FindOptions options = FIND_DEFAULT);
+ /// \brief Imelementation of the ZoneFinder::findPreviousName method
+ ///
+ /// This one throws NotImplemented exception, as InMemory doesn't
+ /// support DNSSEC currently.
+ virtual isc::dns::Name findPreviousName(const isc::dns::Name& query) const;
+
/// \brief Inserts an rrset into the zone.
///
/// It puts another RRset into the zone.
diff --git a/src/lib/datasrc/sqlite3_accessor.cc b/src/lib/datasrc/sqlite3_accessor.cc
index 956f447..69d5649 100644
--- a/src/lib/datasrc/sqlite3_accessor.cc
+++ b/src/lib/datasrc/sqlite3_accessor.cc
@@ -47,7 +47,8 @@ enum StatementID {
ADD_RECORD = 7,
DEL_RECORD = 8,
ITERATE = 9,
- NUM_STATEMENTS = 10
+ FIND_PREVIOUS = 10,
+ NUM_STATEMENTS = 11
};
const char* const text_statements[NUM_STATEMENTS] = {
@@ -68,7 +69,15 @@ const char* const text_statements[NUM_STATEMENTS] = {
"DELETE FROM records WHERE zone_id=?1 AND name=?2 " // DEL_RECORD
"AND rdtype=?3 AND rdata=?4",
"SELECT rdtype, ttl, sigtype, rdata, name FROM records " // ITERATE
- "WHERE zone_id = ?1 ORDER BY name, rdtype"
+ "WHERE zone_id = ?1 ORDER BY name, rdtype",
+ /*
+ * This one looks for previous name with NSEC record. It is done by
+ * using the reversed name. The NSEC is checked because we need to
+ * skip glue data, which don't have the NSEC.
+ */
+ "SELECT name FROM records " // FIND_PREVIOUS
+ "WHERE zone_id=?1 AND rdtype = 'NSEC' AND "
+ "rname < $2 ORDER BY rname DESC LIMIT 1"
};
struct SQLite3Parameters {
@@ -391,6 +400,28 @@ SQLite3Accessor::getZone(const std::string& name) const {
return (std::pair<bool, int>(false, 0));
}
+namespace {
+
+// Conversion to plain char
+const char*
+convertToPlainChar(const unsigned char* ucp, sqlite3 *db) {
+ if (ucp == NULL) {
+ // The field can really be NULL, in which case we return an
+ // empty string, or sqlite may have run out of memory, in
+ // which case we raise an error
+ if (sqlite3_errcode(db) == SQLITE_NOMEM) {
+ isc_throw(DataSourceError,
+ "Sqlite3 backend encountered a memory allocation "
+ "error in sqlite3_column_text()");
+ } else {
+ return ("");
+ }
+ }
+ const void* p = ucp;
+ return (static_cast<const char*>(p));
+}
+
+}
class SQLite3Accessor::Context : public DatabaseAccessor::IteratorContext {
public:
// Construct an iterator for all records. When constructed this
@@ -468,7 +499,8 @@ private:
void copyColumn(std::string (&data)[COLUMN_COUNT], int column) {
data[column] = convertToPlainChar(sqlite3_column_text(statement_,
- column));
+ column),
+ accessor_->dbparameters_->db_);
}
void bindZoneId(const int zone_id) {
@@ -495,29 +527,6 @@ private:
statement_ = NULL;
}
- // This helper method converts from the unsigned char* type (used by
- // sqlite3) to char* (wanted by std::string). Technically these types
- // might not be directly convertable
- // In case sqlite3_column_text() returns NULL, we just make it an
- // empty string, unless it was caused by a memory error
- const char* convertToPlainChar(const unsigned char* ucp) {
- if (ucp == NULL) {
- // The field can really be NULL, in which case we return an
- // empty string, or sqlite may have run out of memory, in
- // which case we raise an error
- if (sqlite3_errcode(accessor_->dbparameters_->db_)
- == SQLITE_NOMEM) {
- isc_throw(DataSourceError,
- "Sqlite3 backend encountered a memory allocation "
- "error in sqlite3_column_text()");
- } else {
- return ("");
- }
- }
- const void* p = ucp;
- return (static_cast<const char*>(p));
- }
-
const IteratorType iterator_type_;
boost::shared_ptr<const SQLite3Accessor> accessor_;
sqlite3_stmt *statement_;
@@ -658,5 +667,49 @@ SQLite3Accessor::deleteRecordInZone(const string (¶ms)[DEL_PARAM_COUNT]) {
*dbparameters_, DEL_RECORD, params, "delete record from zone");
}
+std::string
+SQLite3Accessor::findPreviousName(int zone_id, const std::string& rname)
+ const
+{
+ sqlite3_reset(dbparameters_->statements_[FIND_PREVIOUS]);
+ sqlite3_clear_bindings(dbparameters_->statements_[FIND_PREVIOUS]);
+
+ if (sqlite3_bind_int(dbparameters_->statements_[FIND_PREVIOUS], 1,
+ zone_id) != SQLITE_OK) {
+ isc_throw(SQLite3Error, "Could not bind zone ID " << zone_id <<
+ " to SQL statement (find previous): " <<
+ sqlite3_errmsg(dbparameters_->db_));
+ }
+ if (sqlite3_bind_text(dbparameters_->statements_[FIND_PREVIOUS], 2,
+ rname.c_str(), -1, SQLITE_STATIC) != SQLITE_OK) {
+ isc_throw(SQLite3Error, "Could not bind name " << rname <<
+ " to SQL statement (find previous): " <<
+ sqlite3_errmsg(dbparameters_->db_));
+ }
+
+ std::string result;
+ const int rc = sqlite3_step(dbparameters_->statements_[FIND_PREVIOUS]);
+ if (rc == SQLITE_ROW) {
+ // We found it
+ result = convertToPlainChar(sqlite3_column_text(dbparameters_->
+ statements_[FIND_PREVIOUS], 0), dbparameters_->db_);
+ }
+ sqlite3_reset(dbparameters_->statements_[FIND_PREVIOUS]);
+
+ if (rc == SQLITE_DONE) {
+ // No NSEC records here, this DB doesn't support DNSSEC or
+ // we asked before the apex
+ isc_throw(isc::NotImplemented, "The zone doesn't support DNSSEC or "
+ "query before apex");
+ }
+
+ if (rc != SQLITE_ROW && rc != SQLITE_DONE) {
+ // Some kind of error
+ isc_throw(SQLite3Error, "Could not get data for previous name");
+ }
+
+ return (result);
+}
+
}
}
diff --git a/src/lib/datasrc/sqlite3_accessor.h b/src/lib/datasrc/sqlite3_accessor.h
index fae249b..c4bacad 100644
--- a/src/lib/datasrc/sqlite3_accessor.h
+++ b/src/lib/datasrc/sqlite3_accessor.h
@@ -170,6 +170,10 @@ public:
/// "sqlite3_bind10.sqlite3".
virtual const std::string& getDBName() const { return (database_name_); }
+ /// \brief Concrete implementation of the pure virtual method
+ virtual std::string findPreviousName(int zone_id, const std::string& rname)
+ const;
+
private:
/// \brief Private database data
boost::scoped_ptr<SQLite3Parameters> dbparameters_;
diff --git a/src/lib/datasrc/tests/database_unittest.cc b/src/lib/datasrc/tests/database_unittest.cc
index 4ed9f12..fe57185 100644
--- a/src/lib/datasrc/tests/database_unittest.cc
+++ b/src/lib/datasrc/tests/database_unittest.cc
@@ -48,9 +48,11 @@ const char* const TEST_RECORDS[][5] = {
{"www.example.org.", "A", "3600", "", "192.0.2.1"},
{"www.example.org.", "AAAA", "3600", "", "2001:db8::1"},
{"www.example.org.", "AAAA", "3600", "", "2001:db8::2"},
+ {"www.example.org.", "NSEC", "3600", "", "www2.example.org. A AAAA NSEC RRSIG"},
+ {"www.example.org.", "RRSIG", "3600", "", "NSEC 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"},
{"www2.example.org.", "A", "3600", "", "192.0.2.1"},
- {"www2.example.org.","AAAA", "3600", "", "2001:db8::1"},
+ {"www2.example.org.", "AAAA", "3600", "", "2001:db8::1"},
{"www2.example.org.", "A", "3600", "", "192.0.2.2"},
{"cname.example.org.", "CNAME", "3600", "", "www.example.org."},
@@ -125,6 +127,7 @@ const char* const TEST_RECORDS[][5] = {
{"delegation.example.org.", "NS", "3600", "", "ns.example.com."},
{"delegation.example.org.", "NS", "3600", "",
"ns.delegation.example.org."},
+ {"delegation.example.org.", "DS", "3600", "", "1 RSAMD5 2 abcd"},
{"delegation.example.org.", "RRSIG", "3600", "", "NS 5 3 3600 "
"20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"},
{"ns.delegation.example.org.", "A", "3600", "", "192.0.2.1"},
@@ -153,6 +156,9 @@ const char* const TEST_RECORDS[][5] = {
// doesn't break anything
{"example.org.", "NS", "3600", "", "ns.example.com."},
{"example.org.", "A", "3600", "", "192.0.2.1"},
+ {"example.org.", "NSEC", "3600", "", "acnamesig1.example.org. NS A NSEC RRSIG"},
+ {"example.org.", "RRSIG", "3600", "", "NSEC 5 3 3600 20000101000000 "
+ "20000201000000 12345 example.org. FAKEFAKEFAKE"},
{"example.org.", "RRSIG", "3600", "", "NS 5 3 3600 20000101000000 "
"20000201000000 12345 example.org. FAKEFAKEFAKE"},
@@ -162,11 +168,23 @@ const char* const TEST_RECORDS[][5] = {
// Something for wildcards
{"*.wild.example.org.", "A", "3600", "", "192.0.2.5"},
{"*.wild.example.org.", "RRSIG", "3600", "A", "A 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"},
+ {"*.wild.example.org.", "NSEC", "3600", "", "cancel.here.wild.example.org. A NSEC RRSIG"},
+ {"*.wild.example.org.", "RRSIG", "3600", "", "NSEC 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"},
{"cancel.here.wild.example.org.", "AAAA", "3600", "", "2001:db8::5"},
{"delegatedwild.example.org.", "NS", "3600", "", "ns.example.com."},
{"*.delegatedwild.example.org.", "A", "3600", "", "192.0.2.5"},
{"wild.*.foo.example.org.", "A", "3600", "", "192.0.2.5"},
{"wild.*.foo.*.bar.example.org.", "A", "3600", "", "192.0.2.5"},
+ {"bao.example.org.", "NSEC", "3600", "", "wild.*.foo.*.bar.example.org. NSEC"},
+ {"*.cnamewild.example.org.", "CNAME", "3600", "", "www.example.org."},
+ {"*.nswild.example.org.", "NS", "3600", "", "ns.example.com."},
+ // For NSEC empty non-terminal
+ {"l.example.org.", "NSEC", "3600", "", "empty.nonterminal.example.org. NSEC"},
+ {"empty.nonterminal.example.org.", "A", "3600", "", "192.0.2.1"},
+ // Invalid rdata
+ {"invalidrdata.example.org.", "A", "3600", "", "Bunch of nonsense"},
+ {"invalidrdata2.example.org.", "A", "3600", "", "192.0.2.1"},
+ {"invalidrdata2.example.org.", "RRSIG", "3600", "", "Nonsense"},
{NULL, NULL, NULL, NULL, NULL},
};
@@ -223,6 +241,10 @@ public:
"This database datasource can't be iterated");
}
+ virtual std::string findPreviousName(int, const std::string&) const {
+ isc_throw(isc::NotImplemented,
+ "This data source doesn't support DNSSEC");
+ }
private:
const std::string database_name_;
@@ -529,6 +551,38 @@ public:
return (latest_clone_);
}
+ virtual std::string findPreviousName(int id, const std::string& rname)
+ const
+ {
+ // Hardcoded for now, but we could compute it from the data
+ // Maybe do it when it is needed some time in future?
+ if (id == -1) {
+ isc_throw(isc::NotImplemented, "Test not implemented behaviour");
+ } else if (id == 42) {
+ if (rname == "org.example.nonterminal.") {
+ return ("l.example.org.");
+ } else if (rname == "org.example.aa.") {
+ return ("example.org.");
+ } else if (rname == "org.example.www2." ||
+ rname == "org.example.www1.") {
+ return ("www.example.org.");
+ } else if (rname == "org.example.badnsec2.") {
+ return ("badnsec1.example.org.");
+ } else if (rname == "org.example.brokenname.") {
+ return ("brokenname...example.org.");
+ } else if (rname == "org.example.bar.*.") {
+ return ("bao.example.org.");
+ } else if (rname == "org.example.notimplnsec." ||
+ rname == "org.example.wild.here.") {
+ isc_throw(isc::NotImplemented, "Not implemented in this test");
+ } else {
+ isc_throw(isc::Unexpected, "Unexpected name");
+ }
+ } else {
+ isc_throw(isc::Unexpected, "Unknown zone ID");
+ }
+ }
+
private:
// The following member variables are storage and/or update work space
// of the test zone. The "master"s are the real objects that contain
@@ -797,7 +851,17 @@ public:
// The following two lines instantiate test cases with concrete accessor
// classes to be tested.
+// XXX: clang++ installed on our FreeBSD buildbot cannot complete compiling
+// this file, seemingly due to the size of the code. We'll consider more
+// complete workaround, but for a short term workaround we'll reduce the
+// number of tested accessor classes (thus reducing the amount of code
+// to be compiled) for this particular environment.
+#if defined(__clang__) && defined(__FreeBSD__)
+typedef ::testing::Types<MockAccessor> TestAccessorTypes;
+#else
typedef ::testing::Types<MockAccessor, TestSQLite3Accessor> TestAccessorTypes;
+#endif
+
TYPED_TEST_CASE(DatabaseClientTest, TestAccessorTypes);
// In some cases the entire test fixture is for the mock accessor only.
@@ -967,21 +1031,25 @@ doFindTest(ZoneFinder& finder,
ZoneFinder::FindResult result =
finder.find(name, type, NULL, options);
ASSERT_EQ(expected_result, result.code) << name << " " << type;
- if (!expected_rdatas.empty()) {
+ if (!expected_rdatas.empty() && result.rrset) {
checkRRset(result.rrset, expected_name != Name(".") ? expected_name :
name, finder.getClass(), expected_type, expected_ttl,
expected_rdatas);
- if (!expected_sig_rdatas.empty()) {
+ if (!expected_sig_rdatas.empty() && result.rrset->getRRsig()) {
checkRRset(result.rrset->getRRsig(), expected_name != Name(".") ?
expected_name : name, finder.getClass(),
isc::dns::RRType::RRSIG(), expected_ttl,
expected_sig_rdatas);
- } else {
+ } else if (expected_sig_rdatas.empty()) {
EXPECT_EQ(isc::dns::RRsetPtr(), result.rrset->getRRsig());
+ } else {
+ ADD_FAILURE() << "Missing RRSIG";
}
- } else {
+ } else if (expected_rdatas.empty()) {
EXPECT_EQ(isc::dns::RRsetPtr(), result.rrset);
+ } else {
+ ADD_FAILURE() << "Missing result";
}
}
@@ -1422,21 +1490,21 @@ TYPED_TEST(DatabaseClientTest, wildcard) {
"FAKEFAKEFAKE");
doFindTest(*finder, isc::dns::Name("a.wild.example.org"),
this->qtype_, this->qtype_, this->rrttl_,
- ZoneFinder::SUCCESS, this->expected_rdatas_,
+ ZoneFinder::WILDCARD, this->expected_rdatas_,
this->expected_sig_rdatas_);
doFindTest(*finder, isc::dns::Name("b.a.wild.example.org"),
- this->qtype_, this->qtype_, this->rrttl_, ZoneFinder::SUCCESS,
+ this->qtype_, this->qtype_, this->rrttl_, ZoneFinder::WILDCARD,
this->expected_rdatas_, this->expected_sig_rdatas_);
this->expected_rdatas_.clear();
this->expected_sig_rdatas_.clear();
doFindTest(*finder, isc::dns::Name("a.wild.example.org"),
isc::dns::RRType::AAAA(), isc::dns::RRType::AAAA(),
- this->rrttl_, ZoneFinder::NXRRSET, this->expected_rdatas_,
- this->expected_sig_rdatas_);
+ this->rrttl_, ZoneFinder::WILDCARD_NXRRSET,
+ this->expected_rdatas_, this->expected_sig_rdatas_);
doFindTest(*finder, isc::dns::Name("b.a.wild.example.org"),
isc::dns::RRType::AAAA(), isc::dns::RRType::AAAA(),
- this->rrttl_, ZoneFinder::NXRRSET, this->expected_rdatas_,
- this->expected_sig_rdatas_);
+ this->rrttl_, ZoneFinder::WILDCARD_NXRRSET,
+ this->expected_rdatas_, this->expected_sig_rdatas_);
// Direct request for this wildcard
this->expected_rdatas_.push_back("192.0.2.5");
@@ -1532,11 +1600,146 @@ TYPED_TEST(DatabaseClientTest, wildcard) {
doFindTest(*finder, isc::dns::Name(*name), this->qtype_,
this->qtype_, this->rrttl_, ZoneFinder::NXRRSET,
this->expected_rdatas_, this->expected_sig_rdatas_);
+ // FIXME: What should be returned in this case? How does the
+ // DNSSEC logic handle it?
}
+
+ const char* negative_dnssec_names[] = {
+ "a.bar.example.org.",
+ "foo.baz.bar.example.org.",
+ "a.foo.bar.example.org.",
+ NULL
+ };
+
+ this->expected_rdatas_.clear();
+ this->expected_rdatas_.push_back("wild.*.foo.*.bar.example.org. NSEC");
+ this->expected_sig_rdatas_.clear();
+ for (const char** name(negative_dnssec_names); *name != NULL; ++ name) {
+ doFindTest(*finder, isc::dns::Name(*name), this->qtype_,
+ RRType::NSEC(), this->rrttl_, ZoneFinder::WILDCARD_NXRRSET,
+ this->expected_rdatas_, this->expected_sig_rdatas_,
+ Name("bao.example.org."), ZoneFinder::FIND_DNSSEC);
+ }
+
+ // Some strange things in the wild node
+ this->expected_rdatas_.clear();
+ this->expected_rdatas_.push_back("www.example.org.");
+ this->expected_sig_rdatas_.clear();
+ doFindTest(*finder, isc::dns::Name("a.cnamewild.example.org."),
+ isc::dns::RRType::TXT(), isc::dns::RRType::CNAME(),
+ this->rrttl_, ZoneFinder::CNAME,
+ this->expected_rdatas_, this->expected_sig_rdatas_);
+
+ this->expected_rdatas_.clear();
+ this->expected_rdatas_.push_back("ns.example.com.");
+ doFindTest(*finder, isc::dns::Name("a.nswild.example.org."),
+ isc::dns::RRType::TXT(), isc::dns::RRType::NS(),
+ this->rrttl_, ZoneFinder::DELEGATION,
+ this->expected_rdatas_, this->expected_sig_rdatas_);
+}
+
+TYPED_TEST(DatabaseClientTest, NXRRSET_NSEC) {
+ // The domain exists, but doesn't have this RRType
+ // So we should get its NSEC
+ shared_ptr<DatabaseClient::Finder> finder(this->getFinder());
+
+ this->expected_rdatas_.push_back("www2.example.org. A AAAA NSEC RRSIG");
+ this->expected_sig_rdatas_.push_back("NSEC 5 3 3600 20000101000000 "
+ "20000201000000 12345 example.org. "
+ "FAKEFAKEFAKE");
+ doFindTest(*finder, isc::dns::Name("www.example.org."),
+ isc::dns::RRType::TXT(), isc::dns::RRType::NSEC(),
+ this->rrttl_, ZoneFinder::NXRRSET,
+ this->expected_rdatas_, this->expected_sig_rdatas_,
+ Name::ROOT_NAME(), ZoneFinder::FIND_DNSSEC);
+}
+
+TYPED_TEST(DatabaseClientTest, wildcardNXRRSET_NSEC) {
+ // The domain exists, but doesn't have this RRType
+ // So we should get its NSEC
+ //
+ // The user will have to query us again to get the correct
+ // answer (eg. prove there's not an exact match)
+ shared_ptr<DatabaseClient::Finder> finder(this->getFinder());
+
+ this->expected_rdatas_.push_back("cancel.here.wild.example.org. A NSEC "
+ "RRSIG");
+ this->expected_sig_rdatas_.push_back("NSEC 5 3 3600 20000101000000 "
+ "20000201000000 12345 example.org. "
+ "FAKEFAKEFAKE");
+ // Note that the NSEC name should NOT be synthesized.
+ doFindTest(*finder, isc::dns::Name("a.wild.example.org."),
+ isc::dns::RRType::TXT(), isc::dns::RRType::NSEC(),
+ this->rrttl_, ZoneFinder::WILDCARD_NXRRSET,
+ this->expected_rdatas_, this->expected_sig_rdatas_,
+ Name("*.wild.example.org"), ZoneFinder::FIND_DNSSEC);
+}
+
+TYPED_TEST(DatabaseClientTest, NXDOMAIN_NSEC) {
+ // The domain doesn't exist, so we must get the right NSEC
+ shared_ptr<DatabaseClient::Finder> finder(this->getFinder());
+
+ this->expected_rdatas_.push_back("www2.example.org. A AAAA NSEC RRSIG");
+ this->expected_sig_rdatas_.push_back("NSEC 5 3 3600 20000101000000 "
+ "20000201000000 12345 example.org. "
+ "FAKEFAKEFAKE");
+ doFindTest(*finder, isc::dns::Name("www1.example.org."),
+ isc::dns::RRType::TXT(), isc::dns::RRType::NSEC(),
+ this->rrttl_, ZoneFinder::NXDOMAIN,
+ this->expected_rdatas_, this->expected_sig_rdatas_,
+ Name("www.example.org."), ZoneFinder::FIND_DNSSEC);
+ this->expected_rdatas_.clear();
+ this->expected_rdatas_.push_back("acnamesig1.example.org. NS A NSEC RRSIG");
+ // This tests it works correctly in apex (there was a bug, where a check
+ // for NS-alone was there and it would throw).
+ doFindTest(*finder, isc::dns::Name("aa.example.org."),
+ isc::dns::RRType::TXT(), isc::dns::RRType::NSEC(),
+ this->rrttl_, ZoneFinder::NXDOMAIN,
+ this->expected_rdatas_, this->expected_sig_rdatas_,
+ Name("example.org."), ZoneFinder::FIND_DNSSEC);
+
+ // Check that if the DB doesn't support it, the exception from there
+ // is not propagated and it only does not include the NSEC
+ if (!this->is_mock_) {
+ return; // We don't make the real DB to throw
+ }
+ EXPECT_NO_THROW(doFindTest(*finder,
+ isc::dns::Name("notimplnsec.example.org."),
+ isc::dns::RRType::TXT(),
+ isc::dns::RRType::NSEC(), this->rrttl_,
+ ZoneFinder::NXDOMAIN, this->empty_rdatas_,
+ this->empty_rdatas_, Name::ROOT_NAME(),
+ ZoneFinder::FIND_DNSSEC));
+}
+
+TYPED_TEST(DatabaseClientTest, emptyNonterminalNSEC) {
+ // Same as NXDOMAIN_NSEC, but with empty non-terminal
+ shared_ptr<DatabaseClient::Finder> finder(this->getFinder());
+
+ this->expected_rdatas_.push_back("empty.nonterminal.example.org. NSEC");
+ doFindTest(*finder, isc::dns::Name("nonterminal.example.org."),
+ isc::dns::RRType::TXT(), isc::dns::RRType::NSEC(), this->rrttl_,
+ ZoneFinder::NXRRSET,
+ this->expected_rdatas_, this->expected_sig_rdatas_,
+ Name("l.example.org."), ZoneFinder::FIND_DNSSEC);
+
+ // Check that if the DB doesn't support it, the exception from there
+ // is not propagated and it only does not include the NSEC
+ if (!this->is_mock_) {
+ return; // We don't make the real DB to throw
+ }
+ EXPECT_NO_THROW(doFindTest(*finder,
+ isc::dns::Name("here.wild.example.org."),
+ isc::dns::RRType::TXT(),
+ isc::dns::RRType::NSEC(),
+ this->rrttl_, ZoneFinder::NXRRSET,
+ this->empty_rdatas_, this->empty_rdatas_,
+ Name::ROOT_NAME(), ZoneFinder::FIND_DNSSEC));
}
TYPED_TEST(DatabaseClientTest, getOrigin) {
- DataSourceClient::FindResult zone(this->client_->findZone(this->zname_));
+ DataSourceClient::FindResult
+ zone(this->client_->findZone(Name("example.org")));
ASSERT_EQ(result::SUCCESS, zone.code);
shared_ptr<DatabaseClient::Finder> finder(
dynamic_pointer_cast<DatabaseClient::Finder>(zone.zone_finder));
@@ -2142,4 +2345,66 @@ TYPED_TEST(DatabaseClientTest, compoundUpdate) {
ZoneFinder::SUCCESS, this->expected_rdatas_,
this->empty_rdatas_);
}
+
+TYPED_TEST(DatabaseClientTest, previous) {
+ shared_ptr<DatabaseClient::Finder> finder(this->getFinder());
+
+ EXPECT_EQ(Name("www.example.org."),
+ finder->findPreviousName(Name("www2.example.org.")));
+ // Check a name that doesn't exist there
+ EXPECT_EQ(Name("www.example.org."),
+ finder->findPreviousName(Name("www1.example.org.")));
+ if (this->is_mock_) { // We can't really force the DB to throw
+ // Check it doesn't crash or anything if the underlying DB throws
+ DataSourceClient::FindResult
+ zone(this->client_->findZone(Name("bad.example.org")));
+ finder =
+ dynamic_pointer_cast<DatabaseClient::Finder>(zone.zone_finder);
+
+ EXPECT_THROW(finder->findPreviousName(Name("bad.example.org")),
+ isc::NotImplemented);
+ } else {
+ // No need to test this on mock one, because we test only that
+ // the exception gets through
+
+ // A name before the origin
+ EXPECT_THROW(finder->findPreviousName(Name("example.com")),
+ isc::NotImplemented);
+ }
+}
+
+TYPED_TEST(DatabaseClientTest, invalidRdata) {
+ shared_ptr<DatabaseClient::Finder> finder(this->getFinder());
+
+ EXPECT_THROW(finder->find(Name("invalidrdata.example.org."), RRType::A()),
+ DataSourceError);
+ EXPECT_THROW(finder->find(Name("invalidrdata2.example.org."), RRType::A()),
+ DataSourceError);
+}
+
+TEST_F(MockDatabaseClientTest, missingNSEC) {
+ shared_ptr<DatabaseClient::Finder> finder(this->getFinder());
+
+ /*
+ * FIXME: For now, we can't really distinguish this bogus input
+ * from not-signed zone so we can't throw. But once we can,
+ * enable the original test.
+ */
+#if 0
+ EXPECT_THROW(finder->find(Name("badnsec2.example.org."), RRType::A(), NULL,
+ ZoneFinder::FIND_DNSSEC),
+ DataSourceError);
+#endif
+ doFindTest(*finder, Name("badnsec2.example.org."), RRType::A(),
+ RRType::A(), this->rrttl_, ZoneFinder::NXDOMAIN,
+ this->expected_rdatas_, this->expected_sig_rdatas_);
+}
+
+TEST_F(MockDatabaseClientTest, badName) {
+ shared_ptr<DatabaseClient::Finder> finder(this->getFinder());
+
+ EXPECT_THROW(finder->findPreviousName(Name("brokenname.example.org.")),
+ DataSourceError);
+}
+
}
diff --git a/src/lib/datasrc/tests/memory_datasrc_unittest.cc b/src/lib/datasrc/tests/memory_datasrc_unittest.cc
index a926935..2b854db 100644
--- a/src/lib/datasrc/tests/memory_datasrc_unittest.cc
+++ b/src/lib/datasrc/tests/memory_datasrc_unittest.cc
@@ -395,6 +395,14 @@ public:
};
/**
+ * \brief Check that findPreviousName throws as it should now.
+ */
+TEST_F(InMemoryZoneFinderTest, findPreviousName) {
+ EXPECT_THROW(zone_finder_.findPreviousName(Name("www.example.org")),
+ isc::NotImplemented);
+}
+
+/**
* \brief Test InMemoryZoneFinder::InMemoryZoneFinder constructor.
*
* Takes the created zone finder and checks its properties they are the same
diff --git a/src/lib/datasrc/tests/sqlite3_accessor_unittest.cc b/src/lib/datasrc/tests/sqlite3_accessor_unittest.cc
index 8b423f8..3974977 100644
--- a/src/lib/datasrc/tests/sqlite3_accessor_unittest.cc
+++ b/src/lib/datasrc/tests/sqlite3_accessor_unittest.cc
@@ -351,6 +351,45 @@ TEST_F(SQLite3AccessorTest, getRecords) {
EXPECT_FALSE(context->getNext(columns));
}
+TEST_F(SQLite3AccessorTest, findPrevious) {
+ EXPECT_EQ("dns01.example.com.",
+ accessor->findPreviousName(1, "com.example.dns02."));
+ // A name that doesn't exist
+ EXPECT_EQ("dns01.example.com.",
+ accessor->findPreviousName(1, "com.example.dns01x."));
+ // Largest name
+ EXPECT_EQ("www.example.com.",
+ accessor->findPreviousName(1, "com.example.wwww"));
+ // Out of zone after the last name
+ EXPECT_EQ("www.example.com.",
+ accessor->findPreviousName(1, "org.example."));
+ // Case insensitive?
+ EXPECT_EQ("dns01.example.com.",
+ accessor->findPreviousName(1, "com.exaMple.DNS02."));
+ // A name that doesn't exist
+ EXPECT_EQ("dns01.example.com.",
+ accessor->findPreviousName(1, "com.exaMple.DNS01X."));
+ // The DB contains foo.bar.example.com., which would be in between
+ // these two names. However, that one does not have an NSEC record,
+ // which is how this database recognizes glue data, so it should
+ // be skipped.
+ EXPECT_EQ("example.com.",
+ accessor->findPreviousName(1, "com.example.cname-ext."));
+ // Throw when we are before the origin
+ EXPECT_THROW(accessor->findPreviousName(1, "com.example."),
+ isc::NotImplemented);
+ EXPECT_THROW(accessor->findPreviousName(1, "a.example."),
+ isc::NotImplemented);
+}
+
+TEST_F(SQLite3AccessorTest, findPreviousNoData) {
+ // This one doesn't hold any NSEC records, so it shouldn't work
+ // The underlying DB/data don't support DNSSEC, so it's not implemented
+ // (does it make sense? Or different exception here?)
+ EXPECT_THROW(accessor->findPreviousName(3, "com.example.sql2.www."),
+ isc::NotImplemented);
+}
+
// Test fixture for creating a db that automatically deletes it before start,
// and when done
class SQLite3Create : public ::testing::Test {
diff --git a/src/lib/datasrc/zone.h b/src/lib/datasrc/zone.h
index bb4f435..c83b14b 100644
--- a/src/lib/datasrc/zone.h
+++ b/src/lib/datasrc/zone.h
@@ -54,13 +54,50 @@ public:
///
/// Note: the codes are tentative. We may need more, or we may find
/// some of them unnecessary as we implement more details.
+ ///
+ /// Some are synonyms of others in terms of RCODE returned to user.
+ /// But they help the logic to decide if it should ask for a NSEC
+ /// that covers something or not (for example, in case of NXRRSET,
+ /// the directly returned NSEC is sufficient, but with wildcard one,
+ /// we need to add one proving there's no exact match and this is
+ /// actually the best wildcard we have). Data sources that don't
+ /// support DNSSEC don't need to distinguish them.
+ ///
+ /// In case of NXRRSET related results, the returned NSEC record
+ /// belongs to the domain which would provide the result if it
+ /// contained the correct type (in case of NXRRSET, it is the queried
+ /// domain, in case of WILDCARD_NXRRSET, it is the wildcard domain
+ /// that matched the query name). In case of an empty nonterminal,
+ /// an NSEC is provided for the interval where the empty nonterminal
+ /// lives. The end of the interval is the subdomain causing existence
+ /// of the empty nonterminal (if there's sub.x.example.com, and no record
+ /// in x.example.com, then x.example.com exists implicitly - is the empty
+ /// nonterminal and sub.x.example.com is the subdomain causing it).
+ ///
+ /// Examples: if zone "example.com" has the following record:
+ /// \code
+ /// a.b.example.com. NSEC c.example.com.
+ /// \endcode
+ /// a call to \c find() for "b.example.com." will result in NXRRSET,
+ /// and if the FIND_DNSSEC option is set this NSEC will be returned.
+ /// Likewise, if zone "example.org" has the following record,
+ /// \code
+ /// x.*.example.org. NSEC a.example.org.
+ /// \endcode
+ /// a call to \c find() for "y.example.org" will result in
+ /// WILDCARD_NXRRSET (*.example.org is an empty nonterminal wildcard node),
+ /// and if the FIND_DNSSEC option is set this NSEC will be returned.
+ ///
+ /// In case of NXDOMAIN, the returned NSEC covers the queried domain.
enum Result {
SUCCESS, ///< An exact match is found.
DELEGATION, ///< The search encounters a zone cut.
NXDOMAIN, ///< There is no domain name that matches the search name
NXRRSET, ///< There is a matching name but no RRset of the search type
CNAME, ///< The search encounters and returns a CNAME RR
- DNAME ///< The search encounters and returns a DNAME RR
+ DNAME, ///< The search encounters and returns a DNAME RR
+ WILDCARD, ///< Succes by wildcard match, for DNSSEC
+ WILDCARD_NXRRSET ///< NXRRSET on wildcard, for DNSSEC
};
/// A helper structure to represent the search result of \c find().
@@ -135,7 +172,7 @@ public:
//@}
///
- /// \name Search Method
+ /// \name Search Methods
///
//@{
/// Search the zone for a given pair of domain name and RR type.
@@ -167,8 +204,8 @@ public:
/// We should revisit the interface before we heavily rely on it.
///
/// The \c options parameter specifies customized behavior of the search.
- /// Their semantics is as follows:
- /// - \c GLUE_OK Allow search under a zone cut. By default the search
+ /// Their semantics is as follows (they are or bit-field):
+ /// - \c FIND_GLUE_OK Allow search under a zone cut. By default the search
/// will stop once it encounters a zone cut. If this option is specified
/// it remembers information about the highest zone cut and continues
/// the search until it finds an exact match for the given name or it
@@ -176,6 +213,9 @@ public:
/// RRsets for that name are searched just like the normal case;
/// otherwise, if the search has encountered a zone cut, \c DELEGATION
/// with the information of the highest zone cut will be returned.
+ /// - \c FIND_DNSSEC Request that DNSSEC data (like NSEC, RRSIGs) are
+ /// returned with the answer. It is allowed for the data source to
+ /// include them even when not requested.
///
/// A derived version of this method may involve internal resource
/// allocation, especially for constructing the resulting RRset, and may
@@ -195,6 +235,31 @@ public:
isc::dns::RRsetList* target = NULL,
const FindOptions options
= FIND_DEFAULT) = 0;
+
+ /// \brief Get previous name in the zone
+ ///
+ /// Gets the previous name in the DNSSEC order. This can be used
+ /// to find the correct NSEC records for proving nonexistence
+ /// of domains.
+ ///
+ /// The concrete implementation might throw anything it thinks appropriate,
+ /// however it is recommended to stick to the ones listed here. The user
+ /// of this method should be able to handle any exceptions.
+ ///
+ /// This method does not include under-zone-cut data (glue data).
+ ///
+ /// \param query The name for which one we look for a previous one. The
+ /// queried name doesn't have to exist in the zone.
+ /// \return The preceding name
+ ///
+ /// \throw NotImplemented in case the data source backend doesn't support
+ /// DNSSEC or there is no previous in the zone (NSEC records might be
+ /// missing in the DB, the queried name is less or equal to the apex).
+ /// \throw DataSourceError for low-level or internal datasource errors
+ /// (like broken connection to database, wrong data living there).
+ /// \throw std::bad_alloc For allocation errors.
+ virtual isc::dns::Name findPreviousName(const isc::dns::Name& query)
+ const = 0;
//@}
};
diff --git a/src/lib/dns/gen-rdatacode.py.in b/src/lib/dns/gen-rdatacode.py.in
index b3c8da2..f3cd5df 100755
--- a/src/lib/dns/gen-rdatacode.py.in
+++ b/src/lib/dns/gen-rdatacode.py.in
@@ -133,7 +133,15 @@ def import_definitions(classcode2txt, typecode2txt, typeandclass):
if classdir_mtime < getmtime('@srcdir@/rdata'):
classdir_mtime = getmtime('@srcdir@/rdata')
- for dir in list(os.listdir('@srcdir@/rdata')):
+ # Sort directories before iterating through them so that the directory
+ # list is processed in the same order on all systems. The resulting
+ # files should compile regardless of the order in which the components
+ # are included but... Having a fixed order for the directories should
+ # eliminate system-dependent problems. (Note that the drectory names
+ # in BIND 10 are ASCII, so the order should be locale-independent.)
+ dirlist = os.listdir('@srcdir@/rdata')
+ dirlist.sort()
+ for dir in dirlist:
classdir = '@srcdir@/rdata' + os.sep + dir
m = re_typecode.match(dir)
if os.path.isdir(classdir) and (m != None or dir == 'generic'):
@@ -145,7 +153,12 @@ def import_definitions(classcode2txt, typecode2txt, typeandclass):
class_code = m.group(2)
if not class_code in classcode2txt:
classcode2txt[class_code] = class_txt
- for file in list(os.listdir(classdir)):
+
+ # Same considerations as directories regarding sorted order
+ # also apply to files.
+ filelist = os.listdir(classdir)
+ filelist.sort()
+ for file in filelist:
file = classdir + os.sep + file
m = re_typecode.match(os.path.split(file)[1])
if m != None:
diff --git a/src/lib/dns/rdata/any_255/tsig_250.cc b/src/lib/dns/rdata/any_255/tsig_250.cc
index 04a4dc4..4eb72bc 100644
--- a/src/lib/dns/rdata/any_255/tsig_250.cc
+++ b/src/lib/dns/rdata/any_255/tsig_250.cc
@@ -23,6 +23,7 @@
#include <util/encode/base64.h>
#include <dns/messagerenderer.h>
+#include <dns/name.h>
#include <dns/rdata.h>
#include <dns/rdataclass.h>
#include <dns/tsigerror.h>
diff --git a/src/lib/dns/rdata/generic/afsdb_18.cc b/src/lib/dns/rdata/generic/afsdb_18.cc
index dd7fa5f..6afc4de 100644
--- a/src/lib/dns/rdata/generic/afsdb_18.cc
+++ b/src/lib/dns/rdata/generic/afsdb_18.cc
@@ -26,6 +26,7 @@
#include <boost/lexical_cast.hpp>
using namespace std;
+using namespace isc::util;
using namespace isc::util::str;
// BEGIN_ISC_NAMESPACE
diff --git a/src/lib/dns/rdata/generic/minfo_14.cc b/src/lib/dns/rdata/generic/minfo_14.cc
index 734fbc3..aa5272c 100644
--- a/src/lib/dns/rdata/generic/minfo_14.cc
+++ b/src/lib/dns/rdata/generic/minfo_14.cc
@@ -24,6 +24,7 @@
using namespace std;
using namespace isc::dns;
+using namespace isc::util;
// BEGIN_ISC_NAMESPACE
// BEGIN_RDATA_NAMESPACE
diff --git a/src/lib/dns/rdata/generic/rp_17.cc b/src/lib/dns/rdata/generic/rp_17.cc
index b8b2ba2..781b55d 100644
--- a/src/lib/dns/rdata/generic/rp_17.cc
+++ b/src/lib/dns/rdata/generic/rp_17.cc
@@ -24,6 +24,7 @@
using namespace std;
using namespace isc::dns;
+using namespace isc::util;
// BEGIN_ISC_NAMESPACE
// BEGIN_RDATA_NAMESPACE
diff --git a/src/lib/dns/rdata/template.cc b/src/lib/dns/rdata/template.cc
index d9f08ee..e85f82c 100644
--- a/src/lib/dns/rdata/template.cc
+++ b/src/lib/dns/rdata/template.cc
@@ -18,6 +18,7 @@
#include <dns/messagerenderer.h>
#include <dns/rdata.h>
#include <dns/rdataclass.h>
+#include <dns/rrtype.h>
using namespace std;
using namespace isc::util;
diff --git a/src/lib/python/isc/__init__.py b/src/lib/python/isc/__init__.py
index 8fcbf42..029f110 100644
--- a/src/lib/python/isc/__init__.py
+++ b/src/lib/python/isc/__init__.py
@@ -1,4 +1,7 @@
-import isc.datasrc
+# On some systems, it appears the dynamic linker gets
+# confused if the order is not right here
+# There is probably a solution for this, but for now:
+# order is important here!
import isc.cc
import isc.config
-#import isc.dns
+import isc.datasrc
diff --git a/src/lib/python/isc/datasrc/Makefile.am b/src/lib/python/isc/datasrc/Makefile.am
index e5f20c1..d8f6b08 100644
--- a/src/lib/python/isc/datasrc/Makefile.am
+++ b/src/lib/python/isc/datasrc/Makefile.am
@@ -25,7 +25,7 @@ datasrc_la_LDFLAGS += -module
datasrc_la_LIBADD = $(top_builddir)/src/lib/datasrc/libdatasrc.la
datasrc_la_LIBADD += $(top_builddir)/src/lib/dns/python/libpydnspp.la
datasrc_la_LIBADD += $(PYTHON_LIB)
-datasrc_la_LIBADD += $(SQLITE_LIBS)
+#datasrc_la_LIBADD += $(SQLITE_LIBS)
EXTRA_DIST = client_inc.cc
EXTRA_DIST += finder_inc.cc
diff --git a/src/lib/python/isc/datasrc/__init__.py b/src/lib/python/isc/datasrc/__init__.py
index 05911f7..53711db 100644
--- a/src/lib/python/isc/datasrc/__init__.py
+++ b/src/lib/python/isc/datasrc/__init__.py
@@ -1,5 +1,5 @@
-from isc.datasrc.master import *
-from isc.datasrc.sqlite3_ds import *
+import sys
+import os
for base in sys.path[:]:
datasrc_libdir = os.path.join(base, 'isc/datasrc/.libs')
@@ -7,3 +7,6 @@ for base in sys.path[:]:
sys.path.insert(0, datasrc_libdir)
from datasrc import *
+from isc.datasrc.sqlite3_ds import *
+from isc.datasrc.master import *
+
More information about the bind10-changes
mailing list