BIND 10 trac2361, updated. a82ee1d047fe786113491226f7e81a08ed58e8c4 [trac2361]Merge branch 'master' into trac2361
BIND 10 source code commits
bind10-changes at lists.isc.org
Thu Oct 18 15:18:45 UTC 2012
The branch, trac2361 has been updated
via a82ee1d047fe786113491226f7e81a08ed58e8c4 (commit)
via 7e1c4328789fc66d6fb7f4840112d89024d00eef (commit)
via 8580400e9333574d49de88aaf34dac87bd270020 (commit)
via 110a33e6c237d71d2efa42d27ae2434b35ddcaa3 (commit)
via 5080ddf460167c252502db23b97cdb69fce7f240 (commit)
via 6fca5f367bef49f3c3002ee6abb4c5c19ab971d9 (commit)
via 650655fd267d652ec1b0d5e57a261ab767ada447 (commit)
via c450d77e81322fb6a3c6753d721f5d3bb62fff71 (commit)
via 39faa67b7a8e9b3680792582bb65372ddf610b77 (commit)
via 2252c5776f5447f47e567262f8a5a05753fc6ce9 (commit)
via f559b99dcccd35703989fce985ff200cd664ccc0 (commit)
via c91151db16e215692830bf9b6211c22aae8619dc (commit)
via 7ffd1e0cad3bd125b490953dbea9f22c67e40fae (commit)
via 34701adba7f7390640c70ba769cdb0b20f01f82c (commit)
via 26313fafb0f28d8bb1e440c83a780cdb6fc6fda2 (commit)
via 42c638e07a45b69d902a51b35838ae21ac19d116 (commit)
via 5735b337b8e78502bd54cba748bb622b150f6667 (commit)
via b57f65e686286f211ef1b177f69a81fa5baf3021 (commit)
via 579b92381b604c84f70ae5fe6237ca5dc45a9302 (commit)
via 8d321e3ea9f85227296cfac94a9ce79c7db70fdf (commit)
via 83d00dc305a33e69e4bee379895697742a1eaab7 (commit)
via f8181bb72251fd3d5f33e157d1f2a2a75a58d765 (commit)
via 6adfc244341c07a70eb0984536390486649316ae (commit)
via 8c02f8a83f011816c3fb19f655a1e57edf0c1048 (commit)
via a439004524335feb296ee7477a5b450a2a7d7953 (commit)
via 410987bf3c9299053c07aee46591193c97af6ea4 (commit)
via 7f94f671e0da9c98cabcc38433852732294bee4a (commit)
via 453b58784344390cbc5375624e6e92a86b4ff3f1 (commit)
via 405d85c8a0042ba807a3a123611ff383c4081ee1 (commit)
via 671005eea2c18ad34bb0bad435b538447cdc7a50 (commit)
via b6d2a03ff5dfc0b1269e69e44defb2999e8a5d33 (commit)
via d504c06cbe387363d11ae091eeaa980444cc5190 (commit)
via 50500e62ac0b43e3579e98832b06bde19d01302c (commit)
via 66524e1594263bb19fd73660fef31f4e49dbe81d (commit)
via 6920774dd67be1221c379e9d5161f0cc932fbd73 (commit)
via 3a73fff8f44f18188c1f810f3f5395e6ea107a7d (commit)
via 9565a7939e66bb0f3bbb4f3be8778dec13582db1 (commit)
via 9c2e94bb8ae9368aac3e0991dbebe3738ee914d7 (commit)
via efded5c3713a67a6eaa0284a322195b2f5075a27 (commit)
via 5b781744ac6f909958754f7e5947115ec799ac18 (commit)
via 4ed683ce42d061905a4e5b06fcc1a38c64136e7a (commit)
via e6ac5ef47d9885e20e247697c8c3d1fe55fd2500 (commit)
via 48e80a5c7505df708999ef817911d1eb505deaad (commit)
via fff8929b13d8c2cd725b861c7e930c0060b54d83 (commit)
via ecef170dd0af4f282d4426ef663b667c76873737 (commit)
via 350c775f7bd5c7777f0345754dac4ecaa0a9af18 (commit)
via 666bfa9c7b6de4ee31e210d7f3e0f2f68704f2de (commit)
via 932edbb0ec8a4dbb820c6cccf4a0becd9df533c2 (commit)
via 0e2c14089b820cbb0bdef6e813042992896375bc (commit)
via 45f0d1f2e771efaf7ca21e7f0450626847e75d90 (commit)
via 31d092681cc44a6d742474090430c7ddba3f81e6 (commit)
via 3744e7a8584a1c51bc5163751975af8e297c53c5 (commit)
via d3983e1e68f66bbf6c69838aebe01a8f95b7bc33 (commit)
via ae0565a4ed6e8a947120a7bfe826c1cf9d672760 (commit)
via e93e6f3873ca08ae835dfd883c36f19b42df247f (commit)
via 95a47e7f0ac9e87d3d0c6c51c07e4761d0a50932 (commit)
via 26483ea27726d4c1097485196a0908c565478188 (commit)
via 0e1a1b2ee0fc9a3a510d348d5449a825779c03b2 (commit)
via e2b120a2e016f249eabaf7753b97a79ece0bffa1 (commit)
via 1e6d2102d1402cfcd6d2c1ecd88c6aa83133daa2 (commit)
via 8b59a7d661ce81cb47c2f18d2908a01a8ab9ded0 (commit)
via f17651217197a8006509ef121f1a3d7a5eada1b4 (commit)
via e77ef46d0c78265ab0bd0ca12b541aaee2a52898 (commit)
via 97a6125c538e5f44790b55ded90fccf363416496 (commit)
via 2150123bd60f00e070198b9c9717eb685dd0eebe (commit)
via 6bceaa250bef898410db1555341887b6a6f7e900 (commit)
via 65c54c90ed874825b0418acade6179bd905bd470 (commit)
via c1ce5449aa72d538cbfca94f88cda0b8617081ba (commit)
via 2768eb3551fdaa2c4728563535dfb69f732dc033 (commit)
via 9c655172a298766a37cc83173438f70fff83264c (commit)
via 1e0c106c45251a61579d37a5148e9f258b3e5cea (commit)
via 9e11459509c9223201dcbe10636b15ba923e1516 (commit)
via 9dac9afb2569288bb630b00d2a89a490ad944710 (commit)
via c9de286421049dc1a75a981c511506d16c014501 (commit)
via 92f2cc51478005befb016e410a4fcb9c768ea3b8 (commit)
via f97ecbe97b6b3e098ebde2c984041dd24ce2dbbe (commit)
via e78384f8c6aca75a41ea000d22ecc7648513ad02 (commit)
via bc03b9cc89d6c3cad7308b884679125ab547b5cb (commit)
via 55be177fc4f7537143ab6ef5a728bd44bdf9d783 (commit)
via 16823263f5bc357de97f8b5d72f7b4d046583d23 (commit)
via ecdf1aeb4bfd97f636cc081cce30cc3394b38d11 (commit)
via b6501267d7058d8ed879eb4ce63f3d502ccebcc1 (commit)
via 47cfab4fa97829309b1c6093d65c27a142b1c87a (commit)
via bd7ef43a90c7e6f3a9b7140d0cacb2459dcd4091 (commit)
via aea67d494996a4b86a1671591e4cfa0ed1cbee77 (commit)
via 52ba8a9314e28f0f2f39510e66c2dc03531c47e1 (commit)
via ed72f2bf21f13a214106d222f6537a43f6f3318d (commit)
via ff2fabced6d1b9e88a2c6c2da66573ad96ae4e00 (commit)
via dda8b3fa72979fd24d5c3bdca6709408b5bafedb (commit)
via 35bdd068cf6192eba30d94d6a7a73751be4220ff (commit)
via 1645116db47739bc194d10db67a3a085b1ac551d (commit)
via 6207e0ccf7924b0976a21ffb7f901d946a273c41 (commit)
via e3079eb5ceeeca83507f6dfbf712b374661c3935 (commit)
via 5097c34950518370eabcade6cb71ba952bfeefd4 (commit)
via 9f77979fd649eaa9ec1cc3e9ce5b80059e2df3a5 (commit)
via a969883cd42fef3c3782fea117c156b76014df25 (commit)
via 214377d8511c7f8fdb8b8c8052bcb301aad1d06f (commit)
via c76f74659b3bc90b28f4af8940bd47193b92a28a (commit)
via 64bcd98477cfc62851a21aa64ced28d0cff92201 (commit)
via 0fc7d94b29ecc8a267d32047636d6525a3133612 (commit)
via 5703d87a9e031873305da04b88fb49ac5172c3c1 (commit)
via 4d27a4533c2cbdcb3c9e74c12d03024733e79895 (commit)
via a6cafe644f6a9413ba7f917e741b82cc292d3577 (commit)
via 52272e7986c23da3a0b6c4897f94245bfb408420 (commit)
via ed2aceb01d516724a4f289343c4ad1c65b4322b4 (commit)
via b15be74974e7e1d4f9acc0590e8853a064e3b6bf (commit)
via d7e42bb0c48c961c320ba1bf4324c49b9c43c19a (commit)
via 919fe74ccbeecf5d546b60f017c3e9e1b073655a (commit)
via f44918fd6cd6c3357609c82865a1e6c4a0e4d44a (commit)
via 44b1d42219a1787165a01497c21fec2661ba8277 (commit)
via 3efb1402e7d62755971c8e1440d31f85f14053e3 (commit)
via a3953a556b472084d669dcb629fb801dec67c5dd (commit)
via ea4149ff70f0a90ed995a04e41e4fa06664278c4 (commit)
via 7b2aa97d01af2934b408caa6044cbfab4c6679f6 (commit)
via e84770a80501161a4de8ca0fd9d821b0ce7c7da9 (commit)
via fa5076be0c7037c239c00fb7c2c53d02bdc87260 (commit)
via b31ee9e4712a22bd04637aa3222863ab0eb3c2b1 (commit)
via 45ae6bad11071f2b416c267bf2e71f2208487dda (commit)
via a270e0425f64f0fca563b7d49708325250911b96 (commit)
via 0a23277c5bcbd35467652610f7feb2b7f7c65a0b (commit)
via bb347f194134acc2a3d1e7f2c6d6b4f0d56d2a17 (commit)
via 40083686017562d13d812240499a8090383bfa11 (commit)
via a841ad0aec2fa5d0966dc43b3036ff99b5f3ae42 (commit)
via a25609c439090aeb21c01b487a2070b4df8d435b (commit)
via b2eedd0aebf2739acfb5e83363667fd2c1101f7d (commit)
via fa2026935d4738b6b5b7365d2fab53f9dea09cc4 (commit)
via dd752b34220a5b6a07a4d2c057169c6b88394c32 (commit)
via b675d21e2e7b1864b32824c97e180abf47c1f42e (commit)
via fe0e9a269f318aae152fb4b55437b45609bfd6cc (commit)
via 666c34d9e2c70936c264bd0432f874a95af42a1a (commit)
via 4686e5da4ecc7801a232a1858bf11f124aca3ee7 (commit)
via 4e5ca3a84b4c02ffef3bf8b6c6360b05c8600a4e (commit)
via cdbc11cf13f6cc69498b56a23c61748ed67f3631 (commit)
via 0dca4820d8473e8e1db07b49ef54d52d250e9209 (commit)
via 0edff937af7a4fcb6565d2b7ad14e0484b3905b1 (commit)
via ecb43490182626c4e62cded9d02ac9a94feb4cfa (commit)
via 83cdd8bb5d8cb846bfc85e65b33fc57fa56939b7 (commit)
via dc506d0714924ad48c3cd6a93c181ddd663f0059 (commit)
via b2cb1e1e337281fe923f1065fa19eac896d63b15 (commit)
via 4da98b699e5d73acbf7f0a01799b3a006aec4832 (commit)
via 6f008e19162fe3ae3911db0f770d6d38612bcba0 (commit)
via e0f618ac8f36e7fa8dbeab6164d40fd838178831 (commit)
via bb21aa5022e544357f9faa327278680acf5f16e0 (commit)
via f38d24a77f7a146b6a37f912c98af268df40f5e7 (commit)
via da22b37709978a87d276b1d88a92b401ae9a2084 (commit)
via 0e9c2a4c688d85c32e671a1564dd65818b1a6043 (commit)
via f7c0410f7d2f1c3a06f886b548713bae6e78e9fb (commit)
via 13089ae122acefb8d2a632174da4e4dd53ce4473 (commit)
via dd83505acac98d122777883a304465f69ecd03ec (commit)
via 06c667cd025d42798dbce055a24ada9e601f8c35 (commit)
via c66fe1c8ad169c7bd0bc15c0cbe2357194acfaa4 (commit)
via 1eb3ce6df757a23ff59b71469e0f19329893d8c7 (commit)
via 9334389794384c62c517d646608fb23a1abeb40d (commit)
via 3d98322d24b54475ce801880a5d17a27dc8f57ef (commit)
via 1c4ec91a6f44300af78f4964496cf000c97e93f8 (commit)
via b41cca56e918501b7a84cfe31067f786ed1d065e (commit)
via c3a2105cbd7516aada1b954184b699e1ae8200ee (commit)
via 347e333b52c7589ad5bf4fb9cc6f4ff6474b510d (commit)
via fac39dc29a4f858bfe6d1c48f76737926a043f32 (commit)
via adb92afc6f7d0ceda51780577ebe4c2487745cad (commit)
via c28be089b929185c72aba5fe90c5f8d355c833ac (commit)
via 998645aaeb5a7369ae94e71f84d40512ffee996b (commit)
via 55d94bb5f735252542908e9565d7a49c9a6e0966 (commit)
via 3e2a372012e633d017a97029d13894e743199741 (commit)
via 5cc254b16bd0120a7bacd33f90936333fbecfd79 (commit)
via b05952bb39bc7f038e65c5e61aca711278096dee (commit)
via b770f510961784b1cbf766a1b40fb026788d8cb0 (commit)
via 9ef297b7faf705148c7aed37df6c796e55660062 (commit)
via a017390f15c132fdb08f244f2e111ae6964e4cc4 (commit)
via 6fc2942362354dd4960a25d644095f6eb44b6023 (commit)
via 1426bb5c63cd7d79a4a16aedf619aff5ea2394aa (commit)
via 73f22adb3400726277f06f9d1f8760e43bcc133f (commit)
via 030aef19982a37a8b81376adb15864097b208ff8 (commit)
via eb84acab80c1a4f59f7c49ef5214279358e8ed79 (commit)
via 0375b6599cabcefe4e9cf1b326f54b3519bde8b3 (commit)
via 50f42396efa039096dd74c13425d1a2bc0efc978 (commit)
via 64bba5b1c6a4155e8179217c10e998fbe3c2822c (commit)
via 31fdd062b59d6ac480ff48cde57e5abad2b3487c (commit)
via 7ad6e5687c4835d70422ee09d1bfdb032979060a (commit)
via a78e560343b41f0f692c7903c938b2b2b24bf56b (commit)
via de5fd0b12800235b15ee02cbed8e862ea6c974ad (commit)
via 028bed9014b15facf1a29d3d4a822c9d14fc6411 (commit)
via 4784ca566b009035404a5abf1759c2eedf5a7515 (commit)
via a6c74caf3dc0aef52b89c78ebbcd02fcc5e47609 (commit)
via f6616492db24a4831ca25258c4bc234e5bd148bb (commit)
via df196f7609757253c4f2f918cd91012bb3af1163 (commit)
via 5fadea676fc73e068977173bb62b58d04fedf52c (commit)
via 532ac3d0054f6a11b91ee369964f3a84dabc6040 (commit)
via ca61588dc2c1ad17200416ac7f07ac272adc8903 (commit)
via dc5492b8401c85daf1b0c8801e497bac3d5eeea5 (commit)
via 6962458ec49390d007dbf0fa9d5d3befa53deb23 (commit)
via 28b3efcf6100d2eab66c407a8f7ec54a87ac5f87 (commit)
via 43959b29a67ba17054bd4bb70b6fe3b8be3b2f2a (commit)
via c0d50a204134b57f36a9e83cc694abcb5924f357 (commit)
via bc7551cca83bf060410a5645f014ddb0eb233834 (commit)
via 8722bc208f5a232707824f3537fe0f6d073151f1 (commit)
via 1adba86b37b9c11a8120e31cc4eeb137f8cde6d9 (commit)
via e2d1490eb82b26c501df4d0ed7323478b46a1cca (commit)
via 73aebe53f210471b768d79b1f24ee154c56c8554 (commit)
via f2b4e736bb7d4612c33aa2a648b0b9640a015154 (commit)
via 88db890d8d1c64de49be87f03c24a2021bcf63da (commit)
via 65cddb545cbf31c0372ab09e59a727d7cfdf1f89 (commit)
via 99fd73694c6768e36d79563be991d9132adbdd74 (commit)
via b10b39f8ac7b07f6b429c0e09fcd9d0a3a873492 (commit)
via a1983f43686919fae5dfb05ad8e6da07e5534111 (commit)
via 56dd56426342f9fa3d63c436f35ba86208512ee0 (commit)
via b6cfd9d20c49a0f292d37e663801987632e9d2a6 (commit)
via a0b7dd977eba590ecd94d4f08ed774a0727fbed0 (commit)
via 7565788d06f216ab254008ffdfae16678bcd00e5 (commit)
via 183ea20803b1dfc0bb6e932c75623864cd0a0def (commit)
via d042cd04c0784641738a0f06e5a6b193b433d39f (commit)
via 55178c917f014922f8213921a44f0443b3dc40f8 (commit)
via 7bb6309fbc34c32a0e37ea2b2175738b6110bcf7 (commit)
via fd45bcdb5182e09d33b92e222772b54c6e744d01 (commit)
via 08d6f75709a7669481b92fc0b2e4176174bc0f48 (commit)
via e4f9ff3f23810d365e13ee2e204f70eac5ffdc8f (commit)
via fe827c7e47a642f3aead3b5414c6e8cf3a9970b4 (commit)
via c6391ad769265a1f5c2d5594dcdb113420c7eb4d (commit)
via 28a6c7133b1fa8f5334f91c514f5dc8e0802e4eb (commit)
via ed2b8eb0e3f827c89ba16200312424384a54a9a7 (commit)
via 2975fbae44dbaacdff7b9039a395b00c4375f01b (commit)
via 593b348cbb8207fca3e9d01620794cfe118455b2 (commit)
via c16c1ab29b3a52012a806974dd6afaddc01c9436 (commit)
via 2eb96fb05e736e2f16d03174f663d030cc883f00 (commit)
via c6f7cced73d38d5108862be4a104cb89d0f832e4 (commit)
via c0d25c429a4f2026bb97b9e013730b8f265862e6 (commit)
via e71cd13c5cb261635f7115e0d30fbd7dcc5fe151 (commit)
via 86e4008ac1623aef96fd0c6140e5496841c3b9f0 (commit)
via f496bbe69fbff9228c48fc7bf9cc986ed7f420e4 (commit)
via 74911480e9ff1f21f63365ac789ecd30f67891b8 (commit)
via 66424553f3fa05e46e558f3b4e16677463e0e412 (commit)
via e562ac798865d18ece6a7ddd28ed79d8b4abe0ae (commit)
via d12ce3c245fc86e3c34a5fdd5950d899cdf889af (commit)
via 1517f32c9e68baacfc68c2aab412b29045d846b9 (commit)
via 5e39872cc6dc260c91596ee31f1601dbf53de503 (commit)
via ebfe8a5ab083e3ec1ead648709bfba801fb617a5 (commit)
via d78dd497ecb846ab2545ce1cb7b6e41341ff5995 (commit)
via 3a6ee0d12d06c1f9e5b9f261b3fcaab444a9ae80 (commit)
via 7888a2127db48e004cb1cce24552db230cb3d685 (commit)
via bcf2117983fa80d3f4bb9a13f896963497553b85 (commit)
via fb5aa58dabb47c1ef51890bee7da070c6a567f69 (commit)
via 5879860e006e809fa75fdb78b61f82fdffd69b97 (commit)
via 1e93f1db983cc35b118853b88ab2919decafe2c5 (commit)
via 3a68ea2e8de8d6df97d95c077ff9e2874ca0d9c3 (commit)
via 4e159ffca0b633c817472101087be4a235a42af0 (commit)
via 7b053fd99e6503d3a0a8fb7dbb9d3139df942565 (commit)
via 04db154ec2fdd1d768fe716b0ccabaa1660e872b (commit)
via c7ab107ab6a5f079475085e87851d940863481dc (commit)
via dd191127774034a51d658dfe6799ccb397916103 (commit)
via 4973e638d354d8b56dcadf71123ef23c15662021 (commit)
via d41d8af0c726bb923a48948feb6a8f4f6775ea3e (commit)
via 869d14d7056ee7b0c3cf1dd0db1b375be755bba7 (commit)
via 9a9260c2589a8468b73f2ecc16929acd66aeb81c (commit)
via daedcaea541b1193018cb71d3cc68ba3ba9ff59c (commit)
via 432064bc3ec7c67797898a182c52add8f6012232 (commit)
via 1ab996e4c54f1449b8e72cce411331193868e78c (commit)
via bf260bbc80a813c30bb7499b8a085d7390d83e83 (commit)
via f8261d10f177e52b19af56343ddb5e7548f30195 (commit)
via 7bcaf2b7903ea5a51c2049579f6dc4670746527f (commit)
via f5bd1368b73131abd6e0562e57482310d26ff3eb (commit)
via c629dec43601cd76158bef6aa8b76cea84181fc2 (commit)
via e4bd572168b357f9803ce9c5d96ff3a95d935b81 (commit)
via b211a060119605ebbded0a94f070009b7a8bb5ce (commit)
via d62a1e4250c88e765b3cd7f77fe9a948db0a565d (commit)
via 9ea5133b172ccde43427f9658f5be2ba5d497b34 (commit)
via daf81c8846bf0b4e4308068a019c96beb09c5199 (commit)
via 23da2f078af486c381649611d2987c24a538739a (commit)
via 587a13699275e9a582fb9358463047362ef9f21e (commit)
via ec2aecacc46f071cc6479881418825920e8f8f50 (commit)
via 83a27d452c13b468d2103b0314ecf2bae17cd6b1 (commit)
via ece07281abf99ae142b20b4aead428e3333121a9 (commit)
via d05029362843411c46c6d8d278fcf17ba9d8d32a (commit)
via ef49adadf64f56558c75d34483895e1e8d8bcf66 (commit)
via 00d3de1af86efe6b4df1310a039adad8f6a719ae (commit)
via 73900f62a74bf769b5f6ea7b3cf43428baa19842 (commit)
via 7a628baa1a158b5837d6f383e10b30542d2ac59b (commit)
via 915576b6aa27d020faadf7fbfc4e4d9bb1df129e (commit)
via e1b4bf1cb50160c436f2e5440fd774680b91b7a2 (commit)
via 76243c3ccd7a30484da8478f7457e18fce485492 (commit)
via 86a4bae1ffa68eb38921d6cb3f16dad43b616009 (commit)
via 0b6e74f544b522943436bfdff79c2fa06273b49d (commit)
via d7846ea4a9d811c35641f77384d9825f12488e71 (commit)
via 5d61dba1dc3e51d66ba40d82b1bc378577783dc0 (commit)
via c286e0dec766c3d9b2fff6a8bd55539ec976aae9 (commit)
via d664fca786643e2345d9722da6d8fd35996bbea1 (commit)
via a0a5e207d4a05738f278df6124172f415a43d4ad (commit)
via b160122e462422d82fd122839f9fff1e861aa21d (commit)
via 44d9dfa8aad85d583b7b90fa01c50613a9d9d1a5 (commit)
via fbc8d38199a95cc51704c40269d0c4c6625ec6b0 (commit)
via 7a40926af1fd90e0fd685a8db05795f699560245 (commit)
via f8a03432904899e1874f2696ff036322be539022 (commit)
via 74cc637858d0879f33f1828aabbb1f78e9636d4f (commit)
via fa1d1161f76487ceea122baf4dac79c407a07ba8 (commit)
via 17bffd083824af840b8af99a043bcf55e4c0f555 (commit)
via 0ff7d113ebb54889bbcd9be91c37a0e5ffcc2fc4 (commit)
via 3584fbbffa6a633df2988ef8e52a4f367cdc814c (commit)
via bff3846230660040790debeacf8d9432ac302fb9 (commit)
via a1d9278fd64f74a4a0657a2292851157ad3bb43a (commit)
via 93e596c474ed1c637da7d6653eaa8c499b0b6a6d (commit)
via f037ebb23cb966f86d11c07bb0f4a80b6043c9b0 (commit)
via a475756bf185c9e364ed00e245e307789e22fed3 (commit)
via 27be672609dde261c85ffc671f1026277cfc3e9a (commit)
via 18cff787ef721f58322db5c5662ab7980357fe68 (commit)
via 34020849e9b1d560fdc21b25ac0fe03c64f71a01 (commit)
via ae53c8fd697e2cd037facfccc8205d580b27f04e (commit)
via 97fb03f6b015205da25e9c5be3cd90d4f9948d04 (commit)
via 23d2eaf5f8e5febc4732f3fdc851e1b04914daf8 (commit)
via 3a8ea5fd501d131e99ec02c4be10a874a94f2d83 (commit)
via 46c03d26e27417bdce132b90eef18686443bd5a7 (commit)
via 86b1a2387a10ff8e5e646b117c289ebb2e9e57ec (commit)
via 1d3b7eabbb5fa3b6674789428b36d7501768b635 (commit)
via 41538b7ce9d6ab4131b572b79d84292884cf4a11 (commit)
via 78dfd7dcd6d76cb75d288a3887e8b099c12f3080 (commit)
via b1d4ff896a7781d98513fab8d171090ac3d6a1e4 (commit)
via 1137e979e1a22465feb03ee0a48499bebcdf6aae (commit)
via 6b99a39f0ab27939c29a05b0332d5ec255bc0764 (commit)
via 12bc8985691e22aad1811322fe1f550b3f710ab8 (commit)
via bf382bba28e32de82649284620e36749a183472d (commit)
via 47c013b000db7235e1ab798eadebc3b42984b06e (commit)
via 247cb9c1d6ca73bb212cb9dfe0aa85dcf342e924 (commit)
via c91bffdd00deb8b7f3ceda9954a84e31c0cfb9f1 (commit)
via 0d4a6b6be1e6ba8b3b5681b5c3be7c86d56c076f (commit)
via 686eb4e9edb4adee0cee357762fe9448fb0cb6bd (commit)
via 25cd191443ffd0cffc036c53a0ff57ad2391cead (commit)
via 3d68f767731a682d4a80e9ba74d953502c2ada7d (commit)
via e01716291c402d11710387fa6a9b2cfcd8e3953f (commit)
via f802291f6461f3fa7e13fcf3364bb39b4be42c06 (commit)
via fa51dffbe444e5d2ac6a768da001b60ad16d0341 (commit)
via 9cfcbb9475e1f42b5c8953a5a037b833edc5aa69 (commit)
via 0c38d26f6acc7d156dc6c6871b4fa332e30728ee (commit)
via b89e44112425a8d5ab44c26ce988cfb08087ff7e (commit)
via d5a5e815fc96116ee205256b9cba7c11752f0692 (commit)
via 98692b1e0663b4d167670b7808f26b1bb718851c (commit)
via b3d273056fa988f289ae4bf3f0a677808613b97b (commit)
via b9250ecc03ef6854def286e388e9042fbd579971 (commit)
via 8de132995eade03f030a3a66c0f0bf4bc1b0726c (commit)
via 86fcb73a66e62bda72bf5d6aea7f1f852c7f5739 (commit)
via 61beda153425bfbd855c9bca9ff51fb0e99f2943 (commit)
via 038ba06b6e663343e93e63e0d77d3e3c1abf66bf (commit)
via b643e4ef3098a5671687031b2b1348ef60c780fa (commit)
via 1886c9f3083ce3e04d42b8a91e46838ee5a1e774 (commit)
via 371a652b90f43c0a2759b2be4d2fb6ef95d57ff0 (commit)
via 37fd8a72b131d5de9a6fbe612d3dc3d19bafdf83 (commit)
via 1fb24f540ade83926d311316e77e1a6c8bef8432 (commit)
via c4c3946bad5bf1e9be0f375ac1029e1f95e94d94 (commit)
via 5c975417200358c916a62415caf51a1a73e0d470 (commit)
via 538a160ef48f7dbe927f75cafc335e470a82db33 (commit)
via e45115237b2c8c12f0c2fd3c62ae0aff01248cc9 (commit)
via a072ee8db55261a67dc7041c7c17d0536f8def0b (commit)
via 473e340929d0869bf71d972e1a1843dc81b0fbfa (commit)
via 94656c03d42d0339c79d10be8ccedaea6da55028 (commit)
via a6748eee324ebb4cf4ba99aa225df51d71458c81 (commit)
via 5acf0ff36e8503de33ce04fe16e279ec6ee871f1 (commit)
via 14680316801f9274e5b4d099a4dfbf63d7cb110b (commit)
via d25bb8d27cf235603cb8a1d789066b2aa993eccd (commit)
via f2b62e24805e97f66aeac4f9ab3db2d9778c9314 (commit)
via e0ce6d2f3010bfc5998e6c7da202258cbe28c6c6 (commit)
via d73842ed35d5aea29466e25a9a6b2dfb4e550e97 (commit)
via 5c74085e2f4c16655e77e1bcbc308d4fb0a694f8 (commit)
via 4babe763de5b20437e32829024f25a1b902df82f (commit)
via 574a6f8834ffa1ef3faddc637257efcde4eae445 (commit)
via 2bed6220f561fa39358fb81fde8a2ee0a1b85c38 (commit)
via 274eddb40bacdf8d5933058e33b7d469cb9258c2 (commit)
via f00da76cb77226e6a6b7487f6c5c0bc62d0c3f48 (commit)
via 75ee32436fa5bae4a8f15e25e587d94bf41a59fd (commit)
via ef8ecac02aa21192fb4af8d573daa11b09b3685a (commit)
via 8f7c28971b48c4ee0ab89a3f88d84c4b6fed8185 (commit)
via 5b9ab4bc5c038d3fe6e12a0cf90c33e5f54d0f45 (commit)
via 43636d3f31d1684984eb5337b91a418dd1725fbe (commit)
via 938d8b3145666d6f7d7084668b0ee665b3458e4a (commit)
via 680279e6e9ac7a15d23b8a4d6d03a391166a7c8b (commit)
via 1ab6aafab6660d60a5f2e7cf7125d59d5807f2e5 (commit)
via a38fe354acb2ba6e641d91bec364a22989f30b16 (commit)
via 66f45b3d67dbcb714d445fb3511e19b83cbc8b05 (commit)
via f284e18628afb282c1f5c9feec74b36abd9352be (commit)
via 5bc38109a4c2775a2a2a8389023836facfe8400d (commit)
via 27e2b20fc709884a145df81553504bfc90b8d539 (commit)
via 805fda7d95ada6af81f17fca2816b3a1fe4a3008 (commit)
via 370861e432e88669b2382954b15dbecf1672e032 (commit)
via ff837bb1fc512920556a796c40cacc20407b165c (commit)
via 7e7171deec8d2a31147a9d5700f4315e2ea3e87b (commit)
via 4a6553ba36f68f141e7420b070ad6d621f9a8465 (commit)
via 8e51d2165fb35be71aee1a0c381fc71f08a821b5 (commit)
via 718888317b9fb6b24c20d8d417ea540ff23257c5 (commit)
via e2a552fcaa6fcc1f0d39defd990bd5a35a4c73a3 (commit)
via e3cc1803c6fb2aa3373528b6095eb9cf6e8b24d1 (commit)
via fb86b2f215ca1a08440c5c200f22af7747c9de2a (commit)
via 5ee024c32a3ce4afd33d0d5d4df59fcde90d739d (commit)
via 0b7e9646307a6e58d3e1df60c68cbac5dbcbfecb (commit)
via 01eb4277b933cb0537204d98b9200550ee3eb641 (commit)
via 5ff43d80fb8eaae5ac2621a5a754cf94204149de (commit)
via 2c7936f05e6e2055016cdf7f4c915a80f465cde5 (commit)
via a49d8187d7b89c1f40b520b4af324096eb69fa5d (commit)
from 7c40eaf7779f7fb3b80ad141c58f60e69a2f3e4e (commit)
Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.
- Log -----------------------------------------------------------------
commit a82ee1d047fe786113491226f7e81a08ed58e8c4
Merge: 7c40eaf 7e1c432
Author: Jeremy C. Reed <jreed at ISC.org>
Date: Thu Oct 18 09:42:44 2012 -0500
[trac2361]Merge branch 'master' into trac2361
-----------------------------------------------------------------------
Summary of changes:
ChangeLog | 81 ++
configure.ac | 204 ++---
doc/devel/02-dhcp.dox | 23 -
doc/devel/mainpage.dox | 5 +
doc/guide/bind10-guide.xml | 99 ++-
NEWS => examples/AUTHORS | 0
COPYING => examples/COPYING | 8 +-
TODO => examples/ChangeLog | 0
examples/INSTALL | 9 +
examples/Makefile.am | 4 +
NEWS => examples/NEWS | 0
examples/README | 32 +
examples/configure.ac | 28 +
{src/bin => examples}/host/.gitignore | 0
examples/host/Makefile.am | 6 +
{src/bin => examples}/host/README | 0
{src/bin => examples}/host/b10-host.xml | 0
{src/bin => examples}/host/host.cc | 0
examples/m4/ax_boost_include.m4 | 64 ++
examples/m4/ax_isc_bind10.m4 | 122 +++
src/bin/Makefile.am | 2 +-
src/bin/auth/Makefile.am | 2 +-
src/bin/auth/auth_config.h | 2 +-
src/bin/auth/auth_srv.cc | 71 +-
src/bin/auth/auth_srv.h | 69 +-
src/bin/auth/benchmarks/Makefile.am | 2 +-
src/bin/auth/benchmarks/query_bench.cc | 38 +-
src/bin/auth/command.cc | 7 +-
.../{datasrc_configurator.cc => datasrc_config.cc} | 11 +-
src/bin/auth/datasrc_config.h | 83 ++
src/bin/auth/datasrc_configurator.h | 120 ---
src/bin/auth/main.cc | 46 +-
src/bin/auth/tests/Makefile.am | 4 +-
src/bin/auth/tests/auth_srv_unittest.cc | 109 +--
src/bin/auth/tests/command_unittest.cc | 69 +-
...ator_unittest.cc => datasrc_config_unittest.cc} | 142 ++--
src/bin/auth/tests/query_unittest.cc | 17 +-
src/bin/bind10/bind10_messages.mes | 41 +-
src/bin/bind10/bind10_src.py.in | 61 +-
src/bin/bind10/tests/bind10_test.py.in | 71 +-
src/bin/dbutil/dbutil.py.in | 9 +-
src/bin/dbutil/tests/Makefile.am | 2 +
src/bin/dbutil/tests/dbutil_test.sh.in | 6 +-
src/bin/dbutil/tests/testdata/Makefile.am | 1 +
.../{empty_version.sqlite3 => v2_1.sqlite3} | Bin 13312 -> 15360 bytes
src/bin/dhcp4/tests/Makefile.am | 1 +
src/bin/dhcp6/Makefile.am | 2 +
src/bin/dhcp6/config_parser.cc | 797 ++++++++++++++++++++
src/bin/dhcp6/config_parser.h | 147 ++++
src/bin/dhcp6/ctrl_dhcp6_srv.cc | 30 +-
src/bin/dhcp6/dhcp6.dox | 79 ++
src/bin/dhcp6/dhcp6.spec | 92 ++-
src/bin/dhcp6/dhcp6_messages.mes | 20 +
src/bin/dhcp6/dhcp6_srv.cc | 7 +
src/bin/dhcp6/tests/Makefile.am | 4 +
src/bin/dhcp6/tests/config_parser_unittest.cc | 243 ++++++
src/bin/host/Makefile.am | 37 -
src/cppcheck-suppress.lst | 15 +
src/lib/acl/dns.cc | 23 +-
src/lib/cache/tests/rrset_entry_unittest.cc | 3 -
src/lib/cc/data.cc | 24 +-
src/lib/cc/data.h | 44 +-
src/lib/cc/tests/data_unittests.cc | 249 ++++--
src/lib/datasrc/database.cc | 16 +-
src/lib/datasrc/memory/Makefile.am | 7 +-
src/lib/datasrc/memory/domaintree.h | 72 +-
src/lib/datasrc/memory/memory_client.cc | 676 ++---------------
src/lib/datasrc/memory/memory_client.h | 86 +--
src/lib/datasrc/memory/zone_data_loader.cc | 250 ++++++
src/lib/datasrc/memory/zone_data_loader.h | 80 ++
src/lib/datasrc/memory/zone_data_updater.cc | 347 +++++++++
src/lib/datasrc/memory/zone_data_updater.h | 180 +++++
src/lib/datasrc/memory/zone_finder.cc | 20 +-
src/lib/datasrc/memory/zone_table.cc | 44 +-
src/lib/datasrc/memory/zone_table.h | 63 +-
.../memory/zone_table_segment.cc} | 44 +-
src/lib/datasrc/memory/zone_table_segment.h | 110 +++
.../memory/zone_table_segment_local.cc} | 47 +-
src/lib/datasrc/memory/zone_table_segment_local.h | 66 ++
src/lib/datasrc/memory_datasrc.cc | 12 +-
src/lib/datasrc/memory_datasrc_link.cc | 2 +-
src/lib/datasrc/sqlite3_accessor.cc | 134 ++--
src/lib/datasrc/tests/memory/Makefile.am | 2 +
.../datasrc/tests/memory/domaintree_unittest.cc | 31 +-
.../datasrc/tests/memory/memory_client_unittest.cc | 194 +++--
src/lib/datasrc/tests/memory/zone_data_unittest.cc | 2 +-
.../datasrc/tests/memory/zone_finder_unittest.cc | 276 ++-----
.../tests/memory/zone_table_segment_unittest.cc | 83 ++
.../datasrc/tests/memory/zone_table_unittest.cc | 115 +--
src/lib/datasrc/tests/memory_datasrc_unittest.cc | 1 -
src/lib/datasrc/tests/sqlite3_accessor_unittest.cc | 15 +-
src/lib/datasrc/tests/testdata/contexttest.zone | 3 +-
src/lib/datasrc/tests/testdata/example.org.sqlite3 | Bin 16384 -> 15360 bytes
src/lib/datasrc/zone.h | 130 +++-
src/lib/datasrc/zone_finder_context.cc | 17 +-
src/lib/dhcp/Makefile.am | 2 +
src/lib/dhcp/addr_utilities.cc | 105 ++-
src/lib/dhcp/cfgmgr.cc | 33 +
src/lib/dhcp/cfgmgr.h | 47 +-
src/lib/dhcp/duid.cc | 90 +++
src/lib/dhcp/duid.h | 98 +++
src/lib/dhcp/lease_mgr.cc | 68 ++
src/lib/dhcp/lease_mgr.h | 480 ++++++++++++
src/lib/dhcp/pool.cc | 33 +-
src/lib/dhcp/pool.h | 27 +
src/lib/dhcp/subnet.cc | 44 ++
src/lib/dhcp/subnet.h | 51 ++
src/lib/dhcp/tests/.gitignore | 1 +
src/lib/dhcp/tests/Makefile.am | 2 +
src/lib/dhcp/tests/addr_utilities_unittest.cc | 65 +-
src/lib/dhcp/tests/cfgmgr_unittest.cc | 36 +
src/lib/dhcp/tests/duid_unittest.cc | 169 +++++
src/lib/dhcp/tests/iface_mgr_unittest.cc | 2 +-
src/lib/dhcp/tests/lease_mgr_unittest.cc | 296 ++++++++
src/lib/dhcp/tests/pool_unittest.cc | 73 ++
src/lib/dhcp/tests/subnet_unittest.cc | 82 +-
src/lib/dhcp/triplet.h | 5 +
src/lib/dns/Makefile.am | 3 +-
src/lib/dns/benchmarks/message_renderer_bench.cc | 2 +-
src/lib/dns/benchmarks/rdatarender_bench.cc | 5 +-
src/lib/dns/python/edns_python.cc | 3 +-
src/lib/dns/rdata/generic/afsdb_18.cc | 1 -
src/lib/dns/rrsetlist.cc | 60 --
src/lib/dns/rrsetlist.h | 132 ----
src/lib/dns/tests/Makefile.am | 2 +-
src/lib/dns/tests/labelsequence_unittest.cc | 8 +-
src/lib/dns/tests/rrparamregistry_unittest.cc | 5 +-
src/lib/dns/tests/rrsetlist_unittest.cc | 188 -----
src/lib/log/compiler/message.cc | 10 +-
src/lib/log/logger.h | 10 +-
src/lib/log/tests/log_formatter_unittest.cc | 14 +-
src/lib/log/tests/logger_example.cc | 1 -
src/lib/log/tests/logger_unittest.cc | 21 +-
.../log/tests/message_initializer_2_unittest.cc | 11 +-
src/lib/python/isc/bind10/component.py | 36 +-
src/lib/python/isc/bind10/sockcreator.py | 5 +
src/lib/python/isc/bind10/tests/component_test.py | 65 +-
src/lib/python/isc/datasrc/sqlite3_ds.py | 3 +-
src/lib/resolve/recursive_query.cc | 85 ++-
src/lib/resolve/recursive_query.h | 45 +-
src/lib/resolve/tests/recursive_query_unittest.cc | 87 +--
.../resolve/tests/recursive_query_unittest_2.cc | 35 +-
.../resolve/tests/recursive_query_unittest_3.cc | 36 +-
src/lib/server_common/tests/portconfig_unittest.cc | 67 +-
.../server_common/tests/socket_requestor_test.cc | 4 +-
src/lib/testutils/dnsmessage_test.h | 116 +--
src/lib/util/Makefile.am | 3 +
src/lib/util/hash/sha1.cc | 4 +-
src/lib/util/io/fd_share.cc | 1 +
src/lib/util/locks.h | 8 +-
src/lib/util/tests/buffer_unittest.cc | 23 +-
src/lib/util/tests/fd_tests.cc | 4 +-
src/lib/util/tests/lru_list_unittest.cc | 6 +-
src/lib/util/threads/Makefile.am | 4 +-
src/lib/util/threads/lock.h | 128 ----
src/lib/util/threads/{lock.cc => sync.cc} | 89 ++-
src/lib/util/threads/sync.h | 223 ++++++
.../auth => lib/util/threads}/tests/.gitignore | 0
src/lib/util/threads/tests/Makefile.am | 5 +-
src/lib/util/threads/tests/condvar_unittest.cc | 162 ++++
src/lib/util/threads/tests/lock_unittest.cc | 29 +-
src/lib/util/threads/thread.cc | 6 +-
src/lib/util/unittests/Makefile.am | 1 +
.../unittests/{resource.cc => check_valgrind.cc} | 28 +-
src/lib/util/unittests/check_valgrind.h | 50 ++
tests/tools/perfdhcp/templates/.gitignore | 5 +
166 files changed, 7363 insertions(+), 2897 deletions(-)
copy NEWS => examples/AUTHORS (100%)
copy COPYING => examples/COPYING (68%)
rename TODO => examples/ChangeLog (100%)
create mode 100644 examples/INSTALL
create mode 100644 examples/Makefile.am
rename NEWS => examples/NEWS (100%)
create mode 100644 examples/README
create mode 100644 examples/configure.ac
rename {src/bin => examples}/host/.gitignore (100%)
create mode 100644 examples/host/Makefile.am
rename {src/bin => examples}/host/README (100%)
rename {src/bin => examples}/host/b10-host.xml (100%)
rename {src/bin => examples}/host/host.cc (100%)
create mode 100644 examples/m4/ax_boost_include.m4
create mode 100644 examples/m4/ax_isc_bind10.m4
rename src/bin/auth/{datasrc_configurator.cc => datasrc_config.cc} (80%)
create mode 100644 src/bin/auth/datasrc_config.h
delete mode 100644 src/bin/auth/datasrc_configurator.h
rename src/bin/auth/tests/{datasrc_configurator_unittest.cc => datasrc_config_unittest.cc} (71%)
copy src/bin/dbutil/tests/testdata/{empty_version.sqlite3 => v2_1.sqlite3} (75%)
create mode 100644 src/bin/dhcp6/config_parser.cc
create mode 100644 src/bin/dhcp6/config_parser.h
create mode 100644 src/bin/dhcp6/dhcp6.dox
create mode 100644 src/bin/dhcp6/tests/config_parser_unittest.cc
delete mode 100644 src/bin/host/Makefile.am
create mode 100644 src/lib/datasrc/memory/zone_data_loader.cc
create mode 100644 src/lib/datasrc/memory/zone_data_loader.h
create mode 100644 src/lib/datasrc/memory/zone_data_updater.cc
create mode 100644 src/lib/datasrc/memory/zone_data_updater.h
copy src/lib/{python/isc/datasrc/configurableclientlist_python.h => datasrc/memory/zone_table_segment.cc} (60%)
create mode 100644 src/lib/datasrc/memory/zone_table_segment.h
copy src/lib/{python/isc/datasrc/configurableclientlist_python.h => datasrc/memory/zone_table_segment_local.cc} (60%)
create mode 100644 src/lib/datasrc/memory/zone_table_segment_local.h
create mode 100644 src/lib/datasrc/tests/memory/zone_table_segment_unittest.cc
create mode 100644 src/lib/dhcp/duid.cc
create mode 100644 src/lib/dhcp/duid.h
create mode 100644 src/lib/dhcp/lease_mgr.cc
create mode 100644 src/lib/dhcp/lease_mgr.h
create mode 100644 src/lib/dhcp/tests/duid_unittest.cc
create mode 100644 src/lib/dhcp/tests/lease_mgr_unittest.cc
delete mode 100644 src/lib/dns/rrsetlist.cc
delete mode 100644 src/lib/dns/rrsetlist.h
delete mode 100644 src/lib/dns/tests/rrsetlist_unittest.cc
delete mode 100644 src/lib/util/threads/lock.h
rename src/lib/util/threads/{lock.cc => sync.cc} (58%)
create mode 100644 src/lib/util/threads/sync.h
copy src/{bin/auth => lib/util/threads}/tests/.gitignore (100%)
create mode 100644 src/lib/util/threads/tests/condvar_unittest.cc
copy src/lib/util/unittests/{resource.cc => check_valgrind.cc} (67%)
create mode 100644 src/lib/util/unittests/check_valgrind.h
create mode 100644 tests/tools/perfdhcp/templates/.gitignore
-----------------------------------------------------------------------
diff --git a/ChangeLog b/ChangeLog
index bad54da..9e76a18 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,84 @@
+494. [bug] jinmei
+ Fixed a problem that shutting down BIND 10 kept some of the
+ processes alive. It was two-fold: when the main bind10 process
+ started as a root, started b10-sockcreator with the privilege, and
+ then dropped the privilege, the bind10 process cannot kill the
+ sockcreator via signal any more (when it has to), but it kept
+ sending the signal and didn't stop. Also, when running on Python
+ 3.1 (or older), the sockcreator had some additional file
+ descriptor open, which prevented it from exiting even after the
+ bind10 process terminated. Now the bind10 process simply gives up
+ killing a subprocess if it fails due to lack of permission, and it
+ makes sure the socket creator is spawned without any unnecessary
+ FDs open.
+ (Trac #1858, git 405d85c8a0042ba807a3a123611ff383c4081ee1)
+
+493. [build] jinmei
+ Fixed build failure with newer versions of clang++. These
+ versions are stricter regarding "unused variable" and "unused
+ (driver) arguments" warnings, and cause fatal build error
+ with -Werror. The affected versions of clang++ include Apple's
+ customized version 4.1 included in Xcode 4.5.1. So this fix
+ will solve build errors for Mac OS X that uses newer versions of
+ Xcode.
+ (Trac #2340, git 55be177fc4f7537143ab6ef5a728bd44bdf9d783,
+ 3e2a372012e633d017a97029d13894e743199741 and commits before it
+ with [2340] in the commit log)
+
+492. [func] tomek
+ libdhcpsrv: The DHCP Configuration Manager is now able to store
+ information about IPv4 subnets and pools. It is still not possible
+ to configure that information. Such capability will be implemented
+ in a near future.
+ (Trac #2237, git a78e560343b41f0f692c7903c938b2b2b24bf56b)
+
+491. [func] tomek
+ b10-dhcp6: Configuration for DHCPv6 has been implemented.
+ Currently it is possible to configure IPv6 subnets and pools
+ within those subnets, global and per subnet values of renew,
+ rebind, preferred and valid lifetimes. Configured parameters
+ are accepted, but are not used yet by the allocation engine yet.
+ (Trac #2269, git 028bed9014b15facf1a29d3d4a822c9d14fc6411)
+
+490. [func] tomek
+ libdhcpsrv: An abstract API for lease database has been
+ implemented. It offers a common interface to all concrete
+ database backends.
+ (Trac #2140, git df196f7609757253c4f2f918cd91012bb3af1163)
+
+489. [func] muks
+ The isc::dns::RRsetList class has been removed. It was now unused
+ inside the BIND 10 codebase, and the interface was considered
+ prone to misuse.
+ (Trac #2266, git 532ac3d0054f6a11b91ee369964f3a84dabc6040)
+
+488. [build] jinmei
+ On configure, changed the search order for Python executable.
+ It first ties more specific file names such as "python3.2" before
+ more generic "python3". This will prevent configure failure on
+ Mac OS X that installs Python3 via recent versions of Homebrew.
+ (Trac #2339, git 88db890d8d1c64de49be87f03c24a2021bcf63da)
+
+487. [bug] jinmei
+ The bind10 process now terminates a component (subprocess) by the
+ "config remove Boss/components" bindctl command even if the
+ process crashes immediately before the command is sent to bind10.
+ Previously this led to an inconsistent state between the
+ configuration and an internal component list of bind10, and bind10
+ kept trying to restart the component. A known specific case of
+ this problem is that b10-ddns could keep failing (due to lack of
+ dependency modules) and the administrator couldn't stop the
+ restart via bindctl.
+ (Trac #2244, git 7565788d06f216ab254008ffdfae16678bcd00e5)
+
+486. [bug]* jinmei
+ All public header files for libb10-dns++ are now installed.
+ Template configure.ac and utility AC macros for external projects
+ using the library are provided under the "examples" directory.
+ The src/bin/host was moved as part of the examples (and not
+ installed with other BIND 10 programs any more).
+ (Trac #1870, git 4973e638d354d8b56dcadf71123ef23c15662021)
+
485. [bug] jelte
Several bugs have been fixed in bindctl; tab-completion now works
within configuration lists, the problem where sometimes the
diff --git a/NEWS b/NEWS
deleted file mode 100644
index e69de29..0000000
diff --git a/TODO b/TODO
deleted file mode 100644
index e69de29..0000000
diff --git a/configure.ac b/configure.ac
index c6a2f01..09de67f 100644
--- a/configure.ac
+++ b/configure.ac
@@ -4,7 +4,7 @@
AC_PREREQ([2.59])
AC_INIT(bind10-devel, 20120817, bind10-dev at isc.org)
AC_CONFIG_SRCDIR(README)
-AM_INIT_AUTOMAKE
+AM_INIT_AUTOMAKE([foreign])
m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])dnl be backward compatible
AC_CONFIG_HEADERS([config.h])
AC_CONFIG_MACRO_DIR([m4macros])
@@ -70,6 +70,106 @@ AC_TRY_LINK([],[],
])
LDFLAGS=$LDFLAGS_SAVED
+# Compiler dependent settings: define some mandatory CXXFLAGS here.
+# We also use a separate variable B10_CXXFLAGS. This will (and should) be
+# used as the default value for each specific AM_CXXFLAGS:
+# AM_CXXFLAGS = $(B10_CXXFLAGS)
+# AM_CXXFLAGS += ... # add module specific flags
+# We need this so that we can disable some specific compiler warnings per
+# module basis; since AM_CXXFLAGS are placed before CXXFLAGS, and since
+# gcc's -Wno-XXX option must be specified after -Wall or -Wextra, we cannot
+# specify the default warning flags in CXXFLAGS and let specific modules
+# "override" the default.
+
+# This may be used to try linker flags.
+AC_DEFUN([BIND10_CXX_TRY_FLAG], [
+ AC_MSG_CHECKING([whether $CXX supports $1])
+
+ bind10_save_CXXFLAGS="$CXXFLAGS"
+ CXXFLAGS="$CXXFLAGS $1"
+
+ AC_LINK_IFELSE([AC_LANG_SOURCE([int main(void){ return 0;}])],
+ [bind10_cxx_flag=yes], [bind10_cxx_flag=no])
+ CXXFLAGS="$bind10_save_CXXFLAGS"
+
+ if test "x$bind10_cxx_flag" = "xyes"; then
+ ifelse([$2], , :, [$2])
+ else
+ ifelse([$3], , :, [$3])
+ fi
+
+ AC_MSG_RESULT([$bind10_cxx_flag])
+])
+
+# SunStudio compiler requires special compiler options for boost
+# (http://blogs.sun.com/sga/entry/boost_mini_howto)
+if test "$SUNCXX" = "yes"; then
+CXXFLAGS="$CXXFLAGS -library=stlport4 -features=tmplife -features=tmplrefstatic"
+MULTITHREADING_FLAG="-mt"
+fi
+
+# Newer versions of clang++ promotes "unused driver arguments" warnings to
+# a fatal error with -Werror, causing build failure. Since we use multiple
+# compilers on multiple systems, this can easily happen due to settings for
+# non clang++ environments that could be just ignored otherwise. It can also
+# happen if clang++ is used via ccache. So, although probably suboptimal,
+# we suppress this particular warning. Note that it doesn't weaken checks
+# on the source code.
+if test "$CLANGPP" = "yes"; then
+ B10_CXXFLAGS="$B10_CXXFLAGS -Qunused-arguments"
+fi
+
+BIND10_CXX_TRY_FLAG([-Wno-missing-field-initializers],
+ [WARNING_NO_MISSING_FIELD_INITIALIZERS_CFLAG="-Wno-missing-field-initializers"])
+AC_SUBST(WARNING_NO_MISSING_FIELD_INITIALIZERS_CFLAG)
+
+# gcc specific settings:
+if test "X$GXX" = "Xyes"; then
+B10_CXXFLAGS="$B10_CXXFLAGS -Wall -Wextra -Wwrite-strings -Woverloaded-virtual -Wno-sign-compare"
+case "$host" in
+*-solaris*)
+ MULTITHREADING_FLAG=-pthreads
+ # In Solaris, IN6ADDR_ANY_INIT and IN6ADDR_LOOPBACK_INIT need -Wno-missing-braces
+ B10_CXXFLAGS="$B10_CXXFLAGS -Wno-missing-braces"
+ ;;
+*)
+ MULTITHREADING_FLAG=-pthread
+ ;;
+esac
+
+# Don't use -Werror if configured not to
+AC_ARG_WITH(werror,
+ AC_HELP_STRING([--with-werror], [Compile using -Werror (default=yes)]),
+ [
+ case "${withval}" in
+ yes) with_werror=1 ;;
+ no) with_werror=0 ;;
+ *) AC_MSG_ERROR(bad value ${withval} for --with-werror) ;;
+ esac],
+ [with_werror=1])
+
+werror_ok=0
+
+# Certain versions of gcc (g++) have a bug that incorrectly warns about
+# the use of anonymous name spaces even if they're closed in a single
+# translation unit. For these versions we have to disable -Werror.
+if test $with_werror = 1; then
+ CXXFLAGS_SAVED="$CXXFLAGS"
+ CXXFLAGS="$CXXFLAGS $B10_CXXFLAGS -Werror"
+ AC_MSG_CHECKING(for in-TU anonymous namespace breakage)
+ AC_TRY_COMPILE([namespace { class Foo {}; }
+ namespace isc {class Bar {Foo foo_;};} ],,
+ [AC_MSG_RESULT(no)
+ werror_ok=1
+ B10_CXXFLAGS="$B10_CXXFLAGS -Werror"],
+ [AC_MSG_RESULT(yes)])
+ CXXFLAGS="$CXXFLAGS_SAVED"
+fi
+
+fi dnl GXX = yes
+
+AM_CONDITIONAL(GCC_WERROR_OK, test $werror_ok = 1)
+
# allow building programs with static link. we need to make it selective
# because loadable modules cannot be statically linked.
AC_ARG_ENABLE([static-link],
@@ -132,7 +232,7 @@ AM_CONDITIONAL(SET_ENV_LIBRARY_PATH, test $SET_ENV_LIBRARY_PATH = yes)
AC_SUBST(SET_ENV_LIBRARY_PATH)
AC_SUBST(ENV_LIBRARY_PATH)
-m4_define([_AM_PYTHON_INTERPRETER_LIST], [python python3 python3.1 python3.2])
+m4_define([_AM_PYTHON_INTERPRETER_LIST], [python python3.2 python3.1 python3])
AC_ARG_WITH([pythonpath],
AC_HELP_STRING([--with-pythonpath=PATH],
[specify an absolute path to python executable when automatic version check (incorrectly) fails]),
@@ -256,95 +356,11 @@ fi
# TODO: check for _sqlite3.py module
-# Compiler dependent settings: define some mandatory CXXFLAGS here.
-# We also use a separate variable B10_CXXFLAGS. This will (and should) be
-# used as the default value for each specific AM_CXXFLAGS:
-# AM_CXXFLAGS = $(B10_CXXFLAGS)
-# AM_CXXFLAGS += ... # add module specific flags
-# We need this so that we can disable some specific compiler warnings per
-# module basis; since AM_CXXFLAGS are placed before CXXFLAGS, and since
-# gcc's -Wno-XXX option must be specified after -Wall or -Wextra, we cannot
-# specify the default warning flags in CXXFLAGS and let specific modules
-# "override" the default.
-
-# This may be used to try linker flags.
-AC_DEFUN([BIND10_CXX_TRY_FLAG], [
- AC_MSG_CHECKING([whether $CXX supports $1])
-
- bind10_save_CXXFLAGS="$CXXFLAGS"
- CXXFLAGS="$CXXFLAGS $1"
-
- AC_LINK_IFELSE([AC_LANG_SOURCE([int main(void){ return 0;}])],
- [bind10_cxx_flag=yes], [bind10_cxx_flag=no])
- CXXFLAGS="$bind10_save_CXXFLAGS"
-
- if test "x$bind10_cxx_flag" = "xyes"; then
- ifelse([$2], , :, [$2])
- else
- ifelse([$3], , :, [$3])
- fi
-
- AC_MSG_RESULT([$bind10_cxx_flag])
-])
-
-# SunStudio compiler requires special compiler options for boost
-# (http://blogs.sun.com/sga/entry/boost_mini_howto)
-if test "$SUNCXX" = "yes"; then
-CXXFLAGS="$CXXFLAGS -library=stlport4 -features=tmplife -features=tmplrefstatic"
-MULTITHREADING_FLAG="-mt"
-fi
-
-BIND10_CXX_TRY_FLAG([-Wno-missing-field-initializers],
- [WARNING_NO_MISSING_FIELD_INITIALIZERS_CFLAG="-Wno-missing-field-initializers"])
-AC_SUBST(WARNING_NO_MISSING_FIELD_INITIALIZERS_CFLAG)
-
-# gcc specific settings:
-if test "X$GXX" = "Xyes"; then
-B10_CXXFLAGS="-Wall -Wextra -Wwrite-strings -Woverloaded-virtual -Wno-sign-compare"
-case "$host" in
-*-solaris*)
- MULTITHREADING_FLAG=-pthreads
- # In Solaris, IN6ADDR_ANY_INIT and IN6ADDR_LOOPBACK_INIT need -Wno-missing-braces
- B10_CXXFLAGS="$B10_CXXFLAGS -Wno-missing-braces"
- ;;
-*)
- MULTITHREADING_FLAG=-pthread
- ;;
-esac
-
-# Don't use -Werror if configured not to
-AC_ARG_WITH(werror,
- AC_HELP_STRING([--with-werror], [Compile using -Werror (default=yes)]),
- [
- case "${withval}" in
- yes) with_werror=1 ;;
- no) with_werror=0 ;;
- *) AC_MSG_ERROR(bad value ${withval} for --with-werror) ;;
- esac],
- [with_werror=1])
-
-werror_ok=0
-
-# Certain versions of gcc (g++) have a bug that incorrectly warns about
-# the use of anonymous name spaces even if they're closed in a single
-# translation unit. For these versions we have to disable -Werror.
-if test $with_werror = 1; then
- CXXFLAGS_SAVED="$CXXFLAGS"
- CXXFLAGS="$CXXFLAGS $B10_CXXFLAGS -Werror"
- AC_MSG_CHECKING(for in-TU anonymous namespace breakage)
- AC_TRY_COMPILE([namespace { class Foo {}; }
- namespace isc {class Bar {Foo foo_;};} ],,
- [AC_MSG_RESULT(no)
- werror_ok=1
- B10_CXXFLAGS="$B10_CXXFLAGS -Werror"],
- [AC_MSG_RESULT(yes)])
- CXXFLAGS="$CXXFLAGS_SAVED"
-fi
-
+# (g++ only check)
# Python 3.2 has an unused parameter in one of its headers. This
# has been reported, but not fixed as of yet, so we check if we need
# to set -Wno-unused-parameter.
-if test $werror_ok = 1; then
+if test "X$GXX" = "Xyes" -a $werror_ok = 1; then
CPPFLAGS_SAVED="$CPPFLAGS"
CPPFLAGS=${PYTHON_INCLUDES}
CXXFLAGS_SAVED="$CXXFLAGS"
@@ -370,10 +386,6 @@ if test $werror_ok = 1; then
CPPFLAGS="$CPPFLAGS_SAVED"
fi
-fi dnl GXX = yes
-
-AM_CONDITIONAL(GCC_WERROR_OK, test $werror_ok = 1)
-
# produce PIC unless we disable shared libraries. need this for python bindings.
if test $enable_shared != "no" -a "X$GXX" = "Xyes"; then
B10_CXXFLAGS="$B10_CXXFLAGS -fPIC"
@@ -1055,6 +1067,13 @@ AM_COND_IF([ENABLE_LOGGER_CHECKS], [AC_DEFINE([ENABLE_LOGGER_CHECKS], [1], [Chec
AC_PATH_PROG(VALGRIND, valgrind, no)
AM_CONDITIONAL(HAVE_VALGRIND, test "x$VALGRIND" != "xno")
+# Also check for valgrind headers
+# We could consider adding them to the source code tree, as this
+# is the encouraged method of using them; they are BSD-licensed.
+# However, until we find that this is a problem, we just use
+# the system-provided ones, if available
+AC_CHECK_HEADERS(valgrind/valgrind.h, [AC_DEFINE([HAVE_VALGRIND_HEADERS], [1], [Check valgrind headers])])
+
found_valgrind="not found"
if test "x$VALGRIND" != "xno"; then
found_valgrind="found"
@@ -1099,7 +1118,6 @@ AC_CONFIG_FILES([Makefile
src/bin/dbutil/Makefile
src/bin/dbutil/tests/Makefile
src/bin/dbutil/tests/testdata/Makefile
- src/bin/host/Makefile
src/bin/loadzone/Makefile
src/bin/loadzone/tests/correct/Makefile
src/bin/loadzone/tests/error/Makefile
diff --git a/doc/devel/02-dhcp.dox b/doc/devel/02-dhcp.dox
index 5217f73..a4bbff9 100644
--- a/doc/devel/02-dhcp.dox
+++ b/doc/devel/02-dhcp.dox
@@ -57,29 +57,6 @@
* that does not support msgq. That is useful for embedded environments.
* It may also be useful in validation.
*
- * @page dhcpv6 DHCPv6 Server Component
- *
- * BIND10 offers DHCPv6 server implementation. It is implemented as
- * b10-dhcp6 component. Its primary code is located in
- * isc::dhcp::Dhcpv6Srv class. It uses \ref libdhcp extensively,
- * especially lib::dhcp::Pkt6, isc::dhcp::Option and
- * isc::dhcp::IfaceMgr classes. Currently this code offers skeleton
- * functionality, i.e. it is able to receive and process incoming
- * requests and trasmit responses. However, it does not have database
- * management, so it returns only one, hardcoded lease to whoever asks
- * for it.
- *
- * DHCPv6 server component does not support relayed traffic yet, as
- * support for relay decapsulation is not implemented yet.
- *
- * DHCPv6 server component does not use BIND10 logging yet.
- *
- * @section dhcpv6Session BIND10 message queue integration
- *
- * DHCPv4 server component is now integrated with BIND10 message queue.
- * It follows the same principle as DHCPv4. See \ref dhcpv4Session for
- * details.
- *
* @page libdhcp libdhcp++
*
* @section libdhcpIntro Libdhcp++ Library Introduction
diff --git a/doc/devel/mainpage.dox b/doc/devel/mainpage.dox
index ca9d881..0da5287 100644
--- a/doc/devel/mainpage.dox
+++ b/doc/devel/mainpage.dox
@@ -15,12 +15,17 @@
* <a href="http://bind10.isc.org/">BIND10 webpage (http://bind10.isc.org)</a>
*
* @section DNS
+ * - Authoritative DNS (todo)
+ * - Recursive resolver (todo)
* - @subpage DataScrubbing
*
* @section DHCP
* - @subpage dhcpv4
* - @subpage dhcpv4Session
* - @subpage dhcpv6
+ * - @subpage dhcpv6-session
+ * - @subpage dhcpv6-config-parser
+ * - @subpage dhcpv6-config-inherit
* - @subpage libdhcp
* - @subpage libdhcpIntro
* - @subpage libdhcpIfaceMgr
diff --git a/doc/guide/bind10-guide.xml b/doc/guide/bind10-guide.xml
index 2e66ad5..6065616 100644
--- a/doc/guide/bind10-guide.xml
+++ b/doc/guide/bind10-guide.xml
@@ -2751,13 +2751,13 @@ then change those defaults with config set Resolver/forward_addresses[0]/address
<title>DHCPv4 Server Configuration</title>
<para>
The DHCPv4 server does not have a lease database implemented yet
- nor any support for configuration, so every time the same set
+ nor any support for configuration, so the same set
of configuration options (including the same fixed address)
will be assigned every time.
</para>
<para>
At this stage of development, the only way to alter the server
- configuration is to tweak its source code. To do so, please
+ configuration is to modify its source code. To do so, please
edit src/bin/dhcp4/dhcp4_srv.cc file and modify following
parameters and recompile:
<screen>
@@ -2944,16 +2944,95 @@ const std::string HARDCODED_SERVER_ID = "192.0.2.1";</screen>
<section id="dhcp6-config">
<title>DHCPv6 Server Configuration</title>
<para>
- The DHCPv6 server does not have lease database implemented yet
- or any support for configuration, so every time the same set
- of configuration options (including the same fixed address)
- will be assigned every time.
+ Once the server is started, it can be configured. To view the
+ current configuration, use the following command in <command>bindctl</command>:
+ <screen>
+ > <userinput>config show Dhcp6</userinput></screen>
+ When starting Dhcp6 daemon for the first time, the default configuration
+ will be available. It will look similar to this:
+ <screen>
+> <userinput>config show Dhcp6</userinput>
+Dhcp6/interface "eth0" string (default)
+Dhcp6/renew-timer 1000 integer (default)
+Dhcp6/rebind-timer 2000 integer (default)
+Dhcp6/preferred-lifetime 3000 integer (default)
+Dhcp6/valid-lifetime 4000 integer (default)
+Dhcp6/subnet6 [] list (default)</screen>
</para>
+
<para>
- At this stage of development, the only way to alter server
- configuration is to tweak its source code. To do so, please
- edit src/bin/dhcp6/dhcp6_srv.cc file, modify the following
- parameters and recompile:
+ To change one of the parameters, simply follow
+ the usual <command>bindctl</command> procedure. For example, to make the
+ leases longer, change their valid-lifetime parameter:
+ <screen>
+> <userinput>config set Dhcp6/valid-lifetime 7200</userinput>
+> <userinput>config commit</userinput></screen>
+ Please note that most Dhcp6 parameters are of global scope
+ and apply to all defined subnets, unless they are overridden on a
+ per-subnet basis.
+ </para>
+
+ <para>
+ The essential role of DHCPv6 server is address assignment. The server
+ has to be configured with at least one subnet and one pool of dynamic
+ addresses to be managed. For example, assume that the server
+ is connected to a network segment that uses the 2001:db8:1::/64
+ prefix. The Administrator of that network has decided that addresses from range
+ 2001:db8:1::1 to 2001:db8:1::ffff are going to be managed by the Dhcp6
+ server. Such a configuration can be achieved in the following way:
+ <screen>
+> <userinput>config add Dhcp6/subnet6</userinput>
+> <userinput>config set Dhcp6/subnet6[0]/subnet "2001:db8:1::/64"</userinput>
+> <userinput>config set Dhcp6/subnet6[0]/pool [ "2001:db8:1::0 - 2001:db8:1::ffff" ]</userinput>
+> <userinput>config commit</userinput></screen>
+ Note that subnet is defined as a simple string, but the pool parameter
+ is actually a list of pools: for this reason, the pool definition is
+ enclosed in square brackets, even though only one range of addresses
+ is specified.</para>
+ <para>It is possible to define more than one pool in a
+ subnet: continuing the previous example, further assume that
+ 2001:db8:1:0:5::/80 should be also be managed by the server. It could be written as
+ 2001:db8:1:0:5:: to 2001:db8:1::5:ffff:ffff:ffff, but typing so many 'f's
+ is cumbersome. It can be expressed more simply as 2001:db8:1:0:5::/80. Both
+ formats are supported by Dhcp6 and can be mixed in the pool list.
+ For example, one could define the following pools:
+ <screen>
+> <userinput>config set Dhcp6/subnet6[0]/pool [ "2001:db8:1::1 - 2001:db8:1::ffff", "2001:db8:1:0:5::/80" ]</userinput>
+> <userinput>config commit</userinput></screen>
+ The number of pools is not limited, but for performance reasons it is recommended to
+ use as few as possible.
+ </para>
+ <para>
+ The server may be configured to serve more than one subnet. To add a second subnet,
+ use a command similar to the following:
+ <screen>
+> <userinput>config add Dhcp6/subnet6</userinput>
+> <userinput>config set Dhcp6/subnet6[1]/subnet "2001:db8:beef::/48"</userinput>
+> <userinput>config set Dhcp6/subnet6[1]/pool [ "2001:db8:beef::/48" ]</userinput>
+> <userinput>config commit</userinput></screen>
+ Arrays are counted from 0. subnet[0] refers to the subnet defined in the
+ previous example. The <command>config add Dhcp6/subnet6</command> adds
+ another (second) subnet. It can be referred to as
+ <command>Dhcp6/subnet6[1]</command>. In this example, we allow server to
+ dynamically assign all addresses available in the whole subnet. Although
+ very wasteful, it is certainly a valid configuration to dedicate the
+ whole /48 subnet for that purpose.
+ </para>
+ <para>
+ When configuring a DHCPv6 server using prefix/length notation, please pay
+ attention to the boundary values. When specifying that the server should use
+ a given pool, it will be able to allocate also first (typically network
+ address) address from that pool. For example for pool 2001:db8::/64 the
+ 2001:db8:: address may be assigned as well. If you want to avoid this,
+ please use min-max notation.
+ </para>
+
+ <para>
+ Note: Although configuration is now accepted, it is not internally used
+ by they server yet. At this stage of development, the only way to alter
+ server configuration is to modify its source code. To do so, please edit
+ src/bin/dhcp6/dhcp6_srv.cc file, modify the following parameters and
+ recompile:
<screen>
const std::string HARDCODED_LEASE = "2001:db8:1::1234:abcd";
const uint32_t HARDCODED_T1 = 1500; // in seconds
diff --git a/examples/AUTHORS b/examples/AUTHORS
new file mode 100644
index 0000000..e69de29
diff --git a/examples/COPYING b/examples/COPYING
new file mode 100644
index 0000000..f3febbe
--- /dev/null
+++ b/examples/COPYING
@@ -0,0 +1,13 @@
+Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC")
+
+Permission to use, copy, modify, and/or distribute this software for any
+purpose with or without fee is hereby granted, provided that the above
+copyright notice and this permission notice appear in all copies.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+PERFORMANCE OF THIS SOFTWARE.
diff --git a/examples/ChangeLog b/examples/ChangeLog
new file mode 100644
index 0000000..e69de29
diff --git a/examples/INSTALL b/examples/INSTALL
new file mode 100644
index 0000000..43436c2
--- /dev/null
+++ b/examples/INSTALL
@@ -0,0 +1,9 @@
+If using git (not the tarball), build the "configure" file:
+ autoreconf --install
+
+To then build from source:
+ ./configure
+ make
+
+You may have to specify the location of BIND 10 header files and
+library objects. See configure options by ./configure --help.
diff --git a/examples/Makefile.am b/examples/Makefile.am
new file mode 100644
index 0000000..ef437eb
--- /dev/null
+++ b/examples/Makefile.am
@@ -0,0 +1,4 @@
+SUBDIRS = host
+
+# Make sure macros under m4 will be included
+ACLOCAL_AMFLAGS = -I m4
diff --git a/examples/NEWS b/examples/NEWS
new file mode 100644
index 0000000..e69de29
diff --git a/examples/README b/examples/README
new file mode 100644
index 0000000..65f777b
--- /dev/null
+++ b/examples/README
@@ -0,0 +1,32 @@
+This is the top directory for sample programs that can be developed
+using public BIND 10 libraries outside of the BIND 10 project. It's
+intended to be built with installed BIND 10 header files and library
+objects, so it's not a target of the main build tree, and does not
+refer to any other part of the BIND 10 source tree that contains
+this directory.
+
+On the top (sub) directory (where this README file is stored), we
+provide a sample configure.ac and Makefile.am files for GNU automake
+environments with helper autoconf macros to detect the availability and
+location of BIND 10 header files and library objects.
+
+You can use the configure.ac and Makefile.am files with macros under
+the "m4" subdirectory as a template for your own project. The key is
+to call the AX_ISC_BIND10 function (as the sample configure.ac does)
+from your configure.ac. Then it will check the availability of
+necessary stuff and set some corresponding AC variables. You can then
+use the resulting variables in your Makefile.in or Makefile.ac.
+
+If you use automake, don't forget adding the following line to the top
+level Makefile.am:
+
+ACLOCAL_AMFLAGS = -I m4
+
+This is necessary to incorporate the helper macro definitions.
+
+If you don't use automake but autoconf, make sure to add the following
+to the configure.ac file:
+
+sinclude(m4/ax_boost_include.m4)
+sinclude(m4/ax_isc_bind10.m4)
+(and same for other m4 files as they are added under m4/)
diff --git a/examples/configure.ac b/examples/configure.ac
new file mode 100644
index 0000000..9379687
--- /dev/null
+++ b/examples/configure.ac
@@ -0,0 +1,28 @@
+# -*- Autoconf -*-
+# Process this file with autoconf to produce a configure script.
+
+AC_PREREQ([2.59])
+AC_INIT(bind10-examples, 20120817, bind10-dev at isc.org)
+AC_CONFIG_SRCDIR([README])
+AM_INIT_AUTOMAKE
+AC_CONFIG_HEADERS([config.h])
+
+# Checks for programs.
+AC_PROG_CXX
+AC_LANG([C++])
+
+# Checks for BIND 10 headers and libraries
+AX_ISC_BIND10
+
+# For the example host program, we require the BIND 10 DNS library
+if test "x$BIND10_DNS_LIB" = "x"; then
+ AC_MSG_ERROR([unable to find BIND 10 DNS library needed to build 'host'])
+fi
+
+# Checks for typedefs, structures, and compiler characteristics.
+AC_HEADER_STDBOOL
+
+AC_CONFIG_FILES([Makefile
+ host/Makefile])
+
+AC_OUTPUT
diff --git a/examples/host/.gitignore b/examples/host/.gitignore
new file mode 100644
index 0000000..01ef357
--- /dev/null
+++ b/examples/host/.gitignore
@@ -0,0 +1,2 @@
+/b10-host
+/b10-host.1
diff --git a/examples/host/Makefile.am b/examples/host/Makefile.am
new file mode 100644
index 0000000..dbd57a2
--- /dev/null
+++ b/examples/host/Makefile.am
@@ -0,0 +1,6 @@
+AM_CPPFLAGS = $(BOOST_CPPFLAGS) $(BIND10_CPPFLAGS)
+
+bin_PROGRAMS = b10-host
+b10_host_SOURCES = host.cc
+b10_host_LDFLAGS = ${BIND10_LDFLAGS}
+b10_host_LDADD = ${BIND10_DNS_LIB}
diff --git a/examples/host/README b/examples/host/README
new file mode 100644
index 0000000..5cc4068
--- /dev/null
+++ b/examples/host/README
@@ -0,0 +1,4 @@
+Rewriting host(1) in C++ from scratch using BIND 10's libdns++.
+
+The bugs and incompatibilities are listed in the manual page
+and in the source code.
diff --git a/examples/host/b10-host.xml b/examples/host/b10-host.xml
new file mode 100644
index 0000000..a17ef67
--- /dev/null
+++ b/examples/host/b10-host.xml
@@ -0,0 +1,196 @@
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
+ "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd"
+ [<!ENTITY mdash "—">]>
+<!--
+ - Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+ -
+ - Permission to use, copy, modify, and/or distribute this software for any
+ - purpose with or without fee is hereby granted, provided that the above
+ - copyright notice and this permission notice appear in all copies.
+ -
+ - THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+ - REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ - AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ - INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ - LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ - OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ - PERFORMANCE OF THIS SOFTWARE.
+-->
+
+<!-- $Id$ -->
+<refentry>
+
+ <refentryinfo>
+ <date>May 4, 2011</date>
+ </refentryinfo>
+
+ <refmeta>
+ <refentrytitle>b10-host</refentrytitle>
+ <manvolnum>1</manvolnum>
+ <refmiscinfo>BIND10</refmiscinfo>
+ </refmeta>
+
+ <refnamediv>
+ <refname>b10-host</refname>
+ <refpurpose>DNS lookup utility</refpurpose>
+ </refnamediv>
+
+ <docinfo>
+ <copyright>
+ <year>2011</year>
+ <holder>Internet Systems Consortium, Inc. ("ISC")</holder>
+ </copyright>
+ </docinfo>
+
+ <refsynopsisdiv>
+ <cmdsynopsis>
+ <command>b10-host</command>
+ <arg><option>-a</option></arg>
+ <arg><option>-c <replaceable>class</replaceable></option></arg>
+ <arg><option>-d</option></arg>
+ <arg><option>-p <replaceable>port</replaceable></option></arg>
+ <arg><option>-r</option></arg>
+ <arg><option>-t <replaceable>type</replaceable></option></arg>
+ <arg><option>-v</option></arg>
+ <arg><replaceable>name</replaceable></arg>
+ <arg><option><replaceable>server</replaceable></option></arg>
+ </cmdsynopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>DESCRIPTION</title>
+ <para>
+ The <command>b10-host</command> utility does DNS lookups.
+ Its initial goal is to be a
+ <citerefentry><refentrytitle>host</refentrytitle>
+ <manvolnum>1</manvolnum></citerefentry>
+ clone, but also add a few features useful for BIND 10 development
+ testing.
+ </para>
+
+ <para>
+ By default, it looks up the A, AAAA, and MX record sets for the
+ <replaceable>name</replaceable>.
+ Optionally, you may select a name server to query against by adding
+ the <replaceable>server</replaceable> argument.
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>OPTIONS</title>
+
+ <para>The arguments are as follows:</para>
+
+ <variablelist>
+
+ <varlistentry>
+ <term><option>-a</option></term>
+ <listitem><para>
+ Enable verbose mode and do a query for type ANY.
+ (If the <option>-t</option> option is also set, then the
+ ANY query is not done, but it still uses verbose mode.)
+ </para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>-c <replaceable>class</replaceable></option></term>
+ <listitem><para>
+ Define the class for the query.
+ The default is IN (Internet).
+<!-- TODO: bug if class is unknown causes seg fault and possible core dump -->
+ </para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>-d</option></term>
+ <listitem><para>
+ Enable verbose output mode, including elapsed time in
+ milliseconds.
+ Verbose mode shows the header, question, answer, authority,
+ and additional sections (if provided).
+ (Same as <option>-v</option>.)
+ </para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>-p <replaceable>port</replaceable></option></term>
+ <listitem><para>
+ Select an alternative port for the query.
+ This may be a number or a service name.
+ The default is 53 (domain).
+ This is not a standard feature of
+ <citerefentry><refentrytitle>host</refentrytitle>
+ <manvolnum>1</manvolnum></citerefentry>.
+ </para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>-r</option></term>
+ <listitem><para>
+ Disable recursive processing by not setting the
+ Recursion Desired flag in the query.
+ </para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>-t <replaceable>type</replaceable></option></term>
+ <listitem><para>
+ Select a specific resource record type for the query.
+ By default, it looks up the A, AAAA, and MX record sets.
+<!-- TODO: bug if class is unknown causes seg fault and possible core dump -->
+ (This overrides the <option>-a</option> option.)
+ </para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>-v</option></term>
+ <listitem><para>
+ Same as <option>-d</option> option.
+ </para></listitem>
+ </varlistentry>
+
+ </variablelist>
+
+ </refsect1>
+
+ <refsect1>
+ <title>COMPATIBILITY / BUGS</title>
+ <para>
+ <command>b10-host</command> does not do reverse lookups by
+ default yet (by detecting if name is a IPv4 or IPv6 address).
+ </para>
+
+ <para>
+ Unknown <option>-c</option> class or <option>-t</option> type
+ causes <command>b10-host</command> to Abort.
+ </para>
+
+ <para>
+ Not all types are supported yet for formatting.
+ Not all switches are supported yet.
+ </para>
+
+ <para>
+ It doesn't use <filename>/etc/resolv.conf</filename> at this time.
+ The default name server used is 127.0.0.1.
+ </para>
+
+ <para>
+ <option>-p</option> is not a standard feature.
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>HISTORY</title>
+ <para>
+ The C++ version of <command>b10-host</command> was started in
+ October 2009 by Jeremy C. Reed of ISC.
+ Its usage and output were based on the standard <command>host</command>
+ command.
+ </para>
+ </refsect1>
+</refentry><!--
+ - Local variables:
+ - mode: sgml
+ - End:
+-->
diff --git a/examples/host/host.cc b/examples/host/host.cc
new file mode 100644
index 0000000..a5c6522
--- /dev/null
+++ b/examples/host/host.cc
@@ -0,0 +1,253 @@
+// Copyright (C) 2010-2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+// host rewritten in C++ using BIND 10 DNS library
+
+#include <arpa/inet.h>
+#include <netdb.h> // for getaddrinfo
+#include <sys/time.h> // for gettimeofday
+#include <sys/socket.h> // networking functions and definitions on FreeBSD
+
+#include <unistd.h>
+
+#include <string>
+#include <iostream>
+
+#include <util/buffer.h>
+
+#include <dns/name.h>
+#include <dns/message.h>
+#include <dns/messagerenderer.h>
+#include <dns/opcode.h>
+#include <dns/rcode.h>
+#include <dns/rrclass.h>
+#include <dns/rrtype.h>
+#include <dns/rrset.h>
+#include <dns/message.h>
+
+using namespace std;
+using namespace isc::dns;
+using namespace isc::util;
+
+namespace {
+char* dns_type = NULL; // not set, so A, AAAA, MX
+const char* server = "127.0.0.1";
+const char* server_port = "53";
+const char* dns_class = "IN";
+bool verbose = false;
+bool dns_any = false;
+int first_time = 1;
+bool recursive_bit = true;
+struct timeval before_time, after_time;
+
+int
+host_lookup(const char* const name, const char* const dns_class,
+ const char* const type, bool any) {
+
+ Message msg(Message::RENDER);
+
+ msg.setQid(0); // does this matter?
+
+ // TODO: add switch for this
+ msg.setOpcode(Opcode::QUERY());
+ msg.setRcode(Rcode::NOERROR());
+ if (recursive_bit) {
+ msg.setHeaderFlag(Message::HEADERFLAG_RD); // set recursive bit
+ }
+
+ msg.addQuestion(Question(Name(name),
+ RRClass(dns_class),
+ any ? RRType::ANY() : RRType(type))); // if NULL then:
+
+ MessageRenderer renderer;
+ msg.toWire(renderer);
+
+ struct addrinfo hints, *res;
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = AF_UNSPEC;
+ hints.ai_socktype = SOCK_DGRAM;
+ hints.ai_flags = 0; // not using AI_NUMERICHOST in case to bootstrap
+ if (getaddrinfo(server, server_port, &hints, &res) != 0) {
+ cerr << "address/port conversion for " << server << ":"
+ << server_port << " failed" << endl;
+ return (1);
+ }
+
+ if (verbose) {
+ cout << "Trying \"" << name << "\"\n";
+ }
+
+ if (verbose && first_time) {
+ // this is only output the first time
+ first_time = 0;
+ cout << "Using domain server:\n";
+ cout << "Name: " << server << "\n";
+ // TODO: I guess I have to do a lookup to get that address and aliases
+ // too
+ //cout << "Address: " << address << "\n" ; // "#" << port << "\n";
+ //cout << "Aliases: " << server << "\n";
+ }
+
+ int s = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
+
+ if (s < 0) {
+ cerr << "failed to open socket" << endl;
+ return (1);
+ }
+
+ if (verbose) {
+ gettimeofday(&before_time, NULL);
+ }
+
+ sendto(s, renderer.getData(), renderer.getLength(), 0, res->ai_addr,
+ res->ai_addrlen);
+
+ struct sockaddr_storage ss;
+ struct sockaddr* sa;
+ socklen_t sa_len;
+
+ sa_len = sizeof(ss);
+ sa = static_cast<struct sockaddr*>((void*)&ss);
+
+ char recvbuf[4096];
+ int cc;
+ if ((cc = recvfrom(s, recvbuf, sizeof(recvbuf), 0, sa, &sa_len)) > 0) {
+ try {
+ Message rmsg(Message::PARSE);
+ InputBuffer ibuffer(recvbuf, cc);
+
+ rmsg.fromWire(ibuffer);
+ if (!verbose) {
+ string description = "";
+ for (RRsetIterator it =
+ rmsg.beginSection(Message::SECTION_ANSWER);
+ it != rmsg.endSection(Message::SECTION_ANSWER);
+ ++it) {
+
+ if ((*it)->getType() == RRType::A()) {
+ description = "has address";
+ }
+ else if ((*it)->getType() == RRType::AAAA()) {
+ description = "has IPv6 address";
+ }
+ else if ((*it)->getType() == RRType::MX()) {
+ description = "mail is handled by";
+ }
+ else if ((*it)->getType() == RRType::TXT()) {
+ description = "descriptive text";
+ }
+
+ RdataIteratorPtr rit = (*it)->getRdataIterator();
+ for (; !rit->isLast(); rit->next()) {
+ // instead of using my name, maybe use returned label?
+ cout << name << " " << description << " " <<
+ (*rit).getCurrent().toText() << endl;
+ }
+ }
+ } else {
+ gettimeofday(&after_time, NULL);
+
+ // HEADER and QUESTION, ANSWER, AUTHORITY, and ADDITIONAL
+ std::cout << rmsg.toText() << std::endl;
+
+ if (before_time.tv_usec > after_time.tv_usec) {
+ after_time.tv_usec += 1000000;
+ --after_time.tv_sec;
+ }
+
+ int elapsed_time =
+ (after_time.tv_sec - before_time.tv_sec)
+ + ((after_time.tv_usec - before_time.tv_usec))/1000;
+
+ // TODO: if NXDOMAIN, host(1) doesn't show HEADER
+ // Host hsdjkfhksjhdfkj not found: 3(NXDOMAIN)
+ // TODO: test if NXDOMAIN
+
+ std::cout << "Received " << cc <<
+ " bytes in " << elapsed_time << " ms\n";
+ // TODO: " bytes from 127.0.0.1#53 in 0 ms
+
+ } //verbose
+/*
+TODO: handle InvalidRRClass
+TODO: handle invalid type exception
+ } catch (InvalidType ivt) {
+ std::cerr << "invalid type:" << ivt.what();
+*/
+ } catch (const exception& ex) {
+ std::cerr << "parse failed for " <<
+ string(name) << "/" << type << ": " << ex.what() << std::endl;
+ } catch (...) {
+ std::cerr << "parse failed for " << string(name) << "/" << type;
+ }
+ }
+
+ freeaddrinfo(res);
+
+ return (0);
+} // host_lookup()
+}
+
+int
+main(int argc, char* argv[]) {
+ int c;
+
+ while ((c = getopt(argc, argv, "ac:dp:rt:v")) != -1)
+ switch (c) {
+ case 'a':
+ dns_any = true;
+ verbose = true;
+ break;
+ case 'c':
+ dns_class = optarg;
+ break;
+ // p for port is a non-standard switch
+ case 'p':
+ server_port = optarg;
+ break;
+ case 'r':
+ recursive_bit = false;
+ break;
+ case 't':
+ dns_type = optarg;
+ break;
+ case 'd':
+ // drop through to v, because debug and verbose are equivalent
+ case 'v':
+ verbose = true;
+ break;
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc < 1) {
+ cout << "Usage: host [-adrv] [-c class] [-p port] [-t type] hostname [server]\n";
+ exit(1);
+ }
+
+ if (argc >= 2) {
+ server = argv[1];
+ }
+
+ if (dns_type == NULL) {
+ host_lookup(argv[0], dns_class, "A", dns_any);
+ // TODO: don't do next if A doesn't exist
+ host_lookup(argv[0], dns_class, "AAAA", dns_any);
+ host_lookup(argv[0], dns_class, "MX", dns_any);
+ } else {
+ // -t overrides -a, regardless of order
+ host_lookup(argv[0], dns_class, dns_type, false);
+ }
+ return (0);
+}
diff --git a/examples/m4/ax_boost_include.m4 b/examples/m4/ax_boost_include.m4
new file mode 100644
index 0000000..e41614d
--- /dev/null
+++ b/examples/m4/ax_boost_include.m4
@@ -0,0 +1,64 @@
+dnl @synopsis AX_BOOST_INCLUDE
+dnl
+dnl Test for the Boost C++ header files
+dnl
+dnl If no path to the installed boost header files is given via the
+dnl --with-boost-include option, the macro searchs under
+dnl /usr/local /usr/pkg /opt /opt/local directories.
+dnl
+dnl This macro calls:
+dnl
+dnl AC_SUBST(BOOST_CPPFLAGS)
+dnl
+
+AC_DEFUN([AX_BOOST_INCLUDE], [
+AC_LANG_SAVE
+AC_LANG([C++])
+
+#
+# Configure Boost header path
+#
+# If explicitly specified, use it.
+AC_ARG_WITH([boost-include],
+ AS_HELP_STRING([--with-boost-include=PATH],
+ [specify exact directory for Boost headers]),
+ [boost_include_path="$withval"])
+# If not specified, try some common paths.
+if test -z "$with_boost_include"; then
+ boostdirs="/usr/local /usr/pkg /opt /opt/local"
+ for d in $boostdirs
+ do
+ if test -f $d/include/boost/shared_ptr.hpp; then
+ boost_include_path=$d/include
+ break
+ fi
+ done
+fi
+CPPFLAGS_SAVES="$CPPFLAGS"
+if test "${boost_include_path}" ; then
+ BOOST_CPPFLAGS="-I${boost_include_path}"
+ CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS"
+fi
+# Make sure some commonly used headers are available
+AC_CHECK_HEADERS([boost/shared_ptr.hpp boost/bind.hpp boost/function.hpp],,
+ AC_MSG_ERROR([Missing required Boost header files.]))
+
+# Detect whether Boost tries to use threads by default, and, if not,
+# make it sure explicitly. In some systems the automatic detection
+# may depend on preceding header files, and if inconsistency happens
+# it could lead to a critical disruption.
+AC_MSG_CHECKING([whether Boost tries to use threads])
+AC_TRY_COMPILE([
+#include <boost/config.hpp>
+#ifdef BOOST_HAS_THREADS
+#error "boost will use threads"
+#endif],,
+[AC_MSG_RESULT(no)
+ CPPFLAGS_BOOST_THREADCONF="-DBOOST_DISABLE_THREADS=1"],
+[AC_MSG_RESULT(yes)])
+
+CPPFLAGS="$CPPFLAGS_SAVES $CPPFLAGS_BOOST_THREADCONF"
+AC_SUBST(BOOST_CPPFLAGS)
+
+AC_LANG_RESTORE
+])dnl AX_BOOST_INCLUDE
diff --git a/examples/m4/ax_isc_bind10.m4 b/examples/m4/ax_isc_bind10.m4
new file mode 100644
index 0000000..63e028c
--- /dev/null
+++ b/examples/m4/ax_isc_bind10.m4
@@ -0,0 +1,122 @@
+dnl @synopsis AX_BIND10
+dnl
+dnl @summary figure out how to build C++ programs using ISC BIND 10 libraries
+dnl
+dnl If no path to the installed BIND 10 header files or libraries is given
+dnl via the --with-bind10-include or --with-bind10-lib option, the macro
+dnl searchs under /usr/local/{include, lib}, /usr/pkg/{include, lib},
+dnl /opt/{include, lib}, /opt/local/{include, lib} directories, respectively.
+dnl
+dnl This macro calls:
+dnl
+dnl AC_SUBST(BIND10_CPPFLAGS)
+dnl AC_SUBST(BIND10_LDFLAGS)
+dnl AC_SUBST(BIND10_COMMON_LIB)
+dnl AC_SUBST(BIND10_DNS_LIB)
+dnl
+dnl If this macro finds CPPFLAGS, LDFLAGS or COMMON_LIB unavailable, it treats
+dnl that as a fatal error.
+dnl Checks for other BIND 10 module libraries are option, as not all
+dnl applications need all libraries. The main configure.ac can handle any
+dnl missing library as fatal by checking whether the corresponding
+dnl BIND10_xxx_LIB is defined.
+
+AC_DEFUN([AX_ISC_BIND10], [
+AC_REQUIRE([AX_BOOST_INCLUDE])
+AC_LANG_SAVE
+AC_LANG([C++])
+
+# Check for BIND10 common headers
+
+AC_ARG_WITH(bind10-include,
+ AS_HELP_STRING([--with-bind10-include=PATH],
+ [specify a path to BIND 10 header files]),
+ bind10_inc_path="$withval", bind10_inc_path="no")
+# If not specified, try some common paths.
+if test "$bind10_inc_path" = "no"; then
+ for d in /usr/local /usr/pkg /opt /opt/local
+ do
+ if test -f $d/include/util/buffer.h; then
+ bind10_inc_path=$d
+ break
+ fi
+ done
+fi
+CPPFLAGS_SAVES="$CPPFLAGS"
+if test "${bind10_inc_path}" != "no"; then
+ BIND10_CPPFLAGS="-I${bind10_inc_path}"
+ CPPFLAGS="$CPPFLAGS $BIND10_CPPFLAGS"
+fi
+AC_CHECK_HEADERS([util/buffer.h],,
+ AC_MSG_ERROR([Missing a commonly used BIND 10 header files]))
+CPPFLAGS="$CPPFLAGS_SAVES"
+AC_SUBST(BIND10_CPPFLAGS)
+
+# Check for BIND10 libraries
+CPPFLAGS_SAVED="$CPPFLAGS"
+CPPFLAGS="$CPPFLAGS $BIND10_CPPFLAGS"
+
+AC_ARG_WITH(bind10-lib,
+ AS_HELP_STRING([--with-bind10-lib=PATH],
+ [specify a path to BIND 10 library files]),
+ bind10_lib_path="$withval", bind10_lib_path="no")
+if test $bind10_lib_path != "no"; then
+ bind10_lib_dirs=$bind10_lib_path
+else
+ # If not specified, try some common paths.
+ bind10_lib_dirs="/usr/local/lib /usr/pkg/lib /opt/lib /opt/local/lib"
+fi
+
+# make sure we have buildable libraries
+AC_MSG_CHECKING([for BIND 10 common library])
+BIND10_COMMON_LIB="-lb10-util -lb10-exceptions"
+LDFLAGS="$LDFLAGS $BIND10_LDFLAGS"
+LIBS="$LIBS $BIND10_COMMON_LIB"
+for d in $bind10_lib_dirs
+do
+ LDFLAGS_SAVED="$LDFLAGS"
+ LDFLAGS="$LDFLAGS -L$d"
+ AC_TRY_LINK([
+#include <util/buffer.h>
+],[
+isc::util::OutputBuffer buffer(0);
+], [BIND10_LDFLAGS="-L${d}"])
+ if test "x$BIND10_LDFLAGS" != "x"; then
+ break
+ fi
+ LDFLAGS="$LDFLAGS_SAVED"
+done
+if test "x$BIND10_LDFLAGS" != "x"; then
+ AC_MSG_RESULT(yes)
+else
+ AC_MSG_RESULT(no)
+ AC_MSG_ERROR([unable to find required BIND 10 libraries])
+fi
+
+# restore LIBS once at this point
+LIBS="$LIBS_SAVES"
+
+AC_SUBST(BIND10_LDFLAGS)
+AC_SUBST(BIND10_COMMON_LIB)
+
+# Check per-module BIND 10 libraries
+
+# DNS library
+AC_MSG_CHECKING([for BIND 10 DNS library])
+LIBS="$LIBS $BIND10_COMMON_LIB -lb10-dns++"
+AC_TRY_LINK([
+#include <dns/rrtype.h>
+],[
+isc::dns::RRType rrtype(1);
+], [BIND10_DNS_LIB="-lb10-dns++"
+ AC_MSG_RESULT(yes)],
+ [AC_MSG_RESULT(no)])
+LIBS="$LIBS_SAVES"
+AC_SUBST(BIND10_DNS_LIB)
+
+# Restore other flags
+CPPFLAGS="$CPPFLAGS_SAVED"
+LDFLAGS="$LDFLAGS_SAVES"
+
+AC_LANG_RESTORE
+])dnl AX_ISC_BIND10
diff --git a/src/bin/Makefile.am b/src/bin/Makefile.am
index 7af44f9..0b4c1ae 100644
--- a/src/bin/Makefile.am
+++ b/src/bin/Makefile.am
@@ -1,4 +1,4 @@
-SUBDIRS = bind10 bindctl cfgmgr ddns loadzone msgq host cmdctl auth xfrin \
+SUBDIRS = bind10 bindctl cfgmgr ddns loadzone msgq cmdctl auth xfrin \
xfrout usermgr zonemgr stats tests resolver sockcreator dhcp4 dhcp6 \
dbutil sysinfo
diff --git a/src/bin/auth/Makefile.am b/src/bin/auth/Makefile.am
index df30a42..9eee9d4 100644
--- a/src/bin/auth/Makefile.am
+++ b/src/bin/auth/Makefile.am
@@ -55,7 +55,7 @@ b10_auth_SOURCES += auth_config.cc auth_config.h
b10_auth_SOURCES += command.cc command.h
b10_auth_SOURCES += common.h common.cc
b10_auth_SOURCES += statistics.cc statistics.h
-b10_auth_SOURCES += datasrc_configurator.h datasrc_configurator.cc
+b10_auth_SOURCES += datasrc_config.h datasrc_config.cc
b10_auth_SOURCES += main.cc
nodist_b10_auth_SOURCES = auth_messages.h auth_messages.cc
diff --git a/src/bin/auth/auth_config.h b/src/bin/auth/auth_config.h
index 6f18810..8e816a3 100644
--- a/src/bin/auth/auth_config.h
+++ b/src/bin/auth/auth_config.h
@@ -93,7 +93,7 @@ public:
/// that corresponds to this derived class and prepares a new value to
/// apply to the server.
/// In the above example, the derived class for the identifier "param1"
- /// would be passed an data \c Element storing an integer whose value
+ /// would be passed a data \c Element storing an integer whose value
/// is 10, and would record that value internally;
/// the derived class for the identifier "param2" would be passed a
/// map element and (after parsing) convert it into some internal
diff --git a/src/bin/auth/auth_srv.cc b/src/bin/auth/auth_srv.cc
index b870ae2..315a752 100644
--- a/src/bin/auth/auth_srv.cc
+++ b/src/bin/auth/auth_srv.cc
@@ -26,7 +26,7 @@
#include <exceptions/exceptions.h>
#include <util/buffer.h>
-#include <util/threads/lock.h>
+#include <util/threads/sync.h>
#include <dns/edns.h>
#include <dns/exceptions.h>
@@ -69,6 +69,8 @@
using namespace std;
+using boost::shared_ptr;
+
using namespace isc;
using namespace isc::cc;
using namespace isc::datasrc;
@@ -264,23 +266,22 @@ public:
AddressList listen_addresses_;
/// The TSIG keyring
- const boost::shared_ptr<TSIGKeyRing>* keyring_;
+ const shared_ptr<TSIGKeyRing>* keyring_;
- /// The client list
- std::map<RRClass, boost::shared_ptr<ConfigurableClientList> >
- client_lists_;
+ /// The data source client list
+ AuthSrv::DataSrcClientListsPtr datasrc_client_lists_;
- boost::shared_ptr<ConfigurableClientList> getClientList(const RRClass&
- rrclass)
+ shared_ptr<ConfigurableClientList> getDataSrcClientList(
+ const RRClass& rrclass)
{
// TODO: Debug-build only check
if (!mutex_.locked()) {
isc_throw(isc::Unexpected, "Not locked!");
}
- const std::map<RRClass, boost::shared_ptr<ConfigurableClientList> >::
- const_iterator it(client_lists_.find(rrclass));
- if (it == client_lists_.end()) {
- return (boost::shared_ptr<ConfigurableClientList>());
+ const std::map<RRClass, shared_ptr<ConfigurableClientList> >::
+ const_iterator it(datasrc_client_lists_->find(rrclass));
+ if (it == datasrc_client_lists_->end()) {
+ return (shared_ptr<ConfigurableClientList>());
} else {
return (it->second);
}
@@ -335,6 +336,8 @@ AuthSrvImpl::AuthSrvImpl(AbstractXfroutClient& xfrout_client,
xfrin_session_(NULL),
counters_(),
keyring_(NULL),
+ datasrc_client_lists_(new std::map<RRClass,
+ shared_ptr<ConfigurableClientList> >()),
ddns_base_forwarder_(ddns_forwarder),
ddns_forwarder_(NULL),
xfrout_connected_(false),
@@ -645,13 +648,13 @@ AuthSrvImpl::processNormalQuery(const IOMessage& io_message, Message& message,
}
// Lock the client lists and keep them under the lock until the processing
// and rendering is done (this is the same mutex as from
- // AuthSrv::getClientListMutex()).
+ // AuthSrv::getDataSrcClientListMutex()).
isc::util::thread::Mutex::Locker locker(mutex_);
try {
const ConstQuestionPtr question = *message.beginQuestion();
- const boost::shared_ptr<datasrc::ClientList>
- list(getClientList(question->getClass()));
+ const shared_ptr<datasrc::ClientList>
+ list(getDataSrcClientList(question->getClass()));
if (list) {
const RRType& qtype = question->getType();
const Name& qname = question->getName();
@@ -911,7 +914,7 @@ AuthSrv::setDNSService(isc::asiodns::DNSServiceBase& dnss) {
}
void
-AuthSrv::setTSIGKeyRing(const boost::shared_ptr<TSIGKeyRing>* keyring) {
+AuthSrv::setTSIGKeyRing(const shared_ptr<TSIGKeyRing>* keyring) {
impl_->keyring_ = keyring;
}
@@ -930,43 +933,23 @@ AuthSrv::destroyDDNSForwarder() {
}
}
-void
-AuthSrv::setClientList(const RRClass& rrclass,
- const boost::shared_ptr<ConfigurableClientList>& list) {
+AuthSrv::DataSrcClientListsPtr
+AuthSrv::swapDataSrcClientLists(DataSrcClientListsPtr new_lists) {
// TODO: Debug-build only check
if (!impl_->mutex_.locked()) {
- isc_throw(isc::Unexpected, "Not locked");
- }
-
- if (list) {
- impl_->client_lists_[rrclass] = list;
- } else {
- impl_->client_lists_.erase(rrclass);
+ isc_throw(isc::Unexpected, "Not locked!");
}
-}
-boost::shared_ptr<ConfigurableClientList>
-AuthSrv::getClientList(const RRClass& rrclass) {
- return (impl_->getClientList(rrclass));
+ std::swap(new_lists, impl_->datasrc_client_lists_);
+ return (new_lists);
}
-vector<RRClass>
-AuthSrv::getClientListClasses() const {
- // TODO: Debug-build only check
- if (!impl_->mutex_.locked()) {
- isc_throw(isc::Unexpected, "Not locked");
- }
-
- vector<RRClass> result;
- for (std::map<RRClass, boost::shared_ptr<ConfigurableClientList> >::
- const_iterator it(impl_->client_lists_.begin());
- it != impl_->client_lists_.end(); ++it) {
- result.push_back(it->first);
- }
- return (result);
+shared_ptr<ConfigurableClientList>
+AuthSrv::getDataSrcClientList(const RRClass& rrclass) {
+ return (impl_->getDataSrcClientList(rrclass));
}
util::thread::Mutex&
-AuthSrv::getClientListMutex() const {
+AuthSrv::getDataSrcClientListMutex() const {
return (impl_->mutex_);
}
diff --git a/src/bin/auth/auth_srv.h b/src/bin/auth/auth_srv.h
index ee7bd52..0849bdd 100644
--- a/src/bin/auth/auth_srv.h
+++ b/src/bin/auth/auth_srv.h
@@ -15,10 +15,11 @@
#ifndef __AUTH_SRV_H
#define __AUTH_SRV_H 1
-#include <string>
-
#include <config/ccsession.h>
+
#include <datasrc/factory.h>
+#include <datasrc/client_list.h>
+
#include <dns/message.h>
#include <dns/opcode.h>
#include <util/buffer.h>
@@ -35,6 +36,11 @@
#include <server_common/portconfig.h>
#include <auth/statistics.h>
+#include <boost/shared_ptr.hpp>
+
+#include <map>
+#include <string>
+
namespace isc {
namespace util {
namespace io {
@@ -296,31 +302,46 @@ public:
/// If there was no forwarder yet, this method does nothing.
void destroyDDNSForwarder();
- /// \brief Sets the currently used list for data sources of given
- /// class.
+ /// \brief Shortcut typedef used for swapDataSrcClientLists().
+ typedef boost::shared_ptr<std::map<
+ isc::dns::RRClass, boost::shared_ptr<
+ isc::datasrc::ConfigurableClientList> > >
+ DataSrcClientListsPtr;
+
+ /// \brief Swap the currently used set of data source client lists with
+ /// given one.
+ ///
+ /// The "set" of lists is actually given in the form of map from
+ /// RRClasses to shared pointers to isc::datasrc::ConfigurableClientList.
+ ///
+ /// This method returns the swapped set of lists, which was previously
+ /// used by the server.
+ ///
+ /// This method is intended to be used by a separate method to update
+ /// the data source configuration "at once". The caller must hold
+ /// a lock for the mutex object returned by \c getDataSrcClientListMutex()
+ /// before calling this method.
///
- /// Replaces the internally used client list with a new one. Other
- /// classes are not changed.
+ /// The ownership of the returned pointer is transferred to the caller.
+ /// The caller is generally expected to release the resources used in
+ /// the old lists. Note that it could take longer time if some of the
+ /// data source clients contain a large size of in-memory data.
///
- /// \param rrclass The class to modify.
- /// \param list Shared pointer to the client list. If it is NULL,
- /// the list is removed instead.
- void setClientList(const isc::dns::RRClass& rrclass, const
- boost::shared_ptr<isc::datasrc::ConfigurableClientList>&
- list);
+ /// The caller can pass a NULL pointer. This effectively disables
+ /// any data source for the server.
+ ///
+ /// \param new_lists Shared pointer to a new set of data source client
+ /// lists.
+ /// \return The previous set of lists. It can be NULL.
+ DataSrcClientListsPtr swapDataSrcClientLists(DataSrcClientListsPtr
+ new_lists);
/// \brief Returns the currently used client list for the class.
///
/// \param rrclass The class for which to get the list.
/// \return The list, or NULL if no list is set for the class.
boost::shared_ptr<isc::datasrc::ConfigurableClientList>
- getClientList(const isc::dns::RRClass& rrclass);
-
- /// \brief Returns a list of classes that have a client list.
- ///
- /// \return List of classes for which a non-NULL client list
- /// has been set by setClientList.
- std::vector<isc::dns::RRClass> getClientListClasses() const;
+ getDataSrcClientList(const isc::dns::RRClass& rrclass);
/// \brief Return a mutex for the client lists.
///
@@ -331,9 +352,9 @@ public:
/// is correct:
/// \code
/// {
- /// Mutex::Locker locker(auth->getClientListMutex());
+ /// Mutex::Locker locker(auth->getDataSrcClientListMutex());
/// boost::shared_ptr<isc::datasrc::ConfigurableClientList>
- /// list(auth->getClientList(RRClass::IN()));
+ /// list(auth->getDataSrcClientList(RRClass::IN()));
/// // Do some processing here
/// }
/// \endcode
@@ -342,8 +363,8 @@ public:
/// \code
/// boost::shared_ptr<isc::datasrc::ConfigurableClientList> list;
/// {
- /// Mutex::Locker locker(auth->getClientListMutex());
- /// list = auth->getClientList(RRClass::IN()));
+ /// Mutex::Locker locker(auth->getDataSrcClientListMutex());
+ /// list = auth->getDataSrcClientList(RRClass::IN()));
/// }
/// // Do some processing here
/// \endcode
@@ -352,7 +373,7 @@ public:
/// (lock) the mutex. It's because locking of the mutex is not really
/// a modification of the server object and it is needed to protect the
/// lists even on read-only operations.
- isc::util::thread::Mutex& getClientListMutex() const;
+ isc::util::thread::Mutex& getDataSrcClientListMutex() const;
/// \brief Sets the timeout for incoming TCP connections
///
diff --git a/src/bin/auth/benchmarks/Makefile.am b/src/bin/auth/benchmarks/Makefile.am
index fcfcb8a..c525b66 100644
--- a/src/bin/auth/benchmarks/Makefile.am
+++ b/src/bin/auth/benchmarks/Makefile.am
@@ -17,7 +17,7 @@ query_bench_SOURCES += ../auth_srv.h ../auth_srv.cc
query_bench_SOURCES += ../auth_config.h ../auth_config.cc
query_bench_SOURCES += ../statistics.h ../statistics.cc
query_bench_SOURCES += ../auth_log.h ../auth_log.cc
-query_bench_SOURCES += ../datasrc_configurator.h ../datasrc_configurator.cc
+query_bench_SOURCES += ../datasrc_config.h ../datasrc_config.cc
nodist_query_bench_SOURCES = ../auth_messages.h ../auth_messages.cc
diff --git a/src/bin/auth/benchmarks/query_bench.cc b/src/bin/auth/benchmarks/query_bench.cc
index 07111a5..44aeb8d 100644
--- a/src/bin/auth/benchmarks/query_bench.cc
+++ b/src/bin/auth/benchmarks/query_bench.cc
@@ -18,6 +18,8 @@
#include <bench/benchmark_util.h>
#include <util/buffer.h>
+#include <util/threads/sync.h>
+
#include <dns/message.h>
#include <dns/name.h>
#include <dns/question.h>
@@ -30,7 +32,7 @@
#include <auth/auth_srv.h>
#include <auth/auth_config.h>
-#include <auth/datasrc_configurator.h>
+#include <auth/datasrc_config.h>
#include <auth/query.h>
#include <asiodns/asiodns.h>
@@ -125,13 +127,15 @@ public:
OutputBuffer& buffer) :
QueryBenchMark(queries, query_message, buffer)
{
- configureDataSource(
- *server_,
- Element::fromJSON("{\"IN\":"
- " [{\"type\": \"sqlite3\","
- " \"params\": {"
- " \"database_file\": \"" +
- string(datasrc_file) + "\"}}]}"));
+ isc::util::thread::Mutex::Locker locker(
+ server_->getDataSrcClientListMutex());
+ server_->swapDataSrcClientLists(
+ configureDataSource(
+ Element::fromJSON("{\"IN\":"
+ " [{\"type\": \"sqlite3\","
+ " \"params\": {"
+ " \"database_file\": \"" +
+ string(datasrc_file) + "\"}}]}")));
}
};
@@ -144,14 +148,16 @@ public:
OutputBuffer& buffer) :
QueryBenchMark(queries, query_message, buffer)
{
- configureDataSource(
- *server_,
- Element::fromJSON("{\"IN\":"
- " [{\"type\": \"MasterFiles\","
- " \"cache-enable\": true, "
- " \"params\": {\"" +
- string(zone_origin) + "\": \"" +
- string(zone_file) + "\"}}]}"));
+ isc::util::thread::Mutex::Locker locker(
+ server_->getDataSrcClientListMutex());
+ server_->swapDataSrcClientLists(
+ configureDataSource(
+ Element::fromJSON("{\"IN\":"
+ " [{\"type\": \"MasterFiles\","
+ " \"cache-enable\": true, "
+ " \"params\": {\"" +
+ string(zone_origin) + "\": \"" +
+ string(zone_file) + "\"}}]}")));
}
};
diff --git a/src/bin/auth/command.cc b/src/bin/auth/command.cc
index 448a31b..efd60f2 100644
--- a/src/bin/auth/command.cc
+++ b/src/bin/auth/command.cc
@@ -21,7 +21,7 @@
#include <config/ccsession.h>
#include <exceptions/exceptions.h>
#include <dns/rrclass.h>
-#include <util/threads/lock.h>
+#include <util/threads/sync.h>
#include <string>
@@ -192,9 +192,10 @@ public:
// We're going to work with the client lists. They may be used
// from a different thread too, protect them.
- isc::util::thread::Mutex::Locker locker(server.getClientListMutex());
+ isc::util::thread::Mutex::Locker locker(
+ server.getDataSrcClientListMutex());
const boost::shared_ptr<isc::datasrc::ConfigurableClientList>
- list(server.getClientList(zone_class));
+ list(server.getDataSrcClientList(zone_class));
if (!list) {
isc_throw(AuthCommandError, "There's no client list for "
diff --git a/src/bin/auth/datasrc_config.cc b/src/bin/auth/datasrc_config.cc
new file mode 100644
index 0000000..62c3c7a
--- /dev/null
+++ b/src/bin/auth/datasrc_config.cc
@@ -0,0 +1,25 @@
+// Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <cc/data.h>
+#include "auth_srv.h"
+#include "datasrc_config.h"
+
+// This is a trivial specialization for the commonly used version.
+// Defined in .cc to avoid accidental creation of multiple copies.
+AuthSrv::DataSrcClientListsPtr
+configureDataSource(const isc::data::ConstElementPtr& config) {
+ return (configureDataSourceGeneric<
+ isc::datasrc::ConfigurableClientList>(config));
+}
diff --git a/src/bin/auth/datasrc_config.h b/src/bin/auth/datasrc_config.h
new file mode 100644
index 0000000..5707c6c
--- /dev/null
+++ b/src/bin/auth/datasrc_config.h
@@ -0,0 +1,83 @@
+// Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef DATASRC_CONFIG_H
+#define DATASRC_CONFIG_H
+
+#include "auth_srv.h"
+
+#include <cc/data.h>
+#include <datasrc/client_list.h>
+
+#include <boost/shared_ptr.hpp>
+
+#include <utility>
+#include <set>
+
+/// \brief Configure data source client lists
+///
+/// This will hook into the data_sources module configuration and it will
+/// return a new set (in the form of a shared pointer to map) of data source
+/// client lists corresponding to the configuration.
+///
+/// This function is templated. This is simply because of easier testing.
+/// You don't need to pay attention to it, use the configureDataSource
+/// specialization instead.
+///
+/// \note In future we may want to make the reconfiguration more efficient
+/// by only creating newly configured data and just moving the rest from
+/// the running configuration if they are used in the new configuration
+/// without any parameter change. We could probably do it by passing
+/// the old lists in addition to the new config, but further details are
+/// still to be defined yet. It will surely require changes in the
+/// data source library, too. So, right now, we don't introduce the
+/// possibility in the function interface. If and when we decide to introduce
+/// the optimization, we'll extend the interface.
+///
+/// \param config The configuration value to parse. It is in the form
+/// as an update from the config manager.
+/// \return A map from RR classes to configured lists.
+template<class List>
+boost::shared_ptr<std::map<isc::dns::RRClass,
+ boost::shared_ptr<List> > > // = ListMap below
+configureDataSourceGeneric(const isc::data::ConstElementPtr& config) {
+ typedef boost::shared_ptr<List> ListPtr;
+ typedef std::map<std::string, isc::data::ConstElementPtr> Map;
+ typedef std::map<isc::dns::RRClass, ListPtr> ListMap;
+
+ boost::shared_ptr<ListMap> new_lists(new ListMap);
+
+ // Go through the configuration and create corresponding list.
+ const Map& map(config->mapValue());
+ for (Map::const_iterator it(map.begin()); it != map.end(); ++it) {
+ const isc::dns::RRClass rrclass(it->first);
+ ListPtr list(new List(rrclass));
+ list->configure(it->second, true);
+ new_lists->insert(std::pair<isc::dns::RRClass, ListPtr>(rrclass,
+ list));
+ }
+
+ return (new_lists);
+}
+
+/// \brief Concrete version of configureDataSource() for the
+/// use with authoritative server implementation.
+AuthSrv::DataSrcClientListsPtr
+configureDataSource(const isc::data::ConstElementPtr& config);
+
+#endif // DATASRC_CONFIG_H
+
+// Local Variables:
+// mode: c++
+// End:
diff --git a/src/bin/auth/datasrc_configurator.cc b/src/bin/auth/datasrc_configurator.cc
deleted file mode 100644
index 5469389..0000000
--- a/src/bin/auth/datasrc_configurator.cc
+++ /dev/null
@@ -1,26 +0,0 @@
-// Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC")
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
-
-#include <cc/data.h>
-#include "auth_srv.h"
-#include "datasrc_configurator.h"
-
-// This is a trivial specialization for the commonly used version.
-// Defined in .cc to avoid accidental creation of multiple copies.
-void
-configureDataSource(AuthSrv& server, const isc::data::ConstElementPtr& config)
-{
- return (configureDataSourceGeneric<AuthSrv,
- isc::datasrc::ConfigurableClientList>(server, config));
-}
diff --git a/src/bin/auth/datasrc_configurator.h b/src/bin/auth/datasrc_configurator.h
deleted file mode 100644
index b8d21f3..0000000
--- a/src/bin/auth/datasrc_configurator.h
+++ /dev/null
@@ -1,120 +0,0 @@
-// Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC")
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
-
-#ifndef DATASRC_CONFIGURATOR_H
-#define DATASRC_CONFIGURATOR_H
-
-#include "auth_srv.h"
-
-#include <cc/data.h>
-#include <datasrc/client_list.h>
-#include <util/threads/lock.h>
-
-#include <boost/shared_ptr.hpp>
-
-#include <set>
-
-/// \brief Configure the authoritative server's data source lists
-///
-/// This will hook into the data_sources module configuration and it will
-/// keep the local copy of data source clients in the list in the authoritative
-/// server.
-///
-/// This function is templated. This is simply because of easier testing.
-/// You don't need to pay attention to it, use the configureDataSource
-/// specialization instead.
-///
-/// \param server It is the server to configure.
-/// \param config The configuration value to parse. It is in the form
-/// as an update from the config manager.
-template<class Server, class List>
-void
-configureDataSourceGeneric(Server& server,
- const isc::data::ConstElementPtr& config)
-{
- typedef boost::shared_ptr<List> ListPtr;
- typedef std::map<std::string, isc::data::ConstElementPtr> Map;
- typedef std::pair<isc::dns::RRClass, ListPtr> RollbackPair;
- typedef std::pair<isc::dns::RRClass, isc::data::ConstElementPtr>
- RollbackConfiguration;
-
- // Lock the client lists, we're going to manipulate them.
- isc::util::thread::Mutex::Locker locker(server.getClientListMutex());
-
- // Some structures to be able to perform a rollback
- std::vector<RollbackPair> rollback_sets;
- std::vector<RollbackConfiguration> rollback_configurations;
- try {
- // Get the configuration and current state.
- const Map& map(config->mapValue());
- const std::vector<isc::dns::RRClass>
- activeVector(server.getClientListClasses());
- std::set<isc::dns::RRClass> active(activeVector.begin(),
- activeVector.end());
- // Go through the configuration and change everything.
- for (Map::const_iterator it(map.begin()); it != map.end(); ++it) {
- const isc::dns::RRClass rrclass(it->first);
- active.erase(rrclass);
- ListPtr list(server.getClientList(rrclass));
- bool need_set(false);
- if (list) {
- rollback_configurations.
- push_back(RollbackConfiguration(rrclass,
- list->getConfiguration()));
- } else {
- list.reset(new List(rrclass));
- need_set = true;
- rollback_sets.push_back(RollbackPair(rrclass, ListPtr()));
- }
- list->configure(it->second, true);
- if (need_set) {
- server.setClientList(rrclass, list);
- }
- }
- // Remove the ones that are not in the configuration.
- for (std::set<isc::dns::RRClass>::iterator it(active.begin());
- it != active.end(); ++it) {
- // There seems to be no way the setClientList could throw.
- // But this is just to make sure in case it did to restore
- // the original.
- rollback_sets.push_back(
- RollbackPair(*it, server.getClientList(*it)));
- server.setClientList(*it, ListPtr());
- }
- } catch (...) {
- // Perform a rollback of the changes. The old configuration should
- // work.
- for (typename std::vector<RollbackPair>::const_iterator
- it(rollback_sets.begin()); it != rollback_sets.end(); ++it) {
- server.setClientList(it->first, it->second);
- }
- for (typename std::vector<RollbackConfiguration>::const_iterator
- it(rollback_configurations.begin());
- it != rollback_configurations.end(); ++it) {
- server.getClientList(it->first)->configure(it->second, true);
- }
- throw;
- }
-}
-
-/// \brief Concrete version of DataSourceConfiguratorGeneric for the
-/// use with authoritative server implementation.
-void
-configureDataSource(AuthSrv& server, const isc::data::ConstElementPtr& config);
-
-#endif
-
-// Local Variables:
-// mode: c++
-// End:
diff --git a/src/bin/auth/main.cc b/src/bin/auth/main.cc
index eecc5aa..27ac04a 100644
--- a/src/bin/auth/main.cc
+++ b/src/bin/auth/main.cc
@@ -18,6 +18,7 @@
#include <util/buffer.h>
#include <util/io/socketsession.h>
+#include <util/threads/sync.h>
#include <dns/message.h>
#include <dns/messagerenderer.h>
@@ -34,7 +35,7 @@
#include <auth/command.h>
#include <auth/auth_srv.h>
#include <auth/auth_log.h>
-#include <auth/datasrc_configurator.h>
+#include <auth/datasrc_config.h>
#include <asiodns/asiodns.h>
#include <asiolink/asiolink.h>
#include <log/logger_support.h>
@@ -42,7 +43,6 @@
#include <server_common/socket_request.h>
#include <boost/bind.hpp>
-#include <boost/scoped_ptr.hpp>
#include <sys/types.h>
#include <sys/socket.h>
@@ -87,13 +87,38 @@ my_command_handler(const string& command, ConstElementPtr args) {
}
void
-datasrcConfigHandler(AuthSrv* server, const std::string&,
+datasrcConfigHandler(AuthSrv* server, bool* first_time,
+ ModuleCCSession* config_session, const std::string&,
isc::data::ConstElementPtr config,
const isc::config::ConfigData&)
{
assert(server != NULL);
if (config->contains("classes")) {
- configureDataSource(*server, config->get("classes"));
+ AuthSrv::DataSrcClientListsPtr lists;
+
+ if (*first_time) {
+ // HACK: The default is not passed to the handler in the first
+ // callback. This one will get the default (or, current value).
+ // Further updates will work the usual way.
+ assert(config_session != NULL);
+ *first_time = false;
+ lists = configureDataSource(
+ config_session->getRemoteConfigValue("data_sources",
+ "classes"));
+ } else {
+ lists = configureDataSource(config->get("classes"));
+ }
+
+ // Replace the server's lists. The returned lists will be stored
+ // in a local variable 'lists', and will be destroyed outside of
+ // the temporary block for the lock scope. That way we can minimize
+ // the range of the critical section.
+ {
+ isc::util::thread::Mutex::Locker locker(
+ server->getDataSrcClientListMutex());
+ lists = server->swapDataSrcClientLists(lists);
+ }
+ // The previous lists are destroyed here.
}
}
@@ -205,19 +230,16 @@ main(int argc, char* argv[]) {
isc::server_common::initKeyring(*config_session);
auth_server->setTSIGKeyRing(&isc::server_common::keyring);
- // Start the data source configuration
+ // Start the data source configuration. We pass first_time and
+ // config_session for the hack described in datasrcConfigHandler.
+ bool first_time = true;
config_session->addRemoteConfig("data_sources",
boost::bind(datasrcConfigHandler,
- auth_server,
+ auth_server, &first_time,
+ config_session,
_1, _2, _3),
false);
- // HACK: The default is not passed to the handler. This one will
- // get the default (or, current value). Further updates will work
- // the usual way.
- configureDataSource(*auth_server,
- config_session->getRemoteConfigValue("data_sources", "classes"));
-
// Now start asynchronous read.
config_session->start();
LOG_DEBUG(auth_logger, DBG_AUTH_START, AUTH_CONFIG_CHANNEL_STARTED);
diff --git a/src/bin/auth/tests/Makefile.am b/src/bin/auth/tests/Makefile.am
index 3b18736..6b9d385 100644
--- a/src/bin/auth/tests/Makefile.am
+++ b/src/bin/auth/tests/Makefile.am
@@ -42,7 +42,7 @@ run_unittests_SOURCES += ../auth_config.h ../auth_config.cc
run_unittests_SOURCES += ../command.h ../command.cc
run_unittests_SOURCES += ../common.h ../common.cc
run_unittests_SOURCES += ../statistics.h ../statistics.cc
-run_unittests_SOURCES += ../datasrc_configurator.h ../datasrc_configurator.cc
+run_unittests_SOURCES += ../datasrc_config.h ../datasrc_config.cc
run_unittests_SOURCES += datasrc_util.h datasrc_util.cc
run_unittests_SOURCES += auth_srv_unittest.cc
run_unittests_SOURCES += config_unittest.cc
@@ -51,7 +51,7 @@ run_unittests_SOURCES += command_unittest.cc
run_unittests_SOURCES += common_unittest.cc
run_unittests_SOURCES += query_unittest.cc
run_unittests_SOURCES += statistics_unittest.cc
-run_unittests_SOURCES += datasrc_configurator_unittest.cc
+run_unittests_SOURCES += datasrc_config_unittest.cc
run_unittests_SOURCES += run_unittests.cc
nodist_run_unittests_SOURCES = ../auth_messages.h ../auth_messages.cc
diff --git a/src/bin/auth/tests/auth_srv_unittest.cc b/src/bin/auth/tests/auth_srv_unittest.cc
index 00d7350..396b247 100644
--- a/src/bin/auth/tests/auth_srv_unittest.cc
+++ b/src/bin/auth/tests/auth_srv_unittest.cc
@@ -36,10 +36,10 @@
#include <auth/command.h>
#include <auth/common.h>
#include <auth/statistics.h>
-#include <auth/datasrc_configurator.h>
+#include <auth/datasrc_config.h>
#include <util/unittests/mock_socketsession.h>
-#include <util/threads/lock.h>
+#include <util/threads/sync.h>
#include <dns/tests/unittest_util.h>
#include <testutils/dnsmessage_test.h>
#include <testutils/srv_test.h>
@@ -63,6 +63,7 @@
using namespace std;
using namespace isc::cc;
using namespace isc::dns;
+using namespace isc::datasrc;
using namespace isc::util;
using namespace isc::util::io::internal;
using namespace isc::util::unittests;
@@ -90,6 +91,9 @@ const char* const STATIC_DSRC_FILE = DSRC_DIR "/static.zone";
// a signed example zone.
const char* const CONFIG_INMEMORY_EXAMPLE = TEST_DATA_DIR "/rfc5155-example.zone.signed";
+// shortcut commonly used in tests
+typedef boost::shared_ptr<ConfigurableClientList> ListPtr;
+
class AuthSrvTest : public SrvTestBase {
protected:
AuthSrvTest() :
@@ -722,13 +726,21 @@ TEST_F(AuthSrvTest, notifyWithSessionMessageError) {
}
void
+installDataSrcClientLists(AuthSrv& server,
+ AuthSrv::DataSrcClientListsPtr lists)
+{
+ thread::Mutex::Locker locker(server.getDataSrcClientListMutex());
+ server.swapDataSrcClientLists(lists);
+}
+
+void
updateDatabase(AuthSrv& server, const char* params) {
const ConstElementPtr config(Element::fromJSON("{"
"\"IN\": [{"
" \"type\": \"sqlite3\","
" \"params\": " + string(params) +
"}]}"));
- configureDataSource(server, config);
+ installDataSrcClientLists(server, configureDataSource(config));
}
void
@@ -745,7 +757,7 @@ updateInMemory(AuthSrv& server, const char* origin, const char* filename) {
" \"type\": \"static\","
" \"params\": \"" + string(STATIC_DSRC_FILE) + "\""
"}]}"));
- configureDataSource(server, config);
+ installDataSrcClientLists(server, configureDataSource(config));
}
void
@@ -755,7 +767,7 @@ updateBuiltin(AuthSrv& server) {
" \"type\": \"static\","
" \"params\": \"" + string(STATIC_DSRC_FILE) + "\""
"}]}"));
- configureDataSource(server, config);
+ installDataSrcClientLists(server, configureDataSource(config));
}
// Try giving the server a TSIG signed request and see it can anwer signed as
@@ -953,7 +965,7 @@ TEST_F(AuthSrvTest, updateWithInMemoryClient) {
" \"params\": {},"
" \"cache-enable\": true"
"}]}"));
- configureDataSource(server, config);
+ installDataSrcClientLists(server, configureDataSource(config));
// after successful configuration, we should have one (with empty zoneset).
// The memory data source is empty, should return REFUSED rcode.
@@ -1287,7 +1299,7 @@ public:
if (fake_rrset_ && fake_rrset_->getName() == name &&
fake_rrset_->getType() == type)
{
- return (ZoneFinderContextPtr(new ZoneFinder::Context(
+ return (ZoneFinderContextPtr(new ZoneFinder::GenericContext(
*this, options,
ResultContext(SUCCESS,
fake_rrset_))));
@@ -1427,11 +1439,14 @@ TEST_F(AuthSrvTest,
// Set real inmem client to proxy
updateInMemory(server, "example.", CONFIG_INMEMORY_EXAMPLE);
{
- isc::util::thread::Mutex::Locker locker(server.getClientListMutex());
+ isc::util::thread::Mutex::Locker locker(
+ server.getDataSrcClientListMutex());
boost::shared_ptr<isc::datasrc::ConfigurableClientList>
- list(new FakeList(server.getClientList(RRClass::IN()), THROW_NEVER,
- false));
- server.setClientList(RRClass::IN(), list);
+ list(new FakeList(server.getDataSrcClientList(RRClass::IN()),
+ THROW_NEVER, false));
+ AuthSrv::DataSrcClientListsPtr lists(new std::map<RRClass, ListPtr>);
+ lists->insert(pair<RRClass, ListPtr>(RRClass::IN(), list));
+ server.swapDataSrcClientLists(lists);
}
createDataFromFile("nsec3query_nodnssec_fromWire.wire");
@@ -1455,11 +1470,14 @@ setupThrow(AuthSrv& server, ThrowWhen throw_when, bool isc_exception,
{
updateInMemory(server, "example.", CONFIG_INMEMORY_EXAMPLE);
- isc::util::thread::Mutex::Locker locker(server.getClientListMutex());
+ isc::util::thread::Mutex::Locker locker(
+ server.getDataSrcClientListMutex());
boost::shared_ptr<isc::datasrc::ConfigurableClientList>
- list(new FakeList(server.getClientList(RRClass::IN()), throw_when,
- isc_exception, rrset));
- server.setClientList(RRClass::IN(), list);
+ list(new FakeList(server.getDataSrcClientList(RRClass::IN()),
+ throw_when, isc_exception, rrset));
+ AuthSrv::DataSrcClientListsPtr lists(new std::map<RRClass, ListPtr>);
+ lists->insert(pair<RRClass, ListPtr>(RRClass::IN(), list));
+ server.swapDataSrcClientLists(lists);
}
TEST_F(AuthSrvTest,
@@ -1771,46 +1789,51 @@ TEST_F(AuthSrvTest, clientList) {
// We need to lock the mutex to make the (get|set)ClientList happy.
// There's a debug-build only check in them to make sure everything
// locks them and we call them directly here.
- isc::util::thread::Mutex::Locker locker(server.getClientListMutex());
+ isc::util::thread::Mutex::Locker locker(
+ server.getDataSrcClientListMutex());
+
+ AuthSrv::DataSrcClientListsPtr lists; // initially empty
+
// The lists don't exist. Therefore, the list of RRClasses is empty.
- // We also have no IN list.
- EXPECT_TRUE(server.getClientListClasses().empty());
- EXPECT_EQ(boost::shared_ptr<const isc::datasrc::ClientList>(),
- server.getClientList(RRClass::IN()));
+ EXPECT_TRUE(server.swapDataSrcClientLists(lists)->empty());
+
// Put something in.
- const boost::shared_ptr<isc::datasrc::ConfigurableClientList>
- list(new isc::datasrc::ConfigurableClientList(RRClass::IN()));
- const boost::shared_ptr<isc::datasrc::ConfigurableClientList>
- list2(new isc::datasrc::ConfigurableClientList(RRClass::CH()));
- server.setClientList(RRClass::IN(), list);
- server.setClientList(RRClass::CH(), list2);
- // There are two things in the list and they are IN and CH
- vector<RRClass> classes(server.getClientListClasses());
- ASSERT_EQ(2, classes.size());
- EXPECT_EQ(RRClass::IN(), classes[0]);
- EXPECT_EQ(RRClass::CH(), classes[1]);
+ const ListPtr list(new ConfigurableClientList(RRClass::IN()));
+ const ListPtr list2(new ConfigurableClientList(RRClass::CH()));
+
+ lists.reset(new std::map<RRClass, ListPtr>);
+ lists->insert(pair<RRClass, ListPtr>(RRClass::IN(), list));
+ lists->insert(pair<RRClass, ListPtr>(RRClass::CH(), list2));
+ server.swapDataSrcClientLists(lists);
+
// And the lists can be retrieved.
- EXPECT_EQ(list, server.getClientList(RRClass::IN()));
- EXPECT_EQ(list2, server.getClientList(RRClass::CH()));
- // Remove one of them
- server.setClientList(RRClass::CH(),
- boost::shared_ptr<isc::datasrc::ConfigurableClientList>());
- // This really got deleted, including the class.
- classes = server.getClientListClasses();
- ASSERT_EQ(1, classes.size());
- EXPECT_EQ(RRClass::IN(), classes[0]);
- EXPECT_EQ(list, server.getClientList(RRClass::IN()));
+ EXPECT_EQ(list, server.getDataSrcClientList(RRClass::IN()));
+ EXPECT_EQ(list2, server.getDataSrcClientList(RRClass::CH()));
+
+ // Replace the lists with new lists containing only one list.
+ lists.reset(new std::map<RRClass, ListPtr>);
+ lists->insert(pair<RRClass, ListPtr>(RRClass::IN(), list));
+ lists = server.swapDataSrcClientLists(lists);
+
+ // Old one had two lists. That confirms our swap for IN and CH classes
+ // (i.e., no other entries were there).
+ EXPECT_EQ(2, lists->size());
+
+ // The CH list really got deleted.
+ EXPECT_EQ(list, server.getDataSrcClientList(RRClass::IN()));
+ EXPECT_FALSE(server.getDataSrcClientList(RRClass::CH()));
}
// We just test the mutex can be locked (exactly once).
TEST_F(AuthSrvTest, mutex) {
- isc::util::thread::Mutex::Locker l1(server.getClientListMutex());
+ isc::util::thread::Mutex::Locker l1(server.getDataSrcClientListMutex());
// TODO: Once we have non-debug build, this one will not work, since
// we currently use the fact that we can't lock twice from the same
// thread. In the non-debug mode, this would deadlock.
// Skip then.
EXPECT_THROW({
- isc::util::thread::Mutex::Locker l2(server.getClientListMutex());
+ isc::util::thread::Mutex::Locker l2(
+ server.getDataSrcClientListMutex());
}, isc::InvalidOperation);
}
diff --git a/src/bin/auth/tests/command_unittest.cc b/src/bin/auth/tests/command_unittest.cc
index c746526..a245c3b 100644
--- a/src/bin/auth/tests/command_unittest.cc
+++ b/src/bin/auth/tests/command_unittest.cc
@@ -16,10 +16,12 @@
#include "datasrc_util.h"
+#include <util/threads/sync.h>
+
#include <auth/auth_srv.h>
#include <auth/auth_config.h>
#include <auth/command.h>
-#include <auth/datasrc_configurator.h>
+#include <auth/datasrc_config.h>
#include <dns/name.h>
#include <dns/rrclass.h>
@@ -174,22 +176,32 @@ TEST_F(AuthCommandTest, shutdownIncorrectPID) {
// zones, and checks the zones are correctly loaded.
void
zoneChecks(AuthSrv& server) {
- isc::util::thread::Mutex::Locker locker(server.getClientListMutex());
- EXPECT_EQ(ZoneFinder::SUCCESS, server.getClientList(RRClass::IN())->
+ isc::util::thread::Mutex::Locker locker(
+ server.getDataSrcClientListMutex());
+ EXPECT_EQ(ZoneFinder::SUCCESS, server.getDataSrcClientList(RRClass::IN())->
find(Name("ns.test1.example")).finder_->
find(Name("ns.test1.example"), RRType::A())->code);
- EXPECT_EQ(ZoneFinder::NXRRSET, server.getClientList(RRClass::IN())->
+ EXPECT_EQ(ZoneFinder::NXRRSET, server.getDataSrcClientList(RRClass::IN())->
find(Name("ns.test1.example")).finder_->
find(Name("ns.test1.example"), RRType::AAAA())->code);
- EXPECT_EQ(ZoneFinder::SUCCESS, server.getClientList(RRClass::IN())->
+ EXPECT_EQ(ZoneFinder::SUCCESS, server.getDataSrcClientList(RRClass::IN())->
find(Name("ns.test2.example")).finder_->
find(Name("ns.test2.example"), RRType::A())->code);
- EXPECT_EQ(ZoneFinder::NXRRSET, server.getClientList(RRClass::IN())->
+ EXPECT_EQ(ZoneFinder::NXRRSET, server.getDataSrcClientList(RRClass::IN())->
find(Name("ns.test2.example")).finder_->
find(Name("ns.test2.example"), RRType::AAAA())->code);
}
void
+installDataSrcClientLists(AuthSrv& server,
+ AuthSrv::DataSrcClientListsPtr lists)
+{
+ isc::util::thread::Mutex::Locker locker(
+ server.getDataSrcClientListMutex());
+ server.swapDataSrcClientLists(lists);
+}
+
+void
configureZones(AuthSrv& server) {
ASSERT_EQ(0, system(INSTALL_PROG " -c " TEST_DATA_DIR "/test1.zone.in "
TEST_DATA_BUILDDIR "/test1.zone.copied"));
@@ -208,27 +220,29 @@ configureZones(AuthSrv& server) {
" \"cache-enable\": true"
"}]}"));
- configureDataSource(server, config);
+ installDataSrcClientLists(server, configureDataSource(config));
zoneChecks(server);
}
void
newZoneChecks(AuthSrv& server) {
- isc::util::thread::Mutex::Locker locker(server.getClientListMutex());
- EXPECT_EQ(ZoneFinder::SUCCESS, server.getClientList(RRClass::IN())->
+ isc::util::thread::Mutex::Locker locker(
+ server.getDataSrcClientListMutex());
+ EXPECT_EQ(ZoneFinder::SUCCESS, server.getDataSrcClientList(RRClass::IN())->
find(Name("ns.test1.example")).finder_->
find(Name("ns.test1.example"), RRType::A())->code);
// now test1.example should have ns/AAAA
- EXPECT_EQ(ZoneFinder::SUCCESS, server.getClientList(RRClass::IN())->
+ EXPECT_EQ(ZoneFinder::SUCCESS, server.getDataSrcClientList(RRClass::IN())->
find(Name("ns.test1.example")).finder_->
find(Name("ns.test1.example"), RRType::AAAA())->code);
// test2.example shouldn't change
- EXPECT_EQ(ZoneFinder::SUCCESS, server.getClientList(RRClass::IN())->
+ EXPECT_EQ(ZoneFinder::SUCCESS, server.getDataSrcClientList(RRClass::IN())->
find(Name("ns.test2.example")).finder_->
find(Name("ns.test2.example"), RRType::A())->code);
- EXPECT_EQ(ZoneFinder::NXRRSET, server.getClientList(RRClass::IN())->
+ EXPECT_EQ(ZoneFinder::NXRRSET,
+ server.getDataSrcClientList(RRClass::IN())->
find(Name("ns.test2.example")).finder_->
find(Name("ns.test2.example"), RRType::AAAA())->code);
}
@@ -271,12 +285,14 @@ TEST_F(AuthCommandTest,
" \"cache-enable\": true,"
" \"cache-zones\": [\"example.org\"]"
"}]}"));
- configureDataSource(server_, config);
+ installDataSrcClientLists(server_, configureDataSource(config));
{
- isc::util::thread::Mutex::Locker locker(server_.getClientListMutex());
+ isc::util::thread::Mutex::Locker locker(
+ server_.getDataSrcClientListMutex());
// Check that the A record at www.example.org does not exist
- EXPECT_EQ(ZoneFinder::NXDOMAIN, server_.getClientList(RRClass::IN())->
+ EXPECT_EQ(ZoneFinder::NXDOMAIN,
+ server_.getDataSrcClientList(RRClass::IN())->
find(Name("example.org")).finder_->
find(Name("www.example.org"), RRType::A())->code);
@@ -296,7 +312,8 @@ TEST_F(AuthCommandTest,
sql_updater->addRRset(*rrset);
sql_updater->commit();
- EXPECT_EQ(ZoneFinder::NXDOMAIN, server_.getClientList(RRClass::IN())->
+ EXPECT_EQ(ZoneFinder::NXDOMAIN,
+ server_.getDataSrcClientList(RRClass::IN())->
find(Name("example.org")).finder_->
find(Name("www.example.org"), RRType::A())->code);
}
@@ -308,9 +325,11 @@ TEST_F(AuthCommandTest,
checkAnswer(0, "Successful load");
{
- isc::util::thread::Mutex::Locker locker(server_.getClientListMutex());
+ isc::util::thread::Mutex::Locker locker(
+ server_.getDataSrcClientListMutex());
// And now it should be present too.
- EXPECT_EQ(ZoneFinder::SUCCESS, server_.getClientList(RRClass::IN())->
+ EXPECT_EQ(ZoneFinder::SUCCESS,
+ server_.getDataSrcClientList(RRClass::IN())->
find(Name("example.org")).finder_->
find(Name("www.example.org"), RRType::A())->code);
}
@@ -321,9 +340,11 @@ TEST_F(AuthCommandTest,
checkAnswer(1, "example.com");
{
- isc::util::thread::Mutex::Locker locker(server_.getClientListMutex());
+ isc::util::thread::Mutex::Locker locker(
+ server_.getDataSrcClientListMutex());
// The previous zone is not hurt in any way
- EXPECT_EQ(ZoneFinder::SUCCESS, server_.getClientList(RRClass::IN())->
+ EXPECT_EQ(ZoneFinder::SUCCESS,
+ server_.getDataSrcClientList(RRClass::IN())->
find(Name("example.org")).finder_->
find(Name("example.org"), RRType::SOA())->code);
}
@@ -335,16 +356,18 @@ TEST_F(AuthCommandTest,
" \"cache-enable\": true,"
" \"cache-zones\": [\"example.com\"]"
"}]}"));
- EXPECT_THROW(configureDataSource(server_, config2),
+ EXPECT_THROW(configureDataSource(config2),
ConfigurableClientList::ConfigurationError);
result_ = execAuthServerCommand(server_, "loadzone",
Element::fromJSON("{\"origin\": \"example.com\"}"));
checkAnswer(1, "Unreadable");
- isc::util::thread::Mutex::Locker locker(server_.getClientListMutex());
+ isc::util::thread::Mutex::Locker locker(
+ server_.getDataSrcClientListMutex());
// The previous zone is not hurt in any way
- EXPECT_EQ(ZoneFinder::SUCCESS, server_.getClientList(RRClass::IN())->
+ EXPECT_EQ(ZoneFinder::SUCCESS,
+ server_.getDataSrcClientList(RRClass::IN())->
find(Name("example.org")).finder_->
find(Name("example.org"), RRType::SOA())->code);
}
diff --git a/src/bin/auth/tests/datasrc_config_unittest.cc b/src/bin/auth/tests/datasrc_config_unittest.cc
new file mode 100644
index 0000000..2dc70d1
--- /dev/null
+++ b/src/bin/auth/tests/datasrc_config_unittest.cc
@@ -0,0 +1,305 @@
+// Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <auth/datasrc_config.h>
+
+#include <config/tests/fake_session.h>
+#include <config/ccsession.h>
+#include <util/threads/sync.h>
+
+#include <gtest/gtest.h>
+
+#include <boost/bind.hpp>
+#include <boost/shared_ptr.hpp>
+
+#include <memory>
+
+using namespace isc;
+using namespace isc::cc;
+using namespace isc::config;
+using namespace isc::data;
+using namespace isc::dns;
+using namespace std;
+using namespace boost;
+
+namespace {
+
+class DatasrcConfigTest;
+
+class FakeList {
+public:
+ FakeList(const RRClass&) :
+ configuration_(new ListElement)
+ {}
+ void configure(const ConstElementPtr& configuration, bool allow_cache) {
+ EXPECT_TRUE(allow_cache);
+ conf_ = configuration->get(0)->get("type")->stringValue();
+ configuration_ = configuration;
+ }
+ const string& getConf() const {
+ return (conf_);
+ }
+ ConstElementPtr getConfiguration() const {
+ return (configuration_);
+ }
+private:
+ string conf_;
+ ConstElementPtr configuration_;
+};
+
+typedef shared_ptr<FakeList> ListPtr;
+
+// Forward declaration. We need precise definition of DatasrcConfigTest
+// to complete this function.
+void
+testConfigureDataSource(DatasrcConfigTest& test,
+ const isc::data::ConstElementPtr& config);
+
+void
+datasrcConfigHandler(DatasrcConfigTest* fake_server, const std::string&,
+ isc::data::ConstElementPtr config,
+ const isc::config::ConfigData&)
+{
+ if (config->contains("classes")) {
+ testConfigureDataSource(*fake_server, config->get("classes"));
+ }
+}
+
+class DatasrcConfigTest : public ::testing::Test {
+public:
+ // These pretend to be the server
+ isc::util::thread::Mutex& getDataSrcClientListMutex() const {
+ return (mutex_);
+ }
+ void swapDataSrcClientLists(shared_ptr<std::map<dns::RRClass, ListPtr> >
+ new_lists)
+ {
+ lists_.clear(); // first empty it
+
+ // Record the operation and results. Note that map elements are
+ // sorted by RRClass, so the ordering should be predictable.
+ for (std::map<dns::RRClass, ListPtr>::const_iterator it =
+ new_lists->begin();
+ it != new_lists->end();
+ ++it)
+ {
+ const RRClass rrclass = it->first;
+ ListPtr list = it->second;
+ log_ += "set " + rrclass.toText() + " " +
+ (list ? list->getConf() : "") + "\n";
+ lists_[rrclass] = list;
+ }
+ }
+
+protected:
+ DatasrcConfigTest() :
+ session(ElementPtr(new ListElement), ElementPtr(new ListElement),
+ ElementPtr(new ListElement)),
+ specfile(string(TEST_OWN_DATA_DIR) + "/spec.spec")
+ {
+ initSession();
+ }
+ void initSession() {
+ session.getMessages()->add(createAnswer());
+ mccs.reset(new ModuleCCSession(specfile, session, NULL, NULL, false,
+ false));
+ }
+ void TearDown() {
+ // Make sure no matter what we did, it is cleaned up. Also check
+ // we really have subscribed to the configuration, and after removing
+ // it we actually cancel it.
+ EXPECT_TRUE(session.haveSubscription("data_sources", "*"));
+ mccs->removeRemoteConfig("data_sources");
+ EXPECT_FALSE(session.haveSubscription("data_sources", "*"));
+ }
+ void SetUp() {
+ session.getMessages()->
+ add(createAnswer(0,
+ moduleSpecFromFile(string(PLUGIN_DATA_PATH) +
+ "/datasrc.spec").
+ getFullSpec()));
+ session.getMessages()->add(createAnswer(0,
+ ElementPtr(new MapElement)));
+ mccs->addRemoteConfig("data_sources",
+ boost::bind(datasrcConfigHandler,
+ this, _1, _2, _3), false);
+ }
+ ElementPtr buildConfig(const string& config) const {
+ const ElementPtr internal(Element::fromJSON(config));
+ const ElementPtr external(Element::fromJSON("{\"version\": 1}"));
+ external->set("classes", internal);
+ return (external);
+ }
+ void initializeINList() {
+ const ConstElementPtr
+ config(buildConfig("{\"IN\": [{\"type\": \"xxx\"}]}"));
+ session.addMessage(createCommand("config_update", config),
+ "data_sources", "*");
+ mccs->checkCommand();
+ // Check that the passed config is stored.
+ EXPECT_EQ("set IN xxx\n", log_);
+ EXPECT_EQ(1, lists_.size());
+ }
+ FakeSession session;
+ auto_ptr<ModuleCCSession> mccs;
+ const string specfile;
+ std::map<RRClass, ListPtr> lists_;
+ string log_;
+ mutable isc::util::thread::Mutex mutex_;
+};
+
+void
+testConfigureDataSource(DatasrcConfigTest& test,
+ const isc::data::ConstElementPtr& config)
+{
+ // We use customized (faked lists) for the List type. This makes it
+ // possible to easily look that they were called.
+ shared_ptr<std::map<dns::RRClass, ListPtr> > lists =
+ configureDataSourceGeneric<FakeList>(config);
+ test.swapDataSrcClientLists(lists);
+}
+
+// Push there a configuration with a single list.
+TEST_F(DatasrcConfigTest, createList) {
+ initializeINList();
+}
+
+TEST_F(DatasrcConfigTest, modifyList) {
+ // First, initialize the list, and confirm the current config
+ initializeINList();
+ EXPECT_EQ("xxx", lists_[RRClass::IN()]->getConf());
+
+ // And now change the configuration of the list
+ const ElementPtr
+ config(buildConfig("{\"IN\": [{\"type\": \"yyy\"}]}"));
+ session.addMessage(createCommand("config_update", config), "data_sources",
+ "*");
+ log_ = "";
+ mccs->checkCommand();
+ // Now the new one should be installed.
+ EXPECT_EQ("yyy", lists_[RRClass::IN()]->getConf());
+ EXPECT_EQ(1, lists_.size());
+}
+
+// Check we can have multiple lists at once
+TEST_F(DatasrcConfigTest, multiple) {
+ const ElementPtr
+ config(buildConfig("{\"IN\": [{\"type\": \"yyy\"}], "
+ "\"CH\": [{\"type\": \"xxx\"}]}"));
+ session.addMessage(createCommand("config_update", config), "data_sources",
+ "*");
+ mccs->checkCommand();
+ // We have set commands for both classes.
+ EXPECT_EQ("set IN yyy\nset CH xxx\n", log_);
+ // We should have both there
+ EXPECT_EQ("yyy", lists_[RRClass::IN()]->getConf());
+ EXPECT_EQ("xxx", lists_[RRClass::CH()]->getConf());
+ EXPECT_EQ(2, lists_.size());
+}
+
+// Check we can add another one later and the old one does not get
+// overwritten.
+//
+// It's almost like above, but we initialize first with single-list
+// config.
+TEST_F(DatasrcConfigTest, updateAdd) {
+ initializeINList();
+ const ElementPtr
+ config(buildConfig("{\"IN\": [{\"type\": \"yyy\"}], "
+ "\"CH\": [{\"type\": \"xxx\"}]}"));
+ session.addMessage(createCommand("config_update", config), "data_sources",
+ "*");
+ log_ = "";
+ mccs->checkCommand();
+ EXPECT_EQ("set IN yyy\nset CH xxx\n", log_);
+ EXPECT_EQ("xxx", lists_[RRClass::CH()]->getConf());
+ EXPECT_EQ("yyy", lists_[RRClass::IN()]->getConf());
+ EXPECT_EQ(2, lists_.size());
+}
+
+// We delete a class list in this test.
+TEST_F(DatasrcConfigTest, updateDelete) {
+ initializeINList();
+ const ElementPtr
+ config(buildConfig("{}"));
+ session.addMessage(createCommand("config_update", config), "data_sources",
+ "*");
+ log_ = "";
+ mccs->checkCommand();
+
+ // No operation takes place in the configuration, and the old one is
+ // just dropped
+ EXPECT_EQ("", log_);
+ EXPECT_TRUE(lists_.empty());
+}
+
+// Check that broken new configuration doesn't break the running configuration.
+TEST_F(DatasrcConfigTest, brokenConfigForAdd) {
+ initializeINList();
+ // The configuration is wrong. However, the CH one will be handled
+ // without an error first.
+ const ElementPtr
+ config(buildConfig("{\"IN\": [{\"type\": 13}], "
+ "\"CH\": [{\"type\": \"xxx\"}]}"));
+ session.addMessage(createCommand("config_update", config), "data_sources",
+ "*");
+ log_ = "";
+ // It does not throw, as it is handled in the ModuleCCSession.
+ // Throwing from the reconfigure is checked in other tests.
+ EXPECT_NO_THROW(mccs->checkCommand());
+ // Anyway, the result should not contain CH now and the original IN should
+ // be there.
+ EXPECT_EQ("xxx", lists_[RRClass::IN()]->getConf());
+ EXPECT_FALSE(lists_[RRClass::CH()]);
+}
+
+// Similar to the previous one, but the broken config would delete part of
+// the running config.
+TEST_F(DatasrcConfigTest, brokenConfigForDelete) {
+ initializeINList();
+ // Put the CH there
+ const ElementPtr
+ config1(Element::fromJSON("{\"IN\": [{\"type\": \"yyy\"}], "
+ "\"CH\": [{\"type\": \"xxx\"}]}"));
+ testConfigureDataSource(*this, config1);
+ const ElementPtr
+ config2(Element::fromJSON("{\"IN\": [{\"type\": 13}]}"));
+ // This would delete CH. However, the new config is broken, so it won't
+ // actually apply.
+ EXPECT_THROW(testConfigureDataSource(*this, config2), TypeError);
+ EXPECT_EQ("yyy", lists_[RRClass::IN()]->getConf());
+ EXPECT_EQ("xxx", lists_[RRClass::CH()]->getConf());
+}
+
+// Similar to the previous cases, but the broken config would modify the
+// running config of one of the classes.
+TEST_F(DatasrcConfigTest, brokenConfigForModify) {
+ initializeINList();
+ // Put the CH there
+ const ElementPtr
+ config1(Element::fromJSON("{\"IN\": [{\"type\": \"yyy\"}], "
+ "\"CH\": [{\"type\": \"xxx\"}]}"));
+ testConfigureDataSource(*this, config1);
+ // Now, the CH change will be handled first without an error, then
+ // the change to the IN class will fail, and the none of the changes
+ // will apply.
+ const ElementPtr
+ config2(Element::fromJSON("{\"IN\": [{\"type\": 13}], "
+ "\"CH\": [{\"type\": \"yyy\"}]}"));
+ EXPECT_THROW(testConfigureDataSource(*this, config2), TypeError);
+ EXPECT_EQ("yyy", lists_[RRClass::IN()]->getConf());
+ EXPECT_EQ("xxx", lists_[RRClass::CH()]->getConf());
+}
+
+}
diff --git a/src/bin/auth/tests/datasrc_configurator_unittest.cc b/src/bin/auth/tests/datasrc_configurator_unittest.cc
deleted file mode 100644
index f294536..0000000
--- a/src/bin/auth/tests/datasrc_configurator_unittest.cc
+++ /dev/null
@@ -1,299 +0,0 @@
-// Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC")
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
-
-#include <auth/datasrc_configurator.h>
-
-#include <config/tests/fake_session.h>
-#include <config/ccsession.h>
-#include <util/threads/lock.h>
-
-#include <gtest/gtest.h>
-
-#include <boost/bind.hpp>
-#include <boost/shared_ptr.hpp>
-
-#include <memory>
-
-using namespace isc;
-using namespace isc::cc;
-using namespace isc::config;
-using namespace isc::data;
-using namespace isc::dns;
-using namespace std;
-using namespace boost;
-
-namespace {
-
-class DatasrcConfiguratorTest;
-
-class FakeList {
-public:
- FakeList(const RRClass&) :
- configuration_(new ListElement)
- {}
- void configure(const ConstElementPtr& configuration, bool allow_cache) {
- EXPECT_TRUE(allow_cache);
- conf_ = configuration->get(0)->get("type")->stringValue();
- configuration_ = configuration;
- }
- const string& getConf() const {
- return (conf_);
- }
- ConstElementPtr getConfiguration() const {
- return (configuration_);
- }
-private:
- string conf_;
- ConstElementPtr configuration_;
-};
-
-typedef shared_ptr<FakeList> ListPtr;
-
-void
-testConfigureDataSource(DatasrcConfiguratorTest& test,
- const isc::data::ConstElementPtr& config)
-{
- // We use the test fixture for the Server type. This makes it possible
- // to easily fake all needed methods and look that they were called.
- configureDataSourceGeneric<DatasrcConfiguratorTest, FakeList>(test,
- config);
-}
-
-void
-datasrcConfigHandler(DatasrcConfiguratorTest* fake_server, const std::string&,
- isc::data::ConstElementPtr config,
- const isc::config::ConfigData&)
-{
- if (config->contains("classes")) {
- testConfigureDataSource(*fake_server, config->get("classes"));
- }
-}
-
-class DatasrcConfiguratorTest : public ::testing::Test {
-public:
- // These pretend to be the server
- ListPtr getClientList(const RRClass& rrclass) {
- log_ += "get " + rrclass.toText() + "\n";
- return (lists_[rrclass]);
- }
- void setClientList(const RRClass& rrclass, const ListPtr& list) {
- log_ += "set " + rrclass.toText() + " " +
- (list ? list->getConf() : "") + "\n";
- lists_[rrclass] = list;
- }
- vector<RRClass> getClientListClasses() const {
- vector<RRClass> result;
- for (std::map<RRClass, ListPtr>::const_iterator it(lists_.begin());
- it != lists_.end(); ++it) {
- result.push_back(it->first);
- }
- return (result);
- }
- isc::util::thread::Mutex& getClientListMutex() const {
- return (mutex_);
- }
-protected:
- DatasrcConfiguratorTest() :
- session(ElementPtr(new ListElement), ElementPtr(new ListElement),
- ElementPtr(new ListElement)),
- specfile(string(TEST_OWN_DATA_DIR) + "/spec.spec")
- {
- initSession();
- }
- void initSession() {
- session.getMessages()->add(createAnswer());
- mccs.reset(new ModuleCCSession(specfile, session, NULL, NULL, false,
- false));
- }
- void TearDown() {
- // Make sure no matter what we did, it is cleaned up. Also check
- // we really have subscribed to the configuration, and after removing
- // it we actually cancel it.
- EXPECT_TRUE(session.haveSubscription("data_sources", "*"));
- mccs->removeRemoteConfig("data_sources");
- EXPECT_FALSE(session.haveSubscription("data_sources", "*"));
- }
- void SetUp() {
- session.getMessages()->
- add(createAnswer(0,
- moduleSpecFromFile(string(PLUGIN_DATA_PATH) +
- "/datasrc.spec").
- getFullSpec()));
- session.getMessages()->add(createAnswer(0,
- ElementPtr(new MapElement)));
- mccs->addRemoteConfig("data_sources",
- boost::bind(datasrcConfigHandler,
- this, _1, _2, _3), false);
- }
- ElementPtr buildConfig(const string& config) const {
- const ElementPtr internal(Element::fromJSON(config));
- const ElementPtr external(Element::fromJSON("{\"version\": 1}"));
- external->set("classes", internal);
- return (external);
- }
- void initializeINList() {
- const ConstElementPtr
- config(buildConfig("{\"IN\": [{\"type\": \"xxx\"}]}"));
- session.addMessage(createCommand("config_update", config),
- "data_sources", "*");
- mccs->checkCommand();
- // Check it called the correct things (check that there's no IN yet and
- // set a new one.
- EXPECT_EQ("get IN\nset IN xxx\n", log_);
- EXPECT_EQ(1, lists_.size());
- }
- FakeSession session;
- auto_ptr<ModuleCCSession> mccs;
- const string specfile;
- std::map<RRClass, ListPtr> lists_;
- string log_;
- mutable isc::util::thread::Mutex mutex_;
-};
-
-// Push there a configuration with a single list.
-TEST_F(DatasrcConfiguratorTest, createList) {
- initializeINList();
-}
-
-TEST_F(DatasrcConfiguratorTest, modifyList) {
- // First, initialize the list
- initializeINList();
- // And now change the configuration of the list
- const ElementPtr
- config(buildConfig("{\"IN\": [{\"type\": \"yyy\"}]}"));
- session.addMessage(createCommand("config_update", config), "data_sources",
- "*");
- log_ = "";
- mccs->checkCommand();
- // This one does not set
- EXPECT_EQ("get IN\n", log_);
- // But this should contain the yyy configuration
- EXPECT_EQ("yyy", lists_[RRClass::IN()]->getConf());
- EXPECT_EQ(1, lists_.size());
-}
-
-// Check we can have multiple lists at once
-TEST_F(DatasrcConfiguratorTest, multiple) {
- const ElementPtr
- config(buildConfig("{\"IN\": [{\"type\": \"yyy\"}], "
- "\"CH\": [{\"type\": \"xxx\"}]}"));
- session.addMessage(createCommand("config_update", config), "data_sources",
- "*");
- mccs->checkCommand();
- // We have set commands for both classes.
- EXPECT_EQ("get CH\nset CH xxx\nget IN\nset IN yyy\n", log_);
- // We should have both there
- EXPECT_EQ("yyy", lists_[RRClass::IN()]->getConf());
- EXPECT_EQ("xxx", lists_[RRClass::CH()]->getConf());
- EXPECT_EQ(2, lists_.size());
-}
-
-// Check we can add another one later and the old one does not get
-// overwritten.
-//
-// It's almost like above, but we initialize first with single-list
-// config.
-TEST_F(DatasrcConfiguratorTest, updateAdd) {
- initializeINList();
- const ElementPtr
- config(buildConfig("{\"IN\": [{\"type\": \"yyy\"}], "
- "\"CH\": [{\"type\": \"xxx\"}]}"));
- session.addMessage(createCommand("config_update", config), "data_sources",
- "*");
- log_ = "";
- mccs->checkCommand();
- // The CH is set, IN not
- EXPECT_EQ("get CH\nset CH xxx\nget IN\n", log_);
- // But this should contain the yyy configuration
- EXPECT_EQ("xxx", lists_[RRClass::CH()]->getConf());
- EXPECT_EQ("yyy", lists_[RRClass::IN()]->getConf());
- EXPECT_EQ(2, lists_.size());
-}
-
-// We delete a class list in this test.
-TEST_F(DatasrcConfiguratorTest, updateDelete) {
- initializeINList();
- const ElementPtr
- config(buildConfig("{}"));
- session.addMessage(createCommand("config_update", config), "data_sources",
- "*");
- log_ = "";
- mccs->checkCommand();
- EXPECT_EQ("get IN\nset IN \n", log_);
- EXPECT_FALSE(lists_[RRClass::IN()]);
- // In real auth server, the NULL one would be removed. However, we just
- // store it, so the IN bucket is still in there. This checks there's nothing
- // else.
- EXPECT_EQ(1, lists_.size());
-}
-
-// Check that we can rollback an addition if something else fails
-TEST_F(DatasrcConfiguratorTest, rollbackAddition) {
- initializeINList();
- // The configuration is wrong. However, the CH one will get done first.
- const ElementPtr
- config(buildConfig("{\"IN\": [{\"type\": 13}], "
- "\"CH\": [{\"type\": \"xxx\"}]}"));
- session.addMessage(createCommand("config_update", config), "data_sources",
- "*");
- log_ = "";
- // It does not throw, as it is handled in the ModuleCCSession.
- // Throwing from the reconfigure is checked in other tests.
- EXPECT_NO_THROW(mccs->checkCommand());
- // Anyway, the result should not contain CH now and the original IN should
- // be there.
- EXPECT_EQ("xxx", lists_[RRClass::IN()]->getConf());
- EXPECT_FALSE(lists_[RRClass::CH()]);
-}
-
-// Check that we can rollback a deletion if something else fails
-TEST_F(DatasrcConfiguratorTest, rollbackDeletion) {
- initializeINList();
- // Put the CH there
- const ElementPtr
- config1(Element::fromJSON("{\"IN\": [{\"type\": \"yyy\"}], "
- "\"CH\": [{\"type\": \"xxx\"}]}"));
- testConfigureDataSource(*this, config1);
- const ElementPtr
- config2(Element::fromJSON("{\"IN\": [{\"type\": 13}]}"));
- // This would delete CH. However, the IN one fails.
- // As the deletions happen after the additions/settings
- // and there's no known way to cause an exception during the
- // deletions, it is not a true rollback, but the result should
- // be the same.
- EXPECT_THROW(testConfigureDataSource(*this, config2), TypeError);
- EXPECT_EQ("yyy", lists_[RRClass::IN()]->getConf());
- EXPECT_EQ("xxx", lists_[RRClass::CH()]->getConf());
-}
-
-// Check that we can roll back configuration change if something
-// fails later on.
-TEST_F(DatasrcConfiguratorTest, rollbackConfiguration) {
- initializeINList();
- // Put the CH there
- const ElementPtr
- config1(Element::fromJSON("{\"IN\": [{\"type\": \"yyy\"}], "
- "\"CH\": [{\"type\": \"xxx\"}]}"));
- testConfigureDataSource(*this, config1);
- // Now, the CH happens first. But nevertheless, it should be
- // restored to the previoeus version.
- const ElementPtr
- config2(Element::fromJSON("{\"IN\": [{\"type\": 13}], "
- "\"CH\": [{\"type\": \"yyy\"}]}"));
- EXPECT_THROW(testConfigureDataSource(*this, config2), TypeError);
- EXPECT_EQ("yyy", lists_[RRClass::IN()]->getConf());
- EXPECT_EQ("xxx", lists_[RRClass::CH()]->getConf());
-}
-
-}
diff --git a/src/bin/auth/tests/query_unittest.cc b/src/bin/auth/tests/query_unittest.cc
index 603bf5c..84b7f8a 100644
--- a/src/bin/auth/tests/query_unittest.cc
+++ b/src/bin/auth/tests/query_unittest.cc
@@ -442,10 +442,9 @@ public:
ConstRRsetPtr rrset)
{
nsec_name_ = nsec_name;
- nsec_context_.reset(new Context(*this,
- FIND_DEFAULT, // a fake value
- ResultContext(code, rrset,
- RESULT_NSEC_SIGNED)));
+ nsec_context_.reset(
+ new GenericContext(*this, FIND_DEFAULT, // a fake value
+ ResultContext(code, rrset, RESULT_NSEC_SIGNED)));
}
// Once called, the findNSEC3 will return the provided result for the next
@@ -487,8 +486,8 @@ protected:
{
ConstRRsetPtr rp = stripRRsigs(rrset, options);
return (ZoneFinderContextPtr(
- new Context(*this, options,
- ResultContext(code, rp, flags))));
+ new GenericContext(*this, options,
+ ResultContext(code, rp, flags))));
}
private:
@@ -604,9 +603,9 @@ MockZoneFinder::findAll(const Name& name, std::vector<ConstRRsetPtr>& target,
target.push_back(stripRRsigs(found_rrset->second, options));
}
return (ZoneFinderContextPtr(
- new Context(*this, options,
- ResultContext(SUCCESS, RRsetPtr()),
- target)));
+ new GenericContext(*this, options,
+ ResultContext(SUCCESS, RRsetPtr()),
+ target)));
}
}
diff --git a/src/bin/bind10/bind10_messages.mes b/src/bin/bind10/bind10_messages.mes
index c751583..ed2a5d9 100644
--- a/src/bin/bind10/bind10_messages.mes
+++ b/src/bin/bind10/bind10_messages.mes
@@ -141,6 +141,16 @@ it now. The new configuration is printed.
% BIND10_RECEIVED_SIGNAL received signal %1
The boss module received the given signal.
+% BIND10_RESTART_COMPONENT_SKIPPED Skipped restarting a component %1
+The boss module tried to restart a component after it failed (crashed)
+unexpectedly, but the boss then found that the component had been removed
+from its local configuration of components to run. This is an unusal
+situation but can happen if the administrator removes the component from
+the configuration after the component's crash and before the restart time.
+The boss module simply skipped restarting that module, and the whole system
+went back to the expected state (except that the crash itself is likely
+to be a bug).
+
% BIND10_RESURRECTED_PROCESS resurrected %1 (PID %2)
The given process has been restarted successfully, and is now running
with the given process id.
@@ -157,6 +167,30 @@ so BIND 10 will now shut down. The specific error is printed.
% BIND10_SEND_SIGKILL sending SIGKILL to %1 (PID %2)
The boss module is sending a SIGKILL signal to the given process.
+% BIND10_SEND_SIGNAL_FAIL sending %1 to %2 (PID %3) failed: %4
+The boss module sent a single (either SIGTERM or SIGKILL) to a process,
+but it failed due to some system level error. There are two major cases:
+the target process has already terminated but the boss module had sent
+the signal before it noticed the termination. In this case an error
+message should indicate something like "no such process". This can be
+safely ignored. The other case is that the boss module doesn't have
+the privilege to send a signal to the process. It can typically
+happen when the boss module started as a privileged process, spawned a
+subprocess, and then dropped the privilege. It includes the case for
+the socket creator when the boss process runs with the -u command line
+option. In this case, the boss module simply gives up to terminate
+the process explicitly because it's unlikely to succeed by keeping
+sending the signal. Although the socket creator is implemented so
+that it will terminate automatically when the boss process exits
+(and that should be the case for any other future process running with
+a higher privilege), but it's recommended to check if there's any
+remaining BIND 10 process if this message is logged. For all other
+cases, the boss module will keep sending the signal until it confirms
+all child processes terminate. Although unlikely, this could prevent
+the boss module from exiting, just keeping sending the signals. So,
+again, it's advisable to check if it really terminates when this
+message is logged.
+
% BIND10_SEND_SIGTERM sending SIGTERM to %1 (PID %2)
The boss module is sending a SIGTERM signal to the given process.
@@ -264,13 +298,6 @@ During the startup process, a number of messages are exchanged between the
Boss process and the processes it starts. This error is output when a
message received by the Boss process is not recognised.
-% BIND10_START_AS_NON_ROOT_AUTH starting b10-auth as a user, not root. This might fail.
-The authoritative server is being started or restarted without root privileges.
-If the module needs these privileges, it may have problems starting.
-Note that this issue should be resolved by the pending 'socket-creator'
-process; once that has been implemented, modules should not need root
-privileges anymore. See tickets #800 and #801 for more information.
-
% BIND10_START_AS_NON_ROOT_RESOLVER starting b10-resolver as a user, not root. This might fail.
The resolver is being started or restarted without root privileges.
If the module needs these privileges, it may have problems starting.
diff --git a/src/bin/bind10/bind10_src.py.in b/src/bin/bind10/bind10_src.py.in
index bb2edaf..45a2ccb 100755
--- a/src/bin/bind10/bind10_src.py.in
+++ b/src/bin/bind10/bind10_src.py.in
@@ -546,8 +546,6 @@ class BoB:
"""
Start the Authoritative server
"""
- if self.uid is not None and self.__started:
- logger.warn(BIND10_START_AS_NON_ROOT_AUTH)
authargs = ['b10-auth']
if self.verbose:
authargs += ['-v']
@@ -693,32 +691,42 @@ class BoB:
# from doing so
if not self.nokill:
# next try sending a SIGTERM
- components_to_stop = list(self.components.values())
- for component in components_to_stop:
- logger.info(BIND10_SEND_SIGTERM, component.name(), component.pid())
- try:
- component.kill()
- except OSError:
- # ignore these (usually ESRCH because the child
- # finally exited)
- pass
- # finally, send SIGKILL (unmaskable termination) until everybody dies
+ self.__kill_children(False)
+ # finally, send SIGKILL (unmaskable termination) until everybody
+ # dies
while self.components:
# XXX: some delay probably useful... how much is uncertain
time.sleep(0.1)
self.reap_children()
- components_to_stop = list(self.components.values())
- for component in components_to_stop:
- logger.info(BIND10_SEND_SIGKILL, component.name(),
- component.pid())
- try:
- component.kill(True)
- except OSError:
- # ignore these (usually ESRCH because the child
- # finally exited)
- pass
+ self.__kill_children(True)
logger.info(BIND10_SHUTDOWN_COMPLETE)
+ def __kill_children(self, forceful):
+ '''Terminate remaining subprocesses by sending a signal.
+
+ The forceful paramter will be passed Component.kill().
+ This is a dedicated subroutine of shutdown(), just to unify two
+ similar cases.
+
+ '''
+ logmsg = BIND10_SEND_SIGKILL if forceful else BIND10_SEND_SIGTERM
+ # We need to make a copy of values as the components may be modified
+ # in the loop.
+ for component in list(self.components.values()):
+ logger.info(logmsg, component.name(), component.pid())
+ try:
+ component.kill(forceful)
+ except OSError as ex:
+ # If kill() failed due to EPERM, it doesn't make sense to
+ # keep trying, so we just log the fact and forget that
+ # component. Ignore other OSErrors (usually ESRCH because
+ # the child finally exited)
+ signame = "SIGKILL" if forceful else "SIGTERM"
+ logger.info(BIND10_SEND_SIGNAL_FAIL, signame,
+ component.name(), component.pid(), ex)
+ if ex.errno == errno.EPERM:
+ del self.components[component.pid()]
+
def _get_process_exit_status(self):
return os.waitpid(-1, os.WNOHANG)
@@ -739,7 +747,7 @@ class BoB:
component = self.components.pop(pid)
logger.info(BIND10_PROCESS_ENDED, component.name(), pid,
exit_status)
- if component.running() and self.runnable:
+ if component.is_running() and self.runnable:
# Tell it it failed. But only if it matters (we are
# not shutting down and the component considers itself
# to be running.
@@ -771,7 +779,12 @@ class BoB:
next_restart_time = None
now = time.time()
for component in self.components_to_restart:
- if not component.restart(now):
+ # If the component was removed from the configurator between since
+ # scheduled to restart, just ignore it. The object will just be
+ # dropped here.
+ if not self._component_configurator.has_component(component):
+ logger.info(BIND10_RESTART_COMPONENT_SKIPPED, component.name())
+ elif not component.restart(now):
still_dead.append(component)
if next_restart_time is None or\
next_restart_time > component.get_restart_time():
diff --git a/src/bin/bind10/tests/bind10_test.py.in b/src/bin/bind10/tests/bind10_test.py.in
index 9a40e42..ece6370 100644
--- a/src/bin/bind10/tests/bind10_test.py.in
+++ b/src/bin/bind10/tests/bind10_test.py.in
@@ -929,7 +929,14 @@ class MockComponent:
self.name = lambda: name
self.pid = lambda: pid
self.address = lambda: address
+ self.restarted = False
+ def get_restart_time(self):
+ return 0 # arbitrary dummy value
+
+ def restart(self, now):
+ self.restarted = True
+ return True
class TestBossCmd(unittest.TestCase):
def test_ping(self):
@@ -1174,7 +1181,7 @@ class TestBossComponents(unittest.TestCase):
# We check somewhere else that the shutdown is actually called
# from there (the test_kills).
- def __real_test_kill(self, nokill = False):
+ def __real_test_kill(self, nokill=False, ex_on_kill=None):
"""
Helper function that does the actual kill functionality testing.
"""
@@ -1188,8 +1195,23 @@ class TestBossComponents(unittest.TestCase):
(anyway it is not told so). It does not die if it is killed
the first time. It dies only when killed forcefully.
"""
+ def __init__(self):
+ # number of kill() calls, preventing infinite loop.
+ self.__call_count = 0
+
def kill(self, forceful=False):
+ self.__call_count += 1
+ if self.__call_count > 2:
+ raise Exception('Too many calls to ImmortalComponent.kill')
+
killed.append(forceful)
+ if ex_on_kill is not None:
+ # If exception is given by the test, raise it here.
+ # In the case of ESRCH, the process should have gone
+ # somehow, so we clear the components.
+ if ex_on_kill.errno == errno.ESRCH:
+ bob.components = {}
+ raise ex_on_kill
if forceful:
bob.components = {}
def pid(self):
@@ -1217,7 +1239,10 @@ class TestBossComponents(unittest.TestCase):
if nokill:
self.assertEqual([], killed)
else:
- self.assertEqual([False, True], killed)
+ if ex_on_kill is not None:
+ self.assertEqual([False], killed)
+ else:
+ self.assertEqual([False, True], killed)
self.assertTrue(self.__called)
@@ -1229,6 +1254,20 @@ class TestBossComponents(unittest.TestCase):
"""
self.__real_test_kill()
+ def test_kill_fail(self):
+ """Test cases where kill() results in an exception due to OS error.
+
+ The behavior should be different for EPERM, so we test two cases.
+
+ """
+
+ ex = OSError()
+ ex.errno, ex.strerror = errno.ESRCH, 'No such process'
+ self.__real_test_kill(ex_on_kill=ex)
+
+ ex.errno, ex.strerror = errno.EPERM, 'Operation not permitted'
+ self.__real_test_kill(ex_on_kill=ex)
+
def test_nokill(self):
"""
Test that the boss *doesn't* kill components which don't want to
@@ -1266,6 +1305,34 @@ class TestBossComponents(unittest.TestCase):
bob.start_all_components()
self.__check_extended(self.__param)
+ def __setup_restart(self, bob, component):
+ '''Common procedure for restarting a component used below.'''
+ bob.components_to_restart = { component }
+ component.restarted = False
+ bob.restart_processes()
+
+ def test_restart_processes(self):
+ '''Check some behavior on restarting processes.'''
+ bob = MockBob()
+ bob.runnable = True
+ component = MockComponent('test', 53)
+
+ # A component to be restarted will actually be restarted iff it's
+ # in the configurator's configuration.
+ # We bruteforce the configurator internal below; ugly, but the easiest
+ # way for the test.
+ bob._component_configurator._components['test'] = (None, component)
+ self.__setup_restart(bob, component)
+ self.assertTrue(component.restarted)
+ self.assertFalse(component in bob.components_to_restart)
+
+ # Remove the component from the configuration. It won't be restarted
+ # even if scheduled, nor will remain in the to-be-restarted list.
+ del bob._component_configurator._components['test']
+ self.__setup_restart(bob, component)
+ self.assertFalse(component.restarted)
+ self.assertFalse(component in bob.components_to_restart)
+
class SocketSrvTest(unittest.TestCase):
"""
This tests some methods of boss related to the unix domain sockets used
diff --git a/src/bin/dbutil/dbutil.py.in b/src/bin/dbutil/dbutil.py.in
index 4b76a56..a844484 100755
--- a/src/bin/dbutil/dbutil.py.in
+++ b/src/bin/dbutil/dbutil.py.in
@@ -193,10 +193,17 @@ UPGRADES = [
"ALTER TABLE schema_version " +
"ADD COLUMN minor INTEGER NOT NULL DEFAULT 0"
]
+ },
+
+ {'from': (2, 0), 'to': (2, 1),
+ 'statements': [
+ "CREATE INDEX nsec3_byhash_and_rdtype ON nsec3 " +
+ "(hash, rdtype)"
+ ]
}
# To extend this, leave the above statements in place and add another
-# dictionary to the list. The "from" version should be (2, 0), the "to"
+# dictionary to the list. The "from" version should be (2, 1), the "to"
# version whatever the version the update is to, and the SQL statements are
# the statements required to perform the upgrade. This way, the upgrade
# program will be able to upgrade both a V1.0 and a V2.0 database.
diff --git a/src/bin/dbutil/tests/Makefile.am b/src/bin/dbutil/tests/Makefile.am
index b4231b3..aaa57cc 100644
--- a/src/bin/dbutil/tests/Makefile.am
+++ b/src/bin/dbutil/tests/Makefile.am
@@ -2,6 +2,8 @@ SUBDIRS = . testdata
# Tests of the update script.
+noinst_SCRIPTS = dbutil_test.sh
+
check-local:
B10_LOCKFILE_DIR_FROM_BUILD=$(abs_top_builddir) \
$(SHELL) $(abs_builddir)/dbutil_test.sh
diff --git a/src/bin/dbutil/tests/dbutil_test.sh.in b/src/bin/dbutil/tests/dbutil_test.sh.in
index f82eeb0..35314e8 100755
--- a/src/bin/dbutil/tests/dbutil_test.sh.in
+++ b/src/bin/dbutil/tests/dbutil_test.sh.in
@@ -165,7 +165,7 @@ upgrade_ok_test() {
if [ $? -eq 0 ]
then
# Compare schema with the reference
- get_schema $testdata/v2_0.sqlite3
+ get_schema $testdata/v2_1.sqlite3
expected_schema=$db_schema
get_schema $tempfile
actual_schema=$db_schema
@@ -177,7 +177,7 @@ upgrade_ok_test() {
fi
# Check the version is set correctly
- check_version $tempfile "V2.0"
+ check_version $tempfile "V2.1"
# Check that a backup was made
check_backup $1 $2
@@ -449,7 +449,7 @@ copy_file $testdata/old_v1.sqlite3 $tempfile
Yes
.
passzero $?
-check_version $tempfile "V2.0"
+check_version $tempfile "V2.1"
rm -f $tempfile $backupfile
echo "13.4 Interactive prompt - no"
diff --git a/src/bin/dbutil/tests/testdata/Makefile.am b/src/bin/dbutil/tests/testdata/Makefile.am
index 0d850a7..f4873f4 100644
--- a/src/bin/dbutil/tests/testdata/Makefile.am
+++ b/src/bin/dbutil/tests/testdata/Makefile.am
@@ -10,3 +10,4 @@ EXTRA_DIST += old_v1.sqlite3
EXTRA_DIST += README
EXTRA_DIST += too_many_version.sqlite3
EXTRA_DIST += v2_0.sqlite3
+EXTRA_DIST += v2_1.sqlite3
diff --git a/src/bin/dbutil/tests/testdata/v2_1.sqlite3 b/src/bin/dbutil/tests/testdata/v2_1.sqlite3
new file mode 100644
index 0000000..ca2dee9
Binary files /dev/null and b/src/bin/dbutil/tests/testdata/v2_1.sqlite3 differ
diff --git a/src/bin/dhcp4/tests/Makefile.am b/src/bin/dhcp4/tests/Makefile.am
index 906b5d1..ddc3000 100644
--- a/src/bin/dhcp4/tests/Makefile.am
+++ b/src/bin/dhcp4/tests/Makefile.am
@@ -16,6 +16,7 @@ check-local:
for pytest in $(PYTESTS) ; do \
echo Running test: $$pytest ; \
PYTHONPATH=$(COMMON_PYTHON_PATH):$(abs_top_srcdir)/src/bin:$(abs_top_builddir)/src/bin/bind10:$(abs_top_builddir)/src/lib/util/io/.libs \
+ B10_LOCKFILE_DIR_FROM_BUILD=$(abs_top_builddir) \
$(LIBRARY_PATH_PLACEHOLDER) \
$(PYCOVERAGE_RUN) $(abs_srcdir)/$$pytest || exit ; \
done
diff --git a/src/bin/dhcp6/Makefile.am b/src/bin/dhcp6/Makefile.am
index 4dec4e7..68aadea 100644
--- a/src/bin/dhcp6/Makefile.am
+++ b/src/bin/dhcp6/Makefile.am
@@ -46,6 +46,7 @@ pkglibexec_PROGRAMS = b10-dhcp6
b10_dhcp6_SOURCES = main.cc
b10_dhcp6_SOURCES += ctrl_dhcp6_srv.cc ctrl_dhcp6_srv.h
+b10_dhcp6_SOURCES += config_parser.cc config_parser.h
b10_dhcp6_SOURCES += dhcp6_log.cc dhcp6_log.h
b10_dhcp6_SOURCES += dhcp6_srv.cc dhcp6_srv.h
@@ -62,6 +63,7 @@ b10_dhcp6_LDADD = $(top_builddir)/src/lib/exceptions/libb10-exceptions.la
b10_dhcp6_LDADD += $(top_builddir)/src/lib/asiolink/libb10-asiolink.la
b10_dhcp6_LDADD += $(top_builddir)/src/lib/log/libb10-log.la
b10_dhcp6_LDADD += $(top_builddir)/src/lib/dhcp/libb10-dhcp++.la
+b10_dhcp6_LDADD += $(top_builddir)/src/lib/dhcp/libb10-dhcpsrv.la
b10_dhcp6_LDADD += $(top_builddir)/src/lib/config/libb10-cfgclient.la
b10_dhcp6_LDADD += $(top_builddir)/src/lib/cc/libb10-cc.la
diff --git a/src/bin/dhcp6/config_parser.cc b/src/bin/dhcp6/config_parser.cc
new file mode 100644
index 0000000..dbffc40
--- /dev/null
+++ b/src/bin/dhcp6/config_parser.cc
@@ -0,0 +1,797 @@
+// Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <stdint.h>
+#include <iostream>
+#include <vector>
+#include <map>
+#include <boost/foreach.hpp>
+#include <boost/shared_ptr.hpp>
+#include <boost/scoped_ptr.hpp>
+#include <boost/lexical_cast.hpp>
+#include <boost/algorithm/string.hpp>
+#include <asiolink/io_address.h>
+#include <cc/data.h>
+#include <config/ccsession.h>
+#include <log/logger_support.h>
+#include <dhcp/triplet.h>
+#include <dhcp/pool.h>
+#include <dhcp/subnet.h>
+#include <dhcp/cfgmgr.h>
+#include <dhcp6/config_parser.h>
+#include <dhcp6/dhcp6_log.h>
+
+using namespace std;
+using namespace isc::data;
+using namespace isc::asiolink;
+
+namespace isc {
+namespace dhcp {
+
+/// @brief auxiliary type used for storing element name and its parser
+typedef pair<string, ConstElementPtr> ConfigPair;
+
+/// @brief a factory method that will create a parser for a given element name
+typedef DhcpConfigParser* ParserFactory(const std::string& config_id);
+
+/// @brief a collection of factories that creates parsers for specified element names
+typedef std::map<std::string, ParserFactory*> FactoryMap;
+
+/// @brief a collection of elements that store uint32 values (e.g. renew-timer = 900)
+typedef std::map<string, uint32_t> Uint32Storage;
+
+/// @brief a collection of elements that store string values
+typedef std::map<string, string> StringStorage;
+
+/// @brief a collection of pools
+///
+/// That type is used as intermediate storage, when pools are parsed, but there is
+/// no subnet object created yet to store them.
+typedef std::vector<Pool6Ptr> PoolStorage;
+
+/// @brief Global uint32 parameters that will be used as defaults.
+Uint32Storage uint32_defaults;
+
+/// @brief global string parameters that will be used as defaults.
+StringStorage string_defaults;
+
+/// @brief a dummy configuration parser
+///
+/// It is a debugging parser. It does not configure anything,
+/// will accept any configuration and will just print it out
+/// on commit. Useful for debugging existing configurations and
+/// adding new ones.
+class DebugParser : public DhcpConfigParser {
+public:
+
+ /// @brief Constructor
+ ///
+ /// See \ref DhcpConfigParser class for details.
+ ///
+ /// @param param_name name of the parsed parameter
+ DebugParser(const std::string& param_name)
+ :param_name_(param_name) {
+ }
+
+ /// @brief builds parameter value
+ ///
+ /// See \ref DhcpConfigParser class for details.
+ ///
+ /// @param new_config pointer to the new configuration
+ virtual void build(ConstElementPtr new_config) {
+ std::cout << "Build for token: [" << param_name_ << "] = ["
+ << value_->str() << "]" << std::endl;
+ value_ = new_config;
+ }
+
+ /// @brief pretends to apply the configuration
+ ///
+ /// This is a method required by base class. It pretends to apply the
+ /// configuration, but in fact it only prints the parameter out.
+ ///
+ /// See \ref DhcpConfigParser class for details.
+ virtual void commit() {
+ // Debug message. The whole DebugParser class is used only for parser
+ // debugging, and is not used in production code. It is very convenient
+ // to keep it around. Please do not turn this cout into logger calls.
+ std::cout << "Commit for token: [" << param_name_ << "] = ["
+ << value_->str() << "]" << std::endl;
+ }
+
+ /// @brief factory that constructs DebugParser objects
+ ///
+ /// @param param_name name of the parameter to be parsed
+ static DhcpConfigParser* Factory(const std::string& param_name) {
+ return (new DebugParser(param_name));
+ }
+
+protected:
+ /// name of the parsed parameter
+ std::string param_name_;
+
+ /// pointer to the actual value of the parameter
+ ConstElementPtr value_;
+};
+
+/// @brief Configuration parser for uint32 parameters
+///
+/// This class is a generic parser that is able to handle any uint32 integer
+/// type. By default it stores the value in external global container
+/// (uint32_defaults). If used in smaller scopes (e.g. to parse parameters
+/// in subnet config), it can be pointed to a different storage, using
+/// setStorage() method. This class follows the parser interface, laid out
+/// in its base class, \ref DhcpConfigParser.
+///
+/// For overview of usability of this generic purpose parser, see
+/// \ref dhcpv6-config-inherit page.
+class Uint32Parser : public DhcpConfigParser {
+public:
+
+ /// @brief constructor for Uint32Parser
+ /// @param param_name name of the configuration parameter being parsed
+ Uint32Parser(const std::string& param_name)
+ :storage_(&uint32_defaults), param_name_(param_name) {
+ }
+
+ /// @brief builds parameter value
+ ///
+ /// Parses configuration entry and stores it in a storage. See
+ /// \ref setStorage() for details.
+ ///
+ /// @param value pointer to the content of parsed values
+ virtual void build(ConstElementPtr value) {
+ try {
+ value_ = boost::lexical_cast<uint32_t>(value->str());
+ } catch (const boost::bad_lexical_cast &) {
+ isc_throw(BadValue, "Failed to parse value " << value->str()
+ << " as unsigned 32-bit integer.");
+ }
+ storage_->insert(pair<string, uint32_t>(param_name_, value_));
+ }
+
+ /// @brief does nothing
+ ///
+ /// This method is required for all parser. The value itself
+ /// is not commited anywhere. Higher level parsers are expected to
+ /// use values stored in the storage, e.g. renew-timer for a given
+ /// subnet is stored in subnet-specific storage. It is not commited
+ /// here, but is rather used by \ref Subnet6Parser when constructing
+ /// the subnet.
+ virtual void commit() {
+ }
+
+ /// @brief factory that constructs Uint32Parser objects
+ ///
+ /// @param param_name name of the parameter to be parsed
+ static DhcpConfigParser* Factory(const std::string& param_name) {
+ return (new Uint32Parser(param_name));
+ }
+
+ /// @brief sets storage for value of this parameter
+ ///
+ /// See \ref dhcpv6-config-inherit for details.
+ ///
+ /// @param storage pointer to the storage container
+ void setStorage(Uint32Storage* storage) {
+ storage_ = storage;
+ }
+
+protected:
+ /// pointer to the storage, where parsed value will be stored
+ Uint32Storage* storage_;
+
+ /// name of the parameter to be parsed
+ std::string param_name_;
+
+ /// the actual parsed value
+ uint32_t value_;
+};
+
+/// @brief Configuration parser for string parameters
+///
+/// This class is a generic parser that is able to handle any string
+/// parameter. By default it stores the value in external global container
+/// (string_defaults). If used in smaller scopes (e.g. to parse parameters
+/// in subnet config), it can be pointed to a different storage, using
+/// setStorage() method. This class follows the parser interface, laid out
+/// in its base class, \ref DhcpConfigParser.
+///
+/// For overview of usability of this generic purpose parser, see
+/// \ref dhcpv6-config-inherit page.
+class StringParser : public DhcpConfigParser {
+public:
+
+ /// @brief constructor for StringParser
+ /// @param param_name name of the configuration parameter being parsed
+ StringParser(const std::string& param_name)
+ :storage_(&string_defaults), param_name_(param_name) {
+ }
+
+ /// @brief parses parameter value
+ ///
+ /// Parses configuration entry and stored it in storage. See
+ /// \ref setStorage() for details.
+ ///
+ /// @param value pointer to the content of parsed values
+ virtual void build(ConstElementPtr value) {
+ value_ = value->str();
+ boost::erase_all(value_, "\"");
+ storage_->insert(pair<string, string>(param_name_, value_));
+ }
+
+ /// @brief does nothing
+ ///
+ /// This method is required for all parser. The value itself
+ /// is not commited anywhere. Higher level parsers are expected to
+ /// use values stored in the storage, e.g. renew-timer for a given
+ /// subnet is stored in subnet-specific storage. It is not commited
+ /// here, but is rather used by its parent parser when constructing
+ /// an object, e.g. the subnet.
+ virtual void commit() {
+ }
+
+ /// @brief factory that constructs StringParser objects
+ ///
+ /// @param param_name name of the parameter to be parsed
+ static DhcpConfigParser* Factory(const std::string& param_name) {
+ return (new StringParser(param_name));
+ }
+
+ /// @brief sets storage for value of this parameter
+ ///
+ /// See \ref dhcpv6-config-inherit for details.
+ ///
+ /// @param storage pointer to the storage container
+ void setStorage(StringStorage* storage) {
+ storage_ = storage;
+ }
+
+protected:
+ /// pointer to the storage, where parsed value will be stored
+ StringStorage* storage_;
+
+ /// name of the parameter to be parsed
+ std::string param_name_;
+
+ /// the actual parsed value
+ std::string value_;
+};
+
+
+/// @brief parser for interface list definition
+///
+/// This parser handles Dhcp6/interface entry.
+/// It contains a list of network interfaces that the server listens on.
+/// In particular, it can contain an entry called "all" or "any" that
+/// designates all interfaces.
+///
+/// It is useful for parsing Dhcp6/interface parameter.
+class InterfaceListConfigParser : public DhcpConfigParser {
+public:
+
+ /// @brief constructor
+ ///
+ /// As this is a dedicated parser, it must be used to parse
+ /// "interface" parameter only. All other types will throw exception.
+ ///
+ /// @param param_name name of the configuration parameter being parsed
+ InterfaceListConfigParser(const std::string& param_name) {
+ if (param_name != "interface") {
+ isc_throw(NotImplemented, "Internal error. Interface configuration "
+ "parser called for the wrong parameter: " << param_name);
+ }
+ }
+
+ /// @brief parses parameters value
+ ///
+ /// Parses configuration entry (list of parameters) and stores it in
+ /// storage. See \ref setStorage() for details.
+ ///
+ /// @param value pointer to the content of parsed values
+ virtual void build(ConstElementPtr value) {
+ BOOST_FOREACH(ConstElementPtr iface, value->listValue()) {
+ interfaces_.push_back(iface->str());
+ }
+ }
+
+ /// @brief commits interfaces list configuration
+ virtual void commit() {
+ /// @todo: Implement per interface listening. Currently always listening
+ /// on all interfaces.
+ }
+
+ /// @brief factory that constructs InterfaceListConfigParser objects
+ ///
+ /// @param param_name name of the parameter to be parsed
+ static DhcpConfigParser* Factory(const std::string& param_name) {
+ return (new InterfaceListConfigParser(param_name));
+ }
+
+protected:
+ /// contains list of network interfaces
+ vector<string> interfaces_;
+};
+
+/// @brief parser for pool definition
+///
+/// This parser handles pool definitions, i.e. a list of entries of one
+/// of two syntaxes: min-max and prefix/len. Pool6 objects are created
+/// and stored in chosen PoolStorage container.
+///
+/// As there are no default values for pool, setStorage() must be called
+/// before build(). Otherwise exception will be thrown.
+///
+/// It is useful for parsing Dhcp6/subnet6[X]/pool parameters.
+class PoolParser : public DhcpConfigParser {
+public:
+
+ /// @brief constructor.
+ PoolParser(const std::string& /*param_name*/)
+ :pools_(NULL) {
+ // ignore parameter name, it is always Dhcp6/subnet6[X]/pool
+ }
+
+ /// @brief parses the actual list
+ ///
+ /// This method parses the actual list of interfaces.
+ /// No validation is done at this stage, everything is interpreted as
+ /// interface name.
+ void build(ConstElementPtr pools_list) {
+ // setStorage() should have been called before build
+ if (!pools_) {
+ isc_throw(NotImplemented, "Parser logic error. No pool storage set,"
+ " but pool parser asked to parse pools");
+ }
+
+ BOOST_FOREACH(ConstElementPtr text_pool, pools_list->listValue()) {
+
+ // That should be a single pool representation. It should contain
+ // text is form prefix/len or first - last. Note that spaces
+ // are allowed
+ string txt = text_pool->stringValue();
+
+ // first let's remove any whitespaces
+ boost::erase_all(txt, " "); // space
+ boost::erase_all(txt, "\t"); // tabulation
+
+ // Is this prefix/len notation?
+ size_t pos = txt.find("/");
+ if (pos != string::npos) {
+ IOAddress addr("::");
+ uint8_t len = 0;
+ try {
+ addr = IOAddress(txt.substr(0, pos));
+
+ // start with the first character after /
+ string prefix_len = txt.substr(pos + 1);
+
+ // It is lexical cast to int and then downcast to uint8_t.
+ // Direct cast to uint8_t (which is really an unsigned char)
+ // will result in interpreting the first digit as output
+ // value and throwing exception if length is written on two
+ // digits (because there are extra characters left over).
+
+ // No checks for values over 128. Range correctness will
+ // be checked in Pool6 constructor.
+ len = boost::lexical_cast<int>(prefix_len);
+ } catch (...) {
+ isc_throw(Dhcp6ConfigError, "Failed to parse pool "
+ "definition: " << text_pool->stringValue());
+ }
+
+ Pool6Ptr pool(new Pool6(Pool6::TYPE_IA, addr, len));
+ pools_->push_back(pool);
+ continue;
+ }
+
+ // Is this min-max notation?
+ pos = txt.find("-");
+ if (pos != string::npos) {
+ // using min-max notation
+ IOAddress min(txt.substr(0,pos - 1));
+ IOAddress max(txt.substr(pos + 1));
+
+ Pool6Ptr pool(new Pool6(Pool6::TYPE_IA, min, max));
+
+ pools_->push_back(pool);
+ continue;
+ }
+
+ isc_throw(Dhcp6ConfigError, "Failed to parse pool definition:"
+ << text_pool->stringValue() <<
+ ". Does not contain - (for min-max) nor / (prefix/len)");
+ }
+ }
+
+ /// @brief sets storage for value of this parameter
+ ///
+ /// See \ref dhcpv6-config-inherit for details.
+ ///
+ /// @param storage pointer to the storage container
+ void setStorage(PoolStorage* storage) {
+ pools_ = storage;
+ }
+
+ /// @brief does nothing.
+ ///
+ /// This method is required for all parser. The value itself
+ /// is not commited anywhere. Higher level parsers (for subnet) are expected
+ /// to use values stored in the storage.
+ virtual void commit() {}
+
+ /// @brief factory that constructs PoolParser objects
+ ///
+ /// @param param_name name of the parameter to be parsed
+ static DhcpConfigParser* Factory(const std::string& param_name) {
+ return (new PoolParser(param_name));
+ }
+
+protected:
+ /// @brief pointer to the actual Pools storage
+ ///
+ /// That is typically a storage somewhere in Subnet parser
+ /// (an upper level parser).
+ PoolStorage* pools_;
+};
+
+/// @brief this class parses a single subnet
+///
+/// This class parses the whole subnet definition. It creates parsers
+/// for received configuration parameters as needed.
+class Subnet6ConfigParser : public DhcpConfigParser {
+public:
+
+ /// @brief constructor
+ Subnet6ConfigParser(const std::string& ) {
+ // The parameter should always be "subnet", but we don't check here
+ // against it in case some wants to reuse this parser somewhere.
+ }
+
+ /// @brief parses parameter value
+ ///
+ /// @param subnet pointer to the content of subnet definition
+ void build(ConstElementPtr subnet) {
+
+ BOOST_FOREACH(ConfigPair param, subnet->mapValue()) {
+
+ ParserPtr parser(createSubnet6ConfigParser(param.first));
+
+ // if this is an Uint32 parser, tell it to store the values
+ // in values_, rather than in global storage
+ boost::shared_ptr<Uint32Parser> uintParser =
+ boost::dynamic_pointer_cast<Uint32Parser>(parser);
+ if (uintParser) {
+ uintParser->setStorage(&uint32_values_);
+ } else {
+
+ boost::shared_ptr<StringParser> stringParser =
+ boost::dynamic_pointer_cast<StringParser>(parser);
+ if (stringParser) {
+ stringParser->setStorage(&string_values_);
+ } else {
+
+ boost::shared_ptr<PoolParser> poolParser =
+ boost::dynamic_pointer_cast<PoolParser>(parser);
+ if (poolParser) {
+ poolParser->setStorage(&pools_);
+ }
+ }
+ }
+
+ parser->build(param.second);
+ parsers_.push_back(parser);
+ }
+
+ // Ok, we now have subnet parsed
+ }
+
+ /// @brief commits received configuration.
+ ///
+ /// This method does most of the configuration. Many other parsers are just
+ /// storing the values that are actually consumed here. Pool definitions
+ /// created in other parsers are used here and added to newly created Subnet6
+ /// objects. Subnet6 are then added to DHCP CfgMgr.
+ void commit() {
+
+ StringStorage::const_iterator it = string_values_.find("subnet");
+ if (it == string_values_.end()) {
+ isc_throw(Dhcp6ConfigError,
+ "Mandatory subnet definition in subnet missing");
+ }
+ string subnet_txt = it->second;
+ boost::erase_all(subnet_txt, " ");
+ boost::erase_all(subnet_txt, "\t");
+
+ size_t pos = subnet_txt.find("/");
+ if (pos == string::npos) {
+ isc_throw(Dhcp6ConfigError,
+ "Invalid subnet syntax (prefix/len expected):" << it->second);
+ }
+ IOAddress addr(subnet_txt.substr(0, pos));
+ uint8_t len = boost::lexical_cast<unsigned int>(subnet_txt.substr(pos + 1));
+
+ Triplet<uint32_t> t1 = getParam("renew-timer");
+ Triplet<uint32_t> t2 = getParam("rebind-timer");
+ Triplet<uint32_t> pref = getParam("preferred-lifetime");
+ Triplet<uint32_t> valid = getParam("valid-lifetime");
+
+ /// @todo: Convert this to logger once the parser is working reliably
+ stringstream tmp;
+ tmp << addr.toText() << "/" << (int)len
+ << " with params t1=" << t1 << ", t2=" << t2 << ", pref="
+ << pref << ", valid=" << valid;
+
+ LOG_INFO(dhcp6_logger, DHCP6_CONFIG_NEW_SUBNET).arg(tmp.str());
+
+ Subnet6Ptr subnet(new Subnet6(addr, len, t1, t2, pref, valid));
+
+ for (PoolStorage::iterator it = pools_.begin(); it != pools_.end(); ++it) {
+ subnet->addPool6(*it);
+ }
+
+ CfgMgr::instance().addSubnet6(subnet);
+ }
+
+protected:
+
+ /// @brief creates parsers for entries in subnet definition
+ ///
+ /// @todo Add subnet-specific things here (e.g. subnet-specific options)
+ ///
+ /// @param config_id name od the entry
+ /// @return parser object for specified entry name
+ DhcpConfigParser* createSubnet6ConfigParser(const std::string& config_id) {
+ FactoryMap factories;
+
+ factories.insert(pair<string, ParserFactory*>(
+ "preferred-lifetime", Uint32Parser::Factory));
+ factories.insert(pair<string, ParserFactory*>(
+ "valid-lifetime", Uint32Parser::Factory));
+ factories.insert(pair<string, ParserFactory*>(
+ "renew-timer", Uint32Parser::Factory));
+ factories.insert(pair<string, ParserFactory*>(
+ "rebind-timer", Uint32Parser::Factory));
+
+ factories.insert(pair<string, ParserFactory*>(
+ "subnet", StringParser::Factory));
+
+ factories.insert(pair<string, ParserFactory*>(
+ "pool", PoolParser::Factory));
+
+ FactoryMap::iterator f = factories.find(config_id);
+ if (f == factories.end()) {
+ // Used for debugging only.
+ // return new DebugParser(config_id);
+
+ isc_throw(NotImplemented,
+ "Parser error: Subnet6 parameter not supported: "
+ << config_id);
+ }
+ return (f->second(config_id));
+ }
+
+ /// @brief returns value for a given parameter (after using inheritance)
+ ///
+ /// This method implements inheritance. For a given parameter name, it first
+ /// checks if there is a global value for it and overwrites it with specific
+ /// value if such value was defined in subnet.
+ ///
+ /// @param name name of the parameter
+ /// @return triplet with the parameter name
+ Triplet<uint32_t> getParam(const std::string& name) {
+ uint32_t value = 0;
+ bool found = false;
+ Uint32Storage::iterator global = uint32_defaults.find(name);
+ if (global != uint32_defaults.end()) {
+ value = global->second;
+ found = true;
+ }
+
+ Uint32Storage::iterator local = uint32_values_.find(name);
+ if (local != uint32_values_.end()) {
+ value = local->second;
+ found = true;
+ }
+
+ if (found) {
+ return (Triplet<uint32_t>(value));
+ } else {
+ isc_throw(Dhcp6ConfigError, "Mandatory parameter " << name
+ << " missing (no global default and no subnet-"
+ << "specific value)");
+ }
+ }
+
+ /// storage for subnet-specific uint32 values
+ Uint32Storage uint32_values_;
+
+ /// storage for subnet-specific integer values
+ StringStorage string_values_;
+
+ /// storage for pools belonging to this subnet
+ PoolStorage pools_;
+
+ /// parsers are stored here
+ ParserCollection parsers_;
+};
+
+/// @brief this class parses list of subnets
+///
+/// This is a wrapper parser that handles the whole list of Subnet6
+/// definitions. It iterates over all entries and creates Subnet6ConfigParser
+/// for each entry.
+class Subnets6ListConfigParser : public DhcpConfigParser {
+public:
+
+ /// @brief constructor
+ ///
+ Subnets6ListConfigParser(const std::string&) {
+ /// parameter name is ignored
+ }
+
+ /// @brief parses contents of the list
+ ///
+ /// Iterates over all entries on the list and creates Subnet6ConfigParser
+ /// for each entry.
+ ///
+ /// @param subnets_list pointer to a list of IPv6 subnets
+ void build(ConstElementPtr subnets_list) {
+
+ // No need to define FactoryMap here. There's only one type
+ // used: Subnet6ConfigParser
+
+ BOOST_FOREACH(ConstElementPtr subnet, subnets_list->listValue()) {
+
+ ParserPtr parser(new Subnet6ConfigParser("subnet"));
+ parser->build(subnet);
+ subnets_.push_back(parser);
+ }
+
+ }
+
+ /// @brief commits subnets definitions.
+ ///
+ /// Iterates over all Subnet6 parsers. Each parser contains definitions
+ /// of a single subnet and its parameters and commits each subnet separately.
+ void commit() {
+ // @todo: Implement more subtle reconfiguration than toss
+ // the old one and replace with the new one.
+
+ // remove old subnets
+ CfgMgr::instance().deleteSubnets6();
+
+ BOOST_FOREACH(ParserPtr subnet, subnets_) {
+ subnet->commit();
+ }
+
+ }
+
+ /// @brief Returns Subnet6ListConfigParser object
+ /// @param param_name name of the parameter
+ /// @return Subnets6ListConfigParser object
+ static DhcpConfigParser* Factory(const std::string& param_name) {
+ return (new Subnets6ListConfigParser(param_name));
+ }
+
+ /// @brief collection of subnet parsers.
+ ParserCollection subnets_;
+};
+
+/// @brief creates global parsers
+///
+/// This method creates global parsers that parse global parameters, i.e.
+/// those that take format of Dhcp6/param1, Dhcp6/param2 and so forth.
+///
+/// @param config_id pointer to received global configuration entry
+/// @return parser for specified global DHCPv6 parameter
+DhcpConfigParser* createGlobalDhcpConfigParser(const std::string& config_id) {
+ FactoryMap factories;
+
+ //
+ factories.insert(pair<string, ParserFactory*>(
+ "preferred-lifetime", Uint32Parser::Factory));
+ factories.insert(pair<string, ParserFactory*>(
+ "valid-lifetime", Uint32Parser::Factory));
+ factories.insert(pair<string, ParserFactory*>(
+ "renew-timer", Uint32Parser::Factory));
+ factories.insert(pair<string, ParserFactory*>(
+ "rebind-timer", Uint32Parser::Factory));
+
+ factories.insert(pair<string, ParserFactory*>(
+ "interface", InterfaceListConfigParser::Factory));
+ factories.insert(pair<string, ParserFactory*>(
+ "subnet6", Subnets6ListConfigParser::Factory));
+
+ factories.insert(pair<string, ParserFactory*>(
+ "version", StringParser::Factory));
+
+ FactoryMap::iterator f = factories.find(config_id);
+ if (f == factories.end()) {
+ // Used for debugging only.
+ // return new DebugParser(config_id);
+
+ isc_throw(NotImplemented,
+ "Parser error: Global configuration parameter not supported: "
+ << config_id);
+ }
+ return (f->second(config_id));
+}
+
+/// @brief configures DHCPv6 server
+///
+/// This function is called every time a new configuration is received. The extra
+/// parameter is a reference to DHCPv6 server component. It is currently not used
+/// and CfgMgr::instance() is accessed instead.
+///
+/// This method does not throw. It catches all exceptions and returns them as
+/// reconfiguration statuses. It may return the following response codes:
+/// 0 - configuration successful
+/// 1 - malformed configuration (parsing failed)
+/// 2 - logical error (parsing was successful, but the values are invalid)
+///
+/// @param config_set a new configuration for DHCPv6 server
+/// @return answer that contains result of reconfiguration
+ConstElementPtr
+configureDhcp6Server(Dhcpv6Srv& , ConstElementPtr config_set) {
+ if (!config_set) {
+ isc_throw(Dhcp6ConfigError,
+ "Null pointer is passed to configuration parser");
+ }
+
+ /// @todo: append most essential info here (like "2 new subnets configured")
+ string config_details;
+
+ LOG_DEBUG(dhcp6_logger, DBG_DHCP6_COMMAND, DHCP6_CONFIG_START).arg(config_set->str());
+
+ ParserCollection parsers;
+ try {
+ BOOST_FOREACH(ConfigPair config_pair, config_set->mapValue()) {
+
+ ParserPtr parser(createGlobalDhcpConfigParser(config_pair.first));
+ parser->build(config_pair.second);
+ parsers.push_back(parser);
+ }
+ } catch (const isc::Exception& ex) {
+ ConstElementPtr answer = isc::config::createAnswer(1,
+ string("Configuration parsing failed:") + ex.what());
+ return (answer);
+ } catch (...) {
+ // for things like bad_cast in boost::lexical_cast
+ ConstElementPtr answer = isc::config::createAnswer(1,
+ string("Configuration parsing failed"));
+ }
+
+ try {
+ BOOST_FOREACH(ParserPtr parser, parsers) {
+ parser->commit();
+ }
+ }
+ catch (const isc::Exception& ex) {
+ ConstElementPtr answer = isc::config::createAnswer(2,
+ string("Configuration commit failed:") + ex.what());
+ return (answer);
+ } catch (...) {
+ // for things like bad_cast in boost::lexical_cast
+ ConstElementPtr answer = isc::config::createAnswer(2,
+ string("Configuration commit failed"));
+ }
+
+ LOG_INFO(dhcp6_logger, DHCP6_CONFIG_COMPLETE).arg(config_details);
+
+ ConstElementPtr answer = isc::config::createAnswer(0, "Configuration commited.");
+ return (answer);
+}
+
+}; // end of isc::dhcp namespace
+}; // end of isc namespace
diff --git a/src/bin/dhcp6/config_parser.h b/src/bin/dhcp6/config_parser.h
new file mode 100644
index 0000000..6758c99
--- /dev/null
+++ b/src/bin/dhcp6/config_parser.h
@@ -0,0 +1,147 @@
+// Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <string>
+#include <exceptions/exceptions.h>
+#include <cc/data.h>
+
+#ifndef DHCP6_CONFIG_PARSER_H
+#define DHCP6_CONFIG_PARSER_H
+
+namespace isc {
+namespace dhcp {
+
+class Dhcpv6Srv;
+
+/// An exception that is thrown if an error occurs while configuring an
+/// \c Dhcpv6Srv object.
+class Dhcp6ConfigError : public isc::Exception {
+public:
+
+/// @brief constructor
+///
+/// @param file name of the file, where exception occurred
+/// @param line line of the file, where exception occurred
+/// @param what text description of the issue that caused exception
+Dhcp6ConfigError(const char* file, size_t line, const char* what) :
+ isc::Exception(file, line, what) {}
+};
+
+class DhcpConfigParser {
+ ///
+ /// \name Constructors and Destructor
+ ///
+ /// Note: The copy constructor and the assignment operator are
+ /// intentionally defined as private to make it explicit that this is a
+ /// pure base class.
+ //@{
+private:
+ DhcpConfigParser(const DhcpConfigParser& source);
+ DhcpConfigParser& operator=(const DhcpConfigParser& source);
+protected:
+ /// \brief The default constructor.
+ ///
+ /// This is intentionally defined as \c protected as this base class should
+ /// never be instantiated (except as part of a derived class).
+ DhcpConfigParser() {}
+public:
+ /// The destructor.
+ virtual ~DhcpConfigParser() {}
+ //@}
+
+ /// \brief Prepare configuration value.
+ ///
+ /// This method parses the "value part" of the configuration identifier
+ /// that corresponds to this derived class and prepares a new value to
+ /// apply to the server.
+ ///
+ /// This method must validate the given value both in terms of syntax
+ /// and semantics of the configuration, so that the server will be
+ /// validly configured at the time of \c commit(). Note: the given
+ /// configuration value is normally syntactically validated, but the
+ /// \c build() implementation must also expect invalid input. If it
+ /// detects an error it may throw an exception of a derived class
+ /// of \c isc::Exception.
+ ///
+ /// Preparing a configuration value will often require resource
+ /// allocation. If it fails, it may throw a corresponding standard
+ /// exception.
+ ///
+ /// This method is not expected to be called more than once in the
+ /// life of the object. Although multiple calls are not prohibited
+ /// by the interface, the behavior is undefined.
+ ///
+ /// \param config_value The configuration value for the identifier
+ /// corresponding to the derived class.
+ virtual void build(isc::data::ConstElementPtr config_value) = 0;
+
+ /// \brief Apply the prepared configuration value to the server.
+ ///
+ /// This method is expected to be exception free, and, as a consequence,
+ /// it should normally not involve resource allocation.
+ /// Typically it would simply perform exception free assignment or swap
+ /// operation on the value prepared in \c build().
+ /// In some cases, however, it may be very difficult to meet this
+ /// condition in a realistic way, while the failure case should really
+ /// be very rare. In such a case it may throw, and, if the parser is
+ /// called via \c configureDhcp6Server(), the caller will convert the
+ /// exception as a fatal error.
+ ///
+ /// This method is expected to be called after \c build(), and only once.
+ /// The result is undefined otherwise.
+ virtual void commit() = 0;
+};
+
+/// @brief a pointer to configuration parser
+typedef boost::shared_ptr<DhcpConfigParser> ParserPtr;
+
+/// @brief a collection of parsers
+///
+/// This container is used to store pointer to parsers for a given scope.
+typedef std::vector<ParserPtr> ParserCollection;
+
+
+/// \brief Configure an \c Dhcpv6Srv object with a set of configuration values.
+///
+/// This function parses configuration information stored in \c config_set
+/// and configures the \c server by applying the configuration to it.
+/// It provides the strong exception guarantee as long as the underlying
+/// derived class implementations of \c DhcpConfigParser meet the assumption,
+/// that is, it ensures that either configuration is fully applied or the
+/// state of the server is intact.
+///
+/// If a syntax or semantics level error happens during the configuration
+/// (such as malformed configuration or invalid configuration parameter),
+/// this function throws an exception of class \c Dhcp6ConfigError.
+/// If the given configuration requires resource allocation and it fails,
+/// a corresponding standard exception will be thrown.
+/// Other exceptions may also be thrown, depending on the implementation of
+/// the underlying derived class of \c Dhcp6ConfigError.
+/// In any case the strong guarantee is provided as described above except
+/// in the very rare cases where the \c commit() method of a parser throws
+/// an exception. If that happens this function converts the exception
+/// into a \c FatalError exception and rethrows it. This exception is
+/// expected to be caught at the highest level of the application to terminate
+/// the program gracefully.
+///
+/// \param server The \c Dhcpv6Srv object to be configured.
+/// \param config_set A JSON style configuration to apply to \c server.
+isc::data::ConstElementPtr
+configureDhcp6Server(Dhcpv6Srv& server,
+ isc::data::ConstElementPtr config_set);
+
+}; // end of isc::dhcp namespace
+}; // end of isc namespace
+
+#endif // DHCP6_CONFIG_PARSER_H
diff --git a/src/bin/dhcp6/ctrl_dhcp6_srv.cc b/src/bin/dhcp6/ctrl_dhcp6_srv.cc
index 4afb203..7370583 100644
--- a/src/bin/dhcp6/ctrl_dhcp6_srv.cc
+++ b/src/bin/dhcp6/ctrl_dhcp6_srv.cc
@@ -25,6 +25,7 @@
#include <dhcp6/ctrl_dhcp6_srv.h>
#include <dhcp6/dhcp6_log.h>
#include <dhcp6/spec_config.h>
+#include <dhcp6/config_parser.h>
#include <dhcp/iface_mgr.h>
#include <exceptions/exceptions.h>
#include <util/buffer.h>
@@ -47,8 +48,15 @@ ConstElementPtr
ControlledDhcpv6Srv::dhcp6ConfigHandler(ConstElementPtr new_config) {
LOG_DEBUG(dhcp6_logger, DBG_DHCP6_COMMAND, DHCP6_CONFIG_UPDATE)
.arg(new_config->str());
- ConstElementPtr answer = isc::config::createAnswer(0,
- "Thank you for sending config.");
+
+ if (server_) {
+ return (configureDhcp6Server(*server_, new_config));
+ }
+
+ // That should never happen as we install config_handler after we instantiate
+ // the server.
+ ConstElementPtr answer = isc::config::createAnswer(1,
+ "Configuration rejected, server is during startup/shutdown phase.");
return (answer);
}
@@ -86,7 +94,7 @@ void ControlledDhcpv6Srv::sessionReader(void) {
}
void ControlledDhcpv6Srv::establishSession() {
-
+
string specfile;
if (getenv("B10_FROM_BUILD")) {
specfile = string(getenv("B10_FROM_BUILD")) +
@@ -96,15 +104,27 @@ void ControlledDhcpv6Srv::establishSession() {
}
/// @todo: Check if session is not established already. Throw, if it is.
-
+
LOG_DEBUG(dhcp6_logger, DBG_DHCP6_START, DHCP6_CCSESSION_STARTING)
.arg(specfile);
cc_session_ = new Session(io_service_.get_io_service());
config_session_ = new ModuleCCSession(specfile, *cc_session_,
- dhcp6ConfigHandler,
+ NULL,
dhcp6CommandHandler, false);
config_session_->start();
+ // We initially create ModuleCCSession() without configHandler, as
+ // the session module is too eager to send partial configuration.
+ // We want to get the full configuration, so we explicitly call
+ // getFullConfig() and then pass it to our configHandler.
+ config_session_->setConfigHandler(dhcp6ConfigHandler);
+
+ try {
+ configureDhcp6Server(*this, config_session_->getFullConfig());
+ } catch (const Dhcp6ConfigError& ex) {
+ LOG_ERROR(dhcp6_logger, DHCP6_CONFIG_LOAD_FAIL).arg(ex.what());
+ }
+
/// Integrate the asynchronous I/O model of BIND 10 configuration
/// control with the "select" model of the DHCP server. This is
/// fully explained in \ref dhcpv6Session.
diff --git a/src/bin/dhcp6/dhcp6.dox b/src/bin/dhcp6/dhcp6.dox
new file mode 100644
index 0000000..fe842de
--- /dev/null
+++ b/src/bin/dhcp6/dhcp6.dox
@@ -0,0 +1,79 @@
+/**
+ @page dhcpv6 DHCPv6 Server Component
+
+ BIND10 offers DHCPv6 server implementation. It is implemented as
+ b10-dhcp6 component. Its primary code is located in
+ isc::dhcp::Dhcpv6Srv class. It uses \ref libdhcp extensively,
+ especially lib::dhcp::Pkt6, isc::dhcp::Option and
+ isc::dhcp::IfaceMgr classes. Currently this code offers skeleton
+ functionality, i.e. it is able to receive and process incoming
+ requests and trasmit responses. However, it does not have database
+ management, so it returns only one, hardcoded lease to whoever asks
+ for it.
+
+ DHCPv6 server component does not support relayed traffic yet, as
+ support for relay decapsulation is not implemented yet.
+
+ DHCPv6 server component does not use BIND10 logging yet.
+
+ @section dhcpv6-session BIND10 message queue integration
+
+ DHCPv4 server component is now integrated with BIND10 message queue.
+ It follows the same principle as DHCPv4. See \ref dhcpv4Session for
+ details.
+
+ @section dhcpv6-config-parser Configuration Parser in DHCPv6
+
+ b10-dhcp6 component uses BIND10 cfgmgr for commands and configuration. During
+ initial configuration (See \ref
+ isc::dhcp::ControlledDhcpv6Srv::establishSession()), the configuration handler
+ callback is installed (see isc::dhcp::ControlledDhcpv6Srv::dhcp6ConfigHandler().
+ It is called every time there is a new configuration. In particular, it is
+ called every time during daemon start process. It contains a
+ isc::data::ConstElementPtr to a new configuration. This simple handler calls
+ \ref isc::dhcp::configureDhcp6Server() method that processes received configuration.
+
+ This method iterates over list of received configuration elements and creates a
+ list of parsers for each received entry. Parser is an object that is derived
+ from a \ref isc::dhcp::Dhcp6ConfigParser class. Once a parser is created
+ (constructor), its value is set (using build() method). Once all parsers are
+ build, the configuration is then applied ("commited") and commit() method is
+ called.
+
+ All parsers are defined in src/bin/dhcp6/config_parser.cc file. Some of them
+ are generic (e.g. \ref isc::dhcp::Uint32Parser that is able to handle any
+ unsigned 32 bit integer), but some are very specialized (e.g. \ref
+ isc::dhcp::Subnets6ListConfigParser parses definitions of Subnet6 lists). In
+ some cases, e.g. subnet6 definitions, the configuration entry is not a simple
+ value, but a map or a list itself. In such case, the parser iterates over all
+ elements and creates parsers for a given scope. This process may be repeated
+ (sort of) recursively.
+
+ @section dhcpv6-config-inherit DHCPv6 Configuration Inheritance
+
+ One notable useful features of DHCP configuration is its parameter inheritance.
+ For example, renew-timer value may be specified at a global scope and it then
+ applies to all subnets. However, some subnets may have it overwritten with more
+ specific values that takes precedence over global values that are considered
+ defaults. Some parsers (e.g. \ref isc::dhcp::Uint32Parser and \ref
+ isc::dhcp::StringParser) implement that inheritance. By default, they store
+ values in global uint32_defaults and string_defaults storages. However, it is
+ possible to instruct them to store parsed values in more specific
+ storages. That capability is used, e.g. in \ref isc::dhcp::Subnet6ConfigParser
+ that has its own storage that is unique for each subnet. Finally, during commit
+ phase (commit() method), appropriate parsers can use apply parameter inheritance.
+
+ Debugging configuration parser may be confusing. Therefore there is a special
+ class called \ref isc::dhcp::DummyParser. It does not configure anything, but just
+ accepts any parameter of any type. If requested to commit configuration, it will
+ print out received parameter name and its value. This class is not currently used,
+ but it is convenient to have it every time a new parameter is added to DHCP
+ configuration. For that purpose it should be left in the code.
+
+ Parameter inheritance is done during reconfiguration phase, as reconfigurations
+ are rare, so extra logic here is not a problem. On the other hand, values of
+ those parameters may be used thousands times per second, so its use must be as
+ simple as possible. In fact, currently the code has to call Subnet6->getT1() and
+ do not implement any fancy inheritance logic.
+
+ */
diff --git a/src/bin/dhcp6/dhcp6.spec b/src/bin/dhcp6/dhcp6.spec
index 2a82a2d..f35f606 100644
--- a/src/bin/dhcp6/dhcp6.spec
+++ b/src/bin/dhcp6/dhcp6.spec
@@ -4,9 +4,97 @@
"module_description": "DHCPv6 server daemon",
"config_data": [
{ "item_name": "interface",
- "item_type": "string",
+ "item_type": "list",
"item_optional": false,
- "item_default": "eth0"
+ "item_default": [ "all" ],
+ "list_item_spec":
+ {
+ "item_name": "interface_name",
+ "item_type": "string",
+ "item_optional": false,
+ "item_default": "all"
+ }
+ } ,
+
+ { "item_name": "renew-timer",
+ "item_type": "integer",
+ "item_optional": false,
+ "item_default": 1000
+ },
+
+ { "item_name": "rebind-timer",
+ "item_type": "integer",
+ "item_optional": false,
+ "item_default": 2000
+ },
+
+ { "item_name": "preferred-lifetime",
+ "item_type": "integer",
+ "item_optional": false,
+ "item_default": 3000
+ },
+
+ { "item_name": "valid-lifetime",
+ "item_type": "integer",
+ "item_optional": false,
+ "item_default": 4000
+ },
+
+ { "item_name": "subnet6",
+ "item_type": "list",
+ "item_optional": false,
+ "item_default": [],
+ "list_item_spec":
+ {
+ "item_name": "single-subnet6",
+ "item_type": "map",
+ "item_optional": false,
+ "item_default": {},
+ "map_item_spec": [
+
+ { "item_name": "subnet",
+ "item_type": "string",
+ "item_optional": false,
+ "item_default": ""
+ },
+
+ { "item_name": "renew-timer",
+ "item_type": "integer",
+ "item_optional": false,
+ "item_default": 1000
+ },
+
+ { "item_name": "rebind-timer",
+ "item_type": "integer",
+ "item_optional": false,
+ "item_default": 2000
+ },
+
+ { "item_name": "preferred-lifetime",
+ "item_type": "integer",
+ "item_optional": false,
+ "item_default": 3000
+ },
+
+ { "item_name": "valid-lifetime",
+ "item_type": "integer",
+ "item_optional": false,
+ "item_default": 7200
+ },
+ { "item_name": "pool",
+ "item_type": "list",
+ "item_optional": false,
+ "item_default": [],
+ "list_item_spec":
+ {
+ "item_name": "type",
+ "item_type": "string",
+ "item_optional": false,
+ "item_default": ""
+ }
+ }
+ ]
+ }
}
],
"commands": [
diff --git a/src/bin/dhcp6/dhcp6_messages.mes b/src/bin/dhcp6/dhcp6_messages.mes
index 1564940..2399c19 100644
--- a/src/bin/dhcp6/dhcp6_messages.mes
+++ b/src/bin/dhcp6/dhcp6_messages.mes
@@ -109,3 +109,23 @@ processed any command-line switches and is starting.
This is a debug message issued during the IPv6 DHCP server startup.
It lists some information about the parameters with which the server
is running.
+
+% DHCP6_CONFIG_LOAD_FAIL failed to load configuration: %1
+This critical error message indicates that the initial DHCPv6
+configuration has failed. The server will start, but nothing will be
+served until the configuration has been corrected.
+
+% DHCP6_CONFIG_START DHCPv6 server is processing the following configuration: %1
+This is a debug message that is issued every time the server receives a
+configuration. That happens start up and also when a server configuration
+change is committed by the administrator.
+
+% DHCP6_CONFIG_NEW_SUBNET A new subnet has been added to configuration: %1
+This is an informational message reporting that the configuration has
+been extended to include the specified subnet.
+
+% DHCP6_CONFIG_COMPLETE DHCPv6 server has completed configuration: %1
+This is an informational message announcing the successful processing of a
+new configuration. it is output during server startup, and when an updated
+configuration is committed by the administrator. Additional information
+may be provided.
diff --git a/src/bin/dhcp6/dhcp6_srv.cc b/src/bin/dhcp6/dhcp6_srv.cc
index 54fa2b5..7c57a61 100644
--- a/src/bin/dhcp6/dhcp6_srv.cc
+++ b/src/bin/dhcp6/dhcp6_srv.cc
@@ -42,6 +42,13 @@ const uint32_t HARDCODED_VALID_LIFETIME = 7200; // in seconds
const std::string HARDCODED_DNS_SERVER = "2001:db8:1::1";
Dhcpv6Srv::Dhcpv6Srv(uint16_t port) {
+ if (port == 0) {
+ // used for testing purposes. Some tests, e.g. configuration parser,
+ // require Dhcpv6Srv object, but they don't really need it to do
+ // anything. This speed up and simplifies the tests.
+ return;
+ }
+
LOG_DEBUG(dhcp6_logger, DBG_DHCP6_START, DHCP6_OPEN_SOCKET).arg(port);
// First call to instance() will create IfaceMgr (it's a singleton)
diff --git a/src/bin/dhcp6/tests/Makefile.am b/src/bin/dhcp6/tests/Makefile.am
index 1d9308f..1629ae6 100644
--- a/src/bin/dhcp6/tests/Makefile.am
+++ b/src/bin/dhcp6/tests/Makefile.am
@@ -15,6 +15,7 @@ check-local:
for pytest in $(PYTESTS) ; do \
echo Running test: $$pytest ; \
$(LIBRARY_PATH_PLACEHOLDER) \
+ B10_LOCKFILE_DIR_FROM_BUILD=$(abs_top_builddir) \
PYTHONPATH=$(COMMON_PYTHON_PATH):$(abs_top_srcdir)/src/bin:$(abs_top_builddir)/src/bin/bind10:$(abs_top_builddir)/src/lib/util/io/.libs \
$(PYCOVERAGE_RUN) $(abs_srcdir)/$$pytest || exit ; \
done
@@ -45,9 +46,11 @@ TESTS += dhcp6_unittests
dhcp6_unittests_SOURCES = dhcp6_unittests.cc
dhcp6_unittests_SOURCES += dhcp6_srv_unittest.cc
dhcp6_unittests_SOURCES += ctrl_dhcp6_srv_unittest.cc
+dhcp6_unittests_SOURCES += config_parser_unittest.cc
dhcp6_unittests_SOURCES += ../dhcp6_srv.h ../dhcp6_srv.cc
dhcp6_unittests_SOURCES += ../dhcp6_log.h ../dhcp6_log.cc
dhcp6_unittests_SOURCES += ../ctrl_dhcp6_srv.cc
+dhcp6_unittests_SOURCES += ../config_parser.cc ../config_parser.h
nodist_dhcp6_unittests_SOURCES = ../dhcp6_messages.h ../dhcp6_messages.cc
if USE_CLANGPP
@@ -61,6 +64,7 @@ dhcp6_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
dhcp6_unittests_LDADD = $(GTEST_LDADD)
dhcp6_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libb10-asiolink.la
dhcp6_unittests_LDADD += $(top_builddir)/src/lib/dhcp/libb10-dhcp++.la
+dhcp6_unittests_LDADD += $(top_builddir)/src/lib/dhcp/libb10-dhcpsrv.la
dhcp6_unittests_LDADD += $(top_builddir)/src/lib/log/libb10-log.la
dhcp6_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libb10-exceptions.la
dhcp6_unittests_LDADD += $(top_builddir)/src/lib/config/libb10-cfgclient.la
diff --git a/src/bin/dhcp6/tests/config_parser_unittest.cc b/src/bin/dhcp6/tests/config_parser_unittest.cc
new file mode 100644
index 0000000..22592e8
--- /dev/null
+++ b/src/bin/dhcp6/tests/config_parser_unittest.cc
@@ -0,0 +1,243 @@
+// Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <config.h>
+#include <iostream>
+#include <fstream>
+#include <sstream>
+
+#include <arpa/inet.h>
+#include <gtest/gtest.h>
+
+#include <dhcp6/dhcp6_srv.h>
+#include <dhcp6/config_parser.h>
+#include <config/ccsession.h>
+#include <dhcp/subnet.h>
+#include <dhcp/cfgmgr.h>
+
+using namespace std;
+using namespace isc;
+using namespace isc::dhcp;
+using namespace isc::asiolink;
+using namespace isc::data;
+using namespace isc::config;
+
+namespace {
+
+class Dhcp6ParserTest : public ::testing::Test {
+public:
+ Dhcp6ParserTest()
+ :rcode_(-1) {
+ // Open port 0 means to not do anything at all. We don't want to
+ // deal with sockets here, just check if configuration handling
+ // is sane.
+ srv_ = new Dhcpv6Srv(0);
+ }
+
+ ~Dhcp6ParserTest() {
+ delete srv_;
+ };
+
+ Dhcpv6Srv* srv_;
+
+ int rcode_;
+ ConstElementPtr comment_;
+};
+
+// Goal of this test is a verification if a very simple config update
+// with just a bumped version number. That's the simplest possible
+// config update.
+TEST_F(Dhcp6ParserTest, version) {
+
+ ConstElementPtr x;
+
+ EXPECT_NO_THROW(x = configureDhcp6Server(*srv_,
+ Element::fromJSON("{\"version\": 0}")));
+
+ // returned value must be 0 (configuration accepted)
+ ASSERT_TRUE(x);
+ comment_ = parseAnswer(rcode_, x);
+ EXPECT_EQ(0, rcode_);
+}
+
+/// The goal of this test is to verify that the code accepts only
+/// valid commands and malformed or unsupported parameters are rejected.
+TEST_F(Dhcp6ParserTest, bogus_command) {
+
+ ConstElementPtr x;
+
+ EXPECT_NO_THROW(x = configureDhcp6Server(*srv_,
+ Element::fromJSON("{\"bogus\": 5}")));
+
+ // returned value must be 1 (configuration parse error)
+ ASSERT_TRUE(x);
+ comment_ = parseAnswer(rcode_, x);
+ EXPECT_EQ(1, rcode_);
+}
+
+/// The goal of this test is to verify if wrongly defined subnet will
+/// be rejected. Properly defined subnet must include at least one
+/// pool definition.
+TEST_F(Dhcp6ParserTest, empty_subnet) {
+
+ ConstElementPtr status;
+
+ EXPECT_NO_THROW(status = configureDhcp6Server(*srv_,
+ Element::fromJSON("{ \"interface\": [ \"all\" ],"
+ "\"preferred-lifetime\": 3000,"
+ "\"rebind-timer\": 2000, "
+ "\"renew-timer\": 1000, "
+ "\"subnet6\": [ ], "
+ "\"valid-lifetime\": 4000 }")));
+
+ // returned value should be 0 (success)
+ ASSERT_TRUE(status);
+ comment_ = parseAnswer(rcode_, status);
+ EXPECT_EQ(0, rcode_);
+}
+
+/// The goal of this test is to verify if defined subnet uses global
+/// parameter timer definitions.
+TEST_F(Dhcp6ParserTest, subnet_global_defaults) {
+
+ ConstElementPtr status;
+
+ string config = "{ \"interface\": [ \"all\" ],"
+ "\"preferred-lifetime\": 3000,"
+ "\"rebind-timer\": 2000, "
+ "\"renew-timer\": 1000, "
+ "\"subnet6\": [ { "
+ " \"pool\": [ \"2001:db8:1::1 - 2001:db8:1::ffff\" ],"
+ " \"subnet\": \"2001:db8:1::/64\" } ],"
+ "\"valid-lifetime\": 4000 }";
+ cout << config << endl;
+
+ ElementPtr json = Element::fromJSON(config);
+
+ EXPECT_NO_THROW(status = configureDhcp6Server(*srv_, json));
+
+ // check if returned status is OK
+ ASSERT_TRUE(status);
+ comment_ = parseAnswer(rcode_, status);
+ EXPECT_EQ(0, rcode_);
+
+ // Now check if the configuration was indeed handled and we have
+ // expected pool configured.
+ Subnet6Ptr subnet = CfgMgr::instance().getSubnet6(IOAddress("2001:db8:1::5"));
+ ASSERT_TRUE(subnet);
+ EXPECT_EQ(1000, subnet->getT1());
+ EXPECT_EQ(2000, subnet->getT2());
+ EXPECT_EQ(3000, subnet->getPreferred());
+ EXPECT_EQ(4000, subnet->getValid());
+}
+
+// This test checks if it is possible to override global values
+// on a per subnet basis.
+TEST_F(Dhcp6ParserTest, subnet_local) {
+
+ ConstElementPtr status;
+
+ string config = "{ \"interface\": [ \"all\" ],"
+ "\"preferred-lifetime\": 3000,"
+ "\"rebind-timer\": 2000, "
+ "\"renew-timer\": 1000, "
+ "\"subnet6\": [ { "
+ " \"pool\": [ \"2001:db8:1::1 - 2001:db8:1::ffff\" ],"
+ " \"renew-timer\": 1, "
+ " \"rebind-timer\": 2, "
+ " \"preferred-lifetime\": 3,"
+ " \"valid-lifetime\": 4,"
+ " \"subnet\": \"2001:db8:1::/64\" } ],"
+ "\"valid-lifetime\": 4000 }";
+ cout << config << endl;
+
+ ElementPtr json = Element::fromJSON(config);
+
+ EXPECT_NO_THROW(status = configureDhcp6Server(*srv_, json));
+
+ // returned value should be 0 (configuration success)
+ ASSERT_TRUE(status);
+ comment_ = parseAnswer(rcode_, status);
+ EXPECT_EQ(0, rcode_);
+
+ Subnet6Ptr subnet = CfgMgr::instance().getSubnet6(IOAddress("2001:db8:1::5"));
+ ASSERT_TRUE(subnet);
+ EXPECT_EQ(1, subnet->getT1());
+ EXPECT_EQ(2, subnet->getT2());
+ EXPECT_EQ(3, subnet->getPreferred());
+ EXPECT_EQ(4, subnet->getValid());
+}
+
+// Test verifies that a subnet with pool values that do not belong to that
+// pool are rejected.
+TEST_F(Dhcp6ParserTest, pool_out_of_subnet) {
+
+ ConstElementPtr status;
+
+ string config = "{ \"interface\": [ \"all\" ],"
+ "\"preferred-lifetime\": 3000,"
+ "\"rebind-timer\": 2000, "
+ "\"renew-timer\": 1000, "
+ "\"subnet6\": [ { "
+ " \"pool\": [ \"4001:db8:1::/80\" ],"
+ " \"subnet\": \"2001:db8:1::/64\" } ],"
+ "\"valid-lifetime\": 4000 }";
+ cout << config << endl;
+
+ ElementPtr json = Element::fromJSON(config);
+
+ EXPECT_NO_THROW(status = configureDhcp6Server(*srv_, json));
+
+ // returned value must be 2 (values error)
+ // as the pool does not belong to that subnet
+ ASSERT_TRUE(status);
+ comment_ = parseAnswer(rcode_, status);
+ EXPECT_EQ(2, rcode_);
+}
+
+// Goal of this test is to verify if pools can be defined
+// using prefix/length notation. There is no separate test for min-max
+// notation as it was tested in several previous tests.
+TEST_F(Dhcp6ParserTest, pool_prefix_len) {
+
+ ConstElementPtr x;
+
+ string config = "{ \"interface\": [ \"all\" ],"
+ "\"preferred-lifetime\": 3000,"
+ "\"rebind-timer\": 2000, "
+ "\"renew-timer\": 1000, "
+ "\"subnet6\": [ { "
+ " \"pool\": [ \"2001:db8:1::/80\" ],"
+ " \"subnet\": \"2001:db8:1::/64\" } ],"
+ "\"valid-lifetime\": 4000 }";
+ cout << config << endl;
+
+ ElementPtr json = Element::fromJSON(config);
+
+ EXPECT_NO_THROW(x = configureDhcp6Server(*srv_, json));
+
+ // returned value must be 1 (configuration parse error)
+ ASSERT_TRUE(x);
+ comment_ = parseAnswer(rcode_, x);
+ EXPECT_EQ(0, rcode_);
+
+ Subnet6Ptr subnet = CfgMgr::instance().getSubnet6(IOAddress("2001:db8:1::5"));
+ ASSERT_TRUE(subnet);
+ EXPECT_EQ(1000, subnet->getT1());
+ EXPECT_EQ(2000, subnet->getT2());
+ EXPECT_EQ(3000, subnet->getPreferred());
+ EXPECT_EQ(4000, subnet->getValid());
+}
+
+};
diff --git a/src/bin/host/.gitignore b/src/bin/host/.gitignore
deleted file mode 100644
index 01ef357..0000000
--- a/src/bin/host/.gitignore
+++ /dev/null
@@ -1,2 +0,0 @@
-/b10-host
-/b10-host.1
diff --git a/src/bin/host/Makefile.am b/src/bin/host/Makefile.am
deleted file mode 100644
index 42ef954..0000000
--- a/src/bin/host/Makefile.am
+++ /dev/null
@@ -1,37 +0,0 @@
-AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
-AM_CPPFLAGS += -I$(top_srcdir)/src/lib/dns -I$(top_builddir)/src/lib/dns
-AM_CPPFLAGS += $(BOOST_INCLUDES)
-
-AM_CXXFLAGS = $(B10_CXXFLAGS)
-
-if USE_STATIC_LINK
-AM_LDFLAGS = -static
-endif
-
-CLEANFILES = *.gcno *.gcda
-
-bin_PROGRAMS = b10-host
-b10_host_SOURCES = host.cc
-b10_host_LDADD = $(top_builddir)/src/lib/dns/libb10-dns++.la
-b10_host_LDADD += $(top_builddir)/src/lib/util/libb10-util.la
-b10_host_LDADD += $(top_builddir)/src/lib/exceptions/libb10-exceptions.la
-
-man_MANS = b10-host.1
-DISTCLEANFILES = $(man_MANS)
-EXTRA_DIST = $(man_MANS) b10-host.xml
-
-.PHONY: man
-if GENERATE_DOCS
-
-man: b10-host.1
-
-b10-host.1: b10-host.xml
- @XSLTPROC@ --novalid --xinclude --nonet -o $@ http://docbook.sourceforge.net/release/xsl/current/manpages/docbook.xsl $(srcdir)/b10-host.xml
-
-else
-
-$(man_MANS):
- @echo Man generation disabled. Creating dummy $@. Configure with --enable-generate-docs to enable it.
- @echo Man generation disabled. Remove this file, configure with --enable-generate-docs, and rebuild BIND 10 > $@
-
-endif
diff --git a/src/bin/host/README b/src/bin/host/README
deleted file mode 100644
index 5cc4068..0000000
--- a/src/bin/host/README
+++ /dev/null
@@ -1,4 +0,0 @@
-Rewriting host(1) in C++ from scratch using BIND 10's libdns++.
-
-The bugs and incompatibilities are listed in the manual page
-and in the source code.
diff --git a/src/bin/host/b10-host.xml b/src/bin/host/b10-host.xml
deleted file mode 100644
index a17ef67..0000000
--- a/src/bin/host/b10-host.xml
+++ /dev/null
@@ -1,196 +0,0 @@
-<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
- "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd"
- [<!ENTITY mdash "—">]>
-<!--
- - Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
- -
- - Permission to use, copy, modify, and/or distribute this software for any
- - purpose with or without fee is hereby granted, provided that the above
- - copyright notice and this permission notice appear in all copies.
- -
- - THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
- - REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
- - AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
- - INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
- - LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
- - OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
- - PERFORMANCE OF THIS SOFTWARE.
--->
-
-<!-- $Id$ -->
-<refentry>
-
- <refentryinfo>
- <date>May 4, 2011</date>
- </refentryinfo>
-
- <refmeta>
- <refentrytitle>b10-host</refentrytitle>
- <manvolnum>1</manvolnum>
- <refmiscinfo>BIND10</refmiscinfo>
- </refmeta>
-
- <refnamediv>
- <refname>b10-host</refname>
- <refpurpose>DNS lookup utility</refpurpose>
- </refnamediv>
-
- <docinfo>
- <copyright>
- <year>2011</year>
- <holder>Internet Systems Consortium, Inc. ("ISC")</holder>
- </copyright>
- </docinfo>
-
- <refsynopsisdiv>
- <cmdsynopsis>
- <command>b10-host</command>
- <arg><option>-a</option></arg>
- <arg><option>-c <replaceable>class</replaceable></option></arg>
- <arg><option>-d</option></arg>
- <arg><option>-p <replaceable>port</replaceable></option></arg>
- <arg><option>-r</option></arg>
- <arg><option>-t <replaceable>type</replaceable></option></arg>
- <arg><option>-v</option></arg>
- <arg><replaceable>name</replaceable></arg>
- <arg><option><replaceable>server</replaceable></option></arg>
- </cmdsynopsis>
- </refsynopsisdiv>
-
- <refsect1>
- <title>DESCRIPTION</title>
- <para>
- The <command>b10-host</command> utility does DNS lookups.
- Its initial goal is to be a
- <citerefentry><refentrytitle>host</refentrytitle>
- <manvolnum>1</manvolnum></citerefentry>
- clone, but also add a few features useful for BIND 10 development
- testing.
- </para>
-
- <para>
- By default, it looks up the A, AAAA, and MX record sets for the
- <replaceable>name</replaceable>.
- Optionally, you may select a name server to query against by adding
- the <replaceable>server</replaceable> argument.
- </para>
- </refsect1>
-
- <refsect1>
- <title>OPTIONS</title>
-
- <para>The arguments are as follows:</para>
-
- <variablelist>
-
- <varlistentry>
- <term><option>-a</option></term>
- <listitem><para>
- Enable verbose mode and do a query for type ANY.
- (If the <option>-t</option> option is also set, then the
- ANY query is not done, but it still uses verbose mode.)
- </para></listitem>
- </varlistentry>
-
- <varlistentry>
- <term><option>-c <replaceable>class</replaceable></option></term>
- <listitem><para>
- Define the class for the query.
- The default is IN (Internet).
-<!-- TODO: bug if class is unknown causes seg fault and possible core dump -->
- </para></listitem>
- </varlistentry>
-
- <varlistentry>
- <term><option>-d</option></term>
- <listitem><para>
- Enable verbose output mode, including elapsed time in
- milliseconds.
- Verbose mode shows the header, question, answer, authority,
- and additional sections (if provided).
- (Same as <option>-v</option>.)
- </para></listitem>
- </varlistentry>
-
- <varlistentry>
- <term><option>-p <replaceable>port</replaceable></option></term>
- <listitem><para>
- Select an alternative port for the query.
- This may be a number or a service name.
- The default is 53 (domain).
- This is not a standard feature of
- <citerefentry><refentrytitle>host</refentrytitle>
- <manvolnum>1</manvolnum></citerefentry>.
- </para></listitem>
- </varlistentry>
-
- <varlistentry>
- <term><option>-r</option></term>
- <listitem><para>
- Disable recursive processing by not setting the
- Recursion Desired flag in the query.
- </para></listitem>
- </varlistentry>
-
- <varlistentry>
- <term><option>-t <replaceable>type</replaceable></option></term>
- <listitem><para>
- Select a specific resource record type for the query.
- By default, it looks up the A, AAAA, and MX record sets.
-<!-- TODO: bug if class is unknown causes seg fault and possible core dump -->
- (This overrides the <option>-a</option> option.)
- </para></listitem>
- </varlistentry>
-
- <varlistentry>
- <term><option>-v</option></term>
- <listitem><para>
- Same as <option>-d</option> option.
- </para></listitem>
- </varlistentry>
-
- </variablelist>
-
- </refsect1>
-
- <refsect1>
- <title>COMPATIBILITY / BUGS</title>
- <para>
- <command>b10-host</command> does not do reverse lookups by
- default yet (by detecting if name is a IPv4 or IPv6 address).
- </para>
-
- <para>
- Unknown <option>-c</option> class or <option>-t</option> type
- causes <command>b10-host</command> to Abort.
- </para>
-
- <para>
- Not all types are supported yet for formatting.
- Not all switches are supported yet.
- </para>
-
- <para>
- It doesn't use <filename>/etc/resolv.conf</filename> at this time.
- The default name server used is 127.0.0.1.
- </para>
-
- <para>
- <option>-p</option> is not a standard feature.
- </para>
- </refsect1>
-
- <refsect1>
- <title>HISTORY</title>
- <para>
- The C++ version of <command>b10-host</command> was started in
- October 2009 by Jeremy C. Reed of ISC.
- Its usage and output were based on the standard <command>host</command>
- command.
- </para>
- </refsect1>
-</refentry><!--
- - Local variables:
- - mode: sgml
- - End:
--->
diff --git a/src/bin/host/host.cc b/src/bin/host/host.cc
deleted file mode 100644
index a5c6522..0000000
--- a/src/bin/host/host.cc
+++ /dev/null
@@ -1,253 +0,0 @@
-// Copyright (C) 2010-2011 Internet Systems Consortium, Inc. ("ISC")
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
-
-// host rewritten in C++ using BIND 10 DNS library
-
-#include <arpa/inet.h>
-#include <netdb.h> // for getaddrinfo
-#include <sys/time.h> // for gettimeofday
-#include <sys/socket.h> // networking functions and definitions on FreeBSD
-
-#include <unistd.h>
-
-#include <string>
-#include <iostream>
-
-#include <util/buffer.h>
-
-#include <dns/name.h>
-#include <dns/message.h>
-#include <dns/messagerenderer.h>
-#include <dns/opcode.h>
-#include <dns/rcode.h>
-#include <dns/rrclass.h>
-#include <dns/rrtype.h>
-#include <dns/rrset.h>
-#include <dns/message.h>
-
-using namespace std;
-using namespace isc::dns;
-using namespace isc::util;
-
-namespace {
-char* dns_type = NULL; // not set, so A, AAAA, MX
-const char* server = "127.0.0.1";
-const char* server_port = "53";
-const char* dns_class = "IN";
-bool verbose = false;
-bool dns_any = false;
-int first_time = 1;
-bool recursive_bit = true;
-struct timeval before_time, after_time;
-
-int
-host_lookup(const char* const name, const char* const dns_class,
- const char* const type, bool any) {
-
- Message msg(Message::RENDER);
-
- msg.setQid(0); // does this matter?
-
- // TODO: add switch for this
- msg.setOpcode(Opcode::QUERY());
- msg.setRcode(Rcode::NOERROR());
- if (recursive_bit) {
- msg.setHeaderFlag(Message::HEADERFLAG_RD); // set recursive bit
- }
-
- msg.addQuestion(Question(Name(name),
- RRClass(dns_class),
- any ? RRType::ANY() : RRType(type))); // if NULL then:
-
- MessageRenderer renderer;
- msg.toWire(renderer);
-
- struct addrinfo hints, *res;
- memset(&hints, 0, sizeof(hints));
- hints.ai_family = AF_UNSPEC;
- hints.ai_socktype = SOCK_DGRAM;
- hints.ai_flags = 0; // not using AI_NUMERICHOST in case to bootstrap
- if (getaddrinfo(server, server_port, &hints, &res) != 0) {
- cerr << "address/port conversion for " << server << ":"
- << server_port << " failed" << endl;
- return (1);
- }
-
- if (verbose) {
- cout << "Trying \"" << name << "\"\n";
- }
-
- if (verbose && first_time) {
- // this is only output the first time
- first_time = 0;
- cout << "Using domain server:\n";
- cout << "Name: " << server << "\n";
- // TODO: I guess I have to do a lookup to get that address and aliases
- // too
- //cout << "Address: " << address << "\n" ; // "#" << port << "\n";
- //cout << "Aliases: " << server << "\n";
- }
-
- int s = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
-
- if (s < 0) {
- cerr << "failed to open socket" << endl;
- return (1);
- }
-
- if (verbose) {
- gettimeofday(&before_time, NULL);
- }
-
- sendto(s, renderer.getData(), renderer.getLength(), 0, res->ai_addr,
- res->ai_addrlen);
-
- struct sockaddr_storage ss;
- struct sockaddr* sa;
- socklen_t sa_len;
-
- sa_len = sizeof(ss);
- sa = static_cast<struct sockaddr*>((void*)&ss);
-
- char recvbuf[4096];
- int cc;
- if ((cc = recvfrom(s, recvbuf, sizeof(recvbuf), 0, sa, &sa_len)) > 0) {
- try {
- Message rmsg(Message::PARSE);
- InputBuffer ibuffer(recvbuf, cc);
-
- rmsg.fromWire(ibuffer);
- if (!verbose) {
- string description = "";
- for (RRsetIterator it =
- rmsg.beginSection(Message::SECTION_ANSWER);
- it != rmsg.endSection(Message::SECTION_ANSWER);
- ++it) {
-
- if ((*it)->getType() == RRType::A()) {
- description = "has address";
- }
- else if ((*it)->getType() == RRType::AAAA()) {
- description = "has IPv6 address";
- }
- else if ((*it)->getType() == RRType::MX()) {
- description = "mail is handled by";
- }
- else if ((*it)->getType() == RRType::TXT()) {
- description = "descriptive text";
- }
-
- RdataIteratorPtr rit = (*it)->getRdataIterator();
- for (; !rit->isLast(); rit->next()) {
- // instead of using my name, maybe use returned label?
- cout << name << " " << description << " " <<
- (*rit).getCurrent().toText() << endl;
- }
- }
- } else {
- gettimeofday(&after_time, NULL);
-
- // HEADER and QUESTION, ANSWER, AUTHORITY, and ADDITIONAL
- std::cout << rmsg.toText() << std::endl;
-
- if (before_time.tv_usec > after_time.tv_usec) {
- after_time.tv_usec += 1000000;
- --after_time.tv_sec;
- }
-
- int elapsed_time =
- (after_time.tv_sec - before_time.tv_sec)
- + ((after_time.tv_usec - before_time.tv_usec))/1000;
-
- // TODO: if NXDOMAIN, host(1) doesn't show HEADER
- // Host hsdjkfhksjhdfkj not found: 3(NXDOMAIN)
- // TODO: test if NXDOMAIN
-
- std::cout << "Received " << cc <<
- " bytes in " << elapsed_time << " ms\n";
- // TODO: " bytes from 127.0.0.1#53 in 0 ms
-
- } //verbose
-/*
-TODO: handle InvalidRRClass
-TODO: handle invalid type exception
- } catch (InvalidType ivt) {
- std::cerr << "invalid type:" << ivt.what();
-*/
- } catch (const exception& ex) {
- std::cerr << "parse failed for " <<
- string(name) << "/" << type << ": " << ex.what() << std::endl;
- } catch (...) {
- std::cerr << "parse failed for " << string(name) << "/" << type;
- }
- }
-
- freeaddrinfo(res);
-
- return (0);
-} // host_lookup()
-}
-
-int
-main(int argc, char* argv[]) {
- int c;
-
- while ((c = getopt(argc, argv, "ac:dp:rt:v")) != -1)
- switch (c) {
- case 'a':
- dns_any = true;
- verbose = true;
- break;
- case 'c':
- dns_class = optarg;
- break;
- // p for port is a non-standard switch
- case 'p':
- server_port = optarg;
- break;
- case 'r':
- recursive_bit = false;
- break;
- case 't':
- dns_type = optarg;
- break;
- case 'd':
- // drop through to v, because debug and verbose are equivalent
- case 'v':
- verbose = true;
- break;
- }
- argc -= optind;
- argv += optind;
-
- if (argc < 1) {
- cout << "Usage: host [-adrv] [-c class] [-p port] [-t type] hostname [server]\n";
- exit(1);
- }
-
- if (argc >= 2) {
- server = argv[1];
- }
-
- if (dns_type == NULL) {
- host_lookup(argv[0], dns_class, "A", dns_any);
- // TODO: don't do next if A doesn't exist
- host_lookup(argv[0], dns_class, "AAAA", dns_any);
- host_lookup(argv[0], dns_class, "MX", dns_any);
- } else {
- // -t overrides -a, regardless of order
- host_lookup(argv[0], dns_class, dns_type, false);
- }
- return (0);
-}
diff --git a/src/cppcheck-suppress.lst b/src/cppcheck-suppress.lst
index ff4a79a..9dc8d99 100644
--- a/src/cppcheck-suppress.lst
+++ b/src/cppcheck-suppress.lst
@@ -11,3 +11,18 @@ missingInclude
//
// // cppcheck-suppress duplicateExpression
// EXPECT_FALSE(small_name < small_name);
+
+// With cppcheck 1.56, there are a number of false positives, which
+// All of these should be checked and hopefully removed after upgrading
+// cppcheck past 1.56
+
+// eraseDereference: This is a known false positive, which has been
+// fixed in the current development version of cppcheck
+eraseDereference
+
+// Unused functions: there suddenly are a lot of unused function errors
+// We could address those by adding for instance early declarations or
+// (unnecessary) header files, but they were all somewhat false positives
+// When we upgrade past 1.56, we should re-check this, and perhaps enable
+// unused-functions again.
+unusedFunction
diff --git a/src/lib/acl/dns.cc b/src/lib/acl/dns.cc
index d16ec65..5466dad 100644
--- a/src/lib/acl/dns.cc
+++ b/src/lib/acl/dns.cc
@@ -12,12 +12,6 @@
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
-#include <memory>
-#include <string>
-#include <vector>
-
-#include <boost/shared_ptr.hpp>
-
#include <exceptions/exceptions.h>
#include <dns/name.h>
@@ -31,6 +25,13 @@
#include <acl/loader.h>
#include <acl/logic_check.h>
+#include <boost/shared_ptr.hpp>
+#include <boost/scoped_ptr.hpp>
+
+#include <memory>
+#include <string>
+#include <vector>
+
using namespace std;
using namespace isc::dns;
using namespace isc::data;
@@ -106,10 +107,12 @@ internal::RequestCheckCreator::create(const string& name,
RequestLoader&
getRequestLoader() {
- static RequestLoader* loader(NULL);
- if (loader == NULL) {
+ // To ensure that the singleton gets destroyed at the end of the
+ // program's lifetime, we put it in a static scoped_ptr.
+ static boost::scoped_ptr<RequestLoader> loader(NULL);
+ if (loader.get() == NULL) {
// Creator registration may throw, so we first store the new loader
- // in an auto pointer in order to provide the strong exception
+ // in a second auto pointer in order to provide the strong exception
// guarantee.
auto_ptr<RequestLoader> loader_ptr =
auto_ptr<RequestLoader>(new RequestLoader(REJECT));
@@ -129,7 +132,7 @@ getRequestLoader() {
new LogicCreator<AllOfSpec, RequestContext>("ALL")));
// From this point there shouldn't be any exception thrown
- loader = loader_ptr.release();
+ loader.reset(loader_ptr.release());
}
return (*loader);
diff --git a/src/lib/cache/tests/rrset_entry_unittest.cc b/src/lib/cache/tests/rrset_entry_unittest.cc
index c7c3c6e..a6ac27e 100644
--- a/src/lib/cache/tests/rrset_entry_unittest.cc
+++ b/src/lib/cache/tests/rrset_entry_unittest.cc
@@ -50,9 +50,6 @@ class DerivedRRsetEntry: public RRsetEntry {
public:
DerivedRRsetEntry(const isc::dns::RRset& rrset, const RRsetTrustLevel& level) : RRsetEntry(rrset, level) {};
- void updateTTLForTest() {
-
- }
};
#define TEST_TTL 100
diff --git a/src/lib/cc/data.cc b/src/lib/cc/data.cc
index 6ec243a..8d9b87d 100644
--- a/src/lib/cc/data.cc
+++ b/src/lib/cc/data.cc
@@ -57,32 +57,32 @@ Element::toWire(std::ostream& ss) const {
}
bool
-Element::getValue(long int&) {
+Element::getValue(long int&) const {
return (false);
}
bool
-Element::getValue(double&) {
+Element::getValue(double&) const {
return (false);
}
bool
-Element::getValue(bool&) {
+Element::getValue(bool&) const {
return (false);
}
bool
-Element::getValue(std::string&) {
+Element::getValue(std::string&) const {
return (false);
}
bool
-Element::getValue(std::vector<ConstElementPtr>&) {
+Element::getValue(std::vector<ConstElementPtr>&) const {
return (false);
}
bool
-Element::getValue(std::map<std::string, ConstElementPtr>&) {
+Element::getValue(std::map<std::string, ConstElementPtr>&) const {
return (false);
}
@@ -167,7 +167,7 @@ Element::find(const std::string&) const {
}
bool
-Element::find(const std::string&, ConstElementPtr) const {
+Element::find(const std::string&, ConstElementPtr&) const {
return (false);
}
@@ -413,7 +413,7 @@ from_stringstream_number(std::istream &in, int &pos) {
isc_throw(JSONError, std::string("Number overflow: ") + number);
}
}
-
+
if (is_double) {
return (Element::create(d));
} else {
@@ -501,7 +501,7 @@ from_stringstream_map(std::istream &in, const std::string& file, int& line,
ConstElementPtr value = Element::fromJSON(in, file, line, pos);
map->set(key, value);
-
+
skip_to(in, file, line, pos, ",}", WHITESPACE);
c = in.get();
pos++;
@@ -812,7 +812,7 @@ MapElement::set(const std::string& key, ConstElementPtr value) {
}
bool
-MapElement::find(const std::string& id, ConstElementPtr t) const {
+MapElement::find(const std::string& id, ConstElementPtr& t) const {
try {
ConstElementPtr p = find(id);
if (p) {
@@ -942,7 +942,7 @@ removeIdentical(ConstElementPtr a, ConstElementPtr b) {
if (!b) {
return (result);
}
-
+
if (a->getType() != Element::map || b->getType() != Element::map) {
isc_throw(TypeError, "Non-map Elements passed to removeIdentical");
}
@@ -965,7 +965,7 @@ merge(ElementPtr element, ConstElementPtr other) {
other->getType() != Element::map) {
isc_throw(TypeError, "merge arguments not MapElements");
}
-
+
const std::map<std::string, ConstElementPtr>& m = other->mapValue();
for (std::map<std::string, ConstElementPtr>::const_iterator it = m.begin();
it != m.end() ; ++it) {
diff --git a/src/lib/cc/data.h b/src/lib/cc/data.h
index 5c731e6..c1147a2 100644
--- a/src/lib/cc/data.h
+++ b/src/lib/cc/data.h
@@ -71,7 +71,7 @@ public:
/// the type in question.
///
class Element {
-
+
private:
// technically the type could be omitted; is it useful?
// should we remove it or replace it with a pure virtual
@@ -112,7 +112,7 @@ public:
/// \returns true if the other ElementPtr has the same type and
/// value
virtual bool equals(const Element& other) const = 0;
-
+
/// Converts the Element to JSON format and appends it to
/// the given stringstream.
virtual void toJSON(std::ostream& ss) const = 0;
@@ -152,12 +152,12 @@ public:
/// data to the given reference and returning true
///
//@{
- virtual bool getValue(long int& t);
- virtual bool getValue(double& t);
- virtual bool getValue(bool& t);
- virtual bool getValue(std::string& t);
- virtual bool getValue(std::vector<ConstElementPtr>& t);
- virtual bool getValue(std::map<std::string, ConstElementPtr>& t);
+ virtual bool getValue(long int& t) const;
+ virtual bool getValue(double& t) const;
+ virtual bool getValue(bool& t) const;
+ virtual bool getValue(std::string& t) const;
+ virtual bool getValue(std::vector<ConstElementPtr>& t) const;
+ virtual bool getValue(std::map<std::string, ConstElementPtr>& t) const;
//@}
///
@@ -209,7 +209,7 @@ public:
virtual size_t size() const;
//@}
-
+
/// \name MapElement functions
///
/// \brief If the Element on which these functions are called are not
@@ -253,12 +253,12 @@ public:
/// \param identifier The identifier of the element to find
/// \param t Reference to store the resulting ElementPtr, if found.
/// \return true if the element was found, false if not.
- virtual bool find(const std::string& identifier, ConstElementPtr t) const;
+ virtual bool find(const std::string& identifier, ConstElementPtr& t) const;
//@}
/// \name Factory functions
-
+
// TODO: should we move all factory functions to a different class
// so as not to burden the Element base with too many functions?
// and/or perhaps even to a separate header?
@@ -349,7 +349,7 @@ public:
/// These function pparse the wireformat at the given stringstream
/// (of the given length). If there is a parse error an exception
/// of the type isc::cc::DecodeError is raised.
-
+
//@{
/// Creates an Element from the wire format in the given
/// stringstream of the given length.
@@ -378,7 +378,7 @@ public:
IntElement(long int v) : Element(integer), i(v) { }
long int intValue() const { return (i); }
using Element::getValue;
- bool getValue(long int& t) { t = i; return (true); }
+ bool getValue(long int& t) const { t = i; return (true); }
using Element::setValue;
bool setValue(const long int v) { i = v; return (true); }
void toJSON(std::ostream& ss) const;
@@ -392,7 +392,7 @@ public:
DoubleElement(double v) : Element(real), d(v) {};
double doubleValue() const { return (d); }
using Element::getValue;
- bool getValue(double& t) { t = d; return (true); }
+ bool getValue(double& t) const { t = d; return (true); }
using Element::setValue;
bool setValue(const double v) { d = v; return (true); }
void toJSON(std::ostream& ss) const;
@@ -406,7 +406,7 @@ public:
BoolElement(const bool v) : Element(boolean), b(v) {};
bool boolValue() const { return (b); }
using Element::getValue;
- bool getValue(bool& t) { t = b; return (true); }
+ bool getValue(bool& t) const { t = b; return (true); }
using Element::setValue;
bool setValue(const bool v) { b = v; return (true); }
void toJSON(std::ostream& ss) const;
@@ -427,7 +427,7 @@ public:
StringElement(std::string v) : Element(string), s(v) {};
std::string stringValue() const { return (s); }
using Element::getValue;
- bool getValue(std::string& t) { t = s; return (true); }
+ bool getValue(std::string& t) const { t = s; return (true); }
using Element::setValue;
bool setValue(const std::string& v) { s = v; return (true); }
void toJSON(std::ostream& ss) const;
@@ -441,7 +441,7 @@ public:
ListElement() : Element(list) {}
const std::vector<ConstElementPtr>& listValue() const { return (l); }
using Element::getValue;
- bool getValue(std::vector<ConstElementPtr>& t) {
+ bool getValue(std::vector<ConstElementPtr>& t) const {
t = l;
return (true);
}
@@ -474,7 +474,7 @@ public:
return (m);
}
using Element::getValue;
- bool getValue(std::map<std::string, ConstElementPtr>& t) {
+ bool getValue(std::map<std::string, ConstElementPtr>& t) const {
t = m;
return (true);
}
@@ -495,7 +495,7 @@ public:
return (m.find(s) != m.end());
}
void toJSON(std::ostream& ss) const;
-
+
// we should name the two finds better...
// find the element at id; raises TypeError if one of the
// elements at path except the one we're looking for is not a
@@ -507,7 +507,7 @@ public:
// returns true if found, or false if not found (either because
// it doesnt exist or one of the elements in the path is not
// a MapElement)
- bool find(const std::string& id, ConstElementPtr t) const;
+ bool find(const std::string& id, ConstElementPtr& t) const;
bool equals(const Element& other) const;
};
@@ -569,6 +569,6 @@ bool operator!=(const Element& a, const Element& b);
} }
#endif // _ISC_DATA_H
-// Local Variables:
+// Local Variables:
// mode: c++
-// End:
+// End:
diff --git a/src/lib/cc/tests/data_unittests.cc b/src/lib/cc/tests/data_unittests.cc
index 87d92f6..1565418 100644
--- a/src/lib/cc/tests/data_unittests.cc
+++ b/src/lib/cc/tests/data_unittests.cc
@@ -112,7 +112,7 @@ TEST(Element, from_and_to_json) {
std::string s = std::string(pe.what());
EXPECT_EQ("String expected in <string>:1:3", s);
}
-
+
sv.clear();
sv.push_back("{1}");
//ElementPtr ep = Element::fromJSON("\"aaa\nbbb\"err");
@@ -172,14 +172,14 @@ TEST(Element, from_and_to_json) {
}
-TEST(Element, create_and_value_throws) {
- // this test checks whether elements throw exceptions if the
- // incorrect type is requested
- ElementPtr el;
+template <typename T>
+void
+testGetValueInt() {
+ T el;
long int i;
double d;
bool b;
- std::string s("asdf");
+ std::string s;
std::vector<ConstElementPtr> v;
std::map<std::string, ConstElementPtr> m;
@@ -196,27 +196,19 @@ TEST(Element, create_and_value_throws) {
EXPECT_FALSE(el->getValue(s));
EXPECT_FALSE(el->getValue(v));
EXPECT_FALSE(el->getValue(m));
- EXPECT_EQ(i, 1);
- i = 2;
- EXPECT_TRUE(el->setValue(i));
- EXPECT_EQ(2, el->intValue());
- EXPECT_FALSE(el->setValue(d));
- EXPECT_FALSE(el->setValue(b));
- EXPECT_FALSE(el->setValue(s));
- EXPECT_FALSE(el->setValue(v));
- EXPECT_FALSE(el->setValue(m));
- EXPECT_THROW(el->get(1), TypeError);
- EXPECT_THROW(el->set(1, el), TypeError);
- EXPECT_THROW(el->add(el), TypeError);
- EXPECT_THROW(el->remove(1), TypeError);
- EXPECT_THROW(el->size(), TypeError);
- EXPECT_THROW(el->get("foo"), TypeError);
- EXPECT_THROW(el->set("foo", el), TypeError);
- EXPECT_THROW(el->remove("foo"), TypeError);
- EXPECT_THROW(el->contains("foo"), TypeError);
- ConstElementPtr tmp;
- EXPECT_FALSE(el->find("foo", tmp));
-
+ EXPECT_EQ(1, i);
+}
+
+template <typename T>
+void
+testGetValueDouble() {
+ T el;
+ long int i;
+ double d;
+ bool b;
+ std::string s;
+ std::vector<ConstElementPtr> v;
+ std::map<std::string, ConstElementPtr> m;
el = Element::create(1.1);
EXPECT_THROW(el->intValue(), TypeError);
@@ -231,15 +223,19 @@ TEST(Element, create_and_value_throws) {
EXPECT_FALSE(el->getValue(s));
EXPECT_FALSE(el->getValue(v));
EXPECT_FALSE(el->getValue(m));
- EXPECT_EQ(d, 1.1);
- d = 2.2;
- EXPECT_TRUE(el->setValue(d));
- EXPECT_EQ(2.2, el->doubleValue());
- EXPECT_FALSE(el->setValue(i));
- EXPECT_FALSE(el->setValue(b));
- EXPECT_FALSE(el->setValue(s));
- EXPECT_FALSE(el->setValue(v));
- EXPECT_FALSE(el->setValue(m));
+ EXPECT_EQ(1.1, d);
+}
+
+template <typename T>
+void
+testGetValueBool() {
+ T el;
+ long int i;
+ double d;
+ bool b;
+ std::string s;
+ std::vector<ConstElementPtr> v;
+ std::map<std::string, ConstElementPtr> m;
el = Element::create(true);
EXPECT_THROW(el->intValue(), TypeError);
@@ -254,10 +250,19 @@ TEST(Element, create_and_value_throws) {
EXPECT_FALSE(el->getValue(s));
EXPECT_FALSE(el->getValue(v));
EXPECT_FALSE(el->getValue(m));
- EXPECT_EQ(b, true);
- b = false;
- EXPECT_TRUE(el->setValue(b));
- EXPECT_FALSE(el->boolValue());
+ EXPECT_EQ(true, b);
+}
+
+template <typename T>
+void
+testGetValueString() {
+ T el;
+ long int i;
+ double d;
+ bool b;
+ std::string s;
+ std::vector<ConstElementPtr> v;
+ std::map<std::string, ConstElementPtr> m;
el = Element::create("foo");
EXPECT_THROW(el->intValue(), TypeError);
@@ -272,10 +277,19 @@ TEST(Element, create_and_value_throws) {
EXPECT_TRUE(el->getValue(s));
EXPECT_FALSE(el->getValue(v));
EXPECT_FALSE(el->getValue(m));
- EXPECT_EQ(s, "foo");
- s = "bar";
- EXPECT_TRUE(el->setValue(s));
- EXPECT_EQ("bar", el->stringValue());
+ EXPECT_EQ("foo", s);
+}
+
+template <typename T>
+void
+testGetValueList() {
+ T el;
+ long int i;
+ double d;
+ bool b;
+ std::string s;
+ std::vector<ConstElementPtr> v;
+ std::map<std::string, ConstElementPtr> m;
el = Element::createList();
EXPECT_THROW(el->intValue(), TypeError);
@@ -291,9 +305,18 @@ TEST(Element, create_and_value_throws) {
EXPECT_TRUE(el->getValue(v));
EXPECT_FALSE(el->getValue(m));
EXPECT_EQ("[ ]", el->str());
- v.push_back(Element::create(1));
- EXPECT_TRUE(el->setValue(v));
- EXPECT_EQ("[ 1 ]", el->str());
+}
+
+template <typename T>
+void
+testGetValueMap() {
+ T el;
+ long int i;
+ double d;
+ bool b;
+ std::string s;
+ std::vector<ConstElementPtr> v;
+ std::map<std::string, ConstElementPtr> m;
el = Element::createMap();
EXPECT_THROW(el->intValue(), TypeError);
@@ -308,7 +331,128 @@ TEST(Element, create_and_value_throws) {
EXPECT_FALSE(el->getValue(s));
EXPECT_FALSE(el->getValue(v));
EXPECT_TRUE(el->getValue(m));
+ EXPECT_EQ("{ }", el->str());
+}
+
+TEST(Element, create_and_value_throws) {
+ // this test checks whether elements throw exceptions if the
+ // incorrect type is requested
+ ElementPtr el;
+ ConstElementPtr cel;
+ long int i = 0;
+ double d = 0.0;
+ bool b = false;
+ std::string s("asdf");
+ std::vector<ConstElementPtr> v;
+ std::map<std::string, ConstElementPtr> m;
+ ConstElementPtr tmp;
+
+ testGetValueInt<ElementPtr>();
+ testGetValueInt<ConstElementPtr>();
+
+ el = Element::create(1);
+ i = 2;
+ EXPECT_TRUE(el->setValue(i));
+ EXPECT_EQ(2, el->intValue());
+ EXPECT_FALSE(el->setValue(d));
+ EXPECT_FALSE(el->setValue(b));
+ EXPECT_FALSE(el->setValue(s));
+ EXPECT_FALSE(el->setValue(v));
+ EXPECT_FALSE(el->setValue(m));
+ EXPECT_THROW(el->get(1), TypeError);
+ EXPECT_THROW(el->set(1, el), TypeError);
+ EXPECT_THROW(el->add(el), TypeError);
+ EXPECT_THROW(el->remove(1), TypeError);
+ EXPECT_THROW(el->size(), TypeError);
+ EXPECT_THROW(el->get("foo"), TypeError);
+ EXPECT_THROW(el->set("foo", el), TypeError);
+ EXPECT_THROW(el->remove("foo"), TypeError);
+ EXPECT_THROW(el->contains("foo"), TypeError);
+ EXPECT_FALSE(el->find("foo", tmp));
+
+ testGetValueDouble<ElementPtr>();
+ testGetValueDouble<ConstElementPtr>();
+
+ el = Element::create(1.1);
+ d = 2.2;
+ EXPECT_TRUE(el->setValue(d));
+ EXPECT_EQ(2.2, el->doubleValue());
+ EXPECT_FALSE(el->setValue(i));
+ EXPECT_FALSE(el->setValue(b));
+ EXPECT_FALSE(el->setValue(s));
+ EXPECT_FALSE(el->setValue(v));
+ EXPECT_FALSE(el->setValue(m));
+ EXPECT_THROW(el->get(1), TypeError);
+ EXPECT_THROW(el->set(1, el), TypeError);
+ EXPECT_THROW(el->add(el), TypeError);
+ EXPECT_THROW(el->remove(1), TypeError);
+ EXPECT_THROW(el->size(), TypeError);
+ EXPECT_THROW(el->get("foo"), TypeError);
+ EXPECT_THROW(el->set("foo", el), TypeError);
+ EXPECT_THROW(el->remove("foo"), TypeError);
+ EXPECT_THROW(el->contains("foo"), TypeError);
+ EXPECT_FALSE(el->find("foo", tmp));
+
+ testGetValueBool<ElementPtr>();
+ testGetValueBool<ConstElementPtr>();
+ el = Element::create(true);
+ b = false;
+ EXPECT_TRUE(el->setValue(b));
+ EXPECT_FALSE(el->boolValue());
+ EXPECT_FALSE(el->setValue(i));
+ EXPECT_FALSE(el->setValue(d));
+ EXPECT_FALSE(el->setValue(s));
+ EXPECT_FALSE(el->setValue(v));
+ EXPECT_FALSE(el->setValue(m));
+ EXPECT_THROW(el->get(1), TypeError);
+ EXPECT_THROW(el->set(1, el), TypeError);
+ EXPECT_THROW(el->add(el), TypeError);
+ EXPECT_THROW(el->remove(1), TypeError);
+ EXPECT_THROW(el->size(), TypeError);
+ EXPECT_THROW(el->get("foo"), TypeError);
+ EXPECT_THROW(el->set("foo", el), TypeError);
+ EXPECT_THROW(el->remove("foo"), TypeError);
+ EXPECT_THROW(el->contains("foo"), TypeError);
+ EXPECT_FALSE(el->find("foo", tmp));
+
+ testGetValueString<ElementPtr>();
+ testGetValueString<ConstElementPtr>();
+
+ el = Element::create("foo");
+ s = "bar";
+ EXPECT_TRUE(el->setValue(s));
+ EXPECT_EQ("bar", el->stringValue());
+ EXPECT_FALSE(el->setValue(i));
+ EXPECT_FALSE(el->setValue(b));
+ EXPECT_FALSE(el->setValue(d));
+ EXPECT_FALSE(el->setValue(v));
+ EXPECT_FALSE(el->setValue(m));
+ EXPECT_THROW(el->get(1), TypeError);
+ EXPECT_THROW(el->set(1, el), TypeError);
+ EXPECT_THROW(el->add(el), TypeError);
+ EXPECT_THROW(el->remove(1), TypeError);
+ EXPECT_THROW(el->size(), TypeError);
+ EXPECT_THROW(el->get("foo"), TypeError);
+ EXPECT_THROW(el->set("foo", el), TypeError);
+ EXPECT_THROW(el->remove("foo"), TypeError);
+ EXPECT_THROW(el->contains("foo"), TypeError);
+ EXPECT_FALSE(el->find("foo", tmp));
+
+ testGetValueList<ElementPtr>();
+ testGetValueList<ConstElementPtr>();
+
+ el = Element::createList();
+ v.push_back(Element::create(1));
+ EXPECT_TRUE(el->setValue(v));
+ EXPECT_EQ("[ 1 ]", el->str());
+
+ testGetValueMap<ElementPtr>();
+ testGetValueMap<ConstElementPtr>();
+
+ el = Element::createMap();
+ EXPECT_NO_THROW(el->set("foo", Element::create("bar")));
+ EXPECT_EQ("{ \"foo\": \"bar\" }", el->str());
}
// Helper for escape check; it puts the given string in a StringElement,
@@ -382,7 +526,7 @@ TEST(Element, MapElement) {
// this function checks the specific functions for ListElements
ElementPtr el = Element::fromJSON("{ \"name\": \"foo\", \"value1\": \"bar\", \"value2\": { \"number\": 42 } }");
ConstElementPtr el2;
-
+
EXPECT_EQ(el->get("name")->stringValue(), "foo");
EXPECT_EQ(el->get("value2")->getType(), Element::map);
@@ -396,11 +540,12 @@ TEST(Element, MapElement) {
EXPECT_EQ(el->find("value2/number")->intValue(), 42);
EXPECT_TRUE(isNull(el->find("value2/nothing/")));
-
+
EXPECT_EQ(el->find("value1")->stringValue(), "bar");
EXPECT_EQ(el->find("value1/")->stringValue(), "bar");
-
+
EXPECT_TRUE(el->find("value1", el2));
+ EXPECT_EQ("bar", el2->stringValue());
EXPECT_FALSE(el->find("name/error", el2));
// A map element whose (only) element has the maximum length of tag.
@@ -410,7 +555,7 @@ TEST(Element, MapElement) {
"9123456789abcdefa123456789abcdefb123456789abcdef"
"c123456789abcdefd123456789abcdefe123456789abcdef"
"f123456789abcde");
-
+
EXPECT_EQ(255, long_maptag.length()); // check prerequisite
el = Element::fromJSON("{ \"" + long_maptag + "\": \"bar\"}");
EXPECT_EQ("bar", el->find(long_maptag)->stringValue());
@@ -689,7 +834,7 @@ TEST(Element, merge) {
c = Element::fromJSON("{ \"a\": { \"b\": \"c\" } }");
merge(b, a);
EXPECT_EQ(*b, *c);
-
+
// And some tests with multiple values
a = Element::fromJSON("{ \"a\": 1, \"b\": true, \"c\": null }");
b = Element::fromJSON("{ \"a\": 1, \"b\": null, \"c\": \"a string\" }");
diff --git a/src/lib/datasrc/database.cc b/src/lib/datasrc/database.cc
index ede7aa3..fbada44 100644
--- a/src/lib/datasrc/database.cc
+++ b/src/lib/datasrc/database.cc
@@ -386,10 +386,11 @@ DatabaseClient::Finder::findAll(const isc::dns::Name& name,
std::vector<isc::dns::ConstRRsetPtr>& target,
const FindOptions options)
{
- return (ZoneFinderContextPtr(new Context(*this, options,
- findInternal(name, RRType::ANY(),
- &target, options),
- target)));
+ return (ZoneFinderContextPtr(new GenericContext(
+ *this, options,
+ findInternal(name, RRType::ANY(),
+ &target, options),
+ target)));
}
ZoneFinderContextPtr
@@ -400,9 +401,10 @@ DatabaseClient::Finder::find(const isc::dns::Name& name,
if (type == RRType::ANY()) {
isc_throw(isc::Unexpected, "Use findAll to answer ANY");
}
- return (ZoneFinderContextPtr(new Context(*this, options,
- findInternal(name, type, NULL,
- options))));
+ return (ZoneFinderContextPtr(new GenericContext(
+ *this, options,
+ findInternal(name, type, NULL,
+ options))));
}
DatabaseClient::Finder::DelegationSearchResult
diff --git a/src/lib/datasrc/memory/Makefile.am b/src/lib/datasrc/memory/Makefile.am
index 3bdec0d..6fce958 100644
--- a/src/lib/datasrc/memory/Makefile.am
+++ b/src/lib/datasrc/memory/Makefile.am
@@ -16,10 +16,15 @@ libdatasrc_memory_la_SOURCES += treenode_rrset.h treenode_rrset.cc
libdatasrc_memory_la_SOURCES += rdata_serialization.h rdata_serialization.cc
libdatasrc_memory_la_SOURCES += zone_data.h zone_data.cc
libdatasrc_memory_la_SOURCES += segment_object_holder.h
-libdatasrc_memory_la_SOURCES += memory_client.h memory_client.cc
libdatasrc_memory_la_SOURCES += logger.h logger.cc
libdatasrc_memory_la_SOURCES += zone_table.h zone_table.cc
libdatasrc_memory_la_SOURCES += zone_finder.h zone_finder.cc
+libdatasrc_memory_la_SOURCES += zone_table_segment.h zone_table_segment.cc
+libdatasrc_memory_la_SOURCES += zone_table_segment_local.h zone_table_segment_local.cc
+libdatasrc_memory_la_SOURCES += zone_data_updater.h zone_data_updater.cc
+libdatasrc_memory_la_SOURCES += zone_data_loader.h zone_data_loader.cc
+libdatasrc_memory_la_SOURCES += memory_client.h memory_client.cc
+
nodist_libdatasrc_memory_la_SOURCES = memory_messages.h memory_messages.cc
EXTRA_DIST = rdata_serialization_priv.cc
diff --git a/src/lib/datasrc/memory/domaintree.h b/src/lib/datasrc/memory/domaintree.h
index 20f4693..272245d 100644
--- a/src/lib/datasrc/memory/domaintree.h
+++ b/src/lib/datasrc/memory/domaintree.h
@@ -947,7 +947,7 @@ public:
PARTIALMATCH, ///< A superdomain node was found
NOTFOUND, ///< Not even any superdomain was found
/// \brief Returned by insert() if a node of the name already exists
- ALREADYEXISTS,
+ ALREADYEXISTS
};
/// \brief Allocate and construct \c DomainTree
@@ -1080,55 +1080,25 @@ public:
/// of it. In that case, node parameter is left intact.
//@{
- /// \brief Simple find.
+ /// \brief Simple find
///
/// Acts as described in the \ref find section.
Result find(const isc::dns::Name& name,
- DomainTreeNode<T>** node) const {
- DomainTreeNodeChain<T> node_path;
- const isc::dns::LabelSequence ls(name);
- return (find<void*>(ls, node, node_path, NULL, NULL));
- }
-
- /// \brief Simple find returning immutable node.
- ///
- /// Acts as described in the \ref find section, but returns immutable node
- /// pointer.
- Result find(const isc::dns::Name& name,
const DomainTreeNode<T>** node) const {
DomainTreeNodeChain<T> node_path;
- DomainTreeNode<T> *target_node = NULL;
const isc::dns::LabelSequence ls(name);
- Result ret = (find<void*>(ls, &target_node, node_path, NULL, NULL));
- if (ret != NOTFOUND) {
- *node = target_node;
- }
+ Result ret = (find<void*>(ls, node, node_path, NULL, NULL));
return (ret);
}
/// \brief Simple find, with node_path tracking
///
/// Acts as described in the \ref find section.
- Result find(const isc::dns::Name& name, DomainTreeNode<T>** node,
- DomainTreeNodeChain<T>& node_path) const
- {
- const isc::dns::LabelSequence ls(name);
- return (find<void*>(ls, node, node_path, NULL, NULL));
- }
-
- /// \brief Simple find returning immutable node, with node_path tracking
- ///
- /// Acts as described in the \ref find section, but returns immutable node
- /// pointer.
Result find(const isc::dns::Name& name, const DomainTreeNode<T>** node,
DomainTreeNodeChain<T>& node_path) const
{
- DomainTreeNode<T> *target_node = NULL;
const isc::dns::LabelSequence ls(name);
- Result ret = (find<void*>(ls, &target_node, node_path, NULL, NULL));
- if (ret != NOTFOUND) {
- *node = target_node;
- }
+ Result ret = (find<void*>(ls, node, node_path, NULL, NULL));
return (ret);
}
@@ -1143,13 +1113,9 @@ public:
bool (*callback)(const DomainTreeNode<T>&, CBARG),
CBARG callback_arg) const
{
- DomainTreeNode<T>* target_node = NULL;
const isc::dns::LabelSequence ls(name);
- Result ret = find(ls, &target_node, node_path, callback,
+ Result ret = find(ls, node, node_path, callback,
callback_arg);
- if (ret != NOTFOUND) {
- *node = target_node;
- }
return (ret);
}
@@ -1229,30 +1195,10 @@ public:
/// \c true, it returns immediately with the current node.
template <typename CBARG>
Result find(const isc::dns::LabelSequence& target_labels_orig,
- DomainTreeNode<T>** node,
- DomainTreeNodeChain<T>& node_path,
- bool (*callback)(const DomainTreeNode<T>&, CBARG),
- CBARG callback_arg) const;
-
- /// \brief Simple find returning immutable node.
- ///
- /// Acts as described in the \ref find section, but returns immutable
- /// node pointer.
- template <typename CBARG>
- Result find(const isc::dns::LabelSequence& target_labels,
const DomainTreeNode<T>** node,
DomainTreeNodeChain<T>& node_path,
bool (*callback)(const DomainTreeNode<T>&, CBARG),
- CBARG callback_arg) const
- {
- DomainTreeNode<T>* target_node = NULL;
- Result ret = find(target_labels, &target_node, node_path,
- callback, callback_arg);
- if (ret != NOTFOUND) {
- *node = target_node;
- }
- return (ret);
- }
+ CBARG callback_arg) const;
//@}
/// \brief return the next bigger node in DNSSEC order from a given node
@@ -1515,7 +1461,7 @@ template <typename T>
template <typename CBARG>
typename DomainTree<T>::Result
DomainTree<T>::find(const isc::dns::LabelSequence& target_labels_orig,
- DomainTreeNode<T>** target,
+ const DomainTreeNode<T>** target,
DomainTreeNodeChain<T>& node_path,
bool (*callback)(const DomainTreeNode<T>&, CBARG),
CBARG callback_arg) const
@@ -1526,11 +1472,11 @@ DomainTree<T>::find(const isc::dns::LabelSequence& target_labels_orig,
" and label sequence");
}
- DomainTreeNode<T>* node;
+ const DomainTreeNode<T>* node;
if (!node_path.isEmpty()) {
// Get the top node in the node chain
- node = const_cast<DomainTreeNode<T>*>(node_path.top());
+ node = node_path.top();
// Start searching from its down pointer
node = node->getDown();
} else {
diff --git a/src/lib/datasrc/memory/memory_client.cc b/src/lib/datasrc/memory/memory_client.cc
index 5f6f510..2f5fe67 100644
--- a/src/lib/datasrc/memory/memory_client.cc
+++ b/src/lib/datasrc/memory/memory_client.cc
@@ -17,12 +17,11 @@
#include <datasrc/memory/memory_client.h>
#include <datasrc/memory/logger.h>
#include <datasrc/memory/zone_data.h>
-#include <datasrc/memory/rdata_serialization.h>
#include <datasrc/memory/rdataset.h>
-#include <datasrc/memory/domaintree.h>
#include <datasrc/memory/segment_object_holder.h>
#include <datasrc/memory/treenode_rrset.h>
#include <datasrc/memory/zone_finder.h>
+#include <datasrc/memory/zone_data_loader.h>
#include <util/memory_segment_local.h>
@@ -31,21 +30,10 @@
#include <datasrc/result.h>
#include <dns/name.h>
-#include <dns/nsec3hash.h>
#include <dns/rdataclass.h>
#include <dns/rrclass.h>
-#include <dns/rrsetlist.h>
-#include <dns/masterload.h>
-
-#include <boost/function.hpp>
-#include <boost/shared_ptr.hpp>
-#include <boost/scoped_ptr.hpp>
-#include <boost/bind.hpp>
-#include <boost/foreach.hpp>
-#include <boost/noncopyable.hpp>
#include <algorithm>
-#include <map>
#include <utility>
#include <cctype>
#include <cassert>
@@ -54,7 +42,6 @@ using namespace std;
using namespace isc::dns;
using namespace isc::dns::rdata;
using namespace isc::datasrc::memory;
-using boost::scoped_ptr;
namespace isc {
namespace datasrc {
@@ -62,552 +49,49 @@ namespace memory {
using detail::SegmentObjectHolder;
-namespace {
-// Some type aliases
-typedef DomainTree<std::string> FileNameTree;
-typedef DomainTreeNode<std::string> FileNameNode;
-
-// A functor type used for loading.
-typedef boost::function<void(ConstRRsetPtr)> LoadCallback;
-
-} // end of anonymous namespace
-
-/// Implementation details for \c InMemoryClient hidden from the public
-/// interface.
-///
-/// For now, \c InMemoryClient only contains a \c ZoneTable object, which
-/// consists of (pointers to) \c InMemoryZoneFinder objects, we may add more
-/// member variables later for new features.
-class InMemoryClient::InMemoryClientImpl {
-private:
- // The deleter for the filenames stored in the tree.
- struct FileNameDeleter {
- FileNameDeleter() {}
- void operator()(std::string* filename) const {
- delete filename;
- }
- };
+namespace { // unnamed namespace
+// A helper internal class used by the memory client, used for deleting
+// filenames stored in an internal tree.
+class FileNameDeleter {
public:
- InMemoryClientImpl(util::MemorySegment& mem_sgmt, RRClass rrclass) :
- mem_sgmt_(mem_sgmt),
- rrclass_(rrclass),
- zone_count_(0),
- zone_table_(ZoneTable::create(mem_sgmt_, rrclass)),
- file_name_tree_(FileNameTree::create(mem_sgmt_, false))
- {}
- ~InMemoryClientImpl() {
- FileNameDeleter deleter;
- FileNameTree::destroy(mem_sgmt_, file_name_tree_, deleter);
-
- ZoneTable::destroy(mem_sgmt_, zone_table_, rrclass_);
- }
-
- util::MemorySegment& mem_sgmt_;
- const RRClass rrclass_;
- unsigned int zone_count_;
- ZoneTable* zone_table_;
- FileNameTree* file_name_tree_;
-
- // Common process for zone load.
- // rrset_installer is a functor that takes another functor as an argument,
- // and expected to call the latter for each RRset of the zone. How the
- // sequence of the RRsets is generated depends on the internal
- // details of the loader: either from a textual master file or from
- // another data source.
- // filename is the file name of the master file or empty if the zone is
- // loaded from another data source.
- result::Result load(const Name& zone_name, const string& filename,
- boost::function<void(LoadCallback)> rrset_installer);
-
- // Add the necessary magic for any wildcard contained in 'name'
- // (including itself) to be found in the zone.
- //
- // In order for wildcard matching to work correctly in the zone finder,
- // we must ensure that a node for the wildcarding level exists in the
- // backend ZoneTree.
- // E.g. if the wildcard name is "*.sub.example." then we must ensure
- // that "sub.example." exists and is marked as a wildcard level.
- // Note: the "wildcarding level" is for the parent name of the wildcard
- // name (such as "sub.example.").
- //
- // We also perform the same trick for empty wild card names possibly
- // contained in 'name' (e.g., '*.foo.example' in 'bar.*.foo.example').
- void addWildcards(const Name& zone_name, ZoneData& zone_data,
- const Name& name)
- {
- Name wname(name);
- const unsigned int labels(wname.getLabelCount());
- const unsigned int origin_labels(zone_name.getLabelCount());
- for (unsigned int l = labels;
- l > origin_labels;
- --l, wname = wname.split(1)) {
- if (wname.isWildcard()) {
- LOG_DEBUG(logger, DBG_TRACE_DATA,
- DATASRC_MEMORY_MEM_ADD_WILDCARD).arg(name);
-
- // Ensure a separate level exists for the "wildcarding" name,
- // and mark the node as "wild".
- ZoneNode* node;
- zone_data.insertName(mem_sgmt_, wname.split(1), &node);
- node->setFlag(ZoneData::WILDCARD_NODE);
-
- // Ensure a separate level exists for the wildcard name.
- // Note: for 'name' itself we do this later anyway, but the
- // overhead should be marginal because wildcard names should
- // be rare.
- zone_data.insertName(mem_sgmt_, wname, &node);
- }
- }
- }
-
- /*
- * Does some checks in context of the data that are already in the zone.
- * Currently checks for forbidden combinations of RRsets in the same
- * domain (CNAME+anything, DNAME+NS).
- *
- * If such condition is found, it throws AddError.
- */
- void contextCheck(const Name& zone_name, const AbstractRRset& rrset,
- const RdataSet* set) const
- {
- // Ensure CNAME and other type of RR don't coexist for the same
- // owner name except with NSEC, which is the only RR that can coexist
- // with CNAME (and also RRSIG, which is handled separately)
- if (rrset.getType() == RRType::CNAME()) {
- for (const RdataSet* sp = set; sp != NULL; sp = sp->getNext()) {
- if (sp->type != RRType::NSEC()) {
- LOG_ERROR(logger, DATASRC_MEMORY_MEM_CNAME_TO_NONEMPTY).
- arg(rrset.getName());
- isc_throw(AddError, "CNAME can't be added with "
- << sp->type << " RRType for "
- << rrset.getName());
- }
- }
- } else if ((rrset.getType() != RRType::NSEC()) &&
- (RdataSet::find(set, RRType::CNAME()) != NULL)) {
- LOG_ERROR(logger,
- DATASRC_MEMORY_MEM_CNAME_COEXIST).arg(rrset.getName());
- isc_throw(AddError, "CNAME and " << rrset.getType() <<
- " can't coexist for " << rrset.getName());
- }
-
- /*
- * Similar with DNAME, but it must not coexist only with NS and only in
- * non-apex domains.
- * RFC 2672 section 3 mentions that it is implied from it and RFC 2181
- */
- if (rrset.getName() != zone_name &&
- // Adding DNAME, NS already there
- ((rrset.getType() == RRType::DNAME() &&
- RdataSet::find(set, RRType::NS()) != NULL) ||
- // Adding NS, DNAME already there
- (rrset.getType() == RRType::NS() &&
- RdataSet::find(set, RRType::DNAME()) != NULL)))
- {
- LOG_ERROR(logger, DATASRC_MEMORY_MEM_DNAME_NS).arg(rrset.getName());
- isc_throw(AddError, "DNAME can't coexist with NS in non-apex "
- "domain " << rrset.getName());
- }
- }
-
- // Validate rrset before adding it to the zone. If something is wrong
- // it throws an exception. It doesn't modify the zone, and provides
- // the strong exception guarantee.
- void addValidation(const Name& zone_name, const ConstRRsetPtr rrset) {
- if (!rrset) {
- isc_throw(NullRRset, "The rrset provided is NULL");
- }
- if (rrset->getRdataCount() == 0) {
- isc_throw(AddError, "The rrset provided is empty: " <<
- rrset->getName() << "/" << rrset->getType());
- }
- // Check for singleton RRs. It should probably handled at a different
- // layer in future.
- if ((rrset->getType() == RRType::CNAME() ||
- rrset->getType() == RRType::DNAME()) &&
- rrset->getRdataCount() > 1)
- {
- // XXX: this is not only for CNAME or DNAME. We should generalize
- // this code for all other "singleton RR types" (such as SOA) in a
- // separate task.
- LOG_ERROR(logger,
- DATASRC_MEMORY_MEM_SINGLETON).arg(rrset->getName()).
- arg(rrset->getType());
- isc_throw(AddError, "multiple RRs of singleton type for "
- << rrset->getName());
- }
- // NSEC3/NSEC3PARAM is not a "singleton" per protocol, but this
- // implementation requests it be so at the moment.
- if ((rrset->getType() == RRType::NSEC3() ||
- rrset->getType() == RRType::NSEC3PARAM()) &&
- rrset->getRdataCount() > 1) {
- isc_throw(AddError, "Multiple NSEC3/NSEC3PARAM RDATA is given for "
- << rrset->getName() << " which isn't supported");
- }
-
- // For RRSIGs, check consistency of the type covered.
- // We know the RRset isn't empty, so the following check is safe.
- if (rrset->getType() == RRType::RRSIG()) {
- RdataIteratorPtr rit = rrset->getRdataIterator();
- const RRType covered = dynamic_cast<const generic::RRSIG&>(
- rit->getCurrent()).typeCovered();
- for (rit->next(); !rit->isLast(); rit->next()) {
- if (dynamic_cast<const generic::RRSIG&>(
- rit->getCurrent()).typeCovered() != covered) {
- isc_throw(AddError, "RRSIG contains mixed covered types: "
- << rrset->toText());
- }
- }
- }
-
- const NameComparisonResult compare =
- zone_name.compare(rrset->getName());
- if (compare.getRelation() != NameComparisonResult::SUPERDOMAIN &&
- compare.getRelation() != NameComparisonResult::EQUAL)
- {
- LOG_ERROR(logger,
- DATASRC_MEMORY_MEM_OUT_OF_ZONE).arg(rrset->getName()).
- arg(zone_name);
- isc_throw(OutOfZone, "The name " << rrset->getName() <<
- " is not contained in zone " << zone_name);
- }
-
- // Some RR types do not really work well with a wildcard.
- // Even though the protocol specifically doesn't completely ban such
- // usage, we refuse to load a zone containing such RR in order to
- // keep the lookup logic simpler and more predictable.
- // See RFC4592 and (for DNAME) RFC6672 for more technical background.
- // Note also that BIND 9 refuses NS at a wildcard, so in that sense
- // we simply provide compatible behavior.
- if (rrset->getName().isWildcard()) {
- if (rrset->getType() == RRType::NS()) {
- LOG_ERROR(logger, DATASRC_MEMORY_MEM_WILDCARD_NS).
- arg(rrset->getName());
- isc_throw(AddError, "Invalid NS owner name (wildcard): " <<
- rrset->getName());
- }
- if (rrset->getType() == RRType::DNAME()) {
- LOG_ERROR(logger, DATASRC_MEMORY_MEM_WILDCARD_DNAME).
- arg(rrset->getName());
- isc_throw(AddError, "Invalid DNAME owner name (wildcard): " <<
- rrset->getName());
- }
- }
-
- // Owner names of NSEC3 have special format as defined in RFC5155,
- // and cannot be a wildcard name or must be one label longer than
- // the zone origin. While the RFC doesn't prohibit other forms of
- // names, no sane zone would have such names for NSEC3.
- // BIND 9 also refuses NSEC3 at wildcard.
- if (rrset->getType() == RRType::NSEC3() &&
- (rrset->getName().isWildcard() ||
- rrset->getName().getLabelCount() !=
- zone_name.getLabelCount() + 1)) {
- LOG_ERROR(logger, DATASRC_MEMORY_BAD_NSEC3_NAME).
- arg(rrset->getName());
- isc_throw(AddError, "Invalid NSEC3 owner name: " <<
- rrset->getName());
- }
- }
-
- void addNSEC3(const ConstRRsetPtr rrset,
- const ConstRRsetPtr rrsig,
- ZoneData& zone_data)
- {
- // We know rrset has exactly one RDATA
- const generic::NSEC3& nsec3_rdata =
- dynamic_cast<const generic::NSEC3&>(
- rrset->getRdataIterator()->getCurrent());
-
- NSEC3Data* nsec3_data = zone_data.getNSEC3Data();
- if (nsec3_data == NULL) {
- nsec3_data = NSEC3Data::create(mem_sgmt_, nsec3_rdata);
- zone_data.setNSEC3Data(nsec3_data);
- zone_data.setSigned(true);
- } else {
- size_t salt_len = nsec3_data->getSaltLen();
- const uint8_t* salt_data = nsec3_data->getSaltData();
- const vector<uint8_t>& salt_data_2 = nsec3_rdata.getSalt();
-
- if ((nsec3_rdata.getHashalg() != nsec3_data->hashalg) ||
- (nsec3_rdata.getIterations() != nsec3_data->iterations) ||
- (salt_data_2.size() != salt_len)) {
- isc_throw(AddError,
- "NSEC3 with inconsistent parameters: " <<
- rrset->toText());
- }
- if ((salt_len > 0) &&
- (std::memcmp(&salt_data_2[0], salt_data, salt_len) != 0)) {
- isc_throw(AddError,
- "NSEC3 with inconsistent parameters: " <<
- rrset->toText());
- }
- }
-
- ZoneNode* node;
- nsec3_data->insertName(mem_sgmt_, rrset->getName(), &node);
-
- RdataEncoder encoder;
- RdataSet* set = RdataSet::create(mem_sgmt_, encoder, rrset, rrsig);
- RdataSet* old_set = node->setData(set);
- if (old_set != NULL) {
- RdataSet::destroy(mem_sgmt_, rrclass_, old_set);
- }
- }
-
- void addRdataSet(const Name& zone_name, ZoneData& zone_data,
- const ConstRRsetPtr rrset, const ConstRRsetPtr rrsig)
- {
- if (rrset->getType() == RRType::NSEC3()) {
- addNSEC3(rrset, rrsig, zone_data);
- } else {
- ZoneNode* node;
- zone_data.insertName(mem_sgmt_, rrset->getName(), &node);
-
- RdataSet* rdataset_head = node->getData();
-
- // Checks related to the surrounding data.
- // Note: when the check fails and the exception is thrown,
- // it may break strong exception guarantee. At the moment
- // we prefer code simplicity and don't bother to introduce
- // complicated recovery code.
- contextCheck(zone_name, *rrset, rdataset_head);
-
- if (RdataSet::find(rdataset_head, rrset->getType()) != NULL) {
- isc_throw(AddError,
- "RRset of the type already exists: "
- << rrset->getName() << " (type: "
- << rrset->getType() << ")");
- }
-
- RdataEncoder encoder;
- RdataSet* rdataset = RdataSet::create(mem_sgmt_, encoder, rrset,
- rrsig);
- rdataset->next = rdataset_head;
- node->setData(rdataset);
-
- // Ok, we just put it in
-
- // If this RRset creates a zone cut at this node, mark the
- // node indicating the need for callback in find().
- if (rrset->getType() == RRType::NS() &&
- rrset->getName() != zone_name) {
- node->setFlag(ZoneNode::FLAG_CALLBACK);
- // If it is DNAME, we have a callback as well here
- } else if (rrset->getType() == RRType::DNAME()) {
- node->setFlag(ZoneNode::FLAG_CALLBACK);
- }
-
- // If we've added NSEC3PARAM at zone origin, set up NSEC3
- // specific data or check consistency with already set up
- // parameters.
- if (rrset->getType() == RRType::NSEC3PARAM() &&
- rrset->getName() == zone_name) {
- // We know rrset has exactly one RDATA
- const generic::NSEC3PARAM& param =
- dynamic_cast<const generic::NSEC3PARAM&>
- (rrset->getRdataIterator()->getCurrent());
-
- NSEC3Data* nsec3_data = zone_data.getNSEC3Data();
- if (nsec3_data == NULL) {
- nsec3_data = NSEC3Data::create(mem_sgmt_, param);
- zone_data.setNSEC3Data(nsec3_data);
- zone_data.setSigned(true);
- } else {
- size_t salt_len = nsec3_data->getSaltLen();
- const uint8_t* salt_data = nsec3_data->getSaltData();
- const vector<uint8_t>& salt_data_2 = param.getSalt();
-
- if ((param.getHashalg() != nsec3_data->hashalg) ||
- (param.getIterations() != nsec3_data->iterations) ||
- (salt_data_2.size() != salt_len)) {
- isc_throw(AddError,
- "NSEC3PARAM with inconsistent parameters: "
- << rrset->toText());
- }
-
- if ((salt_len > 0) &&
- (std::memcmp(&salt_data_2[0],
- salt_data, salt_len) != 0)) {
- isc_throw(AddError,
- "NSEC3PARAM with inconsistent parameters: "
- << rrset->toText());
- }
- }
- } else if (rrset->getType() == RRType::NSEC()) {
- // If it is NSEC signed zone, we mark the zone as signed
- // (conceptually "signed" is a broader notion but our current
- // zone finder implementation regards "signed" as "NSEC
- // signed")
- zone_data.setSigned(true);
- }
- }
- }
-
- // Implementation of InMemoryClient::add()
- void add(const ConstRRsetPtr& rrset, const ConstRRsetPtr& sig_rrset,
- const Name& zone_name, ZoneData& zone_data)
- {
- // Sanitize input. This will cause an exception to be thrown
- // if the input RRset is empty.
- addValidation(zone_name, rrset);
- if (sig_rrset) {
- addValidation(zone_name, sig_rrset);
- }
+ FileNameDeleter() {}
- // OK, can add the RRset.
- LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEMORY_MEM_ADD_RRSET).
- arg(rrset->getName()).arg(rrset->getType()).arg(zone_name);
-
- // Add wildcards possibly contained in the owner name to the domain
- // tree. This can only happen for the normal (non-NSEC3) tree.
- // Note: this can throw an exception, breaking strong exception
- // guarantee. (see also the note for the call to contextCheck()
- // above).
- if (rrset->getType() != RRType::NSEC3()) {
- addWildcards(zone_name, zone_data, rrset->getName());
- }
-
- addRdataSet(zone_name, zone_data, rrset, sig_rrset);
+ void operator()(std::string* filename) const {
+ delete filename;
}
};
-// A helper internal class for load(). make it non-copyable to avoid
-// accidental copy.
-//
-// The current internal implementation expects that both a normal
-// (non RRSIG) RRset and (when signed) its RRSIG are added at once.
-// Also in the current implementation, the input sequence of RRsets
-// are grouped with their owner name (so once a new owner name is encountered,
-// no subsequent RRset has the previous owner name), but the ordering
-// in the same group is not fixed. So we hold all RRsets of the same
-// owner name in node_rrsets_ and node_rrsigsets_, and add the matching
-// pairs of RRsets to the zone when we see a new owner name.
-//
-// The caller is responsible for adding the RRsets of the last group
-// in the input sequence by explicitly calling flushNodeRRsets() at the
-// end. It's cleaner and more robust if we let the destructor of this class
-// do it, but since we cannot guarantee the adding operation is exception free,
-// we don't choose that option to maintain the common expectation for
-// destructors.
-class InMemoryClient::Loader : boost::noncopyable {
- typedef std::map<RRType, ConstRRsetPtr> NodeRRsets;
- typedef NodeRRsets::value_type NodeRRsetsVal;
-public:
- Loader(InMemoryClientImpl* client_impl, const Name& zone_name,
- ZoneData& zone_data) :
- client_impl_(client_impl), zone_name_(zone_name), zone_data_(zone_data)
- {}
- void addFromLoad(const ConstRRsetPtr& rrset) {
- // If we see a new name, flush the temporary holders, adding the
- // pairs of RRsets and RRSIGs of the previous name to the zone.
- if ((!node_rrsets_.empty() || !node_rrsigsets_.empty()) &&
- getCurrentName() != rrset->getName()) {
- flushNodeRRsets();
- }
+} // end of unnamed namespace
- // Store this RRset until it can be added to the zone. The current
- // implementation requires RRs of the same RRset should be added at
- // once, so we check the "duplicate" here.
- const bool is_rrsig = rrset->getType() == RRType::RRSIG();
- NodeRRsets& node_rrsets = is_rrsig ? node_rrsigsets_ : node_rrsets_;
- const RRType& rrtype = is_rrsig ?
- getCoveredType(rrset) : rrset->getType();
- if (!node_rrsets.insert(NodeRRsetsVal(rrtype, rrset)).second) {
- isc_throw(AddError,
- "Duplicate add of the same type of"
- << (is_rrsig ? " RRSIG" : "") << " RRset: "
- << rrset->getName() << "/" << rrtype);
- }
- }
- void flushNodeRRsets() {
- BOOST_FOREACH(NodeRRsetsVal val, node_rrsets_) {
- // Identify the corresponding RRSIG for the RRset, if any.
- // If found add both the RRset and its RRSIG at once.
- ConstRRsetPtr sig_rrset;
- NodeRRsets::iterator sig_it =
- node_rrsigsets_.find(val.first);
- if (sig_it != node_rrsigsets_.end()) {
- sig_rrset = sig_it->second;
- node_rrsigsets_.erase(sig_it);
- }
- client_impl_->add(val.second, sig_rrset, zone_name_, zone_data_);
- }
+InMemoryClient::InMemoryClient(util::MemorySegment& mem_sgmt,
+ RRClass rrclass) :
+ mem_sgmt_(mem_sgmt),
+ rrclass_(rrclass),
+ zone_count_(0)
+{
+ SegmentObjectHolder<ZoneTable, RRClass> holder(
+ mem_sgmt_, ZoneTable::create(mem_sgmt_, rrclass), rrclass_);
- // Right now, we don't accept RRSIG without covered RRsets (this
- // should eventually allowed, but to do so we'll need to update the
- // finder).
- if (!node_rrsigsets_.empty()) {
- isc_throw(AddError, "RRSIG is added without covered RRset for "
- << getCurrentName());
- }
+ file_name_tree_ = FileNameTree::create(mem_sgmt_, false);
- node_rrsets_.clear();
- node_rrsigsets_.clear();
- }
-private:
- // A helper to identify the covered type of an RRSIG.
- static RRType getCoveredType(const ConstRRsetPtr& sig_rrset) {
- RdataIteratorPtr it = sig_rrset->getRdataIterator();
- // Empty RRSIG shouldn't be passed either via a master file or another
- // data source iterator, but it could still happen if the iterator
- // has a bug. We catch and reject such cases.
- if (it->isLast()) {
- isc_throw(isc::Unexpected,
- "Empty RRset is passed in-memory loader, name: "
- << sig_rrset->getName());
- }
- return (dynamic_cast<const generic::RRSIG&>(it->getCurrent()).
- typeCovered());
- }
- const Name& getCurrentName() const {
- if (!node_rrsets_.empty()) {
- return (node_rrsets_.begin()->second->getName());
- }
- assert(!node_rrsigsets_.empty());
- return (node_rrsigsets_.begin()->second->getName());
- }
+ zone_table_ = holder.release();
+}
-private:
- InMemoryClientImpl* client_impl_;
- const Name& zone_name_;
- ZoneData& zone_data_;
- NodeRRsets node_rrsets_;
- NodeRRsets node_rrsigsets_;
-};
+InMemoryClient::~InMemoryClient() {
+ FileNameDeleter deleter;
+ FileNameTree::destroy(mem_sgmt_, file_name_tree_, deleter);
+
+ ZoneTable::destroy(mem_sgmt_, zone_table_, rrclass_);
+}
result::Result
-InMemoryClient::InMemoryClientImpl::load(
- const Name& zone_name,
- const string& filename,
- boost::function<void(LoadCallback)> rrset_installer)
+InMemoryClient::loadInternal(const isc::dns::Name& zone_name,
+ const std::string& filename,
+ ZoneData* zone_data)
{
SegmentObjectHolder<ZoneData, RRClass> holder(
- mem_sgmt_, ZoneData::create(mem_sgmt_, zone_name), rrclass_);
-
- Loader loader(this, zone_name, *holder.get());
- rrset_installer(boost::bind(&Loader::addFromLoad, &loader, _1));
- // Add any last RRsets that were left
- loader.flushNodeRRsets();
-
- const ZoneNode* origin_node = holder.get()->getOriginNode();
- const RdataSet* set = origin_node->getData();
- // If the zone is NSEC3-signed, check if it has NSEC3PARAM
- if (holder.get()->isNSEC3Signed()) {
- if (RdataSet::find(set, RRType::NSEC3PARAM()) == NULL) {
- LOG_WARN(logger, DATASRC_MEMORY_MEM_NO_NSEC3PARAM).
- arg(zone_name).arg(rrclass_);
- }
- }
-
- // When an empty zone file is loaded, the origin doesn't even have
- // an SOA RR. This condition should be avoided, and hence load()
- // should throw when an empty zone is loaded.
- if (RdataSet::find(set, RRType::SOA()) == NULL) {
- isc_throw(EmptyZone,
- "Won't create an empty zone for: " << zone_name);
- }
+ mem_sgmt_, zone_data, rrclass_);
LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_MEMORY_MEM_ADD_ZONE).
arg(zone_name).arg(rrclass_);
@@ -627,67 +111,33 @@ InMemoryClient::InMemoryClientImpl::load(
// node must point to a valid node now
assert(node != NULL);
- std::string* tstr = node->setData(new std::string(filename));
+ const std::string* tstr = node->setData(new std::string(filename));
delete tstr;
- ZoneTable::AddResult result(zone_table_->addZone(mem_sgmt_, rrclass_,
- zone_name));
+ const ZoneTable::AddResult result(zone_table_->addZone(mem_sgmt_, rrclass_,
+ zone_name,
+ holder.release()));
if (result.code == result::SUCCESS) {
// Only increment the zone count if the zone doesn't already
// exist.
++zone_count_;
}
-
- ZoneTable::FindResult fr(zone_table_->setZoneData(zone_name,
- holder.release()));
- assert(fr.code == result::SUCCESS);
- if (fr.zone_data != NULL) {
- ZoneData::destroy(mem_sgmt_, fr.zone_data, rrclass_);
+ // Destroy the old instance of the zone if there was any
+ if (result.zone_data != NULL) {
+ ZoneData::destroy(mem_sgmt_, result.zone_data, rrclass_);
}
return (result.code);
}
-namespace {
-// A wrapper for dns::masterLoad used by load() below. Essentially it
-// converts the two callback types. Note the mostly redundant wrapper of
-// boost::bind. It converts function<void(ConstRRsetPtr)> to
-// function<void(RRsetPtr)> (masterLoad() expects the latter). SunStudio
-// doesn't seem to do this conversion if we just pass 'callback'.
-void
-masterLoadWrapper(const char* const filename, const Name& origin,
- const RRClass& zone_class, LoadCallback callback)
-{
- masterLoad(filename, origin, zone_class, boost::bind(callback, _1));
-}
-
-// The installer called from Impl::load() for the iterator version of load().
-void
-generateRRsetFromIterator(ZoneIterator* iterator, LoadCallback callback) {
- ConstRRsetPtr rrset;
- while ((rrset = iterator->getNextRRset()) != NULL) {
- callback(rrset);
- }
-}
-}
-
-InMemoryClient::InMemoryClient(util::MemorySegment& mem_sgmt,
- RRClass rrclass) :
- impl_(new InMemoryClientImpl(mem_sgmt, rrclass))
-{}
-
-InMemoryClient::~InMemoryClient() {
- delete impl_;
-}
-
RRClass
InMemoryClient::getClass() const {
- return (impl_->rrclass_);
+ return (rrclass_);
}
unsigned int
InMemoryClient::getZoneCount() const {
- return (impl_->zone_count_);
+ return (zone_count_);
}
isc::datasrc::DataSourceClient::FindResult
@@ -695,7 +145,7 @@ InMemoryClient::findZone(const isc::dns::Name& zone_name) const {
LOG_DEBUG(logger, DBG_TRACE_DATA,
DATASRC_MEMORY_MEM_FIND_ZONE).arg(zone_name);
- ZoneTable::FindResult result(impl_->zone_table_->findZone(zone_name));
+ ZoneTable::FindResult result(zone_table_->findZone(zone_name));
ZoneFinderPtr finder;
if (result.code != result::NOTFOUND) {
@@ -707,34 +157,34 @@ InMemoryClient::findZone(const isc::dns::Name& zone_name) const {
const ZoneData*
InMemoryClient::findZoneData(const isc::dns::Name& zone_name) {
- ZoneTable::FindResult result(impl_->zone_table_->findZone(zone_name));
+ ZoneTable::FindResult result(zone_table_->findZone(zone_name));
return (result.zone_data);
}
result::Result
InMemoryClient::load(const isc::dns::Name& zone_name,
- const std::string& filename) {
+ const std::string& filename)
+{
LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_MEMORY_MEM_LOAD).arg(zone_name).
arg(filename);
- return (impl_->load(zone_name, filename,
- boost::bind(masterLoadWrapper, filename.c_str(),
- zone_name, getClass(), _1)));
+ ZoneData* zone_data = loadZoneData(mem_sgmt_, rrclass_, zone_name,
+ filename);
+ return (loadInternal(zone_name, filename, zone_data));
}
result::Result
-InMemoryClient::load(const isc::dns::Name& zone_name,
- ZoneIterator& iterator) {
- return (impl_->load(zone_name, string(),
- boost::bind(generateRRsetFromIterator,
- &iterator, _1)));
+InMemoryClient::load(const isc::dns::Name& zone_name, ZoneIterator& iterator) {
+ ZoneData* zone_data = loadZoneData(mem_sgmt_, rrclass_, zone_name,
+ iterator);
+ return (loadInternal(zone_name, string(), zone_data));
}
const std::string
InMemoryClient::getFileName(const isc::dns::Name& zone_name) const {
- FileNameNode* node(NULL);
- FileNameTree::Result result = impl_->file_name_tree_->find(zone_name,
- &node);
+ const FileNameNode* node(NULL);
+ const FileNameTree::Result result = file_name_tree_->find(zone_name,
+ &node);
if (result == FileNameTree::EXACTMATCH) {
return (*node->getData());
} else {
@@ -742,24 +192,6 @@ InMemoryClient::getFileName(const isc::dns::Name& zone_name) const {
}
}
-result::Result
-InMemoryClient::add(const isc::dns::Name& zone_name,
- const ConstRRsetPtr& rrset)
-{
- const ZoneTable::FindResult result =
- impl_->zone_table_->findZone(zone_name);
- if (result.code != result::SUCCESS) {
- isc_throw(DataSourceError, "No such zone: " + zone_name.toText());
- }
-
- const ConstRRsetPtr sig_rrset =
- rrset ? rrset->getRRsig() : ConstRRsetPtr();
- impl_->add(rrset, sig_rrset, zone_name, *result.zone_data);
-
- // add() doesn't allow duplicate add, so we always return SUCCESS.
- return (result::SUCCESS);
-}
-
namespace {
class MemoryIterator : public ZoneIterator {
@@ -874,7 +306,7 @@ public:
ZoneIteratorPtr
InMemoryClient::getIterator(const Name& name, bool separate_rrs) const {
- ZoneTable::FindResult result(impl_->zone_table_->findZone(name));
+ ZoneTable::FindResult result(zone_table_->findZone(name));
if (result.code != result::SUCCESS) {
isc_throw(DataSourceError, "No such zone: " + name.toText());
}
diff --git a/src/lib/datasrc/memory/memory_client.h b/src/lib/datasrc/memory/memory_client.h
index 330d62e..3313c5b 100644
--- a/src/lib/datasrc/memory/memory_client.h
+++ b/src/lib/datasrc/memory/memory_client.h
@@ -139,69 +139,6 @@ public:
/// zone from a file before.
const std::string getFileName(const isc::dns::Name& zone_name) const;
- /// \brief Inserts an rrset into the zone.
- ///
- /// It puts another RRset into the zone.
- ///
- /// In the current implementation, this method doesn't allow an existing
- /// RRset to be updated or overridden. So the caller must make sure that
- /// all RRs of the same type and name must be given in the form of a
- /// single RRset. The current implementation will also require that
- /// when an RRSIG is added, the RRset to be covered has already been
- /// added. These restrictions are probably too strict when this data
- /// source accepts various forms of input, so they should be revisited
- /// later.
- ///
- /// Except for NullRRset and OutOfZone, this method does not guarantee
- /// strong exception safety (it is currently not needed, if it is needed
- /// in future, it should be implemented).
- ///
- /// \throw NullRRset \c rrset is a NULL pointer.
- /// \throw OutOfZone The owner name of \c rrset is outside of the
- /// origin of the zone.
- /// \throw AddError Other general errors.
- /// \throw Others This method might throw standard allocation exceptions.
- ///
- /// \param rrset The set to add.
- /// \return SUCCESS or EXIST (if an rrset for given name and type already
- /// exists).
- result::Result add(const isc::dns::Name& zone_name,
- const isc::dns::ConstRRsetPtr& rrset);
-
- /// \brief RRset is NULL exception.
- ///
- /// This is thrown if the provided RRset parameter is NULL.
- struct NullRRset : public InvalidParameter {
- NullRRset(const char* file, size_t line, const char* what) :
- InvalidParameter(file, line, what)
- { }
- };
-
- /// \brief Zone is empty exception.
- ///
- /// This is thrown if we have an empty zone created as a result of
- /// load().
- struct EmptyZone : public InvalidParameter {
- EmptyZone(const char* file, size_t line, const char* what) :
- InvalidParameter(file, line, what)
- { }
- };
-
- /// \brief General failure exception for \c add().
- ///
- /// This is thrown against general error cases in adding an RRset
- /// to the zone.
- ///
- /// Note: this exception would cover cases for \c OutOfZone or
- /// \c NullRRset. We'll need to clarify and unify the granularity
- /// of exceptions eventually. For now, exceptions are added as
- /// developers see the need for it.
- struct AddError : public InvalidParameter {
- AddError(const char* file, size_t line, const char* what) :
- InvalidParameter(file, line, what)
- { }
- };
-
/// Returns a \c ZoneFinder result that best matches the given name.
///
/// This derived version of the method never throws an exception.
@@ -239,14 +176,21 @@ public:
uint32_t end_serial) const;
private:
- // TODO: Do we still need the PImpl if nobody should manipulate this class
- // directly any more (it should be handled through DataSourceClient)?
- class InMemoryClientImpl;
- InMemoryClientImpl* impl_;
-
- // A helper internal class used by load(). It maintains some intermediate
- // states while loading RRs of the zone.
- class Loader;
+ // Some type aliases
+ typedef DomainTree<std::string> FileNameTree;
+ typedef DomainTreeNode<std::string> FileNameNode;
+
+ // Common process for zone load. Registers filename internally and
+ // adds the ZoneData to the ZoneTable.
+ result::Result loadInternal(const isc::dns::Name& zone_name,
+ const std::string& filename,
+ ZoneData* zone_data);
+
+ util::MemorySegment& mem_sgmt_;
+ const isc::dns::RRClass rrclass_;
+ unsigned int zone_count_;
+ ZoneTable* zone_table_;
+ FileNameTree* file_name_tree_;
};
} // namespace memory
diff --git a/src/lib/datasrc/memory/zone_data_loader.cc b/src/lib/datasrc/memory/zone_data_loader.cc
new file mode 100644
index 0000000..97c8092
--- /dev/null
+++ b/src/lib/datasrc/memory/zone_data_loader.cc
@@ -0,0 +1,250 @@
+// Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <datasrc/memory/zone_data_loader.h>
+#include <datasrc/memory/zone_data_updater.h>
+#include <datasrc/memory/logger.h>
+#include <datasrc/memory/segment_object_holder.h>
+
+#include <dns/rdataclass.h>
+#include <dns/rrset.h>
+#include <dns/masterload.h>
+
+#include <boost/foreach.hpp>
+#include <boost/bind.hpp>
+#include <boost/noncopyable.hpp>
+
+#include <map>
+
+using namespace isc::dns;
+using namespace isc::dns::rdata;
+
+namespace isc {
+namespace datasrc {
+namespace memory {
+
+using detail::SegmentObjectHolder;
+
+namespace { // unnamed namespace
+
+// A functor type used for loading.
+typedef boost::function<void(isc::dns::ConstRRsetPtr)> LoadCallback;
+
+// A helper internal class for \c loadZoneData(). make it non-copyable
+// to avoid accidental copy.
+//
+// The current internal implementation expects that both a normal
+// (non RRSIG) RRset and (when signed) its RRSIG are added at once.
+// Also in the current implementation, the input sequence of RRsets
+// are grouped with their owner name (so once a new owner name is encountered,
+// no subsequent RRset has the previous owner name), but the ordering
+// in the same group is not fixed. So we hold all RRsets of the same
+// owner name in node_rrsets_ and node_rrsigsets_, and add the matching
+// pairs of RRsets to the zone when we see a new owner name.
+//
+// The caller is responsible for adding the RRsets of the last group
+// in the input sequence by explicitly calling flushNodeRRsets() at the
+// end. It's cleaner and more robust if we let the destructor of this class
+// do it, but since we cannot guarantee the adding operation is exception free,
+// we don't choose that option to maintain the common expectation for
+// destructors.
+class ZoneDataLoader : boost::noncopyable {
+public:
+ ZoneDataLoader(util::MemorySegment& mem_sgmt,
+ const isc::dns::RRClass rrclass,
+ const isc::dns::Name& zone_name, ZoneData& zone_data) :
+ updater_(mem_sgmt, rrclass, zone_name, zone_data)
+ {}
+
+ void addFromLoad(const isc::dns::ConstRRsetPtr& rrset);
+ void flushNodeRRsets();
+
+private:
+ typedef std::map<isc::dns::RRType, isc::dns::ConstRRsetPtr> NodeRRsets;
+ typedef NodeRRsets::value_type NodeRRsetsVal;
+
+ // A helper to identify the covered type of an RRSIG.
+ static isc::dns::RRType getCoveredType
+ (const isc::dns::ConstRRsetPtr& sig_rrset);
+ const isc::dns::Name& getCurrentName() const;
+
+private:
+ NodeRRsets node_rrsets_;
+ NodeRRsets node_rrsigsets_;
+ ZoneDataUpdater updater_;
+};
+
+void
+ZoneDataLoader::addFromLoad(const ConstRRsetPtr& rrset) {
+ // If we see a new name, flush the temporary holders, adding the
+ // pairs of RRsets and RRSIGs of the previous name to the zone.
+ if ((!node_rrsets_.empty() || !node_rrsigsets_.empty()) &&
+ (getCurrentName() != rrset->getName())) {
+ flushNodeRRsets();
+ }
+
+ // Store this RRset until it can be added to the zone. The current
+ // implementation requires RRs of the same RRset should be added at
+ // once, so we check the "duplicate" here.
+ const bool is_rrsig = rrset->getType() == RRType::RRSIG();
+ NodeRRsets& node_rrsets = is_rrsig ? node_rrsigsets_ : node_rrsets_;
+ const RRType& rrtype = is_rrsig ? getCoveredType(rrset) : rrset->getType();
+ if (!node_rrsets.insert(NodeRRsetsVal(rrtype, rrset)).second) {
+ isc_throw(ZoneDataUpdater::AddError,
+ "Duplicate add of the same type of"
+ << (is_rrsig ? " RRSIG" : "") << " RRset: "
+ << rrset->getName() << "/" << rrtype);
+ }
+
+ if (rrset->getRRsig()) {
+ addFromLoad(rrset->getRRsig());
+ }
+}
+
+void
+ZoneDataLoader::flushNodeRRsets() {
+ BOOST_FOREACH(NodeRRsetsVal val, node_rrsets_) {
+ // Identify the corresponding RRSIG for the RRset, if any. If
+ // found add both the RRset and its RRSIG at once.
+ ConstRRsetPtr sig_rrset;
+ NodeRRsets::iterator sig_it = node_rrsigsets_.find(val.first);
+ if (sig_it != node_rrsigsets_.end()) {
+ sig_rrset = sig_it->second;
+ node_rrsigsets_.erase(sig_it);
+ }
+ updater_.add(val.second, sig_rrset);
+ }
+
+ // Right now, we don't accept RRSIG without covered RRsets (this
+ // should eventually allowed, but to do so we'll need to update the
+ // finder).
+ if (!node_rrsigsets_.empty()) {
+ isc_throw(ZoneDataUpdater::AddError,
+ "RRSIG is added without covered RRset for "
+ << getCurrentName());
+ }
+
+ node_rrsets_.clear();
+ node_rrsigsets_.clear();
+}
+
+RRType
+ZoneDataLoader::getCoveredType(const ConstRRsetPtr& sig_rrset) {
+ RdataIteratorPtr it = sig_rrset->getRdataIterator();
+ // Empty RRSIG shouldn't be passed either via a master file or
+ // another data source iterator, but it could still happen if the
+ // iterator has a bug. We catch and reject such cases.
+ if (it->isLast()) {
+ isc_throw(isc::Unexpected,
+ "Empty RRset is passed in-memory loader, name: "
+ << sig_rrset->getName());
+ }
+ return (dynamic_cast<const generic::RRSIG&>(it->getCurrent()).
+ typeCovered());
+}
+
+const Name&
+ZoneDataLoader::getCurrentName() const {
+ if (!node_rrsets_.empty()) {
+ return (node_rrsets_.begin()->second->getName());
+ }
+ assert(!node_rrsigsets_.empty());
+ return (node_rrsigsets_.begin()->second->getName());
+}
+
+ZoneData*
+loadZoneDataInternal(util::MemorySegment& mem_sgmt,
+ const isc::dns::RRClass rrclass,
+ const Name& zone_name,
+ boost::function<void(LoadCallback)> rrset_installer)
+{
+ SegmentObjectHolder<ZoneData, RRClass> holder(
+ mem_sgmt, ZoneData::create(mem_sgmt, zone_name), rrclass);
+
+ ZoneDataLoader loader(mem_sgmt, rrclass, zone_name, *holder.get());
+ rrset_installer(boost::bind(&ZoneDataLoader::addFromLoad, &loader, _1));
+ // Add any last RRsets that were left
+ loader.flushNodeRRsets();
+
+ const ZoneNode* origin_node = holder.get()->getOriginNode();
+ const RdataSet* rdataset = origin_node->getData();
+ // If the zone is NSEC3-signed, check if it has NSEC3PARAM
+ if (holder.get()->isNSEC3Signed()) {
+ if (RdataSet::find(rdataset, RRType::NSEC3PARAM()) == NULL) {
+ LOG_WARN(logger, DATASRC_MEMORY_MEM_NO_NSEC3PARAM).
+ arg(zone_name).arg(rrclass);
+ }
+ }
+
+ // When an empty zone file is loaded, the origin doesn't even have
+ // an SOA RR. This condition should be avoided, and hence load()
+ // should throw when an empty zone is loaded.
+ if (RdataSet::find(rdataset, RRType::SOA()) == NULL) {
+ isc_throw(EmptyZone,
+ "Won't create an empty zone for: " << zone_name);
+ }
+
+ return (holder.release());
+}
+
+// A wrapper for dns::masterLoad used by loadZoneData() below. Essentially it
+// converts the two callback types. Note the mostly redundant wrapper of
+// boost::bind. It converts function<void(ConstRRsetPtr)> to
+// function<void(RRsetPtr)> (masterLoad() expects the latter). SunStudio
+// doesn't seem to do this conversion if we just pass 'callback'.
+void
+masterLoadWrapper(const char* const filename, const Name& origin,
+ const RRClass& zone_class, LoadCallback callback)
+{
+ masterLoad(filename, origin, zone_class, boost::bind(callback, _1));
+}
+
+// The installer called from the iterator version of loadZoneData().
+void
+generateRRsetFromIterator(ZoneIterator* iterator, LoadCallback callback) {
+ ConstRRsetPtr rrset;
+ while ((rrset = iterator->getNextRRset()) != NULL) {
+ callback(rrset);
+ }
+}
+
+} // end of unnamed namespace
+
+ZoneData*
+loadZoneData(util::MemorySegment& mem_sgmt,
+ const isc::dns::RRClass rrclass,
+ const isc::dns::Name& zone_name,
+ const std::string& zone_file)
+{
+ return (loadZoneDataInternal(mem_sgmt, rrclass, zone_name,
+ boost::bind(masterLoadWrapper,
+ zone_file.c_str(),
+ zone_name, rrclass,
+ _1)));
+}
+
+ZoneData*
+loadZoneData(util::MemorySegment& mem_sgmt,
+ const isc::dns::RRClass rrclass,
+ const isc::dns::Name& zone_name,
+ ZoneIterator& iterator)
+{
+ return (loadZoneDataInternal(mem_sgmt, rrclass, zone_name,
+ boost::bind(generateRRsetFromIterator,
+ &iterator, _1)));
+}
+
+} // namespace memory
+} // namespace datasrc
+} // namespace isc
diff --git a/src/lib/datasrc/memory/zone_data_loader.h b/src/lib/datasrc/memory/zone_data_loader.h
new file mode 100644
index 0000000..298af46
--- /dev/null
+++ b/src/lib/datasrc/memory/zone_data_loader.h
@@ -0,0 +1,80 @@
+// Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef DATASRC_ZONE_DATA_LOADER_H
+#define DATASRC_ZONE_DATA_LOADER_H 1
+
+#include <datasrc/memory/zone_data.h>
+#include <datasrc/iterator.h>
+#include <dns/name.h>
+#include <dns/rrclass.h>
+#include <util/memory_segment.h>
+
+namespace isc {
+namespace datasrc {
+namespace memory {
+
+/// \brief Zone is empty exception.
+///
+/// This is thrown if an empty zone would be created during
+/// \c loadZoneData().
+struct EmptyZone : public InvalidParameter {
+ EmptyZone(const char* file, size_t line, const char* what) :
+ InvalidParameter(file, line, what)
+ {}
+};
+
+/// \brief Create and return a ZoneData instance populated from the
+/// \c zone_file.
+///
+/// Throws \c ZoneDataUpdater::AddError if invalid or inconsistent data
+/// is present in the \c zone_file. Throws \c isc::Unexpected if empty
+/// RRsets are passed by the master loader. Throws \c EmptyZone if an
+/// empty zone would be created due to the \c loadZoneData().
+///
+/// \param mem_sgmt The memory segment.
+/// \param rrclass The RRClass.
+/// \param zone_name The name of the zone that is being loaded.
+/// \param zone_file Filename which contains the zone data for \c zone_name.
+ZoneData* loadZoneData(util::MemorySegment& mem_sgmt,
+ const isc::dns::RRClass rrclass,
+ const isc::dns::Name& zone_name,
+ const std::string& zone_file);
+
+/// \brief Create and return a ZoneData instance populated from the
+/// \c iterator.
+///
+/// Throws \c ZoneDataUpdater::AddError if invalid or inconsistent data
+/// is present in the \c zone_file. Throws \c isc::Unexpected if empty
+/// RRsets are passed by the zone iterator. Throws \c EmptyZone if an
+/// empty zone would be created due to the \c loadZoneData().
+///
+/// \param mem_sgmt The memory segment.
+/// \param rrclass The RRClass.
+/// \param zone_name The name of the zone that is being loaded.
+/// \param iterator Iterator that returns RRsets to load into the zone.
+ZoneData* loadZoneData(util::MemorySegment& mem_sgmt,
+ const isc::dns::RRClass rrclass,
+ const isc::dns::Name& zone_name,
+ ZoneIterator& iterator);
+
+} // namespace memory
+} // namespace datasrc
+} // namespace isc
+
+#endif // DATASRC_ZONE_DATA_LOADER_H
+
+// Local Variables:
+// mode: c++
+// End:
diff --git a/src/lib/datasrc/memory/zone_data_updater.cc b/src/lib/datasrc/memory/zone_data_updater.cc
new file mode 100644
index 0000000..037eeb4
--- /dev/null
+++ b/src/lib/datasrc/memory/zone_data_updater.cc
@@ -0,0 +1,347 @@
+// Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <datasrc/memory/zone_data_updater.h>
+#include <datasrc/memory/logger.h>
+#include <datasrc/zone.h>
+
+#include <dns/rdataclass.h>
+
+using namespace isc::dns;
+using namespace isc::dns::rdata;
+
+namespace isc {
+namespace datasrc {
+namespace memory {
+
+void
+ZoneDataUpdater::addWildcards(const Name& name) {
+ Name wname(name);
+ const unsigned int labels(wname.getLabelCount());
+ const unsigned int origin_labels(zone_name_.getLabelCount());
+ for (unsigned int l = labels;
+ l > origin_labels;
+ --l, wname = wname.split(1))
+ {
+ if (wname.isWildcard()) {
+ LOG_DEBUG(logger, DBG_TRACE_DATA,
+ DATASRC_MEMORY_MEM_ADD_WILDCARD).arg(name);
+
+ // Ensure a separate level exists for the "wildcarding"
+ // name, and mark the node as "wild".
+ ZoneNode* node;
+ zone_data_.insertName(mem_sgmt_, wname.split(1), &node);
+ node->setFlag(ZoneData::WILDCARD_NODE);
+
+ // Ensure a separate level exists for the wildcard name.
+ // Note: for 'name' itself we do this later anyway, but the
+ // overhead should be marginal because wildcard names should
+ // be rare.
+ zone_data_.insertName(mem_sgmt_, wname, &node);
+ }
+ }
+}
+
+void
+ZoneDataUpdater::contextCheck(const AbstractRRset& rrset,
+ const RdataSet* rdataset) const
+{
+ // Ensure CNAME and other type of RR don't coexist for the same
+ // owner name except with NSEC, which is the only RR that can
+ // coexist with CNAME (and also RRSIG, which is handled separately)
+ if (rrset.getType() == RRType::CNAME()) {
+ for (const RdataSet* sp = rdataset; sp != NULL; sp = sp->getNext()) {
+ if (sp->type != RRType::NSEC()) {
+ LOG_ERROR(logger, DATASRC_MEMORY_MEM_CNAME_TO_NONEMPTY).
+ arg(rrset.getName());
+ isc_throw(AddError,
+ "CNAME can't be added with " << sp->type
+ << " RRType for " << rrset.getName());
+ }
+ }
+ } else if ((rrset.getType() != RRType::NSEC()) &&
+ (RdataSet::find(rdataset, RRType::CNAME()) != NULL))
+ {
+ LOG_ERROR(logger,
+ DATASRC_MEMORY_MEM_CNAME_COEXIST).arg(rrset.getName());
+ isc_throw(AddError,
+ "CNAME and " << rrset.getType() <<
+ " can't coexist for " << rrset.getName());
+ }
+
+ // Similar with DNAME, but it must not coexist only with NS and only
+ // in non-apex domains. RFC 2672 section 3 mentions that it is
+ // implied from it and RFC 2181.
+ if (rrset.getName() != zone_name_ &&
+ // Adding DNAME, NS already there
+ ((rrset.getType() == RRType::DNAME() &&
+ RdataSet::find(rdataset, RRType::NS()) != NULL) ||
+ // Adding NS, DNAME already there
+ (rrset.getType() == RRType::NS() &&
+ RdataSet::find(rdataset, RRType::DNAME()) != NULL)))
+ {
+ LOG_ERROR(logger, DATASRC_MEMORY_MEM_DNAME_NS).arg(rrset.getName());
+ isc_throw(AddError, "DNAME can't coexist with NS in non-apex domain: "
+ << rrset.getName());
+ }
+}
+
+void
+ZoneDataUpdater::validate(const isc::dns::ConstRRsetPtr rrset) const {
+ if (!rrset) {
+ isc_throw(NullRRset, "The rrset provided is NULL");
+ }
+
+ if (rrset->getRdataCount() == 0) {
+ isc_throw(AddError,
+ "The rrset provided is empty: "
+ << rrset->getName() << "/" << rrset->getType());
+ }
+
+ // Check for singleton RRs. It should probably handled at a different
+ // layer in future.
+ if ((rrset->getType() == RRType::CNAME() ||
+ rrset->getType() == RRType::DNAME()) &&
+ rrset->getRdataCount() > 1)
+ {
+ // XXX: this is not only for CNAME or DNAME. We should
+ // generalize this code for all other "singleton RR types" (such
+ // as SOA) in a separate task.
+ LOG_ERROR(logger,
+ DATASRC_MEMORY_MEM_SINGLETON).arg(rrset->getName()).
+ arg(rrset->getType());
+ isc_throw(AddError, "multiple RRs of singleton type for "
+ << rrset->getName());
+ }
+
+ // NSEC3/NSEC3PARAM is not a "singleton" per protocol, but this
+ // implementation requests it be so at the moment.
+ if ((rrset->getType() == RRType::NSEC3() ||
+ rrset->getType() == RRType::NSEC3PARAM()) &&
+ (rrset->getRdataCount() > 1))
+ {
+ isc_throw(AddError, "Multiple NSEC3/NSEC3PARAM RDATA is given for "
+ << rrset->getName() << " which isn't supported");
+ }
+
+ // For RRSIGs, check consistency of the type covered. We know the
+ // RRset isn't empty, so the following check is safe.
+ if (rrset->getType() == RRType::RRSIG()) {
+ RdataIteratorPtr rit = rrset->getRdataIterator();
+ const RRType covered = dynamic_cast<const generic::RRSIG&>(
+ rit->getCurrent()).typeCovered();
+ for (rit->next(); !rit->isLast(); rit->next()) {
+ if (dynamic_cast<const generic::RRSIG&>(
+ rit->getCurrent()).typeCovered() != covered)
+ {
+ isc_throw(AddError, "RRSIG contains mixed covered types: "
+ << rrset->toText());
+ }
+ }
+ }
+
+ const NameComparisonResult compare = zone_name_.compare(rrset->getName());
+ if (compare.getRelation() != NameComparisonResult::SUPERDOMAIN &&
+ compare.getRelation() != NameComparisonResult::EQUAL)
+ {
+ LOG_ERROR(logger,
+ DATASRC_MEMORY_MEM_OUT_OF_ZONE).arg(rrset->getName()).
+ arg(zone_name_);
+ isc_throw(OutOfZone,
+ "The name " << rrset->getName() <<
+ " is not contained in zone " << zone_name_);
+ }
+
+ // Some RR types do not really work well with a wildcard. Even
+ // though the protocol specifically doesn't completely ban such
+ // usage, we refuse to load a zone containing such RR in order to
+ // keep the lookup logic simpler and more predictable. See RFC4592
+ // and (for DNAME) RFC6672 for more technical background. Note also
+ // that BIND 9 refuses NS at a wildcard, so in that sense we simply
+ // provide compatible behavior.
+ if (rrset->getName().isWildcard()) {
+ if (rrset->getType() == RRType::NS()) {
+ LOG_ERROR(logger, DATASRC_MEMORY_MEM_WILDCARD_NS).
+ arg(rrset->getName());
+ isc_throw(AddError, "Invalid NS owner name (wildcard): "
+ << rrset->getName());
+ }
+
+ if (rrset->getType() == RRType::DNAME()) {
+ LOG_ERROR(logger, DATASRC_MEMORY_MEM_WILDCARD_DNAME).
+ arg(rrset->getName());
+ isc_throw(AddError, "Invalid DNAME owner name (wildcard): "
+ << rrset->getName());
+ }
+ }
+
+ // Owner names of NSEC3 have special format as defined in RFC5155,
+ // and cannot be a wildcard name or must be one label longer than
+ // the zone origin. While the RFC doesn't prohibit other forms of
+ // names, no sane zone would have such names for NSEC3. BIND 9 also
+ // refuses NSEC3 at wildcard.
+ if (rrset->getType() == RRType::NSEC3() &&
+ (rrset->getName().isWildcard() ||
+ rrset->getName().getLabelCount() != zone_name_.getLabelCount() + 1))
+ {
+ LOG_ERROR(logger, DATASRC_MEMORY_BAD_NSEC3_NAME).arg(rrset->getName());
+ isc_throw(AddError, "Invalid NSEC3 owner name: " <<
+ rrset->getName() << "; zone: " << zone_name_);
+ }
+}
+
+const NSEC3Hash*
+ZoneDataUpdater::getNSEC3Hash() {
+ if (hash_ == NULL) {
+ NSEC3Data* nsec3_data = zone_data_.getNSEC3Data();
+ // This should never be NULL in this codepath.
+ assert(nsec3_data != NULL);
+
+ hash_ = NSEC3Hash::create(nsec3_data->hashalg,
+ nsec3_data->iterations,
+ nsec3_data->getSaltData(),
+ nsec3_data->getSaltLen());
+ }
+
+ return (hash_);
+}
+
+template <typename T>
+void
+ZoneDataUpdater::setupNSEC3(const ConstRRsetPtr rrset) {
+ // We know rrset has exactly one RDATA
+ const T& nsec3_rdata =
+ dynamic_cast<const T&>(
+ rrset->getRdataIterator()->getCurrent());
+
+ NSEC3Data* nsec3_data = zone_data_.getNSEC3Data();
+ if (nsec3_data == NULL) {
+ nsec3_data = NSEC3Data::create(mem_sgmt_, nsec3_rdata);
+ zone_data_.setNSEC3Data(nsec3_data);
+ zone_data_.setSigned(true);
+ } else {
+ const NSEC3Hash* hash = getNSEC3Hash();
+ if (!hash->match(nsec3_rdata)) {
+ isc_throw(AddError,
+ rrset->getType() << " with inconsistent parameters: "
+ << rrset->toText());
+ }
+ }
+}
+
+void
+ZoneDataUpdater::addNSEC3(const ConstRRsetPtr rrset, const ConstRRsetPtr rrsig)
+{
+ setupNSEC3<generic::NSEC3>(rrset);
+
+ NSEC3Data* nsec3_data = zone_data_.getNSEC3Data();
+
+ ZoneNode* node;
+ nsec3_data->insertName(mem_sgmt_, rrset->getName(), &node);
+
+ RdataSet* rdataset = RdataSet::create(mem_sgmt_, encoder_, rrset, rrsig);
+ RdataSet* old_rdataset = node->setData(rdataset);
+ if (old_rdataset != NULL) {
+ RdataSet::destroy(mem_sgmt_, rrclass_, old_rdataset);
+ }
+}
+
+void
+ZoneDataUpdater::addRdataSet(const ConstRRsetPtr rrset,
+ const ConstRRsetPtr rrsig)
+{
+ if (rrset->getType() == RRType::NSEC3()) {
+ addNSEC3(rrset, rrsig);
+ } else {
+ ZoneNode* node;
+ zone_data_.insertName(mem_sgmt_, rrset->getName(), &node);
+
+ RdataSet* rdataset_head = node->getData();
+
+ // Checks related to the surrounding data. Note: when the check
+ // fails and the exception is thrown, it may break strong
+ // exception guarantee. At the moment we prefer code simplicity
+ // and don't bother to introduce complicated recovery code.
+ contextCheck(*rrset, rdataset_head);
+
+ if (RdataSet::find(rdataset_head, rrset->getType()) != NULL) {
+ isc_throw(AddError,
+ "RRset of the type already exists: "
+ << rrset->getName() << " (type: "
+ << rrset->getType() << ")");
+ }
+
+ RdataSet* rdataset_new = RdataSet::create(mem_sgmt_, encoder_,
+ rrset, rrsig);
+ rdataset_new->next = rdataset_head;
+ node->setData(rdataset_new);
+
+ // Ok, we just put it in.
+
+ // If this RRset creates a zone cut at this node, mark the node
+ // indicating the need for callback in find().
+ if (rrset->getType() == RRType::NS() &&
+ rrset->getName() != zone_name_) {
+ node->setFlag(ZoneNode::FLAG_CALLBACK);
+ // If it is DNAME, we have a callback as well here
+ } else if (rrset->getType() == RRType::DNAME()) {
+ node->setFlag(ZoneNode::FLAG_CALLBACK);
+ }
+
+ // If we've added NSEC3PARAM at zone origin, set up NSEC3
+ // specific data or check consistency with already set up
+ // parameters.
+ if (rrset->getType() == RRType::NSEC3PARAM() &&
+ rrset->getName() == zone_name_) {
+ setupNSEC3<generic::NSEC3PARAM>(rrset);
+ } else if (rrset->getType() == RRType::NSEC()) {
+ // If it is NSEC signed zone, we mark the zone as signed
+ // (conceptually "signed" is a broader notion but our
+ // current zone finder implementation regards "signed" as
+ // "NSEC signed")
+ zone_data_.setSigned(true);
+ }
+ }
+}
+
+void
+ZoneDataUpdater::add(const ConstRRsetPtr& rrset,
+ const ConstRRsetPtr& sig_rrset)
+{
+ // Validate input. This will cause an exception to be thrown if the
+ // input RRset is empty.
+ validate(rrset);
+ if (sig_rrset) {
+ validate(sig_rrset);
+ }
+
+ // OK, can add the RRset.
+ LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEMORY_MEM_ADD_RRSET).
+ arg(rrset->getName()).arg(rrset->getType()).arg(zone_name_);
+
+ // Add wildcards possibly contained in the owner name to the domain
+ // tree. This can only happen for the normal (non-NSEC3) tree.
+ // Note: this can throw an exception, breaking strong exception
+ // guarantee. (see also the note for the call to contextCheck()
+ // above).
+ if (rrset->getType() != RRType::NSEC3()) {
+ addWildcards(rrset->getName());
+ }
+
+ addRdataSet(rrset, sig_rrset);
+}
+
+} // namespace memory
+} // namespace datasrc
+} // namespace isc
diff --git a/src/lib/datasrc/memory/zone_data_updater.h b/src/lib/datasrc/memory/zone_data_updater.h
new file mode 100644
index 0000000..341d8ae
--- /dev/null
+++ b/src/lib/datasrc/memory/zone_data_updater.h
@@ -0,0 +1,180 @@
+// Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef DATASRC_ZONE_DATA_UPDATER_H
+#define DATASRC_ZONE_DATA_UPDATER_H 1
+
+#include <datasrc/memory/zone_data.h>
+#include <datasrc/memory/rdata_serialization.h>
+#include <dns/name.h>
+#include <dns/rrclass.h>
+#include <dns/rrset.h>
+#include <dns/nsec3hash.h>
+#include <util/memory_segment.h>
+
+#include <boost/noncopyable.hpp>
+
+namespace isc {
+namespace datasrc {
+namespace memory {
+
+/// \brief A helper class to add records to a zone.
+///
+/// This class provides an \c add() method that can be used to add
+/// RRsets to a ZoneData instance. The RRsets are first validated for
+/// correctness and consistency, and their data is made into RdataSets
+/// which are added to the ZoneData for the zone.
+///
+/// The way to use this is to make a ZoneDataUpdater instance, and call
+/// add() on it as follows:
+///
+/// \code
+/// ZoneDataUpdater updater(mem_sgmt, rrclass, zone_origin_name, zone_data);
+/// ConstRRsetPtr rrset;
+/// updater.add(rrset, ConstRRsetPtr());
+/// \endcode
+///
+/// We enforce that instances are non-copyable as it's pointless to make
+/// copies.
+class ZoneDataUpdater : boost::noncopyable {
+public:
+ ///
+ /// \name Constructors and Destructor.
+ ///
+ //@{
+
+ /// The constructor.
+ ///
+ /// \throw none
+ ///
+ /// \param mem_sgmt The memory segment used for the zone data.
+ /// \param rrclass The RRclass of the zone data.
+ /// \param zone_name The Name of the zone under which records will be
+ /// added.
+ // \param zone_data The ZoneData object which is populated with
+ // record data.
+ ZoneDataUpdater(util::MemorySegment& mem_sgmt,
+ isc::dns::RRClass rrclass,
+ const isc::dns::Name& zone_name,
+ ZoneData& zone_data) :
+ mem_sgmt_(mem_sgmt),
+ rrclass_(rrclass),
+ zone_name_(zone_name),
+ zone_data_(zone_data),
+ hash_(NULL)
+ {}
+
+ /// The destructor.
+ ~ZoneDataUpdater() {
+ delete hash_;
+ }
+
+ //@}
+
+ /// This is thrown if the provided RRset parameter passed to \c
+ /// add() is NULL.
+ struct NullRRset : public InvalidParameter {
+ NullRRset(const char* file, size_t line, const char* what) :
+ InvalidParameter(file, line, what)
+ {}
+ };
+
+ /// \brief General failure exception for \c add().
+ ///
+ /// This is thrown against general error cases in adding an RRset
+ /// to the zone.
+ ///
+ /// Note: this exception would cover cases for \c OutOfZone or
+ /// \c NullRRset. We'll need to clarify and unify the granularity
+ /// of exceptions eventually. For now, exceptions are added as
+ /// developers see the need for it.
+ struct AddError : public InvalidParameter {
+ AddError(const char* file, size_t line, const char* what) :
+ InvalidParameter(file, line, what)
+ {}
+ };
+
+ /// \brief Add an RRset to the zone.
+ ///
+ /// This is the core method of this class. It is used to add an
+ /// RRset to the ZoneData associated with this object. The RRset is
+ /// first validated for correctness and consistency with the rest of
+ /// the records in the zone, and then an RdataSet is created and
+ /// populated with the record data and added to the ZoneData for the
+ /// name in the RRset.
+ ///
+ /// This method throws an \c NullRRset exception (see above) if
+ /// \c rrset is empty. It throws \c AddError if any of a variety of
+ /// validation checks fail for the \c rrset and its associated
+ /// \c sig_rrset.
+ ///
+ /// \param rrset The RRset to be added.
+ /// \param sig_rrset An associated RRSIG RRset for the \c rrset. It
+ /// can be empty if there is no RRSIG for the \c rrset.
+ void add(const isc::dns::ConstRRsetPtr& rrset,
+ const isc::dns::ConstRRsetPtr& sig_rrset);
+
+private:
+ // Add the necessary magic for any wildcard contained in 'name'
+ // (including itself) to be found in the zone.
+ //
+ // In order for wildcard matching to work correctly in the zone finder,
+ // we must ensure that a node for the wildcarding level exists in the
+ // backend ZoneTree.
+ // E.g. if the wildcard name is "*.sub.example." then we must ensure
+ // that "sub.example." exists and is marked as a wildcard level.
+ // Note: the "wildcarding level" is for the parent name of the wildcard
+ // name (such as "sub.example.").
+ //
+ // We also perform the same trick for empty wild card names possibly
+ // contained in 'name' (e.g., '*.foo.example' in 'bar.*.foo.example').
+ void addWildcards(const isc::dns::Name& name);
+
+ // Does some checks in context of the data that are already in the
+ // zone. Currently checks for forbidden combinations of RRsets in
+ // the same domain (CNAME+anything, DNAME+NS). If such condition is
+ // found, it throws AddError.
+ void contextCheck(const isc::dns::AbstractRRset& rrset,
+ const RdataSet* set) const;
+
+ // Validate rrset before adding it to the zone. If something is wrong
+ // it throws an exception. It doesn't modify the zone, and provides
+ // the strong exception guarantee.
+ void validate(const isc::dns::ConstRRsetPtr rrset) const;
+
+ const isc::dns::NSEC3Hash* getNSEC3Hash();
+ template <typename T>
+ void setupNSEC3(const isc::dns::ConstRRsetPtr rrset);
+ void addNSEC3(const isc::dns::ConstRRsetPtr rrset,
+ const isc::dns::ConstRRsetPtr rrsig);
+ void addRdataSet(const isc::dns::ConstRRsetPtr rrset,
+ const isc::dns::ConstRRsetPtr rrsig);
+
+ util::MemorySegment& mem_sgmt_;
+ const isc::dns::RRClass rrclass_;
+ const isc::dns::Name& zone_name_;
+ ZoneData& zone_data_;
+ RdataEncoder encoder_;
+ const isc::dns::NSEC3Hash* hash_;
+};
+
+} // namespace memory
+} // namespace datasrc
+} // namespace isc
+
+#endif // DATASRC_ZONE_DATA_UPDATER_H
+
+// Local Variables:
+// mode: c++
+// End:
diff --git a/src/lib/datasrc/memory/zone_finder.cc b/src/lib/datasrc/memory/zone_finder.cc
index c318f70..11188a0 100644
--- a/src/lib/datasrc/memory/zone_finder.cc
+++ b/src/lib/datasrc/memory/zone_finder.cc
@@ -428,7 +428,7 @@ FindNodeResult findNode(const ZoneData& zone_data,
ZoneFinder::FindOptions options,
bool out_of_zone_ok = false)
{
- ZoneNode* node = NULL;
+ const ZoneNode* node = NULL;
FindState state((options & ZoneFinder::FIND_GLUE_OK) != 0);
const ZoneTree& tree(zone_data.getZoneTree());
@@ -534,17 +534,26 @@ FindNodeResult findNode(const ZoneData& zone_data,
/// context.
class InMemoryZoneFinder::Context : public ZoneFinder::Context {
public:
- Context(ZoneFinder& finder, ZoneFinder::FindOptions options,
+ Context(InMemoryZoneFinder& finder, ZoneFinder::FindOptions options,
const RRClass& rrclass, const ZoneFinderResultContext& result) :
- ZoneFinder::Context(finder, options,
- ResultContext(result.code, result.rrset,
- result.flags)),
+ ZoneFinder::Context(options, ResultContext(result.code, result.rrset,
+ result.flags)),
+ finder_(finder), // NOTE: when entire #2283 is done we won't need this
rrclass_(rrclass), zone_data_(result.zone_data),
found_node_(result.found_node),
found_rdset_(result.found_rdset)
{}
protected:
+ // When all tickets in #2283 are done this can simply return NULL.
+ virtual ZoneFinder* getFinder() { return (&finder_); }
+
+ // We don't use the default protected methods that rely on this method,
+ // so we can simply return NULL.
+ virtual const std::vector<isc::dns::ConstRRsetPtr>* getAllRRsets() const {
+ return (NULL);
+ }
+
virtual void getAdditionalImpl(const std::vector<RRType>& requested_types,
std::vector<ConstRRsetPtr>& result)
{
@@ -626,6 +635,7 @@ private:
}
private:
+ InMemoryZoneFinder& finder_;
const RRClass rrclass_;
const ZoneData* const zone_data_;
const ZoneNode* const found_node_;
diff --git a/src/lib/datasrc/memory/zone_table.cc b/src/lib/datasrc/memory/zone_table.cc
index a74a61d..836b020 100644
--- a/src/lib/datasrc/memory/zone_table.cc
+++ b/src/lib/datasrc/memory/zone_table.cc
@@ -69,17 +69,13 @@ ZoneTable::destroy(util::MemorySegment& mem_sgmt, ZoneTable* ztable,
ZoneTable::AddResult
ZoneTable::addZone(util::MemorySegment& mem_sgmt, RRClass zone_class,
- const Name& zone_name)
+ const Name& zone_name, ZoneData* content)
{
- // Create a new ZoneData instance first. If the specified name already
- // exists in the table, the new data will soon be destroyed, but we want
- // to make sure if this allocation fails the tree won't be changed to
- // provide as strong guarantee as possible. In practice, we generally
- // expect the caller tries to add a zone only when it's a new one, so
- // this should be a minor concern.
- SegmentObjectHolder<ZoneData, RRClass> holder(
- mem_sgmt, ZoneData::create(mem_sgmt, zone_name), zone_class);
-
+ if (content == NULL) {
+ isc_throw(isc::BadValue, "Zone content must not be NULL");
+ }
+ SegmentObjectHolder<ZoneData, RRClass> holder(mem_sgmt, content,
+ zone_class);
// Get the node where we put the zone
ZoneTableNode* node(NULL);
switch (zones_->insert(mem_sgmt, zone_name, &node)) {
@@ -94,18 +90,18 @@ ZoneTable::addZone(util::MemorySegment& mem_sgmt, RRClass zone_class,
// Can Not Happen
assert(node != NULL);
- // Is it empty? We either just created it or it might be nonterminal
- if (node->isEmpty()) {
- node->setData(holder.get());
- return (AddResult(result::SUCCESS, holder.release()));
- } else { // There's something there already
- return (AddResult(result::EXIST, node->getData()));
+ // We can release now, setData never throws
+ ZoneData* old = node->setData(holder.release());
+ if (old != NULL) {
+ return (AddResult(result::EXIST, old));
+ } else {
+ return (AddResult(result::SUCCESS, NULL));
}
}
ZoneTable::FindResult
ZoneTable::findZone(const Name& name) const {
- ZoneTableNode* node(NULL);
+ const ZoneTableNode* node(NULL);
result::Result my_result;
// Translate the return codes
@@ -132,20 +128,6 @@ ZoneTable::findZone(const Name& name) const {
return (FindResult(my_result, node->getData()));
}
-ZoneTable::FindResult
-ZoneTable::setZoneData(const Name& name, ZoneData* data)
-{
- ZoneTableNode* node(NULL);
-
- ZoneTableTree::Result result(zones_->find(name, &node));
-
- if (result != ZoneTableTree::EXACTMATCH) {
- return (FindResult(result::NOTFOUND, NULL));
- } else {
- return (FindResult(result::SUCCESS, node->setData(data)));
- }
-}
-
} // end of namespace memory
} // end of namespace datasrc
} // end of namespace isc
diff --git a/src/lib/datasrc/memory/zone_table.h b/src/lib/datasrc/memory/zone_table.h
index 8ad6213..024558e 100644
--- a/src/lib/datasrc/memory/zone_table.h
+++ b/src/lib/datasrc/memory/zone_table.h
@@ -74,23 +74,23 @@ private:
typedef DomainTreeNode<ZoneData> ZoneTableNode;
public:
- /// \brief Result data of addZone() method.
- struct AddResult {
- AddResult(result::Result param_code, ZoneData* param_zone_data) :
- code(param_code), zone_data(param_zone_data)
- {}
- const result::Result code;
- ZoneData* const zone_data;
- };
+ /// \brief Result data of addZone() method.
+ struct AddResult {
+ AddResult(result::Result param_code, ZoneData* param_zone_data) :
+ code(param_code), zone_data(param_zone_data)
+ {}
+ const result::Result code;
+ ZoneData* const zone_data;
+ };
/// \brief Result data of findZone() method.
struct FindResult {
FindResult(result::Result param_code,
- ZoneData* param_zone_data) :
+ const ZoneData* param_zone_data) :
code(param_code), zone_data(param_zone_data)
{}
const result::Result code;
- ZoneData* const zone_data;
+ const ZoneData* const zone_data;
};
private:
@@ -140,30 +140,29 @@ public:
/// Add a new zone to the \c ZoneTable.
///
- /// This method creates a new \c ZoneData for the given zone name and
- /// holds it in the internal table. The newly created zone data will be
- /// returned via the \c zone_data member of the return value. If the given
- /// zone name already exists in the table, a new data object won't be
- /// created; instead, the existing corresponding data will be returned.
- ///
- /// The zone table keeps the ownership of the created zone data; the
- /// caller must not try to destroy it directly. (We'll eventually
- /// add an interface to delete specific zone data from the table).
+ /// This method adds a given zone data to the internal table.
///
/// \throw std::bad_alloc Internal resource allocation fails.
///
/// \param mem_sgmt The \c MemorySegment to allocate zone data to be
- /// created. It must be the same segment that was used to create
- /// the zone table at the time of create().
+ /// created. It must be the same segment that was used to create
+ /// the zone table at the time of create().
/// \param zone_name The name of the zone to be added.
/// \param zone_class The RR class of the zone. It must be the RR class
- /// that is supposed to be associated to the zone table.
+ /// that is supposed to be associated to the zone table.
+ /// \param content This one should hold the zone content (the ZoneData).
+ /// The ownership is passed onto the zone table. Must not be null.
+ /// Must correspond to the name and class and must be allocated from
+ /// mem_sgmt.
/// \return \c result::SUCCESS If the zone is successfully
- /// added to the zone table.
- /// \return \c result::EXIST The zone table already contains
- /// zone of the same origin.
- AddResult addZone(util::MemorySegment& mem_sgmt, dns::RRClass zone_class,
- const dns::Name& zone_name);
+ /// added to the zone table.
+ /// \return \c result::EXIST The zone table already contained
+ /// zone of the same origin. The old data is replaced and returned
+ /// inside the result.
+ AddResult addZone(util::MemorySegment& mem_sgmt,
+ dns::RRClass zone_class,
+ const dns::Name& zone_name,
+ ZoneData* content);
/// Find a zone that best matches the given name in the \c ZoneTable.
///
@@ -185,16 +184,6 @@ public:
/// \return A \c FindResult object enclosing the search result (see above).
FindResult findZone(const isc::dns::Name& name) const;
- /// Override the ZoneData for a node (zone) in the zone tree.
- ///
- /// \throw none
- ///
- /// \param name A domain name for which the zone data is set.
- /// \param data The new zone data to set.
- /// \return A \c FindResult object containing the old data if the
- /// zone was found.
- FindResult setZoneData(const isc::dns::Name& name, ZoneData* data);
-
private:
boost::interprocess::offset_ptr<ZoneTableTree> zones_;
};
diff --git a/src/lib/datasrc/memory/zone_table_segment.cc b/src/lib/datasrc/memory/zone_table_segment.cc
new file mode 100644
index 0000000..7a80e3c
--- /dev/null
+++ b/src/lib/datasrc/memory/zone_table_segment.cc
@@ -0,0 +1,38 @@
+// Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <datasrc/memory/zone_table_segment.h>
+#include <datasrc/memory/zone_table_segment_local.h>
+
+namespace isc {
+namespace datasrc {
+namespace memory {
+
+ZoneTableSegment*
+ZoneTableSegment::create(const isc::data::Element&) {
+ /// FIXME: For now, we always return ZoneTableSegmentLocal. This
+ /// should be updated eventually to parse the passed Element
+ /// argument and construct a corresponding ZoneTableSegment
+ /// implementation.
+ return (new ZoneTableSegmentLocal);
+}
+
+void
+ZoneTableSegment::destroy(ZoneTableSegment *segment) {
+ delete segment;
+}
+
+} // namespace memory
+} // namespace datasrc
+} // namespace isc
diff --git a/src/lib/datasrc/memory/zone_table_segment.h b/src/lib/datasrc/memory/zone_table_segment.h
new file mode 100644
index 0000000..7fd1310
--- /dev/null
+++ b/src/lib/datasrc/memory/zone_table_segment.h
@@ -0,0 +1,110 @@
+// Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef __ZONE_TABLE_SEGMENT_H__
+#define __ZONE_TABLE_SEGMENT_H__
+
+#include <datasrc/memory/zone_table.h>
+#include <cc/data.h>
+#include <util/memory_segment.h>
+
+#include <boost/interprocess/offset_ptr.hpp>
+
+#include <stdlib.h>
+
+namespace isc {
+namespace datasrc {
+namespace memory {
+
+/// \brief Memory-management independent entry point that contains a
+/// pointer to a zone table in memory.
+///
+/// An instance of this type lives inside a ZoneTableSegment
+/// implementation. It contains an offset pointer to the zone table (a
+/// map from domain names to zone locators) in memory.
+struct ZoneTableHeader {
+public:
+ /// \brief Returns a pointer to the underlying zone table.
+ ZoneTable* getTable() {
+ return (table.get());
+ }
+
+ /// \brief const version of \c getTable().
+ const ZoneTable* getTable() const {
+ return (table.get());
+ }
+
+private:
+ boost::interprocess::offset_ptr<ZoneTable> table;
+};
+
+/// \brief Manages a ZoneTableHeader, an entry point into a table of
+/// zones
+///
+/// This class specifies an interface for derived implementations which
+/// return a pointer to an object of type ZoneTableHeader, an entry
+/// point into a table of zones regardless of the underlying memory
+/// management implementation. Derived classes would implement the
+/// interface for specific memory-implementation behavior.
+class ZoneTableSegment {
+protected:
+ /// \brief Protected constructor
+ ///
+ /// An instance implementing this interface is expected to be
+ /// created by the factory method (\c create()), so this constructor
+ /// is protected.
+ ZoneTableSegment()
+ {}
+public:
+ /// \brief Destructor
+ virtual ~ZoneTableSegment() {}
+
+ /// \brief Return the ZoneTableHeader for the zone table segment.
+ virtual ZoneTableHeader& getHeader() = 0;
+
+ /// \brief const version of \c getHeader().
+ virtual const ZoneTableHeader& getHeader() const = 0;
+
+ /// \brief Return the MemorySegment for the zone table segment.
+ virtual isc::util::MemorySegment& getMemorySegment() = 0;
+
+ /// \brief Create an instance depending on the memory segment model
+ ///
+ /// This is a factory method to create a derived ZoneTableSegment
+ /// object based on the \c config passed. The method returns a
+ /// dynamically-allocated object. The caller is responsible for
+ /// destroying it with \c ZoneTableSegment::destroy().
+ ///
+ /// FIXME: For now, we always return ZoneTableSegmentLocal
+ /// regardless of the passed \c config.
+ ///
+ /// \param config The configuration based on which a derived object
+ /// is returned.
+ /// \return Returns a ZoneTableSegment object
+ static ZoneTableSegment* create(const isc::data::Element& config);
+
+ /// \brief Destroy a ZoneTableSegment
+ ///
+ /// This method destroys the passed ZoneTableSegment. It must be
+ /// passed a segment previously created by \c ZoneTableSegment::create().
+ ///
+ /// \param segment The segment to destroy.
+ static void destroy(ZoneTableSegment* segment);
+};
+
+} // namespace memory
+} // namespace datasrc
+} // namespace isc
+
+#endif // __ZONE_TABLE_SEGMENT_H__
diff --git a/src/lib/datasrc/memory/zone_table_segment_local.cc b/src/lib/datasrc/memory/zone_table_segment_local.cc
new file mode 100644
index 0000000..589c9af
--- /dev/null
+++ b/src/lib/datasrc/memory/zone_table_segment_local.cc
@@ -0,0 +1,43 @@
+// Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <datasrc/memory/zone_table_segment_local.h>
+
+using namespace isc::util;
+
+namespace isc {
+namespace datasrc {
+namespace memory {
+
+// After more methods' definitions are added here, it would be a good
+// idea to move getHeader() and getMemorySegment() definitions to the
+// header file.
+ZoneTableHeader&
+ZoneTableSegmentLocal::getHeader() {
+ return (header_);
+}
+
+const ZoneTableHeader&
+ZoneTableSegmentLocal::getHeader() const {
+ return (header_);
+}
+
+MemorySegment&
+ZoneTableSegmentLocal::getMemorySegment() {
+ return (mem_sgmt_);
+}
+
+} // namespace memory
+} // namespace datasrc
+} // namespace isc
diff --git a/src/lib/datasrc/memory/zone_table_segment_local.h b/src/lib/datasrc/memory/zone_table_segment_local.h
new file mode 100644
index 0000000..de776a9
--- /dev/null
+++ b/src/lib/datasrc/memory/zone_table_segment_local.h
@@ -0,0 +1,66 @@
+// Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef __ZONE_TABLE_SEGMENT_LOCAL_H__
+#define __ZONE_TABLE_SEGMENT_LOCAL_H__
+
+#include <datasrc/memory/zone_table_segment.h>
+#include <util/memory_segment_local.h>
+
+namespace isc {
+namespace datasrc {
+namespace memory {
+
+/// \brief Local implementation of ZoneTableSegment class
+///
+/// This class specifies a concrete implementation for a
+/// MemorySegmentLocal based ZoneTableSegment. Please see the
+/// ZoneTableSegment class documentation for usage.
+class ZoneTableSegmentLocal : public ZoneTableSegment {
+ // This is so that ZoneTableSegmentLocal can be instantiated from
+ // ZoneTableSegment::create().
+ friend class ZoneTableSegment;
+protected:
+ /// \brief Protected constructor
+ ///
+ /// Instances are expected to be created by the factory method
+ /// (\c ZoneTableSegment::create()), so this constructor is
+ /// protected.
+ ZoneTableSegmentLocal()
+ {}
+public:
+ /// \brief Destructor
+ virtual ~ZoneTableSegmentLocal() {}
+
+ /// \brief Return the ZoneTableHeader for the local zone table
+ /// segment implementation.
+ virtual ZoneTableHeader& getHeader();
+
+ /// \brief const version of \c getHeader().
+ virtual const ZoneTableHeader& getHeader() const;
+
+ /// \brief Return the MemorySegment for the local zone table segment
+ /// implementation (a MemorySegmentLocal instance).
+ virtual isc::util::MemorySegment& getMemorySegment();
+
+private:
+ ZoneTableHeader header_;
+ isc::util::MemorySegmentLocal mem_sgmt_;
+};
+
+} // namespace memory
+} // namespace datasrc
+} // namespace isc
+
+#endif // __ZONE_TABLE_SEGMENT_LOCAL_H__
diff --git a/src/lib/datasrc/memory_datasrc.cc b/src/lib/datasrc/memory_datasrc.cc
index e38a487..686dd94 100644
--- a/src/lib/datasrc/memory_datasrc.cc
+++ b/src/lib/datasrc/memory_datasrc.cc
@@ -20,7 +20,6 @@
#include <dns/nsec3hash.h>
#include <dns/rdataclass.h>
#include <dns/rrclass.h>
-#include <dns/rrsetlist.h>
#include <dns/masterload.h>
#include <datasrc/memory_datasrc.h>
@@ -792,13 +791,19 @@ public:
/// context.
Context(ZoneFinder& finder, ZoneFinder::FindOptions options,
const RBNodeResultContext& result) :
- ZoneFinder::Context(finder, options,
+ ZoneFinder::Context(options,
ResultContext(result.code, result.rrset,
result.flags)),
- rrset_(result.rrset), found_node_(result.found_node)
+ finder_(finder), rrset_(result.rrset), found_node_(result.found_node)
{}
protected:
+ virtual ZoneFinder* getFinder() { return (&finder_); }
+
+ virtual const std::vector<isc::dns::ConstRRsetPtr>* getAllRRsets() const {
+ return (NULL);
+ }
+
virtual void getAdditionalImpl(const vector<RRType>& requested_types,
vector<ConstRRsetPtr>& result)
{
@@ -866,6 +871,7 @@ private:
}
}
+ ZoneFinder& finder_;
const ConstRBNodeRRsetPtr rrset_;
const DomainNode* const found_node_;
};
diff --git a/src/lib/datasrc/memory_datasrc_link.cc b/src/lib/datasrc/memory_datasrc_link.cc
index cbbc6db..857223f 100644
--- a/src/lib/datasrc/memory_datasrc_link.cc
+++ b/src/lib/datasrc/memory_datasrc_link.cc
@@ -129,7 +129,7 @@ checkConfig(ConstElementPtr config, ElementPtr errors) {
result = false;
} else {
try {
- RRClass rrc(config->get("class")->stringValue());
+ RRClass(config->get("class")->stringValue());
} catch (const isc::Exception& rrce) {
addError(errors,
"Error parsing class config for memory backend: " +
diff --git a/src/lib/datasrc/sqlite3_accessor.cc b/src/lib/datasrc/sqlite3_accessor.cc
index 672121e..68d6554 100644
--- a/src/lib/datasrc/sqlite3_accessor.cc
+++ b/src/lib/datasrc/sqlite3_accessor.cc
@@ -44,7 +44,7 @@ namespace {
// program may not be taking advantage of features (possibly performance
// improvements) added to the database.
const int SQLITE_SCHEMA_MAJOR_VERSION = 2;
-const int SQLITE_SCHEMA_MINOR_VERSION = 0;
+const int SQLITE_SCHEMA_MINOR_VERSION = 1;
}
namespace isc {
@@ -65,19 +65,20 @@ enum StatementID {
DEL_ZONE_RECORDS = 6,
ADD_RECORD = 7,
DEL_RECORD = 8,
- ITERATE = 9,
- FIND_PREVIOUS = 10,
- ADD_RECORD_DIFF = 11,
- LOW_DIFF_ID = 12,
- HIGH_DIFF_ID = 13,
- DIFF_RECS = 14,
- NSEC3 = 15,
- NSEC3_PREVIOUS = 16,
- NSEC3_LAST = 17,
- ADD_NSEC3_RECORD = 18,
- DEL_ZONE_NSEC3_RECORDS = 19,
- DEL_NSEC3_RECORD = 20,
- NUM_STATEMENTS = 21
+ ITERATE_RECORDS = 9,
+ ITERATE_NSEC3 = 10,
+ FIND_PREVIOUS = 11,
+ ADD_RECORD_DIFF = 12,
+ LOW_DIFF_ID = 13,
+ HIGH_DIFF_ID = 14,
+ DIFF_RECS = 15,
+ NSEC3 = 16,
+ NSEC3_PREVIOUS = 17,
+ NSEC3_LAST = 18,
+ ADD_NSEC3_RECORD = 19,
+ DEL_ZONE_NSEC3_RECORDS = 20,
+ DEL_NSEC3_RECORD = 21,
+ NUM_STATEMENTS = 22
};
const char* const text_statements[NUM_STATEMENTS] = {
@@ -102,19 +103,17 @@ const char* const text_statements[NUM_STATEMENTS] = {
"VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7)",
"DELETE FROM records WHERE zone_id=?1 AND name=?2 " // DEL_RECORD
"AND rdtype=?3 AND rdata=?4",
- // The following iterates the whole zone. As the NSEC3 records
- // (and corresponding RRSIGs) live in separate table, we need to
- // take both of them. As the RRSIGs are for NSEC3s in the other
- // table, we can easily hardcode the sigtype.
- //
- // The extra column is so we can order it by rname. This is to
- // preserve the previous order, mostly for tests.
- // TODO: Is it possible to get rid of the ordering?
- "SELECT rdtype, ttl, sigtype, rdata, name, rname FROM records " // ITERATE
- "WHERE zone_id = ?1 "
- "UNION "
- "SELECT rdtype, ttl, \"NSEC3\", rdata, owner, owner FROM nsec3 "
- "WHERE zone_id = ?1 ORDER by rname, rdtype",
+
+ // ITERATE_RECORDS:
+ // The following iterates the whole zone in the records table.
+ "SELECT rdtype, ttl, sigtype, rdata, name FROM records "
+ "WHERE zone_id = ?1 ORDER BY rname, rdtype",
+
+ // ITERATE_NSEC3:
+ // The following iterates the whole zone in the nsec3 table. As the
+ // RRSIGs are for NSEC3s, we can hardcode the sigtype.
+ "SELECT rdtype, ttl, \"NSEC3\", rdata, owner FROM nsec3 "
+ "WHERE zone_id = ?1 ORDER BY hash, 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
@@ -332,7 +331,7 @@ public:
const char* const SCHEMA_LIST[] = {
"CREATE TABLE schema_version (version INTEGER NOT NULL, "
"minor INTEGER NOT NULL DEFAULT 0)",
- "INSERT INTO schema_version VALUES (2, 0)",
+ "INSERT INTO schema_version VALUES (2, 1)",
"CREATE TABLE zones (id INTEGER PRIMARY KEY, "
"name TEXT NOT NULL COLLATE NOCASE, "
"rdclass TEXT NOT NULL COLLATE NOCASE DEFAULT 'IN', "
@@ -358,6 +357,7 @@ const char* const SCHEMA_LIST[] = {
"ttl INTEGER NOT NULL, rdtype TEXT NOT NULL COLLATE NOCASE, "
"rdata TEXT NOT NULL)",
"CREATE INDEX nsec3_byhash ON nsec3 (hash)",
+ "CREATE INDEX nsec3_byhash_and_rdtype ON nsec3 (hash, rdtype)",
"CREATE TABLE diffs (id INTEGER PRIMARY KEY, "
"zone_id INTEGER NOT NULL, "
"version INTEGER NOT NULL, "
@@ -642,11 +642,21 @@ public:
iterator_type_(ITT_ALL),
accessor_(accessor),
statement_(NULL),
+ statement2_(NULL),
+ rc_(SQLITE_OK),
+ rc2_(SQLITE_OK),
name_("")
{
- // We create the statement now and then just keep getting data from it
+ // We create the statements now and then just keep getting data
+ // from them.
statement_ = prepare(accessor->dbparameters_->db_,
- text_statements[ITERATE]);
+ text_statements[ITERATE_NSEC3]);
+ bindZoneId(id);
+
+ std::swap(statement_, statement2_);
+
+ statement_ = prepare(accessor->dbparameters_->db_,
+ text_statements[ITERATE_RECORDS]);
bindZoneId(id);
}
@@ -665,6 +675,9 @@ public:
iterator_type_(qtype == QT_NSEC3 ? ITT_NSEC3 : ITT_NAME),
accessor_(accessor),
statement_(NULL),
+ statement2_(NULL),
+ rc_(SQLITE_OK),
+ rc2_(SQLITE_OK),
name_(name)
{
// Choose the statement text depending on the query type, and
@@ -703,29 +716,35 @@ public:
// If there's another row, get it
// If finalize has been called (e.g. when previous getNext() got
// SQLITE_DONE), directly return false
- if (statement_ == NULL) {
- return false;
- }
- const int rc(sqlite3_step(statement_));
- if (rc == SQLITE_ROW) {
- // For both types, we copy the first four columns
- copyColumn(data, TYPE_COLUMN);
- copyColumn(data, TTL_COLUMN);
- // The NSEC3 lookup does not provide the SIGTYPE, it is not
- // necessary and not contained in the table.
- if (iterator_type_ != ITT_NSEC3) {
- copyColumn(data, SIGTYPE_COLUMN);
+ while (statement_ != NULL) {
+ rc_ = sqlite3_step(statement_);
+ if (rc_ == SQLITE_ROW) {
+ // For both types, we copy the first four columns
+ copyColumn(data, TYPE_COLUMN);
+ copyColumn(data, TTL_COLUMN);
+ // The NSEC3 lookup does not provide the SIGTYPE, it is not
+ // necessary and not contained in the table.
+ if (iterator_type_ != ITT_NSEC3) {
+ copyColumn(data, SIGTYPE_COLUMN);
+ }
+ copyColumn(data, RDATA_COLUMN);
+ // Only copy Name if we are iterating over every record
+ if (iterator_type_ == ITT_ALL) {
+ copyColumn(data, NAME_COLUMN);
+ }
+ return (true);
+ } else if (rc_ != SQLITE_DONE) {
+ isc_throw(DataSourceError,
+ "Unexpected failure in sqlite3_step: " <<
+ sqlite3_errmsg(accessor_->dbparameters_->db_));
}
- copyColumn(data, RDATA_COLUMN);
- // Only copy Name if we are iterating over every record
- if (iterator_type_ == ITT_ALL) {
- copyColumn(data, NAME_COLUMN);
+ // We are done with statement_. If statement2_ has not been
+ // used yet, try that one now.
+ if ((statement2_ == NULL) || (rc2_ != SQLITE_OK)) {
+ break;
}
- return (true);
- } else if (rc != SQLITE_DONE) {
- isc_throw(DataSourceError,
- "Unexpected failure in sqlite3_step: " <<
- sqlite3_errmsg(accessor_->dbparameters_->db_));
+ std::swap(statement_, statement2_);
+ std::swap(rc_, rc2_);
}
finalize();
return (false);
@@ -771,13 +790,22 @@ private:
}
void finalize() {
- sqlite3_finalize(statement_);
- statement_ = NULL;
+ if (statement_ != NULL) {
+ sqlite3_finalize(statement_);
+ statement_ = NULL;
+ }
+ if (statement2_ != NULL) {
+ sqlite3_finalize(statement2_);
+ statement2_ = NULL;
+ }
}
const IteratorType iterator_type_;
boost::shared_ptr<const SQLite3Accessor> accessor_;
sqlite3_stmt* statement_;
+ sqlite3_stmt* statement2_;
+ int rc_;
+ int rc2_;
const std::string name_;
};
diff --git a/src/lib/datasrc/tests/memory/Makefile.am b/src/lib/datasrc/tests/memory/Makefile.am
index 00d5594..37e9043 100644
--- a/src/lib/datasrc/tests/memory/Makefile.am
+++ b/src/lib/datasrc/tests/memory/Makefile.am
@@ -32,12 +32,14 @@ run_unittests_SOURCES += ../../tests/faked_nsec3.h ../../tests/faked_nsec3.cc
run_unittests_SOURCES += memory_segment_test.h
run_unittests_SOURCES += segment_object_holder_unittest.cc
run_unittests_SOURCES += memory_client_unittest.cc
+run_unittests_SOURCES += zone_table_segment_unittest.cc
run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
run_unittests_LDADD = $(top_builddir)/src/lib/datasrc/libb10-datasrc.la
run_unittests_LDADD += $(top_builddir)/src/lib/dns/libb10-dns++.la
+run_unittests_LDADD += $(top_builddir)/src/lib/cc/libb10-cc.la
run_unittests_LDADD += $(top_builddir)/src/lib/util/unittests/libutil_unittests.la
run_unittests_LDADD += $(top_builddir)/src/lib/util/libb10-util.la
run_unittests_LDADD += $(top_builddir)/src/lib/testutils/libb10-testutils.la
diff --git a/src/lib/datasrc/tests/memory/domaintree_unittest.cc b/src/lib/datasrc/tests/memory/domaintree_unittest.cc
index cb16e02..45e256a 100644
--- a/src/lib/datasrc/tests/memory/domaintree_unittest.cc
+++ b/src/lib/datasrc/tests/memory/domaintree_unittest.cc
@@ -256,8 +256,8 @@ TEST_F(DomainTreeTest, subTreeRoot) {
// "g.h" is not a subtree root
EXPECT_EQ(TestDomainTree::EXACTMATCH,
- dtree_expose_empty_node.find(Name("g.h"), &dtnode));
- EXPECT_FALSE(dtnode->getFlag(TestDomainTreeNode::FLAG_SUBTREE_ROOT));
+ dtree_expose_empty_node.find(Name("g.h"), &cdtnode));
+ EXPECT_FALSE(cdtnode->getFlag(TestDomainTreeNode::FLAG_SUBTREE_ROOT));
// fission the node "g.h"
EXPECT_EQ(TestDomainTree::ALREADYEXISTS,
@@ -270,8 +270,8 @@ TEST_F(DomainTreeTest, subTreeRoot) {
// "g.h" should be a subtree root now.
EXPECT_EQ(TestDomainTree::EXACTMATCH,
- dtree_expose_empty_node.find(Name("g.h"), &dtnode));
- EXPECT_TRUE(dtnode->getFlag(TestDomainTreeNode::FLAG_SUBTREE_ROOT));
+ dtree_expose_empty_node.find(Name("g.h"), &cdtnode));
+ EXPECT_TRUE(cdtnode->getFlag(TestDomainTreeNode::FLAG_SUBTREE_ROOT));
}
TEST_F(DomainTreeTest, additionalNodeFission) {
@@ -286,8 +286,8 @@ TEST_F(DomainTreeTest, additionalNodeFission) {
// "t.0" is not a subtree root
EXPECT_EQ(TestDomainTree::EXACTMATCH,
- dtree_expose_empty_node.find(Name("t.0"), &dtnode));
- EXPECT_FALSE(dtnode->getFlag(TestDomainTreeNode::FLAG_SUBTREE_ROOT));
+ dtree_expose_empty_node.find(Name("t.0"), &cdtnode));
+ EXPECT_FALSE(cdtnode->getFlag(TestDomainTreeNode::FLAG_SUBTREE_ROOT));
// fission the node "t.0"
EXPECT_EQ(TestDomainTree::ALREADYEXISTS,
@@ -300,8 +300,8 @@ TEST_F(DomainTreeTest, additionalNodeFission) {
// "t.0" should be a subtree root now.
EXPECT_EQ(TestDomainTree::EXACTMATCH,
- dtree_expose_empty_node.find(Name("t.0"), &dtnode));
- EXPECT_TRUE(dtnode->getFlag(TestDomainTreeNode::FLAG_SUBTREE_ROOT));
+ dtree_expose_empty_node.find(Name("t.0"), &cdtnode));
+ EXPECT_TRUE(cdtnode->getFlag(TestDomainTreeNode::FLAG_SUBTREE_ROOT));
}
TEST_F(DomainTreeTest, findName) {
@@ -328,10 +328,10 @@ TEST_F(DomainTreeTest, findName) {
EXPECT_EQ(TestDomainTree::PARTIALMATCH,
dtree_expose_empty_node.find(Name("m.d.e.f"), &cdtnode));
- // find dtnode
+ // find cdtnode
EXPECT_EQ(TestDomainTree::EXACTMATCH, dtree.find(Name("q.w.y.d.e.f"),
- &dtnode));
- EXPECT_EQ(Name("q"), dtnode->getName());
+ &cdtnode));
+ EXPECT_EQ(Name("q"), cdtnode->getName());
}
TEST_F(DomainTreeTest, findError) {
@@ -411,11 +411,12 @@ performCallbackTest(TestDomainTree& dtree,
Name("example"),
&parentdtnode));
// the child/parent nodes shouldn't "inherit" the callback flag.
- // "dtnode" may be invalid due to the insertion, so we need to re-find
- // it.
+ // "dtnode" should still validly point to "callback.example", but we
+ // explicitly confirm it.
EXPECT_EQ(TestDomainTree::EXACTMATCH, dtree.find(Name("callback.example"),
- &dtnode));
- EXPECT_TRUE(dtnode->getFlag(TestDomainTreeNode::FLAG_CALLBACK));
+ &cdtnode));
+ ASSERT_EQ(dtnode, cdtnode);
+ EXPECT_TRUE(cdtnode->getFlag(TestDomainTreeNode::FLAG_CALLBACK));
EXPECT_FALSE(subdtnode->getFlag(TestDomainTreeNode::FLAG_CALLBACK));
EXPECT_FALSE(parentdtnode->getFlag(TestDomainTreeNode::FLAG_CALLBACK));
diff --git a/src/lib/datasrc/tests/memory/memory_client_unittest.cc b/src/lib/datasrc/tests/memory/memory_client_unittest.cc
index 58979a4..8785a10 100644
--- a/src/lib/datasrc/tests/memory/memory_client_unittest.cc
+++ b/src/lib/datasrc/tests/memory/memory_client_unittest.cc
@@ -22,7 +22,6 @@
#include <dns/nsec3hash.h>
#include <dns/rdata.h>
#include <dns/rdataclass.h>
-#include <dns/rrsetlist.h>
#include <dns/rrttl.h>
#include <dns/masterload.h>
@@ -30,6 +29,8 @@
#include <datasrc/data_source.h>
#include <datasrc/memory/zone_data.h>
#include <datasrc/memory/zone_table.h>
+#include <datasrc/memory/zone_data_updater.h>
+#include <datasrc/memory/zone_data_loader.h>
#include <datasrc/memory/memory_client.h>
#include <testutils/dnsmessage_test.h>
@@ -45,6 +46,7 @@ using namespace isc::dns::rdata;
using namespace isc::datasrc;
using namespace isc::datasrc::memory;
using namespace isc::testutils;
+using std::vector;
namespace {
@@ -87,8 +89,7 @@ private:
MockIterator(const char** rrset_data_ptr, bool pass_empty_rrsig) :
rrset_data_ptr_(rrset_data_ptr),
pass_empty_rrsig_(pass_empty_rrsig)
- {
- }
+ {}
const char** rrset_data_ptr_;
// If true, emulate an unexpected bogus case where an RRSIG RRset is
@@ -124,6 +125,34 @@ public:
}
};
+class MockVectorIterator : public ZoneIterator {
+private:
+ MockVectorIterator(const vector<ConstRRsetPtr>& rrsets) :
+ rrsets_(rrsets),
+ counter_(0)
+ {}
+
+ const vector<ConstRRsetPtr> rrsets_;
+ int counter_;
+
+public:
+ virtual ConstRRsetPtr getNextRRset() {
+ if (counter_ >= rrsets_.size()) {
+ return (ConstRRsetPtr());
+ }
+
+ return (rrsets_[counter_++]);
+ }
+
+ virtual ConstRRsetPtr getSOA() const {
+ isc_throw(isc::NotImplemented, "Not implemented");
+ }
+
+ static ZoneIteratorPtr makeIterator(const vector<ConstRRsetPtr>& rrsets) {
+ return (ZoneIteratorPtr(new MockVectorIterator(rrsets)));
+ }
+};
+
class MemoryClientTest : public ::testing::Test {
protected:
MemoryClientTest() : zclass_(RRClass::IN()),
@@ -188,7 +217,7 @@ TEST_F(MemoryClientTest, loadEmptyZoneFileThrows) {
EXPECT_THROW(client_->load(Name("."),
TEST_DATA_DIR "/empty.zone"),
- InMemoryClient::EmptyZone);
+ EmptyZone);
EXPECT_EQ(0, client_->getZoneCount());
@@ -242,13 +271,13 @@ TEST_F(MemoryClientTest, loadFromIterator) {
EXPECT_THROW(client_->load(Name("example.org"),
*MockIterator::makeIterator(
rrset_data_separated)),
- InMemoryClient::AddError);
+ ZoneDataUpdater::AddError);
// Similar to the previous case, but with separated RRSIGs.
EXPECT_THROW(client_->load(Name("example.org"),
*MockIterator::makeIterator(
rrset_data_sigseparated)),
- InMemoryClient::AddError);
+ ZoneDataUpdater::AddError);
// Emulating bogus iterator implementation that passes empty RRSIGs.
EXPECT_THROW(client_->load(Name("example.org"),
@@ -260,10 +289,17 @@ TEST_F(MemoryClientTest, loadMemoryAllocationFailures) {
// Just to check that things get cleaned up
for (int i = 1; i < 16; i++) {
+ SCOPED_TRACE("For throw count = " + i);
mem_sgmt_.setThrowCount(i);
- EXPECT_THROW(client_->load(Name("example.org"),
- TEST_DATA_DIR "/example.org.zone"),
- std::bad_alloc);
+ EXPECT_THROW({
+ // Include the InMemoryClient construction too here. Now,
+ // even allocations done from InMemoryClient constructor
+ // fail (due to MemorySegmentTest throwing) and we check for
+ // leaks when this happens.
+ InMemoryClient client2(mem_sgmt_, zclass_);
+ client2.load(Name("example.org"),
+ TEST_DATA_DIR "/example.org.zone");
+ }, std::bad_alloc);
}
// Teardown checks for memory segment leaks
}
@@ -384,7 +420,7 @@ TEST_F(MemoryClientTest, loadDuplicateType) {
EXPECT_THROW(client_->load(Name("example.org"),
TEST_DATA_DIR
"/example.org-duplicate-type-bad.zone"),
- InMemoryClient::AddError);
+ ZoneDataUpdater::AddError);
// Teardown checks for memory segment leaks
}
@@ -393,7 +429,7 @@ TEST_F(MemoryClientTest, loadMultipleCNAMEThrows) {
EXPECT_THROW(client_->load(Name("example.org"),
TEST_DATA_DIR
"/example.org-multiple-cname.zone"),
- InMemoryClient::AddError);
+ ZoneDataUpdater::AddError);
// Teardown checks for memory segment leaks
}
@@ -402,7 +438,7 @@ TEST_F(MemoryClientTest, loadMultipleDNAMEThrows) {
EXPECT_THROW(client_->load(Name("example.org"),
TEST_DATA_DIR
"/example.org-multiple-dname.zone"),
- InMemoryClient::AddError);
+ ZoneDataUpdater::AddError);
// Teardown checks for memory segment leaks
}
@@ -411,7 +447,7 @@ TEST_F(MemoryClientTest, loadMultipleNSEC3Throws) {
EXPECT_THROW(client_->load(Name("example.org"),
TEST_DATA_DIR
"/example.org-multiple-nsec3.zone"),
- InMemoryClient::AddError);
+ ZoneDataUpdater::AddError);
// Teardown checks for memory segment leaks
}
@@ -420,7 +456,7 @@ TEST_F(MemoryClientTest, loadMultipleNSEC3PARAMThrows) {
EXPECT_THROW(client_->load(Name("example.org"),
TEST_DATA_DIR
"/example.org-multiple-nsec3param.zone"),
- InMemoryClient::AddError);
+ ZoneDataUpdater::AddError);
// Teardown checks for memory segment leaks
}
@@ -438,7 +474,7 @@ TEST_F(MemoryClientTest, loadWildcardNSThrows) {
EXPECT_THROW(client_->load(Name("example.org"),
TEST_DATA_DIR
"/example.org-wildcard-ns.zone"),
- InMemoryClient::AddError);
+ ZoneDataUpdater::AddError);
// Teardown checks for memory segment leaks
}
@@ -447,7 +483,7 @@ TEST_F(MemoryClientTest, loadWildcardDNAMEThrows) {
EXPECT_THROW(client_->load(Name("example.org"),
TEST_DATA_DIR
"/example.org-wildcard-dname.zone"),
- InMemoryClient::AddError);
+ ZoneDataUpdater::AddError);
// Teardown checks for memory segment leaks
}
@@ -456,7 +492,7 @@ TEST_F(MemoryClientTest, loadWildcardNSEC3Throws) {
EXPECT_THROW(client_->load(Name("example.org"),
TEST_DATA_DIR
"/example.org-wildcard-nsec3.zone"),
- InMemoryClient::AddError);
+ ZoneDataUpdater::AddError);
// Teardown checks for memory segment leaks
}
@@ -465,7 +501,7 @@ TEST_F(MemoryClientTest, loadNSEC3WithFewerLabelsThrows) {
EXPECT_THROW(client_->load(Name("example.org"),
TEST_DATA_DIR
"/example.org-nsec3-fewer-labels.zone"),
- InMemoryClient::AddError);
+ ZoneDataUpdater::AddError);
// Teardown checks for memory segment leaks
}
@@ -474,7 +510,7 @@ TEST_F(MemoryClientTest, loadNSEC3WithMoreLabelsThrows) {
EXPECT_THROW(client_->load(Name("example.org"),
TEST_DATA_DIR
"/example.org-nsec3-more-labels.zone"),
- InMemoryClient::AddError);
+ ZoneDataUpdater::AddError);
// Teardown checks for memory segment leaks
}
@@ -483,12 +519,12 @@ TEST_F(MemoryClientTest, loadCNAMEAndNotNSECThrows) {
EXPECT_THROW(client_->load(Name("example.org"),
TEST_DATA_DIR
"/example.org-cname-and-not-nsec-1.zone"),
- InMemoryClient::AddError);
+ ZoneDataUpdater::AddError);
EXPECT_THROW(client_->load(Name("example.org"),
TEST_DATA_DIR
"/example.org-cname-and-not-nsec-2.zone"),
- InMemoryClient::AddError);
+ ZoneDataUpdater::AddError);
// Teardown checks for memory segment leaks
}
@@ -514,7 +550,7 @@ TEST_F(MemoryClientTest, loadDNAMEAndNSNonApex1) {
EXPECT_THROW(client_->load(Name("example.org"),
TEST_DATA_DIR
"/example.org-dname-ns-nonapex-1.zone"),
- InMemoryClient::AddError);
+ ZoneDataUpdater::AddError);
// Teardown checks for memory segment leaks
}
@@ -523,7 +559,7 @@ TEST_F(MemoryClientTest, loadDNAMEAndNSNonApex2) {
EXPECT_THROW(client_->load(Name("example.org"),
TEST_DATA_DIR
"/example.org-dname-ns-nonapex-2.zone"),
- InMemoryClient::AddError);
+ ZoneDataUpdater::AddError);
// Teardown checks for memory segment leaks
}
@@ -533,7 +569,7 @@ TEST_F(MemoryClientTest, loadRRSIGFollowsNothing) {
EXPECT_THROW(client_->load(Name("example.org"),
TEST_DATA_DIR
"/example.org-rrsig-follows-nothing.zone"),
- InMemoryClient::AddError);
+ ZoneDataUpdater::AddError);
// Teardown checks for memory segment leaks
}
@@ -544,25 +580,29 @@ TEST_F(MemoryClientTest, loadRRSIGs) {
}
TEST_F(MemoryClientTest, loadRRSIGsRdataMixedCoveredTypes) {
- client_->load(Name("example.org"),
- TEST_DATA_DIR "/example.org-rrsigs.zone");
-
- RRsetPtr rrset(new RRset(Name("example.org"),
- RRClass::IN(), RRType::A(), RRTTL(3600)));
- rrset->addRdata(in::A("192.0.2.1"));
- rrset->addRdata(in::A("192.0.2.2"));
-
- RRsetPtr rrsig(new RRset(Name("example.org"), zclass_,
- RRType::RRSIG(), RRTTL(300)));
- rrsig->addRdata(generic::RRSIG("A 5 3 3600 20000101000000 20000201000000 "
- "12345 example.org. FAKEFAKEFAKE"));
+ vector<ConstRRsetPtr> rrsets_vec;
+
+ rrsets_vec.push_back(textToRRset("example.org. 3600 IN SOA "
+ "ns1.example.org. bugs.x.w.example.org. "
+ "2010012601 3600 300 3600000 1200",
+ zclass_, Name("example.org")));
+ RRsetPtr rrset(textToRRset("example.org. 3600 IN A 192.0.2.1\n"
+ "example.org. 3600 IN A 192.0.2.2\n"));
+ RRsetPtr rrsig(textToRRset("example.org. 300 IN RRSIG "
+ "A 5 3 3600 20000101000000 20000201000000 "
+ "12345 example.org. FAKEFAKEFAKE"));
+ // textToRRset (correctly) consider this RDATA belongs to a different
+ // RRSIG, so we need to manually add it.
rrsig->addRdata(generic::RRSIG("NS 5 3 3600 20000101000000 20000201000000 "
"54321 example.org. FAKEFAKEFAKEFAKE"));
rrset->addRRsig(rrsig);
- EXPECT_THROW(client_->add(Name("example.org"), rrset),
- InMemoryClient::AddError);
+ rrsets_vec.push_back(rrset);
+ EXPECT_THROW(
+ client_->load(Name("example.org"),
+ *MockVectorIterator::makeIterator(rrsets_vec)),
+ ZoneDataUpdater::AddError);
// Teardown checks for memory segment leaks
}
@@ -655,75 +695,22 @@ TEST_F(MemoryClientTest, getIteratorGetSOAThrowsNotImplemented) {
EXPECT_THROW(iterator->getSOA(), isc::NotImplemented);
}
-TEST_F(MemoryClientTest, addRRsetToNonExistentZoneThrows) {
- // The zone "example.org" doesn't exist, so we can't add an RRset to
- // it.
- RRsetPtr rrset_a(new RRset(Name("example.org"), RRClass::IN(), RRType::A(),
- RRTTL(300)));
- rrset_a->addRdata(rdata::in::A("192.0.2.1"));
- EXPECT_THROW(client_->add(Name("example.org"), rrset_a), DataSourceError);
-}
-
-TEST_F(MemoryClientTest, addOutOfZoneThrows) {
- // Out of zone names should throw.
- client_->load(Name("example.org"),
- TEST_DATA_DIR "/example.org-empty.zone");
-
- RRsetPtr rrset_a(new RRset(Name("a.example.com"),
- RRClass::IN(), RRType::A(), RRTTL(300)));
- rrset_a->addRdata(rdata::in::A("192.0.2.1"));
-
- EXPECT_THROW(client_->add(Name("example.org"), rrset_a),
- OutOfZone);
- // Teardown checks for memory segment leaks
-}
-
-TEST_F(MemoryClientTest, addNullRRsetThrows) {
- client_->load(Name("example.org"),
- TEST_DATA_DIR "/example.org-rrsigs.zone");
-
- EXPECT_THROW(client_->add(Name("example.org"), ConstRRsetPtr()),
- InMemoryClient::NullRRset);
-
- // Teardown checks for memory segment leaks
-}
-
TEST_F(MemoryClientTest, addEmptyRRsetThrows) {
- client_->load(Name("example.org"),
- TEST_DATA_DIR "/example.org-rrsigs.zone");
-
- RRsetPtr rrset_a(new RRset(Name("example.org"), RRClass::IN(), RRType::A(),
- RRTTL(300)));
- EXPECT_THROW(client_->add(Name("example.org"), rrset_a),
- InMemoryClient::AddError);
-
+ vector<ConstRRsetPtr> rrsets_vec;
+ rrsets_vec.push_back(textToRRset("example.org. 3600 IN SOA "
+ "ns1.example.org. bugs.x.w.example.org. "
+ "2010012601 3600 300 3600000 1200",
+ zclass_, Name("example.org")));
+ rrsets_vec.push_back(RRsetPtr(new RRset(Name("example.org"), zclass_,
+ RRType::A(), RRTTL(3600))));
+
+ EXPECT_THROW(
+ client_->load(Name("example.org"),
+ *MockVectorIterator::makeIterator(rrsets_vec)),
+ ZoneDataUpdater::AddError);
// Teardown checks for memory segment leaks
}
-TEST_F(MemoryClientTest, add) {
- client_->load(Name("example.org"), TEST_DATA_DIR "/example.org-empty.zone");
-
- // Add another RRset
- RRsetPtr rrset_a(new RRset(Name("example.org"), RRClass::IN(), RRType::A(),
- RRTTL(300)));
- rrset_a->addRdata(rdata::in::A("192.0.2.1"));
- client_->add(Name("example.org"), rrset_a);
-
- ZoneIteratorPtr iterator(client_->getIterator(Name("example.org")));
-
- // First we have the SOA
- ConstRRsetPtr rrset(iterator->getNextRRset());
- EXPECT_TRUE(rrset);
- EXPECT_EQ(RRType::A(), rrset->getType());
-
- rrset = iterator->getNextRRset();
- EXPECT_TRUE(rrset);
- EXPECT_EQ(RRType::SOA(), rrset->getType());
-
- // There's nothing else in this zone
- EXPECT_EQ(ConstRRsetPtr(), iterator->getNextRRset());
-}
-
TEST_F(MemoryClientTest, findZoneData) {
client_->load(Name("example.org"),
TEST_DATA_DIR "/example.org-rrsigs.zone");
@@ -774,4 +761,5 @@ TEST_F(MemoryClientTest, getJournalReaderNotImplemented) {
EXPECT_THROW(client_->getJournalReader(Name("."), 0, 0),
isc::NotImplemented);
}
+
}
diff --git a/src/lib/datasrc/tests/memory/zone_data_unittest.cc b/src/lib/datasrc/tests/memory/zone_data_unittest.cc
index d15fe8b..3c28cec 100644
--- a/src/lib/datasrc/tests/memory/zone_data_unittest.cc
+++ b/src/lib/datasrc/tests/memory/zone_data_unittest.cc
@@ -108,7 +108,7 @@ void
checkFindRdataSet(const ZoneTree& tree, const Name& name, RRType type,
const RdataSet* expected_set)
{
- ZoneNode* node = NULL;
+ const ZoneNode* node = NULL;
tree.find(name, &node);
ASSERT_NE(static_cast<ZoneNode*>(NULL), node);
EXPECT_EQ(expected_set, RdataSet::find(node->getData(), type));
diff --git a/src/lib/datasrc/tests/memory/zone_finder_unittest.cc b/src/lib/datasrc/tests/memory/zone_finder_unittest.cc
index a536bf5..4cd08c0 100644
--- a/src/lib/datasrc/tests/memory/zone_finder_unittest.cc
+++ b/src/lib/datasrc/tests/memory/zone_finder_unittest.cc
@@ -23,6 +23,7 @@
#include "../../tests/faked_nsec3.h"
#include <datasrc/memory/zone_finder.h>
+#include <datasrc/memory/zone_data_updater.h>
#include <datasrc/memory/rdata_serialization.h>
#include <datasrc/data_source.h>
#include <testutils/dnsmessage_test.h>
@@ -105,7 +106,8 @@ public:
class_(RRClass::IN()),
origin_("example.org"),
zone_data_(ZoneData::create(mem_sgmt_, origin_)),
- zone_finder_(*zone_data_, class_)
+ zone_finder_(*zone_data_, class_),
+ updater_(mem_sgmt_, class_, origin_, *zone_data_)
{
// Build test RRsets. Below, we construct an RRset for
// each textual RR(s) of zone_data, and assign it to the corresponding
@@ -190,130 +192,8 @@ public:
ZoneData::destroy(mem_sgmt_, zone_data_, RRClass::IN());
}
- // NSEC3-specific call for 'loading' data
- void addZoneDataNSEC3(const ConstRRsetPtr rrset) {
- assert(rrset->getType() == RRType::NSEC3());
-
- const generic::NSEC3& nsec3_rdata =
- dynamic_cast<const generic::NSEC3&>(
- rrset->getRdataIterator()->getCurrent());
-
- NSEC3Data* nsec3_data = zone_data_->getNSEC3Data();
- if (nsec3_data == NULL) {
- nsec3_data = NSEC3Data::create(mem_sgmt_, nsec3_rdata);
- zone_data_->setNSEC3Data(nsec3_data);
- } else {
- const size_t salt_len = nsec3_data->getSaltLen();
- const uint8_t* salt_data = nsec3_data->getSaltData();
- const vector<uint8_t>& salt_data_2 = nsec3_rdata.getSalt();
-
- if ((nsec3_rdata.getHashalg() != nsec3_data->hashalg) ||
- (nsec3_rdata.getIterations() != nsec3_data->iterations) ||
- (salt_data_2.size() != salt_len)) {
- isc_throw(isc::Unexpected,
- "NSEC3 with inconsistent parameters: " <<
- rrset->toText());
- }
-
- if ((salt_len > 0) &&
- (std::memcmp(&salt_data_2[0], salt_data, salt_len) != 0)) {
- isc_throw(isc::Unexpected,
- "NSEC3 with inconsistent parameters: " <<
- rrset->toText());
- }
- }
-
- ZoneNode* node;
- nsec3_data->insertName(mem_sgmt_, rrset->getName(), &node);
-
- RdataSet* rdset = RdataSet::create(mem_sgmt_, encoder_,
- rrset, ConstRRsetPtr());
- RdataSet* old_rdset = node->setData(rdset);
- if (old_rdset != NULL) {
- RdataSet::destroy(mem_sgmt_, class_, old_rdset);
- }
- zone_data_->setSigned(true);
- }
-
- // simplified version of 'loading' data
- void addZoneData(const ConstRRsetPtr rrset) {
- ZoneNode* node = NULL;
-
- if (rrset->getType() == RRType::NSEC3()) {
- return (addZoneDataNSEC3(rrset));
- } else if (rrset->getType() == RRType::NSEC()) {
- zone_data_->setSigned(true);
- }
-
- zone_data_->insertName(mem_sgmt_, rrset->getName(), &node);
-
- if (rrset->getType() == RRType::NS() &&
- rrset->getName() != zone_data_->getOriginNode()->getName()) {
- node->setFlag(DomainTreeNode<RdataSet>::FLAG_CALLBACK);
- } else if (rrset->getType() == RRType::DNAME()) {
- node->setFlag(DomainTreeNode<RdataSet>::FLAG_CALLBACK);
- }
-
- RdataSet* next_rds = node->getData();
- RdataSet* rdataset =
- RdataSet::create(mem_sgmt_, encoder_, rrset, rrset->getRRsig());
- rdataset->next = next_rds;
- node->setData(rdataset);
-
- // find wildcard nodes in name (go through all of them in case there
- // is a nonterminal one)
- // Note that this method is pretty much equal to the 'real' loader;
- // but less efficient
- Name name(rrset->getName());
- while (name.getLabelCount() > 1) {
- if (name.isWildcard()) {
- ZoneNode* wnode = NULL;
- // add Wild node
- zone_data_->insertName(mem_sgmt_, name.split(1), &wnode);
- wnode->setFlag(ZoneData::WILDCARD_NODE);
- // add wildcard name itself too
- zone_data_->insertName(mem_sgmt_, name, &wnode);
- }
- name = name.split(1);
- }
-
- // If we've added NSEC3PARAM at zone origin, set up NSEC3
- // specific data or check consistency with already set up
- // parameters.
- if (rrset->getType() == RRType::NSEC3PARAM() &&
- rrset->getName() == origin_) {
- // We know rrset has exactly one RDATA
- const generic::NSEC3PARAM& param =
- dynamic_cast<const generic::NSEC3PARAM&>
- (rrset->getRdataIterator()->getCurrent());
-
- NSEC3Data* nsec3_data = zone_data_->getNSEC3Data();
- if (nsec3_data == NULL) {
- nsec3_data = NSEC3Data::create(mem_sgmt_, param);
- zone_data_->setNSEC3Data(nsec3_data);
- zone_data_->setSigned(true);
- } else {
- size_t salt_len = nsec3_data->getSaltLen();
- const uint8_t* salt_data = nsec3_data->getSaltData();
- const vector<uint8_t>& salt_data_2 = param.getSalt();
-
- if ((param.getHashalg() != nsec3_data->hashalg) ||
- (param.getIterations() != nsec3_data->iterations) ||
- (salt_data_2.size() != salt_len)) {
- isc_throw(isc::Unexpected,
- "NSEC3PARAM with inconsistent parameters: "
- << rrset->toText());
- }
-
- if ((salt_len > 0) &&
- (std::memcmp(&salt_data_2[0],
- salt_data, salt_len) != 0)) {
- isc_throw(isc::Unexpected,
- "NSEC3PARAM with inconsistent parameters: "
- << rrset->toText());
- }
- }
- }
+ void addToZoneData(const ConstRRsetPtr rrset) {
+ updater_.add(rrset, rrset->getRRsig());
}
// Some data to test with
@@ -323,7 +203,7 @@ public:
MemorySegmentTest mem_sgmt_;
memory::ZoneData* zone_data_;
memory::InMemoryZoneFinder zone_finder_;
- isc::datasrc::memory::RdataEncoder encoder_;
+ ZoneDataUpdater updater_;
// Placeholder for storing RRsets to be checked with rrsetsCheck()
vector<ConstRRsetPtr> actual_rrsets_;
@@ -538,7 +418,7 @@ TEST_F(InMemoryZoneFinderTest, constructor) {
TEST_F(InMemoryZoneFinderTest, findCNAME) {
// install CNAME RR
- addZoneData(rr_cname_);
+ addToZoneData(rr_cname_);
// Find A RR of the same. Should match the CNAME
findTest(rr_cname_->getName(), RRType::NS(), ZoneFinder::CNAME, true,
@@ -553,10 +433,10 @@ TEST_F(InMemoryZoneFinderTest, findCNAMEUnderZoneCut) {
// There's nothing special when we find a CNAME under a zone cut
// (with FIND_GLUE_OK). The behavior is different from BIND 9,
// so we test this case explicitly.
- addZoneData(rr_child_ns_);
+ addToZoneData(rr_child_ns_);
ConstRRsetPtr rr_cname_under_cut_ = textToRRset(
"cname.child.example.org. 300 IN CNAME target.child.example.org.");
- addZoneData(rr_cname_under_cut_);
+ addToZoneData(rr_cname_under_cut_);
findTest(Name("cname.child.example.org"), RRType::AAAA(),
ZoneFinder::CNAME, true, rr_cname_under_cut_,
ZoneFinder::RESULT_DEFAULT, NULL, ZoneFinder::FIND_GLUE_OK);
@@ -564,7 +444,7 @@ TEST_F(InMemoryZoneFinderTest, findCNAMEUnderZoneCut) {
// Search under a DNAME record. It should return the DNAME
TEST_F(InMemoryZoneFinderTest, findBelowDNAME) {
- EXPECT_NO_THROW(addZoneData(rr_dname_));
+ EXPECT_NO_THROW(addToZoneData(rr_dname_));
findTest(Name("below.dname.example.org"), RRType::A(), ZoneFinder::DNAME,
true, rr_dname_);
}
@@ -572,8 +452,8 @@ TEST_F(InMemoryZoneFinderTest, findBelowDNAME) {
// Search at the domain with DNAME. It should act as DNAME isn't there, DNAME
// influences only the data below (see RFC 2672, section 3)
TEST_F(InMemoryZoneFinderTest, findAtDNAME) {
- EXPECT_NO_THROW(addZoneData(rr_dname_));
- EXPECT_NO_THROW(addZoneData(rr_dname_a_));
+ EXPECT_NO_THROW(addToZoneData(rr_dname_));
+ EXPECT_NO_THROW(addToZoneData(rr_dname_a_));
const Name dname_name(rr_dname_->getName());
findTest(dname_name, RRType::A(), ZoneFinder::SUCCESS, true, rr_dname_a_);
@@ -585,8 +465,8 @@ TEST_F(InMemoryZoneFinderTest, findAtDNAME) {
// Try searching something that is both under NS and DNAME, without and with
// GLUE_OK mode (it should stop at the NS and DNAME respectively).
TEST_F(InMemoryZoneFinderTest, DNAMEUnderNS) {
- addZoneData(rr_child_ns_);
- addZoneData(rr_child_dname_);
+ addToZoneData(rr_child_ns_);
+ addToZoneData(rr_child_dname_);
Name lowName("below.dname.child.example.org.");
@@ -598,10 +478,10 @@ TEST_F(InMemoryZoneFinderTest, DNAMEUnderNS) {
// Test adding child zones and zone cut handling
TEST_F(InMemoryZoneFinderTest, delegationNS) {
// add in-zone data
- EXPECT_NO_THROW(addZoneData(rr_ns_));
+ EXPECT_NO_THROW(addToZoneData(rr_ns_));
// install a zone cut
- EXPECT_NO_THROW(addZoneData(rr_child_ns_));
+ EXPECT_NO_THROW(addToZoneData(rr_child_ns_));
// below the zone cut
findTest(Name("www.child.example.org"), RRType::A(),
@@ -618,7 +498,7 @@ TEST_F(InMemoryZoneFinderTest, delegationNS) {
findTest(origin_, RRType::NS(), ZoneFinder::SUCCESS, true, rr_ns_);
// unusual case of "nested delegation": the highest cut should be used.
- EXPECT_NO_THROW(addZoneData(rr_grandchild_ns_));
+ EXPECT_NO_THROW(addToZoneData(rr_grandchild_ns_));
findTest(Name("www.grand.child.example.org"), RRType::A(),
// note: !rr_grandchild_ns_
ZoneFinder::DELEGATION, true, rr_child_ns_);
@@ -627,9 +507,9 @@ TEST_F(InMemoryZoneFinderTest, delegationNS) {
TEST_F(InMemoryZoneFinderTest, delegationWithDS) {
// Similar setup to the previous one, but with DS RR at the delegation
// point.
- addZoneData(rr_ns_);
- addZoneData(rr_child_ns_);
- addZoneData(rr_child_ds_);
+ addToZoneData(rr_ns_);
+ addToZoneData(rr_child_ns_);
+ addToZoneData(rr_child_ds_);
// Normal types of query should result in delegation, but DS query
// should be considered in-zone (but only exactly at the delegation point).
@@ -647,9 +527,9 @@ TEST_F(InMemoryZoneFinderTest, delegationWithDS) {
}
TEST_F(InMemoryZoneFinderTest, findAny) {
- EXPECT_NO_THROW(addZoneData(rr_a_));
- EXPECT_NO_THROW(addZoneData(rr_ns_));
- EXPECT_NO_THROW(addZoneData(rr_child_glue_));
+ EXPECT_NO_THROW(addToZoneData(rr_a_));
+ EXPECT_NO_THROW(addToZoneData(rr_ns_));
+ EXPECT_NO_THROW(addToZoneData(rr_child_glue_));
vector<ConstRRsetPtr> expected_sets;
@@ -668,7 +548,7 @@ TEST_F(InMemoryZoneFinderTest, findAny) {
findAllTest(rr_child_glue_->getName(), ZoneFinder::SUCCESS, expected_sets);
// add zone cut
- EXPECT_NO_THROW(addZoneData(rr_child_ns_));
+ EXPECT_NO_THROW(addToZoneData(rr_child_ns_));
// zone cut
findAllTest(rr_child_ns_->getName(), ZoneFinder::DELEGATION,
@@ -684,16 +564,16 @@ TEST_F(InMemoryZoneFinderTest, findAny) {
TEST_F(InMemoryZoneFinderTest, glue) {
// install zone data:
// a zone cut
- EXPECT_NO_THROW(addZoneData(rr_child_ns_));
+ EXPECT_NO_THROW(addToZoneData(rr_child_ns_));
// glue for this cut
- EXPECT_NO_THROW(addZoneData(rr_child_glue_));
+ EXPECT_NO_THROW(addToZoneData(rr_child_glue_));
// a nested zone cut (unusual)
- EXPECT_NO_THROW(addZoneData(rr_grandchild_ns_));
+ EXPECT_NO_THROW(addToZoneData(rr_grandchild_ns_));
// glue under the deeper zone cut
- EXPECT_NO_THROW(addZoneData(rr_grandchild_glue_));
+ EXPECT_NO_THROW(addToZoneData(rr_grandchild_glue_));
// glue 'at the' zone cut
- EXPECT_NO_THROW(addZoneData(rr_ns_a_));
- EXPECT_NO_THROW(addZoneData(rr_ns_ns_));
+ EXPECT_NO_THROW(addToZoneData(rr_ns_a_));
+ EXPECT_NO_THROW(addToZoneData(rr_ns_ns_));
// by default glue is hidden due to the zone cut
findTest(rr_child_glue_->getName(), RRType::A(), ZoneFinder::DELEGATION,
@@ -749,16 +629,16 @@ InMemoryZoneFinderTest::findCheck(ZoneFinder::FindResultFlags expected_flags,
rr_a_->addRRsig(createRdata(RRType::RRSIG(), RRClass::IN(),
"A 5 3 3600 20120814220826 20120715220826 "
"1234 example.com. FAKE"));
- EXPECT_NO_THROW(addZoneData(rr_ns_));
- EXPECT_NO_THROW(addZoneData(rr_ns_a_));
- EXPECT_NO_THROW(addZoneData(rr_ns_aaaa_));
- EXPECT_NO_THROW(addZoneData(rr_a_));
+ EXPECT_NO_THROW(addToZoneData(rr_ns_));
+ EXPECT_NO_THROW(addToZoneData(rr_ns_a_));
+ EXPECT_NO_THROW(addToZoneData(rr_ns_aaaa_));
+ EXPECT_NO_THROW(addToZoneData(rr_a_));
if ((expected_flags & ZoneFinder::RESULT_NSEC3_SIGNED) != 0) {
- addZoneData(rr_nsec3_);
+ addToZoneData(rr_nsec3_);
zone_data_->setSigned(true);
}
if ((expected_flags & ZoneFinder::RESULT_NSEC_SIGNED) != 0) {
- addZoneData(rr_nsec_);
+ addToZoneData(rr_nsec_);
zone_data_->setSigned(true);
}
@@ -805,7 +685,7 @@ InMemoryZoneFinderTest::findCheck(ZoneFinder::FindResultFlags expected_flags,
expected_nsec, expected_flags, NULL, find_options);
if ((expected_flags & ZoneFinder::RESULT_NSEC_SIGNED) != 0) {
- addZoneData(rr_ns_nsec_);
+ addToZoneData(rr_ns_nsec_);
zone_data_->setSigned(true);
if ((find_options & ZoneFinder::FIND_DNSSEC) != 0) {
expected_nsec = rr_ns_nsec_;
@@ -841,8 +721,8 @@ InMemoryZoneFinderTest::findNSECENTCheck(const Name& ent_name,
ConstRRsetPtr expected_nsec,
ZoneFinder::FindResultFlags expected_flags)
{
- addZoneData(rr_emptywild_);
- addZoneData(rr_under_wild_);
+ addToZoneData(rr_emptywild_);
+ addToZoneData(rr_under_wild_);
// Sanity check: Should result in NXRRSET
findTest(ent_name, RRType::A(), ZoneFinder::NXRRSET, true,
@@ -854,10 +734,10 @@ InMemoryZoneFinderTest::findNSECENTCheck(const Name& ent_name,
// Now add the NSEC rrs making it a 'complete' zone (in terms of NSEC,
// there are no sigs)
- addZoneData(rr_nsec_);
- addZoneData(rr_ent_nsec2_);
- addZoneData(rr_ent_nsec3_);
- addZoneData(rr_ent_nsec4_);
+ addToZoneData(rr_nsec_);
+ addToZoneData(rr_ent_nsec2_);
+ addToZoneData(rr_ent_nsec3_);
+ addToZoneData(rr_ent_nsec4_);
zone_data_->setSigned(true);
// Should result in NXRRSET, and RESULT_NSEC_SIGNED
@@ -915,14 +795,14 @@ InMemoryZoneFinderTest::emptyNodeCheck(
for (int i = 0; names[i] != NULL; ++i) {
ConstRRsetPtr rrset = textToRRset(string(names[i]) +
" 300 IN A 192.0.2.1");
- addZoneData(rrset);
+ addToZoneData(rrset);
}
if ((expected_flags & ZoneFinder::RESULT_NSEC3_SIGNED) != 0) {
- addZoneData(rr_nsec3_);
+ addToZoneData(rr_nsec3_);
zone_data_->setSigned(true);
}
if ((expected_flags & ZoneFinder::RESULT_NSEC_SIGNED) != 0) {
- addZoneData(rr_nsec_);
+ addToZoneData(rr_nsec_);
zone_data_->setSigned(true);
}
@@ -989,15 +869,15 @@ InMemoryZoneFinderTest::wildcardCheck(
"RRSIG CNAME " +
string(rrsig_common)));
}
- addZoneData(rr_wild_);
- addZoneData(rr_cnamewild_);
+ addToZoneData(rr_wild_);
+ addToZoneData(rr_cnamewild_);
// If the zone is expected to be "signed" with NSEC3, add an NSEC3.
// (the content of the NSEC3 shouldn't matter)
if ((expected_flags & ZoneFinder::RESULT_NSEC3_SIGNED) != 0) {
- addZoneData(rr_nsec3_);
+ addToZoneData(rr_nsec3_);
}
if ((expected_flags & ZoneFinder::RESULT_NSEC_SIGNED) != 0) {
- addZoneData(rr_nsec_);
+ addToZoneData(rr_nsec_);
}
// Search at the parent. The parent will not have the A, but it will
@@ -1073,7 +953,7 @@ InMemoryZoneFinderTest::wildcardCheck(
wild_expected_flags, NULL, find_options, wild_ok);
}
- addZoneData(rr_under_wild_);
+ addToZoneData(rr_under_wild_);
{
SCOPED_TRACE("Search under non-wildcard");
findTest(Name("bar.foo.wild.example.org"), RRType::A(),
@@ -1090,7 +970,7 @@ InMemoryZoneFinderTest::wildcardCheck(
// NO_WILDCARD effect itself can be checked by the result code (NXDOMAIN).
ConstRRsetPtr expected_wild_nsec; // by default it's NULL
if ((expected_flags & ZoneFinder::RESULT_NSEC_SIGNED) != 0) {
- addZoneData(rr_wild_nsec_);
+ addToZoneData(rr_wild_nsec_);
expected_wild_nsec = rr_wild_nsec_;
}
{
@@ -1127,8 +1007,8 @@ TEST_F(InMemoryZoneFinderTest, wildcardDisabledWithoutNSEC) {
* the wildcard defaults."
*/
TEST_F(InMemoryZoneFinderTest, delegatedWildcard) {
- addZoneData(rr_child_wild_);
- addZoneData(rr_child_ns_);
+ addToZoneData(rr_child_wild_);
+ addToZoneData(rr_child_ns_);
{
SCOPED_TRACE("Looking under delegation point");
@@ -1149,12 +1029,12 @@ void
InMemoryZoneFinderTest::anyWildcardCheck(
ZoneFinder::FindResultFlags expected_flags)
{
- addZoneData(rr_wild_);
+ addToZoneData(rr_wild_);
if ((expected_flags & ZoneFinder::RESULT_NSEC3_SIGNED) != 0) {
- addZoneData(rr_nsec3_);
+ addToZoneData(rr_nsec3_);
}
if ((expected_flags & ZoneFinder::RESULT_NSEC_SIGNED) != 0) {
- addZoneData(rr_nsec_);
+ addToZoneData(rr_nsec_);
}
vector<ConstRRsetPtr> expected_sets;
@@ -1206,12 +1086,12 @@ InMemoryZoneFinderTest::emptyWildcardCheck(
* *
* wild
*/
- addZoneData(rr_emptywild_);
+ addToZoneData(rr_emptywild_);
if ((expected_flags & ZoneFinder::RESULT_NSEC3_SIGNED) != 0) {
- addZoneData(rr_nsec3_);
+ addToZoneData(rr_nsec3_);
}
if ((expected_flags & ZoneFinder::RESULT_NSEC_SIGNED) != 0) {
- addZoneData(rr_nsec_);
+ addToZoneData(rr_nsec_);
}
{
@@ -1263,7 +1143,7 @@ TEST_F(InMemoryZoneFinderTest, emptyWildcardNSEC) {
// Same as emptyWildcard, but with multiple * in the path.
TEST_F(InMemoryZoneFinderTest, nestedEmptyWildcard) {
- addZoneData(rr_nested_emptywild_);
+ addToZoneData(rr_nested_emptywild_);
{
SCOPED_TRACE("Asking for the original record under wildcards");
@@ -1394,8 +1274,8 @@ InMemoryZoneFinderTest::doCancelWildcardCheck(
* shouldn't be canceled isn't.
*/
TEST_F(InMemoryZoneFinderTest, cancelWildcard) {
- addZoneData(rr_wild_);
- addZoneData(rr_not_wild_);
+ addToZoneData(rr_wild_);
+ addToZoneData(rr_not_wild_);
{
SCOPED_TRACE("Runnig with single entry under foo.wild.example.org");
@@ -1405,7 +1285,7 @@ TEST_F(InMemoryZoneFinderTest, cancelWildcard) {
// Try putting another one under foo.wild....
// The result should be the same but it will be done in another way in the
// code, because the foo.wild.example.org will exist in the tree.
- addZoneData(rr_not_wild_another_);
+ addToZoneData(rr_not_wild_another_);
{
SCOPED_TRACE("Runnig with two entries under foo.wild.example.org");
doCancelWildcardCheck();
@@ -1414,15 +1294,15 @@ TEST_F(InMemoryZoneFinderTest, cancelWildcard) {
// Same tests as cancelWildcard for NSEC3-signed zone
TEST_F(InMemoryZoneFinderTest, cancelWildcardNSEC3) {
- addZoneData(rr_wild_);
- addZoneData(rr_not_wild_);
- addZoneData(rr_nsec3_);
+ addToZoneData(rr_wild_);
+ addToZoneData(rr_not_wild_);
+ addToZoneData(rr_nsec3_);
{
SCOPED_TRACE("Runnig with single entry under foo.wild.example.org");
doCancelWildcardCheck(ZoneFinder::RESULT_NSEC3_SIGNED);
}
- addZoneData(rr_not_wild_another_);
+ addToZoneData(rr_not_wild_another_);
{
SCOPED_TRACE("Runnig with two entries under foo.wild.example.org");
doCancelWildcardCheck(ZoneFinder::RESULT_NSEC3_SIGNED);
@@ -1433,9 +1313,9 @@ TEST_F(InMemoryZoneFinderTest, cancelWildcardNSEC3) {
// or without FIND_DNSSEC option. NSEC should be returned only when the option
// is given.
TEST_F(InMemoryZoneFinderTest, cancelWildcardNSEC) {
- addZoneData(rr_wild_);
- addZoneData(rr_not_wild_);
- addZoneData(rr_nsec_);
+ addToZoneData(rr_wild_);
+ addToZoneData(rr_not_wild_);
+ addToZoneData(rr_nsec_);
{
SCOPED_TRACE("Runnig with single entry under foo.wild.example.org");
@@ -1443,7 +1323,7 @@ TEST_F(InMemoryZoneFinderTest, cancelWildcardNSEC) {
ZoneFinder::FIND_DNSSEC);
doCancelWildcardCheck(ZoneFinder::RESULT_NSEC_SIGNED);
}
- addZoneData(rr_not_wild_another_);
+ addToZoneData(rr_not_wild_another_);
{
SCOPED_TRACE("Runnig with two entries under foo.wild.example.org");
doCancelWildcardCheck(ZoneFinder::RESULT_NSEC_SIGNED,
@@ -1464,7 +1344,7 @@ TEST_F(InMemoryZoneFinderTest, findNSEC3ForBadZone) {
DataSourceError);
// Only having NSEC3PARAM isn't enough
- addZoneData(textToRRset("example.org. 300 IN NSEC3PARAM "
+ addToZoneData(textToRRset("example.org. 300 IN NSEC3PARAM "
"1 0 12 aabbccdd"));
EXPECT_THROW(zone_finder_.findNSEC3(Name("www.example.org"), true),
DataSourceError);
@@ -1473,7 +1353,7 @@ TEST_F(InMemoryZoneFinderTest, findNSEC3ForBadZone) {
// is guaranteed.
const string ns1_nsec3_text = string(ns1_hash) + ".example.org." +
string(nsec3_common);
- addZoneData(textToRRset(ns1_nsec3_text));
+ addToZoneData(textToRRset(ns1_nsec3_text));
EXPECT_THROW(zone_finder_.findNSEC3(Name("www.example.org"), true),
DataSourceError);
}
@@ -1492,16 +1372,16 @@ public:
// zzz.example.org: hash=R5..
const string apex_nsec3_text = string(apex_hash) + ".example.org." +
string(nsec3_common);
- addZoneData(textToRRset(apex_nsec3_text));
+ addToZoneData(textToRRset(apex_nsec3_text));
const string ns1_nsec3_text = string(ns1_hash) + ".example.org." +
string(nsec3_common);
- addZoneData(textToRRset(ns1_nsec3_text));
+ addToZoneData(textToRRset(ns1_nsec3_text));
const string w_nsec3_text = string(w_hash) + ".example.org." +
string(nsec3_common);
- addZoneData(textToRRset(w_nsec3_text));
+ addToZoneData(textToRRset(w_nsec3_text));
const string zzz_nsec3_text = string(zzz_hash) + ".example.org." +
string(nsec3_common);
- addZoneData(textToRRset(zzz_nsec3_text));
+ addToZoneData(textToRRset(zzz_nsec3_text));
}
private:
diff --git a/src/lib/datasrc/tests/memory/zone_table_segment_unittest.cc b/src/lib/datasrc/tests/memory/zone_table_segment_unittest.cc
new file mode 100644
index 0000000..6bdd737
--- /dev/null
+++ b/src/lib/datasrc/tests/memory/zone_table_segment_unittest.cc
@@ -0,0 +1,83 @@
+// Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <datasrc/memory/zone_table_segment.h>
+#include <gtest/gtest.h>
+
+using namespace isc::datasrc::memory;
+using namespace isc::data;
+using namespace isc::util;
+using namespace std;
+
+namespace {
+
+class ZoneTableSegmentTest : public ::testing::Test {
+protected:
+ ZoneTableSegmentTest() :
+ config_(Element::fromJSON("{}")),
+ segment_(ZoneTableSegment::create((*config_.get())))
+ {}
+
+ ~ZoneTableSegmentTest() {
+ if (segment_ != NULL) {
+ ZoneTableSegment::destroy(segment_);
+ }
+ }
+
+ void TearDown() {
+ // Catch any future leaks here.
+ const MemorySegment& mem_sgmt = segment_->getMemorySegment();
+ EXPECT_TRUE(mem_sgmt.allMemoryDeallocated());
+
+ ZoneTableSegment::destroy(segment_);
+ segment_ = NULL;
+ }
+
+ const ElementPtr config_;
+ ZoneTableSegment* segment_;
+};
+
+
+TEST_F(ZoneTableSegmentTest, create) {
+ // By default, a local zone table segment is created.
+ EXPECT_NE(static_cast<void*>(NULL), segment_);
+}
+
+// Helper function to check const and non-const methods.
+template <typename TS, typename TH, typename TT>
+void
+testGetHeader(ZoneTableSegment* segment) {
+ TH& header = static_cast<TS*>(segment)->getHeader();
+
+ // The zone table is unset.
+ TT* table = header.getTable();
+ EXPECT_EQ(static_cast<void*>(NULL), table);
+}
+
+TEST_F(ZoneTableSegmentTest, getHeader) {
+ // non-const version.
+ testGetHeader<ZoneTableSegment, ZoneTableHeader, ZoneTable>(segment_);
+
+ // const version.
+ testGetHeader<const ZoneTableSegment, const ZoneTableHeader,
+ const ZoneTable>(segment_);
+}
+
+TEST_F(ZoneTableSegmentTest, getMemorySegment) {
+ // This doesn't do anything fun except test the API.
+ MemorySegment& mem_sgmt = segment_->getMemorySegment();
+ EXPECT_TRUE(mem_sgmt.allMemoryDeallocated());
+}
+
+} // anonymous namespace
diff --git a/src/lib/datasrc/tests/memory/zone_table_unittest.cc b/src/lib/datasrc/tests/memory/zone_table_unittest.cc
index 359df49..80f2a6e 100644
--- a/src/lib/datasrc/tests/memory/zone_table_unittest.cc
+++ b/src/lib/datasrc/tests/memory/zone_table_unittest.cc
@@ -12,6 +12,8 @@
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
+#include "memory_segment_test.h"
+
#include <exceptions/exceptions.h>
#include <util/memory_segment_local.h>
@@ -22,6 +24,7 @@
#include <datasrc/result.h>
#include <datasrc/memory/zone_data.h>
#include <datasrc/memory/zone_table.h>
+#include <datasrc/memory/segment_object_holder.h>
#include <gtest/gtest.h>
@@ -30,29 +33,9 @@
using namespace isc::dns;
using namespace isc::datasrc;
using namespace isc::datasrc::memory;
+using namespace isc::datasrc::memory::detail;
namespace {
-// Memory segment specified for tests. It normally behaves like a "local"
-// memory segment. If "throw count" is set to non 0 via setThrowCount(),
-// it continues the normal behavior up to the specified number of calls to
-// allocate(), and throws an exception at the next call.
-class TestMemorySegment : public isc::util::MemorySegmentLocal {
-public:
- TestMemorySegment() : throw_count_(0) {}
- virtual void* allocate(size_t size) {
- if (throw_count_ > 0) {
- if (--throw_count_ == 0) {
- throw std::bad_alloc();
- }
- }
- return (isc::util::MemorySegmentLocal::allocate(size));
- }
- void setThrowCount(size_t count) { throw_count_ = count; }
-
-private:
- size_t throw_count_;
-};
-
class ZoneTableTest : public ::testing::Test {
protected:
ZoneTableTest() : zclass_(RRClass::IN()),
@@ -73,7 +56,7 @@ protected:
}
const RRClass zclass_;
const Name zname1, zname2, zname3;
- TestMemorySegment mem_sgmt_;
+ test::MemorySegmentTest mem_sgmt_;
ZoneTable* zone_table;
};
@@ -87,46 +70,89 @@ TEST_F(ZoneTableTest, create) {
}
TEST_F(ZoneTableTest, addZone) {
+ // It doesn't accept empty (NULL) zones
+ EXPECT_THROW(zone_table->addZone(mem_sgmt_, zclass_, zname1, NULL),
+ isc::BadValue);
+
+ SegmentObjectHolder<ZoneData, RRClass> holder1(
+ mem_sgmt_, ZoneData::create(mem_sgmt_, zname1), zclass_);
+ const ZoneData* data1(holder1.get());
// Normal successful case.
- const ZoneTable::AddResult result1 =
- zone_table->addZone(mem_sgmt_, zclass_, zname1);
+ const ZoneTable::AddResult result1(zone_table->addZone(mem_sgmt_, zclass_,
+ zname1,
+ holder1.release()));
EXPECT_EQ(result::SUCCESS, result1.code);
+ EXPECT_EQ(static_cast<const ZoneData*>(NULL), result1.zone_data);
+ // It got released by it
+ EXPECT_EQ(static_cast<const ZoneData*>(NULL), holder1.get());
// Duplicate add doesn't replace the existing data.
- EXPECT_EQ(result::EXIST, zone_table->addZone(mem_sgmt_, zclass_,
- zname1).code);
- EXPECT_EQ(result1.zone_data,
- zone_table->addZone(mem_sgmt_, zclass_, zname1).zone_data);
+ SegmentObjectHolder<ZoneData, RRClass> holder2(
+ mem_sgmt_, ZoneData::create(mem_sgmt_, zname1), zclass_);
+ const ZoneTable::AddResult result2(zone_table->addZone(mem_sgmt_, zclass_,
+ zname1,
+ holder2.release()));
+ EXPECT_EQ(result::EXIST, result2.code);
+ // The old one gets out
+ EXPECT_EQ(data1, result2.zone_data);
+ // It releases this one even when we replace the old zone
+ EXPECT_EQ(static_cast<const ZoneData*>(NULL), holder2.get());
+ // We need to release the old one manually
+ ZoneData::destroy(mem_sgmt_, result2.zone_data, zclass_);
+
+ SegmentObjectHolder<ZoneData, RRClass> holder3(
+ mem_sgmt_, ZoneData::create(mem_sgmt_, Name("EXAMPLE.COM")),
+ zclass_);
// names are compared in a case insensitive manner.
- EXPECT_EQ(result::EXIST, zone_table->addZone(mem_sgmt_, zclass_,
- Name("EXAMPLE.COM")).code);
+ const ZoneTable::AddResult result3(zone_table->addZone(mem_sgmt_, zclass_,
+ Name("EXAMPLE.COM"),
+ holder3.release()));
+ EXPECT_EQ(result::EXIST, result3.code);
+ ZoneData::destroy(mem_sgmt_, result3.zone_data, zclass_);
// Add some more different ones. Should just succeed.
+ SegmentObjectHolder<ZoneData, RRClass> holder4(
+ mem_sgmt_, ZoneData::create(mem_sgmt_, zname2), zclass_);
EXPECT_EQ(result::SUCCESS,
- zone_table->addZone(mem_sgmt_, zclass_, zname2).code);
+ zone_table->addZone(mem_sgmt_, zclass_, zname2,
+ holder4.release()).code);
+ SegmentObjectHolder<ZoneData, RRClass> holder5(
+ mem_sgmt_, ZoneData::create(mem_sgmt_, zname3), zclass_);
EXPECT_EQ(result::SUCCESS,
- zone_table->addZone(mem_sgmt_, zclass_, zname3).code);
+ zone_table->addZone(mem_sgmt_, zclass_, zname3,
+ holder5.release()).code);
// Have the memory segment throw an exception in extending the internal
// tree. It still shouldn't cause memory leak (which would be detected
// in TearDown()).
- mem_sgmt_.setThrowCount(2);
- EXPECT_THROW(zone_table->addZone(mem_sgmt_, zclass_, Name("example.org")),
+ SegmentObjectHolder<ZoneData, RRClass> holder6(
+ mem_sgmt_, ZoneData::create(mem_sgmt_, Name("example.org")), zclass_);
+ mem_sgmt_.setThrowCount(1);
+ EXPECT_THROW(zone_table->addZone(mem_sgmt_, zclass_, Name("example.org"),
+ holder6.release()),
std::bad_alloc);
}
TEST_F(ZoneTableTest, findZone) {
- const ZoneTable::AddResult add_result1 =
- zone_table->addZone(mem_sgmt_, zclass_, zname1);
- EXPECT_EQ(result::SUCCESS, add_result1.code);
+ SegmentObjectHolder<ZoneData, RRClass> holder1(
+ mem_sgmt_, ZoneData::create(mem_sgmt_, zname1), zclass_);
+ ZoneData* zone_data = holder1.get();
+ EXPECT_EQ(result::SUCCESS, zone_table->addZone(mem_sgmt_, zclass_, zname1,
+ holder1.release()).code);
+ SegmentObjectHolder<ZoneData, RRClass> holder2(
+ mem_sgmt_, ZoneData::create(mem_sgmt_, zname2), zclass_);
EXPECT_EQ(result::SUCCESS,
- zone_table->addZone(mem_sgmt_, zclass_, zname2).code);
+ zone_table->addZone(mem_sgmt_, zclass_, zname2,
+ holder2.release()).code);
+ SegmentObjectHolder<ZoneData, RRClass> holder3(
+ mem_sgmt_, ZoneData::create(mem_sgmt_, zname3), zclass_);
EXPECT_EQ(result::SUCCESS,
- zone_table->addZone(mem_sgmt_, zclass_, zname3).code);
+ zone_table->addZone(mem_sgmt_, zclass_, zname3,
+ holder3.release()).code);
const ZoneTable::FindResult find_result1 =
zone_table->findZone(Name("example.com"));
EXPECT_EQ(result::SUCCESS, find_result1.code);
- EXPECT_EQ(add_result1.zone_data, find_result1.zone_data);
+ EXPECT_EQ(zone_data, find_result1.zone_data);
EXPECT_EQ(result::NOTFOUND,
zone_table->findZone(Name("example.org")).code);
@@ -137,14 +163,17 @@ TEST_F(ZoneTableTest, findZone) {
// and the code should be PARTIALMATCH.
EXPECT_EQ(result::PARTIALMATCH,
zone_table->findZone(Name("www.example.com")).code);
- EXPECT_EQ(add_result1.zone_data,
+ EXPECT_EQ(zone_data,
zone_table->findZone(Name("www.example.com")).zone_data);
// make sure the partial match is indeed the longest match by adding
// a zone with a shorter origin and query again.
+ SegmentObjectHolder<ZoneData, RRClass> holder4(
+ mem_sgmt_, ZoneData::create(mem_sgmt_, Name("com")), zclass_);
EXPECT_EQ(result::SUCCESS, zone_table->addZone(mem_sgmt_, zclass_,
- Name("com")).code);
- EXPECT_EQ(add_result1.zone_data,
+ Name("com"),
+ holder4.release()).code);
+ EXPECT_EQ(zone_data,
zone_table->findZone(Name("www.example.com")).zone_data);
}
}
diff --git a/src/lib/datasrc/tests/memory_datasrc_unittest.cc b/src/lib/datasrc/tests/memory_datasrc_unittest.cc
index 958c9e1..5223d83 100644
--- a/src/lib/datasrc/tests/memory_datasrc_unittest.cc
+++ b/src/lib/datasrc/tests/memory_datasrc_unittest.cc
@@ -22,7 +22,6 @@
#include <dns/rdata.h>
#include <dns/rdataclass.h>
#include <dns/rrclass.h>
-#include <dns/rrsetlist.h>
#include <dns/rrttl.h>
#include <dns/masterload.h>
diff --git a/src/lib/datasrc/tests/sqlite3_accessor_unittest.cc b/src/lib/datasrc/tests/sqlite3_accessor_unittest.cc
index 718d29b..100a0dd 100644
--- a/src/lib/datasrc/tests/sqlite3_accessor_unittest.cc
+++ b/src/lib/datasrc/tests/sqlite3_accessor_unittest.cc
@@ -191,6 +191,11 @@ TEST_F(SQLite3AccessorTest, iterator) {
checkRR(context, "sub.example.org.", "3600", "NS", "ns.sub.example.org.");
checkRR(context, "ns.sub.example.org.", "3600", "A", "192.0.2.101");
checkRR(context, "www.example.org.", "3600", "A", "192.0.2.1");
+ checkRR(context, "ns3.example.org.", "3600", "NSEC3",
+ "1 1 12 aabbccdd 2T7B4G4VSA5SMI47K61MV5BV1A22BOJR A RRSIG");
+ checkRR(context, "ns3.example.org.", "3600", "RRSIG",
+ "NSEC3 5 3 3600 20000101000000 20000201000000 "
+ "12345 ns3.example.org. FAKEFAKEFAKE");
// Check there's no other
EXPECT_FALSE(context->getNext(data));
@@ -665,16 +670,16 @@ TEST_F(SQLite3Create, creationtest) {
TEST_F(SQLite3Create, emptytest) {
ASSERT_FALSE(isReadable(SQLITE_NEW_DBFILE));
- // open one manualle
+ // open one manually
sqlite3* db;
ASSERT_EQ(SQLITE_OK, sqlite3_open(SQLITE_NEW_DBFILE, &db));
- // empty, but not locked, so creating it now should work
+ // empty, but not locked, so creating another accessor should work
SQLite3Accessor accessor2(SQLITE_NEW_DBFILE, "IN");
sqlite3_close(db);
- // should work now that we closed it
+ // should still work now that we closed it
SQLite3Accessor accessor3(SQLITE_NEW_DBFILE, "IN");
}
@@ -692,8 +697,10 @@ TEST_F(SQLite3Create, lockedtest) {
sqlite3_exec(db, "ROLLBACK TRANSACTION", NULL, NULL, NULL);
- // should work now that we closed it
+ // should work now that the transaction has been rolled back
SQLite3Accessor accessor3(SQLITE_NEW_DBFILE, "IN");
+
+ ASSERT_EQ(SQLITE_OK, sqlite3_close(db));
}
TEST_F(SQLite3AccessorTest, clone) {
diff --git a/src/lib/datasrc/tests/testdata/contexttest.zone b/src/lib/datasrc/tests/testdata/contexttest.zone
index a39649d..0c1393c 100644
--- a/src/lib/datasrc/tests/testdata/contexttest.zone
+++ b/src/lib/datasrc/tests/testdata/contexttest.zone
@@ -1,9 +1,10 @@
;; test zone file used for ZoneFinderContext tests.
;; RRSIGs are (obviouslly) faked ones for testing.
-example.org. 3600 IN SOA ns1.example.org. bugs.x.w.example.org. 71 3600 300 3600000 3600
+example.org. 3600 IN SOA ns1.example.org. bugs.x.w.example.org. 74 3600 300 3600000 3600
example.org. 3600 IN NS ns1.example.org.
example.org. 3600 IN NS ns2.example.org.
+example.org. 3600 IN RRSIG NS 7 3 3600 20150420235959 20051021000000 40430 example.org. FAKEFAKEFAKE
example.org. 3600 IN MX 1 mx1.example.org.
example.org. 3600 IN MX 2 mx2.example.org.
example.org. 3600 IN MX 3 mx.a.example.org.
diff --git a/src/lib/datasrc/tests/testdata/example.org.sqlite3 b/src/lib/datasrc/tests/testdata/example.org.sqlite3
index c7388ff..c799d2e 100644
Binary files a/src/lib/datasrc/tests/testdata/example.org.sqlite3 and b/src/lib/datasrc/tests/testdata/example.org.sqlite3 differ
diff --git a/src/lib/datasrc/zone.h b/src/lib/datasrc/zone.h
index 2330412..36a1cff 100644
--- a/src/lib/datasrc/zone.h
+++ b/src/lib/datasrc/zone.h
@@ -168,48 +168,25 @@ public:
/// can define a derived class of the base Context and override the
/// specific virtual method.
///
+ /// This base class defines these common protected methods along with
+ /// some helper pure virtual methods that would be necessary for the
+ /// common methods. If a derived class wants to use the common version
+ /// of the protected method, it needs to provide expected result through
+ /// their implementation of the pure virtual methods.
+ ///
/// This class object is generally expected to be associated with the
/// ZoneFinder that originally performed the \c find() call, and expects
/// the finder is valid throughout the lifetime of this object. It's
/// caller's responsibility to ensure that assumption.
class Context {
public:
- /// \brief The constructor for the normal find call.
- ///
- /// This constructor is expected to be called from the \c find()
- /// method when it constructs the return value.
+ /// \brief The constructor.
///
- /// \param finder The ZoneFinder on which find() is called.
/// \param options The find options specified for the find() call.
/// \param result The result of the find() call.
- Context(ZoneFinder& finder, FindOptions options,
- const ResultContext& result) :
- code(result.code), rrset(result.rrset),
- finder_(finder), flags_(result.flags), options_(options)
- {}
-
- /// \brief The constructor for the normal findAll call.
- ///
- /// This constructor is expected to be called from the \c findAll()
- /// method when it constructs the return value.
- ///
- /// It copies the vector that is to be returned to the caller of
- /// \c findAll() for possible subsequent use. Note that it cannot
- /// simply hold a reference to the vector because the caller may
- /// alter it after the \c findAll() call.
- ///
- /// \param finder The ZoneFinder on which findAll() is called.
- /// \param options The find options specified for the findAll() call.
- /// \param result The result of the findAll() call (whose rrset is
- /// expected to be NULL).
- /// \param all_set Reference to the vector given by the caller of
- /// \c findAll(), storing the RRsets to be returned.
- Context(ZoneFinder& finder, FindOptions options,
- const ResultContext& result,
- const std::vector<isc::dns::ConstRRsetPtr> &all_set) :
+ Context(FindOptions options, const ResultContext& result) :
code(result.code), rrset(result.rrset),
- finder_(finder), flags_(result.flags), options_(options),
- all_set_(all_set)
+ flags_(result.flags), options_(options)
{}
/// \brief The destructor.
@@ -292,21 +269,108 @@ public:
}
protected:
+ /// \brief Return the \c ZoneFinder that created this \c Context.
+ ///
+ /// A derived class implementation can return NULL if it defines
+ /// other protected methods that require a non NULL result from
+ /// this method. Otherwise it must return a valid, non NULL pointer
+ /// to the \c ZoneFinder object.
+ ///
+ /// When returning non NULL, the ownership of the pointed object
+ /// was not transferred to the caller; it cannot be assumed to be
+ /// valid after the originating \c Context object is destroyed.
+ /// Also, the caller must not try to delete the returned object.
+ virtual ZoneFinder* getFinder() = 0;
+
+ /// \brief Return a vector of RRsets corresponding to findAll() result.
+ ///
+ /// This method returns a set of RRsets that correspond to the
+ /// returned RRsets to a prior \c findAll() call.
+ ///
+ /// A derived class implementation can return NULL if it defines
+ /// other protected methods that require a non NULL result from
+ /// this method. Otherwise it must return a valid, non NULL pointer
+ /// to a vector that correspond to the expected set of RRsets.
+ ///
+ /// When returning non NULL, the ownership of the pointed object
+ /// was not transferred to the caller; it cannot be assumed to be
+ /// valid after the originating \c Context object is destroyed.
+ /// Also, the caller must not try to delete the returned object.
+ virtual const std::vector<isc::dns::ConstRRsetPtr>*
+ getAllRRsets() const = 0;
+
/// \brief Actual implementation of getAdditional().
///
/// This base class defines a default implementation that can be
/// used for any type of data sources. A data source implementation
/// can override it.
+ ///
+ /// The default version of this implementation requires both
+ /// \c getFinder() and \c getAllRRsets() return valid results.
virtual void getAdditionalImpl(
const std::vector<isc::dns::RRType>& requested_types,
std::vector<isc::dns::ConstRRsetPtr>& result);
private:
- ZoneFinder& finder_;
const FindResultFlags flags_;
protected:
const FindOptions options_;
+ };
+
+ /// \brief Generic ZoneFinder context that works for all implementations.
+ ///
+ /// This is a concrete derived class of \c ZoneFinder::Context that
+ /// only use the generic (default) versions of the protected methods
+ /// and therefore work for any data source implementation.
+ ///
+ /// A data source implementation can use this class to create a
+ /// \c Context object as a return value of \c find() or \c findAll()
+ /// method if it doesn't have to optimize specific protected methods.
+ class GenericContext : public Context {
+ public:
+ /// \brief The constructor for the normal find call.
+ ///
+ /// This constructor is expected to be called from the \c find()
+ /// method when it constructs the return value.
+ ///
+ /// \param finder The ZoneFinder on which find() is called.
+ /// \param options See the \c Context class.
+ /// \param result See the \c Context class.
+ GenericContext(ZoneFinder& finder, FindOptions options,
+ const ResultContext& result) :
+ Context(options, result), finder_(finder)
+ {}
+
+ /// \brief The constructor for the normal findAll call.
+ ///
+ /// This constructor is expected to be called from the \c findAll()
+ /// method when it constructs the return value.
+ ///
+ /// It copies the vector that is to be returned to the caller of
+ /// \c findAll() for possible subsequent use. Note that it cannot
+ /// simply hold a reference to the vector because the caller may
+ /// alter it after the \c findAll() call.
+ ///
+ /// \param finder The ZoneFinder on which findAll() is called.
+ /// \param options See the \c Context class.
+ /// \param result See the \c Context class.
+ /// \param all_set Reference to the vector given by the caller of
+ /// \c findAll(), storing the RRsets to be returned.
+ GenericContext(ZoneFinder& finder, FindOptions options,
+ const ResultContext& result,
+ const std::vector<isc::dns::ConstRRsetPtr>& all_set) :
+ Context(options, result), finder_(finder), all_set_(all_set)
+ {}
+
+ protected:
+ virtual ZoneFinder* getFinder() { return (&finder_); }
+ virtual const std::vector<isc::dns::ConstRRsetPtr>*
+ getAllRRsets() const {
+ return (&all_set_);
+ }
+
private:
+ ZoneFinder& finder_;
std::vector<isc::dns::ConstRRsetPtr> all_set_;
};
diff --git a/src/lib/datasrc/zone_finder_context.cc b/src/lib/datasrc/zone_finder_context.cc
index 7913d71..482eb65 100644
--- a/src/lib/datasrc/zone_finder_context.cc
+++ b/src/lib/datasrc/zone_finder_context.cc
@@ -12,6 +12,8 @@
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
+#include <exceptions/exceptions.h>
+
#include <dns/rdata.h>
#include <dns/rrset.h>
#include <dns/rrtype.h>
@@ -87,13 +89,22 @@ ZoneFinder::Context::getAdditionalImpl(const vector<RRType>& requested_types,
{
// If rrset is non NULL, it should have been SUCCESS/DELEGATION; otherwise
// we should have responded to type ANY query.
+ ZoneFinder* finder = getFinder();
+ if (finder == NULL) {
+ // This is a bug of the derived class implementation.
+ isc_throw(isc::Unexpected, "NULL ZoneFinder in finder Context");
+ }
if (rrset) {
- getAdditionalForRRset(finder_, *rrset, requested_types, result,
+ getAdditionalForRRset(*finder, *rrset, requested_types, result,
options_);
return;
}
- BOOST_FOREACH(ConstRRsetPtr rrset_in_set, all_set_) {
- getAdditionalForRRset(finder_, *rrset_in_set, requested_types, result,
+ const vector<ConstRRsetPtr>* all_sets = getAllRRsets();
+ if (all_sets == NULL) { // bug of the derived class implementation.
+ isc_throw(isc::Unexpected, "All RRsets is NULL in finder Context");
+ }
+ BOOST_FOREACH(ConstRRsetPtr rrset_in_set, *getAllRRsets()) {
+ getAdditionalForRRset(*finder, *rrset_in_set, requested_types, result,
options_);
}
}
diff --git a/src/lib/dhcp/Makefile.am b/src/lib/dhcp/Makefile.am
index 4c22b35..6585a38 100644
--- a/src/lib/dhcp/Makefile.am
+++ b/src/lib/dhcp/Makefile.am
@@ -16,6 +16,7 @@ CLEANFILES = *.gcno *.gcda
lib_LTLIBRARIES = libb10-dhcp++.la libb10-dhcpsrv.la
libb10_dhcp___la_SOURCES =
libb10_dhcp___la_SOURCES += libdhcp++.cc libdhcp++.h
+libb10_dhcp___la_SOURCES += lease_mgr.cc lease_mgr.h
libb10_dhcp___la_SOURCES += iface_mgr.cc iface_mgr.h
libb10_dhcp___la_SOURCES += iface_mgr_linux.cc
libb10_dhcp___la_SOURCES += iface_mgr_bsd.cc
@@ -28,6 +29,7 @@ libb10_dhcp___la_SOURCES += option4_addrlst.cc option4_addrlst.h
libb10_dhcp___la_SOURCES += dhcp6.h dhcp4.h
libb10_dhcp___la_SOURCES += pkt6.cc pkt6.h
libb10_dhcp___la_SOURCES += pkt4.cc pkt4.h
+libb10_dhcp___la_SOURCES += duid.cc duid.h
libb10_dhcpsrv_la_SOURCES = cfgmgr.cc cfgmgr.h
libb10_dhcpsrv_la_SOURCES += pool.cc pool.h
diff --git a/src/lib/dhcp/addr_utilities.cc b/src/lib/dhcp/addr_utilities.cc
index ad72833..de1e8b4 100644
--- a/src/lib/dhcp/addr_utilities.cc
+++ b/src/lib/dhcp/addr_utilities.cc
@@ -13,17 +13,39 @@
// PERFORMANCE OF THIS SOFTWARE.
#include <string.h>
+#include <exceptions/exceptions.h>
#include <dhcp/addr_utilities.h>
using namespace isc::asiolink;
-namespace isc {
-namespace dhcp {
-
-isc::asiolink::IOAddress firstAddrInPrefix(const isc::asiolink::IOAddress& prefix,
+namespace {
+
+/// @brief mask used for first/last address calculation in a IPv4 prefix
+///
+/// Using a static mask is faster than calculating it dynamically every time.
+const uint32_t bitMask4[] = { 0xffffffff, 0x7fffffff, 0x3fffffff, 0x1fffffff,
+ 0x0fffffff, 0x07ffffff, 0x03ffffff, 0x01ffffff,
+ 0x00ffffff, 0x007fffff, 0x003fffff, 0x001fffff,
+ 0x000fffff, 0x0007ffff, 0x0003ffff, 0x0001ffff,
+ 0x0000ffff, 0x00007fff, 0x00003fff, 0x00001fff,
+ 0x00000fff, 0x000007ff, 0x000003ff, 0x000001ff,
+ 0x000000ff, 0x0000007f, 0x0000003f, 0x0000001f,
+ 0x0000000f, 0x00000007, 0x00000003, 0x00000001,
+ 0x00000000 };
+
+/// @brief mask used for first/last address calculation in a IPv6 prefix
+const uint8_t bitMask6[]= { 0, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff };
+
+/// @brief calculates the first IPv6 address in a IPv6 prefix
+///
+/// Note: This is a private function. Do not use it directly.
+/// Please use firstAddrInPrefix() instead.
+///
+/// @param prefix IPv6 prefix
+/// @param len prefix length
+isc::asiolink::IOAddress firstAddrInPrefix6(const isc::asiolink::IOAddress& prefix,
uint8_t len) {
- const static uint8_t bitMask[]= { 0, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff };
uint8_t packed[V6ADDRESS_LEN];
// First we copy the whole address as 16 bytes.
@@ -36,7 +58,7 @@ isc::asiolink::IOAddress firstAddrInPrefix(const isc::asiolink::IOAddress& prefi
// Get the appropriate mask. It has relevant bits (those that should
// stay) set and irrelevant (those that should be wiped) cleared.
- uint8_t mask = bitMask[len % 8];
+ uint8_t mask = bitMask6[len % 8];
// Let's leave only whatever the mask says should not be cleared.
packed[len / 8] = packed[len / 8] & mask;
@@ -55,10 +77,50 @@ isc::asiolink::IOAddress firstAddrInPrefix(const isc::asiolink::IOAddress& prefi
return (isc::asiolink::IOAddress::from_bytes(AF_INET6, packed));
}
-isc::asiolink::IOAddress lastAddrInPrefix(const isc::asiolink::IOAddress& prefix,
+/// @brief calculates the first IPv4 address in a IPv4 prefix
+///
+/// Note: This is a private function. Do not use it directly.
+/// Please use firstAddrInPrefix() instead.
+///
+/// @param prefix IPv4 prefix
+/// @param len netmask length (0-32)
+isc::asiolink::IOAddress firstAddrInPrefix4(const isc::asiolink::IOAddress& prefix,
+ uint8_t len) {
+ uint32_t addr = prefix;
+ if (len > 32) {
+ isc_throw(isc::BadValue, "Too large netmask. 0..32 is allowed in IPv4");
+ }
+
+ return (IOAddress(addr & (~bitMask4[len])));
+}
+
+/// @brief calculates the last IPv4 address in a IPv4 prefix
+///
+/// Note: This is a private function. Do not use it directly.
+/// Please use firstAddrInPrefix() instead.
+///
+/// @param prefix IPv4 prefix that we calculate first address for
+/// @param len netmask length (0-32)
+isc::asiolink::IOAddress lastAddrInPrefix4(const isc::asiolink::IOAddress& prefix,
+ uint8_t len) {
+ uint32_t addr = prefix;
+ if (len>32) {
+ isc_throw(isc::BadValue, "Too large netmask. 0..32 is allowed in IPv4");
+ }
+
+ return (IOAddress(addr | bitMask4[len]));
+}
+
+/// @brief calculates the last IPv6 address in a IPv6 prefix
+///
+/// Note: This is a private function. Do not use it directly.
+/// Please use lastAddrInPrefix() instead.
+///
+/// @param prefix IPv6 prefix that we calculate first address for
+/// @param len netmask length (0-128)
+isc::asiolink::IOAddress lastAddrInPrefix6(const isc::asiolink::IOAddress& prefix,
uint8_t len) {
- const static uint8_t bitMask[]= { 0, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff };
uint8_t packed[V6ADDRESS_LEN];
// First we copy the whole address as 16 bytes.
@@ -70,10 +132,10 @@ isc::asiolink::IOAddress lastAddrInPrefix(const isc::asiolink::IOAddress& prefix
if (len % 8 != 0) {
// Get the appropriate mask. It has relevant bits (those that should
// stay) set and irrelevant (those that should be set to 1) cleared.
- uint8_t mask = bitMask[len % 8];
+ uint8_t mask = bitMask6[len % 8];
// Let's set those irrelevant bits with 1. It would be perhaps
- // easier to not use negation here and invert bitMask content. However,
+ // easier to not use negation here and invert bitMask6 content. However,
// with this approach, we can use the same mask in first and last
// address calculations.
packed[len / 8] = packed[len / 8] | ~mask;
@@ -92,5 +154,28 @@ isc::asiolink::IOAddress lastAddrInPrefix(const isc::asiolink::IOAddress& prefix
return (isc::asiolink::IOAddress::from_bytes(AF_INET6, packed));
}
+}; // end of anonymous namespace
+
+namespace isc {
+namespace dhcp {
+
+isc::asiolink::IOAddress firstAddrInPrefix(const isc::asiolink::IOAddress& prefix,
+ uint8_t len) {
+ if (prefix.getFamily() == AF_INET) {
+ return firstAddrInPrefix4(prefix, len);
+ } else {
+ return firstAddrInPrefix6(prefix, len);
+ }
+}
+
+isc::asiolink::IOAddress lastAddrInPrefix(const isc::asiolink::IOAddress& prefix,
+ uint8_t len) {
+ if (prefix.getFamily() == AF_INET) {
+ return lastAddrInPrefix4(prefix, len);
+ } else {
+ return lastAddrInPrefix6(prefix, len);
+ }
+}
+
};
};
diff --git a/src/lib/dhcp/cfgmgr.cc b/src/lib/dhcp/cfgmgr.cc
index 15e3ad9..d06544c 100644
--- a/src/lib/dhcp/cfgmgr.cc
+++ b/src/lib/dhcp/cfgmgr.cc
@@ -68,6 +68,39 @@ void CfgMgr::addSubnet6(const Subnet6Ptr& subnet) {
subnets6_.push_back(subnet);
}
+Subnet4Ptr
+CfgMgr::getSubnet4(const isc::asiolink::IOAddress& hint) {
+
+ // If there's only one subnet configured, let's just use it
+ // The idea is to keep small deployments easy. In a small network - one
+ // router that also runs DHCPv6 server. Users specifies a single pool and
+ // expects it to just work. Without this, the server would complain that it
+ // doesn't have IP address on its interfaces that matches that
+ // configuration. Such requirement makes sense in IPv4, but not in IPv6.
+ // The server does not need to have a global address (using just link-local
+ // is ok for DHCPv6 server) from the pool it serves.
+ if (subnets4_.size() == 1) {
+ return (subnets4_[0]);
+ }
+
+ // If there is more than one, we need to choose the proper one
+ for (Subnet4Collection::iterator subnet = subnets4_.begin();
+ subnet != subnets4_.end(); ++subnet) {
+ if ((*subnet)->inRange(hint)) {
+ return (*subnet);
+ }
+ }
+
+ // sorry, we don't support that subnet
+ return (Subnet4Ptr());
+}
+
+void CfgMgr::addSubnet4(const Subnet4Ptr& subnet) {
+ /// @todo: Check that this new subnet does not cross boundaries of any
+ /// other already defined subnet.
+ subnets4_.push_back(subnet);
+}
+
CfgMgr::CfgMgr() {
}
diff --git a/src/lib/dhcp/cfgmgr.h b/src/lib/dhcp/cfgmgr.h
index 5b73f2b..2911d05 100644
--- a/src/lib/dhcp/cfgmgr.h
+++ b/src/lib/dhcp/cfgmgr.h
@@ -72,7 +72,7 @@ public:
/// accessing it.
static CfgMgr& instance();
- /// @brief get subnet by address
+ /// @brief get IPv6 subnet by address
///
/// Finds a matching subnet, based on an address. This can be used
/// in two cases: when trying to find an appropriate lease based on
@@ -83,7 +83,7 @@ public:
/// @param hint an address that belongs to a searched subnet
Subnet6Ptr getSubnet6(const isc::asiolink::IOAddress& hint);
- /// @brief get subnet by interface-id
+ /// @brief get IPv6 subnet by interface-id
///
/// Another possibility to find a subnet is based on interface-id.
///
@@ -91,13 +91,44 @@ public:
/// @todo This method is not currently supported.
Subnet6Ptr getSubnet6(OptionPtr interface_id);
- /// @brief adds a subnet6
+ /// @brief adds an IPv6 subnet
void addSubnet6(const Subnet6Ptr& subnet);
/// @todo: Add subnet6 removal routines. Currently it is not possible
/// to remove subnets. The only case where subnet6 removal would be
/// needed is a dynamic server reconfiguration - a use case that is not
/// planned to be supported any time soon.
+
+ /// @brief removes all subnets
+ ///
+ /// This method removes all existing subnets. It is used during
+ /// reconfiguration - old configuration is wiped and new definitions
+ /// are used to recreate subnets.
+ ///
+ /// @todo Implement more intelligent approach. Note that comparison
+ /// between old and new configuration is tricky. For example: is
+ /// 2000::/64 and 2000::/48 the same subnet or is it something
+ /// completely new?
+ void deleteSubnets6() {
+ subnets6_.clear();
+ }
+
+ /// @brief get IPv4 subnet by address
+ ///
+ /// Finds a matching subnet, based on an address. This can be used
+ /// in two cases: when trying to find an appropriate lease based on
+ /// a) relay link address (that must be the address that is on link)
+ /// b) our global address on the interface the message was received on
+ /// (for directly connected clients)
+ ///
+ /// @param hint an address that belongs to a searched subnet
+ Subnet4Ptr getSubnet4(const isc::asiolink::IOAddress& hint);
+
+ /// @brief adds a subnet4
+ void addSubnet4(const Subnet4Ptr& subnet);
+
+ /// @brief removes all IPv4 subnets
+ void removeSubnets4();
protected:
/// @brief Protected constructor.
@@ -111,13 +142,21 @@ protected:
/// @brief virtual desctructor
virtual ~CfgMgr();
- /// @brief a container for Subnet6
+ /// @brief a container for IPv6 subnets.
///
/// That is a simple vector of pointers. It does not make much sense to
/// optimize access time (e.g. using a map), because typical search
/// pattern will use calling inRange() method on each subnet until
/// a match is found.
Subnet6Collection subnets6_;
+
+ /// @brief a container for IPv4 subnets.
+ ///
+ /// That is a simple vector of pointers. It does not make much sense to
+ /// optimize access time (e.g. using a map), because typical search
+ /// pattern will use calling inRange() method on each subnet until
+ /// a match is found.
+ Subnet4Collection subnets4_;
};
} // namespace isc::dhcp
diff --git a/src/lib/dhcp/duid.cc b/src/lib/dhcp/duid.cc
new file mode 100644
index 0000000..db7ba25
--- /dev/null
+++ b/src/lib/dhcp/duid.cc
@@ -0,0 +1,90 @@
+// Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <vector>
+#include <exceptions/exceptions.h>
+#include <stdint.h>
+#include <util/io_utilities.h>
+#include <dhcp/duid.h>
+
+namespace isc {
+namespace dhcp {
+
+DUID::DUID(const std::vector<uint8_t>& duid) {
+ if (duid.size() > MAX_DUID_LEN) {
+ isc_throw(OutOfRange, "DUID too large");
+ } else {
+ duid_ = duid;
+ }
+}
+
+DUID::DUID(const uint8_t * data, size_t len) {
+ if (len > MAX_DUID_LEN) {
+ isc_throw(OutOfRange, "DUID too large");
+ }
+
+ duid_ = std::vector<uint8_t>(data, data + len);
+}
+
+const std::vector<uint8_t> DUID::getDuid() const {
+ return (duid_);
+}
+
+DUID::DUIDType DUID::getType() const {
+ if (duid_.size() < 2) {
+ return (DUID_UNKNOWN);
+ }
+ uint16_t type = (duid_[0] << 8) + duid_[1];
+ if (type < DUID_MAX) {
+ return (static_cast<DUID::DUIDType>(type));
+ } else {
+ return (DUID_UNKNOWN);
+ }
+}
+
+bool DUID::operator == (const DUID& other) const {
+ return (this->duid_ == other.duid_);
+}
+
+bool DUID::operator != (const DUID& other) const {
+ return (this->duid_ != other.duid_);
+}
+
+/// constructor based on vector<uint8_t>
+ClientId::ClientId(const std::vector<uint8_t>& clientid)
+ :DUID(clientid) {
+}
+
+/// constructor based on C-style data
+ClientId::ClientId(const uint8_t *clientid, size_t len)
+ :DUID(clientid, len) {
+}
+
+/// @brief returns a copy of client-id data
+const std::vector<uint8_t> ClientId::getClientId() const {
+ return (duid_);
+}
+
+// compares two client-ids
+bool ClientId::operator == (const ClientId& other) const {
+ return (this->duid_ == other.duid_);
+}
+
+// compares two client-ids
+bool ClientId::operator != (const ClientId& other) const {
+ return (this->duid_ != other.duid_);
+}
+
+}; // end of isc::dhcp namespace
+}; // end of isc namespace
diff --git a/src/lib/dhcp/duid.h b/src/lib/dhcp/duid.h
new file mode 100644
index 0000000..53257db
--- /dev/null
+++ b/src/lib/dhcp/duid.h
@@ -0,0 +1,98 @@
+// Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <stdint.h>
+#include <unistd.h>
+#include <vector>
+#include <asiolink/io_address.h>
+
+
+namespace isc {
+namespace dhcp {
+
+/// @brief Holds DUID (DHCPv6 Unique Identifier)
+///
+/// This class holds DUID, that is used in client-id, server-id and
+/// several other options. It is used to identify DHCPv6 entity.
+class DUID {
+ public:
+ /// @brief maximum duid size
+ /// As defined in RFC3315, section 9.1
+ static const size_t MAX_DUID_LEN = 128;
+
+ /// @brief specifies DUID type
+ typedef enum {
+ DUID_UNKNOWN = 0, ///< invalid/unknown type
+ DUID_LLT = 1, ///< link-layer + time, see RFC3315, section 9.2
+ DUID_EN = 2, ///< enterprise-id, see RFC3315, section 9.3
+ DUID_LL = 3, ///< link-layer, see RFC3315, section 9.4
+ DUID_UUID = 4, ///< UUID, see RFC6355
+ DUID_MAX ///< not a real type, just maximum defined value + 1
+ } DUIDType;
+
+ /// @brief creates a DUID
+ DUID(const std::vector<uint8_t>& duid);
+
+ /// @brief creates a DUID
+ DUID(const uint8_t *duid, size_t len);
+
+ /// @brief returns a const reference to the actual DUID value
+ ///
+ /// Note: For safety reasons, this method returns a copy of data as
+ /// otherwise the reference would be only valid as long as the object that
+ /// returned it. In any case, this method should be used only sporadically.
+ /// If there are frequent uses, we must implement some other method
+ /// (e.g. storeSelf()) that will avoid data copying.
+ const std::vector<uint8_t> getDuid() const;
+
+ /// @brief returns DUID type
+ DUIDType getType() const;
+
+ // compares two DUIDs
+ bool operator == (const DUID& other) const;
+
+ // compares two DUIDs
+ bool operator != (const DUID& other) const;
+
+ protected:
+ /// the actual content of the DUID
+ std::vector<uint8_t> duid_;
+};
+
+/// @brief Holds Client identifier or client IPv4 address
+///
+/// This class is intended to be a generic IPv4 client identifier. It can hold
+/// a client-id
+class ClientId : DUID {
+ public:
+
+ /// constructor based on vector<uint8_t>
+ ClientId(const std::vector<uint8_t>& clientid);
+
+ /// constructor based on C-style data
+ ClientId(const uint8_t *clientid, size_t len);
+
+ /// @brief returns reference to the client-id data
+ ///
+ const std::vector<uint8_t> getClientId() const;
+
+ // compares two client-ids
+ bool operator == (const ClientId& other) const;
+
+ // compares two client-ids
+ bool operator != (const ClientId& other) const;
+};
+
+}; // end of isc::dhcp namespace
+}; // end of isc namespace
diff --git a/src/lib/dhcp/lease_mgr.cc b/src/lib/dhcp/lease_mgr.cc
new file mode 100644
index 0000000..8291df3
--- /dev/null
+++ b/src/lib/dhcp/lease_mgr.cc
@@ -0,0 +1,68 @@
+// Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <sstream>
+#include <iostream>
+#include <map>
+#include <iostream>
+#include <string>
+#include <sstream>
+#include <algorithm>
+#include <iterator>
+#include <exceptions/exceptions.h>
+#include <boost/foreach.hpp>
+#include <boost/algorithm/string.hpp>
+#include "lease_mgr.h"
+
+using namespace std;
+
+using namespace isc::dhcp;
+
+LeaseMgr::LeaseMgr(const std::string& dbconfig) {
+
+ if (dbconfig.length() == 0) {
+ return;
+ }
+
+ vector<string> tokens;
+
+ // we need to pass a string to is_any_of, not just char *. Otherwise there
+ // are cryptic warnings on Debian6 running g++ 4.4 in /usr/include/c++/4.4
+ // /bits/stl_algo.h:2178 "array subscript is above array bounds"
+ boost::split(tokens, dbconfig, boost::is_any_of( string("\t ") ));
+ BOOST_FOREACH(std::string token, tokens) {
+ size_t pos = token.find("=");
+ if (pos != string::npos) {
+ string name = token.substr(0, pos);
+ string value = token.substr(pos + 1);
+ parameters_.insert(pair<string,string>(name, value));
+ } else {
+ isc_throw(InvalidParameter, "Cannot parse " << token
+ << ", expected format is name=value");
+ }
+
+ }
+}
+
+std::string LeaseMgr::getParameter(const std::string& name) const {
+ std::map<std::string, std::string>::const_iterator param
+ = parameters_.find(name);
+ if (param == parameters_.end()) {
+ isc_throw(BadValue, "Parameter not found");
+ }
+ return (param->second);
+}
+
+LeaseMgr::~LeaseMgr() {
+}
diff --git a/src/lib/dhcp/lease_mgr.h b/src/lib/dhcp/lease_mgr.h
new file mode 100644
index 0000000..4b7a1af
--- /dev/null
+++ b/src/lib/dhcp/lease_mgr.h
@@ -0,0 +1,480 @@
+// Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <string>
+#include <fstream>
+#include <vector>
+#include <map>
+#include <asiolink/io_address.h>
+#include <boost/shared_ptr.hpp>
+#include <dhcp/option.h>
+#include <dhcp/duid.h>
+
+/// @file dhcp/lease_mgr.h
+/// @brief An abstract API for lease database
+///
+/// This file contains declarations of Lease4, Lease6 and LeaseMgr classes.
+/// They are essential components of the interface to any database backend.
+/// Each concrete database backend (e.g. MySQL) will define a class derived
+/// from LeaseMgr class.
+///
+/// Failover considerations:
+/// There are no intermediate plans to implement DHCPv4 failover
+/// (draft-ietf-dhc-failover-12.txt). Currently (Oct. 2012) the DHCPv6 failover
+/// is being defined in DHC WG in IETF (draft-ietf-dhcpv6-failover-requirements,
+/// draft-ietf-dhcpv6-dailover-design), but the work is not advanced enough
+/// for implementation plans yet. v4 failover requires additional parameters
+/// to be kept with a lease. It is likely that v6 failover will require similar
+/// fields. Such implementation will require database schema extension.
+/// We have designed a way to expand/upgrade schemas during upgrades: a database
+/// schema is versioned and sanity checks about required version will be done
+/// upon start and/or upgrade. With this mechanism in place, we can add new
+/// fields to the database. In particular we can use that capability to
+/// introduce failover related fields.
+///
+/// However, there is another approach that can be reliably used to provide
+/// failover, even without the actual failover protocol implemented. As the
+/// first backend will use MySQL, we will be able to use Multi-Master capability
+/// offered by MySQL and use two separatate Kea instances connecting to the
+/// same database.
+///
+/// Nevertheless, we hope to have failover protocol eventually implemented in
+/// the Kea.
+
+namespace isc {
+namespace dhcp {
+
+/// @brief specifies unique subnet identifier
+/// @todo: Move this to subnet.h once ticket #2237 is merged
+typedef uint32_t SubnetID;
+
+/// @brief Structure that holds a lease for IPv4 address
+///
+/// For performance reasons it is a simple structure, not a class. If we chose
+/// make it a class, all fields would have to made private and getters/setters
+/// would be required. As this is a critical part of the code that will be used
+/// extensively, direct access is warranted.
+struct Lease4 {
+ /// IPv4 address
+ isc::asiolink::IOAddress addr_;
+
+ /// @brief Address extension
+ ///
+ /// It is envisaged that in some cases IPv4 address will be accompanied with some
+ /// additional data. One example of such use are Address + Port solutions (or
+ /// Port-restricted Addresses), where several clients may get the same address, but
+ /// different port ranges. This feature is not expected to be widely used.
+ /// Under normal circumstances, the value should be 0.
+ uint32_t ext_;
+
+ /// @brief hardware address
+ std::vector<uint8_t> hwaddr_;
+
+ /// @brief client identifier
+ boost::shared_ptr<ClientId> client_id_;
+
+ /// @brief renewal timer
+ ///
+ /// Specifies renewal time. Although technically it is a property of IA container,
+ /// not the address itself, since our data model does not define separate IA
+ /// entity, we are keeping it in the lease. In case of multiple addresses/prefixes
+ /// for the same IA, each must have consistent T1 and T2 values. Specified in
+ /// seconds since cltt.
+ uint32_t t1_;
+
+ /// @brief rebinding timer
+ ///
+ /// Specifies rebinding time. Although technically it is a property of IA container,
+ /// not the address itself, since our data model does not define separate IA
+ /// entity, we are keeping it in the lease. In case of multiple addresses/prefixes
+ /// for the same IA, each must have consistent T1 and T2 values. Specified in
+ /// seconds since cltt.
+ uint32_t t2_;
+
+ /// @brief valid lifetime
+ ///
+ /// Expressed as number of seconds since cltt
+ uint32_t valid_lft_;
+
+ /// @brief client last transmission time
+ ///
+ /// Specifies a timestamp, when last transmission from a client was received.
+ time_t cltt_;
+
+ /// @brief Subnet identifier
+ ///
+ /// Specifies subnet-id of the subnet that the lease belongs to
+ SubnetID subnet_id_;
+
+ /// @brief Is this a fixed lease?
+ ///
+ /// Fixed leases are kept after they are released/expired.
+ bool fixed_;
+
+ /// @brief client hostname
+ ///
+ /// This field may be empty
+ std::string hostname_;
+
+ /// @brief did we update AAAA record for this lease?
+ bool fqdn_fwd_;
+
+ /// @brief did we update PTR record for this lease?
+ bool fqdn_rev_;
+
+ /// @brief Lease comments.
+ ///
+ /// Currently not used. It may be used for keeping comments made by the
+ /// system administrator.
+ std::string comments_;
+
+ /// @todo: Add DHCPv4 failover related fields here
+};
+
+/// @brief Pointer to a Lease4 structure.
+typedef boost::shared_ptr<Lease4> Lease4Ptr;
+
+/// @brief A collection of IPv4 leases.
+typedef std::vector< boost::shared_ptr<Lease4Ptr> > Lease4Collection;
+
+/// @brief Structure that holds a lease for IPv6 address and/or prefix
+///
+/// For performance reasons it is a simple structure, not a class. Had we chose to
+/// make it a class, all fields would have to be made private and getters/setters
+/// would be required. As this is a critical part of the code that will be used
+/// extensively, direct access rather than through getters/setters is warranted.
+struct Lease6 {
+ typedef enum {
+ LEASE_IA_NA, /// the lease contains non-temporary IPv6 address
+ LEASE_IA_TA, /// the lease contains temporary IPv6 address
+ LEASE_IA_PD /// the lease contains IPv6 prefix (for prefix delegation)
+ } LeaseType;
+
+ /// @brief specifies lease type (normal addr, temporary addr, prefix)
+ LeaseType type_;
+
+ /// IPv6 address
+ isc::asiolink::IOAddress addr_;
+
+ /// IPv6 prefix length (used only for PD)
+ uint8_t prefixlen_;
+
+ /// @brief IAID
+ ///
+ /// Identity Association IDentifier. DHCPv6 stores all addresses and prefixes
+ /// in IA containers (IA_NA, IA_TA, IA_PD). Most containers may appear more
+ /// than once in a message. To differentiate between them, IAID field is present
+ uint32_t iaid_;
+
+ /// @brief hardware address
+ ///
+ /// This field is not really used and is optional at best. The concept of identifying
+ /// clients by their hardware address was replaced in DHCPv6 by DUID concept. Each
+ /// client has its own unique DUID (DHCP Unique IDentifier). Furthermore, client's
+ /// HW address is not always available, because client may be behind a relay (relay
+ /// stores only link-local address).
+ std::vector<uint8_t> hwaddr_;
+
+ /// @brief client identifier
+ boost::shared_ptr<DUID> duid_;
+
+ /// @brief preferred lifetime
+ ///
+ /// This parameter specifies preferred lifetime since the lease was assigned/renewed
+ /// (cltt), expressed in seconds.
+ uint32_t preferred_lft_;
+
+ /// @brief valid lifetime
+ ///
+ /// This parameter specified valid lifetime since the lease was assigned/renewed
+ /// (cltt), expressed in seconds.
+ uint32_t valid_lft_;
+
+ /// @brief T1 timer
+ ///
+ /// Specifies renewal time. Although technically it is a property of IA container,
+ /// not the address itself, since our data model does not define separate IA
+ /// entity, we are keeping it in the lease. In case of multiple addresses/prefixes
+ /// for the same IA, each must have consistent T1 and T2 values. Specified in
+ /// seconds since cltt.
+ uint32_t t1_;
+
+ /// @brief T2 timer
+ ///
+ /// Specifies rebinding time. Although technically it is a property of IA container,
+ /// not the address itself, since our data model does not define separate IA
+ /// entity, we are keeping it in the lease. In case of multiple addresses/prefixes
+ /// for the same IA, each must have consistent T1 and T2 values. Specified in
+ /// seconds since cltt.
+ uint32_t t2_;
+
+ /// @brief client last transmission time
+ ///
+ /// Specifies a timestamp, when last transmission from a client was received.
+ time_t cltt_;
+
+ /// @brief Subnet identifier
+ ///
+ /// Specifies subnet-id of the subnet that the lease belongs to
+ SubnetID subnet_id_;
+
+ /// @brief Is this a fixed lease?
+ ///
+ /// Fixed leases are kept after they are released/expired.
+ bool fixed_;
+
+ /// @brief client hostname
+ ///
+ /// This field may be empty
+ std::string hostname_;
+
+ /// @brief did we update AAAA record for this lease?
+ bool fqdn_fwd_;
+
+ /// @brief did we update PTR record for this lease?
+ bool fqdn_rev_;
+
+ /// @brief Lease comments
+ ///
+ /// This field is currently not used.
+ std::string comments_;
+
+ /// @todo: Add DHCPv6 failover related fields here
+};
+
+/// @brief Pointer to a Lease6 structure.
+typedef boost::shared_ptr<Lease6> Lease6Ptr;
+
+/// @brief Const pointer to a Lease6 structure.
+typedef boost::shared_ptr<const Lease6> ConstLease6Ptr;
+
+/// @brief A collection of IPv6 leases.
+typedef std::vector< boost::shared_ptr<Lease6Ptr> > Lease6Collection;
+
+/// @brief Abstract Lease Manager
+///
+/// This is an abstract API for lease database backends. It provides unified
+/// interface to all backends. As this is an abstract class, it should not
+/// be used directly, but rather specialized derived class should be used
+/// instead.
+class LeaseMgr {
+public:
+
+ /// Client Hardware address
+ typedef std::vector<uint8_t> HWAddr;
+
+ /// @brief The sole lease manager constructor
+ ///
+ /// dbconfig is a generic way of passing parameters. Parameters
+ /// are passed in the "name=value" format, separated by spaces.
+ /// Values may be enclosed in double quotes, if needed.
+ ///
+ /// @param dbconfig database configuration
+ LeaseMgr(const std::string& dbconfig);
+
+ /// @brief Destructor (closes file)
+ virtual ~LeaseMgr();
+
+ /// @brief Adds an IPv4 lease.
+ ///
+ /// @param lease lease to be added
+ virtual bool addLease(Lease4Ptr lease) = 0;
+
+ /// @brief Adds an IPv6 lease.
+ ///
+ /// @param lease lease to be added
+ virtual bool addLease(Lease6Ptr lease) = 0;
+
+ /// @brief Returns existing IPv4 lease for specified IPv4 address and subnet_id
+ ///
+ /// This method is used to get a lease for specific subnet_id. There can be
+ /// at most one lease for any given subnet, so this method returns a single
+ /// pointer.
+ ///
+ /// @param addr address of the searched lease
+ /// @param subnet_id ID of the subnet the lease must belong to
+ ///
+ /// @return smart pointer to the lease (or NULL if a lease is not found)
+ virtual Lease4Ptr getLease4(isc::asiolink::IOAddress addr,
+ SubnetID subnet_id) const = 0;
+
+ /// @brief Returns an IPv4 lease for specified IPv4 address
+ ///
+ /// This method return a lease that is associated with a given address.
+ /// For other query types (by hardware addr, by client-id) there can be
+ /// several leases in different subnets (e.g. for mobile clients that
+ /// got address in different subnets). However, for a single address
+ /// there can be only one lease, so this method returns a pointer to
+ /// a single lease, not a container of leases.
+ ///
+ /// @param addr address of the searched lease
+ /// @param subnet_id ID of the subnet the lease must belong to
+ ///
+ /// @return smart pointer to the lease (or NULL if a lease is not found)
+ virtual Lease4Ptr getLease4(isc::asiolink::IOAddress addr) const = 0;
+
+ /// @brief Returns existing IPv4 leases for specified hardware address.
+ ///
+ /// Although in the usual case there will be only one lease, for mobile
+ /// clients or clients with multiple static/fixed/reserved leases there
+ /// can be more than one. Thus return type is a container, not a single
+ /// pointer.
+ ///
+ /// @param hwaddr hardware address of the client
+ ///
+ /// @return lease collection
+ virtual Lease4Collection getLease4(const HWAddr& hwaddr) const = 0;
+
+ /// @brief Returns existing IPv4 leases for specified hardware address
+ /// and a subnet
+ ///
+ /// There can be at most one lease for a given HW address in a single
+ /// pool, so this method with either return a single lease or NULL.
+ ///
+ /// @param hwaddr hardware address of the client
+ /// @param subnet_id identifier of the subnet that lease must belong to
+ ///
+ /// @return a pointer to the lease (or NULL if a lease is not found)
+ virtual Lease4Ptr getLease4(const HWAddr& hwaddr,
+ SubnetID subnet_id) const = 0;
+
+ /// @brief Returns existing IPv4 lease for specified client-id
+ ///
+ /// Although in the usual case there will be only one lease, for mobile
+ /// clients or clients with multiple static/fixed/reserved leases there
+ /// can be more than one. Thus return type is a container, not a single
+ /// pointer.
+ ///
+ /// @param clientid client identifier
+ ///
+ /// @return lease collection
+ virtual Lease4Collection getLease4(const ClientId& clientid) const = 0;
+
+ /// @brief Returns existing IPv4 lease for specified client-id
+ ///
+ /// There can be at most one lease for a given HW address in a single
+ /// pool, so this method with either return a single lease or NULL.
+ ///
+ /// @param clientid client identifier
+ /// @param subnet_id identifier of the subnet that lease must belong to
+ ///
+ /// @return a pointer to the lease (or NULL if a lease is not found)
+ virtual Lease4Ptr getLease4(const ClientId& clientid,
+ SubnetID subnet_id) const = 0;
+
+ /// @brief Returns existing IPv6 lease for a given IPv6 address.
+ ///
+ /// For a given address, we assume that there will be only one lease.
+ /// The assumtion here is that there will not be site or link-local
+ /// addresses used, so there is no way of having address duplication.
+ ///
+ /// @param addr address of the searched lease
+ ///
+ /// @return smart pointer to the lease (or NULL if a lease is not found)
+ virtual Lease6Ptr getLease6(isc::asiolink::IOAddress addr) const = 0;
+
+ /// @brief Returns existing IPv6 leases for a given DUID+IA combination
+ ///
+ /// Although in the usual case there will be only one lease, for mobile
+ /// clients or clients with multiple static/fixed/reserved leases there
+ /// can be more than one. Thus return type is a container, not a single
+ /// pointer.
+ ///
+ /// @param duid client DUID
+ /// @param iaid IA identifier
+ ///
+ /// @return smart pointer to the lease (or NULL if a lease is not found)
+ virtual Lease6Collection getLease6(const DUID& duid,
+ uint32_t iaid) const = 0;
+
+ /// @brief Returns existing IPv6 lease for a given DUID+IA combination
+ ///
+ /// @param duid client DUID
+ /// @param iaid IA identifier
+ /// @param subnet_id subnet id of the subnet the lease belongs to
+ ///
+ /// @return smart pointer to the lease (or NULL if a lease is not found)
+ virtual Lease6Ptr getLease6(const DUID& duid, uint32_t iaid,
+ SubnetID subnet_id) const = 0;
+
+ /// @brief Updates IPv4 lease.
+ ///
+ /// @param lease4 The lease to be updated.
+ ///
+ /// If no such lease is present, an exception will be thrown.
+ virtual void updateLease4(Lease4Ptr lease4) = 0;
+
+ /// @brief Updates IPv4 lease.
+ ///
+ /// @param lease4 The lease to be updated.
+ ///
+ /// If no such lease is present, an exception will be thrown.
+ virtual void updateLease6(Lease6Ptr lease6) = 0;
+
+ /// @brief Deletes a lease.
+ ///
+ /// @param addr IPv4 address of the lease to be deleted.
+ ///
+ /// @return true if deletion was successful, false if no such lease exists
+ virtual bool deleteLease4(uint32_t addr) = 0;
+
+ /// @brief Deletes a lease.
+ ///
+ /// @param addr IPv4 address of the lease to be deleted.
+ ///
+ /// @return true if deletion was successful, false if no such lease exists
+ virtual bool deleteLease6(isc::asiolink::IOAddress addr) = 0;
+
+ /// @brief Returns backend name.
+ ///
+ /// Each backend have specific name, e.g. "mysql" or "sqlite".
+ virtual std::string getName() const = 0;
+
+ /// @brief Returns description of the backend.
+ ///
+ /// This description may be multiline text that describes the backend.
+ virtual std::string getDescription() const = 0;
+
+ /// @brief Returns backend version.
+ ///
+ /// @todo: We will need to implement 3 version functions eventually:
+ /// A. abstract API version
+ /// B. backend version
+ /// C. database version (stored in the database scheme)
+ ///
+ /// and then check that:
+ /// B>=A and B=C (it is ok to have newer backend, as it should be backward
+ /// compatible)
+ /// Also if B>C, some database upgrade procedure may be triggered
+ virtual std::string getVersion() const = 0;
+
+ /// @todo: Add host management here
+ /// As host reservation is outside of scope for 2012, support for hosts
+ /// is currently postponed.
+
+protected:
+ /// @brief returns value of the parameter
+ std::string getParameter(const std::string& name) const;
+
+ /// @brief list of parameters passed in dbconfig
+ ///
+ /// That will be mostly used for storing database name, username,
+ /// password and other parameters required for DB access. It is not
+ /// intended to keep any DHCP-related parameters.
+ std::map<std::string, std::string> parameters_;
+};
+
+}; // end of isc::dhcp namespace
+
+}; // end of isc namespace
diff --git a/src/lib/dhcp/pool.cc b/src/lib/dhcp/pool.cc
index da8a2e3..1cf47a3 100644
--- a/src/lib/dhcp/pool.cc
+++ b/src/lib/dhcp/pool.cc
@@ -30,6 +30,38 @@ bool Pool::inRange(const isc::asiolink::IOAddress& addr) const {
return (first_.smallerEqual(addr) && addr.smallerEqual(last_));
}
+Pool4::Pool4(const isc::asiolink::IOAddress& first,
+ const isc::asiolink::IOAddress& last)
+ :Pool(first, last) {
+ // check if specified address boundaries are sane
+ if (first.getFamily() != AF_INET || last.getFamily() != AF_INET) {
+ isc_throw(BadValue, "Invalid Pool4 address boundaries: not IPv4");
+ }
+
+ if (last < first) {
+ isc_throw(BadValue, "Upper boundary is smaller than lower boundary.");
+ }
+}
+
+Pool4::Pool4(const isc::asiolink::IOAddress& prefix,
+ uint8_t prefix_len)
+ :Pool(prefix, IOAddress("0.0.0.0")) {
+
+ // check if the prefix is sane
+ if (prefix.getFamily() != AF_INET) {
+ isc_throw(BadValue, "Invalid Pool4 address boundaries: not IPv4");
+ }
+
+ // check if the prefix length is sane
+ if (prefix_len == 0 || prefix_len > 32) {
+ isc_throw(BadValue, "Invalid prefix length");
+ }
+
+ // Let's now calculate the last address in defined pool
+ last_ = lastAddrInPrefix(prefix, prefix_len);
+}
+
+
Pool6::Pool6(Pool6Type type, const isc::asiolink::IOAddress& first,
const isc::asiolink::IOAddress& last)
:Pool(first, last), type_(type), prefix_len_(0) {
@@ -52,7 +84,6 @@ Pool6::Pool6(Pool6Type type, const isc::asiolink::IOAddress& first,
// last_ = first;
}
-
// TYPE_PD is not supported by this constructor. first-last style
// parameters are for IA and TA only. There is another dedicated
// constructor for that (it uses prefix/length)
diff --git a/src/lib/dhcp/pool.h b/src/lib/dhcp/pool.h
index 8f6fd86..46f6578 100644
--- a/src/lib/dhcp/pool.h
+++ b/src/lib/dhcp/pool.h
@@ -91,6 +91,33 @@ protected:
std::string comments_;
};
+/// @brief Pool information for IPv4 addresses
+///
+/// It holds information about pool4, i.e. a range of IPv4 address space that
+/// is configured for DHCP allocation.
+class Pool4 : public Pool {
+public:
+ /// @brief the constructor for Pool4 "min-max" style definition
+ ///
+ /// @param first the first address in a pool
+ /// @param last the last address in a pool
+ Pool4(const isc::asiolink::IOAddress& first,
+ const isc::asiolink::IOAddress& last);
+
+ /// @brief the constructor for Pool4 "prefix/len" style definition
+ ///
+ /// @param prefix specifies prefix of the pool
+ /// @param prefix_len specifies length of the prefix of the pool
+ Pool4(const isc::asiolink::IOAddress& prefix,
+ uint8_t prefix_len);
+};
+
+/// @brief a pointer an IPv4 Pool
+typedef boost::shared_ptr<Pool4> Pool4Ptr;
+
+/// @brief a container for IPv4 Pools
+typedef std::vector<Pool4Ptr> Pool4Collection;
+
/// @brief Pool information for IPv6 addresses and prefixes
///
/// It holds information about pool6, i.e. a range of IPv6 address space that
diff --git a/src/lib/dhcp/subnet.cc b/src/lib/dhcp/subnet.cc
index a999295..d0c4cb3 100644
--- a/src/lib/dhcp/subnet.cc
+++ b/src/lib/dhcp/subnet.cc
@@ -41,6 +41,50 @@ bool Subnet::inRange(const isc::asiolink::IOAddress& addr) const {
return ((first <= addr) && (addr <= last));
}
+Subnet4::Subnet4(const isc::asiolink::IOAddress& prefix, uint8_t length,
+ const Triplet<uint32_t>& t1,
+ const Triplet<uint32_t>& t2,
+ const Triplet<uint32_t>& valid_lifetime)
+ :Subnet(prefix, length, t1, t2, valid_lifetime) {
+ if (prefix.getFamily() != AF_INET) {
+ isc_throw(BadValue, "Non IPv4 prefix " << prefix.toText()
+ << " specified in subnet4");
+ }
+}
+
+void Subnet4::addPool4(const Pool4Ptr& pool) {
+ IOAddress first_addr = pool->getFirstAddress();
+ IOAddress last_addr = pool->getLastAddress();
+
+ if (!inRange(first_addr) || !inRange(last_addr)) {
+ isc_throw(BadValue, "Pool4 (" << first_addr.toText() << "-" << last_addr.toText()
+ << " does not belong in this (" << prefix_ << "/" << prefix_len_
+ << ") subnet4");
+ }
+
+ /// @todo: Check that pools do not overlap
+
+ pools_.push_back(pool);
+}
+
+Pool4Ptr Subnet4::getPool4(const isc::asiolink::IOAddress& hint /* = IOAddress("::")*/ ) {
+ Pool4Ptr candidate;
+ for (Pool4Collection::iterator pool = pools_.begin(); pool != pools_.end(); ++pool) {
+
+ // if we won't find anything better, then let's just use the first pool
+ if (!candidate) {
+ candidate = *pool;
+ }
+
+ // if the client provided a pool and there's a pool that hint is valid in,
+ // then let's use that pool
+ if ((*pool)->inRange(hint)) {
+ return (*pool);
+ }
+ }
+ return (candidate);
+}
+
Subnet6::Subnet6(const isc::asiolink::IOAddress& prefix, uint8_t length,
const Triplet<uint32_t>& t1,
const Triplet<uint32_t>& t2,
diff --git a/src/lib/dhcp/subnet.h b/src/lib/dhcp/subnet.h
index aa59010..7e4e0b7 100644
--- a/src/lib/dhcp/subnet.h
+++ b/src/lib/dhcp/subnet.h
@@ -93,6 +93,57 @@ protected:
Triplet<uint32_t> valid_;
};
+/// @brief A configuration holder for IPv4 subnet.
+///
+/// This class represents an IPv4 subnet.
+class Subnet4 : public Subnet {
+public:
+
+ /// @brief Constructor with all parameters
+ ///
+ /// @param prefix Subnet4 prefix
+ /// @param length prefix length
+ /// @param t1 renewal timer (in seconds)
+ /// @param t2 rebind timer (in seconds)
+ /// @param valid_lifetime preferred lifetime of leases (in seconds)
+ Subnet4(const isc::asiolink::IOAddress& prefix, uint8_t length,
+ const Triplet<uint32_t>& t1,
+ const Triplet<uint32_t>& t2,
+ const Triplet<uint32_t>& valid_lifetime);
+
+ /// @brief Returns a pool that specified address belongs to
+ ///
+ /// @param hint address that the returned pool should cover (optional)
+ /// @return Pointer to found pool4 (or NULL)
+ Pool4Ptr getPool4(const isc::asiolink::IOAddress& hint =
+ isc::asiolink::IOAddress("0.0.0.0"));
+
+ /// @brief Adds a new pool.
+ /// @param pool pool to be added
+ void addPool4(const Pool4Ptr& pool);
+
+ /// @brief returns all pools
+ ///
+ /// The reference is only valid as long as the object that
+ /// returned it.
+ ///
+ /// @return a collection of all pools
+ const Pool4Collection& getPools() const {
+ return pools_;
+ }
+
+protected:
+ /// @brief collection of pools in that list
+ Pool4Collection pools_;
+};
+
+/// @brief A pointer to a Subnet4 object
+typedef boost::shared_ptr<Subnet4> Subnet4Ptr;
+
+/// @brief A collection of Subnet6 objects
+typedef std::vector<Subnet4Ptr> Subnet4Collection;
+
+
/// @brief A configuration holder for IPv6 subnet.
///
/// This class represents an IPv6 subnet.
diff --git a/src/lib/dhcp/tests/.gitignore b/src/lib/dhcp/tests/.gitignore
index 313429d..89ea505 100644
--- a/src/lib/dhcp/tests/.gitignore
+++ b/src/lib/dhcp/tests/.gitignore
@@ -1 +1,2 @@
/libdhcp++_unittests
+/libdhcpsrv_unittests
diff --git a/src/lib/dhcp/tests/Makefile.am b/src/lib/dhcp/tests/Makefile.am
index 9fd3492..a15d957 100644
--- a/src/lib/dhcp/tests/Makefile.am
+++ b/src/lib/dhcp/tests/Makefile.am
@@ -28,6 +28,7 @@ TESTS += libdhcp++_unittests libdhcpsrv_unittests
libdhcp___unittests_SOURCES = run_unittests.cc
libdhcp___unittests_SOURCES += libdhcp++_unittest.cc
libdhcp___unittests_SOURCES += iface_mgr_unittest.cc
+libdhcp___unittests_SOURCES += lease_mgr_unittest.cc
libdhcp___unittests_SOURCES += option6_iaaddr_unittest.cc
libdhcp___unittests_SOURCES += option6_ia_unittest.cc
libdhcp___unittests_SOURCES += option6_addrlst_unittest.cc
@@ -35,6 +36,7 @@ libdhcp___unittests_SOURCES += option4_addrlst_unittest.cc
libdhcp___unittests_SOURCES += option_unittest.cc
libdhcp___unittests_SOURCES += pkt6_unittest.cc
libdhcp___unittests_SOURCES += pkt4_unittest.cc
+libdhcp___unittests_SOURCES += duid_unittest.cc
libdhcp___unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES) $(LOG4CPLUS_INCLUDES)
libdhcp___unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
diff --git a/src/lib/dhcp/tests/addr_utilities_unittest.cc b/src/lib/dhcp/tests/addr_utilities_unittest.cc
index 8382827..2ea4e2a 100644
--- a/src/lib/dhcp/tests/addr_utilities_unittest.cc
+++ b/src/lib/dhcp/tests/addr_utilities_unittest.cc
@@ -26,7 +26,67 @@ using namespace std;
using namespace isc::dhcp;
using namespace isc::asiolink;
-TEST(Pool6Test, lastAddrInPrefix) {
+// This test verifies that lastAddrInPrefix is able to handle IPv4 operations.
+TEST(AddrUtilitiesTest, lastAddrInPrefix4) {
+ IOAddress addr1("192.0.2.1");
+
+ // Prefixes rounded to addresses are easy...
+ EXPECT_EQ("192.255.255.255", lastAddrInPrefix(addr1, 8).toText());
+ EXPECT_EQ("192.0.255.255", lastAddrInPrefix(addr1, 16).toText());
+ EXPECT_EQ("192.0.2.255", lastAddrInPrefix(addr1, 24).toText());
+
+ // these are trickier
+ EXPECT_EQ("192.0.2.127", lastAddrInPrefix(addr1, 25).toText());
+ EXPECT_EQ("192.0.2.63", lastAddrInPrefix(addr1, 26).toText());
+ EXPECT_EQ("192.0.2.31", lastAddrInPrefix(addr1, 27).toText());
+ EXPECT_EQ("192.0.2.15", lastAddrInPrefix(addr1, 28).toText());
+ EXPECT_EQ("192.0.2.7", lastAddrInPrefix(addr1, 29).toText());
+ EXPECT_EQ("192.0.2.3", lastAddrInPrefix(addr1, 30).toText());
+
+ // that doesn't make much sense as /31 subnet consists of network address
+ // and a broadcast address, with 0 usable addresses.
+ EXPECT_EQ("192.0.2.1", lastAddrInPrefix(addr1, 31).toText());
+ EXPECT_EQ("192.0.2.1", lastAddrInPrefix(addr1, 32).toText());
+
+ // Let's check extreme cases
+ IOAddress anyAddr("0.0.0.0");
+ EXPECT_EQ("127.255.255.255", lastAddrInPrefix(anyAddr, 1).toText());
+ EXPECT_EQ("255.255.255.255", lastAddrInPrefix(anyAddr, 0).toText());
+ EXPECT_EQ("0.0.0.0", lastAddrInPrefix(anyAddr, 32).toText());
+}
+
+// This test checks if firstAddrInPrefix is able to handle IPv4 operations.
+TEST(AddrUtilitiesTest, firstAddrInPrefix4) {
+ IOAddress addr1("192.223.2.255");
+
+ // Prefixes rounded to addresses are easy...
+ EXPECT_EQ("192.0.0.0", firstAddrInPrefix(addr1, 8).toText());
+ EXPECT_EQ("192.223.0.0", firstAddrInPrefix(addr1, 16).toText());
+ EXPECT_EQ("192.223.2.0", firstAddrInPrefix(addr1, 24).toText());
+
+ // these are trickier
+ EXPECT_EQ("192.223.2.128", firstAddrInPrefix(addr1, 25).toText());
+ EXPECT_EQ("192.223.2.192", firstAddrInPrefix(addr1, 26).toText());
+ EXPECT_EQ("192.223.2.224", firstAddrInPrefix(addr1, 27).toText());
+ EXPECT_EQ("192.223.2.240", firstAddrInPrefix(addr1, 28).toText());
+ EXPECT_EQ("192.223.2.248", firstAddrInPrefix(addr1, 29).toText());
+ EXPECT_EQ("192.223.2.252", firstAddrInPrefix(addr1, 30).toText());
+
+ // that doesn't make much sense as /31 subnet consists of network address
+ // and a broadcast address, with 0 usable addresses.
+ EXPECT_EQ("192.223.2.254", firstAddrInPrefix(addr1, 31).toText());
+ EXPECT_EQ("192.223.2.255", firstAddrInPrefix(addr1, 32).toText());
+
+ // Let's check extreme cases.
+ IOAddress bcast("255.255.255.255");
+ EXPECT_EQ("128.0.0.0", firstAddrInPrefix(bcast, 1).toText());
+ EXPECT_EQ("0.0.0.0", firstAddrInPrefix(bcast, 0).toText());
+ EXPECT_EQ("255.255.255.255", firstAddrInPrefix(bcast, 32).toText());
+
+}
+
+/// This test checks if lastAddrInPrefix properly supports IPv6 operations
+TEST(AddrUtilitiesTest, lastAddrInPrefix6) {
IOAddress addr1("2001:db8:1:1234:5678:abcd:1234:beef");
// Prefixes rounded to nibbles are easy...
@@ -63,7 +123,8 @@ TEST(Pool6Test, lastAddrInPrefix) {
EXPECT_EQ("::", lastAddrInPrefix(anyAddr, 128).toText());
}
-TEST(Pool6Test, firstAddrInPrefix) {
+/// This test checks if firstAddrInPrefix properly supports IPv6 operations
+TEST(AddrUtilitiesTest, firstAddrInPrefix6) {
IOAddress addr1("2001:db8:1:1234:5678:1234:abcd:beef");
// Prefixes rounded to nibbles are easy...
diff --git a/src/lib/dhcp/tests/cfgmgr_unittest.cc b/src/lib/dhcp/tests/cfgmgr_unittest.cc
index a11acbf..462ca9e 100644
--- a/src/lib/dhcp/tests/cfgmgr_unittest.cc
+++ b/src/lib/dhcp/tests/cfgmgr_unittest.cc
@@ -34,6 +34,38 @@ namespace {
// This test verifies if the configuration manager is able to hold and return
// valid leases
+TEST(CfgMgrTest, subnet4) {
+ CfgMgr& cfg_mgr = CfgMgr::instance();
+
+ ASSERT_TRUE(&cfg_mgr != 0);
+
+ Subnet4Ptr subnet1(new Subnet4(IOAddress("192.0.2.0"), 26, 1, 2, 3));
+ Subnet4Ptr subnet2(new Subnet4(IOAddress("192.0.2.64"), 26, 1, 2, 3));
+ Subnet4Ptr subnet3(new Subnet4(IOAddress("192.0.2.128"), 26, 1, 2, 3));
+
+ // there shouldn't be any subnet configured at this stage
+ EXPECT_EQ( Subnet4Ptr(), cfg_mgr.getSubnet4(IOAddress("192.0.2.0")));
+
+ cfg_mgr.addSubnet4(subnet1);
+
+ // Now we have only one subnet, any request will be served from it
+ EXPECT_EQ(subnet1, cfg_mgr.getSubnet4(IOAddress("192.0.2.63")));
+
+ // Now we add more subnets and check that both old and new subnets
+ // are accessible.
+ cfg_mgr.addSubnet4(subnet2);
+ cfg_mgr.addSubnet4(subnet3);
+
+ EXPECT_EQ(subnet3, cfg_mgr.getSubnet4(IOAddress("192.0.2.191")));
+ EXPECT_EQ(subnet1, cfg_mgr.getSubnet4(IOAddress("192.0.2.15")));
+ EXPECT_EQ(subnet2, cfg_mgr.getSubnet4(IOAddress("192.0.2.85")));
+
+ // Try to find an address that does not belong to any subnet
+ EXPECT_EQ(Subnet4Ptr(), cfg_mgr.getSubnet4(IOAddress("192.0.2.192")));
+}
+
+// This test verifies if the configuration manager is able to hold and return
+// valid leases
TEST(CfgMgrTest, subnet6) {
CfgMgr& cfg_mgr = CfgMgr::instance();
@@ -58,6 +90,10 @@ TEST(CfgMgrTest, subnet6) {
EXPECT_EQ(subnet2, cfg_mgr.getSubnet6(IOAddress("3000::dead:beef")));
EXPECT_EQ(Subnet6Ptr(), cfg_mgr.getSubnet6(IOAddress("5000::1")));
+ cfg_mgr.deleteSubnets6();
+ EXPECT_EQ(Subnet6Ptr(), cfg_mgr.getSubnet6(IOAddress("200::123")));
+ EXPECT_EQ(Subnet6Ptr(), cfg_mgr.getSubnet6(IOAddress("3000::123")));
+ EXPECT_EQ(Subnet6Ptr(), cfg_mgr.getSubnet6(IOAddress("4000::123")));
}
} // end of anonymous namespace
diff --git a/src/lib/dhcp/tests/duid_unittest.cc b/src/lib/dhcp/tests/duid_unittest.cc
new file mode 100644
index 0000000..aaf6d91
--- /dev/null
+++ b/src/lib/dhcp/tests/duid_unittest.cc
@@ -0,0 +1,169 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <config.h>
+#include <iostream>
+#include <sstream>
+#include <arpa/inet.h>
+#include <gtest/gtest.h>
+#include <exceptions/exceptions.h>
+#include <boost/scoped_ptr.hpp>
+#include <asiolink/io_address.h>
+#include <dhcp/duid.h>
+
+using namespace std;
+using namespace isc;
+using namespace isc::dhcp;
+using namespace isc::asiolink;
+
+// don't import the entire boost namespace. It will unexpectedly hide uint8_t
+// for some systems.
+using boost::scoped_ptr;
+
+namespace {
+
+// This test verifies if the constructors are working as expected
+// and process passed parameters.
+TEST(DuidTest, constructor) {
+
+ uint8_t data1[] = {0, 1, 2, 3, 4, 5, 6};
+
+ vector<uint8_t> data2(data1, data1 + sizeof(data1));
+
+ scoped_ptr<DUID> duid1(new DUID(data1, sizeof(data1)));
+ scoped_ptr<DUID> duid2(new DUID(data2));
+
+ vector<uint8_t> vecdata = duid1->getDuid();
+ EXPECT_TRUE(data2 == vecdata);
+ EXPECT_EQ(DUID::DUID_LLT, duid1->getType());
+
+ vecdata = duid2->getDuid();
+ EXPECT_TRUE(data2 == vecdata);
+
+ EXPECT_EQ(DUID::DUID_LLT, duid2->getType());
+}
+
+// This test verifies if DUID size restrictions are implemented
+// properly.
+TEST(DuidTest, size) {
+ const int MAX_DUID_SIZE = 128;
+ uint8_t data[MAX_DUID_SIZE + 1];
+ vector<uint8_t> data2;
+ for (uint8_t i = 0; i < MAX_DUID_SIZE + 1; ++i) {
+ data[i] = i;
+ if (i < MAX_DUID_SIZE)
+ data2.push_back(i);
+ }
+ ASSERT_EQ(data2.size(), MAX_DUID_SIZE);
+
+ scoped_ptr<DUID> duidmaxsize1(new DUID(data, MAX_DUID_SIZE));
+ scoped_ptr<DUID> duidmaxsize2(new DUID(data2));
+
+ EXPECT_THROW(
+ scoped_ptr<DUID> toolarge1(new DUID(data, MAX_DUID_SIZE + 1)),
+ OutOfRange);
+
+ // that's one too much
+ data2.push_back(128);
+
+ EXPECT_THROW(
+ scoped_ptr<DUID> toolarge2(new DUID(data2)),
+ OutOfRange);
+}
+
+// This test verifies if the implementation supports all defined
+// DUID types.
+TEST(DuidTest, getType) {
+ uint8_t llt[] = {0, 1, 2, 3, 4, 5, 6};
+ uint8_t en[] = {0, 2, 2, 3, 4, 5, 6};
+ uint8_t ll[] = {0, 3, 2, 3, 4, 5, 6};
+ uint8_t uuid[] = {0, 4, 2, 3, 4, 5, 6};
+ uint8_t invalid[] = {0,55, 2, 3, 4, 5, 6};
+
+ scoped_ptr<DUID> duid_llt(new DUID(llt, sizeof(llt)));
+ scoped_ptr<DUID> duid_en(new DUID(en, sizeof(en)));
+ scoped_ptr<DUID> duid_ll(new DUID(ll, sizeof(ll)));
+ scoped_ptr<DUID> duid_uuid(new DUID(uuid, sizeof(uuid)));
+ scoped_ptr<DUID> duid_invalid(new DUID(invalid, sizeof(invalid)));
+
+ EXPECT_EQ(DUID::DUID_LLT, duid_llt->getType());
+ EXPECT_EQ(DUID::DUID_EN, duid_en->getType());
+ EXPECT_EQ(DUID::DUID_LL, duid_ll->getType());
+ EXPECT_EQ(DUID::DUID_UUID, duid_uuid->getType());
+ EXPECT_EQ(DUID::DUID_UNKNOWN, duid_invalid->getType());
+}
+
+// This test checks if the comparison operators are sane.
+TEST(DuidTest, operators) {
+ uint8_t data1[] = {0, 1, 2, 3, 4, 5, 6};
+ uint8_t data2[] = {0, 1, 2, 3, 4};
+ uint8_t data3[] = {0, 1, 2, 3, 4, 5, 7}; // last digit different
+ uint8_t data4[] = {0, 1, 2, 3, 4, 5, 6}; // the same as 1
+
+ scoped_ptr<DUID> duid1(new DUID(data1, sizeof(data1)));
+ scoped_ptr<DUID> duid2(new DUID(data2, sizeof(data2)));
+ scoped_ptr<DUID> duid3(new DUID(data3, sizeof(data3)));
+ scoped_ptr<DUID> duid4(new DUID(data4, sizeof(data4)));
+
+ EXPECT_TRUE(*duid1 == *duid4);
+ EXPECT_FALSE(*duid1 == *duid2);
+ EXPECT_FALSE(*duid1 == *duid3);
+
+ EXPECT_FALSE(*duid1 != *duid4);
+ EXPECT_TRUE(*duid1 != *duid2);
+ EXPECT_TRUE(*duid1 != *duid3);
+}
+
+// This test verifies if the ClientId constructors are working properly
+// and passed parameters are used
+TEST(ClientIdTest, constructor) {
+ IOAddress addr2("192.0.2.1");
+ IOAddress addr3("2001:db8:1::1");
+
+ uint8_t data1[] = {0, 1, 2, 3, 4, 5, 6};
+ vector<uint8_t> data2(data1, data1 + sizeof(data1));
+
+ // checks for C-style construtor (uint8_t * + len)
+ scoped_ptr<ClientId> id1(new ClientId(data1, sizeof(data1)));
+ vector<uint8_t> vecdata = id1->getClientId();
+ EXPECT_TRUE(data2 == vecdata);
+
+ // checks for vector-based constructor
+ scoped_ptr<ClientId> id2(new ClientId(data2));
+ vecdata = id2->getClientId();
+ EXPECT_TRUE(data2 == vecdata);
+}
+
+// This test checks if the comparison operators are sane.
+TEST(ClientIdTest, operators) {
+ uint8_t data1[] = {0, 1, 2, 3, 4, 5, 6};
+ uint8_t data2[] = {0, 1, 2, 3, 4};
+ uint8_t data3[] = {0, 1, 2, 3, 4, 5, 7}; // last digit different
+ uint8_t data4[] = {0, 1, 2, 3, 4, 5, 6}; // the same as 1
+
+ scoped_ptr<ClientId> id1(new ClientId(data1, sizeof(data1)));
+ scoped_ptr<ClientId> id2(new ClientId(data2, sizeof(data2)));
+ scoped_ptr<ClientId> id3(new ClientId(data3, sizeof(data3)));
+ scoped_ptr<ClientId> id4(new ClientId(data4, sizeof(data4)));
+
+ EXPECT_TRUE(*id1 == *id4);
+ EXPECT_FALSE(*id1 == *id2);
+ EXPECT_FALSE(*id1 == *id3);
+
+ EXPECT_FALSE(*id1 != *id4);
+ EXPECT_TRUE(*id1 != *id2);
+ EXPECT_TRUE(*id1 != *id3);
+}
+
+} // end of anonymous namespace
diff --git a/src/lib/dhcp/tests/iface_mgr_unittest.cc b/src/lib/dhcp/tests/iface_mgr_unittest.cc
index 2c7b86b..462910b 100644
--- a/src/lib/dhcp/tests/iface_mgr_unittest.cc
+++ b/src/lib/dhcp/tests/iface_mgr_unittest.cc
@@ -1096,7 +1096,7 @@ void parse_ifconfig(const std::string& textFile, IfaceMgr::IfaceCollection& ifac
addr = addr.substr(0, addr.find_first_of(" "));
IOAddress a(addr);
iface->addAddress(a);
- } else if(line.find("Metric")) {
+ } else if(line.find("Metric") != string::npos) {
// flags
if (line.find("UP") != string::npos) {
iface->flag_up_ = true;
diff --git a/src/lib/dhcp/tests/lease_mgr_unittest.cc b/src/lib/dhcp/tests/lease_mgr_unittest.cc
new file mode 100644
index 0000000..97659a1
--- /dev/null
+++ b/src/lib/dhcp/tests/lease_mgr_unittest.cc
@@ -0,0 +1,296 @@
+// Copyright (C) 2011-2012 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <config.h>
+#include <iostream>
+#include <sstream>
+#include <gtest/gtest.h>
+
+#include <asiolink/io_address.h>
+#include <dhcp/lease_mgr.h>
+
+using namespace std;
+using namespace isc;
+using namespace isc::asiolink;
+using namespace isc::dhcp;
+
+// This is a concrete implementation of a Lease database.
+// It does not do anything useful now, and is used for abstract LeaseMgr
+// class testing. It may later evolve into more useful backend if the
+// need arises. We can reuse code from memfile benchmark. See code in
+// tests/tools/dhcp-ubench/memfile_bench.{cc|h}
+class Memfile_LeaseMgr : public LeaseMgr {
+public:
+
+ /// @brief The sole lease manager constructor
+ ///
+ /// dbconfig is a generic way of passing parameters. Parameters
+ /// are passed in the "name=value" format, separated by spaces.
+ /// Values may be enclosed in double quotes, if needed.
+ ///
+ /// @param dbconfig database configuration
+ Memfile_LeaseMgr(const std::string& dbconfig);
+
+ /// @brief Destructor (closes file)
+ virtual ~Memfile_LeaseMgr();
+
+ /// @brief Adds an IPv4 lease.
+ ///
+ /// @param lease lease to be added
+ virtual bool addLease(Lease4Ptr lease);
+
+ /// @brief Adds an IPv6 lease.
+ ///
+ /// @param lease lease to be added
+ virtual bool addLease(Lease6Ptr lease);
+
+ /// @brief Returns existing IPv4 lease for specified IPv4 address.
+ ///
+ /// @param addr address of the searched lease
+ ///
+ /// @return a collection of leases
+ virtual Lease4Ptr getLease4(isc::asiolink::IOAddress addr) const;
+
+ /// @brief Returns existing IPv4 lease for specific address and subnet
+ /// @param addr address of the searched lease
+ /// @param subnet_id ID of the subnet the lease must belong to
+ ///
+ /// @return smart pointer to the lease (or NULL if a lease is not found)
+ virtual Lease4Ptr getLease4(isc::asiolink::IOAddress addr,
+ SubnetID subnet_id) const;
+
+ /// @brief Returns existing IPv4 leases for specified hardware address.
+ ///
+ /// Although in the usual case there will be only one lease, for mobile
+ /// clients or clients with multiple static/fixed/reserved leases there
+ /// can be more than one. Thus return type is a container, not a single
+ /// pointer.
+ ///
+ /// @param hwaddr hardware address of the client
+ ///
+ /// @return lease collection
+ virtual Lease4Collection getLease4(const HWAddr& hwaddr) const;
+
+ /// @brief Returns existing IPv4 leases for specified hardware address
+ /// and a subnet
+ ///
+ /// There can be at most one lease for a given HW address in a single
+ /// pool, so this method with either return a single lease or NULL.
+ ///
+ /// @param hwaddr hardware address of the client
+ /// @param subnet_id identifier of the subnet that lease must belong to
+ ///
+ /// @return a pointer to the lease (or NULL if a lease is not found)
+ virtual Lease4Ptr getLease4(const HWAddr& hwaddr,
+ SubnetID subnet_id) const;
+
+ /// @brief Returns existing IPv4 lease for specified client-id
+ ///
+ /// @param clientid client identifier
+ virtual Lease4Collection getLease4(const ClientId& clientid) const;
+
+ /// @brief Returns existing IPv4 lease for specified client-id
+ ///
+ /// There can be at most one lease for a given HW address in a single
+ /// pool, so this method with either return a single lease or NULL.
+ ///
+ /// @param clientid client identifier
+ /// @param subnet_id identifier of the subnet that lease must belong to
+ ///
+ /// @return a pointer to the lease (or NULL if a lease is not found)
+ virtual Lease4Ptr getLease4(const ClientId& clientid,
+ SubnetID subnet_id) const;
+
+ /// @brief Returns existing IPv6 lease for a given IPv6 address.
+ ///
+ /// @param addr address of the searched lease
+ ///
+ /// @return smart pointer to the lease (or NULL if a lease is not found)
+ Lease6Ptr getLease6(isc::asiolink::IOAddress addr) const;
+
+ /// @brief Returns existing IPv6 lease for a given DUID+IA combination
+ ///
+ /// @param duid client DUID
+ /// @param iaid IA identifier
+ ///
+ /// @return collection of IPv6 leases
+ Lease6Collection getLease6(const DUID& duid, uint32_t iaid) const;
+
+ /// @brief Returns existing IPv6 lease for a given DUID+IA combination
+ ///
+ /// @param duid client DUID
+ /// @param iaid IA identifier
+ /// @param subnet_id identifier of the subnet the lease must belong to
+ ///
+ /// @return smart pointer to the lease (or NULL if a lease is not found)
+ Lease6Ptr getLease6(const DUID& duid, uint32_t iaid, SubnetID subnet_id) const;
+
+ /// @brief Updates IPv4 lease.
+ ///
+ /// @param lease4 The lease to be updated.
+ ///
+ /// If no such lease is present, an exception will be thrown.
+ void updateLease4(Lease4Ptr lease4);
+
+ /// @brief Updates IPv4 lease.
+ ///
+ /// @param lease4 The lease to be updated.
+ ///
+ /// If no such lease is present, an exception will be thrown.
+ void updateLease6(Lease6Ptr lease6);
+
+ /// @brief Deletes a lease.
+ ///
+ /// @param addr IPv4 address of the lease to be deleted.
+ ///
+ /// @return true if deletion was successful, false if no such lease exists
+ bool deleteLease4(uint32_t addr);
+
+ /// @brief Deletes a lease.
+ ///
+ /// @param addr IPv4 address of the lease to be deleted.
+ ///
+ /// @return true if deletion was successful, false if no such lease exists
+ bool deleteLease6(isc::asiolink::IOAddress addr);
+
+ /// @brief Returns backend name.
+ ///
+ /// Each backend have specific name, e.g. "mysql" or "sqlite".
+ std::string getName() const { return "memfile"; }
+
+ /// @brief Returns description of the backend.
+ ///
+ /// This description may be multiline text that describes the backend.
+ std::string getDescription() const;
+
+ /// @brief Returns backend version.
+ std::string getVersion() const { return "test-version"; }
+
+ using LeaseMgr::getParameter;
+
+protected:
+
+
+};
+
+Memfile_LeaseMgr::Memfile_LeaseMgr(const std::string& dbconfig)
+ : LeaseMgr(dbconfig) {
+}
+
+Memfile_LeaseMgr::~Memfile_LeaseMgr() {
+}
+
+bool Memfile_LeaseMgr::addLease(boost::shared_ptr<isc::dhcp::Lease4>) {
+ return (false);
+}
+
+bool Memfile_LeaseMgr::addLease(boost::shared_ptr<isc::dhcp::Lease6>) {
+ return (false);
+}
+
+Lease4Ptr Memfile_LeaseMgr::getLease4(isc::asiolink::IOAddress) const {
+ return (Lease4Ptr());
+}
+
+Lease4Collection Memfile_LeaseMgr::getLease4(const HWAddr& ) const {
+ return (Lease4Collection());
+}
+
+Lease4Ptr Memfile_LeaseMgr::getLease4(isc::asiolink::IOAddress ,
+ SubnetID) const {
+ return (Lease4Ptr());
+}
+
+Lease4Ptr Memfile_LeaseMgr::getLease4(const HWAddr&,
+ SubnetID) const {
+ return (Lease4Ptr());
+}
+
+
+Lease4Ptr Memfile_LeaseMgr::getLease4(const ClientId&,
+ SubnetID) const {
+ return (Lease4Ptr());
+}
+
+Lease4Collection Memfile_LeaseMgr::getLease4(const ClientId& ) const {
+ return (Lease4Collection());
+}
+
+Lease6Ptr Memfile_LeaseMgr::getLease6(isc::asiolink::IOAddress) const {
+ return (Lease6Ptr());
+}
+
+Lease6Collection Memfile_LeaseMgr::getLease6(const DUID& , uint32_t ) const {
+ return (Lease6Collection());
+}
+
+Lease6Ptr Memfile_LeaseMgr::getLease6(const DUID&, uint32_t,
+ SubnetID) const {
+ return (Lease6Ptr());
+}
+
+void Memfile_LeaseMgr::updateLease4(Lease4Ptr ) {
+}
+
+void Memfile_LeaseMgr::updateLease6(Lease6Ptr ) {
+
+}
+
+bool Memfile_LeaseMgr::deleteLease4(uint32_t ) {
+ return (false);
+}
+
+bool Memfile_LeaseMgr::deleteLease6(isc::asiolink::IOAddress ) {
+ return (false);
+}
+
+std::string Memfile_LeaseMgr::getDescription() const {
+ return (string("This is a dummy memfile backend implementation.\n"
+ "It does not offer any useful lease management and its only\n"
+ "purpose is to test abstract lease manager API."));
+}
+
+namespace {
+// empty class for now, but may be extended once Addr6 becomes bigger
+class LeaseMgrTest : public ::testing::Test {
+public:
+ LeaseMgrTest() {
+ }
+};
+
+// This test checks if the LeaseMgr can be instantiated and that it
+// parses parameters string properly.
+TEST_F(LeaseMgrTest, constructor) {
+
+ // should not throw any exceptions here
+ Memfile_LeaseMgr * leaseMgr = new Memfile_LeaseMgr("");
+ delete leaseMgr;
+
+ leaseMgr = new Memfile_LeaseMgr("param1=value1 param2=value2");
+
+ EXPECT_EQ("value1", leaseMgr->getParameter("param1"));
+ EXPECT_EQ("value2", leaseMgr->getParameter("param2"));
+ EXPECT_THROW(leaseMgr->getParameter("param3"), BadValue);
+
+ delete leaseMgr;
+}
+
+// There's no point in calling any other methods in LeaseMgr, as they
+// are purely virtual, so we would only call Memfile_LeaseMgr methods.
+// Those methods are just stubs that does not return anything.
+// It seems likely that we will need to extend the memfile code for
+// allocation engine tests, so we may implement tests that call
+// Memfile_LeaseMgr methods then.
+
+}; // end of anonymous namespace
diff --git a/src/lib/dhcp/tests/pool_unittest.cc b/src/lib/dhcp/tests/pool_unittest.cc
index 61d4c4a..63d4289 100644
--- a/src/lib/dhcp/tests/pool_unittest.cc
+++ b/src/lib/dhcp/tests/pool_unittest.cc
@@ -27,6 +27,79 @@ using namespace isc::asiolink;
namespace {
+TEST(Pool4Test, constructor_first_last) {
+
+ // let's construct 192.0.2.1-192.0.2.255 pool
+ Pool4 pool1(IOAddress("192.0.2.1"), IOAddress("192.0.2.255"));
+
+ EXPECT_EQ(IOAddress("192.0.2.1"), pool1.getFirstAddress());
+ EXPECT_EQ(IOAddress("192.0.2.255"), pool1.getLastAddress());
+
+ // This is Pool4, IPv6 addresses do not belong here
+ EXPECT_THROW(Pool6(Pool6::TYPE_IA, IOAddress("2001:db8::1"),
+ IOAddress("192.168.0.5")), BadValue);
+ EXPECT_THROW(Pool6(Pool6::TYPE_IA, IOAddress("192.168.0.2"),
+ IOAddress("2001:db8::1")), BadValue);
+
+ // Should throw. Range should be 192.0.2.1-192.0.2.2, not
+ // the other way around.
+ EXPECT_THROW(Pool6(Pool6::TYPE_IA, IOAddress("192.0.2.2"),
+ IOAddress("192.0.2.1")), BadValue);
+}
+
+TEST(Pool4Test, constructor_prefix_len) {
+
+ // let's construct 2001:db8:1::/96 pool
+ Pool4 pool1(IOAddress("192.0.2.0"), 25);
+
+ EXPECT_EQ("192.0.2.0", pool1.getFirstAddress().toText());
+ EXPECT_EQ("192.0.2.127", pool1.getLastAddress().toText());
+
+ // No such thing as /33 prefix
+ EXPECT_THROW(Pool4(IOAddress("192.0.2.1"), 33), BadValue);
+
+ // /0 prefix does not make sense
+ EXPECT_THROW(Pool4(IOAddress("192.0.2.0"), 0), BadValue);
+
+ // This is Pool6, IPv4 addresses do not belong here
+ EXPECT_THROW(Pool4(IOAddress("2001:db8::1"), 20), BadValue);
+}
+
+TEST(Pool4Test, in_range) {
+ Pool4 pool1(IOAddress("192.0.2.10"), IOAddress("192.0.2.20"));
+
+ EXPECT_FALSE(pool1.inRange(IOAddress("192.0.2.0")));
+ EXPECT_TRUE(pool1.inRange(IOAddress("192.0.2.10")));
+ EXPECT_TRUE(pool1.inRange(IOAddress("192.0.2.17")));
+ EXPECT_TRUE(pool1.inRange(IOAddress("192.0.2.20")));
+ EXPECT_FALSE(pool1.inRange(IOAddress("192.0.2.21")));
+ EXPECT_FALSE(pool1.inRange(IOAddress("192.0.2.255")));
+ EXPECT_FALSE(pool1.inRange(IOAddress("255.255.255.255")));
+ EXPECT_FALSE(pool1.inRange(IOAddress("0.0.0.0")));
+}
+
+// This test creates 100 pools and verifies that their IDs are unique.
+TEST(Pool4Test, unique_id) {
+
+ const int num_pools = 100;
+ std::vector<Pool4Ptr> pools;
+
+ for (int i = 0; i < num_pools; ++i) {
+ pools.push_back(Pool4Ptr(new Pool4(IOAddress("192.0.2.0"),
+ IOAddress("192.0.2.255"))));
+ }
+
+ for (int i = 0; i < num_pools; ++i) {
+ for (int j = i + 1; j < num_pools; ++j) {
+ if (pools[i]->getId() == pools[j]->getId()) {
+ FAIL() << "Pool-ids must be unique";
+ }
+ }
+ }
+
+}
+
+
TEST(Pool6Test, constructor_first_last) {
// let's construct 2001:db8:1:: - 2001:db8:1::ffff:ffff:ffff:ffff pool
diff --git a/src/lib/dhcp/tests/subnet_unittest.cc b/src/lib/dhcp/tests/subnet_unittest.cc
index 6afebb8..6c26106 100644
--- a/src/lib/dhcp/tests/subnet_unittest.cc
+++ b/src/lib/dhcp/tests/subnet_unittest.cc
@@ -29,6 +29,83 @@ using namespace isc::asiolink;
namespace {
+TEST(Subnet4Test, constructor) {
+ EXPECT_NO_THROW(Subnet4 subnet1(IOAddress("192.0.2.2"), 16,
+ 1, 2, 3));
+
+ EXPECT_THROW(Subnet4 subnet2(IOAddress("192.0.2.0"), 33, 1, 2, 3),
+ BadValue); // invalid prefix length
+ EXPECT_THROW(Subnet4 subnet3(IOAddress("2001:db8::1"), 24, 1, 2, 3),
+ BadValue); // IPv6 addresses are not allowed in Subnet4
+}
+
+TEST(Subnet4Test, in_range) {
+ Subnet4 subnet(IOAddress("192.0.2.1"), 24, 1000, 2000, 3000);
+
+ EXPECT_EQ(1000, subnet.getT1());
+ EXPECT_EQ(2000, subnet.getT2());
+ EXPECT_EQ(3000, subnet.getValid());
+
+ EXPECT_FALSE(subnet.inRange(IOAddress("192.0.0.0")));
+ EXPECT_TRUE(subnet.inRange(IOAddress("192.0.2.0")));
+ EXPECT_TRUE(subnet.inRange(IOAddress("192.0.2.1")));
+ EXPECT_TRUE(subnet.inRange(IOAddress("192.0.2.255")));
+ EXPECT_FALSE(subnet.inRange(IOAddress("192.0.3.0")));
+ EXPECT_FALSE(subnet.inRange(IOAddress("0.0.0.0")));
+ EXPECT_FALSE(subnet.inRange(IOAddress("255.255.255.255")));
+}
+
+TEST(Subnet4Test, Pool4InSubnet4) {
+
+ Subnet4Ptr subnet(new Subnet4(IOAddress("192.1.2.0"), 24, 1, 2, 3));
+
+ Pool4Ptr pool1(new Pool4(IOAddress("192.1.2.0"), 25));
+ Pool4Ptr pool2(new Pool4(IOAddress("192.1.2.128"), 26));
+ Pool4Ptr pool3(new Pool4(IOAddress("192.1.2.192"), 30));
+
+ subnet->addPool4(pool1);
+
+ // If there's only one pool, get that pool
+ Pool4Ptr mypool = subnet->getPool4();
+ EXPECT_EQ(mypool, pool1);
+
+
+ subnet->addPool4(pool2);
+ subnet->addPool4(pool3);
+
+ // If there are more than one pool and we didn't provide hint, we
+ // should get the first pool
+ mypool = subnet->getPool4();
+
+ EXPECT_EQ(mypool, pool1);
+
+ // If we provide a hint, we should get a pool that this hint belongs to
+ mypool = subnet->getPool4(IOAddress("192.1.2.195"));
+
+ EXPECT_EQ(mypool, pool3);
+
+}
+
+TEST(Subnet4Test, Subnet4_Pool4_checks) {
+
+ Subnet4Ptr subnet(new Subnet4(IOAddress("192.0.2.0"), 8, 1, 2, 3));
+
+ // this one is in subnet
+ Pool4Ptr pool1(new Pool4(IOAddress("192.255.0.0"), 16));
+ subnet->addPool4(pool1);
+
+ // this one is larger than the subnet!
+ Pool4Ptr pool2(new Pool4(IOAddress("193.0.0.0"), 24));
+
+ EXPECT_THROW(subnet->addPool4(pool2), BadValue);
+
+ // this one is totally out of blue
+ Pool4Ptr pool3(new Pool4(IOAddress("1.2.3.4"), 16));
+ EXPECT_THROW(subnet->addPool4(pool3), BadValue);
+}
+
+// Tests for Subnet6
+
TEST(Subnet6Test, constructor) {
EXPECT_NO_THROW(Subnet6 subnet1(IOAddress("2001:db8:1::"), 64,
@@ -48,7 +125,6 @@ TEST(Subnet6Test, in_range) {
EXPECT_EQ(3000, subnet.getPreferred());
EXPECT_EQ(4000, subnet.getValid());
-
EXPECT_FALSE(subnet.inRange(IOAddress("2001:db8:0:ffff:ffff:ffff:ffff:ffff")));
EXPECT_TRUE(subnet.inRange(IOAddress("2001:db8:1::0")));
EXPECT_TRUE(subnet.inRange(IOAddress("2001:db8:1::1")));
@@ -106,7 +182,9 @@ TEST(Subnet6Test, Subnet6_Pool6_checks) {
Pool6Ptr pool3(new Pool6(Pool6::TYPE_IA, IOAddress("3000::"), 16));
EXPECT_THROW(subnet->addPool6(pool3), BadValue);
-}
+ Pool6Ptr pool4(new Pool6(Pool6::TYPE_IA, IOAddress("4001:db8:1::"), 80));
+ EXPECT_THROW(subnet->addPool6(pool4), BadValue);
+}
};
diff --git a/src/lib/dhcp/triplet.h b/src/lib/dhcp/triplet.h
index d45f003..d9388fe 100644
--- a/src/lib/dhcp/triplet.h
+++ b/src/lib/dhcp/triplet.h
@@ -12,6 +12,9 @@
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
+#ifndef TRIPLET_H
+#define TRIPLET_H
+
#include <exceptions/exceptions.h>
namespace isc {
@@ -108,3 +111,5 @@ protected:
} // namespace isc::dhcp
} // namespace isc
+
+#endif // ifdef TRIPLET_H
diff --git a/src/lib/dns/Makefile.am b/src/lib/dns/Makefile.am
index 38809e0..977854d 100644
--- a/src/lib/dns/Makefile.am
+++ b/src/lib/dns/Makefile.am
@@ -107,7 +107,6 @@ libb10_dns___la_SOURCES += rdatafields.h rdatafields.cc
libb10_dns___la_SOURCES += rrclass.cc
libb10_dns___la_SOURCES += rrparamregistry.h
libb10_dns___la_SOURCES += rrset.h rrset.cc
-libb10_dns___la_SOURCES += rrsetlist.h rrsetlist.cc
libb10_dns___la_SOURCES += rrttl.h rrttl.cc
libb10_dns___la_SOURCES += rrtype.cc
libb10_dns___la_SOURCES += question.h question.cc
@@ -149,11 +148,11 @@ libdns___include_HEADERS = \
messagerenderer.h \
name.h \
question.h \
+ opcode.h \
rcode.h \
rdata.h \
rrparamregistry.h \
rrset.h \
- rrsetlist.h \
rrttl.h \
tsigkey.h
# Purposely not installing these headers:
diff --git a/src/lib/dns/benchmarks/message_renderer_bench.cc b/src/lib/dns/benchmarks/message_renderer_bench.cc
index 6376498..abf3192 100644
--- a/src/lib/dns/benchmarks/message_renderer_bench.cc
+++ b/src/lib/dns/benchmarks/message_renderer_bench.cc
@@ -39,7 +39,7 @@ public:
renderer_(NULL),
names_(names)
{}
- MessageRendererBenchMark() {
+ ~MessageRendererBenchMark() {
delete renderer_;
}
unsigned int run() {
diff --git a/src/lib/dns/benchmarks/rdatarender_bench.cc b/src/lib/dns/benchmarks/rdatarender_bench.cc
index 38ee2ac..65576ee 100644
--- a/src/lib/dns/benchmarks/rdatarender_bench.cc
+++ b/src/lib/dns/benchmarks/rdatarender_bench.cc
@@ -42,9 +42,10 @@ template <typename T>
class RdataRenderBenchMark {
public:
RdataRenderBenchMark(const vector<T>& dataset) :
- dataset_(dataset)
+ dataset_(dataset),
+ renderer_(NULL)
{}
- RdataRenderBenchMark() {
+ ~RdataRenderBenchMark() {
delete renderer_;
}
unsigned int run() {
diff --git a/src/lib/dns/python/edns_python.cc b/src/lib/dns/python/edns_python.cc
index 8f0f1a4..e9d54c1 100644
--- a/src/lib/dns/python/edns_python.cc
+++ b/src/lib/dns/python/edns_python.cc
@@ -269,7 +269,6 @@ EDNS_createFromRR(const s_EDNS* null_self, PyObject* args) {
const PyObject* rrtype;
const PyObject* rrttl;
const PyObject* rdata;
- s_EDNS* edns_obj = NULL;
assert(null_self == NULL);
@@ -277,7 +276,7 @@ EDNS_createFromRR(const s_EDNS* null_self, PyObject* args) {
&rrclass_type, &rrclass, &rrtype_type, &rrtype,
&rrttl_type, &rrttl, &rdata_type, &rdata)) {
uint8_t extended_rcode;
- edns_obj = PyObject_New(s_EDNS, &edns_type);
+ s_EDNS* edns_obj = PyObject_New(s_EDNS, &edns_type);
if (edns_obj == NULL) {
return (NULL);
}
diff --git a/src/lib/dns/rdata/generic/afsdb_18.cc b/src/lib/dns/rdata/generic/afsdb_18.cc
index 6afc4de..ec76ee0 100644
--- a/src/lib/dns/rdata/generic/afsdb_18.cc
+++ b/src/lib/dns/rdata/generic/afsdb_18.cc
@@ -57,7 +57,6 @@ AFSDB::AFSDB(const std::string& afsdb_str) :
try {
const uint32_t subtype = tokenToNum<int32_t, 16>(getToken(iss));
const Name servername(getToken(iss));
- string server;
if (!iss.eof()) {
isc_throw(InvalidRdataText, "Unexpected input for AFSDB"
diff --git a/src/lib/dns/rrsetlist.cc b/src/lib/dns/rrsetlist.cc
deleted file mode 100644
index fcdcfbb..0000000
--- a/src/lib/dns/rrsetlist.cc
+++ /dev/null
@@ -1,60 +0,0 @@
-// Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC")
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
-
-#include <vector>
-
-#include <boost/foreach.hpp>
-
-#include <exceptions/exceptions.h>
-
-#include <dns/rrclass.h>
-#include <dns/rrtype.h>
-#include <dns/rrset.h>
-#include <dns/rrsetlist.h>
-
-namespace isc {
-namespace dns {
-
-void
-RRsetList::addRRset(RRsetPtr rrsetptr) {
- ConstRRsetPtr rrset_found = findRRset(rrsetptr->getType(),
- rrsetptr->getClass());
- if (rrset_found != NULL) {
- isc_throw(DuplicateRRset, "RRset is being doubly added to RRsetList: "
- "type=" << rrsetptr->getType() << ", class=" <<
- rrsetptr->getClass());
- }
- rrsets_.push_back(rrsetptr);
-}
-
-void
-RRsetList::append(RRsetList& source) {
- BOOST_FOREACH(RRsetPtr rrset, source) {
- addRRset(rrset);
- }
-}
-
-RRsetPtr
-RRsetList::findRRset(const RRType& rrtype, const RRClass& rrclass) {
- BOOST_FOREACH(RRsetPtr rrsetptr, rrsets_) {
- if ((rrsetptr->getClass() == rrclass) &&
- (rrsetptr->getType() == rrtype)) {
- return (rrsetptr);
- }
- }
- return (RRsetPtr());
-}
-
-}
-}
diff --git a/src/lib/dns/rrsetlist.h b/src/lib/dns/rrsetlist.h
deleted file mode 100644
index 0e05b5b..0000000
--- a/src/lib/dns/rrsetlist.h
+++ /dev/null
@@ -1,132 +0,0 @@
-// Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC")
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
-
-#ifndef __RRSETLIST_H
-#define __RRSETLIST_H 1
-
-#include <iostream>
-#include <iterator>
-#include <vector>
-
-#include <boost/shared_ptr.hpp>
-
-#include <dns/rrset.h>
-#include <dns/rrclass.h>
-#include <dns/rrtype.h>
-
-namespace isc {
-namespace dns {
-
-class DuplicateRRset : public Exception {
-public:
- DuplicateRRset(const char* file, size_t line, const char* what) :
- isc::Exception(file, line, what) {}
-};
-
-template <typename T, typename P, typename R>
-class RRsetListIterator :
- public std::iterator<std::input_iterator_tag, RRsetPtr> {
-public:
- RRsetListIterator() {}
- explicit RRsetListIterator(const T& it) :
- it_(it) {}
- RRsetListIterator& operator++()
- {
- ++it_;
- return (*this);
- }
- RRsetListIterator operator++(int)
- {
- RRsetListIterator tmp(*this);
- ++it_;
- return (tmp);
- }
- R operator*() const
- {
- return (*it_);
- }
- P operator->() const
- {
- return (&(operator*()));
- }
- bool operator==(const RRsetListIterator& other)
- {
- return (it_ == other.it_);
- }
- bool operator!=(const RRsetListIterator& other)
- {
- return (it_ != other.it_);
- }
-
-private:
- T it_;
-};
-
-/// A set of RRsets.
-///
-/// \note Do not use this class unless you really understand what
-/// you're doing and you're 100% sure that this class is the best choice
-/// for your purpose.
-///
-/// Counter intuitively, this class is not a "list" of RRsets but a
-/// "set" of them; it doesn't allow multiple RRsets of the same RR
-/// type and RR class to be added at the same time. And, for that
-/// reason, adding an RRset is more expensive than you'd expect. The
-/// class name is confusing, but was named so as a result of
-/// compromise: "RRsetset" would look awkward; RRsets would be
-/// confusing (with RRset).
-///
-/// In any case, if you want a list like container of RRsets, your best choice
-/// would be \c std::vector<RRset> or \c std::list<RRset>, not this class.
-/// In fact, in many cases \c RRsetList will be a suboptimal choice.
-/// This class is defined publicly as part of libdns++ for a historical
-/// reason and is actually quite specific to a particular need for libdatasrc.
-/// If you are tempted to use it, think twice to assess if this class
-/// is really what you want. Again, in many cases the answer will be no.
-class RRsetList {
-private:
- RRsetList(const RRsetList& source);
- RRsetList& operator=(const RRsetList& source);
-public:
- RRsetList() {}
- void addRRset(RRsetPtr new_rrsetptr);
- void append(RRsetList& source);
- RRsetPtr findRRset(const RRType& rrtype, const RRClass& rrclass);
-
- typedef RRsetListIterator<std::vector<RRsetPtr>::iterator,
- RRsetPtr*,
- RRsetPtr&> iterator;
- typedef RRsetListIterator<std::vector<RRsetPtr>::const_iterator,
- const RRsetPtr*,
- const RRsetPtr&> const_iterator;
-
- const_iterator begin() const { return (const_iterator(rrsets_.begin())); }
- const_iterator end() const { return (const_iterator(rrsets_.end())); }
-
- iterator begin() { return (iterator(rrsets_.begin())); }
- iterator end() { return (iterator(rrsets_.end())); }
-
- size_t size() const { return (rrsets_.size()); }
-
-private:
- std::vector<RRsetPtr> rrsets_;
-};
-
-} // end of namespace dns
-} // end of namespace isc
-#endif // __RRSETLIST_H
-
-// Local Variables:
-// mode: c++
-// End:
diff --git a/src/lib/dns/tests/Makefile.am b/src/lib/dns/tests/Makefile.am
index 0abb389..e8cbe10 100644
--- a/src/lib/dns/tests/Makefile.am
+++ b/src/lib/dns/tests/Makefile.am
@@ -56,7 +56,7 @@ run_unittests_SOURCES += rdata_minfo_unittest.cc
run_unittests_SOURCES += rdata_tsig_unittest.cc
run_unittests_SOURCES += rdata_naptr_unittest.cc
run_unittests_SOURCES += rdata_hinfo_unittest.cc
-run_unittests_SOURCES += rrset_unittest.cc rrsetlist_unittest.cc
+run_unittests_SOURCES += rrset_unittest.cc
run_unittests_SOURCES += question_unittest.cc
run_unittests_SOURCES += rrparamregistry_unittest.cc
run_unittests_SOURCES += masterload_unittest.cc
diff --git a/src/lib/dns/tests/labelsequence_unittest.cc b/src/lib/dns/tests/labelsequence_unittest.cc
index 90d470d..62cbcec 100644
--- a/src/lib/dns/tests/labelsequence_unittest.cc
+++ b/src/lib/dns/tests/labelsequence_unittest.cc
@@ -860,8 +860,12 @@ TEST_F(LabelSequenceTest, badDeserialize) {
const uint8_t toomany_offsets[] = { Name::MAX_LABELS + 1 };
EXPECT_THROW(LabelSequence ls(toomany_offsets), isc::BadValue);
- // exceed MAX_LABEL_LEN
- const uint8_t offsets_toolonglabel[] = { 2, 0, 64 };
+ // (second) offset does not match actual label length
+ const uint8_t offsets_wrongoffset[] = { 2, 0, 64, 1 };
+ EXPECT_THROW(LabelSequence ls(offsets_wrongoffset), isc::BadValue);
+
+ // offset matches, but exceeds MAX_LABEL_LEN
+ const uint8_t offsets_toolonglabel[] = { 2, 0, 64, 64 };
EXPECT_THROW(LabelSequence ls(offsets_toolonglabel), isc::BadValue);
// Inconsistent data: an offset is lower than the previous offset
diff --git a/src/lib/dns/tests/rrparamregistry_unittest.cc b/src/lib/dns/tests/rrparamregistry_unittest.cc
index d2bec5c..c155b53 100644
--- a/src/lib/dns/tests/rrparamregistry_unittest.cc
+++ b/src/lib/dns/tests/rrparamregistry_unittest.cc
@@ -37,6 +37,7 @@ protected:
{
ostringstream oss1;
oss1 << test_class_code;
+ // cppcheck-suppress useInitializationList
test_class_unknown_str = "CLASS" + oss1.str();
ostringstream oss2;
@@ -60,7 +61,7 @@ protected:
// we assume class/type numbers are officially unassigned. If not we'll
// need to update the test cases.
- static const uint16_t test_class_code = 65533;
+ static const uint16_t test_class_code = 65533;
static const uint16_t test_type_code = 65534;
static const string test_class_str;
static const string test_type_str;
@@ -77,7 +78,7 @@ TEST_F(RRParamRegistryTest, addRemove) {
// the first removal attempt should succeed
EXPECT_TRUE(RRParamRegistry::getRegistry().removeType(test_type_code));
- // then toText() should treat it as an "unknown"
+ // then toText() should treat it as an "unknown"
EXPECT_EQ(test_type_unknown_str, RRType(test_type_code).toText());
// attempt of removing non-existent mapping should result in 'false'
EXPECT_FALSE(RRParamRegistry::getRegistry().removeType(test_type_code));
diff --git a/src/lib/dns/tests/rrsetlist_unittest.cc b/src/lib/dns/tests/rrsetlist_unittest.cc
deleted file mode 100644
index 080f888..0000000
--- a/src/lib/dns/tests/rrsetlist_unittest.cc
+++ /dev/null
@@ -1,188 +0,0 @@
-// Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC")
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
-
-#include <vector>
-#include <boost/foreach.hpp>
-
-#include <dns/rdata.h>
-#include <dns/rdataclass.h>
-#include <dns/rrclass.h>
-#include <dns/rrtype.h>
-#include <dns/rrsetlist.h>
-#include <dns/rrset.h>
-#include <dns/rrttl.h>
-
-#include <gtest/gtest.h>
-
-using namespace std;
-using namespace isc::dns;
-using namespace isc::dns::rdata;
-
-namespace {
-class RRsetListTest : public ::testing::Test {
-protected:
- RRsetListTest() : example_name(Name("example.com")),
- example_ttl(RRTTL(3600))
- {}
- void setupList(RRsetList& list);
- Name example_name;
- RRTTL example_ttl;
-};
-
-const in::A rdata_in_a("192.0.2.1");
-const in::AAAA rdata_in_aaaa("2001:db8::1234");
-const generic::NS rdata_ns("ns.example.com");
-const generic::SOA rdata_soa(Name("ns.example.com"), Name("root.example.com"),
- 2010012601, 3600, 300, 3600000, 1200);
-const generic::CNAME rdata_cname("target.example.com");
-const generic::DNAME rdata_dname("dtarget.example.com");
-
-void
-RRsetListTest::setupList(RRsetList& list) {
- RRsetPtr a(new RRset(Name("example.com"), RRClass::IN(),
- RRType::A(), example_ttl));
- RRsetPtr aaaa(new RRset(Name("example.com"), RRClass::IN(),
- RRType::AAAA(), example_ttl));
- RRsetPtr ns(new RRset(Name("example.com"), RRClass::IN(),
- RRType::NS(), example_ttl));
- RRsetPtr soa(new RRset(Name("example.com"), RRClass::IN(),
- RRType::SOA(), example_ttl));
- RRsetPtr cname(new RRset(Name("example.com"), RRClass::IN(),
- RRType::CNAME(), example_ttl));
-
- a->addRdata(rdata_in_a);
- aaaa->addRdata(rdata_in_aaaa);
- ns->addRdata(rdata_ns);
- soa->addRdata(rdata_soa);
- cname->addRdata(rdata_cname);
-
- list.addRRset(a);
- list.addRRset(aaaa);
- list.addRRset(ns);
- list.addRRset(soa);
- list.addRRset(cname);
-}
-
-TEST_F(RRsetListTest, emptyOnInitialCreate) {
- RRsetList list;
- EXPECT_EQ(list.size(), 0);
-}
-
-TEST_F(RRsetListTest, addRRsets) {
- RRsetList list;
- setupList(list);
- EXPECT_EQ(list.size(), 5);
-}
-
-TEST_F(RRsetListTest, append) {
- RRsetList list1;
- setupList(list1);
- RRsetList list2;
- RRsetPtr dname(new RRset(Name("example.com"), RRClass::IN(),
- RRType::DNAME(), example_ttl));
- dname->addRdata(rdata_dname);
- list2.addRRset(dname);
- list1.append(list2);
- EXPECT_EQ(list2.size(), 1);
- EXPECT_EQ(list1.size(), 6);
-
- RRsetPtr rrset = list1.findRRset(RRType::DNAME(), RRClass::IN());
- EXPECT_EQ(RRType::DNAME(), rrset->getType());
-
- EXPECT_THROW(list1.append(list2), DuplicateRRset);
-}
-
-TEST_F(RRsetListTest, extraRRset) {
- RRsetList list;
- setupList(list);
- RRsetPtr cname(new RRset(Name("another.example.com"), RRClass::IN(),
- RRType::CNAME(), example_ttl));
- EXPECT_THROW(list.addRRset(cname), DuplicateRRset);
-}
-
-void
-checkFindResult(RRsetList& list, const Name& name,
- const RRType& rrtype, const RRClass& rrclass,
- const RRTTL& rrttl)
-{
- RRsetPtr rrset = list.findRRset(rrtype, rrclass);;
- EXPECT_EQ(name, rrset->getName());
- EXPECT_EQ(rrtype, rrset->getType());
- EXPECT_EQ(rrclass, rrset->getClass());
- EXPECT_EQ(rrttl, rrset->getTTL());
-}
-
-TEST_F(RRsetListTest, findRRset) {
- RRsetList list;
- setupList(list);
-
- checkFindResult(list, example_name, RRType::A(), RRClass::IN(),
- example_ttl);
- checkFindResult(list, example_name, RRType::CNAME(), RRClass::IN(),
- example_ttl);
- checkFindResult(list, example_name, RRType::AAAA(), RRClass::IN(),
- example_ttl);
- checkFindResult(list, example_name, RRType::NS(), RRClass::IN(),
- example_ttl);
- checkFindResult(list, example_name, RRType::SOA(), RRClass::IN(),
- example_ttl);
-}
-
-TEST_F(RRsetListTest, checkData) {
- RRsetList list;
- RRsetPtr a(new RRset(Name("example.com"), RRClass::IN(),
- RRType::A(), example_ttl));
- a->addRdata(rdata_in_a);
- list.addRRset(a);
-
- RdataIteratorPtr it =
- list.findRRset(RRType::A(), RRClass::IN())->getRdataIterator();
- EXPECT_FALSE(it->isLast());
- EXPECT_EQ("192.0.2.1", it->getCurrent().toText());
-}
-
-TEST_F(RRsetListTest, iterate) {
- RRsetList list;
- setupList(list);
-
- bool has_a = false, has_aaaa = false, has_ns = false, has_soa = false,
- has_cname = false;
- int i = 0;
- BOOST_FOREACH(RRsetPtr rrset, list) {
- if (rrset->getType() == RRType::A()) {
- has_a = true;
- }
- if (rrset->getType() == RRType::AAAA()) {
- has_aaaa = true;
- }
- if (rrset->getType() == RRType::NS()) {
- has_ns = true;
- }
- if (rrset->getType() == RRType::SOA()) {
- has_soa = true;
- }
- if (rrset->getType() == RRType::CNAME()) {
- has_cname = true;
- }
- ++i;
- }
- EXPECT_TRUE(has_a);
- EXPECT_TRUE(has_aaaa);
- EXPECT_TRUE(has_ns);
- EXPECT_TRUE(has_soa);
- EXPECT_TRUE(has_cname);
- EXPECT_TRUE(i == 5);
-}
-
-}
diff --git a/src/lib/log/compiler/message.cc b/src/lib/log/compiler/message.cc
index 86c5f20..ef62b2f 100644
--- a/src/lib/log/compiler/message.cc
+++ b/src/lib/log/compiler/message.cc
@@ -109,11 +109,13 @@ currentTime() {
// Get a text representation of the current time.
time_t curtime;
time(&curtime);
- char* buffer = ctime(&curtime);
+ struct tm* timeinfo;
+ timeinfo = localtime(&curtime);
- // Convert to string and strip out the trailing newline
- string current_time = buffer;
- return (isc::util::str::trim(current_time));
+ char buffer[80];
+ strftime(buffer, 80, "%a %b %d %Y %H:%M", timeinfo);
+
+ return (std::string(buffer));
}
diff --git a/src/lib/log/logger.h b/src/lib/log/logger.h
index 6405488..4291eae 100644
--- a/src/lib/log/logger.h
+++ b/src/lib/log/logger.h
@@ -33,9 +33,9 @@ namespace log {
/// \page LoggingApi Logging API
/// \section LoggingApiOverview Overview
/// BIND 10 logging uses the concepts of the widely-used Java logging
-/// package log4j (http://logging.apache.log/log4j), albeit implemented
+/// package log4j (http://logging.apache.log/log4j), albeit implemented
/// in C++ using an open-source port. Features of the system are:
-///
+///
/// - Within the code objects - known as loggers - can be created and
/// used to log messages. These loggers have names; those with the
/// same name share characteristics (such as output destination).
@@ -50,7 +50,7 @@ namespace log {
/// message is logged, it is output only if it is logged at a level equal
/// to the logger severity level or greater, e.g. if the logger's severity
/// is WARN, only messages logged at WARN, ERROR or FATAL will be output.
-///
+///
/// \section LoggingApiLoggerNames BIND 10 Logger Names
/// Within BIND 10, the root logger root logger is given the name of the
/// program (via the stand-alone function setRootLoggerName()). Other loggers
@@ -58,7 +58,7 @@ namespace log {
/// This name appears in logging output, allowing users to identify both
/// the BIND 10 program and the component within the program that generated
/// the message.
-///
+///
/// When creating a logger, the abbreviated name "<sublogger>" can be used;
/// the program name will be prepended to it when the logger is created.
/// In this way, individual libraries can have their own loggers without
@@ -66,7 +66,7 @@ namespace log {
/// - The origin of the message will be clearly identified.
/// - The same component can have different options (e.g. logging severity)
/// in different programs at the same time.
-///
+///
/// \section LoggingApiLoggingMessages Logging Messages
/// Instead of embedding the text of messages within the code, each message
/// is referred to using a symbolic name. The logging code uses this name as
diff --git a/src/lib/log/tests/log_formatter_unittest.cc b/src/lib/log/tests/log_formatter_unittest.cc
index 435b200..f0a9a59 100644
--- a/src/lib/log/tests/log_formatter_unittest.cc
+++ b/src/lib/log/tests/log_formatter_unittest.cc
@@ -16,6 +16,7 @@
#include <gtest/gtest.h>
#include <util/unittests/resource.h>
+#include <util/unittests/check_valgrind.h>
#include <log/log_formatter.h>
#include <log/logger_level.h>
@@ -119,12 +120,13 @@ TEST_F(FormatterTest, mismatchedPlaceholders) {
// Likewise, if there's a redundant placeholder (or missing argument), the
// check detects it and aborts the program. Due to the restriction of the
// current implementation, it doesn't throw.
- EXPECT_DEATH({
- isc::util::unittests::dontCreateCoreDumps();
- Formatter(isc::log::INFO, s("Too many arguments in %1 %2"), this).
- arg("only one");
- }, ".*");
-
+ if (!isc::util::unittests::runningOnValgrind()) {
+ EXPECT_DEATH({
+ isc::util::unittests::dontCreateCoreDumps();
+ Formatter(isc::log::INFO, s("Too many arguments in %1 %2"), this).
+ arg("only one");
+ }, ".*");
+ }
#endif /* EXPECT_DEATH */
// Mixed case of above two: the exception will be thrown due to the missing
// placeholder. The other check is disabled due to that.
diff --git a/src/lib/log/tests/logger_example.cc b/src/lib/log/tests/logger_example.cc
index 6f95e5d..08c9084 100644
--- a/src/lib/log/tests/logger_example.cc
+++ b/src/lib/log/tests/logger_example.cc
@@ -118,7 +118,6 @@ int main(int argc, char** argv) {
LoggerSpecification cur_spec(ROOT_NAME);// Current specification
OutputOption cur_opt; // Current output option
vector<LoggerSpecification> loggers; // Set of logger specifications
- vector<OutputOption> options; // Output options for logger
std::string severity; // Severity set for logger
// Initialize logging system - set the root logger name.
diff --git a/src/lib/log/tests/logger_unittest.cc b/src/lib/log/tests/logger_unittest.cc
index a9330a9..7b62d79 100644
--- a/src/lib/log/tests/logger_unittest.cc
+++ b/src/lib/log/tests/logger_unittest.cc
@@ -11,13 +11,10 @@
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
-
-#include <iostream>
-#include <string>
-
#include <gtest/gtest.h>
#include <util/unittests/resource.h>
+#include <util/unittests/check_valgrind.h>
#include <log/logger.h>
#include <log/logger_manager.h>
@@ -27,6 +24,9 @@
#include <util/interprocess_sync_file.h>
+#include <iostream>
+#include <string>
+
using namespace isc;
using namespace isc::log;
using namespace std;
@@ -356,7 +356,6 @@ TEST_F(LoggerTest, IsDebugEnabledLevel) {
// Check that if a logger name is too long, it triggers the appropriate
// assertion.
-
TEST_F(LoggerTest, LoggerNameLength) {
// The following statements should just declare a logger and nothing
// should happen.
@@ -374,12 +373,14 @@ TEST_F(LoggerTest, LoggerNameLength) {
// Too long a logger name should trigger an assertion failure.
// Note that we just check that it dies - we don't check what message is
// output.
- EXPECT_DEATH({
- isc::util::unittests::dontCreateCoreDumps();
+ if (!isc::util::unittests::runningOnValgrind()) {
+ EXPECT_DEATH({
+ isc::util::unittests::dontCreateCoreDumps();
- string ok3(Logger::MAX_LOGGER_NAME_SIZE + 1, 'x');
- Logger l3(ok3.c_str());
- }, ".*");
+ string ok3(Logger::MAX_LOGGER_NAME_SIZE + 1, 'x');
+ Logger l3(ok3.c_str());
+ }, ".*");
+ }
#endif
}
diff --git a/src/lib/log/tests/message_initializer_2_unittest.cc b/src/lib/log/tests/message_initializer_2_unittest.cc
index b479eee..8cc78fa 100644
--- a/src/lib/log/tests/message_initializer_2_unittest.cc
+++ b/src/lib/log/tests/message_initializer_2_unittest.cc
@@ -16,6 +16,7 @@
#include <gtest/gtest.h>
#include <util/unittests/resource.h>
+#include <util/unittests/check_valgrind.h>
using namespace isc::log;
@@ -43,10 +44,12 @@ TEST(MessageInitializerTest2, MessageLoadTest) {
// test for its presence and bypass the test if not available.
#ifdef EXPECT_DEATH
// Adding one more should take us over the limit.
- EXPECT_DEATH({
- isc::util::unittests::dontCreateCoreDumps();
+ if (!isc::util::unittests::runningOnValgrind()) {
+ EXPECT_DEATH({
+ isc::util::unittests::dontCreateCoreDumps();
- MessageInitializer initializer2(values);
- }, ".*");
+ MessageInitializer initializer2(values);
+ }, ".*");
+ }
#endif
}
diff --git a/src/lib/python/isc/bind10/component.py b/src/lib/python/isc/bind10/component.py
index 9c29ace..1f7006c 100644
--- a/src/lib/python/isc/bind10/component.py
+++ b/src/lib/python/isc/bind10/component.py
@@ -45,6 +45,7 @@ COMPONENT_RESTART_DELAY = 10
STATE_DEAD = 'dead'
STATE_STOPPED = 'stopped'
+STATE_RESTARTING = 'restarting'
STATE_RUNNING = 'running'
def get_signame(signal_number):
@@ -68,6 +69,7 @@ class BaseComponent:
explicitly).
- Running - after start() was called, it started successfully and is
now running.
+ - Restarting - the component failed (crashed) and is waiting for a restart
- Dead - it failed and can not be resurrected.
Init
@@ -79,11 +81,11 @@ class BaseComponent:
| | |
|failure | failed() |
| | |
- v | |
+ v | | start()/restart()
+<-----------+ |
| |
| kind == dispensable or kind|== needed and failed late
- +-----------------------------+
+ +-----------------------> Restarting
|
| kind == core or kind == needed and it failed too soon
v
@@ -163,7 +165,7 @@ class BaseComponent:
"""
if self.__state == STATE_DEAD:
raise ValueError("Can't resurrect already dead component")
- if self.running():
+ if self.is_running():
raise ValueError("Can't start already running component")
logger.info(BIND10_COMPONENT_START, self.name())
self.__state = STATE_RUNNING
@@ -188,7 +190,7 @@ class BaseComponent:
"""
# This is not tested. It talks with the outher world, which is out
# of scope of unittests.
- if not self.running():
+ if not self.is_running():
raise ValueError("Can't stop a component which is not running")
logger.info(BIND10_COMPONENT_STOP, self.name())
self.__state = STATE_STOPPED
@@ -234,9 +236,9 @@ class BaseComponent:
logger.error(BIND10_COMPONENT_FAILED, self.name(), self.pid(),
exit_str)
- if not self.running():
+ if not self.is_running():
raise ValueError("Can't fail component that isn't running")
- self.__state = STATE_STOPPED
+ self.__state = STATE_RESTARTING # tentatively set, maybe changed to DEAD
self._failed_internal()
# If it is a core component or the needed component failed to start
# (including it stopped really soon)
@@ -284,7 +286,7 @@ class BaseComponent:
else:
return False
- def running(self):
+ def is_running(self):
"""
Informs if the component is currently running. It assumes the failed
is called whenever the component really fails and there might be some
@@ -296,6 +298,15 @@ class BaseComponent:
"""
return self.__state == STATE_RUNNING
+ def is_restarting(self):
+ """Informs if the component has failed and is waiting for a restart.
+
+ Unlike the case of is_running(), if this returns True it always means
+ the corresponding process has died and not yet restarted.
+
+ """
+ return self.__state == STATE_RESTARTING
+
def _start_internal(self):
"""
This method does the actual starting of a process. You need to override
@@ -560,6 +571,13 @@ class Configurator:
self._running = False
self.__reconfigure_internal(self._components, {})
+ def has_component(self, component):
+ '''Return if a specified component is configured.'''
+ # Values of self._components are tuples of (config, component).
+ # Extract the components of the tuples and see if the given one
+ # is included.
+ return component in map(lambda x: x[1], self._components.values())
+
def reconfigure(self, configuration):
"""
Changes configuration from the current one to the provided. It
@@ -591,7 +609,7 @@ class Configurator:
for cname in old.keys():
if cname not in new:
component = self._components[cname][1]
- if component.running():
+ if component.is_running() or component.is_restarting():
plan.append({
'command': STOP_CMD,
'component': component,
@@ -674,7 +692,7 @@ class Configurator:
self._components[task['name']] = (task['config'],
component)
elif command == STOP_CMD:
- if component.running():
+ if component.is_running():
component.stop()
del self._components[task['name']]
else:
diff --git a/src/lib/python/isc/bind10/sockcreator.py b/src/lib/python/isc/bind10/sockcreator.py
index c681d07..55142ee 100644
--- a/src/lib/python/isc/bind10/sockcreator.py
+++ b/src/lib/python/isc/bind10/sockcreator.py
@@ -214,9 +214,14 @@ class Creator(Parser):
socket.SOCK_STREAM)
env = copy.deepcopy(os.environ)
env['PATH'] = path
+ # We explicitly set close_fs to True; it's False by default before
+ # Python 3.2. If we don't close the remaining FDs, the copy of
+ # 'local' will prevent the child process from terminating when
+ # the parent process died abruptly.
self.__process = subprocess.Popen(['b10-sockcreator'], env=env,
stdin=remote.fileno(),
stdout=remote2.fileno(),
+ close_fds=True,
preexec_fn=self.__preexec_work)
remote.close()
remote2.close()
diff --git a/src/lib/python/isc/bind10/tests/component_test.py b/src/lib/python/isc/bind10/tests/component_test.py
index 3f26870..18efea7 100644
--- a/src/lib/python/isc/bind10/tests/component_test.py
+++ b/src/lib/python/isc/bind10/tests/component_test.py
@@ -191,7 +191,8 @@ class ComponentTests(BossUtils, unittest.TestCase):
self.assertFalse(self.__start_called)
self.assertFalse(self.__stop_called)
self.assertFalse(self.__failed_called)
- self.assertFalse(component.running())
+ self.assertFalse(component.is_running())
+ self.assertFalse(component.is_restarting())
# We can't stop or fail the component yet
self.assertRaises(ValueError, component.stop)
self.assertRaises(ValueError, component.failed, 1)
@@ -204,7 +205,8 @@ class ComponentTests(BossUtils, unittest.TestCase):
self.assertTrue(self.__start_called)
self.assertFalse(self.__stop_called)
self.assertFalse(self.__failed_called)
- self.assertTrue(component.running())
+ self.assertTrue(component.is_running())
+ self.assertFalse(component.is_restarting())
def __check_dead(self, component):
"""
@@ -215,7 +217,8 @@ class ComponentTests(BossUtils, unittest.TestCase):
self.assertFalse(self.__stop_called)
self.assertTrue(self.__failed_called)
self.assertEqual(1, self._exitcode)
- self.assertFalse(component.running())
+ self.assertFalse(component.is_running())
+ self.assertFalse(component.is_restarting())
# Surely it can't be stopped when already dead
self.assertRaises(ValueError, component.stop)
# Nor started
@@ -234,7 +237,8 @@ class ComponentTests(BossUtils, unittest.TestCase):
self.assertTrue(self.__start_called)
self.assertFalse(self.__stop_called)
self.assertTrue(self.__failed_called)
- self.assertTrue(component.running())
+ self.assertTrue(component.is_running())
+ self.assertFalse(component.is_restarting())
# Check it can't be started again
self.assertRaises(ValueError, component.start)
@@ -246,7 +250,8 @@ class ComponentTests(BossUtils, unittest.TestCase):
self.assertTrue(self.__start_called)
self.assertFalse(self.__stop_called)
self.assertTrue(self.__failed_called)
- self.assertFalse(component.running())
+ self.assertFalse(component.is_running())
+ self.assertTrue(component.is_restarting())
def __do_start_stop(self, kind):
"""
@@ -270,7 +275,8 @@ class ComponentTests(BossUtils, unittest.TestCase):
self.assertTrue(self.__start_called)
self.assertTrue(self.__stop_called)
self.assertFalse(self.__failed_called)
- self.assertFalse(component.running())
+ self.assertFalse(component.is_running())
+ self.assertFalse(component.is_restarting())
# Check it can't be stopped twice
self.assertRaises(ValueError, component.stop)
# Or failed
@@ -553,10 +559,10 @@ class ComponentTests(BossUtils, unittest.TestCase):
self.assertIsNone(component.pid())
self.assertEqual(['hello'], component._params)
self.assertEqual('Address', component._address)
- self.assertFalse(component.running())
+ self.assertFalse(component.is_running())
self.assertEqual({}, self.__registered_processes)
component.start()
- self.assertTrue(component.running())
+ self.assertTrue(component.is_running())
# Some versions of unittest miss assertIsInstance
self.assertTrue(isinstance(component._procinfo, TestProcInfo))
self.assertEqual(42, component.pid())
@@ -580,11 +586,11 @@ class ComponentTests(BossUtils, unittest.TestCase):
"""
component = Component('component', self, 'needed', 'Address')
component.start()
- self.assertTrue(component.running())
+ self.assertTrue(component.is_running())
self.assertEqual('component', self.__start_simple_params)
component.pid = lambda: 42
component.stop()
- self.assertFalse(component.running())
+ self.assertFalse(component.is_running())
self.assertEqual(('component', 'Address', 42),
self.__stop_process_params)
@@ -609,7 +615,7 @@ class ComponentTests(BossUtils, unittest.TestCase):
component = Component('component', self, 'needed', 'Address',
[], ProcInfo)
component.start()
- self.assertTrue(component.running())
+ self.assertTrue(component.is_running())
component.kill()
self.assertTrue(process.terminated)
self.assertFalse(process.killed)
@@ -872,12 +878,13 @@ class ConfiguratorTest(BossUtils, unittest.TestCase):
'priority': 6,
'special': 'test',
'process': 'additional',
- 'kind': 'needed'
+ 'kind': 'dispensable' # need to be dispensable so it could restart
}
self.log = []
plan = configurator._build_plan(self.__build_components(self.__core),
bigger)
- self.assertEqual([('additional', 'init'), ('additional', 'needed')],
+ self.assertEqual([('additional', 'init'),
+ ('additional', 'dispensable')],
self.log)
self.assertEqual(1, len(plan))
self.assertTrue('component' in plan[0])
@@ -888,15 +895,29 @@ class ConfiguratorTest(BossUtils, unittest.TestCase):
# Now remove the one component again
# We run the plan so the component is wired into internal structures
configurator._run_plan(plan)
- self.log = []
- plan = configurator._build_plan(self.__build_components(bigger),
- self.__core)
- self.assertEqual([], self.log)
- self.assertEqual([{
- 'command': 'stop',
- 'name': 'additional',
- 'component': component
- }], plan)
+ # We should have the 'additional' component in the configurator.
+ self.assertTrue(configurator.has_component(component))
+ for count in [0, 1]: # repeat two times, see below
+ self.log = []
+ plan = configurator._build_plan(self.__build_components(bigger),
+ self.__core)
+ self.assertEqual([], self.log)
+ self.assertEqual([{
+ 'command': 'stop',
+ 'name': 'additional',
+ 'component': component
+ }], plan)
+
+ if count is 0:
+ # We then emulate unexpected failure of the component (but
+ # before it restarts). It shouldn't confuse the
+ # configurator in the second phase of the test
+ component.failed(0)
+ # Run the plan, confirm the specified component is gone.
+ configurator._run_plan(plan)
+ self.assertFalse(configurator.has_component(component))
+ # There shouldn't be any other object that has the same name
+ self.assertFalse('additional' in configurator._components)
# We want to switch a component. So, prepare the configurator so it
# holds one
diff --git a/src/lib/python/isc/datasrc/sqlite3_ds.py b/src/lib/python/isc/datasrc/sqlite3_ds.py
index f9b47c0..dc80afd 100644
--- a/src/lib/python/isc/datasrc/sqlite3_ds.py
+++ b/src/lib/python/isc/datasrc/sqlite3_ds.py
@@ -25,7 +25,7 @@ RR_RDATA_INDEX = 7
# Current major and minor versions of schema
SCHEMA_MAJOR_VERSION = 2
-SCHEMA_MINOR_VERSION = 0
+SCHEMA_MINOR_VERSION = 1
class Sqlite3DSError(Exception):
""" Define exceptions."""
@@ -81,6 +81,7 @@ def create(cur):
rdtype TEXT NOT NULL COLLATE NOCASE,
rdata TEXT NOT NULL)""")
cur.execute("CREATE INDEX nsec3_byhash ON nsec3 (hash)")
+ cur.execute("CREATE INDEX nsec3_byhash_and_rdtype ON nsec3 (hash, rdtype)")
cur.execute("""CREATE TABLE diffs (id INTEGER PRIMARY KEY,
zone_id INTEGER NOT NULL,
version INTEGER NOT NULL,
diff --git a/src/lib/resolve/recursive_query.cc b/src/lib/resolve/recursive_query.cc
index 8d03c1c..7eae6fe 100644
--- a/src/lib/resolve/recursive_query.cc
+++ b/src/lib/resolve/recursive_query.cc
@@ -122,8 +122,6 @@ deepestDelegation(Name name, RRClass rrclass,
return (".");
}
-typedef std::vector<std::pair<std::string, uint16_t> > AddressVector;
-
// Here we do not use the typedef above, as the SunStudio compiler
// mishandles this in its name mangling, and wouldn't compile.
// We can probably use a typedef, but need to move it to a central
@@ -161,7 +159,6 @@ RecursiveQuery::setRttRecorder(boost::shared_ptr<RttRecorder>& recorder) {
}
namespace {
-
typedef std::pair<std::string, uint16_t> addr_t;
/*
@@ -171,12 +168,12 @@ typedef std::pair<std::string, uint16_t> addr_t;
*
* Used by RecursiveQuery::sendQuery.
*/
-class RunningQuery : public IOFetch::Callback {
+class RunningQuery : public IOFetch::Callback, public AbstractRunningQuery {
class ResolverNSASCallback : public isc::nsas::AddressRequestCallback {
public:
ResolverNSASCallback(RunningQuery* rq) : rq_(rq) {}
-
+
void success(const isc::nsas::NameserverAddress& address) {
// Success callback, send query to found namesever
LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_CB, RESLIB_RUNQ_SUCCESS)
@@ -184,7 +181,7 @@ public:
rq_->nsasCallbackCalled();
rq_->sendTo(address);
}
-
+
void unreachable() {
// Nameservers unreachable: drop query or send servfail?
LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_CB, RESLIB_RUNQ_FAIL);
@@ -261,7 +258,7 @@ private:
bool done_;
// If we have a client timeout, we call back with a failure message,
- // but we do not stop yet. We use this variable to make sure we
+ // but we do not stop yet. We use this variable to make sure we
// don't call back a second time later
bool callback_called_;
@@ -270,7 +267,7 @@ private:
// Reference to our cache
isc::cache::ResolverCache& cache_;
-
+
// the 'current' zone we are in (i.e.) we start out at the root,
// and for each delegation this gets updated with the zone the
// delegation points to.
@@ -278,7 +275,7 @@ private:
// of the call we use it in take a string, we need update those
// too).
std::string cur_zone_;
-
+
// This is the handler we pass on to the NSAS; it is called when
// the NSAS has an address for us to query
boost::shared_ptr<ResolverNSASCallback> nsas_callback_;
@@ -295,7 +292,7 @@ private:
// The moment in time we sent a query to the nameserver above.
struct timeval current_ns_qsent_time;
-
+
// RunningQuery deletes itself when it is done. In order for us
// to do this safely, we must make sure that there are no events
// that might call back to it. There are two types of events in
@@ -365,7 +362,7 @@ private:
io_.get_io_service().post(query);
}
}
-
+
// 'general' send, ask the NSAS to give us an address.
void send(IOFetch::Protocol protocol = IOFetch::UDP, bool edns = true) {
protocol_ = protocol; // Store protocol being used for this
@@ -397,7 +394,7 @@ private:
nsas_.lookup(cur_zone_, question_.getClass(), nsas_callback_);
}
}
-
+
// Called by our NSAS callback handler so we know we do not have
// an outstanding NSAS call anymore.
void nsasCallbackCalled() {
@@ -422,13 +419,13 @@ private:
// here (classify() will set it when it walks through
// the cname chain to verify it).
Name cname_target(question_.getName());
-
+
isc::resolve::ResponseClassifier::Category category =
isc::resolve::ResponseClassifier::classify(
question_, incoming, cname_target, cname_count_);
bool found_ns = false;
-
+
switch (category) {
case isc::resolve::ResponseClassifier::ANSWER:
case isc::resolve::ResponseClassifier::ANSWERCNAME:
@@ -569,7 +566,7 @@ private:
// SERVFAIL if we get FORMERR instead
}
goto SERVFAIL;
-
+
default:
SERVFAIL:
// Some error in received packet it. Report it and return SERVFAIL
@@ -718,7 +715,7 @@ public:
++outstanding_events_;
lookup_timer.async_wait(boost::bind(&RunningQuery::lookupTimeout, this));
}
-
+
// Setup the timer to send an answer (client_timeout)
if (client_timeout >= 0) {
client_timer.expires_from_now(
@@ -726,10 +723,12 @@ public:
++outstanding_events_;
client_timer.async_wait(boost::bind(&RunningQuery::clientTimeout, this));
}
-
+
doLookup();
}
+ virtual ~RunningQuery() {};
+
// called if we have a lookup timeout; if our callback has
// not been called, call it now. Then stop.
void lookupTimeout() {
@@ -741,7 +740,7 @@ public:
--outstanding_events_;
stop();
}
-
+
// called if we have a client timeout; if our callback has
// not been called, call it now. But do not stop.
void clientTimeout() {
@@ -810,7 +809,7 @@ public:
// XXX is this the place for TCP retry?
assert(outstanding_events_ > 0);
--outstanding_events_;
-
+
if (!done_ && result != IOFetch::TIME_OUT) {
// we got an answer
@@ -890,15 +889,17 @@ public:
stop();
}
}
-
+
// Clear the answer parts of answer_message, and set the rcode
// to servfail
void makeSERVFAIL() {
- isc::resolve::makeErrorMessage(answer_message_, Rcode::SERVFAIL());
+ if (answer_message_) {
+ isc::resolve::makeErrorMessage(answer_message_, Rcode::SERVFAIL());
+ }
}
};
-class ForwardQuery : public IOFetch::Callback {
+class ForwardQuery : public IOFetch::Callback, public AbstractRunningQuery {
private:
// The io service to handle async calls
IOService& io_;
@@ -999,6 +1000,8 @@ public:
send();
}
+ virtual ~ForwardQuery() {};
+
virtual void lookupTimeout() {
if (!callback_called_) {
makeSERVFAIL();
@@ -1076,7 +1079,7 @@ public:
}
-void
+AbstractRunningQuery*
RecursiveQuery::resolve(const QuestionPtr& question,
const isc::resolve::ResolverInterface::CallbackPtr callback)
{
@@ -1096,7 +1099,7 @@ RecursiveQuery::resolve(const QuestionPtr& question,
// Message found, return that
LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_CACHE, RESLIB_RECQ_CACHE_FIND)
.arg(questionText(*question)).arg(1);
-
+
// TODO: err, should cache set rcode as well?
answer_message->setRcode(Rcode::NOERROR());
callback->success(answer_message);
@@ -1119,16 +1122,17 @@ RecursiveQuery::resolve(const QuestionPtr& question,
// delete itself when it is done
LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_TRACE, RESLIB_RECQ_CACHE_NO_FIND)
.arg(questionText(*question)).arg(1);
- new RunningQuery(io, *question, answer_message,
- test_server_, buffer, callback,
- query_timeout_, client_timeout_,
- lookup_timeout_, retries_, nsas_,
- cache_, rtt_recorder_);
+ return (new RunningQuery(io, *question, answer_message,
+ test_server_, buffer, callback,
+ query_timeout_, client_timeout_,
+ lookup_timeout_, retries_, nsas_,
+ cache_, rtt_recorder_));
}
}
+ return (NULL);
}
-void
+AbstractRunningQuery*
RecursiveQuery::resolve(const Question& question,
MessagePtr answer_message,
OutputBufferPtr buffer,
@@ -1146,11 +1150,11 @@ RecursiveQuery::resolve(const Question& question,
// TODO: general 'prepareinitialanswer'
answer_message->setOpcode(isc::dns::Opcode::QUERY());
answer_message->addQuestion(question);
-
+
// First try to see if we have something cached in the messagecache
LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_TRACE, RESLIB_RESOLVE)
.arg(questionText(question)).arg(2);
-
+
if (cache_.lookup(question.getName(), question.getType(),
question.getClass(), *answer_message) &&
answer_message->getRRCount(Message::SECTION_ANSWER) > 0) {
@@ -1181,15 +1185,16 @@ RecursiveQuery::resolve(const Question& question,
// delete itself when it is done
LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_TRACE, RESLIB_RECQ_CACHE_NO_FIND)
.arg(questionText(question)).arg(2);
- new RunningQuery(io, question, answer_message,
- test_server_, buffer, crs, query_timeout_,
- client_timeout_, lookup_timeout_, retries_,
- nsas_, cache_, rtt_recorder_);
+ return (new RunningQuery(io, question, answer_message,
+ test_server_, buffer, crs, query_timeout_,
+ client_timeout_, lookup_timeout_, retries_,
+ nsas_, cache_, rtt_recorder_));
}
}
+ return (NULL);
}
-void
+AbstractRunningQuery*
RecursiveQuery::forward(ConstMessagePtr query_message,
MessagePtr answer_message,
OutputBufferPtr buffer,
@@ -1215,9 +1220,9 @@ RecursiveQuery::forward(ConstMessagePtr query_message,
// everything throught without interpretation, except
// QID, port number. The response will not be cached.
// It will delete itself when it is done
- new ForwardQuery(io, query_message, answer_message,
- upstream_, buffer, callback, query_timeout_,
- client_timeout_, lookup_timeout_);
+ return (new ForwardQuery(io, query_message, answer_message,
+ upstream_, buffer, callback, query_timeout_,
+ client_timeout_, lookup_timeout_));
}
} // namespace asiodns
diff --git a/src/lib/resolve/recursive_query.h b/src/lib/resolve/recursive_query.h
index a819a94..7cda837 100644
--- a/src/lib/resolve/recursive_query.h
+++ b/src/lib/resolve/recursive_query.h
@@ -52,6 +52,26 @@ private:
std::vector<uint32_t> rtt_; ///< Stored round-trip times
};
+typedef std::vector<std::pair<std::string, uint16_t> > AddressVector;
+
+/// \brief A Running query
+///
+/// This base class represents an active running query object;
+/// i.e. an outstanding query to an authoritative name server or
+/// upstream server (when running in forwarder mode).
+///
+/// It can not be instantiated directly, but is created by
+/// RecursiveQuery::resolve() and RecursiveQuery::forward().
+///
+/// Its only public method is its destructor, and that should in theory
+/// not be called either except in some unit tests. Instances should
+/// delete themselves when the query is finished.
+class AbstractRunningQuery {
+protected:
+ AbstractRunningQuery() {};
+public:
+ virtual ~AbstractRunningQuery() {};
+};
/// \brief Recursive Query
///
@@ -126,8 +146,8 @@ public:
/// \param question The question being answered <qname/qclass/qtype>
/// \param callback Callback object. See
/// \c ResolverInterface::Callback for more information
- void resolve(const isc::dns::QuestionPtr& question,
- const isc::resolve::ResolverInterface::CallbackPtr callback);
+ AbstractRunningQuery* resolve(const isc::dns::QuestionPtr& question,
+ const isc::resolve::ResolverInterface::CallbackPtr callback);
/// \brief Initiates resolving for the given question.
@@ -142,10 +162,17 @@ public:
/// \param buffer An output buffer into which the intermediate responses will
/// be copied.
/// \param server A pointer to the \c DNSServer object handling the client
- void resolve(const isc::dns::Question& question,
- isc::dns::MessagePtr answer_message,
- isc::util::OutputBufferPtr buffer,
- DNSServer* server);
+ /// \return A pointer to the active AbstractRunningQuery object
+ /// created by this call (if any); this object should delete
+ /// itself in normal circumstances, and can normally be ignored
+ /// by the caller, but a pointer is returned for use-cases
+ /// such as unit tests.
+ /// Returns NULL if the data was found internally and no actual
+ /// query was sent.
+ AbstractRunningQuery* resolve(const isc::dns::Question& question,
+ isc::dns::MessagePtr answer_message,
+ isc::util::OutputBufferPtr buffer,
+ DNSServer* server);
/// \brief Initiates forwarding for the given query.
///
@@ -158,7 +185,11 @@ public:
/// \param server Server object that handles receipt and processing of the
/// received messages.
/// \param callback callback object
- void forward(isc::dns::ConstMessagePtr query_message,
+ /// \return A pointer to the active ForwardQuery created by this call;
+ /// this object should delete itself in normal circumstances,
+ /// and can normally be ignored by the caller, but a pointer
+ /// is returned for use-cases such as unit tests.
+ AbstractRunningQuery* forward(isc::dns::ConstMessagePtr query_message,
isc::dns::MessagePtr answer_message,
isc::util::OutputBufferPtr buffer,
DNSServer* server,
diff --git a/src/lib/resolve/tests/recursive_query_unittest.cc b/src/lib/resolve/tests/recursive_query_unittest.cc
index 02721f1..e5e46e1 100644
--- a/src/lib/resolve/tests/recursive_query_unittest.cc
+++ b/src/lib/resolve/tests/recursive_query_unittest.cc
@@ -173,6 +173,9 @@ protected:
// It would delete itself, but after the io_service_, which could
// segfailt in case there were unhandled requests
resolver_.reset();
+ // In a similar note, we wait until the resolver has been cleaned up
+ // until deleting and active test running_query_
+ delete running_query_;
}
void SetUp() {
@@ -218,7 +221,7 @@ protected:
}
// Receive a UDP packet from a mock server; used for testing
- // recursive lookup. The caller must place a RecursiveQuery
+ // recursive lookup. The caller must place a RecursiveQuery
// on the IO Service queue before running this routine.
void recvUDP(const int family, void* buffer, size_t& size) {
ScopedAddrInfo sai(resolveAddress(family, IPPROTO_UDP, true));
@@ -267,7 +270,7 @@ protected:
if (ret < 0) {
isc_throw(IOError, "recvfrom failed: " << strerror(errno));
}
-
+
// Pass the message size back via the size parameter
size = ret;
}
@@ -507,11 +510,13 @@ protected:
vector<uint8_t> callback_data_;
ScopedSocket sock_;
boost::shared_ptr<isc::util::unittests::TestResolver> resolver_;
+ AbstractRunningQuery* running_query_;
};
RecursiveQueryTest::RecursiveQueryTest() :
dns_service_(NULL), callback_(NULL), callback_protocol_(0),
- callback_native_(-1), resolver_(new isc::util::unittests::TestResolver())
+ callback_native_(-1), resolver_(new isc::util::unittests::TestResolver()),
+ running_query_(NULL)
{
nsas_.reset(new isc::nsas::NameserverAddressStore(resolver_));
}
@@ -652,12 +657,12 @@ TEST_F(RecursiveQueryTest, forwarderSend) {
singleAddress(TEST_IPV4_ADDR, port));
Question q(Name("example.com"), RRClass::IN(), RRType::TXT());
- Message query_message(Message::RENDER);
- isc::resolve::initResponseMessage(q, query_message);
+ MessagePtr query_message(new Message(Message::RENDER));
+ isc::resolve::initResponseMessage(q, *query_message);
OutputBufferPtr buffer(new OutputBuffer(0));
MessagePtr answer(new Message(Message::RENDER));
- rq.forward(ConstMessagePtr(&query_message), answer, buffer, &server);
+ running_query_ = rq.forward(query_message, answer, buffer, &server);
char data[4096];
size_t size = sizeof(data);
@@ -693,37 +698,6 @@ createTestSocket() {
return (sock.release());
}
-int
-setSocketTimeout(int sock, size_t tv_sec, size_t tv_usec) {
- const struct timeval timeo = { tv_sec, tv_usec };
- int recv_options = 0;
- if (setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &timeo, sizeof(timeo))) {
- if (errno == ENOPROTOOPT) { // see RecursiveQueryTest::recvUDP()
- recv_options = MSG_DONTWAIT;
- } else {
- isc_throw(IOError, "set RCVTIMEO failed: " << strerror(errno));
- }
- }
- return (recv_options);
-}
-
-// try to read from the socket max time
-// *num is incremented for every succesfull read
-// returns true if it can read max times, false otherwise
-bool tryRead(int sock, int recv_options, size_t max, int* num) {
- size_t i = 0;
- do {
- char inbuff[512];
- if (recv(sock, inbuff, sizeof(inbuff), recv_options) < 0) {
- return false;
- } else {
- ++i;
- ++*num;
- }
- } while (i < max);
- return true;
-}
-
// Mock resolver callback for testing forward query.
class MockResolverCallback : public isc::resolve::ResolverInterface::Callback {
public:
@@ -780,11 +754,11 @@ TEST_F(RecursiveQueryTest, forwardQueryTimeout) {
Question question(Name("example.net"), RRClass::IN(), RRType::A());
OutputBufferPtr buffer(new OutputBuffer(0));
MessagePtr answer(new Message(Message::RENDER));
- Message query_message(Message::RENDER);
- isc::resolve::initResponseMessage(question, query_message);
+ MessagePtr query_message(new Message(Message::RENDER));
+ isc::resolve::initResponseMessage(question, *query_message);
boost::shared_ptr<MockResolverCallback> callback(new MockResolverCallback(&server));
- query.forward(ConstMessagePtr(&query_message), answer, buffer, &server, callback);
+ running_query_ = query.forward(query_message, answer, buffer, &server, callback);
// Run the test
io_service_.run();
EXPECT_EQ(callback->result, MockResolverCallback::FAILURE);
@@ -815,11 +789,11 @@ TEST_F(RecursiveQueryTest, forwardClientTimeout) {
1000, 10, 4000, 4);
Question q(Name("example.net"), RRClass::IN(), RRType::A());
OutputBufferPtr buffer(new OutputBuffer(0));
- Message query_message(Message::RENDER);
- isc::resolve::initResponseMessage(q, query_message);
+ MessagePtr query_message(new Message(Message::RENDER));
+ isc::resolve::initResponseMessage(q, *query_message);
boost::shared_ptr<MockResolverCallback> callback(new MockResolverCallback(&server));
- query.forward(ConstMessagePtr(&query_message), answer, buffer, &server, callback);
+ running_query_ = query.forward(query_message, answer, buffer, &server, callback);
// Run the test
io_service_.run();
EXPECT_EQ(callback->result, MockResolverCallback::FAILURE);
@@ -850,11 +824,12 @@ TEST_F(RecursiveQueryTest, forwardLookupTimeout) {
Question question(Name("example.net"), RRClass::IN(), RRType::A());
OutputBufferPtr buffer(new OutputBuffer(0));
- Message query_message(Message::RENDER);
- isc::resolve::initResponseMessage(question, query_message);
+ //Message query_message(Message::RENDER);
+ MessagePtr query_message(new Message(Message::RENDER));
+ isc::resolve::initResponseMessage(question, *query_message);
boost::shared_ptr<MockResolverCallback> callback(new MockResolverCallback(&server));
- query.forward(ConstMessagePtr(&query_message), answer, buffer, &server, callback);
+ running_query_ = query.forward(query_message, answer, buffer, &server, callback);
// Run the test
io_service_.run();
EXPECT_EQ(callback->result, MockResolverCallback::FAILURE);
@@ -885,11 +860,11 @@ TEST_F(RecursiveQueryTest, lowtimeouts) {
Question question(Name("example.net"), RRClass::IN(), RRType::A());
OutputBufferPtr buffer(new OutputBuffer(0));
- Message query_message(Message::RENDER);
- isc::resolve::initResponseMessage(question, query_message);
+ MessagePtr query_message(new Message(Message::RENDER));
+ isc::resolve::initResponseMessage(question, *query_message);
boost::shared_ptr<MockResolverCallback> callback(new MockResolverCallback(&server));
- query.forward(ConstMessagePtr(&query_message), answer, buffer, &server, callback);
+ running_query_ = query.forward(query_message, answer, buffer, &server, callback);
// Run the test
io_service_.run();
EXPECT_EQ(callback->result, MockResolverCallback::FAILURE);
@@ -904,7 +879,7 @@ TEST_F(RecursiveQueryTest, lowtimeouts) {
TEST_F(RecursiveQueryTest, DISABLED_recursiveSendOk) {
setDNSService(true, false);
bool done;
-
+
MockServerStop server(io_service_, &done);
vector<pair<string, uint16_t> > empty_vector;
RecursiveQuery rq(*dns_service_, *nsas_, cache_, empty_vector,
@@ -913,7 +888,7 @@ TEST_F(RecursiveQueryTest, DISABLED_recursiveSendOk) {
Question q(Name("www.isc.org"), RRClass::IN(), RRType::A());
OutputBufferPtr buffer(new OutputBuffer(0));
MessagePtr answer(new Message(Message::RENDER));
- rq.resolve(q, answer, buffer, &server);
+ running_query_ = rq.resolve(q, answer, buffer, &server);
io_service_.run();
// Check that the answer we got matches the one we wanted
@@ -930,7 +905,7 @@ TEST_F(RecursiveQueryTest, DISABLED_recursiveSendOk) {
TEST_F(RecursiveQueryTest, DISABLED_recursiveSendNXDOMAIN) {
setDNSService(true, false);
bool done;
-
+
MockServerStop server(io_service_, &done);
vector<pair<string, uint16_t> > empty_vector;
RecursiveQuery rq(*dns_service_, *nsas_, cache_, empty_vector,
@@ -939,7 +914,7 @@ TEST_F(RecursiveQueryTest, DISABLED_recursiveSendNXDOMAIN) {
Question q(Name("wwwdoesnotexist.isc.org"), RRClass::IN(), RRType::A());
OutputBufferPtr buffer(new OutputBuffer(0));
MessagePtr answer(new Message(Message::RENDER));
- rq.resolve(q, answer, buffer, &server);
+ running_query_ = rq.resolve(q, answer, buffer, &server);
io_service_.run();
// Check that the answer we got matches the one we wanted
@@ -1003,9 +978,9 @@ TEST_F(RecursiveQueryTest, CachedNS) {
// Prepare the recursive query
vector<pair<string, uint16_t> > roots;
roots.push_back(pair<string, uint16_t>("192.0.2.2", 53));
-
+ vector<pair<string, uint16_t> > upstream;
RecursiveQuery rq(*dns_service_, *nsas_, cache_,
- vector<pair<string, uint16_t> >(), roots);
+ upstream, roots);
// Ask a question at the bottom. It should not use the lower NS, because
// it would lead to a loop in NS. But it can use the nsUpper one, it has
// an IP address and we can avoid asking root.
@@ -1015,7 +990,7 @@ TEST_F(RecursiveQueryTest, CachedNS) {
MessagePtr answer(new Message(Message::RENDER));
// The server is here so we have something to pass there
MockServer server(io_service_);
- rq.resolve(q, answer, buffer, &server);
+ running_query_ = rq.resolve(q, answer, buffer, &server);
// We don't need to run the service in this test. We are interested only
// in the place it starts resolving at
diff --git a/src/lib/resolve/tests/recursive_query_unittest_2.cc b/src/lib/resolve/tests/recursive_query_unittest_2.cc
index 2b3d129..0b38c59 100644
--- a/src/lib/resolve/tests/recursive_query_unittest_2.cc
+++ b/src/lib/resolve/tests/recursive_query_unittest_2.cc
@@ -76,14 +76,17 @@ using namespace std;
/// directed to one or other of the "servers" in the RecursiveQueryTest2 class,
/// regardless of the glue returned in referrals.
+namespace {
+const char* const TEST_ADDRESS = "127.0.0.1"; ///< Servers are on this address
+const uint16_t TEST_PORT = 5301; ///< ... and this port
+const size_t BUFFER_SIZE = 1024; ///< For all buffers
+const char* const WWW_EXAMPLE_ORG = "192.0.2.254";
+ ///< Address of www.example.org
+} //end anonymous namespace
+
namespace isc {
namespace asiodns {
-const std::string TEST_ADDRESS = "127.0.0.1"; ///< Servers are on this address
-const uint16_t TEST_PORT = 5301; ///< ... and this port
-const size_t BUFFER_SIZE = 1024; ///< For all buffers
-const char* WWW_EXAMPLE_ORG = "192.0.2.254"; ///< Address of www.example.org
-
// As the test is fairly long and complex, debugging "print" statements have
// been left in although they are disabled. Set the following to "true" to
// enable them.
@@ -146,6 +149,12 @@ public:
OutputBufferPtr udp_send_buffer_; ///< Send buffer for UDP I/O
udp::socket udp_socket_; ///< Socket used by UDP server
+ /// Some of the tests cause an 'active' running query to be created, but
+ /// don't complete the framework that makes that query delete itself.
+ /// This member can be used to store it so that it is deleted automatically
+ /// when the test is finished.
+ AbstractRunningQuery* running_query_;
+
/// \brief Constructor
RecursiveQueryTest2() :
debug_(DEBUG_PRINT),
@@ -167,8 +176,18 @@ public:
udp_length_(0),
udp_receive_buffer_(),
udp_send_buffer_(new OutputBuffer(BUFFER_SIZE)),
- udp_socket_(service_.get_io_service(), udp::v4())
- {
+ udp_socket_(service_.get_io_service(), udp::v4()),
+ running_query_(NULL)
+ {}
+
+ ~RecursiveQueryTest2() {
+ // It would delete itself, but after the io_service_, which could
+ // segfailt in case there were unhandled requests
+ resolver_.reset();
+ // In a similar note, we wait until the resolver has been cleaned up
+ // until deleting and active test running_query_
+ delete running_query_;
+ delete nsas_;
}
/// \brief Set Common Message Bits
@@ -683,7 +702,7 @@ TEST_F(RecursiveQueryTest2, Resolve) {
// Kick off the resolution process. We expect the first question to go to
// "root".
expected_ = UDP_ROOT;
- query.resolve(question_, resolver_callback);
+ running_query_ = query.resolve(question_, resolver_callback);
service_.run();
// Check what ran. (We have to cast the callback to ResolverCallback as we
diff --git a/src/lib/resolve/tests/recursive_query_unittest_3.cc b/src/lib/resolve/tests/recursive_query_unittest_3.cc
index 168ec80..92ec589 100644
--- a/src/lib/resolve/tests/recursive_query_unittest_3.cc
+++ b/src/lib/resolve/tests/recursive_query_unittest_3.cc
@@ -69,15 +69,16 @@ using namespace std;
/// By using the "test_server_" element of RecursiveQuery, all queries are
/// directed to one or other of the "servers" in the RecursiveQueryTest3 class.
-namespace isc {
-namespace asiodns {
-
-const std::string TEST_ADDRESS3 = "127.0.0.1";
- ///< Servers are on this address
+namespace {
+const char* const TEST_ADDRESS3 = "127.0.0.1"; ///< Servers are on this address
const uint16_t TEST_PORT3 = 5303; ///< ... and this port
-const size_t BUFFER_SIZE = 1024; ///< For all buffers
+const size_t BUFFER_SIZE = 1024; ///< For all buffers
-const std::string DUMMY_ADDR3 = "1.2.3.4"; ///< address to return as A
+const char* const DUMMY_ADDR3 = "1.2.3.4"; ///< address to return as A
+} // end anonymous namespace
+
+namespace isc {
+namespace asiodns {
class MockResolver3 : public isc::resolve::ResolverInterface {
public:
@@ -131,6 +132,12 @@ public:
OutputBufferPtr udp_send_buffer_; ///< Send buffer for UDP I/O
udp::socket udp_socket_; ///< Socket used by UDP server
+ /// Some of the tests cause an 'active' running query to be created, but
+ /// don't complete the framework that makes that query delete itself.
+ /// This member can be used to store it so that it is deleted automatically
+ /// when the test is finished.
+ AbstractRunningQuery* running_query_;
+
/// \brief Constructor
RecursiveQueryTest3() :
service_(),
@@ -153,10 +160,21 @@ public:
udp_length_(0),
udp_receive_buffer_(),
udp_send_buffer_(new OutputBuffer(BUFFER_SIZE)),
- udp_socket_(service_.get_io_service(), udp::v4())
+ udp_socket_(service_.get_io_service(), udp::v4()),
+ running_query_(NULL)
{
}
+ ~RecursiveQueryTest3() {
+ delete nsas_;
+ // It would delete itself, but after the io_service_, which could
+ // segfailt in case there were unhandled requests
+ resolver_.reset();
+ // In a similar note, we wait until the resolver has been cleaned up
+ // until deleting and active test running_query_
+ delete running_query_;
+ }
+
/// \brief Set Common Message Bits
///
/// Sets up the common bits of a response message returned by the handlers.
@@ -541,7 +559,7 @@ TEST_F(RecursiveQueryTest3, Resolve) {
// Kick off the resolution process.
expected_ = EDNS_UDP;
- query.resolve(question_, resolver_callback);
+ running_query_ = query.resolve(question_, resolver_callback);
service_.run();
// Check what ran. (We have to cast the callback to ResolverCallback3 as we
diff --git a/src/lib/server_common/tests/portconfig_unittest.cc b/src/lib/server_common/tests/portconfig_unittest.cc
index ac880c0..0c971ee 100644
--- a/src/lib/server_common/tests/portconfig_unittest.cc
+++ b/src/lib/server_common/tests/portconfig_unittest.cc
@@ -18,6 +18,7 @@
#include <testutils/socket_request.h>
#include <testutils/mockups.h>
+#include <util/unittests/check_valgrind.h>
#include <util/unittests/resource.h>
#include <cc/data.h>
@@ -311,44 +312,48 @@ typedef InstallListenAddresses InstallListenAddressesDeathTest;
// We make the socket requestor throw a "fatal" exception, one where we can't be
// sure the state between processes is consistent. So we abort in that case.
TEST_F(InstallListenAddressesDeathTest, inconsistent) {
- AddressList deathAddresses;
- deathAddresses.push_back(AddressPair("192.0.2.3", 5288));
- // Make sure it actually kills the application (there should be an abort
- // in this case)
- EXPECT_DEATH({
- isc::util::unittests::dontCreateCoreDumps();
+ if (!isc::util::unittests::runningOnValgrind()) {
+ AddressList deathAddresses;
+ deathAddresses.push_back(AddressPair("192.0.2.3", 5288));
+ // Make sure it actually kills the application (there should be an abort
+ // in this case)
+ EXPECT_DEATH({
+ isc::util::unittests::dontCreateCoreDumps();
- try {
- installListenAddresses(deathAddresses, store_, dnss_);
- } catch (...) {
- // Prevent exceptions killing the application, we need
- // to make sure it dies the real hard way
- };
- }, "");
+ try {
+ installListenAddresses(deathAddresses, store_, dnss_);
+ } catch (...) {
+ // Prevent exceptions killing the application, we need
+ // to make sure it dies the real hard way
+ };
+ }, "");
+ }
}
// If we are unable to tell the boss we closed a socket, we abort, as we are
// not consistent with the boss most probably.
TEST_F(InstallListenAddressesDeathTest, cantClose) {
- installListenAddresses(valid_, store_, dnss_);
- AddressList empty;
- // Instruct it to fail on close
- sock_requestor_.break_release_ = true;
- EXPECT_DEATH({
- isc::util::unittests::dontCreateCoreDumps();
+ if (!isc::util::unittests::runningOnValgrind()) {
+ installListenAddresses(valid_, store_, dnss_);
+ AddressList empty;
+ // Instruct it to fail on close
+ sock_requestor_.break_release_ = true;
+ EXPECT_DEATH({
+ isc::util::unittests::dontCreateCoreDumps();
- try {
- // Setting to empty will close all current sockets.
- // And thanks to the break_release_, the close will
- // throw, which will make it crash.
- installListenAddresses(empty, store_, dnss_);
- } catch (...) {
- // To make sure it is killed by abort, not by some
- // (unhandled) exception
- };
- }, "");
- // And reset it back, so it can safely clean up itself.
- sock_requestor_.break_release_ = false;
+ try {
+ // Setting to empty will close all current sockets.
+ // And thanks to the break_release_, the close will
+ // throw, which will make it crash.
+ installListenAddresses(empty, store_, dnss_);
+ } catch (...) {
+ // To make sure it is killed by abort, not by some
+ // (unhandled) exception
+ };
+ }, "");
+ // And reset it back, so it can safely clean up itself.
+ sock_requestor_.break_release_ = false;
+ }
}
#endif // EXPECT_DEATH
diff --git a/src/lib/server_common/tests/socket_requestor_test.cc b/src/lib/server_common/tests/socket_requestor_test.cc
index 9878c63..ac1731f 100644
--- a/src/lib/server_common/tests/socket_requestor_test.cc
+++ b/src/lib/server_common/tests/socket_requestor_test.cc
@@ -410,7 +410,8 @@ private:
isc_throw(Unexpected,
"mkstemp() created a filename too long for sun_path");
}
- strncpy(socket_address.sun_path, path_, len);
+ strncpy(socket_address.sun_path, path_,
+ sizeof(socket_address.sun_path));
#ifdef HAVE_SA_LEN
socket_address.sun_len = len;
#endif
@@ -542,7 +543,6 @@ TEST_F(SocketRequestorTest, testSocketPassing) {
EXPECT_EQ("foo", socket_id.second);
EXPECT_EQ(0, close(socket_id.first));
}
-
// Create a second socket server, to test that multiple different
// domains sockets would work as well (even though we don't actually
// use that feature)
diff --git a/src/lib/testutils/dnsmessage_test.h b/src/lib/testutils/dnsmessage_test.h
index fd1d51a..02550eb 100644
--- a/src/lib/testutils/dnsmessage_test.h
+++ b/src/lib/testutils/dnsmessage_test.h
@@ -160,22 +160,6 @@ public:
private:
std::vector<isc::dns::ConstRRsetPtr>& rrsets_;
};
-
-class RRsetDumper {
-public:
- RRsetDumper(std::string& output) :
- output_(output)
- {}
- void operator()(isc::dns::ConstRRsetPtr rrset) {
- output_ += " " + rrset->toText();
-
- if (rrset->getRRsig()) {
- output_ += " " + rrset->getRRsig()->toText();
- }
- }
-private:
- std::string& output_;
-};
}
/// \brief A converter from a string to RRset.
@@ -201,6 +185,37 @@ isc::dns::RRsetPtr textToRRset(const std::string& text_rrset,
const isc::dns::Name& origin =
isc::dns::Name::ROOT_NAME());
+/// \brief Pull out signatures and convert to text
+///
+/// This is a helper function for rrsetsCheck.
+///
+/// It adds all the rrsets to the given vector. It also adds the
+/// signatures of those rrsets as separate rrsets into the vector
+/// (but does not modify the original rrset; i.e. technically the
+/// signatures are in the resulting vector twice).
+///
+/// Additionally, it adds the string representation of all rrsets
+/// and their signatures to the given string (for use in scoped_trace).
+///
+/// \param rrsets A vector to add the rrsets and signatures to
+/// \param text A string to add the rrsets string representations to
+/// \param begin The beginning of the rrsets iterator
+/// \param end The end of the rrsets iterator
+template <typename ITERATOR>
+void
+pullSigs(std::vector<isc::dns::ConstRRsetPtr>& rrsets,
+ std::string& text, ITERATOR begin, ITERATOR end)
+{
+ for (ITERATOR it = begin; it != end; ++it) {
+ rrsets.push_back(*it);
+ text += (*it)->toText();
+ if ((*it)->getRRsig()) {
+ rrsets.push_back((*it)->getRRsig());
+ text += (*it)->getRRsig()->toText();
+ }
+ }
+}
+
/// Set of unit tests to check if two sets of RRsets are identical.
///
/// This templated function takes two sets of sequences, each defined by
@@ -236,55 +251,50 @@ void
rrsetsCheck(EXPECTED_ITERATOR expected_begin, EXPECTED_ITERATOR expected_end,
ACTUAL_ITERATOR actual_begin, ACTUAL_ITERATOR actual_end)
{
- std::vector<isc::dns::ConstRRsetPtr> checked_rrsets; // for duplicate check
+ // Iterators can have their RRsig sets as separate RRsets,
+ // or they can have them attached to the RRset they cover.
+ // For ease of use of this method, we first flatten out both
+ // iterators, and pull out the signature sets, and add them as
+ // separate RRsets (rrsetCheck() later does not check signatures
+ // attached to rrsets)
+ std::vector<isc::dns::ConstRRsetPtr> expected_rrsets, actual_rrsets;
std::string expected_text, actual_text;
- std::for_each(expected_begin, expected_end,
- detail::RRsetDumper(expected_text));
- std::for_each(actual_begin, actual_end, detail::RRsetDumper(actual_text));
- unsigned int rrset_matched = 0;
- ACTUAL_ITERATOR it;
- for (it = actual_begin; it != actual_end; ++it) {
+
+ pullSigs(expected_rrsets, expected_text, expected_begin, expected_end);
+ pullSigs(actual_rrsets, actual_text, actual_begin, actual_end);
+
+ SCOPED_TRACE(std::string("Comparing two RRset lists:\n") +
+ "Actual:\n" + actual_text +
+ "Expected:\n" + expected_text);
+
+ // The vectors should have the same number of sets
+ ASSERT_EQ(expected_rrsets.size(), actual_rrsets.size());
+
+ // Now we check if all RRsets from the actual_rrsets are in
+ // expected_rrsets, and that actual_rrsets has no duplicates.
+ std::vector<isc::dns::ConstRRsetPtr> checked_rrsets; // for duplicate check
+
+ std::vector<isc::dns::ConstRRsetPtr>::const_iterator it;
+ for (it = actual_rrsets.begin(); it != actual_rrsets.end(); ++it) {
// Make sure there's no duplicate RRset in actual (using a naive
- // search). Since the actual set is guaranteed to be unique, we can
- // detect it if the expected data has a duplicate by the match/size
- // checks at the end of the function.
+ // search). By guaranteeing the actual set is unique, and the
+ // size of both vectors is the same, we can conclude that
+ // the two sets are identical after this loop.
// Note: we cannot use EXPECT_EQ for iterators
EXPECT_TRUE(checked_rrsets.end() ==
std::find_if(checked_rrsets.begin(), checked_rrsets.end(),
detail::RRsetMatch(*it)));
checked_rrsets.push_back(*it);
- EXPECTED_ITERATOR found_rrset_it =
- std::find_if(expected_begin, expected_end,
+ std::vector<isc::dns::ConstRRsetPtr>::const_iterator found_rrset_it =
+ std::find_if(expected_rrsets.begin(), expected_rrsets.end(),
detail::RRsetMatch(*it));
- if (found_rrset_it != expected_end) {
+ if (found_rrset_it != expected_rrsets.end()) {
rrsetCheck(*found_rrset_it, *it);
- ++rrset_matched;
- rrset_matched += (*it)->getRRsigDataCount();
+ } else {
+ FAIL() << (*it)->toText() << " not found in expected rrsets";
}
}
-
- {
- SCOPED_TRACE(std::string("Comparing two RRsets:\n") +
- "Actual:\n" + actual_text +
- "Expected:\n" + expected_text);
- // make sure all expected RRsets are in actual sets
- EXPECT_EQ(std::distance(expected_begin, expected_end), rrset_matched);
-
-#if (0)
- // TODO: see bug #2223. The following code was
- // disabled by #2165. The RRSIG RRsets are no longer directly
- // stored in the Message's rrsets, so the iterator will not find
- // them. The expected text used in many tests are flattened,
- // where the RRSIGs are inline. In other words, RRSIGs may occur
- // between (expected_begin, expected_end) but not between
- // (actual_begin, actual_end).
-
- // make sure rrsets only contains expected RRsets
- EXPECT_EQ(std::distance(expected_begin, expected_end),
- std::distance(actual_begin, actual_end));
-#endif
- }
}
/// Set of unit tests to check if two sets of RRsets are identical using
diff --git a/src/lib/util/Makefile.am b/src/lib/util/Makefile.am
index fe1b327..13f8f7b 100644
--- a/src/lib/util/Makefile.am
+++ b/src/lib/util/Makefile.am
@@ -33,3 +33,6 @@ EXTRA_DIST = python/pycppwrapper_util.h
libb10_util_la_LIBADD = $(top_builddir)/src/lib/exceptions/libb10-exceptions.la
CLEANFILES = *.gcno *.gcda
+
+libb10_util_includedir = $(includedir)/$(PACKAGE_NAME)/util
+libb10_util_include_HEADERS = buffer.h
diff --git a/src/lib/util/hash/sha1.cc b/src/lib/util/hash/sha1.cc
index 091e293..1fdadfd 100644
--- a/src/lib/util/hash/sha1.cc
+++ b/src/lib/util/hash/sha1.cc
@@ -48,7 +48,7 @@
* merchantability of this software or the suitability of this
* software for any particular purpose. It is provided "as is"
* without express or implied warranty of any kind.
- *
+ *
*/
#include <util/hash/sha1.h>
@@ -79,7 +79,7 @@ SHA_Parity(const uint32_t x, const uint32_t y, const uint32_t z) {
return ((x) ^ (y) ^ (z));
}
-static inline int
+static inline int
SHA1CircularShift(uint8_t bits, uint32_t word) {
return ((word << bits) | (word >> (32 - bits)));
}
diff --git a/src/lib/util/io/fd_share.cc b/src/lib/util/io/fd_share.cc
index 7adbbbe..8666441 100644
--- a/src/lib/util/io/fd_share.cc
+++ b/src/lib/util/io/fd_share.cc
@@ -142,6 +142,7 @@ send_fd(const int sock, const int fd) {
if (msghdr.msg_control == NULL) {
return (FD_OTHER_ERROR);
}
+ std::memset(msghdr.msg_control, 0, msghdr.msg_controllen);
struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msghdr);
cmsg->cmsg_len = cmsg_len(sizeof(int));
diff --git a/src/lib/util/locks.h b/src/lib/util/locks.h
index da9e9cd..f33ff96 100644
--- a/src/lib/util/locks.h
+++ b/src/lib/util/locks.h
@@ -42,13 +42,17 @@ class upgradable_mutex {
template <typename T>
class sharable_lock {
public:
- sharable_lock(T) { }
+ sharable_lock(T) {}
};
template <typename T>
class scoped_lock {
public:
- scoped_lock(T) { }
+ scoped_lock(T) {}
+
+ // We need to define this explicitly. Some versions of clang++ would
+ // complain about this otherwise. See Trac ticket #2340
+ ~scoped_lock() {}
void lock() {}
void unlock() {}
diff --git a/src/lib/util/tests/buffer_unittest.cc b/src/lib/util/tests/buffer_unittest.cc
index 9af3d57..02ca83d 100644
--- a/src/lib/util/tests/buffer_unittest.cc
+++ b/src/lib/util/tests/buffer_unittest.cc
@@ -18,6 +18,7 @@
#ifdef EXPECT_DEATH
#include <util/unittests/resource.h>
+#include <util/unittests/check_valgrind.h>
#endif /* EXPECT_DEATH */
#include <util/buffer.h>
@@ -188,16 +189,18 @@ TEST_F(BufferTest, outputBufferReadat) {
}
#ifdef EXPECT_DEATH
// We use assert now, so we check it dies
- EXPECT_DEATH({
- isc::util::unittests::dontCreateCoreDumps();
-
- try {
- obuffer[sizeof(testdata)];
- } catch (...) {
- // Prevent exceptions killing the application, we need
- // to make sure it dies the real hard way
- }
- }, "");
+ if (!isc::util::unittests::runningOnValgrind()) {
+ EXPECT_DEATH({
+ isc::util::unittests::dontCreateCoreDumps();
+
+ try {
+ obuffer[sizeof(testdata)];
+ } catch (...) {
+ // Prevent exceptions killing the application, we need
+ // to make sure it dies the real hard way
+ }
+ }, "");
+ }
#endif
}
diff --git a/src/lib/util/tests/fd_tests.cc b/src/lib/util/tests/fd_tests.cc
index 6ba2766..b709405 100644
--- a/src/lib/util/tests/fd_tests.cc
+++ b/src/lib/util/tests/fd_tests.cc
@@ -34,7 +34,9 @@ class FDTest : public ::testing::Test {
// We do not care what is inside, we just need it to be the same
data(new unsigned char[TEST_DATA_SIZE]),
buffer(NULL)
- { }
+ {
+ memset(data, 0, TEST_DATA_SIZE);
+ }
~ FDTest() {
delete[] data;
delete[] buffer;
diff --git a/src/lib/util/tests/lru_list_unittest.cc b/src/lib/util/tests/lru_list_unittest.cc
index bfb3b4d..c0201ea 100644
--- a/src/lib/util/tests/lru_list_unittest.cc
+++ b/src/lib/util/tests/lru_list_unittest.cc
@@ -168,7 +168,7 @@ protected:
entry7_(new TestEntry("eta", 1))
{}
- virtual ~LruListTest()
+ virtual ~LruListTest()
{}
boost::shared_ptr<TestEntry> entry1_;
@@ -355,7 +355,7 @@ TEST_F(LruListTest, Dropped) {
lru.add(entry5_);
EXPECT_NE(0, (entry2_->getCode() & 0x8000));
- // Delete an entry and check that the handler does not run.
+ // Delete an entry and check that the handler does not run.
EXPECT_EQ(0, (entry3_->getCode() & 0x8000));
lru.remove(entry3_);
EXPECT_EQ(0, (entry3_->getCode() & 0x8000));
@@ -386,7 +386,7 @@ TEST_F(LruListTest, Clear) {
EXPECT_NE(0, (entry1_->getCode() & 0x8000));
EXPECT_NE(0, (entry2_->getCode() & 0x8000));
EXPECT_NE(0, (entry3_->getCode() & 0x8000));
-
+
EXPECT_EQ(0, lru.size());
}
diff --git a/src/lib/util/threads/Makefile.am b/src/lib/util/threads/Makefile.am
index 85f70b9..121e4ab 100644
--- a/src/lib/util/threads/Makefile.am
+++ b/src/lib/util/threads/Makefile.am
@@ -2,10 +2,10 @@ SUBDIRS = . tests
AM_CXXFLAGS = $(B10_CXXFLAGS)
AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
-AM_CPPFLAGS += $(BOOST_INCLUDES)
+AM_CPPFLAGS += $(BOOST_INCLUDES) $(MULTITHREADING_FLAG)
lib_LTLIBRARIES = libb10-threads.la
-libb10_threads_la_SOURCES = lock.h lock.cc
+libb10_threads_la_SOURCES = sync.h sync.cc
libb10_threads_la_SOURCES += thread.h thread.cc
libb10_threads_la_LIBADD = $(top_builddir)/src/lib/exceptions/libb10-exceptions.la
diff --git a/src/lib/util/threads/lock.cc b/src/lib/util/threads/lock.cc
deleted file mode 100644
index de7bf07..0000000
--- a/src/lib/util/threads/lock.cc
+++ /dev/null
@@ -1,138 +0,0 @@
-// Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC")
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
-
-#include "lock.h"
-
-#include <exceptions/exceptions.h>
-
-#include <cstring>
-#include <memory>
-#include <cerrno>
-#include <cassert>
-
-#include <pthread.h>
-
-using std::auto_ptr;
-
-namespace isc {
-namespace util {
-namespace thread {
-
-class Mutex::Impl {
-public:
- Impl() :
- locked_count(0)
- {}
- pthread_mutex_t mutex;
- // Only in debug mode
- size_t locked_count;
-};
-
-namespace {
-
-struct Deinitializer {
- Deinitializer(pthread_mutexattr_t& attributes):
- attributes_(attributes)
- {}
- ~Deinitializer() {
- const int result = pthread_mutexattr_destroy(&attributes_);
- // This should never happen. According to the man page,
- // if there's error, it's our fault.
- assert(result == 0);
- }
- pthread_mutexattr_t& attributes_;
-};
-
-}
-
-Mutex::Mutex() :
- impl_(NULL)
-{
- pthread_mutexattr_t attributes;
- int result = pthread_mutexattr_init(&attributes);
- switch (result) {
- case 0: // All 0K
- break;
- case ENOMEM:
- throw std::bad_alloc();
- default:
- isc_throw(isc::InvalidOperation, strerror(result));
- }
- Deinitializer deinitializer(attributes);
- // TODO: Distinguish if debug mode is enabled in compilation.
- // If so, it should be PTHREAD_MUTEX_NORMAL or NULL
- result = pthread_mutexattr_settype(&attributes, PTHREAD_MUTEX_ERRORCHECK);
- if (result != 0) {
- isc_throw(isc::InvalidOperation, strerror(result));
- }
- auto_ptr<Impl> impl(new Impl);
- result = pthread_mutex_init(&impl->mutex, &attributes);
- switch (result) {
- case 0: // All 0K
- impl_ = impl.release();
- break;
- case ENOMEM:
- case EAGAIN:
- throw std::bad_alloc();
- default:
- isc_throw(isc::InvalidOperation, strerror(result));
- }
-}
-
-Mutex::~Mutex() {
- if (impl_ != NULL) {
- const int result = pthread_mutex_destroy(&impl_->mutex);
- const bool locked = impl_->locked_count != 0;
- delete impl_;
- // We don't want to throw from the destructor. Also, if this ever
- // fails, something is really screwed up a lot.
- assert(result == 0);
-
- // We should not try to destroy a locked mutex, bad threaded monsters
- // could get loose if we ever do and it is also forbidden by pthreads.
-
- // This should not be possible to happen, since the
- // pthread_mutex_destroy should check for it already. But it seems
- // there are systems that don't check it.
- assert(!locked);
- }
-}
-
-void
-Mutex::lock() {
- assert(impl_ != NULL);
- const int result = pthread_mutex_lock(&impl_->mutex);
- if (result != 0) {
- isc_throw(isc::InvalidOperation, strerror(result));
- }
- ++impl_->locked_count; // Only in debug mode
-}
-
-void
-Mutex::unlock() {
- assert(impl_ != NULL);
- --impl_->locked_count; // Only in debug mode
- const int result = pthread_mutex_unlock(&impl_->mutex);
- assert(result == 0); // This should never be possible
-}
-
-// TODO: Disable in non-debug build
-bool
-Mutex::locked() const {
- return (impl_->locked_count != 0);
-}
-
-}
-}
-}
diff --git a/src/lib/util/threads/lock.h b/src/lib/util/threads/lock.h
deleted file mode 100644
index fef537b..0000000
--- a/src/lib/util/threads/lock.h
+++ /dev/null
@@ -1,128 +0,0 @@
-// Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC")
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
-
-#ifndef B10_THREAD_LOCK_H
-#define B10_THREAD_LOCK_H
-
-#include <boost/noncopyable.hpp>
-
-#include <cstdlib> // for NULL.
-
-namespace isc {
-namespace util {
-namespace thread {
-
-/// \brief Mutex with very simple interface
-///
-/// Since mutexes are very system dependant, we create our own wrapper around
-/// whatever is available on the system and hide it.
-///
-/// To use this mutex, create it and then lock and unlock it by creating the
-/// Mutex::Locker object.
-///
-/// Also, as mutex is a low-level system object, an error might happen at any
-/// operation with it. We convert many errors to the isc::InvalidOperation,
-/// since the errors usually happen only when used in a wrong way. Any methods
-/// or constructors in this class can throw. Allocation errors are converted
-/// to std::bad_alloc (for example when OS-dependant limit of mutexes is
-/// exceeded). Some errors which usually mean a programmer error abort the
-/// program, since there could be no safe way to recover from them.
-///
-/// The current interface is somewhat minimalistic. If we ever need more, we
-/// can add it later.
-class Mutex : public boost::noncopyable {
-public:
- /// \brief Constructor.
- ///
- /// Creates a mutex. It is a non-recursive mutex (can be locked just once,
- /// if the same threads tries to lock it again, Bad Things Happen).
- ///
- /// Depending on compilation parameters and OS, the mutex may or may not
- /// do some error and sanity checking. However, such checking is meant
- /// only to aid development, not rely on it as a feature.
- ///
- /// \throw std::bad_alloc In case allocation of something (memory, the
- /// OS mutex) fails.
- /// \throw isc::InvalidOperation Other unspecified errors around the mutex.
- /// This should be rare.
- Mutex();
-
- /// \brief Destructor.
- ///
- /// Destroys the mutex. It is not allowed to destroy a mutex which is
- /// currently locked. This means a Locker created with this Mutex must
- /// never live longer than the Mutex itself.
- ~Mutex();
-
- /// \brief This holds a lock on a Mutex.
- ///
- /// To lock a mutex, create a locker. It'll get unlocked when the locker
- /// is destroyed.
- ///
- /// If you create the locker on the stack or using some other "garbage
- /// collecting" mechanism (auto_ptr, for example), it ensures exception
- /// safety with regards to the mutex - it'll get released on the exit
- /// of function no matter by what means.
- class Locker : public boost::noncopyable {
- public:
- /// \brief Constructor.
- ///
- /// Locks the mutex. May block for extended period of time.
- ///
- /// \throw isc::InvalidOperation when OS reports error. This usually
- /// means an attempt to use the mutex in a wrong way (locking
- /// a mutex second time from the same thread, for example).
- Locker(Mutex& mutex) :
- mutex_(NULL)
- {
- // Set the mutex_ after we acquire the lock. This is because of
- // exception safety. If lock() throws, it didn't work, so we must
- // not unlock when we are destroyed. In such case, mutex_ is
- // NULL and checked in the destructor.
- mutex.lock();
- mutex_ = &mutex;
- }
-
- /// \brief Destructor.
- ///
- /// Unlocks the mutex.
- ~Locker() {
- if (mutex_ != NULL) {
- mutex_->unlock();
- }
- }
- private:
- Mutex* mutex_;
- };
- /// \brief If the mutex is currently locked
- ///
- /// This is debug aiding method only. And it might be unavailable in
- /// non-debug build (because keeping the state might be needlesly
- /// slow).
- ///
- /// \todo Disable in non-debug build
- bool locked() const;
-private:
- class Impl;
- Impl* impl_;
- void lock();
- void unlock();
-};
-
-
-}
-}
-}
-
-#endif
diff --git a/src/lib/util/threads/sync.cc b/src/lib/util/threads/sync.cc
new file mode 100644
index 0000000..c98a7a6
--- /dev/null
+++ b/src/lib/util/threads/sync.cc
@@ -0,0 +1,213 @@
+// Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include "sync.h"
+
+#include <exceptions/exceptions.h>
+
+#include <cstring>
+#include <memory>
+#include <cerrno>
+#include <cassert>
+
+#include <pthread.h>
+
+using std::auto_ptr;
+
+namespace isc {
+namespace util {
+namespace thread {
+
+class Mutex::Impl {
+public:
+ Impl() :
+ locked_count(0)
+ {}
+ pthread_mutex_t mutex;
+ // Only in debug mode
+ size_t locked_count;
+};
+
+namespace {
+
+struct Deinitializer {
+ Deinitializer(pthread_mutexattr_t& attributes):
+ attributes_(attributes)
+ {}
+ ~Deinitializer() {
+ const int result = pthread_mutexattr_destroy(&attributes_);
+ // This should never happen. According to the man page,
+ // if there's error, it's our fault.
+ assert(result == 0);
+ }
+ pthread_mutexattr_t& attributes_;
+};
+
+}
+
+Mutex::Mutex() :
+ impl_(NULL)
+{
+ pthread_mutexattr_t attributes;
+ int result = pthread_mutexattr_init(&attributes);
+ switch (result) {
+ case 0: // All 0K
+ break;
+ case ENOMEM:
+ throw std::bad_alloc();
+ default:
+ isc_throw(isc::InvalidOperation, std::strerror(result));
+ }
+ Deinitializer deinitializer(attributes);
+ // TODO: Distinguish if debug mode is enabled in compilation.
+ // If so, it should be PTHREAD_MUTEX_NORMAL or NULL
+ result = pthread_mutexattr_settype(&attributes, PTHREAD_MUTEX_ERRORCHECK);
+ if (result != 0) {
+ isc_throw(isc::InvalidOperation, std::strerror(result));
+ }
+ auto_ptr<Impl> impl(new Impl);
+ result = pthread_mutex_init(&impl->mutex, &attributes);
+ switch (result) {
+ case 0: // All 0K
+ impl_ = impl.release();
+ break;
+ case ENOMEM:
+ case EAGAIN:
+ throw std::bad_alloc();
+ default:
+ isc_throw(isc::InvalidOperation, std::strerror(result));
+ }
+}
+
+Mutex::~Mutex() {
+ if (impl_ != NULL) {
+ const int result = pthread_mutex_destroy(&impl_->mutex);
+ const bool locked = impl_->locked_count != 0;
+ delete impl_;
+ // We don't want to throw from the destructor. Also, if this ever
+ // fails, something is really screwed up a lot.
+ assert(result == 0);
+
+ // We should not try to destroy a locked mutex, bad threaded monsters
+ // could get loose if we ever do and it is also forbidden by pthreads.
+
+ // This should not be possible to happen, since the
+ // pthread_mutex_destroy should check for it already. But it seems
+ // there are systems that don't check it.
+ assert(!locked);
+ }
+}
+
+void
+Mutex::postLockAction() {
+ // This assertion would fail only in non-debugging mode, in which case
+ // this method wouldn't be called either, so we simply assert the
+ // condition.
+ assert(impl_->locked_count == 0);
+ ++impl_->locked_count;
+}
+
+void
+Mutex::lock() {
+ assert(impl_ != NULL);
+ const int result = pthread_mutex_lock(&impl_->mutex);
+ if (result != 0) {
+ isc_throw(isc::InvalidOperation, std::strerror(result));
+ }
+ postLockAction(); // Only in debug mode
+}
+
+void
+Mutex::preUnlockAction(bool throw_ok) {
+ if (impl_->locked_count == 0) {
+ if (throw_ok) {
+ isc_throw(isc::InvalidOperation,
+ "Unlock attempt for unlocked mutex");
+ } else {
+ assert(false);
+ }
+ }
+ --impl_->locked_count;
+}
+
+void
+Mutex::unlock() {
+ assert(impl_ != NULL);
+ preUnlockAction(false); // Only in debug mode. Ensure no throw.
+ const int result = pthread_mutex_unlock(&impl_->mutex);
+ assert(result == 0); // This should never be possible
+}
+
+// TODO: Disable in non-debug build
+bool
+Mutex::locked() const {
+ return (impl_->locked_count != 0);
+}
+
+class CondVar::Impl {
+public:
+ Impl() {
+ const int result = pthread_cond_init(&cond_, NULL);
+ if (result != 0) {
+ isc_throw(isc::Unexpected, "pthread_cond_init failed: "
+ << std::strerror(result));
+ }
+ }
+ ~Impl() {
+ const int result = pthread_cond_destroy(&cond_);
+
+ // This can happen if we try to destroy cond_ while some other thread
+ // is waiting on it. assert() may be too strong for such a case,
+ // but we cannot safely destroy cond_ anyway. In order to avoid
+ // throwing from a destructor we simply let the process die.
+ assert(result == 0);
+ }
+
+ // For convenience allow the main class to access this directly.
+ pthread_cond_t cond_;
+};
+
+CondVar::CondVar() : impl_(new Impl)
+{}
+
+CondVar::~CondVar() {
+ delete impl_;
+}
+
+void
+CondVar::wait(Mutex& mutex) {
+ mutex.preUnlockAction(true); // Only in debug mode
+ const int result = pthread_cond_wait(&impl_->cond_, &mutex.impl_->mutex);
+ mutex.postLockAction(); // Only in debug mode
+
+ // pthread_cond_wait should normally succeed unless mutex is completely
+ // broken.
+ if (result != 0) {
+ isc_throw(isc::BadValue, "pthread_cond_wait failed unexpectedly: " <<
+ std::strerror(result));
+ }
+}
+
+void
+CondVar::signal() {
+ const int result = pthread_cond_signal(&impl_->cond_);
+
+ // pthread_cond_signal() can only fail when if cond_ is invalid. It
+ //should be impossible as long as this is a valid CondVar object.
+ assert(result == 0);
+}
+
+}
+}
+}
diff --git a/src/lib/util/threads/sync.h b/src/lib/util/threads/sync.h
new file mode 100644
index 0000000..ff56999
--- /dev/null
+++ b/src/lib/util/threads/sync.h
@@ -0,0 +1,223 @@
+// Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef B10_THREAD_SYNC_H
+#define B10_THREAD_SYNC_H
+
+#include <boost/noncopyable.hpp>
+
+#include <cstdlib> // for NULL.
+
+namespace isc {
+namespace util {
+namespace thread {
+class CondVar;
+
+/// \brief Mutex with very simple interface
+///
+/// Since mutexes are very system dependant, we create our own wrapper around
+/// whatever is available on the system and hide it.
+///
+/// To use this mutex, create it and then lock and unlock it by creating the
+/// Mutex::Locker object.
+///
+/// Also, as mutex is a low-level system object, an error might happen at any
+/// operation with it. We convert many errors to the isc::InvalidOperation,
+/// since the errors usually happen only when used in a wrong way. Any methods
+/// or constructors in this class can throw. Allocation errors are converted
+/// to std::bad_alloc (for example when OS-dependant limit of mutexes is
+/// exceeded). Some errors which usually mean a programmer error abort the
+/// program, since there could be no safe way to recover from them.
+///
+/// The current interface is somewhat minimalistic. If we ever need more, we
+/// can add it later.
+class Mutex : boost::noncopyable {
+public:
+ /// \brief Constructor.
+ ///
+ /// Creates a mutex. It is a non-recursive mutex (can be locked just once,
+ /// if the same threads tries to lock it again, Bad Things Happen).
+ ///
+ /// Depending on compilation parameters and OS, the mutex may or may not
+ /// do some error and sanity checking. However, such checking is meant
+ /// only to aid development, not rely on it as a feature.
+ ///
+ /// \throw std::bad_alloc In case allocation of something (memory, the
+ /// OS mutex) fails.
+ /// \throw isc::InvalidOperation Other unspecified errors around the mutex.
+ /// This should be rare.
+ Mutex();
+
+ /// \brief Destructor.
+ ///
+ /// Destroys the mutex. It is not allowed to destroy a mutex which is
+ /// currently locked. This means a Locker created with this Mutex must
+ /// never live longer than the Mutex itself.
+ ~Mutex();
+
+ /// \brief This holds a lock on a Mutex.
+ ///
+ /// To lock a mutex, create a locker. It'll get unlocked when the locker
+ /// is destroyed.
+ ///
+ /// If you create the locker on the stack or using some other "garbage
+ /// collecting" mechanism (auto_ptr, for example), it ensures exception
+ /// safety with regards to the mutex - it'll get released on the exit
+ /// of function no matter by what means.
+ class Locker : boost::noncopyable {
+ public:
+ /// \brief Constructor.
+ ///
+ /// Locks the mutex. May block for extended period of time.
+ ///
+ /// \throw isc::InvalidOperation when OS reports error. This usually
+ /// means an attempt to use the mutex in a wrong way (locking
+ /// a mutex second time from the same thread, for example).
+ Locker(Mutex& mutex) :
+ mutex_(mutex)
+ {
+ mutex.lock();
+ }
+
+ /// \brief Destructor.
+ ///
+ /// Unlocks the mutex.
+ ~Locker() {
+ mutex_.unlock();
+ }
+ private:
+ Mutex& mutex_;
+ };
+ /// \brief If the mutex is currently locked
+ ///
+ /// This is debug aiding method only. And it might be unavailable in
+ /// non-debug build (because keeping the state might be needlesly
+ /// slow).
+ ///
+ /// \todo Disable in non-debug build
+ bool locked() const;
+private:
+ friend class CondVar;
+
+ // Commonly called after acquiring the lock, checking and updating
+ // internal state for debug.
+ void postLockAction();
+
+ // Commonly called before releasing the lock, checking and updating
+ // internal state for debug.
+ //
+ // If throw_ok is true, it throws \c isc::InvalidOperation when the check
+ // fails; otherwise it aborts the process. This parameter must be set
+ // to false if the call to this shouldn't result in an exception (e.g.
+ // when called from a destructor).
+ void preUnlockAction(bool throw_ok);
+
+ class Impl;
+ Impl* impl_;
+ void lock();
+ void unlock();
+};
+
+/// \brief Encapsulation for a condition variable.
+///
+/// This class provides a simple encapsulation of condition variable for
+/// inter-thread synchronization. It has similar but simplified interface as
+/// that for \c pthread_cond_ variants.
+///
+/// It uses the \c Mutex class object for the mutex used with the condition
+/// variable. Since for normal applications the internal \c Mutex::Locker
+/// class is the only available interface to acquire a lock, sample code
+/// for waiting on a condition variable would look like this:
+/// \code
+/// CondVar cond;
+/// Mutex mutex;
+/// {
+/// Mutex::Locker locker(mutex);
+/// while (some_condition) {
+/// cond.wait(mutex);
+/// }
+/// // do something under the protection of locker
+/// } // lock is released here
+/// \endcode
+/// Note that \c mutex passed to the \c wait() method must be the same one
+/// used to construct the \c locker.
+///
+/// Right now there is no equivalent to pthread_cond_broadcast() or
+/// pthread_cond_timedwait() in this class, because this class is meant
+/// for internal development of BIND 10 and we don't need these at the
+/// moment. If and when we need these interfaces they can be added at that
+/// point.
+///
+/// \note This class is defined as a friend class of \c Mutex and directly
+/// refers to and modifies private internals of the \c Mutex class. It breaks
+/// the assumption that the lock is only acquired or released via the
+/// \c Locker class and breaks other integrity assumption on \c Mutex,
+/// thereby making it more fragile, but we couldn't find other way to
+/// implement a safe and still simple realization of condition variables.
+/// So, this is a kind of compromise. If this class is needed to be
+/// extended, first consider a way to use public interfaces of \c Mutex;
+/// do not easily rely on the fact that this class is a friend of it.
+class CondVar : boost::noncopyable {
+public:
+ /// \brief Constructor.
+ ///
+ /// \throw std::bad_alloc memory allocation failure
+ /// \throw isc::Unexpected other unexpected shortage of system resource
+ CondVar();
+
+ /// \brief Destructor.
+ ///
+ /// An object of this class must not be destroyed while some thread
+ /// is waiting on it. If this condition isn't met the destructor will
+ /// terminate the program.
+ ~CondVar();
+
+ /// \brief Wait on the condition variable.
+ ///
+ /// This method works like \c pthread_cond_wait(). For mutex it takes
+ /// an \c Mutex class object. A lock for the mutex must have been
+ /// acquired. If this condition isn't met, it can throw an exception
+ /// (in the debug mode build) or result in undefined behavior.
+ ///
+ /// The lock will be automatically released within this method, and
+ /// will be re-acquired on the exit of this method.
+ ///
+ /// \throw isc::InvalidOperation mutex isn't locked
+ /// \throw isc::BadValue mutex is not a valid \c Mutex object
+ ///
+ /// \param mutex A \c Mutex object to be released on wait().
+ void wait(Mutex& mutex);
+
+ /// \brief Unblock a thread waiting for the condition variable.
+ ///
+ /// This method wakes one of other threads (if any) waiting on this object
+ /// via the \c wait() call.
+ ///
+ /// This method never throws; if some unexpected low level error happens
+ /// it terminates the program.
+ void signal();
+private:
+ class Impl;
+ Impl* impl_;
+};
+
+} // namespace thread
+} // namespace util
+} // namespace isc
+
+#endif
+
+// Local Variables:
+// mode: c++
+// End:
diff --git a/src/lib/util/threads/tests/.gitignore b/src/lib/util/threads/tests/.gitignore
new file mode 100644
index 0000000..d6d1ec8
--- /dev/null
+++ b/src/lib/util/threads/tests/.gitignore
@@ -0,0 +1 @@
+/run_unittests
diff --git a/src/lib/util/threads/tests/Makefile.am b/src/lib/util/threads/tests/Makefile.am
index e86aedc..a12d221 100644
--- a/src/lib/util/threads/tests/Makefile.am
+++ b/src/lib/util/threads/tests/Makefile.am
@@ -1,7 +1,7 @@
SUBDIRS = .
AM_CPPFLAGS = -I$(top_builddir)/src/lib -I$(top_srcdir)/src/lib
-AM_CPPFLAGS += $(BOOST_INCLUDES)
+AM_CPPFLAGS += $(BOOST_INCLUDES) $(MULTITHREADING_FLAG)
# XXX: we'll pollute the top builddir for creating a temporary test file
# # used to bind a UNIX domain socket so we can minimize the risk of exceeding
# # the limit of file name path size.
@@ -23,9 +23,10 @@ TESTS += run_unittests
run_unittests_SOURCES = run_unittests.cc
run_unittests_SOURCES += thread_unittest.cc
run_unittests_SOURCES += lock_unittest.cc
+run_unittests_SOURCES += condvar_unittest.cc
run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
-run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
+run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS) $(PTHREAD_LDFLAGS)
run_unittests_LDADD = $(top_builddir)/src/lib/util/threads/libb10-threads.la
run_unittests_LDADD += \
diff --git a/src/lib/util/threads/tests/condvar_unittest.cc b/src/lib/util/threads/tests/condvar_unittest.cc
new file mode 100644
index 0000000..77a8980
--- /dev/null
+++ b/src/lib/util/threads/tests/condvar_unittest.cc
@@ -0,0 +1,162 @@
+// Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <exceptions/exceptions.h>
+
+#include <util/threads/sync.h>
+#include <util/threads/thread.h>
+
+#include <gtest/gtest.h>
+
+#include <boost/bind.hpp>
+#include <boost/scoped_ptr.hpp>
+
+#include <cstring>
+
+#include <unistd.h>
+#include <signal.h>
+
+using namespace isc::util::thread;
+
+namespace {
+// Used as a signal handler below.
+volatile bool do_exit; // use for emergency escape
+void
+alarmHandler(int) {
+ do_exit = true;
+}
+
+class CondVarTest : public ::testing::Test {
+protected:
+ // We use a signal in case some of the thread synchronization tests
+ // unexpectedly cause a deadlock.
+ void SetUp() {
+ do_exit = false;
+
+ std::memset(&handler_, 0, sizeof(handler_));
+ handler_.sa_handler = alarmHandler;
+ if (sigaction(SIGALRM, &handler_, &original_) != 0) {
+ FAIL() << "Couldn't set alarm";
+ }
+ alarm(10); // 10sec duration: arbitrary choice
+ }
+ void TearDown() {
+ // Cancel the alarm and return the original handler
+ alarm(0);
+ if (sigaction(SIGALRM, &original_, NULL)) {
+ FAIL() << "Couldn't restore alarm";
+ }
+ }
+
+ CondVar condvar_;
+ Mutex mutex_;
+private:
+ struct sigaction handler_, original_;
+};
+
+TEST(CondVarTest0, create) {
+ // Just construct and destruct it. Nothing unusual should happen.
+ EXPECT_NO_THROW(CondVar condvar);
+}
+
+// Running on a separate thread, just updating the argument and waking up
+// the other thread via the condition variable passed.
+void
+ringSignal(CondVar* condvar, Mutex* mutex, int* arg) {
+ assert(*arg == 0);
+ Mutex::Locker locker(*mutex);
+ ++*arg;
+ condvar->signal();
+}
+
+// A simple wait-signal operation on a condition variable.
+TEST_F(CondVarTest, waitAndSignal) {
+ Mutex::Locker locker(mutex_);
+ int shared_var = 0; // let the other thread increment this
+ Thread t(boost::bind(&ringSignal, &condvar_, &mutex_, &shared_var));
+ condvar_.wait(mutex_);
+ t.wait();
+ EXPECT_EQ(1, shared_var);
+}
+
+// Thread's main code for the next test
+void
+signalAndWait(CondVar* condvar1, CondVar* condvar2, Mutex* mutex, int* arg) {
+ Mutex::Locker locker(*mutex);
+ ++*arg;
+ condvar2->signal(); // let the main thread know this one is ready
+ condvar1->wait(*mutex);
+ ++*arg;
+}
+
+// Similar to the previous test, but having two threads wait for a condvar.
+TEST_F(CondVarTest, multiWaits) {
+ boost::scoped_ptr<Mutex::Locker> locker(new Mutex::Locker(mutex_));
+ CondVar condvar2; // separate cond var for initial synchronization
+ int shared_var = 0; // let the other thread increment this
+ Thread t1(boost::bind(&signalAndWait, &condvar_, &condvar2, &mutex_,
+ &shared_var));
+ Thread t2(boost::bind(&signalAndWait, &condvar_, &condvar2, &mutex_,
+ &shared_var));
+
+ // Wait until both threads are waiting on condvar_.
+ while (shared_var < 2 && !do_exit) {
+ condvar2.wait(mutex_);
+ }
+ // Check we exited from the loop successfully.
+ ASSERT_FALSE(do_exit);
+ ASSERT_EQ(2, shared_var);
+
+ // release the lock, wake up both threads, wait for them to die, and
+ // confirm they successfully woke up.
+ locker.reset();
+ condvar_.signal();
+ condvar_.signal();
+ t1.wait();
+ t2.wait();
+ EXPECT_EQ(4, shared_var);
+}
+
+// Similar to the previous version of the same function, but just do
+// condvar operations. It will never wake up.
+void
+signalAndWait(CondVar* condvar, Mutex* mutex) {
+ Mutex::Locker locker(*mutex);
+ condvar->signal();
+ condvar->wait(*mutex);
+}
+
+// Temporarily disabled: Solaris seems to make this behavior "undefined"
+TEST_F(CondVarTest, DISABLED_destroyWhileWait) {
+ // We'll destroy a CondVar object while the thread is still waiting
+ // on it. This will trigger an assertion failure.
+ EXPECT_DEATH_IF_SUPPORTED({
+ CondVar cond;
+ Mutex::Locker locker(mutex_);
+ Thread t(boost::bind(&signalAndWait, &cond, &mutex_));
+ cond.wait(mutex_);
+ }, "");
+}
+
+TEST_F(CondVarTest, badWait) {
+ // In our implementation, wait() requires acquiring the lock beforehand.
+ EXPECT_THROW(condvar_.wait(mutex_), isc::InvalidOperation);
+}
+
+TEST_F(CondVarTest, emptySignal) {
+ // It's okay to call signal when no one waits.
+ EXPECT_NO_THROW(condvar_.signal());
+}
+
+}
diff --git a/src/lib/util/threads/tests/lock_unittest.cc b/src/lib/util/threads/tests/lock_unittest.cc
index 0b4d3ce..9c17e6f 100644
--- a/src/lib/util/threads/tests/lock_unittest.cc
+++ b/src/lib/util/threads/tests/lock_unittest.cc
@@ -12,11 +12,12 @@
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
-#include <util/threads/lock.h>
-#include <util/threads/thread.h>
-
#include <gtest/gtest.h>
+#include <util/threads/sync.h>
+#include <util/threads/thread.h>
+#include <util/unittests/check_valgrind.h>
+
#include <boost/bind.hpp>
#include <unistd.h>
#include <signal.h>
@@ -42,13 +43,15 @@ TEST(MutexTest, lockMultiple) {
// Destroying a locked mutex is a bad idea as well
#ifdef EXPECT_DEATH
TEST(MutexTest, destroyLocked) {
- EXPECT_DEATH({
- Mutex* mutex = new Mutex;
- new Mutex::Locker(*mutex);
- delete mutex;
- // This'll leak the locker, but inside the slave process, it should
- // not be an issue.
- }, "");
+ if (!isc::util::unittests::runningOnValgrind()) {
+ EXPECT_DEATH({
+ Mutex* mutex = new Mutex;
+ new Mutex::Locker(*mutex);
+ delete mutex;
+ // This'll leak the locker, but inside the slave process, it should
+ // not be an issue.
+ }, "");
+ }
}
#endif
@@ -80,13 +83,13 @@ performIncrement(volatile double* canary, volatile bool* ready_me,
}
void
-no_handler(int) {}
+noHandler(int) {}
TEST(MutexTest, swarm) {
// Create a timeout in case something got stuck here
struct sigaction ignored, original;
- memset(&ignored, 0, sizeof ignored);
- ignored.sa_handler = no_handler;
+ memset(&ignored, 0, sizeof(ignored));
+ ignored.sa_handler = noHandler;
if (sigaction(SIGALRM, &ignored, &original)) {
FAIL() << "Couldn't set alarm";
}
diff --git a/src/lib/util/threads/thread.cc b/src/lib/util/threads/thread.cc
index 3382fe2..5608e49 100644
--- a/src/lib/util/threads/thread.cc
+++ b/src/lib/util/threads/thread.cc
@@ -13,7 +13,7 @@
// PERFORMANCE OF THIS SOFTWARE.
#include "thread.h"
-#include "lock.h"
+#include "sync.h"
#include <memory>
#include <string>
@@ -115,7 +115,7 @@ Thread::Thread(const boost::function<void ()>& main) :
case EAGAIN:
throw std::bad_alloc();
default: // Other errors. They should not happen.
- isc_throw(isc::InvalidOperation, strerror(result));
+ isc_throw(isc::InvalidOperation, std::strerror(result));
}
}
@@ -139,7 +139,7 @@ Thread::wait() {
const int result = pthread_join(impl_->tid_, NULL);
if (result != 0) {
- isc_throw(isc::InvalidOperation, strerror(result));
+ isc_throw(isc::InvalidOperation, std::strerror(result));
}
// Was there an exception in the thread?
diff --git a/src/lib/util/unittests/Makefile.am b/src/lib/util/unittests/Makefile.am
index 451ab4e..55e0372 100644
--- a/src/lib/util/unittests/Makefile.am
+++ b/src/lib/util/unittests/Makefile.am
@@ -7,6 +7,7 @@ libutil_unittests_la_SOURCES += newhook.h newhook.cc
libutil_unittests_la_SOURCES += testdata.h testdata.cc
if HAVE_GTEST
libutil_unittests_la_SOURCES += resource.h resource.cc
+libutil_unittests_la_SOURCES += check_valgrind.h check_valgrind.cc
libutil_unittests_la_SOURCES += run_all.h run_all.cc
libutil_unittests_la_SOURCES += textdata.h
libutil_unittests_la_SOURCES += wiredata.h wiredata.cc
diff --git a/src/lib/util/unittests/check_valgrind.cc b/src/lib/util/unittests/check_valgrind.cc
new file mode 100644
index 0000000..2951d25
--- /dev/null
+++ b/src/lib/util/unittests/check_valgrind.cc
@@ -0,0 +1,41 @@
+// Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <config.h>
+
+namespace isc {
+namespace util {
+namespace unittests {
+
+#if HAVE_VALGRIND_HEADERS
+#include <valgrind/valgrind.h>
+/// \brief Check if the program is run in valgrind
+///
+/// \return true if valgrind headers are available, and valgrind is running,
+/// false if the headers are not available, or if valgrind is not
+/// running
+bool
+runningOnValgrind() {
+ return (RUNNING_ON_VALGRIND != 0);
+}
+#else
+bool
+runningOnValgrind() {
+ return (false);
+}
+#endif // HAVE_VALGRIND_HEADERS
+
+} // end of namespace unittests
+} // end of namespace util
+} // end of namespace isc
diff --git a/src/lib/util/unittests/check_valgrind.h b/src/lib/util/unittests/check_valgrind.h
new file mode 100644
index 0000000..c08b58b
--- /dev/null
+++ b/src/lib/util/unittests/check_valgrind.h
@@ -0,0 +1,50 @@
+// Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+//
+// If we have the valgrind headers available, we can detect whether
+// valgrind is running. This should normally never be done, as you
+// want the to test the actual code in operation with valgrind.
+//
+// However, there is a limited set of operations where we want to
+// skip some tests if run under valgrind, most notably the
+// EXPECT_DEATH tests, as these would report memory leaks by
+// definition.
+//
+// If the valgrind headers are NOT available, the method checkValgrind()
+// always returns false; i.e. it always pretends the program is run
+// natively
+//
+
+#ifndef __UTIL_UNITTESTS_CHECK_VALGRIND_H
+#define __UTIL_UNITTESTS_CHECK_VALGRIND_H 1
+
+#include <config.h>
+
+namespace isc {
+namespace util {
+namespace unittests {
+
+/// \brief Check if the program is run in valgrind
+///
+/// \return true if valgrind headers are available, and valgrind is running,
+/// false if the headers are not available, or if valgrind is not
+/// running
+bool runningOnValgrind();
+
+} // end namespace unittests
+} // end namespace util
+} // end namespace isc
+
+#endif // __UTIL_UNITTESTS_CHECK_VALGRIND_H
diff --git a/tests/tools/perfdhcp/templates/.gitignore b/tests/tools/perfdhcp/templates/.gitignore
new file mode 100644
index 0000000..6f865da
--- /dev/null
+++ b/tests/tools/perfdhcp/templates/.gitignore
@@ -0,0 +1,5 @@
+/test1.hex
+/test2.hex
+/test3.hex
+/test4.hex
+/test5.hex
More information about the bind10-changes
mailing list