BIND 10 trac1704, updated. 8801ca7c8d5e612629fbe7425f5c9005dd87bcfb [1704] Use a lock file for mutual exclusion when printing log messages
BIND 10 source code commits
bind10-changes at lists.isc.org
Thu May 17 07:45:34 UTC 2012
The branch, trac1704 has been updated
via 8801ca7c8d5e612629fbe7425f5c9005dd87bcfb (commit)
via 0ef581a23047183b6908f80d59c7457b7035849f (commit)
via 953ca1293459bdc2bf1026e7d9b9f85310c9d82f (commit)
via 35803b41bb858ed41b8bea1a27d499484b2d48ce (commit)
via 8bdcbf99f497f4cae2d0344e06f8f65824706642 (commit)
via e6eb21d556b9d4cf2c689d6ea53fc214b61b716b (commit)
via 8cbcf4941b9c521d1bf9f77ae052c474e988b3a9 (commit)
via f8ec7011e8978fcf5b6e297811e34112b969f36d (commit)
via 0106b037b838868c5719c3635702ce899fa937c9 (commit)
via 6eeddeaa3cb86fb31ec5f98a1e179a114ea27c84 (commit)
via 10c00f0507b9b25b34e41d45b3347c581269e5ca (commit)
via 7bce49524b48bfb30e0f49f21765fd59f95c78a8 (commit)
via 6499662b418e31ef897f541ebafa340b1049c5aa (commit)
via ad069fe61cb441326b08e0f5bb1e84ae47bbeb7d (commit)
via 419648870c84855008cd66e0870137878df4e730 (commit)
via 6313b727c2f978e4d4775a32c08e49bb3afc1bf8 (commit)
via b1e8bf0d957426abf9aacaa992e966ef0c6aac81 (commit)
via c7a5a56e8269ad841c1b43a635fc3c49ea254638 (commit)
via 39b658ef4f5b272f727eb501d68caa30d9a5ebc3 (commit)
via e401615079a02327a9803abc01ab0cb2e36000fb (commit)
via 94a2da0d06d02d1d5991e0f8d6cb19bed1e5d665 (commit)
via 3692c48386327df01b447308fb39ca748c451f47 (commit)
via 74ac6abb7e9bdf613d5aa6c93426ff7bf9f671ad (commit)
via a807c0d3ef2c845efdc1dae050cf52f951c21cbf (commit)
via ce7d1aef2ca88084e4dacef97132337dd3e50d6c (commit)
via ce461b1b1bb0bc412e92ffc41365cb86d5544736 (commit)
via e876441fa25c919d3e91947b5ffc406f444ca060 (commit)
via dd7517989045bc0f434fbed9eb1944421c430835 (commit)
via 3bed90aa7b7e979b6e4d1f9bdb06ad71139a3462 (commit)
via bc9d78dae2780eb90d67f8593f7c8fbad202d4ca (commit)
via 5b0c9e4cb9de6ead5b73af9c64bf4abb6e65c86f (commit)
via 0d03b06138e080cc0391fb912a5a5e75f0f97cec (commit)
via f987a99a35de4596af2021d441698f8425298663 (commit)
via d872878ef5d5e6ccdd03803a76a8162dabab1147 (commit)
via b685e7c4e4fdfde574993839eb00d9e001aad379 (commit)
via ffdc8bef2778bd4aa1c69120cd759e4a9787b34c (commit)
via 6d2d11d4bd5ba585f773309760b539fc6da64f3f (commit)
via 286753919453c62ec150306ce6bdc1f5f5fe89d9 (commit)
via 95b852252c2d8e81b3235246f3e8ee63b6943afb (commit)
via 631dc5b98750e2ffb1f53845b7f4888d894c9354 (commit)
via d05c3695d4b198fbb6bc0e66fa8a2b18db098527 (commit)
via 33d21231741cb9dbacfdfe3fe3a09d2919464fbc (commit)
via 5de8f5172aa4becbf65c6918095d23d08bc9d19c (commit)
via 613641bd80c49e8094efbbf809623154804adcfa (commit)
via 9a9348605260eba1ff3ee2cbfd58d1f07779414e (commit)
via d59c24a32a58bd5edf37d32faccff6cdfc362f9d (commit)
via ab86972df562a452de633cb13403f0ed8eb00510 (commit)
via 84f5946ae2885cae3412fe2b5855c0d954796877 (commit)
via 7eb4965e0a67bac076e50a769ca9284a591abe20 (commit)
via c46763c887f9cef568cae847bffc72f1b0c939b2 (commit)
via a1528b46bf4a76a2a07736ad2b3b5421298568ae (commit)
via c2d70c85d3c5b06f05952cb4227485a2dc6630ac (commit)
via 9dc2c2ee14162ff74a1509ce620daa28fdf29261 (commit)
via bce439d0e7c18c147f3f675c03e40ecbd92fc65f (commit)
via f37c712d3dc978323e629d9ee4e982f1ae7570ec (commit)
via 57d30d529adbb49aaaba18f8347430a166d087d6 (commit)
via 617bdac8b0f2222920e63faa65f5fcb5ac729054 (commit)
via 0960094a732dde31e21013c2f2ada3c2077eaf6e (commit)
via 14a18f32396b316df44b68b21c79c073d1cbadd5 (commit)
via 5b68f13805548425c66ce562688933f99700ed9b (commit)
via e304c3e5e99d52252921370306d05c64d3a19a86 (commit)
via ae3f3a68590912bb21304c9cdc5f55833984e7a6 (commit)
via 6d4b7f727228d8966a6700b920e0d1ae71c0aa92 (commit)
via 82cf8a980ba6ec9f7df3fc80703bb5988640cd99 (commit)
via 7b053b086b440f13275474f704c5ff7cce0d8cfa (commit)
via 2f9aa4a553a05aa1d9eac06f1140d78f0c99408b (commit)
via 8efd8ed25b2bf5ac515b20e6fdd567f854365fb8 (commit)
via 4223766cd50a3465a58e8b5d655f0c34d87ddf37 (commit)
via 9ea901973b1eeb7bfe5e20768eaffb368d1dab99 (commit)
via 076510013b800546f6bb83c44fb66dcd6ad64158 (commit)
via 911f3f6bcb273fb4edf35771530ea5e4d81796d8 (commit)
via c00c53f51b120edf4d39629b303f3e2b4f6fdbb4 (commit)
via ff68e9ec714e6cffa7350f4379a813c4fab91b4e (commit)
via 03e9e74294ad900fb4f74694d8fd9d75f13536d4 (commit)
via d6e1ef18ea838bc64ee0165e0a30aa96c61a63f4 (commit)
via 077179407af119b6374302e74b90b3c79ea0682d (commit)
via 0a31990d43d949e40408fbbdcf48a074efdfe772 (commit)
via 906ba788fd5c8fb08f4cf2717e0fbccdca4850ff (commit)
via d27a40e4c0cff7e670f4d0add2a7b57db230f588 (commit)
via a584d5c1242a8b82be7eca4af4da85319d5cfbfd (commit)
via d305cc8c84239d6fce7f53188b59d1b8829c64a8 (commit)
via 773df391b037d466d8176f8682c390600215917b (commit)
via 6db9bdd58f2dae27feb07e9bb23da82aa5867e18 (commit)
via e9506d53ade201b4808ab5e083c028a835c0dcba (commit)
via 7fb1cf498139c61f673faeeaa6f487fcdd0b503a (commit)
via 60899bcd88a05293f18143f769a418ec34630cf8 (commit)
via 07eb75f4b57d4deb715cf806379b81723832f8c2 (commit)
via 90a1b131d672d476cdc1dd30d32f1554f45f98ce (commit)
via 35cf5f4b8fe9aed43d5374fd3829871d0d7a4c8f (commit)
via f3ff650b401bfe97f12554caa9ced91b431de5f1 (commit)
via bea193a5f6c6e44144496c399a333b7bbd277d39 (commit)
via dee64b333f7ec54a7647347679790b40f0c2b76b (commit)
via c11a19fe6b38b10575aa8e2a9582eb1d7e12281d (commit)
via d12c2c41040c41d2070aa18884177c9090501b7b (commit)
via 8b64be78db91e57c4b9b380150d110ef1c566bcf (commit)
via 85a727dcbf99b3af919623e87b526e5eba12c300 (commit)
via fa16480fcbc6ac41cd8582af4158b0ab8112dff6 (commit)
via c09c891d2c63f34f5d9ede256a956fc99c27c9e3 (commit)
via 9180097f88518a57f314863f9822413d325692f4 (commit)
via c40eee6618fe722031b2760d7107a8b39df9572b (commit)
via d813447bb8127d6e2a12279abf5d3c52dffee185 (commit)
via 113592770df3708dd4a5ed29d429e4b8926b4b97 (commit)
via a45f34fd77c66fa5097dbc153955094e28f36c52 (commit)
via 3896117692c2dbbbf40b7f2ca80aa61a7f20e3bf (commit)
via 47e89192baa118b32f5423b70a1b81434b7f6c3f (commit)
via 08980b117b2b64fbf767922c25e0f3625e6abf95 (commit)
via 5419a73a9e9b5f963ebd9515d049ed49a2c354d5 (commit)
via 32bb3f9e17a9c3c2bbc50b4239ee8463f9216299 (commit)
via 751e4b49ae09892db8e81f270014ad8190edca9a (commit)
via fad2e0dab86ee0625378b8410d7ba15dd25c880b (commit)
via a86b0a75d7deee554f96c6f753ea732e36c2507d (commit)
via 7468970f434e5c50be3e3cf2962a74b22b266e67 (commit)
via 626e81094b7d69a1ca96043e7dea11466031bfb0 (commit)
via 16e7be208020dd3eee46b0d8c120c23ae5f50ccc (commit)
via c3b6b5bbceee256e5415149c90861618a1deb2af (commit)
via 3b3fdd3a93aed76dd7a14f472b38b04c96c389cc (commit)
via fe58fab6bdad19c5ae7191b9ee1e3b5629f1045b (commit)
via 221f5649496821d19a40863e53e72685524b9ab2 (commit)
via 028d5ab64c816945dc310a6d8b4d159e22ba9184 (commit)
via a40b6c665617125eeb8716b12d92d806f0342396 (commit)
via b22ff2de4d9421bd1ef3976967aee886c8811611 (commit)
via 28d86506099a3e8cfa78855b97971427153ed85e (commit)
via 1322f10b28fb7ccbc69768c409ac36fe26ce0657 (commit)
via 86f48610df6dc6c82c92c39c8935a006c5696d13 (commit)
via 153b142c0e4dc2e96f717ac4ce9451f988ee6fe8 (commit)
via e6b2f289d677d2396eb3a29df2516cbe2d241b69 (commit)
via 409ca483d117688be552d27d1e06616d44701c82 (commit)
via 892f6b266acb98ad005e1c6dad6c0621c04f1d82 (commit)
via 18675a2d6a237c775ac8bb91c26c94c7b4219095 (commit)
via 25f0bb96ddbe5adc6538e68c2d6eb2da6bcc7c0c (commit)
via 6c0da87a7bdbb7be59fbac47b6c5f2ef0043aecb (commit)
via 36d930c2e66f13fadfff64689bee2547ca2ab8cd (commit)
via be560f4516ae9b0bbbad1740a519feed99c04170 (commit)
via 0c1c1eb1a972e9f85f99510766290b7ed9fe06f5 (commit)
via b3dd1982b72e5f4710e33266310180c71203ce0d (commit)
via 1c88a49755f6617184c45fe127f00ffec9e1b89d (commit)
via fbe7a931b3f74452a87449ff17792f40b8d8e08e (commit)
via 67a08ea405d64a5767215d57b936b0a6b0f7b802 (commit)
via 3b48a25b8e175427773d9463593b02361333f282 (commit)
via 253701eb2cfbf5830f71c3b00e2254df6b4ecdef (commit)
via 84ce6a7875bd85a0a7c96bff18ba2c07a65120d0 (commit)
via 3706d7583e672d807273ed9fe4dcad66d7f5774a (commit)
via 6b7e514528109978fe9b731e3690331bc5ebf10b (commit)
via 02d9aacf0a7c244a60e436237fdac09062389f48 (commit)
via 6abb27b31deb6c5bbbe18a97bb7e0a719ee9a9ce (commit)
via db211a922da7f6d72575cbb7112f6013babd4291 (commit)
via 6567d28ea958b0a89204f04714bbd90f70dd0d78 (commit)
via 4846fa711c1003092a15f61be4ccd0660828a28c (commit)
via 8c1eb198095738cac179dd2252119cfb27f47fdd (commit)
via fcf2f08db9ebc2198236bfa25cf73286821cba6b (commit)
via 768f28b23d6309f9916f4273222f8c187ba29faf (commit)
via 7287394dbaadfaa1c98883d18b34e6d9ca6e9328 (commit)
via 7d8efaf11c66fc7dfbb79060ded52f779fd523f8 (commit)
via e291c7f960f1a8145652ec785eb9deee6d448935 (commit)
via 4f69760cbd6bdbd9be311b404700f6fb33a37a49 (commit)
via 0a05819f8a0404f2aaa1a0a1ae06caf40a895863 (commit)
via c805a1176ab52d0326771b432de744037f0a76fb (commit)
via 255456507a465ee2cb1b94e386366f259c8e9487 (commit)
via 491896718f86b7514c661b7964cd92673f7e037f (commit)
via 8b0e391e3a838b2d76d87e87088f025491802c8b (commit)
via 408043d940134c7e7c301e762888f10a4184a400 (commit)
via d7191f1b7ae74dc696c386576c422582800aa5b2 (commit)
via 56083b82568a61b2fc48afd95b7e998568216894 (commit)
via cc0b929d0d82eab6df92a23a0a09d330938d79eb (commit)
via d6d267370155e3feeaa108af1d05931afac1f787 (commit)
via 5fb18596288d2eae586cd01da0b27f329fb4e155 (commit)
via dfaf9712d15392caef04df6c2149744f8abace87 (commit)
via f894ef742226f6c9d438a21be01f6a5d0cbcc5fb (commit)
via 3d876bb262c8eea4b3cdc60f293249068adde2c2 (commit)
via 3a9c1b6387eb19a1749e7e9a4dec753b33da5766 (commit)
via 75bfd569cd3a7fdadf72bab974a5303fcc1c0575 (commit)
via 37227b85bf18cf42bcc08bacedbe86bbe7bf6cfc (commit)
via 1ff3eb19126e4c7a43a6f6b64fef46590f462fb8 (commit)
via 156ace04dc1267c73f5e58006dc35fb0d3788c86 (commit)
via 7b0e7e17467d2a8c31fd8a8b4ab34e16f1ad84bf (commit)
via bd2568ebded2746a7a6741c5b2f927392820506a (commit)
via 5ffee903b4e48f34c363215668d3867c0a7f6c94 (commit)
via 95947109c58b1e099ee5c4e955f739213824e49f (commit)
via 5c8bbd7ab648b6b7c48e366e7510dedca5386f6c (commit)
via 653847cf994c75cacd23df1ae0e5c9002cf338c4 (commit)
via 0f18039bc751a8f498c1f832196e2ecc7b997b2a (commit)
via 4c43c7d967b6b9fe70d8431058808926509755f5 (commit)
via c73ffa69fd26e03b0879a7773f7f6796f19aee2e (commit)
via 551657702a4197ef302c567b5c0eaf2fded3e121 (commit)
via 58757c09f3b72768da9a43f1affa7120ebbdeba8 (commit)
via 3e6773dbdbc31ca20637455b110c2907daf2ce0c (commit)
via 17f72c2e942afec37711c9b05c4a5351f40b4676 (commit)
via c016b74723d24469961682defaee0034bb8b5920 (commit)
via 4429acb90d47320d20e9126457594a1f38191b28 (commit)
via 2f7b1a1865ae1c8b981d5f6396ae612421be764b (commit)
via 9a76caecbc29f35dad73a2f9874e8e3a64e4154f (commit)
via 3de6e881f69c6462cf3da43c662cbc1a35595504 (commit)
via a475ef271d4606f791e5ed88d9b8eb8ed8c90ce6 (commit)
via 3055e37cd7bdb8ada5cdf54e611343665ff93675 (commit)
via f9d8efd545fb5bdb5bbab14eac2111ae4e47f75f (commit)
via e63005a633e4f62a5e359ac26ba1704ff55a734c (commit)
via a3893c3664567e1ce3c724b8d478989405ddaa77 (commit)
via 0ddeda7f0669b481c4d7ceadc9f8d76db41baed7 (commit)
via 597e059afaa4a89e767f8f10d2a4d78223af3940 (commit)
via 9f613bb9c9be87fbd800a1e87044070a04d75adf (commit)
via 84a0413302fa935314dd81a352aeb6d746cbbf3a (commit)
via dc142a44a0a0de3d9278937bdb0d70c545e6da6e (commit)
via 8df566b82de165047fceccf6b4eda1974f3e78e5 (commit)
via 373ef178d799790eb5917f5fbb2b725391088b0d (commit)
via e0cb5ab6d32eae3df6e309ff73c6d331f0750168 (commit)
via fd1a4b0eac2f586c2efc98cb99235ddb1c77da8a (commit)
via b24b0f2312ce99cba4af8456179c712d5cc0fe9d (commit)
via 146b5941b9f3d09537b56eb83df14355c2edde59 (commit)
via 4cfab0d82574c3501502a9ed9b84c39112d411a8 (commit)
via e4fd9934de5258ad1668c4cbba408fe59427c040 (commit)
via bed1681e323f5c278a4271b7c948a44b0605e73b (commit)
via 53a7c709b06e670dacf81907a7360ea030341b1d (commit)
via 50d20d7206fad792c4eb035a9e643f4d47518505 (commit)
via c8e538c10d73ea56f079fcb8f3c6f99e5407bdef (commit)
via 5b37859360df57221621144d34119fcc7e417099 (commit)
via 6e05014aa5bd412778a0927ac1babff042e5f46f (commit)
via 264691395b76a2a6f18b02dbd43c78f6e9de83ff (commit)
via 438b5b183c82b04d74be71c7cc396d6393d215b6 (commit)
via da4d8df9ee0412d3717f1009ecccac58736313b8 (commit)
via 672f129700dae33b701bb02069cf276238d66be3 (commit)
via a6cc81d79b42baf06058c25f404b8ec61de0c2d6 (commit)
via a9b59f8a7728418038125bfb3ed2335bf5524b3f (commit)
via bcb62dd7e4d8a3bca40e3725aa457074ada36134 (commit)
via 400ff18a8c98cd0c1c393ef32e1d76bb4222dce6 (commit)
via 1c3fe6e537bf9e7bc742f84a36efbbbb49a6365d (commit)
via 7eaf3a710ceb20f4822a636054aef7d8a6f7e6dd (commit)
via 64f69a14336846132a5588f6523c3f5aadf63382 (commit)
via 233c7295de7021fe24b8342db5d966d3d9b354e6 (commit)
via 52707b19511b499401a61b42eb3a03207f79b0c6 (commit)
via f782145ec0759ef8e595c87d4baf15de89624ea7 (commit)
via f93cd951e3ead684e4efbc8d2eaa523141e0cf65 (commit)
via e7c91fe725369cabe19bd227e06176a117955461 (commit)
via 14a13b8494a53f2a488859ed27726f32e57781b6 (commit)
via 47f1415387f974e7ccdf7d95d7a380827486bc22 (commit)
via 9a4619a0a1ffdde67a58af51f3826e5c6a8da20b (commit)
via fb7201b9ff0aaa126ff82595b7a4c2f69ea8f4c7 (commit)
via bea9aaf1441dd9da91cc1197d02ea52c0d0095f1 (commit)
via d7cf5f81d983e95e4f8bd85637a0ae9785d94a9d (commit)
via 91cb18e244588b8a99ddf9b12fb99986b0258fdb (commit)
via 7aac25a59bfb9dbdbdbc4c8334204d24f7e0b131 (commit)
via 358beb2e1e15bcbe09438719e756f8641ea929d5 (commit)
via d68d574b69ee627b11982276312dfa70160b072a (commit)
via c3be9fa408b02750d56f63ba593b9a0c92647928 (commit)
via c9b2b1cc3060d991f9fdc89a8d70d4364f2e9d6d (commit)
via 0422339a5a2c934aec17869b8096f263d6cae922 (commit)
via 4deb49cf84c399a0ca1a84fef1cd0dee4ca4d5d5 (commit)
via f00dd21c6c495e180feb855139d36fc7f99f23d8 (commit)
via 89071a214099a1de3ce7253e4f91fa4bc8d00272 (commit)
via 36efa7d10ecc4efd39d2ce4dfffa0cbdeffa74b0 (commit)
via a318061e3c20ad5deeebe41884cbe3dcf9a3a413 (commit)
via 4a7381b8b8a03c010372c7a62192bec325a40a8a (commit)
via 211934fe44eb6ea5a74857d4312e10b6cd6a3c55 (commit)
via 136e63b8234f46a46c4278e4ccfe9712a76ebfc9 (commit)
via 7f108b1ad180d002ff6bc2d4be26b6a93ead8c99 (commit)
via a93523dfcf7d640b15d9af71596caa09362cbbfd (commit)
via d78d0dbcec53a339b7789b181954768d181c2a1b (commit)
via be9562a0d38bdb2ba5d3e8456dd13a0cbc19b48e (commit)
via 5149a8149791f6f828704a2aa63178ca98707ebc (commit)
via 78c6b5331109e94c7c5217c500162106b587ae59 (commit)
via ffbfe8fb2978b60c8309b3d7b7d006c98778b304 (commit)
via fa7b9c53b18d220062ed252abbc241e8871e8927 (commit)
via 61fa7a41089bf18f39ae6415fa92c6c2bca6a026 (commit)
via 0bc5c68ef6c5811eb93227e1d8d44f7762a3f7b2 (commit)
via 3057455dd008c612f64fd265a957bdf9c0b58679 (commit)
via 65193e8384b25f0092177adb91a8ee6c2679d2db (commit)
via 6d7a20689e5ac2dd8139c5d6cece17b5d1a4f178 (commit)
via 92cc4a7b436ebad4536979bddbcea21ffa0b118b (commit)
via ed1f3349ee00294fbeb42760a53a2694f9dc02eb (commit)
via 21c1954e0e26413c1110f68d2b7c730ae79c3d44 (commit)
via 1202068e5f61ed72afde9b204fd5cd011ecd190f (commit)
via d8db35358ab97a466ea61e9624edf8d736364211 (commit)
via ce7abc70e21119826398943593b327f12b4bc751 (commit)
via 1c1127916ee5c550c729d3d128c1b6bad100cab9 (commit)
via 795efe06b737662b662ad549b1afa881537790df (commit)
via b8ccce76cae1fd13b07b0c4ce155a5d95d76de12 (commit)
via 485f7966f583f54cf2694f054de6accea9c19364 (commit)
via 4fd6efcde0f9cb8d7d1513530324a389e32a978d (commit)
via dbedca057de99f85a373b918cae849174128e17b (commit)
via cc72ac90f8858d6e24da1816d5e70d38b0db8e9c (commit)
via d53ab0d663c6ead51bdde2d2b16365ff6adab424 (commit)
via 6207354d2f7830511008d49e36aac658ec378348 (commit)
via 44b275d27c8089167cff36195d1102f54c99047c (commit)
via 7f9ddb4963264b1ac022e7d550930dfbc825d71c (commit)
via 4006d91c9555c8dffab444aaa6e83f523ad5b338 (commit)
via 526f8713fb6e13456f7008b5cd075f6fbdb89161 (commit)
via 4b7786c01f7cc727ae9e36b3808bf727022e562e (commit)
via ec38722482e323b9e2674dd0331c074a4f1b9a61 (commit)
via 219324e66e5bf33fde5cd53dc19f82fb1d442fc8 (commit)
via 1b9ab32f79fe18b25a378808192cc2f8f5f6c718 (commit)
via 0dc861f4c32cfd154904819d5c4b8dff71fdc47c (commit)
via 029db306a1acc7c7856cb9da3e18fda5cdaf630f (commit)
via 03effee3f28d0b3958f5e7e745b30cb47f6e0e74 (commit)
from b1c727db35e23c3c72262af0f0a54ab8fe4b411e (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 8801ca7c8d5e612629fbe7425f5c9005dd87bcfb
Author: Mukund Sivaraman <muks at isc.org>
Date: Thu May 17 10:47:24 2012 +0530
[1704] Use a lock file for mutual exclusion when printing log messages
-----------------------------------------------------------------------
Summary of changes:
.gitignore | 8 +
ChangeLog | 93 +++-
Makefile.am | 7 +
configure.ac | 13 +-
depcomp | 630 --------------------
dns++.pc.in | 11 +
doc/.gitignore | 1 +
doc/Doxyfile | 25 +-
doc/Makefile.am | 13 +
doc/devel/01-dns.dox | 14 +
doc/devel/02-dhcp.dox | 117 ++++
doc/devel/mainpage.dox | 36 ++
doc/guide/bind10-guide.html | 130 +++--
doc/guide/bind10-guide.txt | 67 ++-
doc/guide/bind10-guide.xml | 78 ++-
doc/guide/bind10-messages.html | 226 +++++++-
doc/guide/bind10-messages.xml | 150 +++++-
doc/images/isc-logo.png | Bin 0 -> 12595 bytes
install-sh | 520 ----------------
m4macros/.gitignore | 5 +
missing | 376 ------------
src/bin/auth/auth_srv.h | 2 +-
src/bin/auth/b10-auth.8 | 22 +-
src/bin/auth/b10-auth.xml | 21 +-
src/bin/auth/command.cc | 96 +++-
src/bin/auth/tests/Makefile.am | 11 +-
src/bin/auth/tests/command_unittest.cc | 139 ++++-
src/bin/auth/tests/common_unittest.cc | 2 +-
src/bin/auth/tests/datasrc_util.cc | 2 +-
src/bin/auth/tests/datasrc_util.h | 5 +-
src/bin/bind10/bind10.8 | 73 +--
src/bin/bind10/bind10.xml | 26 +-
src/bin/bind10/bind10_messages.mes | 9 +-
src/bin/bind10/bob.spec | 4 -
src/bin/bindctl/Makefile.am | 2 +-
src/bin/bindctl/bindcmd.py | 106 +++-
src/bin/bindctl/bindctl_main.py.in | 2 +
src/bin/bindctl/command_sets.py | 95 +++
src/bin/cfgmgr/b10-cfgmgr.8 | 17 +-
src/bin/cfgmgr/b10-cfgmgr.py.in | 33 +-
src/bin/cfgmgr/b10-cfgmgr.xml | 31 +-
src/bin/cfgmgr/tests/b10-cfgmgr_test.py.in | 23 +-
src/bin/ddns/ddns.py.in | 18 +-
src/bin/dhcp4/dhcp4_srv.cc | 59 +-
src/bin/dhcp4/dhcp4_srv.h | 35 +-
src/bin/dhcp6/dhcp6_srv.cc | 204 ++++---
src/bin/dhcp6/dhcp6_srv.h | 46 +-
src/bin/dhcp6/tests/dhcp6_srv_unittest.cc | 98 +++-
src/bin/dhcp6/tests/dhcp6_test.py | 3 +
src/bin/resolver/resolver_messages.mes | 58 +-
src/bin/stats/stats_httpd.py.in | 7 +-
src/bin/stats/stats_httpd_messages.mes | 32 +-
src/bin/stats/stats_messages.mes | 26 +-
src/bin/stats/tests/b10-stats-httpd_test.py | 18 +
src/bin/xfrin/tests/xfrin_test.py | 187 ++++++-
src/bin/xfrin/xfrin.py.in | 54 ++-
src/bin/xfrin/xfrin_messages.mes | 15 +
src/bin/xfrout/xfrout_messages.mes | 144 +++---
src/bin/zonemgr/zonemgr_messages.mes | 8 +-
src/lib/asiolink/io_address.cc | 4 +-
src/lib/cache/cache_messages.mes | 6 +-
src/lib/config/module_spec.cc | 21 +-
src/lib/config/module_spec.h | 12 +-
src/lib/cryptolink/crypto_hmac.cc | 4 +-
src/lib/datasrc/database.cc | 148 ++++-
src/lib/datasrc/database.h | 88 +++
src/lib/datasrc/memory_datasrc.cc | 143 ++++-
src/lib/datasrc/memory_datasrc.h | 6 +-
src/lib/datasrc/rbtree.h | 310 +++++++++--
src/lib/datasrc/sqlite3_accessor.cc | 205 +++++---
src/lib/datasrc/sqlite3_accessor.h | 6 +
src/lib/datasrc/tests/database_unittest.cc | 333 +++++++++--
src/lib/datasrc/tests/faked_nsec3.cc | 15 +-
src/lib/datasrc/tests/faked_nsec3.h | 24 +-
src/lib/datasrc/tests/memory_datasrc_unittest.cc | 406 +++++++++++--
src/lib/datasrc/tests/rbtree_unittest.cc | 283 ++++++++--
src/lib/datasrc/tests/sqlite3_accessor_unittest.cc | 222 +++++++-
src/lib/datasrc/tests/test_client.cc | 2 +-
src/lib/dhcp/Makefile.am | 1 +
src/lib/dhcp/dhcp6.h | 9 +
src/lib/dhcp/iface_mgr.cc | 467 ++++++---------
src/lib/dhcp/iface_mgr.h | 102 +++-
src/lib/dhcp/iface_mgr_bsd.cc | 21 +-
src/lib/dhcp/iface_mgr_linux.cc | 582 ++++++++++++------
src/lib/dhcp/iface_mgr_sun.cc | 55 ++
src/lib/dhcp/libdhcp++.cc | 142 +++---
src/lib/dhcp/libdhcp++.h | 69 +--
src/lib/dhcp/option.cc | 148 ++----
src/lib/dhcp/option.h | 166 ++----
src/lib/dhcp/option4_addrlst.cc | 14 +-
src/lib/dhcp/option4_addrlst.h | 15 +-
src/lib/dhcp/option6_addrlst.cc | 81 +--
src/lib/dhcp/option6_addrlst.h | 53 +--
src/lib/dhcp/option6_ia.cc | 93 +--
src/lib/dhcp/option6_ia.h | 64 +--
src/lib/dhcp/option6_iaaddr.cc | 79 +--
src/lib/dhcp/option6_iaaddr.h | 52 +--
src/lib/dhcp/pkt4.h | 101 ++--
src/lib/dhcp/pkt6.cc | 140 ++---
src/lib/dhcp/pkt6.h | 198 +++++--
src/lib/dhcp/tests/Makefile.am | 1 +
src/lib/dhcp/tests/iface_mgr_unittest.cc | 137 ++++--
src/lib/dhcp/tests/libdhcp++_unittest.cc | 67 +--
src/lib/dhcp/tests/option6_addrlst_unittest.cc | 52 +-
src/lib/dhcp/tests/option6_ia_unittest.cc | 132 ++---
src/lib/dhcp/tests/option6_iaaddr_unittest.cc | 91 ++--
src/lib/dhcp/tests/option_unittest.cc | 226 ++++----
src/lib/dhcp/tests/pkt4_unittest.cc | 2 +
src/lib/dhcp/tests/pkt6_unittest.cc | 162 +++---
src/lib/dns/Makefile.am | 2 +-
src/lib/dns/labelsequence.cc | 4 +-
src/lib/dns/rrparamregistry-placeholder.cc | 2 +-
src/lib/exceptions/Makefile.am | 2 +-
src/lib/exceptions/exceptions.h | 32 +
src/lib/log/Makefile.am | 2 +
src/lib/log/compiler/Makefile.am | 1 +
src/lib/log/compiler/message.cc | 32 +-
src/lib/log/log_formatter.cc | 36 +-
src/lib/log/log_formatter.h | 22 +
src/lib/log/logger.cc | 72 +++
src/lib/log/logger.h | 11 +-
src/lib/log/message_exception.h | 24 +-
src/lib/log/message_reader.cc | 40 +-
src/lib/log/message_reader.h | 2 +-
src/lib/log/tests/Makefile.am | 10 +-
src/lib/log/tests/log_formatter_unittest.cc | 64 ++-
src/lib/log/tests/logger_manager_unittest.cc | 2 +-
src/lib/log/tests/logger_support_unittest.cc | 2 +-
src/lib/log/tests/logger_unittest.cc | 10 +-
.../log/tests/message_initializer_2_unittest.cc | 6 +-
src/lib/nsas/nsas_messages.mes | 6 +-
src/lib/python/isc/config/cfgmgr.py | 1 +
src/lib/python/isc/config/cfgmgr_messages.mes | 4 +
src/lib/python/isc/config/config_data.py | 16 +-
src/lib/python/isc/config/config_messages.mes | 10 +-
.../python/isc/config/tests/config_data_test.py | 19 +-
src/lib/python/isc/notify/notify_out_messages.mes | 52 +-
src/lib/resolve/resolve_messages.mes | 30 +-
src/lib/server_common/server_common_messages.mes | 10 +-
src/lib/server_common/tests/portconfig_unittest.cc | 43 +-
src/lib/testutils/dnsmessage_test.cc | 26 +
src/lib/testutils/dnsmessage_test.h | 23 +
src/lib/testutils/testdata/.gitignore | 1 +
.../testutils/testdata/auth_test.sqlite3.copied | Bin 16384 -> 0 bytes
src/lib/util/Makefile.am | 1 +
src/lib/util/buffer.h | 10 +-
src/lib/util/io/Makefile.am | 1 +
src/lib/util/io/pktinfo_utilities.h | 51 ++
src/lib/util/io/sockaddr_util.h | 2 +-
src/lib/util/range_utilities.h | 68 +++
src/lib/util/tests/Makefile.am | 1 +
src/lib/util/tests/buffer_unittest.cc | 14 +-
src/lib/util/tests/range_utilities_unittest.cc | 58 ++
src/lib/util/unittests/Makefile.am | 1 +
src/lib/util/unittests/resource.cc | 35 ++
src/lib/util/unittests/resource.h | 39 ++
tests/lettuce/README | 20 +-
tests/lettuce/README.tutorial | 4 +-
tests/lettuce/configurations/NOTES | 4 +
tests/lettuce/configurations/bindctl/.gitignore | 1 +
.../configurations/bindctl/bindctl.config.orig | 22 +
.../inmemory_over_sqlite3/secondary.conf | 2 +-
.../multi_instance/multi_auth.config.orig | 25 +-
.../lettuce/configurations/xfrin/inmem_slave.conf | 34 +
tests/lettuce/data/.gitignore | 1 +
tests/lettuce/data/commands/bad_command | 9 +
tests/lettuce/data/commands/directives | 19 +
AUTHORS => tests/lettuce/data/commands/empty | 0
tests/lettuce/data/commands/nested | 2 +
tests/lettuce/data/commands/nested1 | 2 +
tests/lettuce/data/example.org.sqlite3 | Bin 15360 -> 15360 bytes
tests/lettuce/data/inmem-xfrin | 7 +
tests/lettuce/data/inmem-xfrin.sqlite3.orig | Bin 0 -> 13312 bytes
tests/lettuce/features/bindctl_commands.feature | 213 +++++--
tests/lettuce/features/default.feature | 3 +-
tests/lettuce/features/example.feature | 16 +-
.../lettuce/features/inmemory_over_sqlite3.feature | 36 ++-
tests/lettuce/features/multi_instance.feature | 7 +
tests/lettuce/features/queries.feature | 3 +
tests/lettuce/features/terrain/terrain.py | 18 +-
tests/lettuce/features/terrain/transfer.py | 4 +-
tests/lettuce/features/xfrin_bind10.feature | 17 +-
tests/lettuce/run_lettuce.sh | 25 +
tests/lettuce/setup_intree_bind10.sh.in | 2 +
tests/tools/perfdhcp/perfdhcp.c | 9 +-
185 files changed, 6973 insertions(+), 4445 deletions(-)
delete mode 100755 depcomp
create mode 100644 dns++.pc.in
create mode 100644 doc/devel/01-dns.dox
create mode 100644 doc/devel/02-dhcp.dox
create mode 100644 doc/devel/mainpage.dox
create mode 100644 doc/images/isc-logo.png
delete mode 100755 install-sh
create mode 100644 m4macros/.gitignore
delete mode 100755 missing
create mode 100644 src/bin/bindctl/command_sets.py
create mode 100644 src/lib/dhcp/iface_mgr_sun.cc
delete mode 100755 src/lib/testutils/testdata/auth_test.sqlite3.copied
create mode 100644 src/lib/util/io/pktinfo_utilities.h
create mode 100644 src/lib/util/range_utilities.h
create mode 100644 src/lib/util/tests/range_utilities_unittest.cc
create mode 100644 src/lib/util/unittests/resource.cc
create mode 100644 src/lib/util/unittests/resource.h
create mode 100644 tests/lettuce/configurations/NOTES
create mode 100644 tests/lettuce/configurations/bindctl/.gitignore
create mode 100644 tests/lettuce/configurations/bindctl/bindctl.config.orig
create mode 100644 tests/lettuce/configurations/xfrin/inmem_slave.conf
create mode 100644 tests/lettuce/data/commands/bad_command
create mode 100644 tests/lettuce/data/commands/directives
copy AUTHORS => tests/lettuce/data/commands/empty (100%)
create mode 100644 tests/lettuce/data/commands/nested
create mode 100644 tests/lettuce/data/commands/nested1
create mode 100644 tests/lettuce/data/inmem-xfrin
create mode 100644 tests/lettuce/data/inmem-xfrin.sqlite3.orig
create mode 100755 tests/lettuce/run_lettuce.sh
mode change 100755 => 100644 tests/lettuce/setup_intree_bind10.sh.in
-----------------------------------------------------------------------
diff --git a/.gitignore b/.gitignore
index 1dfbe9f..3480cb6 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,6 @@
+*.gcda
+*.gcno
+*.gcov
*.la
*.lo
*.o
@@ -27,3 +30,8 @@ TAGS
/missing
/py-compile
/stamp-h1
+
+/all.info
+/coverage-cpp-html
+/dns++.pc
+/report.info
diff --git a/ChangeLog b/ChangeLog
index d1a0ad2..2e91069 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,4 +1,93 @@
-425. [func]* muks
+bind10-devel-20120517 released on May 17. 2012
+
+440. [func] muks
+ bindctl: improved some error messages so they will be more
+ helpful. Those include the one when the zone name is unspecified
+ or the name is invalid in the b10-auth configuration.
+ (Trac #1627, git 1a4d0ae65b2c1012611f4c15c5e7a29d65339104)
+
+439. [func] team
+ The in-memory data source can now load zones from the
+ sqlite3 data source, so that zones stored in the database
+ (and updated for example by xfrin) can be served from memory.
+ (Trac #1789,#1790,#1792,#1793,#1911,
+ git 93f11d2a96ce4dba9308889bdb9be6be4a765b27)
+
+438. [bug] naokikambe
+ b10-stats-httpd now sends the system a notification that
+ it is shutting down if it encounters a fatal error during
+ startup.
+ (Trac #1852, git a475ef271d4606f791e5ed88d9b8eb8ed8c90ce6)
+
+437. [build] jinmei
+ Building BIND 10 may fail on MacOS if Python has been
+ installed via Homebrew unless --without-werror is specified.
+ The configure script now includes a URL that explains this
+ issue when it detects failure that is possibly because of
+ this problem.
+ (Trac #1907, git 0d03b06138e080cc0391fb912a5a5e75f0f97cec)
+
+436. [bug] jelte
+ The --config-file option now works correctly with relative paths if
+ --data-path is not given.
+ (Trac #1889, git ce7d1aef2ca88084e4dacef97132337dd3e50d6c)
+
+435. [func] team
+ The in-memory datasource now supports NSEC-signed zones.
+ (Trac #1802-#1810, git 2f9aa4a553a05aa1d9eac06f1140d78f0c99408b)
+
+434. [func] tomek
+ libdhcp++: Linux interface detection refactored. The code is
+ now cleaner. Tests better support certain versions of ifconfig.
+ (Trac #1528, git 221f5649496821d19a40863e53e72685524b9ab2)
+
+433. [func] tomek
+ libdhcp++: Option6 and Pkt6 now follow the same design as
+ options and packet for DHCPv4. General code refactoring after
+ end of 2011 year release.
+ (Trac #1540, git a40b6c665617125eeb8716b12d92d806f0342396)
+
+432. [bug]* muks
+ BIND 10 now installs its header files in a BIND 10 specific
+ sub-directory in the install prefix.
+ (Trac #1930, git fcf2f08db9ebc2198236bfa25cf73286821cba6b)
+
+431. [func]* muks
+ BIND 10 no longer starts b10-stats-httpd by default.
+ (Trac #1885, git 5c8bbd7ab648b6b7c48e366e7510dedca5386f6c)
+
+430. [bug] jelte
+ When displaying configuration data, bindctl no longer treats
+ optional list items as an error, but shows them as an empty list.
+ (Trac #1520, git 0f18039bc751a8f498c1f832196e2ecc7b997b2a)
+
+429. [func] jelte
+ Added an 'execute' component to bindctl, which executes either a set
+ of commands from a file or a built-in set of commands. Currently,
+ only 'init_authoritative_server' is provided as a built-in set, but
+ it is expected that more will be added later.
+ (Trac #1843, git 551657702a4197ef302c567b5c0eaf2fded3e121)
+
+428. [bug] marcin
+ perfdhcp: bind to local address to allow reception of
+ replies from IPv6 DHCP servers.
+ (Trac #1908, git 597e059afaa4a89e767f8f10d2a4d78223af3940)
+
+427. [bug] jinmei
+ libdatasrc, b10-xfrin: the zone updater for database-based data
+ sources now correctly distinguishes NSEC3-related RRs (NSEC3 and
+ NSEC3-covering RRSIG) from others, and the SQLite3 implementation
+ now manipulates them in the separate table for the NSEC3 namespace.
+ As a result b10-xfrin now correctly updates NSEC3-signed zones by
+ inbound zone transfers.
+ (Trac #1781,#1788,#1891, git 672f129700dae33b701bb02069cf276238d66be3)
+
+426. [bug] vorner
+ The NSEC3 records are now included when transferring a
+ signed zone out.
+ (Trac #1782, git 36efa7d10ecc4efd39d2ce4dfffa0cbdeffa74b0)
+
+425. [func]* muks
Don't autostart b10-auth, b10-xfrin, b10-xfrout and b10-zonemgr in
the default configuration.
(Trac #1818, git 31de885ba0409f54d9a1615eff5a4b03ed420393)
@@ -146,7 +235,7 @@ bind10-devel-20120329 released on March 29, 2012
providing result for random instance.
(Trac #1751, git 3285353a660e881ec2b645e1bc10d94e5020f357)
-403. [build]* jelte
+403. [build]* jelte
The configure option for botan (--with-botan=PATH) is replaced by
--with-botan-config=PATH, which takes a full path to a botan-config
script, instead of the botan 'install' directory. Also, if not
diff --git a/Makefile.am b/Makefile.am
index 55a28aa..54216b6 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1,3 +1,7 @@
+ACLOCAL_AMFLAGS = -I m4macros ${ACLOCAL_FLAGS}
+# ^^^^^^^^ This has to be the first line and cannot come later in this
+# Makefile.am due to some bork in some versions of autotools.
+
SUBDIRS = compatcheck doc src tests
USE_LCOV=@USE_LCOV@
LCOV=@LCOV@
@@ -398,3 +402,6 @@ EXTRA_DIST += ext/asio/asio/system_error.hpp
EXTRA_DIST += ext/asio/asio/deadline_timer.hpp
EXTRA_DIST += ext/asio/asio/stream_socket_service.hpp
EXTRA_DIST += ext/coroutine/coroutine.h
+
+pkgconfigdir = $(libdir)/pkgconfig
+pkgconfig_DATA = dns++.pc
diff --git a/configure.ac b/configure.ac
index a35da4c..cf70e3f 100644
--- a/configure.ac
+++ b/configure.ac
@@ -7,6 +7,7 @@ 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])
+AC_CONFIG_MACRO_DIR([m4macros])
# Checks for programs.
AC_PROG_CXX
@@ -361,7 +362,7 @@ if test $werror_ok = 1; then
PYTHON_CXXFLAGS="${PYTHON_CXXFLAGS} -Wno-unused-parameter"
AC_SUBST(PYTHON_CXXFLAGS)
],
- [AC_MSG_ERROR([Can't compile against Python.h])]
+ [AC_MSG_ERROR([Can't compile against Python.h. If you're using MacOS X and have installed Python with Homebrew, see http://bind10.isc.org/wiki/SystemNotesMacOSX])]
)
]
)
@@ -406,9 +407,9 @@ case $system in
OS_TYPE="BSD"
CPPFLAGS="$CPPFLAGS -DOS_BSD"
;;
- Solaris)
+ SunOS)
OS_TYPE="Solaris"
- CPPFLAGS="$CPPFLAGS -DOS_SOLARIS"
+ CPPFLAGS="$CPPFLAGS -DOS_SUN"
;;
*)
OS_TYPE="Unknown"
@@ -976,6 +977,11 @@ AC_ARG_ENABLE(install-configurations,
AM_CONDITIONAL(INSTALL_CONFIGURATIONS, test x$install_configurations = xyes || test x$install_configurations = xtrue)
+AC_ARG_ENABLE(logger-checks, [AC_HELP_STRING([--enable-logger-checks],
+ [check logger messages [default=no]])], enable_logger_checks=$enableval, enable_logger_checks=no)
+AM_CONDITIONAL(ENABLE_LOGGER_CHECKS, test x$enable_logger_checks != xno)
+AM_COND_IF([ENABLE_LOGGER_CHECKS], [AC_DEFINE([ENABLE_LOGGER_CHECKS], [1], [Check logger messages?])])
+
AC_CONFIG_FILES([Makefile
doc/Makefile
doc/guide/Makefile
@@ -1114,6 +1120,7 @@ AC_CONFIG_FILES([Makefile
tests/tools/badpacket/Makefile
tests/tools/badpacket/tests/Makefile
tests/tools/perfdhcp/Makefile
+ dns++.pc
])
AC_OUTPUT([doc/version.ent
src/bin/cfgmgr/b10-cfgmgr.py
diff --git a/depcomp b/depcomp
deleted file mode 100755
index df8eea7..0000000
--- a/depcomp
+++ /dev/null
@@ -1,630 +0,0 @@
-#! /bin/sh
-# depcomp - compile a program generating dependencies as side-effects
-
-scriptversion=2009-04-28.21; # UTC
-
-# Copyright (C) 1999, 2000, 2003, 2004, 2005, 2006, 2007, 2009 Free
-# Software Foundation, Inc.
-
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2, or (at your option)
-# any later version.
-
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-
-# You should have received a copy of the GNU General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
-
-# As a special exception to the GNU General Public License, if you
-# distribute this file as part of a program that contains a
-# configuration script generated by Autoconf, you may include it under
-# the same distribution terms that you use for the rest of that program.
-
-# Originally written by Alexandre Oliva <oliva at dcc.unicamp.br>.
-
-case $1 in
- '')
- echo "$0: No command. Try \`$0 --help' for more information." 1>&2
- exit 1;
- ;;
- -h | --h*)
- cat <<\EOF
-Usage: depcomp [--help] [--version] PROGRAM [ARGS]
-
-Run PROGRAMS ARGS to compile a file, generating dependencies
-as side-effects.
-
-Environment variables:
- depmode Dependency tracking mode.
- source Source file read by `PROGRAMS ARGS'.
- object Object file output by `PROGRAMS ARGS'.
- DEPDIR directory where to store dependencies.
- depfile Dependency file to output.
- tmpdepfile Temporary file to use when outputing dependencies.
- libtool Whether libtool is used (yes/no).
-
-Report bugs to <bug-automake at gnu.org>.
-EOF
- exit $?
- ;;
- -v | --v*)
- echo "depcomp $scriptversion"
- exit $?
- ;;
-esac
-
-if test -z "$depmode" || test -z "$source" || test -z "$object"; then
- echo "depcomp: Variables source, object and depmode must be set" 1>&2
- exit 1
-fi
-
-# Dependencies for sub/bar.o or sub/bar.obj go into sub/.deps/bar.Po.
-depfile=${depfile-`echo "$object" |
- sed 's|[^\\/]*$|'${DEPDIR-.deps}'/&|;s|\.\([^.]*\)$|.P\1|;s|Pobj$|Po|'`}
-tmpdepfile=${tmpdepfile-`echo "$depfile" | sed 's/\.\([^.]*\)$/.T\1/'`}
-
-rm -f "$tmpdepfile"
-
-# Some modes work just like other modes, but use different flags. We
-# parameterize here, but still list the modes in the big case below,
-# to make depend.m4 easier to write. Note that we *cannot* use a case
-# here, because this file can only contain one case statement.
-if test "$depmode" = hp; then
- # HP compiler uses -M and no extra arg.
- gccflag=-M
- depmode=gcc
-fi
-
-if test "$depmode" = dashXmstdout; then
- # This is just like dashmstdout with a different argument.
- dashmflag=-xM
- depmode=dashmstdout
-fi
-
-cygpath_u="cygpath -u -f -"
-if test "$depmode" = msvcmsys; then
- # This is just like msvisualcpp but w/o cygpath translation.
- # Just convert the backslash-escaped backslashes to single forward
- # slashes to satisfy depend.m4
- cygpath_u="sed s,\\\\\\\\,/,g"
- depmode=msvisualcpp
-fi
-
-case "$depmode" in
-gcc3)
-## gcc 3 implements dependency tracking that does exactly what
-## we want. Yay! Note: for some reason libtool 1.4 doesn't like
-## it if -MD -MP comes after the -MF stuff. Hmm.
-## Unfortunately, FreeBSD c89 acceptance of flags depends upon
-## the command line argument order; so add the flags where they
-## appear in depend2.am. Note that the slowdown incurred here
-## affects only configure: in makefiles, %FASTDEP% shortcuts this.
- for arg
- do
- case $arg in
- -c) set fnord "$@" -MT "$object" -MD -MP -MF "$tmpdepfile" "$arg" ;;
- *) set fnord "$@" "$arg" ;;
- esac
- shift # fnord
- shift # $arg
- done
- "$@"
- stat=$?
- if test $stat -eq 0; then :
- else
- rm -f "$tmpdepfile"
- exit $stat
- fi
- mv "$tmpdepfile" "$depfile"
- ;;
-
-gcc)
-## There are various ways to get dependency output from gcc. Here's
-## why we pick this rather obscure method:
-## - Don't want to use -MD because we'd like the dependencies to end
-## up in a subdir. Having to rename by hand is ugly.
-## (We might end up doing this anyway to support other compilers.)
-## - The DEPENDENCIES_OUTPUT environment variable makes gcc act like
-## -MM, not -M (despite what the docs say).
-## - Using -M directly means running the compiler twice (even worse
-## than renaming).
- if test -z "$gccflag"; then
- gccflag=-MD,
- fi
- "$@" -Wp,"$gccflag$tmpdepfile"
- stat=$?
- if test $stat -eq 0; then :
- else
- rm -f "$tmpdepfile"
- exit $stat
- fi
- rm -f "$depfile"
- echo "$object : \\" > "$depfile"
- alpha=ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz
-## The second -e expression handles DOS-style file names with drive letters.
- sed -e 's/^[^:]*: / /' \
- -e 's/^['$alpha']:\/[^:]*: / /' < "$tmpdepfile" >> "$depfile"
-## This next piece of magic avoids the `deleted header file' problem.
-## The problem is that when a header file which appears in a .P file
-## is deleted, the dependency causes make to die (because there is
-## typically no way to rebuild the header). We avoid this by adding
-## dummy dependencies for each header file. Too bad gcc doesn't do
-## this for us directly.
- tr ' ' '
-' < "$tmpdepfile" |
-## Some versions of gcc put a space before the `:'. On the theory
-## that the space means something, we add a space to the output as
-## well.
-## Some versions of the HPUX 10.20 sed can't process this invocation
-## correctly. Breaking it into two sed invocations is a workaround.
- sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' | sed -e 's/$/ :/' >> "$depfile"
- rm -f "$tmpdepfile"
- ;;
-
-hp)
- # This case exists only to let depend.m4 do its work. It works by
- # looking at the text of this script. This case will never be run,
- # since it is checked for above.
- exit 1
- ;;
-
-sgi)
- if test "$libtool" = yes; then
- "$@" "-Wp,-MDupdate,$tmpdepfile"
- else
- "$@" -MDupdate "$tmpdepfile"
- fi
- stat=$?
- if test $stat -eq 0; then :
- else
- rm -f "$tmpdepfile"
- exit $stat
- fi
- rm -f "$depfile"
-
- if test -f "$tmpdepfile"; then # yes, the sourcefile depend on other files
- echo "$object : \\" > "$depfile"
-
- # Clip off the initial element (the dependent). Don't try to be
- # clever and replace this with sed code, as IRIX sed won't handle
- # lines with more than a fixed number of characters (4096 in
- # IRIX 6.2 sed, 8192 in IRIX 6.5). We also remove comment lines;
- # the IRIX cc adds comments like `#:fec' to the end of the
- # dependency line.
- tr ' ' '
-' < "$tmpdepfile" \
- | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' | \
- tr '
-' ' ' >> "$depfile"
- echo >> "$depfile"
-
- # The second pass generates a dummy entry for each header file.
- tr ' ' '
-' < "$tmpdepfile" \
- | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' -e 's/$/:/' \
- >> "$depfile"
- else
- # The sourcefile does not contain any dependencies, so just
- # store a dummy comment line, to avoid errors with the Makefile
- # "include basename.Plo" scheme.
- echo "#dummy" > "$depfile"
- fi
- rm -f "$tmpdepfile"
- ;;
-
-aix)
- # The C for AIX Compiler uses -M and outputs the dependencies
- # in a .u file. In older versions, this file always lives in the
- # current directory. Also, the AIX compiler puts `$object:' at the
- # start of each line; $object doesn't have directory information.
- # Version 6 uses the directory in both cases.
- dir=`echo "$object" | sed -e 's|/[^/]*$|/|'`
- test "x$dir" = "x$object" && dir=
- base=`echo "$object" | sed -e 's|^.*/||' -e 's/\.o$//' -e 's/\.lo$//'`
- if test "$libtool" = yes; then
- tmpdepfile1=$dir$base.u
- tmpdepfile2=$base.u
- tmpdepfile3=$dir.libs/$base.u
- "$@" -Wc,-M
- else
- tmpdepfile1=$dir$base.u
- tmpdepfile2=$dir$base.u
- tmpdepfile3=$dir$base.u
- "$@" -M
- fi
- stat=$?
-
- if test $stat -eq 0; then :
- else
- rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3"
- exit $stat
- fi
-
- for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3"
- do
- test -f "$tmpdepfile" && break
- done
- if test -f "$tmpdepfile"; then
- # Each line is of the form `foo.o: dependent.h'.
- # Do two passes, one to just change these to
- # `$object: dependent.h' and one to simply `dependent.h:'.
- sed -e "s,^.*\.[a-z]*:,$object:," < "$tmpdepfile" > "$depfile"
- # That's a tab and a space in the [].
- sed -e 's,^.*\.[a-z]*:[ ]*,,' -e 's,$,:,' < "$tmpdepfile" >> "$depfile"
- else
- # The sourcefile does not contain any dependencies, so just
- # store a dummy comment line, to avoid errors with the Makefile
- # "include basename.Plo" scheme.
- echo "#dummy" > "$depfile"
- fi
- rm -f "$tmpdepfile"
- ;;
-
-icc)
- # Intel's C compiler understands `-MD -MF file'. However on
- # icc -MD -MF foo.d -c -o sub/foo.o sub/foo.c
- # ICC 7.0 will fill foo.d with something like
- # foo.o: sub/foo.c
- # foo.o: sub/foo.h
- # which is wrong. We want:
- # sub/foo.o: sub/foo.c
- # sub/foo.o: sub/foo.h
- # sub/foo.c:
- # sub/foo.h:
- # ICC 7.1 will output
- # foo.o: sub/foo.c sub/foo.h
- # and will wrap long lines using \ :
- # foo.o: sub/foo.c ... \
- # sub/foo.h ... \
- # ...
-
- "$@" -MD -MF "$tmpdepfile"
- stat=$?
- if test $stat -eq 0; then :
- else
- rm -f "$tmpdepfile"
- exit $stat
- fi
- rm -f "$depfile"
- # Each line is of the form `foo.o: dependent.h',
- # or `foo.o: dep1.h dep2.h \', or ` dep3.h dep4.h \'.
- # Do two passes, one to just change these to
- # `$object: dependent.h' and one to simply `dependent.h:'.
- sed "s,^[^:]*:,$object :," < "$tmpdepfile" > "$depfile"
- # Some versions of the HPUX 10.20 sed can't process this invocation
- # correctly. Breaking it into two sed invocations is a workaround.
- sed 's,^[^:]*: \(.*\)$,\1,;s/^\\$//;/^$/d;/:$/d' < "$tmpdepfile" |
- sed -e 's/$/ :/' >> "$depfile"
- rm -f "$tmpdepfile"
- ;;
-
-hp2)
- # The "hp" stanza above does not work with aCC (C++) and HP's ia64
- # compilers, which have integrated preprocessors. The correct option
- # to use with these is +Maked; it writes dependencies to a file named
- # 'foo.d', which lands next to the object file, wherever that
- # happens to be.
- # Much of this is similar to the tru64 case; see comments there.
- dir=`echo "$object" | sed -e 's|/[^/]*$|/|'`
- test "x$dir" = "x$object" && dir=
- base=`echo "$object" | sed -e 's|^.*/||' -e 's/\.o$//' -e 's/\.lo$//'`
- if test "$libtool" = yes; then
- tmpdepfile1=$dir$base.d
- tmpdepfile2=$dir.libs/$base.d
- "$@" -Wc,+Maked
- else
- tmpdepfile1=$dir$base.d
- tmpdepfile2=$dir$base.d
- "$@" +Maked
- fi
- stat=$?
- if test $stat -eq 0; then :
- else
- rm -f "$tmpdepfile1" "$tmpdepfile2"
- exit $stat
- fi
-
- for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2"
- do
- test -f "$tmpdepfile" && break
- done
- if test -f "$tmpdepfile"; then
- sed -e "s,^.*\.[a-z]*:,$object:," "$tmpdepfile" > "$depfile"
- # Add `dependent.h:' lines.
- sed -ne '2,${
- s/^ *//
- s/ \\*$//
- s/$/:/
- p
- }' "$tmpdepfile" >> "$depfile"
- else
- echo "#dummy" > "$depfile"
- fi
- rm -f "$tmpdepfile" "$tmpdepfile2"
- ;;
-
-tru64)
- # The Tru64 compiler uses -MD to generate dependencies as a side
- # effect. `cc -MD -o foo.o ...' puts the dependencies into `foo.o.d'.
- # At least on Alpha/Redhat 6.1, Compaq CCC V6.2-504 seems to put
- # dependencies in `foo.d' instead, so we check for that too.
- # Subdirectories are respected.
- dir=`echo "$object" | sed -e 's|/[^/]*$|/|'`
- test "x$dir" = "x$object" && dir=
- base=`echo "$object" | sed -e 's|^.*/||' -e 's/\.o$//' -e 's/\.lo$//'`
-
- if test "$libtool" = yes; then
- # With Tru64 cc, shared objects can also be used to make a
- # static library. This mechanism is used in libtool 1.4 series to
- # handle both shared and static libraries in a single compilation.
- # With libtool 1.4, dependencies were output in $dir.libs/$base.lo.d.
- #
- # With libtool 1.5 this exception was removed, and libtool now
- # generates 2 separate objects for the 2 libraries. These two
- # compilations output dependencies in $dir.libs/$base.o.d and
- # in $dir$base.o.d. We have to check for both files, because
- # one of the two compilations can be disabled. We should prefer
- # $dir$base.o.d over $dir.libs/$base.o.d because the latter is
- # automatically cleaned when .libs/ is deleted, while ignoring
- # the former would cause a distcleancheck panic.
- tmpdepfile1=$dir.libs/$base.lo.d # libtool 1.4
- tmpdepfile2=$dir$base.o.d # libtool 1.5
- tmpdepfile3=$dir.libs/$base.o.d # libtool 1.5
- tmpdepfile4=$dir.libs/$base.d # Compaq CCC V6.2-504
- "$@" -Wc,-MD
- else
- tmpdepfile1=$dir$base.o.d
- tmpdepfile2=$dir$base.d
- tmpdepfile3=$dir$base.d
- tmpdepfile4=$dir$base.d
- "$@" -MD
- fi
-
- stat=$?
- if test $stat -eq 0; then :
- else
- rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" "$tmpdepfile4"
- exit $stat
- fi
-
- for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" "$tmpdepfile4"
- do
- test -f "$tmpdepfile" && break
- done
- if test -f "$tmpdepfile"; then
- sed -e "s,^.*\.[a-z]*:,$object:," < "$tmpdepfile" > "$depfile"
- # That's a tab and a space in the [].
- sed -e 's,^.*\.[a-z]*:[ ]*,,' -e 's,$,:,' < "$tmpdepfile" >> "$depfile"
- else
- echo "#dummy" > "$depfile"
- fi
- rm -f "$tmpdepfile"
- ;;
-
-#nosideeffect)
- # This comment above is used by automake to tell side-effect
- # dependency tracking mechanisms from slower ones.
-
-dashmstdout)
- # Important note: in order to support this mode, a compiler *must*
- # always write the preprocessed file to stdout, regardless of -o.
- "$@" || exit $?
-
- # Remove the call to Libtool.
- if test "$libtool" = yes; then
- while test "X$1" != 'X--mode=compile'; do
- shift
- done
- shift
- fi
-
- # Remove `-o $object'.
- IFS=" "
- for arg
- do
- case $arg in
- -o)
- shift
- ;;
- $object)
- shift
- ;;
- *)
- set fnord "$@" "$arg"
- shift # fnord
- shift # $arg
- ;;
- esac
- done
-
- test -z "$dashmflag" && dashmflag=-M
- # Require at least two characters before searching for `:'
- # in the target name. This is to cope with DOS-style filenames:
- # a dependency such as `c:/foo/bar' could be seen as target `c' otherwise.
- "$@" $dashmflag |
- sed 's:^[ ]*[^: ][^:][^:]*\:[ ]*:'"$object"'\: :' > "$tmpdepfile"
- rm -f "$depfile"
- cat < "$tmpdepfile" > "$depfile"
- tr ' ' '
-' < "$tmpdepfile" | \
-## Some versions of the HPUX 10.20 sed can't process this invocation
-## correctly. Breaking it into two sed invocations is a workaround.
- sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' | sed -e 's/$/ :/' >> "$depfile"
- rm -f "$tmpdepfile"
- ;;
-
-dashXmstdout)
- # This case only exists to satisfy depend.m4. It is never actually
- # run, as this mode is specially recognized in the preamble.
- exit 1
- ;;
-
-makedepend)
- "$@" || exit $?
- # Remove any Libtool call
- if test "$libtool" = yes; then
- while test "X$1" != 'X--mode=compile'; do
- shift
- done
- shift
- fi
- # X makedepend
- shift
- cleared=no eat=no
- for arg
- do
- case $cleared in
- no)
- set ""; shift
- cleared=yes ;;
- esac
- if test $eat = yes; then
- eat=no
- continue
- fi
- case "$arg" in
- -D*|-I*)
- set fnord "$@" "$arg"; shift ;;
- # Strip any option that makedepend may not understand. Remove
- # the object too, otherwise makedepend will parse it as a source file.
- -arch)
- eat=yes ;;
- -*|$object)
- ;;
- *)
- set fnord "$@" "$arg"; shift ;;
- esac
- done
- obj_suffix=`echo "$object" | sed 's/^.*\././'`
- touch "$tmpdepfile"
- ${MAKEDEPEND-makedepend} -o"$obj_suffix" -f"$tmpdepfile" "$@"
- rm -f "$depfile"
- cat < "$tmpdepfile" > "$depfile"
- sed '1,2d' "$tmpdepfile" | tr ' ' '
-' | \
-## Some versions of the HPUX 10.20 sed can't process this invocation
-## correctly. Breaking it into two sed invocations is a workaround.
- sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' | sed -e 's/$/ :/' >> "$depfile"
- rm -f "$tmpdepfile" "$tmpdepfile".bak
- ;;
-
-cpp)
- # Important note: in order to support this mode, a compiler *must*
- # always write the preprocessed file to stdout.
- "$@" || exit $?
-
- # Remove the call to Libtool.
- if test "$libtool" = yes; then
- while test "X$1" != 'X--mode=compile'; do
- shift
- done
- shift
- fi
-
- # Remove `-o $object'.
- IFS=" "
- for arg
- do
- case $arg in
- -o)
- shift
- ;;
- $object)
- shift
- ;;
- *)
- set fnord "$@" "$arg"
- shift # fnord
- shift # $arg
- ;;
- esac
- done
-
- "$@" -E |
- sed -n -e '/^# [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' \
- -e '/^#line [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' |
- sed '$ s: \\$::' > "$tmpdepfile"
- rm -f "$depfile"
- echo "$object : \\" > "$depfile"
- cat < "$tmpdepfile" >> "$depfile"
- sed < "$tmpdepfile" '/^$/d;s/^ //;s/ \\$//;s/$/ :/' >> "$depfile"
- rm -f "$tmpdepfile"
- ;;
-
-msvisualcpp)
- # Important note: in order to support this mode, a compiler *must*
- # always write the preprocessed file to stdout.
- "$@" || exit $?
-
- # Remove the call to Libtool.
- if test "$libtool" = yes; then
- while test "X$1" != 'X--mode=compile'; do
- shift
- done
- shift
- fi
-
- IFS=" "
- for arg
- do
- case "$arg" in
- -o)
- shift
- ;;
- $object)
- shift
- ;;
- "-Gm"|"/Gm"|"-Gi"|"/Gi"|"-ZI"|"/ZI")
- set fnord "$@"
- shift
- shift
- ;;
- *)
- set fnord "$@" "$arg"
- shift
- shift
- ;;
- esac
- done
- "$@" -E 2>/dev/null |
- sed -n '/^#line [0-9][0-9]* "\([^"]*\)"/ s::\1:p' | $cygpath_u | sort -u > "$tmpdepfile"
- rm -f "$depfile"
- echo "$object : \\" > "$depfile"
- sed < "$tmpdepfile" -n -e 's% %\\ %g' -e '/^\(.*\)$/ s:: \1 \\:p' >> "$depfile"
- echo " " >> "$depfile"
- sed < "$tmpdepfile" -n -e 's% %\\ %g' -e '/^\(.*\)$/ s::\1\::p' >> "$depfile"
- rm -f "$tmpdepfile"
- ;;
-
-msvcmsys)
- # This case exists only to let depend.m4 do its work. It works by
- # looking at the text of this script. This case will never be run,
- # since it is checked for above.
- exit 1
- ;;
-
-none)
- exec "$@"
- ;;
-
-*)
- echo "Unknown depmode $depmode" 1>&2
- exit 1
- ;;
-esac
-
-exit 0
-
-# Local Variables:
-# mode: shell-script
-# sh-indentation: 2
-# eval: (add-hook 'write-file-hooks 'time-stamp)
-# time-stamp-start: "scriptversion="
-# time-stamp-format: "%:y-%02m-%02d.%02H"
-# time-stamp-time-zone: "UTC"
-# time-stamp-end: "; # UTC"
-# End:
diff --git a/dns++.pc.in b/dns++.pc.in
new file mode 100644
index 0000000..d2a7e06
--- /dev/null
+++ b/dns++.pc.in
@@ -0,0 +1,11 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: dns++
+Description: BIND 10 DNS library
+Version: @PACKAGE_VERSION@
+Requires: botan-1.8
+Cflags: -I${includedir}/@PACKAGE_NAME@
+Libs: -L${libdir} -ldns++ -lcryptolink -lutil -lexceptions -lm
diff --git a/doc/.gitignore b/doc/.gitignore
index cf437ce..fd8742e 100644
--- a/doc/.gitignore
+++ b/doc/.gitignore
@@ -1 +1,2 @@
/version.ent
+html
diff --git a/doc/Doxyfile b/doc/Doxyfile
index c9c5c5a..8730ae4 100644
--- a/doc/Doxyfile
+++ b/doc/Doxyfile
@@ -25,13 +25,17 @@ DOXYFILE_ENCODING = UTF-8
# The PROJECT_NAME tag is a single word (or a sequence of words surrounded
# by quotes) that should identify the project.
-PROJECT_NAME = BIND
+PROJECT_NAME = BIND10
# The PROJECT_NUMBER tag can be used to enter a project or revision number.
# This could be handy for archiving the generated documentation or
# if some version control system is used.
-PROJECT_NUMBER = 10.0.0
+# Currently this variable is overwritten (see devel target in Makefile.am)
+# If the number of parameters to overwrite increases, we should generate
+# Doxyfile (rename it to Doxyfile.in and generate during configure phase)
+
+PROJECT_NUMBER =
# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute)
# base path where the generated documentation will be put.
@@ -47,7 +51,7 @@ OUTPUT_DIRECTORY = html
# source files, where putting all generated files in the same directory would
# otherwise cause performance problems for the file system.
-CREATE_SUBDIRS = NO
+CREATE_SUBDIRS = YES
# The OUTPUT_LANGUAGE tag is used to specify the language in which all
# documentation generated by doxygen is written. Doxygen will use this
@@ -281,7 +285,7 @@ TYPEDEF_HIDES_STRUCT = NO
# causing a significant performance penality.
# If the system has enough physical memory increasing the cache will improve the
# performance by keeping more symbols in memory. Note that the value works on
-# a logarithmic scale so increasing the size by one will rougly double the
+# a logarithmic scale so increasing the size by one will roughly double the
# memory usage. The cache size is given by this formula:
# 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0,
# corresponding to a cache size of 2^16 = 65536 symbols
@@ -574,7 +578,8 @@ INPUT = ../src/lib/exceptions ../src/lib/cc \
../src/lib/log/compiler ../src/lib/asiolink/ ../src/lib/nsas \
../src/lib/testutils ../src/lib/cache ../src/lib/server_common/ \
../src/bin/sockcreator/ ../src/lib/util/ ../src/lib/util/io/ \
- ../src/lib/resolve ../src/lib/acl ../src/bin/dhcp6 ../src/lib/dhcp
+ ../src/lib/resolve ../src/lib/acl ../src/bin/dhcp6 ../src/lib/dhcp \
+ ../src/bin/dhcp4 devel
# This tag can be used to specify the character encoding of the source files
# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is
@@ -591,7 +596,7 @@ INPUT_ENCODING = UTF-8
# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx
# *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py *.f90
-FILE_PATTERNS =
+FILE_PATTERNS = *.c *.cc *.h *.hpp *.dox
# The RECURSIVE tag can be used to turn specify whether or not subdirectories
# should be searched for input files as well. Possible values are YES and NO.
@@ -651,7 +656,7 @@ EXAMPLE_RECURSIVE = NO
# directories that contain image that are included in the documentation (see
# the \image command).
-IMAGE_PATH =
+IMAGE_PATH = ../doc/images
# The INPUT_FILTER tag can be used to specify a program that doxygen should
# invoke to filter for each input file. Doxygen will invoke the filter program
@@ -773,7 +778,7 @@ GENERATE_HTML = YES
# If a relative path is entered the value of OUTPUT_DIRECTORY will be
# put in front of it. If left blank `html' will be used as the default path.
-HTML_OUTPUT = cpp
+HTML_OUTPUT = ../html
# The HTML_FILE_EXTENSION tag can be used to specify the file extension for
# each generated HTML page (for example: .htm,.php,.asp). If it is left blank
@@ -954,7 +959,7 @@ ENUM_VALUES_PER_LINE = 4
# JavaScript, DHTML, CSS and frames is required (i.e. any modern browser).
# Windows users are probably better off using the HTML help feature.
-GENERATE_TREEVIEW = NO
+GENERATE_TREEVIEW = YES
# By enabling USE_INLINE_TREES, doxygen will generate the Groups, Directories,
# and Class Hierarchy pages using a tree view instead of an ordered list.
@@ -965,7 +970,7 @@ USE_INLINE_TREES = NO
# used to set the initial width (in pixels) of the frame in which the tree
# is shown.
-TREEVIEW_WIDTH = 250
+TREEVIEW_WIDTH = 180
# Use this tag to change the font size of Latex formulas included
# as images in the HTML documentation. The default is 10. Note that
diff --git a/doc/Makefile.am b/doc/Makefile.am
index 31d0cfa..7642220 100644
--- a/doc/Makefile.am
+++ b/doc/Makefile.am
@@ -1,3 +1,16 @@
SUBDIRS = guide
EXTRA_DIST = version.ent.in
+
+devel:
+ mkdir -p html
+ (cat Doxyfile; echo PROJECT_NUMBER=$(PACKAGE_VERSION)) | doxygen - > html/doxygen.log 2> html/doxygen-error.log
+ echo `grep -i ": warning:" html/doxygen-error.log | wc -l` warnings/errors detected.
+
+clean:
+ rm -rf html
+
+# That's a bit of a hack, but we are making sure that devel target
+# is always valid. The alternative is to make devel depend on all
+# *.cc *.h files in the whole tree.
+.PHONY: devel
diff --git a/doc/devel/01-dns.dox b/doc/devel/01-dns.dox
new file mode 100644
index 0000000..e72dbbd
--- /dev/null
+++ b/doc/devel/01-dns.dox
@@ -0,0 +1,14 @@
+/**
+ *
+ * @page dns BIND10 DNS
+ *
+ * @section dns-auth b10-auth
+ *
+ * @todo: Describe b10-auth here.
+ *
+ * @section b10-cfgmgr b10-cfgmgr Overview
+ *
+ * @todo: Descibe b10-cfgmgr here.
+ *
+ *
+ */
diff --git a/doc/devel/02-dhcp.dox b/doc/devel/02-dhcp.dox
new file mode 100644
index 0000000..7ebd9b2
--- /dev/null
+++ b/doc/devel/02-dhcp.dox
@@ -0,0 +1,117 @@
+/**
+ * @page dhcpv4 DHCPv4 Server Component
+ *
+ * BIND10 offers DHCPv4 server implementation. It is implemented as
+ * b10-dhcp4 component. Its primary code is located in
+ * isc::dhcp::Dhcpv4Srv class. It uses \ref libdhcp extensively,
+ * especially isc::dhcp::Pkt4, isc::dhcp::Option and
+ * isc::dhcp::IfaceMgr classes. Currently this code offers skeleton
+ * functionality, i.e. it is able to receive and process incoming
+ * requests and trasmit responses. However, it does not have database
+ * management, so it returns only one, hardcoded lease to whoever asks
+ * for it.
+ *
+ * DHCPv4 server component does not support direct traffic (relayed
+ * only), as support for transmission to hosts without IPv4 address
+ * assigned is not implemented in IfaceMgr yet.
+ *
+ * DHCPv4 server component does not listen to BIND10 message queue.
+ *
+ * DHCPv4 server component does not use BIND10 logging yet.
+ *
+ * DHCPv4 server component is not integrated with boss yet.
+ *
+ * @page dhcpv6 DHCPv6 Server Component
+ *
+ * BIND10 offers DHCPv6 server implementation. It is implemented as
+ * b10-dhcp6 component. Its primary code is located in
+ * isc::dhcp::Dhcpv6Srv class. It uses \ref libdhcp extensively,
+ * especially lib::dhcp::Pkt6, isc::dhcp::Option and
+ * isc::dhcp::IfaceMgr classes. Currently this code offers skeleton
+ * functionality, i.e. it is able to receive and process incoming
+ * requests and trasmit responses. However, it does not have database
+ * management, so it returns only one, hardcoded lease to whoever asks
+ * for it.
+ *
+ * DHCPv6 server component does not support relayed traffic yet, as
+ * support for relay decapsulation is not implemented yet.
+ *
+ * DHCPv6 server component does not listen to BIND10 message queue.
+ *
+ * DHCPv6 server component does not use BIND10 logging yet.
+ *
+ * DHCPv6 server component is not integrated with boss yet.
+ *
+ * @page libdhcp libdhcp++ library
+ *
+ * @section libdhcpIntro Libdhcp++ Introduction
+ *
+ * libdhcp++ is an all-purpose DHCP-manipulation library, written in
+ * C++. It offers packet parsing and assembly, DHCPv4 and DHCPv6
+ * options parsing and ssembly, interface detection (currently on
+ * Linux systems only) and socket operations. Following classes are
+ * implemented:
+ *
+ * - isc::dhcp::Pkt4 - represents DHCPv4 packet.
+ * - isc::dhcp::Pkt6 - represents DHCPv6 packet.
+ *
+ * There are two pointer types defined: Pkt4Ptr and Pkt6Ptr. They are
+ * smart pointer and are using boost::shared_ptr. There are not const
+ * versions defined, as we assume that hooks can modify any aspect of
+ * the packet at almost any stage of processing.
+ *
+ * Both packets use collection of Option objects to represent DHCPv4
+ * and DHCPv6 options. The base class -- Option -- can be used to
+ * represent generic option that contains collection of
+ * bytes. Depending on if the option is instantiated as v4 or v6
+ * option, it will adjust its header (DHCPv4 options use 1 octet for
+ * type and 1 octet for length, while DHCPv6 options use 2 bytes for
+ * each).
+ *
+ * There are many specialized classes that are intended to handle options with
+ * specific content:
+ * - isc::dhcp::Option4AddrLst -- DHCPv4 option, contains one or more IPv4 addresses;
+ * - isc::dhcp::Option6AddrLst -- DHCPv6 option, contains one or more IPv6 addresses;
+ * - isc::dhcp::Option6IAAddr -- DHCPv6 option, represents IAADDR_OPTION (an option that
+ * contains IPv6 address with extra parameters);
+ * - isc::dhcp::Option6IA -- DHCPv6 option used to store IA_NA and its suboptions.
+ *
+ * All options can store sub-options (i.e. options that are stored within option
+ * rather than in a message directly). This functionality is commonly used in
+ * DHCPv6, but is rarely used in DHCPv4. isc::dhcp::Option::addOption(),
+ * isc::dhcp::Option::delOption(), isc::dhcp::Option::getOption() can be used
+ * for that purpose.
+ *
+ * @section lidhcpIfaceMgr Interface Manager
+ *
+ * Interface Manager (or IfaceMgr) is an abstraction layer about low-level
+ * network operations. In particlar, it provides information about existing
+ * network interfaces See isc::dhcp::IfaceMgr::Iface class and
+ * isc::dhcp::IfaceMgr::detectIfaces() and isc::dhcp::IfaceMgr::getIface().
+ *
+ * Currently there is interface detection is implemented in Linux only. There
+ * are plans to implement such support for other OSes, but they remain low
+ * priority for now.
+ *
+ * Generic parts of the code are isc::dhcp::IfaceMgr class in
+ * src/lib/dhcp/iface_mgr.cc file. OS-specific code is located in separate
+ * files, e.g. iface_mgr_linux.cc. Such separation should be maintained when
+ * additional code will be developed.
+ *
+ * For systems that interface detection is not supported on, there is a stub
+ * mechanism implemented. It assumes that interface name is read from a text
+ * file. This is a temporary solution and will be removed as soon as proper
+ * interface detection is implemented. It is not going to be developed further.
+ * To use this feature, store interfaces.txt file. It uses a simple syntax.
+ * Each line represents an interface name, followed by IPv4 or IPv6 address
+ * that follows it. This is usually link-local IPv6 address that the server
+ * should bind to. In theory this mechanism also supports IPv4, but it was
+ * never tested. The code currently supports only a single interface defined
+ * that way.
+ *
+ * Another useful methods are dedicated to transmission
+ * (isc::dhcp::IfaceMgr::send(), 2 overloads) and reception
+ * (isc::dhcp::IfaceMgr::receive4() and isc::dhcp::IfaceMgr::receive6()).
+ * Note that receive4() and receive6() methods may return NULL, e.g.
+ * when timeout is reached or if dhcp daemon receives a signal.
+ */
\ No newline at end of file
diff --git a/doc/devel/mainpage.dox b/doc/devel/mainpage.dox
new file mode 100644
index 0000000..a472add
--- /dev/null
+++ b/doc/devel/mainpage.dox
@@ -0,0 +1,36 @@
+/**
+ *
+ * @mainpage BIND10 Developer's Guide
+ *
+ * Welcome to BIND10 Developer's Guide. This documentation is addressed
+ * at existing and prospecting developers and programmers, who would like
+ * to gain insight into internal workings of BIND 10. It could also be useful
+ * for existing and prospective contributors.
+ *
+ * If you are a user or system administrator, rather than software engineer,
+ * you should read <a href="http://bind10.isc.org/docs/bind10-guide.html">BIND10
+ * Guide (Administrator Reference for BIND10)</a> instead.
+ *
+ * Regardless of your field of expertise, you are encouraged to visit
+ * <a href="http://bind10.isc.org/">BIND10 webpage (http://bind10.isc.org)</a>
+ *
+ * @section DNS
+ * - @subpage DataScrubbing
+ *
+ * @section DHCP
+ * - @subpage dhcpv4
+ * - @subpage dhcpv6
+ * - @subpage libdhcp
+ *
+ * @section misc Miscellaneous topics
+ * - @subpage LoggingApi
+ * - @subpage LoggingApiOverview
+ * - @subpage LoggingApiLoggerNames
+ * - @subpage LoggingApiLoggingMessages
+ * - @subpage SocketSessionUtility
+ * - <a href="./doxygen-error.log">Documentation warnings and errors</a>
+ *
+ * @todo: Move this logo to the right (and possibly up). Not sure what
+ * is the best way to do it in Doxygen, without using CSS hacks.
+ * @image html isc-logo.png
+ */
\ No newline at end of file
diff --git a/doc/guide/bind10-guide.html b/doc/guide/bind10-guide.html
index b879af5..abead8b 100644
--- a/doc/guide/bind10-guide.html
+++ b/doc/guide/bind10-guide.html
@@ -1,4 +1,4 @@
-<html><head><meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"><title>BIND 10 Guide</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 framework that features Domain Name System (DNS) suite and Dynamic Host Configuration Protocol (DHCP) servers managed by Internet Systems Consortium (ISC). It includes DNS libraries, modular components for controlling authoritative and recursive DNS servers, and experimental DHCPv4 and DHCPv6 servers. This is the reference guide for BIND 10 version 20120405. 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 ."></head><body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div class="book" title="BIND 10 Guide"><div class="titlepage"><div><div><h1 class="title"><a name="idm146
04752"></a>BIND 10 Guide</h1></div><div><h2 class="subtitle">Administrator Reference for BIND 10</h2></div><div><p class="releaseinfo">This is the reference guide for BIND 10 version
+<html><head><meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"><title>BIND 10 Guide</title><link rel="stylesheet" type="text/css" href="./bind10-guide.css"><meta name="generator" content="DocBook XSL Stylesheets V1.76.1"><meta name="description" content="BIND 10 is a framework that features Domain Name System (DNS) suite and Dynamic Host Configuration Protocol (DHCP) servers managed by Internet Systems Consortium (ISC). It includes DNS libraries, modular components for controlling authoritative and recursive DNS servers, and experimental DHCPv4 and DHCPv6 servers. This is the reference guide for BIND 10 version 20120405. 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 ."></head><body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div class="book" title="BIND 10 Guide"><div class="titlepage"><div><div><h1 class="title"><a name="id3843
20"></a>BIND 10 Guide</h1></div><div><h2 class="subtitle">Administrator Reference for BIND 10</h2></div><div><p class="releaseinfo">This is the reference guide for BIND 10 version
20120405.</p></div><div><p class="copyright">Copyright © 2010-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 framework that features Domain Name System
(DNS) suite and Dynamic Host Configuration Protocol (DHCP)
servers managed by Internet Systems Consortium (ISC). It
@@ -10,9 +10,9 @@
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="#idp17128">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="#idp20200">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="#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="#idp70704">2.3.1. Download Tar File</a></span></dt><dt><span class="section"><a href="#idp72200">2.3.2. Retrieve from Git</a></span></dt><dt><span class="section"><a href="#idp77232">2.3.3. Configure before the build</a></span></dt><dt><span class="section"><a href="#idp84464">2.3.4. Build</a></span></dt><dt><span class="section"><a href="#idp85536">2.3.5. Install</a></span></dt><dt><span class="section"><a href="#idp87192">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. Configuration of started processes</a></span></dt></dl></dd><dt><sp
an 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="#idp156456">8.1. Server Configurations</a></span></dt><dt><span class="section"><a href="#idp174000">8.2. Data Source Backends</a></span></dt><dd><dl><dt><span class="section"><a href="#in-memory-datasource">8.2.1. In-memory Data Source</a></span></dt></dl></dd><dt><span class="section"><a href="#idp185968">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="#idp196456">9.1. Configuration for Incoming Zone Transfers</a></span></dt><dt><span class="section"><a href="#idp199496">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="#idp209248">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="#idp228136">11.1. Access Control</a></span></dt><dt><span class="section"><a href="#idp237328">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 class="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="#idp295312">16.1. Logging configuration</a></span></dt><dd><dl><dt><span class="section"><a href="#idp296304">16.1.1. Loggers</a></span></dt><dt><span class="section"><a href="#idp317512">16.1.2. Output Options</a></span></dt><dt><span class="section"><a href="#idp331704">16.1.3. Example session</a></span></dt></dl></dd><dt><span class="section"><a href="#idp349208">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="#idp106648"></a></dt></dl></div><div class="preface" title="Preface"><div class="titlepage"><div><div><h2 class="title"><a name="idp17128"></a>Preface</h2></div></div></div><div class="toc"><p><b>Table of C
ontents</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="#id383993">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="#id384039">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="#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="#id384785">2.3.1. Download Tar File</a></span></dt><dt><span class="section"><a href="#id384808">2.3.2. Retrieve from Git</a></span></dt><dt><span class="section"><a href="#id384882">2.3.3. Configure before the build</a></span></dt><dt><span class="section"><a href="#id384993">2.3.4. Build</a></span></dt><dt><span class="section"><a href="#id385010">2.3.5. Install</a></span></dt><dt><span class="section"><a href="#id385036">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. Configuration of started processes</a></span></dt></dl></dd><dt><sp
an 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="#id386045">8.1. Server Configurations</a></span></dt><dt><span class="section"><a href="#id386324">8.2. Data Source Backends</a></span></dt><dd><dl><dt><span class="section"><a href="#in-memory-datasource">8.2.1. In-memory Data Source</a></span></dt><dt><span class="section"><a href="#in-memory-datasource-with-sqlite3-backend">8.2.2. In-memory Data Source With SQLite3 Backend</a></span></d
t><dt><span class="section"><a href="#in-memory-datasource-loading">8.2.3. Reloading an In-memory Data Source</a></span></dt><dt><span class="section"><a href="#in-memory-datasource-disabling">8.2.4. Disabling In-memory Data Sources</a></span></dt></dl></dd><dt><span class="section"><a href="#id386600">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="#id386752">9.1. Configuration for Incoming Zone Transfers</a></span></dt><dt><span class="section"><a href="#id386796">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="#id386939">9.4. Trigger an Incoming Zone Transfer Manually</a></span></dt><dt><span class="section"><a href="#id386970">9.5. Incoming Transfers with In-memory Datasource</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="#id387254">11.1. Access Control</a></span></dt><dt><span class="section"><a href="#id387393">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 class="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="#id388289">16.1. Logging configuration</a></span></dt><dd><dl><dt><span class="section"><a href="#id388304">16.1.1. Loggers</a></span></dt><dt><span class="section"><a href="#id388607">16.1.2. Output Options</a></span></dt><d
t><span class="section"><a href="#id388815">16.1.3. Example session</a></span></dt></dl></dd><dt><span class="section"><a href="#id389072">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="#id385324"></a></dt></dl></div><div class="preface" title="Preface"><div class="titlepage"><div><div><h2 class="title"><a name="id383993"></a>Preface</h2></div></div></div><div class="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="#idp20200">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>
+ 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="#id384039">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
interfaces, and DNS tools.
BIND 10 is a rewrite of BIND 9. BIND 10 is written in C++ and Python
@@ -23,7 +23,7 @@
</p><p>
This guide covers the experimental prototype of
BIND 10 version 20120405.
- </p><div class="section" title="1.1. Supported Platforms"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="idp20200"></a>1.1. Supported Platforms</h2></div></div></div><p>
+ </p><div class="section" title="1.1. Supported Platforms"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="id384039"></a>1.1. Supported Platforms</h2></div></div></div><p>
BIND 10 builds have been tested on Debian GNU/Linux 5 and unstable,
Ubuntu 9.10, NetBSD 5, Solaris 10, FreeBSD 7 and 8, CentOS
Linux 5.3, and MacOS 10.6.
@@ -162,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="#idp70704">2.3.1. Download Tar File</a></span></dt><dt><span class="section"><a href="#idp72200">2.3.2. Retrieve from Git</a></span></dt><dt><span class="section"><a href="#idp77232">2.3.3. Configure before the build</a></span></dt><dt><span class="section"><a href="#idp84464">2.3.4. Build</a></span></dt><dt><span class="section"><a href="#idp85536">2.3.5. Install</a></span></dt><dt><span class="section"><a href="#idp87192">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="#id384785">2.3.1. Download Tar File</a></span></dt><dt><span class="section"><a href="#id384808">2.3.2. Retrieve from Git</a></span></dt><dt><span class="section"><a href="#id384882">2.3.3. Configure before the build</a></span></dt><dt><span class="section"><a href="#id384993">2.3.4. Build</a></span></dt><dt><span class="section"><a href="#id385010">2.3.5. Install</a></span></dt><dt><span class="section"><a href="#id385036">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>
@@ -224,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="idp70704"></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="id384785"></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="idp72200"></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="id384808"></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.
@@ -265,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="idp77232"></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="id384882"></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:
@@ -296,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="idp84464"></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="id384993"></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="idp85536"></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="id385010"></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="idp87192"></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="id385036"></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> —
@@ -360,9 +360,8 @@
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 administration tools to
- communicate with the system,
- <span class="command"><strong>b10-stats</strong></span> for statistics collection, and
- <span class="command"><strong>b10-stats-httpd</strong></span> for statistics reporting.
+ communicate with the system, and
+ <span class="command"><strong>b10-stats</strong></span> for statistics collection.
</p><div class="section" title="3.1. Starting BIND 10"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="start"></a>3.1. Starting BIND 10</h2></div></div></div><p>
To start the BIND 10 service, simply run <span class="command"><strong>bind10</strong></span>.
Run it with the <code class="option">--verbose</code> switch to
@@ -398,7 +397,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="idp106648"></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="id385324"></a><p class="title"><b>Table 3.1. </b></p><div class="table-contents"><table border="1"><colgroup><col align="left" class="component"><col align="left" class="special"><col align="left" class="description"></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>
@@ -626,12 +625,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="#idp156456">8.1. Server Configurations</a></span></dt><dt><span class="section"><a href="#idp174000">8.2. Data Source Backends</a></span></dt><dd><dl><dt><span class="section"><a href="#in-memory-datasource">8.2.1. In-memory Data Source</a></span></dt></dl></dd><dt><span class="section"><a href="#idp185968">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="#id386045">8.1. Server Configurations</a></span></dt><dt><span class="section"><a href="#id386324">8.2. Data Source Backends</a></span></dt><dd><dl><dt><span class="section"><a href="#in-memory-datasource">8.2.1. In-memory Data Source</a></span></dt><dt><span class="section"><a href="#in-memory-datasource-with-sqlite3-backend">8.2.2. In-memory Data Source With SQLite3 Backend</a></span></dt><dt><span class="section"><a href="#in-memory-datasource-loading">8.2.3. Reloading an In-memory Data Source</a></span></dt><dt><span class="section"><a href="#in-memory-datasource-disabling">8.2.4. Disabling In-memory Data Sources</a></span></dt></dl></dd><dt><span class="section"><a href="#id386600">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="idp156456"></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="id386045"></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>.
@@ -650,9 +649,10 @@ This may be a temporary setting until then.
<code class="varname">class</code> to optionally select the class
(it defaults to <span class="quote">“<span class="quote">IN</span>”</span>);
and
- <code class="varname">zones</code> to define the
- <code class="varname">file</code> path name and the
- <code class="varname">origin</code> (default domain).
+ <code class="varname">zones</code> to define
+ the <code class="varname">file</code> path name,
+ the <code class="varname">filetype</code> (e.g., <code class="varname">sqlite3</code>),
+ and the <code class="varname">origin</code> (default domain).
By default, this is empty.
@@ -662,7 +662,8 @@ This may be a temporary setting until then.
Only the IN class is supported at this time.
By default, the memory data source is disabled.
Also, currently the zone file must be canonical such as
- generated by <span class="command"><strong>named-compilezone -D</strong></span>.
+ generated by <span class="command"><strong>named-compilezone -D</strong></span>, or
+ must be an SQLite3 database.
</p></div>
</dd><dt><span class="term">listen_on</span></dt><dd>
@@ -711,7 +712,7 @@ This may be a temporary setting until then.
if configured.)
</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="idp174000"></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="id386324"></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.
@@ -743,7 +744,26 @@ This may be a temporary setting until then.
The authoritative server will begin serving it immediately
after it is loaded.
- </p><p>
+ </p></div><div class="section" title="8.2.2. In-memory Data Source With SQLite3 Backend"><div class="titlepage"><div><div><h3 class="title"><a name="in-memory-datasource-with-sqlite3-backend"></a>8.2.2. In-memory Data Source With SQLite3 Backend</h3></div></div></div><p>
+
+ The following commands to <span class="command"><strong>bindctl</strong></span>
+ provide an example of configuring an in-memory data
+ source containing the <span class="quote">“<span class="quote">example.org</span>”</span> zone
+ with a SQLite3 backend file named <span class="quote">“<span class="quote">example.org.sqlite3</span>”</span>:
+
+
+
+ </p><pre class="screen">> <strong class="userinput"><code>config add Auth/datasources</code></strong>
+> <strong class="userinput"><code>config set Auth/datasources[1]/type "<code class="option">memory</code>"</code></strong>
+> <strong class="userinput"><code>config add Auth/datasources[1]/zones</code></strong>
+> <strong class="userinput"><code>config set Auth/datasources[1]/zones[0]/origin "<code class="option">example.org</code>"</code></strong>
+> <strong class="userinput"><code>config set Auth/datasources[1]/zones[0]/file "<code class="option">example.org.sqlite3</code>"</code></strong>
+> <strong class="userinput"><code>config set Auth/datasources[1]/zones[0]/filetype "<code class="option">sqlite3</code>"</code></strong>
+> <strong class="userinput"><code>config commit</code></strong></pre><p>
+
+ The authoritative server will begin serving it immediately
+ after it is loaded.
+ </p></div><div class="section" title="8.2.3. Reloading an In-memory Data Source"><div class="titlepage"><div><div><h3 class="title"><a name="in-memory-datasource-loading"></a>8.2.3. Reloading an In-memory Data Source</h3></div></div></div><p>
Use the <span class="command"><strong>Auth loadzone</strong></span> command in
<span class="command"><strong>bindctl</strong></span> to reload a changed master
file into memory; for example:
@@ -751,7 +771,7 @@ This may be a temporary setting until then.
</p><pre class="screen">> <strong class="userinput"><code>Auth loadzone origin="example.com"</code></strong>
</pre><p>
- </p><p>
+ </p></div><div class="section" title="8.2.4. Disabling In-memory Data Sources"><div class="titlepage"><div><div><h3 class="title"><a name="in-memory-datasource-disabling"></a>8.2.4. Disabling In-memory Data Sources</h3></div></div></div><p>
By default, the memory data source is disabled; it must be
configured explicitly. To disable all the in-memory zones,
specify a null list for <code class="varname">Auth/datasources</code>:
@@ -771,7 +791,7 @@ This may be a temporary setting until then.
and/or <code class="varname">zones[<em class="replaceable"><code>0</code></em>]</code>
for the relevant zone as needed.)
- </p></div></div><div class="section" title="8.3. Loading Master Zones Files"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="idp185968"></a>8.3. Loading Master Zones Files</h2></div></div></div><p>
+ </p></div></div><div class="section" title="8.3. Loading Master Zones Files"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="id386600"></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 SQLite3 data source by using the
<span class="command"><strong>b10-loadzone</strong></span> utility.
@@ -800,7 +820,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="#idp196456">9.1. Configuration for Incoming Zone Transfers</a></span></dt><dt><span class="section"><a href="#idp199496">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="#idp209248">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="#id386752">9.1. Configuration for Incoming Zone Transfers</a></span></dt><dt><span class="section"><a href="#id386796">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="#id386939">9.4. Trigger an Incoming Zone Transfer Manually</a></span></dt><dt><span class="section"><a href="#id386970">9.5. Incoming Transfers with In-memory Datasource</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
@@ -814,11 +834,7 @@ This may be a temporary setting until then.
IXFR. Due to some implementation limitations of the current
development release, however, it only tries AXFR by default,
and care should be taken to enable IXFR.
- </p><div class="note" title="Note" style="margin-left: 0.5in; margin-right: 0.5in;"><h3 class="title">Note</h3><p>
- 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="idp196456"></a>9.1. Configuration for Incoming Zone Transfers</h2></div></div></div><p>
+ </p><div class="section" title="9.1. Configuration for Incoming Zone Transfers"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="id386752"></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
@@ -834,7 +850,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="idp199496"></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="id386796"></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>
@@ -886,12 +902,24 @@ 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="idp209248"></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="id386939"></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:
</p><pre class="screen">> <strong class="userinput"><code>Xfrin retransfer zone_name="<code class="option">foo.example.org</code>" master=<code class="option">192.0.2.99</code></code></strong></pre><p>
+ </p></div><div class="section" title="9.5. Incoming Transfers with In-memory Datasource"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="id386970"></a>9.5. Incoming Transfers with In-memory Datasource</h2></div></div></div><p>
+ In the case of an incoming zone transfer, the received zone is
+ first stored in the corresponding BIND 10 datasource. In
+ case the secondary zone is served by an in-memory datasource
+ with an SQLite3 backend, <span class="command"><strong>b10-auth</strong></span> is
+ automatically sent a <code class="varname">loadzone</code> command to
+ reload the corresponding zone into memory from the backend.
+ </p><p>
+ The administrator doesn't have to do anything for
+ <span class="command"><strong>b10-auth</strong></span> to serve the new version of the
+ zone, except for the configuration such as the one described in
+ <a class="xref" href="#in-memory-datasource-with-sqlite3-backend" title="8.2.2. In-memory Data Source With SQLite3 Backend">Section 8.2.2, “In-memory Data Source With SQLite3 Backend”</a>.
</p></div></div><div class="chapter" title="Chapter 10. Outbound Zone Transfers"><div class="titlepage"><div><div><h2 class="title"><a name="xfrout"></a>Chapter 10. Outbound Zone Transfers</h2></div></div></div><p>
The <span class="command"><strong>b10-xfrout</strong></span> process is started by
<span class="command"><strong>bind10</strong></span>.
@@ -935,7 +963,7 @@ Xfrout/transfer_acl[0] {"action": "ACCEPT"} any (default)</pre><p>
TSIGs in the incoming 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.
- </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="#idp228136">11.1. Access Control</a></span></dt><dt><span class="section"><a href="#idp237328">11.2. Forwarding</a></span></dt></dl></div><p>
+ </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="#id387254">11.1. Access Control</a></span></dt><dt><span class="section"><a href="#id387393">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>.
@@ -969,7 +997,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="idp228136"></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="id387254"></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
@@ -1002,7 +1030,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="idp237328"></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="id387393"></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:
@@ -1314,7 +1342,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="#idp295312">16.1. Logging configuration</a></span></dt><dd><dl><dt><span class="section"><a href="#idp296304">16.1.1. Loggers</a></span></dt><dt><span class="section"><a href="#idp317512">16.1.2. Output Options</a></span></dt><dt><span class="section"><a href="#idp331704">16.1.3. Example session</a></span></dt></dl></dd><dt><span class="section"><a href="#idp349208">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="idp295312"></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="#id388289">16.1. Logging configuration</a></span></dt><dd><dl><dt><span class="section"><a href="#id388304">16.1.1. Loggers</a></span></dt><dt><span class="section"><a href="#id388607">16.1.2. Output Options</a></span></dt><dt><span class="section"><a href="#id388815">16.1.3. Example session</a></span></dt></dl></dd><dt><span class="section"><a href="#id389072">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="id388289"></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
@@ -1323,7 +1351,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="idp296304"></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="id388304"></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
@@ -1344,7 +1372,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="idp298552"></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="id388335"></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,
@@ -1417,7 +1445,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="idp308336"></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="id388469"></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
@@ -1433,7 +1461,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="idp312128"></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="id388527"></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
@@ -1443,7 +1471,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="idp313352"></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="id388546"></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
@@ -1452,7 +1480,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="idp314792"></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="id388566"></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
@@ -1466,18 +1494,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="idp317512"></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="id388607"></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="idp318648"></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="id388624"></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="idp320736"></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="id388658"></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:
@@ -1507,12 +1535,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="idp328280"></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="id388766"></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="idp329144"></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="id388778"></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.
@@ -1521,11 +1549,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="idp330392"></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="id388795"></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="idp331704"></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="id388815"></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>,
@@ -1686,7 +1714,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="idp349208"></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="id389072"></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 cf81af6..d1095be 100644
--- a/doc/guide/bind10-guide.txt
+++ b/doc/guide/bind10-guide.txt
@@ -81,6 +81,13 @@ Administrator Reference for BIND 10
8.2.1. In-memory Data Source
+ 8.2.2. In-memory Data Source With SQLite3
+ Backend
+
+ 8.2.3. Reloading an In-memory Data Source
+
+ 8.2.4. Disabling In-memory Data Sources
+
8.3. Loading Master Zones Files
9. Incoming Zone Transfers
@@ -93,6 +100,8 @@ Administrator Reference for BIND 10
9.4. Trigger an Incoming Zone Transfer Manually
+ 9.5. Incoming Transfers with In-memory Datasource
+
10. Outbound Zone Transfers
11. Recursive Name Server
@@ -497,9 +506,8 @@ 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 administration tools to communicate with the system,
- b10-stats for statistics collection, and b10-stats-httpd for statistics
- reporting.
+ b10-cmdctl for administration tools to communicate with the system, and
+ b10-stats for statistics collection.
3.1. Starting BIND 10
@@ -755,6 +763,12 @@ Chapter 8. Authoritative Server
8.2.1. In-memory Data Source
+ 8.2.2. In-memory Data Source With SQLite3 Backend
+
+ 8.2.3. Reloading an In-memory Data Source
+
+ 8.2.4. Disabling In-memory Data Sources
+
8.3. Loading Master Zones Files
The b10-auth is the authoritative DNS server. It supports EDNS0 and
@@ -775,8 +789,8 @@ Chapter 8. Authoritative Server
datasources configures data sources. The list items include: type
to define the required data source type (such as âmemoryâ); class
to optionally select the class (it defaults to âINâ); and zones to
- define the file path name and the origin (default domain). By
- default, this is empty.
+ define the file path name, the filetype (e.g., sqlite3), and the
+ origin (default domain). By default, this is empty.
Note
@@ -784,7 +798,7 @@ Chapter 8. Authoritative Server
memory data source. Only the IN class is supported at this time.
By default, the memory data source is disabled. Also, currently
the zone file must be canonical such as generated by
- named-compilezone -D.
+ named-compilezone -D, or must be an SQLite3 database.
listen_on
listen_on is a list of addresses and ports for b10-auth to listen
@@ -851,11 +865,32 @@ Chapter 8. Authoritative Server
The authoritative server will begin serving it immediately after it is
loaded.
+ 8.2.2. In-memory Data Source With SQLite3 Backend
+
+ The following commands to bindctl provide an example of configuring an
+ in-memory data source containing the âexample.orgâ zone with a SQLite3
+ backend file named âexample.org.sqlite3â:
+
+ > config add Auth/datasources
+ > config set Auth/datasources[1]/type "memory"
+ > config add Auth/datasources[1]/zones
+ > config set Auth/datasources[1]/zones[0]/origin "example.org"
+ > config set Auth/datasources[1]/zones[0]/file "example.org.sqlite3"
+ > config set Auth/datasources[1]/zones[0]/filetype "sqlite3"
+ > config commit
+
+ The authoritative server will begin serving it immediately after it is
+ loaded.
+
+ 8.2.3. Reloading an In-memory Data Source
+
Use the Auth loadzone command in bindctl to reload a changed master file
into memory; for example:
> Auth loadzone origin="example.com"
+ 8.2.4. Disabling In-memory Data Sources
+
By default, the memory data source is disabled; it must be configured
explicitly. To disable all the in-memory zones, specify a null list for
Auth/datasources:
@@ -914,6 +949,8 @@ Chapter 9. Incoming Zone Transfers
9.4. Trigger an Incoming Zone Transfer Manually
+ 9.5. Incoming Transfers with In-memory Datasource
+
Incoming zones are transferred using the b10-xfrin process which is
started by bind10. When received, the zone is stored in the corresponding
BIND 10 data source, and its records can be served by b10-auth. In
@@ -924,12 +961,6 @@ Chapter 9. Incoming Zone Transfers
implementation limitations of the current development release, however, it
only tries AXFR by default, and care should be taken to enable IXFR.
- Note
-
- 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.
-
9.1. Configuration for Incoming Zone Transfers
In practice, you need to specify a list of secondary zones to enable
@@ -1003,6 +1034,18 @@ Chapter 9. Incoming Zone Transfers
> Xfrin retransfer zone_name="foo.example.org" master=192.0.2.99
+9.5. Incoming Transfers with In-memory Datasource
+
+ In the case of an incoming zone transfer, the received zone is first
+ stored in the corresponding BIND 10 datasource. In case the secondary zone
+ is served by an in-memory datasource with an SQLite3 backend, b10-auth is
+ automatically sent a loadzone command to reload the corresponding zone
+ into memory from the backend.
+
+ The administrator doesn't have to do anything for b10-auth to serve the
+ new version of the zone, except for the configuration such as the one
+ described in Section 8.2.2, âIn-memory Data Source With SQLite3 Backendâ.
+
Chapter 10. Outbound Zone Transfers
The b10-xfrout process is started by bind10. When the b10-auth
diff --git a/doc/guide/bind10-guide.xml b/doc/guide/bind10-guide.xml
index a1c6399..e2b3961 100644
--- a/doc/guide/bind10-guide.xml
+++ b/doc/guide/bind10-guide.xml
@@ -770,9 +770,8 @@ as a dependency earlier -->
In its default configuration, the <command>bind10</command>
master process will also start up
<command>b10-cmdctl</command> for administration tools to
- communicate with the system,
- <command>b10-stats</command> for statistics collection, and
- <command>b10-stats-httpd</command> for statistics reporting.
+ communicate with the system, and
+ <command>b10-stats</command> for statistics collection.
</para>
<section id="start">
@@ -1322,9 +1321,10 @@ This may be a temporary setting until then.
<varname>class</varname> to optionally select the class
(it defaults to <quote>IN</quote>);
and
- <varname>zones</varname> to define the
- <varname>file</varname> path name and the
- <varname>origin</varname> (default domain).
+ <varname>zones</varname> to define
+ the <varname>file</varname> path name,
+ the <varname>filetype</varname> (e.g., <varname>sqlite3</varname>),
+ and the <varname>origin</varname> (default domain).
By default, this is empty.
@@ -1334,7 +1334,8 @@ This may be a temporary setting until then.
Only the IN class is supported at this time.
By default, the memory data source is disabled.
Also, currently the zone file must be canonical such as
- generated by <command>named-compilezone -D</command>.
+ generated by <command>named-compilezone -D</command>, or
+ must be an SQLite3 database.
</simpara></note>
</simpara>
@@ -1479,6 +1480,39 @@ This may be a temporary setting until then.
after it is loaded.
</para>
+ </section>
+
+ <section id="in-memory-datasource-with-sqlite3-backend">
+ <title>In-memory Data Source With SQLite3 Backend</title>
+
+ <para>
+<!-- How to configure it. -->
+ The following commands to <command>bindctl</command>
+ provide an example of configuring an in-memory data
+ source containing the <quote>example.org</quote> zone
+ with a SQLite3 backend file named <quote>example.org.sqlite3</quote>:
+
+<!--
+ <screen>> <userinput> config set Auth/datasources/ [{"type": "memory", "zones": [{"origin": "example.org", "file": "example.org.sqlite3", "filetype": "sqlite3"}]}]</userinput></screen>
+-->
+
+ <screen>> <userinput>config add Auth/datasources</userinput>
+> <userinput>config set Auth/datasources[1]/type "<option>memory</option>"</userinput>
+> <userinput>config add Auth/datasources[1]/zones</userinput>
+> <userinput>config set Auth/datasources[1]/zones[0]/origin "<option>example.org</option>"</userinput>
+> <userinput>config set Auth/datasources[1]/zones[0]/file "<option>example.org.sqlite3</option>"</userinput>
+> <userinput>config set Auth/datasources[1]/zones[0]/filetype "<option>sqlite3</option>"</userinput>
+> <userinput>config commit</userinput></screen>
+
+ The authoritative server will begin serving it immediately
+ after it is loaded.
+ </para>
+
+ </section>
+
+ <section id="in-memory-datasource-loading">
+ <title>Reloading an In-memory Data Source</title>
+
<para>
Use the <command>Auth loadzone</command> command in
<command>bindctl</command> to reload a changed master
@@ -1497,6 +1531,10 @@ This may be a temporary setting until then.
</para>
-->
+ </section>
+ <section id="in-memory-datasource-disabling">
+ <title>Disabling In-memory Data Sources</title>
+
<para>
By default, the memory data source is disabled; it must be
configured explicitly. To disable all the in-memory zones,
@@ -1629,12 +1667,6 @@ TODO
</para>
<!-- TODO: http://bind10.isc.org/ticket/1279 -->
- <note><simpara>
- 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.
- </simpara></note>
-
<section>
<title>Configuration for Incoming Zone Transfers</title>
<para>
@@ -1754,6 +1786,26 @@ what if a NOTIFY is sent?
</para>
</section>
+ <section>
+ <title>Incoming Transfers with In-memory Datasource</title>
+
+ <para>
+ In the case of an incoming zone transfer, the received zone is
+ first stored in the corresponding BIND 10 datasource. In
+ case the secondary zone is served by an in-memory datasource
+ with an SQLite3 backend, <command>b10-auth</command> is
+ automatically sent a <varname>loadzone</varname> command to
+ reload the corresponding zone into memory from the backend.
+ </para>
+
+ <para>
+ The administrator doesn't have to do anything for
+ <command>b10-auth</command> to serve the new version of the
+ zone, except for the configuration such as the one described in
+ <xref linkend="in-memory-datasource-with-sqlite3-backend" />.
+ </para>
+ </section>
+
<!-- TODO: can that retransfer be used to identify a new zone? -->
<!-- TODO: what if doesn't exist at that master IP? -->
diff --git a/doc/guide/bind10-messages.html b/doc/guide/bind10-messages.html
index d3bcb7c..a0bbf77 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 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
+<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 20120405. 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
+ 20120405.</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 20120127.
+ This is the messages manual for BIND 10 version 20120405.
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>.
@@ -183,6 +183,17 @@ This is a debug message issued when the authoritative server has received
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.
+</p></dd><dt><a name="AUTH_RESPONSE_FAILURE"></a><span class="term">AUTH_RESPONSE_FAILURE exception while building response to query: %1</span></dt><dd><p>
+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.
+</p></dd><dt><a name="AUTH_RESPONSE_FAILURE_UNKNOWN"></a><span class="term">AUTH_RESPONSE_FAILURE_UNKNOWN unknown exception while building response to query</span></dt><dd><p>
+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.
</p></dd><dt><a name="AUTH_RESPONSE_RECEIVED"></a><span class="term">AUTH_RESPONSE_RECEIVED received response message, ignoring</span></dt><dd><p>
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
@@ -275,7 +286,7 @@ NOTIFY request will not be honored.
The boss process is starting up and will now check if the message bus
daemon is already running. If so, it will not be able to start, as it
needs a dedicated message bus.
-</p></dd><dt><a name="BIND10_COMPONENT_FAILED"></a><span class="term">BIND10_COMPONENT_FAILED component %1 (pid %2) failed with %3 exit status</span></dt><dd><p>
+</p></dd><dt><a name="BIND10_COMPONENT_FAILED"></a><span class="term">BIND10_COMPONENT_FAILED component %1 (pid %2) failed: %3</span></dt><dd><p>
The process terminated, but the bind10 boss didn't expect it to, which means
it must have failed.
</p></dd><dt><a name="BIND10_COMPONENT_RESTART"></a><span class="term">BIND10_COMPONENT_RESTART component %1 is about to restart</span></dt><dd><p>
@@ -585,7 +596,7 @@ same RRset, but from more trusted source, so the old one is kept and new one
ignored.
</p></dd><dt><a name="CACHE_RRSET_UPDATE"></a><span class="term">CACHE_RRSET_UPDATE updating RRset %1/%2/%3 in the cache</span></dt><dd><p>
Debug message. The RRset is updating its data with this given RRset.
-</p></dd><dt><a name="CC_ASYNC_READ_FAILED"></a><span class="term">CC_ASYNC_READ_FAILED asynchronous read failed</span></dt><dd><p>
+</p></dd><dt><a name="CC_ASYNC_READ_FAILED"></a><span class="term">CC_ASYNC_READ_FAILED asynchronous read failed (error code = %1)</span></dt><dd><p>
This marks a low level error, we tried to read data from the message queue
daemon asynchronously, but the ASIO library returned an error.
</p></dd><dt><a name="CC_CONN_ERROR"></a><span class="term">CC_CONN_ERROR error connecting to message queue (%1)</span></dt><dd><p>
@@ -669,6 +680,9 @@ assumed to have failed, and will not be stored.
</p></dd><dt><a name="CFGMGR_CC_SESSION_ERROR"></a><span class="term">CFGMGR_CC_SESSION_ERROR Error connecting to command channel: %1</span></dt><dd><p>
The configuration manager daemon was unable to connect to the messaging
system. The most likely cause is that msgq is not running.
+</p></dd><dt><a name="CFGMGR_CONFIG_FILE"></a><span class="term">CFGMGR_CONFIG_FILE Configuration manager starting with configuration file: %1</span></dt><dd><p>
+The configuration manager is starting, reading and saving the configuration
+settings to the shown file.
</p></dd><dt><a name="CFGMGR_DATA_READ_ERROR"></a><span class="term">CFGMGR_DATA_READ_ERROR error reading configuration database from disk: %1</span></dt><dd><p>
There was a problem reading the persistent configuration data as stored
on disk. The file may be corrupted, or it is of a version from where
@@ -686,6 +700,10 @@ was trying to write the configuration database to disk. The specific
error is given. The most likely cause is that the system does not have
write access to the configuration database file. The updated
configuration is not stored.
+</p></dd><dt><a name="CFGMGR_RENAMED_CONFIG_FILE"></a><span class="term">CFGMGR_RENAMED_CONFIG_FILE renamed configuration file %1 to %2, will create new %1</span></dt><dd><p>
+BIND 10 has been started with the command to clear the configuration file.
+The existing file is backed up to the given file name, so that data is not
+immediately lost if this was done by accident.
</p></dd><dt><a name="CFGMGR_STOPPED_BY_KEYBOARD"></a><span class="term">CFGMGR_STOPPED_BY_KEYBOARD keyboard interrupt, shutting down</span></dt><dd><p>
There was a keyboard interrupt signal to stop the cfgmgr daemon. The
daemon will now shut down.
@@ -870,6 +888,30 @@ means no limit.
The datasource tried to provide an NSEC proof that the named domain does not
exist, but the database backend doesn't support DNSSEC. No proof is included
in the answer as a result.
+</p></dd><dt><a name="DATASRC_DATABASE_FINDNSEC3"></a><span class="term">DATASRC_DATABASE_FINDNSEC3 Looking for NSEC3 for %1 in %2 mode</span></dt><dd><p>
+Debug information. A search in an database data source for NSEC3 that
+matches or covers the given name is being started.
+</p></dd><dt><a name="DATASRC_DATABASE_FINDNSEC3_COVER"></a><span class="term">DATASRC_DATABASE_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_DATABASE_FINDNSEC3_MATCH"></a><span class="term">DATASRC_DATABASE_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_DATABSE_FINDNSEC3_TRYHASH).
+The found NSEC3 RRset is also displayed.
+</p></dd><dt><a name="DATASRC_DATABASE_FINDNSEC3_TRYHASH"></a><span class="term">DATASRC_DATABASE_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, as "." is 1 label long).
+</p></dd><dt><a name="DATASRC_DATABASE_FINDNSEC3_TRYHASH_PREV"></a><span class="term">DATASRC_DATABASE_FINDNSEC3_TRYHASH_PREV looking for previous NSEC3 for %1 at label count %2 (hash %3)</span></dt><dd><p>
+Debug information. An exact match on hash (see
+DATASRC_DATABASE_FINDNSEC3_TRYHASH) was unsuccessful. We get the previous hash
+to that one instead.
</p></dd><dt><a name="DATASRC_DATABASE_FIND_RECORDS"></a><span class="term">DATASRC_DATABASE_FIND_RECORDS looking in datasource %1 for record %2/%3/%4</span></dt><dd><p>
Debug information. The database data source is looking up records with the given
name and type in the database.
@@ -925,10 +967,12 @@ While iterating through the zone, the program reached end of the data.
While iterating through the zone, the program extracted next RRset from it.
The name and RRtype of the RRset is indicated in the message.
</p></dd><dt><a name="DATASRC_DATABASE_ITERATE_TTL_MISMATCH"></a><span class="term">DATASRC_DATABASE_ITERATE_TTL_MISMATCH TTL values differ for RRs of %1/%2/%3, setting to %4</span></dt><dd><p>
-While iterating through the zone, the time to live for RRs of the given RRset
-were found to be different. This isn't allowed on the wire and is considered
-an error, so we set it to the lowest value we found (but we don't modify the
-database). The data in database should be checked and fixed.
+While iterating through the zone, the time to live for RRs of the
+given RRset were found to be different. Since an RRset cannot have
+multiple TTLs, we set it to the lowest value we found (but we don't
+modify the database). This is what the client would do when such RRs
+were given in a DNS response according to RFC2181. The data in
+database should be checked and fixed.
</p></dd><dt><a name="DATASRC_DATABASE_JOURNALREADER_END"></a><span class="term">DATASRC_DATABASE_JOURNALREADER_END %1/%2 on %3 from %4 to %5</span></dt><dd><p>
This is a debug message indicating that the program (successfully)
reaches the end of sequences of a zone's differences. The zone's name
@@ -1259,8 +1303,10 @@ not have any DS record. This indicates problem with the provided data.
An attempt to add a NSEC3 record into the message failed, because the zone does
not have any DS record. This indicates problem with the provided data.
</p></dd><dt><a name="DATASRC_QUERY_NO_ZONE"></a><span class="term">DATASRC_QUERY_NO_ZONE no zone containing '%1' in class '%2'</span></dt><dd><p>
-Lookup of domain failed because the data have no zone that contain the
-domain. Maybe someone sent a query to the wrong server for some reason.
+Debug information. Lookup of domain failed because the datasource
+has no zone that contains the domain. Maybe someone sent a query
+to the wrong server for some reason. This may also happen when
+looking in the datasource for addresses for NS records.
</p></dd><dt><a name="DATASRC_QUERY_PROCESS"></a><span class="term">DATASRC_QUERY_PROCESS processing query '%1/%2' in the '%3' class</span></dt><dd><p>
Debug information. A sure query is being processed now.
</p></dd><dt><a name="DATASRC_QUERY_PROVE_NX_FAIL"></a><span class="term">DATASRC_QUERY_PROVE_NX_FAIL unable to prove nonexistence of '%1'</span></dt><dd><p>
@@ -1307,6 +1353,16 @@ While processing a wildcard, a referral was met. But it wasn't possible to get
enough information for it. The code is 1 for error, 2 for not implemented.
</p></dd><dt><a name="DATASRC_SQLITE_CLOSE"></a><span class="term">DATASRC_SQLITE_CLOSE closing SQLite database</span></dt><dd><p>
Debug information. The SQLite data source is closing the database file.
+</p></dd><dt><a name="DATASRC_SQLITE_COMPATIBLE_VERSION"></a><span class="term">DATASRC_SQLITE_COMPATIBLE_VERSION database schema V%1.%2 not up to date (expecting V%3.%4) but is compatible</span></dt><dd><p>
+The version of the SQLite3 database schema used to hold the zone data
+is not the latest one - the current version of BIND 10 was written
+with a later schema version in mind. However, the database is
+compatible with the current version of BIND 10, and BIND 10 will run
+without any problems.
+</p><p>
+Consult the release notes for your version of BIND 10. Depending on
+the changes made to the database schema, it is possible that improved
+performance could result if the database were upgraded.
</p></dd><dt><a name="DATASRC_SQLITE_CONNCLOSE"></a><span class="term">DATASRC_SQLITE_CONNCLOSE Closing sqlite database</span></dt><dd><p>
The database file is no longer needed and is being closed.
</p></dd><dt><a name="DATASRC_SQLITE_CONNOPEN"></a><span class="term">DATASRC_SQLITE_CONNOPEN Opening sqlite database file '%1'</span></dt><dd><p>
@@ -1356,6 +1412,13 @@ source.
</p></dd><dt><a name="DATASRC_SQLITE_FIND_NSEC3_NO_ZONE"></a><span class="term">DATASRC_SQLITE_FIND_NSEC3_NO_ZONE no such zone '%1'</span></dt><dd><p>
The SQLite data source was asked to provide a NSEC3 record for given zone.
But it doesn't contain that zone.
+</p></dd><dt><a name="DATASRC_SQLITE_INCOMPATIBLE_VERSION"></a><span class="term">DATASRC_SQLITE_INCOMPATIBLE_VERSION database schema V%1.%2 incompatible with version (V%3.%4) expected</span></dt><dd><p>
+The version of the SQLite3 database schema used to hold the zone data
+is incompatible with the version expected by BIND 10. As a result,
+BIND 10 is unable to run using the database file as the data source.
+</p><p>
+The database should be updated using the means described in the BIND
+10 documentation.
</p></dd><dt><a name="DATASRC_SQLITE_NEWCONN"></a><span class="term">DATASRC_SQLITE_NEWCONN SQLite3Database is being initialized</span></dt><dd><p>
A wrapper object to hold database connection is being initialized.
</p></dd><dt><a name="DATASRC_SQLITE_OPEN"></a><span class="term">DATASRC_SQLITE_OPEN opening SQLite database '%1'</span></dt><dd><p>
@@ -1389,6 +1452,75 @@ 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="DBUTIL_BACKUP"></a><span class="term">DBUTIL_BACKUP created backup of %1 in %2</span></dt><dd><p>
+A backup for the given database file was created. Same of original file and
+backup are given in the output message.
+</p></dd><dt><a name="DBUTIL_CHECK_ERROR"></a><span class="term">DBUTIL_CHECK_ERROR unable to check database version: %1</span></dt><dd><p>
+There was an error while trying to check the current version of the database
+schema. The error is shown in the message.
+</p></dd><dt><a name="DBUTIL_CHECK_NOCONFIRM"></a><span class="term">DBUTIL_CHECK_NOCONFIRM --noconfirm is not compatible with --check</span></dt><dd><p>
+b10-dbutil was called with --check and --noconfirm. --noconfirm only has
+meaning with --upgrade, so this is considered an error.
+</p></dd><dt><a name="DBUTIL_CHECK_OK"></a><span class="term">DBUTIL_CHECK_OK this is the latest version of the database schema. No upgrade is required</span></dt><dd><p>
+The database schema version has been checked, and is up to date.
+No action is required.
+</p></dd><dt><a name="DBUTIL_CHECK_UPGRADE_NEEDED"></a><span class="term">DBUTIL_CHECK_UPGRADE_NEEDED re-run this program with the --upgrade switch to upgrade</span></dt><dd><p>
+The database schema version is not up to date, and an update is required.
+Please run the dbutil tool again, with the --upgrade argument.
+</p></dd><dt><a name="DBUTIL_COMMAND_NONE"></a><span class="term">DBUTIL_COMMAND_NONE must select one of --check or --upgrade</span></dt><dd><p>
+b10-dbutil was called with neither --check nor --upgrade. One action must be
+provided.
+</p></dd><dt><a name="DBUTIL_COMMAND_UPGRADE_CHECK"></a><span class="term">DBUTIL_COMMAND_UPGRADE_CHECK --upgrade is not compatible with --check</span></dt><dd><p>
+b10-dbutil was called with both the commands --upgrade and --check. Only one
+action can be performed at a time.
+</p></dd><dt><a name="DBUTIL_DATABASE_MAY_BE_CORRUPT"></a><span class="term">DBUTIL_DATABASE_MAY_BE_CORRUPT database file %1 may be corrupt, restore it from backup (%2)</span></dt><dd><p>
+The upgrade failed while it was in progress; the database may now be in an
+inconsistent state, and it is advised to restore it from the backup that was
+created when b10-dbutil started.
+</p></dd><dt><a name="DBUTIL_EXECUTE"></a><span class="term">DBUTIL_EXECUTE Executing SQL statement: %1</span></dt><dd><p>
+Debug message; the given SQL statement is executed
+</p></dd><dt><a name="DBUTIL_FILE"></a><span class="term">DBUTIL_FILE Database file: %1</span></dt><dd><p>
+The database file that is being checked.
+</p></dd><dt><a name="DBUTIL_NO_FILE"></a><span class="term">DBUTIL_NO_FILE must supply name of the database file to upgrade</span></dt><dd><p>
+b10-dbutil was called without a database file. Currently, it cannot find this
+file on its own, and it must be provided.
+</p></dd><dt><a name="DBUTIL_STATEMENT_ERROR"></a><span class="term">DBUTIL_STATEMENT_ERROR failed to execute %1: %2</span></dt><dd><p>
+The given database statement failed to execute. The error is shown in the
+message.
+</p></dd><dt><a name="DBUTIL_TOO_MANY_ARGUMENTS"></a><span class="term">DBUTIL_TOO_MANY_ARGUMENTS too many arguments to the command, maximum of one expected</span></dt><dd><p>
+There were too many command-line arguments to b10-dbutil
+</p></dd><dt><a name="DBUTIL_UPGRADE_CANCELED"></a><span class="term">DBUTIL_UPGRADE_CANCELED upgrade canceled; database has not been changed</span></dt><dd><p>
+The user aborted the upgrade, and b10-dbutil will now exit.
+</p></dd><dt><a name="DBUTIL_UPGRADE_DBUTIL"></a><span class="term">DBUTIL_UPGRADE_DBUTIL please get the latest version of b10-dbutil and re-run</span></dt><dd><p>
+A database schema was found that was newer than this version of dbutil, which
+is apparently out of date and should be upgraded itself.
+</p></dd><dt><a name="DBUTIL_UPGRADE_FAILED"></a><span class="term">DBUTIL_UPGRADE_FAILED upgrade failed: %1</span></dt><dd><p>
+While the upgrade was in progress, an unexpected error occurred. The error
+is shown in the message.
+</p></dd><dt><a name="DBUTIL_UPGRADE_NOT_ATTEMPTED"></a><span class="term">DBUTIL_UPGRADE_NOT_ATTEMPTED database upgrade was not attempted</span></dt><dd><p>
+Due to the earlier failure, the database schema upgrade was not attempted,
+and b10-dbutil will now exit.
+</p></dd><dt><a name="DBUTIL_UPGRADE_NOT_NEEDED"></a><span class="term">DBUTIL_UPGRADE_NOT_NEEDED database already at latest version, no upgrade necessary</span></dt><dd><p>
+b10-dbutil was told to upgrade the database schema, but it is already at the
+latest version.
+</p></dd><dt><a name="DBUTIL_UPGRADE_NOT_POSSIBLE"></a><span class="term">DBUTIL_UPGRADE_NOT_POSSIBLE database at a later version than this utility can support</span></dt><dd><p>
+b10-dbutil was told to upgrade the database schema, but it is at a higher
+version than this tool currently supports. Please update b10-dbutil and try
+again.
+</p></dd><dt><a name="DBUTIL_UPGRADE_PREPARATION_FAILED"></a><span class="term">DBUTIL_UPGRADE_PREPARATION_FAILED upgrade preparation failed: %1</span></dt><dd><p>
+An unexpected error occurred while b10-dbutil was preparing to upgrade the
+database schema. The error is shown in the message
+</p></dd><dt><a name="DBUTIL_UPGRADE_SUCCESFUL"></a><span class="term">DBUTIL_UPGRADE_SUCCESFUL database upgrade successfully completed</span></dt><dd><p>
+The database schema update was completed successfully.
+</p></dd><dt><a name="DBUTIL_UPGRADING"></a><span class="term">DBUTIL_UPGRADING upgrading database from %1 to %2</span></dt><dd><p>
+An upgrade is in progress, the versions of the current upgrade action are shown.
+</p></dd><dt><a name="DBUTIL_VERSION_CURRENT"></a><span class="term">DBUTIL_VERSION_CURRENT database version %1</span></dt><dd><p>
+The current version of the database schema.
+</p></dd><dt><a name="DBUTIL_VERSION_HIGH"></a><span class="term">DBUTIL_VERSION_HIGH database is at a later version (%1) than this program can cope with (%2)</span></dt><dd><p>
+The database schema is at a higher version than b10-dbutil knows about.
+</p></dd><dt><a name="DBUTIL_VERSION_LOW"></a><span class="term">DBUTIL_VERSION_LOW database version %1, latest version is %2.</span></dt><dd><p>
+The database schema is not up to date, the current version and the latest
+version are in the message.
</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
@@ -2064,7 +2196,7 @@ 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>
+</p></dd><dt><a name="RESOLVER_SHUTDOWN_RECEIVED"></a><span class="term">RESOLVER_SHUTDOWN_RECEIVED received command to shut down</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>
@@ -2270,6 +2402,14 @@ is unknown in the implementation. The most likely cause is an
installation problem, where the specification file stats.spec is
from a different version of BIND 10 than the stats module itself.
Please check your installation.
+</p></dd><dt><a name="XFRIN_AUTH_CONFIG_NAME_PARSER_ERROR"></a><span class="term">XFRIN_AUTH_CONFIG_NAME_PARSER_ERROR Invalid name when parsing Auth configuration: %1</span></dt><dd><p>
+There was an invalid name when parsing Auth configuration.
+</p></dd><dt><a name="XFRIN_AUTH_CONFIG_RRCLASS_ERROR"></a><span class="term">XFRIN_AUTH_CONFIG_RRCLASS_ERROR Invalid RRClass when parsing Auth configuration: %1</span></dt><dd><p>
+There was an invalid RR class when parsing Auth configuration.
+</p></dd><dt><a name="XFRIN_AUTH_LOADZONE"></a><span class="term">XFRIN_AUTH_LOADZONE sending Auth loadzone for origin=%1, class=%2, datasrc=%3</span></dt><dd><p>
+There was a successful zone transfer, and the zone is served by b10-auth
+in the in-memory data source using sqlite3 as a backend. We send the
+"loadzone" command for the zone to b10-auth.
</p></dd><dt><a name="XFRIN_AXFR_INCONSISTENT_SOA"></a><span class="term">XFRIN_AXFR_INCONSISTENT_SOA AXFR SOAs are inconsistent for %1: %2 expected, %3 received</span></dt><dd><p>
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
@@ -2317,6 +2457,30 @@ is not equal to the requested SOA serial.
</p></dd><dt><a name="XFRIN_IMPORT_DNS"></a><span class="term">XFRIN_IMPORT_DNS error importing python DNS module: %1</span></dt><dd><p>
There was an error importing the python DNS module pydnspp. The most
likely cause is a PYTHONPATH problem.
+</p></dd><dt><a name="XFRIN_IXFR_TRANSFER_SUCCESS"></a><span class="term">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)</span></dt><dd><p>
+The IXFR transfer for the given zone was successful.
+The provided information contains the following values:
+</p><p>
+messages: Number of overhead DNS messages in the transfer.
+</p><p>
+changesets: Number of difference sequences.
+</p><p>
+deletions: Number of Resource Records deleted by all the changesets combined,
+including the SOA records.
+</p><p>
+additions: Number of Resource Records added by all the changesets combined,
+including the SOA records.
+</p><p>
+bytes: Full size of the transfer data on the wire.
+</p><p>
+run time: Time (in seconds) the complete ixfr took.
+</p><p>
+bytes/second: Transfer speed.
+</p><p>
+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).
</p></dd><dt><a name="XFRIN_IXFR_UPTODATE"></a><span class="term">XFRIN_IXFR_UPTODATE IXFR requested serial for %1 is %2, master has %3, not updating</span></dt><dd><p>
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
@@ -2330,6 +2494,9 @@ aborts the transfer just like a successful case.
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
was killed.
+</p></dd><dt><a name="XFRIN_MSGQ_SEND_ERROR_AUTH"></a><span class="term">XFRIN_MSGQ_SEND_ERROR_AUTH error while contacting %1</span></dt><dd><p>
+There was a problem sending a message to b10-auth. This most likely
+means that the msgq daemon has quit or was killed.
</p></dd><dt><a name="XFRIN_MSGQ_SEND_ERROR_ZONE_MANAGER"></a><span class="term">XFRIN_MSGQ_SEND_ERROR_ZONE_MANAGER error while contacting %1</span></dt><dd><p>
There was a problem sending a message to the zone manager. This most
likely means that the msgq daemon has quit or was killed.
@@ -2343,11 +2510,26 @@ 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
for xfrin is incomplete, or there was a typographical error in the
zone name in the configuration.
-</p></dd><dt><a name="XFRIN_STARTING"></a><span class="term">XFRIN_STARTING starting resolver with command line '%1'</span></dt><dd><p>
-An informational message, this is output when the resolver starts up.
+</p></dd><dt><a name="XFRIN_STARTED"></a><span class="term">XFRIN_STARTED xfrin started</span></dt><dd><p>
+This informational message is output by xfrin when all initialization
+has been completed and it is entering its main loop.
</p></dd><dt><a name="XFRIN_STOPPED_BY_KEYBOARD"></a><span class="term">XFRIN_STOPPED_BY_KEYBOARD keyboard interrupt, shutting down</span></dt><dd><p>
There was a keyboard interrupt signal to stop the xfrin daemon. The
daemon will now shut down.
+</p></dd><dt><a name="XFRIN_TRANSFER_SUCCESS"></a><span class="term">XFRIN_TRANSFER_SUCCESS full %1 transfer of zone %2 succeeded (messages: %3, records: %4, bytes: %5, run time: %6 seconds, %7 bytes/second)</span></dt><dd><p>
+The AXFR transfer of the given zone was successful.
+The provided information contains the following values:
+</p><p>
+messages: Number of overhead DNS messages in the transfer
+</p><p>
+records: Number of Resource Records in the full transfer, excluding the
+final SOA record that marks the end of the AXFR.
+</p><p>
+bytes: Full size of the transfer data on the wire.
+</p><p>
+run time: Time (in seconds) the complete axfr took
+</p><p>
+bytes/second: Transfer speed
</p></dd><dt><a name="XFRIN_UNKNOWN_ERROR"></a><span class="term">XFRIN_UNKNOWN_ERROR unknown error: %1</span></dt><dd><p>
An uncaught exception was raised while running the xfrin daemon. The
exception message is printed in the log message.
@@ -2389,8 +2571,6 @@ is recommended to check the primary server configuration.
</p></dd><dt><a name="XFRIN_XFR_TRANSFER_STARTED"></a><span class="term">XFRIN_XFR_TRANSFER_STARTED %1 transfer of zone %2 started</span></dt><dd><p>
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.
-</p></dd><dt><a name="XFRIN_XFR_TRANSFER_SUCCESS"></a><span class="term">XFRIN_XFR_TRANSFER_SUCCESS %1 transfer of zone %2 succeeded</span></dt><dd><p>
-The XFR transfer of the given zone was successfully completed.
</p></dd><dt><a name="XFRIN_ZONE_CREATED"></a><span class="term">XFRIN_ZONE_CREATED Zone %1 not found in the given data source, newly created</span></dt><dd><p>
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
@@ -2557,6 +2737,9 @@ the xfrout daemon that a new xfrout request has arrived. This should
be a result of rare local error such as memory allocation failure and
shouldn't happen under normal conditions. The error is included in the
log message.
+</p></dd><dt><a name="XFROUT_STARTED"></a><span class="term">XFROUT_STARTED xfrout started</span></dt><dd><p>
+This informational message is output by xfrout when all initialization
+has been completed and it is entering its main loop.
</p></dd><dt><a name="XFROUT_STOPPED_BY_KEYBOARD"></a><span class="term">XFROUT_STOPPED_BY_KEYBOARD keyboard interrupt, shutting down</span></dt><dd><p>
There was a keyboard interrupt signal to stop the xfrout daemon. The
daemon will now shut down.
@@ -2673,6 +2856,9 @@ connecting to the command channel daemon. The most usual cause of this
problem is that the daemon is not running.
</p></dd><dt><a name="ZONEMGR_SHUTDOWN"></a><span class="term">ZONEMGR_SHUTDOWN zone manager has shut down</span></dt><dd><p>
A debug message, output when the zone manager has shut down completely.
+</p></dd><dt><a name="ZONEMGR_STARTED"></a><span class="term">ZONEMGR_STARTED zonemgr started</span></dt><dd><p>
+This informational message is output by zonemgr when all initialization
+has been completed and it is entering its main loop.
</p></dd><dt><a name="ZONEMGR_STARTING"></a><span class="term">ZONEMGR_STARTING zone manager starting</span></dt><dd><p>
A debug message output when the zone manager starts up.
</p></dd><dt><a name="ZONEMGR_TIMER_THREAD_RUNNING"></a><span class="term">ZONEMGR_TIMER_THREAD_RUNNING trying to start timer thread but one is already running</span></dt><dd><p>
@@ -2683,9 +2869,11 @@ a problem with stopping a previous instance of the timer. Please submit
a bug report.
</p></dd><dt><a name="ZONEMGR_UNKNOWN_ZONE_FAIL"></a><span class="term">ZONEMGR_UNKNOWN_ZONE_FAIL zone %1 (class %2) is not known to the zone manager</span></dt><dd><p>
An XFRIN operation has failed but the zone that was the subject of the
-operation is not being managed by the zone manager. This may indicate
-an error in the program (as the operation should not have been initiated
-if this were the case). Please submit a bug report.
+operation is not being managed by the zone manager. This can be either the
+result of a bindctl command to transfer in a currently unknown (or mistyped)
+zone, or, if this error appears without the administrator giving transfer
+commands, it can indicate an error in the program, as it should not have
+initiated transfers of unknown zones on its own.
</p></dd><dt><a name="ZONEMGR_UNKNOWN_ZONE_NOTIFIED"></a><span class="term">ZONEMGR_UNKNOWN_ZONE_NOTIFIED notified zone %1 (class %2) is not known to the zone manager</span></dt><dd><p>
A NOTIFY was received but the zone that was the subject of the operation
is not being managed by the zone manager. This may indicate an error
diff --git a/doc/guide/bind10-messages.xml b/doc/guide/bind10-messages.xml
index 60f9665..3edf11b 100644
--- a/doc/guide/bind10-messages.xml
+++ b/doc/guide/bind10-messages.xml
@@ -1399,7 +1399,7 @@ Debug message. The RRset is updating its data with this given RRset.
</varlistentry>
<varlistentry id="CC_ASYNC_READ_FAILED">
-<term>CC_ASYNC_READ_FAILED asynchronous read failed</term>
+<term>CC_ASYNC_READ_FAILED asynchronous read failed (error code = %1)</term>
<listitem><para>
This marks a low level error, we tried to read data from the message queue
daemon asynchronously, but the ASIO library returned an error.
@@ -1607,6 +1607,14 @@ system. The most likely cause is that msgq is not running.
</para></listitem>
</varlistentry>
+<varlistentry id="CFGMGR_CONFIG_FILE">
+<term>CFGMGR_CONFIG_FILE Configuration manager starting with configuration file: %1</term>
+<listitem><para>
+The configuration manager is starting, reading and saving the configuration
+settings to the shown file.
+</para></listitem>
+</varlistentry>
+
<varlistentry id="CFGMGR_DATA_READ_ERROR">
<term>CFGMGR_DATA_READ_ERROR error reading configuration database from disk: %1</term>
<listitem><para>
@@ -2057,6 +2065,55 @@ in the answer as a result.
</para></listitem>
</varlistentry>
+<varlistentry id="DATASRC_DATABASE_FINDNSEC3">
+<term>DATASRC_DATABASE_FINDNSEC3 Looking for NSEC3 for %1 in %2 mode</term>
+<listitem><para>
+Debug information. A search in an database data source for NSEC3 that
+matches or covers the given name is being started.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="DATASRC_DATABASE_FINDNSEC3_COVER">
+<term>DATASRC_DATABASE_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_DATABASE_FINDNSEC3_MATCH">
+<term>DATASRC_DATABASE_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_DATABSE_FINDNSEC3_TRYHASH).
+The found NSEC3 RRset is also displayed.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="DATASRC_DATABASE_FINDNSEC3_TRYHASH">
+<term>DATASRC_DATABASE_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, as "." is 1 label long).
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="DATASRC_DATABASE_FINDNSEC3_TRYHASH_PREV">
+<term>DATASRC_DATABASE_FINDNSEC3_TRYHASH_PREV looking for previous NSEC3 for %1 at label count %2 (hash %3)</term>
+<listitem><para>
+Debug information. An exact match on hash (see
+DATASRC_DATABASE_FINDNSEC3_TRYHASH) was unsuccessful. We get the previous hash
+to that one instead.
+</para></listitem>
+</varlistentry>
+
<varlistentry id="DATASRC_DATABASE_FIND_RECORDS">
<term>DATASRC_DATABASE_FIND_RECORDS looking in datasource %1 for record %2/%3/%4</term>
<listitem><para>
@@ -2189,10 +2246,12 @@ The name and RRtype of the RRset is indicated in the message.
<varlistentry id="DATASRC_DATABASE_ITERATE_TTL_MISMATCH">
<term>DATASRC_DATABASE_ITERATE_TTL_MISMATCH TTL values differ for RRs of %1/%2/%3, setting to %4</term>
<listitem><para>
-While iterating through the zone, the time to live for RRs of the given RRset
-were found to be different. This isn't allowed on the wire and is considered
-an error, so we set it to the lowest value we found (but we don't modify the
-database). The data in database should be checked and fixed.
+While iterating through the zone, the time to live for RRs of the
+given RRset were found to be different. Since an RRset cannot have
+multiple TTLs, we set it to the lowest value we found (but we don't
+modify the database). This is what the client would do when such RRs
+were given in a DNS response according to RFC2181. The data in
+database should be checked and fixed.
</para></listitem>
</varlistentry>
@@ -3096,6 +3155,21 @@ Debug information. The SQLite data source is closing the database file.
</para></listitem>
</varlistentry>
+<varlistentry id="DATASRC_SQLITE_COMPATIBLE_VERSION">
+<term>DATASRC_SQLITE_COMPATIBLE_VERSION database schema V%1.%2 not up to date (expecting V%3.%4) but is compatible</term>
+<listitem><para>
+The version of the SQLite3 database schema used to hold the zone data
+is not the latest one - the current version of BIND 10 was written
+with a later schema version in mind. However, the database is
+compatible with the current version of BIND 10, and BIND 10 will run
+without any problems.
+</para><para>
+Consult the release notes for your version of BIND 10. Depending on
+the changes made to the database schema, it is possible that improved
+performance could result if the database were upgraded.
+</para></listitem>
+</varlistentry>
+
<varlistentry id="DATASRC_SQLITE_CONNCLOSE">
<term>DATASRC_SQLITE_CONNCLOSE Closing sqlite database</term>
<listitem><para>
@@ -3235,6 +3309,18 @@ But it doesn't contain that zone.
</para></listitem>
</varlistentry>
+<varlistentry id="DATASRC_SQLITE_INCOMPATIBLE_VERSION">
+<term>DATASRC_SQLITE_INCOMPATIBLE_VERSION database schema V%1.%2 incompatible with version (V%3.%4) expected</term>
+<listitem><para>
+The version of the SQLite3 database schema used to hold the zone data
+is incompatible with the version expected by BIND 10. As a result,
+BIND 10 is unable to run using the database file as the data source.
+</para><para>
+The database should be updated using the means described in the BIND
+10 documentation.
+</para></listitem>
+</varlistentry>
+
<varlistentry id="DATASRC_SQLITE_NEWCONN">
<term>DATASRC_SQLITE_NEWCONN SQLite3Database is being initialized</term>
<listitem><para>
@@ -5388,6 +5474,29 @@ Please check your installation.
</para></listitem>
</varlistentry>
+<varlistentry id="XFRIN_AUTH_CONFIG_NAME_PARSER_ERROR">
+<term>XFRIN_AUTH_CONFIG_NAME_PARSER_ERROR Invalid name when parsing Auth configuration: %1</term>
+<listitem><para>
+There was an invalid name when parsing Auth configuration.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="XFRIN_AUTH_CONFIG_RRCLASS_ERROR">
+<term>XFRIN_AUTH_CONFIG_RRCLASS_ERROR Invalid RRClass when parsing Auth configuration: %1</term>
+<listitem><para>
+There was an invalid RR class when parsing Auth configuration.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="XFRIN_AUTH_LOADZONE">
+<term>XFRIN_AUTH_LOADZONE sending Auth loadzone for origin=%1, class=%2, datasrc=%3</term>
+<listitem><para>
+There was a successful zone transfer, and the zone is served by b10-auth
+in the in-memory data source using sqlite3 as a backend. We send the
+"loadzone" command for the zone to b10-auth.
+</para></listitem>
+</varlistentry>
+
<varlistentry id="XFRIN_AXFR_INCONSISTENT_SOA">
<term>XFRIN_AXFR_INCONSISTENT_SOA AXFR SOAs are inconsistent for %1: %2 expected, %3 received</term>
<listitem><para>
@@ -5542,6 +5651,14 @@ was killed.
</para></listitem>
</varlistentry>
+<varlistentry id="XFRIN_MSGQ_SEND_ERROR_AUTH">
+<term>XFRIN_MSGQ_SEND_ERROR_AUTH error while contacting %1</term>
+<listitem><para>
+There was a problem sending a message to b10-auth. This most likely
+means that the msgq daemon has quit or was killed.
+</para></listitem>
+</varlistentry>
+
<varlistentry id="XFRIN_MSGQ_SEND_ERROR_ZONE_MANAGER">
<term>XFRIN_MSGQ_SEND_ERROR_ZONE_MANAGER error while contacting %1</term>
<listitem><para>
@@ -5570,10 +5687,11 @@ zone name in the configuration.
</para></listitem>
</varlistentry>
-<varlistentry id="XFRIN_STARTING">
-<term>XFRIN_STARTING starting resolver with command line '%1'</term>
+<varlistentry id="XFRIN_STARTED">
+<term>XFRIN_STARTED xfrin started</term>
<listitem><para>
-An informational message, this is output when the resolver starts up.
+This informational message is output by xfrin when all initialization
+has been completed and it is entering its main loop.
</para></listitem>
</varlistentry>
@@ -6001,6 +6119,14 @@ log message.
</para></listitem>
</varlistentry>
+<varlistentry id="XFROUT_STARTED">
+<term>XFROUT_STARTED xfrout started</term>
+<listitem><para>
+This informational message is output by xfrout when all initialization
+has been completed and it is entering its main loop.
+</para></listitem>
+</varlistentry>
+
<varlistentry id="XFROUT_STOPPED_BY_KEYBOARD">
<term>XFROUT_STOPPED_BY_KEYBOARD keyboard interrupt, shutting down</term>
<listitem><para>
@@ -6257,6 +6383,14 @@ A debug message, output when the zone manager has shut down completely.
</para></listitem>
</varlistentry>
+<varlistentry id="ZONEMGR_STARTED">
+<term>ZONEMGR_STARTED zonemgr started</term>
+<listitem><para>
+This informational message is output by zonemgr when all initialization
+has been completed and it is entering its main loop.
+</para></listitem>
+</varlistentry>
+
<varlistentry id="ZONEMGR_STARTING">
<term>ZONEMGR_STARTING zone manager starting</term>
<listitem><para>
diff --git a/doc/images/isc-logo.png b/doc/images/isc-logo.png
new file mode 100644
index 0000000..1f7af96
Binary files /dev/null and b/doc/images/isc-logo.png differ
diff --git a/install-sh b/install-sh
deleted file mode 100755
index 6781b98..0000000
--- a/install-sh
+++ /dev/null
@@ -1,520 +0,0 @@
-#!/bin/sh
-# install - install a program, script, or datafile
-
-scriptversion=2009-04-28.21; # UTC
-
-# This originates from X11R5 (mit/util/scripts/install.sh), which was
-# later released in X11R6 (xc/config/util/install.sh) with the
-# following copyright and license.
-#
-# Copyright (C) 1994 X Consortium
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to
-# deal in the Software without restriction, including without limitation the
-# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
-# sell copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
-# AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNEC-
-# TION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-#
-# Except as contained in this notice, the name of the X Consortium shall not
-# be used in advertising or otherwise to promote the sale, use or other deal-
-# ings in this Software without prior written authorization from the X Consor-
-# tium.
-#
-#
-# FSF changes to this file are in the public domain.
-#
-# Calling this script install-sh is preferred over install.sh, to prevent
-# `make' implicit rules from creating a file called install from it
-# when there is no Makefile.
-#
-# This script is compatible with the BSD install script, but was written
-# from scratch.
-
-nl='
-'
-IFS=" "" $nl"
-
-# set DOITPROG to echo to test this script
-
-# Don't use :- since 4.3BSD and earlier shells don't like it.
-doit=${DOITPROG-}
-if test -z "$doit"; then
- doit_exec=exec
-else
- doit_exec=$doit
-fi
-
-# Put in absolute file names if you don't have them in your path;
-# or use environment vars.
-
-chgrpprog=${CHGRPPROG-chgrp}
-chmodprog=${CHMODPROG-chmod}
-chownprog=${CHOWNPROG-chown}
-cmpprog=${CMPPROG-cmp}
-cpprog=${CPPROG-cp}
-mkdirprog=${MKDIRPROG-mkdir}
-mvprog=${MVPROG-mv}
-rmprog=${RMPROG-rm}
-stripprog=${STRIPPROG-strip}
-
-posix_glob='?'
-initialize_posix_glob='
- test "$posix_glob" != "?" || {
- if (set -f) 2>/dev/null; then
- posix_glob=
- else
- posix_glob=:
- fi
- }
-'
-
-posix_mkdir=
-
-# Desired mode of installed file.
-mode=0755
-
-chgrpcmd=
-chmodcmd=$chmodprog
-chowncmd=
-mvcmd=$mvprog
-rmcmd="$rmprog -f"
-stripcmd=
-
-src=
-dst=
-dir_arg=
-dst_arg=
-
-copy_on_change=false
-no_target_directory=
-
-usage="\
-Usage: $0 [OPTION]... [-T] SRCFILE DSTFILE
- or: $0 [OPTION]... SRCFILES... DIRECTORY
- or: $0 [OPTION]... -t DIRECTORY SRCFILES...
- or: $0 [OPTION]... -d DIRECTORIES...
-
-In the 1st form, copy SRCFILE to DSTFILE.
-In the 2nd and 3rd, copy all SRCFILES to DIRECTORY.
-In the 4th, create DIRECTORIES.
-
-Options:
- --help display this help and exit.
- --version display version info and exit.
-
- -c (ignored)
- -C install only if different (preserve the last data modification time)
- -d create directories instead of installing files.
- -g GROUP $chgrpprog installed files to GROUP.
- -m MODE $chmodprog installed files to MODE.
- -o USER $chownprog installed files to USER.
- -s $stripprog installed files.
- -t DIRECTORY install into DIRECTORY.
- -T report an error if DSTFILE is a directory.
-
-Environment variables override the default commands:
- CHGRPPROG CHMODPROG CHOWNPROG CMPPROG CPPROG MKDIRPROG MVPROG
- RMPROG STRIPPROG
-"
-
-while test $# -ne 0; do
- case $1 in
- -c) ;;
-
- -C) copy_on_change=true;;
-
- -d) dir_arg=true;;
-
- -g) chgrpcmd="$chgrpprog $2"
- shift;;
-
- --help) echo "$usage"; exit $?;;
-
- -m) mode=$2
- case $mode in
- *' '* | *' '* | *'
-'* | *'*'* | *'?'* | *'['*)
- echo "$0: invalid mode: $mode" >&2
- exit 1;;
- esac
- shift;;
-
- -o) chowncmd="$chownprog $2"
- shift;;
-
- -s) stripcmd=$stripprog;;
-
- -t) dst_arg=$2
- shift;;
-
- -T) no_target_directory=true;;
-
- --version) echo "$0 $scriptversion"; exit $?;;
-
- --) shift
- break;;
-
- -*) echo "$0: invalid option: $1" >&2
- exit 1;;
-
- *) break;;
- esac
- shift
-done
-
-if test $# -ne 0 && test -z "$dir_arg$dst_arg"; then
- # When -d is used, all remaining arguments are directories to create.
- # When -t is used, the destination is already specified.
- # Otherwise, the last argument is the destination. Remove it from $@.
- for arg
- do
- if test -n "$dst_arg"; then
- # $@ is not empty: it contains at least $arg.
- set fnord "$@" "$dst_arg"
- shift # fnord
- fi
- shift # arg
- dst_arg=$arg
- done
-fi
-
-if test $# -eq 0; then
- if test -z "$dir_arg"; then
- echo "$0: no input file specified." >&2
- exit 1
- fi
- # It's OK to call `install-sh -d' without argument.
- # This can happen when creating conditional directories.
- exit 0
-fi
-
-if test -z "$dir_arg"; then
- trap '(exit $?); exit' 1 2 13 15
-
- # Set umask so as not to create temps with too-generous modes.
- # However, 'strip' requires both read and write access to temps.
- case $mode in
- # Optimize common cases.
- *644) cp_umask=133;;
- *755) cp_umask=22;;
-
- *[0-7])
- if test -z "$stripcmd"; then
- u_plus_rw=
- else
- u_plus_rw='% 200'
- fi
- cp_umask=`expr '(' 777 - $mode % 1000 ')' $u_plus_rw`;;
- *)
- if test -z "$stripcmd"; then
- u_plus_rw=
- else
- u_plus_rw=,u+rw
- fi
- cp_umask=$mode$u_plus_rw;;
- esac
-fi
-
-for src
-do
- # Protect names starting with `-'.
- case $src in
- -*) src=./$src;;
- esac
-
- if test -n "$dir_arg"; then
- dst=$src
- dstdir=$dst
- test -d "$dstdir"
- dstdir_status=$?
- else
-
- # Waiting for this to be detected by the "$cpprog $src $dsttmp" command
- # might cause directories to be created, which would be especially bad
- # if $src (and thus $dsttmp) contains '*'.
- if test ! -f "$src" && test ! -d "$src"; then
- echo "$0: $src does not exist." >&2
- exit 1
- fi
-
- if test -z "$dst_arg"; then
- echo "$0: no destination specified." >&2
- exit 1
- fi
-
- dst=$dst_arg
- # Protect names starting with `-'.
- case $dst in
- -*) dst=./$dst;;
- esac
-
- # If destination is a directory, append the input filename; won't work
- # if double slashes aren't ignored.
- if test -d "$dst"; then
- if test -n "$no_target_directory"; then
- echo "$0: $dst_arg: Is a directory" >&2
- exit 1
- fi
- dstdir=$dst
- dst=$dstdir/`basename "$src"`
- dstdir_status=0
- else
- # Prefer dirname, but fall back on a substitute if dirname fails.
- dstdir=`
- (dirname "$dst") 2>/dev/null ||
- expr X"$dst" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
- X"$dst" : 'X\(//\)[^/]' \| \
- X"$dst" : 'X\(//\)$' \| \
- X"$dst" : 'X\(/\)' \| . 2>/dev/null ||
- echo X"$dst" |
- sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
- s//\1/
- q
- }
- /^X\(\/\/\)[^/].*/{
- s//\1/
- q
- }
- /^X\(\/\/\)$/{
- s//\1/
- q
- }
- /^X\(\/\).*/{
- s//\1/
- q
- }
- s/.*/./; q'
- `
-
- test -d "$dstdir"
- dstdir_status=$?
- fi
- fi
-
- obsolete_mkdir_used=false
-
- if test $dstdir_status != 0; then
- case $posix_mkdir in
- '')
- # Create intermediate dirs using mode 755 as modified by the umask.
- # This is like FreeBSD 'install' as of 1997-10-28.
- umask=`umask`
- case $stripcmd.$umask in
- # Optimize common cases.
- *[2367][2367]) mkdir_umask=$umask;;
- .*0[02][02] | .[02][02] | .[02]) mkdir_umask=22;;
-
- *[0-7])
- mkdir_umask=`expr $umask + 22 \
- - $umask % 100 % 40 + $umask % 20 \
- - $umask % 10 % 4 + $umask % 2
- `;;
- *) mkdir_umask=$umask,go-w;;
- esac
-
- # With -d, create the new directory with the user-specified mode.
- # Otherwise, rely on $mkdir_umask.
- if test -n "$dir_arg"; then
- mkdir_mode=-m$mode
- else
- mkdir_mode=
- fi
-
- posix_mkdir=false
- case $umask in
- *[123567][0-7][0-7])
- # POSIX mkdir -p sets u+wx bits regardless of umask, which
- # is incompatible with FreeBSD 'install' when (umask & 300) != 0.
- ;;
- *)
- tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$
- trap 'ret=$?; rmdir "$tmpdir/d" "$tmpdir" 2>/dev/null; exit $ret' 0
-
- if (umask $mkdir_umask &&
- exec $mkdirprog $mkdir_mode -p -- "$tmpdir/d") >/dev/null 2>&1
- then
- if test -z "$dir_arg" || {
- # Check for POSIX incompatibilities with -m.
- # HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or
- # other-writeable bit of parent directory when it shouldn't.
- # FreeBSD 6.1 mkdir -m -p sets mode of existing directory.
- ls_ld_tmpdir=`ls -ld "$tmpdir"`
- case $ls_ld_tmpdir in
- d????-?r-*) different_mode=700;;
- d????-?--*) different_mode=755;;
- *) false;;
- esac &&
- $mkdirprog -m$different_mode -p -- "$tmpdir" && {
- ls_ld_tmpdir_1=`ls -ld "$tmpdir"`
- test "$ls_ld_tmpdir" = "$ls_ld_tmpdir_1"
- }
- }
- then posix_mkdir=:
- fi
- rmdir "$tmpdir/d" "$tmpdir"
- else
- # Remove any dirs left behind by ancient mkdir implementations.
- rmdir ./$mkdir_mode ./-p ./-- 2>/dev/null
- fi
- trap '' 0;;
- esac;;
- esac
-
- if
- $posix_mkdir && (
- umask $mkdir_umask &&
- $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir"
- )
- then :
- else
-
- # The umask is ridiculous, or mkdir does not conform to POSIX,
- # or it failed possibly due to a race condition. Create the
- # directory the slow way, step by step, checking for races as we go.
-
- case $dstdir in
- /*) prefix='/';;
- -*) prefix='./';;
- *) prefix='';;
- esac
-
- eval "$initialize_posix_glob"
-
- oIFS=$IFS
- IFS=/
- $posix_glob set -f
- set fnord $dstdir
- shift
- $posix_glob set +f
- IFS=$oIFS
-
- prefixes=
-
- for d
- do
- test -z "$d" && continue
-
- prefix=$prefix$d
- if test -d "$prefix"; then
- prefixes=
- else
- if $posix_mkdir; then
- (umask=$mkdir_umask &&
- $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir") && break
- # Don't fail if two instances are running concurrently.
- test -d "$prefix" || exit 1
- else
- case $prefix in
- *\'*) qprefix=`echo "$prefix" | sed "s/'/'\\\\\\\\''/g"`;;
- *) qprefix=$prefix;;
- esac
- prefixes="$prefixes '$qprefix'"
- fi
- fi
- prefix=$prefix/
- done
-
- if test -n "$prefixes"; then
- # Don't fail if two instances are running concurrently.
- (umask $mkdir_umask &&
- eval "\$doit_exec \$mkdirprog $prefixes") ||
- test -d "$dstdir" || exit 1
- obsolete_mkdir_used=true
- fi
- fi
- fi
-
- if test -n "$dir_arg"; then
- { test -z "$chowncmd" || $doit $chowncmd "$dst"; } &&
- { test -z "$chgrpcmd" || $doit $chgrpcmd "$dst"; } &&
- { test "$obsolete_mkdir_used$chowncmd$chgrpcmd" = false ||
- test -z "$chmodcmd" || $doit $chmodcmd $mode "$dst"; } || exit 1
- else
-
- # Make a couple of temp file names in the proper directory.
- dsttmp=$dstdir/_inst.$$_
- rmtmp=$dstdir/_rm.$$_
-
- # Trap to clean up those temp files at exit.
- trap 'ret=$?; rm -f "$dsttmp" "$rmtmp" && exit $ret' 0
-
- # Copy the file name to the temp name.
- (umask $cp_umask && $doit_exec $cpprog "$src" "$dsttmp") &&
-
- # and set any options; do chmod last to preserve setuid bits.
- #
- # If any of these fail, we abort the whole thing. If we want to
- # ignore errors from any of these, just make sure not to ignore
- # errors from the above "$doit $cpprog $src $dsttmp" command.
- #
- { test -z "$chowncmd" || $doit $chowncmd "$dsttmp"; } &&
- { test -z "$chgrpcmd" || $doit $chgrpcmd "$dsttmp"; } &&
- { test -z "$stripcmd" || $doit $stripcmd "$dsttmp"; } &&
- { test -z "$chmodcmd" || $doit $chmodcmd $mode "$dsttmp"; } &&
-
- # If -C, don't bother to copy if it wouldn't change the file.
- if $copy_on_change &&
- old=`LC_ALL=C ls -dlL "$dst" 2>/dev/null` &&
- new=`LC_ALL=C ls -dlL "$dsttmp" 2>/dev/null` &&
-
- eval "$initialize_posix_glob" &&
- $posix_glob set -f &&
- set X $old && old=:$2:$4:$5:$6 &&
- set X $new && new=:$2:$4:$5:$6 &&
- $posix_glob set +f &&
-
- test "$old" = "$new" &&
- $cmpprog "$dst" "$dsttmp" >/dev/null 2>&1
- then
- rm -f "$dsttmp"
- else
- # Rename the file to the real destination.
- $doit $mvcmd -f "$dsttmp" "$dst" 2>/dev/null ||
-
- # The rename failed, perhaps because mv can't rename something else
- # to itself, or perhaps because mv is so ancient that it does not
- # support -f.
- {
- # Now remove or move aside any old file at destination location.
- # We try this two ways since rm can't unlink itself on some
- # systems and the destination file might be busy for other
- # reasons. In this case, the final cleanup might fail but the new
- # file should still install successfully.
- {
- test ! -f "$dst" ||
- $doit $rmcmd -f "$dst" 2>/dev/null ||
- { $doit $mvcmd -f "$dst" "$rmtmp" 2>/dev/null &&
- { $doit $rmcmd -f "$rmtmp" 2>/dev/null; :; }
- } ||
- { echo "$0: cannot unlink or rename $dst" >&2
- (exit 1); exit 1
- }
- } &&
-
- # Now rename the file to the real destination.
- $doit $mvcmd "$dsttmp" "$dst"
- }
- fi || exit 1
-
- trap '' 0
- fi
-done
-
-# Local variables:
-# eval: (add-hook 'write-file-hooks 'time-stamp)
-# time-stamp-start: "scriptversion="
-# time-stamp-format: "%:y-%02m-%02d.%02H"
-# time-stamp-time-zone: "UTC"
-# time-stamp-end: "; # UTC"
-# End:
diff --git a/m4macros/.gitignore b/m4macros/.gitignore
new file mode 100644
index 0000000..764b428
--- /dev/null
+++ b/m4macros/.gitignore
@@ -0,0 +1,5 @@
+/libtool.m4
+/lt~obsolete.m4
+/ltoptions.m4
+/ltsugar.m4
+/ltversion.m4
diff --git a/missing b/missing
deleted file mode 100755
index 28055d2..0000000
--- a/missing
+++ /dev/null
@@ -1,376 +0,0 @@
-#! /bin/sh
-# Common stub for a few missing GNU programs while installing.
-
-scriptversion=2009-04-28.21; # UTC
-
-# Copyright (C) 1996, 1997, 1999, 2000, 2002, 2003, 2004, 2005, 2006,
-# 2008, 2009 Free Software Foundation, Inc.
-# Originally by Fran,cois Pinard <pinard at iro.umontreal.ca>, 1996.
-
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2, or (at your option)
-# any later version.
-
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-
-# You should have received a copy of the GNU General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
-
-# As a special exception to the GNU General Public License, if you
-# distribute this file as part of a program that contains a
-# configuration script generated by Autoconf, you may include it under
-# the same distribution terms that you use for the rest of that program.
-
-if test $# -eq 0; then
- echo 1>&2 "Try \`$0 --help' for more information"
- exit 1
-fi
-
-run=:
-sed_output='s/.* --output[ =]\([^ ]*\).*/\1/p'
-sed_minuso='s/.* -o \([^ ]*\).*/\1/p'
-
-# In the cases where this matters, `missing' is being run in the
-# srcdir already.
-if test -f configure.ac; then
- configure_ac=configure.ac
-else
- configure_ac=configure.in
-fi
-
-msg="missing on your system"
-
-case $1 in
---run)
- # Try to run requested program, and just exit if it succeeds.
- run=
- shift
- "$@" && exit 0
- # Exit code 63 means version mismatch. This often happens
- # when the user try to use an ancient version of a tool on
- # a file that requires a minimum version. In this case we
- # we should proceed has if the program had been absent, or
- # if --run hadn't been passed.
- if test $? = 63; then
- run=:
- msg="probably too old"
- fi
- ;;
-
- -h|--h|--he|--hel|--help)
- echo "\
-$0 [OPTION]... PROGRAM [ARGUMENT]...
-
-Handle \`PROGRAM [ARGUMENT]...' for when PROGRAM is missing, or return an
-error status if there is no known handling for PROGRAM.
-
-Options:
- -h, --help display this help and exit
- -v, --version output version information and exit
- --run try to run the given command, and emulate it if it fails
-
-Supported PROGRAM values:
- aclocal touch file \`aclocal.m4'
- autoconf touch file \`configure'
- autoheader touch file \`config.h.in'
- autom4te touch the output file, or create a stub one
- automake touch all \`Makefile.in' files
- bison create \`y.tab.[ch]', if possible, from existing .[ch]
- flex create \`lex.yy.c', if possible, from existing .c
- help2man touch the output file
- lex create \`lex.yy.c', if possible, from existing .c
- makeinfo touch the output file
- tar try tar, gnutar, gtar, then tar without non-portable flags
- yacc create \`y.tab.[ch]', if possible, from existing .[ch]
-
-Version suffixes to PROGRAM as well as the prefixes \`gnu-', \`gnu', and
-\`g' are ignored when checking the name.
-
-Send bug reports to <bug-automake at gnu.org>."
- exit $?
- ;;
-
- -v|--v|--ve|--ver|--vers|--versi|--versio|--version)
- echo "missing $scriptversion (GNU Automake)"
- exit $?
- ;;
-
- -*)
- echo 1>&2 "$0: Unknown \`$1' option"
- echo 1>&2 "Try \`$0 --help' for more information"
- exit 1
- ;;
-
-esac
-
-# normalize program name to check for.
-program=`echo "$1" | sed '
- s/^gnu-//; t
- s/^gnu//; t
- s/^g//; t'`
-
-# Now exit if we have it, but it failed. Also exit now if we
-# don't have it and --version was passed (most likely to detect
-# the program). This is about non-GNU programs, so use $1 not
-# $program.
-case $1 in
- lex*|yacc*)
- # Not GNU programs, they don't have --version.
- ;;
-
- tar*)
- if test -n "$run"; then
- echo 1>&2 "ERROR: \`tar' requires --run"
- exit 1
- elif test "x$2" = "x--version" || test "x$2" = "x--help"; then
- exit 1
- fi
- ;;
-
- *)
- if test -z "$run" && ($1 --version) > /dev/null 2>&1; then
- # We have it, but it failed.
- exit 1
- elif test "x$2" = "x--version" || test "x$2" = "x--help"; then
- # Could not run --version or --help. This is probably someone
- # running `$TOOL --version' or `$TOOL --help' to check whether
- # $TOOL exists and not knowing $TOOL uses missing.
- exit 1
- fi
- ;;
-esac
-
-# If it does not exist, or fails to run (possibly an outdated version),
-# try to emulate it.
-case $program in
- aclocal*)
- echo 1>&2 "\
-WARNING: \`$1' is $msg. You should only need it if
- you modified \`acinclude.m4' or \`${configure_ac}'. You might want
- to install the \`Automake' and \`Perl' packages. Grab them from
- any GNU archive site."
- touch aclocal.m4
- ;;
-
- autoconf*)
- echo 1>&2 "\
-WARNING: \`$1' is $msg. You should only need it if
- you modified \`${configure_ac}'. You might want to install the
- \`Autoconf' and \`GNU m4' packages. Grab them from any GNU
- archive site."
- touch configure
- ;;
-
- autoheader*)
- echo 1>&2 "\
-WARNING: \`$1' is $msg. You should only need it if
- you modified \`acconfig.h' or \`${configure_ac}'. You might want
- to install the \`Autoconf' and \`GNU m4' packages. Grab them
- from any GNU archive site."
- files=`sed -n 's/^[ ]*A[CM]_CONFIG_HEADER(\([^)]*\)).*/\1/p' ${configure_ac}`
- test -z "$files" && files="config.h"
- touch_files=
- for f in $files; do
- case $f in
- *:*) touch_files="$touch_files "`echo "$f" |
- sed -e 's/^[^:]*://' -e 's/:.*//'`;;
- *) touch_files="$touch_files $f.in";;
- esac
- done
- touch $touch_files
- ;;
-
- automake*)
- echo 1>&2 "\
-WARNING: \`$1' is $msg. You should only need it if
- you modified \`Makefile.am', \`acinclude.m4' or \`${configure_ac}'.
- You might want to install the \`Automake' and \`Perl' packages.
- Grab them from any GNU archive site."
- find . -type f -name Makefile.am -print |
- sed 's/\.am$/.in/' |
- while read f; do touch "$f"; done
- ;;
-
- autom4te*)
- echo 1>&2 "\
-WARNING: \`$1' is needed, but is $msg.
- You might have modified some files without having the
- proper tools for further handling them.
- You can get \`$1' as part of \`Autoconf' from any GNU
- archive site."
-
- file=`echo "$*" | sed -n "$sed_output"`
- test -z "$file" && file=`echo "$*" | sed -n "$sed_minuso"`
- if test -f "$file"; then
- touch $file
- else
- test -z "$file" || exec >$file
- echo "#! /bin/sh"
- echo "# Created by GNU Automake missing as a replacement of"
- echo "# $ $@"
- echo "exit 0"
- chmod +x $file
- exit 1
- fi
- ;;
-
- bison*|yacc*)
- echo 1>&2 "\
-WARNING: \`$1' $msg. You should only need it if
- you modified a \`.y' file. You may need the \`Bison' package
- in order for those modifications to take effect. You can get
- \`Bison' from any GNU archive site."
- rm -f y.tab.c y.tab.h
- if test $# -ne 1; then
- eval LASTARG="\${$#}"
- case $LASTARG in
- *.y)
- SRCFILE=`echo "$LASTARG" | sed 's/y$/c/'`
- if test -f "$SRCFILE"; then
- cp "$SRCFILE" y.tab.c
- fi
- SRCFILE=`echo "$LASTARG" | sed 's/y$/h/'`
- if test -f "$SRCFILE"; then
- cp "$SRCFILE" y.tab.h
- fi
- ;;
- esac
- fi
- if test ! -f y.tab.h; then
- echo >y.tab.h
- fi
- if test ! -f y.tab.c; then
- echo 'main() { return 0; }' >y.tab.c
- fi
- ;;
-
- lex*|flex*)
- echo 1>&2 "\
-WARNING: \`$1' is $msg. You should only need it if
- you modified a \`.l' file. You may need the \`Flex' package
- in order for those modifications to take effect. You can get
- \`Flex' from any GNU archive site."
- rm -f lex.yy.c
- if test $# -ne 1; then
- eval LASTARG="\${$#}"
- case $LASTARG in
- *.l)
- SRCFILE=`echo "$LASTARG" | sed 's/l$/c/'`
- if test -f "$SRCFILE"; then
- cp "$SRCFILE" lex.yy.c
- fi
- ;;
- esac
- fi
- if test ! -f lex.yy.c; then
- echo 'main() { return 0; }' >lex.yy.c
- fi
- ;;
-
- help2man*)
- echo 1>&2 "\
-WARNING: \`$1' is $msg. You should only need it if
- you modified a dependency of a manual page. You may need the
- \`Help2man' package in order for those modifications to take
- effect. You can get \`Help2man' from any GNU archive site."
-
- file=`echo "$*" | sed -n "$sed_output"`
- test -z "$file" && file=`echo "$*" | sed -n "$sed_minuso"`
- if test -f "$file"; then
- touch $file
- else
- test -z "$file" || exec >$file
- echo ".ab help2man is required to generate this page"
- exit $?
- fi
- ;;
-
- makeinfo*)
- echo 1>&2 "\
-WARNING: \`$1' is $msg. You should only need it if
- you modified a \`.texi' or \`.texinfo' file, or any other file
- indirectly affecting the aspect of the manual. The spurious
- call might also be the consequence of using a buggy \`make' (AIX,
- DU, IRIX). You might want to install the \`Texinfo' package or
- the \`GNU make' package. Grab either from any GNU archive site."
- # The file to touch is that specified with -o ...
- file=`echo "$*" | sed -n "$sed_output"`
- test -z "$file" && file=`echo "$*" | sed -n "$sed_minuso"`
- if test -z "$file"; then
- # ... or it is the one specified with @setfilename ...
- infile=`echo "$*" | sed 's/.* \([^ ]*\) *$/\1/'`
- file=`sed -n '
- /^@setfilename/{
- s/.* \([^ ]*\) *$/\1/
- p
- q
- }' $infile`
- # ... or it is derived from the source name (dir/f.texi becomes f.info)
- test -z "$file" && file=`echo "$infile" | sed 's,.*/,,;s,.[^.]*$,,'`.info
- fi
- # If the file does not exist, the user really needs makeinfo;
- # let's fail without touching anything.
- test -f $file || exit 1
- touch $file
- ;;
-
- tar*)
- shift
-
- # We have already tried tar in the generic part.
- # Look for gnutar/gtar before invocation to avoid ugly error
- # messages.
- if (gnutar --version > /dev/null 2>&1); then
- gnutar "$@" && exit 0
- fi
- if (gtar --version > /dev/null 2>&1); then
- gtar "$@" && exit 0
- fi
- firstarg="$1"
- if shift; then
- case $firstarg in
- *o*)
- firstarg=`echo "$firstarg" | sed s/o//`
- tar "$firstarg" "$@" && exit 0
- ;;
- esac
- case $firstarg in
- *h*)
- firstarg=`echo "$firstarg" | sed s/h//`
- tar "$firstarg" "$@" && exit 0
- ;;
- esac
- fi
-
- echo 1>&2 "\
-WARNING: I can't seem to be able to run \`tar' with the given arguments.
- You may want to install GNU tar or Free paxutils, or check the
- command line arguments."
- exit 1
- ;;
-
- *)
- echo 1>&2 "\
-WARNING: \`$1' is needed, and is $msg.
- You might have modified some files without having the
- proper tools for further handling them. Check the \`README' file,
- it often tells you about the needed prerequisites for installing
- this package. You may also peek at any GNU archive site, in case
- some other package would contain this missing \`$1' program."
- exit 1
- ;;
-esac
-
-exit 0
-
-# Local variables:
-# eval: (add-hook 'write-file-hooks 'time-stamp)
-# time-stamp-start: "scriptversion="
-# time-stamp-format: "%:y-%02m-%02d.%02H"
-# time-stamp-time-zone: "UTC"
-# time-stamp-end: "; # UTC"
-# End:
diff --git a/src/bin/auth/auth_srv.h b/src/bin/auth/auth_srv.h
index 32b53dd..3be711b 100644
--- a/src/bin/auth/auth_srv.h
+++ b/src/bin/auth/auth_srv.h
@@ -277,7 +277,7 @@ public:
/// in-memory data source.
///
/// \param rrclass The RR class of the in-memory data source to be set.
- /// \param memory_datasrc A (shared) pointer to \c InMemoryClient to be set.
+ /// \param memory_client A (shared) pointer to \c InMemoryClient to be set.
void setInMemoryClient(const isc::dns::RRClass& rrclass,
InMemoryClientPtr memory_client);
diff --git a/src/bin/auth/b10-auth.8 b/src/bin/auth/b10-auth.8
index a5ef4fb..4d146f5 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: March 28, 2012
+.\" Date: May 16, 2012
.\" Manual: BIND10
.\" Source: BIND10
.\" Language: English
.\"
-.TH "B10\-AUTH" "8" "March 28, 2012" "BIND10" "BIND10"
+.TH "B10\-AUTH" "8" "May 16, 2012" "BIND10" "BIND10"
.\" -----------------------------------------------------------------
.\" * set default formatting
.\" -----------------------------------------------------------------
@@ -72,9 +72,21 @@ to optionally select the class (it defaults to
\fIzones\fR
to define the
\fIfile\fR
-path name and the
+path name,
\fIorigin\fR
-(default domain)\&. By default, this is empty\&.
+(default domain), and optional
+\fIfiletype\fR\&. By default,
+\fIzones\fR
+is empty\&. For the in\-memory data source (i\&.e\&., the
+\fItype\fR
+is
+\(lqmemory\(rq), the optional
+\fIfiletype\fR
+configuration item for
+\fIzones\fR
+can be specified so the in\-memory zone data can be built from another data source that is based on a database backend (in practice with current implementation, it would be an SQLite3 database file for the SQLite3 data source)\&. See the
+BIND 10 Guide
+for configuration details\&.
.if n \{\
.sp
.\}
@@ -88,7 +100,7 @@ path name and the
.ps -1
.br
.sp
-In this development version, currently this is only used for the memory data source\&. Only the IN class is supported at this time\&. By default, the memory data source is disabled\&. Also, currently the zone file must be canonical such as generated by \fBnamed\-compilezone \-D\fR\&.
+Only the IN class is supported at this time\&. By default, the memory data source is disabled\&. Also, currently the zone file must be canonical such as generated by \fBnamed\-compilezone \-D\fR\&.
.sp .5v
.RE
.PP
diff --git a/src/bin/auth/b10-auth.xml b/src/bin/auth/b10-auth.xml
index 7f3a492..a6010d1 100644
--- a/src/bin/auth/b10-auth.xml
+++ b/src/bin/auth/b10-auth.xml
@@ -20,7 +20,7 @@
<refentry>
<refentryinfo>
- <date>March 28, 2012</date>
+ <date>May 16, 2012</date>
</refentryinfo>
<refmeta>
@@ -125,14 +125,21 @@
(it defaults to <quote>IN</quote>);
and
<varname>zones</varname> to define the
- <varname>file</varname> path name and the
- <varname>origin</varname> (default domain).
-
- By default, this is empty.
+ <varname>file</varname> path name,
+ <varname>origin</varname> (default domain), and optional
+ <varname>filetype</varname>.
+ By default, <varname>zones</varname> is empty.
+ For the in-memory data source (i.e., the <varname>type</varname>
+ is <quote>memory</quote>), the optional <varname>filetype</varname>
+ configuration item for <varname>zones</varname> can be
+ specified so the in-memory zone data can be built from another
+ data source that is based on a database backend (in practice
+ with current implementation, it would be an SQLite3 database
+ file for the SQLite3 data source).
+ See the <citetitle>BIND 10 Guide</citetitle> for configuration
+ details.
<note><simpara>
- In this development version, currently this is only used for the
- memory data source.
Only the IN class is supported at this time.
By default, the memory data source is disabled.
Also, currently the zone file must be canonical such as
diff --git a/src/bin/auth/command.cc b/src/bin/auth/command.cc
index ad0d134..055c73a 100644
--- a/src/bin/auth/command.cc
+++ b/src/bin/auth/command.cc
@@ -18,6 +18,7 @@
#include <cc/data.h>
#include <datasrc/memory_datasrc.h>
+#include <datasrc/factory.h>
#include <config/ccsession.h>
#include <exceptions/exceptions.h>
#include <dns/rrclass.h>
@@ -149,26 +150,39 @@ public:
return;
}
+ const ConstElementPtr zone_config = getZoneConfig(server);
+
// Load a new zone and replace the current zone with the new one.
// TODO: eventually this should be incremental or done in some way
// that doesn't block other server operations.
// TODO: we may (should?) want to check the "last load time" and
// the timestamp of the file and skip loading if the file isn't newer.
+ const ConstElementPtr type(zone_config->get("filetype"));
boost::shared_ptr<InMemoryZoneFinder> zone_finder(
- new InMemoryZoneFinder(old_zone_finder->getClass(),
- old_zone_finder->getOrigin()));
- zone_finder->load(old_zone_finder->getFileName());
- old_zone_finder->swap(*zone_finder);
+ new InMemoryZoneFinder(old_zone_finder_->getClass(),
+ old_zone_finder_->getOrigin()));
+ if (type && type->stringValue() == "sqlite3") {
+ scoped_ptr<DataSourceClientContainer>
+ container(new DataSourceClientContainer("sqlite3",
+ Element::fromJSON(
+ "{\"database_file\": \"" +
+ zone_config->get("file")->stringValue() + "\"}")));
+ zone_finder->load(*container->getInstance().getIterator(
+ old_zone_finder_->getOrigin()));
+ } else {
+ zone_finder->load(old_zone_finder_->getFileName());
+ }
+ old_zone_finder_->swap(*zone_finder);
LOG_DEBUG(auth_logger, DBG_AUTH_OPS, AUTH_LOAD_ZONE)
.arg(zone_finder->getOrigin()).arg(zone_finder->getClass());
}
private:
// zone finder to be updated with the new file.
- boost::shared_ptr<InMemoryZoneFinder> old_zone_finder;
+ boost::shared_ptr<InMemoryZoneFinder> old_zone_finder_;
// A helper private method to parse and validate command parameters.
- // On success, it sets 'old_zone_finder' to the zone to be updated.
+ // On success, it sets 'old_zone_finder_' to the zone to be updated.
// It returns true if everything is okay; and false if the command is
// valid but there's no need for further process.
bool validate(AuthSrv& server, isc::data::ConstElementPtr args) {
@@ -193,10 +207,11 @@ private:
}
ConstElementPtr class_elem = args->get("class");
- const RRClass zone_class = class_elem ?
- RRClass(class_elem->stringValue()) : RRClass::IN();
+ const RRClass zone_class =
+ class_elem ? RRClass(class_elem->stringValue()) : RRClass::IN();
- AuthSrv::InMemoryClientPtr datasrc(server.getInMemoryClient(zone_class));
+ AuthSrv::InMemoryClientPtr datasrc(server.
+ getInMemoryClient(zone_class));
if (datasrc == NULL) {
isc_throw(AuthCommandError, "Memory data source is disabled");
}
@@ -205,7 +220,7 @@ private:
if (!origin_elem) {
isc_throw(AuthCommandError, "Zone origin is missing");
}
- const Name origin(origin_elem->stringValue());
+ const Name origin = Name(origin_elem->stringValue());
// Get the current zone
const InMemoryClient::FindResult result = datasrc->findZone(origin);
@@ -214,11 +229,70 @@ private:
" is not found in data source");
}
- old_zone_finder = boost::dynamic_pointer_cast<InMemoryZoneFinder>(
+ old_zone_finder_ = boost::dynamic_pointer_cast<InMemoryZoneFinder>(
result.zone_finder);
return (true);
}
+
+ ConstElementPtr getZoneConfig(const AuthSrv &server) {
+ if (!server.getConfigSession()) {
+ // FIXME: This is a hack to make older tests pass. We should
+ // update these tests as well sometime and remove this hack.
+ // (note that under normal situation, the
+ // server.getConfigSession() does not return NULL)
+
+ // We provide an empty map, which means no configuration --
+ // defaults.
+ return (ConstElementPtr(new MapElement()));
+ }
+
+ // Find the config corresponding to the zone.
+ // We expect the configuration to be valid, as we have it and we
+ // accepted it before, therefore it must be validated.
+ const ConstElementPtr config(server.getConfigSession()->
+ getValue("datasources"));
+ ConstElementPtr zone_list;
+ // Unfortunately, we need to walk the list to find the correct data
+ // source.
+ // TODO: Make it named sets. These lists are uncomfortable.
+ for (size_t i(0); i < config->size(); ++i) {
+ // We use the getValue to get defaults as well
+ const ConstElementPtr dsrc_config(config->get(i));
+ const ConstElementPtr class_config(dsrc_config->get("class"));
+ const string class_type(class_config ?
+ class_config->stringValue() : "IN");
+ // It is in-memory and our class matches.
+ // FIXME: Is it allowed to have two datasources for the same
+ // type and class at once? It probably would not work now
+ // anyway and we may want to change the configuration of
+ // datasources somehow.
+ if (dsrc_config->get("type")->stringValue() == "memory" &&
+ RRClass(class_type) == old_zone_finder_->getClass()) {
+ zone_list = dsrc_config->get("zones");
+ break;
+ }
+ }
+
+ if (!zone_list) {
+ isc_throw(AuthCommandError,
+ "Corresponding data source configuration was not found");
+ }
+
+ // Now we need to walk the zones and find the correct one.
+ for (size_t i(0); i < zone_list->size(); ++i) {
+ const ConstElementPtr zone_config(zone_list->get(i));
+ if (Name(zone_config->get("origin")->stringValue()) ==
+ old_zone_finder_->getOrigin()) {
+ // The origins are the same, so we consider this config to be
+ // for the zone.
+ return (zone_config);
+ }
+ }
+
+ isc_throw(AuthCommandError,
+ "Corresponding zone configuration was not found");
+ }
};
// The factory of command objects.
diff --git a/src/bin/auth/tests/Makefile.am b/src/bin/auth/tests/Makefile.am
index b33c7af..f9fac2f 100644
--- a/src/bin/auth/tests/Makefile.am
+++ b/src/bin/auth/tests/Makefile.am
@@ -19,10 +19,11 @@ endif
CLEANFILES = *.gcno *.gcda
+# Do not define global tests, use check-local so
+# environment can be set (needed for dynamic loading)
TESTS =
if HAVE_GTEST
-TESTS += run_unittests
run_unittests_SOURCES = $(top_srcdir)/src/lib/dns/tests/unittest_util.h
run_unittests_SOURCES += $(top_srcdir)/src/lib/dns/tests/unittest_util.cc
run_unittests_SOURCES += ../auth_srv.h ../auth_srv.cc
@@ -70,6 +71,10 @@ run_unittests_LDADD += $(top_builddir)/src/lib/server_common/libserver_common.la
run_unittests_LDADD += $(top_builddir)/src/lib/nsas/libnsas.la
run_unittests_LDADD += $(top_builddir)/src/lib/util/unittests/libutil_unittests.la
run_unittests_LDADD += $(top_builddir)/src/lib/statistics/libstatistics.la
-endif
-noinst_PROGRAMS = $(TESTS)
+check-local:
+ B10_FROM_BUILD=${abs_top_builddir} ./run_unittests
+
+noinst_PROGRAMS = run_unittests
+
+endif
diff --git a/src/bin/auth/tests/command_unittest.cc b/src/bin/auth/tests/command_unittest.cc
index 376087b..bcaf4b1 100644
--- a/src/bin/auth/tests/command_unittest.cc
+++ b/src/bin/auth/tests/command_unittest.cc
@@ -14,6 +14,8 @@
#include <config.h>
+#include "datasrc_util.h"
+
#include <auth/auth_srv.h>
#include <auth/auth_config.h>
#include <auth/command.h>
@@ -50,8 +52,10 @@ using namespace isc::data;
using namespace isc::datasrc;
using namespace isc::config;
using namespace isc::testutils;
+using namespace isc::auth::unittest;
namespace {
+
class AuthCommandTest : public ::testing::Test {
protected:
AuthCommandTest() :
@@ -191,9 +195,9 @@ zoneChecks(AuthSrv& server) {
void
configureZones(AuthSrv& server) {
- ASSERT_EQ(0, system(INSTALL_PROG " " TEST_DATA_DIR "/test1.zone.in "
+ ASSERT_EQ(0, system(INSTALL_PROG " -c " TEST_DATA_DIR "/test1.zone.in "
TEST_DATA_BUILDDIR "/test1.zone.copied"));
- ASSERT_EQ(0, system(INSTALL_PROG " " TEST_DATA_DIR "/test2.zone.in "
+ ASSERT_EQ(0, system(INSTALL_PROG " -c " TEST_DATA_DIR "/test2.zone.in "
TEST_DATA_BUILDDIR "/test2.zone.copied"));
configureAuthServer(server, Element::fromJSON(
"{\"datasources\": "
@@ -232,10 +236,10 @@ newZoneChecks(AuthSrv& server) {
TEST_F(AuthCommandTest, loadZone) {
configureZones(server_);
- ASSERT_EQ(0, system(INSTALL_PROG " " TEST_DATA_DIR
+ ASSERT_EQ(0, system(INSTALL_PROG " -c " TEST_DATA_DIR
"/test1-new.zone.in "
TEST_DATA_BUILDDIR "/test1.zone.copied"));
- ASSERT_EQ(0, system(INSTALL_PROG " " TEST_DATA_DIR
+ ASSERT_EQ(0, system(INSTALL_PROG " -c " TEST_DATA_DIR
"/test2-new.zone.in "
TEST_DATA_BUILDDIR "/test2.zone.copied"));
@@ -246,10 +250,133 @@ TEST_F(AuthCommandTest, loadZone) {
newZoneChecks(server_);
}
+// This test uses dynamic load of a data source module, and won't work when
+// statically linked.
+TEST_F(AuthCommandTest,
+#ifdef USE_STATIC_LINK
+ DISABLED_loadZoneSQLite3
+#else
+ loadZoneSQLite3
+#endif
+ )
+{
+ const char* const SPEC_FILE = AUTH_OBJ_DIR "/auth.spec";
+
+ // Prepare the database first
+ const string test_db = TEST_DATA_BUILDDIR "/auth_test.sqlite3.copied";
+ const string bad_db = TEST_DATA_BUILDDIR "/does-not-exist.sqlite3";
+ stringstream ss("example.org. 3600 IN SOA . . 0 0 0 0 0\n");
+ createSQLite3DB(RRClass::IN(), Name("example.org"), test_db.c_str(), ss);
+
+ // Then store a config of the zone to the auth server
+ // This omits many config options of the auth server, but these are
+ // not read now.
+ isc::testutils::MockSession session;
+ // The session should not take care of anything or start anything, we
+ // need it only to hold the config we're going to put into it.
+ ModuleCCSession module_session(SPEC_FILE, session, NULL, NULL, false,
+ false);
+ // This describes the data source in the configuration
+ const ElementPtr
+ map(Element::fromJSON("{\"datasources\": ["
+ " {"
+ " \"type\": \"memory\","
+ " \"zones\": ["
+ " {"
+ " \"origin\": \"example.org\","
+ " \"file\": \"" + test_db + "\","
+ " \"filetype\": \"sqlite3\""
+ " }"
+ " ]"
+ " }"
+ "]}"));
+ module_session.setLocalConfig(map);
+ server_.setConfigSession(&module_session);
+
+ // The loadzone command needs the zone to be already loaded, because
+ // it is used for reloading only
+ AuthSrv::InMemoryClientPtr dsrc(new InMemoryClient());
+ dsrc->addZone(ZoneFinderPtr(new InMemoryZoneFinder(RRClass::IN(),
+ Name("example.org"))));
+ server_.setInMemoryClient(RRClass::IN(), dsrc);
+
+ // Now send the command to reload it
+ result_ = execAuthServerCommand(server_, "loadzone",
+ Element::fromJSON("{\"origin\": \"example.org\"}"));
+ checkAnswer(0);
+
+ // Get the zone and look if there are data in it (the original one was
+ // empty)
+ ASSERT_TRUE(server_.getInMemoryClient(RRClass::IN()));
+ EXPECT_EQ(ZoneFinder::SUCCESS, server_.getInMemoryClient(RRClass::IN())->
+ findZone(Name("example.org")).zone_finder->
+ find(Name("example.org"), RRType::SOA())->code);
+
+ // Some error cases. First, the zone has no configuration.
+ dsrc->addZone(ZoneFinderPtr(new InMemoryZoneFinder(RRClass::IN(),
+ Name("example.com"))));
+ result_ = execAuthServerCommand(server_, "loadzone",
+ Element::fromJSON("{\"origin\": \"example.com\"}"));
+ checkAnswer(1);
+
+ // The previous zone is not hurt in any way
+ EXPECT_EQ(ZoneFinder::SUCCESS, server_.getInMemoryClient(RRClass::IN())->
+ findZone(Name("example.org")).zone_finder->
+ find(Name("example.org"), RRType::SOA())->code);
+
+ module_session.setLocalConfig(Element::fromJSON("{\"datasources\": []}"));
+ result_ = execAuthServerCommand(server_, "loadzone",
+ Element::fromJSON("{\"origin\": \"example.org\"}"));
+ checkAnswer(1);
+
+ // The previous zone is not hurt in any way
+ EXPECT_EQ(ZoneFinder::SUCCESS, server_.getInMemoryClient(RRClass::IN())->
+ findZone(Name("example.org")).zone_finder->
+ find(Name("example.org"), RRType::SOA())->code);
+ // Configure an unreadable zone. Should fail, but leave the original zone
+ // data there
+ const ElementPtr
+ mapBad(Element::fromJSON("{\"datasources\": ["
+ " {"
+ " \"type\": \"memory\","
+ " \"zones\": ["
+ " {"
+ " \"origin\": \"example.org\","
+ " \"file\": \"" + bad_db + "\","
+ " \"filetype\": \"sqlite3\""
+ " }"
+ " ]"
+ " }"
+ "]}"));
+ module_session.setLocalConfig(mapBad);
+ result_ = execAuthServerCommand(server_, "loadzone",
+ Element::fromJSON("{\"origin\": \"example.com\"}"));
+ checkAnswer(1);
+ // The previous zone is not hurt in any way
+ EXPECT_EQ(ZoneFinder::SUCCESS, server_.getInMemoryClient(RRClass::IN())->
+ findZone(Name("example.org")).zone_finder->
+ find(Name("example.org"), RRType::SOA())->code);
+
+ // Broken configuration (not valid against the spec)
+ const ElementPtr
+ broken(Element::fromJSON("{\"datasources\": ["
+ " {"
+ " \"type\": \"memory\","
+ " \"zones\": [[]]"
+ " }"
+ "]}"));
+ module_session.setLocalConfig(broken);
+ checkAnswer(1);
+ // The previous zone is not hurt in any way
+ EXPECT_EQ(ZoneFinder::SUCCESS, server_.getInMemoryClient(RRClass::IN())->
+ findZone(Name("example.org")).zone_finder->
+ find(Name("example.org"), RRType::SOA())->code);
+}
+
TEST_F(AuthCommandTest, loadBrokenZone) {
configureZones(server_);
- ASSERT_EQ(0, system(INSTALL_PROG " " TEST_DATA_DIR
+ ASSERT_EQ(0, system(INSTALL_PROG " -c " TEST_DATA_DIR
"/test1-broken.zone.in "
TEST_DATA_BUILDDIR "/test1.zone.copied"));
result_ = execAuthServerCommand(server_, "loadzone",
@@ -263,7 +390,7 @@ TEST_F(AuthCommandTest, loadUnreadableZone) {
configureZones(server_);
// install the zone file as unreadable
- ASSERT_EQ(0, system(INSTALL_PROG " -m 000 " TEST_DATA_DIR
+ ASSERT_EQ(0, system(INSTALL_PROG " -c -m 000 " TEST_DATA_DIR
"/test1.zone.in "
TEST_DATA_BUILDDIR "/test1.zone.copied"));
result_ = execAuthServerCommand(server_, "loadzone",
diff --git a/src/bin/auth/tests/common_unittest.cc b/src/bin/auth/tests/common_unittest.cc
index 9b18142..184988d 100644
--- a/src/bin/auth/tests/common_unittest.cc
+++ b/src/bin/auth/tests/common_unittest.cc
@@ -36,7 +36,7 @@ public:
BOOST_FOREACH(const Environ &env, restoreEnviron) {
if (env.second == NULL) {
EXPECT_EQ(0, unsetenv(env.first.c_str())) <<
- "Couldn't restore environment, results of other tests"
+ "Couldn't restore environment, results of other tests "
"are uncertain";
} else {
EXPECT_EQ(0, setenv(env.first.c_str(), env.second->c_str(),
diff --git a/src/bin/auth/tests/datasrc_util.cc b/src/bin/auth/tests/datasrc_util.cc
index d9e99b6..664ae8c 100644
--- a/src/bin/auth/tests/datasrc_util.cc
+++ b/src/bin/auth/tests/datasrc_util.cc
@@ -55,7 +55,7 @@ createSQLite3DB(RRClass zclass, const Name& zname,
{
// We always begin with an empty template SQLite3 DB file and install
// the zone data from the zone file.
- const char* const install_cmd_prefix = INSTALL_PROG " " TEST_DATA_DIR
+ const char* const install_cmd_prefix = INSTALL_PROG " -c " TEST_DATA_DIR
"/rwtest.sqlite3 ";
const string install_cmd = string(install_cmd_prefix) + db_file;
if (system(install_cmd.c_str()) != 0) {
diff --git a/src/bin/auth/tests/datasrc_util.h b/src/bin/auth/tests/datasrc_util.h
index fc4109b..07ebc0c 100644
--- a/src/bin/auth/tests/datasrc_util.h
+++ b/src/bin/auth/tests/datasrc_util.h
@@ -24,10 +24,7 @@ namespace isc {
namespace auth {
namespace unittest {
-// Here we define utility modules for the convenience of tests that create
-// a data source client according to the specified conditions.
-
-/// \brief Create an SQLite3 data source client from a stream.
+/// \brief Create an SQLite3 database file for a given zone from a stream.
///
/// This function creates an SQLite3 DB file for the specified zone
/// with specified content. The zone will be created in the given
diff --git a/src/bin/bind10/bind10.8 b/src/bin/bind10/bind10.8
index 2dafaab..8d6e5ed 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: March 1, 2012
+.\" Date: April 12, 2012
.\" Manual: BIND10
.\" Source: BIND10
.\" Language: English
.\"
-.TH "BIND10" "8" "March 1, 2012" "BIND10" "BIND10"
+.TH "BIND10" "8" "April 12, 2012" "BIND10" "BIND10"
.\" -----------------------------------------------------------------
.\" * set default formatting
.\" -----------------------------------------------------------------
@@ -22,7 +22,7 @@
bind10 \- BIND 10 boss process
.SH "SYNOPSIS"
.HP \w'\fBbind10\fR\ 'u
-\fBbind10\fR [\fB\-c\ \fR\fB\fIconfig\-filename\fR\fR] [\fB\-i\fR] [\fB\-m\ \fR\fB\fIfile\fR\fR] [\fB\-n\fR] [\fB\-p\ \fR\fB\fIdata_path\fR\fR] [\fB\-u\ \fR\fB\fIuser\fR\fR] [\fB\-v\fR] [\fB\-w\ \fR\fB\fIwait_time\fR\fR] [\fB\-\-cmdctl\-port\fR\ \fIport\fR] [\fB\-\-config\-file\fR\ \fIconfig\-filename\fR] [\fB\-\-data\-path\fR\ \fIdirectory\fR] [\fB\-\-msgq\-socket\-file\ \fR\fB\fIfile\fR\fR] [\fB\-\-no\-cache\fR] [\fB\-\-no\-kill\fR] [\fB\-\-pid\-file\fR\ \fIfilename\fR] [\fB\-\-pretty\-name\ \fR\fB\fIname\fR\fR] [\fB\-\-user\ \fR\fB\fIuser\fR\fR] [\fB\-\-verbose\fR] [\fB\-\-wait\ \fR\fB\fIwait_time\fR\fR]
+\fBbind10\fR [\fB\-c\ \fR\fB\fIconfig\-filename\fR\fR] [\fB\-i\fR] [\fB\-m\ \fR\fB\fIfile\fR\fR] [\fB\-n\fR] [\fB\-p\ \fR\fB\fIdata_path\fR\fR] [\fB\-u\ \fR\fB\fIuser\fR\fR] [\fB\-v\fR] [\fB\-w\ \fR\fB\fIwait_time\fR\fR] [\fB\-\-clear\-config\fR] [\fB\-\-cmdctl\-port\fR\ \fIport\fR] [\fB\-\-config\-file\fR\ \fIconfig\-filename\fR] [\fB\-\-data\-path\fR\ \fIdirectory\fR] [\fB\-\-msgq\-socket\-file\ \fR\fB\fIfile\fR\fR] [\fB\-\-no\-cache\fR] [\fB\-\-no\-kill\fR] [\fB\-\-pid\-file\fR\ \fIfilename\fR] [\fB\-\-pretty\-name\ \fR\fB\fIname\fR\fR] [\fB\-\-user\ \fR\fB\fIuser\fR\fR] [\fB\-\-verbose\fR] [\fB\-\-wait\ \fR\fB\fIwait_time\fR\fR]
.SH "DESCRIPTION"
.PP
The
@@ -38,6 +38,13 @@ The configuration filename to use\&. Can be either absolute or relative to data
b10\-config\&.db\&.
.RE
.PP
+\fB\-\-clear\-config\fR
+.RS 4
+This will create a backup of the existing configuration file, remove it and start
+b10\-cfgmgr(8)
+with the default configuration\&. The name of the backup file can be found in the logs (\fICFGMGR_RENAMED_CONFIG_FILE\fR)\&. (It will append a number to the backup filename if a previous backup file exists\&.)
+.RE
+.PP
\fB\-\-cmdctl\-port\fR \fIport\fR
.RS 4
The
@@ -130,18 +137,6 @@ to manage under
.IP \(bu 2.3
.\}
-\fI/Boss/components/b10\-auth\fR
-.RE
-.sp
-.RS 4
-.ie n \{\
-\h'-04'\(bu\h'+03'\c
-.\}
-.el \{\
-.sp -1
-.IP \(bu 2.3
-.\}
-
\fI/Boss/components/b10\-cmdctl\fR
.RE
.sp
@@ -156,54 +151,6 @@ to manage under
\fI/Boss/components/b10\-stats\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\-httpd\fR
-.RE
-.sp
-.RS 4
-.ie n \{\
-\h'-04'\(bu\h'+03'\c
-.\}
-.el \{\
-.sp -1
-.IP \(bu 2.3
-.\}
-
-\fI/Boss/components/b10\-xfrin\fR
-.RE
-.sp
-.RS 4
-.ie n \{\
-\h'-04'\(bu\h'+03'\c
-.\}
-.el \{\
-.sp -1
-.IP \(bu 2.3
-.\}
-
-\fI/Boss/components/b10\-xfrout\fR
-.RE
-.sp
-.RS 4
-.ie n \{\
-\h'-04'\(bu\h'+03'\c
-.\}
-.el \{\
-.sp -1
-.IP \(bu 2.3
-.\}
-
-\fI/Boss/components/b10\-zonemgr\fR
-.RE
.PP
(Note that the startup of
\fBb10\-sockcreator\fR,
diff --git a/src/bin/bind10/bind10.xml b/src/bin/bind10/bind10.xml
index 9a8f2fe..60449de 100644
--- a/src/bin/bind10/bind10.xml
+++ b/src/bin/bind10/bind10.xml
@@ -20,7 +20,7 @@
<refentry>
<refentryinfo>
- <date>March 1, 2012</date>
+ <date>April 12, 2012</date>
</refentryinfo>
<refmeta>
@@ -52,6 +52,7 @@
<arg><option>-u <replaceable>user</replaceable></option></arg>
<arg><option>-v</option></arg>
<arg><option>-w <replaceable>wait_time</replaceable></option></arg>
+ <arg><option>--clear-config</option></arg>
<arg><option>--cmdctl-port</option> <replaceable>port</replaceable></arg>
<arg><option>--config-file</option> <replaceable>config-filename</replaceable></arg>
<arg><option>--data-path</option> <replaceable>directory</replaceable></arg>
@@ -106,6 +107,25 @@
<varlistentry>
<term>
+ <option>--clear-config</option>
+ </term>
+ <listitem>
+ <para>
+ This will create a backup of the existing configuration
+ file, remove it and start
+ <refentrytitle>b10-cfgmgr</refentrytitle><manvolnum>8</manvolnum>
+ with the default configuration.
+ The name of the backup file can be found in the logs
+ (<varname>CFGMGR_RENAMED_CONFIG_FILE</varname>).
+ (It will append a number to the backup filename if a
+ previous backup file exists.)
+
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>
<option>--cmdctl-port</option> <replaceable>port</replaceable>
</term>
<listitem>
@@ -251,10 +271,6 @@ TODO: configuration section
<para> <varname>/Boss/components/b10-stats</varname> </para>
</listitem>
- <listitem>
- <para> <varname>/Boss/components/b10-stats-httpd</varname> </para>
- </listitem>
-
</itemizedlist>
<para>
diff --git a/src/bin/bind10/bind10_messages.mes b/src/bin/bind10/bind10_messages.mes
index 3dd938f..4e7b49b 100644
--- a/src/bin/bind10/bind10_messages.mes
+++ b/src/bin/bind10/bind10_messages.mes
@@ -20,10 +20,6 @@ The boss process is starting up and will now check if the message bus
daemon is already running. If so, it will not be able to start, as it
needs a dedicated message bus.
-% BIND10_INVALID_STATISTICS_DATA invalid specification of statistics data specified
-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: %3
The process terminated, but the bind10 boss didn't expect it to, which means
it must have failed.
@@ -86,6 +82,10 @@ the boss process will try to force them).
A debug message. The configurator is about to perform one task of the plan it
is currently executing on the named component.
+% BIND10_INVALID_STATISTICS_DATA invalid specification of statistics data specified
+An error was encountered when the boss module specified
+statistics data which is invalid for the boss specification file.
+
% BIND10_INVALID_USER invalid user: %1
The boss process was started with the -u option, to drop root privileges
and continue running as the specified user, but the user is unknown.
@@ -290,4 +290,3 @@ the configuration manager to start up. The total length of time Boss
will wait for the configuration manager before reporting an error is
set with the command line --wait switch, which has a default value of
ten seconds.
-
diff --git a/src/bin/bind10/bob.spec b/src/bin/bind10/bob.spec
index b358f96..8b75640 100644
--- a/src/bin/bind10/bob.spec
+++ b/src/bin/bind10/bob.spec
@@ -9,10 +9,6 @@
"item_optional": false,
"item_default": {
"b10-stats": { "address": "Stats", "kind": "dispensable" },
- "b10-stats-httpd": {
- "address": "StatsHttpd",
- "kind": "dispensable"
- },
"b10-cmdctl": { "special": "cmdctl", "kind": "needed" }
},
"named_set_item_spec": {
diff --git a/src/bin/bindctl/Makefile.am b/src/bin/bindctl/Makefile.am
index 700f26e..52a5145 100644
--- a/src/bin/bindctl/Makefile.am
+++ b/src/bin/bindctl/Makefile.am
@@ -8,7 +8,7 @@ EXTRA_DIST = $(man_MANS) bindctl.xml
noinst_SCRIPTS = run_bindctl.sh
python_PYTHON = __init__.py bindcmd.py cmdparse.py exception.py moduleinfo.py \
- mycollections.py
+ mycollections.py command_sets.py
pythondir = $(pyexecdir)/bindctl
bindctldir = $(pkgdatadir)
diff --git a/src/bin/bindctl/bindcmd.py b/src/bin/bindctl/bindcmd.py
index 0271c9c..f1a622e 100644
--- a/src/bin/bindctl/bindcmd.py
+++ b/src/bin/bindctl/bindcmd.py
@@ -23,6 +23,7 @@ from cmd import Cmd
from bindctl.exception import *
from bindctl.moduleinfo import *
from bindctl.cmdparse import BindCmdParse
+from bindctl import command_sets
from xml.dom import minidom
import isc
import isc.cc.data
@@ -37,6 +38,7 @@ from hashlib import sha1
import csv
import pwd
import getpass
+import copy
try:
from collections import OrderedDict
@@ -393,8 +395,9 @@ class BindCmdInterpreter(Cmd):
param_nr += 1
# Convert parameter value according parameter spec file.
- # Ignore check for commands belongs to module 'config'
- if cmd.module != CONFIG_MODULE_NAME:
+ # Ignore check for commands belongs to module 'config' or 'execute
+ if cmd.module != CONFIG_MODULE_NAME and\
+ cmd.module != command_sets.EXECUTE_MODULE_NAME:
for param_name in cmd.params:
param_spec = command_info.get_param_with_name(param_name).param_spec
try:
@@ -408,16 +411,9 @@ class BindCmdInterpreter(Cmd):
if cmd.command == "help" or ("help" in cmd.params.keys()):
self._handle_help(cmd)
elif cmd.module == CONFIG_MODULE_NAME:
- try:
- self.apply_config_cmd(cmd)
- except isc.cc.data.DataTypeError as dte:
- print("Error: " + str(dte))
- except isc.cc.data.DataNotFoundError as dnfe:
- print("Error: " + str(dnfe))
- except isc.cc.data.DataAlreadyPresentError as dape:
- print("Error: " + str(dape))
- except KeyError as ke:
- print("Error: missing " + str(ke))
+ self.apply_config_cmd(cmd)
+ elif cmd.module == command_sets.EXECUTE_MODULE_NAME:
+ self.apply_execute_cmd(cmd)
else:
self.apply_cmd(cmd)
@@ -576,6 +572,14 @@ class BindCmdInterpreter(Cmd):
self._print_correct_usage(err)
except isc.cc.data.DataTypeError as err:
print("Error! ", err)
+ except isc.cc.data.DataTypeError as dte:
+ print("Error: " + str(dte))
+ except isc.cc.data.DataNotFoundError as dnfe:
+ print("Error: " + str(dnfe))
+ except isc.cc.data.DataAlreadyPresentError as dape:
+ print("Error: " + str(dape))
+ except KeyError as ke:
+ print("Error: missing " + str(ke))
def _print_correct_usage(self, ept):
if isinstance(ept, CmdUnknownModuleSyntaxError):
@@ -728,6 +732,84 @@ class BindCmdInterpreter(Cmd):
self.location = new_location
+ def apply_execute_cmd(self, command):
+ '''Handles the 'execute' command, which executes a number of
+ (preset) statements. The command set to execute is either
+ read from a file (e.g. 'execute file <file>'.) or one
+ of the sets as defined in command_sets.py'''
+ if command.command == 'file':
+ try:
+ with open(command.params['filename']) as command_file:
+ commands = command_file.readlines()
+ except IOError as ioe:
+ print("Error: " + str(ioe))
+ return
+ elif command_sets.has_command_set(command.command):
+ commands = command_sets.get_commands(command.command)
+ else:
+ # Should not be reachable; parser should've caught this
+ raise Exception("Unknown execute command type " + command.command)
+
+ # We have our set of commands now, depending on whether 'show' was
+ # specified, show or execute them
+ if 'show' in command.params and command.params['show'] == 'show':
+ self.__show_execute_commands(commands)
+ else:
+ self.__apply_execute_commands(commands)
+
+ def __show_execute_commands(self, commands):
+ '''Prints the command list without executing them'''
+ for line in commands:
+ print(line.strip())
+
+ def __apply_execute_commands(self, commands):
+ '''Applies the configuration commands from the given iterator.
+ This is the method that catches, comments, echo statements, and
+ other directives. All commands not filtered by this method are
+ interpreted as if they are directly entered in an active session.
+ Lines starting with any of the following characters are not
+ passed directly:
+ # - These are comments
+ ! - These are directives
+ !echo: print the rest of the line
+ !verbose on/off: print the commands themselves too
+ Unknown directives are ignored (with a warning)
+ The execution is stopped if there are any errors.
+ '''
+ verbose = False
+ try:
+ for line in commands:
+ line = line.strip()
+ if verbose:
+ print(line)
+ if line.startswith('#') or len(line) == 0:
+ continue
+ elif line.startswith('!'):
+ if re.match('^!echo ', line, re.I) and len(line) > 6:
+ print(line[6:])
+ elif re.match('^!verbose\s+on\s*$', line, re.I):
+ verbose = True
+ elif re.match('^!verbose\s+off$', line, re.I):
+ verbose = False
+ else:
+ print("Warning: ignoring unknown directive: " + line)
+ else:
+ cmd = BindCmdParse(line)
+ self._validate_cmd(cmd)
+ self._handle_cmd(cmd)
+ except (isc.config.ModuleCCSessionError,
+ IOError, http.client.HTTPException,
+ BindCtlException, isc.cc.data.DataTypeError,
+ isc.cc.data.DataNotFoundError,
+ isc.cc.data.DataAlreadyPresentError,
+ KeyError) as err:
+ print('Error: ', err)
+ print()
+ print('Depending on the contents of the script, and which')
+ print('commands it has called, there can be committed and')
+ print('local changes. It is advised to check your settings,')
+ print('and revert local changes with "config revert".')
+
def apply_cmd(self, cmd):
'''Handles a general module command'''
url = '/' + cmd.module + '/' + cmd.command
diff --git a/src/bin/bindctl/bindctl_main.py.in b/src/bin/bindctl/bindctl_main.py.in
index 58c03eb..1685355 100755
--- a/src/bin/bindctl/bindctl_main.py.in
+++ b/src/bin/bindctl/bindctl_main.py.in
@@ -22,6 +22,7 @@ import sys; sys.path.append ('@@PYTHONPATH@@')
from bindctl.moduleinfo import *
from bindctl.bindcmd import *
+from bindctl import command_sets
import pprint
from optparse import OptionParser, OptionValueError
import isc.util.process
@@ -146,5 +147,6 @@ if __name__ == '__main__':
tool = BindCmdInterpreter(server_addr, pem_file=options.cert_chain,
csv_file_dir=options.csv_file_dir)
prepare_config_commands(tool)
+ command_sets.prepare_execute_commands(tool)
result = tool.run()
sys.exit(result)
diff --git a/src/bin/bindctl/command_sets.py b/src/bin/bindctl/command_sets.py
new file mode 100644
index 0000000..9e2c2ef
--- /dev/null
+++ b/src/bin/bindctl/command_sets.py
@@ -0,0 +1,95 @@
+# 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.
+
+# This file provides a built-in set of 'execute' commands, for common
+# functions, such as adding an initial auth server.
+# By calling the function prepare_execute_commands, the
+# commands in the command_sets map are added to the virtual
+# component called 'execute'. This is done in bindctl_main.
+
+from bindctl.moduleinfo import *
+# The name of the 'virtual' command set execution module in bindctl
+EXECUTE_MODULE_NAME = 'execute'
+
+# This is a map of command names to lists
+# Each element in the set should itself be a dict containing:
+# 'description': A string with a description of the command set
+# 'commands': A list of bindctl commands
+command_sets = {
+ 'init_authoritative_server': {
+ 'description':
+ 'Configure and run a basic Authoritative server, with default '+
+ 'SQLite3 backend, and xfrin and xfrout functionality',
+ 'commands':
+ [
+ '!echo adding Authoritative server component',
+ 'config add /Boss/components b10-auth',
+ 'config set /Boss/components/b10-auth/kind needed',
+ 'config set /Boss/components/b10-auth/special auth',
+ '!echo adding Xfrin component',
+ 'config add /Boss/components b10-xfrin',
+ 'config set /Boss/components/b10-xfrin/address Xfrin',
+ 'config set /Boss/components/b10-xfrin/kind dispensable',
+ '!echo adding Xfrout component',
+ 'config add /Boss/components b10-xfrout',
+ 'config set /Boss/components/b10-xfrout/address Xfrout',
+ 'config set /Boss/components/b10-xfrout/kind dispensable',
+ '!echo adding Zone Manager component',
+ 'config add /Boss/components b10-zonemgr',
+ 'config set /Boss/components/b10-zonemgr/address Zonemgr',
+ 'config set /Boss/components/b10-zonemgr/kind dispensable',
+ '!echo Components added. Please enter "config commit" to',
+ '!echo finalize initial setup and run the components.'
+ ]
+ }
+}
+
+def has_command_set(name):
+ return name in command_sets
+
+def get_commands(name):
+ return command_sets[name]['commands']
+
+def get_description(name):
+ return command_sets[name]['description']
+
+# For each
+def prepare_execute_commands(tool):
+ """This function is called by bindctl_main, and sets up the commands
+ defined here for use in bindctl."""
+ # common parameter
+ param_show = ParamInfo(name="show", type="string", optional=True,
+ desc="Show the list of commands without executing them")
+
+ # The command module
+ module = ModuleInfo(name=EXECUTE_MODULE_NAME,
+ desc="Execute a given set of commands")
+
+ # Command to execute a file
+ cmd = CommandInfo(name="file", desc="Read commands from file")
+ param = ParamInfo(name="filename", type="string", optional=False,
+ desc="File to read the set of commands from.")
+ cmd.add_param(param)
+ cmd.add_param(param_show)
+ module.add_command(cmd)
+
+ # and loop through all command sets defined above
+ for name in command_sets:
+ cmd = CommandInfo(name=name, desc=get_description(name))
+ cmd.add_param(param_show)
+ module.add_command(cmd)
+
+ tool.add_module_info(module)
+
diff --git a/src/bin/cfgmgr/b10-cfgmgr.8 b/src/bin/cfgmgr/b10-cfgmgr.8
index 719f4c6..0524d9b 100644
--- a/src/bin/cfgmgr/b10-cfgmgr.8
+++ b/src/bin/cfgmgr/b10-cfgmgr.8
@@ -2,12 +2,12 @@
.\" Title: b10-cfgmgr
.\" Author: [FIXME: author] [see http://docbook.sf.net/el/author]
.\" Generator: DocBook XSL Stylesheets v1.75.2 <http://docbook.sf.net/>
-.\" Date: March 10, 2010
+.\" Date: April 12, 2010
.\" Manual: BIND10
.\" Source: BIND10
.\" Language: English
.\"
-.TH "B10\-CFGMGR" "8" "March 10, 2010" "BIND10" "BIND10"
+.TH "B10\-CFGMGR" "8" "April 12, 2010" "BIND10" "BIND10"
.\" -----------------------------------------------------------------
.\" * set default formatting
.\" -----------------------------------------------------------------
@@ -22,7 +22,7 @@
b10-cfgmgr \- Configuration manager
.SH "SYNOPSIS"
.HP \w'\fBb10\-cfgmgr\fR\ 'u
-\fBb10\-cfgmgr\fR [\fB\-c\fR\fB\fIconfig\-filename\fR\fR] [\fB\-p\fR\fB\fIdata_path\fR\fR]
+\fBb10\-cfgmgr\fR [\fB\-c\ \fR\fB\fIconfig\-filename\fR\fR] [\fB\-p\ \fR\fB\fIdata_path\fR\fR] [\fB\-\-clear\-config\fR] [\fB\-\-config\-filename\ \fR\fB\fIconfig\-filename\fR\fR] [\fB\-\-data\-path\ \fR\fB\fIdata_path\fR\fR]
.SH "DESCRIPTION"
.PP
The
@@ -50,14 +50,21 @@ When it exits, it saves its current configuration to
.PP
The arguments are as follows:
.PP
-\fB\-c\fR\fIconfig\-filename\fR, \fB\-\-config\-filename\fR \fIconfig\-filename\fR
+\fB\-\-clear\-config\fR
+.RS 4
+This will create a backup of the existing configuration file, remove it, and
+b10\-cfgmgr(8)
+will use the default configurations\&. The name of the backup file can be found in the logs (\fICFGMGR_RENAMED_CONFIG_FILE\fR)\&. (It will append a number to the backup filename if a previous backup file exists\&.)
+.RE
+.PP
+\fB\-c\fR \fIconfig\-filename\fR, \fB\-\-config\-filename\fR \fIconfig\-filename\fR
.RS 4
The configuration database filename to use\&. Can be either absolute or relative to data path\&.
.sp
Defaults to b10\-config\&.db
.RE
.PP
-\fB\-p\fR\fIdata\-path\fR, \fB\-\-data\-path\fR \fIdata\-path\fR
+\fB\-p\fR \fIdata\-path\fR, \fB\-\-data\-path\fR \fIdata\-path\fR
.RS 4
The path where BIND 10 looks for files\&. The configuration file is looked for here, if it is relative\&. If it is absolute, the path is ignored\&.
.RE
diff --git a/src/bin/cfgmgr/b10-cfgmgr.py.in b/src/bin/cfgmgr/b10-cfgmgr.py.in
index 760b6d8..f1d0308 100755
--- a/src/bin/cfgmgr/b10-cfgmgr.py.in
+++ b/src/bin/cfgmgr/b10-cfgmgr.py.in
@@ -44,11 +44,11 @@ def parse_options(args=sys.argv[1:], Parser=OptionParser):
parser = Parser()
parser.add_option("-p", "--data-path", dest="data_path",
help="Directory to search for configuration files " +
- "(default=" + DATA_PATH + ")", default=DATA_PATH)
+ "(default=" + DATA_PATH + ")", default=None)
parser.add_option("-c", "--config-filename", dest="config_file",
help="Configuration database filename " +
"(default=" + DEFAULT_CONFIG_FILE + ")",
- default=DEFAULT_CONFIG_FILE)
+ default=None)
parser.add_option("--clear-config", action="store_true",
dest="clear_config", default=False,
help="Back up the configuration file and start with " +
@@ -85,12 +85,37 @@ def load_plugins(path, cm):
# Restore the search path
sys.path = sys.path[1:]
+
+def determine_path_and_file(data_path_option, config_file_option):
+ """Given the data path and config file as specified on the command line
+ (or not specified, as may be the case), determine the full path and
+ file to use when starting the config manager;
+ - if neither are given, use defaults
+ - if both are given, use both
+ - if only data path is given, use default file in that path
+ - if only file is given, use cwd() + file (if file happens to
+ be an absolute file name, path will be ignored)
+ Arguments are either a string, or None.
+ Returns a tuple containing (result_path, result_file).
+ """
+ data_path = data_path_option
+ config_file = config_file_option
+ if config_file is None:
+ config_file = DEFAULT_CONFIG_FILE
+ if data_path is None:
+ data_path = DATA_PATH
+ else:
+ if data_path is None:
+ data_path = os.getcwd()
+ return (data_path, config_file)
+
def main():
options = parse_options()
global cm
try:
- cm = ConfigManager(options.data_path, options.config_file,
- None, options.clear_config)
+ (data_path, config_file) = determine_path_and_file(options.data_path,
+ options.config_file)
+ cm = ConfigManager(data_path, config_file, None, options.clear_config)
signal.signal(signal.SIGINT, signal_handler)
signal.signal(signal.SIGTERM, signal_handler)
cm.read_config()
diff --git a/src/bin/cfgmgr/b10-cfgmgr.xml b/src/bin/cfgmgr/b10-cfgmgr.xml
index 785a058..f8d590b 100644
--- a/src/bin/cfgmgr/b10-cfgmgr.xml
+++ b/src/bin/cfgmgr/b10-cfgmgr.xml
@@ -20,7 +20,7 @@
<refentry>
<refentryinfo>
- <date>March 10, 2010</date>
+ <date>April 12, 2010</date>
</refentryinfo>
<refmeta>
@@ -44,8 +44,11 @@
<refsynopsisdiv>
<cmdsynopsis>
<command>b10-cfgmgr</command>
- <arg><option>-c<replaceable>config-filename</replaceable></option></arg>
- <arg><option>-p<replaceable>data_path</replaceable></option></arg>
+ <arg><option>-c <replaceable>config-filename</replaceable></option></arg>
+ <arg><option>-p <replaceable>data_path</replaceable></option></arg>
+ <arg><option>--clear-config</option></arg>
+ <arg><option>--config-filename <replaceable>config-filename</replaceable></option></arg>
+ <arg><option>--data-path <replaceable>data_path</replaceable></option></arg>
</cmdsynopsis>
</refsynopsisdiv>
@@ -95,7 +98,25 @@
<variablelist>
<varlistentry>
<term>
- <option>-c</option><replaceable>config-filename</replaceable>,
+ <option>--clear-config</option>
+ </term>
+ <listitem>
+ <para>
+ This will create a backup of the existing configuration
+ file, remove it, and
+ <refentrytitle>b10-cfgmgr</refentrytitle><manvolnum>8</manvolnum>
+ will use the default configurations.
+ The name of the backup file can be found in the logs
+ (<varname>CFGMGR_RENAMED_CONFIG_FILE</varname>).
+ (It will append a number to the backup filename if a
+ previous backup file exists.)
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>
+ <option>-c</option> <replaceable>config-filename</replaceable>,
<option>--config-filename</option> <replaceable>config-filename</replaceable>
</term>
<listitem>
@@ -107,7 +128,7 @@
<varlistentry>
<term>
- <option>-p</option><replaceable>data-path</replaceable>,
+ <option>-p</option> <replaceable>data-path</replaceable>,
<option>--data-path</option> <replaceable>data-path</replaceable>
</term>
<listitem>
diff --git a/src/bin/cfgmgr/tests/b10-cfgmgr_test.py.in b/src/bin/cfgmgr/tests/b10-cfgmgr_test.py.in
index ca91c9c..66d8f2a 100644
--- a/src/bin/cfgmgr/tests/b10-cfgmgr_test.py.in
+++ b/src/bin/cfgmgr/tests/b10-cfgmgr_test.py.in
@@ -141,8 +141,8 @@ class TestParseArgs(unittest.TestCase):
# Pass it empty array, not our arguments
b = __import__("b10-cfgmgr")
parsed = b.parse_options([], TestOptParser)
- self.assertEqual(b.DATA_PATH, parsed.data_path)
- self.assertEqual(b.DEFAULT_CONFIG_FILE, parsed.config_file)
+ self.assertEqual(None, parsed.data_path)
+ self.assertEqual(None, parsed.config_file)
def test_wrong_args(self):
"""
@@ -168,10 +168,10 @@ class TestParseArgs(unittest.TestCase):
b = __import__("b10-cfgmgr")
parsed = b.parse_options(['--data-path=/path'], TestOptParser)
self.assertEqual('/path', parsed.data_path)
- self.assertEqual(b.DEFAULT_CONFIG_FILE, parsed.config_file)
+ self.assertEqual(None, parsed.config_file)
parsed = b.parse_options(['-p', '/path'], TestOptParser)
self.assertEqual('/path', parsed.data_path)
- self.assertEqual(b.DEFAULT_CONFIG_FILE, parsed.config_file)
+ self.assertEqual(None, parsed.config_file)
self.assertRaises(OptsError, b.parse_options, ['-p'], TestOptParser)
self.assertRaises(OptsError, b.parse_options, ['--data-path'],
TestOptParser)
@@ -183,15 +183,26 @@ class TestParseArgs(unittest.TestCase):
b = __import__("b10-cfgmgr")
parsed = b.parse_options(['--config-filename=filename'],
TestOptParser)
- self.assertEqual(b.DATA_PATH, parsed.data_path)
+ self.assertEqual(None, parsed.data_path)
self.assertEqual("filename", parsed.config_file)
parsed = b.parse_options(['-c', 'filename'], TestOptParser)
- self.assertEqual(b.DATA_PATH, parsed.data_path)
+ self.assertEqual(None, parsed.data_path)
self.assertEqual("filename", parsed.config_file)
self.assertRaises(OptsError, b.parse_options, ['-c'], TestOptParser)
self.assertRaises(OptsError, b.parse_options, ['--config-filename'],
TestOptParser)
+ def test_determine_path_and_file(self):
+ b = __import__("b10-cfgmgr")
+ self.assertEqual((b.DATA_PATH, b.DEFAULT_CONFIG_FILE),
+ b.determine_path_and_file(None, None))
+ self.assertEqual(("/foo", b.DEFAULT_CONFIG_FILE),
+ b.determine_path_and_file("/foo", None))
+ self.assertEqual((os.getcwd(), "file.config"),
+ b.determine_path_and_file(None, "file.config"))
+ self.assertEqual(("/foo", "bar"),
+ b.determine_path_and_file("/foo", "bar"))
+
def test_clear_config(self):
b = __import__("b10-cfgmgr")
parsed = b.parse_options([], TestOptParser)
diff --git a/src/bin/ddns/ddns.py.in b/src/bin/ddns/ddns.py.in
index 22e4e9c..de69100 100755
--- a/src/bin/ddns/ddns.py.in
+++ b/src/bin/ddns/ddns.py.in
@@ -39,18 +39,26 @@ isc.log.init("b10-ddns")
logger = isc.log.Logger("ddns")
TRACE_BASIC = logger.DBGLVL_TRACE_BASIC
-DATA_PATH = bind10_config.DATA_PATH
-SOCKET_FILE = DATA_PATH + '/ddns_socket'
+# If B10_FROM_SOURCE is set in the environment, we use data files
+# from a directory relative to that, otherwise we use the ones
+# installed on the system
if "B10_FROM_SOURCE" in os.environ:
- DATA_PATH = os.environ['B10_FROM_SOURCE'] + "/src/bin/ddns"
+ SPECFILE_LOCATION = os.environ["B10_FROM_SOURCE"] + os.sep + \
+ "src" + os.sep + "bin" + os.sep + "ddns" + os.sep + "ddns.spec"
+else:
+ PREFIX = "@prefix@"
+ DATAROOTDIR = "@datarootdir@"
+ SPECFILE_LOCATION = "@datadir@" + os.sep + "@PACKAGE@" + os.sep + "ddns.spec"
+ SPECFILE_LOCATION = SPECFILE_LOCATION.replace("${datarootdir}", DATAROOTDIR)\
+ .replace("${prefix}", PREFIX)
+
+SOCKET_FILE = bind10_config.DATA_PATH + '/ddns_socket'
if "B10_FROM_BUILD" in os.environ:
if "B10_FROM_SOURCE_LOCALSTATEDIR" in os.environ:
SOCKET_FILE = os.environ["B10_FROM_SOURCE_LOCALSTATEDIR"] + \
"/ddns_socket"
else:
SOCKET_FILE = os.environ["B10_FROM_BUILD"] + "/ddns_socket"
-SPECFILE_LOCATION = DATA_PATH + "/ddns.spec"
-
isc.util.process.rename()
diff --git a/src/bin/dhcp4/dhcp4_srv.cc b/src/bin/dhcp4/dhcp4_srv.cc
index 89b48c6..d065eda 100644
--- a/src/bin/dhcp4/dhcp4_srv.cc
+++ b/src/bin/dhcp4/dhcp4_srv.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
@@ -59,10 +59,9 @@ Dhcpv4Srv::~Dhcpv4Srv() {
bool
Dhcpv4Srv::run() {
while (!shutdown_) {
- boost::shared_ptr<Pkt4> query; // client's message
- boost::shared_ptr<Pkt4> rsp; // server's response
-
- query = IfaceMgr::instance().receive4();
+ // client's message and server's response
+ Pkt4Ptr query = IfaceMgr::instance().receive4();
+ Pkt4Ptr rsp;
if (query) {
try {
@@ -141,14 +140,13 @@ Dhcpv4Srv::setServerID() {
#if 0
// uncomment this once ticket 1350 is merged.
IOAddress srvId("127.0.0.1");
- serverid_ = boost::shared_ptr<Option>(
+ serverid_ = OptionPtr(
new Option4AddrLst(Option::V4, DHO_DHCP_SERVER_IDENTIFIER, srvId));
#endif
}
-void Dhcpv4Srv::copyDefaultFields(const boost::shared_ptr<Pkt4>& question,
- boost::shared_ptr<Pkt4>& answer) {
+void Dhcpv4Srv::copyDefaultFields(const Pkt4Ptr& question, Pkt4Ptr& answer) {
answer->setIface(question->getIface());
answer->setIndex(question->getIndex());
answer->setCiaddr(question->getCiaddr());
@@ -174,17 +172,17 @@ void Dhcpv4Srv::copyDefaultFields(const boost::shared_ptr<Pkt4>& question,
}
-void Dhcpv4Srv::appendDefaultOptions(boost::shared_ptr<Pkt4>& msg, uint8_t msg_type) {
- boost::shared_ptr<Option> opt;
+void Dhcpv4Srv::appendDefaultOptions(Pkt4Ptr& msg, uint8_t msg_type) {
+ OptionPtr opt;
// add Message Type Option (type 53)
std::vector<uint8_t> tmp;
tmp.push_back(static_cast<uint8_t>(msg_type));
- opt = boost::shared_ptr<Option>(new Option(Option::V4, DHO_DHCP_MESSAGE_TYPE, tmp));
+ opt = OptionPtr(new Option(Option::V4, DHO_DHCP_MESSAGE_TYPE, tmp));
msg->addOption(opt);
// DHCP Server Identifier (type 54)
- opt = boost::shared_ptr<Option>
+ opt = OptionPtr
(new Option4AddrLst(DHO_DHCP_SERVER_IDENTIFIER, IOAddress(HARDCODED_SERVER_ID)));
msg->addOption(opt);
@@ -192,47 +190,43 @@ void Dhcpv4Srv::appendDefaultOptions(boost::shared_ptr<Pkt4>& msg, uint8_t msg_t
}
-void Dhcpv4Srv::appendRequestedOptions(boost::shared_ptr<Pkt4>& msg) {
- boost::shared_ptr<Option> opt;
+void Dhcpv4Srv::appendRequestedOptions(Pkt4Ptr& msg) {
+ OptionPtr opt;
// Domain name (type 15)
vector<uint8_t> domain(HARDCODED_DOMAIN_NAME.begin(), HARDCODED_DOMAIN_NAME.end());
- opt = boost::shared_ptr<Option>(new Option(Option::V4, DHO_DOMAIN_NAME, domain));
+ opt = OptionPtr(new Option(Option::V4, DHO_DOMAIN_NAME, domain));
msg->addOption(opt);
// TODO: Add Option_String class
// DNS servers (type 6)
- opt = boost::shared_ptr<Option>
- (new Option4AddrLst(DHO_DOMAIN_NAME_SERVERS, IOAddress(HARDCODED_DNS_SERVER)));
+ opt = OptionPtr(new Option4AddrLst(DHO_DOMAIN_NAME_SERVERS, IOAddress(HARDCODED_DNS_SERVER)));
msg->addOption(opt);
}
-void Dhcpv4Srv::tryAssignLease(boost::shared_ptr<Pkt4>& msg) {
- boost::shared_ptr<Option> opt;
+void Dhcpv4Srv::tryAssignLease(Pkt4Ptr& msg) {
+ OptionPtr opt;
// TODO: Implement actual lease assignment here
msg->setYiaddr(IOAddress(HARDCODED_LEASE));
// IP Address Lease time (type 51)
- opt = boost::shared_ptr<Option>(new Option(Option::V4, DHO_DHCP_LEASE_TIME));
+ opt = OptionPtr(new Option(Option::V4, DHO_DHCP_LEASE_TIME));
opt->setUint32(HARDCODED_LEASE_TIME);
msg->addOption(opt);
// TODO: create Option_IntArray that holds list of integers, similar to Option4_AddrLst
// Subnet mask (type 1)
- opt = boost::shared_ptr<Option>
- (new Option4AddrLst(DHO_SUBNET_MASK, IOAddress(HARDCODED_NETMASK)));
+ opt = OptionPtr(new Option4AddrLst(DHO_SUBNET_MASK, IOAddress(HARDCODED_NETMASK)));
msg->addOption(opt);
// Router (type 3)
- opt = boost::shared_ptr<Option>
- (new Option4AddrLst(DHO_ROUTERS, IOAddress(HARDCODED_GATEWAY)));
+ opt = OptionPtr(new Option4AddrLst(DHO_ROUTERS, IOAddress(HARDCODED_GATEWAY)));
msg->addOption(opt);
}
-boost::shared_ptr<Pkt4>
-Dhcpv4Srv::processDiscover(boost::shared_ptr<Pkt4>& discover) {
- boost::shared_ptr<Pkt4> offer = boost::shared_ptr<Pkt4>
+Pkt4Ptr Dhcpv4Srv::processDiscover(Pkt4Ptr& discover) {
+ Pkt4Ptr offer = Pkt4Ptr
(new Pkt4(DHCPOFFER, discover->getTransid()));
copyDefaultFields(discover, offer);
@@ -244,9 +238,8 @@ Dhcpv4Srv::processDiscover(boost::shared_ptr<Pkt4>& discover) {
return (offer);
}
-boost::shared_ptr<Pkt4>
-Dhcpv4Srv::processRequest(boost::shared_ptr<Pkt4>& request) {
- boost::shared_ptr<Pkt4> ack = boost::shared_ptr<Pkt4>
+Pkt4Ptr Dhcpv4Srv::processRequest(Pkt4Ptr& request) {
+ Pkt4Ptr ack = Pkt4Ptr
(new Pkt4(DHCPACK, request->getTransid()));
copyDefaultFields(request, ack);
@@ -258,17 +251,17 @@ Dhcpv4Srv::processRequest(boost::shared_ptr<Pkt4>& request) {
return (ack);
}
-void Dhcpv4Srv::processRelease(boost::shared_ptr<Pkt4>& release) {
+void Dhcpv4Srv::processRelease(Pkt4Ptr& release) {
/// TODO: Implement this.
cout << "Received RELEASE on " << release->getIface() << " interface." << endl;
}
-void Dhcpv4Srv::processDecline(boost::shared_ptr<Pkt4>& decline) {
+void Dhcpv4Srv::processDecline(Pkt4Ptr& decline) {
/// TODO: Implement this.
cout << "Received DECLINE on " << decline->getIface() << " interface." << endl;
}
-boost::shared_ptr<Pkt4> Dhcpv4Srv::processInform(boost::shared_ptr<Pkt4>& inform) {
+Pkt4Ptr Dhcpv4Srv::processInform(Pkt4Ptr& inform) {
/// TODO: Currently implemented echo mode. Implement this for real
return (inform);
}
diff --git a/src/bin/dhcp4/dhcp4_srv.h b/src/bin/dhcp4/dhcp4_srv.h
index cd46977..745a4ec 100644
--- a/src/bin/dhcp4/dhcp4_srv.h
+++ b/src/bin/dhcp4/dhcp4_srv.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
@@ -15,7 +15,6 @@
#ifndef DHCPV4_SRV_H
#define DHCPV4_SRV_H
-#include <boost/shared_ptr.hpp>
#include <boost/noncopyable.hpp>
#include <dhcp/dhcp4.h>
#include <dhcp/pkt4.h>
@@ -68,11 +67,10 @@ protected:
/// should be served. In particular, a lease is selected and sent
/// as an offer to a client if it should be served.
///
- /// @param solicit DISCOVER message received from client
+ /// @param discover DISCOVER message received from client
///
/// @return OFFER message or NULL
- boost::shared_ptr<Pkt4>
- processDiscover(boost::shared_ptr<Pkt4>& discover);
+ Pkt4Ptr processDiscover(Pkt4Ptr& discover);
/// @brief Processes incoming REQUEST and returns REPLY response.
///
@@ -86,7 +84,7 @@ protected:
/// @param request a message received from client
///
/// @return ACK or NACK message
- boost::shared_ptr<Pkt4> processRequest(boost::shared_ptr<Pkt4>& request);
+ Pkt4Ptr processRequest(Pkt4Ptr& request);
/// @brief Stub function that will handle incoming RELEASE messages.
///
@@ -94,17 +92,17 @@ protected:
/// this function does not return anything.
///
/// @param release message received from client
- void processRelease(boost::shared_ptr<Pkt4>& release);
+ void processRelease(Pkt4Ptr& release);
/// @brief Stub function that will handle incoming DHCPDECLINE messages.
///
/// @param decline message received from client
- void processDecline(boost::shared_ptr<Pkt4>& decline);
+ void processDecline(Pkt4Ptr& decline);
/// @brief Stub function that will handle incoming INFORM messages.
///
- /// @param infRequest message received from client
- boost::shared_ptr<Pkt4> processInform(boost::shared_ptr<Pkt4>& inform);
+ /// @param inform message received from client
+ Pkt4Ptr processInform(Pkt4Ptr& inform);
/// @brief Copies default parameters from client's to server's message
///
@@ -113,9 +111,7 @@ protected:
///
/// @param question any message sent by client
/// @param answer any message server is going to send as response
- void copyDefaultFields(const boost::shared_ptr<Pkt4>& question,
- boost::shared_ptr<Pkt4>& answer);
-
+ void copyDefaultFields(const Pkt4Ptr& question, Pkt4Ptr& answer);
/// @brief Appends options requested by client.
///
@@ -123,8 +119,7 @@ protected:
/// (sent in PRL) or are enforced by server.
///
/// @param msg outgoing message (options will be added here)
- void appendRequestedOptions(boost::shared_ptr<Pkt4>& msg);
-
+ void appendRequestedOptions(Pkt4Ptr& msg);
/// @brief Assigns a lease and appends corresponding options
///
@@ -136,20 +131,18 @@ protected:
/// used fixed, hardcoded lease.
///
/// @param msg OFFER or ACK message (lease options will be added here)
- void tryAssignLease(boost::shared_ptr<Pkt4>& msg);
-
+ void tryAssignLease(Pkt4Ptr& msg);
/// @brief Appends default options to a message
///
/// @param msg message object (options will be added to it)
/// @param msg_type specifies message type
- void appendDefaultOptions(boost::shared_ptr<Pkt4>& msg, uint8_t msg_type);
+ void appendDefaultOptions(Pkt4Ptr& msg, uint8_t msg_type);
/// @brief Returns server-intentifier option
///
/// @return server-id option
- boost::shared_ptr<isc::dhcp::Option>
- getServerID() { return serverid_; }
+ OptionPtr getServerID() { return serverid_; }
/// @brief Sets server-identifier.
///
@@ -163,7 +156,7 @@ protected:
void setServerID();
/// server DUID (to be sent in server-identifier option)
- boost::shared_ptr<isc::dhcp::Option> serverid_;
+ OptionPtr serverid_;
/// indicates if shutdown is in progress. Setting it to true will
/// initiate server shutdown procedure.
diff --git a/src/bin/dhcp6/dhcp6_srv.cc b/src/bin/dhcp6/dhcp6_srv.cc
index 4e69f05..4497e22 100644
--- a/src/bin/dhcp6/dhcp6_srv.cc
+++ b/src/bin/dhcp6/dhcp6_srv.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
@@ -12,6 +12,8 @@
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
+#include <stdlib.h>
+#include <time.h>
#include <dhcp/dhcp6.h>
#include <dhcp/pkt6.h>
#include <dhcp/iface_mgr.h>
@@ -21,11 +23,14 @@
#include <dhcp/option6_addrlst.h>
#include <asiolink/io_address.h>
#include <exceptions/exceptions.h>
+#include <util/io_utilities.h>
+#include <util/range_utilities.h>
using namespace std;
using namespace isc;
using namespace isc::dhcp;
using namespace isc::asiolink;
+using namespace isc::util;
const std::string HARDCODED_LEASE = "2001:db8:1::1234:abcd";
const uint32_t HARDCODED_T1 = 1500; // in seconds
@@ -67,13 +72,12 @@ Dhcpv6Srv::~Dhcpv6Srv() {
IfaceMgr::instance().closeSockets();
}
-bool
-Dhcpv6Srv::run() {
+bool Dhcpv6Srv::run() {
while (!shutdown) {
- boost::shared_ptr<Pkt6> query; // client's message
- boost::shared_ptr<Pkt6> rsp; // server's response
- query = IfaceMgr::instance().receive6();
+ // client's message and server's response
+ Pkt6Ptr query = IfaceMgr::instance().receive6();
+ Pkt6Ptr rsp;
if (query) {
if (!query->unpack()) {
@@ -110,16 +114,16 @@ Dhcpv6Srv::run() {
<< query->getType() << endl;
}
- cout << "Received " << query->data_len_ << " bytes packet type="
+ cout << "Received " << query->getBuffer().getLength() << " bytes packet type="
<< query->getType() << endl;
cout << query->toText();
if (rsp) {
- rsp->remote_addr_ = query->remote_addr_;
- rsp->local_addr_ = query->local_addr_;
- rsp->remote_port_ = DHCP6_CLIENT_PORT;
- rsp->local_port_ = DHCP6_SERVER_PORT;
- rsp->ifindex_ = query->ifindex_;
- rsp->iface_ = query->iface_;
+ rsp->setRemoteAddr(query->getRemoteAddr());
+ rsp->setLocalAddr(query->getLocalAddr());
+ rsp->setRemotePort(DHCP6_CLIENT_PORT);
+ rsp->setLocalPort(DHCP6_SERVER_PORT);
+ rsp->setIndex(query->getIndex());
+ rsp->setIface(query->getIface());
cout << "Replying with:" << rsp->getType() << endl;
cout << rsp->toText();
cout << "----" << endl;
@@ -138,28 +142,93 @@ Dhcpv6Srv::run() {
return (true);
}
-void
-Dhcpv6Srv::setServerID() {
- /// TODO implement this for real once interface detection is done.
- /// Use hardcoded server-id for now
-
- boost::shared_array<uint8_t> srvid(new uint8_t[14]);
- srvid[0] = 0;
- srvid[1] = 1; // DUID type 1 = DUID-LLT (see section 9.2 of RFC3315)
- srvid[2] = 0;
- srvid[3] = 6; // HW type = ethernet (I think. I'm typing this from my head
- // in hotel, without Internet connection)
- for (int i=4; i<14; i++) {
- srvid[i]=i-4;
+void Dhcpv6Srv::setServerID() {
+
+ /// @todo: DUID should be generated once and then stored, rather
+ /// than generated each time
+
+ /// @todo: This code implements support for DUID-LLT (the recommended one).
+ /// We should eventually add support for other DUID types: DUID-LL, DUID-EN
+ /// and DUID-UUID
+
+ const IfaceMgr::IfaceCollection& ifaces = IfaceMgr::instance().getIfaces();
+
+ // let's find suitable interface
+ for (IfaceMgr::IfaceCollection::const_iterator iface = ifaces.begin();
+ iface != ifaces.end(); ++iface) {
+ // All the following checks could be merged into one multi-condition
+ // statement, but let's keep them separated as perhaps one day
+ // we will grow knobs to selectively turn them on or off. Also,
+ // this code is used only *once* during first start on a new machine
+ // and then server-id is stored. (or at least it will be once
+ // DUID storage is implemente
+
+ // I wish there was a this_is_a_real_physical_interface flag...
+
+ // MAC address should be at least 6 bytes. Although there is no such
+ // requirement in any RFC, all decent physical interfaces (Ethernet,
+ // WiFi, Infiniband, etc.) have 6 bytes long MAC address. We want to
+ // base our DUID on real hardware address, rather than virtual
+ // interface that pretends that underlying IP address is its MAC.
+ if (iface->getMacLen() < MIN_MAC_LEN) {
+ continue;
+ }
+
+ // let's don't use loopback
+ if (iface->flag_loopback_) {
+ continue;
+ }
+
+ // let's skip downed interfaces. It is better to use working ones.
+ if (!iface->flag_up_) {
+ continue;
+ }
+
+ // some interfaces (like lo on Linux) report 6-bytes long
+ // MAC adress 00:00:00:00:00:00. Let's not use such weird interfaces
+ // to generate DUID.
+ if (isRangeZero(iface->getMac(), iface->getMac() + iface->getMacLen())) {
+ continue;
+ }
+
+ // Ok, we have useful MAC. Let's generate DUID-LLT based on
+ // it. See RFC3315, Section 9.2 for details.
+
+ // DUID uses seconds since midnight of 01-01-2000, time() returns
+ // seconds since 01-01-1970. DUID_TIME_EPOCH substution corrects that.
+ time_t seconds = time(NULL);
+ seconds -= DUID_TIME_EPOCH;
+
+ OptionBuffer srvid(8 + iface->getMacLen());
+ writeUint16(DUID_LLT, &srvid[0]);
+ writeUint16(HWTYPE_ETHERNET, &srvid[2]);
+ writeUint32(static_cast<uint32_t>(seconds), &srvid[4]);
+ memcpy(&srvid[0]+8, iface->getMac(), iface->getMacLen());
+
+ serverid_ = OptionPtr(new Option(Option::V6, D6O_SERVERID,
+ srvid.begin(), srvid.end()));
+ return;
}
- serverid_ = boost::shared_ptr<Option>(new Option(Option::V6,
- D6O_SERVERID,
- srvid,
- 0, 14));
+
+ // if we reached here, there are no suitable interfaces found.
+ // Either interface detection is not supported on this platform or
+ // this is really weird box. Let's use DUID-EN instead.
+ // See Section 9.3 of RFC3315 for details.
+
+ OptionBuffer srvid(12);
+ writeUint16(DUID_EN, &srvid[0]);
+ writeUint32(ENTERPRISE_ID_ISC, &srvid[2]);
+
+ // Length of the identifier is company specific. I hereby declare
+ // ISC "standard" of 6 bytes long pseudo-random numbers.
+ srandom(time(NULL));
+ fillRandom(&srvid[6],&srvid[12]);
+
+ serverid_ = OptionPtr(new Option(Option::V6, D6O_SERVERID,
+ srvid.begin(), srvid.end()));
}
-void Dhcpv6Srv::copyDefaultOptions(const boost::shared_ptr<Pkt6>& question,
- boost::shared_ptr<Pkt6>& answer) {
+void Dhcpv6Srv::copyDefaultOptions(const Pkt6Ptr& question, Pkt6Ptr& answer) {
// add client-id
boost::shared_ptr<Option> clientid = question->getOption(D6O_CLIENTID);
if (clientid) {
@@ -169,8 +238,7 @@ void Dhcpv6Srv::copyDefaultOptions(const boost::shared_ptr<Pkt6>& question,
// TODO: Should throw if there is no client-id (except anonymous INF-REQUEST)
}
-void Dhcpv6Srv::appendDefaultOptions(const boost::shared_ptr<Pkt6>& /*question*/,
- boost::shared_ptr<Pkt6>& answer) {
+void Dhcpv6Srv::appendDefaultOptions(const Pkt6Ptr& /*question*/, Pkt6Ptr& answer) {
// TODO: question is currently unused, but we need it at least to know
// message type we are answering
@@ -179,8 +247,7 @@ void Dhcpv6Srv::appendDefaultOptions(const boost::shared_ptr<Pkt6>& /*question*/
}
-void Dhcpv6Srv::appendRequestedOptions(const boost::shared_ptr<Pkt6>& /*question*/,
- boost::shared_ptr<Pkt6>& answer) {
+void Dhcpv6Srv::appendRequestedOptions(const Pkt6Ptr& /*question*/, Pkt6Ptr& answer) {
// TODO: question is currently unused, but we need to extract ORO from it
// and act on its content. Now we just send DNS-SERVERS option.
@@ -190,8 +257,7 @@ void Dhcpv6Srv::appendRequestedOptions(const boost::shared_ptr<Pkt6>& /*question
answer->addOption(dnsservers);
}
-void Dhcpv6Srv::assignLeases(const boost::shared_ptr<Pkt6>& question,
- boost::shared_ptr<Pkt6>& answer) {
+void Dhcpv6Srv::assignLeases(const Pkt6Ptr& question, Pkt6Ptr& answer) {
/// TODO Rewrite this once LeaseManager is implemented.
// answer client's IA (this is mostly a dummy,
@@ -217,11 +283,8 @@ void Dhcpv6Srv::assignLeases(const boost::shared_ptr<Pkt6>& question,
}
}
-boost::shared_ptr<Pkt6>
-Dhcpv6Srv::processSolicit(const boost::shared_ptr<Pkt6>& solicit) {
-
- boost::shared_ptr<Pkt6> advertise(new Pkt6(DHCPV6_ADVERTISE,
- solicit->getTransid()));
+Pkt6Ptr Dhcpv6Srv::processSolicit(const Pkt6Ptr& solicit) {
+ Pkt6Ptr advertise(new Pkt6(DHCPV6_ADVERTISE, solicit->getTransid()));
copyDefaultOptions(solicit, advertise);
appendDefaultOptions(solicit, advertise);
@@ -232,11 +295,8 @@ Dhcpv6Srv::processSolicit(const boost::shared_ptr<Pkt6>& solicit) {
return (advertise);
}
-boost::shared_ptr<Pkt6>
-Dhcpv6Srv::processRequest(const boost::shared_ptr<Pkt6>& request) {
-
- boost::shared_ptr<Pkt6> reply(new Pkt6(DHCPV6_REPLY,
- request->getTransid()));
+Pkt6Ptr Dhcpv6Srv::processRequest(const Pkt6Ptr& request) {
+ Pkt6Ptr reply(new Pkt6(DHCPV6_REPLY, request->getTransid()));
copyDefaultOptions(request, reply);
appendDefaultOptions(request, reply);
@@ -247,50 +307,38 @@ Dhcpv6Srv::processRequest(const boost::shared_ptr<Pkt6>& request) {
return (reply);
}
-boost::shared_ptr<Pkt6>
-Dhcpv6Srv::processRenew(const boost::shared_ptr<Pkt6>& renew) {
- boost::shared_ptr<Pkt6> reply(new Pkt6(DHCPV6_REPLY,
- renew->getTransid(),
- Pkt6::UDP));
+Pkt6Ptr Dhcpv6Srv::processRenew(const Pkt6Ptr& renew) {
+ /// @todo: Implement this
+ Pkt6Ptr reply(new Pkt6(DHCPV6_REPLY, renew->getTransid()));
return reply;
}
-boost::shared_ptr<Pkt6>
-Dhcpv6Srv::processRebind(const boost::shared_ptr<Pkt6>& rebind) {
- boost::shared_ptr<Pkt6> reply(new Pkt6(DHCPV6_REPLY,
- rebind->getTransid(),
- Pkt6::UDP));
+Pkt6Ptr Dhcpv6Srv::processRebind(const Pkt6Ptr& rebind) {
+ /// @todo: Implement this
+ Pkt6Ptr reply(new Pkt6(DHCPV6_REPLY, rebind->getTransid()));
return reply;
}
-boost::shared_ptr<Pkt6>
-Dhcpv6Srv::processConfirm(const boost::shared_ptr<Pkt6>& confirm) {
- boost::shared_ptr<Pkt6> reply(new Pkt6(DHCPV6_REPLY,
- confirm->getTransid(),
- Pkt6::UDP));
+Pkt6Ptr Dhcpv6Srv::processConfirm(const Pkt6Ptr& confirm) {
+ /// @todo: Implement this
+ Pkt6Ptr reply(new Pkt6(DHCPV6_REPLY, confirm->getTransid()));
return reply;
}
-boost::shared_ptr<Pkt6>
-Dhcpv6Srv::processRelease(const boost::shared_ptr<Pkt6>& release) {
- boost::shared_ptr<Pkt6> reply(new Pkt6(DHCPV6_REPLY,
- release->getTransid(),
- Pkt6::UDP));
+Pkt6Ptr Dhcpv6Srv::processRelease(const Pkt6Ptr& release) {
+ /// @todo: Implement this
+ Pkt6Ptr reply(new Pkt6(DHCPV6_REPLY, release->getTransid()));
return reply;
}
-boost::shared_ptr<Pkt6>
-Dhcpv6Srv::processDecline(const boost::shared_ptr<Pkt6>& decline) {
- boost::shared_ptr<Pkt6> reply(new Pkt6(DHCPV6_REPLY,
- decline->getTransid(),
- Pkt6::UDP));
+Pkt6Ptr Dhcpv6Srv::processDecline(const Pkt6Ptr& decline) {
+ /// @todo: Implement this
+ Pkt6Ptr reply(new Pkt6(DHCPV6_REPLY, decline->getTransid()));
return reply;
}
-boost::shared_ptr<Pkt6>
-Dhcpv6Srv::processInfRequest(const boost::shared_ptr<Pkt6>& infRequest) {
- boost::shared_ptr<Pkt6> reply(new Pkt6(DHCPV6_REPLY,
- infRequest->getTransid(),
- Pkt6::UDP));
+Pkt6Ptr Dhcpv6Srv::processInfRequest(const Pkt6Ptr& infRequest) {
+ /// @todo: Implement this
+ Pkt6Ptr reply(new Pkt6(DHCPV6_REPLY, infRequest->getTransid()));
return reply;
}
diff --git a/src/bin/dhcp6/dhcp6_srv.h b/src/bin/dhcp6/dhcp6_srv.h
index 44a9f8a..b6ae637 100644
--- a/src/bin/dhcp6/dhcp6_srv.h
+++ b/src/bin/dhcp6/dhcp6_srv.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
@@ -15,7 +15,6 @@
#ifndef DHCPV6_SRV_H
#define DHCPV6_SRV_H
-#include <boost/shared_ptr.hpp>
#include <boost/noncopyable.hpp>
#include <dhcp/dhcp6.h>
#include <dhcp/pkt6.h>
@@ -36,6 +35,10 @@ namespace dhcp {
class Dhcpv6Srv : public boost::noncopyable {
public:
+
+ /// @brief Minimum length of a MAC address to be used in DUID generation.
+ static const size_t MIN_MAC_LEN = 6;
+
/// @brief Default constructor.
///
/// Instantiates necessary services, required to run DHCPv6 server.
@@ -52,8 +55,7 @@ public:
/// @brief Returns server-intentifier option
///
/// @return server-id option
- boost::shared_ptr<isc::dhcp::Option>
- getServerID() { return serverid_; }
+ OptionPtr getServerID() { return serverid_; }
/// @brief Main server processing loop.
///
@@ -80,8 +82,7 @@ protected:
/// @param solicit SOLICIT message received from client
///
/// @return ADVERTISE, REPLY message or NULL
- boost::shared_ptr<Pkt6>
- processSolicit(const boost::shared_ptr<Pkt6>& solicit);
+ Pkt6Ptr processSolicit(const Pkt6Ptr& solicit);
/// @brief Processes incoming REQUEST and returns REPLY response.
///
@@ -94,44 +95,37 @@ protected:
/// @param request a message received from client
///
/// @return REPLY message or NULL
- boost::shared_ptr<Pkt6>
- processRequest(const boost::shared_ptr<Pkt6>& request);
+ Pkt6Ptr processRequest(const Pkt6Ptr& request);
/// @brief Stub function that will handle incoming RENEW messages.
///
/// @param renew message received from client
- boost::shared_ptr<Pkt6>
- processRenew(const boost::shared_ptr<Pkt6>& renew);
+ Pkt6Ptr processRenew(const Pkt6Ptr& renew);
/// @brief Stub function that will handle incoming REBIND messages.
///
/// @param rebind message received from client
- boost::shared_ptr<Pkt6>
- processRebind(const boost::shared_ptr<Pkt6>& rebind);
+ Pkt6Ptr processRebind(const Pkt6Ptr& rebind);
/// @brief Stub function that will handle incoming CONFIRM messages.
///
/// @param confirm message received from client
- boost::shared_ptr<Pkt6>
- processConfirm(const boost::shared_ptr<Pkt6>& confirm);
+ Pkt6Ptr processConfirm(const Pkt6Ptr& confirm);
/// @brief Stub function that will handle incoming RELEASE messages.
///
/// @param release message received from client
- boost::shared_ptr<Pkt6>
- processRelease(const boost::shared_ptr<Pkt6>& release);
+ Pkt6Ptr processRelease(const Pkt6Ptr& release);
/// @brief Stub function that will handle incoming DECLINE messages.
///
/// @param decline message received from client
- boost::shared_ptr<Pkt6>
- processDecline(const boost::shared_ptr<Pkt6>& decline);
+ Pkt6Ptr processDecline(const Pkt6Ptr& decline);
/// @brief Stub function that will handle incoming INF-REQUEST messages.
///
/// @param infRequest message received from client
- boost::shared_ptr<Pkt6>
- processInfRequest(const boost::shared_ptr<Pkt6>& infRequest);
+ Pkt6Ptr processInfRequest(const Pkt6Ptr& infRequest);
/// @brief Copies required options from client message to server answer
///
@@ -141,8 +135,7 @@ protected:
///
/// @param question client's message (options will be copied from here)
/// @param answer server's message (options will be copied here)
- void copyDefaultOptions(const boost::shared_ptr<Pkt6>& question,
- boost::shared_ptr<Pkt6>& answer);
+ void copyDefaultOptions(const Pkt6Ptr& question, Pkt6Ptr& answer);
/// @brief Appends default options to server's answer.
///
@@ -152,8 +145,7 @@ protected:
///
/// @param question client's message
/// @param answer server's message (options will be added here)
- void appendDefaultOptions(const boost::shared_ptr<Pkt6>& question,
- boost::shared_ptr<Pkt6>& answer);
+ void appendDefaultOptions(const Pkt6Ptr& question, Pkt6Ptr& answer);
/// @brief Appends requested options to server's answer.
///
@@ -163,8 +155,7 @@ protected:
///
/// @param question client's message
/// @param answer server's message (options will be added here)
- void appendRequestedOptions(const boost::shared_ptr<Pkt6>& question,
- boost::shared_ptr<Pkt6>& answer);
+ void appendRequestedOptions(const Pkt6Ptr& question, Pkt6Ptr& answer);
/// @brief Assigns leases.
///
@@ -174,8 +165,7 @@ protected:
///
/// @param question client's message (with requested IA_NA)
/// @param answer server's message (IA_NA options will be added here)
- void assignLeases(const boost::shared_ptr<Pkt6>& question,
- boost::shared_ptr<Pkt6>& answer);
+ void assignLeases(const Pkt6Ptr& question, Pkt6Ptr& answer);
/// @brief Sets server-identifier.
///
diff --git a/src/bin/dhcp6/tests/dhcp6_srv_unittest.cc b/src/bin/dhcp6/tests/dhcp6_srv_unittest.cc
index 09d1cec..5b908ee 100644
--- a/src/bin/dhcp6/tests/dhcp6_srv_unittest.cc
+++ b/src/bin/dhcp6/tests/dhcp6_srv_unittest.cc
@@ -20,13 +20,16 @@
#include <arpa/inet.h>
#include <gtest/gtest.h>
-#include "dhcp/dhcp6.h"
-#include "dhcp6/dhcp6_srv.h"
-#include "dhcp/option6_ia.h"
+#include <dhcp/dhcp6.h>
+#include <dhcp/option6_ia.h>
+#include <dhcp6/dhcp6_srv.h>
+#include <util/buffer.h>
+#include <util/range_utilities.h>
using namespace std;
using namespace isc;
using namespace isc::dhcp;
+using namespace isc::util;
// namespace has to be named, because friends are defined in Dhcpv6Srv class
// Maybe it should be isc::test?
@@ -79,18 +82,87 @@ TEST_F(Dhcpv6SrvTest, basic) {
delete srv;
}
-TEST_F(Dhcpv6SrvTest, Solicit_basic) {
+TEST_F(Dhcpv6SrvTest, DUID) {
+ // tests that DUID is generated properly
+
+ Dhcpv6Srv* srv = NULL;
+ ASSERT_NO_THROW( {
+ srv = new Dhcpv6Srv(DHCP6_SERVER_PORT + 10000);
+ });
+
+ OptionPtr srvid = srv->getServerID();
+ ASSERT_TRUE(srvid);
+
+ EXPECT_EQ(D6O_SERVERID, srvid->getType());
+
+ OutputBuffer buf(32);
+ srvid->pack(buf);
+
+ // length of the actual DUID
+ size_t len = buf.getLength() - srvid->getHeaderLen();
+
+ InputBuffer data(buf.getData(), buf.getLength());
+
+ // ignore first four bytes (standard DHCPv6 header)
+ data.readUint32();
+
+ uint16_t duid_type = data.readUint16();
+ cout << "Duid-type=" << duid_type << endl;
+ switch(duid_type) {
+ case DUID_LLT: {
+ // DUID must contain at least 6 bytes long MAC
+ // + 8 bytes of fixed header
+ EXPECT_GE(14, len);
+
+ uint16_t hw_type = data.readUint16();
+ // there's no real way to find out "correct"
+ // hardware type
+ EXPECT_GT(hw_type, 0);
+
+ // check that timer is counted since 1.1.2000,
+ // not from 1.1.1970.
+ uint32_t seconds = data.readUint32();
+ EXPECT_LE(seconds, DUID_TIME_EPOCH);
+ // this test will start failing after 2030.
+ // Hopefully we will be at BIND12 by then.
+
+ // MAC must not be zeros
+ vector<uint8_t> mac(len-8);
+ vector<uint8_t> zeros(len-8, 0);
+ data.readVector(mac, len-8);
+ EXPECT_TRUE(mac != zeros);
+ break;
+ }
+ case DUID_EN: {
+ // there's not much we can check. Just simple
+ // check if it is not all zeros
+ vector<uint8_t> content(len-2);
+ data.readVector(content, len-2);
+ EXPECT_FALSE(isRangeZero(content.begin(), content.end()));
+ break;
+ }
+ case DUID_LL: {
+ // not supported yet
+ cout << "Test not implemented for DUID-LL yet." << endl;
+ }
+ case DUID_UUID: // not supported yet
+ default:
+ cout << "Not supported duid type=" << duid_type << endl;
+ FAIL();
+ }
+}
+
+TEST_F(Dhcpv6SrvTest, DISABLED_Solicit_basic) {
NakedDhcpv6Srv* srv = NULL;
ASSERT_NO_THROW( srv = new NakedDhcpv6Srv(); );
// a dummy content for client-id
- boost::shared_array<uint8_t> clntDuid(new uint8_t[32]);
- for (int i = 0; i < 32; i++)
+ OptionBuffer clntDuid(32);
+ for (int i = 0; i < 32; i++) {
clntDuid[i] = 100 + i;
+ }
- boost::shared_ptr<Pkt6> sol =
- boost::shared_ptr<Pkt6>(new Pkt6(DHCPV6_SOLICIT,
- 1234, Pkt6::UDP));
+ Pkt6Ptr sol = Pkt6Ptr(new Pkt6(DHCPV6_SOLICIT, 1234));
boost::shared_ptr<Option6IA> ia =
boost::shared_ptr<Option6IA>(new Option6IA(D6O_IA_NA, 234));
@@ -113,9 +185,9 @@ TEST_F(Dhcpv6SrvTest, Solicit_basic) {
// - server-id
// - IA that includes IAADDR
- boost::shared_ptr<Option> clientid =
- boost::shared_ptr<Option>(new Option(Option::V6, D6O_CLIENTID,
- clntDuid, 0, 16));
+ OptionPtr clientid = OptionPtr(new Option(Option::V6, D6O_CLIENTID,
+ clntDuid.begin(),
+ clntDuid.begin() + 16));
sol->addOption(clientid);
boost::shared_ptr<Pkt6> reply = srv->processSolicit(sol);
@@ -126,7 +198,7 @@ TEST_F(Dhcpv6SrvTest, Solicit_basic) {
EXPECT_EQ( DHCPV6_ADVERTISE, reply->getType() );
EXPECT_EQ( 1234, reply->getTransid() );
- boost::shared_ptr<Option> tmp = reply->getOption(D6O_IA_NA);
+ OptionPtr tmp = reply->getOption(D6O_IA_NA);
ASSERT_TRUE( tmp );
Option6IA* reply_ia = dynamic_cast<Option6IA*>(tmp.get());
diff --git a/src/bin/dhcp6/tests/dhcp6_test.py b/src/bin/dhcp6/tests/dhcp6_test.py
index d63e04d..e080c77 100644
--- a/src/bin/dhcp6/tests/dhcp6_test.py
+++ b/src/bin/dhcp6/tests/dhcp6_test.py
@@ -36,6 +36,9 @@ class TestDhcpv6Daemon(unittest.TestCase):
# to the main program ASAP in each test... this prevents
# hangs reading from the child process (as the pipe is only
# open in the child), and also insures nice pretty output
+ print("Please ignore any socket errors. Purpose of this test is to")
+ print("verify that DHCPv6 process could be started, not that socket")
+ print("could be bound. Binding fails when run as non-root user.")
def tearDown(self):
# clean up our stdout munging
diff --git a/src/bin/resolver/resolver_messages.mes b/src/bin/resolver/resolver_messages.mes
index 4999dbe..34a97ae 100644
--- a/src/bin/resolver/resolver_messages.mes
+++ b/src/bin/resolver/resolver_messages.mes
@@ -152,6 +152,27 @@ the parsing of the body of the message failed due to some protocol error
(although the parsing of the header succeeded). The message parameters
give a textual description of the problem and the RCODE returned.
+% RESOLVER_QUERY_ACCEPTED query accepted: '%1/%2/%3' from %4
+This debug message is produced by the resolver when an incoming query
+is accepted in terms of the query ACL. 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_QUERY_DROPPED query dropped: '%1/%2/%3' from %4
+This is an informational message that indicates an incoming query has
+been dropped by the resolver because of the query ACL. Unlike the
+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_QUERY_REJECTED query rejected: '%1/%2/%3' from %4
+This is an informational message that indicates an incoming query has
+been rejected by the resolver because of the query ACL. This results
+in a response with an RCODE of REFUSED. 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_QUERY_SETUP query setup
This is a debug message noting that the resolver is creating a
RecursiveQuery object.
@@ -197,6 +218,10 @@ there comes a time - the lookup timeout - when even the resolver gives up.
At this point it will wait for pending upstream queries to complete or
timeout and drop the query.
+% RESOLVER_SET_QUERY_ACL query ACL is configured
+This debug message is generated when a new query ACL is configured for
+the resolver.
+
% RESOLVER_SET_ROOT_ADDRESS setting root address %1(%2)
This message gives the address of one of the root servers used by the
resolver. It is output during startup and may appear multiple times,
@@ -205,6 +230,10 @@ once for each root server address.
% RESOLVER_SHUTDOWN resolver shutdown complete
This informational message is output when the resolver has shut down.
+% 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.
+
% RESOLVER_STARTED resolver started
This informational message is output by the resolver when all initialization
has been completed and it is entering its main loop.
@@ -221,32 +250,3 @@ 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.
-
-% RESOLVER_SET_QUERY_ACL query ACL is configured
-This debug message is generated when a new query ACL is configured for
-the resolver.
-
-% RESOLVER_QUERY_ACCEPTED query accepted: '%1/%2/%3' from %4
-This debug message is produced by the resolver when an incoming query
-is accepted in terms of the query ACL. 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_QUERY_REJECTED query rejected: '%1/%2/%3' from %4
-This is an informational message that indicates an incoming query has
-been rejected by the resolver because of the query ACL. This results
-in a response with an RCODE of REFUSED. 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_QUERY_DROPPED query dropped: '%1/%2/%3' from %4
-This is an informational message that indicates an incoming query has
-been dropped by the resolver because of the query ACL. Unlike the
-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/stats/stats_httpd.py.in b/src/bin/stats/stats_httpd.py.in
index c9bd0f5..7e4da96 100644
--- a/src/bin/stats/stats_httpd.py.in
+++ b/src/bin/stats/stats_httpd.py.in
@@ -189,7 +189,12 @@ class StatsHttpd:
self.load_config()
self.http_addrs = []
self.mccs.start()
- self.open_httpd()
+ try:
+ self.open_httpd()
+ except HttpServerError:
+ # if some exception, e.g. address in use, is raised, then it closes mccs and httpd
+ self.close_mccs()
+ raise
def open_mccs(self):
"""Opens a ModuleCCSession object"""
diff --git a/src/bin/stats/stats_httpd_messages.mes b/src/bin/stats/stats_httpd_messages.mes
index dbd0650..ad2e97f 100644
--- a/src/bin/stats/stats_httpd_messages.mes
+++ b/src/bin/stats/stats_httpd_messages.mes
@@ -24,14 +24,14 @@ The stats-httpd module was unable to connect to the BIND 10 command
and control bus. A likely problem is that the message bus daemon
(b10-msgq) is not running. The stats-httpd module will now shut down.
-% STATHTTPD_CLOSING_CC_SESSION stopping cc session
-Debug message indicating that the stats-httpd module is disconnecting
-from the command and control bus.
-
% STATHTTPD_CLOSING closing %1#%2
The stats-httpd daemon will stop listening for requests on the given
address and port number.
+% STATHTTPD_CLOSING_CC_SESSION stopping cc session
+Debug message indicating that the stats-httpd module is disconnecting
+from the command and control bus.
+
% STATHTTPD_HANDLE_CONFIG reading configuration: %1
The stats-httpd daemon has received new configuration data and will now
process it. The (changed) data is printed.
@@ -49,18 +49,18 @@ An unknown command has been sent to the stats-httpd module. The
stats-httpd module will respond with an error, and the command will
be ignored.
-% STATHTTPD_SERVER_ERROR HTTP server error: %1
-An internal error occurred while handling an HTTP request. An HTTP 500
-response will be sent back, and the specific error is printed. This
-is an error condition that likely points to a module that is not
-responding correctly to statistic requests.
-
% STATHTTPD_SERVER_DATAERROR HTTP server data error: %1
An internal error occurred while handling an HTTP request. An HTTP 404
response will be sent back, and the specific error is printed. This
is an error condition that likely points the specified data
corresponding to the requested URI is incorrect.
+% STATHTTPD_SERVER_ERROR HTTP server error: %1
+An internal error occurred while handling an HTTP request. An HTTP 500
+response will be sent back, and the specific error is printed. This
+is an error condition that likely points to a module that is not
+responding correctly to statistic requests.
+
% STATHTTPD_SERVER_INIT_ERROR HTTP server initialization error: %1
There was a problem initializing the HTTP server in the stats-httpd
module upon receiving its configuration data. The most likely cause
@@ -71,12 +71,6 @@ and an error is sent back.
% STATHTTPD_SHUTDOWN shutting down
The stats-httpd daemon is shutting down.
-% STATHTTPD_START_SERVER_INIT_ERROR HTTP server initialization error: %1
-There was a problem initializing the HTTP server in the stats-httpd
-module upon startup. The most likely cause is that it was not able
-to bind to the listening port. The specific error is printed, and the
-module will shut down.
-
% STATHTTPD_STARTED listening on %1#%2
The stats-httpd daemon will now start listening for requests on the
given address and port number.
@@ -85,6 +79,12 @@ given address and port number.
Debug message indicating that the stats-httpd module is connecting to
the command and control bus.
+% STATHTTPD_START_SERVER_INIT_ERROR HTTP server initialization error: %1
+There was a problem initializing the HTTP server in the stats-httpd
+module upon startup. The most likely cause is that it was not able
+to bind to the listening port. The specific error is printed, and the
+module will shut down.
+
% STATHTTPD_STOPPED_BY_KEYBOARD keyboard interrupt, shutting down
There was a keyboard interrupt signal to stop the stats-httpd
daemon. The daemon will now shut down.
diff --git a/src/bin/stats/stats_messages.mes b/src/bin/stats/stats_messages.mes
index cfffb3a..3e75348 100644
--- a/src/bin/stats/stats_messages.mes
+++ b/src/bin/stats/stats_messages.mes
@@ -28,6 +28,12 @@ control bus. A likely problem is that the message bus daemon
This debug message is printed when the stats module has received a
configuration update from the configuration manager.
+% STATS_RECEIVED_SHOWSCHEMA_ALL_COMMAND received command to show all statistics schema
+The stats module received a command to show all statistics schemas of all modules.
+
+% STATS_RECEIVED_SHOWSCHEMA_NAME_COMMAND received command to show statistics schema for %1
+The stats module received a command to show the specified statistics schema of the specified module.
+
% STATS_RECEIVED_SHOW_ALL_COMMAND received command to show all statistics
The stats module received a command to show all statistics that it has
collected.
@@ -51,6 +57,13 @@ will respond with an error and the command will be ignored.
This debug message is printed when a request is sent to the boss module
to send its data to the stats module.
+% STATS_STARTING starting
+The stats module will be now starting.
+
+% STATS_START_ERROR stats module error: %1
+An internal error occurred while starting the stats module. The stats
+module will be now shutting down.
+
% STATS_STOPPED_BY_KEYBOARD keyboard interrupt, shutting down
There was a keyboard interrupt signal to stop the stats module. The
daemon will now shut down.
@@ -61,16 +74,3 @@ is unknown in the implementation. The most likely cause is an
installation problem, where the specification file stats.spec is
from a different version of BIND 10 than the stats module itself.
Please check your installation.
-
-% STATS_STARTING starting
-The stats module will be now starting.
-
-% STATS_RECEIVED_SHOWSCHEMA_ALL_COMMAND received command to show all statistics schema
-The stats module received a command to show all statistics schemas of all modules.
-
-% STATS_RECEIVED_SHOWSCHEMA_NAME_COMMAND received command to show statistics schema for %1
-The stats module received a command to show the specified statistics schema of the specified module.
-
-% STATS_START_ERROR stats module error: %1
-An internal error occurred while starting the stats module. The stats
-module will be now shutting down.
diff --git a/src/bin/stats/tests/b10-stats-httpd_test.py b/src/bin/stats/tests/b10-stats-httpd_test.py
index e74405a..57abed9 100644
--- a/src/bin/stats/tests/b10-stats-httpd_test.py
+++ b/src/bin/stats/tests/b10-stats-httpd_test.py
@@ -676,6 +676,24 @@ class TestStatsHttpd(unittest.TestCase):
self.assertTrue('address' in self.stats_httpd.config['listen_on'][0])
self.assertTrue('port' in self.stats_httpd.config['listen_on'][0])
self.assertTrue(server_address in set(self.stats_httpd.http_addrs))
+ ans = send_command(
+ isc.config.ccsession.COMMAND_GET_MODULE_SPEC,
+ "ConfigManager", {"module_name":"StatsHttpd"})
+ # assert StatsHttpd is added to ConfigManager
+ self.assertNotEqual(ans, (0,{}))
+ self.assertTrue(ans[1]['module_name'], 'StatsHttpd')
+
+ def test_init_hterr(self):
+ orig_open_httpd = stats_httpd.StatsHttpd.open_httpd
+ def err_open_httpd(arg): raise stats_httpd.HttpServerError
+ stats_httpd.StatsHttpd.open_httpd = err_open_httpd
+ self.assertRaises(stats_httpd.HttpServerError, stats_httpd.StatsHttpd)
+ ans = send_command(
+ isc.config.ccsession.COMMAND_GET_MODULE_SPEC,
+ "ConfigManager", {"module_name":"StatsHttpd"})
+ # assert StatsHttpd is removed from ConfigManager
+ self.assertEqual(ans, (0,{}))
+ stats_httpd.StatsHttpd.open_httpd = orig_open_httpd
def test_openclose_mccs(self):
self.stats_httpd = MyStatsHttpd(get_availaddr())
diff --git a/src/bin/xfrin/tests/xfrin_test.py b/src/bin/xfrin/tests/xfrin_test.py
index b88d6a9..a860022 100644
--- a/src/bin/xfrin/tests/xfrin_test.py
+++ b/src/bin/xfrin/tests/xfrin_test.py
@@ -2577,7 +2577,7 @@ class TestXfrin(unittest.TestCase):
self.common_ixfr_setup('refresh', False)
self.assertEqual(RRType.AXFR(), self.xfr.xfrin_started_request_type)
-class TextXfrinMemoryZones(unittest.TestCase):
+class TestXfrinMemoryZones(unittest.TestCase):
def setUp(self):
self.xfr = MockXfrin()
# Configuration snippet containing 2 memory datasources,
@@ -2736,6 +2736,44 @@ class TestMain(unittest.TestCase):
MockXfrin.check_command_hook = raise_exception
main(MockXfrin, False)
+class TestXfrinProcessMockCC:
+ def __init__(self):
+ self.get_called = False
+ self.get_called_correctly = False
+ self.config = []
+
+ def get_remote_config_value(self, module, identifier):
+ self.get_called = True
+ if module == 'Auth' and identifier == 'datasources':
+ self.get_called_correctly = True
+ return (self.config, False)
+ else:
+ return (None, True)
+
+class TestXfrinProcessMockCCSession:
+ def __init__(self):
+ self.send_called = False
+ self.send_called_correctly = False
+ self.recv_called = False
+ self.recv_called_correctly = False
+
+ def group_sendmsg(self, msg, module):
+ self.send_called = True
+ if module == 'Auth' and msg['command'][0] == 'loadzone':
+ self.send_called_correctly = True
+ seq = "random-e068c2de26d760f20cf10afc4b87ef0f"
+ else:
+ seq = None
+
+ return seq
+
+ def group_recvmsg(self, message, seq):
+ self.recv_called = True
+ if message == False and seq == "random-e068c2de26d760f20cf10afc4b87ef0f":
+ self.recv_called_correctly = True
+ # return values are ignored
+ return (None, None)
+
class TestXfrinProcess(unittest.TestCase):
"""
Some tests for the xfrin_process function. This replaces the
@@ -2751,6 +2789,8 @@ class TestXfrinProcess(unittest.TestCase):
Also sets up several internal variables to watch what happens.
"""
+ self._module_cc = TestXfrinProcessMockCC()
+ self._send_cc_session = TestXfrinProcessMockCCSession()
# This will hold a "log" of what transfers were attempted.
self.__transfers = []
# This will "log" if failures or successes happened.
@@ -2795,6 +2835,9 @@ class TestXfrinProcess(unittest.TestCase):
Part of pretending to be the server as well. This just logs the
success/failure of the previous operation.
"""
+ if ret == XFRIN_OK:
+ xfrin._do_auth_loadzone(self, zone_name, rrclass)
+
self.__published.append(ret)
def close(self):
@@ -2825,12 +2868,22 @@ class TestXfrinProcess(unittest.TestCase):
# Create a connection for each attempt
self.assertEqual(len(transfers), self.__created_connections)
self.assertEqual([published], self.__published)
+ if published == XFRIN_OK:
+ self.assertTrue(self._module_cc.get_called)
+ self.assertTrue(self._module_cc.get_called_correctly)
+ else:
+ self.assertFalse(self._module_cc.get_called)
+ self.assertFalse(self._module_cc.get_called_correctly)
def test_ixfr_ok(self):
"""
Everything OK the first time, over IXFR.
"""
self.__do_test([XFRIN_OK], [RRType.IXFR()], RRType.IXFR())
+ self.assertFalse(self._send_cc_session.send_called)
+ self.assertFalse(self._send_cc_session.send_called_correctly)
+ self.assertFalse(self._send_cc_session.recv_called)
+ self.assertFalse(self._send_cc_session.recv_called_correctly)
def test_axfr_ok(self):
"""
@@ -2861,6 +2914,138 @@ class TestXfrinProcess(unittest.TestCase):
"""
self.__do_test([XFRIN_FAIL, XFRIN_FAIL],
[RRType.IXFR(), RRType.AXFR()], RRType.IXFR())
+
+ def test_inmem_ok(self):
+ """
+ Inmem configuration where all the configuration is just right
+ for loadzone to be sent to b10-auth (origin is the name received
+ by xfrin, filetype is sqlite3, type is memory and class is the
+ one received by xfrin).
+ """
+ self._module_cc.config = [{'zones': [{'origin': 'example.org', 'filetype': 'sqlite3',
+ 'file': 'data/inmem-xfrin.sqlite3'}],
+ 'type': 'memory', 'class': 'IN'}]
+ self.__do_test([XFRIN_OK], [RRType.IXFR()], RRType.IXFR())
+ self.assertTrue(self._send_cc_session.send_called)
+ self.assertTrue(self._send_cc_session.send_called_correctly)
+ self.assertTrue(self._send_cc_session.recv_called)
+ self.assertTrue(self._send_cc_session.recv_called_correctly)
+
+ def test_inmem_datasource_type_not_memory(self):
+ """
+ Inmem configuration where the datasource type is not memory. In
+ this case, loadzone should not be sent to b10-auth.
+ """
+ self._module_cc.config = [{'zones': [{'origin': 'example.org', 'filetype': 'sqlite3',
+ 'file': 'data/inmem-xfrin.sqlite3'}],
+ 'type': 'punched-card', 'class': 'IN'}]
+ self.__do_test([XFRIN_OK], [RRType.IXFR()], RRType.IXFR())
+ self.assertFalse(self._send_cc_session.send_called)
+ self.assertFalse(self._send_cc_session.send_called_correctly)
+ self.assertFalse(self._send_cc_session.recv_called)
+ self.assertFalse(self._send_cc_session.recv_called_correctly)
+
+ def test_inmem_datasource_type_is_missing(self):
+ """
+ Inmem configuration where the datasource type is missing. In
+ this case, loadzone should not be sent to b10-auth.
+ """
+ self._module_cc.config = [{'zones': [{'origin': 'example.org', 'filetype': 'sqlite3',
+ 'file': 'data/inmem-xfrin.sqlite3'}],
+ 'class': 'IN'}]
+ self.__do_test([XFRIN_OK], [RRType.IXFR()], RRType.IXFR())
+ self.assertFalse(self._send_cc_session.send_called)
+ self.assertFalse(self._send_cc_session.send_called_correctly)
+ self.assertFalse(self._send_cc_session.recv_called)
+ self.assertFalse(self._send_cc_session.recv_called_correctly)
+
+ def test_inmem_backend_type_not_sqlite3(self):
+ """
+ Inmem configuration where the datasource backing file is not of
+ type sqlite3. In this case, loadzone should not be sent to
+ b10-auth.
+ """
+ self._module_cc.config = [{'zones': [{'origin': 'example.org', 'filetype': 'postgresql',
+ 'file': 'data/inmem-xfrin.db'}],
+ 'type': 'memory', 'class': 'IN'}]
+ self.__do_test([XFRIN_OK], [RRType.IXFR()], RRType.IXFR())
+ self.assertFalse(self._send_cc_session.send_called)
+ self.assertFalse(self._send_cc_session.send_called_correctly)
+ self.assertFalse(self._send_cc_session.recv_called)
+ self.assertFalse(self._send_cc_session.recv_called_correctly)
+
+ def test_inmem_backend_type_is_missing(self):
+ """
+ Inmem configuration where the datasource backing file type is
+ not set. In this case, loadzone should not be sent to b10-auth.
+ """
+ self._module_cc.config = [{'zones': [{'origin': 'example.org',
+ 'file': 'data/inmem-xfrin'}],
+ 'type': 'memory', 'class': 'IN'}]
+ self.__do_test([XFRIN_OK], [RRType.IXFR()], RRType.IXFR())
+ self.assertFalse(self._send_cc_session.send_called)
+ self.assertFalse(self._send_cc_session.send_called_correctly)
+ self.assertFalse(self._send_cc_session.recv_called)
+ self.assertFalse(self._send_cc_session.recv_called_correctly)
+
+ def test_inmem_class_is_different(self):
+ """
+ Inmem configuration where the datasource class does not match
+ the received class. In this case, loadzone should not be sent to
+ b10-auth.
+ """
+ self._module_cc.config = [{'zones': [{'origin': 'example.org', 'filetype': 'sqlite3',
+ 'file': 'data/inmem-xfrin.sqlite3'}],
+ 'type': 'memory', 'class': 'XX'}]
+ self.__do_test([XFRIN_OK], [RRType.IXFR()], RRType.IXFR())
+ self.assertFalse(self._send_cc_session.send_called)
+ self.assertFalse(self._send_cc_session.send_called_correctly)
+ self.assertFalse(self._send_cc_session.recv_called)
+ self.assertFalse(self._send_cc_session.recv_called_correctly)
+
+ def test_inmem_class_is_missing(self):
+ """
+ Inmem configuration where the datasource class is missing. In
+ this case, we assume the IN class and loadzone may be sent to
+ b10-auth if everything else matches.
+ """
+ self._module_cc.config = [{'zones': [{'origin': 'example.org', 'filetype': 'sqlite3',
+ 'file': 'data/inmem-xfrin.sqlite3'}],
+ 'type': 'memory'}]
+ self.__do_test([XFRIN_OK], [RRType.IXFR()], RRType.IXFR())
+ self.assertTrue(self._send_cc_session.send_called)
+ self.assertTrue(self._send_cc_session.send_called_correctly)
+ self.assertTrue(self._send_cc_session.recv_called)
+ self.assertTrue(self._send_cc_session.recv_called_correctly)
+
+ def test_inmem_name_doesnt_match(self):
+ """
+ Inmem configuration where the origin does not match the received
+ name. In this case, loadzone should not be sent to b10-auth.
+ """
+ self._module_cc.config = [{'zones': [{'origin': 'isc.org', 'filetype': 'sqlite3',
+ 'file': 'data/inmem-xfrin.sqlite3'}],
+ 'type': 'memory', 'class': 'IN'}]
+ self.__do_test([XFRIN_OK], [RRType.IXFR()], RRType.IXFR())
+ self.assertFalse(self._send_cc_session.send_called)
+ self.assertFalse(self._send_cc_session.send_called_correctly)
+ self.assertFalse(self._send_cc_session.recv_called)
+ self.assertFalse(self._send_cc_session.recv_called_correctly)
+
+ def test_inmem_name_is_missing(self):
+ """
+ Inmem configuration where the origin is missing. In this case,
+ loadzone should not be sent to b10-auth.
+ """
+ self._module_cc.config = [{'zones': [{'filetype': 'sqlite3',
+ 'file': 'data/inmem-xfrin.sqlite3'}],
+ 'type': 'memory', 'class': 'IN'}]
+ self.__do_test([XFRIN_OK], [RRType.IXFR()], RRType.IXFR())
+ self.assertFalse(self._send_cc_session.send_called)
+ self.assertFalse(self._send_cc_session.send_called_correctly)
+ self.assertFalse(self._send_cc_session.recv_called)
+ self.assertFalse(self._send_cc_session.recv_called_correctly)
+
class TestFormatting(unittest.TestCase):
# If the formatting functions are moved to a more general library
# (ticket #1379), these tests should be moved with them.
diff --git a/src/bin/xfrin/xfrin.py.in b/src/bin/xfrin/xfrin.py.in
index 58713be..114bac4 100755
--- a/src/bin/xfrin/xfrin.py.in
+++ b/src/bin/xfrin/xfrin.py.in
@@ -66,6 +66,7 @@ else:
SPECFILE_LOCATION = SPECFILE_PATH + "/xfrin.spec"
AUTH_SPECFILE_LOCATION = AUTH_SPECFILE_PATH + "/auth.spec"
+AUTH_MODULE_NAME = 'Auth'
XFROUT_MODULE_NAME = 'Xfrout'
ZONE_MANAGER_MODULE_NAME = 'Zonemgr'
REFRESH_FROM_ZONEMGR = 'refresh_from_zonemgr'
@@ -367,7 +368,6 @@ class XfrinFirstData(XfrinState):
conn.zone_str())
# We are now going to add RRs to the new zone. We need create
# a Diff object. It will be used throughtout the XFR session.
- # DISABLE FOR DEBUG
conn._diff = Diff(conn._datasrc_client, conn._zone_name, True)
self.set_xfrstate(conn, XfrinAXFR())
return False
@@ -1247,6 +1247,52 @@ class ZoneInfo:
return (self.master_addr.family, socket.SOCK_STREAM,
(str(self.master_addr), self.master_port))
+def _do_auth_loadzone(server, zone_name, zone_class):
+ # On a successful zone transfer, if the zone is served by
+ # b10-auth in the in-memory data source using sqlite3 as a
+ # backend, send the "loadzone" command for the zone to auth.
+ datasources, is_default =\
+ server._module_cc.get_remote_config_value(AUTH_MODULE_NAME, "datasources")
+ if is_default:
+ return
+ for d in datasources:
+ if "type" not in d:
+ continue
+ try:
+ if "class" in d:
+ dclass = RRClass(d["class"])
+ else:
+ dclass = RRClass("IN")
+ except InvalidRRClass as err:
+ logger.info(XFRIN_AUTH_CONFIG_RRCLASS_ERROR, str(err))
+ continue
+
+ if d["type"].lower() == "memory" and dclass == zone_class:
+ for zone in d["zones"]:
+ if "filetype" not in zone:
+ continue
+ if "origin" not in zone:
+ continue
+ if "filetype" not in zone:
+ continue
+ try:
+ name = Name(zone["origin"])
+ except (EmptyLabel, TooLongLabel, BadLabelType, BadEscape, TooLongName, IncompleteName):
+ logger.info(XFRIN_AUTH_CONFIG_NAME_PARSER_ERROR, str(err))
+ continue
+
+ if zone["filetype"].lower() == "sqlite3" and name == zone_name:
+ param = {"origin": zone_name.to_text(),
+ "class": zone_class.to_text(),
+ "datasrc": d["type"]}
+
+ logger.debug(DBG_XFRIN_TRACE, XFRIN_AUTH_LOADZONE,
+ param["origin"], param["class"], param["datasrc"])
+
+ msg = create_command("loadzone", param)
+ seq = server._send_cc_session.group_sendmsg(msg, AUTH_MODULE_NAME)
+ answer, env = server._send_cc_session.group_recvmsg(False, seq)
+
class Xfrin:
def __init__(self):
self._max_transfers_in = 10
@@ -1530,7 +1576,7 @@ class Xfrin:
def _set_db_file(self):
db_file, is_default =\
- self._module_cc.get_remote_config_value("Auth", "database_file")
+ self._module_cc.get_remote_config_value(AUTH_MODULE_NAME, "database_file")
if is_default and "B10_FROM_BUILD" in os.environ:
# override the local database setting if it is default and we
# are running from the source tree
@@ -1540,7 +1586,7 @@ class Xfrin:
"bind10_zones.sqlite3"
self._db_file = db_file
- def publish_xfrin_news(self, zone_name, zone_class, xfr_result):
+ def publish_xfrin_news(self, zone_name, zone_class, xfr_result):
'''Send command to xfrout/zone manager module.
If xfrin has finished successfully for one zone, tell the good
news(command: zone_new_data_ready) to zone manager and xfrout.
@@ -1549,6 +1595,7 @@ class Xfrin:
param = {'zone_name': zone_name.to_text(),
'zone_class': zone_class.to_text()}
if xfr_result == XFRIN_OK:
+ _do_auth_loadzone(self, zone_name, zone_class)
msg = create_command(notify_out.ZONE_NEW_DATA_READY_CMD, param)
# catch the exception, in case msgq has been killed.
try:
@@ -1567,6 +1614,7 @@ class Xfrin:
pass # for now we just ignore the failure
except socket.error as err:
logger.error(XFRIN_MSGQ_SEND_ERROR, XFROUT_MODULE_NAME, ZONE_MANAGER_MODULE_NAME)
+
else:
msg = create_command(ZONE_XFRIN_FAILED, param)
# catch the exception, in case msgq has been killed.
diff --git a/src/bin/xfrin/xfrin_messages.mes b/src/bin/xfrin/xfrin_messages.mes
index 25a1fc1..6b51661 100644
--- a/src/bin/xfrin/xfrin_messages.mes
+++ b/src/bin/xfrin/xfrin_messages.mes
@@ -15,6 +15,17 @@
# No namespace declaration - these constants go in the global namespace
# of the xfrin messages python module.
+% XFRIN_AUTH_CONFIG_NAME_PARSER_ERROR Invalid name when parsing Auth configuration: %1
+There was an invalid name when parsing Auth configuration.
+
+% XFRIN_AUTH_CONFIG_RRCLASS_ERROR Invalid RRClass when parsing Auth configuration: %1
+There was an invalid RR class when parsing Auth configuration.
+
+% XFRIN_AUTH_LOADZONE sending Auth loadzone for origin=%1, class=%2, datasrc=%3
+There was a successful zone transfer, and the zone is served by b10-auth
+in the in-memory data source using sqlite3 as a backend. We send the
+"loadzone" command for the zone to b10-auth.
+
% 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
@@ -113,6 +124,10 @@ 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
was killed.
+% XFRIN_MSGQ_SEND_ERROR_AUTH error while contacting %1
+There was a problem sending a message to b10-auth. This most likely
+means that the msgq daemon has quit or was killed.
+
% XFRIN_MSGQ_SEND_ERROR_ZONE_MANAGER error while contacting %1
There was a problem sending a message to the zone manager. This most
likely means that the msgq daemon has quit or was killed.
diff --git a/src/bin/xfrout/xfrout_messages.mes b/src/bin/xfrout/xfrout_messages.mes
index 9996a5a..a636f78 100644
--- a/src/bin/xfrout/xfrout_messages.mes
+++ b/src/bin/xfrout/xfrout_messages.mes
@@ -23,22 +23,15 @@ a valid TSIG key.
There was a problem reading from the command and control channel. The
most likely cause is that the msgq daemon is not running.
-% XFROUT_MODULECC_SESSION_ERROR error encountered by configuration/command module: %1
-There was a problem in the lower level module handling configuration and
-control commands. This could happen for various reasons, but the most likely
-cause is that the configuration database contains a syntax error and xfrout
-failed to start at initialization. A detailed error message from the module
-will also be displayed.
-
-% XFROUT_CONFIG_ERROR error found in configuration data: %1
-The xfrout process encountered an error when installing the configuration at
-startup time. Details of the error are included in the log message.
-
% XFROUT_CC_SESSION_TIMEOUT_ERROR timeout waiting for cc response
There was a problem reading a response from another module over the
command and control channel. The most likely cause is that the
configuration manager b10-cfgmgr is not running.
+% XFROUT_CONFIG_ERROR error found in configuration data: %1
+The xfrout process encountered an error when installing the configuration at
+startup time. Details of the error are included in the log message.
+
% XFROUT_FETCH_REQUEST_ERROR socket error while fetching a request from the auth daemon
There was a socket error while contacting the b10-auth daemon to
fetch a transfer request. The auth daemon may have shutdown.
@@ -56,6 +49,52 @@ are missing on the system, or the PYTHONPATH variable is not correct.
The specific place where this library needs to be depends on your
system and your specific installation.
+% XFROUT_IXFR_MULTIPLE_SOA IXFR client %1: authority section has multiple SOAs
+An IXFR request was received with more than one SOA RRs in the authority
+section. The xfrout daemon rejects the request with an RCODE of
+FORMERR.
+
+% XFROUT_IXFR_NO_JOURNAL_SUPPORT IXFR client %1, %2: journaling not supported in the data source, falling back to AXFR
+An IXFR request was received but the underlying data source did
+not support journaling. The xfrout daemon fell back to AXFR-style
+IXFR.
+
+% XFROUT_IXFR_NO_SOA IXFR client %1: missing SOA
+An IXFR request was received with no SOA RR in the authority section.
+The xfrout daemon rejects the request with an RCODE of FORMERR.
+
+% XFROUT_IXFR_NO_VERSION IXFR client %1, %2: version (%3 to %4) not in journal, falling back to AXFR
+An IXFR request was received, but the requested range of differences
+were not found in the data source. The xfrout daemon fell back to
+AXFR-style IXFR.
+
+% XFROUT_IXFR_NO_ZONE IXFR client %1, %2: zone not found with journal
+The requested zone in IXFR was not found in the data source
+even though the xfrout daemon sucessfully found the SOA RR of the zone
+in the data source. This can happen if the administrator removed the
+zone from the data source within the small duration between these
+operations, but it's more likely to be a bug or broken data source.
+Unless you know why this message was logged, and especially if it
+happens often, it's advisable to check whether the data source is
+valid for this zone. The xfrout daemon considers it a possible,
+though unlikely, event, and returns a response with an RCODE of
+NOTAUTH.
+
+% XFROUT_IXFR_UPTODATE IXFR client %1, %2: client version is new enough (theirs=%3, ours=%4)
+An IXFR request was received, but the client's SOA version is the same as
+or newer than that of the server. The xfrout server responds to the
+request with the answer section being just one SOA of that version.
+Note: as of this wrting the 'newer version' cannot be identified due to
+the lack of support for the serial number arithmetic. This will soon
+be implemented.
+
+% XFROUT_MODULECC_SESSION_ERROR error encountered by configuration/command module: %1
+There was a problem in the lower level module handling configuration and
+control commands. This could happen for various reasons, but the most likely
+cause is that the configuration database contains a syntax error and xfrout
+failed to start at initialization. A detailed error message from the module
+will also be displayed.
+
% XFROUT_NEW_CONFIG Update xfrout configuration
New configuration settings have been sent from the configuration
manager. The xfrout daemon will now apply them.
@@ -88,12 +127,6 @@ given host. This is required by the ACLs. The %2 represents the IP
address and port of the peer requesting the transfer, and the %3
represents the zone name and class.
-% XFROUT_QUERY_REJECTED %1 client %2: request to transfer %3 rejected
-The xfrout process rejected (by REFUSED rcode) a request to transfer zone to
-given host. This is because of ACLs. The %2 represents the IP
-address and port of the peer requesting the transfer, and the %3
-represents the zone name and class.
-
% XFROUT_QUERY_QUOTA_EXCCEEDED %1 client %2: request denied due to quota (%3)
The xfr request was rejected because the server was already handling
the maximum number of allowable transfers as specified in the transfers_out
@@ -104,20 +137,21 @@ this parameter; if the server is being too busy due to requests from
unexpected clients you may want to restrict the legitimate clients
with ACL.
-% XFROUT_RECEIVE_FILE_DESCRIPTOR_ERROR error receiving the file descriptor for an XFR connection
-There was an error receiving the file descriptor for the transfer
-request. Normally, the request is received by b10-auth, and passed on
-to the xfrout daemon, so it can answer directly. However, there was a
-problem receiving this file descriptor. The request will be ignored.
+% XFROUT_QUERY_REJECTED %1 client %2: request to transfer %3 rejected
+The xfrout process rejected (by REFUSED rcode) a request to transfer zone to
+given host. This is because of ACLs. The %2 represents the IP
+address and port of the peer requesting the transfer, and the %3
+represents the zone name and class.
% XFROUT_RECEIVED_SHUTDOWN_COMMAND shutdown command received
The xfrout daemon received a shutdown command from the command channel
and will now shut down.
-% XFROUT_REMOVE_UNIX_SOCKET_FILE_ERROR error clearing unix socket file %1: %2
-When shutting down, the xfrout daemon tried to clear the unix socket
-file used for communication with the auth daemon. It failed to remove
-the file. The reason for the failure is given in the error message.
+% XFROUT_RECEIVE_FILE_DESCRIPTOR_ERROR error receiving the file descriptor for an XFR connection
+There was an error receiving the file descriptor for the transfer
+request. Normally, the request is received by b10-auth, and passed on
+to the xfrout daemon, so it can answer directly. However, there was a
+problem receiving this file descriptor. The request will be ignored.
% XFROUT_REMOVE_OLD_UNIX_SOCKET_FILE_ERROR error removing unix socket file %1: %2
The unix socket file xfrout needs for contact with the auth daemon
@@ -126,6 +160,11 @@ removing it. It is likely that we do not have permission to remove
this file. The specific error is show in the log message. The xfrout
daemon will shut down.
+% XFROUT_REMOVE_UNIX_SOCKET_FILE_ERROR error clearing unix socket file %1: %2
+When shutting down, the xfrout daemon tried to clear the unix socket
+file used for communication with the auth daemon. It failed to remove
+the file. The reason for the failure is given in the error message.
+
% XFROUT_SOCKET_SELECT_ERROR error while calling select() on request socket: %1
There was an error while calling select() on the socket that informs
the xfrout daemon that a new xfrout request has arrived. This should
@@ -151,6 +190,13 @@ on, but the file is in use. The most likely cause is that another
xfrout daemon process is still running. This xfrout daemon (the one
printing this message) will not start.
+% XFROUT_XFR_TRANSFER_CHECK_ERROR %1 client %2: check for transfer of %3 failed: %4
+Pre-response check for an incomding XFR request failed unexpectedly.
+The most likely cause of this is that some low level error in the data
+source, but it may also be other general (more unlikely) errors such
+as memory shortage. Some detail of the error is also included in the
+message. The xfrout server tries to return a SERVFAIL response in this case.
+
% XFROUT_XFR_TRANSFER_DONE %1 client %2: transfer of %3 complete
The transfer of the given zone has been completed successfully, or was
aborted due to a shutdown event.
@@ -161,13 +207,6 @@ an AXFR query. The error message of the exception is included in the
log message, but this error most likely points to incomplete exception
handling in the code.
-% XFROUT_XFR_TRANSFER_CHECK_ERROR %1 client %2: check for transfer of %3 failed: %4
-Pre-response check for an incomding XFR request failed unexpectedly.
-The most likely cause of this is that some low level error in the data
-source, but it may also be other general (more unlikely) errors such
-as memory shortage. Some detail of the error is also included in the
-message. The xfrout server tries to return a SERVFAIL response in this case.
-
% XFROUT_XFR_TRANSFER_FAILED %1 client %2: transfer of %3 failed, rcode: %4
A transfer out for the given zone failed. An error response is sent
to the client. The given rcode is the rcode that is set in the error
@@ -181,42 +220,3 @@ Xfrout/max_transfers_out, has been reached).
% XFROUT_XFR_TRANSFER_STARTED %1 client %2: transfer of zone %3 has started
A transfer out of the given zone has started.
-
-% XFROUT_IXFR_MULTIPLE_SOA IXFR client %1: authority section has multiple SOAs
-An IXFR request was received with more than one SOA RRs in the authority
-section. The xfrout daemon rejects the request with an RCODE of
-FORMERR.
-
-% XFROUT_IXFR_NO_SOA IXFR client %1: missing SOA
-An IXFR request was received with no SOA RR in the authority section.
-The xfrout daemon rejects the request with an RCODE of FORMERR.
-
-% XFROUT_IXFR_NO_JOURNAL_SUPPORT IXFR client %1, %2: journaling not supported in the data source, falling back to AXFR
-An IXFR request was received but the underlying data source did
-not support journaling. The xfrout daemon fell back to AXFR-style
-IXFR.
-
-% XFROUT_IXFR_UPTODATE IXFR client %1, %2: client version is new enough (theirs=%3, ours=%4)
-An IXFR request was received, but the client's SOA version is the same as
-or newer than that of the server. The xfrout server responds to the
-request with the answer section being just one SOA of that version.
-Note: as of this wrting the 'newer version' cannot be identified due to
-the lack of support for the serial number arithmetic. This will soon
-be implemented.
-
-% XFROUT_IXFR_NO_VERSION IXFR client %1, %2: version (%3 to %4) not in journal, falling back to AXFR
-An IXFR request was received, but the requested range of differences
-were not found in the data source. The xfrout daemon fell back to
-AXFR-style IXFR.
-
-% XFROUT_IXFR_NO_ZONE IXFR client %1, %2: zone not found with journal
-The requested zone in IXFR was not found in the data source
-even though the xfrout daemon sucessfully found the SOA RR of the zone
-in the data source. This can happen if the administrator removed the
-zone from the data source within the small duration between these
-operations, but it's more likely to be a bug or broken data source.
-Unless you know why this message was logged, and especially if it
-happens often, it's advisable to check whether the data source is
-valid for this zone. The xfrout daemon considers it a possible,
-though unlikely, event, and returns a response with an RCODE of
-NOTAUTH.
diff --git a/src/bin/zonemgr/zonemgr_messages.mes b/src/bin/zonemgr/zonemgr_messages.mes
index c866b79..88f8dcf 100644
--- a/src/bin/zonemgr/zonemgr_messages.mes
+++ b/src/bin/zonemgr/zonemgr_messages.mes
@@ -67,10 +67,6 @@ zone manager to record the master server for the zone and start a timer;
when the timer expires, the master will be polled to see if it contains
new data.
-% ZONEMGR_STARTED zonemgr started
-This informational message is output by zonemgr when all initialization
-has been completed and it is entering its main loop.
-
% ZONEMGR_RECEIVE_SHUTDOWN received SHUTDOWN command
This is a debug message indicating that the zone manager has received
a SHUTDOWN command over the command channel from the Boss process.
@@ -120,6 +116,10 @@ problem is that the daemon is not running.
% ZONEMGR_SHUTDOWN zone manager has shut down
A debug message, output when the zone manager has shut down completely.
+% ZONEMGR_STARTED zonemgr started
+This informational message is output by zonemgr when all initialization
+has been completed and it is entering its main loop.
+
% ZONEMGR_STARTING zone manager starting
A debug message output when the zone manager starts up.
diff --git a/src/lib/asiolink/io_address.cc b/src/lib/asiolink/io_address.cc
index 370e8f5..832452c 100644
--- a/src/lib/asiolink/io_address.cc
+++ b/src/lib/asiolink/io_address.cc
@@ -37,7 +37,7 @@ namespace asiolink {
// XXX: we cannot simply construct the address in the initialization list,
// because we'd like to throw our own exception on failure.
-IOAddress::IOAddress(const string& address_str) {
+IOAddress::IOAddress(const std::string& address_str) {
asio::error_code err;
asio_address_ = ip::address::from_string(address_str, err);
if (err) {
@@ -46,7 +46,7 @@ IOAddress::IOAddress(const string& address_str) {
}
}
-IOAddress::IOAddress(const ip::address& asio_address) :
+IOAddress::IOAddress(const asio::ip::address& asio_address) :
asio_address_(asio_address)
{}
diff --git a/src/lib/cache/cache_messages.mes b/src/lib/cache/cache_messages.mes
index 19102ae..51f2264 100644
--- a/src/lib/cache/cache_messages.mes
+++ b/src/lib/cache/cache_messages.mes
@@ -66,14 +66,14 @@ is created.
Debug message. The resolver cache is looking up the deepest known nameserver,
so the resolution doesn't have to start from the root.
+% CACHE_RESOLVER_INIT initializing resolver cache for class %1
+Debug message. The resolver cache is being created for this given class.
+
% CACHE_RESOLVER_INIT_INFO initializing resolver cache for class %1
Debug message, the resolver cache is being created for this given class. The
difference from CACHE_RESOLVER_INIT is only in different format of passed
information, otherwise it does the same.
-% CACHE_RESOLVER_INIT initializing resolver cache for class %1
-Debug message. The resolver cache is being created for this given class.
-
% CACHE_RESOLVER_LOCAL_MSG message for %1/%2 found in local zone data
Debug message. The resolver cache found a complete message for the user query
in the zone data.
diff --git a/src/lib/config/module_spec.cc b/src/lib/config/module_spec.cc
index 0defd6d..a931070 100644
--- a/src/lib/config/module_spec.cc
+++ b/src/lib/config/module_spec.cc
@@ -40,13 +40,14 @@ check_leaf_item(ConstElementPtr spec, const std::string& name,
if (spec->get(name)->getType() == type) {
return;
} else {
- throw ModuleSpecError(name + " not of type " + Element::typeToName(type));
+ isc_throw(ModuleSpecError,
+ name + " not of type " + Element::typeToName(type));
}
} else if (mandatory) {
// todo: want parent item name, and perhaps some info about location
// in list? or just catch and throw new...
// or make this part non-throwing and check return value...
- throw ModuleSpecError(name + " missing in " + spec->str());
+ isc_throw(ModuleSpecError, name + " missing in " + spec->str());
}
}
@@ -80,7 +81,7 @@ check_config_item(ConstElementPtr spec) {
void
check_config_item_list(ConstElementPtr spec) {
if (spec->getType() != Element::list) {
- throw ModuleSpecError("config_data is not a list of elements");
+ isc_throw(ModuleSpecError, "config_data is not a list of elements");
}
BOOST_FOREACH(ConstElementPtr item, spec->listValue()) {
check_config_item(item);
@@ -122,7 +123,7 @@ void check_statistics_item_list(ConstElementPtr spec);
void
check_statistics_item_list(ConstElementPtr spec) {
if (spec->getType() != Element::list) {
- throw ModuleSpecError("statistics is not a list of elements");
+ isc_throw(ModuleSpecError, "statistics is not a list of elements");
}
BOOST_FOREACH(ConstElementPtr item, spec->listValue()) {
check_config_item(item);
@@ -135,7 +136,7 @@ check_statistics_item_list(ConstElementPtr spec) {
&& item->contains("item_default")) {
if(!check_format(item->get("item_default"),
item->get("item_format"))) {
- throw ModuleSpecError(
+ isc_throw(ModuleSpecError,
"item_default not valid type of item_format");
}
}
@@ -152,7 +153,7 @@ check_command(ConstElementPtr spec) {
void
check_command_list(ConstElementPtr spec) {
if (spec->getType() != Element::list) {
- throw ModuleSpecError("commands is not a list of elements");
+ isc_throw(ModuleSpecError, "commands is not a list of elements");
}
BOOST_FOREACH(ConstElementPtr item, spec->listValue()) {
check_command(item);
@@ -183,7 +184,7 @@ check_module_specification(ConstElementPtr def) {
try {
check_data_specification(def);
} catch (const TypeError& te) {
- throw ModuleSpecError(te.what());
+ isc_throw(ModuleSpecError, te.what());
}
}
}
@@ -314,14 +315,14 @@ moduleSpecFromFile(const std::string& file_name, const bool check)
if (!file) {
std::stringstream errs;
errs << "Error opening " << file_name << ": " << strerror(errno);
- throw ModuleSpecError(errs.str());
+ isc_throw(ModuleSpecError, errs.str());
}
ConstElementPtr module_spec_element = Element::fromJSON(file, file_name);
if (module_spec_element->contains("module_spec")) {
return (ModuleSpec(module_spec_element->get("module_spec"), check));
} else {
- throw ModuleSpecError("No module_spec in specification");
+ isc_throw(ModuleSpecError, "No module_spec in specification");
}
}
@@ -333,7 +334,7 @@ moduleSpecFromFile(std::ifstream& in, const bool check)
if (module_spec_element->contains("module_spec")) {
return (ModuleSpec(module_spec_element->get("module_spec"), check));
} else {
- throw ModuleSpecError("No module_spec in specification");
+ isc_throw(ModuleSpecError, "No module_spec in specification");
}
}
diff --git a/src/lib/config/module_spec.h b/src/lib/config/module_spec.h
index ce3762f..27dcfe3 100644
--- a/src/lib/config/module_spec.h
+++ b/src/lib/config/module_spec.h
@@ -26,15 +26,11 @@ namespace isc { namespace config {
/// A standard ModuleSpec exception that is thrown when a
/// specification is not in the correct form.
///
- /// TODO: use jinmei's exception class as a base and not c_str in
- /// what() there
- class ModuleSpecError : public std::exception {
+ class ModuleSpecError : public isc::Exception {
public:
- ModuleSpecError(std::string m = "Module specification is invalid") : msg(m) {}
- ~ModuleSpecError() throw() {}
- const char* what() const throw() { return (msg.c_str()); }
- private:
- std::string msg;
+ ModuleSpecError(const char* file, size_t line,
+ const char* what = "Module specification is invalid") :
+ isc::Exception(file, line, what) {}
};
///
diff --git a/src/lib/cryptolink/crypto_hmac.cc b/src/lib/cryptolink/crypto_hmac.cc
index 277b036..c1bbfa8 100644
--- a/src/lib/cryptolink/crypto_hmac.cc
+++ b/src/lib/cryptolink/crypto_hmac.cc
@@ -23,6 +23,8 @@
#include <botan/hash.h>
#include <botan/types.h>
+#include <cstring>
+
namespace {
const char*
getBotanHashAlgorithmName(isc::cryptolink::HashAlgorithm algorithm) {
@@ -155,7 +157,7 @@ public:
if (output_size > len) {
output_size = len;
}
- memcpy(result, b_result.begin(), output_size);
+ std::memcpy(result, b_result.begin(), output_size);
} catch (const Botan::Exception& exc) {
isc_throw(isc::cryptolink::LibraryError, exc.what());
}
diff --git a/src/lib/datasrc/database.cc b/src/lib/datasrc/database.cc
index fcb2971..7b271f1 100644
--- a/src/lib/datasrc/database.cc
+++ b/src/lib/datasrc/database.cc
@@ -1415,49 +1415,132 @@ DatabaseUpdater::validateAddOrDelete(const char* const op_str,
}
}
+// This is a helper class used in adding/deleting RRsets to/from a database.
+// The purpose of this class is to provide conversion interface from various
+// parameters of the RRset to corresponding textual representations that the
+// underlying database interface expects. The necessary parameters and how
+// to convert them depend on several things, such as whether it's NSEC3 related
+// or not, or whether journaling is requested. In order to avoid unnecessary
+// conversion, this class also performs the conversion in a lazy manner.
+// Also, in order to avoid redundant conversion when the conversion is
+// requested for the same parameter multiple times, it remembers the
+// conversion result first time, and reuses it for subsequent requests
+// (this implicitly assumes copying std::string objects is not very expensive;
+// this is often the case in some common implementations that have
+// copy-on-write semantics for the string class).
+class RRParameterConverter {
+public:
+ RRParameterConverter(const AbstractRRset& rrset) : rrset_(rrset)
+ {}
+ const string& getName() {
+ if (name_.empty()) {
+ name_ = rrset_.getName().toText();
+ }
+ return (name_);
+ }
+ const string& getNSEC3Name() {
+ if (nsec3_name_.empty()) {
+ nsec3_name_ = rrset_.getName().split(0, 1).toText(true);
+ }
+ return (nsec3_name_);
+ }
+ const string& getRevName() {
+ if (revname_.empty()) {
+ revname_ = rrset_.getName().reverse().toText();
+ }
+ return (revname_);
+ }
+ const string& getTTL() {
+ if (ttl_.empty()) {
+ ttl_ = rrset_.getTTL().toText();
+ }
+ return (ttl_);
+ }
+ const string& getType() {
+ if (type_.empty()) {
+ type_ = rrset_.getType().toText();
+ }
+ return (type_);
+ }
+
+private:
+ string name_;
+ string nsec3_name_;
+ string revname_;
+ string ttl_;
+ string type_;
+ const AbstractRRset& rrset_;
+};
+
+namespace {
+// A shared shortcut to detect if the given type of RDATA is NSEC3 or
+// RRSIG covering NSEC3. RRSIG for NSEC3 should go to the (conceptual)
+// separate namespace, so we need to check the covered type.
+// Note: in principle the type covered should be the same for
+// all RDATA, but the RRset interface doesn't ensure that condition.
+// So we explicitly check that for every RDATA below.
+bool
+isNSEC3KindType(RRType rrtype, const Rdata& rdata) {
+ if (rrtype == RRType::NSEC3()) {
+ return (true);
+ }
+ if (rrtype == RRType::RRSIG() &&
+ dynamic_cast<const generic::RRSIG&>(rdata).typeCovered() ==
+ RRType::NSEC3())
+ {
+ return (true);
+ }
+ return (false);
+}
+}
+
void
DatabaseUpdater::addRRset(const AbstractRRset& rrset) {
validateAddOrDelete("add", rrset, DELETE, ADD);
// It's guaranteed rrset has at least one RDATA at this point.
RdataIteratorPtr it = rrset.getRdataIterator();
-
- string columns[Accessor::ADD_COLUMN_COUNT]; // initialized with ""
- columns[Accessor::ADD_NAME] = rrset.getName().toText();
- columns[Accessor::ADD_REV_NAME] = rrset.getName().reverse().toText();
- columns[Accessor::ADD_TTL] = rrset.getTTL().toText();
- columns[Accessor::ADD_TYPE] = rrset.getType().toText();
- string journal[Accessor::DIFF_PARAM_COUNT];
if (journaling_) {
- journal[Accessor::DIFF_NAME] = columns[Accessor::ADD_NAME];
- journal[Accessor::DIFF_TYPE] = columns[Accessor::ADD_TYPE];
- journal[Accessor::DIFF_TTL] = columns[Accessor::ADD_TTL];
diff_phase_ = ADD;
if (rrset.getType() == RRType::SOA()) {
- serial_ =
- dynamic_cast<const generic::SOA&>(it->getCurrent()).
+ serial_ = dynamic_cast<const generic::SOA&>(it->getCurrent()).
getSerial();
}
}
+
+ RRParameterConverter cvtr(rrset);
for (; !it->isLast(); it->next()) {
+ const Rdata& rdata = it->getCurrent();
+ const bool nsec3_type = isNSEC3KindType(rrset.getType(), rdata);
+
+ string sigtype;
if (rrset.getType() == RRType::RRSIG()) {
// XXX: the current interface (based on the current sqlite3
// data source schema) requires a separate "sigtype" column,
// even though it won't be used in a newer implementation.
// We should eventually clean up the schema design and simplify
// the interface, but until then we have to conform to the schema.
- const generic::RRSIG& rrsig_rdata =
- dynamic_cast<const generic::RRSIG&>(it->getCurrent());
- columns[Accessor::ADD_SIGTYPE] =
- rrsig_rdata.typeCovered().toText();
+ sigtype = dynamic_cast<const generic::RRSIG&>(rdata).
+ typeCovered().toText();
}
- columns[Accessor::ADD_RDATA] = it->getCurrent().toText();
+ const string& rdata_txt = rdata.toText();
if (journaling_) {
- journal[Accessor::DIFF_RDATA] = columns[Accessor::ADD_RDATA];
+ const string journal[Accessor::DIFF_PARAM_COUNT] =
+ { cvtr.getName(), cvtr.getType(), cvtr.getTTL(), rdata_txt };
accessor_->addRecordDiff(zone_id_, serial_.getValue(),
Accessor::DIFF_ADD, journal);
}
- accessor_->addRecordToZone(columns);
+ if (nsec3_type) {
+ const string nsec3_columns[Accessor::ADD_NSEC3_COLUMN_COUNT] =
+ { cvtr.getNSEC3Name(), cvtr.getTTL(), cvtr.getType(),
+ rdata_txt };
+ accessor_->addNSEC3RecordToZone(nsec3_columns);
+ } else {
+ const string columns[Accessor::ADD_COLUMN_COUNT] =
+ { cvtr.getName(), cvtr.getRevName(), cvtr.getTTL(),
+ cvtr.getType(), sigtype, rdata_txt };
+ accessor_->addRecordToZone(columns);
+ }
}
}
@@ -1472,15 +1555,7 @@ DatabaseUpdater::deleteRRset(const AbstractRRset& rrset) {
validateAddOrDelete("delete", rrset, ADD, DELETE);
RdataIteratorPtr it = rrset.getRdataIterator();
-
- string params[Accessor::DEL_PARAM_COUNT]; // initialized with ""
- params[Accessor::DEL_NAME] = rrset.getName().toText();
- params[Accessor::DEL_TYPE] = rrset.getType().toText();
- string journal[Accessor::DIFF_PARAM_COUNT];
if (journaling_) {
- journal[Accessor::DIFF_NAME] = params[Accessor::DEL_NAME];
- journal[Accessor::DIFF_TYPE] = params[Accessor::DEL_TYPE];
- journal[Accessor::DIFF_TTL] = rrset.getTTL().toText();
diff_phase_ = DELETE;
if (rrset.getType() == RRType::SOA()) {
serial_ =
@@ -1488,14 +1563,27 @@ DatabaseUpdater::deleteRRset(const AbstractRRset& rrset) {
getSerial();
}
}
+
+ RRParameterConverter cvtr(rrset);
for (; !it->isLast(); it->next()) {
- params[Accessor::DEL_RDATA] = it->getCurrent().toText();
+ const Rdata& rdata = it->getCurrent();
+ const bool nsec3_type = isNSEC3KindType(rrset.getType(), rdata);
+ const string& rdata_txt = it->getCurrent().toText();
+
if (journaling_) {
- journal[Accessor::DIFF_RDATA] = params[Accessor::DEL_RDATA];
+ const string journal[Accessor::DIFF_PARAM_COUNT] =
+ { cvtr.getName(), cvtr.getType(), cvtr.getTTL(), rdata_txt };
accessor_->addRecordDiff(zone_id_, serial_.getValue(),
Accessor::DIFF_DELETE, journal);
}
- accessor_->deleteRecordInZone(params);
+ const string params[Accessor::DEL_PARAM_COUNT] =
+ { nsec3_type ? cvtr.getNSEC3Name() : cvtr.getName(),
+ cvtr.getType(), rdata_txt };
+ if (nsec3_type) {
+ accessor_->deleteNSEC3RecordInZone(params);
+ } else {
+ accessor_->deleteRecordInZone(params);
+ }
}
}
diff --git a/src/lib/datasrc/database.h b/src/lib/datasrc/database.h
index 40134fc..8083322 100644
--- a/src/lib/datasrc/database.h
+++ b/src/lib/datasrc/database.h
@@ -95,13 +95,36 @@ public:
ADD_COLUMN_COUNT = 6 ///< Number of columns
};
+ /// \brief Definitions of the fields to be passed to addNSEC3RecordToZone()
+ ///
+ /// Each derived implementation of addNSEC3RecordToZone() should expect
+ /// the "columns" array to be filled with the values as described in this
+ /// enumeration, in this order.
+ ///
+ /// Note that there is no "reversed name" column. Since the conceptual
+ /// separate namespace for NSEC3 is very simplified and essentially only
+ /// consists of a single-label names, there is no need for using reversed
+ /// names to identify the "previous hash".
+ enum AddNSEC3RecordColumns {
+ ADD_NSEC3_HASH = 0, ///< The hash (1st) label of the owner name,
+ ///< excluding the dot character
+ ADD_NSEC3_TTL = 1, ///< The TTL of the record (in numeric form)
+ ADD_NSEC3_TYPE = 2, ///< The RRType of the record (either NSEC3 or
+ ///< RRSIG for NSEC3)
+ ADD_NSEC3_RDATA = 3, ///< Full text representation of the record's
+ ///< RDATA
+ ADD_NSEC3_COLUMN_COUNT = 4 ///< Number of columns
+ };
+
/// \brief Definitions of the fields to be passed to deleteRecordInZone()
+ /// and deleteNSEC3RecordInZone()
///
/// Each derived implementation of deleteRecordInZone() should expect
/// the "params" array to be filled with the values as described in this
/// enumeration, in this order.
enum DeleteRecordParams {
DEL_NAME = 0, ///< The owner name of the record (a domain name)
+ ///< or the hash label for deleteNSEC3RecordInZone()
DEL_TYPE = 1, ///< The RRType of the record (A/NS/TXT etc.)
DEL_RDATA = 2, ///< Full text representation of the record's RDATA
DEL_PARAM_COUNT = 3 ///< Number of parameters
@@ -432,6 +455,46 @@ public:
virtual void addRecordToZone(
const std::string (&columns)[ADD_COLUMN_COUNT]) = 0;
+ /// \brief Add a single NSEC3-related record to the zone to be updated.
+ ///
+ /// This method is similar to \c addRecordToZone(), but is expected to
+ /// be only used for NSEC3 RRs or RRSIG RRs that cover NSEC3. In terms
+ /// of the DNS protocol, these types of RRs reside in a separate space
+ /// of the zone. While this interface does not mandate a specific way
+ /// of implementing the separate namespaces in the underlying database,
+ /// it would be more convenient for the underlying implementation if the
+ /// interfaces are separated; for example, the implementation does not
+ /// have to examine the given data to identify the appropriate namespace.
+ ///
+ /// An implementation may choose to skip providing this interface if the
+ /// zones managed by that data source are known to not support NSEC3.
+ /// In that case the implementation should throw the
+ /// \c isc::NotImplemented exception.
+ ///
+ /// Note that the \c ADD_NSEC3_HASH column of \c columns is expected to
+ /// store only the hash label, not the entire owner name. This is similar
+ /// to the \c hash parameter of \c getNSEC3Records().
+ ///
+ /// The RRs to be added using this method are expected to be limited to
+ /// NSEC3 or RRSIG RRs that cover NSEC3, but it's generally assumed to
+ /// be the caller's responsibility to ensure that; the implementation
+ /// is not required to check that condition. The result of adding
+ /// unexpected type of RRs (and the result of subsequent lookups) is
+ /// undefined.
+ ///
+ /// Other general notes for \c addRecordToZone() also apply to this
+ /// method.
+ ///
+ /// \exception DataSourceError Invalid call without starting a transaction,
+ /// or other internal database error.
+ /// \exception isc::NotImplemented in case the database does not support
+ /// NSEC3
+ ///
+ /// \param columns An array of strings that defines a record to be added
+ /// to the NSEC3 namespace of the zone.
+ virtual void addNSEC3RecordToZone(
+ const std::string (&columns)[ADD_NSEC3_COLUMN_COUNT]) = 0;
+
/// \brief Delete a single record from the zone to be updated.
///
/// This method provides a simple interface to delete a record
@@ -469,6 +532,31 @@ public:
virtual void deleteRecordInZone(
const std::string (¶ms)[DEL_PARAM_COUNT]) = 0;
+ /// \brief Delete a single NSEC3-related record from the zone to be
+ /// updated.
+ ///
+ /// This method is similar to \c deleteRecordInZone(), but is expected to
+ /// be only used for NSEC3 RRs or RRSIG RRs that cover NSEC3. The
+ /// relationship between these two methods is similar to that between
+ /// \c addRecordToZone() and \c addNSEC3RecordToZone(), and the same
+ /// notes apply to this method.
+ ///
+ /// This method uses the same set of parameters to specify the record
+ /// to be deleted as \c deleteRecordInZone(), but the \c DEL_NAME column
+ /// is expected to only store the hash label of the owner name.
+ /// This is the same as \c ADD_NSEC3_HASH column for
+ /// \c addNSEC3RecordToZone().
+ ///
+ /// \exception DataSourceError Invalid call without starting a transaction,
+ /// or other internal database error.
+ /// \exception isc::NotImplemented in case the database does not support
+ /// NSEC3
+ ///
+ /// \param params An array of strings that defines a record to be deleted
+ /// from the NSEC3 namespace of the zone.
+ virtual void deleteNSEC3RecordInZone(
+ const std::string (¶ms)[DEL_PARAM_COUNT]) = 0;
+
/// \brief Start a general transaction.
///
/// Each derived class version of this method starts a database
diff --git a/src/lib/datasrc/memory_datasrc.cc b/src/lib/datasrc/memory_datasrc.cc
index c19d5ae..8e834cb 100644
--- a/src/lib/datasrc/memory_datasrc.cc
+++ b/src/lib/datasrc/memory_datasrc.cc
@@ -120,7 +120,11 @@ typedef NSEC3Map::value_type NSEC3Pair;
// Actual zone data: Essentially a set of zone's RRs. This is defined as
// a separate structure so that it'll be replaceable on reload.
struct ZoneData {
- ZoneData(const Name& origin) : domains_(true), origin_data_(NULL) {
+ ZoneData(const Name& origin) :
+ domains_(true),
+ origin_data_(NULL),
+ nsec_signed_(false)
+ {
// We create the node for origin (it needs to exist anyway in future)
domains_.insert(origin, &origin_data_);
DomainPtr origin_domain(new Domain);
@@ -170,6 +174,7 @@ public:
const scoped_ptr<NSEC3Hash> hash_; // hash parameter/calculator
};
scoped_ptr<NSEC3Data> nsec3_data_; // non NULL only when it's NSEC3 signed
+ bool nsec_signed_; // True if there's at least one NSEC record
// This templated structure encapsulates the find result of findNode()
// method (also templated) below.
@@ -207,11 +212,66 @@ public:
// Identify the RBTree node that best matches the given name.
// See implementation notes below.
+ //
+ // The caller should pass an empty node_path, and it will contain the
+ // search context (all ancestor nodes that the underlying RBTree search
+ // traverses, and how the search stops) for possible later use at the
+ // caller side.
template <typename ResultType>
ResultType findNode(const Name& name,
+ RBTreeNodeChain<Domain>& node_path,
ZoneFinder::FindOptions options) const;
+
+ // A helper method for NSEC-signed zones. It searches the zone for
+ // the "closest" NSEC corresponding to the search context stored in
+ // node_path (it should contain sufficient information to identify the
+ // previous name of the query name in the zone). In some cases the
+ // immediate closest name may not have NSEC (when it's under a zone cut
+ // for glue records, or even when the zone is partly broken), so this
+ // method continues the search until it finds a name that has NSEC,
+ // and returns the one found first. Due to the prerequisite (see below),
+ // it should always succeed.
+ //
+ // node_path must store valid search context (in practice, it's expected
+ // to be set by findNode()); otherwise the underlying RBTree implementation
+ // throws.
+ //
+ // If the zone is not considered NSEC-signed or DNSSEC records were not
+ // required in the original search context (specified in options), this
+ // method doesn't bother to find NSEC, and simply returns NULL. So, by
+ // definition of "NSEC-signed", when it really tries to find an NSEC it
+ // should succeed; there should be one at least at the zone origin.
+ ConstRBNodeRRsetPtr
+ getClosestNSEC(RBTreeNodeChain<Domain>& node_path,
+ ZoneFinder::FindOptions options) const;
};
+ConstRBNodeRRsetPtr
+ZoneData::getClosestNSEC(RBTreeNodeChain<Domain>& node_path,
+ ZoneFinder::FindOptions options) const
+{
+ if (!nsec_signed_ || (options & ZoneFinder::FIND_DNSSEC) == 0) {
+ return (ConstRBNodeRRsetPtr());
+ }
+
+ const DomainNode* prev_node;
+ while ((prev_node = domains_.previousNode(node_path)) != NULL) {
+ if (!prev_node->isEmpty()) {
+ const Domain::const_iterator found =
+ prev_node->getData()->find(RRType::NSEC());
+ if (found != prev_node->getData()->end()) {
+ return (found->second);
+ }
+ }
+ }
+ // This must be impossible and should be an internal bug.
+ // See the description at the method declaration.
+ assert(false);
+ // Even though there is an assert here, strict compilers
+ // will still need some return value.
+ return (ConstRBNodeRRsetPtr());
+}
+
/// Maintain intermediate data specific to the search context used in
/// \c find().
///
@@ -354,9 +414,10 @@ bool cutCallback(const DomainNode& node, FindState* state) {
// the zone.
template <typename ResultType>
ResultType
-ZoneData::findNode(const Name& name, ZoneFinder::FindOptions options) const {
+ZoneData::findNode(const Name& name, RBTreeNodeChain<Domain>& node_path,
+ ZoneFinder::FindOptions options) const
+{
DomainNode* node = NULL;
- RBTreeNodeChain<Domain> node_path;
FindState state((options & ZoneFinder::FIND_GLUE_OK) != 0);
const DomainTree::Result result =
@@ -382,9 +443,10 @@ ZoneData::findNode(const Name& name, ZoneFinder::FindOptions options) const {
NameComparisonResult::SUPERDOMAIN) { // empty node, so NXRRSET
LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_SUPER_STOP).arg(name);
return (ResultType(ZoneFinder::NXRRSET, node,
- ConstRBNodeRRsetPtr()));
+ getClosestNSEC(node_path, options)));
}
- if (node->getFlag(domain_flag::WILD)) { // maybe a wildcard
+ if (node->getFlag(domain_flag::WILD) && // maybe a wildcard, check only
+ (options & ZoneFinder::NO_WILDCARD) == 0) { // if not disabled.
if (node_path.getLastComparisonResult().getRelation() ==
NameComparisonResult::COMMONANCESTOR &&
node_path.getLastComparisonResult().getCommonLabels() > 1) {
@@ -398,12 +460,17 @@ ZoneData::findNode(const Name& name, ZoneFinder::FindOptions options) const {
LOG_DEBUG(logger, DBG_TRACE_DATA,
DATASRC_MEM_WILDCARD_CANCEL).arg(name);
return (ResultType(ZoneFinder::NXDOMAIN, NULL,
- ConstRBNodeRRsetPtr()));
+ getClosestNSEC(node_path, options)));
}
// Now the wildcard should be the best match.
const Name wildcard(Name("*").concatenate(
node_path.getAbsoluteName()));
- DomainTree::Result result = domains_.find(wildcard, &node);
+
+ // Clear the node_path so that we don't keep incorrect (NSEC)
+ // context
+ node_path.clear();
+ DomainTree::Result result(domains_.find(wildcard, &node,
+ node_path));
// Otherwise, why would the domain_flag::WILD be there if
// there was no wildcard under it?
assert(result == DomainTree::EXACTMATCH);
@@ -413,7 +480,8 @@ ZoneData::findNode(const Name& name, ZoneFinder::FindOptions options) const {
}
// Nothing really matched.
LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_NOT_FOUND).arg(name);
- return (ResultType(ZoneFinder::NXDOMAIN, node, state.rrset_));
+ return (ResultType(ZoneFinder::NXDOMAIN, node,
+ getClosestNSEC(node_path, options)));
} else {
// If the name is neither an exact or partial match, it is
// out of bailiwick, which is considered an error.
@@ -1162,6 +1230,10 @@ struct InMemoryZoneFinder::InMemoryZoneFinderImpl {
isc_throw(AddError, "NSEC3PARAM with inconsistent "
"parameters: " << rrset->toText());
}
+ } else if (rrset->getType() == RRType::NSEC()) {
+ // If it is NSEC signed zone, so we put a flag there
+ // (flag is enough)
+ zone_data.nsec_signed_ = true;
}
return (result::SUCCESS);
} else {
@@ -1190,6 +1262,24 @@ struct InMemoryZoneFinder::InMemoryZoneFinderImpl {
}
}
+ // A helper function for the NXRRSET case in find(). If the zone is
+ // NSEC-signed and DNSSEC records are requested, try to find NSEC
+ // on the given node, and return it if found; return NULL for all other
+ // cases.
+ ConstRBNodeRRsetPtr getNSECForNXRRSET(FindOptions options,
+ const DomainNode& node) const
+ {
+ if (zone_data_->nsec_signed_ &&
+ (options & ZoneFinder::FIND_DNSSEC) != 0) {
+ const Domain::const_iterator found =
+ node.getData()->find(RRType::NSEC());
+ if (found != node.getData()->end()) {
+ return (found->second);
+ }
+ }
+ return (ConstRBNodeRRsetPtr());
+ }
+
// 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
@@ -1206,9 +1296,13 @@ struct InMemoryZoneFinder::InMemoryZoneFinderImpl {
if (wild) {
flags = flags | RESULT_WILDCARD;
}
- if ((code == NXRRSET || code == NXDOMAIN || wild) &&
- zone_data_->nsec3_data_) {
- flags = flags | RESULT_NSEC3_SIGNED;
+ if (code == NXRRSET || code == NXDOMAIN || wild) {
+ if (zone_data_->nsec3_data_) {
+ flags = flags | RESULT_NSEC3_SIGNED;
+ }
+ if (zone_data_->nsec_signed_) {
+ flags = flags | RESULT_NSEC_SIGNED;
+ }
}
return (RBNodeResultContext(code, rrset, flags, node));
}
@@ -1223,8 +1317,10 @@ struct InMemoryZoneFinder::InMemoryZoneFinderImpl {
// Get the node. All other cases than an exact match are handled
// in findNode(). We simply construct a result structure and return.
+ RBTreeNodeChain<Domain> node_path; // findNode will fill in this
const ZoneData::FindNodeResult node_result =
- zone_data_->findNode<ZoneData::FindNodeResult>(name, options);
+ zone_data_->findNode<ZoneData::FindNodeResult>(name, node_path,
+ options);
if (node_result.code != SUCCESS) {
return (createFindResult(node_result.code, node_result.rrset));
}
@@ -1240,7 +1336,10 @@ struct InMemoryZoneFinder::InMemoryZoneFinderImpl {
if (node->isEmpty()) {
LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_DOMAIN_EMPTY).
arg(name);
- return (createFindResult(NXRRSET, ConstRBNodeRRsetPtr(), rename));
+ return (createFindResult(NXRRSET,
+ zone_data_->getClosestNSEC(node_path,
+ options),
+ rename));
}
Domain::const_iterator found;
@@ -1296,10 +1395,9 @@ struct InMemoryZoneFinder::InMemoryZoneFinderImpl {
rename));
}
}
- // No exact match or CNAME. Return NXRRSET.
- LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_NXRRSET).arg(type).
- arg(name);
- return (createFindResult(NXRRSET, ConstRBNodeRRsetPtr(), rename));
+ // No exact match or CNAME. Get NSEC if necessary and return NXRRSET.
+ return (createFindResult(NXRRSET, getNSECForNXRRSET(options, *node),
+ rename));
}
};
@@ -1461,6 +1559,7 @@ addAdditional(RBNodeRRset* rrset, ZoneData* zone_data,
{
RdataIteratorPtr rdata_iterator = rrset->getRdataIterator();
bool match_wild = false; // will be true if wildcard match is found
+ RBTreeNodeChain<Domain> node_path; // placeholder for findNode()
for (; !rdata_iterator->isLast(); rdata_iterator->next()) {
// For each domain name that requires additional section processing
// in each RDATA, search the tree for the name and remember it if
@@ -1473,13 +1572,14 @@ addAdditional(RBNodeRRset* rrset, ZoneData* zone_data,
// if the name is not in or below this zone, skip it
const NameComparisonResult::NameRelation reln =
name.compare(zone_data->origin_data_->getName()).getRelation();
- if (reln != NameComparisonResult::SUBDOMAIN &&
- reln != NameComparisonResult::EQUAL) {
+ if (reln != NameComparisonResult::SUBDOMAIN &&
+ reln != NameComparisonResult::EQUAL) {
continue;
}
+ node_path.clear();
const ZoneData::FindMutableNodeResult result =
zone_data->findNode<ZoneData::FindMutableNodeResult>(
- name, ZoneFinder::FIND_GLUE_OK);
+ name, node_path, ZoneFinder::FIND_GLUE_OK);
if (result.code != ZoneFinder::SUCCESS) {
// We are not interested in anything but a successful match.
continue;
@@ -1749,8 +1849,7 @@ public:
{
// Find the first node (origin) and preserve the node chain for future
// searches
- DomainTree::Result result(tree_.find<void*>(origin, &node_, chain_,
- NULL, NULL));
+ DomainTree::Result result(tree_.find(origin, &node_, chain_));
// It can't happen that the origin is not in there
if (result != DomainTree::EXACTMATCH) {
isc_throw(Unexpected,
diff --git a/src/lib/datasrc/memory_datasrc.h b/src/lib/datasrc/memory_datasrc.h
index c687d1b..f4e99a8 100644
--- a/src/lib/datasrc/memory_datasrc.h
+++ b/src/lib/datasrc/memory_datasrc.h
@@ -65,11 +65,7 @@ public:
/// \brief Returns the class of the zone.
virtual isc::dns::RRClass getClass() const;
- /// \brief Looks up an RRset in the zone.
- ///
- /// See documentation in \c Zone.
- ///
- /// It returns NULL pointer in case of NXDOMAIN and NXRRSET.
+ /// \brief Find an RRset in the datasource
virtual ZoneFinderContextPtr find(const isc::dns::Name& name,
const isc::dns::RRType& type,
const FindOptions options =
diff --git a/src/lib/datasrc/rbtree.h b/src/lib/datasrc/rbtree.h
index dbf0591..39646ac 100644
--- a/src/lib/datasrc/rbtree.h
+++ b/src/lib/datasrc/rbtree.h
@@ -237,7 +237,7 @@ private:
/// Return if callback is enabled at the node.
//@}
-private:
+
/// \brief Define rbnode color
enum RBNodeColor {BLACK, RED};
/// This is a factory class method of a special singleton null node.
@@ -263,6 +263,37 @@ private:
/// This method never throws an exception.
const RBNode<T>* successor() const;
+ /// \brief return the next node which is smaller than current node
+ /// in the same subtree
+ ///
+ /// The predecessor for this node is the next smaller node in terms of
+ /// the DNSSEC order relation within the same single subtree.
+ /// Note that it may NOT be the next smaller node in the entire RBTree;
+ /// RBTree is a tree in tree, and the real next node may reside in
+ /// an upper or lower subtree of the subtree where this node belongs.
+ /// For example, if the predecessor node has a sub domain, the real next
+ /// node is the largest node in the sub domain tree.
+ ///
+ /// If this node is the smallest node within the subtree, this method
+ /// returns \c NULL_NODE().
+ ///
+ /// This method never throws an exception.
+ const RBNode<T>* predecessor() const;
+
+ /// \brief private shared implementation of successor and predecessor
+ ///
+ /// As the two mentioned functions are merely mirror images of each other,
+ /// it makes little sense to keep both versions. So this is the body of the
+ /// functions and we call it with the correct pointers.
+ ///
+ /// Not to be called directly, not even by friends.
+ ///
+ /// The overhead of the member pointers should be optimised out, as this
+ /// will probably get completely inlined into predecessor and successor
+ /// methods.
+ const RBNode<T>* abstractSuccessor(RBNode<T>* RBNode<T>::*left,
+ RBNode<T>* RBNode<T>::*right) const;
+
/// \name Data to maintain the rbtree structure.
//@{
RBNode<T>* parent_;
@@ -283,7 +314,7 @@ private:
/// \par Adding down pointer to \c RBNode has two purposes:
/// \li Accelerate the search process, with sub domain tree, it splits the
/// big flat tree into several hierarchy trees.
- /// \li It saves memory useage as it allows storing only relative names,
+ /// \li It saves memory usage as it allows storing only relative names,
/// avoiding storage of the same domain labels multiple times.
RBNode<T>* down_;
@@ -333,30 +364,48 @@ RBNode<T>::~RBNode() {
template <typename T>
const RBNode<T>*
-RBNode<T>::successor() const {
+RBNode<T>::abstractSuccessor(RBNode<T>* RBNode<T>::*left, RBNode<T>*
+ RBNode<T>::*right) const
+{
+ // This function is written as a successor. It becomes predecessor if
+ // the left and right pointers are swapped. So in case of predecessor,
+ // the left pointer points to right and vice versa. Don't get confused
+ // by the idea, just imagine the pointers look into a mirror.
+
const RBNode<T>* current = this;
// If it has right node, the successor is the left-most node of the right
// subtree.
- if (right_ != NULL_NODE()) {
- current = right_;
- while (current->left_ != NULL_NODE()) {
- current = current->left_;
+ if (current->*right != RBNode<T>::NULL_NODE()) {
+ current = current->*right;
+ while (current->*left != RBNode<T>::NULL_NODE()) {
+ current = current->*left;
}
return (current);
}
-
// Otherwise go up until we find the first left branch on our path to
// root. If found, the parent of the branch is the successor.
// Otherwise, we return the null node
const RBNode<T>* parent = current->parent_;
- while (parent != NULL_NODE() && current == parent->right_) {
+ while (parent != RBNode<T>::NULL_NODE() && current == parent->*right) {
current = parent;
parent = parent->parent_;
}
return (parent);
}
+template <typename T>
+const RBNode<T>*
+RBNode<T>::successor() const {
+ return (abstractSuccessor(&RBNode<T>::left_, &RBNode<T>::right_));
+}
+
+template <typename T>
+const RBNode<T>*
+RBNode<T>::predecessor() const {
+ // Swap the left and right pointers for the abstractSuccessor
+ return (abstractSuccessor(&RBNode<T>::right_, &RBNode<T>::left_));
+}
/// \brief RBTreeNodeChain stores detailed information of \c RBTree::find()
/// result.
@@ -364,8 +413,7 @@ RBNode<T>::successor() const {
/// - The \c RBNode that was last compared with the search name, and
/// the comparison result at that point in the form of
/// \c isc::dns::NameComparisonResult.
-/// - A sequence of nodes that forms a path to the found node (which is
-/// not yet implemented).
+/// - A sequence of nodes that forms a path to the found node.
///
/// The comparison result can be used to handle some rare cases such as
/// empty node processing.
@@ -396,7 +444,7 @@ RBNode<T>::successor() const {
template <typename T>
class RBTreeNodeChain {
/// RBTreeNodeChain is initialized by RBTree, only RBTree has
- /// knowledge to manipuate it.
+ /// knowledge to manipulate it.
friend class RBTree<T>;
public:
/// \name Constructors and Assignment Operator.
@@ -498,10 +546,10 @@ public:
private:
// the following private functions check invariants about the internal
// state using assert() instead of exception. The state of a chain
- // can only be modified operations within this file, so if any of the
+ // can only be modified by operations within this file, so if any of the
// assumptions fails it means an internal bug.
- /// \brief return whther node chain has node in it.
+ /// \brief return whether node chain has node in it.
///
/// \exception None
bool isEmpty() const { return (node_count_ == 0); }
@@ -655,7 +703,7 @@ public:
/// By default, nodes that don't have data (see RBNode::isEmpty) are
/// ignored and the result can be NOTFOUND even if there's a node whose
/// name matches. If the \c RBTree is constructed with its
- /// \c returnEmptyNode parameter being \c true, an empty node will also
+ /// \c returnEmptyNode parameter being \c true, empty nodes will also
/// be match candidates.
///
/// \note Even when \c returnEmptyNode is \c true, not all empty nodes
@@ -673,7 +721,7 @@ public:
/// if it throws, the exception will be propagated to the caller.
///
/// The \c name parameter says what should be found. The node parameter
- /// is output only and in case of EXACTMATCH and PARTIALMATCH, it is set
+ /// is output-only, and in case of EXACTMATCH or PARTIALMATCH, it is set
/// to a pointer to the found node.
///
/// They return:
@@ -710,6 +758,30 @@ public:
return (ret);
}
+ /// \brief Simple find, with node_path tracking
+ ///
+ /// Acts as described in the \ref find section.
+ Result find(const isc::dns::Name& name, RBNode<T>** node,
+ RBTreeNodeChain<T>& node_path) const
+ {
+ return (find<void*>(name, node, node_path, NULL, NULL));
+ }
+
+ /// \brief Simple find returning immutable node, with node_path tracking
+ ///
+ /// Acts as described in the \ref find section, but returns immutable node
+ /// pointer.
+ Result find(const isc::dns::Name& name, const RBNode<T>** node,
+ RBTreeNodeChain<T>& node_path) const
+ {
+ RBNode<T> *target_node = NULL;
+ Result ret = (find<void*>(name, &target_node, node_path, NULL, NULL));
+ if (ret != NOTFOUND) {
+ *node = target_node;
+ }
+ return (ret);
+ }
+
/// \brief Find with callback and node chain.
/// \anchor callback
///
@@ -720,13 +792,16 @@ public:
///
/// This version of \c find() calls the callback whenever traversing (on
/// the way from root down the tree) a marked node on the way down through
- /// the domain namespace (see \c RBNode::enableCallback and related
- /// functions).
+ /// the domain namespace (see \c RBNode::FLAG_CALLBACK).
///
/// If you return true from the callback, the search is stopped and a
/// PARTIALMATCH is returned with the given node. Note that this node
/// doesn't really need to be the one with longest possible match.
///
+ /// The callback is not called for the node which matches exactly
+ /// (EXACTMATCH is returned). This is typically the last node in the
+ /// traversal during a successful search.
+ ///
/// This callback mechanism was designed with zone cut (delegation)
/// processing in mind. The marked nodes would be the ones at delegation
/// points. It is not expected that any other applications would need
@@ -741,38 +816,36 @@ public:
/// which is an object of class \c RBTreeNodeChain.
/// The passed parameter must be empty.
///
- /// \note The rest of the description isn't yet implemented. It will be
- /// handled in Trac ticket #517.
- ///
- /// On success, the node sequence stoed in \c node_path will contain all
+ /// On success, the node sequence stored in \c node_path will contain all
/// the ancestor nodes from the found node towards the root.
/// For example, if we look for o.w.y.d.e.f in the example \ref diagram,
/// \c node_path will contain w.y and d.e.f; the \c top() node of the
- /// chain will be o, w.f and d.e.f will be stored below it.
+ /// chain will be o, w.y and d.e.f will be stored below it.
///
/// This feature can be used to get the absolute name for a node;
/// to do so, we need to travel upside from the node toward the root,
/// concatenating all ancestor names. With the current implementation
/// it's not possible without a node chain, because there is a no pointer
/// from the root of a subtree to the parent subtree (this may change
- /// in a future version). A node chain can also be used to find the next
- /// node of a given node in the entire RBTree; the \c nextNode() method
- /// takes a node chain as a parameter.
+ /// in a future version). A node chain can also be used to find the
+ /// next and previous nodes of a given node in the entire RBTree;
+ /// the \c nextNode() and \c previousNode() methods take a node
+ /// chain as a parameter.
///
- /// \exception isc::BadValue node_path is not empty (not yet implemented).
+ /// \exception isc::BadValue node_path is not empty.
///
/// \param name Target to be found
/// \param node On success (either \c EXACTMATCH or \c PARTIALMATCH)
/// it will store a pointer to the matching node
/// \param node_path Other search details will be stored (see the
/// description)
- /// \param callback If non \c NULL, a call back function to be called
- /// at marked nodes (see above).
+ /// \param callback If non- \c NULL, a call back function to be called
+ /// at marked nodes (see the description).
/// \param callback_arg A caller supplied argument to be passed to
/// \c callback.
///
- /// \return As described above, but in case of callback returning true,
- /// it returns immediately with the current node.
+ /// \return As in the description, but in case of callback returning
+ /// \c true, it returns immediately with the current node.
template <typename CBARG>
Result find(const isc::dns::Name& name,
RBNode<T>** node,
@@ -826,6 +899,30 @@ public:
/// the largest, \c NULL will be returned.
const RBNode<T>* nextNode(RBTreeNodeChain<T>& node_path) const;
+ /// \brief return the next smaller node in DNSSEC order from a node
+ /// searched by RBTree::find().
+ ///
+ /// This acts similarly to \c nextNode(), but it walks in the other
+ /// direction. But unlike \c nextNode(), this can start even if the
+ /// node requested by \c find() was not found. In that case, it will
+ /// identify the node that is previous to the queried name.
+ ///
+ /// \note \c previousNode() will iterate over all the nodes in RBTree
+ /// including empty nodes. If empty node isn't desired, it's easy to add
+ /// logic to check return node and keep invoking \c previousNode() until the
+ /// non-empty node is retrieved.
+ ///
+ /// \exception isc::BadValue node_path is empty.
+ ///
+ /// \param node_path A node chain that stores all the nodes along the path
+ /// from root to node and the result of \c find(). This will get modified.
+ /// You should not use the node_path again except for repetitive calls
+ /// of this method.
+ ///
+ /// \return An \c RBNode that is next smaller than \c node; if \c node is
+ /// the smallest, \c NULL will be returned.
+ const RBNode<T>* previousNode(RBTreeNodeChain<T>& node_path) const;
+
/// \brief Get the total number of nodes in the tree
///
/// It includes nodes internally created as a result of adding a domain
@@ -848,8 +945,8 @@ public:
//@{
/// \brief Insert the domain name into the tree.
///
- /// It either finds an already existing node of the given name or inserts
- /// a new one, if none exists yet. In any case, the inserted_node parameter
+ /// It either finds an already existing node of the given name, or inserts
+ /// a new one if none exists yet. In any case, the \c inserted_node parameter
/// is set to point to that node. You can fill data into it or modify it.
/// So, if you don't know if a node exists or not and you need to modify
/// it, just call insert and act by the result.
@@ -1059,15 +1156,7 @@ RBTree<T>::nextNode(RBTreeNodeChain<T>& node_path) const {
return (left_most);
}
- // node_path go to up level
- node_path.pop();
- // otherwise found the successor node in current level
- const RBNode<T>* successor = node->successor();
- if (successor != NULLNODE) {
- node_path.push(successor);
- return (successor);
- }
-
+ // try to find a successor.
// if no successor found move to up level, the next successor
// is the successor of up node in the up level tree, if
// up node doesn't have successor we gonna keep moving to up
@@ -1084,6 +1173,143 @@ RBTree<T>::nextNode(RBTreeNodeChain<T>& node_path) const {
return (NULL);
}
+template <typename T>
+const RBNode<T>*
+RBTree<T>::previousNode(RBTreeNodeChain<T>& node_path) const {
+ if (getNodeCount() == 0) {
+ // Special case for empty trees. It would look every time like
+ // we didn't search, because the last compared is empty. This is
+ // a slight hack and not perfect, but this is better than throwing
+ // on empty tree. And we probably won't meet an empty tree in practice
+ // anyway.
+ return (NULL);
+ }
+ if (node_path.last_compared_ == NULL) {
+ isc_throw(isc::BadValue,
+ "RBTree::previousNode() called before find()");
+ }
+
+ // If the relation isn't EQUAL, it means the find was called previously
+ // and didn't find the exact node. Therefore we need to locate the place
+ // to start iterating the chain of domains.
+ //
+ // The logic here is not too complex, we just need to take care to handle
+ // all the cases and decide where to go from there.
+ switch (node_path.last_comparison_.getRelation()) {
+ case dns::NameComparisonResult::COMMONANCESTOR:
+ // We compared with a leaf in the tree and wanted to go to one of
+ // the children. But the child was not there. It now depends on the
+ // direction in which we wanted to go.
+ if (node_path.last_comparison_.getOrder() < 0) {
+ // We wanted to go left. So the one we compared with is
+ // the one higher than we wanted. If we just put it into
+ // the node_path, then the following algorithm below will find
+ // the smaller one.
+ //
+ // This is exactly the same as with superdomain below.
+ // Therefore, we just fall through to the next case.
+ } else {
+ // We wanted to go right. That means we want to output the
+ // one which is the largest in the tree defined by the
+ // compared one (it is either the compared one, or some
+ // subdomain of it). There probably is not an easy trick
+ // for this, so we just find the correct place.
+ const RBNode<T>* current(node_path.last_compared_);
+ while (current != NULLNODE) {
+ node_path.push(current);
+ // Go a level down and as much right there as possible
+ current = current->down_;
+ while (current->right_ != NULLNODE) {
+ // A small trick. The current may be NULLNODE, but
+ // such node has the right_ pointer and it is equal
+ // to NULLNODE.
+ current = current->right_;
+ }
+ }
+ // Now, the one on top of the path is the one we want. We
+ // return it now and leave it there, so we can search for
+ // previous of it the next time we'are called.
+ node_path.last_comparison_ =
+ dns::NameComparisonResult(0, 0,
+ dns::NameComparisonResult::EQUAL);
+ return (node_path.top());
+ }
+ // No break; here - we want to fall through. See above.
+ case dns::NameComparisonResult::SUPERDOMAIN:
+ // This is the case there's a "compressed" node and we looked for
+ // only part of it. The node itself is larger than we wanted, but
+ // if we put it to the node_path and then go one step left from it,
+ // we get the correct result.
+ node_path.push(node_path.last_compared_);
+ // Correct the comparison result, so we won't trigger this case
+ // next time previousNode is called. We already located the correct
+ // place to start. The value is partly nonsense, but that doesn't
+ // matter any more.
+ node_path.last_comparison_ =
+ dns::NameComparisonResult(0, 0,
+ dns::NameComparisonResult::EQUAL);
+ break;
+ case dns::NameComparisonResult::SUBDOMAIN:
+ // A subdomain means we returned the one above the searched one
+ // already and it is on top of the stack. This is was smaller
+ // than the one already, but we want to return yet smaller one.
+ // So we act as if it was EQUAL.
+ break;
+ case dns::NameComparisonResult::EQUAL:
+ // The find gave us an exact match or the previousNode was called
+ // already, which located the exact node. The rest of the function
+ // goes one domain left and returns it for us.
+ break;
+ }
+
+ // So, the node_path now contains the path to a node we want previous for.
+ // We just need to go one step left.
+
+ if (node_path.isEmpty()) {
+ // We got past the first one. So, we're returning NULL from
+ // now on.
+ return (NULL);
+ }
+
+ const RBNode<T>* node(node_path.top());
+
+ // Try going left in this tree
+ node = node->predecessor();
+ if (node == NULLNODE) {
+ // We are the smallest ones in this tree. We go one level
+ // up. That one is the smaller one than us.
+
+ node_path.pop();
+ if (node_path.isEmpty()) {
+ // We're past the first one
+ return (NULL);
+ } else {
+ return (node_path.top());
+ }
+ }
+
+ // Exchange the node at the top of the path, as we move horizontaly
+ // through the domain tree
+ node_path.pop();
+ node_path.push(node);
+
+ // Try going as deep as possible, keeping on the right side of the trees
+ while (node->down_ != NULLNODE) {
+ // Move to the tree below
+ node = node->down_;
+ // And get as much to the right of the tree as possible
+ while (node->right_ != NULLNODE) {
+ node = node->right_;
+ }
+ // Now, we found the right-most node in the sub-tree, we need to
+ // include it in the path
+ node_path.push(node);
+ }
+
+ // Now, if the current node has no down_ pointer any more, it's the
+ // correct one.
+ return (node);
+}
template <typename T>
typename RBTree<T>::Result
diff --git a/src/lib/datasrc/sqlite3_accessor.cc b/src/lib/datasrc/sqlite3_accessor.cc
index 11f364e..ba21de8 100644
--- a/src/lib/datasrc/sqlite3_accessor.cc
+++ b/src/lib/datasrc/sqlite3_accessor.cc
@@ -66,14 +66,16 @@ enum StatementID {
ITERATE = 9,
FIND_PREVIOUS = 10,
ADD_RECORD_DIFF = 11,
- GET_RECORD_DIFF = 12, // This is temporary for testing "add diff"
- LOW_DIFF_ID = 13,
- HIGH_DIFF_ID = 14,
- DIFF_RECS = 15,
- NSEC3 = 16,
- NSEC3_PREVIOUS = 17,
- NSEC3_LAST = 18,
- NUM_STATEMENTS = 19
+ LOW_DIFF_ID = 12,
+ HIGH_DIFF_ID = 13,
+ DIFF_RECS = 14,
+ NSEC3 = 15,
+ NSEC3_PREVIOUS = 16,
+ NSEC3_LAST = 17,
+ ADD_NSEC3_RECORD = 18,
+ DEL_ZONE_NSEC3_RECORDS = 19,
+ DEL_NSEC3_RECORD = 20,
+ NUM_STATEMENTS = 21
};
const char* const text_statements[NUM_STATEMENTS] = {
@@ -93,8 +95,19 @@ const char* const text_statements[NUM_STATEMENTS] = {
"VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7)",
"DELETE FROM records WHERE zone_id=?1 AND name=?2 " // DEL_RECORD
"AND rdtype=?3 AND rdata=?4",
- "SELECT rdtype, ttl, sigtype, rdata, name FROM records " // ITERATE
- "WHERE zone_id = ?1 ORDER BY rname, rdtype",
+ // The following iterates the whole zone. As the NSEC3 records
+ // (and corresponding RRSIGs) live in separate table, we need to
+ // take both of them. As the RRSIGs are for NSEC3s in the other
+ // table, we can easily hardcode the sigtype.
+ //
+ // The extra column is so we can order it by rname. This is to
+ // preserve the previous order, mostly for tests.
+ // TODO: Is it possible to get rid of the ordering?
+ "SELECT rdtype, ttl, sigtype, rdata, name, rname FROM records " // ITERATE
+ "WHERE zone_id = ?1 "
+ "UNION "
+ "SELECT rdtype, ttl, \"NSEC3\", rdata, owner, owner FROM nsec3 "
+ "WHERE zone_id = ?1 ORDER by rname, rdtype",
/*
* This one looks for previous name with NSEC record. It is done by
* using the reversed name. The NSEC is checked because we need to
@@ -106,8 +119,6 @@ const char* const text_statements[NUM_STATEMENTS] = {
"INSERT INTO diffs " // ADD_RECORD_DIFF
"(zone_id, version, operation, name, rrtype, ttl, rdata) "
"VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7)",
- "SELECT name, rrtype, ttl, rdata, version, operation " // GET_RECORD_DIFF
- "FROM diffs WHERE zone_id = ?1 ORDER BY id, operation",
// Two statements to select the lowest ID and highest ID in a set of
// differences.
@@ -124,19 +135,27 @@ const char* const text_statements[NUM_STATEMENTS] = {
"WHERE zone_id=?1 AND id>=?2 and id<=?3 "
"ORDER BY id ASC",
- // Query to get the NSEC3 records
+ // NSEC3: Query to get the NSEC3 records
//
// The "1" in SELECT is for positioning the rdata column to the
// expected position, so we can reuse the same code as for other
// lookups.
"SELECT rdtype, ttl, 1, rdata FROM nsec3 WHERE zone_id=?1 AND "
"hash=?2",
- // For getting the previous NSEC3 hash
+ // NSEC3_PREVIOUS: For getting the previous NSEC3 hash
"SELECT DISTINCT hash FROM nsec3 WHERE zone_id=?1 AND hash < ?2 "
"ORDER BY hash DESC LIMIT 1",
- // And for wrap-around
+ // NSEC3_LAST: And for wrap-around
"SELECT DISTINCT hash FROM nsec3 WHERE zone_id=?1 "
"ORDER BY hash DESC LIMIT 1",
+ // ADD_NSEC3_RECORD: Add NSEC3-related (NSEC3 or NSEC3-covering RRSIG) RR
+ "INSERT INTO nsec3 (zone_id, hash, owner, ttl, rdtype, rdata) "
+ "VALUES (?1, ?2, ?3, ?4, ?5, ?6)",
+ // DEL_ZONE_NSEC3_RECORDS: delete all NSEC3-related records from the zone
+ "DELETE FROM nsec3 WHERE zone_id=?1",
+ // DEL_NSEC3_RECORD: delete specified NSEC3-related records
+ "DELETE FROM nsec3 WHERE zone_id=?1 AND hash=?2 "
+ "AND rdtype=?3 AND rdata=?4"
};
struct SQLite3Parameters {
@@ -185,6 +204,7 @@ struct SQLite3Parameters {
bool in_transaction; // whether or not a transaction has been started
bool updating_zone; // whether or not updating the zone
int updated_zone_id; // valid only when in_transaction is true
+ string updated_zone_origin_; // ditto, and only needed to handle NSEC3s
private:
// statements_ are private and must be accessed via getStatement() outside
// of this structure.
@@ -199,6 +219,10 @@ private:
// statement, which is completed with a single "step" (normally within a
// single call to an SQLite3Database method). In particular, it cannot be
// used for "SELECT" variants, which generally expect multiple matching rows.
+//
+// The bindXXX methods are straightforward wrappers for the corresponding
+// sqlite3_bind_xxx functions that make bindings with the given parameters
+// on the statement maintained in this class.
class StatementProcessor {
public:
// desc will be used on failure in the what() message of the resulting
@@ -215,6 +239,33 @@ public:
sqlite3_reset(stmt_);
}
+ void bindInt(int index, int val) {
+ if (sqlite3_bind_int(stmt_, index, val) != SQLITE_OK) {
+ isc_throw(DataSourceError,
+ "failed to bind SQLite3 parameter: " <<
+ sqlite3_errmsg(dbparameters_.db_));
+ }
+ }
+
+ void bindInt64(int index, sqlite3_int64 val) {
+ if (sqlite3_bind_int64(stmt_, index, val) != SQLITE_OK) {
+ isc_throw(DataSourceError,
+ "failed to bind SQLite3 parameter: " <<
+ sqlite3_errmsg(dbparameters_.db_));
+ }
+ }
+
+ // For simplicity, we assume val is a NULL-terminated string, and the
+ // entire non NUL characters are to be bound. The destructor parameter
+ // is normally either SQLITE_TRANSIENT or SQLITE_STATIC.
+ void bindText(int index, const char* val, void(*destructor)(void*)) {
+ if (sqlite3_bind_text(stmt_, index, val, -1, destructor)
+ != SQLITE_OK) {
+ isc_throw(DataSourceError, "failed to bind SQLite3 parameter: " <<
+ sqlite3_errmsg(dbparameters_.db_));
+ }
+ }
+
void exec() {
if (sqlite3_step(stmt_) != SQLITE_DONE) {
sqlite3_reset(stmt_);
@@ -795,7 +846,7 @@ public:
///
/// \return bool true if data is returned, false if not.
///
- /// \exceptions any Varied
+ /// \exception any Varied
bool getNext(std::string (&data)[COLUMN_COUNT]) {
if (last_status_ != SQLITE_DONE) {
@@ -1010,19 +1061,22 @@ SQLite3Accessor::startUpdateZone(const string& zone_name, const bool replace) {
"start an SQLite3 update transaction").exec();
if (replace) {
+ // First, clear all current data from tables.
+ typedef pair<StatementID, const char* const> StatementSpec;
+ const StatementSpec delzone_stmts[] =
+ { StatementSpec(DEL_ZONE_RECORDS, "delete zone records"),
+ StatementSpec(DEL_ZONE_NSEC3_RECORDS,
+ "delete zone NSEC3 records") };
try {
- StatementProcessor delzone_exec(*dbparameters_, DEL_ZONE_RECORDS,
- "delete zone records");
-
- sqlite3_stmt* stmt = dbparameters_->getStatement(DEL_ZONE_RECORDS);
- sqlite3_clear_bindings(stmt);
- if (sqlite3_bind_int(stmt, 1, zone_info.second) != SQLITE_OK) {
- isc_throw(DataSourceError,
- "failed to bind SQLite3 parameter: " <<
- sqlite3_errmsg(dbparameters_->db_));
+ for (size_t i = 0;
+ i < sizeof(delzone_stmts) / sizeof(delzone_stmts[0]);
+ ++i) {
+ StatementProcessor delzone_proc(*dbparameters_,
+ delzone_stmts[i].first,
+ delzone_stmts[i].second);
+ delzone_proc.bindInt(1, zone_info.second);
+ delzone_proc.exec();
}
-
- delzone_exec.exec();
} catch (const DataSourceError&) {
// Once we start a transaction, if something unexpected happens
// we need to rollback the transaction so that a subsequent update
@@ -1036,6 +1090,7 @@ SQLite3Accessor::startUpdateZone(const string& zone_name, const bool replace) {
dbparameters_->in_transaction = true;
dbparameters_->updating_zone = true;
dbparameters_->updated_zone_id = zone_info.second;
+ dbparameters_->updated_zone_origin_ = zone_name;
return (zone_info);
}
@@ -1062,7 +1117,9 @@ SQLite3Accessor::commit() {
StatementProcessor(*dbparameters_, COMMIT,
"commit an SQLite3 transaction").exec();
dbparameters_->in_transaction = false;
+ dbparameters_->updating_zone = false;
dbparameters_->updated_zone_id = -1;
+ dbparameters_->updated_zone_origin_.clear();
}
void
@@ -1075,7 +1132,9 @@ SQLite3Accessor::rollback() {
StatementProcessor(*dbparameters_, ROLLBACK,
"rollback an SQLite3 transaction").exec();
dbparameters_->in_transaction = false;
+ dbparameters_->updating_zone = false;
dbparameters_->updated_zone_id = -1;
+ dbparameters_->updated_zone_origin_.clear();
}
namespace {
@@ -1085,29 +1144,19 @@ void
doUpdate(SQLite3Parameters& dbparams, StatementID stmt_id,
COLUMNS_TYPE update_params, const char* exec_desc)
{
- sqlite3_stmt* const stmt = dbparams.getStatement(stmt_id);
- StatementProcessor executer(dbparams, stmt_id, exec_desc);
+ StatementProcessor proc(dbparams, stmt_id, exec_desc);
int param_id = 0;
- if (sqlite3_bind_int(stmt, ++param_id, dbparams.updated_zone_id)
- != SQLITE_OK) {
- isc_throw(DataSourceError, "failed to bind SQLite3 parameter: " <<
- sqlite3_errmsg(dbparams.db_));
- }
+ proc.bindInt(++param_id, dbparams.updated_zone_id);
const size_t column_count =
sizeof(update_params) / sizeof(update_params[0]);
for (int i = 0; i < column_count; ++i) {
// The old sqlite3 data source API assumes NULL for an empty column.
// We need to provide compatibility at least for now.
- if (sqlite3_bind_text(stmt, ++param_id,
- update_params[i].empty() ? NULL :
- update_params[i].c_str(),
- -1, SQLITE_TRANSIENT) != SQLITE_OK) {
- isc_throw(DataSourceError, "failed to bind SQLite3 parameter: " <<
- sqlite3_errmsg(dbparams.db_));
- }
+ proc.bindText(++param_id, update_params[i].empty() ? NULL :
+ update_params[i].c_str(), SQLITE_TRANSIENT);
}
- executer.exec();
+ proc.exec();
}
}
@@ -1117,21 +1166,58 @@ SQLite3Accessor::addRecordToZone(const string (&columns)[ADD_COLUMN_COUNT]) {
isc_throw(DataSourceError, "adding record to SQLite3 "
"data source without transaction");
}
- doUpdate<const string (&)[DatabaseAccessor::ADD_COLUMN_COUNT]>(
+ doUpdate<const string (&)[ADD_COLUMN_COUNT]>(
*dbparameters_, ADD_RECORD, columns, "add record to zone");
}
void
+SQLite3Accessor::addNSEC3RecordToZone(
+ const string (&columns)[ADD_NSEC3_COLUMN_COUNT])
+{
+ if (!dbparameters_->updating_zone) {
+ isc_throw(DataSourceError, "adding NSEC3-related record to SQLite3 "
+ "data source without transaction");
+ }
+
+ // XXX: the current implementation of SQLite3 schema requires the 'owner'
+ // column, and the current implementation of getAllRecords() relies on it,
+ // while the addNSEC3RecordToZone interface doesn't provide it explicitly.
+ // We should revisit it at the design level, but for now we internally
+ // convert the given parameter to satisfy the internal requirements.
+ const string sqlite3_columns[ADD_NSEC3_COLUMN_COUNT + 1] =
+ { columns[ADD_NSEC3_HASH],
+ columns[ADD_NSEC3_HASH] + "." + dbparameters_->updated_zone_origin_,
+ columns[ADD_NSEC3_TTL],
+ columns[ADD_NSEC3_TYPE], columns[ADD_NSEC3_RDATA] };
+ doUpdate<const string (&)[ADD_NSEC3_COLUMN_COUNT + 1]>(
+ *dbparameters_, ADD_NSEC3_RECORD, sqlite3_columns,
+ "add NSEC3 record to zone");
+}
+
+void
SQLite3Accessor::deleteRecordInZone(const string (¶ms)[DEL_PARAM_COUNT]) {
if (!dbparameters_->updating_zone) {
isc_throw(DataSourceError, "deleting record in SQLite3 "
"data source without transaction");
}
- doUpdate<const string (&)[DatabaseAccessor::DEL_PARAM_COUNT]>(
+ doUpdate<const string (&)[DEL_PARAM_COUNT]>(
*dbparameters_, DEL_RECORD, params, "delete record from zone");
}
void
+SQLite3Accessor::deleteNSEC3RecordInZone(
+ const string (¶ms)[DEL_PARAM_COUNT])
+{
+ if (!dbparameters_->updating_zone) {
+ isc_throw(DataSourceError, "deleting NSEC3-related record in SQLite3 "
+ "data source without transaction");
+ }
+ doUpdate<const string (&)[DEL_PARAM_COUNT]>(
+ *dbparameters_, DEL_NSEC3_RECORD, params,
+ "delete NSEC3 record from zone");
+}
+
+void
SQLite3Accessor::addRecordDiff(int zone_id, uint32_t serial,
DiffOperation operation,
const std::string (¶ms)[DIFF_PARAM_COUNT])
@@ -1146,33 +1232,16 @@ SQLite3Accessor::addRecordDiff(int zone_id, uint32_t serial,
<< dbparameters_->updated_zone_id);
}
- sqlite3_stmt* const stmt = dbparameters_->getStatement(ADD_RECORD_DIFF);
- StatementProcessor executer(*dbparameters_, ADD_RECORD_DIFF,
- "add record diff");
+ StatementProcessor proc(*dbparameters_, ADD_RECORD_DIFF,
+ "add record diff");
int param_id = 0;
- if (sqlite3_bind_int(stmt, ++param_id, zone_id)
- != SQLITE_OK) {
- isc_throw(DataSourceError, "failed to bind SQLite3 parameter: " <<
- sqlite3_errmsg(dbparameters_->db_));
- }
- if (sqlite3_bind_int64(stmt, ++param_id, serial)
- != SQLITE_OK) {
- isc_throw(DataSourceError, "failed to bind SQLite3 parameter: " <<
- sqlite3_errmsg(dbparameters_->db_));
- }
- if (sqlite3_bind_int(stmt, ++param_id, operation)
- != SQLITE_OK) {
- isc_throw(DataSourceError, "failed to bind SQLite3 parameter: " <<
- sqlite3_errmsg(dbparameters_->db_));
- }
+ proc.bindInt(++param_id, zone_id);
+ proc.bindInt64(++param_id, serial);
+ proc.bindInt(++param_id, operation);
for (int i = 0; i < DIFF_PARAM_COUNT; ++i) {
- if (sqlite3_bind_text(stmt, ++param_id, params[i].c_str(),
- -1, SQLITE_TRANSIENT) != SQLITE_OK) {
- isc_throw(DataSourceError, "failed to bind SQLite3 parameter: " <<
- sqlite3_errmsg(dbparameters_->db_));
- }
+ proc.bindText(++param_id, params[i].c_str(), SQLITE_TRANSIENT);
}
- executer.exec();
+ proc.exec();
}
std::string
diff --git a/src/lib/datasrc/sqlite3_accessor.h b/src/lib/datasrc/sqlite3_accessor.h
index 9f3b60e..3e44d5b 100644
--- a/src/lib/datasrc/sqlite3_accessor.h
+++ b/src/lib/datasrc/sqlite3_accessor.h
@@ -214,9 +214,15 @@ public:
virtual void addRecordToZone(
const std::string (&columns)[ADD_COLUMN_COUNT]);
+ virtual void addNSEC3RecordToZone(
+ const std::string (&columns)[ADD_NSEC3_COLUMN_COUNT]);
+
virtual void deleteRecordInZone(
const std::string (¶ms)[DEL_PARAM_COUNT]);
+ virtual void deleteNSEC3RecordInZone(
+ const std::string (¶ms)[DEL_PARAM_COUNT]);
+
/// This derived version of the method prepares an SQLite3 statement
/// for adding the diff first time it's called, and if it fails throws
// an \c SQLite3Error exception.
diff --git a/src/lib/datasrc/tests/database_unittest.cc b/src/lib/datasrc/tests/database_unittest.cc
index c18cfad..a0a9ca8 100644
--- a/src/lib/datasrc/tests/database_unittest.cc
+++ b/src/lib/datasrc/tests/database_unittest.cc
@@ -14,15 +14,9 @@
#include "faked_nsec3.h"
-#include <stdlib.h>
-
-#include <boost/shared_ptr.hpp>
-#include <boost/lexical_cast.hpp>
-
-#include <gtest/gtest.h>
-
#include <exceptions/exceptions.h>
+#include <dns/masterload.h>
#include <dns/name.h>
#include <dns/rrttl.h>
#include <dns/rrset.h>
@@ -37,6 +31,13 @@
#include <testutils/dnsmessage_test.h>
+#include <gtest/gtest.h>
+
+#include <boost/bind.hpp>
+#include <boost/shared_ptr.hpp>
+#include <boost/lexical_cast.hpp>
+
+#include <cstdlib>
#include <map>
#include <vector>
@@ -47,6 +48,7 @@ using namespace std;
using boost::dynamic_pointer_cast;
using boost::lexical_cast;
using namespace isc::dns;
+using namespace isc::testutils;
using namespace isc::datasrc::test;
namespace {
@@ -212,6 +214,15 @@ const char* const TEST_RECORDS[][5] = {
{NULL, NULL, NULL, NULL, NULL},
};
+// NSEC3PARAM at the zone origin and its RRSIG. These will be added
+// separately for some NSEC3 related tests.
+const char* TEST_NSEC3PARAM_RECORDS[][5] = {
+ {"example.org.", "NSEC3PARAM", "3600", "", "1 0 12 aabbccdd"},
+ {"example.org.", "RRSIG", "3600", "", "NSEC3PARAM 5 3 3600 20000101000000 "
+ "20000201000000 12345 example.org. FAKEFAKEFAKE"},
+ {NULL, NULL, NULL, NULL, NULL}
+};
+
// FIXME: Taken from a different test. Fill with proper data when creating a test.
const char* TEST_NSEC3_RECORDS[][5] = {
{apex_hash, "NSEC3", "300", "", "1 1 12 AABBCCDD 2T7B4G4VSA5SMI47K61MV5BV1A22BOJR A RRSIG"},
@@ -261,7 +272,10 @@ public:
virtual void commit() {}
virtual void rollback() {}
virtual void addRecordToZone(const string (&)[ADD_COLUMN_COUNT]) {}
+ virtual void addNSEC3RecordToZone(const string (&)[ADD_NSEC3_COLUMN_COUNT])
+ {}
virtual void deleteRecordInZone(const string (&)[DEL_PARAM_COUNT]) {}
+ virtual void deleteNSEC3RecordInZone(const string (&)[DEL_PARAM_COUNT]) {}
virtual void addRecordDiff(int, uint32_t, DiffOperation,
const std::string (&)[DIFF_PARAM_COUNT]) {}
@@ -374,6 +388,8 @@ public:
MockAccessor() : rollbacked_(false), did_transaction_(false) {
readonly_records_ = &readonly_records_master_;
update_records_ = &update_records_master_;
+ nsec3_namespace_ = &nsec3_namespace_master_;
+ update_nsec3_namespace_ = &update_nsec3_namespace_master_;
empty_records_ = &empty_records_master_;
journal_entries_ = &journal_entries_master_;
fillData();
@@ -383,6 +399,9 @@ public:
boost::shared_ptr<MockAccessor> cloned_accessor(new MockAccessor());
cloned_accessor->readonly_records_ = &readonly_records_master_;
cloned_accessor->update_records_ = &update_records_master_;
+ cloned_accessor->nsec3_namespace_ = &nsec3_namespace_master_;
+ cloned_accessor->update_nsec3_namespace_ =
+ &update_nsec3_namespace_master_;
cloned_accessor->empty_records_ = &empty_records_master_;
cloned_accessor->journal_entries_ = &journal_entries_master_;
latest_clone_ = cloned_accessor;
@@ -649,8 +668,8 @@ public:
virtual IteratorContextPtr getNSEC3Records(const std::string& hash,
int) const
{
- Domains::const_iterator it(nsec3_namespace_.find(hash));
- if (it == nsec3_namespace_.end()) {
+ Domains::const_iterator it(nsec3_namespace_->find(hash));
+ if (it == nsec3_namespace_->end()) {
return (IteratorContextPtr(new EmptyIteratorContext()));
} else {
return (IteratorContextPtr(new DomainIterator(it->second)));
@@ -670,8 +689,10 @@ public:
// original.
if (replace) {
update_records_->clear();
+ update_nsec3_namespace_->clear();
} else {
*update_records_ = *readonly_records_;
+ *update_nsec3_namespace_ = nsec3_namespace_master_;
}
if (zone_name == "bad.example.org.") {
@@ -684,7 +705,9 @@ public:
}
virtual void commit() {
*readonly_records_ = *update_records_;
+ *nsec3_namespace_ = *update_nsec3_namespace_;
}
+
virtual void rollback() {
// Special hook: if something with a name of "throw.example.org"
// has been added, trigger an imaginary unexpected event with an
@@ -695,27 +718,54 @@ public:
rollbacked_ = true;
}
- virtual void addRecordToZone(const string (&columns)[ADD_COLUMN_COUNT]) {
+
+private:
+ // Common subroutine for addRecordToZone and addNSEC3RecordToZone.
+ void addRecord(Domains& domains,
+ const string (&columns)[ADD_COLUMN_COUNT])
+ {
// Copy the current value to cur_name. If it doesn't exist,
// operator[] will create a new one.
- cur_name_ = (*update_records_)[columns[DatabaseAccessor::ADD_NAME]];
+ cur_name_ = domains[columns[ADD_NAME]];
vector<string> record_columns;
- record_columns.push_back(columns[DatabaseAccessor::ADD_TYPE]);
- record_columns.push_back(columns[DatabaseAccessor::ADD_TTL]);
- record_columns.push_back(columns[DatabaseAccessor::ADD_SIGTYPE]);
- record_columns.push_back(columns[DatabaseAccessor::ADD_RDATA]);
- record_columns.push_back(columns[DatabaseAccessor::ADD_NAME]);
+ record_columns.push_back(columns[ADD_TYPE]);
+ record_columns.push_back(columns[ADD_TTL]);
+ record_columns.push_back(columns[ADD_SIGTYPE]);
+ record_columns.push_back(columns[ADD_RDATA]);
+ record_columns.push_back(columns[ADD_NAME]);
// copy back the added entry
cur_name_.push_back(record_columns);
- (*update_records_)[columns[DatabaseAccessor::ADD_NAME]] = cur_name_;
+ domains[columns[DatabaseAccessor::ADD_NAME]] = cur_name_;
// remember this one so that test cases can check it.
copy(columns, columns + DatabaseAccessor::ADD_COLUMN_COUNT,
columns_lastadded_);
}
+public:
+ virtual void addRecordToZone(const string (&columns)[ADD_COLUMN_COUNT]) {
+ addRecord(*update_records_, columns);
+ }
+
+ virtual void addNSEC3RecordToZone(
+ const string (&columns)[ADD_NSEC3_COLUMN_COUNT])
+ {
+ // Convert the NSEC3 parameters in the normal (non NSEC3) style so
+ // we can share the merge code, and then update using addRecord().
+ string normal_columns[ADD_COLUMN_COUNT];
+
+ normal_columns[ADD_TYPE] = columns[ADD_NSEC3_TYPE];
+ normal_columns[ADD_TTL] = columns[ADD_NSEC3_TTL];
+ normal_columns[ADD_SIGTYPE] = "";
+ normal_columns[ADD_RDATA] = columns[ADD_NSEC3_RDATA];
+ normal_columns[ADD_NAME] = columns[ADD_NSEC3_HASH];
+
+ addRecord(*update_nsec3_namespace_, normal_columns);
+ }
+
+private:
// Helper predicate class used in deleteRecordInZone().
struct deleteMatch {
deleteMatch(const string& type, const string& rdata) :
@@ -728,19 +778,33 @@ public:
const string& rdata_;
};
- virtual void deleteRecordInZone(const string (¶ms)[DEL_PARAM_COUNT]) {
+ // Common subroutine for deleteRecordinZone and deleteNSEC3RecordInZone.
+ void deleteRecord(Domains& domains,
+ const string (¶ms)[DEL_PARAM_COUNT])
+ {
vector<vector<string> >& records =
- (*update_records_)[params[DatabaseAccessor::DEL_NAME]];
+ domains[params[DatabaseAccessor::DEL_NAME]];
records.erase(remove_if(records.begin(), records.end(),
deleteMatch(
params[DatabaseAccessor::DEL_TYPE],
params[DatabaseAccessor::DEL_RDATA])),
records.end());
if (records.empty()) {
- (*update_records_).erase(params[DatabaseAccessor::DEL_NAME]);
+ domains.erase(params[DatabaseAccessor::DEL_NAME]);
}
}
+public:
+ virtual void deleteRecordInZone(const string (¶ms)[DEL_PARAM_COUNT]) {
+ deleteRecord(*update_records_, params);
+ }
+
+ virtual void deleteNSEC3RecordInZone(
+ const string (¶ms)[DEL_PARAM_COUNT])
+ {
+ deleteRecord(*update_nsec3_namespace_, params);
+ }
+
//
// Helper methods to keep track of some update related activities
//
@@ -799,13 +863,13 @@ public:
{
// TODO: Provide some broken data, but it is not known yet how broken
// they'll have to be.
- Domains::const_iterator it(nsec3_namespace_.lower_bound(hash));
+ Domains::const_iterator it(nsec3_namespace_->lower_bound(hash));
// We got just after the one we want
- if (it == nsec3_namespace_.begin()) {
+ if (it == nsec3_namespace_->begin()) {
// Hmm, we got something really small. So we wrap around.
// This is one after the last, so after decreasing it we'll get
// the biggest.
- it = nsec3_namespace_.end();
+ it = nsec3_namespace_->end();
}
return ((--it)->first);
}
@@ -889,12 +953,13 @@ private:
Domains* readonly_records_;
Domains update_records_master_;
Domains* update_records_;
+ Domains nsec3_namespace_master_;
+ Domains* nsec3_namespace_;
+ Domains update_nsec3_namespace_master_;
+ Domains* update_nsec3_namespace_;
const Domains empty_records_master_;
const Domains* empty_records_;
- // The NSEC3 namespace. The above trick will be added once it is needed.
- Domains nsec3_namespace_;
-
// The journal data
std::vector<JournalEntry> journal_entries_master_;
std::vector<JournalEntry>* journal_entries_;
@@ -959,13 +1024,13 @@ private:
// the NSEC3 namespace. You don't provide the full name, only
// the hash part.
void addCurHash(const std::string& hash) {
- ASSERT_EQ(0, nsec3_namespace_.count(hash));
+ ASSERT_EQ(0, nsec3_namespace_->count(hash));
// Append the name to all of them
for (std::vector<std::vector<std::string> >::iterator
i = cur_name_.begin(); i != cur_name_.end(); ++ i) {
i->push_back(hash);
}
- nsec3_namespace_[hash] = cur_name_;
+ (*nsec3_namespace_)[hash] = cur_name_;
cur_name_.clear();
}
@@ -1009,24 +1074,15 @@ public:
// tests. Note that the NSEC3 namespace is available in other tests, but
// it should not be accessed at that time.
void enableNSEC3() {
- // We place the signature first, so it's in the block with the other
- // signatures
- vector<string> signature;
- signature.push_back("RRSIG");
- signature.push_back("3600");
- signature.push_back("");
- signature.push_back("NSEC3PARAM 5 3 3600 20000101000000 20000201000000 "
- "12345 example.org. FAKEFAKEFAKE");
- signature.push_back("exmaple.org.");
- (*readonly_records_)["example.org."].push_back(signature);
- // Now the NSEC3 param itself
- vector<string> param;
- param.push_back("NSEC3PARAM");
- param.push_back("3600");
- param.push_back("");
- param.push_back("1 0 12 aabbccdd");
- param.push_back("example.org.");
- (*readonly_records_)["example.org."].push_back(param);
+ for (int i = 0; TEST_NSEC3PARAM_RECORDS[i][0] != NULL; ++i) {
+ vector<string> param;
+ param.push_back(TEST_NSEC3PARAM_RECORDS[i][1]); // RRtype
+ param.push_back(TEST_NSEC3PARAM_RECORDS[i][2]); // TTL
+ param.push_back(""); // sigtype, unused
+ param.push_back(TEST_NSEC3PARAM_RECORDS[i][4]); // RDATA
+ param.push_back(TEST_NSEC3PARAM_RECORDS[i][0]); // owner name
+ (*readonly_records_)[param.back()].push_back(param);
+ }
}
};
@@ -1099,7 +1155,7 @@ public:
// probably move this to some specialized templated method specific
// to SQLite3 (or for even a longer term we should add an API to
// purge the diffs table).
- const char* const install_cmd = INSTALL_PROG " " TEST_DATA_COMMONDIR
+ const char* const install_cmd = INSTALL_PROG " -c " TEST_DATA_COMMONDIR
"/rwtest.sqlite3 " TEST_DATA_BUILDDIR
"/rwtest.sqlite3.copied";
if (system(install_cmd) != 0) {
@@ -1207,7 +1263,7 @@ public:
rdata::createRdata(expected_rrset->getType(),
expected_rrset->getClass(),
(*it).data_[Accessor::DIFF_RDATA]));
- isc::testutils::rrsetCheck(expected_rrset, rrset);
+ rrsetCheck(expected_rrset, rrset);
}
// We should have examined all entries of both expected and
// actual data.
@@ -1268,6 +1324,36 @@ public:
addRecordToZone(columns);
}
+ // We don't add NSEC3s until we are explicitly told we need them
+ // in enableNSEC3(); these would break some non NSEC3 tests.
+ commit();
+ }
+
+ void enableNSEC3() {
+ startUpdateZone("example.org.", false);
+
+ // Add NSECPARAM at the zone origin
+ for (int i = 0; TEST_NSEC3PARAM_RECORDS[i][0] != NULL; ++i) {
+ const string param_columns[ADD_COLUMN_COUNT] = {
+ TEST_NSEC3PARAM_RECORDS[i][0], // name
+ Name(param_columns[ADD_NAME]).reverse().toText(), // revname
+ TEST_NSEC3PARAM_RECORDS[i][2], // TTL
+ TEST_NSEC3PARAM_RECORDS[i][1], // RR type
+ TEST_NSEC3PARAM_RECORDS[i][3], // sigtype
+ TEST_NSEC3PARAM_RECORDS[i][4] }; // RDATA
+ addRecordToZone(param_columns);
+ }
+
+ // Add NSEC3s
+ for (int i = 0; TEST_NSEC3_RECORDS[i][0] != NULL; ++i) {
+ const string nsec3_columns[ADD_NSEC3_COLUMN_COUNT] = {
+ Name(TEST_NSEC3_RECORDS[i][0]).split(0, 1).toText(true),
+ TEST_NSEC3_RECORDS[i][2], // TTL
+ TEST_NSEC3_RECORDS[i][1], // RR type
+ TEST_NSEC3_RECORDS[i][4] }; // RDATA
+ addNSEC3RecordToZone(nsec3_columns);
+ }
+
commit();
}
};
@@ -1376,7 +1462,7 @@ checkRRset(isc::dns::ConstRRsetPtr rrset,
isc::dns::rdata::createRdata(rrtype, rrclass,
rdatas[i]));
}
- isc::testutils::rrsetCheck(expected_rrset, rrset);
+ rrsetCheck(expected_rrset, rrset);
}
// Iterate through a zone, common case
@@ -1512,7 +1598,7 @@ TYPED_TEST(DatabaseClientTest, getSOAFromIterator) {
}
ASSERT_TRUE(rrset);
// It should be identical to the result of getSOA().
- isc::testutils::rrsetCheck(it->getSOA(), rrset);
+ rrsetCheck(it->getSOA(), rrset);
}
TYPED_TEST(DatabaseClientTest, noSOAFromIterator) {
@@ -1550,7 +1636,7 @@ TYPED_TEST(DatabaseClientTest, iterateThenUpdate) {
}
ASSERT_TRUE(rrset);
// It should be identical to the result of getSOA().
- isc::testutils::rrsetCheck(it->getSOA(), rrset);
+ rrsetCheck(it->getSOA(), rrset);
}
TYPED_TEST(DatabaseClientTest, updateThenIterateThenUpdate) {
@@ -2946,6 +3032,96 @@ TYPED_TEST(DatabaseClientTest, addRRsetToNewZone) {
this->checkLastAdded(rrset_added);
}
+//
+// Below we define a set of NSEC3 update tests.
+//
+// Commonly used data for NSEC3 update tests below.
+const char* const nsec3_hash = "1BB7SO0452U1QHL98UISNDD9218GELR5";
+const char* const nsec3_rdata = "1 1 12 AABBCCDD "
+ "2T7B4G4VSA5SMI47K61MV5BV1A22BOJR NS SOA RRSIG NSEC3PARAM";
+const char* const nsec3_rdata2 = "1 1 12 AABBCCDD "
+ "2T7B4G4VSA5SMI47K61MV5BV1A22BOJR NS SOA RRSIG"; // differ in bitmaps
+const char* const nsec3_sig_rdata = "NSEC3 5 3 3600 20000101000000 "
+ "20000201000000 12345 example.org. FAKEFAKEFAKE";
+const char* const nsec3_sig_rdata2 = "NSEC3 5 3 3600 20000101000000 "
+ "20000201000000 12345 example.org. FAKEFAKE"; // differ in the signature
+
+// Commonly used subroutine that checks if we can get the expected record.
+// According to the API, implementations can skip filling in columns other
+// than those explicitly checked below, so we don't check them.
+void
+nsec3Check(const vector<ConstRRsetPtr>& expected_rrsets,
+ const Name& zone_name, const string& expected_hash,
+ DatabaseAccessor& accessor)
+{
+ const int zone_id = accessor.getZone(zone_name.toText()).second;
+ DatabaseAccessor::IteratorContextPtr itctx =
+ accessor.getNSEC3Records(expected_hash, zone_id);
+ ASSERT_TRUE(itctx);
+
+ // Build a list of matched RRsets and compare the both expected and built
+ // ones as sets.
+ string columns[DatabaseAccessor::COLUMN_COUNT];
+ vector<ConstRRsetPtr> actual_rrsets;
+ while (itctx->getNext(columns)) {
+ actual_rrsets.push_back(
+ textToRRset(expected_hash + "." + zone_name.toText() + " " +
+ columns[DatabaseAccessor::TTL_COLUMN] + " IN " +
+ columns[DatabaseAccessor::TYPE_COLUMN] + " " +
+ columns[DatabaseAccessor::RDATA_COLUMN]));
+ }
+ rrsetsCheck(expected_rrsets.begin(), expected_rrsets.end(),
+ actual_rrsets.begin(), actual_rrsets.end());
+}
+
+TYPED_TEST(DatabaseClientTest, addDeleteNSEC3InZone) {
+ // Add one NSEC3 RR to the zone, delete it, and add another one.
+ this->updater_ = this->client_->getUpdater(this->zname_, true);
+ const ConstRRsetPtr nsec3_rrset =
+ textToRRset(string(nsec3_hash) + ".example.org. 3600 IN NSEC3 " +
+ string(nsec3_rdata));
+ const ConstRRsetPtr nsec3_rrset2 =
+ textToRRset(string(nsec3_hash) + ".example.org. 3600 IN NSEC3 " +
+ string(nsec3_rdata2));
+ this->updater_->addRRset(*nsec3_rrset);
+ this->updater_->deleteRRset(*nsec3_rrset);
+ this->updater_->addRRset(*nsec3_rrset2);
+ this->updater_->commit();
+
+ // Check if we can get the expected record.
+ vector<ConstRRsetPtr> expected_rrsets;
+ expected_rrsets.push_back(nsec3_rrset2);
+ nsec3Check(expected_rrsets, this->zname_, nsec3_hash,
+ *this->current_accessor_);
+}
+
+TYPED_TEST(DatabaseClientTest, addDeleteNSEC3AndRRSIGToZone) {
+ // Add one NSEC3 RR and its RRSIG to the zone, delete the RRSIG and add
+ // a new one.
+ this->updater_ = this->client_->getUpdater(this->zname_, true);
+ const ConstRRsetPtr nsec3_rrset =
+ textToRRset(string(nsec3_hash) + ".example.org. 3600 IN NSEC3 " +
+ string(nsec3_rdata));
+ const ConstRRsetPtr nsec3_sig_rrset =
+ textToRRset(string(nsec3_hash) + ".example.org. 3600 IN RRSIG " +
+ string(nsec3_sig_rdata));
+ const ConstRRsetPtr nsec3_sig_rrset2 =
+ textToRRset(string(nsec3_hash) + ".example.org. 3600 IN RRSIG " +
+ string(nsec3_sig_rdata2));
+ this->updater_->addRRset(*nsec3_rrset);
+ this->updater_->addRRset(*nsec3_sig_rrset);
+ this->updater_->deleteRRset(*nsec3_sig_rrset);
+ this->updater_->addRRset(*nsec3_sig_rrset2);
+ this->updater_->commit();
+
+ // Check if we can get the expected record.
+ vector<ConstRRsetPtr> expected_rrsets;
+ expected_rrsets.push_back(nsec3_rrset);
+ expected_rrsets.push_back(nsec3_sig_rrset2);
+ nsec3Check(expected_rrsets, this->zname_, nsec3_hash,
+ *this->current_accessor_);
+}
+
TYPED_TEST(DatabaseClientTest, addRRsetToCurrentZone) {
// Similar to the previous test, but not replacing the existing data.
boost::shared_ptr<DatabaseClient::Finder> finder(this->getFinder());
@@ -3492,6 +3668,49 @@ TYPED_TEST(DatabaseClientTest, journal) {
this->checkJournal(expected);
}
+TYPED_TEST(DatabaseClientTest, journalForNSEC3) {
+ // Similar to the previous test, but adding/deleting NSEC3 RRs, just to
+ // confirm that NSEC3 is not special for managing diffs.
+ const ConstRRsetPtr nsec3_rrset =
+ textToRRset(string(nsec3_hash) + ".example.org. 3600 IN NSEC3 " +
+ string(nsec3_rdata));
+
+ this->updater_ = this->client_->getUpdater(this->zname_, false, true);
+ this->updater_->deleteRRset(*this->soa_);
+ this->updater_->deleteRRset(*nsec3_rrset);
+
+ this->soa_.reset(new RRset(this->zname_, this->qclass_, RRType::SOA(),
+ this->rrttl_));
+ this->soa_->addRdata(rdata::createRdata(this->soa_->getType(),
+ this->soa_->getClass(),
+ "ns1.example.org. "
+ "admin.example.org. "
+ "1235 3600 1800 2419200 7200"));
+ this->updater_->addRRset(*this->soa_);
+ this->updater_->addRRset(*nsec3_rrset);
+ this->updater_->commit();
+ std::vector<JournalEntry> expected;
+ expected.push_back(JournalEntry(WRITABLE_ZONE_ID, 1234,
+ DatabaseAccessor::DIFF_DELETE,
+ "example.org.", "SOA", "3600",
+ "ns1.example.org. admin.example.org. "
+ "1234 3600 1800 2419200 7200"));
+ expected.push_back(JournalEntry(WRITABLE_ZONE_ID, 1234,
+ DatabaseAccessor::DIFF_DELETE,
+ string(nsec3_hash) + ".example.org.",
+ "NSEC3", "3600", nsec3_rdata));
+ expected.push_back(JournalEntry(WRITABLE_ZONE_ID, 1235,
+ DatabaseAccessor::DIFF_ADD,
+ "example.org.", "SOA", "3600",
+ "ns1.example.org. admin.example.org. "
+ "1235 3600 1800 2419200 7200"));
+ expected.push_back(JournalEntry(WRITABLE_ZONE_ID, 1235,
+ DatabaseAccessor::DIFF_ADD,
+ string(nsec3_hash) + ".example.org.",
+ "NSEC3", "3600", nsec3_rdata));
+ this->checkJournal(expected);
+}
+
/*
* Push multiple delete-add sequences. Checks it is allowed and all is
* saved.
@@ -3673,10 +3892,10 @@ TYPED_TEST(DatabaseClientTest, journalReader) {
ASSERT_TRUE(jnl_reader);
ConstRRsetPtr rrset = jnl_reader->getNextDiff();
ASSERT_TRUE(rrset);
- isc::testutils::rrsetCheck(this->soa_, rrset);
+ rrsetCheck(this->soa_, rrset);
rrset = jnl_reader->getNextDiff();
ASSERT_TRUE(rrset);
- isc::testutils::rrsetCheck(soa_end, rrset);
+ rrsetCheck(soa_end, rrset);
rrset = jnl_reader->getNextDiff();
ASSERT_FALSE(rrset);
@@ -3720,7 +3939,7 @@ TYPED_TEST(DatabaseClientTest, readLargeJournal) {
ConstRRsetPtr actual;
int i = 0;
while ((actual = jnl_reader->getNextDiff()) != NULL) {
- isc::testutils::rrsetCheck(expected.at(i++), actual);
+ rrsetCheck(expected.at(i++), actual);
}
EXPECT_EQ(expected.size(), i); // we should have eaten all expected data
}
@@ -3786,11 +4005,11 @@ TEST_F(MockDatabaseClientTest, journalWithBadData) {
}
/// Let us test a little bit of NSEC3.
-TEST_F(MockDatabaseClientTest, findNSEC3) {
+TYPED_TEST(DatabaseClientTest, findNSEC3) {
// Set up the faked hash calculator.
- setNSEC3HashCreator(&test_nsec3_hash_creator_);
+ setNSEC3HashCreator(&this->test_nsec3_hash_creator_);
- DataSourceClient::FindResult
+ const DataSourceClient::FindResult
zone(this->client_->findZone(Name("example.org")));
ASSERT_EQ(result::SUCCESS, zone.code);
boost::shared_ptr<DatabaseClient::Finder> finder(
diff --git a/src/lib/datasrc/tests/faked_nsec3.cc b/src/lib/datasrc/tests/faked_nsec3.cc
index 4ca22a5..1e37b8e 100644
--- a/src/lib/datasrc/tests/faked_nsec3.cc
+++ b/src/lib/datasrc/tests/faked_nsec3.cc
@@ -28,6 +28,19 @@ namespace isc {
namespace datasrc {
namespace test {
+// Constant data definitions
+
+const char* const nsec3_common = " 300 IN NSEC3 1 1 12 aabbccdd "
+ "2T7B4G4VSA5SMI47K61MV5BV1A22BOJR A RRSIG";
+const char* const nsec3_rrsig_common = " 300 IN RRSIG NSEC3 5 3 3600 "
+ "20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE";
+const char* const apex_hash = "0P9MHAVEQVM6T7VBL5LOP2U3T2RP3TOM";
+const char* const apex_hash_lower = "0p9mhaveqvm6t7vbl5lop2u3t2rp3tom";
+const char* const ns1_hash = "2T7B4G4VSA5SMI47K61MV5BV1A22BOJR";
+const char* const w_hash = "01UDEMVP1J2F7EG6JEBPS17VP3N8I58H";
+const char* const xyw_hash = "2vptu5timamqttgl4luu9kg21e0aor3s";
+const char* const zzz_hash = "R53BQ7CC2UVMUBFU5OCMM6PERS9TK9EN";
+
class TestNSEC3HashCreator::TestNSEC3Hash : public NSEC3Hash {
private:
typedef map<Name, string> NSEC3HashMap;
@@ -117,7 +130,7 @@ performNSEC3Test(ZoneFinder &finder) {
EXPECT_THROW(finder.findNSEC3(Name("example.com"), false), OutOfZone);
EXPECT_THROW(finder.findNSEC3(Name("org"), true), OutOfZone);
- Name origin("example.org");
+ const Name origin("example.org");
const string apex_nsec3_text = string(apex_hash) + ".example.org." +
string(nsec3_common);
const string ns1_nsec3_text = string(ns1_hash) + ".example.org." +
diff --git a/src/lib/datasrc/tests/faked_nsec3.h b/src/lib/datasrc/tests/faked_nsec3.h
index 51b4059..10d9444 100644
--- a/src/lib/datasrc/tests/faked_nsec3.h
+++ b/src/lib/datasrc/tests/faked_nsec3.h
@@ -31,26 +31,24 @@ namespace test {
//
// Commonly used NSEC3 suffix. It's incorrect to use it for all NSEC3s, but
// doesn't matter for the purpose of our tests.
-const char* const nsec3_common = " 300 IN NSEC3 1 1 12 aabbccdd "
- "2T7B4G4VSA5SMI47K61MV5BV1A22BOJR A RRSIG";
+extern const char* const nsec3_common;
// Likewise, common RRSIG suffix for NSEC3s.
-const char* const nsec3_rrsig_common = " 300 IN RRSIG NSEC3 5 3 3600 "
- "20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE";
+extern const char* const nsec3_rrsig_common;
// 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";
+extern const char* const apex_hash;
+extern const char* const apex_hash_lower;
// For ns1.example.org
-const char* const ns1_hash = "2T7B4G4VSA5SMI47K61MV5BV1A22BOJR";
+extern const char* const ns1_hash;
// For w.example.org
-const char* const w_hash = "01UDEMVP1J2F7EG6JEBPS17VP3N8I58H";
+extern const char* const w_hash;
// For x.y.w.example.org (lower-cased)
-const char* const xyw_hash = "2vptu5timamqttgl4luu9kg21e0aor3s";
+extern const char* const xyw_hash;
// For zzz.example.org.
-const char* const zzz_hash = "R53BQ7CC2UVMUBFU5OCMM6PERS9TK9EN";
+extern const char* const zzz_hash;
// A simple faked NSEC3 hash calculator with a dedicated creator for it.
//
@@ -83,4 +81,8 @@ performNSEC3Test(ZoneFinder &finder);
}
}
-#endif
+#endif // FAKED_NSEC3_H
+
+// Local Variables:
+// mode: c++
+// End:
diff --git a/src/lib/datasrc/tests/memory_datasrc_unittest.cc b/src/lib/datasrc/tests/memory_datasrc_unittest.cc
index a7d13d5..a881f2d 100644
--- a/src/lib/datasrc/tests/memory_datasrc_unittest.cc
+++ b/src/lib/datasrc/tests/memory_datasrc_unittest.cc
@@ -293,17 +293,71 @@ setRRset(RRsetPtr rrset, vector<RRsetPtr*>::iterator& it) {
++it;
}
-RRsetPtr
-textToRRset(const string& text_rrset, const RRClass& rrclass = RRClass::IN(),
- const Name& origin = Name::ROOT_NAME())
-{
- stringstream ss(text_rrset);
- RRsetPtr rrset;
- vector<RRsetPtr*> rrsets;
- rrsets.push_back(&rrset);
- masterLoad(ss, origin, 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 {
@@ -319,18 +373,30 @@ protected:
// expected_flags is set to either RESULT_NSEC_SIGNED or
// RESULT_NSEC3_SIGNED when it's NSEC/NSEC3 signed respectively and
// find() is expected to set the corresponding flags.
+ // find_options should be set to FIND_DNSSEC for NSEC-signed case when
+ // NSEC is expected to be returned.
void findCheck(ZoneFinder::FindResultFlags expected_flags =
- ZoneFinder::RESULT_DEFAULT);
+ ZoneFinder::RESULT_DEFAULT,
+ ZoneFinder::FindOptions find_options =
+ ZoneFinder::FIND_DEFAULT);
void emptyNodeCheck(ZoneFinder::FindResultFlags expected_flags =
ZoneFinder::RESULT_DEFAULT);
void wildcardCheck(ZoneFinder::FindResultFlags expected_flags =
- ZoneFinder::RESULT_DEFAULT);
+ ZoneFinder::RESULT_DEFAULT,
+ ZoneFinder::FindOptions find_options =
+ ZoneFinder::FIND_DEFAULT);
void doCancelWildcardCheck(ZoneFinder::FindResultFlags expected_flags =
- ZoneFinder::RESULT_DEFAULT);
+ ZoneFinder::RESULT_DEFAULT,
+ ZoneFinder::FindOptions find_options =
+ ZoneFinder::FIND_DEFAULT);
void anyWildcardCheck(ZoneFinder::FindResultFlags expected_flags =
ZoneFinder::RESULT_DEFAULT);
void emptyWildcardCheck(ZoneFinder::FindResultFlags expected_flags =
ZoneFinder::RESULT_DEFAULT);
+ void findNSECENTCheck(const Name& ent_name,
+ ConstRRsetPtr expected_nsec,
+ ZoneFinder::FindResultFlags expected_flags =
+ ZoneFinder::RESULT_DEFAULT);
public:
InMemoryZoneFinderTest() :
@@ -387,6 +453,23 @@ public:
{"0P9MHAVEQVM6T7VBL5LOP2U3T2RP3TOM.example.org. 300 IN "
"NSEC3 1 1 12 aabbccdd 2T7B4G4VSA5SMI47K61MV5BV1A22BOJR A RRSIG",
&rr_nsec3_},
+ {"example.org. 300 IN NSEC wild.*.foo.example.org. "
+ "NS SOA RRSIG NSEC DNSKEY", &rr_nsec_},
+ // Together with the apex NSEC, these next NSECs make a complete
+ // chain in the case of the zone for the emptyNonterminal tests
+ // (We may want to clean up this generator code and/or masterLoad
+ // so that we can prepare conflicting datasets better)
+ {"wild.*.foo.example.org. 3600 IN NSEC ns.example.org. "
+ "A RRSIG NSEC", &rr_ent_nsec2_},
+ {"ns.example.org. 3600 IN NSEC foo.wild.example.org. A RRSIG NSEC",
+ &rr_ent_nsec3_},
+ {"foo.wild.example.org. 3600 IN NSEC example.org. A RRSIG NSEC",
+ &rr_ent_nsec4_},
+ // And these are NSECs used in different tests
+ {"ns.example.org. 300 IN NSEC *.nswild.example.org. A AAAA NSEC",
+ &rr_ns_nsec_},
+ {"*.wild.example.org. 300 IN NSEC foo.wild.example.org. A NSEC",
+ &rr_wild_nsec_},
{NULL, NULL}
};
@@ -451,6 +534,12 @@ public:
RRsetPtr rr_not_wild_;
RRsetPtr rr_not_wild_another_;
RRsetPtr rr_nsec3_;
+ RRsetPtr rr_nsec_;
+ RRsetPtr rr_ent_nsec2_;
+ RRsetPtr rr_ent_nsec3_;
+ RRsetPtr rr_ent_nsec4_;
+ RRsetPtr rr_ns_nsec_;
+ RRsetPtr rr_wild_nsec_;
// A faked NSEC3 hash calculator for convenience.
// Tests that need to use the faked hashed values should call
@@ -920,7 +1009,9 @@ TEST_F(InMemoryZoneFinderTest, glue) {
* directly there, it just tells it doesn't exist.
*/
void
-InMemoryZoneFinderTest::findCheck(ZoneFinder::FindResultFlags expected_flags) {
+InMemoryZoneFinderTest::findCheck(ZoneFinder::FindResultFlags expected_flags,
+ ZoneFinder::FindOptions find_options)
+{
// Fill some data inside
// Now put all the data we have there. It should throw nothing
EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_finder_.add(rr_ns_)));
@@ -930,23 +1021,53 @@ InMemoryZoneFinderTest::findCheck(ZoneFinder::FindResultFlags expected_flags) {
if ((expected_flags & ZoneFinder::RESULT_NSEC3_SIGNED) != 0) {
EXPECT_EQ(SUCCESS, zone_finder_.add(rr_nsec3_));
}
+ if ((expected_flags & ZoneFinder::RESULT_NSEC_SIGNED) != 0) {
+ EXPECT_EQ(SUCCESS, zone_finder_.add(rr_nsec_));
+ }
// These two should be successful
findTest(origin_, RRType::NS(), ZoneFinder::SUCCESS, true, rr_ns_);
findTest(rr_ns_a_->getName(), RRType::A(), ZoneFinder::SUCCESS, true,
rr_ns_a_);
- // These domain exist but don't have the provided RRType
- findTest(origin_, RRType::AAAA(), ZoneFinder::NXRRSET, true,
- ConstRRsetPtr(), expected_flags);
- findTest(rr_ns_a_->getName(), RRType::NS(), ZoneFinder::NXRRSET, true,
- ConstRRsetPtr(), expected_flags);
+ // These domains don't exist. (and one is out of the zone). In an
+ // NSEC-signed zone with DNSSEC records requested, it should return the
+ // covering NSEC for the query name (the actual NSEC in the test data may
+ // not really "cover" it, but for the purpose of this test it's okay).
+ ConstRRsetPtr expected_nsec; // by default it's NULL
+ if ((expected_flags & ZoneFinder::RESULT_NSEC_SIGNED) != 0 &&
+ (find_options & ZoneFinder::FIND_DNSSEC) != 0) {
+ expected_nsec = rr_nsec_;
+ }
- // These domains don't exist (and one is out of the zone)
- findTest(Name("nothere.example.org"), RRType::A(), ZoneFinder::NXDOMAIN,
- true, ConstRRsetPtr(), expected_flags);
+ // There's no other name between this one and the origin, so when NSEC
+ // is to be returned it should be the origin NSEC.
+ findTest(Name("nothere.example.org"), RRType::A(),
+ ZoneFinder::NXDOMAIN, true, expected_nsec, expected_flags,
+ NULL, find_options);
+
+ // The previous name in the zone is "ns.example.org", but it doesn't
+ // have an NSEC. It should be skipped and the origin NSEC will be
+ // returned as the "closest NSEC".
+ findTest(Name("nxdomain.example.org"), RRType::A(),
+ ZoneFinder::NXDOMAIN, true, expected_nsec, expected_flags,
+ NULL, find_options);
EXPECT_THROW(zone_finder_.find(Name("example.net"), RRType::A()),
OutOfZone);
+
+ // These domain exist but don't have the provided RRType. For the latter
+ // one we now add its NSEC (which was delayed so that it wouldn't break
+ // other cases above).
+ findTest(origin_, RRType::AAAA(), ZoneFinder::NXRRSET, true,
+ expected_nsec, expected_flags, NULL, find_options);
+ if ((expected_flags & ZoneFinder::RESULT_NSEC_SIGNED) != 0) {
+ EXPECT_EQ(SUCCESS, zone_finder_.add(rr_ns_nsec_));
+ if ((find_options & ZoneFinder::FIND_DNSSEC) != 0) {
+ expected_nsec = rr_ns_nsec_;
+ }
+ }
+ findTest(rr_ns_a_->getName(), RRType::NS(), ZoneFinder::NXRRSET, true,
+ expected_nsec, expected_flags, NULL, find_options);
}
TEST_F(InMemoryZoneFinderTest, find) {
@@ -957,6 +1078,74 @@ TEST_F(InMemoryZoneFinderTest, findNSEC3Signed) {
findCheck(ZoneFinder::RESULT_NSEC3_SIGNED);
}
+TEST_F(InMemoryZoneFinderTest, findNSEC3SignedWithDNSSEC) {
+ // For NSEC3-signed zones, specifying the DNSSEC option shouldn't change
+ // anything (the NSEC3_SIGNED flag is always set, and no records are
+ // returned for negative cases regardless).
+ findCheck(ZoneFinder::RESULT_NSEC3_SIGNED, ZoneFinder::FIND_DNSSEC);
+}
+
+TEST_F(InMemoryZoneFinderTest, findNSECSigned) {
+ // NSEC-signed zone, without requesting DNSSEC (no NSEC should be provided)
+ findCheck(ZoneFinder::RESULT_NSEC_SIGNED);
+}
+
+// Generalized test for Empty Nonterminal (ENT) cases with NSEC
+void
+InMemoryZoneFinderTest::findNSECENTCheck(const Name& ent_name,
+ ConstRRsetPtr expected_nsec,
+ ZoneFinder::FindResultFlags expected_flags)
+{
+ EXPECT_EQ(SUCCESS, zone_finder_.add(rr_emptywild_));
+ EXPECT_EQ(SUCCESS, zone_finder_.add(rr_under_wild_));
+
+ // Sanity check: Should result in NXRRSET
+ findTest(ent_name, RRType::A(), ZoneFinder::NXRRSET, true,
+ ConstRRsetPtr(), expected_flags);
+ // Sanity check: No NSEC added yet
+ findTest(ent_name, RRType::A(), ZoneFinder::NXRRSET, true,
+ ConstRRsetPtr(), expected_flags,
+ NULL, ZoneFinder::FIND_DNSSEC);
+
+ // Now add the NSEC rrs making it a 'complete' zone (in terms of NSEC,
+ // there are no sigs)
+ EXPECT_EQ(SUCCESS, zone_finder_.add(rr_nsec_));
+ EXPECT_EQ(SUCCESS, zone_finder_.add(rr_ent_nsec2_));
+ EXPECT_EQ(SUCCESS, zone_finder_.add(rr_ent_nsec3_));
+ EXPECT_EQ(SUCCESS, zone_finder_.add(rr_ent_nsec4_));
+
+ // Should result in NXRRSET, and RESULT_NSEC_SIGNED
+ findTest(ent_name, RRType::A(), ZoneFinder::NXRRSET, true,
+ ConstRRsetPtr(),
+ expected_flags | ZoneFinder::RESULT_NSEC_SIGNED);
+
+ // And check for the NSEC if DNSSEC_OK
+ findTest(ent_name, RRType::A(), ZoneFinder::NXRRSET, true,
+ expected_nsec, expected_flags | ZoneFinder::RESULT_NSEC_SIGNED,
+ NULL, ZoneFinder::FIND_DNSSEC);
+}
+
+TEST_F(InMemoryZoneFinderTest,findNSECEmptyNonterminal) {
+ // Non-wildcard case
+ findNSECENTCheck(Name("wild.example.org"), rr_ent_nsec3_);
+}
+
+TEST_F(InMemoryZoneFinderTest,findNSECEmptyNonterminalWildcard) {
+ // Wildcard case, above actual wildcard
+ findNSECENTCheck(Name("foo.example.org"), rr_nsec_);
+}
+
+TEST_F(InMemoryZoneFinderTest,findNSECEmptyNonterminalAtWildcard) {
+ // Wildcard case, at actual wildcard
+ findNSECENTCheck(Name("bar.foo.example.org"), rr_nsec_,
+ ZoneFinder::RESULT_WILDCARD);
+}
+
+TEST_F(InMemoryZoneFinderTest, findNSECSignedWithDNSSEC) {
+ // NSEC-signed zone, requesting DNSSEC (NSEC should be provided)
+ findCheck(ZoneFinder::RESULT_NSEC_SIGNED, ZoneFinder::FIND_DNSSEC);
+}
+
void
InMemoryZoneFinderTest::emptyNodeCheck(
ZoneFinder::FindResultFlags expected_flags)
@@ -985,6 +1174,9 @@ InMemoryZoneFinderTest::emptyNodeCheck(
if ((expected_flags & ZoneFinder::RESULT_NSEC3_SIGNED) != 0) {
EXPECT_EQ(SUCCESS, zone_finder_.add(rr_nsec3_));
}
+ if ((expected_flags & ZoneFinder::RESULT_NSEC_SIGNED) != 0) {
+ EXPECT_EQ(SUCCESS, zone_finder_.add(rr_nsec_));
+ }
// empty node matching, easy case: the node for 'baz' exists with
// no data.
@@ -1012,6 +1204,10 @@ TEST_F(InMemoryZoneFinderTest, emptyNodeNSEC3) {
emptyNodeCheck(ZoneFinder::RESULT_NSEC3_SIGNED);
}
+TEST_F(InMemoryZoneFinderTest, emptyNodeNSEC) {
+ emptyNodeCheck(ZoneFinder::RESULT_NSEC_SIGNED);
+}
+
TEST_F(InMemoryZoneFinderTest, load) {
// Put some data inside the zone
EXPECT_NO_THROW(EXPECT_EQ(result::SUCCESS, zone_finder_.add(rr_ns_)));
@@ -1112,7 +1308,8 @@ TEST_F(InMemoryZoneFinderTest, loadFromIterator) {
*/
void
InMemoryZoneFinderTest::wildcardCheck(
- ZoneFinder::FindResultFlags expected_flags)
+ ZoneFinder::FindResultFlags expected_flags,
+ ZoneFinder::FindOptions find_options)
{
/*
* example.org.
@@ -1124,7 +1321,6 @@ 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
@@ -1146,14 +1342,38 @@ InMemoryZoneFinderTest::wildcardCheck(
if ((expected_flags & ZoneFinder::RESULT_NSEC3_SIGNED) != 0) {
EXPECT_EQ(SUCCESS, zone_finder_.add(rr_nsec3_));
}
+ if ((expected_flags & ZoneFinder::RESULT_NSEC_SIGNED) != 0) {
+ EXPECT_EQ(SUCCESS, zone_finder_.add(rr_nsec_));
+ }
// Search at the parent. The parent will not have the A, but it will
// be in the wildcard (so check the wildcard isn't matched at the parent)
{
SCOPED_TRACE("Search at parent");
- findTest(Name("wild.example.org"), RRType::A(), ZoneFinder::NXRRSET,
- true, ConstRRsetPtr(), expected_flags, NULL, find_options);
+ if ((expected_flags & ZoneFinder::RESULT_NSEC_SIGNED) != 0) {
+ findTest(Name("wild.example.org"), RRType::A(),
+ ZoneFinder::NXRRSET, true, rr_nsec_, expected_flags,
+ NULL, find_options);
+ } else {
+ findTest(Name("wild.example.org"), RRType::A(),
+ ZoneFinder::NXRRSET, true, ConstRRsetPtr(),
+ expected_flags, NULL, find_options);
+ }
+ }
+
+ // For the test setup of "NSEC-signed" zone, we might expect it will
+ // be returned with a negative result, either because wildcard match is
+ // disabled by the search option or because wildcard match is canceled
+ // per protocol.
+ ConstRRsetPtr expected_nsec; // by default it's NULL
+ if ((expected_flags & ZoneFinder::RESULT_NSEC_SIGNED) != 0 &&
+ (find_options & ZoneFinder::FIND_DNSSEC) != 0) {
+ expected_nsec = rr_nsec_;
}
+ // Explicitly converting the following to const pointers; some compilers
+ // would complain about mixed use of const and non const in ?: below.
+ const ConstRRsetPtr rr_wild = rr_wild_;
+ const ConstRRsetPtr rr_cnamewild = rr_cnamewild_;
// Search the original name of wildcard
{
@@ -1162,45 +1382,70 @@ InMemoryZoneFinderTest::wildcardCheck(
true, rr_wild_, ZoneFinder::RESULT_DEFAULT, NULL,
find_options);
}
+
+ // Below some of the test cases will normally result in a wildcard match;
+ // if NO_WILDCARD is specified, it should result in NXDOMAIN instead,
+ // and, when available and requested, the covering NSEC will be returned.
+ // The following are shortcut parameters to unify these cases.
+ const bool wild_ok = ((find_options & ZoneFinder::NO_WILDCARD) == 0);
+ const ZoneFinder::FindResultFlags wild_expected_flags =
+ wild_ok ? (ZoneFinder::RESULT_WILDCARD | expected_flags) :
+ expected_flags;
+
// Search "created" name.
{
SCOPED_TRACE("Search at created child");
- findTest(Name("a.wild.example.org"), RRType::A(), ZoneFinder::SUCCESS,
- false, rr_wild_,
- ZoneFinder::RESULT_WILDCARD | expected_flags, NULL,
- 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, NULL,
- find_options);
+ findTest(Name("a.wild.example.org"), RRType::A(),
+ wild_ok ? ZoneFinder::SUCCESS : ZoneFinder::NXDOMAIN, false,
+ wild_ok ? rr_wild : expected_nsec,
+ wild_expected_flags, NULL, find_options, wild_ok);
}
// Search name that has CNAME.
{
SCOPED_TRACE("Matching CNAME");
findTest(Name("a.cnamewild.example.org"), RRType::A(),
- ZoneFinder::CNAME, false, rr_cnamewild_,
- ZoneFinder::RESULT_WILDCARD | expected_flags, NULL,
- find_options, true);
+ wild_ok ? ZoneFinder::CNAME : ZoneFinder::NXDOMAIN, false,
+ wild_ok ? rr_cnamewild : expected_nsec,
+ wild_expected_flags, NULL, find_options, wild_ok);
}
// Search another created name, this time little bit lower
{
SCOPED_TRACE("Search at created grand-child");
findTest(Name("a.b.wild.example.org"), RRType::A(),
- ZoneFinder::SUCCESS, false, rr_wild_,
- ZoneFinder::RESULT_WILDCARD | expected_flags, NULL,
- find_options, true);
+ wild_ok ? ZoneFinder::SUCCESS : ZoneFinder::NXDOMAIN, false,
+ wild_ok ? rr_wild : expected_nsec,
+ wild_expected_flags, NULL, find_options, wild_ok);
}
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, expected_nsec, expected_flags,
NULL, find_options);
}
+
+ // Wildcard match, but no data. We add the additional NSEC at the wildcard
+ // at this point so that it wouldn't break other tests above. Note also
+ // that in the NO_WILDCARD case the resulting NSEC is the same. Ideally
+ // we could use a more tricky setup so we can distinguish these cases,
+ // but for this purpose it's not bad; what we'd like to test here is that
+ // wildcard substitution doesn't happen for either case, and the
+ // NO_WILDCARD effect itself can be checked by the result code (NXDOMAIN).
+ ConstRRsetPtr expected_wild_nsec; // by default it's NULL
+ if ((expected_flags & ZoneFinder::RESULT_NSEC_SIGNED) != 0) {
+ EXPECT_EQ(SUCCESS, zone_finder_.add(rr_wild_nsec_));
+ expected_wild_nsec = rr_wild_nsec_;
+ }
+ {
+ SCOPED_TRACE("Search at wildcard, no data");
+ findTest(Name("a.wild.example.org"), RRType::AAAA(),
+ wild_ok ? ZoneFinder::NXRRSET : ZoneFinder::NXDOMAIN, true,
+ wild_ok ? expected_wild_nsec : expected_wild_nsec,
+ wild_expected_flags, NULL, find_options);
+ }
}
TEST_F(InMemoryZoneFinderTest, wildcard) {
@@ -1213,6 +1458,23 @@ TEST_F(InMemoryZoneFinderTest, wildcardNSEC3) {
wildcardCheck(ZoneFinder::RESULT_NSEC3_SIGNED);
}
+TEST_F(InMemoryZoneFinderTest, wildcardNSEC) {
+ // Similar to the previous one, but the zone is signed with NSEC
+ wildcardCheck(ZoneFinder::RESULT_NSEC_SIGNED);
+}
+
+TEST_F(InMemoryZoneFinderTest, wildcardDisabledWithNSEC) {
+ // Wildcard is disabled. In practice, this is used as part of query
+ // processing for an NSEC-signed zone, so we test that case specifically.
+ wildcardCheck(ZoneFinder::RESULT_NSEC_SIGNED, ZoneFinder::NO_WILDCARD);
+}
+
+TEST_F(InMemoryZoneFinderTest, wildcardDisabledWithoutNSEC) {
+ // Similar to the previous once, but check the behavior for a non signed
+ // zone just in case.
+ wildcardCheck(ZoneFinder::RESULT_DEFAULT, ZoneFinder::NO_WILDCARD);
+}
+
/*
* Test that we don't match a wildcard if we get under delegation.
* By 4.3.3 of RFC1034:
@@ -1247,6 +1509,9 @@ InMemoryZoneFinderTest::anyWildcardCheck(
if ((expected_flags & ZoneFinder::RESULT_NSEC3_SIGNED) != 0) {
EXPECT_EQ(SUCCESS, zone_finder_.add(rr_nsec3_));
}
+ if ((expected_flags & ZoneFinder::RESULT_NSEC_SIGNED) != 0) {
+ EXPECT_EQ(SUCCESS, zone_finder_.add(rr_nsec_));
+ }
vector<ConstRRsetPtr> expected_sets;
@@ -1281,6 +1546,10 @@ TEST_F(InMemoryZoneFinderTest, anyWildcardNSEC3) {
anyWildcardCheck(ZoneFinder::RESULT_NSEC3_SIGNED);
}
+TEST_F(InMemoryZoneFinderTest, anyWildcardNSEC) {
+ anyWildcardCheck(ZoneFinder::RESULT_NSEC_SIGNED);
+}
+
// Test there's nothing in the wildcard in the middle if we load
// wild.*.foo.example.org.
void
@@ -1297,6 +1566,9 @@ InMemoryZoneFinderTest::emptyWildcardCheck(
if ((expected_flags & ZoneFinder::RESULT_NSEC3_SIGNED) != 0) {
EXPECT_EQ(SUCCESS, zone_finder_.add(rr_nsec3_));
}
+ if ((expected_flags & ZoneFinder::RESULT_NSEC_SIGNED) != 0) {
+ EXPECT_EQ(SUCCESS, zone_finder_.add(rr_nsec_));
+ }
{
SCOPED_TRACE("Asking for the original record under wildcard");
@@ -1341,6 +1613,10 @@ TEST_F(InMemoryZoneFinderTest, emptyWildcardNSEC3) {
emptyWildcardCheck(ZoneFinder::RESULT_NSEC3_SIGNED);
}
+TEST_F(InMemoryZoneFinderTest, emptyWildcardNSEC) {
+ emptyWildcardCheck(ZoneFinder::RESULT_NSEC_SIGNED);
+}
+
// Same as emptyWildcard, but with multiple * in the path.
TEST_F(InMemoryZoneFinderTest, nestedEmptyWildcard) {
EXPECT_EQ(SUCCESS, zone_finder_.add(rr_nested_emptywild_));
@@ -1402,15 +1678,29 @@ TEST_F(InMemoryZoneFinderTest, nestedEmptyWildcard) {
// situations
void
InMemoryZoneFinderTest::doCancelWildcardCheck(
- ZoneFinder::FindResultFlags expected_flags)
+ ZoneFinder::FindResultFlags expected_flags,
+ ZoneFinder::FindOptions find_options)
{
// These should be canceled
{
SCOPED_TRACE("Canceled under foo.wild.example.org");
+
+ // For an NSEC-signed zone with DNSSEC requested, the covering NSEC
+ // should be returned. The expected NSEC is actually just the only
+ // NSEC in the test data, but in this context it doesn't matter;
+ // it's sufficient just to check any NSEC is returned (or not).
+ ConstRRsetPtr expected_nsec; // by default it's NULL
+ if ((expected_flags & ZoneFinder::RESULT_NSEC_SIGNED) != 0 &&
+ (find_options & ZoneFinder::FIND_DNSSEC)) {
+ expected_nsec = rr_nsec_;
+ }
+
findTest(Name("aaa.foo.wild.example.org"), RRType::A(),
- ZoneFinder::NXDOMAIN, true, ConstRRsetPtr(), expected_flags);
+ ZoneFinder::NXDOMAIN, true, expected_nsec, expected_flags,
+ NULL, find_options);
findTest(Name("zzz.foo.wild.example.org"), RRType::A(),
- ZoneFinder::NXDOMAIN, true, ConstRRsetPtr(), expected_flags);
+ ZoneFinder::NXDOMAIN, true, expected_nsec, expected_flags,
+ NULL, find_options);
}
// This is existing, non-wildcard domain, shouldn't wildcard at all
@@ -1478,6 +1768,7 @@ TEST_F(InMemoryZoneFinderTest, cancelWildcard) {
}
}
+// Same tests as cancelWildcard for NSEC3-signed zone
TEST_F(InMemoryZoneFinderTest, cancelWildcardNSEC3) {
EXPECT_EQ(SUCCESS, zone_finder_.add(rr_wild_));
EXPECT_EQ(SUCCESS, zone_finder_.add(rr_not_wild_));
@@ -1494,6 +1785,29 @@ TEST_F(InMemoryZoneFinderTest, cancelWildcardNSEC3) {
}
}
+// Same tests as cancelWildcard for NSEC-signed zone. Check both cases with
+// or without FIND_DNSSEC option. NSEC should be returned only when the option
+// is given.
+TEST_F(InMemoryZoneFinderTest, cancelWildcardNSEC) {
+ EXPECT_EQ(SUCCESS, zone_finder_.add(rr_wild_));
+ EXPECT_EQ(SUCCESS, zone_finder_.add(rr_not_wild_));
+ EXPECT_EQ(SUCCESS, zone_finder_.add(rr_nsec_));
+
+ {
+ SCOPED_TRACE("Runnig with single entry under foo.wild.example.org");
+ doCancelWildcardCheck(ZoneFinder::RESULT_NSEC_SIGNED,
+ ZoneFinder::FIND_DNSSEC);
+ doCancelWildcardCheck(ZoneFinder::RESULT_NSEC_SIGNED);
+ }
+ EXPECT_EQ(SUCCESS, zone_finder_.add(rr_not_wild_another_));
+ {
+ SCOPED_TRACE("Runnig with two entries under foo.wild.example.org");
+ doCancelWildcardCheck(ZoneFinder::RESULT_NSEC_SIGNED,
+ ZoneFinder::FIND_DNSSEC);
+ doCancelWildcardCheck(ZoneFinder::RESULT_NSEC_SIGNED);
+ }
+}
+
TEST_F(InMemoryZoneFinderTest, loadBadWildcard) {
// We reject loading the zone if it contains a wildcard name for
// NS or DNAME.
diff --git a/src/lib/datasrc/tests/rbtree_unittest.cc b/src/lib/datasrc/tests/rbtree_unittest.cc
index b26a22b..a11bff5 100644
--- a/src/lib/datasrc/tests/rbtree_unittest.cc
+++ b/src/lib/datasrc/tests/rbtree_unittest.cc
@@ -45,8 +45,8 @@ const size_t Name::MAX_LABELS;
* c | g.h
* | |
* w.y i
- * / | \
- * x | z
+ * / | \ \
+ * x | z k
* | |
* p j
* / \
@@ -59,7 +59,7 @@ protected:
RBTreeTest() : rbtree_expose_empty_node(true), crbtnode(NULL) {
const char* const domain_names[] = {
"c", "b", "a", "x.d.e.f", "z.d.e.f", "g.h", "i.g.h", "o.w.y.d.e.f",
- "j.z.d.e.f", "p.w.y.d.e.f", "q.w.y.d.e.f"};
+ "j.z.d.e.f", "p.w.y.d.e.f", "q.w.y.d.e.f", "k.g.h"};
int name_count = sizeof(domain_names) / sizeof(domain_names[0]);
for (int i = 0; i < name_count; ++i) {
rbtree.insert(Name(domain_names[i]), &rbtnode);
@@ -79,7 +79,7 @@ protected:
TEST_F(RBTreeTest, getNodeCount) {
- EXPECT_EQ(13, rbtree.getNodeCount());
+ EXPECT_EQ(14, rbtree.getNodeCount());
}
TEST_F(RBTreeTest, setGetData) {
@@ -91,46 +91,46 @@ TEST_F(RBTreeTest, insertNames) {
EXPECT_EQ(RBTree<int>::ALREADYEXISTS, rbtree.insert(Name("d.e.f"),
&rbtnode));
EXPECT_EQ(Name("d.e.f"), rbtnode->getName());
- EXPECT_EQ(13, rbtree.getNodeCount());
+ EXPECT_EQ(14, rbtree.getNodeCount());
//insert not exist node
EXPECT_EQ(RBTree<int>::SUCCESS, rbtree.insert(Name("."), &rbtnode));
EXPECT_EQ(Name("."), rbtnode->getName());
- EXPECT_EQ(14, rbtree.getNodeCount());
+ EXPECT_EQ(15, rbtree.getNodeCount());
EXPECT_EQ(RBTree<int>::SUCCESS, rbtree.insert(Name("example.com"), &rbtnode));
- EXPECT_EQ(15, rbtree.getNodeCount());
+ EXPECT_EQ(16, rbtree.getNodeCount());
rbtnode->setData(RBNode<int>::NodeDataPtr(new int(12)));
// return ALREADYEXISTS, since node "example.com" already has been explicitly inserted
EXPECT_EQ(RBTree<int>::ALREADYEXISTS, rbtree.insert(Name("example.com"), &rbtnode));
- EXPECT_EQ(15, rbtree.getNodeCount());
+ EXPECT_EQ(16, rbtree.getNodeCount());
// split the node "d.e.f"
EXPECT_EQ(RBTree<int>::SUCCESS, rbtree.insert(Name("k.e.f"), &rbtnode));
EXPECT_EQ(Name("k"), rbtnode->getName());
- EXPECT_EQ(17, rbtree.getNodeCount());
+ EXPECT_EQ(18, rbtree.getNodeCount());
// split the node "g.h"
EXPECT_EQ(RBTree<int>::ALREADYEXISTS, rbtree.insert(Name("h"), &rbtnode));
EXPECT_EQ(Name("h"), rbtnode->getName());
- EXPECT_EQ(18, rbtree.getNodeCount());
+ EXPECT_EQ(19, rbtree.getNodeCount());
// add child domain
EXPECT_EQ(RBTree<int>::SUCCESS, rbtree.insert(Name("m.p.w.y.d.e.f"), &rbtnode));
EXPECT_EQ(Name("m"), rbtnode->getName());
- EXPECT_EQ(19, rbtree.getNodeCount());
+ EXPECT_EQ(20, rbtree.getNodeCount());
EXPECT_EQ(RBTree<int>::SUCCESS, rbtree.insert(Name("n.p.w.y.d.e.f"), &rbtnode));
EXPECT_EQ(Name("n"), rbtnode->getName());
- EXPECT_EQ(20, rbtree.getNodeCount());
+ EXPECT_EQ(21, rbtree.getNodeCount());
EXPECT_EQ(RBTree<int>::SUCCESS, rbtree.insert(Name("l.a"), &rbtnode));
EXPECT_EQ(Name("l"), rbtnode->getName());
- EXPECT_EQ(21, rbtree.getNodeCount());
+ EXPECT_EQ(22, rbtree.getNodeCount());
EXPECT_EQ(RBTree<int>::SUCCESS, rbtree.insert(Name("r.d.e.f"), &rbtnode));
EXPECT_EQ(RBTree<int>::SUCCESS, rbtree.insert(Name("s.d.e.f"), &rbtnode));
- EXPECT_EQ(23, rbtree.getNodeCount());
+ EXPECT_EQ(24, rbtree.getNodeCount());
EXPECT_EQ(RBTree<int>::SUCCESS, rbtree.insert(Name("h.w.y.d.e.f"), &rbtnode));
@@ -180,10 +180,10 @@ TEST_F(RBTreeTest, findName) {
TEST_F(RBTreeTest, findError) {
// For the version that takes a node chain, the chain must be empty.
RBTreeNodeChain<int> chain;
- EXPECT_EQ(RBTree<int>::EXACTMATCH, rbtree.find<void*>(Name("a"), &crbtnode,
- chain, NULL, NULL));
+ EXPECT_EQ(RBTree<int>::EXACTMATCH, rbtree.find(Name("a"), &crbtnode,
+ chain));
// trying to reuse the same chain. it should result in an exception.
- EXPECT_THROW(rbtree.find<void*>(Name("a"), &crbtnode, chain, NULL, NULL),
+ EXPECT_THROW(rbtree.find(Name("a"), &crbtnode, chain),
BadValue);
}
@@ -280,7 +280,7 @@ TEST_F(RBTreeTest, chainLevel) {
Name node_name(Name::ROOT_NAME());
EXPECT_EQ(RBTree<int>::SUCCESS, tree.insert(node_name, &rbtnode));
EXPECT_EQ(RBTree<int>::EXACTMATCH,
- tree.find<void*>(node_name, &crbtnode, chain, NULL, NULL));
+ tree.find(node_name, &crbtnode, chain));
EXPECT_EQ(1, chain.getLevelCount());
/*
@@ -303,8 +303,7 @@ TEST_F(RBTreeTest, chainLevel) {
EXPECT_EQ(RBTree<int>::SUCCESS, tree.insert(node_name, &rbtnode));
RBTreeNodeChain<int> found_chain;
EXPECT_EQ(RBTree<int>::EXACTMATCH,
- tree.find<void*>(node_name, &crbtnode, found_chain,
- NULL, NULL));
+ tree.find(node_name, &crbtnode, found_chain));
EXPECT_EQ(i, found_chain.getLevelCount());
}
@@ -324,7 +323,7 @@ TEST_F(RBTreeTest, getAbsoluteNameError) {
/*
*the domain order should be:
* a, b, c, d.e.f, x.d.e.f, w.y.d.e.f, o.w.y.d.e.f, p.w.y.d.e.f, q.w.y.d.e.f,
- * z.d.e.f, j.z.d.e.f, g.h, i.g.h
+ * z.d.e.f, j.z.d.e.f, g.h, i.g.h, k.g.h
* b
* / \
* a d.e.f
@@ -332,23 +331,24 @@ TEST_F(RBTreeTest, getAbsoluteNameError) {
* c | g.h
* | |
* w.y i
- * / | \
- * x | z
+ * / | \ \
+ * x | z k
* | |
* p j
* / \
* o q
*/
+const char* const names[] = {
+ "a", "b", "c", "d.e.f", "x.d.e.f", "w.y.d.e.f", "o.w.y.d.e.f",
+ "p.w.y.d.e.f", "q.w.y.d.e.f", "z.d.e.f", "j.z.d.e.f",
+ "g.h", "i.g.h", "k.g.h"};
+const size_t name_count(sizeof(names) / sizeof(*names));
+
TEST_F(RBTreeTest, nextNode) {
- const char* const names[] = {
- "a", "b", "c", "d.e.f", "x.d.e.f", "w.y.d.e.f", "o.w.y.d.e.f",
- "p.w.y.d.e.f", "q.w.y.d.e.f", "z.d.e.f", "j.z.d.e.f", "g.h", "i.g.h"};
- const int name_count = sizeof(names) / sizeof(names[0]);
RBTreeNodeChain<int> node_path;
const RBNode<int>* node = NULL;
EXPECT_EQ(RBTree<int>::EXACTMATCH,
- rbtree.find<void*>(Name(names[0]), &node, node_path, NULL,
- NULL));
+ rbtree.find(Name(names[0]), &node, node_path));
for (int i = 0; i < name_count; ++i) {
EXPECT_NE(static_cast<void*>(NULL), node);
EXPECT_EQ(Name(names[i]), node_path.getAbsoluteName());
@@ -359,6 +359,201 @@ TEST_F(RBTreeTest, nextNode) {
EXPECT_EQ(static_cast<void*>(NULL), node);
}
+// Just walk using previousNode until the beginning of the tree and check it is
+// OK
+//
+// rbtree - the tree to walk
+// node - result of previous call to find(), starting position of the walk
+// node_path - the path from the previous call to find(), will be modified
+// chain_length - the number of names that should be in the chain to be walked
+// (0 means it should be empty, 3 means 'a', 'b' and 'c' should be there -
+// this is always from the beginning of the names[] list).
+// skip_first - if this is false, the node should already contain the node with
+// the first name of the chain. If it is true, the node should be NULL
+// (true is for finds that return no match, false for the ones that return
+// match)
+void
+previousWalk(RBTree<int>& rbtree, const RBNode<int>* node,
+ RBTreeNodeChain<int>& node_path, size_t chain_length,
+ bool skip_first)
+{
+ if (skip_first) {
+ // If the first is not found, this is supposed to be NULL and we skip
+ // it in our checks.
+ EXPECT_EQ(static_cast<void*>(NULL), node);
+ node = rbtree.previousNode(node_path);
+ }
+ for (size_t i(chain_length); i > 0; --i) {
+ EXPECT_NE(static_cast<void*>(NULL), node);
+ EXPECT_EQ(Name(names[i - 1]), node_path.getAbsoluteName());
+ // Find the node at the path and check the value is the same
+ // (that it really returns the correct corresponding node)
+ //
+ // The "empty" nodes can not be found
+ if (node->getData()) {
+ const RBNode<int>* node2(NULL);
+ RBTreeNodeChain<int> node_path2;
+ EXPECT_EQ(RBTree<int>::EXACTMATCH,
+ rbtree.find(Name(names[i - 1]), &node2, node_path2));
+ EXPECT_EQ(node, node2);
+ }
+ node = rbtree.previousNode(node_path);
+ }
+
+ // We should have reached the start of the tree.
+ EXPECT_EQ(static_cast<void*>(NULL), node);
+
+ // Calling previousNode() yet again should still return NULL without
+ // fail.
+ node = rbtree.previousNode(node_path);
+ EXPECT_EQ(static_cast<void*>(NULL), node);
+}
+
+// Check the previousNode
+TEST_F(RBTreeTest, previousNode) {
+ // First, iterate the whole tree from the end to the beginning.
+ RBTreeNodeChain<int> node_path;
+ EXPECT_THROW(rbtree.previousNode(node_path), isc::BadValue) <<
+ "Throw before a search was done on the path";
+ const RBNode<int>* node(NULL);
+ {
+ SCOPED_TRACE("Iterate through");
+ EXPECT_EQ(RBTree<int>::EXACTMATCH,
+ rbtree.find(Name(names[name_count - 1]), &node, node_path));
+ previousWalk(rbtree, node, node_path, name_count, false);
+ node = NULL;
+ node_path.clear();
+ }
+
+ {
+ SCOPED_TRACE("Iterate from the middle");
+ // Now, start somewhere in the middle, but within the real node.
+ EXPECT_EQ(RBTree<int>::EXACTMATCH,
+ rbtree.find(Name(names[4]), &node, node_path));
+ previousWalk(rbtree, node, node_path, 5, false);
+ node = NULL;
+ node_path.clear();
+ }
+
+ {
+ SCOPED_TRACE("Start at the first");
+ // If we start at the lowest (which is "a"), we get to the beginning
+ // right away.
+ EXPECT_EQ(RBTree<int>::EXACTMATCH,
+ rbtree.find(Name(names[0]), &node, node_path));
+ EXPECT_NE(static_cast<void*>(NULL), node);
+ EXPECT_EQ(static_cast<void*>(NULL), rbtree.previousNode(node_path));
+ node = NULL;
+ node_path.clear();
+ }
+
+ {
+ SCOPED_TRACE("Start before the first");
+ // If we start before the lowest (0 < a), we should not get a node nor
+ EXPECT_EQ(RBTree<int>::NOTFOUND,
+ rbtree.find<void*>(Name("0"), &node, node_path, NULL, NULL));
+ EXPECT_EQ(static_cast<void*>(NULL), node);
+ EXPECT_EQ(static_cast<void*>(NULL), rbtree.previousNode(node_path));
+ node = NULL;
+ node_path.clear();
+ }
+
+ {
+ SCOPED_TRACE("Start after the last");
+ EXPECT_EQ(RBTree<int>::NOTFOUND,
+ rbtree.find(Name("z"), &node, node_path));
+ previousWalk(rbtree, node, node_path, name_count, true);
+ node = NULL;
+ node_path.clear();
+ }
+
+ {
+ SCOPED_TRACE("Start below a leaf");
+ // We exit a leaf by going down. We should start by the one
+ // we exited - 'c' (actually, we should get it by the find, as partial
+ // match).
+ EXPECT_EQ(RBTree<int>::PARTIALMATCH,
+ rbtree.find(Name("b.c"), &node, node_path));
+ previousWalk(rbtree, node, node_path, 3, false);
+ node = NULL;
+ node_path.clear();
+ }
+
+ {
+ SCOPED_TRACE("Start to the right of a leaf");
+ // When searching for this, we exit the 'x' node to the right side,
+ // so we should go x afterwards.
+
+ // The d.e.f is empty node, so it is hidden by find. Therefore NOTFOUND
+ // and not PARTIALMATCH.
+ EXPECT_EQ(RBTree<int>::NOTFOUND,
+ rbtree.find(Name("xy.d.e.f"), &node, node_path));
+ previousWalk(rbtree, node, node_path, 5, true);
+ node = NULL;
+ node_path.clear();
+ }
+
+ {
+ SCOPED_TRACE("Start to the left of a leaf");
+ // This is similar to the previous, but we exit the 'z' leaf to the
+ // left side, so should not visit z at all then.
+
+ // The d.e.f is empty node, so it is hidden by find. Therefore NOTFOUND
+ // and not PARTIALMATCH.
+ EXPECT_EQ(RBTree<int>::NOTFOUND,
+ rbtree.find(Name("yz.d.e.f"), &node, node_path));
+ previousWalk(rbtree, node, node_path, 9, true);
+ node = NULL;
+ node_path.clear();
+ }
+
+ {
+ SCOPED_TRACE("Start to the right of a parent");
+ // When searching for this, we exit the 'g.h' node to the right
+ // side, so we should go to g.h's children afterwards.
+
+ // 'g.h' is an empty node, so we get a NOTFOUND and not
+ // PARTIALMATCH.
+ EXPECT_EQ(RBTree<int>::NOTFOUND,
+ rbtree.find(Name("x.h"), &node, node_path));
+ // 'g.h' is the COMMONANCESTOR.
+ EXPECT_EQ(node_path.getLastComparedNode()->getName(), Name("g.h"));
+ EXPECT_EQ(NameComparisonResult::COMMONANCESTOR,
+ node_path.getLastComparisonResult().getRelation());
+ // find() exits to the right of 'g.h'
+ EXPECT_GT(node_path.getLastComparisonResult().getOrder(), 0);
+ // We then descend into 'i.g.h' and walk all the nodes in the
+ // tree.
+ previousWalk(rbtree, node, node_path, name_count, true);
+ node = NULL;
+ node_path.clear();
+ }
+
+ {
+ SCOPED_TRACE("Start inside a wrong node");
+ // The d.e.f is a single node, but we want only part of it. We
+ // should start iterating before it.
+ EXPECT_EQ(RBTree<int>::NOTFOUND,
+ rbtree.find(Name("e.f"), &node, node_path));
+ previousWalk(rbtree, node, node_path, 3, true);
+ node = NULL;
+ node_path.clear();
+ }
+
+ {
+ SCOPED_TRACE("Lookup in empty tree");
+ // Just check it doesn't crash, etc.
+ RBTree<int> empty_tree;
+ EXPECT_EQ(RBTree<int>::NOTFOUND,
+ empty_tree.find(Name("x"), &node, node_path));
+ EXPECT_EQ(static_cast<void*>(NULL), node);
+ EXPECT_EQ(static_cast<void*>(NULL),
+ empty_tree.previousNode(node_path));
+ node = NULL;
+ node_path.clear();
+ }
+}
+
TEST_F(RBTreeTest, nextNodeError) {
// Empty chain for nextNode() is invalid.
RBTreeNodeChain<int> chain;
@@ -394,7 +589,7 @@ TEST_F(RBTreeTest, getLastComparedNode) {
// A search for an empty tree should result in no 'last compared', too.
RBTree<int> empty_tree;
EXPECT_EQ(RBTree<int>::NOTFOUND,
- empty_tree.find<void*>(Name("a"), &crbtnode, chain, NULL, NULL));
+ empty_tree.find(Name("a"), &crbtnode, chain));
EXPECT_EQ(static_cast<void*>(NULL), chain.getLastComparedNode());
chain.clear();
@@ -402,8 +597,7 @@ TEST_F(RBTreeTest, getLastComparedNode) {
// Exact match case. The returned node should be last compared.
EXPECT_EQ(RBTree<int>::EXACTMATCH,
- tree.find<void*>(Name("x.d.e.f"), &expected_node, chain,
- NULL, NULL));
+ tree.find(Name("x.d.e.f"), &expected_node, chain));
EXPECT_EQ(expected_node, chain.getLastComparedNode());
// 2 = # labels of "x."
comparisonChecks(chain, 0, 2, NameComparisonResult::EQUAL);
@@ -412,12 +606,11 @@ TEST_F(RBTreeTest, getLastComparedNode) {
// Partial match, search stopped at the matching node, which should be
// the last compared node.
EXPECT_EQ(RBTree<int>::EXACTMATCH,
- tree.find(Name("i.g.h"), &expected_node));
+ tree.find(Name("k.g.h"), &expected_node));
EXPECT_EQ(RBTree<int>::PARTIALMATCH,
- tree.find<void*>(Name("x.i.g.h"), &crbtnode, chain,
- NULL, NULL));
+ tree.find(Name("x.k.g.h"), &crbtnode, chain));
EXPECT_EQ(expected_node, chain.getLastComparedNode());
- // i.g.h < x.i.g.h, 2 = # labels of "i."
+ // k.g.h < x.k.g.h, 2 = # labels of "k."
comparisonChecks(chain, 1, 2, NameComparisonResult::SUBDOMAIN);
chain.clear();
@@ -426,8 +619,7 @@ TEST_F(RBTreeTest, getLastComparedNode) {
EXPECT_EQ(RBTree<int>::EXACTMATCH,
tree.find(Name("x.d.e.f"), &expected_node));
EXPECT_EQ(RBTree<int>::PARTIALMATCH,
- tree.find<void*>(Name("a.d.e.f"), &crbtnode, chain,
- NULL, NULL));
+ tree.find(Name("a.d.e.f"), &crbtnode, chain));
EXPECT_EQ(expected_node, chain.getLastComparedNode());
// a < x, 1 = # labels of "." (trailing dot)
comparisonChecks(chain, -1, 1, NameComparisonResult::COMMONANCESTOR);
@@ -438,8 +630,7 @@ TEST_F(RBTreeTest, getLastComparedNode) {
EXPECT_EQ(RBTree<int>::EXACTMATCH,
tree.find(Name("z.d.e.f"), &expected_node));
EXPECT_EQ(RBTree<int>::PARTIALMATCH,
- tree.find<void*>(Name("zz.d.e.f"), &crbtnode, chain,
- NULL, NULL));
+ tree.find(Name("zz.d.e.f"), &crbtnode, chain));
EXPECT_EQ(expected_node, chain.getLastComparedNode());
// zz > z, 1 = # labels of "." (trailing dot)
comparisonChecks(chain, 1, 1, NameComparisonResult::COMMONANCESTOR);
@@ -450,8 +641,7 @@ TEST_F(RBTreeTest, getLastComparedNode) {
EXPECT_EQ(RBTree<int>::EXACTMATCH,
tree.find(Name("w.y.d.e.f"), &expected_node));
EXPECT_EQ(RBTree<int>::PARTIALMATCH,
- tree.find<void*>(Name("y.d.e.f"), &crbtnode, chain,
- NULL, NULL));
+ tree.find(Name("y.d.e.f"), &crbtnode, chain));
EXPECT_EQ(expected_node, chain.getLastComparedNode());
// y < w.y, 2 = # labels of "y."
comparisonChecks(chain, -1, 2, NameComparisonResult::SUPERDOMAIN);
@@ -461,8 +651,7 @@ TEST_F(RBTreeTest, getLastComparedNode) {
// with the search name in the subtree below the matching node.
// (the expected node is the same as the previous case)
EXPECT_EQ(RBTree<int>::PARTIALMATCH,
- tree.find<void*>(Name("z.y.d.e.f"), &crbtnode, chain,
- NULL, NULL));
+ tree.find(Name("z.y.d.e.f"), &crbtnode, chain));
EXPECT_EQ(expected_node, chain.getLastComparedNode());
// z.y > w.y, 2 = # labels of "y."
comparisonChecks(chain, 1, 2, NameComparisonResult::COMMONANCESTOR);
@@ -471,7 +660,7 @@ TEST_F(RBTreeTest, getLastComparedNode) {
// Search stops in the highest level after following a left branch.
EXPECT_EQ(RBTree<int>::EXACTMATCH, tree.find(Name("c"), &expected_node));
EXPECT_EQ(RBTree<int>::NOTFOUND,
- tree.find<void*>(Name("bb"), &crbtnode, chain, NULL, NULL));
+ tree.find(Name("bb"), &crbtnode, chain));
EXPECT_EQ(expected_node, chain.getLastComparedNode());
// bb < c, 1 = # labels of "." (trailing dot)
comparisonChecks(chain, -1, 1, NameComparisonResult::COMMONANCESTOR);
@@ -480,7 +669,7 @@ TEST_F(RBTreeTest, getLastComparedNode) {
// Search stops in the highest level after following a right branch.
// (the expected node is the same as the previous case)
EXPECT_EQ(RBTree<int>::NOTFOUND,
- tree.find<void*>(Name("d"), &crbtnode, chain, NULL, NULL));
+ tree.find(Name("d"), &crbtnode, chain));
EXPECT_EQ(expected_node, chain.getLastComparedNode());
// d > c, 1 = # labels of "." (trailing dot)
comparisonChecks(chain, 1, 1, NameComparisonResult::COMMONANCESTOR);
@@ -491,7 +680,7 @@ TEST_F(RBTreeTest, dumpTree) {
std::ostringstream str;
std::ostringstream str2;
rbtree.dumpTree(str);
- str2 << "tree has 13 node(s)\nb. (black)\n a. (black)\n NULL\n NULL\n d.e.f. (black)[invisible] \n begin down from d.e.f.\n w.y. (black)[invisible] \n begin down from w.y.\n p. (black)\n o. (red)\n NULL\n NULL\n q. (red)\n NULL\n NULL\n end down from w.y.\n x. (red)\n NULL\n NULL\n z. (red)\n begin down from z.\n j. (black)\n NULL\n NULL\n end down from z.\n NULL\n NULL\n end down from d.e.f.\n c. (red)\n NULL\n NULL\n g.h. (red)\n begin down from g.h.\n i. (black)\n
NULL\n NULL\n end down from g.h.\n NULL\n NULL\n";
+ str2 << "tree has 14 node(s)\nb. (black)\n a. (black)\n NULL\n NULL\n d.e.f. (black)[invisible] \n begin down from d.e.f.\n w.y. (black)[invisible] \n begin down from w.y.\n p. (black)\n o. (red)\n NULL\n NULL\n q. (red)\n NULL\n NULL\n end down from w.y.\n x. (red)\n NULL\n NULL\n z. (red)\n begin down from z.\n j. (black)\n NULL\n NULL\n end down from z.\n NULL\n NULL\n end down from d.e.f.\n c. (red)\n NULL\n NULL\n g.h. (red)\n begin down from g.h.\n i. (black)\n
NULL\n k. (red)\n NULL\n NULL\n end down from g.h.\n NULL\n NULL\n";
EXPECT_EQ(str.str(), str2.str());
}
diff --git a/src/lib/datasrc/tests/sqlite3_accessor_unittest.cc b/src/lib/datasrc/tests/sqlite3_accessor_unittest.cc
index c36c94b..718d29b 100644
--- a/src/lib/datasrc/tests/sqlite3_accessor_unittest.cc
+++ b/src/lib/datasrc/tests/sqlite3_accessor_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 <algorithm>
-#include <vector>
+#include "faked_nsec3.h"
#include <datasrc/sqlite3_accessor.h>
@@ -21,14 +20,20 @@
#include <dns/rrclass.h>
+#include <sqlite3.h>
+
#include <gtest/gtest.h>
+
#include <boost/lexical_cast.hpp>
#include <boost/scoped_ptr.hpp>
+
+#include <algorithm>
+#include <vector>
#include <fstream>
-#include <sqlite3.h>
using namespace std;
using namespace isc::datasrc;
+using namespace isc::datasrc::test;
using boost::lexical_cast;
using isc::data::ConstElementPtr;
using isc::data::Element;
@@ -194,6 +199,49 @@ TEST_F(SQLite3AccessorTest, iterator) {
EXPECT_FALSE(context->getNext(data));
}
+// This tests the iterator through the whole zone returns NSEC3 records as
+// well. We test this specifically, as it lives in separate table and needs
+// extra handling.
+TEST_F(SQLite3AccessorTest, nsec3Iterator) {
+ // Get the zone
+ const std::pair<bool, int>
+ zone_info(accessor->getZone("sql2.example.com."));
+ ASSERT_TRUE(zone_info.first);
+
+ // Iterate through it
+ DatabaseAccessor::IteratorContextPtr
+ context(accessor->getAllRecords(zone_info.second));
+
+ // We just pick a random NSEC3 to check, the check of complete iterator
+ // is in the above test. In addition, we count the number of NSEC3, RRSIG
+ // and all records, as some kind of check it returns all the data.
+ std::string data[DatabaseAccessor::COLUMN_COUNT];
+
+ size_t nsec3count(0), rrsigcount(0), recordcount(0);
+ bool nsec3match(false);
+ while (context->getNext(data)) {
+ if (data[DatabaseAccessor::TYPE_COLUMN] == "NSEC3") {
+ nsec3count ++;
+ if (data[DatabaseAccessor::NAME_COLUMN] ==
+ "1BB7SO0452U1QHL98UISNDD9218GELR5.sql2.example.com.") {
+ nsec3match = true;
+ EXPECT_EQ("7200", data[DatabaseAccessor::TTL_COLUMN]);
+ EXPECT_EQ("1 0 10 FEEDABEE 4KLSVDE8KH8G95VU68R7AHBE1CPQN38J",
+ data[DatabaseAccessor::RDATA_COLUMN]);
+ }
+ } else if (data[DatabaseAccessor::TYPE_COLUMN] == "RRSIG") {
+ rrsigcount ++;
+ }
+ recordcount ++;
+ }
+
+ // We counted everything now, so check there's nothing else to count
+ EXPECT_EQ(11, nsec3count);
+ EXPECT_EQ(22, rrsigcount);
+ EXPECT_EQ(46, recordcount);
+ EXPECT_TRUE(nsec3match) << "No NSEC3 found when iterating the zone";
+}
+
// This tests getting NSEC3 records
TEST_F(SQLite3AccessorTest, nsec3) {
const std::pair<bool, int>
@@ -420,11 +468,11 @@ TEST(SQLite3Open, getDBNameExampleROOT) {
// Simple function to match records
void
checkRecordRow(const std::string columns[],
- const std::string& field0,
- const std::string& field1,
- const std::string& field2,
- const std::string& field3,
- const std::string& field4)
+ const std::string& field0, // for type
+ const std::string& field1, // for TTL
+ const std::string& field2, // for "sigtype"
+ const std::string& field3, // for rdata
+ const std::string& field4) // for name
{
EXPECT_EQ(field0, columns[DatabaseAccessor::TYPE_COLUMN]);
EXPECT_EQ(field1, columns[DatabaseAccessor::TTL_COLUMN]);
@@ -693,13 +741,30 @@ const char* const deleted_data[] = {
// Existing data to be removed commonly used by some of the tests below
"foo.bar.example.com.", "A", "192.0.2.1"
};
+const char* const nsec3_data[DatabaseAccessor::ADD_NSEC3_COLUMN_COUNT] = {
+ // example NSEC3 parameters. Using "apex_hash" just as a convenient
+ // shortcut; otherwise it has nothing to do with the zone apex for the
+ // purpose of this test.
+ apex_hash, "3600", "NSEC3",
+ "1 1 12 AABBCCDD 2T7B4G4VSA5SMI47K61MV5BV1A22BOJR NS SOA"
+};
+const char* const nsec3_sig_data[DatabaseAccessor::ADD_NSEC3_COLUMN_COUNT] = {
+ ns1_hash, "3600", "RRSIG",
+ "NSEC3 5 3 3600 20000101000000 20000201000000 12345 "
+ "example.com. FAKEFAKEFAKE"
+};
+const char* const nsec3_deleted_data[] = {
+ // Delete parameters for nsec3_data
+ apex_hash, nsec3_data[DatabaseAccessor::ADD_NSEC3_TYPE],
+ nsec3_data[DatabaseAccessor::ADD_NSEC3_RDATA]
+};
class SQLite3Update : public SQLite3AccessorTest {
protected:
SQLite3Update() {
// Note: if "installing" the test file fails some of the subsequent
// tests would fail.
- const char *install_cmd = INSTALL_PROG " " TEST_DATA_DIR
+ const char *install_cmd = INSTALL_PROG " -c " TEST_DATA_DIR
"/test.sqlite3 " TEST_DATA_BUILDDIR
"/test.sqlite3.copied";
if (system(install_cmd) != 0) {
@@ -719,6 +784,7 @@ protected:
int zone_id;
std::string get_columns[DatabaseAccessor::COLUMN_COUNT];
std::string add_columns[DatabaseAccessor::ADD_COLUMN_COUNT];
+ std::string add_nsec3_columns[DatabaseAccessor::ADD_NSEC3_COLUMN_COUNT];
std::string del_params[DatabaseAccessor::DEL_PARAM_COUNT];
std::string diff_params[DatabaseAccessor::DIFF_PARAM_COUNT];
@@ -746,6 +812,28 @@ checkRecords(SQLite3Accessor& accessor, int zone_id, const std::string& name,
EXPECT_TRUE(it == expected_rows.end());
}
+// Similar to the previous one, but checking transactions on the nsec3 table.
+void
+checkNSEC3Records(SQLite3Accessor& accessor, int zone_id,
+ const std::string& hash,
+ vector<const char* const*> expected_rows)
+{
+ DatabaseAccessor::IteratorContextPtr iterator =
+ accessor.getNSEC3Records(hash, zone_id);
+ std::string columns[DatabaseAccessor::COLUMN_COUNT];
+ vector<const char* const*>::const_iterator it = expected_rows.begin();
+ while (iterator->getNext(columns)) {
+ ASSERT_TRUE(it != expected_rows.end());
+ checkRecordRow(columns, (*it)[DatabaseAccessor::ADD_NSEC3_TYPE],
+ (*it)[DatabaseAccessor::ADD_NSEC3_TTL],
+ "", // sigtype, should always be empty
+ (*it)[DatabaseAccessor::ADD_NSEC3_RDATA],
+ ""); // name, always empty
+ ++it;
+ }
+ EXPECT_TRUE(it == expected_rows.end());
+}
+
TEST_F(SQLite3Update, emptyUpdate) {
// If we do nothing between start and commit, the zone content
// should be intact.
@@ -768,6 +856,26 @@ TEST_F(SQLite3Update, flushZone) {
checkRecords(*accessor, zone_id, "foo.bar.example.com.", empty_stored);
}
+TEST_F(SQLite3Update, flushZoneWithNSEC3) {
+ // Similar to the previous case, but make sure the separate nsec3 table
+ // is also cleared. We first need to add something to the table.
+ zone_id = accessor->startUpdateZone("example.com.", false).second;
+ copy(nsec3_data, nsec3_data + DatabaseAccessor::ADD_NSEC3_COLUMN_COUNT,
+ add_nsec3_columns);
+ accessor->addNSEC3RecordToZone(add_nsec3_columns);
+ accessor->commit();
+
+ // Confirm it surely exists.
+ expected_stored.clear();
+ expected_stored.push_back(nsec3_data);
+ checkNSEC3Records(*accessor, zone_id, apex_hash, expected_stored);
+
+ // Then starting zone replacement. the NSEC3 record should have been
+ // removed.
+ zone_id = accessor->startUpdateZone("example.com.", true).second;
+ checkNSEC3Records(*accessor, zone_id, apex_hash, empty_stored);
+}
+
TEST_F(SQLite3Update, readWhileUpdate) {
zone_id = accessor->startUpdateZone("example.com.", true).second;
checkRecords(*accessor, zone_id, "foo.bar.example.com.", empty_stored);
@@ -881,6 +989,62 @@ TEST_F(SQLite3Update, addRecord) {
checkRecords(*accessor, zone_id, "newdata.example.com.", expected_stored);
}
+TEST_F(SQLite3Update, addNSEC3Record) {
+ // Similar to the previous test, but for NSEC3-related records
+ checkRecords(*accessor, zone_id, apex_hash, empty_stored);
+ checkRecords(*accessor, zone_id, ns1_hash, empty_stored);
+
+ zone_id = accessor->startUpdateZone("example.com.", false).second;
+ // Add an NSEC3
+ copy(nsec3_data, nsec3_data + DatabaseAccessor::ADD_NSEC3_COLUMN_COUNT,
+ add_nsec3_columns);
+ accessor->addNSEC3RecordToZone(add_nsec3_columns);
+
+ // Add an RRSIG for NSEC3
+ copy(nsec3_sig_data,
+ nsec3_sig_data + DatabaseAccessor::ADD_NSEC3_COLUMN_COUNT,
+ add_nsec3_columns);
+ accessor->addNSEC3RecordToZone(add_nsec3_columns);
+
+ // Check the stored data, before and after commit().
+ for (size_t i = 0; i < 2; ++i) {
+ expected_stored.clear();
+ expected_stored.push_back(nsec3_data);
+ checkNSEC3Records(*accessor, zone_id, apex_hash, expected_stored);
+
+ expected_stored.clear();
+ expected_stored.push_back(nsec3_sig_data);
+ checkNSEC3Records(*accessor, zone_id, ns1_hash, expected_stored);
+
+ if (i == 0) { // make sure commit() happens only once
+ accessor->commit();
+ }
+ }
+}
+
+TEST_F(SQLite3Update, nsec3IteratorOnAdd) {
+ // This test checks if an added NSEC3 record will appear in the iterator
+ // result, meeting the expectation of addNSEC3RecordToZone.
+ // Specifically, it checks if the name column is filled with the complete
+ // owner name.
+
+ // We'll replace the zone, and add one NSEC3 record, and only that one.
+ zone_id = accessor->startUpdateZone("example.com.", true).second;
+ copy(nsec3_data, nsec3_data + DatabaseAccessor::ADD_NSEC3_COLUMN_COUNT,
+ add_nsec3_columns);
+ accessor->addNSEC3RecordToZone(add_nsec3_columns);
+ accessor->commit();
+
+ // the zone should contain only one record we just added.
+ DatabaseAccessor::IteratorContextPtr context =
+ accessor->getAllRecords(zone_id);
+ string data[DatabaseAccessor::COLUMN_COUNT];
+ EXPECT_TRUE(context->getNext(data));
+ EXPECT_EQ(string(apex_hash) + ".example.com.",
+ data[DatabaseAccessor::NAME_COLUMN]);
+ EXPECT_FALSE(context->getNext(data));
+}
+
TEST_F(SQLite3Update, addThenRollback) {
zone_id = accessor->startUpdateZone("example.com.", false).second;
copy(new_data, new_data + DatabaseAccessor::ADD_COLUMN_COUNT,
@@ -891,7 +1055,11 @@ TEST_F(SQLite3Update, addThenRollback) {
expected_stored.push_back(new_data);
checkRecords(*accessor, zone_id, "newdata.example.com.", expected_stored);
+ // Rollback the transaction, and confirm the zone reverts to the previous
+ // state. We also start another update to check if the accessor can be
+ // reused for a new update after rollback.
accessor->rollback();
+ zone_id = accessor->startUpdateZone("example.com.", false).second;
checkRecords(*accessor, zone_id, "newdata.example.com.", empty_stored);
}
@@ -917,6 +1085,12 @@ TEST_F(SQLite3Update, duplicateAdd) {
TEST_F(SQLite3Update, invalidAdd) {
// An attempt of add before an explicit start of transaction
EXPECT_THROW(accessor->addRecordToZone(add_columns), DataSourceError);
+
+ // Same for addNSEC3.
+ copy(nsec3_data, nsec3_data + DatabaseAccessor::ADD_NSEC3_COLUMN_COUNT,
+ add_nsec3_columns);
+ EXPECT_THROW(accessor->addNSEC3RecordToZone(add_nsec3_columns),
+ DataSourceError);
}
TEST_F(SQLite3Update, deleteRecord) {
@@ -934,6 +1108,32 @@ TEST_F(SQLite3Update, deleteRecord) {
checkRecords(*accessor, zone_id, "foo.bar.example.com.", empty_stored);
}
+TEST_F(SQLite3Update, deleteNSEC3Record) {
+ // Similar to the previous test, but for NSEC3.
+ zone_id = accessor->startUpdateZone("example.com.", false).second;
+ checkNSEC3Records(*accessor, zone_id, apex_hash, empty_stored);
+
+ // We first need to add some record.
+ copy(nsec3_data, nsec3_data + DatabaseAccessor::ADD_NSEC3_COLUMN_COUNT,
+ add_nsec3_columns);
+ accessor->addNSEC3RecordToZone(add_nsec3_columns);
+
+ // Now it should exist.
+ expected_stored.clear();
+ expected_stored.push_back(nsec3_data);
+ checkNSEC3Records(*accessor, zone_id, apex_hash, expected_stored);
+
+ // Delete it, and confirm that.
+ copy(nsec3_deleted_data,
+ nsec3_deleted_data + DatabaseAccessor::DEL_PARAM_COUNT, del_params);
+ accessor->deleteNSEC3RecordInZone(del_params);
+ checkNSEC3Records(*accessor, zone_id, apex_hash, empty_stored);
+
+ // Commit the change, and confirm the deleted data still isn't there.
+ accessor->commit();
+ checkNSEC3Records(*accessor, zone_id, apex_hash, empty_stored);
+}
+
TEST_F(SQLite3Update, deleteThenRollback) {
zone_id = accessor->startUpdateZone("example.com.", false).second;
@@ -980,6 +1180,10 @@ TEST_F(SQLite3Update, deleteNonexistent) {
TEST_F(SQLite3Update, invalidDelete) {
// An attempt of delete before an explicit start of transaction
EXPECT_THROW(accessor->deleteRecordInZone(del_params), DataSourceError);
+
+ // Same for NSEC3.
+ EXPECT_THROW(accessor->deleteNSEC3RecordInZone(del_params),
+ DataSourceError);
}
TEST_F(SQLite3Update, emptyTransaction) {
diff --git a/src/lib/datasrc/tests/test_client.cc b/src/lib/datasrc/tests/test_client.cc
index 3974371..c7854ed 100644
--- a/src/lib/datasrc/tests/test_client.cc
+++ b/src/lib/datasrc/tests/test_client.cc
@@ -67,7 +67,7 @@ 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_prefix = INSTALL_PROG " " TEST_DATA_COMMONDIR
+ const char* const install_cmd_prefix = INSTALL_PROG " -c " TEST_DATA_COMMONDIR
"/rwtest.sqlite3 ";
const string install_cmd = string(install_cmd_prefix) + db_file;
if (system(install_cmd.c_str()) != 0) {
diff --git a/src/lib/dhcp/Makefile.am b/src/lib/dhcp/Makefile.am
index 5eada15..9d00911 100644
--- a/src/lib/dhcp/Makefile.am
+++ b/src/lib/dhcp/Makefile.am
@@ -13,6 +13,7 @@ libdhcp___la_SOURCES += libdhcp++.cc libdhcp++.h
libdhcp___la_SOURCES += iface_mgr.cc iface_mgr.h
libdhcp___la_SOURCES += iface_mgr_linux.cc
libdhcp___la_SOURCES += iface_mgr_bsd.cc
+libdhcp___la_SOURCES += iface_mgr_sun.cc
libdhcp___la_SOURCES += option.cc option.h
libdhcp___la_SOURCES += option6_ia.cc option6_ia.h
libdhcp___la_SOURCES += option6_iaaddr.cc option6_iaaddr.h
diff --git a/src/lib/dhcp/dhcp6.h b/src/lib/dhcp/dhcp6.h
index 6012003..15c306d 100644
--- a/src/lib/dhcp/dhcp6.h
+++ b/src/lib/dhcp/dhcp6.h
@@ -108,6 +108,15 @@ extern const int dhcpv6_type_name_max;
#define DUID_LLT 1
#define DUID_EN 2
#define DUID_LL 3
+#define DUID_UUID 4
+
+// Define hardware types
+// Taken from http://www.iana.org/assignments/arp-parameters/
+#define HWTYPE_ETHERNET 0x0001
+#define HWTYPE_INIFINIBAND 0x0020
+
+// Taken from http://www.iana.org/assignments/enterprise-numbers
+#define ENTERPRISE_ID_ISC 2495
/* Offsets into IA_*'s where Option spaces commence. */
#define IA_NA_OFFSET 12 /* IAID, T1, T2, all 4 octets each */
diff --git a/src/lib/dhcp/iface_mgr.cc b/src/lib/dhcp/iface_mgr.cc
index 14de5a0..1505fbf 100644
--- a/src/lib/dhcp/iface_mgr.cc
+++ b/src/lib/dhcp/iface_mgr.cc
@@ -23,13 +23,14 @@
#include <dhcp/dhcp6.h>
#include <dhcp/iface_mgr.h>
#include <exceptions/exceptions.h>
+#include <util/io/pktinfo_utilities.h>
using namespace std;
-using namespace isc;
using namespace isc::asiolink;
-using namespace isc::dhcp;
+using namespace isc::util::io::internal;
namespace isc {
+namespace dhcp {
/// IfaceMgr is a singleton implementation
IfaceMgr* IfaceMgr::instance_ = 0;
@@ -54,9 +55,9 @@ IfaceMgr::instance() {
}
IfaceMgr::Iface::Iface(const std::string& name, int ifindex)
- :name_(name), ifindex_(ifindex), mac_len_(0), flag_loopback_(false),
- flag_up_(false), flag_running_(false), flag_multicast_(false),
- flag_broadcast_(false), flags_(0), hardware_type_(0)
+ :name_(name), ifindex_(ifindex), mac_len_(0), hardware_type_(0),
+ flag_loopback_(false), flag_up_(false), flag_running_(false),
+ flag_multicast_(false), flag_broadcast_(false), flags_(0)
{
memset(mac_, 0, sizeof(mac_));
}
@@ -83,6 +84,17 @@ IfaceMgr::Iface::getPlainMac() const {
return (tmp.str());
}
+void IfaceMgr::Iface::setMac(const uint8_t* mac, size_t len) {
+ if (len > IfaceMgr::MAX_MAC_LEN) {
+ isc_throw(OutOfRange, "Interface " << getFullName()
+ << " was detected to have link address of length "
+ << len << ", but maximum supported length is "
+ << IfaceMgr::MAX_MAC_LEN);
+ }
+ mac_len_ = len;
+ memcpy(mac_, mac, len);
+}
+
bool IfaceMgr::Iface::delAddress(const isc::asiolink::IOAddress& addr) {
for (AddressCollection::iterator a = addrs_.begin();
a!=addrs_.end(); ++a) {
@@ -155,12 +167,12 @@ IfaceMgr::~IfaceMgr() {
closeSockets();
}
-void
-IfaceMgr::stubDetectIfaces() {
+void IfaceMgr::stubDetectIfaces() {
string ifaceName, linkLocal;
- // TODO do the actual detection. Currently interface detection is faked
- // by reading a text file.
+ // This is a stub implementation for interface detection. Actual detection
+ // is faked by reading a text file. It will eventually be removed once
+ // we have actual implementations for all supported systems.
cout << "Interface detection is not implemented yet. "
<< "Reading interfaces.txt file instead." << endl;
@@ -178,7 +190,13 @@ IfaceMgr::stubDetectIfaces() {
cout << "Detected interface " << ifaceName << "/" << linkLocal << endl;
- Iface iface(ifaceName, if_nametoindex( ifaceName.c_str() ) );
+ Iface iface(ifaceName, if_nametoindex(ifaceName.c_str()));
+ iface.flag_up_ = true;
+ iface.flag_running_ = true;
+ iface.flag_loopback_ = false;
+ iface.flag_multicast_ = true;
+ iface.flag_broadcast_ = true;
+ iface.setHWType(HWTYPE_ETHERNET);
IOAddress addr(linkLocal);
iface.addAddress(addr);
addInterface(iface);
@@ -195,13 +213,7 @@ IfaceMgr::stubDetectIfaces() {
}
}
-#if !defined(OS_LINUX) && !defined(OS_BSD)
-void IfaceMgr::detectIfaces() {
- stubDetectIfaces();
-}
-#endif
-
-bool IfaceMgr::openSockets4(uint16_t port) {
+bool IfaceMgr::openSockets4(const uint16_t port) {
int sock;
int count = 0;
@@ -241,7 +253,7 @@ bool IfaceMgr::openSockets4(uint16_t port) {
}
-bool IfaceMgr::openSockets6(uint16_t port) {
+bool IfaceMgr::openSockets6(const uint16_t port) {
int sock;
int count = 0;
@@ -272,6 +284,9 @@ bool IfaceMgr::openSockets6(uint16_t port) {
return (false);
}
+ // Binding socket to unicast address and then joining multicast group
+ // works well on Mac OS (and possibly other BSDs), but does not work
+ // on Linux.
if ( !joinMulticast(sock, iface->getName(),
string(ALL_DHCP_RELAY_AGENTS_AND_SERVERS) ) ) {
close(sock);
@@ -280,8 +295,12 @@ bool IfaceMgr::openSockets6(uint16_t port) {
}
count++;
+
+ /// @todo: Remove this ifdef once we start supporting BSD systems.
#if defined(OS_LINUX)
- // this doesn't work too well on NetBSD
+ // To receive multicast traffic, Linux requires binding socket to
+ // a multicast group. That in turn doesn't work on NetBSD.
+
int sock2 = openSocket(iface->getName(),
IOAddress(ALL_DHCP_RELAY_AGENTS_AND_SERVERS),
port);
@@ -305,8 +324,8 @@ IfaceMgr::printIfaces(std::ostream& out /*= std::cout*/) {
const AddressCollection& addrs = iface->getAddresses();
out << "Detected interface " << iface->getFullName()
- << ", hwtype=" << iface->hardware_type_ << ", maclen=" << iface->mac_len_
- << ", mac=" << iface->getPlainMac();
+ << ", hwtype=" << iface->getHWType()
+ << ", mac=" << iface->getPlainMac();
out << ", flags=" << hex << iface->flags_ << dec << "("
<< (iface->flag_loopback_?"LOOPBACK ":"")
<< (iface->flag_up_?"UP ":"")
@@ -348,9 +367,8 @@ IfaceMgr::getIface(const std::string& ifname) {
return (NULL); // not found
}
-int IfaceMgr::openSocket(const std::string& ifname,
- const IOAddress& addr,
- int port) {
+int IfaceMgr::openSocket(const std::string& ifname, const IOAddress& addr,
+ const uint16_t port) {
Iface* iface = getIface(ifname);
if (!iface) {
isc_throw(BadValue, "There is no " << ifname << " interface present.");
@@ -366,7 +384,7 @@ int IfaceMgr::openSocket(const std::string& ifname,
}
}
-int IfaceMgr::openSocket4(Iface& iface, const IOAddress& addr, int port) {
+int IfaceMgr::openSocket4(Iface& iface, const IOAddress& addr, uint16_t port) {
cout << "Creating UDP4 socket on " << iface.getFullName()
<< " " << addr.toText() << "/port=" << port << endl;
@@ -410,7 +428,7 @@ int IfaceMgr::openSocket4(Iface& iface, const IOAddress& addr, int port) {
return (sock);
}
-int IfaceMgr::openSocket6(Iface& iface, const IOAddress& addr, int port) {
+int IfaceMgr::openSocket6(Iface& iface, const IOAddress& addr, uint16_t port) {
cout << "Creating UDP6 socket on " << iface.getFullName()
<< " " << addr.toText() << "/port=" << port << endl;
@@ -437,8 +455,8 @@ int IfaceMgr::openSocket6(Iface& iface, const IOAddress& addr, int port) {
isc_throw(Unexpected, "Failed to create UDP6 socket.");
}
- /* Set the REUSEADDR option so that we don't fail to start if
- we're being restarted. */
+ // Set the REUSEADDR option so that we don't fail to start if
+ // we're being restarted.
int flag = 1;
if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
(char *)&flag, sizeof(flag)) < 0) {
@@ -452,14 +470,14 @@ int IfaceMgr::openSocket6(Iface& iface, const IOAddress& addr, int port) {
<< "/port=" << port);
}
#ifdef IPV6_RECVPKTINFO
- /* RFC3542 - a new way */
+ // RFC3542 - a new way
if (setsockopt(sock, IPPROTO_IPV6, IPV6_RECVPKTINFO,
&flag, sizeof(flag)) != 0) {
close(sock);
isc_throw(Unexpected, "setsockopt: IPV6_RECVPKTINFO failed.");
}
#else
- /* RFC2292 - an old way */
+ // RFC2292 - an old way
if (setsockopt(sock, IPPROTO_IPV6, IPV6_PKTINFO,
&flag, sizeof(flag)) != 0) {
close(sock);
@@ -516,42 +534,49 @@ const std::string & mcast) {
}
bool
-IfaceMgr::send(boost::shared_ptr<Pkt6>& pkt) {
- struct msghdr m;
- struct iovec v;
+IfaceMgr::send(const Pkt6Ptr& pkt) {
int result;
- struct in6_pktinfo *pktinfo;
- struct cmsghdr *cmsg;
- Iface* iface = getIface(pkt->iface_);
+ Iface* iface = getIface(pkt->getIface());
if (!iface) {
isc_throw(BadValue, "Unable to send Pkt6. Invalid interface ("
- << pkt->iface_ << ") specified.");
+ << pkt->getIface() << ") specified.");
}
memset(&control_buf_[0], 0, control_buf_len_);
- // Initialize our message header structure.
- memset(&m, 0, sizeof(m));
// Set the target address we're sending to.
sockaddr_in6 to;
memset(&to, 0, sizeof(to));
to.sin6_family = AF_INET6;
- to.sin6_port = htons(pkt->remote_port_);
+ to.sin6_port = htons(pkt->getRemotePort());
memcpy(&to.sin6_addr,
- pkt->remote_addr_.getAddress().to_v6().to_bytes().data(),
+ pkt->getRemoteAddr().getAddress().to_v6().to_bytes().data(),
16);
- to.sin6_scope_id = pkt->ifindex_;
+ to.sin6_scope_id = pkt->getIndex();
+ // Initialize our message header structure.
+ struct msghdr m;
+ memset(&m, 0, sizeof(m));
m.msg_name = &to;
m.msg_namelen = sizeof(to);
// Set the data buffer we're sending. (Using this wacky
// "scatter-gather" stuff... we only have a single chunk
// of data to send, so we declare a single vector entry.)
- v.iov_base = (char *) &pkt->data_[0];
- v.iov_len = pkt->data_len_;
+
+ // As v structure is a C-style is used for both sending and
+ // receiving data, it is shared between sending and receiving
+ // (sendmsg and recvmsg). It is also defined in system headers,
+ // so we have no control over its definition. To set iov_base
+ // (defined as void*) we must use const cast from void *.
+ // Otherwise C++ compiler would complain that we are trying
+ // to assign const void* to void*.
+ struct iovec v;
+ memset(&v, 0, sizeof(v));
+ v.iov_base = const_cast<void *>(pkt->getBuffer().getData());
+ v.iov_len = pkt->getBuffer().getLength();
m.msg_iov = &v;
m.msg_iovlen = 1;
@@ -563,34 +588,31 @@ IfaceMgr::send(boost::shared_ptr<Pkt6>& pkt) {
// kernel decide what that should be.
m.msg_control = &control_buf_[0];
m.msg_controllen = control_buf_len_;
- cmsg = CMSG_FIRSTHDR(&m);
+ struct cmsghdr *cmsg = CMSG_FIRSTHDR(&m);
cmsg->cmsg_level = IPPROTO_IPV6;
cmsg->cmsg_type = IPV6_PKTINFO;
- cmsg->cmsg_len = CMSG_LEN(sizeof(*pktinfo));
- pktinfo = (struct in6_pktinfo *)CMSG_DATA(cmsg);
- memset(pktinfo, 0, sizeof(*pktinfo));
- pktinfo->ipi6_ifindex = pkt->ifindex_;
+ cmsg->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo));
+ struct in6_pktinfo *pktinfo = convertPktInfo6(CMSG_DATA(cmsg));
+ memset(pktinfo, 0, sizeof(struct in6_pktinfo));
+ pktinfo->ipi6_ifindex = pkt->getIndex();
m.msg_controllen = cmsg->cmsg_len;
result = sendmsg(getSocket(*pkt), &m, 0);
if (result < 0) {
- cout << "Send packet failed." << endl;
+ isc_throw(Unexpected, "Pkt6 send failed: sendmsg() returned " << result);
}
- cout << "Sent " << pkt->data_len_ << " bytes over socket " << getSocket(*pkt)
+ cout << "Sent " << pkt->getBuffer().getLength() << " bytes over socket " << getSocket(*pkt)
<< " on " << iface->getFullName() << " interface: "
- << " dst=" << pkt->remote_addr_.toText()
- << ", src=" << pkt->local_addr_.toText()
+ << " dst=[" << pkt->getRemoteAddr().toText() << "]:" << pkt->getRemotePort()
+ << ", src=" << pkt->getLocalAddr().toText() << "]:" << pkt->getLocalPort()
<< endl;
return (result);
}
bool
-IfaceMgr::send(boost::shared_ptr<Pkt4>& pkt)
+IfaceMgr::send(const Pkt4Ptr& pkt)
{
- struct msghdr m;
- struct iovec v;
- int result;
Iface* iface = getIface(pkt->getIface());
if (!iface) {
@@ -600,8 +622,6 @@ IfaceMgr::send(boost::shared_ptr<Pkt4>& pkt)
memset(&control_buf_[0], 0, control_buf_len_);
- // Initialize our message header structure.
- memset(&m, 0, sizeof(m));
// Set the target address we're sending to.
sockaddr_in to;
@@ -610,45 +630,33 @@ IfaceMgr::send(boost::shared_ptr<Pkt4>& pkt)
to.sin_port = htons(pkt->getRemotePort());
to.sin_addr.s_addr = htonl(pkt->getRemoteAddr());
+ struct msghdr m;
+ // Initialize our message header structure.
+ memset(&m, 0, sizeof(m));
m.msg_name = &to;
m.msg_namelen = sizeof(to);
// Set the data buffer we're sending. (Using this wacky
// "scatter-gather" stuff... we only have a single chunk
// of data to send, so we declare a single vector entry.)
- v.iov_base = (char *) pkt->getBuffer().getData();
+ struct iovec v;
+ memset(&v, 0, sizeof(v));
+ // iov_base field is of void * type. We use it for packet
+ // transmission, so this buffer will not be modified.
+ v.iov_base = const_cast<void *>(pkt->getBuffer().getData());
v.iov_len = pkt->getBuffer().getLength();
m.msg_iov = &v;
m.msg_iovlen = 1;
-// OS_LINUX defines are part of ticket #1237
-#if defined(OS_LINUX)
- // Setting the interface is a bit more involved.
- //
- // We have to create a "control message", and set that to
- // define the IPv4 packet information. We could set the
- // source address if we wanted, but we can safely let the
- // kernel decide what that should be.
- struct in_pktinfo *pktinfo;
- struct cmsghdr *cmsg;
- m.msg_control = &control_buf_[0];
- m.msg_controllen = control_buf_len_;
- cmsg = CMSG_FIRSTHDR(&m);
- cmsg->cmsg_level = IPPROTO_IP;
- cmsg->cmsg_type = IP_PKTINFO;
- cmsg->cmsg_len = CMSG_LEN(sizeof(*pktinfo));
- pktinfo = (struct in_pktinfo *)CMSG_DATA(cmsg);
- memset(pktinfo, 0, sizeof(*pktinfo));
- pktinfo->ipi_ifindex = pkt->getIndex();
- m.msg_controllen = cmsg->cmsg_len;
-#endif
+ // call OS-specific routines (like setting interface index)
+ os_send4(m, control_buf_, control_buf_len_, pkt);
cout << "Trying to send " << pkt->getBuffer().getLength() << " bytes to "
<< pkt->getRemoteAddr().toText() << ":" << pkt->getRemotePort()
<< " over socket " << getSocket(*pkt) << " on interface "
<< getIface(pkt->getIface())->getFullName() << endl;
- result = sendmsg(getSocket(*pkt), &m, 0);
+ int result = sendmsg(getSocket(*pkt), &m, 0);
if (result < 0) {
isc_throw(Unexpected, "Pkt4 send failed.");
}
@@ -670,22 +678,12 @@ IfaceMgr::receive4() {
IfaceCollection::const_iterator iface;
for (iface = ifaces_.begin(); iface != ifaces_.end(); ++iface) {
-#if 0
- // uncomment this once #1237 is merged
- // Let's skip loopback and downed interfaces.
- if (iface->flag_loopback_ ||
- !iface->flag_up_ ||
- !iface->flag_running_) {
- continue;
- }
-#endif
-
- SocketCollection::const_iterator s = iface->sockets_.begin();
- while (s != iface->sockets_.end()) {
+ /// @todo: rewrite this as part of #1555
+ for (SocketCollection::const_iterator s = iface->sockets_.begin();
+ s != iface->sockets_.end(); ++s) {
// We don't want IPv6 addresses here.
if (s->addr_.getFamily() != AF_INET) {
- ++s;
continue;
}
@@ -694,8 +692,6 @@ IfaceMgr::receive4() {
candidate = &(*s);
break;
}
-
- ++s;
}
if (candidate) {
@@ -712,28 +708,22 @@ IfaceMgr::receive4() {
<< iface->getFullName() << endl;
// Now we have a socket, let's get some data from it!
-
- struct msghdr m;
- struct iovec v;
- int result;
struct sockaddr_in from_addr;
- struct in_addr to_addr;
- boost::shared_ptr<Pkt4> pkt;
- const uint32_t RCVBUFSIZE = 1500;
- static uint8_t buf[RCVBUFSIZE];
+ uint8_t buf[RCVBUFSIZE];
memset(&control_buf_[0], 0, control_buf_len_);
memset(&from_addr, 0, sizeof(from_addr));
- memset(&to_addr, 0, sizeof(to_addr));
// Initialize our message header structure.
+ struct msghdr m;
memset(&m, 0, sizeof(m));
// Point so we can get the from address.
m.msg_name = &from_addr;
m.msg_namelen = sizeof(from_addr);
- v.iov_base = (void*)buf;
+ struct iovec v;
+ v.iov_base = static_cast<void*>(buf);
v.iov_len = RCVBUFSIZE;
m.msg_iov = &v;
m.msg_iovlen = 1;
@@ -747,132 +737,73 @@ IfaceMgr::receive4() {
m.msg_control = &control_buf_[0];
m.msg_controllen = control_buf_len_;
- result = recvmsg(candidate->sockfd_, &m, 0);
-
+ int result = recvmsg(candidate->sockfd_, &m, 0);
if (result < 0) {
cout << "Failed to receive UDP4 data." << endl;
- return (boost::shared_ptr<Pkt4>()); // NULL
+ return (Pkt4Ptr()); // NULL
}
- unsigned int ifindex = iface->getIndex();
+ // We have all data let's create Pkt4 object.
+ Pkt4Ptr pkt = Pkt4Ptr(new Pkt4(buf, result));
-// OS_LINUX defines are part of ticket #1237
-#if defined(OS_LINUX)
- struct cmsghdr* cmsg;
- struct in_pktinfo* pktinfo;
-
- int found_pktinfo = 0;
- cmsg = CMSG_FIRSTHDR(&m);
- while (cmsg != NULL) {
- if ((cmsg->cmsg_level == IPPROTO_IP) &&
- (cmsg->cmsg_type == IP_PKTINFO)) {
- pktinfo = (struct in_pktinfo*)CMSG_DATA(cmsg);
-
- ifindex = pktinfo->ipi_ifindex;
- to_addr = pktinfo->ipi_addr;
-
- // This field is useful, when we are bound to unicast
- // address e.g. 192.0.2.1 and the packet was sent to
- // broadcast. This will return broadcast address, not
- // the address we are bound to.
-
- // IOAddress tmp(htonl(pktinfo->ipi_spec_dst.s_addr));
- // cout << "The other addr is: " << tmp.toText() << endl;
-
- // Perhaps we should uncomment this:
- // to_addr = pktinfo->ipi_spec_dst;
- found_pktinfo = 1;
- }
- cmsg = CMSG_NXTHDR(&m, cmsg);
- }
- if (!found_pktinfo) {
- cout << "Unable to find pktinfo" << endl;
- return (boost::shared_ptr<Pkt4>()); // NULL
- }
-#endif
+ unsigned int ifindex = iface->getIndex();
- IOAddress to(htonl(to_addr.s_addr));
IOAddress from(htonl(from_addr.sin_addr.s_addr));
uint16_t from_port = htons(from_addr.sin_port);
- cout << "Received " << result << " bytes from " << from.toText()
- << "/port=" << from_port
- << " sent to " << to.toText() << " over interface "
- << iface->getFullName() << endl;
-
- // we have all data let's create Pkt4 object
- pkt = boost::shared_ptr<Pkt4>(new Pkt4(buf, result));
-
- pkt->setIface(iface->getName());
+ // Set receiving interface based on information, which socket was used to
+ // receive data. OS-specific info (see os_receive4()) may be more reliable,
+ // so this value may be overwritten.
pkt->setIndex(ifindex);
- pkt->setLocalAddr(to);
+ pkt->setIface(iface->getName());
pkt->setRemoteAddr(from);
pkt->setRemotePort(from_port);
pkt->setLocalPort(candidate->port_);
+ if (!os_receive4(m, pkt)) {
+ cout << "Unable to find pktinfo" << endl;
+ return (boost::shared_ptr<Pkt4>()); // NULL
+ }
+
+ cout << "Received " << result << " bytes from " << from.toText()
+ << "/port=" << from_port
+ << " sent to " << pkt->getLocalAddr().toText() << " over interface "
+ << iface->getFullName() << endl;
+
return (pkt);
}
-boost::shared_ptr<Pkt6>
-IfaceMgr::receive6() {
- struct msghdr m;
- struct iovec v;
- int result;
- struct cmsghdr* cmsg;
- struct in6_pktinfo* pktinfo;
- struct sockaddr_in6 from;
- struct in6_addr to_addr;
- boost::shared_ptr<Pkt6> pkt;
- char addr_str[INET6_ADDRSTRLEN];
-
- try {
- // RFC3315 states that server responses may be
- // fragmented if they are over MTU. There is no
- // text whether client's packets may be larger
- // than 1500. Nevertheless to be on the safe side
- // we use larger buffer. This buffer limit is checked
- // during reception (see iov_len below), so we are
- // safe
- pkt = boost::shared_ptr<Pkt6>(new Pkt6(65536));
- } catch (const std::exception& ex) {
- cout << "Failed to create new packet." << endl;
- return (boost::shared_ptr<Pkt6>()); // NULL
- }
+Pkt6Ptr IfaceMgr::receive6() {
+ uint8_t buf[RCVBUFSIZE];
memset(&control_buf_[0], 0, control_buf_len_);
-
+ struct sockaddr_in6 from;
memset(&from, 0, sizeof(from));
- memset(&to_addr, 0, sizeof(to_addr));
- /*
- * Initialize our message header structure.
- */
+ // Initialize our message header structure.
+ struct msghdr m;
memset(&m, 0, sizeof(m));
- /*
- * Point so we can get the from address.
- */
+ // Point so we can get the from address.
m.msg_name = &from;
m.msg_namelen = sizeof(from);
- /*
- * Set the data buffer we're receiving. (Using this wacky
- * "scatter-gather" stuff... but we that doesn't really make
- * sense for us, so we use a single vector entry.)
- */
- v.iov_base = (void*)&pkt->data_[0];
- v.iov_len = pkt->data_len_;
+ // Set the data buffer we're receiving. (Using this wacky
+ // "scatter-gather" stuff... but we that doesn't really make
+ // sense for us, so we use a single vector entry.)
+ struct iovec v;
+ memset(&v, 0, sizeof(v));
+ v.iov_base = static_cast<void*>(buf);
+ v.iov_len = RCVBUFSIZE;
m.msg_iov = &v;
m.msg_iovlen = 1;
- /*
- * Getting the interface is a bit more involved.
- *
- * We set up some space for a "control message". We have
- * previously asked the kernel to give us packet
- * information (when we initialized the interface), so we
- * should get the destination address from that.
- */
+ // Getting the interface is a bit more involved.
+ //
+ // We set up some space for a "control message". We have
+ // previously asked the kernel to give us packet
+ // information (when we initialized the interface), so we
+ // should get the destination address from that.
m.msg_control = &control_buf_[0];
m.msg_controllen = control_buf_len_;
@@ -882,10 +813,9 @@ IfaceMgr::receive6() {
IfaceCollection::const_iterator iface = ifaces_.begin();
const SocketInfo* candidate = 0;
while (iface != ifaces_.end()) {
- SocketCollection::const_iterator s = iface->sockets_.begin();
- while (s != iface->sockets_.end()) {
+ for (SocketCollection::const_iterator s = iface->sockets_.begin();
+ s != iface->sockets_.end(); ++s) {
if (s->addr_.getFamily() != AF_INET6) {
- ++s;
continue;
}
if (s->addr_.getAddress().to_v6().is_multicast()) {
@@ -895,7 +825,6 @@ IfaceMgr::receive6() {
if (!candidate) {
candidate = &(*s); // it's not multicast, but it's better than nothing
}
- ++s;
}
if (candidate) {
break;
@@ -903,7 +832,7 @@ IfaceMgr::receive6() {
++iface;
}
if (iface == ifaces_.end()) {
- isc_throw(Unexpected, "No interfaces detected. Can't receive anything.");
+ isc_throw(Unexpected, "No suitable IPv6 interfaces detected. Can't receive anything.");
}
if (!candidate) {
@@ -914,125 +843,121 @@ IfaceMgr::receive6() {
cout << "Trying to receive over UDP6 socket " << candidate->sockfd_ << " bound to "
<< candidate->addr_.toText() << "/port=" << candidate->port_ << " on "
<< iface->getFullName() << endl;
- result = recvmsg(candidate->sockfd_, &m, 0);
+ int result = recvmsg(candidate->sockfd_, &m, 0);
+
+ struct in6_addr to_addr;
+ memset(&to_addr, 0, sizeof(to_addr));
+ int ifindex = -1;
if (result >= 0) {
- /*
- * If we did read successfully, then we need to loop
- * through the control messages we received and
- * find the one with our destination address.
- *
- * We also keep a flag to see if we found it. If we
- * didn't, then we consider this to be an error.
- */
- int found_pktinfo = 0;
- cmsg = CMSG_FIRSTHDR(&m);
+ struct in6_pktinfo* pktinfo = NULL;
+
+
+ // If we did read successfully, then we need to loop
+ // through the control messages we received and
+ // find the one with our destination address.
+ //
+ // We also keep a flag to see if we found it. If we
+ // didn't, then we consider this to be an error.
+ bool found_pktinfo = false;
+ struct cmsghdr* cmsg = CMSG_FIRSTHDR(&m);
while (cmsg != NULL) {
if ((cmsg->cmsg_level == IPPROTO_IPV6) &&
(cmsg->cmsg_type == IPV6_PKTINFO)) {
- pktinfo = (struct in6_pktinfo*)CMSG_DATA(cmsg);
+ pktinfo = convertPktInfo6(CMSG_DATA(cmsg));
to_addr = pktinfo->ipi6_addr;
- pkt->ifindex_ = pktinfo->ipi6_ifindex;
- found_pktinfo = 1;
+ ifindex = pktinfo->ipi6_ifindex;
+ found_pktinfo = true;
+ break;
}
cmsg = CMSG_NXTHDR(&m, cmsg);
}
if (!found_pktinfo) {
cout << "Unable to find pktinfo" << endl;
- return (boost::shared_ptr<Pkt6>()); // NULL
+ return (Pkt6Ptr()); // NULL
}
} else {
cout << "Failed to receive data." << endl;
- return (boost::shared_ptr<Pkt6>()); // NULL
+ return (Pkt6Ptr()); // NULL
}
- // That's ugly.
- // TODO add IOAddress constructor that will take struct in6_addr*
- // TODO: there's from_bytes() method added in IOAddress. Use it!
- inet_ntop(AF_INET6, &to_addr, addr_str,INET6_ADDRSTRLEN);
- pkt->local_addr_ = IOAddress(string(addr_str));
-
- // TODO: there's from_bytes() method added in IOAddress. Use it!
- inet_ntop(AF_INET6, &from.sin6_addr, addr_str, INET6_ADDRSTRLEN);
- pkt->remote_addr_ = IOAddress(string(addr_str));
+ // Let's create a packet.
+ Pkt6Ptr pkt;
+ try {
+ pkt = Pkt6Ptr(new Pkt6(buf, result));
+ } catch (const std::exception& ex) {
+ cout << "Failed to create new packet." << endl;
+ return (Pkt6Ptr()); // NULL
+ }
- pkt->remote_port_ = ntohs(from.sin6_port);
+ pkt->setLocalAddr(IOAddress::from_bytes(AF_INET6,
+ reinterpret_cast<const uint8_t*>(&to_addr)));
+ pkt->setRemoteAddr(IOAddress::from_bytes(AF_INET6,
+ reinterpret_cast<const uint8_t*>(&from.sin6_addr)));
+ pkt->setRemotePort(ntohs(from.sin6_port));
+ pkt->setIndex(ifindex);
- Iface* received = getIface(pkt->ifindex_);
+ Iface* received = getIface(pkt->getIndex());
if (received) {
- pkt->iface_ = received->getName();
+ pkt->setIface(received->getName());
} else {
cout << "Received packet over unknown interface (ifindex="
- << pkt->ifindex_ << ")." << endl;
+ << pkt->getIndex() << ")." << endl;
return (boost::shared_ptr<Pkt6>()); // NULL
}
- pkt->data_len_ = result;
-
- // TODO Move this to LOG_DEBUG
- cout << "Received " << pkt->data_len_ << " bytes over "
- << pkt->iface_ << "/" << pkt->ifindex_ << " interface: "
- << " src=" << pkt->remote_addr_.toText()
- << ", dst=" << pkt->local_addr_.toText()
+ /// @todo: Move this to LOG_DEBUG
+ cout << "Received " << pkt->getBuffer().getLength() << " bytes over "
+ << pkt->getIface() << "/" << pkt->getIndex() << " interface: "
+ << " src=" << pkt->getRemoteAddr().toText()
+ << ", dst=" << pkt->getLocalAddr().toText()
<< endl;
return (pkt);
}
-uint16_t
-IfaceMgr::getSocket(isc::dhcp::Pkt6 const& pkt) {
- Iface* iface = getIface(pkt.iface_);
- if (!iface) {
+uint16_t IfaceMgr::getSocket(const isc::dhcp::Pkt6& pkt) {
+ Iface* iface = getIface(pkt.getIface());
+ if (iface == NULL) {
isc_throw(BadValue, "Tried to find socket for non-existent interface "
- << pkt.iface_);
+ << pkt.getIface());
}
SocketCollection::const_iterator s;
for (s = iface->sockets_.begin(); s != iface->sockets_.end(); ++s) {
- if (s->family_ != AF_INET6) {
- // don't use IPv4 sockets
- continue;
- }
- if (s->addr_.getAddress().to_v6().is_multicast()) {
- // don't use IPv6 sockets bound to multicast address
- continue;
+ if ((s->family_ == AF_INET6) &&
+ (!s->addr_.getAddress().to_v6().is_multicast())) {
+ return (s->sockfd_);
}
- /// TODO: Add more checks here later. If remote address is
+ /// @todo: Add more checks here later. If remote address is
/// not link-local, we can't use link local bound socket
/// to send data.
-
- return (s->sockfd_);
}
isc_throw(Unexpected, "Interface " << iface->getFullName()
<< " does not have any suitable IPv6 sockets open.");
}
-uint16_t
-IfaceMgr::getSocket(isc::dhcp::Pkt4 const& pkt) {
+uint16_t IfaceMgr::getSocket(isc::dhcp::Pkt4 const& pkt) {
Iface* iface = getIface(pkt.getIface());
- if (!iface) {
+ if (iface == NULL) {
isc_throw(BadValue, "Tried to find socket for non-existent interface "
<< pkt.getIface());
}
SocketCollection::const_iterator s;
for (s = iface->sockets_.begin(); s != iface->sockets_.end(); ++s) {
- if (s->family_ != AF_INET) {
- // don't use IPv4 sockets
- continue;
+ if (s->family_ == AF_INET) {
+ return (s->sockfd_);
}
/// TODO: Add more checks here later. If remote address is
/// not link-local, we can't use link local bound socket
/// to send data.
-
- return (s->sockfd_);
}
isc_throw(Unexpected, "Interface " << iface->getFullName()
<< " does not have any suitable IPv4 sockets open.");
}
-
-
-}
+} // end of namespace isc::dhcp
+} // end of namespace isc
diff --git a/src/lib/dhcp/iface_mgr.h b/src/lib/dhcp/iface_mgr.h
index 5eba76b..b09a1ac 100644
--- a/src/lib/dhcp/iface_mgr.h
+++ b/src/lib/dhcp/iface_mgr.h
@@ -42,6 +42,15 @@ public:
/// maximum MAC address length (Infiniband uses 20 bytes)
static const unsigned int MAX_MAC_LEN = 20;
+ /// @brief Packet reception buffer size
+ ///
+ /// RFC3315 states that server responses may be
+ /// fragmented if they are over MTU. There is no
+ /// text whether client's packets may be larger
+ /// than 1500. For now, we can assume that
+ /// we don't support packets larger than 1500.
+ static const uint32_t RCVBUFSIZE = 1500;
+
/// Holds information about socket.
struct SocketInfo {
uint16_t sockfd_; /// socket descriptor
@@ -87,6 +96,23 @@ public:
/// @return MAC address as a plain text (string)
std::string getPlainMac() const;
+ /// @brief Sets MAC address of the interface.
+ ///
+ /// @param mac pointer to MAC address buffer
+ /// @param macLen length of mac address
+ void setMac(const uint8_t* mac, size_t macLen);
+
+ /// @brief Returns MAC length.
+ ///
+ /// @return length of MAC address
+ size_t getMacLen() const { return mac_len_; }
+
+ /// @brief Returns pointer to MAC address.
+ ///
+ /// Note: Returned pointer is only valid as long as the interface object
+ /// that returned it.
+ const uint8_t* getMac() const { return mac_; }
+
/// @brief Sets flag_*_ fields based on bitmask value returned by OS
///
/// Note: Implementation of this method is OS-dependent as bits have
@@ -105,6 +131,16 @@ public:
/// @return interface name
std::string getName() const { return name_; };
+ /// @brief Sets up hardware type of the interface.
+ ///
+ /// @param type hardware type
+ void setHWType(uint16_t type ) { hardware_type_ = type; }
+
+ /// @brief Returns hardware type of the interface.
+ ///
+ /// @return hardware type
+ uint16_t getHWType() const { return hardware_type_; }
+
/// @brief Returns all interfaces available on an interface.
///
/// Care should be taken to not use this collection after Iface object
@@ -140,7 +176,7 @@ public:
/// @brief Adds socket descriptor to an interface.
///
- /// @param socket SocketInfo structure that describes socket.
+ /// @param sock SocketInfo structure that describes socket.
void addSocket(const SocketInfo& sock)
{ sockets_.push_back(sock); }
@@ -149,7 +185,7 @@ public:
/// Closes socket and removes corresponding SocketInfo structure
/// from an interface.
///
- /// @param socket descriptor to be closed/removed.
+ /// @param sockfd socket descriptor to be closed/removed.
/// @return true if there was such socket, false otherwise
bool delSocket(uint16_t sockfd);
@@ -167,12 +203,18 @@ public:
/// list of assigned addresses
AddressCollection addrs_;
- public:
/// link-layer address
uint8_t mac_[MAX_MAC_LEN];
/// length of link-layer address (usually 6)
- int mac_len_;
+ size_t mac_len_;
+
+ /// hardware type
+ uint16_t hardware_type_;
+
+ public:
+ /// @todo: Make those fields protected once we start supporting more
+ /// than just Linux
/// specifies if selected interface is loopback
bool flag_loopback_;
@@ -193,9 +235,6 @@ public:
/// interface flags (this value is as is returned by OS,
/// it may mean different things on different OSes)
uint32_t flags_;
-
- /// hardware type
- uint16_t hardware_type_;
};
// TODO performance improvement: we may change this into
@@ -230,6 +269,15 @@ public:
Iface*
getIface(const std::string& ifname);
+ /// @brief Returns container with all interfaces.
+ ///
+ /// This reference is only valid as long as IfaceMgr is valid. However,
+ /// since IfaceMgr is a singleton and is expected to be destroyed after
+ /// main() function completes, you should not worry much about this.
+ ///
+ /// @return container with all interfaces.
+ const IfaceCollection& getIfaces() { return ifaces_; }
+
/// @brief Return most suitable socket for transmitting specified IPv6 packet.
///
/// This method takes Pkt6 (see overloaded implementation that takes
@@ -271,7 +319,7 @@ public:
/// @param pkt packet to be sent
///
/// @return true if sending was successful
- bool send(boost::shared_ptr<Pkt6>& pkt);
+ bool send(const Pkt6Ptr& pkt);
/// @brief Sends an IPv4 packet.
///
@@ -282,7 +330,7 @@ public:
/// @param pkt a packet to be sent
///
/// @return true if sending was successful
- bool send(boost::shared_ptr<Pkt4>& pkt);
+ bool send(const Pkt4Ptr& pkt);
/// @brief Tries to receive IPv6 packet over open IPv6 sockets.
///
@@ -295,7 +343,7 @@ public:
/// (e.g. remove expired leases)
///
/// @return Pkt6 object representing received packet (or NULL)
- boost::shared_ptr<Pkt6> receive6();
+ Pkt6Ptr receive6();
/// @brief Tries to receive IPv4 packet over open IPv4 sockets.
///
@@ -308,7 +356,7 @@ public:
/// (e.g. remove expired leases)
///
/// @return Pkt4 object representing received packet (or NULL)
- boost::shared_ptr<Pkt4> receive4();
+ Pkt4Ptr receive4();
/// Opens UDP/IP socket and binds it to address, interface and port.
///
@@ -325,7 +373,7 @@ public:
/// @return socket descriptor, if socket creation, binding and multicast
/// group join were all successful.
int openSocket(const std::string& ifname,
- const isc::asiolink::IOAddress& addr, int port);
+ const isc::asiolink::IOAddress& addr, const uint16_t port);
/// Opens IPv6 sockets on detected interfaces.
///
@@ -334,7 +382,7 @@ public:
/// @param port specifies port number (usually DHCP6_SERVER_PORT)
///
/// @return true if any sockets were open
- bool openSockets6(uint16_t port = DHCP6_SERVER_PORT);
+ bool openSockets6(const uint16_t port = DHCP6_SERVER_PORT);
/// @brief Closes all open sockets.
/// Is used in destructor, but also from Dhcpv4_srv and Dhcpv6_srv classes.
@@ -346,7 +394,7 @@ public:
/// @param port specifies port number (usually DHCP4_SERVER_PORT)
///
/// @return true if any sockets were open
- bool openSockets4(uint16_t port = DHCP4_SERVER_PORT);
+ bool openSockets4(const uint16_t port = DHCP4_SERVER_PORT);
/// @brief returns number of detected interfaces
///
@@ -375,7 +423,7 @@ protected:
/// @param port a port that created socket should be bound to
///
/// @return socket descriptor
- int openSocket4(Iface& iface, const isc::asiolink::IOAddress& addr, int port);
+ int openSocket4(Iface& iface, const isc::asiolink::IOAddress& addr, uint16_t port);
/// @brief Opens IPv6 socket.
///
@@ -388,7 +436,7 @@ protected:
/// @param port a port that created socket should be bound to
///
/// @return socket descriptor
- int openSocket6(Iface& iface, const isc::asiolink::IOAddress& addr, int port);
+ int openSocket6(Iface& iface, const isc::asiolink::IOAddress& addr, uint16_t port);
/// @brief Adds an interface to list of known interfaces.
///
@@ -434,14 +482,32 @@ protected:
// to people who try to use multicast as source address.
/// length of the control_buf_ array
- int control_buf_len_;
+ size_t control_buf_len_;
/// control-buffer, used in transmission and reception
boost::scoped_array<char> control_buf_;
+
+ /// @brief A wrapper for OS-specific operations before sending IPv4 packet
+ ///
+ /// @param m message header (will be later used for sendmsg() call)
+ /// @param control_buf buffer to be used during transmission
+ /// @param control_buf_len buffer length
+ /// @param pkt packet to be sent
+ void os_send4(struct msghdr& m, boost::scoped_array<char>& control_buf,
+ size_t control_buf_len, const Pkt4Ptr& pkt);
+
+ /// @brief OS-specific operations during IPv4 packet reception
+ ///
+ /// @param m message header (was used during recvmsg() call)
+ /// @param pkt packet received (some fields will be set here)
+ ///
+ /// @return true if successful, false otherwise
+ bool os_receive4(struct msghdr& m, Pkt4Ptr& pkt);
+
private:
- /// creates a single instance of this class (a singleton implementation)
+ /// @brief Creates a single instance of this class (a singleton implementation)
static void
instanceCreate();
diff --git a/src/lib/dhcp/iface_mgr_bsd.cc b/src/lib/dhcp/iface_mgr_bsd.cc
index 7786b92..e3f11a1 100644
--- a/src/lib/dhcp/iface_mgr_bsd.cc
+++ b/src/lib/dhcp/iface_mgr_bsd.cc
@@ -25,14 +25,31 @@ using namespace isc::asiolink;
using namespace isc::dhcp;
namespace isc {
+namespace dhcp {
void
IfaceMgr::detectIfaces() {
- // TODO do the actual detection on BSDs. Currently just calling
- // stub implementation.
+ /// @todo do the actual detection on BSDs. Currently just calling
+ /// stub implementation.
stubDetectIfaces();
}
+void IfaceMgr::os_send4(struct msghdr& /*m*/,
+ boost::scoped_array<char>& /*control_buf*/,
+ size_t /*control_buf_len*/,
+ const Pkt4Ptr& /*pkt*/) {
+ // @todo: Are there any specific actions required before sending IPv4 packet
+ // on BSDs? See iface_mgr_linux.cc for working Linux implementation.
}
+bool IfaceMgr::os_receive4(struct msghdr& /*m*/, Pkt4Ptr& /*pkt*/) {
+ // @todo: Are there any specific actions required before receiving IPv4 packet
+ // on BSDs? See iface_mgr_linux.cc for working Linux implementation.
+
+ return (true); // pretend that we have everything set up for reception.
+}
+
+} // end of isc::dhcp namespace
+} // end of dhcp namespace
+
#endif
diff --git a/src/lib/dhcp/iface_mgr_linux.cc b/src/lib/dhcp/iface_mgr_linux.cc
index 8b19c27..90431de 100644
--- a/src/lib/dhcp/iface_mgr_linux.cc
+++ b/src/lib/dhcp/iface_mgr_linux.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
@@ -12,6 +12,20 @@
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
+/// @file
+/// Access to interface information on Linux is via netlink, a socket-based
+/// method for transferring information between the kernel and user processes.
+///
+/// For detailed information about netlink interface, please refer to
+/// http://en.wikipedia.org/wiki/Netlink and RFC3549. Comments in the
+/// detectIfaces() method (towards the end of this file) provide an overview
+/// on how the netlink interface is used here.
+///
+/// Note that this interface is very robust and allows many operations:
+/// add/get/set/delete links, addresses, routes, queuing, manipulation of
+/// traffic classes, manipulation of neighbourhood tables and even the ability
+/// to do something with address labels. Getting a list of interfaces with
+/// addresses configured on it is just a small subset of all possible actions.
#include <config.h>
@@ -20,135 +34,233 @@
#include <dhcp/iface_mgr.h>
#include <exceptions/exceptions.h>
+#include <stdint.h>
#include <net/if.h>
#include <linux/rtnetlink.h>
+#include <boost/array.hpp>
+#include <boost/static_assert.hpp>
+#include <dhcp/iface_mgr.h>
+#include <exceptions/exceptions.h>
+#include <asiolink/io_address.h>
+#include <util/io/sockaddr_util.h>
using namespace std;
using namespace isc;
using namespace isc::asiolink;
using namespace isc::dhcp;
+using namespace isc::util::io::internal;
-/// This is a structure that defines context for netlink connection.
-struct rtnl_handle
-{
- int fd;
- struct sockaddr_nl local;
- struct sockaddr_nl peer;
- __u32 seq;
- __u32 dump;
-};
+BOOST_STATIC_ASSERT(IFLA_MAX>=IFA_MAX);
+
+namespace {
-struct nlmsg_list
+/// @brief This class offers utility methods for netlink connection.
+///
+/// See IfaceMgr::detectIfaces() (Linux implementation, towards the end of this
+/// file) for example usage.
+class Netlink
{
- struct nlmsg_list *next;
- struct nlmsghdr h;
-};
+public:
-const int sndbuf = 32768;
-const int rcvbuf = 32768;
+/// @brief Holds pointers to netlink messages.
+///
+/// netlink (a Linux interface for getting information about network
+/// interfaces) uses memory aliasing. Linux kernel returns a memory
+/// blob that should be interpreted as series of nlmessages. There
+/// are different nlmsg structures defined with varying size. They
+/// have one thing common - inital fields are laid out in the same
+/// way as nlmsghdr. Therefore different messages can be represented
+/// as nlmsghdr with followed variable number of bytes that are
+/// message-specific. The only reasonable way to represent this in
+/// C++ is to use vector of pointers to nlmsghdr (the common structure).
+ typedef vector<nlmsghdr*> NetlinkMessages;
+
+/// @brief Holds pointers to interface or address attributes.
+///
+/// Note that to get address info, a shorter (IFA_MAX rather than IFLA_MAX)
+/// table could be used, but we will use the bigger one anyway to
+/// make the code reusable.
+///
+/// rtattr is a generic structure, similar to sockaddr. It is defined
+/// in linux/rtnetlink.h and shown here for documentation purposes only:
+///
+/// struct rtattr {
+/// unsigned short<>rta_len;
+/// unsigned short<>rta_type;
+/// };
+ typedef boost::array<struct rtattr*, IFLA_MAX + 1> RTattribPtrs;
+
+ Netlink() : fd_(-1), seq_(0), dump_(0) {
+ memset(&local_, 0, sizeof(struct sockaddr_nl));
+ memset(&peer_, 0, sizeof(struct sockaddr_nl));
+ }
-namespace isc {
+ ~Netlink() {
+ rtnl_close_socket();
+ }
+
+
+ void rtnl_open_socket();
+ void rtnl_send_request(int family, int type);
+ void rtnl_store_reply(NetlinkMessages& storage, const nlmsghdr* msg);
+ void parse_rtattr(RTattribPtrs& table, rtattr* rta, int len);
+ void ipaddrs_get(IfaceMgr::Iface& iface, NetlinkMessages& addr_info);
+ void rtnl_process_reply(NetlinkMessages& info);
+ void release_list(NetlinkMessages& messages);
+ void rtnl_close_socket();
+
+private:
+ int fd_; // Netlink file descriptor
+ sockaddr_nl local_; // Local addresses
+ sockaddr_nl peer_; // Remote address
+ uint32_t seq_; // Counter used for generating unique sequence numbers
+ uint32_t dump_; // Number of expected message response
+};
+/// @brief defines a size of a sent netlink buffer
+const static size_t SNDBUF_SIZE = 32768;
+
+/// @brief defines a size of a received netlink buffer
+const static size_t RCVBUF_SIZE = 32768;
/// @brief Opens netlink socket and initializes handle structure.
///
/// @exception Unexpected Thrown if socket configuration fails.
-///
-/// @param handle Context will be stored in this structure.
-void rtnl_open_socket(struct rtnl_handle& handle) {
- // equivalent of rtnl_open
- handle.fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
- if (handle.fd < 0) {
+void Netlink::rtnl_open_socket() {
+
+ fd_ = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
+ if (fd_ < 0) {
isc_throw(Unexpected, "Failed to create NETLINK socket.");
}
- if (setsockopt(handle.fd, SOL_SOCKET, SO_SNDBUF, &sndbuf, sizeof(sndbuf)) < 0) {
+ if (setsockopt(fd_, SOL_SOCKET, SO_SNDBUF, &SNDBUF_SIZE, sizeof(SNDBUF_SIZE)) < 0) {
isc_throw(Unexpected, "Failed to set send buffer in NETLINK socket.");
}
- if (setsockopt(handle.fd, SOL_SOCKET, SO_RCVBUF, &sndbuf, sizeof(rcvbuf)) < 0) {
+ if (setsockopt(fd_, SOL_SOCKET, SO_RCVBUF, &RCVBUF_SIZE, sizeof(RCVBUF_SIZE)) < 0) {
isc_throw(Unexpected, "Failed to set receive buffer in NETLINK socket.");
}
- memset(&handle.local, 0, sizeof(handle.local));
- handle.local.nl_family = AF_NETLINK;
- handle.local.nl_groups = 0;
+ local_.nl_family = AF_NETLINK;
+ local_.nl_groups = 0;
- if (bind(handle.fd, (struct sockaddr*)&handle.local, sizeof(handle.local)) < 0) {
+ if (bind(fd_, convertSockAddr(&local_), sizeof(local_)) < 0) {
isc_throw(Unexpected, "Failed to bind netlink socket.");
}
- socklen_t addr_len = sizeof(handle.local);
- if (getsockname(handle.fd, (struct sockaddr*)&handle.local, &addr_len) < 0) {
+ socklen_t addr_len = sizeof(local_);
+ if (getsockname(fd_, convertSockAddr(&local_), &addr_len) < 0) {
isc_throw(Unexpected, "Getsockname for netlink socket failed.");
}
// just 2 sanity checks and we are done
- if ( (addr_len != sizeof(handle.local)) ||
- (handle.local.nl_family != AF_NETLINK) ) {
+ if ( (addr_len != sizeof(local_)) ||
+ (local_.nl_family != AF_NETLINK) ) {
isc_throw(Unexpected, "getsockname() returned unexpected data for netlink socket.");
}
}
+/// @brief Closes netlink communication socket
+void Netlink::rtnl_close_socket() {
+ if (fd_ != -1) {
+ close(fd_);
+ }
+ fd_ = -1;
+}
+
/// @brief Sends request over NETLINK socket.
///
-/// @param handle context structure
-/// @param family requested information family
-/// @param type request type (RTM_GETLINK or RTM_GETADDR)
-void rtnl_send_request(struct rtnl_handle& handle, int family, int type) {
- struct {
- struct nlmsghdr nlh;
- struct rtgenmsg g;
- } req;
+/// @param family requested information family.
+/// @param type request type (RTM_GETLINK or RTM_GETADDR).
+void Netlink::rtnl_send_request(int family, int type) {
+ struct Req {
+ nlmsghdr netlink_header;
+ rtgenmsg generic;
+ };
+ Req req; // we need this type named for offsetof() used in assert
struct sockaddr_nl nladdr;
+ // do a sanity check. Verify that Req structure is aligned properly
+ BOOST_STATIC_ASSERT(sizeof(nlmsghdr) == offsetof(Req, generic));
+
memset(&nladdr, 0, sizeof(nladdr));
nladdr.nl_family = AF_NETLINK;
+ // According to netlink(7) manpage, mlmsg_seq must be set to a sequence
+ // number and is used to track messages. That is just a value that is
+ // opaque to kernel, and user-space code is supposed to use it to match
+ // incoming responses to sent requests. That is not really useful as we
+ // send a single request and get a single response at a time. However, we
+ // obey the man page suggestion and just set this to monotonically
+ // increasing numbers.
+ seq_++;
+
+ // This will be used to finding correct response (responses
+ // sent by kernel are supposed to have the same sequence number
+ // as the request we sent).
+ dump_ = seq_;
+
memset(&req, 0, sizeof(req));
- req.nlh.nlmsg_len = sizeof(req);
- req.nlh.nlmsg_type = type;
- req.nlh.nlmsg_flags = NLM_F_ROOT|NLM_F_MATCH|NLM_F_REQUEST;
- req.nlh.nlmsg_pid = 0;
- req.nlh.nlmsg_seq = handle.dump = ++handle.seq;
- req.g.rtgen_family = family;
+ req.netlink_header.nlmsg_len = sizeof(req);
+ req.netlink_header.nlmsg_type = type;
+ req.netlink_header.nlmsg_flags = NLM_F_ROOT | NLM_F_MATCH | NLM_F_REQUEST;
+ req.netlink_header.nlmsg_pid = 0;
+ req.netlink_header.nlmsg_seq = seq_;
+ req.generic.rtgen_family = family;
- int status = sendto(handle.fd, (void*)&req, sizeof(req), 0,
- (struct sockaddr*)&nladdr, sizeof(nladdr));
+ int status = sendto(fd_, static_cast<void*>(&req), sizeof(req), 0,
+ static_cast<struct sockaddr*>(static_cast<void*>(&nladdr)),
+ sizeof(nladdr));
if (status<0) {
- isc_throw(Unexpected, "Failed to send " << sizeof(nladdr) << " bytes over netlink socket.");
+ isc_throw(Unexpected, "Failed to send " << sizeof(nladdr)
+ << " bytes over netlink socket.");
}
}
-/// @brief Appends nlmsg to a list
+/// @brief Appends nlmsg to a storage.
///
-/// @param n a message to be added
-/// @param link_info a list
-void rtnl_store_reply(struct nlmsghdr *n, struct nlmsg_list** link_info)
+/// This method copies pointed nlmsg to a newly allocated memory
+/// and adds it to storage.
+///
+/// @param storage A vector that holds pointers to netlink messages. The caller
+/// is responsible for freeing the pointed-to messages.
+/// @param msg A netlink message to be added.
+void Netlink::rtnl_store_reply(NetlinkMessages& storage, const struct nlmsghdr *msg)
{
- struct nlmsg_list *h;
- struct nlmsg_list **lp;
-
- h = (nlmsg_list*)malloc(n->nlmsg_len+sizeof(void*));
- if (h == NULL) {
- isc_throw(Unexpected, "Failed to allocate " << n->nlmsg_len+sizeof(void*)
- << " bytes.");
- }
-
- memcpy(&h->h, n, n->nlmsg_len);
- h->next = NULL;
-
- for (lp = link_info; *lp; lp = &(*lp)->next) /* NOTHING */;
- *lp = h;
+ // we need to make a copy of this message. We really can't allocate
+ // nlmsghdr directly as it is only part of the structure. There are
+ // many message types with varying lengths and a common header.
+ struct nlmsghdr* copy = reinterpret_cast<struct nlmsghdr*>(new char[msg->nlmsg_len]);
+ memcpy(copy, msg, msg->nlmsg_len);
+
+ // push_back copies only pointer content, not the pointed-to object.
+ storage.push_back(copy);
}
-void parse_rtattr(struct rtattr *tb[], int max, struct rtattr *rta, int len)
+/// @brief Parses rtattr message.
+///
+/// Some netlink messages represent address information. Such messages
+/// are concatenated collection of rtaddr structures. This function
+/// iterates over that list and stores pointers to those messages in
+/// flat array (table).
+///
+/// @param table rtattr Messages will be stored here
+/// @param rta Pointer to first rtattr object
+/// @param len Length (in bytes) of concatenated rtattr list.
+void Netlink::parse_rtattr(RTattribPtrs& table, struct rtattr* rta, int len)
{
- memset(tb, 0, sizeof(struct rtattr *) * (max + 1));
+ std::fill(table.begin(), table.end(), static_cast<struct rtattr*>(NULL));
+ // RTA_OK and RTA_NEXT() are macros defined in linux/rtnetlink.h
+ // they are used to handle rtattributes. RTA_OK checks if the structure
+ // pointed by rta is reasonable and passes all sanity checks.
+ // RTA_NEXT() returns pointer to the next rtattr structure that
+ // immediately follows pointed rta structure. See aforementioned
+ // header for details.
while (RTA_OK(rta, len)) {
- if (rta->rta_type <= max)
- tb[rta->rta_type] = rta;
+ if (rta->rta_type < table.size()) {
+ table[rta->rta_type] = rta;
+ }
rta = RTA_NEXT(rta,len);
}
if (len) {
@@ -156,114 +268,122 @@ void parse_rtattr(struct rtattr *tb[], int max, struct rtattr *rta, int len)
}
}
-void ipaddrs_get(IfaceMgr::Iface& iface, struct nlmsg_list *addr_info) {
- uint8_t addr[16];
- struct rtattr * rta_tb[IFA_MAX+1];
-
- for ( ;addr_info ; addr_info = addr_info->next) {
- struct nlmsghdr *n = &addr_info->h;
- struct ifaddrmsg *ifa = (ifaddrmsg*)NLMSG_DATA(n);
-
- // these are not the addresses you are looking for
- if ( ifa->ifa_index != iface.getIndex()) {
+/// @brief Parses addr_info and appends appropriate addresses to Iface object.
+///
+/// Netlink is a fine, but convoluted interface. It returns a concatenated
+/// collection of netlink messages. Some of those messages convey information
+/// about addresses. Those messages are in fact appropriate header followed
+/// by concatenated lists of rtattr structures that define various pieces
+/// of address information.
+///
+/// @param iface interface representation (addresses will be added here)
+/// @param addr_info collection of parsed netlink messages
+void Netlink::ipaddrs_get(IfaceMgr::Iface& iface, NetlinkMessages& addr_info) {
+ uint8_t addr[V6ADDRESS_LEN];
+ RTattribPtrs rta_tb;
+
+ for (NetlinkMessages::const_iterator msg = addr_info.begin();
+ msg != addr_info.end(); ++msg) {
+ ifaddrmsg* ifa = static_cast<ifaddrmsg*>(NLMSG_DATA(*msg));
+
+ // These are not the addresses you are looking for
+ if (ifa->ifa_index != iface.getIndex()) {
continue;
}
- if ( ifa->ifa_family == AF_INET6 ) {
- memset(rta_tb, 0, sizeof(rta_tb));
- parse_rtattr(rta_tb, IFA_MAX, IFA_RTA(ifa), n->nlmsg_len - NLMSG_LENGTH(sizeof(*ifa)));
- if (!rta_tb[IFA_LOCAL])
- rta_tb[IFA_LOCAL] = rta_tb[IFA_ADDRESS];
- if (!rta_tb[IFA_ADDRESS])
+ if ((ifa->ifa_family == AF_INET6) || (ifa->ifa_family == AF_INET)) {
+ std::fill(rta_tb.begin(), rta_tb.end(), static_cast<rtattr*>(NULL));
+ parse_rtattr(rta_tb, IFA_RTA(ifa), (*msg)->nlmsg_len - NLMSG_LENGTH(sizeof(*ifa)));
+ if (!rta_tb[IFA_LOCAL]) {
+ rta_tb[IFA_LOCAL] = rta_tb[IFA_ADDRESS];
+ }
+ if (!rta_tb[IFA_ADDRESS]) {
rta_tb[IFA_ADDRESS] = rta_tb[IFA_LOCAL];
+ }
- memcpy(addr,(char*)RTA_DATA(rta_tb[IFLA_ADDRESS]),16);
- IOAddress a = IOAddress::from_bytes(AF_INET6, addr);
+ memcpy(addr, RTA_DATA(rta_tb[IFLA_ADDRESS]),
+ ifa->ifa_family==AF_INET?V4ADDRESS_LEN:V6ADDRESS_LEN);
+ IOAddress a = IOAddress::from_bytes(ifa->ifa_family, addr);
iface.addAddress(a);
- /// TODO: Read lifetimes of configured addresses
- }
-
- if ( ifa->ifa_family == AF_INET ) {
- memset(rta_tb, 0, sizeof(rta_tb));
- parse_rtattr(rta_tb, IFA_MAX, IFA_RTA(ifa), n->nlmsg_len - NLMSG_LENGTH(sizeof(*ifa)));
- if (!rta_tb[IFA_LOCAL])
- rta_tb[IFA_LOCAL] = rta_tb[IFA_ADDRESS];
- if (!rta_tb[IFA_ADDRESS])
- rta_tb[IFA_ADDRESS] = rta_tb[IFA_LOCAL];
-
- memcpy(addr,(char*)RTA_DATA(rta_tb[IFLA_ADDRESS]),4);
- IOAddress a = IOAddress::from_bytes(AF_INET, addr);
- iface.addAddress(a);
+ /// TODO: Read lifetimes of configured IPv6 addresses
}
}
}
-
-void rtnl_process_reply(struct rtnl_handle &rth, struct nlmsg_list *&info) {
-
- struct sockaddr_nl nladdr;
- struct iovec iov;
- struct msghdr msg;
- memset(&msg, 0, sizeof(struct msghdr));
+/// @brief Processes reply received over netlink socket.
+///
+/// This method parses the received buffer (a collection of concatenated
+/// netlink messages), copies each received message to newly allocated
+/// memory and stores pointers to it in the "info" container.
+///
+/// @param info received netlink messages will be stored here. It is the
+/// caller's responsibility to release the memory associated with the
+/// messages by calling the release_list() method.
+void Netlink::rtnl_process_reply(NetlinkMessages& info) {
+ sockaddr_nl nladdr;
+ iovec iov;
+ msghdr msg;
+ memset(&msg, 0, sizeof(msghdr));
msg.msg_name = &nladdr;
msg.msg_namelen = sizeof(nladdr);
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
- char buf[rcvbuf];
+ char buf[RCVBUF_SIZE];
iov.iov_base = buf;
- while (1) {
- int status;
- struct nlmsghdr *h;
-
- iov.iov_len = sizeof(buf);
- status = recvmsg(rth.fd, &msg, 0);
+ iov.iov_len = sizeof(buf);
+ while (true) {
+ int status = recvmsg(fd_, &msg, 0);
if (status < 0) {
- if (errno == EINTR)
+ if (errno == EINTR) {
continue;
- isc_throw(Unexpected, "Overrun while processing reply from netlink socket.");
+ }
+ isc_throw(Unexpected, "Error " << errno
+ << " while processing reply from netlink socket.");
}
if (status == 0) {
isc_throw(Unexpected, "EOF while reading netlink socket.");
}
- h = (struct nlmsghdr*)buf;
- while (NLMSG_OK(h, status)) {
+ nlmsghdr* header = static_cast<nlmsghdr*>(static_cast<void*>(buf));
+ while (NLMSG_OK(header, status)) {
- // why we received this anyway?
+ // Received a message not addressed to our process, or not
+ // with a sequence number we are expecting. Ignore, and
+ // look at the next one.
if (nladdr.nl_pid != 0 ||
- h->nlmsg_pid != rth.local.nl_pid ||
- h->nlmsg_seq != rth.dump) {
- h = NLMSG_NEXT(h, status);
+ header->nlmsg_pid != local_.nl_pid ||
+ header->nlmsg_seq != dump_) {
+ header = NLMSG_NEXT(header, status);
continue;
}
- if (h->nlmsg_type == NLMSG_DONE) {
- // end of message
+ if (header->nlmsg_type == NLMSG_DONE) {
+ // End of message.
return;
}
- if (h->nlmsg_type == NLMSG_ERROR) {
- struct nlmsgerr *err = (struct nlmsgerr*)NLMSG_DATA(h);
- if (h->nlmsg_len < NLMSG_LENGTH(sizeof(struct nlmsgerr))) {
- // we are really out of luck here. We can't even say what is
+ if (header->nlmsg_type == NLMSG_ERROR) {
+ nlmsgerr* err = static_cast<nlmsgerr*>(NLMSG_DATA(header));
+ if (header->nlmsg_len < NLMSG_LENGTH(sizeof(struct nlmsgerr))) {
+ // We are really out of luck here. We can't even say what is
// wrong as error message is truncated. D'oh.
isc_throw(Unexpected, "Netlink reply read failed.");
} else {
isc_throw(Unexpected, "Netlink reply read error " << -err->error);
}
- // never happens we throw before we reach here
+ // Never happens we throw before we reach here
return;
}
// store the data
- rtnl_store_reply(h, &info);
+ rtnl_store_reply(info, header);
- h = NLMSG_NEXT(h, status);
+ header = NLMSG_NEXT(header, status);
}
if (msg.msg_flags & MSG_TRUNC) {
isc_throw(Unexpected, "Message received over netlink truncated.");
@@ -274,87 +394,107 @@ void rtnl_process_reply(struct rtnl_handle &rth, struct nlmsg_list *&info) {
}
}
-/// @brief releases nlmsg list
+/// @brief releases nlmsg structure
///
-/// @param head first element of the list to be released
-void release_list(struct nlmsg_list *head) {
- struct nlmsg_list *tmp;
- while (head) {
- tmp = head->next;
- free(head);
- head = tmp;
+/// @param messages Set of messages to be freed.
+void Netlink::release_list(NetlinkMessages& messages) {
+ // let's free local copies of stored messages
+ for (NetlinkMessages::iterator msg = messages.begin(); msg != messages.end(); ++msg) {
+ delete (*msg);
}
+
+ // ang get rid of the message pointers as well
+ messages.clear();
}
+} // end of anonymous namespace
+
+namespace isc {
+namespace dhcp {
+
+/// @brief Detect available interfaces on Linux systems.
+///
+/// Uses the socket-based netlink protocol to retrieve the list of interfaces
+/// from the Linux kernel.
void IfaceMgr::detectIfaces() {
cout << "Linux: detecting interfaces." << endl;
- struct nlmsg_list* link_info = NULL; // link info list
- struct nlmsg_list* addr_info = NULL; // address info list
- struct nlmsg_list* l = NULL;
- struct rtnl_handle rth;
+ // Copies of netlink messages about links will be stored here.
+ Netlink::NetlinkMessages link_info;
+
+ // Copies of netlink messages about addresses will be stored here.
+ Netlink::NetlinkMessages addr_info;
+
+ // Socket descriptors and other rtnl-related parameters.
+ Netlink nl;
- // required to display information about interface
- struct ifinfomsg* ifi = NULL;
- struct rtattr* tb[IFLA_MAX+1];
- int len = 0;
- memset(tb, 0, sizeof(tb));
- memset(&rth,0, sizeof(rth));
+ // Table with pointers to address attributes.
+ Netlink::RTattribPtrs attribs_table;
+ std::fill(attribs_table.begin(), attribs_table.end(),
+ static_cast<struct rtattr*>(NULL));
- // open socket
- rtnl_open_socket(rth);
+ // Open socket
+ nl.rtnl_open_socket();
- // now we have open functional socket, let's use it!
- // ask for list of interface...
- rtnl_send_request(rth, AF_PACKET, RTM_GETLINK);
+ // Now we have open functional socket, let's use it!
+ // Ask for list of network interfaces...
+ nl.rtnl_send_request(AF_PACKET, RTM_GETLINK);
- // Get reply and store it in link_info list.
- rtnl_process_reply(rth, link_info);
+ // Get reply and store it in link_info list:
+ // response is received as with any other socket - just a series
+ // of bytes. They are representing collection of netlink messages
+ // concatenated together. rtnl_process_reply will parse this
+ // buffer, copy each message to a newly allocated memory and
+ // store pointers to it in link_info. This allocated memory will
+ // be released later. See release_info(link_info) below.
+ nl.rtnl_process_reply(link_info);
// Now ask for list of addresses (AF_UNSPEC = of any family)
- rtnl_send_request(rth, AF_UNSPEC, RTM_GETADDR);
+ // Let's repeat, but this time ask for any addresses.
+ // That includes IPv4, IPv6 and any other address families that
+ // are happen to be supported by this system.
+ nl.rtnl_send_request(AF_UNSPEC, RTM_GETADDR);
// Get reply and store it in addr_info list.
- rtnl_process_reply(rth, addr_info);
+ // Again, we will allocate new memory and store messages in
+ // addr_info. It will be released later using release_info(addr_info).
+ nl.rtnl_process_reply(addr_info);
// Now build list with interface names
- for (l=link_info; l; l = l->next) {
- ifi = (ifinfomsg*)NLMSG_DATA(&l->h);
- len = (&l->h)->nlmsg_len;
- len -= NLMSG_LENGTH(sizeof(*ifi));
- parse_rtattr(tb, IFLA_MAX, IFLA_RTA(ifi), len);
-
- Iface* iface = new Iface(string( (char*)RTA_DATA(tb[IFLA_IFNAME])), ifi->ifi_index);
-
- iface->hardware_type_ = ifi->ifi_type;
- iface->setFlags(ifi->ifi_flags);
-
- iface->mac_len_ = 0;
- memset(iface->mac_, 0, IfaceMgr::MAX_MAC_LEN);
- // Does inetface has LL_ADDR?
- if (tb[IFLA_ADDRESS]) {
- iface->mac_len_ = RTA_PAYLOAD(tb[IFLA_ADDRESS]);
- if (iface->mac_len_ > IfaceMgr::MAX_MAC_LEN) {
- iface->mac_len_ = 0;
- isc_throw(Unexpected, "Interface " << iface->getFullName()
- << " was detected to have link address of length " << RTA_PAYLOAD(tb[IFLA_ADDRESS])
- << ", but maximum supported length is " << IfaceMgr::MAX_MAC_LEN);
- }
- memcpy(iface->mac_, RTA_DATA(tb[IFLA_ADDRESS]), iface->mac_len_);
+ for (Netlink::NetlinkMessages::iterator msg = link_info.begin();
+ msg != link_info.end(); ++msg) {
+ // Required to display information about interface
+ struct ifinfomsg* interface_info = static_cast<ifinfomsg*>(NLMSG_DATA(*msg));
+ int len = (*msg)->nlmsg_len;
+ len -= NLMSG_LENGTH(sizeof(*interface_info));
+ nl.parse_rtattr(attribs_table, IFLA_RTA(interface_info), len);
+
+ // valgrind reports *possible* memory leak in the line below, but it is
+ // bogus. Nevertheless, the whole interface definition has been split
+ // into three separate steps for easier debugging.
+ const char* tmp = static_cast<const char*>(RTA_DATA(attribs_table[IFLA_IFNAME]));
+ string iface_name(tmp); // <--- bogus valgrind warning here
+ Iface iface = Iface(iface_name, interface_info->ifi_index);
+
+ iface.setHWType(interface_info->ifi_type);
+ iface.setFlags(interface_info->ifi_flags);
+
+ // Does inetface have LL_ADDR?
+ if (attribs_table[IFLA_ADDRESS]) {
+ iface.setMac(static_cast<const uint8_t*>(RTA_DATA(attribs_table[IFLA_ADDRESS])),
+ RTA_PAYLOAD(attribs_table[IFLA_ADDRESS]));
}
else {
- // Tunnels can have no LL_ADDR. RTA_PAYLOAD doesn't check it and try to
- // dereference it in this manner
- // #define RTA_PAYLOAD(rta) ((int)((rta)->rta_len) - RTA_LENGTH(0))
- iface->mac_len_ = 0;
+ // Tunnels can have no LL_ADDR. RTA_PAYLOAD doesn't check it and
+ // try to dereference it in this manner
}
- ipaddrs_get(*iface, addr_info);
- ifaces_.push_back(*iface);
+ nl.ipaddrs_get(iface, addr_info);
+ ifaces_.push_back(iface);
}
- release_list(link_info);
- release_list(addr_info);
+ nl.release_list(link_info);
+ nl.release_list(addr_info);
printIfaces();
}
@@ -375,6 +515,62 @@ void IfaceMgr::Iface::setFlags(uint32_t flags) {
flag_broadcast_ = flags & IFF_BROADCAST;
}
+void IfaceMgr::os_send4(struct msghdr& m, boost::scoped_array<char>& control_buf,
+ size_t control_buf_len, const Pkt4Ptr& pkt) {
+
+ // Setting the interface is a bit more involved.
+ //
+ // We have to create a "control message", and set that to
+ // define the IPv4 packet information. We could set the
+ // source address if we wanted, but we can safely let the
+ // kernel decide what that should be.
+ m.msg_control = &control_buf[0];
+ m.msg_controllen = control_buf_len;
+ struct cmsghdr* cmsg = CMSG_FIRSTHDR(&m);
+ cmsg->cmsg_level = IPPROTO_IP;
+ cmsg->cmsg_type = IP_PKTINFO;
+ cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo));
+ struct in_pktinfo* pktinfo =(struct in_pktinfo *)CMSG_DATA(cmsg);
+ memset(pktinfo, 0, sizeof(struct in_pktinfo));
+ pktinfo->ipi_ifindex = pkt->getIndex();
+ m.msg_controllen = cmsg->cmsg_len;
+}
+
+bool IfaceMgr::os_receive4(struct msghdr& m, Pkt4Ptr& pkt) {
+ struct cmsghdr* cmsg;
+ struct in_pktinfo* pktinfo;
+ struct in_addr to_addr;
+
+ memset(&to_addr, 0, sizeof(to_addr));
+
+ cmsg = CMSG_FIRSTHDR(&m);
+ while (cmsg != NULL) {
+ if ((cmsg->cmsg_level == IPPROTO_IP) &&
+ (cmsg->cmsg_type == IP_PKTINFO)) {
+ pktinfo = (struct in_pktinfo*)CMSG_DATA(cmsg);
+
+ pkt->setIndex(pktinfo->ipi_ifindex);
+ pkt->setLocalAddr(IOAddress(htonl(pktinfo->ipi_addr.s_addr)));
+ return (true);
+
+ // This field is useful, when we are bound to unicast
+ // address e.g. 192.0.2.1 and the packet was sent to
+ // broadcast. This will return broadcast address, not
+ // the address we are bound to.
+
+ // IOAddress tmp(htonl(pktinfo->ipi_spec_dst.s_addr));
+ // cout << "The other addr is: " << tmp.toText() << endl;
+
+ // Perhaps we should uncomment this:
+ // to_addr = pktinfo->ipi_spec_dst;
+ }
+ cmsg = CMSG_NXTHDR(&m, cmsg);
+ }
+
+ return (false);
}
+} // end of isc::dhcp namespace
+} // end of isc namespace
+
#endif // if defined(LINUX)
diff --git a/src/lib/dhcp/iface_mgr_sun.cc b/src/lib/dhcp/iface_mgr_sun.cc
new file mode 100644
index 0000000..5847906
--- /dev/null
+++ b/src/lib/dhcp/iface_mgr_sun.cc
@@ -0,0 +1,55 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <config.h>
+
+#if defined(OS_SUN)
+
+#include <dhcp/iface_mgr.h>
+#include <exceptions/exceptions.h>
+
+using namespace std;
+using namespace isc;
+using namespace isc::asiolink;
+using namespace isc::dhcp;
+
+namespace isc {
+namespace dhcp {
+
+void
+IfaceMgr::detectIfaces() {
+ /// @todo do the actual detection on Solaris. Currently just calling
+ /// stub implementation.
+ stubDetectIfaces();
+}
+
+void IfaceMgr::os_send4(struct msghdr& /*m*/,
+ boost::scoped_array<char>& /*control_buf*/,
+ size_t /*control_buf_len*/,
+ const Pkt4Ptr& /*pkt*/) {
+ // @todo: Are there any specific actions required before sending IPv4 packet
+ // on BSDs? See iface_mgr_linux.cc for working Linux implementation.
+}
+
+bool IfaceMgr::os_receive4(struct msghdr& /*m*/, Pkt4Ptr& /*pkt*/) {
+ // @todo: Are there any specific actions required before receiving IPv4 packet
+ // on BSDs? See iface_mgr_linux.cc for working Linux implementation.
+
+ return (true); // pretend that we have everything set up for reception.
+}
+
+} // end of isc::dhcp namespace
+} // end of dhcp namespace
+
+#endif
diff --git a/src/lib/dhcp/libdhcp++.cc b/src/lib/dhcp/libdhcp++.cc
index 801c136..c054a4b 100644
--- a/src/lib/dhcp/libdhcp++.cc
+++ b/src/lib/dhcp/libdhcp++.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
@@ -28,153 +28,159 @@ using namespace isc::dhcp;
using namespace isc::util;
// static array with factories for options
+std::map<unsigned short, Option::Factory*> LibDHCP::v4factories_;
+
+// static array with factories for options
std::map<unsigned short, Option::Factory*> LibDHCP::v6factories_;
-unsigned int
-LibDHCP::unpackOptions6(const boost::shared_array<uint8_t> buf,
- unsigned int buf_len,
- unsigned int offset, unsigned int parse_len,
- isc::dhcp::Option::OptionCollection& options) {
- if (offset + parse_len > buf_len) {
- isc_throw(OutOfRange, "Option parse failed. Tried to parse "
- << parse_len << " bytes at offset " << offset
- << ": out of buffer");
- }
- unsigned int end = offset + parse_len;
+
+size_t LibDHCP::unpackOptions6(const OptionBuffer& buf,
+ isc::dhcp::Option::OptionCollection& options) {
+ size_t offset = 0;
+ size_t end = buf.size();
while (offset +4 <= end) {
- uint16_t opt_type = buf[offset]*256 + buf[offset+1];
+ uint16_t opt_type = buf[offset] * 256 + buf[offset + 1];
offset += 2;
- uint16_t opt_len = buf[offset]*256 + buf[offset+1];
+ uint16_t opt_len = buf[offset] * 256 + buf[offset + 1];
offset += 2;
- if (offset + opt_len > end ) {
+ if (offset + opt_len > end) {
cout << "Option " << opt_type << " truncated." << endl;
return (offset);
}
- boost::shared_ptr<Option> opt;
+ OptionPtr opt;
switch (opt_type) {
case D6O_IA_NA:
case D6O_IA_PD:
// cout << "Creating Option6IA" << endl;
- opt = boost::shared_ptr<Option>(new Option6IA(opt_type,
- buf, buf_len,
- offset,
- opt_len));
+ opt = OptionPtr(new Option6IA(opt_type,
+ buf.begin() + offset,
+ buf.begin() + offset + opt_len));
break;
case D6O_IAADDR:
// cout << "Creating Option6IAAddr" << endl;
- opt = boost::shared_ptr<Option>(new Option6IAAddr(opt_type,
- buf, buf_len,
- offset, opt_len));
+ opt = OptionPtr(new Option6IAAddr(opt_type,
+ buf.begin() + offset,
+ buf.begin() + offset + opt_len));
break;
default:
// cout << "Creating Option" << endl;
- opt = boost::shared_ptr<Option>(new Option(Option::V6,
- opt_type,
- buf,
- offset,
- opt_len));
+ opt = OptionPtr(new Option(Option::V6,
+ opt_type,
+ buf.begin() + offset,
+ buf.begin() + offset + opt_len));
break;
}
// add option to options
- options.insert(pair<int, boost::shared_ptr<Option> >(opt_type, opt));
+ options.insert(std::make_pair(opt_type, opt));
offset += opt_len;
}
return (offset);
}
-void
-LibDHCP::unpackOptions4(const std::vector<uint8_t>& buf,
- isc::dhcp::Option::OptionCollection& options) {
+size_t LibDHCP::unpackOptions4(const OptionBuffer& buf,
+ isc::dhcp::Option::OptionCollection& options) {
size_t offset = 0;
- // 2 - header of DHCPv4 option
+ // 2 byte - header of DHCPv4 option
while (offset + 1 <= buf.size()) {
uint8_t opt_type = buf[offset++];
+ // DHO_END is a special, one octet long option
if (opt_type == DHO_END)
- return; // just return. Don't need to add DHO_END option
+ return (offset); // just return. Don't need to add DHO_END option
+
+ // DHO_PAD is just a padding after DHO_END. Let's continue parsing
+ // in case we receive a message without DHO_END.
+ if (opt_type == DHO_PAD)
+ continue;
if (offset + 1 >= buf.size()) {
- isc_throw(OutOfRange, "Attempt to parse truncated option "
- << opt_type);
+ isc_throw(OutOfRange, "Attempt to parse truncated option "
+ << opt_type);
}
uint8_t opt_len = buf[offset++];
- if (offset + opt_len > buf.size() ) {
+ if (offset + opt_len > buf.size()) {
isc_throw(OutOfRange, "Option parse failed. Tried to parse "
<< offset + opt_len << " bytes from " << buf.size()
<< "-byte long buffer.");
}
- boost::shared_ptr<Option> opt;
+ OptionPtr opt;
switch(opt_type) {
default:
- opt = boost::shared_ptr<Option>(new Option(Option::V4, opt_type,
- buf.begin()+offset,
- buf.begin()+offset+opt_len));
+ opt = OptionPtr(new Option(Option::V4, opt_type,
+ buf.begin()+offset,
+ buf.begin()+offset+opt_len));
}
- options.insert(pair<int, boost::shared_ptr<Option> >(opt_type, opt));
+ options.insert(std::make_pair(opt_type, opt));
offset += opt_len;
}
+ return (offset);
}
-unsigned int
-LibDHCP::packOptions6(boost::shared_array<uint8_t> data,
- unsigned int data_len,
- unsigned int offset,
- const isc::dhcp::Option::OptionCollection& options) {
+void LibDHCP::packOptions6(isc::util::OutputBuffer &buf,
+ const isc::dhcp::Option::OptionCollection& options) {
try {
for (Option::OptionCollection::const_iterator it = options.begin();
- it != options.end();
- ++it) {
- unsigned short opt_len = (*it).second->len();
- if (offset + opt_len > data_len) {
- isc_throw(OutOfRange, "Failed to build option " <<
- (*it).first << ": out of buffer");
- }
- offset = it->second->pack(data, data_len, offset);
+ it != options.end(); ++it) {
+ it->second->pack(buf);
}
}
catch (const Exception&) {
cout << "Packet build failed (Option build failed)." << endl;
throw;
}
- return (offset);
}
void
LibDHCP::packOptions(isc::util::OutputBuffer& buf,
const Option::OptionCollection& options) {
for (Option::OptionCollection::const_iterator it = options.begin();
- it != options.end();
- ++it) {
+ it != options.end(); ++it) {
it->second->pack4(buf);
}
}
-
-bool
-LibDHCP::OptionFactoryRegister(Option::Universe u,
- unsigned short opt_type,
- Option::Factory * factory) {
+void LibDHCP::OptionFactoryRegister(Option::Universe u,
+ uint16_t opt_type,
+ Option::Factory* factory) {
switch (u) {
case Option::V6: {
- if (v6factories_.find(opt_type)!=v6factories_.end()) {
+ if (v6factories_.find(opt_type) != v6factories_.end()) {
isc_throw(BadValue, "There is already DHCPv6 factory registered "
<< "for option type " << opt_type);
}
v6factories_[opt_type]=factory;
- return true;
+ return;
}
case Option::V4:
- default:{
- isc_throw(BadValue, "This universe type is not supported yet.");
- return false; // never happens
+ {
+ // Option 0 is special (a one octet-long, equal 0) PAD option. It is never
+ // instantiated as an Option object, but rather consumed during packet parsing.
+ if (opt_type == 0) {
+ isc_throw(BadValue, "Cannot redefine PAD option (code=0)");
+ }
+ // Option 255 is never instantiated as an option object. It is special
+ // (a one-octet equal 255) option that is added at the end of all options
+ // during packet assembly. It is also silently consumed during packet parsing.
+ if (opt_type > 254) {
+ isc_throw(BadValue, "Too big option type for DHCPv4, only 0-254 allowed.");
+ }
+ if (v4factories_.find(opt_type)!=v4factories_.end()) {
+ isc_throw(BadValue, "There is already DHCPv4 factory registered "
+ << "for option type " << opt_type);
+ }
+ v4factories_[opt_type]=factory;
+ return;
}
+ default:
+ isc_throw(BadValue, "Invalid universe type specified.");
}
+ return;
}
diff --git a/src/lib/dhcp/libdhcp++.h b/src/lib/dhcp/libdhcp++.h
index 468e6bb..c7935c8 100644
--- a/src/lib/dhcp/libdhcp++.h
+++ b/src/lib/dhcp/libdhcp++.h
@@ -29,18 +29,10 @@ public:
///
/// Builds raw (on-wire) data for provided collection of options.
///
- /// @param buf shared pointer to buffer. Data will be stored there.
- /// @param buf_len buffer length. Used for buffer overflow protection.
- /// @param offset Offset from beginning of the buffer, where store options
+ /// @param buf output buffer (assembled options will be stored here)
/// @param options collection of options to store to
- ///
- /// @return offset to the first unused byte in buffer (next one after last
- /// used byte)
- ///
- static unsigned int
- packOptions6(boost::shared_array<uint8_t> buf, unsigned int buf_len,
- unsigned int offset,
- const isc::dhcp::Option::OptionCollection& options);
+ static void packOptions6(isc::util::OutputBuffer& buf,
+ const isc::dhcp::Option::OptionCollection& options);
/// @brief Stores options in a buffer.
@@ -52,48 +44,49 @@ public:
/// may be different reasons (option too large, option malformed,
/// too many options etc.)
///
- /// @param buf
- /// @param options
- static void
- packOptions(isc::util::OutputBuffer& buf,
- const isc::dhcp::Option::OptionCollection& options);
+ /// @param buf output buffer (assembled options will be stored here)
+ /// @param options collection of options to store to
+ static void packOptions(isc::util::OutputBuffer& buf,
+ const isc::dhcp::Option::OptionCollection& options);
- static void
- unpackOptions4(const std::vector<uint8_t>& buf,
- isc::dhcp::Option::OptionCollection& options);
- ///
- /// Parses provided buffer and creates Option objects.
+ /// @brief Parses provided buffer as DHCPv4 options and creates Option objects.
///
- /// Parses provided buf array and stores created Option objects
+ /// Parses provided buffer and stores created Option objects
/// in options container.
///
/// @param buf Buffer to be parsed.
- /// @param offset Specifies offset for the first option.
/// @param options Reference to option container. Options will be
/// put here.
+ static size_t unpackOptions4(const OptionBuffer& buf,
+ isc::dhcp::Option::OptionCollection& options);
+
+ /// @brief Parses provided buffer as DHCPv6 options and creates Option objects.
///
- /// @return offset to first byte after last parsed option
+ /// Parses provided buffer and stores created Option objects
+ /// in options container.
///
- static unsigned int
- unpackOptions6(const boost::shared_array<uint8_t> buf, unsigned int buf_len,
- unsigned int offset, unsigned int parse_len,
- isc::dhcp::Option::OptionCollection& options_);
+ /// @param buf Buffer to be parsed.
+ /// @param options Reference to option container. Options will be
+ /// put here.
+ static size_t unpackOptions6(const OptionBuffer& buf,
+ isc::dhcp::Option::OptionCollection& options);
- ///
/// Registers factory method that produces options of specific option types.
///
+ /// @exception BadValue if provided type is already registered, has too large
+ /// value or invalid universe is specified
+ ///
/// @param u universe of the option (V4 or V6)
- /// @param opt_type option-type
+ /// @param type option-type
/// @param factory function pointer
- ///
- /// @return true, if registration was successful, false otherwise
- ///
- static bool
- OptionFactoryRegister(Option::Universe u,
- unsigned short type,
- Option::Factory * factory);
+ static void OptionFactoryRegister(Option::Universe u,
+ uint16_t type,
+ Option::Factory * factory);
protected:
- // pointers to factories that produce DHCPv6 options
+ /// pointers to factories that produce DHCPv6 options
+ static std::map<unsigned short, Option::Factory*> v4factories_;
+
+ /// pointers to factories that produce DHCPv6 options
static std::map<unsigned short, Option::Factory*> v6factories_;
};
diff --git a/src/lib/dhcp/option.cc b/src/lib/dhcp/option.cc
index 23de315..03b4a3d 100644
--- a/src/lib/dhcp/option.cc
+++ b/src/lib/dhcp/option.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,7 +17,6 @@
#include <arpa/inet.h>
#include <sstream>
#include <iomanip>
-#include <boost/shared_array.hpp>
#include "exceptions/exceptions.h"
#include "util/io_utilities.h"
@@ -25,38 +24,29 @@
#include "dhcp/libdhcp++.h"
using namespace std;
-using namespace isc::dhcp;
using namespace isc::util;
-Option::Option(Universe u, unsigned short type)
+namespace isc {
+namespace dhcp {
+
+Option::Option(Universe u, uint16_t type)
:universe_(u), type_(type) {
- if ((u == V4) && (type > 255)) {
+ // END option (type 255 is forbidden as well)
+ if ((u == V4) && ((type == 0) || (type > 254))) {
isc_throw(BadValue, "Can't create V4 option of type "
- << type << ", V4 options are in range 0..255");
+ << type << ", V4 options are in range 1..254");
}
}
-Option::Option(Universe u, unsigned short type,
- const boost::shared_array<uint8_t>& buf,
- unsigned int offset, unsigned int len)
- :universe_(u), type_(type),
- offset_(offset)
-{
- uint8_t* ptr = &buf[offset];
- data_ = std::vector<uint8_t>(ptr, ptr + len);
-
- check();
-}
-
-Option::Option(Universe u, unsigned short type, std::vector<uint8_t>& data)
+Option::Option(Universe u, uint16_t type, const OptionBuffer& data)
:universe_(u), type_(type), data_(data) {
check();
}
-Option::Option(Universe u, uint16_t type, vector<uint8_t>::const_iterator first,
- vector<uint8_t>::const_iterator last)
- :universe_(u), type_(type), data_(std::vector<uint8_t>(first,last)) {
+Option::Option(Universe u, uint16_t type, OptionBufferConstIter first,
+ OptionBufferConstIter last)
+ :universe_(u), type_(type), data_(OptionBuffer(first,last)) {
check();
}
@@ -84,22 +74,23 @@ Option::check() {
// both types and data size.
}
-unsigned int
-Option::pack(boost::shared_array<uint8_t>& buf,
- unsigned int buf_len,
- unsigned int offset) {
- if (universe_ != V6) {
+void Option::pack(isc::util::OutputBuffer& buf) {
+ switch (universe_) {
+ case V6:
+ return (pack6(buf));
+ case V4:
+ return (pack4(buf));
+ default:
isc_throw(BadValue, "Failed to pack " << type_ << " option. Do not "
<< "use this method for options other than DHCPv6.");
}
- return pack6(buf, buf_len, offset);
}
void
Option::pack4(isc::util::OutputBuffer& buf) {
switch (universe_) {
case V4: {
- if (data_.size() > 255) {
+ if (len() > 255) {
isc_throw(OutOfRange, "DHCPv4 Option " << type_ << " is too big."
<< "At most 255 bytes are supported.");
/// TODO Larger options can be stored as separate instances
@@ -127,84 +118,25 @@ Option::pack4(isc::util::OutputBuffer& buf) {
}
}
-unsigned int
-Option::pack6(boost::shared_array<uint8_t>& buf,
- unsigned int buf_len,
- unsigned int offset) {
- if (offset+len() > buf_len) {
- isc_throw(OutOfRange, "Failed to pack v6 option=" <<
- type_ << ",len=" << len() << ": too small buffer.");
- }
-
- uint8_t* ptr = &buf[offset];
-
- ptr = writeUint16(type_, ptr);
-
- ptr = writeUint16(len() - getHeaderLen(), ptr);
-
- if (! data_.empty())
- memcpy(ptr, &data_[0], data_.size());
-
- // end of fixed part of this option
- offset += OPTION6_HDR_LEN + data_.size();
-
- return LibDHCP::packOptions6(buf, buf_len, offset, options_);
-}
+void Option::pack6(isc::util::OutputBuffer& buf) {
+ buf.writeUint16(type_);
+ buf.writeUint16(len() - getHeaderLen());
-unsigned int
-Option::unpack(const boost::shared_array<uint8_t>& buf,
- unsigned int buf_len,
- unsigned int offset,
- unsigned int parse_len) {
- switch (universe_) {
- case V4:
- return unpack4(buf, buf_len, offset, parse_len);
- case V6:
- return unpack6(buf, buf_len, offset, parse_len);
- default:
- isc_throw(BadValue, "Unknown universe defined for Option " << type_);
+ if (! data_.empty()) {
+ buf.writeData(&data_[0], data_.size());
}
- return 0; // should not happen
+ return LibDHCP::packOptions6(buf, options_);
}
-unsigned int
-Option::unpack4(const boost::shared_array<uint8_t>&,
- unsigned int ,
- unsigned int ,
- unsigned int ) {
- isc_throw(Unexpected, "IPv4 support not implemented yet.");
- return 0;
+void Option::unpack(OptionBufferConstIter begin,
+ OptionBufferConstIter end) {
+ data_ = OptionBuffer(begin, end);
}
-unsigned int
-Option::unpack6(const boost::shared_array<uint8_t>& buf,
- unsigned int buf_len,
- unsigned int offset,
- unsigned int parse_len) {
-
- if (buf_len < offset+parse_len) {
- isc_throw(OutOfRange, "Failed to unpack DHCPv6 option len="
- << parse_len << " offset=" << offset
- << " from buffer (length=" << buf_len
- << "): too small buffer.");
- }
-
- uint8_t* ptr = &buf[offset];
- data_ = std::vector<uint8_t>(ptr, ptr + parse_len);
-
- offset_ = offset;
-
- return (offset+parse_len);
-
- //return LibDHCP::unpackOptions6(buf, buf_len, offset, parse_len,
- // options_);
-}
-
-/// Returns length of the complete option (data length + DHCPv4/DHCPv6
-/// option header)
-uint16_t
-Option::len() {
+uint16_t Option::len() {
+ // Returns length of the complete option (data length + DHCPv4/DHCPv6
+ // option header)
// length of the whole option is header and data stored in this option...
int length = getHeaderLen() + data_.size();
@@ -232,18 +164,16 @@ Option::valid() {
return (true);
}
-boost::shared_ptr<isc::dhcp::Option>
-Option::getOption(unsigned short opt_type) {
+OptionPtr Option::getOption(uint16_t opt_type) {
isc::dhcp::Option::OptionCollection::const_iterator x =
options_.find(opt_type);
if ( x != options_.end() ) {
return (*x).second;
}
- return boost::shared_ptr<isc::dhcp::Option>(); // NULL
+ return OptionPtr(); // NULL
}
-bool
-Option::delOption(unsigned short opt_type) {
+bool Option::delOption(uint16_t opt_type) {
isc::dhcp::Option::OptionCollection::iterator x = options_.find(opt_type);
if ( x != options_.end() ) {
options_.erase(x);
@@ -289,8 +219,7 @@ Option::getHeaderLen() {
return 0; // should not happen
}
-void
-Option::addOption(boost::shared_ptr<Option> opt) {
+void Option::addOption(OptionPtr opt) {
if (universe_ == V4) {
// check for uniqueness (DHCPv4 options must be unique)
if (getOption(opt->getType())) {
@@ -298,7 +227,7 @@ Option::addOption(boost::shared_ptr<Option> opt) {
<< " already present in this message.");
}
}
- options_.insert(pair<int, boost::shared_ptr<Option> >(opt->getType(), opt));
+ options_.insert(make_pair(opt->getType(), opt));
}
uint8_t Option::getUint8() {
@@ -344,3 +273,6 @@ void Option::setUint32(uint32_t value) {
Option::~Option() {
}
+
+} // end of isc::dhcp namespace
+} // end of isc namespace
diff --git a/src/lib/dhcp/option.h b/src/lib/dhcp/option.h
index d80fc05..c7f5d10 100644
--- a/src/lib/dhcp/option.h
+++ b/src/lib/dhcp/option.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
@@ -19,12 +19,30 @@
#include <map>
#include <vector>
#include <boost/shared_ptr.hpp>
-#include <boost/shared_array.hpp>
#include <util/buffer.h>
namespace isc {
namespace dhcp {
+/// @brief buffer types used in DHCP code.
+///
+/// Dereferencing OptionBuffer iterator will point out to contiguous memory.
+typedef std::vector<uint8_t> OptionBuffer;
+
+/// iterator for walking over OptionBuffer
+typedef OptionBuffer::iterator OptionBufferIter;
+
+/// const_iterator for walking over OptionBuffer
+typedef OptionBuffer::const_iterator OptionBufferConstIter;
+
+/// pointer to a DHCP buffer
+typedef boost::shared_ptr<OptionBuffer> OptionBufferPtr;
+
+/// shared pointer to Option object
+class Option;
+typedef boost::shared_ptr<Option> OptionPtr;
+
+
class Option {
public:
/// length of the usual DHCPv4 option header (there are exceptions)
@@ -37,46 +55,22 @@ public:
enum Universe { V4, V6 };
/// a collection of DHCPv6 options
- typedef std::multimap<unsigned int, boost::shared_ptr<Option> >
- OptionCollection;
+ typedef std::multimap<unsigned int, OptionPtr> OptionCollection;
/// @brief a factory function prototype
///
/// @param u option universe (DHCPv4 or DHCPv6)
/// @param type option type
/// @param buf pointer to a buffer
- /// @param offset offset to first data byte in that buffer
- /// @param len data length of this option
///
/// @return a pointer to a created option object
- typedef boost::shared_ptr<Option> Factory(Option::Universe u,
- unsigned short type,
- boost::shared_array<uint8_t>& buf,
- unsigned int offset,
- unsigned int len);
+ typedef OptionPtr Factory(Option::Universe u, uint16_t type, const OptionBuffer& buf);
/// @brief ctor, used for options constructed, usually during transmission
///
/// @param u option universe (DHCPv4 or DHCPv6)
/// @param type option type
- Option(Universe u, unsigned short type);
-
- /// @brief ctor, used for received options
- ///
- /// boost::shared_array allows sharing a buffer, but it requires that
- /// different instances share pointer to the whole array, not point
- /// to different elements in shared array. Therefore we need to share
- /// pointer to the whole array and remember offset where data for
- /// this option begins
- ///
- /// @param u specifies universe (V4 or V6)
- /// @param type option type
- /// @param buf pointer to a buffer
- /// @param offset offset in a buffer pointing to first byte of data
- /// @param len length of the option data
- Option(Universe u, unsigned short type,
- const boost::shared_array<uint8_t>& buf, unsigned int offset,
- unsigned int len);
+ Option(Universe u, uint16_t type);
/// @brief Constructor, used for received options.
///
@@ -88,7 +82,7 @@ public:
/// @param u specifies universe (V4 or V6)
/// @param type option type (0-255 for V4 and 0-65535 for V6)
/// @param data content of the option
- Option(Universe u, unsigned short type, std::vector<uint8_t>& data);
+ Option(Universe u, uint16_t type, const OptionBuffer& data);
/// @brief Constructor, used for received options.
///
@@ -110,15 +104,13 @@ public:
/// @param first iterator to the first element that should be copied
/// @param last iterator to the next element after the last one
/// to be copied.
- Option(Universe u, uint16_t type,
- std::vector<uint8_t>::const_iterator first,
- std::vector<uint8_t>::const_iterator last);
+ Option(Universe u, uint16_t type, OptionBufferConstIter first,
+ OptionBufferConstIter last);
/// @brief returns option universe (V4 or V6)
///
/// @return universe type
- Universe
- getUniverse() { return universe_; };
+ Universe getUniverse() { return universe_; };
/// @brief Writes option in wire-format to a buffer.
///
@@ -129,14 +121,7 @@ public:
/// TODO: Migrate DHCPv6 code to pack(OutputBuffer& buf) version
///
/// @param buf pointer to a buffer
- /// @param buf_len length of the buffer
- /// @param offset offset to place, where option shout be stored
- ///
- /// @return offset to first unused byte after stored option
- ///
- virtual unsigned int
- pack(boost::shared_array<uint8_t>& buf, unsigned int buf_len,
- unsigned int offset);
+ virtual void pack(isc::util::OutputBuffer& buf);
/// @brief Writes option in a wire-format to a buffer.
///
@@ -146,64 +131,48 @@ public:
/// unify pack4() and pack6() and rename them to just pack().
///
/// @param buf output buffer (option will be stored there)
- virtual void
- pack4(isc::util::OutputBuffer& buf);
-
+ virtual void pack4(isc::util::OutputBuffer& buf);
- /// @brief Parses buffer.
+ /// @brief Parses received buffer.
///
- /// Parses received buffer, returns offset to the first unused byte after
- /// parsed option.
- ///
- /// @param buf pointer to buffer
- /// @param buf_len length of buf
- /// @param offset offset, where start parsing option
- /// @param parse_len how many bytes should be parsed
- ///
- /// @return offset after last parsed octet
- virtual unsigned int
- unpack(const boost::shared_array<uint8_t>& buf,
- unsigned int buf_len,
- unsigned int offset,
- unsigned int parse_len);
+ /// @param begin iterator to first byte of option data
+ /// @param end iterator to end of option data (first byte after option end)
+ virtual void unpack(OptionBufferConstIter begin,
+ OptionBufferConstIter end);
/// Returns string representation of the option.
///
/// @param indent number of spaces before printing text
///
/// @return string with text representation.
- virtual std::string
- toText(int indent = 0);
+ virtual std::string toText(int indent = 0);
/// Returns option type (0-255 for DHCPv4, 0-65535 for DHCPv6)
///
/// @return option type
- unsigned short getType() { return (type_); }
+ uint16_t getType() { return (type_); }
/// Returns length of the complete option (data length + DHCPv4/DHCPv6
/// option header)
///
/// @return length of the option
- virtual uint16_t
- len();
+ virtual uint16_t len();
/// @brief Returns length of header (2 for v4, 4 for v6)
///
/// @return length of option header
- virtual uint16_t
- getHeaderLen();
+ virtual uint16_t getHeaderLen();
/// returns if option is valid (e.g. option may be truncated)
///
/// @return true, if option is valid
- virtual bool
- valid();
+ virtual bool valid();
/// Returns pointer to actual data.
///
/// @return pointer to actual data (or reference to an empty vector
/// if there is no data)
- virtual const std::vector<uint8_t>& getData() { return (data_); }
+ virtual const OptionBuffer& getData() { return (data_); }
/// Adds a sub-option.
///
@@ -217,24 +186,21 @@ public:
/// many places. Requiring casting is not feasible.
///
/// @param opt shared pointer to a suboption that is going to be added.
- void
- addOption(boost::shared_ptr<Option> opt);
+ void addOption(OptionPtr opt);
/// Returns shared_ptr to suboption of specific type
///
/// @param type type of requested suboption
///
/// @return shared_ptr to requested suoption
- boost::shared_ptr<isc::dhcp::Option>
- getOption(unsigned short type);
+ OptionPtr getOption(uint16_t type);
/// Attempts to delete first suboption of requested type
///
/// @param type Type of option to be deleted.
///
/// @return true if option was deleted, false if no such option existed
- bool
- delOption(unsigned short type);
+ bool delOption(uint16_t type);
/// @brief Returns content of first byte.
///
@@ -286,40 +252,7 @@ protected:
/// defined suboptions. Version for building DHCPv4 options.
///
/// @param buf output buffer (built options will be stored here)
- /// @param buf_len buffer length (used for buffer overflow checks)
- /// @param offset offset from start of the buf buffer
- ///
- /// @return offset to the next byte after last used byte
- virtual unsigned int
- pack6(boost::shared_array<uint8_t>& buf,
- unsigned int buf_len,
- unsigned int offset);
-
- /// Parses provided buffer and creates DHCPv4 options.
- ///
- /// @param buf buffer that contains raw buffer to parse (on-wire format)
- /// @param buf_len buffer length (used for buffer overflow checks)
- /// @param offset offset from start of the buf buffer
- ///
- /// @return offset to the next byte after last parsed byte
- virtual unsigned int
- unpack4(const boost::shared_array<uint8_t>& buf,
- unsigned int buf_len,
- unsigned int offset,
- unsigned int parse_len);
-
- /// Parses provided buffer and creates DHCPv6 options.
- ///
- /// @param buf buffer that contains raw buffer to parse (on-wire format)
- /// @param buf_len buffer length (used for buffer overflow checks)
- /// @param offset offset from start of the buf buffer
- ///
- /// @return offset to the next byte after last parsed byte
- virtual unsigned int
- unpack6(const boost::shared_array<uint8_t>& buf,
- unsigned int buf_len,
- unsigned int offset,
- unsigned int parse_len);
+ virtual void pack6(isc::util::OutputBuffer& buf);
/// @brief A private method used for option correctness.
///
@@ -332,17 +265,10 @@ protected:
Universe universe_;
/// option type (0-255 for DHCPv4, 0-65535 for DHCPv6)
- unsigned short type_;
+ uint16_t type_;
/// contains content of this data
- std::vector<uint8_t> data_;
-
- /// TODO: Remove this field. vector<uint8_t> should be used
- /// instead.
- /// data is a shared_pointer that points out to the
- /// whole packet. offset_ specifies where data for
- /// this option begins.
- unsigned int offset_;
+ OptionBuffer data_;
/// collection for storing suboptions
OptionCollection options_;
diff --git a/src/lib/dhcp/option4_addrlst.cc b/src/lib/dhcp/option4_addrlst.cc
index 88eb915..4b0224f 100644
--- a/src/lib/dhcp/option4_addrlst.cc
+++ b/src/lib/dhcp/option4_addrlst.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
@@ -23,10 +23,12 @@
#include <dhcp/option4_addrlst.h>
using namespace std;
-using namespace isc::dhcp;
using namespace isc::util;
using namespace isc::asiolink;
+namespace isc {
+namespace dhcp {
+
Option4AddrLst::Option4AddrLst(uint8_t type)
:Option(V4, type) {
}
@@ -38,9 +40,8 @@ Option4AddrLst::Option4AddrLst(uint8_t type, const AddressContainer& addrs)
}
-Option4AddrLst::Option4AddrLst(uint8_t type,
- vector<uint8_t>::const_iterator first,
- vector<uint8_t>::const_iterator last)
+Option4AddrLst::Option4AddrLst(uint8_t type, OptionBufferConstIter first,
+ OptionBufferConstIter last)
:Option(V4, type) {
if ( (distance(first, last) % V4ADDRESS_LEN) ) {
isc_throw(OutOfRange, "DHCPv4 Option4AddrLst " << type_
@@ -133,3 +134,6 @@ std::string Option4AddrLst::toText(int indent /* =0 */ ) {
return tmp.str();
}
+
+} // end of isc::dhcp namespace
+} // end of isc namespace
diff --git a/src/lib/dhcp/option4_addrlst.h b/src/lib/dhcp/option4_addrlst.h
index c795805..3bedc6d 100644
--- a/src/lib/dhcp/option4_addrlst.h
+++ b/src/lib/dhcp/option4_addrlst.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
@@ -77,8 +77,8 @@ public:
/// @param first iterator to the first element that should be copied
/// @param last iterator to the next element after the last one
/// to be copied.
- Option4AddrLst(uint8_t type, std::vector<uint8_t>::const_iterator first,
- std::vector<uint8_t>::const_iterator last);
+ Option4AddrLst(uint8_t type, OptionBufferConstIter first,
+ OptionBufferConstIter last);
/// @brief Writes option in a wire-format to a buffer.
///
@@ -88,16 +88,14 @@ public:
/// unify pack4() and pack6() and rename them to just pack().
///
/// @param buf output buffer (option will be stored there)
- virtual void
- pack4(isc::util::OutputBuffer& buf);
+ virtual void pack4(isc::util::OutputBuffer& buf);
/// Returns string representation of the option.
///
/// @param indent number of spaces before printing text
///
/// @return string with text representation.
- virtual std::string
- toText(int indent = 0);
+ virtual std::string toText(int indent = 0);
/// Returns length of the complete option (data length + DHCPv4/DHCPv6
/// option header)
@@ -113,8 +111,7 @@ public:
/// a couple (1-3) addresses, the overhead is not that big.
///
/// @return address container with addresses
- AddressContainer
- getAddresses() { return addrs_; };
+ AddressContainer getAddresses() { return addrs_; };
/// @brief Sets addresses list.
///
diff --git a/src/lib/dhcp/option6_addrlst.cc b/src/lib/dhcp/option6_addrlst.cc
index fb082fa..d23b700 100644
--- a/src/lib/dhcp/option6_addrlst.cc
+++ b/src/lib/dhcp/option6_addrlst.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
@@ -29,23 +29,21 @@ using namespace isc::dhcp;
using namespace isc::asiolink;
using namespace isc::util;
-Option6AddrLst::Option6AddrLst(unsigned short type,
- const AddressContainer& addrs)
+namespace isc {
+namespace dhcp {
+
+Option6AddrLst::Option6AddrLst(uint16_t type, const AddressContainer& addrs)
:Option(V6, type), addrs_(addrs) {
}
-Option6AddrLst::Option6AddrLst(unsigned short type,
- const isc::asiolink::IOAddress& addr)
+Option6AddrLst::Option6AddrLst(uint16_t type, const isc::asiolink::IOAddress& addr)
:Option(V6, type), addrs_(1,addr) {
}
-Option6AddrLst::Option6AddrLst(unsigned short type,
- boost::shared_array<uint8_t> buf,
- unsigned int buf_len,
- unsigned int offset,
- unsigned int option_len)
+Option6AddrLst::Option6AddrLst(uint16_t type, OptionBufferConstIter begin,
+ OptionBufferConstIter end)
:Option(V6, type) {
- unpack(buf, buf_len, offset, option_len);
+ unpack(begin, end);
}
void
@@ -63,70 +61,42 @@ Option6AddrLst::setAddresses(const AddressContainer& addrs) {
addrs_ = addrs;
}
-unsigned int
-Option6AddrLst::pack(boost::shared_array<uint8_t>& buf,
- unsigned int buf_len,
- unsigned int offset) {
- if (len() > buf_len) {
- isc_throw(OutOfRange, "Failed to pack IA option: len=" << len()
- << ", buffer=" << buf_len << ": too small buffer.");
- }
+void Option6AddrLst::pack(isc::util::OutputBuffer& buf) {
- writeUint16(type_, &buf[offset]);
- offset += sizeof(uint16_t);
+ buf.writeUint16(type_);
// len() returns complete option length.
// len field contains length without 4-byte option header
- writeUint16(len() - OPTION6_HDR_LEN, &buf[offset]);
- offset += sizeof(uint16_t);
+ buf.writeUint16(len() - getHeaderLen());
- // this wrapping is *ugly*. I wish there was a a
for (AddressContainer::const_iterator addr=addrs_.begin();
- addr!=addrs_.end();
- ++addr) {
- memcpy(&buf[offset],
- addr->getAddress().to_v6().to_bytes().data(),
- V6ADDRESS_LEN);
- offset += V6ADDRESS_LEN;
+ addr!=addrs_.end(); ++addr) {
+ buf.writeData(addr->getAddress().to_v6().to_bytes().data(), V6ADDRESS_LEN);
}
-
- return offset;
}
-unsigned int
-Option6AddrLst::unpack(const boost::shared_array<uint8_t>& buf,
- unsigned int buf_len,
- unsigned int offset,
- unsigned int option_len) {
- if (offset+option_len > buf_len) {
- isc_throw(OutOfRange, "Option " << type_
- << " truncated.");
- }
-
- if (option_len%16) {
+void Option6AddrLst::unpack(OptionBufferConstIter begin,
+ OptionBufferConstIter end) {
+ if ((distance(begin, end) % V6ADDRESS_LEN) != 0) {
isc_throw(OutOfRange, "Option " << type_
- << " malformed: len=" << option_len
+ << " malformed: len=" << distance(begin, end)
<< " is not divisible by 16.");
}
- while (option_len > 0) {
- addrs_.push_back(IOAddress::from_bytes(AF_INET6, &buf[offset]));
- offset += 16;
- option_len -= 16;
+ while (begin != end) {
+ addrs_.push_back(IOAddress::from_bytes(AF_INET6, &(*begin)));
+ begin += V6ADDRESS_LEN;
}
-
- return offset;
}
std::string Option6AddrLst::toText(int indent /* =0 */) {
stringstream tmp;
- for (int i=0; i<indent; i++)
+ for (int i = 0; i < indent; i++)
tmp << " ";
tmp << "type=" << type_ << " " << addrs_.size() << "addr(s): ";
for (AddressContainer::const_iterator addr=addrs_.begin();
- addr!=addrs_.end();
- ++addr) {
+ addr!=addrs_.end(); ++addr) {
tmp << addr->toText() << " ";
}
return tmp.str();
@@ -134,5 +104,8 @@ std::string Option6AddrLst::toText(int indent /* =0 */) {
uint16_t Option6AddrLst::len() {
- return (OPTION6_HDR_LEN + addrs_.size()*16);
+ return (OPTION6_HDR_LEN + addrs_.size()*V6ADDRESS_LEN);
}
+
+} // end of namespace isc::dhcp
+} // end of namespace isc
diff --git a/src/lib/dhcp/option6_addrlst.h b/src/lib/dhcp/option6_addrlst.h
index a73dc55..209d2dd 100644
--- a/src/lib/dhcp/option6_addrlst.h
+++ b/src/lib/dhcp/option6_addrlst.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
@@ -36,70 +36,44 @@ public:
///
/// @param type option type
/// @param addrs vector of addresses to be stored
- ///
- Option6AddrLst(unsigned short type,
- const AddressContainer& addrs);
+ Option6AddrLst(uint16_t type, const AddressContainer& addrs);
/// @brief Simplified constructor for a single address
///
/// @param type option type
/// @param addr a single address to be stored
- ///
- Option6AddrLst(unsigned short type,
- const isc::asiolink::IOAddress& addr);
+ Option6AddrLst(uint16_t type, const isc::asiolink::IOAddress& addr);
/// @brief Constructor used for parsing received option
///
/// @param type option type
- /// @param buf pointer to packet buffer
- /// @param buf_len length of packet buffer
- /// @param offset offset to beginning of option data
- /// @param len length of option data
- ///
- Option6AddrLst(unsigned short type, boost::shared_array<uint8_t> buf,
- unsigned int buf_len,
- unsigned int offset,
- unsigned int len);
+ /// @param begin iterator to first byte of option data
+ /// @param end iterator to end of option data (first byte after option end)
+ Option6AddrLst(uint16_t type, OptionBufferConstIter begin,
+ OptionBufferConstIter end);
/// @brief Assembles on-wire form of this option
///
/// @param buf pointer to packet buffer
- /// @param buf_len length of packet buffer
- /// @param offset offset to place, where option is to be stored
- ///
- /// @return offset to the next unused char (just after stored option)
- ///
- unsigned int
- pack(boost::shared_array<uint8_t>& buf, unsigned int buf_len,
- unsigned int offset);
+ void pack(isc::util::OutputBuffer& buf);
/// @brief Parses received data
///
- /// @param buf pointer to packet buffer
- /// @param buf_len length of packet buffer
- /// @param offset offset to option data
- /// @param parse_len specified option data length
- ///
- /// @return offset to the next unparsed char (just after parsed option)
- ///
- virtual unsigned int
- unpack(const boost::shared_array<uint8_t>& buf,
- unsigned int buf_len,
- unsigned int offset,
- unsigned int parse_len);
+ /// @param begin iterator to first byte of option data
+ /// @param end iterator to end of option data (first byte after option end)
+ virtual void unpack(OptionBufferConstIter begin,
+ OptionBufferConstIter end);
virtual std::string toText(int indent = 0);
/// @brief Sets a single address.
///
/// @param addr a single address to be added
- ///
void setAddress(const isc::asiolink::IOAddress& addr);
/// @brief Sets list of addresses.
///
/// @param addrs a vector of addresses to be added
- ///
void setAddresses(const AddressContainer& addrs);
/// @brief Returns vector with addresses.
@@ -110,8 +84,7 @@ public:
/// a couple (1-3) addresses, the overhead is not that big.
///
/// @return address container with addresses
- AddressContainer
- getAddresses() { return addrs_; };
+ AddressContainer getAddresses() { return addrs_; };
// returns data length (data length + DHCPv4/DHCPv6 option header)
virtual uint16_t len();
diff --git a/src/lib/dhcp/option6_ia.cc b/src/lib/dhcp/option6_ia.cc
index cd55553..65be711 100644
--- a/src/lib/dhcp/option6_ia.cc
+++ b/src/lib/dhcp/option6_ia.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
@@ -15,82 +15,54 @@
#include <stdint.h>
#include <arpa/inet.h>
#include <sstream>
-#include "exceptions/exceptions.h"
-#include "dhcp/libdhcp++.h"
-#include "dhcp/option6_ia.h"
-#include "dhcp/dhcp6.h"
-#include "util/io_utilities.h"
+#include <exceptions/exceptions.h>
+#include <dhcp/libdhcp++.h>
+#include <dhcp/option6_ia.h>
+#include <dhcp/dhcp6.h>
+#include <util/io_utilities.h>
using namespace std;
-using namespace isc;
-using namespace isc::dhcp;
using namespace isc::util;
-Option6IA::Option6IA(unsigned short type, unsigned int iaid)
+namespace isc {
+namespace dhcp {
+
+Option6IA::Option6IA(uint16_t type, uint32_t iaid)
:Option(Option::V6, type), iaid_(iaid) {
}
-Option6IA::Option6IA(unsigned short type,
- const boost::shared_array<uint8_t>& buf,
- unsigned int buf_len,
- unsigned int offset,
- unsigned int option_len)
+Option6IA::Option6IA(uint16_t type, OptionBufferConstIter begin, OptionBufferConstIter end)
:Option(Option::V6, type) {
- unpack(buf, buf_len, offset, option_len);
+ unpack(begin, end);
}
-unsigned int
-Option6IA::pack(boost::shared_array<uint8_t>& buf,
- unsigned int buf_len,
- unsigned int offset) {
- if (offset + len() > buf_len) {
- isc_throw(OutOfRange, "Failed to pack IA option: len=" << len()
- << ", buffer=" << buf_len << ": too small buffer.");
- }
-
- if (len() < 16 ) {
- isc_throw(OutOfRange, "Attempt to build malformed IA option: len="
- << len() << " is too small (at least 16 is required).");
- }
-
- uint8_t* ptr = &buf[offset];
-
- ptr = writeUint16(type_, ptr);
- ptr = writeUint16(len() - OPTION6_HDR_LEN, ptr);
- offset += OPTION6_HDR_LEN;
+void Option6IA::pack(isc::util::OutputBuffer& buf) {
+ buf.writeUint16(type_);
+ buf.writeUint16(len() - OPTION6_HDR_LEN);
+ buf.writeUint32(iaid_);
+ buf.writeUint32(t1_);
+ buf.writeUint32(t2_);
- ptr = writeUint32(iaid_, ptr);
- ptr = writeUint32(t1_, ptr);
- ptr = writeUint32(t2_, ptr);
- offset += OPTION6_IA_LEN;
-
- offset = LibDHCP::packOptions6(buf, buf_len, offset, options_);
- return offset;
+ LibDHCP::packOptions6(buf, options_);
}
-unsigned int
-Option6IA::unpack(const boost::shared_array<uint8_t>& buf,
- unsigned int buf_len,
- unsigned int offset,
- unsigned int parse_len) {
- if ( parse_len < OPTION6_IA_LEN || offset + OPTION6_IA_LEN > buf_len) {
+void Option6IA::unpack(OptionBufferConstIter begin,
+ OptionBufferConstIter end) {
+ // IA_NA and IA_PD have 12 bytes content (iaid, t1, t2 fields)
+ // followed by 0 or more sub-options.
+ if (distance(begin, end) < OPTION6_IA_LEN) {
isc_throw(OutOfRange, "Option " << type_ << " truncated");
}
+ iaid_ = readUint32( &(*begin) );
+ begin += sizeof(uint32_t);
+ t1_ = readUint32( &(*begin) );
+ begin += sizeof(uint32_t);
- iaid_ = readUint32(&buf[offset]);
- offset += sizeof(uint32_t);
-
- t1_ = readUint32(&buf[offset]);
- offset += sizeof(uint32_t);
-
- t2_ = readUint32(&buf[offset]);
- offset += sizeof(uint32_t);
+ t2_ = readUint32( &(*begin) );
+ begin += sizeof(uint32_t);
- offset = LibDHCP::unpackOptions6(buf, buf_len, offset,
- parse_len - OPTION6_IA_LEN, options_);
-
- return (offset);
+ LibDHCP::unpackOptions6(OptionBuffer(begin, end), options_);
}
std::string Option6IA::toText(int indent /* = 0*/) {
@@ -134,3 +106,6 @@ uint16_t Option6IA::len() {
}
return (length);
}
+
+} // end of isc::dhcp namespace
+} // end of isc namespace
diff --git a/src/lib/dhcp/option6_ia.h b/src/lib/dhcp/option6_ia.h
index cab8068..c2089d4 100644
--- a/src/lib/dhcp/option6_ia.h
+++ b/src/lib/dhcp/option6_ia.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
@@ -27,54 +27,34 @@ public:
/// Length of IA_NA and IA_PD content
const static size_t OPTION6_IA_LEN = 12;
- /// @brief ctor, used for options constructed, usually during transmission
+ /// @brief Ctor, used for constructed options, usually during transmission.
///
/// @param type option type (usually 4 for IA_NA, 25 for IA_PD)
/// @param iaid identity association identifier (id of IA)
- Option6IA(uint16_t type, unsigned int iaid);
+ Option6IA(uint16_t type, uint32_t iaid);
- /// @brief ctor, used for received options
- ///
- /// boost::shared_array allows sharing a buffer, but it requires that
- /// different instances share pointer to the whole array, not point
- /// to different elements in shared array. Therefore we need to share
- /// pointer to the whole array and remember offset where data for
- /// this option begins
+ /// @brief Ctor, used for received options.
///
/// @param type option type (usually 4 for IA_NA, 25 for IA_PD)
- /// @param buf buffer to be parsed
- /// @param buf_len buffer length
- /// @param offset offset in buffer
- /// @param len number of bytes to parse
- Option6IA(uint16_t type, const boost::shared_array<uint8_t>& buf,
- unsigned int buf_len, unsigned int offset, unsigned int len);
+ /// @param begin iterator to first byte of option data
+ /// @param end iterator to end of option data (first byte after option end)
+ Option6IA(uint16_t type, OptionBuffer::const_iterator begin,
+ OptionBuffer::const_iterator end);
/// Writes option in wire-format to buf, returns pointer to first unused
/// byte after stored option.
///
/// @param buf buffer (option will be stored here)
- /// @param buf_len (buffer length)
- /// @param offset offset place where option should be stored
- ///
- /// @return offset to the first unused byte after stored option
- unsigned int
- pack(boost::shared_array<uint8_t>& buf, unsigned int buf_len,
- unsigned int offset);
+ void pack(isc::util::OutputBuffer& buf);
/// @brief Parses received buffer
///
/// Parses received buffer and returns offset to the first unused byte after
/// parsed option.
///
- /// @param buf pointer to buffer
- /// @param buf_len length of buf
- /// @param offset offset, where start parsing option
- /// @param parse_len how many bytes should be parsed
- ///
- /// @return offset after last parsed octet
- virtual unsigned int
- unpack(const boost::shared_array<uint8_t>& buf, unsigned int buf_len,
- unsigned int offset, unsigned int parse_len);
+ /// @param begin iterator to first byte of option data
+ /// @param end iterator to end of option data (first byte after option end)
+ virtual void unpack(OptionBufferConstIter begin, OptionBufferConstIter end);
/// Provides human readable text representation
///
@@ -87,48 +67,46 @@ public:
/// Sets T1 timer.
///
/// @param t1 t1 value to be set
- void setT1(unsigned int t1) { t1_=t1; }
-
+ void setT1(uint32_t t1) { t1_=t1; }
/// Sets T2 timer.
///
/// @param t2 t2 value to be set
- void setT2(unsigned int t2) { t2_=t2; }
+ void setT2(uint32_t t2) { t2_=t2; }
/// Returns IA identifier.
///
/// @return IAID value.
///
- unsigned int getIAID() const { return iaid_; }
+ uint32_t getIAID() const { return iaid_; }
/// Returns T1 timer.
///
/// @return T1 value.
- unsigned int getT1() const { return t1_; }
+ uint32_t getT1() const { return t1_; }
/// Returns T2 timer.
///
/// @return T2 value.
- unsigned int getT2() const { return t2_; }
+ uint32_t getT2() const { return t2_; }
/// @brief returns complete length of option
///
/// Returns length of this option, including option header and suboptions
///
/// @return length of this option
- virtual uint16_t
- len();
+ virtual uint16_t len();
protected:
/// keeps IA identifier
- unsigned int iaid_;
+ uint32_t iaid_;
/// keeps T1 timer value
- unsigned int t1_;
+ uint32_t t1_;
/// keeps T2 timer value
- unsigned int t2_;
+ uint32_t t2_;
};
} // isc::dhcp namespace
diff --git a/src/lib/dhcp/option6_iaaddr.cc b/src/lib/dhcp/option6_iaaddr.cc
index 70c2948..084a5f3 100644
--- a/src/lib/dhcp/option6_iaaddr.cc
+++ b/src/lib/dhcp/option6_iaaddr.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
@@ -24,79 +24,59 @@
#include "util/io_utilities.h"
using namespace std;
-using namespace isc;
-using namespace isc::dhcp;
using namespace isc::asiolink;
using namespace isc::util;
-Option6IAAddr::Option6IAAddr(unsigned short type,
- const isc::asiolink::IOAddress& addr,
- unsigned int pref, unsigned int valid)
+namespace isc {
+namespace dhcp {
+
+Option6IAAddr::Option6IAAddr(uint16_t type, const isc::asiolink::IOAddress& addr,
+ uint32_t pref, uint32_t valid)
:Option(V6, type), addr_(addr), preferred_(pref),
valid_(valid) {
}
-Option6IAAddr::Option6IAAddr(unsigned short type,
- boost::shared_array<uint8_t> buf,
- unsigned int buf_len, unsigned int offset,
- unsigned int option_len)
+Option6IAAddr::Option6IAAddr(uint32_t type, OptionBuffer::const_iterator begin,
+ OptionBuffer::const_iterator end)
:Option(V6, type), addr_("::") {
- unpack(buf, buf_len, offset, option_len);
+ unpack(begin, end);
}
-unsigned int
-Option6IAAddr::pack(boost::shared_array<uint8_t>& buf,
- unsigned int buf_len,
- unsigned int offset) {
- if (len() > buf_len) {
- isc_throw(OutOfRange, "Failed to pack IA option: len=" << len()
- << ", buffer=" << buf_len << ": too small buffer.");
- }
-
- uint8_t* ptr = &buf[offset];
+void Option6IAAddr::pack(isc::util::OutputBuffer& buf) {
- ptr = writeUint16(type_, ptr);
+ buf.writeUint16(type_);
// len() returns complete option length. len field contains
// length without 4-byte option header
- ptr = writeUint16(len() - OPTION6_HDR_LEN, ptr);
- offset += OPTION6_HDR_LEN;
+ buf.writeUint16(len() - getHeaderLen());
- memcpy(ptr, addr_.getAddress().to_v6().to_bytes().data(), 16);
- ptr += V6ADDRESS_LEN;
- ptr = writeUint32(preferred_, ptr);
+ buf.writeData(addr_.getAddress().to_v6().to_bytes().data(),
+ isc::asiolink::V6ADDRESS_LEN);
- ptr = writeUint32(valid_, ptr);
- offset += OPTION6_IAADDR_LEN;
+ buf.writeUint32(preferred_);
+ buf.writeUint32(valid_);
- // parse suboption (there shouldn't be any)
- offset = LibDHCP::packOptions6(buf, buf_len, offset, options_);
- return offset;
+ // parse suboption (there shouldn't be any for IAADDR)
+ LibDHCP::packOptions6(buf, options_);
}
-unsigned int
-Option6IAAddr::unpack(const boost::shared_array<uint8_t>& buf,
- unsigned int buf_len,
- unsigned int offset,
- unsigned int parse_len) {
- if ( parse_len < OPTION6_IAADDR_LEN || offset + OPTION6_IAADDR_LEN > buf_len) {
+void Option6IAAddr::unpack(OptionBuffer::const_iterator begin,
+ OptionBuffer::const_iterator end) {
+ if ( distance(begin, end) < OPTION6_IAADDR_LEN) {
isc_throw(OutOfRange, "Option " << type_ << " truncated");
}
// 16 bytes: IPv6 address
- addr_ = IOAddress::from_bytes(AF_INET6, &buf[offset]);
- offset += V6ADDRESS_LEN;
+ addr_ = IOAddress::from_bytes(AF_INET6, &(*begin));
+ begin += V6ADDRESS_LEN;
- preferred_ = readUint32(&buf[offset]);
- offset += sizeof(uint32_t);
+ preferred_ = readUint32( &(*begin) );
+ begin += sizeof(uint32_t);
- valid_ = readUint32(&buf[offset]);
- offset += sizeof(uint32_t);
- offset = LibDHCP::unpackOptions6(buf, buf_len, offset,
- parse_len - 24, options_);
-
- return offset;
+ valid_ = readUint32( &(*begin) );
+ begin += sizeof(uint32_t);
+ LibDHCP::unpackOptions6(OptionBuffer(begin, end), options_);
}
std::string Option6IAAddr::toText(int indent /* =0 */) {
@@ -130,3 +110,6 @@ uint16_t Option6IAAddr::len() {
}
return (length);
}
+
+} // end of namespace isc::dhcp
+} // end of namespace isc
diff --git a/src/lib/dhcp/option6_iaaddr.h b/src/lib/dhcp/option6_iaaddr.h
index 40e5967..e6e2c16 100644
--- a/src/lib/dhcp/option6_iaaddr.h
+++ b/src/lib/dhcp/option6_iaaddr.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
@@ -27,28 +27,22 @@ public:
/// length of the fixed part of the IAADDR option
static const size_t OPTION6_IAADDR_LEN = 24;
- /// @brief ctor, used for options constructed (during transmission)
+ /// @brief Ctor, used for options constructed (during transmission).
///
/// @param type option type
/// @param addr reference to an address
/// @param preferred address preferred lifetime (in seconds)
/// @param valid address valid lifetime (in seconds)
- Option6IAAddr(unsigned short type, const isc::asiolink::IOAddress& addr,
- unsigned int preferred, unsigned int valid);
-
- /// ctor, used for received options
- /// boost::shared_array allows sharing a buffer, but it requires that
- /// different instances share pointer to the whole array, not point
- /// to different elements in shared array. Therefore we need to share
- /// pointer to the whole array and remember offset where data for
- /// this option begins
+ Option6IAAddr(uint16_t type, const isc::asiolink::IOAddress& addr,
+ uint32_t preferred, uint32_t valid);
+
+ /// @brief ctor, used for received options.
///
/// @param type option type
- /// @param buf pointer to a buffer
- /// @param offset offset to first data byte in that buffer
- /// @param len data length of this option
- Option6IAAddr(unsigned short type, boost::shared_array<uint8_t> buf,
- unsigned int buf_len, unsigned int offset, unsigned int len);
+ /// @param begin iterator to first byte of option data
+ /// @param end iterator to end of option data (first byte after option end)
+ Option6IAAddr(uint32_t type, OptionBuffer::const_iterator begin,
+ OptionBuffer::const_iterator end);
/// @brief Writes option in wire-format.
///
@@ -56,30 +50,14 @@ public:
/// byte after stored option.
///
/// @param buf pointer to a buffer
- /// @param buf_len length of the buffer
- /// @param offset offset to place, where option shout be stored
- ///
- /// @return offset to first unused byte after stored option
- unsigned int
- pack(boost::shared_array<uint8_t>& buf, unsigned int buf_len,
- unsigned int offset);
+ void pack(isc::util::OutputBuffer& buf);
- /// @brief Parses buffer.
- ///
- /// Parses received buffer, returns offset to the first unused byte after
- /// parsed option.
- ///
- /// @param buf pointer to buffer
- /// @param buf_len length of buf
- /// @param offset offset, where start parsing option
- /// @param parse_len how many bytes should be parsed
+ /// @brief Parses received buffer.
///
- /// @return offset after last parsed octet
- virtual unsigned int
- unpack(const boost::shared_array<uint8_t>& buf,
- unsigned int buf_len,
- unsigned int offset,
- unsigned int parse_len);
+ /// @param begin iterator to first byte of option data
+ /// @param end iterator to end of option data (first byte after option end)
+ virtual void unpack(OptionBufferConstIter begin,
+ OptionBufferConstIter end);
/// Returns string representation of the option.
///
diff --git a/src/lib/dhcp/pkt4.h b/src/lib/dhcp/pkt4.h
index c520747..a3f683f 100644
--- a/src/lib/dhcp/pkt4.h
+++ b/src/lib/dhcp/pkt4.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
@@ -18,7 +18,6 @@
#include <iostream>
#include <vector>
#include <boost/shared_ptr.hpp>
-#include <boost/shared_array.hpp>
#include "asiolink/io_address.h"
#include "util/buffer.h"
#include "dhcp/option.h"
@@ -103,8 +102,7 @@ public:
/// This function is useful mainly for debugging.
///
/// @return string with text representation
- std::string
- toText();
+ std::string toText();
/// @brief Returns the size of the required buffer to build the packet.
///
@@ -112,118 +110,110 @@ public:
/// the current set of packet options.
///
/// @return number of bytes required to build this packet
- size_t
- len();
+ size_t len();
- /// Sets hops field
+ /// @brief Sets hops field.
///
/// @param hops value to be set
- void
- setHops(uint8_t hops) { hops_ = hops; };
+ void setHops(uint8_t hops) { hops_ = hops; };
- /// Returns hops field
+ /// @brief Returns hops field.
///
/// @return hops field
- uint8_t
- getHops() const { return (hops_); };
+ uint8_t getHops() const { return (hops_); };
// Note: There's no need to manipulate OP field directly,
// thus no setOp() method. See op_ comment.
- /// Returns op field
+ /// @brief Returns op field.
///
/// @return op field
- uint8_t
- getOp() const { return (op_); };
+ uint8_t getOp() const { return (op_); };
- /// Sets secs field
+ /// @brief Sets secs field.
///
/// @param secs value to be set
- void
- setSecs(uint16_t secs) { secs_ = secs; };
+ void setSecs(uint16_t secs) { secs_ = secs; };
- /// Returns secs field
+ /// @brief Returns secs field.
///
/// @return secs field
- uint16_t
- getSecs() const { return (secs_); };
+ uint16_t getSecs() const { return (secs_); };
- /// Sets flags field
+ /// @brief Sets flags field.
///
/// @param flags value to be set
- void
- setFlags(uint16_t flags) { flags_ = flags; };
+ void setFlags(uint16_t flags) { flags_ = flags; };
- /// Returns flags field
+ /// @brief Returns flags field.
///
/// @return flags field
- uint16_t
- getFlags() const { return (flags_); };
+ uint16_t getFlags() const { return (flags_); };
- /// Returns ciaddr field
+ /// @brief Returns ciaddr field.
///
/// @return ciaddr field
const isc::asiolink::IOAddress&
getCiaddr() const { return (ciaddr_); };
- /// Sets ciaddr field
+ /// @brief Sets ciaddr field.
///
/// @param ciaddr value to be set
void
setCiaddr(const isc::asiolink::IOAddress& ciaddr) { ciaddr_ = ciaddr; };
- /// Returns siaddr field
+ /// @brief Returns siaddr field.
///
/// @return siaddr field
const isc::asiolink::IOAddress&
getSiaddr() const { return (siaddr_); };
- /// Sets siaddr field
+ /// @brief Sets siaddr field.
///
/// @param siaddr value to be set
void
setSiaddr(const isc::asiolink::IOAddress& siaddr) { siaddr_ = siaddr; };
- /// Returns yiaddr field
+ /// @brief Returns yiaddr field.
///
/// @return yiaddr field
const isc::asiolink::IOAddress&
getYiaddr() const { return (yiaddr_); };
- /// Sets yiaddr field
+ /// @brief Sets yiaddr field.
///
/// @param yiaddr value to be set
void
setYiaddr(const isc::asiolink::IOAddress& yiaddr) { yiaddr_ = yiaddr; };
- /// Returns giaddr field
+ /// @brief Returns giaddr field.
///
/// @return giaddr field
const isc::asiolink::IOAddress&
getGiaddr() const { return (giaddr_); };
- /// Sets giaddr field
+ /// @brief Sets giaddr field.
///
/// @param giaddr value to be set
void
setGiaddr(const isc::asiolink::IOAddress& giaddr) { giaddr_ = giaddr; };
- /// Returns value of transaction-id field
+ /// @brief Returns value of transaction-id field.
///
/// @return transaction-id
uint32_t getTransid() const { return (transid_); };
- /// Returns message type (e.g. 1 = DHCPDISCOVER)
+ /// @brief Returns message type (e.g. 1 = DHCPDISCOVER).
///
/// @return message type
uint8_t
getType() const { return (msg_type_); }
- /// Sets message type (e.g. 1 = DHCPDISCOVER)
+ /// @brief Sets message type (e.g. 1 = DHCPDISCOVER).
///
/// @param type message type to be set
void setType(uint8_t type) { msg_type_=type; };
@@ -234,14 +224,14 @@ public:
/// null-terminated. Do not use strlen() or similar on it.
///
/// @return sname field
- const std::vector<uint8_t>
+ const OptionBuffer
getSname() const { return (std::vector<uint8_t>(sname_, &sname_[MAX_SNAME_LEN])); };
- /// Sets sname field
+ /// @brief Sets sname field.
///
/// @param sname value to be set
- void
- setSname(const uint8_t* sname, size_t snameLen = MAX_SNAME_LEN);
+ /// @param sname_len length of the sname buffer (up to MAX_SNAME_LEN)
+ void setSname(const uint8_t* sname, size_t sname_len = MAX_SNAME_LEN);
/// @brief Returns file field
///
@@ -249,14 +239,15 @@ public:
/// null-terminated. Do not use strlen() or similar on it.
///
/// @return pointer to file field
- const std::vector<uint8_t>
+ const OptionBuffer
getFile() const { return (std::vector<uint8_t>(file_, &file_[MAX_FILE_LEN])); };
/// Sets file field
///
/// @param file value to be set
+ /// @param file_len length of the file buffer (up to MAX_FILE_LEN)
void
- setFile(const uint8_t* file, size_t fileLen = MAX_FILE_LEN);
+ setFile(const uint8_t* file, size_t file_len = MAX_FILE_LEN);
/// @brief Sets hardware address.
///
@@ -266,7 +257,7 @@ public:
///
/// Note: macAddr must be a buffer of at least hlen bytes.
///
- /// @param hwType hardware type (will be sent in htype field)
+ /// @param hType hardware type (will be sent in htype field)
/// @param hlen hardware length (will be sent in hlen field)
/// @param macAddr pointer to hardware address
void setHWAddr(uint8_t hType, uint8_t hlen,
@@ -350,7 +341,7 @@ public:
/// @brief Sets remote address.
///
- /// @params remote specifies remote address
+ /// @param remote specifies remote address
void setRemoteAddr(const isc::asiolink::IOAddress& remote) {
remote_addr_ = remote;
}
@@ -364,7 +355,7 @@ public:
/// @brief Sets local address.
///
- /// @params local specifies local address
+ /// @param local specifies local address
void setLocalAddr(const isc::asiolink::IOAddress& local) {
local_addr_ = local;
}
@@ -378,7 +369,7 @@ public:
/// @brief Sets local port.
///
- /// @params local specifies local port
+ /// @param local specifies local port
void setLocalPort(uint16_t local) { local_port_ = local; }
/// @brief Returns local port.
@@ -388,7 +379,7 @@ public:
/// @brief Sets remote port.
///
- /// @params remote specifies remote port
+ /// @param remote specifies remote port
void setRemotePort(uint16_t remote) { remote_port_ = remote; }
/// @brief Returns remote port.
@@ -478,13 +469,13 @@ protected:
// end of real DHCPv4 fields
- /// output buffer (used during message
+ /// output buffer (used during message transmission)
isc::util::OutputBuffer bufferOut_;
- // that's the data of input buffer used in RX packet. Note that
- // InputBuffer does not store the data itself, but just expects that
- // data will be valid for the whole life of InputBuffer. Therefore we
- // need to keep the data around.
+ /// that's the data of input buffer used in RX packet. Note that
+ /// InputBuffer does not store the data itself, but just expects that
+ /// data will be valid for the whole life of InputBuffer. Therefore we
+ /// need to keep the data around.
std::vector<uint8_t> data_;
/// message type (e.g. 1=DHCPDISCOVER)
@@ -496,6 +487,8 @@ protected:
isc::dhcp::Option::OptionCollection options_;
}; // Pkt4 class
+typedef boost::shared_ptr<Pkt4> Pkt4Ptr;
+
} // isc::dhcp namespace
} // isc namespace
diff --git a/src/lib/dhcp/pkt6.cc b/src/lib/dhcp/pkt6.cc
index ff27d5e..aea3cde 100644
--- a/src/lib/dhcp/pkt6.cc
+++ b/src/lib/dhcp/pkt6.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
@@ -13,55 +13,48 @@
// PERFORMANCE OF THIS SOFTWARE.
-#include "dhcp/dhcp6.h"
-#include "dhcp/pkt6.h"
-#include "dhcp/libdhcp++.h"
-#include "exceptions/exceptions.h"
+#include <dhcp/dhcp6.h>
+#include <dhcp/pkt6.h>
+#include <dhcp/libdhcp++.h>
+#include <exceptions/exceptions.h>
#include <iostream>
#include <sstream>
using namespace std;
-using namespace isc::dhcp;
namespace isc {
-
-Pkt6::Pkt6(unsigned int dataLen, DHCPv6Proto proto /* = UDP */)
- :data_len_(dataLen),
- local_addr_("::"),
- remote_addr_("::"),
- iface_(""),
- ifindex_(-1),
- local_port_(-1),
- remote_port_(-1),
- proto_(proto),
- msg_type_(-1),
- transid_(rand()%0xffffff)
-{
-
- data_ = boost::shared_array<uint8_t>(new uint8_t[dataLen]);
- data_len_ = dataLen;
+namespace dhcp {
+
+Pkt6::Pkt6(const uint8_t* buf, uint32_t buf_len, DHCPv6Proto proto /* = UDP */) :
+ proto_(proto),
+ msg_type_(0),
+ transid_(rand()%0xffffff),
+ iface_(""),
+ ifindex_(-1),
+ local_addr_("::"),
+ remote_addr_("::"),
+ local_port_(0),
+ remote_port_(0),
+ bufferOut_(0) {
+ data_.resize(buf_len);
+ memcpy(&data_[0], buf, buf_len);
}
-Pkt6::Pkt6(uint8_t msg_type,
- unsigned int transid,
- DHCPv6Proto proto /*= UDP*/)
- :local_addr_("::"),
- remote_addr_("::"),
- iface_(""),
- ifindex_(-1),
- local_port_(-1),
- remote_port_(-1),
- proto_(proto),
- msg_type_(msg_type),
- transid_(transid) {
-
- data_ = boost::shared_array<uint8_t>(new uint8_t[4]);
- data_len_ = 4;
+Pkt6::Pkt6(uint8_t msg_type, uint32_t transid, DHCPv6Proto proto /*= UDP*/) :
+ proto_(proto),
+ msg_type_(msg_type),
+ transid_(transid),
+ iface_(""),
+ ifindex_(-1),
+ local_addr_("::"),
+ remote_addr_("::"),
+ local_port_(0),
+ remote_port_(0),
+ bufferOut_(0) {
}
-unsigned short
-Pkt6::len() {
- unsigned int length = DHCPV6_PKT_HDR_LEN; // DHCPv6 header
+uint16_t Pkt6::len() {
+ uint16_t length = DHCPV6_PKT_HDR_LEN; // DHCPv6 header
for (Option::OptionCollection::iterator it = options_.begin();
it != options_.end();
@@ -95,44 +88,21 @@ Pkt6::packUDP() {
// It is better to implement a method in IOAddress that extracts
// vector<uint8_t>
- unsigned short length = len();
- if (data_len_ < length) {
- cout << "Previous len=" << data_len_ << ", allocating new buffer: len="
- << length << endl;
-
- // May throw exception if out of memory. That is rather fatal,
- // so we don't catch this
- data_ = boost::shared_array<uint8_t>(new uint8_t[length]);
- data_len_ = length;
- }
-
- data_len_ = length;
try {
// DHCPv6 header: message-type (1 octect) + transaction id (3 octets)
- data_[0] = msg_type_;
-
+ bufferOut_.writeUint8(msg_type_);
// store 3-octet transaction-id
- data_[1] = (transid_ >> 16) & 0xff;
- data_[2] = (transid_ >> 8) & 0xff;
- data_[3] = (transid_) & 0xff;
+ bufferOut_.writeUint8( (transid_ >> 16) & 0xff );
+ bufferOut_.writeUint8( (transid_ >> 8) & 0xff );
+ bufferOut_.writeUint8( (transid_) & 0xff );
// the rest are options
- unsigned short offset = LibDHCP::packOptions6(data_, length,
- 4/*offset*/,
- options_);
-
- // sanity check
- if (offset != length) {
- isc_throw(OutOfRange, "Packet build failed: expected size="
- << length << ", actual len=" << offset);
- }
+ LibDHCP::packOptions6(bufferOut_, options_);
}
catch (const Exception& e) {
cout << "Packet build failed:" << e.what() << endl;
return (false);
}
- // Limited verbosity of this method
- // cout << "Packet built, len=" << len() << endl;
return (true);
}
@@ -158,8 +128,8 @@ Pkt6::unpack() {
bool
Pkt6::unpackUDP() {
- if (data_len_ < 4) {
- std::cout << "DHCPv6 packet truncated. Only " << data_len_
+ if (data_.size() < 4) {
+ std::cout << "DHCPv6 packet truncated. Only " << data_.size()
<< " bytes. Need at least 4." << std::endl;
return (false);
}
@@ -168,16 +138,13 @@ Pkt6::unpackUDP() {
((data_[2]) << 8) + (data_[3]);
transid_ = transid_ & 0xffffff;
- unsigned int offset = LibDHCP::unpackOptions6(data_,
- data_len_,
- 4, //offset
- data_len_ - 4,
- options_);
- if (offset != data_len_) {
- cout << "DHCPv6 packet contains trailing garbage. Parsed "
- << offset << " bytes, packet is " << data_len_ << " bytes."
- << endl;
- // just a warning. Ignore trailing garbage and continue
+ try {
+ OptionBuffer opt_buffer(data_.begin() + 4, data_.end());
+
+ LibDHCP::unpackOptions6(opt_buffer, options_);
+ } catch (const Exception& e) {
+ cout << "Packet parsing failed:" << e.what() << endl;
+ return (false);
}
return (true);
}
@@ -206,7 +173,7 @@ Pkt6::toText() {
}
boost::shared_ptr<isc::dhcp::Option>
-Pkt6::getOption(unsigned short opt_type) {
+Pkt6::getOption(uint16_t opt_type) {
isc::dhcp::Option::OptionCollection::const_iterator x = options_.find(opt_type);
if (x!=options_.end()) {
return (*x).second;
@@ -220,7 +187,7 @@ Pkt6::addOption(boost::shared_ptr<Option> opt) {
}
bool
-Pkt6::delOption(unsigned short type) {
+Pkt6::delOption(uint16_t type) {
isc::dhcp::Option::OptionCollection::iterator x = options_.find(type);
if (x!=options_.end()) {
options_.erase(x);
@@ -229,4 +196,11 @@ Pkt6::delOption(unsigned short type) {
return (false); // can't find option to be deleted
}
-};
+void Pkt6::repack() {
+ cout << "Convering RX packet to TX packet: " << data_.size() << " bytes." << endl;
+
+ bufferOut_.writeData(&data_[0], data_.size());
+}
+
+} // end of isc::dhcp namespace
+} // end of isc namespace
diff --git a/src/lib/dhcp/pkt6.h b/src/lib/dhcp/pkt6.h
index 019eeb2..97ac996 100644
--- a/src/lib/dhcp/pkt6.h
+++ b/src/lib/dhcp/pkt6.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
@@ -41,17 +41,18 @@ public:
/// @param msg_type type of message (SOLICIT=1, ADVERTISE=2, ...)
/// @param transid transaction-id
/// @param proto protocol (TCP or UDP)
- Pkt6(unsigned char msg_type,
- unsigned int transid,
+ Pkt6(uint8_t msg_type,
+ uint32_t transid,
DHCPv6Proto proto = UDP);
/// Constructor, used in message transmission
///
/// Creates new message. Transaction-id will randomized.
///
- /// @param len size of buffer to be allocated for this packet.
+ /// @param buf pointer to a buffer of received packet content
+ /// @param len size of buffer of received packet content
/// @param proto protocol (usually UDP, but TCP will be supported eventually)
- Pkt6(unsigned int len, DHCPv6Proto proto = UDP);
+ Pkt6(const uint8_t* buf, uint32_t len, DHCPv6Proto proto = UDP);
/// @brief Prepares on-wire format.
///
@@ -61,8 +62,7 @@ public:
/// will be set in data_len_.
///
/// @return true if packing procedure was successful
- bool
- pack();
+ bool pack();
/// @brief Dispatch method that handles binary packet parsing.
///
@@ -70,59 +70,74 @@ public:
/// unpackTCP).
///
/// @return true if parsing was successful
- bool
- unpack();
+ bool unpack();
- /// Returns protocol of this packet (UDP or TCP)
+ /// @brief Returns reference to output buffer.
+ ///
+ /// Returned buffer will contain reasonable data only for
+ /// output (TX) packet and after pack() was called. This buffer
+ /// is only valid till Pkt6 object is valid.
+ ///
+ /// RX packet or TX packet before pack() will return buffer with
+ /// zero length
+ ///
+ /// @return reference to output buffer
+ const isc::util::OutputBuffer& getBuffer() const { return (bufferOut_); };
+
+
+ /// @brief Returns reference to input buffer.
+ ///
+ /// @return reference to input buffer
+ const OptionBuffer& getData() const { return(data_); }
+
+ /// @brief Returns protocol of this packet (UDP or TCP).
///
/// @return protocol type
- DHCPv6Proto
- getProto();
+ DHCPv6Proto getProto();
/// Sets protocol of this packet.
///
/// @param proto protocol (UDP or TCP)
- ///
- void
- setProto(DHCPv6Proto proto = UDP) { proto_ = proto; }
+ void setProto(DHCPv6Proto proto = UDP) { proto_ = proto; }
/// @brief Returns text representation of the packet.
///
/// This function is useful mainly for debugging.
///
/// @return string with text representation
- std::string
- toText();
+ std::string toText();
- /// @brief Returns calculated length of the packet.
+ /// @brief Returns length of the packet.
+ ///
+ /// This function returns size required to hold this packet.
+ /// It includes DHCPv6 header and all options stored in
+ /// options_ field.
///
- /// This function returns size of required buffer to buld this packet.
- /// To use that function, options_ field must be set.
+ /// Note: It does not return proper length of incoming packets
+ /// before they are unpacked.
///
- /// @return number of bytes required to build this packet
- unsigned short
- len();
+ /// @return number of bytes required to assemble this packet
+ uint16_t len();
/// Returns message type (e.g. 1 = SOLICIT)
///
/// @return message type
- unsigned char
- getType() { return (msg_type_); }
+ uint8_t getType() { return (msg_type_); }
/// Sets message type (e.g. 1 = SOLICIT)
///
/// @param type message type to be set
- void setType(unsigned char type) { msg_type_=type; };
+ void setType(uint8_t type) { msg_type_=type; };
/// Returns value of transaction-id field
///
/// @return transaction-id
- unsigned int getTransid() { return (transid_); };
+ uint32_t getTransid() { return (transid_); };
/// Adds an option to this packet.
///
/// @param opt option to be added.
- void addOption(boost::shared_ptr<isc::dhcp::Option> opt);
+ void addOption(OptionPtr opt);
/// @brief Returns the first option of specified type.
///
@@ -130,52 +145,88 @@ public:
/// instances of the same option are allowed (and frequently used).
/// See getOptions().
///
- /// @param opt_type option type we are looking for
+ /// @param type option type we are looking for
///
/// @return pointer to found option (or NULL)
- boost::shared_ptr<isc::dhcp::Option>
- getOption(unsigned short type);
+ OptionPtr getOption(uint16_t type);
/// Attempts to delete first suboption of requested type
///
/// @param type Type of option to be deleted.
///
/// @return true if option was deleted, false if no such option existed
- bool
- delOption(unsigned short type);
+ bool delOption(uint16_t type);
- /// TODO need getter/setter wrappers
- /// and hide following fields as protected
+ /// @brief This method copies data from output buffer to input buffer
+ ///
+ /// This is useful only in testing
+ void repack();
- /// buffer that holds memory. It is shared_array as options may
- /// share pointer to this buffer
- boost::shared_array<uint8_t> data_;
+ /// @brief Sets remote address.
+ ///
+ /// @param remote specifies remote address
+ void setRemoteAddr(const isc::asiolink::IOAddress& remote) { remote_addr_ = remote; }
- /// length of the data
- unsigned int data_len_;
+ /// @brief Returns remote address
+ ///
+ /// @return remote address
+ const isc::asiolink::IOAddress& getRemoteAddr() { return (remote_addr_); }
- /// local address (dst if receiving packet, src if sending packet)
- isc::asiolink::IOAddress local_addr_;
+ /// @brief Sets local address.
+ ///
+ /// @param local specifies local address
+ void setLocalAddr(const isc::asiolink::IOAddress& local) { local_addr_ = local; }
- /// remote address (src if receiving packet, dst if sending packet)
- isc::asiolink::IOAddress remote_addr_;
+ /// @brief Returns local address.
+ ///
+ /// @return local address
+ const isc::asiolink::IOAddress& getLocalAddr() { return (local_addr_); }
- /// name of the network interface the packet was received/to be sent over
- std::string iface_;
+ /// @brief Sets local port.
+ ///
+ /// @param local specifies local port
+ void setLocalPort(uint16_t local) { local_port_ = local; }
- /// @brief interface index
+ /// @brief Returns local port.
///
- /// interface index (each network interface has assigned unique ifindex
- /// it is functional equvalent of name, but sometimes more useful, e.g.
- /// when using crazy systems that allow spaces in interface names
- /// e.g. windows
- int ifindex_;
+ /// @return local port
+ uint16_t getLocalPort() { return (local_port_); }
- /// local TDP or UDP port
- int local_port_;
+ /// @brief Sets remote port.
+ ///
+ /// @param remote specifies remote port
+ void setRemotePort(uint16_t remote) { remote_port_ = remote; }
- /// remote TCP or UDP port
- int remote_port_;
+ /// @brief Returns remote port.
+ ///
+ /// @return remote port
+ uint16_t getRemotePort() { return (remote_port_); }
+
+ /// @brief Sets interface index.
+ ///
+ /// @param ifindex specifies interface index.
+ void setIndex(uint32_t ifindex) { ifindex_ = ifindex; };
+
+ /// @brief Returns interface index.
+ ///
+ /// @return interface index
+ uint32_t getIndex() const { return (ifindex_); };
+
+ /// @brief Returns interface name.
+ ///
+ /// Returns interface name over which packet was received or is
+ /// going to be transmitted.
+ ///
+ /// @return interface name
+ std::string getIface() const { return iface_; };
+
+ /// @brief Sets interface name.
+ ///
+ /// Sets interface name over which packet was received or is
+ /// going to be transmitted.
+ ///
+ /// @return interface name
+ void setIface(const std::string& iface ) { iface_ = iface; };
/// TODO Need to implement getOptions() as well
@@ -221,12 +272,43 @@ protected:
DHCPv6Proto proto_;
/// DHCPv6 message type
- int msg_type_;
+ uint8_t msg_type_;
/// DHCPv6 transaction-id
- unsigned int transid_;
+ uint32_t transid_;
+
+ /// unparsed data (in received packets)
+ OptionBuffer data_;
+
+ /// name of the network interface the packet was received/to be sent over
+ std::string iface_;
+
+ /// @brief interface index
+ ///
+ /// interface index (each network interface has assigned unique ifindex
+ /// it is functional equvalent of name, but sometimes more useful, e.g.
+ /// when using crazy systems that allow spaces in interface names
+ /// e.g. windows
+ int ifindex_;
+
+ /// local address (dst if receiving packet, src if sending packet)
+ isc::asiolink::IOAddress local_addr_;
+
+ /// remote address (src if receiving packet, dst if sending packet)
+ isc::asiolink::IOAddress remote_addr_;
+
+ /// local TDP or UDP port
+ uint16_t local_port_;
+
+ /// remote TCP or UDP port
+ uint16_t remote_port_;
+
+ /// output buffer (used during message transmission)
+ isc::util::OutputBuffer bufferOut_;
}; // Pkt6 class
+typedef boost::shared_ptr<Pkt6> Pkt6Ptr;
+
} // isc::dhcp namespace
} // isc namespace
diff --git a/src/lib/dhcp/tests/Makefile.am b/src/lib/dhcp/tests/Makefile.am
index e86fa24..c68de01 100644
--- a/src/lib/dhcp/tests/Makefile.am
+++ b/src/lib/dhcp/tests/Makefile.am
@@ -23,6 +23,7 @@ libdhcp___unittests_SOURCES += libdhcp++_unittest.cc
libdhcp___unittests_SOURCES += ../iface_mgr.cc ../iface_mgr.h iface_mgr_unittest.cc
libdhcp___unittests_SOURCES += ../iface_mgr_linux.cc
libdhcp___unittests_SOURCES += ../iface_mgr_bsd.cc
+libdhcp___unittests_SOURCES += ../iface_mgr_sun.cc
libdhcp___unittests_SOURCES += ../option6_iaaddr.h ../option6_iaaddr.cc option6_iaaddr_unittest.cc
libdhcp___unittests_SOURCES += ../option6_ia.h ../option6_ia.cc option6_ia_unittest.cc
libdhcp___unittests_SOURCES += ../option6_addrlst.h ../option6_addrlst.cc option6_addrlst_unittest.cc
diff --git a/src/lib/dhcp/tests/iface_mgr_unittest.cc b/src/lib/dhcp/tests/iface_mgr_unittest.cc
index 5d666b3..83ba231 100644
--- a/src/lib/dhcp/tests/iface_mgr_unittest.cc
+++ b/src/lib/dhcp/tests/iface_mgr_unittest.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
@@ -7,7 +7,7 @@
// 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
+ // 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.
@@ -127,8 +127,8 @@ TEST_F(IfaceMgrTest, dhcp6Sniffer) {
cout << " pkt->ifindex_ = " << pkt->ifindex_ << ";" << endl;
cout << " pkt->iface_ = \"" << pkt->iface_ << "\";" << endl;
- // TODO it is better to declare an array and then memcpy it to
- // packet.
+ // TODO it is better to declare statically initialize the array
+ // and then memcpy it to packet.
for (int i=0; i< pkt->data_len_; i++) {
cout << " pkt->data_[" << i << "]="
<< (int)(unsigned char)pkt->data_[i] << "; ";
@@ -202,16 +202,16 @@ TEST_F(IfaceMgrTest, getIface) {
// check that interface can be retrieved by ifindex
IfaceMgr::Iface* tmp = ifacemgr->getIface(5);
// ASSERT_NE(NULL, tmp); is not supported. hmmmm.
- ASSERT_TRUE( tmp != NULL );
+ ASSERT_TRUE(tmp != NULL);
- EXPECT_EQ( "en3", tmp->getName() );
+ EXPECT_EQ("en3", tmp->getName());
EXPECT_EQ(5, tmp->getIndex());
// check that interface can be retrieved by name
tmp = ifacemgr->getIface("lo1");
- ASSERT_TRUE( tmp != NULL );
+ ASSERT_TRUE(tmp != NULL);
- EXPECT_EQ( "lo1", tmp->getName() );
+ EXPECT_EQ("lo1", tmp->getName());
EXPECT_EQ(1, tmp->getIndex());
// check that non-existing interfaces are not returned
@@ -237,7 +237,7 @@ TEST_F(IfaceMgrTest, detectIfaces_stub) {
NakedIfaceMgr* ifacemgr = new NakedIfaceMgr();
- ASSERT_TRUE( ifacemgr->getIface("eth0") != NULL );
+ ASSERT_TRUE(ifacemgr->getIface("eth0") != NULL);
IfaceMgr::Iface* eth0 = ifacemgr->getIface("eth0");
@@ -247,7 +247,7 @@ TEST_F(IfaceMgrTest, detectIfaces_stub) {
IOAddress addr = *addrs.begin();
- EXPECT_STREQ( "fe80::1234", addr.toText().c_str() );
+ EXPECT_STREQ("fe80::1234", addr.toText().c_str());
delete ifacemgr;
}
@@ -263,8 +263,8 @@ TEST_F(IfaceMgrTest, sockets6) {
IOAddress loAddr("::1");
- Pkt6 pkt6(128);
- pkt6.iface_ = LOOPBACK;
+ Pkt6 pkt6(DHCPV6_SOLICIT, 123);
+ pkt6.setIface(LOOPBACK);
// bind multicast socket to port 10547
int socket1 = ifacemgr->openSocket(LOOPBACK, loAddr, 10547);
@@ -335,38 +335,41 @@ TEST_F(IfaceMgrTest, sendReceive6) {
EXPECT_GT(socket1, 0);
EXPECT_GT(socket2, 0);
- boost::shared_ptr<Pkt6> sendPkt(new Pkt6(128) );
// prepare dummy payload
- for (int i=0;i<128; i++) {
- sendPkt->data_[i] = i;
+ uint8_t data[128];
+ for (int i = 0; i < 128; i++) {
+ data[i] = i;
}
+ Pkt6Ptr sendPkt = Pkt6Ptr(new Pkt6(data, 128));
- sendPkt->remote_port_ = 10547;
- sendPkt->remote_addr_ = IOAddress("::1");
- sendPkt->ifindex_ = 1;
- sendPkt->iface_ = LOOPBACK;
+ sendPkt->repack();
- boost::shared_ptr<Pkt6> rcvPkt;
+ sendPkt->setRemotePort(10547);
+ sendPkt->setRemoteAddr(IOAddress("::1"));
+ sendPkt->setIndex(1);
+ sendPkt->setIface(LOOPBACK);
+
+ Pkt6Ptr rcvPkt;
EXPECT_EQ(true, ifacemgr->send(sendPkt));
rcvPkt = ifacemgr->receive6();
- ASSERT_TRUE( rcvPkt ); // received our own packet
+ ASSERT_TRUE(rcvPkt); // received our own packet
// let's check that we received what was sent
- EXPECT_EQ(sendPkt->data_len_, rcvPkt->data_len_);
- EXPECT_EQ(0, memcmp(&sendPkt->data_[0], &rcvPkt->data_[0],
- rcvPkt->data_len_) );
+ ASSERT_EQ(sendPkt->getData().size(), rcvPkt->getData().size());
+ EXPECT_EQ(0, memcmp(&sendPkt->getData()[0], &rcvPkt->getData()[0],
+ rcvPkt->getData().size()));
- EXPECT_EQ(sendPkt->remote_addr_.toText(), rcvPkt->remote_addr_.toText());
+ EXPECT_EQ(sendPkt->getRemoteAddr().toText(), rcvPkt->getRemoteAddr().toText());
// since we opened 2 sockets on the same interface and none of them is multicast,
// none is preferred over the other for sending data, so we really should not
// assume the one or the other will always be choosen for sending data. Therefore
// we should accept both values as source ports.
- EXPECT_TRUE( (rcvPkt->remote_port_ == 10546) || (rcvPkt->remote_port_ == 10547) );
+ EXPECT_TRUE((rcvPkt->getRemotePort() == 10546) || (rcvPkt->getRemotePort() == 10547));
delete ifacemgr;
}
@@ -427,7 +430,7 @@ TEST_F(IfaceMgrTest, sendReceive4) {
rcvPkt = ifacemgr->receive4();
- ASSERT_TRUE( rcvPkt ); // received our own packet
+ ASSERT_TRUE(rcvPkt); // received our own packet
ASSERT_NO_THROW(
rcvPkt->unpack();
@@ -532,6 +535,39 @@ TEST_F(IfaceMgrTest, iface) {
);
}
+TEST_F(IfaceMgrTest, iface_methods) {
+ IfaceMgr::Iface iface("foo", 1234);
+
+ iface.setHWType(42);
+ EXPECT_EQ(42, iface.getHWType());
+
+ uint8_t mac[IfaceMgr::MAX_MAC_LEN+10];
+ for (int i = 0; i < IfaceMgr::MAX_MAC_LEN + 10; i++)
+ mac[i] = 255 - i;
+
+ EXPECT_EQ("foo", iface.getName());
+ EXPECT_EQ(1234, iface.getIndex());
+
+ // MAC is too long. Exception should be thrown and
+ // MAC length should not be set.
+ EXPECT_THROW(
+ iface.setMac(mac, IfaceMgr::MAX_MAC_LEN + 1),
+ OutOfRange
+ );
+
+ // MAC length should stay not set as excep
+ EXPECT_EQ(0, iface.getMacLen());
+
+ // Setting maximum length MAC should be ok.
+ iface.setMac(mac, IfaceMgr::MAX_MAC_LEN);
+
+ // For some reason constants cannot be used directly in EXPECT_EQ
+ // as this produces linking error.
+ size_t len = IfaceMgr::MAX_MAC_LEN;
+ EXPECT_EQ(len, iface.getMacLen());
+ EXPECT_EQ(0, memcmp(mac, iface.getMac(), iface.getMacLen()));
+}
+
TEST_F(IfaceMgrTest, socketInfo) {
// check that socketinfo for IPv4 socket is functional
@@ -556,7 +592,7 @@ TEST_F(IfaceMgrTest, socketInfo) {
loopback->addSocket(sock1);
loopback->addSocket(sock2);
- Pkt6 pkt6(100);
+ Pkt6 pkt6(DHCPV6_REPLY, 123456);
// pkt6 dos not have interface set yet
EXPECT_THROW(
@@ -565,14 +601,14 @@ TEST_F(IfaceMgrTest, socketInfo) {
);
// try to send over non-existing interface
- pkt6.iface_ = "nosuchinterface45";
+ pkt6.setIface("nosuchinterface45");
EXPECT_THROW(
ifacemgr->getSocket(pkt6),
BadValue
);
// this will work
- pkt6.iface_ = LOOPBACK;
+ pkt6.setIface(LOOPBACK);
EXPECT_EQ(9, ifacemgr->getSocket(pkt6));
bool deleted = false;
@@ -679,6 +715,8 @@ size_t parse_mac(const std::string& textMac, uint8_t* mac, size_t macLen) {
/// of text that ifconfig prints and robustness to handle slight differences
/// in ifconfig output.
///
+/// @todo: Consider using isc::util::str::tokens here.
+///
/// @param textFile name of a text file that holds output of ifconfig -a
/// @param ifaces empty list of interfaces to be filled
void parse_ifconfig(const std::string& textFile, IfaceMgr::IfaceCollection& ifaces) {
@@ -708,6 +746,11 @@ void parse_ifconfig(const std::string& textFile, IfaceMgr::IfaceCollection& ifac
if (offset == string::npos) {
isc_throw(BadValue, "Malformed output of ifconfig");
}
+
+ // ifconfig in Gentoo prints out eth0: instead of eth0
+ if (line[offset - 1] == ':') {
+ offset--;
+ }
string name = line.substr(0, offset);
// sadly, ifconfig does not return ifindex
@@ -723,19 +766,41 @@ void parse_ifconfig(const std::string& textFile, IfaceMgr::IfaceCollection& ifac
mac = line.substr(offset, string::npos);
mac = mac.substr(0, mac.find_first_of(" "));
- iface->mac_len_ = parse_mac(mac, iface->mac_, IfaceMgr::MAX_MAC_LEN);
+ uint8_t buf[IfaceMgr::MAX_MAC_LEN];
+ int mac_len = parse_mac(mac, buf, IfaceMgr::MAX_MAC_LEN);
+ iface->setMac(buf, mac_len);
}
}
if (line.find("inet6") != string::npos) {
// IPv6 address
- string addr = line.substr(line.find("inet6")+12, string::npos);
+ string addr;
+ if (line.find("addr:", line.find("inet6")) != string::npos) {
+ // Ubuntu style format: inet6 addr: ::1/128 Scope:Host
+ addr = line.substr(line.find("addr:") + 6, string::npos);
+ } else {
+ // Gentoo style format: inet6 fe80::6ef0:49ff:fe96:ba17 prefixlen 64 scopeid 0x20<link>
+ addr = line.substr(line.find("inet6") + 6, string::npos);
+ }
+
+ // handle Ubuntu format: inet6 addr: fe80::f66d:4ff:fe96:58f2/64 Scope:Link
addr = addr.substr(0, addr.find("/"));
+
+ // handle inet6 fe80::ca3a:35ff:fed4:8f1d prefixlen 64 scopeid 0x20<link>
+ addr = addr.substr(0, addr.find(" "));
IOAddress a(addr);
iface->addAddress(a);
} else if(line.find("inet") != string::npos) {
// IPv4 address
- string addr = line.substr(line.find("inet")+10, string::npos);
+ string addr;
+ if (line.find("addr:", line.find("inet")) != string::npos) {
+ // Ubuntu style format: inet addr:127.0.0.1 Mask:255.0.0.0
+ addr = line.substr(line.find("addr:") + 5, string::npos);
+ } else {
+ // Gentoo style format: inet 10.53.0.4 netmask 255.255.255.0
+ addr = line.substr(line.find("inet") + 5, string::npos);
+ }
+
addr = addr.substr(0, addr.find_first_of(" "));
IOAddress a(addr);
iface->addAddress(a);
@@ -786,7 +851,7 @@ TEST_F(IfaceMgrTest, DISABLED_detectIfaces_linux) {
// list of interfaces parsed from ifconfig
IfaceMgr::IfaceCollection parsedIfaces;
- EXPECT_NO_THROW(
+ ASSERT_NO_THROW(
parse_ifconfig(textFile, parsedIfaces);
);
unlink(textFile.c_str());
@@ -845,8 +910,8 @@ TEST_F(IfaceMgrTest, DISABLED_detectIfaces_linux) {
// skip MAC comparison for loopback as netlink returns MAC
// 00:00:00:00:00:00 for lo
if (!detected->flag_loopback_) {
- ASSERT_EQ(detected->mac_len_, i->mac_len_);
- EXPECT_EQ(0, memcmp(detected->mac_, i->mac_, i->mac_len_));
+ ASSERT_EQ(detected->getMacLen(), i->getMacLen());
+ EXPECT_EQ(0, memcmp(detected->getMac(), i->getMac(), i->getMacLen()));
}
EXPECT_EQ(detected->getAddresses().size(), i->getAddresses().size());
diff --git a/src/lib/dhcp/tests/libdhcp++_unittest.cc b/src/lib/dhcp/tests/libdhcp++_unittest.cc
index ee3b873..7e18be6 100644
--- a/src/lib/dhcp/tests/libdhcp++_unittest.cc
+++ b/src/lib/dhcp/tests/libdhcp++_unittest.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
@@ -42,7 +42,7 @@ static const uint8_t packed[] = {
};
TEST(LibDhcpTest, packOptions6) {
- boost::shared_array<uint8_t> buf(new uint8_t[512]);
+ OptionBuffer buf(512);
isc::dhcp::Option::OptionCollection opts; // list of options
// generate content for options
@@ -50,24 +50,23 @@ TEST(LibDhcpTest, packOptions6) {
buf[i]=i+100;
}
- boost::shared_ptr<Option> opt1(new Option(Option::V6, 12, buf, 0, 5));
- boost::shared_ptr<Option> opt2(new Option(Option::V6, 13, buf, 5, 3));
- boost::shared_ptr<Option> opt3(new Option(Option::V6, 14, buf, 8, 2));
- boost::shared_ptr<Option> opt4(new Option(Option::V6,256, buf,10, 4));
- boost::shared_ptr<Option> opt5(new Option(Option::V6,257, buf,14, 1));
+ OptionPtr opt1(new Option(Option::V6, 12, buf.begin() + 0, buf.begin() + 5));
+ OptionPtr opt2(new Option(Option::V6, 13, buf.begin() + 5, buf.begin() + 8));
+ OptionPtr opt3(new Option(Option::V6, 14, buf.begin() + 8, buf.begin() + 10));
+ OptionPtr opt4(new Option(Option::V6,256, buf.begin() + 10,buf.begin() + 14));
+ OptionPtr opt5(new Option(Option::V6,257, buf.begin() + 14,buf.begin() + 15));
- opts.insert(pair<int, boost::shared_ptr<Option> >(opt1->getType(), opt1));
- opts.insert(pair<int, boost::shared_ptr<Option> >(opt1->getType(), opt2));
- opts.insert(pair<int, boost::shared_ptr<Option> >(opt1->getType(), opt3));
- opts.insert(pair<int, boost::shared_ptr<Option> >(opt1->getType(), opt4));
- opts.insert(pair<int, boost::shared_ptr<Option> >(opt1->getType(), opt5));
+ opts.insert(pair<int, OptionPtr >(opt1->getType(), opt1));
+ opts.insert(pair<int, OptionPtr >(opt1->getType(), opt2));
+ opts.insert(pair<int, OptionPtr >(opt1->getType(), opt3));
+ opts.insert(pair<int, OptionPtr >(opt1->getType(), opt4));
+ opts.insert(pair<int, OptionPtr >(opt1->getType(), opt5));
- unsigned int offset;
- EXPECT_NO_THROW ({
- offset = LibDHCP::packOptions6(buf, 512, 100, opts);
- });
- EXPECT_EQ(135, offset); // options should take 35 bytes
- EXPECT_EQ(0, memcmp(&buf[100], packed, 35) );
+ OutputBuffer assembled(512);
+
+ EXPECT_NO_THROW(LibDHCP::packOptions6(assembled, opts));
+ EXPECT_EQ(35, assembled.getLength()); // options should take 35 bytes
+ EXPECT_EQ(0, memcmp(assembled.getData(), packed, 35) );
}
TEST(LibDhcpTest, unpackOptions6) {
@@ -78,17 +77,13 @@ TEST(LibDhcpTest, unpackOptions6) {
// specific derived classes.
isc::dhcp::Option::OptionCollection options; // list of options
- // we can't use packed directly, as shared_array would try to
- // free it eventually
- boost::shared_array<uint8_t> buf(new uint8_t[512]);
+ OptionBuffer buf(512);
memcpy(&buf[0], packed, 35);
- unsigned int offset;
EXPECT_NO_THROW ({
- offset = LibDHCP::unpackOptions6(buf, 512, 0, 35, options);
+ LibDHCP::unpackOptions6(OptionBuffer(buf.begin(), buf.begin()+35), options);
});
- EXPECT_EQ(35, offset); // parsed first 35 bytes (offset 0..34)
EXPECT_EQ(options.size(), 5); // there should be 5 options
isc::dhcp::Option::OptionCollection::const_iterator x = options.find(12);
@@ -153,25 +148,23 @@ TEST(LibDhcpTest, packOptions4) {
payload[i][2] = i*10+2;
}
- boost::shared_ptr<Option> opt1(new Option(Option::V4, 12, payload[0]));
- boost::shared_ptr<Option> opt2(new Option(Option::V4, 13, payload[1]));
- boost::shared_ptr<Option> opt3(new Option(Option::V4, 14, payload[2]));
- boost::shared_ptr<Option> opt4(new Option(Option::V4,254, payload[3]));
- boost::shared_ptr<Option> opt5(new Option(Option::V4,128, payload[4]));
+ OptionPtr opt1(new Option(Option::V4, 12, payload[0]));
+ OptionPtr opt2(new Option(Option::V4, 13, payload[1]));
+ OptionPtr opt3(new Option(Option::V4, 14, payload[2]));
+ OptionPtr opt4(new Option(Option::V4,254, payload[3]));
+ OptionPtr opt5(new Option(Option::V4,128, payload[4]));
isc::dhcp::Option::OptionCollection opts; // list of options
- opts.insert(pair<int, boost::shared_ptr<Option> >(opt1->getType(), opt1));
- opts.insert(pair<int, boost::shared_ptr<Option> >(opt1->getType(), opt2));
- opts.insert(pair<int, boost::shared_ptr<Option> >(opt1->getType(), opt3));
- opts.insert(pair<int, boost::shared_ptr<Option> >(opt1->getType(), opt4));
- opts.insert(pair<int, boost::shared_ptr<Option> >(opt1->getType(), opt5));
+ opts.insert(make_pair(opt1->getType(), opt1));
+ opts.insert(make_pair(opt1->getType(), opt2));
+ opts.insert(make_pair(opt1->getType(), opt3));
+ opts.insert(make_pair(opt1->getType(), opt4));
+ opts.insert(make_pair(opt1->getType(), opt5));
vector<uint8_t> expVect(v4Opts, v4Opts + sizeof(v4Opts));
OutputBuffer buf(100);
- EXPECT_NO_THROW (
- LibDHCP::packOptions(buf, opts);
- );
+ EXPECT_NO_THROW(LibDHCP::packOptions(buf, opts));
ASSERT_EQ(buf.getLength(), sizeof(v4Opts));
EXPECT_EQ(0, memcmp(v4Opts, buf.getData(), sizeof(v4Opts)));
diff --git a/src/lib/dhcp/tests/option6_addrlst_unittest.cc b/src/lib/dhcp/tests/option6_addrlst_unittest.cc
index 60b618b..89d4f7c 100644
--- a/src/lib/dhcp/tests/option6_addrlst_unittest.cc
+++ b/src/lib/dhcp/tests/option6_addrlst_unittest.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
@@ -21,17 +21,24 @@
#include <dhcp/dhcp6.h>
#include <dhcp/option.h>
#include <dhcp/option6_addrlst.h>
+#include <util/buffer.h>
using namespace std;
using namespace isc;
using namespace isc::dhcp;
using namespace isc::asiolink;
+using namespace isc::util;
namespace {
class Option6AddrLstTest : public ::testing::Test {
public:
- Option6AddrLstTest() {
+ Option6AddrLstTest(): buf_(255), outBuf_(255) {
+ for (int i = 0; i < 255; i++) {
+ buf_[i] = 255 - i;
+ }
}
+ OptionBuffer buf_;
+ OutputBuffer outBuf_;
};
TEST_F(Option6AddrLstTest, basic) {
@@ -97,16 +104,12 @@ TEST_F(Option6AddrLstTest, basic) {
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
};
- boost::shared_array<uint8_t> buf(new uint8_t[300]);
- for (int i = 0; i < 300; i++)
- buf[i] = 0;
-
- memcpy(&buf[0], sampledata, 48);
+ memcpy(&buf_[0], sampledata, 48);
// just a single address
Option6AddrLst* opt1 = 0;
EXPECT_NO_THROW(
- opt1 = new Option6AddrLst(D6O_NAME_SERVERS, buf, 128, 0, 16);
+ opt1 = new Option6AddrLst(D6O_NAME_SERVERS, buf_.begin(), buf_.begin() + 16 );
);
EXPECT_EQ(Option::V6, opt1->getUniverse());
@@ -118,17 +121,16 @@ TEST_F(Option6AddrLstTest, basic) {
IOAddress addr = addrs[0];
EXPECT_EQ("2001:db8:1::dead:beef", addr.toText());
- // pack this option again in the same buffer, but in
- // different place
- int offset = opt1->pack(buf,300, 100);
+ // pack this option
+ opt1->pack(outBuf_);
- EXPECT_EQ(120, offset);
- EXPECT_EQ( 0, memcmp(expected1, &buf[100], 20) );
+ EXPECT_EQ(20, outBuf_.getLength());
+ EXPECT_EQ(0, memcmp(expected1, outBuf_.getData(), 20));
// two addresses
Option6AddrLst* opt2 = 0;
EXPECT_NO_THROW(
- opt2 = new Option6AddrLst(D6O_SIP_SERVERS_ADDR, buf, 128, 0, 32);
+ opt2 = new Option6AddrLst(D6O_SIP_SERVERS_ADDR, buf_.begin(), buf_.begin() + 32);
);
EXPECT_EQ(D6O_SIP_SERVERS_ADDR, opt2->getType());
EXPECT_EQ(36, opt2->len());
@@ -137,17 +139,17 @@ TEST_F(Option6AddrLstTest, basic) {
EXPECT_EQ("2001:db8:1::dead:beef", addrs[0].toText());
EXPECT_EQ("ff02::face:b00c", addrs[1].toText());
- // pack this option again in the same buffer, but in
- // different place
- offset = opt2->pack(buf,300, 150);
+ // pack this option
+ outBuf_.clear();
+ opt2->pack(outBuf_);
- EXPECT_EQ(150+36, offset);
- EXPECT_EQ( 0, memcmp(expected2, &buf[150], 36));
+ EXPECT_EQ(36, outBuf_.getLength() );
+ EXPECT_EQ(0, memcmp(expected2, outBuf_.getData(), 36));
// three addresses
Option6AddrLst* opt3 = 0;
EXPECT_NO_THROW(
- opt3 = new Option6AddrLst(D6O_NIS_SERVERS, buf, 128, 0, 48);
+ opt3 = new Option6AddrLst(D6O_NIS_SERVERS, buf_.begin(), buf_.begin() + 48);
);
EXPECT_EQ(D6O_NIS_SERVERS, opt3->getType());
@@ -158,12 +160,12 @@ TEST_F(Option6AddrLstTest, basic) {
EXPECT_EQ("ff02::face:b00c", addrs[1].toText());
EXPECT_EQ("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", addrs[2].toText());
- // pack this option again in the same buffer, but in
- // different place
- offset = opt3->pack(buf,300, 200);
+ // pack this option
+ outBuf_.clear();
+ opt3->pack(outBuf_);
- EXPECT_EQ(252, offset);
- EXPECT_EQ( 0, memcmp(expected3, &buf[200], 52) );
+ EXPECT_EQ(52, outBuf_.getLength());
+ EXPECT_EQ(0, memcmp(expected3, outBuf_.getData(), 52));
EXPECT_NO_THROW(
delete opt1;
diff --git a/src/lib/dhcp/tests/option6_ia_unittest.cc b/src/lib/dhcp/tests/option6_ia_unittest.cc
index 3fd52f5..47af50e 100644
--- a/src/lib/dhcp/tests/option6_ia_unittest.cc
+++ b/src/lib/dhcp/tests/option6_ia_unittest.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
@@ -19,53 +19,51 @@
#include <arpa/inet.h>
#include <gtest/gtest.h>
-#include <boost/shared_array.hpp>
-#include <boost/shared_ptr.hpp>
-
-#include "dhcp/dhcp6.h"
-#include "dhcp/option.h"
-#include "dhcp/option6_ia.h"
-#include "dhcp/option6_iaaddr.h"
+#include <dhcp/dhcp6.h>
+#include <dhcp/option.h>
+#include <dhcp/option6_ia.h>
+#include <dhcp/option6_iaaddr.h>
+#include <util/buffer.h>
using namespace std;
using namespace isc;
using namespace isc::dhcp;
using namespace isc::asiolink;
+using namespace isc::util;
namespace {
class Option6IATest : public ::testing::Test {
public:
- Option6IATest() {
+ Option6IATest(): buf_(255), outBuf_(255) {
+ for (int i = 0; i < 255; i++) {
+ buf_[i] = 255 - i;
+ }
}
+ OptionBuffer buf_;
+ OutputBuffer outBuf_;
};
TEST_F(Option6IATest, basic) {
+ buf_[0] = 0xa1; // iaid
+ buf_[1] = 0xa2;
+ buf_[2] = 0xa3;
+ buf_[3] = 0xa4;
- boost::shared_array<uint8_t> simple_buf(new uint8_t[128]);
- for (int i = 0; i < 128; i++)
- simple_buf[i] = 0;
- simple_buf[0] = 0xa1; // iaid
- simple_buf[1] = 0xa2;
- simple_buf[2] = 0xa3;
- simple_buf[3] = 0xa4;
-
- simple_buf[4] = 0x81; // T1
- simple_buf[5] = 0x02;
- simple_buf[6] = 0x03;
- simple_buf[7] = 0x04;
+ buf_[4] = 0x81; // T1
+ buf_[5] = 0x02;
+ buf_[6] = 0x03;
+ buf_[7] = 0x04;
- simple_buf[8] = 0x84; // T2
- simple_buf[9] = 0x03;
- simple_buf[10] = 0x02;
- simple_buf[11] = 0x01;
+ buf_[8] = 0x84; // T2
+ buf_[9] = 0x03;
+ buf_[10] = 0x02;
+ buf_[11] = 0x01;
// create an option
// unpack() is called from constructor
Option6IA* opt = new Option6IA(D6O_IA_NA,
- simple_buf,
- 128,
- 0,
- 12);
+ buf_.begin(),
+ buf_.begin() + 12);
EXPECT_EQ(Option::V6, opt->getUniverse());
EXPECT_EQ(D6O_IA_NA, opt->getType());
@@ -77,36 +75,31 @@ TEST_F(Option6IATest, basic) {
// different place
// test for pack()
- int offset = opt->pack(simple_buf, 128, 60);
+ opt->pack(outBuf_);
- // 4 bytes header + 4 bytes content
- EXPECT_EQ(12, opt->len() - 4);
+ // 12 bytes header + 4 bytes content
+ EXPECT_EQ(12, opt->len() - opt->getHeaderLen());
EXPECT_EQ(D6O_IA_NA, opt->getType());
- EXPECT_EQ(offset, 76); // 60 + lenght(IA_NA) = 76
+ EXPECT_EQ(16, outBuf_.getLength()); // lenght(IA_NA) = 16
// check if pack worked properly:
+ InputBuffer out(outBuf_.getData(), outBuf_.getLength());
+
// if option type is correct
- EXPECT_EQ(D6O_IA_NA, simple_buf[60]*256 + simple_buf[61]);
+ EXPECT_EQ(D6O_IA_NA, out.readUint16());
// if option length is correct
- EXPECT_EQ(12, simple_buf[62]*256 + simple_buf[63]);
+ EXPECT_EQ(12, out.readUint16());
// if iaid is correct
- unsigned int iaid = htonl(*(unsigned int*)&simple_buf[64]);
- EXPECT_EQ(0xa1a2a3a4, iaid );
+ EXPECT_EQ(0xa1a2a3a4, out.readUint32() );
// if T1 is correct
- EXPECT_EQ(0x81020304, (simple_buf[68] << 24) +
- (simple_buf[69] << 16) +
- (simple_buf[70] << 8) +
- (simple_buf[71]) );
+ EXPECT_EQ(0x81020304, out.readUint32() );
// if T1 is correct
- EXPECT_EQ(0x84030201, (simple_buf[72] << 24) +
- (simple_buf[73] << 16) +
- (simple_buf[74] << 8) +
- (simple_buf[75]) );
+ EXPECT_EQ(0x84030201, out.readUint32() );
EXPECT_NO_THROW(
delete opt;
@@ -114,10 +107,6 @@ TEST_F(Option6IATest, basic) {
}
TEST_F(Option6IATest, simple) {
- boost::shared_array<uint8_t> simple_buf(new uint8_t[128]);
- for (int i = 0; i < 128; i++)
- simple_buf[i] = 0;
-
Option6IA * ia = new Option6IA(D6O_IA_NA, 1234);
ia->setT1(2345);
ia->setT2(3456);
@@ -133,25 +122,21 @@ TEST_F(Option6IATest, simple) {
);
}
+
// test if option can build suboptions
TEST_F(Option6IATest, suboptions_pack) {
- boost::shared_array<uint8_t> buf(new uint8_t[128]);
- for (int i=0; i<128; i++)
- buf[i] = 0;
- buf[0] = 0xff;
- buf[1] = 0xfe;
- buf[2] = 0xfc;
+ buf_[0] = 0xff;
+ buf_[1] = 0xfe;
+ buf_[2] = 0xfc;
Option6IA * ia = new Option6IA(D6O_IA_NA, 0x13579ace);
ia->setT1(0x2345);
ia->setT2(0x3456);
- boost::shared_ptr<Option> sub1(new Option(Option::V6,
- 0xcafe));
+ OptionPtr sub1(new Option(Option::V6, 0xcafe));
boost::shared_ptr<Option6IAAddr> addr1(
- new Option6IAAddr(D6O_IAADDR, IOAddress("2001:db8:1234:5678::abcd"),
- 0x5000, 0x7000));
+ new Option6IAAddr(D6O_IAADDR, IOAddress("2001:db8:1234:5678::abcd"), 0x5000, 0x7000));
ia->addOption(sub1);
ia->addOption(addr1);
@@ -180,29 +165,29 @@ TEST_F(Option6IATest, suboptions_pack) {
0, 0 // len
};
- int offset = ia->pack(buf, 128, 10);
- ASSERT_EQ(offset, 10 + 48);
+ ia->pack(outBuf_);
+ ASSERT_EQ(48, outBuf_.getLength());
- EXPECT_EQ(0, memcmp(&buf[10], expected, 48));
+ EXPECT_EQ(0, memcmp(outBuf_.getData(), expected, 48));
EXPECT_NO_THROW(
delete ia;
);
}
+
// test if option can parse suboptions
TEST_F(Option6IATest, suboptions_unpack) {
-
-
+ // sizeof (expected) = 48 bytes
uint8_t expected[] = {
- D6O_IA_NA/256, D6O_IA_NA%256, // type
+ D6O_IA_NA / 256, D6O_IA_NA % 256, // type
0, 28, // length
0x13, 0x57, 0x9a, 0xce, // iaid
0, 0, 0x23, 0x45, // T1
0, 0, 0x34, 0x56, // T2
// iaaddr suboption
- D6O_IAADDR/256, D6O_IAADDR%256, // type
+ D6O_IAADDR / 256, D6O_IAADDR % 256, // type
0, 24, // len
0x20, 0x01, 0xd, 0xb8, 0x12,0x34, 0x56, 0x78,
0, 0, 0, 0, 0, 0, 0xab, 0xcd, // IP address
@@ -213,18 +198,13 @@ TEST_F(Option6IATest, suboptions_unpack) {
0xca, 0xfe, // type
0, 0 // len
};
+ ASSERT_EQ(48, sizeof(expected));
- boost::shared_array<uint8_t> buf(new uint8_t[128]);
- for (int i = 0; i < 128; i++)
- buf[i] = 0;
- memcpy(&buf[0], expected, 48);
+ memcpy(&buf_[0], expected, sizeof(expected));
Option6IA* ia = 0;
EXPECT_NO_THROW({
- ia = new Option6IA(D6O_IA_NA, buf, 128, 4, 44);
-
- // let's limit verbosity of this test
- // cout << "Parsed option:" << endl << ia->toText() << endl;
+ ia = new Option6IA(D6O_IA_NA, buf_.begin() + 4, buf_.begin() + sizeof(expected));
});
ASSERT_TRUE(ia);
@@ -233,8 +213,8 @@ TEST_F(Option6IATest, suboptions_unpack) {
EXPECT_EQ(0x2345, ia->getT1());
EXPECT_EQ(0x3456, ia->getT2());
- boost::shared_ptr<Option> subopt = ia->getOption(D6O_IAADDR);
- ASSERT_NE(boost::shared_ptr<Option>(), subopt); // non-NULL
+ OptionPtr subopt = ia->getOption(D6O_IAADDR);
+ ASSERT_NE(OptionPtr(), subopt); // non-NULL
// checks for address option
Option6IAAddr * addr = dynamic_cast<Option6IAAddr*>(subopt.get());
diff --git a/src/lib/dhcp/tests/option6_iaaddr_unittest.cc b/src/lib/dhcp/tests/option6_iaaddr_unittest.cc
index 81c3eb3..e351d17 100644
--- a/src/lib/dhcp/tests/option6_iaaddr_unittest.cc
+++ b/src/lib/dhcp/tests/option6_iaaddr_unittest.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
@@ -19,61 +19,62 @@
#include <arpa/inet.h>
#include <gtest/gtest.h>
-#include "dhcp/dhcp6.h"
-#include "dhcp/option.h"
-#include "dhcp/option6_iaaddr.h"
+#include <dhcp/dhcp6.h>
+#include <dhcp/option.h>
+#include <dhcp/option6_iaaddr.h>
+#include <util/buffer.h>
using namespace std;
using namespace isc;
using namespace isc::dhcp;
+using namespace isc::util;
namespace {
class Option6IAAddrTest : public ::testing::Test {
public:
- Option6IAAddrTest() {
+ Option6IAAddrTest() : buf_(255), outBuf_(255) {
+ for (int i = 0; i < 255; i++) {
+ buf_[i] = 255 - i;
+ }
}
+ OptionBuffer buf_;
+ OutputBuffer outBuf_;
};
-/// TODO reenable this once ticket #1313 is implemented.
TEST_F(Option6IAAddrTest, basic) {
-
- boost::shared_array<uint8_t> simple_buf(new uint8_t[128]);
- for (int i = 0; i < 128; i++)
- simple_buf[i] = 0;
-
- simple_buf[0] = 0x20;
- simple_buf[1] = 0x01;
- simple_buf[2] = 0x0d;
- simple_buf[3] = 0xb8;
- simple_buf[4] = 0x00;
- simple_buf[5] = 0x01;
- simple_buf[12] = 0xde;
- simple_buf[13] = 0xad;
- simple_buf[14] = 0xbe;
- simple_buf[15] = 0xef; // 2001:db8:1::dead:beef
-
- simple_buf[16] = 0x00;
- simple_buf[17] = 0x00;
- simple_buf[18] = 0x03;
- simple_buf[19] = 0xe8; // 1000
-
- simple_buf[20] = 0xb2;
- simple_buf[21] = 0xd0;
- simple_buf[22] = 0x5e;
- simple_buf[23] = 0x00; // 3,000,000,000
+ for (int i = 0; i < 255; i++) {
+ buf_[i] = 0;
+ }
+ buf_[0] = 0x20;
+ buf_[1] = 0x01;
+ buf_[2] = 0x0d;
+ buf_[3] = 0xb8;
+ buf_[4] = 0x00;
+ buf_[5] = 0x01;
+ buf_[12] = 0xde;
+ buf_[13] = 0xad;
+ buf_[14] = 0xbe;
+ buf_[15] = 0xef; // 2001:db8:1::dead:beef
+
+ buf_[16] = 0x00;
+ buf_[17] = 0x00;
+ buf_[18] = 0x03;
+ buf_[19] = 0xe8; // 1000
+
+ buf_[20] = 0xb2;
+ buf_[21] = 0xd0;
+ buf_[22] = 0x5e;
+ buf_[23] = 0x00; // 3,000,000,000
// create an option (unpack content)
Option6IAAddr* opt = new Option6IAAddr(D6O_IAADDR,
- simple_buf,
- 128,
- 0,
- 24);
+ buf_.begin(),
+ buf_.begin() + 24);
- // pack this option again in the same buffer, but in
- // different place
- int offset = opt->pack(simple_buf, 128, 50);
+ // pack this option
+ opt->pack(outBuf_);
- EXPECT_EQ(78, offset);
+ EXPECT_EQ(28, outBuf_.getLength());
EXPECT_EQ(Option::V6, opt->getUniverse());
@@ -88,18 +89,18 @@ TEST_F(Option6IAAddrTest, basic) {
opt->len());
// check if pack worked properly:
+ const uint8_t* out = (const uint8_t*)outBuf_.getData();
+
// if option type is correct
- EXPECT_EQ(D6O_IAADDR, simple_buf[50]*256 + simple_buf[51]);
+ EXPECT_EQ(D6O_IAADDR, out[0]*256 + out[1]);
// if option length is correct
- EXPECT_EQ(24, simple_buf[52]*256 + simple_buf[53]);
+ EXPECT_EQ(24, out[2]*256 + out[3]);
// if option content is correct
- EXPECT_EQ(0, memcmp(&simple_buf[0], &simple_buf[54],24));
+ EXPECT_EQ(0, memcmp(out + 4, &buf_[0], 24));
- EXPECT_NO_THROW(
- delete opt;
- );
+ EXPECT_NO_THROW( delete opt );
}
}
diff --git a/src/lib/dhcp/tests/option_unittest.cc b/src/lib/dhcp/tests/option_unittest.cc
index c83a839..5daf75d 100644
--- a/src/lib/dhcp/tests/option_unittest.cc
+++ b/src/lib/dhcp/tests/option_unittest.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
@@ -33,14 +33,13 @@ using namespace isc::util;
namespace {
class OptionTest : public ::testing::Test {
public:
- OptionTest(): outBuffer_(255) {
- buf_ = boost::shared_array<uint8_t>(new uint8_t[255]);
+ OptionTest(): buf_(255), outBuf_(255) {
for (int i = 0; i < 255; i++) {
buf_[i] = 255 - i;
}
}
- boost::shared_array<uint8_t> buf_;
- OutputBuffer outBuffer_;
+ OptionBuffer buf_;
+ OutputBuffer outBuf_;
};
// v4 is not really implemented yet. A simple test will do for now
@@ -66,10 +65,27 @@ TEST_F(OptionTest, v4_basic) {
opt = new Option(Option::V4, 256),
BadValue
);
- if (opt) {
- delete opt;
- opt = 0;
- }
+
+ delete opt;
+ opt = 0;
+
+ // 0 is a special PAD option
+ EXPECT_THROW(
+ opt = new Option(Option::V4, 0),
+ BadValue
+ );
+
+ delete opt;
+ opt = 0;
+
+ // 255 is a special END option
+ EXPECT_THROW(
+ opt = new Option(Option::V4, 255),
+ BadValue
+ );
+
+ delete opt;
+ opt = 0;
}
const uint8_t dummyPayload[] =
@@ -84,9 +100,7 @@ TEST_F(OptionTest, v4_data1) {
// create DHCPv4 option of type 123
// that contains 4 bytes of data
ASSERT_NO_THROW(
- opt= new Option(Option::V4,
- 123, // type
- data);
+ opt= new Option(Option::V4, 123, data);
);
// check that content is reported properly
@@ -143,10 +157,7 @@ TEST_F(OptionTest, v4_data2) {
// Create DHCPv4 option of type 123 that contains
// 4 bytes (sizeof(dummyPayload).
ASSERT_NO_THROW(
- opt= new Option(Option::V4,
- 123, // type
- data.begin() + 1,
- data.end() - 1);
+ opt= new Option(Option::V4, 123, data.begin() + 1, data.end() - 1);
);
// check that content is reported properly
@@ -210,30 +221,29 @@ TEST_F(OptionTest, v6_basic) {
// tests contructor used in pkt reception
// option contains actual data
TEST_F(OptionTest, v6_data1) {
- boost::shared_array<uint8_t> buf(new uint8_t[32]);
for (int i = 0; i < 32; i++)
- buf[i] = 100+i;
- Option* opt = new Option(Option::V6, 333, //type
- buf,
- 3, // offset
- 7); // 7 bytes of data
+ buf_[i] = 100+i;
+ Option* opt = new Option(Option::V6, 333, //type
+ buf_.begin() + 3, // begin offset
+ buf_.begin() + 10); // end offset (7 bytes of data)
EXPECT_EQ(333, opt->getType());
ASSERT_EQ(11, opt->len());
ASSERT_EQ(7, opt->getData().size());
- EXPECT_EQ(0, memcmp(&buf[3], &opt->getData()[0], 7) );
+ EXPECT_EQ(0, memcmp(&buf_[3], &opt->getData()[0], 7) );
- int offset = opt->pack(buf, 32, 20);
- EXPECT_EQ(31, offset);
+ opt->pack(outBuf_);
+ EXPECT_EQ(11, outBuf_.getLength());
- EXPECT_EQ(buf[20], 333/256); // type
- EXPECT_EQ(buf[21], 333%256);
+ const uint8_t* out = (const uint8_t*)outBuf_.getData();
+ EXPECT_EQ(out[0], 333/256); // type
+ EXPECT_EQ(out[1], 333%256);
- EXPECT_EQ(buf[22], 0); // len
- EXPECT_EQ(buf[23], 7);
+ EXPECT_EQ(out[2], 0); // len
+ EXPECT_EQ(out[3], 7);
// payload
- EXPECT_EQ(0, memcmp(&buf[3], &buf[24], 7) );
+ EXPECT_EQ(0, memcmp(&buf_[3], out+4, 7) );
EXPECT_NO_THROW(
delete opt;
@@ -244,40 +254,37 @@ TEST_F(OptionTest, v6_data1) {
// with different input parameters
TEST_F(OptionTest, v6_data2) {
- boost::shared_array<uint8_t> simple_buf(new uint8_t[128]);
- for (int i = 0; i < 128; i++)
- simple_buf[i] = 0;
- simple_buf[0] = 0xa1;
- simple_buf[1] = 0xa2;
- simple_buf[2] = 0xa3;
- simple_buf[3] = 0xa4;
+ buf_[0] = 0xa1;
+ buf_[1] = 0xa2;
+ buf_[2] = 0xa3;
+ buf_[3] = 0xa4;
// create an option (unpack content)
Option* opt = new Option(Option::V6,
D6O_CLIENTID,
- simple_buf,
- 0,
- 4);
+ buf_.begin(),
+ buf_.begin() + 4);
- // pack this option again in the same buffer, but in
- // different place
- int offset18 = opt->pack(simple_buf, 128, 10);
+ // pack this option
+ opt->pack(outBuf_);
// 4 bytes header + 4 bytes content
EXPECT_EQ(8, opt->len());
EXPECT_EQ(D6O_CLIENTID, opt->getType());
- EXPECT_EQ(offset18, 18);
+ EXPECT_EQ(8, outBuf_.getLength());
// check if pack worked properly:
// if option type is correct
- EXPECT_EQ(D6O_CLIENTID, simple_buf[10]*256 + simple_buf[11]);
+ const uint8_t* out = (const uint8_t*)outBuf_.getData();
+
+ EXPECT_EQ(D6O_CLIENTID, out[0]*256 + out[1]);
// if option length is correct
- EXPECT_EQ(4, simple_buf[12]*256 + simple_buf[13]);
+ EXPECT_EQ(4, out[2]*256 + out[3]);
// if option content is correct
- EXPECT_EQ(0, memcmp(&simple_buf[0], &simple_buf[14],4));
+ EXPECT_EQ(0, memcmp(&buf_[0], out + 4, 4));
EXPECT_NO_THROW(
delete opt;
@@ -291,18 +298,15 @@ TEST_F(OptionTest, v6_data2) {
// +----opt3
//
TEST_F(OptionTest, v6_suboptions1) {
- boost::shared_array<uint8_t> buf(new uint8_t[128]);
for (int i=0; i<128; i++)
- buf[i] = 100+i;
+ buf_[i] = 100+i;
Option* opt1 = new Option(Option::V6, 65535, //type
- buf,
- 0, // offset
- 3); // 3 bytes of data
- boost::shared_ptr<Option> opt2(new Option(Option::V6, 13));
- boost::shared_ptr<Option> opt3(new Option(Option::V6, 7,
- buf,
- 3, // offset
- 5)); // 5 bytes of data
+ buf_.begin(), // 3 bytes of data
+ buf_.begin() + 3);
+ OptionPtr opt2(new Option(Option::V6, 13));
+ OptionPtr opt3(new Option(Option::V6, 7,
+ buf_.begin() + 3,
+ buf_.begin() + 8)); // 5 bytes of data
opt1->addOption(opt2);
opt1->addOption(opt3);
// opt2 len = 4 (just header)
@@ -319,11 +323,11 @@ TEST_F(OptionTest, v6_suboptions1) {
0, 13, 0, 0 // no data at all
};
- int offset = opt1->pack(buf, 128, 20);
- EXPECT_EQ(40, offset);
+ opt1->pack(outBuf_);
+ EXPECT_EQ(20, outBuf_.getLength());
// payload
- EXPECT_EQ(0, memcmp(&buf[20], expected, 20) );
+ EXPECT_EQ(0, memcmp(outBuf_.getData(), expected, 20) );
EXPECT_NO_THROW(
delete opt1;
@@ -337,18 +341,15 @@ TEST_F(OptionTest, v6_suboptions1) {
// +----opt3
//
TEST_F(OptionTest, v6_suboptions2) {
- boost::shared_array<uint8_t> buf(new uint8_t[128]);
for (int i=0; i<128; i++)
- buf[i] = 100+i;
+ buf_[i] = 100+i;
Option* opt1 = new Option(Option::V6, 65535, //type
- buf,
- 0, // offset
- 3); // 3 bytes of data
- boost::shared_ptr<Option> opt2(new Option(Option::V6, 13));
- boost::shared_ptr<Option> opt3(new Option(Option::V6, 7,
- buf,
- 3, // offset
- 5)); // 5 bytes of data
+ buf_.begin(),
+ buf_.begin() + 3);
+ OptionPtr opt2(new Option(Option::V6, 13));
+ OptionPtr opt3(new Option(Option::V6, 7,
+ buf_.begin() + 3,
+ buf_.begin() + 8));
opt1->addOption(opt2);
opt2->addOption(opt3);
// opt3 len = 9 4(header)+5(data)
@@ -361,11 +362,11 @@ TEST_F(OptionTest, v6_suboptions2) {
0, 7, 0, 5, 103, 104, 105, 106, 107,
};
- int offset = opt1->pack(buf, 128, 20);
- EXPECT_EQ(40, offset);
+ opt1->pack(outBuf_);
+ EXPECT_EQ(20, outBuf_.getLength());
// payload
- EXPECT_EQ(0, memcmp(&buf[20], expected, 20) );
+ EXPECT_EQ(0, memcmp(outBuf_.getData(), expected, 20) );
EXPECT_NO_THROW(
delete opt1;
@@ -373,13 +374,12 @@ TEST_F(OptionTest, v6_suboptions2) {
}
TEST_F(OptionTest, v6_addgetdel) {
- boost::shared_array<uint8_t> buf(new uint8_t[128]);
for (int i=0; i<128; i++)
- buf[i] = 100+i;
+ buf_[i] = 100+i;
Option* parent = new Option(Option::V6, 65535); //type
- boost::shared_ptr<Option> opt1(new Option(Option::V6, 1));
- boost::shared_ptr<Option> opt2(new Option(Option::V6, 2));
- boost::shared_ptr<Option> opt3(new Option(Option::V6, 2));
+ OptionPtr opt1(new Option(Option::V6, 1));
+ OptionPtr opt2(new Option(Option::V6, 2));
+ OptionPtr opt3(new Option(Option::V6, 2));
parent->addOption(opt1);
parent->addOption(opt2);
@@ -389,7 +389,7 @@ TEST_F(OptionTest, v6_addgetdel) {
EXPECT_EQ(opt2, parent->getOption(2));
// expect NULL
- EXPECT_EQ(boost::shared_ptr<Option>(), parent->getOption(4));
+ EXPECT_EQ(OptionPtr(), parent->getOption(4));
// now there are 2 options of type 2
parent->addOption(opt3);
@@ -398,13 +398,13 @@ TEST_F(OptionTest, v6_addgetdel) {
EXPECT_EQ(true, parent->delOption(2));
// there still should be the other option 2
- EXPECT_NE(boost::shared_ptr<Option>(), parent->getOption(2));
+ EXPECT_NE(OptionPtr(), parent->getOption(2));
// let's delete the other option 2
EXPECT_EQ(true, parent->delOption(2));
// no more options with type=2
- EXPECT_EQ(boost::shared_ptr<Option>(), parent->getOption(2));
+ EXPECT_EQ(OptionPtr(), parent->getOption(2));
// let's try to delete - should fail
EXPECT_TRUE(false == parent->delOption(2));
@@ -412,36 +412,31 @@ TEST_F(OptionTest, v6_addgetdel) {
delete parent;
}
-}
-
TEST_F(OptionTest, v6_toText) {
- boost::shared_array<uint8_t> buf(new uint8_t[3]);
- buf[0] = 0;
- buf[1] = 0xf;
- buf[2] = 0xff;
+ buf_[0] = 0;
+ buf_[1] = 0xf;
+ buf_[2] = 0xff;
- boost::shared_ptr<Option> opt(new Option(Option::V6, 258,
- buf, 0, 3));
+ OptionPtr opt(new Option(Option::V6, 258, buf_.begin(), buf_.begin() + 3 ));
EXPECT_EQ("type=258, len=3: 00:0f:ff", opt->toText());
}
+
TEST_F(OptionTest, getUintX) {
- // TODO: Update this test to use buf_ instead of buf
- boost::shared_array<uint8_t> buf(new uint8_t[5]);
- buf[0] = 0x5;
- buf[1] = 0x4;
- buf[2] = 0x3;
- buf[3] = 0x2;
- buf[4] = 0x1;
+ buf_[0] = 0x5;
+ buf_[1] = 0x4;
+ buf_[2] = 0x3;
+ buf_[3] = 0x2;
+ buf_[4] = 0x1;
// five options with varying lengths
- boost::shared_ptr<Option> opt1(new Option(Option::V6, 258, buf, 0, 1));
- boost::shared_ptr<Option> opt2(new Option(Option::V6, 258, buf, 0, 2));
- boost::shared_ptr<Option> opt3(new Option(Option::V6, 258, buf, 0, 3));
- boost::shared_ptr<Option> opt4(new Option(Option::V6, 258, buf, 0, 4));
- boost::shared_ptr<Option> opt5(new Option(Option::V6, 258, buf, 0, 5));
+ OptionPtr opt1(new Option(Option::V6, 258, buf_.begin(), buf_.begin() + 1));
+ OptionPtr opt2(new Option(Option::V6, 258, buf_.begin(), buf_.begin() + 2));
+ OptionPtr opt3(new Option(Option::V6, 258, buf_.begin(), buf_.begin() + 3));
+ OptionPtr opt4(new Option(Option::V6, 258, buf_.begin(), buf_.begin() + 4));
+ OptionPtr opt5(new Option(Option::V6, 258, buf_.begin(), buf_.begin() + 5));
EXPECT_EQ(5, opt1->getUint8());
EXPECT_THROW(opt1->getUint16(), OutOfRange);
@@ -467,36 +462,37 @@ TEST_F(OptionTest, getUintX) {
}
TEST_F(OptionTest, setUintX) {
- boost::shared_ptr<Option> opt1(new Option(Option::V4, 125));
- boost::shared_ptr<Option> opt2(new Option(Option::V4, 125));
- boost::shared_ptr<Option> opt4(new Option(Option::V4, 125));
+ OptionPtr opt1(new Option(Option::V4, 125));
+ OptionPtr opt2(new Option(Option::V4, 125));
+ OptionPtr opt4(new Option(Option::V4, 125));
// verify setUint8
opt1->setUint8(255);
EXPECT_EQ(255, opt1->getUint8());
- opt1->pack4(outBuffer_);
+ opt1->pack4(outBuf_);
EXPECT_EQ(3, opt1->len());
- EXPECT_EQ(3, outBuffer_.getLength());
+ EXPECT_EQ(3, outBuf_.getLength());
uint8_t exp1[] = {125, 1, 255};
- EXPECT_TRUE(0 == memcmp(exp1, outBuffer_.getData(), 3));
+ EXPECT_TRUE(0 == memcmp(exp1, outBuf_.getData(), 3));
// verify getUint16
- outBuffer_.clear();
+ outBuf_.clear();
opt2->setUint16(12345);
- opt2->pack4(outBuffer_);
+ opt2->pack4(outBuf_);
EXPECT_EQ(12345, opt2->getUint16());
EXPECT_EQ(4, opt2->len());
- EXPECT_EQ(4, outBuffer_.getLength());
+ EXPECT_EQ(4, outBuf_.getLength());
uint8_t exp2[] = {125, 2, 12345/256, 12345%256};
- EXPECT_TRUE(0 == memcmp(exp2, outBuffer_.getData(), 4));
+ EXPECT_TRUE(0 == memcmp(exp2, outBuf_.getData(), 4));
// verity getUint32
- outBuffer_.clear();
+ outBuf_.clear();
opt4->setUint32(0x12345678);
- opt4->pack4(outBuffer_);
+ opt4->pack4(outBuf_);
EXPECT_EQ(0x12345678, opt4->getUint32());
EXPECT_EQ(6, opt4->len());
- EXPECT_EQ(6, outBuffer_.getLength());
+ EXPECT_EQ(6, outBuf_.getLength());
uint8_t exp4[] = {125, 4, 0x12, 0x34, 0x56, 0x78};
- EXPECT_TRUE(0 == memcmp(exp4, outBuffer_.getData(), 6));
+ EXPECT_TRUE(0 == memcmp(exp4, outBuf_.getData(), 6));
+}
}
diff --git a/src/lib/dhcp/tests/pkt4_unittest.cc b/src/lib/dhcp/tests/pkt4_unittest.cc
index 9936ca4..bed8c2f 100644
--- a/src/lib/dhcp/tests/pkt4_unittest.cc
+++ b/src/lib/dhcp/tests/pkt4_unittest.cc
@@ -594,6 +594,8 @@ TEST(Pkt4Test, metaFields) {
EXPECT_EQ(42, pkt->getIndex());
EXPECT_EQ("1.2.3.4", pkt->getRemoteAddr().toText());
EXPECT_EQ("4.3.2.1", pkt->getLocalAddr().toText());
+
+ delete pkt;
}
} // end of anonymous namespace
diff --git a/src/lib/dhcp/tests/pkt6_unittest.cc b/src/lib/dhcp/tests/pkt6_unittest.cc
index 968b24c..e07ea9f 100644
--- a/src/lib/dhcp/tests/pkt6_unittest.cc
+++ b/src/lib/dhcp/tests/pkt6_unittest.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
@@ -37,56 +37,67 @@ public:
};
TEST_F(Pkt6Test, constructor) {
- Pkt6 * pkt1 = new Pkt6(17);
+ uint8_t data[] = { 0, 1, 2, 3, 4, 5 };
+ Pkt6 * pkt1 = new Pkt6(data, sizeof(data) );
- EXPECT_EQ(pkt1->data_len_, 17);
+ EXPECT_EQ(6, pkt1->getData().size());
+ EXPECT_EQ(0, memcmp( &pkt1->getData()[0], data, sizeof(data)));
delete pkt1;
}
-// captured actual SOLICIT packet: transid=0x3d79fb
-// options: client-id, in_na, dns-server, elapsed-time, option-request
-// this code is autogenerated (see src/bin/dhcp6/tests/iface_mgr_unittest.c)
-Pkt6 *capture1() {
+/// @brief returns captured actual SOLICIT packet
+///
+/// Captured SOLICIT packet with transid=0x3d79fb and options: client-id,
+/// in_na, dns-server, elapsed-time, option-request
+/// This code was autogenerated (see src/bin/dhcp6/tests/iface_mgr_unittest.c),
+/// but we spent some time to make is less ugly than it used to be.
+///
+/// @return pointer to Pkt6 that represents received SOLICIT
+Pkt6* capture1() {
Pkt6* pkt;
- pkt = new Pkt6(98);
- pkt->remote_port_ = 546;
- pkt->remote_addr_ = IOAddress("fe80::21e:8cff:fe9b:7349");
- pkt->local_port_ = 0;
- pkt->local_addr_ = IOAddress("ff02::1:2");
- pkt->ifindex_ = 2;
- pkt->iface_ = "eth0";
- pkt->data_[0]=1;
- pkt->data_[1]=01; pkt->data_[2]=02; pkt->data_[3]=03; pkt->data_[4]=0;
- pkt->data_[5]=1; pkt->data_[6]=0; pkt->data_[7]=14; pkt->data_[8]=0;
- pkt->data_[9]=1; pkt->data_[10]=0; pkt->data_[11]=1; pkt->data_[12]=21;
- pkt->data_[13]=158; pkt->data_[14]=60; pkt->data_[15]=22; pkt->data_[16]=0;
- pkt->data_[17]=30; pkt->data_[18]=140; pkt->data_[19]=155; pkt->data_[20]=115;
- pkt->data_[21]=73; pkt->data_[22]=0; pkt->data_[23]=3; pkt->data_[24]=0;
- pkt->data_[25]=40; pkt->data_[26]=0; pkt->data_[27]=0; pkt->data_[28]=0;
- pkt->data_[29]=1; pkt->data_[30]=255; pkt->data_[31]=255; pkt->data_[32]=255;
- pkt->data_[33]=255; pkt->data_[34]=255; pkt->data_[35]=255; pkt->data_[36]=255;
- pkt->data_[37]=255; pkt->data_[38]=0; pkt->data_[39]=5; pkt->data_[40]=0;
- pkt->data_[41]=24; pkt->data_[42]=32; pkt->data_[43]=1; pkt->data_[44]=13;
- pkt->data_[45]=184; pkt->data_[46]=0; pkt->data_[47]=1; pkt->data_[48]=0;
- pkt->data_[49]=0; pkt->data_[50]=0; pkt->data_[51]=0; pkt->data_[52]=0;
- pkt->data_[53]=0; pkt->data_[54]=0; pkt->data_[55]=0; pkt->data_[56]=18;
- pkt->data_[57]=52; pkt->data_[58]=255; pkt->data_[59]=255; pkt->data_[60]=255;
- pkt->data_[61]=255; pkt->data_[62]=255; pkt->data_[63]=255; pkt->data_[64]=255;
- pkt->data_[65]=255; pkt->data_[66]=0; pkt->data_[67]=23; pkt->data_[68]=0;
- pkt->data_[69]=16; pkt->data_[70]=32; pkt->data_[71]=1; pkt->data_[72]=13;
- pkt->data_[73]=184; pkt->data_[74]=0; pkt->data_[75]=1; pkt->data_[76]=0;
- pkt->data_[77]=0; pkt->data_[78]=0; pkt->data_[79]=0; pkt->data_[80]=0;
- pkt->data_[81]=0; pkt->data_[82]=0; pkt->data_[83]=0; pkt->data_[84]=221;
- pkt->data_[85]=221; pkt->data_[86]=0; pkt->data_[87]=8; pkt->data_[88]=0;
- pkt->data_[89]=2; pkt->data_[90]=0; pkt->data_[91]=100; pkt->data_[92]=0;
- pkt->data_[93]=6; pkt->data_[94]=0; pkt->data_[95]=2; pkt->data_[96]=0;
- pkt->data_[97]=23;
+ uint8_t data[98];
+ data[0] = 1;
+ data[1] = 1; data[2] = 2; data[3] = 3; data[4] = 0;
+ data[5] = 1; data[6] = 0; data[7] = 14; data[8] = 0;
+ data[9] = 1; data[10] = 0; data[11] = 1; data[12] = 21;
+ data[13] = 158; data[14] = 60; data[15] = 22; data[16] = 0;
+ data[17] = 30; data[18] = 140; data[19] = 155; data[20] = 115;
+ data[21] = 73; data[22] = 0; data[23] = 3; data[24] = 0;
+ data[25] = 40; data[26] = 0; data[27] = 0; data[28] = 0;
+ data[29] = 1; data[30] = 255; data[31] = 255; data[32] = 255;
+ data[33] = 255; data[34] = 255; data[35] = 255; data[36] = 255;
+ data[37] = 255; data[38] = 0; data[39] = 5; data[40] = 0;
+ data[41] = 24; data[42] = 32; data[43] = 1; data[44] = 13;
+ data[45] = 184; data[46] = 0; data[47] = 1; data[48] = 0;
+ data[49] = 0; data[50] = 0; data[51] = 0; data[52] = 0;
+ data[53] = 0; data[54] = 0; data[55] = 0; data[56] = 18;
+ data[57] = 52; data[58] = 255; data[59] = 255; data[60] = 255;
+ data[61] = 255; data[62] = 255; data[63] = 255; data[64] = 255;
+ data[65] = 255; data[66] = 0; data[67] = 23; data[68] = 0;
+ data[69] = 16; data[70] = 32; data[71] = 1; data[72] = 13;
+ data[73] = 184; data[74] = 0; data[75] = 1; data[76] = 0;
+ data[77] = 0; data[78] = 0; data[79] = 0; data[80] = 0;
+ data[81] = 0; data[82] = 0; data[83] = 0; data[84] = 221;
+ data[85] = 221; data[86] = 0; data[87] = 8; data[88] = 0;
+ data[89] = 2; data[90] = 0; data[91] = 100; data[92] = 0;
+ data[93] = 6; data[94] = 0; data[95] = 2; data[96] = 0;
+ data[97] = 23;
+
+ pkt = new Pkt6(data, sizeof(data));
+ pkt->setRemotePort(546);
+ pkt->setRemoteAddr(IOAddress("fe80::21e:8cff:fe9b:7349"));
+ pkt->setLocalPort(0);
+ pkt->setLocalAddr(IOAddress("ff02::1:2"));
+ pkt->setIndex(2);
+ pkt->setIface("eth0");
+
return (pkt);
}
+
TEST_F(Pkt6Test, unpack_solicit1) {
- Pkt6 * sol = capture1();
+ Pkt6* sol = capture1();
ASSERT_EQ(true, sol->unpack());
@@ -108,21 +119,16 @@ TEST_F(Pkt6Test, unpack_solicit1) {
EXPECT_FALSE(sol->getOption(D6O_IA_TA));
EXPECT_FALSE(sol->getOption(D6O_IAADDR));
- // let's limit verbosity of this test
- // std::cout << sol->toText();
-
delete sol;
}
TEST_F(Pkt6Test, packUnpack) {
- Pkt6 * parent = new Pkt6(100);
+ Pkt6* parent = new Pkt6(DHCPV6_SOLICIT, 0x020304);
- parent->setType(DHCPV6_SOLICIT);
-
- boost::shared_ptr<Option> opt1(new Option(Option::V6, 1));
- boost::shared_ptr<Option> opt2(new Option(Option::V6, 2));
- boost::shared_ptr<Option> opt3(new Option(Option::V6, 100));
+ OptionPtr opt1(new Option(Option::V6, 1));
+ OptionPtr opt2(new Option(Option::V6, 2));
+ OptionPtr opt3(new Option(Option::V6, 100));
// let's not use zero-length option type 3 as it is IA_NA
parent->addOption(opt1);
@@ -130,47 +136,42 @@ TEST_F(Pkt6Test, packUnpack) {
parent->addOption(opt3);
EXPECT_EQ(DHCPV6_SOLICIT, parent->getType());
- int transid = parent->getTransid();
- // transaction-id was randomized, let's remember it
// calculated length should be 16
- EXPECT_EQ( Pkt6::DHCPV6_PKT_HDR_LEN + 3*Option::OPTION6_HDR_LEN,
- parent->len() );
-
- EXPECT_TRUE( parent->pack() );
+ EXPECT_EQ(Pkt6::DHCPV6_PKT_HDR_LEN + 3 * Option::OPTION6_HDR_LEN,
+ parent->len());
- //
- EXPECT_EQ( Pkt6::DHCPV6_PKT_HDR_LEN + 3*Option::OPTION6_HDR_LEN,
- parent->len() );
+ EXPECT_TRUE(parent->pack());
- // let's delete options from options_ collection
- // they still be defined in packed
- parent->options_.clear();
+ EXPECT_EQ(Pkt6::DHCPV6_PKT_HDR_LEN + 3 * Option::OPTION6_HDR_LEN,
+ parent->len());
- // that that removed options are indeed are gone
- EXPECT_EQ( 4, parent->len() );
+ // create second packet,based on assembled data from the first one
+ Pkt6* clone = new Pkt6(static_cast<const uint8_t*>(parent->getBuffer().getData()),
+ parent->getBuffer().getLength());
// now recreate options list
- EXPECT_TRUE( parent->unpack() );
+ EXPECT_TRUE( clone->unpack() );
// transid, message-type should be the same as before
- EXPECT_EQ(transid, parent->getTransid());
- EXPECT_EQ(DHCPV6_SOLICIT, parent->getType());
-
- EXPECT_TRUE( parent->getOption(1));
- EXPECT_TRUE( parent->getOption(2));
- EXPECT_TRUE( parent->getOption(100));
- EXPECT_FALSE( parent->getOption(4));
-
+ EXPECT_EQ(parent->getTransid(), parent->getTransid());
+ EXPECT_EQ(DHCPV6_SOLICIT, clone->getType());
+
+ EXPECT_TRUE( clone->getOption(1));
+ EXPECT_TRUE( clone->getOption(2));
+ EXPECT_TRUE( clone->getOption(100));
+ EXPECT_FALSE( clone->getOption(4));
+
delete parent;
+ delete clone;
}
TEST_F(Pkt6Test, addGetDelOptions) {
- Pkt6 * parent = new Pkt6(100);
+ Pkt6* parent = new Pkt6(DHCPV6_SOLICIT, random() );
- boost::shared_ptr<Option> opt1(new Option(Option::V6, 1));
- boost::shared_ptr<Option> opt2(new Option(Option::V6, 2));
- boost::shared_ptr<Option> opt3(new Option(Option::V6, 2));
+ OptionPtr opt1(new Option(Option::V6, 1));
+ OptionPtr opt2(new Option(Option::V6, 2));
+ OptionPtr opt3(new Option(Option::V6, 2));
parent->addOption(opt1);
parent->addOption(opt2);
@@ -180,7 +181,7 @@ TEST_F(Pkt6Test, addGetDelOptions) {
EXPECT_EQ(opt2, parent->getOption(2));
// expect NULL
- EXPECT_EQ(boost::shared_ptr<Option>(), parent->getOption(4));
+ EXPECT_EQ(OptionPtr(), parent->getOption(4));
// now there are 2 options of type 2
parent->addOption(opt3);
@@ -189,13 +190,13 @@ TEST_F(Pkt6Test, addGetDelOptions) {
EXPECT_EQ(true, parent->delOption(2));
// there still should be the other option 2
- EXPECT_NE(boost::shared_ptr<Option>(), parent->getOption(2));
+ EXPECT_NE(OptionPtr(), parent->getOption(2));
// let's delete the other option 2
EXPECT_EQ(true, parent->delOption(2));
// no more options with type=2
- EXPECT_EQ(boost::shared_ptr<Option>(), parent->getOption(2));
+ EXPECT_EQ(OptionPtr(), parent->getOption(2));
// let's try to delete - should fail
EXPECT_TRUE(false == parent->delOption(2));
@@ -203,5 +204,4 @@ TEST_F(Pkt6Test, addGetDelOptions) {
delete parent;
}
-
}
diff --git a/src/lib/dns/Makefile.am b/src/lib/dns/Makefile.am
index 39f4429..2cd889d 100644
--- a/src/lib/dns/Makefile.am
+++ b/src/lib/dns/Makefile.am
@@ -140,7 +140,7 @@ rrparamregistry.cc: rrparamregistry-placeholder.cc
rrclass.h rrtype.h rrparamregistry.cc rdataclass.h rdataclass.cc: Makefile
./gen-rdatacode.py
-libdns___includedir = $(includedir)/dns
+libdns___includedir = $(includedir)/$(PACKAGE_NAME)/dns
libdns___include_HEADERS = \
edns.h \
exceptions.h \
diff --git a/src/lib/dns/labelsequence.cc b/src/lib/dns/labelsequence.cc
index 0ec450f..71e0f82 100644
--- a/src/lib/dns/labelsequence.cc
+++ b/src/lib/dns/labelsequence.cc
@@ -18,6 +18,8 @@
#include <boost/functional/hash.hpp>
+#include <cstring>
+
namespace isc {
namespace dns {
@@ -52,7 +54,7 @@ LabelSequence::equals(const LabelSequence& other, bool case_sensitive) const {
return (false);
}
if (case_sensitive) {
- return (strncmp(data, other_data, len) == 0);
+ return (std::strncmp(data, other_data, len) == 0);
}
// As long as the data was originally validated as (part of) a name,
diff --git a/src/lib/dns/rrparamregistry-placeholder.cc b/src/lib/dns/rrparamregistry-placeholder.cc
index 62a9e34..8b01e41 100644
--- a/src/lib/dns/rrparamregistry-placeholder.cc
+++ b/src/lib/dns/rrparamregistry-placeholder.cc
@@ -401,7 +401,7 @@ textToCode(const string& code_str, MS& stringmap) {
return (code);
}
}
- isc_throw(ET, "Unrecognized RR parameter string");
+ isc_throw(ET, "Unrecognized RR parameter string: " + code_str);
}
template <typename PT, typename MC>
diff --git a/src/lib/exceptions/Makefile.am b/src/lib/exceptions/Makefile.am
index eff83b2..1d0ce2d 100644
--- a/src/lib/exceptions/Makefile.am
+++ b/src/lib/exceptions/Makefile.am
@@ -8,5 +8,5 @@ libexceptions_la_SOURCES = exceptions.h exceptions.cc
CLEANFILES = *.gcno *.gcda
-libexceptions_includedir = $(includedir)/exceptions
+libexceptions_includedir = $(includedir)/$(PACKAGE_NAME)/exceptions
libexceptions_include_HEADERS = exceptions.h
diff --git a/src/lib/exceptions/exceptions.h b/src/lib/exceptions/exceptions.h
index b68f3c4..010fd39 100644
--- a/src/lib/exceptions/exceptions.h
+++ b/src/lib/exceptions/exceptions.h
@@ -197,6 +197,38 @@ public:
throw type(__FILE__, __LINE__, oss__.str().c_str(), param1); \
} while (1)
+///
+/// Similar as isc_throw, but allows the exception to have two additional
+/// parameters (the stream/text goes first)
+#define isc_throw_2(type, stream, param1, param2) \
+ do { \
+ std::ostringstream oss__; \
+ oss__ << stream; \
+ throw type(__FILE__, __LINE__, oss__.str().c_str(), param1, param2); \
+ } while (1)
+
+///
+/// Similar as isc_throw, but allows the exception to have three additional
+/// parameters (the stream/text goes first)
+#define isc_throw_3(type, stream, param1, param2, param3) \
+ do { \
+ std::ostringstream oss__; \
+ oss__ << stream; \
+ throw type(__FILE__, __LINE__, oss__.str().c_str(), param1, param2,\
+ param3); \
+ } while (1)
+
+///
+/// Similar as isc_throw, but allows the exception to have four additional
+/// parameters (the stream/text goes first)
+#define isc_throw_4(type, stream, param1, param2, param3, param4) \
+ do { \
+ std::ostringstream oss__; \
+ oss__ << stream; \
+ throw type(__FILE__, __LINE__, oss__.str().c_str(), param1, param2,\
+ param3, param4); \
+ } while (1)
+
}
#endif // __EXCEPTIONS_H
diff --git a/src/lib/log/Makefile.am b/src/lib/log/Makefile.am
index 286e9fd..6a0251d 100644
--- a/src/lib/log/Makefile.am
+++ b/src/lib/log/Makefile.am
@@ -2,6 +2,7 @@ SUBDIRS = . compiler tests
AM_CPPFLAGS = -I$(top_builddir)/src/lib -I$(top_srcdir)/src/lib
AM_CPPFLAGS += $(BOOST_INCLUDES)
+AM_CPPFLAGS += -DLOCKFILE_DIR=\"${localstatedir}/${PACKAGE_NAME}\"
CLEANFILES = *.gcno *.gcda
@@ -47,3 +48,4 @@ liblog_la_CXXFLAGS += -Wno-error
endif
liblog_la_CPPFLAGS = $(AM_CPPFLAGS) $(LOG4CPLUS_INCLUDES)
liblog_la_LIBADD = $(LOG4CPLUS_LIBS) $(top_builddir)/src/lib/util/libutil.la
+liblog_la_LDFLAGS = -no-undefined -version-info 1:0:0
diff --git a/src/lib/log/compiler/Makefile.am b/src/lib/log/compiler/Makefile.am
index 1f47ba9..84cd4f6 100644
--- a/src/lib/log/compiler/Makefile.am
+++ b/src/lib/log/compiler/Makefile.am
@@ -16,3 +16,4 @@ noinst_PROGRAMS = message
message_SOURCES = message.cc
message_LDADD = $(top_builddir)/src/lib/log/liblog.la
message_LDADD += $(top_builddir)/src/lib/util/libutil.la
+message_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
diff --git a/src/lib/log/compiler/message.cc b/src/lib/log/compiler/message.cc
index fb0f038..66dc9c7 100644
--- a/src/lib/log/compiler/message.cc
+++ b/src/lib/log/compiler/message.cc
@@ -25,6 +25,8 @@
#include <time.h>
#include <unistd.h>
+#include <exceptions/exceptions.h>
+
#include <util/filename.h>
#include <util/strutil.h>
@@ -245,7 +247,7 @@ writeClosingNamespace(ostream& output, const vector<string>& ns) {
}
}
-/// \breif Write python file
+/// \brief Write python file
///
/// Writes the python file containing the symbol definitions as module level
/// constants. These are objects which register themself at creation time,
@@ -301,8 +303,8 @@ writePythonFile(const string& file, MessageDictionary& dictionary,
///
/// \param file Name of the message file. The header file is written to a
/// file of the same name but with a .h suffix.
-/// \param ns Namespace in which the definitions are to be placed. An empty
-/// string indicates no namespace.
+/// \param ns_components Namespace in which the definitions are to be placed.
+/// An empty string indicates no namespace.
/// \param dictionary Dictionary holding the message definitions.
/// \param output_directory if not null NULL, output files are written
/// to the given directory. If NULL, they are written to the current
@@ -325,8 +327,9 @@ writeHeaderFile(const string& file, const vector<string>& ns_components,
ofstream hfile(header_file.fullName().c_str());
if (hfile.fail()) {
- throw MessageException(LOG_OPEN_OUTPUT_FAIL, header_file.fullName(),
- strerror(errno));
+ isc_throw_4(MessageException, "Failed to open output file",
+ LOG_OPEN_OUTPUT_FAIL, header_file.fullName(),
+ strerror(errno), 0);
}
// Write the header preamble. If there is an error, we'll pick it up
@@ -359,8 +362,9 @@ writeHeaderFile(const string& file, const vector<string>& ns_components,
// Report errors (if any) and exit
if (hfile.fail()) {
- throw MessageException(LOG_WRITE_ERROR, header_file.fullName(),
- strerror(errno));
+ isc_throw_4(MessageException, "Error writing to output file",
+ LOG_WRITE_ERROR, header_file.fullName(), strerror(errno),
+ 0);
}
hfile.close();
@@ -404,8 +408,8 @@ replaceNonAlphaNum(char c) {
///
/// \param file Name of the message file. The header file is written to a
/// file of the same name but with a .h suffix.
-/// \param ns Namespace in which the definitions are to be placed. An empty
-/// string indicates no namespace.
+/// \param ns_components Namespace in which the definitions are to be placed.
+/// An empty string indicates no namespace.
/// \param dictionary Dictionary holding the message definitions.
/// \param output_directory if not null NULL, output files are written
/// to the given directory. If NULL, they are written to the current
@@ -425,8 +429,9 @@ writeProgramFile(const string& file, const vector<string>& ns_components,
ofstream ccfile(program_file.fullName().c_str());
if (ccfile.fail()) {
- throw MessageException(LOG_OPEN_OUTPUT_FAIL, program_file.fullName(),
- strerror(errno));
+ isc_throw_4(MessageException, "Error opening output file",
+ LOG_OPEN_OUTPUT_FAIL, program_file.fullName(),
+ strerror(errno), 0);
}
// Write the preamble. If there is an error, we'll pick it up after
@@ -483,8 +488,9 @@ writeProgramFile(const string& file, const vector<string>& ns_components,
// Report errors (if any) and exit
if (ccfile.fail()) {
- throw MessageException(LOG_WRITE_ERROR, program_file.fullName(),
- strerror(errno));
+ isc_throw_4(MessageException, "Error writing to output file",
+ LOG_WRITE_ERROR, program_file.fullName(), strerror(errno),
+ 0);
}
ccfile.close();
diff --git a/src/lib/log/log_formatter.cc b/src/lib/log/log_formatter.cc
index 18c4741..c728cb5 100644
--- a/src/lib/log/log_formatter.cc
+++ b/src/lib/log/log_formatter.cc
@@ -12,8 +12,11 @@
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
+#include "config.h"
#include <log/log_formatter.h>
+#include <cassert>
+
using namespace std;
using namespace boost;
@@ -24,17 +27,42 @@ void
replacePlaceholder(string* message, const string& arg,
const unsigned placeholder)
{
- string mark("%" + lexical_cast<string>(placeholder));
+ const string mark("%" + lexical_cast<string>(placeholder));
size_t pos(message->find(mark));
if (pos != string::npos) {
do {
message->replace(pos, mark.size(), arg);
pos = message->find(mark, pos + arg.size());
} while (pos != string::npos);
- } else {
+ }
+#ifdef ENABLE_LOGGER_CHECKS
+ else {
+ // We're missing the placeholder, so throw an exception
+ isc_throw(MismatchedPlaceholders,
+ "Missing logger placeholder in message: " << *message);
+ }
+#else
+ else {
// We're missing the placeholder, so add some complain
- message->append(" @@Missing placeholder " + mark + " for '" + arg +
- "'@@");
+ message->append(" @@Missing placeholder " + mark + " for '" + arg + "'@@");
+ }
+#endif /* ENABLE_LOGGER_CHECKS */
+}
+
+void
+checkExcessPlaceholders(string* message, unsigned int placeholder) {
+ const string mark("%" + lexical_cast<string>(placeholder));
+ const size_t pos(message->find(mark));
+ if (pos != string::npos) {
+ // Excess placeholders were found. If we enable the harsh check,
+ // abort it. Note: ideally we'd like to throw MismatchedPlaceholders,
+ // but we can't at least for now because this function is called from
+ // the Formatter's destructor.
+#ifdef ENABLE_LOGGER_CHECKS
+ assert("Excess logger placeholders still exist in message" == NULL);
+#else
+ message->append(" @@Excess logger placeholders still exist@@");
+#endif /* ENABLE_LOGGER_CHECKS */
}
}
diff --git a/src/lib/log/log_formatter.h b/src/lib/log/log_formatter.h
index 7a9e5fa..fc60203 100644
--- a/src/lib/log/log_formatter.h
+++ b/src/lib/log/log_formatter.h
@@ -39,6 +39,27 @@ public:
};
+/// \brief Mismatched Placeholders
+///
+/// This exception is used when the number of placeholders do not match
+/// the number of arguments passed to the formatter.
+
+class MismatchedPlaceholders : public isc::Exception {
+public:
+ MismatchedPlaceholders(const char* file, size_t line, const char* what) :
+ isc::Exception(file, line, what)
+ {}
+};
+
+
+///
+/// \brief Internal excess placeholder checker
+///
+/// This is used internally by the Formatter to check for excess
+/// placeholders (and fewer arguments).
+void
+checkExcessPlaceholders(std::string* message, unsigned int placeholder);
+
///
/// \brief The internal replacement routine
///
@@ -142,6 +163,7 @@ public:
/// This is the place where output happens if the formatter is active.
~ Formatter() {
if (logger_) {
+ checkExcessPlaceholders(message_, ++nextPlaceholder_);
logger_->output(severity_, *message_);
delete message_;
}
diff --git a/src/lib/log/logger.cc b/src/lib/log/logger.cc
index d10e979..31bcabd 100644
--- a/src/lib/log/logger.cc
+++ b/src/lib/log/logger.cc
@@ -14,6 +14,10 @@
#include <stdarg.h>
#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
#include <log/logger.h>
#include <log/logger_impl.h>
@@ -29,6 +33,30 @@ using namespace std;
namespace isc {
namespace log {
+// Constructor
+
+Logger::Logger(const char* name) : loggerptr_(NULL) {
+ assert(std::strlen(name) < sizeof(name_));
+ // Do the copy. Note that the assertion above has checked that the
+ // contents of "name" and a trailing null will fit within the space
+ // allocated for name_, so we could use strcpy here and be safe.
+ // However, a bit of extra paranoia doesn't hurt.
+ std::strncpy(name_, name, sizeof(name_));
+ assert(name_[sizeof(name_) - 1] == '\0');
+
+ lockfile_path_ = LOCKFILE_DIR;
+
+ const char *env = getenv("B10_FROM_SOURCE");
+ if (env)
+ lockfile_path_ = env;
+
+ const char *env2 = getenv("B10_FROM_SOURCE_LOCALSTATEDIR");
+ if (env2)
+ lockfile_path_ = env2;
+
+ lockfile_path_ += "/logger_lockfile";
+}
+
// Initialize underlying logger, but only if logging has been initialized.
void Logger::initLoggerImpl() {
if (isLoggingInitialized()) {
@@ -126,7 +154,51 @@ Logger::isFatalEnabled() {
void
Logger::output(const Severity& severity, const std::string& message) {
+ // Use a lock file for mutual exclusion from other processes to
+ // avoid log messages getting interspersed
+
+ int fd;
+ struct flock lock;
+ int status;
+
+ // Acquire the exclusive lock
+ fd = open(lockfile_path_.c_str(), O_CREAT | O_RDWR, 0600);
+ if (fd == -1) {
+ getLoggerPtr()->outputRaw(isc::log::FATAL, "Unable to open logger lockfile: " + lockfile_path_);
+ return;
+ }
+
+ memset(&lock, 0, sizeof lock);
+ lock.l_type = F_WRLCK;
+ lock.l_whence = SEEK_SET;
+ lock.l_start = 0;
+ lock.l_len = 1;
+ status = fcntl(fd, F_SETLKW, &lock);
+ if (status != 0) {
+ getLoggerPtr()->outputRaw(isc::log::FATAL, "Unable to lock logger lockfile: " + lockfile_path_);
+ close (fd);
+ return;
+ }
+
+ // Output the message
getLoggerPtr()->outputRaw(severity, message);
+
+ // Release the exclusive lock
+ memset(&lock, 0, sizeof lock);
+ lock.l_type = F_UNLCK;
+ lock.l_whence = SEEK_SET;
+ lock.l_start = 0;
+ lock.l_len = 1;
+ status = fcntl(fd, F_SETLKW, &lock);
+ if (status != 0) {
+ getLoggerPtr()->outputRaw(isc::log::FATAL, "Unable to unlock logger lockfile: " + lockfile_path_);
+ close (fd);
+ return;
+ }
+
+ close(fd);
+
+ // The lockfile will continue to exist, as we mustn't delete it.
}
Logger::Formatter
diff --git a/src/lib/log/logger.h b/src/lib/log/logger.h
index 5715bc4..b7362b3 100644
--- a/src/lib/log/logger.h
+++ b/src/lib/log/logger.h
@@ -133,15 +133,7 @@ public:
/// \note Note also that there is no constructor taking a std::string. This
/// minimises the possibility of initializing a static logger with a
/// string, so leading to problems mentioned above.
- Logger(const char* name) : loggerptr_(NULL) {
- assert(std::strlen(name) < sizeof(name_));
- // Do the copy. Note that the assertion above has checked that the
- // contents of "name" and a trailing null will fit within the space
- // allocated for name_, so we could use strcpy here and be safe.
- // However, a bit of extra paranoia doesn't hurt.
- std::strncpy(name_, name, sizeof(name_));
- assert(name_[sizeof(name_) - 1] == '\0');
- }
+ Logger(const char* name);
/// \brief Destructor
virtual ~Logger();
@@ -291,6 +283,7 @@ private:
LoggerImpl* loggerptr_; ///< Pointer to underlying logger
char name_[MAX_LOGGER_NAME_SIZE + 1]; ///< Copy of the logger name
+ std::string lockfile_path_;
};
} // namespace log
diff --git a/src/lib/log/message_exception.h b/src/lib/log/message_exception.h
index eebee89..cd6caf2 100644
--- a/src/lib/log/message_exception.h
+++ b/src/lib/log/message_exception.h
@@ -15,12 +15,14 @@
#ifndef __MESSAGE_EXCEPTION_H
#define __MESSAGE_EXCEPTION_H
+#include <exceptions/exceptions.h>
+#include <log/message_types.h>
+
#include <stdexcept>
#include <string>
#include <vector>
#include <boost/lexical_cast.hpp>
-#include <log/message_types.h>
namespace isc {
namespace log {
@@ -31,16 +33,18 @@ namespace log {
/// code and its arguments to be encapsulated in an exception and thrown
/// up the stack.
-class MessageException : public std::exception {
+class MessageException : public isc::Exception {
public:
/// \brief Constructor
///
/// \param id Message identification.
/// \param lineno Line number on which error occurred (if > 0).
- MessageException(MessageID id, int lineno = 0) : id_(id)
+ MessageException(const char* file, size_t line, const char* what,
+ MessageID id, int lineno)
+ : isc::Exception(file, line, what), id_(id), lineno_(lineno)
{
- if (lineno > 0) {
+ if (lineno_ > 0) {
args_.push_back(boost::lexical_cast<std::string>(lineno));
}
}
@@ -50,8 +54,9 @@ public:
/// \param id Message identification.
/// \param arg1 First message argument.
/// \param lineno Line number on which error occurred (if > 0).
- MessageException(MessageID id, const std::string& arg1, int lineno = 0)
- : id_(id)
+ MessageException(const char* file, size_t line, const char* what,
+ MessageID id, const std::string& arg1, int lineno)
+ : isc::Exception(file, line, what), id_(id)
{
if (lineno > 0) {
args_.push_back(boost::lexical_cast<std::string>(lineno));
@@ -65,8 +70,10 @@ public:
/// \param arg1 First message argument.
/// \param arg2 Second message argument.
/// \param lineno Line number on which error occurred (if > 0).
- MessageException(MessageID id, const std::string& arg1,
- const std::string& arg2, int lineno = 0) : id_(id)
+ MessageException(const char* file, size_t line, const char *what,
+ MessageID id, const std::string& arg1,
+ const std::string& arg2, int lineno)
+ : isc::Exception(file, line, what), id_(id)
{
if (lineno > 0) {
args_.push_back(boost::lexical_cast<std::string>(lineno));
@@ -95,6 +102,7 @@ public:
private:
MessageID id_; // Exception ID
std::vector<std::string> args_; // Exception arguments
+ int lineno_;
};
} // namespace log
diff --git a/src/lib/log/message_reader.cc b/src/lib/log/message_reader.cc
index 2710ab8..b5a4d35 100644
--- a/src/lib/log/message_reader.cc
+++ b/src/lib/log/message_reader.cc
@@ -48,7 +48,8 @@ MessageReader::readFile(const string& file, MessageReader::Mode mode) {
// Open the file.
ifstream infile(file.c_str());
if (infile.fail()) {
- throw MessageException(LOG_INPUT_OPEN_FAIL, file, strerror(errno));
+ isc_throw_4(MessageException, "Failed to open message file",
+ LOG_INPUT_OPEN_FAIL, file, strerror(errno), 0);
}
// Loop round reading it. As we process the file one line at a time,
@@ -65,7 +66,8 @@ MessageReader::readFile(const string& file, MessageReader::Mode mode) {
// Why did the loop terminate?
if (!infile.eof()) {
- throw MessageException(LOG_READ_ERROR, file, strerror(errno));
+ isc_throw_4(MessageException, "Error reading message file",
+ LOG_READ_ERROR, file, strerror(errno), 0);
}
infile.close();
}
@@ -114,7 +116,9 @@ MessageReader::parseDirective(const std::string& text) {
} else {
// Unrecognised directive
- throw MessageException(LOG_UNRECOGNISED_DIRECTIVE, tokens[0], lineno_);
+ isc_throw_3(MessageException, "Unrecognized directive",
+ LOG_UNRECOGNISED_DIRECTIVE, tokens[0],
+ lineno_);
}
}
@@ -138,13 +142,15 @@ MessageReader::parsePrefix(const vector<string>& tokens) {
// and numeric characters (and underscores) and does not start with a
// digit.
if (invalidSymbol(prefix_)) {
- throw MessageException(LOG_PREFIX_INVALID_ARG, prefix_, lineno_);
+ isc_throw_3(MessageException, "Invalid prefix",
+ LOG_PREFIX_INVALID_ARG, prefix_, lineno_);
}
} else {
// Too many arguments
- throw MessageException(LOG_PREFIX_EXTRA_ARGS, lineno_);
+ isc_throw_2(MessageException, "Too many arguments",
+ LOG_PREFIX_EXTRA_ARGS, lineno_);
}
}
@@ -172,10 +178,12 @@ MessageReader::parseNamespace(const vector<string>& tokens) {
// Check argument count
if (tokens.size() < 2) {
- throw MessageException(LOG_NAMESPACE_NO_ARGS, lineno_);
+ isc_throw_2(MessageException, "No arguments", LOG_NAMESPACE_NO_ARGS,
+ lineno_);
} else if (tokens.size() > 2) {
- throw MessageException(LOG_NAMESPACE_EXTRA_ARGS, lineno_);
+ isc_throw_2(MessageException, "Too many arguments",
+ LOG_NAMESPACE_EXTRA_ARGS, lineno_);
}
@@ -187,12 +195,14 @@ MessageReader::parseNamespace(const vector<string>& tokens) {
"abcdefghijklmnopqrstuvwxyz"
"0123456789_:";
if (tokens[1].find_first_not_of(valid_chars) != string::npos) {
- throw MessageException(LOG_NAMESPACE_INVALID_ARG, tokens[1], lineno_);
+ isc_throw_3(MessageException, "Invalid argument",
+ LOG_NAMESPACE_INVALID_ARG, tokens[1], lineno_);
}
// All OK - unless the namespace has already been set.
if (ns_.size() != 0) {
- throw MessageException(LOG_DUPLICATE_NAMESPACE, lineno_);
+ isc_throw_2(MessageException, "Duplicate namespace",
+ LOG_DUPLICATE_NAMESPACE, lineno_);
}
// Prefix has not been set, so set it and return success.
@@ -219,7 +229,8 @@ MessageReader::parseMessage(const std::string& text, MessageReader::Mode mode) {
// A line comprising just the message introducer is not valid.
if (text.size() == 1) {
- throw MessageException(LOG_NO_MESSAGE_ID, text, lineno_);
+ isc_throw_3(MessageException, "No message ID", LOG_NO_MESSAGE_ID,
+ text, lineno_);
}
// Strip off the introducer and any leading space after that.
@@ -230,7 +241,8 @@ MessageReader::parseMessage(const std::string& text, MessageReader::Mode mode) {
if (first_delim == string::npos) {
// Just a single token in the line - this is not valid
- throw MessageException(LOG_NO_MESSAGE_TEXT, message_line, lineno_);
+ isc_throw_3(MessageException, "No message text", LOG_NO_MESSAGE_TEXT,
+ message_line, lineno_);
}
// Extract the first token into the message ID, preceding it with the
@@ -240,7 +252,8 @@ MessageReader::parseMessage(const std::string& text, MessageReader::Mode mode) {
string ident = prefix_ + message_line.substr(0, first_delim);
if (prefix_.empty()) {
if (invalidSymbol(ident)) {
- throw MessageException(LOG_INVALID_MESSAGE_ID, ident, lineno_);
+ isc_throw_3(MessageException, "Invalid message ID",
+ LOG_INVALID_MESSAGE_ID, ident, lineno_);
}
}
isc::util::str::uppercase(ident);
@@ -252,7 +265,8 @@ MessageReader::parseMessage(const std::string& text, MessageReader::Mode mode) {
// ?? This happens if there are trailing delimiters, which should not
// occur as we have stripped trailing spaces off the line. Just treat
// this as a single-token error for simplicity's sake.
- throw MessageException(LOG_NO_MESSAGE_TEXT, message_line, lineno_);
+ isc_throw_3(MessageException, "No message text", LOG_NO_MESSAGE_TEXT,
+ message_line, lineno_);
}
// Add the result to the dictionary and to the non-added list if the add to
diff --git a/src/lib/log/message_reader.h b/src/lib/log/message_reader.h
index eded9c6..a468d43 100644
--- a/src/lib/log/message_reader.h
+++ b/src/lib/log/message_reader.h
@@ -61,7 +61,7 @@ public:
/// The ownership of the dictionary object is not transferred - the caller
/// is responsible for managing the lifetime of the dictionary.
MessageReader(MessageDictionary* dictionary = NULL) :
- dictionary_(dictionary)
+ dictionary_(dictionary), lineno_(0)
{}
/// \brief Virtual Destructor
diff --git a/src/lib/log/tests/Makefile.am b/src/lib/log/tests/Makefile.am
index c07eb4a..6f3d768 100644
--- a/src/lib/log/tests/Makefile.am
+++ b/src/lib/log/tests/Makefile.am
@@ -16,19 +16,19 @@ noinst_PROGRAMS = logger_example
logger_example_SOURCES = logger_example.cc
logger_example_CPPFLAGS = $(AM_CPPFLAGS)
logger_example_LDFLAGS = $(AM_LDFLAGS)
-logger_example_LDADD = $(AM_LDADD) $(LOG4CPLUS_LIBS)
-logger_example_LDADD += $(top_builddir)/src/lib/log/liblog.la
+logger_example_LDADD = $(top_builddir)/src/lib/log/liblog.la
logger_example_LDADD += $(top_builddir)/src/lib/util/libutil.la
logger_example_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
+logger_example_LDADD += $(AM_LDADD) $(LOG4CPLUS_LIBS)
noinst_PROGRAMS += init_logger_test
init_logger_test_SOURCES = init_logger_test.cc
init_logger_test_CPPFLAGS = $(AM_CPPFLAGS)
init_logger_test_LDFLAGS = $(AM_LDFLAGS)
-init_logger_test_LDADD = $(AM_LDADD) $(LOG4CPLUS_LIBS)
-init_logger_test_LDADD += $(top_builddir)/src/lib/log/liblog.la
+init_logger_test_LDADD = $(top_builddir)/src/lib/log/liblog.la
init_logger_test_LDADD += $(top_builddir)/src/lib/util/libutil.la
init_logger_test_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
+init_logger_test_LDADD += $(AM_LDADD) $(LOG4CPLUS_LIBS)
if HAVE_GTEST
TESTS =
@@ -41,12 +41,12 @@ endif
AM_CPPFLAGS += $(GTEST_INCLUDES) $(LOG4CPLUS_INCLUDES)
AM_LDFLAGS += $(GTEST_LDFLAGS)
-AM_LDADD += $(GTEST_LDADD)
AM_LDADD += $(top_builddir)/src/lib/log/liblog.la
AM_LDADD += $(top_builddir)/src/lib/util/unittests/libutil_unittests.la
AM_LDADD += $(top_builddir)/src/lib/util/libutil.la
AM_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
AM_LDADD += $(top_builddir)/src/lib/util/unittests/libutil_unittests.la
+AM_LDADD += $(GTEST_LDADD)
# Set of unit tests for the general logging classes
TESTS += run_unittests
diff --git a/src/lib/log/tests/log_formatter_unittest.cc b/src/lib/log/tests/log_formatter_unittest.cc
index b91665d..83fc062 100644
--- a/src/lib/log/tests/log_formatter_unittest.cc
+++ b/src/lib/log/tests/log_formatter_unittest.cc
@@ -12,7 +12,11 @@
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
+#include "config.h"
#include <gtest/gtest.h>
+
+#include <util/unittests/resource.h>
+
#include <log/log_formatter.h>
#include <log/logger_level.h>
@@ -94,16 +98,66 @@ TEST_F(FormatterTest, multiArg) {
EXPECT_EQ("The arguments are switched", outputs[0].second);
}
-// Can survive and complains if placeholder is missing
-TEST_F(FormatterTest, missingPlace) {
- EXPECT_NO_THROW(Formatter(isc::log::INFO, s("Missing the first %2"), this).
- arg("missing").arg("argument"));
+#ifdef ENABLE_LOGGER_CHECKS
+
+TEST_F(FormatterTest, mismatchedPlaceholders) {
+ // Throws MismatchedPlaceholders exception if the placeholder is missing
+ // for a supplied argument.
+ EXPECT_THROW(Formatter(isc::log::INFO, s("Missing the second %1"), this).
+ arg("argument").arg("missing"),
+ isc::log::MismatchedPlaceholders);
+
+#ifdef EXPECT_DEATH
+ // Likewise, if there's a redundant placeholder (or missing argument), the
+ // check detects it and aborts the program. Due to the restriction of the
+ // current implementation, it doesn't throw.
+ EXPECT_DEATH({
+ isc::util::unittests::dontCreateCoreDumps();
+ Formatter(isc::log::INFO, s("Too many arguments in %1 %2"), this).
+ arg("only one");
+ }, ".*");
+
+ // Mixed case of above two: the exception will be thrown due to the missing
+ // placeholder, but before even it's caught the program will be aborted
+ // due to the unused placeholder as a result of the exception.
+ EXPECT_DEATH({
+ isc::util::unittests::dontCreateCoreDumps();
+ Formatter(isc::log::INFO, s("Missing the first %2"), this).
+ arg("missing").arg("argument");
+ }, ".*");
+#endif /* EXPECT_DEATH */
+}
+
+#else
+
+// If logger checks are not enabled, nothing is thrown
+TEST_F(FormatterTest, mismatchedPlaceholders) {
+ Formatter(isc::log::INFO, s("Missing the second %1"), this).
+ arg("argument").arg("missing");
ASSERT_EQ(1, outputs.size());
EXPECT_EQ(isc::log::INFO, outputs[0].first);
+ EXPECT_EQ("Missing the second argument "
+ "@@Missing placeholder %2 for 'missing'@@", outputs[0].second);
+
+ EXPECT_NO_THROW(Formatter(isc::log::INFO,
+ s("Too many arguments in %1 %2"), this).
+ arg("only one"));
+ ASSERT_EQ(2, outputs.size());
+ EXPECT_EQ(isc::log::INFO, outputs[1].first);
+ EXPECT_EQ("Too many arguments in only one %2 "
+ "@@Excess logger placeholders still exist@@",
+ outputs[1].second);
+
+ EXPECT_NO_THROW(Formatter(isc::log::INFO, s("Missing the first %2"), this).
+ arg("missing").arg("argument"));
+ ASSERT_EQ(3, outputs.size());
+ EXPECT_EQ(isc::log::INFO, outputs[2].first);
EXPECT_EQ("Missing the first argument "
- "@@Missing placeholder %1 for 'missing'@@", outputs[0].second);
+ "@@Missing placeholder %1 for 'missing'@@", outputs[2].second);
}
+#endif /* ENABLE_LOGGER_CHECKS */
+
// Can replace multiple placeholders
TEST_F(FormatterTest, multiPlaceholder) {
Formatter(isc::log::INFO, s("The %1 is the %1"), this).
diff --git a/src/lib/log/tests/logger_manager_unittest.cc b/src/lib/log/tests/logger_manager_unittest.cc
index f2713b0..584d0f5 100644
--- a/src/lib/log/tests/logger_manager_unittest.cc
+++ b/src/lib/log/tests/logger_manager_unittest.cc
@@ -296,7 +296,7 @@ TEST_F(LoggerManagerTest, FileSizeRollover) {
manager.process(spec);
{
Logger logger(file_spec.getLoggerName().c_str());
- LOG_FATAL(logger, LOG_NO_MESSAGE_TEXT).arg(big_arg);
+ LOG_FATAL(logger, LOG_NO_MESSAGE_TEXT).arg("42").arg(big_arg);
}
LoggerManager::reset(); // Ensure files are closed
diff --git a/src/lib/log/tests/logger_support_unittest.cc b/src/lib/log/tests/logger_support_unittest.cc
index b418906..c626c6d 100644
--- a/src/lib/log/tests/logger_support_unittest.cc
+++ b/src/lib/log/tests/logger_support_unittest.cc
@@ -79,5 +79,5 @@ TEST_F(LoggerSupportTest, LoggingInitializationCheck) {
// ... and check that they work when logging is initialized.
setLoggingInitialized(true);
EXPECT_NO_THROW(test_logger.isDebugEnabled());
- EXPECT_NO_THROW(test_logger.info(LOG_INPUT_OPEN_FAIL));
+ EXPECT_NO_THROW(test_logger.info(LOG_INPUT_OPEN_FAIL).arg("foo").arg("bar"));
}
diff --git a/src/lib/log/tests/logger_unittest.cc b/src/lib/log/tests/logger_unittest.cc
index d60bcb0..069205e 100644
--- a/src/lib/log/tests/logger_unittest.cc
+++ b/src/lib/log/tests/logger_unittest.cc
@@ -17,6 +17,8 @@
#include <gtest/gtest.h>
+#include <util/unittests/resource.h>
+
#include <log/logger.h>
#include <log/logger_manager.h>
#include <log/logger_name.h>
@@ -370,8 +372,10 @@ TEST_F(LoggerTest, LoggerNameLength) {
// Note that we just check that it dies - we don't check what message is
// output.
EXPECT_DEATH({
- string ok3(Logger::MAX_LOGGER_NAME_SIZE + 1, 'x');
- Logger l3(ok3.c_str());
- }, ".*");
+ isc::util::unittests::dontCreateCoreDumps();
+
+ string ok3(Logger::MAX_LOGGER_NAME_SIZE + 1, 'x');
+ Logger l3(ok3.c_str());
+ }, ".*");
#endif
}
diff --git a/src/lib/log/tests/message_initializer_2_unittest.cc b/src/lib/log/tests/message_initializer_2_unittest.cc
index ca34b36..b479eee 100644
--- a/src/lib/log/tests/message_initializer_2_unittest.cc
+++ b/src/lib/log/tests/message_initializer_2_unittest.cc
@@ -15,6 +15,8 @@
#include <log/message_initializer.h>
#include <gtest/gtest.h>
+#include <util/unittests/resource.h>
+
using namespace isc::log;
// Declare a set of messages to go into the global dictionary.
@@ -42,7 +44,9 @@ TEST(MessageInitializerTest2, MessageLoadTest) {
#ifdef EXPECT_DEATH
// Adding one more should take us over the limit.
EXPECT_DEATH({
+ isc::util::unittests::dontCreateCoreDumps();
+
MessageInitializer initializer2(values);
- }, ".*");
+ }, ".*");
#endif
}
diff --git a/src/lib/nsas/nsas_messages.mes b/src/lib/nsas/nsas_messages.mes
index 6c35172..0b19a9e 100644
--- a/src/lib/nsas/nsas_messages.mes
+++ b/src/lib/nsas/nsas_messages.mes
@@ -54,8 +54,8 @@ This message indicates an internal error in the NSAS. Please raise a
bug report.
% NSAS_SEARCH_ZONE_NS searching NSAS for nameservers for zone %1
-A debug message output when a call is made to the NSAS (nameserver
-address store - part of the resolver) to obtain the nameservers for
+A debug message output when a call is made to the NSAS (nameserver
+address store - part of the resolver) to obtain the nameservers for
the specified zone.
% NSAS_UPDATE_RTT update RTT for %1: was %2 ms, is now %3 ms
@@ -72,5 +72,5 @@ A NSAS (nameserver address store - part of the resolver) made a query for
a resource record of a particular type and class, but instead received
an answer with a different given type and class.
-This message indicates an internal error in the NSAS. Please raise a
+This message indicates an internal error in the NSAS. Please raise a
bug report.
diff --git a/src/lib/python/isc/config/cfgmgr.py b/src/lib/python/isc/config/cfgmgr.py
index 9f9ce68..23e627e 100644
--- a/src/lib/python/isc/config/cfgmgr.py
+++ b/src/lib/python/isc/config/cfgmgr.py
@@ -81,6 +81,7 @@ class ConfigManagerData:
and stop loading the system.
"""
config = ConfigManagerData(data_path, file_name)
+ logger.info(CFGMGR_CONFIG_FILE, config.db_filename)
file = None
try:
file = open(config.db_filename, 'r')
diff --git a/src/lib/python/isc/config/cfgmgr_messages.mes b/src/lib/python/isc/config/cfgmgr_messages.mes
index ad78be0..e608594 100644
--- a/src/lib/python/isc/config/cfgmgr_messages.mes
+++ b/src/lib/python/isc/config/cfgmgr_messages.mes
@@ -31,6 +31,10 @@ assumed to have failed, and will not be stored.
The configuration manager daemon was unable to connect to the messaging
system. The most likely cause is that msgq is not running.
+% CFGMGR_CONFIG_FILE Configuration manager starting with configuration file: %1
+The configuration manager is starting, reading and saving the configuration
+settings to the shown file.
+
% CFGMGR_DATA_READ_ERROR error reading configuration database from disk: %1
There was a problem reading the persistent configuration data as stored
on disk. The file may be corrupted, or it is of a version from where
diff --git a/src/lib/python/isc/config/config_data.py b/src/lib/python/isc/config/config_data.py
index 346585b..2bec4ab 100644
--- a/src/lib/python/isc/config/config_data.py
+++ b/src/lib/python/isc/config/config_data.py
@@ -23,6 +23,7 @@ two through the classes in ccsession)
import isc.cc.data
import isc.config.module_spec
import ast
+import copy
class ConfigDataError(Exception): pass
@@ -210,7 +211,8 @@ def find_spec_part(element, identifier, strict_identifier = True):
cur_el = _get_map_or_list(cur_el)
cur_el = _find_spec_part_single(cur_el, id_parts[-1])
- return cur_el
+ # Due to the raw datatypes we use, it is safer to return a deep copy here
+ return copy.deepcopy(cur_el)
def spec_name_list(spec, prefix="", recurse=False):
"""Returns a full list of all possible item identifiers in the
@@ -418,6 +420,14 @@ class MultiConfigData:
manager or the modules."""
return self._local_changes
+ def set_local_changes(self, new_local_changes):
+ """Sets the entire set of local changes, used when reverting
+ changes done automatically in case there was a problem (e.g.
+ when executing commands from a script that fails halfway
+ through).
+ """
+ self._local_changes = new_local_changes
+
def clear_local_changes(self):
"""Reverts all local changes"""
self._local_changes = {}
@@ -579,8 +589,10 @@ class MultiConfigData:
if item_type == "list" and (all or first):
spec_part_list = spec_part['list_item_spec']
list_value, status = self.get_value(identifier)
+ # If not set, and no default, lists will show up as 'None',
+ # but it's better to treat it as an empty list then
if list_value is None:
- raise isc.cc.data.DataNotFoundError(identifier + " not found")
+ list_value = []
if type(list_value) != list:
# the identifier specified a single element
diff --git a/src/lib/python/isc/config/config_messages.mes b/src/lib/python/isc/config/config_messages.mes
index 9e93ca3..1fcf597 100644
--- a/src/lib/python/isc/config/config_messages.mes
+++ b/src/lib/python/isc/config/config_messages.mes
@@ -21,16 +21,16 @@
# 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
-% CONFIG_LOG_CONFIG_ERRORS error(s) in logging configuration: %1
-There was a logging configuration update, but the internal validator
-for logging configuration found that it contained errors. The errors
-are shown, and the update is ignored.
-
% CONFIG_GET_FAILED error getting configuration from cfgmgr: %1
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_LOG_CONFIG_ERRORS error(s) in logging configuration: %1
+There was a logging configuration update, but the internal validator
+for logging configuration found that it contained errors. The errors
+are shown, and the update is ignored.
+
% 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
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 864fe70..221ffa6 100644
--- a/src/lib/python/isc/config/tests/config_data_test.py
+++ b/src/lib/python/isc/config/tests/config_data_test.py
@@ -186,6 +186,10 @@ 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)
+ # make sure the returned data is a copy
+ spec_part['item_default'] = 'foo'
+ self.assertNotEqual(spec_part, find_spec_part(config_spec, "item6/value1"))
+
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 +
@@ -420,7 +424,14 @@ class TestMultiConfigData(unittest.TestCase):
self.mcd.set_value("Spec2/item1", 2)
local_changes = self.mcd.get_local_changes()
self.assertEqual({"Spec2": { "item1": 2}}, local_changes)
-
+
+ def test_set_local_changes(self):
+ module_spec = isc.config.module_spec_from_file(self.data_path + os.sep + "spec2.spec")
+ self.mcd.set_specification(module_spec)
+ self.assertEqual({}, self.mcd.get_local_changes())
+ new_local_changes = {"Spec2": { "item1": 2}}
+ self.mcd.set_local_changes(new_local_changes)
+ self.assertEqual(new_local_changes, self.mcd.get_local_changes())
def test_clear_local_changes(self):
module_spec = isc.config.module_spec_from_file(self.data_path + os.sep + "spec2.spec")
@@ -585,8 +596,10 @@ class TestMultiConfigData(unittest.TestCase):
module_spec = isc.config.module_spec_from_file(self.data_path + os.sep + "spec24.spec")
self.mcd.set_specification(module_spec)
- self.assertRaises(isc.cc.data.DataNotFoundError,
- self.mcd.get_value_maps, "/Spec24/item", 4)
+ # optional list item that is not set should return as empty list
+ maps = self.mcd.get_value_maps("/Spec24/item", 4)
+ self.assertEqual([{'default': False, 'type': 'list', 'name': 'Spec24/item', 'value': [], 'modified': False}], maps)
+
self.mcd._set_current_config({ "Spec24": { "item": [] } })
maps = self.mcd.get_value_maps("/Spec24/item")
self.assertEqual([{'default': False, 'modified': False, 'name': 'Spec24/item', 'type': 'list', 'value': []}], maps)
diff --git a/src/lib/python/isc/notify/notify_out_messages.mes b/src/lib/python/isc/notify/notify_out_messages.mes
index b77a60c..3bc0f38 100644
--- a/src/lib/python/isc/notify/notify_out_messages.mes
+++ b/src/lib/python/isc/notify/notify_out_messages.mes
@@ -15,6 +15,18 @@
# No namespace declaration - these constants go in the global namespace
# of the notify_out_messages python module.
+% NOTIFY_OUT_DATASRC_ACCESS_FAILURE failed to get access to data source: %1
+notify_out failed to get access to one of configured data sources.
+Detailed error is shown in the log message. This can be either a
+configuration error or installation setup failure.
+
+% NOTIFY_OUT_DATASRC_ZONE_NOT_FOUND Zone %1 is not found
+notify_out attempted to get slave information of a zone but the zone
+isn't found in the expected data source. This shouldn't happen,
+because notify_out first identifies a list of available zones before
+this process. So this means some critical inconsistency in the data
+source or software bug.
+
% NOTIFY_OUT_INVALID_ADDRESS invalid address %1#%2: %3
The notify_out library tried to send a notify message to the given
address, but it appears to be an invalid address. The configuration
@@ -48,6 +60,16 @@ given address, but the reply did not have the QR bit set to one.
Since there was a response, no more notifies will be sent to this
server for this notification event.
+% NOTIFY_OUT_REPLY_UNCAUGHT_EXCEPTION uncaught exception: %1
+There was an uncaught exception in the handling of a notify reply
+message, either in the message parser, or while trying to extract data
+from the parsed message. The error is printed, and notify_out will
+treat the response as a bad message, but this does point to a
+programming error, since all exceptions should have been caught
+explicitly. Please file a bug report. Since there was a response,
+no more notifies will be sent to this server for this notification
+event.
+
% NOTIFY_OUT_RETRY_EXCEEDED notify to %1#%2: number of retries (%3) exceeded
The maximum number of retries for the notify target has been exceeded.
Either the address of the secondary nameserver is wrong, or it is not
@@ -72,33 +94,11 @@ The notify message to the given address (noted as address#port) has
timed out, and the message will be resent until the max retry limit
is reached.
-% NOTIFY_OUT_REPLY_UNCAUGHT_EXCEPTION uncaught exception: %1
-There was an uncaught exception in the handling of a notify reply
-message, either in the message parser, or while trying to extract data
-from the parsed message. The error is printed, and notify_out will
-treat the response as a bad message, but this does point to a
-programming error, since all exceptions should have been caught
-explicitly. Please file a bug report. Since there was a response,
-no more notifies will be sent to this server for this notification
-event.
-
-% NOTIFY_OUT_DATASRC_ACCESS_FAILURE failed to get access to data source: %1
-notify_out failed to get access to one of configured data sources.
-Detailed error is shown in the log message. This can be either a
-configuration error or installation setup failure.
-
-% NOTIFY_OUT_DATASRC_ZONE_NOT_FOUND Zone %1 is not found
-notify_out attempted to get slave information of a zone but the zone
-isn't found in the expected data source. This shouldn't happen,
-because notify_out first identifies a list of available zones before
-this process. So this means some critical inconsistency in the data
-source or software bug.
-
-% NOTIFY_OUT_ZONE_NO_NS Zone %1 doesn't have NS RR
-This is a warning issued when the notify_out module finds a zone that
-doesn't have an NS RR. Notify message won't be sent to such a zone.
-
% NOTIFY_OUT_ZONE_BAD_SOA Zone %1 is invalid in terms of SOA
This is a warning issued when the notify_out module finds a zone that
doesn't have an SOA RR or has multiple SOA RRs. Notify message won't
be sent to such a zone.
+
+% NOTIFY_OUT_ZONE_NO_NS Zone %1 doesn't have NS RR
+This is a warning issued when the notify_out module finds a zone that
+doesn't have an NS RR. Notify message won't be sent to such a zone.
diff --git a/src/lib/resolve/resolve_messages.mes b/src/lib/resolve/resolve_messages.mes
index b59fd8c..1df544a 100644
--- a/src/lib/resolve/resolve_messages.mes
+++ b/src/lib/resolve/resolve_messages.mes
@@ -84,11 +84,11 @@ the specified name contained multiple RRsets in the answer and not all
were of the same class. This is a violation of the standard and so a
SERVFAIL will be returned.
-% RESLIB_NO_NS_RRSET no NS RRSet in referral response received to query for <%1>
-A debug message, this indicates that a response was received for the specified
-query and was categorized as a referral. However, the received message did
-not contain any NS RRsets. This may indicate a programming error in the
-response classification code.
+% RESLIB_NOTSINGLE_RESPONSE response to query for <%1> was not a response
+A debug message, the response to the specified query from an upstream
+nameserver was a CNAME that had mutiple RRs in the RRset. This is
+an invalid response according to the standards so a SERVFAIL will be
+returned to the system making the original query.
% RESLIB_NOT_ONE_QNAME_RESPONSE not one question in response to query for <%1>
A debug message, the response to the specified query from an upstream
@@ -102,16 +102,21 @@ nameserver (as identified by the ID of the response) did not have the QR
bit set (thus indicating that the packet was a query, not a response).
A SERVFAIL will be returned to the system making the original query.
-% RESLIB_NOTSINGLE_RESPONSE response to query for <%1> was not a response
-A debug message, the response to the specified query from an upstream
-nameserver was a CNAME that had mutiple RRs in the RRset. This is
-an invalid response according to the standards so a SERVFAIL will be
-returned to the system making the original query.
+% RESLIB_NO_NS_RRSET no NS RRSet in referral response received to query for <%1>
+A debug message, this indicates that a response was received for the specified
+query and was categorized as a referral. However, the received message did
+not contain any NS RRsets. This may indicate a programming error in the
+response classification code.
% RESLIB_NSAS_LOOKUP looking up nameserver for zone %1 in the NSAS
A debug message, the RunningQuery object is querying the NSAS for the
nameservers for the specified zone.
+% RESLIB_NXDOM_NXRR NXDOMAIN/NXRRSET received in response to query for <%1>
+A debug message recording that either a NXDOMAIN or an NXRRSET response has
+been received to an upstream query for the specified question. Previous debug
+messages will have indicated the server to which the question was sent.
+
% RESLIB_OPCODE_RESPONSE response to query for <%1> did not have query opcode
A debug message, the response to the specified query from an upstream
nameserver was a response that did not have the opcode set to that of
@@ -119,11 +124,6 @@ a query. According to the standards, this is an invalid response to
the query that was made, so a SERVFAIL will be returned to the system
making the original query.
-% RESLIB_NXDOM_NXRR NXDOMAIN/NXRRSET received in response to query for <%1>
-A debug message recording that either a NXDOMAIN or an NXRRSET response has
-been received to an upstream query for the specified question. Previous debug
-messages will have indicated the server to which the question was sent.
-
% RESLIB_PROTOCOL protocol error in answer for %1: %3
A debug message indicating that a protocol error was received. As there
are no retries left, an error will be reported.
diff --git a/src/lib/server_common/server_common_messages.mes b/src/lib/server_common/server_common_messages.mes
index 0817928..0e4efa5 100644
--- a/src/lib/server_common/server_common_messages.mes
+++ b/src/lib/server_common/server_common_messages.mes
@@ -89,11 +89,6 @@ 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).
-% SRVCOMM_UNKNOWN_EXCEPTION_ALLOC unknown exception when allocating a socket
-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.
-
% SRVCOMM_KEYS_DEINIT deinitializing TSIG keyring
Debug message indicating that the server is deinitializing the TSIG keyring.
@@ -112,3 +107,8 @@ specification is outside the valid range of 0 to 65535.
% SRVCOMM_SET_LISTEN setting addresses to listen to
Debug message, noting that the server is about to start listening on a
different set of IP addresses and ports than before.
+
+% SRVCOMM_UNKNOWN_EXCEPTION_ALLOC unknown exception when allocating a socket
+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.
diff --git a/src/lib/server_common/tests/portconfig_unittest.cc b/src/lib/server_common/tests/portconfig_unittest.cc
index 6563815..ac880c0 100644
--- a/src/lib/server_common/tests/portconfig_unittest.cc
+++ b/src/lib/server_common/tests/portconfig_unittest.cc
@@ -12,16 +12,19 @@
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
+#include <gtest/gtest.h>
+
#include <server_common/portconfig.h>
#include <testutils/socket_request.h>
#include <testutils/mockups.h>
+#include <util/unittests/resource.h>
+
#include <cc/data.h>
#include <exceptions/exceptions.h>
#include <asiolink/asiolink.h>
#include <asiodns/asiodns.h>
-#include <gtest/gtest.h>
#include <string>
using namespace isc::server_common::portconfig;
@@ -313,13 +316,15 @@ TEST_F(InstallListenAddressesDeathTest, inconsistent) {
// Make sure it actually kills the application (there should be an abort
// in this case)
EXPECT_DEATH({
- try {
- installListenAddresses(deathAddresses, store_, dnss_);
- } catch (...) {
- // Prevent exceptions killing the application, we need
- // to make sure it dies the real hard way
- };
- }, "");
+ isc::util::unittests::dontCreateCoreDumps();
+
+ try {
+ installListenAddresses(deathAddresses, store_, dnss_);
+ } catch (...) {
+ // Prevent exceptions killing the application, we need
+ // to make sure it dies the real hard way
+ };
+ }, "");
}
// If we are unable to tell the boss we closed a socket, we abort, as we are
@@ -330,16 +335,18 @@ TEST_F(InstallListenAddressesDeathTest, cantClose) {
// Instruct it to fail on close
sock_requestor_.break_release_ = true;
EXPECT_DEATH({
- try {
- // Setting to empty will close all current sockets.
- // And thanks to the break_release_, the close will
- // throw, which will make it crash.
- installListenAddresses(empty, store_, dnss_);
- } catch (...) {
- // To make sure it is killed by abort, not by some
- // (unhandled) exception
- };
- }, "");
+ isc::util::unittests::dontCreateCoreDumps();
+
+ try {
+ // Setting to empty will close all current sockets.
+ // And thanks to the break_release_, the close will
+ // throw, which will make it crash.
+ installListenAddresses(empty, store_, dnss_);
+ } catch (...) {
+ // To make sure it is killed by abort, not by some
+ // (unhandled) exception
+ };
+ }, "");
// And reset it back, so it can safely clean up itself.
sock_requestor_.break_release_ = false;
}
diff --git a/src/lib/testutils/dnsmessage_test.cc b/src/lib/testutils/dnsmessage_test.cc
index 3d653c0..ec6914d 100644
--- a/src/lib/testutils/dnsmessage_test.cc
+++ b/src/lib/testutils/dnsmessage_test.cc
@@ -23,6 +23,12 @@
#include <testutils/dnsmessage_test.h>
+#include <boost/bind.hpp>
+
+#include <string>
+#include <sstream>
+
+using namespace std;
using namespace isc::dns;
namespace isc {
@@ -80,6 +86,26 @@ matchRdata(const char*, const char*,
}
return (::testing::AssertionSuccess());
}
+
+// A helper callback of masterLoad() used by textToRRset() below.
+void
+setRRset(RRsetPtr rrset, RRsetPtr* rrsetp) {
+ if (*rrsetp) {
+ isc_throw(isc::Unexpected,
+ "multiple RRsets are given to textToRRset");
+ }
+ *rrsetp = rrset;
+}
+}
+
+RRsetPtr
+textToRRset(const string& text_rrset, const RRClass& rrclass,
+ const Name& origin)
+{
+ stringstream ss(text_rrset);
+ RRsetPtr rrset;
+ masterLoad(ss, origin, rrclass, boost::bind(setRRset, _1, &rrset));
+ return (rrset);
}
void
diff --git a/src/lib/testutils/dnsmessage_test.h b/src/lib/testutils/dnsmessage_test.h
index 5c74011..57cb72c 100644
--- a/src/lib/testutils/dnsmessage_test.h
+++ b/src/lib/testutils/dnsmessage_test.h
@@ -174,6 +174,29 @@ private:
};
}
+/// \brief A converter from a string to RRset.
+///
+/// This is a convenient shortcut for tests that need to create an RRset
+/// from textual representation with a single call to a function.
+///
+/// An RRset consisting of multiple RRs can be constructed, but only one
+/// RRset is allowed. If the given string contains mixed types of RRs
+/// it throws an \c isc::Unexpected exception.
+///
+/// \param text_rrset A complete textual representation of an RRset.
+/// It must meets the assumption of the \c dns::masterLoad() function.
+/// \param rrclass The RR class of the RRset. Note that \c text_rrset should
+/// contain the RR class, but it's needed for \c dns::masterLoad().
+/// \param origin The zone origin where the RR is expected to belong. This
+/// parameter normally doesn't have to be specified, but for an SOA RR it
+/// must be set to its owner name, due to the internal check of
+/// \c dns::masterLoad().
+isc::dns::RRsetPtr textToRRset(const std::string& text_rrset,
+ const isc::dns::RRClass& rrclass =
+ isc::dns::RRClass::IN(),
+ const isc::dns::Name& origin =
+ isc::dns::Name::ROOT_NAME());
+
/// Set of unit tests to check if two sets of RRsets are identical.
///
/// This templated function takes two sets of sequences, each defined by
diff --git a/src/lib/testutils/testdata/.gitignore b/src/lib/testutils/testdata/.gitignore
index 1f61066..4d41a44 100644
--- a/src/lib/testutils/testdata/.gitignore
+++ b/src/lib/testutils/testdata/.gitignore
@@ -1,3 +1,4 @@
+/auth_test.sqlite3.copied
/badExampleQuery_fromWire.wire
/examplequery_fromWire.wire
/iquery_fromWire.wire
diff --git a/src/lib/testutils/testdata/auth_test.sqlite3.copied b/src/lib/testutils/testdata/auth_test.sqlite3.copied
deleted file mode 100755
index 205e4ef..0000000
Binary files a/src/lib/testutils/testdata/auth_test.sqlite3.copied and /dev/null differ
diff --git a/src/lib/util/Makefile.am b/src/lib/util/Makefile.am
index 0b78b29..c2b3020 100644
--- a/src/lib/util/Makefile.am
+++ b/src/lib/util/Makefile.am
@@ -12,6 +12,7 @@ libutil_la_SOURCES += locks.h lru_list.h
libutil_la_SOURCES += strutil.h strutil.cc
libutil_la_SOURCES += buffer.h io_utilities.h
libutil_la_SOURCES += time_utilities.h time_utilities.cc
+libutil_la_SOURCES += range_utilities.h
libutil_la_SOURCES += hash/sha1.h hash/sha1.cc
libutil_la_SOURCES += encode/base16_from_binary.h
libutil_la_SOURCES += encode/base32hex.h encode/base64.h
diff --git a/src/lib/util/buffer.h b/src/lib/util/buffer.h
index b8cc12d..1263636 100644
--- a/src/lib/util/buffer.h
+++ b/src/lib/util/buffer.h
@@ -19,8 +19,6 @@
#include <cstring>
#include <vector>
-#include <string.h>
-
#include <stdint.h>
#include <exceptions/exceptions.h>
@@ -198,7 +196,7 @@ public:
throwError("read beyond end of buffer");
}
- memcpy(data, &data_[position_], len);
+ std::memcpy(data, &data_[position_], len);
position_ += len;
}
//@}
@@ -333,7 +331,7 @@ public:
if (buffer_ == NULL && allocated_ != 0) {
throw std::bad_alloc();
}
- memcpy(buffer_, other.buffer_, size_);
+ std::memcpy(buffer_, other.buffer_, size_);
}
/// \brief Destructor
@@ -352,7 +350,7 @@ public:
buffer_ = newbuff;
size_ = other.size_;
allocated_ = other.allocated_;
- memcpy(buffer_, other.buffer_, size_);
+ std::memcpy(buffer_, other.buffer_, size_);
return (*this);
}
@@ -493,7 +491,7 @@ public:
void writeData(const void *data, size_t len)
{
ensureAllocated(size_ + len);
- memcpy(buffer_ + size_, data, len);
+ std::memcpy(buffer_ + size_, data, len);
size_ += len;
}
//@}
diff --git a/src/lib/util/io/Makefile.am b/src/lib/util/io/Makefile.am
index 2c3ed96..1cb0ab9 100644
--- a/src/lib/util/io/Makefile.am
+++ b/src/lib/util/io/Makefile.am
@@ -6,6 +6,7 @@ AM_CPPFLAGS += $(BOOST_INCLUDES)
lib_LTLIBRARIES = libutil_io.la
libutil_io_la_SOURCES = fd.h fd.cc fd_share.h fd_share.cc
libutil_io_la_SOURCES += socketsession.h socketsession.cc sockaddr_util.h
+libutil_io_la_SOURCES += pktinfo_utilities.h
libutil_io_la_LIBADD = $(top_builddir)/src/lib/exceptions/libexceptions.la
CLEANFILES = *.gcno *.gcda
diff --git a/src/lib/util/io/pktinfo_utilities.h b/src/lib/util/io/pktinfo_utilities.h
new file mode 100644
index 0000000..9883c30
--- /dev/null
+++ b/src/lib/util/io/pktinfo_utilities.h
@@ -0,0 +1,51 @@
+// 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 __PKTINFO_UTIL_H_
+#define __PKTINFO_UTIL_H_ 1
+
+#include <sys/socket.h>
+#include <netinet/in.h>
+
+// These definitions in this file are for the convenience of internal
+// implementation and test code, and are not intended to be used publicly.
+// The namespace "internal" indicates the intent.
+
+namespace isc {
+namespace util {
+namespace io {
+namespace internal {
+
+// Lower level C-APIs require conversion between char* pointers
+// (like structures returned by CMSG_DATA macro) and in6_pktinfo,
+// which is not friendly with C++. The following templates
+// are a shortcut of common workaround conversion in such cases.
+inline struct in6_pktinfo*
+convertPktInfo6(char* pktinfo) {
+ return (static_cast<struct in6_pktinfo*>(static_cast<void*>(pktinfo)));
+}
+
+inline struct in6_pktinfo*
+convertPktInfo6(unsigned char* pktinfo) {
+ return (static_cast<struct in6_pktinfo*>(static_cast<void*>(pktinfo)));
+}
+
+/// @todo: Do we need const versions as well?
+
+}
+}
+}
+}
+
+#endif // __PKTINFO_UTIL_H_
diff --git a/src/lib/util/io/sockaddr_util.h b/src/lib/util/io/sockaddr_util.h
index 3ec6cf0..9b4a0cb 100644
--- a/src/lib/util/io/sockaddr_util.h
+++ b/src/lib/util/io/sockaddr_util.h
@@ -20,7 +20,7 @@
#include <cassert>
-// This definitions in this file are for the convenience of internal
+// These definitions in this file are for the convenience of internal
// implementation and test code, and are not intended to be used publicly.
// The namespace "internal" indicates the intent.
diff --git a/src/lib/util/range_utilities.h b/src/lib/util/range_utilities.h
new file mode 100644
index 0000000..3f8b971
--- /dev/null
+++ b/src/lib/util/range_utilities.h
@@ -0,0 +1,68 @@
+// Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef __RANGE_UTIL_H_
+#define __RANGE_UTIL_H_ 1
+
+#include <stdlib.h>
+#include <algorithm>
+
+// This header contains useful methods for conduction operations on
+// a range of container elements. Currently the collection is limited,
+// but it is expected to grow.
+
+namespace isc {
+namespace util {
+
+/// @brief Checks if specified range in a container contains only zeros
+///
+/// @param begin beginning of the range
+/// @param end end of the range
+///
+/// @return true if there are only zeroes, false otherwise
+template <typename Iterator>
+bool
+isRangeZero(Iterator begin, Iterator end) {
+ return (std::find_if(begin, end,
+ std::bind1st(std::not_equal_to<int>(), 0))
+ == end);
+}
+
+/// @brief Fill in specified range with a random data.
+///
+/// Make sure that random number generator is initialized properly. Otherwise you
+/// will get a the same pseudo-random sequence after every start of your process.
+/// Calling srand() is enough. This method uses default rand(), which is usually
+/// a LCG pseudo-random number generator, so it is not suitable for security
+/// purposes. Please get a decent PRNG implementation, like mersene twister, if
+/// you are doing anything related with security.
+///
+/// PRNG initialization is left out of this function on purpose. It may be
+/// initialized to specific value on purpose, e.g. to repeat exactly the same
+/// sequence in a test.
+///
+/// @param begin
+/// @param end
+template <typename Iterator>
+void
+fillRandom(Iterator begin, Iterator end) {
+ for (Iterator x = begin; x != end; ++x) {
+ *x = random();
+ }
+}
+
+} // end of isc::util namespace
+} // end of isc namespace
+
+#endif // __PKTINFO_UTIL_H_
diff --git a/src/lib/util/tests/Makefile.am b/src/lib/util/tests/Makefile.am
index dc144bd..4aea951 100644
--- a/src/lib/util/tests/Makefile.am
+++ b/src/lib/util/tests/Makefile.am
@@ -34,6 +34,7 @@ run_unittests_SOURCES += sha1_unittest.cc
run_unittests_SOURCES += socketsession_unittest.cc
run_unittests_SOURCES += strutil_unittest.cc
run_unittests_SOURCES += time_utilities_unittest.cc
+run_unittests_SOURCES += range_utilities_unittest.cc
run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
diff --git a/src/lib/util/tests/buffer_unittest.cc b/src/lib/util/tests/buffer_unittest.cc
index 8cccd28..9af3d57 100644
--- a/src/lib/util/tests/buffer_unittest.cc
+++ b/src/lib/util/tests/buffer_unittest.cc
@@ -12,11 +12,15 @@
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
+#include <gtest/gtest.h>
+
#include <exceptions/exceptions.h>
-#include <util/buffer.h>
+#ifdef EXPECT_DEATH
+#include <util/unittests/resource.h>
+#endif /* EXPECT_DEATH */
-#include <gtest/gtest.h>
+#include <util/buffer.h>
using namespace isc;
@@ -185,6 +189,8 @@ TEST_F(BufferTest, outputBufferReadat) {
#ifdef EXPECT_DEATH
// We use assert now, so we check it dies
EXPECT_DEATH({
+ isc::util::unittests::dontCreateCoreDumps();
+
try {
obuffer[sizeof(testdata)];
} catch (...) {
@@ -249,7 +255,7 @@ TEST_F(BufferTest, outputBufferZeroSize) {
});
}
-TEST_F(BufferTest, readVectorAll) {
+TEST_F(BufferTest, inputBufferReadVectorAll) {
std::vector<uint8_t> vec;
// check that vector can read the whole buffer
@@ -265,7 +271,7 @@ TEST_F(BufferTest, readVectorAll) {
);
}
-TEST_F(BufferTest, readVectorChunks) {
+TEST_F(BufferTest, inputBufferReadVectorChunks) {
std::vector<uint8_t> vec;
// check that vector can read the whole buffer
diff --git a/src/lib/util/tests/range_utilities_unittest.cc b/src/lib/util/tests/range_utilities_unittest.cc
new file mode 100644
index 0000000..6fa1c64
--- /dev/null
+++ b/src/lib/util/tests/range_utilities_unittest.cc
@@ -0,0 +1,58 @@
+// Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <config.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+#include <gtest/gtest.h>
+#include <vector>
+
+#include <util/range_utilities.h>
+
+using namespace std;
+using namespace isc::util;
+
+TEST(RangeUtilitiesTest, isZero) {
+
+ vector<uint8_t> vec(32,0);
+
+ EXPECT_TRUE(isRangeZero(vec.begin(), vec.end()));
+
+ EXPECT_TRUE(isRangeZero(vec.begin(), vec.begin()+1));
+
+ vec[5] = 1;
+ EXPECT_TRUE(isRangeZero(vec.begin(), vec.begin()+5));
+ EXPECT_FALSE(isRangeZero(vec.begin(), vec.begin()+6));
+}
+
+TEST(RangeUtilitiesTest, randomFill) {
+
+ srandom(time(NULL));
+
+ vector<uint8_t> vec1(16,0);
+ vector<uint8_t> vec2(16,0);
+
+ // Testing if returned value is actually random is extraordinary difficult.
+ // Let's just generate bunch of vectors and see if we get the same
+ // value. If we manage to do that in 100 tries, pseudo-random generator
+ // really sucks.
+ fillRandom(vec1.begin(), vec1.end());
+ for (int i=0; i<100; i++) {
+ fillRandom(vec2.begin(), vec2.end());
+ if (vec1 == vec2)
+ FAIL();
+ }
+
+}
diff --git a/src/lib/util/unittests/Makefile.am b/src/lib/util/unittests/Makefile.am
index bbb0d49..2827471 100644
--- a/src/lib/util/unittests/Makefile.am
+++ b/src/lib/util/unittests/Makefile.am
@@ -6,6 +6,7 @@ libutil_unittests_la_SOURCES = fork.h fork.cc resolver.h
libutil_unittests_la_SOURCES += newhook.h newhook.cc
libutil_unittests_la_SOURCES += testdata.h testdata.cc
if HAVE_GTEST
+libutil_unittests_la_SOURCES += resource.h resource.cc
libutil_unittests_la_SOURCES += run_all.h run_all.cc
libutil_unittests_la_SOURCES += textdata.h
endif
diff --git a/src/lib/util/unittests/resource.cc b/src/lib/util/unittests/resource.cc
new file mode 100644
index 0000000..3e77e0d
--- /dev/null
+++ b/src/lib/util/unittests/resource.cc
@@ -0,0 +1,35 @@
+// 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 "resource.h"
+
+#include <gtest/gtest.h>
+
+#include <sys/time.h>
+#include <sys/resource.h>
+
+namespace isc {
+namespace util {
+namespace unittests {
+
+void
+dontCreateCoreDumps() {
+ const rlimit core_limit = {0, 0};
+
+ EXPECT_EQ(setrlimit(RLIMIT_CORE, &core_limit), 0);
+}
+
+} // end of namespace unittests
+} // end of namespace util
+} // end of namespace isc
diff --git a/src/lib/util/unittests/resource.h b/src/lib/util/unittests/resource.h
new file mode 100644
index 0000000..6430ab2
--- /dev/null
+++ b/src/lib/util/unittests/resource.h
@@ -0,0 +1,39 @@
+// 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 __UTIL_UNITTESTS_RESOURCE_H
+#define __UTIL_UNITTESTS_RESOURCE_H 1
+
+namespace isc {
+namespace util {
+namespace unittests {
+
+/// Don't create core dumps.
+///
+/// This function sets the core size to 0, inhibiting the creation of
+/// core dumps. It is meant to be used in testcases where EXPECT_DEATH
+/// is used, where processes abort (and create cores in the process).
+/// As a new process is forked to run EXPECT_DEATH tests, the rlimits of
+/// the parent process that runs the other tests should be unaffected.
+void dontCreateCoreDumps();
+
+} // end of namespace unittests
+} // end of namespace util
+} // end of namespace isc
+
+#endif /* __UTIL_UNITTESTS_RESOURCE_H */
+
+// Local Variables:
+// mode: c++
+// End:
diff --git a/tests/lettuce/README b/tests/lettuce/README
index 21a57c7..94bf82b 100644
--- a/tests/lettuce/README
+++ b/tests/lettuce/README
@@ -6,7 +6,7 @@ these tests are specific for BIND10, but we are keeping in mind that RFC-related
tests could be separated, so that we can test other systems as well.
Prerequisites:
-- Installed version of BIND 10 (but see below how to run it from source tree)
+- BIND 10 must be compiled or installed
- dig
- lettuce (http://lettuce.it)
@@ -26,27 +26,23 @@ Port 47805 is used for cmdctl, and must also be available.
(note, we will need to extend this to a range, or if possible, we will need to
do some on-the-fly available port finding)
-The bind10 main program, bindctl, and dig must all be in the default search
-path of your environment, and BIND 10 must not be running if you use the
-installed version when you run the tests.
+You can run the lettuce tests with the provided run_lettuce.sh script.
-If you want to test an installed version of bind 10, just run 'lettuce' in
-this directory.
+By default it will use the build tree, but you can use an installed version
+of bind10 by passing -I as the first argument of run_lettuce.sh
-We have provided a script that sets up the shell environment to run the tests
-with the build tree version of bind. If your shell uses export to set
-environment variables, you can source the script setup_intree_bind10.sh, then
-run lettuce.
+The tool 'dig' must be in the default search path of your environment. If
+you specified -I, so must the main bind10 program, as well as bindctl.
Due to the default way lettuce prints its output, it is advisable to run it
in a terminal that is wide than the default. If you see a lot of lines twice
in different colors, the terminal is not wide enough.
If you just want to run one specific feature test, use
-lettuce features/<feature file>
+run_lettuce.sh [-I] features/<feature file>
To run a specific scenario from a feature, use
-lettuce features/<feature file> -s <scenario number>
+run_lettuce.sh [-I] features/<feature file> -s <scenario number>
We have set up the tests to assume that lettuce is run from this directory,
so even if you specify a specific feature file, you should do it from this
diff --git a/tests/lettuce/README.tutorial b/tests/lettuce/README.tutorial
index c7d3cd7..7d1c801 100644
--- a/tests/lettuce/README.tutorial
+++ b/tests/lettuce/README.tutorial
@@ -54,7 +54,7 @@ The one scenario we have has no steps, so if we run it we should
see something like:
-- output
-> lettuce
+> ./run_lettuce.sh
Feature: showing off BIND 10
This is to show BIND 10 running and that it answer queries
@@ -95,7 +95,7 @@ it to be started before we continue.
And let's run the tests again.
--
-> lettuce
+> ./run_lettuce.sh
Feature: showing off BIND 10
This is to show BIND 10 running and that it answer queries
diff --git a/tests/lettuce/configurations/NOTES b/tests/lettuce/configurations/NOTES
new file mode 100644
index 0000000..a9b7976
--- /dev/null
+++ b/tests/lettuce/configurations/NOTES
@@ -0,0 +1,4 @@
+- In some configuration we intentionally use an IPv6 address (::1) with
+ port 47807. DO NOT CHANGE THAT; at least do not change it to
+ 127.0.0.1:47807. See git e3f4b290d17a68db728166cdffcbe93517966e8b
+ for why.
diff --git a/tests/lettuce/configurations/bindctl/.gitignore b/tests/lettuce/configurations/bindctl/.gitignore
new file mode 100644
index 0000000..e14ae76
--- /dev/null
+++ b/tests/lettuce/configurations/bindctl/.gitignore
@@ -0,0 +1 @@
+/bindctl.config
diff --git a/tests/lettuce/configurations/bindctl/bindctl.config.orig b/tests/lettuce/configurations/bindctl/bindctl.config.orig
new file mode 100644
index 0000000..bf623c5
--- /dev/null
+++ b/tests/lettuce/configurations/bindctl/bindctl.config.orig
@@ -0,0 +1,22 @@
+{
+ "version": 2,
+ "Logging": {
+ "loggers": [ {
+ "debuglevel": 99,
+ "severity": "DEBUG",
+ "name": "*"
+ } ]
+ },
+ "Auth": {
+ "database_file": "data/example.org.sqlite3",
+ "listen_on": [ {
+ "port": 47806,
+ "address": "127.0.0.1"
+ } ]
+ },
+ "Boss": {
+ "components": {
+ "b10-cmdctl": { "special": "cmdctl", "kind": "needed" }
+ }
+ }
+}
diff --git a/tests/lettuce/configurations/inmemory_over_sqlite3/secondary.conf b/tests/lettuce/configurations/inmemory_over_sqlite3/secondary.conf
index a104726..8571015 100644
--- a/tests/lettuce/configurations/inmemory_over_sqlite3/secondary.conf
+++ b/tests/lettuce/configurations/inmemory_over_sqlite3/secondary.conf
@@ -4,7 +4,7 @@
"loggers": [ {
"debuglevel": 99,
"severity": "DEBUG",
- "name": "auth"
+ "name": "*"
} ]
},
"Auth": {
diff --git a/tests/lettuce/configurations/multi_instance/multi_auth.config.orig b/tests/lettuce/configurations/multi_instance/multi_auth.config.orig
index 53c2b7a..35b8e72 100644
--- a/tests/lettuce/configurations/multi_instance/multi_auth.config.orig
+++ b/tests/lettuce/configurations/multi_instance/multi_auth.config.orig
@@ -1 +1,24 @@
-{"version": 2, "Logging": {"loggers": [{"severity": "DEBUG", "name": "*", "debuglevel": 99}]}, "Auth": {"listen_on": [{"port": 47806, "address": "0.0.0.0"}]}, "Boss": {"components": {"b10-auth-2": {"kind": "dispensable", "special": "auth"}, "b10-auth": {"kind": "dispensable", "special": "auth"}, "b10-cmdctl": {"kind": "needed", "special": "cmdctl"}}}}
+{
+ "version": 2,
+ "Logging": {
+ "loggers": [ {
+ "debuglevel": 99,
+ "severity": "DEBUG",
+ "name": "*"
+ } ]
+ },
+ "Auth": {
+ "database_file": "data/test_nonexistent_db.sqlite3",
+ "listen_on": [ {
+ "port": 47806,
+ "address": "127.0.0.1"
+ } ]
+ },
+ "Boss": {
+ "components": {
+ "b10-auth-2": {"kind": "dispensable", "special": "auth"},
+ "b10-auth": {"kind": "dispensable", "special": "auth"},
+ "b10-cmdctl": {"kind": "needed", "special": "cmdctl"}
+ }
+ }
+}
diff --git a/tests/lettuce/configurations/xfrin/inmem_slave.conf b/tests/lettuce/configurations/xfrin/inmem_slave.conf
new file mode 100644
index 0000000..a6d88ee
--- /dev/null
+++ b/tests/lettuce/configurations/xfrin/inmem_slave.conf
@@ -0,0 +1,34 @@
+{
+ "version": 2,
+ "Logging": {
+ "loggers": [ {
+ "debuglevel": 99,
+ "severity": "DEBUG",
+ "name": "*"
+ } ]
+ },
+ "Auth": {
+ "database_file": "data/inmem-xfrin.sqlite3",
+ "datasources": [ {
+ "type": "memory",
+ "class": "IN",
+ "zones": [ {
+ "origin": "example.org",
+ "file": "data/inmem-xfrin.sqlite3",
+ "filetype": "sqlite3"
+ } ]
+ } ],
+ "listen_on": [ {
+ "port": 47806,
+ "address": "127.0.0.1"
+ } ]
+ },
+ "Boss": {
+ "components": {
+ "b10-auth": { "kind": "needed", "special": "auth" },
+ "b10-xfrin": { "address": "Xfrin", "kind": "dispensable" },
+ "b10-zonemgr": { "address": "Zonemgr", "kind": "dispensable" },
+ "b10-cmdctl": { "special": "cmdctl", "kind": "needed" }
+ }
+ }
+}
diff --git a/tests/lettuce/data/.gitignore b/tests/lettuce/data/.gitignore
index 299b6d2..8c54200 100644
--- a/tests/lettuce/data/.gitignore
+++ b/tests/lettuce/data/.gitignore
@@ -1 +1,2 @@
+/inmem-xfrin.sqlite3
/test_nonexistent_db.sqlite3
diff --git a/tests/lettuce/data/commands/bad_command b/tests/lettuce/data/commands/bad_command
new file mode 100644
index 0000000..95d1694
--- /dev/null
+++ b/tests/lettuce/data/commands/bad_command
@@ -0,0 +1,9 @@
+!echo shouldshow
+# just add something so the test can verify it's reverted
+config add /Boss/components b10-auth
+config set /Boss/components/b10-auth/kind needed
+config set /Boss/components/b10-auth/special auth
+bad command
+# this should not be reached
+!echo shouldnotshow
+config commit
diff --git a/tests/lettuce/data/commands/directives b/tests/lettuce/data/commands/directives
new file mode 100644
index 0000000..4fe10f5
--- /dev/null
+++ b/tests/lettuce/data/commands/directives
@@ -0,0 +1,19 @@
+# this is a comment: commentexample1
+!echo this is an echo: echoexample2
+!verbose on
+# this is a comment with verbose on: verbosecommentexample3
+!verbose off
+# this is a comment with verbose off again: commentexample4
+# empty lines and lines with only whitespace should be ignored
+
+
+
+
+
+# directives are case insensitive, and should handle whitespace
+!ECHO echoexample5
+!eChO echoexample6
+!Verbose ON
+# verbosecommentexample7
+!verBOSE off
+# commentexample8
diff --git a/tests/lettuce/data/commands/empty b/tests/lettuce/data/commands/empty
new file mode 100644
index 0000000..e69de29
diff --git a/tests/lettuce/data/commands/nested b/tests/lettuce/data/commands/nested
new file mode 100644
index 0000000..c153694
--- /dev/null
+++ b/tests/lettuce/data/commands/nested
@@ -0,0 +1,2 @@
+# include a different file
+execute file data/commands/nested1
diff --git a/tests/lettuce/data/commands/nested1 b/tests/lettuce/data/commands/nested1
new file mode 100644
index 0000000..8f984d5
--- /dev/null
+++ b/tests/lettuce/data/commands/nested1
@@ -0,0 +1,2 @@
+# this is included by nested
+!echo shouldshow
diff --git a/tests/lettuce/data/example.org.sqlite3 b/tests/lettuce/data/example.org.sqlite3
index 715a092..427fa24 100644
Binary files a/tests/lettuce/data/example.org.sqlite3 and b/tests/lettuce/data/example.org.sqlite3 differ
diff --git a/tests/lettuce/data/inmem-xfrin b/tests/lettuce/data/inmem-xfrin
new file mode 100644
index 0000000..9e02591
--- /dev/null
+++ b/tests/lettuce/data/inmem-xfrin
@@ -0,0 +1,7 @@
+example.org. 3600 IN SOA ns1.example.org. admin.example.org. 1234 3600 1800 2419200 7200
+example.org. 3600 IN NS ns1.example.org.
+example.org. 3600 IN NS ns2.example.org.
+example.org. 3600 IN MX 10 mail.example.org.
+www.example.org. 3600 IN A 192.0.2.63
+ns1.example.org. 3600 IN A 192.0.2.3
+ns2.example.org. 3600 IN A 192.0.2.4
diff --git a/tests/lettuce/data/inmem-xfrin.sqlite3.orig b/tests/lettuce/data/inmem-xfrin.sqlite3.orig
new file mode 100644
index 0000000..287d980
Binary files /dev/null and b/tests/lettuce/data/inmem-xfrin.sqlite3.orig differ
diff --git a/tests/lettuce/features/bindctl_commands.feature b/tests/lettuce/features/bindctl_commands.feature
index 03a04bf..1ab506d 100644
--- a/tests/lettuce/features/bindctl_commands.feature
+++ b/tests/lettuce/features/bindctl_commands.feature
@@ -1,65 +1,156 @@
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 bindctl_commands.config
- And wait for bind10 stderr message BIND10_STARTED_CC
- And wait for bind10 stderr message CMDCTL_STARTED
- And wait for bind10 stderr message ZONEMGR_STARTED
- And wait for bind10 stderr message AUTH_SERVER_STARTED
- And wait for bind10 stderr message XFRIN_STARTED
- And wait for bind10 stderr message XFROUT_STARTED
- And wait for bind10 stderr message STATS_STARTING
- And wait for bind10 stderr message STATHTTPD_STARTED
-
- 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
- bind10 module Resolver should not be running
-
- Then remove bind10 configuration Boss/components value b10-xfrout
- And wait for new bind10 stderr message BIND10_PROCESS_ENDED
- 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-httpd
- And wait for new bind10 stderr message BIND10_PROCESS_ENDED
- last bindctl output should not contain Error
-
- Then remove bind10 configuration Boss/components value b10-stats
- And wait for new bind10 stderr message BIND10_PROCESS_ENDED
- last bindctl output should not contain Error
-
- Then remove bind10 configuration Boss/components value b10-zonemgr
- And wait for new bind10 stderr message BIND10_PROCESS_ENDED
- last bindctl output should not contain Error
-
- Then remove bind10 configuration Boss/components value b10-xfrin
- And wait for new bind10 stderr message BIND10_PROCESS_ENDED
- last bindctl output should not contain Error
-
- Then remove bind10 configuration Boss/components value b10-auth
- And wait for new bind10 stderr message BIND10_PROCESS_ENDED
- last bindctl output should not contain Error
-
- # After these ^^^ have been stopped...
- bind10 module Xfrout 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
- bind10 module Stats should not be running
- bind10 module Resolver should not be running
+ # 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 bindctl_commands.config
+ And wait for bind10 stderr message BIND10_STARTED_CC
+ And wait for bind10 stderr message CMDCTL_STARTED
+ And wait for bind10 stderr message ZONEMGR_STARTED
+ And wait for bind10 stderr message AUTH_SERVER_STARTED
+ And wait for bind10 stderr message XFRIN_STARTED
+ And wait for bind10 stderr message XFROUT_STARTED
+ And wait for bind10 stderr message STATS_STARTING
+ And wait for bind10 stderr message STATHTTPD_STARTED
+
+ 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
+ bind10 module Resolver should not be running
+
+ Then remove bind10 configuration Boss/components value b10-xfrout
+ And wait for new bind10 stderr message BIND10_PROCESS_ENDED
+ 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-httpd
+ And wait for new bind10 stderr message BIND10_PROCESS_ENDED
+ last bindctl output should not contain Error
+
+ Then remove bind10 configuration Boss/components value b10-stats
+ And wait for new bind10 stderr message BIND10_PROCESS_ENDED
+ last bindctl output should not contain Error
+
+ Then remove bind10 configuration Boss/components value b10-zonemgr
+ And wait for new bind10 stderr message BIND10_PROCESS_ENDED
+ last bindctl output should not contain Error
+
+ Then remove bind10 configuration Boss/components value b10-xfrin
+ And wait for new bind10 stderr message BIND10_PROCESS_ENDED
+ last bindctl output should not contain Error
+
+ Then remove bind10 configuration Boss/components value b10-auth
+ And wait for new bind10 stderr message BIND10_PROCESS_ENDED
+ last bindctl output should not contain Error
+
+ # After these ^^^ have been stopped...
+ bind10 module Xfrout 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
+ bind10 module Stats should not be running
+ bind10 module Resolver should not be running
+
+ Scenario: Executing scripts from files
+ # This test tests the 'execute' command, which reads and executes
+ # bindctl commands from a file
+ Given I have bind10 running with configuration bindctl/bindctl.config
+ And wait for bind10 stderr message BIND10_STARTED_CC
+ And wait for bind10 stderr message CMDCTL_STARTED
+
+ # first a few bad commands
+ When I send bind10 the command execute
+ last bindctl output should contain Error
+ When I send bind10 the command execute file
+ last bindctl output should contain Error
+ When I send bind10 the command execute file data/commands/nosuchfile
+ last bindctl output should contain Error
+
+ # empty list should be no-op
+ When I send bind10 the command execute file data/commands/empty
+ last bindctl output should not contain Error
+
+ # some tests of directives like !echo and !verbose
+ When I send bind10 the command execute file data/commands/directives
+ last bindctl output should not contain Error
+ last bindctl output should not contain commentexample1
+ last bindctl output should contain echoexample2
+ last bindctl output should contain verbosecommentexample3
+ last bindctl output should not contain commentexample4
+ last bindctl output should contain echoexample5
+ last bindctl output should contain echoexample6
+ last bindctl output should contain verbosecommentexample7
+ last bindctl output should not contain commentexample8
+
+ # bad_command contains a bad command, at which point execution should stop
+ When I send bind10 the command execute file data/commands/bad_command
+ last bindctl output should contain shouldshow
+ last bindctl output should contain Error
+ last bindctl output should not contain shouldnotshow
+ # This would fail if the entire list was passed, or the configuration
+ # was committed
+ send bind10 the command config show Boss/components
+ last bindctl output should not contain b10-auth
+
+ # nested_command contains another execute script
+ When I send bind10 the command execute file data/commands/nested
+ last bindctl output should contain shouldshow
+ last bindctl output should not contain Error
+
+ # show commands from a file
+ When I send bind10 the command execute file data/commands/bad_command show
+ last bindctl output should not contain Error
+ last bindctl output should contain shouldshow
+ last bindctl output should contain shouldnotshow
+
+ Scenario: Executing builting script init_authoritative_server
+ Given I have bind10 running with configuration bindctl/bindctl.config
+ And wait for bind10 stderr message BIND10_STARTED_CC
+ And wait for bind10 stderr message CMDCTL_STARTED
+
+ When I send bind10 the command execute init_authoritative_server show
+ # just test some parts of the output
+ last bindctl output should contain /Boss/components/b10-auth/special
+ last bindctl output should contain /Boss/components/b10-zonemgr/kind
+ last bindctl output should contain Please
+
+ # nothing should have been changed
+ When I send bind10 the command config diff
+ last bindctl output should contain {}
+
+ # ok now make sure modules aren't running, execute it, and make
+ # sure modules are running
+ bind10 module Auth should not be running
+ bind10 module Xfrout should not be running
+ bind10 module Xfrin should not be running
+ bind10 module Zonemgr should not be running
+
+ When I send bind10 the following commands:
+ """
+ execute init_authoritative_server
+ config commit
+ """
+ And wait for bind10 stderr message AUTH_SERVER_STARTED
+ And wait for bind10 stderr message ZONEMGR_STARTED
+ And wait for bind10 stderr message XFRIN_STARTED
+ And wait for bind10 stderr message XFROUT_STARTED
+
+ last bindctl output should not contain Error
+ bind10 module Auth should be running
+ bind10 module Xfrout should be running
+ bind10 module Xfrin should be running
+ bind10 module Zonemgr should be running
diff --git a/tests/lettuce/features/default.feature b/tests/lettuce/features/default.feature
index daace57..ce7ee1e 100644
--- a/tests/lettuce/features/default.feature
+++ b/tests/lettuce/features/default.feature
@@ -6,13 +6,11 @@ Feature: default bind10 config
And wait for bind10 stderr message BIND10_STARTED_CC
And wait for bind10 stderr message CMDCTL_STARTED
And wait for bind10 stderr message STATS_STARTING
- And wait for bind10 stderr message STATHTTPD_STARTED
# These should be running
bind10 module Boss should be running
And bind10 module Logging should be running
And bind10 module Stats should be running
- And bind10 module StatsHttpd should be running
# These should not be running
bind10 module Resolver should not be running
@@ -20,3 +18,4 @@ Feature: default bind10 config
And bind10 module Zonemgr should not be running
And bind10 module Xfrin should not be running
And bind10 module Auth should not be running
+ And bind10 module StatsHttpd should not be running
diff --git a/tests/lettuce/features/example.feature b/tests/lettuce/features/example.feature
index ca5ffbf..9b7c2ca 100644
--- a/tests/lettuce/features/example.feature
+++ b/tests/lettuce/features/example.feature
@@ -5,7 +5,7 @@ Feature: Example feature
is intentionally uncommented.
The later scenarios have comments to show what the test steps do and
support
-
+
Scenario: A simple example
Given I have bind10 running with configuration example.org.config
And wait for bind10 stderr message BIND10_STARTED_CC
@@ -29,9 +29,9 @@ Feature: Example feature
# Underwater, we take advantage of our intialization routines so
# that we are sure this file does not exist, see
# features/terrain/terrain.py
-
- # Standard check to test (non-)existence of a file
- # This file is actually automatically
+
+ # Standard check to test (non-)existence of a file.
+ # This file is actually automatically created.
The file data/test_nonexistent_db.sqlite3 should not exist
# In the first scenario, we used 'given I have bind10 running', which
@@ -43,6 +43,9 @@ Feature: Example feature
And wait for bind10 stderr message CMDCTL_STARTED
And wait for bind10 stderr message AUTH_SERVER_STARTED
+ # Now we use the first step again to see if the file has been created
+ The file data/test_nonexistent_db.sqlite3 should exist
+
bind10 module Auth should be running
And bind10 module Resolver should not be running
And bind10 module Xfrout should not be running
@@ -56,16 +59,13 @@ Feature: Example feature
# use in the start step (for bind 10, that is 'I start bind10 with')
# See scenario 'Multiple instances' for more.
Then stop process bind10
-
- # Now we use the first step again to see if the file has been created
- The file data/test_nonexistent_db.sqlite3 should exist
Scenario: example.org queries
# This scenario performs a number of queries and inspects the results
# Simple queries have already been show, but after we have sent a query,
# we can also do more extensive checks on the result.
# See querying.py for more information on these steps.
-
+
# note: lettuce can group similar checks by using tables, but we
# intentionally do not make use of that here
diff --git a/tests/lettuce/features/inmemory_over_sqlite3.feature b/tests/lettuce/features/inmemory_over_sqlite3.feature
index 60945c8..2e48689 100644
--- a/tests/lettuce/features/inmemory_over_sqlite3.feature
+++ b/tests/lettuce/features/inmemory_over_sqlite3.feature
@@ -3,7 +3,41 @@ Feature: In-memory zone using SQLite3 backend
data source that uses the SQLite3 data source as the backend, and tests
scenarios that update the zone via incoming zone transfers.
- Scenario: Load and response
+ Scenario: 1. Load and response
Given I have bind10 running with configuration inmemory_over_sqlite3/secondary.conf
+ And wait for bind10 stderr message BIND10_STARTED_CC
+ And wait for bind10 stderr message CMDCTL_STARTED
+ And wait for bind10 stderr message AUTH_SERVER_STARTED
A query for www.example.org should have rcode NOERROR
The SOA serial for example.org should be 1234
+
+ Scenario: 2. In-memory datasource backed by sqlite3
+ Given I have bind10 running with configuration xfrin/retransfer_master.conf with cmdctl port 47804 as master
+ And wait for master stderr message BIND10_STARTED_CC
+ And wait for master stderr message CMDCTL_STARTED
+ And wait for master stderr message AUTH_SERVER_STARTED
+ And wait for master stderr message XFROUT_STARTED
+ And wait for master stderr message ZONEMGR_STARTED
+
+ And I have bind10 running with configuration xfrin/inmem_slave.conf
+ And wait for bind10 stderr message BIND10_STARTED_CC
+ And wait for bind10 stderr message CMDCTL_STARTED
+ And wait for bind10 stderr message AUTH_SERVER_STARTED
+ And wait for bind10 stderr message XFRIN_STARTED
+ And wait for bind10 stderr message ZONEMGR_STARTED
+
+ A query for www.example.org should have rcode NOERROR
+ """
+ www.example.org. 3600 IN A 192.0.2.63
+ """
+ A query for mail.example.org should have rcode NXDOMAIN
+ When I send bind10 the command Xfrin retransfer example.org IN ::1 47807
+ Then wait for new bind10 stderr message XFRIN_TRANSFER_SUCCESS not XFRIN_XFR_PROCESS_FAILURE
+ Then wait for new bind10 stderr message AUTH_LOAD_ZONE
+
+ A query for www.example.org should have rcode NOERROR
+ The answer section of the last query response should be
+ """
+ www.example.org. 3600 IN A 192.0.2.1
+ """
+ A query for mail.example.org should have rcode NOERROR
diff --git a/tests/lettuce/features/multi_instance.feature b/tests/lettuce/features/multi_instance.feature
index 048e60e..4ce135a 100644
--- a/tests/lettuce/features/multi_instance.feature
+++ b/tests/lettuce/features/multi_instance.feature
@@ -3,6 +3,10 @@ Feature: Multiple instances
removing them does not affect the running of other instances
Scenario: Multiple instances of Auth
+ # Standard check to test (non-)existence of a file
+ # This file is actually automatically
+ The file data/test_nonexistent_db.sqlite3 should not exist
+
# This config should have two running instances
Given I have bind10 running with configuration multi_instance/multi_auth.config
And wait for bind10 stderr message BIND10_STARTED_CC
@@ -21,6 +25,9 @@ Feature: Multiple instances
And bind10 module Stats should not be running
And bind10 module StatsHttpd should not be running
+ # Now we use the first step again to see if the file has been created
+ The file data/test_nonexistent_db.sqlite3 should exist
+
A query for example.com should have rcode REFUSED
# this also checks whether the process is running
diff --git a/tests/lettuce/features/queries.feature b/tests/lettuce/features/queries.feature
index f549f2d..de6f0fa 100644
--- a/tests/lettuce/features/queries.feature
+++ b/tests/lettuce/features/queries.feature
@@ -105,6 +105,9 @@ Feature: Querying feature
"""
Scenario: Delegation query for unsigned child zone
Given I have bind10 running with configuration example.org.inmem.config
+ And wait for bind10 stderr message BIND10_STARTED_CC
+ And wait for bind10 stderr message CMDCTL_STARTED
+ And wait for bind10 stderr message AUTH_SERVER_STARTED
A dnssec query for www.sub.example.org type AAAA should have rcode NOERROR
The last query response should have flags qr rd
The last query response should have edns_flags do
diff --git a/tests/lettuce/features/terrain/terrain.py b/tests/lettuce/features/terrain/terrain.py
index 753d912..caa17ce 100644
--- a/tests/lettuce/features/terrain/terrain.py
+++ b/tests/lettuce/features/terrain/terrain.py
@@ -28,6 +28,7 @@ import subprocess
import os.path
import shutil
import re
+import sys
import time
# In order to make sure we start all tests with a 'clean' environment,
@@ -46,10 +47,14 @@ copylist = [
"configurations/bindctl_commands.config"],
["configurations/example.org.config.orig",
"configurations/example.org.config"],
+ ["configurations/bindctl/bindctl.config.orig",
+ "configurations/bindctl/bindctl.config"],
["configurations/resolver/resolver_basic.config.orig",
"configurations/resolver/resolver_basic.config"],
["configurations/multi_instance/multi_auth.config.orig",
- "configurations/multi_instance/multi_auth.config"]
+ "configurations/multi_instance/multi_auth.config"],
+ ["data/inmem-xfrin.sqlite3.orig",
+ "data/inmem-xfrin.sqlite3"]
]
# This is a list of files that, if present, will be removed before a scenario
@@ -383,4 +388,13 @@ def cleanup(scenario):
world.processes.keep_files()
# Stop any running processes we may have had around
world.processes.stop_all_processes()
-
+
+# Environment check
+# Checks if LETTUCE_SETUP_COMPLETED is set in the environment
+# If not, abort with an error to use the run-script
+if 'LETTUCE_SETUP_COMPLETED' not in os.environ:
+ print("Environment check failure; LETTUCE_SETUP_COMPLETED not set")
+ print("Please use the run_lettuce.sh script. If you want to test an")
+ print("installed version of bind10 with these tests, use")
+ print("run_lettuce.sh -I [lettuce arguments]")
+ sys.exit(1)
diff --git a/tests/lettuce/features/terrain/transfer.py b/tests/lettuce/features/terrain/transfer.py
index 305e677..4ba45b8 100644
--- a/tests/lettuce/features/terrain/transfer.py
+++ b/tests/lettuce/features/terrain/transfer.py
@@ -58,7 +58,7 @@ class TransferResult(object):
if len(line) > 0 and line[0] != ';':
self.records.append(line)
- at step('An AXFR transfer of ([\w.]+)(?: from ([^:]+)(?::([0-9]+))?)?')
+ at step('An AXFR transfer of ([\w.]+)(?: from ([^:]+|\[[0-9a-fA-F:]+\])(?::([0-9]+))?)?')
def perform_axfr(step, zone_name, address, port):
"""
Perform an AXFR transfer, and store the result as an instance of
@@ -72,6 +72,8 @@ def perform_axfr(step, zone_name, address, port):
"""
if address is None:
address = "127.0.0.1"
+ # convert [IPv6_addr] to IPv6_addr:
+ address = re.sub(r"\[(.+)\]", r"\1", address)
if port is None:
port = 47806
args = [ 'dig', 'AXFR', '@' + str(address), '-p', str(port), zone_name ]
diff --git a/tests/lettuce/features/xfrin_bind10.feature b/tests/lettuce/features/xfrin_bind10.feature
index 69043d8..bccb53b 100644
--- a/tests/lettuce/features/xfrin_bind10.feature
+++ b/tests/lettuce/features/xfrin_bind10.feature
@@ -2,6 +2,10 @@ Feature: Xfrin
Tests for Xfrin, specific for BIND 10 behaviour.
Scenario: Retransfer command
+ # Standard check to test (non-)existence of a file.
+ # This file is actually automatically created.
+ The file data/test_nonexistent_db.sqlite3 should not exist
+
Given I have bind10 running with configuration xfrin/retransfer_master.conf with cmdctl port 47804 as master
And wait for master stderr message BIND10_STARTED_CC
And wait for master stderr message CMDCTL_STARTED
@@ -16,8 +20,19 @@ Feature: Xfrin
And wait for bind10 stderr message XFRIN_STARTED
And wait for bind10 stderr message ZONEMGR_STARTED
+ # Now we use the first step again to see if the file has been created
+ The file data/test_nonexistent_db.sqlite3 should exist
+
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 ::1 47807
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
+
+ # The transferred zone should have 11 non-NSEC3 RRs and 1 NSEC3 RR.
+ # The following check will get these by AXFR, so the total # of RRs
+ # should be 13, counting the duplicated SOA.
+ # At this point we can confirm both in and out of AXFR for a zone
+ # containing an NSEC3 RR.
+ # We don't have to specify the address/port here; the defaults will work.
+ When I do an AXFR transfer of example.org
+ Then transfer result should have 13 rrs
diff --git a/tests/lettuce/run_lettuce.sh b/tests/lettuce/run_lettuce.sh
new file mode 100755
index 0000000..9580dce
--- /dev/null
+++ b/tests/lettuce/run_lettuce.sh
@@ -0,0 +1,25 @@
+#! /bin/sh
+
+# 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.
+
+if [ "$1" = "-I" ]; then
+ shift
+ echo "$@"
+ LETTUCE_SETUP_COMPLETED=1 exec lettuce $@
+else
+ . ./setup_intree_bind10.sh
+ exec lettuce $@
+fi
diff --git a/tests/lettuce/setup_intree_bind10.sh.in b/tests/lettuce/setup_intree_bind10.sh.in
old mode 100755
new mode 100644
index 9d72778..4ccf6ca
--- a/tests/lettuce/setup_intree_bind10.sh.in
+++ b/tests/lettuce/setup_intree_bind10.sh.in
@@ -44,3 +44,5 @@ export B10_FROM_BUILD
BIND10_MSGQ_SOCKET_FILE=@abs_top_builddir@/msgq_socket
export BIND10_MSGQ_SOCKET_FILE
+
+export LETTUCE_SETUP_COMPLETED=1
diff --git a/tests/tools/perfdhcp/perfdhcp.c b/tests/tools/perfdhcp/perfdhcp.c
index ba115cc..3ab9a2e 100644
--- a/tests/tools/perfdhcp/perfdhcp.c
+++ b/tests/tools/perfdhcp/perfdhcp.c
@@ -93,7 +93,7 @@ pselect (int nfds, fd_set *readfds, fd_set *writefds,
return (select(nfds, readfds, writefds, exceptfds, &my_timeout));
}
-#endif /* HAVE_PSELECT */
+#endif /* !HAVE_PSELECT */
/* DHCPv4 defines (to be moved/shared) */
@@ -1236,6 +1236,13 @@ getsock6(void)
perror("socket");
exit(1);
}
+ ret = bind(sock,
+ (struct sockaddr *) &localaddr,
+ sizeof(struct sockaddr_in6));
+ if (ret < 0) {
+ perror("Failed to bind v6 socket to local-link address");
+ exit(1);
+ }
/* perform the multicast stuff when the destination is multicast */
if (IN6_IS_ADDR_MULTICAST(&s6->sin6_addr)) {
int hops = 1;
More information about the bind10-changes
mailing list