BIND 10 trac1644, updated. 3d8ac71299d439906a11820e4ef914eb18b9d1d3 [trac1644]Merge branch 'master' into trac1644
BIND 10 source code commits
bind10-changes at lists.isc.org
Thu Mar 15 01:57:10 UTC 2012
The branch, trac1644 has been updated
via 3d8ac71299d439906a11820e4ef914eb18b9d1d3 (commit)
via 6b1caed1a4161932e68ef52f067d07d822d85c94 (commit)
via 2b01d944b6a137f95d47673ea8367315289c205d (commit)
via ee3042d8a998de0a2a272003f53957a1af500901 (commit)
via 6e7eee31fa6b4884490778ce4148eb346fa4bf28 (commit)
via 2b13ceb6879f7c02662a0973d5c37333b3b3d660 (commit)
via 71032a09e3cc7e34133378b426ad83d13ca3e0ac (commit)
via 42991798446e79e8e5e60641430d3d5a8cc0c1bd (commit)
via 99c9c5e589d0359c115265864302b8dfefb210db (commit)
via ead42e5f5189b6fbd72823d6931eddff10409d9a (commit)
via 3adabc6a1ece55ee01bc5b0d6aee8aebca2510d2 (commit)
via 1cd0d0e4fc9324bbe7f8593478e2396d06337b1e (commit)
via 14d4b5dd3ef4cb9cee473e70aaeaa5d63dac6113 (commit)
via abfbe328e4a378f63a4eb5b3fb95949a38d21557 (commit)
via 2e940ea65d5b9f371c26352afd9e66719c38a6b9 (commit)
via 4a82ece4c4e353ab8e3ceb01fd8e0f4824ac6bbf (commit)
via b8960ab85c717fe70ad282e0052ac0858c5b57f7 (commit)
via f537c7e12fb7b25801408f93132ed33410edae76 (commit)
via 737c22c1d763e638bd958a5507bb2012267c8a22 (commit)
via bfe0f613e15f8c732462f43677f846610e496e08 (commit)
via 81d738596851cc2f0d4e475c9c26e7e41c8bc1f7 (commit)
via d7fb4b7244e60a034e21873d5bd32f7148ccd973 (commit)
via d9ae23e5bc7ef47d29953bf54f2735d8f6f5f531 (commit)
via 5e17c750847213e462b9ca7537358d3d97975f61 (commit)
via 736fb87e53b0d57241c4e414ed5de2f3eacc6e6e (commit)
via 4cee65dd891d8dac00680084680fa57d89bea82d (commit)
via 5a8474bf5e3b8e10959c76b573d3f0e1af03ebf1 (commit)
via cdbcf6b6134197bb28a3ee1c5460e78f9e89cdb5 (commit)
via 516e15b88a729bc28ec04ee2713aa6b352f38875 (commit)
via 510292e639de813a6f8b02ee3e36726fae1da5d8 (commit)
via a0a39d4b6e67fe575206ae191ddadc2f6f29e35c (commit)
via cb40afcbf13f2f456f5e671c755db9457c04e012 (commit)
via 97fa3578b37fce3897f78bfd59990ec9928822b9 (commit)
via b5740c6b3962a55e46325b3c8b14c9d64cf0d845 (commit)
via bc65585535ce775f6a5cf8dae25dfbda98a489ae (commit)
via a2a9e24d6a5744ab6b916c8e94ab0b77137417f2 (commit)
via b3933db8909e3040dec00fffd670b0963e264506 (commit)
via d08e8c4fca2bdcbe3fd573c1872c8e9347a71fb0 (commit)
via 2730ac6f20e6512c9c72cf22e0fe223f9bf66b09 (commit)
via c12cdc5ffe704bca024a84223774871e5049f90f (commit)
via 13fe9c1bdcc2e0a1c8947a328429e6f301c6fb3e (commit)
via 55b948ebbc47f5ab7a66eff50083921d9eb740bf (commit)
via d908fc0e44923c9e9196e137f4b8cc6a1a285568 (commit)
via 5b7155e4ac83a12410038ea4bcba080689378d73 (commit)
via ba1bfc79c7079e9c0886d542229c917d305695d9 (commit)
via b33929ed5df7c8f482d095e96e667d4a03180c78 (commit)
via cb28f3c4199610fb06c8939f5b5d95b99ae7a071 (commit)
via 64611b969652b074976c72867bc78ca43f8e51be (commit)
via 6f31530ff7e74757980eb2d01b82852636991672 (commit)
via 29fab3811a8bbae6bc28198f7aec4378b338a03c (commit)
via 1633572f050616e4fc41502fa2bcbfd70ea594ef (commit)
via 70778ebed8f5d0af186460600facc563865bafe8 (commit)
via ef1eba02e4cf550e48e7318702cff6d67c1ec82e (commit)
via 184bd43c0451a18f9d7d792050c9089f8d329a1c (commit)
via b2e7f7e47b9ac2dc93ca57c63635255a9a50fbe7 (commit)
via de4b10ee8d53f5c9537ba98ad401f84d008efd69 (commit)
via 9a5e82f81a296fb59dfe75c7f47fe91471fcb13c (commit)
via 1d3d7c590668be4503e70c2d3f2ee1da955874ac (commit)
via 3e88cb09890b3d05c82c56be350b1b76325dff15 (commit)
via ac299353640a32e77ad0e3f630d1a6bdbfdbbb06 (commit)
via 118f1f9d41fed38bb12d46f41d50dd2fd7367a80 (commit)
via ff863e03a8cbdb0c971a101af01b604dc13d1457 (commit)
via ee3e3d95c2554eed560ae6fea8f24864d0f32074 (commit)
via d2e2cb97e821844dc430ef1e47d13d618c06d9fb (commit)
via 6b3dabe34a4fafac4a91fd0b953b49dfb846b713 (commit)
via 4bcec5ffb6b84ccc0e3b6598567d1e0b67b95976 (commit)
via 9e89c7638a259fd103316f2b7f9be539b2cf3dce (commit)
via 1202e2c13c17c4e3013c635f9e4ef8629c9c7b69 (commit)
via eddbaf44858226fafadfad360eba55a79f69f085 (commit)
via 56f14dee951465a712f2fee1d4dcf36d7b87a4b7 (commit)
via ea85b4abd01332057c8a8a50e729baf444baaaca (commit)
via d0111f488addd8cf0f4a7217ce09a91e68cc04cc (commit)
via d93639f9d862eee755aecbde69e37f40543f7109 (commit)
via 48b2af87d906f6280ab477950362e76f73b23482 (commit)
via c271fcc053b0ff9b2e7273e992591549258c18b7 (commit)
via 03f679c989087d9d4a5e403bf37b2c99ad7c5ecd (commit)
via dd519159e091b7cc977b47692a45800157ed65ea (commit)
via 103b3ba8ad4891afa8f808fd9b1187983737a0b6 (commit)
via 567c9411dc8574817c4155ac1215a2a8f08fd192 (commit)
via 9c4d0cadf4adc802cc41a2610dc2c30b25aad728 (commit)
via a74802e8424747421511511f78a344de22105c1d (commit)
via fbe90b8b726e59ec1cddd5338850c7f744340759 (commit)
via bb4cc9928f968cd9e72503116a83c1fbe8f07361 (commit)
via fa862e7d1208c8cd0526b9512b9a1979366252ba (commit)
via 9cabc799f2bf9a3579dae7f1f5d5467c8bb1aa40 (commit)
via 75ff883128aa4a2e4bd003342a2d84c216418e00 (commit)
via 3e173f46ee0d807f752ffb88d908c7ca601d0c3a (commit)
via 548aac884cccb2c18420f3a14d1736a904c096c6 (commit)
via e85922d37b1f4b43db76b41286268af4d8649288 (commit)
via c0a44c70c4a2e0dce3792bbb7f9ca15938128633 (commit)
via 5900df9c41a9ab0837cd25667cf6a371bd7e850f (commit)
via f88766fd3085974acaa0c3e7930f8295374a8467 (commit)
via dbfaad4a2ce1f682267373b323a293e365bc4cc0 (commit)
via fbc79ee7d2f1a79f6388939beccaaf50dde0865e (commit)
via e5d53f4bb27024759f8712af254998c9dfa48f7a (commit)
via 11b5709d5a2e18ed0f83c291ec684a46d94609ff (commit)
via 055622f347c9e5beb43a5145c82a4f4a54e89d4b (commit)
via a9d6dd7cab00acd5379309d94ecb8b088ca3d295 (commit)
via a756c801315d320a855f312da4cad370e52371e3 (commit)
via e202628c6950839aaa6d318ea7a08b4f596499c0 (commit)
via 776afd136b2675b928605368199af7dc130607d1 (commit)
via 7b92c815b458b3fb4011fede59b787391bec7058 (commit)
via a7f978695cb84f9be3dd4ca4ff819c85d4477746 (commit)
via 4724623b5fd7748dac9fd291e6661ab94780fbb6 (commit)
via 36601beb98e8f746156ec430cc54ee9f03db2fe7 (commit)
via c257c2e0301a0605bd038e52e4df72734ccd92dc (commit)
via 62c19c0fdfd14b00dc515852731211f951c75201 (commit)
via 39b898c33b05f9936f9c822dfd0f90bfa2089369 (commit)
via 2d0b7b1cbf42fd7a92e09b8b3d37752829ce65b3 (commit)
via f6bd9f9c413e93070c2eddc6d84d6e77dd789454 (commit)
via 045edb53054c1e4acd985430f6b21b605348e959 (commit)
via ed3ab3da850807c3df2a6fc991702086bc65c008 (commit)
via c983a484207a8af30fbe90dcf2b2b4078ae8ff09 (commit)
via 1c1308a191d8e151abb28acbb22db033764acb67 (commit)
via 107995cf3a709e71baab30a82eafa6d7850750fc (commit)
via abb524e9809b09307c95c82a09d0d57bea652401 (commit)
via 6ebbb5d27d5a52d5f5e5caf89d3514a5135f07c7 (commit)
via 24347dec2bc47e3112a2b8103a8570ffc4a8ade5 (commit)
via b98523c1260637cb33436964dc18e9763622a242 (commit)
via 3b53272a6f77fa03e34f298ec06ec31de7cd32e1 (commit)
via c9f943875b76aba6204609dcd70eac4a5fb60b4e (commit)
via 3f6b00419fb5d447a4e2e84be58397632a716abe (commit)
via afa40133222b39078a914b0bf5780be013b8b992 (commit)
via b6c08e3ac581afd21e033808bdcf7c96f83f0ef4 (commit)
via c2d13346d9c16ba018a4f154c6862c3f9b208a82 (commit)
via c8fe7f2e869938bee1b3283c55c99f9487b6995b (commit)
via b5ae5d8fe31c721fe63f6cfa7ef880cf27ffbb06 (commit)
via bdb2771f51482995116460fb6594ba69b13e725e (commit)
via 8684a411d7718a71ad9fb616f56b26436c4f03e5 (commit)
via 555c57669226bca31e961412fde174f04db876ab (commit)
via 04f9d8ebe54788a4f4f3aa7be7d50347a26565c0 (commit)
via 90e30a79b4fdfe52a70fbc72f9f84a8dd1968506 (commit)
via bb52e879ee7b68c162b5ad36648b9f2c0fc18f19 (commit)
via d125d7da8b990b238993f59353e8a11832785706 (commit)
via 3b8892835a95f7b17f93f5f3ca36c14da1a82363 (commit)
via ff013364643f9bfa736b2d23fec39ac35872d6ad (commit)
via c5ba45e293726f7642f6e081de16bc3b2daff98b (commit)
via 280742f34c2471024aebdf2736edf5570f1f3da2 (commit)
via 79e99b7285b93325862f80746d3a4d1b5938ca4d (commit)
via 5a7953933a49a0ddd4ee1feaddc908cd2285522d (commit)
via 064dd51b725493270dec9f1cd2c2b7164135ee6b (commit)
via d2b5da0a8146343bd6f72f3a145fc90be7888d18 (commit)
via 56b495e4bd8aeffed8642bfb6bb4802ba39a6277 (commit)
via 429c88cfe0483e0c67f4625481995c2510485792 (commit)
via ffd2c98603eb6db553d4cf18d2fd7ac29a62080f (commit)
via 16b39f3c2422a324963b67e136b07619209f869e (commit)
via d0fc36b1522db78541bbcc560f5f05cbefd4c5bc (commit)
via 5ba4c6e1f349f890200322b2e2d2ff0e5b179944 (commit)
via fceb2a853bf88760d5d5dec2a3e6f169799415ea (commit)
via 30d7686cb6e2fa64866c983e0cfb7b8fabedc7a2 (commit)
via 04aa12f01ec871e625fdb8ee1a07c387ab0a8f2a (commit)
via 4d472746171f67ac492234ef46a3bba43f55d5de (commit)
via cf00216570a36d2e3e688b197deea781bfbe7d8d (commit)
via e4b2c2633ebb3859286e9a4c19e97e17bcac41b3 (commit)
via 54c6617096e184520c918d308ccb31eb422046ec (commit)
via 883ef5b0d7466d080916f98af4ea6a94258ca655 (commit)
via 82bb5bc1cd3385d1bd0362c10308afb04a0e6914 (commit)
via b995540a1bd00fab2ca883c965edc954080be84c (commit)
via 38edb7e80589f08524b2753a7f29f1b6570ef4d6 (commit)
via 6f771b28eea25c693fe93a0e2379af924464a562 (commit)
via 389ceb4af859b59d18db14ef25a2bd3c2dd3ddd7 (commit)
via d18c04987a47c89aa3038d2bb0e99aa40b2f4e51 (commit)
via dad96d1c2aac267f2a11b9bdb7c83ce33c1a3f34 (commit)
via dbdf5296c3e98beb234ea1a161b004bed5e17a8a (commit)
via 8f5187d167219ac263ef940eb33923ef8a86e87b (commit)
via 6b3f5f252791b1c4b92589deaa3810e5e1eb867f (commit)
via a72ef46cca91d1718b2830e9d827040e6742d643 (commit)
via a11e3b2165821e9ca039196b12d70025845a5d22 (commit)
via b0d241f85f89f1d352f9b7a521b24d80107ffc20 (commit)
via fcbe40140a5e21ecf3ff078206afe1d68e945462 (commit)
via 07dd87ca1a14058c926497ec1c6ac61ffd3a41f2 (commit)
via 4085d32d3df744aaa7fe333032f978edbfc293c3 (commit)
via 462e2807bed92a21e8e2c070e49bab118323de6b (commit)
via 89f1ae8c2f2bb8829ae249414e9d8464b74b54be (commit)
via a9d3e3d62464b76c467c5cd8c0c229a75b5df5fc (commit)
via fb9060e5dccc2bd43ede4da3e0fe0733aa9cc6fa (commit)
via 0169ac7327f43e82979102b0fbd87bf004dece16 (commit)
via 464cada643779ceff4b3886aa5ccfec6605e2e92 (commit)
via 73b4d32109e8103c50f73dcd0c171930c3d9b322 (commit)
via cb1ca47492ba2c493f88a45725f91d1b53251023 (commit)
via 9d56a4097505b05e6a8e69ebb967a30416e39b41 (commit)
via 324be1acbdb7d432f4204d9cd6ed28e71a4fe7cf (commit)
via 6ca1f0da6b1e7dc488e5ff57b9ef041f4322ab67 (commit)
via 766508d03d1304dc4d66b814a7a21d160b3404c2 (commit)
via a4789bb27d209a54652bc2d7c6bf0992613c18df (commit)
via bc4fc1865977c94d4ad6eddf0cf96a7881a71536 (commit)
via f43af5ae2a1a904d1fa091d227f6d5cb1c580fa3 (commit)
via 5339fd0dc7b0a00f2e80754e7a1aed146440cff9 (commit)
via ccc2fb769ced7cef416b55d9074591022b8a673b (commit)
via 69360a12e78e7567f88dcd3079c9a56be534ea7d (commit)
via 38fdfa7720fd8e4c07aeb6aad4b7ac492d364b88 (commit)
via 71898b1a575fdefa01c657290b99ce7ba4693054 (commit)
via 920106980b5a66212397f5f699f2aac9e6d69cbc (commit)
via c430386cb0a163ffabc477d864635b4a21f2ef59 (commit)
via 900180e27ecac721a7ea7a5c3cff62230082a391 (commit)
via 6ec73d5de3e72a8d6adbb7e3bc353b68584b344f (commit)
via d6d42bcdfcc80b450b89937ffd5ba39b071b3f37 (commit)
via 0c0161c6e555e2c8b33b3291043727bc07324dc2 (commit)
via 6dc12fe4b4f751a8692bb2f577d178de458cbe31 (commit)
via c8f1422e69ad8cce8538c4f25a92ff225b4c2bb8 (commit)
via e07c5a1459d94b1c7c24debdbc05f23a5c79b4f0 (commit)
via ce39e53bff7967573672e7172ec9d18b8bac4bab (commit)
via f161f37c16c1ce2b84ca467b6f6302e0d7d7a462 (commit)
via 9e60ca625be2c1875e65e360fd2b06c9ee8c4a57 (commit)
via d1f64edacac52f7016b36381d93bec0275dde2cb (commit)
via f6d6f8d947ffe15b7aa0d1fe73adea1a1963f774 (commit)
via 7ac5487d208f8b4cb9c3e2955bd7d1f03b2ad942 (commit)
via d7dc63b8a899e4e4c1be30e6f24ad113e02c6582 (commit)
via bd0a50e9b37f2e8c0030e905ef1889729df3ad5c (commit)
via 51146871aa0303636eff16193b66f61c80b9b7f8 (commit)
via 2ec4a397f113081c96807b3138549bd4bf1e2edf (commit)
via 5b301ea5a3aa71a5f82f0924713a7360c5f32f35 (commit)
via d121f511463ae2c469ec5ddeac1eb1ad60ca4cd9 (commit)
via 18eda2228413b1bd5764ad23dd5ab45a2d9c1809 (commit)
via eed7f24a8ee8ac1647a2998e38ce4766e48adb72 (commit)
via 19ba70c7cc3da462c70e8c4f74b321b8daad0100 (commit)
via ecd7dbe0f231c04d19eba3cd02ff513a23a9003a (commit)
via bcf5e635365f60d79e5b7d28b9280482fbe1529a (commit)
via 7626330333e85f0f0e3d63530681b3708932e7a5 (commit)
via f472a687b70982f906bcc96900fdbc3ee8d36ff6 (commit)
via aa0318899f62fc3069572bb18bc6dd071bb8448d (commit)
via 5758984e174f25954af41dcf07979e2dfca12763 (commit)
via 11fc8fbb59146320ba5ce8a86d08f937878250b3 (commit)
via 5f4eb187db9895ceb2297711b6eef6fdcc520625 (commit)
via 0255bfb9925710dc04d1bf88ad138c33ed7e7530 (commit)
via 4fea1ab53d06b5deaa87def247818f67839a9c9e (commit)
via 719a5941f529e8139cc2cf970d8903adf0741043 (commit)
via 6ab057e9726a30b87909a09026c797e99cd935d7 (commit)
via 0e8c2c9e572204db1b0586d6da94891168fd3a48 (commit)
via ac91cd885b563c71193cca2a33d6d11aa4a25927 (commit)
via 73506bcbd64a043f7e66c193a15b2d09a0a47bf0 (commit)
via aacfc3fee9cbf5a2b7945c165cace1af81877f5f (commit)
via 65bbecec757696dbde3faa5b019a574feb002ab4 (commit)
via 2ea7c1a1fbb55f0f2ba0e42fca3b4e237c5edd16 (commit)
via 70cccd62f3b6f433cb8790f3bc0a2455b1d11b6f (commit)
via e98da500d7b02e11347431a74f2efce5a7d622aa (commit)
via 5b6f56a3edae024549cf37438ef2000347befdd6 (commit)
via 7713d4f0d29ad4b19bcb67e2702a6c153cc115b5 (commit)
via ea1476c0b43a024db7ccdfabd23904a8d1ddf7ab (commit)
via f8cecc0e0625c40be6a44f24b8bcd0420e19946b (commit)
via c540444d6220133977db22de4c0adc116afd24a6 (commit)
via a915ddb9782f93f69b922cf04110725b46f97d31 (commit)
via fe58afc5c628dec30829fea01a9083d401c1e8de (commit)
via 5b2b5a2c16102329bdbc7d1421758dcd206fa45f (commit)
via 66d14dd2dff194e69a12db0c89176c399ac5ce0b (commit)
via 30f55bf6510a4463d511a90c0b7deaeb046f80f2 (commit)
via 61f4da5053c6a79fbc162fb16f195cdf8f94df64 (commit)
via f55b90a5fa11ee55e33e49f25f9f5e4858965e88 (commit)
via cb086eeafa84b7cbc30ef2da3e3f8bae3e58a473 (commit)
via 694ee39256cd2ebad4b1f5b657fd2a2556ad10a5 (commit)
via 863e56e656f11f8bd37ed75564d01640076d0215 (commit)
via 80df2d4fcda75c7b9e8ca0e8d2e4824c13ff2867 (commit)
via 4851408343c11ba763b1c7e4318ef68f1e3393fc (commit)
via 31ae358bc11d43cde0206b534548d142a42f3fcc (commit)
via a4abbe54f677835af7dd7ccc4a9aee1225da06f5 (commit)
via c9b1b85ad2266b1f1971193db1c0ee7f093b7697 (commit)
via af00b01970fa7d8da6d40d8621f4a1a58dcdf297 (commit)
via 966c129cc3c538841421f1e554167d33ef9bdf25 (commit)
via 45c762f1a150d2ba1f21ca169642ff6eccd0eb3a (commit)
via 2a349b194cf4f946cbd60d2295a278a6e0f3c0fa (commit)
via a49f5b952b7d8d93b0ae275c3f3f8fe95ae34939 (commit)
via 673496a27df7e7fbf98ccbdb26171b7c4618bfc2 (commit)
via fddc9d07afa5e4c26f701672c9503f353b27fe69 (commit)
via fcc5fed77df391264c67db28bba28dc48aa5021b (commit)
via 543e6f2de2a1d9193b2e5c24f0f536594e9050bf (commit)
via 003ca8597c8d0eb558b1819dbee203fda346ba77 (commit)
via 83e953bee2f7762739880a4ef01ca3246230af31 (commit)
via 38742cb1561683410ab0f61388dbccd57e9f0f3c (commit)
via 955d8f31227b9429548de4da6fbe40350509cf04 (commit)
via eadab44ef359700ba6ca50a2dc93b7bef2feae39 (commit)
via 451fd576615b47baa616d49ccaccedfae5595a76 (commit)
via a265a99016e8fe7943e1b6e8d4f2a69c0b5fb391 (commit)
via 6129c3537cc81af4561269d5652321ac2250ab93 (commit)
via 097a376ed65be53dbfe4cd1f2f46df288fb9d6ae (commit)
via a0f1ca23cd394e0d94845ecc08c35e56bf313b01 (commit)
via 466339143a9161f821fa63e5545ee9cc72d8e1a6 (commit)
via fe5b8226c1d3237ed8feb924ad789cbdbee0287e (commit)
via e494212d02fa6efcb437f966708992de8b02cb74 (commit)
via 2217a6591fef2fbd4026d6c2c2384a026044fc0c (commit)
via 7de090b9c37f4f68a4242127af8bb4da9eca9eb8 (commit)
via e7b760a474b50989ba32c3766580508a3283148d (commit)
via 7f74352cccef3e5b8ed292930746e8b8c723f7af (commit)
via ecb4b75c7b226482e5f2592d14f8534f6f9e03a4 (commit)
via 8d191a6771288e0cf71d48e93091f355719a3089 (commit)
via e50e1bf6c169c72fb084deb42e739b7c9ebfb94e (commit)
via be83d3bfd516ff84824a44ff8d2e0ad68bc0c613 (commit)
via d3781127f6d22976c761003fba52df8c9c30436d (commit)
via b0d21f92b8fa563a1cba116a16109dc4c7164dc1 (commit)
via 48aa1e839cf475521de07bdc0d8c870bb29f335e (commit)
via 5af17f43de48dd58fe87b85d974a554ac0136b83 (commit)
via 427039d6ab89abc66c53479f27e5c15d677b1431 (commit)
via c8739afa54bafeffac2cad157020242b8acb3b28 (commit)
via 82708a4ff9185158d779cc194e94fc3aa1c3bd09 (commit)
via 326b046810b3d77997fe95f2eab21b591a709ac7 (commit)
via d3877a19035f09caa0b457cbe01fe28edacdeddd (commit)
via e40cec7b4d5a0fc473bb5d4abfb6a93a712e0cb4 (commit)
via ebd9ca8fd9d7a39e8f566644e62b68234e566155 (commit)
via 61dd18377938c6f2e5d026d432062db6773abf65 (commit)
via ee4edb317a513bcc1230a4edb8edcd91b8a796ca (commit)
via 77ceb4384e75a1e613b1fbf8766745dbd95221a9 (commit)
via aa39c499c1f3b58685987252e7e5dc1e41a4b915 (commit)
via 01a765b995b55195fc2fdffe3017e7280018dae5 (commit)
via 17f7af0d8a42a0a67a2aade5bc269533efeb840a (commit)
via d7aff6bf2f4d45365305117fc19648b41e01c8ca (commit)
via b1a417fa43cc2d467fd67d79ee7749fddaf92011 (commit)
via 001717c6baf2ed1929f9a585408ae66f26eed014 (commit)
via bf67156c7798685263abd1fc8c60669a78bcec2d (commit)
via 6292462f7e27fe5970d7f46b0cb3b360d21d830c (commit)
via cfb3f053d87cd3d406c84acd548290b3ca77492d (commit)
via 602204a5a15d7e602381a885f366acf9ba10b0d4 (commit)
via 75e6307ae2766d7eb46875d95fb13b5ef2b11f10 (commit)
via c91c89ac62e179ac0a1568cd835f55990de3fd19 (commit)
via c356ad2e53fe54c417b00ab7b6d806a31fa3ac5f (commit)
via a5fec871b80f8ef0bddc2e708235581bde00533f (commit)
via 95b8b13fa13f48b524aa46691e96bc21062559d6 (commit)
via a2bc5862342ad6063a80809c284fe395cf3e6531 (commit)
via d20389d04053862712eae4e1005ce38faf813f20 (commit)
via 0a3535e441c8b183c0d20f1cc8236ad191e6a4ca (commit)
via 47ed2b145a1194d54b7d267d5addf74032f8acc3 (commit)
via bbd3fd0f48b0f20174ad7fb75ff1ba75c4bd2731 (commit)
via c70cfef4856f2df56349d9f4798f63e32633f6d2 (commit)
via 1293666c78916a823abb78d2cc4069667ed274d1 (commit)
via bb45b29c9fc7c350b43389d6eec8dbc8613d1d3e (commit)
via b31b7eff36397f30f00d7c31805bf7634395dfa8 (commit)
via 811f0ab9da1b1e5029c9ec9d6d2fc8e5ddc1e03e (commit)
via 082faedd5757d9a4e6c63c52fca1aa266cb0b183 (commit)
via 8c5e01a82168993347e060a7079ec5014106e57e (commit)
via c5a39e131286add03835505d9cf7d82cd9a23efc (commit)
via 7be2f0a4db2e3e20ee28429fddea1dea11592eb7 (commit)
via 578f4a5e28f06ff84d75502d5484d995904947dd (commit)
via 9ba95c664865ceb3fc551dbb0d8cc120c8aa2ba2 (commit)
via f9593f5d5a28d42d01251f009606aab5cc1e6997 (commit)
via a9ade7fc8f8db95124c33bf475a59fd522914e49 (commit)
via 4c8164b131661552f3bc8e74806d4cbbe1cda1d1 (commit)
via f15c13fb80fd6512f31df035555392494c6dfbd2 (commit)
via da724029ca02951b51d63333394bac0d62dbb0a2 (commit)
via eb872b35100ce2b602064017cd2de8ab517ffb76 (commit)
via d20fafd80c35a7c5ad85f097556e79b9d73dbf8a (commit)
via 11401abc34828a747f7dd716a1645b143efaf2f8 (commit)
via 988a731a52ff69d86cd5669a98994662d705fb85 (commit)
via 3f6415fc870ed25719f6c71f27ab4f2154358f9c (commit)
via 9b1d96cccb6572d7ebdeed0ac9c59fa3059a3db3 (commit)
via bc105b59161101bb6cf757e5f863bb7a3c97e6cc (commit)
via 05a6a866c9ab6eb451ba8d106c78726b64c5c5c2 (commit)
via 605afe96c79c356df9488f2e75c05efc9ceddedc (commit)
via 4d266d80beb7e0d8b1f45a4a4bc7cc39a1657486 (commit)
via 1c0df5ba07b95d2e2b1c678675be16a2a1059593 (commit)
via 28ba8bd71ae4d100cb250fd8d99d80a17a6323a2 (commit)
via 0aef7c479e7d127d0c219d6b3a5a4d0f3f701161 (commit)
via 185cb45b7ab1a164a7bb0a989c582a74ce50f2f3 (commit)
via 047380ffbbf6e876282cc269ef16d8d6b168d6d2 (commit)
via 0db8f9c91a54b2454a9210741da887280981a26a (commit)
via eaff1f5816bd2ed57a011f658e0b2dd762800079 (commit)
via 2a29f6b8725bf106dce2f45f2705bac555022c5d (commit)
via 9ff58bb76e632a8f7a28a4f86f94dd1d77690796 (commit)
via a9eadb451bea1a4813a7b3942a7ca2eea97618a5 (commit)
via ddaaee4b050468048409170f1bb56530f33567d6 (commit)
via 0a78488462cb3d7998f8af45fc830c18648dd45e (commit)
via 1aa4924e5a9560ceca73c0e777156309e125f5bb (commit)
via 540d2a66787b2ff57e20a2c99a84d2875dfbcc54 (commit)
via 1cde47ac78e581f185afe2b7e8523c34bcec9a38 (commit)
via 6d675cbff066b5ee3f1c3bde7aab7dfd0ad1482c (commit)
via 72b239285aa03f9afa5685a4665451d89a941143 (commit)
via 1b43a3adbb58860353dba59f48b23e2efc936041 (commit)
via ad2bcdd85d6e40cbfd9eb99acf817b626083962c (commit)
via 9e3c7d57f3144b6992d8967af5a4583aa3d55c62 (commit)
via 809c35dc7dfc5e9fd0a498928f6aaff89049757c (commit)
via 9e363eaa09c4ab1945ba91a117cca7ea892ea78a (commit)
via 30219fe1326f00e468fd05bcf80e3ac47b5f4e3c (commit)
via 6e12d95c6a1fac2d4a182e58072934d2ab4b237e (commit)
via 78785509428db0da450a2be7e89a342d1200cf0a (commit)
via 8e98da42d48e5e847cd12debab6738c585db4740 (commit)
via f10604eb75f876d77af25b30b0709d5c554a6b26 (commit)
via d9640f6f30078a6b86d7b096ad0563bf45ffeb0f (commit)
via 5c4967145b426d190fb6086307ade06e8ad093e1 (commit)
via 4a20cc2eef19a7ada8e2c594724ca44e40be5cf8 (commit)
via d72e05e550f443209afbfd0b173d0a1e96dff749 (commit)
via e9647bb3c689941916508a663b72df63be7d3963 (commit)
via b86f7c330eba5b252adbea0594c2225fbc31e2e2 (commit)
via ecd4db4c298f4f1aae038bc0b42837478fe9910b (commit)
via 263d38ef6a1fe8bce1a229acc1cc14144d180c0a (commit)
via 03a1f23dbeec538ee042f3f28203ea8cb3b29bfd (commit)
via 67167017ac51545493ed1adb6c7c33d3c19501c5 (commit)
via 716ae36b91bc71f72098b8c831c5e86c845a99bc (commit)
via e6532b8bb0bca5d9b2a64c721f99063371055add (commit)
via 56596cd21e95779796a1e69dcd759ce77c6f3ce0 (commit)
via 64b4c44c2d67130c304cf720bff62723d2fc2293 (commit)
via c41cda65e06b6776c7111e65b04c72d5bc84c54f (commit)
via 17e78fa1bb1227340aa9815e91ed5c50d174425d (commit)
via 2da5a3de561484f902aa7fb8475551217b9535b9 (commit)
via db3d9fdfd8da2d9ced567ca43d9be8d9885e0cff (commit)
via 6bb658482335f1904c3b210ef4380719170677eb (commit)
via ac686701c48b92a98507e1533ccd4e3c851bf3ec (commit)
via 2858b2098a10a8cc2d34bf87463ace0629d3670e (commit)
via f328341c1cfed8e5ca0479619e9d12d3b4907c86 (commit)
via 373435e91924553439f4881a1fbf71aacd5fb4a1 (commit)
via 5a3853394c09ef698795c3b7fe98661e752e0426 (commit)
via 308d0c39eade6985c4f61feb215255cf2ec92abf (commit)
via 0ad08d16ff5100ec01865bf2814481960c79358e (commit)
via 63f7b1ab7c81110633ed2e381e5fdfe05e143d72 (commit)
via 61203697e8fc714835ef504a7e3eda925dfcda42 (commit)
via 5da80cb570399e277dd02d6d45bc4efb3f949d61 (commit)
via 5d11f8513af16aca9eb298db3226791f1f4e584a (commit)
via 871e4c22effffd79ff1d0a44d06a102b27b24ac7 (commit)
via cb4049864f51f5dae26d7e985568e06e3ee1f9bc (commit)
via 66a1a1ac3cec4b9b5f453370c85546ddafb1a9a4 (commit)
via 18828275414b6a7fa529282e2e53d3ffb2b55082 (commit)
via e8580780dacffa82089d030c5b9950eb31c693f6 (commit)
via 959f4cebed4f1182f4c9f4c3058364e83d553344 (commit)
via ef979231a2925c3cb6c079b181c6747005d72baa (commit)
via 5eaa333fe39ddc02af78ffceba8173497e1352c3 (commit)
via 82f947e1bc6978f7bde731955b96dc5c68840a6f (commit)
via 4f23a482e885f3fe3dd104fa43638352f9ff3e7b (commit)
via 88aa930fd6189c121c784b2175aab519d72ed5ab (commit)
via 46232cfc39be398a04cfbc4e6fe2e75b41d8eb7c (commit)
via a5fa9a0f01113a92f6136f6dfc1716f877cbf07e (commit)
via 90f98423f675dec3fc8335f17bbf761662536cce (commit)
via cb2af9730fdf9f0a9488c1197f29dec90b31c19d (commit)
via bf8c9216523617f5834b4acf9f876587995f5078 (commit)
via 013e14c7d9c84f00510aa8f7a99955aa47c018ad (commit)
via 82caa5734136ca29981a5107c9b0e4f644a573cf (commit)
via 6b45467a71b534657a435dbb455f0ec3da99d16a (commit)
via 170556d806125d1556fa570070b9f876c97df30c (commit)
via 42b7d6a5c8f04659a4463d932b95e5305d6de3f7 (commit)
via b82c78512d63424b805e1ab15e6b781e4d923dbd (commit)
via f5ea0617661d39ecd576f3d997cf20009ac05048 (commit)
via 0ad8fea24b58d05e8c57337c7b77f1b76be12015 (commit)
via c6aec4cdb46dd8825248437bc9c45d6a54b81121 (commit)
via 287e17100b60fbfceacc7337cd6ac9bab36185be (commit)
via deadd3b30b20cad6964bebf38dba417c42b78289 (commit)
via 3d457417da0ef05512383a9ca4e2448882339344 (commit)
via 94ca32cb24762fc41c23631d7183c35f643545e7 (commit)
via 58b7b149b8b3afbdbd8c960fc716f4e85f1803ee (commit)
via 3071211d2c537150a691120b0a5ce2b18d010239 (commit)
via 33ff861b4cd62e8d2e6063452cc14e980d35bbaf (commit)
via b341b3ca37749a29e1f1f286d103298d3c3fa568 (commit)
via 4a8fa93781cce0dbd33f55e604d87db511fb5903 (commit)
via 78e535f5a0e65658f7de0c3b777f9e0b296ad53e (commit)
via 53cde03994472c09094721f94307f46599c0d1d5 (commit)
via a092343db9338c7e6acfb238d8c432291ad31e58 (commit)
via f364574080cd8f9d1a2acb27ddb3318157393ebc (commit)
via efd87c8cddb380d78a0cef4ee7fcc91559f7352b (commit)
via 3007ba9ef2cffdf4903b43a14ccf52a0c1d3ec65 (commit)
via afc721fe5f2964c9d03bdf0738852556c69ad41b (commit)
via 6cba1c1241e8c23779d0dc29374ce8420fadc2c5 (commit)
via 76f823d42af55ce3f30a0d741fc9297c211d8b38 (commit)
via ab457a83d9ab58e722b84a7f99bd83dd49d75483 (commit)
via b9aed5e8c47dd0e44981e6a7c40b5b3c8e403e53 (commit)
via f5a196c2aaa02ab50f8bb50f9e23cdff7e945559 (commit)
via 158f5113fe1effcf74cd0e09b5b072508e8462ed (commit)
via 3d7ea0c377844f75053cf095f1e982a5bb6c32d7 (commit)
via d1c5effaacd00d4ae5c97a48f820c5e281964cdb (commit)
via caf03348be996e4fe58efa70927b119b4fdf3d84 (commit)
via f45a95082d8db85dff8d7b0bf0e0817e7175103e (commit)
via 2c40ed8bbbab2e095be163a6f144eb45be465f80 (commit)
via e3f86da049a7af0ddb0369db2e9db3fa0903d3c5 (commit)
via 0e7c11723dcde11c0d9d789d072801d383e9ec83 (commit)
via 9bbda62a5e88e9f1e4c8aeb7924308e37ef65a7c (commit)
via 0d1406f71c9cd9439017b56957fa9d37ba7729cb (commit)
via 32d9c527a98f1e417d21799d982361892ca87c6c (commit)
via 47bca52baed667a6788450e1a0912c7a06363c8c (commit)
via 00de2fb5e636d527a7d1a7bf97a6e4552d84b1f0 (commit)
via 5fbff1bd2b79b417955e53dba3e3788ff32ebd6a (commit)
via df25eedae7f4e65ed0a8ecd2f95722061335d498 (commit)
via e16deb4665c50c5232cec96ae2bd69348610c926 (commit)
via ddbf6fe0f244d0c808408e94b502bc462c5d2f41 (commit)
via ae0979752fd9062a5b184ec855c2f9eab1363aba (commit)
via 4e8a429913d5ecf888ec95d5d337f4ebc5c1976a (commit)
via 5883a489bd72fd9a7faa485d770cc74b99457e7f (commit)
via ee5cc69d9e5320e3d9c420c22b54d0ebae255268 (commit)
via 85c2888f2fe2c9985ce89fe22fa518b91976398b (commit)
via d5a8a75cf9e5cd88b5e7471eb7be8255cd2da28d (commit)
via 88b907df1f580e6241f2af12870df5c769c539fa (commit)
via 6afe3a0422647e7e6973857bed9c657366f10674 (commit)
via 848e9d9759c84f822a6d866477016b5a95415dfc (commit)
via ac03fb060596dbebbb012d091292e4c9690f1c88 (commit)
from 78c0ce22b39230347bc652f7903aac57d2b4c577 (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 3d8ac71299d439906a11820e4ef914eb18b9d1d3
Merge: 78c0ce22b39230347bc652f7903aac57d2b4c577 6b1caed1a4161932e68ef52f067d07d822d85c94
Author: Jeremy C. Reed <jreed at ISC.org>
Date: Wed Mar 14 20:57:05 2012 -0500
[trac1644]Merge branch 'master' into trac1644
-----------------------------------------------------------------------
Summary of changes:
.gitignore | 28 +
ChangeLog | 201 ++++-
compatcheck/.gitignore | 1 +
configure.ac | 138 +--
doc/.gitignore | 1 +
doc/guide/bind10-guide.html | 136 ++-
doc/guide/bind10-guide.txt | 58 +-
doc/guide/bind10-guide.xml | 97 ++-
doc/guide/bind10-messages.html | 150 ++-
doc/guide/bind10-messages.xml | 271 ++++-
ext/asio/asio/detail/impl/kqueue_reactor.ipp | 2 +
ext/asio/asio/detail/impl/socket_ops.ipp | 36 +-
src/bin/auth/.gitignore | 7 +
src/bin/auth/auth.spec.pre.in | 160 +++-
src/bin/auth/auth_log.h | 2 +
src/bin/auth/auth_messages.mes | 49 +-
src/bin/auth/auth_srv.cc | 233 +++--
src/bin/auth/auth_srv.h | 23 +-
src/bin/auth/b10-auth.8 | 18 +-
src/bin/auth/b10-auth.xml | 19 +-
src/bin/auth/benchmarks/.gitignore | 1 +
src/bin/auth/benchmarks/query_bench.cc | 24 +-
src/bin/auth/command.cc | 47 +-
src/bin/auth/common.cc | 2 +
src/bin/auth/common.h | 5 +
src/bin/auth/main.cc | 4 +-
src/bin/auth/query.cc | 460 ++++++---
src/bin/auth/query.h | 155 ++-
src/bin/auth/statistics.cc | 33 +-
src/bin/auth/statistics.h | 25 +
src/bin/auth/tests/.gitignore | 1 +
src/bin/auth/tests/auth_srv_unittest.cc | 477 +++++++-
src/bin/auth/tests/command_unittest.cc | 256 +++--
src/bin/auth/tests/config_unittest.cc | 7 +-
src/bin/auth/tests/query_unittest.cc | 1168 +++++++++++++++-----
src/bin/auth/tests/statistics_unittest.cc | 71 ++
src/bin/bind10/.gitignore | 3 +
src/bin/bind10/bind10.8 | 52 +-
src/bin/bind10/bind10.xml | 75 +-
src/bin/bind10/bind10_messages.mes | 2 +-
src/bin/bind10/bind10_src.py.in | 31 +-
src/bin/bind10/tests/.gitignore | 1 +
src/bin/bind10/tests/bind10_test.py.in | 23 +-
src/bin/bindctl/.gitignore | 3 +
src/bin/bindctl/tests/.gitignore | 1 +
src/bin/cfgmgr/.gitignore | 2 +
src/bin/cfgmgr/tests/.gitignore | 1 +
src/bin/cmdctl/.gitignore | 5 +
src/bin/cmdctl/b10-cmdctl.8 | 33 +-
src/bin/cmdctl/b10-cmdctl.xml | 50 +-
src/bin/cmdctl/cmdctl.py.in | 15 +-
src/bin/cmdctl/cmdctl.spec.pre.in | 8 +-
src/bin/cmdctl/tests/.gitignore | 1 +
src/bin/cmdctl/tests/cmdctl_test.py | 34 +
src/bin/ddns/.gitignore | 2 +
src/bin/ddns/b10-ddns.8 | 12 +-
src/bin/ddns/b10-ddns.xml | 13 +-
src/bin/ddns/ddns.py.in | 5 +-
src/bin/ddns/ddns.spec | 8 +-
src/bin/ddns/tests/ddns_test.py | 6 +
src/bin/dhcp4/.gitignore | 3 +
src/bin/dhcp4/Makefile.am | 2 +-
src/bin/dhcp4/tests/.gitignore | 1 +
src/bin/dhcp6/Makefile.am | 2 +-
src/bin/host/.gitignore | 1 +
src/bin/host/host.cc | 5 +-
src/bin/loadzone/.gitignore | 3 +
src/bin/loadzone/tests/correct/.gitignore | 1 +
src/bin/loadzone/tests/error/.gitignore | 1 +
src/bin/msgq/.gitignore | 3 +
src/bin/msgq/tests/.gitignore | 1 +
src/bin/resolver/.gitignore | 7 +
src/bin/resolver/Makefile.am | 1 +
src/bin/resolver/b10-resolver.8 | 10 +-
src/bin/resolver/b10-resolver.xml | 11 +-
src/bin/resolver/common.cc | 17 +
src/bin/resolver/common.h | 23 +
src/bin/resolver/main.cc | 70 +-
src/bin/resolver/resolver.cc | 7 +-
src/bin/resolver/resolver.spec.pre.in | 8 +-
src/bin/resolver/resolver_messages.mes | 4 +
src/bin/resolver/tests/.gitignore | 1 +
src/bin/resolver/tests/resolver_config_unittest.cc | 5 +-
src/bin/sockcreator/.gitignore | 1 +
src/bin/sockcreator/Makefile.am | 3 +-
src/bin/sockcreator/main.cc | 8 +-
src/bin/sockcreator/sockcreator.cc | 356 ++++--
src/bin/sockcreator/sockcreator.h | 190 ++--
src/bin/sockcreator/tests/.gitignore | 1 +
src/bin/sockcreator/tests/Makefile.am | 5 +-
src/bin/sockcreator/tests/sockcreator_tests.cc | 514 ++++++----
src/bin/stats/.gitignore | 4 +
src/bin/stats/b10-stats-httpd.8 | 23 +-
src/bin/stats/b10-stats-httpd.xml | 17 +-
src/bin/stats/b10-stats.8 | 38 +-
src/bin/stats/b10-stats.xml | 38 +-
src/bin/stats/stats-httpd.spec | 8 +-
src/bin/stats/stats.py.in | 11 +-
src/bin/stats/stats.spec | 8 +-
src/bin/stats/stats_httpd.py.in | 1 +
src/bin/stats/tests/b10-stats-httpd_test.py | 11 +-
src/bin/stats/tests/b10-stats_test.py | 14 +-
src/bin/stats/tests/test_utils.py | 14 +-
src/bin/tests/.gitignore | 1 +
src/bin/usermgr/.gitignore | 3 +
src/bin/xfrin/.gitignore | 3 +
src/bin/xfrin/tests/.gitignore | 1 +
src/bin/xfrin/tests/xfrin_test.py | 86 ++-
src/bin/xfrin/xfrin.py.in | 93 ++-
src/bin/xfrin/xfrin.spec | 8 +-
src/bin/xfrin/xfrin_messages.mes | 287 +++---
src/bin/xfrout/.gitignore | 5 +
src/bin/xfrout/b10-xfrout.8 | 15 +-
src/bin/xfrout/b10-xfrout.xml | 55 +-
src/bin/xfrout/tests/.gitignore | 2 +
src/bin/xfrout/tests/xfrout_test.py.in | 75 +-
src/bin/xfrout/xfrout.py.in | 30 +-
src/bin/xfrout/xfrout.spec.pre.in | 50 +-
src/bin/zonemgr/.gitignore | 5 +
src/bin/zonemgr/b10-zonemgr.8 | 10 +-
src/bin/zonemgr/b10-zonemgr.xml | 11 +-
src/bin/zonemgr/tests/.gitignore | 1 +
src/bin/zonemgr/tests/zonemgr_test.py | 10 +-
src/bin/zonemgr/zonemgr.py.in | 7 +-
src/bin/zonemgr/zonemgr.spec.pre.in | 8 +-
src/cppcheck-suppress.lst | 21 +-
src/lib/acl/tests/.gitignore | 1 +
src/lib/asiodns/.gitignore | 2 +
src/lib/asiodns/Makefile.am | 1 +
src/lib/asiodns/dns_server.h | 16 -
src/lib/asiodns/io_fetch.cc | 7 +-
src/lib/asiodns/sync_udp_server.cc | 215 ++++
src/lib/asiodns/sync_udp_server.h | 161 +++
src/lib/asiodns/tcp_server.h | 3 -
src/lib/asiodns/tests/.gitignore | 1 +
src/lib/asiodns/tests/dns_server_unittest.cc | 119 ++-
src/lib/asiodns/tests/io_fetch_unittest.cc | 27 +-
src/lib/asiodns/udp_server.cc | 5 -
src/lib/asiodns/udp_server.h | 10 -
src/lib/asiolink/tests/.gitignore | 1 +
src/lib/bench/benchmark_util.cc | 9 +-
src/lib/bench/example/.gitignore | 1 +
src/lib/bench/tests/.gitignore | 1 +
src/lib/bench/tests/benchmark_unittest.cc | 10 +-
src/lib/cache/.gitignore | 2 +
src/lib/cache/local_zone_data.cc | 2 +-
src/lib/cache/local_zone_data.h | 2 +-
src/lib/cache/rrset_cache.cc | 2 +-
src/lib/cache/rrset_cache.h | 2 +-
src/lib/cache/rrset_copy.cc | 2 +-
src/lib/cache/rrset_copy.h | 2 +-
src/lib/cache/rrset_entry.cc | 3 +-
src/lib/cache/rrset_entry.h | 3 +-
src/lib/cache/tests/.gitignore | 1 +
src/lib/cache/tests/Makefile.am | 5 -
src/lib/cc/.gitignore | 4 +
src/lib/cc/tests/.gitignore | 2 +
src/lib/config/.gitignore | 2 +
src/lib/config/ccsession.cc | 23 +
src/lib/config/ccsession.h | 9 +
src/lib/config/config_messages.mes | 22 +-
src/lib/config/module_spec.cc | 1 -
src/lib/config/tests/.gitignore | 2 +
src/lib/config/tests/ccsession_unittests.cc | 63 ++
src/lib/config/tests/fake_session.cc | 9 +-
src/lib/config/tests/fake_session.h | 9 +
src/lib/config/tests/testdata/.gitignore | 1 +
src/lib/cryptolink/tests/.gitignore | 1 +
src/lib/datasrc/.gitignore | 4 +
src/lib/datasrc/Makefile.am | 11 +-
src/lib/datasrc/database.cc | 72 +-
src/lib/datasrc/database.h | 77 +-
src/lib/datasrc/datasrc_config.h.pre.in | 2 +-
src/lib/datasrc/datasrc_messages.mes | 26 +-
src/lib/datasrc/memory_datasrc.cc | 410 +++-----
src/lib/datasrc/memory_datasrc.h | 24 +-
src/lib/datasrc/memory_datasrc_link.cc | 173 +++
src/lib/datasrc/rbnode_rrset.h | 209 ++++
src/lib/datasrc/sqlite3_accessor.cc | 74 --
src/lib/datasrc/sqlite3_accessor_link.cc | 105 ++
src/lib/datasrc/sqlite3_datasrc.cc | 8 +
src/lib/datasrc/static_datasrc.cc | 2 +
src/lib/datasrc/tests/.gitignore | 4 +
src/lib/datasrc/tests/Makefile.am | 61 +-
src/lib/datasrc/tests/database_unittest.cc | 74 +-
src/lib/datasrc/tests/datasrc_unittest.cc | 3 +-
src/lib/datasrc/tests/memory_datasrc_unittest.cc | 600 +++++++++--
src/lib/datasrc/tests/rbnode_rrset_unittest.cc | 258 +++++
src/lib/datasrc/tests/static_unittest.cc | 4 +-
src/lib/datasrc/tests/testdata/contexttest.zone | 35 +
.../{dns => datasrc}/tests/testdata/rrset_toWire1 | 0
src/lib/datasrc/tests/testdata/rrset_toWire2 | 26 +
.../datasrc/tests/zone_finder_context_unittest.cc | 322 ++++++
src/lib/datasrc/zone.h | 265 ++++--
src/lib/datasrc/zone_finder_context.cc | 102 ++
src/lib/dhcp/iface_mgr.cc | 4 +-
src/lib/dhcp/tests/.gitignore | 1 +
src/lib/dns/.gitignore | 6 +
src/lib/dns/Makefile.am | 6 +
src/lib/dns/benchmarks/.gitignore | 1 +
src/lib/dns/benchmarks/rdatarender_bench.cc | 3 +-
src/lib/dns/labelsequence.cc | 77 ++
src/lib/dns/labelsequence.h | 136 +++
src/lib/dns/masterload.cc | 33 +-
src/lib/dns/message.cc | 4 +
src/lib/dns/message.h | 16 +-
src/lib/dns/messagerenderer.cc | 40 +-
src/lib/dns/messagerenderer.h | 94 ++-
src/lib/dns/name.h | 6 +
src/lib/dns/nsec3hash.cc | 34 +-
src/lib/dns/nsec3hash.h | 90 ++
src/lib/dns/python/message_python.cc | 5 +-
src/lib/dns/python/messagerenderer_python.cc | 6 +-
src/lib/dns/python/rrset_python.cc | 4 +-
src/lib/dns/python/rrset_python.h | 4 +-
src/lib/dns/python/tests/.gitignore | 1 +
.../dns/rdata/generic/detail/nsec3param_common.cc | 130 +++
.../dns/rdata/generic/detail/nsec3param_common.h | 134 +++
src/lib/dns/rdata/generic/detail/nsec_bitmap.cc | 84 ++-
src/lib/dns/rdata/generic/detail/nsec_bitmap.h | 68 +-
src/lib/dns/rdata/generic/nsec3_50.cc | 261 ++---
src/lib/dns/rdata/generic/nsec3param_51.cc | 119 +--
src/lib/dns/rdata/generic/nsec_47.cc | 62 +-
src/lib/dns/rdatafields.cc | 6 +-
src/lib/dns/rrset.h | 145 +++-
src/lib/dns/tests/.gitignore | 1 +
src/lib/dns/tests/Makefile.am | 2 +
src/lib/dns/tests/edns_unittest.cc | 4 +-
src/lib/dns/tests/labelsequence_unittest.cc | 253 +++++
src/lib/dns/tests/masterload_unittest.cc | 98 ++
src/lib/dns/tests/message_unittest.cc | 15 +-
src/lib/dns/tests/messagerenderer_unittest.cc | 86 ++-
src/lib/dns/tests/name_unittest.cc | 13 +-
src/lib/dns/tests/nsec3hash_unittest.cc | 79 ++-
src/lib/dns/tests/question_unittest.cc | 6 +-
src/lib/dns/tests/rdata_afsdb_unittest.cc | 2 +-
src/lib/dns/tests/rdata_cname_unittest.cc | 4 +-
src/lib/dns/tests/rdata_dname_unittest.cc | 4 +-
src/lib/dns/tests/rdata_dnskey_unittest.cc | 4 +-
src/lib/dns/tests/rdata_ds_like_unittest.cc | 4 +-
src/lib/dns/tests/rdata_hinfo_unittest.cc | 5 +-
src/lib/dns/tests/rdata_in_a_unittest.cc | 2 +-
src/lib/dns/tests/rdata_in_aaaa_unittest.cc | 2 +-
src/lib/dns/tests/rdata_minfo_unittest.cc | 8 +-
src/lib/dns/tests/rdata_mx_unittest.cc | 6 +-
src/lib/dns/tests/rdata_naptr_unittest.cc | 5 +-
src/lib/dns/tests/rdata_ns_unittest.cc | 4 +-
src/lib/dns/tests/rdata_nsec3_unittest.cc | 122 +--
.../dns/tests/rdata_nsec3param_like_unittest.cc | 260 +++++
src/lib/dns/tests/rdata_nsec3param_unittest.cc | 46 +-
src/lib/dns/tests/rdata_nsec_unittest.cc | 33 +-
src/lib/dns/tests/rdata_nsecbitmap_unittest.cc | 253 ++++-
src/lib/dns/tests/rdata_ptr_unittest.cc | 4 +-
src/lib/dns/tests/rdata_rp_unittest.cc | 2 +-
src/lib/dns/tests/rdata_soa_unittest.cc | 4 +-
src/lib/dns/tests/rdata_srv_unittest.cc | 4 +-
src/lib/dns/tests/rdata_unittest.cc | 5 +-
src/lib/dns/tests/rdatafields_unittest.cc | 5 +-
src/lib/dns/tests/rrclass_unittest.cc | 4 +-
src/lib/dns/tests/rrset_unittest.cc | 6 +-
src/lib/dns/tests/rrttl_unittest.cc | 8 +-
src/lib/dns/tests/rrtype_unittest.cc | 4 +-
src/lib/dns/tests/testdata/.gitignore | 117 ++
src/lib/dns/tests/testdata/Makefile.am | 11 +
.../dns/tests/testdata/rdata_nsec3_fromWire16.spec | 8 +
.../dns/tests/testdata/rdata_nsec3_fromWire17.spec | 8 +
.../dns/tests/testdata/rdata_nsec3param_fromWire1 | 4 +-
.../testdata/rdata_nsec3param_fromWire11.spec | 8 +
.../testdata/rdata_nsec3param_fromWire13.spec | 9 +
.../tests/testdata/rdata_nsec3param_fromWire2.spec | 9 +
.../dns/tests/testdata/rdata_nsec_fromWire16.spec | 8 +
src/lib/dns/tests/tsig_unittest.cc | 3 +-
src/lib/dns/tests/tsigrecord_unittest.cc | 2 +-
src/lib/dns/tests/unittest_util.cc | 18 +
src/lib/dns/tests/unittest_util.h | 16 +
src/lib/exceptions/tests/.gitignore | 1 +
src/lib/log/compiler/.gitignore | 1 +
src/lib/log/compiler/message.cc | 18 +-
src/lib/log/tests/.gitignore | 9 +
src/lib/nsas/.gitignore | 2 +
src/lib/nsas/glue_hints.cc | 4 +-
src/lib/nsas/hash_table.h | 2 +-
src/lib/nsas/tests/.gitignore | 1 +
src/lib/nsas/tests/Makefile.am | 5 -
.../tests/nameserver_address_store_unittest.cc | 2 +-
src/lib/nsas/tests/nameserver_entry_unittest.cc | 4 +-
src/lib/nsas/zone_entry.cc | 2 +-
src/lib/python/.gitignore | 1 +
src/lib/python/isc/Makefile.am | 2 +-
src/lib/python/isc/bind10/component.py | 36 +-
src/lib/python/isc/bind10/tests/component_test.py | 8 +-
src/lib/python/isc/cc/data.py | 8 +
src/lib/python/isc/cc/tests/.gitignore | 1 +
src/lib/python/isc/config/ccsession.py | 145 ++-
src/lib/python/isc/config/cfgmgr.py | 65 +-
src/lib/python/isc/config/config_data.py | 67 +-
src/lib/python/isc/config/config_messages.mes | 6 +
src/lib/python/isc/config/tests/.gitignore | 1 +
src/lib/python/isc/config/tests/ccsession_test.py | 367 ++++++-
src/lib/python/isc/config/tests/cfgmgr_test.py | 255 +++--
.../python/isc/config/tests/config_data_test.py | 65 ++-
src/lib/python/isc/datasrc/finder_python.cc | 24 +-
src/lib/python/isc/log/Makefile.am | 9 -
src/lib/python/isc/log/tests/.gitignore | 1 +
src/lib/python/isc/log_messages/Makefile.am | 2 +
.../isc/log_messages/server_common_messages.py | 1 +
src/lib/python/isc/log_messages/work/.gitignore | 2 +
src/lib/python/isc/notify/tests/.gitignore | 1 +
src/lib/python/isc/server_common/Makefile.am | 24 +
.../isc/{bind10 => server_common}/__init__.py | 0
.../isc/server_common/server_common_messages.mes | 36 +
src/lib/python/isc/server_common/tests/Makefile.am | 24 +
.../isc/server_common/tests/tsig_keyring_test.py | 193 ++++
src/lib/python/isc/server_common/tsig_keyring.py | 121 ++
src/lib/python/isc/testutils/Makefile.am | 3 +-
src/lib/python/isc/testutils/ccsession_mock.py | 34 +
src/lib/resolve/.gitignore | 2 +
src/lib/resolve/tests/.gitignore | 1 +
.../resolve/tests/recursive_query_unittest_2.cc | 17 +-
.../resolve/tests/recursive_query_unittest_3.cc | 17 +-
src/lib/server_common/.gitignore | 2 +
src/lib/server_common/portconfig.cc | 14 +-
src/lib/server_common/server_common_messages.mes | 2 +-
src/lib/server_common/socket_request.cc | 20 +-
src/lib/server_common/socket_request.h | 35 +-
src/lib/server_common/tests/.gitignore | 2 +
src/lib/server_common/tests/portconfig_unittest.cc | 5 +-
.../server_common/tests/socket_requestor_test.cc | 97 +-
src/lib/statistics/tests/.gitignore | 1 +
src/lib/testutils/dnsmessage_test.cc | 3 +
src/lib/testutils/dnsmessage_test.h | 31 +-
src/lib/testutils/socket_request.h | 25 +-
src/lib/testutils/srv_test.cc | 3 +-
src/lib/testutils/srv_test.h | 1 -
src/lib/testutils/testdata/.gitignore | 12 +
src/lib/testutils/testdata/Makefile.am | 3 +
.../testutils/testdata/nsec3query_fromWire.spec | 11 +
.../testdata/nsec3query_nodnssec_fromWire.spec | 9 +
.../testutils/testdata/rfc5155-example.zone.signed | 72 ++
src/lib/util/io/fd_share.cc | 17 +-
src/lib/util/io/sockaddr_util.h | 14 +
src/lib/util/locks.h | 58 +-
src/lib/util/python/.gitignore | 2 +
src/lib/util/python/gen_wiredata.py.in | 48 +-
src/lib/util/tests/.gitignore | 1 +
src/lib/xfr/tests/.gitignore | 1 +
tests/lettuce/.gitignore | 1 +
.../multi_instance/multi_auth.config.orig | 1 +
.../lettuce/configurations/nsec3/nsec3_auth.config | 1 +
.../nsec3/rfc5155-example.zone.signed | 72 ++
tests/lettuce/features/bindctl_commands.feature | 38 +
tests/lettuce/features/multi_instance.feature | 35 +
tests/lettuce/features/nsec3_auth.feature | 302 +++++
tests/lettuce/features/terrain/bind10_control.py | 213 ++++-
tests/lettuce/features/terrain/querying.py | 77 ++-
tests/lettuce/features/terrain/terrain.py | 8 +-
tests/lettuce/features/xfrin_bind10.feature | 2 +-
tests/system/.gitignore | 2 +
tests/system/bindctl/nsx1/.gitignore | 1 +
.../system/bindctl/nsx1/b10-config.db.template.in | 3 -
tests/system/glue/.gitignore | 1 +
tests/system/glue/nsx1/.gitignore | 1 +
tests/system/glue/nsx1/b10-config.db.in | 3 -
tests/system/ixfr/.gitignore | 8 +
tests/system/ixfr/in-1/.gitignore | 1 +
tests/system/ixfr/in-2/.gitignore | 1 +
tests/system/ixfr/in-2/tests.sh | 2 +-
tests/system/ixfr/in-3/.gitignore | 1 +
tests/system/ixfr/in-4/.gitignore | 1 +
tests/tools/badpacket/.gitignore | 1 +
tests/tools/badpacket/scan.cc | 3 +-
tests/tools/badpacket/tests/.gitignore | 1 +
tests/tools/perfdhcp/.gitignore | 1 +
373 files changed, 12935 insertions(+), 3799 deletions(-)
create mode 100644 .gitignore
create mode 100644 compatcheck/.gitignore
create mode 100644 doc/.gitignore
create mode 100644 src/bin/auth/.gitignore
create mode 100644 src/bin/auth/benchmarks/.gitignore
create mode 100644 src/bin/auth/tests/.gitignore
create mode 100644 src/bin/bind10/.gitignore
create mode 100644 src/bin/bind10/tests/.gitignore
create mode 100644 src/bin/bindctl/.gitignore
create mode 100644 src/bin/bindctl/tests/.gitignore
create mode 100644 src/bin/cfgmgr/.gitignore
create mode 100644 src/bin/cfgmgr/tests/.gitignore
create mode 100644 src/bin/cmdctl/.gitignore
create mode 100644 src/bin/cmdctl/tests/.gitignore
create mode 100644 src/bin/ddns/.gitignore
create mode 100644 src/bin/dhcp4/.gitignore
create mode 100644 src/bin/dhcp4/tests/.gitignore
create mode 100644 src/bin/host/.gitignore
create mode 100644 src/bin/loadzone/.gitignore
create mode 100644 src/bin/loadzone/tests/correct/.gitignore
create mode 100644 src/bin/loadzone/tests/error/.gitignore
create mode 100644 src/bin/msgq/.gitignore
create mode 100644 src/bin/msgq/tests/.gitignore
create mode 100644 src/bin/resolver/.gitignore
create mode 100644 src/bin/resolver/common.cc
create mode 100644 src/bin/resolver/common.h
create mode 100644 src/bin/resolver/tests/.gitignore
create mode 100644 src/bin/sockcreator/.gitignore
create mode 100644 src/bin/sockcreator/tests/.gitignore
create mode 100644 src/bin/stats/.gitignore
create mode 100644 src/bin/tests/.gitignore
create mode 100644 src/bin/usermgr/.gitignore
create mode 100644 src/bin/xfrin/.gitignore
create mode 100644 src/bin/xfrin/tests/.gitignore
create mode 100644 src/bin/xfrout/.gitignore
create mode 100644 src/bin/xfrout/tests/.gitignore
create mode 100644 src/bin/zonemgr/.gitignore
create mode 100644 src/bin/zonemgr/tests/.gitignore
create mode 100644 src/lib/acl/tests/.gitignore
create mode 100644 src/lib/asiodns/.gitignore
create mode 100644 src/lib/asiodns/sync_udp_server.cc
create mode 100644 src/lib/asiodns/sync_udp_server.h
create mode 100644 src/lib/asiodns/tests/.gitignore
create mode 100644 src/lib/asiolink/tests/.gitignore
create mode 100644 src/lib/bench/example/.gitignore
create mode 100644 src/lib/bench/tests/.gitignore
create mode 100644 src/lib/cache/.gitignore
create mode 100644 src/lib/cache/tests/.gitignore
create mode 100644 src/lib/cc/.gitignore
create mode 100644 src/lib/cc/tests/.gitignore
create mode 100644 src/lib/config/.gitignore
create mode 100644 src/lib/config/tests/.gitignore
create mode 100644 src/lib/config/tests/testdata/.gitignore
create mode 100644 src/lib/cryptolink/tests/.gitignore
create mode 100644 src/lib/datasrc/.gitignore
create mode 100644 src/lib/datasrc/memory_datasrc_link.cc
create mode 100644 src/lib/datasrc/rbnode_rrset.h
create mode 100644 src/lib/datasrc/sqlite3_accessor_link.cc
create mode 100644 src/lib/datasrc/tests/.gitignore
create mode 100644 src/lib/datasrc/tests/rbnode_rrset_unittest.cc
create mode 100644 src/lib/datasrc/tests/testdata/contexttest.zone
copy src/lib/{dns => datasrc}/tests/testdata/rrset_toWire1 (100%)
create mode 100644 src/lib/datasrc/tests/testdata/rrset_toWire2
create mode 100644 src/lib/datasrc/tests/zone_finder_context_unittest.cc
create mode 100644 src/lib/datasrc/zone_finder_context.cc
create mode 100644 src/lib/dhcp/tests/.gitignore
create mode 100644 src/lib/dns/.gitignore
create mode 100644 src/lib/dns/benchmarks/.gitignore
create mode 100644 src/lib/dns/labelsequence.cc
create mode 100644 src/lib/dns/labelsequence.h
create mode 100644 src/lib/dns/python/tests/.gitignore
create mode 100644 src/lib/dns/rdata/generic/detail/nsec3param_common.cc
create mode 100644 src/lib/dns/rdata/generic/detail/nsec3param_common.h
create mode 100644 src/lib/dns/tests/.gitignore
create mode 100644 src/lib/dns/tests/labelsequence_unittest.cc
create mode 100644 src/lib/dns/tests/rdata_nsec3param_like_unittest.cc
create mode 100644 src/lib/dns/tests/testdata/.gitignore
create mode 100644 src/lib/dns/tests/testdata/rdata_nsec3_fromWire16.spec
create mode 100644 src/lib/dns/tests/testdata/rdata_nsec3_fromWire17.spec
create mode 100644 src/lib/dns/tests/testdata/rdata_nsec3param_fromWire11.spec
create mode 100644 src/lib/dns/tests/testdata/rdata_nsec3param_fromWire13.spec
create mode 100644 src/lib/dns/tests/testdata/rdata_nsec3param_fromWire2.spec
create mode 100644 src/lib/dns/tests/testdata/rdata_nsec_fromWire16.spec
create mode 100644 src/lib/exceptions/tests/.gitignore
create mode 100644 src/lib/log/compiler/.gitignore
create mode 100644 src/lib/log/tests/.gitignore
create mode 100644 src/lib/nsas/.gitignore
create mode 100644 src/lib/nsas/tests/.gitignore
create mode 100644 src/lib/python/.gitignore
create mode 100644 src/lib/python/isc/cc/tests/.gitignore
create mode 100644 src/lib/python/isc/config/tests/.gitignore
create mode 100644 src/lib/python/isc/log/tests/.gitignore
create mode 100644 src/lib/python/isc/log_messages/server_common_messages.py
create mode 100644 src/lib/python/isc/log_messages/work/.gitignore
create mode 100644 src/lib/python/isc/notify/tests/.gitignore
create mode 100644 src/lib/python/isc/server_common/Makefile.am
copy src/lib/python/isc/{bind10 => server_common}/__init__.py (100%)
create mode 100644 src/lib/python/isc/server_common/server_common_messages.mes
create mode 100644 src/lib/python/isc/server_common/tests/Makefile.am
create mode 100644 src/lib/python/isc/server_common/tests/tsig_keyring_test.py
create mode 100644 src/lib/python/isc/server_common/tsig_keyring.py
create mode 100644 src/lib/python/isc/testutils/ccsession_mock.py
create mode 100644 src/lib/resolve/.gitignore
create mode 100644 src/lib/resolve/tests/.gitignore
create mode 100644 src/lib/server_common/.gitignore
create mode 100644 src/lib/server_common/tests/.gitignore
create mode 100644 src/lib/statistics/tests/.gitignore
create mode 100644 src/lib/testutils/testdata/.gitignore
create mode 100644 src/lib/testutils/testdata/nsec3query_fromWire.spec
create mode 100644 src/lib/testutils/testdata/nsec3query_nodnssec_fromWire.spec
create mode 100644 src/lib/testutils/testdata/rfc5155-example.zone.signed
create mode 100644 src/lib/util/python/.gitignore
create mode 100644 src/lib/util/tests/.gitignore
create mode 100644 src/lib/xfr/tests/.gitignore
create mode 100644 tests/lettuce/.gitignore
create mode 100644 tests/lettuce/configurations/multi_instance/multi_auth.config.orig
create mode 100644 tests/lettuce/configurations/nsec3/nsec3_auth.config
create mode 100644 tests/lettuce/configurations/nsec3/rfc5155-example.zone.signed
create mode 100644 tests/lettuce/features/bindctl_commands.feature
create mode 100644 tests/lettuce/features/multi_instance.feature
create mode 100644 tests/lettuce/features/nsec3_auth.feature
create mode 100644 tests/system/.gitignore
create mode 100644 tests/system/bindctl/nsx1/.gitignore
create mode 100644 tests/system/glue/.gitignore
create mode 100644 tests/system/glue/nsx1/.gitignore
create mode 100644 tests/system/ixfr/.gitignore
create mode 100644 tests/system/ixfr/in-1/.gitignore
create mode 100644 tests/system/ixfr/in-2/.gitignore
create mode 100644 tests/system/ixfr/in-3/.gitignore
create mode 100644 tests/system/ixfr/in-4/.gitignore
create mode 100644 tests/tools/badpacket/.gitignore
create mode 100644 tests/tools/badpacket/tests/.gitignore
create mode 100644 tests/tools/perfdhcp/.gitignore
-----------------------------------------------------------------------
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..600aac3
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,28 @@
+*.la
+*.lo
+*.o
+.deps/
+.libs/
+Makefile
+Makefile.in
+TAGS
+
+/aclocal.m4
+/autom4te.cache/
+/config.guess
+/config.h
+/config.h.in
+/config.log
+/config.report
+/config.status
+/config.sub
+/configure
+/cscope.files
+/cscope.out
+/depcomp
+/install-sh
+/libtool
+/ltmain.sh
+/missing
+/py-compile
+/stamp-h1
diff --git a/ChangeLog b/ChangeLog
index 14eb4ab..d9afae0 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,7 +1,192 @@
+398. [func] jelte
+ The b10-xfrin module now logs more information on successful
+ incoming transfers. In the case of IXFR, it logs the number of
+ changesets, and the total number of added and deleted resource
+ records. For AXFR (or AXFR-style IXFR), it logs the number of
+ resource records. In both cases, the number of overhead DNS
+ messages, runtime, amount of wire data, and transfer speed are logged.
+ (Trac #1280, git 2b01d944b6a137f95d47673ea8367315289c205d)
+
+397. [func] muks
+ The boss process now gives more helpful description when a
+ sub-process exits due to a signal.
+ (Trac #1673, git 1cd0d0e4fc9324bbe7f8593478e2396d06337b1e)
+
+396. [func]* jinmei
+ libdatasrc: change the return type of ZoneFinder::find() so it can
+ contain more context of the search, which can be used for
+ optimizing post find() processing. A new method getAdditional()
+ is added to it for finding additional RRsets based on the result
+ of find(). External behavior shouldn't change. The query
+ handling code of b10-auth now uses the new interface.
+ (Trac #1607, git 2e940ea65d5b9f371c26352afd9e66719c38a6b9)
+
+395. [bug] jelte
+ The log message compiler now errors (resulting in build failures) if
+ duplicate log message identifiers are found in a single message file.
+ Renamed one duplicate that was found (RESOLVER_SHUTDOWN, renamed to
+ RESOLVER_SHUTDOWN_RECEIVED).
+ (Trac #1093, git f537c7e12fb7b25801408f93132ed33410edae76)
+ (Trac #1741, git b8960ab85c717fe70ad282e0052ac0858c5b57f7)
+
+394. [bug] jelte
+ b10-auth now catches any exceptions during response building; if any
+ datasource either throws an exception or causes an exception to be
+ thrown, the message processing code will now catch it, log a debug
+ message, and return a SERVFAIL response.
+ (Trac #1612, git b5740c6b3962a55e46325b3c8b14c9d64cf0d845)
+
+393. [func] jelte
+ Introduced a new class LabelSequence in libdns++, which provides
+ lightweight accessor functionality to the Name class, for more
+ efficient comparison of parts of names.
+ (Trac #1602, git b33929ed5df7c8f482d095e96e667d4a03180c78)
+
+392. [func]* jinmei
+ libdns++: revised the (Abstract)MessageRenderer class so that it
+ has a default internal buffer and the buffer can be temporarily
+ switched. The constructor interface was modified, and a new
+ method setBuffer() was added.
+ (Trac #1697, git 9cabc799f2bf9a3579dae7f1f5d5467c8bb1aa40)
+
+391. [bug]* vorner
+ The long time unused configuration options of Xfrout "log_name",
+ "log_file", "log_severity", "log_version" and "log_max_bytes" were
+ removed, as they had no effect (Xfrout uses the global logging
+ framework). However, if you have them set, you need to remove
+ them from the configuration file or the configuration will be
+ rejected.
+ (Trac #1090, git ef1eba02e4cf550e48e7318702cff6d67c1ec82e)
+
+bind10-devel-20120301 released on March 1, 2012
+
+390. [bug] vorner
+ The UDP IPv6 packets are now correctly fragmented for maximum
+ guaranteed MTU, so they won't get lost because being too large
+ for some hop.
+ (Trac #1534, git ff013364643f9bfa736b2d23fec39ac35872d6ad)
+
+389. [func]* vorner
+ Xfrout now uses the global TSIG keyring, instead of its own. This
+ means the keys need to be set only once (in tsig_keys/keys).
+ However, the old configuration of Xfrout/tsig_keys need to be
+ removed for Xfrout to work.
+ (Trac #1643, git 5a7953933a49a0ddd4ee1feaddc908cd2285522d)
+
+388. [func] jreed
+ Use prefix "sockcreator-" for the private temporary directory
+ used for b10-sockcreator communication.
+ (git b98523c1260637cb33436964dc18e9763622a242)
+
+387. [build] muks
+ Accept a --without-werror configure switch so that some builders can
+ disable the use of -Werror in CFLAGS when building.
+ (Trac #1671, git 8684a411d7718a71ad9fb616f56b26436c4f03e5)
+
+386. [bug] jelte
+ Upon initial sqlite3 database creation, the 'diffs' table is now
+ always created. This already happened most of the time, but there
+ are a few cases where it was skipped, resulting in potential errors
+ in xfrout later.
+ (Trac #1717, git 30d7686cb6e2fa64866c983e0cfb7b8fabedc7a2)
+
+385. [bug] jinmei
+ libdns++: masterLoad() didn't accept comments placed at the end of
+ an RR. Due to this the in-memory data source cannot load a master
+ file for a signed zone even if it's preprocessed with BIND 9's
+ named-compilezone.
+ Note: this fix is considered temporary and still only accepts some
+ limited form of such comments. The main purpose is to allow the
+ in-memory data source to load any signed or unsigned zone files as
+ long as they are at least normalized with named-compilezone.
+ (Trac #1667, git 6f771b28eea25c693fe93a0e2379af924464a562)
+
+384. [func] jinmei, jelte, vorner, haikuo, kevin
+ b10-auth now supports NSEC3-signed zones in the in-memory data
+ source.
+ (Trac #1580, #1581, #1582, #1583, #1584, #1585, #1587, and
+ other related changes to the in-memory data source)
+
+383. [build] jinmei
+ Fixed build failure on MacOS 10.7 (Lion) due to the use of
+ IPV6_PKTINFO; the OS requires a special definition to make it
+ visible to the compiler.
+ (Trac #1633, git 19ba70c7cc3da462c70e8c4f74b321b8daad0100)
+
+382. [func] jelte
+ b10-auth now also experimentally supports statistics counters of
+ the rcode responses it sends. The counters can be shown as
+ rcode.<code name>, where code name is the lowercase textual
+ representation of the rcode (e.g. "noerror", "formerr", etc.).
+ Same note applies as for opcodes, see changelog entry 364.
+ (Trac #1613, git e98da500d7b02e11347431a74f2efce5a7d622aa)
+
+381. [bug] jinmei
+ b10-auth: honor the DNSSEC DO bit in the new query handler.
+ (Trac #1695, git 61f4da5053c6a79fbc162fb16f195cdf8f94df64)
+
+380. [bug] jinmei
+ libdns++: miscellaneous bug fixes for the NSECPARAM RDATA
+ implementation, including incorrect handling for empty salt and
+ incorrect comparison logic.
+ (Trac #1638, git 966c129cc3c538841421f1e554167d33ef9bdf25)
+
+379. [bug] jelte
+ Configuration commands in bindctl now check for list indices if
+ the 'identifier' argument points to a child element of a list
+ item. Previously, it was possible to 'get' non-existent values
+ by leaving out the index, e.g. "config show Auth/listen_on/port,
+ which should be config show Auth/listen_on[<index>]/port, since
+ Auth/listen_on is a list. The command without an index will now
+ show an error. It is still possible to show/set the entire list
+ ("config show Auth/listen_on").
+ (Trac #1649, git 003ca8597c8d0eb558b1819dbee203fda346ba77)
+
+378. [func] vorner
+ It is possible to start authoritative server or resolver in multiple
+ instances, to use more than one core. Configuration is described in
+ the guide.
+ (Trac #1596, git 17f7af0d8a42a0a67a2aade5bc269533efeb840a)
+
+377. [bug] jinmei
+ libdns++: miscellaneous bug fixes for the NSEC and NSEC3 RDATA
+ implementation, including a crash in NSEC3::toText() for some RR
+ types, incorrect handling of empty NSEC3 salt, and incorrect
+ comparison logic in NSEC3::compare().
+ (Trac #1641, git 28ba8bd71ae4d100cb250fd8d99d80a17a6323a2)
+
+376. [bug] jinmei, vorner
+ The new query handling module of b10-auth did not handle type DS
+ query correctly: It didn't look for it in the parent zone, and
+ it incorrectly returned a DS from the child zone if it
+ happened to exist there. Both were corrected, and it now also
+ handles the case of having authority for the child and a grand
+ ancestor.
+ (Trac #1570, git 2858b2098a10a8cc2d34bf87463ace0629d3670e)
+
+375. [func] jelte
+ Modules now inform the system when they are stopping. As a result,
+ they are removed from the 'active modules' list in bindctl, which
+ can then inform the user directly when it tries to send them a
+ command or configuration update. Previously this would result
+ in a 'not responding' error instead of 'not running'.
+ (Trac #640, git 17e78fa1bb1227340aa9815e91ed5c50d174425d)
+
+374. [func]* stephen
+ Alter RRsetPtr and ConstRRsetPtr to point to AbstractRRset (instead
+ of RRset) to allow for specialised implementations of RRsets in
+ data sources.
+ (Trac #1604, git 3071211d2c537150a691120b0a5ce2b18d010239)
+
+373. [bug] jinmei
+ libdatasrc: the in-memory data source incorrectly rejected loading
+ a zone containing a CNAME RR with RRSIG and/or NSEC.
+ (Trac #1551, git 76f823d42af55ce3f30a0d741fc9297c211d8b38)
+
372. [func] vorner
When the allocation of a socket fails for a different reason than the
- socket not being provided by the OS, the b10-auth and b10-resolver abort,
- as the system might be in inconsistent state after such error.
+ socket not being provided by the OS, the b10-auth and b10-resolver
+ abort, as the system might be in inconsistent state after such error.
(Trac #1543, git 49ac4659f15c443e483922bf9c4f2de982bae25d)
371. [bug] jelte
@@ -20,10 +205,11 @@
(Trac #1575, git 2c421b58e810028b303d328e4e2f5b74ea124839)
369. [func] vorner
- The SocketRequestor provides more information about what error happened
- when it throws, by using subclasses of the original exception. This way
- a user not interested in the difference can still use the original
- exception, while it can be recognized if necessary.
+ The SocketRequestor provides more information about what error
+ happened when it throws, by using subclasses of the original
+ exception. This way a user not interested in the difference can
+ still use the original exception, while it can be recognized if
+ necessary.
(Trac #1542, git 2080e0316a339fa3cadea00e10b1ec4bc322ada0)
368. [func]* jinmei
@@ -81,7 +267,8 @@ bind10-devel-20120119 released on January 19, 2012
configuration. If your b10-config.db contains "setuid" for
Boss.components, you'll need to remove that entry by hand before
starting BIND 10.
- (Trac #1508-#1510, git edc5b3c12eb45437361484c843794416ad86bb00)
+ (Trac #1508, #1509, #1510,
+ git edc5b3c12eb45437361484c843794416ad86bb00)
361. [func] vorner,jelte,jinmei
The socket creator is now used to provide sockets. It means you can
diff --git a/compatcheck/.gitignore b/compatcheck/.gitignore
new file mode 100644
index 0000000..180a3ec
--- /dev/null
+++ b/compatcheck/.gitignore
@@ -0,0 +1 @@
+/sqlite3-difftbl-check.py
diff --git a/configure.ac b/configure.ac
index 8889ddc..72a462f 100644
--- a/configure.ac
+++ b/configure.ac
@@ -5,6 +5,7 @@ AC_PREREQ([2.59])
AC_INIT(bind10-devel, 20120127, bind10-dev at isc.org)
AC_CONFIG_SRCDIR(README)
AM_INIT_AUTOMAKE
+m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])dnl be backward compatible
AC_CONFIG_HEADERS([config.h])
# Checks for programs.
@@ -84,11 +85,6 @@ if test $enable_shared = no; then
AC_MSG_ERROR([BIND 10 requires shared libraries to be built])
fi
-AC_ARG_ENABLE(boost-threads,
-AC_HELP_STRING([--enable-boost-threads],
- [use boost threads. Currently this only means using its locks instead of dummy locks, in the cache and NSAS]),
- use_boost_threads=$enableval, use_boost_threads=no)
-
# allow configuring without setproctitle.
AC_ARG_ENABLE(setproctitle-check,
AC_HELP_STRING([--disable-setproctitle-check],
@@ -108,6 +104,10 @@ case "$host" in
LDFLAGS="$LDFLAGS -z now"
;;
*-apple-darwin*)
+ # Starting with OSX 10.7 (Lion) we must choose which IPv6 API to use
+ # (RFC2292 or RFC3542).
+ CPPFLAGS="$CPPFLAGS -D__APPLE_USE_RFC_3542"
+
# libtool doesn't work perfectly with Darwin: libtool embeds the
# final install path in dynamic libraries and our loadable python
# modules always refer to that path even if it's loaded within the
@@ -270,7 +270,7 @@ AC_DEFUN([BIND10_CXX_TRY_FLAG], [
bind10_save_CXXFLAGS="$CXXFLAGS"
CXXFLAGS="$CXXFLAGS $1"
- AC_LINK_IFELSE([int main(void){ return 0;} ],
+ AC_LINK_IFELSE([int main(void){ return 0;}],
[bind10_cxx_flag=yes], [bind10_cxx_flag=no])
CXXFLAGS="$bind10_save_CXXFLAGS"
@@ -283,8 +283,6 @@ AC_DEFUN([BIND10_CXX_TRY_FLAG], [
AC_MSG_RESULT([$bind10_cxx_flag])
])
-werror_ok=0
-
# SunStudio compiler requires special compiler options for boost
# (http://blogs.sun.com/sga/entry/boost_mini_howto)
if test "$SUNCXX" = "yes"; then
@@ -292,7 +290,7 @@ CXXFLAGS="$CXXFLAGS -library=stlport4 -features=tmplife -features=tmplrefstatic"
MULTITHREADING_FLAG="-mt"
fi
-BIND10_CXX_TRY_FLAG(-Wno-missing-field-initializers,
+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)
@@ -310,19 +308,34 @@ case "$host" in
;;
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.
-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_;};} ],,
+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"
+ CXXFLAGS="$CXXFLAGS_SAVED"
+fi
# 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
@@ -517,21 +530,22 @@ else
AC_PATH_PROG([BOTAN_CONFIG], [botan-config])
fi
fi
-
-BOTAN_LIBS=`${BOTAN_CONFIG} --libs`
-BOTAN_INCLUDES=`${BOTAN_CONFIG} --cflags`
-
-# We expect botan-config --libs to contain -L<path_to_libbotan>, but
-# this is not always the case. As a heuristics workaround we add
-# -L`botan-config --prefix/lib` in this case (if not present already).
-# Same for BOTAN_INCLUDES (but using include instead of lib) below.
-if [ $BOTAN_CONFIG --prefix >/dev/null 2>&1 ] ; then
- echo ${BOTAN_LIBS} | grep -- -L > /dev/null || \
- BOTAN_LIBS="-L`${BOTAN_CONFIG} --prefix`/lib ${BOTAN_LIBS}"
- echo ${BOTAN_INCLUDES} | grep -- -I > /dev/null || \
- BOTAN_INCLUDES="-I`${BOTAN_CONFIG} --prefix`/include ${BOTAN_INCLUDES}"
+if test "x${BOTAN_CONFIG}" != "x"
+then
+ BOTAN_LIBS=`${BOTAN_CONFIG} --libs`
+ BOTAN_INCLUDES=`${BOTAN_CONFIG} --cflags`
+
+ # We expect botan-config --libs to contain -L<path_to_libbotan>, but
+ # this is not always the case. As a heuristics workaround we add
+ # -L`botan-config --prefix/lib` in this case (if not present already).
+ # Same for BOTAN_INCLUDES (but using include instead of lib) below.
+ if [ ${BOTAN_CONFIG} --prefix >/dev/null 2>&1 ] ; then
+ echo ${BOTAN_LIBS} | grep -- -L > /dev/null || \
+ BOTAN_LIBS="-L`${BOTAN_CONFIG} --prefix`/lib ${BOTAN_LIBS}"
+ echo ${BOTAN_INCLUDES} | grep -- -I > /dev/null || \
+ BOTAN_INCLUDES="-I`${BOTAN_CONFIG} --prefix`/include ${BOTAN_INCLUDES}"
+ fi
fi
-
# botan-config script (and the way we call pkg-config) returns -L and -l
# as one string, but we need them in separate values
BOTAN_LDFLAGS=
@@ -661,68 +675,6 @@ AC_CHECK_HEADERS([boost/shared_ptr.hpp boost/foreach.hpp boost/interprocess/sync
CPPFLAGS="$CPPFLAGS_SAVES"
AC_SUBST(BOOST_INCLUDES)
-
-if test "${use_boost_threads}" = "yes" ; then
- AC_DEFINE([USE_BOOST_THREADS], [], [Use boost threads])
-
- # Using boost::mutex can result in requiring libboost_thread with older
- # versions of Boost. We'd like to avoid relying on a compiled Boost library
- # whenever possible, so we need to check for it step by step.
- #
- # NOTE: another fix of this problem is to simply require newer versions of
- # boost. If we choose that solution we should simplify the following tricky
- # checks accordingly and all Makefile.am's that refer to NEED_LIBBOOST_THREAD.
- AC_MSG_CHECKING(for boost::mutex)
- CPPFLAGS_SAVES="$CPPFLAGS"
- LIBS_SAVES="$LIBS"
- CPPFLAGS="$BOOST_INCLUDES $CPPFLAGS $MULTITHREADING_FLAG"
- need_libboost_thread=0
- need_sunpro_workaround=0
- AC_TRY_LINK([
- #include <boost/thread.hpp>
- ],[
- boost::mutex m;
- ],
- [ AC_MSG_RESULT(yes (without libboost_thread)) ],
- # there is one specific problem with SunStudio 5.10
- # where including boost/thread causes a compilation failure
- # There is a workaround in boost but it checks the version not being 5.10
- # This will probably be fixed in the future, in which case this
- # is only a temporary workaround
- [ AC_TRY_LINK([
- #if defined(__SUNPRO_CC) && __SUNPRO_CC == 0x5100
- #undef __SUNPRO_CC
- #define __SUNPRO_CC 0x5090
- #endif
- #include <boost/thread.hpp>
- ],[
- boost::mutex m;
- ],
- [ AC_MSG_RESULT(yes (with SUNOS workaround))
- need_sunpro_workaround=1 ],
- [ LIBS=" $LIBS -lboost_thread"
- AC_TRY_LINK([
- #include <boost/thread.hpp>
- ],[
- boost::mutex m;
- ],
- [ AC_MSG_RESULT(yes (with libboost_thread))
- need_libboost_thread=1 ],
- [ AC_MSG_RESULT(no)
- AC_MSG_ERROR([boost::mutex cannot be linked in this build environment.
- Perhaps you are using an older version of Boost that requires libboost_thread for the mutex support, which does not appear to be available.
- You may want to check the availability of the library or to upgrade Boost.])
- ])])])
- CPPFLAGS="$CPPFLAGS_SAVES"
- LIBS="$LIBS_SAVES"
- AM_CONDITIONAL(NEED_LIBBOOST_THREAD, test $need_libboost_thread = 1)
- if test $need_sunpro_workaround = 1; then
- AC_DEFINE([NEED_SUNPRO_WORKAROUND], [], [Need boost sunstudio workaround])
- fi
-else
- AM_CONDITIONAL(NEED_LIBBOOST_THREAD, test "${use_boost_threads}" = "yes")
-fi
-
# I can't get some of the #include <asio.hpp> right without this
# TODO: find the real cause of asio/boost wanting pthreads
# (this currently only occurs for src/lib/cc/session_unittests)
@@ -907,7 +859,7 @@ AC_PATH_PROGS(AWK, gawk awk)
AC_SUBST(AWK)
AC_ARG_ENABLE(man, [AC_HELP_STRING([--enable-man],
- [regenerate man pages [default=no]])], enable_man=yes, enable_man=no)
+ [regenerate man pages [default=no]])], enable_man=$enableval, enable_man=no)
AM_CONDITIONAL(ENABLE_MAN, test x$enable_man != xno)
@@ -1001,6 +953,8 @@ AC_CONFIG_FILES([Makefile
src/lib/python/isc/bind10/tests/Makefile
src/lib/python/isc/xfrin/Makefile
src/lib/python/isc/xfrin/tests/Makefile
+ src/lib/python/isc/server_common/Makefile
+ src/lib/python/isc/server_common/tests/Makefile
src/lib/config/Makefile
src/lib/config/tests/Makefile
src/lib/config/tests/testdata/Makefile
diff --git a/doc/.gitignore b/doc/.gitignore
new file mode 100644
index 0000000..cf437ce
--- /dev/null
+++ b/doc/.gitignore
@@ -0,0 +1 @@
+/version.ent
diff --git a/doc/guide/bind10-guide.html b/doc/guide/bind10-guide.html
index 6abe287..9a76d5a 100644
--- a/doc/guide/bind10-guide.html
+++ b/doc/guide/bind10-guide.html
@@ -10,7 +10,7 @@
The most up-to-date version of this document (in PDF, HTML,
and plain text formats), along with other documents for
BIND 10, can be found at <a class="ulink" href="http://bind10.isc.org/docs" target="_top">http://bind10.isc.org/docs</a>.
- </p></div></div></div><hr></div><div class="toc"><p><b>Table of Contents</b></p><dl><dt><span class="preface"><a href="#id1168229451188">Preface</a></span></dt><dd><dl><dt><span class="section"><a href="#acknowledgements">1. Acknowledgements</a></span></dt></dl></dd><dt><span class="chapter"><a href="#intro">1. Introduction</a></span></dt><dd><dl><dt><span class="section"><a href="#id1168229451269">1.1. Supported Platforms</a></span></dt><dt><span class="section"><a href="#required-software">1.2. Required Software</a></span></dt><dt><span class="section"><a href="#starting_stopping">1.3. Starting and Stopping the Server</a></span></dt><dt><span class="section"><a href="#managing_once_running">1.4. Managing BIND 10</a></span></dt></dl></dd><dt><span class="chapter"><a href="#installation">2. Installation</a></span></dt><dd><dl><dt><span class="section"><a href="#build-requirements">2.1. Building Requirements</a></span></dt><dt><span class="section"><a href="#quickstar
t">2.2. Quick start</a></span></dt><dt><span class="section"><a href="#install">2.3. Installation from source</a></span></dt><dd><dl><dt><span class="section"><a href="#id1168229436789">2.3.1. Download Tar File</a></span></dt><dt><span class="section"><a href="#id1168229436809">2.3.2. Retrieve from Git</a></span></dt><dt><span class="section"><a href="#id1168229436870">2.3.3. Configure before the build</a></span></dt><dt><span class="section"><a href="#id1168229436967">2.3.4. Build</a></span></dt><dt><span class="section"><a href="#id1168229436982">2.3.5. Install</a></span></dt><dt><span class="section"><a href="#id1168229437006">2.3.6. Install Hierarchy</a></span></dt></dl></dd></dl></dd><dt><span class="chapter"><a href="#bind10">3. Starting BIND10 with <span class="command"><strong>bind10</strong></span></a></span></dt><dd><dl><dt><span class="section"><a href="#start">3.1. Starting BIND 10</a></span></dt><dt><span class="section"><a href="#bind10.config">3.2. Configurati
on of started processes</a></span></dt></dl></dd><dt><span class="chapter"><a href="#msgq">4. Command channel</a></span></dt><dt><span class="chapter"><a href="#cfgmgr">5. Configuration manager</a></span></dt><dt><span class="chapter"><a href="#cmdctl">6. Remote control daemon</a></span></dt><dd><dl><dt><span class="section"><a href="#cmdctl.spec">6.1. Configuration specification for b10-cmdctl</a></span></dt></dl></dd><dt><span class="chapter"><a href="#bindctl">7. Control and configure user interface</a></span></dt><dt><span class="chapter"><a href="#authserver">8. Authoritative Server</a></span></dt><dd><dl><dt><span class="section"><a href="#id1168229437939">8.1. Server Configurations</a></span></dt><dt><span class="section"><a href="#id1168229438004">8.2. Data Source Backends</a></span></dt><dt><span class="section"><a href="#id1168229438035">8.3. Loading Master Zones Files</a></span></dt></dl></dd><dt><span class="chapter"><a href="#xfrin">9. Incoming Zone Transfers</a
></span></dt><dd><dl><dt><span class="section"><a href="#id1168229438166">9.1. Configuration for Incoming Zone Transfers</a></span></dt><dt><span class="section"><a href="#id1168229438203">9.2. Enabling IXFR</a></span></dt><dt><span class="section"><a href="#zonemgr">9.3. Secondary Manager</a></span></dt><dt><span class="section"><a href="#id1168229438317">9.4. Trigger an Incoming Zone Transfer Manually</a></span></dt></dl></dd><dt><span class="chapter"><a href="#xfrout">10. Outbound Zone Transfers</a></span></dt><dt><span class="chapter"><a href="#resolverserver">11. Recursive Name Server</a></span></dt><dd><dl><dt><span class="section"><a href="#id1168229438745">11.1. Access Control</a></span></dt><dt><span class="section"><a href="#id1168229438860">11.2. Forwarding</a></span></dt></dl></dd><dt><span class="chapter"><a href="#dhcp4">12. DHCPv4 Server</a></span></dt><dd><dl><dt><span class="section"><a href="#dhcp4-usage">12.1. DHCPv4 Server Usage</a></span></dt><dt><span c
lass="section"><a href="#dhcp4-config">12.2. DHCPv4 Server Configuration</a></span></dt><dt><span class="section"><a href="#dhcp4-std">12.3. Supported standards</a></span></dt><dt><span class="section"><a href="#dhcp4-limit">12.4. DHCPv4 Server Limitations</a></span></dt></dl></dd><dt><span class="chapter"><a href="#dhcp6">13. DHCPv6 Server</a></span></dt><dd><dl><dt><span class="section"><a href="#dhcp6-usage">13.1. DHCPv6 Server Usage</a></span></dt><dt><span class="section"><a href="#dhcp6-config">13.2. DHCPv6 Server Configuration</a></span></dt><dt><span class="section"><a href="#dhcp6-std">13.3. Supported DHCPv6 Standards</a></span></dt><dt><span class="section"><a href="#dhcp6-limit">13.4. DHCPv6 Server Limitations</a></span></dt></dl></dd><dt><span class="chapter"><a href="#libdhcp">14. libdhcp++ library</a></span></dt><dd><dl><dt><span class="section"><a href="#iface-detect">14.1. Interface detection</a></span></dt><dt><span class="section"><a href="#packet-handling"
>14.2. DHCPv4/DHCPv6 packet handling</a></span></dt></dl></dd><dt><span class="chapter"><a href="#statistics">15. Statistics</a></span></dt><dt><span class="chapter"><a href="#logging">16. Logging</a></span></dt><dd><dl><dt><span class="section"><a href="#id1168229439976">16.1. Logging configuration</a></span></dt><dd><dl><dt><span class="section"><a href="#id1168229439987">16.1.1. Loggers</a></span></dt><dt><span class="section"><a href="#id1168229440229">16.1.2. Output Options</a></span></dt><dt><span class="section"><a href="#id1168229440471">16.1.3. Example session</a></span></dt></dl></dd><dt><span class="section"><a href="#id1168229440680">16.2. Logging Message Format</a></span></dt></dl></dd></dl></div><div class="list-of-tables"><p><b>List of Tables</b></p><dl><dt>3.1. <a href="#id1168229437268"></a></dt></dl></div><div class="preface" title="Preface"><div class="titlepage"><div><div><h2 class="title"><a name="id1168229451188"></a>Preface</h2></div></div></div><div c
lass="toc"><p><b>Table of Contents</b></p><dl><dt><span class="section"><a href="#acknowledgements">1. Acknowledgements</a></span></dt></dl></div><div class="section" title="1. Acknowledgements"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="acknowledgements"></a>1. Acknowledgements</h2></div></div></div><p>ISC would like to acknowledge generous support for
+ </p></div></div></div><hr></div><div class="toc"><p><b>Table of Contents</b></p><dl><dt><span class="preface"><a href="#id1168229451188">Preface</a></span></dt><dd><dl><dt><span class="section"><a href="#acknowledgements">1. Acknowledgements</a></span></dt></dl></dd><dt><span class="chapter"><a href="#intro">1. Introduction</a></span></dt><dd><dl><dt><span class="section"><a href="#id1168229451269">1.1. Supported Platforms</a></span></dt><dt><span class="section"><a href="#required-software">1.2. Required Software</a></span></dt><dt><span class="section"><a href="#starting_stopping">1.3. Starting and Stopping the Server</a></span></dt><dt><span class="section"><a href="#managing_once_running">1.4. Managing BIND 10</a></span></dt></dl></dd><dt><span class="chapter"><a href="#installation">2. Installation</a></span></dt><dd><dl><dt><span class="section"><a href="#build-requirements">2.1. Building Requirements</a></span></dt><dt><span class="section"><a href="#quickstar
t">2.2. Quick start</a></span></dt><dt><span class="section"><a href="#install">2.3. Installation from source</a></span></dt><dd><dl><dt><span class="section"><a href="#id1168229436809">2.3.1. Download Tar File</a></span></dt><dt><span class="section"><a href="#id1168229436828">2.3.2. Retrieve from Git</a></span></dt><dt><span class="section"><a href="#id1168229436889">2.3.3. Configure before the build</a></span></dt><dt><span class="section"><a href="#id1168229436986">2.3.4. Build</a></span></dt><dt><span class="section"><a href="#id1168229437002">2.3.5. Install</a></span></dt><dt><span class="section"><a href="#id1168229437026">2.3.6. Install Hierarchy</a></span></dt></dl></dd></dl></dd><dt><span class="chapter"><a href="#bind10">3. Starting BIND10 with <span class="command"><strong>bind10</strong></span></a></span></dt><dd><dl><dt><span class="section"><a href="#start">3.1. Starting BIND 10</a></span></dt><dt><span class="section"><a href="#bind10.config">3.2. Configurati
on of started processes</a></span></dt></dl></dd><dt><span class="chapter"><a href="#msgq">4. Command channel</a></span></dt><dt><span class="chapter"><a href="#cfgmgr">5. Configuration manager</a></span></dt><dt><span class="chapter"><a href="#cmdctl">6. Remote control daemon</a></span></dt><dd><dl><dt><span class="section"><a href="#cmdctl.spec">6.1. Configuration specification for b10-cmdctl</a></span></dt></dl></dd><dt><span class="chapter"><a href="#bindctl">7. Control and configure user interface</a></span></dt><dt><span class="chapter"><a href="#authserver">8. Authoritative Server</a></span></dt><dd><dl><dt><span class="section"><a href="#id1168229437990">8.1. Server Configurations</a></span></dt><dt><span class="section"><a href="#id1168229438055">8.2. Data Source Backends</a></span></dt><dt><span class="section"><a href="#id1168229438085">8.3. Loading Master Zones Files</a></span></dt></dl></dd><dt><span class="chapter"><a href="#xfrin">9. Incoming Zone Transfers</a
></span></dt><dd><dl><dt><span class="section"><a href="#id1168229438216">9.1. Configuration for Incoming Zone Transfers</a></span></dt><dt><span class="section"><a href="#id1168229438254">9.2. Enabling IXFR</a></span></dt><dt><span class="section"><a href="#zonemgr">9.3. Secondary Manager</a></span></dt><dt><span class="section"><a href="#id1168229438369">9.4. Trigger an Incoming Zone Transfer Manually</a></span></dt></dl></dd><dt><span class="chapter"><a href="#xfrout">10. Outbound Zone Transfers</a></span></dt><dt><span class="chapter"><a href="#resolverserver">11. Recursive Name Server</a></span></dt><dd><dl><dt><span class="section"><a href="#id1168229438715">11.1. Access Control</a></span></dt><dt><span class="section"><a href="#id1168229438900">11.2. Forwarding</a></span></dt></dl></dd><dt><span class="chapter"><a href="#dhcp4">12. DHCPv4 Server</a></span></dt><dd><dl><dt><span class="section"><a href="#dhcp4-usage">12.1. DHCPv4 Server Usage</a></span></dt><dt><span c
lass="section"><a href="#dhcp4-config">12.2. DHCPv4 Server Configuration</a></span></dt><dt><span class="section"><a href="#dhcp4-std">12.3. Supported standards</a></span></dt><dt><span class="section"><a href="#dhcp4-limit">12.4. DHCPv4 Server Limitations</a></span></dt></dl></dd><dt><span class="chapter"><a href="#dhcp6">13. DHCPv6 Server</a></span></dt><dd><dl><dt><span class="section"><a href="#dhcp6-usage">13.1. DHCPv6 Server Usage</a></span></dt><dt><span class="section"><a href="#dhcp6-config">13.2. DHCPv6 Server Configuration</a></span></dt><dt><span class="section"><a href="#dhcp6-std">13.3. Supported DHCPv6 Standards</a></span></dt><dt><span class="section"><a href="#dhcp6-limit">13.4. DHCPv6 Server Limitations</a></span></dt></dl></dd><dt><span class="chapter"><a href="#libdhcp">14. libdhcp++ library</a></span></dt><dd><dl><dt><span class="section"><a href="#iface-detect">14.1. Interface detection</a></span></dt><dt><span class="section"><a href="#packet-handling"
>14.2. DHCPv4/DHCPv6 packet handling</a></span></dt></dl></dd><dt><span class="chapter"><a href="#statistics">15. Statistics</a></span></dt><dt><span class="chapter"><a href="#logging">16. Logging</a></span></dt><dd><dl><dt><span class="section"><a href="#id1168229440014">16.1. Logging configuration</a></span></dt><dd><dl><dt><span class="section"><a href="#id1168229440025">16.1.1. Loggers</a></span></dt><dt><span class="section"><a href="#id1168229440268">16.1.2. Output Options</a></span></dt><dt><span class="section"><a href="#id1168229440509">16.1.3. Example session</a></span></dt></dl></dd><dt><span class="section"><a href="#id1168229440720">16.2. Logging Message Format</a></span></dt></dl></dd></dl></div><div class="list-of-tables"><p><b>List of Tables</b></p><dl><dt>3.1. <a href="#id1168229437291"></a></dt></dl></div><div class="preface" title="Preface"><div class="titlepage"><div><div><h2 class="title"><a name="id1168229451188"></a>Preface</h2></div></div></div><div c
lass="toc"><p><b>Table of Contents</b></p><dl><dt><span class="section"><a href="#acknowledgements">1. Acknowledgements</a></span></dt></dl></div><div class="section" title="1. Acknowledgements"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="acknowledgements"></a>1. Acknowledgements</h2></div></div></div><p>ISC would like to acknowledge generous support for
BIND 10 development of DHCPv4 and DHCPv6 components provided
by <a class="ulink" href="http://www.comcast.com/" target="_top">Comcast</a>.</p></div></div><div class="chapter" title="Chapter 1. Introduction"><div class="titlepage"><div><div><h2 class="title"><a name="intro"></a>Chapter 1. Introduction</h2></div></div></div><div class="toc"><p><b>Table of Contents</b></p><dl><dt><span class="section"><a href="#id1168229451269">1.1. Supported Platforms</a></span></dt><dt><span class="section"><a href="#required-software">1.2. Required Software</a></span></dt><dt><span class="section"><a href="#starting_stopping">1.3. Starting and Stopping the Server</a></span></dt><dt><span class="section"><a href="#managing_once_running">1.4. Managing BIND 10</a></span></dt></dl></div><p>
BIND is the popular implementation of a DNS server, developer
@@ -78,11 +78,6 @@
</p><p>
</p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem">
- <span class="command"><strong>b10-msgq</strong></span> —
- Message bus daemon.
- This process coordinates communication between all of the other
- BIND 10 processes.
- </li><li class="listitem">
<span class="command"><strong>b10-auth</strong></span> —
Authoritative DNS server.
This process serves DNS requests.
@@ -95,15 +90,29 @@
Command and control service.
This process allows external control of the BIND 10 system.
</li><li class="listitem">
+ <span class="command"><strong>b10-msgq</strong></span> —
+ Message bus daemon.
+ This process coordinates communication between all of the other
+ BIND 10 processes.
+ </li><li class="listitem">
<span class="command"><strong>b10-resolver</strong></span> —
Recursive name server.
This process handles incoming queries.
</li><li class="listitem">
+ <span class="command"><strong>b10-sockcreator</strong></span> —
+ Socket creator daemon.
+ This process creates sockets used by
+ network-listening BIND 10 processes.
+ </li><li class="listitem">
<span class="command"><strong>b10-stats</strong></span> —
Statistics collection daemon.
This process collects and reports statistics data.
</li><li class="listitem">
+ <span class="command"><strong>b10-stats-httpd</strong></span> —
+ HTTP server for statistics reporting.
+ This process reports statistics data in XML format over HTTP.
+ </li><li class="listitem">
<span class="command"><strong>b10-xfrin</strong></span> —
Incoming zone transfer service.
This process is used to transfer a new copy
@@ -129,8 +138,9 @@
</p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem">
<span class="command"><strong>bindctl</strong></span> —
interactive administration interface.
- This is a command-line tool which allows an administrator
- to control BIND 10.
+ This is a low-level command-line tool which allows
+ a developer or an experienced administrator to control
+ BIND 10.
</li><li class="listitem">
<span class="command"><strong>b10-loadzone</strong></span> —
zone file loader.
@@ -152,7 +162,7 @@
and, of course, DNS. These include detailed developer
documentation and code examples.
- </p></div><div class="chapter" title="Chapter 2. Installation"><div class="titlepage"><div><div><h2 class="title"><a name="installation"></a>Chapter 2. Installation</h2></div></div></div><div class="toc"><p><b>Table of Contents</b></p><dl><dt><span class="section"><a href="#build-requirements">2.1. Building Requirements</a></span></dt><dt><span class="section"><a href="#quickstart">2.2. Quick start</a></span></dt><dt><span class="section"><a href="#install">2.3. Installation from source</a></span></dt><dd><dl><dt><span class="section"><a href="#id1168229436789">2.3.1. Download Tar File</a></span></dt><dt><span class="section"><a href="#id1168229436809">2.3.2. Retrieve from Git</a></span></dt><dt><span class="section"><a href="#id1168229436870">2.3.3. Configure before the build</a></span></dt><dt><span class="section"><a href="#id1168229436967">2.3.4. Build</a></span></dt><dt><span class="section"><a href="#id1168229436982">2.3.5. Install</a></span></dt><dt><span class="s
ection"><a href="#id1168229437006">2.3.6. Install Hierarchy</a></span></dt></dl></dd></dl></div><div class="section" title="2.1. Building Requirements"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="build-requirements"></a>2.1. Building Requirements</h2></div></div></div><p>
+ </p></div><div class="chapter" title="Chapter 2. Installation"><div class="titlepage"><div><div><h2 class="title"><a name="installation"></a>Chapter 2. Installation</h2></div></div></div><div class="toc"><p><b>Table of Contents</b></p><dl><dt><span class="section"><a href="#build-requirements">2.1. Building Requirements</a></span></dt><dt><span class="section"><a href="#quickstart">2.2. Quick start</a></span></dt><dt><span class="section"><a href="#install">2.3. Installation from source</a></span></dt><dd><dl><dt><span class="section"><a href="#id1168229436809">2.3.1. Download Tar File</a></span></dt><dt><span class="section"><a href="#id1168229436828">2.3.2. Retrieve from Git</a></span></dt><dt><span class="section"><a href="#id1168229436889">2.3.3. Configure before the build</a></span></dt><dt><span class="section"><a href="#id1168229436986">2.3.4. Build</a></span></dt><dt><span class="section"><a href="#id1168229437002">2.3.5. Install</a></span></dt><dt><span class="s
ection"><a href="#id1168229437026">2.3.6. Install Hierarchy</a></span></dt></dl></dd></dl></div><div class="section" title="2.1. Building Requirements"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="build-requirements"></a>2.1. Building Requirements</h2></div></div></div><p>
In addition to the run-time requirements, building BIND 10
from source code requires various development include headers.
</p><div class="note" title="Note" style="margin-left: 0.5in; margin-right: 0.5in;"><h3 class="title">Note</h3><p>
@@ -214,14 +224,14 @@
the Git code revision control system or as a downloadable
tar file. It may also be available in pre-compiled ready-to-use
packages from operating system vendors.
- </p><div class="section" title="2.3.1. Download Tar File"><div class="titlepage"><div><div><h3 class="title"><a name="id1168229436789"></a>2.3.1. Download Tar File</h3></div></div></div><p>
+ </p><div class="section" title="2.3.1. Download Tar File"><div class="titlepage"><div><div><h3 class="title"><a name="id1168229436809"></a>2.3.1. Download Tar File</h3></div></div></div><p>
Downloading a release tar file is the recommended method to
obtain the source code.
</p><p>
The BIND 10 releases are available as tar file downloads from
<a class="ulink" href="ftp://ftp.isc.org/isc/bind10/" target="_top">ftp://ftp.isc.org/isc/bind10/</a>.
Periodic development snapshots may also be available.
- </p></div><div class="section" title="2.3.2. Retrieve from Git"><div class="titlepage"><div><div><h3 class="title"><a name="id1168229436809"></a>2.3.2. Retrieve from Git</h3></div></div></div><p>
+ </p></div><div class="section" title="2.3.2. Retrieve from Git"><div class="titlepage"><div><div><h3 class="title"><a name="id1168229436828"></a>2.3.2. Retrieve from Git</h3></div></div></div><p>
Downloading this "bleeding edge" code is recommended only for
developers or advanced users. Using development code in a production
environment is not recommended.
@@ -255,7 +265,7 @@
<span class="command"><strong>autoheader</strong></span>,
<span class="command"><strong>automake</strong></span>,
and related commands.
- </p></div><div class="section" title="2.3.3. Configure before the build"><div class="titlepage"><div><div><h3 class="title"><a name="id1168229436870"></a>2.3.3. Configure before the build</h3></div></div></div><p>
+ </p></div><div class="section" title="2.3.3. Configure before the build"><div class="titlepage"><div><div><h3 class="title"><a name="id1168229436889"></a>2.3.3. Configure before the build</h3></div></div></div><p>
BIND 10 uses the GNU Build System to discover build environment
details.
To generate the makefiles using the defaults, simply run:
@@ -286,16 +296,16 @@
</p><p>
If the configure fails, it may be due to missing or old
dependencies.
- </p></div><div class="section" title="2.3.4. Build"><div class="titlepage"><div><div><h3 class="title"><a name="id1168229436967"></a>2.3.4. Build</h3></div></div></div><p>
+ </p></div><div class="section" title="2.3.4. Build"><div class="titlepage"><div><div><h3 class="title"><a name="id1168229436986"></a>2.3.4. Build</h3></div></div></div><p>
After the configure step is complete, to build the executables
from the C++ code and prepare the Python scripts, run:
</p><pre class="screen">$ <strong class="userinput"><code>make</code></strong></pre><p>
- </p></div><div class="section" title="2.3.5. Install"><div class="titlepage"><div><div><h3 class="title"><a name="id1168229436982"></a>2.3.5. Install</h3></div></div></div><p>
+ </p></div><div class="section" title="2.3.5. Install"><div class="titlepage"><div><div><h3 class="title"><a name="id1168229437002"></a>2.3.5. Install</h3></div></div></div><p>
To install the BIND 10 executables, support files,
and documentation, run:
</p><pre class="screen">$ <strong class="userinput"><code>make install</code></strong></pre><p>
- </p><div class="note" title="Note" style="margin-left: 0.5in; margin-right: 0.5in;"><h3 class="title">Note</h3><p>The install step may require superuser privileges.</p></div></div><div class="section" title="2.3.6. Install Hierarchy"><div class="titlepage"><div><div><h3 class="title"><a name="id1168229437006"></a>2.3.6. Install Hierarchy</h3></div></div></div><p>
+ </p><div class="note" title="Note" style="margin-left: 0.5in; margin-right: 0.5in;"><h3 class="title">Note</h3><p>The install step may require superuser privileges.</p></div></div><div class="section" title="2.3.6. Install Hierarchy"><div class="titlepage"><div><div><h3 class="title"><a name="id1168229437026"></a>2.3.6. Install Hierarchy</h3></div></div></div><p>
The following is the layout of the complete BIND 10 installation:
</p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem">
<code class="filename">bin/</code> —
@@ -349,9 +359,11 @@
</p><p>
In its default configuration, the <span class="command"><strong>bind10</strong></span>
master process will also start up
- <span class="command"><strong>b10-cmdctl</strong></span> for admins to communicate with the
- system, <span class="command"><strong>b10-auth</strong></span> for authoritative DNS service,
+ <span class="command"><strong>b10-cmdctl</strong></span> for administration tools to
+ communicate with the system,
+ <span class="command"><strong>b10-auth</strong></span> for authoritative DNS service,
<span class="command"><strong>b10-stats</strong></span> for statistics collection,
+ <span class="command"><strong>b10-stats-httpd</strong></span> for statistics reporting,
<span class="command"><strong>b10-xfrin</strong></span> for inbound DNS zone transfers,
<span class="command"><strong>b10-xfrout</strong></span> for outbound DNS zone transfers,
and <span class="command"><strong>b10-zonemgr</strong></span> for secondary service.
@@ -395,7 +407,7 @@
during startup or shutdown. Unless specified, the component is started
in usual way. This is the list of components that need to be started
in a special way, with the value of special used for them:
- </p><div class="table"><a name="id1168229437268"></a><p class="title"><b>Table 3.1. </b></p><div class="table-contents"><table border="1"><colgroup><col align="left"><col align="left"><col align="left"></colgroup><thead><tr><th align="left">Component</th><th align="left">Special</th><th align="left">Description</th></tr></thead><tbody><tr><td align="left">b10-auth</td><td align="left">auth</td><td align="left">Authoritative server</td></tr><tr><td align="left">b10-resolver</td><td align="left">resolver</td><td align="left">The resolver</td></tr><tr><td align="left">b10-cmdctl</td><td align="left">cmdctl</td><td align="left">The command control (remote control interface)</td></tr></tbody></table></div></div><p><br class="table-break">
+ </p><div class="table"><a name="id1168229437291"></a><p class="title"><b>Table 3.1. </b></p><div class="table-contents"><table border="1"><colgroup><col align="left"><col align="left"><col align="left"></colgroup><thead><tr><th align="left">Component</th><th align="left">Special</th><th align="left">Description</th></tr></thead><tbody><tr><td align="left">b10-auth</td><td align="left">auth</td><td align="left">Authoritative server</td></tr><tr><td align="left">b10-resolver</td><td align="left">resolver</td><td align="left">The resolver</td></tr><tr><td align="left">b10-cmdctl</td><td align="left">cmdctl</td><td align="left">The command control (remote control interface)</td></tr></tbody></table></div></div><p><br class="table-break">
</p><p>
The kind specifies how a failure of the component should
be handled. If it is set to <span class="quote">“<span class="quote">dispensable</span>”</span>
@@ -451,7 +463,23 @@
</p><p>
In short, you should think twice before disabling something here.
- </p></div></div></div><div class="chapter" title="Chapter 4. Command channel"><div class="titlepage"><div><div><h2 class="title"><a name="msgq"></a>Chapter 4. Command channel</h2></div></div></div><p>
+ </p></div><p>
+ It is possible to start some components multiple times (currently
+ <span class="command"><strong>b10-auth</strong></span> and <span class="command"><strong>b10-resolzer</strong></span>).
+ You might want to do that to gain more performance (each one uses only
+ single core). Just put multiple entries under different names, like
+ this, with the same config:
+ </p><pre class="screen">> <strong class="userinput"><code>config add Boss/components b10-resolver-2</code></strong>
+> <strong class="userinput"><code>config set Boss/components/b10-resolver-2/special resolver</code></strong>
+> <strong class="userinput"><code>config set Boss/components/b10-resolver-2/kind needed</code></strong>
+> <strong class="userinput"><code>config commit</code></strong></pre><p>
+ </p><p>
+ However, this is work in progress and the support is not yet complete.
+ For example, each resolver will have its own cache, each authoritative
+ server will keep its own copy of in-memory data and there could be
+ problems with locking the sqlite database, if used. The configuration
+ might be changed to something more convenient in future.
+ </p></div></div><div class="chapter" title="Chapter 4. Command channel"><div class="titlepage"><div><div><h2 class="title"><a name="msgq"></a>Chapter 4. Command channel</h2></div></div></div><p>
The BIND 10 components use the <span class="command"><strong>b10-msgq</strong></span>
message routing daemon to communicate with other BIND 10 components.
The <span class="command"><strong>b10-msgq</strong></span> implements what is called the
@@ -607,12 +635,12 @@ shutdown
the details and relays (over a <span class="command"><strong>b10-msgq</strong></span> command
channel) the configuration on to the specified module.
</p><p>
- </p></div><div class="chapter" title="Chapter 8. Authoritative Server"><div class="titlepage"><div><div><h2 class="title"><a name="authserver"></a>Chapter 8. Authoritative Server</h2></div></div></div><div class="toc"><p><b>Table of Contents</b></p><dl><dt><span class="section"><a href="#id1168229437939">8.1. Server Configurations</a></span></dt><dt><span class="section"><a href="#id1168229438004">8.2. Data Source Backends</a></span></dt><dt><span class="section"><a href="#id1168229438035">8.3. Loading Master Zones Files</a></span></dt></dl></div><p>
+ </p></div><div class="chapter" title="Chapter 8. Authoritative Server"><div class="titlepage"><div><div><h2 class="title"><a name="authserver"></a>Chapter 8. Authoritative Server</h2></div></div></div><div class="toc"><p><b>Table of Contents</b></p><dl><dt><span class="section"><a href="#id1168229437990">8.1. Server Configurations</a></span></dt><dt><span class="section"><a href="#id1168229438055">8.2. Data Source Backends</a></span></dt><dt><span class="section"><a href="#id1168229438085">8.3. Loading Master Zones Files</a></span></dt></dl></div><p>
The <span class="command"><strong>b10-auth</strong></span> is the authoritative DNS server.
It supports EDNS0 and DNSSEC. It supports IPv6.
Normally it is started by the <span class="command"><strong>bind10</strong></span> master
process.
- </p><div class="section" title="8.1. Server Configurations"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="id1168229437939"></a>8.1. Server Configurations</h2></div></div></div><p>
+ </p><div class="section" title="8.1. Server Configurations"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="id1168229437990"></a>8.1. Server Configurations</h2></div></div></div><p>
<span class="command"><strong>b10-auth</strong></span> is configured via the
<span class="command"><strong>b10-cfgmgr</strong></span> configuration manager.
The module name is <span class="quote">“<span class="quote">Auth</span>”</span>.
@@ -632,7 +660,7 @@ This may be a temporary setting until then.
</p><div class="variablelist"><dl><dt><span class="term">shutdown</span></dt><dd>Stop the authoritative DNS server.
</dd></dl></div><p>
- </p></div><div class="section" title="8.2. Data Source Backends"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="id1168229438004"></a>8.2. Data Source Backends</h2></div></div></div><div class="note" title="Note" style="margin-left: 0.5in; margin-right: 0.5in;"><h3 class="title">Note</h3><p>
+ </p></div><div class="section" title="8.2. Data Source Backends"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="id1168229438055"></a>8.2. Data Source Backends</h2></div></div></div><div class="note" title="Note" style="margin-left: 0.5in; margin-right: 0.5in;"><h3 class="title">Note</h3><p>
For the development prototype release, <span class="command"><strong>b10-auth</strong></span>
supports a SQLite3 data source backend and in-memory data source
backend.
@@ -646,7 +674,7 @@ This may be a temporary setting until then.
The default is <code class="filename">/usr/local/var/</code>.)
This data file location may be changed by defining the
<span class="quote">“<span class="quote">database_file</span>”</span> configuration.
- </p></div><div class="section" title="8.3. Loading Master Zones Files"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="id1168229438035"></a>8.3. Loading Master Zones Files</h2></div></div></div><p>
+ </p></div><div class="section" title="8.3. Loading Master Zones Files"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="id1168229438085"></a>8.3. Loading Master Zones Files</h2></div></div></div><p>
RFC 1035 style DNS master zone files may imported
into a BIND 10 data source by using the
<span class="command"><strong>b10-loadzone</strong></span> utility.
@@ -675,7 +703,7 @@ This may be a temporary setting until then.
If you reload a zone already existing in the database,
all records from that prior zone disappear and a whole new set
appears.
- </p></div></div><div class="chapter" title="Chapter 9. Incoming Zone Transfers"><div class="titlepage"><div><div><h2 class="title"><a name="xfrin"></a>Chapter 9. Incoming Zone Transfers</h2></div></div></div><div class="toc"><p><b>Table of Contents</b></p><dl><dt><span class="section"><a href="#id1168229438166">9.1. Configuration for Incoming Zone Transfers</a></span></dt><dt><span class="section"><a href="#id1168229438203">9.2. Enabling IXFR</a></span></dt><dt><span class="section"><a href="#zonemgr">9.3. Secondary Manager</a></span></dt><dt><span class="section"><a href="#id1168229438317">9.4. Trigger an Incoming Zone Transfer Manually</a></span></dt></dl></div><p>
+ </p></div></div><div class="chapter" title="Chapter 9. Incoming Zone Transfers"><div class="titlepage"><div><div><h2 class="title"><a name="xfrin"></a>Chapter 9. Incoming Zone Transfers</h2></div></div></div><div class="toc"><p><b>Table of Contents</b></p><dl><dt><span class="section"><a href="#id1168229438216">9.1. Configuration for Incoming Zone Transfers</a></span></dt><dt><span class="section"><a href="#id1168229438254">9.2. Enabling IXFR</a></span></dt><dt><span class="section"><a href="#zonemgr">9.3. Secondary Manager</a></span></dt><dt><span class="section"><a href="#id1168229438369">9.4. Trigger an Incoming Zone Transfer Manually</a></span></dt></dl></div><p>
Incoming zones are transferred using the <span class="command"><strong>b10-xfrin</strong></span>
process which is started by <span class="command"><strong>bind10</strong></span>.
When received, the zone is stored in the corresponding BIND 10
@@ -693,7 +721,7 @@ This may be a temporary setting until then.
In the current development release of BIND 10, incoming zone
transfers are only available for SQLite3-based data sources,
that is, they don't work for an in-memory data source.
- </p></div><div class="section" title="9.1. Configuration for Incoming Zone Transfers"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="id1168229438166"></a>9.1. Configuration for Incoming Zone Transfers</h2></div></div></div><p>
+ </p></div><div class="section" title="9.1. Configuration for Incoming Zone Transfers"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="id1168229438216"></a>9.1. Configuration for Incoming Zone Transfers</h2></div></div></div><p>
In practice, you need to specify a list of secondary zones to
enable incoming zone transfers for these zones (you can still
trigger a zone transfer manually, without a prior configuration
@@ -709,7 +737,7 @@ This may be a temporary setting until then.
> <strong class="userinput"><code>config commit</code></strong></pre><p>
(We assume there has been no zone configuration before).
- </p></div><div class="section" title="9.2. Enabling IXFR"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="id1168229438203"></a>9.2. Enabling IXFR</h2></div></div></div><p>
+ </p></div><div class="section" title="9.2. Enabling IXFR"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="id1168229438254"></a>9.2. Enabling IXFR</h2></div></div></div><p>
As noted above, <span class="command"><strong>b10-xfrin</strong></span> uses AXFR for
zone transfers by default. To enable IXFR for zone transfers
for a particular zone, set the <strong class="userinput"><code>use_ixfr</code></strong>
@@ -761,7 +789,7 @@ This may be a temporary setting until then.
(i.e. no SOA record for it), <span class="command"><strong>b10-zonemgr</strong></span>
will automatically tell <span class="command"><strong>b10-xfrin</strong></span>
to transfer the zone in.
- </p></div><div class="section" title="9.4. Trigger an Incoming Zone Transfer Manually"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="id1168229438317"></a>9.4. Trigger an Incoming Zone Transfer Manually</h2></div></div></div><p>
+ </p></div><div class="section" title="9.4. Trigger an Incoming Zone Transfer Manually"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="id1168229438369"></a>9.4. Trigger an Incoming Zone Transfer Manually</h2></div></div></div><p>
To manually trigger a zone transfer to retrieve a remote zone,
you may use the <span class="command"><strong>bindctl</strong></span> utility.
For example, at the <span class="command"><strong>bindctl</strong></span> prompt run:
@@ -799,26 +827,18 @@ Xfrout/transfer_acl[0] {"action": "ACCEPT"} any (default)</pre><p>
for <code class="option">transfer_acl</code> were divided for
readability. In the actual input it must be in a single line.
</p></div><p>
- If you want to require TSIG in access control, a separate TSIG
- "key ring" must be configured specifically
- for <span class="command"><strong>b10-xfrout</strong></span> as well as a system wide
- key ring, both containing a consistent set of keys.
+ If you want to require TSIG in access control, a system wide TSIG
+ "key ring" must be configured.
For example, to change the previous example to allowing requests
from 192.0.2.1 signed by a TSIG with a key name of
"key.example", you'll need to do this:
</p><pre class="screen">> <strong class="userinput"><code>config set tsig_keys/keys ["key.example:<base64-key>"]</code></strong>
-> <strong class="userinput"><code>config set Xfrout/tsig_keys/keys ["key.example:<base64-key>"]</code></strong>
> <strong class="userinput"><code>config set Xfrout/zone_config[0]/transfer_acl [{"action": "ACCEPT", "from": "192.0.2.1", "key": "key.example"}]</code></strong>
-> <strong class="userinput"><code>config commit</code></strong></pre><p>
- The first line of configuration defines a system wide key ring.
- This is necessary because the <span class="command"><strong>b10-auth</strong></span> server
- also checks TSIGs and it uses the system wide configuration.
- </p><div class="note" title="Note" style="margin-left: 0.5in; margin-right: 0.5in;"><h3 class="title">Note</h3><p>
- In a future version, <span class="command"><strong>b10-xfrout</strong></span> will also
- use the system wide TSIG configuration.
+> <strong class="userinput"><code>config commit</code></strong></pre><p>Both Xfrout and Auth will use the system wide keyring to check
+ TSIGs in the incomming messages and to sign responses.</p><div class="note" title="Note" style="margin-left: 0.5in; margin-right: 0.5in;"><h3 class="title">Note</h3><p>
The way to specify zone specific configuration (ACLs, etc) is
- likely to be changed, too.
- </p></div></div><div class="chapter" title="Chapter 11. Recursive Name Server"><div class="titlepage"><div><div><h2 class="title"><a name="resolverserver"></a>Chapter 11. Recursive Name Server</h2></div></div></div><div class="toc"><p><b>Table of Contents</b></p><dl><dt><span class="section"><a href="#id1168229438745">11.1. Access Control</a></span></dt><dt><span class="section"><a href="#id1168229438860">11.2. Forwarding</a></span></dt></dl></div><p>
+ likely to be changed.
+ </p></div></div><div class="chapter" title="Chapter 11. Recursive Name Server"><div class="titlepage"><div><div><h2 class="title"><a name="resolverserver"></a>Chapter 11. Recursive Name Server</h2></div></div></div><div class="toc"><p><b>Table of Contents</b></p><dl><dt><span class="section"><a href="#id1168229438715">11.1. Access Control</a></span></dt><dt><span class="section"><a href="#id1168229438900">11.2. Forwarding</a></span></dt></dl></div><p>
The <span class="command"><strong>b10-resolver</strong></span> process is started by
<span class="command"><strong>bind10</strong></span>.
@@ -857,7 +877,7 @@ Xfrout/transfer_acl[0] {"action": "ACCEPT"} any (default)</pre><p>
</pre><p>
</p><p>(Replace the <span class="quote">“<span class="quote"><em class="replaceable"><code>2</code></em></span>”</span>
as needed; run <span class="quote">“<span class="quote"><strong class="userinput"><code>config show
- Resolver/listen_on</code></strong></span>”</span> if needed.)</p><div class="section" title="11.1. Access Control"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="id1168229438745"></a>11.1. Access Control</h2></div></div></div><p>
+ Resolver/listen_on</code></strong></span>”</span> if needed.)</p><div class="section" title="11.1. Access Control"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="id1168229438715"></a>11.1. Access Control</h2></div></div></div><p>
By default, the <span class="command"><strong>b10-resolver</strong></span> daemon only accepts
DNS queries from the localhost (127.0.0.1 and ::1).
The <code class="option">Resolver/query_acl</code> configuration may
@@ -890,7 +910,7 @@ Xfrout/transfer_acl[0] {"action": "ACCEPT"} any (default)</pre><p>
</pre><p>(Replace the <span class="quote">“<span class="quote"><em class="replaceable"><code>2</code></em></span>”</span>
as needed; run <span class="quote">“<span class="quote"><strong class="userinput"><code>config show
Resolver/query_acl</code></strong></span>”</span> if needed.)</p><div class="note" title="Note" style="margin-left: 0.5in; margin-right: 0.5in;"><h3 class="title">Note</h3><p>This prototype access control configuration
- syntax may be changed.</p></div></div><div class="section" title="11.2. Forwarding"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="id1168229438860"></a>11.2. Forwarding</h2></div></div></div><p>
+ syntax may be changed.</p></div></div><div class="section" title="11.2. Forwarding"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="id1168229438900"></a>11.2. Forwarding</h2></div></div></div><p>
To enable forwarding, the upstream address and port must be
configured to forward queries to, such as:
@@ -1202,7 +1222,7 @@ eth0 fe80::21e:8cff:fe9b:7349
}
}
</pre><p>
- </p></div><div class="chapter" title="Chapter 16. Logging"><div class="titlepage"><div><div><h2 class="title"><a name="logging"></a>Chapter 16. Logging</h2></div></div></div><div class="toc"><p><b>Table of Contents</b></p><dl><dt><span class="section"><a href="#id1168229439976">16.1. Logging configuration</a></span></dt><dd><dl><dt><span class="section"><a href="#id1168229439987">16.1.1. Loggers</a></span></dt><dt><span class="section"><a href="#id1168229440229">16.1.2. Output Options</a></span></dt><dt><span class="section"><a href="#id1168229440471">16.1.3. Example session</a></span></dt></dl></dd><dt><span class="section"><a href="#id1168229440680">16.2. Logging Message Format</a></span></dt></dl></div><div class="section" title="16.1. Logging configuration"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="id1168229439976"></a>16.1. Logging configuration</h2></div></div></div><p>
+ </p></div><div class="chapter" title="Chapter 16. Logging"><div class="titlepage"><div><div><h2 class="title"><a name="logging"></a>Chapter 16. Logging</h2></div></div></div><div class="toc"><p><b>Table of Contents</b></p><dl><dt><span class="section"><a href="#id1168229440014">16.1. Logging configuration</a></span></dt><dd><dl><dt><span class="section"><a href="#id1168229440025">16.1.1. Loggers</a></span></dt><dt><span class="section"><a href="#id1168229440268">16.1.2. Output Options</a></span></dt><dt><span class="section"><a href="#id1168229440509">16.1.3. Example session</a></span></dt></dl></dd><dt><span class="section"><a href="#id1168229440720">16.2. Logging Message Format</a></span></dt></dl></div><div class="section" title="16.1. Logging configuration"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="id1168229440014"></a>16.1. Logging configuration</h2></div></div></div><p>
The logging system in BIND 10 is configured through the
Logging module. All BIND 10 modules will look at the
@@ -1211,7 +1231,7 @@ eth0 fe80::21e:8cff:fe9b:7349
- </p><div class="section" title="16.1.1. Loggers"><div class="titlepage"><div><div><h3 class="title"><a name="id1168229439987"></a>16.1.1. Loggers</h3></div></div></div><p>
+ </p><div class="section" title="16.1.1. Loggers"><div class="titlepage"><div><div><h3 class="title"><a name="id1168229440025"></a>16.1.1. Loggers</h3></div></div></div><p>
Within BIND 10, a message is logged through a component
called a "logger". Different parts of BIND 10 log messages
@@ -1232,7 +1252,7 @@ eth0 fe80::21e:8cff:fe9b:7349
(what to log), and the <code class="option">output_options</code>
(where to log).
- </p><div class="section" title="16.1.1.1. name (string)"><div class="titlepage"><div><div><h4 class="title"><a name="id1168229440011"></a>16.1.1.1. name (string)</h4></div></div></div><p>
+ </p><div class="section" title="16.1.1.1. name (string)"><div class="titlepage"><div><div><h4 class="title"><a name="id1168229440051"></a>16.1.1.1. name (string)</h4></div></div></div><p>
Each logger in the system has a name, the name being that
of the component using it to log messages. For instance,
if you want to configure logging for the resolver module,
@@ -1305,7 +1325,7 @@ eth0 fe80::21e:8cff:fe9b:7349
<span class="quote">“<span class="quote">Auth.cache</span>”</span> logger will appear in the output
with a logger name of <span class="quote">“<span class="quote">b10-auth.cache</span>”</span>).
- </p></div><div class="section" title="16.1.1.2. severity (string)"><div class="titlepage"><div><div><h4 class="title"><a name="id1168229440112"></a>16.1.1.2. severity (string)</h4></div></div></div><p>
+ </p></div><div class="section" title="16.1.1.2. severity (string)"><div class="titlepage"><div><div><h4 class="title"><a name="id1168229440150"></a>16.1.1.2. severity (string)</h4></div></div></div><p>
This specifies the category of messages logged.
Each message is logged with an associated severity which
@@ -1321,7 +1341,7 @@ eth0 fe80::21e:8cff:fe9b:7349
- </p></div><div class="section" title="16.1.1.3. output_options (list)"><div class="titlepage"><div><div><h4 class="title"><a name="id1168229440162"></a>16.1.1.3. output_options (list)</h4></div></div></div><p>
+ </p></div><div class="section" title="16.1.1.3. output_options (list)"><div class="titlepage"><div><div><h4 class="title"><a name="id1168229440201"></a>16.1.1.3. output_options (list)</h4></div></div></div><p>
Each logger can have zero or more
<code class="option">output_options</code>. These specify where log
@@ -1331,7 +1351,7 @@ eth0 fe80::21e:8cff:fe9b:7349
The other options for a logger are:
- </p></div><div class="section" title="16.1.1.4. debuglevel (integer)"><div class="titlepage"><div><div><h4 class="title"><a name="id1168229440179"></a>16.1.1.4. debuglevel (integer)</h4></div></div></div><p>
+ </p></div><div class="section" title="16.1.1.4. debuglevel (integer)"><div class="titlepage"><div><div><h4 class="title"><a name="id1168229440217"></a>16.1.1.4. debuglevel (integer)</h4></div></div></div><p>
When a logger's severity is set to DEBUG, this value
specifies what debug messages should be printed. It ranges
@@ -1340,7 +1360,7 @@ eth0 fe80::21e:8cff:fe9b:7349
If severity for the logger is not DEBUG, this value is ignored.
- </p></div><div class="section" title="16.1.1.5. additive (true or false)"><div class="titlepage"><div><div><h4 class="title"><a name="id1168229440194"></a>16.1.1.5. additive (true or false)</h4></div></div></div><p>
+ </p></div><div class="section" title="16.1.1.5. additive (true or false)"><div class="titlepage"><div><div><h4 class="title"><a name="id1168229440232"></a>16.1.1.5. additive (true or false)</h4></div></div></div><p>
If this is true, the <code class="option">output_options</code> from
the parent will be used. For example, if there are two
@@ -1354,18 +1374,18 @@ eth0 fe80::21e:8cff:fe9b:7349
- </p></div></div><div class="section" title="16.1.2. Output Options"><div class="titlepage"><div><div><h3 class="title"><a name="id1168229440229"></a>16.1.2. Output Options</h3></div></div></div><p>
+ </p></div></div><div class="section" title="16.1.2. Output Options"><div class="titlepage"><div><div><h3 class="title"><a name="id1168229440268"></a>16.1.2. Output Options</h3></div></div></div><p>
The main settings for an output option are the
<code class="option">destination</code> and a value called
<code class="option">output</code>, the meaning of which depends on
the destination that is set.
- </p><div class="section" title="16.1.2.1. destination (string)"><div class="titlepage"><div><div><h4 class="title"><a name="id1168229440245"></a>16.1.2.1. destination (string)</h4></div></div></div><p>
+ </p><div class="section" title="16.1.2.1. destination (string)"><div class="titlepage"><div><div><h4 class="title"><a name="id1168229440283"></a>16.1.2.1. destination (string)</h4></div></div></div><p>
The destination is the type of output. It can be one of:
- </p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"> console </li><li class="listitem"> file </li><li class="listitem"> syslog </li></ul></div></div><div class="section" title="16.1.2.2. output (string)"><div class="titlepage"><div><div><h4 class="title"><a name="id1168229440277"></a>16.1.2.2. output (string)</h4></div></div></div><p>
+ </p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"> console </li><li class="listitem"> file </li><li class="listitem"> syslog </li></ul></div></div><div class="section" title="16.1.2.2. output (string)"><div class="titlepage"><div><div><h4 class="title"><a name="id1168229440385"></a>16.1.2.2. output (string)</h4></div></div></div><p>
Depending on what is set as the output destination, this
value is interpreted as follows:
@@ -1387,12 +1407,12 @@ eth0 fe80::21e:8cff:fe9b:7349
The other options for <code class="option">output_options</code> are:
- </p><div class="section" title="16.1.2.2.1. flush (true of false)"><div class="titlepage"><div><div><h5 class="title"><a name="id1168229440361"></a>16.1.2.2.1. flush (true of false)</h5></div></div></div><p>
+ </p><div class="section" title="16.1.2.2.1. flush (true of false)"><div class="titlepage"><div><div><h5 class="title"><a name="id1168229440469"></a>16.1.2.2.1. flush (true of false)</h5></div></div></div><p>
Flush buffers after each log message. Doing this will
reduce performance but will ensure that if the program
terminates abnormally, all messages up to the point of
termination are output.
- </p></div><div class="section" title="16.1.2.2.2. maxsize (integer)"><div class="titlepage"><div><div><h5 class="title"><a name="id1168229440371"></a>16.1.2.2.2. maxsize (integer)</h5></div></div></div><p>
+ </p></div><div class="section" title="16.1.2.2.2. maxsize (integer)"><div class="titlepage"><div><div><h5 class="title"><a name="id1168229440478"></a>16.1.2.2.2. maxsize (integer)</h5></div></div></div><p>
Only relevant when destination is file, this is maximum
file size of output files in bytes. When the maximum
size is reached, the file is renamed and a new file opened.
@@ -1401,11 +1421,11 @@ eth0 fe80::21e:8cff:fe9b:7349
etc.)
</p><p>
If this is 0, no maximum file size is used.
- </p></div><div class="section" title="16.1.2.2.3. maxver (integer)"><div class="titlepage"><div><div><h5 class="title"><a name="id1168229440453"></a>16.1.2.2.3. maxver (integer)</h5></div></div></div><p>
+ </p></div><div class="section" title="16.1.2.2.3. maxver (integer)"><div class="titlepage"><div><div><h5 class="title"><a name="id1168229440491"></a>16.1.2.2.3. maxver (integer)</h5></div></div></div><p>
Maximum number of old log files to keep around when
rolling the output file. Only relevant when
<code class="option">destination</code> is <span class="quote">“<span class="quote">file</span>”</span>.
- </p></div></div></div><div class="section" title="16.1.3. Example session"><div class="titlepage"><div><div><h3 class="title"><a name="id1168229440471"></a>16.1.3. Example session</h3></div></div></div><p>
+ </p></div></div></div><div class="section" title="16.1.3. Example session"><div class="titlepage"><div><div><h3 class="title"><a name="id1168229440509"></a>16.1.3. Example session</h3></div></div></div><p>
In this example we want to set the global logging to
write to the file <code class="filename">/var/log/my_bind10.log</code>,
@@ -1566,7 +1586,7 @@ Logging/loggers[0]/output_options[0]/maxver 8 integer (modified)
And every module will now be using the values from the
logger named <span class="quote">“<span class="quote">*</span>”</span>.
- </p></div></div><div class="section" title="16.2. Logging Message Format"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="id1168229440680"></a>16.2. Logging Message Format</h2></div></div></div><p>
+ </p></div></div><div class="section" title="16.2. Logging Message Format"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="id1168229440720"></a>16.2. Logging Message Format</h2></div></div></div><p>
Each message written by BIND 10 to the configured logging
destinations comprises a number of components that identify
the origin of the message and, if the message indicates
diff --git a/doc/guide/bind10-guide.txt b/doc/guide/bind10-guide.txt
index 7ec35e6..aad5c04 100644
--- a/doc/guide/bind10-guide.txt
+++ b/doc/guide/bind10-guide.txt
@@ -221,18 +221,22 @@ Chapter 1. Introduction
processes as needed. The processes started by the bind10 command have
names starting with "b10-", including:
- o b10-msgq -- Message bus daemon. This process coordinates communication
- between all of the other BIND 10 processes.
o b10-auth -- Authoritative DNS server. This process serves DNS
requests.
o b10-cfgmgr -- Configuration manager. This process maintains all of the
configuration for BIND 10.
o b10-cmdctl -- Command and control service. This process allows
external control of the BIND 10 system.
+ o b10-msgq -- Message bus daemon. This process coordinates communication
+ between all of the other BIND 10 processes.
o b10-resolver -- Recursive name server. This process handles incoming
queries.
+ o b10-sockcreator -- Socket creator daemon. This process creates sockets
+ used by network-listening BIND 10 processes.
o b10-stats -- Statistics collection daemon. This process collects and
reports statistics data.
+ o b10-stats-httpd -- HTTP server for statistics reporting. This process
+ reports statistics data in XML format over HTTP.
o b10-xfrin -- Incoming zone transfer service. This process is used to
transfer a new copy of a zone into BIND 10, when acting as a secondary
server.
@@ -249,8 +253,9 @@ Chapter 1. Introduction
Once BIND 10 is running, a few commands are used to interact directly with
the system:
- o bindctl -- interactive administration interface. This is a
- command-line tool which allows an administrator to control BIND 10.
+ o bindctl -- interactive administration interface. This is a low-level
+ command-line tool which allows a developer or an experienced
+ administrator to control BIND 10.
o b10-loadzone -- zone file loader. This tool will load standard
masterfile-format zone files into BIND 10.
o b10-cmdctl-usermgr -- user access control. This tool allows an
@@ -491,10 +496,11 @@ Chapter 3. Starting BIND10 with bind10
b10-sockcreator will allocate sockets for the rest of the system.
In its default configuration, the bind10 master process will also start up
- b10-cmdctl for admins to communicate with the system, b10-auth for
- authoritative DNS service, b10-stats for statistics collection, b10-xfrin
- for inbound DNS zone transfers, b10-xfrout for outbound DNS zone
- transfers, and b10-zonemgr for secondary service.
+ b10-cmdctl for administration tools to communicate with the system,
+ b10-auth for authoritative DNS service, b10-stats for statistics
+ collection, b10-stats-httpd for statistics reporting, b10-xfrin for
+ inbound DNS zone transfers, b10-xfrout for outbound DNS zone transfers,
+ and b10-zonemgr for secondary service.
3.1. Starting BIND 10
@@ -600,6 +606,22 @@ Chapter 3. Starting BIND10 with bind10
In short, you should think twice before disabling something here.
+ It is possible to start some components multiple times (currently b10-auth
+ and b10-resolzer). You might want to do that to gain more performance
+ (each one uses only single core). Just put multiple entries under
+ different names, like this, with the same config:
+
+ > config add Boss/components b10-resolver-2
+ > config set Boss/components/b10-resolver-2/special resolver
+ > config set Boss/components/b10-resolver-2/kind needed
+ > config commit
+
+ However, this is work in progress and the support is not yet complete. For
+ example, each resolver will have its own cache, each authoritative server
+ will keep its own copy of in-memory data and there could be problems with
+ locking the sqlite database, if used. The configuration might be changed
+ to something more convenient in future.
+
Chapter 4. Command channel
The BIND 10 components use the b10-msgq message routing daemon to
@@ -939,26 +961,22 @@ Chapter 10. Outbound Zone Transfers
In the above example the lines for transfer_acl were divided for
readability. In the actual input it must be in a single line.
- If you want to require TSIG in access control, a separate TSIG "key ring"
- must be configured specifically for b10-xfrout as well as a system wide
- key ring, both containing a consistent set of keys. For example, to change
- the previous example to allowing requests from 192.0.2.1 signed by a TSIG
- with a key name of "key.example", you'll need to do this:
+ If you want to require TSIG in access control, a system wide TSIG "key
+ ring" must be configured. For example, to change the previous example to
+ allowing requests from 192.0.2.1 signed by a TSIG with a key name of
+ "key.example", you'll need to do this:
> config set tsig_keys/keys ["key.example:<base64-key>"]
- > config set Xfrout/tsig_keys/keys ["key.example:<base64-key>"]
> config set Xfrout/zone_config[0]/transfer_acl [{"action": "ACCEPT", "from": "192.0.2.1", "key": "key.example"}]
> config commit
- The first line of configuration defines a system wide key ring. This is
- necessary because the b10-auth server also checks TSIGs and it uses the
- system wide configuration.
+ Both Xfrout and Auth will use the system wide keyring to check TSIGs in
+ the incomming messages and to sign responses.
Note
- In a future version, b10-xfrout will also use the system wide TSIG
- configuration. The way to specify zone specific configuration (ACLs, etc)
- is likely to be changed, too.
+ The way to specify zone specific configuration (ACLs, etc) is likely to be
+ changed.
Chapter 11. Recursive Name Server
diff --git a/doc/guide/bind10-guide.xml b/doc/guide/bind10-guide.xml
index c551c5f..6a68f82 100644
--- a/doc/guide/bind10-guide.xml
+++ b/doc/guide/bind10-guide.xml
@@ -172,15 +172,6 @@
<listitem>
<simpara>
- <command>b10-msgq</command> —
- Message bus daemon.
- This process coordinates communication between all of the other
- BIND 10 processes.
- </simpara>
- </listitem>
-
- <listitem>
- <simpara>
<command>b10-auth</command> —
Authoritative DNS server.
This process serves DNS requests.
@@ -205,6 +196,15 @@
<listitem>
<simpara>
+ <command>b10-msgq</command> —
+ Message bus daemon.
+ This process coordinates communication between all of the other
+ BIND 10 processes.
+ </simpara>
+ </listitem>
+
+ <listitem>
+ <simpara>
<command>b10-resolver</command> —
Recursive name server.
This process handles incoming queries.
@@ -214,6 +214,15 @@
<listitem>
<simpara>
+ <command>b10-sockcreator</command> —
+ Socket creator daemon.
+ This process creates sockets used by
+ network-listening BIND 10 processes.
+ </simpara>
+ </listitem>
+
+ <listitem>
+ <simpara>
<command>b10-stats</command> —
Statistics collection daemon.
This process collects and reports statistics data.
@@ -222,6 +231,14 @@
<listitem>
<simpara>
+ <command>b10-stats-httpd</command> —
+ HTTP server for statistics reporting.
+ This process reports statistics data in XML format over HTTP.
+ </simpara>
+ </listitem>
+
+ <listitem>
+ <simpara>
<command>b10-xfrin</command> —
Incoming zone transfer service.
This process is used to transfer a new copy
@@ -269,8 +286,9 @@
<simpara>
<command>bindctl</command> —
interactive administration interface.
- This is a command-line tool which allows an administrator
- to control BIND 10.
+ This is a low-level command-line tool which allows
+ a developer or an experienced administrator to control
+ BIND 10.
</simpara>
</listitem>
<listitem>
@@ -751,9 +769,11 @@ as a dependency earlier -->
<para>
In its default configuration, the <command>bind10</command>
master process will also start up
- <command>b10-cmdctl</command> for admins to communicate with the
- system, <command>b10-auth</command> for authoritative DNS service,
+ <command>b10-cmdctl</command> for administration tools to
+ communicate with the system,
+ <command>b10-auth</command> for authoritative DNS service,
<command>b10-stats</command> for statistics collection,
+ <command>b10-stats-httpd</command> for statistics reporting,
<command>b10-xfrin</command> for inbound DNS zone transfers,
<command>b10-xfrout</command> for outbound DNS zone transfers,
and <command>b10-zonemgr</command> for secondary service.
@@ -889,7 +909,7 @@ address, but the usual ones don't." mean? -->
This system allows you to start the same component multiple times
(by including it in the configuration with different names, but the
same process setting). However, the rest of the system doesn't expect
- such situation, so it would probably not do what you want. Such
+ such a situation, so it would probably not do what you want. Such
support is yet to be implemented.
</para>
</note>
@@ -901,10 +921,10 @@ address, but the usual ones don't." mean? -->
<command>b10-cmdctl</command>, but then you couldn't
change it back the usual way, as it would require it to
be running (you would have to find and edit the configuration
- directly). Also, some modules might have dependencies
- -- <command>b10-stats-httpd</command> need
+ directly). Also, some modules might have dependencies:
+ <command>b10-stats-httpd</command> needs
<command>b10-stats</command>, <command>b10-xfrout</command>
- needs the <command>b10-auth</command> to be running, etc.
+ needs <command>b10-auth</command> to be running, etc.
<!-- TODO: should we define dependencies? -->
@@ -913,7 +933,24 @@ address, but the usual ones don't." mean? -->
In short, you should think twice before disabling something here.
</para>
</note>
-
+ <para>
+ It is possible to start some components multiple times (currently
+ <command>b10-auth</command> and <command>b10-resolzer</command>).
+ You might want to do that to gain more performance (each one uses only
+ single core). Just put multiple entries under different names, like
+ this, with the same config:
+ <screen>> <userinput>config add Boss/components b10-resolver-2</userinput>
+> <userinput>config set Boss/components/b10-resolver-2/special resolver</userinput>
+> <userinput>config set Boss/components/b10-resolver-2/kind needed</userinput>
+> <userinput>config commit</userinput></screen>
+ </para>
+ <para>
+ However, this is work in progress and the support is not yet complete.
+ For example, each resolver will have its own cache, each authoritative
+ server will keep its own copy of in-memory data and there could be
+ problems with locking the sqlite database, if used. The configuration
+ might be changed to something more convenient in future.
+ </para>
</section>
</chapter>
@@ -982,7 +1019,7 @@ Unix domain sockets
<!-- TODO -->
<note>
<para>
- The development prototype release only provides the
+ The development prototype release only provides
<command>bindctl</command> as a user interface to
<command>b10-cmdctl</command>.
Upcoming releases will provide another interactive command-line
@@ -1173,7 +1210,7 @@ or accounts database -->
The port can be set by using the <option>--port</option> command line option.
The address to listen on can be set using the <option>--address</option> command
line argument.
- Each HTTPS connection is stateless and timesout in 1200 seconds
+ Each HTTPS connection is stateless and times out in 1200 seconds
by default. This can be
redefined by using the <option>--idle-timeout</option> command line argument.
</para>
@@ -1612,31 +1649,23 @@ Xfrout/transfer_acl[0] {"action": "ACCEPT"} any (default)</screen>
</simpara></note>
<para>
- If you want to require TSIG in access control, a separate TSIG
- "key ring" must be configured specifically
- for <command>b10-xfrout</command> as well as a system wide
- key ring, both containing a consistent set of keys.
+ If you want to require TSIG in access control, a system wide TSIG
+ "key ring" must be configured.
For example, to change the previous example to allowing requests
from 192.0.2.1 signed by a TSIG with a key name of
"key.example", you'll need to do this:
</para>
<screen>> <userinput>config set tsig_keys/keys ["key.example:<base64-key>"]</userinput>
-> <userinput>config set Xfrout/tsig_keys/keys ["key.example:<base64-key>"]</userinput>
> <userinput>config set Xfrout/zone_config[0]/transfer_acl [{"action": "ACCEPT", "from": "192.0.2.1", "key": "key.example"}]</userinput>
> <userinput>config commit</userinput></screen>
- <para>
- The first line of configuration defines a system wide key ring.
- This is necessary because the <command>b10-auth</command> server
- also checks TSIGs and it uses the system wide configuration.
- </para>
+ <para>Both Xfrout and Auth will use the system wide keyring to check
+ TSIGs in the incoming messages and to sign responses.</para>
<note><simpara>
- In a future version, <command>b10-xfrout</command> will also
- use the system wide TSIG configuration.
The way to specify zone specific configuration (ACLs, etc) is
- likely to be changed, too.
+ likely to be changed.
</simpara></note>
<!--
@@ -2342,7 +2371,7 @@ eth0 fe80::21e:8cff:fe9b:7349
In the Logging module, you can specify the configuration
for zero or more loggers; any that are not specified will
- take appropriate default values..
+ take appropriate default values.
</para>
diff --git a/doc/guide/bind10-messages.html b/doc/guide/bind10-messages.html
index b82b485..d3bcb7c 100644
--- a/doc/guide/bind10-messages.html
+++ b/doc/guide/bind10-messages.html
@@ -1,10 +1,10 @@
-<html><head><meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"><title>BIND 10 Messages Manual</title><link rel="stylesheet" href="./bind10-guide.css" type="text/css"><meta name="generator" content="DocBook XSL Stylesheets V1.75.2"><meta name="description" content="BIND 10 is a Domain Name System (DNS) suite managed by Internet Systems Consortium (ISC). It includes DNS libraries and modular components for controlling authoritative and recursive DNS servers. This is the messages manual for BIND 10 version 20111129. The most up-to-date version of this document, along with other documents for BIND 10, can be found at ."></head><body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div class="book" title="BIND 10 Messages Manual"><div class="titlepage"><div><div><h1 class="title"><a name="id1168229451102"></a>BIND 10 Messages Manual</h1></div><div><p class="releaseinfo">This is the messages manual for BIND 10 version
- 20111129.</p></div><div><p class="copyright">Copyright © 2011-2012 Internet Systems Consortium, Inc.</p></div><div><div class="abstract" title="Abstract"><p class="title"><b>Abstract</b></p><p>BIND 10 is a Domain Name System (DNS) suite managed by
+<html><head><meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"><title>BIND 10 Messages Manual</title><link rel="stylesheet" href="./bind10-guide.css" type="text/css"><meta name="generator" content="DocBook XSL Stylesheets V1.75.2"><meta name="description" content="BIND 10 is a Domain Name System (DNS) suite managed by Internet Systems Consortium (ISC). It includes DNS libraries and modular components for controlling authoritative and recursive DNS servers. This is the messages manual for BIND 10 version 20120127. The most up-to-date version of this document, along with other documents for BIND 10, can be found at ."></head><body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div class="book" title="BIND 10 Messages Manual"><div class="titlepage"><div><div><h1 class="title"><a name="id1168229451102"></a>BIND 10 Messages Manual</h1></div><div><p class="releaseinfo">This is the messages manual for BIND 10 version
+ 20120127.</p></div><div><p class="copyright">Copyright © 2011-2012 Internet Systems Consortium, Inc.</p></div><div><div class="abstract" title="Abstract"><p class="title"><b>Abstract</b></p><p>BIND 10 is a Domain Name System (DNS) suite managed by
Internet Systems Consortium (ISC). It includes DNS libraries
and modular components for controlling authoritative and
recursive DNS servers.
</p><p>
- This is the messages manual for BIND 10 version 20111129.
+ This is the messages manual for BIND 10 version 20120127.
The most up-to-date version of this document, along with
other documents for BIND 10, can be found at
<a class="ulink" href="http://bind10.isc.org/docs" target="_top">http://bind10.isc.org/docs</a>.
@@ -215,6 +215,9 @@ reason for the failure is included in the message.
</p></dd><dt><a name="AUTH_SERVER_STARTED"></a><span class="term">AUTH_SERVER_STARTED server started</span></dt><dd><p>
Initialization of the authoritative server has completed successfully
and it is entering the main loop, waiting for queries to arrive.
+</p></dd><dt><a name="AUTH_SHUTDOWN"></a><span class="term">AUTH_SHUTDOWN asked to stop, doing so</span></dt><dd><p>
+This is a debug message indicating the server was asked to shut down and it is
+complying to the request.
</p></dd><dt><a name="AUTH_SQLITE3"></a><span class="term">AUTH_SQLITE3 nothing to do for loading sqlite3</span></dt><dd><p>
This is a debug message indicating that the authoritative server has
found that the data source it is loading is an SQLite3 data source,
@@ -754,6 +757,16 @@ but will not send back an answer.
</p><p>
The most likely cause of this error is a programming error. Please raise
a bug report.
+</p></dd><dt><a name="CONFIG_CCSESSION_STOPPING"></a><span class="term">CONFIG_CCSESSION_STOPPING error sending stopping message: %1</span></dt><dd><p>
+There was a problem when sending a message signaling that the module using
+this CCSession is stopping. This message is sent so that the rest of the
+system is aware that the module is no longer running. Apart from logging
+this message, the error itself is ignored, and the ModuleCCSession is
+still stopped. The specific exception message is printed.
+</p></dd><dt><a name="CONFIG_CCSESSION_STOPPING_UNKNOWN"></a><span class="term">CONFIG_CCSESSION_STOPPING_UNKNOWN unknown error sending stopping message</span></dt><dd><p>
+Similar to CONFIG_CCSESSION_STOPPING, but in this case the exception that
+is seen is not a standard exception, and further information is unknown.
+This is a bug.
</p></dd><dt><a name="CONFIG_GET_FAIL"></a><span class="term">CONFIG_GET_FAIL error getting configuration from cfgmgr: %1</span></dt><dd><p>
The configuration manager returned an error when this module requested
the configuration. The full error message answer from the configuration
@@ -806,6 +819,18 @@ manager.
</p></dd><dt><a name="CONFIG_OPEN_FAIL"></a><span class="term">CONFIG_OPEN_FAIL error opening %1: %2</span></dt><dd><p>
There was an error opening the given file. The reason for the failure
is included in the message.
+</p></dd><dt><a name="CONFIG_SESSION_STOPPING_FAILED"></a><span class="term">CONFIG_SESSION_STOPPING_FAILED error sending stopping message: %1</span></dt><dd><p>
+There was a problem when sending a message signaling that the module using
+this CCSession is stopping. This message is sent so that the rest of the
+system is aware that the module is no longer running. Apart from logging
+this message, the error itself is ignored, and the ModuleCCSession is
+still stopped. The specific exception message is printed.
+</p></dd><dt><a name="DATASRC_BAD_NSEC3_NAME"></a><span class="term">DATASRC_BAD_NSEC3_NAME NSEC3 record has a bad owner name '%1'</span></dt><dd><p>
+The software refuses to load NSEC3 records into a wildcard domain or
+the owner name has two or more labels below the zone origin.
+It isn't explicitly forbidden, but no sane zone wouldn have such names
+for NSEC3. BIND 9 also refuses NSEC3 at wildcard, so this behavior is
+compatible with BIND 9.
</p></dd><dt><a name="DATASRC_CACHE_CREATE"></a><span class="term">DATASRC_CACHE_CREATE creating the hotspot cache</span></dt><dd><p>
This is a debug message issued during startup when the hotspot cache
is created.
@@ -935,24 +960,13 @@ name/type/class in the data source.
Debug information. A set of updates to a zone has been successfully
committed to the corresponding database backend. The zone name,
its class and the database name are printed.
-</p></dd><dt><a name="DATASRC_DATABASE_UPDATER_COMMIT%20(1)"></a><span class="term">DATASRC_DATABASE_UPDATER_COMMIT (1) updates committed for '%1/%2' on %3</span></dt><dd><p>
-Debug information. A set of updates to a zone has been successfully
-committed to the corresponding database backend. The zone name,
-its class and the database name are printed.
</p></dd><dt><a name="DATASRC_DATABASE_UPDATER_CREATED"></a><span class="term">DATASRC_DATABASE_UPDATER_CREATED zone updater created for '%1/%2' on %3</span></dt><dd><p>
Debug information. A zone updater object is created to make updates to
the shown zone on the shown backend database.
-</p></dd><dt><a name="DATASRC_DATABASE_UPDATER_CREATED%20(1)"></a><span class="term">DATASRC_DATABASE_UPDATER_CREATED (1) zone updater created for '%1/%2' on %3</span></dt><dd><p>
-Debug information. A zone updater object is created to make updates to
-the shown zone on the shown backend database.
</p></dd><dt><a name="DATASRC_DATABASE_UPDATER_DESTROYED"></a><span class="term">DATASRC_DATABASE_UPDATER_DESTROYED zone updater destroyed for '%1/%2' on %3</span></dt><dd><p>
Debug information. A zone updater object is destroyed, either successfully
or after failure of, making updates to the shown zone on the shown backend
database.
-</p></dd><dt><a name="DATASRC_DATABASE_UPDATER_DESTROYED%20(1)"></a><span class="term">DATASRC_DATABASE_UPDATER_DESTROYED (1) zone updater destroyed for '%1/%2' on %3</span></dt><dd><p>
-Debug information. A zone updater object is destroyed, either successfully
-or after failure of, making updates to the shown zone on the shown backend
-database.
</p></dd><dt><a name="DATASRC_DATABASE_UPDATER_ROLLBACK"></a><span class="term">DATASRC_DATABASE_UPDATER_ROLLBACK zone updates roll-backed for '%1/%2' on %3</span></dt><dd><p>
A zone updater is being destroyed without committing the changes.
This would typically mean the update attempt was aborted due to some
@@ -960,13 +974,6 @@ error, but may also be a bug of the application that forgets committing
the changes. The intermediate changes made through the updater won't
be applied to the underlying database. The zone name, its class, and
the underlying database name are shown in the log message.
-</p></dd><dt><a name="DATASRC_DATABASE_UPDATER_ROLLBACK%20(1)"></a><span class="term">DATASRC_DATABASE_UPDATER_ROLLBACK (1) zone updates roll-backed for '%1/%2' on %3</span></dt><dd><p>
-A zone updater is being destroyed without committing the changes.
-This would typically mean the update attempt was aborted due to some
-error, but may also be a bug of the application that forgets committing
-the changes. The intermediate changes made through the updater won't
-be applied to the underlying database. The zone name, its class, and
-the underlying database name are shown in the log message.
</p></dd><dt><a name="DATASRC_DATABASE_UPDATER_ROLLBACKFAIL"></a><span class="term">DATASRC_DATABASE_UPDATER_ROLLBACKFAIL failed to roll back zone updates for '%1/%2' on %3: %4</span></dt><dd><p>
A zone updater is being destroyed without committing the changes to
the database, and attempts to rollback incomplete updates, but it
@@ -979,18 +986,6 @@ examine the underlying data source to see what exactly happens and
whether the data is still valid. The zone name, its class, and the
underlying database name as well as the error message thrown from the
database module are shown in the log message.
-</p></dd><dt><a name="DATASRC_DATABASE_UPDATER_ROLLBACKFAIL%20(1)"></a><span class="term">DATASRC_DATABASE_UPDATER_ROLLBACKFAIL (1) failed to roll back zone updates for '%1/%2' on %3: %4</span></dt><dd><p>
-A zone updater is being destroyed without committing the changes to
-the database, and attempts to rollback incomplete updates, but it
-unexpectedly fails. The higher level implementation does not expect
-it to fail, so this means either a serious operational error in the
-underlying data source (such as a system failure of a database) or
-software bug in the underlying data source implementation. In either
-case if this message is logged the administrator should carefully
-examine the underlying data source to see what exactly happens and
-whether the data is still valid. The zone name, its class, and the
-underlying database name as well as the error message thrown from the
-database module are shown in the log message.
</p></dd><dt><a name="DATASRC_DATABASE_WILDCARD_ANY"></a><span class="term">DATASRC_DATABASE_WILDCARD_ANY search in datasource %1 resulted in wildcard match type ANY on %2</span></dt><dd><p>
The database doesn't contain directly matching name. When searching
for a wildcard match, a wildcard record matching the name of the query
@@ -1080,6 +1075,26 @@ this zone is not authoritative for the requested domain, but a delegation
should be followed. The requested domain is an apex of some zone.
</p></dd><dt><a name="DATASRC_MEM_FIND"></a><span class="term">DATASRC_MEM_FIND find '%1/%2'</span></dt><dd><p>
Debug information. A search for the requested RRset is being started.
+</p></dd><dt><a name="DATASRC_MEM_FINDNSEC3"></a><span class="term">DATASRC_MEM_FINDNSEC3 finding NSEC3 for %1, mode %2</span></dt><dd><p>
+Debug information. A search in an in-memory data source for NSEC3 that
+matches or covers the given name is being started.
+</p></dd><dt><a name="DATASRC_MEM_FINDNSEC3_COVER"></a><span class="term">DATASRC_MEM_FINDNSEC3_COVER found a covering NSEC3 for %1: %2</span></dt><dd><p>
+Debug information. An NSEC3 that covers the given name is found and
+being returned. The found NSEC3 RRset is also displayed.
+</p></dd><dt><a name="DATASRC_MEM_FINDNSEC3_MATCH"></a><span class="term">DATASRC_MEM_FINDNSEC3_MATCH found a matching NSEC3 for %1 at label count %2: %3</span></dt><dd><p>
+Debug information. An NSEC3 that matches (a possibly superdomain of)
+the given name is found and being returned. When the shown label
+count is smaller than that of the given name, the matching NSEC3 is
+for a superdomain of the given name (see DATASRC_MEM_FINDNSEC3_TRYHASH).
+The found NSEC3 RRset is also displayed.
+</p></dd><dt><a name="DATASRC_MEM_FINDNSEC3_TRYHASH"></a><span class="term">DATASRC_MEM_FINDNSEC3_TRYHASH looking for NSEC3 for %1 at label count %2 (hash %3)</span></dt><dd><p>
+Debug information. In an attempt of finding an NSEC3 for the give name,
+(a possibly superdomain of) the name is hashed and searched for in the
+NSEC3 name space. When the shown label count is smaller than that of the
+shown name, the search tries the superdomain name that share the shown
+(higher) label count of the shown name (e.g., for
+www.example.com. with shown label count of 3, example.com. is being
+tried).
</p></dd><dt><a name="DATASRC_MEM_FIND_ZONE"></a><span class="term">DATASRC_MEM_FIND_ZONE looking for zone '%1'</span></dt><dd><p>
Debug information. A zone object for this zone is being searched for in the
in-memory data source.
@@ -1087,6 +1102,13 @@ in-memory data source.
Debug information. The content of master file is being loaded into the memory.
</p></dd><dt><a name="DATASRC_MEM_NOT_FOUND"></a><span class="term">DATASRC_MEM_NOT_FOUND requested domain '%1' not found</span></dt><dd><p>
Debug information. The requested domain does not exist.
+</p></dd><dt><a name="DATASRC_MEM_NO_NSEC3PARAM"></a><span class="term">DATASRC_MEM_NO_NSEC3PARAM NSEC3PARAM is missing for NSEC3-signed zone %1/%2</span></dt><dd><p>
+The in-memory data source has loaded a zone signed with NSEC3 RRs,
+but it doesn't have a NSEC3PARAM RR at the zone origin. It's likely that
+the zone is somehow broken, but this RR is not necessarily needed for
+handling lookups with NSEC3 in this data source, so it accepts the given
+content of the zone. Nevertheless the administrator should look into
+the integrity of the zone data.
</p></dd><dt><a name="DATASRC_MEM_NS_ENCOUNTERED"></a><span class="term">DATASRC_MEM_NS_ENCOUNTERED encountered a NS</span></dt><dd><p>
Debug information. While searching for the requested domain, a NS was
encountered on the way (a delegation). This may lead to stop of the search.
@@ -1107,10 +1129,12 @@ Some resource types are singletons -- only one is allowed in a domain
(for example CNAME or SOA). This indicates a problem with provided data.
</p></dd><dt><a name="DATASRC_MEM_SUCCESS"></a><span class="term">DATASRC_MEM_SUCCESS query for '%1/%2' successful</span></dt><dd><p>
Debug information. The requested record was found.
-</p></dd><dt><a name="DATASRC_MEM_SUPER_STOP"></a><span class="term">DATASRC_MEM_SUPER_STOP stopped at superdomain '%1', domain '%2' is empty</span></dt><dd><p>
-Debug information. The search stopped at a superdomain of the requested
-domain. The domain is an empty nonterminal, therefore it is treated as NXRRSET
-case (eg. the domain exists, but it doesn't have the requested record type).
+</p></dd><dt><a name="DATASRC_MEM_SUPER_STOP"></a><span class="term">DATASRC_MEM_SUPER_STOP stopped as '%1' is superdomain of a zone node, meaning it's empty</span></dt><dd><p>
+Debug information. The search stopped because the requested domain was
+detected to be a superdomain of some existing node of zone (while there
+was no exact match). This means that the domain is an empty nonterminal,
+therefore it is treated as NXRRSET case (eg. the domain exists, but it
+doesn't have the requested record type).
</p></dd><dt><a name="DATASRC_MEM_SWAP"></a><span class="term">DATASRC_MEM_SWAP swapping contents of two zone representations ('%1' and '%2')</span></dt><dd><p>
Debug information. The contents of two in-memory zones are being exchanged.
This is usual practice to do some manipulation in exception-safe manner -- the
@@ -1247,7 +1271,7 @@ to prove the nonexistence.
The underlying data source failed to answer the query for referral information.
1 means some error, 2 is not implemented. The data source should have logged
the specific error already.
-</p></dd><dt><a name="DATASRC_QUERY_RRSIG"></a><span class="term">DATASRC_QUERY_RRSIG unable to answer RRSIG query</span></dt><dd><p>
+</p></dd><dt><a name="DATASRC_QUERY_RRSIG"></a><span class="term">DATASRC_QUERY_RRSIG unable to answer RRSIG query for %1</span></dt><dd><p>
The server is unable to answer a direct query for RRSIG type, but was asked
to do so.
</p></dd><dt><a name="DATASRC_QUERY_SIMPLE_FAIL"></a><span class="term">DATASRC_QUERY_SIMPLE_FAIL the underlying data source failed with %1</span></dt><dd><p>
@@ -1365,6 +1389,11 @@ data source.
</p></dd><dt><a name="DATASRC_UNEXPECTED_QUERY_STATE"></a><span class="term">DATASRC_UNEXPECTED_QUERY_STATE unexpected query state</span></dt><dd><p>
This indicates a programming error. An internal task of unknown type was
generated.
+</p></dd><dt><a name="DDNS_ACCEPT_FAILURE"></a><span class="term">DDNS_ACCEPT_FAILURE error accepting a connection: %1</span></dt><dd><p>
+There was a low-level error when we tried to accept an incoming connection
+(probably coming from b10-auth). We continue serving on whatever other
+connections we already have, but this connection is dropped. The reason
+is logged.
</p></dd><dt><a name="DDNS_CC_SESSION_ERROR"></a><span class="term">DDNS_CC_SESSION_ERROR error reading from cc channel: %1</span></dt><dd><p>
There was a problem reading from the command and control channel. The
most likely cause is that the msgq process is not running.
@@ -1375,18 +1404,33 @@ configuration manager b10-cfgmgr is not running.
</p></dd><dt><a name="DDNS_CONFIG_ERROR"></a><span class="term">DDNS_CONFIG_ERROR error found in configuration data: %1</span></dt><dd><p>
The ddns process encountered an error when installing the configuration at
startup time. Details of the error are included in the log message.
+</p></dd><dt><a name="DDNS_DROP_CONN"></a><span class="term">DDNS_DROP_CONN dropping connection on file descriptor %1 because of error %2</span></dt><dd><p>
+There was an error on a connection with the b10-auth server (or whatever
+connects to the ddns daemon). This might be OK, for example when the
+authoritative server shuts down, the connection would get closed. It also
+can mean the system is busy and can't keep up or that the other side got
+confused and sent bad data.
</p></dd><dt><a name="DDNS_MODULECC_SESSION_ERROR"></a><span class="term">DDNS_MODULECC_SESSION_ERROR error encountered by configuration/command module: %1</span></dt><dd><p>
There was a problem in the lower level module handling configuration and
control commands. This could happen for various reasons, but the most likely
cause is that the configuration database contains a syntax error and ddns
failed to start at initialization. A detailed error message from the module
will also be displayed.
+</p></dd><dt><a name="DDNS_NEW_CONN"></a><span class="term">DDNS_NEW_CONN new connection on file descriptor %1 from %2</span></dt><dd><p>
+Debug message. We received a connection and we are going to start handling
+requests from it. The file descriptor number and the address where the request
+comes from is logged. The connection is over a unix domain socket and is likely
+coming from a b10-auth process.
</p></dd><dt><a name="DDNS_RECEIVED_SHUTDOWN_COMMAND"></a><span class="term">DDNS_RECEIVED_SHUTDOWN_COMMAND shutdown command received</span></dt><dd><p>
The ddns process received a shutdown command from the command channel
and will now shut down.
</p></dd><dt><a name="DDNS_RUNNING"></a><span class="term">DDNS_RUNNING ddns server is running and listening for updates</span></dt><dd><p>
The ddns process has successfully started and is now ready to receive commands
and updates.
+</p></dd><dt><a name="DDNS_SESSION"></a><span class="term">DDNS_SESSION session arrived on file descriptor %1</span></dt><dd><p>
+A debug message, informing there's some activity on the given file descriptor.
+It will be either a request or the file descriptor will be closed. See
+following log messages to see what of it.
</p></dd><dt><a name="DDNS_SHUTDOWN"></a><span class="term">DDNS_SHUTDOWN ddns server shutting down</span></dt><dd><p>
The ddns process is shutting down. It will no longer listen for new commands
or updates. Any command or update that is being addressed at this moment will
@@ -1659,6 +1703,17 @@ an answer with a different given type and class.
</p><p>
This message indicates an internal error in the NSAS. Please raise a
bug report.
+</p></dd><dt><a name="PYSERVER_COMMON_TSIG_KEYRING_DEINIT"></a><span class="term">PYSERVER_COMMON_TSIG_KEYRING_DEINIT Deinitializing global TSIG keyring</span></dt><dd><p>
+A debug message noting that the global TSIG keyring is being removed from
+memory. Most programs don't do that, they just exit, which is OK.
+</p></dd><dt><a name="PYSERVER_COMMON_TSIG_KEYRING_INIT"></a><span class="term">PYSERVER_COMMON_TSIG_KEYRING_INIT Initializing global TSIG keyring</span></dt><dd><p>
+A debug message noting the TSIG keyring storage is being prepared. It should
+appear at most once in the lifetime of a program. The keyring still needs
+to be loaded from configuration.
+</p></dd><dt><a name="PYSERVER_COMMON_TSIG_KEYRING_UPDATE"></a><span class="term">PYSERVER_COMMON_TSIG_KEYRING_UPDATE Updating global TSIG keyring</span></dt><dd><p>
+A debug message. The TSIG keyring is being (re)loaded from configuration.
+This happens at startup or when the configuration changes. The old keyring
+is removed and new one created with all the keys.
</p></dd><dt><a name="RESLIB_ANSWER"></a><span class="term">RESLIB_ANSWER answer received in response to query for <%1></span></dt><dd><p>
A debug message reporting that an answer has been received to an upstream
query for the specified question. Previous debug messages will have
@@ -2009,6 +2064,9 @@ resolver. It is output during startup and may appear multiple times,
once for each root server address.
</p></dd><dt><a name="RESOLVER_SHUTDOWN"></a><span class="term">RESOLVER_SHUTDOWN resolver shutdown complete</span></dt><dd><p>
This informational message is output when the resolver has shut down.
+</p></dd><dt><a name="RESOLVER_SHUTDOWN%20(1)"></a><span class="term">RESOLVER_SHUTDOWN (1) asked to shut down, doing so</span></dt><dd><p>
+A debug message noting that the server was asked to terminate and is
+complying to the request.
</p></dd><dt><a name="RESOLVER_STARTED"></a><span class="term">RESOLVER_STARTED resolver started</span></dt><dd><p>
This informational message is output by the resolver when all initialization
has been completed and it is entering its main loop.
@@ -2022,7 +2080,7 @@ has been ignored.
This is debug message output when the resolver received a message with an
unsupported opcode (it can only process QUERY opcodes). It will return
a message to the sender with the RCODE set to NOTIMP.
-</p></dd><dt><a name="SOCKETREQUESTOR_CREATED"></a><span class="term">SOCKETREQUESTOR_CREATED Socket requestor created</span></dt><dd><p>
+</p></dd><dt><a name="SOCKETREQUESTOR_CREATED"></a><span class="term">SOCKETREQUESTOR_CREATED Socket requestor created for application %1</span></dt><dd><p>
Debug message. A socket requesor (client of the socket creator) is created
for the corresponding application. Normally this should happen at most
one time throughout the lifetime of the application.
@@ -2074,6 +2132,16 @@ Debug message. This lists one address and port value of the set of
addresses we are going to listen on (eg. there will be one log message
per pair). This appears only after SRVCOMM_SET_LISTEN, but might
be hidden, as it has higher debug level.
+</p></dd><dt><a name="SRVCOMM_EXCEPTION_ALLOC"></a><span class="term">SRVCOMM_EXCEPTION_ALLOC exception when allocating a socket: %1</span></dt><dd><p>
+The process tried to allocate a socket using the socket creator, but an error
+occurred. But it is not one of the errors we are sure are "safe". In this case
+it is unclear if the unsuccessful communication left the process and the bind10
+process in inconsistent state, so the process is going to abort to prevent
+further problems in that area.
+</p><p>
+This is probably a bug in the code, but it could be caused by other unusual
+conditions (like insufficient memory, deleted socket file used for
+communication).
</p></dd><dt><a name="SRVCOMM_KEYS_DEINIT"></a><span class="term">SRVCOMM_KEYS_DEINIT deinitializing TSIG keyring</span></dt><dd><p>
Debug message indicating that the server is deinitializing the TSIG keyring.
</p></dd><dt><a name="SRVCOMM_KEYS_INIT"></a><span class="term">SRVCOMM_KEYS_INIT initializing TSIG keyring</span></dt><dd><p>
@@ -2088,6 +2156,10 @@ specification is outside the valid range of 0 to 65535.
</p></dd><dt><a name="SRVCOMM_SET_LISTEN"></a><span class="term">SRVCOMM_SET_LISTEN setting addresses to listen to</span></dt><dd><p>
Debug message, noting that the server is about to start listening on a
different set of IP addresses and ports than before.
+</p></dd><dt><a name="SRVCOMM_UNKNOWN_EXCEPTION_ALLOC"></a><span class="term">SRVCOMM_UNKNOWN_EXCEPTION_ALLOC unknown exception when allocating a socket</span></dt><dd><p>
+The situation is the same as in the SRVCOMM_EXCEPTION_ALLOC case, but further
+details about the error are unknown, because it was signaled by throwing
+something not being an exception. This is definitely a bug.
</p></dd><dt><a name="STATHTTPD_BAD_OPTION_VALUE"></a><span class="term">STATHTTPD_BAD_OPTION_VALUE bad command line argument: %1</span></dt><dd><p>
The stats-httpd module was called with a bad command-line argument
and will not start.
diff --git a/doc/guide/bind10-messages.xml b/doc/guide/bind10-messages.xml
index c085cb1..b0cbb26 100644
--- a/doc/guide/bind10-messages.xml
+++ b/doc/guide/bind10-messages.xml
@@ -467,6 +467,14 @@ and it is entering the main loop, waiting for queries to arrive.
</para></listitem>
</varlistentry>
+<varlistentry id="AUTH_SHUTDOWN">
+<term>AUTH_SHUTDOWN asked to stop, doing so</term>
+<listitem><para>
+This is a debug message indicating the server was asked to shut down and it is
+complying to the request.
+</para></listitem>
+</varlistentry>
+
<varlistentry id="AUTH_SQLITE3">
<term>AUTH_SQLITE3 nothing to do for loading sqlite3</term>
<listitem><para>
@@ -590,7 +598,7 @@ needs a dedicated message bus.
</varlistentry>
<varlistentry id="BIND10_COMPONENT_FAILED">
-<term>BIND10_COMPONENT_FAILED component %1 (pid %2) failed with %3 exit status</term>
+<term>BIND10_COMPONENT_FAILED component %1 (pid %2) failed: %3</term>
<listitem><para>
The process terminated, but the bind10 boss didn't expect it to, which means
it must have failed.
@@ -1766,6 +1774,26 @@ a bug report.
</para></listitem>
</varlistentry>
+<varlistentry id="CONFIG_CCSESSION_STOPPING">
+<term>CONFIG_CCSESSION_STOPPING error sending stopping message: %1</term>
+<listitem><para>
+There was a problem when sending a message signaling that the module using
+this CCSession is stopping. This message is sent so that the rest of the
+system is aware that the module is no longer running. Apart from logging
+this message, the error itself is ignored, and the ModuleCCSession is
+still stopped. The specific exception message is printed.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="CONFIG_CCSESSION_STOPPING_UNKNOWN">
+<term>CONFIG_CCSESSION_STOPPING_UNKNOWN unknown error sending stopping message</term>
+<listitem><para>
+Similar to CONFIG_CCSESSION_STOPPING, but in this case the exception that
+is seen is not a standard exception, and further information is unknown.
+This is a bug.
+</para></listitem>
+</varlistentry>
+
<varlistentry id="CONFIG_GET_FAIL">
<term>CONFIG_GET_FAIL error getting configuration from cfgmgr: %1</term>
<listitem><para>
@@ -1873,6 +1901,28 @@ is included in the message.
</para></listitem>
</varlistentry>
+<varlistentry id="CONFIG_SESSION_STOPPING_FAILED">
+<term>CONFIG_SESSION_STOPPING_FAILED error sending stopping message: %1</term>
+<listitem><para>
+There was a problem when sending a message signaling that the module using
+this CCSession is stopping. This message is sent so that the rest of the
+system is aware that the module is no longer running. Apart from logging
+this message, the error itself is ignored, and the ModuleCCSession is
+still stopped. The specific exception message is printed.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="DATASRC_BAD_NSEC3_NAME">
+<term>DATASRC_BAD_NSEC3_NAME NSEC3 record has a bad owner name '%1'</term>
+<listitem><para>
+The software refuses to load NSEC3 records into a wildcard domain or
+the owner name has two or more labels below the zone origin.
+It isn't explicitly forbidden, but no sane zone wouldn have such names
+for NSEC3. BIND 9 also refuses NSEC3 at wildcard, so this behavior is
+compatible with BIND 9.
+</para></listitem>
+</varlistentry>
+
<varlistentry id="DATASRC_CACHE_CREATE">
<term>DATASRC_CACHE_CREATE creating the hotspot cache</term>
<listitem><para>
@@ -2177,15 +2227,6 @@ its class and the database name are printed.
</para></listitem>
</varlistentry>
-<varlistentry id="DATASRC_DATABASE_UPDATER_COMMIT (1)">
-<term>DATASRC_DATABASE_UPDATER_COMMIT (1) updates committed for '%1/%2' on %3</term>
-<listitem><para>
-Debug information. A set of updates to a zone has been successfully
-committed to the corresponding database backend. The zone name,
-its class and the database name are printed.
-</para></listitem>
-</varlistentry>
-
<varlistentry id="DATASRC_DATABASE_UPDATER_CREATED">
<term>DATASRC_DATABASE_UPDATER_CREATED zone updater created for '%1/%2' on %3</term>
<listitem><para>
@@ -2194,14 +2235,6 @@ the shown zone on the shown backend database.
</para></listitem>
</varlistentry>
-<varlistentry id="DATASRC_DATABASE_UPDATER_CREATED (1)">
-<term>DATASRC_DATABASE_UPDATER_CREATED (1) zone updater created for '%1/%2' on %3</term>
-<listitem><para>
-Debug information. A zone updater object is created to make updates to
-the shown zone on the shown backend database.
-</para></listitem>
-</varlistentry>
-
<varlistentry id="DATASRC_DATABASE_UPDATER_DESTROYED">
<term>DATASRC_DATABASE_UPDATER_DESTROYED zone updater destroyed for '%1/%2' on %3</term>
<listitem><para>
@@ -2211,15 +2244,6 @@ database.
</para></listitem>
</varlistentry>
-<varlistentry id="DATASRC_DATABASE_UPDATER_DESTROYED (1)">
-<term>DATASRC_DATABASE_UPDATER_DESTROYED (1) zone updater destroyed for '%1/%2' on %3</term>
-<listitem><para>
-Debug information. A zone updater object is destroyed, either successfully
-or after failure of, making updates to the shown zone on the shown backend
-database.
-</para></listitem>
-</varlistentry>
-
<varlistentry id="DATASRC_DATABASE_UPDATER_ROLLBACK">
<term>DATASRC_DATABASE_UPDATER_ROLLBACK zone updates roll-backed for '%1/%2' on %3</term>
<listitem><para>
@@ -2232,18 +2256,6 @@ the underlying database name are shown in the log message.
</para></listitem>
</varlistentry>
-<varlistentry id="DATASRC_DATABASE_UPDATER_ROLLBACK (1)">
-<term>DATASRC_DATABASE_UPDATER_ROLLBACK (1) zone updates roll-backed for '%1/%2' on %3</term>
-<listitem><para>
-A zone updater is being destroyed without committing the changes.
-This would typically mean the update attempt was aborted due to some
-error, but may also be a bug of the application that forgets committing
-the changes. The intermediate changes made through the updater won't
-be applied to the underlying database. The zone name, its class, and
-the underlying database name are shown in the log message.
-</para></listitem>
-</varlistentry>
-
<varlistentry id="DATASRC_DATABASE_UPDATER_ROLLBACKFAIL">
<term>DATASRC_DATABASE_UPDATER_ROLLBACKFAIL failed to roll back zone updates for '%1/%2' on %3: %4</term>
<listitem><para>
@@ -2261,23 +2273,6 @@ database module are shown in the log message.
</para></listitem>
</varlistentry>
-<varlistentry id="DATASRC_DATABASE_UPDATER_ROLLBACKFAIL (1)">
-<term>DATASRC_DATABASE_UPDATER_ROLLBACKFAIL (1) failed to roll back zone updates for '%1/%2' on %3: %4</term>
-<listitem><para>
-A zone updater is being destroyed without committing the changes to
-the database, and attempts to rollback incomplete updates, but it
-unexpectedly fails. The higher level implementation does not expect
-it to fail, so this means either a serious operational error in the
-underlying data source (such as a system failure of a database) or
-software bug in the underlying data source implementation. In either
-case if this message is logged the administrator should carefully
-examine the underlying data source to see what exactly happens and
-whether the data is still valid. The zone name, its class, and the
-underlying database name as well as the error message thrown from the
-database module are shown in the log message.
-</para></listitem>
-</varlistentry>
-
<varlistentry id="DATASRC_DATABASE_WILDCARD_ANY">
<term>DATASRC_DATABASE_WILDCARD_ANY search in datasource %1 resulted in wildcard match type ANY on %2</term>
<listitem><para>
@@ -2497,6 +2492,46 @@ Debug information. A search for the requested RRset is being started.
</para></listitem>
</varlistentry>
+<varlistentry id="DATASRC_MEM_FINDNSEC3">
+<term>DATASRC_MEM_FINDNSEC3 finding NSEC3 for %1, mode %2</term>
+<listitem><para>
+Debug information. A search in an in-memory data source for NSEC3 that
+matches or covers the given name is being started.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="DATASRC_MEM_FINDNSEC3_COVER">
+<term>DATASRC_MEM_FINDNSEC3_COVER found a covering NSEC3 for %1: %2</term>
+<listitem><para>
+Debug information. An NSEC3 that covers the given name is found and
+being returned. The found NSEC3 RRset is also displayed.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="DATASRC_MEM_FINDNSEC3_MATCH">
+<term>DATASRC_MEM_FINDNSEC3_MATCH found a matching NSEC3 for %1 at label count %2: %3</term>
+<listitem><para>
+Debug information. An NSEC3 that matches (a possibly superdomain of)
+the given name is found and being returned. When the shown label
+count is smaller than that of the given name, the matching NSEC3 is
+for a superdomain of the given name (see DATASRC_MEM_FINDNSEC3_TRYHASH).
+The found NSEC3 RRset is also displayed.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="DATASRC_MEM_FINDNSEC3_TRYHASH">
+<term>DATASRC_MEM_FINDNSEC3_TRYHASH looking for NSEC3 for %1 at label count %2 (hash %3)</term>
+<listitem><para>
+Debug information. In an attempt of finding an NSEC3 for the give name,
+(a possibly superdomain of) the name is hashed and searched for in the
+NSEC3 name space. When the shown label count is smaller than that of the
+shown name, the search tries the superdomain name that share the shown
+(higher) label count of the shown name (e.g., for
+www.example.com. with shown label count of 3, example.com. is being
+tried).
+</para></listitem>
+</varlistentry>
+
<varlistentry id="DATASRC_MEM_FIND_ZONE">
<term>DATASRC_MEM_FIND_ZONE looking for zone '%1'</term>
<listitem><para>
@@ -2519,6 +2554,18 @@ Debug information. The requested domain does not exist.
</para></listitem>
</varlistentry>
+<varlistentry id="DATASRC_MEM_NO_NSEC3PARAM">
+<term>DATASRC_MEM_NO_NSEC3PARAM NSEC3PARAM is missing for NSEC3-signed zone %1/%2</term>
+<listitem><para>
+The in-memory data source has loaded a zone signed with NSEC3 RRs,
+but it doesn't have a NSEC3PARAM RR at the zone origin. It's likely that
+the zone is somehow broken, but this RR is not necessarily needed for
+handling lookups with NSEC3 in this data source, so it accepts the given
+content of the zone. Nevertheless the administrator should look into
+the integrity of the zone data.
+</para></listitem>
+</varlistentry>
+
<varlistentry id="DATASRC_MEM_NS_ENCOUNTERED">
<term>DATASRC_MEM_NS_ENCOUNTERED encountered a NS</term>
<listitem><para>
@@ -2570,11 +2617,13 @@ Debug information. The requested record was found.
</varlistentry>
<varlistentry id="DATASRC_MEM_SUPER_STOP">
-<term>DATASRC_MEM_SUPER_STOP stopped at superdomain '%1', domain '%2' is empty</term>
+<term>DATASRC_MEM_SUPER_STOP stopped as '%1' is superdomain of a zone node, meaning it's empty</term>
<listitem><para>
-Debug information. The search stopped at a superdomain of the requested
-domain. The domain is an empty nonterminal, therefore it is treated as NXRRSET
-case (eg. the domain exists, but it doesn't have the requested record type).
+Debug information. The search stopped because the requested domain was
+detected to be a superdomain of some existing node of zone (while there
+was no exact match). This means that the domain is an empty nonterminal,
+therefore it is treated as NXRRSET case (eg. the domain exists, but it
+doesn't have the requested record type).
</para></listitem>
</varlistentry>
@@ -2925,7 +2974,7 @@ the specific error already.
</varlistentry>
<varlistentry id="DATASRC_QUERY_RRSIG">
-<term>DATASRC_QUERY_RRSIG unable to answer RRSIG query</term>
+<term>DATASRC_QUERY_RRSIG unable to answer RRSIG query for %1</term>
<listitem><para>
The server is unable to answer a direct query for RRSIG type, but was asked
to do so.
@@ -3232,6 +3281,16 @@ generated.
</para></listitem>
</varlistentry>
+<varlistentry id="DDNS_ACCEPT_FAILURE">
+<term>DDNS_ACCEPT_FAILURE error accepting a connection: %1</term>
+<listitem><para>
+There was a low-level error when we tried to accept an incoming connection
+(probably coming from b10-auth). We continue serving on whatever other
+connections we already have, but this connection is dropped. The reason
+is logged.
+</para></listitem>
+</varlistentry>
+
<varlistentry id="DDNS_CC_SESSION_ERROR">
<term>DDNS_CC_SESSION_ERROR error reading from cc channel: %1</term>
<listitem><para>
@@ -3257,6 +3316,17 @@ startup time. Details of the error are included in the log message.
</para></listitem>
</varlistentry>
+<varlistentry id="DDNS_DROP_CONN">
+<term>DDNS_DROP_CONN dropping connection on file descriptor %1 because of error %2</term>
+<listitem><para>
+There was an error on a connection with the b10-auth server (or whatever
+connects to the ddns daemon). This might be OK, for example when the
+authoritative server shuts down, the connection would get closed. It also
+can mean the system is busy and can't keep up or that the other side got
+confused and sent bad data.
+</para></listitem>
+</varlistentry>
+
<varlistentry id="DDNS_MODULECC_SESSION_ERROR">
<term>DDNS_MODULECC_SESSION_ERROR error encountered by configuration/command module: %1</term>
<listitem><para>
@@ -3268,6 +3338,16 @@ will also be displayed.
</para></listitem>
</varlistentry>
+<varlistentry id="DDNS_NEW_CONN">
+<term>DDNS_NEW_CONN new connection on file descriptor %1 from %2</term>
+<listitem><para>
+Debug message. We received a connection and we are going to start handling
+requests from it. The file descriptor number and the address where the request
+comes from is logged. The connection is over a unix domain socket and is likely
+coming from a b10-auth process.
+</para></listitem>
+</varlistentry>
+
<varlistentry id="DDNS_RECEIVED_SHUTDOWN_COMMAND">
<term>DDNS_RECEIVED_SHUTDOWN_COMMAND shutdown command received</term>
<listitem><para>
@@ -3284,6 +3364,15 @@ and updates.
</para></listitem>
</varlistentry>
+<varlistentry id="DDNS_SESSION">
+<term>DDNS_SESSION session arrived on file descriptor %1</term>
+<listitem><para>
+A debug message, informing there's some activity on the given file descriptor.
+It will be either a request or the file descriptor will be closed. See
+following log messages to see what of it.
+</para></listitem>
+</varlistentry>
+
<varlistentry id="DDNS_SHUTDOWN">
<term>DDNS_SHUTDOWN ddns server shutting down</term>
<listitem><para>
@@ -3826,6 +3915,32 @@ bug report.
</para></listitem>
</varlistentry>
+<varlistentry id="PYSERVER_COMMON_TSIG_KEYRING_DEINIT">
+<term>PYSERVER_COMMON_TSIG_KEYRING_DEINIT Deinitializing global TSIG keyring</term>
+<listitem><para>
+A debug message noting that the global TSIG keyring is being removed from
+memory. Most programs don't do that, they just exit, which is OK.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="PYSERVER_COMMON_TSIG_KEYRING_INIT">
+<term>PYSERVER_COMMON_TSIG_KEYRING_INIT Initializing global TSIG keyring</term>
+<listitem><para>
+A debug message noting the TSIG keyring storage is being prepared. It should
+appear at most once in the lifetime of a program. The keyring still needs
+to be loaded from configuration.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="PYSERVER_COMMON_TSIG_KEYRING_UPDATE">
+<term>PYSERVER_COMMON_TSIG_KEYRING_UPDATE Updating global TSIG keyring</term>
+<listitem><para>
+A debug message. The TSIG keyring is being (re)loaded from configuration.
+This happens at startup or when the configuration changes. The old keyring
+is removed and new one created with all the keys.
+</para></listitem>
+</varlistentry>
+
<varlistentry id="RESLIB_ANSWER">
<term>RESLIB_ANSWER answer received in response to query for <%1></term>
<listitem><para>
@@ -4571,6 +4686,14 @@ This informational message is output when the resolver has shut down.
</para></listitem>
</varlistentry>
+<varlistentry id="RESOLVER_SHUTDOWN (1)">
+<term>RESOLVER_SHUTDOWN (1) asked to shut down, doing so</term>
+<listitem><para>
+A debug message noting that the server was asked to terminate and is
+complying to the request.
+</para></listitem>
+</varlistentry>
+
<varlistentry id="RESOLVER_STARTED">
<term>RESOLVER_STARTED resolver started</term>
<listitem><para>
@@ -4605,7 +4728,7 @@ a message to the sender with the RCODE set to NOTIMP.
</varlistentry>
<varlistentry id="SOCKETREQUESTOR_CREATED">
-<term>SOCKETREQUESTOR_CREATED Socket requestor created</term>
+<term>SOCKETREQUESTOR_CREATED Socket requestor created for application %1</term>
<listitem><para>
Debug message. A socket requesor (client of the socket creator) is created
for the corresponding application. Normally this should happen at most
@@ -4706,6 +4829,21 @@ be hidden, as it has higher debug level.
</para></listitem>
</varlistentry>
+<varlistentry id="SRVCOMM_EXCEPTION_ALLOC">
+<term>SRVCOMM_EXCEPTION_ALLOC exception when allocating a socket: %1</term>
+<listitem><para>
+The process tried to allocate a socket using the socket creator, but an error
+occurred. But it is not one of the errors we are sure are "safe". In this case
+it is unclear if the unsuccessful communication left the process and the bind10
+process in inconsistent state, so the process is going to abort to prevent
+further problems in that area.
+</para><para>
+This is probably a bug in the code, but it could be caused by other unusual
+conditions (like insufficient memory, deleted socket file used for
+communication).
+</para></listitem>
+</varlistentry>
+
<varlistentry id="SRVCOMM_KEYS_DEINIT">
<term>SRVCOMM_KEYS_DEINIT deinitializing TSIG keyring</term>
<listitem><para>
@@ -4745,6 +4883,15 @@ different set of IP addresses and ports than before.
</para></listitem>
</varlistentry>
+<varlistentry id="SRVCOMM_UNKNOWN_EXCEPTION_ALLOC">
+<term>SRVCOMM_UNKNOWN_EXCEPTION_ALLOC unknown exception when allocating a socket</term>
+<listitem><para>
+The situation is the same as in the SRVCOMM_EXCEPTION_ALLOC case, but further
+details about the error are unknown, because it was signaled by throwing
+something not being an exception. This is definitely a bug.
+</para></listitem>
+</varlistentry>
+
<varlistentry id="STATHTTPD_BAD_OPTION_VALUE">
<term>STATHTTPD_BAD_OPTION_VALUE bad command line argument: %1</term>
<listitem><para>
diff --git a/ext/asio/asio/detail/impl/kqueue_reactor.ipp b/ext/asio/asio/detail/impl/kqueue_reactor.ipp
index 8db77cb..00ebfeb 100644
--- a/ext/asio/asio/detail/impl/kqueue_reactor.ipp
+++ b/ext/asio/asio/detail/impl/kqueue_reactor.ipp
@@ -301,12 +301,14 @@ void kqueue_reactor::run(bool block, op_queue<operation>& ops)
EV_ADD | EV_ONESHOT, EV_OOBAND, 0, descriptor_data);
else
continue;
+ break;
case EVFILT_WRITE:
if (!descriptor_data->op_queue_[write_op].empty())
ASIO_KQUEUE_EV_SET(&event, descriptor, EVFILT_WRITE,
EV_ADD | EV_ONESHOT, 0, 0, descriptor_data);
else
continue;
+ break;
default:
break;
}
diff --git a/ext/asio/asio/detail/impl/socket_ops.ipp b/ext/asio/asio/detail/impl/socket_ops.ipp
index 66006ea..a1b0ec6 100644
--- a/ext/asio/asio/detail/impl/socket_ops.ipp
+++ b/ext/asio/asio/detail/impl/socket_ops.ipp
@@ -282,15 +282,26 @@ int close(socket_type s, state_type& state,
int result = 0;
if (s != invalid_socket)
{
-#if defined(BOOST_WINDOWS) || defined(__CYGWIN__)
- if ((state & non_blocking) && (state & user_set_linger))
+ if (destruction && (state & user_set_linger))
{
- ioctl_arg_type arg = 0;
- ::ioctlsocket(s, FIONBIO, &arg);
- state &= ~non_blocking;
+ ::linger opt;
+ opt.l_onoff = 0;
+ opt.l_linger = 0;
+ asio::error_code ignored_ec;
+ socket_ops::setsockopt(s, state, SOL_SOCKET,
+ SO_LINGER, &opt, sizeof(opt), ignored_ec);
}
+
+ clear_last_error();
+#if defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+ result = error_wrapper(::closesocket(s), ec);
#else // defined(BOOST_WINDOWS) || defined(__CYGWIN__)
- if (state & non_blocking)
+ result = error_wrapper(::close(s), ec);
+#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+
+ if (result != 0
+ && (ec == asio::error::would_block
+ || ec == asio::error::try_again))
{
#if defined(__SYMBIAN32__)
int flags = ::fcntl(s, F_GETFL, 0);
@@ -301,18 +312,6 @@ int close(socket_type s, state_type& state,
::ioctl(s, FIONBIO, &arg);
#endif // defined(__SYMBIAN32__)
state &= ~non_blocking;
- }
-#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__)
-
- if (destruction && (state & user_set_linger))
- {
- ::linger opt;
- opt.l_onoff = 0;
- opt.l_linger = 0;
- asio::error_code ignored_ec;
- socket_ops::setsockopt(s, state, SOL_SOCKET,
- SO_LINGER, &opt, sizeof(opt), ignored_ec);
- }
clear_last_error();
#if defined(BOOST_WINDOWS) || defined(__CYGWIN__)
@@ -320,6 +319,7 @@ int close(socket_type s, state_type& state,
#else // defined(BOOST_WINDOWS) || defined(__CYGWIN__)
result = error_wrapper(::close(s), ec);
#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+ }
}
if (result == 0)
diff --git a/src/bin/auth/.gitignore b/src/bin/auth/.gitignore
new file mode 100644
index 0000000..64c3fd7
--- /dev/null
+++ b/src/bin/auth/.gitignore
@@ -0,0 +1,7 @@
+/auth.spec
+/auth.spec.pre
+/auth_messages.cc
+/auth_messages.h
+/b10-auth
+/spec_config.h
+/spec_config.h.pre
diff --git a/src/bin/auth/auth.spec.pre.in b/src/bin/auth/auth.spec.pre.in
index 693e813..97b0e79 100644
--- a/src/bin/auth/auth.spec.pre.in
+++ b/src/bin/auth/auth.spec.pre.in
@@ -97,7 +97,13 @@
{
"command_name": "shutdown",
"command_description": "Shut down authoritative DNS server",
- "command_args": []
+ "command_args": [
+ {
+ "item_name": "pid",
+ "item_type": "integer",
+ "item_optional": true
+ }
+ ]
},
{
"command_name": "sendstats",
@@ -210,7 +216,7 @@
"item_optional": true,
"item_default": 0,
"item_title": "Received requests opcode 8",
- "item_description": "The number of total request counts whose opcode is8 (reserved)"
+ "item_description": "The number of total request counts whose opcode is 8 (reserved)"
},
{
"item_name": "opcode.reserved9",
@@ -218,7 +224,7 @@
"item_optional": true,
"item_default": 0,
"item_title": "Received requests opcode 9",
- "item_description": "The number of total request counts whose opcode is9 (reserved)"
+ "item_description": "The number of total request counts whose opcode is 9 (reserved)"
},
{
"item_name": "opcode.reserved10",
@@ -226,7 +232,7 @@
"item_optional": true,
"item_default": 0,
"item_title": "Received requests opcode 10",
- "item_description": "The number of total request counts whose opcode is10 (reserved)"
+ "item_description": "The number of total request counts whose opcode is 10 (reserved)"
},
{
"item_name": "opcode.reserved11",
@@ -234,7 +240,7 @@
"item_optional": true,
"item_default": 0,
"item_title": "Received requests opcode 11",
- "item_description": "The number of total request counts whose opcode is11 (reserved)"
+ "item_description": "The number of total request counts whose opcode is 11 (reserved)"
},
{
"item_name": "opcode.reserved12",
@@ -242,7 +248,7 @@
"item_optional": true,
"item_default": 0,
"item_title": "Received requests opcode 12",
- "item_description": "The number of total request counts whose opcode is12 (reserved)"
+ "item_description": "The number of total request counts whose opcode is 12 (reserved)"
},
{
"item_name": "opcode.reserved13",
@@ -250,7 +256,7 @@
"item_optional": true,
"item_default": 0,
"item_title": "Received requests opcode 13",
- "item_description": "The number of total request counts whose opcode is13 (reserved)"
+ "item_description": "The number of total request counts whose opcode is 13 (reserved)"
},
{
"item_name": "opcode.reserved14",
@@ -258,7 +264,7 @@
"item_optional": true,
"item_default": 0,
"item_title": "Received requests opcode 14",
- "item_description": "The number of total request counts whose opcode is14 (reserved)"
+ "item_description": "The number of total request counts whose opcode is 14 (reserved)"
},
{
"item_name": "opcode.reserved15",
@@ -266,7 +272,143 @@
"item_optional": true,
"item_default": 0,
"item_title": "Received requests opcode 15",
- "item_description": "The number of total request counts whose opcode is15 (reserved)"
+ "item_description": "The number of total request counts whose opcode is 15 (reserved)"
+ },
+ {
+ "item_name": "rcode.noerror",
+ "item_type": "integer",
+ "item_optional": true,
+ "item_default": 0,
+ "item_title": "Sent success response",
+ "item_description": "The number of total responses with rcode 0 (NOERROR)"
+ },
+ {
+ "item_name": "rcode.formerr",
+ "item_type": "integer",
+ "item_optional": true,
+ "item_default": 0,
+ "item_title": "Sent 'format error' response",
+ "item_description": "The number of total responses with rcode 1 (FORMERR)"
+ },
+ {
+ "item_name": "rcode.servfail",
+ "item_type": "integer",
+ "item_optional": true,
+ "item_default": 0,
+ "item_title": "Sent 'server failure' response",
+ "item_description": "The number of total responses with rcode 2 (SERVFAIL)"
+ },
+ {
+ "item_name": "rcode.nxdomain",
+ "item_type": "integer",
+ "item_optional": true,
+ "item_default": 0,
+ "item_title": "Sent 'name error' response",
+ "item_description": "The number of total responses with rcode 3 (NXDOMAIN)"
+ },
+ {
+ "item_name": "rcode.notimp",
+ "item_type": "integer",
+ "item_optional": true,
+ "item_default": 0,
+ "item_title": "Sent 'not implemented' response",
+ "item_description": "The number of total responses with rcode 4 (NOTIMP)"
+ },
+ {
+ "item_name": "rcode.refused",
+ "item_type": "integer",
+ "item_optional": true,
+ "item_default": 0,
+ "item_title": "Sent 'refused' response",
+ "item_description": "The number of total responses with rcode 5 (REFUSED)"
+ },
+ {
+ "item_name": "rcode.yxdomain",
+ "item_type": "integer",
+ "item_optional": true,
+ "item_default": 0,
+ "item_title": "Sent 'name unexpectedly exists' response",
+ "item_description": "The number of total responses with rcode 6 (YXDOMAIN)"
+ },
+ {
+ "item_name": "rcode.yxrrset",
+ "item_type": "integer",
+ "item_optional": true,
+ "item_default": 0,
+ "item_title": "Sent 'rrset unexpectedly exists' response",
+ "item_description": "The number of total responses with rcode 7 (YXRRSET)"
+ },
+ {
+ "item_name": "rcode.nxrrset",
+ "item_type": "integer",
+ "item_optional": true,
+ "item_default": 0,
+ "item_title": "Sent 'no such rrset' response",
+ "item_description": "The number of total responses with rcode 8 (NXRRSET)"
+ },
+ {
+ "item_name": "rcode.notauth",
+ "item_type": "integer",
+ "item_optional": true,
+ "item_default": 0,
+ "item_title": "Sent 'not authoritative' response",
+ "item_description": "The number of total responses with rcode 9 (NOTAUTH)"
+ },
+ {
+ "item_name": "rcode.notzone",
+ "item_type": "integer",
+ "item_optional": true,
+ "item_default": 0,
+ "item_title": "Sent 'name not in zone' response",
+ "item_description": "The number of total responses with rcode 10 (NOTZONE)"
+ },
+ {
+ "item_name": "rcode.reserved11",
+ "item_type": "integer",
+ "item_optional": true,
+ "item_default": 0,
+ "item_title": "Sent response with rcode 11",
+ "item_description": "The number of total responses with rcode 11 (reserved)"
+ },
+ {
+ "item_name": "rcode.reserved12",
+ "item_type": "integer",
+ "item_optional": true,
+ "item_default": 0,
+ "item_title": "Sent response with rcode 12",
+ "item_description": "The number of total responses with rcode 12 (reserved)"
+ },
+ {
+ "item_name": "rcode.reserved13",
+ "item_type": "integer",
+ "item_optional": true,
+ "item_default": 0,
+ "item_title": "Sent response with rcode 13",
+ "item_description": "The number of total responses with rcode 13 (reserved)"
+ },
+ {
+ "item_name": "rcode.reserved14",
+ "item_type": "integer",
+ "item_optional": true,
+ "item_default": 0,
+ "item_title": "Sent response with rcode 14",
+ "item_description": "The number of total responses with rcode 14 (reserved)"
+ },
+ {
+ "item_name": "rcode.reserved15",
+ "item_type": "integer",
+ "item_optional": true,
+ "item_default": 0,
+ "item_title": "Sent response with rcode 15",
+ "item_description": "The number of total responses with rcode 15 (reserved)"
+ },
+ {
+ "item_name": "rcode.badvers",
+ "item_type": "integer",
+ "item_optional": true,
+ "item_default": 0,
+ "item_title": "Sent 'EDNS version not implemented' response",
+ "item_description": "The number of total responses with rcode 16 (BADVERS)"
}
]
}
diff --git a/src/bin/auth/auth_log.h b/src/bin/auth/auth_log.h
index e0cae0f..33d4432 100644
--- a/src/bin/auth/auth_log.h
+++ b/src/bin/auth/auth_log.h
@@ -29,6 +29,8 @@ namespace auth {
// Debug messages indicating normal startup are logged at this debug level.
const int DBG_AUTH_START = DBGLVL_START_SHUT;
+// Debug messages upon shutdown
+const int DBG_AUTH_SHUT = DBGLVL_START_SHUT;
// Debug level used to log setting information (such as configuration changes).
const int DBG_AUTH_OPS = DBGLVL_COMMAND;
diff --git a/src/bin/auth/auth_messages.mes b/src/bin/auth/auth_messages.mes
index 4706690..b18feb1 100644
--- a/src/bin/auth/auth_messages.mes
+++ b/src/bin/auth/auth_messages.mes
@@ -73,6 +73,10 @@ attempt to parse the header of a received DNS packet has failed. (The
reason for the failure is given in the message.) The server will drop the
packet.
+% AUTH_INVALID_STATISTICS_DATA invalid specification of statistics data specified
+An error was encountered when the authoritiative server specified
+statistics data which is invalid for the auth specification file.
+
% AUTH_LOAD_TSIG loading TSIG keys
This is a debug message indicating that the authoritative server
has requested the keyring holding TSIG keys from the configuration
@@ -92,6 +96,18 @@ discovered that the memory data source is disabled for the given class.
This is a debug message reporting that the authoritative server has
discovered that the memory data source is enabled for the given class.
+% AUTH_NOTIFY_QUESTIONS invalid number of questions (%1) in incoming NOTIFY
+This debug message is logged by the authoritative server when it receives
+a NOTIFY packet that contains zero or more than one question. (A valid
+NOTIFY packet contains one question.) The server will return a FORMERR
+error to the sender.
+
+% AUTH_NOTIFY_RRTYPE invalid question RR type (%1) in incoming NOTIFY
+This debug message is logged by the authoritative server when it receives
+a NOTIFY packet that an RR type of something other than SOA in the
+question section. (The RR type received is included in the message.) The
+server will return a FORMERR error to the sender.
+
% AUTH_NO_STATS_SESSION session interface for statistics is not available
The authoritative server had no session with the statistics module at the
time it attempted to send it data: the attempt has been abandoned. This
@@ -102,18 +118,6 @@ This is a debug message produced by the authoritative server when it receives
a NOTIFY packet but the XFRIN process is not running. The packet will be
dropped and nothing returned to the sender.
-% AUTH_NOTIFY_RRTYPE invalid question RR type (%1) in incoming NOTIFY
-This debug message is logged by the authoritative server when it receives
-a NOTIFY packet that an RR type of something other than SOA in the
-question section. (The RR type received is included in the message.) The
-server will return a FORMERR error to the sender.
-
-% AUTH_NOTIFY_QUESTIONS invalid number of questions (%1) in incoming NOTIFY
-This debug message is logged by the authoritative server when it receives
-a NOTIFY packet that contains zero or more than one question. (A valid
-NOTIFY packet contains one question.) The server will return a FORMERR
-error to the sender.
-
% AUTH_PACKET_PARSE_ERROR unable to parse received DNS packet: %1
This is a debug message, generated by the authoritative server when an
attempt to parse a received DNS packet has failed due to something other
@@ -154,6 +158,19 @@ a command from the statistics module to send it data. The 'sendstats'
command is handled differently to other commands, which is why the debug
message associated with it has its own code.
+% AUTH_RESPONSE_FAILURE exception while building response to query: %1
+This is a debug message, generated by the authoritative server when an
+attempt to create a response to a received DNS packet has failed. The
+reason for the failure is given in the log message. A SERVFAIL response
+is sent back. The most likely cause of this is an error in the data
+source implementation; it is either creating bad responses or raising
+exceptions itself.
+
+% AUTH_RESPONSE_FAILURE_UNKNOWN unknown exception while building response to query
+This debug message is similar to AUTH_RESPONSE_FAILURE, but further
+details about the error are unknown, because it was signaled by something
+which is not an exception. This is definitely a bug.
+
% AUTH_RESPONSE_RECEIVED received response message, ignoring
This is a debug message, this is output if the authoritative server
receives a DNS packet with the QR bit set, i.e. a DNS response. The
@@ -192,6 +209,10 @@ reason for the failure is included in the message.
Initialization of the authoritative server has completed successfully
and it is entering the main loop, waiting for queries to arrive.
+% AUTH_SHUTDOWN asked to stop, doing so
+This is a debug message indicating the server was asked to shut down and it is
+complying to the request.
+
% AUTH_SQLITE3 nothing to do for loading sqlite3
This is a debug message indicating that the authoritative server has
found that the data source it is loading is an SQLite3 data source,
@@ -256,7 +277,3 @@ This is a debug message output during the processing of a NOTIFY
request. The zone manager component has been informed of the request,
but has returned an error response (which is included in the message). The
NOTIFY request will not be honored.
-
-% AUTH_INVALID_STATISTICS_DATA invalid specification of statistics data specified
-An error was encountered when the authoritiative server specified
-statistics data which is invalid for the auth specification file.
diff --git a/src/bin/auth/auth_srv.cc b/src/bin/auth/auth_srv.cc
index 5a68898..478253f 100644
--- a/src/bin/auth/auth_srv.cc
+++ b/src/bin/auth/auth_srv.cc
@@ -88,14 +88,14 @@ public:
~AuthSrvImpl();
isc::data::ConstElementPtr setDbFile(isc::data::ConstElementPtr config);
- bool processNormalQuery(const IOMessage& io_message, MessagePtr message,
- OutputBufferPtr buffer,
+ bool processNormalQuery(const IOMessage& io_message, Message& message,
+ OutputBuffer& buffer,
auto_ptr<TSIGContext> tsig_context);
- bool processXfrQuery(const IOMessage& io_message, MessagePtr message,
- OutputBufferPtr buffer,
+ bool processXfrQuery(const IOMessage& io_message, Message& message,
+ OutputBuffer& buffer,
auto_ptr<TSIGContext> tsig_context);
- bool processNotify(const IOMessage& io_message, MessagePtr message,
- OutputBufferPtr buffer,
+ bool processNotify(const IOMessage& io_message, Message& message,
+ OutputBuffer& buffer,
auto_ptr<TSIGContext> tsig_context);
IOService io_service_;
@@ -129,6 +129,22 @@ public:
/// Bind the ModuleSpec object in config_session_ with
/// isc:config::ModuleSpec::validateStatistics.
void registerStatisticsValidator();
+
+ /// \brief Resume the server
+ ///
+ /// This is a wrapper call for DNSServer::resume(done), if 'done' is true,
+ /// the Rcode set in the given Message is counted in the statistics
+ /// counter.
+ ///
+ /// This method is expected to be called by processMessage()
+ ///
+ /// \param server The DNSServer as passed to processMessage()
+ /// \param message The response as constructed by processMessage()
+ /// \param done If true, the Rcode from the given message is counted,
+ /// this value is then passed to server->resume(bool)
+ void resumeServer(isc::asiodns::DNSServer* server,
+ isc::dns::Message& message,
+ bool done);
private:
std::string db_file_;
@@ -185,12 +201,11 @@ public:
MessageLookup(AuthSrv* srv) : server_(srv) {}
virtual void operator()(const IOMessage& io_message,
MessagePtr message,
- MessagePtr answer_message,
+ MessagePtr, // Not used here
OutputBufferPtr buffer,
DNSServer* server) const
{
- (void) answer_message;
- server_->processMessage(io_message, message, buffer, server);
+ server_->processMessage(io_message, *message, *buffer, server);
}
private:
AuthSrv* server_;
@@ -251,55 +266,57 @@ AuthSrv::~AuthSrv() {
namespace {
class QuestionInserter {
public:
- QuestionInserter(MessagePtr message) : message_(message) {}
+ QuestionInserter(Message& message) : message_(message) {}
void operator()(const QuestionPtr question) {
- message_->addQuestion(question);
+ message_.addQuestion(question);
}
- MessagePtr message_;
+ Message& message_;
};
void
-makeErrorMessage(MessagePtr message, OutputBufferPtr buffer,
- const Rcode& rcode,
+makeErrorMessage(Message& message, OutputBuffer& buffer,
+ const Rcode& rcode,
std::auto_ptr<TSIGContext> tsig_context =
std::auto_ptr<TSIGContext>())
{
// extract the parameters that should be kept.
// XXX: with the current implementation, it's not easy to set EDNS0
// depending on whether the query had it. So we'll simply omit it.
- const qid_t qid = message->getQid();
- const bool rd = message->getHeaderFlag(Message::HEADERFLAG_RD);
- const bool cd = message->getHeaderFlag(Message::HEADERFLAG_CD);
- const Opcode& opcode = message->getOpcode();
+ const qid_t qid = message.getQid();
+ const bool rd = message.getHeaderFlag(Message::HEADERFLAG_RD);
+ const bool cd = message.getHeaderFlag(Message::HEADERFLAG_CD);
+ const Opcode& opcode = message.getOpcode();
vector<QuestionPtr> questions;
// If this is an error to a query or notify, we should also copy the
// question section.
if (opcode == Opcode::QUERY() || opcode == Opcode::NOTIFY()) {
- questions.assign(message->beginQuestion(), message->endQuestion());
+ questions.assign(message.beginQuestion(), message.endQuestion());
}
- message->clear(Message::RENDER);
- message->setQid(qid);
- message->setOpcode(opcode);
- message->setHeaderFlag(Message::HEADERFLAG_QR);
+ message.clear(Message::RENDER);
+ message.setQid(qid);
+ message.setOpcode(opcode);
+ message.setHeaderFlag(Message::HEADERFLAG_QR);
if (rd) {
- message->setHeaderFlag(Message::HEADERFLAG_RD);
+ message.setHeaderFlag(Message::HEADERFLAG_RD);
}
if (cd) {
- message->setHeaderFlag(Message::HEADERFLAG_CD);
+ message.setHeaderFlag(Message::HEADERFLAG_CD);
}
for_each(questions.begin(), questions.end(), QuestionInserter(message));
- message->setRcode(rcode);
+ message.setRcode(rcode);
- MessageRenderer renderer(*buffer);
+ MessageRenderer renderer;
+ renderer.setBuffer(&buffer);
if (tsig_context.get() != NULL) {
- message->toWire(renderer, *tsig_context);
+ message.toWire(renderer, *tsig_context);
} else {
- message->toWire(renderer);
+ message.toWire(renderer);
}
+ renderer.setBuffer(NULL);
LOG_DEBUG(auth_logger, DBG_AUTH_MESSAGES, AUTH_SEND_ERROR_RESPONSE)
- .arg(renderer.getLength()).arg(*message);
+ .arg(renderer.getLength()).arg(message);
}
}
@@ -397,54 +414,54 @@ AuthSrv::setStatisticsTimerInterval(uint32_t interval) {
}
void
-AuthSrv::processMessage(const IOMessage& io_message, MessagePtr message,
- OutputBufferPtr buffer, DNSServer* server)
+AuthSrv::processMessage(const IOMessage& io_message, Message& message,
+ OutputBuffer& buffer, DNSServer* server)
{
InputBuffer request_buffer(io_message.getData(), io_message.getDataSize());
// First, check the header part. If we fail even for the base header,
// just drop the message.
try {
- message->parseHeader(request_buffer);
+ message.parseHeader(request_buffer);
// Ignore all responses.
- if (message->getHeaderFlag(Message::HEADERFLAG_QR)) {
+ if (message.getHeaderFlag(Message::HEADERFLAG_QR)) {
LOG_DEBUG(auth_logger, DBG_AUTH_DETAIL, AUTH_RESPONSE_RECEIVED);
- server->resume(false);
+ impl_->resumeServer(server, message, false);
return;
}
} catch (const Exception& ex) {
LOG_DEBUG(auth_logger, DBG_AUTH_DETAIL, AUTH_HEADER_PARSE_FAIL)
.arg(ex.what());
- server->resume(false);
+ impl_->resumeServer(server, message, false);
return;
}
try {
// Parse the message.
- message->fromWire(request_buffer);
+ message.fromWire(request_buffer);
} catch (const DNSProtocolError& error) {
LOG_DEBUG(auth_logger, DBG_AUTH_DETAIL, AUTH_PACKET_PROTOCOL_ERROR)
.arg(error.getRcode().toText()).arg(error.what());
makeErrorMessage(message, buffer, error.getRcode());
- server->resume(true);
+ impl_->resumeServer(server, message, true);
return;
} catch (const Exception& ex) {
LOG_DEBUG(auth_logger, DBG_AUTH_DETAIL, AUTH_PACKET_PARSE_ERROR)
.arg(ex.what());
makeErrorMessage(message, buffer, Rcode::SERVFAIL());
- server->resume(true);
+ impl_->resumeServer(server, message, true);
return;
} // other exceptions will be handled at a higher layer.
LOG_DEBUG(auth_logger, DBG_AUTH_MESSAGES, AUTH_PACKET_RECEIVED)
- .arg(message->toText());
+ .arg(message);
// Perform further protocol-level validation.
// TSIG first
// If this is set to something, we know we need to answer with TSIG as well
std::auto_ptr<TSIGContext> tsig_context;
- const TSIGRecord* tsig_record(message->getTSIGRecord());
+ const TSIGRecord* tsig_record(message.getTSIGRecord());
TSIGError tsig_error(TSIGError::NOERROR());
// Do we do TSIG?
@@ -460,55 +477,63 @@ AuthSrv::processMessage(const IOMessage& io_message, MessagePtr message,
if (tsig_error != TSIGError::NOERROR()) {
makeErrorMessage(message, buffer, tsig_error.toRcode(), tsig_context);
- server->resume(true);
+ impl_->resumeServer(server, message, true);
return;
}
- // update per opcode statistics counter. This can only be reliable after
- // TSIG check succeeds.
- impl_->counters_.inc(message->getOpcode());
-
bool send_answer = true;
- if (message->getOpcode() == Opcode::NOTIFY()) {
- send_answer = impl_->processNotify(io_message, message, buffer,
- tsig_context);
- } else if (message->getOpcode() != Opcode::QUERY()) {
- LOG_DEBUG(auth_logger, DBG_AUTH_DETAIL, AUTH_UNSUPPORTED_OPCODE)
- .arg(message->getOpcode().toText());
- makeErrorMessage(message, buffer, Rcode::NOTIMP(), tsig_context);
- } else if (message->getRRCount(Message::SECTION_QUESTION) != 1) {
- makeErrorMessage(message, buffer, Rcode::FORMERR(), tsig_context);
- } else {
- ConstQuestionPtr question = *message->beginQuestion();
- const RRType &qtype = question->getType();
- if (qtype == RRType::AXFR()) {
- send_answer = impl_->processXfrQuery(io_message, message, buffer,
- tsig_context);
- } else if (qtype == RRType::IXFR()) {
- send_answer = impl_->processXfrQuery(io_message, message, buffer,
- tsig_context);
+ try {
+ // update per opcode statistics counter. This can only be reliable
+ // after TSIG check succeeds.
+ impl_->counters_.inc(message.getOpcode());
+
+ if (message.getOpcode() == Opcode::NOTIFY()) {
+ send_answer = impl_->processNotify(io_message, message, buffer,
+ tsig_context);
+ } else if (message.getOpcode() != Opcode::QUERY()) {
+ LOG_DEBUG(auth_logger, DBG_AUTH_DETAIL, AUTH_UNSUPPORTED_OPCODE)
+ .arg(message.getOpcode().toText());
+ makeErrorMessage(message, buffer, Rcode::NOTIMP(), tsig_context);
+ } else if (message.getRRCount(Message::SECTION_QUESTION) != 1) {
+ makeErrorMessage(message, buffer, Rcode::FORMERR(), tsig_context);
} else {
- send_answer = impl_->processNormalQuery(io_message, message,
- buffer, tsig_context);
+ ConstQuestionPtr question = *message.beginQuestion();
+ const RRType &qtype = question->getType();
+ if (qtype == RRType::AXFR()) {
+ send_answer = impl_->processXfrQuery(io_message, message,
+ buffer, tsig_context);
+ } else if (qtype == RRType::IXFR()) {
+ send_answer = impl_->processXfrQuery(io_message, message,
+ buffer, tsig_context);
+ } else {
+ send_answer = impl_->processNormalQuery(io_message, message,
+ buffer, tsig_context);
+ }
}
+ } catch (const std::exception& ex) {
+ LOG_DEBUG(auth_logger, DBG_AUTH_DETAIL, AUTH_RESPONSE_FAILURE)
+ .arg(ex.what());
+ makeErrorMessage(message, buffer, Rcode::SERVFAIL());
+ } catch (...) {
+ LOG_DEBUG(auth_logger, DBG_AUTH_DETAIL, AUTH_RESPONSE_FAILURE_UNKNOWN);
+ makeErrorMessage(message, buffer, Rcode::SERVFAIL());
}
-
- server->resume(send_answer);
+ impl_->resumeServer(server, message, send_answer);
}
bool
-AuthSrvImpl::processNormalQuery(const IOMessage& io_message, MessagePtr message,
- OutputBufferPtr buffer,
+AuthSrvImpl::processNormalQuery(const IOMessage& io_message, Message& message,
+ OutputBuffer& buffer,
auto_ptr<TSIGContext> tsig_context)
{
- ConstEDNSPtr remote_edns = message->getEDNS();
+ ConstEDNSPtr remote_edns = message.getEDNS();
const bool dnssec_ok = remote_edns && remote_edns->getDNSSECAwareness();
const uint16_t remote_bufsize = remote_edns ? remote_edns->getUDPSize() :
Message::DEFAULT_MAX_UDPSIZE;
- message->makeResponse();
- message->setHeaderFlag(Message::HEADERFLAG_AA);
- message->setRcode(Rcode::NOERROR());
+ message.makeResponse();
+ message.setHeaderFlag(Message::HEADERFLAG_AA);
+ message.setRcode(Rcode::NOERROR());
// Increment query counter.
incCounter(io_message.getSocket().getProtocol());
@@ -517,19 +542,20 @@ AuthSrvImpl::processNormalQuery(const IOMessage& io_message, MessagePtr message,
EDNSPtr local_edns = EDNSPtr(new EDNS());
local_edns->setDNSSECAwareness(dnssec_ok);
local_edns->setUDPSize(AuthSrvImpl::DEFAULT_LOCAL_UDPSIZE);
- message->setEDNS(local_edns);
+ message.setEDNS(local_edns);
}
try {
// If a memory data source is configured call the separate
// Query::process()
- const ConstQuestionPtr question = *message->beginQuestion();
+ const ConstQuestionPtr question = *message.beginQuestion();
if (memory_client_ && memory_client_class_ == question->getClass()) {
const RRType& qtype = question->getType();
const Name& qname = question->getName();
- auth::Query(*memory_client_, qname, qtype, *message).process();
+ auth::Query(*memory_client_, qname, qtype, message,
+ dnssec_ok).process();
} else {
- datasrc::Query query(*message, cache_, dnssec_ok);
+ datasrc::Query query(message, cache_, dnssec_ok);
data_sources_.doQuery(query);
}
} catch (const Exception& ex) {
@@ -538,24 +564,26 @@ AuthSrvImpl::processNormalQuery(const IOMessage& io_message, MessagePtr message,
return (true);
}
- MessageRenderer renderer(*buffer);
+ MessageRenderer renderer;
+ renderer.setBuffer(&buffer);
const bool udp_buffer =
(io_message.getSocket().getProtocol() == IPPROTO_UDP);
renderer.setLengthLimit(udp_buffer ? remote_bufsize : 65535);
if (tsig_context.get() != NULL) {
- message->toWire(renderer, *tsig_context);
+ message.toWire(renderer, *tsig_context);
} else {
- message->toWire(renderer);
+ message.toWire(renderer);
}
+ renderer.setBuffer(NULL);
LOG_DEBUG(auth_logger, DBG_AUTH_MESSAGES, AUTH_SEND_NORMAL_RESPONSE)
- .arg(renderer.getLength()).arg(message->toText());
+ .arg(renderer.getLength()).arg(message);
return (true);
}
bool
-AuthSrvImpl::processXfrQuery(const IOMessage& io_message, MessagePtr message,
- OutputBufferPtr buffer,
+AuthSrvImpl::processXfrQuery(const IOMessage& io_message, Message& message,
+ OutputBuffer& buffer,
auto_ptr<TSIGContext> tsig_context)
{
// Increment query counter.
@@ -596,19 +624,19 @@ AuthSrvImpl::processXfrQuery(const IOMessage& io_message, MessagePtr message,
}
bool
-AuthSrvImpl::processNotify(const IOMessage& io_message, MessagePtr message,
- OutputBufferPtr buffer,
+AuthSrvImpl::processNotify(const IOMessage& io_message, Message& message,
+ OutputBuffer& buffer,
std::auto_ptr<TSIGContext> tsig_context)
{
// The incoming notify must contain exactly one question for SOA of the
// zone name.
- if (message->getRRCount(Message::SECTION_QUESTION) != 1) {
+ if (message.getRRCount(Message::SECTION_QUESTION) != 1) {
LOG_DEBUG(auth_logger, DBG_AUTH_DETAIL, AUTH_NOTIFY_QUESTIONS)
- .arg(message->getRRCount(Message::SECTION_QUESTION));
+ .arg(message.getRRCount(Message::SECTION_QUESTION));
makeErrorMessage(message, buffer, Rcode::FORMERR(), tsig_context);
return (true);
}
- ConstQuestionPtr question = *message->beginQuestion();
+ ConstQuestionPtr question = *message.beginQuestion();
if (question->getType() != RRType::SOA()) {
LOG_DEBUG(auth_logger, DBG_AUTH_DETAIL, AUTH_NOTIFY_RRTYPE)
.arg(question->getType().toText());
@@ -663,16 +691,18 @@ AuthSrvImpl::processNotify(const IOMessage& io_message, MessagePtr message,
return (false);
}
- message->makeResponse();
- message->setHeaderFlag(Message::HEADERFLAG_AA);
- message->setRcode(Rcode::NOERROR());
+ message.makeResponse();
+ message.setHeaderFlag(Message::HEADERFLAG_AA);
+ message.setRcode(Rcode::NOERROR());
- MessageRenderer renderer(*buffer);
+ MessageRenderer renderer;
+ renderer.setBuffer(&buffer);
if (tsig_context.get() != NULL) {
- message->toWire(renderer, *tsig_context);
+ message.toWire(renderer, *tsig_context);
} else {
- message->toWire(renderer);
+ message.toWire(renderer);
}
+ renderer.setBuffer(NULL);
return (true);
}
@@ -755,6 +785,14 @@ AuthSrvImpl::setDbFile(ConstElementPtr config) {
return (answer);
}
+void
+AuthSrvImpl::resumeServer(DNSServer* server, Message& message, bool done) {
+ if (done) {
+ counters_.inc(message.getRcode());
+ }
+ server->resume(done);
+}
+
ConstElementPtr
AuthSrv::updateConfig(ConstElementPtr new_config) {
try {
@@ -784,6 +822,11 @@ AuthSrv::getCounter(const Opcode opcode) const {
return (impl_->counters_.getCounter(opcode));
}
+uint64_t
+AuthSrv::getCounter(const Rcode rcode) const {
+ return (impl_->counters_.getCounter(rcode));
+}
+
const AddressList&
AuthSrv::getListenAddresses() const {
return (impl_->listen_addresses_);
diff --git a/src/bin/auth/auth_srv.h b/src/bin/auth/auth_srv.h
index ad61fd8..02a578d 100644
--- a/src/bin/auth/auth_srv.h
+++ b/src/bin/auth/auth_srv.h
@@ -115,14 +115,14 @@ public:
/// send the reply.
///
/// \param io_message The raw message received
- /// \param message Pointer to the \c Message object
- /// \param buffer Pointer to an \c OutputBuffer for the resposne
+ /// \param message the \c Message object
+ /// \param buffer an \c OutputBuffer for the resposne
/// \param server Pointer to the \c DNSServer
///
/// \throw isc::Unexpected Protocol type of \a message is unexpected
void processMessage(const isc::asiolink::IOMessage& io_message,
- isc::dns::MessagePtr message,
- isc::util::OutputBufferPtr buffer,
+ isc::dns::Message& message,
+ isc::util::OutputBuffer& buffer,
isc::asiodns::DNSServer* server);
/// \brief Updates the data source for the \c AuthSrv object.
@@ -344,6 +344,7 @@ public:
/// \param type Type of a counter to get the value of
///
/// \return the value of the counter.
+
uint64_t getCounter(const AuthCounters::ServerCounterType type) const;
/// \brief Get the value of per Opcode counter in the Auth Counters.
@@ -360,6 +361,20 @@ public:
/// \return the value of the counter.
uint64_t getCounter(const isc::dns::Opcode opcode) const;
+ /// \brief Get the value of per Rcode counter in the Auth Counters.
+ ///
+ /// This function calls AuthCounters::getCounter(isc::dns::Rcode) and
+ /// returns its return value.
+ ///
+ /// \note This is a tentative interface as an attempt of experimentally
+ /// supporting more statistics counters. This should eventually be more
+ /// generalized. In any case, this method is mainly for testing.
+ ///
+ /// \throw None
+ /// \param rcode The rcode of the counter to get the value of
+ /// \return the value of the counter.
+ uint64_t getCounter(const isc::dns::Rcode rcode) const;
+
/**
* \brief Set and get the addresses we listen on.
*/
diff --git a/src/bin/auth/b10-auth.8 b/src/bin/auth/b10-auth.8
index 56b1732..14ba2ae 100644
--- a/src/bin/auth/b10-auth.8
+++ b/src/bin/auth/b10-auth.8
@@ -2,12 +2,12 @@
.\" Title: b10-auth
.\" Author: [FIXME: author] [see http://docbook.sf.net/el/author]
.\" Generator: DocBook XSL Stylesheets v1.75.2 <http://docbook.sf.net/>
-.\" Date: December 28, 2011
+.\" Date: March 1, 2012
.\" Manual: BIND10
.\" Source: BIND10
.\" Language: English
.\"
-.TH "B10\-AUTH" "8" "December 28, 2011" "BIND10" "BIND10"
+.TH "B10\-AUTH" "8" "March 1, 2012" "BIND10" "BIND10"
.\" -----------------------------------------------------------------
.\" * set default formatting
.\" -----------------------------------------------------------------
@@ -154,21 +154,25 @@ immediately\&.
\fBshutdown\fR
exits
-\fBb10\-auth\fR\&. (Note that the BIND 10 boss process will restart this service\&.)
+\fBb10\-auth\fR\&. This has an optional
+\fIpid\fR
+argument to select the process ID to stop\&. (Note that the BIND 10 boss process may restart this service if configured\&.)
.SH "STATISTICS DATA"
.PP
The statistics data collected by the
\fBb10\-stats\fR
-daemon include:
+daemon for
+\(lqAuth\(rq
+include:
.PP
-auth\&.queries\&.tcp
+queries\&.tcp
.RS 4
Total count of queries received by the
\fBb10\-auth\fR
server over TCP since startup\&.
.RE
.PP
-auth\&.queries\&.udp
+queries\&.udp
.RS 4
Total count of queries received by the
\fBb10\-auth\fR
@@ -198,5 +202,5 @@ The
daemon was first coded in October 2009\&.
.SH "COPYRIGHT"
.br
-Copyright \(co 2010 Internet Systems Consortium, Inc. ("ISC")
+Copyright \(co 2010-2012 Internet Systems Consortium, Inc. ("ISC")
.br
diff --git a/src/bin/auth/b10-auth.xml b/src/bin/auth/b10-auth.xml
index 947c316..7575217 100644
--- a/src/bin/auth/b10-auth.xml
+++ b/src/bin/auth/b10-auth.xml
@@ -2,7 +2,7 @@
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd"
[<!ENTITY mdash "—">]>
<!--
- - Copyright (C) 2010-2011 Internet Systems Consortium, Inc. ("ISC")
+ - Copyright (C) 2010-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
@@ -20,7 +20,7 @@
<refentry>
<refentryinfo>
- <date>December 28, 2011</date>
+ <date>March 1, 2012</date>
</refentryinfo>
<refmeta>
@@ -36,7 +36,7 @@
<docinfo>
<copyright>
- <year>2010</year>
+ <year>2010-2012</year>
<holder>Internet Systems Consortium, Inc. ("ISC")</holder>
</copyright>
</docinfo>
@@ -188,7 +188,10 @@
<para>
<command>shutdown</command> exits <command>b10-auth</command>.
- (Note that the BIND 10 boss process will restart this service.)
+ This has an optional <varname>pid</varname> argument to
+ select the process ID to stop.
+ (Note that the BIND 10 boss process may restart this service
+ if configured.)
</para>
</refsect1>
@@ -198,20 +201,20 @@
<para>
The statistics data collected by the <command>b10-stats</command>
- daemon include:
+ daemon for <quote>Auth</quote> include:
</para>
<variablelist>
<varlistentry>
- <term>auth.queries.tcp</term>
+ <term>queries.tcp</term>
<listitem><simpara>Total count of queries received by the
<command>b10-auth</command> server over TCP since startup.
</simpara></listitem>
</varlistentry>
<varlistentry>
- <term>auth.queries.udp</term>
+ <term>queries.udp</term>
<listitem><simpara>Total count of queries received by the
<command>b10-auth</command> server over UDP since startup.
</simpara></listitem>
@@ -219,6 +222,8 @@
</variablelist>
+<!-- TODO: missing stats docs. See ticket #1721 -->
+
</refsect1>
<refsect1>
diff --git a/src/bin/auth/benchmarks/.gitignore b/src/bin/auth/benchmarks/.gitignore
new file mode 100644
index 0000000..24e9944
--- /dev/null
+++ b/src/bin/auth/benchmarks/.gitignore
@@ -0,0 +1 @@
+/query_bench
diff --git a/src/bin/auth/benchmarks/query_bench.cc b/src/bin/auth/benchmarks/query_bench.cc
index a365085..aa238c0 100644
--- a/src/bin/auth/benchmarks/query_bench.cc
+++ b/src/bin/auth/benchmarks/query_bench.cc
@@ -76,8 +76,8 @@ private:
typedef boost::shared_ptr<const IOEndpoint> IOEndpointPtr;
protected:
QueryBenchMark(const bool enable_cache,
- const BenchQueries& queries, MessagePtr query_message,
- OutputBufferPtr buffer) :
+ const BenchQueries& queries, Message& query_message,
+ OutputBuffer& buffer) :
server_(new AuthSrv(enable_cache, xfrout_client)),
queries_(queries),
query_message_(query_message),
@@ -95,8 +95,8 @@ public:
for (query = queries_.begin(); query != query_end; ++query) {
IOMessage io_message(&(*query)[0], (*query).size(), dummy_socket,
*dummy_endpoint);
- query_message_->clear(Message::PARSE);
- buffer_->clear();
+ query_message_.clear(Message::PARSE);
+ buffer_.clear();
server_->processMessage(io_message, query_message_, buffer_,
&server);
}
@@ -107,8 +107,8 @@ protected:
AuthSrvPtr server_;
private:
const BenchQueries& queries_;
- MessagePtr query_message_;
- OutputBufferPtr buffer_;
+ Message& query_message_;
+ OutputBuffer& buffer_;
IOSocket& dummy_socket;
IOEndpointPtr dummy_endpoint;
};
@@ -118,8 +118,8 @@ public:
Sqlite3QueryBenchMark(const int cache_slots,
const char* const datasrc_file,
const BenchQueries& queries,
- MessagePtr query_message,
- OutputBufferPtr buffer) :
+ Message& query_message,
+ OutputBuffer& buffer) :
QueryBenchMark(cache_slots >= 0 ? true : false, queries,
query_message, buffer)
{
@@ -136,8 +136,8 @@ public:
MemoryQueryBenchMark(const char* const zone_file,
const char* const zone_origin,
const BenchQueries& queries,
- MessagePtr query_message,
- OutputBufferPtr buffer) :
+ Message& query_message,
+ OutputBuffer& buffer) :
QueryBenchMark(false, queries, query_message, buffer)
{
configureAuthServer(*server_,
@@ -255,8 +255,8 @@ main(int argc, char* argv[]) {
BenchQueries queries;
loadQueryData(query_data_file, queries, RRClass::IN());
- OutputBufferPtr buffer(new OutputBuffer(4096));
- MessagePtr message(new Message(Message::PARSE));
+ OutputBuffer buffer(4096);
+ Message message(Message::PARSE);
cout << "Parameters:" << endl;
cout << " Iterations: " << iteration << endl;
diff --git a/src/bin/auth/command.cc b/src/bin/auth/command.cc
index 280558d..ad0d134 100644
--- a/src/bin/auth/command.cc
+++ b/src/bin/auth/command.cc
@@ -12,24 +12,23 @@
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
-#include <string>
-
-#include <boost/scoped_ptr.hpp>
-#include <boost/shared_ptr.hpp>
+#include <auth/command.h>
+#include <auth/auth_log.h>
+#include <auth/auth_srv.h>
+#include <cc/data.h>
+#include <datasrc/memory_datasrc.h>
+#include <config/ccsession.h>
#include <exceptions/exceptions.h>
-
#include <dns/rrclass.h>
-#include <cc/data.h>
-
-#include <datasrc/memory_datasrc.h>
+#include <string>
-#include <config/ccsession.h>
+#include <boost/scoped_ptr.hpp>
+#include <boost/shared_ptr.hpp>
-#include <auth/auth_log.h>
-#include <auth/auth_srv.h>
-#include <auth/command.h>
+#include <sys/types.h>
+#include <unistd.h>
using boost::scoped_ptr;
using namespace isc::auth;
@@ -104,10 +103,30 @@ public:
virtual void exec(AuthSrv& server, isc::data::ConstElementPtr args) = 0;
};
-// Handle the "shutdown" command. No argument is assumed.
+// Handle the "shutdown" command. An optional parameter "pid" is used to
+// see if it is really for our instance.
class ShutdownCommand : public AuthCommand {
public:
- virtual void exec(AuthSrv& server, isc::data::ConstElementPtr) {
+ virtual void exec(AuthSrv& server, isc::data::ConstElementPtr args) {
+ // Is the pid argument provided?
+ if (args && args->contains("pid")) {
+ // If it is, we check it is the same as our PID
+
+ // This might throw in case the type is not an int, but that's
+ // OK, as it'll get converted to an error on higher level.
+ const int pid(args->get("pid")->intValue());
+ const pid_t my_pid(getpid());
+ if (my_pid != pid) {
+ // It is not for us
+ //
+ // Note that this is completely expected situation, if
+ // there are multiple instances of the server running and
+ // another instance is being shut down, we get the message
+ // too, due to the multicast nature of our message bus.
+ return;
+ }
+ }
+ LOG_DEBUG(auth_logger, DBG_AUTH_SHUT, AUTH_SHUTDOWN);
server.stop();
}
};
diff --git a/src/bin/auth/common.cc b/src/bin/auth/common.cc
index a7031f3..1602a1a 100644
--- a/src/bin/auth/common.cc
+++ b/src/bin/auth/common.cc
@@ -37,3 +37,5 @@ getXfroutSocketPath() {
}
}
}
+
+const char* const AUTH_NAME = "b10-auth";
diff --git a/src/bin/auth/common.h b/src/bin/auth/common.h
index b913593..cf71214 100644
--- a/src/bin/auth/common.h
+++ b/src/bin/auth/common.h
@@ -38,6 +38,11 @@ public:
/// The logic should be the same as in b10-xfrout, so they find each other.
std::string getXfroutSocketPath();
+/// \brief The name used when identifieng the process
+///
+/// This is currently b10-auth, but it can be changed easily in one place.
+extern const char* const AUTH_NAME;
+
#endif // __COMMON_H
// Local Variables:
diff --git a/src/bin/auth/main.cc b/src/bin/auth/main.cc
index 4a425e5..fc2f751 100644
--- a/src/bin/auth/main.cc
+++ b/src/bin/auth/main.cc
@@ -116,7 +116,7 @@ main(int argc, char* argv[]) {
}
// Initialize logging. If verbose, we'll use maximum verbosity.
- isc::log::initLogger("b10-auth",
+ isc::log::initLogger(AUTH_NAME,
(verbose ? isc::log::DEBUG : isc::log::INFO),
isc::log::MAX_DEBUG_LEVEL, NULL);
@@ -154,7 +154,7 @@ main(int argc, char* argv[]) {
cc_session = new Session(io_service.get_io_service());
LOG_DEBUG(auth_logger, DBG_AUTH_START, AUTH_CONFIG_CHANNEL_CREATED);
// Initialize the Socket Requestor
- isc::server_common::initSocketRequestor(*cc_session);
+ isc::server_common::initSocketRequestor(*cc_session, AUTH_NAME);
// We delay starting listening to new commands/config just before we
// go into the main loop to avoid confusion due to mixture of
diff --git a/src/bin/auth/query.cc b/src/bin/auth/query.cc
index 88624b8..7d50393 100644
--- a/src/bin/auth/query.cc
+++ b/src/bin/auth/query.cc
@@ -12,89 +12,89 @@
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
-#include <algorithm> // for std::max
-#include <vector>
-#include <boost/foreach.hpp>
-#include <boost/bind.hpp>
-#include <boost/function.hpp>
-
#include <dns/message.h>
#include <dns/rcode.h>
+#include <dns/rrtype.h>
#include <dns/rdataclass.h>
#include <datasrc/client.h>
#include <auth/query.h>
+#include <boost/foreach.hpp>
+#include <boost/bind.hpp>
+#include <boost/function.hpp>
+
+#include <algorithm> // for std::max
+#include <vector>
+
+using namespace std;
using namespace isc::dns;
using namespace isc::datasrc;
using namespace isc::dns::rdata;
-namespace isc {
-namespace auth {
+// Commonly used helper callback object for vector<ConstRRsetPtr> to
+// insert them to (the specified section of) a message.
+namespace {
+class RRsetInserter {
+public:
+ RRsetInserter(Message& msg, Message::Section section, bool dnssec) :
+ msg_(msg), section_(section), dnssec_(dnssec)
+ {}
+ void operator()(const ConstRRsetPtr& rrset) {
+ msg_.addRRset(section_,
+ boost::const_pointer_cast<AbstractRRset>(rrset),
+ dnssec_);
+ }
-void
-Query::addAdditional(ZoneFinder& zone, const RRset& rrset) {
- RdataIteratorPtr rdata_iterator(rrset.getRdataIterator());
- for (; !rdata_iterator->isLast(); rdata_iterator->next()) {
- const Rdata& rdata(rdata_iterator->getCurrent());
- if (rrset.getType() == RRType::NS()) {
- // Need to perform the search in the "GLUE OK" mode.
- const generic::NS& ns = dynamic_cast<const generic::NS&>(rdata);
- addAdditionalAddrs(zone, ns.getNSName(), ZoneFinder::FIND_GLUE_OK);
- } else if (rrset.getType() == RRType::MX()) {
- const generic::MX& mx(dynamic_cast<const generic::MX&>(rdata));
- addAdditionalAddrs(zone, mx.getMXName());
- }
+private:
+ Message& msg_;
+ const Message::Section section_;
+ const bool dnssec_;
+};
+
+// This is a "constant" vector storing desired RR types for the additional
+// section. The vector is filled first time it's used.
+const vector<RRType>&
+A_AND_AAAA() {
+ static vector<RRType> needed_types;
+ if (needed_types.empty()) {
+ needed_types.push_back(RRType::A());
+ needed_types.push_back(RRType::AAAA());
}
+ return (needed_types);
}
+// A wrapper for ZoneFinder::Context::getAdditional() so we don't include
+// duplicate RRs. This is not efficient, and we should actually unify
+// this at the end of the process() method. See also #1688.
void
-Query::addAdditionalAddrs(ZoneFinder& zone, const Name& qname,
- const ZoneFinder::FindOptions options)
+getAdditional(const Name& qname, RRType qtype,
+ ZoneFinder::Context& ctx, vector<ConstRRsetPtr>& results)
{
- // Out of zone name
- NameComparisonResult result = zone.getOrigin().compare(qname);
- if ((result.getRelation() != NameComparisonResult::SUPERDOMAIN) &&
- (result.getRelation() != NameComparisonResult::EQUAL))
- return;
-
- // Omit additional data which has already been provided in the answer
- // section from the additional.
- //
- // All the address rrset with the owner name of qname have been inserted
- // into ANSWER section.
- if (qname_ == qname && qtype_ == RRType::ANY())
- return;
-
- // Find A rrset
- if (qname_ != qname || qtype_ != RRType::A()) {
- ZoneFinder::FindResult a_result = zone.find(qname, RRType::A(),
- options | dnssec_opt_);
- if (a_result.code == ZoneFinder::SUCCESS) {
- response_.addRRset(Message::SECTION_ADDITIONAL,
- boost::const_pointer_cast<RRset>(a_result.rrset), dnssec_);
- }
- }
+ vector<ConstRRsetPtr> additionals;
+ ctx.getAdditional(A_AND_AAAA(), additionals);
- // Find AAAA rrset
- if (qname_ != qname || qtype_ != RRType::AAAA()) {
- ZoneFinder::FindResult aaaa_result = zone.find(qname, RRType::AAAA(),
- options | dnssec_opt_);
- if (aaaa_result.code == ZoneFinder::SUCCESS) {
- response_.addRRset(Message::SECTION_ADDITIONAL,
- boost::const_pointer_cast<RRset>(aaaa_result.rrset),
- dnssec_);
+ vector<ConstRRsetPtr>::const_iterator it = additionals.begin();
+ vector<ConstRRsetPtr>::const_iterator it_end = additionals.end();
+ for (; it != it_end; ++it) {
+ if ((qtype == (*it)->getType() || qtype == RRType::ANY()) &&
+ qname == (*it)->getName()) {
+ continue;
}
+ results.push_back(*it);
}
}
+}
+
+namespace isc {
+namespace auth {
void
Query::addSOA(ZoneFinder& finder) {
- ZoneFinder::FindResult soa_result = finder.find(finder.getOrigin(),
- RRType::SOA(),
- dnssec_opt_);
- if (soa_result.code != ZoneFinder::SUCCESS) {
+ ZoneFinderContextPtr soa_ctx = finder.find(finder.getOrigin(),
+ RRType::SOA(), dnssec_opt_);
+ if (soa_ctx->code != ZoneFinder::SUCCESS) {
isc_throw(NoSOA, "There's no SOA record in zone " <<
finder.getOrigin().toText());
} else {
@@ -104,7 +104,7 @@ Query::addSOA(ZoneFinder& finder) {
* to insist.
*/
response_.addRRset(Message::SECTION_AUTHORITY,
- boost::const_pointer_cast<RRset>(soa_result.rrset), dnssec_);
+ boost::const_pointer_cast<AbstractRRset>(soa_ctx->rrset), dnssec_);
}
}
@@ -117,14 +117,14 @@ Query::addSOA(ZoneFinder& finder) {
// either an SERVFAIL response or just ignoring the query. We at least prevent
// a complete crash due to such broken behavior.
void
-Query::addNXDOMAINProof(ZoneFinder& finder, ConstRRsetPtr nsec) {
+Query::addNXDOMAINProofByNSEC(ZoneFinder& finder, ConstRRsetPtr nsec) {
if (nsec->getRdataCount() == 0) {
isc_throw(BadNSEC, "NSEC for NXDOMAIN is empty");
}
// Add the NSEC proving NXDOMAIN to the authority section.
response_.addRRset(Message::SECTION_AUTHORITY,
- boost::const_pointer_cast<RRset>(nsec), dnssec_);
+ boost::const_pointer_cast<AbstractRRset>(nsec), dnssec_);
// Next, identify the best possible wildcard name that would match
// the query name. It's the longer common suffix with the qname
@@ -148,10 +148,10 @@ Query::addNXDOMAINProof(ZoneFinder& finder, ConstRRsetPtr nsec) {
// Confirm the wildcard doesn't exist (this should result in NXDOMAIN;
// otherwise we shouldn't have got NXDOMAIN for the original query in
// the first place).
- const ZoneFinder::FindResult fresult =
+ ConstZoneFinderContextPtr fcontext =
finder.find(wildname, RRType::NSEC(), dnssec_opt_);
- if (fresult.code != ZoneFinder::NXDOMAIN || !fresult.rrset ||
- fresult.rrset->getRdataCount() == 0) {
+ if (fcontext->code != ZoneFinder::NXDOMAIN || !fcontext->rrset ||
+ fcontext->rrset->getRdataCount() == 0) {
isc_throw(BadNSEC, "Unexpected result for wildcard NXDOMAIN proof");
}
@@ -160,31 +160,108 @@ Query::addNXDOMAINProof(ZoneFinder& finder, ConstRRsetPtr nsec) {
// Note: name comparison is relatively expensive. When we are at the
// stage of performance optimization, we should consider optimizing this
// for some optimized data source implementations.
- if (nsec->getName() != fresult.rrset->getName()) {
+ if (nsec->getName() != fcontext->rrset->getName()) {
response_.addRRset(Message::SECTION_AUTHORITY,
- boost::const_pointer_cast<RRset>(fresult.rrset),
+ boost::const_pointer_cast<AbstractRRset>(fcontext->rrset),
dnssec_);
}
}
+uint8_t
+Query::addClosestEncloserProof(ZoneFinder& finder, const Name& name,
+ bool exact_ok, bool add_closest)
+{
+ const ZoneFinder::FindNSEC3Result result = finder.findNSEC3(name, true);
+
+ // Validity check (see the method description). Note that a completely
+ // broken findNSEC3 implementation could even return NULL RRset in
+ // closest_proof. We don't explicitly check such case; addRRset() will
+ // throw an exception, and it will be converted to SERVFAIL at the caller.
+ if (!exact_ok && !result.next_proof) {
+ isc_throw(BadNSEC3, "Matching NSEC3 found for a non existent name: "
+ << qname_);
+ }
+
+ if (add_closest) {
+ response_.addRRset(Message::SECTION_AUTHORITY,
+ boost::const_pointer_cast<AbstractRRset>(
+ result.closest_proof),
+ dnssec_);
+ }
+ if (result.next_proof) {
+ response_.addRRset(Message::SECTION_AUTHORITY,
+ boost::const_pointer_cast<AbstractRRset>(
+ result.next_proof),
+ dnssec_);
+ }
+ return (result.closest_labels);
+}
+
void
-Query::addWildcardProof(ZoneFinder& finder) {
- // The query name shouldn't exist in the zone if there were no wildcard
- // substitution. Confirm that by specifying NO_WILDCARD. It should result
- // in NXDOMAIN and an NSEC RR that proves it should be returned.
- const ZoneFinder::FindResult fresult =
- finder.find(qname_, RRType::NSEC(),
- dnssec_opt_ | ZoneFinder::NO_WILDCARD);
- if (fresult.code != ZoneFinder::NXDOMAIN || !fresult.rrset ||
- fresult.rrset->getRdataCount() == 0) {
- isc_throw(BadNSEC, "Unexpected result for wildcard proof");
+Query::addNSEC3ForName(ZoneFinder& finder, const Name& name, bool match) {
+ const ZoneFinder::FindNSEC3Result result = finder.findNSEC3(name, false);
+
+ // See the comment for addClosestEncloserProof(). We don't check a
+ // totally bogus case where closest_proof is NULL here.
+ if (match != result.matched) {
+ isc_throw(BadNSEC3, "Unexpected "
+ << (result.matched ? "matching" : "covering")
+ << " NSEC3 found for " << name);
}
response_.addRRset(Message::SECTION_AUTHORITY,
- boost::const_pointer_cast<RRset>(fresult.rrset),
+ boost::const_pointer_cast<AbstractRRset>(
+ result.closest_proof),
dnssec_);
}
void
+Query::addNXDOMAINProofByNSEC3(ZoneFinder& finder) {
+ // Firstly get the NSEC3 proves for Closest Encloser Proof
+ // See Section 7.2.1 of RFC 5155.
+ const uint8_t closest_labels =
+ addClosestEncloserProof(finder, qname_, false);
+
+ // Next, construct the wildcard name at the closest encloser, i.e.,
+ // '*' followed by the closest encloser, and add NSEC3 for it.
+ const Name wildname(Name("*").concatenate(
+ qname_.split(qname_.getLabelCount() - closest_labels)));
+ addNSEC3ForName(finder, wildname, false);
+}
+
+void
+Query::addWildcardProof(ZoneFinder& finder,
+ const ZoneFinder::Context& db_context)
+{
+ if (db_context.isNSECSigned()) {
+ // Case for RFC4035 Section 3.1.3.3.
+ //
+ // The query name shouldn't exist in the zone if there were no wildcard
+ // substitution. Confirm that by specifying NO_WILDCARD. It should
+ // result in NXDOMAIN and an NSEC RR that proves it should be returned.
+ ConstZoneFinderContextPtr fcontext =
+ finder.find(qname_, RRType::NSEC(),
+ dnssec_opt_ | ZoneFinder::NO_WILDCARD);
+ if (fcontext->code != ZoneFinder::NXDOMAIN || !fcontext->rrset ||
+ fcontext->rrset->getRdataCount() == 0) {
+ isc_throw(BadNSEC,
+ "Unexpected NSEC result for wildcard proof");
+ }
+ response_.addRRset(Message::SECTION_AUTHORITY,
+ boost::const_pointer_cast<AbstractRRset>(
+ fcontext->rrset),
+ dnssec_);
+ } else if (db_context.isNSEC3Signed()) {
+ // Case for RFC 5155 Section 7.2.6.
+ //
+ // Note that the closest encloser must be the immediate ancestor
+ // of the matching wildcard, so NSEC3 for its next closer (and only
+ // that NSEC3) is what we are expected to provided per the RFC (if
+ // this assumption isn't met the zone is broken anyway).
+ addClosestEncloserProof(finder, qname_, false, false);
+ }
+}
+
+void
Query::addWildcardNXRRSETProof(ZoneFinder& finder, ConstRRsetPtr nsec) {
// There should be one NSEC RR which was found in the zone to prove
// that there is not matched <QNAME,QTYPE> via wildcard expansion.
@@ -192,32 +269,38 @@ Query::addWildcardNXRRSETProof(ZoneFinder& finder, ConstRRsetPtr nsec) {
isc_throw(BadNSEC, "NSEC for WILDCARD_NXRRSET is empty");
}
- const ZoneFinder::FindResult fresult =
+ ConstZoneFinderContextPtr fcontext =
finder.find(qname_, RRType::NSEC(),
dnssec_opt_ | ZoneFinder::NO_WILDCARD);
- if (fresult.code != ZoneFinder::NXDOMAIN || !fresult.rrset ||
- fresult.rrset->getRdataCount() == 0) {
+ if (fcontext->code != ZoneFinder::NXDOMAIN || !fcontext->rrset ||
+ fcontext->rrset->getRdataCount() == 0) {
isc_throw(BadNSEC, "Unexpected result for no match QNAME proof");
}
- if (nsec->getName() != fresult.rrset->getName()) {
+ if (nsec->getName() != fcontext->rrset->getName()) {
// one NSEC RR proves wildcard_nxrrset that no matched QNAME.
response_.addRRset(Message::SECTION_AUTHORITY,
- boost::const_pointer_cast<RRset>(fresult.rrset),
+ boost::const_pointer_cast<AbstractRRset>(fcontext->rrset),
dnssec_);
}
}
void
Query::addDS(ZoneFinder& finder, const Name& dname) {
- ZoneFinder::FindResult ds_result =
+ ConstZoneFinderContextPtr ds_context =
finder.find(dname, RRType::DS(), dnssec_opt_);
- if (ds_result.code == ZoneFinder::SUCCESS) {
+ if (ds_context->code == ZoneFinder::SUCCESS) {
response_.addRRset(Message::SECTION_AUTHORITY,
- boost::const_pointer_cast<RRset>(ds_result.rrset),
+ boost::const_pointer_cast<AbstractRRset>(
+ ds_context->rrset),
dnssec_);
- } else if (ds_result.code == ZoneFinder::NXRRSET) {
- addNXRRsetProof(finder, ds_result);
+ } else if (ds_context->code == ZoneFinder::NXRRSET &&
+ ds_context->isNSECSigned()) {
+ addNXRRsetProof(finder, *ds_context);
+ } else if (ds_context->code == ZoneFinder::NXRRSET &&
+ ds_context->isNSEC3Signed()) {
+ // Add no DS proof with NSEC3 as specified in RFC 5155 Section 7.2.7.
+ addClosestEncloserProof(finder, dname, true);
} else {
// Any other case should be an error
isc_throw(BadDS, "Unexpected result for DS lookup for delegation");
@@ -226,44 +309,78 @@ Query::addDS(ZoneFinder& finder, const Name& dname) {
void
Query::addNXRRsetProof(ZoneFinder& finder,
- const ZoneFinder::FindResult& db_result)
+ const ZoneFinder::Context& db_context)
{
- if (db_result.isNSECSigned() && db_result.rrset) {
+ if (db_context.isNSECSigned() && db_context.rrset) {
response_.addRRset(Message::SECTION_AUTHORITY,
- boost::const_pointer_cast<RRset>(
- db_result.rrset),
+ boost::const_pointer_cast<AbstractRRset>(
+ db_context.rrset),
dnssec_);
- if (db_result.isWildcard()) {
- addWildcardNXRRSETProof(finder, db_result.rrset);
+ if (db_context.isWildcard()) {
+ addWildcardNXRRSETProof(finder, db_context.rrset);
+ }
+ } else if (db_context.isNSEC3Signed() && !db_context.isWildcard()) {
+ if (qtype_ == RRType::DS()) {
+ // RFC 5155, Section 7.2.4. Add either NSEC3 for the qname or
+ // closest (provable) encloser proof in case of optout.
+ addClosestEncloserProof(finder, qname_, true);
+ } else {
+ // RFC 5155, Section 7.2.3. Just add NSEC3 for the qname.
+ addNSEC3ForName(finder, qname_, true);
}
+ } else if (db_context.isNSEC3Signed() && db_context.isWildcard()) {
+ // Case for RFC 5155 Section 7.2.5: add closest encloser proof for the
+ // qname, construct the matched wildcard name and add NSEC3 for it.
+ const uint8_t closest_labels =
+ addClosestEncloserProof(finder, qname_, false);
+ const Name wname = Name("*").concatenate(
+ qname_.split(qname_.getLabelCount() - closest_labels));
+ addNSEC3ForName(finder, wname, true);
}
}
void
-Query::addAuthAdditional(ZoneFinder& finder) {
+Query::addAuthAdditional(ZoneFinder& finder,
+ vector<ConstRRsetPtr>& additionals)
+{
+ const Name& origin = finder.getOrigin();
+
// Fill in authority and addtional sections.
- ZoneFinder::FindResult ns_result =
- finder.find(finder.getOrigin(), RRType::NS(), dnssec_opt_);
+ ConstZoneFinderContextPtr ns_context = finder.find(origin, RRType::NS(),
+ dnssec_opt_);
// zone origin name should have NS records
- if (ns_result.code != ZoneFinder::SUCCESS) {
+ if (ns_context->code != ZoneFinder::SUCCESS) {
isc_throw(NoApexNS, "There's no apex NS records in zone " <<
- finder.getOrigin().toText());
- } else {
- response_.addRRset(Message::SECTION_AUTHORITY,
- boost::const_pointer_cast<RRset>(ns_result.rrset), dnssec_);
- // Handle additional for authority section
- addAdditional(finder, *ns_result.rrset);
+ finder.getOrigin().toText());
}
+ response_.addRRset(Message::SECTION_AUTHORITY,
+ boost::const_pointer_cast<AbstractRRset>(
+ ns_context->rrset), dnssec_);
+ getAdditional(qname_, qtype_, *ns_context, additionals);
+}
+
+namespace {
+// A simple wrapper for DataSourceClient::findZone(). Normally we can simply
+// check the closest zone to the qname, but for type DS query we need to
+// look into the parent zone. Nevertheless, if there is no "parent" (i.e.,
+// the qname consists of a single label, which also means it's the root name),
+// we should search the deepest zone we have (which should be the root zone;
+// otherwise it's a query error).
+DataSourceClient::FindResult
+findZone(const DataSourceClient& client, const Name& qname, RRType qtype) {
+ if (qtype != RRType::DS() || qname.getLabelCount() == 1) {
+ return (client.findZone(qname));
+ }
+ return (client.findZone(qname.split(1)));
+}
}
void
Query::process() {
- const bool qtype_is_any = (qtype_ == RRType::ANY());
-
- response_.setHeaderFlag(Message::HEADERFLAG_AA, false);
- const DataSourceClient::FindResult result =
- datasrc_client_.findZone(qname_);
+ // Found a zone which is the nearest ancestor to QNAME
+ const DataSourceClient::FindResult result = findZone(datasrc_client_,
+ qname_, qtype_);
// If we have no matching authoritative zone for the query name, return
// REFUSED. In short, this is to be compatible with BIND 9, but the
@@ -272,16 +389,27 @@ Query::process() {
// https://lists.isc.org/mailman/htdig/bind10-dev/2010-December/001633.html
if (result.code != result::SUCCESS &&
result.code != result::PARTIALMATCH) {
+ // If we tried to find a "parent zone" for a DS query and failed,
+ // we may still have authority at the child side. If we do, the query
+ // has to be handled there.
+ if (qtype_ == RRType::DS() && qname_.getLabelCount() > 1 &&
+ processDSAtChild()) {
+ return;
+ }
+ response_.setHeaderFlag(Message::HEADERFLAG_AA, false);
response_.setRcode(Rcode::REFUSED());
return;
}
ZoneFinder& zfinder = *result.zone_finder;
- // Found a zone which is the nearest ancestor to QNAME, set the AA bit
+ // We have authority for a zone that contain the query name (possibly
+ // indirectly via delegation). Look into the zone.
response_.setHeaderFlag(Message::HEADERFLAG_AA);
response_.setRcode(Rcode::NOERROR());
- std::vector<ConstRRsetPtr> target;
- boost::function0<ZoneFinder::FindResult> find;
+ vector<ConstRRsetPtr> target;
+ vector<ConstRRsetPtr> additionals;
+ boost::function0<ZoneFinderContextPtr> find;
+ const bool qtype_is_any = (qtype_ == RRType::ANY());
if (qtype_is_any) {
find = boost::bind(&ZoneFinder::findAll, &zfinder, qname_,
boost::ref(target), dnssec_opt_);
@@ -289,12 +417,12 @@ Query::process() {
find = boost::bind(&ZoneFinder::find, &zfinder, qname_, qtype_,
dnssec_opt_);
}
- ZoneFinder::FindResult db_result(find());
- switch (db_result.code) {
+ ZoneFinderContextPtr db_context(find());
+ switch (db_context->code) {
case ZoneFinder::DNAME: {
// First, put the dname into the answer
response_.addRRset(Message::SECTION_ANSWER,
- boost::const_pointer_cast<RRset>(db_result.rrset),
+ boost::const_pointer_cast<AbstractRRset>(db_context->rrset),
dnssec_);
/*
* Empty DNAME should never get in, as it is impossible to
@@ -302,14 +430,14 @@ Query::process() {
*
* FIXME: Other way to prevent this should be done
*/
- assert(db_result.rrset->getRdataCount() > 0);
+ assert(db_context->rrset->getRdataCount() > 0);
// Get the data of DNAME
const rdata::generic::DNAME& dname(
dynamic_cast<const rdata::generic::DNAME&>(
- db_result.rrset->getRdataIterator()->getCurrent()));
+ db_context->rrset->getRdataIterator()->getCurrent()));
// The yet unmatched prefix dname
const Name prefix(qname_.split(0, qname_.getLabelCount() -
- db_result.rrset->getName().getLabelCount()));
+ db_context->rrset->getName().getLabelCount()));
// If we put it together, will it be too long?
// (The prefix contains trailing ., which will be removed
if (prefix.getLength() - Name::ROOT_NAME().getLength() +
@@ -324,12 +452,12 @@ Query::process() {
// The new CNAME we are creating (it will be unsigned even
// with DNSSEC, the DNAME is signed and it can be validated
// by that)
- RRsetPtr cname(new RRset(qname_, db_result.rrset->getClass(),
- RRType::CNAME(), db_result.rrset->getTTL()));
+ RRsetPtr cname(new RRset(qname_, db_context->rrset->getClass(),
+ RRType::CNAME(), db_context->rrset->getTTL()));
// Construct the new target by replacing the end
cname->addRdata(rdata::generic::CNAME(qname_.split(0,
qname_.getLabelCount() -
- db_result.rrset->getName().getLabelCount()).
+ db_context->rrset->getName().getLabelCount()).
concatenate(dname.getDname())));
response_.addRRset(Message::SECTION_ANSWER, cname, dnssec_);
break;
@@ -345,13 +473,13 @@ Query::process() {
* So, just put it there.
*/
response_.addRRset(Message::SECTION_ANSWER,
- boost::const_pointer_cast<RRset>(db_result.rrset),
+ boost::const_pointer_cast<AbstractRRset>(db_context->rrset),
dnssec_);
// If the answer is a result of wildcard substitution,
// add a proof that there's no closer name.
- if (dnssec_ && db_result.isWildcard()) {
- addWildcardProof(*result.zone_finder);
+ if (dnssec_ && db_context->isWildcard()) {
+ addWildcardProof(*result.zone_finder, *db_context);
}
break;
case ZoneFinder::SUCCESS:
@@ -360,57 +488,73 @@ Query::process() {
// into answer section.
BOOST_FOREACH(ConstRRsetPtr rrset, target) {
response_.addRRset(Message::SECTION_ANSWER,
- boost::const_pointer_cast<RRset>(rrset), dnssec_);
- // Handle additional for answer section
- addAdditional(*result.zone_finder, *rrset.get());
+ boost::const_pointer_cast<AbstractRRset>(rrset), dnssec_);
}
} else {
response_.addRRset(Message::SECTION_ANSWER,
- boost::const_pointer_cast<RRset>(db_result.rrset),
+ boost::const_pointer_cast<AbstractRRset>(db_context->rrset),
dnssec_);
- // Handle additional for answer section
- addAdditional(*result.zone_finder, *db_result.rrset);
}
+
+ // Retrieve additional records for the answer
+ getAdditional(qname_, qtype_, *db_context, additionals);
+
// If apex NS records haven't been provided in the answer
// section, insert apex NS records into the authority section
// and AAAA/A RRS of each of the NS RDATA into the additional
// section.
- if (qname_ != result.zone_finder->getOrigin() ||
- db_result.code != ZoneFinder::SUCCESS ||
+ // Checking the findZone() is a lightweight check to see if
+ // qname is the zone origin.
+ if (result.code != result::SUCCESS ||
+ db_context->code != ZoneFinder::SUCCESS ||
(qtype_ != RRType::NS() && !qtype_is_any))
{
- addAuthAdditional(*result.zone_finder);
+ addAuthAdditional(*result.zone_finder, additionals);
}
// If the answer is a result of wildcard substitution,
// add a proof that there's no closer name.
- if (dnssec_ && db_result.isWildcard()) {
- addWildcardProof(*result.zone_finder);
+ if (dnssec_ && db_context->isWildcard()) {
+ addWildcardProof(*result.zone_finder, *db_context);
}
break;
case ZoneFinder::DELEGATION:
+ // If a DS query resulted in delegation, we also need to check
+ // if we are an authority of the child, too. If so, we need to
+ // complete the process in the child as specified in Section
+ // 2.2.1.2. of RFC3658.
+ if (qtype_ == RRType::DS() && processDSAtChild()) {
+ return;
+ }
+
response_.setHeaderFlag(Message::HEADERFLAG_AA, false);
response_.addRRset(Message::SECTION_AUTHORITY,
- boost::const_pointer_cast<RRset>(db_result.rrset),
+ boost::const_pointer_cast<AbstractRRset>(db_context->rrset),
dnssec_);
+ // Retrieve additional records for the name servers
+ db_context->getAdditional(A_AND_AAAA(), additionals);
+
// If DNSSEC is requested, see whether there is a DS
// record for this delegation.
if (dnssec_) {
- addDS(*result.zone_finder, db_result.rrset->getName());
+ addDS(*result.zone_finder, db_context->rrset->getName());
}
- addAdditional(*result.zone_finder, *db_result.rrset);
break;
case ZoneFinder::NXDOMAIN:
response_.setRcode(Rcode::NXDOMAIN());
addSOA(*result.zone_finder);
- if (dnssec_ && db_result.rrset) {
- addNXDOMAINProof(zfinder, db_result.rrset);
+ if (dnssec_) {
+ if (db_context->isNSECSigned() && db_context->rrset) {
+ addNXDOMAINProofByNSEC(zfinder, db_context->rrset);
+ } else if (db_context->isNSEC3Signed()) {
+ addNXDOMAINProofByNSEC3(zfinder);
+ }
}
break;
case ZoneFinder::NXRRSET:
addSOA(*result.zone_finder);
if (dnssec_) {
- addNXRRsetProof(zfinder, db_result);
+ addNXRRsetProof(zfinder, *db_context);
}
break;
default:
@@ -420,6 +564,42 @@ Query::process() {
isc_throw(isc::NotImplemented, "Unknown result code");
break;
}
+
+ for_each(additionals.begin(), additionals.end(),
+ RRsetInserter(response_, Message::SECTION_ADDITIONAL,
+ dnssec_));
+}
+
+bool
+Query::processDSAtChild() {
+ const DataSourceClient::FindResult zresult =
+ datasrc_client_.findZone(qname_);
+
+ if (zresult.code != result::SUCCESS) {
+ return (false);
+ }
+
+ // We are receiving a DS query at the child side of the owner name,
+ // where the DS isn't supposed to belong. We should return a "no data"
+ // response as described in Section 3.1.4.1 of RFC4035 and Section
+ // 2.2.1.1 of RFC 3658. find(DS) should result in NXRRSET, in which
+ // case (and if DNSSEC is required) we also add the proof for that,
+ // but even if find() returns an unexpected result, we don't bother.
+ // The important point in this case is to return SOA so that the resolver
+ // that happens to contact us can hunt for the appropriate parent zone
+ // by seeing the SOA.
+ response_.setHeaderFlag(Message::HEADERFLAG_AA);
+ response_.setRcode(Rcode::NOERROR());
+ addSOA(*zresult.zone_finder);
+ ConstZoneFinderContextPtr ds_context =
+ zresult.zone_finder->find(qname_, RRType::DS(), dnssec_opt_);
+ if (ds_context->code == ZoneFinder::NXRRSET) {
+ if (dnssec_) {
+ addNXRRsetProof(*zresult.zone_finder, *ds_context);
+ }
+ }
+
+ return (true);
}
}
diff --git a/src/bin/auth/query.h b/src/bin/auth/query.h
index 886ff93..f8a8f02 100644
--- a/src/bin/auth/query.h
+++ b/src/bin/auth/query.h
@@ -15,8 +15,11 @@
*/
#include <exceptions/exceptions.h>
+#include <dns/rrset.h>
#include <datasrc/zone.h>
+#include <vector>
+
namespace isc {
namespace dns {
class Message;
@@ -86,27 +89,36 @@ private:
void addDS(isc::datasrc::ZoneFinder& finder,
const isc::dns::Name& ds_name);
- /// \brief Adds NSEC denial proof for the given NXRRset result
+ /// \brief Adds NSEC(3) denial proof for the given NXRRset result
///
- /// NSEC records, if available (signaled by isNSECSigned(), are added
- /// to the authority section.
+ /// If available, NSEC or NSEC3 records are added to the authority
+ /// section (depending on whether isNSECSigned() or isNSEC3Signed()
+ /// returns true).
///
/// \param finder The ZoneFinder that was used to search for the missing
/// data
/// \param db_result The ZoneFinder::FindResult returned by find()
void addNXRRsetProof(isc::datasrc::ZoneFinder& finder,
- const isc::datasrc::ZoneFinder::FindResult& db_result);
+ const isc::datasrc::ZoneFinder::Context& db_context);
/// Add NSEC RRs that prove an NXDOMAIN result.
///
/// This corresponds to Section 3.1.3.2 of RFC 4035.
- void addNXDOMAINProof(isc::datasrc::ZoneFinder& finder,
- isc::dns::ConstRRsetPtr nsec);
+ void addNXDOMAINProofByNSEC(isc::datasrc::ZoneFinder& finder,
+ isc::dns::ConstRRsetPtr nsec);
+
+ /// Add NSEC3 RRs that prove an NXDOMAIN result.
+ ///
+ /// This corresponds to Section 7.2.2 of RFC 5155.
+ void addNXDOMAINProofByNSEC3(isc::datasrc::ZoneFinder& finder);
- /// Add NSEC RRs that prove a wildcard answer is the best one.
+ /// Add NSEC or NSEC3 RRs that prove a wildcard answer is the best one.
///
- /// This corresponds to Section 3.1.3.3 of RFC 4035.
- void addWildcardProof(isc::datasrc::ZoneFinder& finder);
+ /// This corresponds to Section 3.1.3.3 of RFC 4035 and Section 7.2.6
+ /// of RFC5155.
+ void addWildcardProof(
+ isc::datasrc::ZoneFinder& finder,
+ const isc::datasrc::ZoneFinder::Context& db_context);
/// \brief Adds one NSEC RR proved no matched QNAME,one NSEC RR proved no
/// matched <QNAME,QTYPE> through wildcard extension.
@@ -119,44 +131,6 @@ private:
/// <QNAME,QTTYPE>.
void addWildcardNXRRSETProof(isc::datasrc::ZoneFinder& finder,
isc::dns::ConstRRsetPtr nsec);
-
- /// \brief Look up additional data (i.e., address records for the names
- /// included in NS or MX records) and add them to the additional section.
- ///
- /// Note: Any additional data which has already been provided in the
- /// answer section (i.e., if the original query happend to be for the
- /// address of the DNS server), it should be omitted from the additional.
- ///
- /// This method may throw a exception because its underlying methods may
- /// throw exceptions.
- ///
- /// \param zone The ZoneFinder through which the additional data for the
- /// query is to be found.
- /// \param rrset The RRset (i.e., NS or MX rrset) which require additional
- /// processing.
- void addAdditional(isc::datasrc::ZoneFinder& zone,
- const isc::dns::RRset& rrset);
-
- /// \brief Find address records for a specified name.
- ///
- /// Search the specified zone for AAAA/A RRs of each of the NS/MX RDATA
- /// (domain name), and insert the found ones into the additional section
- /// if address records are available. By default the search will stop
- /// once it encounters a zone cut.
- ///
- /// Note: we need to perform the search in the "GLUE OK" mode for NS RDATA,
- /// which means that we should include A/AAAA RRs under a zone cut.
- /// The glue records must exactly match the name in the NS RDATA, without
- /// CNAME or wildcard processing.
- ///
- /// \param zone The \c ZoneFinder through which the address records is to
- /// be found.
- /// \param qname The name in rrset RDATA.
- /// \param options The search options.
- void addAdditionalAddrs(isc::datasrc::ZoneFinder& zone,
- const isc::dns::Name& qname,
- const isc::datasrc::ZoneFinder::FindOptions options
- = isc::datasrc::ZoneFinder::FIND_DEFAULT);
/// \brief Look up a zone's NS RRset and their address records for an
/// authoritative answer, and add them to the additional section.
@@ -176,7 +150,81 @@ private:
///
/// \param finder The \c ZoneFinder through which the NS and additional
/// data for the query are to be found.
- void addAuthAdditional(isc::datasrc::ZoneFinder& finder);
+ void addAuthAdditional(isc::datasrc::ZoneFinder& finder,
+ std::vector<isc::dns::ConstRRsetPtr>& additionals);
+
+ /// \brief Process a DS query possible at the child side of zone cut.
+ ///
+ /// This private method is a subroutine of process(), and is called if
+ /// there's a possibility that this server has authority for the child
+ /// side of the DS's owner name (and it's detected that the server at
+ /// least doesn't have authority at the parent side). This method
+ /// first checks if it has authority for the child, and if does,
+ /// just build a "no data" response with SOA for the zone origin
+ /// (possibly with a proof for the no data) as specified in Section
+ /// 2.2.1.1 of RFC3658.
+ ///
+ /// It returns true if this server has authority of the child; otherwise
+ /// it returns false. In the former case, the caller is expected to
+ /// terminate the query processing, because it should have been completed
+ /// within this method.
+ bool processDSAtChild();
+
+ /// \brief Add NSEC3 to the response for a closest encloser proof for a
+ /// given name.
+ ///
+ /// This method calls \c findNSEC3() of the given zone finder for the
+ /// given name in the recursive mode, and adds the returned NSEC3(s) to
+ /// the authority section of the response message associated with the
+ /// \c Query object.
+ ///
+ /// It returns the number of labels of the closest encloser (returned via
+ /// the \c findNSEC3() call) in case the caller needs to use that value
+ /// for subsequent processing, i.e, constructing the best possible wildcard
+ /// name that (would) match the query name.
+ ///
+ /// Unless \c exact_ok is true, \c name is expected to be non existent,
+ /// in which case findNSEC3() in the recursive mode must return both
+ /// closest and next proofs. If the latter is NULL, it means a run time
+ /// collision (or the zone is broken in other way), and this method throws
+ /// a BadNSEC3 exception.
+ ///
+ /// If \c exact_ok is true, this method takes into account the case
+ /// where the name exists and may or may not be at a zone cut to an
+ /// optout zone. In this case, depending on whether the zone is optout
+ /// or not, findNSEC3() may return non-NULL or NULL next_proof
+ /// (respectively). This method adds the next proof if and only if
+ /// findNSEC3() returns non NULL value for it. The Opt-Out flag
+ /// must be set or cleared accordingly, but this method doesn't check that
+ /// in this level (as long as the zone is signed validly and findNSEC3()
+ /// for the data source is implemented as documented, the condition
+ /// should be met; otherwise we'd let the validator detect the error).
+ ///
+ /// By default this method always adds the closest proof.
+ /// If \c add_closest is false, it only adds the next proof to the message.
+ /// This correspond to the case of "wildcard answer responses" as described
+ /// in Section 7.2.6 of RFC5155.
+ uint8_t addClosestEncloserProof(isc::datasrc::ZoneFinder& finder,
+ const isc::dns::Name& name, bool exact_ok,
+ bool add_closest = true);
+
+ /// \brief Add matching or covering NSEC3 to the response for a give name.
+ ///
+ /// This method calls \c findNSEC3() of the given zone finder for the
+ /// given name in the non recursive mode, and adds the returned NSEC3 to
+ /// the authority section of the response message associated with the
+ /// \c Query object.
+ ///
+ /// Depending on the caller's context, the returned NSEC3 is one and
+ /// only one of matching or covering NSEC3. If \c match is true the
+ /// returned NSEC3 must be a matching one; otherwise it must be a covering
+ /// one. If this assumption isn't met this method throws a BadNSEC3
+ /// exception (if it must be a matching NSEC3 but is not, it means a broken
+ /// zone, maybe with incorrect optout NSEC3s; if it must be a covering
+ /// NSEC3 but is not, it means a run time collision; or the \c findNSEC3()
+ /// implementation is broken for both cases.)
+ void addNSEC3ForName(isc::datasrc::ZoneFinder& finder,
+ const isc::dns::Name& name, bool match);
public:
/// Constructor from query parameters.
@@ -270,6 +318,17 @@ public:
{}
};
+ /// An invalid result is given when a valid NSEC3 is expected
+ ///
+ /// This can only happen when the underlying data source implementation or
+ /// the zone is broken. By throwing an exception we treat such cases
+ /// as SERVFAIL.
+ struct BadNSEC3 : public BadZone {
+ BadNSEC3(const char* file, size_t line, const char* what) :
+ BadZone(file, line, what)
+ {}
+ };
+
/// An invalid result is given when a valid DS records (or NXRRSET) is
/// expected
///
diff --git a/src/bin/auth/statistics.cc b/src/bin/auth/statistics.cc
index 8a81bd5..bd16537 100644
--- a/src/bin/auth/statistics.cc
+++ b/src/bin/auth/statistics.cc
@@ -50,6 +50,9 @@ public:
void inc(const Opcode opcode) {
opcode_counter_.inc(opcode.getCode());
}
+ void inc(const Rcode rcode) {
+ rcode_counter_.inc(rcode.getCode());
+ }
void inc(const std::string& zone,
const AuthCounters::PerZoneCounterType type);
bool submitStatistics() const;
@@ -61,10 +64,15 @@ public:
uint64_t getCounter(const Opcode opcode) const {
return (opcode_counter_.get(opcode.getCode()));
}
+ uint64_t getCounter(const Rcode rcode) const {
+ return (rcode_counter_.get(rcode.getCode()));
+ }
private:
Counter server_counter_;
Counter opcode_counter_;
static const size_t NUM_OPCODES = 16;
+ Counter rcode_counter_;
+ static const size_t NUM_RCODES = 17;
CounterDictionary per_zone_counter_;
isc::cc::AbstractSession* statistics_session_;
AuthCounters::validator_type validator_;
@@ -75,7 +83,7 @@ AuthCountersImpl::AuthCountersImpl() :
// size of server_counter_: AuthCounters::SERVER_COUNTER_TYPES
// size of per_zone_counter_: AuthCounters::PER_ZONE_COUNTER_TYPES
server_counter_(AuthCounters::SERVER_COUNTER_TYPES),
- opcode_counter_(NUM_OPCODES),
+ opcode_counter_(NUM_OPCODES), rcode_counter_(NUM_RCODES),
per_zone_counter_(AuthCounters::PER_ZONE_COUNTER_TYPES),
statistics_session_(NULL)
{
@@ -125,6 +133,19 @@ AuthCountersImpl::submitStatistics() const {
<< counter;
}
}
+ // Insert non 0 Rcode counters.
+ for (int i = 0; i < NUM_RCODES; ++i) {
+ const Counter::Type counter = rcode_counter_.get(i);
+ if (counter != 0) {
+ // The counter item name should be derived lower-cased textual
+ // representation of the code.
+ std::string rcode_txt = Rcode(i).toText();
+ std::transform(rcode_txt.begin(), rcode_txt.end(),
+ rcode_txt.begin(), ::tolower);
+ statistics_string << ", \"rcode." << rcode_txt << "\": "
+ << counter;
+ }
+ }
statistics_string << " }"
<< "}"
<< "]}";
@@ -194,6 +215,11 @@ AuthCounters::inc(const Opcode opcode) {
impl_->inc(opcode);
}
+void
+AuthCounters::inc(const Rcode rcode) {
+ impl_->inc(rcode);
+}
+
bool
AuthCounters::submitStatistics() const {
return (impl_->submitStatistics());
@@ -216,6 +242,11 @@ AuthCounters::getCounter(const Opcode opcode) const {
return (impl_->getCounter(opcode));
}
+uint64_t
+AuthCounters::getCounter(const Rcode rcode) const {
+ return (impl_->getCounter(rcode));
+}
+
void
AuthCounters::registerStatisticsValidator
(AuthCounters::validator_type validator) const
diff --git a/src/bin/auth/statistics.h b/src/bin/auth/statistics.h
index 8ca09ce..0c92605 100644
--- a/src/bin/auth/statistics.h
+++ b/src/bin/auth/statistics.h
@@ -16,6 +16,7 @@
#define __STATISTICS_H 1
#include <dns/opcode.h>
+#include <dns/rcode.h>
#include <cc/session.h>
#include <stdint.h>
@@ -98,6 +99,15 @@ public:
/// \throw None
void inc(const isc::dns::Opcode opcode);
+ /// \brief Increment the counter of a per rcode counter.
+ ///
+ /// \note This is a tentative interface. See \c getCounter().
+ ///
+ /// \param rcode The rcode of the counter to increment.
+ ///
+ /// \throw None
+ void inc(const isc::dns::Rcode rcode);
+
/// \brief Submit statistics counters to statistics module.
///
/// This method is desinged to be called periodically
@@ -162,6 +172,21 @@ public:
/// \return the value of the counter.
uint64_t getCounter(const isc::dns::Opcode opcode) const;
+ /// \brief Get the value of a per rcode counter.
+ ///
+ /// This method returns the value of the per rcode counter for the
+ /// specified \c rcode.
+ ///
+ /// \note As mentioned in getCounter(const isc::dns::Opcode opcode),
+ /// This is a tentative interface as an attempt of experimentally
+ /// supporting more statistics counters. This should eventually be more
+ /// generalized. In any case, this method is mainly for testing.
+ ///
+ /// \throw None
+ /// \param rcode The rcode of the counter to get the value of
+ /// \return the value of the counter.
+ uint64_t getCounter(const isc::dns::Rcode rcode) const;
+
/// \brief A type of validation function for the specification in
/// isc::config::ModuleSpec.
///
diff --git a/src/bin/auth/tests/.gitignore b/src/bin/auth/tests/.gitignore
new file mode 100644
index 0000000..d6d1ec8
--- /dev/null
+++ b/src/bin/auth/tests/.gitignore
@@ -0,0 +1 @@
+/run_unittests
diff --git a/src/bin/auth/tests/auth_srv_unittest.cc b/src/bin/auth/tests/auth_srv_unittest.cc
index 2fccccf..4823ad7 100644
--- a/src/bin/auth/tests/auth_srv_unittest.cc
+++ b/src/bin/auth/tests/auth_srv_unittest.cc
@@ -65,22 +65,76 @@ const char* const CONFIG_TESTDB =
const char* const BADCONFIG_TESTDB =
"{ \"database_file\": \"" TEST_DATA_DIR "/nodir/notexist\"}";
+// This is a configuration that uses the in-memory data source containing
+// a signed example zone.
+const char* const CONFIG_INMEMORY_EXAMPLE =
+ "{\"datasources\": [{\"type\": \"memory\","
+ "\"zones\": [{\"origin\": \"example\","
+ "\"file\": \"" TEST_DATA_DIR "/rfc5155-example.zone.signed\"}]}]}";
+
class AuthSrvTest : public SrvTestBase {
protected:
AuthSrvTest() :
dnss_(ios_, NULL, NULL, NULL),
server(true, xfrout),
rrclass(RRClass::IN()),
- sock_requestor_(dnss_, address_store_, 53210)
+ // The empty string is expected value of the parameter of
+ // requestSocket, not the app_name (there's no fallback, it checks
+ // the empty string is passed).
+ sock_requestor_(dnss_, address_store_, 53210, "")
{
server.setDNSService(dnss_);
server.setXfrinSession(¬ify_session);
server.setStatisticsSession(&statistics_session);
}
+
virtual void processMessage() {
- server.processMessage(*io_message, parse_message, response_obuffer,
+ // If processMessage has been called before, parse_message needs
+ // to be reset. If it hasn't, there's no harm in doing so
+ parse_message->clear(Message::PARSE);
+ server.processMessage(*io_message, *parse_message, *response_obuffer,
&dnsserv);
}
+
+ // Helper for checking Rcode statistic counters;
+ // Checks for one specific Rcode statistics counter value
+ void checkRcodeCounter(const Rcode& rcode, int expected_value) const {
+ EXPECT_EQ(expected_value, server.getCounter(rcode)) <<
+ "Expected Rcode count for " << rcode.toText() <<
+ " " << expected_value << ", was: " <<
+ server.getCounter(rcode);
+ }
+
+ // Checks whether all Rcode counters are set to zero
+ void checkAllRcodeCountersZero() const {
+ for (int i = 0; i < 17; i++) {
+ checkRcodeCounter(Rcode(i), 0);
+ }
+ }
+
+ // Checks whether all Rcode counters are set to zero except the given
+ // rcode (it is checked to be set to 'value')
+ void checkAllRcodeCountersZeroExcept(const Rcode& rcode, int value) const {
+ for (int i = 0; i < 17; i++) {
+ const Rcode rc(i);
+ if (rc == rcode) {
+ checkRcodeCounter(Rcode(i), value);
+ } else {
+ checkRcodeCounter(Rcode(i), 0);
+ }
+ }
+ }
+
+ // Convenience method for tests that expect to return SERVFAIL
+ // It calls processMessage, checks if there is an answer, and
+ // check the header for default SERVFAIL data
+ void processAndCheckSERVFAIL() {
+ processMessage();
+ EXPECT_TRUE(dnsserv.hasAnswer());
+ headerCheck(*parse_message, default_qid, Rcode::SERVFAIL(),
+ opcode.getCode(), QR_FLAG, 1, 0, 0, 0);
+ }
+
IOService ios_;
DNSService dnss_;
MockSession statistics_session;
@@ -115,8 +169,7 @@ createBuiltinVersionResponse(const qid_t qid, vector<uint8_t>& data) {
rrset_version_ns->addRdata(generic::NS(version_name));
message.addRRset(Message::SECTION_AUTHORITY, rrset_version_ns);
- OutputBuffer obuffer(0);
- MessageRenderer renderer(obuffer);
+ MessageRenderer renderer;
message.toWire(renderer);
data.clear();
@@ -135,13 +188,14 @@ TEST_F(AuthSrvTest, builtInQuery) {
default_qid, Name("version.bind"),
RRClass::CH(), RRType::TXT());
createRequestPacket(request_message, IPPROTO_UDP);
- server.processMessage(*io_message, parse_message, response_obuffer,
+ server.processMessage(*io_message, *parse_message, *response_obuffer,
&dnsserv);
createBuiltinVersionResponse(default_qid, response_data);
EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
response_obuffer->getData(),
response_obuffer->getLength(),
&response_data[0], response_data.size());
+ checkAllRcodeCountersZeroExcept(Rcode::NOERROR(), 1);
}
// Same test emulating the UDPServer class behavior (defined in libasiolink).
@@ -192,38 +246,46 @@ TEST_F(AuthSrvTest, iqueryViaDNSServer) {
// Unsupported requests. Should result in NOTIMP.
TEST_F(AuthSrvTest, unsupportedRequest) {
unsupportedRequest();
+ // unsupportedRequest tries 14 different opcodes
+ checkAllRcodeCountersZeroExcept(Rcode::NOTIMP(), 14);
}
// Multiple questions. Should result in FORMERR.
TEST_F(AuthSrvTest, multiQuestion) {
multiQuestion();
+ checkAllRcodeCountersZeroExcept(Rcode::FORMERR(), 1);
}
// Incoming data doesn't even contain the complete header. Must be silently
// dropped.
TEST_F(AuthSrvTest, shortMessage) {
shortMessage();
+ checkAllRcodeCountersZero();
}
// Response messages. Must be silently dropped, whether it's a valid response
// or malformed or could otherwise cause a protocol error.
TEST_F(AuthSrvTest, response) {
response();
+ checkAllRcodeCountersZero();
}
// Query with a broken question
TEST_F(AuthSrvTest, shortQuestion) {
shortQuestion();
+ checkAllRcodeCountersZeroExcept(Rcode::FORMERR(), 1);
}
// Query with a broken answer section
TEST_F(AuthSrvTest, shortAnswer) {
shortAnswer();
+ checkAllRcodeCountersZeroExcept(Rcode::FORMERR(), 1);
}
// Query with unsupported version of EDNS.
TEST_F(AuthSrvTest, ednsBadVers) {
ednsBadVers();
+ checkAllRcodeCountersZeroExcept(Rcode::BADVERS(), 1);
}
TEST_F(AuthSrvTest, AXFROverUDP) {
@@ -238,9 +300,11 @@ TEST_F(AuthSrvTest, AXFRSuccess) {
createRequestPacket(request_message, IPPROTO_TCP);
// On success, the AXFR query has been passed to a separate process,
// so we shouldn't have to respond.
- server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
+ server.processMessage(*io_message, *parse_message, *response_obuffer,
+ &dnsserv);
EXPECT_FALSE(dnsserv.hasAnswer());
EXPECT_TRUE(xfrout.isConnected());
+ checkAllRcodeCountersZero();
}
// Try giving the server a TSIG signed request and see it can anwer signed as
@@ -258,7 +322,7 @@ TEST_F(AuthSrvTest, TSIGSigned) {
boost::shared_ptr<TSIGKeyRing> keyring(new TSIGKeyRing);
keyring->add(key);
server.setTSIGKeyRing(&keyring);
- server.processMessage(*io_message, parse_message, response_obuffer,
+ server.processMessage(*io_message, *parse_message, *response_obuffer,
&dnsserv);
// What did we get?
@@ -276,6 +340,8 @@ TEST_F(AuthSrvTest, TSIGSigned) {
response_obuffer->getLength()));
EXPECT_EQ(TSIGError::NOERROR(), error) <<
"The server signed the response, but it doesn't seem to be valid";
+
+ checkAllRcodeCountersZeroExcept(Rcode::NOERROR(), 1);
}
// Give the server a signed request, but don't give it the key. It will
@@ -291,7 +357,7 @@ TEST_F(AuthSrvTest, TSIGSignedBadKey) {
// Process the message, but use a different key there
boost::shared_ptr<TSIGKeyRing> keyring(new TSIGKeyRing);
server.setTSIGKeyRing(&keyring);
- server.processMessage(*io_message, parse_message, response_obuffer,
+ server.processMessage(*io_message, *parse_message, *response_obuffer,
&dnsserv);
EXPECT_TRUE(dnsserv.hasAnswer());
@@ -308,6 +374,8 @@ TEST_F(AuthSrvTest, TSIGSignedBadKey) {
EXPECT_EQ(TSIGError::BAD_KEY_CODE, tsig->getRdata().getError());
EXPECT_EQ(0, tsig->getRdata().getMACSize()) <<
"It should be unsigned with this error";
+
+ checkAllRcodeCountersZeroExcept(Rcode::NOTAUTH(), 1);
}
// Give the server a signed request, but signed by a different key
@@ -324,7 +392,7 @@ TEST_F(AuthSrvTest, TSIGBadSig) {
boost::shared_ptr<TSIGKeyRing> keyring(new TSIGKeyRing);
keyring->add(TSIGKey("key:QkFECg==:hmac-sha1"));
server.setTSIGKeyRing(&keyring);
- server.processMessage(*io_message, parse_message, response_obuffer,
+ server.processMessage(*io_message, *parse_message, *response_obuffer,
&dnsserv);
EXPECT_TRUE(dnsserv.hasAnswer());
@@ -341,6 +409,8 @@ TEST_F(AuthSrvTest, TSIGBadSig) {
EXPECT_EQ(TSIGError::BAD_SIG_CODE, tsig->getRdata().getError());
EXPECT_EQ(0, tsig->getRdata().getMACSize()) <<
"It should be unsigned with this error";
+
+ checkAllRcodeCountersZeroExcept(Rcode::NOTAUTH(), 1);
}
// Give the server a signed unsupported request with a bad signature.
@@ -360,7 +430,7 @@ TEST_F(AuthSrvTest, TSIGCheckFirst) {
boost::shared_ptr<TSIGKeyRing> keyring(new TSIGKeyRing);
keyring->add(TSIGKey("key:QkFECg==:hmac-sha1"));
server.setTSIGKeyRing(&keyring);
- server.processMessage(*io_message, parse_message, response_obuffer,
+ server.processMessage(*io_message, *parse_message, *response_obuffer,
&dnsserv);
EXPECT_TRUE(dnsserv.hasAnswer());
@@ -380,6 +450,8 @@ TEST_F(AuthSrvTest, TSIGCheckFirst) {
// TSIG should have failed, and so the per opcode counter shouldn't be
// incremented.
EXPECT_EQ(0, server.getCounter(Opcode::RESERVED14()));
+
+ checkAllRcodeCountersZeroExcept(Rcode::NOTAUTH(), 1);
}
TEST_F(AuthSrvTest, AXFRConnectFail) {
@@ -389,7 +461,8 @@ TEST_F(AuthSrvTest, AXFRConnectFail) {
Name("example.com"), RRClass::IN(),
RRType::AXFR());
createRequestPacket(request_message, IPPROTO_TCP);
- server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
+ server.processMessage(*io_message, *parse_message, *response_obuffer,
+ &dnsserv);
EXPECT_TRUE(dnsserv.hasAnswer());
headerCheck(*parse_message, default_qid, Rcode::SERVFAIL(),
opcode.getCode(), QR_FLAG, 1, 0, 0, 0);
@@ -403,7 +476,8 @@ TEST_F(AuthSrvTest, AXFRSendFail) {
Name("example.com"), RRClass::IN(),
RRType::AXFR());
createRequestPacket(request_message, IPPROTO_TCP);
- server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
+ server.processMessage(*io_message, *parse_message, *response_obuffer,
+ &dnsserv);
EXPECT_TRUE(xfrout.isConnected());
xfrout.disableSend();
@@ -413,7 +487,8 @@ TEST_F(AuthSrvTest, AXFRSendFail) {
Name("example.com"), RRClass::IN(),
RRType::AXFR());
createRequestPacket(request_message, IPPROTO_TCP);
- server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
+ server.processMessage(*io_message, *parse_message, *response_obuffer,
+ &dnsserv);
EXPECT_TRUE(dnsserv.hasAnswer());
headerCheck(*parse_message, default_qid, Rcode::SERVFAIL(),
opcode.getCode(), QR_FLAG, 1, 0, 0, 0);
@@ -423,17 +498,17 @@ TEST_F(AuthSrvTest, AXFRSendFail) {
}
TEST_F(AuthSrvTest, AXFRDisconnectFail) {
- // In our usage disconnect() shouldn't fail. So we'll see the exception
- // should it be thrown.
+ // In our usage disconnect() shouldn't fail. But even if it does,
+ // it should not disrupt service (so processMessage should have caught it)
xfrout.disableSend();
xfrout.disableDisconnect();
UnitTestUtil::createRequestMessage(request_message, opcode, default_qid,
Name("example.com"), RRClass::IN(),
RRType::AXFR());
createRequestPacket(request_message, IPPROTO_TCP);
- EXPECT_THROW(server.processMessage(*io_message, parse_message,
- response_obuffer, &dnsserv),
- XfroutError);
+ EXPECT_NO_THROW(server.processMessage(*io_message, *parse_message,
+ *response_obuffer, &dnsserv));
+ // Since the disconnect failed, we should still be 'connected'
EXPECT_TRUE(xfrout.isConnected());
// XXX: we need to re-enable disconnect. otherwise an exception would be
// thrown via the destructor of the server.
@@ -447,7 +522,8 @@ TEST_F(AuthSrvTest, IXFRConnectFail) {
Name("example.com"), RRClass::IN(),
RRType::IXFR());
createRequestPacket(request_message, IPPROTO_TCP);
- server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
+ server.processMessage(*io_message, *parse_message, *response_obuffer,
+ &dnsserv);
EXPECT_TRUE(dnsserv.hasAnswer());
headerCheck(*parse_message, default_qid, Rcode::SERVFAIL(),
opcode.getCode(), QR_FLAG, 1, 0, 0, 0);
@@ -461,7 +537,8 @@ TEST_F(AuthSrvTest, IXFRSendFail) {
Name("example.com"), RRClass::IN(),
RRType::IXFR());
createRequestPacket(request_message, IPPROTO_TCP);
- server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
+ server.processMessage(*io_message, *parse_message, *response_obuffer,
+ &dnsserv);
EXPECT_TRUE(xfrout.isConnected());
xfrout.disableSend();
@@ -471,7 +548,8 @@ TEST_F(AuthSrvTest, IXFRSendFail) {
Name("example.com"), RRClass::IN(),
RRType::IXFR());
createRequestPacket(request_message, IPPROTO_TCP);
- server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
+ server.processMessage(*io_message, *parse_message, *response_obuffer,
+ &dnsserv);
EXPECT_TRUE(dnsserv.hasAnswer());
headerCheck(*parse_message, default_qid, Rcode::SERVFAIL(),
opcode.getCode(), QR_FLAG, 1, 0, 0, 0);
@@ -481,17 +559,16 @@ TEST_F(AuthSrvTest, IXFRSendFail) {
}
TEST_F(AuthSrvTest, IXFRDisconnectFail) {
- // In our usage disconnect() shouldn't fail. So we'll see the exception
- // should it be thrown.
+ // In our usage disconnect() shouldn't fail, but even if it does,
+ // procesMessage() should catch it.
xfrout.disableSend();
xfrout.disableDisconnect();
UnitTestUtil::createRequestMessage(request_message, opcode, default_qid,
Name("example.com"), RRClass::IN(),
RRType::IXFR());
createRequestPacket(request_message, IPPROTO_TCP);
- EXPECT_THROW(server.processMessage(*io_message, parse_message,
- response_obuffer, &dnsserv),
- XfroutError);
+ EXPECT_NO_THROW(server.processMessage(*io_message, *parse_message,
+ *response_obuffer, &dnsserv));
EXPECT_TRUE(xfrout.isConnected());
// XXX: we need to re-enable disconnect. otherwise an exception would be
// thrown via the destructor of the server.
@@ -504,7 +581,8 @@ TEST_F(AuthSrvTest, notify) {
RRClass::IN(), RRType::SOA());
request_message.setHeaderFlag(Message::HEADERFLAG_AA);
createRequestPacket(request_message, IPPROTO_UDP);
- server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
+ server.processMessage(*io_message, *parse_message, *response_obuffer,
+ &dnsserv);
EXPECT_TRUE(dnsserv.hasAnswer());
// An internal command message should have been created and sent to an
@@ -528,6 +606,8 @@ TEST_F(AuthSrvTest, notify) {
EXPECT_EQ(Name("example.com"), question->getName());
EXPECT_EQ(RRClass::IN(), question->getClass());
EXPECT_EQ(RRType::SOA(), question->getType());
+
+ checkAllRcodeCountersZeroExcept(Rcode::NOERROR(), 1);
}
TEST_F(AuthSrvTest, notifyForCHClass) {
@@ -537,7 +617,8 @@ TEST_F(AuthSrvTest, notifyForCHClass) {
RRClass::CH(), RRType::SOA());
request_message.setHeaderFlag(Message::HEADERFLAG_AA);
createRequestPacket(request_message, IPPROTO_UDP);
- server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
+ server.processMessage(*io_message, *parse_message, *response_obuffer,
+ &dnsserv);
EXPECT_TRUE(dnsserv.hasAnswer());
// Other conditions should be the same, so simply confirm the RR class is
@@ -555,7 +636,8 @@ TEST_F(AuthSrvTest, notifyEmptyQuestion) {
request_message.setQid(default_qid);
request_message.toWire(request_renderer);
createRequestPacket(request_message, IPPROTO_UDP);
- server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
+ server.processMessage(*io_message, *parse_message, *response_obuffer,
+ &dnsserv);
EXPECT_TRUE(dnsserv.hasAnswer());
headerCheck(*parse_message, default_qid, Rcode::FORMERR(),
Opcode::NOTIFY().getCode(), QR_FLAG, 0, 0, 0, 0);
@@ -570,7 +652,8 @@ TEST_F(AuthSrvTest, notifyMultiQuestions) {
RRType::SOA()));
request_message.setHeaderFlag(Message::HEADERFLAG_AA);
createRequestPacket(request_message, IPPROTO_UDP);
- server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
+ server.processMessage(*io_message, *parse_message, *response_obuffer,
+ &dnsserv);
EXPECT_TRUE(dnsserv.hasAnswer());
headerCheck(*parse_message, default_qid, Rcode::FORMERR(),
Opcode::NOTIFY().getCode(), QR_FLAG, 2, 0, 0, 0);
@@ -582,7 +665,8 @@ TEST_F(AuthSrvTest, notifyNonSOAQuestion) {
RRClass::IN(), RRType::NS());
request_message.setHeaderFlag(Message::HEADERFLAG_AA);
createRequestPacket(request_message, IPPROTO_UDP);
- server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
+ server.processMessage(*io_message, *parse_message, *response_obuffer,
+ &dnsserv);
EXPECT_TRUE(dnsserv.hasAnswer());
headerCheck(*parse_message, default_qid, Rcode::FORMERR(),
Opcode::NOTIFY().getCode(), QR_FLAG, 1, 0, 0, 0);
@@ -594,7 +678,8 @@ TEST_F(AuthSrvTest, notifyWithoutAA) {
default_qid, Name("example.com"),
RRClass::IN(), RRType::SOA());
createRequestPacket(request_message, IPPROTO_UDP);
- server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
+ server.processMessage(*io_message, *parse_message, *response_obuffer,
+ &dnsserv);
EXPECT_TRUE(dnsserv.hasAnswer());
headerCheck(*parse_message, default_qid, Rcode::NOERROR(),
Opcode::NOTIFY().getCode(), QR_FLAG | AA_FLAG, 1, 0, 0, 0);
@@ -607,7 +692,8 @@ TEST_F(AuthSrvTest, notifyWithErrorRcode) {
request_message.setHeaderFlag(Message::HEADERFLAG_AA);
request_message.setRcode(Rcode::SERVFAIL());
createRequestPacket(request_message, IPPROTO_UDP);
- server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
+ server.processMessage(*io_message, *parse_message, *response_obuffer,
+ &dnsserv);
EXPECT_TRUE(dnsserv.hasAnswer());
headerCheck(*parse_message, default_qid, Rcode::NOERROR(),
Opcode::NOTIFY().getCode(), QR_FLAG | AA_FLAG, 1, 0, 0, 0);
@@ -624,7 +710,8 @@ TEST_F(AuthSrvTest, notifyWithoutSession) {
// we simply ignore the notify and let it be resent if an internal error
// happens.
- server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
+ server.processMessage(*io_message, *parse_message, *response_obuffer,
+ &dnsserv);
EXPECT_FALSE(dnsserv.hasAnswer());
}
@@ -637,7 +724,8 @@ TEST_F(AuthSrvTest, notifySendFail) {
request_message.setHeaderFlag(Message::HEADERFLAG_AA);
createRequestPacket(request_message, IPPROTO_UDP);
- server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
+ server.processMessage(*io_message, *parse_message, *response_obuffer,
+ &dnsserv);
EXPECT_FALSE(dnsserv.hasAnswer());
}
@@ -649,7 +737,8 @@ TEST_F(AuthSrvTest, notifyReceiveFail) {
RRClass::IN(), RRType::SOA());
request_message.setHeaderFlag(Message::HEADERFLAG_AA);
createRequestPacket(request_message, IPPROTO_UDP);
- server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
+ server.processMessage(*io_message, *parse_message, *response_obuffer,
+ &dnsserv);
EXPECT_FALSE(dnsserv.hasAnswer());
}
@@ -661,7 +750,8 @@ TEST_F(AuthSrvTest, notifyWithBogusSessionMessage) {
RRClass::IN(), RRType::SOA());
request_message.setHeaderFlag(Message::HEADERFLAG_AA);
createRequestPacket(request_message, IPPROTO_UDP);
- server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
+ server.processMessage(*io_message, *parse_message, *response_obuffer,
+ &dnsserv);
EXPECT_FALSE(dnsserv.hasAnswer());
}
@@ -674,7 +764,8 @@ TEST_F(AuthSrvTest, notifyWithSessionMessageError) {
RRClass::IN(), RRType::SOA());
request_message.setHeaderFlag(Message::HEADERFLAG_AA);
createRequestPacket(request_message, IPPROTO_UDP);
- server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
+ server.processMessage(*io_message, *parse_message, *response_obuffer,
+ &dnsserv);
EXPECT_FALSE(dnsserv.hasAnswer());
}
@@ -689,7 +780,8 @@ updateConfig(AuthSrv* server, const char* const config_data,
ConstElementPtr result = config_answer->get("result");
EXPECT_EQ(Element::list, result->getType());
- EXPECT_EQ(expect_success ? 0 : 1, result->get(0)->intValue());
+ EXPECT_EQ(expect_success ? 0 : 1, result->get(0)->intValue()) <<
+ "Bad result from updateConfig: " << result->str();
}
// Install a Sqlite3 data source with testing data.
@@ -700,7 +792,8 @@ TEST_F(AuthSrvTest, updateConfig) {
// response should have the AA flag on, and have an RR in each answer
// and authority section.
createDataFromFile("examplequery_fromWire.wire");
- server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
+ server.processMessage(*io_message, *parse_message, *response_obuffer,
+ &dnsserv);
EXPECT_TRUE(dnsserv.hasAnswer());
headerCheck(*parse_message, default_qid, Rcode::NOERROR(), opcode.getCode(),
QR_FLAG | AA_FLAG, 1, 1, 1, 0);
@@ -714,7 +807,8 @@ TEST_F(AuthSrvTest, datasourceFail) {
// in a SERVFAIL response, and the answer and authority sections should
// be empty.
createDataFromFile("badExampleQuery_fromWire.wire");
- server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
+ server.processMessage(*io_message, *parse_message, *response_obuffer,
+ &dnsserv);
EXPECT_TRUE(dnsserv.hasAnswer());
headerCheck(*parse_message, default_qid, Rcode::SERVFAIL(),
opcode.getCode(), QR_FLAG, 1, 0, 0, 0);
@@ -729,7 +823,8 @@ TEST_F(AuthSrvTest, updateConfigFail) {
// The original data source should still exist.
createDataFromFile("examplequery_fromWire.wire");
- server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
+ server.processMessage(*io_message, *parse_message, *response_obuffer,
+ &dnsserv);
EXPECT_TRUE(dnsserv.hasAnswer());
headerCheck(*parse_message, default_qid, Rcode::NOERROR(), opcode.getCode(),
QR_FLAG | AA_FLAG, 1, 1, 1, 0);
@@ -749,13 +844,48 @@ TEST_F(AuthSrvTest, updateWithInMemoryClient) {
// The memory data source is empty, should return REFUSED rcode.
createDataFromFile("examplequery_fromWire.wire");
- server.processMessage(*io_message, parse_message, response_obuffer,
+ server.processMessage(*io_message, *parse_message, *response_obuffer,
&dnsserv);
EXPECT_TRUE(dnsserv.hasAnswer());
headerCheck(*parse_message, default_qid, Rcode::REFUSED(),
opcode.getCode(), QR_FLAG, 1, 0, 0, 0);
}
+TEST_F(AuthSrvTest, queryWithInMemoryClientNoDNSSEC) {
+ // In this example, we do simple check that query is handled from the
+ // query handler class, and confirm it returns no error and a non empty
+ // answer section. Detailed examination on the response content
+ // for various types of queries are tested in the query tests.
+ updateConfig(&server, CONFIG_INMEMORY_EXAMPLE, true);
+ ASSERT_NE(AuthSrv::InMemoryClientPtr(), server.getInMemoryClient(rrclass));
+ EXPECT_EQ(1, server.getInMemoryClient(rrclass)->getZoneCount());
+
+ createDataFromFile("nsec3query_nodnssec_fromWire.wire");
+ server.processMessage(*io_message, *parse_message, *response_obuffer,
+ &dnsserv);
+
+ EXPECT_TRUE(dnsserv.hasAnswer());
+ headerCheck(*parse_message, default_qid, Rcode::NOERROR(),
+ opcode.getCode(), QR_FLAG | AA_FLAG, 1, 1, 2, 1);
+}
+
+TEST_F(AuthSrvTest, queryWithInMemoryClientDNSSEC) {
+ // Similar to the previous test, but the query has the DO bit on.
+ // The response should contain RRSIGs, and should have more RRs than
+ // the previous case.
+ updateConfig(&server, CONFIG_INMEMORY_EXAMPLE, true);
+ ASSERT_NE(AuthSrv::InMemoryClientPtr(), server.getInMemoryClient(rrclass));
+ EXPECT_EQ(1, server.getInMemoryClient(rrclass)->getZoneCount());
+
+ createDataFromFile("nsec3query_fromWire.wire");
+ server.processMessage(*io_message, *parse_message, *response_obuffer,
+ &dnsserv);
+
+ EXPECT_TRUE(dnsserv.hasAnswer());
+ headerCheck(*parse_message, default_qid, Rcode::NOERROR(),
+ opcode.getCode(), QR_FLAG | AA_FLAG, 1, 2, 3, 3);
+}
+
TEST_F(AuthSrvTest, chQueryWithInMemoryClient) {
// Configure memory data source for class IN
updateConfig(&server, "{\"datasources\": "
@@ -766,7 +896,7 @@ TEST_F(AuthSrvTest, chQueryWithInMemoryClient) {
default_qid, Name("version.bind"),
RRClass::CH(), RRType::TXT());
createRequestPacket(request_message, IPPROTO_UDP);
- server.processMessage(*io_message, parse_message, response_obuffer,
+ server.processMessage(*io_message, *parse_message, *response_obuffer,
&dnsserv);
EXPECT_TRUE(dnsserv.hasAnswer());
headerCheck(*parse_message, default_qid, Rcode::NOERROR(),
@@ -792,10 +922,14 @@ TEST_F(AuthSrvTest, queryCounterUDPNormal) {
default_qid, Name("example.com"),
RRClass::IN(), RRType::NS());
createRequestPacket(request_message, IPPROTO_UDP);
- server.processMessage(*io_message, parse_message, response_obuffer,
+ server.processMessage(*io_message, *parse_message, *response_obuffer,
&dnsserv);
// After processing UDP query, the counter should be 1.
EXPECT_EQ(1, server.getCounter(AuthCounters::SERVER_UDP_QUERY));
+ // The counter for opcode Query should also be one
+ EXPECT_EQ(1, server.getCounter(Opcode::QUERY()));
+ // The counter for REFUSED responses should also be one, the rest zero
+ checkAllRcodeCountersZeroExcept(Rcode::REFUSED(), 1);
}
// Submit TCP normal query and check query counter
@@ -807,10 +941,14 @@ TEST_F(AuthSrvTest, queryCounterTCPNormal) {
default_qid, Name("example.com"),
RRClass::IN(), RRType::NS());
createRequestPacket(request_message, IPPROTO_TCP);
- server.processMessage(*io_message, parse_message, response_obuffer,
+ server.processMessage(*io_message, *parse_message, *response_obuffer,
&dnsserv);
// After processing TCP query, the counter should be 1.
EXPECT_EQ(1, server.getCounter(AuthCounters::SERVER_TCP_QUERY));
+ // The counter for SUCCESS responses should also be one
+ EXPECT_EQ(1, server.getCounter(Opcode::QUERY()));
+ // The counter for REFUSED responses should also be one, the rest zero
+ checkAllRcodeCountersZeroExcept(Rcode::REFUSED(), 1);
}
// Submit TCP AXFR query and check query counter
@@ -822,10 +960,13 @@ TEST_F(AuthSrvTest, queryCounterTCPAXFR) {
createRequestPacket(request_message, IPPROTO_TCP);
// On success, the AXFR query has been passed to a separate process,
// so auth itself shouldn't respond.
- server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
+ server.processMessage(*io_message, *parse_message, *response_obuffer,
+ &dnsserv);
EXPECT_FALSE(dnsserv.hasAnswer());
// After processing TCP AXFR query, the counter should be 1.
EXPECT_EQ(1, server.getCounter(AuthCounters::SERVER_TCP_QUERY));
+ // No rcodes should be incremented
+ checkAllRcodeCountersZero();
}
// Submit TCP IXFR query and check query counter
@@ -837,7 +978,8 @@ TEST_F(AuthSrvTest, queryCounterTCPIXFR) {
createRequestPacket(request_message, IPPROTO_TCP);
// On success, the IXFR query has been passed to a separate process,
// so auth itself shouldn't respond.
- server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
+ server.processMessage(*io_message, *parse_message, *response_obuffer,
+ &dnsserv);
EXPECT_FALSE(dnsserv.hasAnswer());
// After processing TCP IXFR query, the counter should be 1.
EXPECT_EQ(1, server.getCounter(AuthCounters::SERVER_TCP_QUERY));
@@ -858,7 +1000,8 @@ TEST_F(AuthSrvTest, queryCounterOpcodes) {
// we intentionally use different values for each code
for (int j = 0; j <= i; ++j) {
parse_message->clear(Message::PARSE);
- server.processMessage(*io_message, parse_message, response_obuffer,
+ server.processMessage(*io_message, *parse_message,
+ *response_obuffer,
&dnsserv);
}
@@ -884,11 +1027,10 @@ getDummyUnknownSocket() {
return (socket);
}
-// Submit unexpected type of query and check it throws isc::Unexpected
+// Submit unexpected type of query and check it is ignored
TEST_F(AuthSrvTest, queryCounterUnexpected) {
// This code isn't exception safe, but we'd rather keep the code
- // simpler and more readable as this is only for tests and if it throws
- // the program would immediately terminate anyway.
+ // simpler and more readable as this is only for tests
// Create UDP query packet.
UnitTestUtil::createRequestMessage(request_message, Opcode::QUERY(),
@@ -904,9 +1046,7 @@ TEST_F(AuthSrvTest, queryCounterUnexpected) {
request_renderer.getLength(),
getDummyUnknownSocket(), *endpoint);
- EXPECT_THROW(server.processMessage(*io_message, parse_message,
- response_obuffer, &dnsserv),
- isc::Unexpected);
+ EXPECT_FALSE(dnsserv.hasAnswer());
}
TEST_F(AuthSrvTest, stop) {
@@ -935,4 +1075,231 @@ TEST_F(AuthSrvTest, listenAddresses) {
"Released tokens");
}
+//
+// Tests for catching exceptions in various stages of the query processing
+//
+// These tests work by defining two proxy classes, that act as an in-memory
+// client by default, but can throw exceptions at various points.
+//
+namespace {
+
+/// A the possible methods to throw in, either in FakeInMemoryClient or
+/// FakeZoneFinder
+enum ThrowWhen {
+ THROW_NEVER,
+ THROW_AT_FIND_ZONE,
+ THROW_AT_GET_ORIGIN,
+ THROW_AT_GET_CLASS,
+ THROW_AT_FIND,
+ THROW_AT_FIND_ALL,
+ THROW_AT_FIND_NSEC3
+};
+
+/// convenience function to check whether and what to throw
+void
+checkThrow(ThrowWhen method, ThrowWhen throw_at, bool isc_exception) {
+ if (method == throw_at) {
+ if (isc_exception) {
+ isc_throw(isc::Exception, "foo");
+ } else {
+ throw std::exception();
+ }
+ }
+}
+
+/// \brief proxy class for the ZoneFinder returned by the InMemoryClient
+/// proxied by FakeInMemoryClient
+///
+/// See the documentation for FakeInMemoryClient for more information,
+/// all methods simply check whether they should throw, and if not, call
+/// their proxied equivalent.
+class FakeZoneFinder : public isc::datasrc::ZoneFinder {
+public:
+ FakeZoneFinder(isc::datasrc::ZoneFinderPtr zone_finder,
+ ThrowWhen throw_when,
+ bool isc_exception) :
+ real_zone_finder_(zone_finder),
+ throw_when_(throw_when),
+ isc_exception_(isc_exception)
+ {}
+
+ virtual isc::dns::Name
+ getOrigin() const {
+ checkThrow(THROW_AT_GET_ORIGIN, throw_when_, isc_exception_);
+ return (real_zone_finder_->getOrigin());
+ }
+
+ virtual isc::dns::RRClass
+ getClass() const {
+ checkThrow(THROW_AT_GET_CLASS, throw_when_, isc_exception_);
+ return (real_zone_finder_->getClass());
+ }
+
+ virtual isc::datasrc::ZoneFinderContextPtr
+ find(const isc::dns::Name& name,
+ const isc::dns::RRType& type,
+ isc::datasrc::ZoneFinder::FindOptions options)
+ {
+ checkThrow(THROW_AT_FIND, throw_when_, isc_exception_);
+ return (real_zone_finder_->find(name, type, options));
+ }
+
+ virtual isc::datasrc::ZoneFinderContextPtr
+ findAll(const isc::dns::Name& name,
+ std::vector<isc::dns::ConstRRsetPtr> &target,
+ const FindOptions options = FIND_DEFAULT)
+ {
+ checkThrow(THROW_AT_FIND_ALL, throw_when_, isc_exception_);
+ return (real_zone_finder_->findAll(name, target, options));
+ }
+
+ virtual FindNSEC3Result
+ findNSEC3(const isc::dns::Name& name, bool recursive) {
+ checkThrow(THROW_AT_FIND_NSEC3, throw_when_, isc_exception_);
+ return (real_zone_finder_->findNSEC3(name, recursive));
+ }
+
+ virtual isc::dns::Name
+ findPreviousName(const isc::dns::Name& query) const {
+ return (real_zone_finder_->findPreviousName(query));
+ }
+
+private:
+ isc::datasrc::ZoneFinderPtr real_zone_finder_;
+ ThrowWhen throw_when_;
+ bool isc_exception_;
+};
+
+/// \brief Proxy InMemoryClient that can throw exceptions at specified times
+///
+/// It is based on the memory client since that one is easy to override
+/// (with setInMemoryClient) with the current design of AuthSrv.
+class FakeInMemoryClient : public isc::datasrc::InMemoryClient {
+public:
+ /// \brief Create a proxy memory client
+ ///
+ /// \param real_client The real in-memory client to proxy
+ /// \param throw_when if set to any value other than never, that is
+ /// the method that will throw an exception (either in this
+ /// class or the related FakeZoneFinder)
+ /// \param isc_exception if true, throw isc::Exception, otherwise,
+ /// throw std::exception
+ FakeInMemoryClient(AuthSrv::InMemoryClientPtr real_client,
+ ThrowWhen throw_when,
+ bool isc_exception) :
+ real_client_(real_client),
+ throw_when_(throw_when),
+ isc_exception_(isc_exception)
+ {}
+
+ /// \brief proxy call for findZone
+ ///
+ /// if this instance was constructed with throw_when set to find_zone,
+ /// this method will throw. Otherwise, it will return a FakeZoneFinder
+ /// instance which will throw at the method specified at the
+ /// construction of this instance.
+ virtual FindResult
+ findZone(const isc::dns::Name& name) const {
+ checkThrow(THROW_AT_FIND_ZONE, throw_when_, isc_exception_);
+ const FindResult result = real_client_->findZone(name);
+ return (FindResult(result.code, isc::datasrc::ZoneFinderPtr(
+ new FakeZoneFinder(result.zone_finder,
+ throw_when_,
+ isc_exception_))));
+ }
+
+private:
+ AuthSrv::InMemoryClientPtr real_client_;
+ ThrowWhen throw_when_;
+ bool isc_exception_;
+};
+
+} // end anonymous namespace for throwing proxy classes
+
+// Test for the tests
+//
+// Set the proxies to never throw, this should have the same result as
+// queryWithInMemoryClientNoDNSSEC, and serves to test the two proxy classes
+TEST_F(AuthSrvTest, queryWithInMemoryClientProxy) {
+ // Set real inmem client to proxy
+ updateConfig(&server, CONFIG_INMEMORY_EXAMPLE, true);
+
+ AuthSrv::InMemoryClientPtr fake_client(
+ new FakeInMemoryClient(server.getInMemoryClient(rrclass),
+ THROW_NEVER,
+ false));
+
+ ASSERT_NE(AuthSrv::InMemoryClientPtr(), server.getInMemoryClient(rrclass));
+ server.setInMemoryClient(rrclass, fake_client);
+
+ createDataFromFile("nsec3query_nodnssec_fromWire.wire");
+ server.processMessage(*io_message, *parse_message, *response_obuffer,
+ &dnsserv);
+
+ EXPECT_TRUE(dnsserv.hasAnswer());
+ headerCheck(*parse_message, default_qid, Rcode::NOERROR(),
+ opcode.getCode(), QR_FLAG | AA_FLAG, 1, 1, 2, 1);
+}
+
+// Convenience function for the rest of the tests, set up a proxy
+// to throw in the given method
+// If isc_exception is true, it will throw isc::Exception, otherwise
+// it will throw std::exception
+void
+setupThrow(AuthSrv* server, const char *config, ThrowWhen throw_when,
+ bool isc_exception)
+{
+ // Set real inmem client to proxy
+ updateConfig(server, config, true);
+
+ // Set it to throw on findZone(), this should result in
+ // SERVFAIL on any exception
+ AuthSrv::InMemoryClientPtr fake_client(
+ new FakeInMemoryClient(
+ server->getInMemoryClient(isc::dns::RRClass::IN()),
+ throw_when,
+ isc_exception));
+
+ ASSERT_NE(AuthSrv::InMemoryClientPtr(),
+ server->getInMemoryClient(isc::dns::RRClass::IN()));
+ server->setInMemoryClient(isc::dns::RRClass::IN(), fake_client);
+}
+
+TEST_F(AuthSrvTest, queryWithThrowingProxyServfails) {
+ // Test the common cases, all of which should simply return SERVFAIL
+ // Use THROW_NEVER as end marker
+ ThrowWhen throws[] = { THROW_AT_FIND_ZONE,
+ THROW_AT_GET_ORIGIN,
+ THROW_AT_FIND,
+ THROW_AT_FIND_NSEC3,
+ THROW_NEVER };
+ UnitTestUtil::createDNSSECRequestMessage(request_message, opcode,
+ default_qid, Name("foo.example."),
+ RRClass::IN(), RRType::TXT());
+ for (ThrowWhen* when(throws); *when != THROW_NEVER; ++when) {
+ createRequestPacket(request_message, IPPROTO_UDP);
+ setupThrow(&server, CONFIG_INMEMORY_EXAMPLE, *when, true);
+ processAndCheckSERVFAIL();
+ // To be sure, check same for non-isc-exceptions
+ createRequestPacket(request_message, IPPROTO_UDP);
+ setupThrow(&server, CONFIG_INMEMORY_EXAMPLE, *when, false);
+ processAndCheckSERVFAIL();
+ }
+}
+
+// Throw isc::Exception in getClass(). (Currently?) getClass is not called
+// in the processMessage path, so this should result in a normal answer
+TEST_F(AuthSrvTest, queryWithInMemoryClientProxyGetClass) {
+ createDataFromFile("nsec3query_nodnssec_fromWire.wire");
+ setupThrow(&server, CONFIG_INMEMORY_EXAMPLE, THROW_AT_GET_CLASS, true);
+
+ // getClass is not called so it should just answer
+ server.processMessage(*io_message, *parse_message, *response_obuffer,
+ &dnsserv);
+
+ EXPECT_TRUE(dnsserv.hasAnswer());
+ headerCheck(*parse_message, default_qid, Rcode::NOERROR(),
+ opcode.getCode(), QR_FLAG | AA_FLAG, 1, 1, 2, 1);
+}
+
}
diff --git a/src/bin/auth/tests/command_unittest.cc b/src/bin/auth/tests/command_unittest.cc
index 8a82367..093afda 100644
--- a/src/bin/auth/tests/command_unittest.cc
+++ b/src/bin/auth/tests/command_unittest.cc
@@ -14,14 +14,9 @@
#include <config.h>
-#include <cassert>
-#include <cstdlib>
-#include <string>
-#include <stdexcept>
-
-#include <boost/bind.hpp>
-
-#include <gtest/gtest.h>
+#include <auth/auth_srv.h>
+#include <auth/auth_config.h>
+#include <auth/command.h>
#include <dns/name.h>
#include <dns/rrclass.h>
@@ -33,14 +28,22 @@
#include <datasrc/memory_datasrc.h>
-#include <auth/auth_srv.h>
-#include <auth/auth_config.h>
-#include <auth/command.h>
-
#include <asiolink/asiolink.h>
#include <testutils/mockups.h>
+#include <cassert>
+#include <cstdlib>
+#include <string>
+#include <stdexcept>
+
+#include <boost/bind.hpp>
+
+#include <gtest/gtest.h>
+
+#include <sys/types.h>
+#include <unistd.h>
+
using namespace std;
using namespace isc::dns;
using namespace isc::data;
@@ -50,58 +53,119 @@ using namespace isc::config;
namespace {
class AuthCommandTest : public ::testing::Test {
protected:
- AuthCommandTest() : server(false, xfrout), rcode(-1) {
- server.setStatisticsSession(&statistics_session);
+ AuthCommandTest() :
+ server_(false, xfrout_),
+ rcode_(-1),
+ expect_rcode_(0),
+ itimer_(server_.getIOService())
+ {
+ server_.setStatisticsSession(&statistics_session_);
}
void checkAnswer(const int expected_code) {
- parseAnswer(rcode, result);
- EXPECT_EQ(expected_code, rcode);
+ parseAnswer(rcode_, result_);
+ EXPECT_EQ(expected_code, rcode_);
}
- MockSession statistics_session;
- MockXfroutClient xfrout;
- AuthSrv server;
- ConstElementPtr result;
- int rcode;
+ MockSession statistics_session_;
+ MockXfroutClient xfrout_;
+ AuthSrv server_;
+ ConstElementPtr result_;
+ // The shutdown command parameter
+ ConstElementPtr param_;
+ int rcode_, expect_rcode_;
+ isc::asiolink::IntervalTimer itimer_;
public:
void stopServer(); // need to be public for boost::bind
+ void dontStopServer(); // need to be public for boost::bind
};
TEST_F(AuthCommandTest, unknownCommand) {
- result = execAuthServerCommand(server, "no_such_command",
- ConstElementPtr());
- parseAnswer(rcode, result);
- EXPECT_EQ(1, rcode);
+ result_ = execAuthServerCommand(server_, "no_such_command",
+ ConstElementPtr());
+ parseAnswer(rcode_, result_);
+ EXPECT_EQ(1, rcode_);
}
TEST_F(AuthCommandTest, DISABLED_unexpectedException) {
// execAuthServerCommand() won't catch standard exceptions.
// Skip this test for now: ModuleCCSession doesn't seem to validate
// commands.
- EXPECT_THROW(execAuthServerCommand(server, "_throw_exception",
+ EXPECT_THROW(execAuthServerCommand(server_, "_throw_exception",
ConstElementPtr()),
runtime_error);
}
TEST_F(AuthCommandTest, sendStatistics) {
- result = execAuthServerCommand(server, "sendstats", ConstElementPtr());
+ result_ = execAuthServerCommand(server_, "sendstats", ConstElementPtr());
// Just check some message has been sent. Detailed tests specific to
// statistics are done in its own tests.
- EXPECT_EQ("Stats", statistics_session.getMessageDest());
+ EXPECT_EQ("Stats", statistics_session_.getMessageDest());
checkAnswer(0);
}
void
AuthCommandTest::stopServer() {
- result = execAuthServerCommand(server, "shutdown", ConstElementPtr());
- parseAnswer(rcode, result);
- assert(rcode == 0); // make sure the test stops when something is wrong
+ result_ = execAuthServerCommand(server_, "shutdown", param_);
+ parseAnswer(rcode_, result_);
+ assert(rcode_ == 0); // make sure the test stops when something is wrong
}
TEST_F(AuthCommandTest, shutdown) {
- isc::asiolink::IntervalTimer itimer(server.getIOService());
- itimer.setup(boost::bind(&AuthCommandTest::stopServer, this), 1);
- server.getIOService().run();
- EXPECT_EQ(0, rcode);
+ // Param defaults to empty/null pointer on creation
+ itimer_.setup(boost::bind(&AuthCommandTest::stopServer, this), 1);
+ server_.getIOService().run();
+ EXPECT_EQ(0, rcode_);
+}
+
+TEST_F(AuthCommandTest, shutdownCorrectPID) {
+ // Put the pid parameter there
+ const pid_t pid(getpid());
+ ElementPtr param(new isc::data::MapElement());
+ param->set("pid", ConstElementPtr(new isc::data::IntElement(pid)));
+ param_ = param;
+ // With the correct PID, it should act exactly the same as in case
+ // of no parameter
+ itimer_.setup(boost::bind(&AuthCommandTest::stopServer, this), 1);
+ server_.getIOService().run();
+ EXPECT_EQ(0, rcode_);
+}
+
+// This is like stopServer, but the server should not stop after the
+// command, it should be running
+void
+AuthCommandTest::dontStopServer() {
+ result_ = execAuthServerCommand(server_, "shutdown", param_);
+ parseAnswer(rcode_, result_);
+ EXPECT_EQ(expect_rcode_, rcode_);
+ rcode_ = -1;
+ // We run the stopServer now, to really stop the server.
+ // If it had stopped already, it won't be run and the rcode -1 will
+ // be left here.
+ param_ = ConstElementPtr();
+ itimer_.cancel();
+ itimer_.setup(boost::bind(&AuthCommandTest::stopServer, this), 1);
+}
+
+// If we provide something not an int, the PID is not really specified, so
+// act as if nothing came.
+TEST_F(AuthCommandTest, shutdownNotInt) {
+ // Put the pid parameter there
+ ElementPtr param(new isc::data::MapElement());
+ param->set("pid", ConstElementPtr(new isc::data::StringElement("pid")));
+ param_ = param;
+ expect_rcode_ = 1;
+ // It should reject to stop if the PID is not an int.
+ itimer_.setup(boost::bind(&AuthCommandTest::dontStopServer, this), 1);
+ server_.getIOService().run();
+ EXPECT_EQ(0, rcode_);
+}
+
+TEST_F(AuthCommandTest, shutdownIncorrectPID) {
+ // The PID = 0 should be taken by init, so we are not init and the
+ // PID should be different
+ param_ = Element::fromJSON("{\"pid\": 0}");
+ itimer_.setup(boost::bind(&AuthCommandTest::dontStopServer, this), 1);
+ server_.getIOService().run();
+ EXPECT_EQ(0, rcode_);
}
// A helper function commonly used for the "loadzone" command tests.
@@ -112,16 +176,16 @@ zoneChecks(AuthSrv& server) {
EXPECT_TRUE(server.getInMemoryClient(RRClass::IN()));
EXPECT_EQ(ZoneFinder::SUCCESS, server.getInMemoryClient(RRClass::IN())->
findZone(Name("ns.test1.example")).zone_finder->
- find(Name("ns.test1.example"), RRType::A()).code);
+ find(Name("ns.test1.example"), RRType::A())->code);
EXPECT_EQ(ZoneFinder::NXRRSET, server.getInMemoryClient(RRClass::IN())->
findZone(Name("ns.test1.example")).zone_finder->
- find(Name("ns.test1.example"), RRType::AAAA()).code);
+ find(Name("ns.test1.example"), RRType::AAAA())->code);
EXPECT_EQ(ZoneFinder::SUCCESS, server.getInMemoryClient(RRClass::IN())->
findZone(Name("ns.test2.example")).zone_finder->
- find(Name("ns.test2.example"), RRType::A()).code);
+ find(Name("ns.test2.example"), RRType::A())->code);
EXPECT_EQ(ZoneFinder::NXRRSET, server.getInMemoryClient(RRClass::IN())->
findZone(Name("ns.test2.example")).zone_finder->
- find(Name("ns.test2.example"), RRType::AAAA()).code);
+ find(Name("ns.test2.example"), RRType::AAAA())->code);
}
void
@@ -149,23 +213,23 @@ newZoneChecks(AuthSrv& server) {
EXPECT_TRUE(server.getInMemoryClient(RRClass::IN()));
EXPECT_EQ(ZoneFinder::SUCCESS, server.getInMemoryClient(RRClass::IN())->
findZone(Name("ns.test1.example")).zone_finder->
- find(Name("ns.test1.example"), RRType::A()).code);
+ find(Name("ns.test1.example"), RRType::A())->code);
// now test1.example should have ns/AAAA
EXPECT_EQ(ZoneFinder::SUCCESS, server.getInMemoryClient(RRClass::IN())->
findZone(Name("ns.test1.example")).zone_finder->
- find(Name("ns.test1.example"), RRType::AAAA()).code);
+ find(Name("ns.test1.example"), RRType::AAAA())->code);
// test2.example shouldn't change
EXPECT_EQ(ZoneFinder::SUCCESS, server.getInMemoryClient(RRClass::IN())->
findZone(Name("ns.test2.example")).zone_finder->
- find(Name("ns.test2.example"), RRType::A()).code);
+ find(Name("ns.test2.example"), RRType::A())->code);
EXPECT_EQ(ZoneFinder::NXRRSET, server.getInMemoryClient(RRClass::IN())->
findZone(Name("ns.test2.example")).zone_finder->
- find(Name("ns.test2.example"), RRType::AAAA()).code);
+ find(Name("ns.test2.example"), RRType::AAAA())->code);
}
TEST_F(AuthCommandTest, loadZone) {
- configureZones(server);
+ configureZones(server_);
ASSERT_EQ(0, system(INSTALL_PROG " " TEST_DATA_DIR
"/test1-new.zone.in "
@@ -174,118 +238,118 @@ TEST_F(AuthCommandTest, loadZone) {
"/test2-new.zone.in "
TEST_DATA_BUILDDIR "/test2.zone.copied"));
- result = execAuthServerCommand(server, "loadzone",
- Element::fromJSON(
- "{\"origin\": \"test1.example\"}"));
+ result_ = execAuthServerCommand(server_, "loadzone",
+ Element::fromJSON(
+ "{\"origin\": \"test1.example\"}"));
checkAnswer(0);
- newZoneChecks(server);
+ newZoneChecks(server_);
}
TEST_F(AuthCommandTest, loadBrokenZone) {
- configureZones(server);
+ configureZones(server_);
ASSERT_EQ(0, system(INSTALL_PROG " " TEST_DATA_DIR
"/test1-broken.zone.in "
TEST_DATA_BUILDDIR "/test1.zone.copied"));
- result = execAuthServerCommand(server, "loadzone",
- Element::fromJSON(
- "{\"origin\": \"test1.example\"}"));
+ result_ = execAuthServerCommand(server_, "loadzone",
+ Element::fromJSON(
+ "{\"origin\": \"test1.example\"}"));
checkAnswer(1);
- zoneChecks(server); // zone shouldn't be replaced
+ zoneChecks(server_); // zone shouldn't be replaced
}
TEST_F(AuthCommandTest, loadUnreadableZone) {
- configureZones(server);
+ configureZones(server_);
// install the zone file as unreadable
ASSERT_EQ(0, system(INSTALL_PROG " -m 000 " TEST_DATA_DIR
"/test1.zone.in "
TEST_DATA_BUILDDIR "/test1.zone.copied"));
- result = execAuthServerCommand(server, "loadzone",
- Element::fromJSON(
- "{\"origin\": \"test1.example\"}"));
+ result_ = execAuthServerCommand(server_, "loadzone",
+ Element::fromJSON(
+ "{\"origin\": \"test1.example\"}"));
checkAnswer(1);
- zoneChecks(server); // zone shouldn't be replaced
+ zoneChecks(server_); // zone shouldn't be replaced
}
TEST_F(AuthCommandTest, loadZoneWithoutDataSrc) {
// try to execute load command without configuring the zone beforehand.
// it should fail.
- result = execAuthServerCommand(server, "loadzone",
- Element::fromJSON(
- "{\"origin\": \"test1.example\"}"));
+ result_ = execAuthServerCommand(server_, "loadzone",
+ Element::fromJSON(
+ "{\"origin\": \"test1.example\"}"));
checkAnswer(1);
}
TEST_F(AuthCommandTest, loadSqlite3DataSrc) {
// For sqlite3 data source we don't have to do anything (the data source
// (re)loads itself automatically)
- result = execAuthServerCommand(server, "loadzone",
- Element::fromJSON(
- "{\"origin\": \"test1.example\","
- " \"datasrc\": \"sqlite3\"}"));
+ result_ = execAuthServerCommand(server_, "loadzone",
+ Element::fromJSON(
+ "{\"origin\": \"test1.example\","
+ " \"datasrc\": \"sqlite3\"}"));
checkAnswer(0);
}
TEST_F(AuthCommandTest, loadZoneInvalidParams) {
- configureZones(server);
+ configureZones(server_);
// null arg
- result = execAuthServerCommand(server, "loadzone", ElementPtr());
+ result_ = execAuthServerCommand(server_, "loadzone", ElementPtr());
checkAnswer(1);
// zone class is bogus
- result = execAuthServerCommand(server, "loadzone",
- Element::fromJSON(
- "{\"origin\": \"test1.example\","
- " \"class\": \"no_such_class\"}"));
+ result_ = execAuthServerCommand(server_, "loadzone",
+ Element::fromJSON(
+ "{\"origin\": \"test1.example\","
+ " \"class\": \"no_such_class\"}"));
checkAnswer(1);
- result = execAuthServerCommand(server, "loadzone",
- Element::fromJSON(
- "{\"origin\": \"test1.example\","
- " \"class\": 1}"));
+ result_ = execAuthServerCommand(server_, "loadzone",
+ Element::fromJSON(
+ "{\"origin\": \"test1.example\","
+ " \"class\": 1}"));
checkAnswer(1);
// unsupported zone class
- result = execAuthServerCommand(server, "loadzone",
- Element::fromJSON(
- "{\"origin\": \"test1.example\","
- " \"class\": \"CH\"}"));
+ result_ = execAuthServerCommand(server_, "loadzone",
+ Element::fromJSON(
+ "{\"origin\": \"test1.example\","
+ " \"class\": \"CH\"}"));
checkAnswer(1);
// unsupported data source class
- result = execAuthServerCommand(server, "loadzone",
- Element::fromJSON(
- "{\"origin\": \"test1.example\","
- " \"datasrc\": \"not supported\"}"));
+ result_ = execAuthServerCommand(server_, "loadzone",
+ Element::fromJSON(
+ "{\"origin\": \"test1.example\","
+ " \"datasrc\": \"not supported\"}"));
checkAnswer(1);
// data source is bogus
- result = execAuthServerCommand(server, "loadzone",
- Element::fromJSON(
- "{\"origin\": \"test1.example\","
- " \"datasrc\": 0}"));
+ result_ = execAuthServerCommand(server_, "loadzone",
+ Element::fromJSON(
+ "{\"origin\": \"test1.example\","
+ " \"datasrc\": 0}"));
checkAnswer(1);
// origin is missing
- result = execAuthServerCommand(server, "loadzone",
- Element::fromJSON("{}"));
+ result_ = execAuthServerCommand(server_, "loadzone",
+ Element::fromJSON("{}"));
checkAnswer(1);
// zone doesn't exist in the data source
- result = execAuthServerCommand(server, "loadzone",
- Element::fromJSON("{\"origin\": \"xx\"}"));
+ result_ = execAuthServerCommand(server_, "loadzone",
+ Element::fromJSON("{\"origin\": \"xx\"}"));
checkAnswer(1);
// origin is bogus
- result = execAuthServerCommand(server, "loadzone",
- Element::fromJSON(
- "{\"origin\": \"...\"}"));
+ result_ = execAuthServerCommand(server_, "loadzone",
+ Element::fromJSON(
+ "{\"origin\": \"...\"}"));
checkAnswer(1);
- result = execAuthServerCommand(server, "loadzone",
- Element::fromJSON("{\"origin\": 10}"));
+ result_ = execAuthServerCommand(server_, "loadzone",
+ Element::fromJSON("{\"origin\": 10}"));
checkAnswer(1);
}
}
diff --git a/src/bin/auth/tests/config_unittest.cc b/src/bin/auth/tests/config_unittest.cc
index 18092c1..fcf88b1 100644
--- a/src/bin/auth/tests/config_unittest.cc
+++ b/src/bin/auth/tests/config_unittest.cc
@@ -46,7 +46,10 @@ protected:
dnss_(ios_, NULL, NULL, NULL),
rrclass(RRClass::IN()),
server(true, xfrout),
- sock_requestor_(dnss_, address_store_, 53210)
+ // The empty string is expected value of the parameter of
+ // requestSocket, not the app_name (there's no fallback, it checks
+ // the empty string is passed).
+ sock_requestor_(dnss_, address_store_, 53210, "")
{
server.setDNSService(dnss_);
}
@@ -188,7 +191,7 @@ TEST_F(MemoryDatasrcConfigTest, addOneZone) {
// Check it actually loaded something
EXPECT_EQ(ZoneFinder::SUCCESS, server.getInMemoryClient(rrclass)->findZone(
Name("ns.example.com.")).zone_finder->find(Name("ns.example.com."),
- RRType::A()).code);
+ RRType::A())->code);
}
TEST_F(MemoryDatasrcConfigTest, addMultiZones) {
diff --git a/src/bin/auth/tests/query_unittest.cc b/src/bin/auth/tests/query_unittest.cc
index 0f330f5..3c901aa 100644
--- a/src/bin/auth/tests/query_unittest.cc
+++ b/src/bin/auth/tests/query_unittest.cc
@@ -52,11 +52,23 @@ namespace {
// dns::masterLoad(). Some of the RRs are also used as the expected
// data in specific tests, in which case they are referenced via specific
// local variables (such as soa_txt).
-const char* const soa_txt = "example.com. 3600 IN SOA . . 0 0 0 0 0\n";
+//
+// For readability consistency, all strings are placed in a separate line,
+// even if they are very short and can reasonably fit in a single line with
+// the corresponding variable. For example, we write
+// const char* const foo_txt =
+// "foo.example.com. 3600 IN AAAA 2001:db8::1\n";
+// instead of
+// const char* const foo_txt = "foo.example.com. 3600 IN AAAA 2001:db8::1\n";
+const char* const soa_txt =
+ "example.com. 3600 IN SOA . . 0 0 0 0 0\n";
const char* const zone_ns_txt =
"example.com. 3600 IN NS glue.delegation.example.com.\n"
"example.com. 3600 IN NS noglue.example.com.\n"
"example.com. 3600 IN NS example.net.\n";
+const char* const zone_ds_txt =
+ "example.com. 3600 IN DS 57855 5 1 "
+ "B6DCD485719ADCA18E5F3D48A2331627FDD3 636B\n";
const char* const ns_addrs_txt =
"glue.delegation.example.com. 3600 IN A 192.0.2.153\n"
"glue.delegation.example.com. 3600 IN AAAA 2001:db8::53\n"
@@ -66,11 +78,16 @@ const char* const delegation_txt =
"delegation.example.com. 3600 IN NS noglue.example.com.\n"
"delegation.example.com. 3600 IN NS cname.example.com.\n"
"delegation.example.com. 3600 IN NS example.org.\n";
+// Borrowed from the RFC4035
+const char* const delegation_ds_txt =
+ "delegation.example.com. 3600 IN DS 57855 5 1 "
+ "B6DCD485719ADCA18E5F3D48A2331627FDD3 636B\n";
const char* const mx_txt =
"mx.example.com. 3600 IN MX 10 www.example.com.\n"
"mx.example.com. 3600 IN MX 20 mailer.example.org.\n"
"mx.example.com. 3600 IN MX 30 mx.delegation.example.com.\n";
-const char* const www_a_txt = "www.example.com. 3600 IN A 192.0.2.80\n";
+const char* const www_a_txt =
+ "www.example.com. 3600 IN A 192.0.2.80\n";
const char* const cname_txt =
"cname.example.com. 3600 IN CNAME www.example.com.\n";
const char* const cname_nxdom_txt =
@@ -95,13 +112,15 @@ const char* const other_zone_rrs =
"cnamemx.example.com. 3600 IN MX 10 cnamemailer.example.com.\n"
"mx.delegation.example.com. 3600 IN A 192.0.2.100\n";
// Wildcards
-const char* const wild_txt = "*.wild.example.com. 3600 IN A 192.0.2.7\n";
+const char* const wild_txt =
+ "*.wild.example.com. 3600 IN A 192.0.2.7\n";
const char* const nsec_wild_txt =
"*.wild.example.com. 3600 IN NSEC www.example.com. A NSEC RRSIG\n";
const char* const cnamewild_txt =
"*.cnamewild.example.com. 3600 IN CNAME www.example.org.\n";
-const char* const nsec_cnamewild_txt = "*.cnamewild.example.com. "
- "3600 IN NSEC delegation.example.com. CNAME NSEC RRSIG\n";
+const char* const nsec_cnamewild_txt =
+ "*.cnamewild.example.com. 3600 IN NSEC "
+ "delegation.example.com. CNAME NSEC RRSIG\n";
// Wildcard_nxrrset
const char* const wild_txt_nxrrset =
"*.uwild.example.com. 3600 IN A 192.0.2.9\n";
@@ -112,10 +131,12 @@ const char* const wild_txt_next =
const char* const nsec_wild_txt_next =
"www.uwild.example.com. 3600 IN NSEC *.wild.example.com. A NSEC RRSIG\n";
// Wildcard empty
-const char* const empty_txt = "b.*.t.example.com. 3600 IN A 192.0.2.13\n";
+const char* const empty_txt =
+ "b.*.t.example.com. 3600 IN A 192.0.2.13\n";
const char* const nsec_empty_txt =
"b.*.t.example.com. 3600 IN NSEC *.uwild.example.com. A NSEC RRSIG\n";
-const char* const empty_prev_txt = "t.example.com. 3600 IN A 192.0.2.15\n";
+const char* const empty_prev_txt =
+ "t.example.com. 3600 IN A 192.0.2.15\n";
const char* const nsec_empty_prev_txt =
"t.example.com. 3600 IN NSEC b.*.t.example.com. A NSEC RRSIG\n";
// Used in NXDOMAIN proof test. We are going to test some unusual case where
@@ -153,7 +174,8 @@ const char* const nsec_www_txt =
"www.example.com. 3600 IN NSEC example.com. A NSEC RRSIG\n";
// Authoritative data without NSEC
-const char* const nonsec_a_txt = "nonsec.example.com. 3600 IN A 192.0.2.0\n";
+const char* const nonsec_a_txt =
+ "nonsec.example.com. 3600 IN A 192.0.2.0\n";
// NSEC3 RRs. You may also need to add mapping to MockZoneFinder::hash_map_.
const char* const nsec3_apex_txt =
@@ -163,6 +185,28 @@ const char* const nsec3_www_txt =
"q04jkcevqvmu85r014c7dkba38o0ji5r.example.com. 3600 IN NSEC3 1 1 12 "
"aabbccdd r53bq7cc2uvmubfu5ocmm6pers9tk9en A RRSIG\n";
+// NSEC3 for wild.example.com (used in wildcard tests, will be added on
+// demand not to confuse other tests)
+const char* const nsec3_atwild_txt =
+ "ji6neoaepv8b5o6k4ev33abha8ht9fgc.example.com. 3600 IN NSEC3 1 1 12 "
+ "aabbccdd r53bq7cc2uvmubfu5ocmm6pers9tk9en\n";
+
+// NSEC3 for cnamewild.example.com (used in wildcard tests, will be added on
+// demand not to confuse other tests)
+const char* const nsec3_atcnamewild_txt =
+ "k8udemvp1j2f7eg6jebps17vp3n8i58h.example.com. 3600 IN NSEC3 1 1 12 "
+ "aabbccdd r53bq7cc2uvmubfu5ocmm6pers9tk9en\n";
+
+// NSEC3 for *.uwild.example.com (will be added on demand not to confuse
+// other tests)
+const char* const nsec3_wild_txt =
+ "b4um86eghhds6nea196smvmlo4ors995.example.com. 3600 IN NSEC3 1 1 12 "
+ "aabbccdd r53bq7cc2uvmubfu5ocmm6pers9tk9en A RRSIG\n";
+// NSEC3 for uwild.example.com. (will be added on demand)
+const char* const nsec3_uwild_txt =
+ "t644ebqk9bibcna874givr6joj62mlhv.example.com. 3600 IN NSEC3 1 1 12 "
+ "aabbccdd r53bq7cc2uvmubfu5ocmm6pers9tk9en A RRSIG\n";
+
// (Secure) delegation data; Delegation with DS record
const char* const signed_delegation_txt =
"signed-delegation.example.com. 3600 IN NS ns.example.net.\n";
@@ -170,12 +214,23 @@ const char* const signed_delegation_ds_txt =
"signed-delegation.example.com. 3600 IN DS 12345 8 2 "
"764501411DE58E8618945054A3F620B36202E115D015A7773F4B78E0F952CECA\n";
-// (Secure) delegation data; Delegation without DS record (and NSEC denying
-// its existence.
+// (Secure) delegation data; Delegation without DS record (and both NSEC
+// and NSEC3 denying its existence)
const char* const unsigned_delegation_txt =
"unsigned-delegation.example.com. 3600 IN NS ns.example.net.\n";
const char* const unsigned_delegation_nsec_txt =
"unsigned-delegation.example.com. 3600 IN NSEC "
+ "unsigned-delegation-optout.example.com. NS RRSIG NSEC\n";
+// This one will be added on demand
+const char* const unsigned_delegation_nsec3_txt =
+ "q81r598950igr1eqvc60aedlq66425b5.example.com. 3600 IN NSEC3 1 1 12 "
+ "aabbccdd 0p9mhaveqvm6t7vbl5lop2u3t2rp3tom NS RRSIG\n";
+
+// Delegation without DS record, and no direct matching NSEC3 record
+const char* const unsigned_delegation_optout_txt =
+ "unsigned-delegation-optout.example.com. 3600 IN NS ns.example.net.\n";
+const char* const unsigned_delegation_optout_nsec_txt =
+ "unsigned-delegation-optout.example.com. 3600 IN NSEC "
"*.uwild.example.com. NS RRSIG NSEC\n";
// (Secure) delegation data; Delegation where the DS lookup will raise an
@@ -195,13 +250,34 @@ getCommonRRSIGText(const string& type) {
"example.com. FAKEFAKEFAKE"));
}
+// A helper callback of masterLoad() used in InMemoryZoneFinderTest.
+void
+setRRset(RRsetPtr rrset, vector<RRsetPtr*>::iterator& it) {
+ *(*it) = rrset;
+ ++it;
+}
+
+// A helper function that converts a textual form of a single RR into a
+// RRsetPtr object. If it's SOA, origin must be set to its owner name;
+// otherwise masterLoad() will reject it.
+RRsetPtr
+textToRRset(const string& text_rrset, const Name& origin = Name::ROOT_NAME()) {
+ stringstream ss(text_rrset);
+ RRsetPtr rrset;
+ vector<RRsetPtr*> rrsets;
+ rrsets.push_back(&rrset);
+ masterLoad(ss, origin, RRClass::IN(),
+ boost::bind(setRRset, _1, rrsets.begin()));
+ return (rrset);
+}
+
// This is a mock Zone Finder class for testing.
// It is a derived class of ZoneFinder for the convenient of tests.
// Its find() method emulates the common behavior of protocol compliant
// ZoneFinder classes, but simplifies some minor cases and also supports broken
// behavior.
-// For simplicity, most names are assumed to be "in zone"; there's only
-// one zone cut at the point of name "delegation.example.com".
+// For simplicity, most names are assumed to be "in zone"; delegations
+// to child zones are identified by the existence of non origin NS records.
// Another special name is "dname.example.com". Query names under this name
// will result in DNAME.
// This mock zone doesn't handle empty non terminal nodes (if we need to test
@@ -210,22 +286,21 @@ class MockZoneFinder : public ZoneFinder {
public:
MockZoneFinder() :
origin_(Name("example.com")),
- delegation_name_("delegation.example.com"),
- signed_delegation_name_("signed-delegation.example.com"),
bad_signed_delegation_name_("bad-delegation.example.com"),
- unsigned_delegation_name_("unsigned-delegation.example.com"),
dname_name_("dname.example.com"),
has_SOA_(true),
has_apex_NS_(true),
rrclass_(RRClass::IN()),
include_rrsig_anyway_(false),
use_nsec3_(false),
- nsec_name_(origin_)
+ nsec_name_(origin_),
+ nsec3_fake_(NULL),
+ nsec3_name_(NULL)
{
stringstream zone_stream;
zone_stream << soa_txt << zone_ns_txt << ns_addrs_txt <<
- delegation_txt << mx_txt << www_a_txt << cname_txt <<
- cname_nxdom_txt << cname_out_txt << dname_txt <<
+ delegation_txt << delegation_ds_txt << mx_txt << www_a_txt <<
+ cname_txt << cname_nxdom_txt << cname_out_txt << dname_txt <<
dname_a_txt << other_zone_rrs << no_txt << nz_txt <<
nsec_apex_txt << nsec_mx_txt << nsec_no_txt << nsec_nz_txt <<
nsec_nxdomain_txt << nsec_www_txt << nonsec_a_txt <<
@@ -236,6 +311,8 @@ public:
nsec3_apex_txt << nsec3_www_txt <<
signed_delegation_txt << signed_delegation_ds_txt <<
unsigned_delegation_txt << unsigned_delegation_nsec_txt <<
+ unsigned_delegation_optout_txt <<
+ unsigned_delegation_optout_nsec_txt <<
bad_delegation_txt;
masterLoad(zone_stream, origin_, rrclass_,
@@ -248,8 +325,12 @@ public:
// (Faked) NSEC3 hash map. For convenience we use hardcoded built-in
// map instead of calculating and using actual hash.
- // The used hash values are borrowed from RFC5155 examples.
+ // The used hash values are borrowed from RFC5155 examples (they are
+ // based on the query name, not that they would correspond directly
+ // to the name).
hash_map_[Name("example.com")] = "0p9mhaveqvm6t7vbl5lop2u3t2rp3tom";
+ hash_map_[Name("www.example.com")] =
+ "q04jkcevqvmu85r014c7dkba38o0ji5r";
hash_map_[Name("nxdomain.example.com")] =
"v644ebqk9bibcna874givr6joj62mlhv";
hash_map_[Name("nx.domain.example.com")] =
@@ -260,15 +341,43 @@ public:
"q00jkcevqvmu85r014c7dkba38o0ji5r";
hash_map_[Name("nxdomain3.example.com")] =
"009mhaveqvm6t7vbl5lop2u3t2rp3tom";
+ hash_map_[Name("*.example.com")] =
+ "r53bq7cc2uvmubfu5ocmm6pers9tk9en";
+ hash_map_[Name("unsigned-delegation.example.com")] =
+ "q81r598950igr1eqvc60aedlq66425b5"; // a bit larger than H(www)
+ hash_map_[Name("*.uwild.example.com")] =
+ "b4um86eghhds6nea196smvmlo4ors995";
+ hash_map_[Name("unsigned-delegation-optout.example.com")] =
+ "vld46lphhasfapj8og1pglgiasa5o5gt";
+
+ // For wildcard proofs
+ hash_map_[Name("wild.example.com")] =
+ "ji6neoaepv8b5o6k4ev33abha8ht9fgc";
+ hash_map_[Name("y.wild.example.com")] =
+ "0p9mhaveqvm6t7vbl5lop2u3t2rp3ton"; // a bit larger than H(<apex>)
+ hash_map_[Name("x.y.wild.example.com")] =
+ "q04jkcevqvmu85r014c7dkba38o0ji6r"; // a bit larger than H(www)
+ hash_map_[Name("cnamewild.example.com")] =
+ "k8udemvp1j2f7eg6jebps17vp3n8i58h";
+ hash_map_[Name("www.cnamewild.example.com")] =
+ "q04jkcevqvmu85r014c7dkba38o0ji6r"; // a bit larger than H(www)
+
+ // For closest encloser proof for www1.uwild.example.com:
+ hash_map_[Name("uwild.example.com")] =
+ "t644ebqk9bibcna874givr6joj62mlhv";
+ hash_map_[Name("www1.uwild.example.com")] =
+ "q04jkcevqvmu85r014c7dkba38o0ji6r"; // a bit larger than H(www)
}
virtual isc::dns::Name getOrigin() const { return (origin_); }
virtual isc::dns::RRClass getClass() const { return (rrclass_); }
- virtual FindResult find(const isc::dns::Name& name,
- const isc::dns::RRType& type,
- const FindOptions options = FIND_DEFAULT);
- virtual FindResult findAll(const isc::dns::Name& name,
- std::vector<ConstRRsetPtr>& target,
- const FindOptions options = FIND_DEFAULT);
+ virtual ZoneFinderContextPtr find(const isc::dns::Name& name,
+ const isc::dns::RRType& type,
+ const FindOptions options =
+ FIND_DEFAULT);
+ virtual ZoneFinderContextPtr findAll(const isc::dns::Name& name,
+ std::vector<ConstRRsetPtr>& target,
+ const FindOptions options =
+ FIND_DEFAULT);
virtual ZoneFinder::FindNSEC3Result
findNSEC3(const Name& name, bool recursive);
@@ -290,31 +399,80 @@ public:
ConstRRsetPtr rrset)
{
nsec_name_ = nsec_name;
- nsec_result_.reset(new ZoneFinder::FindResult(code, rrset));
+ nsec_context_.reset(new Context(*this,
+ FIND_DEFAULT, // a fake value
+ ResultContext(code, rrset,
+ RESULT_NSEC_SIGNED)));
+ }
+
+ // Once called, the findNSEC3 will return the provided result for the next
+ // query. After that, it'll return to operate normally.
+ // NULL disables. Does not take ownership of the pointer (it is generally
+ // expected to be a local variable in the test function).
+ void setNSEC3Result(const FindNSEC3Result* result,
+ const Name* name = NULL)
+ {
+ nsec3_fake_ = result;
+ nsec3_name_ = name;
}
// If true is passed return an empty NSEC3 RRset for some negative
// answers when DNSSEC is required.
void setNSEC3Flag(bool on) { use_nsec3_ = on; }
- Name findPreviousName(const Name&) const {
+ virtual Name findPreviousName(const Name&) const {
isc_throw(isc::NotImplemented, "Mock doesn't support previous name");
}
+ // This method allows tests to insert new record in the middle of the test.
+ //
+ // \param record_txt textual RR representation of RR (such as soa_txt, etc)
+ void addRecord(const string& record_txt) {
+ stringstream record_stream;
+ record_stream << record_txt;
+ masterLoad(record_stream, origin_, rrclass_,
+ boost::bind(&MockZoneFinder::loadRRset, this, _1));
+ }
+
public:
// We allow the tests to use these for convenience
- ConstRRsetPtr delegation_rrset_;
- ConstRRsetPtr signed_delegation_rrset_;
- ConstRRsetPtr signed_delegation_ds_rrset_;
- ConstRRsetPtr bad_signed_delegation_rrset_;
- ConstRRsetPtr unsigned_delegation_rrset_;
+ ConstRRsetPtr dname_rrset_; // could be used as an arbitrary bogus RRset
ConstRRsetPtr empty_nsec_rrset_;
+protected:
+ // A convenient shortcut. Will also be used by further derived mocks.
+ ZoneFinderContextPtr createContext(FindOptions options,
+ Result code,
+ isc::dns::ConstRRsetPtr rrset,
+ FindResultFlags flags = RESULT_DEFAULT)
+ {
+ return (ZoneFinderContextPtr(
+ new Context(*this, options,
+ ResultContext(code, rrset, flags))));
+ }
+
private:
typedef map<RRType, ConstRRsetPtr> RRsetStore;
typedef map<Name, RRsetStore> Domains;
Domains domains_;
+ Domains delegations_;
Domains nsec3_domains_;
+
+ // This is used to identify delegation to a child zone, and used to
+ // find a matching entry in delegations_. Note that first found entry
+ // is returned, so it's not a longest match. Test data must be set up
+ // to ensure the first match is always the longest match.
+ struct SubdomainMatch {
+ SubdomainMatch(const Name& name) : name_(name) {}
+ bool operator()(const pair<Name, RRsetStore>& domain_elem) const {
+ return (name_ == domain_elem.first ||
+ name_.compare(domain_elem.first).getRelation() ==
+ NameComparisonResult::SUBDOMAIN);
+ }
+ private:
+ const Name& name_;
+ };
+
void loadRRset(RRsetPtr rrset) {
if (rrset->getType() == RRType::NSEC3()) {
// NSEC3 should go to the dedicated table
@@ -328,39 +486,24 @@ private:
return;
}
domains_[rrset->getName()][rrset->getType()] = rrset;
- if (rrset->getName() == delegation_name_ &&
- rrset->getType() == RRType::NS()) {
- delegation_rrset_ = rrset;
- } else if (rrset->getName() == signed_delegation_name_ &&
- rrset->getType() == RRType::NS()) {
- signed_delegation_rrset_ = rrset;
- } else if (rrset->getName() == bad_signed_delegation_name_ &&
- rrset->getType() == RRType::NS()) {
- bad_signed_delegation_rrset_ = rrset;
- } else if (rrset->getName() == unsigned_delegation_name_ &&
- rrset->getType() == RRType::NS()) {
- unsigned_delegation_rrset_ = rrset;
- } else if (rrset->getName() == signed_delegation_name_ &&
- rrset->getType() == RRType::DS()) {
- signed_delegation_ds_rrset_ = rrset;
- // Like NSEC(3), by nature it should have an RRSIG.
- rrset->addRRsig(RdataPtr(new generic::RRSIG(
- getCommonRRSIGText(rrset->getType().
- toText()))));
+
+ // Remember delegation (NS/DNAME) related RRsets separately.
+ if (rrset->getType() == RRType::NS() && rrset->getName() != origin_) {
+ delegations_[rrset->getName()][rrset->getType()] = rrset;
} else if (rrset->getName() == dname_name_ &&
- rrset->getType() == RRType::DNAME()) {
+ rrset->getType() == RRType::DNAME()) {
dname_rrset_ = rrset;
- // Add some signatures
- } else if (rrset->getName() == Name("example.com.") &&
- rrset->getType() == RRType::NS()) {
- // For NS, we only have RRSIG for the origin name.
- rrset->addRRsig(RdataPtr(new generic::RRSIG(
- getCommonRRSIGText("NS"))));
- } else {
- // For others generate RRSIG unconditionally. Technically this
- // is wrong because we shouldn't have it for names under a zone
- // cut. But in our tests that doesn't matter, so we add them
- // just for simplicity.
+ }
+
+ // Add some signatures. For NS, we only have RRSIG for the origin
+ // name. For others generate RRSIG unconditionally. Technically this
+ // is wrong because we shouldn't have it for names under a zone
+ // cut. But in our tests that doesn't matter, so we add them
+ // just for simplicity.
+ // Note that this includes RRSIG for DS with secure delegations.
+ // They should have RRSIGs, so that's actually expected data, not just
+ // for simplicity.
+ if (rrset->getType() != RRType::NS() || rrset->getName() == origin_) {
rrset->addRRsig(RdataPtr(new generic::RRSIG(
getCommonRRSIGText(rrset->getType().
toText()))));
@@ -369,27 +512,29 @@ private:
const Name origin_;
// Names where we delegate somewhere else
- const Name delegation_name_;
- const Name signed_delegation_name_;
const Name bad_signed_delegation_name_;
- const Name unsigned_delegation_name_;
const Name dname_name_;
bool has_SOA_;
bool has_apex_NS_;
- ConstRRsetPtr dname_rrset_;
const RRClass rrclass_;
bool include_rrsig_anyway_;
bool use_nsec3_;
// The following two will be used for faked NSEC cases
Name nsec_name_;
- boost::scoped_ptr<ZoneFinder::FindResult> nsec_result_;
+ ZoneFinderContextPtr nsec_context_;
+ // The following two are for faking bad NSEC3 responses
+ // Enabled when not NULL
+ const FindNSEC3Result* nsec3_fake_;
+ const Name* nsec3_name_;
+public:
+ // Public, to allow tests looking up the right names for something
map<Name, string> hash_map_;
};
// A helper function that generates a new RRset based on "wild_rrset",
// replacing its owner name with 'real_name'.
ConstRRsetPtr
-substituteWild(const RRset& wild_rrset, const Name& real_name) {
+substituteWild(const AbstractRRset& wild_rrset, const Name& real_name) {
RRsetPtr rrset(new RRset(real_name, wild_rrset.getClass(),
wild_rrset.getType(), wild_rrset.getTTL()));
// For simplicity we only consider the case with one RDATA (for now)
@@ -404,12 +549,12 @@ substituteWild(const RRset& wild_rrset, const Name& real_name) {
return (rrset);
}
-ZoneFinder::FindResult
+ZoneFinderContextPtr
MockZoneFinder::findAll(const Name& name, std::vector<ConstRRsetPtr>& target,
const FindOptions options)
{
- ZoneFinder::FindResult result(find(name, RRType::ANY(), options));
- if (result.code == NXRRSET) {
+ ZoneFinderContextPtr result(find(name, RRType::ANY(), options));
+ if (result->code == NXRRSET) {
const Domains::const_iterator found_domain = domains_.find(name);
if (!found_domain->second.empty()) {
for (RRsetStore::const_iterator found_rrset =
@@ -418,7 +563,10 @@ MockZoneFinder::findAll(const Name& name, std::vector<ConstRRsetPtr>& target,
// Insert RRs under the domain name into target
target.push_back(found_rrset->second);
}
- return (FindResult(SUCCESS, RRsetPtr()));
+ return (ZoneFinderContextPtr(
+ new Context(*this, options,
+ ResultContext(SUCCESS, RRsetPtr()),
+ target)));
}
}
@@ -427,6 +575,13 @@ MockZoneFinder::findAll(const Name& name, std::vector<ConstRRsetPtr>& target,
ZoneFinder::FindNSEC3Result
MockZoneFinder::findNSEC3(const Name& name, bool recursive) {
+ // Do we have a fake result set? If so, use it.
+ if (nsec3_fake_ != NULL &&
+ (nsec3_name_ == NULL || *nsec3_name_ == name)) {
+ const FindNSEC3Result* result(nsec3_fake_);
+ return (*result);
+ }
+
ConstRRsetPtr covering_proof;
const int labels = name.getLabelCount();
@@ -434,6 +589,10 @@ MockZoneFinder::findNSEC3(const Name& name, bool recursive) {
// expected entry when operator[] is used; maps are not empty.
for (int i = 0; i < labels; ++i) {
const string hlabel = hash_map_[name.split(i, labels - i)];
+ if (hlabel.empty()) {
+ isc_throw(isc::Unexpected, "findNSEC3() hash failure for " <<
+ name.split(i, labels - i));
+ }
const Name hname = Name(hlabel + ".example.com");
// We don't use const_iterator so that we can use operator[] below
Domains::iterator found_domain = nsec3_domains_.lower_bound(hname);
@@ -470,52 +629,35 @@ MockZoneFinder::findNSEC3(const Name& name, bool recursive) {
isc_throw(isc::Unexpected, "findNSEC3() isn't expected to fail");
}
-ZoneFinder::FindResult
+ZoneFinderContextPtr
MockZoneFinder::find(const Name& name, const RRType& type,
const FindOptions options)
{
// Emulating a broken zone: mandatory apex RRs are missing if specifically
// configured so (which are rare cases).
if (name == origin_ && type == RRType::SOA() && !has_SOA_) {
- return (FindResult(NXDOMAIN, RRsetPtr()));
+ return (createContext(options, NXDOMAIN, RRsetPtr()));
} else if (name == origin_ && type == RRType::NS() && !has_apex_NS_) {
- return (FindResult(NXDOMAIN, RRsetPtr()));
+ return (createContext(options, NXDOMAIN, RRsetPtr()));
}
- // Special case for names on or under a zone cut
+ // Special case for names on or under a zone cut and under DNAME
+ Domains::iterator it;
if ((options & FIND_GLUE_OK) == 0 &&
- (name == delegation_name_ ||
- name.compare(delegation_name_).getRelation() ==
- NameComparisonResult::SUBDOMAIN)) {
- return (FindResult(DELEGATION, delegation_rrset_));
- // And under DNAME
- } else if (name.compare(dname_name_).getRelation() ==
- NameComparisonResult::SUBDOMAIN) {
- if (type != RRType::DS()) {
- return (FindResult(DNAME, dname_rrset_));
- }
- } else if (name == signed_delegation_name_ ||
- name.compare(signed_delegation_name_).getRelation() ==
- NameComparisonResult::SUBDOMAIN) {
- if (type != RRType::DS()) {
- return (FindResult(DELEGATION, signed_delegation_rrset_));
- } else {
- return (FindResult(SUCCESS, signed_delegation_ds_rrset_));
- }
- } else if (name == unsigned_delegation_name_ ||
- name.compare(unsigned_delegation_name_).getRelation() ==
- NameComparisonResult::SUBDOMAIN) {
- if (type != RRType::DS()) {
- return (FindResult(DELEGATION, unsigned_delegation_rrset_));
+ (it = find_if(delegations_.begin(), delegations_.end(),
+ SubdomainMatch(name))) != delegations_.end()) {
+ ConstRRsetPtr delegation_ns = it->second[RRType::NS()];
+ assert(delegation_ns); // should be ensured by how we construct it
+
+ // DS query for the delegated domain (i.e. an exact match) will be
+ // handled just like an in-zone case below. Others result in
+ // DELEGATION.
+ if (type != RRType::DS() || it->first != name) {
+ return (createContext(options, DELEGATION, delegation_ns));
}
- } else if (name == bad_signed_delegation_name_ ||
- name.compare(bad_signed_delegation_name_).getRelation() ==
+ } else if (name.compare(dname_name_).getRelation() ==
NameComparisonResult::SUBDOMAIN) {
- if (type != RRType::DS()) {
- return (FindResult(DELEGATION, bad_signed_delegation_rrset_));
- } else {
- return (FindResult(NXDOMAIN, RRsetPtr()));
- }
+ return (createContext(options, DNAME, dname_rrset_));
}
// normal cases. names are searched for only per exact-match basis
@@ -545,27 +687,35 @@ MockZoneFinder::find(const Name& name, const RRType& type,
}
rrset = noconst;
}
- return (FindResult(SUCCESS, rrset));
+ return (createContext(options, SUCCESS, rrset));
}
// Otherwise, if this domain name has CNAME, return it.
found_rrset = found_domain->second.find(RRType::CNAME());
if (found_rrset != found_domain->second.end()) {
- return (FindResult(CNAME, found_rrset->second));
+ return (createContext(options, CNAME, found_rrset->second));
}
- // Otherwise it's NXRRSET case.
+ // Otherwise it's NXRRSET case...
+ // ...but a special pathological case first:
+ if (found_domain->first == bad_signed_delegation_name_ &&
+ type == RRType::DS()) {
+ return (createContext(options, NXDOMAIN, RRsetPtr()));
+ }
+ // normal cases follow.
if ((options & FIND_DNSSEC) != 0) {
if (use_nsec3_) {
- return (FindResult(NXRRSET, RRsetPtr(), RESULT_NSEC3_SIGNED));
+ return (createContext(options, NXRRSET, RRsetPtr(),
+ RESULT_NSEC3_SIGNED));
}
found_rrset = found_domain->second.find(RRType::NSEC());
if (found_rrset != found_domain->second.end()) {
- return (FindResult(NXRRSET, found_rrset->second,
- RESULT_NSEC_SIGNED));
+ return (createContext(options, NXRRSET, found_rrset->second,
+ RESULT_NSEC_SIGNED));
}
}
- return (FindResult(NXRRSET, RRsetPtr(), RESULT_NSEC_SIGNED));
+ return (createContext(options, NXRRSET, RRsetPtr(),
+ RESULT_NSEC_SIGNED));
}
// query name isn't found in our domains.
@@ -585,27 +735,30 @@ MockZoneFinder::find(const Name& name, const RRType& type,
--domain; // reset domain to the "previous name"
if ((options & FIND_DNSSEC) != 0) {
if (use_nsec3_) {
- return (FindResult(NXRRSET, RRsetPtr(), RESULT_NSEC3_SIGNED));
+ return (createContext(options, NXRRSET, RRsetPtr(),
+ RESULT_NSEC3_SIGNED));
}
RRsetStore::const_iterator found_rrset =
(*domain).second.find(RRType::NSEC());
if (found_rrset != (*domain).second.end()) {
- return (FindResult(NXRRSET, found_rrset->second,
- RESULT_NSEC_SIGNED));
+ return (createContext(options, NXRRSET, found_rrset->second,
+ RESULT_NSEC_SIGNED));
}
}
- return (FindResult(NXRRSET, RRsetPtr()));
+ return (createContext(options, NXRRSET, RRsetPtr()));
}
// Another possibility is wildcard. For simplicity we only check
// hardcoded specific cases, ignoring other details such as canceling
// due to the existence of closer name.
if ((options & NO_WILDCARD) == 0) {
- const Name wild_suffix(name.split(1));
+ const Name wild_suffix(name == Name("x.y.wild.example.com") ?
+ Name("wild.example.com") : name.split(1));
// Unit Tests use those domains for Wildcard test.
- if (name.equals(Name("www.wild.example.com"))||
- name.equals(Name("www1.uwild.example.com"))||
- name.equals(Name("a.t.example.com"))) {
+ if (name.equals(Name("www.wild.example.com")) ||
+ name.equals(Name("x.y.wild.example.com")) ||
+ name.equals(Name("www1.uwild.example.com")) ||
+ name.equals(Name("a.t.example.com"))) {
if (name.compare(wild_suffix).getRelation() ==
NameComparisonResult::SUBDOMAIN) {
domain = domains_.find(Name("*").concatenate(wild_suffix));
@@ -615,38 +768,37 @@ MockZoneFinder::find(const Name& name, const RRType& type,
domain->second.find(type);
// Matched the QTYPE
if(found_rrset != domain->second.end()) {
- return (FindResult(SUCCESS,
- substituteWild(
- *found_rrset->second, name),
- RESULT_WILDCARD |
- (use_nsec3_ ?
- RESULT_NSEC3_SIGNED :
- RESULT_NSEC_SIGNED)));
+ return (createContext(options,SUCCESS, substituteWild(
+ *found_rrset->second, name),
+ RESULT_WILDCARD |
+ (use_nsec3_ ?
+ RESULT_NSEC3_SIGNED :
+ RESULT_NSEC_SIGNED)));
} else {
- // No matched QTYPE, this case is for WILDCARD_NXRRSET
+ // No matched QTYPE, this case is for NXRRSET with
+ // WILDCARD
if (use_nsec3_) {
- return (FindResult(NXRRSET, RRsetPtr(),
- RESULT_WILDCARD |
- RESULT_NSEC3_SIGNED));
+ return (createContext(options, NXRRSET, RRsetPtr(),
+ RESULT_WILDCARD |
+ RESULT_NSEC3_SIGNED));
}
const Name new_name =
Name("*").concatenate(wild_suffix);
found_rrset = domain->second.find(RRType::NSEC());
assert(found_rrset != domain->second.end());
- return (FindResult(NXRRSET,
- substituteWild(
- *found_rrset->second,
- new_name),
- RESULT_WILDCARD |
- RESULT_NSEC_SIGNED));
+ return (createContext(options, NXRRSET, substituteWild(
+ *found_rrset->second,
+ new_name),
+ RESULT_WILDCARD |
+ RESULT_NSEC_SIGNED));
}
} else {
// This is empty non terminal name case on wildcard.
const Name empty_name = Name("*").concatenate(wild_suffix);
if (use_nsec3_) {
- return (FindResult(NXRRSET, RRsetPtr(),
- RESULT_WILDCARD |
- RESULT_NSEC3_SIGNED));
+ return (createContext(options, NXRRSET, RRsetPtr(),
+ RESULT_WILDCARD |
+ RESULT_NSEC3_SIGNED));
}
for (Domains::reverse_iterator it = domains_.rbegin();
it != domains_.rend();
@@ -655,13 +807,15 @@ MockZoneFinder::find(const Name& name, const RRType& type,
if ((*it).first < empty_name &&
(nsec_it = (*it).second.find(RRType::NSEC()))
!= (*it).second.end()) {
- return (FindResult(NXRRSET, (*nsec_it).second,
- RESULT_WILDCARD |
- RESULT_NSEC_SIGNED));
+ return (createContext(options, NXRRSET,
+ (*nsec_it).second,
+ RESULT_WILDCARD |
+ RESULT_NSEC_SIGNED));
}
}
}
- return (FindResult(NXRRSET, RRsetPtr(), RESULT_WILDCARD));
+ return (createContext(options, NXRRSET, RRsetPtr(),
+ RESULT_WILDCARD));
}
}
const Name cnamewild_suffix("cnamewild.example.com");
@@ -672,11 +826,11 @@ MockZoneFinder::find(const Name& name, const RRType& type,
RRsetStore::const_iterator found_rrset =
domain->second.find(RRType::CNAME());
assert(found_rrset != domain->second.end());
- return (FindResult(CNAME,
- substituteWild(*found_rrset->second, name),
- RESULT_WILDCARD |
- (use_nsec3_ ? RESULT_NSEC3_SIGNED :
- RESULT_NSEC_SIGNED)));
+ return (createContext(options, CNAME,
+ substituteWild(*found_rrset->second, name),
+ RESULT_WILDCARD |
+ (use_nsec3_ ? RESULT_NSEC3_SIGNED :
+ RESULT_NSEC_SIGNED)));
}
}
@@ -689,12 +843,13 @@ MockZoneFinder::find(const Name& name, const RRType& type,
// than the origin)
if ((options & FIND_DNSSEC) != 0) {
if (use_nsec3_) {
- return (FindResult(NXDOMAIN, RRsetPtr(), RESULT_NSEC3_SIGNED));
+ return (createContext(options, NXDOMAIN, RRsetPtr(),
+ RESULT_NSEC3_SIGNED));
}
// Emulate a broken DataSourceClient for some special names.
- if (nsec_result_ && nsec_name_ == name) {
- return (*nsec_result_);
+ if (nsec_context_ && nsec_name_ == name) {
+ return (nsec_context_);
}
// Normal case
@@ -707,12 +862,12 @@ MockZoneFinder::find(const Name& name, const RRType& type,
if ((*it).first < name &&
(nsec_it = (*it).second.find(RRType::NSEC()))
!= (*it).second.end()) {
- return (FindResult(NXDOMAIN, (*nsec_it).second,
- RESULT_NSEC_SIGNED));
+ return (createContext(options, NXDOMAIN, (*nsec_it).second,
+ RESULT_NSEC_SIGNED));
}
}
}
- return (FindResult(NXDOMAIN, RRsetPtr()));
+ return (createContext(options,NXDOMAIN, RRsetPtr()));
}
class QueryTest : public ::testing::Test {
@@ -720,7 +875,14 @@ protected:
QueryTest() :
qname(Name("www.example.com")), qclass(RRClass::IN()),
qtype(RRType::A()), response(Message::RENDER),
- qid(response.getQid()), query_code(Opcode::QUERY().getCode())
+ qid(response.getQid()), query_code(Opcode::QUERY().getCode()),
+ ns_addrs_and_sig_txt(string(ns_addrs_txt) +
+ "glue.delegation.example.com. 3600 IN RRSIG " +
+ getCommonRRSIGText("A") + "\n" +
+ "glue.delegation.example.com. 3600 IN RRSIG " +
+ getCommonRRSIGText("AAAA") + "\n" +
+ "noglue.example.com. 3600 IN RRSIG " +
+ getCommonRRSIGText("A"))
{
response.setRcode(Rcode::NOERROR());
response.setOpcode(Opcode::QUERY());
@@ -740,6 +902,7 @@ protected:
Message response;
const qid_t qid;
const uint16_t query_code;
+ const string ns_addrs_and_sig_txt; // convenient shortcut
};
// A wrapper to check resulting response message commonly used in
@@ -812,10 +975,6 @@ TEST_F(QueryTest, dnssecPositive) {
Query query(memory_client, qname, qtype, response, true);
EXPECT_NO_THROW(query.process());
// find match rrset
- // We can't let responseCheck to check the additional section as well,
- // it gets confused by the two RRs for glue.delegation.../RRSIG due
- // to it's design and fixing it would be hard. Therefore we simply
- // check manually this one time.
responseCheck(response, Rcode::NOERROR(), AA_FLAG, 2, 4, 6,
(www_a_txt + std::string("www.example.com. 3600 IN RRSIG "
"A 5 3 3600 20000101000000 "
@@ -825,27 +984,8 @@ TEST_F(QueryTest, dnssecPositive) {
"3 3600 20000101000000 "
"20000201000000 12345 "
"example.com. FAKEFAKEFAKE\n")).
- c_str(), NULL);
- RRsetIterator iterator(response.beginSection(Message::SECTION_ADDITIONAL));
- const char* additional[] = {
- "glue.delegation.example.com. 3600 IN A 192.0.2.153\n",
- "glue.delegation.example.com. 3600 IN RRSIG A 5 3 3600 20000101000000 "
- "20000201000000 12345 example.com. FAKEFAKEFAKE\n",
- "glue.delegation.example.com. 3600 IN AAAA 2001:db8::53\n",
- "glue.delegation.example.com. 3600 IN RRSIG AAAA 5 3 3600 "
- "20000101000000 20000201000000 12345 example.com. FAKEFAKEFAKE\n",
- "noglue.example.com. 3600 IN A 192.0.2.53\n",
- "noglue.example.com. 3600 IN RRSIG A 5 3 3600 20000101000000 "
- "20000201000000 12345 example.com. FAKEFAKEFAKE\n",
- NULL
- };
- for (const char** rr(additional); *rr != NULL; ++ rr) {
- ASSERT_FALSE(iterator ==
- response.endSection(Message::SECTION_ADDITIONAL));
- EXPECT_EQ(*rr, (*iterator)->toText());
- iterator ++;
- }
- EXPECT_TRUE(iterator == response.endSection(Message::SECTION_ADDITIONAL));
+ c_str(),
+ ns_addrs_and_sig_txt.c_str());
}
TEST_F(QueryTest, exactAddrMatch) {
@@ -968,6 +1108,52 @@ TEST_F(QueryTest, secureUnsignedDelegation) {
NULL);
}
+TEST_F(QueryTest, secureUnsignedDelegationWithNSEC3) {
+ // Similar to the previous case, but the zone is signed with NSEC3,
+ // and this delegation is NOT an optout.
+ const Name insecurechild_name("unsigned-delegation.example.com");
+ mock_finder->setNSEC3Flag(true);
+ mock_finder->addRecord(unsigned_delegation_nsec3_txt);
+
+ Query(memory_client, Name("foo.unsigned-delegation.example.com"),
+ qtype, response, true).process();
+
+ // The response should contain the NS and matching NSEC3 with its RRSIG
+ responseCheck(response, Rcode::NOERROR(), 0, 0, 3, 0,
+ NULL,
+ (string(unsigned_delegation_txt) +
+ string(unsigned_delegation_nsec3_txt) +
+ mock_finder->hash_map_[insecurechild_name] +
+ ".example.com. 3600 IN RRSIG " +
+ getCommonRRSIGText("NSEC3")).c_str(),
+ NULL);
+}
+
+TEST_F(QueryTest, secureUnsignedDelegationWithNSEC3OptOut) {
+ // Similar to the previous case, but the delegation is an optout.
+ mock_finder->setNSEC3Flag(true);
+
+ Query(memory_client, Name("foo.unsigned-delegation.example.com"),
+ qtype, response, true).process();
+
+ // The response should contain the NS and the closest provable encloser
+ // proof (and their RRSIGs). The closest encloser is the apex (origin),
+ // and with our faked hash the covering NSEC3 for the next closer
+ // (= child zone name) is that for www.example.com.
+ responseCheck(response, Rcode::NOERROR(), 0, 0, 5, 0,
+ NULL,
+ (string(unsigned_delegation_txt) +
+ string(nsec3_apex_txt) +
+ mock_finder->hash_map_[mock_finder->getOrigin()] +
+ ".example.com. 3600 IN RRSIG " +
+ getCommonRRSIGText("NSEC3") + "\n" +
+ string(nsec3_www_txt) +
+ mock_finder->hash_map_[Name("www.example.com")] +
+ ".example.com. 3600 IN RRSIG " +
+ getCommonRRSIGText("NSEC3")).c_str(),
+ NULL);
+}
+
TEST_F(QueryTest, badSecureDelegation) {
// Test whether exception is raised if DS query at delegation results in
// something different than SUCCESS or NXRRSET
@@ -1046,7 +1232,7 @@ TEST_F(QueryTest, nxdomainBadNSEC1) {
// ZoneFinder::find() returns NXDOMAIN with non NSEC RR.
mock_finder->setNSECResult(Name("badnsec.example.com"),
ZoneFinder::NXDOMAIN,
- mock_finder->delegation_rrset_);
+ mock_finder->dname_rrset_);
EXPECT_THROW(Query(memory_client, Name("badnsec.example.com"), qtype,
response, true).process(),
std::bad_cast);
@@ -1066,7 +1252,7 @@ TEST_F(QueryTest, nxdomainBadNSEC3) {
// "no-wildcard proof" returns SUCCESS. it should be NXDOMAIN.
mock_finder->setNSECResult(Name("*.example.com"),
ZoneFinder::SUCCESS,
- mock_finder->delegation_rrset_);
+ mock_finder->dname_rrset_);
EXPECT_THROW(Query(memory_client, Name("nxdomain.example.com"), qtype,
response, true).process(),
Query::BadNSEC);
@@ -1085,18 +1271,20 @@ TEST_F(QueryTest, nxdomainBadNSEC5) {
// "no-wildcard proof" returns non NSEC.
mock_finder->setNSECResult(Name("*.example.com"),
ZoneFinder::NXDOMAIN,
- mock_finder->delegation_rrset_);
+ mock_finder->dname_rrset_);
// This is a bit odd, but we'll simply include the returned RRset.
Query(memory_client, Name("nxdomain.example.com"), qtype,
response, true).process();
- responseCheck(response, Rcode::NXDOMAIN(), AA_FLAG, 0, 8, 0,
+ responseCheck(response, Rcode::NXDOMAIN(), AA_FLAG, 0, 6, 0,
NULL, (string(soa_txt) +
string("example.com. 3600 IN RRSIG ") +
getCommonRRSIGText("SOA") + "\n" +
string(nsec_nxdomain_txt) + "\n" +
string("noglue.example.com. 3600 IN RRSIG ") +
getCommonRRSIGText("NSEC") + "\n" +
- delegation_txt).c_str(),
+ dname_txt + "\n" +
+ string("dname.example.com. 3600 IN RRSIG ") +
+ getCommonRRSIGText("DNAME")).c_str(),
NULL, mock_finder->getOrigin());
}
@@ -1203,12 +1391,75 @@ TEST_F(QueryTest, CNAMEwildNSEC) {
mock_finder->getOrigin());
}
+TEST_F(QueryTest, wildcardNSEC3) {
+ // Similar to wildcardNSEC, but the zone is signed with NSEC3.
+ // The next closer is y.wild.example.com, the covering NSEC3 for it
+ // is (in our setup) the NSEC3 for the apex.
+ mock_finder->setNSEC3Flag(true);
+
+ // This is NSEC3 for wild.example.com, which will be used in the middle
+ // of identifying the next closer name.
+ mock_finder->addRecord(nsec3_atwild_txt);
+
+ Query(memory_client, Name("x.y.wild.example.com"), RRType::A(), response,
+ true).process();
+ responseCheck(response, Rcode::NOERROR(), AA_FLAG, 2, 6, 6,
+ (string(wild_txt).replace(0, 1, "x.y") +
+ string("x.y.wild.example.com. 3600 IN RRSIG ") +
+ getCommonRRSIGText("A") + "\n").c_str(),
+ // 3 NSes and their RRSIG
+ (zone_ns_txt + string("example.com. 3600 IN RRSIG ") +
+ getCommonRRSIGText("NS") + "\n" +
+ // NSEC3 for the wildcard proof and its RRSIG
+ string(nsec3_apex_txt) +
+ mock_finder->hash_map_[Name("example.com.")] +
+ string(".example.com. 3600 IN RRSIG ") +
+ getCommonRRSIGText("NSEC3")).c_str(),
+ NULL, // we are not interested in additionals in this test
+ mock_finder->getOrigin());
+}
+
+TEST_F(QueryTest, CNAMEwildNSEC3) {
+ // Similar to CNAMEwildNSEC, but with NSEC3.
+ // The next closer is qname itself, the covering NSEC3 for it
+ // is (in our setup) the NSEC3 for the www.example.com.
+ mock_finder->setNSEC3Flag(true);
+ mock_finder->addRecord(nsec3_atcnamewild_txt);
+
+ Query(memory_client, Name("www.cnamewild.example.com"), RRType::A(),
+ response, true).process();
+ responseCheck(response, Rcode::NOERROR(), AA_FLAG, 2, 2, 0,
+ (string(cnamewild_txt).replace(0, 1, "www") +
+ string("www.cnamewild.example.com. 3600 IN RRSIG ") +
+ getCommonRRSIGText("CNAME") + "\n").c_str(),
+ (string(nsec3_www_txt) +
+ mock_finder->hash_map_[Name("www.example.com.")] +
+ string(".example.com. 3600 IN RRSIG ") +
+ getCommonRRSIGText("NSEC3")).c_str(),
+ NULL, // we are not interested in additionals in this test
+ mock_finder->getOrigin());
+}
+
+TEST_F(QueryTest, badWildcardNSEC3) {
+ // Similar to wildcardNSEC3, but emulating run time collision by
+ // returning NULL in the next closer proof for the closest encloser
+ // proof.
+ mock_finder->setNSEC3Flag(true);
+ ZoneFinder::FindNSEC3Result nsec3(true, 0, textToRRset(nsec3_apex_txt),
+ ConstRRsetPtr());
+ mock_finder->setNSEC3Result(&nsec3);
+
+ EXPECT_THROW(Query(memory_client, Name("www.wild.example.com"),
+ RRType::A(), response, true).process(),
+ Query::BadNSEC3);
+}
+
TEST_F(QueryTest, badWildcardProof1) {
// Unexpected case in wildcard proof: ZoneFinder::find() returns SUCCESS
// when NXDOMAIN is expected.
mock_finder->setNSECResult(Name("www.wild.example.com"),
ZoneFinder::SUCCESS,
- mock_finder->delegation_rrset_);
+ mock_finder->dname_rrset_);
EXPECT_THROW(Query(memory_client, Name("www.wild.example.com"),
RRType::A(), response, true).process(),
Query::BadNSEC);
@@ -1234,8 +1485,8 @@ TEST_F(QueryTest, badWildcardProof3) {
}
TEST_F(QueryTest, wildcardNxrrsetWithDuplicateNSEC) {
- // WILDCARD_NXRRSET with DNSSEC proof. We should have SOA, NSEC that proves the
- // NXRRSET and their RRSIGs. In this case we only need one NSEC,
+ // NXRRSET on WILDCARD with DNSSEC proof. We should have SOA, NSEC that
+ // proves the NXRRSET and their RRSIGs. In this case we only need one NSEC,
// which proves both NXDOMAIN and the non existence RRSETs of wildcard.
Query(memory_client, Name("www.wild.example.com"), RRType::TXT(), response,
true).process();
@@ -1250,11 +1501,12 @@ TEST_F(QueryTest, wildcardNxrrsetWithDuplicateNSEC) {
}
TEST_F(QueryTest, wildcardNxrrsetWithNSEC) {
- // WILDCARD_NXRRSET with DNSSEC proof. We should have SOA, NSEC that proves the
- // NXRRSET and their RRSIGs. In this case we need two NSEC RRs,
- // one proves NXDOMAIN and the other proves non existence RRSETs of wildcard.
- Query(memory_client, Name("www1.uwild.example.com"), RRType::TXT(), response,
- true).process();
+ // WILDCARD + NXRRSET with DNSSEC proof. We should have SOA, NSEC that
+ // proves the NXRRSET and their RRSIGs. In this case we need two NSEC RRs,
+ // one proves NXDOMAIN and the other proves non existence RRSETs of
+ // wildcard.
+ Query(memory_client, Name("www1.uwild.example.com"), RRType::TXT(),
+ response, true).process();
responseCheck(response, Rcode::NOERROR(), AA_FLAG, 0, 6, 0, NULL,
(string(soa_txt) + string("example.com. 3600 IN RRSIG ") +
@@ -1268,9 +1520,73 @@ TEST_F(QueryTest, wildcardNxrrsetWithNSEC) {
NULL, mock_finder->getOrigin());
}
+TEST_F(QueryTest, wildcardNxrrsetWithNSEC3) {
+ // Similar to the previous case, but providing NSEC3 proofs according to
+ // RFC5155 Section 7.2.5.
+
+ mock_finder->addRecord(nsec3_wild_txt);
+ mock_finder->addRecord(nsec3_uwild_txt);
+ mock_finder->setNSEC3Flag(true);
+
+ Query(memory_client, Name("www1.uwild.example.com"), RRType::TXT(),
+ response, true).process();
+
+ responseCheck(response, Rcode::NOERROR(), AA_FLAG, 0, 8, 0, NULL,
+ // SOA + its RRSIG
+ (string(soa_txt) + string("example.com. 3600 IN RRSIG ") +
+ getCommonRRSIGText("SOA") + "\n" +
+ // NSEC3 for the closest encloser + its RRSIG
+ string(nsec3_uwild_txt) +
+ mock_finder->hash_map_[Name("uwild.example.com.")] +
+ ".example.com. 3600 IN RRSIG " +
+ getCommonRRSIGText("NSEC3") + "\n" +
+ // NSEC3 for the next closer + its RRSIG
+ string(nsec3_www_txt) +
+ mock_finder->hash_map_[Name("www.example.com.")] +
+ ".example.com. 3600 IN RRSIG " +
+ getCommonRRSIGText("NSEC3") + "\n" +
+ // NSEC3 for the wildcard + its RRSIG
+ string(nsec3_wild_txt) +
+ mock_finder->hash_map_[Name("*.uwild.example.com.")] +
+ ".example.com. 3600 IN RRSIG " +
+ getCommonRRSIGText("NSEC3")).c_str(),
+ NULL, mock_finder->getOrigin());
+}
+
+TEST_F(QueryTest, wildcardNxrrsetWithNSEC3Collision) {
+ // Similar to the previous case, but emulating run time collision by
+ // returning NULL in the next closer proof for the closest encloser
+ // proof.
+ mock_finder->setNSEC3Flag(true);
+ ZoneFinder::FindNSEC3Result nsec3(true, 0, textToRRset(nsec3_apex_txt),
+ ConstRRsetPtr());
+ mock_finder->setNSEC3Result(&nsec3);
+
+ EXPECT_THROW(Query(memory_client, Name("www1.uwild.example.com"),
+ RRType::TXT(), response, true).process(),
+ Query::BadNSEC3);
+}
+
+TEST_F(QueryTest, wildcardNxrrsetWithNSEC3Broken) {
+ // Similar to wildcardNxrrsetWithNSEC3, but no matching NSEC3 for the
+ // wildcard name will be returned. This shouldn't happen in a reasonably
+ // NSEC-signed zone, and should result in an exception.
+ mock_finder->setNSEC3Flag(true);
+ const Name wname("*.uwild.example.com.");
+ ZoneFinder::FindNSEC3Result nsec3(false, 0, textToRRset(nsec3_apex_txt),
+ ConstRRsetPtr());
+ mock_finder->setNSEC3Result(&nsec3, &wname);
+ mock_finder->addRecord(nsec3_wild_txt);
+ mock_finder->addRecord(nsec3_uwild_txt);
+
+ EXPECT_THROW(Query(memory_client, Name("www1.uwild.example.com"),
+ RRType::TXT(), response, true).process(),
+ Query::BadNSEC3);
+}
+
TEST_F(QueryTest, wildcardEmptyWithNSEC) {
- // WILDCARD_EMPTY with DNSSEC proof. We should have SOA, NSEC that proves the
- // NXDOMAIN and their RRSIGs. In this case we need two NSEC RRs,
+ // Empty WILDCARD with DNSSEC proof. We should have SOA, NSEC that proves
+ // the NXDOMAIN and their RRSIGs. In this case we need two NSEC RRs,
// one proves NXDOMAIN and the other proves non existence wildcard.
Query(memory_client, Name("a.t.example.com"), RRType::A(), response,
true).process();
@@ -1594,87 +1910,435 @@ TEST_F(QueryTest, findNSEC3) {
Name("example.com").getLabelCount();
// Apex name. It should have a matching NSEC3
- nsec3Check(true, expected_closest_labels, nsec3_apex_txt,
- mock_finder->findNSEC3(Name("example.com"), false));
+ {
+ SCOPED_TRACE("apex, non recursive");
+ nsec3Check(true, expected_closest_labels, nsec3_apex_txt,
+ mock_finder->findNSEC3(Name("example.com"), false));
+ }
// Recursive mode doesn't change the result in this case.
- nsec3Check(true, expected_closest_labels, nsec3_apex_txt,
- mock_finder->findNSEC3(Name("example.com"), true));
+ {
+ SCOPED_TRACE("apex, recursive");
+ nsec3Check(true, expected_closest_labels, nsec3_apex_txt,
+ mock_finder->findNSEC3(Name("example.com"), true));
+ }
// Non existent name. Disabling recursion, a covering NSEC3 should be
// returned.
- nsec3Check(false, 4, nsec3_www_txt,
- mock_finder->findNSEC3(Name("nxdomain.example.com"), false));
+ {
+ SCOPED_TRACE("nxdomain, non recursive");
+ nsec3Check(false, 4, nsec3_www_txt,
+ mock_finder->findNSEC3(Name("nxdomain.example.com"),
+ false));
+ }
// Non existent name. The closest provable encloser is the apex,
// and next closer is the query name.
- nsec3Check(true, expected_closest_labels,
- string(nsec3_apex_txt) + string(nsec3_www_txt),
- mock_finder->findNSEC3(Name("nxdomain.example.com"), true));
+ {
+ SCOPED_TRACE("nxdomain, recursive");
+ nsec3Check(true, expected_closest_labels,
+ string(nsec3_apex_txt) + string(nsec3_www_txt),
+ mock_finder->findNSEC3(Name("nxdomain.example.com"), true));
+ }
// Similar to the previous case, but next closer name is different
// (is the parent) of the non existent name.
- nsec3Check(true, expected_closest_labels,
- string(nsec3_apex_txt) + string(nsec3_www_txt),
- mock_finder->findNSEC3(Name("nx.domain.example.com"), true));
+ {
+ SCOPED_TRACE("nxdomain, next closer != qname");
+ nsec3Check(true, expected_closest_labels,
+ string(nsec3_apex_txt) + string(nsec3_www_txt),
+ mock_finder->findNSEC3(Name("nx.domain.example.com"),
+ true));
+ }
// In the rest of test we check hash comparison for wrap around cases.
- nsec3Check(false, 4, nsec3_apex_txt,
- mock_finder->findNSEC3(Name("nxdomain2.example.com"), false));
- nsec3Check(false, 4, nsec3_www_txt,
- mock_finder->findNSEC3(Name("nxdomain3.example.com"), false));
+ {
+ SCOPED_TRACE("largest");
+ nsec3Check(false, 4, nsec3_apex_txt,
+ mock_finder->findNSEC3(Name("nxdomain2.example.com"),
+ false));
+ }
+ {
+ SCOPED_TRACE("smallest");
+ nsec3Check(false, 4, nsec3_www_txt,
+ mock_finder->findNSEC3(Name("nxdomain3.example.com"),
+ false));
+ }
}
-// The following are tentative tests until we really add tests for the
-// query logic for these cases. At that point it's probably better to
-// clean them up.
-TEST_F(QueryTest, nxdomainWithNSEC3) {
- mock_finder->setNSEC3Flag(true);
- ZoneFinder::FindResult result = mock_finder->find(
- Name("nxdomain.example.com"), RRType::A(), ZoneFinder::FIND_DNSSEC);
- EXPECT_EQ(ZoneFinder::NXDOMAIN, result.code);
- EXPECT_FALSE(result.rrset);
- EXPECT_TRUE(result.isNSEC3Signed());
- EXPECT_FALSE(result.isWildcard());
+// This tests that the DS is returned above the delegation point as
+// an authoritative answer, not a delegation. This is as described in
+// RFC 4035, section 3.1.4.1.
+
+// This mock finder is used for some DS-query tests to support the cases
+// where the query is expected to be handled in a different zone than our
+// main test zone, example.com. Only limited methods are expected to called
+// (and for limited purposes) on this class object in these tests, which
+// are overridden below.
+class AlternateZoneFinder : public MockZoneFinder {
+public:
+ // This zone is expected not to have a DS by default and return NXRRSET
+ // for a DS query. If have_ds is set to true on construction, it will
+ // return a faked DS answer.
+ AlternateZoneFinder(const Name& origin, bool have_ds = false) :
+ MockZoneFinder(), origin_(origin), have_ds_(have_ds)
+ {}
+ virtual isc::dns::Name getOrigin() const { return (origin_); }
+ virtual ZoneFinderContextPtr find(const isc::dns::Name&,
+ const isc::dns::RRType& type,
+ const FindOptions options)
+ {
+ if (type == RRType::SOA()) {
+ RRsetPtr soa = textToRRset(origin_.toText() + " 3600 IN SOA . . "
+ "0 0 0 0 0\n", origin_);
+ soa->addRRsig(RdataPtr(new generic::RRSIG(
+ getCommonRRSIGText("SOA"))));
+ return (createContext(options, SUCCESS, soa));
+ }
+ if (type == RRType::NS()) {
+ RRsetPtr ns = textToRRset(origin_.toText() + " 3600 IN NS " +
+ Name("ns").concatenate(origin_).toText());
+ ns->addRRsig(RdataPtr(new generic::RRSIG(
+ getCommonRRSIGText("NS"))));
+ return (createContext(options, SUCCESS, ns));
+ }
+ if (type == RRType::DS()) {
+ if (have_ds_) {
+ RRsetPtr ds = textToRRset(origin_.toText() +
+ " 3600 IN DS 57855 5 1 " +
+ "49FD46E6C4B45C55D4AC69CBD"
+ "3CD34AC1AFE51DE");
+ ds->addRRsig(RdataPtr(new generic::RRSIG(
+ getCommonRRSIGText("DS"))));
+ return (createContext(options, SUCCESS, ds));
+ } else {
+ RRsetPtr nsec = textToRRset(origin_.toText() +
+ " 3600 IN NSEC " +
+ origin_.toText() +
+ " SOA NSEC RRSIG");
+ nsec->addRRsig(RdataPtr(new generic::RRSIG(
+ getCommonRRSIGText("NSEC"))));
+ return (createContext(options, NXRRSET, nsec,
+ RESULT_NSEC_SIGNED));
+ }
+ }
+
+ // Returning NXDOMAIN is not correct, but doesn't matter for our tests.
+ return (createContext(options, NXDOMAIN, ConstRRsetPtr()));
+ }
+private:
+ const Name origin_;
+ const bool have_ds_;
+};
+
+TEST_F(QueryTest, dsAboveDelegation) {
+ // Pretending to have authority for the child zone, too.
+ memory_client.addZone(ZoneFinderPtr(new AlternateZoneFinder(
+ Name("delegation.example.com"))));
+
+ // The following will succeed only if the search goes to the parent
+ // zone, not the child one we added above.
+ EXPECT_NO_THROW(Query(memory_client, Name("delegation.example.com"),
+ RRType::DS(), response, true).process());
+
+ responseCheck(response, Rcode::NOERROR(), AA_FLAG, 2, 4, 6,
+ (string(delegation_ds_txt) + "\n" +
+ "delegation.example.com. 3600 IN RRSIG " +
+ getCommonRRSIGText("DS")).c_str(),
+ (string(zone_ns_txt) + "\n" +
+ "example.com. 3600 IN RRSIG " +
+ getCommonRRSIGText("NS")).c_str(),
+ ns_addrs_and_sig_txt.c_str());
+}
+
+TEST_F(QueryTest, dsAboveDelegationNoData) {
+ // Similar to the previous case, but the query is for an unsigned zone
+ // (which doesn't have a DS at the parent). The response should be a
+ // "no data" error. The query should still be handled at the parent.
+ memory_client.addZone(ZoneFinderPtr(
+ new AlternateZoneFinder(
+ Name("unsigned-delegation.example.com"))));
+
+ // The following will succeed only if the search goes to the parent
+ // zone, not the child one we added above.
+ EXPECT_NO_THROW(Query(memory_client,
+ Name("unsigned-delegation.example.com"),
+ RRType::DS(), response, true).process());
+
+ responseCheck(response, Rcode::NOERROR(), AA_FLAG, 0, 4, 0, NULL,
+ (string(soa_txt) +
+ string("example.com. 3600 IN RRSIG ") +
+ getCommonRRSIGText("SOA") + "\n" +
+ string(unsigned_delegation_nsec_txt) +
+ "unsigned-delegation.example.com. 3600 IN RRSIG " +
+ getCommonRRSIGText("NSEC")).c_str(),
+ NULL, mock_finder->getOrigin());
+}
+
+// This one checks that type-DS query results in a "no data" response
+// when it happens to be sent to the child zone, as described in RFC 4035,
+// section 3.1.4.1. The example is inspired by the B.8. example from the RFC.
+TEST_F(QueryTest, dsBelowDelegation) {
+ EXPECT_NO_THROW(Query(memory_client, Name("example.com"),
+ RRType::DS(), response, true).process());
+
+ responseCheck(response, Rcode::NOERROR(), AA_FLAG, 0, 4, 0, NULL,
+ (string(soa_txt) + string("example.com. 3600 IN RRSIG ") +
+ getCommonRRSIGText("SOA") + "\n" +
+ string(nsec_apex_txt) + "\n" +
+ string("example.com. 3600 IN RRSIG ") +
+ getCommonRRSIGText("NSEC")).c_str(), NULL,
+ mock_finder->getOrigin());
+}
+
+// Similar to the previous case, but even more pathological: the DS somehow
+// exists in the child zone. The Query module should still return SOA.
+// In our implementation NSEC/NSEC3 isn't attached in this case.
+TEST_F(QueryTest, dsBelowDelegationWithDS) {
+ mock_finder->addRecord(zone_ds_txt); // add the DS to the child's apex
+ EXPECT_NO_THROW(Query(memory_client, Name("example.com"),
+ RRType::DS(), response, true).process());
+
+ responseCheck(response, Rcode::NOERROR(), AA_FLAG, 0, 2, 0, NULL,
+ (string(soa_txt) + string("example.com. 3600 IN RRSIG ") +
+ getCommonRRSIGText("SOA")).c_str(), NULL,
+ mock_finder->getOrigin());
+}
+
+// DS query received at a completely irrelevant (neither parent nor child)
+// server. It should just like the "noZone" test case, but DS query involves
+// special processing, so we test it explicitly.
+TEST_F(QueryTest, dsNoZone) {
+ Query(memory_client, Name("example"), RRType::DS(), response,
+ true).process();
+ responseCheck(response, Rcode::REFUSED(), 0, 0, 0, 0, NULL, NULL, NULL);
+}
+
+// DS query for a "grandchild" zone. This should result in normal
+// delegation (unless this server also has authority of the grandchild zone).
+TEST_F(QueryTest, dsAtGrandParent) {
+ Query(memory_client, Name("grand.delegation.example.com"), RRType::DS(),
+ response, true).process();
+ responseCheck(response, Rcode::NOERROR(), 0, 0, 6, 6, NULL,
+ (string(delegation_txt) + string(delegation_ds_txt) +
+ "delegation.example.com. 3600 IN RRSIG " +
+ getCommonRRSIGText("DS")).c_str(),
+ ns_addrs_and_sig_txt.c_str());
+}
+
+// DS query sent to a "grandparent" server that also has authority for the
+// child zone. In this case the query should be handled in the child
+// side and should result in no data with SOA. Note that the server doesn't
+// have authority for the "parent". Unlike the dsAboveDelegation test case
+// the query should be handled in the child zone, not in the grandparent.
+TEST_F(QueryTest, dsAtGrandParentAndChild) {
+ // Pretending to have authority for the child zone, too.
+ const Name childname("grand.delegation.example.com");
+ memory_client.addZone(ZoneFinderPtr(
+ new AlternateZoneFinder(childname)));
+ Query(memory_client, childname, RRType::DS(), response, true).process();
+ responseCheck(response, Rcode::NOERROR(), AA_FLAG, 0, 4, 0, NULL,
+ (childname.toText() + " 3600 IN SOA . . 0 0 0 0 0\n" +
+ childname.toText() + " 3600 IN RRSIG " +
+ getCommonRRSIGText("SOA") + "\n" +
+ childname.toText() + " 3600 IN NSEC " +
+ childname.toText() + " SOA NSEC RRSIG\n" +
+ childname.toText() + " 3600 IN RRSIG " +
+ getCommonRRSIGText("NSEC")).c_str(), NULL, childname);
+}
+
+// DS query for the root name (quite pathological). Since there's no "parent",
+// the query will be handled in the root zone anyway, and should (normally)
+// result in no data.
+TEST_F(QueryTest, dsAtRoot) {
+ // Pretend to be a root server.
+ memory_client.addZone(ZoneFinderPtr(
+ new AlternateZoneFinder(Name::ROOT_NAME())));
+ Query(memory_client, Name::ROOT_NAME(), RRType::DS(), response,
+ true).process();
+ responseCheck(response, Rcode::NOERROR(), AA_FLAG, 0, 4, 0, NULL,
+ (string(". 3600 IN SOA . . 0 0 0 0 0\n") +
+ ". 3600 IN RRSIG " + getCommonRRSIGText("SOA") + "\n" +
+ ". 3600 IN NSEC " + ". SOA NSEC RRSIG\n" +
+ ". 3600 IN RRSIG " +
+ getCommonRRSIGText("NSEC")).c_str(), NULL);
+}
+
+// Even more pathological case: A faked root zone actually has its own DS
+// query. How we respond wouldn't matter much in practice, but check if
+// it behaves as it's intended. This implementation should return the DS.
+TEST_F(QueryTest, dsAtRootWithDS) {
+ memory_client.addZone(ZoneFinderPtr(
+ new AlternateZoneFinder(Name::ROOT_NAME(),
+ true)));
+ Query(memory_client, Name::ROOT_NAME(), RRType::DS(), response,
+ true).process();
+ responseCheck(response, Rcode::NOERROR(), AA_FLAG, 2, 2, 0,
+ (string(". 3600 IN DS 57855 5 1 49FD46E6C4B45C55D4AC69CBD"
+ "3CD34AC1AFE51DE\n") +
+ ". 3600 IN RRSIG " + getCommonRRSIGText("DS")).c_str(),
+ (string(". 3600 IN NS ns.\n") +
+ ". 3600 IN RRSIG " + getCommonRRSIGText("NS")).c_str(),
+ NULL);
}
+// Check the signature is present when an NXRRSET is returned
TEST_F(QueryTest, nxrrsetWithNSEC3) {
mock_finder->setNSEC3Flag(true);
- ZoneFinder::FindResult result = mock_finder->find(
- Name("www.example.com"), RRType::TXT(), ZoneFinder::FIND_DNSSEC);
- EXPECT_EQ(ZoneFinder::NXRRSET, result.code);
- EXPECT_FALSE(result.rrset);
- EXPECT_TRUE(result.isNSEC3Signed());
- EXPECT_FALSE(result.isWildcard());
+
+ // NXRRSET with DNSSEC proof. We should have SOA, NSEC3 that proves the
+ // NXRRSET and their RRSIGs.
+ Query(memory_client, Name("www.example.com"), RRType::TXT(), response,
+ true).process();
+
+ responseCheck(response, Rcode::NOERROR(), AA_FLAG, 0, 4, 0, NULL,
+ (string(soa_txt) + string("example.com. 3600 IN RRSIG ") +
+ getCommonRRSIGText("SOA") + "\n" +
+ string(nsec3_www_txt) + "\n" +
+ mock_finder->hash_map_[Name("www.example.com.")] +
+ ".example.com. 3600 IN RRSIG " +
+ getCommonRRSIGText("NSEC3") + "\n").c_str(),
+ NULL, mock_finder->getOrigin());
}
-TEST_F(QueryTest, emptyNameWithNSEC3) {
+// Check the exception is correctly raised when the NSEC3 thing isn't in the
+// zone
+TEST_F(QueryTest, nxrrsetMissingNSEC3) {
mock_finder->setNSEC3Flag(true);
- ZoneFinder::FindResult result = mock_finder->find(
- Name("no.example.com"), RRType::A(), ZoneFinder::FIND_DNSSEC);
- EXPECT_EQ(ZoneFinder::NXRRSET, result.code);
- EXPECT_FALSE(result.rrset);
- EXPECT_TRUE(result.isNSEC3Signed());
- EXPECT_FALSE(result.isWildcard());
+ // We just need it to return false for "matched". This indicates
+ // there's no exact match for NSEC3 on www.example.com.
+ ZoneFinder::FindNSEC3Result nsec3(false, 0, ConstRRsetPtr(),
+ ConstRRsetPtr());
+ mock_finder->setNSEC3Result(&nsec3);
+
+ EXPECT_THROW(Query(memory_client, Name("www.example.com"), RRType::TXT(),
+ response, true).process(), Query::BadNSEC3);
}
-TEST_F(QueryTest, wildcardNxrrsetWithNSEC3) {
+TEST_F(QueryTest, nxrrsetWithNSEC3_ds_exact) {
+ mock_finder->addRecord(unsigned_delegation_nsec3_txt);
mock_finder->setNSEC3Flag(true);
- ZoneFinder::FindResult result = mock_finder->find(
- Name("www1.uwild.example.com"), RRType::TXT(),
- ZoneFinder::FIND_DNSSEC);
- EXPECT_EQ(ZoneFinder::NXRRSET, result.code);
- EXPECT_FALSE(result.rrset);
- EXPECT_TRUE(result.isNSEC3Signed());
- EXPECT_TRUE(result.isWildcard());
+
+ // This delegation has no DS, but does have a matching NSEC3 record
+ // (See RFC5155 section 7.2.4)
+ Query(memory_client, Name("unsigned-delegation.example.com."),
+ RRType::DS(), response, true).process();
+ responseCheck(response, Rcode::NOERROR(), AA_FLAG, 0, 4, 0, NULL,
+ (string(soa_txt) + string("example.com. 3600 IN RRSIG ") +
+ getCommonRRSIGText("SOA") + "\n" +
+ string(unsigned_delegation_nsec3_txt) + "\n" +
+ mock_finder->
+ hash_map_[Name("unsigned-delegation.example.com.")] +
+ ".example.com. 3600 IN RRSIG " +
+ getCommonRRSIGText("NSEC3") + "\n").c_str(),
+ NULL, mock_finder->getOrigin());
}
-TEST_F(QueryTest, wildcardEmptyWithNSEC3) {
+TEST_F(QueryTest, nxrrsetWithNSEC3_ds_no_exact) {
+ mock_finder->addRecord(unsigned_delegation_nsec3_txt);
mock_finder->setNSEC3Flag(true);
- ZoneFinder::FindResult result = mock_finder->find(
- Name("a.t.example.com"), RRType::A(), ZoneFinder::FIND_DNSSEC);
- EXPECT_EQ(ZoneFinder::NXRRSET, result.code);
- EXPECT_TRUE(result.isNSEC3Signed());
- EXPECT_TRUE(result.isWildcard());
+
+ // This delegation has no DS, and no directly matching NSEC3 record
+ // So the response should contain closest encloser proof (and the
+ // 'next closer' should have opt-out set, though that is not
+ // actually checked)
+ // (See RFC5155 section 7.2.4)
+ Query(memory_client, Name("unsigned-delegation-optout.example.com."),
+ RRType::DS(), response, true).process();
+ responseCheck(response, Rcode::NOERROR(), AA_FLAG, 0, 6, 0, NULL,
+ (string(soa_txt) + string("example.com. 3600 IN RRSIG ") +
+ getCommonRRSIGText("SOA") + "\n" +
+ string(nsec3_apex_txt) + "\n" +
+ mock_finder->hash_map_[Name("example.com.")] +
+ ".example.com. 3600 IN RRSIG " +
+ getCommonRRSIGText("NSEC3") + "\n" +
+ string(unsigned_delegation_nsec3_txt) + "\n" +
+ mock_finder->
+ hash_map_[Name("unsigned-delegation.example.com.")] +
+ ".example.com. 3600 IN RRSIG " +
+ getCommonRRSIGText("NSEC3") + "\n").c_str(),
+ NULL, mock_finder->getOrigin());
+}
+
+TEST_F(QueryTest, nxdomainWithNSEC3Proof) {
+ // Name Error (NXDOMAIN) case with NSEC3 proof per RFC5155 Section 7.2.2.
+
+ // Enable NSEC3
+ mock_finder->setNSEC3Flag(true);
+ // This will be the covering NSEC3 for the next closer
+ mock_finder->addRecord(nsec3_uwild_txt);
+ // This will be the covering NSEC3 for the possible wildcard
+ mock_finder->addRecord(unsigned_delegation_nsec3_txt);
+
+ Query(memory_client, Name("nxdomain.example.com"), qtype,
+ response, true).process();
+ responseCheck(response, Rcode::NXDOMAIN(), AA_FLAG, 0, 8, 0, NULL,
+ // SOA + its RRSIG
+ (string(soa_txt) +
+ string("example.com. 3600 IN RRSIG ") +
+ getCommonRRSIGText("SOA") + "\n" +
+ // NSEC3 for the closest encloser + its RRSIG
+ string(nsec3_apex_txt) + "\n" +
+ mock_finder->hash_map_[mock_finder->getOrigin()] +
+ string(".example.com. 3600 IN RRSIG ") +
+ getCommonRRSIGText("NSEC3") + "\n" +
+ // NSEC3 for the next closer + its RRSIG
+ string(nsec3_uwild_txt) + "\n" +
+ mock_finder->hash_map_[Name("uwild.example.com")] +
+ ".example.com. 3600 IN RRSIG " +
+ getCommonRRSIGText("NSEC3") + "\n" +
+ // NSEC3 for the wildcard + its RRSIG
+ string(unsigned_delegation_nsec3_txt) +
+ mock_finder->hash_map_[
+ Name("unsigned-delegation.example.com")] +
+ ".example.com. 3600 IN RRSIG " +
+ getCommonRRSIGText("NSEC3")).c_str(),
+ NULL, mock_finder->getOrigin());
+}
+
+TEST_F(QueryTest, nxdomainWithBadNextNSEC3Proof) {
+ // Similar to the previous case, but emulating run time collision by
+ // returning NULL in the next closer proof for the closest encloser
+ // proof.
+ mock_finder->setNSEC3Flag(true);
+ ZoneFinder::FindNSEC3Result nsec3(true, 0, textToRRset(nsec3_apex_txt),
+ ConstRRsetPtr());
+ mock_finder->setNSEC3Result(&nsec3);
+
+ EXPECT_THROW(Query(memory_client, Name("nxdomain.example.com"),
+ RRType::TXT(), response, true).process(),
+ Query::BadNSEC3);
+}
+
+TEST_F(QueryTest, nxdomainWithBadWildcardNSEC3Proof) {
+ // Similar to nxdomainWithNSEC3Proof, but let findNSEC3() return a matching
+ // NSEC3 for the possible wildcard name, emulating run-time collision.
+ // This should result in BadNSEC3 exception.
+
+ mock_finder->setNSEC3Flag(true);
+ mock_finder->addRecord(nsec3_uwild_txt);
+ mock_finder->addRecord(unsigned_delegation_nsec3_txt);
+
+ const Name wname("*.example.com");
+ ZoneFinder::FindNSEC3Result nsec3(true, 0, textToRRset(nsec3_apex_txt),
+ ConstRRsetPtr());
+ mock_finder->setNSEC3Result(&nsec3, &wname);
+
+ EXPECT_THROW(Query(memory_client, Name("nxdomain.example.com"), qtype,
+ response, true).process(),
+ Query::BadNSEC3);
+}
+
+// The following are tentative tests until we really add tests for the
+// query logic for these cases. At that point it's probably better to
+// clean them up.
+TEST_F(QueryTest, emptyNameWithNSEC3) {
+ mock_finder->setNSEC3Flag(true);
+ ZoneFinderContextPtr result = mock_finder->find(
+ Name("no.example.com"), RRType::A(), ZoneFinder::FIND_DNSSEC);
+ EXPECT_EQ(ZoneFinder::NXRRSET, result->code);
+ EXPECT_FALSE(result->rrset);
+ EXPECT_TRUE(result->isNSEC3Signed());
+ EXPECT_FALSE(result->isWildcard());
}
}
diff --git a/src/bin/auth/tests/statistics_unittest.cc b/src/bin/auth/tests/statistics_unittest.cc
index 7d777fd..0595829 100644
--- a/src/bin/auth/tests/statistics_unittest.cc
+++ b/src/bin/auth/tests/statistics_unittest.cc
@@ -21,6 +21,7 @@
#include <boost/bind.hpp>
#include <dns/opcode.h>
+#include <dns/rcode.h>
#include <cc/data.h>
#include <cc/session.h>
@@ -184,6 +185,16 @@ TEST_F(AuthCountersTest, incrementOpcodeCounter) {
}
}
+TEST_F(AuthCountersTest, incrementRcodeCounter) {
+ // The counter should be initialized to 0. If we increment it by 1
+ // the counter should be 1.
+ for (int i = 0; i < 17; ++i) {
+ EXPECT_EQ(0, counters.getCounter(Rcode(i)));
+ counters.inc(Rcode(i));
+ EXPECT_EQ(1, counters.getCounter(Rcode(i)));
+ }
+}
+
TEST_F(AuthCountersTest, submitStatisticsWithoutSession) {
// Set statistics_session to NULL and call submitStatistics().
// Expect to return false.
@@ -225,6 +236,29 @@ opcodeDataCheck(ConstElementPtr data, const int expected[16]) {
ASSERT_EQ(static_cast<const char*>(NULL), item_names[i]);
}
+void
+rcodeDataCheck(ConstElementPtr data, const int expected[17]) {
+ const char* item_names[] = {
+ "noerror", "formerr", "servfail", "nxdomain", "notimp", "refused",
+ "yxdomain", "yxrrset", "nxrrset", "notauth", "notzone", "reserved11",
+ "reserved12", "reserved13", "reserved14", "reserved15", "badvers",
+ NULL
+ };
+ int i;
+ for (i = 0; i < 17; ++i) {
+ ASSERT_NE(static_cast<const char*>(NULL), item_names[i]);
+ const string item_name = "rcode." + string(item_names[i]);
+ if (expected[i] == 0) {
+ EXPECT_FALSE(data->get(item_name));
+ } else {
+ EXPECT_EQ(expected[i], data->get(item_name)->intValue());
+ }
+ }
+ // We should have examined all names
+ ASSERT_EQ(static_cast<const char*>(NULL), item_names[i]);
+}
+
+
TEST_F(AuthCountersTest, submitStatisticsWithoutValidator) {
// Submit statistics data.
// Validate if it submits correct data.
@@ -258,6 +292,10 @@ TEST_F(AuthCountersTest, submitStatisticsWithoutValidator) {
const int opcode_results[16] = { 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0 };
opcodeDataCheck(statistics_data, opcode_results);
+ // By default rcode counters are all 0 and omitted
+ const int rcode_results[17] = { 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0 };
+ rcodeDataCheck(statistics_data, rcode_results);
}
void
@@ -269,6 +307,15 @@ updateOpcodeCounters(AuthCounters &counters, const int expected[16]) {
}
}
+void
+updateRcodeCounters(AuthCounters &counters, const int expected[17]) {
+ for (int i = 0; i < 17; ++i) {
+ for (int j = 0; j < expected[i]; ++j) {
+ counters.inc(Rcode(i));
+ }
+ }
+}
+
TEST_F(AuthCountersTest, submitStatisticsWithOpcodeCounters) {
// Increment some of the opcode counters. Then they should appear in the
// submitted data; others shouldn't
@@ -293,6 +340,30 @@ TEST_F(AuthCountersTest, submitStatisticsWithAllOpcodeCounters) {
opcodeDataCheck(statistics_data, opcode_results);
}
+TEST_F(AuthCountersTest, submitStatisticsWithRcodeCounters) {
+ // Increment some of the rcode counters. Then they should appear in the
+ // submitted data; others shouldn't
+ const int rcode_results[17] = { 1, 2, 3, 4, 5, 6, 7, 8, 9,
+ 10, 0, 0, 0, 0, 0, 0, 11 };
+ updateRcodeCounters(counters, rcode_results);
+ counters.submitStatistics();
+ ConstElementPtr statistics_data = statistics_session_.sent_msg
+ ->get("command")->get(1)->get("data");
+ rcodeDataCheck(statistics_data, rcode_results);
+}
+
+TEST_F(AuthCountersTest, submitStatisticsWithAllRcodeCounters) {
+ // Increment all rcode counters. Then they should appear in the
+ // submitted data.
+ const int rcode_results[17] = { 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1 };
+ updateOpcodeCounters(counters, rcode_results);
+ counters.submitStatistics();
+ ConstElementPtr statistics_data = statistics_session_.sent_msg
+ ->get("command")->get(1)->get("data");
+ opcodeDataCheck(statistics_data, rcode_results);
+}
+
TEST_F(AuthCountersTest, submitStatisticsWithValidator) {
//a validator for the unittest
diff --git a/src/bin/bind10/.gitignore b/src/bin/bind10/.gitignore
new file mode 100644
index 0000000..8dc8a04
--- /dev/null
+++ b/src/bin/bind10/.gitignore
@@ -0,0 +1,3 @@
+/bind10
+/bind10_src.py
+/run_bind10.sh
diff --git a/src/bin/bind10/bind10.8 b/src/bin/bind10/bind10.8
index c2e44e7..11aee7a 100644
--- a/src/bin/bind10/bind10.8
+++ b/src/bin/bind10/bind10.8
@@ -2,12 +2,12 @@
.\" Title: bind10
.\" Author: [see the "AUTHORS" section]
.\" Generator: DocBook XSL Stylesheets v1.75.2 <http://docbook.sf.net/>
-.\" Date: November 23, 2011
+.\" Date: March 1, 2012
.\" Manual: BIND10
.\" Source: BIND10
.\" Language: English
.\"
-.TH "BIND10" "8" "November 23, 2011" "BIND10" "BIND10"
+.TH "BIND10" "8" "March 1, 2012" "BIND10" "BIND10"
.\" -----------------------------------------------------------------
.\" * set default formatting
.\" -----------------------------------------------------------------
@@ -34,9 +34,8 @@ The arguments are as follows:
.PP
\fB\-c\fR \fIconfig\-filename\fR, \fB\-\-config\-file\fR \fIconfig\-filename\fR
.RS 4
-The configuration filename to use\&. Can be either absolute or relative to data path\&. In case it is absolute, value of data path is not considered\&.
-.sp
-Defaults to b10\-config\&.db\&.
+The configuration filename to use\&. Can be either absolute or relative to data path\&. In case it is absolute, value of data path is not considered\&. Defaults to
+b10\-config\&.db\&.
.RE
.PP
\fB\-\-cmdctl\-port\fR \fIport\fR
@@ -50,7 +49,9 @@ for the default\&.)
.PP
\fB\-p\fR \fIdirectory\fR, \fB\-\-data\-path\fR \fIdirectory\fR
.RS 4
-The path where BIND 10 programs look for various data files\&. Currently only b10\-cfgmgr uses it to locate the configuration file, but the usage might be extended for other programs and other types of files\&.
+The path where BIND 10 programs look for various data files\&. Currently only
+\fBb10-cfgmgr\fR(8)
+uses it to locate the configuration file, but the usage might be extended for other programs and other types of files\&.
.RE
.PP
\fB\-m\fR \fIfile\fR, \fB\-\-msgq\-socket\-file\fR \fIfile\fR
@@ -73,7 +74,6 @@ daemon\&.
The username for
\fBbind10\fR
to run as\&.
-
\fBbind10\fR
must be initially ran as the root user to use this option\&. The default is to run as the current user\&.
.RE
@@ -82,7 +82,7 @@ must be initially ran as the root user to use this option\&. The default is to r
.RS 4
If defined, the PID of the
\fBbind10\fR
-is stored in this file\&. This is used for testing purposes\&.
+is stored in this file\&.
.RE
.PP
\fB\-\-pretty\-name \fR\fB\fIname\fR\fR
@@ -103,7 +103,9 @@ and its child processes\&.
.PP
\fB\-w\fR \fIwait_time\fR, \fB\-\-wait\fR \fIwait_time\fR
.RS 4
-Sets the amount of time that BIND 10 will wait for the configuration manager (a key component of BIND 10) to initialize itself before abandoning the start up and terminating with an error\&. The wait_time is specified in seconds and has a default value of 10\&.
+Sets the amount of time that BIND 10 will wait for the configuration manager (a key component of BIND 10) to initialize itself before abandoning the start up and terminating with an error\&. The
+\fIwait_time\fR
+is specified in seconds and has a default value of 10\&.
.RE
.SH "CONFIGURATION AND COMMANDS"
.PP
@@ -145,18 +147,6 @@ to manage under
.IP \(bu 2.3
.\}
-\fI/Boss/components/setuid\fR
-.RE
-.sp
-.RS 4
-.ie n \{\
-\h'-04'\(bu\h'+03'\c
-.\}
-.el \{\
-.sp -1
-.IP \(bu 2.3
-.\}
-
\fI/Boss/components/b10\-stats\fR
.RE
.sp
@@ -212,11 +202,11 @@ to manage under
\fBb10\-sockcreator\fR,
\fBb10\-cfgmgr\fR, and
\fBb10\-msgq\fR
-is not configurable\&. It is hardcoded and
+is not configurable\&. They are hardcoded and
\fBbind10\fR
will not run without them\&.)
.PP
-These named sets (listed above) contain the following settings:
+The named sets for components contain the following settings:
.PP
\fIaddress\fR
.RS 4
@@ -258,7 +248,7 @@ will use the component name instead\&.
.PP
\fIspecial\fR
.RS 4
-This defines if the component is started a special way\&.
+This defines if the component is started a special, hardcoded way\&.
.RE
.PP
The
@@ -307,14 +297,22 @@ will exit\&.
.PP
The statistics data collected by the
\fBb10\-stats\fR
-daemon include:
+daemon for
+\(lqBoss\(rq
+include:
.PP
-bind10\&.boot_time
+boot_time
.RS 4
The date and time that the
\fBbind10\fR
process started\&. This is represented in ISO 8601 format\&.
.RE
+.SH "FILES"
+.PP
+sockcreator\-XXXXXX/sockcreator
+\(em the Unix Domain socket located in a temporary file directory for
+\fBb10\-sockcreator\fR
+communication\&.
.SH "SEE ALSO"
.PP
@@ -339,5 +337,5 @@ The
daemon was initially designed by Shane Kerr of ISC\&.
.SH "COPYRIGHT"
.br
-Copyright \(co 2011 Internet Systems Consortium, Inc. ("ISC")
+Copyright \(co 2010-2012 Internet Systems Consortium, Inc. ("ISC")
.br
diff --git a/src/bin/bind10/bind10.xml b/src/bin/bind10/bind10.xml
index 6705760..1f3cb68 100644
--- a/src/bin/bind10/bind10.xml
+++ b/src/bin/bind10/bind10.xml
@@ -2,7 +2,7 @@
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd"
[<!ENTITY mdash "—">]>
<!--
- - Copyright (C) 2010-2011 Internet Systems Consortium, Inc. ("ISC")
+ - Copyright (C) 2010-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
@@ -20,7 +20,7 @@
<refentry>
<refentryinfo>
- <date>November 23, 2011</date>
+ <date>March 1, 2012</date>
</refentryinfo>
<refmeta>
@@ -36,7 +36,7 @@
<docinfo>
<copyright>
- <year>2011</year>
+ <year>2010-2012</year>
<holder>Internet Systems Consortium, Inc. ("ISC")</holder>
</copyright>
</docinfo>
@@ -97,8 +97,8 @@
<listitem>
<para>The configuration filename to use. Can be either absolute or
relative to data path. In case it is absolute, value of data path is
- not considered.</para>
- <para>Defaults to b10-config.db.</para>
+ not considered.
+ Defaults to <filename>b10-config.db</filename>.</para>
</listitem>
</varlistentry>
@@ -123,9 +123,11 @@
</term>
<listitem>
<para>The path where BIND 10 programs look for various data files.
- Currently only b10-cfgmgr uses it to locate the configuration file,
- but the usage might be extended for other programs and other types
- of files.</para>
+ Currently only
+ <citerefentry><refentrytitle>b10-cfgmgr</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+ uses it to locate the configuration file, but the usage
+ might be extended for other programs and other types of
+ files.</para>
</listitem>
</varlistentry>
@@ -155,9 +157,9 @@
<varlistentry>
<term><option>-u</option> <replaceable>user</replaceable>, <option>--user</option> <replaceable>name</replaceable></term>
+<!-- TODO: example more detail. -->
<listitem>
<para>The username for <command>bind10</command> to run as.
-<!-- TODO: example more detail. -->
<command>bind10</command> must be initially ran as the
root user to use this option.
The default is to run as the current user.</para>
@@ -169,7 +171,6 @@
<listitem>
<para>If defined, the PID of the <command>bind10</command> is stored
in this file.
- This is used for testing purposes.
</para>
</listitem>
</varlistentry>
@@ -201,11 +202,12 @@ The default is the basename of ARG 0.
<varlistentry>
<term><option>-w</option> <replaceable>wait_time</replaceable>, <option>--wait</option> <replaceable>wait_time</replaceable></term>
<listitem>
- <para>Sets the amount of time that BIND 10 will wait for
- the configuration manager (a key component of BIND 10) to
- initialize itself before abandoning the start up and
- terminating with an error. The wait_time is specified in
- seconds and has a default value of 10.
+ <para>Sets the amount of time that BIND 10 will wait for
+ the configuration manager (a key component of BIND 10)
+ to initialize itself before abandoning the start up and
+ terminating with an error. The
+ <replaceable>wait_time</replaceable> is specified in
+ seconds and has a default value of 10.
</para>
</listitem>
</varlistentry>
@@ -238,10 +240,6 @@ TODO: configuration section
</listitem>
<listitem>
- <para> <varname>/Boss/components/setuid</varname> </para>
- </listitem>
-
- <listitem>
<para> <varname>/Boss/components/b10-stats</varname> </para>
</listitem>
@@ -266,12 +264,12 @@ TODO: configuration section
<para>
(Note that the startup of <command>b10-sockcreator</command>,
<command>b10-cfgmgr</command>, and <command>b10-msgq</command>
- is not configurable. It is hardcoded and <command>bind10</command>
+ is not configurable. They are hardcoded and <command>bind10</command>
will not run without them.)
</para>
<para>
- These named sets (listed above) contain the following settings:
+ The named sets for components contain the following settings:
</para>
<variablelist>
@@ -346,7 +344,7 @@ list
<term> <varname>special</varname> </term>
<listitem>
<para>
- This defines if the component is started a special
+ This defines if the component is started a special, hardcoded
way.
<!--
TODO: document this ... but maybe some of these will be removed
@@ -357,7 +355,6 @@ cfgmgr
cmdctl
msgq
resolver
-setuid
sockcreator
xfrin
-->
@@ -374,6 +371,22 @@ xfrin
</para>
<!-- TODO: let's just let bind10 be known as bind10 and not Boss -->
+<!-- TODO -->
+<!--
+ <para>
+ <command>drop_socket</command>
+ This is an internal command and not exposed to the administrator.
+ </para>
+-->
+
+<!-- TODO -->
+<!--
+ <para>
+ <command>get_socket</command>
+ This is an internal command and not exposed to the administrator.
+ </para>
+-->
+
<para>
<command>getstats</command> tells <command>bind10</command>
to send its statistics data to the <command>b10-stats</command>
@@ -420,13 +433,13 @@ xfrin
<para>
The statistics data collected by the <command>b10-stats</command>
- daemon include:
+ daemon for <quote>Boss</quote> include:
</para>
<variablelist>
<varlistentry>
- <term>bind10.boot_time</term>
+ <term>boot_time</term>
<listitem><para>
The date and time that the <command>bind10</command>
process started.
@@ -438,13 +451,16 @@ xfrin
</refsect1>
-<!--
<refsect1>
<title>FILES</title>
- <para><filename></filename>
+ <para><filename>sockcreator-XXXXXX/sockcreator</filename>
+ —
+ the Unix Domain socket located in a temporary file directory for
+ <command>b10-sockcreator</command>
+<!-- <citerefentry><refentrytitle>b10-sockcreator</refentrytitle><manvolnum>8</manvolnum></citerefentry> -->
+ communication.
</para>
</refsect1>
--->
<refsect1>
<title>SEE ALSO</title>
@@ -476,6 +492,9 @@ xfrin
<citetitle>BIND 10 Guide</citetitle>.
</para>
</refsect1>
+<!-- <citerefentry>
+ <refentrytitle>b10-sockcreator</refentrytitle><manvolnum>8</manvolnum>
+ </citerefentry>, -->
<refsect1 id='history'><title>HISTORY</title>
<para>The development of <command>bind10</command>
diff --git a/src/bin/bind10/bind10_messages.mes b/src/bin/bind10/bind10_messages.mes
index 79635fd..3dd938f 100644
--- a/src/bin/bind10/bind10_messages.mes
+++ b/src/bin/bind10/bind10_messages.mes
@@ -24,7 +24,7 @@ needs a dedicated message bus.
An error was encountered when the boss module specified
statistics data which is invalid for the boss specification file.
-% BIND10_COMPONENT_FAILED component %1 (pid %2) failed with %3 exit status
+% BIND10_COMPONENT_FAILED component %1 (pid %2) failed: %3
The process terminated, but the bind10 boss didn't expect it to, which means
it must have failed.
diff --git a/src/bin/bind10/bind10_src.py.in b/src/bin/bind10/bind10_src.py.in
index a09677e..edc1b69 100755
--- a/src/bin/bind10/bind10_src.py.in
+++ b/src/bin/bind10/bind10_src.py.in
@@ -193,9 +193,13 @@ class BoB:
self.nocache = nocache
self.component_config = {}
# Some time in future, it may happen that a single component has
- # multple processes. If so happens, name "components" may be
- # inapropriate. But as the code isn't probably completely ready
- # for it, we leave it at components for now.
+ # multple processes (like a pipeline-like component). If so happens,
+ # name "components" may be inapropriate. But as the code isn't probably
+ # completely ready for it, we leave it at components for now. We also
+ # want to support multiple instances of a single component. If it turns
+ # out that we'll have a single component with multiple same processes
+ # or if we start multiple components with the same configuration (we do
+ # this now, but it might change) is an open question.
self.components = {}
# Simply list of components that died and need to wait for a
# restart. Components manage their own restart schedule now
@@ -649,14 +653,17 @@ class BoB:
self.__started = True
return None
- def stop_process(self, process, recipient):
+ def stop_process(self, process, recipient, pid):
"""
Stop the given process, friendly-like. The process is the name it has
- (in logs, etc), the recipient is the address on msgq.
+ (in logs, etc), the recipient is the address on msgq. The pid is the
+ pid of the process (if we have multiple processes of the same name,
+ it might want to choose if it is for this one).
"""
logger.info(BIND10_STOP_PROCESS, process)
- self.cc_session.group_sendmsg({'command': ['shutdown']}, recipient,
- recipient)
+ self.cc_session.group_sendmsg(isc.config.ccsession.
+ create_command('shutdown', {'pid': pid}),
+ recipient, recipient)
def component_shutdown(self, exitcode=0):
"""
@@ -679,7 +686,13 @@ class BoB:
def shutdown(self):
"""Stop the BoB instance."""
logger.info(BIND10_SHUTDOWN)
- # first try using the BIND 10 request to stop
+ # If ccsession is still there, inform rest of the system this module
+ # is stopping. Since everything will be stopped shortly, this is not
+ # really necessary, but this is done to reflect that boss is also
+ # 'just' a module.
+ self.ccs.send_stopping()
+
+ # try using the BIND 10 request to stop
try:
self._component_configurator.shutdown()
except:
@@ -879,7 +892,7 @@ class BoB:
# the need to find the place ourself or bother users. Also, this
# secures the socket on some platforms, as it creates a private
# directory.
- self._tmpdir = tempfile.mkdtemp()
+ self._tmpdir = tempfile.mkdtemp(prefix='sockcreator-')
# Get the name
self._socket_path = os.path.join(self._tmpdir, "sockcreator")
# And bind the socket to the name
diff --git a/src/bin/bind10/tests/.gitignore b/src/bin/bind10/tests/.gitignore
new file mode 100644
index 0000000..5e54716
--- /dev/null
+++ b/src/bin/bind10/tests/.gitignore
@@ -0,0 +1 @@
+/bind10_test.py
diff --git a/src/bin/bind10/tests/bind10_test.py.in b/src/bin/bind10/tests/bind10_test.py.in
index 018a3b9..882824d 100644
--- a/src/bin/bind10/tests/bind10_test.py.in
+++ b/src/bin/bind10/tests/bind10_test.py.in
@@ -36,6 +36,7 @@ import isc.bind10.socket_cache
import errno
from isc.testutils.parse_args import TestOptParser, OptsError
+from isc.testutils.ccsession_mock import MockModuleCCSession
class TestProcessInfo(unittest.TestCase):
def setUp(self):
@@ -459,6 +460,22 @@ class TestBoB(unittest.TestCase):
# The drop_socket is not tested here, but in TestCacheCommands.
# It needs the cache mocks to be in place and they are there.
+ def test_stop_process(self):
+ """
+ Test checking the stop_process method sends the right message over
+ the message bus.
+ """
+ class DummySession():
+ def group_sendmsg(self, msg, group, instance="*"):
+ (self.msg, self.group, self.instance) = (msg, group, instance)
+ bob = BoB()
+ bob.cc_session = DummySession()
+ bob.stop_process('process', 'address', 42)
+ self.assertEqual('address', bob.cc_session.group)
+ self.assertEqual('address', bob.cc_session.instance)
+ self.assertEqual({'command': ['shutdown', {'pid': 42}]},
+ bob.cc_session.msg)
+
# Class for testing the BoB without actually starting processes.
# This is used for testing the start/stop components routines and
# the BoB commands.
@@ -597,7 +614,7 @@ class MockBob(BoB):
procinfo.pid = 14
return procinfo
- def stop_process(self, process, recipient):
+ def stop_process(self, process, recipient, pid):
procmap = { 'b10-auth': self.stop_auth,
'b10-resolver': self.stop_resolver,
'b10-xfrout': self.stop_xfrout,
@@ -1171,8 +1188,12 @@ class TestBossComponents(unittest.TestCase):
bob._component_configurator.shutdown = self.__nullary_hook
self.__called = False
+ bob.ccs = MockModuleCCSession()
+ self.assertFalse(bob.ccs.stopped)
+
bob.shutdown()
+ self.assertTrue(bob.ccs.stopped)
self.assertEqual([False, True], killed)
self.assertTrue(self.__called)
diff --git a/src/bin/bindctl/.gitignore b/src/bin/bindctl/.gitignore
new file mode 100644
index 0000000..71fba03
--- /dev/null
+++ b/src/bin/bindctl/.gitignore
@@ -0,0 +1,3 @@
+/bindctl
+/bindctl_main.py
+/run_bindctl.sh
diff --git a/src/bin/bindctl/tests/.gitignore b/src/bin/bindctl/tests/.gitignore
new file mode 100644
index 0000000..284bacc
--- /dev/null
+++ b/src/bin/bindctl/tests/.gitignore
@@ -0,0 +1 @@
+/bindctl_test
diff --git a/src/bin/cfgmgr/.gitignore b/src/bin/cfgmgr/.gitignore
new file mode 100644
index 0000000..aad54f4
--- /dev/null
+++ b/src/bin/cfgmgr/.gitignore
@@ -0,0 +1,2 @@
+/b10-cfgmgr
+/b10-cfgmgr.py
diff --git a/src/bin/cfgmgr/tests/.gitignore b/src/bin/cfgmgr/tests/.gitignore
new file mode 100644
index 0000000..dcdab2d
--- /dev/null
+++ b/src/bin/cfgmgr/tests/.gitignore
@@ -0,0 +1 @@
+/b10-cfgmgr_test.py
diff --git a/src/bin/cmdctl/.gitignore b/src/bin/cmdctl/.gitignore
new file mode 100644
index 0000000..a194135
--- /dev/null
+++ b/src/bin/cmdctl/.gitignore
@@ -0,0 +1,5 @@
+/b10-cmdctl
+/cmdctl.py
+/cmdctl.spec
+/cmdctl.spec.pre
+/run_b10-cmdctl.sh
diff --git a/src/bin/cmdctl/b10-cmdctl.8 b/src/bin/cmdctl/b10-cmdctl.8
index c8c938b..0b478fe 100644
--- a/src/bin/cmdctl/b10-cmdctl.8
+++ b/src/bin/cmdctl/b10-cmdctl.8
@@ -2,12 +2,12 @@
.\" Title: b10-cmdctl
.\" Author: [see the "AUTHORS" section]
.\" Generator: DocBook XSL Stylesheets v1.75.2 <http://docbook.sf.net/>
-.\" Date: March 9, 2010
+.\" Date: February 28, 2012
.\" Manual: BIND10
.\" Source: BIND10
.\" Language: English
.\"
-.TH "B10\-CMDCTL" "8" "March 9, 2010" "BIND10" "BIND10"
+.TH "B10\-CMDCTL" "8" "February 28, 2012" "BIND10" "BIND10"
.\" -----------------------------------------------------------------
.\" * set default formatting
.\" -----------------------------------------------------------------
@@ -70,6 +70,33 @@ Enable verbose mode\&.
.RS 4
Display the version number and exit\&.
.RE
+.SH "CONFIGURATION AND COMMANDS"
+.PP
+The configurable settings are:
+.PP
+
+\fIaccounts_file\fR
+defines the path to the user accounts database\&. The default is
+/usr/local/etc/bind10\-devel/cmdctl\-accounts\&.csv\&.
+.PP
+
+\fIcert_file\fR
+defines the path to the PEM certificate file\&. The default is
+/usr/local/etc/bind10\-devel/cmdctl\-certfile\&.pem\&.
+.PP
+
+\fIkey_file\fR
+defines the path to the PEM private key file\&. The default is
+/usr/local/etc/bind10\-devel/cmdctl\-keyfile\&.pem\&.
+.PP
+The configuration command is:
+.PP
+
+\fBshutdown\fR
+exits
+\fBb10\-cmdctl\fR\&. This has an optional
+\fIpid\fR
+argument to select the process ID to stop\&. (Note that the BIND 10 boss process may restart this service if configured\&.)
.SH "FILES"
.PP
/usr/local/etc/bind10\-devel/cmdctl\-accounts\&.csv
@@ -93,5 +120,5 @@ The
daemon was initially designed and coded by Zhang Likun of CNNIC\&.
.SH "COPYRIGHT"
.br
-Copyright \(co 2010 Internet Systems Consortium, Inc. ("ISC")
+Copyright \(co 2010-2012 Internet Systems Consortium, Inc. ("ISC")
.br
diff --git a/src/bin/cmdctl/b10-cmdctl.xml b/src/bin/cmdctl/b10-cmdctl.xml
index 06953a4..e01d5a2 100644
--- a/src/bin/cmdctl/b10-cmdctl.xml
+++ b/src/bin/cmdctl/b10-cmdctl.xml
@@ -2,7 +2,7 @@
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd"
[<!ENTITY mdash "—">]>
<!--
- - Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC")
+ - Copyright (C) 2010-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
@@ -20,7 +20,7 @@
<refentry>
<refentryinfo>
- <date>March 9, 2010</date>
+ <date>February 28, 2012</date>
</refentryinfo>
<refmeta>
@@ -37,7 +37,7 @@
<docinfo>
<copyright>
- <year>2010</year>
+ <year>2010-2012</year>
<holder>Internet Systems Consortium, Inc. ("ISC")</holder>
</copyright>
</docinfo>
@@ -138,6 +138,50 @@
</refsect1>
<refsect1>
+ <title>CONFIGURATION AND COMMANDS</title>
+ <para>
+ The configurable settings are:
+ </para>
+
+ <para>
+ <varname>accounts_file</varname> defines the path to the
+ user accounts database.
+ The default is
+ <filename>/usr/local/etc/bind10-devel/cmdctl-accounts.csv</filename>.
+ </para>
+
+ <para>
+ <varname>cert_file</varname> defines the path to the
+ PEM certificate file.
+ The default is
+ <filename>/usr/local/etc/bind10-devel/cmdctl-certfile.pem</filename>.
+ </para>
+
+ <para>
+ <varname>key_file</varname> defines the path to the PEM private key
+ file.
+ The default is
+ <filename>/usr/local/etc/bind10-devel/cmdctl-keyfile.pem</filename>.
+ </para>
+
+<!-- TODO: formating -->
+ <para>
+ The configuration command is:
+ </para>
+
+<!-- NOTE: print_settings is not documented since I think will be removed -->
+
+ <para>
+ <command>shutdown</command> exits <command>b10-cmdctl</command>.
+ This has an optional <varname>pid</varname> argument to
+ select the process ID to stop.
+ (Note that the BIND 10 boss process may restart this service
+ if configured.)
+ </para>
+
+ </refsect1>
+
+ <refsect1>
<title>FILES</title>
<!-- TODO: replace /usr/local -->
<!-- TODO: permissions -->
diff --git a/src/bin/cmdctl/cmdctl.py.in b/src/bin/cmdctl/cmdctl.py.in
index ff221db..74fc364 100755
--- a/src/bin/cmdctl/cmdctl.py.in
+++ b/src/bin/cmdctl/cmdctl.py.in
@@ -310,12 +310,25 @@ class CommandControl():
def command_handler(self, command, args):
answer = ccsession.create_answer(0)
if command == ccsession.COMMAND_MODULE_SPECIFICATION_UPDATE:
+ # The 'value' of a specification update can be either
+ # a specification, or None. In the first case, simply
+ # set it. If it is None, delete the module if it is
+ # known.
with self._lock:
- self.modules_spec[args[0]] = args[1]
+ if args[1] is None:
+ if args[0] in self.modules_spec:
+ del self.modules_spec[args[0]]
+ else:
+ answer = ccsession.create_answer(1,
+ 'No such module: ' +
+ args[0])
+ else:
+ self.modules_spec[args[0]] = args[1]
elif command == ccsession.COMMAND_SHUTDOWN:
#When cmdctl get 'shutdown' command from boss,
#shutdown the outer httpserver.
+ self._module_cc.send_stopping()
self._httpserver.shutdown()
self._serving = False
diff --git a/src/bin/cmdctl/cmdctl.spec.pre.in b/src/bin/cmdctl/cmdctl.spec.pre.in
index 537b678..d04e2e3 100644
--- a/src/bin/cmdctl/cmdctl.spec.pre.in
+++ b/src/bin/cmdctl/cmdctl.spec.pre.in
@@ -31,7 +31,13 @@
{
"command_name": "shutdown",
"command_description": "shutdown cmdctl",
- "command_args": []
+ "command_args": [
+ {
+ "item_name": "pid",
+ "item_type": "integer",
+ "item_optional": true
+ }
+ ]
}
]
}
diff --git a/src/bin/cmdctl/tests/.gitignore b/src/bin/cmdctl/tests/.gitignore
new file mode 100644
index 0000000..ab9dfef
--- /dev/null
+++ b/src/bin/cmdctl/tests/.gitignore
@@ -0,0 +1 @@
+/cmdctl_test
diff --git a/src/bin/cmdctl/tests/cmdctl_test.py b/src/bin/cmdctl/tests/cmdctl_test.py
index 3103f47..5fdabb4 100644
--- a/src/bin/cmdctl/tests/cmdctl_test.py
+++ b/src/bin/cmdctl/tests/cmdctl_test.py
@@ -345,6 +345,40 @@ class TestCommandControl(unittest.TestCase):
self.assertEqual(rcode, 0)
self.assertTrue(msg != None)
+ def test_command_handler_spec_update(self):
+ # Should not be present
+ self.assertFalse("foo" in self.cmdctl.modules_spec)
+
+ answer = self.cmdctl.command_handler(
+ ccsession.COMMAND_MODULE_SPECIFICATION_UPDATE, [ "foo", {} ])
+ rcode, msg = ccsession.parse_answer(answer)
+ self.assertEqual(rcode, 0)
+ self.assertEqual(msg, None)
+
+ # Should now be present
+ self.assertTrue("foo" in self.cmdctl.modules_spec)
+
+ # When sending specification 'None', it should be removed
+ answer = self.cmdctl.command_handler(
+ ccsession.COMMAND_MODULE_SPECIFICATION_UPDATE, [ "foo", None ])
+ rcode, msg = ccsession.parse_answer(answer)
+ self.assertEqual(rcode, 0)
+ self.assertEqual(msg, None)
+
+ # Should no longer be present
+ self.assertFalse("foo" in self.cmdctl.modules_spec)
+
+ # Don't store 'None' if it wasn't there in the first place!
+ answer = self.cmdctl.command_handler(
+ ccsession.COMMAND_MODULE_SPECIFICATION_UPDATE, [ "foo", None ])
+ rcode, msg = ccsession.parse_answer(answer)
+ self.assertEqual(rcode, 1)
+ self.assertEqual(msg, "No such module: foo")
+
+ # Should still not present
+ self.assertFalse("foo" in self.cmdctl.modules_spec)
+
+
def test_check_config_handler(self):
answer = self.cmdctl.config_handler({'non-exist': 123})
self._check_answer(answer, 1, 'unknown config item: non-exist')
diff --git a/src/bin/ddns/.gitignore b/src/bin/ddns/.gitignore
new file mode 100644
index 0000000..92b86f3
--- /dev/null
+++ b/src/bin/ddns/.gitignore
@@ -0,0 +1,2 @@
+/b10-ddns
+/ddns.py
diff --git a/src/bin/ddns/b10-ddns.8 b/src/bin/ddns/b10-ddns.8
index 67a5059..131b6cc 100644
--- a/src/bin/ddns/b10-ddns.8
+++ b/src/bin/ddns/b10-ddns.8
@@ -2,12 +2,12 @@
.\" Title: b10-ddns
.\" Author: [FIXME: author] [see http://docbook.sf.net/el/author]
.\" Generator: DocBook XSL Stylesheets v1.75.2 <http://docbook.sf.net/>
-.\" Date: December 9, 2011
+.\" Date: February 28, 2012
.\" Manual: BIND10
.\" Source: BIND10
.\" Language: English
.\"
-.TH "B10\-DDNS" "8" "December 9, 2011" "BIND10" "BIND10"
+.TH "B10\-DDNS" "8" "February 28, 2012" "BIND10" "BIND10"
.\" -----------------------------------------------------------------
.\" * set default formatting
.\" -----------------------------------------------------------------
@@ -81,8 +81,10 @@ The module commands are:
.PP
\fBshutdown\fR
-Exits
-\fBb10\-ddns\fR\&. (Note that the BIND 10 boss process will restart this service\&.)
+exits
+\fBb10\-ddns\fR\&. This has an optional
+\fIpid\fR
+argument to select the process ID to stop\&. (Note that the BIND 10 boss process may restart this service if configured\&.)
.SH "SEE ALSO"
.PP
@@ -98,5 +100,5 @@ The
daemon was first implemented in December 2011 for the ISC BIND 10 project\&.
.SH "COPYRIGHT"
.br
-Copyright \(co 2011 Internet Systems Consortium, Inc. ("ISC")
+Copyright \(co 2011-2012 Internet Systems Consortium, Inc. ("ISC")
.br
diff --git a/src/bin/ddns/b10-ddns.xml b/src/bin/ddns/b10-ddns.xml
index b9cd117..15fcb1a 100644
--- a/src/bin/ddns/b10-ddns.xml
+++ b/src/bin/ddns/b10-ddns.xml
@@ -2,7 +2,7 @@
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd"
[<!ENTITY mdash "—">]>
<!--
- - Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+ - 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
@@ -20,7 +20,7 @@
<refentry>
<refentryinfo>
- <date>December 9, 2011</date>
+ <date>February 28, 2012</date>
</refentryinfo>
<refmeta>
@@ -36,7 +36,7 @@
<docinfo>
<copyright>
- <year>2011</year>
+ <year>2011-2012</year>
<holder>Internet Systems Consortium, Inc. ("ISC")</holder>
</copyright>
</docinfo>
@@ -122,8 +122,11 @@
The module commands are:
</para>
<para>
- <command>shutdown</command> Exits <command>b10-ddns</command>.
- (Note that the BIND 10 boss process will restart this service.)
+ <command>shutdown</command> exits <command>b10-ddns</command>.
+ This has an optional <varname>pid</varname> argument to
+ select the process ID to stop.
+ (Note that the BIND 10 boss process may restart this service
+ if configured.)
</para>
</refsect1>
diff --git a/src/bin/ddns/ddns.py.in b/src/bin/ddns/ddns.py.in
index cde14c7..22e4e9c 100755
--- a/src/bin/ddns/ddns.py.in
+++ b/src/bin/ddns/ddns.py.in
@@ -150,9 +150,10 @@ class DDNSServer:
Perform any cleanup that is necessary when shutting down the server.
Do NOT call this to initialize shutdown, use trigger_shutdown().
- Currently, it does nothing, but cleanup routines are expected.
+ Currently, it only causes the ModuleCCSession to send a message that
+ this module is stopping.
'''
- pass
+ self._cc.send_stopping()
def accept(self):
"""
diff --git a/src/bin/ddns/ddns.spec b/src/bin/ddns/ddns.spec
index 07cd2a9..55dab5c 100644
--- a/src/bin/ddns/ddns.spec
+++ b/src/bin/ddns/ddns.spec
@@ -34,7 +34,13 @@
{
"command_name": "shutdown",
"command_description": "Shut down DDNS",
- "command_args": []
+ "command_args": [
+ {
+ "item_name": "pid",
+ "item_type": "integer",
+ "item_optional": true
+ }
+ ]
}
]
}
diff --git a/src/bin/ddns/tests/ddns_test.py b/src/bin/ddns/tests/ddns_test.py
index 6adb97c..395aacc 100755
--- a/src/bin/ddns/tests/ddns_test.py
+++ b/src/bin/ddns/tests/ddns_test.py
@@ -58,11 +58,16 @@ class MyCCSession(isc.config.ConfigData):
ddns.SPECFILE_LOCATION)
isc.config.ConfigData.__init__(self, module_spec)
self._started = False
+ self._stopped = False
def start(self):
'''Called by DDNSServer initialization, but not used in tests'''
self._started = True
+ def send_stopping(self):
+ '''Called by shutdown code'''
+ self._stopped = True
+
def get_socket(self):
"""
Used to get the file number for select.
@@ -289,6 +294,7 @@ class TestDDNSServer(unittest.TestCase):
self.__select_answer = ([3], [], [])
self.ddns_server.run()
self.assertTrue(self.ddns_server._shutdown)
+ self.assertTrue(self.__cc_session._stopped)
self.assertIsNone(self.__select_answer)
self.assertEqual(3, self.__hook_called)
diff --git a/src/bin/dhcp4/.gitignore b/src/bin/dhcp4/.gitignore
new file mode 100644
index 0000000..f7e9973
--- /dev/null
+++ b/src/bin/dhcp4/.gitignore
@@ -0,0 +1,3 @@
+/b10-dhcp4
+/spec_config.h
+/spec_config.h.pre
diff --git a/src/bin/dhcp4/Makefile.am b/src/bin/dhcp4/Makefile.am
index 513ae1c..6c52c05 100644
--- a/src/bin/dhcp4/Makefile.am
+++ b/src/bin/dhcp4/Makefile.am
@@ -15,7 +15,7 @@ pkglibexecdir = $(libexecdir)/@PACKAGE@
CLEANFILES = spec_config.h
man_MANS = b10-dhcp4.8
-EXTRA_DIST = $(man_MANS) dhcp4.spec
+EXTRA_DIST = $(man_MANS) b10-dhcp4.xml dhcp4.spec
if ENABLE_MAN
diff --git a/src/bin/dhcp4/tests/.gitignore b/src/bin/dhcp4/tests/.gitignore
new file mode 100644
index 0000000..5d14dac
--- /dev/null
+++ b/src/bin/dhcp4/tests/.gitignore
@@ -0,0 +1 @@
+/dhcp4_unittests
diff --git a/src/bin/dhcp6/Makefile.am b/src/bin/dhcp6/Makefile.am
index b1b0798..abfffb9 100644
--- a/src/bin/dhcp6/Makefile.am
+++ b/src/bin/dhcp6/Makefile.am
@@ -17,7 +17,7 @@ pkglibexecdir = $(libexecdir)/@PACKAGE@
CLEANFILES = *.gcno *.gcda spec_config.h
man_MANS = b10-dhcp6.8
-EXTRA_DIST = $(man_MANS) dhcp6.spec interfaces.txt
+EXTRA_DIST = $(man_MANS) b10-dhcp6.xml dhcp6.spec interfaces.txt
if ENABLE_MAN
diff --git a/src/bin/host/.gitignore b/src/bin/host/.gitignore
new file mode 100644
index 0000000..0073523
--- /dev/null
+++ b/src/bin/host/.gitignore
@@ -0,0 +1 @@
+/b10-host
diff --git a/src/bin/host/host.cc b/src/bin/host/host.cc
index f0df0c8..f1bb415 100644
--- a/src/bin/host/host.cc
+++ b/src/bin/host/host.cc
@@ -70,8 +70,7 @@ host_lookup(const char* const name, const char* const dns_class,
RRClass(dns_class),
any ? RRType::ANY() : RRType(type))); // if NULL then:
- OutputBuffer obuffer(512);
- MessageRenderer renderer(obuffer);
+ MessageRenderer renderer;
msg.toWire(renderer);
struct addrinfo hints, *res;
@@ -111,7 +110,7 @@ host_lookup(const char* const name, const char* const dns_class,
gettimeofday(&before_time, NULL);
}
- sendto(s, obuffer.getData(), obuffer.getLength(), 0, res->ai_addr,
+ sendto(s, renderer.getData(), renderer.getLength(), 0, res->ai_addr,
res->ai_addrlen);
struct sockaddr_storage ss;
diff --git a/src/bin/loadzone/.gitignore b/src/bin/loadzone/.gitignore
new file mode 100644
index 0000000..86761ee
--- /dev/null
+++ b/src/bin/loadzone/.gitignore
@@ -0,0 +1,3 @@
+/b10-loadzone
+/b10-loadzone.py
+/run_loadzone.sh
diff --git a/src/bin/loadzone/tests/correct/.gitignore b/src/bin/loadzone/tests/correct/.gitignore
new file mode 100644
index 0000000..2d58698
--- /dev/null
+++ b/src/bin/loadzone/tests/correct/.gitignore
@@ -0,0 +1 @@
+/correct_test.sh
diff --git a/src/bin/loadzone/tests/error/.gitignore b/src/bin/loadzone/tests/error/.gitignore
new file mode 100644
index 0000000..5d20adb
--- /dev/null
+++ b/src/bin/loadzone/tests/error/.gitignore
@@ -0,0 +1 @@
+/error_test.sh
diff --git a/src/bin/msgq/.gitignore b/src/bin/msgq/.gitignore
new file mode 100644
index 0000000..ee1d942
--- /dev/null
+++ b/src/bin/msgq/.gitignore
@@ -0,0 +1,3 @@
+/b10-msgq
+/msgq.py
+/run_msgq.sh
diff --git a/src/bin/msgq/tests/.gitignore b/src/bin/msgq/tests/.gitignore
new file mode 100644
index 0000000..70acfff
--- /dev/null
+++ b/src/bin/msgq/tests/.gitignore
@@ -0,0 +1 @@
+/msgq_test
diff --git a/src/bin/resolver/.gitignore b/src/bin/resolver/.gitignore
new file mode 100644
index 0000000..95abd50
--- /dev/null
+++ b/src/bin/resolver/.gitignore
@@ -0,0 +1,7 @@
+/b10-resolver
+/resolver.spec
+/resolver.spec.pre
+/resolver_messages.cc
+/resolver_messages.h
+/spec_config.h
+/spec_config.h.pre
diff --git a/src/bin/resolver/Makefile.am b/src/bin/resolver/Makefile.am
index faa166f..2bb3768 100644
--- a/src/bin/resolver/Makefile.am
+++ b/src/bin/resolver/Makefile.am
@@ -51,6 +51,7 @@ b10_resolver_SOURCES += resolver_log.cc resolver_log.h
b10_resolver_SOURCES += response_scrubber.cc response_scrubber.h
b10_resolver_SOURCES += $(top_builddir)/src/bin/auth/common.h
b10_resolver_SOURCES += main.cc
+b10_resolver_SOURCES += common.cc common.h
nodist_b10_resolver_SOURCES = resolver_messages.cc resolver_messages.h
diff --git a/src/bin/resolver/b10-resolver.8 b/src/bin/resolver/b10-resolver.8
index 7a4cb6d..eed69b8 100644
--- a/src/bin/resolver/b10-resolver.8
+++ b/src/bin/resolver/b10-resolver.8
@@ -2,12 +2,12 @@
.\" Title: b10-resolver
.\" Author: [FIXME: author] [see http://docbook.sf.net/el/author]
.\" Generator: DocBook XSL Stylesheets v1.75.2 <http://docbook.sf.net/>
-.\" Date: December 28, 2011
+.\" Date: February 28, 2012
.\" Manual: BIND10
.\" Source: BIND10
.\" Language: English
.\"
-.TH "B10\-RESOLVER" "8" "December 28, 2011" "BIND10" "BIND10"
+.TH "B10\-RESOLVER" "8" "February 28, 2012" "BIND10" "BIND10"
.\" -----------------------------------------------------------------
.\" * set default formatting
.\" -----------------------------------------------------------------
@@ -118,7 +118,9 @@ The configuration command is:
\fBshutdown\fR
exits
-\fBb10\-resolver\fR\&. (Note that the BIND 10 boss process will restart this service\&.)
+\fBb10\-resolver\fR\&. This has an optional
+\fIpid\fR
+argument to select the process ID to stop\&. (Note that the BIND 10 boss process may restart this service if configured\&.)
.SH "SEE ALSO"
.PP
@@ -134,5 +136,5 @@ The
daemon was first coded in September 2010\&. The initial implementation only provided forwarding\&. Iteration was introduced in January 2011\&. Caching was implemented in February 2011\&. Access control was introduced in June 2011\&.
.SH "COPYRIGHT"
.br
-Copyright \(co 2010 Internet Systems Consortium, Inc. ("ISC")
+Copyright \(co 2010-2012 Internet Systems Consortium, Inc. ("ISC")
.br
diff --git a/src/bin/resolver/b10-resolver.xml b/src/bin/resolver/b10-resolver.xml
index 05472b5..aca8fb2 100644
--- a/src/bin/resolver/b10-resolver.xml
+++ b/src/bin/resolver/b10-resolver.xml
@@ -2,7 +2,7 @@
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd"
[<!ENTITY mdash "—">]>
<!--
- - Copyright (C) 2010-2011 Internet Systems Consortium, Inc. ("ISC")
+ - Copyright (C) 2010-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
@@ -20,7 +20,7 @@
<refentry>
<refentryinfo>
- <date>December 28, 2011</date>
+ <date>February 28, 2012</date>
</refentryinfo>
<refmeta>
@@ -36,7 +36,7 @@
<docinfo>
<copyright>
- <year>2010</year>
+ <year>2010-2012</year>
<holder>Internet Systems Consortium, Inc. ("ISC")</holder>
</copyright>
</docinfo>
@@ -201,7 +201,10 @@ once that is merged you can for instance do 'config add Resolver/forward_address
<para>
<command>shutdown</command> exits <command>b10-resolver</command>.
- (Note that the BIND 10 boss process will restart this service.)
+ This has an optional <varname>pid</varname> argument to
+ select the process ID to stop.
+ (Note that the BIND 10 boss process may restart this service
+ if configured.)
</para>
</refsect1>
diff --git a/src/bin/resolver/common.cc b/src/bin/resolver/common.cc
new file mode 100644
index 0000000..ad4fc50
--- /dev/null
+++ b/src/bin/resolver/common.cc
@@ -0,0 +1,17 @@
+// 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 "common.h"
+
+const char* const RESOLVER_NAME = "b10-resolver";
diff --git a/src/bin/resolver/common.h b/src/bin/resolver/common.h
new file mode 100644
index 0000000..bcab8ba
--- /dev/null
+++ b/src/bin/resolver/common.h
@@ -0,0 +1,23 @@
+// 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 RESOLVER_COMMON_H
+#define RESOLVER_COMMON_H
+
+/// \brief The name used to identify the resolver between modules.
+///
+/// It is currently set to b10-resolver.
+extern const char* const RESOLVER_NAME;
+
+#endif
diff --git a/src/bin/resolver/main.cc b/src/bin/resolver/main.cc
index bfcad67..d14fb0b 100644
--- a/src/bin/resolver/main.cc
+++ b/src/bin/resolver/main.cc
@@ -14,18 +14,10 @@
#include <config.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <sys/select.h>
-#include <netdb.h>
-#include <netinet/in.h>
-#include <stdlib.h>
-#include <errno.h>
-
-#include <string>
-#include <iostream>
-
-#include <boost/foreach.hpp>
+#include <resolver/spec_config.h>
+#include <resolver/resolver.h>
+#include "resolver_log.h"
+#include "common.h"
#include <asiodns/asiodns.h>
#include <asiolink/asiolink.h>
@@ -47,9 +39,6 @@
#include <auth/common.h>
-#include <resolver/spec_config.h>
-#include <resolver/resolver.h>
-
#include <cache/resolver_cache.h>
#include <nsas/nameserver_address_store.h>
@@ -57,6 +46,20 @@
#include <log/logger_level.h>
#include "resolver_log.h"
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/select.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <unistd.h>
+
+#include <string>
+#include <iostream>
+
+#include <boost/foreach.hpp>
+
using namespace std;
using namespace isc::cc;
using namespace isc::config;
@@ -80,15 +83,32 @@ ConstElementPtr
my_command_handler(const string& command, ConstElementPtr args) {
ConstElementPtr answer = createAnswer();
- if (command == "print_message") {
- LOG_INFO(resolver_logger, RESOLVER_PRINT_COMMAND).arg(args);
- /* let's add that message to our answer as well */
- answer = createAnswer(0, args);
- } else if (command == "shutdown") {
- io_service.stop();
- }
+ try {
+ if (command == "print_message") {
+ LOG_INFO(resolver_logger, RESOLVER_PRINT_COMMAND).arg(args);
+ /* let's add that message to our answer as well */
+ answer = createAnswer(0, args);
+ } else if (command == "shutdown") {
+ // Is the pid argument provided?
+ if (args && args->contains("pid")) {
+ // If it is, we check it is the same as our PID
+ const int pid(args->get("pid")->intValue());
+ const pid_t my_pid(getpid());
+ if (my_pid != pid) {
+ // It is not for us (this is expected, see auth/command.cc
+ // and the ShutdownCommand there).
+ return (answer);
+ }
+ }
+ LOG_DEBUG(resolver_logger, RESOLVER_DBG_INIT,
+ RESOLVER_SHUTDOWN_RECEIVED);
+ io_service.stop();
+ }
- return (answer);
+ return (answer);
+ } catch (const std::exception& e) {
+ return (createAnswer(1, e.what()));
+ }
}
void
@@ -121,7 +141,7 @@ main(int argc, char* argv[]) {
// Until proper logging comes along, initialize the logging with the
// temporary initLogger() code. If verbose, we'll use maximum verbosity.
- isc::log::initLogger("b10-resolver",
+ isc::log::initLogger(RESOLVER_NAME,
(verbose ? isc::log::DEBUG : isc::log::INFO),
isc::log::MAX_DEBUG_LEVEL, NULL);
@@ -202,7 +222,7 @@ main(int argc, char* argv[]) {
LOG_DEBUG(resolver_logger, RESOLVER_DBG_INIT, RESOLVER_SERVICE_CREATED);
cc_session = new Session(io_service.get_io_service());
- isc::server_common::initSocketRequestor(*cc_session);
+ isc::server_common::initSocketRequestor(*cc_session, RESOLVER_NAME);
// We delay starting listening to new commands/config just before we
// go into the main loop. See auth/main.cc for the rationale.
diff --git a/src/bin/resolver/resolver.cc b/src/bin/resolver/resolver.cc
index 948b364..711379e 100644
--- a/src/bin/resolver/resolver.cc
+++ b/src/bin/resolver/resolver.cc
@@ -253,7 +253,8 @@ makeErrorMessage(MessagePtr message, MessagePtr answer_message,
}
for_each(questions.begin(), questions.end(), QuestionInserter(message));
message->setRcode(rcode);
- MessageRenderer renderer(*buffer);
+ MessageRenderer renderer;
+ renderer.setBuffer(buffer.get());
message->toWire(renderer);
}
@@ -304,7 +305,8 @@ public:
// Now we can clear the buffer and render the new message into it
buffer->clear();
- MessageRenderer renderer(*buffer);
+ MessageRenderer renderer;
+ renderer.setBuffer(buffer.get());
ConstEDNSPtr edns(query_message->getEDNS());
const bool dnssec_ok = edns && edns->getDNSSECAwareness();
@@ -328,6 +330,7 @@ public:
}
answer_message->toWire(renderer);
+ renderer.setBuffer(NULL);
LOG_DEBUG(resolver_logger, RESOLVER_DBG_DETAIL,
RESOLVER_DNS_MESSAGE_SENT)
diff --git a/src/bin/resolver/resolver.spec.pre.in b/src/bin/resolver/resolver.spec.pre.in
index 076ef85..d6bb226 100644
--- a/src/bin/resolver/resolver.spec.pre.in
+++ b/src/bin/resolver/resolver.spec.pre.in
@@ -154,7 +154,13 @@
{
"command_name": "shutdown",
"command_description": "Shut down recursive DNS server",
- "command_args": []
+ "command_args": [
+ {
+ "item_name": "pid",
+ "item_type": "integer",
+ "item_optional": true
+ }
+ ]
}
]
}
diff --git a/src/bin/resolver/resolver_messages.mes b/src/bin/resolver/resolver_messages.mes
index 7930c52..4999dbe 100644
--- a/src/bin/resolver/resolver_messages.mes
+++ b/src/bin/resolver/resolver_messages.mes
@@ -246,3 +246,7 @@ RESOLVER_QUERY_REJECTED case, the server does not return any response.
The log message shows the query in the form of <query name>/<query
type>/<query class>, and the client that sends the query in the form of
<Source IP address>#<source port>.
+
+% RESOLVER_SHUTDOWN_RECEIVED received command to shut down
+A debug message noting that the server was asked to terminate and is
+complying to the request.
diff --git a/src/bin/resolver/tests/.gitignore b/src/bin/resolver/tests/.gitignore
new file mode 100644
index 0000000..d6d1ec8
--- /dev/null
+++ b/src/bin/resolver/tests/.gitignore
@@ -0,0 +1 @@
+/run_unittests
diff --git a/src/bin/resolver/tests/resolver_config_unittest.cc b/src/bin/resolver/tests/resolver_config_unittest.cc
index 1a30941..ea42ba0 100644
--- a/src/bin/resolver/tests/resolver_config_unittest.cc
+++ b/src/bin/resolver/tests/resolver_config_unittest.cc
@@ -85,7 +85,10 @@ protected:
scoped_ptr<const RequestContext> request;
ResolverConfig() :
dnss(ios, NULL, NULL, NULL),
- sock_requestor_(dnss, address_store_, 53210)
+ // The empty string is expected value of the parameter of
+ // requestSocket, not the app_name (there's no fallback, it checks
+ // the empty string is passed).
+ sock_requestor_(dnss, address_store_, 53210, "")
{
server.setDNSService(dnss);
}
diff --git a/src/bin/sockcreator/.gitignore b/src/bin/sockcreator/.gitignore
new file mode 100644
index 0000000..2985184
--- /dev/null
+++ b/src/bin/sockcreator/.gitignore
@@ -0,0 +1 @@
+/b10-sockcreator
diff --git a/src/bin/sockcreator/Makefile.am b/src/bin/sockcreator/Makefile.am
index 1ac4640..e954c02 100644
--- a/src/bin/sockcreator/Makefile.am
+++ b/src/bin/sockcreator/Makefile.am
@@ -15,4 +15,5 @@ CLEANFILES = *.gcno *.gcda
pkglibexec_PROGRAMS = b10-sockcreator
b10_sockcreator_SOURCES = sockcreator.cc sockcreator.h main.cc
-b10_sockcreator_LDADD = $(top_builddir)/src/lib/util/io/libutil_io.la
+b10_sockcreator_LDADD = $(top_builddir)/src/lib/util/io/libutil_io.la
+b10_sockcreator_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
diff --git a/src/bin/sockcreator/main.cc b/src/bin/sockcreator/main.cc
index 37da303..17efd04 100644
--- a/src/bin/sockcreator/main.cc
+++ b/src/bin/sockcreator/main.cc
@@ -13,6 +13,7 @@
// PERFORMANCE OF THIS SOFTWARE.
#include "sockcreator.h"
+#include <unistd.h>
using namespace isc::socket_creator;
@@ -22,5 +23,10 @@ main() {
* TODO Maybe use some OS-specific caps interface and drop everything
* but ability to bind ports? It would be nice.
*/
- return run(0, 1); // Read commands from stdin, output to stdout
+ try {
+ run(STDIN_FILENO, STDOUT_FILENO, getSock, isc::util::io::send_fd, close);
+ } catch (const SocketCreatorError& ec) {
+ return (ec.getExitCode());
+ }
+ return (0);
}
diff --git a/src/bin/sockcreator/sockcreator.cc b/src/bin/sockcreator/sockcreator.cc
index 827969c..167e3f0 100644
--- a/src/bin/sockcreator/sockcreator.cc
+++ b/src/bin/sockcreator/sockcreator.cc
@@ -15,151 +15,275 @@
#include "sockcreator.h"
#include <util/io/fd.h>
+#include <util/io/sockaddr_util.h>
-#include <unistd.h>
#include <cerrno>
#include <string.h>
+
+#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
using namespace isc::util::io;
+using namespace isc::util::io::internal;
+using namespace isc::socket_creator;
+
+namespace {
+
+// Simple wrappers for read_data/write_data that throw an exception on error.
+void
+readMessage(const int fd, void* where, const size_t length) {
+ if (read_data(fd, where, length) < length) {
+ isc_throw(ReadError, "Error reading from socket creator client");
+ }
+}
+
+void
+writeMessage(const int fd, const void* what, const size_t length) {
+ if (!write_data(fd, what, length)) {
+ isc_throw(WriteError, "Error writing to socket creator client");
+ }
+}
+
+// Exit on a protocol error after informing the client of the problem.
+void
+protocolError(const int fd, const char reason = 'I') {
+
+ // Tell client we have a problem
+ char message[2];
+ message[0] = 'F';
+ message[1] = reason;
+ writeMessage(fd, message, sizeof(message));
+
+ // ... and exit
+ isc_throw(ProtocolError, "Fatal error, reason: " << reason);
+}
+
+// Return appropriate socket type constant for the socket type requested.
+// The output_fd argument is required to report a protocol error.
+int
+getSocketType(const char type_code, const int output_fd) {
+ int socket_type = 0;
+ switch (type_code) {
+ case 'T':
+ socket_type = SOCK_STREAM;
+ break;
+
+ case 'U':
+ socket_type = SOCK_DGRAM;
+ break;
+
+ default:
+ protocolError(output_fd); // Does not return
+ }
+ return (socket_type);
+}
+
+// Convert return status from getSock() to a character to be sent back to
+// the caller.
+char
+getErrorCode(const int status) {
+ char error_code = ' ';
+ switch (status) {
+ case -1:
+ error_code = 'S';
+ break;
+
+ case -2:
+ error_code = 'B';
+ break;
+
+ default:
+ isc_throw(InternalError, "Error creating socket");
+ }
+ return (error_code);
+}
+
+
+// Handle the request from the client.
+//
+// Reads the type and family of socket required, creates the socket and returns
+// it to the client.
+//
+// The arguments passed (and the exceptions thrown) are the same as those for
+// run().
+void
+handleRequest(const int input_fd, const int output_fd,
+ const get_sock_t get_sock, const send_fd_t send_fd_fun,
+ const close_t close_fun)
+{
+ // Read the message from the client
+ char type[2];
+ readMessage(input_fd, type, sizeof(type));
+
+ // Decide what type of socket is being asked for
+ const int sock_type = getSocketType(type[0], output_fd);
+
+ // Read the address they ask for depending on what address family was
+ // specified.
+ sockaddr* addr = NULL;
+ size_t addr_len = 0;
+ sockaddr_in addr_in;
+ sockaddr_in6 addr_in6;
+ switch (type[1]) { // The address family
+
+ // The casting to apparently incompatible types is required by the
+ // C low-level interface.
+
+ case '4':
+ addr = convertSockAddr(&addr_in);
+ addr_len = sizeof(addr_in);
+ memset(&addr_in, 0, sizeof(addr_in));
+ addr_in.sin_family = AF_INET;
+ readMessage(input_fd, &addr_in.sin_port, sizeof(addr_in.sin_port));
+ readMessage(input_fd, &addr_in.sin_addr.s_addr,
+ sizeof(addr_in.sin_addr.s_addr));
+ break;
+
+ case '6':
+ addr = convertSockAddr(&addr_in6);
+ addr_len = sizeof(addr_in6);
+ memset(&addr_in6, 0, sizeof(addr_in6));
+ addr_in6.sin6_family = AF_INET6;
+ readMessage(input_fd, &addr_in6.sin6_port,
+ sizeof(addr_in6.sin6_port));
+ readMessage(input_fd, &addr_in6.sin6_addr.s6_addr,
+ sizeof(addr_in6.sin6_addr.s6_addr));
+ break;
+
+ default:
+ protocolError(output_fd);
+ }
+
+ // Obtain the socket
+ const int result = get_sock(sock_type, addr, addr_len, close_fun);
+ if (result >= 0) {
+ // Got the socket, send it to the client.
+ writeMessage(output_fd, "S", 1);
+ if (send_fd_fun(output_fd, result) != 0) {
+ // Error. Close the socket (ignore any error from that operation)
+ // and abort.
+ close_fun(result);
+ isc_throw(InternalError, "Error sending descriptor");
+ }
+
+ // Successfully sent the socket, so free up resources we still hold
+ // for it.
+ if (close_fun(result) == -1) {
+ isc_throw(InternalError, "Error closing socket");
+ }
+ } else {
+ // Error. Tell the client.
+ char error_message[2];
+ error_message[0] = 'E';
+ error_message[1] = getErrorCode(result);
+ writeMessage(output_fd, error_message, sizeof(error_message));
+
+ // ...and append the reason code to the error message
+ const int error_number = errno;
+ writeMessage(output_fd, &error_number, sizeof(error_number));
+ }
+}
+
+// Sets the MTU related flags for IPv6 UDP sockets.
+// It is borrowed from bind-9 lib/isc/unix/socket.c and modified
+// to compile here.
+//
+// The function returns -2 if it fails or the socket file descriptor
+// on success (for convenience, so the result can be just returned).
+int
+mtu(int fd) {
+#ifdef IPV6_USE_MIN_MTU /* RFC 3542, not too common yet*/
+ const int on(1);
+ // use minimum MTU
+ if (setsockopt(fd, IPPROTO_IPV6, IPV6_USE_MIN_MTU, &on, sizeof(on)) < 0) {
+ return (-2);
+ }
+#else // Try the following as fallback
+#ifdef IPV6_MTU
+ // Use minimum MTU on systems that don't have the IPV6_USE_MIN_MTU
+ const int mtu = 1280;
+ if (setsockopt(fd, IPPROTO_IPV6, IPV6_MTU, &mtu, sizeof(mtu)) < 0) {
+ return (-2);
+ }
+#endif
+#if defined(IPV6_MTU_DISCOVER) && defined(IPV6_PMTUDISC_DONT)
+ // Turn off Path MTU discovery on IPv6/UDP sockets.
+ const int action = IPV6_PMTUDISC_DONT;
+ if (setsockopt(fd, IPPROTO_IPV6, IPV6_MTU_DISCOVER, &action,
+ sizeof(action)) < 0) {
+
+ return (-2);
+ }
+#endif
+#endif
+ return (fd);
+}
+
+// This one closes the socket if result is negative. Used not to leak socket
+// on error.
+int maybeClose(const int result, const int socket, const close_t close_fun) {
+ if (result < 0) {
+ if (close_fun(socket) == -1) {
+ isc_throw(InternalError, "Error closing socket");
+ }
+ }
+ return (result);
+}
+
+} // Anonymous namespace
namespace isc {
namespace socket_creator {
+// Get the socket and bind to it.
int
-get_sock(const int type, struct sockaddr *bind_addr, const socklen_t addr_len)
-{
- int sock(socket(bind_addr->sa_family, type, 0));
+getSock(const int type, struct sockaddr* bind_addr, const socklen_t addr_len,
+ const close_t close_fun) {
+ const int sock = socket(bind_addr->sa_family, type, 0);
if (sock == -1) {
- return -1;
+ return (-1);
}
- const int on(1);
+ const int on = 1;
if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1) {
- return -2; // This is part of the binding process, so it's a bind error
+ // This is part of the binding process, so it's a bind error
+ return (maybeClose(-2, sock, close_fun));
}
if (bind_addr->sa_family == AF_INET6 &&
setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on)) == -1) {
- return -2; // This is part of the binding process, so it's a bind error
+ // This is part of the binding process, so it's a bind error
+ return (maybeClose(-2, sock, close_fun));
}
if (bind(sock, bind_addr, addr_len) == -1) {
- return -2;
+ return (maybeClose(-2, sock, close_fun));
+ }
+ if (type == SOCK_DGRAM && bind_addr->sa_family == AF_INET6) {
+ // Set some MTU flags on IPv6 UDP sockets.
+ return (maybeClose(mtu(sock), sock, close_fun));
}
- return sock;
+ return (sock);
}
-// These are macros so they can exit the function
-#define READ(WHERE, HOW_MANY) do { \
- size_t how_many = (HOW_MANY); \
- if (read_data(input_fd, (WHERE), how_many) < how_many) { \
- return 1; \
- } \
- } while (0)
-
-#define WRITE(WHAT, HOW_MANY) do { \
- if (!write_data(output_fd, (WHAT), (HOW_MANY))) { \
- return 2; \
- } \
- } while (0)
-
-#define DEFAULT \
- default: /* Unrecognized part of protocol */ \
- WRITE("FI", 2); \
- return 3;
-
-int
-run(const int input_fd, const int output_fd, const get_sock_t get_sock,
- const send_fd_t send_fd_fun, const close_t close_fun)
+// Main run loop.
+void
+run(const int input_fd, const int output_fd, get_sock_t get_sock,
+ send_fd_t send_fd_fun, close_t close_fun)
{
for (;;) {
- // Read the command
char command;
- READ(&command, 1);
+ readMessage(input_fd, &command, sizeof(command));
switch (command) {
- case 'T': // The "terminate" command
- return 0;
- case 'S': { // Create a socket
- // Read what type of socket they want
- char type[2];
- READ(type, 2);
- // Read the address they ask for
- struct sockaddr *addr(NULL);
- size_t addr_len(0);
- struct sockaddr_in addr_in;
- struct sockaddr_in6 addr_in6;
- switch (type[1]) { // The address family
- /*
- * Here are some casts. They are required by C++ and
- * the low-level interface (they are implicit in C).
- */
- case '4':
- addr = static_cast<struct sockaddr *>(
- static_cast<void *>(&addr_in));
- addr_len = sizeof addr_in;
- memset(&addr_in, 0, sizeof addr_in);
- addr_in.sin_family = AF_INET;
- READ(static_cast<char *>(static_cast<void *>(
- &addr_in.sin_port)), 2);
- READ(static_cast<char *>(static_cast<void *>(
- &addr_in.sin_addr.s_addr)), 4);
- break;
- case '6':
- addr = static_cast<struct sockaddr *>(
- static_cast<void *>(&addr_in6));
- addr_len = sizeof addr_in6;
- memset(&addr_in6, 0, sizeof addr_in6);
- addr_in6.sin6_family = AF_INET6;
- READ(static_cast<char *>(static_cast<void *>(
- &addr_in6.sin6_port)), 2);
- READ(static_cast<char *>(static_cast<void *>(
- &addr_in6.sin6_addr.s6_addr)), 16);
- break;
- DEFAULT
- }
- int sock_type;
- switch (type[0]) { // Translate the type
- case 'T':
- sock_type = SOCK_STREAM;
- break;
- case 'U':
- sock_type = SOCK_DGRAM;
- break;
- DEFAULT
- }
- int result(get_sock(sock_type, addr, addr_len));
- if (result >= 0) { // We got the socket
- WRITE("S", 1);
- if (send_fd_fun(output_fd, result) != 0) {
- // We'll soon abort ourselves, but make sure we still
- // close the socket; don't bother if it fails as the
- // higher level result (abort) is the same.
- close_fun(result);
- return 3;
- }
- // Don't leak the socket
- if (close_fun(result) == -1) {
- return 4;
- }
- } else {
- WRITE("E", 1);
- switch (result) {
- case -1:
- WRITE("S", 1);
- break;
- case -2:
- WRITE("B", 1);
- break;
- default:
- return 4;
- }
- int error(errno);
- WRITE(static_cast<char *>(static_cast<void *>(&error)),
- sizeof error);
- }
+ case 'S': // The "get socket" command
+ handleRequest(input_fd, output_fd, get_sock,
+ send_fd_fun, close_fun);
break;
- }
- DEFAULT
+
+ case 'T': // The "terminate" command
+ return;
+
+ default: // Don't recognise anything else
+ protocolError(output_fd);
}
}
}
diff --git a/src/bin/sockcreator/sockcreator.h b/src/bin/sockcreator/sockcreator.h
index 216e486..e5d4783 100644
--- a/src/bin/sockcreator/sockcreator.h
+++ b/src/bin/sockcreator/sockcreator.h
@@ -1,4 +1,4 @@
-// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+// 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
@@ -12,18 +12,17 @@
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
-/**
- * \file sockcreator.h
- * \short Socket creator functionality.
- *
- * This module holds the functionality of the socket creator. It is
- * a separate module from main to ease up the tests.
- */
+/// \file sockcreator.h
+/// \short Socket creator functionality.
+///
+/// This module holds the functionality of the socket creator. It is a separate
+/// module from main to make testing easier.
#ifndef __SOCKCREATOR_H
#define __SOCKCREATOR_H 1
#include <util/io/fd_share.h>
+#include <exceptions/exceptions.h>
#include <sys/types.h>
#include <sys/socket.h>
@@ -32,78 +31,117 @@
namespace isc {
namespace socket_creator {
-/**
- * \short Create a socket and bind it.
- *
- * This is just a bundle of socket() and bind() calls. The sa_family of
- * bind_addr is used to determine the domain of the socket.
- *
- * \return The file descriptor of the newly created socket, if everything
- * goes well. A negative number is returned if an error occurs -
- * -1 if the socket() call fails or -2 if bind() fails. In case of error,
- * errno is set (or better, left intact from socket() or bind()).
- * \param type The type of socket to create (SOCK_STREAM, SOCK_DGRAM, etc).
- * \param bind_addr The address to bind.
- * \param addr_len The actual length of bind_addr.
- */
-int
-get_sock(const int type, struct sockaddr *bind_addr, const socklen_t addr_len);
+// Exception classes - the base class exception SocketCreatorError is caught
+// by main() and holds an exit code returned to the environment. The code
+// depends on the exact exception raised.
+class SocketCreatorError : public isc::Exception {
+public:
+ SocketCreatorError(const char* file, size_t line, const char* what,
+ int exit_code) :
+ isc::Exception(file, line, what), exit_code_(exit_code) {}
-/**
- * Type of the get_sock function, to pass it as parameter.
- */
-typedef
-int
-(*get_sock_t)(const int, struct sockaddr *, const socklen_t);
+ int getExitCode() const {
+ return (exit_code_);
+ }
-/**
- * Type of the send_fd() function, so it can be passed as a parameter.
- */
-typedef
-int
-(*send_fd_t)(const int, const int);
+private:
+ const int exit_code_; // Code returned to exit()
+};
-/// \brief Type of the close() function, so it can be passed as a parameter.
-typedef
-int
-(*close_t)(int);
-
-/**
- * \short Infinite loop parsing commands and returning the sockets.
- *
- * This reads commands and socket descriptions from the input_fd
- * file descriptor, creates sockets and writes the results (socket or
- * error) to output_fd.
- *
- * Current errors are:
- * - 1: Read error
- * - 2: Write error
- * - 3: Protocol error (unknown command, etc)
- * - 4: Some internal inconsistency detected
- *
- * It terminates either if a command asks it to or when unrecoverable
- * error happens.
- *
- * \return Like a return value of a main - 0 means everything OK, anything
- * else is error.
- * \param input_fd Here is where it reads the commads.
- * \param output_fd Here is where it writes the results.
- * \param get_sock_fun The function that is used to create the sockets.
- * This should be left on the default value, the parameter is here
- * for testing purposes.
- * \param send_fd_fun The function that is used to send the socket over
- * a file descriptor. This should be left on the default value, it is
- * here for testing purposes.
- * \param close_fun The close function used to close sockets, coming from
- * unistd.h. It can be overriden in tests.
- */
+class ReadError : public SocketCreatorError {
+public:
+ ReadError(const char* file, size_t line, const char* what) :
+ SocketCreatorError(file, line, what, 1) {}
+};
+
+class WriteError : public SocketCreatorError {
+public:
+ WriteError(const char* file, size_t line, const char* what) :
+ SocketCreatorError(file, line, what, 2) {}
+};
+
+class ProtocolError : public SocketCreatorError {
+public:
+ ProtocolError(const char* file, size_t line, const char* what) :
+ SocketCreatorError(file, line, what, 3) {}
+};
+
+class InternalError : public SocketCreatorError {
+public:
+ InternalError(const char* file, size_t line, const char* what) :
+ SocketCreatorError(file, line, what, 4) {}
+};
+
+
+// Type of the close() function, so it can be passed as a parameter.
+// Argument is the same as that for close(2).
+typedef int (*close_t)(int);
+
+/// \short Create a socket and bind it.
+///
+/// This is just a bundle of socket() and bind() calls. The sa_family of
+/// bind_addr is used to determine the domain of the socket.
+///
+/// \param type The type of socket to create (SOCK_STREAM, SOCK_DGRAM, etc).
+/// \param bind_addr The address to bind.
+/// \param addr_len The actual length of bind_addr.
+/// \param close_fun The furction used to close a socket if there's an error
+/// after the creation.
+///
+/// \return The file descriptor of the newly created socket, if everything
+/// goes well. A negative number is returned if an error occurs -
+/// -1 if the socket() call fails or -2 if bind() fails. In case of
+/// error, errno is set (or left intact from socket() or bind()).
int
-run(const int input_fd, const int output_fd,
- const get_sock_t get_sock_fun = get_sock,
- const send_fd_t send_fd_fun = isc::util::io::send_fd,
- const close_t close_fun = close);
+getSock(const int type, struct sockaddr* bind_addr, const socklen_t addr_len,
+ const close_t close_fun);
+
+// Define some types for functions used to perform socket-related operations.
+// These are typedefed so that alternatives can be passed through to the
+// main functions for testing purposes.
+
+// Type of the function to get a socket and to pass it as parameter.
+// Arguments are those described above for getSock().
+typedef int (*get_sock_t)(const int, struct sockaddr *, const socklen_t,
+ const close_t close_fun);
+
+// Type of the send_fd() function, so it can be passed as a parameter.
+// Arguments are the same as those of the send_fd() function.
+typedef int (*send_fd_t)(const int, const int);
+
+
+/// \brief Infinite loop parsing commands and returning the sockets.
+///
+/// This reads commands and socket descriptions from the input_fd file
+/// descriptor, creates sockets and writes the results (socket or error) to
+/// output_fd.
+///
+/// It terminates either if a command asks it to or when unrecoverable error
+/// happens.
+///
+/// \param input_fd File number of the stream from which the input commands
+/// are read.
+/// \param output_fd File number of the stream to which the response is
+/// written. The socket is sent as part of a control message associated
+/// with that stream.
+/// \param get_sock_fun The function that is used to create the sockets.
+/// This should be left on the default value, the parameter is here
+/// for testing purposes.
+/// \param send_fd_fun The function that is used to send the socket over
+/// a file descriptor. This should be left on the default value, it is
+/// here for testing purposes.
+/// \param close_fun The close function used to close sockets, coming from
+/// unistd.h. It can be overriden in tests.
+///
+/// \exception isc::socket_creator::ReadError Error reading from input
+/// \exception isc::socket_creator::WriteError Error writing to output
+/// \exception isc::socket_creator::ProtocolError Unrecognised command received
+/// \exception isc::socket_creator::InternalError Other error
+void
+run(const int input_fd, const int output_fd, get_sock_t get_sock_fun,
+ send_fd_t send_fd_fun, close_t close_fun);
-} // End of the namespaces
-}
+} // namespace socket_creator
+} // NAMESPACE ISC
#endif // __SOCKCREATOR_H
diff --git a/src/bin/sockcreator/tests/.gitignore b/src/bin/sockcreator/tests/.gitignore
new file mode 100644
index 0000000..d6d1ec8
--- /dev/null
+++ b/src/bin/sockcreator/tests/.gitignore
@@ -0,0 +1 @@
+/run_unittests
diff --git a/src/bin/sockcreator/tests/Makefile.am b/src/bin/sockcreator/tests/Makefile.am
index 223e761..ef518b5 100644
--- a/src/bin/sockcreator/tests/Makefile.am
+++ b/src/bin/sockcreator/tests/Makefile.am
@@ -1,7 +1,8 @@
CLEANFILES = *.gcno *.gcda
-AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
-AM_CXXFLAGS = $(B10_CXXFLAGS)
+AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
+AM_CPPFLAGS += $(BOOST_INCLUDES)
+AM_CXXFLAGS = $(B10_CXXFLAGS)
if USE_STATIC_LINK
AM_LDFLAGS = -static
diff --git a/src/bin/sockcreator/tests/sockcreator_tests.cc b/src/bin/sockcreator/tests/sockcreator_tests.cc
index 360c750..9604567 100644
--- a/src/bin/sockcreator/tests/sockcreator_tests.cc
+++ b/src/bin/sockcreator/tests/sockcreator_tests.cc
@@ -1,4 +1,4 @@
-// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+// 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
@@ -17,11 +17,15 @@
#include <util/unittests/fork.h>
#include <util/io/fd.h>
+#include <boost/lexical_cast.hpp>
#include <gtest/gtest.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
+#include <arpa/inet.h>
#include <unistd.h>
+
+#include <iostream>
#include <cstring>
#include <cerrno>
@@ -29,282 +33,400 @@ using namespace isc::socket_creator;
using namespace isc::util::unittests;
using namespace isc::util::io;
+// The tests check both TCP and UDP sockets on IPv4 and IPv6.
+//
+// Essentially we need to check all four combinations of TCP/UDP and IPv4/IPv6.
+// The different address families (IPv4/IPv6) require different structures to
+// hold the address information, and so some common code is in the form of
+// templates (or overloads), parameterised on the structure type.
+//
+// The protocol is determined by an integer (SOCK_STREAM or SOCK_DGRAM) so
+// cannot be templated in the same way. Relevant check functions are
+// selected manually.
+
namespace {
-/*
- * Generic version of the creation of socket test. It just tries to
- * create the socket and checks the result is not negative (eg.
- * it is valid descriptor) and that it can listen.
- *
- * This is a macro so ASSERT_* does abort the TEST, not just the
- * function inside.
- */
-#define TEST_ANY_CREATE(SOCK_TYPE, ADDR_TYPE, ADDR_FAMILY, FAMILY_FIELD, \
- ADDR_SET, CHECK_SOCK) \
- do { \
- /*
- * This should create an address that binds on all interfaces
- * and lets the OS choose a free port.
- */ \
- struct ADDR_TYPE addr; \
- memset(&addr, 0, sizeof addr); \
- ADDR_SET(addr); \
- addr.FAMILY_FIELD = ADDR_FAMILY; \
- struct sockaddr *addr_ptr = static_cast<struct sockaddr *>( \
- static_cast<void *>(&addr)); \
- \
- int socket = get_sock(SOCK_TYPE, addr_ptr, sizeof addr); \
- /* Provide even nice error message. */ \
- ASSERT_GE(socket, 0) << "Couldn't create a socket of type " \
- #SOCK_TYPE " and family " #ADDR_FAMILY ", failed with " \
- << socket << " and error " << strerror(errno); \
- CHECK_SOCK(ADDR_TYPE, socket); \
- int on; \
- socklen_t len(sizeof(on)); \
- EXPECT_EQ(0, getsockopt(socket, SOL_SOCKET, SO_REUSEADDR, &on, &len));\
- EXPECT_NE(0, on); \
- if (ADDR_FAMILY == AF_INET6) { \
- EXPECT_EQ(0, getsockopt(socket, IPPROTO_IPV6, IPV6_V6ONLY, &on, \
- &len)); \
- EXPECT_NE(0, on); \
- } \
- EXPECT_EQ(0, close(socket)); \
- } while (0)
-
-// Just helper macros
-#define INADDR_SET(WHAT) do { WHAT.sin_addr.s_addr = INADDR_ANY; } while (0)
-#define IN6ADDR_SET(WHAT) do { WHAT.sin6_addr = in6addr_loopback; } while (0)
-// If the get_sock returned something useful, listen must work
-#define TCP_CHECK(UNUSED, SOCKET) do { \
- EXPECT_EQ(0, listen(SOCKET, 1)); \
- } while (0)
-// More complicated with UDP, so we send a packet to ourselfs and se if it
-// arrives
-#define UDP_CHECK(ADDR_TYPE, SOCKET) do { \
- struct ADDR_TYPE addr; \
- memset(&addr, 0, sizeof addr); \
- struct sockaddr *addr_ptr = static_cast<struct sockaddr *>( \
- static_cast<void *>(&addr)); \
- \
- socklen_t len = sizeof addr; \
- ASSERT_EQ(0, getsockname(SOCKET, addr_ptr, &len)); \
- ASSERT_EQ(5, sendto(SOCKET, "test", 5, 0, addr_ptr, sizeof addr)) << \
- "Send failed with error " << strerror(errno) << " on socket " << \
- SOCKET; \
- char buffer[5]; \
- ASSERT_EQ(5, recv(SOCKET, buffer, 5, 0)) << \
- "Recv failed with error " << strerror(errno) << " on socket " << \
- SOCKET; \
- EXPECT_STREQ("test", buffer); \
- } while (0)
-
-/*
- * Several tests to ensure we can create the sockets.
- */
+// Set IP-version-specific fields.
+
+void
+setAddressFamilyFields(sockaddr_in* address) {
+ address->sin_family = AF_INET;
+ address->sin_addr.s_addr = INADDR_ANY;
+}
+
+void
+setAddressFamilyFields(sockaddr_in6* address) {
+ address->sin6_family = AF_INET6;
+ address->sin6_addr = in6addr_loopback;
+}
+
+// Socket has been opened, peform a check on it. The sole argument is the
+// socket descriptor. The TCP check is the same regardless of the address
+// family. The UDP check requires that the socket address be obtained so
+// is parameterised on the type of structure required to hold the address.
+
+void
+tcpCheck(const int socknum) {
+ // Sufficient to be able to listen on the socket.
+ EXPECT_EQ(0, listen(socknum, 1));
+}
+
+template <typename ADDRTYPE>
+void
+udpCheck(const int socknum) {
+ // UDP testing is more complicated than TCP: send a packet to ourselves and
+ // see if it arrives.
+
+ // Get details of the socket so that we can use it as the target of the
+ // sendto().
+ ADDRTYPE addr;
+ memset(&addr, 0, sizeof(addr));
+ sockaddr* addr_ptr = reinterpret_cast<sockaddr*>(&addr);
+ socklen_t len = sizeof(addr);
+ ASSERT_EQ(0, getsockname(socknum, addr_ptr, &len));
+
+ // Send the packet to ourselves and check we receive it.
+ ASSERT_EQ(5, sendto(socknum, "test", 5, 0, addr_ptr, sizeof(addr))) <<
+ "Send failed with error " << strerror(errno) << " on socket " <<
+ socknum;
+ char buffer[5];
+ ASSERT_EQ(5, recv(socknum, buffer, 5, 0)) <<
+ "Recv failed with error " << strerror(errno) << " on socket " <<
+ socknum;
+ EXPECT_STREQ("test", buffer);
+}
+
+// The check function (tcpCheck/udpCheck) is passed as a parameter to the test
+// code, so provide a conveniet typedef.
+typedef void (*socket_check_t)(const int);
+
+// Address-family-specific scoket checks.
+//
+// The first argument is used to select the overloaded check function.
+// The other argument is the socket descriptor number.
+
+// IPv4 check
+void addressFamilySpecificCheck(const sockaddr_in*, const int, const int) {
+}
+
+// IPv6 check
+void addressFamilySpecificCheck(const sockaddr_in6*, const int socknum,
+ const int socket_type)
+{
+ int options;
+ socklen_t len = sizeof(options);
+ EXPECT_EQ(0, getsockopt(socknum, IPPROTO_IPV6, IPV6_V6ONLY, &options,
+ &len));
+ EXPECT_NE(0, options);
+ if (socket_type == SOCK_DGRAM) {
+ // Some more checks for UDP - MTU
+#ifdef IPV6_USE_MIN_MTU /* RFC 3542, not too common yet*/
+ // use minimum MTU
+ EXPECT_EQ(0, getsockopt(socknum, IPPROTO_IPV6, IPV6_USE_MIN_MTU,
+ &options, &len)) << strerror(errno);
+ EXPECT_NE(0, options);
+#else
+ // We do not check for the IPV6_MTU, because while setting works (eg.
+ // the packets are fragmented correctly), the getting does not. If
+ // we try to getsockopt it, an error complaining it can't be accessed
+ // on unconnected socket is returned. If we try to connect it, the
+ // MTU of the interface is returned, not the one we set. So we live
+ // in belief it works because we examined the packet dump.
+#if defined(IPV6_MTU_DISCOVER) && defined(IPV6_PMTUDISC_DONT)
+ // Turned off Path MTU discovery on IPv6/UDP sockets?
+ EXPECT_EQ(0, getsockopt(socknum, IPPROTO_IPV6, IPV6_MTU_DISCOVER,
+ &options, &len)) << strerror(errno);
+ EXPECT_EQ(IPV6_PMTUDISC_DONT, options);
+#endif
+#endif
+ }
+}
+
+// Just ignore the fd and pretend success. We close invalid fds in the tests.
+int
+closeIgnore(int) {
+ return (0);
+}
+
+// Generic version of the socket test. It creates the socket and checks that
+// it is a valid descriptor. The family-specific check functions are called
+// to check that the socket is valid. The function is parameterised according
+// to the structure used to hold the address.
+//
+// Arguments:
+// socket_type Type of socket to create (SOCK_DGRAM or SOCK_STREAM)
+// socket_check Associated check function - udpCheck() or tcpCheck()
+template <typename ADDRTYPE>
+void testAnyCreate(int socket_type, socket_check_t socket_check) {
+
+ // Create the socket.
+ ADDRTYPE addr;
+ memset(&addr, 0, sizeof(addr));
+ setAddressFamilyFields(&addr);
+ sockaddr* addr_ptr = reinterpret_cast<sockaddr*>(&addr);
+ const int socket = getSock(socket_type, addr_ptr, sizeof(addr),
+ closeIgnore);
+ ASSERT_GE(socket, 0) << "Couldn't create socket: failed with " <<
+ "return code " << socket << " and error " << strerror(errno);
+
+ // Perform socket-type-specific testing.
+ socket_check(socket);
+
+ // Do address-family-independent
+ int options;
+ socklen_t len = sizeof(options);
+ EXPECT_EQ(0, getsockopt(socket, SOL_SOCKET, SO_REUSEADDR, &options, &len));
+ EXPECT_NE(0, options);
+
+ // ...and the address-family specific tests.
+ addressFamilySpecificCheck(&addr, socket, socket_type);
+
+ // Tidy up and exit.
+ EXPECT_EQ(0, close(socket));
+}
+
+
+// Several tests to ensure we can create the sockets.
TEST(get_sock, udp4_create) {
- TEST_ANY_CREATE(SOCK_DGRAM, sockaddr_in, AF_INET, sin_family, INADDR_SET,
- UDP_CHECK);
+ testAnyCreate<sockaddr_in>(SOCK_DGRAM, udpCheck<sockaddr_in>);
}
TEST(get_sock, tcp4_create) {
- TEST_ANY_CREATE(SOCK_STREAM, sockaddr_in, AF_INET, sin_family, INADDR_SET,
- TCP_CHECK);
+ testAnyCreate<sockaddr_in>(SOCK_STREAM, tcpCheck);
}
TEST(get_sock, udp6_create) {
- TEST_ANY_CREATE(SOCK_DGRAM, sockaddr_in6, AF_INET6, sin6_family,
- IN6ADDR_SET, UDP_CHECK);
+ testAnyCreate<sockaddr_in6>(SOCK_DGRAM, udpCheck<sockaddr_in6>);
}
TEST(get_sock, tcp6_create) {
- TEST_ANY_CREATE(SOCK_STREAM, sockaddr_in6, AF_INET6, sin6_family,
- IN6ADDR_SET, TCP_CHECK);
+ testAnyCreate<sockaddr_in6>(SOCK_STREAM, tcpCheck);
}
-/*
- * Try to ask the get_sock function some nonsense and test if it
- * is able to report error.
- */
+bool close_called(false);
+
+// You can use it as a close mockup. If you care about checking if it was really
+// called, you can use the close_called variable. But set it to false before the
+// test.
+int closeCall(int socket) {
+ close(socket);
+ close_called = true;
+ return (0);
+}
+
+// Ask the get_sock function for some nonsense and test if it is able to report
+// an error.
TEST(get_sock, fail_with_nonsense) {
- struct sockaddr addr;
- memset(&addr, 0, sizeof addr);
- ASSERT_LT(get_sock(0, &addr, sizeof addr), 0);
+ sockaddr addr;
+ memset(&addr, 0, sizeof(addr));
+ close_called = false;
+ ASSERT_EQ(-1, getSock(0, &addr, sizeof addr, closeCall));
+ ASSERT_FALSE(close_called); // The "socket" call should have failed already
+}
+
+// Bind should have failed here
+TEST(get_sock, fail_with_bind) {
+ sockaddr_in addr;
+ memset(&addr, 0, sizeof(addr));
+ addr.sin_family = AF_INET;
+ addr.sin_port = 1;
+ // No host should have this address on the interface, so it should not be
+ // possible to bind it.
+ addr.sin_addr.s_addr = inet_addr("192.0.2.1");
+ close_called = false;
+ ASSERT_EQ(-2, getSock(SOCK_STREAM, reinterpret_cast<sockaddr*>(&addr),
+ sizeof addr, closeCall));
+ ASSERT_TRUE(close_called); // The "socket" call should have failed already
}
-/*
- * Helper functions to pass to run during testing.
- */
+// The main run() function in the socket creator takes three functions to
+// get the socket, send information to it, and close it. These allow for
+// alternatives to the system functions to be used for testing.
+
+// Replacement getSock() function.
+// The return value indicates the result of checks and is encoded. Using LSB
+// bit numbering (least-significant bit is bit 0) then:
+//
+// bit 0: 1 if "type" is known, 0 otherwise
+// bit 1: 1 for UDP, 0 for TCP
+// bit 2: 1 if address family is known, 0 otherwise
+// bit 3: 1 for IPv6, 0 for IPv4
+// bit 4: 1 if port passed was valid
+//
+// Other possible return values are:
+//
+// -1: The simulated bind() call has failed
+// -2: The simulated socket() call has failed
int
-get_sock_dummy(const int type, struct sockaddr *addr, const socklen_t)
-{
- int result(0);
- int port(0);
- /*
- * We encode the type and address family into the int and return it.
- * Lets ignore the port and address for now
- * First bit is 1 if it is known type. Second tells if TCP or UDP.
- * The familly is similar - third bit is known address family,
- * the fourth is the family.
- */
+getSockDummy(const int type, struct sockaddr* addr, const socklen_t,
+ const close_t) {
+ int result = 0;
+ int port = 0;
+
+ // Validate type field
switch (type) {
case SOCK_STREAM:
- result += 1;
+ result |= 0x01;
break;
+
case SOCK_DGRAM:
- result += 3;
+ result |= 0x03;
break;
}
+
+ // Validate address family
switch (addr->sa_family) {
case AF_INET:
- result += 4;
- port = static_cast<struct sockaddr_in *>(
- static_cast<void *>(addr))->sin_port;
+ result |= 0x04;
+ port = reinterpret_cast<sockaddr_in*>(addr)->sin_port;
break;
+
case AF_INET6:
- result += 12;
- port = static_cast<struct sockaddr_in6 *>(
- static_cast<void *>(addr))->sin6_port;
+ result |= 0x0C;
+ port = reinterpret_cast<sockaddr_in6*>(addr)->sin6_port;
break;
}
- /*
- * The port should be 0xffff. If it's not, we change the result.
- * The port of 0xbbbb means bind should fail and 0xcccc means
- * socket should fail.
- */
+
+ // The port should be 0xffff. If it's not, we change the result.
+ // The port of 0xbbbb means bind should fail and 0xcccc means
+ // socket should fail.
if (port != 0xffff) {
errno = 0;
if (port == 0xbbbb) {
- return -2;
+ return (-2);
} else if (port == 0xcccc) {
- return -1;
+ return (-1);
} else {
- result += 16;
+ result |= 0x10;
}
}
- return result;
-}
-
-int
-send_fd_dummy(const int destination, const int what)
-{
- /*
- * Make sure it is 1 byte so we know the length. We do not use more during
- * the test anyway.
- */
- char fd_data(what);
- if (!write_data(destination, &fd_data, 1)) {
- return -1;
- } else {
- return 0;
- }
+ return (result);
}
-// Just ignore the fd and pretend success. We close invalid fds in the tests.
+// Dummy send function - return data (the result of getSock()) to the destination.
int
-closeIgnore(int) {
- return (0);
+send_FdDummy(const int destination, const int what) {
+ // Make sure it is 1 byte so we know the length. We do not use more during
+ // the test anyway. And even with the LS bute, we can distinguish between
+ // the different results.
+ const char fd_data = what & 0xff;
+ const bool status = write_data(destination, &fd_data, sizeof(fd_data));
+ return (status ? 0 : -1);
}
-/*
- * Generic test that it works, with various inputs and outputs.
- * It uses different functions to create the socket and send it and pass
- * data to it and check it returns correct data back, to see if the run()
- * parses the commands correctly.
- */
-void run_test(const char *input_data, const size_t input_size,
- const char *output_data, const size_t output_size,
- bool should_succeed = true, const close_t test_close = closeIgnore,
- const send_fd_t send_fd = send_fd_dummy)
+// Generic test that it works, with various inputs and outputs.
+// It uses different functions to create the socket and send it and pass
+// data to it and check it returns correct data back, to see if the run()
+// parses the commands correctly.
+void runTest(const char* input_data, const size_t input_size,
+ const char* output_data, const size_t output_size,
+ bool should_succeed = true,
+ const close_t test_close = closeIgnore,
+ const send_fd_t send_fd = send_FdDummy)
{
- // Prepare the input feeder and output checker processes
- int input_fd(0), output_fd(0);
- pid_t input(provide_input(&input_fd, input_data, input_size)),
- output(check_output(&output_fd, output_data, output_size));
+ // Prepare the input feeder and output checker processes. The feeder
+ // process sends data from the client to run() and the checker process
+ // reads the response and checks it against the expected response.
+ int input_fd = 0;
+ const pid_t input = provide_input(&input_fd, input_data, input_size);
ASSERT_NE(-1, input) << "Couldn't start input feeder";
+
+ int output_fd = 0;
+ const pid_t output = check_output(&output_fd, output_data, output_size);
ASSERT_NE(-1, output) << "Couldn't start output checker";
+
// Run the body
- int result(run(input_fd, output_fd, get_sock_dummy, send_fd, test_close));
- // Close the pipes
- close(input_fd);
- close(output_fd);
- // Did it run well?
if (should_succeed) {
- EXPECT_EQ(0, result);
+ EXPECT_NO_THROW(run(input_fd, output_fd, getSockDummy, send_fd,
+ test_close));
} else {
- EXPECT_NE(0, result);
+ EXPECT_THROW(run(input_fd, output_fd, getSockDummy, send_fd,
+ test_close), isc::socket_creator::SocketCreatorError);
}
+
+ // Close the pipes
+ close(input_fd);
+ close(output_fd);
+
// Check the subprocesses say everything is OK too
EXPECT_TRUE(process_ok(input));
EXPECT_TRUE(process_ok(output));
}
-/*
- * Check it terminates successfully when asked to.
- */
+
+// Check it terminates successfully when asked to.
TEST(run, terminate) {
- run_test("T", 1, NULL, 0);
+ runTest("T", 1, NULL, 0);
}
-/*
- * Check it rejects incorrect input.
- */
+// Check it rejects incorrect input.
TEST(run, bad_input) {
- run_test("XXX", 3, "FI", 2, false);
+ runTest("XXX", 3, "FI", 2, false);
}
-/*
- * Check it correctly parses queries to create sockets.
- */
+// Check it correctly parses query stream to create sockets.
TEST(run, sockets) {
- run_test(
- "SU4\xff\xff\0\0\0\0" // This has 9 bytes
- "ST4\xff\xff\0\0\0\0" // This has 9 bytes
- "ST6\xff\xff\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" // This has 21 bytes
- "SU6\xff\xff\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" // This has 21 bytes
- "T", 61,
- "S\x07S\x05S\x0dS\x0f", 8);
+ runTest(
+ // Commands:
+ "SU4\xff\xff\0\0\0\0" // IPv4 UDP socket, port 0xffffff, address 0.0.0.0
+ "ST4\xff\xff\0\0\0\0" // IPv4 TCP socket, port 0xffffff, address 0.0.0.0
+ "ST6\xff\xff\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ // IPv6 UDP socket, port 0xffffff, address ::
+ "SU6\xff\xff\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ // IPv6 TCP socket, port 0xffffff, address ::
+ "T", // ... and terminate
+ 9 + 9 + 21 + 21 + 1, // Length of command string
+ "S\x07S\x05S\x0dS\x0f", // Response ("S" + LS byte of getSock() return)
+ 8); // Length of response
}
-/*
- * Check if failures of get_socket are handled correctly.
- */
+
+// Check if failures of get_socket are handled correctly.
TEST(run, bad_sockets) {
- // We need to construct the answer, but it depends on int length.
- size_t int_len(sizeof(int));
- size_t result_len(4 + 2 * int_len);
- char result[4 + sizeof(int) * 2];
- // Both errno parts should be 0
- memset(result, 0, result_len);
- // Fill the 2 control parts
+ // We need to construct the answer, but it depends on int length. We expect
+ // two failure answers in this test, each answer comprising two characters
+ // followed by the (int) errno value.
+ char result[2 * (2 + sizeof(int))];
+
+ // We expect the errno parts to be zero but the characters to depend on the
+ // exact failure.
+ memset(result, 0, sizeof(result));
strcpy(result, "EB");
- strcpy(result + 2 + int_len, "ES");
+ strcpy(result + 2 + sizeof(int), "ES");
+
// Run the test
- run_test(
- "SU4\xbb\xbb\0\0\0\0"
- "SU4\xcc\xcc\0\0\0\0"
- "T", 19,
- result, result_len);
+ runTest(
+ "SU4\xbb\xbb\0\0\0\0" // Port number will trigger simulated bind() fail
+ "SU4\xcc\xcc\0\0\0\0" // Port number will trigger simulated socket() fail
+ "T", // Terminate
+ 19, // Length of command string
+ result, sizeof(result));
}
-// A close that fails
+// A close that fails. (This causes an abort.)
int
closeFail(int) {
return (-1);
}
TEST(run, cant_close) {
- run_test("SU4\xff\xff\0\0\0\0", // This has 9 bytes
- 9, "S\x07", 2, false, closeFail);
+ runTest("SU4\xff\xff\0\0\0\0", 9,
+ "S\x07", 2,
+ false, closeFail);
}
+// A send of the file descriptor that fails. In this case we expect the client
+// to receive the "S" indicating that the descriptor is being sent and nothing
+// else. This causes an abort.
int
sendFDFail(const int, const int) {
return (FD_SYSTEM_ERROR);
}
TEST(run, cant_send_fd) {
- run_test("SU4\xff\xff\0\0\0\0", // This has 9 bytes
- 9, "S", 1, false, closeIgnore, sendFDFail);
+ runTest("SU4\xff\xff\0\0\0\0", 9,
+ "S", 1,
+ false, closeIgnore, sendFDFail);
}
-}
+} // Anonymous namespace
diff --git a/src/bin/stats/.gitignore b/src/bin/stats/.gitignore
new file mode 100644
index 0000000..7ff3797
--- /dev/null
+++ b/src/bin/stats/.gitignore
@@ -0,0 +1,4 @@
+/b10-stats
+/b10-stats-httpd
+/stats.py
+/stats_httpd.py
diff --git a/src/bin/stats/b10-stats-httpd.8 b/src/bin/stats/b10-stats-httpd.8
index 1206e1d..6e53543 100644
--- a/src/bin/stats/b10-stats-httpd.8
+++ b/src/bin/stats/b10-stats-httpd.8
@@ -1,22 +1,13 @@
'\" t
.\" Title: b10-stats-httpd
.\" Author: [FIXME: author] [see http://docbook.sf.net/el/author]
-.\" Generator: DocBook XSL Stylesheets v1.76.1 <http://docbook.sf.net/>
-.\" Date: Mar 8, 2011
+.\" Generator: DocBook XSL Stylesheets v1.75.2 <http://docbook.sf.net/>
+.\" Date: February 28, 2012
.\" Manual: BIND10
.\" Source: BIND10
.\" Language: English
.\"
-.TH "B10\-STATS\-HTTPD" "8" "Mar 8, 2011" "BIND10" "BIND10"
-.\" -----------------------------------------------------------------
-.\" * Define some portability stuff
-.\" -----------------------------------------------------------------
-.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-.\" http://bugs.debian.org/507673
-.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
-.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-.ie \n(.g .ds Aq \(aq
-.el .ds Aq '
+.TH "B10\-STATS\-HTTPD" "8" "February 28, 2012" "BIND10" "BIND10"
.\" -----------------------------------------------------------------
.\" * set default formatting
.\" -----------------------------------------------------------------
@@ -110,7 +101,9 @@ with its PID\&.
.RS 4
exits the
\fBb10\-stats\-httpd\fR
-process\&. (Note that the BIND 10 boss process will restart this service\&.)
+process\&. This has an optional
+\fIpid\fR
+argument to select the process ID to stop\&. (Note that the BIND 10 boss process may restart this service if configured\&.)
.RE
.SH "SEE ALSO"
.PP
@@ -125,8 +118,8 @@ BIND 10 Guide\&.
.PP
\fBb10\-stats\-httpd\fR
-was designed and implemented by Naoki Kambe of JPRS in Mar 2011\&.
+was designed and implemented by Naoki Kambe of JPRS in March 2011\&.
.SH "COPYRIGHT"
.br
-Copyright \(co 2011 Internet Systems Consortium, Inc. ("ISC")
+Copyright \(co 2011-2012 Internet Systems Consortium, Inc. ("ISC")
.br
diff --git a/src/bin/stats/b10-stats-httpd.xml b/src/bin/stats/b10-stats-httpd.xml
index c8df9b8..a372244 100644
--- a/src/bin/stats/b10-stats-httpd.xml
+++ b/src/bin/stats/b10-stats-httpd.xml
@@ -2,7 +2,7 @@
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd"
[<!ENTITY mdash "—">]>
<!--
- - Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+ - 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
@@ -20,7 +20,7 @@
<refentry>
<refentryinfo>
- <date>Mar 8, 2011</date>
+ <date>February 28, 2012</date>
</refentryinfo>
<refmeta>
@@ -36,7 +36,7 @@
<docinfo>
<copyright>
- <year>2011</year>
+ <year>2011-2012</year>
<holder>Internet Systems Consortium, Inc. ("ISC")</holder>
</copyright>
</docinfo>
@@ -171,9 +171,12 @@
<term><command>shutdown</command></term>
<listitem>
<para>
- exits the <command>b10-stats-httpd</command> process. (Note that
- the BIND 10 boss process will restart this service.)
- </para>
+ exits the <command>b10-stats-httpd</command> process.
+ This has an optional <varname>pid</varname> argument to
+ select the process ID to stop.
+ (Note that the BIND 10 boss process may restart this service
+ if configured.)
+ </para>
</listitem>
</varlistentry>
</variablelist>
@@ -205,7 +208,7 @@
<title>HISTORY</title>
<para>
<command>b10-stats-httpd</command> was designed and implemented by Naoki
- Kambe of JPRS in Mar 2011.
+ Kambe of JPRS in March 2011.
</para>
</refsect1>
</refentry><!--
diff --git a/src/bin/stats/b10-stats.8 b/src/bin/stats/b10-stats.8
index 0204ca1..80fbb50 100644
--- a/src/bin/stats/b10-stats.8
+++ b/src/bin/stats/b10-stats.8
@@ -2,12 +2,12 @@
.\" Title: b10-stats
.\" Author: [FIXME: author] [see http://docbook.sf.net/el/author]
.\" Generator: DocBook XSL Stylesheets v1.75.2 <http://docbook.sf.net/>
-.\" Date: August 11, 2011
+.\" Date: March 1, 2012
.\" Manual: BIND10
.\" Source: BIND10
.\" Language: English
.\"
-.TH "B10\-STATS" "8" "August 11, 2011" "BIND10" "BIND10"
+.TH "B10\-STATS" "8" "March 1, 2012" "BIND10" "BIND10"
.\" -----------------------------------------------------------------
.\" * set default formatting
.\" -----------------------------------------------------------------
@@ -79,9 +79,9 @@ will send the statistics data in JSON format\&. By default, it outputs all the s
\fBshutdown\fR
will shutdown the
\fBb10\-stats\fR
-process\&. (Note that the
-\fBbind10\fR
-parent may restart it\&.)
+process\&. This has an optional
+\fIpid\fR
+argument to select the process ID to stop\&. (Note that the BIND 10 boss process may restart this service if configured\&.)
.PP
\fBstatus\fR
@@ -90,25 +90,22 @@ simply indicates that the daemon is running\&.
.PP
The
\fBb10\-stats\fR
-daemon contains these statistics:
-.PP
-report_time
-.RS 4
-The latest report date and time in ISO 8601 format\&.
-.RE
+daemon contains these
+\(lqStats\(rq
+statistics:
.PP
-stats\&.boot_time
+boot_time
.RS 4
The date and time when this daemon was started in ISO 8601 format\&. This is a constant which can\'t be reset except by restarting
\fBb10\-stats\fR\&.
.RE
.PP
-stats\&.last_update_time
+last_update_time
.RS 4
The date and time (in ISO 8601 format) when this daemon last received data from another component\&.
.RE
.PP
-stats\&.lname
+lname
.RS 4
This is the name used for the
\fBb10\-msgq\fR
@@ -116,14 +113,19 @@ command\-control channel\&. (This is a constant which can\'t be reset except by
\fBb10\-stats\fR\&.)
.RE
.PP
-stats\&.start_time
+report_time
+.RS 4
+The latest report date and time in ISO 8601 format\&.
+.RE
+.PP
+start_time
.RS 4
This is the date and time (in ISO 8601 format) when this daemon started collecting data\&.
.RE
.PP
-stats\&.timestamp
+timestamp
.RS 4
-The current date and time represented in seconds since UNIX epoch (1970\-01\-01T0 0:00:00Z) with precision (delimited with a period) up to one hundred thousandth of second\&.
+The current date and time represented in seconds since UNIX epoch (1970\-01\-01T00:00:00Z) with precision (delimited with a period) up to one hundred thousandth of second\&.
.RE
.PP
See other manual pages for explanations for their statistics that are kept track by
@@ -150,5 +152,5 @@ The
daemon was initially designed and implemented by Naoki Kambe of JPRS in October 2010\&.
.SH "COPYRIGHT"
.br
-Copyright \(co 2010 Internet Systems Consortium, Inc. ("ISC")
+Copyright \(co 2010-2012 Internet Systems Consortium, Inc. ("ISC")
.br
diff --git a/src/bin/stats/b10-stats.xml b/src/bin/stats/b10-stats.xml
index 13ada7a..4599563 100644
--- a/src/bin/stats/b10-stats.xml
+++ b/src/bin/stats/b10-stats.xml
@@ -2,7 +2,7 @@
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd"
[<!ENTITY mdash "—">]>
<!--
- - Copyright (C) 2010,2011 Internet Systems Consortium, Inc. ("ISC")
+ - Copyright (C) 2010-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
@@ -20,7 +20,7 @@
<refentry>
<refentryinfo>
- <date>August 11, 2011</date>
+ <date>March 1, 2012</date>
</refentryinfo>
<refmeta>
@@ -36,7 +36,7 @@
<docinfo>
<copyright>
- <year>2010</year>
+ <year>2010-2012</year>
<holder>Internet Systems Consortium, Inc. ("ISC")</holder>
</copyright>
</docinfo>
@@ -129,7 +129,10 @@
<para>
<command>shutdown</command> will shutdown the
<command>b10-stats</command> process.
- (Note that the <command>bind10</command> parent may restart it.)
+ This has an optional <varname>pid</varname> argument to
+ select the process ID to stop.
+ (Note that the BIND 10 boss process may restart this service
+ if configured.)
</para>
<para>
@@ -143,20 +146,15 @@
<title>STATISTICS DATA</title>
<para>
- The <command>b10-stats</command> daemon contains these statistics:
+ The <command>b10-stats</command> daemon contains these
+ <quote>Stats</quote> statistics:
</para>
<variablelist>
- <varlistentry>
- <term>report_time</term>
-<!-- TODO: why not named stats.report_time? -->
- <listitem><simpara>The latest report date and time in
- ISO 8601 format.</simpara></listitem>
- </varlistentry>
<varlistentry>
- <term>stats.boot_time</term>
+ <term>boot_time</term>
<listitem><simpara>The date and time when this daemon was
started in ISO 8601 format.
This is a constant which can't be reset except by restarting
@@ -165,14 +163,14 @@
</varlistentry>
<varlistentry>
- <term>stats.last_update_time</term>
+ <term>last_update_time</term>
<listitem><simpara>The date and time (in ISO 8601 format)
when this daemon last received data from another component.
</simpara></listitem>
</varlistentry>
<varlistentry>
- <term>stats.lname</term>
+ <term>lname</term>
<listitem><simpara>This is the name used for the
<command>b10-msgq</command> command-control channel.
(This is a constant which can't be reset except by restarting
@@ -181,16 +179,22 @@
</varlistentry>
<varlistentry>
- <term>stats.start_time</term>
+ <term>report_time</term>
+ <listitem><simpara>The latest report date and time in
+ ISO 8601 format.</simpara></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>start_time</term>
<listitem><simpara>This is the date and time (in ISO 8601 format)
when this daemon started collecting data.
</simpara></listitem>
</varlistentry>
<varlistentry>
- <term>stats.timestamp</term>
+ <term>timestamp</term>
<listitem><simpara>The current date and time represented in
- seconds since UNIX epoch (1970-01-01T0 0:00:00Z) with
+ seconds since UNIX epoch (1970-01-01T00:00:00Z) with
precision (delimited with a period) up to
one hundred thousandth of second.</simpara></listitem>
</varlistentry>
diff --git a/src/bin/stats/stats-httpd.spec b/src/bin/stats/stats-httpd.spec
index 6307135..783611d 100644
--- a/src/bin/stats/stats-httpd.spec
+++ b/src/bin/stats/stats-httpd.spec
@@ -47,7 +47,13 @@
{
"command_name": "shutdown",
"command_description": "Shut down the stats httpd",
- "command_args": []
+ "command_args": [
+ {
+ "item_name": "pid",
+ "item_type": "integer",
+ "item_optional": true
+ }
+ ]
}
]
}
diff --git a/src/bin/stats/stats.py.in b/src/bin/stats/stats.py.in
index 51c4e09..938a062 100755
--- a/src/bin/stats/stats.py.in
+++ b/src/bin/stats/stats.py.in
@@ -184,8 +184,11 @@ class Stats:
raise StatsError("stats spec file is incorrect: "
+ ", ".join(errors))
- while self.running:
- self.mccs.check_command(False)
+ try:
+ while self.running:
+ self.mccs.check_command(False)
+ finally:
+ self.mccs.send_stopping()
def config_handler(self, new_config):
"""
@@ -301,9 +304,11 @@ class Stats:
return isc.config.create_answer(
0, "Stats is up. (PID " + str(os.getpid()) + ")")
- def command_shutdown(self):
+ def command_shutdown(self, pid=None):
"""
handle shutdown command
+
+ The pid argument is ignored, it is here to match the signature.
"""
logger.info(STATS_RECEIVED_SHUTDOWN_COMMAND)
self.running = False
diff --git a/src/bin/stats/stats.spec b/src/bin/stats/stats.spec
index e716b62..d3bdcca 100644
--- a/src/bin/stats/stats.spec
+++ b/src/bin/stats/stats.spec
@@ -12,7 +12,13 @@
{
"command_name": "shutdown",
"command_description": "Shut down the stats module",
- "command_args": []
+ "command_args": [
+ {
+ "item_name": "pid",
+ "item_type": "integer",
+ "item_optional": true
+ }
+ ]
},
{
"command_name": "show",
diff --git a/src/bin/stats/stats_httpd.py.in b/src/bin/stats/stats_httpd.py.in
index f265abb..c9bd0f5 100644
--- a/src/bin/stats/stats_httpd.py.in
+++ b/src/bin/stats/stats_httpd.py.in
@@ -203,6 +203,7 @@ class StatsHttpd:
"""Closes a ModuleCCSession object"""
if self.mccs is None:
return
+ self.mccs.send_stopping()
logger.debug(DBG_STATHTTPD_INIT, STATHTTPD_CLOSING_CC_SESSION)
self.mccs.close()
diff --git a/src/bin/stats/tests/b10-stats-httpd_test.py b/src/bin/stats/tests/b10-stats-httpd_test.py
index b6847bd..e74405a 100644
--- a/src/bin/stats/tests/b10-stats-httpd_test.py
+++ b/src/bin/stats/tests/b10-stats-httpd_test.py
@@ -37,7 +37,10 @@ import random
import isc
import stats_httpd
import stats
-from test_utils import BaseModules, ThreadingServerManager, MyStats, MyStatsHttpd, SignalHandler, send_command, send_shutdown
+from test_utils import BaseModules, ThreadingServerManager, MyStats,\
+ MyStatsHttpd, SignalHandler,\
+ send_command, send_shutdown
+from isc.testutils.ccsession_mock import MockModuleCCSession
DUMMY_DATA = {
'Boss' : {
@@ -676,7 +679,13 @@ class TestStatsHttpd(unittest.TestCase):
def test_openclose_mccs(self):
self.stats_httpd = MyStatsHttpd(get_availaddr())
+ mccs = MockModuleCCSession()
+ self.stats_httpd.mccs = mccs
+ self.assertFalse(self.stats_httpd.mccs.stopped)
+ self.assertFalse(self.stats_httpd.mccs.closed)
self.stats_httpd.close_mccs()
+ self.assertTrue(mccs.stopped)
+ self.assertTrue(mccs.closed)
self.assertEqual(self.stats_httpd.mccs, None)
self.stats_httpd.open_mccs()
self.assertIsNotNone(self.stats_httpd.mccs)
diff --git a/src/bin/stats/tests/b10-stats_test.py b/src/bin/stats/tests/b10-stats_test.py
index 3c8599a..d9f8d37 100644
--- a/src/bin/stats/tests/b10-stats_test.py
+++ b/src/bin/stats/tests/b10-stats_test.py
@@ -31,6 +31,7 @@ import imp
import stats
import isc.cc.session
from test_utils import BaseModules, ThreadingServerManager, MyStats, SignalHandler, send_command, send_shutdown
+from isc.testutils.ccsession_mock import MockModuleCCSession
class TestUtilties(unittest.TestCase):
items = [
@@ -201,9 +202,20 @@ class TestStats(unittest.TestCase):
self.assertEqual(send_command("status", "Stats"),
(0, "Stats is up. (PID " + str(os.getpid()) + ")"))
self.assertTrue(self.stats.running)
+ # Due to a race-condition related to the threads used in these
+ # tests, use of the mock session and the stopped check (see
+ # below), are temporarily disabled
+ # See ticket #1668
+ # Override moduleCCSession so we can check if send_stopping is called
+ #self.stats.mccs = MockModuleCCSession()
self.assertEqual(send_shutdown("Stats"), (0, None))
self.assertFalse(self.stats.running)
- self.stats_server.shutdown()
+ # Call server.shutdown with argument True so the thread.join() call
+ # blocks and we are sure the main loop has finished (and set
+ # mccs.stopped)
+ self.stats_server.shutdown(True)
+ # Also temporarily disabled for #1668, see above
+ #self.assertTrue(self.stats.mccs.stopped)
# start with err
self.stats = stats.Stats()
diff --git a/src/bin/stats/tests/test_utils.py b/src/bin/stats/tests/test_utils.py
index 3f6ff33..29fc785 100644
--- a/src/bin/stats/tests/test_utils.py
+++ b/src/bin/stats/tests/test_utils.py
@@ -72,9 +72,19 @@ class ThreadingServerManager:
self.server._started.wait()
self.server._started.clear()
- def shutdown(self):
+ def shutdown(self, blocking=False):
+ """Shut down the server by calling its own shutdown() method.
+ Then wait for its thread to finish. If blocking is True,
+ the thread.join() blocks until the thread finishes. If not,
+ it uses a zero timeout. The latter is necessary in a number
+ of existing tests. We should redo this part (we should not
+ even need threads in most, if not all, of these threads, see
+ ticket #1668)"""
self.server.shutdown()
- self.server._thread.join(0) # timeout is 0
+ if blocking:
+ self.server._thread.join()
+ else:
+ self.server._thread.join(0) # timeout is 0
def do_nothing(*args, **kwargs): pass
diff --git a/src/bin/tests/.gitignore b/src/bin/tests/.gitignore
new file mode 100644
index 0000000..b39aa86
--- /dev/null
+++ b/src/bin/tests/.gitignore
@@ -0,0 +1 @@
+/process_rename_test.py
diff --git a/src/bin/usermgr/.gitignore b/src/bin/usermgr/.gitignore
new file mode 100644
index 0000000..e116052
--- /dev/null
+++ b/src/bin/usermgr/.gitignore
@@ -0,0 +1,3 @@
+/b10-cmdctl-usermgr
+/b10-cmdctl-usermgr.py
+/run_b10-cmdctl-usermgr.sh
diff --git a/src/bin/xfrin/.gitignore b/src/bin/xfrin/.gitignore
new file mode 100644
index 0000000..5ac1942
--- /dev/null
+++ b/src/bin/xfrin/.gitignore
@@ -0,0 +1,3 @@
+/b10-xfrin
+/run_b10-xfrin.sh
+/xfrin.py
diff --git a/src/bin/xfrin/tests/.gitignore b/src/bin/xfrin/tests/.gitignore
new file mode 100644
index 0000000..4de3f47
--- /dev/null
+++ b/src/bin/xfrin/tests/.gitignore
@@ -0,0 +1 @@
+/xfrin_test
diff --git a/src/bin/xfrin/tests/xfrin_test.py b/src/bin/xfrin/tests/xfrin_test.py
index 986957a..8ce1ae0 100644
--- a/src/bin/xfrin/tests/xfrin_test.py
+++ b/src/bin/xfrin/tests/xfrin_test.py
@@ -20,6 +20,7 @@ import socket
import sys
import io
from isc.testutils.tsigctx_mock import MockTSIGContext
+from isc.testutils.ccsession_mock import MockModuleCCSession
from isc.testutils.rrset_utils import *
from xfrin import *
import xfrin
@@ -105,7 +106,7 @@ class XfrinTestException(Exception):
class XfrinTestTimeoutException(Exception):
pass
-class MockCC():
+class MockCC(MockModuleCCSession):
def get_default_value(self, identifier):
# The returned values should be identical to the spec file
# XXX: these should be retrieved from the spec file
@@ -1317,6 +1318,14 @@ class TestAXFR(TestXfrinConnection):
self.assertEqual(self.conn.do_xfrin(False), XFRIN_OK)
self.assertFalse(self.conn._datasrc_client._journaling_enabled)
+ self.assertEqual(2, self.conn._transfer_stats.message_count)
+ self.assertEqual(2, self.conn._transfer_stats.axfr_rr_count)
+ self.assertEqual(0, self.conn._transfer_stats.ixfr_changeset_count)
+ self.assertEqual(0, self.conn._transfer_stats.ixfr_deletion_count)
+ self.assertEqual(0, self.conn._transfer_stats.ixfr_addition_count)
+ self.assertEqual(177, self.conn._transfer_stats.byte_count)
+ self.assertGreater(self.conn._transfer_stats.get_running_time(), 0)
+
def test_do_xfrin_with_tsig(self):
# use TSIG with a mock context. we fake all verify results to
# emulate successful verification.
@@ -1686,6 +1695,14 @@ class TestIXFRSession(TestXfrinConnection):
self.assertEqual(TEST_ZONE_NAME, qmsg.get_question()[0].get_name())
self.assertEqual(RRType.IXFR(), qmsg.get_question()[0].get_type())
+ self.assertEqual(1, self.conn._transfer_stats.message_count)
+ self.assertEqual(0, self.conn._transfer_stats.axfr_rr_count)
+ self.assertEqual(1, self.conn._transfer_stats.ixfr_changeset_count)
+ self.assertEqual(1, self.conn._transfer_stats.ixfr_deletion_count)
+ self.assertEqual(1, self.conn._transfer_stats.ixfr_addition_count)
+ self.assertEqual(188, self.conn._transfer_stats.byte_count)
+ self.assertGreater(self.conn._transfer_stats.get_running_time(), 0)
+
def test_do_xfrin_fail(self):
'''IXFR fails due to a protocol error.
@@ -1718,6 +1735,14 @@ class TestIXFRSession(TestXfrinConnection):
self.conn.response_generator = create_response
self.assertEqual(XFRIN_OK, self.conn.do_xfrin(False, RRType.IXFR()))
+ self.assertEqual(1, self.conn._transfer_stats.message_count)
+ self.assertEqual(0, self.conn._transfer_stats.axfr_rr_count)
+ self.assertEqual(0, self.conn._transfer_stats.ixfr_changeset_count)
+ self.assertEqual(0, self.conn._transfer_stats.ixfr_deletion_count)
+ self.assertEqual(0, self.conn._transfer_stats.ixfr_addition_count)
+ self.assertEqual(80, self.conn._transfer_stats.byte_count)
+ self.assertGreater(self.conn._transfer_stats.get_running_time(), 0)
+
class TestXFRSessionWithSQLite3(TestXfrinConnection):
'''Tests for XFR sessions using an SQLite3 DB.
@@ -2052,7 +2077,9 @@ class TestXfrin(unittest.TestCase):
self.args['tsig_key'] = ''
def tearDown(self):
+ self.assertFalse(self.xfr._module_cc.stopped);
self.xfr.shutdown()
+ self.assertTrue(self.xfr._module_cc.stopped);
sys.stderr= self.stderr_backup
def _do_parse_zone_name_class(self):
@@ -2712,6 +2739,63 @@ class TestFormatting(unittest.TestCase):
self.assertRaises(TypeError, format_addrinfo,
(socket.AF_INET, "asdf", ()))
+class TestXfrinTransferStats(unittest.TestCase):
+ def setUp(self):
+ self.stats = XfrinTransferStats()
+
+ def zero_check(self):
+ # Checks whether all counters are zero
+ self.assertEqual(0, self.stats.message_count)
+ self.assertEqual(0, self.stats.axfr_rr_count)
+ self.assertEqual(0, self.stats.byte_count)
+ self.assertEqual(0, self.stats.ixfr_changeset_count)
+ self.assertEqual(0, self.stats.ixfr_deletion_count)
+ self.assertEqual(0, self.stats.ixfr_addition_count)
+
+ def test_init(self):
+ self.zero_check()
+ self.assertIsNone(self.stats._end_time)
+
+ def test_get_running_time(self):
+ self.assertIsNone(self.stats._end_time)
+ runtime = self.stats.get_running_time()
+ self.assertIsNotNone(self.stats._end_time)
+ self.assertGreater(runtime, 0)
+ # make sure a second get does not change anything
+ runtime2 = self.stats.get_running_time()
+ self.assertEqual(runtime, runtime2)
+ # And that no counters have been modified
+ self.zero_check()
+
+ def test_bytes_per_second(self):
+ zbps = self.stats.get_bytes_per_second()
+ self.assertEqual(0, zbps)
+
+ self.stats._start_time = 1
+ self.stats._end_time = 2
+ self.stats.byte_count += 4
+ zbps = self.stats.get_bytes_per_second()
+ self.assertEqual(4, zbps)
+
+ self.stats._start_time = float(1)
+ self.stats._end_time = float(11)
+ self.assertEqual(10, self.stats.get_running_time())
+ self.stats.byte_count = 1234
+ zbps = self.stats.get_bytes_per_second()
+ self.assertEqual(123.4, zbps)
+
+ # if for some reason the runtime is 0, depending
+ # on whether bytes have actually been seen, bps is either
+ # 0 or 'infinite'
+ self.stats._end_time = self.stats._start_time
+ zbps = self.stats.get_bytes_per_second()
+ self.assertEqual(float("inf"), zbps)
+
+ self.stats.byte_count = 0
+ zbps = self.stats.get_bytes_per_second()
+ self.assertEqual(0, zbps)
+
+
if __name__== "__main__":
try:
isc.log.resetUnitTestRootLogger()
diff --git a/src/bin/xfrin/xfrin.py.in b/src/bin/xfrin/xfrin.py.in
index 8c0e754..863c5b9 100755
--- a/src/bin/xfrin/xfrin.py.in
+++ b/src/bin/xfrin/xfrin.py.in
@@ -24,6 +24,7 @@ import struct
import threading
import socket
import random
+import time
from functools import reduce
from optparse import OptionParser, OptionValueError
from isc.config.ccsession import *
@@ -381,6 +382,7 @@ class XfrinIXFRDeleteSOA(XfrinState):
conn._diff = Diff(conn._datasrc_client, conn._zone_name, False, True)
conn._diff.delete_data(rr)
self.set_xfrstate(conn, XfrinIXFRDelete())
+ conn.get_transfer_stats().ixfr_deletion_count += 1
return True
class XfrinIXFRDelete(XfrinState):
@@ -391,6 +393,7 @@ class XfrinIXFRDelete(XfrinState):
self.set_xfrstate(conn, XfrinIXFRAddSOA())
return False
conn._diff.delete_data(rr)
+ conn.get_transfer_stats().ixfr_deletion_count += 1
return True
class XfrinIXFRAddSOA(XfrinState):
@@ -402,11 +405,14 @@ class XfrinIXFRAddSOA(XfrinState):
' RR is given in IXFRAddSOA state')
conn._diff.add_data(rr)
self.set_xfrstate(conn, XfrinIXFRAdd())
+ conn.get_transfer_stats().ixfr_addition_count += 1
return True
class XfrinIXFRAdd(XfrinState):
def handle_rr(self, conn, rr):
if rr.get_type() == RRType.SOA():
+ # This SOA marks the end of a difference sequence
+ conn.get_transfer_stats().ixfr_changeset_count += 1
soa_serial = get_soa_serial(rr.get_rdata()[0])
if soa_serial == conn._end_serial:
conn._diff.commit()
@@ -422,6 +428,7 @@ class XfrinIXFRAdd(XfrinState):
self.set_xfrstate(conn, XfrinIXFRDeleteSOA())
return False
conn._diff.add_data(rr)
+ conn.get_transfer_stats().ixfr_addition_count += 1
return True
class XfrinIXFREnd(XfrinState):
@@ -462,6 +469,7 @@ class XfrinAXFR(XfrinState):
conn._end_serial, soa_serial)
self.set_xfrstate(conn, XfrinAXFREnd())
+ conn.get_transfer_stats().axfr_rr_count += 1
# Yes, we've eaten this RR.
return True
@@ -484,6 +492,55 @@ class XfrinAXFREnd(XfrinState):
conn._diff.commit()
return False
+class XfrinTransferStats:
+ """
+ This class keeps a record of transfer data for logging purposes.
+ It records number of messages, rrs, and bytes transfered, as well
+ as the start and end time. The start time is set upon instantiation of
+ this class. The end time is set the first time finalize(),
+ get_running_time(), or get_bytes_per_second() is called. The end time is
+ set only once; subsequent calls to any of these methods does not modify
+ it further.
+ All _count instance variables can be directly set as needed by the
+ class collecting these results.
+ """
+ def __init__(self):
+ self.message_count = 0
+ self.axfr_rr_count = 0
+ self.byte_count = 0
+ self.ixfr_changeset_count = 0;
+ self.ixfr_deletion_count = 0;
+ self.ixfr_addition_count = 0;
+ self._start_time = time.time()
+ self._end_time = None
+
+ def finalize(self):
+ """Sets the end time to time.time() if not done already."""
+ if self._end_time is None:
+ self._end_time = time.time()
+
+ def get_running_time(self):
+ """Calls finalize(), then returns the difference between creation
+ and finalization time"""
+ self.finalize()
+ return self._end_time - self._start_time
+
+ def get_bytes_per_second(self):
+ """Returns the number of bytes per second, based on the result of
+ get_running_time() and the value of bytes_count."""
+ runtime = self.get_running_time()
+ if runtime > 0.0:
+ return float(self.byte_count) / runtime
+ else:
+ # This should never happen, but if some clock is so
+ # off or reset in the meantime, we do need to return
+ # *something* (and not raise an error)
+ if self.byte_count == 0:
+ return 0.0
+ else:
+ return float("inf")
+
+
class XfrinConnection(asyncore.dispatcher):
'''Do xfrin in this class. '''
@@ -534,6 +591,10 @@ class XfrinConnection(asyncore.dispatcher):
# easier tests (in normal case we always use the default)
self._tsig_ctx_creator = lambda key : TSIGContext(key)
+ # keep a record of this specific transfer to log on success
+ # (time, rr/s, etc)
+ self._transfer_stats = XfrinTransferStats()
+
def init_socket(self):
'''Initialize the underlyig socket.
@@ -599,6 +660,11 @@ class XfrinConnection(asyncore.dispatcher):
def get_xfrstate(self):
return self.__state
+ def get_transfer_stats(self):
+ """Returns the transfer stats object, used to measure transfer time,
+ and number of messages/records/bytes transfered."""
+ return self._transfer_stats
+
def zone_str(self):
'''A convenience function for logging to include zone name and class'''
return format_zone_str(self._zone_name, self._rrclass)
@@ -823,7 +889,29 @@ class XfrinConnection(asyncore.dispatcher):
self._send_query(self._request_type)
self.__state = XfrinInitialSOA()
self._handle_xfrin_responses()
- logger.info(XFRIN_XFR_TRANSFER_SUCCESS, req_str, self.zone_str())
+ # Depending what data was found, we log different status reports
+ # (In case of an AXFR-style IXFR, print the 'AXFR' message)
+ if self._transfer_stats.axfr_rr_count == 0:
+ logger.info(XFRIN_IXFR_TRANSFER_SUCCESS,
+ self.zone_str(),
+ self._transfer_stats.message_count,
+ self._transfer_stats.ixfr_changeset_count,
+ self._transfer_stats.ixfr_deletion_count,
+ self._transfer_stats.ixfr_addition_count,
+ self._transfer_stats.byte_count,
+ "%.3f" % self._transfer_stats.get_running_time(),
+ "%.f" % self._transfer_stats.get_bytes_per_second()
+ )
+ else:
+ logger.info(XFRIN_TRANSFER_SUCCESS,
+ req_str,
+ self.zone_str(),
+ self._transfer_stats.message_count,
+ self._transfer_stats.axfr_rr_count,
+ self._transfer_stats.byte_count,
+ "%.3f" % self._transfer_stats.get_running_time(),
+ "%.f" % self._transfer_stats.get_bytes_per_second()
+ )
except XfrinZoneUptodate:
# Eventually we'll probably have to treat this case as a trigger
@@ -895,9 +983,11 @@ class XfrinConnection(asyncore.dispatcher):
while read_next_msg:
data_len = self._get_request_response(2)
msg_len = socket.htons(struct.unpack('H', data_len)[0])
+ self._transfer_stats.byte_count += msg_len + 2
recvdata = self._get_request_response(msg_len)
msg = Message(Message.PARSE)
msg.from_wire(recvdata, Message.PRESERVE_ORDER)
+ self._transfer_stats.message_count += 1
# TSIG related checks, including an unexpected signed response
self._check_response_tsig(msg, recvdata)
@@ -1224,6 +1314,7 @@ class Xfrin:
''' shutdown the xfrin process. the thread which is doing xfrin should be
terminated.
'''
+ self._module_cc.send_stopping()
self._shutdown_event.set()
main_thread = threading.currentThread()
for th in threading.enumerate():
diff --git a/src/bin/xfrin/xfrin.spec b/src/bin/xfrin/xfrin.spec
index c1ba61e..7ab1085 100644
--- a/src/bin/xfrin/xfrin.spec
+++ b/src/bin/xfrin/xfrin.spec
@@ -86,7 +86,13 @@
{
"command_name": "shutdown",
"command_description": "Shut down xfrin module",
- "command_args": []
+ "command_args": [
+ {
+ "item_name": "pid",
+ "item_type": "integer",
+ "item_optional": true
+ }
+ ]
}
]
}
diff --git a/src/bin/xfrin/xfrin_messages.mes b/src/bin/xfrin/xfrin_messages.mes
index 5e182d8..eae1c69 100644
--- a/src/bin/xfrin/xfrin_messages.mes
+++ b/src/bin/xfrin/xfrin_messages.mes
@@ -15,92 +15,21 @@
# No namespace declaration - these constants go in the global namespace
# of the xfrin messages python module.
-% XFRIN_ZONE_CREATED Zone %1 not found in the given data source, newly created
-On starting an xfrin session, it is identified that the zone to be
-transferred is not found in the data source. This can happen if a
-secondary DNS server first tries to perform AXFR from a primary server
-without creating the zone image beforehand (e.g. by b10-loadzone). As
-of this writing the xfrin process provides backward compatible
-behavior to previous versions: creating a new one in the data source
-not to surprise existing users too much. This is probably not a good
-idea, however, in terms of who should be responsible for managing
-zones at a higher level. In future it is more likely that a separate
-zone management framework is provided, and the situation where the
-given zone isn't found in xfrout will be treated as an error.
-
-% XFRIN_ZONE_NO_SOA Zone %1 does not have SOA
-On starting an xfrin session, it is identified that the zone to be
-transferred does not have an SOA RR in the data source. This is not
-necessarily an error; if a secondary DNS server first tries to perform
-transfer from a primary server, the zone can be empty, and therefore
-doesn't have an SOA. Subsequent AXFR will fill in the zone; if the
-attempt is IXFR it will fail in query creation.
-
-% XFRIN_ZONE_MULTIPLE_SOA Zone %1 has %2 SOA RRs
-On starting an xfrin session, it is identified that the zone to be
-transferred has multiple SOA RRs. Such a zone is broken, but could be
-accidentally configured especially in a data source using "non
-captive" backend database. The implementation ignores entire SOA RRs
-and tries to continue processing as if the zone were empty. This
-means subsequent AXFR can succeed and possibly replace the zone with
-valid content, but an IXFR attempt will fail.
-
-% XFRIN_ZONE_SERIAL_AHEAD Serial number (%1) for %2 received from master %3 < ours (%4)
-The response to an SOA query prior to xfr indicated that the zone's
-SOA serial at the primary server is smaller than that of the xfrin
-client. This is not necessarily an error especially if that
-particular primary server is another secondary server which hasn't got
-the latest version of the zone. But if the primary server is known to
-be the real source of the zone, some unexpected inconsistency may have
-happened, and you may want to take a closer look. In this case xfrin
-doesn't perform subsequent zone transfer.
-
-% XFRIN_XFR_OTHER_FAILURE %1 transfer of zone %2 failed: %3
-The XFR transfer for the given zone has failed due to a problem outside
-of the xfrin module. Possible reasons are a broken DNS message or failure
-in database connection. The error is shown in the log message.
-
-% XFRIN_XFR_TRANSFER_PROTOCOL_ERROR %1 transfer of zone %2 with %3 failed: %4
-The XFR transfer for the given zone has failed due to a protocol
-error, such as an unexpected response from the primary server. The
-error is shown in the log message. It may be because the primary
-server implementation is broken or (although less likely) there was
-some attack attempt, but it can also happen due to configuration
-mismatch such as the remote server does not have authority for the
-zone any more but the local configuration hasn't been updated. So it
-is recommended to check the primary server configuration.
-
-% XFRIN_XFR_TRANSFER_FAILURE %1 transfer of zone %2 with %3 failed: %4
-The XFR transfer for the given zone has failed due to an internal error.
-The error is shown in the log message.
-
-% XFRIN_XFR_TRANSFER_FALLBACK falling back from IXFR to AXFR for %1
-The IXFR transfer of the given zone failed. This might happen in many cases,
-such that the remote server doesn't support IXFR, we don't have the SOA record
-(or the zone at all), we are out of sync, etc. In many of these situations,
-AXFR could still work. Therefore we try that one in case it helps.
-
-% XFRIN_XFR_PROCESS_FAILURE %1 transfer of zone %2/%3 failed: %4
-An XFR session failed outside the main protocol handling. This
-includes an error at the data source level at the initialization
-phase, unexpected failure in the network connection setup to the
-master server, or even more unexpected failure due to unlikely events
-such as memory allocation failure. Details of the error are shown in
-the log message. In general, these errors are not really expected
-ones, and indicate an installation error or a program bug. The
-session handler thread tries to clean up all intermediate resources
-even on these errors, but it may be incomplete. So, if this log
-message continuously appears, system resource consumption should be
-checked, and you may even want to disable the corresponding transfers.
-You may also want to file a bug report if this message appears so
-often.
-
-% XFRIN_XFR_TRANSFER_STARTED %1 transfer of zone %2 started
-A connection to the master server has been made, the serial value in
-the SOA record has been checked, and a zone transfer has been started.
-
-% XFRIN_XFR_TRANSFER_SUCCESS %1 transfer of zone %2 succeeded
-The XFR transfer of the given zone was successfully completed.
+% XFRIN_AXFR_INCONSISTENT_SOA AXFR SOAs are inconsistent for %1: %2 expected, %3 received
+The serial fields of the first and last SOAs of AXFR (including AXFR-style
+IXFR) are not the same. According to RFC 5936 these two SOAs must be the
+"same" (not only for the serial), but it is still not clear what the
+receiver should do if this condition does not hold. There was a discussion
+about this at the IETF dnsext wg:
+http://www.ietf.org/mail-archive/web/dnsext/current/msg07908.html
+and the general feeling seems that it would be better to reject the
+transfer if a mismatch is detected. On the other hand, also as noted
+in that email thread, neither BIND 9 nor NSD performs any comparison
+on the SOAs. For now, we only check the serials (ignoring other fields)
+and only leave a warning log message when a mismatch is found. If it
+turns out to happen with a real world primary server implementation
+and that server actually feeds broken data (e.g. mixed versions of
+zone), we can consider a stricter action.
% XFRIN_BAD_MASTER_ADDR_FORMAT bad format for master address: %1
The given master address is not a valid IP address.
@@ -127,6 +56,58 @@ error is given in the log message.
There was an error opening a connection to the master. The error is
shown in the log message.
+% XFRIN_GOT_INCREMENTAL_RESP got incremental response for %1
+In an attempt of IXFR processing, the begenning SOA of the first difference
+(following the initial SOA that specified the final SOA for all the
+differences) was found. This means a connection for xfrin tried IXFR
+and really aot a response for incremental updates.
+
+% XFRIN_GOT_NONINCREMENTAL_RESP got nonincremental response for %1
+Non incremental transfer was detected at the "first data" of a transfer,
+which is the RR following the initial SOA. Non incremental transfer is
+either AXFR or AXFR-style IXFR. In the latter case, it means that
+in a response to IXFR query the first data is not SOA or its SOA serial
+is not equal to the requested SOA serial.
+
+% XFRIN_IMPORT_DNS error importing python DNS module: %1
+There was an error importing the python DNS module pydnspp. The most
+likely cause is a PYTHONPATH problem.
+
+% XFRIN_IXFR_TRANSFER_SUCCESS incremental IXFR transfer of zone %1 succeeded (messages: %2, changesets: %3, deletions: %4, additions: %5, bytes: %6, run time: %7 seconds, %8 bytes/second)
+The IXFR transfer for the given zone was successful.
+The provided information contains the following values:
+
+messages: Number of overhead DNS messages in the transfer.
+
+changesets: Number of difference sequences.
+
+deletions: Number of Resource Records deleted by all the changesets combined,
+including the SOA records.
+
+additions: Number of Resource Records added by all the changesets combined,
+including the SOA records.
+
+bytes: Full size of the transfer data on the wire.
+
+run time: Time (in seconds) the complete ixfr took.
+
+bytes/second: Transfer speed.
+
+Note that there is no cross-checking of additions and deletions; if the same
+RR gets added and deleted in multiple changesets, it is counted each time;
+therefore, for each changeset, there should at least be 1 deletion and 1
+addition (the updated SOA record).
+
+% XFRIN_IXFR_UPTODATE IXFR requested serial for %1 is %2, master has %3, not updating
+The first SOA record in an IXFR response indicates the zone's serial
+at the primary server is not newer than the client's. This is
+basically unexpected event because normally the client first checks
+the SOA serial by an SOA query, but can still happen if the transfer
+is manually invoked or (although unlikely) there is a rapid change at
+the primary server between the SOA and IXFR queries. The client
+implementation confirms the whole response is this single SOA, and
+aborts the transfer just like a successful case.
+
% XFRIN_MSGQ_SEND_ERROR error while contacting %1 and %2
There was a problem sending a message to the xfrout module or the
zone manager. This most likely means that the msgq daemon has quit or
@@ -142,10 +123,6 @@ from does not match the master address in the Xfrin configuration. The notify
is ignored. This may indicate that the configuration for the master is wrong,
that a wrong machine is sending notifies, or that fake notifies are being sent.
-% XFRIN_IMPORT_DNS error importing python DNS module: %1
-There was an error importing the python DNS module pydnspp. The most
-likely cause is a PYTHONPATH problem.
-
% XFRIN_RETRANSFER_UNKNOWN_ZONE got notification to retransfer unknown zone %1
There was an internal command to retransfer the given zone, but the
zone is not known to the system. This may indicate that the configuration
@@ -159,45 +136,105 @@ An informational message, this is output when the resolver starts up.
There was a keyboard interrupt signal to stop the xfrin daemon. The
daemon will now shut down.
+% XFRIN_TRANSFER_SUCCESS full %1 transfer of zone %2 succeeded (messages: %3, records: %4, bytes: %5, run time: %6 seconds, %7 bytes/second)
+The AXFR transfer of the given zone was successful.
+The provided information contains the following values:
+
+messages: Number of overhead DNS messages in the transfer
+
+records: Number of Resource Records in the full transfer, excluding the
+final SOA record that marks the end of the AXFR.
+
+bytes: Full size of the transfer data on the wire.
+
+run time: Time (in seconds) the complete axfr took
+
+bytes/second: Transfer speed
+
% XFRIN_UNKNOWN_ERROR unknown error: %1
An uncaught exception was raised while running the xfrin daemon. The
exception message is printed in the log message.
-% XFRIN_IXFR_UPTODATE IXFR requested serial for %1 is %2, master has %3, not updating
-The first SOA record in an IXFR response indicates the zone's serial
-at the primary server is not newer than the client's. This is
-basically unexpected event because normally the client first checks
-the SOA serial by an SOA query, but can still happen if the transfer
-is manually invoked or (although unlikely) there is a rapid change at
-the primary server between the SOA and IXFR queries. The client
-implementation confirms the whole response is this single SOA, and
-aborts the transfer just like a successful case.
+% XFRIN_XFR_OTHER_FAILURE %1 transfer of zone %2 failed: %3
+The XFR transfer for the given zone has failed due to a problem outside
+of the xfrin module. Possible reasons are a broken DNS message or failure
+in database connection. The error is shown in the log message.
-% XFRIN_GOT_INCREMENTAL_RESP got incremental response for %1
-In an attempt of IXFR processing, the begenning SOA of the first difference
-(following the initial SOA that specified the final SOA for all the
-differences) was found. This means a connection for xfrin tried IXFR
-and really aot a response for incremental updates.
+% XFRIN_XFR_PROCESS_FAILURE %1 transfer of zone %2/%3 failed: %4
+An XFR session failed outside the main protocol handling. This
+includes an error at the data source level at the initialization
+phase, unexpected failure in the network connection setup to the
+master server, or even more unexpected failure due to unlikely events
+such as memory allocation failure. Details of the error are shown in
+the log message. In general, these errors are not really expected
+ones, and indicate an installation error or a program bug. The
+session handler thread tries to clean up all intermediate resources
+even on these errors, but it may be incomplete. So, if this log
+message continuously appears, system resource consumption should be
+checked, and you may even want to disable the corresponding transfers.
+You may also want to file a bug report if this message appears so
+often.
-% XFRIN_GOT_NONINCREMENTAL_RESP got nonincremental response for %1
-Non incremental transfer was detected at the "first data" of a transfer,
-which is the RR following the initial SOA. Non incremental transfer is
-either AXFR or AXFR-style IXFR. In the latter case, it means that
-in a response to IXFR query the first data is not SOA or its SOA serial
-is not equal to the requested SOA serial.
+% XFRIN_XFR_TRANSFER_FAILURE %1 transfer of zone %2 with %3 failed: %4
+The XFR transfer for the given zone has failed due to an internal error.
+The error is shown in the log message.
-% XFRIN_AXFR_INCONSISTENT_SOA AXFR SOAs are inconsistent for %1: %2 expected, %3 received
-The serial fields of the first and last SOAs of AXFR (including AXFR-style
-IXFR) are not the same. According to RFC 5936 these two SOAs must be the
-"same" (not only for the serial), but it is still not clear what the
-receiver should do if this condition does not hold. There was a discussion
-about this at the IETF dnsext wg:
-http://www.ietf.org/mail-archive/web/dnsext/current/msg07908.html
-and the general feeling seems that it would be better to reject the
-transfer if a mismatch is detected. On the other hand, also as noted
-in that email thread, neither BIND 9 nor NSD performs any comparison
-on the SOAs. For now, we only check the serials (ignoring other fields)
-and only leave a warning log message when a mismatch is found. If it
-turns out to happen with a real world primary server implementation
-and that server actually feeds broken data (e.g. mixed versions of
-zone), we can consider a stricter action.
+% XFRIN_XFR_TRANSFER_FALLBACK falling back from IXFR to AXFR for %1
+The IXFR transfer of the given zone failed. This might happen in many cases,
+such that the remote server doesn't support IXFR, we don't have the SOA record
+(or the zone at all), we are out of sync, etc. In many of these situations,
+AXFR could still work. Therefore we try that one in case it helps.
+
+% XFRIN_XFR_TRANSFER_PROTOCOL_ERROR %1 transfer of zone %2 with %3 failed: %4
+The XFR transfer for the given zone has failed due to a protocol
+error, such as an unexpected response from the primary server. The
+error is shown in the log message. It may be because the primary
+server implementation is broken or (although less likely) there was
+some attack attempt, but it can also happen due to configuration
+mismatch such as the remote server does not have authority for the
+zone any more but the local configuration hasn't been updated. So it
+is recommended to check the primary server configuration.
+
+% XFRIN_XFR_TRANSFER_STARTED %1 transfer of zone %2 started
+A connection to the master server has been made, the serial value in
+the SOA record has been checked, and a zone transfer has been started.
+
+% XFRIN_ZONE_CREATED Zone %1 not found in the given data source, newly created
+On starting an xfrin session, it is identified that the zone to be
+transferred is not found in the data source. This can happen if a
+secondary DNS server first tries to perform AXFR from a primary server
+without creating the zone image beforehand (e.g. by b10-loadzone). As
+of this writing the xfrin process provides backward compatible
+behavior to previous versions: creating a new one in the data source
+not to surprise existing users too much. This is probably not a good
+idea, however, in terms of who should be responsible for managing
+zones at a higher level. In future it is more likely that a separate
+zone management framework is provided, and the situation where the
+given zone isn't found in xfrout will be treated as an error.
+
+% XFRIN_ZONE_MULTIPLE_SOA Zone %1 has %2 SOA RRs
+On starting an xfrin session, it is identified that the zone to be
+transferred has multiple SOA RRs. Such a zone is broken, but could be
+accidentally configured especially in a data source using "non
+captive" backend database. The implementation ignores entire SOA RRs
+and tries to continue processing as if the zone were empty. This
+means subsequent AXFR can succeed and possibly replace the zone with
+valid content, but an IXFR attempt will fail.
+
+% XFRIN_ZONE_NO_SOA Zone %1 does not have SOA
+On starting an xfrin session, it is identified that the zone to be
+transferred does not have an SOA RR in the data source. This is not
+necessarily an error; if a secondary DNS server first tries to perform
+transfer from a primary server, the zone can be empty, and therefore
+doesn't have an SOA. Subsequent AXFR will fill in the zone; if the
+attempt is IXFR it will fail in query creation.
+
+% XFRIN_ZONE_SERIAL_AHEAD Serial number (%1) for %2 received from master %3 < ours (%4)
+The response to an SOA query prior to xfr indicated that the zone's
+SOA serial at the primary server is smaller than that of the xfrin
+client. This is not necessarily an error especially if that
+particular primary server is another secondary server which hasn't got
+the latest version of the zone. But if the primary server is known to
+be the real source of the zone, some unexpected inconsistency may have
+happened, and you may want to take a closer look. In this case xfrin
+doesn't perform subsequent zone transfer.
diff --git a/src/bin/xfrout/.gitignore b/src/bin/xfrout/.gitignore
new file mode 100644
index 0000000..2ace679
--- /dev/null
+++ b/src/bin/xfrout/.gitignore
@@ -0,0 +1,5 @@
+/b10-xfrout
+/run_b10-xfrout.sh
+/xfrout.py
+/xfrout.spec
+/xfrout.spec.pre
diff --git a/src/bin/xfrout/b10-xfrout.8 b/src/bin/xfrout/b10-xfrout.8
index c37198c..3670ec5 100644
--- a/src/bin/xfrout/b10-xfrout.8
+++ b/src/bin/xfrout/b10-xfrout.8
@@ -2,12 +2,12 @@
.\" Title: b10-xfrout
.\" Author: [FIXME: author] [see http://docbook.sf.net/el/author]
.\" Generator: DocBook XSL Stylesheets v1.75.2 <http://docbook.sf.net/>
-.\" Date: December 15, 2011
+.\" Date: February 28. 2012
.\" Manual: BIND10
.\" Source: BIND10
.\" Language: English
.\"
-.TH "B10\-XFROUT" "8" "December 15, 2011" "BIND10" "BIND10"
+.TH "B10\-XFROUT" "8" "February 28\&. 2012" "BIND10" "BIND10"
.\" -----------------------------------------------------------------
.\" * set default formatting
.\" -----------------------------------------------------------------
@@ -70,11 +70,6 @@ The configurable settings are:
defines the maximum number of outgoing zone transfers that can run concurrently\&. The default is 10\&.
.PP
-\fItsig_key_ring\fR
-A list of TSIG keys (each of which is in the form of
-\fIname:base64\-key[:algorithm]\fR) used for access control on transfer requests\&. The default is an empty list\&.
-.PP
-
\fItransfer_acl\fR
A list of ACL elements that apply to all transfer requests by default (unless overridden in
\fIzone_config\fR)\&. See the
@@ -129,7 +124,9 @@ The configuration commands are:
\fBshutdown\fR
stops all outbound zone transfers and exits
-\fBb10\-xfrout\fR\&. (Note that the BIND 10 boss process will restart this service\&.)
+\fBb10\-xfrout\fR\&. This has an optional
+\fIpid\fR
+argument to select the process ID to stop\&. (Note that the BIND 10 boss process may restart this service if configured\&.)
.PP
\fBzone_new_data_ready\fR
@@ -154,5 +151,5 @@ The
daemon was first implemented in March 2010 by Zhang Likun of CNNIC for the ISC BIND 10 project\&.
.SH "COPYRIGHT"
.br
-Copyright \(co 2010 Internet Systems Consortium, Inc. ("ISC")
+Copyright \(co 2010-2012 Internet Systems Consortium, Inc. ("ISC")
.br
diff --git a/src/bin/xfrout/b10-xfrout.xml b/src/bin/xfrout/b10-xfrout.xml
index 87d0267..aaf0eb1 100644
--- a/src/bin/xfrout/b10-xfrout.xml
+++ b/src/bin/xfrout/b10-xfrout.xml
@@ -2,7 +2,7 @@
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd"
[<!ENTITY mdash "—">]>
<!--
- - Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC")
+ - Copyright (C) 2010-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
@@ -20,7 +20,7 @@
<refentry>
<refentryinfo>
- <date>December 15, 2011</date>
+ <date>February 28. 2012</date>
</refentryinfo>
<refmeta>
@@ -36,7 +36,7 @@
<docinfo>
<copyright>
- <year>2010</year>
+ <year>2010-2012</year>
<holder>Internet Systems Consortium, Inc. ("ISC")</holder>
</copyright>
</docinfo>
@@ -98,13 +98,6 @@
that can run concurrently. The default is 10.
</para>
<para>
- <varname>tsig_key_ring</varname>
- A list of TSIG keys (each of which is in the form of
- <replaceable>name:base64-key[:algorithm]</replaceable>)
- used for access control on transfer requests.
- The default is an empty list.
- </para>
- <para>
<varname>transfer_acl</varname>
A list of ACL elements that apply to all transfer requests by
default (unless overridden in <varname>zone_config</varname>).
@@ -122,33 +115,6 @@
See the <citetitle>BIND 10 Guide</citetitle> for configuration examples.
The default is an empty list, that is, no zone specific configuration.
</para>
- <para>
- <varname>log_name</varname>
-<!-- TODO -->
- </para>
- <para>
- <varname>log_file</varname>
-<!-- TODO -->
- The location of the log file if using a file channel.
- If undefined, then the file channel is closed.
- The default is
- <filename>/usr/local/var/bind10-devel/log/Xfrout.log</filename>.
- </para>
- <para>
- <varname>log_severity</varname>
-<!-- TODO -->
- The default is "debug".
- </para>
- <para>
- <varname>log_versions</varname>
-<!-- TODO -->
- The default is 5.
- </para>
- <para>
- <varname>log_max_bytes</varname>
-<!-- TODO -->
- The default is 1048576.
- </para>
<!-- TODO: log configurations not documented yet in here. jreed
has some but waiting on decisions ... -->
@@ -160,21 +126,18 @@
</simpara></note>
-<!--
-
-tsig_key_ring list of
-tsig_key string
-
--->
-
<!-- TODO: formating -->
<para>
The configuration commands are:
</para>
+
<para>
<command>shutdown</command> stops all outbound zone transfers
- and exits <command>b10-xfrout</command>. (Note that the BIND 10
- boss process will restart this service.)
+ and exits <command>b10-xfrout</command>.
+ This has an optional <varname>pid</varname> argument to
+ select the process ID to stop.
+ (Note that the BIND 10 boss process may restart this service
+ if configured.)
</para>
<para>
diff --git a/src/bin/xfrout/tests/.gitignore b/src/bin/xfrout/tests/.gitignore
new file mode 100644
index 0000000..92c94aa
--- /dev/null
+++ b/src/bin/xfrout/tests/.gitignore
@@ -0,0 +1,2 @@
+/xfrout_test
+/xfrout_test.py
diff --git a/src/bin/xfrout/tests/xfrout_test.py.in b/src/bin/xfrout/tests/xfrout_test.py.in
index 396139e..b60535c 100644
--- a/src/bin/xfrout/tests/xfrout_test.py.in
+++ b/src/bin/xfrout/tests/xfrout_test.py.in
@@ -19,6 +19,7 @@
import unittest
import os
from isc.testutils.tsigctx_mock import MockTSIGContext
+from isc.testutils.ccsession_mock import MockModuleCCSession
from isc.cc.session import *
import isc.config
from isc.dns import *
@@ -27,6 +28,7 @@ from xfrout import *
import xfrout
import isc.log
import isc.acl.dns
+import isc.server_common.tsig_keyring
TESTDATA_SRCDIR = os.getenv("TESTDATASRCDIR")
TSIG_KEY = TSIGKey("example.com:SFuWd/q99SzF8Yzd1QbB9g==")
@@ -1154,6 +1156,39 @@ class TestUnixSockServer(unittest.TestCase):
self.write_sock, self.read_sock = socket.socketpair()
self.unix = MyUnixSockServer()
+ def test_tsig_keyring(self):
+ """
+ Check we use the global keyring when starting a request.
+ """
+ try:
+ # These are just so the keyring can be started
+ self.unix._cc.add_remote_config_by_name = \
+ lambda name, callback: None
+ self.unix._cc.get_remote_config_value = \
+ lambda module, name: ([], True)
+ self.unix._cc.remove_remote_config = lambda name: None
+ isc.server_common.tsig_keyring.init_keyring(self.unix._cc)
+ # These are not really interesting for the test. These are just
+ # handled over, so strings are OK.
+ self.unix._guess_remote = lambda sock: "Address"
+ self.unix._zone_config = "Zone config"
+ self.unix._acl = "acl"
+ # This would be the handler class, but we just check it is passed
+ # the right parametes, so function is enough for that.
+ keys = isc.server_common.tsig_keyring.get_keyring()
+ def handler(sock, data, server, keyring, address, acl, config):
+ self.assertEqual("sock", sock)
+ self.assertEqual("data", data)
+ self.assertEqual(self.unix, server)
+ self.assertEqual(keys, keyring)
+ self.assertEqual("Address", address)
+ self.assertEqual("acl", acl)
+ self.assertEqual("Zone config", config)
+ self.unix.RequestHandlerClass = handler
+ self.unix.finish_request("sock", "data")
+ finally:
+ isc.server_common.tsig_keyring.deinit_keyring()
+
def test_guess_remote(self):
"""Test we can guess the remote endpoint when we have only the
file descriptor. This is needed, because we get only that one
@@ -1213,25 +1248,12 @@ class TestUnixSockServer(unittest.TestCase):
def test_update_config_data(self):
self.check_default_ACL()
- tsig_key_str = 'example.com:SFuWd/q99SzF8Yzd1QbB9g=='
- tsig_key_list = [tsig_key_str]
- bad_key_list = ['bad..example.com:SFuWd/q99SzF8Yzd1QbB9g==']
self.unix.update_config_data({'transfers_out':10 })
self.assertEqual(self.unix._max_transfers_out, 10)
- self.assertTrue(self.unix.tsig_key_ring is not None)
self.check_default_ACL()
- self.unix.update_config_data({'transfers_out':9,
- 'tsig_key_ring':tsig_key_list})
+ self.unix.update_config_data({'transfers_out':9})
self.assertEqual(self.unix._max_transfers_out, 9)
- self.assertEqual(self.unix.tsig_key_ring.size(), 1)
- self.unix.tsig_key_ring.remove(Name("example.com."))
- self.assertEqual(self.unix.tsig_key_ring.size(), 0)
-
- # bad tsig key
- config_data = {'transfers_out':9, 'tsig_key_ring': bad_key_list}
- self.assertRaises(None, self.unix.update_config_data(config_data))
- self.assertEqual(self.unix.tsig_key_ring.size(), 0)
# Load the ACL
self.unix.update_config_data({'transfer_acl': [{'from': '127.0.0.1',
@@ -1423,6 +1445,31 @@ class TestInitialization(unittest.TestCase):
xfrout.init_paths()
self.assertEqual(xfrout.UNIX_SOCKET_FILE, "The/Socket/File")
+class MyNotifier():
+ def __init__(self):
+ self.shutdown_called = False
+
+ def shutdown(self):
+ self.shutdown_called = True
+
+class MyXfroutServer(XfroutServer):
+ def __init__(self):
+ self._cc = MockModuleCCSession()
+ self._shutdown_event = threading.Event()
+ self._notifier = MyNotifier()
+ self._unix_socket_server = None
+ # Disable the wait for threads
+ self._wait_for_threads = lambda : None
+
+class TestXfroutServer(unittest.TestCase):
+ def setUp(self):
+ self.xfrout_server = MyXfroutServer()
+
+ def test_shutdown(self):
+ self.xfrout_server.shutdown()
+ self.assertTrue(self.xfrout_server._notifier.shutdown_called)
+ self.assertTrue(self.xfrout_server._cc.stopped)
+
if __name__== "__main__":
isc.log.resetUnitTestRootLogger()
unittest.main()
diff --git a/src/bin/xfrout/xfrout.py.in b/src/bin/xfrout/xfrout.py.in
index 38ef9c7..165560b 100755
--- a/src/bin/xfrout/xfrout.py.in
+++ b/src/bin/xfrout/xfrout.py.in
@@ -34,6 +34,7 @@ import select
import errno
from optparse import OptionParser, OptionValueError
from isc.util import socketserver_mixin
+import isc.server_common.tsig_keyring
from isc.log_messages.xfrout_messages import *
@@ -769,7 +770,7 @@ class UnixSockServer(socketserver_mixin.NoPollMixIn,
zone_config = self._zone_config
self._lock.release()
self.RequestHandlerClass(sock_fd, request_data, self,
- self.tsig_key_ring,
+ isc.server_common.tsig_keyring.get_keyring(),
self._guess_remote(sock_fd), acl, zone_config)
def _remove_unused_sock_file(self, sock_file):
@@ -833,7 +834,6 @@ class UnixSockServer(socketserver_mixin.NoPollMixIn,
self._acl = new_acl
self._zone_config = new_zone_config
self._max_transfers_out = new_config.get('transfers_out')
- self.set_tsig_key_ring(new_config.get('tsig_key_ring'))
except Exception as e:
self._lock.release()
raise e
@@ -870,21 +870,6 @@ class UnixSockServer(socketserver_mixin.NoPollMixIn,
zclass_str + ': ' + str(e))
return new_config
- def set_tsig_key_ring(self, key_list):
- """Set the tsig_key_ring , given a TSIG key string list representation. """
-
- # XXX add values to configure zones/tsig options
- self.tsig_key_ring = TSIGKeyRing()
- # If key string list is empty, create a empty tsig_key_ring
- if not key_list:
- return
-
- for key_item in key_list:
- try:
- self.tsig_key_ring.add(TSIGKey(key_item))
- except InvalidParameter as ipe:
- logger.error(XFROUT_BAD_TSIG_KEY_STRING, str(key_item))
-
def get_db_file(self):
file, is_default = self._cc.get_remote_config_value("Auth", "database_file")
# this too should be unnecessary, but currently the
@@ -920,7 +905,8 @@ class XfroutServer:
self._cc = isc.config.ModuleCCSession(SPECFILE_LOCATION, self.config_handler, self.command_handler)
self._config_data = self._cc.get_full_config()
self._cc.start()
- self._cc.add_remote_config(AUTH_SPECFILE_LOCATION);
+ self._cc.add_remote_config(AUTH_SPECFILE_LOCATION)
+ isc.server_common.tsig_keyring.init_keyring(self._cc)
self._start_xfr_query_listener()
self._start_notifier()
@@ -969,12 +955,18 @@ class XfroutServer:
global xfrout_server
xfrout_server = None #Avoid shutdown is called twice
+ self._cc.send_stopping()
self._shutdown_event.set()
self._notifier.shutdown()
if self._unix_socket_server:
self._unix_socket_server.shutdown()
+ self._wait_for_threads()
- # Wait for all threads to terminate
+ def _wait_for_threads(self):
+ # Wait for all threads to terminate. this is a call that is only used
+ # in shutdown(), but it has its own method, so we can test shutdown
+ # without involving thread operations (the test would override this
+ # method)
main_thread = threading.currentThread()
for th in threading.enumerate():
if th is main_thread:
diff --git a/src/bin/xfrout/xfrout.spec.pre.in b/src/bin/xfrout/xfrout.spec.pre.in
index 0891a57..ce8686e 100644
--- a/src/bin/xfrout/xfrout.spec.pre.in
+++ b/src/bin/xfrout/xfrout.spec.pre.in
@@ -9,48 +9,6 @@
"item_default": 10
},
{
- "item_name": "log_name",
- "item_type": "string",
- "item_optional": false,
- "item_default": "Xfrout"
- },
- {
- "item_name": "log_file",
- "item_type": "string",
- "item_optional": false,
- "item_default": "@@LOCALSTATEDIR@@/@PACKAGE@/log/Xfrout.log"
- },
- {
- "item_name": "log_severity",
- "item_type": "string",
- "item_optional": false,
- "item_default": "debug"
- },
- {
- "item_name": "log_versions",
- "item_type": "integer",
- "item_optional": false,
- "item_default": 5
- },
- {
- "item_name": "log_max_bytes",
- "item_type": "integer",
- "item_optional": false,
- "item_default": 1048576
- },
- {
- "item_name": "tsig_key_ring",
- "item_type": "list",
- "item_optional": true,
- "item_default": [],
- "list_item_spec" :
- {
- "item_name": "tsig_key",
- "item_type": "string",
- "item_optional": true
- }
- },
- {
"item_name": "transfer_acl",
"item_type": "list",
"item_optional": false,
@@ -106,7 +64,13 @@
{
"command_name": "shutdown",
"command_description": "Shut down Xfrout",
- "command_args": []
+ "command_args": [
+ {
+ "item_name": "pid",
+ "item_type": "integer",
+ "item_optional": true
+ }
+ ]
}
]
}
diff --git a/src/bin/zonemgr/.gitignore b/src/bin/zonemgr/.gitignore
new file mode 100644
index 0000000..2d64f2d
--- /dev/null
+++ b/src/bin/zonemgr/.gitignore
@@ -0,0 +1,5 @@
+/b10-zonemgr
+/run_b10-zonemgr.sh
+/zonemgr.py
+/zonemgr.spec
+/zonemgr.spec.pre
diff --git a/src/bin/zonemgr/b10-zonemgr.8 b/src/bin/zonemgr/b10-zonemgr.8
index 1d24bbf..1bce2af 100644
--- a/src/bin/zonemgr/b10-zonemgr.8
+++ b/src/bin/zonemgr/b10-zonemgr.8
@@ -2,12 +2,12 @@
.\" Title: b10-zonemgr
.\" Author: [FIXME: author] [see http://docbook.sf.net/el/author]
.\" Generator: DocBook XSL Stylesheets v1.75.2 <http://docbook.sf.net/>
-.\" Date: December 8, 2011
+.\" Date: February 28, 2012
.\" Manual: BIND10
.\" Source: BIND10
.\" Language: English
.\"
-.TH "B10\-ZONEMGR" "8" "December 8, 2011" "BIND10" "BIND10"
+.TH "B10\-ZONEMGR" "8" "February 28, 2012" "BIND10" "BIND10"
.\" -----------------------------------------------------------------
.\" * set default formatting
.\" -----------------------------------------------------------------
@@ -98,7 +98,9 @@ This is an internal command and not exposed to the administrator\&.
\fBshutdown\fR
exits
-\fBb10\-zonemgr\fR\&. (Note that the BIND 10 boss process will restart this service\&.)
+\fBb10\-zonemgr\fR\&. This has an optional
+\fIpid\fR
+argument to select the process ID to stop\&. (Note that the BIND 10 boss process may restart this service if configured\&.)
.PP
\fBzone_new_data_ready\fR
@@ -128,5 +130,5 @@ The
daemon was designed in July 2010 by CNNIC for the ISC BIND 10 project\&.
.SH "COPYRIGHT"
.br
-Copyright \(co 2010-2011 Internet Systems Consortium, Inc. ("ISC")
+Copyright \(co 2010-2012 Internet Systems Consortium, Inc. ("ISC")
.br
diff --git a/src/bin/zonemgr/b10-zonemgr.xml b/src/bin/zonemgr/b10-zonemgr.xml
index 5ea6041..f859d23 100644
--- a/src/bin/zonemgr/b10-zonemgr.xml
+++ b/src/bin/zonemgr/b10-zonemgr.xml
@@ -2,7 +2,7 @@
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd"
[<!ENTITY mdash "—">]>
<!--
- - Copyright (C) 2010-2011 Internet Systems Consortium, Inc. ("ISC")
+ - Copyright (C) 2010-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
@@ -20,7 +20,7 @@
<refentry>
<refentryinfo>
- <date>December 8, 2011</date>
+ <date>February 28, 2012</date>
</refentryinfo>
<refmeta>
@@ -36,7 +36,7 @@
<docinfo>
<copyright>
- <year>2010-2011</year>
+ <year>2010-2012</year>
<holder>Internet Systems Consortium, Inc. ("ISC")</holder>
</copyright>
</docinfo>
@@ -186,7 +186,10 @@
<para>
<command>shutdown</command> exits <command>b10-zonemgr</command>.
- (Note that the BIND 10 boss process will restart this service.)
+ This has an optional <varname>pid</varname> argument to
+ select the process ID to stop.
+ (Note that the BIND 10 boss process may restart this service
+ if configured.)
</para>
<para>
diff --git a/src/bin/zonemgr/tests/.gitignore b/src/bin/zonemgr/tests/.gitignore
new file mode 100644
index 0000000..41465e0
--- /dev/null
+++ b/src/bin/zonemgr/tests/.gitignore
@@ -0,0 +1 @@
+/zonemgr_test
diff --git a/src/bin/zonemgr/tests/zonemgr_test.py b/src/bin/zonemgr/tests/zonemgr_test.py
index 600453d..29924c8 100644
--- a/src/bin/zonemgr/tests/zonemgr_test.py
+++ b/src/bin/zonemgr/tests/zonemgr_test.py
@@ -20,6 +20,7 @@ import unittest
import os
import tempfile
from zonemgr import *
+from isc.testutils.ccsession_mock import MockModuleCCSession
ZONE_NAME_CLASS1_IN = ("example.net.", "IN")
ZONE_NAME_CLASS1_CH = ("example.net.", "CH")
@@ -48,10 +49,11 @@ class MySession():
def group_recvmsg(self, nonblock, seq):
return None, None
-class FakeCCSession(isc.config.ConfigData):
+class FakeCCSession(isc.config.ConfigData, MockModuleCCSession):
def __init__(self):
module_spec = isc.config.module_spec_from_file(SPECFILE_LOCATION)
ConfigData.__init__(self, module_spec)
+ MockModuleCCSession.__init__(self)
def get_remote_config_value(self, module_name, identifier):
if module_name == "Auth" and identifier == "database_file":
@@ -683,6 +685,12 @@ class TestZonemgr(unittest.TestCase):
self.zonemgr._config_data_check(config_data3)
self.assertEqual(0.5, config_data3.get("refresh_jitter"))
+ def test_shutdown(self):
+ self.assertFalse(self.zonemgr._module_cc.stopped)
+ self.zonemgr._shutdown_event.set()
+ self.zonemgr.run()
+ self.assertTrue(self.zonemgr._module_cc.stopped)
+
def tearDown(self):
pass
diff --git a/src/bin/zonemgr/zonemgr.py.in b/src/bin/zonemgr/zonemgr.py.in
index 4060bb5..7b16f1b 100755
--- a/src/bin/zonemgr/zonemgr.py.in
+++ b/src/bin/zonemgr/zonemgr.py.in
@@ -658,8 +658,11 @@ class Zonemgr:
def run(self):
self.running = True
- while not self._shutdown_event.is_set():
- self._module_cc.check_command(False)
+ try:
+ while not self._shutdown_event.is_set():
+ self._module_cc.check_command(False)
+ finally:
+ self._module_cc.send_stopping()
zonemgrd = None
diff --git a/src/bin/zonemgr/zonemgr.spec.pre.in b/src/bin/zonemgr/zonemgr.spec.pre.in
index 36f02df..defa203 100644
--- a/src/bin/zonemgr/zonemgr.spec.pre.in
+++ b/src/bin/zonemgr/zonemgr.spec.pre.in
@@ -63,7 +63,13 @@
{
"command_name": "shutdown",
"command_description": "Shut down Zonemgr",
- "command_args": []
+ "command_args": [
+ {
+ "item_name": "pid",
+ "item_type": "integer",
+ "item_optional": true
+ }
+ ]
}
]
}
diff --git a/src/cppcheck-suppress.lst b/src/cppcheck-suppress.lst
index 1020ffe..164c549 100644
--- a/src/cppcheck-suppress.lst
+++ b/src/cppcheck-suppress.lst
@@ -4,8 +4,19 @@ debug
missingInclude
// This is a template, and should be excluded from the check
unreadVariable:src/lib/dns/rdata/template.cc:61
-// Intentional self assignment tests. Suppress warning about them.
-selfAssignment:src/lib/dns/tests/name_unittest.cc:293
-selfAssignment:src/lib/dns/tests/rdata_unittest.cc:228
-selfAssignment:src/lib/dns/tests/tsigkey_unittest.cc:137
-selfAssignment:src/lib/dns/tests/rdata_txt_like_unittest.cc:222
+
+// Intentional self-comparisons
+duplicateExpression:src/lib/dns/tests/name_unittest.cc:569
+duplicateExpression:src/lib/dns/tests/name_unittest.cc:580
+duplicateExpression:src/lib/dns/tests/rrttl_unittest.cc:164
+duplicateExpression:src/lib/dns/tests/rrttl_unittest.cc:175
+duplicateExpression:src/lib/dns/tests/name_unittest.cc:568
+duplicateExpression:src/lib/dns/tests/name_unittest.cc:579
+
+// Intentional self-comparisons
+uselessCallsCompare:src/lib/dns/tests/rdata_dhcid_unittest.cc:96
+uselessCallsCompare:src/lib/dns/tests/rdata_in_a_unittest.cc:98
+uselessCallsCompare:src/lib/dns/tests/rdata_in_aaaa_unittest.cc:94
+uselessCallsCompare:src/lib/dns/tests/rdata_mx_unittest.cc:104
+uselessCallsCompare:src/lib/dns/tests/rdata_unittest.cc:254
+uselessCallsCompare:src/lib/dns/tests/rdata_unittest.cc:253
diff --git a/src/lib/acl/tests/.gitignore b/src/lib/acl/tests/.gitignore
new file mode 100644
index 0000000..d6d1ec8
--- /dev/null
+++ b/src/lib/acl/tests/.gitignore
@@ -0,0 +1 @@
+/run_unittests
diff --git a/src/lib/asiodns/.gitignore b/src/lib/asiodns/.gitignore
new file mode 100644
index 0000000..dedf17e
--- /dev/null
+++ b/src/lib/asiodns/.gitignore
@@ -0,0 +1,2 @@
+/asiodns_messages.cc
+/asiodns_messages.h
diff --git a/src/lib/asiodns/Makefile.am b/src/lib/asiodns/Makefile.am
index b5d030d..a03f147 100644
--- a/src/lib/asiodns/Makefile.am
+++ b/src/lib/asiodns/Makefile.am
@@ -24,6 +24,7 @@ libasiodns_la_SOURCES += dns_server.h
libasiodns_la_SOURCES += dns_service.cc dns_service.h
libasiodns_la_SOURCES += tcp_server.cc tcp_server.h
libasiodns_la_SOURCES += udp_server.cc udp_server.h
+libasiodns_la_SOURCES += sync_udp_server.cc sync_udp_server.h
libasiodns_la_SOURCES += io_fetch.cc io_fetch.h
libasiodns_la_SOURCES += logger.h logger.cc
diff --git a/src/lib/asiodns/dns_server.h b/src/lib/asiodns/dns_server.h
index d3a8528..119aa66 100644
--- a/src/lib/asiodns/dns_server.h
+++ b/src/lib/asiodns/dns_server.h
@@ -88,22 +88,6 @@ public:
/// to return.
virtual void resume(const bool done) { self_->resume(done); }
- /// \brief Indicate whether the server is able to send an answer
- /// to a query.
- ///
- /// This is presently used only for testing purposes.
- virtual bool hasAnswer() { return (self_->hasAnswer()); }
-
- /// \brief Returns the current value of the 'coroutine' object
- ///
- /// This is a temporary method, intended to be used for debugging
- /// purposes during development and removed later. It allows
- /// callers from outside the coroutine object to retrieve information
- /// about its current state.
- ///
- /// \return The value of the 'coroutine' object
- virtual int value() { return (self_->value()); }
-
/// \brief Returns a pointer to a clone of this DNSServer object.
///
/// When a \c DNSServer object is copied or assigned, the result will
diff --git a/src/lib/asiodns/io_fetch.cc b/src/lib/asiodns/io_fetch.cc
index ce3c5d2..eed5fdf 100644
--- a/src/lib/asiodns/io_fetch.cc
+++ b/src/lib/asiodns/io_fetch.cc
@@ -205,7 +205,8 @@ IOFetch::IOFetch(Protocol protocol, IOService& service,
}
void
-IOFetch::initIOFetch(MessagePtr& query_msg, Protocol protocol, IOService& service,
+IOFetch::initIOFetch(MessagePtr& query_msg, Protocol protocol,
+ IOService& service,
const isc::dns::Question& question,
const IOAddress& address, uint16_t port,
OutputBufferPtr& buff, Callback* cb, int wait, bool edns)
@@ -225,8 +226,10 @@ IOFetch::initIOFetch(MessagePtr& query_msg, Protocol protocol, IOService& servic
query_msg->setEDNS(edns_query);
}
- MessageRenderer renderer(*data_->msgbuf);
+ MessageRenderer renderer;
+ renderer.setBuffer(data_->msgbuf.get());
query_msg->toWire(renderer);
+ renderer.setBuffer(NULL);
}
// Return protocol in use.
diff --git a/src/lib/asiodns/sync_udp_server.cc b/src/lib/asiodns/sync_udp_server.cc
new file mode 100644
index 0000000..52da3bf
--- /dev/null
+++ b/src/lib/asiodns/sync_udp_server.cc
@@ -0,0 +1,215 @@
+// 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 <asio.hpp>
+#include <asio/error.hpp>
+
+#include "sync_udp_server.h"
+#include "logger.h"
+
+#include <asiolink/dummy_io_cb.h>
+#include <asiolink/udp_endpoint.h>
+#include <asiolink/udp_socket.h>
+
+#include <boost/bind.hpp>
+
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <unistd.h> // for some IPC/network system calls
+#include <errno.h>
+
+using namespace std;
+using namespace isc::asiolink;
+
+namespace isc {
+namespace asiodns {
+
+SyncUDPServer::SyncUDPServer(asio::io_service& io_service,
+ const asio::ip::address& addr,
+ const uint16_t port,
+ asiolink::SimpleCallback* checkin,
+ DNSLookup* lookup, DNSAnswer* answer) :
+ output_buffer_(new isc::util::OutputBuffer(0)),
+ query_(new isc::dns::Message(isc::dns::Message::PARSE)),
+ answer_(new isc::dns::Message(isc::dns::Message::RENDER)),
+ io_(io_service), checkin_callback_(checkin), lookup_callback_(lookup),
+ answer_callback_(answer), stopped_(false)
+{
+ // We must use different instantiations for v4 and v6;
+ // otherwise ASIO will bind to both
+ asio::ip::udp proto = addr.is_v4() ? asio::ip::udp::v4() :
+ asio::ip::udp::v6();
+ socket_.reset(new asio::ip::udp::socket(io_service, proto));
+ socket_->set_option(asio::socket_base::reuse_address(true));
+ if (addr.is_v6()) {
+ socket_->set_option(asio::ip::v6_only(true));
+ }
+ socket_->bind(asio::ip::udp::endpoint(addr, port));
+}
+
+SyncUDPServer::SyncUDPServer(asio::io_service& io_service, const int fd,
+ const int af, asiolink::SimpleCallback* checkin,
+ DNSLookup* lookup, DNSAnswer* answer) :
+ output_buffer_(new isc::util::OutputBuffer(0)),
+ query_(new isc::dns::Message(isc::dns::Message::PARSE)),
+ answer_(new isc::dns::Message(isc::dns::Message::RENDER)),
+ io_(io_service), checkin_callback_(checkin), lookup_callback_(lookup),
+ answer_callback_(answer), stopped_(false)
+{
+ if (af != AF_INET && af != AF_INET6) {
+ isc_throw(InvalidParameter, "Address family must be either AF_INET "
+ "or AF_INET6, not " << af);
+ }
+ LOG_DEBUG(logger, DBGLVL_TRACE_BASIC, ASIODNS_FD_ADD_UDP).arg(fd);
+ try {
+ socket_.reset(new asio::ip::udp::socket(io_service));
+ socket_->assign(af == AF_INET6 ? asio::ip::udp::v6() :
+ asio::ip::udp::v4(), fd);
+ } catch (const std::exception& exception) {
+ // Whatever the thing throws, it is something from ASIO and we
+ // convert it
+ isc_throw(IOError, exception.what());
+ }
+}
+
+void
+SyncUDPServer::scheduleRead() {
+ socket_->async_receive_from(asio::buffer(data_, MAX_LENGTH), sender_,
+ boost::bind(&SyncUDPServer::handleRead, this,
+ _1, _2));
+}
+
+void
+SyncUDPServer::handleRead(const asio::error_code& ec, const size_t length) {
+ // Abort on fatal errors
+ if (ec) {
+ using namespace asio::error;
+ if (ec.value() != would_block && ec.value() != try_again &&
+ ec.value() != interrupted) {
+ return;
+ }
+ }
+ // Some kind of interrupt, spurious wakeup, or like that. Just try reading
+ // again.
+ if (ec || length == 0) {
+ scheduleRead();
+ return;
+ }
+ // OK, we have a real packet of data. Let's dig into it!
+
+ // XXX: This is taken (and ported) from UDPSocket class. What the hell does
+ // it really mean?
+
+ // The UDP socket class has been extended with asynchronous functions
+ // and takes as a template parameter a completion callback class. As
+ // UDPServer does not use these extended functions (only those defined
+ // in the IOSocket base class) - but needs a UDPSocket to get hold of
+ // the underlying Boost UDP socket - DummyIOCallback is used. This
+ // provides the appropriate operator() but is otherwise functionless.
+ UDPSocket<DummyIOCallback> socket(*socket_);
+ UDPEndpoint endpoint(sender_);
+ IOMessage message(data_, length, socket, endpoint);
+ if (checkin_callback_ != NULL) {
+ (*checkin_callback_)(message);
+ if (stopped_) {
+ return;
+ }
+ }
+
+ // If we don't have a DNS Lookup provider, there's no point in
+ // continuing; we exit the coroutine permanently.
+ if (lookup_callback_ == NULL) {
+ scheduleRead();
+ return;
+ }
+
+ // Make sure the buffers are fresh
+ output_buffer_->clear();
+ query_->clear(isc::dns::Message::PARSE);
+ answer_->clear(isc::dns::Message::RENDER);
+
+ // Mark that we don't have an answer yet.
+ done_ = false;
+ resume_called_ = false;
+
+ // Call the actual lookup
+ (*lookup_callback_)(message, query_, answer_, output_buffer_, this);
+
+ if (!resume_called_) {
+ isc_throw(isc::Unexpected,
+ "No resume called from the lookup callback");
+ }
+
+ if (stopped_) {
+ return;
+ }
+
+ if (done_) {
+ // Good, there's an answer.
+ // Call the answer callback to render it.
+ (*answer_callback_)(message, query_, answer_, output_buffer_);
+
+ if (stopped_) {
+ return;
+ }
+
+ socket_->send_to(asio::buffer(output_buffer_->getData(),
+ output_buffer_->getLength()),
+ sender_);
+ }
+
+ // And schedule handling another socket.
+ scheduleRead();
+}
+
+void
+SyncUDPServer::operator()(asio::error_code, size_t) {
+ // To start the server, we just schedule reading of data when they
+ // arrive.
+ scheduleRead();
+}
+
+/// Stop the UDPServer
+void
+SyncUDPServer::stop() {
+ /// Using close instead of cancel, because cancel
+ /// will only cancel the asynchornized event already submitted
+ /// to io service, the events post to io service after
+ /// cancel still can be scheduled by io service, if
+ /// the socket is cloesed, all the asynchronized event
+ /// for it won't be scheduled by io service not matter it is
+ /// submit to io serice before or after close call. And we will
+ //. get bad_descriptor error
+ socket_->close();
+ stopped_ = true;
+}
+
+/// Post this coroutine on the ASIO service queue so that it will
+/// resume processing where it left off. The 'done' parameter indicates
+/// whether there is an answer to return to the client.
+void
+SyncUDPServer::resume(const bool done) {
+ resume_called_ = true;
+ done_ = done;
+}
+
+bool
+SyncUDPServer::hasAnswer() {
+ return (done_);
+}
+
+} // namespace asiodns
+} // namespace isc
diff --git a/src/lib/asiodns/sync_udp_server.h b/src/lib/asiodns/sync_udp_server.h
new file mode 100644
index 0000000..f21d3e5
--- /dev/null
+++ b/src/lib/asiodns/sync_udp_server.h
@@ -0,0 +1,161 @@
+// 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 __SYNC_UDP_SERVER_H
+#define __SYNC_UDP_SERVER_H 1
+
+#ifndef ASIO_HPP
+#error "asio.hpp must be included before including this, see asiolink.h as to why"
+#endif
+
+#include "dns_answer.h"
+#include "dns_lookup.h"
+#include "dns_server.h"
+
+#include <dns/message.h>
+#include <asiolink/simple_callback.h>
+#include <util/buffer.h>
+#include <exceptions/exceptions.h>
+
+#include <boost/noncopyable.hpp>
+
+#include <stdint.h>
+
+namespace isc {
+namespace asiodns {
+
+/// \brief An UDP server that doesn't asynchronous lookup handlers.
+///
+/// That means, the lookup handler must provide the answer right away.
+/// This allows for implementation with less overhead, compared with
+/// the UDPClass.
+class SyncUDPServer : public DNSServer, public boost::noncopyable {
+public:
+ /// \brief Constructor
+ /// \param io_service the asio::io_service to work with
+ /// \param addr the IP address to listen for queries on
+ /// \param port the port to listen for queries on
+ /// \param checkin the callbackprovider for non-DNS events
+ /// \param lookup the callbackprovider for DNS lookup events
+ /// \param answer the callbackprovider for DNS answer events
+ explicit SyncUDPServer(asio::io_service& io_service,
+ const asio::ip::address& addr, const uint16_t port,
+ isc::asiolink::SimpleCallback* checkin = NULL,
+ DNSLookup* lookup = NULL,
+ DNSAnswer* answer = NULL);
+
+ /// \brief Constructor
+ /// \param io_service the asio::io_service to work with
+ /// \param fd the file descriptor of opened UDP socket
+ /// \param af address family, either AF_INET or AF_INET6
+ /// \param checkin the callbackprovider for non-DNS events
+ /// \param lookup the callbackprovider for DNS lookup events
+ /// \param answer the callbackprovider for DNS answer events
+ /// \throw isc::InvalidParameter if af is neither AF_INET nor AF_INET6
+ /// \throw isc::asiolink::IOError when a low-level error happens, like the
+ /// fd is not a valid descriptor.
+ SyncUDPServer(asio::io_service& io_service, const int fd, const int af,
+ isc::asiolink::SimpleCallback* checkin = NULL,
+ DNSLookup* lookup = NULL, DNSAnswer* answer = NULL);
+
+ /// \brief Start the SyncUDPServer.
+ ///
+ /// This is the function operator to keep interface with other server
+ /// classes. They need that because they're coroutines.
+ virtual void operator()(asio::error_code ec = asio::error_code(),
+ size_t length = 0);
+
+ /// \brief Calls the lookup callback
+ virtual void asyncLookup() {
+ isc_throw(Unexpected,
+ "SyncUDPServer doesn't support asyncLookup by design, use "
+ "UDPServer if you need it.");
+ }
+
+ /// \brief Stop the running server
+ /// \note once the server stopped, it can't restart
+ virtual void stop();
+
+ /// \brief Resume operation
+ ///
+ /// Note that unlike other servers, this one expects it to be called
+ /// directly from the lookup callback. If it isn't, the server will
+ /// throw an Unexpected exception (probably to the event loop, which
+ /// would usually lead to termination of the program, but that's OK,
+ /// as it would be serious programmer error).
+ ///
+ /// \param done Set this to true if the lookup action is done and
+ /// we have an answer
+ virtual void resume(const bool done);
+
+ /// \brief Check if we have an answer
+ ///
+ /// \return true if we have an answer
+ virtual bool hasAnswer();
+
+ /// \brief Clones the object
+ ///
+ /// Since cloning is for the use of coroutines, the synchronous UDP server
+ /// does not need to be cloned. Therefore supporting it would be needless
+ /// work, and trying to clone it would be a programmer error anyway, this
+ /// throws Unexpected.
+ ///
+ /// \return a newly allocated copy of this object
+ virtual DNSServer* clone() {
+ isc_throw(Unexpected, "SyncUDPServer can't be cloned.");
+ }
+private:
+ // Internal state & buffers. We don't use the PIMPL idiom, as this class
+ // isn't usually used directly anyway.
+
+ // Maximum size of incoming UDP packet
+ static const size_t MAX_LENGTH = 4096;
+ // Buffer for incoming data
+ uint8_t data_[MAX_LENGTH];
+ // The buffer to render the output to and send it.
+ // If it was OK to have just a buffer, not the wrapper class,
+ // we could reuse the data_
+ isc::util::OutputBufferPtr output_buffer_;
+ // Objects to hold the query message and the answer
+ isc::dns::MessagePtr query_, answer_;
+ // The socket used for the communication
+ std::auto_ptr<asio::ip::udp::socket> socket_;
+ // The event loop we use
+ asio::io_service& io_;
+ // Place the socket puts the sender of a packet when it is received
+ asio::ip::udp::endpoint sender_;
+ // Callbacks
+ const asiolink::SimpleCallback* checkin_callback_;
+ const DNSLookup* lookup_callback_;
+ const DNSAnswer* answer_callback_;
+ // Answers from the lookup callback (not sent directly, but signalled
+ // through resume()
+ bool resume_called_, done_;
+ // This turns true when the server stops. Allows for not sending the
+ // answer after we closed the socket.
+ bool stopped_;
+
+ // Auxiliary functions
+
+ // Schedule next read on the socket. Just a wrapper around
+ // socket_->async_read_from with the correct parameters.
+ void scheduleRead();
+ // Callback from the socket's read call (called when there's an error or
+ // when a new packet comes).
+ void handleRead(const asio::error_code& ec, const size_t length);
+};
+
+} // namespace asiodns
+} // namespace isc
+#endif // __SYNC_UDP_SERVER_H
diff --git a/src/lib/asiodns/tcp_server.h b/src/lib/asiodns/tcp_server.h
index d079e97..a75fddb 100644
--- a/src/lib/asiodns/tcp_server.h
+++ b/src/lib/asiodns/tcp_server.h
@@ -62,9 +62,6 @@ public:
void asyncLookup();
void stop();
void resume(const bool done);
- bool hasAnswer() { return (done_); }
- int value() { return (get_value()); }
-
DNSServer* clone() {
TCPServer* s = new TCPServer(*this);
return (s);
diff --git a/src/lib/asiodns/tests/.gitignore b/src/lib/asiodns/tests/.gitignore
new file mode 100644
index 0000000..d6d1ec8
--- /dev/null
+++ b/src/lib/asiodns/tests/.gitignore
@@ -0,0 +1 @@
+/run_unittests
diff --git a/src/lib/asiodns/tests/dns_server_unittest.cc b/src/lib/asiodns/tests/dns_server_unittest.cc
index c9bbe7c..0064bba 100644
--- a/src/lib/asiodns/tests/dns_server_unittest.cc
+++ b/src/lib/asiodns/tests/dns_server_unittest.cc
@@ -19,6 +19,7 @@
#include <asiolink/io_endpoint.h>
#include <asiolink/io_error.h>
#include <asiodns/udp_server.h>
+#include <asiodns/sync_udp_server.h>
#include <asiodns/tcp_server.h>
#include <asiodns/dns_answer.h>
#include <asiodns/dns_lookup.h>
@@ -112,15 +113,22 @@ class DummyChecker : public SimpleCallback, public ServerStopper {
// \brief no lookup logic at all,just provide a checkpoint to stop the server
class DummyLookup : public DNSLookup, public ServerStopper {
- public:
- void operator()(const IOMessage& io_message,
- isc::dns::MessagePtr message,
- isc::dns::MessagePtr answer_message,
- isc::util::OutputBufferPtr buffer,
- DNSServer* server) const {
- stopServer();
+public:
+ DummyLookup() :
+ allow_resume_(true)
+ { }
+ void operator()(const IOMessage& io_message,
+ isc::dns::MessagePtr message,
+ isc::dns::MessagePtr answer_message,
+ isc::util::OutputBufferPtr buffer,
+ DNSServer* server) const {
+ stopServer();
+ if (allow_resume_) {
server->resume(true);
}
+ }
+ // If you want it not to call resume, set this to false
+ bool allow_resume_;
};
// \brief copy the data received from user to the answer part
@@ -314,10 +322,11 @@ class TCPClient : public SimpleClient {
// two servers, UDP client will only communicate with UDP server, same for TCP
// client
//
-// This is only the active part of the test. We run the test case twice, once
+// This is only the active part of the test. We run the test case four times, once
// for each type of initialization (once when giving it the address and port,
-// once when giving the file descriptor), to ensure it works both ways exactly
-// the same.
+// once when giving the file descriptor) multiplied by once for each type of UDP
+// server (UDPServer and SyncUDPServer), to ensure it works exactly the same.
+template<class UDPServerClass>
class DNSServerTestBase : public::testing::Test {
protected:
DNSServerTestBase() :
@@ -396,7 +405,7 @@ class DNSServerTestBase : public::testing::Test {
SimpleAnswer* const answer_;
UDPClient* const udp_client_;
TCPClient* const tcp_client_;
- UDPServer* udp_server_;
+ UDPServerClass* udp_server_;
TCPServer* tcp_server_;
// To access them in signal handle function, the following
@@ -406,18 +415,23 @@ class DNSServerTestBase : public::testing::Test {
};
// Initialization with name and port
-class AddrPortInit : public DNSServerTestBase {
+template<class UDPServerClass>
+class AddrPortInit : public DNSServerTestBase<UDPServerClass> {
protected:
AddrPortInit() {
- udp_server_ = new UDPServer(service, server_address_, server_port,
- checker_, lookup_, answer_);
- tcp_server_ = new TCPServer(service, server_address_, server_port,
- checker_, lookup_, answer_);
+ this->udp_server_ = new UDPServerClass(this->service,
+ this->server_address_,
+ server_port, this->checker_,
+ this->lookup_, this->answer_);
+ this->tcp_server_ = new TCPServer(this->service, this->server_address_,
+ server_port, this->checker_,
+ this->lookup_, this->answer_);
}
};
// Initialization by the file descriptor
-class FdInit : public DNSServerTestBase {
+template<class UDPServerClass>
+class FdInit : public DNSServerTestBase<UDPServerClass> {
private:
// Opens the file descriptor for us
// It uses the low-level C api, as it seems to be the easiest way to get
@@ -465,12 +479,14 @@ protected:
void SetUp() {
const int fdUDP(getFd(SOCK_DGRAM));
ASSERT_NE(-1, fdUDP) << strerror(errno);
- udp_server_ = new UDPServer(service, fdUDP, AF_INET6, checker_,
- lookup_, answer_);
+ this->udp_server_ = new UDPServerClass(this->service, fdUDP, AF_INET6,
+ this->checker_, this->lookup_,
+ this->answer_);
const int fdTCP(getFd(SOCK_STREAM));
ASSERT_NE(-1, fdTCP) << strerror(errno);
- tcp_server_ = new TCPServer(service, fdTCP, AF_INET6, checker_,
- lookup_, answer_);
+ this->tcp_server_ = new TCPServer(this->service, fdTCP, AF_INET6,
+ this->checker_, this->lookup_,
+ this->answer_);
}
};
@@ -478,11 +494,24 @@ protected:
template<class Parent>
class DNSServerTest : public Parent { };
-typedef ::testing::Types<AddrPortInit, FdInit> ServerTypes;
+typedef ::testing::Types<AddrPortInit<UDPServer>, AddrPortInit<SyncUDPServer>,
+ FdInit<UDPServer>, FdInit<SyncUDPServer> >
+ ServerTypes;
TYPED_TEST_CASE(DNSServerTest, ServerTypes);
-bool DNSServerTestBase::io_service_is_time_out = false;
-asio::io_service* DNSServerTestBase::current_service(NULL);
+typedef ::testing::Types<UDPServer, SyncUDPServer> UDPServerTypes;
+TYPED_TEST_CASE(DNSServerTestBase, UDPServerTypes);
+
+template<class UDPServerClass>
+bool DNSServerTestBase<UDPServerClass>::io_service_is_time_out = false;
+template<class UDPServerClass>
+asio::io_service* DNSServerTestBase<UDPServerClass>::current_service(NULL);
+
+typedef ::testing::Types<AddrPortInit<SyncUDPServer>, FdInit<SyncUDPServer> >
+ SyncTypes;
+template<class Parent>
+class SyncServerTest : public Parent { };
+TYPED_TEST_CASE(SyncServerTest, SyncTypes);
// Test whether server stopped successfully after client get response
// client will send query and start to wait for response, once client
@@ -608,17 +637,20 @@ TYPED_TEST(DNSServerTest, stopTCPServeMoreThanOnce) {
}
// It raises an exception when invalid address family is passed
-TEST_F(DNSServerTestBase, invalidFamily) {
+// The parameter here doesn't mean anything
+TYPED_TEST(DNSServerTestBase, invalidFamily) {
// We abuse DNSServerTestBase for this test, as we don't need the
// initialization.
- EXPECT_THROW(UDPServer(service, 0, AF_UNIX, checker_, lookup_,
- answer_), isc::InvalidParameter);
- EXPECT_THROW(TCPServer(service, 0, AF_UNIX, checker_, lookup_,
- answer_), isc::InvalidParameter);
+ EXPECT_THROW(TypeParam(this->service, 0, AF_UNIX, this->checker_,
+ this->lookup_, this->answer_),
+ isc::InvalidParameter);
+ EXPECT_THROW(TCPServer(this->service, 0, AF_UNIX, this->checker_,
+ this->lookup_, this->answer_),
+ isc::InvalidParameter);
}
// It raises an exception when invalid address family is passed
-TEST_F(DNSServerTestBase, invalidTCPFD) {
+TYPED_TEST(DNSServerTestBase, invalidTCPFD) {
// We abuse DNSServerTestBase for this test, as we don't need the
// initialization.
/*
@@ -630,11 +662,12 @@ TEST_F(DNSServerTestBase, invalidTCPFD) {
EXPECT_THROW(UDPServer(service, -1, AF_INET, checker_, lookup_,
answer_), isc::asiolink::IOError);
*/
- EXPECT_THROW(TCPServer(service, -1, AF_INET, checker_, lookup_,
- answer_), isc::asiolink::IOError);
+ EXPECT_THROW(TCPServer(this->service, -1, AF_INET, this->checker_,
+ this->lookup_, this->answer_),
+ isc::asiolink::IOError);
}
-TEST_F(DNSServerTestBase, DISABLED_invalidUDPFD) {
+TYPED_TEST(DNSServerTestBase, DISABLED_invalidUDPFD) {
/*
FIXME: The UDP server doesn't fail reliably with an invalid FD.
We need to find a way to trigger it reliably (it seems epoll
@@ -642,8 +675,24 @@ TEST_F(DNSServerTestBase, DISABLED_invalidUDPFD) {
not the others, maybe we could make it run this at least on epoll-based
systems).
*/
- EXPECT_THROW(UDPServer(service, -1, AF_INET, checker_, lookup_,
- answer_), isc::asiolink::IOError);
+ EXPECT_THROW(TypeParam(this->service, -1, AF_INET, this->checker_,
+ this->lookup_, this->answer_),
+ isc::asiolink::IOError);
+}
+
+// Check it rejects some of the unsupported operatirons
+TYPED_TEST(SyncServerTest, unsupportedOps) {
+ EXPECT_THROW(this->udp_server_->clone(), isc::Unexpected);
+ EXPECT_THROW(this->udp_server_->asyncLookup(), isc::Unexpected);
+}
+
+// Check it rejects forgotten resume (eg. insists that it is synchronous)
+TYPED_TEST(SyncServerTest, mustResume) {
+ this->lookup_->allow_resume_ = false;
+ ASSERT_THROW(this->testStopServerByStopper(this->udp_server_,
+ this->udp_client_,
+ this->lookup_),
+ isc::Unexpected);
}
}
diff --git a/src/lib/asiodns/tests/io_fetch_unittest.cc b/src/lib/asiodns/tests/io_fetch_unittest.cc
index 936c6c7..acb184c 100644
--- a/src/lib/asiodns/tests/io_fetch_unittest.cc
+++ b/src/lib/asiodns/tests/io_fetch_unittest.cc
@@ -133,10 +133,15 @@ public:
EDNSPtr msg_edns(new EDNS());
msg_edns->setUDPSize(Message::DEFAULT_MAX_EDNS0_UDPSIZE);
msg.setEDNS(msg_edns);
- MessageRenderer renderer(*msgbuf_);
+
+ MessageRenderer renderer;
+ renderer.setBuffer(msgbuf_.get());
+ msg.toWire(renderer);
+ renderer.setBuffer(NULL);
+
+ renderer.setBuffer(expected_buffer_.get());
msg.toWire(renderer);
- MessageRenderer renderer2(*expected_buffer_);
- msg.toWire(renderer2);
+ renderer.setBuffer(NULL);
// Initialize the test data to be returned: tests will return a
// substring of this data. (It's convenient to have this as a member of
@@ -581,20 +586,22 @@ public:
return_data_ = "Message returned to the client";
udp::endpoint remote;
- socket.async_receive_from(asio::buffer(receive_buffer_, sizeof(receive_buffer_)),
- remote,
- boost::bind(&IOFetchTest::udpReceiveHandler, this, &remote, &socket,
- _1, _2, bad_qid, second_send));
+ socket.async_receive_from(asio::buffer(receive_buffer_,
+ sizeof(receive_buffer_)),
+ remote,
+ boost::bind(&IOFetchTest::udpReceiveHandler,
+ this, &remote, &socket,
+ _1, _2, bad_qid, second_send));
service_.get_io_service().post(udp_fetch_);
if (debug_) {
- cout << "udpSendReceive: async_receive_from posted, waiting for callback" <<
- endl;
+ cout << "udpSendReceive: async_receive_from posted,"
+ "waiting for callback" << endl;
}
service_.run();
socket.close();
- EXPECT_TRUE(run_);;
+ EXPECT_TRUE(run_);
}
};
diff --git a/src/lib/asiodns/udp_server.cc b/src/lib/asiodns/udp_server.cc
index e4e7896..0fb8bec 100644
--- a/src/lib/asiodns/udp_server.cc
+++ b/src/lib/asiodns/udp_server.cc
@@ -343,10 +343,5 @@ UDPServer::resume(const bool done) {
data_->io_.post(*this);
}
-bool
-UDPServer::hasAnswer() {
- return (data_->done_);
-}
-
} // namespace asiodns
} // namespace isc
diff --git a/src/lib/asiodns/udp_server.h b/src/lib/asiodns/udp_server.h
index c82b78c..2b6a574 100644
--- a/src/lib/asiodns/udp_server.h
+++ b/src/lib/asiodns/udp_server.h
@@ -83,16 +83,6 @@ public:
/// we have an answer
void resume(const bool done);
- /// \brief Check if we have an answer
- ///
- /// \return true if we have an answer
- bool hasAnswer();
-
- /// \brief Returns the coroutine state value
- ///
- /// \return the coroutine state value
- int value() { return (get_value()); }
-
/// \brief Clones the object
///
/// \return a newly allocated copy of this object
diff --git a/src/lib/asiolink/tests/.gitignore b/src/lib/asiolink/tests/.gitignore
new file mode 100644
index 0000000..d6d1ec8
--- /dev/null
+++ b/src/lib/asiolink/tests/.gitignore
@@ -0,0 +1 @@
+/run_unittests
diff --git a/src/lib/bench/benchmark_util.cc b/src/lib/bench/benchmark_util.cc
index 9cf3b26..34356c8 100644
--- a/src/lib/bench/benchmark_util.cc
+++ b/src/lib/bench/benchmark_util.cc
@@ -61,8 +61,7 @@ loadQueryData(istream& input, BenchQueries& queries, const RRClass& qclass,
string line;
unsigned int linenum = 0;
Message query_message(Message::RENDER);
- OutputBuffer buffer(128); // this should be sufficiently large
- MessageRenderer renderer(buffer);
+ MessageRenderer renderer;
while (getline(input, line), !input.eof()) {
++linenum;
if (input.bad() || input.fail()) {
@@ -99,9 +98,9 @@ loadQueryData(istream& input, BenchQueries& queries, const RRClass& qclass,
renderer.clear();
query_message.toWire(renderer);
vector<unsigned char> query_data(
- static_cast<const unsigned char*>(buffer.getData()),
- static_cast<const unsigned char*>(buffer.getData()) +
- buffer.getLength());
+ static_cast<const unsigned char*>(renderer.getData()),
+ static_cast<const unsigned char*>(renderer.getData()) +
+ renderer.getLength());
queries.push_back(query_data);
} catch (const Exception&) {
if (strict) {
diff --git a/src/lib/bench/example/.gitignore b/src/lib/bench/example/.gitignore
new file mode 100644
index 0000000..eb3877d
--- /dev/null
+++ b/src/lib/bench/example/.gitignore
@@ -0,0 +1 @@
+/search_bench
diff --git a/src/lib/bench/tests/.gitignore b/src/lib/bench/tests/.gitignore
new file mode 100644
index 0000000..d6d1ec8
--- /dev/null
+++ b/src/lib/bench/tests/.gitignore
@@ -0,0 +1 @@
+/run_unittests
diff --git a/src/lib/bench/tests/benchmark_unittest.cc b/src/lib/bench/tests/benchmark_unittest.cc
index 7bb8a60..9b476cd 100644
--- a/src/lib/bench/tests/benchmark_unittest.cc
+++ b/src/lib/bench/tests/benchmark_unittest.cc
@@ -70,9 +70,9 @@ TEST(BenchMarkTest, run) {
const int sleep_time = 50000; // will sleep for 50ms
const struct timespec sleep_timespec = { 0, sleep_time * 1000 };
// we cannot expect particular accuracy on the measured duration, so
- // we'll include some conservative margin (25%) and perform range
+ // we'll include some conservative margin (50%) and perform range
// comparison below.
- const int duration_margin = 12500; // 12.5ms
+ const int duration_margin = 25000; // 25ms
const int ONE_MILLION = 1000000;
// Prerequisite check: since the tests in this case may depend on subtle
@@ -80,6 +80,8 @@ TEST(BenchMarkTest, run) {
// where sleeping doesn't work as this test expects. So we check the
// conditions before the tests, and if it fails skip the tests at the
// risk of overlooking possible bugs.
+ // We do this with a tighter margin than the checks themselves
+ const int duration_soft_margin = 12500; // 12.5ms
struct timeval check_begin, check_end;
gettimeofday(&check_begin, NULL);
nanosleep(&sleep_timespec, 0);
@@ -93,8 +95,8 @@ TEST(BenchMarkTest, run) {
--check_end.tv_sec;
}
if (check_end.tv_sec != 0 ||
- sleep_time - duration_margin > check_end.tv_usec ||
- sleep_time + duration_margin < check_end.tv_usec) {
+ sleep_time - duration_soft_margin > check_end.tv_usec ||
+ sleep_time + duration_soft_margin < check_end.tv_usec) {
cerr << "Prerequisite check failed. skipping test" << endl;
return;
}
diff --git a/src/lib/cache/.gitignore b/src/lib/cache/.gitignore
new file mode 100644
index 0000000..a33f3f0
--- /dev/null
+++ b/src/lib/cache/.gitignore
@@ -0,0 +1,2 @@
+/cache_messages.cc
+/cache_messages.h
diff --git a/src/lib/cache/local_zone_data.cc b/src/lib/cache/local_zone_data.cc
index 13d1d75..29ab2bf 100644
--- a/src/lib/cache/local_zone_data.cc
+++ b/src/lib/cache/local_zone_data.cc
@@ -43,7 +43,7 @@ LocalZoneData::lookup(const isc::dns::Name& name,
}
void
-LocalZoneData::update(const isc::dns::RRset& rrset) {
+LocalZoneData::update(const isc::dns::AbstractRRset& rrset) {
//TODO Do we really need to recreate the rrset again?
string key = genCacheEntryName(rrset.getName(), rrset.getType());
LOG_DEBUG(logger, DBG_TRACE_DATA, CACHE_LOCALZONE_UPDATE).arg(key);
diff --git a/src/lib/cache/local_zone_data.h b/src/lib/cache/local_zone_data.h
index 3015847..df77f40 100644
--- a/src/lib/cache/local_zone_data.h
+++ b/src/lib/cache/local_zone_data.h
@@ -47,7 +47,7 @@ public:
/// Otherwise, the existed one will be overwritten.
///
/// \param rrset The rrset to update
- void update(const isc::dns::RRset& rrset);
+ void update(const isc::dns::AbstractRRset& rrset);
private:
std::map<std::string, isc::dns::RRsetPtr> rrsets_map_; // RRsets of the zone
diff --git a/src/lib/cache/rrset_cache.cc b/src/lib/cache/rrset_cache.cc
index 1a5fd48..bb4d339 100644
--- a/src/lib/cache/rrset_cache.cc
+++ b/src/lib/cache/rrset_cache.cc
@@ -70,7 +70,7 @@ RRsetCache::lookup(const isc::dns::Name& qname,
}
RRsetEntryPtr
-RRsetCache::update(const isc::dns::RRset& rrset,
+RRsetCache::update(const isc::dns::AbstractRRset& rrset,
const RRsetTrustLevel& level)
{
LOG_DEBUG(logger, DBG_TRACE_DATA, CACHE_RRSET_UPDATE).arg(rrset.getName()).
diff --git a/src/lib/cache/rrset_cache.h b/src/lib/cache/rrset_cache.h
index 73f9b58..a4ea54e 100644
--- a/src/lib/cache/rrset_cache.h
+++ b/src/lib/cache/rrset_cache.h
@@ -73,7 +73,7 @@ public:
/// \param level trustworthiness of the rrset.
/// \return return the rrset entry in the cache, it may be the
/// new added rrset entry or existed one if it is not replaced.
- RRsetEntryPtr update(const isc::dns::RRset& rrset,
+ RRsetEntryPtr update(const isc::dns::AbstractRRset& rrset,
const RRsetTrustLevel& level);
/// \short Protected memebers, so they can be accessed by tests.
diff --git a/src/lib/cache/rrset_copy.cc b/src/lib/cache/rrset_copy.cc
index 05b139a..b395ce1 100644
--- a/src/lib/cache/rrset_copy.cc
+++ b/src/lib/cache/rrset_copy.cc
@@ -20,7 +20,7 @@ namespace isc {
namespace cache {
void
-rrsetCopy(const isc::dns::RRset& src, isc::dns::RRset& dst) {
+rrsetCopy(const isc::dns::AbstractRRset& src, isc::dns::AbstractRRset& dst) {
RdataIteratorPtr rdata_itor = src.getRdataIterator();
rdata_itor->first();
while(!rdata_itor->isLast()){
diff --git a/src/lib/cache/rrset_copy.h b/src/lib/cache/rrset_copy.h
index b6af8d6..e1dc489 100644
--- a/src/lib/cache/rrset_copy.h
+++ b/src/lib/cache/rrset_copy.h
@@ -33,7 +33,7 @@ namespace cache {
/// we have to do the copy.
void
-rrsetCopy(const isc::dns::RRset& src, isc::dns::RRset& dst);
+rrsetCopy(const isc::dns::AbstractRRset& src, isc::dns::AbstractRRset& dst);
} // namespace cache
} // namespace isc
diff --git a/src/lib/cache/rrset_entry.cc b/src/lib/cache/rrset_entry.cc
index c829956..359fd68 100644
--- a/src/lib/cache/rrset_entry.cc
+++ b/src/lib/cache/rrset_entry.cc
@@ -25,7 +25,8 @@ using namespace isc::dns;
namespace isc {
namespace cache {
-RRsetEntry::RRsetEntry(const isc::dns::RRset& rrset, const RRsetTrustLevel& level):
+RRsetEntry::RRsetEntry(const isc::dns::AbstractRRset& rrset,
+ const RRsetTrustLevel& level):
entry_name_(genCacheEntryName(rrset.getName(), rrset.getType())),
expire_time_(time(NULL) + rrset.getTTL().getValue()),
trust_level_(level),
diff --git a/src/lib/cache/rrset_entry.h b/src/lib/cache/rrset_entry.h
index 09cf79c..129300d 100644
--- a/src/lib/cache/rrset_entry.h
+++ b/src/lib/cache/rrset_entry.h
@@ -75,7 +75,8 @@ public:
/// \brief Constructor
/// \param rrset The RRset used to initialize the RRset entry.
/// \param level trustworthiness of the RRset.
- RRsetEntry(const isc::dns::RRset& rrset, const RRsetTrustLevel& level);
+ RRsetEntry(const isc::dns::AbstractRRset& rrset,
+ const RRsetTrustLevel& level);
/// The destructor.
~RRsetEntry() {}
diff --git a/src/lib/cache/tests/.gitignore b/src/lib/cache/tests/.gitignore
new file mode 100644
index 0000000..d6d1ec8
--- /dev/null
+++ b/src/lib/cache/tests/.gitignore
@@ -0,0 +1 @@
+/run_unittests
diff --git a/src/lib/cache/tests/Makefile.am b/src/lib/cache/tests/Makefile.am
index a215c56..b638f55 100644
--- a/src/lib/cache/tests/Makefile.am
+++ b/src/lib/cache/tests/Makefile.am
@@ -47,11 +47,6 @@ run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
run_unittests_LDADD = $(GTEST_LDADD)
-# NOTE: we may have to clean up this hack later (see the note in configure.ac)
-if NEED_LIBBOOST_THREAD
-run_unittests_LDADD += -lboost_thread
-endif
-
run_unittests_LDADD += $(top_builddir)/src/lib/cache/libcache.la
run_unittests_LDADD += $(top_builddir)/src/lib/log/liblog.la
run_unittests_LDADD += $(top_builddir)/src/lib/nsas/libnsas.la
diff --git a/src/lib/cc/.gitignore b/src/lib/cc/.gitignore
new file mode 100644
index 0000000..cb2800f
--- /dev/null
+++ b/src/lib/cc/.gitignore
@@ -0,0 +1,4 @@
+/cc_messages.cc
+/cc_messages.h
+/session_config.h
+/session_config.h.pre
diff --git a/src/lib/cc/tests/.gitignore b/src/lib/cc/tests/.gitignore
new file mode 100644
index 0000000..f10451c
--- /dev/null
+++ b/src/lib/cc/tests/.gitignore
@@ -0,0 +1,2 @@
+/run_unittests
+/session_unittests_config.h
diff --git a/src/lib/config/.gitignore b/src/lib/config/.gitignore
new file mode 100644
index 0000000..c7ec9d3
--- /dev/null
+++ b/src/lib/config/.gitignore
@@ -0,0 +1,2 @@
+/config_messages.cc
+/config_messages.h
diff --git a/src/lib/config/ccsession.cc b/src/lib/config/ccsession.cc
index ac85077..63fa4cd 100644
--- a/src/lib/config/ccsession.cc
+++ b/src/lib/config/ccsession.cc
@@ -489,6 +489,18 @@ ModuleCCSession::ModuleCCSession(
}
+ModuleCCSession::~ModuleCCSession() {
+ try {
+ sendStopping();
+ } catch (const std::exception& exc) {
+ LOG_ERROR(config_logger,
+ CONFIG_CCSESSION_STOPPING).arg(exc.what());
+ } catch (...) {
+ LOG_ERROR(config_logger,
+ CONFIG_CCSESSION_STOPPING_UNKNOWN);
+ }
+};
+
void
ModuleCCSession::start() {
if (started_) {
@@ -741,5 +753,16 @@ ModuleCCSession::updateRemoteConfig(const std::string& module_name,
}
}
+void
+ModuleCCSession::sendStopping() {
+ // Inform the configuration manager that this module is stopping
+ ConstElementPtr cmd(createCommand("stopping",
+ Element::fromJSON(
+ "{\"module_name\": \"" +
+ module_name_ + "\"}")));
+ // It's just an FYI, configmanager is not expected to respond.
+ session_.group_sendmsg(cmd, "ConfigManager");
+}
+
}
}
diff --git a/src/lib/config/ccsession.h b/src/lib/config/ccsession.h
index 80ef7c5..059968c 100644
--- a/src/lib/config/ccsession.h
+++ b/src/lib/config/ccsession.h
@@ -192,6 +192,14 @@ public:
bool handle_logging = true
);
+ ///
+ /// Destructor
+ ///
+ /// The destructor automatically calls sendStopping(), which sends
+ /// a message to the ConfigManager that this module is stopping
+ ///
+ virtual ~ModuleCCSession();
+
/// Start receiving new commands and configuration changes asynchronously.
///
/// This method must be called only once, and only when the ModuleCCSession
@@ -353,6 +361,7 @@ public:
private:
ModuleSpec readModuleSpecification(const std::string& filename);
void startCheck();
+ void sendStopping();
bool started_;
std::string module_name_;
diff --git a/src/lib/config/config_messages.mes b/src/lib/config/config_messages.mes
index c439edd..552256c 100644
--- a/src/lib/config/config_messages.mes
+++ b/src/lib/config/config_messages.mes
@@ -30,6 +30,18 @@ but will not send back an answer.
The most likely cause of this error is a programming error. Please raise
a bug report.
+% CONFIG_CCSESSION_STOPPING error sending stopping message: %1
+There was a problem when sending a message signaling that the module using
+this CCSession is stopping. This message is sent so that the rest of the
+system is aware that the module is no longer running. Apart from logging
+this message, the error itself is ignored, and the ModuleCCSession is
+still stopped. The specific exception message is printed.
+
+% CONFIG_CCSESSION_STOPPING_UNKNOWN unknown error sending stopping message
+Similar to CONFIG_CCSESSION_STOPPING, but in this case the exception that
+is seen is not a standard exception, and further information is unknown.
+This is a bug.
+
% CONFIG_GET_FAIL error getting configuration from cfgmgr: %1
The configuration manager returned an error when this module requested
the configuration. The full error message answer from the configuration
@@ -37,6 +49,11 @@ manager is appended to the log error. The most likely cause is that
the module is of a different (command specification) version than the
running configuration manager.
+% CONFIG_JSON_PARSE JSON parse error in %1: %2
+There was an error parsing the JSON file. The given file does not appear
+to be in valid JSON format. Please verify that the filename is correct
+and that the contents are valid JSON.
+
% CONFIG_LOG_EXPLICIT will use logging configuration for explicitly-named logger %1
This is a debug message. When processing the "loggers" part of the
configuration file, the configuration library found an entry for the named
@@ -62,11 +79,6 @@ wildcard entry (one containing the "*" character) that matches a logger
specification in the program. The logging configuration for the program
will be updated with the information.
-% CONFIG_JSON_PARSE JSON parse error in %1: %2
-There was an error parsing the JSON file. The given file does not appear
-to be in valid JSON format. Please verify that the filename is correct
-and that the contents are valid JSON.
-
% CONFIG_MOD_SPEC_FORMAT module specification error in %1: %2
The given file does not appear to be a valid specification file: details
are included in the message. Please verify that the filename is correct
diff --git a/src/lib/config/module_spec.cc b/src/lib/config/module_spec.cc
index bebe695..0defd6d 100644
--- a/src/lib/config/module_spec.cc
+++ b/src/lib/config/module_spec.cc
@@ -466,7 +466,6 @@ ModuleSpec::validateSpecList(ConstElementPtr spec, ConstElementPtr data,
const bool full, ElementPtr errors) const
{
bool validated = true;
- std::string cur_item_name;
BOOST_FOREACH(ConstElementPtr cur_spec_el, spec->listValue()) {
if (!validateSpec(cur_spec_el, data, full, errors)) {
validated = false;
diff --git a/src/lib/config/tests/.gitignore b/src/lib/config/tests/.gitignore
new file mode 100644
index 0000000..abdfa8a
--- /dev/null
+++ b/src/lib/config/tests/.gitignore
@@ -0,0 +1,2 @@
+/data_def_unittests_config.h
+/run_unittests
diff --git a/src/lib/config/tests/ccsession_unittests.cc b/src/lib/config/tests/ccsession_unittests.cc
index 793fa30..abaff8e 100644
--- a/src/lib/config/tests/ccsession_unittests.cc
+++ b/src/lib/config/tests/ccsession_unittests.cc
@@ -26,6 +26,8 @@
#include <log/logger_name.h>
+#include <boost/scoped_ptr.hpp>
+
using namespace isc::data;
using namespace isc::config;
using namespace isc::cc;
@@ -190,6 +192,67 @@ TEST_F(CCSessionTest, session2) {
EXPECT_EQ(0, session.getMsgQueue()->size());
}
+TEST_F(CCSessionTest, session_close) {
+ // Test whether ModuleCCSession automatically sends a 'stopping'
+ // message when it is destroyed
+ ConstElementPtr msg;
+ std::string group, to;
+
+ EXPECT_FALSE(session.haveSubscription("Spec2", "*"));
+
+ boost::scoped_ptr<ModuleCCSession> mccs(new ModuleCCSession(
+ ccspecfile("spec2.spec"),
+ session, NULL, NULL,
+ true, false));
+ EXPECT_TRUE(session.haveSubscription("Spec2", "*"));
+ // The initial message is irrelevant for this test
+ // (see session2 test), drop it
+ session.getFirstMessage(group, to);
+ // Queue should now be empty
+ ASSERT_EQ(0, session.getMsgQueue()->size());
+ // Invoke the destructor
+ mccs.reset();
+ // Destructor should have caused a new message
+ ASSERT_EQ(1, session.getMsgQueue()->size());
+ msg = session.getFirstMessage(group, to);
+ EXPECT_EQ("{ \"command\": [ \"stopping\", "
+ "{ \"module_name\": \"Spec2\" } ] }", msg->str());
+ EXPECT_EQ("ConfigManager", group);
+ EXPECT_EQ("*", to);
+ EXPECT_EQ(0, session.getMsgQueue()->size());
+}
+
+TEST_F(CCSessionTest, session_close_exception) {
+ // Test whether an exception encountered during the destructor is
+ // handled correctly
+ ConstElementPtr msg;
+ std::string group, to;
+
+ EXPECT_FALSE(session.haveSubscription("Spec2", "*"));
+
+ boost::scoped_ptr<ModuleCCSession> mccs(new ModuleCCSession(
+ ccspecfile("spec2.spec"),
+ session, NULL, NULL,
+ true, false));
+ EXPECT_TRUE(session.haveSubscription("Spec2", "*"));
+ // The initial message is irrelevant for this test
+ // (see session2 test), drop it
+ session.getFirstMessage(group, to);
+ // Queue should now be empty
+ ASSERT_EQ(0, session.getMsgQueue()->size());
+
+ // Set fake session to throw an exception
+ session.setThrowOnSend(true);
+
+ // Invoke the destructor
+ mccs.reset();
+ // Destructor should not have caused a new message (since fakesession
+ // should have thrown an exception)
+ ASSERT_EQ(0, session.getMsgQueue()->size());
+ //EXPECT_EQ(0, session.getMsgQueue()->size());
+}
+
+
ConstElementPtr my_config_handler(ConstElementPtr new_config) {
if (new_config && new_config->contains("item1") &&
new_config->get("item1")->intValue() == 5) {
diff --git a/src/lib/config/tests/fake_session.cc b/src/lib/config/tests/fake_session.cc
index 2b216e7..177e629 100644
--- a/src/lib/config/tests/fake_session.cc
+++ b/src/lib/config/tests/fake_session.cc
@@ -72,7 +72,8 @@ FakeSession::FakeSession(isc::data::ElementPtr initial_messages,
messages_(initial_messages),
subscriptions_(subscriptions),
msg_queue_(msg_queue),
- started_(false)
+ started_(false),
+ throw_on_send_(false)
{
}
@@ -181,8 +182,9 @@ int
FakeSession::group_sendmsg(ConstElementPtr msg, std::string group,
std::string to, std::string)
{
- //cout << "[XX] client sends message: " << msg << endl;
- //cout << "[XX] to: " << group << " . " << instance << "." << to << endl;
+ if (throw_on_send_) {
+ isc_throw(Exception, "Throw on send is set in FakeSession");
+ }
addMessage(msg, group, to);
return (1);
}
@@ -261,6 +263,5 @@ FakeSession::haveSubscription(ConstElementPtr group, ConstElementPtr instance)
{
return (haveSubscription(group->stringValue(), instance->stringValue()));
}
-
}
}
diff --git a/src/lib/config/tests/fake_session.h b/src/lib/config/tests/fake_session.h
index 85e47d5..79ff174 100644
--- a/src/lib/config/tests/fake_session.h
+++ b/src/lib/config/tests/fake_session.h
@@ -87,6 +87,14 @@ public:
isc::data::ElementPtr getMessages() { return (messages_); }
isc::data::ElementPtr getMsgQueue() { return (msg_queue_); }
+ /// Throw exception on sendmsg()
+ ///
+ /// When set to true, and sendmsg() is later called, this
+ /// will throw isc::Exception
+ ///
+ /// \param value If true, enable throw. If false, disable it
+ void setThrowOnSend(bool value) { throw_on_send_ = value; }
+
private:
bool recvmsg(isc::data::ConstElementPtr& msg,
bool nonblock = true, int seq = -1);
@@ -98,6 +106,7 @@ private:
isc::data::ElementPtr subscriptions_;
isc::data::ElementPtr msg_queue_;
bool started_;
+ bool throw_on_send_;
};
} // namespace cc
} // namespace isc
diff --git a/src/lib/config/tests/testdata/.gitignore b/src/lib/config/tests/testdata/.gitignore
new file mode 100644
index 0000000..1c67281
--- /dev/null
+++ b/src/lib/config/tests/testdata/.gitignore
@@ -0,0 +1 @@
+/b10-config.db
diff --git a/src/lib/cryptolink/tests/.gitignore b/src/lib/cryptolink/tests/.gitignore
new file mode 100644
index 0000000..d6d1ec8
--- /dev/null
+++ b/src/lib/cryptolink/tests/.gitignore
@@ -0,0 +1 @@
+/run_unittests
diff --git a/src/lib/datasrc/.gitignore b/src/lib/datasrc/.gitignore
new file mode 100644
index 0000000..05c761e
--- /dev/null
+++ b/src/lib/datasrc/.gitignore
@@ -0,0 +1,4 @@
+/datasrc_messages.cc
+/datasrc_messages.h
+/datasrc_config.h
+/datasrc_config.h.pre
diff --git a/src/lib/datasrc/Makefile.am b/src/lib/datasrc/Makefile.am
index b6c314c..dde3a82 100644
--- a/src/lib/datasrc/Makefile.am
+++ b/src/lib/datasrc/Makefile.am
@@ -7,10 +7,10 @@ AM_CPPFLAGS += $(SQLITE_CFLAGS)
AM_CXXFLAGS = $(B10_CXXFLAGS)
-pkglibexecdir = $(libexecdir)/@PACKAGE@/backends
+pkglibdir = $(libexecdir)/@PACKAGE@/backends
datasrc_config.h: datasrc_config.h.pre
- $(SED) -e "s|@@PKGLIBEXECDIR@@|$(pkglibexecdir)|" datasrc_config.h.pre >$@
+ $(SED) -e "s|@@PKGLIBDIR@@|$(pkglibdir)|" datasrc_config.h.pre >$@
CLEANFILES = *.gcno *.gcda datasrc_messages.h datasrc_messages.cc
CLEANFILES += datasrc_config.h
@@ -21,9 +21,10 @@ libdatasrc_la_SOURCES += static_datasrc.h static_datasrc.cc
libdatasrc_la_SOURCES += sqlite3_datasrc.h sqlite3_datasrc.cc
libdatasrc_la_SOURCES += query.h query.cc
libdatasrc_la_SOURCES += cache.h cache.cc
+libdatasrc_la_SOURCES += rbnode_rrset.h
libdatasrc_la_SOURCES += rbtree.h
libdatasrc_la_SOURCES += zonetable.h zonetable.cc
-libdatasrc_la_SOURCES += zone.h
+libdatasrc_la_SOURCES += zone.h zone_finder_context.cc
libdatasrc_la_SOURCES += result.h
libdatasrc_la_SOURCES += logger.h logger.cc
libdatasrc_la_SOURCES += client.h iterator.h
@@ -31,9 +32,10 @@ libdatasrc_la_SOURCES += database.h database.cc
libdatasrc_la_SOURCES += factory.h factory.cc
nodist_libdatasrc_la_SOURCES = datasrc_messages.h datasrc_messages.cc
-pkglibexec_LTLIBRARIES = sqlite3_ds.la memory_ds.la
+pkglib_LTLIBRARIES = sqlite3_ds.la memory_ds.la
sqlite3_ds_la_SOURCES = sqlite3_accessor.h sqlite3_accessor.cc
+sqlite3_ds_la_SOURCES += sqlite3_accessor_link.cc
sqlite3_ds_la_LDFLAGS = -module
sqlite3_ds_la_LDFLAGS += -no-undefined -version-info 1:0:0
sqlite3_ds_la_LIBADD = $(top_builddir)/src/lib/exceptions/libexceptions.la
@@ -41,6 +43,7 @@ sqlite3_ds_la_LIBADD += libdatasrc.la
sqlite3_ds_la_LIBADD += $(SQLITE_LIBS)
memory_ds_la_SOURCES = memory_datasrc.h memory_datasrc.cc
+memory_ds_la_SOURCES += memory_datasrc_link.cc
memory_ds_la_LDFLAGS = -module
memory_ds_la_LIBADD = $(top_builddir)/src/lib/exceptions/libexceptions.la
memory_ds_la_LIBADD += libdatasrc.la
diff --git a/src/lib/datasrc/database.cc b/src/lib/datasrc/database.cc
index 1dc5359..4e2fb15 100644
--- a/src/lib/datasrc/database.cc
+++ b/src/lib/datasrc/database.cc
@@ -396,15 +396,18 @@ DatabaseClient::Finder::findNSECCover(const Name& name) {
return (ConstRRsetPtr());
}
-ZoneFinder::FindResult
+ZoneFinderContextPtr
DatabaseClient::Finder::findAll(const isc::dns::Name& name,
std::vector<isc::dns::ConstRRsetPtr>& target,
const FindOptions options)
{
- return (findInternal(name, RRType::ANY(), &target, options));
+ return (ZoneFinderContextPtr(new Context(*this, options,
+ findInternal(name, RRType::ANY(),
+ &target, options),
+ target)));
}
-ZoneFinder::FindResult
+ZoneFinderContextPtr
DatabaseClient::Finder::find(const isc::dns::Name& name,
const isc::dns::RRType& type,
const FindOptions options)
@@ -412,7 +415,9 @@ DatabaseClient::Finder::find(const isc::dns::Name& name,
if (type == RRType::ANY()) {
isc_throw(isc::Unexpected, "Use findAll to answer ANY");
}
- return (findInternal(name, type, NULL, options));
+ return (ZoneFinderContextPtr(new Context(*this, options,
+ findInternal(name, type,
+ NULL, options))));
}
DatabaseClient::Finder::DelegationSearchResult
@@ -573,7 +578,7 @@ DatabaseClient::Finder::findDelegationPoint(const isc::dns::Name& name,
// covering NSEC record.
//
// If none of the above applies in any level, the search fails with NXDOMAIN.
-ZoneFinder::FindResult
+ZoneFinder::ResultContext
DatabaseClient::Finder::findWildcardMatch(
const isc::dns::Name& name, const isc::dns::RRType& type,
const FindOptions options, const DelegationSearchResult& dresult,
@@ -616,8 +621,7 @@ DatabaseClient::Finder::findWildcardMatch(
DATASRC_DATABASE_WILDCARD_CANCEL_NS).
arg(accessor_->getDBName()).arg(wildcard).
arg(dresult.first_ns->getName());
- return (FindResult(DELEGATION, dresult.first_ns));
-
+ return (ResultContext(DELEGATION, dresult.first_ns));
} else if (!hasSubdomains(name.split(i - 1).toText())) {
// The wildcard match is the best one, find the final result
// at it. Note that wildcard should never be the zone origin.
@@ -630,7 +634,7 @@ DatabaseClient::Finder::findWildcardMatch(
DATASRC_DATABASE_WILDCARD_CANCEL_SUB).
arg(accessor_->getDBName()).arg(wildcard).
arg(name).arg(superdomain);
- return (FindResult(NXDOMAIN, ConstRRsetPtr()));
+ return (ResultContext(NXDOMAIN, ConstRRsetPtr()));
}
} else if (hasSubdomains(wildcard)) {
@@ -641,19 +645,20 @@ DatabaseClient::Finder::findWildcardMatch(
if ((options & FIND_DNSSEC) != 0) {
ConstRRsetPtr nsec = findNSECCover(Name(wildcard));
if (nsec) {
- return (FindResult(NXRRSET, nsec,
- RESULT_WILDCARD | RESULT_NSEC_SIGNED));
+ return (ResultContext(NXRRSET, nsec,
+ RESULT_WILDCARD |
+ RESULT_NSEC_SIGNED));
}
}
- return (FindResult(NXRRSET, ConstRRsetPtr(), RESULT_WILDCARD));
+ return (ResultContext(NXRRSET, ConstRRsetPtr(), RESULT_WILDCARD));
}
}
// Nothing found at any level.
- return (FindResult(NXDOMAIN, ConstRRsetPtr()));
+ return (ResultContext(NXDOMAIN, ConstRRsetPtr()));
}
-ZoneFinder::FindResult
+ZoneFinder::ResultContext
DatabaseClient::Finder::logAndCreateResult(
const Name& name, const string* wildname, const RRType& type,
ZoneFinder::Result code, ConstRRsetPtr rrset,
@@ -680,10 +685,10 @@ DatabaseClient::Finder::logAndCreateResult(
arg(getClass()).arg(*wildname);
}
}
- return (ZoneFinder::FindResult(code, rrset, flags));
+ return (ResultContext(code, rrset, flags));
}
-ZoneFinder::FindResult
+ZoneFinder::ResultContext
DatabaseClient::Finder::findOnNameResult(const Name& name,
const RRType& type,
const FindOptions options,
@@ -799,7 +804,7 @@ DatabaseClient::Finder::findOnNameResult(const Name& name,
DATASRC_DATABASE_FOUND_NXRRSET, flags));
}
-ZoneFinder::FindResult
+ZoneFinder::ResultContext
DatabaseClient::Finder::findNoNameResult(const Name& name, const RRType& type,
FindOptions options,
const DelegationSearchResult& dresult,
@@ -821,17 +826,17 @@ DatabaseClient::Finder::findNoNameResult(const Name& name, const RRType& type,
arg(accessor_->getDBName()).arg(name);
const ConstRRsetPtr nsec = dnssec_data ? findNSECCover(name) :
ConstRRsetPtr();
- return (FindResult(NXRRSET, nsec,
- nsec ? RESULT_NSEC_SIGNED : RESULT_DEFAULT));
+ return (ResultContext(NXRRSET, nsec,
+ nsec ? RESULT_NSEC_SIGNED : RESULT_DEFAULT));
} else if ((options & NO_WILDCARD) == 0) {
// It's not an empty non-terminal and wildcard matching is not
// disabled, so check for wildcards. If there is a wildcard match
// (i.e. all results except NXDOMAIN) return it; otherwise fall
// through to the NXDOMAIN case below.
- const ZoneFinder::FindResult wresult =
+ const ResultContext wcontext =
findWildcardMatch(name, type, options, dresult, target);
- if (wresult.code != NXDOMAIN) {
- return (wresult);
+ if (wcontext.code != NXDOMAIN) {
+ return (wcontext);
}
}
@@ -841,11 +846,11 @@ DatabaseClient::Finder::findNoNameResult(const Name& name, const RRType& type,
arg(accessor_->getDBName()).arg(name).arg(type).arg(getClass());
const ConstRRsetPtr nsec = dnssec_data ? findNSECCover(name) :
ConstRRsetPtr();
- return (FindResult(NXDOMAIN, nsec,
- nsec ? RESULT_NSEC_SIGNED : RESULT_DEFAULT));
+ return (ResultContext(NXDOMAIN, nsec,
+ nsec ? RESULT_NSEC_SIGNED : RESULT_DEFAULT));
}
-ZoneFinder::FindResult
+ZoneFinder::ResultContext
DatabaseClient::Finder::findInternal(const Name& name, const RRType& type,
std::vector<ConstRRsetPtr>* target,
const FindOptions options)
@@ -860,7 +865,7 @@ DatabaseClient::Finder::findInternal(const Name& name, const RRType& type,
name.compare(getOrigin()).getRelation();
if (reln != NameComparisonResult::SUBDOMAIN &&
reln != NameComparisonResult::EQUAL) {
- return (FindResult(NXDOMAIN, ConstRRsetPtr()));
+ return (ResultContext(NXDOMAIN, ConstRRsetPtr()));
}
// First, go through all superdomains from the origin down, searching for
@@ -877,7 +882,7 @@ DatabaseClient::Finder::findInternal(const Name& name, const RRType& type,
const DelegationSearchResult dresult = findDelegationPoint(name, options);
if (dresult.rrset) {
// In this case no special flags are needed.
- return (FindResult(dresult.code, dresult.rrset));
+ return (ResultContext(dresult.code, dresult.rrset));
}
// If there is no delegation, look for the exact match to the request
@@ -975,7 +980,7 @@ public:
// Find the SOA of the zone (may or may not succeed). Note that
// this must be done before starting the iteration context.
soa_ = DatabaseClient::Finder(accessor_, zone.second, zone_name).
- find(zone_name, RRType::SOA()).rrset;
+ find(zone_name, RRType::SOA())->rrset;
// Request the context
context_ = accessor_->getAllRecords(zone.second);
@@ -1120,8 +1125,8 @@ public:
virtual ZoneFinder& getFinder() { return (*finder_); }
- virtual void addRRset(const RRset& rrset);
- virtual void deleteRRset(const RRset& rrset);
+ virtual void addRRset(const AbstractRRset& rrset);
+ virtual void deleteRRset(const AbstractRRset& rrset);
virtual void commit();
private:
@@ -1148,14 +1153,15 @@ private:
// This is a set of validation checks commonly used for addRRset() and
// deleteRRset to minimize duplicate code logic and to make the main
// code concise.
- void validateAddOrDelete(const char* const op_str, const RRset& rrset,
+ void validateAddOrDelete(const char* const op_str,
+ const AbstractRRset& rrset,
DiffPhase prev_phase,
DiffPhase current_phase) const;
};
void
DatabaseUpdater::validateAddOrDelete(const char* const op_str,
- const RRset& rrset,
+ const AbstractRRset& rrset,
DiffPhase prev_phase,
DiffPhase current_phase) const
{
@@ -1193,7 +1199,7 @@ DatabaseUpdater::validateAddOrDelete(const char* const op_str,
}
void
-DatabaseUpdater::addRRset(const RRset& rrset) {
+DatabaseUpdater::addRRset(const AbstractRRset& rrset) {
validateAddOrDelete("add", rrset, DELETE, ADD);
// It's guaranteed rrset has at least one RDATA at this point.
@@ -1239,7 +1245,7 @@ DatabaseUpdater::addRRset(const RRset& rrset) {
}
void
-DatabaseUpdater::deleteRRset(const RRset& rrset) {
+DatabaseUpdater::deleteRRset(const AbstractRRset& rrset) {
// If this is the first operation, pretend we are starting a new delete
// sequence after adds. This will simplify the validation below.
if (diff_phase_ == NOT_STARTED) {
diff --git a/src/lib/datasrc/database.h b/src/lib/datasrc/database.h
index 4d58401..afd3efb 100644
--- a/src/lib/datasrc/database.h
+++ b/src/lib/datasrc/database.h
@@ -26,7 +26,7 @@
#include <datasrc/data_source.h>
#include <datasrc/client.h>
-#include <datasrc/client.h>
+#include <datasrc/zone.h>
#include <datasrc/logger.h>
#include <dns/name.h>
@@ -738,17 +738,19 @@ public:
/// \param type The RRType to find
/// \param options Options about how to search.
/// See ZoneFinder::FindOptions.
- virtual FindResult find(const isc::dns::Name& name,
- const isc::dns::RRType& type,
- const FindOptions options = FIND_DEFAULT);
+ virtual ZoneFinderContextPtr find(const isc::dns::Name& name,
+ const isc::dns::RRType& type,
+ const FindOptions options =
+ FIND_DEFAULT);
/// \brief Implementation of the ZoneFinder::findAll method.
///
/// In short, it is mostly the same thing as find, but it returns all
/// RRsets in the named node through the target parameter in successful
/// case. It acts the same in the unsuccessful one.
- virtual FindResult findAll(const isc::dns::Name& name,
- std::vector<isc::dns::ConstRRsetPtr>& target,
- const FindOptions options = FIND_DEFAULT);
+ virtual ZoneFinderContextPtr findAll(
+ const isc::dns::Name& name,
+ std::vector<isc::dns::ConstRRsetPtr>& target,
+ const FindOptions options = FIND_DEFAULT);
/// \brief Implementation of ZoneFinder::findPreviousName method.
virtual isc::dns::Name findPreviousName(const isc::dns::Name& query)
@@ -785,6 +787,7 @@ public:
FoundRRsets;
/// \brief Just shortcut for set of types
typedef std::set<dns::RRType> WantedTypes;
+
/// \brief Internal logit of find and findAll methods.
///
/// Most of their handling is in the "error" cases and delegations
@@ -794,10 +797,12 @@ public:
/// Parameters and behaviour is like of those combined together.
/// Unexpected parameters, like type != ANY and having the target, are
/// just that - unexpected and not checked.
- FindResult findInternal(const isc::dns::Name& name,
- const isc::dns::RRType& type,
- std::vector<isc::dns::ConstRRsetPtr>* target,
- const FindOptions options = FIND_DEFAULT);
+ ResultContext findInternal(const isc::dns::Name& name,
+ const isc::dns::RRType& type,
+ std::vector<isc::dns::ConstRRsetPtr>*
+ target,
+ const FindOptions options = FIND_DEFAULT);
+
/// \brief Searches database for RRsets of one domain.
///
/// This method scans RRs of single domain specified by name and
@@ -942,9 +947,10 @@ public:
/// success due to an exact match). Also returned if there
/// is no match is an indication as to whether there was an
/// NXDOMAIN or an NXRRSET.
- FindResult findWildcardMatch(
+ ResultContext findWildcardMatch(
const isc::dns::Name& name,
- const isc::dns::RRType& type, const FindOptions options,
+ const isc::dns::RRType& type,
+ const FindOptions options,
const DelegationSearchResult& dresult,
std::vector<isc::dns::ConstRRsetPtr>* target);
@@ -986,13 +992,14 @@ public:
/// the above 4 cases). The return value is intended to be
/// usable as a return value of the caller of this helper
/// method.
- FindResult findOnNameResult(const isc::dns::Name& name,
- const isc::dns::RRType& type,
- const FindOptions options,
- const bool is_origin,
- const FoundRRsets& found,
- const std::string* wildname,
- std::vector<isc::dns::ConstRRsetPtr>* target);
+ ResultContext findOnNameResult(const isc::dns::Name& name,
+ const isc::dns::RRType& type,
+ const FindOptions options,
+ const bool is_origin,
+ const FoundRRsets& found,
+ const std::string* wildname,
+ std::vector<isc::dns::ConstRRsetPtr>*
+ target);
/// \brief Handle no match for name
///
@@ -1023,12 +1030,12 @@ public:
/// indicating the match type (e.g. CNAME at the wildcard
/// match, no RRs of the requested type at the wildcard,
/// success due to an exact match).
- FindResult findNoNameResult(const isc::dns::Name& name,
- const isc::dns::RRType& type,
- FindOptions options,
- const DelegationSearchResult& dresult,
- std::vector<isc::dns::ConstRRsetPtr>*
- target);
+ ResultContext findNoNameResult(const isc::dns::Name& name,
+ const isc::dns::RRType& type,
+ FindOptions options,
+ const DelegationSearchResult& dresult,
+ std::vector<isc::dns::ConstRRsetPtr>*
+ target);
/// Logs condition and creates result
///
@@ -1051,13 +1058,13 @@ public:
///
/// \return FindResult object constructed from the code and rrset
/// arguments.
- FindResult logAndCreateResult(const isc::dns::Name& name,
- const std::string* wildname,
- const isc::dns::RRType& type,
- ZoneFinder::Result code,
- isc::dns::ConstRRsetPtr rrset,
- const isc::log::MessageID& log_id,
- FindResultFlags flags) const;
+ ResultContext logAndCreateResult(const isc::dns::Name& name,
+ const std::string* wildname,
+ const isc::dns::RRType& type,
+ ZoneFinder::Result code,
+ isc::dns::ConstRRsetPtr rrset,
+ const isc::log::MessageID& log_id,
+ FindResultFlags flags) const;
/// \brief Checks if something lives below this domain.
///
@@ -1150,3 +1157,7 @@ private:
}
#endif // __DATABASE_DATASRC_H
+
+// Local Variables:
+// mode: c++
+// End:
diff --git a/src/lib/datasrc/datasrc_config.h.pre.in b/src/lib/datasrc/datasrc_config.h.pre.in
index ff99601..9074df6 100644
--- a/src/lib/datasrc/datasrc_config.h.pre.in
+++ b/src/lib/datasrc/datasrc_config.h.pre.in
@@ -23,7 +23,7 @@ namespace datasrc {
/// such as memory_ds.so and sqlite3_ds.so are found. It is used by the
/// DataSourceClient loader if no absolute path is used and
/// B10_FROM_BUILD is not set in the environment.
-const char* const BACKEND_LIBRARY_PATH = "@@PKGLIBEXECDIR@@/";
+const char* const BACKEND_LIBRARY_PATH = "@@PKGLIBDIR@@/";
} // end namespace datasrc
} // end namespace isc
diff --git a/src/lib/datasrc/datasrc_messages.mes b/src/lib/datasrc/datasrc_messages.mes
index f05ff21..f4ff213 100644
--- a/src/lib/datasrc/datasrc_messages.mes
+++ b/src/lib/datasrc/datasrc_messages.mes
@@ -332,6 +332,30 @@ should be followed. The requested domain is an apex of some zone.
% DATASRC_MEM_FIND find '%1/%2'
Debug information. A search for the requested RRset is being started.
+% DATASRC_MEM_FINDNSEC3 finding NSEC3 for %1, mode %2
+Debug information. A search in an in-memory data source for NSEC3 that
+matches or covers the given name is being started.
+
+% DATASRC_MEM_FINDNSEC3_COVER found a covering NSEC3 for %1: %2
+Debug information. An NSEC3 that covers the given name is found and
+being returned. The found NSEC3 RRset is also displayed.
+
+% DATASRC_MEM_FINDNSEC3_MATCH found a matching NSEC3 for %1 at label count %2: %3
+Debug information. An NSEC3 that matches (a possibly superdomain of)
+the given name is found and being returned. When the shown label
+count is smaller than that of the given name, the matching NSEC3 is
+for a superdomain of the given name (see DATASRC_MEM_FINDNSEC3_TRYHASH).
+The found NSEC3 RRset is also displayed.
+
+% DATASRC_MEM_FINDNSEC3_TRYHASH looking for NSEC3 for %1 at label count %2 (hash %3)
+Debug information. In an attempt of finding an NSEC3 for the give name,
+(a possibly superdomain of) the name is hashed and searched for in the
+NSEC3 name space. When the shown label count is smaller than that of the
+shown name, the search tries the superdomain name that share the shown
+(higher) label count of the shown name (e.g., for
+www.example.com. with shown label count of 3, example.com. is being
+tried).
+
% DATASRC_MEM_FIND_ZONE looking for zone '%1'
Debug information. A zone object for this zone is being searched for in the
in-memory data source.
@@ -561,7 +585,7 @@ The underlying data source failed to answer the query for referral information.
1 means some error, 2 is not implemented. The data source should have logged
the specific error already.
-% DATASRC_QUERY_RRSIG unable to answer RRSIG query
+% DATASRC_QUERY_RRSIG unable to answer RRSIG query for %1
The server is unable to answer a direct query for RRSIG type, but was asked
to do so.
diff --git a/src/lib/datasrc/memory_datasrc.cc b/src/lib/datasrc/memory_datasrc.cc
index bfff5c1..33ab82f 100644
--- a/src/lib/datasrc/memory_datasrc.cc
+++ b/src/lib/datasrc/memory_datasrc.cc
@@ -17,10 +17,10 @@
#include <utility>
#include <cctype>
#include <cassert>
+
#include <boost/shared_ptr.hpp>
#include <boost/scoped_ptr.hpp>
#include <boost/bind.hpp>
-#include <boost/foreach.hpp>
#include <exceptions/exceptions.h>
@@ -33,17 +33,15 @@
#include <datasrc/memory_datasrc.h>
#include <datasrc/rbtree.h>
+#include <datasrc/rbnode_rrset.h>
#include <datasrc/logger.h>
#include <datasrc/iterator.h>
#include <datasrc/data_source.h>
#include <datasrc/factory.h>
-#include <cc/data.h>
-
using namespace std;
using namespace isc::dns;
using namespace isc::dns::rdata;
-using namespace isc::data;
using boost::scoped_ptr;
namespace isc {
@@ -169,6 +167,12 @@ struct InMemoryZoneFinder::InMemoryZoneFinderImpl {
}
}
+ // A helper predicate used in contextCheck() to check if a given domain
+ // name has a RRset of type different than NSEC.
+ static bool isNotNSEC(const DomainPair& element) {
+ return (element.second->getType() != RRType::NSEC());
+ }
+
/*
* Does some checks in context of the data that are already in the zone.
* Currently checks for forbidden combinations of RRsets in the same
@@ -176,24 +180,23 @@ struct InMemoryZoneFinder::InMemoryZoneFinderImpl {
*
* If such condition is found, it throws AddError.
*/
- void contextCheck(const ConstRRsetPtr& rrset,
- const DomainPtr& domain) const {
+ void contextCheck(const AbstractRRset& rrset, const Domain& domain) const {
// Ensure CNAME and other type of RR don't coexist for the same
- // owner name.
- if (rrset->getType() == RRType::CNAME()) {
- // TODO: this check will become incorrect when we support DNSSEC
- // (depending on how we support DNSSEC). We should revisit it
- // at that point.
- if (!domain->empty()) {
+ // 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()) {
+ if (find_if(domain.begin(), domain.end(), isNotNSEC)
+ != domain.end()) {
LOG_ERROR(logger, DATASRC_MEM_CNAME_TO_NONEMPTY).
- arg(rrset->getName());
+ arg(rrset.getName());
isc_throw(AddError, "CNAME can't be added with other data for "
- << rrset->getName());
+ << rrset.getName());
}
- } else if (domain->find(RRType::CNAME()) != domain->end()) {
- LOG_ERROR(logger, DATASRC_MEM_CNAME_COEXIST).arg(rrset->getName());
- isc_throw(AddError, "CNAME and " << rrset->getType() <<
- " can't coexist for " << rrset->getName());
+ } else if (rrset.getType() != RRType::NSEC() &&
+ domain.find(RRType::CNAME()) != domain.end()) {
+ LOG_ERROR(logger, DATASRC_MEM_CNAME_COEXIST).arg(rrset.getName());
+ isc_throw(AddError, "CNAME and " << rrset.getType() <<
+ " can't coexist for " << rrset.getName());
}
/*
@@ -201,17 +204,17 @@ struct InMemoryZoneFinder::InMemoryZoneFinderImpl {
* non-apex domains.
* RFC 2672 section 3 mentions that it is implied from it and RFC 2181
*/
- if (rrset->getName() != origin_ &&
+ if (rrset.getName() != origin_ &&
// Adding DNAME, NS already there
- ((rrset->getType() == RRType::DNAME() &&
- domain->find(RRType::NS()) != domain->end()) ||
+ ((rrset.getType() == RRType::DNAME() &&
+ domain.find(RRType::NS()) != domain.end()) ||
// Adding NS, DNAME already there
- (rrset->getType() == RRType::NS() &&
- domain->find(RRType::DNAME()) != domain->end())))
+ (rrset.getType() == RRType::NS() &&
+ domain.find(RRType::DNAME()) != domain.end())))
{
- LOG_ERROR(logger, DATASRC_MEM_DNAME_NS).arg(rrset->getName());
+ LOG_ERROR(logger, DATASRC_MEM_DNAME_NS).arg(rrset.getName());
isc_throw(AddError, "DNAME can't coexist with NS in non-apex "
- "domain " << rrset->getName());
+ "domain " << rrset.getName());
}
}
@@ -373,7 +376,7 @@ struct InMemoryZoneFinder::InMemoryZoneFinderImpl {
// Note: there's a slight chance of getting an exception.
// As noted in add(), we give up strong exception guarantee in such
// cases.
- boost::const_pointer_cast<RRset>(covered_rrset)->addRRsig(sig_rrset);
+ boost::const_pointer_cast<AbstractRRset>(covered_rrset)->addRRsig(sig_rrset);
return (result::SUCCESS);
}
@@ -413,14 +416,19 @@ struct InMemoryZoneFinder::InMemoryZoneFinderImpl {
* access is without the impl_-> and it will get inlined anyway.
*/
// Implementation of InMemoryZoneFinder::add
- result::Result add(const ConstRRsetPtr& rrset, ZoneData& zone_data) {
+ result::Result add(const ConstRRsetPtr& rawrrset, ZoneData& zone_data) {
// Sanitize input. This will cause an exception to be thrown
// if the input RRset is empty.
- addValidation(rrset);
+ addValidation(rawrrset);
// OK, can add the RRset.
LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_ADD_RRSET).
- arg(rrset->getName()).arg(rrset->getType()).arg(origin_);
+ arg(rawrrset->getName()).arg(rawrrset->getType()).arg(origin_);
+
+ // ... although instead of loading the RRset directly, we encapsulate
+ // it within an RBNodeRRset. This contains additional information that
+ // speeds up queries.
+ ConstRRsetPtr rrset(new internal::RBNodeRRset(rawrrset));
if (rrset->getType() == RRType::NSEC3()) {
return (addNSEC3(rrset, zone_data));
@@ -461,7 +469,7 @@ struct InMemoryZoneFinder::InMemoryZoneFinderImpl {
// break strong exception guarantee. At the moment we prefer
// code simplicity and don't bother to introduce complicated
// recovery code.
- contextCheck(rrset, domain);
+ contextCheck(*rrset, *domain);
// Try inserting the rrset there
if (domain->insert(DomainPair(rrset->getType(), rrset)).second) {
@@ -599,39 +607,52 @@ struct InMemoryZoneFinder::InMemoryZoneFinderImpl {
*
* If rename is false, it returns the one provided. If it is true, it
* creates a new rrset with the same data but with provided name.
+ * In addition, if DNSSEC records are required by the original caller of
+ * find(), it also creates expanded RRSIG based on the RRSIG of the
+ * wildcard RRset.
* It is designed for wildcard case, where we create the rrsets
* dynamically.
*/
- static ConstRRsetPtr prepareRRset(const Name& name, const ConstRRsetPtr&
- rrset, bool rename)
+ static ConstRRsetPtr prepareRRset(const Name& name,
+ const ConstRRsetPtr& rrset,
+ bool rename, FindOptions options)
{
if (rename) {
LOG_DEBUG(logger, DBG_TRACE_DETAILED, DATASRC_MEM_RENAME).
arg(rrset->getName()).arg(name);
- /*
- * We lose a signature here. But it would be wrong anyway, because
- * the name changed. This might turn out to be unimportant in
- * future, because wildcards will probably be handled somehow
- * by DNSSEC.
- */
RRsetPtr result(new RRset(name, rrset->getClass(),
- rrset->getType(), rrset->getTTL()));
+ rrset->getType(), rrset->getTTL()));
for (RdataIteratorPtr i(rrset->getRdataIterator()); !i->isLast();
- i->next()) {
+ i->next()) {
result->addRdata(i->getCurrent());
}
+ if ((options & FIND_DNSSEC) != 0) {
+ ConstRRsetPtr sig_rrset = rrset->getRRsig();
+ if (sig_rrset) {
+ RRsetPtr result_sig(new RRset(name, sig_rrset->getClass(),
+ RRType::RRSIG(),
+ sig_rrset->getTTL()));
+ for (RdataIteratorPtr i(sig_rrset->getRdataIterator());
+ !i->isLast();
+ i->next())
+ {
+ result_sig->addRdata(i->getCurrent());
+ }
+ result->addRRsig(result_sig);
+ }
+ }
return (result);
} else {
return (rrset);
}
}
- // Set up FindResult object as a return value of find(), taking into
+ // Set up FindContext object as a return value of find(), taking into
// account wildcard matches and DNSSEC information. We set the NSEC/NSEC3
// flag when applicable regardless of the find option; the caller would
// simply ignore these when they didn't request DNSSEC related results.
- FindResult createFindResult(Result code, ConstRRsetPtr rrset,
- bool wild) const
+ ResultContext createFindResult(Result code, ConstRRsetPtr rrset,
+ bool wild = false) const
{
FindResultFlags flags = RESULT_DEFAULT;
if (wild) {
@@ -641,13 +662,13 @@ struct InMemoryZoneFinder::InMemoryZoneFinderImpl {
zone_data_->nsec3_data_) {
flags = flags | RESULT_NSEC3_SIGNED;
}
- return (FindResult(code, rrset, flags));
+ return (ZoneFinder::ResultContext(code, rrset, flags));
}
// Implementation of InMemoryZoneFinder::find
- FindResult find(const Name& name, RRType type,
- std::vector<ConstRRsetPtr> *target,
- const FindOptions options) const
+ ZoneFinder::ResultContext find(const Name& name, RRType type,
+ std::vector<ConstRRsetPtr>* target,
+ const FindOptions options) const
{
LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_MEM_FIND).arg(name).
arg(type);
@@ -682,15 +703,16 @@ struct InMemoryZoneFinder::InMemoryZoneFinderImpl {
arg(state.rrset_->getName());
// We were traversing a DNAME node (and wanted to go
// lower below it), so return the DNAME
- return (FindResult(DNAME, prepareRRset(name, state.rrset_,
- false)));
+ return (createFindResult(DNAME,
+ prepareRRset(name, state.rrset_,
+ false, options)));
}
if (state.zonecut_node_ != NULL) {
LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_DELEG_FOUND).
arg(state.rrset_->getName());
- return (FindResult(DELEGATION,
- prepareRRset(name, state.rrset_,
- false)));
+ return (createFindResult(DELEGATION,
+ prepareRRset(name, state.rrset_,
+ false, options)));
}
// If the RBTree search stopped at a node for a super domain
@@ -700,7 +722,7 @@ struct InMemoryZoneFinder::InMemoryZoneFinderImpl {
NameComparisonResult::SUPERDOMAIN) {
LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_SUPER_STOP).
arg(name);
- return (createFindResult(NXRRSET, ConstRRsetPtr(), false));
+ return (createFindResult(NXRRSET, ConstRRsetPtr()));
}
/*
@@ -793,8 +815,9 @@ struct InMemoryZoneFinder::InMemoryZoneFinderImpl {
if (found != node->getData()->end()) {
LOG_DEBUG(logger, DBG_TRACE_DATA,
DATASRC_MEM_EXACT_DELEGATION).arg(name);
- return (FindResult(DELEGATION,
- prepareRRset(name, found->second, rename)));
+ return (createFindResult(DELEGATION,
+ prepareRRset(name, found->second,
+ rename, options)));
}
}
@@ -804,7 +827,8 @@ struct InMemoryZoneFinder::InMemoryZoneFinderImpl {
for (found = node->getData()->begin();
found != node->getData()->end(); ++found)
{
- target->push_back(prepareRRset(name, found->second, rename));
+ target->push_back(prepareRRset(name, found->second, rename,
+ options));
}
LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_ANY_SUCCESS).
arg(name);
@@ -818,15 +842,17 @@ struct InMemoryZoneFinder::InMemoryZoneFinderImpl {
arg(type);
return (createFindResult(SUCCESS, prepareRRset(name,
found->second,
- rename), rename));
+ rename, options),
+ rename));
} else {
// Next, try CNAME.
found = node->getData()->find(RRType::CNAME());
if (found != node->getData()->end()) {
LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_CNAME).arg(name);
return (createFindResult(CNAME,
- prepareRRset(name, found->second,
- rename), rename));
+ prepareRRset(name, found->second,
+ rename, options),
+ rename));
}
}
// No exact match or CNAME. Return NXRRSET.
@@ -859,73 +885,101 @@ InMemoryZoneFinder::getClass() const {
return (impl_->zone_class_);
}
-ZoneFinder::FindResult
+ZoneFinderContextPtr
InMemoryZoneFinder::find(const Name& name, const RRType& type,
- const FindOptions options)
+ const FindOptions options)
{
- return (impl_->find(name, type, NULL, options));
+ return (ZoneFinderContextPtr(
+ new Context(*this, options,
+ impl_->find(name, type, NULL, options))));
}
-ZoneFinder::FindResult
+ZoneFinderContextPtr
InMemoryZoneFinder::findAll(const Name& name,
std::vector<ConstRRsetPtr>& target,
const FindOptions options)
{
- return (impl_->find(name, RRType::ANY(), &target, options));
+ return (ZoneFinderContextPtr(
+ new Context(*this, options, impl_->find(name, RRType::ANY(),
+ &target, options),
+ target)));
}
ZoneFinder::FindNSEC3Result
-InMemoryZoneFinder::findNSEC3(const Name&, bool) {
- isc_throw(NotImplemented, "findNSEC3 is not yet implemented for in memory "
- "data source");
-}
+InMemoryZoneFinder::findNSEC3(const Name& name, bool recursive) {
+ LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_MEM_FINDNSEC3).arg(name).
+ arg(recursive ? "recursive" : "non-recursive");
-ZoneFinder::FindNSEC3Result
-InMemoryZoneFinder::findNSEC3Tmp(const Name& name, bool recursive) {
if (!impl_->zone_data_->nsec3_data_) {
- isc_throw(Unexpected, "findNSEC3 is called for non NSEC3 zone");
+ isc_throw(DataSourceError,
+ "findNSEC3 attempt for non NSEC3 signed zone: " <<
+ impl_->origin_ << "/" << impl_->zone_class_);
}
- if (recursive) {
- isc_throw(Unexpected, "recursive mode isn't expected in tests");
+ const NSEC3Map& map = impl_->zone_data_->nsec3_data_->map_;
+ if (map.empty()) {
+ isc_throw(DataSourceError,
+ "findNSEC3 attempt but zone has no NSEC3 RR: " <<
+ impl_->origin_ << "/" << impl_->zone_class_);
}
-
- // A temporary workaround for testing: convert the original name to
- // NSEC3-hashed name using hardcoded mapping.
- string hname_text;
- if (name == Name("example.org")) {
- hname_text = "0P9MHAVEQVM6T7VBL5LOP2U3T2RP3TOM";
- } else if (name == Name("www.example.org")) {
- hname_text = "2S9MHAVEQVM6T7VBL5LOP2U3T2RP3TOM";
- } else if (name == Name("xxx.example.org")) {
- hname_text = "Q09MHAVEQVM6T7VBL5LOP2U3T2RP3TOM";
- } else if (name == Name("yyy.example.org")) {
- hname_text = "0A9MHAVEQVM6T7VBL5LOP2U3T2RP3TOM";
- } else {
- isc_throw(Unexpected, "unexpected name for NSEC3 test: " << name);
+ const NameComparisonResult cmp_result = name.compare(impl_->origin_);
+ if (cmp_result.getRelation() != NameComparisonResult::EQUAL &&
+ cmp_result.getRelation() != NameComparisonResult::SUBDOMAIN) {
+ isc_throw(InvalidParameter, "findNSEC3 attempt for out-of-zone name: "
+ << name << ", zone: " << impl_->origin_ << "/"
+ << impl_->zone_class_);
}
- // Below we assume the map is not empty for simplicity.
- NSEC3Map::const_iterator found =
- impl_->zone_data_->nsec3_data_->map_.lower_bound(hname_text);
- if (found != impl_->zone_data_->nsec3_data_->map_.end() &&
- found->first == hname_text) {
- // exact match
- return (FindNSEC3Result(true, 2, found->second, ConstRRsetPtr()));
- } else if (found == impl_->zone_data_->nsec3_data_->map_.end() ||
- found == impl_->zone_data_->nsec3_data_->map_.begin()) {
- // the search key is "smaller" than the smallest or "larger" than
- // largest. In either case "previous" is the largest one.
- return (FindNSEC3Result(false, 2,
- impl_->zone_data_->nsec3_data_->map_.
- rbegin()->second, ConstRRsetPtr()));
- } else {
- // Otherwise, H(found_domain-1) < given_hash < H(found_domain)
- // The covering proof is the first one.
- return (FindNSEC3Result(false, 2, (--found)->second, ConstRRsetPtr()));
+ // Convenient shortcuts
+ const NSEC3Hash& nsec3hash = *impl_->zone_data_->nsec3_data_->hash_;
+ const unsigned int olabels = impl_->origin_.getLabelCount();
+ const unsigned int qlabels = name.getLabelCount();
+
+ ConstRRsetPtr covering_proof; // placeholder of the next closer proof
+ // Examine all names from the query name to the origin name, stripping
+ // the deepest label one by one, until we find a name that has a matching
+ // NSEC3 hash.
+ for (unsigned int labels = qlabels; labels >= olabels; --labels) {
+ const string hlabel = nsec3hash.calculate(
+ labels == qlabels ? name : name.split(qlabels - labels, labels));
+ NSEC3Map::const_iterator found = map.lower_bound(hlabel);
+ LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_MEM_FINDNSEC3_TRYHASH).
+ arg(name).arg(labels).arg(hlabel);
+
+ // If the given hash is larger than the largest stored hash or
+ // the first label doesn't match the target, identify the "previous"
+ // hash value and remember it as the candidate next closer proof.
+ if (found == map.end() || found->first != hlabel) {
+ // If the given hash is larger or smaller than everything,
+ // the covering proof is the NSEC3 that has the largest hash.
+ // Note that we know the map isn't empty, so rbegin() is
+ // safe.
+ if (found == map.end() || found == map.begin()) {
+ covering_proof = map.rbegin()->second;
+ } else {
+ // Otherwise, H(found_entry-1) < given_hash < H(found_entry).
+ // The covering proof is the first one (and it's valid
+ // because found is neither begin nor end)
+ covering_proof = (--found)->second;
+ }
+ if (!recursive) { // in non recursive mode, we are done.
+ LOG_DEBUG(logger, DBG_TRACE_BASIC,
+ DATASRC_MEM_FINDNSEC3_COVER).
+ arg(name).arg(*covering_proof);
+ return (FindNSEC3Result(false, labels, covering_proof,
+ ConstRRsetPtr()));
+ }
+ } else { // found an exact match.
+ LOG_DEBUG(logger, DBG_TRACE_BASIC,
+ DATASRC_MEM_FINDNSEC3_MATCH).arg(name).arg(labels).
+ arg(*found->second);
+ return (FindNSEC3Result(true, labels, found->second,
+ covering_proof));
+ }
}
- // We should have covered all cases.
- isc_throw(Unexpected, "Impossible NSEC3 search result for " << name);
+ isc_throw(DataSourceError, "recursive findNSEC3 mode didn't stop, likely "
+ "a broken NSEC3 zone: " << impl_->origin_ << "/"
+ << impl_->zone_class_);
}
result::Result
@@ -1169,147 +1223,5 @@ InMemoryClient::getJournalReader(const isc::dns::Name&, uint32_t,
"in memory data source");
}
-namespace {
-// convencience function to add an error message to a list of those
-// (TODO: move functions like these to some util lib?)
-void
-addError(ElementPtr errors, const std::string& error) {
- if (errors != ElementPtr() && errors->getType() == Element::list) {
- errors->add(Element::create(error));
- }
-}
-
-/// Check if the given element exists in the map, and if it is a string
-bool
-checkConfigElementString(ConstElementPtr config, const std::string& name,
- ElementPtr errors)
-{
- if (!config->contains(name)) {
- addError(errors,
- "Config for memory backend does not contain a '"
- +name+
- "' value");
- return false;
- } else if (!config->get(name) ||
- config->get(name)->getType() != Element::string) {
- addError(errors, "value of " + name +
- " in memory backend config is not a string");
- return false;
- } else {
- return true;
- }
-}
-
-bool
-checkZoneConfig(ConstElementPtr config, ElementPtr errors) {
- bool result = true;
- if (!config || config->getType() != Element::map) {
- addError(errors, "Elements in memory backend's zone list must be maps");
- result = false;
- } else {
- if (!checkConfigElementString(config, "origin", errors)) {
- result = false;
- }
- if (!checkConfigElementString(config, "file", errors)) {
- result = false;
- }
- // we could add some existence/readabilty/parsability checks here
- // if we want
- }
- return result;
-}
-
-bool
-checkConfig(ConstElementPtr config, ElementPtr errors) {
- /* Specific configuration is under discussion, right now this accepts
- * the 'old' configuration, see [TODO]
- * So for memory datasource, we get a structure like this:
- * { "type": string ("memory"),
- * "class": string ("IN"/"CH"/etc),
- * "zones": list
- * }
- * Zones list is a list of maps:
- * { "origin": string,
- * "file": string
- * }
- *
- * At this moment we cannot be completely sure of the contents of the
- * structure, so we have to do some more extensive tests than should
- * strictly be necessary (e.g. existence and type of elements)
- */
- bool result = true;
-
- if (!config || config->getType() != Element::map) {
- addError(errors, "Base config for memory backend must be a map");
- result = false;
- } else {
- if (!checkConfigElementString(config, "type", errors)) {
- result = false;
- } else {
- if (config->get("type")->stringValue() != "memory") {
- addError(errors,
- "Config for memory backend is not of type \"memory\"");
- result = false;
- }
- }
- if (!checkConfigElementString(config, "class", errors)) {
- result = false;
- } else {
- try {
- RRClass rrc(config->get("class")->stringValue());
- } catch (const isc::Exception& rrce) {
- addError(errors,
- "Error parsing class config for memory backend: " +
- std::string(rrce.what()));
- result = false;
- }
- }
- if (!config->contains("zones")) {
- addError(errors, "No 'zones' element in memory backend config");
- result = false;
- } else if (!config->get("zones") ||
- config->get("zones")->getType() != Element::list) {
- addError(errors, "'zones' element in memory backend config is not a list");
- result = false;
- } else {
- BOOST_FOREACH(ConstElementPtr zone_config,
- config->get("zones")->listValue()) {
- if (!checkZoneConfig(zone_config, errors)) {
- result = false;
- }
- }
- }
- }
-
- return (result);
- return true;
-}
-
-} // end anonymous namespace
-
-DataSourceClient *
-createInstance(isc::data::ConstElementPtr config, std::string& error) {
- ElementPtr errors(Element::createList());
- if (!checkConfig(config, errors)) {
- error = "Configuration error: " + errors->str();
- return (NULL);
- }
- try {
- return (new InMemoryClient());
- } catch (const std::exception& exc) {
- error = std::string("Error creating memory datasource: ") + exc.what();
- return (NULL);
- } catch (...) {
- error = std::string("Error creating memory datasource, "
- "unknown exception");
- return (NULL);
- }
-}
-
-void destroyInstance(DataSourceClient* instance) {
- delete instance;
-}
-
-
} // end of namespace datasrc
} // end of namespace isc
diff --git a/src/lib/datasrc/memory_datasrc.h b/src/lib/datasrc/memory_datasrc.h
index 85fa5cf..9ee6747 100644
--- a/src/lib/datasrc/memory_datasrc.h
+++ b/src/lib/datasrc/memory_datasrc.h
@@ -70,18 +70,20 @@ public:
/// See documentation in \c Zone.
///
/// It returns NULL pointer in case of NXDOMAIN and NXRRSET.
- virtual FindResult find(const isc::dns::Name& name,
- const isc::dns::RRType& type,
- const FindOptions options = FIND_DEFAULT);
+ virtual ZoneFinderContextPtr find(const isc::dns::Name& name,
+ const isc::dns::RRType& type,
+ const FindOptions options =
+ FIND_DEFAULT);
/// \brief Version of find that returns all types at once
///
/// It acts the same as find, just that when the correct node is found,
/// all the RRsets are filled into the target parameter instead of being
/// returned by the result.
- virtual FindResult findAll(const isc::dns::Name& name,
- std::vector<isc::dns::ConstRRsetPtr>& target,
- const FindOptions options = FIND_DEFAULT);
+ virtual ZoneFinderContextPtr findAll(
+ const isc::dns::Name& name,
+ std::vector<isc::dns::ConstRRsetPtr>& target,
+ const FindOptions options = FIND_DEFAULT);
/// Look for NSEC3 for proving (non)existence of given name.
///
@@ -89,16 +91,6 @@ public:
virtual FindNSEC3Result
findNSEC3(const isc::dns::Name& name, bool recursive);
- // A temporary fake version of findNSEC3 for tests
- //
- // This method intentionally has the same interface as findNSEC3 but
- // uses internally hardcoded hash values and offers a limited set
- // of functionality for the convenience of tests. This is a temporary
- // workaround until #1577 is completed. At that point this method
- // should be removed.
- FindNSEC3Result
- findNSEC3Tmp(const isc::dns::Name& name, bool recursive);
-
/// \brief Imelementation of the ZoneFinder::findPreviousName method
///
/// This one throws NotImplemented exception, as InMemory doesn't
diff --git a/src/lib/datasrc/memory_datasrc_link.cc b/src/lib/datasrc/memory_datasrc_link.cc
new file mode 100644
index 0000000..a0b4bf6
--- /dev/null
+++ b/src/lib/datasrc/memory_datasrc_link.cc
@@ -0,0 +1,173 @@
+// Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <cc/data.h>
+
+#include <dns/rrclass.h>
+
+#include <datasrc/client.h>
+#include <datasrc/memory_datasrc.h>
+
+#include <boost/foreach.hpp>
+
+#include <string>
+
+using namespace isc::dns;
+using namespace isc::data;
+
+namespace isc {
+namespace datasrc {
+
+namespace {
+// convencience function to add an error message to a list of those
+// (TODO: move functions like these to some util lib?)
+void
+addError(ElementPtr errors, const std::string& error) {
+ if (errors != ElementPtr() && errors->getType() == Element::list) {
+ errors->add(Element::create(error));
+ }
+}
+
+/// Check if the given element exists in the map, and if it is a string
+bool
+checkConfigElementString(ConstElementPtr config, const std::string& name,
+ ElementPtr errors)
+{
+ if (!config->contains(name)) {
+ addError(errors,
+ "Config for memory backend does not contain a '"
+ +name+
+ "' value");
+ return false;
+ } else if (!config->get(name) ||
+ config->get(name)->getType() != Element::string) {
+ addError(errors, "value of " + name +
+ " in memory backend config is not a string");
+ return false;
+ } else {
+ return true;
+ }
+}
+
+bool
+checkZoneConfig(ConstElementPtr config, ElementPtr errors) {
+ bool result = true;
+ if (!config || config->getType() != Element::map) {
+ addError(errors, "Elements in memory backend's zone list must be maps");
+ result = false;
+ } else {
+ if (!checkConfigElementString(config, "origin", errors)) {
+ result = false;
+ }
+ if (!checkConfigElementString(config, "file", errors)) {
+ result = false;
+ }
+ // we could add some existence/readabilty/parsability checks here
+ // if we want
+ }
+ return result;
+}
+
+bool
+checkConfig(ConstElementPtr config, ElementPtr errors) {
+ /* Specific configuration is under discussion, right now this accepts
+ * the 'old' configuration, see [TODO]
+ * So for memory datasource, we get a structure like this:
+ * { "type": string ("memory"),
+ * "class": string ("IN"/"CH"/etc),
+ * "zones": list
+ * }
+ * Zones list is a list of maps:
+ * { "origin": string,
+ * "file": string
+ * }
+ *
+ * At this moment we cannot be completely sure of the contents of the
+ * structure, so we have to do some more extensive tests than should
+ * strictly be necessary (e.g. existence and type of elements)
+ */
+ bool result = true;
+
+ if (!config || config->getType() != Element::map) {
+ addError(errors, "Base config for memory backend must be a map");
+ result = false;
+ } else {
+ if (!checkConfigElementString(config, "type", errors)) {
+ result = false;
+ } else {
+ if (config->get("type")->stringValue() != "memory") {
+ addError(errors,
+ "Config for memory backend is not of type \"memory\"");
+ result = false;
+ }
+ }
+ if (!checkConfigElementString(config, "class", errors)) {
+ result = false;
+ } else {
+ try {
+ RRClass rrc(config->get("class")->stringValue());
+ } catch (const isc::Exception& rrce) {
+ addError(errors,
+ "Error parsing class config for memory backend: " +
+ std::string(rrce.what()));
+ result = false;
+ }
+ }
+ if (!config->contains("zones")) {
+ addError(errors, "No 'zones' element in memory backend config");
+ result = false;
+ } else if (!config->get("zones") ||
+ config->get("zones")->getType() != Element::list) {
+ addError(errors, "'zones' element in memory backend config is not a list");
+ result = false;
+ } else {
+ BOOST_FOREACH(ConstElementPtr zone_config,
+ config->get("zones")->listValue()) {
+ if (!checkZoneConfig(zone_config, errors)) {
+ result = false;
+ }
+ }
+ }
+ }
+
+ return (result);
+}
+
+} // end unnamed namespace
+
+DataSourceClient *
+createInstance(isc::data::ConstElementPtr config, std::string& error) {
+ ElementPtr errors(Element::createList());
+ if (!checkConfig(config, errors)) {
+ error = "Configuration error: " + errors->str();
+ return (NULL);
+ }
+ try {
+ return (new isc::datasrc::InMemoryClient());
+ } catch (const std::exception& exc) {
+ error = std::string("Error creating memory datasource: ") + exc.what();
+ return (NULL);
+ } catch (...) {
+ error = std::string("Error creating memory datasource, "
+ "unknown exception");
+ return (NULL);
+ }
+}
+
+void destroyInstance(DataSourceClient* instance) {
+ delete instance;
+}
+
+} // end of namespace datasrc
+} // end of namespace isc
diff --git a/src/lib/datasrc/rbnode_rrset.h b/src/lib/datasrc/rbnode_rrset.h
new file mode 100644
index 0000000..968e7a8
--- /dev/null
+++ b/src/lib/datasrc/rbnode_rrset.h
@@ -0,0 +1,209 @@
+// 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 __RBNODE_RRSET_H
+#define __RBNODE_RRSET_H
+
+#include <dns/messagerenderer.h>
+#include <dns/name.h>
+#include <dns/rrclass.h>
+#include <dns/rrset.h>
+#include <dns/rrttl.h>
+#include <dns/rrtype.h>
+#include <util/buffer.h>
+
+#include <string>
+
+namespace isc {
+namespace datasrc {
+namespace internal {
+
+/// \brief Special RRset for optimizing memory datasource requirement
+///
+/// To speed up the performance of the in-memory data source, at load time
+/// associate relevant "additional section" data with each RRset in the
+/// data source.
+///
+/// This class, derived from AbstractRRset, holds a "const" pointer to the
+/// underlying RRset object. All calls to methods on the class are passed to
+/// the underlying object. However, there are some restrictions:
+///
+/// - Calls to methods that change attributes of the underlying RRset (such as
+/// TTL or Name) cause an exception to be thrown. The in-memory data source
+/// does not allow modification of these attributes. In theory, it is a bad
+/// practice in that it doesn't preserve the assumed behavior of the base
+/// class. In practice, however, it should be acceptable because this
+/// class is effectively hidden from applications and will only be given
+/// to them as a const pointer to the base class via find() variants.
+/// So the application cannot call non const methods anyway unless it
+/// intentionally breaks the constness.
+///
+/// - Calls that add the pointer to the associated RRSIG to the RRset are
+/// allowed (even though the pointer is to a "const" RRset). The reason here
+/// is that RRSIGs are added to the in-memory data source after the
+/// RBNodeRRset objects have been created. Thus there has to be the
+/// capability of modifying this information.
+///
+/// The class is not derived from RRset itself to simplify coding: part of the
+/// loading of the memory data source is handled in the BIND 10 "libdns++"
+/// code, which creates RRsets and passes them to the data source code. This
+/// does not have to be altered if encapsulation, rather than inheritance, is
+/// used.
+///
+/// \note This class is exposed in this separate header file so that test code
+/// can refer to its definition, and only for that purpose. Otherwise this is
+/// essentially a private class of the in-memory data source implementation,
+/// and an application shouldn't directly refer to this class.
+///
+// Note: non-Doxygen-documented methods are documented in the base class.
+
+class RBNodeRRset : public isc::dns::AbstractRRset {
+
+private:
+ // Note: The copy constructor and the assignment operator are intentionally
+ // defined as private as we would normally not duplicate a RBNodeRRset.
+ // (We use the "private" method instead of inheriting from
+ // boost::noncopyable so as to avoid multiple inheritance.)
+ RBNodeRRset(const RBNodeRRset& source);
+ RBNodeRRset& operator=(const RBNodeRRset& source);
+
+public:
+ /// \brief Usual Constructor
+ ///
+ /// Creates an RBNodeRRset from the pointer to the RRset passed to it.
+ ///
+ /// \param rrset Pointer to underlying RRset encapsulated by this object.
+ explicit RBNodeRRset(const isc::dns::ConstRRsetPtr& rrset) : rrset_(rrset)
+ {}
+
+ /// \brief Destructor
+ virtual ~RBNodeRRset() {}
+
+ // Getter and Setter Methods
+ //
+ // The getter methods pass the call through to the underlying RRset. The
+ // setter methods thrown an exception - this specialisation of the RRset
+ // object does not expect the underlying RRset to be modified.
+
+ virtual unsigned int getRdataCount() const {
+ return (rrset_->getRdataCount());
+ }
+
+ virtual const isc::dns::Name& getName() const {
+ return (rrset_->getName());
+ }
+
+ virtual const isc::dns::RRClass& getClass() const {
+ return (rrset_->getClass());
+ }
+
+ virtual const isc::dns::RRType& getType() const {
+ return (rrset_->getType());
+ }
+
+ virtual const isc::dns::RRTTL& getTTL() const {
+ return (rrset_->getTTL());
+ }
+
+ virtual void setName(const isc::dns::Name&) {
+ isc_throw(isc::NotImplemented, "RBNodeRRset::setName() not supported");
+ }
+
+ virtual void setTTL(const isc::dns::RRTTL&) {
+ isc_throw(isc::NotImplemented, "RBNodeRRset::setTTL() not supported");
+ }
+
+ virtual std::string toText() const {
+ return (rrset_->toText());
+ }
+
+ virtual unsigned int toWire(
+ isc::dns::AbstractMessageRenderer& renderer) const {
+ return (rrset_->toWire(renderer));
+ }
+
+ virtual unsigned int toWire(isc::util::OutputBuffer& buffer) const {
+ return (rrset_->toWire(buffer));
+ }
+
+ virtual void addRdata(isc::dns::rdata::ConstRdataPtr) {
+ isc_throw(isc::NotImplemented,
+ "RBNodeRRset::addRdata() not supported");
+ }
+
+ virtual void addRdata(const isc::dns::rdata::Rdata&) {
+ isc_throw(isc::NotImplemented,
+ "RBNodeRRset::addRdata() not supported");
+ }
+
+ virtual isc::dns::RdataIteratorPtr getRdataIterator() const {
+ return (rrset_->getRdataIterator());
+ }
+
+ virtual isc::dns::RRsetPtr getRRsig() const {
+ return (rrset_->getRRsig());
+ }
+
+ // With all the RRsig methods, we have the problem that we store the
+ // underlying RRset using a ConstRRsetPtr - a pointer to a "const" RRset -
+ // but we need to modify it by adding or removing an RRSIG. We overcome
+ // this by temporarily violating the "const" nature of the RRset to add the
+ // data.
+
+ virtual void addRRsig(const isc::dns::rdata::ConstRdataPtr& rdata) {
+ AbstractRRset* p = const_cast<AbstractRRset*>(rrset_.get());
+ p->addRRsig(rdata);
+ }
+
+ virtual void addRRsig(const isc::dns::rdata::RdataPtr& rdata) {
+ AbstractRRset* p = const_cast<AbstractRRset*>(rrset_.get());
+ p->addRRsig(rdata);
+ }
+
+ virtual void addRRsig(const AbstractRRset& sigs) {
+ AbstractRRset* p = const_cast<AbstractRRset*>(rrset_.get());
+ p->addRRsig(sigs);
+ }
+
+ virtual void addRRsig(const isc::dns::ConstRRsetPtr& sigs) {
+ AbstractRRset* p = const_cast<AbstractRRset*>(rrset_.get());
+ p->addRRsig(sigs);
+ }
+
+ virtual void addRRsig(const isc::dns::RRsetPtr& sigs) {
+ AbstractRRset* p = const_cast<AbstractRRset*>(rrset_.get());
+ p->addRRsig(sigs);
+ }
+
+ virtual void removeRRsig() {
+ AbstractRRset* p = const_cast<AbstractRRset*>(rrset_.get());
+ p->removeRRsig();
+ }
+
+ /// \brief Return underlying RRset pointer
+ ///
+ /// ... mainly for testing.
+ isc::dns::ConstRRsetPtr getUnderlyingRRset() const {
+ return (rrset_);
+ }
+
+private:
+ isc::dns::ConstRRsetPtr rrset_; ///< Underlying RRset
+};
+
+} // namespace internal
+} // namespace datasrc
+} // namespace isc
+
+#endif // __RBNODE_RRSET_H
diff --git a/src/lib/datasrc/sqlite3_accessor.cc b/src/lib/datasrc/sqlite3_accessor.cc
index fb2ffef..a0afb7f 100644
--- a/src/lib/datasrc/sqlite3_accessor.cc
+++ b/src/lib/datasrc/sqlite3_accessor.cc
@@ -17,8 +17,6 @@
#include <string>
#include <vector>
-#include <boost/foreach.hpp>
-
#include <datasrc/sqlite3_accessor.h>
#include <datasrc/logger.h>
#include <datasrc/data_source.h>
@@ -31,8 +29,6 @@ using namespace isc::data;
#define SQLITE_SCHEMA_VERSION 1
-#define CONFIG_ITEM_DATABASE_FILE "database_file"
-
namespace isc {
namespace datasrc {
@@ -1096,75 +1092,5 @@ SQLite3Accessor::findPreviousName(int zone_id, const std::string& rname)
return (result);
}
-namespace {
-void
-addError(ElementPtr errors, const std::string& error) {
- if (errors != ElementPtr() && errors->getType() == Element::list) {
- errors->add(Element::create(error));
- }
-}
-
-bool
-checkConfig(ConstElementPtr config, ElementPtr errors) {
- /* Specific configuration is under discussion, right now this accepts
- * the 'old' configuration, see header file
- */
- bool result = true;
-
- if (!config || config->getType() != Element::map) {
- addError(errors, "Base config for SQlite3 backend must be a map");
- result = false;
- } else {
- if (!config->contains(CONFIG_ITEM_DATABASE_FILE)) {
- addError(errors,
- "Config for SQlite3 backend does not contain a '"
- CONFIG_ITEM_DATABASE_FILE
- "' value");
- result = false;
- } else if (!config->get(CONFIG_ITEM_DATABASE_FILE) ||
- config->get(CONFIG_ITEM_DATABASE_FILE)->getType() !=
- Element::string) {
- addError(errors, "value of " CONFIG_ITEM_DATABASE_FILE
- " in SQLite3 backend is not a string");
- result = false;
- } else if (config->get(CONFIG_ITEM_DATABASE_FILE)->stringValue() ==
- "") {
- addError(errors, "value of " CONFIG_ITEM_DATABASE_FILE
- " in SQLite3 backend is empty");
- result = false;
- }
- }
-
- return (result);
-}
-
-} // end anonymous namespace
-
-DataSourceClient *
-createInstance(isc::data::ConstElementPtr config, std::string& error) {
- ElementPtr errors(Element::createList());
- if (!checkConfig(config, errors)) {
- error = "Configuration error: " + errors->str();
- return (NULL);
- }
- std::string dbfile = config->get(CONFIG_ITEM_DATABASE_FILE)->stringValue();
- try {
- boost::shared_ptr<DatabaseAccessor> sqlite3_accessor(
- new SQLite3Accessor(dbfile, "IN")); // XXX: avoid hardcode RR class
- return (new DatabaseClient(isc::dns::RRClass::IN(), sqlite3_accessor));
- } catch (const std::exception& exc) {
- error = std::string("Error creating sqlite3 datasource: ") + exc.what();
- return (NULL);
- } catch (...) {
- error = std::string("Error creating sqlite3 datasource, "
- "unknown exception");
- return (NULL);
- }
-}
-
-void destroyInstance(DataSourceClient* instance) {
- delete instance;
-}
-
} // end of namespace datasrc
} // end of namespace isc
diff --git a/src/lib/datasrc/sqlite3_accessor_link.cc b/src/lib/datasrc/sqlite3_accessor_link.cc
new file mode 100644
index 0000000..81ac6b5
--- /dev/null
+++ b/src/lib/datasrc/sqlite3_accessor_link.cc
@@ -0,0 +1,105 @@
+// Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <cc/data.h>
+
+#include <dns/rrclass.h>
+
+#include <datasrc/sqlite3_accessor.h>
+#include <datasrc/database.h>
+
+#include <string>
+
+using namespace std;
+using namespace isc::dns;
+using namespace isc::data;
+
+namespace isc {
+namespace datasrc {
+
+namespace {
+
+const char* const CONFIG_ITEM_DATABASE_FILE = "database_file";
+
+void
+addError(ElementPtr errors, const std::string& error) {
+ if (errors != ElementPtr() && errors->getType() == Element::list) {
+ errors->add(Element::create(error));
+ }
+}
+
+bool
+checkConfig(ConstElementPtr config, ElementPtr errors) {
+ /* Specific configuration is under discussion, right now this accepts
+ * the 'old' configuration, see header file
+ */
+ bool result = true;
+
+ if (!config || config->getType() != Element::map) {
+ addError(errors, "Base config for SQlite3 backend must be a map");
+ result = false;
+ } else {
+ if (!config->contains(CONFIG_ITEM_DATABASE_FILE)) {
+ addError(errors,
+ "Config for SQlite3 backend does not contain a '" +
+ string(CONFIG_ITEM_DATABASE_FILE) +
+ "' value");
+ result = false;
+ } else if (!config->get(CONFIG_ITEM_DATABASE_FILE) ||
+ config->get(CONFIG_ITEM_DATABASE_FILE)->getType() !=
+ Element::string) {
+ addError(errors, "value of " + string(CONFIG_ITEM_DATABASE_FILE) +
+ " in SQLite3 backend is not a string");
+ result = false;
+ } else if (config->get(CONFIG_ITEM_DATABASE_FILE)->stringValue() ==
+ "") {
+ addError(errors, "value of " + string(CONFIG_ITEM_DATABASE_FILE) +
+ " in SQLite3 backend is empty");
+ result = false;
+ }
+ }
+
+ return (result);
+}
+
+} // end unnamed namespace
+
+DataSourceClient *
+createInstance(isc::data::ConstElementPtr config, std::string& error) {
+ ElementPtr errors(Element::createList());
+ if (!checkConfig(config, errors)) {
+ error = "Configuration error: " + errors->str();
+ return (NULL);
+ }
+ std::string dbfile = config->get(CONFIG_ITEM_DATABASE_FILE)->stringValue();
+ try {
+ boost::shared_ptr<DatabaseAccessor> sqlite3_accessor(
+ new SQLite3Accessor(dbfile, "IN")); // XXX: avoid hardcode RR class
+ return (new DatabaseClient(isc::dns::RRClass::IN(), sqlite3_accessor));
+ } catch (const std::exception& exc) {
+ error = std::string("Error creating sqlite3 datasource: ") + exc.what();
+ return (NULL);
+ } catch (...) {
+ error = std::string("Error creating sqlite3 datasource, "
+ "unknown exception");
+ return (NULL);
+ }
+}
+
+void destroyInstance(DataSourceClient* instance) {
+ delete instance;
+}
+
+} // end of namespace datasrc
+} // end of namespace isc
diff --git a/src/lib/datasrc/sqlite3_datasrc.cc b/src/lib/datasrc/sqlite3_datasrc.cc
index 03b057c..7cd565d 100644
--- a/src/lib/datasrc/sqlite3_datasrc.cc
+++ b/src/lib/datasrc/sqlite3_datasrc.cc
@@ -76,6 +76,14 @@ const char* const SCHEMA_LIST[] = {
"ttl INTEGER NOT NULL, rdtype STRING NOT NULL COLLATE NOCASE, "
"rdata STRING NOT NULL)",
"CREATE INDEX nsec3_byhash ON nsec3 (hash)",
+ "CREATE TABLE diffs (id INTEGER PRIMARY KEY, "
+ "zone_id INTEGER NOT NULL, "
+ "version INTEGER NOT NULL, "
+ "operation INTEGER NOT NULL, "
+ "name STRING NOT NULL COLLATE NOCASE, "
+ "rrtype STRING NOT NULL COLLATE NOCASE, "
+ "ttl INTEGER NOT NULL, "
+ "rdata STRING NOT NULL)",
NULL
};
diff --git a/src/lib/datasrc/static_datasrc.cc b/src/lib/datasrc/static_datasrc.cc
index fd43e1c..1ce3966 100644
--- a/src/lib/datasrc/static_datasrc.cc
+++ b/src/lib/datasrc/static_datasrc.cc
@@ -73,9 +73,11 @@ StaticDataSrcImpl::StaticDataSrcImpl() :
authors->addRdata(generic::TXT("Dmitriy Volodin"));
authors->addRdata(generic::TXT("Evan Hunt"));
authors->addRdata(generic::TXT("Haidong Wang")); // Ocean
+ authors->addRdata(generic::TXT("Haikuo Zhang"));
authors->addRdata(generic::TXT("Han Feng"));
authors->addRdata(generic::TXT("Jelte Jansen"));
authors->addRdata(generic::TXT("Jeremy C. Reed"));
+ authors->addRdata(generic::TXT("Xie Jiagui")); // Kevin Tes
authors->addRdata(generic::TXT("Jin Jian"));
authors->addRdata(generic::TXT("JINMEI Tatuya"));
authors->addRdata(generic::TXT("Kazunori Fujiwara"));
diff --git a/src/lib/datasrc/tests/.gitignore b/src/lib/datasrc/tests/.gitignore
new file mode 100644
index 0000000..bae5d90
--- /dev/null
+++ b/src/lib/datasrc/tests/.gitignore
@@ -0,0 +1,4 @@
+/run_unittests
+/run_unittests_factory
+/run_unittests_memory
+/run_unittests_sqlite3
diff --git a/src/lib/datasrc/tests/Makefile.am b/src/lib/datasrc/tests/Makefile.am
index 69c7959..c8ffa58 100644
--- a/src/lib/datasrc/tests/Makefile.am
+++ b/src/lib/datasrc/tests/Makefile.am
@@ -5,6 +5,7 @@ AM_CPPFLAGS += -I$(top_builddir)/src/lib/dns -I$(top_srcdir)/src/lib/dns
AM_CPPFLAGS += $(BOOST_INCLUDES)
AM_CPPFLAGS += $(SQLITE_CFLAGS)
AM_CPPFLAGS += -DTEST_DATA_DIR=\"$(abs_srcdir)/testdata\"
+AM_CPPFLAGS += -DTEST_DATA_COMMONDIR=\"$(abs_top_srcdir)/src/lib/testutils/testdata\"
AM_CPPFLAGS += -DTEST_DATA_BUILDDIR=\"$(abs_builddir)/testdata\"
AM_CPPFLAGS += -DINSTALL_PROG=\"$(abs_top_srcdir)/install-sh\"
@@ -19,17 +20,13 @@ CLEANFILES = *.gcno *.gcda
TESTS =
noinst_PROGRAMS =
if HAVE_GTEST
-TESTS += run_unittests run_unittests_sqlite3 run_unittests_memory
+TESTS += run_unittests
-#
-# For each specific datasource, there is a separate binary that includes
-# the code itself (we can't unittest through the public API). These need
-# to be separate because the included code, by design, contains conflicting
-# symbols.
-# We also have a 'general' run_unittests with non-datasource-specific tests
-#
+# We have two sets of tests: the general tests and factory tests (see below
+# for the latter). They are separate binary files sharing some program files
+# and libraries.
-# First define the parts shared by all
+# First define the parts shared by both
common_sources = run_unittests.cc
common_sources += $(top_srcdir)/src/lib/dns/tests/unittest_util.h
common_sources += $(top_srcdir)/src/lib/dns/tests/unittest_util.cc
@@ -45,7 +42,6 @@ common_ldadd += $(top_builddir)/src/lib/cc/libcc.la
common_ldadd += $(top_builddir)/src/lib/testutils/libtestutils.la
common_ldadd += $(top_builddir)/src/lib/util/unittests/libutil_unittests.la
-
# The general tests
run_unittests_SOURCES = $(common_sources)
run_unittests_SOURCES += datasrc_unittest.cc
@@ -56,35 +52,23 @@ run_unittests_SOURCES += test_datasrc.h test_datasrc.cc
run_unittests_SOURCES += rbtree_unittest.cc
run_unittests_SOURCES += logger_unittest.cc
run_unittests_SOURCES += client_unittest.cc
+run_unittests_SOURCES += database_unittest.cc
+run_unittests_SOURCES += sqlite3_unittest.cc
+run_unittests_SOURCES += sqlite3_accessor_unittest.cc
+run_unittests_SOURCES += memory_datasrc_unittest.cc
+run_unittests_SOURCES += rbnode_rrset_unittest.cc
+run_unittests_SOURCES += zone_finder_context_unittest.cc
+
+# We need the actual module implementation in the tests (they are not part
+# of libdatasrc)
+run_unittests_SOURCES += $(top_srcdir)/src/lib/datasrc/sqlite3_accessor.cc
+run_unittests_SOURCES += $(top_srcdir)/src/lib/datasrc/memory_datasrc.cc
run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
run_unittests_LDADD = $(common_ldadd)
-
-# SQlite3 datasource tests
-run_unittests_sqlite3_SOURCES = $(common_sources)
-run_unittests_sqlite3_SOURCES += database_unittest.cc
-run_unittests_sqlite3_SOURCES += sqlite3_unittest.cc
-run_unittests_sqlite3_SOURCES += sqlite3_accessor_unittest.cc
-run_unittests_sqlite3_SOURCES += $(top_srcdir)/src/lib/datasrc/sqlite3_accessor.cc
-
-run_unittests_sqlite3_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
-run_unittests_sqlite3_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
-
-run_unittests_sqlite3_LDADD = $(common_ldadd)
-
-# In-memory datasource tests
-run_unittests_memory_SOURCES = $(common_sources)
-run_unittests_memory_SOURCES += memory_datasrc_unittest.cc
-run_unittests_memory_SOURCES += $(top_srcdir)/src/lib/datasrc/memory_datasrc.cc
-
-run_unittests_memory_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
-run_unittests_memory_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
-
-run_unittests_memory_LDADD = $(common_ldadd)
-
noinst_PROGRAMS+= $(TESTS)
# For the factory unit tests, we need to specify that we want
@@ -108,19 +92,22 @@ endif
endif
EXTRA_DIST = testdata/brokendb.sqlite3
+EXTRA_DIST += testdata/contexttest.zone
+EXTRA_DIST += testdata/diffs.sqlite3
+EXTRA_DIST += testdata/example2.com
+EXTRA_DIST += testdata/example2.com.sqlite3
EXTRA_DIST += testdata/example.com.signed
EXTRA_DIST += testdata/example.org
EXTRA_DIST += testdata/example.org.nsec3-signed
EXTRA_DIST += testdata/example.org.nsec3-signed-noparam
EXTRA_DIST += testdata/example.org.sqlite3
-EXTRA_DIST += testdata/example2.com
-EXTRA_DIST += testdata/example2.com.sqlite3
EXTRA_DIST += testdata/mkbrokendb.c
EXTRA_DIST += testdata/root.zone
+EXTRA_DIST += testdata/rrset_toWire1
+EXTRA_DIST += testdata/rrset_toWire2
+EXTRA_DIST += testdata/rwtest.sqlite3
EXTRA_DIST += testdata/sql1.example.com.signed
EXTRA_DIST += testdata/sql2.example.com.signed
EXTRA_DIST += testdata/test-root.sqlite3
EXTRA_DIST += testdata/test.sqlite3
EXTRA_DIST += testdata/test.sqlite3.nodiffs
-EXTRA_DIST += testdata/rwtest.sqlite3
-EXTRA_DIST += testdata/diffs.sqlite3
diff --git a/src/lib/datasrc/tests/database_unittest.cc b/src/lib/datasrc/tests/database_unittest.cc
index 408913a..758095b 100644
--- a/src/lib/datasrc/tests/database_unittest.cc
+++ b/src/lib/datasrc/tests/database_unittest.cc
@@ -1364,7 +1364,7 @@ TYPED_TEST(DatabaseClientTest, iterateThenUpdate) {
// Confirm at least it doesn't contain any SOA
EXPECT_EQ(ZoneFinder::NXDOMAIN,
- this->getFinder()->find(this->zname_, RRType::SOA()).code);
+ this->getFinder()->find(this->zname_, RRType::SOA())->code);
} catch (const DataSourceError&) {}
ConstRRsetPtr rrset;
@@ -1422,31 +1422,31 @@ doFindTest(ZoneFinder& finder,
const ZoneFinder::FindOptions options = ZoneFinder::FIND_DEFAULT)
{
SCOPED_TRACE("doFindTest " + name.toText() + " " + type.toText());
- const ZoneFinder::FindResult result = finder.find(name, type, options);
- ASSERT_EQ(expected_result, result.code) << name << " " << type;
+ ConstZoneFinderContextPtr result = finder.find(name, type, options);
+ ASSERT_EQ(expected_result, result->code) << name << " " << type;
EXPECT_EQ((expected_flags & ZoneFinder::RESULT_WILDCARD) != 0,
- result.isWildcard());
+ result->isWildcard());
EXPECT_EQ((expected_flags & ZoneFinder::RESULT_NSEC_SIGNED) != 0,
- result.isNSECSigned());
+ result->isNSECSigned());
EXPECT_EQ((expected_flags & ZoneFinder::RESULT_NSEC3_SIGNED) != 0,
- result.isNSEC3Signed());
- if (!expected_rdatas.empty() && result.rrset) {
- checkRRset(result.rrset, expected_name != Name(".") ? expected_name :
+ result->isNSEC3Signed());
+ if (!expected_rdatas.empty() && result->rrset) {
+ checkRRset(result->rrset, expected_name != Name(".") ? expected_name :
name, finder.getClass(), expected_type, expected_ttl,
expected_rdatas);
- if (!expected_sig_rdatas.empty() && result.rrset->getRRsig()) {
- checkRRset(result.rrset->getRRsig(), expected_name != Name(".") ?
+ if (!expected_sig_rdatas.empty() && result->rrset->getRRsig()) {
+ checkRRset(result->rrset->getRRsig(), expected_name != Name(".") ?
expected_name : name, finder.getClass(),
isc::dns::RRType::RRSIG(), expected_ttl,
expected_sig_rdatas);
} else if (expected_sig_rdatas.empty()) {
- EXPECT_EQ(isc::dns::RRsetPtr(), result.rrset->getRRsig());
+ EXPECT_EQ(isc::dns::RRsetPtr(), result->rrset->getRRsig());
} else {
ADD_FAILURE() << "Missing RRSIG";
}
} else if (expected_rdatas.empty()) {
- EXPECT_EQ(isc::dns::RRsetPtr(), result.rrset);
+ EXPECT_EQ(isc::dns::RRsetPtr(), result->rrset);
} else {
ADD_FAILURE() << "Missing result";
}
@@ -1464,11 +1464,11 @@ doFindAllTestResult(ZoneFinder& finder, const isc::dns::Name& name,
{
SCOPED_TRACE("All test for " + name.toText());
std::vector<ConstRRsetPtr> target;
- ZoneFinder::FindResult result(finder.findAll(name, target, options));
+ ConstZoneFinderContextPtr result(finder.findAll(name, target, options));
EXPECT_TRUE(target.empty());
- EXPECT_EQ(expected_result, result.code);
- EXPECT_EQ(expected_type, result.rrset->getType());
- RdataIteratorPtr it(result.rrset->getRdataIterator());
+ EXPECT_EQ(expected_result, result->code);
+ EXPECT_EQ(expected_type, result->rrset->getType());
+ RdataIteratorPtr it(result->rrset->getRdataIterator());
std::vector<std::string> rdata;
while (!it->isLast()) {
rdata.push_back(it->getCurrent().toText());
@@ -1482,7 +1482,7 @@ doFindAllTestResult(ZoneFinder& finder, const isc::dns::Name& name,
}
EXPECT_TRUE(expected_rdata == rdata);
EXPECT_EQ(expected_name == isc::dns::Name::ROOT_NAME() ? name :
- expected_name, result.rrset->getName());
+ expected_name, result->rrset->getName());
}
// When asking for an RRset where RRs somehow have different TTLs, it should
@@ -1787,30 +1787,30 @@ TYPED_TEST(DatabaseClientTest, findOutOfZone) {
doFindTest(*finder, Name("org"), this->qtype_, this->qtype_,
this->rrttl_, ZoneFinder::NXDOMAIN,
this->empty_rdatas_, this->empty_rdatas_);
- EXPECT_EQ(ZoneFinder::NXDOMAIN, finder->findAll(Name("org"), target).code);
+ EXPECT_EQ(ZoneFinder::NXDOMAIN, finder->findAll(Name("org"), target)->code);
// sharing a common ancestor
doFindTest(*finder, Name("noexample.org"), this->qtype_, this->qtype_,
this->rrttl_, ZoneFinder::NXDOMAIN,
this->empty_rdatas_, this->empty_rdatas_);
EXPECT_EQ(ZoneFinder::NXDOMAIN, finder->findAll(Name("noexample.org"),
- target).code);
+ target)->code);
// totally unrelated domain, smaller number of labels
doFindTest(*finder, Name("com"), this->qtype_, this->qtype_,
this->rrttl_, ZoneFinder::NXDOMAIN,
this->empty_rdatas_, this->empty_rdatas_);
- EXPECT_EQ(ZoneFinder::NXDOMAIN, finder->findAll(Name("com"), target).code);
+ EXPECT_EQ(ZoneFinder::NXDOMAIN, finder->findAll(Name("com"), target)->code);
// totally unrelated domain, same number of labels
doFindTest(*finder, Name("example.com"), this->qtype_, this->qtype_,
this->rrttl_, ZoneFinder::NXDOMAIN,
this->empty_rdatas_, this->empty_rdatas_);
EXPECT_EQ(ZoneFinder::NXDOMAIN, finder->findAll(Name("example.com"),
- target).code);
+ target)->code);
// totally unrelated domain, larger number of labels
doFindTest(*finder, Name("more.example.com"), this->qtype_, this->qtype_,
this->rrttl_, ZoneFinder::NXDOMAIN,
this->empty_rdatas_, this->empty_rdatas_);
EXPECT_EQ(ZoneFinder::NXDOMAIN, finder->findAll(Name("more.example.com"),
- target).code);
+ target)->code);
}
TYPED_TEST(DatabaseClientTest, findDelegation) {
@@ -2363,11 +2363,11 @@ TYPED_TEST(DatabaseClientTest, getAll) {
std::vector<ConstRRsetPtr> target;
EXPECT_EQ(ZoneFinder::NXDOMAIN,
finder->findAll(isc::dns::Name("nothere.example.org."),
- target).code);
+ target)->code);
EXPECT_TRUE(target.empty());
EXPECT_EQ(ZoneFinder::NXRRSET,
finder->findAll(isc::dns::Name("here.wild.example.org."),
- target).code);
+ target)->code);
this->expected_rdatas_.push_back("ns.delegation.example.org.");
this->expected_rdatas_.push_back("ns.example.com.");
doFindAllTestResult(*finder, isc::dns::Name("xx.delegation.example.org."),
@@ -2388,7 +2388,7 @@ TYPED_TEST(DatabaseClientTest, getAll) {
// It should get the data on success
EXPECT_EQ(ZoneFinder::SUCCESS,
finder->findAll(isc::dns::Name("www2.example.org."),
- target).code);
+ target)->code);
ASSERT_EQ(2, target.size());
size_t a_idx(target[1]->getType() == RRType::A());
EXPECT_EQ(RRType::A(), target[a_idx]->getType());
@@ -2412,13 +2412,13 @@ TYPED_TEST(DatabaseClientTest, getAll) {
// And on wildcard. Check the signatures as well.
target.clear();
- const ZoneFinder::FindResult result =
+ ConstZoneFinderContextPtr result =
finder->findAll(Name("a.wild.example.org"), target,
ZoneFinder::FIND_DNSSEC);
- EXPECT_EQ(ZoneFinder::SUCCESS, result.code);
- EXPECT_TRUE(result.isWildcard());
- EXPECT_TRUE(result.isNSECSigned());
- EXPECT_FALSE(result.isNSEC3Signed());
+ EXPECT_EQ(ZoneFinder::SUCCESS, result->code);
+ EXPECT_TRUE(result->isWildcard());
+ EXPECT_TRUE(result->isNSECSigned());
+ EXPECT_FALSE(result->isNSEC3Signed());
ASSERT_EQ(2, target.size());
a_idx = target[1]->getType() == RRType::A();
EXPECT_EQ(RRType::A(), target[a_idx]->getType());
@@ -2496,22 +2496,22 @@ TYPED_TEST(DatabaseClientTest, flushZone) {
// Before update, the name exists.
EXPECT_EQ(ZoneFinder::SUCCESS, finder->find(this->qname_,
- this->qtype_).code);
+ this->qtype_)->code);
// start update in the replace mode. the normal finder should still
// be able to see the record, but the updater's finder shouldn't.
this->updater_ = this->client_->getUpdater(this->zname_, true);
this->setUpdateAccessor();
EXPECT_EQ(ZoneFinder::SUCCESS,
- finder->find(this->qname_, this->qtype_).code);
+ finder->find(this->qname_, this->qtype_)->code);
EXPECT_EQ(ZoneFinder::NXDOMAIN,
this->updater_->getFinder().find(this->qname_,
- this->qtype_).code);
+ this->qtype_)->code);
// commit the update. now the normal finder shouldn't see it.
this->updater_->commit();
EXPECT_EQ(ZoneFinder::NXDOMAIN, finder->find(this->qname_,
- this->qtype_).code);
+ this->qtype_)->code);
// Check rollback wasn't accidentally performed.
EXPECT_FALSE(this->isRollbacked());
@@ -2522,13 +2522,13 @@ TYPED_TEST(DatabaseClientTest, updateCancel) {
ZoneFinderPtr finder = this->client_->findZone(this->zname_).zone_finder;
EXPECT_EQ(ZoneFinder::SUCCESS, finder->find(this->qname_,
- this->qtype_).code);
+ this->qtype_)->code);
this->updater_ = this->client_->getUpdater(this->zname_, true);
this->setUpdateAccessor();
EXPECT_EQ(ZoneFinder::NXDOMAIN,
this->updater_->getFinder().find(this->qname_,
- this->qtype_).code);
+ this->qtype_)->code);
// DB should not have been rolled back yet.
EXPECT_FALSE(this->isRollbacked());
this->updater_.reset(); // destruct without commit
@@ -2538,7 +2538,7 @@ TYPED_TEST(DatabaseClientTest, updateCancel) {
// isRollbacked())
EXPECT_TRUE(this->isRollbacked(true));
EXPECT_EQ(ZoneFinder::SUCCESS, finder->find(this->qname_,
- this->qtype_).code);
+ this->qtype_)->code);
}
TYPED_TEST(DatabaseClientTest, exceptionFromRollback) {
diff --git a/src/lib/datasrc/tests/datasrc_unittest.cc b/src/lib/datasrc/tests/datasrc_unittest.cc
index cd1a40c..36bed1d 100644
--- a/src/lib/datasrc/tests/datasrc_unittest.cc
+++ b/src/lib/datasrc/tests/datasrc_unittest.cc
@@ -58,7 +58,7 @@ ConstElementPtr SQLITE_DBFILE_EXAMPLE = Element::fromJSON(
class DataSrcTest : public ::testing::Test {
protected:
- DataSrcTest() : obuffer(0), renderer(obuffer), msg(Message::PARSE),
+ DataSrcTest() : msg(Message::PARSE),
opcodeval(Opcode::QUERY().getCode()), qid(0)
{
DataSrcPtr sql3_source = DataSrcPtr(new Sqlite3DataSrc);
@@ -76,7 +76,6 @@ protected:
HotCache cache;
MetaDataSrc meta_source;
- OutputBuffer obuffer;
MessageRenderer renderer;
Message msg;
const uint16_t opcodeval;
diff --git a/src/lib/datasrc/tests/memory_datasrc_unittest.cc b/src/lib/datasrc/tests/memory_datasrc_unittest.cc
index f10adac..9096a9e 100644
--- a/src/lib/datasrc/tests/memory_datasrc_unittest.cc
+++ b/src/lib/datasrc/tests/memory_datasrc_unittest.cc
@@ -22,6 +22,7 @@
#include <dns/masterload.h>
#include <dns/name.h>
+#include <dns/nsec3hash.h>
#include <dns/rdata.h>
#include <dns/rdataclass.h>
#include <dns/rrclass.h>
@@ -171,14 +172,23 @@ TEST_F(InMemoryClientTest, iterator) {
EXPECT_EQ(result::SUCCESS, zone->add(aRRsetA));
EXPECT_EQ(result::SUCCESS, zone->add(aRRsetAAAA));
EXPECT_EQ(result::SUCCESS, zone->add(subRRsetA));
- // Check it with full zone, one by one.
- // It should be in ascending order in case of InMemory data source
- // (isn't guaranteed in general)
+
+ // Check it with full zone.
+ vector<ConstRRsetPtr> expected_rrsets;
+ expected_rrsets.push_back(aRRsetA);
+ expected_rrsets.push_back(aRRsetAAAA);
+ expected_rrsets.push_back(subRRsetA);
+
iterator = memory_client.getIterator(Name("a"));
- EXPECT_EQ(aRRsetA, iterator->getNextRRset());
- EXPECT_EQ(aRRsetAAAA, iterator->getNextRRset());
- EXPECT_EQ(subRRsetA, iterator->getNextRRset());
- EXPECT_EQ(ConstRRsetPtr(), iterator->getNextRRset());
+ vector<ConstRRsetPtr> actual_rrsets;
+ ConstRRsetPtr actual;
+ while ((actual = iterator->getNextRRset()) != NULL) {
+ actual_rrsets.push_back(actual);
+ }
+
+ rrsetsCheck(expected_rrsets.begin(), expected_rrsets.end(),
+ actual_rrsets.begin(), actual_rrsets.end());
+
}
TEST_F(InMemoryClientTest, iterator_separate_rrs) {
@@ -275,6 +285,83 @@ setRRset(RRsetPtr rrset, vector<RRsetPtr*>::iterator& it) {
++it;
}
+ConstRRsetPtr
+textToRRset(const string& text_rrset, const RRClass& rrclass = RRClass::IN()) {
+ stringstream ss(text_rrset);
+ RRsetPtr rrset;
+ vector<RRsetPtr*> rrsets;
+ rrsets.push_back(&rrset);
+ masterLoad(ss, Name::ROOT_NAME(), rrclass,
+ boost::bind(setRRset, _1, rrsets.begin()));
+ return (rrset);
+}
+
+// Some faked NSEC3 hash values commonly used in tests and the faked NSEC3Hash
+// object.
+//
+// For apex (example.org)
+const char* const apex_hash = "0P9MHAVEQVM6T7VBL5LOP2U3T2RP3TOM";
+const char* const apex_hash_lower = "0p9mhaveqvm6t7vbl5lop2u3t2rp3tom";
+// For ns1.example.org
+const char* const ns1_hash = "2T7B4G4VSA5SMI47K61MV5BV1A22BOJR";
+// For w.example.org
+const char* const w_hash = "01UDEMVP1J2F7EG6JEBPS17VP3N8I58H";
+// For x.y.w.example.org (lower-cased)
+const char* const xyw_hash = "2vptu5timamqttgl4luu9kg21e0aor3s";
+// For zzz.example.org.
+const char* const zzz_hash = "R53BQ7CC2UVMUBFU5OCMM6PERS9TK9EN";
+
+// A simple faked NSEC3 hash calculator with a dedicated creator for it.
+//
+// This is used in some NSEC3-related tests below.
+class TestNSEC3HashCreator : public NSEC3HashCreator {
+ class TestNSEC3Hash : public NSEC3Hash {
+ private:
+ typedef map<Name, string> NSEC3HashMap;
+ typedef NSEC3HashMap::value_type NSEC3HashPair;
+ NSEC3HashMap map_;
+ public:
+ TestNSEC3Hash() {
+ // Build pre-defined hash
+ map_[Name("example.org")] = apex_hash;
+ map_[Name("www.example.org")] = "2S9MHAVEQVM6T7VBL5LOP2U3T2RP3TOM";
+ map_[Name("xxx.example.org")] = "Q09MHAVEQVM6T7VBL5LOP2U3T2RP3TOM";
+ map_[Name("yyy.example.org")] = "0A9MHAVEQVM6T7VBL5LOP2U3T2RP3TOM";
+ map_[Name("x.y.w.example.org")] =
+ "2VPTU5TIMAMQTTGL4LUU9KG21E0AOR3S";
+ map_[Name("y.w.example.org")] = "K8UDEMVP1J2F7EG6JEBPS17VP3N8I58H";
+ map_[Name("w.example.org")] = w_hash;
+ map_[Name("zzz.example.org")] = zzz_hash;
+ map_[Name("smallest.example.org")] =
+ "00000000000000000000000000000000";
+ map_[Name("largest.example.org")] =
+ "UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU";
+ }
+ virtual string calculate(const Name& name) const {
+ const NSEC3HashMap::const_iterator found = map_.find(name);
+ if (found != map_.end()) {
+ return (found->second);
+ }
+ isc_throw(isc::Unexpected, "unexpected name for NSEC3 test: "
+ << name);
+ }
+ virtual bool match(const generic::NSEC3PARAM&) const {
+ return (true);
+ }
+ virtual bool match(const generic::NSEC3&) const {
+ return (true);
+ }
+ };
+
+public:
+ virtual NSEC3Hash* create(const generic::NSEC3PARAM&) const {
+ return (new TestNSEC3Hash);
+ }
+ virtual NSEC3Hash* create(const generic::NSEC3&) const {
+ return (new TestNSEC3Hash);
+ }
+};
+
/// \brief Test fixture for the InMemoryZoneFinder class
class InMemoryZoneFinderTest : public ::testing::Test {
// A straightforward pair of textual RR(set) and a RRsetPtr variable
@@ -368,6 +455,12 @@ public:
masterLoad(zone_data_stream, Name::ROOT_NAME(), class_,
boost::bind(setRRset, _1, rrsets.begin()));
}
+
+ ~InMemoryZoneFinderTest() {
+ // Make sure we reset the hash creator to the default
+ setNSEC3HashCreator(NULL);
+ }
+
// Some data to test with
const RRClass class_;
const Name origin_;
@@ -414,6 +507,12 @@ public:
RRsetPtr rr_not_wild_another_;
RRsetPtr rr_nsec3_;
+ // A faked NSEC3 hash calculator for convenience.
+ // Tests that need to use the faked hashed values should call
+ // setNSEC3HashCreator() with a pointer to this variable at the beginning
+ // of the test (at least before adding any NSEC3/NSEC3PARAM RR).
+ TestNSEC3HashCreator nsec3_hash_creator_;
+
/**
* \brief Test one find query to the zone finder.
*
@@ -449,31 +548,38 @@ public:
if (zone_finder == NULL) {
zone_finder = &zone_finder_;
}
+ const ConstRRsetPtr answer_sig = answer ? answer->getRRsig() :
+ RRsetPtr(); // note we use the same type as of retval of getRRsig()
// The whole block is inside, because we need to check the result and
// we can't assign to FindResult
EXPECT_NO_THROW({
- ZoneFinder::FindResult find_result(zone_finder->find(
- name, rrtype, options));
+ ZoneFinderContextPtr find_result(zone_finder->find(
+ name, rrtype, options));
// Check it returns correct answers
- EXPECT_EQ(result, find_result.code);
+ EXPECT_EQ(result, find_result->code);
EXPECT_EQ((expected_flags & ZoneFinder::RESULT_WILDCARD) != 0,
- find_result.isWildcard());
+ find_result->isWildcard());
EXPECT_EQ((expected_flags & ZoneFinder::RESULT_NSEC_SIGNED)
- != 0, find_result.isNSECSigned());
+ != 0, find_result->isNSECSigned());
EXPECT_EQ((expected_flags & ZoneFinder::RESULT_NSEC3_SIGNED)
- != 0, find_result.isNSEC3Signed());
+ != 0, find_result->isNSEC3Signed());
if (check_answer) {
if (!answer) {
- ASSERT_FALSE(find_result.rrset);
+ ASSERT_FALSE(find_result->rrset);
} else {
- ASSERT_TRUE(find_result.rrset);
- rrsetCheck(answer, find_result.rrset);
+ ASSERT_TRUE(find_result->rrset);
+ rrsetCheck(answer, find_result->rrset);
+ if (answer_sig) {
+ ASSERT_TRUE(find_result->rrset->getRRsig());
+ rrsetCheck(answer_sig,
+ find_result->rrset->getRRsig());
+ }
}
} else if (check_wild_answer) {
ASSERT_NE(ConstRRsetPtr(), answer) <<
"Wrong test, don't check for wild names if you expect "
"empty answer";
- ASSERT_NE(ConstRRsetPtr(), find_result.rrset) <<
+ ASSERT_NE(ConstRRsetPtr(), find_result->rrset) <<
"No answer found";
// Build the expected answer using the given name and
// other parameter of the base wildcard RRset.
@@ -484,7 +590,23 @@ public:
for (; !expectedIt->isLast(); expectedIt->next()) {
wildanswer->addRdata(expectedIt->getCurrent());
}
- rrsetCheck(wildanswer, find_result.rrset);
+ rrsetCheck(wildanswer, find_result->rrset);
+
+ // Same for the RRSIG, if any.
+ if (answer_sig) {
+ ASSERT_TRUE(find_result->rrset->getRRsig());
+
+ RRsetPtr wildsig(new RRset(name,
+ answer_sig->getClass(),
+ RRType::RRSIG(),
+ answer_sig->getTTL()));
+ RdataIteratorPtr expectedIt(
+ answer_sig->getRdataIterator());
+ for (; !expectedIt->isLast(); expectedIt->next()) {
+ wildsig->addRdata(expectedIt->getCurrent());
+ }
+ rrsetCheck(wildsig, find_result->rrset->getRRsig());
+ }
}
});
}
@@ -504,36 +626,24 @@ public:
finder = &zone_finder_;
}
std::vector<ConstRRsetPtr> target;
- ZoneFinder::FindResult find_result(finder->findAll(name, target,
- options));
- EXPECT_EQ(result, find_result.code);
+ ZoneFinderContextPtr find_result(finder->findAll(name, target,
+ options));
+ EXPECT_EQ(result, find_result->code);
if (!rrset_result) {
- EXPECT_FALSE(find_result.rrset);
+ EXPECT_FALSE(find_result->rrset);
} else {
- ASSERT_TRUE(find_result.rrset);
- rrsetCheck(rrset_result, find_result.rrset);
+ ASSERT_TRUE(find_result->rrset);
+ rrsetCheck(rrset_result, find_result->rrset);
}
EXPECT_EQ((expected_flags & ZoneFinder::RESULT_WILDCARD) != 0,
- find_result.isWildcard());
+ find_result->isWildcard());
EXPECT_EQ((expected_flags & ZoneFinder::RESULT_NSEC_SIGNED)
- != 0, find_result.isNSECSigned());
+ != 0, find_result->isNSECSigned());
EXPECT_EQ((expected_flags & ZoneFinder::RESULT_NSEC3_SIGNED)
- != 0, find_result.isNSEC3Signed());
+ != 0, find_result->isNSEC3Signed());
rrsetsCheck(expected_rrsets.begin(), expected_rrsets.end(),
target.begin(), target.end());
}
-
- ConstRRsetPtr textToRRset(const string& text_rrset,
- const RRClass& rrclass = RRClass::IN()) const
- {
- stringstream ss(text_rrset);
- RRsetPtr rrset;
- vector<RRsetPtr*> rrsets;
- rrsets.push_back(&rrset);
- masterLoad(ss, Name::ROOT_NAME(), rrclass,
- boost::bind(setRRset, _1, rrsets.begin()));
- return (rrset);
- }
};
/**
@@ -593,6 +703,37 @@ TEST_F(InMemoryZoneFinderTest, addOtherThenCNAME) {
EXPECT_THROW(zone_finder_.add(rr_cname_), InMemoryZoneFinder::AddError);
}
+TEST_F(InMemoryZoneFinderTest, addCNAMEAndDNSSECRecords) {
+ // CNAME and RRSIG can coexist
+ EXPECT_EQ(SUCCESS, zone_finder_.add(rr_cname_));
+ EXPECT_EQ(SUCCESS, zone_finder_.add(
+ textToRRset("cname.example.org. 300 IN RRSIG CNAME 5 3 "
+ "3600 20000101000000 20000201000000 12345 "
+ "example.org. FAKEFAKEFAKE")));
+
+ // Same for NSEC
+ EXPECT_EQ(SUCCESS, zone_finder_.add(
+ textToRRset("cname.example.org. 300 IN NSEC "
+ "dname.example.org. CNAME RRSIG NSEC")));
+
+ // Same as above, but adding NSEC first.
+ EXPECT_EQ(SUCCESS, zone_finder_.add(
+ textToRRset("cname2.example.org. 300 IN NSEC "
+ "dname.example.org. CNAME RRSIG NSEC")));
+ EXPECT_EQ(SUCCESS, zone_finder_.add(
+ textToRRset("cname2.example.org. 300 IN CNAME c.example.")));
+
+ // If there's another type of RRset with NSEC, it should still fail.
+ EXPECT_EQ(SUCCESS, zone_finder_.add(
+ textToRRset("cname3.example.org. 300 IN A 192.0.2.1")));
+ EXPECT_EQ(SUCCESS, zone_finder_.add(
+ textToRRset("cname3.example.org. 300 IN NSEC "
+ "dname.example.org. CNAME RRSIG NSEC")));
+ EXPECT_THROW(zone_finder_.add(textToRRset("cname3.example.org. 300 "
+ "IN CNAME c.example.")),
+ InMemoryZoneFinder::AddError);
+}
+
TEST_F(InMemoryZoneFinderTest, findCNAME) {
// install CNAME RR
EXPECT_EQ(SUCCESS, zone_finder_.add(rr_cname_));
@@ -731,11 +872,13 @@ TEST_F(InMemoryZoneFinderTest, delegationWithDS) {
EXPECT_EQ(SUCCESS, zone_finder_.add(rr_child_ds_));
// Normal types of query should result in delegation, but DS query
- // should be considered in-zone.
+ // should be considered in-zone (but only exactly at the delegation point).
findTest(Name("child.example.org"), RRType::A(), ZoneFinder::DELEGATION,
true, rr_child_ns_);
findTest(Name("child.example.org"), RRType::DS(), ZoneFinder::SUCCESS,
true, rr_child_ds_);
+ findTest(Name("grand.child.example.org"), RRType::DS(),
+ ZoneFinder::DELEGATION, true, rr_child_ns_);
// There's nothing special for DS query at the zone apex. It would
// normally result in NXRRSET.
@@ -862,7 +1005,7 @@ TEST_F(InMemoryZoneFinderTest, find) {
findCheck();
}
-TEST_F(InMemoryZoneFinderTest, findNSEC3) {
+TEST_F(InMemoryZoneFinderTest, findNSEC3Signed) {
findCheck(ZoneFinder::RESULT_NSEC3_SIGNED);
}
@@ -968,6 +1111,24 @@ InMemoryZoneFinderTest::wildcardCheck(
* |
* *
*/
+
+ // If the zone is "signed" (detecting it by the NSEC/NSEC3 signed flags),
+ // add RRSIGs to the records.
+ ZoneFinder::FindOptions find_options = ZoneFinder::FIND_DEFAULT;
+ if ((expected_flags & ZoneFinder::RESULT_NSEC_SIGNED) != 0 ||
+ (expected_flags & ZoneFinder::RESULT_NSEC3_SIGNED) != 0) {
+ // Convenience shortcut. The RDATA is not really validatable, but
+ // it doesn't matter for our tests.
+ const char* const rrsig_common = "5 3 3600 "
+ "20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE";
+
+ find_options = find_options | ZoneFinder::FIND_DNSSEC;
+ rr_wild_->addRRsig(textToRRset("*.wild.example.org. 300 IN RRSIG A " +
+ string(rrsig_common)));
+ rr_cnamewild_->addRRsig(textToRRset("*.cnamewild.example.org. 300 IN "
+ "RRSIG CNAME " +
+ string(rrsig_common)));
+ }
EXPECT_EQ(SUCCESS, zone_finder_.add(rr_wild_));
EXPECT_EQ(SUCCESS, zone_finder_.add(rr_cnamewild_));
// If the zone is expected to be "signed" with NSEC3, add an NSEC3.
@@ -981,14 +1142,15 @@ InMemoryZoneFinderTest::wildcardCheck(
{
SCOPED_TRACE("Search at parent");
findTest(Name("wild.example.org"), RRType::A(), ZoneFinder::NXRRSET,
- true, ConstRRsetPtr(), expected_flags);
+ true, ConstRRsetPtr(), expected_flags, NULL, find_options);
}
// Search the original name of wildcard
{
SCOPED_TRACE("Search directly at *");
findTest(Name("*.wild.example.org"), RRType::A(), ZoneFinder::SUCCESS,
- true, rr_wild_);
+ true, rr_wild_, ZoneFinder::RESULT_DEFAULT, NULL,
+ find_options);
}
// Search "created" name.
{
@@ -996,11 +1158,12 @@ InMemoryZoneFinderTest::wildcardCheck(
findTest(Name("a.wild.example.org"), RRType::A(), ZoneFinder::SUCCESS,
false, rr_wild_,
ZoneFinder::RESULT_WILDCARD | expected_flags, NULL,
- ZoneFinder::FIND_DEFAULT, true);
+ find_options, true);
// Wildcard match, but no data
findTest(Name("a.wild.example.org"), RRType::AAAA(),
ZoneFinder::NXRRSET, true, ConstRRsetPtr(),
- ZoneFinder::RESULT_WILDCARD | expected_flags);
+ ZoneFinder::RESULT_WILDCARD | expected_flags, NULL,
+ find_options);
}
// Search name that has CNAME.
@@ -1009,7 +1172,7 @@ InMemoryZoneFinderTest::wildcardCheck(
findTest(Name("a.cnamewild.example.org"), RRType::A(),
ZoneFinder::CNAME, false, rr_cnamewild_,
ZoneFinder::RESULT_WILDCARD | expected_flags, NULL,
- ZoneFinder::FIND_DEFAULT, true);
+ find_options, true);
}
// Search another created name, this time little bit lower
@@ -1018,14 +1181,15 @@ InMemoryZoneFinderTest::wildcardCheck(
findTest(Name("a.b.wild.example.org"), RRType::A(),
ZoneFinder::SUCCESS, false, rr_wild_,
ZoneFinder::RESULT_WILDCARD | expected_flags, NULL,
- ZoneFinder::FIND_DEFAULT, true);
+ find_options, true);
}
EXPECT_EQ(SUCCESS, zone_finder_.add(rr_under_wild_));
{
SCOPED_TRACE("Search under non-wildcard");
findTest(Name("bar.foo.wild.example.org"), RRType::A(),
- ZoneFinder::NXDOMAIN, true, ConstRRsetPtr(), expected_flags);
+ ZoneFinder::NXDOMAIN, true, ConstRRsetPtr(), expected_flags,
+ NULL, find_options);
}
}
@@ -1388,37 +1552,35 @@ TEST_F(InMemoryZoneFinderTest, addRRsig) {
// that covers the first RRset
zone_finder_.add(rr_a_);
zone_finder_.add(textToRRset(rrsig_a_txt));
- ZoneFinder::FindResult result = zone_finder_.find(origin_, RRType::A(),
- ZoneFinder::FIND_DNSSEC);
- EXPECT_EQ(ZoneFinder::SUCCESS, result.code);
- ASSERT_TRUE(result.rrset);
- ASSERT_TRUE(result.rrset->getRRsig());
- actual_rrsets_.push_back(result.rrset->getRRsig());
+ ZoneFinderContextPtr result = zone_finder_.find(origin_, RRType::A(),
+ ZoneFinder::FIND_DNSSEC);
+ EXPECT_EQ(ZoneFinder::SUCCESS, result->code);
+ ASSERT_TRUE(result->rrset);
+ ASSERT_TRUE(result->rrset->getRRsig());
+ actual_rrsets_.push_back(result->rrset->getRRsig());
rrsetsCheck(rrsig_a_txt, actual_rrsets_.begin(), actual_rrsets_.end());
// Confirm a separate RRISG for a different type can be added
actual_rrsets_.clear();
zone_finder_.add(rr_ns_);
zone_finder_.add(textToRRset(rrsig_ns_txt));
- ZoneFinder::FindResult result2 =
- zone_finder_.find(origin_, RRType::NS(), ZoneFinder::FIND_DNSSEC);
- EXPECT_EQ(ZoneFinder::SUCCESS, result2.code);
- ASSERT_TRUE(result2.rrset);
- ASSERT_TRUE(result2.rrset->getRRsig());
- actual_rrsets_.push_back(result2.rrset->getRRsig());
+ result = zone_finder_.find(origin_, RRType::NS(), ZoneFinder::FIND_DNSSEC);
+ EXPECT_EQ(ZoneFinder::SUCCESS, result->code);
+ ASSERT_TRUE(result->rrset);
+ ASSERT_TRUE(result->rrset->getRRsig());
+ actual_rrsets_.push_back(result->rrset->getRRsig());
rrsetsCheck(rrsig_ns_txt, actual_rrsets_.begin(), actual_rrsets_.end());
// Check a case with multiple RRSIGs
actual_rrsets_.clear();
zone_finder_.add(rr_ns_aaaa_);
zone_finder_.add(textToRRset(rrsig_aaaa_txt));
- ZoneFinder::FindResult result3 =
- zone_finder_.find(Name("ns.example.org"), RRType::AAAA(),
- ZoneFinder::FIND_DNSSEC);
- EXPECT_EQ(ZoneFinder::SUCCESS, result3.code);
- ASSERT_TRUE(result3.rrset);
- ASSERT_TRUE(result3.rrset->getRRsig());
- actual_rrsets_.push_back(result3.rrset->getRRsig());
+ result = zone_finder_.find(Name("ns.example.org"), RRType::AAAA(),
+ ZoneFinder::FIND_DNSSEC);
+ EXPECT_EQ(ZoneFinder::SUCCESS, result->code);
+ ASSERT_TRUE(result->rrset);
+ ASSERT_TRUE(result->rrset->getRRsig());
+ actual_rrsets_.push_back(result->rrset->getRRsig());
rrsetsCheck(rrsig_aaaa_txt, actual_rrsets_.begin(), actual_rrsets_.end());
}
@@ -1485,50 +1647,55 @@ const char* const nsec3_common = " 300 IN NSEC3 1 1 12 aabbccdd "
const char* const nsec3_rrsig_common = " 300 IN RRSIG NSEC3 5 3 3600 "
"20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE";
-// For apex (example.org)
-const char* const apex_hash = "0P9MHAVEQVM6T7VBL5LOP2U3T2RP3TOM";
-const char* const apex_hash_lower = "0p9mhaveqvm6t7vbl5lop2u3t2rp3tom";
-// For ns1.example.org
-const char* const ns1_hash = "2T7B4G4VSA5SMI47K61MV5BV1A22BOJR";
-// For x.y.w.example.org (lower-cased)
-const char* const xrw_hash = "2vptu5timamqttgl4luu9kg21e0aor3s";
-
void
-nsec3Check(bool expected_matched, const string& expected_rrsets_txt,
- const ZoneFinder::FindNSEC3Result& result,
- bool expected_sig = false)
+findNSEC3Check(bool expected_matched, uint8_t expected_labels,
+ const string& expected_closest,
+ const string& expected_next,
+ const ZoneFinder::FindNSEC3Result& result,
+ bool expected_sig = false)
{
- vector<ConstRRsetPtr> actual_rrsets;
EXPECT_EQ(expected_matched, result.matched);
+ // Convert to int so the error messages would be more readable:
+ EXPECT_EQ(static_cast<int>(expected_labels),
+ static_cast<int>(result.closest_labels));
+
+ vector<ConstRRsetPtr> actual_rrsets;
ASSERT_TRUE(result.closest_proof);
- if (expected_sig) {
- ASSERT_TRUE(result.closest_proof->getRRsig());
- }
actual_rrsets.push_back(result.closest_proof);
if (expected_sig) {
actual_rrsets.push_back(result.closest_proof->getRRsig());
}
- rrsetsCheck(expected_rrsets_txt, actual_rrsets.begin(),
+ rrsetsCheck(expected_closest, actual_rrsets.begin(),
actual_rrsets.end());
-}
-// In the following tests we use a temporary faked version of findNSEC3
-// as the real version isn't implemented yet (it's a task for #1577).
-// When #1577 is done the tests should be updated using the real version.
-// If we can use fake hash calculator (see #1575), we should be able to
-// just replace findNSEC3Tmp with findNSEC3.
+ actual_rrsets.clear();
+ if (expected_next.empty()) {
+ EXPECT_FALSE(result.next_proof);
+ } else {
+ ASSERT_TRUE(result.next_proof);
+ actual_rrsets.push_back(result.next_proof);
+ if (expected_sig) {
+ actual_rrsets.push_back(result.next_proof->getRRsig());
+ }
+ rrsetsCheck(expected_next, actual_rrsets.begin(),
+ actual_rrsets.end());
+ }
+}
TEST_F(InMemoryZoneFinderTest, addNSEC3) {
+ // Set up the faked hash calculator.
+ setNSEC3HashCreator(&nsec3_hash_creator_);
+
const string nsec3_text = string(apex_hash) + ".example.org." +
string(nsec3_common);
// This name shouldn't be found in the normal domain tree.
EXPECT_EQ(result::SUCCESS, zone_finder_.add(textToRRset(nsec3_text)));
EXPECT_EQ(ZoneFinder::NXDOMAIN,
zone_finder_.find(Name(string(apex_hash) + ".example.org"),
- RRType::NSEC3()).code);
+ RRType::NSEC3())->code);
// Dedicated NSEC3 find should be able to find it.
- nsec3Check(true, nsec3_text,
- zone_finder_.findNSEC3Tmp(Name("example.org"), false));
+ findNSEC3Check(true, origin_.getLabelCount(), nsec3_text, "",
+ zone_finder_.findNSEC3(Name("example.org"), false));
// This implementation rejects duplicate/update add of the same hash name
EXPECT_EQ(result::EXIST,
@@ -1536,8 +1703,8 @@ TEST_F(InMemoryZoneFinderTest, addNSEC3) {
string(apex_hash) + ".example.org." +
string(nsec3_common) + " AAAA")));
// The original NSEC3 should be intact
- nsec3Check(true, nsec3_text,
- zone_finder_.findNSEC3Tmp(Name("example.org"), false));
+ findNSEC3Check(true, origin_.getLabelCount(), nsec3_text, "",
+ zone_finder_.findNSEC3(Name("example.org"), false));
// NSEC3-like name but of ordinary RR type should go to normal tree.
const string nonsec3_text = string(apex_hash) + ".example.org. " +
@@ -1545,19 +1712,25 @@ TEST_F(InMemoryZoneFinderTest, addNSEC3) {
EXPECT_EQ(result::SUCCESS, zone_finder_.add(textToRRset(nonsec3_text)));
EXPECT_EQ(ZoneFinder::SUCCESS,
zone_finder_.find(Name(string(apex_hash) + ".example.org"),
- RRType::A()).code);
+ RRType::A())->code);
}
TEST_F(InMemoryZoneFinderTest, addNSEC3Lower) {
+ // Set up the faked hash calculator.
+ setNSEC3HashCreator(&nsec3_hash_creator_);
+
// Similar to the previous case, but NSEC3 owner name is lower-cased.
const string nsec3_text = string(apex_hash_lower) + ".example.org." +
string(nsec3_common);
EXPECT_EQ(result::SUCCESS, zone_finder_.add(textToRRset(nsec3_text)));
- nsec3Check(true, nsec3_text,
- zone_finder_.findNSEC3Tmp(Name("example.org"), false));
+ findNSEC3Check(true, origin_.getLabelCount(), nsec3_text, "",
+ zone_finder_.findNSEC3(Name("example.org"), false));
}
TEST_F(InMemoryZoneFinderTest, addNSEC3Ordering) {
+ // Set up the faked hash calculator.
+ setNSEC3HashCreator(&nsec3_hash_creator_);
+
// Check that the internal storage ensures comparison based on the NSEC3
// semantics, regardless of the add order or the letter-case of hash.
@@ -1566,7 +1739,7 @@ TEST_F(InMemoryZoneFinderTest, addNSEC3Ordering) {
string(nsec3_common);
const string middle = string(ns1_hash) + ".example.org." +
string(nsec3_common);
- const string largest = string(xrw_hash) + ".example.org." +
+ const string largest = string(xyw_hash) + ".example.org." +
string(nsec3_common);
zone_finder_.add(textToRRset(smallest));
zone_finder_.add(textToRRset(largest));
@@ -1574,15 +1747,15 @@ TEST_F(InMemoryZoneFinderTest, addNSEC3Ordering) {
// Then look for NSEC3 that covers a name whose hash is "2S.."
// The covering NSEC3 should be "0P.."
- nsec3Check(false, smallest,
- zone_finder_.findNSEC3Tmp(Name("www.example.org"), false));
+ findNSEC3Check(false, 4, smallest, "",
+ zone_finder_.findNSEC3(Name("www.example.org"), false));
// Look for NSEC3 that covers names whose hash are "Q0.." and "0A.."
// The covering NSEC3 should be "2v.." in both cases
- nsec3Check(false, largest,
- zone_finder_.findNSEC3Tmp(Name("xxx.example.org"), false));
- nsec3Check(false, largest,
- zone_finder_.findNSEC3Tmp(Name("yyy.example.org"), false));
+ findNSEC3Check(false, 4, largest, "",
+ zone_finder_.findNSEC3(Name("xxx.example.org"), false));
+ findNSEC3Check(false, 4, largest, "",
+ zone_finder_.findNSEC3(Name("yyy.example.org"), false));
}
TEST_F(InMemoryZoneFinderTest, badNSEC3Name) {
@@ -1610,6 +1783,9 @@ TEST_F(InMemoryZoneFinderTest, addMultiNSEC3) {
}
TEST_F(InMemoryZoneFinderTest, addNSEC3WithRRSIG) {
+ // Set up the faked hash calculator.
+ setNSEC3HashCreator(&nsec3_hash_creator_);
+
// Adding NSEC3 and its RRSIG
const string nsec3_text = string(apex_hash) + ".example.org." +
string(nsec3_common);
@@ -1619,8 +1795,9 @@ TEST_F(InMemoryZoneFinderTest, addNSEC3WithRRSIG) {
EXPECT_EQ(result::SUCCESS, zone_finder_.add(textToRRset(nsec3_rrsig_text)));
// Then look for it. The NSEC3 should have the RRSIG that was just added.
- nsec3Check(true, nsec3_text + "\n" + nsec3_rrsig_text,
- zone_finder_.findNSEC3Tmp(Name("example.org"), false), true);
+ findNSEC3Check(true, origin_.getLabelCount(),
+ nsec3_text + "\n" + nsec3_rrsig_text, "",
+ zone_finder_.findNSEC3(Name("example.org"), false), true);
// Duplicate add of RRSIG for the same NSEC3 is prohibited.
EXPECT_THROW(zone_finder_.add(textToRRset(nsec3_rrsig_text)),
@@ -1718,4 +1895,215 @@ TEST_F(InMemoryZoneFinderTest, loadNSEC3Zone) {
// This is an abnormal case, but the implementation accepts it.
zone_finder_.load(TEST_DATA_DIR "/example.org.nsec3-signed-noparam");
}
+
+// This test checks that the NSEC3 names don't really exist in the real
+// namespace.
+TEST_F(InMemoryZoneFinderTest, queryToNSEC3Name) {
+ // Add the NSEC3 and NSEC3PARAM there.
+ EXPECT_EQ(result::SUCCESS,
+ zone_finder_.add(textToRRset("example.org. 300 IN NSEC3PARAM "
+ "1 0 12 aabbccdd")));
+ const Name nsec3domain(string(apex_hash) + ".example.org.");
+ // Adding an NSEC3 that has matching parameters is okay.
+ EXPECT_EQ(result::SUCCESS, zone_finder_.add(
+ textToRRset(string(apex_hash) + ".example.org." +
+ string(nsec3_common))));
+ // Now, the domain should not exist
+ findTest(nsec3domain, RRType::AAAA(), ZoneFinder::NXDOMAIN, false,
+ ConstRRsetPtr(), ZoneFinder::RESULT_NSEC3_SIGNED, &zone_finder_,
+ ZoneFinder::FIND_DNSSEC);
+ // If we add an A record, the domain should exist
+ ConstRRsetPtr rrset(textToRRset(string(apex_hash) +
+ ".example.org. 300 IN A 192.0.2.1"));
+ EXPECT_EQ(result::SUCCESS, zone_finder_.add(rrset));
+ // Searching for a different RRType will tell us this RRset doesn't exist
+ findTest(nsec3domain, RRType::AAAA(), ZoneFinder::NXRRSET, false,
+ ConstRRsetPtr(), ZoneFinder::RESULT_NSEC3_SIGNED, &zone_finder_,
+ ZoneFinder::FIND_DNSSEC);
+ // Searching for the A record would find it
+ findTest(nsec3domain, RRType::A(), ZoneFinder::SUCCESS, true,
+ rrset, ZoneFinder::RESULT_DEFAULT, &zone_finder_,
+ ZoneFinder::FIND_DNSSEC);
+}
+
+// Continuation of the previous test (queryToNSEC3Name), we check we don't break
+// the empty nonterminal case by existence of NSEC3 record with that name.
+TEST_F(InMemoryZoneFinderTest, queryToNSEC3NameNonterminal) {
+ // Add the NSEC3 and NSEC3PARAM there.
+ EXPECT_EQ(result::SUCCESS,
+ zone_finder_.add(textToRRset("example.org. 300 IN NSEC3PARAM "
+ "1 0 12 aabbccdd")));
+ const Name nsec3domain(string(apex_hash) + ".example.org.");
+ // Adding an NSEC3 that has matching parameters is okay.
+ EXPECT_EQ(result::SUCCESS, zone_finder_.add(
+ textToRRset(string(apex_hash) + ".example.org." +
+ string(nsec3_common))));
+ // Something below the name
+ ConstRRsetPtr rrset(textToRRset("below." + string(apex_hash) +
+ ".example.org. 300 IN A 192.0.2.1"));
+ EXPECT_EQ(result::SUCCESS, zone_finder_.add(rrset));
+ // Now, the node is empty non-terminal.
+ findTest(nsec3domain, RRType::AAAA(), ZoneFinder::NXRRSET, false,
+ ConstRRsetPtr(), ZoneFinder::RESULT_NSEC3_SIGNED, &zone_finder_,
+ ZoneFinder::FIND_DNSSEC);
+}
+
+TEST_F(InMemoryZoneFinderTest, findNSEC3) {
+ // Set up the faked hash calculator.
+ setNSEC3HashCreator(&nsec3_hash_creator_);
+
+ // Add a few NSEC3 records:
+ // apex (example.org.): hash=0P..
+ // ns1.example.org: hash=2T..
+ // w.example.org: hash=01..
+ // zzz.example.org: hash=R5..
+ const string apex_nsec3_text = string(apex_hash) + ".example.org." +
+ string(nsec3_common);
+ EXPECT_EQ(result::SUCCESS, zone_finder_.add(textToRRset(apex_nsec3_text)));
+ const string ns1_nsec3_text = string(ns1_hash) + ".example.org." +
+ string(nsec3_common);
+ EXPECT_EQ(result::SUCCESS, zone_finder_.add(textToRRset(ns1_nsec3_text)));
+ const string w_nsec3_text = string(w_hash) + ".example.org." +
+ string(nsec3_common);
+ EXPECT_EQ(result::SUCCESS, zone_finder_.add(textToRRset(w_nsec3_text)));
+ const string zzz_nsec3_text = string(zzz_hash) + ".example.org." +
+ string(nsec3_common);
+ EXPECT_EQ(result::SUCCESS, zone_finder_.add(textToRRset(zzz_nsec3_text)));
+
+ // Parameter validation: the query name must be in or below the zone
+ EXPECT_THROW(zone_finder_.findNSEC3(Name("example.com"), false),
+ isc::InvalidParameter);
+ EXPECT_THROW(zone_finder_.findNSEC3(Name("org"), true),
+ isc::InvalidParameter);
+
+ // Apex name. It should have a matching NSEC3.
+ {
+ SCOPED_TRACE("apex, non recursive mode");
+ findNSEC3Check(true, origin_.getLabelCount(), apex_nsec3_text, "",
+ zone_finder_.findNSEC3(origin_, false));
+ }
+
+ // Recursive mode doesn't change the result in this case.
+ {
+ SCOPED_TRACE("apex, recursive mode");
+ findNSEC3Check(true, origin_.getLabelCount(), apex_nsec3_text, "",
+ zone_finder_.findNSEC3(origin_, true));
+ }
+
+ // Non existent name. Disabling recursion, a covering NSEC3 should be
+ // returned.
+ const Name www_name("www.example.org");
+ {
+ SCOPED_TRACE("non existent name, non recursive mode");
+ findNSEC3Check(false, www_name.getLabelCount(), apex_nsec3_text, "",
+ zone_finder_.findNSEC3(www_name, false));
+ }
+
+ // Non existent name. The closest provable encloser is the apex,
+ // and next closer is the query name itself (which NSEC3 for ns1
+ // covers)
+ // H(ns1) = 2T... < H(xxx) = Q0... < H(zzz) = R5...
+ {
+ SCOPED_TRACE("non existent name, recursive mode");
+ findNSEC3Check(true, origin_.getLabelCount(), apex_nsec3_text,
+ ns1_nsec3_text,
+ zone_finder_.findNSEC3(Name("xxx.example.org"), true));
+ }
+
+ // Similar to the previous case, but next closer name is different
+ // from the query name. The closet encloser is w.example.org, and
+ // next closer is y.w.example.org.
+ // H(ns1) = 2T.. < H(y.w) = K8.. < H(zzz) = R5
+ {
+ SCOPED_TRACE("non existent name, non qname next closer");
+ findNSEC3Check(true, Name("w.example.org").getLabelCount(),
+ w_nsec3_text, ns1_nsec3_text,
+ zone_finder_.findNSEC3(Name("x.y.w.example.org"),
+ true));
+ }
+
+ // In the rest of test we check hash comparison for wrap around cases.
+ {
+ SCOPED_TRACE("very small hash");
+ const Name smallest_name("smallest.example.org");
+ findNSEC3Check(false, smallest_name.getLabelCount(),
+ zzz_nsec3_text, "",
+ zone_finder_.findNSEC3(smallest_name, false));
+ }
+ {
+ SCOPED_TRACE("very large hash");
+ const Name largest_name("largest.example.org");
+ findNSEC3Check(false, largest_name.getLabelCount(),
+ zzz_nsec3_text, "",
+ zone_finder_.findNSEC3(largest_name, false));
+ }
+}
+
+TEST_F(InMemoryZoneFinderTest, findNSEC3ForBadZone) {
+ // Set up the faked hash calculator.
+ setNSEC3HashCreator(&nsec3_hash_creator_);
+
+ // If the zone has nothing about NSEC3 (neither NSEC3 or NSEC3PARAM),
+ // findNSEC3() should be rejected.
+ EXPECT_THROW(zone_finder_.findNSEC3(Name("www.example.org"), true),
+ DataSourceError);
+
+ // Only having NSEC3PARAM isn't enough
+ EXPECT_EQ(result::SUCCESS,
+ zone_finder_.add(textToRRset("example.org. 300 IN NSEC3PARAM "
+ "1 0 12 aabbccdd")));
+ EXPECT_THROW(zone_finder_.findNSEC3(Name("www.example.org"), true),
+ DataSourceError);
+
+ // Unless NSEC3 for apex is added the result in the recursive mode
+ // is guaranteed.
+ const string ns1_nsec3_text = string(ns1_hash) + ".example.org." +
+ string(nsec3_common);
+ EXPECT_EQ(result::SUCCESS, zone_finder_.add(textToRRset(ns1_nsec3_text)));
+ EXPECT_THROW(zone_finder_.findNSEC3(Name("www.example.org"), true),
+ DataSourceError);
+}
+
+TEST_F(InMemoryZoneFinderTest, loadAndFindNSEC3) {
+ // Using more realistic example, borrowed from RFC5155, with compliant
+ // hash calculator. We only confirm the data source can load it
+ // successfully and find correct NSEC3 RRs for some selected cases
+ // (detailed tests have been done above).
+
+ InMemoryZoneFinder finder(class_, Name("example"));
+ finder.load(TEST_DATA_COMMONDIR "/rfc5155-example.zone.signed");
+
+ // See RFC5155 B.1
+ ZoneFinder::FindNSEC3Result result1 =
+ finder.findNSEC3(Name("c.x.w.example"), true);
+ ASSERT_TRUE(result1.closest_proof);
+ // We compare closest_labels as int so the error report will be more
+ // readable in case it fails.
+ EXPECT_EQ(4, static_cast<int>(result1.closest_labels));
+ EXPECT_EQ(Name("b4um86eghhds6nea196smvmlo4ors995.example"),
+ result1.closest_proof->getName());
+ ASSERT_TRUE(result1.next_proof);
+ EXPECT_EQ(Name("0p9mhaveqvm6t7vbl5lop2u3t2rp3tom.example"),
+ result1.next_proof->getName());
+
+ // See RFC5155 B.2.
+ ZoneFinder::FindNSEC3Result result2 =
+ finder.findNSEC3(Name("ns1.example"), true);
+ ASSERT_TRUE(result2.closest_proof);
+ EXPECT_EQ(3, static_cast<int>(result2.closest_labels));
+ EXPECT_EQ(Name("2t7b4g4vsa5smi47k61mv5bv1a22bojr.example"),
+ result2.closest_proof->getName());
+ ASSERT_FALSE(result2.next_proof);
+
+ // See RFC5155 B.5.
+ ZoneFinder::FindNSEC3Result result3 =
+ finder.findNSEC3(Name("a.z.w.example"), true);
+ ASSERT_TRUE(result3.closest_proof);
+ EXPECT_EQ(3, static_cast<int>(result3.closest_labels));
+ EXPECT_EQ(Name("k8udemvp1j2f7eg6jebps17vp3n8i58h.example"),
+ result3.closest_proof->getName());
+ ASSERT_TRUE(result3.next_proof);
+ EXPECT_EQ(Name("q04jkcevqvmu85r014c7dkba38o0ji5r.example"),
+ result3.next_proof->getName());
+}
}
diff --git a/src/lib/datasrc/tests/rbnode_rrset_unittest.cc b/src/lib/datasrc/tests/rbnode_rrset_unittest.cc
new file mode 100644
index 0000000..a46d9cf
--- /dev/null
+++ b/src/lib/datasrc/tests/rbnode_rrset_unittest.cc
@@ -0,0 +1,258 @@
+// 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 <stdexcept>
+
+#include <exceptions/exceptions.h>
+#include <dns/rdataclass.h>
+#include <datasrc/rbnode_rrset.h>
+#include <testutils/dnsmessage_test.h>
+
+#include <gtest/gtest.h>
+
+#include <dns/tests/unittest_util.h>
+
+using isc::UnitTestUtil;
+
+using namespace isc;
+using namespace isc::datasrc;
+using namespace isc::datasrc::internal;
+using namespace isc::dns;
+using namespace isc::dns::rdata;
+using namespace isc::testutils;
+using namespace isc::util;
+using namespace std;
+
+// These tests are very similar to those for RRset - indeed, this file was
+// created from those tests. However, the significant difference in behaviour
+// between RRset and RBNodeRRset - that the "set" methods in the latter mostly
+// result in exceptions being thrown - preclude use of full type
+// parameterisation of the tests.
+
+namespace {
+const char* const RRSIG_TXT =
+ "A 5 4 43200 20100223214617 20100222214617 8496 isc.org. "
+ "evxhlGx13mpKLVkKsjpGzycS5twtIoxOmlN14w9t5AgzGBmz"
+ "diGdLIrFabqr72af2rUq+UDBKMWXujwZTZUTws32sVldDPk/"
+ "NbuacJM25fQXfv5mO3Af7TOoow3AjMaVG9icjCW0V55WcWQU"
+ "f49t+sXKPzbipN9g+s1ZPiIyofc=";
+
+class RBNodeRRsetTest : public ::testing::Test {
+protected:
+ RBNodeRRsetTest() :
+ test_name("test.example.com"),
+ test_domain("example.com"),
+ test_nsname("ns.example.com"),
+ rrset_a(ConstRRsetPtr(new RRset(
+ test_name, RRClass::IN(), RRType::A(), RRTTL(3600)))),
+ rrset_a_empty(ConstRRsetPtr(new RRset(
+ test_name, RRClass::IN(), RRType::A(), RRTTL(3600)))),
+ rrset_ns(ConstRRsetPtr(new RRset(
+ test_domain, RRClass::IN(), RRType::NS(), RRTTL(86400)))),
+ rrset_ch_txt(ConstRRsetPtr(new RRset(
+ test_domain, RRClass::CH(), RRType::TXT(), RRTTL(0)))),
+ rrset_siga(new RRset(test_name, RRClass::IN(), RRType::RRSIG(),
+ RRTTL(3600)))
+
+ {
+ // Add a couple of Rdata elements to the A RRset. The easiest way to
+ // do this is to override the "const" restrictions. As this is a test,
+ // we don't feel too bad about doing so.
+ AbstractRRset* a_rrset =
+ const_cast<AbstractRRset*>(rrset_a.getUnderlyingRRset().get());
+ a_rrset->addRdata(in::A("192.0.2.1"));
+ a_rrset->addRdata(in::A("192.0.2.2"));
+
+ // Create the RRSIG corresponding to the rrset_a record. The RDATA
+ // won't match the A record it covers, although it is internally
+ // self-consistent.
+ AbstractRRset* sig_rrset =
+ const_cast<AbstractRRset*>(rrset_siga.get());
+ sig_rrset->addRdata(generic::RRSIG(RRSIG_TXT));
+ }
+
+ const Name test_name;
+ const Name test_domain;
+ const Name test_nsname;
+
+ RBNodeRRset rrset_a;
+ RBNodeRRset rrset_a_empty;
+ const RBNodeRRset rrset_ns;
+ const RBNodeRRset rrset_ch_txt;
+
+ ConstRRsetPtr rrset_siga;
+};
+
+TEST_F(RBNodeRRsetTest, getRdataCount) {
+ EXPECT_EQ(0, rrset_a_empty.getRdataCount());
+ EXPECT_EQ(2, rrset_a.getRdataCount());
+}
+
+TEST_F(RBNodeRRsetTest, getName) {
+ EXPECT_EQ(test_name, rrset_a.getName());
+ EXPECT_EQ(test_domain, rrset_ns.getName());
+}
+
+TEST_F(RBNodeRRsetTest, getClass) {
+ EXPECT_EQ(RRClass("IN"), rrset_a.getClass());
+ EXPECT_EQ(RRClass("CH"), rrset_ch_txt.getClass());
+}
+
+TEST_F(RBNodeRRsetTest, getType) {
+ EXPECT_EQ(RRType("A"), rrset_a.getType());
+ EXPECT_EQ(RRType("NS"), rrset_ns.getType());
+ EXPECT_EQ(RRType("TXT"), rrset_ch_txt.getType());
+}
+
+TEST_F(RBNodeRRsetTest, getTTL) {
+ EXPECT_EQ(RRTTL(3600), rrset_a.getTTL());
+ EXPECT_EQ(RRTTL(86400), rrset_ns.getTTL());
+ EXPECT_EQ(RRTTL(0), rrset_ch_txt.getTTL());
+}
+
+TEST_F(RBNodeRRsetTest, setName) {
+ EXPECT_THROW(rrset_a.setName(test_nsname), NotImplemented);
+}
+
+TEST_F(RBNodeRRsetTest, setTTL) {
+ EXPECT_THROW(rrset_a.setTTL(RRTTL(86400)), NotImplemented);
+}
+
+TEST_F(RBNodeRRsetTest, toText) {
+ EXPECT_EQ("test.example.com. 3600 IN A 192.0.2.1\n"
+ "test.example.com. 3600 IN A 192.0.2.2\n",
+ rrset_a.toText());
+
+ // toText() cannot be performed for an empty RRset.
+ EXPECT_THROW(rrset_a_empty.toText(), EmptyRRset);
+}
+
+// Note: although the next two tests are essentially the same and used common
+// test code, they use different test data: the MessageRenderer produces
+// compressed wire data whereas the OutputBuffer does not.
+
+template <typename T>
+void
+performToWireTest(T& dataHolder, const RBNodeRRset& rrset,
+ const RBNodeRRset& rrset_empty, const char* testdata)
+{
+ rrset.toWire(dataHolder);
+
+ std::vector<unsigned char> wiredata;
+ UnitTestUtil::readWireData(testdata, wiredata);
+ EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, dataHolder.getData(),
+ dataHolder.getLength(), &wiredata[0], wiredata.size());
+
+ // toWire() cannot be performed for an empty RRset.
+ dataHolder.clear();
+ EXPECT_THROW(rrset_empty.toWire(dataHolder), EmptyRRset);
+}
+
+TEST_F(RBNodeRRsetTest, toWireRenderer) {
+ MessageRenderer renderer;
+ performToWireTest(renderer, rrset_a, rrset_a_empty, "rrset_toWire2");
+}
+
+TEST_F(RBNodeRRsetTest, toWireBuffer) {
+ OutputBuffer buffer(0);
+ performToWireTest(buffer, rrset_a, rrset_a_empty, "rrset_toWire1");
+}
+
+TEST_F(RBNodeRRsetTest, addRdata) {
+ EXPECT_THROW(rrset_a.addRdata(in::A("192.0.2.3")), NotImplemented);
+
+ // Check the same goes for trying to add the wrong type of data
+ EXPECT_THROW(rrset_a.addRdata(generic::NS(test_nsname)), NotImplemented);
+}
+
+TEST_F(RBNodeRRsetTest, addRdataPtr) {
+ EXPECT_THROW(rrset_a_empty.addRdata(createRdata(rrset_a_empty.getType(),
+ rrset_a_empty.getClass(),
+ "192.0.2.1")),
+ NotImplemented);
+}
+
+TEST_F(RBNodeRRsetTest, getRDataIterator) {
+ RdataIteratorPtr it = rrset_a.getRdataIterator();
+ for (int i = 0; i < 2; ++i) {
+ ASSERT_FALSE(it->isLast());
+ ASSERT_EQ(0, it->getCurrent().compare(in::A("192.0.2.1")));
+
+ it->next();
+ ASSERT_FALSE(it->isLast());
+ ASSERT_EQ(0, it->getCurrent().compare(in::A("192.0.2.2")));
+
+ it->next();
+ ASSERT_TRUE(it->isLast());
+
+ // Should be able repeat the iteration by calling first().
+ it->first();
+ }
+}
+
+// test operator<<. We simply confirm it appends the result of toText().
+TEST_F(RBNodeRRsetTest, LeftShiftOperator) {
+ ostringstream oss;
+ oss << rrset_a;
+ EXPECT_EQ("test.example.com. 3600 IN A 192.0.2.1\n"
+ "test.example.com. 3600 IN A 192.0.2.2\n", oss.str());
+}
+
+// addRRSIG tests.
+TEST_F(RBNodeRRsetTest, addRRsigConstRdataPointer) {
+ EXPECT_FALSE(rrset_a.getRRsig());
+ ConstRdataPtr data = createRdata(rrset_siga->getType(),
+ rrset_siga->getClass(), RRSIG_TXT);
+ rrset_a.addRRsig(data);
+ rrsetCheck(rrset_siga, rrset_a.getRRsig());
+}
+
+TEST_F(RBNodeRRsetTest, addRRsigRdataPointer) {
+ EXPECT_FALSE(rrset_a.getRRsig());
+ RdataPtr data = createRdata(rrset_siga->getType(), rrset_siga->getClass(),
+ RRSIG_TXT);
+ rrset_a.addRRsig(data);
+ rrsetCheck(rrset_siga, rrset_a.getRRsig());
+}
+
+TEST_F(RBNodeRRsetTest, addRRsigAbstractRRset) {
+ EXPECT_FALSE(rrset_a.getRRsig());
+ rrset_a.addRRsig(*(rrset_siga.get()));
+ rrsetCheck(rrset_siga, rrset_a.getRRsig());
+}
+
+TEST_F(RBNodeRRsetTest, addRRsigConstantRRsetPointer) {
+ EXPECT_FALSE(rrset_a.getRRsig());
+ rrset_a.addRRsig(rrset_siga);
+ rrsetCheck(rrset_siga, rrset_a.getRRsig());
+}
+
+TEST_F(RBNodeRRsetTest, addRRsigRRsetPointer) {
+ EXPECT_FALSE(rrset_a.getRRsig());
+ RRsetPtr rrsig(new RRset(test_name, RRClass::IN(), RRType::RRSIG(),
+ RRTTL(3600)));
+ rrsig->addRdata(generic::RRSIG(RRSIG_TXT));
+ rrset_a.addRRsig(rrsig);
+ rrsetCheck(rrset_siga, rrset_a.getRRsig());
+}
+
+TEST_F(RBNodeRRsetTest, removeRRsig) {
+ EXPECT_FALSE(rrset_a.getRRsig());
+ rrset_a.addRRsig(*(rrset_siga.get()));
+ EXPECT_TRUE(rrset_a.getRRsig());
+ rrset_a.removeRRsig();
+ EXPECT_FALSE(rrset_a.getRRsig());
+}
+
+} // Anonymous namespace
diff --git a/src/lib/datasrc/tests/static_unittest.cc b/src/lib/datasrc/tests/static_unittest.cc
index 4c9fe42..6d6bd74 100644
--- a/src/lib/datasrc/tests/static_unittest.cc
+++ b/src/lib/datasrc/tests/static_unittest.cc
@@ -56,9 +56,11 @@ protected:
authors_data.push_back("Dmitriy Volodin");
authors_data.push_back("Evan Hunt");
authors_data.push_back("Haidong Wang");
+ authors_data.push_back("Haikuo Zhang");
authors_data.push_back("Han Feng");
authors_data.push_back("Jelte Jansen");
- authors_data.push_back("Jeremy C. Reed");
+ authors_data.push_back("Jeremy C. Reed");
+ authors_data.push_back("Xie Jiagui");
authors_data.push_back("Jin Jian");
authors_data.push_back("JINMEI Tatuya");
authors_data.push_back("Kazunori Fujiwara");
diff --git a/src/lib/datasrc/tests/testdata/contexttest.zone b/src/lib/datasrc/tests/testdata/contexttest.zone
new file mode 100644
index 0000000..7227ced
--- /dev/null
+++ b/src/lib/datasrc/tests/testdata/contexttest.zone
@@ -0,0 +1,35 @@
+;; 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. 22 3600 300 3600000 3600
+example.org. 3600 IN NS ns1.example.org.
+example.org. 3600 IN NS ns2.example.org.
+example.org. 3600 IN MX 1 mx1.example.org.
+example.org. 3600 IN MX 2 mx2.example.org.
+example.org. 3600 IN MX 3 mx.a.example.org.
+
+ns1.example.org. 3600 IN A 192.0.2.1
+ns1.example.org. 3600 IN RRSIG A 7 3 3600 20150420235959 20051021000000 40430 example.org. FAKEFAKE
+ns1.example.org. 3600 IN AAAA 2001:db8::1
+ns1.example.org. 3600 IN RRSIG AAAA 7 3 3600 20150420235959 20051021000000 40430 example.org. FAKEFAKEFAKE
+ns2.example.org. 3600 IN A 192.0.2.2
+ns2.example.org. 3600 IN TXT "text data"
+
+mx1.example.org. 3600 IN A 192.0.2.10
+mx2.example.org. 3600 IN AAAA 2001:db8::10
+
+;; delegation
+a.example.org. 3600 IN NS ns1.a.example.org.
+a.example.org. 3600 IN NS ns2.a.example.org.
+a.example.org. 3600 IN NS ns.example.com.
+
+ns1.a.example.org. 3600 IN A 192.0.2.5
+ns2.a.example.org. 3600 IN A 192.0.2.6
+ns2.a.example.org. 3600 IN AAAA 2001:db8::6
+mx.a.example.org. 3600 IN A 192.0.2.7
+
+;; CNAME
+alias.example.org. 3600 IN CNAME cname.example.org.
+
+;; DNAME
+dname.example.org. 3600 IN DNAME dname.example.com.
diff --git a/src/lib/datasrc/tests/testdata/rrset_toWire1 b/src/lib/datasrc/tests/testdata/rrset_toWire1
new file mode 100644
index 0000000..8f81a0e
--- /dev/null
+++ b/src/lib/datasrc/tests/testdata/rrset_toWire1
@@ -0,0 +1,23 @@
+#
+# Rendering an IN/A RRset containing 2 RRs:
+# test.example.com. 3600 IN A 192.0.2.1
+# test.example.com. 3600 IN A 192.0.2.2
+#
+#(4) t e s t (7) e x a m p l e (3) c o m .
+ 04 74 65 73 74 07 65 78 61 6d 70 6c 65 03 63 6f 6d 00
+# type/class: A = 1, IN = 1
+00 01 00 01
+# TTL: 3600
+00 00 0e 10
+#6 7
+# RDLENGTH: 4
+00 04
+# RDATA: 192.0.2.1
+c0 00 02 01
+#
+# 2nd RR: mostly the same except the RDATA
+04 74 65 73 74 07 65 78 61 6d 70 6c 65 03 63 6f 6d 00
+00 01 00 01
+00 00 0e 10
+00 04
+c0 00 02 02
diff --git a/src/lib/datasrc/tests/testdata/rrset_toWire2 b/src/lib/datasrc/tests/testdata/rrset_toWire2
new file mode 100644
index 0000000..b9a6a15
--- /dev/null
+++ b/src/lib/datasrc/tests/testdata/rrset_toWire2
@@ -0,0 +1,26 @@
+#
+# Rendering an IN/A RRset and NS RRset as follows:
+# test.example.com. 3600 IN A 192.0.2.1
+# test.example.com. 3600 IN A 192.0.2.2
+# example.com. 1D IN NS ns.example.com.
+# Names will be compressed when possible.
+#
+# 0 1 2 3 4 5
+#(4) t e s t (7) e x a m p l e (3) c o m .
+ 04 74 65 73 74 07 65 78 61 6d 70 6c 65 03 63 6f 6d 00
+# type/class: A = 1, IN = 1
+00 01 00 01
+# TTL: 3600
+00 00 0e 10
+#6 7
+# RDLENGTH: 4
+00 04
+# RDATA: 192.0.2.1
+c0 00 02 01
+#
+# 2nd RR: the owner name is compresed
+c0 00
+00 01 00 01
+00 00 0e 10
+00 04
+c0 00 02 02
diff --git a/src/lib/datasrc/tests/zone_finder_context_unittest.cc b/src/lib/datasrc/tests/zone_finder_context_unittest.cc
new file mode 100644
index 0000000..5639f26
--- /dev/null
+++ b/src/lib/datasrc/tests/zone_finder_context_unittest.cc
@@ -0,0 +1,322 @@
+// Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <dns/masterload.h>
+#include <dns/name.h>
+#include <dns/rrclass.h>
+
+#include <datasrc/zone.h>
+#include <datasrc/memory_datasrc.h>
+#include <datasrc/database.h>
+#include <datasrc/sqlite3_accessor.h>
+
+#include <testutils/dnsmessage_test.h>
+
+#include <gtest/gtest.h>
+
+#include <boost/bind.hpp>
+#include <boost/foreach.hpp>
+#include <boost/shared_ptr.hpp>
+
+#include <cstdlib>
+#include <vector>
+
+using namespace std;
+using boost::shared_ptr;
+
+using namespace isc::dns;
+using namespace isc::datasrc;
+using namespace isc::testutils;
+
+namespace {
+
+// Commonly used test zone file.
+const char* const TEST_ZONE_FILE = TEST_DATA_DIR "/contexttest.zone";
+
+// Convenient shortcut
+typedef shared_ptr<DataSourceClient> DataSourceClientPtr;
+
+// This is the type used as the test parameter. Note that this is
+// intentionally a plain old type (i.e. a function pointer), not a class;
+// otherwise it could cause initialization fiasco at the instantiation time.
+typedef DataSourceClientPtr (*ClientCreator)(RRClass, const Name&);
+
+// Creator for the in-memory client to be tested
+DataSourceClientPtr
+createInMemoryClient(RRClass zclass, const Name& zname) {
+ shared_ptr<InMemoryClient> client(new InMemoryClient);
+
+ shared_ptr<InMemoryZoneFinder> finder(
+ new InMemoryZoneFinder(zclass, zname));
+ finder->load(TEST_ZONE_FILE);
+
+ client->addZone(finder);
+
+ return (client);
+}
+
+// Creator for the SQLite3 client to be tested. addRRset() is a helper
+// subroutine.
+void
+addRRset(ZoneUpdaterPtr updater, ConstRRsetPtr rrset) {
+ updater->addRRset(*rrset);
+}
+
+DataSourceClientPtr
+createSQLite3Client(RRClass zclass, const Name& zname) {
+ // We always begin with an empty template SQLite3 DB file and install
+ // the zone data from the zone file to ensure both cases have the
+ // same test data.
+
+ const char* const install_cmd = INSTALL_PROG " " TEST_DATA_DIR
+ "/rwtest.sqlite3 " TEST_DATA_BUILDDIR "/contexttest.sqlite3.copied";
+ if (system(install_cmd) != 0) {
+ isc_throw(isc::Unexpected,
+ "Error setting up; command failed: " << install_cmd);
+ }
+
+ shared_ptr<SQLite3Accessor> accessor(
+ new SQLite3Accessor(TEST_DATA_BUILDDIR "/contexttest.sqlite3.copied",
+ zclass.toText()));
+ shared_ptr<DatabaseClient> client(new DatabaseClient(zclass, accessor));
+
+ ZoneUpdaterPtr updater = client->getUpdater(zname, true);
+ masterLoad(TEST_ZONE_FILE, zname, zclass, boost::bind(addRRset, updater,
+ _1));
+ // Insert an out-of-zone name to test if it's incorrectly returned.
+ // Note that neither updater nor SQLite3 accessor checks this condition,
+ // so this should succeed.
+ stringstream ss("ns.example.com. 3600 IN A 192.0.2.7");
+ masterLoad(ss, Name::ROOT_NAME(), zclass,
+ boost::bind(addRRset, updater, _1));
+ updater->commit();
+
+ return (client);
+}
+
+// The test class. Its parameterized so we can share the test scnearios
+// for any concrete data source implementaitons.
+class ZoneFinderContextTest :
+ public ::testing::TestWithParam<ClientCreator>
+{
+protected:
+ ZoneFinderContextTest() : qclass_(RRClass::IN()), qzone_("example.org") {
+ client_ = (*GetParam())(qclass_, qzone_);
+ REQUESTED_A.push_back(RRType::A());
+ REQUESTED_AAAA.push_back(RRType::AAAA());
+ REQUESTED_BOTH.push_back(RRType::A());
+ REQUESTED_BOTH.push_back(RRType::AAAA());
+ }
+ void SetUp() {
+ finder_ = client_->findZone(qzone_).zone_finder;
+ ASSERT_TRUE(finder_);
+ }
+
+ const RRClass qclass_;
+ const Name qzone_;
+ DataSourceClientPtr client_;
+ ZoneFinderPtr finder_;
+
+ vector<RRType> requested_types_;
+ vector<RRType> REQUESTED_A;
+ vector<RRType> REQUESTED_AAAA;
+ vector<RRType> REQUESTED_BOTH;
+ vector<ConstRRsetPtr> result_sets_;
+};
+
+// We test the in-memory and SQLite3 data source implementations.
+INSTANTIATE_TEST_CASE_P(, ZoneFinderContextTest,
+ ::testing::Values(createInMemoryClient,
+ createSQLite3Client));
+
+TEST_P(ZoneFinderContextTest, getAdditionalAuthNS) {
+ ZoneFinderContextPtr ctx = finder_->find(qzone_, RRType::NS());
+ EXPECT_EQ(ZoneFinder::SUCCESS, ctx->code);
+
+ // Getting both A and AAAA NS addresses
+ ctx->getAdditional(REQUESTED_BOTH, result_sets_);
+ rrsetsCheck("ns1.example.org. 3600 IN A 192.0.2.1\n"
+ "ns1.example.org. 3600 IN AAAA 2001:db8::1\n"
+ "ns2.example.org. 3600 IN A 192.0.2.2\n",
+ result_sets_.begin(), result_sets_.end());
+
+ // Getting only A
+ result_sets_.clear();
+ ctx->getAdditional(REQUESTED_A, result_sets_);
+ rrsetsCheck("ns1.example.org. 3600 IN A 192.0.2.1\n"
+ "ns2.example.org. 3600 IN A 192.0.2.2\n",
+ result_sets_.begin(), result_sets_.end());
+
+ // Getting only AAAA
+ result_sets_.clear();
+ ctx->getAdditional(REQUESTED_AAAA, result_sets_);
+ rrsetsCheck("ns1.example.org. 3600 IN AAAA 2001:db8::1\n",
+ result_sets_.begin(), result_sets_.end());
+
+ // Getting A again, without clearing the result sets. This confirms
+ // getAdditional() doesn't change the existing vector content.
+ ctx->getAdditional(REQUESTED_A, result_sets_);
+ // The first element should be the existing AAAA RR, followed by the A's.
+ EXPECT_EQ(RRType::AAAA(), result_sets_[0]->getType());
+ rrsetsCheck("ns1.example.org. 3600 IN AAAA 2001:db8::1\n"
+ "ns1.example.org. 3600 IN A 192.0.2.1\n"
+ "ns2.example.org. 3600 IN A 192.0.2.2\n",
+ result_sets_.begin(), result_sets_.end());
+
+ // Normally expected type set contain only A and/or AAAA, but others aren't
+ // excluded.
+ result_sets_.clear();
+ requested_types_.push_back(RRType::TXT());
+ ctx->getAdditional(requested_types_, result_sets_);
+ rrsetsCheck("ns2.example.org. 3600 IN TXT \"text data\"",
+ result_sets_.begin(), result_sets_.end());
+
+ // Even empty set is okay. The result should also be empty.
+ result_sets_.clear();
+ ctx->getAdditional(vector<RRType>(), result_sets_);
+ EXPECT_TRUE(result_sets_.empty());
+}
+
+TEST_P(ZoneFinderContextTest, getAdditionalDelegation) {
+ // Basically similar to the AuthNS case, but NS names are glues.
+ // It contains an out-of-zone NS name. Its address (even if it's somehow
+ // inserted to the zone data) shouldn't be returned.
+ const Name qname("www.a.example.org");
+ ZoneFinderContextPtr ctx = finder_->find(qname, RRType::AAAA());
+ EXPECT_EQ(ZoneFinder::DELEGATION, ctx->code);
+
+ ctx->getAdditional(REQUESTED_BOTH, result_sets_);
+ rrsetsCheck("ns1.a.example.org. 3600 IN A 192.0.2.5\n"
+ "ns2.a.example.org. 3600 IN A 192.0.2.6\n"
+ "ns2.a.example.org. 3600 IN AAAA 2001:db8::6\n",
+ result_sets_.begin(), result_sets_.end());
+
+ result_sets_.clear();
+ ctx->getAdditional(REQUESTED_A, result_sets_);
+ rrsetsCheck("ns1.a.example.org. 3600 IN A 192.0.2.5\n"
+ "ns2.a.example.org. 3600 IN A 192.0.2.6\n",
+ result_sets_.begin(), result_sets_.end());
+
+ result_sets_.clear();
+ ctx->getAdditional(REQUESTED_AAAA, result_sets_);
+ rrsetsCheck("ns2.a.example.org. 3600 IN AAAA 2001:db8::6\n",
+ result_sets_.begin(), result_sets_.end());
+}
+
+TEST_P(ZoneFinderContextTest, getAdditionalMX) {
+ // Similar to the previous cases, but for MX addresses. The test zone
+ // contains MX name under a zone cut. Its address shouldn't be returned.
+ ZoneFinderContextPtr ctx = finder_->find(qzone_, RRType::MX());
+ EXPECT_EQ(ZoneFinder::SUCCESS, ctx->code);
+
+ // Getting both A and AAAA NS addresses
+ ctx->getAdditional(REQUESTED_BOTH, result_sets_);
+ rrsetsCheck("mx1.example.org. 3600 IN A 192.0.2.10\n"
+ "mx2.example.org. 3600 IN AAAA 2001:db8::10\n",
+ result_sets_.begin(), result_sets_.end());
+
+ // Getting only A
+ result_sets_.clear();
+ ctx->getAdditional(REQUESTED_A, result_sets_);
+ rrsetsCheck("mx1.example.org. 3600 IN A 192.0.2.10\n",
+ result_sets_.begin(), result_sets_.end());
+
+ // Getting only AAAA
+ result_sets_.clear();
+ ctx->getAdditional(REQUESTED_AAAA, result_sets_);
+ rrsetsCheck("mx2.example.org. 3600 IN AAAA 2001:db8::10\n",
+ result_sets_.begin(), result_sets_.end());
+}
+
+TEST_P(ZoneFinderContextTest, getAdditionalWithSIG) {
+ // Similar to the AuthNS test, but the original find() requested DNSSEC
+ // RRSIGs. Then additional records will also have RRSIGs.
+ ZoneFinderContextPtr ctx = finder_->find(qzone_, RRType::NS(),
+ ZoneFinder::FIND_DNSSEC);
+ EXPECT_EQ(ZoneFinder::SUCCESS, ctx->code);
+
+ ctx->getAdditional(REQUESTED_BOTH, result_sets_);
+ rrsetsCheck("ns1.example.org. 3600 IN A 192.0.2.1\n"
+ "ns1.example.org. 3600 IN AAAA 2001:db8::1\n"
+ "ns2.example.org. 3600 IN A 192.0.2.2\n",
+ result_sets_.begin(), result_sets_.end());
+
+ vector<ConstRRsetPtr> sigresult_sets;
+ BOOST_FOREACH(ConstRRsetPtr rrset, result_sets_) {
+ ConstRRsetPtr sig_rrset = rrset->getRRsig();
+ if (sig_rrset) {
+ sigresult_sets.push_back(sig_rrset);
+ }
+ }
+ rrsetsCheck("ns1.example.org. 3600 IN RRSIG A 7 3 3600 20150420235959 "
+ "20051021000000 40430 example.org. FAKEFAKE\n"
+ "ns1.example.org. 3600 IN RRSIG AAAA 7 3 3600 20150420235959 "
+ "20051021000000 40430 example.org. FAKEFAKEFAKE\n",
+ sigresult_sets.begin(), sigresult_sets.end());
+}
+
+TEST_P(ZoneFinderContextTest, getAdditionalNoOP) {
+ // getAdditional() is only meaningful after SUCCESS or DELEGATION.
+
+ ZoneFinderContextPtr ctx = finder_->find(Name("nxdomain.example.org"),
+ RRType::NS());
+ EXPECT_EQ(ZoneFinder::NXDOMAIN, ctx->code);
+ ctx->getAdditional(REQUESTED_BOTH, result_sets_);
+ EXPECT_TRUE(result_sets_.empty());
+
+ ctx = finder_->find(qzone_, RRType::TXT());
+ EXPECT_EQ(ZoneFinder::NXRRSET, ctx->code);
+ ctx->getAdditional(REQUESTED_BOTH, result_sets_);
+ EXPECT_TRUE(result_sets_.empty());
+
+ ctx = finder_->find(Name("alias.example.org."), RRType::A());
+ EXPECT_EQ(ZoneFinder::CNAME, ctx->code);
+ ctx->getAdditional(REQUESTED_BOTH, result_sets_);
+ EXPECT_TRUE(result_sets_.empty());
+
+ ctx = finder_->find(Name("www.dname.example.org."), RRType::A());
+ EXPECT_EQ(ZoneFinder::DNAME, ctx->code);
+ ctx->getAdditional(REQUESTED_BOTH, result_sets_);
+ EXPECT_TRUE(result_sets_.empty());
+}
+
+TEST_P(ZoneFinderContextTest, getAdditionalForAny) {
+ // getAdditional() after successful type ANY query should return
+ // the additional records of all returned RRsets.
+ vector<ConstRRsetPtr> all_rrsets;
+ ZoneFinderContextPtr ctx = finder_->findAll(qzone_, all_rrsets);
+ EXPECT_EQ(ZoneFinder::SUCCESS, ctx->code);
+
+ ctx->getAdditional(REQUESTED_BOTH, result_sets_);
+ rrsetsCheck("ns1.example.org. 3600 IN A 192.0.2.1\n"
+ "ns1.example.org. 3600 IN AAAA 2001:db8::1\n"
+ "ns2.example.org. 3600 IN A 192.0.2.2\n"
+ "mx1.example.org. 3600 IN A 192.0.2.10\n"
+ "mx2.example.org. 3600 IN AAAA 2001:db8::10\n",
+ result_sets_.begin(), result_sets_.end());
+
+ // If the type ANY query results in DELEGATION, the result should be the
+ // same as normal query.
+ all_rrsets.clear();
+ result_sets_.clear();
+ ctx = finder_->findAll(Name("www.a.example.org"), all_rrsets);
+ EXPECT_EQ(ZoneFinder::DELEGATION, ctx->code);
+ ctx->getAdditional(REQUESTED_BOTH, result_sets_);
+ rrsetsCheck("ns1.a.example.org. 3600 IN A 192.0.2.5\n"
+ "ns2.a.example.org. 3600 IN A 192.0.2.6\n"
+ "ns2.a.example.org. 3600 IN AAAA 2001:db8::6\n",
+ result_sets_.begin(), result_sets_.end());
+}
+
+}
diff --git a/src/lib/datasrc/zone.h b/src/lib/datasrc/zone.h
index 93d3ca9..361a03a 100644
--- a/src/lib/datasrc/zone.h
+++ b/src/lib/datasrc/zone.h
@@ -15,14 +15,15 @@
#ifndef __ZONE_H
#define __ZONE_H 1
-#include <utility>
-#include <vector>
-
+#include <dns/name.h>
#include <dns/rrset.h>
-#include <dns/rrsetlist.h>
+#include <dns/rrtype.h>
#include <datasrc/result.h>
+#include <utility>
+#include <vector>
+
namespace isc {
namespace datasrc {
@@ -79,7 +80,7 @@ public:
/// proof of the result.
///
/// The caller is generally expected to get access to the information
- /// via read-only getter methods of \c FindResult so that it won't rely
+ /// via read-only getter methods of \c FindContext so that it won't rely
/// on specific details of the representation of the flags. So these
/// definitions are basically only meaningful for data source
/// implementations.
@@ -90,39 +91,120 @@ public:
RESULT_NSEC3_SIGNED = 4 ///< The zone is signed with NSEC3 RRs
};
- /// A helper structure to represent the search result of \c find().
- ///
- /// This is a straightforward tuple of the result code and a pointer
- /// (and optionally special flags) to the found RRset to represent the
- /// result of \c find() (there will be more members in the future -
- /// see the class description).
- /// We use this in order to avoid overloading the return value for both
- /// the result code ("success" or "not found") and the found object,
- /// i.e., avoid using \c NULL to mean "not found", etc.
- ///
- /// This is a simple value class whose internal state never changes,
- /// so for convenience we allow the applications to refer to some of the
- /// members directly. For others we provide read-only accessor methods
- /// to hide specific representation.
- ///
- /// Note: we should eventually include a notion of "zone node", which
- /// corresponds to a particular domain name of the zone, so that we can
- /// find RRsets of a different RR type for that name (e.g. for type ANY
- /// query or to include DS RRs with delegation).
- ///
- /// Note: we may also want to include the closest enclosure "node" to
- /// optimize including the NSEC for no-wildcard proof (FWIW NSD does that).
- struct FindResult {
- FindResult(Result param_code,
- const isc::dns::ConstRRsetPtr param_rrset,
- FindResultFlags param_flags = RESULT_DEFAULT) :
- code(param_code), rrset(param_rrset), flags(param_flags)
+ /// Find options.
+ ///
+ /// The option values are used as a parameter for \c find().
+ /// These are values of a bitmask type. Bitwise operations can be
+ /// performed on these values to express compound options.
+ enum FindOptions {
+ FIND_DEFAULT = 0, ///< The default options
+ FIND_GLUE_OK = 1, ///< Allow search under a zone cut
+ FIND_DNSSEC = 2, ///< Require DNSSEC data in the answer
+ ///< (RRSIG, NSEC, etc.). The implementation
+ ///< is allowed to include it even if it is
+ ///< not set.
+ NO_WILDCARD = 4 ///< Do not try wildcard matching.
+ };
+
+protected:
+ /// \brief A convenient tuple representing a set of find() results.
+ ///
+ /// This helper structure is specifically expected to be used as an input
+ /// for the construct of the \c Context class object used by derived
+ /// ZoneFinder implementations. This is therefore defined as protected.
+ struct ResultContext {
+ ResultContext(Result code_param,
+ isc::dns::ConstRRsetPtr rrset_param,
+ FindResultFlags flags_param = RESULT_DEFAULT) :
+ code(code_param), rrset(rrset_param), flags(flags_param)
{}
const Result code;
const isc::dns::ConstRRsetPtr rrset;
+ const FindResultFlags flags;
+ };
+
+public:
+ /// \brief Context of the result of a find() call.
+ ///
+ /// This class encapsulates results and (possibly) associated context
+ /// of a call to the \c find() method. The public member variables of
+ /// this class reprsent the result of the call. They are a
+ /// straightforward tuple of the result code and a pointer (and
+ /// optionally special flags) to the found RRset.
+ ///
+ /// These member variables will be initialized on construction and never
+ /// change, so for convenience we allow the applications to refer to some
+ /// of the members directly. For some others we provide read-only accessor
+ /// methods to hide specific representation.
+ ///
+ /// Another role of this class is to provide the interface to some common
+ /// processing logic that may be necessary using the result of \c find().
+ /// Specifically, it's expected to be used in the context of DNS query
+ /// handling, where the caller would need to look into the data source
+ /// again based on the \c find() result. For example, it would need to
+ /// get A and/or AAAA records for some of the answer or authority RRs.
+ ///
+ /// This class defines (a set of) method(s) that can be commonly used
+ /// for such purposes for any type of data source (as long as it conforms
+ /// to the public \c find() interface). In some cases, a specific data
+ /// source implementation may want to (and can) optimize the processing
+ /// exploiting its internal data structure and the knowledge of the context
+ /// of the precedent \c find() call. Such a data source implementation
+ /// can define a derived class of the base Context and override the
+ /// specific virtual method.
+ ///
+ /// This class object is generally expected to be associated with the
+ /// ZoneFinder that originally performed the \c find() call, and expects
+ /// the finder is valid throughout the lifetime of this object. It's
+ /// caller's responsibility to ensure that assumption.
+ class Context {
+ public:
+ /// \brief The constructor for the normal find call.
+ ///
+ /// This constructor is expected to be called from the \c find()
+ /// method when it constructs the return value.
+ ///
+ /// \param finder The ZoneFinder on which find() is called.
+ /// \param options The find options specified for the find() call.
+ /// \param result The result of the find() call.
+ Context(ZoneFinder& finder, FindOptions options,
+ const ResultContext& result) :
+ code(result.code), rrset(result.rrset),
+ finder_(finder), flags_(result.flags), options_(options)
+ {}
+
+ /// \brief The constructor for the normal findAll call.
+ ///
+ /// This constructor is expected to be called from the \c findAll()
+ /// method when it constructs the return value.
+ ///
+ /// It copies the vector that is to be returned to the caller of
+ /// \c findAll() for possible subsequent use. Note that it cannot
+ /// simply hold a reference to the vector because the caller may
+ /// alter it after the \c findAll() call.
+ ///
+ /// \param finder The ZoneFinder on which findAll() is called.
+ /// \param options The find options specified for the findAll() call.
+ /// \param result The result of the findAll() call (whose rrset is
+ /// expected to be NULL).
+ /// \param all_set Reference to the vector given by the caller of
+ /// \c findAll(), storing the RRsets to be returned.
+ Context(ZoneFinder& finder, FindOptions options,
+ const ResultContext& result,
+ const std::vector<isc::dns::ConstRRsetPtr> &all_set) :
+ code(result.code), rrset(result.rrset),
+ finder_(finder), flags_(result.flags), options_(options),
+ all_set_(all_set)
+ {}
+
+ /// \brief The destructor.
+ virtual ~Context() {}
+
+ const Result code;
+ const isc::dns::ConstRRsetPtr rrset;
/// Return true iff find() results in a wildcard match.
- bool isWildcard() const { return ((flags & RESULT_WILDCARD) != 0); }
+ bool isWildcard() const { return ((flags_ & RESULT_WILDCARD) != 0); }
/// Return true when the underlying zone is signed with NSEC.
///
@@ -134,7 +216,7 @@ public:
/// that \c rrset be a valid NSEC RRset as described in \c find()
/// documentation.
bool isNSECSigned() const {
- return ((flags & RESULT_NSEC_SIGNED) != 0);
+ return ((flags_ & RESULT_NSEC_SIGNED) != 0);
}
/// Return true when the underlying zone is signed with NSEC3.
@@ -143,25 +225,65 @@ public:
/// \c FIND_DNSSEC isn't specified regardless of whether the zone
/// is signed or which of NSEC/NSEC3 is used.
bool isNSEC3Signed() const {
- return ((flags & RESULT_NSEC3_SIGNED) != 0);
+ return ((flags_ & RESULT_NSEC3_SIGNED) != 0);
}
- private:
- FindResultFlags flags;
- };
- /// Find options.
- ///
- /// The option values are used as a parameter for \c find().
- /// These are values of a bitmask type. Bitwise operations can be
- /// performed on these values to express compound options.
- enum FindOptions {
- FIND_DEFAULT = 0, ///< The default options
- FIND_GLUE_OK = 1, ///< Allow search under a zone cut
- FIND_DNSSEC = 2, ///< Require DNSSEC data in the answer
- ///< (RRSIG, NSEC, etc.). The implementation
- ///< is allowed to include it even if it is
- ///< not set.
- NO_WILDCARD = 4 ///< Do not try wildcard matching.
+ /// \brief Find and return additional RRsets corresponding to the
+ /// result of \c find().
+ ///
+ /// If this context is based on a normal find() call that resulted
+ /// in SUCCESS or DELEGATION, it examines the returned RRset (in many
+ /// cases NS, sometimes MX or others), searches the data source for
+ /// specified type of additional RRs for each RDATA of the RRset
+ /// (e.g., A or AAAA for the name server addresses), and stores the
+ /// result in the given vector. The vector may not be empty; this
+ /// method appends any found RRsets to it, without touching existing
+ /// elements.
+ ///
+ /// If this context is based on a findAll() call that resulted in
+ /// SUCCESS, it performs the same process for each RRset returned in
+ /// the \c findAll() call.
+ ///
+ /// The caller specifies desired RR types of the additional RRsets
+ /// in \c requested_types. Normally it consists of A and/or AAAA
+ /// types, but other types can be specified.
+ ///
+ /// This method is meaningful only when the precedent find()/findAll()
+ /// call resulted in SUCCESS or DELEGATION. Otherwise this method
+ /// does nothing.
+ ///
+ /// \param requested_types A vector of RR types for desired additional
+ /// RRsets.
+ /// \param result A vector to which any found additional RRsets are
+ /// to be inserted.
+ void getAdditional(
+ const std::vector<isc::dns::RRType>& requested_types,
+ std::vector<isc::dns::ConstRRsetPtr>& result)
+ {
+ // Perform common checks, and delegate the process to the default
+ // or specialized implementation.
+ if (code != SUCCESS && code != DELEGATION) {
+ return;
+ }
+
+ getAdditionalImpl(requested_types, result);
+ }
+
+ protected:
+ /// \brief Actual implementation of getAdditional().
+ ///
+ /// This base class defines a default implementation that can be
+ /// used for any type of data sources. A data source implementation
+ /// can override it.
+ virtual void getAdditionalImpl(
+ const std::vector<isc::dns::RRType>& requested_types,
+ std::vector<isc::dns::ConstRRsetPtr>& result);
+
+ private:
+ ZoneFinder& finder_;
+ const FindResultFlags flags_;
+ const FindOptions options_;
+ std::vector<isc::dns::ConstRRsetPtr> all_set_;
};
///
@@ -217,10 +339,10 @@ public:
/// the code of \c DNAME and that DNAME RR.
///
/// No RRset will be returned in the \c NXDOMAIN and \c NXRRSET cases
- /// (\c rrset member of \c FindResult will be NULL), unless DNSSEC data
+ /// (\c rrset member of \c FindContext will be NULL), unless DNSSEC data
/// are required. See below for the cases with DNSSEC.
///
- /// The returned \c FindResult object can also provide supplemental
+ /// The returned \c FindContext object can also provide supplemental
/// information about the search result via its methods returning a
/// boolean value. Such information may be useful for the caller if
/// the caller wants to collect additional DNSSEC proofs based on the
@@ -276,7 +398,7 @@ public:
/// returned from this method.
///
/// In case it's signed with NSEC, this method will possibly return
- /// a related NSEC RRset in the \c rrset member of \c FindResult.
+ /// a related NSEC RRset in the \c rrset member of \c FindContext.
/// What kind of NSEC is returned depends on the result code
/// (\c NXDOMAIN or \c NXRRSET) and on whether it's a wildcard match:
///
@@ -333,7 +455,7 @@ public:
/// \endcode
/// a call to \c find() for "y.b.example.org" with FIND_DNSSEC will
/// result in NXRRSET and this NSEC; \c isWildcard() on the returned
- /// \c FindResult object will return true.
+ /// \c FindContext object will return true.
///
/// \exception std::bad_alloc Memory allocation such as for constructing
/// the resulting RRset fails
@@ -348,11 +470,12 @@ public:
/// \param name The domain name to be searched for.
/// \param type The RR type to be searched for.
/// \param options The search options.
- /// \return A \c FindResult object enclosing the search result (see above).
- virtual FindResult find(const isc::dns::Name& name,
- const isc::dns::RRType& type,
- const FindOptions options
- = FIND_DEFAULT) = 0;
+ /// \return A \c FindContext object enclosing the search result
+ /// (see above).
+ virtual boost::shared_ptr<Context> find(const isc::dns::Name& name,
+ const isc::dns::RRType& type,
+ const FindOptions options
+ = FIND_DEFAULT) = 0;
///
/// \brief Finds all RRsets in the given name.
@@ -370,13 +493,14 @@ public:
/// \param target the successfull result is returned through this
/// \param options \see find, parameter options
/// \return \see find and it's result
- virtual FindResult findAll(const isc::dns::Name& name,
- std::vector<isc::dns::ConstRRsetPtr> &target,
- const FindOptions options = FIND_DEFAULT) = 0;
+ virtual boost::shared_ptr<Context> findAll(
+ const isc::dns::Name& name,
+ std::vector<isc::dns::ConstRRsetPtr> &target,
+ const FindOptions options = FIND_DEFAULT) = 0;
/// A helper structure to represent the search result of \c findNSEC3().
///
- /// The idea is similar to that of \c FindResult, but \c findNSEC3() has
+ /// The idea is similar to that of \c FindContext, but \c findNSEC3() has
/// special interface and semantics, we use a different structure to
/// represent the result.
struct FindNSEC3Result {
@@ -530,9 +654,16 @@ inline ZoneFinder::FindResultFlags operator |(
/// \brief A pointer-like type pointing to a \c ZoneFinder object.
typedef boost::shared_ptr<ZoneFinder> ZoneFinderPtr;
-/// \brief A pointer-like type pointing to a \c ZoneFinder object.
+/// \brief A pointer-like type pointing to an immutable \c ZoneFinder object.
typedef boost::shared_ptr<const ZoneFinder> ConstZoneFinderPtr;
+/// \brief A pointer-like type pointing to a \c ZoneFinder::Context object.
+typedef boost::shared_ptr<ZoneFinder::Context> ZoneFinderContextPtr;
+
+/// \brief A pointer-like type pointing to an immutable
+/// \c ZoneFinder::Context object.
+typedef boost::shared_ptr<ZoneFinder::Context> ConstZoneFinderContextPtr;
+
/// The base class to make updates to a single zone.
///
/// On construction, each derived class object will start a "transaction"
@@ -667,7 +798,7 @@ public:
/// \exception std::bad_alloc Resource allocation failure
///
/// \param rrset The RRset to be added
- virtual void addRRset(const isc::dns::RRset& rrset) = 0;
+ virtual void addRRset(const isc::dns::AbstractRRset& rrset) = 0;
/// Delete an RRset from a zone via the updater
///
@@ -739,7 +870,7 @@ public:
/// \exception std::bad_alloc Resource allocation failure
///
/// \param rrset The RRset to be deleted
- virtual void deleteRRset(const isc::dns::RRset& rrset) = 0;
+ virtual void deleteRRset(const isc::dns::AbstractRRset& rrset) = 0;
/// Commit the updates made in the updater to the zone
///
diff --git a/src/lib/datasrc/zone_finder_context.cc b/src/lib/datasrc/zone_finder_context.cc
new file mode 100644
index 0000000..7913d71
--- /dev/null
+++ b/src/lib/datasrc/zone_finder_context.cc
@@ -0,0 +1,102 @@
+// Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <dns/rdata.h>
+#include <dns/rrset.h>
+#include <dns/rrtype.h>
+#include <dns/rdataclass.h>
+
+#include <datasrc/zone.h>
+
+#include <boost/foreach.hpp>
+
+#include <vector>
+
+using namespace std;
+using namespace isc::dns;
+using namespace isc::dns::rdata;
+
+namespace isc {
+namespace datasrc {
+
+namespace {
+void
+getAdditionalAddrs(ZoneFinder& finder, const Name& name,
+ const vector<RRType>& requested_types,
+ vector<ConstRRsetPtr>& result_rrsets,
+ ZoneFinder::FindOptions options)
+{
+ // Ignore out-of-zone names
+ const NameComparisonResult cmp = finder.getOrigin().compare(name);
+ if ((cmp.getRelation() != NameComparisonResult::SUPERDOMAIN) &&
+ (cmp.getRelation() != NameComparisonResult::EQUAL)) {
+ return;
+ }
+
+ BOOST_FOREACH(RRType rrtype, requested_types) {
+ ConstZoneFinderContextPtr ctx = finder.find(name, rrtype, options);
+ if (ctx->code == ZoneFinder::SUCCESS) {
+ result_rrsets.push_back(ctx->rrset);
+ }
+ }
+}
+
+void
+getAdditionalForRRset(ZoneFinder& finder, const AbstractRRset& rrset,
+ const vector<RRType>& requested_types,
+ vector<ConstRRsetPtr>& result,
+ ZoneFinder::FindOptions orig_options)
+{
+ RdataIteratorPtr rdata_iterator(rrset.getRdataIterator());
+ ZoneFinder::FindOptions options = ZoneFinder::FIND_DEFAULT;
+ if ((orig_options & ZoneFinder::FIND_DNSSEC) != 0) {
+ options = options | ZoneFinder::FIND_DNSSEC;
+ }
+
+ for (; !rdata_iterator->isLast(); rdata_iterator->next()) {
+ const Rdata& rdata(rdata_iterator->getCurrent());
+
+ if (rrset.getType() == RRType::NS()) {
+ // Need to perform the search in the "GLUE OK" mode.
+ const generic::NS& ns = dynamic_cast<const generic::NS&>(rdata);
+ getAdditionalAddrs(finder, ns.getNSName(), requested_types,
+ result, options | ZoneFinder::FIND_GLUE_OK);
+ } else if (rrset.getType() == RRType::MX()) {
+ const generic::MX& mx = dynamic_cast<const generic::MX&>(rdata);
+ getAdditionalAddrs(finder, mx.getMXName(), requested_types,
+ result, options);
+ }
+ }
+}
+}
+
+void
+ZoneFinder::Context::getAdditionalImpl(const vector<RRType>& requested_types,
+ vector<ConstRRsetPtr>& result)
+{
+ // If rrset is non NULL, it should have been SUCCESS/DELEGATION; otherwise
+ // we should have responded to type ANY query.
+ if (rrset) {
+ getAdditionalForRRset(finder_, *rrset, requested_types, result,
+ options_);
+ return;
+ }
+ BOOST_FOREACH(ConstRRsetPtr rrset_in_set, all_set_) {
+ getAdditionalForRRset(finder_, *rrset_in_set, requested_types, result,
+ options_);
+ }
+}
+
+} // namespace datasrc
+} // datasrc isc
diff --git a/src/lib/dhcp/iface_mgr.cc b/src/lib/dhcp/iface_mgr.cc
index b704370..14de5a0 100644
--- a/src/lib/dhcp/iface_mgr.cc
+++ b/src/lib/dhcp/iface_mgr.cc
@@ -130,7 +130,7 @@ IfaceMgr::IfaceMgr()
// interface detection is implemented. Otherwise
// it is not possible to run tests in a portable
// way (see detectIfaces() method).
- throw ex;
+ throw;
}
}
@@ -191,7 +191,7 @@ IfaceMgr::stubDetectIfaces() {
// TODO Do LOG_FATAL here
std::cerr << "Interface detection failed." << std::endl;
- throw ex;
+ throw;
}
}
diff --git a/src/lib/dhcp/tests/.gitignore b/src/lib/dhcp/tests/.gitignore
new file mode 100644
index 0000000..313429d
--- /dev/null
+++ b/src/lib/dhcp/tests/.gitignore
@@ -0,0 +1 @@
+/libdhcp++_unittests
diff --git a/src/lib/dns/.gitignore b/src/lib/dns/.gitignore
new file mode 100644
index 0000000..cdf707c
--- /dev/null
+++ b/src/lib/dns/.gitignore
@@ -0,0 +1,6 @@
+/gen-rdatacode.py
+/rdataclass.cc
+/rdataclass.h
+/rrclass.h
+/rrparamregistry.cc
+/rrtype.h
diff --git a/src/lib/dns/Makefile.am b/src/lib/dns/Makefile.am
index 5e2a48d..8471c2a 100644
--- a/src/lib/dns/Makefile.am
+++ b/src/lib/dns/Makefile.am
@@ -23,6 +23,8 @@ EXTRA_DIST += rdata/generic/cname_5.cc
EXTRA_DIST += rdata/generic/cname_5.h
EXTRA_DIST += rdata/generic/detail/nsec_bitmap.cc
EXTRA_DIST += rdata/generic/detail/nsec_bitmap.h
+EXTRA_DIST += rdata/generic/detail/nsec3param_common.cc
+EXTRA_DIST += rdata/generic/detail/nsec3param_common.h
EXTRA_DIST += rdata/generic/detail/txt_like.h
EXTRA_DIST += rdata/generic/detail/ds_like.h
EXTRA_DIST += rdata/generic/dlv_32769.cc
@@ -89,6 +91,7 @@ libdns___la_LDFLAGS = -no-undefined -version-info 1:0:1
libdns___la_SOURCES =
libdns___la_SOURCES += edns.h edns.cc
libdns___la_SOURCES += exceptions.h exceptions.cc
+libdns___la_SOURCES += labelsequence.h labelsequence.cc
libdns___la_SOURCES += masterload.h masterload.cc
libdns___la_SOURCES += message.h message.cc
libdns___la_SOURCES += messagerenderer.h messagerenderer.cc
@@ -113,6 +116,8 @@ libdns___la_SOURCES += tsigrecord.h tsigrecord.cc
libdns___la_SOURCES += character_string.h character_string.cc
libdns___la_SOURCES += rdata/generic/detail/nsec_bitmap.h
libdns___la_SOURCES += rdata/generic/detail/nsec_bitmap.cc
+libdns___la_SOURCES += rdata/generic/detail/nsec3param_common.cc
+libdns___la_SOURCES += rdata/generic/detail/nsec3param_common.h
libdns___la_SOURCES += rdata/generic/detail/txt_like.h
libdns___la_SOURCES += rdata/generic/detail/ds_like.h
@@ -136,6 +141,7 @@ libdns___includedir = $(includedir)/dns
libdns___include_HEADERS = \
edns.h \
exceptions.h \
+ labelsequence.h \
message.h \
messagerenderer.h \
name.h \
diff --git a/src/lib/dns/benchmarks/.gitignore b/src/lib/dns/benchmarks/.gitignore
new file mode 100644
index 0000000..a6f1fac
--- /dev/null
+++ b/src/lib/dns/benchmarks/.gitignore
@@ -0,0 +1 @@
+/rdatarender_bench
diff --git a/src/lib/dns/benchmarks/rdatarender_bench.cc b/src/lib/dns/benchmarks/rdatarender_bench.cc
index d1fb0f2..368ea6a 100644
--- a/src/lib/dns/benchmarks/rdatarender_bench.cc
+++ b/src/lib/dns/benchmarks/rdatarender_bench.cc
@@ -42,7 +42,7 @@ template <typename T>
class RdataRenderBenchMark {
public:
RdataRenderBenchMark(const vector<T>& dataset) :
- dataset_(dataset), buffer_(4096), renderer_(buffer_)
+ dataset_(dataset)
{}
unsigned int run() {
typename vector<T>::const_iterator data;
@@ -55,7 +55,6 @@ public:
}
private:
const vector<T>& dataset_;
- OutputBuffer buffer_;
MessageRenderer renderer_;
};
diff --git a/src/lib/dns/labelsequence.cc b/src/lib/dns/labelsequence.cc
new file mode 100644
index 0000000..64cc519
--- /dev/null
+++ b/src/lib/dns/labelsequence.cc
@@ -0,0 +1,77 @@
+// Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <dns/labelsequence.h>
+#include <exceptions/exceptions.h>
+
+#include <iostream>
+namespace isc {
+namespace dns {
+
+const char*
+LabelSequence::getData(size_t *len) const {
+ // If the labelsequence is absolute, the current last_label_ falls
+ // out of the vector (since it points to the 'label' after the
+ // root label, which doesn't exist; in that case, return
+ // the length for the 'previous' label (the root label) plus
+ // one (for the root label zero octet)
+ if (isAbsolute()) {
+ *len = name_.offsets_[last_label_ - 1] - name_.offsets_[first_label_] + 1;
+ } else {
+ *len = name_.offsets_[last_label_] - name_.offsets_[first_label_];
+ }
+ return (&name_.ndata_[name_.offsets_[first_label_]]);
+}
+
+bool
+LabelSequence::equals(const LabelSequence& other, bool case_sensitive) const {
+ size_t len, other_len;
+ const char* data = getData(&len);
+ const char* other_data = other.getData(&other_len);
+
+ if (len != other_len) {
+ return (false);
+ }
+ if (case_sensitive) {
+ return (strncasecmp(data, other_data, len) == 0);
+ } else {
+ return (strncmp(data, other_data, len) == 0);
+ }
+}
+
+void
+LabelSequence::stripLeft(size_t i) {
+ if (i >= getLabelCount()) {
+ isc_throw(OutOfRange, "Cannot strip to zero or less labels; " << i <<
+ " (labelcount: " << getLabelCount() << ")");
+ }
+ first_label_ += i;
+}
+
+void
+LabelSequence::stripRight(size_t i) {
+ if (i >= getLabelCount()) {
+ isc_throw(OutOfRange, "Cannot strip to zero or less labels; " << i <<
+ " (labelcount: " << getLabelCount() << ")");
+ }
+ last_label_ -= i;
+}
+
+bool
+LabelSequence::isAbsolute() const {
+ return (last_label_ == name_.offsets_.size());
+}
+
+} // end namespace dns
+} // end namespace isc
diff --git a/src/lib/dns/labelsequence.h b/src/lib/dns/labelsequence.h
new file mode 100644
index 0000000..6fb1501
--- /dev/null
+++ b/src/lib/dns/labelsequence.h
@@ -0,0 +1,136 @@
+// 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 __LABELSEQUENCE_H
+#define __LABELSEQUENCE_H 1
+
+#include <dns/name.h>
+#include <util/buffer.h>
+
+namespace isc {
+namespace dns {
+
+/// \brief Light-weight Accessor to Name object
+///
+/// The purpose of this class is to easily match Names and parts of Names,
+/// without needing to copy the underlying data on each label strip.
+///
+/// It can only work on existing Name objects, and the Name object MUST
+/// remain in scope during the entire lifetime of its associated
+/// LabelSequence(s).
+///
+/// Upon creation of a LabelSequence, it records the offsets of the
+/// labels in the wireformat data of the Name. When stripLeft() or
+/// stripRight() is called on the LabelSequence, no changes in the
+/// Name's data occur, but the internal pointers of the
+/// LabelSequence are modified.
+///
+/// LabelSequences can be compared to other LabelSequences, and their
+/// data can be requested (which then points to part of the original
+/// data of the associated Name object).
+///
+class LabelSequence {
+public:
+ /// \brief Constructs a LabelSequence for the given name
+ ///
+ /// \note The associated Name MUST remain in scope during the lifetime
+ /// of this LabelSequence, since getData() refers to data from the
+ /// Name object (the only data the LabelSequence stores are pointers
+ /// to the labels in the Name object).
+ ///
+ /// \param name The Name to construct a LabelSequence for
+ LabelSequence(const Name& name): name_(name),
+ first_label_(0),
+ last_label_(name.getLabelCount())
+ {}
+
+ /// \brief Return the wire-format data for this LabelSequence
+ ///
+ /// The data, is returned as a pointer to the original wireformat
+ /// data of the original Name object, and the given len value is
+ /// set to the number of octets that match this labelsequence.
+ ///
+ /// \note The data pointed to is only valid if the original Name
+ /// object is still in scope
+ ///
+ /// \param len Pointer to a size_t where the length of the data
+ /// will be stored (in number of octets)
+ /// \return Pointer to the wire-format data of this label sequence
+ const char* getData(size_t* len) const;
+
+ /// \brief Compares two label sequences.
+ ///
+ /// Performs a (optionally case-insensitive) comparison between this
+ /// LabelSequence and another LabelSequence.
+ ///
+ /// \param other The LabelSequence to compare with
+ /// \param case_sensitive If true, comparison is case-insensitive
+ /// \return true if The label sequences consist are the same length,
+ /// and contain the same data.
+ bool equals(const LabelSequence& other, bool case_sensitive = false) const;
+
+ /// \brief Remove labels from the front of this LabelSequence
+ ///
+ /// \note No actual memory is changed, this operation merely updates the
+ /// internal pointers based on the offsets in the Name object.
+ ///
+ /// \exeption OutOfRange if i is greater than or equal to the number
+ /// of labels currently pointed to by this LabelSequence
+ ///
+ /// \param i The number of labels to remove.
+ void stripLeft(size_t i);
+
+ /// \brief Remove labels from the end of this LabelSequence
+ ///
+ /// \note No actual memory is changed, this operation merely updates the
+ /// internal pointers based on the offsets in the Name object.
+ ///
+ /// \exeption OutOfRange if i is greater than or equal to the number
+ /// of labels currently pointed to by this LabelSequence
+ ///
+ /// \param i The number of labels to remove.
+ void stripRight(size_t i);
+
+ /// \brief Returns the current number of labels for this LabelSequence
+ ///
+ /// \return The number of labels
+ size_t getLabelCount() const { return last_label_ - first_label_; }
+
+ /// \brief Returns the original Name object associated with this
+ /// LabelSequence
+ ///
+ /// While the Name should still be in scope during the lifetime of
+ /// the LabelSequence, it can still be useful to have access to it,
+ /// for instance in helper functions that are only passed the
+ /// LabelSequence itself.
+ ///
+ /// \return Reference to the original Name object
+ const Name& getName() const { return name_; }
+
+ /// \brief Checks whether the label sequence is absolute
+ ///
+ /// \return true if the last label is the root label
+ bool isAbsolute() const;
+
+private:
+ const Name& name_;
+ size_t first_label_;
+ size_t last_label_;
+};
+
+
+} // end namespace dns
+} // end namespace isc
+
+#endif
diff --git a/src/lib/dns/masterload.cc b/src/lib/dns/masterload.cc
index 000487c..0b195f6 100644
--- a/src/lib/dns/masterload.cc
+++ b/src/lib/dns/masterload.cc
@@ -37,6 +37,29 @@ using namespace isc::dns::rdata;
namespace isc {
namespace dns {
+namespace {
+// A helper function that strips off any comment or whitespace at the end of
+// an RR.
+// This is an incomplete implementation, and cannot handle all such comments;
+// it's considered a short term workaround to deal with some real world
+// cases.
+string
+stripLine(string& s, const Exception& ex) {
+ // Find any ';' in the text data, and locate the position of the last
+ // occurrence. Note that unless/until we support empty RDATA it
+ // shouldn't be placed at the beginning of the data.
+ const size_t pos_semicolon = s.rfind(';');
+ if (pos_semicolon == 0) {
+ throw ex;
+ } else if (pos_semicolon != string::npos) {
+ s.resize(pos_semicolon);
+ }
+ // Remove any trailing whitespace return the resulting text.
+ s.resize(s.find_last_not_of(" \t") + 1);
+ return (s);
+}
+}
+
void
masterLoad(const char* const filename, const Name& origin,
const RRClass& zone_class, MasterLoadCallback callback)
@@ -116,7 +139,15 @@ masterLoad(istream& input, const Name& origin, const RRClass& zone_class,
ttl.reset(new RRTTL(ttl_txt));
rrclass.reset(new RRClass(rrclass_txt));
rrtype.reset(new RRType(rrtype_txt));
- rdata = createRdata(*rrtype, *rrclass, rdatabuf.str());
+ string rdtext = rdatabuf.str();
+ try {
+ rdata = createRdata(*rrtype, *rrclass, rdtext);
+ } catch (const Exception& ex) {
+ // If the parse for the RDATA fails, check if it has comments
+ // or whitespace at the end, and if so, retry the conversion
+ // after stripping off the comment or whitespace
+ rdata = createRdata(*rrtype, *rrclass, stripLine(rdtext, ex));
+ }
} catch (const Exception& ex) {
isc_throw(MasterLoadError, "Invalid RR text at line " << line_count
<< ": " << ex.what());
diff --git a/src/lib/dns/message.cc b/src/lib/dns/message.cc
index b3e9229..0db68c6 100644
--- a/src/lib/dns/message.cc
+++ b/src/lib/dns/message.cc
@@ -489,6 +489,10 @@ Message::getRRCount(const Section section) const {
void
Message::addRRset(const Section section, RRsetPtr rrset, const bool sign) {
+ if (!rrset) {
+ isc_throw(InvalidParameter,
+ "NULL RRset is given to Message::addRRset");
+ }
if (impl_->mode_ != Message::RENDER) {
isc_throw(InvalidMessageOperation,
"addRRset performed in non-render mode");
diff --git a/src/lib/dns/message.h b/src/lib/dns/message.h
index 47632cb..33551c0 100644
--- a/src/lib/dns/message.h
+++ b/src/lib/dns/message.h
@@ -462,15 +462,19 @@ public:
/// This interface takes into account the RRSIG possibly attached to
/// \c rrset. This interface design needs to be revisited later.
///
- /// This method is only allowed in the \c RENDER mode;
- /// if the \c Message is in other mode, an exception of class
- /// InvalidMessageOperation will be thrown.
- /// \c section must be a valid constant of the \c Section type;
- /// otherwise, an exception of class \c OutOfRange will be thrown.
- ///
/// Note that \c addRRset() does not currently check for duplicate
/// data before inserting RRsets. The caller is responsible for
/// checking for these (see \c hasRRset() below).
+ ///
+ /// \throw InvalidParameter rrset is NULL
+ /// \throw InvalidMessageOperation The message is not in the \c RENDER
+ /// mode.
+ /// \throw OutOfRange \c section doesn't specify a valid \c Section value.
+ ///
+ /// \param section The message section to which the rrset is to be added
+ /// \param rrset The rrset to be added. Must not be NULL.
+ /// \param sign If true, and if \c rrset has associated RRSIGs, the
+ /// RRSIGs will also be added to the same section of the message.
void addRRset(const Section section, RRsetPtr rrset, bool sign = false);
/// \brief Determine whether the given section already has an RRset
diff --git a/src/lib/dns/messagerenderer.cc b/src/lib/dns/messagerenderer.cc
index 1378ba9..bf4795a 100644
--- a/src/lib/dns/messagerenderer.cc
+++ b/src/lib/dns/messagerenderer.cc
@@ -12,14 +12,15 @@
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
-#include <cctype>
-#include <cassert>
-#include <set>
-
+#include <exceptions/exceptions.h>
#include <util/buffer.h>
#include <dns/name.h>
#include <dns/messagerenderer.h>
+#include <cctype>
+#include <cassert>
+#include <set>
+
using namespace isc::util;
namespace isc {
@@ -171,8 +172,8 @@ struct MessageRenderer::MessageRendererImpl {
CompressMode compress_mode_;
};
-MessageRenderer::MessageRenderer(OutputBuffer& buffer) :
- AbstractMessageRenderer(buffer),
+MessageRenderer::MessageRenderer() :
+ AbstractMessageRenderer(),
impl_(new MessageRendererImpl)
{}
@@ -273,9 +274,34 @@ MessageRenderer::writeName(const Name& name, const bool compress) {
}
}
+AbstractMessageRenderer::AbstractMessageRenderer() :
+ local_buffer_(0), buffer_(&local_buffer_)
+{
+}
+
+void
+AbstractMessageRenderer::setBuffer(OutputBuffer* buffer) {
+ if (buffer != NULL && buffer_->getLength() != 0) {
+ isc_throw(isc::InvalidParameter,
+ "MessageRenderer buffer cannot be set when in use");
+ } if (buffer == NULL && buffer_ == &local_buffer_) {
+ isc_throw(isc::InvalidParameter,
+ "Default MessageRenderer buffer cannot be reset");
+ }
+
+ if (buffer == NULL) {
+ // Reset to the default buffer, then clear other internal resources.
+ // The order is important; we need to keep the used buffer intact.
+ buffer_ = &local_buffer_;
+ clear();
+ } else {
+ buffer_ = buffer;
+ }
+}
+
void
AbstractMessageRenderer::clear() {
- buffer_.clear();
+ buffer_->clear();
}
}
diff --git a/src/lib/dns/messagerenderer.h b/src/lib/dns/messagerenderer.h
index 52d9245..f7f2381 100644
--- a/src/lib/dns/messagerenderer.h
+++ b/src/lib/dns/messagerenderer.h
@@ -37,9 +37,15 @@ class Name;
/// comprehensive \c Message class internally; normal applications won't have
/// to care about details of this class.
///
-/// Once a renderer class object is constructed with a buffer, it is
-/// generally expected that all rendering operations are performed via that
-/// object. If the application modifies the buffer in
+/// By default any (derived) renderer class object is associated with
+/// an internal buffer, and subsequent write operations will be performed
+/// on that buffer. The rendering result can be retrieved via the
+/// \c getData() method.
+///
+/// If an application wants a separate buffer can be (normally temporarily)
+/// set for rendering operations via the \c setBuffer() method. In that case,
+/// it is generally expected that all rendering operations are performed via
+/// that object. If the application modifies the buffer in
/// parallel with the renderer, the result will be undefined.
///
/// Note to developers: we introduced a separate class for name compression
@@ -101,30 +107,30 @@ protected:
///
/// This is intentionally defined as \c protected as this base class should
/// never be instantiated (except as part of a derived class).
- /// \param buffer The buffer where the data should be rendered into.
- /// \todo We might want to revisit this API at some point and remove the
- /// buffer parameter. In that case it would create it's own buffer and
- /// a function to extract the data would be available instead. It seems
- /// like a cleaner design, but it's left undone until we would actually
- /// benefit from the change.
- AbstractMessageRenderer(isc::util::OutputBuffer& buffer) :
- buffer_(buffer)
- {}
+ AbstractMessageRenderer();
+
public:
/// \brief The destructor.
virtual ~AbstractMessageRenderer() {}
//@}
protected:
/// \brief Return the output buffer we render into.
- const isc::util::OutputBuffer& getBuffer() const { return (buffer_); }
- isc::util::OutputBuffer& getBuffer() { return (buffer_); }
+ const isc::util::OutputBuffer& getBuffer() const { return (*buffer_); }
+ isc::util::OutputBuffer& getBuffer() { return (*buffer_); }
private:
- /// \short Buffer to store data
+ /// \brief Local (default) buffer to store data.
+ isc::util::OutputBuffer local_buffer_;
+
+ /// \brief Buffer to store data.
+ ///
+ /// Note that the class interface ensures this pointer is never NULL;
+ /// it either refers to \c local_buffer_ or to an application-supplied
+ /// buffer by \c setBuffer().
///
/// It was decided that there's no need to have this in every subclass,
- /// at least not now, and this reduces code size and gives compiler a better
- /// chance to optimise.
- isc::util::OutputBuffer& buffer_;
+ /// at least not now, and this reduces code size and gives compiler a
+ /// better chance to optimise.
+ isc::util::OutputBuffer* buffer_;
public:
///
/// \name Getter Methods
@@ -136,12 +142,12 @@ public:
/// This method works exactly same as the same method of the \c OutputBuffer
/// class; all notes for \c OutputBuffer apply.
const void* getData() const {
- return (buffer_.getData());
+ return (buffer_->getData());
}
/// \brief Return the length of data written in the internal buffer.
size_t getLength() const {
- return (buffer_.getLength());
+ return (buffer_->getLength());
}
/// \brief Return whether truncation has occurred while rendering.
@@ -175,6 +181,35 @@ public:
/// \name Setter Methods
///
//@{
+ /// \brief Set or reset a temporary output buffer.
+ ///
+ /// This method can be used for an application that manages an output
+ /// buffer separately from the message renderer and wants to keep reusing
+ /// the renderer. When the renderer is associated with the default buffer
+ /// and the given pointer is non NULL, the given buffer will be
+ /// (temporarily) used for subsequent message rendering; if the renderer
+ /// is associated with a temporary buffer and the given pointer is NULL,
+ /// the renderer will be reset with the default buffer. In the latter
+ /// case any additional resources (possibly specific to a derived renderer
+ /// class) will be cleared, but the temporary buffer is kept as the latest
+ /// state (which would normally store the rendering result).
+ ///
+ /// This method imposes some restrictions to prevent accidental misuse
+ /// that could cause disruption such as dereferencing an invalid object.
+ /// First, a temporary buffer must not be set when the associated buffer
+ /// is in use, that is, any data are stored in the buffer. Also, the
+ /// default buffer cannot be "reset"; when NULL is specified a temporary
+ /// buffer must have been set beforehand. If these conditions aren't met
+ /// an isc::InvalidParameter exception will be thrown. This method is
+ /// exception free otherwise.
+ ///
+ /// \throw isc::InvalidParameter A restrictions of the method usage isn't
+ /// met.
+ ///
+ /// \param buffer A pointer to a temporary output buffer or NULL for reset
+ /// it.
+ void setBuffer(isc::util::OutputBuffer* buffer);
+
/// \brief Mark the renderer to indicate truncation has occurred while
/// rendering.
///
@@ -209,7 +244,7 @@ public:
///
/// \param len The length of the gap to be inserted in bytes.
void skip(size_t len) {
- buffer_.skip(len);
+ buffer_->skip(len);
}
/// \brief Trim the specified length of data from the end of the internal
@@ -223,7 +258,7 @@ public:
///
/// \param len The length of data that should be trimmed.
void trim(size_t len) {
- buffer_.trim(len);
+ buffer_->trim(len);
}
/// \brief Clear the internal buffer and other internal resources.
@@ -236,7 +271,7 @@ public:
///
/// \param data The 8-bit integer to be written into the internal buffer.
void writeUint8(const uint8_t data) {
- buffer_.writeUint8(data);
+ buffer_->writeUint8(data);
}
/// \brief Write an unsigned 16-bit integer in host byte order into the
@@ -244,7 +279,7 @@ public:
///
/// \param data The 16-bit integer to be written into the buffer.
void writeUint16(uint16_t data) {
- buffer_.writeUint16(data);
+ buffer_->writeUint16(data);
}
/// \brief Write an unsigned 16-bit integer in host byte order at the
@@ -259,7 +294,7 @@ public:
/// \param data The 16-bit integer to be written into the internal buffer.
/// \param pos The beginning position in the buffer to write the data.
void writeUint16At(uint16_t data, size_t pos) {
- buffer_.writeUint16At(data, pos);
+ buffer_->writeUint16At(data, pos);
}
/// \brief Write an unsigned 32-bit integer in host byte order into the
@@ -267,7 +302,7 @@ public:
///
/// \param data The 32-bit integer to be written into the buffer.
void writeUint32(uint32_t data) {
- buffer_.writeUint32(data);
+ buffer_->writeUint32(data);
}
/// \brief Copy an arbitrary length of data into the internal buffer
@@ -278,7 +313,7 @@ public:
/// \param data A pointer to the data to be copied into the internal buffer.
/// \param len The length of the data in bytes.
void writeData(const void *data, size_t len) {
- buffer_.writeData(data, len);
+ buffer_->writeData(data, len);
}
/// \brief Write a \c Name object into the internal buffer in wire format,
@@ -316,10 +351,7 @@ public:
using AbstractMessageRenderer::CASE_SENSITIVE;
/// \brief Constructor from an output buffer.
- ///
- /// \param buffer An \c OutputBuffer object to which wire format data is
- /// written.
- MessageRenderer(isc::util::OutputBuffer& buffer);
+ MessageRenderer();
virtual ~MessageRenderer();
virtual bool isTruncated() const;
diff --git a/src/lib/dns/name.h b/src/lib/dns/name.h
index 4ff7fe5..ca64d69 100644
--- a/src/lib/dns/name.h
+++ b/src/lib/dns/name.h
@@ -210,6 +210,11 @@ private:
/// names as a special case.
///
class Name {
+ // LabelSequences use knowledge about the internal data structure
+ // of this class for efficiency (they use the offsets_ vector and
+ // the ndata_ string)
+ friend class LabelSequence;
+
///
/// \name Constructors and Destructor
///
@@ -298,6 +303,7 @@ public:
}
return (ndata_[pos]);
}
+
/// \brief Gets the length of the <code>Name</code> in its wire format.
///
/// This method never throws an exception.
diff --git a/src/lib/dns/nsec3hash.cc b/src/lib/dns/nsec3hash.cc
index 8755959..159dff3 100644
--- a/src/lib/dns/nsec3hash.cc
+++ b/src/lib/dns/nsec3hash.cc
@@ -142,6 +142,23 @@ NSEC3HashRFC5155::match(const generic::NSEC3PARAM& nsec3param) const {
return (match(nsec3param.getHashalg(), nsec3param.getIterations(),
nsec3param.getSalt()));
}
+
+// A static pointer that refers to the currently usable creator.
+// Only get/setNSEC3HashCreator are expected to get access to this variable
+// directly.
+const NSEC3HashCreator* creator;
+
+// The accessor to the current creator. If it's not explicitly set or has
+// been reset from a customized one, the default creator will be used.
+const NSEC3HashCreator*
+getNSEC3HashCreator() {
+ static DefaultNSEC3HashCreator default_creator;
+ if (creator == NULL) {
+ creator = &default_creator;
+ }
+ return (creator);
+}
+
} // end of unnamed namespace
namespace isc {
@@ -149,15 +166,30 @@ namespace dns {
NSEC3Hash*
NSEC3Hash::create(const generic::NSEC3PARAM& param) {
+ return (getNSEC3HashCreator()->create(param));
+}
+
+NSEC3Hash*
+NSEC3Hash::create(const generic::NSEC3& nsec3) {
+ return (getNSEC3HashCreator()->create(nsec3));
+}
+
+NSEC3Hash*
+DefaultNSEC3HashCreator::create(const generic::NSEC3PARAM& param) const {
return (new NSEC3HashRFC5155(param.getHashalg(), param.getIterations(),
param.getSalt()));
}
NSEC3Hash*
-NSEC3Hash::create(const generic::NSEC3& nsec3) {
+DefaultNSEC3HashCreator::create(const generic::NSEC3& nsec3) const {
return (new NSEC3HashRFC5155(nsec3.getHashalg(), nsec3.getIterations(),
nsec3.getSalt()));
}
+void
+setNSEC3HashCreator(const NSEC3HashCreator* new_creator) {
+ creator = new_creator;
+}
+
} // namespace dns
} // namespace isc
diff --git a/src/lib/dns/nsec3hash.h b/src/lib/dns/nsec3hash.h
index 7937e9d..2056708 100644
--- a/src/lib/dns/nsec3hash.h
+++ b/src/lib/dns/nsec3hash.h
@@ -154,6 +154,96 @@ public:
virtual bool match(const rdata::generic::NSEC3PARAM& nsec3param) const = 0;
};
+/// \brief Factory class of NSEC3Hash.
+///
+/// This class is an abstract base class that provides the creation interfaces
+/// of \c NSEC3Hash objects. By defining a specific derived class of the
+/// creator, normally with a different specific class of \c NSEC3Hash,
+/// the application can use a customized implementation of \c NSEC3Hash
+/// without changing the library itself. The intended primary application of
+/// such customization is tests (it would be convenient for a test to produce
+/// a faked hash value regardless of the input so it doesn't have to identify
+/// a specific input value to produce a particular hash). Another possibility
+/// would be an experimental extension for a newer hash algorithm or
+/// implementation.
+///
+/// The two main methods named \c create() correspond to the static factory
+/// methods of \c NSEC3Hash of the same name.
+///
+/// By default, the library uses the \c DefaultNSEC3HashCreator creator.
+/// The \c setNSEC3HashCreator() function can be used to replace it with a
+/// user defined version. For such customization purposes as implementing
+/// experimental new hash algorithms, the application may internally want to
+/// use the \c DefaultNSEC3HashCreator in general cases while creating a
+/// customized type of \c NSEC3Hash object for that particular hash algorithm.
+///
+/// The creator objects are generally expected to be stateless; they will
+/// only encapsulate the factory logic. The \c create() methods are declared
+/// as const member functions for this reason. But if we see the need for
+/// having a customized creator that benefits from its own state in future,
+/// this condition can be loosened.
+class NSEC3HashCreator {
+protected:
+ /// \brief The default constructor.
+ ///
+ /// Make very sure this isn't directly instantiated by making it protected
+ /// even if this class is modified to lose all pure virtual methods.
+ NSEC3HashCreator() {}
+
+public:
+ /// \brief The destructor.
+ ///
+ /// This does nothing; defined only for allowing derived classes to
+ /// specialize its behavior.
+ virtual ~NSEC3HashCreator() {}
+
+ /// \brief Factory method of NSECHash from NSEC3PARAM RDATA.
+ ///
+ /// See
+ /// <code>NSEC3Hash::create(const rdata::generic::NSEC3PARAM& param)</code>
+ virtual NSEC3Hash* create(const rdata::generic::NSEC3PARAM& nsec3param)
+ const = 0;
+
+ /// \brief Factory method of NSECHash from NSEC3 RDATA.
+ ///
+ /// See
+ /// <code>NSEC3Hash::create(const rdata::generic::NSEC3& param)</code>
+ virtual NSEC3Hash* create(const rdata::generic::NSEC3& nsec3)
+ const = 0;
+};
+
+/// \brief The default NSEC3Hash creator.
+///
+/// This derived class implements the \c NSEC3HashCreator interfaces for
+/// the standard NSEC3 hash calculator as defined in RFC5155. The library
+/// will use this creator by default, so normal applications don't have to
+/// be aware of this class at all. This class is publicly visible for the
+/// convenience of special applications that want to customize the creator
+/// behavior for a particular type of parameters while preserving the default
+/// behavior for others.
+class DefaultNSEC3HashCreator : public NSEC3HashCreator {
+public:
+ virtual NSEC3Hash* create(const rdata::generic::NSEC3PARAM& param) const;
+ virtual NSEC3Hash* create(const rdata::generic::NSEC3& nsec3) const;
+};
+
+/// \brief The registrar of \c NSEC3HashCreator.
+///
+/// This function sets or resets the system-wide \c NSEC3HashCreator that
+/// is used by \c NSEC3Hash::create().
+///
+/// If \c new_creator is non NULL, the given creator object will replace
+/// any existing creator. If it's NULL, the default builtin creator will be
+/// used again from that point.
+///
+/// When \c new_creator is non NULL, the caller is responsible for keeping
+/// the referenced object valid as long as it can be used via
+/// \c NSEC3Hash::create().
+///
+/// \exception None
+/// \param new_creator A pointer to the new creator object or NULL.
+void setNSEC3HashCreator(const NSEC3HashCreator* new_creator);
+
}
}
#endif // __NSEC3HASH_H
diff --git a/src/lib/dns/python/message_python.cc b/src/lib/dns/python/message_python.cc
index 48fff94..c7ad2ff 100644
--- a/src/lib/dns/python/message_python.cc
+++ b/src/lib/dns/python/message_python.cc
@@ -377,8 +377,9 @@ Message_getTSIGRecord(s_Message* self) {
if (tsig_record == NULL) {
Py_RETURN_NONE;
+ } else {
+ return (createTSIGRecordObject(*tsig_record));
}
- return (createTSIGRecordObject(*tsig_record));
} catch (const InvalidMessageOperation& ex) {
PyErr_SetString(po_InvalidMessageOperation, ex.what());
} catch (const exception& ex) {
@@ -433,7 +434,7 @@ private:
};
typedef SectionInserter<ConstQuestionPtr, Question> QuestionInserter;
-typedef SectionInserter<ConstRRsetPtr, RRset> RRsetInserter;
+typedef SectionInserter<ConstRRsetPtr, AbstractRRset> RRsetInserter;
// TODO use direct iterators for these? (or simply lists for now?)
PyObject*
diff --git a/src/lib/dns/python/messagerenderer_python.cc b/src/lib/dns/python/messagerenderer_python.cc
index bb89622..5561c12 100644
--- a/src/lib/dns/python/messagerenderer_python.cc
+++ b/src/lib/dns/python/messagerenderer_python.cc
@@ -36,7 +36,6 @@ namespace {
class s_MessageRenderer : public PyObject {
public:
s_MessageRenderer();
- isc::util::OutputBuffer* outputbuffer;
MessageRenderer* cppobj;
};
@@ -78,17 +77,14 @@ PyMethodDef MessageRenderer_methods[] = {
int
MessageRenderer_init(s_MessageRenderer* self) {
- self->outputbuffer = new OutputBuffer(4096);
- self->cppobj = new MessageRenderer(*self->outputbuffer);
+ self->cppobj = new MessageRenderer;
return (0);
}
void
MessageRenderer_destroy(s_MessageRenderer* self) {
delete self->cppobj;
- delete self->outputbuffer;
self->cppobj = NULL;
- self->outputbuffer = NULL;
Py_TYPE(self)->tp_free(self);
}
diff --git a/src/lib/dns/python/rrset_python.cc b/src/lib/dns/python/rrset_python.cc
index 77d520b..2992522 100644
--- a/src/lib/dns/python/rrset_python.cc
+++ b/src/lib/dns/python/rrset_python.cc
@@ -415,7 +415,7 @@ PyTypeObject rrset_type = {
};
PyObject*
-createRRsetObject(const RRset& source) {
+createRRsetObject(const AbstractRRset& source) {
// RRsets are noncopyable, so as a workaround we recreate a new one
// and copy over all content
@@ -450,7 +450,7 @@ PyRRset_Check(PyObject* obj) {
return (PyObject_TypeCheck(obj, &rrset_type));
}
-RRset&
+AbstractRRset&
PyRRset_ToRRset(PyObject* rrset_obj) {
s_RRset* rrset = static_cast<s_RRset*>(rrset_obj);
return (*rrset->cppobj);
diff --git a/src/lib/dns/python/rrset_python.h b/src/lib/dns/python/rrset_python.h
index 4268678..2435397 100644
--- a/src/lib/dns/python/rrset_python.h
+++ b/src/lib/dns/python/rrset_python.h
@@ -36,7 +36,7 @@ extern PyTypeObject rrset_type;
/// returns a NULL pointer).
/// This function is expected to be called within a try block
/// followed by necessary setup for python exception.
-PyObject* createRRsetObject(const RRset& source);
+PyObject* createRRsetObject(const AbstractRRset& source);
/// \brief Checks if the given python object is a RRset object
///
@@ -56,7 +56,7 @@ bool PyRRset_Check(PyObject* obj);
/// may be destroyed, the caller must copy it itself.
///
/// \param rrset_obj The rrset object to convert
-RRset& PyRRset_ToRRset(PyObject* rrset_obj);
+AbstractRRset& PyRRset_ToRRset(PyObject* rrset_obj);
/// \brief Returns the shared_ptr of the RRset object contained within the
/// given Python object.
diff --git a/src/lib/dns/python/tests/.gitignore b/src/lib/dns/python/tests/.gitignore
new file mode 100644
index 0000000..225fc6f
--- /dev/null
+++ b/src/lib/dns/python/tests/.gitignore
@@ -0,0 +1 @@
+/__pycache__
diff --git a/src/lib/dns/rdata/generic/detail/nsec3param_common.cc b/src/lib/dns/rdata/generic/detail/nsec3param_common.cc
new file mode 100644
index 0000000..a7a0bb4
--- /dev/null
+++ b/src/lib/dns/rdata/generic/detail/nsec3param_common.cc
@@ -0,0 +1,130 @@
+// 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/encode/hex.h>
+#include <util/buffer.h>
+
+#include <dns/exceptions.h>
+#include <dns/rdata.h>
+#include <dns/rdata/generic/detail/nsec3param_common.h>
+
+#include <boost/lexical_cast.hpp>
+
+#include <sstream>
+#include <vector>
+#include <stdint.h>
+
+using namespace std;
+using namespace isc::util;
+using namespace isc::util::encode;
+
+namespace isc {
+namespace dns {
+namespace rdata {
+namespace generic {
+namespace detail {
+namespace nsec3 {
+
+ParseNSEC3ParamResult
+parseNSEC3ParamText(const char* const rrtype_name,
+ const string& rdata_str, istringstream& iss,
+ vector<uint8_t>& salt)
+{
+ unsigned int hashalg, flags, iterations;
+ string iterations_str, salthex;
+
+ iss >> hashalg >> flags >> iterations_str >> salthex;
+ if (iss.bad() || iss.fail()) {
+ isc_throw(InvalidRdataText, "Invalid " << rrtype_name <<
+ " text: " << rdata_str);
+ }
+ if (hashalg > 0xff) {
+ isc_throw(InvalidRdataText, rrtype_name <<
+ " hash algorithm out of range: " << hashalg);
+ }
+ if (flags > 0xff) {
+ isc_throw(InvalidRdataText, rrtype_name << " flags out of range: " <<
+ flags);
+ }
+ // Convert iteration. To reject an invalid case where there's no space
+ // between iteration and salt, we extract this field as string and convert
+ // to integer.
+ try {
+ iterations = boost::lexical_cast<unsigned int>(iterations_str);
+ } catch (const boost::bad_lexical_cast&) {
+ isc_throw(InvalidRdataText, "Bad " << rrtype_name <<
+ " iteration: " << iterations_str);
+ }
+ if (iterations > 0xffff) {
+ isc_throw(InvalidRdataText, rrtype_name <<
+ " iterations out of range: " <<
+ iterations);
+ }
+
+ // Salt is up to 255 bytes, and space is not allowed in the HEX encoding,
+ // so the encoded string cannot be longer than the double of max length
+ // of the actual salt.
+ if (salthex.size() > 255 * 2) {
+ isc_throw(InvalidRdataText, rrtype_name << " salt is too long: "
+ << salthex.size() << " (encoded) bytes");
+ }
+ if (salthex != "-") { // "-" means a 0-length salt
+ decodeHex(salthex, salt);
+ }
+
+ return (ParseNSEC3ParamResult(hashalg, flags, iterations));
+}
+
+ParseNSEC3ParamResult
+parseNSEC3ParamWire(const char* const rrtype_name,
+ InputBuffer& buffer,
+ size_t& rdata_len, std::vector<uint8_t>& salt)
+{
+ // NSEC3/NSEC3PARAM RR must have at least 5 octets:
+ // hash algorithm(1), flags(1), iteration(2), saltlen(1)
+ if (rdata_len < 5) {
+ isc_throw(DNSMessageFORMERR, rrtype_name << " too short, length: "
+ << rdata_len);
+ }
+
+ const uint8_t hashalg = buffer.readUint8();
+ const uint8_t flags = buffer.readUint8();
+ const uint16_t iterations = buffer.readUint16();
+
+ const uint8_t saltlen = buffer.readUint8();
+ rdata_len -= 5;
+ if (rdata_len < saltlen) {
+ isc_throw(DNSMessageFORMERR, rrtype_name <<
+ " salt length is too large: " <<
+ static_cast<unsigned int>(saltlen));
+ }
+
+ salt.resize(saltlen);
+ if (saltlen > 0) {
+ buffer.readData(&salt[0], saltlen);
+ rdata_len -= saltlen;
+ }
+
+ return (ParseNSEC3ParamResult(hashalg, flags, iterations));
+}
+
+} // end of nsec3
+} // end of detail
+} // end of generic
+} // end of rdata
+} // end of dns
+} // end of isc
+
diff --git a/src/lib/dns/rdata/generic/detail/nsec3param_common.h b/src/lib/dns/rdata/generic/detail/nsec3param_common.h
new file mode 100644
index 0000000..515777b
--- /dev/null
+++ b/src/lib/dns/rdata/generic/detail/nsec3param_common.h
@@ -0,0 +1,134 @@
+// 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 __NSEC3PARAM_COMMON_H
+#define __NSEC3PARAM_COMMON_H 1
+
+#include <util/buffer.h>
+
+#include <stdint.h>
+
+#include <sstream>
+#include <string>
+#include <vector>
+
+namespace isc {
+namespace dns {
+namespace rdata {
+namespace generic {
+namespace detail {
+namespace nsec3 {
+
+/// \file
+///
+/// This helper module provides some utilities that handle NSEC3 and
+/// NSEC3PARAM RDATA. They share the first few fields, and some operations
+/// on these fields are sufficiently complicated, so it would make sense to
+/// consolidate the processing logic into a single implementation module.
+///
+/// The functions defined here are essentially private and are only expected
+/// to be called from the \c NSEC3 and \c NSEC3PARAM class implementations.
+
+/// \brief Result values of the utilities.
+///
+/// This structure encapsulates a tuple of NSEC3/NSEC3PARAM algorithm,
+/// flags and iterations field values. This is used as the return value
+/// of the utility functions defined in this module so the caller can
+/// use it for constructing the corresponding RDATA.
+struct ParseNSEC3ParamResult {
+ ParseNSEC3ParamResult(uint8_t param_algorithm, uint8_t param_flags,
+ uint16_t param_iterations) :
+ algorithm(param_algorithm), flags(param_flags),
+ iterations(param_iterations)
+ {}
+ const uint8_t algorithm;
+ const uint8_t flags;
+ const uint16_t iterations;
+};
+
+/// \brief Convert textual representation of NSEC3 parameters.
+///
+/// This function takes an input string stream that consists of a complete
+/// textual representation of an NSEC3 or NSEC3PARAM RDATA and parses it
+/// extracting the hash algorithm, flags, iterations, and salt fields.
+///
+/// The first three fields are returned as the return value of this function.
+/// The salt will be stored in the given vector. The vector is expected
+/// to be empty, but if not, the existing content will be overridden.
+///
+/// On successful return the given input stream will reach the end of the
+/// salt field.
+///
+/// \exception isc::BadValue The salt is not a valid hex string.
+/// \exception InvalidRdataText The given string is otherwise invalid for
+/// NSEC3 or NSEC3PARAM fields.
+///
+/// \param rrtype_name Either "NSEC3" or "NSEC3PARAM"; used as part of
+/// exception messages.
+/// \param rdata_str A complete textual string of the RDATA; used as part of
+/// exception messages.
+/// \param iss Input stream that consists of a complete textual string of
+/// the RDATA.
+/// \param salt A placeholder for the salt field value of the RDATA.
+/// Expected to be empty, but it's not checked (and will be overridden).
+///
+/// \return The hash algorithm, flags, iterations in the form of
+/// ParseNSEC3ParamResult.
+ParseNSEC3ParamResult parseNSEC3ParamText(const char* const rrtype_name,
+ const std::string& rdata_str,
+ std::istringstream& iss,
+ std::vector<uint8_t>& salt);
+
+/// \brief Extract NSEC3 parameters from wire-format data.
+///
+/// This function takes an input buffer that stores wire-format NSEC3 or
+/// NSEC3PARAM RDATA and parses it extracting the hash algorithm, flags,
+/// iterations, and salt fields.
+///
+/// The first three fields are returned as the return value of this function.
+/// The salt will be stored in the given vector. The vector is expected
+/// to be empty, but if not, the existing content will be overridden.
+///
+/// On successful return the input buffer will point to the end of the
+/// salt field; rdata_len will be the length of the rest of RDATA
+/// (in the case of a valid NSEC3PARAM, it should be 0).
+///
+/// \exception DNSMessageFORMERR The wire data is invalid.
+///
+/// \param rrtype_name Either "NSEC3" or "NSEC3PARAM"; used as part of
+/// exception messages.
+/// \param buffer An input buffer that stores wire-format RDATA. It must
+/// point to the beginning of the data.
+/// \param rdata_len The total length of the RDATA.
+/// \param salt A placeholder for the salt field value of the RDATA.
+/// Expected to be empty, but it's not checked (and will be overridden).
+///
+/// \return The hash algorithm, flags, iterations in the form of
+/// ParseNSEC3ParamResult.
+ParseNSEC3ParamResult parseNSEC3ParamWire(const char* const rrtype_name,
+ isc::util::InputBuffer& buffer,
+ size_t& rdata_len,
+ std::vector<uint8_t>& salt);
+}
+}
+}
+}
+}
+}
+
+#endif // __NSEC3PARAM_COMMON_H
+
+// Local Variables:
+// mode: c++
+// End:
diff --git a/src/lib/dns/rdata/generic/detail/nsec_bitmap.cc b/src/lib/dns/rdata/generic/detail/nsec_bitmap.cc
index a72058f..bb48705 100644
--- a/src/lib/dns/rdata/generic/detail/nsec_bitmap.cc
+++ b/src/lib/dns/rdata/generic/detail/nsec_bitmap.cc
@@ -12,11 +12,17 @@
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
-#include <stdint.h>
-
-#include <vector>
+#include <exceptions/exceptions.h>
#include <dns/exceptions.h>
+#include <dns/rdata.h>
+#include <dns/rrtype.h>
+
+#include <cassert>
+#include <sstream>
+#include <vector>
+#include <cstring>
+#include <stdint.h>
using namespace std;
@@ -70,6 +76,78 @@ checkRRTypeBitmaps(const char* const rrtype_name,
first = false;
}
}
+
+void
+buildBitmapsFromText(const char* const rrtype_name,
+ istringstream& iss, vector<uint8_t>& typebits)
+{
+ uint8_t bitmap[8 * 1024]; // 64k bits
+ memset(bitmap, 0, sizeof(bitmap));
+
+ do {
+ string type;
+ iss >> type;
+ if (iss.bad() || iss.fail()) {
+ isc_throw(InvalidRdataText, "Unexpected input for "
+ << rrtype_name << " bitmap");
+ }
+ try {
+ const int code = RRType(type).getCode();
+ bitmap[code / 8] |= (0x80 >> (code % 8));
+ } catch (const InvalidRRType&) {
+ isc_throw(InvalidRdataText, "Invalid RRtype in "
+ << rrtype_name << " bitmap: " << type);
+ }
+ } while (!iss.eof());
+
+ for (int window = 0; window < 256; ++window) {
+ int octet;
+ for (octet = 31; octet >= 0; octet--) {
+ if (bitmap[window * 32 + octet] != 0) {
+ break;
+ }
+ }
+ if (octet < 0) {
+ continue;
+ }
+ typebits.push_back(window);
+ typebits.push_back(octet + 1);
+ for (int i = 0; i <= octet; ++i) {
+ typebits.push_back(bitmap[window * 32 + i]);
+ }
+ }
+}
+
+void
+bitmapsToText(const vector<uint8_t>& typebits, ostringstream& oss) {
+ // In the following loop we use string::at() rather than operator[].
+ // Since the index calculation is a bit complicated, it will be safer
+ // and easier to find a bug (if any). Note that this conversion method
+ // is generally not expected to be very efficient, so the slight overhead
+ // of at() should be acceptable.
+ const size_t typebits_len = typebits.size();
+ size_t len = 0;
+ for (size_t i = 0; i < typebits_len; i += len) {
+ assert(i + 2 <= typebits.size());
+ const unsigned int block = typebits.at(i);
+ len = typebits.at(i + 1);
+ assert(len > 0 && len <= 32);
+ i += 2;
+ for (size_t j = 0; j < len; ++j) {
+ if (typebits.at(i + j) == 0) {
+ continue;
+ }
+ for (size_t k = 0; k < 8; ++k) {
+ if ((typebits.at(i + j) & (0x80 >> k)) == 0) {
+ continue;
+ }
+ const unsigned int t = block * 256 + j * 8 + k;
+ assert(t < 65536);
+ oss << " " << RRType(t);
+ }
+ }
+ }
+}
}
}
}
diff --git a/src/lib/dns/rdata/generic/detail/nsec_bitmap.h b/src/lib/dns/rdata/generic/detail/nsec_bitmap.h
index 6431e10..85cae2e 100644
--- a/src/lib/dns/rdata/generic/detail/nsec_bitmap.h
+++ b/src/lib/dns/rdata/generic/detail/nsec_bitmap.h
@@ -12,8 +12,12 @@
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
+#ifndef __NSECBITMAP_H
+#define __NSECBITMAP_H 1
+
#include <stdint.h>
+#include <sstream>
#include <vector>
namespace isc {
@@ -22,14 +26,22 @@ namespace rdata {
namespace generic {
namespace detail {
namespace nsec {
-/// Check if a given "type bitmap" for NSEC/NSEC3 is valid.
+
+/// \file
+///
+/// This helper module provides some utilities that handle NSEC and NSEC3
+/// type bitmaps. The format and processing of the type bitmaps are generally
+/// the same for these two RRs, so it would make sense to consolidate
+/// the processing logic into a single implementation module.
+///
+/// The functions defined here are essentially private and are only expected
+/// to be called from the \c NSEC and \c NSEC3 class implementations.
+
+/// \brief Check if a given "type bitmap" for NSEC/NSEC3 is valid.
///
-/// This helper function checks given wire format data (stored in a
+/// This function checks given wire format data (stored in a
/// \c std::vector) is a valid type bitmaps used for the NSEC and NSEC3 RRs
-/// according to RFC4034 and RFC5155. The validation logic is the same
-/// for these two RRs, so a unified check function is provided.
-/// This function is essentially private and is only expected to be called
-/// from the \c NSEC and \c NSEC3 class implementations.
+/// according to RFC4034 and RFC5155.
///
/// \exception DNSMessageFORMERR The bitmap is not valid.
///
@@ -39,6 +51,48 @@ namespace nsec {
/// is the total length of the bitmaps.
void checkRRTypeBitmaps(const char* const rrtype_name,
const std::vector<uint8_t>& typebits);
+
+/// \brief Convert textual sequence of RR types into type bitmaps.
+///
+/// This function extracts a sequence of strings, converts each sequence
+/// into an RR type, and builds NSEC/NSEC3 type bitmaps with the corresponding
+/// bits for the extracted types being on. The resulting bitmaps (which are
+/// in the wire format according to RFC4034 and RFC5155) are stored in the
+/// given vector. This function expects the given string stream ends with
+/// the sequence.
+///
+/// \exception InvalidRdataText The given input stream does not meet the
+/// assumption (e.g. including invalid form of RR type, not ending with
+/// an RR type string).
+///
+/// \param rrtype_name Either "NSEC" or "NSEC3"; used as part of exception
+/// messages.
+/// \param iss Input stream that consists of a complete sequence of textual
+/// RR types for which the corresponding bits are set.
+/// \param typebits A placeholder for the resulting bitmaps. Expected to be
+/// empty, but it's not checked.
+void buildBitmapsFromText(const char* const rrtype_name,
+ std::istringstream& iss,
+ std::vector<uint8_t>& typebits);
+
+/// \brief Convert type bitmaps to textual sequence of RR types.
+///
+/// This function converts wire-format data of type bitmaps for NSEC/NSEC3
+/// into a sequence of corresponding RR type strings, and inserts them
+/// into the given output stream with separating them by a single space
+/// character.
+///
+/// This function assumes the given bitmaps are valid in terms of RFC4034
+/// and RFC5155 (in practice, it assumes it's from a validly constructed
+/// NSEC or NSEC3 object); if it detects a format error, it aborts the
+/// program with assert().
+///
+/// \param typebits The type bitmaps in wire format. The size of vector
+/// is the total length of the bitmaps.
+/// \param oss The output stream to which the converted RR type sequence
+/// are to be inserted.
+void bitmapsToText(const std::vector<uint8_t>& typebits,
+ std::ostringstream& oss);
}
}
}
@@ -46,6 +100,8 @@ void checkRRTypeBitmaps(const char* const rrtype_name,
}
}
+#endif // __NSECBITMAP_H
+
// Local Variables:
// mode: c++
// End:
diff --git a/src/lib/dns/rdata/generic/nsec3_50.cc b/src/lib/dns/rdata/generic/nsec3_50.cc
index 092695c..b569d91 100644
--- a/src/lib/dns/rdata/generic/nsec3_50.cc
+++ b/src/lib/dns/rdata/generic/nsec3_50.cc
@@ -17,6 +17,7 @@
#include <string>
#include <sstream>
#include <vector>
+#include <cassert>
#include <boost/lexical_cast.hpp>
@@ -32,12 +33,14 @@
#include <dns/rdata.h>
#include <dns/rdataclass.h>
#include <dns/rdata/generic/detail/nsec_bitmap.h>
+#include <dns/rdata/generic/detail/nsec3param_common.h>
#include <stdio.h>
#include <time.h>
using namespace std;
using namespace isc::dns::rdata::generic::detail::nsec;
+using namespace isc::dns::rdata::generic::detail::nsec3;
using namespace isc::util::encode;
using namespace isc::util;
@@ -53,54 +56,32 @@ struct NSEC3Impl {
salt_(salt), next_(next), typebits_(typebits)
{}
- uint8_t hashalg_;
- uint8_t flags_;
- uint16_t iterations_;
- vector<uint8_t> salt_;
- vector<uint8_t> next_;
- vector<uint8_t> typebits_;
+ const uint8_t hashalg_;
+ const uint8_t flags_;
+ const uint16_t iterations_;
+ const vector<uint8_t> salt_;
+ const vector<uint8_t> next_;
+ const vector<uint8_t> typebits_;
};
NSEC3::NSEC3(const string& nsec3_str) :
impl_(NULL)
{
istringstream iss(nsec3_str);
- unsigned int hashalg, flags, iterations;
- string iterations_str, salthex, nexthash;
+ vector<uint8_t> salt;
+ const ParseNSEC3ParamResult params =
+ parseNSEC3ParamText("NSEC3", nsec3_str, iss, salt);
- iss >> hashalg >> flags >> iterations_str >> salthex >> nexthash;
+ // Extract Next hash. It must be an unpadded base32hex string.
+ string nexthash;
+ iss >> nexthash;
if (iss.bad() || iss.fail()) {
isc_throw(InvalidRdataText, "Invalid NSEC3 text: " << nsec3_str);
}
- if (hashalg > 0xff) {
- isc_throw(InvalidRdataText,
- "NSEC3 hash algorithm out of range: " << hashalg);
- }
- if (flags > 0xff) {
- isc_throw(InvalidRdataText, "NSEC3 flags out of range: " << flags);
- }
- // Convert iteration. To reject an invalid case where there's no space
- // between iteration and salt, we extract this field as string and convert
- // to integer.
- try {
- iterations = boost::lexical_cast<unsigned int>(iterations_str);
- } catch (const boost::bad_lexical_cast&) {
- isc_throw(InvalidRdataText, "Bad NSEC3 iteration: " << iterations_str);
- }
- if (iterations > 0xffff) {
- isc_throw(InvalidRdataText, "NSEC3 iterations out of range: " <<
- iterations);
- }
-
- vector<uint8_t> salt;
- if (salthex != "-") { // "-" means a 0-length salt
- decodeHex(salthex, salt);
- }
- if (salt.size() > 255) {
- isc_throw(InvalidRdataText, "NSEC3 salt is too long: "
- << salt.size() << " bytes");
+ assert(!nexthash.empty());
+ if (*nexthash.rbegin() == '=') {
+ isc_throw(InvalidRdataText, "NSEC3 hash has padding: " << nsec3_str);
}
-
vector<uint8_t> next;
decodeBase32Hex(nexthash, next);
if (next.size() > 255) {
@@ -110,70 +91,28 @@ NSEC3::NSEC3(const string& nsec3_str) :
// For NSEC3 empty bitmap is possible and allowed.
if (iss.eof()) {
- impl_ = new NSEC3Impl(hashalg, flags, iterations, salt, next,
+ impl_ = new NSEC3Impl(params.algorithm, params.flags,
+ params.iterations, salt, next,
vector<uint8_t>());
return;
}
vector<uint8_t> typebits;
- uint8_t bitmap[8 * 1024]; // 64k bits
- memset(bitmap, 0, sizeof(bitmap));
- do {
- string type;
- iss >> type;
- if (type.length() != 0) {
- try {
- const int code = RRType(type).getCode();
- bitmap[code / 8] |= (0x80 >> (code % 8));
- } catch (...) {
- isc_throw(InvalidRdataText, "Invalid RRtype in NSEC3");
- }
- }
- } while (!iss.eof());
-
- for (int window = 0; window < 256; window++) {
- int octet;
- for (octet = 31; octet >= 0; octet--) {
- if (bitmap[window * 32 + octet] != 0) {
- break;
- }
- }
- if (octet < 0)
- continue;
- typebits.push_back(window);
- typebits.push_back(octet + 1);
- for (int i = 0; i <= octet; i++) {
- typebits.push_back(bitmap[window * 32 + i]);
- }
- }
+ buildBitmapsFromText("NSEC3", iss, typebits);
- impl_ = new NSEC3Impl(hashalg, flags, iterations, salt, next, typebits);
+ impl_ = new NSEC3Impl(params.algorithm, params.flags, params.iterations,
+ salt, next, typebits);
}
NSEC3::NSEC3(InputBuffer& buffer, size_t rdata_len) {
- // NSEC3 RR must have at least 5 octets:
- // hash algorithm(1), flags(1), iteration(2), saltlen(1)
- if (rdata_len < 5) {
- isc_throw(DNSMessageFORMERR, "NSEC3 too short, length: " << rdata_len);
- }
-
- const uint8_t hashalg = buffer.readUint8();
- const uint8_t flags = buffer.readUint8();
- const uint16_t iterations = buffer.readUint16();
-
- const uint8_t saltlen = buffer.readUint8();
- rdata_len -= 5;
- if (rdata_len < saltlen) {
- isc_throw(DNSMessageFORMERR, "NSEC3 salt length is too large: " <<
- static_cast<unsigned int>(saltlen));
- }
+ vector<uint8_t> salt;
+ const ParseNSEC3ParamResult params =
+ parseNSEC3ParamWire("NSEC3", buffer, rdata_len, salt);
- vector<uint8_t> salt(saltlen);
- if (saltlen > 0) {
- buffer.readData(&salt[0], saltlen);
- rdata_len -= saltlen;
+ if (rdata_len < 1) {
+ isc_throw(DNSMessageFORMERR, "NSEC3 too short to contain hash length, "
+ "length: " << rdata_len + salt.size() + 5);
}
-
const uint8_t nextlen = buffer.readUint8();
--rdata_len;
if (nextlen == 0 || rdata_len < nextlen) {
@@ -193,7 +132,8 @@ NSEC3::NSEC3(InputBuffer& buffer, size_t rdata_len) {
checkRRTypeBitmaps("NSEC3", typebits);
}
- impl_ = new NSEC3Impl(hashalg, flags, iterations, salt, next, typebits);
+ impl_ = new NSEC3Impl(params.algorithm, params.flags, params.iterations,
+ salt, next, typebits);
}
NSEC3::NSEC3(const NSEC3& source) :
@@ -220,57 +160,78 @@ NSEC3::~NSEC3() {
string
NSEC3::toText() const {
ostringstream s;
- int len = 0;
- for (size_t i = 0; i < impl_->typebits_.size(); i += len) {
- assert(i + 2 <= impl_->typebits_.size());
- int window = impl_->typebits_[i];
- len = impl_->typebits_[i + 1];
- assert(len >= 0 && len < 32);
- i += 2;
- for (int j = 0; j < len; j++) {
- if (impl_->typebits_[i + j] == 0) {
- continue;
- }
- for (int k = 0; k < 8; k++) {
- if ((impl_->typebits_[i + j] & (0x80 >> k)) == 0) {
- continue;
- }
- int t = window * 256 + j * 8 + k;
- s << " " << RRType(t).toText();
- }
- }
- }
+ bitmapsToText(impl_->typebits_, s);
using namespace boost;
return (lexical_cast<string>(static_cast<int>(impl_->hashalg_)) +
- " " + lexical_cast<string>(static_cast<int>(impl_->flags_)) +
- " " + lexical_cast<string>(static_cast<int>(impl_->iterations_)) +
- " " + encodeHex(impl_->salt_) +
- " " + encodeBase32Hex(impl_->next_) + s.str());
+ " " + lexical_cast<string>(static_cast<int>(impl_->flags_)) +
+ " " + lexical_cast<string>(static_cast<int>(impl_->iterations_)) +
+ " " + (impl_->salt_.empty() ? "-" : encodeHex(impl_->salt_)) +
+ " " + encodeBase32Hex(impl_->next_) + s.str());
+}
+
+template <typename OUTPUT_TYPE>
+void
+toWireHelper(const NSEC3Impl& impl, OUTPUT_TYPE& output) {
+ output.writeUint8(impl.hashalg_);
+ output.writeUint8(impl.flags_);
+ output.writeUint16(impl.iterations_);
+ output.writeUint8(impl.salt_.size());
+ if (!impl.salt_.empty()) {
+ output.writeData(&impl.salt_[0], impl.salt_.size());
+ }
+ assert(!impl.next_.empty());
+ output.writeUint8(impl.next_.size());
+ output.writeData(&impl.next_[0], impl.next_.size());
+ if (!impl.typebits_.empty()) {
+ output.writeData(&impl.typebits_[0], impl.typebits_.size());
+ }
}
void
NSEC3::toWire(OutputBuffer& buffer) const {
- buffer.writeUint8(impl_->hashalg_);
- buffer.writeUint8(impl_->flags_);
- buffer.writeUint16(impl_->iterations_);
- buffer.writeUint8(impl_->salt_.size());
- buffer.writeData(&impl_->salt_[0], impl_->salt_.size());
- buffer.writeUint8(impl_->next_.size());
- buffer.writeData(&impl_->next_[0], impl_->next_.size());
- buffer.writeData(&impl_->typebits_[0], impl_->typebits_.size());
+ toWireHelper(*impl_, buffer);
}
void
NSEC3::toWire(AbstractMessageRenderer& renderer) const {
- renderer.writeUint8(impl_->hashalg_);
- renderer.writeUint8(impl_->flags_);
- renderer.writeUint16(impl_->iterations_);
- renderer.writeUint8(impl_->salt_.size());
- renderer.writeData(&impl_->salt_[0], impl_->salt_.size());
- renderer.writeUint8(impl_->next_.size());
- renderer.writeData(&impl_->next_[0], impl_->next_.size());
- renderer.writeData(&impl_->typebits_[0], impl_->typebits_.size());
+ toWireHelper(*impl_, renderer);
+}
+
+namespace {
+// This is a helper subroutine for compare(). It compares two binary
+// data stored in vector<uint8_t> objects based on the "Canonical RR Ordering"
+// as defined in Section 6.3 of RFC4034, that is, the data are treated
+// "as a left-justified unsigned octet sequence in which the absence of an
+// octet sorts before a zero octet."
+//
+// If check_length_first is true, it treats the compared data as if they
+// began with a single-octet "length" field whose value is the size of the
+// corresponding vector. In this case, if the sizes of the two vectors are
+// different the shorter one is always considered the "smaller"; the contents
+// of the vector don't matter.
+//
+// This function returns:
+// -1 if v1 is considered smaller than v2
+// 1 if v1 is considered larger than v2
+// 0 otherwise
+int
+compareVectors(const vector<uint8_t>& v1, const vector<uint8_t>& v2,
+ bool check_length_first = true)
+{
+ const size_t len1 = v1.size();
+ const size_t len2 = v2.size();
+ if (check_length_first && len1 != len2) {
+ return (len1 - len2);
+ }
+ const size_t cmplen = min(len1, len2);
+ const int cmp = cmplen == 0 ? 0 : memcmp(&v1.at(0), &v2.at(0), cmplen);
+ if (cmp != 0) {
+ return (cmp);
+ } else {
+ return (len1 - len2);
+ }
+}
}
int
@@ -287,44 +248,18 @@ NSEC3::compare(const Rdata& other) const {
return (impl_->iterations_ < other_nsec3.impl_->iterations_ ? -1 : 1);
}
- size_t this_len = impl_->salt_.size();
- size_t other_len = other_nsec3.impl_->salt_.size();
- size_t cmplen = min(this_len, other_len);
- int cmp = memcmp(&impl_->salt_[0], &other_nsec3.impl_->salt_[0], cmplen);
- if (cmp != 0) {
- return (cmp);
- } else if (this_len < other_len) {
- return (-1);
- } else if (this_len > other_len) {
- return (1);
- }
-
- this_len = impl_->salt_.size();
- other_len = other_nsec3.impl_->salt_.size();
- cmplen = min(this_len, other_len);
- cmp = memcmp(&impl_->next_[0], &other_nsec3.impl_->next_[0], cmplen);
+ int cmp = compareVectors(impl_->salt_, other_nsec3.impl_->salt_);
if (cmp != 0) {
return (cmp);
- } else if (this_len < other_len) {
- return (-1);
- } else if (this_len > other_len) {
- return (1);
}
-
- this_len = impl_->typebits_.size();
- other_len = other_nsec3.impl_->typebits_.size();
- cmplen = min(this_len, other_len);
- cmp = memcmp(&impl_->typebits_[0], &other_nsec3.impl_->typebits_[0],
- cmplen);
+ cmp = compareVectors(impl_->next_, other_nsec3.impl_->next_);
if (cmp != 0) {
return (cmp);
- } else if (this_len < other_len) {
- return (-1);
- } else if (this_len > other_len) {
- return (1);
- } else {
- return (0);
}
+ // Note that bitmap doesn't have a dedicated length field, so we shouldn't
+ // terminate the comparison just because the lengths are different.
+ return (compareVectors(impl_->typebits_, other_nsec3.impl_->typebits_,
+ false));
}
uint8_t
diff --git a/src/lib/dns/rdata/generic/nsec3param_51.cc b/src/lib/dns/rdata/generic/nsec3param_51.cc
index 850be14..ac09b57 100644
--- a/src/lib/dns/rdata/generic/nsec3param_51.cc
+++ b/src/lib/dns/rdata/generic/nsec3param_51.cc
@@ -12,22 +12,19 @@
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
-#include <iostream>
-#include <string>
-#include <sstream>
-#include <vector>
-
-#include <boost/lexical_cast.hpp>
-
#include <util/buffer.h>
#include <util/encode/hex.h>
+
#include <dns/messagerenderer.h>
-#include <dns/name.h>
#include <dns/rdata.h>
#include <dns/rdataclass.h>
+#include <dns/rdata/generic/detail/nsec3param_common.h>
+
+#include <boost/lexical_cast.hpp>
-#include <stdio.h>
-#include <time.h>
+#include <string>
+#include <sstream>
+#include <vector>
using namespace std;
using namespace isc::util;
@@ -43,9 +40,9 @@ struct NSEC3PARAMImpl {
hashalg_(hashalg), flags_(flags), iterations_(iterations), salt_(salt)
{}
- uint8_t hashalg_;
- uint8_t flags_;
- uint16_t iterations_;
+ const uint8_t hashalg_;
+ const uint8_t flags_;
+ const uint16_t iterations_;
const vector<uint8_t> salt_;
};
@@ -53,50 +50,26 @@ NSEC3PARAM::NSEC3PARAM(const string& nsec3param_str) :
impl_(NULL)
{
istringstream iss(nsec3param_str);
- uint16_t hashalg, flags, iterations;
- stringbuf saltbuf;
-
- iss >> hashalg >> flags >> iterations >> &saltbuf;
- if (iss.bad() || iss.fail()) {
- isc_throw(InvalidRdataText, "Invalid NSEC3PARAM text");
- }
- if (hashalg > 0xf) {
- isc_throw(InvalidRdataText, "NSEC3PARAM hash algorithm out of range");
- }
- if (flags > 0xff) {
- isc_throw(InvalidRdataText, "NSEC3PARAM flags out of range");
- }
-
- const string salt_str = saltbuf.str();
vector<uint8_t> salt;
- if (salt_str != "-") { // "-" means an empty salt, no need to touch vector
- decodeHex(saltbuf.str(), salt);
+ const ParseNSEC3ParamResult params =
+ parseNSEC3ParamText("NSEC3PARAM", nsec3param_str, iss, salt);
+
+ if (!iss.eof()) {
+ isc_throw(InvalidRdataText, "Invalid NSEC3PARAM (redundant text): "
+ << nsec3param_str);
}
- impl_ = new NSEC3PARAMImpl(hashalg, flags, iterations, salt);
+ impl_ = new NSEC3PARAMImpl(params.algorithm, params.flags,
+ params.iterations, salt);
}
NSEC3PARAM::NSEC3PARAM(InputBuffer& buffer, size_t rdata_len) {
- if (rdata_len < 4) {
- isc_throw(InvalidRdataLength, "NSEC3PARAM too short");
- }
-
- uint8_t hashalg = buffer.readUint8();
- uint8_t flags = buffer.readUint8();
- uint16_t iterations = buffer.readUint16();
- rdata_len -= 4;
-
- uint8_t saltlen = buffer.readUint8();
- --rdata_len;
-
- if (rdata_len < saltlen) {
- isc_throw(InvalidRdataLength, "NSEC3PARAM salt too short");
- }
-
- vector<uint8_t> salt(saltlen);
- buffer.readData(&salt[0], saltlen);
+ vector<uint8_t> salt;
+ const ParseNSEC3ParamResult params =
+ parseNSEC3ParamWire("NSEC3PARAM", buffer, rdata_len, salt);
- impl_ = new NSEC3PARAMImpl(hashalg, flags, iterations, salt);
+ impl_ = new NSEC3PARAMImpl(params.algorithm, params.flags,
+ params.iterations, salt);
}
NSEC3PARAM::NSEC3PARAM(const NSEC3PARAM& source) :
@@ -124,27 +97,31 @@ string
NSEC3PARAM::toText() const {
using namespace boost;
return (lexical_cast<string>(static_cast<int>(impl_->hashalg_)) +
- " " + lexical_cast<string>(static_cast<int>(impl_->flags_)) +
- " " + lexical_cast<string>(static_cast<int>(impl_->iterations_)) +
- " " + encodeHex(impl_->salt_));
+ " " + lexical_cast<string>(static_cast<int>(impl_->flags_)) +
+ " " + lexical_cast<string>(static_cast<int>(impl_->iterations_)) +
+ " " + (impl_->salt_.empty() ? "-" : encodeHex(impl_->salt_)));
+}
+
+template <typename OUTPUT_TYPE>
+void
+toWireHelper(const NSEC3PARAMImpl& impl, OUTPUT_TYPE& output) {
+ output.writeUint8(impl.hashalg_);
+ output.writeUint8(impl.flags_);
+ output.writeUint16(impl.iterations_);
+ output.writeUint8(impl.salt_.size());
+ if (!impl.salt_.empty()) {
+ output.writeData(&impl.salt_[0], impl.salt_.size());
+ }
}
void
NSEC3PARAM::toWire(OutputBuffer& buffer) const {
- buffer.writeUint8(impl_->hashalg_);
- buffer.writeUint8(impl_->flags_);
- buffer.writeUint16(impl_->iterations_);
- buffer.writeUint8(impl_->salt_.size());
- buffer.writeData(&impl_->salt_[0], impl_->salt_.size());
+ toWireHelper(*impl_, buffer);
}
void
NSEC3PARAM::toWire(AbstractMessageRenderer& renderer) const {
- renderer.writeUint8(impl_->hashalg_);
- renderer.writeUint8(impl_->flags_);
- renderer.writeUint16(impl_->iterations_);
- renderer.writeUint8(impl_->salt_.size());
- renderer.writeData(&impl_->salt_[0], impl_->salt_.size());
+ toWireHelper(*impl_, renderer);
}
int
@@ -161,15 +138,18 @@ NSEC3PARAM::compare(const Rdata& other) const {
return (impl_->iterations_ < other_param.impl_->iterations_ ? -1 : 1);
}
- size_t this_len = impl_->salt_.size();
- size_t other_len = other_param.impl_->salt_.size();
- size_t cmplen = min(this_len, other_len);
- int cmp = memcmp(&impl_->salt_[0], &other_param.impl_->salt_[0],
- cmplen);
+ const size_t this_len = impl_->salt_.size();
+ const size_t other_len = other_param.impl_->salt_.size();
+ if (this_len != other_len) {
+ return (this_len - other_len);
+ }
+ const size_t cmplen = min(this_len, other_len);
+ const int cmp = (cmplen == 0) ? 0 :
+ memcmp(&impl_->salt_.at(0), &other_param.impl_->salt_.at(0), cmplen);
if (cmp != 0) {
return (cmp);
} else {
- return ((this_len == other_len) ? 0 : (this_len < other_len) ? -1 : 1);
+ return (this_len - other_len);
}
}
@@ -193,6 +173,5 @@ NSEC3PARAM::getSalt() const {
return (impl_->salt_);
}
-
// END_RDATA_NAMESPACE
// END_ISC_NAMESPACE
diff --git a/src/lib/dns/rdata/generic/nsec_47.cc b/src/lib/dns/rdata/generic/nsec_47.cc
index a9a9f75..08825db 100644
--- a/src/lib/dns/rdata/generic/nsec_47.cc
+++ b/src/lib/dns/rdata/generic/nsec_47.cc
@@ -54,42 +54,18 @@ NSEC::NSEC(const string& nsec_str) :
{
istringstream iss(nsec_str);
string nextname;
- uint8_t bitmap[8 * 1024]; // 64k bits
- vector<uint8_t> typebits;
iss >> nextname;
if (iss.bad() || iss.fail()) {
isc_throw(InvalidRdataText, "Invalid NSEC name");
}
-
- memset(bitmap, 0, sizeof(bitmap));
- do {
- string type;
- iss >> type;
- try {
- const int code = RRType(type).getCode();
- bitmap[code / 8] |= (0x80 >> (code % 8));
- } catch (...) {
- isc_throw(InvalidRdataText, "Invalid RRtype in NSEC");
- }
- } while (!iss.eof());
-
- for (int window = 0; window < 256; window++) {
- int octet;
- for (octet = 31; octet >= 0; octet--) {
- if (bitmap[window * 32 + octet] != 0) {
- break;
- }
- }
- if (octet < 0)
- continue;
- typebits.push_back(window);
- typebits.push_back(octet + 1);
- for (int i = 0; i <= octet; i++) {
- typebits.push_back(bitmap[window * 32 + i]);
- }
+ if (iss.eof()) {
+ isc_throw(InvalidRdataText, "NSEC bitmap is missing");
}
+ vector<uint8_t> typebits;
+ buildBitmapsFromText("NSEC", iss, typebits);
+
impl_ = new NSECImpl(Name(nextname), typebits);
}
@@ -135,34 +111,8 @@ NSEC::~NSEC() {
string
NSEC::toText() const {
ostringstream s;
- int len = 0;
s << impl_->nextname_;
-
- // In the following loop we use string::at() rather than operator[].
- // Since the index calculation is a bit complicated, it will be safer
- // and easier to find a bug (if any). Note that this conversion method
- // is generally not expected to be very efficient, so the slight overhead
- // of at() should be acceptable.
- for (size_t i = 0; i < impl_->typebits_.size(); i += len) {
- assert(i + 2 <= impl_->typebits_.size());
- const int block = impl_->typebits_.at(i);
- len = impl_->typebits_.at(i + 1);
- assert(len > 0 && len <= 32);
- i += 2;
- for (int j = 0; j < len; j++) {
- if (impl_->typebits_.at(i + j) == 0) {
- continue;
- }
- for (int k = 0; k < 8; k++) {
- if ((impl_->typebits_.at(i + j) & (0x80 >> k)) == 0) {
- continue;
- }
- const int t = block * 256 + j * 8 + k;
- s << " " << RRType(t);
- }
- }
- }
-
+ bitmapsToText(impl_->typebits_, s);
return (s.str());
}
diff --git a/src/lib/dns/rdatafields.cc b/src/lib/dns/rdatafields.cc
index af9ba2e..94c9dbf 100644
--- a/src/lib/dns/rdatafields.cc
+++ b/src/lib/dns/rdatafields.cc
@@ -70,8 +70,7 @@ namespace {
// it's hopefully an acceptable practice.
class RdataFieldComposer : public AbstractMessageRenderer {
public:
- RdataFieldComposer(OutputBuffer& buffer) :
- AbstractMessageRenderer(buffer),
+ RdataFieldComposer() :
truncated_(false), length_limit_(65535),
mode_(CASE_INSENSITIVE), last_data_pos_(0)
{}
@@ -128,8 +127,7 @@ public:
}
RdataFields::RdataFields(const Rdata& rdata) {
- OutputBuffer buffer(0);
- RdataFieldComposer field_composer(buffer);
+ RdataFieldComposer field_composer;
rdata.toWire(field_composer);
nfields_ = field_composer.getFields().size();
data_length_ = field_composer.getLength();
diff --git a/src/lib/dns/rrset.h b/src/lib/dns/rrset.h
index aad50aa..5042a98 100644
--- a/src/lib/dns/rrset.h
+++ b/src/lib/dns/rrset.h
@@ -58,14 +58,14 @@ class RRset;
///
/// This type is commonly used as an argument of various functions defined
/// in this library in order to handle RRsets in a polymorphic manner.
-typedef boost::shared_ptr<RRset> RRsetPtr;
+typedef boost::shared_ptr<AbstractRRset> RRsetPtr;
/// \brief A pointer-like type pointing to an (immutable) \c RRset
/// object.
///
/// This type is commonly used as an argument of various functions defined
/// in this library in order to handle RRsets in a polymorphic manner.
-typedef boost::shared_ptr<const RRset> ConstRRsetPtr;
+typedef boost::shared_ptr<const AbstractRRset> ConstRRsetPtr;
/// \brief A pointer-like type point to an \c RdataIterator object.
typedef boost::shared_ptr<RdataIterator> RdataIteratorPtr;
@@ -400,6 +400,82 @@ public:
/// object.
virtual RdataIteratorPtr getRdataIterator() const = 0;
//@}
+
+ ///
+ /// \name Associated RRSIG methods
+ ///
+ /// These methods access an "associated" RRset, that containing the DNSSEC
+ /// signatures for this RRset. It can be argued that this is not a
+ /// fundamental part of the RRset abstraction, since RFC 2181 defined an
+ /// RRset as a group of records with the same label, class and type but
+ /// different data. However, BIND 10 has to deal with DNSSEC and in
+ /// practice, including the information at the AbstractRRset level makes
+ /// implementation easier. (If a class is ever needed that must be
+ /// ignorant of the idea of an associated RRSIG RRset - e.g. a specialised
+ /// RRSIG RRset class - these methods can just throw a "NotImplemented"
+ /// exception.)
+ //@{
+ /// \brief Return pointer to this RRset's RRSIG RRset
+ ///
+ /// \return Pointer to the associated RRSIG RRset or null if there is none.
+ virtual RRsetPtr getRRsig() const = 0;
+
+ /// \brief Adds RRSIG RRset RRs to the associated RRSIG RRset
+ ///
+ /// Adds the (assumed) RRSIG rdata the RRSIG RRset associated with this
+ /// RRset. If one does not exist, it is created using the data given.
+ ///
+ /// \param rdata Pointer to RRSIG rdata to be added.
+ virtual void addRRsig(const rdata::ConstRdataPtr& rdata) = 0;
+
+ /// \brief Adds RRSIG RRset RRs to the associated RRSIG RRset
+ ///
+ /// Adds the (assumed) RRSIG rdata the RRSIG RRset associated with this
+ /// RRset. If one does not exist, it is created using the data given.
+ ///
+ /// (This overload is for an older version of boost that doesn't support
+ /// conversion from shared_ptr<X> to shared_ptr<const X>.)
+ ///
+ /// \param rdata Pointer to RRSIG rdata to be added.
+ virtual void addRRsig(const rdata::RdataPtr& rdata) = 0;
+
+ /// \brief Adds RRSIG RRset RRs to the associated RRSIG RRset
+ ///
+ /// Adds the signatures in the given (assumed) RRSIG RRset to the RRSIG
+ /// RRset associated with this RRset. If one does not exist, it is created
+ /// using the data given.
+ ///
+ /// \param sigs RRSIG RRset containing signatures to be added to the
+ /// RRSIG RRset associated with this class.
+ virtual void addRRsig(const AbstractRRset& sigs) = 0;
+
+ /// \brief Adds RRSIG RRset RRs to the associated RRSIG RRset
+ ///
+ /// Adds the signatures in the given (assumed) RRSIG RRset to the RRSIG
+ /// RRset associated with this RRset. If one does not exist, it is created
+ /// using the data given.
+ ///
+ /// \param sigs Pointer to a RRSIG RRset containing signatures to be added
+ /// to the RRSIG RRset associated with this class.
+ virtual void addRRsig(const ConstRRsetPtr& sigs) = 0;
+
+ /// \brief Adds RRSIG RRset RRs to the associated RRSIG RRset
+ ///
+ /// Adds the signatures in the given (assumed) RRSIG RRset to the RRSIG
+ /// RRset associated with this RRset. If one does not exist, it is created
+ /// using the data given.
+ ///
+ /// (This overload is for an older version of boost that doesn't support
+ /// conversion from shared_ptr<X> to shared_ptr<const X>.)
+ ///
+ /// \param sigs Pointer to a RRSIG RRset containing signatures to be added
+ /// to the RRSIG RRset associated with this class.
+ virtual void addRRsig(const RRsetPtr& sigs) = 0;
+
+ /// \brief Clear the RRSIGs for this RRset
+ virtual void removeRRsig() = 0;
+
+ //@}
};
/// \brief The \c RdataIterator class is an abstract base class that
@@ -660,6 +736,56 @@ public:
/// object for the \c BasicRRset class.
virtual RdataIteratorPtr getRdataIterator() const;
//@}
+
+ ///
+ /// \name Associated RRSIG methods
+ ///
+ /// The associated RRSIG RRset is not supported in BasicRRset. For
+ /// ease of use, getRRsig() returns a null pointer (indicating no RRset).
+ /// The addRRsig()/removeRRsig() methods throw a "NotImplemented"
+ /// exception - if you are using a BasicRRset, you should not be trying
+ /// to modify signatures on it.
+ //@{
+ /// \brief Return pointer to this RRset's RRSIG RRset
+ ///
+ /// \exception NotImplemented Always thrown. Associated RRSIG RRsets are
+ /// not supported in this class.
+ ///
+ /// \return Null pointer, as this class does not support RRSIG records.
+ virtual RRsetPtr getRRsig() const {
+ return (RRsetPtr());
+ }
+
+ virtual void addRRsig(const rdata::ConstRdataPtr&) {
+ isc_throw(NotImplemented,
+ "BasicRRset does not implement the addRRsig() method");
+ }
+
+ virtual void addRRsig(const rdata::RdataPtr&) {
+ isc_throw(NotImplemented,
+ "BasicRRset does not implement the addRRsig() method");
+ }
+
+ virtual void addRRsig(const AbstractRRset&) {
+ isc_throw(NotImplemented,
+ "BasicRRset does not implement the addRRsig() method");
+ }
+
+ virtual void addRRsig(const ConstRRsetPtr&) {
+ isc_throw(NotImplemented,
+ "BasicRRset does not implement the addRRsig() method");
+ }
+
+ virtual void addRRsig(const RRsetPtr&) {
+ isc_throw(NotImplemented,
+ "BasicRRset does not implement the addRRsig() method");
+ }
+
+ virtual void removeRRsig() {
+ isc_throw(NotImplemented,
+ "BasicRRset does not implement the removeRRsig() method");
+ }
+ //@}
private:
BasicRRsetImpl* impl_;
};
@@ -693,7 +819,7 @@ public:
}
/// \brief Adds an RRSIG RR to this RRset's signatures
- virtual void addRRsig(const rdata::ConstRdataPtr rdata) {
+ virtual void addRRsig(const rdata::ConstRdataPtr& rdata) {
if (!rrsig_) {
rrsig_ = RRsetPtr(new RRset(getName(), getClass(),
RRType::RRSIG(), getTTL()));
@@ -705,12 +831,13 @@ public:
// conversion from shared_ptr<X> to shared_ptr<const X>. Note: we should
// revisit the interface of managing RRset signatures, at which point this
// problem may go away.
- void addRRsig(const rdata::RdataPtr rdata) {
- addRRsig(static_cast<rdata::ConstRdataPtr>(rdata));
+ virtual void addRRsig(const rdata::RdataPtr& rdata) {
+ // Don't try to convert as a reference here. SunStudio will reject it.
+ addRRsig(static_cast<const rdata::ConstRdataPtr>(rdata));
}
/// \brief Adds an RRSIG RRset to this RRset
- void addRRsig(const AbstractRRset& sigs) {
+ virtual void addRRsig(const AbstractRRset& sigs) {
RdataIteratorPtr it = sigs.getRdataIterator();
if (!rrsig_) {
@@ -723,13 +850,13 @@ public:
}
}
- void addRRsig(ConstRRsetPtr sigs) { addRRsig(*sigs); }
+ virtual void addRRsig(const ConstRRsetPtr& sigs) { addRRsig(*sigs); }
// Another workaround for older boost (see above)
- void addRRsig(RRsetPtr sigs) { addRRsig(*sigs); }
+ virtual void addRRsig(const RRsetPtr& sigs) { addRRsig(*sigs); }
/// \brief Clear the RRSIGs for this RRset
- void removeRRsig() { rrsig_ = RRsetPtr(); }
+ virtual void removeRRsig() { rrsig_ = RRsetPtr(); }
/// \brief Return a pointer to this RRset's RRSIG RRset
RRsetPtr getRRsig() const { return (rrsig_); }
diff --git a/src/lib/dns/tests/.gitignore b/src/lib/dns/tests/.gitignore
new file mode 100644
index 0000000..d6d1ec8
--- /dev/null
+++ b/src/lib/dns/tests/.gitignore
@@ -0,0 +1 @@
+/run_unittests
diff --git a/src/lib/dns/tests/Makefile.am b/src/lib/dns/tests/Makefile.am
index 7f7ab59..7ba0314 100644
--- a/src/lib/dns/tests/Makefile.am
+++ b/src/lib/dns/tests/Makefile.am
@@ -19,6 +19,7 @@ if HAVE_GTEST
TESTS += run_unittests
run_unittests_SOURCES = unittest_util.h unittest_util.cc
run_unittests_SOURCES += edns_unittest.cc
+run_unittests_SOURCES += labelsequence_unittest.cc
run_unittests_SOURCES += messagerenderer_unittest.cc
run_unittests_SOURCES += name_unittest.cc
run_unittests_SOURCES += nsec3hash_unittest.cc
@@ -43,6 +44,7 @@ run_unittests_SOURCES += rdata_nsec_unittest.cc
run_unittests_SOURCES += rdata_nsec3_unittest.cc
run_unittests_SOURCES += rdata_nsecbitmap_unittest.cc
run_unittests_SOURCES += rdata_nsec3param_unittest.cc
+run_unittests_SOURCES += rdata_nsec3param_like_unittest.cc
run_unittests_SOURCES += rdata_rrsig_unittest.cc
run_unittests_SOURCES += rdata_rp_unittest.cc
run_unittests_SOURCES += rdata_srv_unittest.cc
diff --git a/src/lib/dns/tests/edns_unittest.cc b/src/lib/dns/tests/edns_unittest.cc
index 26cc01c..de2d244 100644
--- a/src/lib/dns/tests/edns_unittest.cc
+++ b/src/lib/dns/tests/edns_unittest.cc
@@ -43,9 +43,7 @@ const uint8_t EDNS::SUPPORTED_VERSION;
namespace {
class EDNSTest : public ::testing::Test {
protected:
- EDNSTest() : rrtype(RRType::OPT()), buffer(NULL, 0), obuffer(0),
- renderer(obuffer), rcode(0)
- {
+ EDNSTest() : rrtype(RRType::OPT()), buffer(NULL, 0), obuffer(0), rcode(0) {
opt_rdata = ConstRdataPtr(new generic::OPT());
edns_base.setUDPSize(4096);
}
diff --git a/src/lib/dns/tests/labelsequence_unittest.cc b/src/lib/dns/tests/labelsequence_unittest.cc
new file mode 100644
index 0000000..72326be
--- /dev/null
+++ b/src/lib/dns/tests/labelsequence_unittest.cc
@@ -0,0 +1,253 @@
+// Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <dns/labelsequence.h>
+#include <exceptions/exceptions.h>
+
+#include <gtest/gtest.h>
+
+using namespace isc::dns;
+
+class LabelSequenceTest : public ::testing::Test {
+public:
+ LabelSequenceTest() : n1("example.org"), n2("example.com"),
+ n3("example.org"), n4("foo.bar.test.example"),
+ n5("example.ORG"), n6("ExAmPlE.org"),
+ n7("."), n8("foo.example.org.bar"),
+ ls1(n1), ls2(n2), ls3(n3), ls4(n4), ls5(n5),
+ ls6(n6), ls7(n7), ls8(n8)
+ {};
+ // Need to keep names in scope for at least the lifetime of
+ // the labelsequences
+ Name n1, n2, n3, n4, n5, n6, n7, n8;
+
+ LabelSequence ls1, ls2, ls3, ls4, ls5, ls6, ls7, ls8;
+};
+
+// Basic equality tests
+TEST_F(LabelSequenceTest, equals_sensitive) {
+ EXPECT_TRUE(ls1.equals(ls1));
+ EXPECT_FALSE(ls1.equals(ls2));
+ EXPECT_TRUE(ls1.equals(ls3));
+ EXPECT_FALSE(ls1.equals(ls4));
+ EXPECT_FALSE(ls1.equals(ls5));
+ EXPECT_FALSE(ls1.equals(ls6));
+ EXPECT_FALSE(ls1.equals(ls7));
+ EXPECT_FALSE(ls1.equals(ls8));
+
+ EXPECT_FALSE(ls2.equals(ls1));
+ EXPECT_TRUE(ls2.equals(ls2));
+ EXPECT_FALSE(ls2.equals(ls3));
+ EXPECT_FALSE(ls2.equals(ls4));
+ EXPECT_FALSE(ls2.equals(ls5));
+ EXPECT_FALSE(ls2.equals(ls6));
+ EXPECT_FALSE(ls2.equals(ls7));
+ EXPECT_FALSE(ls2.equals(ls8));
+
+ EXPECT_FALSE(ls4.equals(ls1));
+ EXPECT_FALSE(ls4.equals(ls2));
+ EXPECT_FALSE(ls4.equals(ls3));
+ EXPECT_TRUE(ls4.equals(ls4));
+ EXPECT_FALSE(ls4.equals(ls5));
+ EXPECT_FALSE(ls4.equals(ls6));
+ EXPECT_FALSE(ls4.equals(ls7));
+ EXPECT_FALSE(ls4.equals(ls8));
+
+ EXPECT_FALSE(ls5.equals(ls1));
+ EXPECT_FALSE(ls5.equals(ls2));
+ EXPECT_FALSE(ls5.equals(ls3));
+ EXPECT_FALSE(ls5.equals(ls4));
+ EXPECT_TRUE(ls5.equals(ls5));
+ EXPECT_FALSE(ls5.equals(ls6));
+ EXPECT_FALSE(ls5.equals(ls7));
+ EXPECT_FALSE(ls5.equals(ls8));
+}
+
+TEST_F(LabelSequenceTest, equals_insensitive) {
+ EXPECT_TRUE(ls1.equals(ls1, true));
+ EXPECT_FALSE(ls1.equals(ls2, true));
+ EXPECT_TRUE(ls1.equals(ls3, true));
+ EXPECT_FALSE(ls1.equals(ls4, true));
+ EXPECT_TRUE(ls1.equals(ls5, true));
+ EXPECT_TRUE(ls1.equals(ls6, true));
+ EXPECT_FALSE(ls1.equals(ls7, true));
+
+ EXPECT_FALSE(ls2.equals(ls1, true));
+ EXPECT_TRUE(ls2.equals(ls2, true));
+ EXPECT_FALSE(ls2.equals(ls3, true));
+ EXPECT_FALSE(ls2.equals(ls4, true));
+ EXPECT_FALSE(ls2.equals(ls5, true));
+ EXPECT_FALSE(ls2.equals(ls6, true));
+ EXPECT_FALSE(ls2.equals(ls7, true));
+
+ EXPECT_TRUE(ls3.equals(ls1, true));
+ EXPECT_FALSE(ls3.equals(ls2, true));
+ EXPECT_TRUE(ls3.equals(ls3, true));
+ EXPECT_FALSE(ls3.equals(ls4, true));
+ EXPECT_TRUE(ls3.equals(ls5, true));
+ EXPECT_TRUE(ls3.equals(ls6, true));
+ EXPECT_FALSE(ls3.equals(ls7, true));
+
+ EXPECT_FALSE(ls4.equals(ls1, true));
+ EXPECT_FALSE(ls4.equals(ls2, true));
+ EXPECT_FALSE(ls4.equals(ls3, true));
+ EXPECT_TRUE(ls4.equals(ls4, true));
+ EXPECT_FALSE(ls4.equals(ls5, true));
+ EXPECT_FALSE(ls4.equals(ls6, true));
+ EXPECT_FALSE(ls4.equals(ls7, true));
+
+ EXPECT_TRUE(ls5.equals(ls1, true));
+ EXPECT_FALSE(ls5.equals(ls2, true));
+ EXPECT_TRUE(ls5.equals(ls3, true));
+ EXPECT_FALSE(ls5.equals(ls4, true));
+ EXPECT_TRUE(ls5.equals(ls5, true));
+ EXPECT_TRUE(ls5.equals(ls6, true));
+ EXPECT_FALSE(ls5.equals(ls7, true));
+}
+
+void
+getDataCheck(const char* expected_data, size_t expected_len,
+ const LabelSequence& ls)
+{
+ size_t len;
+ const char* data = ls.getData(&len);
+ ASSERT_EQ(expected_len, len) << "Expected data: " << expected_data <<
+ " name: " << ls.getName().toText();
+ for (size_t i = 0; i < len; ++i) {
+ EXPECT_EQ(expected_data[i], data[i]) << "Difference at pos " << i <<
+ ": Expected data: " <<
+ expected_data <<
+ " name: " <<
+ ls.getName().toText();;
+ }
+}
+
+TEST_F(LabelSequenceTest, getData) {
+ getDataCheck("\007example\003org\000", 13, ls1);
+ getDataCheck("\007example\003com\000", 13, ls2);
+ getDataCheck("\007example\003org\000", 13, ls3);
+ getDataCheck("\003foo\003bar\004test\007example\000", 22, ls4);
+ getDataCheck("\007example\003ORG\000", 13, ls5);
+ getDataCheck("\007ExAmPlE\003org\000", 13, ls6);
+ getDataCheck("\000", 1, ls7);
+};
+
+TEST_F(LabelSequenceTest, stripLeft) {
+ EXPECT_TRUE(ls1.equals(ls3));
+ ls1.stripLeft(0);
+ getDataCheck("\007example\003org\000", 13, ls1);
+ EXPECT_TRUE(ls1.equals(ls3));
+ ls1.stripLeft(1);
+ getDataCheck("\003org\000", 5, ls1);
+ EXPECT_FALSE(ls1.equals(ls3));
+ ls1.stripLeft(1);
+ getDataCheck("\000", 1, ls1);
+ EXPECT_TRUE(ls1.equals(ls7));
+
+ ls2.stripLeft(2);
+ getDataCheck("\000", 1, ls2);
+ EXPECT_TRUE(ls2.equals(ls7));
+}
+
+TEST_F(LabelSequenceTest, stripRight) {
+ EXPECT_TRUE(ls1.equals(ls3));
+ ls1.stripRight(1);
+ getDataCheck("\007example\003org", 12, ls1);
+ EXPECT_FALSE(ls1.equals(ls3));
+ ls1.stripRight(1);
+ getDataCheck("\007example", 8, ls1);
+ EXPECT_FALSE(ls1.equals(ls3));
+
+ ASSERT_FALSE(ls1.equals(ls2));
+ ls2.stripRight(2);
+ getDataCheck("\007example", 8, ls2);
+ EXPECT_TRUE(ls1.equals(ls2));
+}
+
+TEST_F(LabelSequenceTest, stripOutOfRange) {
+ EXPECT_THROW(ls1.stripLeft(100), isc::OutOfRange);
+ EXPECT_THROW(ls1.stripLeft(5), isc::OutOfRange);
+ EXPECT_THROW(ls1.stripLeft(4), isc::OutOfRange);
+ EXPECT_THROW(ls1.stripLeft(3), isc::OutOfRange);
+ getDataCheck("\007example\003org\000", 13, ls1);
+
+ EXPECT_THROW(ls1.stripRight(100), isc::OutOfRange);
+ EXPECT_THROW(ls1.stripRight(5), isc::OutOfRange);
+ EXPECT_THROW(ls1.stripRight(4), isc::OutOfRange);
+ EXPECT_THROW(ls1.stripRight(3), isc::OutOfRange);
+ getDataCheck("\007example\003org\000", 13, ls1);
+}
+
+TEST_F(LabelSequenceTest, getLabelCount) {
+ EXPECT_EQ(3, ls1.getLabelCount());
+ ls1.stripLeft(0);
+ EXPECT_EQ(3, ls1.getLabelCount());
+ ls1.stripLeft(1);
+ EXPECT_EQ(2, ls1.getLabelCount());
+ ls1.stripLeft(1);
+ EXPECT_EQ(1, ls1.getLabelCount());
+
+ EXPECT_EQ(3, ls2.getLabelCount());
+ ls2.stripRight(1);
+ EXPECT_EQ(2, ls2.getLabelCount());
+ ls2.stripRight(1);
+ EXPECT_EQ(1, ls2.getLabelCount());
+
+ EXPECT_EQ(3, ls3.getLabelCount());
+ ls3.stripRight(2);
+ EXPECT_EQ(1, ls3.getLabelCount());
+
+ EXPECT_EQ(5, ls4.getLabelCount());
+ ls4.stripRight(3);
+ EXPECT_EQ(2, ls4.getLabelCount());
+
+ EXPECT_EQ(3, ls5.getLabelCount());
+ ls5.stripLeft(2);
+ EXPECT_EQ(1, ls5.getLabelCount());
+}
+
+TEST_F(LabelSequenceTest, comparePart) {
+ EXPECT_FALSE(ls1.equals(ls8));
+
+ // strip root label from example.org.
+ ls1.stripRight(1);
+ // strip foo from foo.example.org.bar.
+ ls8.stripLeft(1);
+ // strip bar. (i.e. bar and root) too
+ ls8.stripRight(2);
+
+ EXPECT_TRUE(ls1.equals(ls8));
+
+ // Data comparison
+ size_t len;
+ const char* data = ls1.getData(&len);
+ getDataCheck(data, len, ls8);
+}
+
+TEST_F(LabelSequenceTest, isAbsolute) {
+ ASSERT_TRUE(ls1.isAbsolute());
+
+ ls1.stripLeft(1);
+ ASSERT_TRUE(ls1.isAbsolute());
+ ls1.stripRight(1);
+ ASSERT_FALSE(ls1.isAbsolute());
+
+ ASSERT_TRUE(ls2.isAbsolute());
+ ls2.stripRight(1);
+ ASSERT_FALSE(ls2.isAbsolute());
+
+ ASSERT_TRUE(ls3.isAbsolute());
+ ls3.stripLeft(2);
+ ASSERT_TRUE(ls3.isAbsolute());
+}
diff --git a/src/lib/dns/tests/masterload_unittest.cc b/src/lib/dns/tests/masterload_unittest.cc
index acb9d64..95ce6f3 100644
--- a/src/lib/dns/tests/masterload_unittest.cc
+++ b/src/lib/dns/tests/masterload_unittest.cc
@@ -25,6 +25,7 @@
#include <dns/masterload.h>
#include <dns/name.h>
+#include <dns/rdata.h>
#include <dns/rrclass.h>
#include <dns/rrset.h>
@@ -80,6 +81,11 @@ const char* const rrsig_rr2 =
"www.example.com. 60 IN RRSIG AAAA 5 3 3600 20000101000000 20000201000000 "
"12345 example.com. FAKEFAKEFAKE\n";
+// Commonly used for some tests to check the constructed RR content.
+const char* const dnskey_rdata =
+ "256 3 7 AwEAAaetidLzsKWUt4swWR8yu0wPHPiUi8LUsAD0QPWU+wzt89epO6tH "
+ "zkMBVDkC7qphQO2hTY4hHn9npWFRw5BYubE=\n";
+
TEST_F(MasterLoadTest, loadRRs) {
// a simple case: loading 3 RRs, each consists of a single RRset.
rr_stream << txt_rr << a_rr1 << soa_rr;
@@ -161,6 +167,98 @@ TEST_F(MasterLoadTest, loadRRsigs) {
EXPECT_EQ(2, results.size());
}
+TEST_F(MasterLoadTest, loadRRWithComment) {
+ // Comment at the end of line should be ignored and the RR should be
+ // accepted.
+ rr_stream << "example.com. 3600 IN DNSKEY 256 3 7 "
+ "AwEAAaetidLzsKWUt4swWR8yu0wPHPiUi8LUsAD0QPWU+wzt89epO6tH "
+ "zkMBVDkC7qphQO2hTY4hHn9npWFRw5BYubE= ; key id = 40430\n";
+ masterLoad(rr_stream, origin, zclass, callback);
+ ASSERT_EQ(1, results.size());
+ EXPECT_EQ(0, results[0]->getRdataIterator()->getCurrent().compare(
+ *rdata::createRdata(RRType::DNSKEY(), zclass,
+ dnskey_rdata)));
+}
+
+TEST_F(MasterLoadTest, loadRRWithCommentNoSpace) {
+ // Similar to the previous one, but there's no space before comments.
+ // It should still work.
+ rr_stream << "example.com. 3600 IN DNSKEY 256 3 7 "
+ "AwEAAaetidLzsKWUt4swWR8yu0wPHPiUi8LUsAD0QPWU+wzt89epO6tH "
+ "zkMBVDkC7qphQO2hTY4hHn9npWFRw5BYubE=; key id = 40430\n";
+ masterLoad(rr_stream, origin, zclass, callback);
+ ASSERT_EQ(1, results.size());
+ EXPECT_EQ(0, results[0]->getRdataIterator()->getCurrent().compare(
+ *rdata::createRdata(RRType::DNSKEY(), zclass,
+ dnskey_rdata)));
+}
+
+TEST_F(MasterLoadTest, loadRRWithCommentEmptyComment) {
+ // Similar to the previous one, but there's no data after the ;
+ // It should still work.
+ rr_stream << "example.com. 3600 IN DNSKEY 256 3 7 "
+ "AwEAAaetidLzsKWUt4swWR8yu0wPHPiUi8LUsAD0QPWU+wzt89epO6tH "
+ "zkMBVDkC7qphQO2hTY4hHn9npWFRw5BYubE= ;\n";
+ masterLoad(rr_stream, origin, zclass, callback);
+ ASSERT_EQ(1, results.size());
+ EXPECT_EQ(0, results[0]->getRdataIterator()->getCurrent().compare(
+ *rdata::createRdata(RRType::DNSKEY(), zclass,
+ dnskey_rdata)));
+}
+
+TEST_F(MasterLoadTest, loadRRWithCommentEmptyCommentNoSpace) {
+ // Similar to the previous one, but there's no space before or after ;
+ // It should still work.
+ rr_stream << "example.com. 3600 IN DNSKEY 256 3 7 "
+ "AwEAAaetidLzsKWUt4swWR8yu0wPHPiUi8LUsAD0QPWU+wzt89epO6tH "
+ "zkMBVDkC7qphQO2hTY4hHn9npWFRw5BYubE=;\n";
+ masterLoad(rr_stream, origin, zclass, callback);
+ ASSERT_EQ(1, results.size());
+ EXPECT_EQ(0, results[0]->getRdataIterator()->getCurrent().compare(
+ *rdata::createRdata(RRType::DNSKEY(), zclass,
+ dnskey_rdata)));
+}
+
+TEST_F(MasterLoadTest, loadRRWithEOLWhitespace) {
+ // Test with whitespace after rdata
+ // It should still work.
+ rr_stream << "example.com. 3600 IN NSEC3PARAM 1 0 1 beef \n";
+ masterLoad(rr_stream, origin, zclass, callback);
+ ASSERT_EQ(1, results.size());
+ EXPECT_EQ(0, results[0]->getRdataIterator()->getCurrent().compare(
+ *rdata::createRdata(RRType::NSEC3PARAM(), zclass,
+ "1 0 1 beef")));
+}
+
+TEST_F(MasterLoadTest, loadRRWithEOLWhitespaceTab) {
+ // Similar to the previous one, tab instead of space.
+ // It should still work.
+ rr_stream << "example.com. 3600 IN NSEC3PARAM 1 0 1 beef\t\n";
+ masterLoad(rr_stream, origin, zclass, callback);
+ ASSERT_EQ(1, results.size());
+ EXPECT_EQ(0, results[0]->getRdataIterator()->getCurrent().compare(
+ *rdata::createRdata(RRType::NSEC3PARAM(), zclass,
+ "1 0 1 beef")));
+}
+
+TEST_F(MasterLoadTest, loadRRNoComment) {
+ // A semicolon in a character-string shouldn't confuse the parser.
+ rr_stream << "example.com. 3600 IN TXT \"aaa;bbb\"\n";
+ masterLoad(rr_stream, origin, zclass, callback);
+ EXPECT_EQ(1, results.size());
+ EXPECT_EQ(0, results[0]->getRdataIterator()->getCurrent().compare(
+ *rdata::createRdata(RRType::TXT(), zclass,
+ "\"aaa;bbb\"")));
+}
+
+TEST_F(MasterLoadTest, loadRREmptyAndComment) {
+ // There's no RDATA (invalid in this case) but a comment. This position
+ // shouldn't cause any disruption and should be treated as a normal error.
+ rr_stream << "example.com. 3600 IN A ;\n";
+ EXPECT_THROW(masterLoad(rr_stream, origin, zclass, callback),
+ MasterLoadError);
+}
+
TEST_F(MasterLoadTest, loadWithNoEOF) {
// the input stream doesn't end with a new line (and the following blank
// line). It should be accepted.
diff --git a/src/lib/dns/tests/message_unittest.cc b/src/lib/dns/tests/message_unittest.cc
index c4d4984..3be1436 100644
--- a/src/lib/dns/tests/message_unittest.cc
+++ b/src/lib/dns/tests/message_unittest.cc
@@ -79,7 +79,6 @@ namespace {
class MessageTest : public ::testing::Test {
protected:
MessageTest() : test_name("test.example.com"), obuffer(0),
- renderer(obuffer),
message_parse(Message::PARSE),
message_render(Message::RENDER),
bogus_section(static_cast<Message::Section>(
@@ -324,6 +323,10 @@ TEST_F(MessageTest, badAddRRset) {
rrset_a), InvalidMessageOperation);
// out-of-band section ID
EXPECT_THROW(message_render.addRRset(bogus_section, rrset_a), OutOfRange);
+
+ // NULL RRset
+ EXPECT_THROW(message_render.addRRset(Message::SECTION_ANSWER, RRsetPtr()),
+ InvalidParameter);
}
TEST_F(MessageTest, hasRRset) {
@@ -727,8 +730,8 @@ TEST_F(MessageTest, toWire) {
message_render.toWire(renderer);
vector<unsigned char> data;
UnitTestUtil::readWireData("message_toWire1", data);
- EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, obuffer.getData(),
- obuffer.getLength(), &data[0], data.size());
+ EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, renderer.getData(),
+ renderer.getLength(), &data[0], data.size());
}
TEST_F(MessageTest, toWireInParseMode) {
@@ -957,9 +960,6 @@ TEST_F(MessageTest, toWireTSIGNoTruncation) {
// rendering fail unexpectedly in the test that follows.
class BadRenderer : public MessageRenderer {
public:
- BadRenderer(isc::util::OutputBuffer& buffer) :
- MessageRenderer(buffer)
- {}
virtual void setLengthLimit(size_t len) {
if (getLength() > 0) {
MessageRenderer::setLengthLimit(getLength());
@@ -990,8 +990,7 @@ TEST_F(MessageTest, toWireTSIGLengthErrors) {
InvalidParameter);
// Trying to render a message with TSIG using a buggy renderer.
- obuffer.clear();
- BadRenderer bad_renderer(obuffer);
+ BadRenderer bad_renderer;
bad_renderer.setLengthLimit(512);
message_render.clear(Message::RENDER);
EXPECT_THROW(commonTSIGToWireCheck(message_render, bad_renderer, tsig_ctx,
diff --git a/src/lib/dns/tests/messagerenderer_unittest.cc b/src/lib/dns/tests/messagerenderer_unittest.cc
index fe790fe..2a1869f 100644
--- a/src/lib/dns/tests/messagerenderer_unittest.cc
+++ b/src/lib/dns/tests/messagerenderer_unittest.cc
@@ -12,8 +12,7 @@
// 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 <util/buffer.h>
#include <dns/name.h>
#include <dns/messagerenderer.h>
@@ -22,6 +21,8 @@
#include <gtest/gtest.h>
+#include <vector>
+
using isc::UnitTestUtil;
using isc::dns::Name;
using isc::dns::MessageRenderer;
@@ -30,15 +31,13 @@ using isc::util::OutputBuffer;
namespace {
class MessageRendererTest : public ::testing::Test {
protected:
- MessageRendererTest() : expected_size(0), buffer(0), renderer(buffer)
- {
+ MessageRendererTest() : expected_size(0) {
data16 = (2 << 8) | 3;
data32 = (4 << 24) | (5 << 16) | (6 << 8) | 7;
}
size_t expected_size;
uint16_t data16;
uint32_t data32;
- OutputBuffer buffer;
MessageRenderer renderer;
std::vector<unsigned char> data;
static const uint8_t testdata[5];
@@ -60,21 +59,22 @@ TEST_F(MessageRendererTest, writeName) {
renderer.writeName(Name("a.example.com."));
renderer.writeName(Name("b.example.com."));
renderer.writeName(Name("a.example.org."));
- EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, buffer.getData(),
- buffer.getLength(), &data[0], data.size());
+ EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, renderer.getData(),
+ renderer.getLength(), &data[0], data.size());
}
TEST_F(MessageRendererTest, writeNameInLargeBuffer) {
size_t offset = 0x3fff;
- buffer.skip(offset);
+ renderer.skip(offset);
UnitTestUtil::readWireData("name_toWire2", data);
renderer.writeName(Name("a.example.com."));
renderer.writeName(Name("a.example.com."));
renderer.writeName(Name("b.example.com."));
EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
- static_cast<const uint8_t*>(buffer.getData()) + offset,
- buffer.getLength() - offset,
+ static_cast<const uint8_t*>(renderer.getData()) +
+ offset,
+ renderer.getLength() - offset,
&data[0], data.size());
}
@@ -83,8 +83,8 @@ TEST_F(MessageRendererTest, writeNameWithUncompressed) {
renderer.writeName(Name("a.example.com."));
renderer.writeName(Name("b.example.com."), false);
renderer.writeName(Name("b.example.com."));
- EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, buffer.getData(),
- buffer.getLength(), &data[0], data.size());
+ EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, renderer.getData(),
+ renderer.getLength(), &data[0], data.size());
}
TEST_F(MessageRendererTest, writeNamePointerChain) {
@@ -92,8 +92,8 @@ TEST_F(MessageRendererTest, writeNamePointerChain) {
renderer.writeName(Name("a.example.com."));
renderer.writeName(Name("b.example.com."));
renderer.writeName(Name("b.example.com."));
- EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, buffer.getData(),
- buffer.getLength(), &data[0], data.size());
+ EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, renderer.getData(),
+ renderer.getLength(), &data[0], data.size());
}
TEST_F(MessageRendererTest, compressMode) {
@@ -120,8 +120,8 @@ TEST_F(MessageRendererTest, writeNameCaseCompress) {
// this should match the first name in terms of compression:
renderer.writeName(Name("b.exAmple.CoM."));
renderer.writeName(Name("a.example.org."));
- EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, buffer.getData(),
- buffer.getLength(), &data[0], data.size());
+ EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, renderer.getData(),
+ renderer.getLength(), &data[0], data.size());
}
TEST_F(MessageRendererTest, writeNameCaseSensitiveCompress) {
@@ -132,8 +132,8 @@ TEST_F(MessageRendererTest, writeNameCaseSensitiveCompress) {
renderer.writeName(Name("a.example.com."));
renderer.writeName(Name("b.eXample.com."));
renderer.writeName(Name("c.eXample.com."));
- EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, buffer.getData(),
- buffer.getLength(), &data[0], data.size());
+ EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, renderer.getData(),
+ renderer.getLength(), &data[0], data.size());
}
TEST_F(MessageRendererTest, writeNameMixedCaseCompress) {
@@ -147,8 +147,8 @@ TEST_F(MessageRendererTest, writeNameMixedCaseCompress) {
// allowed in this API.
renderer.setCompressMode(MessageRenderer::CASE_INSENSITIVE);
renderer.writeName(Name("c.b.EXAMPLE.com."));
- EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, buffer.getData(),
- buffer.getLength(), &data[0], data.size());
+ EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, renderer.getData(),
+ renderer.getLength(), &data[0], data.size());
}
TEST_F(MessageRendererTest, writeRootName) {
@@ -164,9 +164,51 @@ TEST_F(MessageRendererTest, writeRootName) {
renderer.writeName(Name("."));
renderer.writeName(example_name);
EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
- static_cast<const uint8_t*>(buffer.getData()),
- buffer.getLength(),
+ static_cast<const uint8_t*>(renderer.getData()),
+ renderer.getLength(),
static_cast<const uint8_t*>(expected.getData()),
expected.getLength());
}
+
+TEST_F(MessageRendererTest, setBuffer) {
+ OutputBuffer new_buffer(0);
+ renderer.setBuffer(&new_buffer);
+ EXPECT_EQ(0, new_buffer.getLength()); // the buffer should be still empty
+ renderer.writeUint32(42);
+ EXPECT_EQ(sizeof(uint32_t), new_buffer.getLength());
+ EXPECT_EQ(sizeof(uint32_t), renderer.getLength());
+
+ // Change some other internal state for the reset test below.
+ EXPECT_EQ(512, renderer.getLengthLimit());
+ renderer.setLengthLimit(4096);
+ EXPECT_EQ(4096, renderer.getLengthLimit());
+
+ // Reset the buffer to the default again. Other internal states and
+ // resources should be cleared. The used buffer should be intact.
+ renderer.setBuffer(NULL);
+ EXPECT_EQ(sizeof(uint32_t), new_buffer.getLength());
+ EXPECT_EQ(0, renderer.getLength());
+ EXPECT_EQ(512, renderer.getLengthLimit());
+}
+
+TEST_F(MessageRendererTest, setBufferErrors) {
+ OutputBuffer new_buffer(0);
+
+ // Buffer cannot be reset when the renderer is in use.
+ renderer.writeUint32(10);
+ EXPECT_THROW(renderer.setBuffer(&new_buffer), isc::InvalidParameter);
+
+ renderer.clear();
+ renderer.setBuffer(&new_buffer);
+ renderer.writeUint32(10);
+ EXPECT_THROW(renderer.setBuffer(&new_buffer), isc::InvalidParameter);
+
+ // Resetting the buffer isn't allowed for the default buffer.
+ renderer.setBuffer(NULL);
+ EXPECT_THROW(renderer.setBuffer(NULL), isc::InvalidParameter);
+
+ // It's okay to reset a temporary buffer without using it.
+ renderer.setBuffer(&new_buffer);
+ EXPECT_NO_THROW(renderer.setBuffer(NULL));
+}
}
diff --git a/src/lib/dns/tests/name_unittest.cc b/src/lib/dns/tests/name_unittest.cc
index 6434229..c5f3b7f 100644
--- a/src/lib/dns/tests/name_unittest.cc
+++ b/src/lib/dns/tests/name_unittest.cc
@@ -357,13 +357,12 @@ TEST_F(NameTest, toWireBuffer) {
//
TEST_F(NameTest, toWireRenderer) {
vector<unsigned char> data;
- OutputBuffer buffer(0);
- MessageRenderer renderer(buffer);
+ MessageRenderer renderer;
UnitTestUtil::readWireData(string("01610376697803636f6d00"), data);
Name("a.vix.com.").toWire(renderer);
EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, &data[0], data.size(),
- buffer.getData(), buffer.getLength());
+ renderer.getData(), renderer.getLength());
}
//
@@ -524,8 +523,8 @@ TEST_F(NameTest, at) {
example_name.toWire(buffer_expected);
EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
- &data[0], data.size(),
- buffer_expected.getData(), buffer_expected.getLength());
+ &data[0], data.size(), buffer_expected.getData(),
+ buffer_expected.getLength());
// Out-of-range access: should trigger an exception.
EXPECT_THROW(example_name.at(example_name.getLength()), OutOfRange);
@@ -543,7 +542,7 @@ TEST_F(NameTest, leq) {
// small <= small is true
EXPECT_TRUE(small_name.leq(small_name));
- EXPECT_TRUE(small_name <= small_name);
+ EXPECT_LE(small_name, small_name);
// large <= small is false
EXPECT_FALSE(large_name.leq(small_name));
@@ -555,7 +554,7 @@ TEST_F(NameTest, geq) {
EXPECT_TRUE(large_name >= small_name);
EXPECT_TRUE(large_name.geq(large_name));
- EXPECT_TRUE(large_name >= large_name);
+ EXPECT_GE(large_name, large_name);
EXPECT_FALSE(small_name.geq(large_name));
EXPECT_FALSE(small_name >= large_name);
diff --git a/src/lib/dns/tests/nsec3hash_unittest.cc b/src/lib/dns/tests/nsec3hash_unittest.cc
index 36a1d4d..e607c74 100644
--- a/src/lib/dns/tests/nsec3hash_unittest.cc
+++ b/src/lib/dns/tests/nsec3hash_unittest.cc
@@ -29,7 +29,7 @@ using namespace isc::dns::rdata;
namespace {
typedef scoped_ptr<NSEC3Hash> NSEC3HashPtr;
-// Commonly used NSEC3 suffix, defined to reduce amount of type
+// Commonly used NSEC3 suffix, defined to reduce the amount of typing
const char* const nsec3_common = "2T7B4G4VSA5SMI47K61MV5BV1A22BOJR A RRSIG";
class NSEC3HashTest : public ::testing::Test {
@@ -41,6 +41,11 @@ protected:
string(nsec3_common))))
{}
+ ~NSEC3HashTest() {
+ // Make sure we reset the hash creator to the default
+ setNSEC3HashCreator(NULL);
+ }
+
// An NSEC3Hash object commonly used in tests. Parameters are borrowed
// from the RFC5155 example. Construction of this object implicitly
// checks a successful case of the creation.
@@ -142,4 +147,76 @@ TEST_F(NSEC3HashTest, matchWithNSEC3PARAM) {
}
}
+// A simple faked hash calculator and a dedicated creator for it.
+class TestNSEC3Hash : public NSEC3Hash {
+ virtual string calculate(const Name&) const {
+ return ("00000000000000000000000000000000");
+ }
+ virtual bool match(const generic::NSEC3PARAM&) const {
+ return (true);
+ }
+ virtual bool match(const generic::NSEC3&) const {
+ return (true);
+ }
+};
+
+// This faked creator basically creates the faked calculator regardless of
+// the passed NSEC3PARAM or NSEC3. But if the most significant bit of flags
+// is set, it will behave like the default creator.
+class TestNSEC3HashCreator : public NSEC3HashCreator {
+public:
+ virtual NSEC3Hash* create(const generic::NSEC3PARAM& param) const {
+ if ((param.getFlags() & 0x80) != 0) {
+ return (default_creator_.create(param));
+ }
+ return (new TestNSEC3Hash);
+ }
+ virtual NSEC3Hash* create(const generic::NSEC3& nsec3) const {
+ if ((nsec3.getFlags() & 0x80) != 0) {
+ return (default_creator_.create(nsec3));
+ }
+ return (new TestNSEC3Hash);
+ }
+private:
+ DefaultNSEC3HashCreator default_creator_;
+};
+
+TEST_F(NSEC3HashTest, setCreator) {
+ // Re-check an existing case using the default creator/hash implementation
+ EXPECT_EQ("0P9MHAVEQVM6T7VBL5LOP2U3T2RP3TOM",
+ test_hash->calculate(Name("example")));
+
+ // Replace the creator, and confirm the hash values are faked
+ TestNSEC3HashCreator test_creator;
+ setNSEC3HashCreator(&test_creator);
+ // Re-create the hash object with the new creator
+ test_hash.reset(NSEC3Hash::create(generic::NSEC3PARAM("1 0 12 aabbccdd")));
+ EXPECT_EQ("00000000000000000000000000000000",
+ test_hash->calculate(Name("example")));
+ // Same for hash from NSEC3 RDATA
+ test_hash.reset(NSEC3Hash::create(generic::NSEC3
+ ("1 0 12 aabbccdd " +
+ string(nsec3_common))));
+ EXPECT_EQ("00000000000000000000000000000000",
+ test_hash->calculate(Name("example")));
+
+ // If we set a special flag big (0x80) on creation, it will act like the
+ // default creator.
+ test_hash.reset(NSEC3Hash::create(generic::NSEC3PARAM(
+ "1 128 12 aabbccdd")));
+ EXPECT_EQ("0P9MHAVEQVM6T7VBL5LOP2U3T2RP3TOM",
+ test_hash->calculate(Name("example")));
+ test_hash.reset(NSEC3Hash::create(generic::NSEC3
+ ("1 128 12 aabbccdd " +
+ string(nsec3_common))));
+ EXPECT_EQ("0P9MHAVEQVM6T7VBL5LOP2U3T2RP3TOM",
+ test_hash->calculate(Name("example")));
+
+ // Reset the creator to default, and confirm that
+ setNSEC3HashCreator(NULL);
+ test_hash.reset(NSEC3Hash::create(generic::NSEC3PARAM("1 0 12 aabbccdd")));
+ EXPECT_EQ("0P9MHAVEQVM6T7VBL5LOP2U3T2RP3TOM",
+ test_hash->calculate(Name("example")));
+}
+
} // end namespace
diff --git a/src/lib/dns/tests/question_unittest.cc b/src/lib/dns/tests/question_unittest.cc
index 1d483f2..54d0942 100644
--- a/src/lib/dns/tests/question_unittest.cc
+++ b/src/lib/dns/tests/question_unittest.cc
@@ -37,7 +37,7 @@ using namespace isc::util;
namespace {
class QuestionTest : public ::testing::Test {
protected:
- QuestionTest() : obuffer(0), renderer(obuffer),
+ QuestionTest() : obuffer(0),
example_name1(Name("foo.example.com")),
example_name2(Name("bar.example.com")),
test_question1(example_name1, RRClass::IN(),
@@ -102,8 +102,8 @@ TEST_F(QuestionTest, toWireRenderer) {
test_question1.toWire(renderer);
test_question2.toWire(renderer);
UnitTestUtil::readWireData("question_toWire2", wiredata);
- EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, obuffer.getData(),
- obuffer.getLength(), &wiredata[0], wiredata.size());
+ EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, renderer.getData(),
+ renderer.getLength(), &wiredata[0], wiredata.size());
}
TEST_F(QuestionTest, toWireTruncated) {
diff --git a/src/lib/dns/tests/rdata_afsdb_unittest.cc b/src/lib/dns/tests/rdata_afsdb_unittest.cc
index 7df8d83..521bec5 100644
--- a/src/lib/dns/tests/rdata_afsdb_unittest.cc
+++ b/src/lib/dns/tests/rdata_afsdb_unittest.cc
@@ -162,7 +162,7 @@ TEST_F(Rdata_AFSDB_Test, toWireRenderer) {
renderer.clear();
// construct actual data
- Name("example.com.").toWire(obuffer);
+ renderer.writeName(Name("example.com."));
rdata_afsdb2.toWire(renderer);
// construct expected data
diff --git a/src/lib/dns/tests/rdata_cname_unittest.cc b/src/lib/dns/tests/rdata_cname_unittest.cc
index d6b02aa..2cce9bc 100644
--- a/src/lib/dns/tests/rdata_cname_unittest.cc
+++ b/src/lib/dns/tests/rdata_cname_unittest.cc
@@ -97,11 +97,11 @@ TEST_F(Rdata_CNAME_Test, toWireBuffer) {
TEST_F(Rdata_CNAME_Test, toWireRenderer) {
rdata_cname.toWire(renderer);
EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
- obuffer.getData(), obuffer.getLength(),
+ renderer.getData(), renderer.getLength(),
wiredata_cname, sizeof(wiredata_cname));
rdata_cname2.toWire(renderer);
EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
- obuffer.getData(), obuffer.getLength(),
+ renderer.getData(), renderer.getLength(),
wiredata_cname2, sizeof(wiredata_cname2));
}
diff --git a/src/lib/dns/tests/rdata_dname_unittest.cc b/src/lib/dns/tests/rdata_dname_unittest.cc
index ebd9e0e..cf3001c 100644
--- a/src/lib/dns/tests/rdata_dname_unittest.cc
+++ b/src/lib/dns/tests/rdata_dname_unittest.cc
@@ -97,11 +97,11 @@ TEST_F(Rdata_DNAME_Test, toWireBuffer) {
TEST_F(Rdata_DNAME_Test, toWireRenderer) {
rdata_dname.toWire(renderer);
EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
- obuffer.getData(), obuffer.getLength(),
+ renderer.getData(), renderer.getLength(),
wiredata_dname, sizeof(wiredata_dname));
rdata_dname2.toWire(renderer);
EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
- obuffer.getData(), obuffer.getLength(),
+ renderer.getData(), renderer.getLength(),
wiredata_dname2, sizeof(wiredata_dname2));
}
diff --git a/src/lib/dns/tests/rdata_dnskey_unittest.cc b/src/lib/dns/tests/rdata_dnskey_unittest.cc
index f0596ed..86b8f69 100644
--- a/src/lib/dns/tests/rdata_dnskey_unittest.cc
+++ b/src/lib/dns/tests/rdata_dnskey_unittest.cc
@@ -90,8 +90,8 @@ TEST_F(Rdata_DNSKEY_Test, toWireRenderer) {
vector<unsigned char> data;
UnitTestUtil::readWireData("rdata_dnskey_fromWire", data);
EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
- static_cast<const uint8_t *>(obuffer.getData()) + 2,
- obuffer.getLength() - 2, &data[2], data.size() - 2);
+ static_cast<const uint8_t *>(renderer.getData()) + 2,
+ renderer.getLength() - 2, &data[2], data.size() - 2);
}
TEST_F(Rdata_DNSKEY_Test, toWireBuffer) {
diff --git a/src/lib/dns/tests/rdata_ds_like_unittest.cc b/src/lib/dns/tests/rdata_ds_like_unittest.cc
index 9b29446..6172431 100644
--- a/src/lib/dns/tests/rdata_ds_like_unittest.cc
+++ b/src/lib/dns/tests/rdata_ds_like_unittest.cc
@@ -115,8 +115,8 @@ TYPED_TEST(Rdata_DS_LIKE_Test, toWireRenderer) {
UnitTestUtil::readWireData("rdata_ds_fromWire", data);
EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
static_cast<const uint8_t*>
- (this->obuffer.getData()) + 2,
- this->obuffer.getLength() - 2,
+ (this->renderer.getData()) + 2,
+ this->renderer.getLength() - 2,
&data[2], data.size() - 2);
}
diff --git a/src/lib/dns/tests/rdata_hinfo_unittest.cc b/src/lib/dns/tests/rdata_hinfo_unittest.cc
index c52b2a0..c934a4f 100644
--- a/src/lib/dns/tests/rdata_hinfo_unittest.cc
+++ b/src/lib/dns/tests/rdata_hinfo_unittest.cc
@@ -94,8 +94,9 @@ TEST_F(Rdata_HINFO_Test, toWireRenderer) {
HINFO hinfo(hinfo_str);
hinfo.toWire(renderer);
- EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, obuffer.getData(),
- obuffer.getLength(), hinfo_rdata, sizeof(hinfo_rdata));
+ EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, renderer.getData(),
+ renderer.getLength(), hinfo_rdata,
+ sizeof(hinfo_rdata));
}
TEST_F(Rdata_HINFO_Test, compare) {
diff --git a/src/lib/dns/tests/rdata_in_a_unittest.cc b/src/lib/dns/tests/rdata_in_a_unittest.cc
index 47e2bfa..af4369d 100644
--- a/src/lib/dns/tests/rdata_in_a_unittest.cc
+++ b/src/lib/dns/tests/rdata_in_a_unittest.cc
@@ -78,7 +78,7 @@ TEST_F(Rdata_IN_A_Test, toWireBuffer) {
TEST_F(Rdata_IN_A_Test, toWireRenderer) {
rdata_in_a.toWire(renderer);
EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
- obuffer.getData(), obuffer.getLength(),
+ renderer.getData(), renderer.getLength(),
wiredata_in_a, sizeof(wiredata_in_a));
}
diff --git a/src/lib/dns/tests/rdata_in_aaaa_unittest.cc b/src/lib/dns/tests/rdata_in_aaaa_unittest.cc
index 6fd4d0e..c3e1e16 100644
--- a/src/lib/dns/tests/rdata_in_aaaa_unittest.cc
+++ b/src/lib/dns/tests/rdata_in_aaaa_unittest.cc
@@ -76,7 +76,7 @@ TEST_F(Rdata_IN_AAAA_Test, toWireBuffer) {
TEST_F(Rdata_IN_AAAA_Test, toWireRenderer) {
rdata_in_aaaa.toWire(renderer);
EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
- obuffer.getData(), obuffer.getLength(),
+ renderer.getData(), renderer.getLength(),
wiredata_in_aaaa, sizeof(wiredata_in_aaaa));
}
diff --git a/src/lib/dns/tests/rdata_minfo_unittest.cc b/src/lib/dns/tests/rdata_minfo_unittest.cc
index 30c7c39..78e8325 100644
--- a/src/lib/dns/tests/rdata_minfo_unittest.cc
+++ b/src/lib/dns/tests/rdata_minfo_unittest.cc
@@ -142,15 +142,15 @@ TEST_F(Rdata_MINFO_Test, toWireRenderer) {
vector<unsigned char> data;
UnitTestUtil::readWireData("rdata_minfo_toWire1.wire", data);
EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
- static_cast<const uint8_t *>(obuffer.getData()),
- obuffer.getLength(), &data[0], data.size());
+ static_cast<const uint8_t *>(renderer.getData()),
+ renderer.getLength(), &data[0], data.size());
renderer.clear();
rdata_minfo2.toWire(renderer);
vector<unsigned char> data2;
UnitTestUtil::readWireData("rdata_minfo_toWire2.wire", data2);
EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
- static_cast<const uint8_t *>(obuffer.getData()),
- obuffer.getLength(), &data2[0], data2.size());
+ static_cast<const uint8_t *>(renderer.getData()),
+ renderer.getLength(), &data2[0], data2.size());
}
TEST_F(Rdata_MINFO_Test, toText) {
diff --git a/src/lib/dns/tests/rdata_mx_unittest.cc b/src/lib/dns/tests/rdata_mx_unittest.cc
index c4c9757..36814ea 100644
--- a/src/lib/dns/tests/rdata_mx_unittest.cc
+++ b/src/lib/dns/tests/rdata_mx_unittest.cc
@@ -68,12 +68,12 @@ TEST_F(Rdata_MX_Test, toWireRenderer) {
vector<unsigned char> data;
UnitTestUtil::readWireData("rdata_mx_toWire1", data);
- EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, obuffer.getData(),
- obuffer.getLength(), &data[0], data.size());
+ EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, renderer.getData(),
+ renderer.getLength(), &data[0], data.size());
}
TEST_F(Rdata_MX_Test, toWireBuffer) {
- renderer.writeName(Name("example.com"));
+ Name("example.com").toWire(obuffer);
rdata_mx.toWire(obuffer);
vector<unsigned char> data;
diff --git a/src/lib/dns/tests/rdata_naptr_unittest.cc b/src/lib/dns/tests/rdata_naptr_unittest.cc
index f905943..5abcaef 100644
--- a/src/lib/dns/tests/rdata_naptr_unittest.cc
+++ b/src/lib/dns/tests/rdata_naptr_unittest.cc
@@ -140,8 +140,9 @@ TEST_F(Rdata_NAPTR_Test, toWireRenderer) {
NAPTR naptr(naptr_str);
naptr.toWire(renderer);
- EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, obuffer.getData(),
- obuffer.getLength(), naptr_rdata, sizeof(naptr_rdata));
+ EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, renderer.getData(),
+ renderer.getLength(), naptr_rdata,
+ sizeof(naptr_rdata));
}
TEST_F(Rdata_NAPTR_Test, toText) {
diff --git a/src/lib/dns/tests/rdata_ns_unittest.cc b/src/lib/dns/tests/rdata_ns_unittest.cc
index b805783..47582ce 100644
--- a/src/lib/dns/tests/rdata_ns_unittest.cc
+++ b/src/lib/dns/tests/rdata_ns_unittest.cc
@@ -96,11 +96,11 @@ TEST_F(Rdata_NS_Test, toWireBuffer) {
TEST_F(Rdata_NS_Test, toWireRenderer) {
rdata_ns.toWire(renderer);
EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
- obuffer.getData(), obuffer.getLength(),
+ renderer.getData(), renderer.getLength(),
wiredata_ns, sizeof(wiredata_ns));
rdata_ns2.toWire(renderer);
EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
- obuffer.getData(), obuffer.getLength(),
+ renderer.getData(), renderer.getLength(),
wiredata_ns2, sizeof(wiredata_ns2));
}
diff --git a/src/lib/dns/tests/rdata_nsec3_unittest.cc b/src/lib/dns/tests/rdata_nsec3_unittest.cc
index 441c6d8..edd2d4b 100644
--- a/src/lib/dns/tests/rdata_nsec3_unittest.cc
+++ b/src/lib/dns/tests/rdata_nsec3_unittest.cc
@@ -39,13 +39,19 @@ using namespace isc::util::encode;
using namespace isc::dns::rdata;
namespace {
+
+// Note: some tests can be shared with NSEC3PARAM. They are unified as
+// typed tests defined in nsec3param_like_unittest.
class Rdata_NSEC3_Test : public RdataTest {
// there's nothing to specialize
public:
Rdata_NSEC3_Test() :
nsec3_txt("1 1 1 D399EAAB H9RSFB7FPF2L8HG35CMPC765TDK23RP6 "
- "NS SOA RRSIG DNSKEY NSEC3PARAM") {}
- string nsec3_txt;
+ "NS SOA RRSIG DNSKEY NSEC3PARAM"),
+ nsec3_nosalt_txt("1 1 1 - H9RSFB7FPF2L8HG35CMPC765TDK23RP6 A" )
+ {}
+ const string nsec3_txt;
+ const string nsec3_nosalt_txt;
};
TEST_F(Rdata_NSEC3_Test, fromText) {
@@ -53,21 +59,6 @@ TEST_F(Rdata_NSEC3_Test, fromText) {
// text and construct nsec3_txt. It will be tested against the wire format
// representation in the createFromWire test.
- // Numeric parameters have possible maximum values. Unusual, but must
- // be accepted.
- EXPECT_NO_THROW(generic::NSEC3("255 255 65535 D399EAAB "
- "H9RSFB7FPF2L8HG35CMPC765TDK23RP6 "
- "NS SOA RRSIG DNSKEY NSEC3PARAM"));
-
- // 0-length salt
- EXPECT_EQ(0, generic::NSEC3("1 1 1 - H9RSFB7FPF2L8HG35CMPC765TDK23RP6 "
- "A").getSalt().size());
-
- // salt that has the possible max length
- EXPECT_EQ(255, generic::NSEC3("1 1 1 " + string(255 * 2, '0') +
- " H9RSFB7FPF2L8HG35CMPC765TDK23RP6 "
- "NS").getSalt().size());
-
// hash that has the possible max length (see badText about the magic
// numbers)
EXPECT_EQ(255, generic::NSEC3("1 1 1 D399EAAB " +
@@ -79,43 +70,20 @@ TEST_F(Rdata_NSEC3_Test, fromText) {
"1 1 1 D399EAAB H9RSFB7FPF2L8HG35CMPC765TDK23RP6"));
}
-TEST_F(Rdata_NSEC3_Test, toText) {
- const generic::NSEC3 rdata_nsec3(nsec3_txt);
- EXPECT_EQ(nsec3_txt, rdata_nsec3.toText());
-}
-
TEST_F(Rdata_NSEC3_Test, badText) {
EXPECT_THROW(generic::NSEC3("1 1 1 ADDAFEEE "
"0123456789ABCDEFGHIJKLMNOPQRSTUV "
"BIFF POW SPOON"),
InvalidRdataText);
- EXPECT_THROW(generic::NSEC3("1 1 1 ADDAFEE "
- "WXYZWXYZWXYZ=WXYZWXYZ==WXYZWXYZW A NS SOA"),
- BadValue); // bad hex
- EXPECT_THROW(generic::NSEC3("1 1 1 -- H9RSFB7FPF2L8HG35CMPC765TDK23RP6 "
- "A"),
- BadValue); // this shouldn't be confused a valid empty salt
EXPECT_THROW(generic::NSEC3("1 1 1 ADDAFEEE "
"WXYZWXYZWXYZ=WXYZWXYZ==WXYZWXYZW A NS SOA"),
BadValue); // bad base32hex
- EXPECT_THROW(generic::NSEC3("1000000 1 1 ADDAFEEE "
- "0123456789ABCDEFGHIJKLMNOPQRSTUV A NS SOA"),
- InvalidRdataText);
- EXPECT_THROW(generic::NSEC3("1 1000000 1 ADDAFEEE "
- "0123456789ABCDEFGHIJKLMNOPQRSTUV A NS SOA"),
- InvalidRdataText);
EXPECT_THROW(generic::NSEC3("1 1 1000000 ADDAFEEE "
"0123456789ABCDEFGHIJKLMNOPQRSTUV A NS SOA"),
InvalidRdataText);
- // There should be a space between "1" and "D399EAAB" (salt)
- EXPECT_THROW(generic::NSEC3(
- "1 1 1D399EAAB H9RSFB7FPF2L8HG35CMPC765TDK23RP6 "
- "NS SOA RRSIG DNSKEY NSEC3PARAM"), InvalidRdataText);
-
- // Salt is too long (possible max + 1 bytes)
- EXPECT_THROW(generic::NSEC3("1 1 1 " + string(256 * 2, '0') +
- " H9RSFB7FPF2L8HG35CMPC765TDK23RP6 NS"),
+ // Next hash shouldn't be padded
+ EXPECT_THROW(generic::NSEC3("1 1 1 ADDAFEEE CPNMU=== A NS SOA"),
InvalidRdataText);
// Hash is too long. Max = 255 bytes, base32-hex converts each 5 bytes
@@ -127,34 +95,12 @@ TEST_F(Rdata_NSEC3_Test, badText) {
}
TEST_F(Rdata_NSEC3_Test, createFromWire) {
- // Normal case
- const generic::NSEC3 rdata_nsec3(nsec3_txt);
- EXPECT_EQ(0, rdata_nsec3.compare(
- *rdataFactoryFromFile(RRType::NSEC3(), RRClass::IN(),
- "rdata_nsec3_fromWire1")));
-
// A valid NSEC3 RR with empty type bitmap.
EXPECT_NO_THROW(rdataFactoryFromFile(RRType::NSEC3(), RRClass::IN(),
"rdata_nsec3_fromWire15.wire"));
- // Too short RDLENGTH: it doesn't even contain the first 5 octets.
- EXPECT_THROW(rdataFactoryFromFile(RRType::NSEC3(), RRClass::IN(),
- "rdata_nsec3_fromWire2.wire"),
- DNSMessageFORMERR);
-
// Invalid bitmap cases are tested in Rdata_NSECBITMAP_Test.
- // salt length is too large
- EXPECT_THROW(rdataFactoryFromFile(RRType::NSEC3(), RRClass::IN(),
- "rdata_nsec3_fromWire11.wire"),
- DNSMessageFORMERR);
-
- // empty salt. unusual, but valid.
- ConstRdataPtr rdata =
- rdataFactoryFromFile(RRType::NSEC3(), RRClass::IN(),
- "rdata_nsec3_fromWire13.wire");
- EXPECT_EQ(0, dynamic_cast<const generic::NSEC3&>(*rdata).getSalt().size());
-
// hash length is too large
EXPECT_THROW(rdataFactoryFromFile(RRType::NSEC3(), RRClass::IN(),
"rdata_nsec3_fromWire12.wire"),
@@ -165,7 +111,11 @@ TEST_F(Rdata_NSEC3_Test, createFromWire) {
"rdata_nsec3_fromWire14.wire"),
DNSMessageFORMERR);
- //
+ // RDLEN is too short to hold the hash length field
+ EXPECT_THROW(rdataFactoryFromFile(RRType::NSEC3(), RRClass::IN(),
+ "rdata_nsec3_fromWire17.wire"),
+ DNSMessageFORMERR);
+
// Short buffer cases. The data is valid NSEC3 RDATA, but the buffer
// is trimmed at the end. All cases should result in an exception from
// the buffer class.
@@ -180,27 +130,35 @@ TEST_F(Rdata_NSEC3_Test, createFromWire) {
}
}
-TEST_F(Rdata_NSEC3_Test, toWireRenderer) {
- renderer.skip(2);
- const generic::NSEC3 rdata_nsec3(nsec3_txt);
- rdata_nsec3.toWire(renderer);
-
- vector<unsigned char> data;
- UnitTestUtil::readWireData("rdata_nsec3_fromWire1", data);
- EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
- static_cast<const uint8_t *>(obuffer.getData()) + 2,
- obuffer.getLength() - 2, &data[2], data.size() - 2);
-}
-
-TEST_F(Rdata_NSEC3_Test, toWireBuffer) {
- const generic::NSEC3 rdata_nsec3(nsec3_txt);
- rdata_nsec3.toWire(obuffer);
-}
-
TEST_F(Rdata_NSEC3_Test, assign) {
generic::NSEC3 rdata_nsec3(nsec3_txt);
generic::NSEC3 other_nsec3 = rdata_nsec3;
EXPECT_EQ(0, rdata_nsec3.compare(other_nsec3));
}
+TEST_F(Rdata_NSEC3_Test, compare) {
+ // trivial case: self equivalence
+ EXPECT_EQ(0, generic::NSEC3(nsec3_txt).compare(generic::NSEC3(nsec3_txt)));
+
+ // comparison attempt between incompatible RR types should be rejected
+ EXPECT_THROW(generic::NSEC3(nsec3_txt).compare(*rdata_nomatch),
+ bad_cast);
+
+ // test RDATAs, sorted in the ascendent order. We only check comparison
+ // on NSEC3-specific fields. Bitmap comparison is tested in the bitmap
+ // tests. Common cases for NSEC3 and NSECPARAM3 are in their shared tests.
+ vector<generic::NSEC3> compare_set;
+ compare_set.push_back(generic::NSEC3("1 1 1 FF99EA0000 D1K6GQ38"));
+ compare_set.push_back(generic::NSEC3("1 1 1 FF99EA0000 D1K6GQ0000000000"));
+ compare_set.push_back(generic::NSEC3("1 1 1 FF99EA0000 D1K6GQ00UUUUUUUU"));
+
+ vector<generic::NSEC3>::const_iterator it;
+ const vector<generic::NSEC3>::const_iterator it_end = compare_set.end();
+ for (it = compare_set.begin(); it != it_end - 1; ++it) {
+ SCOPED_TRACE("compare " + it->toText() + " to " + (it + 1)->toText());
+ EXPECT_GT(0, (*it).compare(*(it + 1)));
+ EXPECT_LT(0, (*(it + 1)).compare(*it));
+ }
+}
+
}
diff --git a/src/lib/dns/tests/rdata_nsec3param_like_unittest.cc b/src/lib/dns/tests/rdata_nsec3param_like_unittest.cc
new file mode 100644
index 0000000..51fef01
--- /dev/null
+++ b/src/lib/dns/tests/rdata_nsec3param_like_unittest.cc
@@ -0,0 +1,260 @@
+// Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <dns/exceptions.h>
+#include <dns/rdata.h>
+#include <dns/rdataclass.h>
+#include <dns/rrclass.h>
+#include <dns/rrtype.h>
+
+#include <gtest/gtest.h>
+
+#include <dns/tests/unittest_util.h>
+#include <dns/tests/rdata_unittest.h>
+
+#include <string>
+#include <vector>
+
+using namespace std;
+using isc::UnitTestUtil;
+using namespace isc::dns;
+using namespace isc::dns::rdata;
+
+namespace {
+
+// Template for shared tests for NSEC3 and NSEC3PARAM
+template <typename RDATA_TYPE>
+class NSEC3PARAMLikeTest : public RdataTest {
+protected:
+ NSEC3PARAMLikeTest() :
+ salt_txt("1 1 1 D399EAAB" + getCommonText()),
+ nosalt_txt("1 1 1 -" + getCommonText()),
+ obuffer(0)
+ {}
+
+ RDATA_TYPE fromText(const string& rdata_text) {
+ return (RDATA_TYPE(rdata_text));
+ }
+
+ void compareCheck() const {
+ typename vector<RDATA_TYPE>::const_iterator it;
+ typename vector<RDATA_TYPE>::const_iterator const it_end =
+ compare_set.end();
+ for (it = compare_set.begin(); it != it_end - 1; ++it) {
+ SCOPED_TRACE("compare " + it->toText() + " to " +
+ (it + 1)->toText());
+ EXPECT_GT(0, (*it).compare(*(it + 1)));
+ EXPECT_LT(0, (*(it + 1)).compare(*it));
+ }
+ }
+
+ const string salt_txt; // RDATA text with salt
+ const string nosalt_txt; // RDATA text without salt
+ OutputBuffer obuffer; // used in toWire() tests
+ MessageRenderer renderer; // ditto
+ vector<RDATA_TYPE> compare_set; // used in compare() tests
+
+ // Convert generic Rdata to the corresponding derived Rdata class object.
+ // Defined here because it depends on the template parameter.
+ static const RDATA_TYPE& convert(const Rdata& rdata) {
+ return (dynamic_cast<const RDATA_TYPE&>(rdata));
+ }
+
+ // These depend on the specific RR type. We use specialized methods
+ // for them.
+ static RRType getType(); // return either RRType::NSEC3() or NSEC3PARAM()
+ static string getWireFilePrefix();
+ static string getCommonText(); // commonly used part of textual form
+};
+
+// Instantiate specific typed tests
+typedef ::testing::Types<generic::NSEC3, generic::NSEC3PARAM> TestRdataTypes;
+TYPED_TEST_CASE(NSEC3PARAMLikeTest, TestRdataTypes);
+
+template <>
+RRType
+NSEC3PARAMLikeTest<generic::NSEC3>::getType() {
+ return (RRType::NSEC3());
+}
+
+template <>
+RRType
+NSEC3PARAMLikeTest<generic::NSEC3PARAM>::getType() {
+ return (RRType::NSEC3PARAM());
+}
+
+template <>
+string
+NSEC3PARAMLikeTest<generic::NSEC3>::getWireFilePrefix() {
+ return ("rdata_nsec3_");
+}
+
+template <>
+string
+NSEC3PARAMLikeTest<generic::NSEC3PARAM>::getWireFilePrefix() {
+ return ("rdata_nsec3param_");
+}
+
+template <>
+string
+NSEC3PARAMLikeTest<generic::NSEC3>::getCommonText() {
+ // next hash + RR type bitmap
+ return (" H9RSFB7FPF2L8HG35CMPC765TDK23RP6 "
+ "NS SOA RRSIG DNSKEY NSEC3PARAM");
+}
+
+template <>
+string
+NSEC3PARAMLikeTest<generic::NSEC3PARAM>::getCommonText() {
+ // there's no more text for NSEC3PARAM
+ return ("");
+}
+
+TYPED_TEST(NSEC3PARAMLikeTest, fromText) {
+ // Numeric parameters have possible maximum values. Unusual, but must
+ // be accepted.
+ EXPECT_NO_THROW(this->fromText("255 255 65535 D399EAAB" +
+ this->getCommonText()));
+
+ // 0-length salt
+ EXPECT_EQ(0, this->fromText(this->nosalt_txt).getSalt().size());
+
+ // salt that has the possible max length
+ EXPECT_EQ(255, this->fromText("1 1 1 " + string(255 * 2, '0') +
+ this->getCommonText()).getSalt().size());
+}
+
+TYPED_TEST(NSEC3PARAMLikeTest, badText) {
+ // Bad salt hex
+ EXPECT_THROW(this->fromText("1 1 1 SPORK0" + this->getCommonText()),
+ isc::BadValue);
+ EXPECT_THROW(this->fromText("1 1 1 ADDAFEE" + this->getCommonText()),
+ isc::BadValue);
+
+ // Space within salt
+ EXPECT_THROW(this->fromText("1 1 1 ADDAFE ADDAFEEE" +
+ this->getCommonText()),
+ InvalidRdataText);
+
+ // Similar to empty salt, but not really. This shouldn't cause confusion.
+ EXPECT_THROW(this->fromText("1 1 1 --" + this->getCommonText()),
+ isc::BadValue);
+
+ // Too large algorithm
+ EXPECT_THROW(this->fromText("1000000 1 1 ADDAFEEE" + this->getCommonText()),
+ InvalidRdataText);
+
+ // Too large flags
+ EXPECT_THROW(this->fromText("1 1000000 1 ADDAFEEE" + this->getCommonText()),
+ InvalidRdataText);
+
+ // Too large iterations
+ EXPECT_THROW(this->fromText("1 1 65536 ADDAFEEE" + this->getCommonText()),
+ InvalidRdataText);
+
+ // There should be a space between "1" and "D399EAAB" (salt)
+ EXPECT_THROW(this->fromText("1 1 1D399EAAB" + this->getCommonText()),
+ InvalidRdataText);
+
+ // Salt is too long (possible max + 1 bytes)
+ EXPECT_THROW(this->fromText("1 1 1 " + string(256 * 2, '0') +
+ this->getCommonText()),
+ InvalidRdataText);
+}
+
+TYPED_TEST(NSEC3PARAMLikeTest, toText) {
+ // normal case
+ EXPECT_EQ(this->salt_txt, this->fromText(this->salt_txt).toText());
+
+ // empty salt case
+ EXPECT_EQ(this->nosalt_txt, this->fromText(this->nosalt_txt).toText());
+}
+
+TYPED_TEST(NSEC3PARAMLikeTest, createFromWire) {
+ // Normal case
+ EXPECT_EQ(0, this->fromText(this->salt_txt).compare(
+ *this->rdataFactoryFromFile(this->getType(), RRClass::IN(),
+ (this->getWireFilePrefix() +
+ "fromWire1").c_str())));
+
+ // Too short RDLENGTH: it doesn't even contain the first 5 octets.
+ EXPECT_THROW(this->rdataFactoryFromFile(this->getType(), RRClass::IN(),
+ (this->getWireFilePrefix() +
+ "fromWire2.wire").c_str()),
+ DNSMessageFORMERR);
+
+ // salt length is too large
+ EXPECT_THROW(this->rdataFactoryFromFile(this->getType(), RRClass::IN(),
+ (this->getWireFilePrefix() +
+ "fromWire11.wire").c_str()),
+ DNSMessageFORMERR);
+
+ // empty salt. not so usual, but valid.
+ ConstRdataPtr rdata =
+ this->rdataFactoryFromFile(this->getType(), RRClass::IN(),
+ (this->getWireFilePrefix() +
+ "fromWire13.wire").c_str());
+ EXPECT_EQ(0, this->convert(*rdata).getSalt().size());
+}
+
+template <typename OUTPUT_TYPE>
+void
+toWireCheck(RRType rrtype, OUTPUT_TYPE& output, const string& data_file) {
+ vector<uint8_t> data;
+ UnitTestUtil::readWireData(data_file.c_str(), data);
+ InputBuffer buffer(&data[0], data.size());
+ const uint16_t rdlen = buffer.readUint16();
+
+ output.clear();
+ output.writeUint16(rdlen);
+ createRdata(rrtype, RRClass::IN(), buffer, rdlen)->toWire(output);
+ EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, output.getData(),
+ output.getLength(), &data[0], data.size());
+}
+
+TYPED_TEST(NSEC3PARAMLikeTest, toWire) {
+ // normal case
+ toWireCheck(this->getType(), this->renderer,
+ this->getWireFilePrefix() + "fromWire1");
+ toWireCheck(this->getType(), this->obuffer,
+ this->getWireFilePrefix() + "fromWire1");
+
+ // empty salt
+ toWireCheck(this->getType(), this->renderer,
+ this->getWireFilePrefix() + "fromWire13.wire");
+ toWireCheck(this->getType(), this->obuffer,
+ this->getWireFilePrefix() + "fromWire13.wire");
+}
+
+TYPED_TEST(NSEC3PARAMLikeTest, compare) {
+ // test RDATAs, sorted in the ascendent order.
+ this->compare_set.push_back(this->fromText("0 0 0 D399EAAB" +
+ this->getCommonText()));
+ this->compare_set.push_back(this->fromText("1 0 0 D399EAAB" +
+ this->getCommonText()));
+ this->compare_set.push_back(this->fromText("1 1 0 D399EAAB" +
+ this->getCommonText()));
+ this->compare_set.push_back(this->fromText("1 1 1 -" +
+ this->getCommonText()));
+ this->compare_set.push_back(this->fromText("1 1 1 D399EAAB" +
+ this->getCommonText()));
+ this->compare_set.push_back(this->fromText("1 1 1 FF99EAAB" +
+ this->getCommonText()));
+ this->compare_set.push_back(this->fromText("1 1 1 FF99EA0000" +
+ this->getCommonText()));
+
+ this->compareCheck();
+}
+
+}
diff --git a/src/lib/dns/tests/rdata_nsec3param_unittest.cc b/src/lib/dns/tests/rdata_nsec3param_unittest.cc
index bcbc0d7..7558b42 100644
--- a/src/lib/dns/tests/rdata_nsec3param_unittest.cc
+++ b/src/lib/dns/tests/rdata_nsec3param_unittest.cc
@@ -41,14 +41,14 @@ using namespace isc::dns::rdata;
namespace {
class Rdata_NSEC3PARAM_Test : public RdataTest {
public:
- Rdata_NSEC3PARAM_Test() : nsec3param_txt("1 0 1 D399EAAB") {}
+ Rdata_NSEC3PARAM_Test() : nsec3param_txt("1 1 1 D399EAAB") {}
const string nsec3param_txt;
};
TEST_F(Rdata_NSEC3PARAM_Test, fromText) {
// With a salt
EXPECT_EQ(1, generic::NSEC3PARAM(nsec3param_txt).getHashalg());
- EXPECT_EQ(0, generic::NSEC3PARAM(nsec3param_txt).getFlags());
+ EXPECT_EQ(1, generic::NSEC3PARAM(nsec3param_txt).getFlags());
// (salt is checked in the toText test)
// With an empty salt
@@ -61,16 +61,9 @@ TEST_F(Rdata_NSEC3PARAM_Test, toText) {
}
TEST_F(Rdata_NSEC3PARAM_Test, badText) {
- EXPECT_THROW(generic::NSEC3PARAM("1 1 1 SPORK"), BadValue); // bad hex
- EXPECT_THROW(generic::NSEC3PARAM("100000 1 1 ADDAFEE"), InvalidRdataText);
- EXPECT_THROW(generic::NSEC3PARAM("1 100000 1 ADDAFEE"), InvalidRdataText);
- EXPECT_THROW(generic::NSEC3PARAM("1 1 100000 ADDAFEE"), InvalidRdataText);
- EXPECT_THROW(generic::NSEC3PARAM("1"), InvalidRdataText);
-}
-
-TEST_F(Rdata_NSEC3PARAM_Test, DISABLED_badText) {
- // this currently fails
- EXPECT_THROW(generic::NSEC3PARAM("1 0 1D399EAAB"), InvalidRdataText);
+ // garbage space at the end
+ EXPECT_THROW(generic::NSEC3PARAM("1 1 1 D399EAAB "),
+ InvalidRdataText);
}
TEST_F(Rdata_NSEC3PARAM_Test, createFromWire) {
@@ -78,6 +71,19 @@ TEST_F(Rdata_NSEC3PARAM_Test, createFromWire) {
EXPECT_EQ(0, rdata_nsec3param.compare(
*rdataFactoryFromFile(RRType::NSEC3PARAM(), RRClass::IN(),
"rdata_nsec3param_fromWire1")));
+
+ // Short buffer cases. The data is valid NSEC3PARAM RDATA, but the buffer
+ // is trimmed at the end. All cases should result in an exception from
+ // the buffer class.
+ vector<uint8_t> data;
+ UnitTestUtil::readWireData("rdata_nsec3param_fromWire1", data);
+ const uint16_t rdlen = (data.at(0) << 8) + data.at(1);
+ for (int i = 0; i < rdlen; ++i) {
+ // intentionally construct a short buffer
+ InputBuffer b(&data[0] + 2, i);
+ EXPECT_THROW(createRdata(RRType::NSEC3PARAM(), RRClass::IN(), b, 9),
+ InvalidBufferPosition);
+ }
}
TEST_F(Rdata_NSEC3PARAM_Test, toWireRenderer) {
@@ -88,8 +94,8 @@ TEST_F(Rdata_NSEC3PARAM_Test, toWireRenderer) {
vector<unsigned char> data;
UnitTestUtil::readWireData("rdata_nsec3param_fromWire1", data);
EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
- static_cast<const uint8_t *>(obuffer.getData()) + 2,
- obuffer.getLength() - 2, &data[2], data.size() - 2);
+ static_cast<const uint8_t *>(renderer.getData()) + 2,
+ renderer.getLength() - 2, &data[2], data.size() - 2);
}
TEST_F(Rdata_NSEC3PARAM_Test, toWireBuffer) {
@@ -103,4 +109,16 @@ TEST_F(Rdata_NSEC3PARAM_Test, assign) {
EXPECT_EQ(0, rdata_nsec3param.compare(other_nsec3param));
}
+TEST_F(Rdata_NSEC3PARAM_Test, compare) {
+ // trivial case: self equivalence
+ EXPECT_EQ(0, generic::NSEC3PARAM(nsec3param_txt).
+ compare(generic::NSEC3PARAM(nsec3param_txt)));
+ EXPECT_EQ(0, generic::NSEC3PARAM("1 1 1 -").
+ compare(generic::NSEC3PARAM("1 1 1 -")));
+
+ // comparison attempt between incompatible RR types should be rejected
+ EXPECT_THROW(generic::NSEC3PARAM(nsec3param_txt).compare(*rdata_nomatch),
+ bad_cast);
+}
+
}
diff --git a/src/lib/dns/tests/rdata_nsec_unittest.cc b/src/lib/dns/tests/rdata_nsec_unittest.cc
index f081cd8..88c6201 100644
--- a/src/lib/dns/tests/rdata_nsec_unittest.cc
+++ b/src/lib/dns/tests/rdata_nsec_unittest.cc
@@ -38,7 +38,7 @@ class Rdata_NSEC_Test : public RdataTest {
// there's nothing to specialize
};
-string nsec_txt("www2.isc.org. CNAME RRSIG NSEC");
+const char* const nsec_txt = "www2.isc.org. CNAME RRSIG NSEC";
TEST_F(Rdata_NSEC_Test, toText_NSEC) {
const generic::NSEC rdata_nsec(nsec_txt);
@@ -74,8 +74,8 @@ TEST_F(Rdata_NSEC_Test, toWireRenderer_NSEC) {
vector<unsigned char> data;
UnitTestUtil::readWireData("rdata_nsec_fromWire1", data);
EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
- static_cast<const uint8_t *>(obuffer.getData()) + 2,
- obuffer.getLength() - 2, &data[2], data.size() - 2);
+ static_cast<const uint8_t *>(renderer.getData()) + 2,
+ renderer.getLength() - 2, &data[2], data.size() - 2);
}
TEST_F(Rdata_NSEC_Test, toWireBuffer_NSEC) {
@@ -95,4 +95,31 @@ TEST_F(Rdata_NSEC_Test, getNextName) {
EXPECT_EQ(Name("www2.isc.org"), generic::NSEC((nsec_txt)).getNextName());
}
+TEST_F(Rdata_NSEC_Test, compare) {
+ // trivial case: self equivalence
+ EXPECT_EQ(0, generic::NSEC("example A").
+ compare(generic::NSEC("example. A")));
+ EXPECT_EQ(0, generic::NSEC("EXAMPLE A"). // should be case insensitive
+ compare(generic::NSEC("example. A")));
+
+ // comparison attempt between incompatible RR types should be rejected
+ EXPECT_THROW(generic::NSEC(nsec_txt).compare(*rdata_nomatch),
+ bad_cast);
+
+ // test RDATAs, sorted in the ascendent order. We only compare the
+ // next name here. Bitmap comparison is tested in the bitmap tests.
+ // Note that names are compared as wire-format data, not based on the
+ // domain name comparison.
+ vector<generic::NSEC> compare_set;
+ compare_set.push_back(generic::NSEC("a.example. A"));
+ compare_set.push_back(generic::NSEC("example. A"));
+ vector<generic::NSEC>::const_iterator it;
+ const vector<generic::NSEC>::const_iterator it_end = compare_set.end();
+ for (it = compare_set.begin(); it != it_end - 1; ++it) {
+ SCOPED_TRACE("compare " + it->toText() + " to " + (it + 1)->toText());
+ EXPECT_GT(0, (*it).compare(*(it + 1)));
+ EXPECT_LT(0, (*(it + 1)).compare(*it));
+ }
+}
+
}
diff --git a/src/lib/dns/tests/rdata_nsecbitmap_unittest.cc b/src/lib/dns/tests/rdata_nsecbitmap_unittest.cc
index 8a90878..d7bce96 100644
--- a/src/lib/dns/tests/rdata_nsecbitmap_unittest.cc
+++ b/src/lib/dns/tests/rdata_nsecbitmap_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 <dns/tests/unittest_util.h>
+
#include <dns/exceptions.h>
#include <dns/rdata.h>
#include <dns/rdataclass.h>
@@ -22,82 +24,251 @@
#include <dns/tests/rdata_unittest.h>
+#include <boost/lexical_cast.hpp>
+
+#include <string>
+#include <vector>
+
+using namespace std;
+using boost::lexical_cast;
+using isc::UnitTestUtil;
using namespace isc::dns;
using namespace isc::dns::rdata;
namespace {
-class Rdata_NSECBITMAP_Test : public RdataTest {
- // there's nothing to specialize
+
+// Template for shared tests for NSEC and NSEC3 bitmaps
+template <typename RDATA_TYPE>
+class NSECLikeBitmapTest : public RdataTest {
+protected:
+ RDATA_TYPE fromText(const string& rdata_text) {
+ return (RDATA_TYPE(rdata_text));
+ }
+
+ vector<RDATA_TYPE> compare_set; // used in compare() tests
+
+ void compareCheck() const {
+ typename vector<RDATA_TYPE>::const_iterator it;
+ typename vector<RDATA_TYPE>::const_iterator const it_end =
+ compare_set.end();
+ for (it = compare_set.begin(); it != it_end - 1; ++it) {
+ SCOPED_TRACE("compare " + it->toText() + " to " +
+ (it + 1)->toText());
+ EXPECT_GT(0, (*it).compare(*(it + 1)));
+ EXPECT_LT(0, (*(it + 1)).compare(*it));
+ }
+ }
+
+ // These depend on the specific RR type. We use specialized methods
+ // for them.
+ static RRType getType(); // return either RRType::NSEC() or NSEC3()
+ static string getWireFilePrefix();
+ static string getCommonText(); // commonly used part of textual form
};
+// Instantiate specific typed tests
+typedef ::testing::Types<generic::NSEC, generic::NSEC3> TestRdataTypes;
+TYPED_TEST_CASE(NSECLikeBitmapTest, TestRdataTypes);
+
+// NSEC and NSEC3 bitmaps have some subtle differences, in which case we
+// need to test them separately. Using these typedef type names with TEST_F
+// will do the trick.
+typedef NSECLikeBitmapTest<generic::NSEC3> NSEC3BitmapTest;
+typedef NSECLikeBitmapTest<generic::NSEC> NSECBitmapTest;
+
+template <>
+string
+NSECLikeBitmapTest<generic::NSEC>::getWireFilePrefix() {
+ return ("rdata_nsec_");
+}
+
+template <>
+RRType
+NSECLikeBitmapTest<generic::NSEC>::getType() {
+ return (RRType::NSEC());
+}
+
+template <>
+string
+NSECLikeBitmapTest<generic::NSEC3>::getWireFilePrefix() {
+ return ("rdata_nsec3_");
+}
+
+template <>
+RRType
+NSECLikeBitmapTest<generic::NSEC3>::getType() {
+ return (RRType::NSEC3());
+}
+
+template <>
+string
+NSECLikeBitmapTest<generic::NSEC>::getCommonText() {
+ return ("next. ");
+}
+
+template <>
+string
+NSECLikeBitmapTest<generic::NSEC3>::getCommonText() {
+ return ("1 1 12 AABBCCDD 2T7B4G4VSA5SMI47K61MV5BV1A22BOJR ");
+}
+
// Tests against various types of bogus NSEC/NSEC3 type bitmaps.
// The syntax and semantics are common for both RR types, and our
// implementation of that part is shared, so in theory it should be sufficient
// to test for only one RR type. But we check for both just in case.
-TEST_F(Rdata_NSECBITMAP_Test, createFromWire_NSEC) {
+TYPED_TEST(NSECLikeBitmapTest, createFromWire) {
// A malformed NSEC bitmap length field that could cause overflow.
- EXPECT_THROW(rdataFactoryFromFile(RRType::NSEC(), RRClass::IN(),
- "rdata_nsec_fromWire4.wire"),
- DNSMessageFORMERR);
- EXPECT_THROW(rdataFactoryFromFile(RRType::NSEC3(), RRClass::IN(),
- "rdata_nsec3_fromWire4.wire"),
+ EXPECT_THROW(this->rdataFactoryFromFile(this->getType(), RRClass::IN(),
+ (this->getWireFilePrefix() +
+ "fromWire4.wire").c_str()),
DNSMessageFORMERR);
// The bitmap field is incomplete (only the first byte is included)
- EXPECT_THROW(rdataFactoryFromFile(RRType::NSEC(), RRClass::IN(),
- "rdata_nsec_fromWire5.wire"),
- DNSMessageFORMERR);
- EXPECT_THROW(rdataFactoryFromFile(RRType::NSEC3(), RRClass::IN(),
- "rdata_nsec3_fromWire5.wire"),
+ EXPECT_THROW(this->rdataFactoryFromFile(this->getType(), RRClass::IN(),
+ (this->getWireFilePrefix() +
+ "fromWire5.wire").c_str()),
DNSMessageFORMERR);
// Bitmap length is 0, which is invalid.
- EXPECT_THROW(rdataFactoryFromFile(RRType::NSEC(), RRClass::IN(),
- "rdata_nsec_fromWire6.wire"),
- DNSMessageFORMERR);
- EXPECT_THROW(rdataFactoryFromFile(RRType::NSEC3(), RRClass::IN(),
- "rdata_nsec3_fromWire6.wire"),
+ EXPECT_THROW(this->rdataFactoryFromFile(this->getType(), RRClass::IN(),
+ (this->getWireFilePrefix() +
+ "fromWire6.wire").c_str()),
DNSMessageFORMERR);
// Too large bitmap length with a short buffer.
- EXPECT_THROW(rdataFactoryFromFile(RRType::NSEC(), RRClass::IN(),
- "rdata_nsec_fromWire3"),
- DNSMessageFORMERR);
- EXPECT_THROW(rdataFactoryFromFile(RRType::NSEC3(), RRClass::IN(),
- "rdata_nsec3_fromWire3"),
+ EXPECT_THROW(this->rdataFactoryFromFile(this->getType(), RRClass::IN(),
+ (this->getWireFilePrefix() +
+ "fromWire3").c_str()),
DNSMessageFORMERR);
// A boundary case: longest possible bitmaps (32 maps). This should be
// accepted.
- EXPECT_NO_THROW(rdataFactoryFromFile(RRType::NSEC(), RRClass::IN(),
- "rdata_nsec_fromWire7.wire"));
- EXPECT_NO_THROW(rdataFactoryFromFile(RRType::NSEC3(), RRClass::IN(),
- "rdata_nsec3_fromWire7.wire"));
+ EXPECT_NO_THROW(this->rdataFactoryFromFile(this->getType(), RRClass::IN(),
+ (this->getWireFilePrefix() +
+ "fromWire7.wire").c_str()));
// Another boundary condition: 33 bitmaps, which should be rejected.
- EXPECT_THROW(rdataFactoryFromFile(RRType::NSEC(), RRClass::IN(),
- "rdata_nsec_fromWire8.wire"),
- DNSMessageFORMERR);
- EXPECT_THROW(rdataFactoryFromFile(RRType::NSEC3(), RRClass::IN(),
- "rdata_nsec3_fromWire8.wire"),
+ EXPECT_THROW(this->rdataFactoryFromFile(this->getType(), RRClass::IN(),
+ (this->getWireFilePrefix() +
+ "fromWire8.wire").c_str()),
DNSMessageFORMERR);
// Disordered bitmap window blocks.
- EXPECT_THROW(rdataFactoryFromFile(RRType::NSEC(), RRClass::IN(),
- "rdata_nsec_fromWire9.wire"),
- DNSMessageFORMERR);
- EXPECT_THROW(rdataFactoryFromFile(RRType::NSEC3(), RRClass::IN(),
- "rdata_nsec3_fromWire9.wire"),
+ EXPECT_THROW(this->rdataFactoryFromFile(this->getType(), RRClass::IN(),
+ (this->getWireFilePrefix() +
+ "fromWire9.wire").c_str()),
DNSMessageFORMERR);
// Bitmap ending with all-zero bytes. Not necessarily harmful except
// the additional overhead of parsing, but invalid according to the
// spec anyway.
- EXPECT_THROW(rdataFactoryFromFile(RRType::NSEC(), RRClass::IN(),
- "rdata_nsec_fromWire10.wire"),
+ EXPECT_THROW(this->rdataFactoryFromFile(this->getType(), RRClass::IN(),
+ (this->getWireFilePrefix() +
+ "fromWire10.wire").c_str()),
DNSMessageFORMERR);
- EXPECT_THROW(rdataFactoryFromFile(RRType::NSEC3(), RRClass::IN(),
- "rdata_nsec3_fromWire10.wire"),
+}
+
+TYPED_TEST(NSECLikeBitmapTest, badText) {
+ // redundant space after the sequence
+ EXPECT_THROW(this->fromText(this->getCommonText() + "A "),
+ InvalidRdataText);
+}
+
+// This tests the result of toText() with various kinds of NSEC/NSEC3 bitmaps.
+// It also tests the "from text" constructor as a result.
+TYPED_TEST(NSECLikeBitmapTest, toText) {
+ // A simple case (some commonly seen RR types in NSEC(3) bitmaps)
+ string rdata_text = this->getCommonText() + "NS SOA RRSIG DNSKEY";
+ EXPECT_EQ(rdata_text, this->fromText(rdata_text).toText());
+
+ // Similar to above, but involves more than one bitmap window blocks.
+ rdata_text = this->getCommonText() + "NS DLV";
+ EXPECT_EQ(rdata_text, this->fromText(rdata_text).toText());
+
+ // Make sure all possible bits in a one-octet bitmap field are handled
+ // correctly.
+ // We use the range around 1024 (reasonably higher number) so it's
+ // unlikely that they have predefined mnemonic and can be safely converted
+ // to TYPEnnnn by toText().
+ for (unsigned int i = 1024; i < 1032; ++i) {
+ rdata_text = this->getCommonText() + "TYPE" + lexical_cast<string>(i);
+ EXPECT_EQ(rdata_text, this->fromText(rdata_text).toText());
+ }
+
+ // Make sure all possible 32 octets in a longest possible block are
+ // handled correctly.
+ for (unsigned int i = 1024; i < 1024 + 256; i += 8) {
+ rdata_text = this->getCommonText() + "TYPE" + lexical_cast<string>(i);
+ EXPECT_EQ(rdata_text, this->fromText(rdata_text).toText());
+ }
+
+ // Check for the highest window block.
+ rdata_text = this->getCommonText() + "TYPE65535";
+ EXPECT_EQ(rdata_text, this->fromText(rdata_text).toText());
+}
+
+TYPED_TEST(NSECLikeBitmapTest, compare) {
+ // Bit map: [win=0][len=1] 00000010
+ this->compare_set.push_back(this->fromText(this->getCommonText() + "SOA"));
+ // Bit map: [win=0][len=1] 00000010, [win=4][len=1] 10000000
+ this->compare_set.push_back(this->fromText(this->getCommonText() +
+ "SOA TYPE1024"));
+ // Bit map: [win=0][len=1] 00100000
+ this->compare_set.push_back(this->fromText(this->getCommonText() + "NS"));
+ // Bit map: [win=0][len=1] 00100010
+ this->compare_set.push_back(this->fromText(this->getCommonText() +
+ "NS SOA"));
+ // Bit map: [win=0][len=2] 00100000, 00000001
+ this->compare_set.push_back(this->fromText(this->getCommonText() +
+ "NS MX"));
+ // Bit map: [win=4][len=1] 10000000
+ this->compare_set.push_back(this->fromText(this->getCommonText() +
+ "TYPE1024"));
+
+ this->compareCheck();
+}
+
+// NSEC bitmaps must not be empty
+TEST_F(NSECBitmapTest, emptyMap) {
+ EXPECT_THROW(this->fromText("next.example").toText(), InvalidRdataText);
+
+ EXPECT_THROW(this->rdataFactoryFromFile(this->getType(), RRClass::IN(),
+ (this->getWireFilePrefix() +
+ "fromWire16.wire").c_str()),
DNSMessageFORMERR);
}
+
+// NSEC3 bitmaps can be empty
+TEST_F(NSEC3BitmapTest, emptyMap) {
+ // Read wire data wit an empty NSEC3 bitmap. This should succeed.
+ vector<uint8_t> data;
+ UnitTestUtil::readWireData((this->getWireFilePrefix() +
+ "fromWire16.wire").c_str(), data);
+ InputBuffer buffer(&data[0], data.size());
+ const uint16_t rdlen = buffer.readUint16();
+ const generic::NSEC3 empty_nsec3 =
+ dynamic_cast<const generic::NSEC3&>(*createRdata(
+ RRType::NSEC3(), RRClass::IN(),
+ buffer, rdlen));
+
+ // Check the toText() result.
+ EXPECT_EQ("1 0 1 7373737373 D1K6GQ38D1K6GQ38D1K6GQ38D1K6GQ38",
+ empty_nsec3.toText());
+
+ // Check the toWire() result.
+ OutputBuffer obuffer(0);
+ obuffer.writeUint16(rdlen);
+ empty_nsec3.toWire(obuffer);
+ EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, obuffer.getData(),
+ obuffer.getLength(), &data[0], data.size());
+
+ // Same for MessageRenderer.
+ obuffer.clear();
+ MessageRenderer renderer;
+ renderer.writeUint16(rdlen);
+ empty_nsec3.toWire(renderer);
+ EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, renderer.getData(),
+ renderer.getLength(), &data[0], data.size());
+}
+
}
diff --git a/src/lib/dns/tests/rdata_ptr_unittest.cc b/src/lib/dns/tests/rdata_ptr_unittest.cc
index 7f5de20..86160fb 100644
--- a/src/lib/dns/tests/rdata_ptr_unittest.cc
+++ b/src/lib/dns/tests/rdata_ptr_unittest.cc
@@ -100,11 +100,11 @@ TEST_F(Rdata_PTR_Test, toWireBuffer) {
TEST_F(Rdata_PTR_Test, toWireRenderer) {
rdata_ptr.toWire(renderer);
EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
- obuffer.getData(), obuffer.getLength(),
+ renderer.getData(), renderer.getLength(),
wiredata_ptr, sizeof(wiredata_ptr));
rdata_ptr2.toWire(renderer);
EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
- obuffer.getData(), obuffer.getLength(),
+ renderer.getData(), renderer.getLength(),
wiredata_ptr2, sizeof(wiredata_ptr2));
}
diff --git a/src/lib/dns/tests/rdata_rp_unittest.cc b/src/lib/dns/tests/rdata_rp_unittest.cc
index 07f5a93..20f32b9 100644
--- a/src/lib/dns/tests/rdata_rp_unittest.cc
+++ b/src/lib/dns/tests/rdata_rp_unittest.cc
@@ -38,7 +38,7 @@ protected:
// this also serves as a test for "from text" constructor in a normal
// case.
rdata_rp("root.example.com. rp-text.example.com."),
- obuffer(0), renderer(obuffer)
+ obuffer(0)
{}
const Name mailbox_name, text_name;
diff --git a/src/lib/dns/tests/rdata_soa_unittest.cc b/src/lib/dns/tests/rdata_soa_unittest.cc
index 07c24d5..a9d782c 100644
--- a/src/lib/dns/tests/rdata_soa_unittest.cc
+++ b/src/lib/dns/tests/rdata_soa_unittest.cc
@@ -56,8 +56,8 @@ TEST_F(Rdata_SOA_Test, toWireRenderer) {
vector<unsigned char> data;
UnitTestUtil::readWireData("rdata_soa_fromWire", data);
EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
- static_cast<const uint8_t *>(obuffer.getData()) + 2,
- obuffer.getLength() - 2, &data[2], data.size() - 2);
+ static_cast<const uint8_t *>(renderer.getData()) + 2,
+ renderer.getLength() - 2, &data[2], data.size() - 2);
}
TEST_F(Rdata_SOA_Test, toWireBuffer) {
diff --git a/src/lib/dns/tests/rdata_srv_unittest.cc b/src/lib/dns/tests/rdata_srv_unittest.cc
index 3394f43..b194b1c 100644
--- a/src/lib/dns/tests/rdata_srv_unittest.cc
+++ b/src/lib/dns/tests/rdata_srv_unittest.cc
@@ -134,12 +134,12 @@ TEST_F(Rdata_SRV_Test, toWireBuffer) {
TEST_F(Rdata_SRV_Test, toWireRenderer) {
rdata_srv.toWire(renderer);
EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
- obuffer.getData(), obuffer.getLength(),
+ renderer.getData(), renderer.getLength(),
wiredata_srv, sizeof(wiredata_srv));
renderer.clear();
rdata_srv2.toWire(renderer);
EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
- obuffer.getData(), obuffer.getLength(),
+ renderer.getData(), renderer.getLength(),
wiredata_srv2, sizeof(wiredata_srv2));
}
diff --git a/src/lib/dns/tests/rdata_unittest.cc b/src/lib/dns/tests/rdata_unittest.cc
index fa791dc..5be06e8 100644
--- a/src/lib/dns/tests/rdata_unittest.cc
+++ b/src/lib/dns/tests/rdata_unittest.cc
@@ -38,8 +38,7 @@ namespace isc {
namespace dns {
namespace rdata {
RdataTest::RdataTest() :
- obuffer(0), renderer(obuffer),
- rdata_nomatch(createRdata(RRType(0), RRClass(1), "\\# 0"))
+ obuffer(0), rdata_nomatch(createRdata(RRType(0), RRClass(1), "\\# 0"))
{}
RdataPtr
@@ -245,7 +244,7 @@ TEST_F(Rdata_Unknown_Test, toWireBuffer) {
TEST_F(Rdata_Unknown_Test, toWireRenderer) {
rdata_unknown.toWire(renderer);
EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
- obuffer.getData(), obuffer.getLength(),
+ renderer.getData(), renderer.getLength(),
wiredata_unknown, sizeof(wiredata_unknown));
}
diff --git a/src/lib/dns/tests/rdatafields_unittest.cc b/src/lib/dns/tests/rdatafields_unittest.cc
index d619220..ef83ed4 100644
--- a/src/lib/dns/tests/rdatafields_unittest.cc
+++ b/src/lib/dns/tests/rdatafields_unittest.cc
@@ -36,9 +36,7 @@ using isc::util::InputBuffer;
namespace {
class RdataFieldsTest : public ::testing::Test {
protected:
- RdataFieldsTest() : obuffer(0), renderer_buffer(0),
- renderer(renderer_buffer),
- ns_name("example.com"),
+ RdataFieldsTest() : obuffer(0), ns_name("example.com"),
other_name("www.example.com")
{}
void constructCommonTests(const RdataFields& fields,
@@ -49,7 +47,6 @@ protected:
void constructCommonTestsRRSIG(const RdataFields& fields);
void constructCommonTestsOPT(const RdataFields& fields);
OutputBuffer obuffer;
- OutputBuffer renderer_buffer;
MessageRenderer renderer;
const Name ns_name;
const Name other_name;
diff --git a/src/lib/dns/tests/rrclass_unittest.cc b/src/lib/dns/tests/rrclass_unittest.cc
index 15f9a8c..6156be5 100644
--- a/src/lib/dns/tests/rrclass_unittest.cc
+++ b/src/lib/dns/tests/rrclass_unittest.cc
@@ -28,7 +28,7 @@ using namespace isc::util;
namespace {
class RRClassTest : public ::testing::Test {
protected:
- RRClassTest() : obuffer(0), renderer(obuffer) {}
+ RRClassTest() : obuffer(0) {}
OutputBuffer obuffer;
MessageRenderer renderer;
@@ -116,7 +116,7 @@ TEST_F(RRClassTest, toWireRenderer) {
rrclass_max.toWire(renderer);
EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
- obuffer.getData(), obuffer.getLength(),
+ renderer.getData(), renderer.getLength(),
wiredata, sizeof(wiredata));
}
diff --git a/src/lib/dns/tests/rrset_unittest.cc b/src/lib/dns/tests/rrset_unittest.cc
index f951341..a6bfe56 100644
--- a/src/lib/dns/tests/rrset_unittest.cc
+++ b/src/lib/dns/tests/rrset_unittest.cc
@@ -38,7 +38,7 @@ using namespace isc::dns::rdata;
namespace {
class RRsetTest : public ::testing::Test {
protected:
- RRsetTest() : buffer(0), renderer(buffer),
+ RRsetTest() : buffer(0),
test_name("test.example.com"),
test_domain("example.com"),
test_nsname("ns.example.com"),
@@ -201,8 +201,8 @@ TEST_F(RRsetTest, toWireRenderer) {
rrset_ns.toWire(renderer);
UnitTestUtil::readWireData("rrset_toWire2", wiredata);
- EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, buffer.getData(),
- buffer.getLength(), &wiredata[0], wiredata.size());
+ EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, renderer.getData(),
+ renderer.getLength(), &wiredata[0], wiredata.size());
// toWire() cannot be performed for an empty RRset.
renderer.clear();
diff --git a/src/lib/dns/tests/rrttl_unittest.cc b/src/lib/dns/tests/rrttl_unittest.cc
index fe75eb6..703616c 100644
--- a/src/lib/dns/tests/rrttl_unittest.cc
+++ b/src/lib/dns/tests/rrttl_unittest.cc
@@ -28,7 +28,7 @@ using namespace isc::util;
namespace {
class RRTTLTest : public ::testing::Test {
protected:
- RRTTLTest() : obuffer(0), renderer(obuffer) {}
+ RRTTLTest() : obuffer(0) {}
OutputBuffer obuffer;
MessageRenderer renderer;
@@ -114,7 +114,7 @@ TEST_F(RRTTLTest, toWireRenderer) {
ttl_max.toWire(renderer);
EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
- obuffer.getData(), obuffer.getLength(),
+ renderer.getData(), renderer.getLength(),
wiredata, sizeof(wiredata));
}
@@ -138,7 +138,7 @@ TEST_F(RRTTLTest, leq) {
// small <= small is true
EXPECT_TRUE(ttl_small.leq(ttl_small));
- EXPECT_TRUE(ttl_small <= ttl_small);
+ EXPECT_LE(ttl_small, ttl_small);
// large <= small is false
EXPECT_FALSE(ttl_large.leq(ttl_small));
@@ -150,7 +150,7 @@ TEST_F(RRTTLTest, geq) {
EXPECT_TRUE(ttl_large >= ttl_small);
EXPECT_TRUE(ttl_large.geq(ttl_large));
- EXPECT_TRUE(ttl_large >= ttl_large);
+ EXPECT_GE(ttl_large, ttl_large);
EXPECT_FALSE(ttl_small.geq(ttl_large));
EXPECT_FALSE(ttl_small >= ttl_large);
diff --git a/src/lib/dns/tests/rrtype_unittest.cc b/src/lib/dns/tests/rrtype_unittest.cc
index 12f6001..28ecee6 100644
--- a/src/lib/dns/tests/rrtype_unittest.cc
+++ b/src/lib/dns/tests/rrtype_unittest.cc
@@ -28,7 +28,7 @@ using namespace isc::util;
namespace {
class RRTypeTest : public ::testing::Test {
protected:
- RRTypeTest() : obuffer(0), renderer(obuffer) {}
+ RRTypeTest() : obuffer(0) {}
OutputBuffer obuffer;
MessageRenderer renderer;
@@ -120,7 +120,7 @@ TEST_F(RRTypeTest, toWireRenderer) {
rrtype_max.toWire(renderer);
EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
- obuffer.getData(), obuffer.getLength(),
+ renderer.getData(), renderer.getLength(),
wiredata, sizeof(wiredata));
}
diff --git a/src/lib/dns/tests/testdata/.gitignore b/src/lib/dns/tests/testdata/.gitignore
new file mode 100644
index 0000000..e56355b
--- /dev/null
+++ b/src/lib/dns/tests/testdata/.gitignore
@@ -0,0 +1,117 @@
+/edns_toWire1.wire
+/edns_toWire2.wire
+/edns_toWire3.wire
+/edns_toWire4.wire
+/message_fromWire10.wire
+/message_fromWire11.wire
+/message_fromWire12.wire
+/message_fromWire13.wire
+/message_fromWire14.wire
+/message_fromWire15.wire
+/message_fromWire16.wire
+/message_fromWire17.wire
+/message_fromWire18.wire
+/message_fromWire19.wire
+/message_fromWire20.wire
+/message_fromWire21.wire
+/message_fromWire22.wire
+/message_toText1.wire
+/message_toText2.wire
+/message_toText3.wire
+/message_toWire2.wire
+/message_toWire3.wire
+/message_toWire4.wire
+/message_toWire5.wire
+/name_toWire5.wire
+/name_toWire6.wire
+/rdata_afsdb_fromWire1.wire
+/rdata_afsdb_fromWire2.wire
+/rdata_afsdb_fromWire3.wire
+/rdata_afsdb_fromWire4.wire
+/rdata_afsdb_fromWire5.wire
+/rdata_afsdb_toWire1.wire
+/rdata_afsdb_toWire2.wire
+/rdata_minfo_fromWire1.wire
+/rdata_minfo_fromWire2.wire
+/rdata_minfo_fromWire3.wire
+/rdata_minfo_fromWire4.wire
+/rdata_minfo_fromWire5.wire
+/rdata_minfo_fromWire6.wire
+/rdata_minfo_toWire1.wire
+/rdata_minfo_toWire2.wire
+/rdata_minfo_toWireUncompressed1.wire
+/rdata_minfo_toWireUncompressed2.wire
+/rdata_nsec3_fromWire10.wire
+/rdata_nsec3_fromWire11.wire
+/rdata_nsec3_fromWire12.wire
+/rdata_nsec3_fromWire13.wire
+/rdata_nsec3_fromWire14.wire
+/rdata_nsec3_fromWire15.wire
+/rdata_nsec3_fromWire16.wire
+/rdata_nsec3_fromWire17.wire
+/rdata_nsec3_fromWire2.wire
+/rdata_nsec3_fromWire4.wire
+/rdata_nsec3_fromWire5.wire
+/rdata_nsec3_fromWire6.wire
+/rdata_nsec3_fromWire7.wire
+/rdata_nsec3_fromWire8.wire
+/rdata_nsec3_fromWire9.wire
+/rdata_nsec3param_fromWire11.wire
+/rdata_nsec3param_fromWire13.wire
+/rdata_nsec3param_fromWire2.wire
+/rdata_nsec_fromWire10.wire
+/rdata_nsec_fromWire16.wire
+/rdata_nsec_fromWire4.wire
+/rdata_nsec_fromWire5.wire
+/rdata_nsec_fromWire6.wire
+/rdata_nsec_fromWire7.wire
+/rdata_nsec_fromWire8.wire
+/rdata_nsec_fromWire9.wire
+/rdata_rp_fromWire1.wire
+/rdata_rp_fromWire2.wire
+/rdata_rp_fromWire3.wire
+/rdata_rp_fromWire4.wire
+/rdata_rp_fromWire5.wire
+/rdata_rp_fromWire6.wire
+/rdata_rp_toWire1.wire
+/rdata_rp_toWire2.wire
+/rdata_rrsig_fromWire2.wire
+/rdata_soa_toWireUncompressed.wire
+/rdata_sshfp_fromWire1.wire
+/rdata_sshfp_fromWire2.wire
+/rdata_tsig_fromWire1.wire
+/rdata_tsig_fromWire2.wire
+/rdata_tsig_fromWire3.wire
+/rdata_tsig_fromWire4.wire
+/rdata_tsig_fromWire5.wire
+/rdata_tsig_fromWire6.wire
+/rdata_tsig_fromWire7.wire
+/rdata_tsig_fromWire8.wire
+/rdata_tsig_fromWire9.wire
+/rdata_tsig_toWire1.wire
+/rdata_tsig_toWire2.wire
+/rdata_tsig_toWire3.wire
+/rdata_tsig_toWire4.wire
+/rdata_tsig_toWire5.wire
+/rdata_txt_fromWire2.wire
+/rdata_txt_fromWire3.wire
+/rdata_txt_fromWire4.wire
+/rdata_txt_fromWire5.wire
+/rdatafields1.wire
+/rdatafields2.wire
+/rdatafields3.wire
+/rdatafields4.wire
+/rdatafields5.wire
+/rdatafields6.wire
+/tsig_verify1.wire
+/tsig_verify10.wire
+/tsig_verify2.wire
+/tsig_verify3.wire
+/tsig_verify4.wire
+/tsig_verify5.wire
+/tsig_verify6.wire
+/tsig_verify7.wire
+/tsig_verify8.wire
+/tsig_verify9.wire
+/tsigrecord_toWire1.wire
+/tsigrecord_toWire2.wire
diff --git a/src/lib/dns/tests/testdata/Makefile.am b/src/lib/dns/tests/testdata/Makefile.am
index 27edf5f..fdf1025 100644
--- a/src/lib/dns/tests/testdata/Makefile.am
+++ b/src/lib/dns/tests/testdata/Makefile.am
@@ -20,6 +20,8 @@ BUILT_SOURCES += rdata_nsec_fromWire4.wire rdata_nsec_fromWire5.wire
BUILT_SOURCES += rdata_nsec_fromWire6.wire rdata_nsec_fromWire7.wire
BUILT_SOURCES += rdata_nsec_fromWire8.wire rdata_nsec_fromWire9.wire
BUILT_SOURCES += rdata_nsec_fromWire10.wire
+# 11-15 are skipped to be consistent with NSEC3 test data
+BUILT_SOURCES += rdata_nsec_fromWire16.wire
BUILT_SOURCES += rdata_nsec3_fromWire2.wire
BUILT_SOURCES += rdata_nsec3_fromWire4.wire rdata_nsec3_fromWire5.wire
BUILT_SOURCES += rdata_nsec3_fromWire6.wire rdata_nsec3_fromWire7.wire
@@ -27,6 +29,10 @@ BUILT_SOURCES += rdata_nsec3_fromWire8.wire rdata_nsec3_fromWire9.wire
BUILT_SOURCES += rdata_nsec3_fromWire10.wire rdata_nsec3_fromWire11.wire
BUILT_SOURCES += rdata_nsec3_fromWire12.wire rdata_nsec3_fromWire13.wire
BUILT_SOURCES += rdata_nsec3_fromWire14.wire rdata_nsec3_fromWire15.wire
+BUILT_SOURCES += rdata_nsec3_fromWire16.wire rdata_nsec3_fromWire17.wire
+BUILT_SOURCES += rdata_nsec3param_fromWire2.wire
+BUILT_SOURCES += rdata_nsec3param_fromWire11.wire
+BUILT_SOURCES += rdata_nsec3param_fromWire13.wire
BUILT_SOURCES += rdata_rrsig_fromWire2.wire
BUILT_SOURCES += rdata_minfo_fromWire1.wire rdata_minfo_fromWire2.wire
BUILT_SOURCES += rdata_minfo_fromWire3.wire rdata_minfo_fromWire4.wire
@@ -99,7 +105,11 @@ EXTRA_DIST += rdata_nsec_fromWire4.spec rdata_nsec_fromWire5.spec
EXTRA_DIST += rdata_nsec_fromWire6.spec rdata_nsec_fromWire7.spec
EXTRA_DIST += rdata_nsec_fromWire8.spec rdata_nsec_fromWire9.spec
EXTRA_DIST += rdata_nsec_fromWire10.spec
+EXTRA_DIST += rdata_nsec_fromWire16.spec
EXTRA_DIST += rdata_nsec3param_fromWire1
+EXTRA_DIST += rdata_nsec3param_fromWire2.spec
+EXTRA_DIST += rdata_nsec3param_fromWire11.spec
+EXTRA_DIST += rdata_nsec3param_fromWire13.spec
EXTRA_DIST += rdata_nsec3_fromWire1
EXTRA_DIST += rdata_nsec3_fromWire2.spec rdata_nsec3_fromWire3
EXTRA_DIST += rdata_nsec3_fromWire4.spec rdata_nsec3_fromWire5.spec
@@ -108,6 +118,7 @@ EXTRA_DIST += rdata_nsec3_fromWire8.spec rdata_nsec3_fromWire9.spec
EXTRA_DIST += rdata_nsec3_fromWire10.spec rdata_nsec3_fromWire11.spec
EXTRA_DIST += rdata_nsec3_fromWire12.spec rdata_nsec3_fromWire13.spec
EXTRA_DIST += rdata_nsec3_fromWire14.spec rdata_nsec3_fromWire15.spec
+EXTRA_DIST += rdata_nsec3_fromWire16.spec rdata_nsec3_fromWire17.spec
EXTRA_DIST += rdata_opt_fromWire rdata_rrsig_fromWire1
EXTRA_DIST += rdata_rrsig_fromWire2.spec
EXTRA_DIST += rdata_rp_fromWire1.spec rdata_rp_fromWire2.spec
diff --git a/src/lib/dns/tests/testdata/rdata_nsec3_fromWire16.spec b/src/lib/dns/tests/testdata/rdata_nsec3_fromWire16.spec
new file mode 100644
index 0000000..dac14ea
--- /dev/null
+++ b/src/lib/dns/tests/testdata/rdata_nsec3_fromWire16.spec
@@ -0,0 +1,8 @@
+#
+# NSEC3 RDATA with an empty bitmap
+#
+
+[custom]
+sections: nsec3
+[nsec3]
+nbitmap: 0
diff --git a/src/lib/dns/tests/testdata/rdata_nsec3_fromWire17.spec b/src/lib/dns/tests/testdata/rdata_nsec3_fromWire17.spec
new file mode 100644
index 0000000..4253349
--- /dev/null
+++ b/src/lib/dns/tests/testdata/rdata_nsec3_fromWire17.spec
@@ -0,0 +1,8 @@
+#
+# An invalid NSEC3 RDATA: RDLEN is too short to include the hash len field.
+#
+
+[custom]
+sections: nsec3
+[nsec3]
+rdlen: 10
diff --git a/src/lib/dns/tests/testdata/rdata_nsec3param_fromWire1 b/src/lib/dns/tests/testdata/rdata_nsec3param_fromWire1
index 3f99f9d..1b8697f 100644
--- a/src/lib/dns/tests/testdata/rdata_nsec3param_fromWire1
+++ b/src/lib/dns/tests/testdata/rdata_nsec3param_fromWire1
@@ -1,5 +1,5 @@
# RDLENGTH, 9 bytes
00 09
# NSEC3PARAM record
-# 1 0 1 D399EAAB
-01 00 00 01 04 d3 99 ea ab
+# 1 1 1 D399EAAB
+01 01 00 01 04 d3 99 ea ab
diff --git a/src/lib/dns/tests/testdata/rdata_nsec3param_fromWire11.spec b/src/lib/dns/tests/testdata/rdata_nsec3param_fromWire11.spec
new file mode 100644
index 0000000..41e1784
--- /dev/null
+++ b/src/lib/dns/tests/testdata/rdata_nsec3param_fromWire11.spec
@@ -0,0 +1,8 @@
+#
+# An invalid NSEC3PARAM RDATA: Saltlen is too large
+#
+
+[custom]
+sections: nsec3param
+[nsec3param]
+rdlen: 7
diff --git a/src/lib/dns/tests/testdata/rdata_nsec3param_fromWire13.spec b/src/lib/dns/tests/testdata/rdata_nsec3param_fromWire13.spec
new file mode 100644
index 0000000..311b2dd
--- /dev/null
+++ b/src/lib/dns/tests/testdata/rdata_nsec3param_fromWire13.spec
@@ -0,0 +1,9 @@
+#
+# A valid (but unusual) NSEC3PARAM RDATA: salt is empty.
+#
+
+[custom]
+sections: nsec3param
+[nsec3param]
+saltlen: 0
+salt: ''
diff --git a/src/lib/dns/tests/testdata/rdata_nsec3param_fromWire2.spec b/src/lib/dns/tests/testdata/rdata_nsec3param_fromWire2.spec
new file mode 100644
index 0000000..bce65ff
--- /dev/null
+++ b/src/lib/dns/tests/testdata/rdata_nsec3param_fromWire2.spec
@@ -0,0 +1,9 @@
+#
+# A malformed NSEC3PARAM RDATA: RDLEN indicates it doesn't even contain the
+# fixed 5 octects
+#
+
+[custom]
+sections: nsec3param
+[nsec3param]
+rdlen: 4
diff --git a/src/lib/dns/tests/testdata/rdata_nsec_fromWire16.spec b/src/lib/dns/tests/testdata/rdata_nsec_fromWire16.spec
new file mode 100644
index 0000000..d7faeed
--- /dev/null
+++ b/src/lib/dns/tests/testdata/rdata_nsec_fromWire16.spec
@@ -0,0 +1,8 @@
+#
+# An invalid NSEC RDATA: with an empty bitmap
+#
+
+[custom]
+sections: nsec
+[nsec]
+nbitmap: 0
diff --git a/src/lib/dns/tests/tsig_unittest.cc b/src/lib/dns/tests/tsig_unittest.cc
index 7944b29..ac503e5 100644
--- a/src/lib/dns/tests/tsig_unittest.cc
+++ b/src/lib/dns/tests/tsig_unittest.cc
@@ -71,7 +71,7 @@ protected:
TSIGTest() :
tsig_ctx(NULL), qid(0x2d65), test_name("www.example.com"),
badkey_name("badkey.example.com"), test_class(RRClass::IN()),
- test_ttl(86400), message(Message::RENDER), buffer(0), renderer(buffer),
+ test_ttl(86400), message(Message::RENDER),
dummy_data(1024, 0xdd), // should be sufficiently large for all tests
dummy_record(badkey_name, any::TSIG(TSIGKey::HMACMD5_NAME(),
0x4da8877a,
@@ -125,7 +125,6 @@ protected:
const RRClass test_class;
const RRTTL test_ttl;
Message message;
- OutputBuffer buffer;
MessageRenderer renderer;
vector<uint8_t> secret;
vector<uint8_t> dummy_data;
diff --git a/src/lib/dns/tests/tsigrecord_unittest.cc b/src/lib/dns/tests/tsigrecord_unittest.cc
index e1b3e93..532681a 100644
--- a/src/lib/dns/tests/tsigrecord_unittest.cc
+++ b/src/lib/dns/tests/tsigrecord_unittest.cc
@@ -46,7 +46,7 @@ protected:
test_mac.size(), &test_mac[0],
0x2d65, 0, 0, NULL)),
test_record(test_name, test_rdata),
- buffer(0), renderer(buffer)
+ buffer(0)
{}
const Name test_name;
vector<unsigned char> test_mac;
diff --git a/src/lib/dns/tests/unittest_util.cc b/src/lib/dns/tests/unittest_util.cc
index ca9d347..66d49a8 100644
--- a/src/lib/dns/tests/unittest_util.cc
+++ b/src/lib/dns/tests/unittest_util.cc
@@ -191,3 +191,21 @@ UnitTestUtil::createRequestMessage(Message& message,
message.addQuestion(Question(name, rrclass, rrtype));
}
+void
+UnitTestUtil::createDNSSECRequestMessage(Message& message,
+ const Opcode& opcode,
+ const uint16_t qid,
+ const Name& name,
+ const RRClass& rrclass,
+ const RRType& rrtype)
+{
+ message.clear(Message::RENDER);
+ message.setOpcode(opcode);
+ message.setRcode(Rcode::NOERROR());
+ message.setQid(qid);
+ message.addQuestion(Question(name, rrclass, rrtype));
+ EDNSPtr edns(new EDNS());
+ edns->setUDPSize(4096);
+ edns->setDNSSECAwareness(true);
+ message.setEDNS(edns);
+}
diff --git a/src/lib/dns/tests/unittest_util.h b/src/lib/dns/tests/unittest_util.h
index f85a921..ebb514d 100644
--- a/src/lib/dns/tests/unittest_util.h
+++ b/src/lib/dns/tests/unittest_util.h
@@ -93,6 +93,22 @@ public:
const isc::dns::Name& name,
const isc::dns::RRClass& rrclass,
const isc::dns::RRType& rrtype);
+
+ ///
+ /// Populate a DNSSEC request message
+ ///
+ /// Create a request message in 'request_message' using the
+ /// opcode 'opcode' and the name/class/type query tuple specified in
+ /// 'name', 'rrclass' and 'rrtype.
+ /// EDNS will be added with DO=1 and bufsize 4096
+ static void
+ createDNSSECRequestMessage(isc::dns::Message& request_message,
+ const isc::dns::Opcode& opcode,
+ const uint16_t qid,
+ const isc::dns::Name& name,
+ const isc::dns::RRClass& rrclass,
+ const isc::dns::RRType& rrtype);
+
};
}
#endif // __UNITTEST_UTIL_H
diff --git a/src/lib/exceptions/tests/.gitignore b/src/lib/exceptions/tests/.gitignore
new file mode 100644
index 0000000..d6d1ec8
--- /dev/null
+++ b/src/lib/exceptions/tests/.gitignore
@@ -0,0 +1 @@
+/run_unittests
diff --git a/src/lib/log/compiler/.gitignore b/src/lib/log/compiler/.gitignore
new file mode 100644
index 0000000..b6afc24
--- /dev/null
+++ b/src/lib/log/compiler/.gitignore
@@ -0,0 +1 @@
+/message
diff --git a/src/lib/log/compiler/message.cc b/src/lib/log/compiler/message.cc
index 3c8a3f2..fb0f038 100644
--- a/src/lib/log/compiler/message.cc
+++ b/src/lib/log/compiler/message.cc
@@ -491,22 +491,22 @@ writeProgramFile(const string& file, const vector<string>& ns_components,
}
-/// \brief Warn of Duplicate Entries
+/// \brief Error and exit if there are duplicate entries
///
-/// If the input file contained duplicate message IDs, only the first will be
-/// processed. However, we should warn about it.
+/// If the input file contained duplicate message IDs, we print an
+/// error for each of them, then exit the program with a non-0 value.
///
/// \param reader Message Reader used to read the file
void
-warnDuplicates(MessageReader& reader) {
+errorDuplicates(MessageReader& reader) {
// Get the duplicates (the overflow) and, if present, sort them into some
// order and remove those which occur more than once (which mean that they
// occur more than twice in the input file).
MessageReader::MessageIDCollection duplicates = reader.getNotAdded();
- if (duplicates.size() > 0) {
- cout << "Warning: the following duplicate IDs were found:\n";
+ if (!duplicates.empty()) {
+ cout << "Error: the following duplicate IDs were found:\n";
sort(duplicates.begin(), duplicates.end());
MessageReader::MessageIDCollection::iterator new_end =
@@ -515,6 +515,7 @@ warnDuplicates(MessageReader& reader) {
i != new_end; ++i) {
cout << " " << *i << "\n";
}
+ exit(1);
}
}
@@ -580,6 +581,9 @@ main(int argc, char* argv[]) {
MessageReader reader(&dictionary);
reader.readFile(message_file);
+ // Error (and quit) if there are of any duplicates encountered.
+ errorDuplicates(reader);
+
if (doPython) {
// Warn in case of ignored directives
if (!reader.getNamespace().empty()) {
@@ -604,8 +608,6 @@ main(int argc, char* argv[]) {
output_directory);
}
- // Finally, warn of any duplicates encountered.
- warnDuplicates(reader);
}
catch (const MessageException& e) {
// Create an error message from the ID and the text
diff --git a/src/lib/log/tests/.gitignore b/src/lib/log/tests/.gitignore
new file mode 100644
index 0000000..8d7e564
--- /dev/null
+++ b/src/lib/log/tests/.gitignore
@@ -0,0 +1,9 @@
+/console_test.sh
+/destination_test.sh
+/init_logger_test
+/init_logger_test.sh
+/local_file_test.sh
+/logger_example
+/run_unittests
+/severity_test.sh
+/tempdir.h
diff --git a/src/lib/nsas/.gitignore b/src/lib/nsas/.gitignore
new file mode 100644
index 0000000..109ef04
--- /dev/null
+++ b/src/lib/nsas/.gitignore
@@ -0,0 +1,2 @@
+/nsas_messages.cc
+/nsas_messages.h
diff --git a/src/lib/nsas/glue_hints.cc b/src/lib/nsas/glue_hints.cc
index 02c27ee..3caae25 100644
--- a/src/lib/nsas/glue_hints.cc
+++ b/src/lib/nsas/glue_hints.cc
@@ -86,8 +86,8 @@ GlueHints::GlueHints(const std::string& zone_name,
bool
GlueHints::hasGlue(AddressFamily family) const {
- return ((addresses_v4.size() > 0 && (family == ANY_OK || family == V4_ONLY)) ||
- (addresses_v6.size() > 0 && (family == ANY_OK || family == V6_ONLY)));
+ return ((!addresses_v4.empty() && (family == ANY_OK || family == V4_ONLY)) ||
+ (!addresses_v6.empty() && (family == ANY_OK || family == V6_ONLY)));
}
NameserverAddress
diff --git a/src/lib/nsas/hash_table.h b/src/lib/nsas/hash_table.h
index c028fa4..6028473 100644
--- a/src/lib/nsas/hash_table.h
+++ b/src/lib/nsas/hash_table.h
@@ -59,7 +59,7 @@ struct HashTableSlot {
/// \brief Copy Constructor
///
/// ... which as noted in the class description does not copy.
- HashTableSlot(const HashTableSlot<T>&)
+ HashTableSlot(const HashTableSlot<T>&) : mutex_(), list_()
{ }
public:
diff --git a/src/lib/nsas/tests/.gitignore b/src/lib/nsas/tests/.gitignore
new file mode 100644
index 0000000..d6d1ec8
--- /dev/null
+++ b/src/lib/nsas/tests/.gitignore
@@ -0,0 +1 @@
+/run_unittests
diff --git a/src/lib/nsas/tests/Makefile.am b/src/lib/nsas/tests/Makefile.am
index 420e897..afd91f6 100644
--- a/src/lib/nsas/tests/Makefile.am
+++ b/src/lib/nsas/tests/Makefile.am
@@ -46,11 +46,6 @@ run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
run_unittests_LDADD = $(GTEST_LDADD)
-# NOTE: we may have to clean up this hack later (see the note in configure.ac)
-if NEED_LIBBOOST_THREAD
-run_unittests_LDADD += -lboost_thread
-endif
-
run_unittests_LDADD += $(top_builddir)/src/lib/nsas/libnsas.la
run_unittests_LDADD += $(top_builddir)/src/lib/util/libutil.la
run_unittests_LDADD += $(top_builddir)/src/lib/log/liblog.la
diff --git a/src/lib/nsas/tests/nameserver_address_store_unittest.cc b/src/lib/nsas/tests/nameserver_address_store_unittest.cc
index 4785627..6ddae72 100644
--- a/src/lib/nsas/tests/nameserver_address_store_unittest.cc
+++ b/src/lib/nsas/tests/nameserver_address_store_unittest.cc
@@ -475,7 +475,7 @@ TEST_F(NameserverAddressStoreTest, updateRTT) {
// for ns.example.com (the nameserver set for example.net in the class
// initialization). We'll set two addresses.
Name ns_example_com(ns_name);
- RRsetPtr ns_address = boost::shared_ptr<RRset>(new RRset(
+ isc::dns::RRsetPtr ns_address = boost::shared_ptr<RRset>(new RRset(
ns_example_com, RRClass::IN(), RRType::A(), RRTTL(300)));
BOOST_FOREACH(string addr, address) {
ns_address->addRdata(rdata::in::A(addr));
diff --git a/src/lib/nsas/tests/nameserver_entry_unittest.cc b/src/lib/nsas/tests/nameserver_entry_unittest.cc
index 3435d26..3aca08f 100644
--- a/src/lib/nsas/tests/nameserver_entry_unittest.cc
+++ b/src/lib/nsas/tests/nameserver_entry_unittest.cc
@@ -139,7 +139,7 @@ TEST_F(NameserverEntryTest, SetRTT) {
NameserverEntry::AddressVector vec;
alpha->getAddresses(vec);
- ASSERT_TRUE(vec.size() > 0);
+ ASSERT_FALSE(vec.empty());
// Take the first address and change the RTT.
IOAddress first_address = vec[0].getAddress();
@@ -174,7 +174,7 @@ TEST_F(NameserverEntryTest, Unreachable) {
NameserverEntry::AddressVector vec;
alpha->getAddresses(vec);
- ASSERT_TRUE(vec.size() > 0);
+ ASSERT_FALSE(vec.empty());
// Take the first address and mark as unreachable.
IOAddress first_address = vec[0].getAddress();
diff --git a/src/lib/nsas/zone_entry.cc b/src/lib/nsas/zone_entry.cc
index 1c5df03..8a72e5f 100644
--- a/src/lib/nsas/zone_entry.cc
+++ b/src/lib/nsas/zone_entry.cc
@@ -329,7 +329,7 @@ updateAddressSelector(std::vector<NameserverAddress>& addresses,
it != probabilities.end(); ++it){
(*it) /= sum;
}
- } else if(probabilities.size() > 0){
+ } else if(!probabilities.empty()){
// If all the nameservers are unreachable, the sum will be 0
// So give each server equal opportunity to be selected.
for(vector<double>::iterator it = probabilities.begin();
diff --git a/src/lib/python/.gitignore b/src/lib/python/.gitignore
new file mode 100644
index 0000000..9252d05
--- /dev/null
+++ b/src/lib/python/.gitignore
@@ -0,0 +1 @@
+/bind10_config.py
diff --git a/src/lib/python/isc/Makefile.am b/src/lib/python/isc/Makefile.am
index a3e74c5..aef5dc3 100644
--- a/src/lib/python/isc/Makefile.am
+++ b/src/lib/python/isc/Makefile.am
@@ -1,5 +1,5 @@
SUBDIRS = datasrc cc config dns log net notify util testutils acl bind10
-SUBDIRS += xfrin log_messages
+SUBDIRS += xfrin log_messages server_common
python_PYTHON = __init__.py
diff --git a/src/lib/python/isc/bind10/component.py b/src/lib/python/isc/bind10/component.py
index 91b7064..da2730c 100644
--- a/src/lib/python/isc/bind10/component.py
+++ b/src/lib/python/isc/bind10/component.py
@@ -22,14 +22,16 @@ Dependencies between them are not yet handled. It might turn out they are
needed, in that case they will be added sometime in future.
This framework allows for a single process to be started multiple times (by
-specifying multiple components with the same configuration). However, the rest
-of the system might not handle such situation well, so until it is made so,
-it would be better to start each process at most once.
+specifying multiple components with the same configuration). We might want
+to add a more convenient support (like providing a count argument to the
+configuration). This is yet to be designed.
"""
import isc.log
from isc.log_messages.bind10_messages import *
import time
+import os
+import signal
logger = isc.log.Logger("boss")
DBG_TRACE_DATA = 20
@@ -45,6 +47,14 @@ STATE_DEAD = 'dead'
STATE_STOPPED = 'stopped'
STATE_RUNNING = 'running'
+def get_signame(signal_number):
+ """Return the symbolic name for a signal."""
+ for sig in dir(signal):
+ if sig.startswith("SIG") and sig[3].isalnum():
+ if getattr(signal, sig) == signal_number:
+ return sig
+ return "unknown signal"
+
class BaseComponent:
"""
This represents a single component. This one is an abstract base class.
@@ -206,8 +216,24 @@ class BaseComponent:
it is considered a core or needed component, or because
the component is to be restarted later.
"""
+
+ if exit_code is not None:
+ if os.WIFEXITED(exit_code):
+ exit_str = "process exited normally with exit status %d" % (exit_code)
+ elif os.WIFSIGNALED(exit_code):
+ sig = os.WTERMSIG(exit_code)
+ signame = get_signame(sig)
+ if os.WCOREDUMP(exit_code):
+ exit_str = "process dumped core with exit status %d (killed by signal %d: %s)" % (exit_code, sig, signame)
+ else:
+ exit_str = "process terminated with exit status %d (killed by signal %d: %s)" % (exit_code, sig, signame)
+ else:
+ exit_str = "unknown condition with exit status %d" % (exit_code)
+ else:
+ exit_str = "unknown condition"
+
logger.error(BIND10_COMPONENT_FAILED, self.name(), self.pid(),
- exit_code if exit_code is not None else "unknown")
+ exit_str)
if not self.running():
raise ValueError("Can't fail component that isn't running")
self.__state = STATE_STOPPED
@@ -408,7 +434,7 @@ class Component(BaseComponent):
self._boss.register_process(self.pid(), self)
def _stop_internal(self):
- self._boss.stop_process(self._process, self._address)
+ self._boss.stop_process(self._process, self._address, self.pid())
# TODO Some way to wait for the process that doesn't want to
# terminate and kill it would prove nice (or add it to boss somewhere?)
diff --git a/src/lib/python/isc/bind10/tests/component_test.py b/src/lib/python/isc/bind10/tests/component_test.py
index 95137a9..ec0e8af 100644
--- a/src/lib/python/isc/bind10/tests/component_test.py
+++ b/src/lib/python/isc/bind10/tests/component_test.py
@@ -553,11 +553,11 @@ class ComponentTests(BossUtils, unittest.TestCase):
self.assertEqual(42, component.pid())
self.assertEqual(component, self.__registered_processes.get(42))
- def stop_process(self, process, address):
+ def stop_process(self, process, address, pid):
"""
Part of pretending to be boss.
"""
- self.__stop_process_params = (process, address)
+ self.__stop_process_params = (process, address, pid)
def start_simple(self, process):
"""
@@ -573,9 +573,11 @@ class ComponentTests(BossUtils, unittest.TestCase):
component.start()
self.assertTrue(component.running())
self.assertEqual('component', self.__start_simple_params)
+ component.pid = lambda: 42
component.stop()
self.assertFalse(component.running())
- self.assertEqual(('component', 'Address'), self.__stop_process_params)
+ self.assertEqual(('component', 'Address', 42),
+ self.__stop_process_params)
def test_component_kill(self):
"""
diff --git a/src/lib/python/isc/cc/data.py b/src/lib/python/isc/cc/data.py
index 76ef942..636e9a9 100644
--- a/src/lib/python/isc/cc/data.py
+++ b/src/lib/python/isc/cc/data.py
@@ -21,6 +21,7 @@
#
import json
+import re
class DataNotFoundError(Exception):
"""Raised if an identifier does not exist according to a spec file,
@@ -86,6 +87,13 @@ def split_identifier(identifier):
id_parts[:] = (value for value in id_parts if value != "")
return id_parts
+def identifier_has_list_index(identifier):
+ """Returns True if the given identifier string has at least one
+ list index (with [I], where I is a number"""
+ return (type(identifier) == str and
+ re.search("\[\d+\]", identifier) is not None)
+
+
def split_identifier_list_indices(identifier):
"""Finds list indexes in the given identifier, which are of the
format [integer].
diff --git a/src/lib/python/isc/cc/tests/.gitignore b/src/lib/python/isc/cc/tests/.gitignore
new file mode 100644
index 0000000..b7ac2ae
--- /dev/null
+++ b/src/lib/python/isc/cc/tests/.gitignore
@@ -0,0 +1 @@
+/cc_test
diff --git a/src/lib/python/isc/config/ccsession.py b/src/lib/python/isc/config/ccsession.py
index 760ae55..703d196 100644
--- a/src/lib/python/isc/config/ccsession.py
+++ b/src/lib/python/isc/config/ccsession.py
@@ -38,6 +38,7 @@
from isc.cc import Session
from isc.config.config_data import ConfigData, MultiConfigData, BIND10_CONFIG_DATA_VERSION
+import isc.config.module_spec
import isc
from isc.util.file import path_search
import bind10_config
@@ -97,6 +98,7 @@ COMMAND_SET_CONFIG = "set_config"
COMMAND_GET_MODULE_SPEC = "get_module_spec"
COMMAND_MODULE_SPEC = "module_spec"
COMMAND_SHUTDOWN = "shutdown"
+COMMAND_MODULE_STOPPING = "stopping"
def parse_command(msg):
"""Parses what may be a command message. If it looks like one,
@@ -210,6 +212,24 @@ class ModuleCCSession(ConfigData):
self.__send_spec()
self.__request_config()
+ def send_stopping(self):
+ """Sends a 'stopping' message to the configuration manager. This
+ message is just an FYI, and no response is expected. Any errors
+ when sending this message (for instance if the msgq session has
+ previously been closed) are logged, but ignored."""
+ # create_command could raise an exception as well, but except for
+ # out of memory related errors, these should all be programming
+ # failures and are not caught
+ msg = create_command(COMMAND_MODULE_STOPPING,
+ self.get_module_spec().get_full_spec())
+ try:
+ self._session.group_sendmsg(msg, "ConfigManager")
+ except Exception as se:
+ # If the session was previously closed, obvously trying to send
+ # a message fails. (TODO: check if session is open so we can
+ # error on real problems?)
+ logger.error(CONFIG_SESSION_STOPPING_FAILED, se)
+
def get_socket(self):
"""Returns the socket from the command channel session. This
should *only* be used for select() loops to see if there
@@ -308,43 +328,97 @@ class ModuleCCSession(ConfigData):
and return an answer created with create_answer()"""
self._command_handler = command_handler
- def add_remote_config(self, spec_file_name, config_update_callback = None):
- """Gives access to the configuration of a different module.
- These remote module options can at this moment only be
- accessed through get_remote_config_value(). This function
- also subscribes to the channel of the remote module name
- to receive the relevant updates. It is not possible to
- specify your own handler for this right now.
- start() must have been called on this CCSession
- prior to the call to this method.
- Returns the name of the module."""
- module_spec = isc.config.module_spec_from_file(spec_file_name)
+ def _add_remote_config_internal(self, module_spec,
+ config_update_callback=None):
+ """The guts of add_remote_config and add_remote_config_by_name"""
module_cfg = ConfigData(module_spec)
module_name = module_spec.get_module_name()
+
self._session.group_subscribe(module_name)
# Get the current config for that module now
seq = self._session.group_sendmsg(create_command(COMMAND_GET_CONFIG, { "module_name": module_name }), "ConfigManager")
try:
- answer, env = self._session.group_recvmsg(False, seq)
+ answer, _ = self._session.group_recvmsg(False, seq)
except isc.cc.SessionTimeout:
raise ModuleCCSessionError("No answer from ConfigManager when "
"asking about Remote module " +
module_name)
+ call_callback = False
if answer:
rcode, value = parse_answer(answer)
if rcode == 0:
- if value != None and module_spec.validate_config(False, value):
- module_cfg.set_local_config(value)
- if config_update_callback is not None:
- config_update_callback(value, module_cfg)
+ if value != None:
+ if module_spec.validate_config(False, value):
+ module_cfg.set_local_config(value)
+ call_callback = True
+ else:
+ raise ModuleCCSessionError("Bad config data for " +
+ module_name + ": " +
+ str(value))
+ else:
+ raise ModuleCCSessionError("Failure requesting remote " +
+ "configuration data for " +
+ module_name)
# all done, add it
self._remote_module_configs[module_name] = module_cfg
self._remote_module_callbacks[module_name] = config_update_callback
+ if call_callback and config_update_callback is not None:
+ config_update_callback(value, module_cfg)
+
+ def add_remote_config_by_name(self, module_name,
+ config_update_callback=None):
+ """
+ This does the same as add_remote_config, but you provide the module name
+ instead of the name of the spec file.
+ """
+ seq = self._session.group_sendmsg(create_command(COMMAND_GET_MODULE_SPEC,
+ { "module_name":
+ module_name }),
+ "ConfigManager")
+ try:
+ answer, env = self._session.group_recvmsg(False, seq)
+ except isc.cc.SessionTimeout:
+ raise ModuleCCSessionError("No answer from ConfigManager when " +
+ "asking about for spec of Remote " +
+ "module " + module_name)
+ if answer:
+ rcode, value = parse_answer(answer)
+ if rcode == 0:
+ module_spec = isc.config.module_spec.ModuleSpec(value)
+ if module_spec.get_module_name() != module_name:
+ raise ModuleCCSessionError("Module name mismatch: " +
+ module_name + " and " +
+ module_spec.get_module_name())
+ self._add_remote_config_internal(module_spec,
+ config_update_callback)
+ else:
+ raise ModuleCCSessionError("Error code " + str(rcode) +
+ "when asking for module spec of " +
+ module_name)
+ else:
+ raise ModuleCCSessionError("No answer when asking for module " +
+ "spec of " + module_name)
+ # Just to be consistent with the add_remote_config
return module_name
-
+
+ def add_remote_config(self, spec_file_name, config_update_callback=None):
+ """Gives access to the configuration of a different module.
+ These remote module options can at this moment only be
+ accessed through get_remote_config_value(). This function
+ also subscribes to the channel of the remote module name
+ to receive the relevant updates. It is not possible to
+ specify your own handler for this right now, but you can
+ specify a callback that is called after the change happened.
+ start() must have been called on this CCSession
+ prior to the call to this method.
+ Returns the name of the module."""
+ module_spec = isc.config.module_spec_from_file(spec_file_name)
+ self._add_remote_config_internal(module_spec, config_update_callback)
+ return module_spec.get_module_name()
+
def remove_remote_config(self, module_name):
"""Removes the remote configuration access for this module"""
if module_name in self._remote_module_configs:
@@ -371,7 +445,7 @@ class ModuleCCSession(ConfigData):
except isc.cc.SessionTimeout:
# TODO: log an error?
pass
-
+
def __request_config(self):
"""Asks the configuration manager for the current configuration, and call the config handler if set.
Raises a ModuleCCSessionError if there is no answer from the configuration manager"""
@@ -410,30 +484,38 @@ class UIModuleCCSession(MultiConfigData):
passed must have send_GET and send_POST functions"""
MultiConfigData.__init__(self)
self._conn = conn
- self.request_specifications()
- self.request_current_config()
+ self.update_specs_and_config()
def request_specifications(self):
- """Request the module specifications from b10-cmdctl"""
- # this step should be unnecessary but is the current way cmdctl returns stuff
- # so changes are needed there to make this clean (we need a command to simply get the
- # full specs for everything, including commands etc, not separate gets for that)
+ """Clears the current list of specifications, and requests a new
+ list from b10-cmdctl. As other actions may have caused modules
+ to be stopped, or new modules to be added, this is expected to
+ be run after each interaction (at this moment). It is usually
+ also combined with request_current_config(). For that reason,
+ we provide update_specs_and_config() which calls both."""
specs = self._conn.send_GET('/module_spec')
+ self.clear_specifications()
for module in specs.keys():
self.set_specification(isc.config.ModuleSpec(specs[module]))
- def update_specs_and_config(self):
- self.request_specifications()
- self.request_current_config()
-
def request_current_config(self):
"""Requests the current configuration from the configuration
- manager through b10-cmdctl, and stores those as CURRENT"""
+ manager through b10-cmdctl, and stores those as CURRENT. This
+ does not modify any local changes, it just updates to the current
+ state of the server itself."""
config = self._conn.send_GET('/config_data')
if 'version' not in config or config['version'] != BIND10_CONFIG_DATA_VERSION:
raise ModuleCCSessionError("Bad config version")
self._set_current_config(config)
+ def update_specs_and_config(self):
+ """Convenience function to both clear and update the known list of
+ module specifications, and update the current configuration on
+ the server side. There are a few cases where the caller might only
+ want to run one of these tasks, but often they are both needed."""
+ self.request_specifications()
+ self.request_current_config()
+
def _add_value_to_list(self, identifier, value, module_spec):
cur_list, status = self.get_value(identifier)
if not cur_list:
@@ -474,8 +556,8 @@ class UIModuleCCSession(MultiConfigData):
self.set_value(identifier, cur_map)
else:
raise isc.cc.data.DataAlreadyPresentError(value +
- " already in "
- + identifier)
+ " already in " +
+ identifier)
def add_value(self, identifier, value_str = None, set_value_str = None):
"""Add a value to a configuration list. Raises a DataTypeError
@@ -582,7 +664,6 @@ class UIModuleCCSession(MultiConfigData):
# answer is either an empty dict (on success), or one
# containing errors
if answer == {}:
- self.request_current_config()
self.clear_local_changes()
elif "error" in answer:
raise ModuleCCSessionError("Error: " + str(answer["error"]) + "\n" + "Configuration not committed")
diff --git a/src/lib/python/isc/config/cfgmgr.py b/src/lib/python/isc/config/cfgmgr.py
index 4d568be..dd97827 100644
--- a/src/lib/python/isc/config/cfgmgr.py
+++ b/src/lib/python/isc/config/cfgmgr.py
@@ -297,7 +297,7 @@ class ConfigManager:
"""Write the current configuration to the file specificied at init()"""
self.config.write_to_file()
- def _handle_get_module_spec(self, cmd):
+ def __handle_get_module_spec(self, cmd):
"""Private function that handles the 'get_module_spec' command"""
answer = {}
if cmd != None:
@@ -318,7 +318,7 @@ class ConfigManager:
answer = ccsession.create_answer(0, self.get_module_spec())
return answer
- def _handle_get_config_dict(self, cmd):
+ def __handle_get_config_dict(self, cmd):
"""Private function that handles the 'get_config' command
where the command has been checked to be a dict"""
if 'module_name' in cmd and cmd['module_name'] != '':
@@ -332,17 +332,17 @@ class ConfigManager:
else:
return ccsession.create_answer(1, "Bad module_name in get_config command")
- def _handle_get_config(self, cmd):
+ def __handle_get_config(self, cmd):
"""Private function that handles the 'get_config' command"""
if cmd != None:
if type(cmd) == dict:
- return self._handle_get_config_dict(cmd)
+ return self.__handle_get_config_dict(cmd)
else:
return ccsession.create_answer(1, "Bad get_config command, argument not a dict")
else:
return ccsession.create_answer(0, self.config.data)
- def _handle_set_config_module(self, module_name, cmd):
+ def __handle_set_config_module(self, module_name, cmd):
# the answer comes (or does not come) from the relevant module
# so we need a variable to see if we got it
answer = None
@@ -405,7 +405,7 @@ class ConfigManager:
self.config.data = old_data
return answer
- def _handle_set_config_all(self, cmd):
+ def __handle_set_config_all(self, cmd):
old_data = copy.deepcopy(self.config.data)
got_error = False
err_list = []
@@ -413,7 +413,7 @@ class ConfigManager:
# sets, so we simply call set_config_module for each of those
for module in cmd:
if module != "version":
- answer = self._handle_set_config_module(module, cmd[module])
+ answer = self.__handle_set_config_module(module, cmd[module])
if answer == None:
got_error = True
err_list.append("No answer message from " + module)
@@ -432,16 +432,16 @@ class ConfigManager:
self.config.data = old_data
return ccsession.create_answer(1, " ".join(err_list))
- def _handle_set_config(self, cmd):
+ def __handle_set_config(self, cmd):
"""Private function that handles the 'set_config' command"""
answer = None
if cmd == None:
return ccsession.create_answer(1, "Wrong number of arguments")
if len(cmd) == 2:
- answer = self._handle_set_config_module(cmd[0], cmd[1])
+ answer = self.__handle_set_config_module(cmd[0], cmd[1])
elif len(cmd) == 1:
- answer = self._handle_set_config_all(cmd[0])
+ answer = self.__handle_set_config_all(cmd[0])
else:
answer = ccsession.create_answer(1, "Wrong number of arguments")
if not answer:
@@ -449,20 +449,41 @@ class ConfigManager:
return answer
- def _handle_module_spec(self, spec):
+ def __handle_module_spec(self, spec):
"""Private function that handles the 'module_spec' command"""
# todo: validate? (no direct access to spec as
# todo: use ModuleSpec class
# todo: error checking (like keyerrors)
answer = {}
self.set_module_spec(spec)
+ self._send_module_spec_to_cmdctl(spec.get_module_name(),
+ spec.get_full_spec())
+ return ccsession.create_answer(0)
- # We should make one general 'spec update for module' that
- # passes both specification and commands at once
+ def __handle_module_stopping(self, arg):
+ """Private function that handles a 'stopping' command;
+ The argument is of the form { 'module_name': <name> }.
+ If the module is known, it is removed from the known list,
+ and a message is sent to the Cmdctl channel to remove it as well.
+ If it is unknown, the message is ignored."""
+ if arg['module_name'] in self.module_specs:
+ del self.module_specs[arg['module_name']]
+ self._send_module_spec_to_cmdctl(arg['module_name'], None)
+ # This command is not expected to be answered
+ return None
+
+ def _send_module_spec_to_cmdctl(self, module_name, spec):
+ """Sends the given module spec for the given module name to Cmdctl.
+ Parameters:
+ module_name: A string with the name of the module
+ spec: dict containing full module specification, as returned by
+ ModuleSpec.get_full_spec(). This argument may also be None,
+ in which case it signals Cmdctl to remove said module from
+ its list.
+ No response from Cmdctl is expected."""
spec_update = ccsession.create_command(ccsession.COMMAND_MODULE_SPECIFICATION_UPDATE,
- [ spec.get_module_name(), spec.get_full_spec() ])
+ [ module_name, spec ])
self.cc.group_sendmsg(spec_update, "Cmdctl")
- return ccsession.create_answer(0)
def handle_msg(self, msg):
"""Handle a command from the cc channel to the configuration manager"""
@@ -474,17 +495,19 @@ class ConfigManager:
elif cmd == ccsession.COMMAND_GET_STATISTICS_SPEC:
answer = ccsession.create_answer(0, self.get_statistics_spec())
elif cmd == ccsession.COMMAND_GET_MODULE_SPEC:
- answer = self._handle_get_module_spec(arg)
+ answer = self.__handle_get_module_spec(arg)
elif cmd == ccsession.COMMAND_GET_CONFIG:
- answer = self._handle_get_config(arg)
+ answer = self.__handle_get_config(arg)
elif cmd == ccsession.COMMAND_SET_CONFIG:
- answer = self._handle_set_config(arg)
+ answer = self.__handle_set_config(arg)
+ elif cmd == ccsession.COMMAND_MODULE_STOPPING:
+ answer = self.__handle_module_stopping(arg)
elif cmd == ccsession.COMMAND_SHUTDOWN:
self.running = False
answer = ccsession.create_answer(0)
elif cmd == ccsession.COMMAND_MODULE_SPEC:
try:
- answer = self._handle_module_spec(isc.config.ModuleSpec(arg))
+ answer = self.__handle_module_spec(isc.config.ModuleSpec(arg))
except isc.config.ModuleSpecError as dde:
answer = ccsession.create_answer(1, "Error in data definition: " + str(dde))
else:
@@ -508,4 +531,6 @@ class ConfigManager:
# not ask
if msg is not None and not 'result' in msg:
answer = self.handle_msg(msg);
- self.cc.group_reply(env, answer)
+ # Only respond if there actually is something to respond with
+ if answer is not None:
+ self.cc.group_reply(env, answer)
diff --git a/src/lib/python/isc/config/config_data.py b/src/lib/python/isc/config/config_data.py
index b2cf048..f8da086 100644
--- a/src/lib/python/isc/config/config_data.py
+++ b/src/lib/python/isc/config/config_data.py
@@ -28,6 +28,22 @@ class ConfigDataError(Exception): pass
BIND10_CONFIG_DATA_VERSION = 2
+# Helper functions
+def spec_part_is_list(spec_part):
+ """Returns True if the given spec_part is a dict that contains a
+ list specification, and False otherwise."""
+ return (type(spec_part) == dict and 'list_item_spec' in spec_part)
+
+def spec_part_is_map(spec_part):
+ """Returns True if the given spec_part is a dict that contains a
+ map specification, and False otherwise."""
+ return (type(spec_part) == dict and 'map_item_spec' in spec_part)
+
+def spec_part_is_named_set(spec_part):
+ """Returns True if the given spec_part is a dict that contains a
+ named_set specification, and False otherwise."""
+ return (type(spec_part) == dict and 'named_map_item_spec' in spec_part)
+
def check_type(spec_part, value):
"""Does nothing if the value is of the correct type given the
specification part relevant for the value. Raises an
@@ -112,9 +128,9 @@ def _get_map_or_list(spec_part):
"""Returns the list or map specification if this is a list or a
map specification part. If not, returns the given spec_part
itself"""
- if "map_item_spec" in spec_part:
+ if spec_part_is_map(spec_part):
return spec_part["map_item_spec"]
- elif "list_item_spec" in spec_part:
+ elif spec_part_is_list(spec_part):
return spec_part["list_item_spec"]
else:
return spec_part
@@ -134,13 +150,13 @@ def _find_spec_part_single(cur_spec, id_part):
# list or a map, which is internally represented by a dict with
# an element 'map_item_spec', a dict with an element 'list_item_spec',
# or a list (when it is the 'main' config_data element of a module).
- if type(cur_spec) == dict and 'map_item_spec' in cur_spec.keys():
+ if spec_part_is_map(cur_spec):
for cur_spec_item in cur_spec['map_item_spec']:
if cur_spec_item['item_name'] == id:
return cur_spec_item
# not found
raise isc.cc.data.DataNotFoundError(id + " not found")
- elif type(cur_spec) == dict and 'list_item_spec' in cur_spec.keys():
+ elif spec_part_is_list(cur_spec):
if cur_spec['item_name'] == id:
return cur_spec['list_item_spec']
# not found
@@ -156,9 +172,22 @@ def _find_spec_part_single(cur_spec, id_part):
else:
raise isc.cc.data.DataNotFoundError("Not a correct config specification")
-def find_spec_part(element, identifier):
+def find_spec_part(element, identifier, strict_identifier = True):
"""find the data definition for the given identifier
- returns either a map with 'item_name' etc, or a list of those"""
+ returns either a map with 'item_name' etc, or a list of those
+ Parameters:
+ element: The specification element to start the search in
+ identifier: The element to find (relative to element above)
+ strict_identifier: If True (the default), additional checking occurs.
+ Currently the only check is whether a list index is
+ specified (except for the last part of the
+ identifier)
+ Raises a DataNotFoundError if the data is not found, or if
+ strict_identifier is True and any non-final identifier parts
+ (i.e. before the last /) identify a list element and do not contain
+ an index.
+ Returns the spec element identified by the given identifier.
+ """
if identifier == "":
return element
id_parts = identifier.split("/")
@@ -171,6 +200,10 @@ def find_spec_part(element, identifier):
# always want the 'full' spec of the item
for id_part in id_parts[:-1]:
cur_el = _find_spec_part_single(cur_el, id_part)
+ if strict_identifier and spec_part_is_list(cur_el) and\
+ not isc.cc.data.identifier_has_list_index(id_part):
+ raise isc.cc.data.DataNotFoundError(id_part +
+ " is a list and needs an index")
cur_el = _get_map_or_list(cur_el)
cur_el = _find_spec_part_single(cur_el, id_parts[-1])
@@ -184,12 +217,12 @@ def spec_name_list(spec, prefix="", recurse=False):
if prefix != "" and not prefix.endswith("/"):
prefix += "/"
if type(spec) == dict:
- if 'map_item_spec' in spec:
+ if spec_part_is_map(spec):
for map_el in spec['map_item_spec']:
name = map_el['item_name']
if map_el['item_type'] == 'map':
name += "/"
- if recurse and 'map_item_spec' in map_el:
+ if recurse and spec_part_is_map(map_el):
result.extend(spec_name_list(map_el['map_item_spec'], prefix + map_el['item_name'], recurse))
else:
result.append(prefix + name)
@@ -244,7 +277,12 @@ class ConfigData:
def get_default_value(self, identifier):
"""Returns the default from the specification, or None if there
is no default"""
- spec = find_spec_part(self.specification.get_config_spec(), identifier)
+ # We are searching for the default value, so we can set
+ # strict_identifier to false (in fact, we need to; we may not know
+ # some list indices, or they may not exist, we are looking for
+ # a default value for a reason here).
+ spec = find_spec_part(self.specification.get_config_spec(),
+ identifier, False)
if spec and 'item_default' in spec:
return spec['item_default']
else:
@@ -313,6 +351,10 @@ class MultiConfigData:
self._current_config = {}
self._local_changes = {}
+ def clear_specifications(self):
+ """Remove all known module specifications"""
+ self._specifications = {}
+
def set_specification(self, spec):
"""Add or update a ModuleSpec. Raises a ConfigDataError is spec is not a ModuleSpec"""
if type(spec) != isc.config.ModuleSpec:
@@ -603,7 +645,7 @@ class MultiConfigData:
Throws DataNotFoundError if the identifier is bad
"""
result = []
- if not identifier:
+ if not identifier or identifier == "/":
# No identifier, so we need the list of current modules
for module in self._specifications.keys():
if all:
@@ -615,8 +657,11 @@ class MultiConfigData:
entry = _create_value_map_entry(module, 'module', None)
result.append(entry)
else:
- if identifier[0] == '/':
+ # Strip off start and end slashes, if they are there
+ if len(identifier) > 0 and identifier[0] == '/':
identifier = identifier[1:]
+ if len(identifier) > 0 and identifier[-1] == '/':
+ identifier = identifier[:-1]
module, sep, id = identifier.partition('/')
spec = self.get_module_spec(module)
if spec:
diff --git a/src/lib/python/isc/config/config_messages.mes b/src/lib/python/isc/config/config_messages.mes
index c52efb4..9e93ca3 100644
--- a/src/lib/python/isc/config/config_messages.mes
+++ b/src/lib/python/isc/config/config_messages.mes
@@ -31,3 +31,9 @@ The configuration manager returned an error response when the module
requested its configuration. The full error message answer from the
configuration manager is appended to the log error.
+% CONFIG_SESSION_STOPPING_FAILED error sending stopping message: %1
+There was a problem when sending a message signaling that the module using
+this CCSession is stopping. This message is sent so that the rest of the
+system is aware that the module is no longer running. Apart from logging
+this message, the error itself is ignored, and the ModuleCCSession is
+still stopped. The specific exception message is printed.
diff --git a/src/lib/python/isc/config/tests/.gitignore b/src/lib/python/isc/config/tests/.gitignore
new file mode 100644
index 0000000..52a9c5e
--- /dev/null
+++ b/src/lib/python/isc/config/tests/.gitignore
@@ -0,0 +1 @@
+/config_test
diff --git a/src/lib/python/isc/config/tests/ccsession_test.py b/src/lib/python/isc/config/tests/ccsession_test.py
index 7bd4476..d1060bf 100644
--- a/src/lib/python/isc/config/tests/ccsession_test.py
+++ b/src/lib/python/isc/config/tests/ccsession_test.py
@@ -250,6 +250,18 @@ class TestModuleCCSession(unittest.TestCase):
self.assertEqual({'command': ['get_config', {'module_name': 'Spec2'}]},
fake_session.get_message('ConfigManager', None))
+ def test_stop(self):
+ fake_session = FakeModuleCCSession()
+ self.assertFalse("Spec1" in fake_session.subscriptions)
+ mccs = self.create_session("spec1.spec", None, None, fake_session)
+ self.assertTrue("Spec1" in fake_session.subscriptions)
+
+ self.assertEqual(len(fake_session.message_queue), 0)
+ mccs.send_stopping()
+ self.assertEqual(len(fake_session.message_queue), 1)
+ self.assertEqual({'command': ['stopping', {'module_name': 'Spec1'}]},
+ fake_session.get_message('ConfigManager', None))
+
def test_get_socket(self):
fake_session = FakeModuleCCSession()
mccs = self.create_session("spec1.spec", None, None, fake_session)
@@ -476,45 +488,6 @@ class TestModuleCCSession(unittest.TestCase):
self.assertEqual({'result': [0]},
fake_session.get_message('Spec2', None))
- def test_check_command_without_recvmsg_remote_module(self):
- "copied from test_check_command3"
- fake_session = FakeModuleCCSession()
- mccs = self.create_session("spec1.spec", None, None, fake_session)
- mccs.set_config_handler(self.my_config_handler_ok)
- self.assertEqual(len(fake_session.message_queue), 0)
-
- fake_session.group_sendmsg(None, 'Spec2')
- rmodname = mccs.add_remote_config(self.spec_file("spec2.spec"))
- print(fake_session.message_queue)
- self.assertEqual({'command': ['get_config', {'module_name': 'Spec2'}]},
- fake_session.get_message('ConfigManager', None))
- self.assertEqual(len(fake_session.message_queue), 0)
-
- cmd = isc.config.ccsession.create_command(isc.config.ccsession.COMMAND_CONFIG_UPDATE, { 'Spec2': { 'item1': 2 }})
- env = { 'group':'Spec2', 'from':None }
- self.assertEqual(len(fake_session.message_queue), 0)
- mccs.check_command_without_recvmsg(cmd, env)
- self.assertEqual(len(fake_session.message_queue), 0)
-
- def test_check_command_without_recvmsg_remote_module2(self):
- "copied from test_check_command3"
- fake_session = FakeModuleCCSession()
- mccs = self.create_session("spec1.spec", None, None, fake_session)
- mccs.set_config_handler(self.my_config_handler_ok)
- self.assertEqual(len(fake_session.message_queue), 0)
-
- fake_session.group_sendmsg(None, 'Spec2')
- rmodname = mccs.add_remote_config(self.spec_file("spec2.spec"))
- self.assertEqual({'command': ['get_config', {'module_name': 'Spec2'}]},
- fake_session.get_message('ConfigManager', None))
- self.assertEqual(len(fake_session.message_queue), 0)
-
- cmd = isc.config.ccsession.create_command(isc.config.ccsession.COMMAND_CONFIG_UPDATE, { 'Spec3': { 'item1': 2 }})
- env = { 'group':'Spec3', 'from':None }
- self.assertEqual(len(fake_session.message_queue), 0)
- mccs.check_command_without_recvmsg(cmd, env)
- self.assertEqual(len(fake_session.message_queue), 0)
-
def test_check_command_block_timeout(self):
"""Check it works if session has timeout and it sets it back."""
def cmd_check(mccs, session):
@@ -542,16 +515,65 @@ class TestModuleCCSession(unittest.TestCase):
mccs.set_command_handler(self.my_command_handler_ok)
self.assertRaises(WouldBlockForever, lambda: mccs.check_command(False))
- def test_remote_module(self):
+ # Now there's a group of tests testing both add_remote_config and
+ # add_remote_config_by_name. Since they are almost the same (they differ
+ # just in the parameter and that the second one asks one more question over
+ # the bus), the actual test code is shared.
+ #
+ # These three functions are helper functions to easy up the writing of them.
+ # To write a test, there need to be 3 functions. First, the function that
+ # does the actual test. It looks like:
+ # def _internal_test(self, function_lambda, param, fill_other_messages):
+ #
+ # The function_lambda provides the tested function if called on the
+ # ccsession. The param is the parameter to pass to the function (either
+ # the module name or the spec file name. The fill_other_messages fills
+ # needed messages (the answer containing the module spec in case of add by
+ # name, no messages in the case of adding by spec file) into the fake bus.
+ # So, the code would look like:
+ #
+ # * Create the fake session and tested ccsession object
+ # * function = function_lambda(ccsession object)
+ # * fill_other_messages(fake session)
+ # * Fill in answer to the get_module_config command
+ # * Test by calling function(param)
+ #
+ # Then you need two wrappers that do launch the tests. There are helpers
+ # for that, so you can just call:
+ # def test_by_spec(self)
+ # self._common_remote_module_test(self._internal_test)
+ # def test_by_name(self)
+ # self._common_remote_module_by_name_test(self._internal_test)
+ def _common_remote_module_test(self, internal_test):
+ internal_test(lambda ccs: ccs.add_remote_config,
+ self.spec_file("spec2.spec"),
+ lambda session: None)
+
+ def _prepare_spec_message(self, session, spec_name):
+ # It could have been one command, but the line would be way too long
+ # to even split it
+ spec_file = self.spec_file(spec_name)
+ spec = isc.config.module_spec_from_file(spec_file)
+ session.group_sendmsg({'result': [0, spec.get_full_spec()]}, "Spec1")
+
+ def _common_remote_module_by_name_test(self, internal_test):
+ internal_test(lambda ccs: ccs.add_remote_config_by_name, "Spec2",
+ lambda session: self._prepare_spec_message(session,
+ "spec2.spec"))
+
+ def _internal_remote_module(self, function_lambda, parameter,
+ fill_other_messages):
fake_session = FakeModuleCCSession()
mccs = self.create_session("spec1.spec", None, None, fake_session)
mccs.remove_remote_config("Spec2")
+ function = function_lambda(mccs)
self.assertRaises(ModuleCCSessionError, mccs.get_remote_config_value, "Spec2", "item1")
self.assertFalse("Spec2" in fake_session.subscriptions)
+ fill_other_messages(fake_session)
fake_session.group_sendmsg(None, 'Spec2')
- rmodname = mccs.add_remote_config(self.spec_file("spec2.spec"))
+ rmodname = function(parameter)
self.assertTrue("Spec2" in fake_session.subscriptions)
self.assertEqual("Spec2", rmodname)
self.assertRaises(isc.cc.data.DataNotFoundError, mccs.get_remote_config_value, rmodname, "asdf")
@@ -563,36 +585,77 @@ class TestModuleCCSession(unittest.TestCase):
self.assertFalse("Spec2" in fake_session.subscriptions)
self.assertRaises(ModuleCCSessionError, mccs.get_remote_config_value, "Spec2", "item1")
- # test if unsubscription is alse sent when object is deleted
+ # test if unsubscription is also sent when object is deleted
+ fill_other_messages(fake_session)
fake_session.group_sendmsg({'result' : [0]}, 'Spec2')
- rmodname = mccs.add_remote_config(self.spec_file("spec2.spec"))
+ rmodname = function(parameter)
self.assertTrue("Spec2" in fake_session.subscriptions)
mccs = None
+ function = None
self.assertFalse("Spec2" in fake_session.subscriptions)
- def test_remote_module_with_custom_config(self):
+ def test_remote_module(self):
+ """
+ Test we can add a remote config and get the configuration.
+ Remote module specified by the spec file name.
+ """
+ self._common_remote_module_test(self._internal_remote_module)
+
+ def test_remote_module_by_name(self):
+ """
+ Test we can add a remote config and get the configuration.
+ Remote module specified its name.
+ """
+ self._common_remote_module_by_name_test(self._internal_remote_module)
+
+ def _internal_remote_module_with_custom_config(self, function_lambda,
+ parameter,
+ fill_other_messages):
fake_session = FakeModuleCCSession()
mccs = self.create_session("spec1.spec", None, None, fake_session)
- # override the default config value for "item1". add_remote_config()
- # should incorporate the overridden value, and we should be abel to
+ function = function_lambda(mccs)
+ # override the default config value for "item1". add_remote_config[_by_name]()
+ # should incorporate the overridden value, and we should be able to
# get it via get_remote_config_value().
+ fill_other_messages(fake_session)
fake_session.group_sendmsg({'result': [0, {"item1": 10}]}, 'Spec2')
- rmodname = mccs.add_remote_config(self.spec_file("spec2.spec"))
+ rmodname = function(parameter)
value, default = mccs.get_remote_config_value(rmodname, "item1")
self.assertEqual(10, value)
self.assertEqual(False, default)
- def test_ignore_command_remote_module(self):
+ def test_remote_module_with_custom_config(self):
+ """
+ Test the config of module will load non-default values on
+ initialization.
+ Remote module specified by the spec file name.
+ """
+ self._common_remote_module_test(
+ self._internal_remote_module_with_custom_config)
+
+ def test_remote_module_by_name_with_custom_config(self):
+ """
+ Test the config of module will load non-default values on
+ initialization.
+ Remote module its name.
+ """
+ self._common_remote_module_by_name_test(
+ self._internal_remote_module_with_custom_config)
+
+ def _internal_ignore_command_remote_module(self, function_lambda, param,
+ fill_other_messages):
# Create a Spec1 module and subscribe to remote config for Spec2
fake_session = FakeModuleCCSession()
mccs = self.create_session("spec1.spec", None, None, fake_session)
mccs.set_command_handler(self.my_command_handler_ok)
+ function = function_lambda(mccs)
+ fill_other_messages(fake_session)
fake_session.group_sendmsg(None, 'Spec2')
- rmodname = mccs.add_remote_config(self.spec_file("spec2.spec"))
+ rmodname = function(param)
- # remove the 'get config' from the queue
- self.assertEqual(len(fake_session.message_queue), 1)
- fake_session.get_message("ConfigManager")
+ # remove the commands from queue
+ while len(fake_session.message_queue) > 0:
+ fake_session.get_message("ConfigManager")
# check if the command for the module itself is received
cmd = isc.config.ccsession.create_command("just_some_command", { 'foo': 'a' })
@@ -610,6 +673,174 @@ class TestModuleCCSession(unittest.TestCase):
mccs.check_command()
self.assertEqual(len(fake_session.message_queue), 0)
+ def test_ignore_commant_remote_module(self):
+ """
+ Test that commands for remote modules aren't handled.
+ Remote module specified by the spec file name.
+ """
+ self._common_remote_module_test(
+ self._internal_ignore_command_remote_module)
+
+ def test_ignore_commant_remote_module_by_name(self):
+ """
+ Test that commands for remote modules aren't handled.
+ Remote module specified by its name.
+ """
+ self._common_remote_module_by_name_test(
+ self._internal_ignore_command_remote_module)
+
+ def _internal_check_command_without_recvmsg_remote_module(self,
+ function_lambda,
+ param,
+ fill_other_messages):
+ fake_session = FakeModuleCCSession()
+ mccs = self.create_session("spec1.spec", None, None, fake_session)
+ mccs.set_config_handler(self.my_config_handler_ok)
+ function = function_lambda(mccs)
+ self.assertEqual(len(fake_session.message_queue), 0)
+
+ fill_other_messages(fake_session)
+ fake_session.group_sendmsg(None, 'Spec2')
+ rmodname = function(param)
+ if (len(fake_session.message_queue) == 2):
+ self.assertEqual({'command': ['get_module_spec',
+ {'module_name': 'Spec2'}]},
+ fake_session.get_message('ConfigManager', None))
+ self.assertEqual({'command': ['get_config', {'module_name': 'Spec2'}]},
+ fake_session.get_message('ConfigManager', None))
+ self.assertEqual(len(fake_session.message_queue), 0)
+
+ cmd = isc.config.ccsession.create_command(isc.config.ccsession.COMMAND_CONFIG_UPDATE, { 'Spec2': { 'item1': 2 }})
+ env = { 'group':'Spec2', 'from':None }
+ self.assertEqual(len(fake_session.message_queue), 0)
+ mccs.check_command_without_recvmsg(cmd, env)
+ self.assertEqual(len(fake_session.message_queue), 0)
+
+ def test_check_command_without_recvmsg_remote_module(self):
+ """
+ Test updates on remote module.
+ The remote module is specified by the spec file name.
+ """
+ self._common_remote_module_test(
+ self._internal_check_command_without_recvmsg_remote_module)
+
+ def test_check_command_without_recvmsg_remote_module_by_name(self):
+ """
+ Test updates on remote module.
+ The remote module is specified by its name.
+ """
+ self._common_remote_module_by_name_test(
+ self._internal_check_command_without_recvmsg_remote_module)
+
+ def _internal_check_command_without_recvmsg_remote_module2(self,
+ function_lambda,
+ param,
+ fill_other_messages):
+ fake_session = FakeModuleCCSession()
+ mccs = self.create_session("spec1.spec", None, None, fake_session)
+ mccs.set_config_handler(self.my_config_handler_ok)
+ function = function_lambda(mccs)
+ self.assertEqual(len(fake_session.message_queue), 0)
+
+ fill_other_messages(fake_session)
+ fake_session.group_sendmsg(None, 'Spec2')
+ rmodname = function(param)
+ if (len(fake_session.message_queue) == 2):
+ self.assertEqual({'command': ['get_module_spec',
+ {'module_name': 'Spec2'}]},
+ fake_session.get_message('ConfigManager', None))
+ self.assertEqual({'command': ['get_config', {'module_name': 'Spec2'}]},
+ fake_session.get_message('ConfigManager', None))
+ self.assertEqual(len(fake_session.message_queue), 0)
+
+ cmd = isc.config.ccsession.create_command(isc.config.ccsession.COMMAND_CONFIG_UPDATE, { 'Spec3': { 'item1': 2 }})
+ env = { 'group':'Spec3', 'from':None }
+ self.assertEqual(len(fake_session.message_queue), 0)
+ mccs.check_command_without_recvmsg(cmd, env)
+ self.assertEqual(len(fake_session.message_queue), 0)
+
+ def test_check_command_without_recvmsg_remote_module2(self):
+ """
+ Test updates on remote module.
+ The remote module is specified by the spec file name.
+ """
+ self._common_remote_module_test(
+ self._internal_check_command_without_recvmsg_remote_module2)
+
+ def test_check_command_without_recvmsg_remote_module_by_name2(self):
+ """
+ Test updates on remote module.
+ The remote module is specified by its name.
+ """
+ self._common_remote_module_by_name_test(
+ self._internal_check_command_without_recvmsg_remote_module2)
+
+ def _internal_remote_module_bad_config(self, function_lambda, parameter,
+ fill_other_messages):
+ fake_session = FakeModuleCCSession()
+ mccs = self.create_session("spec1.spec", None, None, fake_session)
+ function = function_lambda(mccs)
+ # Provide wrong config data. It should be rejected.
+ fill_other_messages(fake_session)
+ fake_session.group_sendmsg({'result': [0, {"bad_item": -1}]}, 'Spec2')
+ self.assertRaises(isc.config.ModuleCCSessionError,
+ function, parameter)
+
+ def test_remote_module_bad_config(self):
+ """
+ Test the remote module rejects bad config data.
+ """
+ self._common_remote_module_test(
+ self._internal_remote_module_bad_config)
+
+ def test_remote_module_by_name_bad_config(self):
+ """
+ Test the remote module rejects bad config data.
+ """
+ self._common_remote_module_by_name_test(
+ self._internal_remote_module_bad_config)
+
+ def _internal_remote_module_error_response(self, function_lambda,
+ parameter, fill_other_messages):
+ fake_session = FakeModuleCCSession()
+ mccs = self.create_session("spec1.spec", None, None, fake_session)
+ function = function_lambda(mccs)
+ # Provide wrong config data. It should be rejected.
+ fill_other_messages(fake_session)
+ fake_session.group_sendmsg({'result': [1, "An error, and I mean it!"]},
+ 'Spec2')
+ self.assertRaises(isc.config.ModuleCCSessionError,
+ function, parameter)
+
+ def test_remote_module_bad_config(self):
+ """
+ Test the remote module complains if there's an error response."
+ """
+ self._common_remote_module_test(
+ self._internal_remote_module_error_response)
+
+ def test_remote_module_by_name_bad_config(self):
+ """
+ Test the remote module complains if there's an error response."
+ """
+ self._common_remote_module_by_name_test(
+ self._internal_remote_module_error_response)
+
+ def test_remote_module_bad_config(self):
+ """
+ Test the remote module rejects bad config data.
+ """
+ self._common_remote_module_by_name_test(
+ self._internal_remote_module_bad_config)
+
+ def test_module_name_mismatch(self):
+ fake_session = FakeModuleCCSession()
+ mccs = self.create_session("spec1.spec", None, None, fake_session)
+ mccs.set_config_handler(self.my_config_handler_ok)
+ self._prepare_spec_message(fake_session, 'spec1.spec')
+ self.assertRaises(isc.config.ModuleCCSessionError,
+ mccs.add_remote_config_by_name, "Spec2")
+
def test_logconfig_handler(self):
# test whether default_logconfig_handler reacts nicely to
# bad data. We assume the actual logger output is tested
@@ -724,6 +955,38 @@ class TestUIModuleCCSession(unittest.TestCase):
fake_conn.set_get_answer('/config_data', { 'version': 123123 })
self.assertRaises(ModuleCCSessionError, UIModuleCCSession, fake_conn)
+ def test_request_specifications(self):
+ module_spec1 = isc.config.module_spec_from_file(
+ self.spec_file("spec1.spec"))
+ module_spec_dict1 = { "module_spec": module_spec1.get_full_spec() }
+ module_spec2 = isc.config.module_spec_from_file(
+ self.spec_file("spec2.spec"))
+ module_spec_dict2 = { "module_spec": module_spec2.get_full_spec() }
+
+ fake_conn = fakeUIConn()
+ # Set the first one in the answer
+ fake_conn.set_get_answer('/module_spec', module_spec_dict1)
+ fake_conn.set_get_answer('/config_data',
+ { 'version': BIND10_CONFIG_DATA_VERSION })
+ uccs = UIModuleCCSession(fake_conn)
+
+ # We should now have the first one, but not the second.
+ self.assertTrue("Spec1" in uccs._specifications)
+ self.assertEqual(module_spec1.get_full_spec(),
+ uccs._specifications["Spec1"].get_full_spec())
+ self.assertFalse("Spec2" in uccs._specifications)
+
+ # Now set an answer where only the second one is present
+ fake_conn.set_get_answer('/module_spec', module_spec_dict2)
+
+ uccs.request_specifications()
+
+ # Now Spec1 should have been removed, and spec2 should be there
+ self.assertFalse("Spec1" in uccs._specifications)
+ self.assertTrue("Spec2" in uccs._specifications)
+ self.assertEqual(module_spec2.get_full_spec(),
+ uccs._specifications["Spec2"].get_full_spec())
+
def test_add_remove_value(self):
fake_conn = fakeUIConn()
uccs = self.create_uccs2(fake_conn)
diff --git a/src/lib/python/isc/config/tests/cfgmgr_test.py b/src/lib/python/isc/config/tests/cfgmgr_test.py
index 589a398..7fe8212 100644
--- a/src/lib/python/isc/config/tests/cfgmgr_test.py
+++ b/src/lib/python/isc/config/tests/cfgmgr_test.py
@@ -240,9 +240,6 @@ class TestConfigManager(unittest.TestCase):
def test_read_config(self):
self.assertEqual(self.cm.config.data, {'version': config_data.BIND10_CONFIG_DATA_VERSION})
- self.cm.read_config()
- # due to what get written, the value here is what the last set_config command in test_handle_msg does
- self.assertEqual(self.cm.config.data, {'TestModule': {'test': 125}, 'version': config_data.BIND10_CONFIG_DATA_VERSION})
self.cm.data_path = "/no_such_path"
self.cm.read_config()
self.assertEqual(self.cm.config.data, {'version': config_data.BIND10_CONFIG_DATA_VERSION})
@@ -255,115 +252,195 @@ class TestConfigManager(unittest.TestCase):
answer = self.cm.handle_msg(msg)
self.assertEqual(expected_answer, answer)
- def test_handle_msg(self):
- self._handle_msg_helper({}, { 'result': [ 1, 'Unknown message format: {}']})
- self._handle_msg_helper("", { 'result': [ 1, 'Unknown message format: ']})
- self._handle_msg_helper({ "command": [ "badcommand" ] }, { 'result': [ 1, "Unknown command: badcommand"]})
- self._handle_msg_helper({ "command": [ "get_commands_spec" ] }, { 'result': [ 0, {} ]})
- self._handle_msg_helper({ "command": [ "get_statistics_spec" ] }, { 'result': [ 0, {} ]})
- self._handle_msg_helper({ "command": [ "get_module_spec" ] }, { 'result': [ 0, {} ]})
- self._handle_msg_helper({ "command": [ "get_module_spec", { "module_name": "Spec2" } ] }, { 'result': [ 0, {} ]})
- #self._handle_msg_helper({ "command": [ "get_module_spec", { "module_name": "nosuchmodule" } ] },
- # {'result': [1, 'No specification for module nosuchmodule']})
+ def test_handle_msg_basic_commands(self):
+ # Some basic commands, where not much interaction happens, just
+ # check the result
+ self._handle_msg_helper({},
+ { 'result': [ 1, 'Unknown message format: {}']})
+ self._handle_msg_helper("",
+ { 'result': [ 1, 'Unknown message format: ']})
+ self._handle_msg_helper({ "command": [ "badcommand" ] },
+ { 'result': [ 1, "Unknown command: badcommand"]})
+ self._handle_msg_helper({ "command": [ "get_commands_spec" ] },
+ { 'result': [ 0, {} ]})
+ self._handle_msg_helper({ "command": [ "get_statistics_spec" ] },
+ { 'result': [ 0, {} ]})
+ self._handle_msg_helper({ "command": [ "get_module_spec" ] },
+ { 'result': [ 0, {} ]})
+ self._handle_msg_helper({ "command": [ "get_module_spec",
+ { "module_name": "Spec2" } ] },
+ { 'result': [ 0, {} ]})
self._handle_msg_helper({ "command": [ "get_module_spec", 1 ] },
- {'result': [1, 'Bad get_module_spec command, argument not a dict']})
+ {'result': [1, 'Bad get_module_spec command, '+
+ 'argument not a dict']})
self._handle_msg_helper({ "command": [ "get_module_spec", { } ] },
- {'result': [1, 'Bad module_name in get_module_spec command']})
- self._handle_msg_helper({ "command": [ "get_config" ] }, { 'result': [ 0, { 'version': config_data.BIND10_CONFIG_DATA_VERSION } ]})
- self._handle_msg_helper({ "command": [ "get_config", { "module_name": "nosuchmodule" } ] },
- {'result': [0, { 'version': config_data.BIND10_CONFIG_DATA_VERSION }]})
+ {'result': [1, 'Bad module_name in '+
+ 'get_module_spec command']})
+ self._handle_msg_helper({ "command": [ "get_config" ] },
+ { 'result': [ 0, { 'version':
+ config_data.BIND10_CONFIG_DATA_VERSION }]})
+ self._handle_msg_helper({ "command": [ "get_config",
+ { "module_name": "nosuchmodule" } ] },
+ {'result': [0, { 'version':
+ config_data.BIND10_CONFIG_DATA_VERSION }]})
self._handle_msg_helper({ "command": [ "get_config", 1 ] },
- {'result': [1, 'Bad get_config command, argument not a dict']})
+ {'result': [1, 'Bad get_config command, '+
+ 'argument not a dict']})
self._handle_msg_helper({ "command": [ "get_config", { } ] },
- {'result': [1, 'Bad module_name in get_config command']})
+ {'result': [1, 'Bad module_name in '+
+ 'get_config command']})
self._handle_msg_helper({ "command": [ "set_config" ] },
{'result': [1, 'Wrong number of arguments']})
self._handle_msg_helper({ "command": [ "set_config", [{}]] },
{'result': [0]})
+
self.assertEqual(len(self.fake_session.message_queue), 0)
- # the targets of some of these tests expect specific answers, put
- # those in our fake msgq first.
- my_ok_answer = { 'result': [ 0 ] }
+ def test_handle_msg_module_and_stats_commands(self):
+ self._handle_msg_helper({ "command":
+ ["module_spec", self.spec.get_full_spec()]
+ },
+ {'result': [0]})
+ # There should be a message on the queue about the 'new' Spec2 module
+ # from ConfigManager to Cmdctl, containing its name and full
+ # specification
+ self.assertEqual(ccsession.create_command(
+ ccsession.COMMAND_MODULE_SPECIFICATION_UPDATE,
+ [ self.spec.get_module_name(),
+ self.spec.get_full_spec()]),
+ self.fake_session.get_message("Cmdctl", None))
+
+ self._handle_msg_helper({ "command": [ "module_spec", { 'foo': 1 } ] },
+ {'result': [1, 'Error in data definition: no '+
+ 'module_name in module_spec']})
+
+ self._handle_msg_helper({ "command": [ "get_module_spec" ] },
+ { 'result': [ 0, { self.spec.get_module_name():
+ self.spec.get_full_spec() } ]})
+ self._handle_msg_helper({ "command": [ "get_module_spec",
+ { "module_name" : "Spec2" } ] },
+ { 'result': [ 0, self.spec.get_full_spec() ] })
+ self._handle_msg_helper({ "command": [ "get_commands_spec" ] },
+ { 'result': [ 0, { self.spec.get_module_name():
+ self.spec.get_commands_spec()}]})
+ self._handle_msg_helper({ "command": [ "get_statistics_spec" ] },
+ { 'result': [ 0, { self.spec.get_module_name():
+ self.spec.get_statistics_spec()}]})
+
+ def __test_handle_msg_update_config_helper(self, new_config):
+ # Helper function for the common pattern in
+ # test_handle_msg_update_config; send 'set config', check for
+ # update message, check if config has indeed been updated
+ my_ok_answer = { 'result': [ 0 ] }
# Send the 'ok' that cfgmgr expects back to the fake queue first
self.fake_session.group_sendmsg(my_ok_answer, "ConfigManager")
- # then send the command
- self._handle_msg_helper({ "command": [ "set_config", [self.name, { "test": 123 }] ] },
+
+ config_version = config_data.BIND10_CONFIG_DATA_VERSION
+ self._handle_msg_helper({ "command": [ "set_config",
+ [ { "version": config_version,
+ self.name: new_config } ] ] },
my_ok_answer)
- # The cfgmgr should have eaten the ok message, and sent out an update again
+
+ # The cfgmgr should have eaten the ok message, and sent out an update
+ # message
self.assertEqual(len(self.fake_session.message_queue), 1)
- self.assertEqual({'command': [ 'config_update', {'test': 123}]},
+ self.assertEqual({'command': [ 'config_update', new_config]},
self.fake_session.get_message(self.name, None))
+
+ # Config should have been updated
+ self.assertEqual(self.cm.config.data, {self.name: new_config,
+ 'version': config_version})
+
# and the queue should now be empty again
self.assertEqual(len(self.fake_session.message_queue), 0)
- # below are variations of the theme above
- self.fake_session.group_sendmsg(my_ok_answer, "ConfigManager")
- self._handle_msg_helper({ "command": [ "set_config", [self.name, { "test": 124 }] ] },
- my_ok_answer)
- self.assertEqual(len(self.fake_session.message_queue), 1)
- self.assertEqual({'command': [ 'config_update', {'test': 124}]},
- self.fake_session.get_message(self.name, None))
- self.assertEqual(len(self.fake_session.message_queue), 0)
+ def test_handle_msg_update_config(self):
+ # Update the configuration and check results a few times
+ # only work the first time
+ self.__test_handle_msg_update_config_helper({ "test": 123 })
+ self.__test_handle_msg_update_config_helper({ "test": 124 })
- # This is the last 'succes' one, the value set here is what test_read_config expects
- self.fake_session.group_sendmsg(my_ok_answer, "ConfigManager")
- self._handle_msg_helper({ "command": [ "set_config", [ { self.name: { "test": 125 } }] ] },
- my_ok_answer )
- self.assertEqual(len(self.fake_session.message_queue), 1)
- self.assertEqual({'command': [ 'config_update', {'test': 125}]},
- self.fake_session.get_message(self.name, None))
- self.assertEqual(len(self.fake_session.message_queue), 0)
+ self.__test_handle_msg_update_config_helper({ "test": 125 })
+
+ self.__test_handle_msg_update_config_helper({ "test": 126 })
- my_bad_answer = { 'result': [1, "bad_answer"] }
+ # Now send an error result (i.e. config not accepted)
+ my_bad_answer = { 'result': [1, "bad config"] }
self.fake_session.group_sendmsg(my_bad_answer, "ConfigManager")
- self._handle_msg_helper({ "command": [ "set_config", [ self.name, { "test": 125 }] ] },
+ self._handle_msg_helper({ "command": [ "set_config",
+ [self.name, { "test": 127 }] ] },
my_bad_answer )
self.assertEqual(len(self.fake_session.message_queue), 1)
- self.assertEqual({'command': [ 'config_update', {'test': 125}]},
+ self.assertEqual({'command': [ 'config_update', {'test': 127}]},
self.fake_session.get_message(self.name, None))
+ # Config should not be updated due to the error
+ self.cm.read_config()
+ self.assertEqual(self.cm.config.data, { self.name: {'test': 126},
+ 'version': config_data.BIND10_CONFIG_DATA_VERSION})
+
self.assertEqual(len(self.fake_session.message_queue), 0)
self.fake_session.group_sendmsg(None, 'ConfigManager')
self._handle_msg_helper({ "command": [ "set_config", [ ] ] },
{'result': [1, 'Wrong number of arguments']} )
- self._handle_msg_helper({ "command": [ "set_config", [ self.name, { "test": 125 }] ] },
- { 'result': [1, 'No answer message from TestModule']} )
-
- #self.assertEqual(len(self.fake_session.message_queue), 1)
- #self.assertEqual({'config_update': {'test': 124}},
- # self.fake_session.get_message(self.name, None))
- #self.assertEqual({'version': 1, 'TestModule': {'test': 124}}, self.cm.config.data)
- #
+ self._handle_msg_helper({ "command": [ "set_config",
+ [ self.name, { "test": 128 }]]},
+ { 'result': [1, 'No answer message '+
+ 'from TestModule']} )
+
+ # This command should leave a message to the TestModule to update its
+ # configuration (since the TestModule did not eat it)
+ self.assertEqual(len(self.fake_session.message_queue), 1)
+ self.assertEqual(
+ ccsession.create_command(ccsession.COMMAND_CONFIG_UPDATE,
+ { "test": 128 }),
+ self.fake_session.get_message("TestModule", None))
+
+ # Make sure queue is empty now
+ self.assertEqual(len(self.fake_session.message_queue), 0)
+
+ # Shutdown should result in 'ok' answer
self._handle_msg_helper({ "command":
- ["module_spec", self.spec.get_full_spec()]
+ ["shutdown"]
},
{'result': [0]})
- self._handle_msg_helper({ "command": [ "module_spec", { 'foo': 1 } ] },
- {'result': [1, 'Error in data definition: no module_name in module_spec']})
- self._handle_msg_helper({ "command": [ "get_module_spec" ] }, { 'result': [ 0, { self.spec.get_module_name(): self.spec.get_full_spec() } ]})
- self._handle_msg_helper({ "command": [ "get_module_spec",
- { "module_name" : "Spec2" } ] },
- { 'result': [ 0, self.spec.get_full_spec() ] })
- self._handle_msg_helper({ "command": [ "get_commands_spec" ] }, { 'result': [ 0, { self.spec.get_module_name(): self.spec.get_commands_spec() } ]})
- self._handle_msg_helper({ "command": [ "get_statistics_spec" ] }, { 'result': [ 0, { self.spec.get_module_name(): self.spec.get_statistics_spec() } ]})
- # re-add this once we have new way to propagate spec changes (1 instead of the current 2 messages)
- #self.assertEqual(len(self.fake_session.message_queue), 2)
- # the name here is actually wrong (and hardcoded), but needed in the current version
- # TODO: fix that
- #self.assertEqual({'specification_update': [ self.name, self.spec ] },
- # self.fake_session.get_message("Cmdctl", None))
- #self.assertEqual({'commands_update': [ self.name, self.commands ] },
- # self.fake_session.get_message("Cmdctl", None))
+ def test_stopping_message(self):
+ # Update the system by announcing this module
self._handle_msg_helper({ "command":
- ["shutdown"]
+ ["module_spec", self.spec.get_full_spec()]
},
{'result': [0]})
+ # This causes a update to be sent from the ConfigManager to the CmdCtl
+ # channel, containing the new module's name and full specification
+ self.assertEqual(ccsession.create_command(
+ ccsession.COMMAND_MODULE_SPECIFICATION_UPDATE,
+ [ self.spec.get_module_name(),
+ self.spec.get_full_spec()]),
+ self.fake_session.get_message("Cmdctl", None))
+
+ # A stopping message should get no response, but should cause another
+ # message to be sent, if it is a known module
+ self._handle_msg_helper({ "command": [ "stopping",
+ { "module_name": "Spec2"}] },
+ None)
+ self.assertEqual(len(self.fake_session.message_queue), 1)
+ self.assertEqual({'command': [ 'module_specification_update',
+ ['Spec2', None] ] },
+ self.fake_session.get_message("Cmdctl", None))
+
+ # but if the 'stopping' module is either unknown or not running,
+ # no followup message should be sent
+ self._handle_msg_helper({ "command":
+ [ "stopping",
+ { "module_name": "NoSuchModule" } ] },
+ None)
+ self.assertEqual(len(self.fake_session.message_queue), 0)
+
def test_set_config_virtual(self):
"""Test that if the module is virtual, we don't send it over the
message bus, but call the checking function.
@@ -381,9 +458,9 @@ class TestConfigManager(unittest.TestCase):
self.cm.set_virtual_module(self.spec, check_test)
# The fake session will throw now if it tries to read a response.
# Handy, we don't need to find a complicated way to check for it.
- result = self.cm._handle_set_config_module(self.spec.
- get_module_name(),
- {'item1': value})
+ result = self.cm.handle_msg(ccsession.create_command(
+ ccsession.COMMAND_SET_CONFIG,
+ [self.spec.get_module_name(), { "item1": value }]))
# Check the correct result is passed and our function was called
# With correct data
self.assertEqual(self.called_with['item1'], value)
@@ -415,19 +492,22 @@ class TestConfigManager(unittest.TestCase):
self.assertEqual({"version": 2}, self.cm.config.data)
self.fake_session.group_sendmsg(my_ok_answer, "ConfigManager")
- self.cm._handle_set_config_all({"test": { "value1": 123 }})
+ self.cm.handle_msg(ccsession.create_command(
+ ccsession.COMMAND_SET_CONFIG, ["test", { "value1": 123 }]))
self.assertEqual({"version": config_data.BIND10_CONFIG_DATA_VERSION,
"test": { "value1": 123 }
}, self.cm.config.data)
self.fake_session.group_sendmsg(my_ok_answer, "ConfigManager")
- self.cm._handle_set_config_all({"test": { "value1": 124 }})
+ self.cm.handle_msg(ccsession.create_command(
+ ccsession.COMMAND_SET_CONFIG, ["test", { "value1": 124 }]))
self.assertEqual({"version": config_data.BIND10_CONFIG_DATA_VERSION,
"test": { "value1": 124 }
}, self.cm.config.data)
self.fake_session.group_sendmsg(my_ok_answer, "ConfigManager")
- self.cm._handle_set_config_all({"test": { "value2": True }})
+ self.cm.handle_msg(ccsession.create_command(
+ ccsession.COMMAND_SET_CONFIG, ["test", { "value2": True }]))
self.assertEqual({"version": config_data.BIND10_CONFIG_DATA_VERSION,
"test": { "value1": 124,
"value2": True
@@ -435,7 +515,8 @@ class TestConfigManager(unittest.TestCase):
}, self.cm.config.data)
self.fake_session.group_sendmsg(my_ok_answer, "ConfigManager")
- self.cm._handle_set_config_all({"test": { "value3": [ 1, 2, 3 ] }})
+ self.cm.handle_msg(ccsession.create_command(
+ ccsession.COMMAND_SET_CONFIG, ["test", { "value3": [ 1, 2, 3 ] }]))
self.assertEqual({"version": config_data.BIND10_CONFIG_DATA_VERSION,
"test": { "value1": 124,
"value2": True,
@@ -444,7 +525,8 @@ class TestConfigManager(unittest.TestCase):
}, self.cm.config.data)
self.fake_session.group_sendmsg(my_ok_answer, "ConfigManager")
- self.cm._handle_set_config_all({"test": { "value2": False }})
+ self.cm.handle_msg(ccsession.create_command(
+ ccsession.COMMAND_SET_CONFIG, ["test", { "value2": False }]))
self.assertEqual({"version": config_data.BIND10_CONFIG_DATA_VERSION,
"test": { "value1": 124,
"value2": False,
@@ -453,7 +535,8 @@ class TestConfigManager(unittest.TestCase):
}, self.cm.config.data)
self.fake_session.group_sendmsg(my_ok_answer, "ConfigManager")
- self.cm._handle_set_config_all({"test": { "value1": None }})
+ self.cm.handle_msg(ccsession.create_command(
+ ccsession.COMMAND_SET_CONFIG, ["test", { "value1": None }]))
self.assertEqual({"version": config_data.BIND10_CONFIG_DATA_VERSION,
"test": { "value2": False,
"value3": [ 1, 2, 3 ]
@@ -461,7 +544,8 @@ class TestConfigManager(unittest.TestCase):
}, self.cm.config.data)
self.fake_session.group_sendmsg(my_ok_answer, "ConfigManager")
- self.cm._handle_set_config_all({"test": { "value3": [ 1 ] }})
+ self.cm.handle_msg(ccsession.create_command(
+ ccsession.COMMAND_SET_CONFIG, ["test", { "value3": [ 1 ] }]))
self.assertEqual({"version": config_data.BIND10_CONFIG_DATA_VERSION,
"test": { "value2": False,
"value3": [ 1 ]
@@ -472,9 +556,14 @@ class TestConfigManager(unittest.TestCase):
def test_run(self):
self.fake_session.group_sendmsg({ "command": [ "get_commands_spec" ] }, "ConfigManager")
self.fake_session.group_sendmsg({ "command": [ "get_statistics_spec" ] }, "ConfigManager")
+ self.fake_session.group_sendmsg({ "command": [ "stopping", { "module_name": "FooModule" } ] }, "ConfigManager")
self.fake_session.group_sendmsg({ "command": [ "shutdown" ] }, "ConfigManager")
+ self.assertEqual(len(self.fake_session.message_queue), 4)
self.cm.run()
- pass
+ # All commands should have been read out by run()
+ # Three of the commands should have been responded to, so the queue
+ # should now contain three answers
+ self.assertEqual(len(self.fake_session.message_queue), 3)
if __name__ == '__main__':
diff --git a/src/lib/python/isc/config/tests/config_data_test.py b/src/lib/python/isc/config/tests/config_data_test.py
index bede625..3638f05 100644
--- a/src/lib/python/isc/config/tests/config_data_test.py
+++ b/src/lib/python/isc/config/tests/config_data_test.py
@@ -185,6 +185,43 @@ class TestConfigData(unittest.TestCase):
spec_part = find_spec_part(config_spec, "item6/value1")
self.assertEqual({'item_name': 'value1', 'item_type': 'string', 'item_optional': True, 'item_default': 'default'}, spec_part)
+ def test_find_spec_part_lists(self):
+ # A few specific tests for list data
+ module_spec = isc.config.module_spec_from_file(self.data_path +
+ os.sep +
+ "spec31.spec")
+ config_spec = module_spec.get_config_spec()
+
+ expected_spec_part = {'item_name': 'number',
+ 'item_type': 'integer',
+ 'item_default': 1,
+ 'item_optional': False}
+
+ # First a check for a correct fetch
+ spec_part = find_spec_part(config_spec,
+ "/first_list_items[0]/second_list_items[1]/"
+ "map_element/list1[1]/list2[1]")
+ self.assertEqual(expected_spec_part, spec_part)
+
+ # Leaving out an index should fail by default
+ self.assertRaises(isc.cc.data.DataNotFoundError,
+ find_spec_part, config_spec,
+ "/first_list_items[0]/second_list_items/"
+ "map_element/list1[1]/list2[1]")
+
+ # But not for the last element
+ spec_part = find_spec_part(config_spec,
+ "/first_list_items[0]/second_list_items[1]/"
+ "map_element/list1[1]/list2")
+ self.assertEqual(expected_spec_part, spec_part)
+
+ # And also not if strict_identifier is false (third argument)
+ spec_part = find_spec_part(config_spec,
+ "/first_list_items[0]/second_list_items/"
+ "map_element/list1[1]/list2[1]", False)
+ self.assertEqual(expected_spec_part, spec_part)
+
+
def test_spec_name_list(self):
name_list = spec_name_list(self.cd.get_module_spec().get_config_spec())
self.assertEqual(['item1', 'item2', 'item3', 'item4', 'item5', 'item6'], name_list)
@@ -312,6 +349,16 @@ class TestMultiConfigData(unittest.TestCase):
self.mcd.remove_specification(module_spec.get_module_name())
self.assertFalse(self.mcd.have_specification(module_spec.get_module_name()))
+ def test_clear_specifications(self):
+ self.assertEqual(0, len(self.mcd._specifications))
+ module_spec = isc.config.module_spec_from_file(self.data_path +
+ os.sep +
+ "spec1.spec")
+ self.mcd.set_specification(module_spec)
+ self.assertEqual(1, len(self.mcd._specifications))
+ self.mcd.clear_specifications()
+ self.assertEqual(0, len(self.mcd._specifications))
+
def test_get_module_spec(self):
module_spec = isc.config.module_spec_from_file(self.data_path + os.sep + "spec1.spec")
self.mcd.set_specification(module_spec)
@@ -473,15 +520,25 @@ class TestMultiConfigData(unittest.TestCase):
self.assertEqual(MultiConfigData.DEFAULT, status)
-
def test_get_value_maps(self):
maps = self.mcd.get_value_maps()
self.assertEqual([], maps)
module_spec = isc.config.module_spec_from_file(self.data_path + os.sep + "spec1.spec")
self.mcd.set_specification(module_spec)
+
+ expected = [{'default': False,
+ 'type': 'module',
+ 'name': 'Spec1',
+ 'value': None,
+ 'modified': False}]
+
maps = self.mcd.get_value_maps()
- self.assertEqual([{'default': False, 'type': 'module', 'name': 'Spec1', 'value': None, 'modified': False}], maps)
+ self.assertEqual(expected, maps)
+
+ maps = self.mcd.get_value_maps("/")
+ self.assertEqual(expected, maps)
+
maps = self.mcd.get_value_maps('Spec2')
self.assertEqual([], maps)
maps = self.mcd.get_value_maps('Spec1')
@@ -556,6 +613,10 @@ class TestMultiConfigData(unittest.TestCase):
maps = self.mcd.get_value_maps("/Spec22/value9")
self.assertEqual(expected, maps)
+ # A slash at the end should not produce different output
+ maps = self.mcd.get_value_maps("/Spec22/value9/")
+ self.assertEqual(expected, maps)
+
def test_get_value_maps_named_set(self):
module_spec = isc.config.module_spec_from_file(self.data_path + os.sep + "spec32.spec")
self.mcd.set_specification(module_spec)
diff --git a/src/lib/python/isc/datasrc/finder_python.cc b/src/lib/python/isc/datasrc/finder_python.cc
index f4b4ceb..33a503f 100644
--- a/src/lib/python/isc/datasrc/finder_python.cc
+++ b/src/lib/python/isc/datasrc/finder_python.cc
@@ -47,15 +47,15 @@ using namespace isc::datasrc::python;
namespace {
ZoneFinder::FindResultFlags
-getFindResultFlags(const ZoneFinder::FindResult& result) {
+getFindResultFlags(const ZoneFinder::Context& context) {
ZoneFinder::FindResultFlags result_flags = ZoneFinder::RESULT_DEFAULT;
- if (result.isWildcard()) {
+ if (context.isWildcard()) {
result_flags = result_flags | ZoneFinder::RESULT_WILDCARD;
}
- if (result.isNSECSigned()) {
+ if (context.isNSECSigned()) {
result_flags = result_flags | ZoneFinder::RESULT_NSEC_SIGNED;
}
- if (result.isNSEC3Signed()) {
+ if (context.isNSEC3Signed()) {
result_flags = result_flags | ZoneFinder::RESULT_NSEC3_SIGNED;
}
return (result_flags);
@@ -83,13 +83,13 @@ PyObject* ZoneFinder_helper(ZoneFinder* finder, PyObject* args) {
try {
ZoneFinder::FindOptions options =
static_cast<ZoneFinder::FindOptions>(options_int);
- const ZoneFinder::FindResult find_result(
+ ConstZoneFinderContextPtr find_ctx(
finder->find(PyName_ToName(name), PyRRType_ToRRType(rrtype),
options));
- const ZoneFinder::Result r = find_result.code;
- isc::dns::ConstRRsetPtr rrsp = find_result.rrset;
+ const ZoneFinder::Result r = find_ctx->code;
+ isc::dns::ConstRRsetPtr rrsp = find_ctx->rrset;
ZoneFinder::FindResultFlags result_flags =
- getFindResultFlags(find_result);
+ getFindResultFlags(*find_ctx);
if (rrsp) {
// Use N instead of O so the refcount isn't increased twice
return (Py_BuildValue("INI", r, createRRsetObject(*rrsp),
@@ -127,12 +127,12 @@ PyObject* ZoneFinder_helper_all(ZoneFinder* finder, PyObject* args) {
ZoneFinder::FindOptions options =
static_cast<ZoneFinder::FindOptions>(options_int);
std::vector<isc::dns::ConstRRsetPtr> target;
- const ZoneFinder::FindResult find_result(
+ ConstZoneFinderContextPtr find_ctx(
finder->findAll(PyName_ToName(name), target, options));
- const ZoneFinder::Result r = find_result.code;
- isc::dns::ConstRRsetPtr rrsp = find_result.rrset;
+ const ZoneFinder::Result r = find_ctx->code;
+ isc::dns::ConstRRsetPtr rrsp = find_ctx->rrset;
ZoneFinder::FindResultFlags result_flags =
- getFindResultFlags(find_result);
+ getFindResultFlags(*find_ctx);
if (r == ZoneFinder::SUCCESS) {
// Copy all the RRsets to the result list
PyObjectContainer list_container(PyList_New(target.size()));
diff --git a/src/lib/python/isc/log/Makefile.am b/src/lib/python/isc/log/Makefile.am
index 5ff2c28..b228caf 100644
--- a/src/lib/python/isc/log/Makefile.am
+++ b/src/lib/python/isc/log/Makefile.am
@@ -23,15 +23,6 @@ log_la_LIBADD += $(PYTHON_LIB)
# This is not installed, it helps locate the module during tests
EXTRA_DIST = __init__.py
-# We're going to abuse install-data-local for a pre-install check.
-# This is to be considered a short term hack and is expected to be removed
-# in a near future version.
-install-data-local:
- if test -d @pyexecdir@/isc/log; then \
- echo "@pyexecdir@/isc/log is deprecated, and will confuse newer versions. Please (re)move it by hand."; \
- exit 1; \
- fi
-
pytest:
$(SHELL) tests/log_test
diff --git a/src/lib/python/isc/log/tests/.gitignore b/src/lib/python/isc/log/tests/.gitignore
new file mode 100644
index 0000000..b9cf241
--- /dev/null
+++ b/src/lib/python/isc/log/tests/.gitignore
@@ -0,0 +1 @@
+/log_console.py
diff --git a/src/lib/python/isc/log_messages/Makefile.am b/src/lib/python/isc/log_messages/Makefile.am
index 4b084cc..4b7e1d1 100644
--- a/src/lib/python/isc/log_messages/Makefile.am
+++ b/src/lib/python/isc/log_messages/Makefile.am
@@ -13,6 +13,7 @@ EXTRA_DIST += cfgmgr_messages.py
EXTRA_DIST += config_messages.py
EXTRA_DIST += notify_out_messages.py
EXTRA_DIST += libxfrin_messages.py
+EXTRA_DIST += server_common_messages.py
CLEANFILES = __init__.pyc
CLEANFILES += bind10_messages.pyc
@@ -27,6 +28,7 @@ CLEANFILES += cfgmgr_messages.pyc
CLEANFILES += config_messages.pyc
CLEANFILES += notify_out_messages.pyc
CLEANFILES += libxfrin_messages.pyc
+CLEANFILES += server_common_messages.pyc
CLEANDIRS = __pycache__
diff --git a/src/lib/python/isc/log_messages/server_common_messages.py b/src/lib/python/isc/log_messages/server_common_messages.py
new file mode 100644
index 0000000..a491071
--- /dev/null
+++ b/src/lib/python/isc/log_messages/server_common_messages.py
@@ -0,0 +1 @@
+from work.server_common_messages import *
diff --git a/src/lib/python/isc/log_messages/work/.gitignore b/src/lib/python/isc/log_messages/work/.gitignore
new file mode 100644
index 0000000..05a7653
--- /dev/null
+++ b/src/lib/python/isc/log_messages/work/.gitignore
@@ -0,0 +1,2 @@
+/__init__.py
+/*_messages.py
diff --git a/src/lib/python/isc/notify/tests/.gitignore b/src/lib/python/isc/notify/tests/.gitignore
new file mode 100644
index 0000000..69f6d95
--- /dev/null
+++ b/src/lib/python/isc/notify/tests/.gitignore
@@ -0,0 +1 @@
+/notify_out_test
diff --git a/src/lib/python/isc/server_common/Makefile.am b/src/lib/python/isc/server_common/Makefile.am
new file mode 100644
index 0000000..a9eca2e
--- /dev/null
+++ b/src/lib/python/isc/server_common/Makefile.am
@@ -0,0 +1,24 @@
+SUBDIRS = tests
+
+python_PYTHON = __init__.py tsig_keyring.py
+
+pythondir = $(pyexecdir)/isc/server_common
+
+BUILT_SOURCES = $(PYTHON_LOGMSGPKG_DIR)/work/server_common_messages.py
+nodist_pylogmessage_PYTHON = $(PYTHON_LOGMSGPKG_DIR)/work/server_common_messages.py
+
+pylogmessagedir = $(pyexecdir)/isc/log_messages/
+
+CLEANFILES = $(PYTHON_LOGMSGPKG_DIR)/work/server_common_messages.py
+CLEANFILES += $(PYTHON_LOGMSGPKG_DIR)/work/server_common_messages.pyc
+
+CLEANDIRS = __pycache__
+
+EXTRA_DIST = server_common_messages.mes
+
+$(PYTHON_LOGMSGPKG_DIR)/work/server_common_messages.py : server_common_messages.mes
+ $(top_builddir)/src/lib/log/compiler/message \
+ -d $(PYTHON_LOGMSGPKG_DIR)/work -p $(srcdir)/server_common_messages.mes
+
+clean-local:
+ rm -rf $(CLEANDIRS)
diff --git a/src/lib/python/isc/server_common/__init__.py b/src/lib/python/isc/server_common/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/src/lib/python/isc/server_common/server_common_messages.mes b/src/lib/python/isc/server_common/server_common_messages.mes
new file mode 100644
index 0000000..b32205c
--- /dev/null
+++ b/src/lib/python/isc/server_common/server_common_messages.mes
@@ -0,0 +1,36 @@
+# 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.
+
+# No namespace declaration - these constants go in the global namespace
+# of the config_messages python module.
+
+# since these messages are for the python server_common library, care must
+# be taken that names do not conflict with the messages from the c++
+# server_common library. A checker script should verify that, but we do not
+# have that at this moment. So when adding a message, make sure that
+# the name is not already used in src/lib/config/config_messages.mes
+
+% PYSERVER_COMMON_TSIG_KEYRING_DEINIT Deinitializing global TSIG keyring
+A debug message noting that the global TSIG keyring is being removed from
+memory. Most programs don't do that, they just exit, which is OK.
+
+% PYSERVER_COMMON_TSIG_KEYRING_INIT Initializing global TSIG keyring
+A debug message noting the TSIG keyring storage is being prepared. It should
+appear at most once in the lifetime of a program. The keyring still needs
+to be loaded from configuration.
+
+% PYSERVER_COMMON_TSIG_KEYRING_UPDATE Updating global TSIG keyring
+A debug message. The TSIG keyring is being (re)loaded from configuration.
+This happens at startup or when the configuration changes. The old keyring
+is removed and new one created with all the keys.
diff --git a/src/lib/python/isc/server_common/tests/Makefile.am b/src/lib/python/isc/server_common/tests/Makefile.am
new file mode 100644
index 0000000..4829edc
--- /dev/null
+++ b/src/lib/python/isc/server_common/tests/Makefile.am
@@ -0,0 +1,24 @@
+PYCOVERAGE_RUN = @PYCOVERAGE_RUN@
+PYTESTS = tsig_keyring_test.py
+EXTRA_DIST = $(PYTESTS)
+
+# If necessary (rare cases), explicitly specify paths to dynamic libraries
+# required by loadable python modules.
+LIBRARY_PATH_PLACEHOLDER =
+if SET_ENV_LIBRARY_PATH
+LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/cryptolink/.libs:$(abs_top_builddir)/src/lib/dns/.libs:$(abs_top_builddir)/src/lib/dns/python/.libs:$(abs_top_builddir)/src/lib/cc/.libs:$(abs_top_builddir)/src/lib/config/.libs:$(abs_top_builddir)/src/lib/log/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$(abs_top_builddir)/src/lib/datasrc/.libs:$$$(ENV_LIBRARY_PATH)
+endif
+
+# test using command-line arguments, so use check-local target instead of TESTS
+check-local:
+if ENABLE_PYTHON_COVERAGE
+ touch $(abs_top_srcdir)/.coverage
+ rm -f .coverage
+ ${LN_S} $(abs_top_srcdir)/.coverage .coverage
+endif
+ for pytest in $(PYTESTS) ; do \
+ echo Running test: $$pytest ; \
+ $(LIBRARY_PATH_PLACEHOLDER) \
+ PYTHONPATH=$(COMMON_PYTHON_PATH):$(abs_top_builddir)/src/lib/dns/python/.libs \
+ $(PYCOVERAGE_RUN) $(abs_srcdir)/$$pytest || exit ; \
+ done
diff --git a/src/lib/python/isc/server_common/tests/tsig_keyring_test.py b/src/lib/python/isc/server_common/tests/tsig_keyring_test.py
new file mode 100644
index 0000000..e9a2174
--- /dev/null
+++ b/src/lib/python/isc/server_common/tests/tsig_keyring_test.py
@@ -0,0 +1,193 @@
+# Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC")
+#
+# Permission to use, copy, modify, and distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SYSTEMS CONSORTIUM
+# DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+# INTERNET SYSTEMS CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
+# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
+# FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+"""
+Tests for isc.server_common.tsig_keyring.
+"""
+
+import unittest
+import isc.log
+from isc.server_common.tsig_keyring import *
+import isc.dns
+from isc.testutils.ccsession_mock import MockModuleCCSession
+
+class Session(MockModuleCCSession):
+ """
+ A class pretending to be the config session.
+ """
+ def __init__(self):
+ MockModuleCCSession.__init__(self)
+ self._name = None
+ self._callback = None
+ self._remove_name = None
+ self._data = None
+
+ def add_remote_config_by_name(self, name, callback):
+ self._name = name
+ self._callback = callback
+
+ def remove_remote_config(self, name):
+ self._remove_name = name
+
+ def get_remote_config_value(self, module, name):
+ if module != 'tsig_keys' or name != 'keys':
+ raise Exception("Asked for bad data element")
+ return (self._data, False)
+
+class TSIGKeyRingTest(unittest.TestCase):
+ """
+ Tests for the isc.server_common.tsig_keyring module.
+ """
+ def setUp(self):
+ self.__session = Session()
+ self.__sha1name = isc.dns.Name('hmac-sha1')
+ self.__md5name = isc.dns.Name('hmac-md5.sig-alg.reg.int')
+
+ def tearDown(self):
+ deinit_keyring()
+
+ def __do_init(self):
+ init_keyring(self.__session)
+ # Some initialization happened
+ self.assertEqual('tsig_keys', self.__session._name)
+
+ def test_initialization(self):
+ """
+ Test we can initialize and deintialize the keyring. It also
+ tests the interaction with the keyring() function.
+ """
+ # The keyring function raises until initialized
+ self.assertRaises(Unexpected, get_keyring)
+ self.__do_init()
+ current_keyring = get_keyring()
+ self.assertTrue(isinstance(current_keyring, isc.dns.TSIGKeyRing))
+ # Another initialization does nothing
+ self.__do_init()
+ self.assertEqual(current_keyring, get_keyring())
+ # When we deinitialize it, it no longer provides the keyring
+ deinit_keyring()
+ self.assertEqual('tsig_keys', self.__session._remove_name)
+ self.__session._remove_name = None
+ self.assertRaises(Unexpected, get_keyring)
+ # Another deinitialization doesn't change anything
+ deinit_keyring()
+ self.assertRaises(Unexpected, get_keyring)
+ self.assertIsNone(self.__session._remove_name)
+ # Test we can init it again (not expected, but not forbidden)
+ self.__do_init()
+ self.assertTrue(isinstance(get_keyring(), isc.dns.TSIGKeyRing))
+
+ def test_load(self):
+ """
+ Test it can load the keys from the configuration and reload them
+ when the data change.
+ """
+ # Initial load
+ self.__session._data = ['key:MTIzNAo=:hmac-sha1']
+ self.__do_init()
+ keys = get_keyring()
+ self.assertEqual(1, keys.size())
+ (rcode, key) = keys.find(isc.dns.Name('key'), self.__sha1name)
+ self.assertEqual(isc.dns.TSIGKeyRing.SUCCESS, rcode)
+ self.assertEqual(isc.dns.Name('key'), key.get_key_name())
+ # There's a change in the configuration
+ # (The key has a different name)
+ self.__session._data = ['key.example:MTIzNAo=:hmac-sha1']
+ self.__session._callback()
+ orig_keys = keys
+ keys = get_keyring()
+ self.assertNotEqual(keys, orig_keys)
+ self.assertEqual(1, keys.size())
+ # The old key is not here
+ (rcode, key) = keys.find(isc.dns.Name('key'), self.__sha1name)
+ self.assertEqual(isc.dns.TSIGKeyRing.NOTFOUND, rcode)
+ self.assertIsNone(key)
+ # But the new one is
+ (rcode, key) = keys.find(isc.dns.Name('key.example'), self.__sha1name)
+ self.assertEqual(isc.dns.TSIGKeyRing.SUCCESS, rcode)
+ self.assertEqual(isc.dns.Name('key.example'), key.get_key_name())
+
+ def test_empty_update(self):
+ """
+ Test an update that doesn't carry the correct element doesn't change
+ anything.
+ """
+ self.__session._data = ['key:MTIzNAo=:hmac-sha1']
+ self.__do_init()
+ keys = get_keyring()
+ self.__session._data = None
+ self.__session._callback()
+ self.assertEqual(keys, get_keyring())
+
+ def test_no_keys_update(self):
+ """
+ Test we can update the keyring to be empty.
+ """
+ self.__session._data = ['key:MTIzNAo=:hmac-sha1']
+ self.__do_init()
+ keys = get_keyring()
+ self.assertEqual(1, keys.size())
+ self.__session._data = []
+ self.__session._callback()
+ keys = get_keyring()
+ self.assertEqual(0, keys.size())
+
+ def test_update_multi(self):
+ """
+ Test we can handle multiple keys in startup/update.
+ """
+ # Init
+ self.__session._data = ['key:MTIzNAo=:hmac-sha1', 'key2:MTIzNAo=']
+ self.__do_init()
+ keys = get_keyring()
+ self.assertEqual(2, keys.size())
+ (rcode, key) = keys.find(isc.dns.Name('key'), self.__sha1name)
+ self.assertEqual(isc.dns.TSIGKeyRing.SUCCESS, rcode)
+ self.assertEqual(isc.dns.Name('key'), key.get_key_name())
+ (rcode, key) = keys.find(isc.dns.Name('key2'), self.__md5name)
+ self.assertEqual(isc.dns.TSIGKeyRing.SUCCESS, rcode)
+ self.assertEqual(isc.dns.Name('key2'), key.get_key_name())
+ # Update
+ self.__session._data = ['key1:MTIzNAo=:hmac-sha1', 'key3:MTIzNAo=']
+ self.__session._callback()
+ keys = get_keyring()
+ self.assertEqual(2, keys.size())
+ (rcode, key) = keys.find(isc.dns.Name('key1'), self.__sha1name)
+ self.assertEqual(isc.dns.TSIGKeyRing.SUCCESS, rcode)
+ self.assertEqual(isc.dns.Name('key1'), key.get_key_name())
+ (rcode, key) = keys.find(isc.dns.Name('key3'), self.__md5name)
+ self.assertEqual(isc.dns.TSIGKeyRing.SUCCESS, rcode)
+ self.assertEqual(isc.dns.Name('key3'), key.get_key_name())
+
+ def test_update_bad(self):
+ """
+ Test it raises on bad updates and doesn't change anything.
+ """
+ self.__session._data = ['key:MTIzNAo=:hmac-sha1']
+ self.__do_init()
+ keys = get_keyring()
+ # Bad TSIG string
+ self.__session._data = ['key:this makes no sense:really']
+ self.assertRaises(isc.dns.InvalidParameter, self.__session._callback)
+ self.assertEqual(keys, get_keyring())
+ # A duplicity
+ self.__session._data = ['key:MTIzNAo=:hmac-sha1', 'key:MTIzNAo=:hmac-sha1']
+ self.assertRaises(AddError, self.__session._callback)
+ self.assertEqual(keys, get_keyring())
+
+if __name__ == "__main__":
+ isc.log.init("bind10") # FIXME Should this be needed?
+ isc.log.resetUnitTestRootLogger()
+ unittest.main()
diff --git a/src/lib/python/isc/server_common/tsig_keyring.py b/src/lib/python/isc/server_common/tsig_keyring.py
new file mode 100644
index 0000000..308cfd4
--- /dev/null
+++ b/src/lib/python/isc/server_common/tsig_keyring.py
@@ -0,0 +1,121 @@
+# Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC")
+#
+# Permission to use, copy, modify, and distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SYSTEMS CONSORTIUM
+# DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+# INTERNET SYSTEMS CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
+# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
+# FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+"""
+This module conveniently keeps a copy of TSIG keyring loaded from the
+tsig_keys module.
+"""
+
+import isc.dns
+import isc.log
+from isc.log_messages.server_common_messages import *
+
+updater = None
+logger = isc.log.Logger("server_common")
+
+class Unexpected(Exception):
+ """
+ Raised when an unexpected operation is requested by the user of this
+ module. For example if calling keyring() before init_keyring().
+ """
+ pass
+
+class AddError(Exception):
+ """
+ Raised when a key can not be added. This usually means there's a
+ duplicate.
+ """
+ pass
+
+class Updater:
+ """
+ The updater of tsig key ring. Not to be used directly.
+ """
+ def __init__(self, session):
+ """
+ Constructor. Pass the ccsession object so the key ring can be
+ downloaded.
+ """
+ logger.debug(logger.DBGLVL_TRACE_BASIC,
+ PYSERVER_COMMON_TSIG_KEYRING_INIT)
+ self.__session = session
+ self.__keyring = isc.dns.TSIGKeyRing()
+ session.add_remote_config_by_name('tsig_keys', self.__update)
+ self.__update()
+
+ def __update(self, value=None, module_cfg=None):
+ """
+ Update the key ring by the configuration.
+
+ Note that this function is used as a callback, but can raise
+ on bad data. The bad data is expected to be handled by the
+ configuration plugin and not be allowed as far as here.
+
+ The parameters are there just to match the signature which
+ the callback should have (i.e. they are ignored).
+ """
+ logger.debug(logger.DBGLVL_TRACE_BASIC,
+ PYSERVER_COMMON_TSIG_KEYRING_UPDATE)
+ (data, _) = self.__session.get_remote_config_value('tsig_keys', 'keys')
+ if data is not None: # There's an update
+ keyring = isc.dns.TSIGKeyRing()
+ for key_data in data:
+ key = isc.dns.TSIGKey(key_data)
+ if keyring.add(key) != isc.dns.TSIGKeyRing.SUCCESS:
+ raise AddError("Can't add key " + str(key))
+ self.__keyring = keyring
+
+ def get_keyring(self):
+ """
+ Return the current key ring.
+ """
+ return self.__keyring
+
+ def deinit(self):
+ """
+ Unregister from getting updates. The object will not be
+ usable any more after this.
+ """
+ logger.debug(logger.DBGLVL_TRACE_BASIC,
+ PYSERVER_COMMON_TSIG_KEYRING_DEINIT)
+ self.__session.remove_remote_config('tsig_keys')
+
+def get_keyring():
+ """
+ Get the current key ring. You need to call init_keyring first.
+ """
+ if updater is None:
+ raise Unexpected("You need to initialize the keyring first by " +
+ "init_keyring()")
+ return updater.get_keyring()
+
+def init_keyring(session):
+ """
+ Initialize the key ring for future use. It does nothing if already
+ initialized.
+ """
+ global updater
+ if updater is None:
+ updater = Updater(session)
+
+def deinit_keyring():
+ """
+ Deinit key ring. Yoeu can no longer access keyring() after this.
+ Does nothing if not initialized.
+ """
+ global updater
+ if updater is not None:
+ updater.deinit()
+ updater = None
diff --git a/src/lib/python/isc/testutils/Makefile.am b/src/lib/python/isc/testutils/Makefile.am
index 5479d83..7abc1bc 100644
--- a/src/lib/python/isc/testutils/Makefile.am
+++ b/src/lib/python/isc/testutils/Makefile.am
@@ -1,4 +1,5 @@
-EXTRA_DIST = __init__.py parse_args.py tsigctx_mock.py rrset_utils.py
+EXTRA_DIST = __init__.py ccsession_mock.py parse_args.py tsigctx_mock.py \
+ rrset_utils.py
CLEANDIRS = __pycache__
diff --git a/src/lib/python/isc/testutils/ccsession_mock.py b/src/lib/python/isc/testutils/ccsession_mock.py
new file mode 100644
index 0000000..5f88678
--- /dev/null
+++ b/src/lib/python/isc/testutils/ccsession_mock.py
@@ -0,0 +1,34 @@
+# Copyright (C) 2012 Internet Systems Consortium.
+#
+# Permission to use, copy, modify, and distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SYSTEMS CONSORTIUM
+# DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+# INTERNET SYSTEMS CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
+# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
+# FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+class MockModuleCCSession():
+ """Fake ModuleCCSession with a minimal implementation as needed by the
+ tests. Currently this module only stores whether some methods have
+ been called on it (send_stopping(), and close())"""
+ def __init__(self):
+ """Will be set to True when send_stopping() is called"""
+ self.stopped = False
+ """Will be set to True when close() is called"""
+ self.closed = False
+
+ def send_stopping(self):
+ """Fake send_stopping() call. No message is sent, but only stores
+ that this method has been called."""
+ self.stopped = True
+
+ def close(self):
+ """Fake close() call. Nothing is closed, but only stores
+ that this method has been called."""
+ self.closed = True
diff --git a/src/lib/resolve/.gitignore b/src/lib/resolve/.gitignore
new file mode 100644
index 0000000..292ed1a
--- /dev/null
+++ b/src/lib/resolve/.gitignore
@@ -0,0 +1,2 @@
+/resolve_messages.cc
+/resolve_messages.h
diff --git a/src/lib/resolve/tests/.gitignore b/src/lib/resolve/tests/.gitignore
new file mode 100644
index 0000000..d6d1ec8
--- /dev/null
+++ b/src/lib/resolve/tests/.gitignore
@@ -0,0 +1 @@
+/run_unittests
diff --git a/src/lib/resolve/tests/recursive_query_unittest_2.cc b/src/lib/resolve/tests/recursive_query_unittest_2.cc
index a222240..2b3d129 100644
--- a/src/lib/resolve/tests/recursive_query_unittest_2.cc
+++ b/src/lib/resolve/tests/recursive_query_unittest_2.cc
@@ -343,7 +343,8 @@ public:
// Convert to wire format
udp_send_buffer_->clear();
- MessageRenderer renderer(*udp_send_buffer_);
+ MessageRenderer renderer;
+ renderer.setBuffer(udp_send_buffer_.get());
msg.toWire(renderer);
if (mangle_response) {
@@ -477,10 +478,9 @@ public:
setReferralExampleOrg(msg);
// Convert to wire format
- // Use a temporary buffer for the dns wire data (we copy it
+ // Use a temporary renderer for the dns wire data (we copy it
// to the 'real' buffer below)
- OutputBuffer msg_buf(BUFFER_SIZE);
- MessageRenderer renderer(msg_buf);
+ MessageRenderer renderer;
msg.toWire(renderer);
// Expected next state (when checked) is the UDP query to example.org.
@@ -496,12 +496,13 @@ public:
// followed by the actual data. We copy them to a new buffer
// first
tcp_send_buffer_->clear();
- tcp_send_buffer_->writeUint16(msg_buf.getLength());
- tcp_send_buffer_->writeData(msg_buf.getData(), msg_buf.getLength());
+ tcp_send_buffer_->writeUint16(renderer.getLength());
+ tcp_send_buffer_->writeData(renderer.getData(), renderer.getLength());
tcp_socket_.async_send(asio::buffer(tcp_send_buffer_->getData(),
tcp_send_buffer_->getLength()),
- boost::bind(&RecursiveQueryTest2::tcpSendHandler, this,
- tcp_send_buffer_->getLength(), _1, _2));
+ boost::bind(
+ &RecursiveQueryTest2::tcpSendHandler, this,
+ tcp_send_buffer_->getLength(), _1, _2));
}
/// \brief Completion Handler for Sending TCP data
diff --git a/src/lib/resolve/tests/recursive_query_unittest_3.cc b/src/lib/resolve/tests/recursive_query_unittest_3.cc
index 3602b03..168ec80 100644
--- a/src/lib/resolve/tests/recursive_query_unittest_3.cc
+++ b/src/lib/resolve/tests/recursive_query_unittest_3.cc
@@ -240,8 +240,10 @@ public:
// Convert to wire format
udp_send_buffer_->clear();
- MessageRenderer renderer(*udp_send_buffer_);
+ MessageRenderer renderer;
+ renderer.setBuffer(udp_send_buffer_.get());
message.toWire(renderer);
+ renderer.setBuffer(NULL);
// Return a message back to the IOFetch object (after setting the
// expected length of data for the check in the send handler).
@@ -353,8 +355,7 @@ public:
// Convert to wire format
// Use a temporary buffer for the dns wire data (we copy it
// to the 'real' buffer below)
- OutputBuffer msg_buf(BUFFER_SIZE);
- MessageRenderer renderer(msg_buf);
+ MessageRenderer renderer;
message.toWire(renderer);
// Also, take this opportunity to clear the accumulated read count in
@@ -368,12 +369,14 @@ public:
// followed by the actual data. We copy them to a new buffer
// first
tcp_send_buffer_->clear();
- tcp_send_buffer_->writeUint16(msg_buf.getLength());
- tcp_send_buffer_->writeData(msg_buf.getData(), msg_buf.getLength());
+ tcp_send_buffer_->writeUint16(renderer.getLength());
+ tcp_send_buffer_->writeData(renderer.getData(), renderer.getLength());
tcp_socket_.async_send(asio::buffer(tcp_send_buffer_->getData(),
tcp_send_buffer_->getLength()),
- boost::bind(&RecursiveQueryTest3::tcpSendHandler,
- this, tcp_send_buffer_->getLength(), _1, _2));
+ boost::bind(
+ &RecursiveQueryTest3::tcpSendHandler,
+ this,
+ tcp_send_buffer_->getLength(), _1, _2));
}
/// \brief Completion Handler for Sending TCP data
diff --git a/src/lib/server_common/.gitignore b/src/lib/server_common/.gitignore
new file mode 100644
index 0000000..e25a98f
--- /dev/null
+++ b/src/lib/server_common/.gitignore
@@ -0,0 +1,2 @@
+/server_common_messages.cc
+/server_common_messages.h
diff --git a/src/lib/server_common/portconfig.cc b/src/lib/server_common/portconfig.cc
index 5c9a7fe..d1e97f3 100644
--- a/src/lib/server_common/portconfig.cc
+++ b/src/lib/server_common/portconfig.cc
@@ -92,17 +92,12 @@ setAddresses(DNSService& service, const AddressList& addresses) {
current_sockets.clear();
BOOST_FOREACH(const AddressPair &address, addresses) {
const int af(IOAddress(address.first).getFamily());
- // TODO: Support sharing somehow in future.
-
- // As for now, we hardcode the application name as dummy_app, because:
- // * we don't have a name available in our interface, which will change
- // soon anyway
- // * we use the DONT_SHARE mode, so the name is irrelevant anyway
+ // We use the application name supplied to the socket requestor on
+ // creation. So we can freely use the SHARE_SAME
const SocketRequestor::SocketID
tcp(socketRequestor().requestSocket(SocketRequestor::TCP,
address.first, address.second,
- SocketRequestor::DONT_SHARE,
- "dummy_app"));
+ SocketRequestor::SHARE_SAME));
current_sockets.push_back(tcp.second);
if (!test_mode) {
service.addServerTCPFromFD(tcp.first, af);
@@ -110,8 +105,7 @@ setAddresses(DNSService& service, const AddressList& addresses) {
const SocketRequestor::SocketID
udp(socketRequestor().requestSocket(SocketRequestor::UDP,
address.first, address.second,
- SocketRequestor::DONT_SHARE,
- "dummy_app"));
+ SocketRequestor::SHARE_SAME));
current_sockets.push_back(udp.second);
if (!test_mode) {
service.addServerUDPFromFD(udp.first, af);
diff --git a/src/lib/server_common/server_common_messages.mes b/src/lib/server_common/server_common_messages.mes
index 3b3090d..0817928 100644
--- a/src/lib/server_common/server_common_messages.mes
+++ b/src/lib/server_common/server_common_messages.mes
@@ -16,7 +16,7 @@ $NAMESPACE isc::server_common
# \brief Messages for the server_common library
-% SOCKETREQUESTOR_CREATED Socket requestor created
+% SOCKETREQUESTOR_CREATED Socket requestor created for application %1
Debug message. A socket requesor (client of the socket creator) is created
for the corresponding application. Normally this should happen at most
one time throughout the lifetime of the application.
diff --git a/src/lib/server_common/socket_request.cc b/src/lib/server_common/socket_request.cc
index 546de05..e471ad0 100644
--- a/src/lib/server_common/socket_request.cc
+++ b/src/lib/server_common/socket_request.cc
@@ -264,8 +264,10 @@ getSocketFd(const std::string& token, int sock_pass_fd) {
// be closed during the lifetime of this class
class SocketRequestorCCSession : public SocketRequestor {
public:
- explicit SocketRequestorCCSession(cc::AbstractSession& session) :
- session_(session)
+ SocketRequestorCCSession(cc::AbstractSession& session,
+ const std::string& app_name) :
+ session_(session),
+ app_name_(app_name)
{
// We need to filter SIGPIPE to prevent it from happening in
// getSocketFd() while writing to the UNIX domain socket after the
@@ -278,7 +280,8 @@ public:
isc_throw(Unexpected, "Failed to filter SIGPIPE: " <<
strerror(errno));
}
- LOG_DEBUG(logger, DBGLVL_TRACE_BASIC, SOCKETREQUESTOR_CREATED);
+ LOG_DEBUG(logger, DBGLVL_TRACE_BASIC, SOCKETREQUESTOR_CREATED).
+ arg(app_name);
}
~SocketRequestorCCSession() {
@@ -293,7 +296,9 @@ public:
{
const isc::data::ConstElementPtr request_msg =
createRequestSocketMessage(protocol, address, port,
- share_mode, share_name);
+ share_mode,
+ share_name.empty() ? app_name_ :
+ share_name);
// Send it to boss
const int seq = session_.group_sendmsg(request_msg, "Boss");
@@ -377,6 +382,7 @@ private:
}
cc::AbstractSession& session_;
+ const std::string app_name_;
std::map<std::string, int> fd_share_sockets_;
};
@@ -392,12 +398,14 @@ socketRequestor() {
}
void
-initSocketRequestor(cc::AbstractSession& session) {
+initSocketRequestor(cc::AbstractSession& session,
+ const std::string& app_name)
+{
if (requestor != NULL) {
isc_throw(InvalidOperation,
"The socket requestor was already initialized");
} else {
- requestor = new SocketRequestorCCSession(session);
+ requestor = new SocketRequestorCCSession(session, app_name);
}
}
diff --git a/src/lib/server_common/socket_request.h b/src/lib/server_common/socket_request.h
index 5708cc1..aac95d1 100644
--- a/src/lib/server_common/socket_request.h
+++ b/src/lib/server_common/socket_request.h
@@ -161,12 +161,31 @@ public:
/// \param port the port to which the socket should be bound (native endian,
/// not network byte order).
/// \param share_mode how the socket can be shared with other requests.
- /// This must be one of the defined values of ShareMode.
+ /// This must be one of the defined values of ShareMode..
/// \param share_name the name of sharing group, relevant for SHARE_SAME
- /// (specified by us or someone else).
+ /// (specified by us or someone else). If left empty (the default),
+ /// the app_name parameter of initSocketRequestor is used. If that one
+ /// is empty as well, it is accepted, but not recommended, as such
+ /// a non-descriptive name has a high chance of collisions between
+ /// applications. Note that you should provide a name (by share_name
+ /// or app_name) even when you set it to DONT_SHARE (for logs and
+ /// debugging) and you need to provide one with SHARE_SAME (to know
+ /// what is same) and SHARE_ANY (someone else might want SHARE_SAME,
+ /// so it would check against this)
/// \return the socket, as a file descriptor and token representing it on
/// the socket creator side.
///
+ /// To understand the modes better:
+ /// - If mode is DONT_SHARE, it succeeds if no one else has opened an FD
+ /// for requested protocol, address and port.
+ /// - If mode is SHARE_SAME, it succeeds if all applications who opened an
+ /// FD for the requested protocol, address and port provided the same
+ /// share_name as this one and none of them had mode DONT_SHARE.
+ /// - If mode is SHARE_ANY, it succeeds if no applications who requested
+ /// the same potocol, address and port provided DONT_SHARE and all the
+ /// applications who provided SHARE_SAME also provided the same
+ /// share_name as this process did.
+ ///
/// \throw InvalidParameter protocol or share_mode is invalid
/// \throw CCSessionError when we have a problem talking over the CC
/// session.
@@ -180,7 +199,7 @@ public:
virtual SocketID requestSocket(Protocol protocol,
const std::string& address,
uint16_t port, ShareMode share_mode,
- const std::string& share_name) = 0;
+ const std::string& share_name = "") = 0;
/// \brief Tell the socket creator we no longer need the socket
///
@@ -215,8 +234,16 @@ SocketRequestor& socketRequestor();
///
/// \param session the CC session that'll be used to talk to the
/// socket creator.
+/// \param app_name default share name if one is not provided with
+/// requestSocket. You can leave this as empty string,
+/// but then you should provide a reasonably descriptive
+/// name to requestSocket. Empty names work like any others,
+/// but have a high chance of collisions, so it is recommended
+/// to avoid them and provide the name of the application
+/// here.
/// \throw InvalidOperation when it is called more than once
-void initSocketRequestor(cc::AbstractSession& session);
+void initSocketRequestor(cc::AbstractSession& session,
+ const std::string& app_name);
/// \brief Initialization for tests
///
diff --git a/src/lib/server_common/tests/.gitignore b/src/lib/server_common/tests/.gitignore
new file mode 100644
index 0000000..0749d37
--- /dev/null
+++ b/src/lib/server_common/tests/.gitignore
@@ -0,0 +1,2 @@
+/data_path.h
+/run_unittests
diff --git a/src/lib/server_common/tests/portconfig_unittest.cc b/src/lib/server_common/tests/portconfig_unittest.cc
index ffeb322..2ea3808 100644
--- a/src/lib/server_common/tests/portconfig_unittest.cc
+++ b/src/lib/server_common/tests/portconfig_unittest.cc
@@ -133,7 +133,10 @@ TEST_F(ParseAddresses, invalid) {
struct InstallListenAddresses : public ::testing::Test {
InstallListenAddresses() :
dnss_(ios_, NULL, NULL, NULL),
- sock_requestor_(dnss_, store_, 5288)
+ // The empty string is expected parameter of requestSocket,
+ // not app_name - the request does not fall back to this, it
+ // is checked to be the same.
+ sock_requestor_(dnss_, store_, 5288, "")
{
valid_.push_back(AddressPair("127.0.0.1", 5288));
valid_.push_back(AddressPair("::1", 5288));
diff --git a/src/lib/server_common/tests/socket_requestor_test.cc b/src/lib/server_common/tests/socket_requestor_test.cc
index e917e0c..829b6d9 100644
--- a/src/lib/server_common/tests/socket_requestor_test.cc
+++ b/src/lib/server_common/tests/socket_requestor_test.cc
@@ -83,7 +83,7 @@ public:
ElementPtr(new ListElement),
ElementPtr(new ListElement))
{
- initSocketRequestor(session);
+ initSocketRequestor(session, "tests");
}
~SocketRequestorTest() {
@@ -158,35 +158,46 @@ TEST_F(SocketRequestorTest, testSocketRequestMessages) {
expected_request = createExpectedRequest("192.0.2.1", 12345, "UDP",
"NO", "test");
- ASSERT_THROW(socketRequestor().requestSocket(SocketRequestor::UDP,
- "192.0.2.1", 12345,
- SocketRequestor::DONT_SHARE,
- "test"),
+ EXPECT_THROW(socketRequestor().requestSocket(SocketRequestor::UDP,
+ "192.0.2.1", 12345,
+ SocketRequestor::DONT_SHARE,
+ "test"),
CCSessionError);
ASSERT_EQ(1, session.getMsgQueue()->size());
- ASSERT_EQ(*expected_request, *(session.getMsgQueue()->get(0)));
+ EXPECT_EQ(*expected_request, *(session.getMsgQueue()->get(0)));
clearMsgQueue();
expected_request = createExpectedRequest("192.0.2.2", 1, "TCP",
"ANY", "test2");
- ASSERT_THROW(socketRequestor().requestSocket(SocketRequestor::TCP,
- "192.0.2.2", 1,
- SocketRequestor::SHARE_ANY,
- "test2"),
+ EXPECT_THROW(socketRequestor().requestSocket(SocketRequestor::TCP,
+ "192.0.2.2", 1,
+ SocketRequestor::SHARE_ANY,
+ "test2"),
CCSessionError);
ASSERT_EQ(1, session.getMsgQueue()->size());
- ASSERT_EQ(*expected_request, *(session.getMsgQueue()->get(0)));
+ EXPECT_EQ(*expected_request, *(session.getMsgQueue()->get(0)));
clearMsgQueue();
expected_request = createExpectedRequest("::1", 2, "UDP",
"SAMEAPP", "test3");
- ASSERT_THROW(socketRequestor().requestSocket(SocketRequestor::UDP,
- "::1", 2,
- SocketRequestor::SHARE_SAME,
- "test3"),
+ EXPECT_THROW(socketRequestor().requestSocket(SocketRequestor::UDP,
+ "::1", 2,
+ SocketRequestor::SHARE_SAME,
+ "test3"),
CCSessionError);
ASSERT_EQ(1, session.getMsgQueue()->size());
- ASSERT_EQ(*expected_request, *(session.getMsgQueue()->get(0)));
+ EXPECT_EQ(*expected_request, *(session.getMsgQueue()->get(0)));
+
+ // A default share name equal to the app name passed on construction
+ clearMsgQueue();
+ expected_request = createExpectedRequest("::1", 2, "UDP",
+ "SAMEAPP", "tests");
+ EXPECT_THROW(socketRequestor().requestSocket(SocketRequestor::UDP,
+ "::1", 2,
+ SocketRequestor::SHARE_SAME),
+ CCSessionError);
+ ASSERT_EQ(1, session.getMsgQueue()->size());
+ EXPECT_EQ(*expected_request, *(session.getMsgQueue()->get(0)));
}
TEST_F(SocketRequestorTest, invalidParameterForSocketRequest) {
@@ -211,19 +222,19 @@ TEST_F(SocketRequestorTest, testBadRequestAnswers) {
// Test various scenarios where the requestor gets back bad answers
// Should raise CCSessionError if there is no answer
- ASSERT_THROW(doRequest(), CCSessionError);
+ EXPECT_THROW(doRequest(), CCSessionError);
// Also if the answer does not match the format
session.getMessages()->add(createAnswer());
- ASSERT_THROW(doRequest(), CCSessionError);
+ EXPECT_THROW(doRequest(), CCSessionError);
// Now a 'real' answer, should fail on socket connect (no such file)
addAnswer("foo", "/does/not/exist");
- ASSERT_THROW(doRequest(), SocketRequestor::SocketError);
+ EXPECT_THROW(doRequest(), SocketRequestor::SocketError);
// Another failure (domain socket path too long)
addAnswer("foo", std::string(1000, 'x'));
- ASSERT_THROW(doRequest(), SocketRequestor::SocketError);
+ EXPECT_THROW(doRequest(), SocketRequestor::SocketError);
// Test values around path boundary
struct sockaddr_un sock_un;
@@ -236,7 +247,7 @@ TEST_F(SocketRequestorTest, testBadRequestAnswers) {
doRequest();
FAIL() << "doRequest did not throw an exception";
} catch (const SocketRequestor::SocketError& se) {
- ASSERT_EQ(std::string::npos, std::string(se.what()).find("too long"));
+ EXPECT_EQ(std::string::npos, std::string(se.what()).find("too long"));
}
const std::string too_long(sizeof(sock_un.sun_path), 'x');
@@ -246,18 +257,18 @@ TEST_F(SocketRequestorTest, testBadRequestAnswers) {
doRequest();
FAIL() << "doRequest did not throw an exception";
} catch (const SocketRequestor::SocketError& se) {
- ASSERT_NE(std::string::npos, std::string(se.what()).find("too long"));
+ EXPECT_NE(std::string::npos, std::string(se.what()).find("too long"));
}
// Send back an error response
// A generic one first
session.getMessages()->add(createAnswer(1, "error"));
- ASSERT_THROW(doRequest(), CCSessionError);
+ EXPECT_THROW(doRequest(), CCSessionError);
// Now some with specific exceptions
session.getMessages()->add(createAnswer(2, "error"));
- ASSERT_THROW(doRequest(), SocketRequestor::SocketAllocateError);
+ EXPECT_THROW(doRequest(), SocketRequestor::SocketAllocateError);
session.getMessages()->add(createAnswer(3, "error"));
- ASSERT_THROW(doRequest(), SocketRequestor::ShareError);
+ EXPECT_THROW(doRequest(), SocketRequestor::ShareError);
}
// Helper function to create the release commands as we expect
@@ -286,24 +297,24 @@ TEST_F(SocketRequestorTest, testSocketReleaseMessages) {
expected_release = createExpectedRelease("foo");
socketRequestor().releaseSocket("foo");
ASSERT_EQ(1, session.getMsgQueue()->size());
- ASSERT_EQ(*expected_release, *(session.getMsgQueue()->get(0)));
+ EXPECT_EQ(*expected_release, *(session.getMsgQueue()->get(0)));
session.getMessages()->add(createAnswer());
clearMsgQueue();
expected_release = createExpectedRelease("bar");
socketRequestor().releaseSocket("bar");
ASSERT_EQ(1, session.getMsgQueue()->size());
- ASSERT_EQ(*expected_release, *(session.getMsgQueue()->get(0)));
+ EXPECT_EQ(*expected_release, *(session.getMsgQueue()->get(0)));
}
TEST_F(SocketRequestorTest, testBadSocketReleaseAnswers) {
// Should fail if there is no answer at all
- ASSERT_THROW(socketRequestor().releaseSocket("bar"),
+ EXPECT_THROW(socketRequestor().releaseSocket("bar"),
CCSessionError);
// Should also fail if the answer is an error
session.getMessages()->add(createAnswer(1, "error"));
- ASSERT_THROW(socketRequestor().releaseSocket("bar"),
+ EXPECT_THROW(socketRequestor().releaseSocket("bar"),
SocketRequestor::SocketError);
}
@@ -514,20 +525,20 @@ TEST_F(SocketRequestorTest, testSocketPassing) {
// 1 should be ok
addAnswer("foo", ts.getPath());
socket_id = doRequest();
- ASSERT_EQ("foo", socket_id.second);
- ASSERT_EQ(0, close(socket_id.first));
+ EXPECT_EQ("foo", socket_id.second);
+ EXPECT_EQ(0, close(socket_id.first));
// 2 should be ok too
addAnswer("bar", ts.getPath());
socket_id = doRequest();
- ASSERT_EQ("bar", socket_id.second);
- ASSERT_EQ(0, close(socket_id.first));
+ EXPECT_EQ("bar", socket_id.second);
+ EXPECT_EQ(0, close(socket_id.first));
// 3 should be ok too (reuse earlier token)
addAnswer("foo", ts.getPath());
socket_id = doRequest();
- ASSERT_EQ("foo", socket_id.second);
- ASSERT_EQ(0, close(socket_id.first));
+ EXPECT_EQ("foo", socket_id.second);
+ EXPECT_EQ(0, close(socket_id.first));
}
// Create a second socket server, to test that multiple different
@@ -542,35 +553,35 @@ TEST_F(SocketRequestorTest, testSocketPassing) {
// 1 should be ok
addAnswer("foo", ts2.getPath());
socket_id = doRequest();
- ASSERT_EQ("foo", socket_id.second);
- ASSERT_EQ(0, close(socket_id.first));
+ EXPECT_EQ("foo", socket_id.second);
+ EXPECT_EQ(0, close(socket_id.first));
}
if (timo_ok) {
// Now use first socket again
addAnswer("foo", ts.getPath());
socket_id = doRequest();
- ASSERT_EQ("foo", socket_id.second);
- ASSERT_EQ(0, close(socket_id.first));
+ EXPECT_EQ("foo", socket_id.second);
+ EXPECT_EQ(0, close(socket_id.first));
// -1 is a "normal" error
addAnswer("foo", ts.getPath());
- ASSERT_THROW(doRequest(), SocketRequestor::SocketError);
+ EXPECT_THROW(doRequest(), SocketRequestor::SocketError);
// -2 is an unexpected error. After this point it's not guaranteed the
// connection works as intended.
addAnswer("foo", ts.getPath());
- ASSERT_THROW(doRequest(), SocketRequestor::SocketError);
+ EXPECT_THROW(doRequest(), SocketRequestor::SocketError);
}
// Vector is of first socket is now empty, so the socket should be gone
addAnswer("foo", ts.getPath());
- ASSERT_THROW(doRequest(), SocketRequestor::SocketError);
+ EXPECT_THROW(doRequest(), SocketRequestor::SocketError);
// Vector is of second socket is now empty too, so the socket should be
// gone
addAnswer("foo", ts2.getPath());
- ASSERT_THROW(doRequest(), SocketRequestor::SocketError);
+ EXPECT_THROW(doRequest(), SocketRequestor::SocketError);
}
}
diff --git a/src/lib/statistics/tests/.gitignore b/src/lib/statistics/tests/.gitignore
new file mode 100644
index 0000000..d6d1ec8
--- /dev/null
+++ b/src/lib/statistics/tests/.gitignore
@@ -0,0 +1 @@
+/run_unittests
diff --git a/src/lib/testutils/dnsmessage_test.cc b/src/lib/testutils/dnsmessage_test.cc
index af354d5..3d653c0 100644
--- a/src/lib/testutils/dnsmessage_test.cc
+++ b/src/lib/testutils/dnsmessage_test.cc
@@ -86,6 +86,9 @@ void
rrsetCheck(isc::dns::ConstRRsetPtr expected_rrset,
isc::dns::ConstRRsetPtr actual_rrset)
{
+ SCOPED_TRACE("Comparing RRsets\n"
+ " Actual: " + actual_rrset->toText() +
+ " Expected: " + expected_rrset->toText());
EXPECT_EQ(expected_rrset->getName(), actual_rrset->getName());
EXPECT_EQ(expected_rrset->getClass(), actual_rrset->getClass());
EXPECT_EQ(expected_rrset->getType(), actual_rrset->getType());
diff --git a/src/lib/testutils/dnsmessage_test.h b/src/lib/testutils/dnsmessage_test.h
index 1aba526..8a46e0e 100644
--- a/src/lib/testutils/dnsmessage_test.h
+++ b/src/lib/testutils/dnsmessage_test.h
@@ -157,6 +157,18 @@ public:
private:
std::vector<isc::dns::ConstRRsetPtr>& rrsets_;
};
+
+class RRsetDumper {
+public:
+ RRsetDumper(std::string& output) :
+ output_(output)
+ {}
+ void operator()(isc::dns::ConstRRsetPtr rrset) {
+ output_ += " " + rrset->toText();
+ }
+private:
+ std::string& output_;
+};
}
/// Set of unit tests to check if two sets of RRsets are identical.
@@ -195,6 +207,10 @@ rrsetsCheck(EXPECTED_ITERATOR expected_begin, EXPECTED_ITERATOR expected_end,
ACTUAL_ITERATOR actual_begin, ACTUAL_ITERATOR actual_end)
{
std::vector<isc::dns::ConstRRsetPtr> checked_rrsets; // for duplicate check
+ std::string expected_text, actual_text;
+ std::for_each(expected_begin, expected_end,
+ detail::RRsetDumper(expected_text));
+ std::for_each(actual_begin, actual_end, detail::RRsetDumper(actual_text));
unsigned int rrset_matched = 0;
ACTUAL_ITERATOR it;
for (it = actual_begin; it != actual_end; ++it) {
@@ -217,11 +233,16 @@ rrsetsCheck(EXPECTED_ITERATOR expected_begin, EXPECTED_ITERATOR expected_end,
}
}
- // make sure all expected RRsets are in actual sets
- EXPECT_EQ(std::distance(expected_begin, expected_end), rrset_matched);
- // make sure rrsets only contains expected RRsets
- EXPECT_EQ(std::distance(expected_begin, expected_end),
- std::distance(actual_begin, actual_end));
+ {
+ SCOPED_TRACE(std::string("Comparing two RRsets:\n") +
+ "Actual:\n" + actual_text +
+ "Expected:\n" + expected_text);
+ // make sure all expected RRsets are in actual sets
+ EXPECT_EQ(std::distance(expected_begin, expected_end), rrset_matched);
+ // make sure rrsets only contains expected RRsets
+ EXPECT_EQ(std::distance(expected_begin, expected_end),
+ std::distance(actual_begin, actual_end));
+ }
}
/// Set of unit tests to check if two sets of RRsets are identical using
diff --git a/src/lib/testutils/socket_request.h b/src/lib/testutils/socket_request.h
index a1e51e2..2c14120 100644
--- a/src/lib/testutils/socket_request.h
+++ b/src/lib/testutils/socket_request.h
@@ -59,11 +59,18 @@ public:
/// \param expect_port The port which is expected to be requested. If
/// the application requests a different port, it is considered
/// a failure.
+ /// \param expeted_app The share name for which all the requests should
+ /// be made. This is not the usual app_name - the requestSocket does
+ /// not fall back to this value if its share_name is left empty, if
+ /// you want to check the code relies on the requestor to use the
+ /// app name, you set this to empty string.
TestSocketRequestor(asiodns::DNSService& dnss,
server_common::portconfig::AddressList& store,
- uint16_t expect_port) :
+ uint16_t expect_port,
+ const std::string& expected_app) :
last_token_(0), break_rollback_(false), break_release_(false),
- dnss_(dnss), store_(store), expect_port_(expect_port)
+ dnss_(dnss), store_(store), expect_port_(expect_port),
+ expected_app_(expected_app)
{
// Prepare the requestor (us) for the test
server_common::initTestSocketRequestor(this);
@@ -141,8 +148,13 @@ public:
/// \param protocol The protocol to request
/// \param address to bind to
/// \param port to bind to
- /// \param mode checked to be DONT_SHARE for now
- /// \param name checked to be dummy_app for now
+ /// \param mode checked to be SHARE_SAME for now
+ /// \param name checked to be the same as expected_app parameter of the
+ /// constructor. Note that this class does not provide the fallback
+ /// to an app_name if this is empty string. To check the code relies
+ /// on the fallback (wants to use the app_name instead of providing
+ /// its own share name), you need to create this class with empty
+ /// expected_app.
/// \return The token and FD
/// \throw SocketAllocateError as described above, to test error handling
/// \throw ShareError as described above, to test error handling
@@ -169,8 +181,8 @@ public:
const std::string proto(protocol == TCP ? "TCP" : "UDP");
const size_t number = ++ last_token_;
EXPECT_EQ(expect_port_, port);
- EXPECT_EQ(DONT_SHARE, mode);
- EXPECT_EQ("dummy_app", name);
+ EXPECT_EQ(SHARE_SAME, mode);
+ EXPECT_EQ(expected_app_, name);
const std::string token(proto + ":" + address + ":" +
boost::lexical_cast<std::string>(port) + ":" +
boost::lexical_cast<std::string>(number));
@@ -207,6 +219,7 @@ private:
asiodns::DNSService& dnss_;
server_common::portconfig::AddressList& store_;
const uint16_t expect_port_;
+ const std::string expected_app_;
};
}
diff --git a/src/lib/testutils/srv_test.cc b/src/lib/testutils/srv_test.cc
index 881eb81..03aec01 100644
--- a/src/lib/testutils/srv_test.cc
+++ b/src/lib/testutils/srv_test.cc
@@ -44,8 +44,6 @@ SrvTestBase::SrvTestBase() : request_message(Message::RENDER),
qclass(RRClass::IN()),
qtype(RRType::A()), io_sock(NULL),
io_message(NULL), endpoint(NULL),
- request_obuffer(0),
- request_renderer(request_obuffer),
response_obuffer(new OutputBuffer(0))
{}
@@ -87,6 +85,7 @@ SrvTestBase::createRequestPacket(Message& message,
IOAddress(DEFAULT_REMOTE_ADDRESS), 53210);
io_sock = (protocol == IPPROTO_UDP) ? &IOSocket::getDummyUDPSocket() :
&IOSocket::getDummyTCPSocket();
+
io_message = new IOMessage(request_renderer.getData(),
request_renderer.getLength(),
*io_sock, *endpoint);
diff --git a/src/lib/testutils/srv_test.h b/src/lib/testutils/srv_test.h
index 630232c..be32b55 100644
--- a/src/lib/testutils/srv_test.h
+++ b/src/lib/testutils/srv_test.h
@@ -100,7 +100,6 @@ protected:
asiolink::IOSocket* io_sock;
asiolink::IOMessage* io_message;
const asiolink::IOEndpoint* endpoint;
- isc::util::OutputBuffer request_obuffer;
isc::dns::MessageRenderer request_renderer;
isc::util::OutputBufferPtr response_obuffer;
std::vector<uint8_t> data;
diff --git a/src/lib/testutils/testdata/.gitignore b/src/lib/testutils/testdata/.gitignore
new file mode 100644
index 0000000..3c239fc
--- /dev/null
+++ b/src/lib/testutils/testdata/.gitignore
@@ -0,0 +1,12 @@
+/badExampleQuery_fromWire.wire
+/examplequery_fromWire.wire
+/iquery_fromWire.wire
+/iquery_response_fromWire.wire
+/iqueryresponse_fromWire.wire
+/multiquestion_fromWire.wire
+/nsec3query_fromWire.wire
+/nsec3query_nodnssec_fromWire.wire
+/queryBadEDNS_fromWire.wire
+/shortanswer_fromWire.wire
+/simplequery_fromWire.wire
+/simpleresponse_fromWire.wire
diff --git a/src/lib/testutils/testdata/Makefile.am b/src/lib/testutils/testdata/Makefile.am
index 918d5c5..a6b8206 100644
--- a/src/lib/testutils/testdata/Makefile.am
+++ b/src/lib/testutils/testdata/Makefile.am
@@ -5,6 +5,7 @@ BUILT_SOURCES += iqueryresponse_fromWire.wire multiquestion_fromWire.wire
BUILT_SOURCES += queryBadEDNS_fromWire.wire shortanswer_fromWire.wire
BUILT_SOURCES += simplequery_fromWire.wire simpleresponse_fromWire.wire
BUILT_SOURCES += iquery_fromWire.wire iquery_response_fromWire.wire
+BUILT_SOURCES += nsec3query_nodnssec_fromWire.wire nsec3query_fromWire.wire
# NOTE: keep this in sync with real file listing
# so is included in tarball
@@ -19,8 +20,10 @@ EXTRA_DIST += shortquestion_fromWire
EXTRA_DIST += shortresponse_fromWire
EXTRA_DIST += simplequery_fromWire.spec
EXTRA_DIST += simpleresponse_fromWire.spec
+EXTRA_DIST += nsec3query_nodnssec_fromWire.spec nsec3query_fromWire.spec
EXTRA_DIST += iquery_fromWire.spec iquery_response_fromWire.spec
EXTRA_DIST += example.com.zone example.net.zone example.org.zone example.zone
+EXTRA_DIST += rfc5155-example.zone.signed
EXTRA_DIST += example.com
EXTRA_DIST += example.sqlite3
diff --git a/src/lib/testutils/testdata/nsec3query_fromWire.spec b/src/lib/testutils/testdata/nsec3query_fromWire.spec
new file mode 100644
index 0000000..f68a09e
--- /dev/null
+++ b/src/lib/testutils/testdata/nsec3query_fromWire.spec
@@ -0,0 +1,11 @@
+#
+# A simple QUERY message (with DO bit on) for "example" zone signed with NSEC3
+#
+
+[header]
+arcount: 1
+[question]
+# use default
+name: ns2.example
+[edns]
+do: 1
diff --git a/src/lib/testutils/testdata/nsec3query_nodnssec_fromWire.spec b/src/lib/testutils/testdata/nsec3query_nodnssec_fromWire.spec
new file mode 100644
index 0000000..06a9561
--- /dev/null
+++ b/src/lib/testutils/testdata/nsec3query_nodnssec_fromWire.spec
@@ -0,0 +1,9 @@
+#
+# A simple QUERY message (without DO bit) for "example" zone signed with NSEC3
+#
+
+[header]
+# use default
+[question]
+# use default
+name: ns2.example
diff --git a/src/lib/testutils/testdata/rfc5155-example.zone.signed b/src/lib/testutils/testdata/rfc5155-example.zone.signed
new file mode 100644
index 0000000..595c441
--- /dev/null
+++ b/src/lib/testutils/testdata/rfc5155-example.zone.signed
@@ -0,0 +1,72 @@
+;; The example NSEC3-signed zone used in RFC5155.
+
+example. 3600 IN SOA ns1.example. bugs.x.w.example. 1 3600 300 3600000 3600
+example. 3600 IN RRSIG SOA 7 1 3600 20150420235959 20051021000000 40430 example. Hu25UIyNPmvPIVBrldN+9Mlp9Zql39qaUd8iq4ZLlYWfUUbbAS41pG+6 8z81q1xhkYAcEyHdVI2LmKusbZsT0Q==
+example. 3600 IN NS ns1.example.
+example. 3600 IN NS ns2.example.
+example. 3600 IN RRSIG NS 7 1 3600 20150420235959 20051021000000 40430 example. PVOgtMK1HHeSTau+HwDWC8Ts+6C8qtqd4pQJqOtdEVgg+MA+ai4fWDEh u3qHJyLcQ9tbD2vvCnMXjtz6SyObxA==
+example. 3600 IN MX 1 xx.example.
+example. 3600 IN RRSIG MX 7 1 3600 20150420235959 20051021000000 40430 example. GgQ1A9xs47k42VPvpL/a1BWUz/6XsnHkjotw9So8MQtZtl2wJBsnOQsa oHrRCrRbyriEl/GZn9Mto/Kx+wBo+w==
+example. 3600 IN DNSKEY 256 3 7 AwEAAaetidLzsKWUt4swWR8yu0wPHPiUi8LUsAD0QPWU+wzt89epO6tH zkMBVDkC7qphQO2hTY4hHn9npWFRw5BYubE= ; key id = 40430
+example. 3600 IN DNSKEY 257 3 7 AwEAAcUlFV1vhmqx6NSOUOq2R/dsR7Xm3upJj7IommWSpJABVfW8Q0rO vXdM6kzt+TAu92L9AbsUdblMFin8CVF3n4s= ; key id = 12708
+example. 3600 IN RRSIG DNSKEY 7 1 3600 20150420235959 20051021000000 12708 example. AuU4juU9RaxescSmStrQks3Gh9FblGBlVU31uzMZ/U/FpsUb8aC6QZS+ sTsJXnLnz7flGOsmMGQZf3bH+QsCtg==
+example. 3600 IN NSEC3PARAM 1 0 12 AABBCCDD
+example. 3600 IN RRSIG NSEC3PARAM 7 1 3600 20150420235959 20051021000000 40430 example. C1Gl8tPZNtnjlrYWDeeUV/sGLCyy/IHie2rerN05XSA3Pq0U3+4VvGWY WdUMfflOdxqnXHwJTLQsjlkynhG6Cg==
+2t7b4g4vsa5smi47k61mv5bv1a22bojr.example. 3600 IN A 192.0.2.127
+2t7b4g4vsa5smi47k61mv5bv1a22bojr.example. 3600 IN RRSIG A 7 2 3600 20150420235959 20051021000000 40430 example. h6c++bzhRuWWt2bykN6mjaTNBcXNq5UuL5EdK+iDP4eY8I0kSiKaCjg3 tC1SQkeloMeub2GWk8p6xHMPZumXlw==
+a.example. 3600 IN NS ns1.a.example.
+a.example. 3600 IN NS ns2.a.example.
+a.example. 3600 IN DS 58470 5 1 3079F1593EBAD6DC121E202A8B766A6A4837206C
+a.example. 3600 IN RRSIG DS 7 2 3600 20150420235959 20051021000000 40430 example. XacFcQVHLVzdoc45EJhN616zQ4mEXtE8FzUhM2KWjfy1VfRKD9r1MeVG wwoukOKgJxBPFsWoo722vZ4UZ2dIdA==
+ns1.a.example. 3600 IN A 192.0.2.5
+ns2.a.example. 3600 IN A 192.0.2.6
+ai.example. 3600 IN A 192.0.2.9
+ai.example. 3600 IN RRSIG A 7 2 3600 20150420235959 20051021000000 40430 example. hVe+wKYMlObTRPhX0NL67GxeZfdxqr/QeR6FtfdAj5+FgYxyzPEjIzvK Wy00hWIl6wD3Vws+rznEn8sQ64UdqA==
+ai.example. 3600 IN HINFO "KLH-10" "ITS"
+ai.example. 3600 IN RRSIG HINFO 7 2 3600 20150420235959 20051021000000 40430 example. Yi42uOq43eyO6qXHNvwwfFnIustWgV5urFcxenkLvs6pKRh00VBjODmf 3Z4nMO7IOl6nHSQ1v0wLHpEZG7Xj2w==
+ai.example. 3600 IN AAAA 2001:db8::f00:baa9
+ai.example. 3600 IN RRSIG AAAA 7 2 3600 20150420235959 20051021000000 40430 example. LcdxKaCB5bGZwPDg+3JJ4O02zoMBrjxqlf6WuaHQZZfTUpb9Nf2nxFGe 2XRPfR5tpJT6GdRGcHueLuXkMjBArQ==
+c.example. 3600 IN NS ns1.c.example.
+c.example. 3600 IN NS ns2.c.example.
+ns1.c.example. 3600 IN A 192.0.2.7
+ns2.c.example. 3600 IN A 192.0.2.8
+ns1.example. 3600 IN A 192.0.2.1
+ns1.example. 3600 IN RRSIG A 7 2 3600 20150420235959 20051021000000 40430 example. bu6kx73n6XEunoVGuRfAgY7EF/AJqHy7hj0jkiqJjB0dOrx3wuz9SaBe GfqWIdn/uta3SavN4FRvZR9SCFHF5Q==
+ns2.example. 3600 IN A 192.0.2.2
+ns2.example. 3600 IN RRSIG A 7 2 3600 20150420235959 20051021000000 40430 example. ktQ3TqE0CfRfki0Rb/Ip5BM0VnxelbuejCC4zpLbFKA/7eD7UNAwxMgx JPtbdST+syjYSJaj4IHfeX6n8vfoGA==
+*.w.example. 3600 IN MX 1 ai.example.
+*.w.example. 3600 IN RRSIG MX 7 2 3600 20150420235959 20051021000000 40430 example. CikebjQwGQPwijVcxgcZcSJKtfynugtlBiKb9FcBTrmOoyQ4InoWVudh CWsh/URX3lc4WRUMivEBP6+4KS3ldA==
+x.w.example. 3600 IN MX 1 xx.example.
+x.w.example. 3600 IN RRSIG MX 7 3 3600 20150420235959 20051021000000 40430 example. IrK3tq/tHFIBF0scHiE/1IwMAvckS/55hAVvQyxTFbkAdDloP3NbZzu+ yoSsr3b3OX6qbBpY7WCtwwekLKRAwQ==
+x.y.w.example. 3600 IN MX 1 xx.example.
+x.y.w.example. 3600 IN RRSIG MX 7 4 3600 20150420235959 20051021000000 40430 example. MqSt5HqJIN8+SLlzTOImrh5h9Xa6gDvAW/GnnbdPc6Z7nXvCpLPJj/5l Cwx3VuzVOjkbvXze8/8Ccl2Zn2hbug==
+xx.example. 3600 IN A 192.0.2.10
+xx.example. 3600 IN RRSIG A 7 2 3600 20150420235959 20051021000000 40430 example. T35hBWEZ017VC5u2c4OriKyVn/pu+fVK4AlXYOxJ6iQylfV2HQIKjv6b 7DzINB3aF/wjJqgXpQvhq+Ac6+ZiFg==
+xx.example. 3600 IN HINFO "KLH-10" "TOPS-20"
+xx.example. 3600 IN RRSIG HINFO 7 2 3600 20150420235959 20051021000000 40430 example. KimG+rDd+7VA1zRsu0ITNAQUTRlpnsmqWrihFRnU+bRa93v2e5oFNFYC s3Rqgv62K93N7AhW6Jfqj/8NzWjvKg==
+xx.example. 3600 IN AAAA 2001:db8::f00:baaa
+xx.example. 3600 IN RRSIG AAAA 7 2 3600 20150420235959 20051021000000 40430 example. IXBcXORITNwd8h3gNwyxtYFvAupS/CYWufVeuBUX0O25ivBCULjZjpDx FSxfohb/KA7YRdxENzYfMItpILl/Xw==
+0p9mhaveqvm6t7vbl5lop2u3t2rp3tom.example. 3600 IN NSEC3 1 1 12 AABBCCDD 2T7B4G4VSA5SMI47K61MV5BV1A22BOJR NS SOA MX RRSIG DNSKEY NSEC3PARAM
+0p9mhaveqvm6t7vbl5lop2u3t2rp3tom.example. 3600 IN RRSIG NSEC3 7 2 3600 20150420235959 20051021000000 40430 example. OSgWSm26B+cS+dDL8b5QrWr/dEWhtCsKlwKLIBHYH6blRxK9rC0bMJPw Q4mLIuw85H2EY762BOCXJZMnpuwhpA==
+2t7b4g4vsa5smi47k61mv5bv1a22bojr.example. 3600 IN NSEC3 1 1 12 AABBCCDD 2VPTU5TIMAMQTTGL4LUU9KG21E0AOR3S A RRSIG
+2t7b4g4vsa5smi47k61mv5bv1a22bojr.example. 3600 IN RRSIG NSEC3 7 2 3600 20150420235959 20051021000000 40430 example. OmBvJ1Vgg1hCKMXHFiNeIYHK9XVW0iLDLwJN4TFoNxZuP03gAXEI634Y wOc4YBNITrj413iqNI6mRk/r1dOSUw==
+2vptu5timamqttgl4luu9kg21e0aor3s.example. 3600 IN NSEC3 1 1 12 AABBCCDD 35MTHGPGCU1QG68FAB165KLNSNK3DPVL MX RRSIG
+2vptu5timamqttgl4luu9kg21e0aor3s.example. 3600 IN RRSIG NSEC3 7 2 3600 20150420235959 20051021000000 40430 example. KL1V2oFYghNV0Hm7Tf2vpJjM6l+0g1JCcVYGVfI0lKrhPmTsOA96cLEA Cgo1x8I7kApJX+obTuktZ+sdsZPY1w==
+35mthgpgcu1qg68fab165klnsnk3dpvl.example. 3600 IN NSEC3 1 1 12 AABBCCDD B4UM86EGHHDS6NEA196SMVMLO4ORS995 NS DS RRSIG
+35mthgpgcu1qg68fab165klnsnk3dpvl.example. 3600 IN RRSIG NSEC3 7 2 3600 20150420235959 20051021000000 40430 example. g6jPUUpduAJKRljUsN8gB4UagAX0NxY9shwQAynzo8EUWH+z6hEIBlUT PGj15eZll6VhQqgZXtAIR3chwgW+SA==
+b4um86eghhds6nea196smvmlo4ors995.example. 3600 IN NSEC3 1 1 12 AABBCCDD GJEQE526PLBF1G8MKLP59ENFD789NJGI MX RRSIG
+b4um86eghhds6nea196smvmlo4ors995.example. 3600 IN RRSIG NSEC3 7 2 3600 20150420235959 20051021000000 40430 example. ZkPG3M32lmoHM6pa3D6gZFGB/rhL//Bs3Omh5u4m/CUiwtblEVOaAKKZ d7S959OeiX43aLX3pOv0TSTyiTxIZg==
+gjeqe526plbf1g8mklp59enfd789njgi.example. 3600 IN NSEC3 1 1 12 AABBCCDD JI6NEOAEPV8B5O6K4EV33ABHA8HT9FGC A HINFO AAAA RRSIG
+gjeqe526plbf1g8mklp59enfd789njgi.example. 3600 IN RRSIG NSEC3 7 2 3600 20150420235959 20051021000000 40430 example. IVnezTJ9iqblFF97vPSmfXZ5Zozngx3KX3byLTZC4QBH2dFWhf6scrGF ZB980AfCxoD9qbbKDy+rdGIeRSVNyw==
+ji6neoaepv8b5o6k4ev33abha8ht9fgc.example. 3600 IN NSEC3 1 1 12 AABBCCDD K8UDEMVP1J2F7EG6JEBPS17VP3N8I58H
+ji6neoaepv8b5o6k4ev33abha8ht9fgc.example. 3600 IN RRSIG NSEC3 7 2 3600 20150420235959 20051021000000 40430 example. gPkFp1s2QDQ6wQzcg1uSebZ61W33rUBDcTj72F3kQ490fEdp7k1BUIfb cZtPbX3YCpE+sIt0MpzVSKfTwx4uYA==
+k8udemvp1j2f7eg6jebps17vp3n8i58h.example. 3600 IN NSEC3 1 1 12 AABBCCDD KOHAR7MBB8DC2CE8A9QVL8HON4K53UHI
+k8udemvp1j2f7eg6jebps17vp3n8i58h.example. 3600 IN RRSIG NSEC3 7 2 3600 20150420235959 20051021000000 40430 example. FtXGbvF0+wf8iWkyo73enAuVx03klN+pILBKS6qCcftVtfH4yVzsEZqu J27NHR7ruxJWDNMtOtx7w9WfcIg62A==
+kohar7mbb8dc2ce8a9qvl8hon4k53uhi.example. 3600 IN NSEC3 1 1 12 AABBCCDD Q04JKCEVQVMU85R014C7DKBA38O0JI5R A RRSIG
+kohar7mbb8dc2ce8a9qvl8hon4k53uhi.example. 3600 IN RRSIG NSEC3 7 2 3600 20150420235959 20051021000000 40430 example. VrDXs2uVW21N08SyQIz88zml+y4ZCInTwgDr6zz43yAg+LFERjOrj3Oj ct51ac7Dp4eZbf9FQJazmASFKGxGXg==
+q04jkcevqvmu85r014c7dkba38o0ji5r.example. 3600 IN NSEC3 1 1 12 AABBCCDD R53BQ7CC2UVMUBFU5OCMM6PERS9TK9EN A RRSIG
+q04jkcevqvmu85r014c7dkba38o0ji5r.example. 3600 IN RRSIG NSEC3 7 2 3600 20150420235959 20051021000000 40430 example. hV5I89b+4FHJDATp09g4bbN0R1F845CaXpL3ZxlMKimoPAyqletMlEWw LfFia7sdpSzn+ZlNNlkxWcLsIlMmUg==
+r53bq7cc2uvmubfu5ocmm6pers9tk9en.example. 3600 IN NSEC3 1 1 12 AABBCCDD T644EBQK9BIBCNA874GIVR6JOJ62MLHV MX RRSIG
+r53bq7cc2uvmubfu5ocmm6pers9tk9en.example. 3600 IN RRSIG NSEC3 7 2 3600 20150420235959 20051021000000 40430 example. aupviViruXs4bDg9rCbezzBMf9h1ZlDvbW/CZFKulIGXXLj8B/fsDJar XVDA9bnUoRhEbKp+HF1FWKW7RIJdtQ==
+t644ebqk9bibcna874givr6joj62mlhv.example. 3600 IN NSEC3 1 1 12 AABBCCDD 0P9MHAVEQVM6T7VBL5LOP2U3T2RP3TOM A HINFO AAAA RRSIG
+t644ebqk9bibcna874givr6joj62mlhv.example. 3600 IN RRSIG NSEC3 7 2 3600 20150420235959 20051021000000 40430 example. RAjGECB8P7O+F4Pa4Dx3tC0M+Z3KmlLKImcafb9XWwx+NWUNz7NBEDBQ HivIyKPVDkChcePIX1xPl1ATNa+8Dw==
diff --git a/src/lib/util/io/fd_share.cc b/src/lib/util/io/fd_share.cc
index 9c02a33..7adbbbe 100644
--- a/src/lib/util/io/fd_share.cc
+++ b/src/lib/util/io/fd_share.cc
@@ -20,6 +20,7 @@
#include <sys/uio.h>
#include <errno.h>
#include <stdlib.h> // for malloc and free
+#include <unistd.h>
#include "fd_share.h"
namespace isc {
@@ -106,7 +107,21 @@ recv_fd(const int sock) {
std::memcpy(&fd, CMSG_DATA(cmsg), sizeof(int));
}
free(msghdr.msg_control);
- return (fd);
+ // It is strange, but the call can return the same file descriptor as
+ // one returned previously, even if that one is not closed yet. So,
+ // we just re-number every one we get, so they are unique.
+ int new_fd(dup(fd));
+ int close_error(close(fd));
+ if (close_error == -1 || new_fd == -1) {
+ // We need to return an error, because something failed. But in case
+ // it was the previous close, we at least try to close the duped FD.
+ if (new_fd != -1) {
+ close(new_fd); // If this fails, nothing but returning error can't
+ // be done and we are doing that anyway.
+ }
+ return (FD_SYSTEM_ERROR);
+ }
+ return (new_fd);
}
int
diff --git a/src/lib/util/io/sockaddr_util.h b/src/lib/util/io/sockaddr_util.h
index 4c9149e..3ec6cf0 100644
--- a/src/lib/util/io/sockaddr_util.h
+++ b/src/lib/util/io/sockaddr_util.h
@@ -51,12 +51,26 @@ convertSockAddr(const SAType* sa) {
}
template <typename SAType>
+const SAType*
+convertSockAddr(const struct sockaddr* sa) {
+ const void* p = sa;
+ return (static_cast<const SAType*>(p));
+}
+
+template <typename SAType>
struct sockaddr*
convertSockAddr(SAType* sa) {
void* p = sa;
return (static_cast<struct sockaddr*>(p));
}
+template <typename SAType>
+SAType*
+convertSockAddr(struct sockaddr* sa) {
+ void* p = sa;
+ return (static_cast<SAType*>(p));
+}
+
}
}
}
diff --git a/src/lib/util/locks.h b/src/lib/util/locks.h
index 971c413..daaf216 100644
--- a/src/lib/util/locks.h
+++ b/src/lib/util/locks.h
@@ -15,13 +15,9 @@
/// This file (right now) provides dummy locks
/// It also contains code to use boost/threads locks:
///
-/// if USE_BOOST_THREADS is defined, we typedef the relevant classes
-/// and derive from the relevant templates so our dummy locks are
-/// replaced by the boost locks (--enable-boost-threads)
///
-/// If USE_BOOST_THREADS is NOT defined, all locks are dummy classes
-/// that don't actually do anything. At this moment, only the very
-/// minimal set of methods that we actually use is defined.
+/// All locks are dummy classes that don't actually do anything. At this moment,
+/// only the very minimal set of methods that we actually use is defined.
///
/// Note that we need to include <config.h> in our .cc files for that
/// to be set. we might want to enfore this at compile time with a check
@@ -30,8 +26,6 @@
#ifndef __LOCKS_
#define __LOCKS_
-#ifndef USE_BOOST_THREADS
-
namespace isc {
namespace util {
namespace locks {
@@ -64,52 +58,4 @@ public:
} // namespace util
} // namespace isc
-#else // USE_BOOST_THREADS
-
-// Workaround for a problem with boost and sunstudio 5.10
-// There is a version check in there that appears wrong,
-// which makes including boost/thread.hpp fail
-// This will probably be fixed in a future version of boost,
-// in which case this part can be removed then
-#ifdef NEED_SUNPRO_WORKAROUND
-#if defined(__SUNPRO_CC) && __SUNPRO_CC == 0x5100
-#undef __SUNPRO_CC
-#define __SUNPRO_CC 0x5090
-#endif
-#endif // NEED_SUNPRO_WORKAROUND
-
-#include <boost/thread.hpp>
-#include <boost/interprocess/sync/sharable_lock.hpp>
-#include <boost/interprocess/sync/scoped_lock.hpp>
-#include <boost/interprocess/sync/interprocess_upgradable_mutex.hpp>
-#include <boost/interprocess/sync/interprocess_recursive_mutex.hpp>
-
-namespace isc {
-namespace util {
-namespace locks {
-
-typedef boost::mutex mutex;
-typedef boost::interprocess::interprocess_upgradable_mutex upgradable_mutex;
-typedef boost::interprocess::interprocess_recursive_mutex recursive_mutex;
-
-template <typename T>
-struct sharable_lock : public boost::interprocess::sharable_lock<T> {
-public:
- sharable_lock(T& mtype) : boost::interprocess::sharable_lock<T>(mtype) {}
-};
-
-
-template <class T>
-struct scoped_lock : public boost::interprocess::scoped_lock<T> {
-public:
- scoped_lock(T& mtype) : boost::interprocess::scoped_lock<T>(mtype) { }
-};
-
-} // namespace locks
-} // namespace util
-} // namespace isc
-
-
-#endif // USE_BOOST_THREADS
-
#endif // __LOCKS_
diff --git a/src/lib/util/python/.gitignore b/src/lib/util/python/.gitignore
new file mode 100644
index 0000000..c54df80
--- /dev/null
+++ b/src/lib/util/python/.gitignore
@@ -0,0 +1,2 @@
+/gen_wiredata.py
+/mkpywrapper.py
diff --git a/src/lib/util/python/gen_wiredata.py.in b/src/lib/util/python/gen_wiredata.py.in
index 8bd2b3c..8b3eac0 100755
--- a/src/lib/util/python/gen_wiredata.py.in
+++ b/src/lib/util/python/gen_wiredata.py.in
@@ -949,12 +949,11 @@ class NSEC(NSECBASE):
int(len(name_wire) / 2)))
f.write('%s\n' % name_wire)
-class NSEC3(NSECBASE):
- '''Implements rendering NSEC3 RDATA in the test data format.
+class NSEC3PARAM(RR):
+ '''Implements rendering NSEC3PARAM RDATA in the test data format.
Configurable parameters are as follows (see the description of the
same name of attribute for the default value):
- - Type bitmap related parameters: see class NSECBASE
- hashalg (8-bit int): The Hash Algorithm field. Note that
currently the only defined algorithm is SHA-1, for which a value
of 1 will be used, and it's the default. So this implementation
@@ -967,9 +966,6 @@ class NSEC3(NSECBASE):
- saltlen (int): The Salt Length field.
- salt (string): The Salt field. It is converted to a sequence of
ascii codes and its hexadecimal representation will be used.
- - hashlen (int): The Hash Length field.
- - hash (string): The Next Hashed Owner Name field. This parameter
- is interpreted as "salt".
'''
hashalg = 1 # SHA-1
@@ -978,15 +974,18 @@ class NSEC3(NSECBASE):
iterations = 1
saltlen = 5
salt = 's' * saltlen
- hashlen = 20
- hash = 'h' * hashlen
- def dump_fixedpart(self, f, bitmap_totallen):
+
+ def dump(self, f):
if self.rdlen is None:
- # if rdlen needs to be calculated, it must be based on the bitmap
- # length, because the configured maplen can be fake.
- self.rdlen = 4 + 1 + len(self.salt) + 1 + len(self.hash) \
- + bitmap_totallen
+ self.rdlen = 4 + 1 + len(self.salt)
self.dump_header(f, self.rdlen)
+ self._dump_params(f)
+
+ def _dump_params(self, f):
+ '''This method is intended to be shared with NSEC3 class.
+
+ '''
+
optout_val = 1 if self.optout else 0
f.write('# Hash Alg=%s, Opt-Out=%d, Other Flags=%0x, Iterations=%d\n' %
(code_totext(self.hashalg, rdict_nsec3_algorithm),
@@ -997,6 +996,29 @@ class NSEC3(NSECBASE):
f.write('%02x%s%s\n' % (self.saltlen,
' ' if len(self.salt) > 0 else '',
encode_string(self.salt)))
+
+class NSEC3(NSECBASE, NSEC3PARAM):
+ '''Implements rendering NSEC3 RDATA in the test data format.
+
+ Configurable parameters are as follows (see the description of the
+ same name of attribute for the default value):
+ - Type bitmap related parameters: see class NSECBASE
+ - Hash parameter related parameters: see class NSEC3PARAM
+ - hashlen (int): The Hash Length field.
+ - hash (string): The Next Hashed Owner Name field. This parameter
+ is interpreted as "salt".
+ '''
+
+ hashlen = 20
+ hash = 'h' * hashlen
+ def dump_fixedpart(self, f, bitmap_totallen):
+ if self.rdlen is None:
+ # if rdlen needs to be calculated, it must be based on the bitmap
+ # length, because the configured maplen can be fake.
+ self.rdlen = 4 + 1 + len(self.salt) + 1 + len(self.hash) \
+ + bitmap_totallen
+ self.dump_header(f, self.rdlen)
+ self._dump_params(f)
f.write("# Hash Len=%d, Hash='%s'\n" % (self.hashlen, self.hash))
f.write('%02x%s%s\n' % (self.hashlen,
' ' if len(self.hash) > 0 else '',
diff --git a/src/lib/util/tests/.gitignore b/src/lib/util/tests/.gitignore
new file mode 100644
index 0000000..d6d1ec8
--- /dev/null
+++ b/src/lib/util/tests/.gitignore
@@ -0,0 +1 @@
+/run_unittests
diff --git a/src/lib/xfr/tests/.gitignore b/src/lib/xfr/tests/.gitignore
new file mode 100644
index 0000000..d6d1ec8
--- /dev/null
+++ b/src/lib/xfr/tests/.gitignore
@@ -0,0 +1 @@
+/run_unittests
diff --git a/tests/lettuce/.gitignore b/tests/lettuce/.gitignore
new file mode 100644
index 0000000..e389f47
--- /dev/null
+++ b/tests/lettuce/.gitignore
@@ -0,0 +1 @@
+/setup_intree_bind10.sh
diff --git a/tests/lettuce/configurations/multi_instance/multi_auth.config.orig b/tests/lettuce/configurations/multi_instance/multi_auth.config.orig
new file mode 100644
index 0000000..f181d42
--- /dev/null
+++ b/tests/lettuce/configurations/multi_instance/multi_auth.config.orig
@@ -0,0 +1 @@
+{"version": 2, "Auth": {"listen_on": [{"port": 47806, "address": "0.0.0.0"}]}, "Boss": {"components": {"b10-auth-2": {"kind": "needed", "special": "auth"}, "b10-auth": {"kind": "needed", "special": "auth"}, "b10-cmdctl": {"kind": "needed", "special": "cmdctl"}}}}
diff --git a/tests/lettuce/configurations/nsec3/nsec3_auth.config b/tests/lettuce/configurations/nsec3/nsec3_auth.config
new file mode 100644
index 0000000..2de5284
--- /dev/null
+++ b/tests/lettuce/configurations/nsec3/nsec3_auth.config
@@ -0,0 +1 @@
+{"version": 2, "Auth": {"datasources": [{"zones": [{"origin": "example.", "file": "configurations/nsec3/rfc5155-example.zone.signed"}], "type": "memory"}], "listen_on": [{"port": 47806, "address": "0.0.0.0"}]}, "Boss": {"components": {"b10-auth": {"kind": "needed", "special": "auth"}, "b10-cmdctl": {"kind": "needed", "special": "cmdctl"}}}}
diff --git a/tests/lettuce/configurations/nsec3/rfc5155-example.zone.signed b/tests/lettuce/configurations/nsec3/rfc5155-example.zone.signed
new file mode 100644
index 0000000..4120224
--- /dev/null
+++ b/tests/lettuce/configurations/nsec3/rfc5155-example.zone.signed
@@ -0,0 +1,72 @@
+;; The example NSEC3-signed zone used in RFC5155.
+
+example. 3600 IN SOA ns1.example. bugs.x.w.example. 1 3600 300 3600000 3600
+example. 3600 IN RRSIG SOA 7 1 3600 20150420235959 20051021000000 40430 example. Hu25UIyNPmvPIVBrldN+9Mlp9Zql39qaUd8iq4ZLlYWfUUbbAS41pG+6 8z81q1xhkYAcEyHdVI2LmKusbZsT0Q==
+example. 3600 IN NS ns1.example.
+example. 3600 IN NS ns2.example.
+example. 3600 IN RRSIG NS 7 1 3600 20150420235959 20051021000000 40430 example. PVOgtMK1HHeSTau+HwDWC8Ts+6C8qtqd4pQJqOtdEVgg+MA+ai4fWDEh u3qHJyLcQ9tbD2vvCnMXjtz6SyObxA==
+example. 3600 IN MX 1 xx.example.
+example. 3600 IN RRSIG MX 7 1 3600 20150420235959 20051021000000 40430 example. GgQ1A9xs47k42VPvpL/a1BWUz/6XsnHkjotw9So8MQtZtl2wJBsnOQsa oHrRCrRbyriEl/GZn9Mto/Kx+wBo+w==
+example. 3600 IN DNSKEY 256 3 7 AwEAAaetidLzsKWUt4swWR8yu0wPHPiUi8LUsAD0QPWU+wzt89epO6tH zkMBVDkC7qphQO2hTY4hHn9npWFRw5BYubE=
+example. 3600 IN DNSKEY 257 3 7 AwEAAcUlFV1vhmqx6NSOUOq2R/dsR7Xm3upJj7IommWSpJABVfW8Q0rO vXdM6kzt+TAu92L9AbsUdblMFin8CVF3n4s=
+example. 3600 IN RRSIG DNSKEY 7 1 3600 20150420235959 20051021000000 12708 example. AuU4juU9RaxescSmStrQks3Gh9FblGBlVU31uzMZ/U/FpsUb8aC6QZS+ sTsJXnLnz7flGOsmMGQZf3bH+QsCtg==
+example. 3600 IN NSEC3PARAM 1 0 12 AABBCCDD
+example. 3600 IN RRSIG NSEC3PARAM 7 1 3600 20150420235959 20051021000000 40430 example. C1Gl8tPZNtnjlrYWDeeUV/sGLCyy/IHie2rerN05XSA3Pq0U3+4VvGWY WdUMfflOdxqnXHwJTLQsjlkynhG6Cg==
+2t7b4g4vsa5smi47k61mv5bv1a22bojr.example. 3600 IN A 192.0.2.127
+2t7b4g4vsa5smi47k61mv5bv1a22bojr.example. 3600 IN RRSIG A 7 2 3600 20150420235959 20051021000000 40430 example. h6c++bzhRuWWt2bykN6mjaTNBcXNq5UuL5EdK+iDP4eY8I0kSiKaCjg3 tC1SQkeloMeub2GWk8p6xHMPZumXlw==
+a.example. 3600 IN NS ns1.a.example.
+a.example. 3600 IN NS ns2.a.example.
+a.example. 3600 IN DS 58470 5 1 3079F1593EBAD6DC121E202A8B766A6A4837206C
+a.example. 3600 IN RRSIG DS 7 2 3600 20150420235959 20051021000000 40430 example. XacFcQVHLVzdoc45EJhN616zQ4mEXtE8FzUhM2KWjfy1VfRKD9r1MeVG wwoukOKgJxBPFsWoo722vZ4UZ2dIdA==
+ns1.a.example. 3600 IN A 192.0.2.5
+ns2.a.example. 3600 IN A 192.0.2.6
+ai.example. 3600 IN A 192.0.2.9
+ai.example. 3600 IN RRSIG A 7 2 3600 20150420235959 20051021000000 40430 example. hVe+wKYMlObTRPhX0NL67GxeZfdxqr/QeR6FtfdAj5+FgYxyzPEjIzvK Wy00hWIl6wD3Vws+rznEn8sQ64UdqA==
+ai.example. 3600 IN HINFO "KLH-10" "ITS"
+ai.example. 3600 IN RRSIG HINFO 7 2 3600 20150420235959 20051021000000 40430 example. Yi42uOq43eyO6qXHNvwwfFnIustWgV5urFcxenkLvs6pKRh00VBjODmf 3Z4nMO7IOl6nHSQ1v0wLHpEZG7Xj2w==
+ai.example. 3600 IN AAAA 2001:db8::f00:baa9
+ai.example. 3600 IN RRSIG AAAA 7 2 3600 20150420235959 20051021000000 40430 example. LcdxKaCB5bGZwPDg+3JJ4O02zoMBrjxqlf6WuaHQZZfTUpb9Nf2nxFGe 2XRPfR5tpJT6GdRGcHueLuXkMjBArQ==
+c.example. 3600 IN NS ns1.c.example.
+c.example. 3600 IN NS ns2.c.example.
+ns1.c.example. 3600 IN A 192.0.2.7
+ns2.c.example. 3600 IN A 192.0.2.8
+ns1.example. 3600 IN A 192.0.2.1
+ns1.example. 3600 IN RRSIG A 7 2 3600 20150420235959 20051021000000 40430 example. bu6kx73n6XEunoVGuRfAgY7EF/AJqHy7hj0jkiqJjB0dOrx3wuz9SaBe GfqWIdn/uta3SavN4FRvZR9SCFHF5Q==
+ns2.example. 3600 IN A 192.0.2.2
+ns2.example. 3600 IN RRSIG A 7 2 3600 20150420235959 20051021000000 40430 example. ktQ3TqE0CfRfki0Rb/Ip5BM0VnxelbuejCC4zpLbFKA/7eD7UNAwxMgx JPtbdST+syjYSJaj4IHfeX6n8vfoGA==
+*.w.example. 3600 IN MX 1 ai.example.
+*.w.example. 3600 IN RRSIG MX 7 2 3600 20150420235959 20051021000000 40430 example. CikebjQwGQPwijVcxgcZcSJKtfynugtlBiKb9FcBTrmOoyQ4InoWVudh CWsh/URX3lc4WRUMivEBP6+4KS3ldA==
+x.w.example. 3600 IN MX 1 xx.example.
+x.w.example. 3600 IN RRSIG MX 7 3 3600 20150420235959 20051021000000 40430 example. IrK3tq/tHFIBF0scHiE/1IwMAvckS/55hAVvQyxTFbkAdDloP3NbZzu+ yoSsr3b3OX6qbBpY7WCtwwekLKRAwQ==
+x.y.w.example. 3600 IN MX 1 xx.example.
+x.y.w.example. 3600 IN RRSIG MX 7 4 3600 20150420235959 20051021000000 40430 example. MqSt5HqJIN8+SLlzTOImrh5h9Xa6gDvAW/GnnbdPc6Z7nXvCpLPJj/5l Cwx3VuzVOjkbvXze8/8Ccl2Zn2hbug==
+xx.example. 3600 IN A 192.0.2.10
+xx.example. 3600 IN RRSIG A 7 2 3600 20150420235959 20051021000000 40430 example. T35hBWEZ017VC5u2c4OriKyVn/pu+fVK4AlXYOxJ6iQylfV2HQIKjv6b 7DzINB3aF/wjJqgXpQvhq+Ac6+ZiFg==
+xx.example. 3600 IN HINFO "KLH-10" "TOPS-20"
+xx.example. 3600 IN RRSIG HINFO 7 2 3600 20150420235959 20051021000000 40430 example. KimG+rDd+7VA1zRsu0ITNAQUTRlpnsmqWrihFRnU+bRa93v2e5oFNFYC s3Rqgv62K93N7AhW6Jfqj/8NzWjvKg==
+xx.example. 3600 IN AAAA 2001:db8::f00:baaa
+xx.example. 3600 IN RRSIG AAAA 7 2 3600 20150420235959 20051021000000 40430 example. IXBcXORITNwd8h3gNwyxtYFvAupS/CYWufVeuBUX0O25ivBCULjZjpDx FSxfohb/KA7YRdxENzYfMItpILl/Xw==
+0p9mhaveqvm6t7vbl5lop2u3t2rp3tom.example. 3600 IN NSEC3 1 1 12 AABBCCDD 2T7B4G4VSA5SMI47K61MV5BV1A22BOJR NS SOA MX RRSIG DNSKEY NSEC3PARAM
+0p9mhaveqvm6t7vbl5lop2u3t2rp3tom.example. 3600 IN RRSIG NSEC3 7 2 3600 20150420235959 20051021000000 40430 example. OSgWSm26B+cS+dDL8b5QrWr/dEWhtCsKlwKLIBHYH6blRxK9rC0bMJPw Q4mLIuw85H2EY762BOCXJZMnpuwhpA==
+2t7b4g4vsa5smi47k61mv5bv1a22bojr.example. 3600 IN NSEC3 1 1 12 AABBCCDD 2VPTU5TIMAMQTTGL4LUU9KG21E0AOR3S A RRSIG
+2t7b4g4vsa5smi47k61mv5bv1a22bojr.example. 3600 IN RRSIG NSEC3 7 2 3600 20150420235959 20051021000000 40430 example. OmBvJ1Vgg1hCKMXHFiNeIYHK9XVW0iLDLwJN4TFoNxZuP03gAXEI634Y wOc4YBNITrj413iqNI6mRk/r1dOSUw==
+2vptu5timamqttgl4luu9kg21e0aor3s.example. 3600 IN NSEC3 1 1 12 AABBCCDD 35MTHGPGCU1QG68FAB165KLNSNK3DPVL MX RRSIG
+2vptu5timamqttgl4luu9kg21e0aor3s.example. 3600 IN RRSIG NSEC3 7 2 3600 20150420235959 20051021000000 40430 example. KL1V2oFYghNV0Hm7Tf2vpJjM6l+0g1JCcVYGVfI0lKrhPmTsOA96cLEA Cgo1x8I7kApJX+obTuktZ+sdsZPY1w==
+35mthgpgcu1qg68fab165klnsnk3dpvl.example. 3600 IN NSEC3 1 1 12 AABBCCDD B4UM86EGHHDS6NEA196SMVMLO4ORS995 NS DS RRSIG
+35mthgpgcu1qg68fab165klnsnk3dpvl.example. 3600 IN RRSIG NSEC3 7 2 3600 20150420235959 20051021000000 40430 example. g6jPUUpduAJKRljUsN8gB4UagAX0NxY9shwQAynzo8EUWH+z6hEIBlUT PGj15eZll6VhQqgZXtAIR3chwgW+SA==
+b4um86eghhds6nea196smvmlo4ors995.example. 3600 IN NSEC3 1 1 12 AABBCCDD GJEQE526PLBF1G8MKLP59ENFD789NJGI MX RRSIG
+b4um86eghhds6nea196smvmlo4ors995.example. 3600 IN RRSIG NSEC3 7 2 3600 20150420235959 20051021000000 40430 example. ZkPG3M32lmoHM6pa3D6gZFGB/rhL//Bs3Omh5u4m/CUiwtblEVOaAKKZ d7S959OeiX43aLX3pOv0TSTyiTxIZg==
+gjeqe526plbf1g8mklp59enfd789njgi.example. 3600 IN NSEC3 1 1 12 AABBCCDD JI6NEOAEPV8B5O6K4EV33ABHA8HT9FGC A HINFO AAAA RRSIG
+gjeqe526plbf1g8mklp59enfd789njgi.example. 3600 IN RRSIG NSEC3 7 2 3600 20150420235959 20051021000000 40430 example. IVnezTJ9iqblFF97vPSmfXZ5Zozngx3KX3byLTZC4QBH2dFWhf6scrGF ZB980AfCxoD9qbbKDy+rdGIeRSVNyw==
+ji6neoaepv8b5o6k4ev33abha8ht9fgc.example. 3600 IN NSEC3 1 1 12 AABBCCDD K8UDEMVP1J2F7EG6JEBPS17VP3N8I58H
+ji6neoaepv8b5o6k4ev33abha8ht9fgc.example. 3600 IN RRSIG NSEC3 7 2 3600 20150420235959 20051021000000 40430 example. gPkFp1s2QDQ6wQzcg1uSebZ61W33rUBDcTj72F3kQ490fEdp7k1BUIfb cZtPbX3YCpE+sIt0MpzVSKfTwx4uYA==
+k8udemvp1j2f7eg6jebps17vp3n8i58h.example. 3600 IN NSEC3 1 1 12 AABBCCDD KOHAR7MBB8DC2CE8A9QVL8HON4K53UHI
+k8udemvp1j2f7eg6jebps17vp3n8i58h.example. 3600 IN RRSIG NSEC3 7 2 3600 20150420235959 20051021000000 40430 example. FtXGbvF0+wf8iWkyo73enAuVx03klN+pILBKS6qCcftVtfH4yVzsEZqu J27NHR7ruxJWDNMtOtx7w9WfcIg62A==
+kohar7mbb8dc2ce8a9qvl8hon4k53uhi.example. 3600 IN NSEC3 1 1 12 AABBCCDD Q04JKCEVQVMU85R014C7DKBA38O0JI5R A RRSIG
+kohar7mbb8dc2ce8a9qvl8hon4k53uhi.example. 3600 IN RRSIG NSEC3 7 2 3600 20150420235959 20051021000000 40430 example. VrDXs2uVW21N08SyQIz88zml+y4ZCInTwgDr6zz43yAg+LFERjOrj3Oj ct51ac7Dp4eZbf9FQJazmASFKGxGXg==
+q04jkcevqvmu85r014c7dkba38o0ji5r.example. 3600 IN NSEC3 1 1 12 AABBCCDD R53BQ7CC2UVMUBFU5OCMM6PERS9TK9EN A RRSIG
+q04jkcevqvmu85r014c7dkba38o0ji5r.example. 3600 IN RRSIG NSEC3 7 2 3600 20150420235959 20051021000000 40430 example. hV5I89b+4FHJDATp09g4bbN0R1F845CaXpL3ZxlMKimoPAyqletMlEWw LfFia7sdpSzn+ZlNNlkxWcLsIlMmUg==
+r53bq7cc2uvmubfu5ocmm6pers9tk9en.example. 3600 IN NSEC3 1 1 12 AABBCCDD T644EBQK9BIBCNA874GIVR6JOJ62MLHV MX RRSIG
+r53bq7cc2uvmubfu5ocmm6pers9tk9en.example. 3600 IN RRSIG NSEC3 7 2 3600 20150420235959 20051021000000 40430 example. aupviViruXs4bDg9rCbezzBMf9h1ZlDvbW/CZFKulIGXXLj8B/fsDJar XVDA9bnUoRhEbKp+HF1FWKW7RIJdtQ==
+t644ebqk9bibcna874givr6joj62mlhv.example. 3600 IN NSEC3 1 1 12 AABBCCDD 0P9MHAVEQVM6T7VBL5LOP2U3T2RP3TOM A HINFO AAAA RRSIG
+t644ebqk9bibcna874givr6joj62mlhv.example. 3600 IN RRSIG NSEC3 7 2 3600 20150420235959 20051021000000 40430 example. RAjGECB8P7O+F4Pa4Dx3tC0M+Z3KmlLKImcafb9XWwx+NWUNz7NBEDBQ HivIyKPVDkChcePIX1xPl1ATNa+8Dw==
diff --git a/tests/lettuce/features/bindctl_commands.feature b/tests/lettuce/features/bindctl_commands.feature
new file mode 100644
index 0000000..872064f
--- /dev/null
+++ b/tests/lettuce/features/bindctl_commands.feature
@@ -0,0 +1,38 @@
+Feature: control with bindctl
+ Assorted tests using bindctl for the administration of BIND 10.
+
+ Scenario: Removing modules
+ # This test runs the original example configuration, which has
+ # a number of modules. It then removes all non-essential modules,
+ # and checks whether they do disappear from the list of running
+ # modules (note that it 'misuses' the help command for this,
+ # there is a Boss command 'show_processes' but it's output is
+ # currently less standardized than 'help')
+ Given I have bind10 running with configuration example.org.config
+
+ Then remove bind10 configuration Boss/components/NOSUCHMODULE
+ last bindctl output should contain Error
+
+ bind10 module Xfrout should be running
+ bind10 module Stats should be running
+ bind10 module Zonemgr should be running
+ bind10 module Xfrin should be running
+ bind10 module Auth should be running
+ bind10 module StatsHttpd should be running
+
+ Then remove bind10 configuration Boss/components value b10-xfrout
+ last bindctl output should not contain Error
+ # assuming it won't error for further modules (if it does, the final
+ # 'should not be running' tests would fail anyway)
+ Then remove bind10 configuration Boss/components value b10-stats
+ Then remove bind10 configuration Boss/components value b10-zonemgr
+ Then remove bind10 configuration Boss/components value b10-xfrin
+ Then remove bind10 configuration Boss/components value b10-auth
+ Then remove bind10 configuration Boss/components value b10-stats-httpd
+
+ bind10 module Xfrout should not be running
+ bind10 module Stats should not be running
+ bind10 module Zonemgr should not be running
+ bind10 module Xfrin should not be running
+ bind10 module Auth should not be running
+ bind10 module StatsHttpd should not be running
diff --git a/tests/lettuce/features/multi_instance.feature b/tests/lettuce/features/multi_instance.feature
new file mode 100644
index 0000000..864431d
--- /dev/null
+++ b/tests/lettuce/features/multi_instance.feature
@@ -0,0 +1,35 @@
+Feature: Multiple instances
+ This feature tests whether multiple instances can be run, and whether
+ removing them does not affect the running of other instances
+
+ Scenario: Multiple instances of Auth
+ # This config should have two running instances
+ Given I have bind10 running with configuration multi_instance/multi_auth.config
+ And bind10 module Auth should be running
+ A query for example.com should have rcode REFUSED
+
+ # this also checks whether the process is running
+ If I remember the pid of process b10-auth
+ And remember the pid of process b10-auth-2
+
+ When I remove bind10 configuration Boss/components value b10-auth-2
+
+ Then the pid of process b10-auth should not have changed
+ And a query for example.com should have rcode REFUSED
+
+ When I send bind10 the following commands
+ """
+ config add Boss/components b10-auth-2
+ config set Boss/components/b10-auth-2/special auth
+ config set Boss/components/b10-auth-2/kind needed
+ config commit
+ """
+ And wait for new bind10 stderr message AUTH_SERVER_STARTED
+ And remember the pid of process b10-auth-2
+
+ Then the pid of process b10-auth should not have changed
+ A query for example.com should have rcode REFUSED
+
+ When I remove bind10 configuration Boss/components value b10-auth
+ Then the pid of process b10-auth-2 should not have changed
+ A query for example.com should have rcode REFUSED
diff --git a/tests/lettuce/features/nsec3_auth.feature b/tests/lettuce/features/nsec3_auth.feature
new file mode 100644
index 0000000..ceb9a7f
--- /dev/null
+++ b/tests/lettuce/features/nsec3_auth.feature
@@ -0,0 +1,302 @@
+Feature: NSEC3 Authoritative service
+ This feature tests NSEC3 as defined in RFC5155, using the example
+ zone from appendix A and testing the example responses from appendix B.
+ Additional tests can be added as well.
+
+ # Response section data is taken directly from RFC5155
+ # It has been modified slightly; it has been 'flattened' (i.e. converted
+ # to 1-line RRs with TTL and class data), and whitespace has been added
+ # in the places where dig adds them too.
+ # Any other changes from the specific example data are added as inline
+ # comments.
+
+ Scenario: B.1. Name Error
+ Given I have bind10 running with configuration nsec3/nsec3_auth.config
+ A dnssec query for a.c.x.w.example. should have rcode NXDOMAIN
+ The last query response should have flags qr aa rd
+ The last query response should have edns_flags do
+ The last query response should have ancount 0
+ The last query response should have nscount 8
+ The last query response should have adcount 1
+ The authority section of the last query response should be
+ """
+ example. 3600 IN SOA ns1.example. bugs.x.w.example. 1 3600 300 3600000 3600
+ example. 3600 IN RRSIG SOA 7 1 3600 20150420235959 20051021000000 40430 example. Hu25UIyNPmvPIVBrldN+9Mlp9Zql39qaUd8iq4ZLlYWfUUbbAS41pG+6 8z81q1xhkYAcEyHdVI2LmKusbZsT0Q==
+ 0p9mhaveqvm6t7vbl5lop2u3t2rp3tom.example. 3600 IN NSEC3 1 1 12 aabbccdd 2t7b4g4vsa5smi47k61mv5bv1a22bojr NS SOA MX RRSIG DNSKEY NSEC3PARAM
+ 0p9mhaveqvm6t7vbl5lop2u3t2rp3tom.example. 3600 IN RRSIG NSEC3 7 2 3600 20150420235959 20051021000000 40430 example. OSgWSm26B+cS+dDL8b5QrWr/dEWhtCsKlwKLIBHYH6blRxK9rC0bMJPw Q4mLIuw85H2EY762BOCXJZMnpuwhpA==
+ b4um86eghhds6nea196smvmlo4ors995.example. 3600 IN NSEC3 1 1 12 aabbccdd gjeqe526plbf1g8mklp59enfd789njgi MX RRSIG
+ b4um86eghhds6nea196smvmlo4ors995.example. 3600 IN RRSIG NSEC3 7 2 3600 20150420235959 20051021000000 40430 example. ZkPG3M32lmoHM6pa3D6gZFGB/rhL//Bs3Omh5u4m/CUiwtblEVOaAKKZ d7S959OeiX43aLX3pOv0TSTyiTxIZg==
+ 35mthgpgcu1qg68fab165klnsnk3dpvl.example. 3600 IN NSEC3 1 1 12 aabbccdd b4um86eghhds6nea196smvmlo4ors995 NS DS RRSIG
+ 35mthgpgcu1qg68fab165klnsnk3dpvl.example. 3600 IN RRSIG NSEC3 7 2 3600 20150420235959 20051021000000 40430 example. g6jPUUpduAJKRljUsN8gB4UagAX0NxY9shwQAynzo8EUWH+z6hEIBlUT PGj15eZll6VhQqgZXtAIR3chwgW+SA==
+ """
+
+ Scenario: B.2. No Data Error
+ Given I have bind10 running with configuration nsec3/nsec3_auth.config
+ A dnssec query for ns1.example. type MX should have rcode NOERROR
+ The last query response should have flags qr aa rd
+ The last query response should have edns_flags do
+ The last query response should have ancount 0
+ The last query response should have nscount 4
+ The last query response should have adcount 1
+ The authority section of the last query response should be
+ """
+ example. 3600 IN SOA ns1.example. bugs.x.w.example. 1 3600 300 3600000 3600
+ example. 3600 IN RRSIG SOA 7 1 3600 20150420235959 20051021000000 40430 example. Hu25UIyNPmvPIVBrldN+9Mlp9Zql39qaUd8iq4ZLlYWfUUbbAS41pG+6 8z81q1xhkYAcEyHdVI2LmKusbZsT0Q==
+ 2t7b4g4vsa5smi47k61mv5bv1a22bojr.example. 3600 IN NSEC3 1 1 12 aabbccdd 2vptu5timamqttgl4luu9kg21e0aor3s A RRSIG
+ 2t7b4g4vsa5smi47k61mv5bv1a22bojr.example. 3600 IN RRSIG NSEC3 7 2 3600 20150420235959 20051021000000 40430 example. OmBvJ1Vgg1hCKMXHFiNeIYHK9XVW0iLDLwJN4TFoNxZuP03gAXEI634Y wOc4YBNITrj413iqNI6mRk/r1dOSUw==
+ """
+
+ Scenario: B2.1. No Data Error, Empty Non-Terminal
+ Given I have bind10 running with configuration nsec3/nsec3_auth.config
+ A dnssec query for y.w.example. should have rcode NOERROR
+ The last query response should have flags qr aa rd
+ The last query response should have edns_flags do
+ The last query response should have ancount 0
+ The last query response should have nscount 4
+ The last query response should have adcount 1
+ The authority section of the last query response should be
+ """
+ example. 3600 IN SOA ns1.example. bugs.x.w.example. 1 3600 300 3600000 3600
+ example. 3600 IN RRSIG SOA 7 1 3600 20150420235959 20051021000000 40430 example. Hu25UIyNPmvPIVBrldN+9Mlp9Zql39qaUd8iq4ZLlYWfUUbbAS41pG+6 8z81q1xhkYAcEyHdVI2LmKusbZsT0Q==
+ ji6neoaepv8b5o6k4ev33abha8ht9fgc.example. 3600 IN NSEC3 1 1 12 aabbccdd k8udemvp1j2f7eg6jebps17vp3n8i58h
+ ji6neoaepv8b5o6k4ev33abha8ht9fgc.example. 3600 IN RRSIG NSEC3 7 2 3600 20150420235959 20051021000000 40430 example. gPkFp1s2QDQ6wQzcg1uSebZ61W33rUBDcTj72F3kQ490fEdp7k1BUIfb cZtPbX3YCpE+sIt0MpzVSKfTwx4uYA==
+ """
+
+ Scenario: B.3. Referral to an Opt-Out Unsigned Zone
+ Given I have bind10 running with configuration nsec3/nsec3_auth.config
+ A dnssec query for mc.c.example. type MX should have rcode NOERROR
+ The last query response should have flags qr rd
+ The last query response should have edns_flags do
+ The last query response should have ancount 0
+ The last query response should have nscount 6
+ The last query response should have adcount 3
+ The authority section of the last query response should be
+ """
+ c.example. 3600 IN NS ns1.c.example.
+ c.example. 3600 IN NS ns2.c.example.
+ 35mthgpgcu1qg68fab165klnsnk3dpvl.example. 3600 IN NSEC3 1 1 12 aabbccdd b4um86eghhds6nea196smvmlo4ors995 NS DS RRSIG
+ 35mthgpgcu1qg68fab165klnsnk3dpvl.example. 3600 IN RRSIG NSEC3 7 2 3600 20150420235959 20051021000000 40430 example. g6jPUUpduAJKRljUsN8gB4UagAX0NxY9shwQAynzo8EUWH+z6hEIBlUT PGj15eZll6VhQqgZXtAIR3chwgW+SA==
+ 0p9mhaveqvm6t7vbl5lop2u3t2rp3tom.example. 3600 IN NSEC3 1 1 12 aabbccdd 2t7b4g4vsa5smi47k61mv5bv1a22bojr NS SOA MX RRSIG DNSKEY NSEC3PARAM
+ 0p9mhaveqvm6t7vbl5lop2u3t2rp3tom.example. 3600 IN RRSIG NSEC3 7 2 3600 20150420235959 20051021000000 40430 example. OSgWSm26B+cS+dDL8b5QrWr/dEWhtCsKlwKLIBHYH6blRxK9rC0bMJPw Q4mLIuw85H2EY762BOCXJZMnpuwhpA==
+ """
+ The additional section of the last query response should be
+ """
+ ns1.c.example. 3600 IN A 192.0.2.7
+ ns2.c.example. 3600 IN A 192.0.2.8
+ """
+
+ Scenario: B.4. Wildcard Expansion
+ Given I have bind10 running with configuration nsec3/nsec3_auth.config
+ A dnssec query for a.z.w.example. type MX should have rcode NOERROR
+ The last query response should have flags qr aa rd
+ The last query response should have edns_flags do
+ The last query response should have ancount 2
+ The last query response should have nscount 5
+ The last query response should have adcount 9
+ The answer section of the last query response should be
+ """
+ a.z.w.example. 3600 IN MX 1 ai.example.
+ a.z.w.example. 3600 IN RRSIG MX 7 2 3600 20150420235959 20051021000000 40430 example. CikebjQwGQPwijVcxgcZcSJKtfynugtlBiKb9FcBTrmOoyQ4InoWVudh CWsh/URX3lc4WRUMivEBP6+4KS3ldA==
+ """
+ The authority section of the last query response should be
+ """
+ example. 3600 IN NS ns1.example.
+ example. 3600 IN NS ns2.example.
+ example. 3600 IN RRSIG NS 7 1 3600 20150420235959 20051021000000 40430 example. PVOgtMK1HHeSTau+HwDWC8Ts+6C8qtqd4pQJqOtdEVgg+MA+ai4fWDEh u3qHJyLcQ9tbD2vvCnMXjtz6SyObxA==
+ q04jkcevqvmu85r014c7dkba38o0ji5r.example. 3600 IN NSEC3 1 1 12 aabbccdd r53bq7cc2uvmubfu5ocmm6pers9tk9en A RRSIG
+ q04jkcevqvmu85r014c7dkba38o0ji5r.example. 3600 IN RRSIG NSEC3 7 2 3600 20150420235959 20051021000000 40430 example. hV5I89b+4FHJDATp09g4bbN0R1F845CaXpL3ZxlMKimoPAyqletMlEWw LfFia7sdpSzn+ZlNNlkxWcLsIlMmUg==
+ """
+ # This is slightly different from the example in RFC5155; there are
+ # more RRs in the additional section.
+ The additional section of the last query response should be
+ """
+ ai.example. 3600 IN A 192.0.2.9
+ ai.example. 3600 IN AAAA 2001:db8::f00:baa9
+ ns1.example. 3600 IN A 192.0.2.1
+ ns2.example. 3600 IN A 192.0.2.2
+ ai.example. 3600 IN RRSIG A 7 2 3600 20150420235959 20051021000000 40430 example. hVe+wKYMlObTRPhX0NL67GxeZfdxqr/QeR6FtfdAj5+FgYxyzPEjIzvK Wy00hWIl6wD3Vws+rznEn8sQ64UdqA==
+ ai.example. 3600 IN RRSIG AAAA 7 2 3600 20150420235959 20051021000000 40430 example. LcdxKaCB5bGZwPDg+3JJ4O02zoMBrjxqlf6WuaHQZZfTUpb9Nf2nxFGe 2XRPfR5tpJT6GdRGcHueLuXkMjBArQ==
+ ns1.example. 3600 IN RRSIG A 7 2 3600 20150420235959 20051021000000 40430 example. bu6kx73n6XEunoVGuRfAgY7EF/AJqHy7hj0jkiqJjB0dOrx3wuz9SaBe GfqWIdn/uta3SavN4FRvZR9SCFHF5Q==
+ ns2.example. 3600 IN RRSIG A 7 2 3600 20150420235959 20051021000000 40430 example. ktQ3TqE0CfRfki0Rb/Ip5BM0VnxelbuejCC4zpLbFKA/7eD7UNAwxMgx JPtbdST+syjYSJaj4IHfeX6n8vfoGA==
+ """
+
+ Scenario: B.5. Wildcard No Data Error
+ Given I have bind10 running with configuration nsec3/nsec3_auth.config
+ A dnssec query for a.z.w.example. type AAAA should have rcode NOERROR
+ The last query response should have flags qr aa rd
+ The last query response should have edns_flags do
+ The last query response should have ancount 0
+ The last query response should have nscount 8
+ The last query response should have adcount 1
+ The authority section of the last query response should be
+ """
+ example. 3600 IN SOA ns1.example. bugs.x.w.example. 1 3600 300 3600000 3600
+ example. 3600 IN RRSIG SOA 7 1 3600 20150420235959 20051021000000 40430 example. Hu25UIyNPmvPIVBrldN+9Mlp9Zql39qaUd8iq4ZLlYWfUUbbAS41pG+6 8z81q1xhkYAcEyHdVI2LmKusbZsT0Q==
+ k8udemvp1j2f7eg6jebps17vp3n8i58h.example. 3600 IN NSEC3 1 1 12 aabbccdd kohar7mbb8dc2ce8a9qvl8hon4k53uhi
+ k8udemvp1j2f7eg6jebps17vp3n8i58h.example. 3600 IN RRSIG NSEC3 7 2 3600 20150420235959 20051021000000 40430 example. FtXGbvF0+wf8iWkyo73enAuVx03klN+pILBKS6qCcftVtfH4yVzsEZqu J27NHR7ruxJWDNMtOtx7w9WfcIg62A==
+ q04jkcevqvmu85r014c7dkba38o0ji5r.example. 3600 IN NSEC3 1 1 12 aabbccdd r53bq7cc2uvmubfu5ocmm6pers9tk9en A RRSIG
+ q04jkcevqvmu85r014c7dkba38o0ji5r.example. 3600 IN RRSIG NSEC3 7 2 3600 20150420235959 20051021000000 40430 example. hV5I89b+4FHJDATp09g4bbN0R1F845CaXpL3ZxlMKimoPAyqletMlEWw LfFia7sdpSzn+ZlNNlkxWcLsIlMmUg==
+ r53bq7cc2uvmubfu5ocmm6pers9tk9en.example. 3600 IN NSEC3 1 1 12 aabbccdd t644ebqk9bibcna874givr6joj62mlhv MX RRSIG
+ r53bq7cc2uvmubfu5ocmm6pers9tk9en.example. 3600 IN RRSIG NSEC3 7 2 3600 20150420235959 20051021000000 40430 example. aupviViruXs4bDg9rCbezzBMf9h1ZlDvbW/CZFKulIGXXLj8B/fsDJar XVDA9bnUoRhEbKp+HF1FWKW7RIJdtQ==
+ """
+
+ Scenario: B.6. DS Child Zone No Data Error
+ Given I have bind10 running with configuration nsec3/nsec3_auth.config
+ A dnssec query for example. type DS should have rcode NOERROR
+ The last query response should have flags qr aa rd
+ The last query response should have edns_flags do
+ The last query response should have ancount 0
+ The last query response should have nscount 4
+ The last query response should have adcount 1
+ The authority section of the last query response should be
+ """
+ example. 3600 IN SOA ns1.example. bugs.x.w.example. 1 3600 300 3600000 3600
+ example. 3600 IN RRSIG SOA 7 1 3600 20150420235959 20051021000000 40430 example. Hu25UIyNPmvPIVBrldN+9Mlp9Zql39qaUd8iq4ZLlYWfUUbbAS41pG+6 8z81q1xhkYAcEyHdVI2LmKusbZsT0Q==
+ 0p9mhaveqvm6t7vbl5lop2u3t2rp3tom.example. 3600 IN NSEC3 1 1 12 aabbccdd 2t7b4g4vsa5smi47k61mv5bv1a22bojr NS SOA MX RRSIG DNSKEY NSEC3PARAM
+ 0p9mhaveqvm6t7vbl5lop2u3t2rp3tom.example. 3600 IN RRSIG NSEC3 7 2 3600 20150420235959 20051021000000 40430 example. OSgWSm26B+cS+dDL8b5QrWr/dEWhtCsKlwKLIBHYH6blRxK9rC0bMJPw Q4mLIuw85H2EY762BOCXJZMnpuwhpA==
+ """
+
+ #
+ # Below are additional tests, not explicitely stated in RFC5155
+ #
+
+ # THIS TEST CURRENTLY FAILS: An NSEC3 record is added twice
+ # See ticket #1688
+ #Scenario: 7.2.2 other; Name Error where one NSEC3 covers multiple parts of proof (closest encloser)
+ # Given I have bind10 running with configuration nsec3/nsec3_auth.config
+ # A dnssec query for b.x.w.example. should have rcode NXDOMAIN
+ # The last query response should have flags qr aa rd
+ # The last query response should have edns_flags do
+ # The last query response should have ancount 0
+ # The last query response should have nscount 6
+ # The last query response should have adcount 1
+ # The authority section of the last query response should be
+ # """
+ # example. 3600 IN SOA ns1.example. bugs.x.w.example. 1 3600 300 3600000 3600
+ # example. 3600 IN RRSIG SOA 7 1 3600 20150420235959 20051021000000 40430 example. Hu25UIyNPmvPIVBrldN+9Mlp9Zql39qaUd8iq4ZLlYWfUUbbAS41pG+6 8z81q1xhkYAcEyHdVI2LmKusbZsT0Q==
+ # b4um86eghhds6nea196smvmlo4ors995.example. 3600 IN NSEC3 1 1 12 aabbccdd gjeqe526plbf1g8mklp59enfd789njgi MX RRSIG
+ # b4um86eghhds6nea196smvmlo4ors995.example. 3600 IN RRSIG NSEC3 7 2 3600 20150420235959 20051021000000 40430 example. ZkPG3M32lmoHM6pa3D6gZFGB/rhL//Bs3Omh5u4m/CUiwtblEVOaAKKZ d7S959OeiX43aLX3pOv0TSTyiTxIZg==
+ # 35mthgpgcu1qg68fab165klnsnk3dpvl.example. 3600 IN NSEC3 1 1 12 aabbccdd b4um86eghhds6nea196smvmlo4ors995 NS DS RRSIG
+ # 35mthgpgcu1qg68fab165klnsnk3dpvl.example. 3600 IN RRSIG NSEC3 7 2 3600 20150420235959 20051021000000 40430 example. g6jPUUpduAJKRljUsN8gB4UagAX0NxY9shwQAynzo8EUWH+z6hEIBlUT PGj15eZll6VhQqgZXtAIR3chwgW+SA==
+ # """
+
+ # THIS TEST CURRENTLY FAILS: An NSEC3 record is added twice
+ # See ticket #1688
+ #Scenario: 7.2.2 other; Name Error where one NSEC3 covers multiple parts of proof (wildcard)
+ # Given I have bind10 running with configuration nsec3/nsec3_auth.config
+ # A dnssec query for a.w.example. should have rcode NXDOMAIN
+ # The last query response should have flags qr aa rd
+ # The last query response should have edns_flags do
+ # The last query response should have ancount 0
+ # The last query response should have nscount 6
+ # The last query response should have adcount 1
+ # The authority section of the last query response should be
+ # """
+ # example. 3600 IN SOA ns1.example. bugs.x.w.example. 1 3600 300 3600000 3600
+ # example. 3600 IN RRSIG SOA 7 1 3600 20150420235959 20051021000000 40430 example. Hu25UIyNPmvPIVBrldN+9Mlp9Zql39qaUd8iq4ZLlYWfUUbbAS41pG+6 8z81q1xhkYAcEyHdVI2LmKusbZsT0Q==
+ # k8udemvp1j2f7eg6jebps17vp3n8i58h.example. 3600 IN NSEC3 1 1 12 AABBCCDD KOHAR7MBB8DC2CE8A9QVL8HON4K53UHI
+ # k8udemvp1j2f7eg6jebps17vp3n8i58h.example. 3600 IN RRSIG NSEC3 7 2 3600 20150420235959 20051021000000 40430 example. FtXGbvF0+wf8iWkyo73enAuVx03klN+pILBKS6qCcftVtfH4yVzsEZqu J27NHR7ruxJWDNMtOtx7w9WfcIg62A==
+ # r53bq7cc2uvmubfu5ocmm6pers9tk9en.example. 3600 IN NSEC3 1 1 12 AABBCCDD T644EBQK9BIBCNA874GIVR6JOJ62MLHV MX RRSIG
+ # r53bq7cc2uvmubfu5ocmm6pers9tk9en.example. 3600 IN RRSIG NSEC3 7 2 3600 20150420235959 20051021000000 40430 example. aupviViruXs4bDg9rCbezzBMf9h1ZlDvbW/CZFKulIGXXLj8B/fsDJar XVDA9bnUoRhEbKp+HF1FWKW7RIJdtQ==
+ # """
+
+ Scenario: Wildcard other: Wildcard name itself
+ Given I have bind10 running with configuration nsec3/nsec3_auth.config
+ A dnssec query for *.w.example. type MX should have rcode NOERROR
+ The last query response should have flags qr aa rd
+ The last query response should have edns_flags do
+ The last query response should have ancount 2
+ The last query response should have nscount 3
+ The last query response should have adcount 9
+ The answer section of the last query response should be
+ """
+ *.w.example. 3600 IN MX 1 ai.example.
+ *.w.example. 3600 IN RRSIG MX 7 2 3600 20150420235959 20051021000000 40430 example. CikebjQwGQPwijVcxgcZcSJKtfynugtlBiKb9FcBTrmOoyQ4InoWVudh CWsh/URX3lc4WRUMivEBP6+4KS3ldA==
+ """
+ The authority section of the last query response should be
+ """
+ example. 3600 IN NS ns1.example.
+ example. 3600 IN NS ns2.example.
+ example. 3600 IN RRSIG NS 7 1 3600 20150420235959 20051021000000 40430 example. PVOgtMK1HHeSTau+HwDWC8Ts+6C8qtqd4pQJqOtdEVgg+MA+ai4fWDEh u3qHJyLcQ9tbD2vvCnMXjtz6SyObxA==
+ """
+ The additional section of the last query response should be
+ """
+ ai.example. 3600 IN A 192.0.2.9
+ ai.example. 3600 IN AAAA 2001:db8::f00:baa9
+ ns1.example. 3600 IN A 192.0.2.1
+ ns2.example. 3600 IN A 192.0.2.2
+ ai.example. 3600 IN RRSIG A 7 2 3600 20150420235959 20051021000000 40430 example. hVe+wKYMlObTRPhX0NL67GxeZfdxqr/QeR6FtfdAj5+FgYxyzPEjIzvK Wy00hWIl6wD3Vws+rznEn8sQ64UdqA==
+ ai.example. 3600 IN RRSIG AAAA 7 2 3600 20150420235959 20051021000000 40430 example. LcdxKaCB5bGZwPDg+3JJ4O02zoMBrjxqlf6WuaHQZZfTUpb9Nf2nxFGe 2XRPfR5tpJT6GdRGcHueLuXkMjBArQ==
+ ns1.example. 3600 IN RRSIG A 7 2 3600 20150420235959 20051021000000 40430 example. bu6kx73n6XEunoVGuRfAgY7EF/AJqHy7hj0jkiqJjB0dOrx3wuz9SaBe GfqWIdn/uta3SavN4FRvZR9SCFHF5Q==
+ ns2.example. 3600 IN RRSIG A 7 2 3600 20150420235959 20051021000000 40430 example. ktQ3TqE0CfRfki0Rb/Ip5BM0VnxelbuejCC4zpLbFKA/7eD7UNAwxMgx JPtbdST+syjYSJaj4IHfeX6n8vfoGA==
+ """
+
+ Scenario: Wildcard other: Wildcard name itself nodata
+ Given I have bind10 running with configuration nsec3/nsec3_auth.config
+ A dnssec query for *.w.example. type A should have rcode NOERROR
+ The last query response should have flags qr aa rd
+ The last query response should have edns_flags do
+ The last query response should have ancount 0
+ The last query response should have nscount 4
+ The last query response should have adcount 1
+ The authority section of the last query response should be
+ """
+ example. 3600 IN SOA ns1.example. bugs.x.w.example. 1 3600 300 3600000 3600
+ example. 3600 IN RRSIG SOA 7 1 3600 20150420235959 20051021000000 40430 example. Hu25UIyNPmvPIVBrldN+9Mlp9Zql39qaUd8iq4ZLlYWfUUbbAS41pG+6 8z81q1xhkYAcEyHdVI2LmKusbZsT0Q==
+ r53bq7cc2uvmubfu5ocmm6pers9tk9en.example. 3600 IN NSEC3 1 1 12 AABBCCDD T644EBQK9BIBCNA874GIVR6JOJ62MLHV MX RRSIG
+ r53bq7cc2uvmubfu5ocmm6pers9tk9en.example. 3600 IN RRSIG NSEC3 7 2 3600 20150420235959 20051021000000 40430 example. aupviViruXs4bDg9rCbezzBMf9h1ZlDvbW/CZFKulIGXXLj8B/fsDJar XVDA9bnUoRhEbKp+HF1FWKW7RIJdtQ==
+ """
+
+ Scenario: Direct query for NSEC3 record
+ Given I have bind10 running with configuration nsec3/nsec3_auth.config
+ A dnssec query for 0p9mhaveqvm6t7vbl5lop2u3t2rp3tom.example. type NSEC3 should have rcode NXDOMAIN
+ The last query response should have flags qr aa rd
+ The last query response should have edns_flags do
+ The last query response should have ancount 0
+ The last query response should have nscount 8
+ The last query response should have adcount 1
+ The authority section of the last query response should be
+ """
+ example. 3600 IN SOA ns1.example. bugs.x.w.example. 1 3600 300 3600000 3600
+ example. 3600 IN RRSIG SOA 7 1 3600 20150420235959 20051021000000 40430 example. Hu25UIyNPmvPIVBrldN+9Mlp9Zql39qaUd8iq4ZLlYWfUUbbAS41pG+6 8z81q1xhkYAcEyHdVI2LmKusbZsT0Q==
+ 0p9mhaveqvm6t7vbl5lop2u3t2rp3tom.example. 3600 IN NSEC3 1 1 12 AABBCCDD 2T7B4G4VSA5SMI47K61MV5BV1A22BOJR NS SOA MX RRSIG DNSKEY NSEC3PARAM
+ 0p9mhaveqvm6t7vbl5lop2u3t2rp3tom.example. 3600 IN RRSIG NSEC3 7 2 3600 20150420235959 20051021000000 40430 example. OSgWSm26B+cS+dDL8b5QrWr/dEWhtCsKlwKLIBHYH6blRxK9rC0bMJPw Q4mLIuw85H2EY762BOCXJZMnpuwhpA==
+ q04jkcevqvmu85r014c7dkba38o0ji5r.example. 3600 IN NSEC3 1 1 12 AABBCCDD R53BQ7CC2UVMUBFU5OCMM6PERS9TK9EN A RRSIG
+ q04jkcevqvmu85r014c7dkba38o0ji5r.example. 3600 IN RRSIG NSEC3 7 2 3600 20150420235959 20051021000000 40430 example. hV5I89b+4FHJDATp09g4bbN0R1F845CaXpL3ZxlMKimoPAyqletMlEWw LfFia7sdpSzn+ZlNNlkxWcLsIlMmUg==
+ gjeqe526plbf1g8mklp59enfd789njgi.example. 3600 IN NSEC3 1 1 12 AABBCCDD JI6NEOAEPV8B5O6K4EV33ABHA8HT9FGC A HINFO AAAA RRSIG
+ gjeqe526plbf1g8mklp59enfd789njgi.example. 3600 IN RRSIG NSEC3 7 2 3600 20150420235959 20051021000000 40430 example. IVnezTJ9iqblFF97vPSmfXZ5Zozngx3KX3byLTZC4QBH2dFWhf6scrGF ZB980AfCxoD9qbbKDy+rdGIeRSVNyw==
+ """
+
+ Scenario: No data, type DS, in-zone
+ Given I have bind10 running with configuration nsec3/nsec3_auth.config
+ A dnssec query for ai.example. type DS should have rcode NOERROR
+ The last query response should have flags qr aa rd
+ The last query response should have edns_flags do
+ The last query response should have ancount 0
+ The last query response should have nscount 4
+ The last query response should have adcount 1
+ The authority section of the last query response should be
+ """
+ example. 3600 IN SOA ns1.example. bugs.x.w.example. 1 3600 300 3600000 3600
+ example. 3600 IN RRSIG SOA 7 1 3600 20150420235959 20051021000000 40430 example. Hu25UIyNPmvPIVBrldN+9Mlp9Zql39qaUd8iq4ZLlYWfUUbbAS41pG+6 8z81q1xhkYAcEyHdVI2LmKusbZsT0Q==
+ gjeqe526plbf1g8mklp59enfd789njgi.example. 3600 IN NSEC3 1 1 12 AABBCCDD JI6NEOAEPV8B5O6K4EV33ABHA8HT9FGC A HINFO AAAA RRSIG
+ gjeqe526plbf1g8mklp59enfd789njgi.example. 3600 IN RRSIG NSEC3 7 2 3600 20150420235959 20051021000000 40430 example. IVnezTJ9iqblFF97vPSmfXZ5Zozngx3KX3byLTZC4QBH2dFWhf6scrGF ZB980AfCxoD9qbbKDy+rdGIeRSVNyw==
+ """
+
+ Scenario: No data, type DS, optout delegation
+ Given I have bind10 running with configuration nsec3/nsec3_auth.config
+ A dnssec query for c.example. type DS should have rcode NOERROR
+ The last query response should have flags qr aa rd
+ The last query response should have edns_flags do
+ The last query response should have ancount 0
+ The last query response should have nscount 6
+ The last query response should have adcount 1
+ The authority section of the last query response should be
+ """
+ example. 3600 IN SOA ns1.example. bugs.x.w.example. 1 3600 300 3600000 3600
+ example. 3600 IN RRSIG SOA 7 1 3600 20150420235959 20051021000000 40430 example. Hu25UIyNPmvPIVBrldN+9Mlp9Zql39qaUd8iq4ZLlYWfUUbbAS41pG+6 8z81q1xhkYAcEyHdVI2LmKusbZsT0Q==
+ 0p9mhaveqvm6t7vbl5lop2u3t2rp3tom.example. 3600 IN NSEC3 1 1 12 AABBCCDD 2T7B4G4VSA5SMI47K61MV5BV1A22BOJR NS SOA MX RRSIG DNSKEY NSEC3PARAM
+ 0p9mhaveqvm6t7vbl5lop2u3t2rp3tom.example. 3600 IN RRSIG NSEC3 7 2 3600 20150420235959 20051021000000 40430 example. OSgWSm26B+cS+dDL8b5QrWr/dEWhtCsKlwKLIBHYH6blRxK9rC0bMJPw Q4mLIuw85H2EY762BOCXJZMnpuwhpA==
+ 35mthgpgcu1qg68fab165klnsnk3dpvl.example. 3600 IN NSEC3 1 1 12 AABBCCDD B4UM86EGHHDS6NEA196SMVMLO4ORS995 NS DS RRSIG
+ 35mthgpgcu1qg68fab165klnsnk3dpvl.example. 3600 IN RRSIG NSEC3 7 2 3600 20150420235959 20051021000000 40430 example. g6jPUUpduAJKRljUsN8gB4UagAX0NxY9shwQAynzo8EUWH+z6hEIBlUT PGj15eZll6VhQqgZXtAIR3chwgW+SA==
+ """
diff --git a/tests/lettuce/features/terrain/bind10_control.py b/tests/lettuce/features/terrain/bind10_control.py
index fdc419b..b2a367c 100644
--- a/tests/lettuce/features/terrain/bind10_control.py
+++ b/tests/lettuce/features/terrain/bind10_control.py
@@ -16,6 +16,7 @@
from lettuce import *
import subprocess
import re
+import json
@step('start bind10(?: with configuration (\S+))?' +\
'(?: with cmdctl port (\d+))?' +\
@@ -112,8 +113,140 @@ def have_bind10_running(step, config_file, cmdctl_port, process_name):
step.given(start_step)
step.given(wait_step)
+# function to send lines to bindctl, and store the result
+def run_bindctl(commands, cmdctl_port=None):
+ """Run bindctl.
+ Parameters:
+ commands: a sequence of strings which will be sent.
+ cmdctl_port: a port number on which cmdctl is listening, is converted
+ to string if necessary. If not provided, or None, defaults
+ to 47805
+
+ bindctl's stdout and stderr streams are stored (as one multiline string
+ in world.last_bindctl_stdout/stderr.
+ Fails if the return code is not 0
+ """
+ if cmdctl_port is None:
+ cmdctl_port = 47805
+ args = ['bindctl', '-p', str(cmdctl_port)]
+ bindctl = subprocess.Popen(args, 1, None, subprocess.PIPE,
+ subprocess.PIPE, None)
+ for line in commands:
+ bindctl.stdin.write(line + "\n")
+ (stdout, stderr) = bindctl.communicate()
+ result = bindctl.returncode
+ world.last_bindctl_stdout = stdout
+ world.last_bindctl_stderr = stderr
+ assert result == 0, "bindctl exit code: " + str(result) +\
+ "\nstdout:\n" + str(stdout) +\
+ "stderr:\n" + str(stderr)
+
+
+ at step('last bindctl( stderr)? output should( not)? contain (\S+)')
+def check_bindctl_output(step, stderr, notv, string):
+ """Checks the stdout (or stderr) stream of the last run of bindctl,
+ fails if the given string is not found in it (or fails if 'not' was
+ set and it is found
+ Parameters:
+ stderr ('stderr'): Check stderr instead of stdout output
+ notv ('not'): reverse the check (fail if string is found)
+ string ('contain <string>') string to look for
+ """
+ if stderr is None:
+ output = world.last_bindctl_stdout
+ else:
+ output = world.last_bindctl_stderr
+ found = False
+ if string in output:
+ found = True
+ if notv is None:
+ assert found == True, "'" + string +\
+ "' was not found in bindctl output:\n" +\
+ output
+ else:
+ assert not found, "'" + string +\
+ "' was found in bindctl output:\n" +\
+ output
+
+def parse_bindctl_output_as_data_structure():
+ """Helper function for data-related command tests: evaluates the
+ last output of bindctl as a data structure that can then be
+ inspected.
+ If the bindctl output is not valid (json) data, this call will
+ fail with an assertion failure.
+ If it is valid, it is parsed and returned as whatever data
+ structure it represented.
+ """
+ # strip any extra output after a charater that commonly terminates a valid
+ # JSON expression, i.e., ']', '}' and '"'. (The extra output would
+ # contain 'Exit from bindctl' message, and depending on environment some
+ # other control-like characters...but why is this message even there?)
+ # Note that this filter is not perfect. For example, it cannot recognize
+ # a simple expression of true/false/null.
+ output = re.sub("(.*)([^]}\"]*$)", r"\1", world.last_bindctl_stdout)
+ try:
+ return json.loads(output)
+ except ValueError as ve:
+ assert False, "Last bindctl output does not appear to be a " +\
+ "parseable data structure: '" + output + "': " + str(ve)
+
+def find_process_pid(step, process_name):
+ """Helper function to request the running processes from Boss, and
+ return the pid of the process with the given process_name.
+ Fails with an assert if the response from boss is not valid JSON,
+ or if the process with the given name is not found.
+ """
+ # show_processes output is a list of lists, where the inner lists
+ # are of the form [ pid, "name" ]
+ # Not checking data form; errors will show anyway (if these turn
+ # out to be too vague, we can change this)
+ step.given('send bind10 the command Boss show_processes')
+ running_processes = parse_bindctl_output_as_data_structure()
+
+ for process in running_processes:
+ if process[1] == process_name:
+ return process[0]
+ assert False, "Process named " + process_name +\
+ " not found in output of Boss show_processes";
+
+ at step("remember the pid of process ([\S]+)")
+def remember_pid(step, process_name):
+ """Stores the PID of the process with the given name as returned by
+ Boss show_processes command.
+ Fails if the process with the given name does not appear to exist.
+ Stores the component_name->pid value in the dict world.process_pids.
+ This should only be used by the related step
+ 'the pid of process <name> should (not) have changed'
+ Arguments:
+ process name ('process <name>') the name of the component to store
+ the pid of.
+ """
+ if world.process_pids is None:
+ world.process_pids = {}
+ world.process_pids[process_name] = find_process_pid(step, process_name)
+
+ at step('pid of process ([\S]+) should not have changed')
+def check_pid(step, process_name):
+ """Checks the PID of the process with the given name as returned by
+ Boss show_processes command.
+ Fails if the process with the given name does not appear to exist.
+ Fails if the process with the given name exists, but has a different
+ pid than it had when the step 'remember the pid of process' was
+ called.
+ Fails if that step has not been called (since world.process_pids
+ does not exist).
+ """
+ assert world.process_pids is not None, "No process pids stored"
+ assert process_name in world.process_pids, "Process named " +\
+ process_name +\
+ " was not stored"
+ pid = find_process_pid(step, process_name)
+ assert world.process_pids[process_name] == pid,\
+ "Expected pid: " + str(world.process_pids[process_name]) +\
+ " Got pid: " + str(pid)
+
@step('set bind10 configuration (\S+) to (.*)(?: with cmdctl port (\d+))?')
-def set_config_command(step, name, value, cmdctl_port):
+def config_set_command(step, name, value, cmdctl_port):
"""
Run bindctl, set the given configuration to the given value, and commit it.
Parameters:
@@ -123,16 +256,44 @@ def set_config_command(step, name, value, cmdctl_port):
the command to. Defaults to 47805.
Fails if cmdctl does not exit with status code 0.
"""
- if cmdctl_port is None:
- cmdctl_port = '47805'
- args = ['bindctl', '-p', cmdctl_port]
- bindctl = subprocess.Popen(args, 1, None, subprocess.PIPE,
- subprocess.PIPE, None)
- bindctl.stdin.write("config set " + name + " " + value + "\n")
- bindctl.stdin.write("config commit\n")
- bindctl.stdin.write("quit\n")
- result = bindctl.wait()
- assert result == 0, "bindctl exit code: " + str(result)
+ commands = ["config set " + name + " " + value,
+ "config commit",
+ "quit"]
+ run_bindctl(commands, cmdctl_port)
+
+ at step('send bind10 the following commands(?: with cmdctl port (\d+))?')
+def send_multiple_commands(step, cmdctl_port):
+ """
+ Run bindctl, and send it the given multiline set of commands.
+ A quit command is always appended.
+ cmdctl_port ('with cmdctl port <portnr>', optional): cmdctl port to send
+ the command to. Defaults to 47805.
+ Fails if cmdctl does not exit with status code 0.
+ """
+ commands = step.multiline.split("\n")
+ # Always add quit
+ commands.append("quit")
+ run_bindctl(commands, cmdctl_port)
+
+ at step('remove bind10 configuration (\S+)(?: value (\S+))?(?: with cmdctl port (\d+))?')
+def config_remove_command(step, name, value, cmdctl_port):
+ """
+ Run bindctl, remove the given configuration item, and commit it.
+ Parameters:
+ name ('configuration <name>'): Identifier of the configuration to remove
+ value ('value <value>'): if name is a named set, use value to identify
+ item to remove
+ cmdctl_port ('with cmdctl port <portnr>', optional): cmdctl port to send
+ the command to. Defaults to 47805.
+ Fails if cmdctl does not exit with status code 0.
+ """
+ cmd = "config remove " + name
+ if value is not None:
+ cmd = cmd + " " + value
+ commands = [cmd,
+ "config commit",
+ "quit"]
+ run_bindctl(commands, cmdctl_port)
@step('send bind10 the command (.+)(?: with cmdctl port (\d+))?')
def send_command(step, command, cmdctl_port):
@@ -144,15 +305,21 @@ def send_command(step, command, cmdctl_port):
the command to. Defaults to 47805.
Fails if cmdctl does not exit with status code 0.
"""
- if cmdctl_port is None:
- cmdctl_port = '47805'
- args = ['bindctl', '-p', cmdctl_port]
- bindctl = subprocess.Popen(args, 1, None, subprocess.PIPE,
- subprocess.PIPE, None)
- bindctl.stdin.write(command + "\n")
- bindctl.stdin.write("quit\n")
- (stdout, stderr) = bindctl.communicate()
- result = bindctl.returncode
- assert result == 0, "bindctl exit code: " + str(result) +\
- "\nstdout:\n" + str(stdout) +\
- "stderr:\n" + str(stderr)
+ commands = [command,
+ "quit"]
+ run_bindctl(commands, cmdctl_port)
+
+ at step('bind10 module (\S+) should( not)? be running')
+def module_is_running(step, name, not_str):
+ """
+ Convenience step to check if a module is running; can only work with
+ default cmdctl port; sends a 'help' command with bindctl, then
+ checks if the output contains the given name.
+ Parameters:
+ name ('module <name>'): The name of the module (case sensitive!)
+ not ('not'): Reverse the check (fail if it is running)
+ """
+ if not_str is None:
+ not_str = ""
+ step.given('send bind10 the command help')
+ step.given('last bindctl output should' + not_str + ' contain ' + name)
diff --git a/tests/lettuce/features/terrain/querying.py b/tests/lettuce/features/terrain/querying.py
index 51c158e..c070dd2 100644
--- a/tests/lettuce/features/terrain/querying.py
+++ b/tests/lettuce/features/terrain/querying.py
@@ -41,9 +41,10 @@ import re
#
# The following attributes are 'parsed' from the response, all as strings,
# and end up as direct attributes of the QueryResult object:
-# opcode, rcode, id, flags, qdcount, ancount, nscount, adcount
-# (flags is one string with all flags, in the order they appear in the
-# response packet.)
+# opcode, rcode, id, flags, qdcount, ancount, nscount, adcount,
+# edns_version, edns_flags, and edns_udp_size
+# (flags and edns_flags are both one string with all flags, in the order
+# in which they appear in the response message.)
#
# this will set 'rcode' as the result code, we 'define' one additional
# rcode, "NO_ANSWER", if the dig process returned an error code itself
@@ -55,10 +56,12 @@ import re
# See server_from_sqlite3.feature for various examples to perform queries
class QueryResult(object):
status_re = re.compile("opcode: ([A-Z])+, status: ([A-Z]+), id: ([0-9]+)")
+ edns_re = re.compile("; EDNS: version: ([0-9]+), flags: ([a-z ]*); udp: ([0-9]+)")
flags_re = re.compile("flags: ([a-z ]+); QUERY: ([0-9]+), ANSWER: " +
"([0-9]+), AUTHORITY: ([0-9]+), ADDITIONAL: ([0-9]+)")
- def __init__(self, name, qtype, qclass, address, port):
+ def __init__(self, name, qtype, qclass, address, port,
+ additional_args=None):
"""
Constructor. This fires of a query using dig.
Parameters:
@@ -67,6 +70,7 @@ class QueryResult(object):
qclass: The RR class to query. Defaults to IN if it is None.
address: The IP adress to send the query to.
port: The port number to send the query to.
+ additional_args: List of additional arguments (e.g. '+dnssec').
All parameters must be either strings or have the correct string
representation.
Only one query attempt will be made.
@@ -78,6 +82,8 @@ class QueryResult(object):
if qclass is not None:
args.append('-c')
args.append(str(qclass))
+ if additional_args is not None:
+ args.extend(additional_args)
args.append(name)
dig_process = subprocess.Popen(args, 1, None, None, subprocess.PIPE,
None)
@@ -102,6 +108,8 @@ class QueryResult(object):
"""
if line == ";; ANSWER SECTION:\n":
self.line_handler = self.parse_answer
+ elif line == ";; OPT PSEUDOSECTION:\n":
+ self.line_handler = self.parse_opt
elif line == ";; AUTHORITY SECTION:\n":
self.line_handler = self.parse_authority
elif line == ";; ADDITIONAL SECTION:\n":
@@ -131,6 +139,19 @@ class QueryResult(object):
self.nscount = flags_match.group(4)
self.adcount = flags_match.group(5)
+ def parse_opt(self, line):
+ """
+ Parse the header lines of the query response.
+ Parameters:
+ line: The current line of the response.
+ """
+ if not self._check_next_header(line):
+ edns_match = self.edns_re.search(line)
+ if edns_match is not None:
+ self.edns_version = edns_match.group(1)
+ self.edns_flags = edns_match.group(2)
+ self.edns_udp_size = edns_match.group(3)
+
def parse_question(self, line):
"""
Parse the question section lines of the query response.
@@ -179,9 +200,10 @@ class QueryResult(object):
"""
pass
- at step('A query for ([\w.-]+) (?:type ([A-Z0-9]+) )?(?:class ([A-Z]+) )?' +
- '(?:to ([^:]+)(?::([0-9]+))? )?should have rcode ([\w.]+)')
-def query(step, query_name, qtype, qclass, addr, port, rcode):
+ at step('A (dnssec )?query for ([\S]+) (?:type ([A-Z0-9]+) )?' +
+ '(?:class ([A-Z]+) )?(?:to ([^:]+)(?::([0-9]+))? )?' +
+ 'should have rcode ([\w.]+)')
+def query(step, dnssec, query_name, qtype, qclass, addr, port, rcode):
"""
Run a query, check the rcode of the response, and store the query
result in world.last_query_result.
@@ -203,7 +225,11 @@ def query(step, query_name, qtype, qclass, addr, port, rcode):
addr = "127.0.0.1"
if port is None:
port = 47806
- query_result = QueryResult(query_name, qtype, qclass, addr, port)
+ additional_arguments = []
+ if dnssec is not None:
+ additional_arguments.append("+dnssec")
+ query_result = QueryResult(query_name, qtype, qclass, addr, port,
+ additional_arguments)
assert query_result.rcode == rcode,\
"Expected: " + rcode + ", got " + query_result.rcode
world.last_query_result = query_result
@@ -255,9 +281,15 @@ def check_last_query_section(step, section):
section ('<section> section'): The name of the section (QUESTION, ANSWER,
AUTHORITY or ADDITIONAL).
The expected response is taken from the multiline part of the step in the
- scenario. Differing whitespace is ignored, but currently the order is
- significant.
+ scenario. Differing whitespace is ignored, the order of the lines is
+ ignored, and the comparison is case insensitive.
Fails if they do not match.
+ WARNING: Case insensitivity is not strictly correct; for instance the
+ data of TXT RRs would be case sensitive. But most other output is, so
+ currently the checks are always case insensitive. Should we decide
+ these checks do need to be case sensitive, we can either remove it
+ or make it optional (for the former, we'll need to update a number of
+ tests).
"""
response_string = None
if section.lower() == 'question':
@@ -265,15 +297,32 @@ def check_last_query_section(step, section):
elif section.lower() == 'answer':
response_string = "\n".join(world.last_query_result.answer_section)
elif section.lower() == 'authority':
- response_string = "\n".join(world.last_query_result.answer_section)
+ response_string = "\n".join(world.last_query_result.authority_section)
elif section.lower() == 'additional':
- response_string = "\n".join(world.last_query_result.answer_section)
+ response_string = "\n".join(world.last_query_result.additional_section)
else:
assert False, "Unknown section " + section
+
+ # Now mangle the data for 'conformance'
+ # This could be done more efficiently, but is done one
+ # by one on a copy of the original data, so it is clear
+ # what is done. Final error output is currently still the
+ # original unchanged multiline strings
+
# replace whitespace of any length by one space
response_string = re.sub("[ \t]+", " ", response_string)
expect = re.sub("[ \t]+", " ", step.multiline)
+ # lowercase them
+ response_string = response_string.lower()
+ expect = expect.lower()
+ # sort them
+ response_string_parts = response_string.split("\n")
+ response_string_parts.sort()
+ response_string = "\n".join(response_string_parts)
+ expect_parts = expect.split("\n")
+ expect_parts.sort()
+ expect = "\n".join(expect_parts)
+
assert response_string.strip() == expect.strip(),\
"Got:\n'" + response_string + "'\nExpected:\n'" + step.multiline +"'"
-
-
+
diff --git a/tests/lettuce/features/terrain/terrain.py b/tests/lettuce/features/terrain/terrain.py
index 90531a1..2bfddd6 100644
--- a/tests/lettuce/features/terrain/terrain.py
+++ b/tests/lettuce/features/terrain/terrain.py
@@ -45,7 +45,9 @@ copylist = [
["configurations/example.org.config.orig",
"configurations/example.org.config"],
["configurations/resolver/resolver_basic.config.orig",
- "configurations/resolver/resolver_basic.config"]
+ "configurations/resolver/resolver_basic.config"],
+ ["configurations/multi_instance/multi_auth.config.orig",
+ "configurations/multi_instance/multi_auth.config"]
]
# This is a list of files that, if present, will be removed before a scenario
@@ -343,6 +345,10 @@ def initialize(scenario):
# Convenience variable to access the last query result from querying.py
world.last_query_result = None
+ # For slightly better errors, initialize a process_pids for the relevant
+ # steps
+ world.process_pids = None
+
# Some tests can modify the settings. If the tests fail half-way, or
# don't clean up, this can leave configurations or data in a bad state,
# so we copy them from originals before each scenario
diff --git a/tests/lettuce/features/xfrin_bind10.feature b/tests/lettuce/features/xfrin_bind10.feature
index 70c3571..8bc6e5e 100644
--- a/tests/lettuce/features/xfrin_bind10.feature
+++ b/tests/lettuce/features/xfrin_bind10.feature
@@ -7,5 +7,5 @@ Feature: Xfrin
A query for www.example.org should have rcode REFUSED
Wait for bind10 stderr message CMDCTL_STARTED
When I send bind10 the command Xfrin retransfer example.org IN 127.0.0.1 47807
- Then wait for new bind10 stderr message XFRIN_XFR_TRANSFER_SUCCESS not XFRIN_XFR_PROCESS_FAILURE
+ Then wait for new bind10 stderr message XFRIN_TRANSFER_SUCCESS not XFRIN_XFR_PROCESS_FAILURE
A query for www.example.org should have rcode NOERROR
diff --git a/tests/system/.gitignore b/tests/system/.gitignore
new file mode 100644
index 0000000..76f87fe
--- /dev/null
+++ b/tests/system/.gitignore
@@ -0,0 +1,2 @@
+/conf.sh
+/run.sh
diff --git a/tests/system/bindctl/nsx1/.gitignore b/tests/system/bindctl/nsx1/.gitignore
new file mode 100644
index 0000000..84432f2
--- /dev/null
+++ b/tests/system/bindctl/nsx1/.gitignore
@@ -0,0 +1 @@
+/b10-config.db.template
diff --git a/tests/system/bindctl/nsx1/b10-config.db.template.in b/tests/system/bindctl/nsx1/b10-config.db.template.in
index 162329a..7a3647c 100644
--- a/tests/system/bindctl/nsx1/b10-config.db.template.in
+++ b/tests/system/bindctl/nsx1/b10-config.db.template.in
@@ -3,8 +3,5 @@
"listen_on": [{"address": "10.53.0.1", "port": 53210}],
"database_file": "@abs_builddir@/zone.sqlite3",
"statistics-interval": 1
- },
- "Xfrout": {
- "log_file": "@abs_builddir@/Xfrout.log"
}
}
diff --git a/tests/system/glue/.gitignore b/tests/system/glue/.gitignore
new file mode 100644
index 0000000..87e08bf
--- /dev/null
+++ b/tests/system/glue/.gitignore
@@ -0,0 +1 @@
+/setup.sh
diff --git a/tests/system/glue/nsx1/.gitignore b/tests/system/glue/nsx1/.gitignore
new file mode 100644
index 0000000..1c67281
--- /dev/null
+++ b/tests/system/glue/nsx1/.gitignore
@@ -0,0 +1 @@
+/b10-config.db
diff --git a/tests/system/glue/nsx1/b10-config.db.in b/tests/system/glue/nsx1/b10-config.db.in
index acd040c..0d5a324 100644
--- a/tests/system/glue/nsx1/b10-config.db.in
+++ b/tests/system/glue/nsx1/b10-config.db.in
@@ -2,8 +2,5 @@
"Auth": {
"listen_on": [{"address": "10.53.0.1", "port": 53210}],
"database_file": "@abs_builddir@/zone.sqlite3"
- },
- "Xfrout": {
- "log_file": "@abs_builddir@/Xfrout.log"
}
}
diff --git a/tests/system/ixfr/.gitignore b/tests/system/ixfr/.gitignore
new file mode 100644
index 0000000..027d45e
--- /dev/null
+++ b/tests/system/ixfr/.gitignore
@@ -0,0 +1,8 @@
+/b10-config.db
+/common_tests.sh
+/db.example.n0
+/db.example.n2
+/db.example.n2.refresh
+/db.example.n4
+/db.example.n6
+/ixfr_init.sh
diff --git a/tests/system/ixfr/in-1/.gitignore b/tests/system/ixfr/in-1/.gitignore
new file mode 100644
index 0000000..87e08bf
--- /dev/null
+++ b/tests/system/ixfr/in-1/.gitignore
@@ -0,0 +1 @@
+/setup.sh
diff --git a/tests/system/ixfr/in-2/.gitignore b/tests/system/ixfr/in-2/.gitignore
new file mode 100644
index 0000000..87e08bf
--- /dev/null
+++ b/tests/system/ixfr/in-2/.gitignore
@@ -0,0 +1 @@
+/setup.sh
diff --git a/tests/system/ixfr/in-2/tests.sh b/tests/system/ixfr/in-2/tests.sh
index 7b1e2a8..3050713 100644
--- a/tests/system/ixfr/in-2/tests.sh
+++ b/tests/system/ixfr/in-2/tests.sh
@@ -54,7 +54,7 @@ then
exit 1
fi
-grep XFRIN_XFR_TRANSFER_SUCCESS nsx2/bind10.run | grep IXFR > /dev/null
+grep XFRIN_IXFR_TRANSFER_SUCCESS nsx2/bind10.run | grep IXFR > /dev/null
if [ $? -ne 0 ];
then
echo "R:$CLIENT_NAME FAIL no 'IXFR successful' message in the BIND 10 log"
diff --git a/tests/system/ixfr/in-3/.gitignore b/tests/system/ixfr/in-3/.gitignore
new file mode 100644
index 0000000..87e08bf
--- /dev/null
+++ b/tests/system/ixfr/in-3/.gitignore
@@ -0,0 +1 @@
+/setup.sh
diff --git a/tests/system/ixfr/in-4/.gitignore b/tests/system/ixfr/in-4/.gitignore
new file mode 100644
index 0000000..87e08bf
--- /dev/null
+++ b/tests/system/ixfr/in-4/.gitignore
@@ -0,0 +1 @@
+/setup.sh
diff --git a/tests/tools/badpacket/.gitignore b/tests/tools/badpacket/.gitignore
new file mode 100644
index 0000000..ad6c1e6
--- /dev/null
+++ b/tests/tools/badpacket/.gitignore
@@ -0,0 +1 @@
+/badpacket
diff --git a/tests/tools/badpacket/scan.cc b/tests/tools/badpacket/scan.cc
index a6e7229..1559a1f 100644
--- a/tests/tools/badpacket/scan.cc
+++ b/tests/tools/badpacket/scan.cc
@@ -61,7 +61,8 @@ Scan::scan(const CommandOptions& options) {
RRType::A()));
OutputBufferPtr msgbuf(new OutputBuffer(512));
- MessageRenderer renderer(*msgbuf);
+ MessageRenderer renderer;
+ renderer.setBuffer(msgbuf.get());
message.toWire(renderer);
iterateFlagsStart(msgbuf, options);
diff --git a/tests/tools/badpacket/tests/.gitignore b/tests/tools/badpacket/tests/.gitignore
new file mode 100644
index 0000000..d6d1ec8
--- /dev/null
+++ b/tests/tools/badpacket/tests/.gitignore
@@ -0,0 +1 @@
+/run_unittests
diff --git a/tests/tools/perfdhcp/.gitignore b/tests/tools/perfdhcp/.gitignore
new file mode 100644
index 0000000..1a8375a
--- /dev/null
+++ b/tests/tools/perfdhcp/.gitignore
@@ -0,0 +1 @@
+/perfdhcp
More information about the bind10-changes
mailing list