BIND 10 trac2000, updated. c1c4ea8d1b7629dac379f192d30cc79535214236 [2000] Stop using API that was removed
BIND 10 source code commits
bind10-changes at lists.isc.org
Wed Feb 19 13:48:51 UTC 2014
The branch, trac2000 has been updated
via c1c4ea8d1b7629dac379f192d30cc79535214236 (commit)
via 6b2038265d47dd365b439051caa7c23b8e19309c (commit)
via 670577e8ed19eea6e5a679d030faea7c2f8641e1 (commit)
via 0770d2df84e5608371db3a47e0456eb2a340b5f4 (commit)
via 91867e0886ec2511053bf0fd25f8cfb9fbfda1d4 (commit)
via 4546dd186782eec5cfcb4ddb61b0a3aa5c700751 (commit)
via c0343b416262f4b88a695e7eb13f9b271277477f (commit)
via 5ba017624caefa28eb330dbcf4bafe0890fbb899 (commit)
via fe4183783d075bd2ac2aef3d7dc6e3de999d8589 (commit)
via 920f29296cf82087c35cc6a57fe05bdd3eef99f7 (commit)
via e07be6f5511f491c1575b289b8ca288434664673 (commit)
via acb20abee749563f5de98d582825f29b39046f11 (commit)
via 5c94286b3fcf47697829b2edba32cd47ed090bd6 (commit)
via 1233aa6063c1790b69247fa96790651742d27c12 (commit)
via 8ec8a19d2342170d7deb9ee771c157eb439bc278 (commit)
via ff45419489bd722cb868695c3a2360b2d08803c9 (commit)
via b05064f681231fe7f8571253c5786f4ff0f2ca03 (commit)
via 89ba2e6d39cb06fab5a594f7cabae57802726133 (commit)
via 3fc1c63ddb5524345e80c07c5dbea3cfc785e683 (commit)
via e8513d7199db5ba79b1f91087f57eb54ab81ef74 (commit)
via afd6081696c2ae59c01f527bdbdb5381bb8052ba (commit)
via b9bfb886c58492b361bbbcc0db90d7a0a641d493 (commit)
via 65c3f68979e3f0fd2078c77f114a45a26faeaaba (commit)
via a06fc6d984b97e2d6aef8233acd2161b44adfdc4 (commit)
via 6c21d3743e3e779bede1ebd6e4906697d3b0076a (commit)
via c03cc8c38810d3316faf205d8dda1d9bbbaab795 (commit)
via d8558bef68e5c67785deaceb23046c168afca4c4 (commit)
via a96232b3826660654f5afe4bb6961e6929635e2a (commit)
via 9aa52ecbbc7366e0246cbc554db9d3fccdf47b3b (commit)
via 4df951747393f5e59babd70ecba190b6dcb2fd74 (commit)
via a0d7b9f1bcd89644e4eeb256c2083809273e40c6 (commit)
via 98a54e274774cb7b8f1d2d3673fa39f3ef14beb8 (commit)
via 23911dbc9aafb2f8b00755a74284ac82dba19bee (commit)
via 1cc088ff06c72fa566dde903f972450f4a78ce1b (commit)
via fe7ae0a7b3eca38b81779b9cad22161e4bcf1d26 (commit)
via 72d32778775dc5517d208e300710f6a96e1848ee (commit)
via 35c022c9fed4ea2b739c7492bdd2a2b7d95e5563 (commit)
via e448f9b0e46ea1a4812572818c5f63051b697ef0 (commit)
via 8a12b4d0c3a1c39e780f7923c6538f1f543c6375 (commit)
via ff31f7b600ba73988f2177a7a6b502e019a86c35 (commit)
via 3ccbb10693264fa527ce7fe82da1b6a4a569e7c8 (commit)
via 45cd4d8881742dbd6bba9c965e01d2c5d206971f (commit)
via 74341d7cf0b8d7d2c3454b6be29b83006e67d85b (commit)
via 26808da540dbf7b5ac1127303ea4562309949d05 (commit)
via 0053becdd7452871439bcc19095ed9b50f7e01bc (commit)
via 9bcad85c5805dd4997d59bf15646aa18ceaf3d20 (commit)
via 670eae890769e6a570cb568f668e9161b9f8c252 (commit)
via 14b17e299221f6df1c7fbd2948b34c8cdbcd41f0 (commit)
via e200b6ca369bd2b49e7dd444c7845e06e66e4e94 (commit)
via 5945c9e2ba2308bca25e25008a6f888062b59375 (commit)
via ecfe0ec289e3a9f790e3ff36e99c3079f161b41f (commit)
via e3f6de57b908d44370bad1a9cdec70ed2f21bfa8 (commit)
via 9559e3c22ec58c532161d182e5dba5b6bbec16de (commit)
via aed23096ae5107148adad1b77532a02f6d9a6706 (commit)
via 412fdad4d0da738faa07d5e3016c4a5ae0bdaa5a (commit)
via 0715531bad337be1763bd10a3eca2c0c2d52513b (commit)
via 1791d19899b92a6ee411199f664bdfc690ec08b2 (commit)
via dbacf27ece77f3d857da793341c6bd31ef1ea239 (commit)
via 0df0f16c287b799a5c9b01764dfc30dbb2ce862c (commit)
via 2ca54a3ed0a89b2b90a10e2cc80bed218ae8607c (commit)
via 0c74cff26aa147bd1f8b807b441d86a064a90f60 (commit)
via 689945c67511e3010efccfeda391d663d446efa0 (commit)
via be2f40d53d36999366ea406d50946e3ef6dc478b (commit)
via 9e571cc217d6b1a2fd6fdae1565fcc6fde6d08b1 (commit)
via a080f7b48a4895a5dfbee3690de6cc2881f123cd (commit)
via 18c4f954d1a7466f2b5bde33f855aa69707e2406 (commit)
via 49d0ce1889192c7df7dc94544dfca8e51054015e (commit)
via 16a6ed6e48a6a950670c4874a2e81b1faf287d99 (commit)
via d8683cbfed08e4bdb0716d6576f8bc7e2e8a3c83 (commit)
via b6c929fa55635e2fc6b673b6eb02569c55a16887 (commit)
via 555c87c709c5b8306f469e620cded1ca4c95f2e6 (commit)
via 97cec5c2ecf2f1864b58497cf12ae848a78ee865 (commit)
via fc439d51f086be9351790fd731355eeb2d27fbb5 (commit)
via 5f56f59e4beab346584b53e8e80973f39ced5cff (commit)
via f2c1e775f7b2281655da68327fcae2be3a5fd2ca (commit)
via 98b660fab463b0dc07aea012192d064ddfc7194c (commit)
via 0274e1589707465e98d00b08fd940753549074ae (commit)
via 340784189765ee4dd56aaea7a71d50d2edf18684 (commit)
via f22c490bd4a194ad693e29a8e9f65a0480405e5c (commit)
via 400bf9a1f5d4e667c72a51255a2477721876d103 (commit)
via 9ffc91f18aa5ec78ef283a8412ed9da1a6874508 (commit)
via 81e8d71150056ee6c86a5a8aadbadb3ee6682f6a (commit)
via 6e4c4c173a90c96b6fefe6951d21f950256b0b6b (commit)
via 064e66c1a00c6dfffe252e5e90f7f49f17accb38 (commit)
via 195af9e40f0715d241af51c704faa4fea6a96839 (commit)
via 8b413f0b6f0325a124af6e170e9c96016d032007 (commit)
via c1b9c0314457a680d7b85a81d794ba5625bca791 (commit)
via b3493c2c68c1ee9bb7688ef66eb01643d3d672a8 (commit)
via a976de8f8bd3e6e0bf23385b47b28eb01edac2ce (commit)
via 6ab70c0a6b695f23987fa38de47aeec1d56f97af (commit)
via ecdb62db16b3f3d447db4a9d2a4079d5260431f0 (commit)
via 7aa18afc3a7ef40af3b1772c79d531ed62fc4188 (commit)
via 86d5acdb8b857924b207ada60173186e1d03afc9 (commit)
via aafa7f654275feaec7258b8434dd845f8400da8a (commit)
via 469f80fa1e994479c42898cffb88595cd3ba9185 (commit)
via 2dec38e5e39d5b391a1cbb3e72e18b31ba341cba (commit)
via 68d24e65c9c3dfee38adfbe1c93367b0083f9a58 (commit)
via 5b29cdb4be35f2e601a6ea962a9b0c056ff59e4b (commit)
via b0297a13fcbfd7309cdc41b8fe314de7201ed264 (commit)
via 9fcbaef68193a99a142f63b576fdfdd5519192c0 (commit)
via 1b522d63a842654c680602d3ee89eac324333d48 (commit)
via b94b3c71b79baf0707e984fd97b11036a04bcc23 (commit)
via c21653aa339f403136a96c06a127b1796c203d78 (commit)
via d9b2ef002120efa341a54b408c326cb40390bd04 (commit)
via fa84ba8b7f0f55ba7ac1cc85f2d1dd8e35247205 (commit)
via 7686ac9a3e309f9cbd179a897cae845f16cc3478 (commit)
via fba33a4f336010b0af70ba5c194d8850adf82485 (commit)
via f6d053c16792e0a511af21ca758b14fcf26ae032 (commit)
via 2fb705ea4e81f57170a9335a1d524946f6eef829 (commit)
via 6473b975c36f68104f002ab1d4f811ceefdc40ae (commit)
via 06cd899614951d962cf1454d2deeff5b31ad38c7 (commit)
via 41328ae3ce0eddddc4bb7c3752ba22b476c1bbf2 (commit)
via 1a9a2d0a3c9665dc260493e3e2e39bfad2643b96 (commit)
via d52f14789d9dc775b3a7f8da71dcd923affb2168 (commit)
via 87300261839710cb90f2176053d1bc5bcd3c0921 (commit)
via fbade26f032fdd495abe2effb0a2913b642b0ac6 (commit)
via b6f93473fc3c57fa2e74f223ddd1a65cd7f6238d (commit)
via 594b8277d0199731a3b282345248fea422d9ff86 (commit)
via 5e1c35d6a779706ba16f3076a0f6fa21dbeea81d (commit)
via ad3f1d33b943410cc4b782be9f07a4083883ea71 (commit)
via 293665afec0c7ee83316eec966574da9f1559d1b (commit)
via 4ca62ac7345e52e17466789bf65f8c27811e0fd6 (commit)
via de4c5e0913d80405f1795293ba28d63a5154eb60 (commit)
via 7c1e2ec6c98e82f0f14fbe479cb2e4f68117f8d0 (commit)
via f2ce3de4f881befc57c6328ceffb549a1a0c76af (commit)
via 290530e1c74db41d9dbf5e3dd733699345e7df2b (commit)
via 683c49b997b39ca4a064e21a11acbafc2ca7a4de (commit)
via c3a1bd5a608a7d39ea5e4220693f8c4068455d1b (commit)
via 1d945d79ff509a3df1485d0155eed206bbab8d53 (commit)
via 4b87d9046594e8c80f1fecf67fe3e87b62d18043 (commit)
via bc216c2cf0f9b78f94e17acef5ece0ea94250b6c (commit)
via 9c9ec6cd3c40f59b7550051106dfcd7ec66b0c42 (commit)
via b7c33a05cd6ec3b6e25eb3a94b41e3f720e3f8e5 (commit)
via 0b6ff4416ca1c990456d05d4a7d5417c523fdbc6 (commit)
via 393abcf8b1661495eb8ace6da2521a0885e43067 (commit)
via 1ebd0b7870956d8e206665a941877997fa16d70b (commit)
via 8d6bd774155061530c7c6cd0029332b0d35fe1ce (commit)
via d17da3a2be2c2649652b4dd0ae5d3d11ba9cc21b (commit)
via ada4354704ee0e4c0b088d57b4a04c219d7e8031 (commit)
via 22d124644f6fc0480295c4d02104687965afd24b (commit)
via 5ff46cb07dfa9f6b373ea9466d7675efa320d926 (commit)
via ce60aa86dcb9aaef23a30ec2ed2a42944245e7a7 (commit)
via 99ca75676abbf8360b807a1c36143d87f393c89a (commit)
via cc0fc600dfdc1f4652aa7ec5a7472db06c1cbd4d (commit)
via 79c9b75382ddd24872d3eb4b23a354c24cb04087 (commit)
via c3c9615f0ed155fc8c710897c985d17779c1c8e3 (commit)
via 734c918dd9a93c4d69e060bd6d81fa53967b0067 (commit)
via 981f0d20d284b83e4d5d624d0120954d341e865a (commit)
via 34802a0ac819bc875d6f980407e090320130ffe3 (commit)
via 4760ac62312e9b5bf24ba73d67b859e6cb3fd61d (commit)
via a58621d8f1799360443be8b542561bc3684e98df (commit)
via a435b27fe184749d643fa63d06be74aebb48aa2e (commit)
via 4f3dc5a36ac27e91e785eb94c9de1b4798477940 (commit)
via abb2b61c859ff5b584a1ec75203e2e4d6914457a (commit)
via 353cb225761b327e3b1b5ac8d35377f3014d9e06 (commit)
via f4548b2768162c8748665d190e288bc87327dde1 (commit)
via 2f88f1b4926783fd03871f434b88fbb3d801e590 (commit)
via aa1c94a54114e848c64771fde308fc9ac0c00fd0 (commit)
via 16750ef2a63764e6b21b034af8db60bf0b6a0051 (commit)
via ab8ff1eda4c0daaeaf06614651ecea31a38339ab (commit)
via 86baf211c8a9f1a5ad1498672728f4a15eaa311d (commit)
via 477969e3d2d399dd063760bb8133dbaddcd3d427 (commit)
via ff0a3090e743cc1aaee0c9a1ae48141f375e2aa1 (commit)
via 0e6c5afbe43ccc3b2478bd1f935255bd7d61906e (commit)
via 3327e16875dc619a549247cb4dee855be5d538d5 (commit)
via 1f0e3dc6f4de89e71282eb500a5841ff85693764 (commit)
via 13e9ea2f4647b3f0b953bce7562ad71f39b7472f (commit)
via 4665a8ff7f7d2667c4d586f7d54478edf683cfd9 (commit)
via f1fabbe371b1905707e19e564443327dbb94114f (commit)
via 3d817285c8b1ab5e95eef930ca2e426b41cc3bf6 (commit)
via 52dc56691fc1e5b5b9f9405e02627cc2791ea608 (commit)
via aed723721a6c4a1296dbc51553eacdff8e205d81 (commit)
via 15f0fc3541cc60700463d6f94b5116024144e5b0 (commit)
via 42b1f1e4c4f5aa48b7588233402876f5012c043c (commit)
via 3b47e007ac82dcc63c6f99b344211ca4661a8471 (commit)
via 040a4aec3997990a3da8061f20d714060716b8d6 (commit)
via f6e165a5736ffd3be849ffa2a6459fcf77dd5296 (commit)
via 7058dd169bfd2491a8735c006ed47ab6e6b59ce7 (commit)
via e19305ef4e339db5ac85f1a3c1ba98abee62dd32 (commit)
via 5dfa164e058d08ab2267a6bce4501c7acc293724 (commit)
via dcd06d51208c8b3cdb30ca89ae752244bd766c60 (commit)
via 9c9e6fb55c3245cbd6e17e372b635930b62e32c8 (commit)
via 56681a342d6775e1ca3b6106319af73296e790aa (commit)
via 31872754f36c840b4ec0b412a86afe9f38be86e0 (commit)
via 06b32bac47c7571281fa9bbcd88017c1b3fd0e48 (commit)
via 507aae916d2ad1c2d367953e333faf0d6e63480a (commit)
via 2d742ae954b869826da72d9c28cb088362839130 (commit)
via c44af5d2b81edb17e9fba450c1718a9dda630e3a (commit)
via b8dade8f46fa3fc0803a433cb4167327e8720d24 (commit)
via 6f07eddae79a4d7afae38bbd514054a26c8da5e1 (commit)
via 5c806a27410b36452daa82dace047579cf42d3c2 (commit)
via bbee45a49834b3e89a9afaf05acd98f1b6b3b76e (commit)
via c415976948d86aac316937f70a46192c4d079da6 (commit)
via 161d460bd035d7855f55d87b2d18f861d35c97a9 (commit)
via 0ba8bbabe09756a4627e80aacdbb5050407faaac (commit)
via 587c50528ab9c42467fc03327dfdbadd8933e360 (commit)
via e1938e5c1fe7e19553e52c611aecf921acc87822 (commit)
via cb81101a054cf68bcd374b218092c2da3c611999 (commit)
via 30c527eedf5ff9b38d20f81c795acfb049c7aed9 (commit)
via 41f5d870b1701e4efd738156ee18f61d9a5f6b8b (commit)
via 2002e7a128f2b4d84d0f67a45e8e0fec2bf541c3 (commit)
via f0a7b4ecbb0e6360ec2686bb08c3bb92682bc8c3 (commit)
via 84d5eda2a6ae0d737aef68d56023fc33fef623e6 (commit)
via 85f9295ccf26f69cd6e7e30a9022b57ea9e9efda (commit)
via 406df0fff274e7ca7ee8a7a13a98195c1073bafa (commit)
via 4d41258322f18a64225f7f9e86b1bb42f64f995f (commit)
via 15884428d46cc9d5c948318682dd883b02f99d46 (commit)
via 84de42ee09a14ef8d2a066815f99700e133dcc33 (commit)
via 1e762fa3653c39e649f1b8cf66f17c0bdd8bdfa7 (commit)
via 63eca991aef43dac723b6aec44345c7747b5939a (commit)
via 6f177a3d2719a0b08501d22cdd725ff5824bb688 (commit)
via 5410f9ce9d90585e00828253cedce1e024ad4524 (commit)
via 000f7efd1c2d316c3ee02902713cedc355efefba (commit)
via 7f9bc84501ca5a850e4975db715eae07234e7c25 (commit)
via f70ff164d003111eeb20a83f13073ab9a9a4a2b7 (commit)
via 73179cbc2030af0c6a6298874161386613e0b9f9 (commit)
via 2f26d781704618c6007ba896ad3d9e0c107d04b0 (commit)
via b26f6251fc5956a22bfe5a63066cd3a1545af32d (commit)
via c9505d8f35ffcd707421fd169704c1ac29aebcd0 (commit)
via 13685cc4580660eaf5b041b683a2d2f31fd24de3 (commit)
via 6d057a28a61986da08074f066740be9a39daf404 (commit)
via 564650bcec0f7ad4669ab29fbf6c3c5213692b02 (commit)
via 71611831f6d1aaaea09143d4837eddbd1d67fbf4 (commit)
via 5c9852647f78d37d6b3eca04daf88ed6a00f45bc (commit)
via 36adb91af2607cd54ed46d882ec622cbab006547 (commit)
via 43eabfa8ba1ebfcfdf2ad22e5425e03c9782c86f (commit)
via 629c9c5a4fd4c96be25003e44a6bc7e2341c51c8 (commit)
via 6b3bc78d60e4d8a430d8e9c2ad45c53930af2c7b (commit)
via 86b12667ad5a7512e6428ba8f44db1d7e8c86a47 (commit)
via 3bd69e9b4ab9be231f7c966fd62b95a4e1595901 (commit)
via e91931226acc70061e191d8e55e3f3c3ef77c02e (commit)
via bf772193aa23203d663612f65ffd0173bfff20f4 (commit)
via a08d702839d9df6cddefeccab1e7e657377145de (commit)
via fcd99b14aaf1b7e5938f1ff66d434eabfcd2b698 (commit)
via d71c2e5e68d17d99349936232277155b7db9f7eb (commit)
via c50566742ff9f7e75d79ae1ad707550be054c3c6 (commit)
via 5519de5b8255213341664ff91d1dc0695de32f6d (commit)
via 32bd410bb036fb31420c2424af685c621cf834be (commit)
via 1685d98e142c2b5915d19bf17f4029459a922321 (commit)
via 04f5b145f2e0ea8fc4ad30b7222198a91b3e5189 (commit)
via 9eedd903b78552493ea8fd55f89b8a58b94398d1 (commit)
via 99fe38e5d0b65cfe32b03ccbd61d5e174e093a4f (commit)
via a071815cc23931803825f6323d88ed1253ddfca7 (commit)
via 44b85098852c11243059319d4a75a124339e17d0 (commit)
via 45809d98ad9b62de6a0736d138547913d808a10e (commit)
via 8885b2d4e06fc897eee4efdace356c1bd29c771c (commit)
via 5bfc72950099a121838afff9fd2bf9e1230e5118 (commit)
via b00635aa7e78a7398dd00788a9f7bf19e261c9d6 (commit)
via b0c660bc1b3dc12f92125c55e633bde7048f2414 (commit)
via 5e0781ef76bd2912ea0f227192fa020041a4b7f4 (commit)
via fb631319f355d4807781090ac62f9db43a287485 (commit)
via 34564befb89523c4d2f1faca4b5891e07d0d132f (commit)
via 99f167be3a1849333e527046104b42c13ee83e0d (commit)
via 05755b228e2dee87eca0deb10a913bce60edf135 (commit)
via 17480c30c5f959e2ccda004819979bcd503bdc94 (commit)
via 74cd52de51b075db9cffac32b48fa8e614d4460e (commit)
via a681bf7867b1369801b6fcf1ac364b2fae612278 (commit)
via 3a4eb2476eb9f7569c293a70205a4d8226c817df (commit)
via 75b6a1057c947a2aa8d2103e6dfb3cee65823d0f (commit)
via dba236c915880a207b812f79b888729976847481 (commit)
via 43c5b414355bf16d31826aeb633ba68f68564848 (commit)
via 6c8f7475059c3b6d9794970a30aefd20d48468ef (commit)
via e3bd56e78491b018a3eb17f2baaa9fe0cf40919d (commit)
via f04b17998eb9024b4801ac96eda3113abcbde74a (commit)
via c45e92efcc01438ac68b523428ae03da43e168c3 (commit)
via 7c4c0514ede3cffc52d8c2874cdbdb74ced5f4ac (commit)
via 275c07332da84907a9a4c7743b089fa8c022fe36 (commit)
via 76ace0c46a5fe0e53a29dad093b817ad6c891f1b (commit)
via 38d7d64c7257ccb3cb7e29d76bfeeb64beda8cc0 (commit)
via da04dd3bbcae3bb61a0da5132edb7f58109eb823 (commit)
via 60a4f25645165d590b0130f810472418cdf3f5b4 (commit)
via bd923e85fd2f9bd6ad4dbfbbadae4fb43ff79210 (commit)
via 650d4636d56af1a6b5a329f4522357b914ea99e1 (commit)
via aa415e021bb1f9e0bb7449fbd9e2b52e84b69bd0 (commit)
via 4013083662dc7a9e6445ee7804dd6a0f5a21af01 (commit)
via 0ceb981bb577cb14d0df4c31ac94c44e94402e67 (commit)
via 4775ad43ab21203bc4802c8aac50e85979b97193 (commit)
via 9f52928a3543fb3cd39c26a8242e7e49c3fe3baf (commit)
via 61fced1a998995c899f9b02c56ae65116a4cb2a9 (commit)
via 63b15fbdec1de9f40ad9cc0d486f59fa104d16d4 (commit)
via d38e7638f9ecafc4e0db3a6b9dcad7a808c49781 (commit)
via f9cea380de68d0c181b4f80ad3df05cd53eb37e9 (commit)
via 93fb1303d8d4fbd974ecb2e7d74f3b02c7568a43 (commit)
via 380e6397302b7f7c238187ca3e6177740261ce67 (commit)
via 579f954850640b4dc7ad5de05b07eaee5175f19b (commit)
via 7bac6cc14920f87a4e15eb5bacc0fff6d43ceaf9 (commit)
via c75896605bb0224250ff5a460d4bb7de55e18751 (commit)
via 4289cec8c39d8ed8411c1b2718c5d8f63aa3f122 (commit)
via 8534089c1e8a7e3c48a49bf08784ebf05d11b3a1 (commit)
via 7f2ef4108043f94170a153174d06323fa2ce9653 (commit)
via 02a70f5541d4577f28114045496798c13e618944 (commit)
via 55196110a2db788d03ab469796d59273073755f6 (commit)
via 33210233e855588fa4c48450071f30a50af04e5f (commit)
via 3cb70f65383ac942c9e2b2d5d0478698d8732d67 (commit)
via 82665961681e3d488183c6babdb15f04ef5f8d52 (commit)
via 1422c7b03ba6d90ce0b8e0c6a02e6cbbe68c7a9e (commit)
via 05e5cf632d5d0cd66536e10719961ace30e1096e (commit)
via a2b5a1c891612415055cd2412a5fd5ee1d4422f9 (commit)
via 16b5a194f9e8767ab77b1c344f3fa37659f28c22 (commit)
via 7d109f38cbdff76b09ee2e587fe751c4b32737c6 (commit)
via 86faa358e17c487ea7b97970bf04ceabb90c0a1b (commit)
via fc5c7370d09294a8920da0780c615dcb77d7efd7 (commit)
via 362a47928d1c939a51d694ab81e68bf47ec564f9 (commit)
via 7bfadaf4d1e916aeddaf4cc17680fb0f71511b4e (commit)
via 4b77db4d8be806ebfb82ef5403fae266c56b1c89 (commit)
via 6db6928823e9f7626c3d49f4583f665d92e8ee95 (commit)
via 0022aaf1bd3bb9bb91bbf5542fdd962848776b59 (commit)
via 7417af2406fa1e11241b31dfe68a36b55f99146a (commit)
via 3a21271bd5b69fd828de3dd3691cc3628c54ae45 (commit)
via e0f5b885fc17cd10f6c5bc83f3bd81e500b5e039 (commit)
via ffdb4e63350c3e15a8a2193c178df11825912c77 (commit)
via ca04edb6565ec41f7549bef921c8fc2b4b78119f (commit)
via ffbbf037b83bef4ed8da16396f65caedd6df6379 (commit)
via 498b511dccc63bc88c0fad6a9ef0821ba8ad1cf6 (commit)
via aff22ac34cd7bc2b4e1c9cde1967d995cf2ce4c4 (commit)
via 4aef41d3641bb940f6e57aeeef9735255b11495f (commit)
via e29050c245ee56989b0249c55a1e5a0c05f9c2a1 (commit)
via 48da2e4e8966ecf8258e4e818f7aa5223e969abe (commit)
via d3f792e43cf9bacf108ad21ec58fa87d64422c7d (commit)
via 85ceb8b8c5fe2e586f512ade653dcd722330c24e (commit)
via f56a2d4422bfa6b6e6372926f15e4d776cef367e (commit)
via 8f422c21d3ea53c7f30bfd1548036331558bbf7d (commit)
via 1d34831c4d70162e769625278dd8c901ad21f42d (commit)
via dbf249256db006eef0111cff5dec4e644706ce54 (commit)
via c1b0ce8c0ac8b79a8d7663d52b5fd553ed828dce (commit)
via caf297138dfa09a73d9311d77ec6298a64152fdc (commit)
via 05265d4e8d24f358f7f55d62430f3592c3573b93 (commit)
via a5664487fa055249bf7afbe413be6a330e6e6b1c (commit)
via 3a932918fc3013f92db4b41fab03b359c20c8611 (commit)
via 1389ed4357607c17fb2ceb47731841b25e3e876d (commit)
via 659fb323f0b7fdd279cc1c36b0f5fe6526a0994e (commit)
via d631937eaeaa137fc1e832993b17d8e331b9b73c (commit)
via c1187e4ae7d409c94a73e6b10fa00751d302e05d (commit)
via e71ef05f8698763d2e604a55aa7e39ac824241d8 (commit)
via a99f8f2985db8650de25810cbb74cf5752c9e703 (commit)
via b3f844906409066e56efb339e0ff22e18d70386f (commit)
via 56148b90078653c55088d7a275a11b480dc32b22 (commit)
via ec47fdb14d1fd03d386147c1c6cf4afdd118c75f (commit)
via f95358d6ac95166841774c4ee48a060a40d363bb (commit)
via bb339dd52f357cf97a3bfc15d68650d9fe2b577d (commit)
via ba1265b9abd3b2e7cf8c819e4c411675fd2d131d (commit)
via fbc7aeaac7211076d70aae031b7b464280dedc72 (commit)
via 908182ebe5cf3a7d0f627cc4d83dcd2e38713acc (commit)
via 9fd55d4b5446d0c3d678b96ceea9750d2fc04ca5 (commit)
via d449fb5d31c800d96b1bea0c1c8d0184d6fb9fc0 (commit)
via 4976235a26688d12d2c2a51f13ef66dc38ff9ee9 (commit)
via c7aeb4bec30cf6373e77216058174d6d35850dde (commit)
via d58433297eb07de97a7713dab8fbedbe46b1be3c (commit)
via 18c3e8911a8e7c007fc95a33d449ca4e25d9d176 (commit)
via 5017a99844df876a3aa65f9a59f4fc8d7b5e8e3d (commit)
via 69aadccbe65a0ed2c4ed52d3ea2093b9b02bb9b5 (commit)
via fb50e3b350be1d916b085cb254a225e5143ecab1 (commit)
via 6271645f1897d26c1279512017d8343b655c5c17 (commit)
via b0f04bed614afdcc3353aa4a15dde9610ee1884e (commit)
via 96bb3a81d983daaf45ba92807d9a6597511192e1 (commit)
via 3ebfb01d35b7571e8aaea6f1b96ef7336b94feb7 (commit)
via 1538645f85c6b99be1d4d52ed2a1d4cbf80ecdd6 (commit)
via e1b65fae14a725bd7d162517750445bf03b3f179 (commit)
via 7fbc3fad3e55b3c24758be3204f1fc2c2abdfdab (commit)
via 782a17cac5214a4b5dae441b383b0504a40be9e9 (commit)
via 8ef57d769740b0e9774bd912c1c28afdc1e418e3 (commit)
via a87c4023df07597dcd3cd0f0b850088233e54462 (commit)
via 99b7eba1c65fbf8c6c0f71fd2f930fd480fd0b03 (commit)
via 660b35748eb0ed8efdc401639d3320854aea0ce5 (commit)
via 82ebca7dc6224c1f884ca1f528026b716f4e4b8f (commit)
via caeaa6012506f8ccbe2432fb1f049f6ae9709586 (commit)
via 14e0986a46342c74398bcac8618bace081b956d9 (commit)
via 93445f2e2a578c547adb2b9e5443468165636383 (commit)
from f9daba2bfb0b915cd17d4330d47c991c18652d8d (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 c1c4ea8d1b7629dac379f192d30cc79535214236
Author: Mukund Sivaraman <muks at isc.org>
Date: Wed Feb 19 19:18:41 2014 +0530
[2000] Stop using API that was removed
commit 6b2038265d47dd365b439051caa7c23b8e19309c
Merge: f9daba2 670577e
Author: Mukund Sivaraman <muks at isc.org>
Date: Wed Feb 19 19:15:57 2014 +0530
Merge branch 'master' into trac2000
-----------------------------------------------------------------------
Summary of changes:
ChangeLog | 131 ++++
doc/devel/contribute.dox | 4 +-
doc/devel/mainpage.dox | 8 +-
doc/guide/bind10-guide.xml | 323 ++++++++-
src/bin/auth/auth_messages.mes | 6 +-
src/bin/auth/auth_srv.cc | 39 +-
src/bin/auth/auth_srv.h | 2 +
src/bin/auth/query.cc | 25 +-
src/bin/auth/query.h | 12 +
src/bin/auth/tests/auth_srv_unittest.cc | 25 +-
src/bin/auth/tests/query_unittest.cc | 15 +
src/bin/bindctl/bindcmd.py | 5 +-
src/bin/d2/dns_client.cc | 2 +-
src/bin/d2/dns_client.h | 10 +-
src/bin/d2/nc_add.cc | 21 +-
src/bin/d2/tests/dns_client_unittests.cc | 18 +-
src/bin/d2/tests/nc_test_utils.cc | 29 +-
src/bin/d2/tests/nc_trans_unittests.cc | 2 +-
src/bin/ddns/ddns.py.in | 5 +-
src/bin/ddns/tests/ddns_test.py | 5 +-
src/bin/dhcp4/config_parser.cc | 20 +-
src/bin/dhcp4/ctrl_dhcp4_srv.cc | 27 +-
src/bin/dhcp4/ctrl_dhcp4_srv.h | 3 +-
src/bin/dhcp4/dhcp4.dox | 8 +-
src/bin/dhcp4/dhcp4.spec | 29 +-
src/bin/dhcp4/dhcp4_messages.mes | 70 +-
src/bin/dhcp4/dhcp4_srv.cc | 420 ++++++-----
src/bin/dhcp4/dhcp4_srv.h | 130 +++-
src/bin/dhcp4/tests/Makefile.am | 6 +-
src/bin/dhcp4/tests/callout_library_common.h | 7 +-
src/bin/dhcp4/tests/config_parser_unittest.cc | 460 ++++++++++--
src/bin/dhcp4/tests/d2_unittest.cc | 379 ++++++++++
src/bin/dhcp4/tests/d2_unittest.h | 117 +++
src/bin/dhcp4/tests/dhcp4_srv_unittest.cc | 434 ++++++++---
src/bin/dhcp4/tests/dhcp4_test_utils.cc | 85 +--
src/bin/dhcp4/tests/dhcp4_test_utils.h | 295 ++++----
src/bin/dhcp4/tests/direct_client_unittest.cc | 413 +++++++++++
src/bin/dhcp4/tests/fqdn_unittest.cc | 462 +++++++++---
src/bin/dhcp6/config_parser.cc | 19 +-
src/bin/dhcp6/ctrl_dhcp6_srv.cc | 11 +-
src/bin/dhcp6/ctrl_dhcp6_srv.h | 3 +-
src/bin/dhcp6/dhcp6.dox | 8 +-
src/bin/dhcp6/dhcp6.spec | 24 +
src/bin/dhcp6/dhcp6_messages.mes | 19 +-
src/bin/dhcp6/dhcp6_srv.cc | 360 +++++----
src/bin/dhcp6/dhcp6_srv.h | 117 +--
src/bin/dhcp6/tests/Makefile.am | 3 +-
src/bin/dhcp6/tests/callout_library_common.h | 7 +-
src/bin/dhcp6/tests/config_parser_unittest.cc | 459 ++++++++++--
src/bin/dhcp6/tests/dhcp6_srv_unittest.cc | 106 ++-
src/bin/dhcp6/tests/dhcp6_test_utils.cc | 3 +
src/bin/dhcp6/tests/dhcp6_test_utils.h | 263 ++++---
src/bin/dhcp6/tests/fqdn_unittest.cc | 262 +++++--
src/bin/dhcp6/tests/hooks_unittest.cc | 2 +
src/bin/dhcp6/tests/wireshark.cc | 2 +
src/bin/resolver/resolver.cc | 4 +-
src/bin/xfrin/tests/xfrin_test.py | 5 +-
src/bin/xfrin/xfrin.py.in | 3 +-
src/bin/xfrin/xfrin_messages.mes | 6 +-
src/bin/xfrout/xfrout.py.in | 13 +-
src/hooks/dhcp/user_chk/Makefile.am | 3 +-
src/hooks/dhcp/user_chk/user_chk.h | 5 +-
src/hooks/dhcp/user_chk/user_file.cc | 2 +-
src/hooks/dhcp/user_chk/user_file.h | 10 +-
src/hooks/dhcp/user_chk/user_registry.h | 8 +-
src/lib/acl/ip_check.cc | 2 +-
src/lib/asiodns/io_fetch.cc | 2 +-
src/lib/asiodns/tests/io_fetch_unittest.cc | 8 +-
src/lib/asiolink/dummy_io_cb.h | 32 +-
src/lib/asiolink/io_address.cc | 21 +-
src/lib/asiolink/io_address.h | 22 +-
src/lib/asiolink/tcp_socket.h | 2 +-
src/lib/asiolink/tests/Makefile.am | 1 +
...t_unittest.cc => dummy_io_callback_unittest.cc} | 26 +-
src/lib/asiolink/tests/io_address_unittest.cc | 35 +
src/lib/asiolink/tests/tcp_socket_unittest.cc | 8 +-
src/lib/asiolink/tests/udp_socket_unittest.cc | 2 +-
src/lib/cache/message_cache.h | 5 +-
src/lib/cache/message_entry.cc | 1 +
src/lib/cache/message_entry.h | 8 +-
src/lib/cache/rrset_cache.h | 2 -
src/lib/cache/rrset_entry.cc | 1 +
src/lib/cache/rrset_entry.h | 8 +-
src/lib/cache/tests/cache_test_messagefromfile.h | 8 +-
src/lib/cache/tests/cache_test_sectioncount.h | 8 +-
src/lib/cache/tests/message_cache_unittest.cc | 1 +
src/lib/cc/session.h | 2 +-
src/lib/cc/tests/data_unittests.cc | 12 +-
src/lib/config/config_data.h | 4 +-
src/lib/config/tests/testdata/spec22.spec | 2 +-
src/lib/config/tests/testdata/spec27.spec | 2 +-
src/lib/datasrc/cache_config.cc | 6 +-
src/lib/datasrc/cache_config.h | 2 +-
src/lib/datasrc/client_list.h | 2 +-
src/lib/datasrc/database.cc | 21 +-
src/lib/datasrc/memory/domaintree.h | 4 +
src/lib/datasrc/memory/memory_client.cc | 15 +-
src/lib/datasrc/memory/rdataset.h | 2 +-
src/lib/datasrc/memory/treenode_rrset.cc | 80 ++
src/lib/datasrc/memory/treenode_rrset.h | 9 +-
src/lib/datasrc/memory/zone_data_loader.cc | 2 +-
src/lib/datasrc/memory/zone_data_updater.h | 2 +-
src/lib/datasrc/memory/zone_finder.cc | 30 +-
src/lib/datasrc/memory/zone_table.h | 2 -
src/lib/datasrc/memory/zone_writer_local.h | 95 +++
src/lib/datasrc/sqlite3_accessor.cc | 1 +
src/lib/datasrc/tests/client_list_unittest.cc | 14 +-
src/lib/datasrc/tests/database_unittest.cc | 16 +-
src/lib/datasrc/tests/faked_nsec3.cc | 8 +
.../datasrc/tests/master_loader_callbacks_test.cc | 2 +-
.../datasrc/tests/memory/domaintree_unittest.cc | 2 +-
.../datasrc/tests/memory/memory_client_unittest.cc | 15 +-
.../tests/memory/treenode_rrset_unittest.cc | 32 +
src/lib/datasrc/tests/mock_client.cc | 4 +-
src/lib/datasrc/zone_finder.cc | 2 +-
src/lib/datasrc/zone_finder.h | 6 +-
src/lib/dhcp/Makefile.am | 1 +
src/lib/dhcp/classify.h | 70 ++
src/lib/dhcp/dhcp4.h | 6 +-
src/lib/dhcp/iface_mgr.cc | 245 +++----
src/lib/dhcp/iface_mgr.h | 76 +-
src/lib/dhcp/iface_mgr_bsd.cc | 25 +-
src/lib/dhcp/iface_mgr_error_handler.h | 52 ++
src/lib/dhcp/iface_mgr_linux.cc | 57 +-
src/lib/dhcp/iface_mgr_sun.cc | 25 +-
src/lib/dhcp/libdhcp++.cc | 8 +-
src/lib/dhcp/option.cc | 27 +-
src/lib/dhcp/option4_addrlst.cc | 2 +-
src/lib/dhcp/option4_client_fqdn.cc | 5 +-
src/lib/dhcp/option6_client_fqdn.cc | 5 +-
src/lib/dhcp/option6_ia.cc | 6 +-
src/lib/dhcp/option6_iaaddr.cc | 4 +-
src/lib/dhcp/option6_iaprefix.cc | 4 +-
src/lib/dhcp/option_data_types.h | 8 +-
src/lib/dhcp/option_definition.cc | 70 +-
src/lib/dhcp/option_definition.h | 33 +-
src/lib/dhcp/option_int.h | 6 +-
src/lib/dhcp/option_int_array.h | 6 +-
src/lib/dhcp/option_vendor.cc | 2 +-
src/lib/dhcp/pkt4.cc | 9 +-
src/lib/dhcp/pkt4.h | 12 +-
src/lib/dhcp/pkt6.h | 8 +-
src/lib/dhcp/std_option_defs.h | 10 +-
src/lib/dhcp/tests/Makefile.am | 20 +
src/lib/dhcp/tests/classify_unittest.cc | 52 ++
src/lib/dhcp/tests/iface_mgr_test_config.cc | 137 ++++
src/lib/dhcp/tests/iface_mgr_test_config.h | 241 +++++++
src/lib/dhcp/tests/iface_mgr_unittest.cc | 366 +++++++++-
src/lib/dhcp/tests/option4_client_fqdn_unittest.cc | 3 +-
src/lib/dhcp/tests/option6_client_fqdn_unittest.cc | 3 +-
src/lib/dhcp/tests/option_definition_unittest.cc | 111 ++-
.../tests/pkt_filter_test_stub.cc} | 40 +-
..._filter_test_utils.h => pkt_filter_test_stub.h} | 94 +--
src/lib/dhcp/tests/pkt_filter_test_utils.h | 6 +-
src/lib/dhcp_ddns/Makefile.am | 1 +
src/lib/dhcp_ddns/dhcp_ddns_messages.mes | 30 +-
src/lib/dhcp_ddns/ncr_io.cc | 109 ++-
src/lib/dhcp_ddns/ncr_io.h | 94 ++-
src/lib/dhcp_ddns/ncr_msg.h | 2 +-
src/lib/dhcp_ddns/ncr_udp.cc | 56 +-
src/lib/dhcp_ddns/ncr_udp.h | 28 +-
src/lib/dhcp_ddns/tests/Makefile.am | 2 +
src/lib/dhcp_ddns/tests/ncr_udp_unittests.cc | 360 ++++++++-
.../tests/test_utils.cc} | 41 +-
.../tests/test_utils.h} | 30 +-
src/lib/dhcp_ddns/tests/watch_socket_unittests.cc | 207 ++++++
src/lib/dhcp_ddns/watch_socket.cc | 152 ++++
src/lib/dhcp_ddns/watch_socket.h | 138 ++++
src/lib/dhcpsrv/Makefile.am | 3 +-
src/lib/dhcpsrv/alloc_engine.cc | 85 ++-
src/lib/dhcpsrv/alloc_engine.h | 35 +-
src/lib/dhcpsrv/cfgmgr.cc | 92 ++-
src/lib/dhcpsrv/cfgmgr.h | 60 +-
src/lib/dhcpsrv/{d2_client.cc => d2_client_cfg.cc} | 65 +-
src/lib/dhcpsrv/{d2_client.h => d2_client_cfg.h} | 82 +--
src/lib/dhcpsrv/d2_client_mgr.cc | 379 ++++++++++
src/lib/dhcpsrv/d2_client_mgr.h | 421 +++++++++++
src/lib/dhcpsrv/dbaccess_parser.h | 3 +-
src/lib/dhcpsrv/dhcp_parsers.cc | 108 ++-
src/lib/dhcpsrv/dhcp_parsers.h | 64 +-
src/lib/dhcpsrv/dhcpsrv_messages.mes | 59 +-
src/lib/dhcpsrv/lease.cc | 9 +-
src/lib/dhcpsrv/lease.h | 10 +-
src/lib/dhcpsrv/subnet.cc | 49 +-
src/lib/dhcpsrv/subnet.h | 119 ++-
src/lib/dhcpsrv/tests/Makefile.am | 5 +-
src/lib/dhcpsrv/tests/alloc_engine_unittest.cc | 138 +++-
src/lib/dhcpsrv/tests/cfgmgr_unittest.cc | 375 ++++++++--
src/lib/dhcpsrv/tests/d2_client_unittest.cc | 564 ++++++++++++++-
src/lib/dhcpsrv/tests/d2_udp_unittest.cc | 477 ++++++++++++
src/lib/dhcpsrv/tests/dhcp_parsers_unittest.cc | 107 ++-
src/lib/dhcpsrv/tests/lease_mgr_unittest.cc | 623 +---------------
src/lib/dhcpsrv/tests/lease_unittest.cc | 694 +++++++++++++++++-
src/lib/dhcpsrv/tests/subnet_unittest.cc | 175 ++++-
src/lib/dns/Makefile.am | 2 +-
src/lib/dns/exceptions.h | 29 +-
src/lib/dns/labelsequence.cc | 17 +-
src/lib/dns/master_lexer.h | 6 +-
src/lib/dns/master_loader.cc | 541 ++++++++++++--
src/lib/dns/message.cc | 21 +-
src/lib/dns/message.h | 56 +-
src/lib/dns/name.h | 11 +-
src/lib/dns/nsec3hash.cc | 52 +-
src/lib/dns/nsec3hash.h | 23 +-
src/lib/dns/python/message_python.cc | 8 +-
src/lib/dns/python/name_python.cc | 2 +-
src/lib/dns/python/name_python.h | 2 +-
src/lib/dns/python/pydnspp.cc | 70 +-
src/lib/dns/python/pydnspp_common.cc | 6 +-
src/lib/dns/python/pydnspp_common.h | 4 +
src/lib/dns/python/rdata_python.cc | 5 +-
src/lib/dns/python/rrset_python.cc | 17 +-
src/lib/dns/python/tests/Makefile.am | 1 +
src/lib/dns/python/tests/message_python_test.py | 7 +
src/lib/dns/python/tests/name_python_test.py | 12 +-
...rdata_python_test.py => pydnspp_python_test.py} | 24 +-
src/lib/dns/python/tests/rrset_python_test.py | 18 +
src/lib/dns/python/tsig_python.cc | 11 +-
src/lib/dns/python/tsig_python.h | 6 +-
src/lib/dns/rdata.cc | 142 ++--
src/lib/dns/rdata.h | 31 +-
src/lib/dns/rdata/generic/sshfp_44.cc | 30 +-
src/lib/dns/rdata/generic/sshfp_44.h | 4 +-
src/lib/dns/rrclass-placeholder.h | 10 +-
src/lib/dns/rrparamregistry.h | 10 +-
src/lib/dns/rrset.cc | 64 ++
src/lib/dns/rrset.h | 58 +-
src/lib/dns/rrttl.cc | 44 +-
src/lib/dns/rrttl.h | 10 +-
src/lib/dns/rrtype-placeholder.h | 10 +-
src/lib/dns/tests/Makefile.am | 1 +
src/lib/dns/tests/dns_exceptions_unittest.cc | 69 ++
src/lib/dns/tests/edns_unittest.cc | 28 +-
src/lib/dns/tests/labelsequence_unittest.cc | 10 +-
src/lib/dns/tests/master_loader_unittest.cc | 496 ++++++++++++-
src/lib/dns/tests/message_unittest.cc | 73 +-
src/lib/dns/tests/messagerenderer_unittest.cc | 55 +-
src/lib/dns/tests/name_unittest.cc | 78 +-
src/lib/dns/tests/nsec3hash_unittest.cc | 33 +-
src/lib/dns/tests/question_unittest.cc | 12 +-
src/lib/dns/tests/rdata_afsdb_unittest.cc | 24 +-
src/lib/dns/tests/rdata_cname_unittest.cc | 20 +-
src/lib/dns/tests/rdata_dhcid_unittest.cc | 12 +-
src/lib/dns/tests/rdata_dname_unittest.cc | 20 +-
src/lib/dns/tests/rdata_dnskey_unittest.cc | 15 +-
src/lib/dns/tests/rdata_ds_like_unittest.cc | 12 +-
src/lib/dns/tests/rdata_hinfo_unittest.cc | 15 +-
src/lib/dns/tests/rdata_in_a_unittest.cc | 14 +-
src/lib/dns/tests/rdata_in_aaaa_unittest.cc | 14 +-
src/lib/dns/tests/rdata_minfo_unittest.cc | 25 +-
src/lib/dns/tests/rdata_mx_unittest.cc | 12 +-
src/lib/dns/tests/rdata_naptr_unittest.cc | 15 +-
src/lib/dns/tests/rdata_ns_unittest.cc | 20 +-
.../dns/tests/rdata_nsec3param_like_unittest.cc | 9 +-
src/lib/dns/tests/rdata_nsec3param_unittest.cc | 15 +-
src/lib/dns/tests/rdata_nsec_unittest.cc | 10 +-
src/lib/dns/tests/rdata_nsecbitmap_unittest.cc | 15 +-
src/lib/dns/tests/rdata_opt_unittest.cc | 22 +-
src/lib/dns/tests/rdata_ptr_unittest.cc | 20 +-
src/lib/dns/tests/rdata_rp_unittest.cc | 14 +-
src/lib/dns/tests/rdata_rrsig_unittest.cc | 51 +-
src/lib/dns/tests/rdata_soa_unittest.cc | 16 +-
src/lib/dns/tests/rdata_srv_unittest.cc | 26 +-
src/lib/dns/tests/rdata_sshfp_unittest.cc | 55 +-
src/lib/dns/tests/rdata_tsig_unittest.cc | 49 +-
src/lib/dns/tests/rdata_txt_like_unittest.cc | 58 +-
src/lib/dns/tests/rdata_unittest.cc | 29 +-
src/lib/dns/tests/rdata_unittest.h | 4 +-
src/lib/dns/tests/rdatafields_unittest.cc | 51 +-
src/lib/dns/tests/rrclass_unittest.cc | 12 +-
src/lib/dns/tests/rrset_unittest.cc | 116 ++-
src/lib/dns/tests/rrttl_unittest.cc | 12 +-
src/lib/dns/tests/rrtype_unittest.cc | 12 +-
src/lib/dns/tests/tsig_unittest.cc | 15 +-
src/lib/dns/tests/tsigkey_unittest.cc | 30 +-
src/lib/dns/tests/tsigrecord_unittest.cc | 17 +-
src/lib/dns/tests/unittest_util.cc | 27 -
src/lib/dns/tests/unittest_util.h | 22 -
src/lib/dns/tsig.cc | 30 +-
src/lib/dns/tsigkey.cc | 2 +-
src/lib/hooks/tests/Makefile.am | 24 +-
src/lib/log/Makefile.am | 2 +-
src/lib/log/README | 529 --------------
src/lib/log/buffer_appender_impl.cc | 1 +
src/lib/log/interprocess/interprocess_sync_file.h | 2 +-
src/lib/log/interprocess/interprocess_sync_null.h | 2 +-
src/lib/log/log_formatter.h | 8 +-
src/lib/log/logging.dox | 762 ++++++++++++++++++++
src/lib/log/message_dictionary.cc | 10 +-
src/lib/log/tests/logger_example.cc | 2 +-
src/lib/nsas/tests/hash_unittest.cc | 1 +
.../tests/nameserver_address_store_unittest.cc | 2 +
src/lib/nsas/tests/nameserver_address_unittest.cc | 7 +-
src/lib/nsas/tests/nameserver_entry_unittest.cc | 2 +-
src/lib/nsas/tests/nsas_entry_compare_unittest.cc | 1 +
src/lib/nsas/tests/nsas_test.h | 124 ++--
src/lib/nsas/tests/zone_entry_unittest.cc | 4 +-
src/lib/python/isc/cc/tests/session_test.py | 8 +-
src/lib/python/isc/config/config_data.py | 40 +-
.../python/isc/config/tests/config_data_test.py | 2 +-
src/lib/resolve/recursive_query.cc | 1 +
src/lib/resolve/tests/recursive_query_unittest.cc | 13 +-
.../resolve/tests/recursive_query_unittest_2.cc | 3 +-
.../resolve/tests/recursive_query_unittest_3.cc | 3 +-
src/lib/testutils/srv_test.cc | 7 +-
src/lib/util/io_utilities.h | 37 +-
src/lib/util/tests/io_utilities_unittest.cc | 29 +-
tests/lettuce/README | 4 +-
tests/lettuce/configurations/.gitignore | 3 +
.../configurations/DO_NOT_USE_127.0.0.1:47807 | 6 -
.../configurations/auth/auth_badzone.config.orig | 2 +-
.../configurations/auth/auth_basic.config.orig | 2 +-
.../configurations/bindctl/bindctl.config.orig | 2 +-
.../configurations/bindctl_commands.config.orig | 2 +-
tests/lettuce/configurations/ddns/ddns.config.orig | 2 +-
.../lettuce/configurations/ddns/noddns.config.orig | 2 +-
tests/lettuce/configurations/default_user.csv | 1 +
.../lettuce/configurations/example.org.config.orig | 2 +-
.../configurations/example.org.inmem.config | 2 +-
tests/lettuce/configurations/example2.org.config | 2 +-
.../{glue.config => generate.config.orig} | 7 +-
tests/lettuce/configurations/glue.config | 2 +-
.../inmemory_over_sqlite3/secondary.conf | 2 +-
.../configurations/ixfr-out/testset1-config.db | 4 +-
.../multi_instance/multi_auth.config.orig | 2 +-
tests/lettuce/configurations/no_db_file.config | 2 +-
.../lettuce/configurations/nsec3/nsec3_auth.config | 2 +-
.../resolver/resolver_basic.config.orig | 2 +-
.../{example.org.config.orig => root.config.orig} | 11 +-
...example.org.inmem.config => static.config.orig} | 11 +-
.../lettuce/configurations/xfrin/inmem_slave.conf | 2 +-
.../xfrin/retransfer_master.conf.orig | 4 +-
.../xfrin/retransfer_master_diffs.conf | 4 +-
.../xfrin/retransfer_master_nons.conf.orig | 4 +-
.../xfrin/retransfer_master_v4.conf.orig | 4 +-
.../xfrin/retransfer_slave.conf.orig | 2 +-
.../xfrin/retransfer_slave_diffs.conf | 4 +-
.../xfrin/retransfer_slave_notify.conf.orig | 4 +-
.../xfrin/retransfer_slave_notify_v4.conf | 4 +-
tests/lettuce/configurations/xfrout_master.conf | 2 +-
tests/lettuce/data/.gitignore | 1 +
tests/lettuce/data/generate.zone | 4 +
.../lettuce/data/root.sqlite3.orig | Bin 22528 -> 22528 bytes
.../testdata => tests/lettuce/data}/static.zone | 0
tests/lettuce/features/ddns_system.feature | 6 +-
tests/lettuce/features/example.feature | 18 +-
.../lettuce/features/inmemory_over_sqlite3.feature | 12 +-
tests/lettuce/features/master_loader.feature | 50 ++
tests/lettuce/features/queries.feature | 80 ++
tests/lettuce/features/terrain/bind10_control.py | 19 +-
tests/lettuce/features/terrain/nsupdate.py | 2 +-
tests/lettuce/features/terrain/querying.py | 10 +-
tests/lettuce/features/terrain/terrain.py | 11 +-
tests/lettuce/features/terrain/transfer.py | 14 +-
tests/lettuce/features/xfrin_bind10.feature | 36 +-
.../lettuce/features/xfrin_notify_handling.feature | 172 ++---
tests/lettuce/features/xfrout_bind10.feature | 2 +-
tests/tools/perfdhcp/test_control.cc | 16 +-
tests/tools/perfdhcp/test_control.h | 7 +-
359 files changed, 15108 insertions(+), 4549 deletions(-)
create mode 100644 src/bin/dhcp4/tests/d2_unittest.cc
create mode 100644 src/bin/dhcp4/tests/d2_unittest.h
create mode 100644 src/bin/dhcp4/tests/direct_client_unittest.cc
copy src/lib/asiolink/tests/{io_socket_unittest.cc => dummy_io_callback_unittest.cc} (62%)
create mode 100644 src/lib/datasrc/memory/zone_writer_local.h
create mode 100644 src/lib/dhcp/classify.h
create mode 100644 src/lib/dhcp/iface_mgr_error_handler.h
create mode 100644 src/lib/dhcp/tests/classify_unittest.cc
create mode 100644 src/lib/dhcp/tests/iface_mgr_test_config.cc
create mode 100644 src/lib/dhcp/tests/iface_mgr_test_config.h
copy src/lib/{dhcpsrv/tests/test_get_callout_handle.cc => dhcp/tests/pkt_filter_test_stub.cc} (53%)
copy src/lib/dhcp/tests/{pkt_filter_test_utils.h => pkt_filter_test_stub.h} (52%)
copy src/lib/{util/unittests/interprocess_util.cc => dhcp_ddns/tests/test_utils.cc} (59%)
copy src/lib/{dns/python/pydnspp_config.h.in => dhcp_ddns/tests/test_utils.h} (60%)
create mode 100644 src/lib/dhcp_ddns/tests/watch_socket_unittests.cc
create mode 100644 src/lib/dhcp_ddns/watch_socket.cc
create mode 100644 src/lib/dhcp_ddns/watch_socket.h
rename src/lib/dhcpsrv/{d2_client.cc => d2_client_cfg.cc} (73%)
rename src/lib/dhcpsrv/{d2_client.h => d2_client_cfg.h} (75%)
create mode 100644 src/lib/dhcpsrv/d2_client_mgr.cc
create mode 100644 src/lib/dhcpsrv/d2_client_mgr.h
create mode 100644 src/lib/dhcpsrv/tests/d2_udp_unittest.cc
copy src/lib/dns/python/tests/{tsig_rdata_python_test.py => pydnspp_python_test.py} (61%)
create mode 100644 src/lib/dns/tests/dns_exceptions_unittest.cc
delete mode 100644 src/lib/log/README
create mode 100644 src/lib/log/logging.dox
delete mode 100644 tests/lettuce/configurations/DO_NOT_USE_127.0.0.1:47807
create mode 100644 tests/lettuce/configurations/default_user.csv
copy tests/lettuce/configurations/{glue.config => generate.config.orig} (78%)
copy tests/lettuce/configurations/{example.org.config.orig => root.config.orig} (69%)
copy tests/lettuce/configurations/{example.org.inmem.config => static.config.orig} (75%)
create mode 100644 tests/lettuce/data/generate.zone
copy src/lib/datasrc/tests/testdata/test-root.sqlite3 => tests/lettuce/data/root.sqlite3.orig (100%)
copy {src/lib/datasrc/tests/testdata => tests/lettuce/data}/static.zone (100%)
create mode 100644 tests/lettuce/features/master_loader.feature
-----------------------------------------------------------------------
diff --git a/ChangeLog b/ChangeLog
index 83e6f3f..78ea162 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,134 @@
+753. [func] muks
+ libdns++: the unknown/generic (RFC 3597) RDATA class now uses the
+ generic lexer in constructors from text.
+ (Trac #2426, git 0770d2df84e5608371db3a47e0456eb2a340b5f4)
+
+752. [func] tmark
+ If configured to do so, b10-dhcp4 will now create DHCP-DDNS update
+ requests and send them to b10-dhcp-ddns for processing.
+ (Trac# 3329, git 4546dd186782eec5cfcb4ddb61b0a3aa5c700751)
+
+751. [func] muks
+ The BIND 10 zone loader now supports the $GENERATE directive (a
+ BIND 9 extension).
+ (Trac #2430, git b05064f681231fe7f8571253c5786f4ff0f2ca03)
+
+750. [func] tomek
+ b10-dhcp4, b10-dhcp6: Simple client classification has been
+ implemented. Incoming packets can be assigned to zero or more
+ client classes. It is possible to restrict subnet usage to a given
+ client class. User's Guide and Developer's Guide has been updated.
+ (Trac #3274, git 1791d19899b92a6ee411199f664bdfc690ec08b2)
+
+749. [bug] tmark
+ b10-dhcp-ddns now sets the TTL value in RRs that add A, AAAA, or PTR DNS
+ entries to the lease length provided in instigating NameChangeRequest.
+ This corrected a bug in which the TTL was always set to 0.
+ (Trac# 3299, git dbacf27ece77f3d857da793341c6bd31ef1ea239)
+
+748. [bug] marcin
+ b10-dhcp4 server picks a subnet, to assign address for a directly
+ connected client, using IP address of the interface on which the
+ client's message has been received. If the message is received on
+ the interface for which there is no suitable subnet, the message
+ is discarded. Also, the subnet for renewing client which unicasts
+ its request, is selected using ciaddr.
+ (Trac #3242, git 9e571cc217d6b1a2fd6fdae1565fcc6fde6d08b1)
+
+747. [bug] marcin
+ libdhcpsrv: server configuration mechanism allows creating definitions
+ for standard options for which Kea doesn't provide a definition yet.
+ Without this, the server administrator couldn't configure options for
+ which a definition didn't exist.
+ (Trac# 3309, git 16a6ed6e48a6a950670c4874a2e81b1faf287d99)
+
+746. [func] tomek
+ IOAddress no longer exposes underlying asio objects. The getAddress()
+ method has been removed and replaced with several convenience methods.
+ (Trac #1485, git ecdb62db16b3f3d447db4a9d2a4079d5260431f0)
+
+745. [bug] muks
+ b10-auth now returns rcode=REFUSED for all questions with
+ qtype=RRSIG (i.e., where RRSIGs are queried directly). This is
+ because RRSIGs are meaningless without being bundled alongside the
+ RRs they cover.
+ (Trac #2226, git 68d24e65c9c3dfee38adfbe1c93367b0083f9a58)
+
+744. [func] marcin
+ b10-dhcp6: Refactored the code which is processing Client FQDN option.
+ The major user-visible change is that server generates DDNS
+ NameChangeRequest for the first IPv6 address (instead of all)
+ acquired by a client. Also, the server generates fully qualified domain
+ name from acquired IPv6 address, if the client sends an empty name in
+ Client FQDN option.
+ (Trac# 3295, git aa1c94a54114e848c64771fde308fc9ac0c00fd0)
+
+743. [func] tmark
+ b10-dhcp4 now responds with changes in DDNS behavior based upon
+ configuration parameters specified through its dhcp-ddns configuration
+ element. The parameters now supported are override-no-update,
+ override-client-update, replace-client-name, generated-prefix, and
+ qualifying-suffix.
+ (Trac# 3282, git 42b1f1e4c4f5aa48b7588233402876f5012c043c)
+
+742. [func] muks
+ The authoritative server now includes the datasource configuration
+ when logging some errors with the
+ AUTH_DATASRC_CLIENTS_BUILDER_RECONFIGURE_ERROR message ID.
+ (Trac #2756, git 31872754f36c840b4ec0b412a86afe9f38be86e0)
+
+741. [bug] shane
+ Remove hard-coded (and unnecessary) TSIG key from error message.
+ This also prevents a crash if the TSIG name is missing.
+ (Trac #3099, git 0ba8bbabe09756a4627e80aacdbb5050407faaac)
+
+740. [func] muks
+ When displaying messages about mismatched configuration data types
+ in entered values (between the supplied value type and expected
+ schema type), bindctl now includes both the supplied and expected
+ configuration data types in the returned error. The user has more
+ information on what caused the error now.
+ (Trac #3239, git 84d5eda2a6ae0d737aef68d56023fc33fef623e6)
+
+739. [bug] muks
+ Various minor updates were made to the SSHFP RDATA parser. Mainly,
+ the SSHFP constructor no longer throws an isc::BadValue exception.
+ generic::SSHFP::getFingerprintLen() was also renamed to
+ getFingerprintLength().
+ (Trac #3287, git 2f26d781704618c6007ba896ad3d9e0c107d04b0)
+
+738. [bug] muks
+ b10-auth now correctly processes NXDOMAIN results in the root zone
+ when using a SQLite3 data source.
+ (Trac #2951, git 13685cc4580660eaf5b041b683a2d2f31fd24de3)
+
+737. [func] muks
+ b10-auth now additionally logs the source address and port when
+ DNS messages with unsupported opcodes are received.
+ (Trac #1516, git 71611831f6d1aaaea09143d4837eddbd1d67fbf4)
+
+736. [bug] wlodek
+ b10-dhcp6 is now capable to determine if a received
+ message is addressed to it, using server identifier option.
+ The messages with non-matching server identifier are dropped.
+ (Trac #2892, git 3bd69e9b4ab9be231f7c966fd62b95a4e1595901)
+
+735. [doc] stephen
+ Expanded Developer's Guide to include chapter on logging.
+ (Trac #2566, git a08d702839d9df6cddefeccab1e7e657377145de)
+
+734. [bug] marcin
+ libdhcp++: fixed a bug which caused an error when setting boolean
+ values for an option. Also, bind10-guide has been updated with the
+ examples how to set the boolean values for an option.
+ (Trac# 3292, git 7c4c0514ede3cffc52d8c2874cdbdb74ced5f4ac)
+
+733. [bug] marcin
+ libdhcp++: a function which opens IPv6/UDPv6 sockets for the
+ DHCPv6 server, gracefully handles errors to bind socket to
+ a multicast address.
+ (Trac #3288, git 76ace0c46a5fe0e53a29dad093b817ad6c891f1b)
+
732. [func] tomek
b10-dhcp4, b10-dhcp6: Support for simplified client classification
added. Incoming packets are now assigned to a client class based on
diff --git a/doc/devel/contribute.dox b/doc/devel/contribute.dox
index 9103bed..f0ff1a6 100644
--- a/doc/devel/contribute.dox
+++ b/doc/devel/contribute.dox
@@ -109,7 +109,9 @@ make distcheck
There are other useful switches which can be passed to configure. It is
always a good idea to use \c --enable-logger-checks, which does sanity
-checks on logger parameters. If you happen to modify anything in the
+checks on logger parameters. Use \c --enable-debug to enable various
+additional consistency checks that reduce performance but help during
+development. If you happen to modify anything in the
documentation, use \c --enable-generate-docs. If you are modifying DHCP
code, you are likely to be interested in enabling the MySQL backend for
DHCP. Note that if the backend is not enabled, MySQL specific unit-tests
diff --git a/doc/devel/mainpage.dox b/doc/devel/mainpage.dox
index f9c5d61..f31e7bf 100644
--- a/doc/devel/mainpage.dox
+++ b/doc/devel/mainpage.dox
@@ -86,10 +86,10 @@
* - @subpage libdhcp_ddns
*
* @section miscellaneousTopics Miscellaneous Topics
- * - @subpage LoggingApi
- * - @subpage LoggingApiOverview
- * - @subpage LoggingApiLoggerNames
- * - @subpage LoggingApiLoggingMessages
+ * - @subpage logBind10Logging
+ * - @subpage logBasicIdeas
+ * - @subpage logDeveloperUse
+ * - @subpage logNotes
* - @subpage SocketSessionUtility
* - <a href="./doxygen-error.log">Documentation warnings and errors</a>
*
diff --git a/doc/guide/bind10-guide.xml b/doc/guide/bind10-guide.xml
index 1be98f9..b74a1e2 100644
--- a/doc/guide/bind10-guide.xml
+++ b/doc/guide/bind10-guide.xml
@@ -7,7 +7,7 @@
]>
<!--
- - Copyright (C) 2010-2013 Internet Systems Consortium, Inc. ("ISC")
+ - Copyright (C) 2010-2014 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
@@ -39,7 +39,7 @@
servers with development 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.
+ and DHCPv6 servers (codenamed Kea).
</para>
<para>
This is the reference guide for BIND 10 version &__VERSION__;.
@@ -1554,7 +1554,10 @@ Parameters:
<term>integer</term>
<listitem>
<simpara>
- A basic integer; can be set directly with <command>config set</command>, to any integer value.
+ A basic integer; can be set directly with
+ <command>config set</command>, to any integer
+ value. The value must not be quoted, or else, it
+ will be parsed as a string.
</simpara>
</listitem>
</varlistentry>
@@ -1562,7 +1565,10 @@ Parameters:
<term>real</term>
<listitem>
<simpara>
- A basic floating point number; can be set directly with <command>config set</command>, to any floating point value.
+ A basic floating point number; can be set
+ directly with <command>config set</command>, to
+ any floating point value. The value must not be
+ quoted, or else, it will be parsed as a string.
</simpara>
</listitem>
</varlistentry>
@@ -1570,7 +1576,12 @@ Parameters:
<term>boolean</term>
<listitem>
<simpara>
- A basic boolean value; can be set directly with <command>config set</command>, to either <command>true</command> or <command>false</command>.
+ A basic boolean value; can be set directly with
+ <command>config set</command>, to either
+ <command>true</command> or
+ <command>false</command>. The value must not be
+ quoted, or else, it will be parsed as a
+ string. Integer values are not allowed.
</simpara>
</listitem>
</varlistentry>
@@ -1578,7 +1589,9 @@ Parameters:
<term>string</term>
<listitem>
<simpara>
- A basic string value; can be set directly with <command>config set,</command> so any string. Double quotation marks are optional.
+ A basic string value; can be set directly with
+ <command>config set</command> to any
+ string. Double quotation marks are optional.
</simpara>
</listitem>
</varlistentry>
@@ -3523,7 +3536,9 @@ then change those defaults with config set Resolver/forward_addresses[0]/address
clients. Even though principles of both DHCPv4 and DHCPv6 are
somewhat similar, these are two radically different
protocols. BIND 10 offers two server implementations, one for DHCPv4
- and one for DHCPv6.</para>
+ and one for DHCPv6. The DHCP part of the BIND 10 project is codenamed
+ Kea. The DHCPv4 component is colloquially referred to as Kea4 and its
+ DHCPv6 counterpart is called Kea6.</para>
<para>This chapter covers those parts of BIND 10 that are common to
both servers. DHCPv4-specific details are covered in <xref linkend="dhcp4"/>,
while those details specific to DHCPv6 are described in <xref linkend="dhcp6"/>
@@ -3633,6 +3648,14 @@ $</screen>
> <userinput>config commit</userinput>
</screen>
</para>
+ <para>
+ Note that the server was only removed from the list, so BIND10 will not
+ restart it, but the server itself is still running. Hence it is usually
+ desired to stop it:
+<screen>
+> <userinput>Dhcp4 shutdown</userinput>
+</screen>
+ </para>
<para>
On start-up, the server will detect available network interfaces
@@ -3803,7 +3826,7 @@ Dhcp4/subnet4 [] list (default)
</section>
<section id="dhcp4-address-config">
- <title>Configuration of Address Pools</title>
+ <title>Configuration of IPv4 Address Pools</title>
<para>
The essential role of DHCPv4 server is address assignment. The server
has to be configured with at least one subnet and one pool of dynamic
@@ -3928,7 +3951,7 @@ Dhcp4/subnet4 [] list (default)
<para>
The following commands override the global
DNS servers option for a particular subnet, setting a single DNS
- server with address 2001:db8:1::3.
+ server with address 192.0.2.3.
<screen>
> <userinput>config add Dhcp4/subnet4[0]/option-data</userinput>
> <userinput>config set Dhcp4/subnet4[0]/option-data[0]/name "domain-name-servers"</userinput>
@@ -3963,6 +3986,20 @@ Dhcp4/subnet4 [] list (default)
<!-- @todo: describe record types -->
<para>
+ The <xref linkend="dhcp4-custom-options"/> describes the configuration
+ syntax to create custom option definitions (formats). It is generally not
+ allowed to create custom definitions for standard options, even if the
+ definition being created matches the actual option format defined in the
+ RFCs. There is an exception from this rule for standard options for which
+ Kea does not provide a definition yet. In order to use such options,
+ a server administrator must create a definition as described in
+ <xref linkend="dhcp4-custom-options"/> in the 'dhcp4' option space. This
+ definition should match the option format described in the relevant
+ RFC but configuration mechanism would allow any option format as it has
+ no means to validate it at the moment.
+ </para>
+
+ <para>
<table frame="all" id="dhcp4-std-options-list">
<title>List of standard DHCPv4 options</title>
<tgroup cols='4'>
@@ -4172,10 +4209,10 @@ Dhcp4/subnet4 [] list (default)
primitives (uint8, string, ipv4-address etc): it is possible to
define an option comprising a number of existing primitives.
</para>
- <para>Assume we
- want to define a new option that will consist of an IPv4
- address, followed by unsigned 16 bit integer, followed by a text
- string. Such an option could be defined in the following way:
+ <para>Assume we want to define a new option that will consist of
+ an IPv4 address, followed by unsigned 16 bit integer, followed by
+ a boolean value, followed by a text string. Such an option could
+ be defined in the following way:
<screen>
> <userinput>config add Dhcp4/option-def</userinput>
> <userinput>config set Dhcp4/option-def[0]/name "bar"</userinput>
@@ -4183,7 +4220,7 @@ Dhcp4/subnet4 [] list (default)
> <userinput>config set Dhcp4/option-def[0]/space "dhcp4"</userinput>
> <userinput>config set Dhcp4/option-def[0]/type "record"</userinput>
> <userinput>config set Dhcp4/option-def[0]/array false</userinput>
-> <userinput>config set Dhcp4/option-def[0]/record-types "ipv4-address, uint16, string"</userinput>
+> <userinput>config set Dhcp4/option-def[0]/record-types "ipv4-address, uint16, boolean, string"</userinput>
> <userinput>config set Dhcp4/option-def[0]/encapsulate ""</userinput>
</screen>
The "type" is set to "record" to indicate that the option contains
@@ -4198,12 +4235,23 @@ Dhcp4/subnet4 [] list (default)
> <userinput>config set Dhcp4/option-data[0]/space "dhcp4"</userinput>
> <userinput>config set Dhcp4/option-data[0]/code 223</userinput>
> <userinput>config set Dhcp4/option-data[0]/csv-format true</userinput>
-> <userinput>config set Dhcp4/option-data[0]/data "192.0.2.100, 123, Hello World"</userinput>
+> <userinput>config set Dhcp4/option-data[0]/data "192.0.2.100, 123, true, Hello World"</userinput>
> <userinput>config commit</userinput></screen>
- </para>
"csv-format" is set "true" to indicate that the "data" field comprises a command-separated
list of values. The values in the "data" must correspond to the types set in
the "record-types" field of the option definition.
+ </para>
+ <note>
+ <para>
+ It is recommended that boolean values are specified using "true" and "false"
+ strings. This helps to prevent errors when typing multiple comma separated
+ values, as it make it easier to identify the type of the value being typed,
+ and compare it with the order of data fields. Nevetheless, it is possible
+ to use integer values: "1" and "0", instead of "true" and "false"
+ accordingly. If other integer value is specified, the configuration is
+ rejected.
+ </para>
+ </note>
</section>
<section id="dhcp4-vendor-opts">
@@ -4372,7 +4420,87 @@ Dhcp4/subnet4 [] list (default)
e.g. "123" - would then be assigned to the uint16 field in the "container" option.
</para>
</section>
- </section>
+
+ <section id="dhcp4-client-classifier">
+ <title>Client Classification in DHCPv4</title>
+ <note>
+ <para>
+ DHCPv4 server has been extended to support limited client classification.
+ Although the current capability is modest, it is expected to be expanded
+ in the future. It is envisaged that the majority of client classification
+ extensions will be using hooks extensions.
+ </para>
+ </note>
+ <para>In certain cases it is useful to differentiate between different types
+ of clients and treat them differently. The process of doing classification
+ is conducted in two steps. The first step is to assess incoming packet and
+ assign it to zero or more classes. This classification is currently simple,
+ but is expected to grow in capability soon. Currently the server checks whether
+ incoming packet has vendor class identifier option (60). If it has, content
+ of that option is interpreted as a class. For example, modern cable modems
+ will send this option with value "docsis3.0" and as a result the
+ packet will belong to class "docsis3.0".
+ </para>
+
+ <para>It is envisaged that the client classification will be used for changing
+ behavior of almost any part of the DHCP message processing, including assigning
+ leases from different pools, assigning different option (or different values of
+ the same options) etc. For now, there are only two mechanisms that are taking
+ advantage of client classification: specific processing for cable modems and
+ subnet selection.</para>
+
+ <para>
+ For clients that belong to the docsis3.0 class, the siaddr field is set to
+ the value of next-server (if specified in a subnet). If there is
+ boot-file-name option specified, its value is also set in the file field
+ in the DHCPv4 packet. For eRouter1.0 class, the siaddr is always set to
+ 0.0.0.0. That capability is expected to be moved to external hook
+ library that will be dedicated to cable modems.
+ </para>
+
+ <para>
+ Kea can be instructed to limit access to given subnets based on class information.
+ This is particularly useful for cases where two types of devices share the
+ same link and are expected to be served from two different subnets. The
+ primary use case for such a scenario is cable networks. There are two
+ classes of devices: cable modem itself, which should be handled a lease
+ from subnet A and all other devices behind modems that should get a lease
+ from subnet B. That segregation is essential to prevent overly curious
+ users from playing with their cable modems. For details on how to set up
+ class restrictions on subnets, see <xref linkend="dhcp4-subnet-class"/>.
+ </para>
+
+ </section>
+
+ <section id="dhcp4-subnet-class">
+ <title>Limiting access to IPv4 subnet to certain classes</title>
+ <para>
+ In certain cases it beneficial to restrict access to certain subnets
+ only to clients that belong to a given subnet. For details on client
+ classes, see <xref linkend="dhcp4-client-classifier"/>. This is an
+ extension of a previous example from <xref linkend="dhcp4-address-config"/>.
+ Let's assume that the server is connected to a network segment that uses
+ the 192.0.2.0/24 prefix. The Administrator of that network has decided
+ that addresses from range 192.0.2.10 to 192.0.2.20 are going to be
+ managed by the Dhcp4 server. Only clients belonging to client class
+ docsis3.0 are allowed to use this subnet. Such a configuration can be
+ achieved in the following way:
+ <screen>
+> <userinput>config add Dhcp4/subnet4</userinput>
+> <userinput>config set Dhcp4/subnet4[0]/subnet "192.0.2.0/24"</userinput>
+> <userinput>config set Dhcp4/subnet4[0]/pool [ "192.0.2.10 - 192.0.2.20" ]</userinput>
+> <userinput>config set Dhcp4/subnet4[0]/client-class "docsis3.0"</userinput>
+> <userinput>config commit</userinput></screen>
+ </para>
+
+ <para>
+ Care should be taken with client classification as it is easy to prevent
+ clients that do not meet class criteria to be denied any service altogether.
+ </para>
+ </section>
+
+ </section> <!-- end of configuring DHCPv4 server section with many subsections -->
+
<section id="dhcp4-serverid">
<title>Server Identifier in DHCPv4</title>
<para>
@@ -4390,6 +4518,7 @@ Dhcp4/subnet4 [] list (default)
</para>
</section>
+
<section id="dhcp4-next-server">
<title>Next server (siaddr)</title>
<para>In some cases, clients want to obtain configuration from the TFTP server.
@@ -4437,6 +4566,40 @@ Dhcp4/subnet4 [] list (default)
</section>
+ <section id="dhcp4-subnet-selection">
+ <title>How DHCPv4 server selects subnet for a client</title>
+ <para>
+ The DHCPv4 server differentiates between the directly connected clients,
+ clients trying to renew leases and clients sending their messages through
+ relays. For the directly connected clients the server will check the
+ configuration of the interface on which the message has been received, and
+ if the server configuration doesn't match any configured subnet the
+ message is discarded.</para>
+ <para>Assuming that the server's interface is configured with the 192.0.2.3
+ IPv4 address, the server will only process messages received through
+ this interface from the directly connected client, if there is a subnet
+ configured, to which this IPv4 address belongs, e.g. 192.0.2.0/24.
+ The server will use this subnet to assign IPv4 address for the client.
+ </para>
+ <para>
+ The rule above does not apply when the client unicasts its message, i.e.
+ is trying to renew its lease. Such message is accepted through any
+ interface. The renewing client sets ciaddr to the currently used IPv4
+ address. The server uses this address to select the subnet for the client
+ (in particular, to extend the lease using this address).
+ </para>
+ <para>
+ If the message is relayed it is accepted through any interface. The giaddr
+ set by the relay agent is used to select the subnet for the client.
+ </para>
+ <note>
+ <para>The subnet selection mechanism described in this section is based
+ on the assumption that client classification is not used. The classification
+ mechanism alters the way in which subnet is selected for the client,
+ depending on the clasess that the client belongs to.</para>
+ </note>
+ </section>
+
<section id="dhcp4-std">
<title>Supported Standards</title>
<para>The following standards and draft standards are currently
@@ -4456,6 +4619,8 @@ Dhcp4/subnet4 [] list (default)
<listitem>
<simpara><ulink url="http://tools.ietf.org/html/rfc3046">RFC 3046</ulink>:
Relay Agent Information option is supported.</simpara>
+ </listitem>
+ <listitem>
<simpara><ulink url="http://tools.ietf.org/html/rfc6842">RFC 6842</ulink>:
Server by default sends back client-id option. That capability may be
disabled. See <xref linkend="dhcp4-echo-client-id"/> for details.
@@ -4550,13 +4715,21 @@ Dhcp4/renew-timer 1000 integer (default)
</para>
<para>
To remove <command>b10-dhcp6</command> from the set of running services,
- the <command>b10-dhcp4</command> is removed from list of Init components:
+ the <command>b10-dhcp6</command> is removed from list of Init components:
<screen>
> <userinput>config remove Init/components b10-dhcp6</userinput>
> <userinput>config commit</userinput>
</screen>
</para>
+ <para>
+ Note that the server was only removed from the list, so BIND10 will not
+ restart it, but the server itself is still running. Hence it is usually
+ desired to stop it:
+<screen>
+> <userinput>Dhcp6 shutdown</userinput>
+</screen>
+ </para>
<para>
During start-up the server will detect available network interfaces
@@ -4762,7 +4935,7 @@ Dhcp6/subnet6/ list
</para>
</section>
- <section>
+ <section id="dhcp6-address-config">
<title>Subnet and Address Pool</title>
<para>
The essential role of a DHCPv6 server is address assignment. For this,
@@ -4954,6 +5127,21 @@ Dhcp6/subnet6/ list
<!-- @todo: describe record types -->
+ <para>
+ The <xref linkend="dhcp6-custom-options"/> describes the configuration
+ syntax to create custom option definitions (formats). It is generally not
+ allowed to create custom definitions for standard options, even if the
+ definition being created matches the actual option format defined in the
+ RFCs. There is an exception from this rule for standard options for which
+ Kea does not provide a definition yet. In order to use such options,
+ a server administrator must create a definition as described in
+ <xref linkend="dhcp6-custom-options"/> in the 'dhcp6' option space. This
+ definition should match the option format described in the relevant
+ RFC but configuration mechanism would allow any option format as it has
+ no means to validate it at the moment.
+ </para>
+
+
<para>
<table frame="all" id="dhcp6-std-options-list">
<title>List of standard DHCPv6 options</title>
@@ -5067,10 +5255,10 @@ Dhcp6/subnet6/ list
define an option comprising a number of existing primitives.
</para>
<para>
- Assume we
- want to define a new option that will consist of an IPv6
- address, followed by unsigned 16 bit integer, followed by a text
- string. Such an option could be defined in the following way:
+ Assume we want to define a new option that will consist of an IPv6
+ address, followed by an unsigned 16 bit integer, followed by a
+ boolean value, followed by a text string. Such an option could
+ be defined in the following way:
<screen>
> <userinput>config add Dhcp6/option-def</userinput>
> <userinput>config set Dhcp6/option-def[0]/name "bar"</userinput>
@@ -5078,7 +5266,7 @@ Dhcp6/subnet6/ list
> <userinput>config set Dhcp6/option-def[0]/space "dhcp6"</userinput>
> <userinput>config set Dhcp6/option-def[0]/type "record"</userinput>
> <userinput>config set Dhcp6/option-def[0]/array false</userinput>
-> <userinput>config set Dhcp6/option-def[0]/record-types "ipv6-address, uint16, string"</userinput>
+> <userinput>config set Dhcp6/option-def[0]/record-types "ipv6-address, uint16, boolean, string"</userinput>
> <userinput>config set Dhcp6/option-def[0]/encapsulate ""</userinput>
</screen>
The "type" is set to "record" to indicate that the option contains
@@ -5093,12 +5281,23 @@ Dhcp6/subnet6/ list
> <userinput>config set Dhcp6/option-data[0]/space "dhcp6"</userinput>
> <userinput>config set Dhcp6/option-data[0]/code 101</userinput>
> <userinput>config set Dhcp6/option-data[0]/csv-format true</userinput>
-> <userinput>config set Dhcp6/option-data[0]/data "2001:db8:1::10, 123, Hello World"</userinput>
+> <userinput>config set Dhcp6/option-data[0]/data "2001:db8:1::10, 123, false, Hello World"</userinput>
> <userinput>config commit</userinput></screen>
- </para>
"csv-format" is set "true" to indicate that the "data" field comprises a command-separated
list of values. The values in the "data" must correspond to the types set in
the "record-types" field of the option definition.
+ </para>
+ <note>
+ <para>
+ It is recommended that boolean values are specified using "true" and "false"
+ strings. This helps to prevent errors when typing multiple comma separated
+ values, as it make it easier to identify the type of the value being typed,
+ and compare it with the order of data fields. Nevetheless, it is possible
+ to use integer values: "1" and "0", instead of "true" and "false"
+ accordingly. If other integer value is specified, the configuration is
+ rejected.
+ </para>
+ </note>
</section>
<section id="dhcp6-vendor-opts">
@@ -5255,7 +5454,7 @@ should include options from the isc option space:
</section>
<section id="dhcp6-config-subnets">
- <title>Subnet Selection</title>
+ <title>IPv6 Subnet Selection</title>
<para>
The DHCPv6 server may receive requests from local (connected to the
same subnet as the server) and remote (connecting via relays) clients.
@@ -5343,6 +5542,76 @@ should include options from the isc option space:
</para>
</section>
+ <section id="dhcp6-client-classifier">
+ <title>Client Classification in DHCPv6</title>
+ <note>
+ <para>
+ DHCPv6 server has been extended to support limited client classification.
+ Although the current capability is modest, it is expected to be expanded
+ in the future. It is envisaged that the majority of client classification
+ extensions will be using hooks extensions.
+ </para>
+ </note>
+ <para>In certain cases it is useful to differentiate between different types
+ of clients and treat them differently. The process of doing classification
+ is conducted in two steps. The first step is to assess incoming packet and
+ assign it to zero or more classes. This classification is currently simple,
+ but is expected to grow in capability soon. Currently the server checks whether
+ incoming packet has vendor class option (16). If it has, content
+ of that option is interpreted as a class. For example, modern cable modems
+ will send this option with value "docsis3.0" and as a result the
+ packet will belong to class "docsis3.0".
+ </para>
+
+ <para>It is envisaged that the client classification will be used for changing
+ behavior of almost any part of the DHCP engine processing, including assigning
+ leases from different pools, assigning different option (or different values of
+ the same options) etc. For now, there is only one mechanism that is taking
+ advantage of client classification: subnet selection.</para>
+
+ <para>
+ Kea can be instructed to limit access to given subnets based on class information.
+ This is particularly useful for cases where two types of devices share the
+ same link and are expected to be served from two different subnets. The
+ primary use case for such a scenario are cable networks. There are two
+ classes of devices: cable modem itself, which should be handled a lease
+ from subnet A and all other devices behind modems that should get a lease
+ from subnet B. That segregation is essential to prevent overly curious
+ users from playing with their cable modems. For details on how to set up
+ class restrictions on subnets, see <xref linkend="dhcp6-subnet-class"/>.
+ </para>
+
+ </section>
+
+ <section id="dhcp6-subnet-class">
+ <title>Limiting access to IPv6 subnet to certain classes</title>
+ <para>
+ In certain cases it beneficial to restrict access to certains subnets
+ only to clients that belong to a given subnet. For details on client
+ classes, see <xref linkend="dhcp6-client-classifier"/>. This is an
+ extension of a previous example from <xref linkend="dhcp6-address-config"/>.
+
+ Let's assume that the server is connected to a network segment that uses
+ the 2001:db8:1::/64 prefix. The Administrator of that network has
+ decided that addresses from range 2001:db8:1::1 to 2001:db8:1::ffff are
+ going to be managed by the Dhcp6 server. Only clients belonging to the
+ eRouter1.0 client class are allowed to use that pool. Such a
+ configuration can be achieved in the following way:
+
+ <screen>
+> <userinput>config add Dhcp6/subnet6</userinput>
+> <userinput>config set Dhcp6/subnet6[0]/subnet "2001:db8:1::/64"</userinput>
+> <userinput>config set Dhcp6/subnet6[0]/pool [ "2001:db8:1::0 - 2001:db8:1::ffff" ]</userinput>
+> <userinput>config set Dhcp6/subnet6[0]/client-class "eRouter1.0"</userinput>
+> <userinput>config commit</userinput></screen>
+ </para>
+
+ <para>
+ Care should be taken with client classification as it is easy to prevent
+ clients that do not meet class criteria to be denied any service altogether.
+ </para>
+ </section>
+
</section>
<section id="dhcp6-serverid">
diff --git a/src/bin/auth/auth_messages.mes b/src/bin/auth/auth_messages.mes
index e5b655a..5009de8 100644
--- a/src/bin/auth/auth_messages.mes
+++ b/src/bin/auth/auth_messages.mes
@@ -383,11 +383,11 @@ This message is also logged when the forwarding is restarted (for instance
if b10-ddns is restarted and the internal connection needs to be created
again), in which case it should be followed by AUTH_START_DDNS_FORWARDER.
-% AUTH_UNSUPPORTED_OPCODE unsupported opcode: %1
+% AUTH_UNSUPPORTED_OPCODE unsupported opcode %1 received from %2
This is a debug message, produced when a received DNS packet being
processed by the authoritative server has been found to contain an
-unsupported opcode. (The opcode is included in the message.) The server
-will return an error code of NOTIMPL to the sender.
+unsupported opcode. (The opcode and sender details are included in the
+message.) The server will return an error code of NOTIMPL to the sender.
% AUTH_XFRIN_CHANNEL_CREATED XFRIN session channel created
This is a debug message indicating that the authoritative server has
diff --git a/src/bin/auth/auth_srv.cc b/src/bin/auth/auth_srv.cc
index c0f9730..879b7b3 100644
--- a/src/bin/auth/auth_srv.cc
+++ b/src/bin/auth/auth_srv.cc
@@ -297,6 +297,8 @@ public:
///
/// \param server The DNSServer as passed to processMessage()
/// \param message The response as constructed by processMessage()
+ /// \param stats_attrs Object to store message attributes in for use
+ /// with statistics
/// \param done If true, it indicates there is a response.
/// this value will be passed to server->resume(bool)
void resumeServer(isc::asiodns::DNSServer* server,
@@ -440,12 +442,9 @@ makeErrorMessage(MessageRenderer& renderer, Message& message,
message.setRcode(rcode);
RendererHolder holder(renderer, &buffer, stats_attrs);
- if (tsig_context.get() != NULL) {
- message.toWire(renderer, *tsig_context);
- stats_attrs.setResponseTSIG(true);
- } else {
- message.toWire(renderer);
- }
+ message.toWire(renderer, tsig_context.get());
+ stats_attrs.setResponseTSIG(tsig_context.get() != NULL);
+
LOG_DEBUG(auth_logger, DBG_AUTH_MESSAGES, AUTH_SEND_ERROR_RESPONSE)
.arg(renderer.getLength()).arg(message);
}
@@ -499,7 +498,7 @@ AuthSrv::processMessage(const IOMessage& io_message, Message& message,
impl_->resumeServer(server, message, stats_attrs, false);
return;
}
- } catch (const Exception& ex) {
+ } catch (const isc::Exception& ex) {
LOG_DEBUG(auth_logger, DBG_AUTH_DETAIL, AUTH_HEADER_PARSE_FAIL)
.arg(ex.what());
impl_->resumeServer(server, message, stats_attrs, false);
@@ -523,7 +522,7 @@ AuthSrv::processMessage(const IOMessage& io_message, Message& message,
stats_attrs);
impl_->resumeServer(server, message, stats_attrs, true);
return;
- } catch (const Exception& ex) {
+ } catch (const isc::Exception& ex) {
LOG_DEBUG(auth_logger, DBG_AUTH_DETAIL, AUTH_PACKET_PARSE_FAILED)
.arg(ex.what());
makeErrorMessage(impl_->renderer_, message, buffer, Rcode::SERVFAIL(),
@@ -582,8 +581,9 @@ AuthSrv::processMessage(const IOMessage& io_message, Message& message,
Rcode::NOTIMP(), stats_attrs, tsig_context);
}
} else if (opcode != Opcode::QUERY()) {
+ const IOEndpoint& remote_ep = io_message.getRemoteEndpoint();
LOG_DEBUG(auth_logger, DBG_AUTH_DETAIL, AUTH_UNSUPPORTED_OPCODE)
- .arg(message.getOpcode().toText());
+ .arg(message.getOpcode().toText()).arg(remote_ep);
makeErrorMessage(impl_->renderer_, message, buffer,
Rcode::NOTIMP(), stats_attrs, tsig_context);
} else if (message.getRRCount(Message::SECTION_QUESTION) != 1) {
@@ -660,7 +660,7 @@ AuthSrvImpl::processNormalQuery(const IOMessage& io_message,
stats_attrs);
return (true);
}
- } catch (const Exception& ex) {
+ } catch (const isc::Exception& ex) {
LOG_ERROR(auth_logger, AUTH_PROCESS_FAIL).arg(ex.what());
makeErrorMessage(renderer_, message, buffer, Rcode::SERVFAIL(),
stats_attrs);
@@ -671,12 +671,9 @@ AuthSrvImpl::processNormalQuery(const IOMessage& io_message,
const bool udp_buffer =
(io_message.getSocket().getProtocol() == IPPROTO_UDP);
renderer_.setLengthLimit(udp_buffer ? remote_bufsize : 65535);
- if (tsig_context.get() != NULL) {
- message.toWire(renderer_, *tsig_context);
- stats_attrs.setResponseTSIG(true);
- } else {
- message.toWire(renderer_);
- }
+ message.toWire(renderer_, tsig_context.get());
+ stats_attrs.setResponseTSIG(tsig_context.get() != NULL);
+
LOG_DEBUG(auth_logger, DBG_AUTH_MESSAGES, AUTH_SEND_NORMAL_RESPONSE)
.arg(renderer_.getLength()).arg(message);
return (true);
@@ -823,7 +820,7 @@ AuthSrvImpl::processNotify(const IOMessage& io_message, Message& message,
.arg(parsed_answer->str());
return (false);
}
- } catch (const Exception& ex) {
+ } catch (const isc::Exception& ex) {
LOG_ERROR(auth_logger, AUTH_ZONEMGR_COMMS).arg(ex.what());
return (false);
}
@@ -833,12 +830,8 @@ AuthSrvImpl::processNotify(const IOMessage& io_message, Message& message,
message.setRcode(Rcode::NOERROR());
RendererHolder holder(renderer_, &buffer, stats_attrs);
- if (tsig_context.get() != NULL) {
- message.toWire(renderer_, *tsig_context);
- stats_attrs.setResponseTSIG(true);
- } else {
- message.toWire(renderer_);
- }
+ message.toWire(renderer_, tsig_context.get());
+ stats_attrs.setResponseTSIG(tsig_context.get() != NULL);
return (true);
}
diff --git a/src/bin/auth/auth_srv.h b/src/bin/auth/auth_srv.h
index b8147e0..02d8798 100644
--- a/src/bin/auth/auth_srv.h
+++ b/src/bin/auth/auth_srv.h
@@ -104,6 +104,8 @@ public:
/// process. It's normally a reference to an xfr::XfroutClient object,
/// but can refer to a local mock object for testing (or other
/// experimental) purposes.
+ /// \param ddns_forwarder Forwarder to which DDNS UPDATE requests
+ /// are passed to
AuthSrv(isc::xfr::AbstractXfroutClient& xfrout_client,
isc::util::io::BaseSocketSessionForwarder& ddns_forwarder);
~AuthSrv();
diff --git a/src/bin/auth/query.cc b/src/bin/auth/query.cc
index 87f8e91..9d6fbfa 100644
--- a/src/bin/auth/query.cc
+++ b/src/bin/auth/query.cc
@@ -37,20 +37,6 @@ using namespace isc::dns;
using namespace isc::datasrc;
using namespace isc::dns::rdata;
-// This is a "constant" vector storing desired RR types for the additional
-// section. The vector is filled first time it's used.
-namespace {
-const vector<RRType>&
-A_AND_AAAA() {
- static vector<RRType> needed_types;
- if (needed_types.empty()) {
- needed_types.push_back(RRType::A());
- needed_types.push_back(RRType::AAAA());
- }
- return (needed_types);
-}
-}
-
namespace isc {
namespace auth {
@@ -393,6 +379,17 @@ Query::process(datasrc::ClientList& client_list,
response_->setRcode(Rcode::SERVFAIL());
return;
}
+
+ if (qtype == RRType::RRSIG()) {
+ // We will not serve RRSIGs directly. See #2226 and the
+ // following thread for discussion why:
+ // http://www.ietf.org/mail-archive/web/dnsext/current/msg07123.html
+ // RRSIGs go together with their covered RRset.
+ response_->setHeaderFlag(Message::HEADERFLAG_AA);
+ response_->setRcode(Rcode::REFUSED());
+ return;
+ }
+
ZoneFinder& zfinder = *result.finder_;
// We have authority for a zone that contain the query name (possibly
diff --git a/src/bin/auth/query.h b/src/bin/auth/query.h
index 2c08312..a692dcc 100644
--- a/src/bin/auth/query.h
+++ b/src/bin/auth/query.h
@@ -286,6 +286,9 @@ public:
answers_.reserve(RESERVE_RRSETS);
authorities_.reserve(RESERVE_RRSETS);
additionals_.reserve(RESERVE_RRSETS);
+
+ a_and_aaaa_.push_back(isc::dns::RRType::A());
+ a_and_aaaa_.push_back(isc::dns::RRType::AAAA());
}
@@ -488,6 +491,15 @@ private:
std::vector<isc::dns::ConstRRsetPtr> answers_;
std::vector<isc::dns::ConstRRsetPtr> authorities_;
std::vector<isc::dns::ConstRRsetPtr> additionals_;
+
+private:
+ /// \brief Returns a reference to a pre-initialized vector (see the
+ /// \c Query constructor).
+ const std::vector<isc::dns::RRType>& A_AND_AAAA() const {
+ return (a_and_aaaa_);
+ }
+
+ std::vector<isc::dns::RRType> a_and_aaaa_;
};
}
diff --git a/src/bin/auth/tests/auth_srv_unittest.cc b/src/bin/auth/tests/auth_srv_unittest.cc
index 9a58692..dbbd0a4 100644
--- a/src/bin/auth/tests/auth_srv_unittest.cc
+++ b/src/bin/auth/tests/auth_srv_unittest.cc
@@ -44,6 +44,7 @@
#include <util/unittests/mock_socketsession.h>
#include <dns/tests/unittest_util.h>
+#include <util/unittests/wiredata.h>
#include <testutils/dnsmessage_test.h>
#include <testutils/srv_test.h>
#include <testutils/mockups.h>
@@ -82,8 +83,9 @@ using namespace isc::testutils;
using namespace isc::server_common::portconfig;
using namespace isc::auth::unittest;
using isc::UnitTestUtil;
-using boost::scoped_ptr;
using isc::auth::statistics::Counters;
+using isc::util::unittests::matchWireData;
+using boost::scoped_ptr;
namespace {
const char* const CONFIG_TESTDB =
@@ -1072,10 +1074,9 @@ TEST_F(AuthSrvTest, builtInQueryViaDNSServer) {
response_message, response_obuffer);
createBuiltinVersionResponse(default_qid, response_data);
- EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
- response_obuffer->getData(),
- response_obuffer->getLength(),
- &response_data[0], response_data.size());
+ matchWireData(&response_data[0], response_data.size(),
+ response_obuffer->getData(),
+ response_obuffer->getLength());
// After it has been run, the message should be cleared
EXPECT_EQ(0, parse_message->getRRCount(Message::SECTION_QUESTION));
@@ -1095,10 +1096,9 @@ TEST_F(AuthSrvTest, builtInQuery) {
server.processMessage(*io_message, *parse_message, *response_obuffer,
&dnsserv);
createBuiltinVersionResponse(default_qid, response_data);
- EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
- response_obuffer->getData(),
- response_obuffer->getLength(),
- &response_data[0], response_data.size());
+ matchWireData(&response_data[0], response_data.size(),
+ response_obuffer->getData(),
+ response_obuffer->getLength());
checkAllRcodeCountersZeroExcept(Rcode::NOERROR(), 1);
}
@@ -1114,10 +1114,9 @@ TEST_F(AuthSrvTest, iqueryViaDNSServer) {
UnitTestUtil::readWireData("iquery_response_fromWire.wire",
response_data);
- EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
- response_obuffer->getData(),
- response_obuffer->getLength(),
- &response_data[0], response_data.size());
+ matchWireData(&response_data[0], response_data.size(),
+ response_obuffer->getData(),
+ response_obuffer->getLength());
}
// Install a Sqlite3 data source with testing data.
diff --git a/src/bin/auth/tests/query_unittest.cc b/src/bin/auth/tests/query_unittest.cc
index f374a87..c01bd8e 100644
--- a/src/bin/auth/tests/query_unittest.cc
+++ b/src/bin/auth/tests/query_unittest.cc
@@ -22,6 +22,7 @@
#include <dns/message.h>
#include <dns/master_loader.h>
#include <dns/name.h>
+#include <dns/labelsequence.h>
#include <dns/nsec3hash.h>
#include <dns/opcode.h>
#include <dns/rcode.h>
@@ -245,6 +246,13 @@ public:
isc_throw(isc::Unexpected, "unexpected name for NSEC3 test: "
<< name);
}
+ virtual string calculate(const LabelSequence& ls) const {
+ assert(ls.isAbsolute());
+ // This is not very optimal, but it's only going to be used in
+ // tests.
+ const Name name(ls.toText());
+ return (calculate(name));
+ }
virtual bool match(const rdata::generic::NSEC3PARAM&) const {
return (true);
}
@@ -1215,6 +1223,13 @@ TEST_P(QueryTest, exactMatchMultipleQueries) {
www_a_txt, zone_ns_txt, ns_addrs_txt);
}
+TEST_P(QueryTest, qtypeIsRRSIG) {
+ // Directly querying for RRSIGs should result in rcode=REFUSED.
+ EXPECT_NO_THROW(query.process(*list_, qname, RRType::RRSIG(), response));
+ responseCheck(response, Rcode::REFUSED(), AA_FLAG, 0, 0, 0,
+ "", "", "");
+}
+
TEST_P(QueryTest, exactMatchIgnoreSIG) {
// Check that we do not include the RRSIG when not requested even when
// we receive it from the data source.
diff --git a/src/bin/bindctl/bindcmd.py b/src/bin/bindctl/bindcmd.py
index bcae95c..a83775b 100644
--- a/src/bin/bindctl/bindcmd.py
+++ b/src/bin/bindctl/bindcmd.py
@@ -446,8 +446,9 @@ WARNING: The Python readline module isn't available, so some command line
raise CmdMissParamSyntaxError(cmd.module, cmd.command, name)
param_nr += 1
- # Convert parameter value according parameter spec file.
- # Ignore check for commands belongs to module 'config' or 'execute
+ # Convert parameter value according to parameter spec
+ # file. Ignore check for commands belonging 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:
diff --git a/src/bin/d2/dns_client.cc b/src/bin/d2/dns_client.cc
index 9202e7d..638ac93 100644
--- a/src/bin/d2/dns_client.cc
+++ b/src/bin/d2/dns_client.cc
@@ -142,7 +142,7 @@ DNSClientImpl::operator()(asiodns::IOFetch::Result result) {
try {
response_->fromWire(response_buf);
- } catch (const Exception& ex) {
+ } catch (const isc::Exception& ex) {
status = DNSClient::INVALID_RESPONSE;
LOG_DEBUG(dctl_logger, DBGLVL_TRACE_DETAIL,
DHCP_DDNS_INVALID_RESPONSE).arg(ex.what());
diff --git a/src/bin/d2/dns_client.h b/src/bin/d2/dns_client.h
index 5960f6a..54d6a6a 100644
--- a/src/bin/d2/dns_client.h
+++ b/src/bin/d2/dns_client.h
@@ -1,4 +1,4 @@
-// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2013-2014 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
@@ -143,8 +143,8 @@ public:
/// @param ns_addr DNS server address.
/// @param ns_port DNS server port.
/// @param update A DNS Update message to be sent to the server.
- /// @param wait A timeout (in seconds) for the response. If a response is
- /// not received within the timeout, exchange is interrupted. This value
+ /// @param wait A timeout (in milliseconds) for the response. If a response
+ /// is not received within the timeout, exchange is interrupted. This value
/// must not exceed maximal value for 'int' data type.
/// @param tsig_key An @c isc::dns::TSIGKey object representing TSIG
/// context which will be used to render the DNS Update message.
@@ -173,8 +173,8 @@ public:
/// @param ns_addr DNS server address.
/// @param ns_port DNS server port.
/// @param update A DNS Update message to be sent to the server.
- /// @param wait A timeout (in seconds) for the response. If a response is
- /// not received within the timeout, exchange is interrupted. This value
+ /// @param wait A timeout (in milliseconds) for the response. If a response
+ /// is not received within the timeout, exchange is interrupted. This value
/// must not exceed maximal value for 'int' data type.
void doUpdate(asiolink::IOService& io_service,
const asiolink::IOAddress& ns_addr,
diff --git a/src/bin/d2/nc_add.cc b/src/bin/d2/nc_add.cc
index abd22f6..f34f45c 100644
--- a/src/bin/d2/nc_add.cc
+++ b/src/bin/d2/nc_add.cc
@@ -1,4 +1,4 @@
-// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2014 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
@@ -588,10 +588,13 @@ NameAddTransaction::buildAddFwdAddressRequest() {
// Next build the Update Section.
+ // Create the TTL based on lease length.
+ dns::RRTTL lease_ttl(getNcr()->getLeaseLength());
+
// Create the FQDN/IP 'add' RR and add it to the to update section.
// Based on RFC 2136, section 2.5.1
dns::RRsetPtr update(new dns::RRset(fqdn, dns::RRClass::IN(),
- getAddressRRType(), dns::RRTTL(0)));
+ getAddressRRType(), lease_ttl));
addLeaseAddressRdata(update);
request->addRRset(D2UpdateMessage::SECTION_UPDATE, update);
@@ -599,7 +602,7 @@ NameAddTransaction::buildAddFwdAddressRequest() {
// Now create the FQDN/DHCID 'add' RR and add it to update section.
// Based on RFC 2136, section 2.5.1
update.reset(new dns::RRset(fqdn, dns::RRClass::IN(),
- dns::RRType::DHCID(), dns::RRTTL(0)));
+ dns::RRType::DHCID(), lease_ttl));
addDhcidRdata(update);
request->addRRset(D2UpdateMessage::SECTION_UPDATE, update);
@@ -635,6 +638,9 @@ NameAddTransaction::buildReplaceFwdAddressRequest() {
// Next build the Update Section.
+ // Create the TTL based on lease length.
+ dns::RRTTL lease_ttl(getNcr()->getLeaseLength());
+
// Create the FQDN/IP 'delete' RR and add it to the update section.
// Based on RFC 2136, section 2.5.2
dns::RRsetPtr update(new dns::RRset(fqdn, dns::RRClass::ANY(),
@@ -644,7 +650,7 @@ NameAddTransaction::buildReplaceFwdAddressRequest() {
// Create the FQDN/IP 'add' RR and add it to the update section.
// Based on RFC 2136, section 2.5.1
update.reset(new dns::RRset(fqdn, dns::RRClass::IN(),
- getAddressRRType(), dns::RRTTL(0)));
+ getAddressRRType(), lease_ttl));
addLeaseAddressRdata(update);
request->addRRset(D2UpdateMessage::SECTION_UPDATE, update);
@@ -661,6 +667,9 @@ NameAddTransaction::buildReplaceRevPtrsRequest() {
std::string rev_addr = D2CfgMgr::reverseIpAddress(getNcr()->getIpAddress());
dns::Name rev_ip(rev_addr);
+ // Create the TTL based on lease length.
+ dns::RRTTL lease_ttl(getNcr()->getLeaseLength());
+
// Content on this request is based on RFC 4703, section 5.4
// Reverse replacement has no prerequisites so straight on to
// building the Update section.
@@ -678,14 +687,14 @@ NameAddTransaction::buildReplaceRevPtrsRequest() {
// Create the FQDN/IP PTR 'add' RR, add the FQDN as the PTR Rdata
// then add it to update section.
update.reset(new dns::RRset(rev_ip, dns::RRClass::IN(),
- dns::RRType::PTR(), dns::RRTTL(0)));
+ dns::RRType::PTR(), lease_ttl));
addPtrRdata(update);
request->addRRset(D2UpdateMessage::SECTION_UPDATE, update);
// Create the FQDN/IP PTR 'add' RR, add the DHCID Rdata
// then add it to update section.
update.reset(new dns::RRset(rev_ip, dns::RRClass::IN(),
- dns::RRType::DHCID(), dns::RRTTL(0)));
+ dns::RRType::DHCID(), lease_ttl));
addDhcidRdata(update);
request->addRRset(D2UpdateMessage::SECTION_UPDATE, update);
diff --git a/src/bin/d2/tests/dns_client_unittests.cc b/src/bin/d2/tests/dns_client_unittests.cc
index 1b4c2bd..0bfc4b4 100644
--- a/src/bin/d2/tests/dns_client_unittests.cc
+++ b/src/bin/d2/tests/dns_client_unittests.cc
@@ -263,8 +263,16 @@ public:
ASSERT_NO_THROW(message.setRcode(Rcode(Rcode::NOERROR_CODE)));
ASSERT_NO_THROW(message.setZone(Name("example.com"), RRClass::IN()));
- // Set the response wait time to 0 so as our test is not hanging. This
- // should cause instant timeout.
+ /// @todo The timeout value could be set to 0 to trigger timeout
+ /// instantly. However, it may lead to situations that the message sent
+ /// in one test will not be dropped by the kernel by the time, the next
+ /// test starts. This will lead to intermittent unit test errors as
+ /// described in the ticket http://bind10.isc.org/ticket/3265.
+ /// Increasing the timeout to a non-zero value mitigates this problem.
+ /// The proper way to solve this problem is to receive the packet
+ /// on our own and drop it. Such a fix will need to be applied not only
+ /// to this test but also for other tests that rely on arbitrary timeout
+ /// values.
const int timeout = 500;
// The doUpdate() function starts asynchronous message exchange with DNS
// server. When message exchange is done or timeout occurs, the
@@ -324,8 +332,10 @@ public:
corrupt_response));
// The socket is now ready to receive the data. Let's post some request
- // message then.
- const int timeout = 5;
+ // message then. Set timeout to some reasonable value to make sure that
+ // there is sufficient amount of time for the test to generate a
+ // response.
+ const int timeout = 500;
expected_++;
dns_client_->doUpdate(service_, IOAddress(TEST_ADDRESS), TEST_PORT,
message, timeout);
diff --git a/src/bin/d2/tests/nc_test_utils.cc b/src/bin/d2/tests/nc_test_utils.cc
index b65b9e4..957688a 100644
--- a/src/bin/d2/tests/nc_test_utils.cc
+++ b/src/bin/d2/tests/nc_test_utils.cc
@@ -16,6 +16,8 @@
#include <dns/opcode.h>
#include <dns/messagerenderer.h>
#include <nc_test_utils.h>
+#include <asio.hpp>
+#include <asiolink/udp_endpoint.h>
#include <gtest/gtest.h>
@@ -42,7 +44,9 @@ FauxServer::FauxServer(asiolink::IOService& io_service,
server_socket_.reset(new asio::ip::udp::socket(io_service_.get_io_service(),
asio::ip::udp::v4()));
server_socket_->set_option(asio::socket_base::reuse_address(true));
- server_socket_->bind(asio::ip::udp::endpoint(address_.getAddress(), port_));
+
+ isc::asiolink::UDPEndpoint endpoint(address_, port_);
+ server_socket_->bind(endpoint.getASIOEndpoint());
}
FauxServer::FauxServer(asiolink::IOService& io_service,
@@ -53,7 +57,8 @@ FauxServer::FauxServer(asiolink::IOService& io_service,
server_socket_.reset(new asio::ip::udp::socket(io_service_.get_io_service(),
asio::ip::udp::v4()));
server_socket_->set_option(asio::socket_base::reuse_address(true));
- server_socket_->bind(asio::ip::udp::endpoint(address_.getAddress(), port_));
+ isc::asiolink::UDPEndpoint endpoint(address_, port_);
+ server_socket_->bind(endpoint.getASIOEndpoint());
}
@@ -422,15 +427,19 @@ void checkAddFwdAddressRequest(NameChangeTransaction& tran) {
// Should be 2 RRs: 1 to add the FQDN/IP and one to add the DHCID RR
checkRRCount(request, D2UpdateMessage::SECTION_UPDATE, 2);
+ // Fetch ttl.
+ uint32_t ttl = ncr->getLeaseLength();
+
// First, Verify the FQDN/IP add RR.
ASSERT_TRUE(rrset = getRRFromSection(request, D2UpdateMessage::
SECTION_UPDATE, 0));
- checkRR(rrset, exp_fqdn, dns::RRClass::IN(), exp_ip_rr_type, 0, ncr);
+ checkRR(rrset, exp_fqdn, dns::RRClass::IN(), exp_ip_rr_type, ttl, ncr);
// Now, verify the DHCID add RR.
ASSERT_TRUE(rrset = getRRFromSection(request, D2UpdateMessage::
SECTION_UPDATE, 1));
- checkRR(rrset, exp_fqdn, dns::RRClass::IN(), dns::RRType::DHCID(), 0, ncr);
+ checkRR(rrset, exp_fqdn, dns::RRClass::IN(), dns::RRType::DHCID(),
+ ttl, ncr);
// Verify there are no RRs in the ADDITIONAL Section.
checkRRCount(request, D2UpdateMessage::SECTION_ADDITIONAL, 0);
@@ -478,6 +487,9 @@ void checkReplaceFwdAddressRequest(NameChangeTransaction& tran) {
// adds the new one.
checkRRCount(request, D2UpdateMessage::SECTION_UPDATE, 2);
+ // Fetch ttl.
+ uint32_t ttl = ncr->getLeaseLength();
+
// Verify the FQDN delete RR.
ASSERT_TRUE(rrset = getRRFromSection(request, D2UpdateMessage::
SECTION_UPDATE, 0));
@@ -486,7 +498,7 @@ void checkReplaceFwdAddressRequest(NameChangeTransaction& tran) {
// Verify the FQDN/IP add RR.
ASSERT_TRUE(rrset = getRRFromSection(request, D2UpdateMessage::
SECTION_UPDATE, 1));
- checkRR(rrset, exp_fqdn, dns::RRClass::IN(), exp_ip_rr_type, 0, ncr);
+ checkRR(rrset, exp_fqdn, dns::RRClass::IN(), exp_ip_rr_type, ttl, ncr);
// Verify there are no RRs in the ADDITIONAL Section.
checkRRCount(request, D2UpdateMessage::SECTION_ADDITIONAL, 0);
@@ -515,6 +527,9 @@ void checkReplaceRevPtrsRequest(NameChangeTransaction& tran) {
// Verify there are no RRs in the PREREQUISITE Section.
checkRRCount(request, D2UpdateMessage::SECTION_PREREQUISITE, 0);
+ // Fetch ttl.
+ uint32_t ttl = ncr->getLeaseLength();
+
// Verify the UPDATE Section.
// It should contain 4 RRs:
// 1. A delete all PTR RRs for the given IP
@@ -540,13 +555,13 @@ void checkReplaceRevPtrsRequest(NameChangeTransaction& tran) {
ASSERT_TRUE(rrset = getRRFromSection(request, D2UpdateMessage::
SECTION_UPDATE, 2));
checkRR(rrset, exp_rev_addr, dns::RRClass::IN(), dns::RRType::PTR(),
- 0, ncr);
+ ttl, ncr);
// Verify the DHCID add RR.
ASSERT_TRUE(rrset = getRRFromSection(request, D2UpdateMessage::
SECTION_UPDATE, 3));
checkRR(rrset, exp_rev_addr, dns::RRClass::IN(), dns::RRType::DHCID(),
- 0, ncr);
+ ttl, ncr);
// Verify there are no RRs in the ADDITIONAL Section.
checkRRCount(request, D2UpdateMessage::SECTION_ADDITIONAL, 0);
diff --git a/src/bin/d2/tests/nc_trans_unittests.cc b/src/bin/d2/tests/nc_trans_unittests.cc
index 010d197..a62d0ae 100644
--- a/src/bin/d2/tests/nc_trans_unittests.cc
+++ b/src/bin/d2/tests/nc_trans_unittests.cc
@@ -326,8 +326,8 @@ public:
ASSERT_EQ(NameChangeTransaction::NOP_EVT,
name_change->getNextEvent());
- int cnt = 0;
while (name_change->getNextEvent() == NameChangeTransaction::NOP_EVT) {
+ int cnt = 0;
ASSERT_NO_THROW(cnt = runTimedIO(run_time));
if (cnt == 0) {
FAIL() << "IO Service stopped unexpectedly";
diff --git a/src/bin/ddns/ddns.py.in b/src/bin/ddns/ddns.py.in
index 6bb81c5..f87a21f 100755
--- a/src/bin/ddns/ddns.py.in
+++ b/src/bin/ddns/ddns.py.in
@@ -495,10 +495,7 @@ class DDNSServer:
return False
msg = update_session.get_message()
self.__response_renderer.clear()
- if tsig_ctx is not None:
- msg.to_wire(self.__response_renderer, tsig_ctx)
- else:
- msg.to_wire(self.__response_renderer)
+ msg.to_wire(self.__response_renderer, tsig_ctx)
ret = self.__send_response(sock, self.__response_renderer.get_data(),
remote_addr)
diff --git a/src/bin/ddns/tests/ddns_test.py b/src/bin/ddns/tests/ddns_test.py
index 66e87a4..c5db61a 100755
--- a/src/bin/ddns/tests/ddns_test.py
+++ b/src/bin/ddns/tests/ddns_test.py
@@ -900,10 +900,7 @@ def create_msg(opcode=Opcode.UPDATE, zones=[TEST_ZONE_RECORD], prereq=[],
msg.add_rrset(SECTION_PREREQUISITE, p)
renderer = MessageRenderer()
- if tsigctx is not None:
- msg.to_wire(renderer, tsigctx)
- else:
- msg.to_wire(renderer)
+ msg.to_wire(renderer, tsigctx)
# re-read the created data in the parse mode
msg.clear(Message.PARSE)
diff --git a/src/bin/dhcp4/config_parser.cc b/src/bin/dhcp4/config_parser.cc
index 7774edc..1d010f3 100644
--- a/src/bin/dhcp4/config_parser.cc
+++ b/src/bin/dhcp4/config_parser.cc
@@ -1,4 +1,4 @@
-// Copyright (C) 2012-2013 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2012-2014 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
@@ -164,7 +164,7 @@ public:
/// @param ignored first parameter
/// stores global scope parameters, options, option defintions.
Subnet4ConfigParser(const std::string&)
- :SubnetConfigParser("", globalContext()) {
+ :SubnetConfigParser("", globalContext(), IOAddress("0.0.0.0")) {
}
/// @brief Adds the created subnet to a server's configuration.
@@ -178,6 +178,11 @@ public:
"Invalid cast in Subnet4ConfigParser::commit");
}
+ // Set relay information if it was parsed
+ if (relay_info_) {
+ sub4ptr->setRelayInfo(*relay_info_);
+ }
+
isc::dhcp::CfgMgr::instance().addSubnet4(sub4ptr);
}
}
@@ -200,10 +205,13 @@ protected:
parser = new Uint32Parser(config_id, uint32_values_);
} else if ((config_id.compare("subnet") == 0) ||
(config_id.compare("interface") == 0) ||
+ (config_id.compare("client-class") == 0) ||
(config_id.compare("next-server") == 0)) {
parser = new StringParser(config_id, string_values_);
} else if (config_id.compare("pool") == 0) {
parser = new Pool4Parser(config_id, pools_);
+ } else if (config_id.compare("relay") == 0) {
+ parser = new RelayInfoParser(config_id, relay_info_, Option::V4);
} else if (config_id.compare("option-data") == 0) {
parser = new OptionDataListParser(config_id, options_,
global_context_,
@@ -292,6 +300,14 @@ protected:
} catch (const DhcpConfigError&) {
// Don't care. next_server is optional. We can live without it
}
+
+ // Try setting up client class (if specified)
+ try {
+ string client_class = string_values_->getParam("client-class");
+ subnet4->allowClientClass(client_class);
+ } catch (const DhcpConfigError&) {
+ // That's ok if it fails. client-class is optional.
+ }
}
};
diff --git a/src/bin/dhcp4/ctrl_dhcp4_srv.cc b/src/bin/dhcp4/ctrl_dhcp4_srv.cc
index 43c08c9..8c52256 100644
--- a/src/bin/dhcp4/ctrl_dhcp4_srv.cc
+++ b/src/bin/dhcp4/ctrl_dhcp4_srv.cc
@@ -1,4 +1,4 @@
-// Copyright (C) 2012-2013 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2012-2014 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
@@ -114,6 +114,16 @@ ControlledDhcpv4Srv::dhcp4ConfigHandler(ConstElementPtr new_config) {
return (answer);
}
+ // Server will start DDNS communications if its enabled.
+ try {
+ server_->startD2();
+ } catch (const std::exception& ex) {
+ std::ostringstream err;
+ err << "error starting DHCP_DDNS client "
+ " after server reconfiguration: " << ex.what();
+ return (isc::config::createAnswer(1, err.str()));
+ }
+
// Configuration may change active interfaces. Therefore, we have to reopen
// sockets according to new configuration. This operation is not exception
// safe and we really don't want to emit exceptions to the callback caller.
@@ -211,11 +221,15 @@ void ControlledDhcpv4Srv::establishSession() {
try {
configureDhcp4Server(*this, config_session_->getFullConfig());
+
+ // Server will start DDNS communications if its enabled.
+ server_->startD2();
+
// Configuration may disable or enable interfaces so we have to
// reopen sockets according to new configuration.
openActiveSockets(getPort(), useBroadcast());
- } catch (const DhcpConfigError& ex) {
+ } catch (const std::exception& ex) {
LOG_ERROR(dhcp4_logger, DHCP4_CONFIG_LOAD_FAIL).arg(ex.what());
}
@@ -226,7 +240,7 @@ void ControlledDhcpv4Srv::establishSession() {
int ctrl_socket = cc_session_->getSocketDesc();
LOG_DEBUG(dhcp4_logger, DBG_DHCP4_START, DHCP4_CCSESSION_STARTED)
.arg(ctrl_socket);
- IfaceMgr::instance().set_session_socket(ctrl_socket, sessionReader);
+ IfaceMgr::instance().addExternalSocket(ctrl_socket, sessionReader);
}
void ControlledDhcpv4Srv::disconnectSession() {
@@ -235,13 +249,14 @@ void ControlledDhcpv4Srv::disconnectSession() {
config_session_ = NULL;
}
if (cc_session_) {
+
+ int ctrl_socket = cc_session_->getSocketDesc();
cc_session_->disconnect();
+
+ IfaceMgr::instance().deleteExternalSocket(ctrl_socket);
delete cc_session_;
cc_session_ = NULL;
}
-
- // deregister session socket
- IfaceMgr::instance().set_session_socket(IfaceMgr::INVALID_SOCKET, NULL);
}
ControlledDhcpv4Srv::ControlledDhcpv4Srv(uint16_t port /*= DHCP4_SERVER_PORT*/)
diff --git a/src/bin/dhcp4/ctrl_dhcp4_srv.h b/src/bin/dhcp4/ctrl_dhcp4_srv.h
index ce2a753..59dde87 100644
--- a/src/bin/dhcp4/ctrl_dhcp4_srv.h
+++ b/src/bin/dhcp4/ctrl_dhcp4_srv.h
@@ -107,7 +107,8 @@ protected:
/// various configuration values. Installing the dummy handler
/// that guarantees to return success causes initial configuration
/// to be stored for the session being created and that it can
- /// be later accessed with \ref isc::config::ConfigData::getFullConfig.
+ /// be later accessed with
+ /// \ref isc::config::ConfigData::getFullConfig().
///
/// @param new_config new configuration.
///
diff --git a/src/bin/dhcp4/dhcp4.dox b/src/bin/dhcp4/dhcp4.dox
index aa43ee3..22603f3 100644
--- a/src/bin/dhcp4/dhcp4.dox
+++ b/src/bin/dhcp4/dhcp4.dox
@@ -1,4 +1,4 @@
-// Copyright (C) 2012-2013 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2012-2014 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
@@ -185,6 +185,12 @@ library. See ticket #3275. The class specific behavior is:
Aforementioned modifications are conducted in @ref isc::dhcp::Dhcpv4Srv::classSpecificProcessing.
+It is possible to define class restrictions in subnet, so a given subnet is only
+accessible to clients that belong to a given class. That is implemented as isc::dhcp::Pkt4::classes_
+being passed in isc::dhcp::Dhcpv4Srv::selectSubnet() to isc::dhcp::CfgMgr::getSubnet4().
+Currently this capability is usable, but the number of scenarios it supports is
+limited.
+
@section dhcpv4Other Other DHCPv4 topics
For hooks API support in DHCPv4, see @ref dhcpv4Hooks.
diff --git a/src/bin/dhcp4/dhcp4.spec b/src/bin/dhcp4/dhcp4.spec
index c6d2c32..5de65b2 100644
--- a/src/bin/dhcp4/dhcp4.spec
+++ b/src/bin/dhcp4/dhcp4.spec
@@ -250,6 +250,28 @@
}
},
+ { "item_name": "client-class",
+ "item_type": "string",
+ "item_optional": false,
+ "item_default": "",
+ "item_description" : "Restricts access to this subnet to specified client class (if defined)"
+ },
+
+ { "item_name": "relay",
+ "item_type": "map",
+ "item_optional": false,
+ "item_default": {},
+ "item_description" : "Structure holding relay information.",
+ "map_item_spec": [
+ {
+ "item_name": "ip-address",
+ "item_type": "string",
+ "item_optional": false,
+ "item_default": "0.0.0.0",
+ "item_description" : "IPv4 address of the relay (defaults to 0.0.0.0 if not specified)."
+ }
+ ]
+ },
{ "item_name": "option-data",
"item_type": "list",
"item_optional": false,
@@ -336,13 +358,6 @@
"item_description" : "Format of the update request packet"
},
{
- "item_name": "remove-on-renew",
- "item_type": "boolean",
- "item_optional": true,
- "item_default": false,
- "item_description": "Enable requesting a DNS Remove, before a DNS Update on renewals"
- },
- {
"item_name": "always-include-fqdn",
"item_type": "boolean",
diff --git a/src/bin/dhcp4/dhcp4_messages.mes b/src/bin/dhcp4/dhcp4_messages.mes
index 297acfd..a7fc174 100644
--- a/src/bin/dhcp4/dhcp4_messages.mes
+++ b/src/bin/dhcp4/dhcp4_messages.mes
@@ -20,11 +20,11 @@ to receive DHCPv4 traffic. IPv4 socket on this interface will be opened once
Interface Manager starts up procedure of opening sockets.
% DHCP4_CCSESSION_STARTED control channel session started on socket %1
-A debug message issued during startup after the IPv4 DHCP server has
+A debug message issued during startup after the DHCPv4 server has
successfully established a session with the BIND 10 control channel.
% DHCP4_CCSESSION_STARTING starting control channel session, specfile: %1
-This debug message is issued just before the IPv4 DHCP server attempts
+This debug message is issued just before the DHCPv4 server attempts
to establish a session with the BIND 10 control channel.
% DHCP4_CLIENT_NAME_PROC_FAIL failed to process the fqdn or hostname sent by a client: %1
@@ -42,7 +42,7 @@ class or classes. This is a norma
% DHCP4_COMMAND_RECEIVED received command %1, arguments: %2
A debug message listing the command (and possible arguments) received
-from the BIND 10 control system by the IPv4 DHCP server.
+from the BIND 10 control system by the DHCPv4 server.
% DHCP4_CONFIG_COMPLETE DHCPv4 server has completed configuration: %1
This is an informational message announcing the successful processing of a
@@ -70,7 +70,7 @@ configuration. That happens at start up and also when a server configuration
change is committed by the administrator.
% DHCP4_CONFIG_UPDATE updated configuration received: %1
-A debug message indicating that the IPv4 DHCP server has received an
+A debug message indicating that the DHCPv4 server has received an
updated configuration from the BIND 10 configuration system.
% DHCP4_DB_BACKEND_STARTED lease database started (type: %1, name: %2)
@@ -135,6 +135,18 @@ A "libreload" command was issued to reload the hooks libraries but for
some reason the reload failed. Other error messages issued from the
hooks framework will indicate the nature of the problem.
+% DHCP4_UNRECOGNIZED_RCVD_PACKET_TYPE received message (transaction id %1) has unrecognized type %2 in option 53
+This debug message indicates that the message type carried in DHCPv4 option
+53 is unrecognized by the server. The valid message types are listed
+on the IANA website: http://www.iana.org/assignments/bootp-dhcp-parameters/bootp-dhcp-parameters.xhtml#message-type-53.
+The message will not be processed by the server.
+
+% DHCP4_UNSUPPORTED_RCVD_PACKET_TYPE received message (transaction id %1), having type %2 is not supported
+This debug message indicates that the message type carried in DHCPv4 option
+53 is valid but the message will not be processed by the server. This includes
+messages being normally sent by the server to the client, such as Offer, ACK,
+NAK etc.
+
% DHCP4_LEASE_ADVERT lease %1 advertised (client client-id %2, hwaddr %3)
This debug message indicates that the server successfully advertised
a lease. It is up to the client to choose one server out of othe advertised
@@ -175,12 +187,19 @@ This warning message is issued when current server configuration specifies
no interfaces that server should listen on, or specified interfaces are not
configured to receive the traffic.
-% DHCP4_NOT_RUNNING IPv4 DHCP server is not running
+% DHCP4_NO_SUBNET_FOR_DIRECT_CLIENT no suitable subnet configured for a direct client sending packet with transaction id %1, on interface %2, received message is dropped
+This info messsage is logged when received a message from a directly connected
+client but there is no suitable subnet configured for the interface on
+which this message has been received. The IPv4 address assigned on this
+interface must belong to one of the configured subnets. Otherwise
+received message is dropped.
+
+% DHCP4_NOT_RUNNING DHCPv4 server is not running
A warning message is issued when an attempt is made to shut down the
-IPv4 DHCP server but it is not running.
+DHCPv4 server but it is not running.
% DHCP4_OPEN_SOCKET opening sockets on port %1
-A debug message issued during startup, this indicates that the IPv4 DHCP
+A debug message issued during startup, this indicates that the DHCPv4
server is about to open sockets on the specified port.
% DHCP4_PACKET_NOT_FOR_US received DHCPv4 message (transid=%1, iface=%2) dropped because it contains foreign server identifier
@@ -195,7 +214,7 @@ A warning message issued when IfaceMgr fails to open and bind a socket. The reas
for the failure is appended as an argument of the log message.
% DHCP4_PACKET_PARSE_FAIL failed to parse incoming packet: %1
-The IPv4 DHCP server has received a packet that it is unable to
+The DHCPv4 server has received a packet that it is unable to
interpret. The reason why the packet is invalid is included in the message.
% DHCP4_PACKET_PROCESS_FAIL failed to process packet received from %1: %2
@@ -214,35 +233,35 @@ may well be a valid DHCP packet, just a type not expected by the server
(e.g. it will report a received OFFER packet as UNKNOWN).
% DHCP4_PACKET_RECEIVE_FAIL error on attempt to receive packet: %1
-The IPv4 DHCP server tried to receive a packet but an error
+The DHCPv4 server tried to receive a packet but an error
occurred during this attempt. The reason for the error is included in
the message.
% DHCP4_PACKET_SEND_FAIL failed to send DHCPv4 packet: %1
-This error is output if the IPv4 DHCP server fails to send an assembled
+This error is output if the DHCPv4 server fails to send an assembled
DHCP message to a client. The reason for the error is included in the
message.
% DHCP4_PARSER_COMMIT_EXCEPTION parser failed to commit changes
-On receipt of message containing details to a change of the IPv4 DHCP
+On receipt of message containing details to a change of the DHCPv4
server configuration, a set of parsers were successfully created, but one
of them failed to commit its changes due to a low-level system exception
being raised. Additional messages may be output indicating the reason.
% DHCP4_PARSER_COMMIT_FAIL parser failed to commit changes: %1
-On receipt of message containing details to a change of the IPv4 DHCP
+On receipt of message containing details to a change of the DHCPv4
server configuration, a set of parsers were successfully created, but
one of them failed to commit its changes. The reason for the failure
is given in the message.
% DHCP4_PARSER_CREATED created parser for configuration element %1
-A debug message output during a configuration update of the IPv4 DHCP
+A debug message output during a configuration update of the DHCPv4
server, notifying that the parser for the specified configuration element
has been successfully created.
% DHCP4_PARSER_EXCEPTION failed to create or run parser for configuration element %1
On receipt of message containing details to a change of its configuration,
-the IPv4 DHCP server failed to create a parser to decode the contents of
+the DHCPv4 server failed to create a parser to decode the contents of
the named configuration element, or the creation succeeded but the parsing
actions and committal of changes failed. The message has been output in
response to a non-BIND 10 exception being raised. Additional messages
@@ -250,7 +269,7 @@ may give further information.
% DHCP4_PARSER_FAIL failed to create or run parser for configuration element %1: %2
On receipt of message containing details to a change of its configuration,
-the IPv4 DHCP server failed to create a parser to decode the contents
+the DHCPv4 server failed to create a parser to decode the contents
of the named configuration element, or the creation succeeded but the
parsing actions and committal of changes failed. The reason for the
failure is given in the message.
@@ -305,40 +324,40 @@ both clones use the same client-id.
A debug message listing the data returned to the client.
% DHCP4_SERVER_FAILED server failed: %1
-The IPv4 DHCP server has encountered a fatal error and is terminating.
+The DHCPv4 server has encountered a fatal error and is terminating.
The reason for the failure is included in the message.
% DHCP4_SESSION_FAIL failed to establish BIND 10 session (%1), running stand-alone
The server has failed to establish communication with the rest of BIND
10 and is running in stand-alone mode. (This behavior will change once
-the IPv4 DHCP server is properly integrated with the rest of BIND 10.)
+the DHCPv4 server is properly integrated with the rest of BIND 10.)
% DHCP4_SHUTDOWN server shutdown
-The IPv4 DHCP server has terminated normally.
+The DHCPv4 server has terminated normally.
% DHCP4_SHUTDOWN_REQUEST shutdown of server requested
-This debug message indicates that a shutdown of the IPv4 server has
+This debug message indicates that a shutdown of the DHCPv4 server has
been requested via a call to the 'shutdown' method of the core Dhcpv4Srv
object.
% DHCP4_SRV_CONSTRUCT_ERROR error creating Dhcpv4Srv object, reason: %1
This error message indicates that during startup, the construction of a
-core component within the IPv4 DHCP server (the Dhcpv4 server object)
+core component within the DHCPv4 server (the Dhcpv4 server object)
has failed. As a result, the server will exit. The reason for the
failure is given within the message.
% DHCP4_STANDALONE skipping message queue, running standalone
-This is a debug message indicating that the IPv4 server is running in
+This is a debug message indicating that the DHCPv4 server is running in
standalone mode, not connected to the message queue. Standalone mode
is only useful during program development, and should not be used in a
production environment.
% DHCP4_STARTING server starting
-This informational message indicates that the IPv4 DHCP server has
+This informational message indicates that the DHCPv4 server has
processed any command-line switches and is starting.
% DHCP4_START_INFO pid: %1, port: %2, verbose: %3, standalone: %4
-This is a debug message issued during the IPv4 DHCP server startup.
+This is a debug message issued during the DHCPv4 server startup.
It lists some information about the parameters with which the server
is running.
@@ -351,3 +370,8 @@ steps in the processing of incoming client message.
This warning message is output when a packet was received from a subnet
for which the DHCPv4 server has not been configured. The most probable
cause is a misconfiguration of the server.
+
+% DHCP4_DDNS_REQUEST_SEND_FAILED failed sending a request to b10-dhcp-ddns, error: %1, ncr: %2
+This error message indicates that DHCP4 server attempted to send a DDNS
+update reqeust to the DHCP-DDNS server. This is most likely a configuration or
+networking error.
diff --git a/src/bin/dhcp4/dhcp4_srv.cc b/src/bin/dhcp4/dhcp4_srv.cc
index 5fc5598..0608b93 100644
--- a/src/bin/dhcp4/dhcp4_srv.cc
+++ b/src/bin/dhcp4/dhcp4_srv.cc
@@ -80,36 +80,6 @@ Dhcp4Hooks Hooks;
namespace isc {
namespace dhcp {
-namespace {
-
-// @todo The following constants describe server's behavior with respect to the
-// DHCPv4 Client FQDN Option sent by a client. They will be removed
-// when DDNS parameters for DHCPv4 are implemented with the ticket #3033.
-
-// @todo Additional configuration parameter which we may consider is the one
-// that controls whether the DHCP server sends the removal NameChangeRequest
-// if it discovers that the entry for the particular client exists or that
-// it always updates the DNS.
-
-// Should server always include the FQDN option in its response, regardless
-// if it has been requested in Parameter Request List Option (Disabled).
-const bool FQDN_ALWAYS_INCLUDE = false;
-// Enable A RR update delegation to the client (Disabled).
-const bool FQDN_ALLOW_CLIENT_UPDATE = false;
-// Globally enable updates (Enabled).
-const bool FQDN_ENABLE_UPDATE = true;
-// Do update, even if client requested no updates with N flag (Disabled).
-const bool FQDN_OVERRIDE_NO_UPDATE = false;
-// Server performs an update when client requested delegation (Enabled).
-const bool FQDN_OVERRIDE_CLIENT_UPDATE = true;
-// The fully qualified domain-name suffix if partial name provided by
-// a client.
-const char* FQDN_PARTIAL_SUFFIX = "example.com";
-// Should server replace the domain-name supplied by the client (Disabled).
-const bool FQDN_REPLACE_CLIENT_NAME = false;
-
-}
-
Dhcpv4Srv::Dhcpv4Srv(uint16_t port, const char* dbconfig, const bool use_bcast,
const bool direct_response_desired)
: shutdown_(true), alloc_engine_(), port_(port),
@@ -260,26 +230,15 @@ Dhcpv4Srv::run() {
}
}
- // Check if the DHCPv4 packet has been sent to us or to someone else.
- // If it hasn't been sent to us, drop it!
- if (!acceptServerId(query)) {
- LOG_DEBUG(dhcp4_logger, DBG_DHCP4_DETAIL, DHCP4_PACKET_NOT_FOR_US)
- .arg(query->getTransid())
- .arg(query->getIface());
- continue;
- }
-
- // When receiving a packet without message type option, getType() will
- // throw. Let's set type to -1 as default error indicator.
- int type = -1;
- try {
- type = query->getType();
- } catch (...) {
- LOG_DEBUG(dhcp4_logger, DBG_DHCP4_DETAIL, DHCP4_PACKET_DROP_NO_TYPE)
- .arg(query->getIface());
+ // Check whether the message should be further processed or discarded.
+ // There is no need to log anything here. This function logs by itself.
+ if (!accept(query)) {
continue;
}
+ // We have sanity checked (in accept() that the Message Type option
+ // exists, so we can safely get it here.
+ int type = query->getType();
LOG_DEBUG(dhcp4_logger, DBG_DHCP4_DETAIL, DHCP4_PACKET_RECEIVED)
.arg(serverReceivedPacketName(type))
.arg(type)
@@ -461,9 +420,6 @@ Dhcpv4Srv::run() {
LOG_ERROR(dhcp4_logger, DHCP4_PACKET_SEND_FAIL)
.arg(e.what());
}
-
- // Send NameChangeRequests to the b10-dhcp_ddns module.
- sendNameChangeRequests();
}
return (true);
@@ -596,8 +552,8 @@ Dhcpv4Srv::appendServerID(const Pkt4Ptr& response) {
// The source address for the outbound message should have been set already.
// This is the address that to the best of the server's knowledge will be
// available from the client.
- // @todo: perhaps we should consider some more sophisticated server id
- // generation, but for the current use cases, it should be ok.
+ /// @todo: perhaps we should consider some more sophisticated server id
+ /// generation, but for the current use cases, it should be ok.
response->addOption(OptionPtr(new Option4AddrLst(DHO_DHCP_SERVER_IDENTIFIER,
response->getLocalAddr()))
);
@@ -666,8 +622,8 @@ Dhcpv4Srv::appendRequestedVendorOptions(const Pkt4Ptr& question, Pkt4Ptr& answer
uint32_t vendor_id = vendor_req->getVendorId();
// Let's try to get ORO within that vendor-option
- /// @todo This is very specific to vendor-id=4491 (Cable Labs). Other vendors
- /// may have different policies.
+ /// @todo This is very specific to vendor-id=4491 (Cable Labs). Other
+ /// vendors may have different policies.
OptionUint8ArrayPtr oro =
boost::dynamic_pointer_cast<OptionUint8Array>(vendor_req->getOption(DOCSIS3_V4_ORO));
@@ -773,68 +729,19 @@ Dhcpv4Srv::processClientFqdnOption(const Option4ClientFqdnPtr& fqdn,
// response to a client.
Option4ClientFqdnPtr fqdn_resp(new Option4ClientFqdn(*fqdn));
- // RFC4702, section 4 - set 'NOS' flags to 0.
- fqdn_resp->setFlag(Option4ClientFqdn::FLAG_S, 0);
- fqdn_resp->setFlag(Option4ClientFqdn::FLAG_O, 0);
- fqdn_resp->setFlag(Option4ClientFqdn::FLAG_N, 0);
-
- // Conditions when N flag has to be set to indicate that server will not
- // perform DNS updates:
- // 1. Updates are globally disabled,
- // 2. Client requested no update and server respects it,
- // 3. Client requested that the forward DNS update is delegated to the
- // client but server neither respects requests for forward update
- // delegation nor it is configured to send update on its own when
- // client requested delegation.
- if (!FQDN_ENABLE_UPDATE ||
- (fqdn->getFlag(Option4ClientFqdn::FLAG_N) &&
- !FQDN_OVERRIDE_NO_UPDATE) ||
- (!fqdn->getFlag(Option4ClientFqdn::FLAG_S) &&
- !FQDN_ALLOW_CLIENT_UPDATE && !FQDN_OVERRIDE_CLIENT_UPDATE)) {
- fqdn_resp->setFlag(Option4ClientFqdn::FLAG_N, true);
-
- // Conditions when S flag is set to indicate that server will perform DNS
- // update on its own:
- // 1. Client requested that server performs DNS update and DNS updates are
- // globally enabled.
- // 2. Client requested that server delegates forward update to the client
- // but server doesn't respect requests for delegation and it is
- // configured to perform an update on its own when client requested the
- // delegation.
- } else if (fqdn->getFlag(Option4ClientFqdn::FLAG_S) ||
- (!fqdn->getFlag(Option4ClientFqdn::FLAG_S) &&
- !FQDN_ALLOW_CLIENT_UPDATE && FQDN_OVERRIDE_CLIENT_UPDATE)) {
- fqdn_resp->setFlag(Option4ClientFqdn::FLAG_S, true);
- }
-
- // Server MUST set the O flag if it has overriden the client's setting
- // of S flag.
- if (fqdn->getFlag(Option4ClientFqdn::FLAG_S) !=
- fqdn_resp->getFlag(Option4ClientFqdn::FLAG_S)) {
- fqdn_resp->setFlag(Option4ClientFqdn::FLAG_O, true);
- }
-
- // If client suppled partial or empty domain-name, server should generate
- // one.
- if (fqdn->getDomainNameType() == Option4ClientFqdn::PARTIAL) {
- std::ostringstream name;
- if (fqdn->getDomainName().empty() || FQDN_REPLACE_CLIENT_NAME) {
- fqdn_resp->setDomainName("", Option4ClientFqdn::PARTIAL);
+ // Set the server S, N, and O flags based on client's flags and
+ // current configuration.
+ D2ClientMgr& d2_mgr = CfgMgr::instance().getD2ClientMgr();
+ d2_mgr.adjustFqdnFlags<Option4ClientFqdn>(*fqdn, *fqdn_resp);
- } else {
- name << fqdn->getDomainName();
- name << "." << FQDN_PARTIAL_SUFFIX;
- fqdn_resp->setDomainName(name.str(), Option4ClientFqdn::FULL);
+ // Carry over the client's E flag.
+ fqdn_resp->setFlag(Option4ClientFqdn::FLAG_E,
+ fqdn->getFlag(Option4ClientFqdn::FLAG_E));
- }
- // Server may be configured to replace a name supplied by a client, even if
- // client supplied fully qualified domain-name. The empty domain-name is
- // is set to indicate that the name must be generated when the new lease
- // is acquired.
- } else if(FQDN_REPLACE_CLIENT_NAME) {
- fqdn_resp->setDomainName("", Option4ClientFqdn::PARTIAL);
- }
+ // Adjust the domain name based on domain name value and type sent by the
+ // client and current configuration.
+ d2_mgr.adjustDomainName<Option4ClientFqdn>(*fqdn, *fqdn_resp);
// Add FQDN option to the response message. Note that, there may be some
// cases when server may choose not to include the FQDN option in a
@@ -854,15 +761,20 @@ Dhcpv4Srv::processClientFqdnOption(const Option4ClientFqdnPtr& fqdn,
void
Dhcpv4Srv::processHostnameOption(const OptionCustomPtr& opt_hostname,
Pkt4Ptr& answer) {
+ // Fetch D2 configuration.
+ D2ClientMgr& d2_mgr = CfgMgr::instance().getD2ClientMgr();
+
// Do nothing if the DNS updates are disabled.
- if (!FQDN_ENABLE_UPDATE) {
+ if (!d2_mgr.ddnsEnabled()) {
return;
}
std::string hostname = isc::util::str::trim(opt_hostname->readString());
unsigned int label_count = OptionDataTypeUtil::getLabelCount(hostname);
// The hostname option sent by the client should be at least 1 octet long.
- // If it isn't we ignore this option.
+ // If it isn't we ignore this option. (Per RFC 2131, section 3.14)
+ /// @todo It would be more liberal to accept this and let it fall into
+ /// the case of replace or less than two below.
if (label_count == 0) {
LOG_DEBUG(dhcp4_logger, DBG_DHCP4_DETAIL_DATA, DHCP4_EMPTY_HOSTNAME);
return;
@@ -880,21 +792,20 @@ Dhcpv4Srv::processHostnameOption(const OptionCustomPtr& opt_hostname,
// By checking the number of labels present in the hostname we may infer
// whether client has sent the fully qualified or unqualified hostname.
- // @todo We may want to reconsider whether it is appropriate for the
- // client to send a root domain name as a Hostname. There are
- // also extensions to the auto generation of the client's name,
- // e.g. conversion to the puny code which may be considered at some point.
- // For now, we just remain liberal and expect that the DNS will handle
- // conversion if needed and possible.
- if (FQDN_REPLACE_CLIENT_NAME || (label_count < 2)) {
+ /// @todo We may want to reconsider whether it is appropriate for the
+ /// client to send a root domain name as a Hostname. There are
+ /// also extensions to the auto generation of the client's name,
+ /// e.g. conversion to the puny code which may be considered at some point.
+ /// For now, we just remain liberal and expect that the DNS will handle
+ /// conversion if needed and possible.
+ if ((d2_mgr.getD2ClientConfig()->getReplaceClientName()) ||
+ (label_count < 2)) {
opt_hostname_resp->writeString("");
- // If there are two labels, it means that the client has specified
- // the unqualified name. We have to concatenate the unqalified name
- // with the domain name.
} else if (label_count == 2) {
- std::ostringstream resp_hostname;
- resp_hostname << hostname << "." << FQDN_PARTIAL_SUFFIX << ".";
- opt_hostname_resp->writeString(resp_hostname.str());
+ // If there are two labels, it means that the client has specified
+ // the unqualified name. We have to concatenate the unqalified name
+ // with the domain name.
+ opt_hostname_resp->writeString(d2_mgr.qualifyName(hostname));
}
answer->addOption(opt_hostname_resp);
@@ -927,17 +838,14 @@ Dhcpv4Srv::createNameChangeRequests(const Lease4Ptr& lease,
// removal request for non-existent hostname.
// - A server has performed reverse, forward or both updates.
// - FQDN data between the new and old lease do not match.
- if ((lease->hostname_ != old_lease->hostname_) ||
- (lease->fqdn_fwd_ != old_lease->fqdn_fwd_) ||
- (lease->fqdn_rev_ != old_lease->fqdn_rev_)) {
+ if (!lease->hasIdenticalFqdn(*old_lease)) {
queueNameChangeRequest(isc::dhcp_ddns::CHG_REMOVE,
old_lease);
// If FQDN data from both leases match, there is no need to update.
- } else if ((lease->hostname_ == old_lease->hostname_) &&
- (lease->fqdn_fwd_ == old_lease->fqdn_fwd_) &&
- (lease->fqdn_rev_ == old_lease->fqdn_rev_)) {
+ } else if (lease->hasIdenticalFqdn(*old_lease)) {
return;
+
}
}
@@ -970,26 +878,23 @@ queueNameChangeRequest(const isc::dhcp_ddns::NameChangeType chg_type,
.arg(ex.what());
return;
}
+
// Create NameChangeRequest
- NameChangeRequest ncr(chg_type, lease->fqdn_fwd_, lease->fqdn_rev_,
- lease->hostname_, lease->addr_.toText(),
- dhcid, lease->cltt_ + lease->valid_lft_,
- lease->valid_lft_);
- // And queue it.
+ NameChangeRequestPtr ncr(new NameChangeRequest(chg_type, lease->fqdn_fwd_,
+ lease->fqdn_rev_,
+ lease->hostname_,
+ lease->addr_.toText(),
+ dhcid,
+ (lease->cltt_ +
+ lease->valid_lft_),
+ lease->valid_lft_));
+
LOG_DEBUG(dhcp4_logger, DBG_DHCP4_DETAIL_DATA, DHCP4_QUEUE_NCR)
.arg(chg_type == CHG_ADD ? "add" : "remove")
- .arg(ncr.toText());
- name_change_reqs_.push(ncr);
-}
+ .arg(ncr->toText());
-void
-Dhcpv4Srv::sendNameChangeRequests() {
- while (!name_change_reqs_.empty()) {
- // @todo Once next NameChangeRequest is picked from the queue
- // we should send it to the b10-dhcp_ddns module. Currently we
- // just drop it.
- name_change_reqs_.pop();
- }
+ // And pass it to the the manager.
+ CfgMgr::instance().getD2ClientMgr().sendRequest(ncr);
}
void
@@ -1006,8 +911,8 @@ Dhcpv4Srv::assignLease(const Pkt4Ptr& question, Pkt4Ptr& answer) {
// thing this client can get is some global information (like DNS
// servers).
- // perhaps this should be logged on some higher level? This is most likely
- // configuration bug.
+ // perhaps this should be logged on some higher level? This is most
+ // likely configuration bug.
LOG_ERROR(dhcp4_logger, DHCP4_SUBNET_SELECTION_FAILED)
.arg(question->getRemoteAddr().toText())
.arg(serverReceivedPacketName(question->getType()));
@@ -1020,7 +925,7 @@ Dhcpv4Srv::assignLease(const Pkt4Ptr& question, Pkt4Ptr& answer) {
// as siaddr has nothing to do with a lease, but otherwise we would have
// to select subnet twice (performance hit) or update too many functions
// at once.
- // @todo: move subnet selection to a common code
+ /// @todo: move subnet selection to a common code
answer->setSiaddr(subnet->getSiaddr());
LOG_DEBUG(dhcp4_logger, DBG_DHCP4_DETAIL_DATA, DHCP4_SUBNET_SELECTED)
@@ -1063,8 +968,8 @@ Dhcpv4Srv::assignLease(const Pkt4Ptr& question, Pkt4Ptr& answer) {
(answer->getOption(DHO_HOST_NAME));
if (opt_hostname) {
hostname = opt_hostname->readString();
- // @todo It could be configurable what sort of updates the server
- // is doing when Hostname option was sent.
+ /// @todo It could be configurable what sort of updates the
+ /// server is doing when Hostname option was sent.
fqdn_fwd = true;
fqdn_rev = true;
}
@@ -1074,7 +979,7 @@ Dhcpv4Srv::assignLease(const Pkt4Ptr& question, Pkt4Ptr& answer) {
// will try to honour the hint, but it is just a hint - some other address
// may be used instead. If fake_allocation is set to false, the lease will
// be inserted into the LeaseMgr as well.
- // @todo pass the actual FQDN data.
+ /// @todo pass the actual FQDN data.
Lease4Ptr old_lease;
Lease4Ptr lease = alloc_engine_->allocateLease4(subnet, client_id, hwaddr,
hint, fqdn_fwd, fqdn_rev,
@@ -1099,14 +1004,9 @@ Dhcpv4Srv::assignLease(const Pkt4Ptr& question, Pkt4Ptr& answer) {
// generating the entire hostname for the client. The example of the
// client's name, generated from the IP address is: host-192-0-2-3.
if ((fqdn || opt_hostname) && lease->hostname_.empty()) {
- hostname = lease->addr_.toText();
- // Replace dots with hyphens.
- std::replace(hostname.begin(), hostname.end(), '.', '-');
- ostringstream stream;
- // The partial suffix will need to be replaced with the actual
- // domain-name for the client when configuration is implemented.
- stream << "host-" << hostname << "." << FQDN_PARTIAL_SUFFIX << ".";
- lease->hostname_ = stream.str();
+ lease->hostname_ = CfgMgr::instance()
+ .getD2ClientMgr().generateFqdn(lease->addr_);
+
// The operations below are rather safe, but we want to catch
// any potential exceptions (e.g. invalid lease database backend
// implementation) and log an error.
@@ -1138,22 +1038,18 @@ Dhcpv4Srv::assignLease(const Pkt4Ptr& question, Pkt4Ptr& answer) {
// Subnet mask (type 1)
answer->addOption(getNetmaskOption(subnet));
- // @todo: send renew timer option (T1, option 58)
- // @todo: send rebind timer option (T2, option 59)
+ /// @todo: send renew timer option (T1, option 58)
+ /// @todo: send rebind timer option (T2, option 59)
- // @todo Currently the NameChangeRequests are always generated if
- // real (not fake) allocation is being performed. Should we have
- // control switch to enable/disable NameChangeRequest creation?
- // Perhaps we need a way to detect whether the b10-dhcp-ddns module
- // is up an running?
- if (!fake_allocation) {
+ // Create NameChangeRequests if DDNS is enabled and this is a
+ // real allocation.
+ if (!fake_allocation && CfgMgr::instance().ddnsEnabled()) {
try {
createNameChangeRequests(lease, old_lease);
} catch (const Exception& ex) {
LOG_ERROR(dhcp4_logger, DHCP4_NCR_CREATION_FAILED)
.arg(ex.what());
}
-
}
} else {
@@ -1194,8 +1090,8 @@ Dhcpv4Srv::adjustIfaceData(const Pkt4Ptr& query, const Pkt4Ptr& response) {
// address for the response. Instead, we have to check what address our
// socket is bound to and use it as a source address. This operation
// may throw if for some reason the socket is closed.
- // @todo Consider an optimization that we use local address from
- // the query if this address is not broadcast.
+ /// @todo Consider an optimization that we use local address from
+ /// the query if this address is not broadcast.
SocketInfo sock_info = IfaceMgr::instance().getSocket(*query);
// Set local adddress, port and interface.
response->setLocalAddr(sock_info.addr_);
@@ -1310,7 +1206,7 @@ Pkt4Ptr
Dhcpv4Srv::processRequest(Pkt4Ptr& request) {
/// @todo Uncomment this (see ticket #3116)
- // sanityCheck(request, MANDATORY);
+ /// sanityCheck(request, MANDATORY);
Pkt4Ptr ack = Pkt4Ptr
(new Pkt4(DHCPACK, request->getTransid()));
@@ -1352,7 +1248,7 @@ void
Dhcpv4Srv::processRelease(Pkt4Ptr& release) {
/// @todo Uncomment this (see ticket #3116)
- // sanityCheck(release, MANDATORY);
+ /// sanityCheck(release, MANDATORY);
// Try to find client-id
ClientIdPtr client_id;
@@ -1377,7 +1273,7 @@ Dhcpv4Srv::processRelease(Pkt4Ptr& release) {
// Does the hardware address match? We don't want one client releasing
// second client's leases.
if (lease->hwaddr_ != release->getHWAddr()->hwaddr_) {
- // @todo: Print hwaddr from lease as part of ticket #2589
+ /// @todo: Print hwaddr from lease as part of ticket #2589
LOG_DEBUG(dhcp4_logger, DBG_DHCP4_DETAIL, DHCP4_RELEASE_FAIL_WRONG_HWADDR)
.arg(release->getCiaddr().toText())
.arg(client_id ? client_id->toText() : "(no client-id)")
@@ -1435,9 +1331,10 @@ Dhcpv4Srv::processRelease(Pkt4Ptr& release) {
.arg(client_id ? client_id->toText() : "(no client-id)")
.arg(release->getHWAddr()->toText());
- // Remove existing DNS entries for the lease, if any.
- queueNameChangeRequest(isc::dhcp_ddns::CHG_REMOVE, lease);
-
+ if (CfgMgr::instance().ddnsEnabled()) {
+ // Remove existing DNS entries for the lease, if any.
+ queueNameChangeRequest(isc::dhcp_ddns::CHG_REMOVE, lease);
+ }
} else {
// Release failed -
LOG_ERROR(dhcp4_logger, DHCP4_RELEASE_FAIL)
@@ -1499,24 +1396,41 @@ Dhcpv4Srv::serverReceivedPacketName(uint8_t type) {
}
Subnet4Ptr
-Dhcpv4Srv::selectSubnet(const Pkt4Ptr& question) {
+Dhcpv4Srv::selectSubnet(const Pkt4Ptr& question) const {
Subnet4Ptr subnet;
- // Is this relayed message?
- IOAddress relay = question->getGiaddr();
static const IOAddress notset("0.0.0.0");
+ static const IOAddress bcast("255.255.255.255");
- if (relay != notset) {
- // Yes: Use relay address to select subnet
- subnet = CfgMgr::instance().getSubnet4(relay);
+ // If a message is relayed, use the relay (giaddr) address to select subnet
+ // for the client. Note that this may result in exception if the value
+ // of hops does not correspond with the Giaddr. Such message is considered
+ // to be malformed anyway and the message will be dropped by the higher
+ // level functions.
+ if (question->isRelayed()) {
+ subnet = CfgMgr::instance().getSubnet4(question->getGiaddr(),
+ question->classes_);
+
+ // The message is not relayed so it is sent directly by a client. But
+ // the client may be renewing its lease and in such case it unicasts
+ // its message to the server. Note that the unicast Request bypasses
+ // relays and the client may be in a different network, so we can't
+ // use IP address on the local interface to get the subnet. Instead,
+ // we rely on the client's address to get the subnet.
+ } else if ((question->getLocalAddr() != bcast) &&
+ (question->getCiaddr() != notset)) {
+ subnet = CfgMgr::instance().getSubnet4(question->getCiaddr(),
+ question->classes_);
+
+ // The message has been received from a directly connected client
+ // and this client appears to have no address. The IPv4 address
+ // assigned to the interface on which this message has been received,
+ // will be used to determine the subnet suitable for the client.
} else {
-
- // No: Use client's address to select subnet
- subnet = CfgMgr::instance().getSubnet4(question->getRemoteAddr());
+ subnet = CfgMgr::instance().getSubnet4(question->getIface(),
+ question->classes_);
}
- /// @todo Implement getSubnet4(interface-name)
-
// Let's execute all callouts registered for subnet4_select
if (HooksManager::calloutsPresent(hook_index_subnet4_select_)) {
CalloutHandlePtr callout_handle = getCalloutHandle(question);
@@ -1551,7 +1465,93 @@ Dhcpv4Srv::selectSubnet(const Pkt4Ptr& question) {
}
bool
-Dhcpv4Srv::acceptServerId(const Pkt4Ptr& pkt) const {
+Dhcpv4Srv::accept(const Pkt4Ptr& query) const {
+ // Check if the message from directly connected client (if directly
+ // connected) should be dropped or processed.
+ if (!acceptDirectRequest(query)) {
+ LOG_INFO(dhcp4_logger, DHCP4_NO_SUBNET_FOR_DIRECT_CLIENT)
+ .arg(query->getTransid())
+ .arg(query->getIface());
+ return (false);
+ }
+
+ // Check if the DHCPv4 packet has been sent to us or to someone else.
+ // If it hasn't been sent to us, drop it!
+ if (!acceptServerId(query)) {
+ LOG_DEBUG(dhcp4_logger, DBG_DHCP4_DETAIL, DHCP4_PACKET_NOT_FOR_US)
+ .arg(query->getTransid())
+ .arg(query->getIface());
+ return (false);
+ }
+
+ // Check that the message type is accepted by the server. We rely on the
+ // function called to log a message if needed.
+ if (!acceptMessageType(query)) {
+ return (false);
+ }
+
+ return (true);
+}
+
+bool
+Dhcpv4Srv::acceptDirectRequest(const Pkt4Ptr& pkt) const {
+ try {
+ if (pkt->isRelayed()) {
+ return (true);
+ }
+ } catch (const Exception& ex) {
+ return (false);
+ }
+ static const IOAddress bcast("255.255.255.255");
+ return ((pkt->getLocalAddr() != bcast || selectSubnet(pkt)));
+}
+
+bool
+Dhcpv4Srv::acceptMessageType(const Pkt4Ptr& query) const {
+ // When receiving a packet without message type option, getType() will
+ // throw.
+ int type;
+ try {
+ type = query->getType();
+
+ } catch (...) {
+ LOG_DEBUG(dhcp4_logger, DBG_DHCP4_DETAIL, DHCP4_PACKET_DROP_NO_TYPE)
+ .arg(query->getIface());
+ return (false);
+ }
+
+ // If we receive a message with a non-existing type, we are logging it.
+ if (type > DHCPLEASEQUERYDONE) {
+ LOG_DEBUG(dhcp4_logger, DBG_DHCP4_DETAIL,
+ DHCP4_UNRECOGNIZED_RCVD_PACKET_TYPE)
+ .arg(type)
+ .arg(query->getTransid());
+ return (false);
+ }
+
+ // Once we know that the message type is within a range of defined DHCPv4
+ // messages, we do a detailed check to make sure that the received message
+ // is targeted at server. Note that we could have received some Offer
+ // message broadcasted by the other server to a relay. Even though, the
+ // server would rather unicast its response to a relay, let's be on the
+ // safe side. Also, we want to drop other messages which we don't support.
+ // All these valid messages that we are not going to process are dropped
+ // silently.
+ if ((type != DHCPDISCOVER) && (type != DHCPREQUEST) &&
+ (type != DHCPRELEASE) && (type != DHCPDECLINE) &&
+ (type != DHCPINFORM)) {
+ LOG_DEBUG(dhcp4_logger, DBG_DHCP4_DETAIL,
+ DHCP4_UNSUPPORTED_RCVD_PACKET_TYPE)
+ .arg(type)
+ .arg(query->getTransid());
+ return (false);
+ }
+
+ return (true);
+}
+
+bool
+Dhcpv4Srv::acceptServerId(const Pkt4Ptr& query) const {
// This function is meant to be called internally by the server class, so
// we rely on the caller to sanity check the pointer and we don't check
// it here.
@@ -1561,7 +1561,7 @@ Dhcpv4Srv::acceptServerId(const Pkt4Ptr& pkt) const {
// Note that we don't check cases that server identifier is mandatory
// but not present. This is meant to be sanity checked in other
// functions.
- OptionPtr option = pkt->getOption(DHO_DHCP_SERVER_IDENTIFIER);
+ OptionPtr option = query->getOption(DHO_DHCP_SERVER_IDENTIFIER);
if (!option) {
return (true);
}
@@ -1678,8 +1678,8 @@ Dhcpv4Srv::openActiveSockets(const uint16_t port,
}
// Let's reopen active sockets. openSockets4 will check internally whether
// sockets are marked active or inactive.
- // @todo Optimization: we should not reopen all sockets but rather select
- // those that have been affected by the new configuration.
+ /// @todo Optimization: we should not reopen all sockets but rather select
+ /// those that have been affected by the new configuration.
isc::dhcp::IfaceMgrErrorMsgCallback error_handler =
boost::bind(&Dhcpv4Srv::ifaceMgrSocket4ErrorHandler, _1);
if (!IfaceMgr::instance().openSockets4(port, use_bcast, error_handler)) {
@@ -1689,8 +1689,8 @@ Dhcpv4Srv::openActiveSockets(const uint16_t port,
size_t
Dhcpv4Srv::unpackOptions(const OptionBuffer& buf,
- const std::string& option_space,
- isc::dhcp::OptionCollection& options) {
+ const std::string& option_space,
+ isc::dhcp::OptionCollection& options) {
size_t offset = 0;
OptionDefContainer option_defs;
@@ -1863,5 +1863,29 @@ bool Dhcpv4Srv::classSpecificProcessing(const Pkt4Ptr& query, const Pkt4Ptr& rsp
return (true);
}
+void
+Dhcpv4Srv::startD2() {
+ D2ClientMgr& d2_mgr = CfgMgr::instance().getD2ClientMgr();
+ if (d2_mgr.ddnsEnabled()) {
+ // Updates are enabled, so lets start the sender, passing in
+ // our error handler.
+ // This may throw so wherever this is called needs to ready.
+ d2_mgr.startSender(boost::bind(&Dhcpv4Srv::d2ClientErrorHandler,
+ this, _1, _2));
+ }
+}
+
+void
+Dhcpv4Srv::d2ClientErrorHandler(const
+ dhcp_ddns::NameChangeSender::Result result,
+ dhcp_ddns::NameChangeRequestPtr& ncr) {
+ LOG_ERROR(dhcp4_logger, DHCP4_DDNS_REQUEST_SEND_FAILED).
+ arg(result).arg((ncr ? ncr->toText() : " NULL "));
+ // We cannot communicate with b10-dhcp-ddns, suspend futher updates.
+ /// @todo We may wish to revisit this, but for now we will simpy turn
+ /// them off.
+ CfgMgr::instance().getD2ClientMgr().suspendUpdates();
+}
+
} // namespace dhcp
} // namespace isc
diff --git a/src/bin/dhcp4/dhcp4_srv.h b/src/bin/dhcp4/dhcp4_srv.h
index ec63e7f..b08df2c 100644
--- a/src/bin/dhcp4/dhcp4_srv.h
+++ b/src/bin/dhcp4/dhcp4_srv.h
@@ -21,6 +21,7 @@
#include <dhcp/option4_client_fqdn.h>
#include <dhcp/option_custom.h>
#include <dhcp_ddns/ncr_msg.h>
+#include <dhcpsrv/d2_client_mgr.h>
#include <dhcpsrv/subnet.h>
#include <dhcpsrv/alloc_engine.h>
#include <hooks/callout_handle.h>
@@ -165,8 +166,109 @@ public:
/// @param use_bcast should broadcast flags be set on the sockets.
static void openActiveSockets(const uint16_t port, const bool use_bcast);
+ /// @brief Starts DHCP_DDNS client IO if DDNS updates are enabled.
+ ///
+ /// If updates are enabled, it Instructs the D2ClientMgr singleton to
+ /// enter send mode. If D2ClientMgr encounters errors it may throw
+ /// D2ClientErrors. This method does not catch exceptions.
+ void startD2();
+
+ /// @brief Implements the error handler for DHCP_DDNS IO errors
+ ///
+ /// Invoked when a NameChangeRequest send to b10-dhcp-ddns completes with
+ /// a failed status. These are communications errors, not data related
+ /// failures.
+ ///
+ /// This method logs the failure and then suspends all further updates.
+ /// Updating can only be restored by reconfiguration or restarting the
+ /// server. There is currently no retry logic so the first IO error that
+ /// occurs will suspend updates.
+ /// @todo We may wish to make this more robust or sophisticated.
+ ///
+ /// @param result Result code of the send operation.
+ /// @param ncr NameChangeRequest which failed to send.
+ virtual void d2ClientErrorHandler(const dhcp_ddns::
+ NameChangeSender::Result result,
+ dhcp_ddns::NameChangeRequestPtr& ncr);
protected:
+ /// @name Functions filtering and sanity-checking received messages.
+ ///
+ /// @todo These functions are supposed to be moved to a new class which
+ /// will manage different rules for accepting and rejecting messages.
+ /// Perhaps ticket #3116 is a good opportunity to do it.
+ ///
+ //@{
+ /// @brief Checks whether received message should be processed or discarded.
+ ///
+ /// This function checks whether received message should be processed or
+ /// discarded. It should be called on the beginning of message processing
+ /// (just after the message has been decoded). This message calls a number
+ /// of other functions which check whether message should be processed,
+ /// using different criteria.
+ ///
+ /// This function should be extended when new criteria for accepting
+ /// received message have to be implemented. This function is meant to
+ /// aggregate all early filtering checks on the received message. By having
+ /// a single function like this, we are avoiding bloat of the server's main
+ /// loop.
+ ///
+ /// @warning This function should remain exception safe.
+ ///
+ /// @param query Received message.
+ ///
+ /// @return true if the message should be further processed, or false if
+ /// the message should be discarded.
+ bool accept(const Pkt4Ptr& query) const;
+
+ /// @brief Check if a message sent by directly connected client should be
+ /// accepted or discared.
+ ///
+ /// This function checks if the received message is from directly connected
+ /// client. If it is, it checks that it should be processed or discarded.
+ ///
+ /// Note that this function doesn't validate all addresses being carried in
+ /// the message. The primary purpose of this function is to filter out
+ /// direct messages in the local network for which there is no suitable
+ /// subnet configured. For example, this function accepts unicast messages
+ /// because unicasts may be used by clients located in remote networks to
+ /// to renew existing leases. If their notion of address is wrong, the
+ /// server will have to sent a NAK, instead of dropping the message.
+ /// Detailed validation of such messages is performed at later stage of
+ /// processing.
+ ///
+ /// This function accepts the following messages:
+ /// - all valid relayed messages,
+ /// - all unicast messages,
+ /// - all broadcast messages received on the interface for which the
+ /// suitable subnet exists (is configured).
+ ///
+ /// @param query Message sent by a client.
+ ///
+ /// @return true if message is accepted for further processing, false
+ /// otherwise.
+ bool acceptDirectRequest(const Pkt4Ptr& query) const;
+
+ /// @brief Check if received message type is valid for the server to
+ /// process.
+ ///
+ /// This function checks that the received message type belongs to the range
+ /// of types regonized by the server and that the message of this type
+ /// should be processed by the server.
+ ///
+ /// The messages types accepted for processing are:
+ /// - Discover
+ /// - Request
+ /// - Release
+ /// - Decline
+ /// - Inform
+ ///
+ /// @param query Message sent by a client.
+ ///
+ /// @return true if message is accepted for further processing, false
+ /// otherwise.
+ bool acceptMessageType(const Pkt4Ptr& query) const;
+
/// @brief Verifies if the server id belongs to our server.
///
/// This function checks if the server identifier carried in the specified
@@ -181,6 +283,7 @@ protected:
/// @return true, if the server identifier is absent or matches one of the
/// server identifiers that the server is using; false otherwise.
bool acceptServerId(const Pkt4Ptr& pkt) const;
+ //@}
/// @brief verifies if specified packet meets RFC requirements
///
@@ -377,10 +480,10 @@ protected:
/// @brief Creates the NameChangeRequest and adds to the queue for
/// processing.
///
- /// This function adds the @c isc::dhcp_ddns::NameChangeRequest to the
- /// queue and emits the debug message which indicates whether the request
- /// being added is to remove DNS entry or add a new entry. This function
- /// is exception free.
+ /// This creates the @c isc::dhcp_ddns::NameChangeRequest; emits a
+ /// the debug message which indicates whether the request being added is
+ /// to remove DNS entry or add a new entry; and then sends the request
+ /// to the D2ClientMgr for transmission to b10-dhcp-ddns.
///
/// @param chg_type A type of the NameChangeRequest (ADD or REMOVE).
/// @param lease A lease for which the NameChangeRequest is created and
@@ -388,17 +491,6 @@ protected:
void queueNameChangeRequest(const isc::dhcp_ddns::NameChangeType chg_type,
const Lease4Ptr& lease);
- /// @brief Sends all outstanding NameChangeRequests to b10-dhcp-ddns module.
- ///
- /// The purpose of this function is to pick all outstanding
- /// NameChangeRequests from the FIFO queue and send them to b10-dhcp-ddns
- /// module.
- ///
- /// @todo Currently this function simply removes all requests from the
- /// queue but doesn't send them anywhere. In the future, the
- /// NameChangeSender will be used to deliver requests to the other module.
- void sendNameChangeRequests();
-
/// @brief Attempts to renew received addresses
///
/// Attempts to renew existing lease. This typically includes finding a lease that
@@ -529,7 +621,7 @@ protected:
///
/// @param question client's message
/// @return selected subnet (or NULL if no suitable subnet was found)
- isc::dhcp::Subnet4Ptr selectSubnet(const Pkt4Ptr& question);
+ isc::dhcp::Subnet4Ptr selectSubnet(const Pkt4Ptr& question) const;
/// indicates if shutdown is in progress. Setting it to true will
/// initiate server shutdown procedure.
@@ -608,12 +700,6 @@ private:
int hook_index_pkt4_receive_;
int hook_index_subnet4_select_;
int hook_index_pkt4_send_;
-
-protected:
-
- /// Holds a list of @c isc::dhcp_ddns::NameChangeRequest objects which
- /// are waiting for sending to b10-dhcp-ddns module.
- std::queue<isc::dhcp_ddns::NameChangeRequest> name_change_reqs_;
};
}; // namespace isc::dhcp
diff --git a/src/bin/dhcp4/tests/Makefile.am b/src/bin/dhcp4/tests/Makefile.am
index c72b8b0..4992501 100644
--- a/src/bin/dhcp4/tests/Makefile.am
+++ b/src/bin/dhcp4/tests/Makefile.am
@@ -58,7 +58,8 @@ if HAVE_GTEST
# to unexpected errors. For this reason, the --enable-static-link option is
# ignored for unit tests built here.
-lib_LTLIBRARIES = libco1.la libco2.la
+nodistdir=$(abs_top_builddir)/src/bin/dhcp4/tests
+nodist_LTLIBRARIES = libco1.la libco2.la
libco1_la_SOURCES = callout_library_1.cc callout_library_common.h
libco1_la_CXXFLAGS = $(AM_CXXFLAGS)
@@ -75,10 +76,12 @@ TESTS += dhcp4_unittests
dhcp4_unittests_SOURCES = ../dhcp4_srv.h ../dhcp4_srv.cc ../ctrl_dhcp4_srv.cc
dhcp4_unittests_SOURCES += ../dhcp4_log.h ../dhcp4_log.cc
dhcp4_unittests_SOURCES += ../config_parser.cc ../config_parser.h
+dhcp4_unittests_SOURCES += d2_unittest.h d2_unittest.cc
dhcp4_unittests_SOURCES += dhcp4_test_utils.h
dhcp4_unittests_SOURCES += dhcp4_unittests.cc
dhcp4_unittests_SOURCES += dhcp4_srv_unittest.cc
dhcp4_unittests_SOURCES += dhcp4_test_utils.cc dhcp4_test_utils.h
+dhcp4_unittests_SOURCES += direct_client_unittest.cc
dhcp4_unittests_SOURCES += wireshark.cc
dhcp4_unittests_SOURCES += ctrl_dhcp4_srv_unittest.cc
dhcp4_unittests_SOURCES += config_parser_unittest.cc
@@ -94,6 +97,7 @@ dhcp4_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libb10-asiolink.la
dhcp4_unittests_LDADD += $(top_builddir)/src/lib/cc/libb10-cc.la
dhcp4_unittests_LDADD += $(top_builddir)/src/lib/config/libb10-cfgclient.la
dhcp4_unittests_LDADD += $(top_builddir)/src/lib/dhcp/libb10-dhcp++.la
+dhcp4_unittests_LDADD += $(top_builddir)/src/lib/dhcp/tests/libdhcptest.la
dhcp4_unittests_LDADD += $(top_builddir)/src/lib/dhcp_ddns/libb10-dhcp_ddns.la
dhcp4_unittests_LDADD += $(top_builddir)/src/lib/dhcpsrv/libb10-dhcpsrv.la
dhcp4_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libb10-exceptions.la
diff --git a/src/bin/dhcp4/tests/callout_library_common.h b/src/bin/dhcp4/tests/callout_library_common.h
index cbabcda..6db761d 100644
--- a/src/bin/dhcp4/tests/callout_library_common.h
+++ b/src/bin/dhcp4/tests/callout_library_common.h
@@ -34,9 +34,6 @@
#include <fstream>
-using namespace isc::hooks;
-using namespace std;
-
extern "C" {
/// @brief Append digit to marker file
@@ -51,7 +48,7 @@ extern "C" {
int
appendDigit(const char* name) {
// Open the file and check if successful.
- fstream file(name, fstream::out | fstream::app);
+ std::fstream file(name, std::fstream::out | std::fstream::app);
if (!file.good()) {
return (1);
}
@@ -70,7 +67,7 @@ version() {
}
int
-load(LibraryHandle&) {
+load(isc::hooks::LibraryHandle&) {
return (appendDigit(LOAD_MARKER_FILE));
}
diff --git a/src/bin/dhcp4/tests/config_parser_unittest.cc b/src/bin/dhcp4/tests/config_parser_unittest.cc
index 14c4f80..45ae8e8 100644
--- a/src/bin/dhcp4/tests/config_parser_unittest.cc
+++ b/src/bin/dhcp4/tests/config_parser_unittest.cc
@@ -24,6 +24,7 @@
#include <dhcp/option_custom.h>
#include <dhcp/option_int.h>
#include <dhcp/docsis3_option_defs.h>
+#include <dhcp/classify.h>
#include <dhcpsrv/subnet.h>
#include <dhcpsrv/cfgmgr.h>
#include <hooks/hooks_manager.h>
@@ -137,19 +138,19 @@ public:
params["name"] = param_value;
params["space"] = "dhcp4";
params["code"] = "56";
- params["data"] = "AB CDEF0105";
+ params["data"] = "ABCDEF0105";
params["csv-format"] = "False";
} else if (parameter == "space") {
params["name"] = "dhcp-message";
params["space"] = param_value;
params["code"] = "56";
- params["data"] = "AB CDEF0105";
+ params["data"] = "ABCDEF0105";
params["csv-format"] = "False";
} else if (parameter == "code") {
params["name"] = "dhcp-message";
params["space"] = "dhcp4";
params["code"] = param_value;
- params["data"] = "AB CDEF0105";
+ params["data"] = "ABCDEF0105";
params["csv-format"] = "False";
} else if (parameter == "data") {
params["name"] = "dhcp-message";
@@ -161,7 +162,7 @@ public:
params["name"] = "dhcp-message";
params["space"] = "dhcp4";
params["code"] = "56";
- params["data"] = "AB CDEF0105";
+ params["data"] = "ABCDEF0105";
params["csv-format"] = param_value;
}
return (createConfigWithOption(params));
@@ -212,6 +213,63 @@ public:
return (stream.str());
}
+ /// @brief Returns an option from the subnet.
+ ///
+ /// This function returns an option from a subnet to which the
+ /// specified subnet address belongs. The option is identified
+ /// by its code.
+ ///
+ /// @param subnet_address Address which belongs to the subnet from
+ /// which the option is to be returned.
+ /// @param option_code Code of the option to be returned.
+ /// @param expected_options_count Expected number of options in
+ /// the particular subnet.
+ ///
+ /// @return Descriptor of the option. If the descriptor holds a
+ /// NULL option pointer, it means that there was no such option
+ /// in the subnet.
+ Subnet::OptionDescriptor
+ getOptionFromSubnet(const IOAddress& subnet_address,
+ const uint16_t option_code,
+ const uint16_t expected_options_count = 1) {
+ Subnet4Ptr subnet = CfgMgr::instance().getSubnet4(subnet_address,
+ classify_);
+ if (!subnet) {
+ /// @todo replace toText() with the use of operator <<.
+ ADD_FAILURE() << "A subnet for the specified address "
+ << subnet_address.toText()
+ << "does not exist in Config Manager";
+ }
+ Subnet::OptionContainerPtr options =
+ subnet->getOptionDescriptors("dhcp4");
+ if (expected_options_count != options->size()) {
+ ADD_FAILURE() << "The number of options in the subnet '"
+ << subnet_address.toText() << "' is different "
+ " than expected number of options '"
+ << expected_options_count << "'";
+ }
+
+ // Get the search index. Index #1 is to search using option code.
+ const Subnet::OptionContainerTypeIndex& idx = options->get<1>();
+
+ // Get the options for specified index. Expecting one option to be
+ // returned but in theory we may have multiple options with the same
+ // code so we get the range.
+ std::pair<Subnet::OptionContainerTypeIndex::const_iterator,
+ Subnet::OptionContainerTypeIndex::const_iterator> range =
+ idx.equal_range(option_code);
+ if (std::distance(range.first, range.second) > 1) {
+ ADD_FAILURE() << "There is more than one option having the"
+ " option code '" << option_code << "' in a subnet '"
+ << subnet_address.toText() << "'. Expected "
+ " at most one option";
+ } else if (std::distance(range.first, range.second) == 0) {
+ return (Subnet::OptionDescriptor(OptionPtr(), false));
+ }
+
+ return (*range.first);
+ }
+
/// @brief Test invalid option parameter value.
///
/// This test function constructs the simple configuration
@@ -233,6 +291,24 @@ public:
ASSERT_EQ(1, rcode_);
}
+ /// @brief Test invalid option paramater value.
+ ///
+ /// This test function constructs the simple configuration
+ /// string and injects invalid option configuration into it.
+ /// It expects that parser will fail with provided option code.
+ ///
+ /// @param params Map of parameters defining an option.
+ void
+ testInvalidOptionParam(const std::map<std::string, std::string>& params) {
+ ConstElementPtr x;
+ std::string config = createConfigWithOption(params);
+ ElementPtr json = Element::fromJSON(config);
+ EXPECT_NO_THROW(x = configureDhcp4Server(*srv_, json));
+ ASSERT_TRUE(x);
+ comment_ = parseAnswer(rcode_, x);
+ ASSERT_EQ(1, rcode_);
+ }
+
/// @brief Test option against given code and data.
///
/// @param option_desc option descriptor that carries the option to
@@ -274,6 +350,39 @@ public:
expected_data_len));
}
+ /// @brief Test option configuration.
+ ///
+ /// This function creates a configuration for a specified option using
+ /// a map of parameters specified as the argument. The map holds
+ /// name/value pairs which identifies option's configuration parameters:
+ /// - name
+ /// - space
+ /// - code
+ /// - data
+ /// - csv-format.
+ /// This function applies a new server configuration and checks that the
+ /// option being configured is inserted into CfgMgr. The raw contents of
+ /// this option are compared with the binary data specified as expected
+ /// data passed to this function.
+ ///
+ /// @param params Map of parameters defining an option.
+ /// @param option_code Option code.
+ /// @param expected_data Array containing binary data expected to be stored
+ /// in the configured option.
+ /// @param expected_data_len Length of the array holding reference data.
+ void testConfiguration(const std::map<std::string, std::string>& params,
+ const uint16_t option_code,
+ const uint8_t* expected_data,
+ const size_t expected_data_len) {
+ std::string config = createConfigWithOption(params);
+ ASSERT_TRUE(executeConfiguration(config, "parse option configuration"));
+ // The subnet should now hold one option with the specified option code.
+ Subnet::OptionDescriptor desc =
+ getOptionFromSubnet(IOAddress("192.0.2.24"), option_code);
+ ASSERT_TRUE(desc.option);
+ testOption(desc, option_code, expected_data, expected_data_len);
+ }
+
/// @brief Parse and Execute configuration
///
/// Parses a configuration and executes a configuration of the server.
@@ -347,9 +456,10 @@ public:
}
- boost::scoped_ptr<Dhcpv4Srv> srv_; // DHCP4 server under test
- int rcode_; // Return code from element parsing
- ConstElementPtr comment_; // Reason for parse fail
+ boost::scoped_ptr<Dhcpv4Srv> srv_; ///< DHCP4 server under test
+ int rcode_; ///< Return code from element parsing
+ ConstElementPtr comment_; ///< Reason for parse fail
+ isc::dhcp::ClientClasses classify_; ///< used in client classification
};
// Goal of this test is a verification if a very simple config update
@@ -424,7 +534,8 @@ TEST_F(Dhcp4ParserTest, subnetGlobalDefaults) {
// Now check if the configuration was indeed handled and we have
// expected pool configured.
- Subnet4Ptr subnet = CfgMgr::instance().getSubnet4(IOAddress("192.0.2.200"));
+ Subnet4Ptr subnet = CfgMgr::instance().getSubnet4(IOAddress("192.0.2.200"),
+ classify_);
ASSERT_TRUE(subnet);
EXPECT_EQ(1000, subnet->getT1());
EXPECT_EQ(2000, subnet->getT2());
@@ -642,7 +753,8 @@ TEST_F(Dhcp4ParserTest, nextServerGlobal) {
// Now check if the configuration was indeed handled and we have
// expected pool configured.
- Subnet4Ptr subnet = CfgMgr::instance().getSubnet4(IOAddress("192.0.2.200"));
+ Subnet4Ptr subnet = CfgMgr::instance().getSubnet4(IOAddress("192.0.2.200"),
+ classify_);
ASSERT_TRUE(subnet);
EXPECT_EQ("1.2.3.4", subnet->getSiaddr().toText());
}
@@ -671,7 +783,8 @@ TEST_F(Dhcp4ParserTest, nextServerSubnet) {
// Now check if the configuration was indeed handled and we have
// expected pool configured.
- Subnet4Ptr subnet = CfgMgr::instance().getSubnet4(IOAddress("192.0.2.200"));
+ Subnet4Ptr subnet = CfgMgr::instance().getSubnet4(IOAddress("192.0.2.200"),
+ classify_);
ASSERT_TRUE(subnet);
EXPECT_EQ("1.2.3.4", subnet->getSiaddr().toText());
}
@@ -758,7 +871,8 @@ TEST_F(Dhcp4ParserTest, nextServerOverride) {
// Now check if the configuration was indeed handled and we have
// expected pool configured.
- Subnet4Ptr subnet = CfgMgr::instance().getSubnet4(IOAddress("192.0.2.200"));
+ Subnet4Ptr subnet = CfgMgr::instance().getSubnet4(IOAddress("192.0.2.200"),
+ classify_);
ASSERT_TRUE(subnet);
EXPECT_EQ("1.2.3.4", subnet->getSiaddr().toText());
}
@@ -828,7 +942,8 @@ TEST_F(Dhcp4ParserTest, subnetLocal) {
// returned value should be 0 (configuration success)
checkResult(status, 0);
- Subnet4Ptr subnet = CfgMgr::instance().getSubnet4(IOAddress("192.0.2.200"));
+ Subnet4Ptr subnet = CfgMgr::instance().getSubnet4(IOAddress("192.0.2.200"),
+ classify_);
ASSERT_TRUE(subnet);
EXPECT_EQ(1, subnet->getT1());
EXPECT_EQ(2, subnet->getT2());
@@ -880,7 +995,8 @@ TEST_F(Dhcp4ParserTest, poolPrefixLen) {
// returned value must be 0 (configuration accepted)
checkResult(status, 0);
- Subnet4Ptr subnet = CfgMgr::instance().getSubnet4(IOAddress("192.0.2.200"));
+ Subnet4Ptr subnet = CfgMgr::instance().getSubnet4(IOAddress("192.0.2.200"),
+ classify_);
ASSERT_TRUE(subnet);
EXPECT_EQ(1000, subnet->getT1());
EXPECT_EQ(2000, subnet->getT2());
@@ -1318,14 +1434,13 @@ TEST_F(Dhcp4ParserTest, optionDefEncapsulateOwnSpace) {
/// The purpose of this test is to verify that it is not allowed
/// to override the standard option (that belongs to dhcp4 option
-/// space) and that it is allowed to define option in the dhcp4
-/// option space that has a code which is not used by any of the
-/// standard options.
+/// space and has its definition) and that it is allowed to define
+/// option in the dhcp4 option space that has a code which is not
+/// used by any of the standard options.
TEST_F(Dhcp4ParserTest, optionStandardDefOverride) {
- // Configuration string. The option code 109 is unassigned
- // so it can be used for a custom option definition in
- // dhcp4 option space.
+ // Configuration string. The option code 109 is unassigned so it
+ // can be used for a custom option definition in dhcp4 option space.
std::string config =
"{ \"option-def\": [ {"
" \"name\": \"foo\","
@@ -1358,15 +1473,14 @@ TEST_F(Dhcp4ParserTest, optionStandardDefOverride) {
EXPECT_EQ(OPT_STRING_TYPE, def->getType());
EXPECT_FALSE(def->getArrayType());
- // The combination of option space and code is
- // invalid. The 'dhcp4' option space groups
- // standard options and the code 100 is reserved
- // for one of them.
+ // The combination of option space and code is invalid. The 'dhcp4' option
+ // space groups standard options and the code 3 is reserved for one of
+ // them.
config =
"{ \"option-def\": [ {"
- " \"name\": \"foo\","
- " \"code\": 100,"
- " \"type\": \"string\","
+ " \"name\": \"routers\","
+ " \"code\": 3,"
+ " \"type\": \"ipv4-address\","
" \"array\": False,"
" \"record-types\": \"\","
" \"space\": \"dhcp4\","
@@ -1380,6 +1494,40 @@ TEST_F(Dhcp4ParserTest, optionStandardDefOverride) {
ASSERT_TRUE(status);
// Expecting parsing error (error code 1).
checkResult(status, 1);
+
+ /// @todo The option 65 is a standard DHCPv4 option. However, at this point
+ /// there is no definition for this option in libdhcp++, so it should be
+ /// allowed to define it from the configuration interface. This test will
+ /// have to be removed once definitions for remaining standard options are
+ /// created.
+ config =
+ "{ \"option-def\": [ {"
+ " \"name\": \"nis-server-addr\","
+ " \"code\": 65,"
+ " \"type\": \"ipv4-address\","
+ " \"array\": False,"
+ " \"record-types\": \"\","
+ " \"space\": \"dhcp4\","
+ " \"encapsulate\": \"\""
+ " } ]"
+ "}";
+ json = Element::fromJSON(config);
+
+ // Use the configuration string to create new option definition.
+ EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json));
+ ASSERT_TRUE(status);
+ // Expecting success.
+ checkResult(status, 0);
+
+ def = CfgMgr::instance().getOptionDef("dhcp4", 65);
+ ASSERT_TRUE(def);
+
+ // Check the option data.
+ EXPECT_EQ("nis-server-addr", def->getName());
+ EXPECT_EQ(65, def->getCode());
+ EXPECT_EQ(OPT_IPV4_ADDRESS_TYPE, def->getType());
+ EXPECT_FALSE(def->getArrayType());
+
}
// Goal of this test is to verify that global option
@@ -1394,7 +1542,7 @@ TEST_F(Dhcp4ParserTest, optionDataDefaults) {
" \"name\": \"dhcp-message\","
" \"space\": \"dhcp4\","
" \"code\": 56,"
- " \"data\": \"AB CDEF0105\","
+ " \"data\": \"ABCDEF0105\","
" \"csv-format\": False"
" },"
" {"
@@ -1417,7 +1565,8 @@ TEST_F(Dhcp4ParserTest, optionDataDefaults) {
comment_ = parseAnswer(rcode_, x);
ASSERT_EQ(0, rcode_);
- Subnet4Ptr subnet = CfgMgr::instance().getSubnet4(IOAddress("192.0.2.200"));
+ Subnet4Ptr subnet = CfgMgr::instance().getSubnet4(IOAddress("192.0.2.200"),
+ classify_);
ASSERT_TRUE(subnet);
Subnet::OptionContainerPtr options = subnet->getOptionDescriptors("dhcp4");
ASSERT_EQ(2, options->size());
@@ -1467,7 +1616,7 @@ TEST_F(Dhcp4ParserTest, optionDataTwoSpaces) {
" \"name\": \"dhcp-message\","
" \"space\": \"dhcp4\","
" \"code\": 56,"
- " \"data\": \"AB CDEF0105\","
+ " \"data\": \"ABCDEF0105\","
" \"csv-format\": False"
" },"
" {"
@@ -1501,7 +1650,8 @@ TEST_F(Dhcp4ParserTest, optionDataTwoSpaces) {
checkResult(status, 0);
// Options should be now available for the subnet.
- Subnet4Ptr subnet = CfgMgr::instance().getSubnet4(IOAddress("192.0.2.200"));
+ Subnet4Ptr subnet = CfgMgr::instance().getSubnet4(IOAddress("192.0.2.200"),
+ classify_);
ASSERT_TRUE(subnet);
// Try to get the option from the space dhcp4.
Subnet::OptionDescriptor desc1 = subnet->getOptionDescriptor("dhcp4", 56);
@@ -1644,7 +1794,6 @@ TEST_F(Dhcp4ParserTest, optionDataEncapsulate) {
" } ]"
"}";
-
json = Element::fromJSON(config);
EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json));
@@ -1652,7 +1801,8 @@ TEST_F(Dhcp4ParserTest, optionDataEncapsulate) {
checkResult(status, 0);
// Get the subnet.
- Subnet4Ptr subnet = CfgMgr::instance().getSubnet4(IOAddress("192.0.2.5"));
+ Subnet4Ptr subnet = CfgMgr::instance().getSubnet4(IOAddress("192.0.2.5"),
+ classify_);
ASSERT_TRUE(subnet);
// We should have one option available.
@@ -1700,7 +1850,7 @@ TEST_F(Dhcp4ParserTest, optionDataInSingleSubnet) {
" \"name\": \"dhcp-message\","
" \"space\": \"dhcp4\","
" \"code\": 56,"
- " \"data\": \"AB CDEF0105\","
+ " \"data\": \"ABCDEF0105\","
" \"csv-format\": False"
" },"
" {"
@@ -1720,7 +1870,8 @@ TEST_F(Dhcp4ParserTest, optionDataInSingleSubnet) {
comment_ = parseAnswer(rcode_, x);
ASSERT_EQ(0, rcode_);
- Subnet4Ptr subnet = CfgMgr::instance().getSubnet4(IOAddress("192.0.2.24"));
+ Subnet4Ptr subnet = CfgMgr::instance().getSubnet4(IOAddress("192.0.2.24"),
+ classify_);
ASSERT_TRUE(subnet);
Subnet::OptionContainerPtr options = subnet->getOptionDescriptors("dhcp4");
ASSERT_EQ(2, options->size());
@@ -1751,6 +1902,89 @@ TEST_F(Dhcp4ParserTest, optionDataInSingleSubnet) {
testOption(*range.first, 23, foo2_expected, sizeof(foo2_expected));
}
+// The goal of this test is to check that the option carrying a boolean
+// value can be configured using one of the values: "true", "false", "0"
+// or "1".
+TEST_F(Dhcp4ParserTest, optionDataBoolean) {
+ // Create configuration. Use standard option 19 (ip-forwarding).
+ std::map<std::string, std::string> params;
+ params["name"] = "ip-forwarding";
+ params["space"] = "dhcp4";
+ params["code"] = "19";
+ params["data"] = "true";
+ params["csv-format"] = "true";
+
+ std::string config = createConfigWithOption(params);
+ ASSERT_TRUE(executeConfiguration(config, "parse configuration with a"
+ " boolean value"));
+
+ // The subnet should now hold one option with the code 19.
+ Subnet::OptionDescriptor desc = getOptionFromSubnet(IOAddress("192.0.2.24"),
+ 19);
+ ASSERT_TRUE(desc.option);
+
+ // This option should be set to "true", represented as 0x1 in the option
+ // buffer.
+ uint8_t expected_option_data[] = {
+ 0x1
+ };
+ testConfiguration(params, 19, expected_option_data,
+ sizeof(expected_option_data));
+
+ // Configure the option with the "1" value. This should have the same
+ // effect as if "true" was specified.
+ params["data"] = "1";
+ testConfiguration(params, 19, expected_option_data,
+ sizeof(expected_option_data));
+
+ // The value of "1" with a few leading zeros should work too.
+ params["data"] = "00001";
+ testConfiguration(params, 19, expected_option_data,
+ sizeof(expected_option_data));
+
+ // Configure the option with the "false" value.
+ params["data"] = "false";
+ // The option buffer should now hold the value of 0.
+ expected_option_data[0] = 0;
+ testConfiguration(params, 19, expected_option_data,
+ sizeof(expected_option_data));
+
+ // Specifying "0" should have the same effect as "false".
+ params["data"] = "0";
+ testConfiguration(params, 19, expected_option_data,
+ sizeof(expected_option_data));
+
+ // The same effect should be for multiple 0 chars.
+ params["data"] = "00000";
+ testConfiguration(params, 19, expected_option_data,
+ sizeof(expected_option_data));
+
+ // Bogus values should not be accepted.
+ params["data"] = "bugus";
+ testInvalidOptionParam(params);
+
+ params["data"] = "2";
+ testInvalidOptionParam(params);
+
+ // Now let's test that it is possible to use binary format.
+ params["data"] = "0";
+ params["csv-format"] = "false";
+ testConfiguration(params, 19, expected_option_data,
+ sizeof(expected_option_data));
+
+ // The binary 1 should work as well.
+ params["data"] = "1";
+ expected_option_data[0] = 1;
+ testConfiguration(params, 19, expected_option_data,
+ sizeof(expected_option_data));
+
+ // As well as an even number of digits.
+ params["data"] = "01";
+ testConfiguration(params, 19, expected_option_data,
+ sizeof(expected_option_data));
+
+}
+
// Goal of this test is to verify options configuration
// for multiple subnets.
TEST_F(Dhcp4ParserTest, optionDataInMultipleSubnets) {
@@ -1789,7 +2023,8 @@ TEST_F(Dhcp4ParserTest, optionDataInMultipleSubnets) {
comment_ = parseAnswer(rcode_, x);
ASSERT_EQ(0, rcode_);
- Subnet4Ptr subnet1 = CfgMgr::instance().getSubnet4(IOAddress("192.0.2.100"));
+ Subnet4Ptr subnet1 = CfgMgr::instance().getSubnet4(IOAddress("192.0.2.100"),
+ classify_);
ASSERT_TRUE(subnet1);
Subnet::OptionContainerPtr options1 = subnet1->getOptionDescriptors("dhcp4");
ASSERT_EQ(1, options1->size());
@@ -1813,7 +2048,8 @@ TEST_F(Dhcp4ParserTest, optionDataInMultipleSubnets) {
testOption(*range1.first, 56, foo_expected, sizeof(foo_expected));
// Test another subnet in the same way.
- Subnet4Ptr subnet2 = CfgMgr::instance().getSubnet4(IOAddress("192.0.3.102"));
+ Subnet4Ptr subnet2 = CfgMgr::instance().getSubnet4(IOAddress("192.0.3.102"),
+ classify_);
ASSERT_TRUE(subnet2);
Subnet::OptionContainerPtr options2 = subnet2->getOptionDescriptors("dhcp4");
ASSERT_EQ(1, options2->size());
@@ -1828,6 +2064,8 @@ TEST_F(Dhcp4ParserTest, optionDataInMultipleSubnets) {
testOption(*range2.first, 23, foo2_expected, sizeof(foo2_expected));
}
+
+
// Verify that empty option name is rejected in the configuration.
TEST_F(Dhcp4ParserTest, optionNameEmpty) {
// Empty option names not allowed.
@@ -1878,14 +2116,6 @@ TEST_F(Dhcp4ParserTest, optionDataUnexpectedPrefix) {
testInvalidOptionParam("0x0102", "data");
}
-// Verify that option data consisting od an odd number of
-// hexadecimal digits is rejected in the configuration.
-TEST_F(Dhcp4ParserTest, optionDataOddLength) {
- // Option code 0 is reserved and should not be accepted
- // by configuration parser.
- testInvalidOptionParam("123", "data");
-}
-
// Verify that either lower or upper case characters are allowed
// to specify the option data.
TEST_F(Dhcp4ParserTest, optionDataLowerCase) {
@@ -1898,7 +2128,8 @@ TEST_F(Dhcp4ParserTest, optionDataLowerCase) {
comment_ = parseAnswer(rcode_, x);
ASSERT_EQ(0, rcode_);
- Subnet4Ptr subnet = CfgMgr::instance().getSubnet4(IOAddress("192.0.2.5"));
+ Subnet4Ptr subnet = CfgMgr::instance().getSubnet4(IOAddress("192.0.2.5"),
+ classify_);
ASSERT_TRUE(subnet);
Subnet::OptionContainerPtr options = subnet->getOptionDescriptors("dhcp4");
ASSERT_EQ(1, options->size());
@@ -1942,7 +2173,8 @@ TEST_F(Dhcp4ParserTest, stdOptionData) {
comment_ = parseAnswer(rcode_, x);
ASSERT_EQ(0, rcode_);
- Subnet4Ptr subnet = CfgMgr::instance().getSubnet4(IOAddress("192.0.2.5"));
+ Subnet4Ptr subnet = CfgMgr::instance().getSubnet4(IOAddress("192.0.2.5"),
+ classify_);
ASSERT_TRUE(subnet);
Subnet::OptionContainerPtr options =
subnet->getOptionDescriptors("dhcp4");
@@ -2144,7 +2376,8 @@ TEST_F(Dhcp4ParserTest, stdOptionDataEncapsulate) {
checkResult(status, 0);
// Get the subnet.
- Subnet4Ptr subnet = CfgMgr::instance().getSubnet4(IOAddress("192.0.2.5"));
+ Subnet4Ptr subnet = CfgMgr::instance().getSubnet4(IOAddress("192.0.2.5"),
+ classify_);
ASSERT_TRUE(subnet);
// We should have one option available.
@@ -2201,7 +2434,7 @@ TEST_F(Dhcp4ParserTest, vendorOptionsHex) {
" \"name\": \"option-one\","
" \"space\": \"vendor-4491\"," // VENDOR_ID_CABLE_LABS = 4491
" \"code\": 100," // just a random code
- " \"data\": \"AB CDEF0105\","
+ " \"data\": \"ABCDEF0105\","
" \"csv-format\": False"
" },"
" {"
@@ -2226,7 +2459,8 @@ TEST_F(Dhcp4ParserTest, vendorOptionsHex) {
checkResult(status, 0);
// Options should be now available for the subnet.
- Subnet4Ptr subnet = CfgMgr::instance().getSubnet4(IOAddress("192.0.2.5"));
+ Subnet4Ptr subnet = CfgMgr::instance().getSubnet4(IOAddress("192.0.2.5"),
+ classify_);
ASSERT_TRUE(subnet);
// Try to get the option from the vendor space 4491
@@ -2285,7 +2519,8 @@ TEST_F(Dhcp4ParserTest, vendorOptionsCsv) {
checkResult(status, 0);
// Options should be now available for the subnet.
- Subnet4Ptr subnet = CfgMgr::instance().getSubnet4(IOAddress("192.0.2.5"));
+ Subnet4Ptr subnet = CfgMgr::instance().getSubnet4(IOAddress("192.0.2.5"),
+ classify_);
ASSERT_TRUE(subnet);
// Try to get the option from the vendor space 4491
@@ -2333,7 +2568,7 @@ buildHooksLibrariesConfig(const std::vector<std::string>& libraries) {
" \"name\": \"dhcp-message\","
" \"space\": \"dhcp4\","
" \"code\": 56,"
- " \"data\": \"AB CDEF0105\","
+ " \"data\": \"ABCDEF0105\","
" \"csv-format\": False"
" },"
" {"
@@ -2529,7 +2764,6 @@ TEST_F(Dhcp4ParserTest, d2ClientConfig) {
" \"server-port\" : 777, "
" \"ncr-protocol\" : \"UDP\", "
" \"ncr-format\" : \"JSON\", "
- " \"remove-on-renew\" : true, "
" \"always-include-fqdn\" : true, "
" \"allow-client-update\" : true, "
" \"override-no-update\" : true, "
@@ -2562,7 +2796,6 @@ TEST_F(Dhcp4ParserTest, d2ClientConfig) {
EXPECT_EQ(777, d2_client_config->getServerPort());
EXPECT_EQ(dhcp_ddns::NCR_UDP, d2_client_config->getNcrProtocol());
EXPECT_EQ(dhcp_ddns::FMT_JSON, d2_client_config->getNcrFormat());
- EXPECT_TRUE(d2_client_config->getRemoveOnRenew());
EXPECT_TRUE(d2_client_config->getAlwaysIncludeFqdn());
EXPECT_TRUE(d2_client_config->getOverrideNoUpdate());
EXPECT_TRUE(d2_client_config->getOverrideClientUpdate());
@@ -2589,7 +2822,6 @@ TEST_F(Dhcp4ParserTest, invalidD2ClientConfig) {
" \"server-port\" : 5301, "
" \"ncr-protocol\" : \"UDP\", "
" \"ncr-format\" : \"JSON\", "
- " \"remove-on-renew\" : true, "
" \"always-include-fqdn\" : true, "
" \"allow-client-update\" : true, "
" \"override-no-update\" : true, "
@@ -2617,4 +2849,124 @@ TEST_F(Dhcp4ParserTest, invalidD2ClientConfig) {
ASSERT_FALSE(CfgMgr::instance().ddnsEnabled());
}
+// This test checks if it is possible to specify relay information
+TEST_F(Dhcp4ParserTest, subnetRelayInfo) {
+
+ ConstElementPtr status;
+
+ // A config with relay information.
+ string config = "{ \"interfaces\": [ \"*\" ],"
+ "\"rebind-timer\": 2000, "
+ "\"renew-timer\": 1000, "
+ "\"subnet4\": [ { "
+ " \"pool\": [ \"192.0.2.1 - 192.0.2.100\" ],"
+ " \"renew-timer\": 1, "
+ " \"rebind-timer\": 2, "
+ " \"valid-lifetime\": 4,"
+ " \"relay\": { "
+ " \"ip-address\": \"192.0.2.123\""
+ " },"
+ " \"subnet\": \"192.0.2.0/24\" } ],"
+ "\"valid-lifetime\": 4000 }";
+
+ ElementPtr json = Element::fromJSON(config);
+
+ EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json));
+
+ // returned value should be 0 (configuration success)
+ checkResult(status, 0);
+
+ Subnet4Ptr subnet = CfgMgr::instance().getSubnet4(IOAddress("192.0.2.200"),
+ classify_);
+ ASSERT_TRUE(subnet);
+ EXPECT_EQ("192.0.2.123", subnet->getRelayInfo().addr_.toText());
+}
+
+// Goal of this test is to verify that multiple subnets can be configured
+// with defined client classes.
+TEST_F(Dhcp4ParserTest, classifySubnets) {
+ ConstElementPtr x;
+ string config = "{ \"interfaces\": [ \"*\" ],"
+ "\"rebind-timer\": 2000, "
+ "\"renew-timer\": 1000, "
+ "\"subnet4\": [ { "
+ " \"pool\": [ \"192.0.2.1 - 192.0.2.100\" ],"
+ " \"subnet\": \"192.0.2.0/24\", "
+ " \"client-class\": \"alpha\" "
+ " },"
+ " {"
+ " \"pool\": [ \"192.0.3.101 - 192.0.3.150\" ],"
+ " \"subnet\": \"192.0.3.0/24\", "
+ " \"client-class\": \"beta\" "
+ " },"
+ " {"
+ " \"pool\": [ \"192.0.4.101 - 192.0.4.150\" ],"
+ " \"subnet\": \"192.0.4.0/24\", "
+ " \"client-class\": \"gamma\" "
+ " },"
+ " {"
+ " \"pool\": [ \"192.0.5.101 - 192.0.5.150\" ],"
+ " \"subnet\": \"192.0.5.0/24\" "
+ " } ],"
+ "\"valid-lifetime\": 4000 }";
+
+ ElementPtr json = Element::fromJSON(config);
+
+ EXPECT_NO_THROW(x = configureDhcp4Server(*srv_, json));
+ ASSERT_TRUE(x);
+ comment_ = parseAnswer(rcode_, x);
+ ASSERT_EQ(0, rcode_);
+
+ const Subnet4Collection* subnets = CfgMgr::instance().getSubnets4();
+ ASSERT_TRUE(subnets);
+ ASSERT_EQ(4, subnets->size()); // We expect 4 subnets
+
+ // Let's check if client belonging to alpha class is supported in subnet[0]
+ // and not supported in any other subnet (except subnet[3], which allows
+ // everyone).
+ ClientClasses classes;
+ classes.insert("alpha");
+ EXPECT_TRUE (subnets->at(0)->clientSupported(classes));
+ EXPECT_FALSE(subnets->at(1)->clientSupported(classes));
+ EXPECT_FALSE(subnets->at(2)->clientSupported(classes));
+ EXPECT_TRUE (subnets->at(3)->clientSupported(classes));
+
+ // Let's check if client belonging to beta class is supported in subnet[1]
+ // and not supported in any other subnet (except subnet[3], which allows
+ // everyone).
+ classes.clear();
+ classes.insert("beta");
+ EXPECT_FALSE(subnets->at(0)->clientSupported(classes));
+ EXPECT_TRUE (subnets->at(1)->clientSupported(classes));
+ EXPECT_FALSE(subnets->at(2)->clientSupported(classes));
+ EXPECT_TRUE (subnets->at(3)->clientSupported(classes));
+
+ // Let's check if client belonging to gamma class is supported in subnet[2]
+ // and not supported in any other subnet (except subnet[3], which allows
+ // everyone).
+ classes.clear();
+ classes.insert("gamma");
+ EXPECT_FALSE(subnets->at(0)->clientSupported(classes));
+ EXPECT_FALSE(subnets->at(1)->clientSupported(classes));
+ EXPECT_TRUE (subnets->at(2)->clientSupported(classes));
+ EXPECT_TRUE (subnets->at(3)->clientSupported(classes));
+
+ // Let's check if client belonging to some other class (not mentioned in
+ // the config) is supported only in subnet[3], which allows everyone.
+ classes.clear();
+ classes.insert("delta");
+ EXPECT_FALSE(subnets->at(0)->clientSupported(classes));
+ EXPECT_FALSE(subnets->at(1)->clientSupported(classes));
+ EXPECT_FALSE(subnets->at(2)->clientSupported(classes));
+ EXPECT_TRUE (subnets->at(3)->clientSupported(classes));
+
+ // Finally, let's check class-less client. He should be allowed only in
+ // the last subnet, which does not have any class restrictions.
+ classes.clear();
+ EXPECT_FALSE(subnets->at(0)->clientSupported(classes));
+ EXPECT_FALSE(subnets->at(1)->clientSupported(classes));
+ EXPECT_FALSE(subnets->at(2)->clientSupported(classes));
+ EXPECT_TRUE (subnets->at(3)->clientSupported(classes));
+}
+
}
diff --git a/src/bin/dhcp4/tests/d2_unittest.cc b/src/bin/dhcp4/tests/d2_unittest.cc
new file mode 100644
index 0000000..031c959
--- /dev/null
+++ b/src/bin/dhcp4/tests/d2_unittest.cc
@@ -0,0 +1,379 @@
+// Copyright (C) 2014 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 <dhcp/iface_mgr.h>
+#include <dhcp4/config_parser.h>
+#include <dhcp4/tests/d2_unittest.h>
+#include <dhcpsrv/cfgmgr.h>
+
+#include <gtest/gtest.h>
+
+#include <string>
+
+using namespace isc;
+using namespace isc::asiolink;
+using namespace isc::data;
+
+namespace isc {
+namespace dhcp {
+namespace test {
+
+void
+D2Dhcpv4Srv::d2ClientErrorHandler(const
+ dhcp_ddns::NameChangeSender::Result result,
+ dhcp_ddns::NameChangeRequestPtr& ncr) {
+ ++error_count_;
+ // call base class error handler
+ Dhcpv4Srv::d2ClientErrorHandler(result, ncr);
+}
+
+const bool Dhcp4SrvD2Test::SHOULD_PASS;
+const bool Dhcp4SrvD2Test::SHOULD_FAIL;
+
+Dhcp4SrvD2Test::Dhcp4SrvD2Test() {
+}
+
+Dhcp4SrvD2Test::~Dhcp4SrvD2Test() {
+ reset();
+}
+
+dhcp_ddns::NameChangeRequestPtr
+Dhcp4SrvD2Test::buildTestNcr(uint32_t dhcid_id_num) {
+ // Build an NCR from json string.
+ std::ostringstream stream;
+
+ stream <<
+ "{"
+ " \"change_type\" : 0 , "
+ " \"forward_change\" : true , "
+ " \"reverse_change\" : false , "
+ " \"fqdn\" : \"myhost.example.com.\" , "
+ " \"ip_address\" : \"192.168.2.1\" , "
+ " \"dhcid\" : \""
+
+ << std::hex << std::setfill('0') << std::setw(16)
+ << dhcid_id_num << "\" , "
+
+ " \"lease_expires_on\" : \"20140121132405\" , "
+ " \"lease_length\" : 1300 "
+ "}";
+
+ return (dhcp_ddns::NameChangeRequest::fromJSON(stream.str()));
+}
+
+void
+Dhcp4SrvD2Test::reset() {
+ std::string config = "{ \"interfaces\": [ \"*\" ],"
+ "\"hooks-libraries\": [ ], "
+ "\"rebind-timer\": 2000, "
+ "\"renew-timer\": 1000, "
+ "\"valid-lifetime\": 4000, "
+ "\"subnet4\": [ ], "
+ "\"dhcp-ddns\": { \"enable-updates\" : false }, "
+ "\"option-def\": [ ], "
+ "\"option-data\": [ ] }";
+ configure(config, SHOULD_PASS);
+}
+
+void
+Dhcp4SrvD2Test::configureD2(bool enable_d2, const bool exp_result,
+ const std::string& ip_address,
+ const uint32_t port) {
+ std::ostringstream config;
+ config <<
+ "{ \"interfaces\": [ \"*\" ],"
+ "\"rebind-timer\": 2000, "
+ "\"renew-timer\": 1000, "
+ "\"subnet4\": [ { "
+ " \"pool\": [ \"192.0.2.1 - 192.0.2.100\" ],"
+ " \"subnet\": \"192.0.2.0/24\" } ],"
+ " \"dhcp-ddns\" : {"
+ " \"enable-updates\" : " << (enable_d2 ? "true" : "false") << ", "
+ " \"server-ip\" : \"" << ip_address << "\", "
+ " \"server-port\" : " << port << ", "
+ " \"ncr-protocol\" : \"UDP\", "
+ " \"ncr-format\" : \"JSON\", "
+ " \"always-include-fqdn\" : true, "
+ " \"allow-client-update\" : true, "
+ " \"override-no-update\" : true, "
+ " \"override-client-update\" : true, "
+ " \"replace-client-name\" : true, "
+ " \"generated-prefix\" : \"test.prefix\", "
+ " \"qualifying-suffix\" : \"test.suffix.\" },"
+ "\"valid-lifetime\": 4000 }";
+
+ configure(config.str(), exp_result);
+}
+
+void
+Dhcp4SrvD2Test::configure(const std::string& config, bool exp_result) {
+ ElementPtr json = Element::fromJSON(config);
+ ConstElementPtr status;
+
+ // Configure the server and make sure the config is accepted
+ EXPECT_NO_THROW(status = configureDhcp4Server(srv_, json));
+ ASSERT_TRUE(status);
+
+ int rcode;
+ ConstElementPtr comment = config::parseAnswer(rcode, status);
+ if (exp_result == SHOULD_PASS) {
+ ASSERT_EQ(0, rcode);
+ } else {
+ ASSERT_EQ(1, rcode);
+ }
+}
+
+// Tests ability to turn on and off ddns updates by submitting
+// by submitting the appropriate configuration to Dhcp4 server
+// and then invoking its startD2() method.
+TEST_F(Dhcp4SrvD2Test, enableDisable) {
+ // Grab the manager and verify that be default ddns is off
+ // and a sender was not started.
+ dhcp::D2ClientMgr& mgr = CfgMgr::instance().getD2ClientMgr();
+ ASSERT_FALSE(mgr.ddnsEnabled());
+ ASSERT_FALSE(mgr.amSending());
+
+ // Verify a valid config with ddns enabled configures ddns properly,
+ // but does not start the sender.
+ ASSERT_NO_FATAL_FAILURE(configureD2(true));
+ ASSERT_TRUE(mgr.ddnsEnabled());
+ ASSERT_FALSE(mgr.amSending());
+
+ // Verify that calling start does not throw and starts the sender.
+ ASSERT_NO_THROW(srv_.startD2());
+ ASSERT_TRUE(mgr.amSending());
+
+ // Verify a valid config with ddns disabled configures ddns properly.
+ // Sender should not have been started.
+ ASSERT_NO_FATAL_FAILURE(configureD2(false));
+ ASSERT_FALSE(mgr.ddnsEnabled());
+ ASSERT_FALSE(mgr.amSending());
+
+ // Verify that the sender does NOT get started when ddns is disabled.
+ srv_.startD2();
+ ASSERT_FALSE(mgr.amSending());
+}
+
+// Tests Dhcp4 server's ability to correctly handle a flawed dhcp-ddns configuration.
+// It does so by first enabling updates by submitting a valid configuration and then
+// ensuring they remain on after submitting a flawed configuration.
+// and then invoking its startD2() method.
+TEST_F(Dhcp4SrvD2Test, badConfig) {
+ // Grab the manager and verify that be default ddns is off
+ // and a sender was not started.
+ dhcp::D2ClientMgr& mgr = CfgMgr::instance().getD2ClientMgr();
+ ASSERT_FALSE(mgr.ddnsEnabled());
+
+ // Configure it enabled and start it.
+ ASSERT_NO_FATAL_FAILURE(configureD2(true));
+ ASSERT_TRUE(mgr.ddnsEnabled());
+ ASSERT_NO_THROW(srv_.startD2());
+ ASSERT_TRUE(mgr.amSending());
+
+ // Now attempt to give it an invalid configuration.
+ // Result should indicate failure.
+ ASSERT_NO_FATAL_FAILURE(configureD2(false, SHOULD_FAIL, "bogus_ip"));
+
+ // Configure was not altered, so ddns should be enabled and still sending.
+ ASSERT_TRUE(mgr.ddnsEnabled());
+ ASSERT_TRUE(mgr.amSending());
+
+ // Verify that calling start does not throw or stop the sender.
+ ASSERT_NO_THROW(srv_.startD2());
+ ASSERT_TRUE(mgr.amSending());
+
+}
+
+// Checks that submitting an identical dhcp-ddns configuration
+// is handled properly. Not effect should be no change in
+// status for ddns updating. Updates should still enabled and
+// in send mode. This indicates that the sender was not stopped.
+TEST_F(Dhcp4SrvD2Test, sameConfig) {
+ // Grab the manager and verify that be default ddns is off
+ // and a sender was not started.
+ dhcp::D2ClientMgr& mgr = CfgMgr::instance().getD2ClientMgr();
+ ASSERT_FALSE(mgr.ddnsEnabled());
+
+ // Configure it enabled and start it.
+ ASSERT_NO_FATAL_FAILURE(configureD2(true));
+ ASSERT_TRUE(mgr.ddnsEnabled());
+ ASSERT_NO_THROW(srv_.startD2());
+ ASSERT_TRUE(mgr.amSending());
+
+ // Now submit an identical configuration.
+ ASSERT_NO_FATAL_FAILURE(configureD2(true));
+
+ // Configuration was not altered, so ddns should still enabled and sending.
+ ASSERT_TRUE(mgr.ddnsEnabled());
+ ASSERT_TRUE(mgr.amSending());
+
+ // Verify that calling start does not throw or stop the sender.
+ ASSERT_NO_THROW(srv_.startD2());
+ ASSERT_TRUE(mgr.amSending());
+}
+
+// Checks that submitting an different, but valid dhcp-ddns configuration
+// is handled properly. Updates should be enabled, however they should
+// not yet be running. This indicates that the sender was stopped and
+// replaced, but not yet started.
+TEST_F(Dhcp4SrvD2Test, differentConfig) {
+ // Grab the manager and verify that be default ddns is off
+ // and a sender was not started.
+ dhcp::D2ClientMgr& mgr = CfgMgr::instance().getD2ClientMgr();
+ ASSERT_FALSE(mgr.ddnsEnabled());
+
+ // Configure it enabled and start it.
+ ASSERT_NO_FATAL_FAILURE(configureD2(true));
+ ASSERT_TRUE(mgr.ddnsEnabled());
+ ASSERT_NO_THROW(srv_.startD2());
+ ASSERT_TRUE(mgr.amSending());
+
+ // Now enable it on a different port.
+ ASSERT_NO_FATAL_FAILURE(configureD2(true, SHOULD_PASS, "127.0.0.1", 54001));
+
+ // Configuration was altered, so ddns should still enabled but not sending.
+ ASSERT_TRUE(mgr.ddnsEnabled());
+ ASSERT_FALSE(mgr.amSending());
+
+ // Verify that calling start starts the sender.
+ ASSERT_NO_THROW(srv_.startD2());
+ ASSERT_TRUE(mgr.amSending());
+}
+
+// Checks that given a valid, enabled configuration and placing
+// sender in send mode, permits NCR requests to be sent via UPD
+// socket. Note this test does not employ any sort of receiving
+// client to verify actual transmission. These types of tests
+// are including under dhcp_ddns and d2 unit testing.
+TEST_F(Dhcp4SrvD2Test, simpleUDPSend) {
+ // Grab the manager and verify that be default ddns is off
+ // and a sender was not started.
+ dhcp::D2ClientMgr& mgr = CfgMgr::instance().getD2ClientMgr();
+ ASSERT_FALSE(mgr.ddnsEnabled());
+
+ // Configure it enabled and start it.
+ ASSERT_NO_FATAL_FAILURE(configureD2(true));
+ ASSERT_TRUE(mgr.ddnsEnabled());
+ ASSERT_NO_THROW(srv_.startD2());
+ ASSERT_TRUE(mgr.amSending());
+
+ // Verify that we can queue up a message.
+ dhcp_ddns::NameChangeRequestPtr ncr = buildTestNcr();
+ ASSERT_NO_THROW(mgr.sendRequest(ncr));
+ EXPECT_EQ(1, mgr.getQueueSize());
+
+ // Calling receive should detect the ready IO on the sender's select-fd,
+ // and invoke callback, which should complete the send.
+ ASSERT_NO_THROW(IfaceMgr::instance().receive4(0,0));
+
+ // Verify the queue is now empty.
+ EXPECT_EQ(0, mgr.getQueueSize());
+}
+
+// Checks that an IO error in sending a request to D2, results in ddns updates being
+// suspended. This indicates that Dhcp4Srv's error handler has been invoked as expected.
+// Note that this unit test relies on an attempt to send to a server address of 0.0.0.0
+// port 0 fails under all OSs.
+TEST_F(Dhcp4SrvD2Test, forceUDPSendFailure) {
+ // Grab the manager and verify that be default ddns is off
+ // and a sender was not started.
+ dhcp::D2ClientMgr& mgr = CfgMgr::instance().getD2ClientMgr();
+ ASSERT_FALSE(mgr.ddnsEnabled());
+
+ // Configure it enabled and start it.
+ // Using server address of 0.0.0.0/0 should induce failure on send.
+ ASSERT_NO_FATAL_FAILURE(configureD2(true, SHOULD_PASS, "0.0.0.0", 0));
+ ASSERT_TRUE(mgr.ddnsEnabled());
+ ASSERT_NO_THROW(srv_.startD2());
+ ASSERT_TRUE(mgr.amSending());
+
+ // Queue up 3 messages.
+ for (int i = 0; i < 3; i++) {
+ dhcp_ddns::NameChangeRequestPtr ncr = buildTestNcr(i + 1);
+ ASSERT_NO_THROW(mgr.sendRequest(ncr));
+ }
+ EXPECT_EQ(3, mgr.getQueueSize());
+
+ // Calling receive should detect the ready IO on the sender's select-fd,
+ // and invoke callback, which should complete the send, which should
+ // fail.
+ ASSERT_NO_THROW(IfaceMgr::instance().receive4(0,0));
+
+ // Verify the error handler was invoked.
+ EXPECT_EQ(1, srv_.error_count_);
+
+ // Verify that updates are disabled and we are no longer sending.
+ ASSERT_FALSE(mgr.ddnsEnabled());
+ ASSERT_FALSE(mgr.amSending());
+
+ // Verify message is still in the queue.
+ EXPECT_EQ(3, mgr.getQueueSize());
+
+ // Verify that we can't just restart it.
+ /// @todo This may change if we add ability to resume.
+ ASSERT_NO_THROW(srv_.startD2());
+ ASSERT_FALSE(mgr.amSending());
+
+ // Configure it enabled and start it.
+ ASSERT_NO_FATAL_FAILURE(configureD2(true));
+ ASSERT_TRUE(mgr.ddnsEnabled());
+ ASSERT_NO_THROW(srv_.startD2());
+ ASSERT_TRUE(mgr.amSending());
+
+ // Verify message is still in the queue.
+ EXPECT_EQ(3, mgr.getQueueSize());
+
+ // This will finish sending the 1st message in queue
+ // and initiate send of 2nd message.
+ ASSERT_NO_THROW(IfaceMgr::instance().receive4(0,0));
+ EXPECT_EQ(1, srv_.error_count_);
+
+ // First message is off the queue.
+ EXPECT_EQ(2, mgr.getQueueSize());
+}
+
+// Tests error handling of D2ClientMgr::sendRequest() failure
+// by attempting to queue maximum number of messages.
+TEST_F(Dhcp4SrvD2Test, queueMaxError) {
+ // Configure it enabled and start it.
+ dhcp::D2ClientMgr& mgr = CfgMgr::instance().getD2ClientMgr();
+ ASSERT_NO_FATAL_FAILURE(configureD2(true));
+ ASSERT_TRUE(mgr.ddnsEnabled());
+ ASSERT_NO_THROW(srv_.startD2());
+ ASSERT_TRUE(mgr.amSending());
+
+ // Attempt to queue more then the maximum allowed.
+ int max_msgs = mgr.getQueueMaxSize();
+ for (int i = 0; i < max_msgs + 1; i++) {
+ dhcp_ddns::NameChangeRequestPtr ncr = buildTestNcr(i + 1);
+ ASSERT_NO_THROW(mgr.sendRequest(ncr));
+ }
+
+ // Stopping sender will complete the first message so there
+ // should be max less one.
+ EXPECT_EQ(max_msgs - 1, mgr.getQueueSize());
+
+ // Verify the error handler was invoked.
+ EXPECT_EQ(1, srv_.error_count_);
+
+ // Verify that updates are disabled and we are no longer sending.
+ ASSERT_FALSE(mgr.ddnsEnabled());
+ ASSERT_FALSE(mgr.amSending());
+}
+
+
+} // namespace test
+} // namespace dhcp
+} // namespace isc
+
diff --git a/src/bin/dhcp4/tests/d2_unittest.h b/src/bin/dhcp4/tests/d2_unittest.h
new file mode 100644
index 0000000..e33a0af
--- /dev/null
+++ b/src/bin/dhcp4/tests/d2_unittest.h
@@ -0,0 +1,117 @@
+// Copyright (C) 2014 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.
+
+/// @file d2_unittest.h Defines classes for testing Dhcpv4srv with D2ClientMgr
+
+#ifndef D2_UNITTEST_H
+#define D2_UNITTEST_H
+
+#include <dhcp4/dhcp4_srv.h>
+#include <config/ccsession.h>
+
+#include <gtest/gtest.h>
+
+namespace isc {
+namespace dhcp {
+namespace test {
+
+/// @brief Test derivation of Dhcpv4Srv class used in D2 testing.
+/// Use of this class allows the intervention at strategic points in testing
+/// by permitting overridden methods and access to scope protected members.
+class D2Dhcpv4Srv : public Dhcpv4Srv {
+public:
+ /// @brief Counts the number of times the client error handler is called.
+ int error_count_;
+
+ /// @brief Constructor
+ D2Dhcpv4Srv()
+ : Dhcpv4Srv(0, "type=memfile", false, false), error_count_(0) {
+ }
+
+ /// @brief virtual Destructor.
+ virtual ~D2Dhcpv4Srv() {
+ }
+
+ /// @brief Override the error handler.
+ virtual void d2ClientErrorHandler(const dhcp_ddns::NameChangeSender::
+ Result result,
+ dhcp_ddns::NameChangeRequestPtr& ncr);
+};
+
+/// @brief Test fixture which permits testing the interaction between the
+/// D2ClientMgr and Dhcpv4Srv.
+class Dhcp4SrvD2Test : public ::testing::Test {
+public:
+ /// @brief Mnemonic constants for calls to configuration methods.
+ static const bool SHOULD_PASS = true;
+ static const bool SHOULD_FAIL = false;
+
+ /// @brief Constructor
+ Dhcp4SrvD2Test();
+
+ /// @brief virtual Destructor
+ virtual ~Dhcp4SrvD2Test();
+
+ /// @brief Resets the CfgMgr singleton to defaults.
+ /// Primarily used in the test destructor as gtest doesn't exit between
+ /// tests.
+ /// @todo CfgMgr should provide a method to reset everything or maybe
+ /// reconstruct the singleton.
+ void reset();
+
+ /// @brief Configures the server with D2 enabled or disabled
+ ///
+ /// Constructs a configuration string including dhcp-ddns with the
+ /// parameters given and passes it into the server's configuration handler.
+ ///
+ /// @param enable_updates value to assign to the enable-updates parameter
+ /// @param exp_result indicates if configuration should pass or fail
+ /// @param ip_address IP address for the D2 server
+ /// @param port port for the D2 server
+ void configureD2(bool enable_updates, bool exp_result = SHOULD_PASS,
+ const std::string& ip_address = "127.0.0.1",
+ const uint32_t port = 53001);
+
+ /// @brief Configures the server with the given configuration
+ ///
+ /// Passes the given configuration string into the server's configuration
+ /// handler. It accepts a flag indicating whether or not the configuration
+ /// is expected to succeed or fail. This permits testing the server's
+ /// response to both valid and invalid configurations.
+ ///
+ /// @param config JSON string containing the configuration
+ /// @param exp_result indicates if configuration should pass or fail
+ void configure(const std::string& config, bool exp_result = SHOULD_PASS);
+
+ /// @brief Contructs a NameChangeRequest message from a fixed JSON string.
+ ///
+ /// @param dhcid_id_num Integer value to use as the DHCID.
+ dhcp_ddns::NameChangeRequestPtr buildTestNcr(uint32_t
+ dhcid_id_num = 0xdeadbeef);
+
+ /// @brief Stores the return code of the last configuration attempt.
+ int rcode_;
+
+ /// @brief Stores the message component of the last configuration tattempt.
+ isc::data::ConstElementPtr comment_;
+
+ /// @brief Server object under test.
+ D2Dhcpv4Srv srv_;
+};
+
+}; // end of isc::dhcp::test namespace
+}; // end of isc::dhcp namespace
+}; // end of isc namespace
+
+#endif // D2_UNITTEST_H
diff --git a/src/bin/dhcp4/tests/dhcp4_srv_unittest.cc b/src/bin/dhcp4/tests/dhcp4_srv_unittest.cc
index e930ba0..ba787aa 100644
--- a/src/bin/dhcp4/tests/dhcp4_srv_unittest.cc
+++ b/src/bin/dhcp4/tests/dhcp4_srv_unittest.cc
@@ -29,6 +29,7 @@
#include <dhcp/pkt_filter.h>
#include <dhcp/pkt_filter_inet.h>
#include <dhcp/docsis3_option_defs.h>
+#include <dhcp/tests/iface_mgr_test_config.h>
#include <dhcp4/dhcp4_srv.h>
#include <dhcp4/dhcp4_log.h>
#include <dhcp4/config_parser.h>
@@ -61,12 +62,15 @@ namespace {
// This test verifies that the destination address of the response
// message is set to giaddr, when giaddr is set to non-zero address
// in the received message.
-TEST_F(Dhcpv4SrvFakeIfaceTest, adjustIfaceDataRelay) {
+TEST_F(Dhcpv4SrvTest, adjustIfaceDataRelay) {
+ IfaceMgrTestConfig test_config(true);
+ IfaceMgr::instance().openSockets4();
+
// Create the instance of the incoming packet.
boost::shared_ptr<Pkt4> req(new Pkt4(DHCPDISCOVER, 1234));
// Set the giaddr to non-zero address and hops to non-zero value
// as if it was relayed.
- req->setGiaddr(IOAddress("192.0.2.1"));
+ req->setGiaddr(IOAddress("192.0.1.1"));
req->setHops(2);
// Set ciaddr to zero. This simulates the client which applies
// for the new lease.
@@ -75,16 +79,16 @@ TEST_F(Dhcpv4SrvFakeIfaceTest, adjustIfaceDataRelay) {
req->setFlags(0x0000);
// Set local address, port and interface.
- req->setLocalAddr(IOAddress("192.0.3.1"));
+ req->setLocalAddr(IOAddress("192.0.2.1"));
req->setLocalPort(1001);
- req->setIface("eth0");
+ req->setIface("eth1");
req->setIndex(1);
// Create a response packet. Assume that the new lease have
// been created and new address allocated. This address is
// stored in yiaddr field.
boost::shared_ptr<Pkt4> resp(new Pkt4(DHCPOFFER, 1234));
- resp->setYiaddr(IOAddress("192.0.2.100"));
+ resp->setYiaddr(IOAddress("192.0.1.100"));
// Clear the remote address.
resp->setRemoteAddr(IOAddress("0.0.0.0"));
// Set hops value for the response.
@@ -94,40 +98,43 @@ TEST_F(Dhcpv4SrvFakeIfaceTest, adjustIfaceDataRelay) {
ASSERT_NO_THROW(NakedDhcpv4Srv::adjustIfaceData(req, resp));
// Now the destination address should be relay's address.
- EXPECT_EQ("192.0.2.1", resp->getRemoteAddr().toText());
+ EXPECT_EQ("192.0.1.1", resp->getRemoteAddr().toText());
// The query has been relayed, so the response must be sent to the port 67.
EXPECT_EQ(DHCP4_SERVER_PORT, resp->getRemotePort());
- // Local address should be copied from the query message.
- EXPECT_EQ("192.0.3.1", resp->getLocalAddr().toText());
+ // Local address should be the address assigned to interface eth1.
+ EXPECT_EQ("192.0.2.3", resp->getLocalAddr().toText());
// The local port is always DHCPv4 server port 67.
EXPECT_EQ(DHCP4_SERVER_PORT, resp->getLocalPort());
// We will send response over the same interface which was used to receive
// query.
- EXPECT_EQ("eth0", resp->getIface());
+ EXPECT_EQ("eth1", resp->getIface());
EXPECT_EQ(1, resp->getIndex());
// Let's do another test and set other fields: ciaddr and
// flags. By doing it, we want to make sure that the relay
// address will take precedence.
- req->setGiaddr(IOAddress("192.0.2.50"));
- req->setCiaddr(IOAddress("192.0.2.11"));
+ req->setGiaddr(IOAddress("192.0.1.50"));
+ req->setCiaddr(IOAddress("192.0.1.11"));
req->setFlags(Pkt4::FLAG_BROADCAST_MASK);
- resp->setYiaddr(IOAddress("192.0.2.100"));
+ resp->setYiaddr(IOAddress("192.0.1.100"));
// Clear remote address.
resp->setRemoteAddr(IOAddress("0.0.0.0"));
ASSERT_NO_THROW(NakedDhcpv4Srv::adjustIfaceData(req, resp));
// Response should be sent back to the relay address.
- EXPECT_EQ("192.0.2.50", resp->getRemoteAddr().toText());
+ EXPECT_EQ("192.0.1.50", resp->getRemoteAddr().toText());
}
// This test verifies that the destination address of the response message
// is set to ciaddr when giaddr is set to zero and the ciaddr is set to
// non-zero address in the received message. This is the case when the
// client is in Renew or Rebind state.
-TEST_F(Dhcpv4SrvFakeIfaceTest, adjustIfaceDataRenew) {
+TEST_F(Dhcpv4SrvTest, adjustIfaceDataRenew) {
+ IfaceMgrTestConfig test_config(true);
+ IfaceMgr::instance().openSockets4();
+
// Create instance of the incoming packet.
boost::shared_ptr<Pkt4> req(new Pkt4(DHCPDISCOVER, 1234));
@@ -136,7 +143,7 @@ TEST_F(Dhcpv4SrvFakeIfaceTest, adjustIfaceDataRenew) {
// Set ciaddr to non-zero address. The response should be sent to this
// address as the client is in renewing or rebinding state (it is fully
// configured).
- req->setCiaddr(IOAddress("192.0.2.15"));
+ req->setCiaddr(IOAddress("192.0.1.15"));
// Let's configure broadcast flag. It should be ignored because
// we are responding directly to the client having an address
// and trying to extend his lease. Broadcast flag is only used
@@ -147,11 +154,11 @@ TEST_F(Dhcpv4SrvFakeIfaceTest, adjustIfaceDataRenew) {
// This is a direct message, so the hops should be cleared.
req->setHops(0);
// Set local unicast address as if we are renewing a lease.
- req->setLocalAddr(IOAddress("192.0.3.1"));
+ req->setLocalAddr(IOAddress("192.0.2.1"));
// Request is received on the DHCPv4 server port.
req->setLocalPort(DHCP4_SERVER_PORT);
// Set the interface. The response should be sent over the same interface.
- req->setIface("eth0");
+ req->setIface("eth1");
req->setIndex(1);
// Create a response.
@@ -160,7 +167,7 @@ TEST_F(Dhcpv4SrvFakeIfaceTest, adjustIfaceDataRenew) {
// it will actually get different address. The response
// should not be sent to this address but rather to ciaddr
// as client still have ciaddr configured.
- resp->setYiaddr(IOAddress("192.0.2.13"));
+ resp->setYiaddr(IOAddress("192.0.1.13"));
// Clear the remote address.
resp->setRemoteAddr(IOAddress("0.0.0.0"));
// Copy hops value from the query.
@@ -169,17 +176,17 @@ TEST_F(Dhcpv4SrvFakeIfaceTest, adjustIfaceDataRenew) {
ASSERT_NO_THROW(NakedDhcpv4Srv::adjustIfaceData(req, resp));
// Check that server responds to ciaddr
- EXPECT_EQ("192.0.2.15", resp->getRemoteAddr().toText());
+ EXPECT_EQ("192.0.1.15", resp->getRemoteAddr().toText());
// The query was non-relayed, so the response should be sent to a DHCPv4
// client port 68.
EXPECT_EQ(DHCP4_CLIENT_PORT, resp->getRemotePort());
// The response should be sent from the unicast address on which the
// query has been received.
- EXPECT_EQ("192.0.3.1", resp->getLocalAddr().toText());
+ EXPECT_EQ("192.0.2.3", resp->getLocalAddr().toText());
// The response should be sent from the DHCPv4 server port.
EXPECT_EQ(DHCP4_SERVER_PORT, resp->getLocalPort());
// The interface data should match the data in the query.
- EXPECT_EQ("eth0", resp->getIface());
+ EXPECT_EQ("eth1", resp->getIface());
EXPECT_EQ(1, resp->getIndex());
}
@@ -191,7 +198,10 @@ TEST_F(Dhcpv4SrvFakeIfaceTest, adjustIfaceDataRenew) {
// of the response should be set to yiaddr if server supports direct responses
// to the client which doesn't have an address yet or broadcast if the server
// doesn't support direct responses.
-TEST_F(Dhcpv4SrvFakeIfaceTest, adjustIfaceDataSelect) {
+TEST_F(Dhcpv4SrvTest, adjustIfaceDataSelect) {
+ IfaceMgrTestConfig test_config(true);
+ IfaceMgr::instance().openSockets4();
+
// Create instance of the incoming packet.
boost::shared_ptr<Pkt4> req(new Pkt4(DHCPDISCOVER, 1234));
@@ -210,13 +220,13 @@ TEST_F(Dhcpv4SrvFakeIfaceTest, adjustIfaceDataSelect) {
// The query has been received on the DHCPv4 server port 67.
req->setLocalPort(DHCP4_SERVER_PORT);
// Set the interface. The response should be sent via the same interface.
- req->setIface("eth0");
+ req->setIface("eth1");
req->setIndex(1);
// Create a response.
boost::shared_ptr<Pkt4> resp(new Pkt4(DHCPOFFER, 1234));
// Assign some new address for this client.
- resp->setYiaddr(IOAddress("192.0.2.13"));
+ resp->setYiaddr(IOAddress("192.0.1.13"));
// Clear the remote address.
resp->setRemoteAddr(IOAddress("0.0.0.0"));
// Copy hops count.
@@ -227,7 +237,7 @@ TEST_F(Dhcpv4SrvFakeIfaceTest, adjustIfaceDataSelect) {
// case, the server should send its response to the broadcast address.
// We can control whether the current packet filter returns that its support
// direct responses or not.
- current_pkt_filter_->direct_resp_supported_ = false;
+ test_config.setDirectResponse(false);
// When running unit tests, the IfaceMgr is using the default Packet
// Filtering class, PktFilterInet. This class does not support direct
@@ -244,14 +254,14 @@ TEST_F(Dhcpv4SrvFakeIfaceTest, adjustIfaceDataSelect) {
// Although the query has been sent to the broadcast address, the
// server should select a unicast address on the particular interface
// as a source address for the response.
- EXPECT_EQ("192.0.3.1", resp->getLocalAddr().toText());
+ EXPECT_EQ("192.0.2.3", resp->getLocalAddr().toText());
// The response should be sent from the DHCPv4 server port.
EXPECT_EQ(DHCP4_SERVER_PORT, resp->getLocalPort());
// The response should be sent via the same interface through which
// query has been received.
- EXPECT_EQ("eth0", resp->getIface());
+ EXPECT_EQ("eth1", resp->getIface());
EXPECT_EQ(1, resp->getIndex());
// We also want to test the case when the server has capability to
@@ -260,13 +270,13 @@ TEST_F(Dhcpv4SrvFakeIfaceTest, adjustIfaceDataSelect) {
// response based on the capability reported by IfaceMgr. We can
// control whether the current packet filter returns that it supports
// direct responses or not.
- current_pkt_filter_->direct_resp_supported_ = true;
+ test_config.setDirectResponse(true);
// Now we expect that the server will send its response to the
// address assigned for the client.
ASSERT_NO_THROW(NakedDhcpv4Srv::adjustIfaceData(req, resp));
- EXPECT_EQ("192.0.2.13", resp->getRemoteAddr().toText());
+ EXPECT_EQ("192.0.1.13", resp->getRemoteAddr().toText());
}
// This test verifies that the destination address of the response message
@@ -274,7 +284,10 @@ TEST_F(Dhcpv4SrvFakeIfaceTest, adjustIfaceDataSelect) {
// query. Client sets this flag to indicate that it can't receive direct
// responses from the server when it doesn't have its interface configured.
// Server must respect broadcast flag.
-TEST_F(Dhcpv4SrvFakeIfaceTest, adjustIfaceDataBroadcast) {
+TEST_F(Dhcpv4SrvTest, adjustIfaceDataBroadcast) {
+ IfaceMgrTestConfig test_config(true);
+ IfaceMgr::instance().openSockets4();
+
// Create instance of the incoming packet.
boost::shared_ptr<Pkt4> req(new Pkt4(DHCPDISCOVER, 1234));
@@ -287,7 +300,7 @@ TEST_F(Dhcpv4SrvFakeIfaceTest, adjustIfaceDataBroadcast) {
// The query has been received on the DHCPv4 server port 67.
req->setLocalPort(DHCP4_SERVER_PORT);
// Set the interface. The response should be sent via the same interface.
- req->setIface("eth0");
+ req->setIface("eth1");
req->setIndex(1);
// Let's set the broadcast flag.
@@ -296,7 +309,7 @@ TEST_F(Dhcpv4SrvFakeIfaceTest, adjustIfaceDataBroadcast) {
// Create a response.
boost::shared_ptr<Pkt4> resp(new Pkt4(DHCPOFFER, 1234));
// Assign some new address for this client.
- resp->setYiaddr(IOAddress("192.0.2.13"));
+ resp->setYiaddr(IOAddress("192.0.1.13"));
// Clear the remote address.
resp->setRemoteAddr(IOAddress("0.0.0.0"));
@@ -310,21 +323,21 @@ TEST_F(Dhcpv4SrvFakeIfaceTest, adjustIfaceDataBroadcast) {
// Although the query has been sent to the broadcast address, the
// server should select a unicast address on the particular interface
// as a source address for the response.
- EXPECT_EQ("192.0.3.1", resp->getLocalAddr().toText());
+ EXPECT_EQ("192.0.2.3", resp->getLocalAddr().toText());
// The response should be sent from the DHCPv4 server port.
EXPECT_EQ(DHCP4_SERVER_PORT, resp->getLocalPort());
// The response should be sent via the same interface through which
// query has been received.
- EXPECT_EQ("eth0", resp->getIface());
+ EXPECT_EQ("eth1", resp->getIface());
EXPECT_EQ(1, resp->getIndex());
}
// This test verifies that exception is thrown of the invalid combination
// of giaddr and hops is specified in a client's message.
-TEST_F(Dhcpv4SrvFakeIfaceTest, adjustIfaceDataInvalid) {
+TEST_F(Dhcpv4SrvTest, adjustIfaceDataInvalid) {
boost::shared_ptr<Pkt4> req(new Pkt4(DHCPDISCOVER, 1234));
// The hops and giaddr values are used to determine if the client's
@@ -435,7 +448,7 @@ TEST_F(Dhcpv4SrvTest, openActiveSockets) {
// are other tests that verify correctness of the allocation
// engine. See DiscoverBasic, DiscoverHint, DiscoverNoClientId
// and DiscoverInvalidHint.
-TEST_F(Dhcpv4SrvFakeIfaceTest, processDiscover) {
+TEST_F(Dhcpv4SrvTest, processDiscover) {
testDiscoverRequest(DHCPDISCOVER);
}
@@ -447,11 +460,11 @@ TEST_F(Dhcpv4SrvFakeIfaceTest, processDiscover) {
// are other tests that verify correctness of the allocation
// engine. See DiscoverBasic, DiscoverHint, DiscoverNoClientId
// and DiscoverInvalidHint.
-TEST_F(Dhcpv4SrvFakeIfaceTest, processRequest) {
+TEST_F(Dhcpv4SrvTest, processRequest) {
testDiscoverRequest(DHCPREQUEST);
}
-TEST_F(Dhcpv4SrvFakeIfaceTest, processRelease) {
+TEST_F(Dhcpv4SrvTest, processRelease) {
NakedDhcpv4Srv srv;
Pkt4Ptr pkt(new Pkt4(DHCPRELEASE, 1234));
@@ -459,7 +472,7 @@ TEST_F(Dhcpv4SrvFakeIfaceTest, processRelease) {
EXPECT_NO_THROW(srv.processRelease(pkt));
}
-TEST_F(Dhcpv4SrvFakeIfaceTest, processDecline) {
+TEST_F(Dhcpv4SrvTest, processDecline) {
NakedDhcpv4Srv srv;
Pkt4Ptr pkt(new Pkt4(DHCPDECLINE, 1234));
@@ -467,7 +480,7 @@ TEST_F(Dhcpv4SrvFakeIfaceTest, processDecline) {
EXPECT_NO_THROW(srv.processDecline(pkt));
}
-TEST_F(Dhcpv4SrvFakeIfaceTest, processInform) {
+TEST_F(Dhcpv4SrvTest, processInform) {
NakedDhcpv4Srv srv;
Pkt4Ptr pkt(new Pkt4(DHCPINFORM, 1234));
@@ -524,7 +537,10 @@ TEST_F(Dhcpv4SrvTest, serverReceivedPacketName) {
// - copy of client-id
// - server-id
// - offered address
-TEST_F(Dhcpv4SrvFakeIfaceTest, DiscoverBasic) {
+TEST_F(Dhcpv4SrvTest, DiscoverBasic) {
+ IfaceMgrTestConfig test_config(true);
+ IfaceMgr::instance().openSockets4();
+
boost::scoped_ptr<NakedDhcpv4Srv> srv;
ASSERT_NO_THROW(srv.reset(new NakedDhcpv4Srv(0)));
@@ -532,7 +548,7 @@ TEST_F(Dhcpv4SrvFakeIfaceTest, DiscoverBasic) {
dis->setRemoteAddr(IOAddress("192.0.2.1"));
OptionPtr clientid = generateClientId();
dis->addOption(clientid);
- dis->setIface("eth0");
+ dis->setIface("eth1");
// Pass it to the server and get an offer
Pkt4Ptr offer = srv->processDiscover(dis);
@@ -562,7 +578,10 @@ TEST_F(Dhcpv4SrvFakeIfaceTest, DiscoverBasic) {
// - copy of client-id
// - server-id
// - offered address
-TEST_F(Dhcpv4SrvFakeIfaceTest, DiscoverHint) {
+TEST_F(Dhcpv4SrvTest, DiscoverHint) {
+ IfaceMgrTestConfig test_config(true);
+ IfaceMgr::instance().openSockets4();
+
boost::scoped_ptr<NakedDhcpv4Srv> srv;
ASSERT_NO_THROW(srv.reset(new NakedDhcpv4Srv(0)));
IOAddress hint("192.0.2.107");
@@ -572,7 +591,7 @@ TEST_F(Dhcpv4SrvFakeIfaceTest, DiscoverHint) {
OptionPtr clientid = generateClientId();
dis->addOption(clientid);
dis->setYiaddr(hint);
- dis->setIface("eth0");
+ dis->setIface("eth1");
// Pass it to the server and get an offer
Pkt4Ptr offer = srv->processDiscover(dis);
@@ -603,7 +622,10 @@ TEST_F(Dhcpv4SrvFakeIfaceTest, DiscoverHint) {
// - copy of client-id
// - server-id
// - offered address
-TEST_F(Dhcpv4SrvFakeIfaceTest, DiscoverNoClientId) {
+TEST_F(Dhcpv4SrvTest, DiscoverNoClientId) {
+ IfaceMgrTestConfig test_config(true);
+ IfaceMgr::instance().openSockets4();
+
boost::scoped_ptr<NakedDhcpv4Srv> srv;
ASSERT_NO_THROW(srv.reset(new NakedDhcpv4Srv(0)));
IOAddress hint("192.0.2.107");
@@ -612,7 +634,7 @@ TEST_F(Dhcpv4SrvFakeIfaceTest, DiscoverNoClientId) {
dis->setRemoteAddr(IOAddress("192.0.2.1"));
dis->setYiaddr(hint);
dis->setHWAddr(generateHWAddr(6));
- dis->setIface("eth0");
+ dis->setIface("eth1");
// Pass it to the server and get an offer
Pkt4Ptr offer = srv->processDiscover(dis);
@@ -642,7 +664,10 @@ TEST_F(Dhcpv4SrvFakeIfaceTest, DiscoverNoClientId) {
// - copy of client-id
// - server-id
// - offered address (!= hint)
-TEST_F(Dhcpv4SrvFakeIfaceTest, DiscoverInvalidHint) {
+TEST_F(Dhcpv4SrvTest, DiscoverInvalidHint) {
+ IfaceMgrTestConfig test_config(true);
+ IfaceMgr::instance().openSockets4();
+
boost::scoped_ptr<NakedDhcpv4Srv> srv;
ASSERT_NO_THROW(srv.reset(new NakedDhcpv4Srv(0)));
IOAddress hint("10.1.2.3");
@@ -652,7 +677,7 @@ TEST_F(Dhcpv4SrvFakeIfaceTest, DiscoverInvalidHint) {
OptionPtr clientid = generateClientId();
dis->addOption(clientid);
dis->setYiaddr(hint);
- dis->setIface("eth0");
+ dis->setIface("eth1");
// Pass it to the server and get an offer
Pkt4Ptr offer = srv->processDiscover(dis);
@@ -681,7 +706,10 @@ TEST_F(Dhcpv4SrvFakeIfaceTest, DiscoverInvalidHint) {
// and this is a correct behavior. It is REQUEST that will fail for the third
// client. OFFER is basically saying "if you send me a request, you will
// probably get an address like this" (there are no guarantees).
-TEST_F(Dhcpv4SrvFakeIfaceTest, ManyDiscovers) {
+TEST_F(Dhcpv4SrvTest, ManyDiscovers) {
+ IfaceMgrTestConfig test_config(true);
+ IfaceMgr::instance().openSockets4();
+
boost::scoped_ptr<NakedDhcpv4Srv> srv;
ASSERT_NO_THROW(srv.reset(new NakedDhcpv4Srv(0)));
@@ -694,9 +722,9 @@ TEST_F(Dhcpv4SrvFakeIfaceTest, ManyDiscovers) {
dis3->setRemoteAddr(IOAddress("192.0.2.3"));
// Assign interfaces
- dis1->setIface("eth0");
- dis2->setIface("eth0");
- dis3->setIface("eth0");
+ dis1->setIface("eth1");
+ dis2->setIface("eth1");
+ dis3->setIface("eth1");
// Different client-id sizes
OptionPtr clientid1 = generateClientId(4); // length 4
@@ -746,14 +774,17 @@ TEST_F(Dhcpv4SrvFakeIfaceTest, ManyDiscovers) {
// Checks whether echoing back client-id is controllable, i.e.
// whether the server obeys echo-client-id and sends (or not)
// client-id
-TEST_F(Dhcpv4SrvFakeIfaceTest, discoverEchoClientId) {
+TEST_F(Dhcpv4SrvTest, discoverEchoClientId) {
+ IfaceMgrTestConfig test_config(true);
+ IfaceMgr::instance().openSockets4();
+
NakedDhcpv4Srv srv(0);
Pkt4Ptr dis = Pkt4Ptr(new Pkt4(DHCPDISCOVER, 1234));
dis->setRemoteAddr(IOAddress("192.0.2.1"));
OptionPtr clientid = generateClientId();
dis->addOption(clientid);
- dis->setIface("eth0");
+ dis->setIface("eth1");
// Pass it to the server and get an offer
Pkt4Ptr offer = srv.processDiscover(dis);
@@ -785,7 +816,10 @@ TEST_F(Dhcpv4SrvFakeIfaceTest, discoverEchoClientId) {
// - assigned address
//
// Test verifies that the lease is actually in the database.
-TEST_F(Dhcpv4SrvFakeIfaceTest, RequestBasic) {
+TEST_F(Dhcpv4SrvTest, RequestBasic) {
+ IfaceMgrTestConfig test_config(true);
+ IfaceMgr::instance().openSockets4();
+
boost::scoped_ptr<NakedDhcpv4Srv> srv;
ASSERT_NO_THROW(srv.reset(new NakedDhcpv4Srv(0)));
@@ -795,7 +829,7 @@ TEST_F(Dhcpv4SrvFakeIfaceTest, RequestBasic) {
OptionPtr clientid = generateClientId();
req->addOption(clientid);
req->setYiaddr(hint);
- req->setIface("eth0");
+ req->setIface("eth1");
// Pass it to the server and get an advertise
Pkt4Ptr ack = srv->processRequest(req);
@@ -831,7 +865,9 @@ TEST_F(Dhcpv4SrvFakeIfaceTest, RequestBasic) {
// - copy of client-id
// - server-id
// - assigned address (different for each client)
-TEST_F(Dhcpv4SrvFakeIfaceTest, ManyRequests) {
+TEST_F(Dhcpv4SrvTest, ManyRequests) {
+ IfaceMgrTestConfig test_config(true);
+ IfaceMgr::instance().openSockets4();
boost::scoped_ptr<NakedDhcpv4Srv> srv;
ASSERT_NO_THROW(srv.reset(new NakedDhcpv4Srv(0)));
@@ -850,9 +886,9 @@ TEST_F(Dhcpv4SrvFakeIfaceTest, ManyRequests) {
req3->setRemoteAddr(relay);
// Assign interfaces
- req1->setIface("eth0");
- req2->setIface("eth0");
- req3->setIface("eth0");
+ req1->setIface("eth1");
+ req2->setIface("eth1");
+ req3->setIface("eth1");
req1->setYiaddr(req_addr1);
req2->setYiaddr(req_addr2);
@@ -919,14 +955,17 @@ TEST_F(Dhcpv4SrvFakeIfaceTest, ManyRequests) {
}
// Checks whether echoing back client-id is controllable
-TEST_F(Dhcpv4SrvFakeIfaceTest, requestEchoClientId) {
+TEST_F(Dhcpv4SrvTest, requestEchoClientId) {
+ IfaceMgrTestConfig test_config(true);
+ IfaceMgr::instance().openSockets4();
+
NakedDhcpv4Srv srv(0);
Pkt4Ptr dis = Pkt4Ptr(new Pkt4(DHCPREQUEST, 1234));
dis->setRemoteAddr(IOAddress("192.0.2.1"));
OptionPtr clientid = generateClientId();
dis->addOption(clientid);
- dis->setIface("eth0");
+ dis->setIface("eth1");
// Pass it to the server and get ACK
Pkt4Ptr ack = srv.processRequest(dis);
@@ -953,7 +992,10 @@ TEST_F(Dhcpv4SrvFakeIfaceTest, requestEchoClientId) {
// - returned REPLY message has server-id
// - returned REPLY message has IA that includes IAADDR
// - lease is actually renewed in LeaseMgr
-TEST_F(Dhcpv4SrvFakeIfaceTest, RenewBasic) {
+TEST_F(Dhcpv4SrvTest, RenewBasic) {
+ IfaceMgrTestConfig test_config(true);
+ IfaceMgr::instance().openSockets4();
+
boost::scoped_ptr<NakedDhcpv4Srv> srv;
ASSERT_NO_THROW(srv.reset(new NakedDhcpv4Srv(0)));
@@ -1038,7 +1080,10 @@ TEST_F(Dhcpv4SrvFakeIfaceTest, RenewBasic) {
// identifiers used by a server is accepted,
// - a message with a server identifier which doesn't match any server
// identifier used by a server, is not accepted.
-TEST_F(Dhcpv4SrvFakeIfaceTest, acceptServerId) {
+TEST_F(Dhcpv4SrvTest, acceptServerId) {
+ IfaceMgrTestConfig test_config(true);
+ IfaceMgr::instance().openSockets4();
+
NakedDhcpv4Srv srv(0);
Pkt4Ptr pkt(new Pkt4(DHCPREQUEST, 1234));
@@ -1065,7 +1110,7 @@ TEST_F(Dhcpv4SrvFakeIfaceTest, acceptServerId) {
// Add a server id being an IPv4 address configured on eth0 interface.
// A DHCPv4 message holding this server identifier should be accepted.
OptionCustomPtr eth0_serverid(new OptionCustom(def, Option::V6));
- eth0_serverid->writeAddress(IOAddress("192.0.3.1"));
+ eth0_serverid->writeAddress(IOAddress("192.0.2.3"));
ASSERT_NO_THROW(pkt->addOption(eth0_serverid));
EXPECT_TRUE(srv.acceptServerId(pkt));
@@ -1117,7 +1162,7 @@ TEST_F(Dhcpv4SrvTest, sanityCheck) {
// This test verifies that incoming (positive) RELEASE can be handled properly.
// As there is no REPLY in DHCPv4, the only thing to verify here is that
// the lease is indeed removed from the database.
-TEST_F(Dhcpv4SrvFakeIfaceTest, ReleaseBasic) {
+TEST_F(Dhcpv4SrvTest, ReleaseBasic) {
boost::scoped_ptr<NakedDhcpv4Srv> srv;
ASSERT_NO_THROW(srv.reset(new NakedDhcpv4Srv(0)));
@@ -1189,7 +1234,7 @@ TEST_F(Dhcpv4SrvFakeIfaceTest, ReleaseBasic) {
// 1. there is no such lease at all
// 2. there is such a lease, but it is assigned to a different IAID
// 3. there is such a lease, but it belongs to a different client
-TEST_F(Dhcpv4SrvFakeIfaceTest, ReleaseReject) {
+TEST_F(Dhcpv4SrvTest, ReleaseReject) {
boost::scoped_ptr<NakedDhcpv4Srv> srv;
ASSERT_NO_THROW(srv.reset(new NakedDhcpv4Srv(0)));
@@ -1278,7 +1323,9 @@ TEST_F(Dhcpv4SrvFakeIfaceTest, ReleaseReject) {
}
// Checks if received relay agent info option is echoed back to the client
-TEST_F(Dhcpv4SrvFakeIfaceTest, relayAgentInfoEcho) {
+TEST_F(Dhcpv4SrvTest, relayAgentInfoEcho) {
+ IfaceMgrTestConfig test_config(true);
+ IfaceMgr::instance().openSockets4();
NakedDhcpv4Srv srv(0);
@@ -1321,7 +1368,9 @@ TEST_F(Dhcpv4SrvFakeIfaceTest, relayAgentInfoEcho) {
// Checks if vendor options are parsed correctly and requested vendor options
// are echoed back.
-TEST_F(Dhcpv4SrvFakeIfaceTest, vendorOptionsDocsis) {
+TEST_F(Dhcpv4SrvTest, vendorOptionsDocsis) {
+ IfaceMgrTestConfig test_config(true);
+ IfaceMgr::instance().openSockets4();
NakedDhcpv4Srv srv(0);
@@ -1515,7 +1564,10 @@ TEST_F(Dhcpv4SrvTest, unpackOptions) {
// Checks whether the server uses default (0.0.0.0) siaddr value, unless
// explicitly specified
-TEST_F(Dhcpv4SrvFakeIfaceTest, siaddrDefault) {
+TEST_F(Dhcpv4SrvTest, siaddrDefault) {
+ IfaceMgrTestConfig test_config(true);
+ IfaceMgr::instance().openSockets4();
+
boost::scoped_ptr<NakedDhcpv4Srv> srv;
ASSERT_NO_THROW(srv.reset(new NakedDhcpv4Srv(0)));
IOAddress hint("192.0.2.107");
@@ -1525,7 +1577,7 @@ TEST_F(Dhcpv4SrvFakeIfaceTest, siaddrDefault) {
OptionPtr clientid = generateClientId();
dis->addOption(clientid);
dis->setYiaddr(hint);
- dis->setIface("eth0");
+ dis->setIface("eth1");
// Pass it to the server and get an offer
Pkt4Ptr offer = srv->processDiscover(dis);
@@ -1539,14 +1591,17 @@ TEST_F(Dhcpv4SrvFakeIfaceTest, siaddrDefault) {
}
// Checks whether the server uses specified siaddr value
-TEST_F(Dhcpv4SrvFakeIfaceTest, siaddr) {
+TEST_F(Dhcpv4SrvTest, siaddr) {
+ IfaceMgrTestConfig test_config(true);
+ IfaceMgr::instance().openSockets4();
+
boost::scoped_ptr<NakedDhcpv4Srv> srv;
ASSERT_NO_THROW(srv.reset(new NakedDhcpv4Srv(0)));
subnet_->setSiaddr(IOAddress("192.0.2.123"));
Pkt4Ptr dis = Pkt4Ptr(new Pkt4(DHCPDISCOVER, 1234));
dis->setRemoteAddr(IOAddress("192.0.2.1"));
- dis->setIface("eth0");
+ dis->setIface("eth1");
OptionPtr clientid = generateClientId();
dis->addOption(clientid);
@@ -1565,7 +1620,9 @@ TEST_F(Dhcpv4SrvFakeIfaceTest, siaddr) {
// specific value and returned in server messages. There's also similar test for
// checking parser only configuration, see Dhcp4ParserTest.nextServerOverride in
// config_parser_unittest.cc.
-TEST_F(Dhcpv4SrvFakeIfaceTest, nextServerOverride) {
+TEST_F(Dhcpv4SrvTest, nextServerOverride) {
+ IfaceMgrTestConfig test_config(true);
+ IfaceMgr::instance().openSockets4();
NakedDhcpv4Srv srv(0);
@@ -1592,7 +1649,7 @@ TEST_F(Dhcpv4SrvFakeIfaceTest, nextServerOverride) {
Pkt4Ptr dis = Pkt4Ptr(new Pkt4(DHCPDISCOVER, 1234));
dis->setRemoteAddr(IOAddress("192.0.2.1"));
- dis->setIface("eth0");
+ dis->setIface("eth1");
OptionPtr clientid = generateClientId();
dis->addOption(clientid);
@@ -1608,7 +1665,9 @@ TEST_F(Dhcpv4SrvFakeIfaceTest, nextServerOverride) {
// when there is no specific value defined in subnet and returned to the client
// properly. There's also similar test for checking parser only configuration,
// see Dhcp4ParserTest.nextServerGlobal in config_parser_unittest.cc.
-TEST_F(Dhcpv4SrvFakeIfaceTest, nextServerGlobal) {
+TEST_F(Dhcpv4SrvTest, nextServerGlobal) {
+ IfaceMgrTestConfig test_config(true);
+ IfaceMgr::instance().openSockets4();
NakedDhcpv4Srv srv(0);
@@ -1634,7 +1693,7 @@ TEST_F(Dhcpv4SrvFakeIfaceTest, nextServerGlobal) {
Pkt4Ptr dis = Pkt4Ptr(new Pkt4(DHCPDISCOVER, 1234));
dis->setRemoteAddr(IOAddress("192.0.2.1"));
- dis->setIface("eth0");
+ dis->setIface("eth1");
OptionPtr clientid = generateClientId();
dis->addOption(clientid);
@@ -1671,7 +1730,7 @@ const uint8_t dummySname[] = "Lorem ipsum dolor sit amet, consectetur "
/// can't modify non-static members (for obvious reasons), so many
/// fields are declared static. It is still better to keep them as
/// one class rather than unrelated collection of global objects.
-class HooksDhcpv4SrvTest : public Dhcpv4SrvFakeIfaceTest {
+class HooksDhcpv4SrvTest : public Dhcpv4SrvTest {
public:
@@ -1766,7 +1825,13 @@ public:
buf.push_back(1); // length (just one byte)
buf.push_back(static_cast<uint8_t>(DHCPDISCOVER));
- return (Pkt4Ptr(new Pkt4(&buf[0], buf.size())));
+ Pkt4Ptr dis(new Pkt4(&buf[0], buf.size()));
+ // Interface must be selected for a Discover. Server will use the interface
+ // name to select a subnet for a client. This test is using fake interfaces
+ // and the fake eth0 interface has IPv4 address matching the subnet
+ // currently configured for this test.
+ dis->setIface("eth1");
+ return (dis);
}
/// Test callback that stores received callout name and pkt4 value
@@ -2126,6 +2191,8 @@ vector<string> HooksDhcpv4SrvTest::callback_argument_names_;
// Note that the test name does not follow test naming convention,
// but the proper hook name is "buffer4_receive".
TEST_F(HooksDhcpv4SrvTest, Buffer4ReceiveSimple) {
+ IfaceMgrTestConfig test_config(true);
+ IfaceMgr::instance().openSockets4();
// Install pkt4_receive_callout
EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
@@ -2159,6 +2226,9 @@ TEST_F(HooksDhcpv4SrvTest, Buffer4ReceiveSimple) {
// Checks if callouts installed on buffer4_receive is able to change
// the values and the parameters are indeed used by the server.
TEST_F(HooksDhcpv4SrvTest, buffer4ReceiveValueChange) {
+ IfaceMgrTestConfig test_config(true);
+ IfaceMgr::instance().openSockets4();
+
// Install callback that modifies MAC addr of incoming packet
EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
@@ -2198,6 +2268,8 @@ TEST_F(HooksDhcpv4SrvTest, buffer4ReceiveValueChange) {
// the server should eventually drop it, because there won't be mandatory options
// (or rather option objects) in it.
TEST_F(HooksDhcpv4SrvTest, buffer4ReceiveSkip) {
+ IfaceMgrTestConfig test_config(true);
+ IfaceMgr::instance().openSockets4();
// Install pkt4_receive_callout
EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
@@ -2225,6 +2297,8 @@ TEST_F(HooksDhcpv4SrvTest, buffer4ReceiveSkip) {
// Note that the test name does not follow test naming convention,
// but the proper hook name is "pkt4_receive".
TEST_F(HooksDhcpv4SrvTest, pkt4ReceiveSimple) {
+ IfaceMgrTestConfig test_config(true);
+ IfaceMgr::instance().openSockets4();
// Install pkt4_receive_callout
EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
@@ -2258,6 +2332,8 @@ TEST_F(HooksDhcpv4SrvTest, pkt4ReceiveSimple) {
// Checks if callouts installed on pkt4_received is able to change
// the values and the parameters are indeed used by the server.
TEST_F(HooksDhcpv4SrvTest, valueChange_pkt4_receive) {
+ IfaceMgrTestConfig test_config(true);
+ IfaceMgr::instance().openSockets4();
// Install pkt4_receive_callout
EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
@@ -2294,6 +2370,8 @@ TEST_F(HooksDhcpv4SrvTest, valueChange_pkt4_receive) {
// existing options and that change impacts server processing (mandatory
// client-id option is deleted, so the packet is expected to be dropped)
TEST_F(HooksDhcpv4SrvTest, pkt4ReceiveDeleteClientId) {
+ IfaceMgrTestConfig test_config(true);
+ IfaceMgr::instance().openSockets4();
// Install pkt4_receive_callout
EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
@@ -2318,6 +2396,8 @@ TEST_F(HooksDhcpv4SrvTest, pkt4ReceiveDeleteClientId) {
// Checks if callouts installed on pkt4_received is able to set skip flag that
// will cause the server to not process the packet (drop), even though it is valid.
TEST_F(HooksDhcpv4SrvTest, pkt4ReceiveSkip) {
+ IfaceMgrTestConfig test_config(true);
+ IfaceMgr::instance().openSockets4();
// Install pkt4_receive_callout
EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
@@ -2343,6 +2423,8 @@ TEST_F(HooksDhcpv4SrvTest, pkt4ReceiveSkip) {
// Checks if callouts installed on pkt4_send are indeed called and the
// all necessary parameters are passed.
TEST_F(HooksDhcpv4SrvTest, pkt4SendSimple) {
+ IfaceMgrTestConfig test_config(true);
+ IfaceMgr::instance().openSockets4();
// Install pkt4_receive_callout
EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
@@ -2379,6 +2461,8 @@ TEST_F(HooksDhcpv4SrvTest, pkt4SendSimple) {
// Checks if callouts installed on pkt4_send is able to change
// the values and the packet sent contains those changes
TEST_F(HooksDhcpv4SrvTest, pkt4SendValueChange) {
+ IfaceMgrTestConfig test_config(true);
+ IfaceMgr::instance().openSockets4();
// Install pkt4_receive_callout
EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
@@ -2416,6 +2500,8 @@ TEST_F(HooksDhcpv4SrvTest, pkt4SendValueChange) {
// we are trying to send a packet without server-id. The packet should
// be sent
TEST_F(HooksDhcpv4SrvTest, pkt4SendDeleteServerId) {
+ IfaceMgrTestConfig test_config(true);
+ IfaceMgr::instance().openSockets4();
// Install pkt4_receive_callout
EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
@@ -2447,6 +2533,8 @@ TEST_F(HooksDhcpv4SrvTest, pkt4SendDeleteServerId) {
// Checks if callouts installed on pkt4_skip is able to set skip flag that
// will cause the server to not process the packet (drop), even though it is valid.
TEST_F(HooksDhcpv4SrvTest, skip_pkt4_send) {
+ IfaceMgrTestConfig test_config(true);
+ IfaceMgr::instance().openSockets4();
// Install pkt4_receive_callout
EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
@@ -2476,6 +2564,8 @@ TEST_F(HooksDhcpv4SrvTest, skip_pkt4_send) {
// Checks if callouts installed on buffer4_send are indeed called and the
// all necessary parameters are passed.
TEST_F(HooksDhcpv4SrvTest, buffer4SendSimple) {
+ IfaceMgrTestConfig test_config(true);
+ IfaceMgr::instance().openSockets4();
// Install pkt4_receive_callout
EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
@@ -2512,6 +2602,8 @@ TEST_F(HooksDhcpv4SrvTest, buffer4SendSimple) {
// Checks if callouts installed on buffer4_send are indeed called and that
// the output buffer can be changed.
TEST_F(HooksDhcpv4SrvTest, buffer4Send) {
+ IfaceMgrTestConfig test_config(true);
+ IfaceMgr::instance().openSockets4();
// Install pkt4_receive_callout
EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
@@ -2541,6 +2633,8 @@ TEST_F(HooksDhcpv4SrvTest, buffer4Send) {
// Checks if callouts installed on buffer4_send can set skip flag and that flag
// causes the packet to not be sent
TEST_F(HooksDhcpv4SrvTest, buffer4SendSkip) {
+ IfaceMgrTestConfig test_config(true);
+ IfaceMgr::instance().openSockets4();
// Install pkt4_receive_callout
EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
@@ -2566,6 +2660,8 @@ TEST_F(HooksDhcpv4SrvTest, buffer4SendSkip) {
// This test checks if subnet4_select callout is triggered and reports
// valid parameters
TEST_F(HooksDhcpv4SrvTest, subnet4SelectSimple) {
+ IfaceMgrTestConfig test_config(true);
+ IfaceMgr::instance().openSockets4();
// Install pkt4_receive_callout
EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
@@ -2598,7 +2694,7 @@ TEST_F(HooksDhcpv4SrvTest, subnet4SelectSimple) {
// Prepare discover packet. Server should select first subnet for it
Pkt4Ptr sol = Pkt4Ptr(new Pkt4(DHCPDISCOVER, 1234));
sol->setRemoteAddr(IOAddress("192.0.2.1"));
- sol->setIface("eth0");
+ sol->setIface("eth1");
OptionPtr clientid = generateClientId();
sol->addOption(clientid);
@@ -2632,6 +2728,8 @@ TEST_F(HooksDhcpv4SrvTest, subnet4SelectSimple) {
// This test checks if callout installed on subnet4_select hook point can pick
// a different subnet.
TEST_F(HooksDhcpv4SrvTest, subnet4SelectChange) {
+ IfaceMgrTestConfig test_config(true);
+ IfaceMgr::instance().openSockets4();
// Install a callout
EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
@@ -2692,6 +2790,8 @@ TEST_F(HooksDhcpv4SrvTest, subnet4SelectChange) {
// properly and that callout installed on lease4_renew is triggered with
// expected parameters.
TEST_F(HooksDhcpv4SrvTest, lease4RenewSimple) {
+ IfaceMgrTestConfig test_config(true);
+ IfaceMgr::instance().openSockets4();
const IOAddress addr("192.0.2.106");
const uint32_t temp_t1 = 50;
@@ -2777,6 +2877,8 @@ TEST_F(HooksDhcpv4SrvTest, lease4RenewSimple) {
// This test verifies that a callout installed on lease4_renew can trigger
// the server to not renew a lease.
TEST_F(HooksDhcpv4SrvTest, lease4RenewSkip) {
+ IfaceMgrTestConfig test_config(true);
+ IfaceMgr::instance().openSockets4();
const IOAddress addr("192.0.2.106");
const uint32_t temp_t1 = 50;
@@ -2842,6 +2944,8 @@ TEST_F(HooksDhcpv4SrvTest, lease4RenewSkip) {
// This test verifies that valid RELEASE triggers lease4_release callouts
TEST_F(HooksDhcpv4SrvTest, lease4ReleaseSimple) {
+ IfaceMgrTestConfig test_config(true);
+ IfaceMgr::instance().openSockets4();
const IOAddress addr("192.0.2.106");
const uint32_t temp_t1 = 50;
@@ -2927,6 +3031,8 @@ TEST_F(HooksDhcpv4SrvTest, lease4ReleaseSimple) {
// This test verifies that skip flag returned by a callout installed on the
// lease4_release hook point will keep the lease
TEST_F(HooksDhcpv4SrvTest, lease4ReleaseSkip) {
+ IfaceMgrTestConfig test_config(true);
+ IfaceMgr::instance().openSockets4();
const IOAddress addr("192.0.2.106");
const uint32_t temp_t1 = 50;
@@ -2990,7 +3096,7 @@ TEST_F(HooksDhcpv4SrvTest, lease4ReleaseSkip) {
}
// Checks if server is able to handle a relayed traffic from DOCSIS3.0 modems
-TEST_F(Dhcpv4SrvFakeIfaceTest, docsisVendorOptionsParse) {
+TEST_F(Dhcpv4SrvTest, docsisVendorOptionsParse) {
// Let's get a traffic capture from DOCSIS3.0 modem
Pkt4Ptr dis = captureRelayedDiscover();
@@ -3014,7 +3120,7 @@ TEST_F(Dhcpv4SrvFakeIfaceTest, docsisVendorOptionsParse) {
}
// Checks if server is able to parse incoming docsis option and extract suboption 1 (docsis ORO)
-TEST_F(Dhcpv4SrvFakeIfaceTest, docsisVendorORO) {
+TEST_F(Dhcpv4SrvTest, docsisVendorORO) {
// Let's get a traffic capture from DOCSIS3.0 modem
Pkt4Ptr dis = captureRelayedDiscover();
@@ -3036,7 +3142,9 @@ TEST_F(Dhcpv4SrvFakeIfaceTest, docsisVendorORO) {
// This test checks if Option Request Option (ORO) in docsis (vendor-id=4491)
// vendor options is parsed correctly and the requested options are actually assigned.
-TEST_F(Dhcpv4SrvFakeIfaceTest, vendorOptionsORO) {
+TEST_F(Dhcpv4SrvTest, vendorOptionsORO) {
+ IfaceMgrTestConfig test_config(true);
+ IfaceMgr::instance().openSockets4();
NakedDhcpv4Srv srv(0);
@@ -3124,7 +3232,7 @@ TEST_F(Dhcpv4SrvFakeIfaceTest, vendorOptionsORO) {
// Test checks whether it is possible to use option definitions defined in
// src/lib/dhcp/docsis3_option_defs.h.
-TEST_F(Dhcpv4SrvFakeIfaceTest, vendorOptionsDocsisDefinitions) {
+TEST_F(Dhcpv4SrvTest, vendorOptionsDocsisDefinitions) {
ConstElementPtr x;
string config_prefix = "{ \"interfaces\": [ \"all\" ],"
"\"rebind-timer\": 2000, "
@@ -3201,4 +3309,162 @@ TEST_F(Dhcpv4SrvTest, clientClassification) {
EXPECT_FALSE(dis2->inClass("docsis3.0"));
}
+// Checks if the client-class field is indeed used for subnet selection.
+// Note that packet classification is already checked in Dhcpv4SrvTest
+// .clientClassification above.
+TEST_F(Dhcpv4SrvTest, clientClassify2) {
+
+ NakedDhcpv4Srv srv(0);
+
+ ConstElementPtr status;
+
+ // This test configures 2 subnets. We actually only need the
+ // first one, but since there's still this ugly hack that picks
+ // the pool if there is only one, we must use more than one
+ // subnet. That ugly hack will be removed in #3242, currently
+ // under review.
+
+ // The second subnet does not play any role here. The client's
+ // IP address belongs to the first subnet, so only that first
+ // subnet it being tested.
+ string config = "{ \"interfaces\": [ \"*\" ],"
+ "\"rebind-timer\": 2000, "
+ "\"renew-timer\": 1000, "
+ "\"subnet4\": [ "
+ "{ \"pool\": [ \"192.0.2.1 - 192.0.2.100\" ],"
+ " \"client-class\": \"foo\", "
+ " \"subnet\": \"192.0.2.0/24\" }, "
+ "{ \"pool\": [ \"192.0.3.1 - 192.0.3.100\" ],"
+ " \"client-class\": \"xyzzy\", "
+ " \"subnet\": \"192.0.3.0/24\" } "
+ "],"
+ "\"valid-lifetime\": 4000 }";
+
+ ElementPtr json = Element::fromJSON(config);
+
+ EXPECT_NO_THROW(status = configureDhcp4Server(srv, json));
+
+ // check if returned status is OK
+ ASSERT_TRUE(status);
+ comment_ = config::parseAnswer(rcode_, status);
+ ASSERT_EQ(0, rcode_);
+
+ Pkt4Ptr dis = Pkt4Ptr(new Pkt4(DHCPDISCOVER, 1234));
+ dis->setRemoteAddr(IOAddress("192.0.2.1"));
+ dis->setCiaddr(IOAddress("192.0.2.1"));
+ dis->setIface("eth0");
+ OptionPtr clientid = generateClientId();
+ dis->addOption(clientid);
+
+ // This discover does not belong to foo class, so it will not
+ // be serviced
+ EXPECT_FALSE(srv.selectSubnet(dis));
+
+ // Let's add the packet to bar class and try again.
+ dis->addClass("bar");
+
+ // Still not supported, because it belongs to wrong class.
+ EXPECT_FALSE(srv.selectSubnet(dis));
+
+ // Let's add it to maching class.
+ dis->addClass("foo");
+
+ // This time it should work
+ EXPECT_TRUE(srv.selectSubnet(dis));
+}
+
+// This test verifies that the direct message is dropped when it has been
+// received by the server via an interface for which there is no subnet
+// configured. It also checks that the message is not dropped (is processed)
+// when it is relayed or unicast.
+TEST_F(Dhcpv4SrvTest, acceptDirectRequest) {
+ IfaceMgrTestConfig test_config(true);
+ IfaceMgr::instance().openSockets4();
+
+ NakedDhcpv4Srv srv(0);
+
+ Pkt4Ptr pkt(new Pkt4(DHCPDISCOVER, 1234));
+ // Set Giaddr and local server's unicast address, but don't set hops.
+ // Hops value must be greater than 0, when giaddr is set. Otherwise,
+ // message is considered malformed and the accept() function should
+ // return false.
+ pkt->setGiaddr(IOAddress("192.0.10.1"));
+ pkt->setLocalAddr(IOAddress("192.0.2.3"));
+ pkt->setIface("eth1");
+ EXPECT_FALSE(srv.accept(pkt));
+
+ // Let's set hops and check that the message is now accepted as
+ // a relayed message.
+ pkt->setHops(1);
+ EXPECT_TRUE(srv.accept(pkt));
+
+ // Make it a direct message but keep unicast server's address. The
+ // messages sent to unicast address should be accepted as they are
+ // most likely to renew existing leases. The server should respond
+ // to renews so they have to be accepted and processed.
+ pkt->setHops(0);
+ pkt->setGiaddr(IOAddress("0.0.0.0"));
+ EXPECT_TRUE(srv.accept(pkt));
+
+ // Direct message is now sent to a broadcast address. The server
+ // should accept this message because it has been received via
+ // eth1 for which there is a subnet configured (see test fixture
+ // class constructor).
+ pkt->setLocalAddr(IOAddress("255.255.255.255"));
+ EXPECT_TRUE(srv.accept(pkt));
+
+ // For eth0, there is no subnet configured. Such message is expected
+ // to be silently dropped.
+ pkt->setIface("eth0");
+ EXPECT_FALSE(srv.accept(pkt));
+
+ // But, if the message is unicast it should be accepted, even though
+ // it has been received via eth0.
+ pkt->setLocalAddr(IOAddress("10.0.0.1"));
+ EXPECT_TRUE(srv.accept(pkt));
+
+}
+
+// This test checks that the server rejects a message with invalid type.
+TEST_F(Dhcpv4SrvTest, acceptMessageType) {
+ IfaceMgrTestConfig test_config(true);
+ IfaceMgr::instance().openSockets4();
+
+ NakedDhcpv4Srv srv(0);
+
+ // Specify messages to be accepted by the server.
+ int allowed[] = {
+ DHCPDISCOVER,
+ DHCPREQUEST,
+ DHCPRELEASE,
+ DHCPDECLINE,
+ DHCPINFORM
+ };
+ size_t allowed_size = sizeof(allowed) / sizeof(allowed[0]);
+ // Check that the server actually accepts these message types.
+ for (int i = 0; i < allowed_size; ++i) {
+ EXPECT_TRUE(srv.acceptMessageType(Pkt4Ptr(new Pkt4(allowed[i], 1234))))
+ << "Test failed for message type " << i;
+ }
+ // Specify messages which server is supposed to drop.
+ int not_allowed[] = {
+ DHCPOFFER,
+ DHCPACK,
+ DHCPNAK,
+ DHCPLEASEQUERY,
+ DHCPLEASEUNASSIGNED,
+ DHCPLEASEUNKNOWN,
+ DHCPLEASEACTIVE,
+ DHCPBULKLEASEQUERY,
+ DHCPLEASEQUERYDONE
+ };
+ size_t not_allowed_size = sizeof(not_allowed) / sizeof(not_allowed[0]);
+ // Actually check that the server will drop these messages.
+ for (int i = 0; i < not_allowed_size; ++i) {
+ EXPECT_FALSE(srv.acceptMessageType(Pkt4Ptr(new Pkt4(not_allowed[i],
+ 1234))))
+ << "Test failed for message type " << i;
+ }
+}
+
}; // end of anonymous namespace
diff --git a/src/bin/dhcp4/tests/dhcp4_test_utils.cc b/src/bin/dhcp4/tests/dhcp4_test_utils.cc
index e983e7b..ab1afc6 100644
--- a/src/bin/dhcp4/tests/dhcp4_test_utils.cc
+++ b/src/bin/dhcp4/tests/dhcp4_test_utils.cc
@@ -1,4 +1,4 @@
-// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2013-2014 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,12 +15,15 @@
#include <config.h>
#include <asiolink/io_address.h>
+#include <cc/data.h>
#include <config/ccsession.h>
+#include <dhcp4/config_parser.h>
#include <dhcp4/tests/dhcp4_test_utils.h>
#include <dhcp/option4_addrlst.h>
#include <dhcp/option_int_array.h>
#include <dhcp/option_custom.h>
#include <dhcp/iface_mgr.h>
+#include <dhcp/tests/iface_mgr_test_config.h>
#include <dhcpsrv/cfgmgr.h>
#include <dhcpsrv/lease.h>
#include <dhcpsrv/lease_mgr.h>
@@ -28,17 +31,14 @@
using namespace std;
using namespace isc::asiolink;
-
+using namespace isc::data;
namespace isc {
namespace dhcp {
namespace test {
-/// dummy server-id file location
-static const char* SRVID_FILE = "server-id-test.txt";
-
Dhcpv4SrvTest::Dhcpv4SrvTest()
-:rcode_(-1) {
+:rcode_(-1), srv_(0) {
subnet_ = Subnet4Ptr(new Subnet4(IOAddress("192.0.2.0"), 24, 1000,
2000, 3000));
pool_ = Pool4Ptr(new Pool4(IOAddress("192.0.2.100"), IOAddress("192.0.2.110")));
@@ -52,9 +52,6 @@ Dhcpv4SrvTest::Dhcpv4SrvTest()
Option4AddrLstPtr opt_routers(new Option4AddrLst(DHO_ROUTERS));
opt_routers->setAddress(IOAddress("192.0.2.2"));
subnet_->addOption(opt_routers, false, "dhcp4");
-
- // it's ok if that fails. There should not be such a file anyway
- unlink(SRVID_FILE);
}
Dhcpv4SrvTest::~Dhcpv4SrvTest() {
@@ -387,9 +384,6 @@ void Dhcpv4SrvTest::TearDown() {
CfgMgr::instance().deleteSubnets4();
- // Let's clean up if there is such a file.
- unlink(SRVID_FILE);
-
// Close all open sockets.
IfaceMgr::instance().closeSockets();
@@ -413,58 +407,11 @@ void Dhcpv4SrvTest::TearDown() {
}
-Dhcpv4SrvFakeIfaceTest::Dhcpv4SrvFakeIfaceTest()
-: Dhcpv4SrvTest(), current_pkt_filter_() {
- // Remove current interface configuration. Instead we want to add
- // a couple of fake interfaces.
- IfaceMgr& ifacemgr = IfaceMgr::instance();
- ifacemgr.closeSockets();
- ifacemgr.clearIfaces();
-
- // Add fake interfaces.
- ifacemgr.addInterface(createIface("lo", 0, "127.0.0.1"));
- ifacemgr.addInterface(createIface("eth0", 1, "192.0.3.1"));
- ifacemgr.addInterface(createIface("eth1", 2, "10.0.0.1"));
-
- // In order to use fake interfaces we have to supply the custom
- // packet filtering class, which can mimic opening sockets on
- // fake interafaces.
- current_pkt_filter_.reset(new PktFilterTest());
- ifacemgr.setPacketFilter(current_pkt_filter_);
- ifacemgr.openSockets4();
-}
-
void
-Dhcpv4SrvFakeIfaceTest::TearDown() {
- // The base class function restores the original packet filtering class.
- Dhcpv4SrvTest::TearDown();
- // The base class however, doesn't re-detect real interfaces.
- try {
- IfaceMgr::instance().clearIfaces();
- IfaceMgr::instance().detectIfaces();
+Dhcpv4SrvTest::testDiscoverRequest(const uint8_t msg_type) {
+ IfaceMgrTestConfig test_config(true);
+ IfaceMgr::instance().openSockets4();
- } catch (const Exception& ex) {
- FAIL() << "Failed to restore interface configuration after using"
- " fake interfaces";
- }
-}
-
-Iface
-Dhcpv4SrvFakeIfaceTest::createIface(const std::string& name, const int ifindex,
- const std::string& addr) {
- Iface iface(name, ifindex);
- iface.addAddress(IOAddress(addr));
- if (name == "lo") {
- iface.flag_loopback_ = true;
- }
- iface.flag_up_ = true;
- iface.flag_running_ = true;
- iface.inactive4_ = false;
- return (iface);
-}
-
-void
-Dhcpv4SrvFakeIfaceTest::testDiscoverRequest(const uint8_t msg_type) {
// Create an instance of the tested class.
boost::scoped_ptr<NakedDhcpv4Srv> srv(new NakedDhcpv4Srv(0));
@@ -608,6 +555,20 @@ Dhcpv4SrvFakeIfaceTest::testDiscoverRequest(const uint8_t msg_type) {
EXPECT_TRUE(noBasicOptions(rsp));
}
+void
+Dhcpv4SrvTest::configure(const std::string& config) {
+ ElementPtr json = Element::fromJSON(config);
+ ConstElementPtr status;
+
+ // Configure the server and make sure the config is accepted
+ EXPECT_NO_THROW(status = configureDhcp4Server(srv_, json));
+ ASSERT_TRUE(status);
+ int rcode;
+ ConstElementPtr comment = config::parseAnswer(rcode, status);
+ ASSERT_EQ(0, rcode);
+}
+
+
}; // end of isc::dhcp::test namespace
}; // end of isc::dhcp namespace
}; // end of isc namespace
diff --git a/src/bin/dhcp4/tests/dhcp4_test_utils.h b/src/bin/dhcp4/tests/dhcp4_test_utils.h
index 8a22117..e8b0204 100644
--- a/src/bin/dhcp4/tests/dhcp4_test_utils.h
+++ b/src/bin/dhcp4/tests/dhcp4_test_utils.h
@@ -1,4 +1,4 @@
-// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2013-2014 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
@@ -87,6 +87,120 @@ public:
typedef boost::shared_ptr<PktFilterTest> PktFilterTestPtr;
+/// @brief "Naked" DHCPv4 server, exposes internal fields
+class NakedDhcpv4Srv: public Dhcpv4Srv {
+public:
+
+ /// @brief Constructor.
+ ///
+ /// This constructor disables default modes of operation used by the
+ /// Dhcpv4Srv class:
+ /// - Send/receive broadcast messages through sockets on interfaces
+ /// which support broadcast traffic.
+ /// - Direct DHCPv4 traffic - communication with clients which do not
+ /// have IP address assigned yet.
+ ///
+ /// Enabling these modes requires root privilges so they must be
+ /// disabled for unit testing.
+ ///
+ /// Note, that disabling broadcast options on sockets does not impact
+ /// the operation of these tests because they use local loopback
+ /// interface which doesn't have broadcast capability anyway. It rather
+ /// prevents setting broadcast options on other (broadcast capable)
+ /// sockets which are opened on other interfaces in Dhcpv4Srv constructor.
+ ///
+ /// The Direct DHCPv4 Traffic capability can be disabled here because
+ /// it is tested with PktFilterLPFTest unittest. The tests which belong
+ /// to PktFilterLPFTest can be enabled on demand when root privileges can
+ /// be guaranteed.
+ ///
+ /// @param port port number to listen on; the default value 0 indicates
+ /// that sockets should not be opened.
+ NakedDhcpv4Srv(uint16_t port = 0)
+ : Dhcpv4Srv(port, "type=memfile", false, false) {
+ // Create fixed server id.
+ server_id_.reset(new Option4AddrLst(DHO_DHCP_SERVER_IDENTIFIER,
+ asiolink::IOAddress("192.0.3.1")));
+ }
+
+ /// @brief Returns fixed server identifier assigned to the naked server
+ /// instance.
+ OptionPtr getServerID() const {
+ return (server_id_);
+ }
+
+ /// @brief fakes packet reception
+ /// @param timeout ignored
+ ///
+ /// The method receives all packets queued in receive queue, one after
+ /// another. Once the queue is empty, it initiates the shutdown procedure.
+ ///
+ /// See fake_received_ field for description
+ virtual Pkt4Ptr receivePacket(int /*timeout*/) {
+
+ // If there is anything prepared as fake incoming traffic, use it
+ if (!fake_received_.empty()) {
+ Pkt4Ptr pkt = fake_received_.front();
+ fake_received_.pop_front();
+ return (pkt);
+ }
+
+ // If not, just trigger shutdown and return immediately
+ shutdown();
+ return (Pkt4Ptr());
+ }
+
+ /// @brief fake packet sending
+ ///
+ /// Pretend to send a packet, but instead just store it in fake_send_ list
+ /// where test can later inspect server's response.
+ virtual void sendPacket(const Pkt4Ptr& pkt) {
+ fake_sent_.push_back(pkt);
+ }
+
+ /// @brief adds a packet to fake receive queue
+ ///
+ /// See fake_received_ field for description
+ void fakeReceive(const Pkt4Ptr& pkt) {
+ fake_received_.push_back(pkt);
+ }
+
+ virtual ~NakedDhcpv4Srv() {
+ }
+
+ /// @brief Dummy server identifier option used by various tests.
+ OptionPtr server_id_;
+
+ /// @brief packets we pretend to receive
+ ///
+ /// Instead of setting up sockets on interfaces that change between OSes, it
+ /// is much easier to fake packet reception. This is a list of packets that
+ /// we pretend to have received. You can schedule new packets to be received
+ /// using fakeReceive() and NakedDhcpv4Srv::receivePacket() methods.
+ std::list<Pkt4Ptr> fake_received_;
+
+ std::list<Pkt4Ptr> fake_sent_;
+
+ using Dhcpv4Srv::adjustIfaceData;
+ using Dhcpv4Srv::appendServerID;
+ using Dhcpv4Srv::processDiscover;
+ using Dhcpv4Srv::processRequest;
+ using Dhcpv4Srv::processRelease;
+ using Dhcpv4Srv::processDecline;
+ using Dhcpv4Srv::processInform;
+ using Dhcpv4Srv::processClientName;
+ using Dhcpv4Srv::computeDhcid;
+ using Dhcpv4Srv::createNameChangeRequests;
+ using Dhcpv4Srv::acceptServerId;
+ using Dhcpv4Srv::sanityCheck;
+ using Dhcpv4Srv::srvidToString;
+ using Dhcpv4Srv::unpackOptions;
+ using Dhcpv4Srv::classifyPacket;
+ using Dhcpv4Srv::accept;
+ using Dhcpv4Srv::acceptMessageType;
+ using Dhcpv4Srv::selectSubnet;
+};
+
class Dhcpv4SrvTest : public ::testing::Test {
public:
@@ -267,61 +381,6 @@ public:
/// @return created packet
Pkt4Ptr packetFromCapture(const std::string& hex_string);
- /// @brief This function cleans up after the test.
- virtual void TearDown();
-
- /// @brief A subnet used in most tests
- Subnet4Ptr subnet_;
-
- /// @brief A pool used in most tests
- Pool4Ptr pool_;
-
- /// @brief A client-id used in most tests
- ClientIdPtr client_id_;
-
- int rcode_;
-
- isc::data::ConstElementPtr comment_;
-
-};
-
-/// @brief Test fixture class to be used for tests which require fake
-/// interfaces.
-///
-/// The DHCPv4 server must always append the server identifier to its response.
-/// The server identifier is typically an IP address assigned to the interface
-/// on which the query has been received. The DHCPv4 server uses IfaceMgr to
-/// check this address. In order to test this functionality, a set of interfaces
-/// must be known to the test. This test fixture class creates a set of well
-/// known (fake) interfaces which can be assigned to the test DHCPv4 messages
-/// so as the response (including server identifier) can be validated.
-/// The real interfaces are removed from the IfaceMgr in the constructor and
-/// they are re-assigned in the destructor.
-class Dhcpv4SrvFakeIfaceTest : public Dhcpv4SrvTest {
-public:
- /// @brief Constructor.
- ///
- /// Creates a set of fake interfaces:
- /// - lo, index: 0, address: 127.0.0.1
- /// - eth0, index: 1, address: 192.0.3.1
- /// - eth1, index: 2, address: 10.0.0.1
- ///
- /// These interfaces replace the real interfaces detected by the IfaceMgr.
- Dhcpv4SrvFakeIfaceTest();
-
- /// @brief Restores the original interface configuration.
- virtual void TearDown();
-
- /// @brief Creates an instance of the interface.
- ///
- /// @param name Name of the interface.
- /// @param ifindex Index of the interface.
- /// @param addr IP address assigned to the interface, represented as string.
- ///
- /// @return Iface Instance of the interface.
- static Iface createIface(const std::string& name, const int ifindex,
- const std::string& addr);
-
/// @brief Tests if Discover or Request message is processed correctly
///
/// This test verifies that the Parameter Request List option is handled
@@ -335,123 +394,29 @@ public:
/// @param msg_type DHCPDISCOVER or DHCPREQUEST
void testDiscoverRequest(const uint8_t msg_type);
- /// @brief Holds a pointer to the packet filter object currently used
- /// by the IfaceMgr.
- PktFilterTestPtr current_pkt_filter_;
-
-};
-
-/// @brief "Naked" DHCPv4 server, exposes internal fields
-class NakedDhcpv4Srv: public Dhcpv4Srv {
-public:
-
- /// @brief Constructor.
- ///
- /// This constructor disables default modes of operation used by the
- /// Dhcpv4Srv class:
- /// - Send/receive broadcast messages through sockets on interfaces
- /// which support broadcast traffic.
- /// - Direct DHCPv4 traffic - communication with clients which do not
- /// have IP address assigned yet.
- ///
- /// Enabling these modes requires root privilges so they must be
- /// disabled for unit testing.
- ///
- /// Note, that disabling broadcast options on sockets does not impact
- /// the operation of these tests because they use local loopback
- /// interface which doesn't have broadcast capability anyway. It rather
- /// prevents setting broadcast options on other (broadcast capable)
- /// sockets which are opened on other interfaces in Dhcpv4Srv constructor.
- ///
- /// The Direct DHCPv4 Traffic capability can be disabled here because
- /// it is tested with PktFilterLPFTest unittest. The tests which belong
- /// to PktFilterLPFTest can be enabled on demand when root privileges can
- /// be guaranteed.
+ /// @brief Runs DHCPv4 configuration from the JSON string.
///
- /// @param port port number to listen on; the default value 0 indicates
- /// that sockets should not be opened.
- NakedDhcpv4Srv(uint16_t port = 0)
- : Dhcpv4Srv(port, "type=memfile", false, false) {
- // Create fixed server id.
- server_id_.reset(new Option4AddrLst(DHO_DHCP_SERVER_IDENTIFIER,
- asiolink::IOAddress("192.0.3.1")));
- }
-
- /// @brief Returns fixed server identifier assigned to the naked server
- /// instance.
- OptionPtr getServerID() const {
- return (server_id_);
- }
+ /// @param config String holding server configuration in JSON format.
+ void configure(const std::string& config);
- /// @brief fakes packet reception
- /// @param timeout ignored
- ///
- /// The method receives all packets queued in receive queue, one after
- /// another. Once the queue is empty, it initiates the shutdown procedure.
- ///
- /// See fake_received_ field for description
- virtual Pkt4Ptr receivePacket(int /*timeout*/) {
-
- // If there is anything prepared as fake incoming traffic, use it
- if (!fake_received_.empty()) {
- Pkt4Ptr pkt = fake_received_.front();
- fake_received_.pop_front();
- return (pkt);
- }
-
- // If not, just trigger shutdown and return immediately
- shutdown();
- return (Pkt4Ptr());
- }
-
- /// @brief fake packet sending
- ///
- /// Pretend to send a packet, but instead just store it in fake_send_ list
- /// where test can later inspect server's response.
- virtual void sendPacket(const Pkt4Ptr& pkt) {
- fake_sent_.push_back(pkt);
- }
+ /// @brief This function cleans up after the test.
+ virtual void TearDown();
- /// @brief adds a packet to fake receive queue
- ///
- /// See fake_received_ field for description
- void fakeReceive(const Pkt4Ptr& pkt) {
- pkt->setIface("eth0");
- fake_received_.push_back(pkt);
- }
+ /// @brief A subnet used in most tests
+ Subnet4Ptr subnet_;
- virtual ~NakedDhcpv4Srv() {
- }
+ /// @brief A pool used in most tests
+ Pool4Ptr pool_;
- /// @brief Dummy server identifier option used by various tests.
- OptionPtr server_id_;
+ /// @brief A client-id used in most tests
+ ClientIdPtr client_id_;
- /// @brief packets we pretend to receive
- ///
- /// Instead of setting up sockets on interfaces that change between OSes, it
- /// is much easier to fake packet reception. This is a list of packets that
- /// we pretend to have received. You can schedule new packets to be received
- /// using fakeReceive() and NakedDhcpv4Srv::receivePacket() methods.
- std::list<Pkt4Ptr> fake_received_;
+ int rcode_;
- std::list<Pkt4Ptr> fake_sent_;
+ isc::data::ConstElementPtr comment_;
- using Dhcpv4Srv::adjustIfaceData;
- using Dhcpv4Srv::appendServerID;
- using Dhcpv4Srv::processDiscover;
- using Dhcpv4Srv::processRequest;
- using Dhcpv4Srv::processRelease;
- using Dhcpv4Srv::processDecline;
- using Dhcpv4Srv::processInform;
- using Dhcpv4Srv::processClientName;
- using Dhcpv4Srv::computeDhcid;
- using Dhcpv4Srv::createNameChangeRequests;
- using Dhcpv4Srv::acceptServerId;
- using Dhcpv4Srv::sanityCheck;
- using Dhcpv4Srv::srvidToString;
- using Dhcpv4Srv::unpackOptions;
- using Dhcpv4Srv::name_change_reqs_;
- using Dhcpv4Srv::classifyPacket;
+ /// @brief Server object under test.
+ NakedDhcpv4Srv srv_;
};
}; // end of isc::dhcp::test namespace
diff --git a/src/bin/dhcp4/tests/direct_client_unittest.cc b/src/bin/dhcp4/tests/direct_client_unittest.cc
new file mode 100644
index 0000000..aacfdc7
--- /dev/null
+++ b/src/bin/dhcp4/tests/direct_client_unittest.cc
@@ -0,0 +1,413 @@
+// Copyright (C) 2014 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 <dhcp/iface_mgr.h>
+#include <dhcp/pkt4.h>
+#include <dhcp/classify.h>
+#include <dhcp/tests/iface_mgr_test_config.h>
+#include <dhcpsrv/cfgmgr.h>
+#include <dhcpsrv/lease_mgr_factory.h>
+#include <dhcpsrv/subnet.h>
+#include <dhcp4/config_parser.h>
+#include <dhcp4/tests/dhcp4_test_utils.h>
+#include <gtest/gtest.h>
+#include <string>
+
+using namespace isc;
+using namespace isc::asiolink;
+using namespace isc::data;
+using namespace isc::dhcp;
+using namespace isc::dhcp::test;
+
+namespace {
+
+/// @brief Test fixture class for testing message processing from directly
+/// connected clients.
+///
+/// This class provides mechanisms for testing processing of DHCPv4 messages
+/// from directly connected clients.
+class DirectClientTest : public Dhcpv4SrvTest {
+public:
+ /// @brief Constructor.
+ ///
+ /// Initializes DHCPv4 server object used by various tests.
+ DirectClientTest();
+
+ /// @brief Configures the server with one subnet.
+ ///
+ /// This creates new configuration for the DHCPv4 with one subnet having
+ /// a specified prefix.
+ ///
+ /// The subnet parameters (such as options, timers etc.) are aribitrarily
+ /// selected. The subnet and pool mask is always /24. The real configuration
+ /// would exclude .0 (network address) and .255 (broadcast address), but we
+ /// ignore that fact for the sake of test simplicity.
+ ///
+ /// @param prefix Prefix for a subnet.
+ void configureSubnet(const std::string& prefix);
+
+ /// @brief Configures the server with two subnets.
+ ///
+ /// This function configures DHCPv4 server with two different subnets.
+ /// The subnet parameters (such as options, timers etc.) are aribitrarily
+ /// selected. The subnet and pool mask is /24. The real configuration
+ /// would exclude .0 (network address) and .255 (broadcast address), but we
+ /// ignore that fact for the sake of test simplicity.
+ ///
+ /// @param prefix1 Prefix of the first subnet to be configured.
+ /// @param prefix2 Prefix of the second subnet to be configured.
+ void configureTwoSubnets(const std::string& prefix1,
+ const std::string& prefix2);
+
+ /// @brief Creates simple message from a client.
+ ///
+ /// This function creates a DHCPv4 message having a specified type
+ /// (e.g. Discover, Request) and sets some properties of this
+ /// message: client identifier, address and interface. The copy of
+ /// this message is then created by parsing wire data of the original
+ /// message. This simulates the case when the message is received and
+ /// parsed by the server.
+ ///
+ /// @param msg_type Type of the message to be created.
+ /// @param iface Name of the interface on which the message has been
+ /// "received" by the server.
+ ///
+ /// @return Generated message.
+ Pkt4Ptr createClientMessage(const uint16_t msg_type,
+ const std::string& iface);
+
+ /// @brief Creates simple message from a client.
+ ///
+ /// This function configures a client's message by adding client identifier,
+ /// setting interface and addresses. The copy of this message is then
+ /// created by parsing wire data of the original message. This simulates the
+ /// case when the message is received and parsed by the server.
+ ///
+ /// @param msg Caller supplied message to be configured. This object must
+ /// not be NULL.
+ /// @param iface Name of the interface on which the message has been
+ /// "received" by the server.
+ ///
+ /// @return Configured and parsed message.
+ Pkt4Ptr createClientMessage(const Pkt4Ptr &msg, const std::string& iface);
+
+ /// @brief classes the client belongs to
+ ///
+ /// This is empty in most cases, but it is needed as a parameter for all
+ /// getSubnet4() calls.
+ ClientClasses classify_;
+};
+
+DirectClientTest::DirectClientTest() : Dhcpv4SrvTest() {
+}
+
+void
+DirectClientTest::configureSubnet(const std::string& prefix) {
+ std::ostringstream config;
+ config << "{ \"interfaces\": [ \"*\" ],"
+ "\"rebind-timer\": 2000, "
+ "\"renew-timer\": 1000, "
+ "\"option-data\": [ ],"
+ "\"subnet4\": [ { "
+ " \"pool\": [ \"" << prefix << "/24\" ],"
+ " \"subnet\": \"" << prefix << "/24\", "
+ " \"rebind-timer\": 2000, "
+ " \"renew-timer\": 1000, "
+ " \"valid-lifetime\": 4000"
+ "} ],"
+ "\"valid-lifetime\": 4000 }";
+
+ configure(config.str());
+
+}
+
+void
+DirectClientTest::configureTwoSubnets(const std::string& prefix1,
+ const std::string& prefix2) {
+ std::ostringstream config;
+ config << "{ \"interfaces\": [ \"*\" ],"
+ "\"rebind-timer\": 2000, "
+ "\"renew-timer\": 1000, "
+ "\"option-data\": [ ],"
+ "\"subnet4\": [ { "
+ " \"pool\": [ \"" << prefix1 << "/24\" ],"
+ " \"subnet\": \"" << prefix1 << "/24\", "
+ " \"rebind-timer\": 2000, "
+ " \"renew-timer\": 1000, "
+ " \"valid-lifetime\": 4000"
+ " },"
+ "{ "
+ " \"pool\": [ \"" << prefix2 << "/24\" ],"
+ " \"subnet\": \"" << prefix2 << "/24\", "
+ " \"rebind-timer\": 2000, "
+ " \"renew-timer\": 1000, "
+ " \"valid-lifetime\": 4000"
+ "} ],"
+ "\"valid-lifetime\": 4000 }";
+
+ configure(config.str());
+}
+
+Pkt4Ptr
+DirectClientTest:: createClientMessage(const uint16_t msg_type,
+ const std::string& iface) {
+ // Create a source packet.
+ Pkt4Ptr msg = Pkt4Ptr(new Pkt4(msg_type, 1234));
+ return (createClientMessage(msg, iface));
+
+}
+
+Pkt4Ptr
+DirectClientTest::createClientMessage(const Pkt4Ptr& msg,
+ const std::string& iface) {
+ msg->setRemoteAddr(IOAddress("255.255.255.255"));
+ msg->addOption(generateClientId());
+ msg->setIface(iface);
+
+ // Create copy of this packet by parsing its wire data. Make sure that the
+ // local and remote address are set like it was a message sent from the
+ // directly connected client.
+ Pkt4Ptr received;
+ createPacketFromBuffer(msg, received);
+ received->setIface(iface);
+ received->setLocalAddr(IOAddress("255.255.255.255"));
+ received->setRemoteAddr(IOAddress("0.0.0.0"));
+
+ return (received);
+}
+
+// This test checks that the message from directly connected client
+// is processed and that client is offered IPv4 address from the subnet which
+// is suitable for the local interface on which the client's message is
+// received. This test uses two subnets, with two active interfaces which IP
+// addresses belong to these subnets. The address offered to the client
+// which message has been sent over eth0 should belong to a different
+// subnet than the address offered for the client sending its message
+// via eth1.
+TEST_F(DirectClientTest, twoSubnets) {
+ // Configure IfaceMgr with fake interfaces lo, eth0 and eth1.
+ IfaceMgrTestConfig iface_config(true);
+ // After creating interfaces we have to open sockets as it is required
+ // by the message processing code.
+ ASSERT_NO_THROW(IfaceMgr::instance().openSockets4());
+ // Add two subnets: address on eth0 belongs to the second subnet,
+ // address on eth1 belongs to the first subnet.
+ ASSERT_NO_FATAL_FAILURE(configureTwoSubnets("192.0.2.0", "10.0.0.0"));
+ // Create Discover and simulate reception of this message through eth0.
+ Pkt4Ptr dis = createClientMessage(DHCPDISCOVER, "eth0");
+ srv_.fakeReceive(dis);
+ // Create Request and simulate reception of this message through eth1.
+ Pkt4Ptr req = createClientMessage(DHCPREQUEST, "eth1");
+ srv_.fakeReceive(req);
+
+ // Process clients' messages.
+ srv_.run();
+
+ // Check that the server did send reposonses.
+ ASSERT_EQ(2, srv_.fake_sent_.size());
+
+ // Make sure that we received a response.
+ Pkt4Ptr response = srv_.fake_sent_.front();
+ ASSERT_TRUE(response);
+ srv_.fake_sent_.pop_front();
+
+ // Client should get an Offer (not a NAK).
+ ASSERT_EQ(DHCPOFFER, response->getType());
+ // Check that the offered address belongs to the suitable subnet.
+ Subnet4Ptr subnet = CfgMgr::instance().getSubnet4(response->getYiaddr(),
+ classify_);
+ ASSERT_TRUE(subnet);
+ EXPECT_EQ("10.0.0.0", subnet->get().first.toText());
+
+ // A client that sent Request over the other interface should get Ack.
+ response = srv_.fake_sent_.front();
+ ASSERT_TRUE(response);
+
+ // Client should get an Ack (not a NAK).
+ ASSERT_EQ(DHCPACK, response->getType());
+ // Check that the offered address belongs to the suitable subnet.
+ subnet = CfgMgr::instance().getSubnet4(response->getYiaddr(), classify_);
+ ASSERT_TRUE(subnet);
+ EXPECT_EQ("192.0.2.0", subnet->get().first.toText());
+
+}
+
+// This test checks that server selects a subnet when receives a message
+// through an interface for which the subnet has been configured. This
+// interface has IPv4 address assigned which belongs to this subnet.
+// This test also verifies that when the message is received through
+// the interface for which there is no suitable subnet, the message
+// is discarded.
+TEST_F(DirectClientTest, oneSubnet) {
+ // Configure IfaceMgr with fake interfaces lo, eth0 and eth1.
+ IfaceMgrTestConfig iface_config(true);
+ // After creating interfaces we have to open sockets as it is required
+ // by the message processing code.
+ ASSERT_NO_THROW(IfaceMgr::instance().openSockets4());
+ // Add a subnet which will be selected when a message from directly
+ // connected client is received through interface eth0.
+ ASSERT_NO_FATAL_FAILURE(configureSubnet("10.0.0.0"));
+ // Create Discover and simulate reception of this message through eth0.
+ Pkt4Ptr dis = createClientMessage(DHCPDISCOVER, "eth0");
+ srv_.fakeReceive(dis);
+ // Create Request and simulate reception of this message through eth1.
+ Pkt4Ptr req = createClientMessage(DHCPDISCOVER, "eth1");
+ srv_.fakeReceive(req);
+
+ // Process clients' messages.
+ srv_.run();
+
+ // Check that the server sent one response for the message received
+ // through eth0. The other client's message should be dicarded.
+ ASSERT_EQ(1, srv_.fake_sent_.size());
+
+ // Check the response. The first Discover was sent via eth0 for which
+ // the subnet has been configured.
+ Pkt4Ptr response = srv_.fake_sent_.front();
+ ASSERT_TRUE(response);
+ srv_.fake_sent_.pop_front();
+
+ // Since Discover has been received through the interface for which
+ // the subnet has been configured, the server should respond with
+ // an Offer message.
+ ASSERT_EQ(DHCPOFFER, response->getType());
+ // Check that the offered address belongs to the suitable subnet.
+ Subnet4Ptr subnet = CfgMgr::instance().getSubnet4(response->getYiaddr(),
+ classify_);
+ ASSERT_TRUE(subnet);
+ EXPECT_EQ("10.0.0.0", subnet->get().first.toText());
+
+}
+
+// This test verifies that the server uses ciaddr to select a subnet for a
+// client which renews its lease.
+TEST_F(DirectClientTest, renew) {
+ // Configure IfaceMgr with fake interfaces lo, eth0 and eth1.
+ IfaceMgrTestConfig iface_config(true);
+ // After creating interfaces we have to open sockets as it is required
+ // by the message processing code.
+ ASSERT_NO_THROW(IfaceMgr::instance().openSockets4());
+ // Add a subnet.
+ ASSERT_NO_FATAL_FAILURE(configureSubnet("10.0.0.0"));
+ // Make sure that the subnet has been really added. Also, the subnet
+ // will be needed to create a lease for a client.
+ Subnet4Ptr subnet = CfgMgr::instance().getSubnet4(IOAddress("10.0.0.10"),
+ classify_);
+ // Create a lease for a client that we will later renewed. By explicitly
+ // creating a lease we will get to know the lease parameters, such as
+ // leased address etc.
+ const uint8_t hwaddr[] = { 1, 2, 3, 4, 5, 6 };
+ Lease4Ptr lease(new Lease4(IOAddress("10.0.0.10"), hwaddr, sizeof(hwaddr),
+ &generateClientId()->getData()[0],
+ generateClientId()->getData().size(),
+ 100, 50, 75, time(NULL),
+ subnet->getID()));
+ LeaseMgrFactory::instance().addLease(lease);
+
+ // Create a Request to renew client's lease. The renew request is unicast
+ // through eth1. Note, that in case of renewal the client unicasts its
+ // Request and sets the ciaddr. The server is supposed to use ciaddr to
+ // pick the subnet for the client. In order to make sure that the server
+ // uses ciaddr, we simulate reception of the packet through eth1, for which
+ // there is no subnet for directly connected clients.
+ Pkt4Ptr req = Pkt4Ptr(new Pkt4(DHCPREQUEST, 1234));
+ req->setCiaddr(IOAddress("10.0.0.10"));
+ req = createClientMessage(req, "eth1");
+ req->setLocalAddr(IOAddress("10.0.0.1"));
+ req->setRemoteAddr(req->getCiaddr());
+
+ srv_.fakeReceive(req);
+
+ // Process clients' messages.
+ srv_.run();
+
+ // Check that the server did send reposonse.
+ ASSERT_EQ(1, srv_.fake_sent_.size());
+ Pkt4Ptr response = srv_.fake_sent_.front();
+ ASSERT_TRUE(response);
+
+ ASSERT_EQ(DHCPACK, response->getType());
+ // Check that the offered address belongs to the suitable subnet.
+ subnet = CfgMgr::instance().getSubnet4(response->getYiaddr(), classify_);
+ ASSERT_TRUE(subnet);
+ EXPECT_EQ("10.0.0.0", subnet->get().first.toText());
+
+}
+
+// This test verifies that when a client in the Rebinding state broadcasts
+// a Request message through an interface for which a subnet is configured,
+// the server responds to this Request. It also verifies that when such a
+// Request is sent through the interface for which there is no subnet configured
+// the client's message is discarded.
+TEST_F(DirectClientTest, rebind) {
+ // Configure IfaceMgr with fake interfaces lo, eth0 and eth1.
+ IfaceMgrTestConfig iface_config(true);
+ // After creating interfaces we have to open sockets as it is required
+ // by the message processing code.
+ ASSERT_NO_THROW(IfaceMgr::instance().openSockets4());
+ // Add a subnet.
+ ASSERT_NO_FATAL_FAILURE(configureSubnet("10.0.0.0"));
+ // Make sure that the subnet has been really added. Also, the subnet
+ // will be needed to create a lease for a client.
+ Subnet4Ptr subnet = CfgMgr::instance().getSubnet4(IOAddress("10.0.0.10"),
+ classify_);
+ // Create a lease, which will be later renewed. By explicitly creating a
+ // lease we will know the lease parameters, such as leased address etc.
+ const uint8_t hwaddr[] = { 1, 2, 3, 4, 5, 6 };
+ Lease4Ptr lease(new Lease4(IOAddress("10.0.0.10"), hwaddr, sizeof(hwaddr),
+ &generateClientId()->getData()[0],
+ generateClientId()->getData().size(),
+ 100, 50, 75, time(NULL),
+ subnet->getID()));
+ LeaseMgrFactory::instance().addLease(lease);
+
+ // Broadcast Request through an interface for which there is no subnet
+ // configured. This messag should be discarded by the server.
+ Pkt4Ptr req = Pkt4Ptr(new Pkt4(DHCPREQUEST, 1234));
+ req->setCiaddr(IOAddress("10.0.0.10"));
+ req = createClientMessage(req, "eth1");
+ req->setRemoteAddr(req->getCiaddr());
+
+ srv_.fakeReceive(req);
+
+ // Broadcast another Request through an interface for which there is
+ // a subnet configured. The server should generate a response.
+ req = Pkt4Ptr(new Pkt4(DHCPREQUEST, 5678));
+ req->setCiaddr(IOAddress("10.0.0.10"));
+ req = createClientMessage(req, "eth0");
+ req->setRemoteAddr(req->getCiaddr());
+
+ srv_.fakeReceive(req);
+
+ // Process clients' messages.
+ srv_.run();
+
+ // Check that the server did send exactly one reposonse.
+ ASSERT_EQ(1, srv_.fake_sent_.size());
+ Pkt4Ptr response = srv_.fake_sent_.front();
+ ASSERT_TRUE(response);
+
+ // Make sure that the server responsed with ACK, not NAK.
+ ASSERT_EQ(DHCPACK, response->getType());
+ // Make sure that the response is generated for the second Request
+ // (transmitted over eth0).
+ EXPECT_EQ(5678, response->getTransid());
+ // Check that the offered address belongs to the suitable subnet.
+ subnet = CfgMgr::instance().getSubnet4(response->getYiaddr(), classify_);
+ ASSERT_TRUE(subnet);
+ EXPECT_EQ("10.0.0.0", subnet->get().first.toText());
+
+}
+
+}
diff --git a/src/bin/dhcp4/tests/fqdn_unittest.cc b/src/bin/dhcp4/tests/fqdn_unittest.cc
index d3bf9ae..4215e92 100644
--- a/src/bin/dhcp4/tests/fqdn_unittest.cc
+++ b/src/bin/dhcp4/tests/fqdn_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2013-2014 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
@@ -16,8 +16,10 @@
#include <asiolink/io_address.h>
#include <dhcp/option4_client_fqdn.h>
#include <dhcp/option_int_array.h>
+#include <dhcp/tests/iface_mgr_test_config.h>
#include <dhcp4/tests/dhcp4_test_utils.h>
#include <dhcp_ddns/ncr_msg.h>
+#include <dhcpsrv/cfgmgr.h>
#include <gtest/gtest.h>
#include <boost/scoped_ptr.hpp>
@@ -30,13 +32,59 @@ using namespace isc::dhcp_ddns;
namespace {
-class NameDhcpv4SrvTest : public Dhcpv4SrvFakeIfaceTest {
+class NameDhcpv4SrvTest : public Dhcpv4SrvTest {
public:
- NameDhcpv4SrvTest() : Dhcpv4SrvFakeIfaceTest() {
+ // Reference to D2ClientMgr singleton
+ D2ClientMgr& d2_mgr_;
+
+ // Bit Constants for turning on and off DDNS configuration options.
+ static const uint16_t ALWAYS_INCLUDE_FQDN = 1;
+ static const uint16_t OVERRIDE_NO_UPDATE = 2;
+ static const uint16_t OVERRIDE_CLIENT_UPDATE = 4;
+ static const uint16_t REPLACE_CLIENT_NAME = 8;
+
+ NameDhcpv4SrvTest() : Dhcpv4SrvTest(),
+ d2_mgr_(CfgMgr::instance().getD2ClientMgr()) {
srv_ = new NakedDhcpv4Srv(0);
+ // Config DDNS to be enabled, all controls off
+ enableD2();
}
+
virtual ~NameDhcpv4SrvTest() {
delete srv_;
+ // CfgMgr singleton doesn't get wiped between tests, so we'll
+ // disable D2 explictly between tests.
+ disableD2();
+ }
+
+ /// @brief Sets the server's DDNS configuration to ddns updates disabled.
+ void disableD2() {
+ // Default constructor creates a config with DHCP-DDNS updates
+ // disabled.
+ D2ClientConfigPtr cfg(new D2ClientConfig());
+ CfgMgr::instance().setD2ClientConfig(cfg);
+ }
+
+ /// @brief Enables DHCP-DDNS updates with the given options enabled.
+ ///
+ /// Replaces the current D2ClientConfiguration with a configuration
+ /// which as updates enabled and the control options set based upon
+ /// the bit mask of options.
+ ///
+ /// @param mask Bit mask of configuration options that should be enabled.
+ void enableD2(const uint16_t mask = 0) {
+ D2ClientConfigPtr cfg;
+
+ ASSERT_NO_THROW(cfg.reset(new D2ClientConfig(true,
+ isc::asiolink::IOAddress("127.0.0.1"), 53001,
+ dhcp_ddns::NCR_UDP, dhcp_ddns::FMT_JSON,
+ (mask & ALWAYS_INCLUDE_FQDN),
+ (mask & OVERRIDE_NO_UPDATE),
+ (mask & OVERRIDE_CLIENT_UPDATE),
+ (mask & REPLACE_CLIENT_NAME),
+ "myhost", "example.com")));
+ ASSERT_NO_THROW(CfgMgr::instance().setD2ClientConfig(cfg));
+ ASSERT_NO_THROW(srv_->startD2());
}
// Create a lease to be used by various tests.
@@ -78,15 +126,18 @@ public:
return (opt_hostname);
}
- // Generates partial hostname from the address. The format of the
- // generated address is: host-A-B-C-D, where A.B.C.D is an IP
- // address.
+ /// @brief Convenience method for generating an FQDN from an IP address.
+ ///
+ /// This is just a wrapper method around the D2ClientMgr's method for
+ /// generating domain names from the configured prefix, suffix, and a
+ /// given IP address. This is useful for verifying that fully generated
+ /// names are correct.
+ ///
+ /// @param addr IP address used in the lease.
+ ///
+ /// @return An std::string contained the generated FQDN.
std::string generatedNameFromAddress(const IOAddress& addr) {
- std::string gen_name = addr.toText();
- std::replace(gen_name.begin(), gen_name.end(), '.', '-');
- std::ostringstream hostname;
- hostname << "host-" << gen_name;
- return (hostname.str());
+ return(CfgMgr::instance().getD2ClientMgr().generateFqdn(addr));
}
// Get the Client FQDN Option from the given message.
@@ -110,7 +161,7 @@ public:
const bool include_clientid = true) {
Pkt4Ptr pkt = Pkt4Ptr(new Pkt4(msg_type, 1234));
pkt->setRemoteAddr(IOAddress("192.0.2.3"));
- pkt->setIface("eth0");
+ pkt->setIface("eth1");
// For DISCOVER we don't include server id, because client broadcasts
// the message to all servers.
if (msg_type != DHCPDISCOVER) {
@@ -182,6 +233,21 @@ public:
Option4ClientFqdnPtr fqdn = getClientFqdnOption(answer);
ASSERT_TRUE(fqdn);
+ checkFqdnFlags(answer, exp_flags);
+
+ EXPECT_EQ(exp_domain_name, fqdn->getDomainName());
+ EXPECT_EQ(exp_domain_type, fqdn->getDomainNameType());
+
+ }
+
+ /// @brief Checks the packet's FQDN option flags against a given mask
+ ///
+ /// @param pkt IPv4 packet whose FQDN flags should be checked.
+ /// @param exp_flags Bit mask of flags that are expected to be true.
+ void checkFqdnFlags(const Pkt4Ptr& pkt, const uint8_t exp_flags) {
+ Option4ClientFqdnPtr fqdn = getClientFqdnOption(pkt);
+ ASSERT_TRUE(fqdn);
+
const bool flag_n = (exp_flags & Option4ClientFqdn::FLAG_N) != 0;
const bool flag_s = (exp_flags & Option4ClientFqdn::FLAG_S) != 0;
const bool flag_o = (exp_flags & Option4ClientFqdn::FLAG_O) != 0;
@@ -191,12 +257,9 @@ public:
EXPECT_EQ(flag_s, fqdn->getFlag(Option4ClientFqdn::FLAG_S));
EXPECT_EQ(flag_o, fqdn->getFlag(Option4ClientFqdn::FLAG_O));
EXPECT_EQ(flag_e, fqdn->getFlag(Option4ClientFqdn::FLAG_E));
-
- EXPECT_EQ(exp_domain_name, fqdn->getDomainName());
- EXPECT_EQ(exp_domain_type, fqdn->getDomainNameType());
-
}
+
// Processes the Hostname option in the client's message and returns
// the hostname option which would be sent to the client. It will
// throw NULL pointer if the hostname option is not to be included
@@ -221,8 +284,8 @@ public:
}
- // Test that the client message holding an FQDN is processed and the
- // NameChangeRequests are generated.
+ // Test that the client message holding an FQDN is processed and
+ // that the response packet is as expected.
void testProcessMessageWithFqdn(const uint8_t msg_type,
const std::string& hostname) {
Pkt4Ptr req = generatePktWithFqdn(msg_type, Option4ClientFqdn::FLAG_S |
@@ -261,16 +324,19 @@ public:
const time_t cltt,
const uint16_t len,
const bool not_strict_expire_check = false) {
- NameChangeRequest ncr = srv_->name_change_reqs_.front();
- EXPECT_EQ(type, ncr.getChangeType());
- EXPECT_EQ(forward, ncr.isForwardChange());
- EXPECT_EQ(reverse, ncr.isReverseChange());
- EXPECT_EQ(addr, ncr.getIpAddress());
- EXPECT_EQ(fqdn, ncr.getFqdn());
+ NameChangeRequestPtr ncr;
+ ASSERT_NO_THROW(ncr = d2_mgr_.peekAt(0));
+ ASSERT_TRUE(ncr);
+
+ EXPECT_EQ(type, ncr->getChangeType());
+ EXPECT_EQ(forward, ncr->isForwardChange());
+ EXPECT_EQ(reverse, ncr->isReverseChange());
+ EXPECT_EQ(addr, ncr->getIpAddress());
+ EXPECT_EQ(fqdn, ncr->getFqdn());
// Compare dhcid if it is not empty. In some cases, the DHCID is
// not known in advance and can't be compared.
if (!dhcid.empty()) {
- EXPECT_EQ(dhcid, ncr.getDhcid().toStr());
+ EXPECT_EQ(dhcid, ncr->getDhcid().toStr());
}
// In some cases, the test doesn't have access to the last transmission
// time for the particular client. In such cases, the test can use the
@@ -278,15 +344,68 @@ public:
// for equality but rather check that the lease expiration time is not
// greater than the current time + lease lifetime.
if (not_strict_expire_check) {
- EXPECT_GE(cltt + len, ncr.getLeaseExpiresOn());
+ EXPECT_GE(cltt + len, ncr->getLeaseExpiresOn());
} else {
- EXPECT_EQ(cltt + len, ncr.getLeaseExpiresOn());
+ EXPECT_EQ(cltt + len, ncr->getLeaseExpiresOn());
}
- EXPECT_EQ(len, ncr.getLeaseLength());
- EXPECT_EQ(isc::dhcp_ddns::ST_NEW, ncr.getStatus());
- srv_->name_change_reqs_.pop();
+ EXPECT_EQ(len, ncr->getLeaseLength());
+ EXPECT_EQ(isc::dhcp_ddns::ST_NEW, ncr->getStatus());
+
+ // Process the message off the queue
+ ASSERT_NO_THROW(d2_mgr_.runReadyIO());
}
+
+ /// @brief Tests processing a request with the given client flags
+ ///
+ /// This method creates a request with its FQDN flags set to the given
+ /// value and submits it to the server for processing. It then checks
+ /// the following:
+ /// 1. Did the server generate an ACK with the correct FQDN flags
+ /// 2. If the server should have generated an NCR, did it? and If
+ /// so was it correct?
+ ///
+ /// @param client_flags Mask of client FQDN flags which are true
+ /// @param response_flags Mask of expected FQDN flags in the response
+ void flagVsConfigScenario(const uint8_t client_flags,
+ const uint8_t response_flags) {
+ // Create fake interfaces and open fake sockets.
+ IfaceMgrTestConfig iface_config(true);
+ IfaceMgr::instance().openSockets4();
+
+ Pkt4Ptr req = generatePktWithFqdn(DHCPREQUEST, client_flags,
+ "myhost.example.com.",
+ Option4ClientFqdn::FULL, true);
+
+ // Process the request.
+ Pkt4Ptr reply;
+ ASSERT_NO_THROW(reply = srv_->processRequest(req));
+
+ // Verify the response and flags.
+ checkResponse(reply, DHCPACK, 1234);
+ checkFqdnFlags(reply, response_flags);
+
+ // NCRs cannot be sent to the d2_mgr unless updates are enabled.
+ if (d2_mgr_.ddnsEnabled()) {
+ // There should be an NCR only if response S flag is 1.
+ /// @todo This logic will need to change if forward and reverse
+ /// updates are ever controlled independently.
+ if ((response_flags & Option4ClientFqdn::FLAG_S) == 0) {
+ ASSERT_EQ(0, d2_mgr_.getQueueSize());
+ } else {
+ // Verify that there is one NameChangeRequest as expected.
+ ASSERT_EQ(1, d2_mgr_.getQueueSize());
+ verifyNameChangeRequest(isc::dhcp_ddns::CHG_ADD, true, true,
+ reply->getYiaddr().toText(),
+ "myhost.example.com.",
+ "", // empty DHCID means don't check it
+ time(NULL) + subnet_->getValid(),
+ subnet_->getValid(), true);
+ }
+ }
+ }
+
+
NakedDhcpv4Srv* srv_;
};
@@ -348,21 +467,101 @@ TEST_F(NameDhcpv4SrvTest, dhcidComputeFromHWAddr) {
EXPECT_EQ(dhcid_ref, dhcid.toStr());
}
+// Tests the following scenario:
+// - Updates are enabled
+// - All overrides are off
+// - Client requests forward update (N = 0, S = 1)
+//
+// Server should perform the update:
+// - Reponse flags should N = 0, S = 1, O = 0
+// - Should queue an NCR
+TEST_F(NameDhcpv4SrvTest, updatesEnabled) {
+ flagVsConfigScenario((Option4ClientFqdn::FLAG_E |
+ Option4ClientFqdn::FLAG_S),
+ (Option4ClientFqdn::FLAG_E |
+ Option4ClientFqdn::FLAG_S));
+}
-// Test that server confirms to perform the forward and reverse DNS update,
-// when client asks for it.
-TEST_F(NameDhcpv4SrvTest, serverUpdateForwardFqdn) {
- Pkt4Ptr query = generatePktWithFqdn(DHCPREQUEST,
- Option4ClientFqdn::FLAG_E |
- Option4ClientFqdn::FLAG_S,
- "myhost.example.com.",
- Option4ClientFqdn::FULL,
- true);
+// Tests the following scenario
+// - Updates are disabled
+// - Client requests forward update (N = 0, S = 1)
+//
+// Server should NOT perform updates:
+// - Response flags should N = 1, S = 0, O = 1
+// - Should not queue any NCRs
+TEST_F(NameDhcpv4SrvTest, updatesDisabled) {
+ disableD2();
+ flagVsConfigScenario((Option4ClientFqdn::FLAG_E |
+ Option4ClientFqdn::FLAG_S),
+ (Option4ClientFqdn::FLAG_E |
+ Option4ClientFqdn::FLAG_N |
+ Option4ClientFqdn::FLAG_O));
+}
- testProcessFqdn(query,
- Option4ClientFqdn::FLAG_E | Option4ClientFqdn::FLAG_S,
- "myhost.example.com.");
+// Tests the following scenario:
+// - Updates are enabled
+// - All overrides are off.
+// - Client requests no updates (N = 1, S = 0)
+//
+// Server should NOT perform updates:
+// - Response flags should N = 1, S = 0, O = 0
+// - Should not queue any NCRs
+TEST_F(NameDhcpv4SrvTest, respectNoUpdate) {
+ flagVsConfigScenario((Option4ClientFqdn::FLAG_E |
+ Option4ClientFqdn::FLAG_N),
+ (Option4ClientFqdn::FLAG_E |
+ Option4ClientFqdn::FLAG_N));
+}
+
+// Tests the following scenario:
+// - Updates are enabled
+// - override-no-update is on
+// - Client requests no updates (N = 1, S = 0)
+//
+// Server should override "no update" request and perform updates:
+// - Response flags should be N = 0, S = 1, O = 1
+// - Should queue an NCR
+TEST_F(NameDhcpv4SrvTest, overrideNoUpdate) {
+ enableD2(OVERRIDE_NO_UPDATE);
+ flagVsConfigScenario((Option4ClientFqdn::FLAG_E |
+ Option4ClientFqdn::FLAG_N),
+ (Option4ClientFqdn::FLAG_E |
+ Option4ClientFqdn::FLAG_S |
+ Option4ClientFqdn::FLAG_O));
+}
+
+// Tests the following scenario:
+// - Updates are enabled
+// - All overrides are off.
+// - Client requests delegation (N = 0, S = 0)
+//
+// Server should respect client's delegation request and NOT do updates:
+// - Response flags should be N = 1, S = 0, O = 0
+// - Should not queue any NCRs
+TEST_F(NameDhcpv4SrvTest, respectClientDelegation) {
+
+ flagVsConfigScenario(Option4ClientFqdn::FLAG_E,
+ (Option4ClientFqdn::FLAG_E |
+ Option4ClientFqdn::FLAG_N));
+}
+
+// Tests the following scenario:
+// - Updates are enabled
+// - override-client-update is on.
+// - Client requests delegation (N = 0, S = 0)
+//
+// Server should override client's delegation request and do updates:
+// - Response flags should be N = 0, S = 1, O = 1
+// - Should queue an NCR
+TEST_F(NameDhcpv4SrvTest, overrideClientDelegation) {
+ // Turn on override-client-update.
+ enableD2(OVERRIDE_CLIENT_UPDATE);
+
+ flagVsConfigScenario(Option4ClientFqdn::FLAG_E,
+ (Option4ClientFqdn::FLAG_E |
+ Option4ClientFqdn::FLAG_S |
+ Option4ClientFqdn::FLAG_O));
}
// Test that server processes the Hostname option sent by a client and
@@ -447,34 +646,6 @@ TEST_F(NameDhcpv4SrvTest, serverUpdateForwardNoNameFqdn) {
}
-// Test server's response when client requests no DNS update.
-TEST_F(NameDhcpv4SrvTest, noUpdateFqdn) {
- Pkt4Ptr query = generatePktWithFqdn(DHCPREQUEST,
- Option4ClientFqdn::FLAG_E |
- Option4ClientFqdn::FLAG_N,
- "myhost.example.com.",
- Option4ClientFqdn::FULL,
- true);
- testProcessFqdn(query, Option4ClientFqdn::FLAG_E |
- Option4ClientFqdn::FLAG_N,
- "myhost.example.com.");
-}
-
-// Test that server does not accept delegation of the forward DNS update
-// to a client.
-TEST_F(NameDhcpv4SrvTest, clientUpdateNotAllowedFqdn) {
- Pkt4Ptr query = generatePktWithFqdn(DHCPREQUEST,
- Option4ClientFqdn::FLAG_E,
- "myhost.example.com.",
- Option4ClientFqdn::FULL,
- true);
-
- testProcessFqdn(query, Option4ClientFqdn::FLAG_E |
- Option4ClientFqdn::FLAG_S | Option4ClientFqdn::FLAG_O,
- "myhost.example.com.");
-
-}
-
// Test that exactly one NameChangeRequest is generated when the new lease
// has been acquired (old lease is NULL).
TEST_F(NameDhcpv4SrvTest, createNameChangeRequestsNewLease) {
@@ -483,7 +654,7 @@ TEST_F(NameDhcpv4SrvTest, createNameChangeRequestsNewLease) {
Lease4Ptr old_lease;
ASSERT_NO_THROW(srv_->createNameChangeRequests(lease, old_lease));
- ASSERT_EQ(1, srv_->name_change_reqs_.size());
+ ASSERT_EQ(1, d2_mgr_.getQueueSize());
verifyNameChangeRequest(isc::dhcp_ddns::CHG_ADD, true, true,
"192.0.2.3", "myhost.example.com.",
@@ -502,7 +673,7 @@ TEST_F(NameDhcpv4SrvTest, createNameChangeRequestsRenewNoChange) {
old_lease->valid_lft_ += 100;
ASSERT_NO_THROW(srv_->createNameChangeRequests(lease, old_lease));
- EXPECT_TRUE(srv_->name_change_reqs_.empty());
+ ASSERT_EQ(0, d2_mgr_.getQueueSize());
}
// Test that no NameChangeRequest is generated when forward and reverse
@@ -515,7 +686,7 @@ TEST_F(NameDhcpv4SrvTest, createNameChangeRequestsNoUpdate) {
"lease2.example.com.",
false, false);
ASSERT_NO_THROW(srv_->createNameChangeRequests(lease2, lease1));
- EXPECT_EQ(1, srv_->name_change_reqs_.size());
+ EXPECT_EQ(1, d2_mgr_.getQueueSize());
verifyNameChangeRequest(isc::dhcp_ddns::CHG_REMOVE, true, true,
"192.0.2.3", "lease1.example.com.",
@@ -527,7 +698,7 @@ TEST_F(NameDhcpv4SrvTest, createNameChangeRequestsNoUpdate) {
lease2->fqdn_rev_ = true;
lease2->fqdn_fwd_ = true;
ASSERT_NO_THROW(srv_->createNameChangeRequests(lease2, lease1));
- EXPECT_EQ(1, srv_->name_change_reqs_.size());
+ EXPECT_EQ(1, d2_mgr_.getQueueSize());
}
@@ -541,7 +712,7 @@ TEST_F(NameDhcpv4SrvTest, createNameChangeRequestsRenew) {
"lease2.example.com.",
true, true);
ASSERT_NO_THROW(srv_->createNameChangeRequests(lease2, lease1));
- ASSERT_EQ(2, srv_->name_change_reqs_.size());
+ ASSERT_EQ(2, d2_mgr_.getQueueSize());
verifyNameChangeRequest(isc::dhcp_ddns::CHG_REMOVE, true, true,
"192.0.2.3", "lease1.example.com.",
@@ -574,6 +745,9 @@ TEST_F(NameDhcpv4SrvTest, createNameChangeRequestsLeaseMismatch) {
// Test that the OFFER message generated as a result of the DISCOVER message
// processing will not result in generation of the NameChangeRequests.
TEST_F(NameDhcpv4SrvTest, processDiscover) {
+ IfaceMgrTestConfig test_config(true);
+ IfaceMgr::instance().openSockets4();
+
Pkt4Ptr req = generatePktWithFqdn(DHCPDISCOVER, Option4ClientFqdn::FLAG_S |
Option4ClientFqdn::FLAG_E,
"myhost.example.com.",
@@ -583,12 +757,15 @@ TEST_F(NameDhcpv4SrvTest, processDiscover) {
ASSERT_NO_THROW(reply = srv_->processDiscover(req));
checkResponse(reply, DHCPOFFER, 1234);
- EXPECT_TRUE(srv_->name_change_reqs_.empty());
+ EXPECT_EQ(0, d2_mgr_.getQueueSize());
}
// Test that server generates client's hostname from the IP address assigned
// to it when DHCPv4 Client FQDN option specifies an empty domain-name.
TEST_F(NameDhcpv4SrvTest, processRequestFqdnEmptyDomainName) {
+ IfaceMgrTestConfig test_config(true);
+ IfaceMgr::instance().openSockets4();
+
Pkt4Ptr req = generatePktWithFqdn(DHCPREQUEST, Option4ClientFqdn::FLAG_S |
Option4ClientFqdn::FLAG_E,
"", Option4ClientFqdn::PARTIAL, true);
@@ -599,25 +776,56 @@ TEST_F(NameDhcpv4SrvTest, processRequestFqdnEmptyDomainName) {
checkResponse(reply, DHCPACK, 1234);
// Verify that there is one NameChangeRequest generated.
- ASSERT_EQ(1, srv_->name_change_reqs_.size());
+ ASSERT_EQ(1, d2_mgr_.getQueueSize());
+
// The hostname is generated from the IP address acquired (yiaddr).
- std::ostringstream hostname;
- hostname << generatedNameFromAddress(reply->getYiaddr())
- << ".example.com.";
+ std::string hostname = generatedNameFromAddress(reply->getYiaddr());
+
verifyNameChangeRequest(isc::dhcp_ddns::CHG_ADD, true, true,
- reply->getYiaddr().toText(), hostname.str(),
+ reply->getYiaddr().toText(), hostname,
"", // empty DHCID forces that it is not checked
time(NULL) + subnet_->getValid(),
subnet_->getValid(), true);
}
// Test that server generates client's hostname from the IP address assigned
+// to it when DHCPv4 Client FQDN option specifies an empty domain-name AND
+// ddns updates are disabled.
+TEST_F(NameDhcpv4SrvTest, processRequestEmptyDomainNameDisabled) {
+ // Create fake interfaces and open fake sockets.
+ IfaceMgrTestConfig test_config(true);
+ IfaceMgr::instance().openSockets4();
+
+ disableD2();
+ Pkt4Ptr req = generatePktWithFqdn(DHCPREQUEST, Option4ClientFqdn::FLAG_S |
+ Option4ClientFqdn::FLAG_E,
+ "", Option4ClientFqdn::PARTIAL, true);
+ Pkt4Ptr reply;
+ ASSERT_NO_THROW(reply = srv_->processRequest(req));
+
+ checkResponse(reply, DHCPACK, 1234);
+
+ Option4ClientFqdnPtr fqdn = getClientFqdnOption(reply);
+ ASSERT_TRUE(fqdn);
+
+ // The hostname is generated from the IP address acquired (yiaddr).
+ std::string hostname = generatedNameFromAddress(reply->getYiaddr());
+
+ EXPECT_EQ(hostname, fqdn->getDomainName());
+ EXPECT_EQ(Option4ClientFqdn::FULL, fqdn->getDomainNameType());
+}
+
+
+// Test that server generates client's hostname from the IP address assigned
// to it when Hostname option carries the top level domain-name.
TEST_F(NameDhcpv4SrvTest, processRequestEmptyHostname) {
+ IfaceMgrTestConfig test_config(true);
+ IfaceMgr::instance().openSockets4();
+
Pkt4Ptr req = generatePktWithHostname(DHCPREQUEST, ".");
// Set interface for the incoming packet. The server requires it to
// generate client id.
- req->setIface("eth0");
+ req->setIface("eth1");
Pkt4Ptr reply;
ASSERT_NO_THROW(reply = srv_->processRequest(req));
@@ -625,12 +833,13 @@ TEST_F(NameDhcpv4SrvTest, processRequestEmptyHostname) {
checkResponse(reply, DHCPACK, 1234);
// Verify that there is one NameChangeRequest generated.
- ASSERT_EQ(1, srv_->name_change_reqs_.size());
+ ASSERT_EQ(1, d2_mgr_.getQueueSize());
+
// The hostname is generated from the IP address acquired (yiaddr).
- std::ostringstream hostname;
- hostname << generatedNameFromAddress(reply->getYiaddr()) << ".example.com.";
+ std::string hostname = generatedNameFromAddress(reply->getYiaddr());
+
verifyNameChangeRequest(isc::dhcp_ddns::CHG_ADD, true, true,
- reply->getYiaddr().toText(), hostname.str(),
+ reply->getYiaddr().toText(), hostname,
"", // empty DHCID forces that it is not checked
time(NULL), subnet_->getValid(), true);
}
@@ -640,6 +849,9 @@ TEST_F(NameDhcpv4SrvTest, processRequestEmptyHostname) {
// request but modify the DNS entries for the lease according to the contents
// of the FQDN sent in the second request.
TEST_F(NameDhcpv4SrvTest, processTwoRequestsFqdn) {
+ IfaceMgrTestConfig test_config(true);
+ IfaceMgr::instance().openSockets4();
+
Pkt4Ptr req1 = generatePktWithFqdn(DHCPREQUEST, Option4ClientFqdn::FLAG_S |
Option4ClientFqdn::FLAG_E,
"myhost.example.com.",
@@ -651,7 +863,7 @@ TEST_F(NameDhcpv4SrvTest, processTwoRequestsFqdn) {
checkResponse(reply, DHCPACK, 1234);
// Verify that there is one NameChangeRequest generated.
- ASSERT_EQ(1, srv_->name_change_reqs_.size());
+ ASSERT_EQ(1, d2_mgr_.getQueueSize());
verifyNameChangeRequest(isc::dhcp_ddns::CHG_ADD, true, true,
reply->getYiaddr().toText(), "myhost.example.com.",
"00010132E91AA355CFBB753C0F0497A5A940436"
@@ -671,7 +883,7 @@ TEST_F(NameDhcpv4SrvTest, processTwoRequestsFqdn) {
checkResponse(reply, DHCPACK, 1234);
// There should be two NameChangeRequests. Verify that they are valid.
- ASSERT_EQ(2, srv_->name_change_reqs_.size());
+ ASSERT_EQ(2, d2_mgr_.getQueueSize());
verifyNameChangeRequest(isc::dhcp_ddns::CHG_REMOVE, true, true,
reply->getYiaddr().toText(),
"myhost.example.com.",
@@ -692,11 +904,14 @@ TEST_F(NameDhcpv4SrvTest, processTwoRequestsFqdn) {
// but modify the DNS entries for the lease according to the contents of the
// Hostname sent in the second request.
TEST_F(NameDhcpv4SrvTest, processTwoRequestsHostname) {
+ IfaceMgrTestConfig test_config(true);
+ IfaceMgr::instance().openSockets4();
+
Pkt4Ptr req1 = generatePktWithHostname(DHCPREQUEST, "myhost.example.com.");
// Set interface for the incoming packet. The server requires it to
// generate client id.
- req1->setIface("eth0");
+ req1->setIface("eth1");
Pkt4Ptr reply;
@@ -705,7 +920,7 @@ TEST_F(NameDhcpv4SrvTest, processTwoRequestsHostname) {
checkResponse(reply, DHCPACK, 1234);
// Verify that there is one NameChangeRequest generated.
- ASSERT_EQ(1, srv_->name_change_reqs_.size());
+ ASSERT_EQ(1, d2_mgr_.getQueueSize());
verifyNameChangeRequest(isc::dhcp_ddns::CHG_ADD, true, true,
reply->getYiaddr().toText(), "myhost.example.com.",
"00010132E91AA355CFBB753C0F0497A5A940436"
@@ -719,14 +934,14 @@ TEST_F(NameDhcpv4SrvTest, processTwoRequestsHostname) {
// Set interface for the incoming packet. The server requires it to
// generate client id.
- req2->setIface("eth0");
+ req2->setIface("eth1");
ASSERT_NO_THROW(reply = srv_->processRequest(req2));
checkResponse(reply, DHCPACK, 1234);
// There should be two NameChangeRequests. Verify that they are valid.
- ASSERT_EQ(2, srv_->name_change_reqs_.size());
+ ASSERT_EQ(2, d2_mgr_.getQueueSize());
verifyNameChangeRequest(isc::dhcp_ddns::CHG_REMOVE, true, true,
reply->getYiaddr().toText(),
"myhost.example.com.",
@@ -742,40 +957,83 @@ TEST_F(NameDhcpv4SrvTest, processTwoRequestsHostname) {
time(NULL), subnet_->getValid(), true);
}
-// Test that when the Release message is sent for the previously acquired
-// lease, then server genenerates a NameChangeRequest to remove the entries
-// corresponding to the lease being released.
+// Test that when a release message is sent for a previously acquired lease,
+// DDNS updates are enabled that the server genenerates a NameChangeRequest
+// to remove entries corresponding to the released lease.
TEST_F(NameDhcpv4SrvTest, processRequestRelease) {
+ IfaceMgrTestConfig test_config(true);
+ IfaceMgr::instance().openSockets4();
+
+ // Verify the updates are enabled.
+ ASSERT_TRUE(CfgMgr::instance().ddnsEnabled());
+
+ // Create and process a lease request so we have a lease to release.
Pkt4Ptr req = generatePktWithFqdn(DHCPREQUEST, Option4ClientFqdn::FLAG_S |
Option4ClientFqdn::FLAG_E,
"myhost.example.com.",
Option4ClientFqdn::FULL, true);
-
Pkt4Ptr reply;
ASSERT_NO_THROW(reply = srv_->processRequest(req));
-
checkResponse(reply, DHCPACK, 1234);
- // Verify that there is one NameChangeRequest generated.
- ASSERT_EQ(1, srv_->name_change_reqs_.size());
+ // Verify that there is one NameChangeRequest generated for lease.
+ ASSERT_EQ(1, d2_mgr_.getQueueSize());
verifyNameChangeRequest(isc::dhcp_ddns::CHG_ADD, true, true,
reply->getYiaddr().toText(), "myhost.example.com.",
"00010132E91AA355CFBB753C0F0497A5A940436"
"965B68B6D438D98E680BF10B09F3BCF",
time(NULL), subnet_->getValid(), true);
- // Create a Release message.
+ // Create and process the Release message.
Pkt4Ptr rel = Pkt4Ptr(new Pkt4(DHCPRELEASE, 1234));
rel->setCiaddr(reply->getYiaddr());
rel->setRemoteAddr(IOAddress("192.0.2.3"));
rel->addOption(generateClientId());
rel->addOption(srv_->getServerID());
-
ASSERT_NO_THROW(srv_->processRelease(rel));
// The lease has been removed, so there should be a NameChangeRequest to
// remove corresponding DNS entries.
- ASSERT_EQ(1, srv_->name_change_reqs_.size());
+ ASSERT_EQ(1, d2_mgr_.getQueueSize());
+ verifyNameChangeRequest(isc::dhcp_ddns::CHG_REMOVE, true, true,
+ reply->getYiaddr().toText(), "myhost.example.com.",
+ "00010132E91AA355CFBB753C0F0497A5A940436"
+ "965B68B6D438D98E680BF10B09F3BCF",
+ time(NULL), subnet_->getValid(), true);
+}
+
+// Test that when the Release message is sent for a previously acquired lease
+// and DDNS updates are disabled that server does NOT generate a
+// NameChangeRequest to remove entries corresponding to the released lease.
+// Queue size is not available when updates are not enabled, however,
+// attempting to send a NCR when updates disabled will result in a throw.
+// If no throws are experienced then no attempt was made to send a NCR.
+TEST_F(NameDhcpv4SrvTest, processRequestReleaseUpdatesDisabled) {
+ // Create fake interfaces and open fake sockets.
+ IfaceMgrTestConfig test_config(true);
+ IfaceMgr::instance().openSockets4();
+
+ // Disable DDNS.
+ disableD2();
+ ASSERT_FALSE(CfgMgr::instance().ddnsEnabled());
+
+ // Create and process a lease request so we have a lease to release.
+ Pkt4Ptr req = generatePktWithFqdn(DHCPREQUEST, Option4ClientFqdn::FLAG_S |
+ Option4ClientFqdn::FLAG_E,
+ "myhost.example.com.",
+ Option4ClientFqdn::FULL, true);
+ Pkt4Ptr reply;
+ ASSERT_NO_THROW(reply = srv_->processRequest(req));
+ checkResponse(reply, DHCPACK, 1234);
+
+ // Create and process the Release message.
+ Pkt4Ptr rel = Pkt4Ptr(new Pkt4(DHCPRELEASE, 1234));
+ rel->setCiaddr(reply->getYiaddr());
+ rel->setRemoteAddr(IOAddress("192.0.2.3"));
+ rel->addOption(generateClientId());
+ rel->addOption(srv_->getServerID());
+ ASSERT_NO_THROW(srv_->processRelease(rel));
}
+
} // end of anonymous namespace
diff --git a/src/bin/dhcp6/config_parser.cc b/src/bin/dhcp6/config_parser.cc
index 32105ad..8976dab 100644
--- a/src/bin/dhcp6/config_parser.cc
+++ b/src/bin/dhcp6/config_parser.cc
@@ -368,7 +368,7 @@ public:
/// @param ignored first parameter
/// stores global scope parameters, options, option defintions.
Subnet6ConfigParser(const std::string&)
- :SubnetConfigParser("", globalContext()) {
+ :SubnetConfigParser("", globalContext(), IOAddress("::")) {
}
/// @brief Adds the created subnet to a server's configuration.
@@ -381,6 +381,12 @@ public:
isc_throw(Unexpected,
"Invalid cast in Subnet4ConfigParser::commit");
}
+
+ // Set relay infomation if it was provided
+ if (relay_info_) {
+ sub6ptr->setRelayInfo(*relay_info_);
+ }
+
isc::dhcp::CfgMgr::instance().addSubnet6(sub6ptr);
}
}
@@ -404,10 +410,13 @@ protected:
parser = new Uint32Parser(config_id, uint32_values_);
} else if ((config_id.compare("subnet") == 0) ||
(config_id.compare("interface") == 0) ||
+ (config_id.compare("client-class") == 0) ||
(config_id.compare("interface-id") == 0)) {
parser = new StringParser(config_id, string_values_);
} else if (config_id.compare("pool") == 0) {
parser = new Pool6Parser(config_id, pools_);
+ } else if (config_id.compare("relay") == 0) {
+ parser = new RelayInfoParser(config_id, relay_info_, Option::V6);
} else if (config_id.compare("pd-pools") == 0) {
parser = new PdPoolListParser(config_id, pools_);
} else if (config_id.compare("option-data") == 0) {
@@ -518,6 +527,14 @@ protected:
subnet6->setInterfaceId(opt);
}
+ // Try setting up client class (if specified)
+ try {
+ string client_class = string_values_->getParam("client-class");
+ subnet6->allowClientClass(client_class);
+ } catch (const DhcpConfigError&) {
+ // That's ok if it fails. client-class is optional.
+ }
+
subnet_.reset(subnet6);
}
diff --git a/src/bin/dhcp6/ctrl_dhcp6_srv.cc b/src/bin/dhcp6/ctrl_dhcp6_srv.cc
index e42c83b..43497cd 100644
--- a/src/bin/dhcp6/ctrl_dhcp6_srv.cc
+++ b/src/bin/dhcp6/ctrl_dhcp6_srv.cc
@@ -227,7 +227,7 @@ void ControlledDhcpv6Srv::establishSession() {
int ctrl_socket = cc_session_->getSocketDesc();
LOG_DEBUG(dhcp6_logger, DBG_DHCP6_START, DHCP6_CCSESSION_STARTED)
.arg(ctrl_socket);
- IfaceMgr::instance().set_session_socket(ctrl_socket, sessionReader);
+ IfaceMgr::instance().addExternalSocket(ctrl_socket, sessionReader);
}
void ControlledDhcpv6Srv::disconnectSession() {
@@ -236,13 +236,16 @@ void ControlledDhcpv6Srv::disconnectSession() {
config_session_ = NULL;
}
if (cc_session_) {
+
+ int ctrl_socket = cc_session_->getSocketDesc();
cc_session_->disconnect();
+
+ // deregister session socket
+ IfaceMgr::instance().deleteExternalSocket(ctrl_socket);
+
delete cc_session_;
cc_session_ = NULL;
}
-
- // deregister session socket
- IfaceMgr::instance().set_session_socket(IfaceMgr::INVALID_SOCKET, NULL);
}
ControlledDhcpv6Srv::ControlledDhcpv6Srv(uint16_t port)
diff --git a/src/bin/dhcp6/ctrl_dhcp6_srv.h b/src/bin/dhcp6/ctrl_dhcp6_srv.h
index 2fb6cc9..62c1813 100644
--- a/src/bin/dhcp6/ctrl_dhcp6_srv.h
+++ b/src/bin/dhcp6/ctrl_dhcp6_srv.h
@@ -105,7 +105,8 @@ protected:
/// various configuration values. Installing the dummy handler
/// that guarantees to return success causes initial configuration
/// to be stored for the session being created and that it can
- /// be later accessed with \ref isc::config::ConfigData::getFullConfig.
+ /// be later accessed with
+ /// \ref isc::config::ConfigData::getFullConfig().
///
/// @param new_config new configuration.
///
diff --git a/src/bin/dhcp6/dhcp6.dox b/src/bin/dhcp6/dhcp6.dox
index c722472..81f52c6 100644
--- a/src/bin/dhcp6/dhcp6.dox
+++ b/src/bin/dhcp6/dhcp6.dox
@@ -32,7 +32,7 @@
@section dhcpv6Session BIND10 message queue integration
- DHCPv4 server component is now integrated with BIND10 message queue.
+ DHCPv6 server component is now integrated with BIND10 message queue.
It follows the same principle as DHCPv4. See \ref dhcpv4Session for
details.
@@ -217,6 +217,12 @@ Currently there is no class behaviour coded in DHCPv6, hence no v6 equivalent of
@ref isc::dhcp::Dhcpv4Srv::classSpecificProcessing. Should any need for such a code arise,
it will be conducted in an external hooks library.
+It is possible to define class restrictions in subnet, so a given subnet is only
+accessible to clients that belong to a given class. That is implemented as isc::dhcp::Pkt6::classes_
+being passed in isc::dhcp::Dhcpv6Srv::selectSubnet() to isc::dhcp::CfgMgr::getSubnet6().
+Currently this capability is usable, but the number of scenarios it supports is
+limited.
+
@section dhcpv6Other Other DHCPv6 topics
For hooks API support in DHCPv6, see @ref dhcpv6Hooks.
diff --git a/src/bin/dhcp6/dhcp6.spec b/src/bin/dhcp6/dhcp6.spec
index 3810fad..540d48a 100644
--- a/src/bin/dhcp6/dhcp6.spec
+++ b/src/bin/dhcp6/dhcp6.spec
@@ -254,6 +254,30 @@
"item_default": ""
}
},
+
+ { "item_name": "client-class",
+ "item_type": "string",
+ "item_optional": false,
+ "item_default": "",
+ "item_description" : "Restricts access to this subnet to specified client class (if defined)"
+ },
+
+ { "item_name": "relay",
+ "item_type": "map",
+ "item_optional": false,
+ "item_default": {},
+ "item_description" : "Structure holding relay information.",
+ "map_item_spec": [
+ {
+ "item_name": "ip-address",
+ "item_type": "string",
+ "item_optional": false,
+ "item_default": "::",
+ "item_description" : "IPv6 address of the relay (defaults to :: if not specified)."
+ }
+ ]
+ },
+
{
"item_name": "pd-pools",
"item_type": "list",
diff --git a/src/bin/dhcp6/dhcp6_messages.mes b/src/bin/dhcp6/dhcp6_messages.mes
index 46449d8..a871a58 100644
--- a/src/bin/dhcp6/dhcp6_messages.mes
+++ b/src/bin/dhcp6/dhcp6_messages.mes
@@ -1,4 +1,4 @@
-# Copyright (C) 2012-2014 Internet Systems Consortium, Inc. ("ISC")
+# Copyright (C) 2012-2014 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
@@ -104,13 +104,7 @@ in its response to the client.
This debug message is logged when server has found the DHCPv6 Client FQDN Option
sent by a client and started processing it.
-% DHCP6_DDNS_REMOVE_EMPTY_HOSTNAME FQDN for the lease being deleted is empty: %1
-This error message is issued when a lease being deleted contains an indication
-that the DNS Update has been performed for it, but the FQDN is missing for this
-lease. This is an indication that someone may have messed up in the lease
-database.
-
-% DHCP6_DDNS_REMOVE_INVALID_HOSTNAME FQDN for the lease being deleted has invalid format: %1
+% DHCP6_DDNS_REMOVE_INVALID_HOSTNAME invalid FQDN: %1 for the lease: %2 when removing DNS bindings
This error message is issued when a lease being deleted contains an indication
that the DNS Update has been performed for it, but the FQDN held in the lease
database has invalid format and can't be transformed to the canonical on-wire
@@ -237,6 +231,11 @@ but the lease does not contain any client identification. This is most
likely due to a software error: please raise a bug report. As a temporary
workaround, manually remove the lease entry from the database.
+% DHCP6_NAME_GEN_UPDATE_FAIL failed to update the lease using address %1, after generating FQDN for a client, reason: %2
+This message indicates the failure when trying to update the lease and/or
+options in the server's response with the hostname generated by the server
+from the acquired address.
+
% DHCP6_NOT_RUNNING IPv6 DHCP server is not running
A warning message is issued when an attempt is made to shut down the
IPv6 DHCP server but it is not running.
@@ -272,6 +271,10 @@ of packet. Note that a packet marked as UNKNOWN may well be a valid
DHCP packet, just a type not expected by the server (e.g. it will report
a received OFFER packet as UNKNOWN).
+% DHCP6_PACKET_MISMATCH_SERVERID_DROP dropping packet %1 (transid=%2, interface=%3) having mismatched server identifier
+A debug message noting that server has received message with server identifier
+option that not matching server identifier that server is using.
+
% DHCP6_PACKET_RECEIVE_FAIL error on attempt to receive packet: %1
The IPv6 DHCP server tried to receive a packet but an error
occurred during this attempt. The reason for the error is included in
diff --git a/src/bin/dhcp6/dhcp6_srv.cc b/src/bin/dhcp6/dhcp6_srv.cc
index 3dffa37..9fcf086 100644
--- a/src/bin/dhcp6/dhcp6_srv.cc
+++ b/src/bin/dhcp6/dhcp6_srv.cc
@@ -104,9 +104,6 @@ namespace {
// DHCPv6 Client FQDN Option sent by a client. They will be removed
// when DDNS parameters for DHCPv6 are implemented with the ticket #3034.
-// Should server always include the FQDN option in its response, regardless
-// if it has been requested in ORO (Disabled).
-const bool FQDN_ALWAYS_INCLUDE = false;
// Enable AAAA RR update delegation to the client (Disabled).
const bool FQDN_ALLOW_CLIENT_UPDATE = false;
// Globally enable updates (Enabled).
@@ -211,6 +208,33 @@ void Dhcpv6Srv::sendPacket(const Pkt6Ptr& packet) {
IfaceMgr::instance().send(packet);
}
+bool
+Dhcpv6Srv::testServerID(const Pkt6Ptr& pkt){
+ /// @todo Currently we always check server identifier regardless if
+ /// it is allowed in the received message or not (per RFC3315).
+ /// If the server identifier is not allowed in the message, the
+ /// sanityCheck function should deal with it. We may rethink this
+ /// design if we decide that it is appropriate to check at this stage
+ /// of message processing that the server identifier must or must not
+ /// be present. In such case however, the logic checking server id
+ /// will have to be removed from sanityCheck and placed here instead,
+ /// to avoid duplicate checks.
+ OptionPtr server_id = pkt->getOption(D6O_SERVERID);
+ if (server_id){
+ // Let us test received ServerID if it is same as ServerID
+ // which is beeing used by server
+ if (getServerID()->getData() != server_id->getData()){
+ LOG_DEBUG(dhcp6_logger, DBG_DHCP6_DETAIL_DATA, DHCP6_PACKET_MISMATCH_SERVERID_DROP)
+ .arg(pkt->getName())
+ .arg(pkt->getTransid())
+ .arg(pkt->getIface());
+ return (false);
+ }
+ }
+ // retun True if: no serverid received or ServerIDs matching
+ return (true);
+}
+
bool Dhcpv6Srv::run() {
while (!shutdown_) {
/// @todo Calculate actual timeout to the next event (e.g. lease
@@ -283,6 +307,12 @@ bool Dhcpv6Srv::run() {
continue;
}
}
+ // Check if received query carries server identifier matching
+ // server identifier being used by the server.
+ if (!testServerID(query)){
+ continue;
+ }
+
LOG_DEBUG(dhcp6_logger, DBG_DHCP6_DETAIL, DHCP6_PACKET_RECEIVED)
.arg(query->getName());
LOG_DEBUG(dhcp6_logger, DBG_DHCP6_DETAIL_DATA, DHCP6_QUERY_DATA)
@@ -613,10 +643,11 @@ Dhcpv6Srv::generateServerID() {
seconds -= DUID_TIME_EPOCH;
OptionBuffer srvid(8 + iface->getMacLen());
- writeUint16(DUID::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());
+ // We know that the buffer is more than 8 bytes long at this point.
+ writeUint16(DUID::DUID_LLT, &srvid[0], 2);
+ writeUint16(HWTYPE_ETHERNET, &srvid[2], 2);
+ writeUint32(static_cast<uint32_t>(seconds), &srvid[4], 4);
+ memcpy(&srvid[8], iface->getMac(), iface->getMacLen());
serverid_ = OptionPtr(new Option(Option::V6, D6O_SERVERID,
srvid.begin(), srvid.end()));
@@ -629,8 +660,8 @@ Dhcpv6Srv::generateServerID() {
// See Section 9.3 of RFC3315 for details.
OptionBuffer srvid(12);
- writeUint16(DUID::DUID_EN, &srvid[0]);
- writeUint32(ENTERPRISE_ID_ISC, &srvid[2]);
+ writeUint16(DUID::DUID_EN, &srvid[0], srvid.size());
+ writeUint32(ENTERPRISE_ID_ISC, &srvid[2], srvid.size() - 2);
// Length of the identifier is company specific. I hereby declare
// ISC "standard" of 6 bytes long pseudo-random numbers.
@@ -829,10 +860,12 @@ Dhcpv6Srv::selectSubnet(const Pkt6Ptr& question) {
// This is a direct (non-relayed) message
// Try to find a subnet if received packet from a directly connected client
- subnet = CfgMgr::instance().getSubnet6(question->getIface());
+ subnet = CfgMgr::instance().getSubnet6(question->getIface(),
+ question->classes_);
if (!subnet) {
// If no subnet was found, try to find it based on remote address
- subnet = CfgMgr::instance().getSubnet6(question->getRemoteAddr());
+ subnet = CfgMgr::instance().getSubnet6(question->getRemoteAddr(),
+ question->classes_);
}
} else {
@@ -840,17 +873,19 @@ Dhcpv6Srv::selectSubnet(const Pkt6Ptr& question) {
OptionPtr interface_id = question->getAnyRelayOption(D6O_INTERFACE_ID,
Pkt6::RELAY_GET_FIRST);
if (interface_id) {
- subnet = CfgMgr::instance().getSubnet6(interface_id);
+ subnet = CfgMgr::instance().getSubnet6(interface_id,
+ question->classes_);
}
if (!subnet) {
- // If no interface-id was specified (or not configured on server), let's
- // try address matching
+ // If no interface-id was specified (or not configured on server),
+ // let's try address matching
IOAddress link_addr = question->relay_info_.back().linkaddr_;
// if relay filled in link_addr field, then let's use it
if (link_addr != IOAddress("::")) {
- subnet = CfgMgr::instance().getSubnet6(link_addr);
+ subnet = CfgMgr::instance().getSubnet6(link_addr,
+ question->classes_);
}
}
}
@@ -890,8 +925,7 @@ Dhcpv6Srv::selectSubnet(const Pkt6Ptr& question) {
}
void
-Dhcpv6Srv::assignLeases(const Pkt6Ptr& question, Pkt6Ptr& answer,
- const Option6ClientFqdnPtr& fqdn) {
+Dhcpv6Srv::assignLeases(const Pkt6Ptr& question, Pkt6Ptr& answer) {
// We need to allocate addresses for all IA_NA options in the client's
// question (i.e. SOLICIT or REQUEST) message.
@@ -944,10 +978,9 @@ Dhcpv6Srv::assignLeases(const Pkt6Ptr& question, Pkt6Ptr& answer,
opt != question->options_.end(); ++opt) {
switch (opt->second->getType()) {
case D6O_IA_NA: {
- OptionPtr answer_opt = assignIA_NA(subnet, duid, question,
+ OptionPtr answer_opt = assignIA_NA(subnet, duid, question, answer,
boost::dynamic_pointer_cast<
- Option6IA>(opt->second),
- fqdn);
+ Option6IA>(opt->second));
if (answer_opt) {
answer->addOption(answer_opt);
}
@@ -967,14 +1000,14 @@ Dhcpv6Srv::assignLeases(const Pkt6Ptr& question, Pkt6Ptr& answer,
}
}
-Option6ClientFqdnPtr
-Dhcpv6Srv::processClientFqdn(const Pkt6Ptr& question) {
+void
+Dhcpv6Srv::processClientFqdn(const Pkt6Ptr& question, const Pkt6Ptr& answer) {
// Get Client FQDN Option from the client's message. If this option hasn't
// been included, do nothing.
Option6ClientFqdnPtr fqdn = boost::dynamic_pointer_cast<
Option6ClientFqdn>(question->getOption(D6O_CLIENT_FQDN));
if (!fqdn) {
- return (fqdn);
+ return;
}
LOG_DEBUG(dhcp6_logger, DBG_DHCP6_DETAIL,
@@ -1025,13 +1058,14 @@ Dhcpv6Srv::processClientFqdn(const Pkt6Ptr& question) {
// generate one.
if (fqdn->getDomainNameType() == Option6ClientFqdn::PARTIAL) {
std::ostringstream name;
- if (fqdn->getDomainName().empty()) {
- name << FQDN_GENERATED_PARTIAL_NAME;
+ if (fqdn->getDomainName().empty() || FQDN_REPLACE_CLIENT_NAME) {
+ fqdn->setDomainName("", Option6ClientFqdn::PARTIAL);
+
} else {
name << fqdn->getDomainName();
+ name << "." << FQDN_PARTIAL_SUFFIX;
+ fqdn_resp->setDomainName(name.str(), Option6ClientFqdn::FULL);
}
- name << "." << FQDN_PARTIAL_SUFFIX;
- fqdn_resp->setDomainName(name.str(), Option6ClientFqdn::FULL);
// Server may be configured to replace a name supplied by a client,
// even if client supplied fully qualified domain-name.
@@ -1042,58 +1076,17 @@ Dhcpv6Srv::processClientFqdn(const Pkt6Ptr& question) {
}
- // Return the FQDN option which can be included in the server's response.
- // Note that it doesn't have to be included, if client didn't request
- // it using ORO and server is not configured to always include it.
- return (fqdn_resp);
+ // The FQDN has been processed successfully. Let's append it to the
+ // response to be sent to a client. Note that the Client FQDN option is
+ // always sent back to the client if Client FQDN was included in the
+ // client's message.
+ answer->addOption(fqdn_resp);
}
-
void
-Dhcpv6Srv::appendClientFqdn(const Pkt6Ptr& question,
- Pkt6Ptr& answer,
- const Option6ClientFqdnPtr& fqdn) {
-
- // If FQDN is NULL, it means that client did not request DNS Update, plus
- // server doesn't force updates.
- if (!fqdn) {
- return;
- }
-
- // Server sends back the FQDN option to the client if client has requested
- // it using Option Request Option. However, server may be configured to
- // send the FQDN option in its response, regardless whether client requested
- // it or not.
- bool include_fqdn = FQDN_ALWAYS_INCLUDE;
- if (!include_fqdn) {
- OptionUint16ArrayPtr oro = boost::dynamic_pointer_cast<
- OptionUint16Array>(question->getOption(D6O_ORO));
- if (oro) {
- const std::vector<uint16_t>& values = oro->getValues();
- for (int i = 0; i < values.size(); ++i) {
- if (values[i] == D6O_CLIENT_FQDN) {
- include_fqdn = true;
- }
- }
- }
- }
-
- if (include_fqdn) {
- LOG_DEBUG(dhcp6_logger, DBG_DHCP6_DETAIL,
- DHCP6_DDNS_SEND_FQDN).arg(fqdn->toText());
- answer->addOption(fqdn);
- }
-
-}
-
-void
-Dhcpv6Srv::createNameChangeRequests(const Pkt6Ptr& answer,
- const Option6ClientFqdnPtr& opt_fqdn) {
-
- // It is likely that client haven't included the FQDN option in the message
- // and server is not configured to always update DNS. In such cases,
- // FQDN option will be NULL. This is valid state, so we simply return.
- if (!opt_fqdn) {
+Dhcpv6Srv::createNameChangeRequests(const Pkt6Ptr& answer) {
+ // Don't create NameChangeRequests if DNS updates are disabled.
+ if (!FQDN_ENABLE_UPDATE) {
return;
}
@@ -1106,6 +1099,14 @@ Dhcpv6Srv::createNameChangeRequests(const Pkt6Ptr& answer,
<< " NULL when creating DNS NameChangeRequest");
}
+ // It is likely that client haven't included the FQDN option. In such case,
+ // FQDN option will be NULL. This is valid state, so we simply return.
+ Option6ClientFqdnPtr opt_fqdn = boost::dynamic_pointer_cast<
+ Option6ClientFqdn>(answer->getOption(D6O_CLIENT_FQDN));
+ if (!opt_fqdn) {
+ return;
+ }
+
// Get the Client Id. It is mandatory and a function creating a response
// would have thrown an exception if it was missing. Thus throwning
// Unexpected if it is missing as it is a programming error.
@@ -1133,8 +1134,8 @@ Dhcpv6Srv::createNameChangeRequests(const Pkt6Ptr& answer,
OptionCollection answer_ias = answer->getOptions(D6O_IA_NA);
for (OptionCollection::const_iterator answer_ia =
answer_ias.begin(); answer_ia != answer_ias.end(); ++answer_ia) {
- // @todo IA_NA may contain multiple addresses. We should process
- // each address individually. Currently we get only one.
+ /// @todo IA_NA may contain multiple addresses. We should process
+ /// each address individually. Currently we get only one.
Option6IAAddrPtr iaaddr = boost::static_pointer_cast<
Option6IAAddr>(answer_ia->second->getOption(D6O_IAADDR));
// We need an address to create a name-to-address mapping.
@@ -1160,31 +1161,33 @@ Dhcpv6Srv::createNameChangeRequests(const Pkt6Ptr& answer,
LOG_DEBUG(dhcp6_logger, DBG_DHCP6_DETAIL,
DHCP6_DDNS_CREATE_ADD_NAME_CHANGE_REQUEST).arg(ncr.toText());
+
+ /// @todo Currently we create NCR with the first IPv6 address that
+ /// is carried in one of the IA_NAs. In the future, the NCR API should
+ /// be extended to map multiple IPv6 addresses to a single FQDN.
+ /// In such case, this return statement will be removed.
+ return;
}
}
void
Dhcpv6Srv::createRemovalNameChangeRequest(const Lease6Ptr& lease) {
- // If we haven't performed a DNS Update when lease was acquired,
- // there is nothing to do here.
- if (!lease->fqdn_fwd_ && !lease->fqdn_rev_) {
+ // Don't create NameChangeRequests if DNS updates are disabled.
+ if (!FQDN_ENABLE_UPDATE) {
return;
}
- // When lease was added into a database the host name should have
- // been added. The hostname can be empty if someone messed up in the
- // lease data base and removed the hostname.
- if (lease->hostname_.empty()) {
- LOG_ERROR(dhcp6_logger, DHCP6_DDNS_REMOVE_EMPTY_HOSTNAME)
- .arg(lease->addr_.toText());
+ // If we haven't performed a DNS Update when lease was acquired,
+ // there is nothing to do here.
+ if (!lease->fqdn_fwd_ && !lease->fqdn_rev_) {
return;
}
// If hostname is non-empty, try to convert it to wire format so as
// DHCID can be computed from it. This may throw an exception if hostname
- // has invalid format. Again, this should be only possible in case of
- // manual intervention in the database. Note that the last parameter
- // passed to the writeFqdn function forces conversion of the FQDN
+ // has invalid format or is empty. Again, this should be only possible
+ // in case of manual intervention in the database. Note that the last
+ // parameter passed to the writeFqdn function forces conversion of the FQDN
// to lower case. This is required by the RFC4701, section 3.5.
// The DHCID computation is further in this function.
std::vector<uint8_t> hostname_wire;
@@ -1192,7 +1195,8 @@ Dhcpv6Srv::createRemovalNameChangeRequest(const Lease6Ptr& lease) {
OptionDataTypeUtil::writeFqdn(lease->hostname_, hostname_wire, true);
} catch (const Exception& ex) {
LOG_ERROR(dhcp6_logger, DHCP6_DDNS_REMOVE_INVALID_HOSTNAME)
- .arg(lease->hostname_);
+ .arg(lease->hostname_.empty() ? "(empty)" : lease->hostname_)
+ .arg(lease->addr_.toText());
return;
}
@@ -1233,14 +1237,14 @@ Dhcpv6Srv::sendNameChangeRequests() {
OptionPtr
Dhcpv6Srv::assignIA_NA(const Subnet6Ptr& subnet, const DuidPtr& duid,
- const Pkt6Ptr& query, boost::shared_ptr<Option6IA> ia,
- const Option6ClientFqdnPtr& fqdn) {
+ const Pkt6Ptr& query, const Pkt6Ptr& answer,
+ boost::shared_ptr<Option6IA> ia) {
// If there is no subnet selected for handling this IA_NA, the only thing to do left is
// to say that we are sorry, but the user won't get an address. As a convenience, we
// use a different status text to indicate that (compare to the same status code,
// but different wording below)
if (!subnet) {
- // Create empty IA_NA option with IAID matching the request.
+ // Creatasse empty IA_NA option with IAID matching the request.
// Note that we don't use OptionDefinition class to create this option.
// This is because we prefer using a constructor of Option6IA that
// initializes IAID. Otherwise we would have to use setIAID() after
@@ -1255,16 +1259,16 @@ Dhcpv6Srv::assignIA_NA(const Subnet6Ptr& subnet, const DuidPtr& duid,
// Check if the client sent us a hint in his IA_NA. Clients may send an
// address in their IA_NA options as a suggestion (e.g. the last address
// they used before).
- boost::shared_ptr<Option6IAAddr> hintOpt = boost::dynamic_pointer_cast<Option6IAAddr>
- (ia->getOption(D6O_IAADDR));
+ boost::shared_ptr<Option6IAAddr> hint_opt =
+ boost::dynamic_pointer_cast<Option6IAAddr>(ia->getOption(D6O_IAADDR));
IOAddress hint("::");
- if (hintOpt) {
- hint = hintOpt->getAddress();
+ if (hint_opt) {
+ hint = hint_opt->getAddress();
}
LOG_DEBUG(dhcp6_logger, DBG_DHCP6_DETAIL, DHCP6_PROCESS_IA_NA_REQUEST)
- .arg(duid?duid->toText():"(no-duid)").arg(ia->getIAID())
- .arg(hintOpt?hint.toText():"(no hint)");
+ .arg(duid ? duid->toText() : "(no-duid)").arg(ia->getIAID())
+ .arg(hint_opt ? hint.toText() : "(no hint)");
// "Fake" allocation is processing of SOLICIT message. We pretend to do an
// allocation, but we do not put the lease in the database. That is ok,
@@ -1288,6 +1292,8 @@ Dhcpv6Srv::assignIA_NA(const Subnet6Ptr& subnet, const DuidPtr& duid,
// the update.
bool do_fwd = false;
bool do_rev = false;
+ Option6ClientFqdnPtr fqdn = boost::dynamic_pointer_cast<
+ Option6ClientFqdn>(answer->getOption(D6O_CLIENT_FQDN));
if (fqdn) {
// Flag S must not coexist with flag N being set to 1, so if S=1
// server takes responsibility for both reverse and forward updates.
@@ -1309,13 +1315,15 @@ Dhcpv6Srv::assignIA_NA(const Subnet6Ptr& subnet, const DuidPtr& duid,
// will try to honour the hint, but it is just a hint - some other address
// may be used instead. If fake_allocation is set to false, the lease will
// be inserted into the LeaseMgr as well.
+ Lease6Collection old_leases;
Lease6Collection leases = alloc_engine_->allocateLeases6(subnet, duid,
ia->getIAID(),
hint, Lease::TYPE_NA,
do_fwd, do_rev,
hostname,
fake_allocation,
- callout_handle);
+ callout_handle,
+ old_leases);
/// @todo: Handle more than one lease
Lease6Ptr lease;
if (!leases.empty()) {
@@ -1350,26 +1358,25 @@ Dhcpv6Srv::assignIA_NA(const Subnet6Ptr& subnet, const DuidPtr& duid,
// but this is considered waste of bandwidth as absence of status
// code is considered a success.
+ Lease6Ptr old_lease;
+ if (!old_leases.empty()) {
+ old_lease = *old_leases.begin();
+ }
// Allocation engine may have returned an existing lease. If so, we
// have to check that the FQDN settings we provided are the same
// that were set. If they aren't, we will have to remove existing
// DNS records and update the lease with the new settings.
- if ((lease->hostname_ != hostname) || (lease->fqdn_fwd_ != do_fwd) ||
- (lease->fqdn_rev_ != do_rev)) {
+ if (!fake_allocation && old_lease &&
+ !lease->hasIdenticalFqdn(*old_lease)) {
LOG_DEBUG(dhcp6_logger, DBG_DHCP6_DETAIL,
DHCP6_DDNS_LEASE_ASSIGN_FQDN_CHANGE)
- .arg(lease->toText())
+ .arg(old_lease->toText())
.arg(hostname)
.arg(do_rev ? "true" : "false")
.arg(do_fwd ? "true" : "false");
// Schedule removal of the existing lease.
- createRemovalNameChangeRequest(lease);
- // Set the new lease properties and update.
- lease->hostname_ = hostname;
- lease->fqdn_fwd_ = do_fwd;
- lease->fqdn_rev_ = do_rev;
- LeaseMgrFactory::instance().updateLease6(lease);
+ createRemovalNameChangeRequest(old_lease);
}
} else {
@@ -1437,13 +1444,15 @@ Dhcpv6Srv::assignIA_PD(const Subnet6Ptr& subnet, const DuidPtr& duid,
// will try to honour the hint, but it is just a hint - some other address
// may be used instead. If fake_allocation is set to false, the lease will
// be inserted into the LeaseMgr as well.
+ Lease6Collection old_leases;
Lease6Collection leases = alloc_engine_->allocateLeases6(subnet, duid,
- ia->getIAID(),
- hint, Lease::TYPE_PD,
- false, false,
- string(),
- fake_allocation,
- callout_handle);
+ ia->getIAID(),
+ hint, Lease::TYPE_PD,
+ false, false,
+ string(),
+ fake_allocation,
+ callout_handle,
+ old_leases);
if (!leases.empty()) {
@@ -1491,8 +1500,8 @@ Dhcpv6Srv::assignIA_PD(const Subnet6Ptr& subnet, const DuidPtr& duid,
OptionPtr
Dhcpv6Srv::renewIA_NA(const Subnet6Ptr& subnet, const DuidPtr& duid,
- const Pkt6Ptr& query, boost::shared_ptr<Option6IA> ia,
- const Option6ClientFqdnPtr& fqdn) {
+ const Pkt6Ptr& query, const Pkt6Ptr& answer,
+ boost::shared_ptr<Option6IA> ia) {
if (!subnet) {
// There's no subnet select for this client. There's nothing to renew.
boost::shared_ptr<Option6IA> ia_rsp(new Option6IA(D6O_IA_NA, ia->getIAID()));
@@ -1541,6 +1550,8 @@ Dhcpv6Srv::renewIA_NA(const Subnet6Ptr& subnet, const DuidPtr& duid,
// the update.
bool do_fwd = false;
bool do_rev = false;
+ Option6ClientFqdnPtr fqdn = boost::dynamic_pointer_cast<
+ Option6ClientFqdn>(answer->getOption(D6O_CLIENT_FQDN));
if (fqdn) {
if (fqdn->getFlag(Option6ClientFqdn::FLAG_S)) {
do_fwd = true;
@@ -1733,8 +1744,7 @@ Dhcpv6Srv::renewIA_PD(const Subnet6Ptr& subnet, const DuidPtr& duid,
}
void
-Dhcpv6Srv::renewLeases(const Pkt6Ptr& renew, Pkt6Ptr& reply,
- const Option6ClientFqdnPtr& fqdn) {
+Dhcpv6Srv::renewLeases(const Pkt6Ptr& renew, Pkt6Ptr& reply) {
// We need to renew addresses for all IA_NA options in the client's
// RENEW message.
@@ -1778,10 +1788,9 @@ Dhcpv6Srv::renewLeases(const Pkt6Ptr& renew, Pkt6Ptr& reply,
switch (opt->second->getType()) {
case D6O_IA_NA: {
- OptionPtr answer_opt = renewIA_NA(subnet, duid, renew,
+ OptionPtr answer_opt = renewIA_NA(subnet, duid, renew, reply,
boost::dynamic_pointer_cast<
- Option6IA>(opt->second),
- fqdn);
+ Option6IA>(opt->second));
if (answer_opt) {
reply->addOption(answer_opt);
}
@@ -2178,13 +2187,14 @@ Dhcpv6Srv::processSolicit(const Pkt6Ptr& solicit) {
appendRequestedOptions(solicit, advertise);
appendRequestedVendorOptions(solicit, advertise);
- Option6ClientFqdnPtr fqdn = processClientFqdn(solicit);
- assignLeases(solicit, advertise, fqdn);
- appendClientFqdn(solicit, advertise, fqdn);
+ processClientFqdn(solicit, advertise);
+ assignLeases(solicit, advertise);
// Note, that we don't create NameChangeRequests here because we don't
// perform DNS Updates for Solicit. Client must send Request to update
// DNS.
+ generateFqdn(advertise);
+
return (advertise);
}
@@ -2200,10 +2210,10 @@ Dhcpv6Srv::processRequest(const Pkt6Ptr& request) {
appendRequestedOptions(request, reply);
appendRequestedVendorOptions(request, reply);
- Option6ClientFqdnPtr fqdn = processClientFqdn(request);
- assignLeases(request, reply, fqdn);
- appendClientFqdn(request, reply, fqdn);
- createNameChangeRequests(reply, fqdn);
+ processClientFqdn(request, reply);
+ assignLeases(request, reply);
+ generateFqdn(reply);
+ createNameChangeRequests(reply);
return (reply);
}
@@ -2219,12 +2229,12 @@ Dhcpv6Srv::processRenew(const Pkt6Ptr& renew) {
appendDefaultOptions(renew, reply);
appendRequestedOptions(renew, reply);
- Option6ClientFqdnPtr fqdn = processClientFqdn(renew);
- renewLeases(renew, reply, fqdn);
- appendClientFqdn(renew, reply, fqdn);
- createNameChangeRequests(reply, fqdn);
+ processClientFqdn(renew, reply);
+ renewLeases(renew, reply);
+ generateFqdn(reply);
+ createNameChangeRequests(reply);
- return reply;
+ return (reply);
}
Pkt6Ptr
@@ -2355,10 +2365,13 @@ Dhcpv6Srv::unpackOptions(const OptionBuffer& buf,
// The buffer being read comprises a set of options, each starting with
// a two-byte type code and a two-byte length field.
while (offset + 4 <= length) {
- uint16_t opt_type = isc::util::readUint16(&buf[offset]);
+ // At this point, from the while condition, we know that there
+ // are at least 4 bytes available following offset in the
+ // buffer.
+ uint16_t opt_type = isc::util::readUint16(&buf[offset], 2);
offset += 2;
- uint16_t opt_len = isc::util::readUint16(&buf[offset]);
+ uint16_t opt_len = isc::util::readUint16(&buf[offset], 2);
offset += 2;
if (offset + opt_len > length) {
@@ -2460,5 +2473,78 @@ void Dhcpv6Srv::classifyPacket(const Pkt6Ptr& pkt) {
}
}
+void
+Dhcpv6Srv::generateFqdn(const Pkt6Ptr& answer) {
+ if (!answer) {
+ isc_throw(isc::Unexpected, "an instance of the object encapsulating"
+ " a message must not be NULL when generating FQDN");
+ }
+
+ // It is likely that client haven't included the FQDN option. In such case,
+ // FQDN option will be NULL. Also, there is nothing to do if the option
+ // is present and conveys the non-empty FQDN.
+ Option6ClientFqdnPtr fqdn = boost::dynamic_pointer_cast<
+ Option6ClientFqdn>(answer->getOption(D6O_CLIENT_FQDN));
+ if (!fqdn || !fqdn->getDomainName().empty()) {
+ return;
+ }
+
+ // Get the first IA_NA acquired for the client.
+ OptionPtr ia = answer->getOption(D6O_IA_NA);
+ if (!ia) {
+ return;
+ }
+
+ // If it has any IAAddr, use the first one to generate unique FQDN.
+ Option6IAAddrPtr iaaddr = boost::dynamic_pointer_cast<
+ Option6IAAddr>(ia->getOption(D6O_IAADDR));
+ if (!iaaddr) {
+ return;
+ }
+ // Get the IPv6 address acquired by the client.
+ IOAddress addr = iaaddr->getAddress();
+ std::string hostname = addr.toText();
+ // Colons may not be ok for FQDNs so let's replace them with hyphens.
+ std::replace(hostname.begin(), hostname.end(), ':', '-');
+ std::ostringstream stream;
+ // The final FQDN consists of the partial domain name and the suffix.
+ // For example, if the acquired address is 2001:db8:1::2, the generated
+ // FQDN may be:
+ // host-2001-db8:1--2.example.com.
+ // where prefix 'host' should be configurable. The domain name suffix
+ // should also be configurable.
+ stream << "host-" << hostname << "." << FQDN_PARTIAL_SUFFIX << ".";
+ try {
+ // The lease has been acquired but the FQDN for this lease hasn't
+ // been updated in the lease database. We now have new FQDN
+ // generated, so the lease database has to be updated here.
+ // However, never update lease database for Advertise, just send
+ // our notion of client's FQDN in the Client FQDN option.
+ if (answer->getType() != DHCPV6_ADVERTISE) {
+ Lease6Ptr lease =
+ LeaseMgrFactory::instance().getLease6(Lease::TYPE_NA, addr);
+ if (lease) {
+ lease->hostname_ = stream.str();
+ LeaseMgrFactory::instance().updateLease6(lease);
+
+ } else {
+ isc_throw(isc::Unexpected, "there is no lease in the database "
+ " for address " << addr << ", so as it is impossible"
+ " to update FQDN data. This is a programmatic error"
+ " as the given address is now being handed to the"
+ " client");
+ }
+ }
+
+ // Set the generated FQDN in the Client FQDN option.
+ fqdn->setDomainName(stream.str(), Option6ClientFqdn::FULL);
+
+ } catch (const Exception& ex) {
+ LOG_ERROR(dhcp6_logger, DHCP6_NAME_GEN_UPDATE_FAIL)
+ .arg(hostname)
+ .arg(ex.what());
+ }
+}
+
};
};
diff --git a/src/bin/dhcp6/dhcp6_srv.h b/src/bin/dhcp6/dhcp6_srv.h
index 594d833..3529841 100644
--- a/src/bin/dhcp6/dhcp6_srv.h
+++ b/src/bin/dhcp6/dhcp6_srv.h
@@ -119,6 +119,16 @@ public:
protected:
+ /// @brief Compare received server id with our server id
+ ///
+ /// Checks if the server id carried in a query from a client matches
+ /// server identifier being used by the server.
+ ///
+ /// @param pkt DHCPv6 packet carrying server identifier to be checked.
+ /// @return true if server id carried in the query matches server id
+ /// used by the server; false otherwise.
+ bool testServerID(const Pkt6Ptr& pkt);
+
/// @brief verifies if specified packet meets RFC requirements
///
/// Checks if mandatory option is really there, that forbidden option
@@ -214,15 +224,17 @@ protected:
/// @param subnet subnet the client is connected to
/// @param duid client's duid
/// @param query client's message (typically SOLICIT or REQUEST)
+ /// @param answer server's response to the client's message. This
+ /// message should contain Client FQDN option being sent by the server
+ /// to the client (if the client sent this option to the server).
/// @param ia pointer to client's IA_NA option (client's request)
- /// @param fqdn A DHCPv6 Client FQDN %Option generated in a response to the
- /// FQDN option sent by a client.
+ ///
/// @return IA_NA option (server's response)
OptionPtr assignIA_NA(const isc::dhcp::Subnet6Ptr& subnet,
const isc::dhcp::DuidPtr& duid,
const isc::dhcp::Pkt6Ptr& query,
- Option6IAPtr ia,
- const Option6ClientFqdnPtr& fqdn);
+ const isc::dhcp::Pkt6Ptr& answer,
+ Option6IAPtr ia);
/// @brief Processes IA_PD option (and assigns prefixes if necessary).
///
@@ -250,12 +262,14 @@ protected:
/// @param subnet subnet the sender belongs to
/// @param duid client's duid
/// @param query client's message
+ /// @param answer server's response to the client's message. This
+ /// message should contain Client FQDN option being sent by the server
+ /// to the client (if the client sent this option to the server).
/// @param ia IA_NA option that is being renewed
- /// @param fqdn DHCPv6 Client FQDN Option included in the server's response
/// @return IA_NA option (server's response)
OptionPtr renewIA_NA(const Subnet6Ptr& subnet, const DuidPtr& duid,
- const Pkt6Ptr& query, boost::shared_ptr<Option6IA> ia,
- const Option6ClientFqdnPtr& fqdn);
+ const Pkt6Ptr& query, const Pkt6Ptr& answer,
+ boost::shared_ptr<Option6IA> ia);
/// @brief Renews specific IA_PD option
///
@@ -352,11 +366,10 @@ protected:
/// @todo: Extend this method once TA and PD becomes supported
///
/// @param question client's message (with requested IA_NA)
- /// @param answer server's message (IA_NA options will be added here)
- /// @param fqdn an FQDN option generated in a response to the client's
- /// FQDN option.
- void assignLeases(const Pkt6Ptr& question, Pkt6Ptr& answer,
- const Option6ClientFqdnPtr& fqdn);
+ /// @param answer server's message (IA_NA options will be added here).
+ /// This message should contain Client FQDN option being sent by the server
+ /// to the client (if the client sent this option to the server).
+ void assignLeases(const Pkt6Ptr& question, Pkt6Ptr& answer);
/// @brief Processes Client FQDN Option.
///
@@ -365,7 +378,7 @@ protected:
/// Received option comprises flags field which controls what DNS updates
/// server should do. Server may override client's preference based on
/// the current configuration. Server indicates that it has overridden
- /// the preference by storing DHCPv6 Client Fqdn %Option with the
+ /// the preference by storing DHCPv6 Client FQDN option with the
/// appropriate flags in the response to a client. This option is also
/// used to communicate the client's domain-name which should be sent
/// to the DNS in the update. Again, server may act upon the received
@@ -376,25 +389,10 @@ protected:
/// held in this function.
///
/// @param question Client's message.
- ///
- /// @return FQDN option produced in the response to the client's message.
- Option6ClientFqdnPtr processClientFqdn(const Pkt6Ptr& question);
-
- /// @brief Adds DHCPv6 Client FQDN %Option to the server response.
- ///
- /// This function will add the specified FQDN option into the server's
- /// response when FQDN is not NULL and server is either configured to
- /// always include the FQDN in the response or client requested it using
- /// %Option Request %Option.
- /// This function is exception safe.
- ///
- /// @param question A message received from the client.
- /// @param [out] answer A server's response where FQDN option will be added.
- /// @param fqdn A DHCPv6 Client FQDN %Option to be added to the server's
- /// response to a client.
- void appendClientFqdn(const Pkt6Ptr& question,
- Pkt6Ptr& answer,
- const Option6ClientFqdnPtr& fqdn);
+ /// @param answer Server's response to a client. If server generated
+ /// Client FQDN option for the client, this option is stored in this
+ /// object.
+ void processClientFqdn(const Pkt6Ptr& question, const Pkt6Ptr& answer);
/// @brief Creates a number of @c isc::dhcp_ddns::NameChangeRequest objects
/// based on the DHCPv6 Client FQDN %Option.
@@ -410,11 +408,9 @@ protected:
///
/// @todo Add support for multiple IAADDR options in the IA_NA.
///
- /// @param answer A message beging sent to the Client.
- /// @param fqdn_answer A DHCPv6 Client FQDN %Option which is included in the
- /// response message sent to a client.
- void createNameChangeRequests(const Pkt6Ptr& answer,
- const Option6ClientFqdnPtr& fqdn_answer);
+ /// @param answer A message beging sent to the Client. If it holds the
+ /// Client FQDN option, this option is used to create NameChangeRequests.
+ void createNameChangeRequests(const Pkt6Ptr& answer);
/// @brief Creates a @c isc::dhcp_ddns::NameChangeRequest which requests
/// removal of DNS entries for a particular lease.
@@ -450,10 +446,7 @@ protected:
/// as IA_NA/IAADDR to reply packet.
/// @param renew client's message asking for renew
/// @param reply server's response
- /// @param fqdn A DHCPv6 Client FQDN %Option generated in the response to the
- /// client's FQDN option.
- void renewLeases(const Pkt6Ptr& renew, Pkt6Ptr& reply,
- const Option6ClientFqdnPtr& fqdn);
+ void renewLeases(const Pkt6Ptr& renew, Pkt6Ptr& reply);
/// @brief Attempts to release received addresses
///
@@ -554,6 +547,48 @@ private:
/// @param errmsg An error message containing a cause of the failure.
static void ifaceMgrSocket6ErrorHandler(const std::string& errmsg);
+ /// @brief Generate FQDN to be sent to a client if none exists.
+ ///
+ /// This function is meant to be called by the functions which process
+ /// client's messages. The function should be called after a function
+ /// which creates FQDN option for the client. This option must exist
+ /// in the answer message specified as an argument. It must also be
+ /// called after functions which assign leases for a client. The
+ /// IA options being a result of lease acquisition must be appended
+ /// to the message specified as a parameter.
+ ///
+ /// If the Client FQDN option being present in the message carries empty
+ /// hostname, this function will attempt to generate hostname from the
+ /// IPv6 address being acquired by the client. The IPv6 address is retrieved
+ /// from the IA_NA option carried in the specified message. If multiple
+ /// addresses are present in the particular IA_NA option or multiple IA_NA
+ /// options exist, the first address found is selected.
+ ///
+ /// The IPv6 address is converted to the hostname using the following
+ /// pattern:
+ /// @code
+ /// prefix-converted-ip-address.domain-name-suffix.
+ /// @endcode
+ /// where:
+ /// - prefix is a configurable prefix string appended to all auto-generated
+ /// hostnames.
+ /// - converted-ip-address is created by replacing all colons from the IPv6
+ /// address with hyphens.
+ /// - domain-name-suffix is a suffix for a domain name that, together with
+ /// the other parts, constitute the fully qualified domain name.
+ ///
+ /// When hostname is successfully generated, it is either used to update
+ /// FQDN-related fields in a lease database or to update the Client FQDN
+ /// option being sent back to the client. The lease database update is
+ /// NOT performed if Advertise message is being processed.
+ ///
+ /// @param answer Message being sent to a client, which may hold IA_NA
+ /// and Client FQDN options to be used to generate name for a client.
+ ///
+ /// @throw isc::Unexpected if specified message is NULL. This is treated
+ /// as a programmatic error.
+ void generateFqdn(const Pkt6Ptr& answer);
+
/// @brief Allocation Engine.
/// Pointer to the allocation engine that we are currently using
/// It must be a pointer, because we will support changing engines
diff --git a/src/bin/dhcp6/tests/Makefile.am b/src/bin/dhcp6/tests/Makefile.am
index f548ec2..92a18cf 100644
--- a/src/bin/dhcp6/tests/Makefile.am
+++ b/src/bin/dhcp6/tests/Makefile.am
@@ -54,7 +54,8 @@ if HAVE_GTEST
# to unexpected errors. For this reason, the --enable-static-link option is
# ignored for unit tests built here.
-lib_LTLIBRARIES = libco1.la libco2.la
+nodistdir=$(abs_top_builddir)/src/bin/dhcp6/tests
+nodist_LTLIBRARIES = libco1.la libco2.la
libco1_la_SOURCES = callout_library_1.cc callout_library_common.h
libco1_la_CXXFLAGS = $(AM_CXXFLAGS)
diff --git a/src/bin/dhcp6/tests/callout_library_common.h b/src/bin/dhcp6/tests/callout_library_common.h
index cbabcda..6db761d 100644
--- a/src/bin/dhcp6/tests/callout_library_common.h
+++ b/src/bin/dhcp6/tests/callout_library_common.h
@@ -34,9 +34,6 @@
#include <fstream>
-using namespace isc::hooks;
-using namespace std;
-
extern "C" {
/// @brief Append digit to marker file
@@ -51,7 +48,7 @@ extern "C" {
int
appendDigit(const char* name) {
// Open the file and check if successful.
- fstream file(name, fstream::out | fstream::app);
+ std::fstream file(name, std::fstream::out | std::fstream::app);
if (!file.good()) {
return (1);
}
@@ -70,7 +67,7 @@ version() {
}
int
-load(LibraryHandle&) {
+load(isc::hooks::LibraryHandle&) {
return (appendDigit(LOAD_MARKER_FILE));
}
diff --git a/src/bin/dhcp6/tests/config_parser_unittest.cc b/src/bin/dhcp6/tests/config_parser_unittest.cc
index 179f460..aa2a9b9 100644
--- a/src/bin/dhcp6/tests/config_parser_unittest.cc
+++ b/src/bin/dhcp6/tests/config_parser_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright (C) 2012-2013 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2012-2014 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
@@ -138,19 +138,19 @@ public:
params["name"] = param_value;
params["space"] = "dhcp6";
params["code"] = "38";
- params["data"] = "AB CDEF0105";
+ params["data"] = "ABCDEF0105";
params["csv-format"] = "False";
} else if (parameter == "space") {
params["name"] = "subscriber-id";
params["space"] = param_value;
params["code"] = "38";
- params["data"] = "AB CDEF0105";
+ params["data"] = "ABCDEF0105";
params["csv-format"] = "False";
} else if (parameter == "code") {
params["name"] = "subscriber-id";
params["space"] = "dhcp6";
params["code"] = param_value;
- params["data"] = "AB CDEF0105";
+ params["data"] = "ABCDEF0105";
params["csv-format"] = "False";
} else if (parameter == "data") {
params["name"] = "subscriber-id";
@@ -162,12 +162,20 @@ public:
params["name"] = "subscriber-id";
params["space"] = "dhcp6";
params["code"] = "38";
- params["data"] = "AB CDEF0105";
+ params["data"] = "ABCDEF0105";
params["csv-format"] = param_value;
}
return (createConfigWithOption(params));
}
+ /// @brief Create simple configuration with single option.
+ ///
+ /// This function creates a configuration for a single option with
+ /// custom values for all parameters that describe the option.
+ ///
+ /// @params params map holding parameters and their values.
+ /// @return configuration string containing custom values of parameters
+ /// describing an option.
std::string createConfigWithOption(const std::map<std::string,
std::string>& params)
{
@@ -176,6 +184,15 @@ public:
"\"preferred-lifetime\": 3000,"
"\"rebind-timer\": 2000, "
"\"renew-timer\": 1000, "
+ "\"option-def\": [ {"
+ " \"name\": \"bool-option\","
+ " \"code\": 1000,"
+ " \"type\": \"boolean\","
+ " \"array\": False,"
+ " \"record-types\": \"\","
+ " \"space\": \"dhcp6\","
+ " \"encapsulate\": \"\""
+ "} ],"
"\"subnet6\": [ { "
" \"pool\": [ \"2001:db8:1::/80\" ],"
" \"subnet\": \"2001:db8:1::/64\", "
@@ -208,6 +225,63 @@ public:
return (stream.str());
}
+ /// @brief Returns an option from the subnet.
+ ///
+ /// This function returns an option from a subnet to which the
+ /// specified subnet address belongs. The option is identified
+ /// by its code.
+ ///
+ /// @param subnet_address Address which belongs to the subnet from
+ /// which the option is to be returned.
+ /// @param option_code Code of the option to be returned.
+ /// @param expected_options_count Expected number of options in
+ /// the particular subnet.
+ ///
+ /// @return Descriptor of the option. If the descriptor holds a
+ /// NULL option pointer, it means that there was no such option
+ /// in the subnet.
+ Subnet::OptionDescriptor
+ getOptionFromSubnet(const IOAddress& subnet_address,
+ const uint16_t option_code,
+ const uint16_t expected_options_count = 1) {
+ Subnet6Ptr subnet = CfgMgr::instance().getSubnet6(subnet_address,
+ classify_);
+ if (!subnet) {
+ /// @todo replace toText() with the use of operator <<.
+ ADD_FAILURE() << "A subnet for the specified address "
+ << subnet_address.toText()
+ << "does not exist in Config Manager";
+ }
+ Subnet::OptionContainerPtr options =
+ subnet->getOptionDescriptors("dhcp6");
+ if (expected_options_count != options->size()) {
+ ADD_FAILURE() << "The number of options in the subnet '"
+ << subnet_address.toText() << "' is different "
+ " than expected number of options '"
+ << expected_options_count << "'";
+ }
+
+ // Get the search index. Index #1 is to search using option code.
+ const Subnet::OptionContainerTypeIndex& idx = options->get<1>();
+
+ // Get the options for specified index. Expecting one option to be
+ // returned but in theory we may have multiple options with the same
+ // code so we get the range.
+ std::pair<Subnet::OptionContainerTypeIndex::const_iterator,
+ Subnet::OptionContainerTypeIndex::const_iterator> range =
+ idx.equal_range(option_code);
+ if (std::distance(range.first, range.second) > 1) {
+ ADD_FAILURE() << "There is more than one option having the"
+ " option code '" << option_code << "' in a subnet '"
+ << subnet_address.toText() << "'. Expected "
+ " at most one option";
+ } else if (std::distance(range.first, range.second) == 0) {
+ return (Subnet::OptionDescriptor(OptionPtr(), false));
+ }
+
+ return (*range.first);
+ }
+
/// @brief Parse and Execute configuration
///
/// Parses a configuration and executes a configuration of the server.
@@ -305,6 +379,24 @@ public:
ASSERT_EQ(1, rcode_);
}
+ /// @brief Test invalid option paramater value.
+ ///
+ /// This test function constructs the simple configuration
+ /// string and injects invalid option configuration into it.
+ /// It expects that parser will fail with provided option code.
+ ///
+ /// @param params Map of parameters defining an option.
+ void
+ testInvalidOptionParam(const std::map<std::string, std::string>& params) {
+ ConstElementPtr x;
+ std::string config = createConfigWithOption(params);
+ ElementPtr json = Element::fromJSON(config);
+ EXPECT_NO_THROW(x = configureDhcp6Server(srv_, json));
+ ASSERT_TRUE(x);
+ comment_ = parseAnswer(rcode_, x);
+ ASSERT_EQ(1, rcode_);
+ }
+
/// @brief Test option against given code and data.
///
/// @param option_desc option descriptor that carries the option to
@@ -346,11 +438,45 @@ public:
expected_data_len));
}
+ /// @brief Test option configuration.
+ ///
+ /// This function creates a configuration for a specified option using
+ /// a map of parameters specified as the argument. The map holds
+ /// name/value pairs which identifies option's configuration parameters:
+ /// - name
+ /// - space
+ /// - code
+ /// - data
+ /// - csv-format.
+ /// This function applies a new server configuration and checks that the
+ /// option being configured is inserted into CfgMgr. The raw contents of
+ /// this option are compared with the binary data specified as expected
+ /// data passed to this function.
+ ///
+ /// @param params Map of parameters defining an option.
+ /// @param option_code Option code.
+ /// @param expected_data Array containing binary data expected to be stored
+ /// in the configured option.
+ /// @param expected_data_len Length of the array holding reference data.
+ void testConfiguration(const std::map<std::string, std::string>& params,
+ const uint16_t option_code,
+ const uint8_t* expected_data,
+ const size_t expected_data_len) {
+ std::string config = createConfigWithOption(params);
+ ASSERT_TRUE(executeConfiguration(config, "parse option configuration"));
+ // The subnet should now hold one option with the specified code.
+ Subnet::OptionDescriptor desc =
+ getOptionFromSubnet(IOAddress("2001:db8:1::5"), option_code);
+ ASSERT_TRUE(desc.option);
+ testOption(desc, option_code, expected_data, expected_data_len);
+ }
+
int rcode_; ///< Return code (see @ref isc::config::parseAnswer)
Dhcpv6Srv srv_; ///< Instance of the Dhcp6Srv used during tests
ConstElementPtr comment_; ///< Comment (see @ref isc::config::parseAnswer)
string valid_iface_; ///< Valid network interface name (present in system)
string bogus_iface_; ///< invalid network interface name (not in system)
+ isc::dhcp::ClientClasses classify_; ///< used in client classification
};
// Goal of this test is a verification if a very simple config update
@@ -430,7 +556,8 @@ TEST_F(Dhcp6ParserTest, subnetGlobalDefaults) {
// Now check if the configuration was indeed handled and we have
// expected pool configured.
- Subnet6Ptr subnet = CfgMgr::instance().getSubnet6(IOAddress("2001:db8:1::5"));
+ Subnet6Ptr subnet = CfgMgr::instance().getSubnet6(IOAddress("2001:db8:1::5"),
+ classify_);
ASSERT_TRUE(subnet);
EXPECT_EQ(1000, subnet->getT1());
EXPECT_EQ(2000, subnet->getT2());
@@ -648,7 +775,8 @@ TEST_F(Dhcp6ParserTest, subnetLocal) {
comment_ = parseAnswer(rcode_, status);
EXPECT_EQ(0, rcode_);
- Subnet6Ptr subnet = CfgMgr::instance().getSubnet6(IOAddress("2001:db8:1::5"));
+ Subnet6Ptr subnet = CfgMgr::instance().getSubnet6(IOAddress("2001:db8:1::5"),
+ classify_);
ASSERT_TRUE(subnet);
EXPECT_EQ(1, subnet->getT1());
EXPECT_EQ(2, subnet->getT2());
@@ -684,7 +812,8 @@ TEST_F(Dhcp6ParserTest, subnetInterface) {
comment_ = parseAnswer(rcode_, status);
EXPECT_EQ(0, rcode_);
- Subnet6Ptr subnet = CfgMgr::instance().getSubnet6(IOAddress("2001:db8:1::5"));
+ Subnet6Ptr subnet = CfgMgr::instance().getSubnet6(IOAddress("2001:db8:1::5"),
+ classify_);
ASSERT_TRUE(subnet);
EXPECT_EQ(valid_iface_, subnet->getIface());
}
@@ -717,7 +846,8 @@ TEST_F(Dhcp6ParserTest, subnetInterfaceBogus) {
comment_ = parseAnswer(rcode_, status);
EXPECT_EQ(1, rcode_);
- Subnet6Ptr subnet = CfgMgr::instance().getSubnet6(IOAddress("2001:db8:1::5"));
+ Subnet6Ptr subnet = CfgMgr::instance().getSubnet6(IOAddress("2001:db8:1::5"),
+ classify_);
EXPECT_FALSE(subnet);
}
@@ -782,13 +912,13 @@ TEST_F(Dhcp6ParserTest, subnetInterfaceId) {
// Try to get a subnet based on bogus interface-id option
OptionBuffer tmp(bogus_interface_id.begin(), bogus_interface_id.end());
OptionPtr ifaceid(new Option(Option::V6, D6O_INTERFACE_ID, tmp));
- Subnet6Ptr subnet = CfgMgr::instance().getSubnet6(ifaceid);
+ Subnet6Ptr subnet = CfgMgr::instance().getSubnet6(ifaceid, classify_);
EXPECT_FALSE(subnet);
// Now try to get subnet for valid interface-id value
tmp = OptionBuffer(valid_interface_id.begin(), valid_interface_id.end());
ifaceid.reset(new Option(Option::V6, D6O_INTERFACE_ID, tmp));
- subnet = CfgMgr::instance().getSubnet6(ifaceid);
+ subnet = CfgMgr::instance().getSubnet6(ifaceid, classify_);
ASSERT_TRUE(subnet);
EXPECT_TRUE(ifaceid->equal(subnet->getInterfaceId()));
}
@@ -901,7 +1031,8 @@ TEST_F(Dhcp6ParserTest, poolPrefixLen) {
comment_ = parseAnswer(rcode_, x);
EXPECT_EQ(0, rcode_);
- Subnet6Ptr subnet = CfgMgr::instance().getSubnet6(IOAddress("2001:db8:1::5"));
+ Subnet6Ptr subnet = CfgMgr::instance().getSubnet6(IOAddress("2001:db8:1::5"),
+ classify_);
ASSERT_TRUE(subnet);
EXPECT_EQ(1000, subnet->getT1());
EXPECT_EQ(2000, subnet->getT2());
@@ -944,9 +1075,8 @@ TEST_F(Dhcp6ParserTest, pdPoolBasics) {
EXPECT_EQ(0, rcode_);
// Test that we can retrieve the subnet.
- Subnet6Ptr subnet = CfgMgr::
- instance().getSubnet6(IOAddress("2001:db8:1::5"));
-
+ Subnet6Ptr subnet = CfgMgr::instance().getSubnet6(IOAddress("2001:db8:1::5"),
+ classify_);
ASSERT_TRUE(subnet);
// Fetch the collection of PD pools. It should have 1 entry.
@@ -1019,8 +1149,8 @@ TEST_F(Dhcp6ParserTest, pdPoolList) {
EXPECT_EQ(0, rcode_);
// Test that we can retrieve the subnet.
- Subnet6Ptr subnet = CfgMgr::
- instance().getSubnet6(IOAddress("2001:db8:1::5"));
+ Subnet6Ptr subnet = CfgMgr::instance().getSubnet6(IOAddress("2001:db8:1::5"),
+ classify_);
ASSERT_TRUE(subnet);
// Fetch the collection of NA pools. It should have 1 entry.
@@ -1077,8 +1207,8 @@ TEST_F(Dhcp6ParserTest, subnetAndPrefixDelegated) {
EXPECT_EQ(0, rcode_);
// Test that we can retrieve the subnet.
- Subnet6Ptr subnet = CfgMgr::
- instance().getSubnet6(IOAddress("2001:db8:1::5"));
+ Subnet6Ptr subnet = CfgMgr::instance().getSubnet6(IOAddress("2001:db8:1::5"),
+ classify_);
ASSERT_TRUE(subnet);
@@ -1622,9 +1752,9 @@ TEST_F(Dhcp6ParserTest, optionDefEncapsulateOwnSpace) {
/// The purpose of this test is to verify that it is not allowed
/// to override the standard option (that belongs to dhcp6 option
-/// space) and that it is allowed to define option in the dhcp6
-/// option space that has a code which is not used by any of the
-/// standard options.
+/// space and has its definition) and that it is allowed to define
+/// option in the dhcp6 option space that has a code which is not
+/// used by any of the standard options.
TEST_F(Dhcp6ParserTest, optionStandardDefOverride) {
// Configuration string. The option code 100 is unassigned
@@ -1662,9 +1792,8 @@ TEST_F(Dhcp6ParserTest, optionStandardDefOverride) {
EXPECT_EQ(OPT_STRING_TYPE, def->getType());
EXPECT_FALSE(def->getArrayType());
- // The combination of option space and code is
- // invalid. The 'dhcp6' option space groups
- // standard options and the code 3 is reserved
+ // The combination of option space and code is invalid. The 'dhcp6'
+ // option space groups standard options and the code 3 is reserved
// for one of them.
config =
"{ \"option-def\": [ {"
@@ -1684,6 +1813,39 @@ TEST_F(Dhcp6ParserTest, optionStandardDefOverride) {
ASSERT_TRUE(status);
// Expecting parsing error (error code 1).
checkResult(status, 1);
+
+ /// @todo The option 59 is a standard DHCPv6 option. However, at this point
+ /// there is no definition for this option in libdhcp++, so it should be
+ /// allowed to define it from the configuration interface. This test will
+ /// have to be removed once definitions for remaining standard options are
+ /// created.
+ config =
+ "{ \"option-def\": [ {"
+ " \"name\": \"boot-file-name\","
+ " \"code\": 59,"
+ " \"type\": \"string\","
+ " \"array\": False,"
+ " \"record-types\": \"\","
+ " \"space\": \"dhcp6\","
+ " \"encapsulate\": \"\""
+ " } ]"
+ "}";
+ json = Element::fromJSON(config);
+
+ // Use the configuration string to create new option definition.
+ EXPECT_NO_THROW(status = configureDhcp6Server(srv_, json));
+ ASSERT_TRUE(status);
+ // Expecting success.
+ checkResult(status, 0);
+
+ def = CfgMgr::instance().getOptionDef("dhcp6", 59);
+ ASSERT_TRUE(def);
+
+ // Check the option data.
+ EXPECT_EQ("boot-file-name", def->getName());
+ EXPECT_EQ(59, def->getCode());
+ EXPECT_EQ(OPT_STRING_TYPE, def->getType());
+ EXPECT_FALSE(def->getArrayType());
}
// Goal of this test is to verify that global option
@@ -1699,7 +1861,7 @@ TEST_F(Dhcp6ParserTest, optionDataDefaults) {
" \"name\": \"subscriber-id\","
" \"space\": \"dhcp6\","
" \"code\": 38,"
- " \"data\": \"AB CDEF0105\","
+ " \"data\": \"ABCDEF0105\","
" \"csv-format\": False"
" },"
" {"
@@ -1722,7 +1884,8 @@ TEST_F(Dhcp6ParserTest, optionDataDefaults) {
comment_ = parseAnswer(rcode_, x);
ASSERT_EQ(0, rcode_);
- Subnet6Ptr subnet = CfgMgr::instance().getSubnet6(IOAddress("2001:db8:1::5"));
+ Subnet6Ptr subnet = CfgMgr::instance().getSubnet6(IOAddress("2001:db8:1::5"),
+ classify_);
ASSERT_TRUE(subnet);
Subnet::OptionContainerPtr options = subnet->getOptionDescriptors("dhcp6");
ASSERT_EQ(2, options->size());
@@ -1780,7 +1943,7 @@ TEST_F(Dhcp6ParserTest, optionDataTwoSpaces) {
" \"name\": \"subscriber-id\","
" \"space\": \"dhcp6\","
" \"code\": 38,"
- " \"data\": \"AB CDEF0105\","
+ " \"data\": \"ABCDEF0105\","
" \"csv-format\": False"
" },"
" {"
@@ -1814,7 +1977,8 @@ TEST_F(Dhcp6ParserTest, optionDataTwoSpaces) {
checkResult(status, 0);
// Options should be now available for the subnet.
- Subnet6Ptr subnet = CfgMgr::instance().getSubnet6(IOAddress("2001:db8:1::5"));
+ Subnet6Ptr subnet = CfgMgr::instance().getSubnet6(IOAddress("2001:db8:1::5"),
+ classify_);
ASSERT_TRUE(subnet);
// Try to get the option from the space dhcp6.
Subnet::OptionDescriptor desc1 = subnet->getOptionDescriptor("dhcp6", 38);
@@ -1965,7 +2129,8 @@ TEST_F(Dhcp6ParserTest, optionDataEncapsulate) {
checkResult(status, 0);
// Get the subnet.
- Subnet6Ptr subnet = CfgMgr::instance().getSubnet6(IOAddress("2001:db8:1::5"));
+ Subnet6Ptr subnet = CfgMgr::instance().getSubnet6(IOAddress("2001:db8:1::5"),
+ classify_);
ASSERT_TRUE(subnet);
// We should have one option available.
@@ -2029,7 +2194,8 @@ TEST_F(Dhcp6ParserTest, optionDataInMultipleSubnets) {
comment_ = parseAnswer(rcode_, x);
ASSERT_EQ(0, rcode_);
- Subnet6Ptr subnet1 = CfgMgr::instance().getSubnet6(IOAddress("2001:db8:1::5"));
+ Subnet6Ptr subnet1 = CfgMgr::instance().getSubnet6(IOAddress("2001:db8:1::5"),
+ classify_);
ASSERT_TRUE(subnet1);
Subnet::OptionContainerPtr options1 = subnet1->getOptionDescriptors("dhcp6");
ASSERT_EQ(1, options1->size());
@@ -2054,7 +2220,8 @@ TEST_F(Dhcp6ParserTest, optionDataInMultipleSubnets) {
sizeof(subid_expected));
// Test another subnet in the same way.
- Subnet6Ptr subnet2 = CfgMgr::instance().getSubnet6(IOAddress("2001:db8:2::4"));
+ Subnet6Ptr subnet2 = CfgMgr::instance().getSubnet6(IOAddress("2001:db8:2::4"),
+ classify_);
ASSERT_TRUE(subnet2);
Subnet::OptionContainerPtr options2 = subnet2->getOptionDescriptors("dhcp6");
ASSERT_EQ(1, options2->size());
@@ -2072,6 +2239,89 @@ TEST_F(Dhcp6ParserTest, optionDataInMultipleSubnets) {
sizeof(user_class_expected));
}
+// The goal of this test is to check that the option carrying a boolean
+// value can be configured using one of the values: "true", "false", "0"
+// or "1".
+TEST_F(Dhcp6ParserTest, optionDataBoolean) {
+ // Create configuration. Use standard option 1000.
+ std::map<std::string, std::string> params;
+ params["name"] = "bool-option";
+ params["space"] = "dhcp6";
+ params["code"] = "1000";
+ params["data"] = "true";
+ params["csv-format"] = "true";
+
+ std::string config = createConfigWithOption(params);
+ ASSERT_TRUE(executeConfiguration(config, "parse configuration with a"
+ " boolean value"));
+
+ // The subnet should now hold one option with the code 1000.
+ Subnet::OptionDescriptor desc =
+ getOptionFromSubnet(IOAddress("2001:db8:1::5"), 1000);
+ ASSERT_TRUE(desc.option);
+
+ // This option should be set to "true", represented as 0x1 in the option
+ // buffer.
+ uint8_t expected_option_data[] = {
+ 0x1
+ };
+ testConfiguration(params, 1000, expected_option_data,
+ sizeof(expected_option_data));
+
+ // Configure the option with the "1" value. This should have the same
+ // effect as if "true" was specified.
+ params["data"] = "1";
+ testConfiguration(params, 1000, expected_option_data,
+ sizeof(expected_option_data));
+
+ // The value of "1" with a few leading zeros should work too.
+ params["data"] = "00001";
+ testConfiguration(params, 1000, expected_option_data,
+ sizeof(expected_option_data));
+
+ // Configure the option with the "false" value.
+ params["data"] = "false";
+ // The option buffer should now hold the value of 0.
+ expected_option_data[0] = 0;
+ testConfiguration(params, 1000, expected_option_data,
+ sizeof(expected_option_data));
+
+ // Specifying "0" should have the same effect as "false".
+ params["data"] = "0";
+ testConfiguration(params, 1000, expected_option_data,
+ sizeof(expected_option_data));
+
+ // The same effect should be for multiple 0 chars.
+ params["data"] = "00000";
+ testConfiguration(params, 1000, expected_option_data,
+ sizeof(expected_option_data));
+
+ // Bogus values should not be accepted.
+ params["data"] = "bugus";
+ testInvalidOptionParam(params);
+
+ params["data"] = "2";
+ testInvalidOptionParam(params);
+
+ // Now let's test that it is possible to use binary format.
+ params["data"] = "0";
+ params["csv-format"] = "false";
+ testConfiguration(params, 1000, expected_option_data,
+ sizeof(expected_option_data));
+
+ // The binary 1 should work as well.
+ params["data"] = "1";
+ expected_option_data[0] = 1;
+ testConfiguration(params, 1000, expected_option_data,
+ sizeof(expected_option_data));
+
+ // As well as an even number of digits.
+ params["data"] = "01";
+ testConfiguration(params, 1000, expected_option_data,
+ sizeof(expected_option_data));
+
+}
+
// Verify that empty option name is rejected in the configuration.
TEST_F(Dhcp6ParserTest, optionNameEmpty) {
// Empty option names not allowed.
@@ -2129,14 +2379,6 @@ TEST_F(Dhcp6ParserTest, optionDataUnexpectedPrefix) {
testInvalidOptionParam("0x0102", "data");
}
-// Verify that option data consisting od an odd number of
-// hexadecimal digits is rejected in the configuration.
-TEST_F(Dhcp6ParserTest, optionDataOddLength) {
- // Option code 0 is reserved and should not be accepted
- // by configuration parser.
- testInvalidOptionParam("123", "data");
-}
-
// Verify that either lower or upper case characters are allowed
// to specify the option data.
TEST_F(Dhcp6ParserTest, optionDataLowerCase) {
@@ -2149,7 +2391,8 @@ TEST_F(Dhcp6ParserTest, optionDataLowerCase) {
comment_ = parseAnswer(rcode_, x);
ASSERT_EQ(0, rcode_);
- Subnet6Ptr subnet = CfgMgr::instance().getSubnet6(IOAddress("2001:db8:1::5"));
+ Subnet6Ptr subnet = CfgMgr::instance().getSubnet6(IOAddress("2001:db8:1::5"),
+ classify_);
ASSERT_TRUE(subnet);
Subnet::OptionContainerPtr options = subnet->getOptionDescriptors("dhcp6");
ASSERT_EQ(1, options->size());
@@ -2193,7 +2436,8 @@ TEST_F(Dhcp6ParserTest, stdOptionData) {
comment_ = parseAnswer(rcode_, x);
ASSERT_EQ(0, rcode_);
- Subnet6Ptr subnet = CfgMgr::instance().getSubnet6(IOAddress("2001:db8:1::5"));
+ Subnet6Ptr subnet = CfgMgr::instance().getSubnet6(IOAddress("2001:db8:1::5"),
+ classify_);
ASSERT_TRUE(subnet);
Subnet::OptionContainerPtr options = subnet->getOptionDescriptors("dhcp6");
ASSERT_EQ(1, options->size());
@@ -2243,7 +2487,7 @@ TEST_F(Dhcp6ParserTest, vendorOptionsHex) {
" \"name\": \"option-one\","
" \"space\": \"vendor-4491\","
" \"code\": 100,"
- " \"data\": \"AB CDEF0105\","
+ " \"data\": \"ABCDEF0105\","
" \"csv-format\": False"
" },"
" {"
@@ -2268,7 +2512,8 @@ TEST_F(Dhcp6ParserTest, vendorOptionsHex) {
checkResult(status, 0);
// Options should be now available for the subnet.
- Subnet6Ptr subnet = CfgMgr::instance().getSubnet6(IOAddress("2001:db8:1::5"));
+ Subnet6Ptr subnet = CfgMgr::instance().getSubnet6(IOAddress("2001:db8:1::5"),
+ classify_);
ASSERT_TRUE(subnet);
// Try to get the option from the vendor space 4491
@@ -2327,7 +2572,8 @@ TEST_F(Dhcp6ParserTest, vendorOptionsCsv) {
checkResult(status, 0);
// Options should be now available for the subnet.
- Subnet6Ptr subnet = CfgMgr::instance().getSubnet6(IOAddress("2001:db8:1::5"));
+ Subnet6Ptr subnet = CfgMgr::instance().getSubnet6(IOAddress("2001:db8:1::5"),
+ classify_);
ASSERT_TRUE(subnet);
// Try to get the option from the vendor space 4491
@@ -2461,7 +2707,8 @@ TEST_F(Dhcp6ParserTest, stdOptionDataEncapsulate) {
checkResult(status, 0);
// Get the subnet.
- Subnet6Ptr subnet = CfgMgr::instance().getSubnet6(IOAddress("2001:db8:1::5"));
+ Subnet6Ptr subnet = CfgMgr::instance().getSubnet6(IOAddress("2001:db8:1::5"),
+ classify_);
ASSERT_TRUE(subnet);
// We should have one option available.
@@ -2725,4 +2972,124 @@ TEST_F(Dhcp6ParserTest, allInterfaces) {
}
+// This test checks if it is possible to specify relay information
+TEST_F(Dhcp6ParserTest, subnetRelayInfo) {
+
+ ConstElementPtr status;
+
+ // A config with relay information.
+ string config = "{ \"interfaces\": [ \"*\" ],"
+ "\"rebind-timer\": 2000, "
+ "\"renew-timer\": 1000, "
+ "\"subnet6\": [ { "
+ " \"pool\": [ \"2001:db8:1::1 - 2001:db8:1::ffff\" ],"
+ " \"relay\": { "
+ " \"ip-address\": \"2001:db8:1::abcd\""
+ " },"
+ " \"subnet\": \"2001:db8:1::/64\" } ],"
+ "\"preferred-lifetime\": 3000, "
+ "\"valid-lifetime\": 4000 }";
+
+ ElementPtr json = Element::fromJSON(config);
+
+ EXPECT_NO_THROW(status = configureDhcp6Server(srv_, json));
+
+ // returned value should be 0 (configuration success)
+ checkResult(status, 0);
+
+ Subnet6Ptr subnet = CfgMgr::instance().getSubnet6(IOAddress("2001:db8:1::1"),
+ classify_);
+ ASSERT_TRUE(subnet);
+ EXPECT_EQ("2001:db8:1::abcd", subnet->getRelayInfo().addr_.toText());
+}
+
+// Goal of this test is to verify that multiple subnets can be configured
+// with defined client classes.
+TEST_F(Dhcp6ParserTest, classifySubnets) {
+ ConstElementPtr x;
+ string config = "{ \"interfaces\": [ \"*\" ],"
+ "\"preferred-lifetime\": 3000,"
+ "\"rebind-timer\": 2000, "
+ "\"renew-timer\": 1000, "
+ "\"subnet6\": [ { "
+ " \"pool\": [ \"2001:db8:1::/80\" ],"
+ " \"subnet\": \"2001:db8:1::/64\", "
+ " \"client-class\": \"alpha\" "
+ " },"
+ " {"
+ " \"pool\": [ \"2001:db8:2::/80\" ],"
+ " \"subnet\": \"2001:db8:2::/64\", "
+ " \"client-class\": \"beta\" "
+ " },"
+ " {"
+ " \"pool\": [ \"2001:db8:3::/80\" ],"
+ " \"subnet\": \"2001:db8:3::/64\", "
+ " \"client-class\": \"gamma\" "
+ " },"
+ " {"
+ " \"pool\": [ \"2001:db8:4::/80\" ],"
+ " \"subnet\": \"2001:db8:4::/64\" "
+ " } ],"
+ "\"valid-lifetime\": 4000 }";
+
+ ElementPtr json = Element::fromJSON(config);
+
+ EXPECT_NO_THROW(x = configureDhcp6Server(srv_, json));
+ ASSERT_TRUE(x);
+ comment_ = parseAnswer(rcode_, x);
+ ASSERT_EQ(0, rcode_);
+
+ const Subnet6Collection* subnets = CfgMgr::instance().getSubnets6();
+ ASSERT_TRUE(subnets);
+ ASSERT_EQ(4, subnets->size()); // We expect 4 subnets
+
+ // Let's check if client belonging to alpha class is supported in subnet[0]
+ // and not supported in any other subnet (except subnet[3], which allows
+ // everyone).
+ ClientClasses classes;
+ classes.insert("alpha");
+ EXPECT_TRUE (subnets->at(0)->clientSupported(classes));
+ EXPECT_FALSE(subnets->at(1)->clientSupported(classes));
+ EXPECT_FALSE(subnets->at(2)->clientSupported(classes));
+ EXPECT_TRUE (subnets->at(3)->clientSupported(classes));
+
+ // Let's check if client belonging to beta class is supported in subnet[1]
+ // and not supported in any other subnet (except subnet[3], which allows
+ // everyone).
+ classes.clear();
+ classes.insert("beta");
+ EXPECT_FALSE(subnets->at(0)->clientSupported(classes));
+ EXPECT_TRUE (subnets->at(1)->clientSupported(classes));
+ EXPECT_FALSE(subnets->at(2)->clientSupported(classes));
+ EXPECT_TRUE (subnets->at(3)->clientSupported(classes));
+
+ // Let's check if client belonging to gamma class is supported in subnet[2]
+ // and not supported in any other subnet (except subnet[3], which allows
+ // everyone).
+ classes.clear();
+ classes.insert("gamma");
+ EXPECT_FALSE(subnets->at(0)->clientSupported(classes));
+ EXPECT_FALSE(subnets->at(1)->clientSupported(classes));
+ EXPECT_TRUE (subnets->at(2)->clientSupported(classes));
+ EXPECT_TRUE (subnets->at(3)->clientSupported(classes));
+
+ // Let's check if client belonging to some other class (not mentioned in
+ // the config) is supported only in subnet[3], which allows everyone.
+ classes.clear();
+ classes.insert("delta");
+ EXPECT_FALSE(subnets->at(0)->clientSupported(classes));
+ EXPECT_FALSE(subnets->at(1)->clientSupported(classes));
+ EXPECT_FALSE(subnets->at(2)->clientSupported(classes));
+ EXPECT_TRUE (subnets->at(3)->clientSupported(classes));
+
+ // Finally, let's check class-less client. He should be allowed only in
+ // the last subnet, which does not have any class restrictions.
+ classes.clear();
+ EXPECT_FALSE(subnets->at(0)->clientSupported(classes));
+ EXPECT_FALSE(subnets->at(1)->clientSupported(classes));
+ EXPECT_FALSE(subnets->at(2)->clientSupported(classes));
+ EXPECT_TRUE (subnets->at(3)->clientSupported(classes));
+}
+
+
};
diff --git a/src/bin/dhcp6/tests/dhcp6_srv_unittest.cc b/src/bin/dhcp6/tests/dhcp6_srv_unittest.cc
index e8f5e79..ea3692e 100644
--- a/src/bin/dhcp6/tests/dhcp6_srv_unittest.cc
+++ b/src/bin/dhcp6/tests/dhcp6_srv_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright (C) 2011-2013 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2011-2014 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
@@ -47,6 +47,8 @@
#include <sstream>
using namespace isc;
+using namespace isc::data;
+using namespace isc::config;
using namespace isc::test;
using namespace isc::asiolink;
using namespace isc::dhcp;
@@ -1073,6 +1075,42 @@ TEST_F(Dhcpv6SrvTest, sanityCheck) {
EXPECT_THROW(srv.sanityCheck(pkt, Dhcpv6Srv::MANDATORY, Dhcpv6Srv::MANDATORY),
RFCViolation);
}
+// Check that the server is testing if server identifier received in the
+// query, matches server identifier used by the server.
+TEST_F(Dhcpv6SrvTest, testServerID) {
+ NakedDhcpv6Srv srv(0);
+
+ Pkt6Ptr req = Pkt6Ptr(new Pkt6(DHCPV6_REQUEST, 1234));
+ std::vector<uint8_t> bin;
+
+ // diud_llt constructed with: time = 0, macaddress = 00:00:00:00:00:00
+ // it's necessary to generate server identifier option
+ isc::util::encode::decodeHex("0001000100000000000000000000", bin);
+ // Now create server identifier option
+ OptionPtr serverid = OptionPtr(new Option(Option::V6, D6O_SERVERID, bin));
+
+ // Server identifier option is MANDATORY in Request message.
+ // Add server identifier option with different value from one that
+ // server is using.
+ req->addOption(serverid);
+
+ // Message shoud be dropped
+ EXPECT_FALSE(srv.testServerID(req));
+
+ // Delete server identifier option and add new one, with same value as
+ // server's server identifier.
+ req->delOption(D6O_SERVERID);
+ req->addOption(srv.getServerID());
+
+ // With proper server identifier we expect true
+ EXPECT_TRUE(srv.testServerID(req));
+
+ // server-id MUST NOT appear in Solicit, so check if server is
+ // not dropping a message without server id.
+ Pkt6Ptr pkt = Pkt6Ptr(new Pkt6(DHCPV6_SOLICIT, 1234));
+
+ EXPECT_TRUE(srv.testServerID(req));
+}
// This test verifies if selectSubnet() selects proper subnet for a given
// source address.
@@ -1702,6 +1740,72 @@ TEST_F(Dhcpv6SrvTest, clientClassification) {
EXPECT_FALSE(sol2->inClass("docsis3.0"));
}
+// Checks if the client-class field is indeed used for subnet selection.
+// Note that packet classification is already checked in Dhcpv6SrvTest
+// .clientClassification above.
+TEST_F(Dhcpv6SrvTest, clientClassify2) {
+
+ NakedDhcpv6Srv srv(0);
+
+ ConstElementPtr status;
+
+ // This test configures 2 subnets. We actually only need the
+ // first one, but since there's still this ugly hack that picks
+ // the pool if there is only one, we must use more than one
+ // subnet. That ugly hack will be removed in #3242, currently
+ // under review.
+
+ // The second subnet does not play any role here. The client's
+ // IP address belongs to the first subnet, so only that first
+ // subnet it being tested.
+ string config = "{ \"interfaces\": [ \"*\" ],"
+ "\"preferred-lifetime\": 3000,"
+ "\"rebind-timer\": 2000, "
+ "\"renew-timer\": 1000, "
+ "\"subnet6\": [ "
+ " { \"pool\": [ \"2001:db8:1::/64\" ],"
+ " \"subnet\": \"2001:db8:1::/48\", "
+ " \"client-class\": \"foo\" "
+ " }, "
+ " { \"pool\": [ \"2001:db8:2::/64\" ],"
+ " \"subnet\": \"2001:db8:2::/48\", "
+ " \"client-class\": \"xyzzy\" "
+ " } "
+ "],"
+ "\"valid-lifetime\": 4000 }";
+
+ ElementPtr json = Element::fromJSON(config);
+
+ EXPECT_NO_THROW(status = configureDhcp6Server(srv, json));
+
+ // check if returned status is OK
+ ASSERT_TRUE(status);
+ comment_ = config::parseAnswer(rcode_, status);
+ ASSERT_EQ(0, rcode_);
+
+ Pkt6Ptr sol = Pkt6Ptr(new Pkt6(DHCPV6_SOLICIT, 1234));
+ sol->setRemoteAddr(IOAddress("2001:db8:1::3"));
+ sol->addOption(generateIA(D6O_IA_NA, 234, 1500, 3000));
+ OptionPtr clientid = generateClientId();
+ sol->addOption(clientid);
+
+ // This discover does not belong to foo class, so it will not
+ // be serviced
+ EXPECT_FALSE(srv.selectSubnet(sol));
+
+ // Let's add the packet to bar class and try again.
+ sol->addClass("bar");
+
+ // Still not supported, because it belongs to wrong class.
+ EXPECT_FALSE(srv.selectSubnet(sol));
+
+ // Let's add it to maching class.
+ sol->addClass("foo");
+
+ // This time it should work
+ EXPECT_TRUE(srv.selectSubnet(sol));
+}
+
/// @todo: Add more negative tests for processX(), e.g. extend sanityCheck() test
/// to call processX() methods.
diff --git a/src/bin/dhcp6/tests/dhcp6_test_utils.cc b/src/bin/dhcp6/tests/dhcp6_test_utils.cc
index 88dee36..75a2ced 100644
--- a/src/bin/dhcp6/tests/dhcp6_test_utils.cc
+++ b/src/bin/dhcp6/tests/dhcp6_test_utils.cc
@@ -15,6 +15,9 @@
#include <gtest/gtest.h>
#include <dhcp6/tests/dhcp6_test_utils.h>
+using namespace isc::dhcp;
+using namespace isc::asiolink;
+
namespace isc {
namespace test {
diff --git a/src/bin/dhcp6/tests/dhcp6_test_utils.h b/src/bin/dhcp6/tests/dhcp6_test_utils.h
index 82ff984..beac1ef 100644
--- a/src/bin/dhcp6/tests/dhcp6_test_utils.h
+++ b/src/bin/dhcp6/tests/dhcp6_test_utils.h
@@ -1,4 +1,4 @@
-// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2013-2014 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,24 +37,16 @@
#include <list>
-using namespace isc::dhcp;
-using namespace isc::config;
-using namespace isc::data;
-using namespace isc::hooks;
-using namespace isc::asiolink;
-using namespace isc::util;
-using namespace isc::hooks;
-
namespace isc {
namespace test {
/// @brief "naked" Dhcpv6Srv class that exposes internal members
class NakedDhcpv6Srv: public isc::dhcp::Dhcpv6Srv {
public:
- NakedDhcpv6Srv(uint16_t port) : Dhcpv6Srv(port) {
+ NakedDhcpv6Srv(uint16_t port) : isc::dhcp::Dhcpv6Srv(port) {
// Open the "memfile" database for leases
std::string memfile = "type=memfile";
- LeaseMgrFactory::create(memfile);
+ isc::dhcp::LeaseMgrFactory::create(memfile);
}
/// @brief fakes packet reception
@@ -70,7 +62,7 @@ public:
// If there is anything prepared as fake incoming
// traffic, use it
if (!fake_received_.empty()) {
- Pkt6Ptr pkt = fake_received_.front();
+ isc::dhcp::Pkt6Ptr pkt = fake_received_.front();
fake_received_.pop_front();
return (pkt);
}
@@ -78,7 +70,7 @@ public:
// If not, just trigger shutdown and
// return immediately
shutdown();
- return (Pkt6Ptr());
+ return (isc::dhcp::Pkt6Ptr());
}
/// @brief fake packet sending
@@ -86,20 +78,20 @@ public:
/// Pretend to send a packet, but instead just store
/// it in fake_send_ list where test can later inspect
/// server's response.
- virtual void sendPacket(const Pkt6Ptr& pkt) {
+ virtual void sendPacket(const isc::dhcp::Pkt6Ptr& pkt) {
fake_sent_.push_back(pkt);
}
/// @brief adds a packet to fake receive queue
///
/// See fake_received_ field for description
- void fakeReceive(const Pkt6Ptr& pkt) {
+ void fakeReceive(const isc::dhcp::Pkt6Ptr& pkt) {
fake_received_.push_back(pkt);
}
virtual ~NakedDhcpv6Srv() {
// Close the lease database
- LeaseMgrFactory::destroy();
+ isc::dhcp::LeaseMgrFactory::destroy();
}
using Dhcpv6Srv::processSolicit;
@@ -111,6 +103,7 @@ public:
using Dhcpv6Srv::createRemovalNameChangeRequest;
using Dhcpv6Srv::createStatusCode;
using Dhcpv6Srv::selectSubnet;
+ using Dhcpv6Srv::testServerID;
using Dhcpv6Srv::sanityCheck;
using Dhcpv6Srv::classifyPacket;
using Dhcpv6Srv::loadServerID;
@@ -120,13 +113,14 @@ public:
/// @brief packets we pretend to receive
///
- /// Instead of setting up sockets on interfaces that change between OSes, it
- /// is much easier to fake packet reception. This is a list of packets that
- /// we pretend to have received. You can schedule new packets to be received
- /// using fakeReceive() and NakedDhcpv6Srv::receivePacket() methods.
- std::list<Pkt6Ptr> fake_received_;
-
- std::list<Pkt6Ptr> fake_sent_;
+ /// Instead of setting up sockets on interfaces that change between
+ /// OSes, it is much easier to fake packet reception. This is a list
+ /// of packets that we pretend to have received. You can schedule
+ /// new packets to be received using fakeReceive() and
+ /// NakedDhcpv6Srv::receivePacket() methods.
+ std::list<isc::dhcp::Pkt6Ptr> fake_received_;
+
+ std::list<isc::dhcp::Pkt6Ptr> fake_sent_;
};
static const char* DUID_FILE = "server-id-test.txt";
@@ -140,7 +134,8 @@ public:
// it's ok if that fails. There should not be such a file anyway
unlink(DUID_FILE);
- const IfaceMgr::IfaceCollection& ifaces = IfaceMgr::instance().getIfaces();
+ const isc::dhcp::IfaceMgr::IfaceCollection& ifaces =
+ isc::dhcp::IfaceMgr::instance().getIfaces();
// There must be some interface detected
if (ifaces.empty()) {
@@ -152,47 +147,56 @@ public:
}
// Generate IA_NA or IA_PD option with specified parameters
- boost::shared_ptr<Option6IA> generateIA(uint16_t type, uint32_t iaid,
- uint32_t t1, uint32_t t2);
+ boost::shared_ptr<isc::dhcp::Option6IA> generateIA
+ (uint16_t type, uint32_t iaid, uint32_t t1, uint32_t t2);
/// @brief generates interface-id option, based on text
///
/// @param iface_id textual representation of the interface-id content
///
/// @return pointer to the option object
- OptionPtr generateInterfaceId(const std::string& iface_id) {
- OptionBuffer tmp(iface_id.begin(), iface_id.end());
- return OptionPtr(new Option(Option::V6, D6O_INTERFACE_ID, tmp));
+ isc::dhcp::OptionPtr generateInterfaceId(const std::string& iface_id) {
+ isc::dhcp::OptionBuffer tmp(iface_id.begin(), iface_id.end());
+ return (isc::dhcp::OptionPtr
+ (new isc::dhcp::Option(isc::dhcp::Option::V6,
+ D6O_INTERFACE_ID, tmp)));
}
// Generate client-id option
- OptionPtr generateClientId(size_t duid_size = 32) {
+ isc::dhcp::OptionPtr generateClientId(size_t duid_size = 32) {
- OptionBuffer clnt_duid(duid_size);
+ isc::dhcp::OptionBuffer clnt_duid(duid_size);
for (int i = 0; i < duid_size; i++) {
clnt_duid[i] = 100 + i;
}
- duid_ = DuidPtr(new DUID(clnt_duid));
+ duid_ = isc::dhcp::DuidPtr(new isc::dhcp::DUID(clnt_duid));
- return (OptionPtr(new Option(Option::V6, D6O_CLIENTID,
- clnt_duid.begin(),
- clnt_duid.begin() + duid_size)));
+ return (isc::dhcp::OptionPtr
+ (new isc::dhcp::Option(isc::dhcp::Option::V6, D6O_CLIENTID,
+ clnt_duid.begin(),
+ clnt_duid.begin() + duid_size)));
}
- // Checks if server response (ADVERTISE or REPLY) includes proper server-id.
- void checkServerId(const Pkt6Ptr& rsp, const OptionPtr& expected_srvid) {
+ // Checks if server response (ADVERTISE or REPLY) includes proper
+ // server-id.
+ void checkServerId(const isc::dhcp::Pkt6Ptr& rsp,
+ const isc::dhcp::OptionPtr& expected_srvid)
+ {
// check that server included its server-id
- OptionPtr tmp = rsp->getOption(D6O_SERVERID);
+ isc::dhcp::OptionPtr tmp = rsp->getOption(D6O_SERVERID);
EXPECT_EQ(tmp->getType(), expected_srvid->getType() );
ASSERT_EQ(tmp->len(), expected_srvid->len() );
EXPECT_TRUE(tmp->getData() == expected_srvid->getData());
}
- // Checks if server response (ADVERTISE or REPLY) includes proper client-id.
- void checkClientId(const Pkt6Ptr& rsp, const OptionPtr& expected_clientid) {
+ // Checks if server response (ADVERTISE or REPLY) includes proper
+ // client-id.
+ void checkClientId(const isc::dhcp::Pkt6Ptr& rsp,
+ const isc::dhcp::OptionPtr& expected_clientid)
+ {
// check that server included our own client-id
- OptionPtr tmp = rsp->getOption(D6O_CLIENTID);
+ isc::dhcp::OptionPtr tmp = rsp->getOption(D6O_CLIENTID);
ASSERT_TRUE(tmp);
EXPECT_EQ(expected_clientid->getType(), tmp->getType());
ASSERT_EQ(expected_clientid->len(), tmp->len());
@@ -202,18 +206,21 @@ public:
}
// Checks if server response is a NAK
- void checkNakResponse(const Pkt6Ptr& rsp, uint8_t expected_message_type,
+ void checkNakResponse(const isc::dhcp::Pkt6Ptr& rsp,
+ uint8_t expected_message_type,
uint32_t expected_transid,
- uint16_t expected_status_code) {
+ uint16_t expected_status_code)
+ {
// Check if we get response at all
checkResponse(rsp, expected_message_type, expected_transid);
// Check that IA_NA was returned
- OptionPtr option_ia_na = rsp->getOption(D6O_IA_NA);
+ isc::dhcp::OptionPtr option_ia_na = rsp->getOption(D6O_IA_NA);
ASSERT_TRUE(option_ia_na);
// check that the status is no address available
- boost::shared_ptr<Option6IA> ia = boost::dynamic_pointer_cast<Option6IA>(option_ia_na);
+ boost::shared_ptr<isc::dhcp::Option6IA> ia =
+ boost::dynamic_pointer_cast<isc::dhcp::Option6IA>(option_ia_na);
ASSERT_TRUE(ia);
checkIA_NAStatusCode(ia, expected_status_code);
@@ -227,8 +234,10 @@ public:
// Status code indicates type of error encountered (in theory it can also
// indicate success, but servers typically don't send success status
// as this is the default result and it saves bandwidth)
- void checkIA_NAStatusCode(const boost::shared_ptr<Option6IA>& ia,
- uint16_t expected_status_code) {
+ void checkIA_NAStatusCode
+ (const boost::shared_ptr<isc::dhcp::Option6IA>& ia,
+ uint16_t expected_status_code)
+ {
// Make sure there is no address assigned.
EXPECT_FALSE(ia->getOption(D6O_IAADDR));
@@ -236,10 +245,12 @@ public:
EXPECT_EQ(0, ia->getT1());
EXPECT_EQ(0, ia->getT2());
- OptionCustomPtr status =
- boost::dynamic_pointer_cast<OptionCustom>(ia->getOption(D6O_STATUS_CODE));
+ isc::dhcp::OptionCustomPtr status =
+ boost::dynamic_pointer_cast<isc::dhcp::OptionCustom>
+ (ia->getOption(D6O_STATUS_CODE));
- // It is ok to not include status success as this is the default behavior
+ // It is ok to not include status success as this is the default
+ // behavior
if (expected_status_code == STATUS_Success && !status) {
return;
}
@@ -247,35 +258,42 @@ public:
EXPECT_TRUE(status);
if (status) {
- // We don't have dedicated class for status code, so let's just interpret
- // first 2 bytes as status. Remainder of the status code option content is
- // just a text explanation what went wrong.
+ // We don't have dedicated class for status code, so let's
+ // just interpret first 2 bytes as status. Remainder of the
+ // status code option content is just a text explanation
+ // what went wrong.
EXPECT_EQ(static_cast<uint16_t>(expected_status_code),
status->readInteger<uint16_t>(0));
}
}
- void checkMsgStatusCode(const Pkt6Ptr& msg, uint16_t expected_status) {
- OptionCustomPtr status =
- boost::dynamic_pointer_cast<OptionCustom>(msg->getOption(D6O_STATUS_CODE));
+ void checkMsgStatusCode(const isc::dhcp::Pkt6Ptr& msg,
+ uint16_t expected_status)
+ {
+ isc::dhcp::OptionCustomPtr status =
+ boost::dynamic_pointer_cast<isc::dhcp::OptionCustom>
+ (msg->getOption(D6O_STATUS_CODE));
- // It is ok to not include status success as this is the default behavior
+ // It is ok to not include status success as this is the default
+ // behavior
if (expected_status == STATUS_Success && !status) {
return;
}
EXPECT_TRUE(status);
if (status) {
- // We don't have dedicated class for status code, so let's just interpret
- // first 2 bytes as status. Remainder of the status code option content is
- // just a text explanation what went wrong.
+ // We don't have dedicated class for status code, so let's
+ // just interpret first 2 bytes as status. Remainder of the
+ // status code option content is just a text explanation
+ // what went wrong.
EXPECT_EQ(static_cast<uint16_t>(expected_status),
status->readInteger<uint16_t>(0));
}
}
// Basic checks for generated response (message type and transaction-id).
- void checkResponse(const Pkt6Ptr& rsp, uint8_t expected_message_type,
+ void checkResponse(const isc::dhcp::Pkt6Ptr& rsp,
+ uint8_t expected_message_type,
uint32_t expected_transid) {
ASSERT_TRUE(rsp);
EXPECT_EQ(expected_message_type, rsp->getType());
@@ -285,28 +303,27 @@ public:
virtual ~NakedDhcpv6SrvTest() {
// Let's clean up if there is such a file.
unlink(DUID_FILE);
- HooksManager::preCalloutsLibraryHandle().deregisterAllCallouts(
- "buffer6_receive");
- HooksManager::preCalloutsLibraryHandle().deregisterAllCallouts(
- "buffer6_send");
- HooksManager::preCalloutsLibraryHandle().deregisterAllCallouts(
- "lease6_renew");
- HooksManager::preCalloutsLibraryHandle().deregisterAllCallouts(
- "lease6_release");
- HooksManager::preCalloutsLibraryHandle().deregisterAllCallouts(
- "pkt6_receive");
- HooksManager::preCalloutsLibraryHandle().deregisterAllCallouts(
- "pkt6_send");
- HooksManager::preCalloutsLibraryHandle().deregisterAllCallouts(
- "subnet6_select");
-
+ isc::hooks::HooksManager::preCalloutsLibraryHandle()
+ .deregisterAllCallouts("buffer6_receive");
+ isc::hooks::HooksManager::preCalloutsLibraryHandle()
+ .deregisterAllCallouts("buffer6_send");
+ isc::hooks::HooksManager::preCalloutsLibraryHandle()
+ .deregisterAllCallouts("lease6_renew");
+ isc::hooks::HooksManager::preCalloutsLibraryHandle()
+ .deregisterAllCallouts("lease6_release");
+ isc::hooks::HooksManager::preCalloutsLibraryHandle()
+ .deregisterAllCallouts("pkt6_receive");
+ isc::hooks::HooksManager::preCalloutsLibraryHandle()
+ .deregisterAllCallouts("pkt6_send");
+ isc::hooks::HooksManager::preCalloutsLibraryHandle()
+ .deregisterAllCallouts("subnet6_select");
};
// A DUID used in most tests (typically as client-id)
- DuidPtr duid_;
+ isc::dhcp::DuidPtr duid_;
int rcode_;
- ConstElementPtr comment_;
+ isc::data::ConstElementPtr comment_;
// Name of a valid network interface
std::string valid_iface_;
@@ -323,18 +340,23 @@ public:
/// Sets up a single subnet6 with one pool for addresses and second
/// pool for prefixes.
Dhcpv6SrvTest() {
- subnet_ = Subnet6Ptr(new Subnet6(IOAddress("2001:db8:1::"), 48, 1000,
- 2000, 3000, 4000));
- pool_ = Pool6Ptr(new Pool6(Lease::TYPE_NA, IOAddress("2001:db8:1:1::"),
- 64));
+ subnet_ = isc::dhcp::Subnet6Ptr
+ (new isc::dhcp::Subnet6(isc::asiolink::IOAddress("2001:db8:1::"),
+ 48, 1000, 2000, 3000, 4000));
+ pool_ = isc::dhcp::Pool6Ptr
+ (new isc::dhcp::Pool6(isc::dhcp::Lease::TYPE_NA,
+ isc::asiolink::IOAddress("2001:db8:1:1::"),
+ 64));
subnet_->addPool(pool_);
- CfgMgr::instance().deleteSubnets6();
- CfgMgr::instance().addSubnet6(subnet_);
+ isc::dhcp::CfgMgr::instance().deleteSubnets6();
+ isc::dhcp::CfgMgr::instance().addSubnet6(subnet_);
// configure PD pool
- pd_pool_ = Pool6Ptr(new Pool6(Lease::TYPE_PD,
- IOAddress("2001:db8:1:2::"), 64, 80));
+ pd_pool_ = isc::dhcp::Pool6Ptr
+ (new isc::dhcp::Pool6(isc::dhcp::Lease::TYPE_PD,
+ isc::asiolink::IOAddress("2001:db8:1:2::"),
+ 64, 80));
subnet_->addPool(pd_pool_);
}
@@ -342,7 +364,7 @@ public:
///
/// Removes existing configuration.
~Dhcpv6SrvTest() {
- CfgMgr::instance().deleteSubnets6();
+ isc::dhcp::CfgMgr::instance().deleteSubnets6();
};
/// @brief Checks that server response (ADVERTISE or REPLY) contains proper
@@ -353,8 +375,8 @@ public:
/// @param expected_t1 expected T1 value
/// @param expected_t2 expected T2 value
/// @return IAADDR option for easy chaining with checkIAAddr method
- boost::shared_ptr<Option6IAAddr>
- checkIA_NA(const Pkt6Ptr& rsp, uint32_t expected_iaid,
+ boost::shared_ptr<isc::dhcp::Option6IAAddr>
+ checkIA_NA(const isc::dhcp::Pkt6Ptr& rsp, uint32_t expected_iaid,
uint32_t expected_t1, uint32_t expected_t2);
/// @brief Checks that server response (ADVERTISE or REPLY) contains proper
@@ -365,15 +387,15 @@ public:
/// @param expected_t1 expected T1 value
/// @param expected_t2 expected T2 value
/// @return IAPREFIX option for easy chaining with checkIAAddr method
- boost::shared_ptr<Option6IAPrefix>
- checkIA_PD(const Pkt6Ptr& rsp, uint32_t expected_iaid,
+ boost::shared_ptr<isc::dhcp::Option6IAPrefix>
+ checkIA_PD(const isc::dhcp::Pkt6Ptr& rsp, uint32_t expected_iaid,
uint32_t expected_t1, uint32_t expected_t2);
// Check that generated IAADDR option contains expected address
// and lifetime values match the configured subnet
- void checkIAAddr(const boost::shared_ptr<Option6IAAddr>& addr,
- const IOAddress& expected_addr,
- Lease::Type type) {
+ void checkIAAddr(const boost::shared_ptr<isc::dhcp::Option6IAAddr>& addr,
+ const isc::asiolink::IOAddress& expected_addr,
+ isc::dhcp::Lease::Type type) {
// Check that the assigned address is indeed from the configured pool.
// Note that when comparing addresses, we compare the textual
@@ -387,8 +409,9 @@ public:
// Checks if the lease sent to client is present in the database
// and is valid when checked agasint the configured subnet
- Lease6Ptr checkLease(const DuidPtr& duid, const OptionPtr& ia_na,
- boost::shared_ptr<Option6IAAddr> addr);
+ isc::dhcp::Lease6Ptr checkLease
+ (const isc::dhcp::DuidPtr& duid, const isc::dhcp::OptionPtr& ia_na,
+ boost::shared_ptr<isc::dhcp::Option6IAAddr> addr);
/// @brief Verifies received IAPrefix option
///
@@ -399,8 +422,9 @@ public:
/// @param ia_pd IA_PD option that contains the IAPRefix option
/// @param prefix pointer to the IAPREFIX option
/// @return corresponding IPv6 lease (if found)
- Lease6Ptr checkPdLease(const DuidPtr& duid, const OptionPtr& ia_pd,
- boost::shared_ptr<Option6IAPrefix> prefix);
+ isc::dhcp::Lease6Ptr checkPdLease
+ (const isc::dhcp::DuidPtr& duid, const isc::dhcp::OptionPtr& ia_pd,
+ boost::shared_ptr<isc::dhcp::Option6IAPrefix> prefix);
/// @brief Creates a message with specified IA
///
@@ -414,10 +438,10 @@ public:
/// @param prefix_len length of the prefix (used for prefixes only)
/// @param iaid IA identifier (used in IA_XX option)
/// @return created message
- Pkt6Ptr
- createMessage(uint8_t message_type, Lease::Type lease_type,
- const IOAddress& addr, const uint8_t prefix_len,
- uint32_t iaid);
+ isc::dhcp::Pkt6Ptr
+ createMessage(uint8_t message_type, isc::dhcp::Lease::Type lease_type,
+ const isc::asiolink::IOAddress& addr,
+ const uint8_t prefix_len, uint32_t iaid);
/// @brief Performs basic (positive) RENEW test
///
@@ -431,7 +455,8 @@ public:
/// @param renew_addr address being sent in RENEW
/// @param prefix_len length of the prefix (128 for addresses)
void
- testRenewBasic(Lease::Type type, const std::string& existing_addr,
+ testRenewBasic(isc::dhcp::Lease::Type type,
+ const std::string& existing_addr,
const std::string& renew_addr, const uint8_t prefix_len);
/// @brief Performs negative RENEW test
@@ -444,7 +469,8 @@ public:
/// @param type type (TYPE_NA or TYPE_PD)
/// @param addr address being sent in RENEW
void
- testRenewReject(Lease::Type type, const IOAddress& addr);
+ testRenewReject(isc::dhcp::Lease::Type type,
+ const isc::asiolink::IOAddress& addr);
/// @brief Performs basic (positive) RELEASE test
///
@@ -457,44 +483,47 @@ public:
/// @param existing address to be preinserted into the database
/// @param release_addr address being sent in RELEASE
void
- testReleaseBasic(Lease::Type type, const IOAddress& existing,
- const IOAddress& release_addr);
+ testReleaseBasic(isc::dhcp::Lease::Type type,
+ const isc::asiolink::IOAddress& existing,
+ const isc::asiolink::IOAddress& release_addr);
/// @brief Performs negative RELEASE test
///
- /// See releaseReject and pdReleaseReject tests for detailed explanation.
- /// In essence the test attempts to perform couple failed RELEASE scenarios.
+ /// See releaseReject and pdReleaseReject tests for detailed
+ /// explanation. In essence the test attempts to perform couple
+ /// failed RELEASE scenarios.
///
/// This method does not throw, but uses gtest macros to signify failures.
///
/// @param type type (TYPE_NA or TYPE_PD)
/// @param addr address being sent in RELEASE
void
- testReleaseReject(Lease::Type type, const IOAddress& addr);
+ testReleaseReject(isc::dhcp::Lease::Type type,
+ const isc::asiolink::IOAddress& addr);
// see wireshark.cc for descriptions
// The descriptions are too large and too closely related to the
// code, so it is kept in .cc rather than traditionally in .h
- Pkt6Ptr captureSimpleSolicit();
- Pkt6Ptr captureRelayedSolicit();
- Pkt6Ptr captureDocsisRelayedSolicit();
- Pkt6Ptr captureeRouterRelayedSolicit();
+ isc::dhcp::Pkt6Ptr captureSimpleSolicit();
+ isc::dhcp::Pkt6Ptr captureRelayedSolicit();
+ isc::dhcp::Pkt6Ptr captureDocsisRelayedSolicit();
+ isc::dhcp::Pkt6Ptr captureeRouterRelayedSolicit();
/// @brief Auxiliary method that sets Pkt6 fields
///
/// Used to reconstruct captured packets. Sets UDP ports, interface names,
/// and other fields to some believable values.
/// @param pkt packet that will have its fields set
- void captureSetDefaultFields(const Pkt6Ptr& pkt);
+ void captureSetDefaultFields(const isc::dhcp::Pkt6Ptr& pkt);
/// A subnet used in most tests
- Subnet6Ptr subnet_;
+ isc::dhcp::Subnet6Ptr subnet_;
/// A normal, non-temporary pool used in most tests
- Pool6Ptr pool_;
+ isc::dhcp::Pool6Ptr pool_;
/// A prefix pool used in most tests
- Pool6Ptr pd_pool_;
+ isc::dhcp::Pool6Ptr pd_pool_;
};
}; // end of isc::test namespace
diff --git a/src/bin/dhcp6/tests/fqdn_unittest.cc b/src/bin/dhcp6/tests/fqdn_unittest.cc
index 8a2da69..952420d 100644
--- a/src/bin/dhcp6/tests/fqdn_unittest.cc
+++ b/src/bin/dhcp6/tests/fqdn_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2013-2014 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
@@ -227,9 +227,6 @@ public:
/// This function verifies that the FQDN option returned is correct.
///
/// @param msg_type A type of the client's message.
- /// @param use_oro A boolean value which indicates whether the DHCPv6 ORO
- /// option (requesting return of the FQDN option by the server) should be
- /// included in the client's message (if true), or not included (if false).
/// @param in_flags A value of flags field to be set for the FQDN carried
/// in the client's message.
/// @param in_domain_name A domain name to be carried in the client's FQDN
@@ -240,7 +237,6 @@ public:
/// @param exp_domain_name A domain name expected in the FQDN sent by a
/// server.
void testFqdn(const uint16_t msg_type,
- const bool use_oro,
const uint8_t in_flags,
const std::string& in_domain_name,
const Option6ClientFqdn::DomainNameType in_domain_type,
@@ -251,11 +247,14 @@ public:
in_flags,
in_domain_name,
in_domain_type,
- use_oro);
+ true);
ASSERT_TRUE(getClientFqdnOption(question));
- Option6ClientFqdnPtr answ_fqdn;
- ASSERT_NO_THROW(answ_fqdn = srv.processClientFqdn(question));
+ Pkt6Ptr answer(new Pkt6(msg_type == DHCPV6_SOLICIT ? DHCPV6_ADVERTISE :
+ DHCPV6_REPLY, question->getTransid()));
+ ASSERT_NO_THROW(srv.processClientFqdn(question, answer));
+ Option6ClientFqdnPtr answ_fqdn = boost::dynamic_pointer_cast<
+ Option6ClientFqdn>(answer->getOption(D6O_CLIENT_FQDN));
ASSERT_TRUE(answ_fqdn);
const bool flag_n = (exp_flags & Option6ClientFqdn::FLAG_N) != 0;
@@ -267,7 +266,19 @@ public:
EXPECT_EQ(flag_o, answ_fqdn->getFlag(Option6ClientFqdn::FLAG_O));
EXPECT_EQ(exp_domain_name, answ_fqdn->getDomainName());
- EXPECT_EQ(Option6ClientFqdn::FULL, answ_fqdn->getDomainNameType());
+ // If server is configured to generate full FQDN for a client, and/or
+ // client sent empty FQDN the expected result of the processing by
+ // processClientFqdn is an empty, partial FQDN. This is an indication
+ // for the code which performs lease allocation that the FQDN has to
+ // be generated from the lease address.
+ if (exp_domain_name.empty()) {
+ EXPECT_EQ(Option6ClientFqdn::PARTIAL,
+ answ_fqdn->getDomainNameType());
+
+ } else {
+ EXPECT_EQ(Option6ClientFqdn::FULL, answ_fqdn->getDomainNameType());
+
+ }
}
/// @brief Tests that the client's message holding an FQDN is processed
@@ -283,15 +294,19 @@ public:
/// that the server doesn't respond with the FQDN.
void testProcessMessage(const uint8_t msg_type,
const std::string& hostname,
+ const std::string& exp_hostname,
NakedDhcpv6Srv& srv,
const bool include_oro = true) {
// Create a message of a specified type, add server id and
// FQDN option.
OptionPtr srvid = srv.getServerID();
+ // Set the appropriate FQDN type. It must be partial if hostname is
+ // empty.
+ Option6ClientFqdn::DomainNameType fqdn_type = (hostname.empty() ?
+ Option6ClientFqdn::PARTIAL : Option6ClientFqdn::FULL);
+
Pkt6Ptr req = generateMessage(msg_type, Option6ClientFqdn::FLAG_S,
- hostname,
- Option6ClientFqdn::FULL,
- include_oro, srvid);
+ hostname, fqdn_type, include_oro, srvid);
// For different client's message types we have to invoke different
// functions to generate response.
@@ -337,13 +352,15 @@ public:
Lease6Ptr lease =
checkLease(duid_, reply->getOption(D6O_IA_NA), addr);
ASSERT_TRUE(lease);
+ EXPECT_EQ(exp_hostname, lease->hostname_);
}
- if (include_oro) {
- ASSERT_TRUE(reply->getOption(D6O_CLIENT_FQDN));
- } else {
- ASSERT_FALSE(reply->getOption(D6O_CLIENT_FQDN));
- }
+ // The Client FQDN option should be always present in the server's
+ // response, regardless if requested using ORO or not.
+ Option6ClientFqdnPtr fqdn;
+ ASSERT_TRUE(fqdn = boost::dynamic_pointer_cast<
+ Option6ClientFqdn>(reply->getOption(D6O_CLIENT_FQDN)));
+ EXPECT_EQ(exp_hostname, fqdn->getDomainName());
}
/// @brief Verify that NameChangeRequest holds valid values.
@@ -394,7 +411,7 @@ public:
// Test server's response when client requests that server performs AAAA update.
TEST_F(FqdnDhcpv6SrvTest, serverAAAAUpdate) {
- testFqdn(DHCPV6_SOLICIT, true, Option6ClientFqdn::FLAG_S,
+ testFqdn(DHCPV6_SOLICIT, Option6ClientFqdn::FLAG_S,
"myhost.example.com",
Option6ClientFqdn::FULL, Option6ClientFqdn::FLAG_S,
"myhost.example.com.");
@@ -403,7 +420,7 @@ TEST_F(FqdnDhcpv6SrvTest, serverAAAAUpdate) {
// Test server's response when client provides partial domain-name and requests
// that server performs AAAA update.
TEST_F(FqdnDhcpv6SrvTest, serverAAAAUpdatePartialName) {
- testFqdn(DHCPV6_SOLICIT, true, Option6ClientFqdn::FLAG_S, "myhost",
+ testFqdn(DHCPV6_SOLICIT, Option6ClientFqdn::FLAG_S, "myhost",
Option6ClientFqdn::PARTIAL, Option6ClientFqdn::FLAG_S,
"myhost.example.com.");
}
@@ -411,14 +428,13 @@ TEST_F(FqdnDhcpv6SrvTest, serverAAAAUpdatePartialName) {
// Test server's response when client provides empty domain-name and requests
// that server performs AAAA update.
TEST_F(FqdnDhcpv6SrvTest, serverAAAAUpdateNoName) {
- testFqdn(DHCPV6_SOLICIT, true, Option6ClientFqdn::FLAG_S, "",
- Option6ClientFqdn::PARTIAL, Option6ClientFqdn::FLAG_S,
- "myhost.example.com.");
+ testFqdn(DHCPV6_SOLICIT, Option6ClientFqdn::FLAG_S, "",
+ Option6ClientFqdn::PARTIAL, Option6ClientFqdn::FLAG_S, "");
}
// Test server's response when client requests no DNS update.
TEST_F(FqdnDhcpv6SrvTest, noUpdate) {
- testFqdn(DHCPV6_SOLICIT, true, Option6ClientFqdn::FLAG_N,
+ testFqdn(DHCPV6_SOLICIT, Option6ClientFqdn::FLAG_N,
"myhost.example.com",
Option6ClientFqdn::FULL, Option6ClientFqdn::FLAG_N,
"myhost.example.com.");
@@ -427,7 +443,7 @@ TEST_F(FqdnDhcpv6SrvTest, noUpdate) {
// Test server's response when client requests that server delegates the AAAA
// update to the client and this delegation is not allowed.
TEST_F(FqdnDhcpv6SrvTest, clientAAAAUpdateNotAllowed) {
- testFqdn(DHCPV6_SOLICIT, true, 0, "myhost.example.com.",
+ testFqdn(DHCPV6_SOLICIT, 0, "myhost.example.com.",
Option6ClientFqdn::FULL,
Option6ClientFqdn::FLAG_S | Option6ClientFqdn::FLAG_O,
"myhost.example.com.");
@@ -439,10 +455,8 @@ TEST_F(FqdnDhcpv6SrvTest, createNameChangeRequestsNoAnswer) {
NakedDhcpv6Srv srv(0);
Pkt6Ptr answer;
- Option6ClientFqdnPtr fqdn = createClientFqdn(Option6ClientFqdn::FLAG_S,
- "myhost.example.com",
- Option6ClientFqdn::FULL);
- EXPECT_THROW(srv.createNameChangeRequests(answer, fqdn),
+
+ EXPECT_THROW(srv.createNameChangeRequests(answer),
isc::Unexpected);
}
@@ -456,22 +470,21 @@ TEST_F(FqdnDhcpv6SrvTest, createNameChangeRequestsNoDUID) {
Option6ClientFqdnPtr fqdn = createClientFqdn(Option6ClientFqdn::FLAG_S,
"myhost.example.com",
Option6ClientFqdn::FULL);
+ answer->addOption(fqdn);
- EXPECT_THROW(srv.createNameChangeRequests(answer, fqdn),
- isc::Unexpected);
+ EXPECT_THROW(srv.createNameChangeRequests(answer), isc::Unexpected);
}
-// Test no NameChangeRequests are added if FQDN option is NULL.
+// Test no NameChangeRequests if Client FQDN is not added to the server's
+// response.
TEST_F(FqdnDhcpv6SrvTest, createNameChangeRequestsNoFQDN) {
NakedDhcpv6Srv srv(0);
// Create Reply message with Client Id and Server id.
Pkt6Ptr answer = generateMessageWithIds(DHCPV6_REPLY, srv);
- // Pass NULL FQDN option. No NameChangeRequests should be created.
- Option6ClientFqdnPtr fqdn;
- ASSERT_NO_THROW(srv.createNameChangeRequests(answer, fqdn));
+ ASSERT_NO_THROW(srv.createNameChangeRequests(answer));
// There should be no new NameChangeRequests.
EXPECT_TRUE(srv.name_change_reqs_.empty());
@@ -485,20 +498,21 @@ TEST_F(FqdnDhcpv6SrvTest, createNameChangeRequestsNoAddr) {
// Create Reply message with Client Id and Server id.
Pkt6Ptr answer = generateMessageWithIds(DHCPV6_REPLY, srv);
+ // Add Client FQDN option.
Option6ClientFqdnPtr fqdn = createClientFqdn(Option6ClientFqdn::FLAG_S,
"myhost.example.com",
Option6ClientFqdn::FULL);
+ answer->addOption(fqdn);
- ASSERT_NO_THROW(srv.createNameChangeRequests(answer, fqdn));
+ ASSERT_NO_THROW(srv.createNameChangeRequests(answer));
// We didn't add any IAs, so there should be no NameChangeRequests in th
// queue.
ASSERT_TRUE(srv.name_change_reqs_.empty());
}
-// Test that a number of NameChangeRequests is created as a result of
-// processing the answer message which holds 3 IAs and when FQDN is
-// specified.
+// Test that exactly one NameChangeRequest is created as a result of processing
+// the answer message which holds 3 IAs and when FQDN is specified.
TEST_F(FqdnDhcpv6SrvTest, createNameChangeRequests) {
NakedDhcpv6Srv srv(0);
@@ -516,33 +530,19 @@ TEST_F(FqdnDhcpv6SrvTest, createNameChangeRequests) {
Option6ClientFqdnPtr fqdn = createClientFqdn(Option6ClientFqdn::FLAG_S,
"MYHOST.EXAMPLE.COM",
Option6ClientFqdn::FULL);
+ answer->addOption(fqdn);
- // Create NameChangeRequests. Since we have added 3 IAs, it should
- // result in generation of 3 distinct NameChangeRequests.
- ASSERT_NO_THROW(srv.createNameChangeRequests(answer, fqdn));
- ASSERT_EQ(3, srv.name_change_reqs_.size());
-
- // Verify that NameChangeRequests are correct. Each call to the
- // verifyNameChangeRequest will pop verified request from the queue.
+ // Create NameChangeRequest for the first allocated address.
+ ASSERT_NO_THROW(srv.createNameChangeRequests(answer));
+ ASSERT_EQ(1, srv.name_change_reqs_.size());
+ // Verify that NameChangeRequest is correct.
verifyNameChangeRequest(srv, isc::dhcp_ddns::CHG_ADD, true, true,
"2001:db8:1::1",
"000201415AA33D1187D148275136FA30300478"
"FAAAA3EBD29826B5C907B2C9268A6F52",
0, 500);
- verifyNameChangeRequest(srv, isc::dhcp_ddns::CHG_ADD, true, true,
- "2001:db8:1::2",
- "000201415AA33D1187D148275136FA30300478"
- "FAAAA3EBD29826B5C907B2C9268A6F52",
- 0, 500);
-
- verifyNameChangeRequest(srv, isc::dhcp_ddns::CHG_ADD, true, true,
- "2001:db8:1::3",
- "000201415AA33D1187D148275136FA30300478"
- "FAAAA3EBD29826B5C907B2C9268A6F52",
- 0, 500);
-
}
// Test creation of the NameChangeRequest to remove both forward and reverse
@@ -642,7 +642,8 @@ TEST_F(FqdnDhcpv6SrvTest, processSolicit) {
// Create a Solicit message with FQDN option and generate server's
// response using processSolicit function.
- testProcessMessage(DHCPV6_SOLICIT, "myhost.example.com", srv);
+ testProcessMessage(DHCPV6_SOLICIT, "myhost.example.com",
+ "myhost.example.com.", srv);
EXPECT_TRUE(srv.name_change_reqs_.empty());
}
@@ -657,7 +658,8 @@ TEST_F(FqdnDhcpv6SrvTest, processTwoRequests) {
// response using processRequest function. This will result in the
// creation of a new lease and the appropriate NameChangeRequest
// to add both reverse and forward mapping to DNS.
- testProcessMessage(DHCPV6_REQUEST, "myhost.example.com", srv);
+ testProcessMessage(DHCPV6_REQUEST, "myhost.example.com",
+ "myhost.example.com.", srv);
ASSERT_EQ(1, srv.name_change_reqs_.size());
verifyNameChangeRequest(srv, isc::dhcp_ddns::CHG_ADD, true, true,
"2001:db8:1:1::dead:beef",
@@ -672,7 +674,8 @@ TEST_F(FqdnDhcpv6SrvTest, processTwoRequests) {
// entries should be removed and the entries for the new domain-name
// should be added. Therefore, we expect two NameChangeRequests. One to
// remove the existing entries, one to add new entries.
- testProcessMessage(DHCPV6_REQUEST, "otherhost.example.com", srv);
+ testProcessMessage(DHCPV6_REQUEST, "otherhost.example.com",
+ "otherhost.example.com.", srv);
ASSERT_EQ(2, srv.name_change_reqs_.size());
verifyNameChangeRequest(srv, isc::dhcp_ddns::CHG_REMOVE, true, true,
"2001:db8:1:1::dead:beef",
@@ -687,6 +690,38 @@ TEST_F(FqdnDhcpv6SrvTest, processTwoRequests) {
}
+// Test that NameChangeRequest is not generated when Solicit message is sent.
+// The Solicit is here sent after a lease has been allocated for a client.
+// The Solicit conveys a different hostname which would trigger updates to
+// DNS if the Request was sent instead of Soicit. The code should differentiate
+// behavior depending whether Solicit or Request is sent.
+TEST_F(FqdnDhcpv6SrvTest, processRequestSolicit) {
+ NakedDhcpv6Srv srv(0);
+
+ // Create a Request message with FQDN option and generate server's
+ // response using processRequest function. This will result in the
+ // creation of a new lease and the appropriate NameChangeRequest
+ // to add both reverse and forward mapping to DNS.
+ testProcessMessage(DHCPV6_REQUEST, "myhost.example.com",
+ "myhost.example.com.", srv);
+ ASSERT_EQ(1, srv.name_change_reqs_.size());
+ verifyNameChangeRequest(srv, isc::dhcp_ddns::CHG_ADD, true, true,
+ "2001:db8:1:1::dead:beef",
+ "000201415AA33D1187D148275136FA30300478"
+ "FAAAA3EBD29826B5C907B2C9268A6F52",
+ 0, 4000);
+
+ // When the returning client sends Solicit the code should never generate
+ // NameChangeRequest and preserve existing DNS entries for the client.
+ // The NameChangeRequest should only be generated when a client sends
+ // Request or Renew.
+ testProcessMessage(DHCPV6_SOLICIT, "otherhost.example.com",
+ "otherhost.example.com.", srv);
+ ASSERT_TRUE(srv.name_change_reqs_.empty());
+
+}
+
+
// Test that client may send Request followed by the Renew, both holding
// FQDN options, but each option holding different domain-name. The Renew
// should result in generation of the two NameChangeRequests, one to remove
@@ -699,7 +734,8 @@ TEST_F(FqdnDhcpv6SrvTest, processRequestRenew) {
// response using processRequest function. This will result in the
// creation of a new lease and the appropriate NameChangeRequest
// to add both reverse and forward mapping to DNS.
- testProcessMessage(DHCPV6_REQUEST, "myhost.example.com", srv);
+ testProcessMessage(DHCPV6_REQUEST, "myhost.example.com",
+ "myhost.example.com.", srv);
ASSERT_EQ(1, srv.name_change_reqs_.size());
verifyNameChangeRequest(srv, isc::dhcp_ddns::CHG_ADD, true, true,
"2001:db8:1:1::dead:beef",
@@ -714,7 +750,8 @@ TEST_F(FqdnDhcpv6SrvTest, processRequestRenew) {
// entries should be removed and the entries for the new domain-name
// should be added. Therefore, we expect two NameChangeRequests. One to
// remove the existing entries, one to add new entries.
- testProcessMessage(DHCPV6_RENEW, "otherhost.example.com", srv);
+ testProcessMessage(DHCPV6_RENEW, "otherhost.example.com",
+ "otherhost.example.com.", srv);
ASSERT_EQ(2, srv.name_change_reqs_.size());
verifyNameChangeRequest(srv, isc::dhcp_ddns::CHG_REMOVE, true, true,
"2001:db8:1:1::dead:beef",
@@ -736,7 +773,8 @@ TEST_F(FqdnDhcpv6SrvTest, processRequestRelease) {
// response using processRequest function. This will result in the
// creation of a new lease and the appropriate NameChangeRequest
// to add both reverse and forward mapping to DNS.
- testProcessMessage(DHCPV6_REQUEST, "myhost.example.com", srv);
+ testProcessMessage(DHCPV6_REQUEST, "myhost.example.com",
+ "myhost.example.com.", srv);
ASSERT_EQ(1, srv.name_change_reqs_.size());
verifyNameChangeRequest(srv, isc::dhcp_ddns::CHG_ADD, true, true,
"2001:db8:1:1::dead:beef",
@@ -748,7 +786,8 @@ TEST_F(FqdnDhcpv6SrvTest, processRequestRelease) {
// removed and all existing DNS entries for this lease should be
// also removed. Therefore, we expect that single NameChangeRequest to
// remove DNS entries is generated.
- testProcessMessage(DHCPV6_RELEASE, "otherhost.example.com", srv);
+ testProcessMessage(DHCPV6_RELEASE, "otherhost.example.com",
+ "otherhost.example.com.", srv);
ASSERT_EQ(1, srv.name_change_reqs_.size());
verifyNameChangeRequest(srv, isc::dhcp_ddns::CHG_REMOVE, true, true,
"2001:db8:1:1::dead:beef",
@@ -758,15 +797,16 @@ TEST_F(FqdnDhcpv6SrvTest, processRequestRelease) {
}
-// Checks that the server does not include DHCPv6 Client FQDN option in its
-// response when client doesn't include ORO option in the Request.
+// Checks that the server include DHCPv6 Client FQDN option in its
+// response even when client doesn't request this option using ORO.
TEST_F(FqdnDhcpv6SrvTest, processRequestWithoutFqdn) {
NakedDhcpv6Srv srv(0);
// The last parameter disables use of the ORO to request FQDN option
// In this case, we expect that the FQDN option will not be included
// in the server's response. The testProcessMessage will check that.
- testProcessMessage(DHCPV6_REQUEST, "myhost.example.com", srv, false);
+ testProcessMessage(DHCPV6_REQUEST, "myhost.example.com",
+ "myhost.example.com.", srv, false);
ASSERT_EQ(1, srv.name_change_reqs_.size());
verifyNameChangeRequest(srv, isc::dhcp_ddns::CHG_ADD, true, true,
"2001:db8:1:1::dead:beef",
@@ -775,4 +815,92 @@ TEST_F(FqdnDhcpv6SrvTest, processRequestWithoutFqdn) {
0, 4000);
}
+// Checks that FQDN is generated from an ip address, when client sends an empty
+// FQDN.
+TEST_F(FqdnDhcpv6SrvTest, processRequestEmptyFqdn) {
+ NakedDhcpv6Srv srv(0);
+
+ testProcessMessage(DHCPV6_REQUEST, "",
+ "host-2001-db8-1-1--dead-beef.example.com.",
+ srv, false);
+ ASSERT_EQ(1, srv.name_change_reqs_.size());
+ verifyNameChangeRequest(srv, isc::dhcp_ddns::CHG_ADD, true, true,
+ "2001:db8:1:1::dead:beef",
+ "0002018D6874B105A5C92DBBD6E4F6C80A93161"
+ "BC03996F0CD0EB75800DEF997C29961",
+ 0, 4000);
+
+}
+
+// Checks that when the server reuses expired lease, the NameChangeRequest
+// is generated to remove the DNS mapping for the expired lease and second
+// NameChangeRequest to add a DNS mapping for a new lease.
+TEST_F(FqdnDhcpv6SrvTest, processRequestReuseExpiredLease) {
+ // This address will be used throughout the test.
+ IOAddress addr("2001:db8:1:1::dead:beef");
+ // We are going to configure a subnet with a pool that consists of
+ // exactly one address. This address will be handed out to the
+ // client, will get expired and then be reused.
+ CfgMgr::instance().deleteSubnets6();
+ subnet_ = Subnet6Ptr(new Subnet6(IOAddress("2001:db8:1:1::"), 56, 1, 2,
+ 3, 4));
+ pool_ = Pool6Ptr(new Pool6(Lease::TYPE_NA, addr, addr));
+ subnet_->addPool(pool_);
+ CfgMgr::instance().addSubnet6(subnet_);
+
+ // Allocate a lease.
+ NakedDhcpv6Srv srv(0);
+ testProcessMessage(DHCPV6_REQUEST, "myhost.example.com",
+ "myhost.example.com.", srv);
+ // Test that the appropriate NameChangeRequest has been generated.
+ ASSERT_EQ(1, srv.name_change_reqs_.size());
+ verifyNameChangeRequest(srv, isc::dhcp_ddns::CHG_ADD, true, true,
+ "2001:db8:1:1::dead:beef",
+ "000201415AA33D1187D148275136FA30300478"
+ "FAAAA3EBD29826B5C907B2C9268A6F52",
+ 0, 4);
+ // Get the lease acquired and modify it. In particular, expire it.
+ Lease6Ptr lease =
+ LeaseMgrFactory::instance().getLease6(Lease::TYPE_NA, addr);
+ ASSERT_TRUE(lease);
+ // One of the following: IAID, DUID or subnet identifier has to be changed
+ // because otherwise the allocation engine will treat the lease as
+ // being renewed by the same client. If we at least change subnet identifier
+ // the lease will be treated as expired lease to be reused.
+ ++lease->subnet_id_;
+
+ // Move the cllt back in time and make sure that the lease got expired.
+ lease->cltt_ = time(NULL) - 10;
+ lease->valid_lft_ = 5;
+ ASSERT_TRUE(lease->expired());
+ // Change the hostname so as the name change request for removing existing
+ // DNS mapping is generated.
+ lease->hostname_ = "otherhost.example.com.";
+ // Update the lease in the lease database.
+ LeaseMgrFactory::instance().updateLease6(lease);
+
+ // Simulate another lease acquisition. Since, our pool consists of
+ // exactly one address and this address is used by the lease in the
+ // lease database, it is guaranteed that the allocation engine will
+ // reuse this lease.
+ testProcessMessage(DHCPV6_REQUEST, "myhost.example.com.",
+ "myhost.example.com.", srv);
+ ASSERT_EQ(2, srv.name_change_reqs_.size());
+ // The first name change request generated, should remove a DNS
+ // mapping for an expired lease.
+ verifyNameChangeRequest(srv, isc::dhcp_ddns::CHG_REMOVE, true, true,
+ "2001:db8:1:1::dead:beef",
+ "000201D422AA463306223D269B6CB7AFE7AAD2"
+ "65FCEA97F93623019B2E0D14E5323D5A",
+ 0, 5);
+ // The second name change request should add a DNS mapping for
+ // a new lease.
+ verifyNameChangeRequest(srv, isc::dhcp_ddns::CHG_ADD, true, true,
+ "2001:db8:1:1::dead:beef",
+ "000201415AA33D1187D148275136FA30300478"
+ "FAAAA3EBD29826B5C907B2C9268A6F52",
+ 0, 4);
+
+}
+
} // end of anonymous namespace
diff --git a/src/bin/dhcp6/tests/hooks_unittest.cc b/src/bin/dhcp6/tests/hooks_unittest.cc
index d4e8e5e..d13ebb4 100644
--- a/src/bin/dhcp6/tests/hooks_unittest.cc
+++ b/src/bin/dhcp6/tests/hooks_unittest.cc
@@ -36,6 +36,8 @@
#include <sstream>
using namespace isc;
+using namespace isc::data;
+using namespace isc::config;
using namespace isc::test;
using namespace isc::asiolink;
using namespace isc::dhcp;
diff --git a/src/bin/dhcp6/tests/wireshark.cc b/src/bin/dhcp6/tests/wireshark.cc
index cd29096..5185e05 100644
--- a/src/bin/dhcp6/tests/wireshark.cc
+++ b/src/bin/dhcp6/tests/wireshark.cc
@@ -38,6 +38,8 @@
/// (Make sure that the packet is expanded in the view. The text file will
/// contain whatever expansion level you have in the graphical tree.)
+using namespace isc::dhcp;
+using namespace isc::asiolink;
using namespace std;
namespace isc {
diff --git a/src/bin/resolver/resolver.cc b/src/bin/resolver/resolver.cc
index 2ac638f..d98630e 100644
--- a/src/bin/resolver/resolver.cc
+++ b/src/bin/resolver/resolver.cc
@@ -418,7 +418,7 @@ Resolver::processMessage(const IOMessage& io_message,
server->resume(false);
return;
}
- } catch (const Exception& ex) {
+ } catch (const isc::Exception& ex) {
LOG_DEBUG(resolver_logger, RESOLVER_DBG_IO,
RESOLVER_HEADER_PROCESSING_FAILED).arg(ex.what());
server->resume(false);
@@ -436,7 +436,7 @@ Resolver::processMessage(const IOMessage& io_message,
buffer, error.getRcode());
server->resume(true);
return;
- } catch (const Exception& ex) {
+ } catch (const isc::Exception& ex) {
LOG_DEBUG(resolver_logger, RESOLVER_DBG_IO,
RESOLVER_MESSAGE_PROCESSING_FAILED)
.arg(ex.what()).arg(Rcode::SERVFAIL());
diff --git a/src/bin/xfrin/tests/xfrin_test.py b/src/bin/xfrin/tests/xfrin_test.py
index 4158638..1405cd8 100644
--- a/src/bin/xfrin/tests/xfrin_test.py
+++ b/src/bin/xfrin/tests/xfrin_test.py
@@ -373,10 +373,7 @@ class MockXfrinConnection(XfrinConnection):
[resp.add_rrset(Message.SECTION_AUTHORITY, a) for a in authorities]
renderer = MessageRenderer()
- if tsig_ctx is not None:
- resp.to_wire(renderer, tsig_ctx)
- else:
- resp.to_wire(renderer)
+ resp.to_wire(renderer, tsig_ctx)
reply_data = struct.pack('H', socket.htons(renderer.get_length()))
reply_data += renderer.get_data()
diff --git a/src/bin/xfrin/xfrin.py.in b/src/bin/xfrin/xfrin.py.in
index a894d55..b8b457e 100755
--- a/src/bin/xfrin/xfrin.py.in
+++ b/src/bin/xfrin/xfrin.py.in
@@ -727,8 +727,9 @@ class XfrinConnection(asyncore.dispatcher):
self.connect(self._master_addrinfo[2])
return True
except socket.error as e:
- logger.error(XFRIN_CONNECT_MASTER, self.tsig_key_name,
+ logger.error(XFRIN_CONNECT_MASTER,
self._master_addrinfo[2],
+ self._zone_name.to_text(),
str(e))
return False
diff --git a/src/bin/xfrin/xfrin_messages.mes b/src/bin/xfrin/xfrin_messages.mes
index 4383324..6dc719f 100644
--- a/src/bin/xfrin/xfrin_messages.mes
+++ b/src/bin/xfrin/xfrin_messages.mes
@@ -56,9 +56,9 @@ most likely cause is that xfrin the msgq daemon is not running.
There was an error while the given command was being processed. The
error is given in the log message.
-% XFRIN_CONNECT_MASTER (with TSIG %1) error connecting to master at %2: %3
-There was an error opening a connection to the master. The error is
-shown in the log message.
+% XFRIN_CONNECT_MASTER error connecting to master at %1 for %2: %3
+There was an error opening a connection to the master for the specified
+zone. The error is shown in the log message.
% XFRIN_DATASRC_CONFIG_ERROR failed to update data source configuration: %1
Configuration for the global data sources is updated, but the update
diff --git a/src/bin/xfrout/xfrout.py.in b/src/bin/xfrout/xfrout.py.in
index aa84587..0fe8e15 100755
--- a/src/bin/xfrout/xfrout.py.in
+++ b/src/bin/xfrout/xfrout.py.in
@@ -143,11 +143,11 @@ def format_addrinfo(addrinfo):
raise TypeError("addrinfo argument to format_addrinfo() does not "
"appear to be consisting of (family, socktype, (addr, port))")
+# This function is not inlined as it is replaced with a mock function
+# during testing.
def get_rrset_len(rrset):
"""Returns the wire length of the given RRset"""
- bytes = bytearray()
- rrset.to_wire(bytes)
- return len(bytes)
+ return rrset.get_length()
def get_soa_serial(soa_rdata):
'''Extract the serial field of an SOA RDATA and returns it as an Serial object.
@@ -345,12 +345,7 @@ class XfroutSession():
render.set_compress_mode(MessageRenderer.CASE_SENSITIVE)
render.set_length_limit(XFROUT_MAX_MESSAGE_SIZE)
- # XXX Currently, python wrapper doesn't accept 'None' parameter in this case,
- # we should remove the if statement and use a universal interface later.
- if tsig_ctx is not None:
- msg.to_wire(render, tsig_ctx)
- else:
- msg.to_wire(render)
+ msg.to_wire(render, tsig_ctx)
header_len = struct.pack('H', socket.htons(render.get_length()))
self._send_data(sock_fd, header_len)
diff --git a/src/hooks/dhcp/user_chk/Makefile.am b/src/hooks/dhcp/user_chk/Makefile.am
index 6b68f38..a7165d8 100644
--- a/src/hooks/dhcp/user_chk/Makefile.am
+++ b/src/hooks/dhcp/user_chk/Makefile.am
@@ -31,7 +31,8 @@ EXTRA_DIST = libdhcp_user_chk.dox
#CLEANFILES = *.gcno *.gcda user_chk_messages.h user_chk_messages.cc s-messages
CLEANFILES = *.gcno *.gcda
-lib_LTLIBRARIES = libdhcp_user_chk.la
+nodistdir=$(abs_top_builddir)/src/hooks/dhcp/user_chk
+nodist_LTLIBRARIES = libdhcp_user_chk.la
libdhcp_user_chk_la_SOURCES =
libdhcp_user_chk_la_SOURCES += load_unload.cc
libdhcp_user_chk_la_SOURCES += pkt_receive_co.cc
diff --git a/src/hooks/dhcp/user_chk/user_chk.h b/src/hooks/dhcp/user_chk/user_chk.h
index 1bc2fc3..9f40c40 100644
--- a/src/hooks/dhcp/user_chk/user_chk.h
+++ b/src/hooks/dhcp/user_chk/user_chk.h
@@ -18,14 +18,11 @@
#include <fstream>
#include <string>
-using namespace std;
-using namespace user_chk;
-
// The following constants are used throughout the library. They are defined
// in load_unload.cc
/// @brief Pointer to the registry instance.
-extern UserRegistryPtr user_registry;
+extern user_chk::UserRegistryPtr user_registry;
/// @brief Output filestream for recording user check outcomes.
extern std::fstream user_chk_output;
diff --git a/src/hooks/dhcp/user_chk/user_file.cc b/src/hooks/dhcp/user_chk/user_file.cc
index 3ffc99e..45b62fe 100644
--- a/src/hooks/dhcp/user_chk/user_file.cc
+++ b/src/hooks/dhcp/user_chk/user_file.cc
@@ -35,7 +35,7 @@ UserFile::~UserFile(){
void
UserFile::open() {
if (isOpen()) {
- isc_throw (UserFileError, "file is already open");
+ isc_throw(UserFileError, "file is already open");
}
file_.open(fname_.c_str(), std::ifstream::in);
diff --git a/src/hooks/dhcp/user_chk/user_file.h b/src/hooks/dhcp/user_chk/user_file.h
index 873dddf..e394b47 100644
--- a/src/hooks/dhcp/user_chk/user_file.h
+++ b/src/hooks/dhcp/user_chk/user_file.h
@@ -23,17 +23,15 @@
#include <fstream>
#include <string>
-using namespace std;
-
namespace user_chk {
/// @brief Thrown a UserFile encounters an error.
/// Note that it derives from UserDataSourceError to comply with the interface.
class UserFileError : public UserDataSourceError {
public:
- UserFileError(const char* file, size_t line,
- const char* what) :
- UserDataSourceError(file, line, what) { };
+ UserFileError(const char* file, size_t line, const char* what) :
+ UserDataSourceError(file, line, what)
+ {}
};
/// @brief Provides a UserDataSource implementation for JSON text files.
@@ -125,7 +123,7 @@ public:
private:
/// @brief Pathname of the input text file.
- string fname_;
+ std::string fname_;
/// @brief Input file stream.
std::ifstream file_;
diff --git a/src/hooks/dhcp/user_chk/user_registry.h b/src/hooks/dhcp/user_chk/user_registry.h
index 48dc32c..3c8ee4c 100644
--- a/src/hooks/dhcp/user_chk/user_registry.h
+++ b/src/hooks/dhcp/user_chk/user_registry.h
@@ -24,16 +24,14 @@
#include <string>
-using namespace std;
-
namespace user_chk {
/// @brief Thrown UserRegistry encounters an error
class UserRegistryError : public isc::Exception {
public:
- UserRegistryError(const char* file, size_t line,
- const char* what) :
- isc::Exception(file, line, what) { };
+ UserRegistryError(const char* file, size_t line, const char* what) :
+ isc::Exception(file, line, what)
+ {}
};
/// @brief Defines a map of unique Users keyed by UserId.
diff --git a/src/lib/acl/ip_check.cc b/src/lib/acl/ip_check.cc
index 7192064..c1f590f 100644
--- a/src/lib/acl/ip_check.cc
+++ b/src/lib/acl/ip_check.cc
@@ -115,7 +115,7 @@ namespace {
const uint8_t*
getSockAddrData(const struct sockaddr& sa) {
const void* sa_ptr = &sa;
- const void* data_ptr;
+ const void* data_ptr = NULL;
if (sa.sa_family == AF_INET) {
const struct sockaddr_in* sin =
static_cast<const struct sockaddr_in*>(sa_ptr);
diff --git a/src/lib/asiodns/io_fetch.cc b/src/lib/asiodns/io_fetch.cc
index a09d8df..aef7c57 100644
--- a/src/lib/asiodns/io_fetch.cc
+++ b/src/lib/asiodns/io_fetch.cc
@@ -162,7 +162,7 @@ struct IOFetchData {
// we sent.
bool responseOK() {
return (*remote_snd == *remote_rcv && cumulative >= 2 &&
- readUint16(received->getData()) == qid);
+ readUint16(received->getData(), received->getLength()) == qid);
}
};
diff --git a/src/lib/asiodns/tests/io_fetch_unittest.cc b/src/lib/asiodns/tests/io_fetch_unittest.cc
index acb184c..7f79a52 100644
--- a/src/lib/asiodns/tests/io_fetch_unittest.cc
+++ b/src/lib/asiodns/tests/io_fetch_unittest.cc
@@ -280,7 +280,8 @@ public:
cumulative_ += length;
bool complete = false;
if (cumulative_ > 2) {
- uint16_t dns_length = readUint16(receive_buffer_);
+ uint16_t dns_length = readUint16(receive_buffer_,
+ sizeof(receive_buffer_));
complete = ((dns_length + 2) == cumulative_);
}
@@ -310,7 +311,10 @@ public:
send_buffer_.clear();
send_buffer_.push_back(0);
send_buffer_.push_back(0);
- writeUint16(return_data_.size(), &send_buffer_[0]);
+ // send_buffer_.capacity() seems more logical below, but the
+ // code above fills in the first two bytes and size() becomes 2
+ // (sizeof uint16_t).
+ writeUint16(return_data_.size(), &send_buffer_[0], send_buffer_.size());
copy(return_data_.begin(), return_data_.end(), back_inserter(send_buffer_));
if (return_data_.size() >= 2) {
send_buffer_[2] = qid_0;
diff --git a/src/lib/asiolink/dummy_io_cb.h b/src/lib/asiolink/dummy_io_cb.h
index c4644c5..71d3a4c 100644
--- a/src/lib/asiolink/dummy_io_cb.h
+++ b/src/lib/asiolink/dummy_io_cb.h
@@ -17,6 +17,8 @@
#include <iostream>
+#include <exceptions/exceptions.h>
+
#include <asio/error.hpp>
#include <asio/error_code.hpp>
@@ -39,20 +41,30 @@ public:
/// \brief Asynchronous I/O callback method
///
- /// TODO: explain why this method should never be called.
- /// This should be unused.
- void operator()(asio::error_code)
- {
- // TODO: log an error if this method ever gets called.
+ /// Should never be called, as this class is a convenience class provided
+ /// for instances where a socket is required but it is known that no
+ /// asynchronous operations will be carried out.
+ void operator()(asio::error_code) {
+ // If the function is called, there is a serious logic error in
+ // the program (this class should not be used as the callback
+ // class). As the asiolink module is too low-level for logging
+ // errors, throw an exception.
+ isc_throw(isc::Unexpected,
+ "DummyIOCallback::operator() must not be called");
}
/// \brief Asynchronous I/O callback method
///
- /// TODO: explain why this method should never be called.
- /// This should be unused.
- void operator()(asio::error_code, size_t)
- {
- // TODO: log an error if this method ever gets called.
+ /// Should never be called, as this class is a convenience class provided
+ /// for instances where a socket is required but it is known that no
+ /// asynchronous operations will be carried out.
+ void operator()(asio::error_code, size_t) {
+ // If the function is called, there is a serious logic error in
+ // the program (this class should not be used as the callback
+ // class). As the asiolink module is too low-level for logging
+ // errors, throw an exception.
+ isc_throw(isc::Unexpected,
+ "DummyIOCallback::operator() must not be called");
}
};
diff --git a/src/lib/asiolink/io_address.cc b/src/lib/asiolink/io_address.cc
index 7986725..6a6ac18 100644
--- a/src/lib/asiolink/io_address.cc
+++ b/src/lib/asiolink/io_address.cc
@@ -100,14 +100,25 @@ IOAddress::getFamily() const {
}
}
-const asio::ip::address&
-IOAddress::getAddress() const {
- return asio_address_;
+bool
+IOAddress::isV6LinkLocal() const {
+ if (!asio_address_.is_v6()) {
+ return (false);
+ }
+ return (asio_address_.to_v6().is_link_local());
+}
+
+bool
+IOAddress::isV6Multicast() const {
+ if (!asio_address_.is_v6()) {
+ return (false);
+ }
+ return (asio_address_.to_v6().is_multicast());
}
IOAddress::operator uint32_t() const {
- if (getAddress().is_v4()) {
- return (getAddress().to_v4().to_ulong());
+ if (asio_address_.is_v4()) {
+ return (asio_address_.to_v4().to_ulong());
} else {
isc_throw(BadValue, "Can't convert " << toText()
<< " address to IPv4.");
diff --git a/src/lib/asiolink/io_address.h b/src/lib/asiolink/io_address.h
index 8b18bf6..cb898e3 100644
--- a/src/lib/asiolink/io_address.h
+++ b/src/lib/asiolink/io_address.h
@@ -91,14 +91,6 @@ public:
/// \return A string representation of the address.
std::string toText() const;
- /// \brief Returns const reference to the underlying address object.
- ///
- /// This is useful, when access to interface offerted by
- // asio::ip::address_v4 and asio::ip::address_v6 is beneficial.
- ///
- /// \return A const reference to asio::ip::address object
- const asio::ip::address& getAddress() const;
-
/// \brief Returns the address family
///
/// \return AF_INET for IPv4 or AF_INET6 for IPv6.
@@ -118,6 +110,16 @@ public:
return (asio_address_.is_v6());
}
+ /// \brief checks whether and address is IPv6 and is link-local
+ ///
+ /// \return true if the address is IPv6 link-local, false otherwise
+ bool isV6LinkLocal() const;
+
+ /// \brief checks whether and address is IPv6 and is multicast
+ ///
+ /// \return true if the address is IPv6 multicast, false otherwise
+ bool isV6Multicast() const;
+
/// \brief Creates an address from over wire data.
///
/// \param family AF_NET for IPv4 or AF_NET6 for IPv6.
@@ -196,7 +198,7 @@ public:
///
/// \param other Address to compare against.
///
- /// See \ref smaller_than method for details.
+ /// See \ref lessThan method for details.
bool operator<(const IOAddress& other) const {
return (lessThan(other));
}
@@ -205,7 +207,7 @@ public:
///
/// \param other Address to compare against.
///
- /// See \ref smaller_equal method for details.
+ /// See \ref smallerEqual method for details.
bool operator<=(const IOAddress& other) const {
return (smallerEqual(other));
}
diff --git a/src/lib/asiolink/tcp_socket.h b/src/lib/asiolink/tcp_socket.h
index 7df4a80..18eca95 100644
--- a/src/lib/asiolink/tcp_socket.h
+++ b/src/lib/asiolink/tcp_socket.h
@@ -358,7 +358,7 @@ TCPSocket<C>::processReceivedData(const void* staging, size_t length,
}
// Have enough data to interpret the packet count, so do so now.
- expected = isc::util::readUint16(data);
+ expected = isc::util::readUint16(data, cumulative);
// We have two bytes less of data to process. Point to the start of the
// data and adjust the packet size. Note that at this point,
diff --git a/src/lib/asiolink/tests/Makefile.am b/src/lib/asiolink/tests/Makefile.am
index 8525c2a..222ec46 100644
--- a/src/lib/asiolink/tests/Makefile.am
+++ b/src/lib/asiolink/tests/Makefile.am
@@ -35,6 +35,7 @@ run_unittests_SOURCES += udp_endpoint_unittest.cc
run_unittests_SOURCES += udp_socket_unittest.cc
run_unittests_SOURCES += io_service_unittest.cc
run_unittests_SOURCES += local_socket_unittest.cc
+run_unittests_SOURCES += dummy_io_callback_unittest.cc
run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
diff --git a/src/lib/asiolink/tests/dummy_io_callback_unittest.cc b/src/lib/asiolink/tests/dummy_io_callback_unittest.cc
new file mode 100644
index 0000000..3acabfd
--- /dev/null
+++ b/src/lib/asiolink/tests/dummy_io_callback_unittest.cc
@@ -0,0 +1,36 @@
+// Copyright (C) 2014 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 <gtest/gtest.h>
+
+#include <asio.hpp>
+
+#include <asiolink/dummy_io_cb.h>
+
+using namespace isc::asiolink;
+using namespace asio;
+
+namespace { // begin unnamed namespace
+
+TEST(DummyIOCallbackTest, throws) {
+ DummyIOCallback cb;
+ asio::error_code error_code;
+
+ // All methods should throw isc::Unexpected.
+ EXPECT_THROW(cb(error_code), isc::Unexpected);
+ EXPECT_THROW(cb(error_code, 42), isc::Unexpected);
+}
+
+} // end of unnamed namespace
diff --git a/src/lib/asiolink/tests/io_address_unittest.cc b/src/lib/asiolink/tests/io_address_unittest.cc
index 5486d5c..221ed5e 100644
--- a/src/lib/asiolink/tests/io_address_unittest.cc
+++ b/src/lib/asiolink/tests/io_address_unittest.cc
@@ -182,3 +182,38 @@ TEST(IOAddressTest, LeftShiftOperator) {
oss << addr;
EXPECT_EQ(addr.toText(), oss.str());
}
+
+// Tests address classification methods (which were previously used by accessing
+// underlying asio objects directly)
+TEST(IOAddressTest, accessClassificationMethods) {
+ IOAddress addr1("192.0.2.5"); // IPv4
+ IOAddress addr2("::"); // IPv6
+ IOAddress addr3("2001:db8::1"); // global IPv6
+ IOAddress addr4("fe80::1234"); // link-local
+ IOAddress addr5("ff02::1:2"); // multicast
+
+ EXPECT_TRUE (addr1.isV4());
+ EXPECT_FALSE(addr1.isV6());
+ EXPECT_FALSE(addr1.isV6LinkLocal());
+ EXPECT_FALSE(addr1.isV6Multicast());
+
+ EXPECT_FALSE(addr2.isV4());
+ EXPECT_TRUE (addr2.isV6());
+ EXPECT_FALSE(addr2.isV6LinkLocal());
+ EXPECT_FALSE(addr2.isV6Multicast());
+
+ EXPECT_FALSE(addr3.isV4());
+ EXPECT_TRUE (addr3.isV6());
+ EXPECT_FALSE(addr3.isV6LinkLocal());
+ EXPECT_FALSE(addr3.isV6Multicast());
+
+ EXPECT_FALSE(addr4.isV4());
+ EXPECT_TRUE (addr4.isV6());
+ EXPECT_TRUE (addr4.isV6LinkLocal());
+ EXPECT_FALSE(addr4.isV6Multicast());
+
+ EXPECT_FALSE(addr5.isV4());
+ EXPECT_TRUE (addr5.isV6());
+ EXPECT_FALSE(addr5.isV6LinkLocal());
+ EXPECT_TRUE (addr5.isV6Multicast());
+}
diff --git a/src/lib/asiolink/tests/tcp_socket_unittest.cc b/src/lib/asiolink/tests/tcp_socket_unittest.cc
index 9de366c..b0adf2e 100644
--- a/src/lib/asiolink/tests/tcp_socket_unittest.cc
+++ b/src/lib/asiolink/tests/tcp_socket_unittest.cc
@@ -227,7 +227,7 @@ serverRead(tcp::socket& socket, TCPCallback& server_cb) {
// If we have read at least two bytes, we can work out how much we
// should be reading.
if (server_cb.cumulative() >= 2) {
- server_cb.expected() = readUint16(server_cb.data());
+ server_cb.expected() = readUint16(server_cb.data(), server_cb.length());
if ((server_cb.expected() + 2) == server_cb.cumulative()) {
// Amount of data read from socket equals the size of the
@@ -261,7 +261,7 @@ TEST(TCPSocket, processReceivedData) {
}
// Check that the method will handle various receive sizes.
- writeUint16(PACKET_SIZE, inbuff);
+ writeUint16(PACKET_SIZE, inbuff, sizeof(inbuff));
cumulative = 0;
offset = 0;
@@ -317,7 +317,7 @@ TEST(TCPSocket, processReceivedData) {
// Tests the operation of a TCPSocket by opening it, sending an asynchronous
// message to a server, receiving an asynchronous message from the server and
// closing.
-TEST(TCPSocket, SequenceTest) {
+TEST(TCPSocket, sequenceTest) {
// Common objects.
IOService service; // Service object for async control
@@ -408,7 +408,7 @@ TEST(TCPSocket, SequenceTest) {
server_cb.length() = 0;
server_cb.cumulative() = 0;
- writeUint16(sizeof(INBOUND_DATA), server_cb.data());
+ writeUint16(sizeof(INBOUND_DATA), server_cb.data(), TCPCallback::MIN_SIZE);
copy(INBOUND_DATA, (INBOUND_DATA + sizeof(INBOUND_DATA) - 1),
(server_cb.data() + 2));
server_socket.async_send(asio::buffer(server_cb.data(),
diff --git a/src/lib/asiolink/tests/udp_socket_unittest.cc b/src/lib/asiolink/tests/udp_socket_unittest.cc
index 3d91874..be62590 100644
--- a/src/lib/asiolink/tests/udp_socket_unittest.cc
+++ b/src/lib/asiolink/tests/udp_socket_unittest.cc
@@ -188,7 +188,7 @@ TEST(UDPSocket, processReceivedData) {
// two bytes of the buffer.
uint16_t count = 0;
for (uint32_t i = 0; i < (2 << 16); ++i, ++count) {
- writeUint16(count, inbuff);
+ writeUint16(count, inbuff, sizeof(inbuff));
// Set some random values
cumulative = 5;
diff --git a/src/lib/cache/message_cache.h b/src/lib/cache/message_cache.h
index 0c19139..0df23ed 100644
--- a/src/lib/cache/message_cache.h
+++ b/src/lib/cache/message_cache.h
@@ -73,8 +73,8 @@ protected:
/// \param name query name of the message.
/// \param type query type of the message.
/// \return return the hash key.
- HashKey getEntryHashKey(const isc::dns::Name& name,
- const isc::dns::RRType& type) const;
+ isc::nsas::HashKey getEntryHashKey(const isc::dns::Name& name,
+ const isc::dns::RRType& type) const;
// Make these variants be protected for easy unittest.
protected:
@@ -91,4 +91,3 @@ typedef boost::shared_ptr<MessageCache> MessageCachePtr;
} // namespace isc
#endif // MESSAGE_CACHE_H
-
diff --git a/src/lib/cache/message_entry.cc b/src/lib/cache/message_entry.cc
index b6af869..36e7691 100644
--- a/src/lib/cache/message_entry.cc
+++ b/src/lib/cache/message_entry.cc
@@ -23,6 +23,7 @@
#include "logger.h"
using namespace isc::dns;
+using namespace isc::nsas;
using namespace std;
// Put file scope functions in unnamed namespace.
diff --git a/src/lib/cache/message_entry.h b/src/lib/cache/message_entry.h
index 206e601..5b55fbb 100644
--- a/src/lib/cache/message_entry.h
+++ b/src/lib/cache/message_entry.h
@@ -22,8 +22,6 @@
#include "rrset_cache.h"
#include "rrset_entry.h"
-using namespace isc::nsas;
-
namespace isc {
namespace cache {
@@ -33,7 +31,7 @@ class RRsetEntry;
///
/// The object of MessageEntry represents one response message
/// answered to the resolver client.
-class MessageEntry : public NsasEntry<MessageEntry> {
+class MessageEntry : public isc::nsas::NsasEntry<MessageEntry> {
// Noncopyable
private:
MessageEntry(const MessageEntry& source);
@@ -92,7 +90,7 @@ public:
/// \brief Get the hash key of the message entry.
///
/// \return return hash key
- virtual HashKey hashKey() const {
+ virtual isc::nsas::HashKey hashKey() const {
return (*hash_key_ptr_);
}
@@ -173,7 +171,7 @@ protected:
private:
std::string entry_name_; // The name for this entry(name + type)
- HashKey* hash_key_ptr_; // the key for messag entry in hash table.
+ isc::nsas::HashKey* hash_key_ptr_; // the key for messag entry in hash table.
std::vector<RRsetRef> rrsets_;
RRsetCachePtr rrset_cache_; //Normal rrset cache
diff --git a/src/lib/cache/rrset_cache.h b/src/lib/cache/rrset_cache.h
index 304c6e8..8587ec7 100644
--- a/src/lib/cache/rrset_cache.h
+++ b/src/lib/cache/rrset_cache.h
@@ -20,8 +20,6 @@
#include <util/lru_list.h>
-using namespace isc::nsas;
-
namespace isc {
namespace cache {
diff --git a/src/lib/cache/rrset_entry.cc b/src/lib/cache/rrset_entry.cc
index 359fd68..2e5bf93 100644
--- a/src/lib/cache/rrset_entry.cc
+++ b/src/lib/cache/rrset_entry.cc
@@ -21,6 +21,7 @@
#include "rrset_copy.h"
using namespace isc::dns;
+using namespace isc::nsas;
namespace isc {
namespace cache {
diff --git a/src/lib/cache/rrset_entry.h b/src/lib/cache/rrset_entry.h
index 0efda36..71b5e4b 100644
--- a/src/lib/cache/rrset_entry.h
+++ b/src/lib/cache/rrset_entry.h
@@ -22,8 +22,6 @@
#include <nsas/fetchable.h>
#include "cache_entry_key.h"
-using namespace isc::nsas;
-
namespace isc {
namespace cache {
@@ -60,7 +58,7 @@ enum RRsetTrustLevel {
/// The object of RRsetEntry represents one cached RRset.
/// Each RRset entry may be refered using shared_ptr by several message
/// entries.
-class RRsetEntry : public NsasEntry<RRsetEntry>
+class RRsetEntry : public isc::nsas::NsasEntry<RRsetEntry>
{
///
/// \name Constructors and Destructor
@@ -105,7 +103,7 @@ public:
/// \brief Get the hash key
///
/// \return return hash key
- HashKey hashKey() const {
+ isc::nsas::HashKey hashKey() const {
return (hash_key_);
}
@@ -124,7 +122,7 @@ private:
time_t expire_time_; // Expiration time of rrset.
RRsetTrustLevel trust_level_; // RRset trustworthiness.
boost::shared_ptr<isc::dns::RRset> rrset_;
- HashKey hash_key_; // RRsetEntry hash key
+ isc::nsas::HashKey hash_key_; // RRsetEntry hash key
};
typedef boost::shared_ptr<RRsetEntry> RRsetEntryPtr;
diff --git a/src/lib/cache/tests/cache_test_messagefromfile.h b/src/lib/cache/tests/cache_test_messagefromfile.h
index 7d55f08..85bc0d9 100644
--- a/src/lib/cache/tests/cache_test_messagefromfile.h
+++ b/src/lib/cache/tests/cache_test_messagefromfile.h
@@ -17,9 +17,6 @@
#include <util/buffer.h>
#include <dns/message.h>
-using namespace isc;
-using namespace isc::dns;
-
namespace {
/// \brief Reads a Message from a data file
@@ -27,13 +24,12 @@ namespace {
/// \param message Message to put the read data in
/// \param datafile The file to read from
void
-messageFromFile(Message& message, const char* datafile) {
+messageFromFile(isc::dns::Message& message, const char* datafile) {
std::vector<unsigned char> data;
- UnitTestUtil::readWireData(datafile, data);
+ isc::UnitTestUtil::readWireData(datafile, data);
isc::util::InputBuffer buffer(&data[0], data.size());
message.fromWire(buffer);
}
} // namespace
-
diff --git a/src/lib/cache/tests/cache_test_sectioncount.h b/src/lib/cache/tests/cache_test_sectioncount.h
index df7cb52..0dcdaee 100644
--- a/src/lib/cache/tests/cache_test_sectioncount.h
+++ b/src/lib/cache/tests/cache_test_sectioncount.h
@@ -17,9 +17,6 @@
#include <util/buffer.h>
#include <dns/message.h>
-using namespace isc;
-using namespace isc::dns;
-
namespace {
/// \brief Counts the number of rrsets in the given section
@@ -29,9 +26,9 @@ namespace {
///
/// \return The number of RRsets in the given section
int
-sectionRRsetCount(Message& msg, Message::Section section) {
+sectionRRsetCount(isc::dns::Message& msg, isc::dns::Message::Section section) {
int count = 0;
- for (RRsetIterator rrset_iter = msg.beginSection(section);
+ for (isc::dns::RRsetIterator rrset_iter = msg.beginSection(section);
rrset_iter != msg.endSection(section);
++rrset_iter) {
++count;
@@ -41,4 +38,3 @@ sectionRRsetCount(Message& msg, Message::Section section) {
}
} // namespace
-
diff --git a/src/lib/cache/tests/message_cache_unittest.cc b/src/lib/cache/tests/message_cache_unittest.cc
index 60ae037..c26b3cb 100644
--- a/src/lib/cache/tests/message_cache_unittest.cc
+++ b/src/lib/cache/tests/message_cache_unittest.cc
@@ -23,6 +23,7 @@
#include "cache_test_messagefromfile.h"
using namespace isc::cache;
+using namespace isc::nsas;
using namespace isc;
using namespace isc::dns;
using namespace isc::util;
diff --git a/src/lib/cc/session.h b/src/lib/cc/session.h
index 63bb41c..e9a3375 100644
--- a/src/lib/cc/session.h
+++ b/src/lib/cc/session.h
@@ -163,7 +163,7 @@ namespace isc {
/// @brief returns socket descriptor from underlying socket connection
///
- /// @param returns socket descriptor used for session connection
+ /// @return socket descriptor used for session connection
virtual int getSocketDesc() const;
private:
// The following two methods are virtual to allow tests steal and
diff --git a/src/lib/cc/tests/data_unittests.cc b/src/lib/cc/tests/data_unittests.cc
index fa8e5fc..4f1057c 100644
--- a/src/lib/cc/tests/data_unittests.cc
+++ b/src/lib/cc/tests/data_unittests.cc
@@ -84,7 +84,7 @@ TEST(Element, from_and_to_json) {
sv.push_back("\"asdf\"");
sv.push_back("null");
sv.push_back("[ 1, 2, 3, 4 ]");
- sv.push_back("{ \"name\": \"foo\", \"value\": 47806 }");
+ sv.push_back("{ \"name\": \"foo\", \"value\": 56176 }");
sv.push_back("[ { \"a\": 1, \"b\": \"c\" }, { \"a\": 2, \"b\": \"d\" } ]");
sv.push_back("8.23");
sv.push_back("123.456");
@@ -570,12 +570,12 @@ TEST(Element, ListElement) {
el->set(0, Element::fromJSON("\"foo\""));
EXPECT_EQ(el->get(0)->stringValue(), "foo");
- el->add(Element::create(47806));
- EXPECT_EQ(el->get(3)->intValue(), 47806);
+ el->add(Element::create(56176));
+ EXPECT_EQ(el->get(3)->intValue(), 56176);
el->remove(1);
el->remove(1);
- EXPECT_EQ(el->str(), "[ \"foo\", 47806 ]");
+ EXPECT_EQ(el->str(), "[ \"foo\", 56176 ]");
// hmm, it errors on EXPECT_THROW(el->get(3), std::out_of_range)
EXPECT_ANY_THROW(el->get(3));
@@ -600,8 +600,8 @@ TEST(Element, MapElement) {
EXPECT_TRUE(isNull(el->get("value3")));
- el->set("value3", Element::create(47806));
- EXPECT_EQ(el->get("value3")->intValue(), 47806);
+ el->set("value3", Element::create(56176));
+ EXPECT_EQ(el->get("value3")->intValue(), 56176);
el->remove("value3");
EXPECT_TRUE(isNull(el->get("value3")));
diff --git a/src/lib/config/config_data.h b/src/lib/config/config_data.h
index bcc97de..7900aa9 100644
--- a/src/lib/config/config_data.h
+++ b/src/lib/config/config_data.h
@@ -110,8 +110,8 @@ public:
isc::data::ConstElementPtr getItemList(const std::string& identifier = "",
bool recurse = false) const;
- /// Returns a map of the top-level configuration items, as currently
- /// set or their defaults
+ /// \brief Returns a map of the top-level configuration items, as
+ /// currently set or their defaults.
///
/// \return An ElementPtr pointing to a MapElement containing
/// the top-level configuration items
diff --git a/src/lib/config/tests/testdata/spec22.spec b/src/lib/config/tests/testdata/spec22.spec
index cccd77b..687db08 100644
--- a/src/lib/config/tests/testdata/spec22.spec
+++ b/src/lib/config/tests/testdata/spec22.spec
@@ -102,7 +102,7 @@
"item_name": "v92b",
"item_type": "integer",
"item_optional": false,
- "item_default": 47806
+ "item_default": 56176
}
]
}
diff --git a/src/lib/config/tests/testdata/spec27.spec b/src/lib/config/tests/testdata/spec27.spec
index 0b246b9..2c73021 100644
--- a/src/lib/config/tests/testdata/spec27.spec
+++ b/src/lib/config/tests/testdata/spec27.spec
@@ -107,7 +107,7 @@
"item_name": "v92b",
"item_type": "integer",
"item_optional": false,
- "item_default": 47806
+ "item_default": 56176
}
]
}
diff --git a/src/lib/datasrc/cache_config.cc b/src/lib/datasrc/cache_config.cc
index ec3cfeb..9a44900 100644
--- a/src/lib/datasrc/cache_config.cc
+++ b/src/lib/datasrc/cache_config.cc
@@ -75,7 +75,8 @@ CacheConfig::CacheConfig(const std::string& datasrc_type,
if (!enabled_) {
isc_throw(CacheConfigError,
- "The cache must be enabled for the MasterFiles type");
+ "The cache must be enabled for the MasterFiles type: "
+ << datasrc_conf);
}
typedef std::map<std::string, ConstElementPtr> ZoneToFile;
@@ -100,7 +101,8 @@ CacheConfig::CacheConfig(const std::string& datasrc_type,
if (!datasrc_conf.contains("cache-zones")) {
isc_throw(NotImplemented, "Auto-detection of zones "
"to cache is not yet implemented, supply "
- "cache-zones parameter");
+ "cache-zones parameter: "
+ << datasrc_conf);
// TODO: Auto-detect list of all zones in the
// data source.
}
diff --git a/src/lib/datasrc/cache_config.h b/src/lib/datasrc/cache_config.h
index c221066..cfcdc7b 100644
--- a/src/lib/datasrc/cache_config.h
+++ b/src/lib/datasrc/cache_config.h
@@ -170,7 +170,7 @@ public:
/// \param zone_name The origin name of the zone
/// \return A \c LoadAction functor to load zone data or an empty functor
/// (see above).
- memory::LoadAction getLoadAction(const dns::RRClass& rrlcass,
+ memory::LoadAction getLoadAction(const dns::RRClass& rrclass,
const dns::Name& zone_name) const;
/// \brief Read only iterator type over configured cached zones.
diff --git a/src/lib/datasrc/client_list.h b/src/lib/datasrc/client_list.h
index 77c2fd5..31cb6c2 100644
--- a/src/lib/datasrc/client_list.h
+++ b/src/lib/datasrc/client_list.h
@@ -316,7 +316,7 @@ typedef boost::shared_ptr<ClientList> ClientListPtr;
typedef boost::shared_ptr<const ClientList> ConstClientListPtr;
/// \brief Concrete implementation of the ClientList, which is constructed
-/// based on configuration.
+/// based on configuration.
///
/// This is the implementation which is expected to be used in the servers.
/// However, it is expected most of the code will use it as the ClientList,
diff --git a/src/lib/datasrc/database.cc b/src/lib/datasrc/database.cc
index 9c85054..bb929f4 100644
--- a/src/lib/datasrc/database.cc
+++ b/src/lib/datasrc/database.cc
@@ -23,6 +23,7 @@
#include <exceptions/exceptions.h>
#include <dns/name.h>
+#include <dns/labelsequence.h>
#include <dns/rrclass.h>
#include <dns/rrttl.h>
#include <dns/rrset.h>
@@ -103,7 +104,7 @@ DatabaseClient::findZone(const Name& name) const {
}
// Then super domains
// Start from 1, as 0 is covered above
- for (size_t i(1); i < name.getLabelCount(); ++i) {
+ for (size_t i = 1; i < name.getLabelCount(); ++i) {
isc::dns::Name superdomain(name.split(i));
zone = accessor_->getZone(superdomain.toText());
if (zone.first) {
@@ -344,7 +345,7 @@ DatabaseClient::Finder::getRRsets(const string& name, const WantedTypes& types,
}
if (!sig_store.empty()) {
// Add signatures to all found RRsets
- for (std::map<RRType, RRsetPtr>::iterator i(result.begin());
+ for (std::map<RRType, RRsetPtr>::iterator i = result.begin();
i != result.end(); ++ i) {
sig_store.appendSignatures(i->second);
}
@@ -646,7 +647,7 @@ DatabaseClient::Finder::findWildcardMatch(
// Strip off the left-more label(s) in the name and replace with a "*".
const Name superdomain(name.split(i));
- const string wildcard("*." + superdomain.toText());
+ const string wildcard(Name("*", 1, &superdomain).toText());
const string construct_name(name.toText());
// TODO Add a check for DNAME, as DNAME wildcards are discouraged (see
@@ -760,7 +761,7 @@ DatabaseClient::Finder::FindDNSSECContext::probe() {
const string origin = finder_.getOrigin().toText();
const FoundRRsets nsec3_found =
finder_.getRRsets(origin, NSEC3PARAM_TYPES(), true);
- const FoundIterator nfi=
+ const FoundIterator nfi =
nsec3_found.second.find(RRType::NSEC3PARAM());
is_nsec3_ = (nfi != nsec3_found.second.end());
@@ -908,7 +909,7 @@ DatabaseClient::Finder::findOnNameResult(const Name& name,
if (any) {
// An ANY query, copy everything to the target instead of returning
// directly.
- for (FoundIterator it(found.second.begin());
+ for (FoundIterator it = found.second.begin();
it != found.second.end(); ++it) {
if (it->second) {
// Skip over the empty ANY
@@ -1108,12 +1109,14 @@ DatabaseClient::Finder::findNSEC3(const Name& name, bool recursive) {
// This will be set to the one covering the query name
ConstRRsetPtr covering_proof;
+ LabelSequence name_ls(name);
// We keep stripping the leftmost label until we find something.
// In case it is recursive, we'll exit the loop at the first iteration.
- for (unsigned labels(qlabels); labels >= olabels; -- labels) {
- const string hash(calculator->calculate(labels == qlabels ? name :
- name.split(qlabels - labels,
- labels)));
+ for (unsigned int labels = qlabels; labels >= olabels;
+ --labels, name_ls.stripLeft(1))
+ {
+ const std::string hash = calculator->calculate(name_ls);
+
// Get the exact match for the name.
LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_DATABASE_FINDNSEC3_TRYHASH).
arg(name).arg(labels).arg(hash);
diff --git a/src/lib/datasrc/memory/domaintree.h b/src/lib/datasrc/memory/domaintree.h
index 05d9795..f309e4a 100644
--- a/src/lib/datasrc/memory/domaintree.h
+++ b/src/lib/datasrc/memory/domaintree.h
@@ -1206,6 +1206,10 @@ public:
///
/// \param mem_sgmt A \c MemorySegment from which memory for the new
/// \c DomainTree is allocated.
+ /// \param return_empty_node Whether \c find() on this tree should
+ /// return empty nodes (which contain no
+ /// data). If it's \c true, \c find() will
+ /// match and return empty nodes.
static DomainTree* create(util::MemorySegment& mem_sgmt,
bool return_empty_node = false)
{
diff --git a/src/lib/datasrc/memory/memory_client.cc b/src/lib/datasrc/memory/memory_client.cc
index 0b0c827..98fbb57 100644
--- a/src/lib/datasrc/memory/memory_client.cc
+++ b/src/lib/datasrc/memory/memory_client.cc
@@ -90,6 +90,7 @@ private:
ZoneChain chain_;
const RdataSet* set_node_;
const RRClass rrclass_;
+ ConstRRsetPtr soa_;
const ZoneTree& tree_;
const ZoneNode* node_;
// Only used when separate_rrs_ is true
@@ -120,6 +121,18 @@ public:
isc_throw(Unexpected,
"In-memory zone corrupted, missing origin node");
}
+
+ if (node_) {
+ const RdataSet* origin_set = node_->getData();
+ if (origin_set) {
+ const RdataSet* soa_set = RdataSet::find(origin_set, RRType::SOA());
+ if (soa_set) {
+ soa_ = ConstRRsetPtr (new TreeNodeRRset(rrclass_, node_,
+ soa_set, true));
+ }
+ }
+ }
+
// Initialize the iterator if there's somewhere to point to
if (node_ != NULL && node_->getData() != NULL) {
set_node_ = node_->getData();
@@ -231,7 +244,7 @@ public:
}
virtual ConstRRsetPtr getSOA() const {
- isc_throw(NotImplemented, "Not implemented");
+ return (soa_);
}
};
diff --git a/src/lib/datasrc/memory/rdataset.h b/src/lib/datasrc/memory/rdataset.h
index bd93781..a30ec05 100644
--- a/src/lib/datasrc/memory/rdataset.h
+++ b/src/lib/datasrc/memory/rdataset.h
@@ -300,7 +300,7 @@ public:
/// needs to be extended, unless there's a reason other than simply
/// because it's already a member function.
///
- /// \param rdata_head A pointer to \c RdataSet from which the search
+ /// \param rdataset_head A pointer to \c RdataSet from which the search
/// starts. It can be NULL.
/// \param type The RRType of \c RdataSet to find.
/// \param sigonly_ok Whether it should find an RdataSet that only has
diff --git a/src/lib/datasrc/memory/treenode_rrset.cc b/src/lib/datasrc/memory/treenode_rrset.cc
index 4734e04..396bf87 100644
--- a/src/lib/datasrc/memory/treenode_rrset.cc
+++ b/src/lib/datasrc/memory/treenode_rrset.cc
@@ -101,6 +101,19 @@ TreeNodeRRset::toText() const {
namespace {
void
+sizeupName(const LabelSequence& name_labels, RdataNameAttributes,
+ size_t* length)
+{
+ *length += name_labels.getDataLength();
+}
+
+void
+sizeupData(const void*, size_t data_len, size_t* length)
+{
+ *length += data_len;
+}
+
+void
renderName(const LabelSequence& name_labels, RdataNameAttributes attr,
AbstractMessageRenderer* renderer)
{
@@ -114,6 +127,35 @@ renderData(const void* data, size_t data_len,
renderer->writeData(data, data_len);
}
+// Helper for calculating wire data length of a single (etiher main or
+// RRSIG) RRset.
+uint16_t
+getLengthHelper(size_t* rlength, size_t rr_count, uint16_t name_labels_size,
+ RdataReader& reader, bool (RdataReader::* rdata_iterate_fn)())
+{
+ uint16_t length = 0;
+
+ for (size_t i = 0; i < rr_count; ++i) {
+ size_t rrlen = 0;
+
+ rrlen += name_labels_size;
+ rrlen += 2; // TYPE field
+ rrlen += 2; // CLASS field
+ rrlen += 4; // TTL field
+ rrlen += 2; // RDLENGTH field
+
+ *rlength = 0;
+ const bool rendered = (reader.*rdata_iterate_fn)();
+ assert(rendered == true);
+
+ rrlen += *rlength;
+ assert(length + rrlen < 65536);
+ length += rrlen;
+ }
+
+ return (length);
+}
+
// Common code logic for rendering a single (either main or RRSIG) RRset.
size_t
writeRRs(AbstractMessageRenderer& renderer, size_t rr_count,
@@ -149,6 +191,39 @@ writeRRs(AbstractMessageRenderer& renderer, size_t rr_count,
}
}
+uint16_t
+TreeNodeRRset::getLength() const {
+ size_t rlength = 0;
+ RdataReader reader(rrclass_, rdataset_->type, rdataset_->getDataBuf(),
+ rdataset_->getRdataCount(), rrsig_count_,
+ boost::bind(sizeupName, _1, _2, &rlength),
+ boost::bind(sizeupData, _1, _2, &rlength));
+
+ // Get the owner name of the RRset in the form of LabelSequence.
+ uint8_t labels_buf[LabelSequence::MAX_SERIALIZED_LENGTH];
+ const LabelSequence name_labels = getOwnerLabels(labels_buf);
+ const uint16_t name_labels_size = name_labels.getDataLength();
+
+ // Find the length of the main (non RRSIG) RRs
+ const uint16_t rrset_length =
+ getLengthHelper(&rlength, rdataset_->getRdataCount(), name_labels_size,
+ reader, &RdataReader::iterateRdata);
+
+ rlength = 0;
+ const bool rendered = reader.iterateRdata();
+ assert(rendered == false); // we should've reached the end
+
+ // Find the length of any RRSIGs, if we supposed to do so
+ const uint16_t rrsig_length = dnssec_ok_ ?
+ getLengthHelper(&rlength, rrsig_count_, name_labels_size,
+ reader, &RdataReader::iterateSingleSig) : 0;
+
+ // the uint16_ts are promoted to ints during addition below, so it
+ // won't overflow a 16-bit register.
+ assert(rrset_length + rrsig_length < 65536);
+ return (rrset_length + rrsig_length);
+}
+
unsigned int
TreeNodeRRset::toWire(AbstractMessageRenderer& renderer) const {
RdataReader reader(rrclass_, rdataset_->type, rdataset_->getDataBuf(),
@@ -195,6 +270,11 @@ TreeNodeRRset::addRdata(const rdata::Rdata&) {
isc_throw(Unexpected, "unexpected method called on TreeNodeRRset");
}
+void
+TreeNodeRRset::addRdata(const std::string&) {
+ isc_throw(Unexpected, "unexpected method called on TreeNodeRRset");
+}
+
namespace {
// In this namespace we define a set of helper stuff to implement the
// RdataIterator for the TreeNodeRRset. We should eventually optimize
diff --git a/src/lib/datasrc/memory/treenode_rrset.h b/src/lib/datasrc/memory/treenode_rrset.h
index 640c972..47d196e 100644
--- a/src/lib/datasrc/memory/treenode_rrset.h
+++ b/src/lib/datasrc/memory/treenode_rrset.h
@@ -155,7 +155,7 @@ public:
node_(node), rdataset_(rdataset),
rrsig_count_(rdataset_->getSigRdataCount()), rrclass_(rrclass),
dnssec_ok_(dnssec_ok), name_(NULL), realname_(new dns::Name(realname)),
- ttl_data_(rdataset->getTTLData()), ttl_(NULL)
+ ttl_data_(rdataset->getTTLData()), ttl_(NULL)
{}
virtual ~TreeNodeRRset() {
@@ -168,6 +168,8 @@ public:
return (rdataset_->getRdataCount());
}
+ virtual uint16_t getLength() const;
+
virtual const dns::Name& getName() const;
virtual const dns::RRClass& getClass() const {
return (rrclass_);
@@ -204,6 +206,11 @@ public:
/// It throws \c isc::Unexpected unconditionally.
virtual void addRdata(const dns::rdata::Rdata& rdata);
+ /// \brief Specialized version of \c addRdata() for \c TreeNodeRRset.
+ ///
+ /// It throws \c isc::Unexpected unconditionally.
+ virtual void addRdata(const std::string& rdata_str);
+
virtual dns::RdataIteratorPtr getRdataIterator() const;
/// \brief Specialized version of \c getRRsig() for \c TreeNodeRRset.
diff --git a/src/lib/datasrc/memory/zone_data_loader.cc b/src/lib/datasrc/memory/zone_data_loader.cc
index e796dd4..ddf6a19 100644
--- a/src/lib/datasrc/memory/zone_data_loader.cc
+++ b/src/lib/datasrc/memory/zone_data_loader.cc
@@ -69,7 +69,7 @@ typedef boost::function<void(isc::dns::ConstRRsetPtr)> LoadCallback;
class ZoneDataLoader : boost::noncopyable {
public:
ZoneDataLoader(util::MemorySegment& mem_sgmt,
- const isc::dns::RRClass rrclass,
+ const isc::dns::RRClass& rrclass,
const isc::dns::Name& zone_name, ZoneData& zone_data) :
updater_(mem_sgmt, rrclass, zone_name, zone_data)
{}
diff --git a/src/lib/datasrc/memory/zone_data_updater.h b/src/lib/datasrc/memory/zone_data_updater.h
index e8826bd..5a45be4 100644
--- a/src/lib/datasrc/memory/zone_data_updater.h
+++ b/src/lib/datasrc/memory/zone_data_updater.h
@@ -67,7 +67,7 @@ public:
/// on the given memory segment. Currently, at most one zone data
/// updater may exist on the same memory segment.
ZoneDataUpdater(util::MemorySegment& mem_sgmt,
- isc::dns::RRClass rrclass,
+ const isc::dns::RRClass& rrclass,
const isc::dns::Name& zone_name,
ZoneData& zone_data) :
mem_sgmt_(mem_sgmt),
diff --git a/src/lib/datasrc/memory/zone_finder.cc b/src/lib/datasrc/memory/zone_finder.cc
index a6c601b..87d9976 100644
--- a/src/lib/datasrc/memory/zone_finder.cc
+++ b/src/lib/datasrc/memory/zone_finder.cc
@@ -523,21 +523,13 @@ FindNodeResult findNode(const ZoneData& zone_data,
return (FindNodeResult(ZoneFinder::NXDOMAIN, nsec_rrset.first,
nsec_rrset.second));
}
- uint8_t ls_buf[LabelSequence::MAX_SERIALIZED_LENGTH];
-
- // Create the wildcard name (i.e. take "*" and extend it
- // with all node labels down to the wildcard node
- LabelSequence wildcard_ls(LabelSequence::WILDCARD(), ls_buf);
- const ZoneNode* extend_with = node;
- while (extend_with != NULL) {
- wildcard_ls.extend(extend_with->getLabels(), ls_buf);
- extend_with = extend_with->getUpperNode();
- }
- // Clear the node_path so that we don't keep incorrect (NSEC)
- // context
- node_path.clear();
- ZoneTree::Result result = tree.find(wildcard_ls, &node, node_path,
+ // Pass the wildcard label sequence ("*") (which is
+ // non-absolute) and the previous node_path result to this
+ // special shortcut form of find() that searches below from
+ // the node_path.
+ ZoneTree::Result result = tree.find(LabelSequence::WILDCARD(),
+ &node, node_path,
cutCallback, &state);
// Otherwise, why would the domain_flag::WILD be there if
// there was no wildcard under it?
@@ -911,7 +903,7 @@ InMemoryZoneFinder::findNSEC3(const isc::dns::Name& name, bool recursive) {
uint8_t labels_buf[LabelSequence::MAX_SERIALIZED_LENGTH];
const LabelSequence origin_ls(zone_data_.getOriginNode()->
getAbsoluteLabels(labels_buf));
- const LabelSequence name_ls(name);
+ LabelSequence name_ls(name);
if (!zone_data_.isNSEC3Signed()) {
isc_throw(DataSourceError,
@@ -967,10 +959,10 @@ InMemoryZoneFinder::findNSEC3(const isc::dns::Name& name, bool recursive) {
// Examine all names from the query name to the origin name, stripping
// the deepest label one by one, until we find a name that has a matching
// NSEC3 hash.
- for (unsigned int labels = qlabels; labels >= olabels; --labels) {
- const Name& hname = (labels == qlabels ?
- name : name.split(qlabels - labels, labels));
- const std::string hlabel = hash->calculate(hname);
+ for (unsigned int labels = qlabels; labels >= olabels;
+ --labels, name_ls.stripLeft(1))
+ {
+ const std::string hlabel = hash->calculate(name_ls);
LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_MEMORY_FINDNSEC3_TRYHASH).
arg(name).arg(labels).arg(hlabel);
diff --git a/src/lib/datasrc/memory/zone_table.h b/src/lib/datasrc/memory/zone_table.h
index 6bad516..dd1c721 100644
--- a/src/lib/datasrc/memory/zone_table.h
+++ b/src/lib/datasrc/memory/zone_table.h
@@ -146,8 +146,6 @@ public:
///
/// \param mem_sgmt The \c MemorySegment that allocated memory for
/// \c ztable and used for prior calls to \c addZone().
- /// \param zone_class The RR class of the zone. It must be the RR class
- /// that is supposed to be associated to the zone table.
/// \param ztable A non NULL pointer to a valid \c ZoneTable object
/// that was originally created by the \c create() method (the behavior
/// is undefined if this condition isn't met).
diff --git a/src/lib/datasrc/memory/zone_writer_local.h b/src/lib/datasrc/memory/zone_writer_local.h
new file mode 100644
index 0000000..e387433
--- /dev/null
+++ b/src/lib/datasrc/memory/zone_writer_local.h
@@ -0,0 +1,95 @@
+// 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 MEM_ZONE_WRITER_LOCAL_H
+#define MEM_ZONE_WRITER_LOCAL_H
+
+#include "zone_writer.h"
+
+#include <dns/rrclass.h>
+#include <dns/name.h>
+
+namespace isc {
+namespace datasrc {
+namespace memory {
+
+class ZoneData;
+class ZoneTableSegmentLocal;
+
+/// \brief Writer implementation which loads data locally.
+///
+/// This implementation prepares a clean zone data and lets one callback
+/// to fill it and another to install it somewhere. The class does mostly
+/// nothing (and delegates the work to the callbacks), just stores little bit
+/// of state between the calls.
+class ZoneWriterLocal : public ZoneWriter {
+public:
+ /// \brief Constructor
+ ///
+ /// \param segment The zone table segment to store the zone into.
+ /// \param load_action The callback used to load data.
+ /// \param origin The origin of the zone.
+ /// \param rrclass The class of the zone.
+ ZoneWriterLocal(ZoneTableSegmentLocal* segment,
+ const LoadAction& load_action, const dns::Name& origin,
+ const dns::RRClass& rrclass);
+
+ /// \brief Destructor
+ ~ZoneWriterLocal();
+
+ /// \brief Loads the data.
+ ///
+ /// This calls the load_action (passed to constructor) and stores the
+ /// data for future use.
+ ///
+ /// \throw isc::InvalidOperation if it is called the second time in
+ /// lifetime of the object.
+ /// \throw Whatever the load_action throws, it is propagated up.
+ virtual void load();
+
+ /// \brief Installs the zone.
+ ///
+ /// It modifies the zone table accessible through the segment (passed to
+ /// constructor).
+ ///
+ /// \throw isc::InvalidOperation if it is called the second time in
+ /// lifetime of the object or if load() was not called previously or if
+ /// cleanup() was already called.
+ virtual void install();
+
+ /// \brief Clean up memory.
+ ///
+ /// Cleans up the memory used by load()ed zone if not yet installed, or
+ /// the old zone replaced by install().
+ virtual void cleanup();
+private:
+ ZoneTableSegmentLocal* segment_;
+ LoadAction load_action_;
+ dns::Name origin_;
+ dns::RRClass rrclass_;
+ ZoneData* zone_data_;
+ enum State {
+ ZW_UNUSED,
+ ZW_LOADED,
+ ZW_INSTALLED,
+ ZW_CLEANED
+ };
+ State state_;
+};
+
+}
+}
+}
+
+#endif
diff --git a/src/lib/datasrc/sqlite3_accessor.cc b/src/lib/datasrc/sqlite3_accessor.cc
index 8fec5c3..02b55e9 100644
--- a/src/lib/datasrc/sqlite3_accessor.cc
+++ b/src/lib/datasrc/sqlite3_accessor.cc
@@ -904,6 +904,7 @@ public:
/// sequence. Note that because of serial number rollover, it may well
/// be that the start serial number is greater than the end one.
///
+ /// \param accessor The accessor to the database to use to get data.
/// \param zone_id ID of the zone (in the zone table)
/// \param start Serial number of first version in difference sequence
/// \param end Serial number of last version in difference sequence
diff --git a/src/lib/datasrc/tests/client_list_unittest.cc b/src/lib/datasrc/tests/client_list_unittest.cc
index 7de5cda..2fff10f 100644
--- a/src/lib/datasrc/tests/client_list_unittest.cc
+++ b/src/lib/datasrc/tests/client_list_unittest.cc
@@ -142,7 +142,7 @@ public:
" \"noiter.org\", \"null.org\"]"
"}]"))
{
- for (size_t i(0); i < ds_count; ++ i) {
+ for (size_t i = 0; i < ds_count; ++ i) {
boost::shared_ptr<MockDataSourceClient>
ds(new MockDataSourceClient(ds_zones[i]));
ds_.push_back(ds);
@@ -155,12 +155,12 @@ public:
~ListTest() {
ds_info_.clear();
- for (size_t i(0); i < ds_count; ++ i) {
+ for (size_t i = 0; i < ds_count; ++ i) {
ds_[i].reset();
}
ds_.clear();
- for (size_t i(0); i < ds_count; ++ i) {
+ for (size_t i = 0; i < ds_count; ++ i) {
boost::interprocess::file_mapping::remove(
getMappedFilename(i).c_str());
}
@@ -465,7 +465,7 @@ const char* const test_names[] = {
TEST_P(ListTest, multiExactMatch) {
// Run through all the multi-configurations
- for (size_t i(0); i < sizeof(test_names) / sizeof(*test_names); ++i) {
+ for (size_t i = 0; i < sizeof(test_names) / sizeof(*test_names); ++i) {
SCOPED_TRACE(test_names[i]);
multiConfiguration(i);
// Something that is nowhere there
@@ -484,7 +484,7 @@ TEST_P(ListTest, multiExactMatch) {
TEST_P(ListTest, multiBestMatch) {
// Run through all the multi-configurations
- for (size_t i(0); i < 4; ++ i) {
+ for (size_t i = 0; i < 4; ++ i) {
SCOPED_TRACE(test_names[i]);
multiConfiguration(i);
// Something that is nowhere there
@@ -545,7 +545,7 @@ TEST_P(ListTest, configureParams) {
"{}",
NULL
};
- for (const char** param(params); *param; ++param) {
+ for (const char** param = params; *param; ++param) {
SCOPED_TRACE(*param);
ConstElementPtr elem(Element::fromJSON(string("["
"{"
@@ -672,7 +672,7 @@ TEST_P(ListTest, wrongConfig) {
// Put something inside to see it survives the exception
list_->configure(config_elem_, true);
checkDS(0, "test_type", "{}", false);
- for (const char** config(configs); *config; ++config) {
+ for (const char** config = configs; *config; ++config) {
SCOPED_TRACE(*config);
ConstElementPtr elem(Element::fromJSON(*config));
EXPECT_THROW(list_->configure(elem, true),
diff --git a/src/lib/datasrc/tests/database_unittest.cc b/src/lib/datasrc/tests/database_unittest.cc
index e6a27b4..17bf7ba 100644
--- a/src/lib/datasrc/tests/database_unittest.cc
+++ b/src/lib/datasrc/tests/database_unittest.cc
@@ -293,7 +293,7 @@ private:
if (position_ == domain_.end()) {
return (false);
} else {
- for (size_t i(0); i < COLUMN_COUNT; ++ i) {
+ for (size_t i = 0; i < COLUMN_COUNT; ++i) {
columns[i] = (*position_)[i];
}
++ position_;
@@ -1683,7 +1683,7 @@ findTestCommon(ZoneFinder& finder, const isc::dns::Name& name,
const isc::dns::RRType& type,
ConstZoneFinderContextPtr actual_result,
const isc::dns::RRType& expected_type,
- const isc::dns::RRTTL expected_ttl,
+ const isc::dns::RRTTL& expected_ttl,
ZoneFinder::Result expected_result,
const std::vector<string>& expected_rdatas,
const std::vector<string>& expected_sig_rdatas,
@@ -1737,7 +1737,7 @@ doFindTest(ZoneFinder& finder,
const isc::dns::Name& name,
const isc::dns::RRType& type,
const isc::dns::RRType& expected_type,
- const isc::dns::RRTTL expected_ttl,
+ const isc::dns::RRTTL& expected_ttl,
ZoneFinder::Result expected_result,
const std::vector<std::string>& expected_rdatas,
const std::vector<std::string>& expected_sig_rdatas,
@@ -1758,7 +1758,7 @@ doFindAtOriginTest(ZoneFinder& finder,
const isc::dns::Name& origin,
const isc::dns::RRType& type,
const isc::dns::RRType& expected_type,
- const isc::dns::RRTTL expected_ttl,
+ const isc::dns::RRTTL& expected_ttl,
ZoneFinder::Result expected_result,
const std::vector<std::string>& expected_rdatas,
const std::vector<std::string>& expected_sig_rdatas,
@@ -1781,7 +1781,7 @@ doFindAtOriginTest(ZoneFinder& finder,
void
doFindAllTestResult(ZoneFinder& finder, const isc::dns::Name& name,
ZoneFinder::Result expected_result,
- const isc::dns::RRType expected_type,
+ const isc::dns::RRType& expected_type,
std::vector<std::string> expected_rdata,
const isc::dns::Name& expected_name =
isc::dns::Name::ROOT_NAME(),
@@ -1814,7 +1814,7 @@ doFindAllTestResult(ZoneFinder& finder, const isc::dns::Name& name,
std::sort(rdata.begin(), rdata.end());
std::sort(expected_rdata.begin(), expected_rdata.end());
ASSERT_EQ(expected_rdata.size(), rdata.size());
- for (size_t i(0); i < expected_rdata.size(); ++ i) {
+ for (size_t i = 0; i < expected_rdata.size(); ++i) {
EXPECT_EQ(expected_rdata[i], rdata[i]);
}
EXPECT_TRUE(expected_rdata == rdata);
@@ -3023,7 +3023,7 @@ TEST_P(DatabaseClientTest, findRRSIGsWithoutDNSSEC) {
std::sort(rdata.begin(), rdata.end());
std::sort(expected_rdata.begin(), expected_rdata.end());
ASSERT_EQ(expected_rdata.size(), rdata.size());
- for (size_t i(0); i < expected_rdata.size(); ++ i) {
+ for (size_t i = 0; i < expected_rdata.size(); ++i) {
EXPECT_EQ(expected_rdata[i], rdata[i]);
}
}
@@ -3065,7 +3065,7 @@ TEST_P(DatabaseClientTest, getAll) {
EXPECT_EQ(RRType::A(), target[a_idx]->getType());
std::string previous;
size_t count(0);
- for (RdataIteratorPtr it(target[a_idx]->getRdataIterator());
+ for (RdataIteratorPtr it = target[a_idx]->getRdataIterator();
!it->isLast(); it->next()) {
++count;
EXPECT_NE(previous, it->getCurrent().toText());
diff --git a/src/lib/datasrc/tests/faked_nsec3.cc b/src/lib/datasrc/tests/faked_nsec3.cc
index f53fbd7..24d4f3c 100644
--- a/src/lib/datasrc/tests/faked_nsec3.cc
+++ b/src/lib/datasrc/tests/faked_nsec3.cc
@@ -15,6 +15,7 @@
#include "faked_nsec3.h"
#include <dns/name.h>
+#include <dns/labelsequence.h>
#include <testutils/dnsmessage_test.h>
#include <map>
@@ -87,6 +88,13 @@ public:
isc_throw(isc::Unexpected, "unexpected name for NSEC3 test: "
<< name);
}
+ virtual string calculate(const LabelSequence& ls) const {
+ assert(ls.isAbsolute());
+ // This is not very optimal, but it's only going to be used in
+ // tests.
+ const Name name(ls.toText());
+ return (calculate(name));
+ }
virtual bool match(const rdata::generic::NSEC3PARAM&) const {
return (true);
}
diff --git a/src/lib/datasrc/tests/master_loader_callbacks_test.cc b/src/lib/datasrc/tests/master_loader_callbacks_test.cc
index fb4487a..6e96243 100644
--- a/src/lib/datasrc/tests/master_loader_callbacks_test.cc
+++ b/src/lib/datasrc/tests/master_loader_callbacks_test.cc
@@ -52,7 +52,7 @@ public:
rrset.getTTL()));
EXPECT_FALSE(rrset.getRRsig()) << "Unexpected RRSIG on rrset, not "
"copying. Following check will likely fail as a result.";
- for (isc::dns::RdataIteratorPtr it(rrset.getRdataIterator());
+ for (isc::dns::RdataIteratorPtr it = rrset.getRdataIterator();
!it->isLast(); it->next()) {
copy->addRdata(it->getCurrent());
}
diff --git a/src/lib/datasrc/tests/memory/domaintree_unittest.cc b/src/lib/datasrc/tests/memory/domaintree_unittest.cc
index 7568f8d..8648dc8 100644
--- a/src/lib/datasrc/tests/memory/domaintree_unittest.cc
+++ b/src/lib/datasrc/tests/memory/domaintree_unittest.cc
@@ -1253,7 +1253,7 @@ previousWalk(TestDomainTree& dtree, const TestDomainTreeNode* node,
EXPECT_EQ(static_cast<void*>(NULL), node);
node = dtree.previousNode(node_path);
}
- for (size_t i(chain_length); i > 0; --i) {
+ for (size_t i = chain_length; i > 0; --i) {
EXPECT_NE(static_cast<void*>(NULL), node);
EXPECT_EQ(Name(ordered_names[i - 1]), node_path.getAbsoluteName());
// Find the node at the path and check the value is the same
diff --git a/src/lib/datasrc/tests/memory/memory_client_unittest.cc b/src/lib/datasrc/tests/memory/memory_client_unittest.cc
index 73862e3..7644a1e 100644
--- a/src/lib/datasrc/tests/memory/memory_client_unittest.cc
+++ b/src/lib/datasrc/tests/memory/memory_client_unittest.cc
@@ -774,13 +774,22 @@ TEST_F(MemoryClientTest, getIteratorSeparateSigned) {
EXPECT_TRUE(seen_nsec3);
}
-TEST_F(MemoryClientTest, getIteratorGetSOAThrowsNotImplemented) {
+TEST_F(MemoryClientTest, getIteratorGetSOA) {
loadZoneIntoTable(*ztable_segment_, Name("example.org"), zclass_,
TEST_DATA_DIR "/example.org-empty.zone");
ZoneIteratorPtr iterator(client_->getIterator(Name("example.org")));
- // This method is not implemented.
- EXPECT_THROW(iterator->getSOA(), isc::NotImplemented);
+ ConstRRsetPtr soa_rrset(iterator->getSOA());
+
+ ASSERT_EQ(RRType::SOA(), soa_rrset->getType());
+ ASSERT_EQ(1, soa_rrset->getRdataCount());
+
+ RdataIteratorPtr it(soa_rrset->getRdataIterator());
+ const generic::SOA& soa
+ (dynamic_cast<const generic::SOA&>(it->getCurrent()));
+
+ EXPECT_EQ(71, soa.getSerial().getValue());
+ EXPECT_EQ(3600, soa.getMinimum());
}
TEST_F(MemoryClientTest, addEmptyRRsetThrows) {
diff --git a/src/lib/datasrc/tests/memory/treenode_rrset_unittest.cc b/src/lib/datasrc/tests/memory/treenode_rrset_unittest.cc
index 4d1f6e1..a1f1b28 100644
--- a/src/lib/datasrc/tests/memory/treenode_rrset_unittest.cc
+++ b/src/lib/datasrc/tests/memory/treenode_rrset_unittest.cc
@@ -333,6 +333,37 @@ checkToWireResult(OutputType& expected_output, OutputType& actual_output,
actual_output.getData(), actual_output.getLength());
}
+TEST_F(TreeNodeRRsetTest, getLength) {
+ // A RR
+ // www.example.com = 1 + 3 + 1 + 7 + 1 + 3 + 1 = 17 octets
+ // TYPE field = 2 octets
+ // CLASS field = 2 octets
+ // TTL field = 4 octets
+ // RDLENGTH field = 2 octets
+ // A RDATA = 4 octets
+ // Total = 17 + 2 + 2 + 4 + 2 + 4 = 31 octets
+
+ // RRSIG RR
+ // www.example.com = 1 + 4 + 1 + 7 + 1 + 3 + 1 = 17 octets
+ // TYPE field = 2 octets
+ // CLASS field = 2 octets
+ // TTL field = 4 octets
+ // RDLENGTH field = 2 octets
+ // RRSIG RDATA = 18 + [1 + 7 + 1 + 3 + 1 (example.com)] + 3 (base64
+ // decode of FAKE) octets
+ // Total = 17 + 2 + 2 + 4 + 2 + 34 = 61 octets
+
+ // 1. with RRSIG, DNSSEC not OK
+ // ` 2 A RRs + 0 RRSIG RRs
+ const TreeNodeRRset rrset1(rrclass_, www_node_, a_rdataset_, false);
+ EXPECT_EQ(31 + 31, rrset1.getLength());
+
+ // 2. with RRSIG, DNSSEC OK
+ // ` 2 A RRs + 1 RRSIG RR
+ const TreeNodeRRset rrset2(rrclass_, www_node_, a_rdataset_, true);
+ EXPECT_EQ(31 + 31 + 61, rrset2.getLength());
+}
+
TEST_F(TreeNodeRRsetTest, toWire) {
MessageRenderer expected_renderer, actual_renderer;
OutputBuffer expected_buffer(0), actual_buffer(0);
@@ -663,6 +694,7 @@ TEST_F(TreeNodeRRsetTest, unexpectedMethods) {
EXPECT_THROW(rrset.setTTL(RRTTL(0)), isc::Unexpected);
EXPECT_THROW(rrset.addRdata(createRdata(RRType::A(), rrclass_, "0.0.0.0")),
isc::Unexpected);
+ EXPECT_THROW(rrset.addRdata("0.0.0.0"), isc::Unexpected);
RdataPtr sig_rdata = createRdata(
RRType::RRSIG(), rrclass_,
"A 5 2 3600 20120814220826 20120715220826 5300 example.com. FAKE");
diff --git a/src/lib/datasrc/tests/mock_client.cc b/src/lib/datasrc/tests/mock_client.cc
index 8cdc05d..1909496 100644
--- a/src/lib/datasrc/tests/mock_client.cc
+++ b/src/lib/datasrc/tests/mock_client.cc
@@ -128,7 +128,7 @@ private:
MockDataSourceClient::MockDataSourceClient(const char* zone_names[]) :
have_a_(true), use_baditerator_(true)
{
- for (const char** zone(zone_names); *zone; ++zone) {
+ for (const char** zone = zone_names; *zone; ++zone) {
zones.insert(Name(*zone));
}
}
@@ -145,7 +145,7 @@ MockDataSourceClient::MockDataSourceClient(
EXPECT_NE("MasterFiles", type) << "MasterFiles is a special case "
"and it never should be created as a data source client";
if (configuration_->getType() == data::Element::list) {
- for (size_t i(0); i < configuration_->size(); ++i) {
+ for (size_t i = 0; i < configuration_->size(); ++i) {
zones.insert(Name(configuration_->get(i)->stringValue()));
}
}
diff --git a/src/lib/datasrc/zone_finder.cc b/src/lib/datasrc/zone_finder.cc
index 70cb8bf..1fcdf2d 100644
--- a/src/lib/datasrc/zone_finder.cc
+++ b/src/lib/datasrc/zone_finder.cc
@@ -130,7 +130,7 @@ ZoneFinder::stripRRsigs(isc::dns::ConstRRsetPtr rp,
rp->getClass(),
rp->getType(),
rp->getTTL()));
- for (isc::dns::RdataIteratorPtr i(rp->getRdataIterator());
+ for (isc::dns::RdataIteratorPtr i = rp->getRdataIterator();
!i->isLast();
i->next()) {
result_base->addRdata(i->getCurrent());
diff --git a/src/lib/datasrc/zone_finder.h b/src/lib/datasrc/zone_finder.h
index 83851f6..13fccc5 100644
--- a/src/lib/datasrc/zone_finder.h
+++ b/src/lib/datasrc/zone_finder.h
@@ -648,10 +648,10 @@ public:
/// proofs, wildcard information etc. The options parameter works the
/// same way and it should conform to the same exception restrictions.
///
- /// \param name \see find, parameter name
+ /// \param name see find(), parameter name
/// \param target the successfull result is returned through this
- /// \param options \see find, parameter options
- /// \return \see find and it's result
+ /// \param options see find(), parameter options
+ /// \return see find() and its result
virtual boost::shared_ptr<Context> findAll(
const isc::dns::Name& name,
std::vector<isc::dns::ConstRRsetPtr> &target,
diff --git a/src/lib/dhcp/Makefile.am b/src/lib/dhcp/Makefile.am
index 1748927..640bac1 100644
--- a/src/lib/dhcp/Makefile.am
+++ b/src/lib/dhcp/Makefile.am
@@ -19,6 +19,7 @@ libb10_dhcp___la_SOURCES += duid.cc duid.h
libb10_dhcp___la_SOURCES += hwaddr.cc hwaddr.h
libb10_dhcp___la_SOURCES += iface_mgr.cc iface_mgr.h
libb10_dhcp___la_SOURCES += iface_mgr_bsd.cc
+libb10_dhcp___la_SOURCES += iface_mgr_error_handler.h
libb10_dhcp___la_SOURCES += iface_mgr_linux.cc
libb10_dhcp___la_SOURCES += iface_mgr_sun.cc
libb10_dhcp___la_SOURCES += libdhcp++.cc libdhcp++.h
diff --git a/src/lib/dhcp/classify.h b/src/lib/dhcp/classify.h
new file mode 100644
index 0000000..47db942
--- /dev/null
+++ b/src/lib/dhcp/classify.h
@@ -0,0 +1,70 @@
+// Copyright (C) 2014 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <config.h>
+
+#ifndef CLASSIFY_H
+#define CLASSIFY_H
+
+#include <set>
+#include <string>
+
+/// @file classify.h
+///
+/// @brief Defines basic elements of client classification.
+///
+/// This file defines common elements used for client classification.
+/// It is simple for now, but the complexity involved in client
+/// classification is expected to grow significantly.
+///
+/// @todo This file should be moved to dhcpsrv eventually as the classification
+/// is server side concept. Client has no notion of classifying incoming server
+/// messages as it usually talks to only one server. That move is not possible
+/// yet, as the Pkt4 and Pkt6 classes have server-side implementation, even
+/// though they reside in the dhcp directory.
+
+namespace isc {
+
+namespace dhcp {
+
+ /// Definition of a single class.
+ typedef std::string ClientClass;
+
+ /// @brief Container for storing client classes
+ ///
+ /// Depending on how you look at it, this is either a little more than just
+ /// a set of strings or a client classifier that performs access control.
+ /// For now, it is a simple access list that may contain zero or more
+ /// class names. It is expected to grow in complexity once support for
+ /// client classes becomes more feature rich.
+ ///
+ /// Note: This class is derived from std::set which may not have Doxygen
+ /// documentation. See http://www.cplusplus.com/reference/set/set/.
+ class ClientClasses : public std::set<ClientClass> {
+ public:
+ /// @brief returns if class x belongs to the defined classes
+ ///
+ /// @param x client class to be checked
+ /// @return true if x belongs to the classes
+ bool
+ contains(const ClientClass& x) const {
+ return (find(x) != end());
+ }
+ };
+
+};
+
+};
+
+#endif /* CLASSIFY_H */
diff --git a/src/lib/dhcp/dhcp4.h b/src/lib/dhcp/dhcp4.h
index 392478a..2b24405 100644
--- a/src/lib/dhcp/dhcp4.h
+++ b/src/lib/dhcp/dhcp4.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2004-2011 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 2004-2011, 2014 by Internet Systems Consortium, Inc. ("ISC")
* Copyright (c) 1995-2003 by Internet Software Consortium
*
* Permission to use, copy, modify, and distribute this software for any
@@ -154,7 +154,9 @@ enum DHCPMessageType {
DHCPLEASEQUERY = 10,
DHCPLEASEUNASSIGNED = 11,
DHCPLEASEUNKNOWN = 12,
- DHCPLEASEACTIVE = 13
+ DHCPLEASEACTIVE = 13,
+ DHCPBULKLEASEQUERY = 14,
+ DHCPLEASEQUERYDONE = 15
};
static const uint16_t DHCP4_CLIENT_PORT = 68;
diff --git a/src/lib/dhcp/iface_mgr.cc b/src/lib/dhcp/iface_mgr.cc
index e3ffe48..e6aea3d 100644
--- a/src/lib/dhcp/iface_mgr.cc
+++ b/src/lib/dhcp/iface_mgr.cc
@@ -22,6 +22,7 @@
#include <dhcp/dhcp4.h>
#include <dhcp/dhcp6.h>
#include <dhcp/iface_mgr.h>
+#include <dhcp/iface_mgr_error_handler.h>
#include <dhcp/pkt_filter_inet.h>
#include <dhcp/pkt_filter_inet6.h>
#include <exceptions/exceptions.h>
@@ -37,40 +38,6 @@
#include <string.h>
#include <sys/select.h>
-/// @brief A macro which handles an error in IfaceMgr.
-///
-/// There are certain cases when IfaceMgr may hit an error which shouldn't
-/// result in interruption of the function processing. A typical case is
-/// the function which opens sockets on available interfaces for a DHCP
-/// server. If this function fails to open a socket on a specific interface
-/// (for example, there is another socket already open on this interface
-/// and bound to the same address and port), it is desired that the server
-/// logs a warning but will try to open sockets on other interfaces. In order
-/// to log an error, the IfaceMgr will use the error handler function provided
-/// by the server and pass an error string to it. When the handler function
-/// returns, the IfaceMgr will proceed to open other sockets. It is allowed
-/// that the error handler function is not installed (is NULL). In these
-/// cases it is expected that the exception is thrown instead. A possible
-/// solution would be to enclose this conditional behavior in a function.
-/// However, despite the hate for macros, the macro seems to be a bit
-/// better solution in this case as it allows to convenietly pass an
-/// error string in a stream (not as a string).
-///
-/// @param ex_type Exception to be thrown if error_handler is NULL.
-/// @param handler Error handler function to be called or NULL to indicate
-/// that exception should be thrown instead.
-/// @param stream stream object holding an error string.
-#define IFACEMGR_ERROR(ex_type, handler, stream) \
-{ \
- std::ostringstream oss__; \
- oss__ << stream; \
- if (handler) { \
- handler(oss__.str()); \
- } else { \
- isc_throw(ex_type, oss__); \
- } \
-} \
-
using namespace std;
using namespace isc::asiolink;
using namespace isc::util::io::internal;
@@ -183,7 +150,7 @@ bool Iface::delAddress(const isc::asiolink::IOAddress& addr) {
return (false);
}
-bool Iface::delSocket(uint16_t sockfd) {
+bool Iface::delSocket(const uint16_t sockfd) {
list<SocketInfo>::iterator sock = sockets_.begin();
while (sock!=sockets_.end()) {
if (sock->sockfd_ == sockfd) {
@@ -203,7 +170,6 @@ bool Iface::delSocket(uint16_t sockfd) {
IfaceMgr::IfaceMgr()
:control_buf_len_(CMSG_SPACE(sizeof(struct in6_pktinfo))),
control_buf_(new char[control_buf_len_]),
- session_socket_(INVALID_SOCKET), session_callback_(NULL),
packet_filter_(new PktFilterInet()),
packet_filter6_(new PktFilterInet6())
{
@@ -233,6 +199,24 @@ void Iface::addUnicast(const isc::asiolink::IOAddress& addr) {
unicasts_.push_back(addr);
}
+bool
+Iface::getAddress4(isc::asiolink::IOAddress& address) const {
+ // Iterate over existing addresses assigned to the interface.
+ // Try to find the one that is IPv4.
+ const AddressCollection& addrs = getAddresses();
+ for (AddressCollection::const_iterator addr = addrs.begin();
+ addr != addrs.end(); ++addr) {
+ // If address is IPv4, we assign it to the function argument
+ // and return true.
+ if (addr->isV4()) {
+ address = *addr;
+ return (true);
+ }
+ }
+ // There is no IPv4 address assigned to this interface.
+ return (false);
+}
+
void IfaceMgr::closeSockets() {
for (IfaceCollection::iterator iface = ifaces_.begin();
iface != ifaces_.end(); ++iface) {
@@ -261,6 +245,37 @@ IfaceMgr::isDirectResponseSupported() const {
}
void
+IfaceMgr::addExternalSocket(int socketfd, SocketCallback callback) {
+ for (SocketCallbackInfoContainer::iterator s = callbacks_.begin();
+ s != callbacks_.end(); ++s) {
+
+ // There's such a socket description there already.
+ // Update the callback and we're done
+ if (s->socket_ == socketfd) {
+ s->callback_ = callback;
+ return;
+ }
+ }
+
+ // Add a new entry to the callbacks vector
+ SocketCallbackInfo x;
+ x.socket_ = socketfd;
+ x.callback_ = callback;
+ callbacks_.push_back(x);
+}
+
+void
+IfaceMgr::deleteExternalSocket(int socketfd) {
+ for (SocketCallbackInfoContainer::iterator s = callbacks_.begin();
+ s != callbacks_.end(); ++s) {
+ if (s->socket_ == socketfd) {
+ callbacks_.erase(s);
+ return;
+ }
+ }
+}
+
+void
IfaceMgr::setPacketFilter(const PktFilterPtr& packet_filter) {
// Do not allow NULL pointer.
if (!packet_filter) {
@@ -526,63 +541,17 @@ IfaceMgr::openSockets6(const uint16_t port,
// with interface with 2 global addresses, we would bind 3 sockets
// (one for link-local and two for global). That would result in
// getting each message 3 times.
- if (!addr->getAddress().to_v6().is_link_local()){
+ if (!addr->isV6LinkLocal()){
continue;
}
- // Open socket and join multicast group only if the interface
- // is multicast-capable.
- // @todo The DHCPv6 requires multicast so we may want to think
- // whether we want to open the socket on a multicast-incapable
- // interface or not. For now, we prefer to be liberal and allow
- // it for some odd use cases which may utilize non-multicast
- // interfaces. Perhaps a warning should be emitted if the
- // interface is not a multicast one.
-
- // The sock variable will hold a socket descriptor. It may be
- // used to close a socket if the function fails to bind to
- // multicast address on Linux systems. Because we only bind
- // a socket to multicast address on Linux, on other systems
- // the sock variable will be initialized but unused. We have
- // to suppress the cppcheck warning which shows up on non-Linux
- // systems.
- // cppcheck-suppress variableScope
- int sock;
- try {
- // cppcheck-suppress unreadVariable
- sock = openSocket(iface->getName(), *addr, port,
- iface->flag_multicast_);
-
- } catch (const Exception& ex) {
- IFACEMGR_ERROR(SocketConfigError, error_handler,
- "Failed to open link-local socket on "
- " interface " << iface->getName() << ": "
- << ex.what());
- continue;
+ // Run OS-specific function to open a socket on link-local address
+ // and join multicast group (non-Linux OSes), or open two sockets and
+ // bind one to link-local, another one to multicast address.
+ if (openMulticastSocket(*iface, *addr, port, error_handler)) {
+ ++count;
}
- count++;
-
- /// @todo: Remove this ifdef once we start supporting BSD systems.
-#if defined(OS_LINUX)
- // To receive multicast traffic, Linux requires binding socket to
- // a multicast group. That in turn doesn't work on NetBSD.
- if (iface->flag_multicast_) {
- try {
- openSocket(iface->getName(),
- IOAddress(ALL_DHCP_RELAY_AGENTS_AND_SERVERS),
- port);
- } catch (const Exception& ex) {
- // Delete previously opened socket.
- iface->delSocket(sock);
- IFACEMGR_ERROR(SocketConfigError, error_handler,
- "Failed to open multicast socket on"
- " interface " << iface->getName()
- << ", reason: " << ex.what());
- continue;
- }
- }
-#endif
}
}
return (count > 0);
@@ -742,7 +711,7 @@ int IfaceMgr::openSocketFromRemoteAddress(const IOAddress& remote_addr,
const uint16_t port) {
try {
// Get local address to be used to connect to remote location.
- IOAddress local_address(getLocalAddress(remote_addr, port).getAddress());
+ IOAddress local_address(getLocalAddress(remote_addr, port));
return openSocketFromAddress(local_address, port);
} catch (const Exception& e) {
isc_throw(SocketConfigError, e.what());
@@ -807,7 +776,6 @@ IfaceMgr::getLocalAddress(const IOAddress& remote_addr, const uint16_t port) {
return IOAddress(local_address);
}
-
int
IfaceMgr::openSocket6(Iface& iface, const IOAddress& addr, uint16_t port,
const bool join_multicast) {
@@ -869,7 +837,6 @@ IfaceMgr::receive4(uint32_t timeout_sec, uint32_t timeout_usec /* = 0 */) {
IfaceCollection::const_iterator iface;
fd_set sockets;
int maxfd = 0;
- stringstream names;
FD_ZERO(&sockets);
@@ -884,7 +851,6 @@ IfaceMgr::receive4(uint32_t timeout_sec, uint32_t timeout_usec /* = 0 */) {
// Only deal with IPv4 addresses.
if (s->addr_.isV4()) {
- names << s->sockfd_ << "(" << iface->getName() << ") ";
// Add this socket to listening set
FD_SET(s->sockfd_, &sockets);
@@ -895,13 +861,15 @@ IfaceMgr::receive4(uint32_t timeout_sec, uint32_t timeout_usec /* = 0 */) {
}
}
- // if there is session socket registered...
- if (session_socket_ != INVALID_SOCKET) {
- // at it to the set as well
- FD_SET(session_socket_, &sockets);
- if (maxfd < session_socket_)
- maxfd = session_socket_;
- names << session_socket_ << "(session)";
+ // if there are any callbacks for external sockets registered...
+ if (!callbacks_.empty()) {
+ for (SocketCallbackInfoContainer::const_iterator s = callbacks_.begin();
+ s != callbacks_.end(); ++s) {
+ FD_SET(s->socket_, &sockets);
+ if (maxfd < s->socket_) {
+ maxfd = s->socket_;
+ }
+ }
}
struct timeval select_timeout;
@@ -918,18 +886,22 @@ IfaceMgr::receive4(uint32_t timeout_sec, uint32_t timeout_usec /* = 0 */) {
}
// Let's find out which socket has the data
- if ((session_socket_ != INVALID_SOCKET) && (FD_ISSET(session_socket_, &sockets))) {
- // something received over session socket
- if (session_callback_) {
- // in theory we could call io_service.run_one() here, instead of
- // implementing callback mechanism, but that would introduce
- // asiolink dependency to libdhcp++ and that is something we want
- // to avoid (see CPE market and out long term plans for minimalistic
- // implementations.
- session_callback_();
+ for (SocketCallbackInfoContainer::iterator s = callbacks_.begin();
+ s != callbacks_.end(); ++s) {
+ if (!FD_ISSET(s->socket_, &sockets)) {
+ continue;
}
- return (Pkt4Ptr()); // NULL
+ // something received over external socket
+
+ // Calling the external socket's callback provides its service
+ // layer access without integrating any specific features
+ // in IfaceMgr
+ if (s->callback_) {
+ s->callback_();
+ }
+
+ return (Pkt4Ptr());
}
// Let's find out which interface/socket has the data
@@ -966,7 +938,6 @@ Pkt6Ptr IfaceMgr::receive6(uint32_t timeout_sec, uint32_t timeout_usec /* = 0 */
const SocketInfo* candidate = 0;
fd_set sockets;
int maxfd = 0;
- stringstream names;
FD_ZERO(&sockets);
@@ -981,7 +952,6 @@ Pkt6Ptr IfaceMgr::receive6(uint32_t timeout_sec, uint32_t timeout_usec /* = 0 */
// Only deal with IPv6 addresses.
if (s->addr_.isV6()) {
- names << s->sockfd_ << "(" << iface->getName() << ") ";
// Add this socket to listening set
FD_SET(s->sockfd_, &sockets);
@@ -992,13 +962,17 @@ Pkt6Ptr IfaceMgr::receive6(uint32_t timeout_sec, uint32_t timeout_usec /* = 0 */
}
}
- // if there is session socket registered...
- if (session_socket_ != INVALID_SOCKET) {
- // at it to the set as well
- FD_SET(session_socket_, &sockets);
- if (maxfd < session_socket_)
- maxfd = session_socket_;
- names << session_socket_ << "(session)";
+ // if there are any callbacks for external sockets registered...
+ if (!callbacks_.empty()) {
+ for (SocketCallbackInfoContainer::const_iterator s = callbacks_.begin();
+ s != callbacks_.end(); ++s) {
+
+ // Add it to the set as well
+ FD_SET(s->socket_, &sockets);
+ if (maxfd < s->socket_) {
+ maxfd = s->socket_;
+ }
+ }
}
struct timeval select_timeout;
@@ -1015,18 +989,22 @@ Pkt6Ptr IfaceMgr::receive6(uint32_t timeout_sec, uint32_t timeout_usec /* = 0 */
}
// Let's find out which socket has the data
- if ((session_socket_ != INVALID_SOCKET) && (FD_ISSET(session_socket_, &sockets))) {
- // something received over session socket
- if (session_callback_) {
- // in theory we could call io_service.run_one() here, instead of
- // implementing callback mechanism, but that would introduce
- // asiolink dependency to libdhcp++ and that is something we want
- // to avoid (see CPE market and out long term plans for minimalistic
- // implementations.
- session_callback_();
+ for (SocketCallbackInfoContainer::iterator s = callbacks_.begin();
+ s != callbacks_.end(); ++s) {
+ if (!FD_ISSET(s->socket_, &sockets)) {
+ continue;
}
- return (Pkt6Ptr()); // NULL
+ // something received over external socket
+
+ // Calling the external socket's callback provides its service
+ // layer access without integrating any specific features
+ // in IfaceMgr
+ if (s->callback_) {
+ s->callback_();
+ }
+
+ return (Pkt6Ptr());
}
// Let's find out which interface/socket has the data
@@ -1073,7 +1051,7 @@ uint16_t IfaceMgr::getSocket(const isc::dhcp::Pkt6& pkt) {
}
// Sockets bound to multicast address are useless for sending anything.
- if (s->addr_.getAddress().to_v6().is_multicast()) {
+ if (s->addr_.isV6Multicast()) {
continue;
}
@@ -1090,10 +1068,10 @@ uint16_t IfaceMgr::getSocket(const isc::dhcp::Pkt6& pkt) {
// If we want to send something to link-local and the socket is
// bound to link-local or we want to send to global and the socket
// is bound to global, then use it as candidate
- if ( (pkt.getRemoteAddr().getAddress().to_v6().is_link_local() &&
- s->addr_.getAddress().to_v6().is_link_local()) ||
- (!pkt.getRemoteAddr().getAddress().to_v6().is_link_local() &&
- !s->addr_.getAddress().to_v6().is_link_local()) ) {
+ if ( (pkt.getRemoteAddr().isV6LinkLocal() &&
+ s->addr_.isV6LinkLocal()) ||
+ (!pkt.getRemoteAddr().isV6LinkLocal() &&
+ !s->addr_.isV6LinkLocal()) ) {
candidate = s;
}
}
@@ -1129,6 +1107,5 @@ IfaceMgr::getSocket(isc::dhcp::Pkt4 const& pkt) {
<< " 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 3fde867..4007604 100644
--- a/src/lib/dhcp/iface_mgr.h
+++ b/src/lib/dhcp/iface_mgr.h
@@ -34,6 +34,7 @@ namespace isc {
namespace dhcp {
+
/// @brief IfaceMgr exception thrown thrown when interface detection fails.
class IfaceDetectError : public Exception {
public:
@@ -245,6 +246,17 @@ public:
/// @return collection of addresses
const AddressCollection& getAddresses() const { return addrs_; }
+ /// @brief Returns IPv4 address assigned to the interface.
+ ///
+ /// This function looks for an IPv4 address assigned to the interface
+ /// and returns it through the argument.
+ ///
+ /// @param [out] address IPv4 address assigned to the interface.
+ ///
+ /// @return Boolean value which informs whether IPv4 address has been found
+ /// for the interface (if true), or not (false).
+ bool getAddress4(isc::asiolink::IOAddress& address) const;
+
/// @brief Adds an address to an interface.
///
/// This only adds an address to collection, it does not physically
@@ -393,8 +405,20 @@ boost::function<void(const std::string& errmsg)> IfaceMgrErrorMsgCallback;
///
class IfaceMgr : public boost::noncopyable {
public:
- /// Defines callback used when commands are received over control session.
- typedef void (*SessionCallback) (void);
+ /// Defines callback used when data is received over external sockets.
+ typedef boost::function<void ()> SocketCallback;
+
+ /// Keeps callback information for external sockets.
+ struct SocketCallbackInfo {
+ /// Socket descriptor of the external socket.
+ int socket_;
+
+ /// A callback that will be called when data arrives over socket_.
+ SocketCallback callback_;
+ };
+
+ /// Defines storage container for callbacks for external sockets
+ typedef std::list<SocketCallbackInfo> SocketCallbackInfoContainer;
/// @brief Packet reception buffer size
///
@@ -784,17 +808,18 @@ public:
/// @return number of detected interfaces
uint16_t countIfaces() { return ifaces_.size(); }
- /// @brief Sets session socket and a callback
+ /// @brief Adds external socket and a callback
///
- /// Specifies session socket and a callback that will be called
+ /// Specifies external socket and a callback that will be called
/// when data will be received over that socket.
///
/// @param socketfd socket descriptor
/// @param callback callback function
- void set_session_socket(int socketfd, SessionCallback callback) {
- session_socket_ = socketfd;
- session_callback_ = callback;
- }
+ void addExternalSocket(int socketfd, SocketCallback callback);
+
+ /// @brief Deletes external socket
+
+ void deleteExternalSocket(int socketfd);
/// @brief Set packet filter object to handle sending and receiving DHCPv4
/// messages.
@@ -880,9 +905,6 @@ public:
/// @return true if there is a socket bound to the specified address.
bool hasOpenSocket(const isc::asiolink::IOAddress& addr) const;
- /// A value of socket descriptor representing "not specified" state.
- static const int INVALID_SOCKET = -1;
-
// don't use private, we need derived classes in tests
protected:
@@ -976,13 +998,7 @@ protected:
/// @return true if successful, false otherwise
bool os_receive4(struct msghdr& m, Pkt4Ptr& pkt);
- /// Socket descriptor of the session socket.
- int session_socket_;
-
- /// A callback that will be called when data arrives over session_socket_.
- SessionCallback session_callback_;
private:
-
/// @brief Identifies local network address to be used to
/// connect to remote address.
///
@@ -1002,6 +1018,29 @@ private:
const uint16_t port);
+ /// @brief Open an IPv6 socket with multicast support.
+ ///
+ /// This function opens socket(s) to allow reception of the DHCPv6 sent
+ /// to multicast address. It opens an IPv6 socket, binds it to link-local
+ /// address and joins multicast group (on non-Linux systems) or opens two
+ /// IPv6 sockets and binds one of them to link-local address and another
+ /// one to multicast address (on Linux systems).
+ ///
+ /// @note This function is intended to be called internally by the
+ /// @c IfaceMgr::openSockets6. It is not intended to be called from any
+ /// other function.
+ ///
+ /// @param iface Interface on which socket should be open.
+ /// @param addr Link-local address to bind the socket to.
+ /// @param port Port number to bind socket to.
+ /// @param error_handler Error handler function to be called when an
+ /// error occurs during opening a socket, or NULL if exception should
+ /// be thrown upon error.
+ bool openMulticastSocket(Iface& iface,
+ const isc::asiolink::IOAddress& addr,
+ const uint16_t port,
+ IfaceMgrErrorMsgCallback error_handler = NULL);
+
/// Holds instance of a class derived from PktFilter, used by the
/// IfaceMgr to open sockets and send/receive packets through these
/// sockets. It is possible to supply custom object using
@@ -1017,6 +1056,9 @@ private:
/// messages. It is possible to supply a custom object using
/// setPacketFilter method.
PktFilter6Ptr packet_filter6_;
+
+ /// @brief Contains list of callbacks for external sockets
+ SocketCallbackInfoContainer callbacks_;
};
}; // namespace isc::dhcp
diff --git a/src/lib/dhcp/iface_mgr_bsd.cc b/src/lib/dhcp/iface_mgr_bsd.cc
index be73bcf..7a01228 100644
--- a/src/lib/dhcp/iface_mgr_bsd.cc
+++ b/src/lib/dhcp/iface_mgr_bsd.cc
@@ -1,4 +1,4 @@
-// Copyright (C) 2011, 2013 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2011, 2013-2014 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,6 +17,7 @@
#if defined(OS_BSD)
#include <dhcp/iface_mgr.h>
+#include <dhcp/iface_mgr_error_handler.h>
#include <dhcp/pkt_filter_inet.h>
#include <exceptions/exceptions.h>
@@ -149,6 +150,28 @@ IfaceMgr::setMatchingPacketFilter(const bool /* direct_response_desired */) {
setPacketFilter(PktFilterPtr(new PktFilterInet()));
}
+bool
+IfaceMgr::openMulticastSocket(Iface& iface,
+ const isc::asiolink::IOAddress& addr,
+ const uint16_t port,
+ IfaceMgrErrorMsgCallback error_handler) {
+ try {
+ // This should open a socket, bound it to link-local address
+ // and join multicast group.
+ openSocket(iface.getName(), addr, port,
+ iface.flag_multicast_);
+
+ } catch (const Exception& ex) {
+ IFACEMGR_ERROR(SocketConfigError, error_handler,
+ "Failed to open link-local socket on "
+ " interface " << iface.getName() << ": "
+ << ex.what());
+ return (false);
+
+ }
+ return (true);
+}
+
} // end of isc::dhcp namespace
} // end of dhcp namespace
diff --git a/src/lib/dhcp/iface_mgr_error_handler.h b/src/lib/dhcp/iface_mgr_error_handler.h
new file mode 100644
index 0000000..c5ef5b4
--- /dev/null
+++ b/src/lib/dhcp/iface_mgr_error_handler.h
@@ -0,0 +1,52 @@
+// Copyright (C) 2014 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 IFACE_MGR_ERROR_HANDLER_H
+#define IFACE_MGR_ERROR_HANDLER_H
+
+/// @brief A macro which handles an error in IfaceMgr.
+///
+/// There are certain cases when IfaceMgr may hit an error which shouldn't
+/// result in interruption of the function processing. A typical case is
+/// the function which opens sockets on available interfaces for a DHCP
+/// server. If this function fails to open a socket on a specific interface
+/// (for example, there is another socket already open on this interface
+/// and bound to the same address and port), it is desired that the server
+/// logs a warning but will try to open sockets on other interfaces. In order
+/// to log an error, the IfaceMgr will use the error handler function provided
+/// by the server and pass an error string to it. When the handler function
+/// returns, the IfaceMgr will proceed to open other sockets. It is allowed
+/// that the error handler function is not installed (is NULL). In these
+/// cases it is expected that the exception is thrown instead. A possible
+/// solution would be to enclose this conditional behavior in a function.
+/// However, despite the hate for macros, the macro seems to be a bit
+/// better solution in this case as it allows to convenietly pass an
+/// error string in a stream (not as a string).
+///
+/// @param ex_type Exception to be thrown if error_handler is NULL.
+/// @param handler Error handler function to be called or NULL to indicate
+/// that exception should be thrown instead.
+/// @param stream stream object holding an error string.
+#define IFACEMGR_ERROR(ex_type, handler, stream) \
+{ \
+ std::ostringstream oss__; \
+ oss__ << stream; \
+ if (handler) { \
+ handler(oss__.str()); \
+ } else { \
+ isc_throw(ex_type, oss__); \
+ } \
+} \
+
+#endif // IFACE_MGR_ERROR_HANDLER_H
diff --git a/src/lib/dhcp/iface_mgr_linux.cc b/src/lib/dhcp/iface_mgr_linux.cc
index dddeb52..f4b0613 100644
--- a/src/lib/dhcp/iface_mgr_linux.cc
+++ b/src/lib/dhcp/iface_mgr_linux.cc
@@ -1,4 +1,4 @@
-// Copyright (C) 2011-2013 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2011-2014 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,6 +33,7 @@
#include <asiolink/io_address.h>
#include <dhcp/iface_mgr.h>
+#include <dhcp/iface_mgr_error_handler.h>
#include <dhcp/pkt_filter_inet.h>
#include <dhcp/pkt_filter_lpf.h>
#include <exceptions/exceptions.h>
@@ -533,6 +534,60 @@ bool IfaceMgr::os_receive4(struct msghdr&, Pkt4Ptr&) {
return (true);
}
+bool
+IfaceMgr::openMulticastSocket(Iface& iface,
+ const isc::asiolink::IOAddress& addr,
+ const uint16_t port,
+ IfaceMgrErrorMsgCallback error_handler) {
+ // This variable will hold a descriptor of the socket bound to
+ // link-local address. It may be required for us to close this
+ // socket if an attempt to open and bind a socket to multicast
+ // address fails.
+ int sock;
+ try {
+ sock = openSocket(iface.getName(), addr, port,
+ iface.flag_multicast_);
+
+ } catch (const Exception& ex) {
+ IFACEMGR_ERROR(SocketConfigError, error_handler,
+ "Failed to open link-local socket on "
+ " interface " << iface.getName() << ": "
+ << ex.what());
+ return (false);
+
+ }
+
+ // To receive multicast traffic, Linux requires binding socket to
+ // the multicast address.
+
+ /// @todo The DHCPv6 requires multicast so we may want to think
+ /// whether we want to open the socket on a multicast-incapable
+ /// interface or not. For now, we prefer to be liberal and allow
+ /// it for some odd use cases which may utilize non-multicast
+ /// interfaces. Perhaps a warning should be emitted if the
+ /// interface is not a multicast one.
+ if (iface.flag_multicast_) {
+ try {
+ openSocket(iface.getName(),
+ IOAddress(ALL_DHCP_RELAY_AGENTS_AND_SERVERS),
+ port);
+ } catch (const Exception& ex) {
+ // An attempt to open and bind a socket to multicast addres
+ // has failed. We have to close the socket we previously
+ // bound to link-local address - this is everything or
+ // nothing strategy.
+ iface.delSocket(sock);
+ IFACEMGR_ERROR(SocketConfigError, error_handler,
+ "Failed to open multicast socket on"
+ " interface " << iface.getName()
+ << ", reason: " << ex.what());
+ return (false);
+ }
+ }
+ // Both sockets have opened successfully.
+ return (true);
+}
+
} // end of isc::dhcp namespace
} // end of isc namespace
diff --git a/src/lib/dhcp/iface_mgr_sun.cc b/src/lib/dhcp/iface_mgr_sun.cc
index fe2b0b3..a78de8f 100644
--- a/src/lib/dhcp/iface_mgr_sun.cc
+++ b/src/lib/dhcp/iface_mgr_sun.cc
@@ -1,4 +1,4 @@
-// Copyright (C) 2011, 2013 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2011, 2013-2014 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,6 +17,7 @@
#if defined(OS_SUN)
#include <dhcp/iface_mgr.h>
+#include <dhcp/iface_mgr_error_handler.h>
#include <dhcp/pkt_filter_inet.h>
#include <exceptions/exceptions.h>
@@ -153,6 +154,28 @@ IfaceMgr::setMatchingPacketFilter(const bool /* direct_response_desired */) {
setPacketFilter(PktFilterPtr(new PktFilterInet()));
}
+bool
+IfaceMgr::openMulticastSocket(Iface& iface,
+ const isc::asiolink::IOAddress& addr,
+ const uint16_t port,
+ IfaceMgrErrorMsgCallback error_handler) {
+ try {
+ // This should open a socket, bound it to link-local address
+ // and join multicast group.
+ openSocket(iface.getName(), addr, port,
+ iface.flag_multicast_);
+
+ } catch (const Exception& ex) {
+ IFACEMGR_ERROR(SocketConfigError, error_handler,
+ "Failed to open link-local socket on "
+ " interface " << iface.getName() << ": "
+ << ex.what());
+ return (false);
+
+ }
+ return (true);
+}
+
} // end of isc::dhcp namespace
} // end of dhcp namespace
diff --git a/src/lib/dhcp/libdhcp++.cc b/src/lib/dhcp/libdhcp++.cc
index f6ba978..f55f6bc 100644
--- a/src/lib/dhcp/libdhcp++.cc
+++ b/src/lib/dhcp/libdhcp++.cc
@@ -231,10 +231,10 @@ size_t LibDHCP::unpackOptions6(const OptionBuffer& buf,
// The buffer being read comprises a set of options, each starting with
// a two-byte type code and a two-byte length field.
while (offset + 4 <= length) {
- uint16_t opt_type = isc::util::readUint16(&buf[offset]);
+ uint16_t opt_type = isc::util::readUint16(&buf[offset], 2);
offset += 2;
- uint16_t opt_len = isc::util::readUint16(&buf[offset]);
+ uint16_t opt_len = isc::util::readUint16(&buf[offset], 2);
offset += 2;
if (offset + opt_len > length) {
@@ -413,10 +413,10 @@ size_t LibDHCP::unpackVendorOptions6(const uint32_t vendor_id,
// The buffer being read comprises a set of options, each starting with
// a two-byte type code and a two-byte length field.
while (offset + 4 <= length) {
- uint16_t opt_type = isc::util::readUint16(&buf[offset]);
+ uint16_t opt_type = isc::util::readUint16(&buf[offset], 2);
offset += 2;
- uint16_t opt_len = isc::util::readUint16(&buf[offset]);
+ uint16_t opt_len = isc::util::readUint16(&buf[offset], 2);
offset += 2;
if (offset + opt_len > length) {
diff --git a/src/lib/dhcp/option.cc b/src/lib/dhcp/option.cc
index f5ab75e..24ddc06 100644
--- a/src/lib/dhcp/option.cc
+++ b/src/lib/dhcp/option.cc
@@ -250,35 +250,28 @@ uint8_t Option::getUint8() {
}
uint16_t Option::getUint16() {
- if (data_.size() < sizeof(uint16_t) ) {
- isc_throw(OutOfRange, "Attempt to read uint16 from option " << type_
- << " that has size " << data_.size());
- }
-
- return ( readUint16(&data_[0]) );
+ // readUint16() checks and throws OutOfRange if data_ is too small.
+ return (readUint16(&data_[0], data_.size()));
}
uint32_t Option::getUint32() {
- if (data_.size() < sizeof(uint32_t) ) {
- isc_throw(OutOfRange, "Attempt to read uint32 from option " << type_
- << " that has size " << data_.size());
- }
- return ( readUint32(&data_[0]) );
+ // readUint32() checks and throws OutOfRange if data_ is too small.
+ return (readUint32(&data_[0], data_.size()));
}
void Option::setUint8(uint8_t value) {
- data_.resize(1);
- data_[0] = value;
+ data_.resize(sizeof(value));
+ data_[0] = value;
}
void Option::setUint16(uint16_t value) {
- data_.resize(2);
- writeUint16(value, &data_[0]);
+ data_.resize(sizeof(value));
+ writeUint16(value, &data_[0], data_.size());
}
void Option::setUint32(uint32_t value) {
- data_.resize(4);
- writeUint32(value, &data_[0]);
+ data_.resize(sizeof(value));
+ writeUint32(value, &data_[0], data_.size());
}
bool Option::equal(const OptionPtr& other) const {
diff --git a/src/lib/dhcp/option4_addrlst.cc b/src/lib/dhcp/option4_addrlst.cc
index 436d07d..223da16 100644
--- a/src/lib/dhcp/option4_addrlst.cc
+++ b/src/lib/dhcp/option4_addrlst.cc
@@ -53,7 +53,7 @@ Option4AddrLst::Option4AddrLst(uint8_t type, OptionBufferConstIter first,
while (first != last) {
const uint8_t* ptr = &(*first);
- addAddress(IOAddress(readUint32(ptr)));
+ addAddress(IOAddress(readUint32(ptr, distance(first, last))));
first += V4ADDRESS_LEN;
}
}
diff --git a/src/lib/dhcp/option4_client_fqdn.cc b/src/lib/dhcp/option4_client_fqdn.cc
index 7f93a50..d546ab2 100644
--- a/src/lib/dhcp/option4_client_fqdn.cc
+++ b/src/lib/dhcp/option4_client_fqdn.cc
@@ -1,4 +1,4 @@
-// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2013-2014 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
@@ -217,7 +217,6 @@ setDomainName(const std::string& domain_name,
} else {
try {
domain_name_.reset(new isc::dns::Name(name));
- domain_name_type_ = name_type;
} catch (const Exception& ex) {
isc_throw(InvalidOption4FqdnDomainName,
@@ -227,6 +226,8 @@ setDomainName(const std::string& domain_name,
}
}
+
+ domain_name_type_ = name_type;
}
void
diff --git a/src/lib/dhcp/option6_client_fqdn.cc b/src/lib/dhcp/option6_client_fqdn.cc
index 761acae..d9eb0b2 100644
--- a/src/lib/dhcp/option6_client_fqdn.cc
+++ b/src/lib/dhcp/option6_client_fqdn.cc
@@ -1,4 +1,4 @@
-// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2013-2014 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
@@ -188,7 +188,6 @@ setDomainName(const std::string& domain_name,
} else {
try {
domain_name_.reset(new isc::dns::Name(name, true));
- domain_name_type_ = name_type;
} catch (const Exception& ex) {
isc_throw(InvalidOption6FqdnDomainName, "invalid domain-name value '"
@@ -197,6 +196,8 @@ setDomainName(const std::string& domain_name,
}
}
+
+ domain_name_type_ = name_type;
}
void
diff --git a/src/lib/dhcp/option6_ia.cc b/src/lib/dhcp/option6_ia.cc
index 825a5bf..a5751dd 100644
--- a/src/lib/dhcp/option6_ia.cc
+++ b/src/lib/dhcp/option6_ia.cc
@@ -72,12 +72,12 @@ void Option6IA::unpack(OptionBufferConstIter begin,
if (distance(begin, end) < OPTION6_IA_LEN) {
isc_throw(OutOfRange, "Option " << type_ << " truncated");
}
- iaid_ = readUint32( &(*begin) );
+ iaid_ = readUint32(&(*begin), distance(begin, end));
begin += sizeof(uint32_t);
- t1_ = readUint32( &(*begin) );
+ t1_ = readUint32(&(*begin), distance(begin, end));
begin += sizeof(uint32_t);
- t2_ = readUint32( &(*begin) );
+ t2_ = readUint32(&(*begin), distance(begin, end));
begin += sizeof(uint32_t);
unpackOptions(OptionBuffer(begin, end));
diff --git a/src/lib/dhcp/option6_iaaddr.cc b/src/lib/dhcp/option6_iaaddr.cc
index cbcd555..9e02be7 100644
--- a/src/lib/dhcp/option6_iaaddr.cc
+++ b/src/lib/dhcp/option6_iaaddr.cc
@@ -78,10 +78,10 @@ void Option6IAAddr::unpack(OptionBuffer::const_iterator begin,
addr_ = IOAddress::fromBytes(AF_INET6, &(*begin));
begin += V6ADDRESS_LEN;
- preferred_ = readUint32( &(*begin) );
+ preferred_ = readUint32(&(*begin), distance(begin, end));
begin += sizeof(uint32_t);
- valid_ = readUint32( &(*begin) );
+ valid_ = readUint32(&(*begin), distance(begin, end));
begin += sizeof(uint32_t);
unpackOptions(OptionBuffer(begin, end));
diff --git a/src/lib/dhcp/option6_iaprefix.cc b/src/lib/dhcp/option6_iaprefix.cc
index d28b4f7..357942f 100644
--- a/src/lib/dhcp/option6_iaprefix.cc
+++ b/src/lib/dhcp/option6_iaprefix.cc
@@ -76,10 +76,10 @@ void Option6IAPrefix::unpack(OptionBuffer::const_iterator begin,
isc_throw(OutOfRange, "Option " << type_ << " truncated");
}
- preferred_ = readUint32( &(*begin) );
+ preferred_ = readUint32(&(*begin), distance(begin, end));
begin += sizeof(uint32_t);
- valid_ = readUint32( &(*begin) );
+ valid_ = readUint32(&(*begin), distance(begin, end));
begin += sizeof(uint32_t);
prefix_len_ = *begin;
diff --git a/src/lib/dhcp/option_data_types.h b/src/lib/dhcp/option_data_types.h
index d9d8a52..fb4d101 100644
--- a/src/lib/dhcp/option_data_types.h
+++ b/src/lib/dhcp/option_data_types.h
@@ -298,12 +298,12 @@ public:
case 2:
// Calling readUint16 works either for unsigned
// or signed types.
- value = isc::util::readUint16(&(*buf.begin()));
+ value = isc::util::readUint16(&(*buf.begin()), buf.size());
break;
case 4:
// Calling readUint32 works either for unsigned
// or signed types.
- value = isc::util::readUint32(&(*buf.begin()));
+ value = isc::util::readUint32(&(*buf.begin()), buf.size());
break;
default:
// This should not happen because we made checks on data types
@@ -331,11 +331,11 @@ public:
break;
case 2:
buf.resize(buf.size() + 2);
- isc::util::writeUint16(static_cast<uint16_t>(value), &buf[buf.size() - 2]);
+ isc::util::writeUint16(static_cast<uint16_t>(value), &buf[buf.size() - 2], 2);
break;
case 4:
buf.resize(buf.size() + 4);
- isc::util::writeUint32(static_cast<uint32_t>(value), &buf[buf.size() - 4]);
+ isc::util::writeUint32(static_cast<uint32_t>(value), &buf[buf.size() - 4], 4);
break;
default:
// The cases above cover whole range of possible data lengths because
diff --git a/src/lib/dhcp/option_definition.cc b/src/lib/dhcp/option_definition.cc
index 78cb39b..98765e6 100644
--- a/src/lib/dhcp/option_definition.cc
+++ b/src/lib/dhcp/option_definition.cc
@@ -1,4 +1,4 @@
-// Copyright (C) 2012-2013 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2012-2014 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
@@ -399,18 +399,49 @@ OptionDefinition::haveVendor6Format() const {
return (getType() == OPT_UINT32_TYPE && !getEncapsulatedSpace().empty());
}
+bool
+OptionDefinition::convertToBool(const std::string& value_str) const {
+ // Case insensitve check that the input is one of: "true" or "false".
+ if (boost::iequals(value_str, "true")) {
+ return (true);
+
+ } else if (boost::iequals(value_str, "false")) {
+ return (false);
+
+ }
+
+ // The input string is neither "true" nor "false", so let's check
+ // if it is not an integer wrapped in a string.
+ int result;
+ try {
+ result = boost::lexical_cast<int>(value_str);
+
+ } catch (const boost::bad_lexical_cast&) {
+ isc_throw(BadDataTypeCast, "unable to covert the value '"
+ << value_str << "' to boolean data type");
+ }
+ // The boolean value is encoded in DHCP option as 0 or 1. Therefore,
+ // we only allow a user to specify those values for options which
+ // have boolean fields.
+ if (result != 1 && result != 0) {
+ isc_throw(BadDataTypeCast, "unable to convert '" << value_str
+ << "' to boolean data type");
+ }
+ return (static_cast<bool>(result));
+}
+
template<typename T>
T
OptionDefinition::lexicalCastWithRangeCheck(const std::string& value_str)
const {
- // Lexical cast in case of our data types make sense only
- // for uintX_t, intX_t and bool type.
- if (!OptionDataTypeTraits<T>::integer_type &&
- OptionDataTypeTraits<T>::type != OPT_BOOLEAN_TYPE) {
+ // The lexical cast should be attempted when converting to an integer
+ // value only.
+ if (!OptionDataTypeTraits<T>::integer_type) {
isc_throw(BadDataTypeCast,
- "unable to do lexical cast to non-integer and"
- << " non-boolean data type");
+ "must not convert '" << value_str
+ << "' to non-integer data type");
}
+
// We use the 64-bit value here because it has wider range than
// any other type we use here and it allows to detect out of
// bounds conditions e.g. negative value specified for uintX_t
@@ -419,23 +450,19 @@ OptionDefinition::lexicalCastWithRangeCheck(const std::string& value_str)
int64_t result = 0;
try {
result = boost::lexical_cast<int64_t>(value_str);
- } catch (const boost::bad_lexical_cast& ex) {
- // Prepare error message here.
- std::string data_type_str = "boolean";
- if (OptionDataTypeTraits<T>::integer_type) {
- data_type_str = "integer";
- }
- isc_throw(BadDataTypeCast, "unable to do lexical cast to "
- << data_type_str << " data type for value "
- << value_str << ": " << ex.what());
+
+ } catch (const boost::bad_lexical_cast&) {
+ isc_throw(BadDataTypeCast, "unable to convert the value '"
+ << value_str << "' to integer data type");
}
- // Perform range checks for integer values only (exclude bool values).
+ // Perform range checks.
if (OptionDataTypeTraits<T>::integer_type) {
if (result > numeric_limits<T>::max() ||
result < numeric_limits<T>::min()) {
- isc_throw(BadDataTypeCast, "unable to do lexical cast for value "
- << value_str << ". This value is expected to be"
- << " in the range of " << numeric_limits<T>::min()
+ isc_throw(BadDataTypeCast, "unable to convert '"
+ << value_str << "' to numeric type. This value is "
+ " expected to be in the range of "
+ << numeric_limits<T>::min()
<< ".." << numeric_limits<T>::max());
}
}
@@ -458,8 +485,7 @@ OptionDefinition::writeToBuffer(const std::string& value,
// That way we actually waste 7 bits but it seems to be the
// simpler way to encode boolean.
// @todo Consider if any other encode methods can be used.
- OptionDataTypeUtil::writeBool(lexicalCastWithRangeCheck<bool>(value),
- buf);
+ OptionDataTypeUtil::writeBool(convertToBool(value), buf);
return;
case OPT_INT8_TYPE:
OptionDataTypeUtil::writeInt<uint8_t>
diff --git a/src/lib/dhcp/option_definition.h b/src/lib/dhcp/option_definition.h
index 25ce5e2..73c0cdf 100644
--- a/src/lib/dhcp/option_definition.h
+++ b/src/lib/dhcp/option_definition.h
@@ -573,19 +573,38 @@ private:
return (type == type_);
}
+ /// @brief Converts a string value to a boolean value.
+ ///
+ /// This function converts the value represented as string to a boolean
+ /// value. The following conversions are acceptable:
+ /// - "true" => true
+ /// - "false" => false
+ /// - "1" => true
+ /// - "0" => false
+ /// The first two conversions are case insensitive, so as conversions from
+ /// strings such as "TRUE", "trUE" etc. will be accepted. Note that the
+ /// only acceptable integer values, carried as strings are: "0" and "1".
+ /// For other values, e.g. "2", "3" etc. an exception will be thrown
+ /// during conversion.
+ ///
+ /// @param value_str Input value.
+ ///
+ /// @return boolean representation of the string specified as the parameter.
+ /// @throw isc::dhcp::BadDataTypeCast if failed to perform the conversion.
+ bool convertToBool(const std::string& value_str) const;
+
/// @brief Perform lexical cast of the value and validate its range.
///
/// This function performs lexical cast of a string value to integer
- /// or boolean value and checks if the resulting value is within a
- /// range of a target type. Note that range checks are not performed
- /// on boolean values. The target type should be one of the supported
- /// integer types or bool.
+ /// value and checks if the resulting value is within a range of a
+ /// target type. The target type should be one of the supported
+ /// integer types.
///
/// @param value_str input value given as string.
- /// @tparam T target type for lexical cast.
+ /// @tparam T target integer type for lexical cast.
///
- /// @return cast value.
- /// @throw BadDataTypeCast if cast was not successful.
+ /// @return Integer value after conversion from the string.
+ /// @throw isc::dhcp::BadDataTypeCast if conversion was not successful.
template<typename T>
T lexicalCastWithRangeCheck(const std::string& value_str) const;
diff --git a/src/lib/dhcp/option_int.h b/src/lib/dhcp/option_int.h
index cbdbcb0..f11b4eb 100644
--- a/src/lib/dhcp/option_int.h
+++ b/src/lib/dhcp/option_int.h
@@ -142,10 +142,12 @@ public:
value_ = *begin;
break;
case 2:
- value_ = isc::util::readUint16(&(*begin));
+ value_ = isc::util::readUint16(&(*begin),
+ std::distance(begin, end));
break;
case 4:
- value_ = isc::util::readUint32(&(*begin));
+ value_ = isc::util::readUint32(&(*begin),
+ std::distance(begin, end));
break;
default:
isc_throw(dhcp::InvalidDataType, "non-integer type");
diff --git a/src/lib/dhcp/option_int_array.h b/src/lib/dhcp/option_int_array.h
index e0e9356..62e121b 100644
--- a/src/lib/dhcp/option_int_array.h
+++ b/src/lib/dhcp/option_int_array.h
@@ -201,10 +201,12 @@ public:
values_.push_back(*begin);
break;
case 2:
- values_.push_back(isc::util::readUint16(&(*begin)));
+ values_.push_back(isc::util::readUint16(&(*begin),
+ std::distance(begin, end)));
break;
case 4:
- values_.push_back(isc::util::readUint32(&(*begin)));
+ values_.push_back(isc::util::readUint32(&(*begin),
+ std::distance(begin, end)));
break;
default:
isc_throw(dhcp::InvalidDataType, "non-integer type");
diff --git a/src/lib/dhcp/option_vendor.cc b/src/lib/dhcp/option_vendor.cc
index 878b534..0d5c550 100644
--- a/src/lib/dhcp/option_vendor.cc
+++ b/src/lib/dhcp/option_vendor.cc
@@ -54,7 +54,7 @@ void OptionVendor::unpack(OptionBufferConstIter begin,
<< ", length=" << distance(begin, end));
}
- vendor_id_ = isc::util::readUint32(&(*begin));
+ vendor_id_ = isc::util::readUint32(&(*begin), distance(begin, end));
OptionBuffer vendor_buffer(begin +4, end);
diff --git a/src/lib/dhcp/pkt4.cc b/src/lib/dhcp/pkt4.cc
index b641a03..08107a5 100644
--- a/src/lib/dhcp/pkt4.cc
+++ b/src/lib/dhcp/pkt4.cc
@@ -1,4 +1,4 @@
-// Copyright (C) 2011-2013 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2011-2014 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
@@ -67,6 +67,7 @@ Pkt4::Pkt4(const uint8_t* data, size_t len)
remote_port_(DHCP4_CLIENT_PORT),
op_(BOOTREQUEST),
hwaddr_(new HWAddr()),
+ hops_(0),
transid_(0),
secs_(0),
flags_(0),
@@ -396,6 +397,7 @@ Pkt4::DHCPTypeToBootpType(uint8_t dhcpType) {
case DHCPRELEASE:
case DHCPINFORM:
case DHCPLEASEQUERY:
+ case DHCPBULKLEASEQUERY:
return (BOOTREQUEST);
case DHCPACK:
@@ -404,6 +406,7 @@ Pkt4::DHCPTypeToBootpType(uint8_t dhcpType) {
case DHCPLEASEUNASSIGNED:
case DHCPLEASEUNKNOWN:
case DHCPLEASEACTIVE:
+ case DHCPLEASEQUERYDONE:
return (BOOTREPLY);
default:
@@ -482,12 +485,12 @@ Pkt4::isRelayed() const {
"hops != 0)");
}
-bool Pkt4::inClass(const std::string& client_class) {
+bool Pkt4::inClass(const isc::dhcp::ClientClass& client_class) {
return (classes_.find(client_class) != classes_.end());
}
void
-Pkt4::addClass(const std::string& client_class) {
+Pkt4::addClass(const isc::dhcp::ClientClass& client_class) {
if (classes_.find(client_class) == classes_.end()) {
classes_.insert(client_class);
}
diff --git a/src/lib/dhcp/pkt4.h b/src/lib/dhcp/pkt4.h
index 485d28a..d5817b8 100644
--- a/src/lib/dhcp/pkt4.h
+++ b/src/lib/dhcp/pkt4.h
@@ -1,4 +1,4 @@
-// Copyright (C) 2011-2013 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2011-2014 Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
@@ -20,6 +20,7 @@
#include <util/buffer.h>
#include <dhcp/option.h>
#include <dhcp/hwaddr.h>
+#include <dhcp/classify.h>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/shared_ptr.hpp>
@@ -53,9 +54,6 @@ public:
/// to check whether client requested broadcast response.
const static uint16_t FLAG_BROADCAST_MASK = 0x8000;
- /// Container for storing client classes
- typedef std::set<std::string> Classes;
-
/// Constructor, used in replying to a message.
///
/// @param msg_type type of message (e.g. DHCPDISOVER=1)
@@ -558,7 +556,7 @@ public:
///
/// @param client_class name of the class
/// @return true if belongs
- bool inClass(const std::string& client_class);
+ bool inClass(const isc::dhcp::ClientClass& client_class);
/// @brief Adds packet to a specified class
///
@@ -575,7 +573,7 @@ public:
/// so I decided to stick with addClass().
///
/// @param client_class name of the class to be added
- void addClass(const std::string& client_class);
+ void addClass(const isc::dhcp::ClientClass& client_class);
/// @brief Classes this packet belongs to.
///
@@ -583,7 +581,7 @@ public:
/// existing classes. Having it public also solves the problem of returned
/// reference lifetime. It is preferred to use @ref inClass and @ref addClass
/// should be used to operate on this field.
- Classes classes_;
+ ClientClasses classes_;
private:
diff --git a/src/lib/dhcp/pkt6.h b/src/lib/dhcp/pkt6.h
index db80fb9..0e88e56 100644
--- a/src/lib/dhcp/pkt6.h
+++ b/src/lib/dhcp/pkt6.h
@@ -1,4 +1,4 @@
-// Copyright (C) 2011-2013 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2011-2014 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,6 +17,7 @@
#include <asiolink/io_address.h>
#include <dhcp/option.h>
+#include <dhcp/classify.h>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/shared_array.hpp>
@@ -48,9 +49,6 @@ public:
TCP = 1 // there are TCP DHCPv6 packets (bulk leasequery, failover)
};
- /// Container for storing client classes
- typedef std::set<std::string> Classes;
-
/// @brief defines relay search pattern
///
/// Defines order in which options are searched in a message that
@@ -457,7 +455,7 @@ public:
///
/// This field is public, so code can iterate over existing classes.
/// Having it public also solves the problem of returned reference lifetime.
- Classes classes_;
+ ClientClasses classes_;
protected:
/// Builds on wire packet for TCP transmission.
diff --git a/src/lib/dhcp/std_option_defs.h b/src/lib/dhcp/std_option_defs.h
index 6611f19..31723c1 100644
--- a/src/lib/dhcp/std_option_defs.h
+++ b/src/lib/dhcp/std_option_defs.h
@@ -19,6 +19,9 @@
#include <dhcp/dhcp4.h>
#include <dhcp/dhcp6.h>
+namespace isc {
+namespace dhcp {
+
namespace {
/// @brief Declare an array holding parameters used to create instance
@@ -42,8 +45,6 @@ namespace {
#define NO_RECORD_DEF 0, 0
#endif
-using namespace isc::dhcp;
-
/// @brief Parameters being used to make up an option definition.
struct OptionDefParams {
const char* name; // option name
@@ -332,6 +333,9 @@ const OptionDefParams OPTION_DEF_PARAMS6[] = {
const int OPTION_DEF_PARAMS_SIZE6 =
sizeof(OPTION_DEF_PARAMS6) / sizeof(OPTION_DEF_PARAMS6[0]);
-}; // anonymous namespace
+} // unnamed namespace
+
+} // namespace dhcp
+} // namespace isc
#endif // STD_OPTION_DEFS_H
diff --git a/src/lib/dhcp/tests/Makefile.am b/src/lib/dhcp/tests/Makefile.am
index 6b3683d..7e08bea 100644
--- a/src/lib/dhcp/tests/Makefile.am
+++ b/src/lib/dhcp/tests/Makefile.am
@@ -24,11 +24,30 @@ TESTS_ENVIRONMENT = \
TESTS =
if HAVE_GTEST
+
+# Creates a library which is shared by various unit tests which require
+# configuration of fake interfaces.
+# The libdhcp++ does not link with this library because this would cause
+# build failures being a result of concurrency between build of this
+# library and the unit tests when make -j option was used, as they
+# are built out of the same makefile. Instead, the libdhcp++ tests link to
+# files belonging to this library, directly.
+noinst_LTLIBRARIES = libdhcptest.la
+
+libdhcptest_la_SOURCES = iface_mgr_test_config.cc iface_mgr_test_config.h
+libdhcptest_la_SOURCES += pkt_filter_test_stub.cc pkt_filter_test_stub.h
+libdhcptest_la_CXXFLAGS = $(AM_CXXFLAGS)
+libdhcptest_la_CPPFLAGS = $(AM_CPPFLAGS)
+libdhcptest_la_LDFLAGS = $(AM_LDFLAGS)
+libdhcptest_la_LIBADD = $(top_builddir)/src/lib/dhcp/libb10-dhcp++.la
+
TESTS += libdhcp++_unittests
libdhcp___unittests_SOURCES = run_unittests.cc
+libdhcp___unittests_SOURCES += classify_unittest.cc
libdhcp___unittests_SOURCES += hwaddr_unittest.cc
libdhcp___unittests_SOURCES += iface_mgr_unittest.cc
+libdhcp___unittests_SOURCES += iface_mgr_test_config.cc iface_mgr_test_config.h
libdhcp___unittests_SOURCES += libdhcp++_unittest.cc
libdhcp___unittests_SOURCES += option4_addrlst_unittest.cc
libdhcp___unittests_SOURCES += option4_client_fqdn_unittest.cc
@@ -51,6 +70,7 @@ libdhcp___unittests_SOURCES += pkt6_unittest.cc
libdhcp___unittests_SOURCES += pkt_filter_unittest.cc
libdhcp___unittests_SOURCES += pkt_filter_inet_unittest.cc
libdhcp___unittests_SOURCES += pkt_filter_inet6_unittest.cc
+libdhcp___unittests_SOURCES += pkt_filter_test_stub.cc pkt_filter_test_stub.h
libdhcp___unittests_SOURCES += pkt_filter_test_utils.h pkt_filter_test_utils.cc
libdhcp___unittests_SOURCES += pkt_filter6_test_utils.h pkt_filter6_test_utils.cc
diff --git a/src/lib/dhcp/tests/classify_unittest.cc b/src/lib/dhcp/tests/classify_unittest.cc
new file mode 100644
index 0000000..cf978ce
--- /dev/null
+++ b/src/lib/dhcp/tests/classify_unittest.cc
@@ -0,0 +1,52 @@
+// Copyright (C) 2011-2013 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 <dhcp/classify.h>
+#include <gtest/gtest.h>
+
+using namespace isc::dhcp;
+
+// Trivial test for now as ClientClass is a std::string.
+TEST(ClassifyTest, ClientClass) {
+
+ ClientClass x("foo");
+ EXPECT_EQ("foo", x);
+
+ x = "baz";
+ EXPECT_EQ("baz", x);
+}
+
+// Checks if ClientClasses object is able to store classes' names and then
+// properly return values of contains() method.
+TEST(ClassifyTest, ClientClasses) {
+ ClientClasses classes;
+
+ EXPECT_FALSE(classes.contains(""));
+ EXPECT_FALSE(classes.contains("alpha"));
+ EXPECT_FALSE(classes.contains("beta"));
+ EXPECT_FALSE(classes.contains("gamma"));
+
+ classes.insert("beta");
+ EXPECT_FALSE(classes.contains(""));
+ EXPECT_FALSE(classes.contains("alpha"));
+ EXPECT_TRUE (classes.contains("beta"));
+ EXPECT_FALSE(classes.contains("gamma"));
+
+ classes.insert("alpha");
+ classes.insert("gamma");
+ EXPECT_TRUE (classes.contains("alpha"));
+ EXPECT_TRUE (classes.contains("beta"));
+ EXPECT_TRUE (classes.contains("gamma"));
+}
diff --git a/src/lib/dhcp/tests/iface_mgr_test_config.cc b/src/lib/dhcp/tests/iface_mgr_test_config.cc
new file mode 100644
index 0000000..8da7e6e
--- /dev/null
+++ b/src/lib/dhcp/tests/iface_mgr_test_config.cc
@@ -0,0 +1,137 @@
+// Copyright (C) 2014 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 <dhcp/pkt_filter.h>
+#include <dhcp/pkt_filter_inet.h>
+#include <dhcp/tests/iface_mgr_test_config.h>
+#include <dhcp/tests/pkt_filter_test_stub.h>
+
+using namespace isc::asiolink;
+
+namespace isc {
+namespace dhcp {
+namespace test {
+
+IfaceMgrTestConfig::IfaceMgrTestConfig(const bool default_config) {
+ IfaceMgr::instance().closeSockets();
+ IfaceMgr::instance().clearIfaces();
+ packet_filter4_ = PktFilterPtr(new PktFilterTestStub());
+ IfaceMgr::instance().setPacketFilter(packet_filter4_);
+
+ // Create default set of fake interfaces: lo, eth0 and eth1.
+ if (default_config) {
+ createIfaces();
+ }
+}
+
+IfaceMgrTestConfig::~IfaceMgrTestConfig() {
+ IfaceMgr::instance().closeSockets();
+ IfaceMgr::instance().clearIfaces();
+ IfaceMgr::instance().setPacketFilter(PktFilterPtr(new PktFilterInet()));
+
+ IfaceMgr::instance().detectIfaces();
+}
+
+void
+IfaceMgrTestConfig::addAddress(const std::string& iface_name,
+ const IOAddress& address) {
+ Iface* iface = IfaceMgr::instance().getIface(iface_name);
+ if (iface == NULL) {
+ isc_throw(isc::BadValue, "interface '" << iface_name
+ << "' doesn't exist");
+ }
+ iface->addAddress(address);
+}
+
+void
+IfaceMgrTestConfig::addIface(const Iface& iface) {
+ IfaceMgr::instance().addInterface(iface);
+}
+
+void
+IfaceMgrTestConfig::addIface(const std::string& name, const int ifindex) {
+ IfaceMgr::instance().addInterface(createIface(name, ifindex));
+}
+
+Iface
+IfaceMgrTestConfig::createIface(const std::string &name, const int ifindex) {
+ Iface iface(name, ifindex);
+ if (name == "lo") {
+ iface.flag_loopback_ = true;
+ }
+ iface.flag_multicast_ = true;
+ // On BSD systems, the SO_BINDTODEVICE option is not supported.
+ // Therefore the IfaceMgr will throw an exception on attempt to
+ // open sockets on more than one broadcast-capable interface at
+ // the same time. In order to prevent this error, we mark all
+ // interfaces broadcast-incapable for unit testing.
+ iface.flag_broadcast_ = false;
+ iface.flag_up_ = true;
+ iface.flag_running_ = true;
+ iface.inactive4_ = false;
+ iface.inactive6_ = false;
+ return (iface);
+}
+
+void
+IfaceMgrTestConfig::createIfaces() {
+ // local loopback
+ addIface("lo", 0);
+ addAddress("lo", IOAddress("127.0.0.1"));
+ addAddress("lo", IOAddress("::1"));
+ // eth0
+ addIface("eth0", 1);
+ addAddress("eth0", IOAddress("10.0.0.1"));
+ addAddress("eth0", IOAddress("fe80::3a60:77ff:fed5:cdef"));
+ addAddress("eth0", IOAddress("2001:db8:1::1"));
+ // eth1
+ addIface("eth1", 2);
+ addAddress("eth1", IOAddress("192.0.2.3"));
+ addAddress("eth1", IOAddress("fe80::3a60:77ff:fed5:abcd"));
+
+}
+
+void
+IfaceMgrTestConfig::setDirectResponse(const bool direct_resp) {
+ boost::shared_ptr<PktFilterTestStub> stub =
+ boost::dynamic_pointer_cast<PktFilterTestStub>(getPacketFilter4());
+ if (!stub) {
+ isc_throw(isc::Unexpected, "unable to set direct response capability for"
+ " test packet filter - current packet filter is not"
+ " of a PktFilterTestStub");
+ }
+ stub->direct_response_supported_ = direct_resp;
+}
+
+void
+IfaceMgrTestConfig::setIfaceFlags(const std::string& name,
+ const FlagLoopback& loopback,
+ const FlagUp& up,
+ const FlagRunning& running,
+ const FlagInactive4& inactive4,
+ const FlagInactive6& inactive6) {
+ Iface* iface = IfaceMgr::instance().getIface(name);
+ if (iface == NULL) {
+ isc_throw(isc::BadValue, "interface '" << name << "' doesn't exist");
+ }
+ iface->flag_loopback_ = loopback.flag_;
+ iface->flag_up_ = up.flag_;
+ iface->flag_running_ = running.flag_;
+ iface->inactive4_ = inactive4.flag_;
+ iface->inactive6_ = inactive6.flag_;
+}
+
+}
+}
+}
diff --git a/src/lib/dhcp/tests/iface_mgr_test_config.h b/src/lib/dhcp/tests/iface_mgr_test_config.h
new file mode 100644
index 0000000..a2ceba5
--- /dev/null
+++ b/src/lib/dhcp/tests/iface_mgr_test_config.h
@@ -0,0 +1,241 @@
+// Copyright (C) 2014 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 IFACE_MGR_TEST_CONFIG_H
+#define IFACE_MGR_TEST_CONFIG_H
+
+#include <asiolink/io_address.h>
+#include <dhcp/iface_mgr.h>
+#include <boost/noncopyable.hpp>
+
+namespace isc {
+namespace dhcp {
+namespace test {
+
+///
+/// @name Set of structures describing interface flags.
+///
+/// These flags encapsulate the boolean type to pass the flags values
+/// to @c IfaceMgrTestConfig methods. If the values passed to these methods
+/// were not encapsulated by the types defined here, the API would become
+/// prone to errors like swapping parameters being passed to specific functions.
+/// For example, in the call to @c IfaceMgrTestConfig::setIfaceFlags:
+/// @code
+/// IfaceMgrTestConfig test_config(true);
+/// test_config.setIfaceFlags("eth1", false, false, true, false, false);
+/// @endcode
+///
+/// it is quite likely that the developer by mistake swaps the values and
+/// assigns them to wrong flags. When the flags are encapsulated with dedicated
+/// structs, the compiler will return an error if values are swapped. For
+/// example:
+/// @code
+/// IfaceMgrTestConfig test_config(true);
+/// test_config.setIfaceFlags("eth1", FlagLoopback(false), FlagUp(false),
+/// FlagRunning(true), FlagInactive4(false),
+/// FlagInactive6(false));
+/// @endcode
+/// will succeed, but the following code will result in the compilation error
+/// and thus protect a developer from making an error:
+/// @code
+/// IfaceMgrTestConfig test_config(true);
+/// test_config.setIfaceFlags("eth1", FlagLoopback(false),
+/// FlagRunning(true), FlagUp(false),
+/// FlagInactive4(false), FlagInactive6(false));
+/// @endcode
+///
+//@{
+/// @brief Structure describing the loopback interface flag.
+struct FlagLoopback {
+ explicit FlagLoopback(bool flag) : flag_(flag) { }
+ bool flag_;
+};
+
+/// @brief Structure describing the up interface flag.
+struct FlagUp {
+ explicit FlagUp(bool flag) : flag_(flag) { }
+ bool flag_;
+};
+
+/// @brief Structure describing the running interface flag.
+struct FlagRunning {
+ explicit FlagRunning(bool flag) : flag_(flag) { }
+ bool flag_;
+};
+
+/// @brief Structure describing the inactive4 interface flag.
+struct FlagInactive4 {
+ explicit FlagInactive4(bool flag) : flag_(flag) { }
+ bool flag_;
+};
+
+/// @brief Structure describing the inactive6 interface flag.
+struct FlagInactive6 {
+ explicit FlagInactive6(bool flag) : flag_(flag) { }
+ bool flag_;
+};
+//@}
+
+/// @brief Convenience class for configuring @c IfaceMgr for unit testing.
+///
+/// This class is used by various unit tests which test the code relaying
+/// on IfaceMgr. The use of this class is not limited to libdhcp++ validation.
+/// There are other libraries and applications (e.g. DHCP servers) which
+/// depend on @c IfaceMgr.
+///
+/// During the normal operation, the @c IfaceMgr detects interfaces present
+/// on the machine where it is running. It also provides the means for
+/// applications to open sockets on these interfaces and perform other
+/// IO operations. This however creates dependency of the applications
+/// using @c IfaceMgr on the physical properties of the system and effectively
+/// makes it very hard to unit test the dependent code.
+///
+/// Unit tests usually require that @c IfaceMgr holds a list of well known
+/// interfaces with the well known set of IP addresses and other properties
+/// (a.k.a. interface flags). The solution which works for many test scenarios
+/// is to provide a set of well known fake interfaces, by bypassing the
+/// standard interface detection procedure and manually adding @c Iface objects
+/// which encapsulate the fake interfaces. As a consequence, it becomes
+/// impossible to test IO operations (e.g. sending packets) because real sockets
+/// can't be opened on these interfaces. The @c PktFilterTestStub class
+/// is used by this class to mimic behavior of IO operations on fake sockets.
+///
+/// This class provides a set of convenience functions that should be called
+/// by unit tests to configure the @c IfaceMgr with fake interfaces.
+///
+/// The class allows the caller to create custom fake interfaces (with custom
+/// IPv4 and IPv6 addresses, flags etc.), but it also provides a default
+/// test configuration for interfaces as follows:
+/// - lo
+/// - 127.0.0.1
+/// - ::1
+/// - eth0
+/// - 10.0.0.1
+/// - fe80::3a60:77ff:fed5:cdef
+/// - 2001:db8:1::1
+/// - eth1
+/// - 192.0.2.3
+/// - fe80::3a60:77ff:fed5:abcd
+///
+/// For all interfaces the following flags are set:
+/// - multicast
+/// - up
+/// - running
+class IfaceMgrTestConfig : public boost::noncopyable {
+public:
+
+ /// @brief Constructor.
+ ///
+ /// It closes all sockets opened by @c IfaceMgr and removes all interfaces
+ /// being used by @c IfaceMgr.
+ IfaceMgrTestConfig(const bool default_config = false);
+
+ /// @brief Destructor.
+ ///
+ /// Closes all currently opened sockets, removes current interfaces and
+ /// sets the default packet filtering classes. The default packet filtering
+ /// classes are used for IO operations on real sockets/interfaces.
+ ///
+ /// Destructor also re-detects real interfaces.
+ ~IfaceMgrTestConfig();
+
+ /// @brief Adds new IPv4 or IPv6 address to the interface.
+ ///
+ /// @param iface_name Name of the interface on which new address should
+ /// be configured.
+ /// @param address IPv4 or IPv6 address to be configured on the interface.
+ void addAddress(const std::string& iface_name,
+ const asiolink::IOAddress& address);
+
+ /// @brief Configures new interface for the @c IfaceMgr.
+ ///
+ /// @param iface Object encapsulating interface to be added.
+ void addIface(const Iface& iface);
+
+ /// @brief Configures new interface for the @c IfaceMgr.
+ ///
+ /// @param name Name of the new interface.
+ /// @param ifindex Index for a new interface.
+ void addIface(const std::string& name, const int ifindex);
+
+ /// @brief Create an object representing interface.
+ ///
+ /// Apart from creating an interface, this function also sets the
+ /// interface flags:
+ /// - loopback flag if interface name is "lo"
+ /// - up always true
+ /// - running always true
+ /// - inactive always to false
+ /// - multicast always to true
+ /// - broadcast always to false
+ ///
+ /// If one needs to modify the default flag settings, the setIfaceFlags
+ /// function should be used.
+ ///
+ /// @param name A name of the interface to be created.
+ /// @param ifindex An index of the interface to be created.
+ ///
+ /// @return An object representing interface.
+ static Iface createIface(const std::string& name, const int ifindex);
+
+ /// @brief Creates a default (example) set of fake interfaces.
+ void createIfaces();
+
+ /// @brief Returns currently used packet filter for DHCPv4.
+ PktFilterPtr getPacketFilter4() const {
+ return (packet_filter4_);
+ }
+
+ /// @brief Sets the direct response capability for current packet filter.
+ ///
+ /// The test uses stub implementation of packet filter object. It is
+ /// possible to configure that object to report having a capability
+ /// to directly repond to clients which don't have an address yet.
+ /// This function sets this property for packet filter object.
+ ///
+ /// @param direct_resp Value to be set.
+ ///
+ /// @throw isc::Unexpected if unable to set the property.
+ void setDirectResponse(const bool direct_resp);
+
+ /// @brief Sets various flags on the specified interface.
+ ///
+ /// This function configures interface with new values for flags.
+ ///
+ /// @param name Interface name.
+ /// @param loopback Specifies if interface is a loopback interface.
+ /// @param up Specifies if the interface is up.
+ /// @param running Specifies if the interface is running.
+ /// @param inactive4 Specifies if the interface is inactive for V4
+ /// traffic, i.e. @c IfaceMgr opens V4 sockets on this interface.
+ /// @param inactive6 Specifies if the interface is inactive for V6
+ /// traffic, i.e. @c IfaceMgr opens V6 sockets on this interface.
+ void setIfaceFlags(const std::string& name,
+ const FlagLoopback& loopback,
+ const FlagUp& up,
+ const FlagRunning& running,
+ const FlagInactive4& inactive4,
+ const FlagInactive6& inactive6);
+
+private:
+ /// @brief Currently used packet filter for DHCPv4.
+ PktFilterPtr packet_filter4_;
+
+};
+
+};
+};
+};
+
+#endif // IFACE_MGR_TEST_CONFIG_H
diff --git a/src/lib/dhcp/tests/iface_mgr_unittest.cc b/src/lib/dhcp/tests/iface_mgr_unittest.cc
index dc5d85b..c8cd195 100644
--- a/src/lib/dhcp/tests/iface_mgr_unittest.cc
+++ b/src/lib/dhcp/tests/iface_mgr_unittest.cc
@@ -19,6 +19,7 @@
#include <dhcp/iface_mgr.h>
#include <dhcp/pkt6.h>
#include <dhcp/pkt_filter.h>
+#include <dhcp/tests/iface_mgr_test_config.h>
#include <dhcp/tests/pkt_filter6_test_utils.h>
#include <boost/bind.hpp>
@@ -529,6 +530,32 @@ TEST_F(IfaceMgrTest, ifaceClass) {
EXPECT_STREQ("eth5/7", iface.getFullName().c_str());
}
+// Test that the IPv4 address can be retrieved for the interface.
+TEST_F(IfaceMgrTest, ifaceGetAddress) {
+ Iface iface("eth0", 0);
+
+ IOAddress addr("::1");
+ // Initially, the Iface has no addresses assigned.
+ EXPECT_FALSE(iface.getAddress4(addr));
+ // Add some addresses with IPv4 address in the middle.
+ iface.addAddress(IOAddress("fe80::3a60:77ff:fed5:cdef"));
+ iface.addAddress(IOAddress("10.1.2.3"));
+ iface.addAddress(IOAddress("2001:db8:1::2"));
+ // The v4 address should be returned.
+ EXPECT_TRUE(iface.getAddress4(addr));
+ EXPECT_EQ("10.1.2.3", addr.toText());
+ // Delete the IPv4 address and leave only two IPv6 addresses.
+ ASSERT_NO_THROW(iface.delAddress(IOAddress("10.1.2.3")));
+ // The IPv4 address should not be returned.
+ EXPECT_FALSE(iface.getAddress4(addr));
+ // Add a different IPv4 address at the end of the list.
+ iface.addAddress(IOAddress("192.0.2.3"));
+ // This new address should now be returned.
+ EXPECT_TRUE(iface.getAddress4(addr));
+ EXPECT_EQ("192.0.2.3", addr.toText());
+
+}
+
// TODO: Implement getPlainMac() test as soon as interface detection
// is implemented.
TEST_F(IfaceMgrTest, getIface) {
@@ -1381,31 +1408,29 @@ TEST_F(IfaceMgrTest, openSockets4) {
// This test verifies that the socket is not open on the interface which is
// down, but sockets are open on all other non-loopback interfaces.
TEST_F(IfaceMgrTest, openSockets4IfaceDown) {
- NakedIfaceMgr ifacemgr;
-
- // Remove all real interfaces and create a set of dummy interfaces.
- ifacemgr.createIfaces();
-
- boost::shared_ptr<TestPktFilter> custom_packet_filter(new TestPktFilter());
- ASSERT_TRUE(custom_packet_filter);
- ASSERT_NO_THROW(ifacemgr.setPacketFilter(custom_packet_filter));
+ IfaceMgrTestConfig config(true);
// Boolean parameters specify that eth0 is:
// - not a loopback
// - is "down" (not up)
// - is not running
// - is active (is not inactive)
- ifacemgr.setIfaceFlags("eth0", false, false, true, false, false);
- ASSERT_FALSE(ifacemgr.getIface("eth0")->flag_up_);
- ASSERT_NO_THROW(ifacemgr.openSockets4(DHCP4_SERVER_PORT, true, NULL));
+ config.setIfaceFlags("eth0", FlagLoopback(false), FlagUp(false),
+ FlagRunning(false), FlagInactive4(false),
+ FlagInactive6(false));
+ ASSERT_FALSE(IfaceMgr::instance().getIface("eth0")->flag_up_);
+ ASSERT_NO_THROW(IfaceMgr::instance().openSockets4(DHCP4_SERVER_PORT, true,
+ NULL));
// There should be no socket on eth0 open, because interface was down.
- EXPECT_TRUE(ifacemgr.getIface("eth0")->getSockets().empty());
+ EXPECT_TRUE(IfaceMgr::instance().getIface("eth0")->getSockets().empty());
+
// Expecting that the socket is open on eth1 because it was up, running
// and active.
- EXPECT_EQ(1, ifacemgr.getIface("eth1")->getSockets().size());
+ EXPECT_EQ(1, IfaceMgr::instance().getIface("eth1")->getSockets().size());
// Never open socket on loopback interface.
- EXPECT_TRUE(ifacemgr.getIface("lo")->getSockets().empty());
+ EXPECT_TRUE(IfaceMgr::instance().getIface("lo")->getSockets().empty());
+
}
// This test verifies that the socket is not open on the interface which is
@@ -2590,15 +2615,19 @@ TEST_F(IfaceMgrTest, detectIfaces) {
}
volatile bool callback_ok;
+volatile bool callback2_ok;
void my_callback(void) {
- cout << "Callback triggered." << endl;
callback_ok = true;
}
-TEST_F(IfaceMgrTest, controlSession) {
- // Tests if extra control socket and its callback can be passed and
- // it is supported properly by receive4() method.
+void my_callback2(void) {
+ callback2_ok = true;
+}
+
+// Tests if a single external socket and its callback can be passed and
+// it is supported properly by receive4() method.
+TEST_F(IfaceMgrTest, SingleExternalSocket4) {
callback_ok = false;
@@ -2607,7 +2636,7 @@ TEST_F(IfaceMgrTest, controlSession) {
// Create pipe and register it as extra socket
int pipefd[2];
EXPECT_TRUE(pipe(pipefd) == 0);
- EXPECT_NO_THROW(ifacemgr->set_session_socket(pipefd[0], my_callback));
+ EXPECT_NO_THROW(ifacemgr->addExternalSocket(pipefd[0], my_callback));
Pkt4Ptr pkt4;
ASSERT_NO_THROW(pkt4 = ifacemgr->receive4(1));
@@ -2635,6 +2664,305 @@ TEST_F(IfaceMgrTest, controlSession) {
close(pipefd[0]);
}
+// Tests if multiple external sockets and their callbacks can be passed and
+// it is supported properly by receive4() method.
+TEST_F(IfaceMgrTest, MiltipleExternalSockets4) {
+
+ callback_ok = false;
+ callback2_ok = false;
+
+ scoped_ptr<NakedIfaceMgr> ifacemgr(new NakedIfaceMgr());
+
+ // Create first pipe and register it as extra socket
+ int pipefd[2];
+ EXPECT_TRUE(pipe(pipefd) == 0);
+ EXPECT_NO_THROW(ifacemgr->addExternalSocket(pipefd[0], my_callback));
+
+ // Let's create a second pipe and register it as well
+ int secondpipe[2];
+ EXPECT_TRUE(pipe(secondpipe) == 0);
+ EXPECT_NO_THROW(ifacemgr->addExternalSocket(secondpipe[0], my_callback2));
+
+ Pkt4Ptr pkt4;
+ ASSERT_NO_THROW(pkt4 = ifacemgr->receive4(1));
+
+ // Our callbacks should not be called this time (there was no data)
+ EXPECT_FALSE(callback_ok);
+ EXPECT_FALSE(callback2_ok);
+
+ // IfaceMgr should not process control socket data as incoming packets
+ EXPECT_FALSE(pkt4);
+
+ // Now, send some data over the first pipe (38 bytes)
+ EXPECT_EQ(38, write(pipefd[1], "Hi, this is a message sent over a pipe", 38));
+
+ // ... and repeat
+ ASSERT_NO_THROW(pkt4 = ifacemgr->receive4(1));
+
+ // IfaceMgr should not process control socket data as incoming packets
+ EXPECT_FALSE(pkt4);
+
+ // There was some data, so this time callback should be called
+ EXPECT_TRUE(callback_ok);
+ EXPECT_FALSE(callback2_ok);
+
+ // Read the data sent, because our test callbacks are too dumb to actually
+ // do it. We don't care about the content read, because we're testing
+ // the callbacks, not pipes.
+ char buf[80];
+ EXPECT_EQ(38, read(pipefd[0], buf, 80));
+
+ // Clear the status...
+ callback_ok = false;
+ callback2_ok = false;
+
+ // And try again, using the second pipe
+ EXPECT_EQ(38, write(secondpipe[1], "Hi, this is a message sent over a pipe", 38));
+
+ // ... and repeat
+ ASSERT_NO_THROW(pkt4 = ifacemgr->receive4(1));
+
+ // IfaceMgr should not process control socket data as incoming packets
+ EXPECT_FALSE(pkt4);
+
+ // There was some data, so this time callback should be called
+ EXPECT_FALSE(callback_ok);
+ EXPECT_TRUE(callback2_ok);
+
+ // close both pipe ends
+ close(pipefd[1]);
+ close(pipefd[0]);
+
+ close(secondpipe[1]);
+ close(secondpipe[0]);
+}
+
+// Tests if existing external socket can be deleted and that such deletion does
+// not affect any other existing sockets. Tests uses receive4()
+TEST_F(IfaceMgrTest, DeleteExternalSockets4) {
+
+ callback_ok = false;
+ callback2_ok = false;
+
+ scoped_ptr<NakedIfaceMgr> ifacemgr(new NakedIfaceMgr());
+
+ // Create first pipe and register it as extra socket
+ int pipefd[2];
+ EXPECT_TRUE(pipe(pipefd) == 0);
+ EXPECT_NO_THROW(ifacemgr->addExternalSocket(pipefd[0], my_callback));
+
+ // Let's create a second pipe and register it as well
+ int secondpipe[2];
+ EXPECT_TRUE(pipe(secondpipe) == 0);
+ EXPECT_NO_THROW(ifacemgr->addExternalSocket(secondpipe[0], my_callback2));
+
+ // Now delete the first session socket
+ EXPECT_NO_THROW(ifacemgr->deleteExternalSocket(pipefd[0]));
+
+ // Now check whether the second callback is still functional
+ EXPECT_EQ(38, write(secondpipe[1], "Hi, this is a message sent over a pipe", 38));
+
+ // ... and repeat
+ Pkt4Ptr pkt4;
+ ASSERT_NO_THROW(pkt4 = ifacemgr->receive4(1));
+
+ // IfaceMgr should not process control socket data as incoming packets
+ EXPECT_FALSE(pkt4);
+
+ // There was some data, so this time callback should be called
+ EXPECT_FALSE(callback_ok);
+ EXPECT_TRUE(callback2_ok);
+
+ // Let's reset the status
+ callback_ok = false;
+ callback2_ok = false;
+
+ // Now let's send something over the first callback that was unregistered.
+ // We should NOT receive any callback.
+ EXPECT_EQ(38, write(pipefd[1], "Hi, this is a message sent over a pipe", 38));
+
+ // Now check that the first callback is NOT called.
+ ASSERT_NO_THROW(pkt4 = ifacemgr->receive4(1));
+ EXPECT_FALSE(callback_ok);
+
+ // close both pipe ends
+ close(pipefd[1]);
+ close(pipefd[0]);
+
+ close(secondpipe[1]);
+ close(secondpipe[0]);
+}
+
+
+// Tests if a single external socket and its callback can be passed and
+// it is supported properly by receive6() method.
+TEST_F(IfaceMgrTest, SingleExternalSocket6) {
+
+ callback_ok = false;
+
+ scoped_ptr<NakedIfaceMgr> ifacemgr(new NakedIfaceMgr());
+
+ // Create pipe and register it as extra socket
+ int pipefd[2];
+ EXPECT_TRUE(pipe(pipefd) == 0);
+ EXPECT_NO_THROW(ifacemgr->addExternalSocket(pipefd[0], my_callback));
+
+ Pkt6Ptr pkt6;
+ ASSERT_NO_THROW(pkt6 = ifacemgr->receive6(1));
+
+ // Our callback should not be called this time (there was no data)
+ EXPECT_FALSE(callback_ok);
+
+ // IfaceMgr should not process control socket data as incoming packets
+ EXPECT_FALSE(pkt6);
+
+ // Now, send some data over pipe (38 bytes)
+ EXPECT_EQ(38, write(pipefd[1], "Hi, this is a message sent over a pipe", 38));
+
+ // ... and repeat
+ ASSERT_NO_THROW(pkt6 = ifacemgr->receive6(1));
+
+ // IfaceMgr should not process control socket data as incoming packets
+ EXPECT_FALSE(pkt6);
+
+ // There was some data, so this time callback should be called
+ EXPECT_TRUE(callback_ok);
+
+ // close both pipe ends
+ close(pipefd[1]);
+ close(pipefd[0]);
+}
+
+// Tests if multiple external sockets and their callbacks can be passed and
+// it is supported properly by receive6() method.
+TEST_F(IfaceMgrTest, MiltipleExternalSockets6) {
+
+ callback_ok = false;
+ callback2_ok = false;
+
+ scoped_ptr<NakedIfaceMgr> ifacemgr(new NakedIfaceMgr());
+
+ // Create first pipe and register it as extra socket
+ int pipefd[2];
+ EXPECT_TRUE(pipe(pipefd) == 0);
+ EXPECT_NO_THROW(ifacemgr->addExternalSocket(pipefd[0], my_callback));
+
+ // Let's create a second pipe and register it as well
+ int secondpipe[2];
+ EXPECT_TRUE(pipe(secondpipe) == 0);
+ EXPECT_NO_THROW(ifacemgr->addExternalSocket(secondpipe[0], my_callback2));
+
+ Pkt6Ptr pkt6;
+ ASSERT_NO_THROW(pkt6 = ifacemgr->receive6(1));
+
+ // Our callbacks should not be called this time (there was no data)
+ EXPECT_FALSE(callback_ok);
+ EXPECT_FALSE(callback2_ok);
+
+ // IfaceMgr should not process control socket data as incoming packets
+ EXPECT_FALSE(pkt6);
+
+ // Now, send some data over the first pipe (38 bytes)
+ EXPECT_EQ(38, write(pipefd[1], "Hi, this is a message sent over a pipe", 38));
+
+ // ... and repeat
+ ASSERT_NO_THROW(pkt6 = ifacemgr->receive6(1));
+
+ // IfaceMgr should not process control socket data as incoming packets
+ EXPECT_FALSE(pkt6);
+
+ // There was some data, so this time callback should be called
+ EXPECT_TRUE(callback_ok);
+ EXPECT_FALSE(callback2_ok);
+
+ // Read the data sent, because our test callbacks are too dumb to actually
+ // do it. We don't care about the content read, because we're testing
+ // the callbacks, not pipes.
+ char buf[80];
+ EXPECT_EQ(38, read(pipefd[0], buf, 80));
+
+ // Clear the status...
+ callback_ok = false;
+ callback2_ok = false;
+
+ // And try again, using the second pipe
+ EXPECT_EQ(38, write(secondpipe[1], "Hi, this is a message sent over a pipe", 38));
+
+ // ... and repeat
+ ASSERT_NO_THROW(pkt6 = ifacemgr->receive6(1));
+
+ // IfaceMgr should not process control socket data as incoming packets
+ EXPECT_FALSE(pkt6);
+
+ // There was some data, so this time callback should be called
+ EXPECT_FALSE(callback_ok);
+ EXPECT_TRUE(callback2_ok);
+
+ // close both pipe ends
+ close(pipefd[1]);
+ close(pipefd[0]);
+
+ close(secondpipe[1]);
+ close(secondpipe[0]);
+}
+
+// Tests if existing external socket can be deleted and that such deletion does
+// not affect any other existing sockets. Tests uses receive6()
+TEST_F(IfaceMgrTest, DeleteExternalSockets6) {
+
+ callback_ok = false;
+ callback2_ok = false;
+
+ scoped_ptr<NakedIfaceMgr> ifacemgr(new NakedIfaceMgr());
+
+ // Create first pipe and register it as extra socket
+ int pipefd[2];
+ EXPECT_TRUE(pipe(pipefd) == 0);
+ EXPECT_NO_THROW(ifacemgr->addExternalSocket(pipefd[0], my_callback));
+
+ // Let's create a second pipe and register it as well
+ int secondpipe[2];
+ EXPECT_TRUE(pipe(secondpipe) == 0);
+ EXPECT_NO_THROW(ifacemgr->addExternalSocket(secondpipe[0], my_callback2));
+
+ // Now delete the first session socket
+ EXPECT_NO_THROW(ifacemgr->deleteExternalSocket(pipefd[0]));
+
+ // Now check whether the second callback is still functional
+ EXPECT_EQ(38, write(secondpipe[1], "Hi, this is a message sent over a pipe", 38));
+
+ // ... and repeat
+ Pkt6Ptr pkt6;
+ ASSERT_NO_THROW(pkt6 = ifacemgr->receive6(1));
+
+ // IfaceMgr should not process control socket data as incoming packets
+ EXPECT_FALSE(pkt6);
+
+ // There was some data, so this time callback should be called
+ EXPECT_FALSE(callback_ok);
+ EXPECT_TRUE(callback2_ok);
+
+ // Let's reset the status
+ callback_ok = false;
+ callback2_ok = false;
+
+ // Now let's send something over the first callback that was unregistered.
+ // We should NOT receive any callback.
+ EXPECT_EQ(38, write(pipefd[1], "Hi, this is a message sent over a pipe", 38));
+
+ // Now check that the first callback is NOT called.
+ ASSERT_NO_THROW(pkt6 = ifacemgr->receive6(1));
+ EXPECT_FALSE(callback_ok);
+
+ // close both pipe ends
+ close(pipefd[1]);
+ close(pipefd[0]);
+
+ close(secondpipe[1]);
+ close(secondpipe[0]);
+}
+
+
// Test checks if the unicast sockets can be opened.
// This test is now disabled, because there is no reliable way to test it. We
// can't even use loopback, beacuse openSockets() skips loopback interface
diff --git a/src/lib/dhcp/tests/option4_client_fqdn_unittest.cc b/src/lib/dhcp/tests/option4_client_fqdn_unittest.cc
index cdd7c64..ae658d2 100644
--- a/src/lib/dhcp/tests/option4_client_fqdn_unittest.cc
+++ b/src/lib/dhcp/tests/option4_client_fqdn_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2013-2014 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
@@ -615,6 +615,7 @@ TEST(Option4ClientFqdnTest, setDomainName) {
// Empty domain name (partial). This should be successful.
ASSERT_NO_THROW(option->setDomainName("", Option4ClientFqdn::PARTIAL));
EXPECT_TRUE(option->getDomainName().empty());
+ EXPECT_EQ(Option4ClientFqdn::PARTIAL, option->getDomainNameType());
// Fully qualified domain-names must not be empty.
EXPECT_THROW(option->setDomainName("", Option4ClientFqdn::FULL),
diff --git a/src/lib/dhcp/tests/option6_client_fqdn_unittest.cc b/src/lib/dhcp/tests/option6_client_fqdn_unittest.cc
index d644a2e..dac38e1 100644
--- a/src/lib/dhcp/tests/option6_client_fqdn_unittest.cc
+++ b/src/lib/dhcp/tests/option6_client_fqdn_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2013-2014 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
@@ -537,6 +537,7 @@ TEST(Option6ClientFqdnTest, setDomainName) {
// Empty domain name (partial). This should be successful.
ASSERT_NO_THROW(option->setDomainName("", Option6ClientFqdn::PARTIAL));
EXPECT_TRUE(option->getDomainName().empty());
+ EXPECT_EQ(Option6ClientFqdn::PARTIAL, option->getDomainNameType());
// Fully qualified domain-names must not be empty.
EXPECT_THROW(option->setDomainName("", Option6ClientFqdn::FULL),
diff --git a/src/lib/dhcp/tests/option_definition_unittest.cc b/src/lib/dhcp/tests/option_definition_unittest.cc
index 357ed56..1ba68a8 100644
--- a/src/lib/dhcp/tests/option_definition_unittest.cc
+++ b/src/lib/dhcp/tests/option_definition_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright (C) 2012-2013 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2012-2014 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
@@ -663,6 +663,112 @@ TEST_F(OptionDefinitionTest, recordIAAddr6Tokenized) {
EXPECT_EQ(5678, option_cast_v6->getValid());
}
+// The purpose of this test is to verify that the definition for option
+// that comprises a boolean value can be created and that this definition
+// can be used to create and option with a single boolean value.
+TEST_F(OptionDefinitionTest, boolValue) {
+ // The IP Forwarding option comprises one boolean value.
+ OptionDefinition opt_def("ip-forwarding", DHO_IP_FORWARDING,
+ "boolean");
+
+ OptionPtr option_v4;
+ // Use an option buffer which holds one value of 1 (true).
+ ASSERT_NO_THROW(
+ option_v4 = opt_def.optionFactory(Option::V4, DHO_IP_FORWARDING,
+ OptionBuffer(1, 1));
+ );
+ ASSERT_TRUE(typeid(*option_v4) == typeid(OptionCustom));
+ // Validate parsed value in the received option.
+ boost::shared_ptr<OptionCustom> option_cast_v4 =
+ boost::static_pointer_cast<OptionCustom>(option_v4);
+ EXPECT_TRUE(option_cast_v4->readBoolean());
+
+ // Repeat the test above, but set the value to 0 (false).
+ ASSERT_NO_THROW(
+ option_v4 = opt_def.optionFactory(Option::V4, DHO_IP_FORWARDING,
+ OptionBuffer(1, 0));
+ );
+ option_cast_v4 = boost::static_pointer_cast<OptionCustom>(option_v4);
+ EXPECT_FALSE(option_cast_v4->readBoolean());
+
+ // Try to provide zero-length buffer. Expect exception.
+ EXPECT_THROW(
+ opt_def.optionFactory(Option::V4, DHO_IP_FORWARDING, OptionBuffer()),
+ InvalidOptionValue
+ );
+
+}
+
+// The purpose of this test is to verify that definition for option that
+// comprises single boolean value can be created and that this definition
+// can be used to create an option holding a single boolean value. The
+// boolean value is converted from a string which is expected to hold
+// the following values: "true", "false", "1" or "0". For all other
+// values exception should be thrown.
+TEST_F(OptionDefinitionTest, boolTokenized) {
+ OptionDefinition opt_def("ip-forwarding", DHO_IP_FORWARDING, "boolean");
+
+ OptionPtr option_v4;
+ std::vector<std::string> values;
+ // Specify a value for the option instance being created.
+ values.push_back("true");
+ ASSERT_NO_THROW(
+ option_v4 = opt_def.optionFactory(Option::V4, DHO_IP_FORWARDING,
+ values);
+ );
+ ASSERT_TRUE(typeid(*option_v4) == typeid(OptionCustom));
+ // Validate the value.
+ OptionCustomPtr option_cast_v4 =
+ boost::static_pointer_cast<OptionCustom>(option_v4);
+ EXPECT_TRUE(option_cast_v4->readBoolean());
+
+ // Repeat the test but for "false" value this time.
+ values[0] = "false";
+ ASSERT_NO_THROW(
+ option_v4 = opt_def.optionFactory(Option::V4, DHO_IP_FORWARDING,
+ values);
+ );
+ ASSERT_TRUE(typeid(*option_v4) == typeid(OptionCustom));
+ // Validate the value.
+ option_cast_v4 = boost::static_pointer_cast<OptionCustom>(option_v4);
+ EXPECT_FALSE(option_cast_v4->readBoolean());
+
+ // Check if that will work for numeric values.
+ values[0] = "0";
+ ASSERT_NO_THROW(
+ option_v4 = opt_def.optionFactory(Option::V4, DHO_IP_FORWARDING,
+ values);
+ );
+ ASSERT_TRUE(typeid(*option_v4) == typeid(OptionCustom));
+ // Validate the value.
+ option_cast_v4 = boost::static_pointer_cast<OptionCustom>(option_v4);
+ EXPECT_FALSE(option_cast_v4->readBoolean());
+
+ // Swap numeric values and test if it works for "true" case.
+ values[0] = "1";
+ ASSERT_NO_THROW(
+ option_v4 = opt_def.optionFactory(Option::V4, DHO_IP_FORWARDING,
+ values);
+ );
+ ASSERT_TRUE(typeid(*option_v4) == typeid(OptionCustom));
+ // Validate the value.
+ option_cast_v4 = boost::static_pointer_cast<OptionCustom>(option_v4);
+ EXPECT_TRUE(option_cast_v4->readBoolean());
+
+ // A conversion of non-numeric value to boolean should fail if
+ // this value is neither "true" nor "false".
+ values[0] = "garbage";
+ EXPECT_THROW(opt_def.optionFactory(Option::V4, DHO_IP_FORWARDING, values),
+ isc::dhcp::BadDataTypeCast);
+
+ // A conversion of numeric value to boolean should fail if this value
+ // is neither "0" nor "1".
+ values[0] = "2";
+ EXPECT_THROW(opt_def.optionFactory(Option::V4, DHO_IP_FORWARDING, values),
+ isc::dhcp::BadDataTypeCast);
+
+}
+
// The purpose of this test is to verify that definition for option that
// comprises single uint8 value can be created and that this definition
// can be used to create an option with single uint8 value.
@@ -672,7 +778,8 @@ TEST_F(OptionDefinitionTest, uint8) {
OptionPtr option_v6;
// Try to use correct buffer length = 1 byte.
ASSERT_NO_THROW(
- option_v6 = opt_def.optionFactory(Option::V6, D6O_PREFERENCE, OptionBuffer(1, 1));
+ option_v6 = opt_def.optionFactory(Option::V6, D6O_PREFERENCE,
+ OptionBuffer(1, 1));
);
ASSERT_TRUE(typeid(*option_v6) == typeid(OptionInt<uint8_t>));
// Validate the value.
diff --git a/src/lib/dhcp/tests/pkt_filter_test_stub.cc b/src/lib/dhcp/tests/pkt_filter_test_stub.cc
new file mode 100644
index 0000000..2e9ab5e
--- /dev/null
+++ b/src/lib/dhcp/tests/pkt_filter_test_stub.cc
@@ -0,0 +1,49 @@
+// Copyright (C) 2014 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 <dhcp/tests/pkt_filter_test_stub.h>
+
+namespace isc {
+namespace dhcp {
+namespace test {
+
+PktFilterTestStub::PktFilterTestStub()
+ : direct_response_supported_(true) {
+}
+
+bool
+PktFilterTestStub::isDirectResponseSupported() const {
+ return (direct_response_supported_);
+}
+
+SocketInfo
+PktFilterTestStub::openSocket(const Iface&,
+ const isc::asiolink::IOAddress& addr,
+ const uint16_t port, const bool, const bool) {
+ return (SocketInfo(addr, port, 0));
+}
+
+Pkt4Ptr
+PktFilterTestStub::receive(const Iface&, const SocketInfo&) {
+ return Pkt4Ptr();
+}
+
+int
+PktFilterTestStub::send(const Iface&, uint16_t, const Pkt4Ptr&) {
+ return (0);
+}
+
+}
+}
+}
diff --git a/src/lib/dhcp/tests/pkt_filter_test_stub.h b/src/lib/dhcp/tests/pkt_filter_test_stub.h
new file mode 100644
index 0000000..f8c6130
--- /dev/null
+++ b/src/lib/dhcp/tests/pkt_filter_test_stub.h
@@ -0,0 +1,105 @@
+// Copyright (C) 2014 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 PKT_FILTER_TEST_STUB_H
+#define PKT_FILTER_TEST_STUB_H
+
+#include <asiolink/io_address.h>
+#include <dhcp/iface_mgr.h>
+#include <dhcp/pkt_filter.h>
+#include <dhcp/pkt4.h>
+
+namespace isc {
+namespace dhcp {
+namespace test {
+
+/// @brief A stub implementation of the PktFilter class.
+///
+/// This class implements abstract methods of the @c isc::dhcp::PktFilter
+/// class. It is used by unit tests, which test protected methods of the
+/// @c isc::dhcp::test::PktFilter class. The implemented abstract methods are
+/// no-op.
+class PktFilterTestStub : public PktFilter {
+public:
+
+ /// @brief Constructor.
+ PktFilterTestStub();
+
+ /// @brief Checks if the direct DHCPv4 response is supported.
+ ///
+ /// This function checks if the direct response capability is supported,
+ /// i.e. if the server can respond to the client which doesn't have an
+ /// address yet. For this dummy class, the true is alaways returned.
+ ///
+ /// @return always true.
+ virtual bool isDirectResponseSupported() const;
+
+ /// @brief Simulate opening of the socket.
+ ///
+ /// This function simulates opening a primary socket. In reality, it doesn't
+ /// open a socket but the socket descriptor returned in the SocketInfo
+ /// structure is always set to 0.
+ ///
+ /// @param iface An interface descriptor.
+ /// @param addr Address on the interface to be used to send packets.
+ /// @param port Port number to bind socket to.
+ /// @param receive_bcast A flag which indicates if socket should be
+ /// configured to receive broadcast packets (if true).
+ /// @param send_bcast A flag which indicates if the socket should be
+ /// configured to send broadcast packets (if true).
+ ///
+ /// @note All parameters are ignored.
+ ///
+ /// @return A SocketInfo structure with the socket descriptor set to 0. The
+ /// fallback socket descriptor is set to a negative value.
+ virtual SocketInfo openSocket(const Iface& iface,
+ const isc::asiolink::IOAddress& addr,
+ const uint16_t port,
+ const bool receive_bcast,
+ const bool send_bcast);
+
+ /// @brief Simulate reception of the DHCPv4 message.
+ ///
+ /// @param iface An interface to be used to receive DHCPv4 message.
+ /// @param sock_info A descriptor of the primary and fallback sockets.
+ ///
+ /// @note All parameters are ignored.
+ ///
+ /// @return always a NULL object.
+ virtual Pkt4Ptr receive(const Iface& iface, const SocketInfo& sock_info);
+
+ /// @brief Simulates sending a DHCPv4 message.
+ ///
+ /// This function does nothing.
+ ///
+ /// @param iface An interface to be used to send DHCPv4 message.
+ /// @param port A port used to send a message.
+ /// @param pkt A DHCPv4 to be sent.
+ ///
+ /// @note All parameters are ignored.
+ ///
+ /// @return 0.
+ virtual int send(const Iface& iface, uint16_t port, const Pkt4Ptr& pkt);
+
+ // Change the scope of the protected function so as they can be unit tested.
+ using PktFilter::openFallbackSocket;
+
+ bool direct_response_supported_;
+};
+
+} // namespace isc::dhcp::test
+} // namespace isc::dhcp
+} // namespace isc
+
+#endif // PKT_FILTER_TEST_STUB_H
diff --git a/src/lib/dhcp/tests/pkt_filter_test_utils.h b/src/lib/dhcp/tests/pkt_filter_test_utils.h
index b2320cf..2ce9dd5 100644
--- a/src/lib/dhcp/tests/pkt_filter_test_utils.h
+++ b/src/lib/dhcp/tests/pkt_filter_test_utils.h
@@ -1,4 +1,4 @@
-// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2013-2014 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
@@ -129,7 +129,9 @@ public:
/// fallback socket descriptor is set to a negative value.
virtual SocketInfo openSocket(const Iface& iface,
const isc::asiolink::IOAddress& addr,
- const uint16_t port, const bool, const bool);
+ const uint16_t port,
+ const bool receive_bcast,
+ const bool send_bcast);
/// @brief Simulate reception of the DHCPv4 message.
///
diff --git a/src/lib/dhcp_ddns/Makefile.am b/src/lib/dhcp_ddns/Makefile.am
index 552f6d9..c934412 100644
--- a/src/lib/dhcp_ddns/Makefile.am
+++ b/src/lib/dhcp_ddns/Makefile.am
@@ -34,6 +34,7 @@ libb10_dhcp_ddns_la_SOURCES += dhcp_ddns_log.cc dhcp_ddns_log.h
libb10_dhcp_ddns_la_SOURCES += ncr_io.cc ncr_io.h
libb10_dhcp_ddns_la_SOURCES += ncr_msg.cc ncr_msg.h
libb10_dhcp_ddns_la_SOURCES += ncr_udp.cc ncr_udp.h
+libb10_dhcp_ddns_la_SOURCES += watch_socket.cc watch_socket.h
nodist_libb10_dhcp_ddns_la_SOURCES = dhcp_ddns_messages.cc dhcp_ddns_messages.h
diff --git a/src/lib/dhcp_ddns/dhcp_ddns_messages.mes b/src/lib/dhcp_ddns/dhcp_ddns_messages.mes
index 51688ad..5cf298e 100644
--- a/src/lib/dhcp_ddns/dhcp_ddns_messages.mes
+++ b/src/lib/dhcp_ddns/dhcp_ddns_messages.mes
@@ -1,4 +1,4 @@
-# Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
+# Copyright (C) 2013-2014 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,6 +19,12 @@ This is an error message that indicates that an invalid request to update
a DNS entry was received by the application. Either the format or the content
of the request is incorrect. The request will be ignored.
+% DHCP_DDNS_NCR_FLUSH_IO_ERROR DHCP-DDNS Last send before stopping did not complete successfully: %1
+This is an error message that indicates the DHCP-DDNS client was unable to
+complete the last send prior to exiting send mode. This is a programmatic
+error, highly unlikely to occur, and should not impair the application's ability
+to process requests.
+
% DHCP_DDNS_NCR_LISTEN_CLOSE_ERROR application encountered an error while closing the listener used to receive NameChangeRequests : %1
This is an error message that indicates the application was unable to close the
listener connection used to receive NameChangeRequests. Closure may occur
@@ -32,19 +38,25 @@ start another read after receiving a request. While possible, this is highly
unlikely and is probably a programmatic error. The application should recover
on its own.
-% DHCP_DDNS_NCR_SEND_CLOSE_ERROR DHCP-DDNS client encountered an error while closing the sender connection used to send NameChangeRequests : %1
+% DHCP_DDNS_NCR_SEND_CLOSE_ERROR DHCP-DDNS client encountered an error while closing the sender connection used to send NameChangeRequests: %1
This is an error message that indicates the DHCP-DDNS client was unable to
close the connection used to send NameChangeRequests. Closure may occur during
the course of error recovery or during normal shutdown procedure. In either
case the error is unlikely to impair the client's ability to send requests but
it should be reported for analysis.
-% DHCP_DDNS_NCR_SEND_NEXT_ERROR DHCP-DDNS client could not initiate the next request send following send completion.
+% DHCP_DDNS_NCR_SEND_NEXT_ERROR DHCP-DDNS client could not initiate the next request send following send completion: %1
This is a error message indicating that NameChangeRequest sender could not
start another send after completing the send of the previous request. While
possible, this is highly unlikely and is probably a programmatic error. The
application should recover on its own.
+% DHCP_DDNS_NCR_UDP_CLEAR_READY_ERROR NCR UDP watch socket failed to clear: %1
+This is an error message that indicates the application was unable to reset the
+UDP NCR sender ready status after completing a send. This is programmatic error
+that should be reported. The application may or may not continue to operate
+correctly.
+
% DHCP_DDNS_NCR_UDP_RECV_CANCELED UDP socket receive was canceled while listening for DNS Update requests: %1
This is an informational message indicating that the listening over a UDP socket for DNS update requests has been canceled. This is a normal part of suspending listening operations.
@@ -74,3 +86,15 @@ This is an error message that indicates that an exception was thrown but not
caught in the application's send completion handler. This is a programmatic
error that needs to be reported. Dependent upon the nature of the error the
client may or may not continue operating normally.
+
+% DHCP_DDNS_WATCH_SINK_CLOSE_ERROR Sink-side watch socket failed to close: %1
+This is an error message that indicates the application was unable to close
+the inbound side of a NCR sender's watch socket. While technically possible
+this error is highly unlikely to occur and should not impair the application's
+ability to process requests.
+
+% DHCP_DDNS_WATCH_SOURCE_CLOSE_ERROR Source-side watch socket failed to close: %1
+This is an error message that indicates the application was unable to close
+the outbound side of a NCR sender's watch socket. While technically possible
+this error is highly unlikely to occur and should not impair the application's
+ability to process requests.
diff --git a/src/lib/dhcp_ddns/ncr_io.cc b/src/lib/dhcp_ddns/ncr_io.cc
index 7e7174a..8e9e7c9 100644
--- a/src/lib/dhcp_ddns/ncr_io.cc
+++ b/src/lib/dhcp_ddns/ncr_io.cc
@@ -1,4 +1,4 @@
-// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2013-2014 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,6 +15,7 @@
#include <dhcp_ddns/dhcp_ddns_log.h>
#include <dhcp_ddns/ncr_io.h>
+#include <asio.hpp>
#include <boost/algorithm/string/predicate.hpp>
namespace isc {
@@ -23,7 +24,7 @@ namespace dhcp_ddns {
NameChangeProtocol stringToNcrProtocol(const std::string& protocol_str) {
if (boost::iequals(protocol_str, "UDP")) {
return (NCR_UDP);
- }
+ }
if (boost::iequals(protocol_str, "TCP")) {
return (NCR_TCP);
@@ -159,13 +160,10 @@ NameChangeListener::invokeRecvHandler(const Result result,
NameChangeSender::NameChangeSender(RequestSendHandler& send_handler,
size_t send_queue_max)
: sending_(false), send_handler_(send_handler),
- send_queue_max_(send_queue_max) {
+ send_queue_max_(send_queue_max), io_service_(NULL) {
// Queue size must be big enough to hold at least 1 entry.
- if (send_queue_max == 0) {
- isc_throw(NcrSenderError, "NameChangeSender constructor"
- " queue size must be greater than zero");
- }
+ setQueueMaxSize(send_queue_max);
}
void
@@ -180,6 +178,8 @@ NameChangeSender::startSending(isc::asiolink::IOService& io_service) {
// Call implementation dependent open.
try {
+ // Remember io service we're given.
+ io_service_ = &io_service;
open(io_service);
} catch (const isc::Exception& ex) {
stopSending();
@@ -188,10 +188,30 @@ NameChangeSender::startSending(isc::asiolink::IOService& io_service) {
// Set our status to sending.
setSending(true);
+
+ // If there's any queued already.. we'll start sending.
+ sendNext();
}
void
NameChangeSender::stopSending() {
+ // Set it send indicator to false, no matter what. This allows us to at
+ // least try to re-open via startSending(). Also, setting it false now,
+ // allows us to break sendNext() chain in invokeSendHandler.
+ setSending(false);
+
+ // If there is an outstanding IO to complete, attempt to process it.
+ if (ioReady() && io_service_ != NULL) {
+ try {
+ runReadyIO();
+ } catch (const std::exception& ex) {
+ // Swallow exceptions. If we have some sort of error we'll log
+ // it but we won't propagate the throw.
+ LOG_ERROR(dhcp_ddns_logger,
+ DHCP_DDNS_NCR_FLUSH_IO_ERROR).arg(ex.what());
+ }
+ }
+
try {
// Call implementation dependent close.
close();
@@ -202,9 +222,7 @@ NameChangeSender::stopSending() {
DHCP_DDNS_NCR_SEND_CLOSE_ERROR).arg(ex.what());
}
- // Set it false, no matter what. This allows us to at least try to
- // re-open via startSending().
- setSending(false);
+ io_service_ = NULL;
}
void
@@ -277,7 +295,9 @@ NameChangeSender::invokeSendHandler(const NameChangeSender::Result result) {
// Set up the next send
try {
- sendNext();
+ if (amSending()) {
+ sendNext();
+ }
} catch (const isc::Exception& ex) {
// It is possible though unlikely, for sendNext to fail without
// scheduling the send. While, unlikely, it does mean the callback
@@ -318,5 +338,72 @@ NameChangeSender::clearSendQueue() {
send_queue_.clear();
}
+void
+NameChangeSender::setQueueMaxSize(const size_t new_max) {
+ if (new_max == 0) {
+ isc_throw(NcrSenderError, "NameChangeSender:"
+ " queue size must be greater than zero");
+ }
+
+ send_queue_max_ = new_max;
+
+}
+const NameChangeRequestPtr&
+NameChangeSender::peekAt(const size_t index) const {
+ if (index >= getQueueSize()) {
+ isc_throw(NcrSenderError,
+ "NameChangeSender::peekAt peek beyond end of queue attempted"
+ << " index: " << index << " queue size: " << getQueueSize());
+ }
+
+ return (send_queue_.at(index));
+}
+
+
+void
+NameChangeSender::assumeQueue(NameChangeSender& source_sender) {
+ if (source_sender.amSending()) {
+ isc_throw(NcrSenderError, "Cannot assume queue:"
+ " source sender is actively sending");
+ }
+
+ if (amSending()) {
+ isc_throw(NcrSenderError, "Cannot assume queue:"
+ " target sender is actively sending");
+ }
+
+ if (getQueueMaxSize() < source_sender.getQueueSize()) {
+ isc_throw(NcrSenderError, "Cannot assume queue:"
+ " source queue count exceeds target queue max");
+ }
+
+ if (!send_queue_.empty()) {
+ isc_throw(NcrSenderError, "Cannot assume queue:"
+ " target queue is not empty");
+ }
+
+ send_queue_.swap(source_sender.getSendQueue());
+}
+
+int
+NameChangeSender::getSelectFd() {
+ isc_throw(NotImplemented, "NameChangeSender::getSelectFd is not supported");
+}
+
+void
+NameChangeSender::runReadyIO() {
+ if (!io_service_) {
+ isc_throw(NcrSenderError, "NameChangeSender::runReadyIO"
+ " sender io service is null");
+ }
+
+ // We shouldn't be here if IO isn't ready to execute.
+ // By running poll we're gauranteed not to hang.
+ /// @todo Trac# 3325 requests that asiolink::IOService provide a
+ /// wrapper for poll().
+ io_service_->get_io_service().poll_one();
+}
+
+
} // namespace isc::dhcp_ddns
} // namespace isc
diff --git a/src/lib/dhcp_ddns/ncr_io.h b/src/lib/dhcp_ddns/ncr_io.h
index a5e513a..83157a1 100644
--- a/src/lib/dhcp_ddns/ncr_io.h
+++ b/src/lib/dhcp_ddns/ncr_io.h
@@ -1,4 +1,4 @@
-// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2013-2014 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
@@ -161,7 +161,7 @@ public:
/// Assuming the open is successful, startListener will call receiveNext, to
/// initiate an asynchronous receive. This method calls the virtual method,
/// doReceive(). The listener derivation uses doReceive to instigate an IO
-/// layer asynchronous receieve passing in its IO layer callback to
+/// layer asynchronous receive passing in its IO layer callback to
/// handle receive events from the IO source.
///
/// As stated earlier, the derivation's NameChangeRequest completion handler
@@ -547,6 +547,40 @@ public:
/// capacity.
void sendRequest(NameChangeRequestPtr& ncr);
+ /// @brief Move all queued requests from a given sender into the send queue
+ ///
+ /// Moves all of the entries in the given sender's queue and places them
+ /// into send queue. This provides a mechanism of reassigning queued
+ /// messages from one sender to another. This is useful for dealing with
+ /// dynamic configuration changes.
+ ///
+ /// @param source_sender from whom the queued messages will be taken
+ ///
+ /// @throw NcrSenderError if either sender is in send mode, if the number of
+ /// messages in the source sender's queue is larger than this sender's
+ /// maxium queue size, or if this sender's queue is not empty.
+ void assumeQueue(NameChangeSender& source_sender);
+
+ /// @brief Returns a file descriptor suitable for use with select
+ ///
+ /// The value returned is an open file descriptor which can be used with
+ /// select() system call to monitor the sender for IO events. This allows
+ /// NameChangeSenders to be used in applications which use select, rather
+ /// than IOService to wait for IO events to occur.
+ ///
+ /// @warning Attempting other use of this value may lead to unpredictable
+ /// behavior in the sender.
+ ///
+ /// @return Returns an "open" file descriptor
+ ///
+ /// @throw NcrSenderError if the sender is not in send mode,
+ virtual int getSelectFd() = 0;
+
+ /// @brief Returns whether or not the sender has IO ready to process.
+ ///
+ /// @return true if the sender has at IO ready, false otherwise.
+ virtual bool ioReady() = 0;
+
protected:
/// @brief Dequeues and sends the next request on the send queue.
///
@@ -659,11 +693,61 @@ public:
return (send_queue_max_);
}
+ /// @brief Sets the maximum queue size to the given value.
+ ///
+ /// Sets the maximum number of entries allowed in the queue to the
+ /// the given value.
+ ///
+ /// @param new_max the new value to use as the maximum
+ ///
+ /// @throw NcrSenderError if the value is less than one.
+ void setQueueMaxSize(const size_t new_max);
+
/// @brief Returns the number of entries currently in the send queue.
size_t getQueueSize() const {
return (send_queue_.size());
}
+ /// @brief Returns the entry at a given position in the queue.
+ ///
+ /// Note that the entry is not removed from the queue.
+ /// @param index the index of the entry in the queue to fetch.
+ /// Valid values are 0 (front of the queue) to (queue size - 1).
+ ///
+ /// @return Pointer reference to the queue entry.
+ ///
+ /// @throw NcrSenderError if the given index is beyond the
+ /// end of the queue.
+ const NameChangeRequestPtr& peekAt(const size_t index) const;
+
+ /// @brief Processes sender IO events
+ ///
+ /// Executes at most one ready handler on the sender's IO service. If
+ /// no handlers are ready it returns immediately.
+ ///
+ /// @warning - Running all ready handlers, in theory, could process all
+ /// messages currently queued.
+ ///
+ /// NameChangeSender daisy chains requests together in its completion
+ /// by one message completion's handler initiating the next message's send.
+ /// When using UDP, a send immediately marks its event handler as ready
+ /// to run. If this occurs inside a call to ioservice::poll() or run(),
+ /// that event will also be run. If that handler calls UDP send then
+ /// that send's handler will be marked ready and executed and so on. If
+ /// there were 1000 messages in the queue then all them would be sent from
+ /// within the context of one call to runReadyIO().
+ /// By running only one handler at time, we ensure that NCR IO activity
+ /// doesn't starve other processing. It is unclear how much of a real
+ /// threat this poses but for now it is best to err on the side of caution.
+ ///
+ virtual void runReadyIO();
+
+protected:
+ /// @brief Returns a reference to the send queue.
+ SendQueue& getSendQueue() {
+ return (send_queue_);
+ }
+
private:
/// @brief Sets the sending indicator to the given value.
///
@@ -689,6 +773,12 @@ private:
/// @brief Pointer to the request which is in the process of being sent.
NameChangeRequestPtr ncr_to_send_;
+
+ /// @brief Pointer to the IOService currently being used by the sender.
+ /// @note We need to remember the io_service but we receive it by
+ /// reference. Use a raw pointer to store it. This value should never be
+ /// exposed and is only valid while in send mode.
+ asiolink::IOService* io_service_;
};
/// @brief Defines a smart pointer to an instance of a sender.
diff --git a/src/lib/dhcp_ddns/ncr_msg.h b/src/lib/dhcp_ddns/ncr_msg.h
index 5dab9e8..7fd6f64 100644
--- a/src/lib/dhcp_ddns/ncr_msg.h
+++ b/src/lib/dhcp_ddns/ncr_msg.h
@@ -72,7 +72,7 @@ enum NameChangeFormat {
/// @brief Function which converts labels to NameChangeFormat enum values.
///
-/// @param format_str text to convert to an enum.
+/// @param fmt_str text to convert to an enum.
/// Valid string values: "JSON"
///
/// @return NameChangeFormat value which maps to the given string.
diff --git a/src/lib/dhcp_ddns/ncr_udp.cc b/src/lib/dhcp_ddns/ncr_udp.cc
index 9e83f7c..54901f2 100644
--- a/src/lib/dhcp_ddns/ncr_udp.cc
+++ b/src/lib/dhcp_ddns/ncr_udp.cc
@@ -1,4 +1,4 @@
-// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2013-2014 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
@@ -94,7 +94,7 @@ NameChangeUDPListener::~NameChangeUDPListener() {
void
NameChangeUDPListener::open(isc::asiolink::IOService& io_service) {
// create our endpoint and bind the the low level socket to it.
- isc::asiolink::UDPEndpoint endpoint(ip_address_.getAddress(), port_);
+ isc::asiolink::UDPEndpoint endpoint(ip_address_, port_);
// Create the low level socket.
try {
@@ -227,7 +227,7 @@ NameChangeUDPSender::~NameChangeUDPSender() {
void
NameChangeUDPSender::open(isc::asiolink::IOService& io_service) {
// create our endpoint and bind the the low level socket to it.
- isc::asiolink::UDPEndpoint endpoint(ip_address_.getAddress(), port_);
+ isc::asiolink::UDPEndpoint endpoint(ip_address_, port_);
// Create the low level socket.
try {
@@ -252,10 +252,11 @@ NameChangeUDPSender::open(isc::asiolink::IOService& io_service) {
// Create the server endpoint
server_endpoint_.reset(new isc::asiolink::
- UDPEndpoint(server_address_.getAddress(),
- server_port_));
+ UDPEndpoint(server_address_, server_port_));
send_callback_->setDataSource(server_endpoint_);
+
+ watch_socket_.reset(new WatchSocket());
}
void
@@ -282,6 +283,8 @@ NameChangeUDPSender::close() {
}
socket_.reset();
+
+ watch_socket_.reset();
}
void
@@ -298,11 +301,32 @@ NameChangeUDPSender::doSend(NameChangeRequestPtr& ncr) {
// Call the socket's asychronous send, passing our callback
socket_->asyncSend(send_callback_->getData(), send_callback_->getPutLen(),
send_callback_->getDataSource().get(), *send_callback_);
+
+ // Set IO ready marker so sender activity is visible to select() or poll().
+ // Note, if this call throws it will manifest itself as a throw from
+ // from sendRequest() which the application calls directly and is documented
+ // as throwing exceptions; or caught inside invokeSendHandler() which
+ // will invoke the application's send_handler with an error status.
+ watch_socket_->markReady();
}
void
NameChangeUDPSender::sendCompletionHandler(const bool successful,
const UDPCallback *send_callback) {
+ // Clear the IO ready marker.
+ try {
+ watch_socket_->clearReady();
+ } catch (const std::exception& ex) {
+ // This can only happen if the WatchSocket's select_fd has been
+ // compromised which is a programmatic error. We'll log the error
+ // here, then continue on and process the IO result we were given.
+ // WatchSocket issue will resurface on the next send as a closed
+ // fd in markReady(). This allows application's handler to deal
+ // with watch errors more uniformly.
+ LOG_ERROR(dhcp_ddns_logger, DHCP_DDNS_NCR_UDP_CLEAR_READY_ERROR)
+ .arg(ex.what());
+ }
+
Result result;
if (successful) {
result = SUCCESS;
@@ -324,5 +348,27 @@ NameChangeUDPSender::sendCompletionHandler(const bool successful,
// Call the application's registered request send handler.
invokeSendHandler(result);
}
+
+int
+NameChangeUDPSender::getSelectFd() {
+ if (!amSending()) {
+ isc_throw(NotImplemented, "NameChangeUDPSender::getSelectFd"
+ " not in send mode");
+ }
+
+ return(watch_socket_->getSelectFd());
+}
+
+bool
+NameChangeUDPSender::ioReady() {
+ if (watch_socket_) {
+ return (watch_socket_->isReady());
+ }
+
+ return (false);
+}
+
+
+
}; // end of isc::dhcp_ddns namespace
}; // end of isc namespace
diff --git a/src/lib/dhcp_ddns/ncr_udp.h b/src/lib/dhcp_ddns/ncr_udp.h
index 7648a61..1af0308 100644
--- a/src/lib/dhcp_ddns/ncr_udp.h
+++ b/src/lib/dhcp_ddns/ncr_udp.h
@@ -1,4 +1,4 @@
-// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2013-2014 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
@@ -112,10 +112,12 @@
#include <asiolink/udp_endpoint.h>
#include <asiolink/udp_socket.h>
#include <dhcp_ddns/ncr_io.h>
+#include <dhcp_ddns/watch_socket.h>
#include <util/buffer.h>
#include <boost/shared_array.hpp>
+
/// responsibility of the completion handler to perform the steps necessary
/// to interpret the raw data provided by the service outcome. The
/// UDPCallback operator implementation is mostly a pass through.
@@ -502,6 +504,7 @@ public:
/// asyncSend() method is called, passing in send_callback_ member's
/// transfer buffer as the send buffer and the send_callback_ itself
/// as the callback object.
+ /// @param ncr NameChangeRequest to send.
virtual void doSend(NameChangeRequestPtr& ncr);
/// @brief Implements the NameChangeRequest level send completion handler.
@@ -524,6 +527,26 @@ public:
void sendCompletionHandler(const bool successful,
const UDPCallback* send_callback);
+ /// @brief Returns a file descriptor suitable for use with select
+ ///
+ /// The value returned is an open file descriptor which can be used with
+ /// select() system call to monitor the sender for IO events. This allows
+ /// NameChangeUDPSenders to be used in applications which use select,
+ /// rather than IOService to wait for IO events to occur.
+ ///
+ /// @warning Attempting other use of this value may lead to unpredictable
+ /// behavior in the sender.
+ ///
+ /// @return Returns an "open" file descriptor
+ ///
+ /// @throw NcrSenderError if the sender is not in send mode,
+ virtual int getSelectFd();
+
+ /// @brief Returns whether or not the sender has IO ready to process.
+ ///
+ /// @return true if the sender has at IO ready, false otherwise.
+ virtual bool ioReady();
+
private:
/// @brief IP address from which to send.
isc::asiolink::IOAddress ip_address_;
@@ -554,6 +577,9 @@ private:
/// @brief Flag which enables the reuse address socket option if true.
bool reuse_address_;
+
+ /// @brief Pointer to WatchSocket instance supplying the "select-fd".
+ WatchSocketPtr watch_socket_;
};
} // namespace isc::dhcp_ddns
diff --git a/src/lib/dhcp_ddns/tests/Makefile.am b/src/lib/dhcp_ddns/tests/Makefile.am
index 78effc7..75bdcb1 100644
--- a/src/lib/dhcp_ddns/tests/Makefile.am
+++ b/src/lib/dhcp_ddns/tests/Makefile.am
@@ -29,6 +29,8 @@ TESTS += libdhcp_ddns_unittests
libdhcp_ddns_unittests_SOURCES = run_unittests.cc
libdhcp_ddns_unittests_SOURCES += ncr_unittests.cc
libdhcp_ddns_unittests_SOURCES += ncr_udp_unittests.cc
+libdhcp_ddns_unittests_SOURCES += test_utils.cc test_utils.h
+libdhcp_ddns_unittests_SOURCES += watch_socket_unittests.cc
libdhcp_ddns_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES) $(LOG4CPLUS_INCLUDES)
diff --git a/src/lib/dhcp_ddns/tests/ncr_udp_unittests.cc b/src/lib/dhcp_ddns/tests/ncr_udp_unittests.cc
index 997ad4f..0ea2f71 100644
--- a/src/lib/dhcp_ddns/tests/ncr_udp_unittests.cc
+++ b/src/lib/dhcp_ddns/tests/ncr_udp_unittests.cc
@@ -1,4 +1,4 @@
-// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2013-2014 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
@@ -16,6 +16,7 @@
#include <dhcp_ddns/ncr_io.h>
#include <dhcp_ddns/ncr_udp.h>
#include <util/time_utilities.h>
+#include <test_utils.h>
#include <asio/ip/udp.hpp>
#include <boost/function.hpp>
@@ -23,6 +24,8 @@
#include <gtest/gtest.h>
#include <algorithm>
+#include <sys/select.h>
+
using namespace std;
using namespace isc;
using namespace isc::dhcp_ddns;
@@ -113,6 +116,7 @@ TEST(NameChangeUDPListenerBasicTest, basicListenTests) {
// Verify that we can start listening.
EXPECT_NO_THROW(listener->startListening(io_service));
+
// Verify that we are in listening mode.
EXPECT_TRUE(listener->amListening());
// Verify that a read is in progress.
@@ -268,9 +272,20 @@ TEST_F(NameChangeUDPListenerTest, basicReceivetest) {
/// @brief A NOP derivation for constructor test purposes.
class SimpleSendHandler : public NameChangeSender::RequestSendHandler {
public:
- virtual void operator ()(const NameChangeSender::Result,
+ SimpleSendHandler() : pass_count_(0), error_count_(0) {
+ }
+
+ virtual void operator ()(const NameChangeSender::Result result,
NameChangeRequestPtr&) {
+ if (result == NameChangeSender::SUCCESS) {
+ ++pass_count_;
+ } else {
+ ++error_count_;
+ }
}
+
+ int pass_count_;
+ int error_count_;
};
/// @brief Tests the NameChangeUDPSender constructors.
@@ -311,7 +326,6 @@ TEST(NameChangeUDPSenderBasicTest, constructionTests) {
/// This test verifies that:
TEST(NameChangeUDPSenderBasicTest, basicSendTests) {
isc::asiolink::IOAddress ip_address(TEST_ADDRESS);
- uint32_t port = SENDER_PORT;
isc::asiolink::IOService io_service;
SimpleSendHandler ncr_handler;
@@ -320,8 +334,9 @@ TEST(NameChangeUDPSenderBasicTest, basicSendTests) {
// Create the sender, setting the queue max equal to the number of
// messages we will have in the list.
- NameChangeUDPSender sender(ip_address, port, ip_address, port,
- FMT_JSON, ncr_handler, num_msgs);
+ NameChangeUDPSender sender(ip_address, SENDER_PORT, ip_address,
+ LISTENER_PORT, FMT_JSON, ncr_handler,
+ num_msgs, true);
// Verify that we can start sending.
EXPECT_NO_THROW(sender.startSending(io_service));
@@ -341,30 +356,63 @@ TEST(NameChangeUDPSenderBasicTest, basicSendTests) {
EXPECT_NO_THROW(sender.startSending(io_service));
EXPECT_TRUE(sender.amSending());
+ // Fetch the sender's select-fd.
+ int select_fd = sender.getSelectFd();
+
+ // Verify select_fd is valid and currently shows no ready to read.
+ ASSERT_NE(dhcp_ddns::WatchSocket::INVALID_SOCKET, select_fd);
+
+ // Make sure select_fd does evaluates to not ready via select and
+ // that ioReady() method agrees.
+ ASSERT_EQ(0, selectCheck(select_fd));
+ ASSERT_FALSE(sender.ioReady());
+
// Iterate over a series of messages, sending each one. Since we
// do not invoke IOService::run, then the messages should accumulate
// in the queue.
NameChangeRequestPtr ncr;
+ NameChangeRequestPtr ncr2;
for (int i = 0; i < num_msgs; i++) {
ASSERT_NO_THROW(ncr = NameChangeRequest::fromJSON(valid_msgs[i]));
EXPECT_NO_THROW(sender.sendRequest(ncr));
// Verify that the queue count increments in step with each send.
EXPECT_EQ(i+1, sender.getQueueSize());
+
+ // Verify that peekAt(i) returns the NCR we just added.
+ ASSERT_NO_THROW(ncr2 = sender.peekAt(i));
+ ASSERT_TRUE(ncr2);
+ EXPECT_TRUE(*ncr == *ncr2);
}
+ // Verify that attempting to peek beyond the end of the queue, throws.
+ ASSERT_THROW(sender.peekAt(sender.getQueueSize()+1), NcrSenderError);
+
// Verify that attempting to send an additional message results in a
// queue full exception.
EXPECT_THROW(sender.sendRequest(ncr), NcrSenderQueueFull);
- // Loop for the number of valid messages and invoke IOService::run_one.
- // This should send exactly one message and the queue count should
- // decrement accordingly.
+ // Loop for the number of valid messages. So long as there is at least
+ // on NCR in the queue, select-fd indicate ready to read. Invoke
+ // IOService::run_one. This should complete the send of exactly one
+ // message and the queue count should decrement accordingly.
for (int i = num_msgs; i > 0; i--) {
- io_service.run_one();
+ // Make sure select_fd does evaluates to ready via select and
+ // that ioReady() method agrees.
+ ASSERT_TRUE(selectCheck(select_fd) > 0);
+ ASSERT_TRUE(sender.ioReady());
+
+ // Execute at one ready handler.
+ ASSERT_NO_THROW(sender.runReadyIO());
+
// Verify that the queue count decrements in step with each run.
EXPECT_EQ(i-1, sender.getQueueSize());
}
+ // Make sure select_fd does evaluates to not ready via select and
+ // that ioReady() method agrees.
+ ASSERT_EQ(0, selectCheck(select_fd));
+ ASSERT_FALSE(sender.ioReady());
+
// Verify that the queue is empty.
EXPECT_EQ(0, sender.getQueueSize());
@@ -379,22 +427,183 @@ TEST(NameChangeUDPSenderBasicTest, basicSendTests) {
// Verify that flushing the queue is not allowed in sending state.
EXPECT_THROW(sender.clearSendQueue(), NcrSenderError);
- // Put a message on the queue.
- EXPECT_NO_THROW(sender.sendRequest(ncr));
- EXPECT_EQ(1, sender.getQueueSize());
+ // Put num_msgs messages on the queue.
+ for (int i = 0; i < num_msgs; i++) {
+ ASSERT_NO_THROW(ncr = NameChangeRequest::fromJSON(valid_msgs[i]));
+ EXPECT_NO_THROW(sender.sendRequest(ncr));
+ }
+
+ // Make sure we have number of messages expected.
+ EXPECT_EQ(num_msgs, sender.getQueueSize());
// Verify that we can gracefully stop sending.
EXPECT_NO_THROW(sender.stopSending());
EXPECT_FALSE(sender.amSending());
// Verify that the queue is preserved after leaving sending state.
- EXPECT_EQ(1, sender.getQueueSize());
+ EXPECT_EQ(num_msgs - 1, sender.getQueueSize());
// Verify that flushing the queue works when not sending.
EXPECT_NO_THROW(sender.clearSendQueue());
EXPECT_EQ(0, sender.getQueueSize());
}
+/// @brief Tests that sending gets kick-started if the queue isn't empty
+/// when startSending is called.
+TEST(NameChangeUDPSenderBasicTest, autoStart) {
+ isc::asiolink::IOAddress ip_address(TEST_ADDRESS);
+ isc::asiolink::IOService io_service;
+ SimpleSendHandler ncr_handler;
+
+ // Tests are based on a list of messages, get the count now.
+ int num_msgs = sizeof(valid_msgs)/sizeof(char*);
+
+ // Create the sender, setting the queue max equal to the number of
+ // messages we will have in the list.
+ NameChangeUDPSender sender(ip_address, SENDER_PORT, ip_address,
+ LISTENER_PORT, FMT_JSON, ncr_handler,
+ num_msgs, true);
+
+ // Verify that we can start sending.
+ EXPECT_NO_THROW(sender.startSending(io_service));
+ EXPECT_TRUE(sender.amSending());
+
+ // Queue up messages.
+ NameChangeRequestPtr ncr;
+ for (int i = 0; i < num_msgs; i++) {
+ ASSERT_NO_THROW(ncr = NameChangeRequest::fromJSON(valid_msgs[i]));
+ EXPECT_NO_THROW(sender.sendRequest(ncr));
+ }
+ // Make sure queue count is what we expect.
+ EXPECT_EQ(num_msgs, sender.getQueueSize());
+
+ // Stop sending.
+ ASSERT_NO_THROW(sender.stopSending());
+ ASSERT_FALSE(sender.amSending());
+
+ // We should have completed the first message only.
+ EXPECT_EQ(--num_msgs, sender.getQueueSize());
+
+ // Restart sending.
+ EXPECT_NO_THROW(sender.startSending(io_service));
+
+ // We should be able to loop through remaining messages and send them.
+ for (int i = num_msgs; i > 0; i--) {
+ // ioReady() should evaluate to true.
+ ASSERT_TRUE(sender.ioReady());
+
+ // Execute at one ready handler.
+ ASSERT_NO_THROW(sender.runReadyIO());
+ }
+
+ // Verify that the queue is empty.
+ EXPECT_EQ(0, sender.getQueueSize());
+}
+
+/// @brief Tests NameChangeUDPSender basic send with INADDR_ANY and port 0.
+TEST(NameChangeUDPSenderBasicTest, anyAddressSend) {
+ isc::asiolink::IOAddress ip_address(TEST_ADDRESS);
+ isc::asiolink::IOAddress any_address("0.0.0.0");
+ isc::asiolink::IOService io_service;
+ SimpleSendHandler ncr_handler;
+
+ // Tests are based on a list of messages, get the count now.
+ int num_msgs = sizeof(valid_msgs)/sizeof(char*);
+
+ // Create the sender, setting the queue max equal to the number of
+ // messages we will have in the list.
+ NameChangeUDPSender sender(any_address, 0, ip_address, LISTENER_PORT,
+ FMT_JSON, ncr_handler, num_msgs);
+
+ // Enter send mode.
+ ASSERT_NO_THROW(sender.startSending(io_service));
+ EXPECT_TRUE(sender.amSending());
+
+ // Create and queue up a message.
+ NameChangeRequestPtr ncr;
+ ASSERT_NO_THROW(ncr = NameChangeRequest::fromJSON(valid_msgs[0]));
+ EXPECT_NO_THROW(sender.sendRequest(ncr));
+ EXPECT_EQ(1, sender.getQueueSize());
+
+ // Verify we have a ready IO, then execute at one ready handler.
+ ASSERT_TRUE(sender.ioReady());
+ ASSERT_NO_THROW(sender.runReadyIO());
+
+ // Verify that sender shows no IO ready.
+ // and that the queue is empty.
+ ASSERT_FALSE(sender.ioReady());
+ EXPECT_EQ(0, sender.getQueueSize());
+}
+
+/// @brief Test the NameChangeSender::assumeQueue method.
+TEST(NameChangeSender, assumeQueue) {
+ isc::asiolink::IOAddress ip_address(TEST_ADDRESS);
+ uint32_t port = SENDER_PORT;
+ isc::asiolink::IOService io_service;
+ SimpleSendHandler ncr_handler;
+ NameChangeRequestPtr ncr;
+
+ // Tests are based on a list of messages, get the count now.
+ int num_msgs = sizeof(valid_msgs)/sizeof(char*);
+
+ // Create two senders with queue max equal to the number of
+ // messages we will have in the list.
+ NameChangeUDPSender sender1(ip_address, port, ip_address, port,
+ FMT_JSON, ncr_handler, num_msgs);
+
+ NameChangeUDPSender sender2(ip_address, port+1, ip_address, port,
+ FMT_JSON, ncr_handler, num_msgs);
+
+ // Place sender1 into send mode and queue up messages.
+ ASSERT_NO_THROW(sender1.startSending(io_service));
+ for (int i = 0; i < num_msgs; i++) {
+ ASSERT_NO_THROW(ncr = NameChangeRequest::fromJSON(valid_msgs[i]));
+ ASSERT_NO_THROW(sender1.sendRequest(ncr));
+ }
+
+ // Make sure sender1's queue count is as expected.
+ ASSERT_EQ(num_msgs, sender1.getQueueSize());
+
+ // Verify sender1 is sending, sender2 is not.
+ ASSERT_TRUE(sender1.amSending());
+ ASSERT_FALSE(sender2.amSending());
+
+ // Transfer from sender1 to sender2 should fail because
+ // sender1 is in send mode.
+ ASSERT_THROW(sender2.assumeQueue(sender1), NcrSenderError);
+
+ // Take sender1 out of send mode.
+ ASSERT_NO_THROW(sender1.stopSending());
+ ASSERT_FALSE(sender1.amSending());
+ // Stopping should have completed the first message.
+ --num_msgs;
+ EXPECT_EQ(num_msgs, sender1.getQueueSize());
+
+ // Transfer should succeed. Verify sender1 has none,
+ // and sender2 has num_msgs queued.
+ EXPECT_NO_THROW(sender2.assumeQueue(sender1));
+ EXPECT_EQ(0, sender1.getQueueSize());
+ EXPECT_EQ(num_msgs, sender2.getQueueSize());
+
+ // Reduce sender1's max queue size.
+ ASSERT_NO_THROW(sender1.setQueueMaxSize(num_msgs - 1));
+
+ // Transfer should fail as sender1's queue is not large enough.
+ ASSERT_THROW(sender1.assumeQueue(sender2), NcrSenderError);
+
+ // Place sender1 into send mode and queue up a message.
+ ASSERT_NO_THROW(sender1.startSending(io_service));
+ ASSERT_NO_THROW(ncr = NameChangeRequest::fromJSON(valid_msgs[0]));
+ ASSERT_NO_THROW(sender1.sendRequest(ncr));
+
+ // Take sender1 out of send mode.
+ ASSERT_NO_THROW(sender1.stopSending());
+
+ // Try to transfer from sender1 to sender2. This should fail
+ // as sender2's queue is not empty.
+ ASSERT_THROW(sender2.assumeQueue(sender1), NcrSenderError);
+}
+
/// @brief Text fixture that allows testing a listener and sender together
/// It derives from both the receive and send handler classes and contains
/// and instance of UDP listener and UDP sender.
@@ -422,9 +631,9 @@ public:
*this, true));
// Create our sender instance. Note that reuse_address is true.
- sender_.reset(
- new NameChangeUDPSender(addr, SENDER_PORT, addr, LISTENER_PORT,
- FMT_JSON, *this, 100, true));
+ sender_.reset(
+ new NameChangeUDPSender(addr, SENDER_PORT, addr, LISTENER_PORT,
+ FMT_JSON, *this, 100, true));
// Set the test timeout to break any running tasks if they hang.
test_timer_.setup(boost::bind(&NameChangeUDPTest::testTimeoutHandler,
@@ -515,4 +724,123 @@ TEST_F (NameChangeUDPTest, roundTripTest) {
EXPECT_FALSE(sender_->amSending());
}
+// Tests error handling of a failure to mark the watch socket ready, when
+// sendRequestt() is called.
+TEST(NameChangeUDPSenderBasicTest, watchClosedBeforeSendRequest) {
+ isc::asiolink::IOAddress ip_address(TEST_ADDRESS);
+ isc::asiolink::IOService io_service;
+ SimpleSendHandler ncr_handler;
+
+ // Create the sender and put into send mode.
+ NameChangeUDPSender sender(ip_address, 0, ip_address, LISTENER_PORT,
+ FMT_JSON, ncr_handler, 100, true);
+ ASSERT_NO_THROW(sender.startSending(io_service));
+ ASSERT_TRUE(sender.amSending());
+
+ // Create an NCR.
+ NameChangeRequestPtr ncr;
+ ASSERT_NO_THROW(ncr = NameChangeRequest::fromJSON(valid_msgs[0]));
+
+ // Tamper with the watch socket by closing the select-fd.
+ close(sender.getSelectFd());
+
+ // Send should fail as we interferred by closing the select-fd.
+ ASSERT_THROW(sender.sendRequest(ncr), WatchSocketError);
+
+ // Verify we didn't invoke the handler.
+ EXPECT_EQ(0, ncr_handler.pass_count_);
+ EXPECT_EQ(0, ncr_handler.error_count_);
+
+ // Request remains in the queue. Technically it was sent but its
+ // completion handler won't get called.
+ EXPECT_EQ(1, sender.getQueueSize());
+}
+
+// Tests error handling of a failure to mark the watch socket ready, when
+// sendNext() is called during completion handling.
+TEST(NameChangeUDPSenderBasicTest, watchClosedAfterSendRequest) {
+ isc::asiolink::IOAddress ip_address(TEST_ADDRESS);
+ isc::asiolink::IOService io_service;
+ SimpleSendHandler ncr_handler;
+
+ // Create the sender and put into send mode.
+ NameChangeUDPSender sender(ip_address, 0, ip_address, LISTENER_PORT,
+ FMT_JSON, ncr_handler, 100, true);
+ ASSERT_NO_THROW(sender.startSending(io_service));
+ ASSERT_TRUE(sender.amSending());
+
+ // Build and queue up 2 messages. No handlers will get called yet.
+ for (int i = 0; i < 2; i++) {
+ NameChangeRequestPtr ncr;
+ ASSERT_NO_THROW(ncr = NameChangeRequest::fromJSON(valid_msgs[i]));
+ sender.sendRequest(ncr);
+ EXPECT_EQ(i+1, sender.getQueueSize());
+ }
+
+ // Tamper with the watch socket by closing the select-fd.
+ close (sender.getSelectFd());
+
+ // Run one handler. This should execute the send completion handler
+ // after sending the first message. Duing completion handling, we will
+ // attempt to queue the second message which should fail.
+ ASSERT_NO_THROW(sender.runReadyIO());
+
+ // Verify handler got called twice. First request should have be sent
+ // without error, second call should have failed to send due to watch
+ // socket markReady failure.
+ EXPECT_EQ(1, ncr_handler.pass_count_);
+ EXPECT_EQ(1, ncr_handler.error_count_);
+
+ // The second request should still be in the queue.
+ EXPECT_EQ(1, sender.getQueueSize());
+}
+
+// Tests error handling of a failure to clear the watch socket during
+// completion handling.
+TEST(NameChangeUDPSenderBasicTest, watchSocketBadRead) {
+ isc::asiolink::IOAddress ip_address(TEST_ADDRESS);
+ isc::asiolink::IOService io_service;
+ SimpleSendHandler ncr_handler;
+
+ // Create the sender and put into send mode.
+ NameChangeUDPSender sender(ip_address, 0, ip_address, LISTENER_PORT,
+ FMT_JSON, ncr_handler, 100, true);
+ ASSERT_NO_THROW(sender.startSending(io_service));
+ ASSERT_TRUE(sender.amSending());
+
+ // Build and queue up 2 messages. No handlers will get called yet.
+ for (int i = 0; i < 2; i++) {
+ NameChangeRequestPtr ncr;
+ ASSERT_NO_THROW(ncr = NameChangeRequest::fromJSON(valid_msgs[i]));
+ sender.sendRequest(ncr);
+ EXPECT_EQ(i+1, sender.getQueueSize());
+ }
+
+ // Fetch the sender's select-fd.
+ int select_fd = sender.getSelectFd();
+
+ // Verify that select_fd appears ready.
+ ASSERT_TRUE(selectCheck(select_fd) > 0);
+
+ // Interfere by reading part of the marker from the select-fd.
+ uint32_t buf = 0;
+ ASSERT_EQ((read (select_fd, &buf, 1)), 1);
+ ASSERT_NE(WatchSocket::MARKER, buf);
+
+ // Run one handler. This should execute the send completion handler
+ // after sending the message. Duing completion handling clearing the
+ // watch socket should fail, which will close the socket, but not
+ // result in a throw.
+ ASSERT_NO_THROW(sender.runReadyIO());
+
+ // Verify handler got called twice. First request should have be sent
+ // without error, second call should have failed to send due to watch
+ // socket markReady failure.
+ EXPECT_EQ(1, ncr_handler.pass_count_);
+ EXPECT_EQ(1, ncr_handler.error_count_);
+
+ // The second request should still be in the queue.
+ EXPECT_EQ(1, sender.getQueueSize());
+}
+
} // end of anonymous namespace
diff --git a/src/lib/dhcp_ddns/tests/test_utils.cc b/src/lib/dhcp_ddns/tests/test_utils.cc
new file mode 100644
index 0000000..34d669e
--- /dev/null
+++ b/src/lib/dhcp_ddns/tests/test_utils.cc
@@ -0,0 +1,43 @@
+// Copyright (C) 2014 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <gtest/gtest.h>
+
+#include <sys/select.h>
+#include <sys/ioctl.h>
+
+using namespace std;
+
+namespace isc {
+namespace dhcp_ddns {
+
+int selectCheck(int fd_to_check) {
+ fd_set read_fds;
+ int maxfd = 0;
+
+ FD_ZERO(&read_fds);
+
+ // Add this socket to listening set
+ FD_SET(fd_to_check, &read_fds);
+ maxfd = fd_to_check;
+
+ struct timeval select_timeout;
+ select_timeout.tv_sec = 0;
+ select_timeout.tv_usec = 0;
+
+ return (select(maxfd + 1, &read_fds, NULL, NULL, &select_timeout));
+}
+
+}; // namespace isc::d2
+}; // namespace isc
diff --git a/src/lib/dhcp_ddns/tests/test_utils.h b/src/lib/dhcp_ddns/tests/test_utils.h
new file mode 100644
index 0000000..71e23e9
--- /dev/null
+++ b/src/lib/dhcp_ddns/tests/test_utils.h
@@ -0,0 +1,37 @@
+// Copyright (C) 2014 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef TEST_UTILS_H
+#define TEST_UTILS_H
+
+/// @file test_utils.h Common dhcp_ddns testing elements
+
+#include <gtest/gtest.h>
+
+
+namespace isc {
+namespace dhcp_ddns {
+
+/// @brief Returns the result of select() given an fd to check for read status.
+///
+/// @param fd_to_check The file descriptor to test
+///
+/// @return Returns less than one on an error, 0 if the fd is not ready to
+/// read, > 0 if it is ready to read.
+int selectCheck(int fd_to_check);
+
+}; // namespace isc::dhcp_ddns;
+}; // namespace isc;
+
+#endif
diff --git a/src/lib/dhcp_ddns/tests/watch_socket_unittests.cc b/src/lib/dhcp_ddns/tests/watch_socket_unittests.cc
new file mode 100644
index 0000000..e19b8cb
--- /dev/null
+++ b/src/lib/dhcp_ddns/tests/watch_socket_unittests.cc
@@ -0,0 +1,207 @@
+// Copyright (C) 2014 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 <dhcp_ddns/watch_socket.h>
+#include <test_utils.h>
+
+#include <gtest/gtest.h>
+
+#include <sys/select.h>
+#include <sys/ioctl.h>
+
+using namespace std;
+using namespace isc;
+using namespace isc::dhcp_ddns;
+
+namespace {
+
+/// @brief Tests the basic functionality of WatchSocket.
+TEST(WatchSocketTest, basics) {
+ WatchSocketPtr watch;
+
+ /// Verify that we can construct a WatchSocket.
+ ASSERT_NO_THROW(watch.reset(new WatchSocket()));
+ ASSERT_TRUE(watch);
+
+ /// Verify that post-construction the state the select-fd is valid.
+ int select_fd = watch->getSelectFd();
+ EXPECT_NE(select_fd, WatchSocket::INVALID_SOCKET);
+
+ /// Verify that isReady() is false and that a call to select agrees.
+ EXPECT_FALSE(watch->isReady());
+ EXPECT_EQ(0, selectCheck(select_fd));
+
+ /// Verify that the socket can be marked ready.
+ ASSERT_NO_THROW(watch->markReady());
+
+ /// Verify that we have exactly one marker waiting to be read.
+ int count = 0;
+ EXPECT_FALSE(ioctl(select_fd, FIONREAD, &count));
+ EXPECT_EQ(sizeof(WatchSocket::MARKER), count);
+
+ /// Verify that we can call markReady again without error.
+ ASSERT_NO_THROW(watch->markReady());
+
+ /// Verify that we STILL have exactly one marker waiting to be read.
+ EXPECT_FALSE(ioctl(select_fd, FIONREAD, &count));
+ EXPECT_EQ(sizeof(WatchSocket::MARKER), count);
+
+ /// Verify that isReady() is true and that a call to select agrees.
+ EXPECT_TRUE(watch->isReady());
+ EXPECT_EQ(1, selectCheck(select_fd));
+
+ /// Verify that the socket can be cleared.
+ ASSERT_NO_THROW(watch->clearReady());
+
+ /// Verify that isReady() is false and that a call to select agrees.
+ EXPECT_FALSE(watch->isReady());
+ EXPECT_EQ(0, selectCheck(select_fd));
+}
+
+/// @brief Checks behavior when select_fd is closed externally while in the
+/// "cleared" state.
+TEST(WatchSocketTest, closedWhileClear) {
+ WatchSocketPtr watch;
+
+ /// Verify that we can construct a WatchSocket.
+ ASSERT_NO_THROW(watch.reset(new WatchSocket()));
+ ASSERT_TRUE(watch);
+
+ /// Verify that post-construction the state the select-fd is valid.
+ int select_fd = watch->getSelectFd();
+ ASSERT_NE(select_fd, WatchSocket::INVALID_SOCKET);
+
+ // Verify that socket does not appear ready.
+ ASSERT_EQ(0, watch->isReady());
+
+ // Interfere by closing the fd.
+ ASSERT_EQ(0, close(select_fd));
+
+ // Verify that socket does not appear ready.
+ ASSERT_EQ(0, watch->isReady());
+
+ // Verify that clear does NOT throw.
+ ASSERT_NO_THROW(watch->clearReady());
+
+ // Verify that trying to mark it fails.
+ ASSERT_THROW(watch->markReady(), WatchSocketError);
+
+ // Verify that clear does NOT throw.
+ ASSERT_NO_THROW(watch->clearReady());
+
+ // Verify that getSelectFd() returns invalid socket.
+ ASSERT_EQ(WatchSocket::INVALID_SOCKET, watch->getSelectFd());
+}
+
+/// @brief Checks behavior when select_fd has closed while in the "ready"
+/// state.
+TEST(WatchSocketTest, closedWhileReady) {
+ WatchSocketPtr watch;
+
+ /// Verify that we can construct a WatchSocket.
+ ASSERT_NO_THROW(watch.reset(new WatchSocket()));
+ ASSERT_TRUE(watch);
+
+ /// Verify that post-construction the state the select-fd is valid.
+ int select_fd = watch->getSelectFd();
+ ASSERT_NE(select_fd, WatchSocket::INVALID_SOCKET);
+
+ /// Verify that the socket can be marked ready.
+ ASSERT_NO_THROW(watch->markReady());
+ EXPECT_EQ(1, selectCheck(select_fd));
+
+ // Interfere by closing the fd.
+ ASSERT_EQ(0, close(select_fd));
+
+ // Verify that trying to clear it does not throw.
+ ASSERT_NO_THROW(watch->clearReady());
+
+ // Verify the select_fd fails as socket is invalid/closed.
+ EXPECT_EQ(-1, selectCheck(select_fd));
+
+ // Verify that subsequent attempts to mark it will fail.
+ ASSERT_THROW(watch->markReady(), WatchSocketError);
+}
+
+/// @brief Checks behavior when select_fd has been marked ready but then
+/// emptied by an external read.
+TEST(WatchSocketTest, emptyReadySelectFd) {
+ WatchSocketPtr watch;
+
+ /// Verify that we can construct a WatchSocket.
+ ASSERT_NO_THROW(watch.reset(new WatchSocket()));
+ ASSERT_TRUE(watch);
+
+ /// Verify that post-construction the state the select-fd is valid.
+ int select_fd = watch->getSelectFd();
+ ASSERT_NE(select_fd, WatchSocket::INVALID_SOCKET);
+
+ /// Verify that the socket can be marked ready.
+ ASSERT_NO_THROW(watch->markReady());
+ EXPECT_EQ(1, selectCheck(select_fd));
+
+ // Interfere by reading the fd. This should empty the read pipe.
+ uint32_t buf = 0;
+ ASSERT_EQ((read (select_fd, &buf, sizeof(buf))), sizeof(buf));
+ ASSERT_EQ(WatchSocket::MARKER, buf);
+
+ // Really nothing that can be done to protect against this, but let's
+ // make sure we aren't in a weird state.
+ ASSERT_NO_THROW(watch->clearReady());
+
+ // Verify the select_fd fails as socket is invalid/closed.
+ EXPECT_EQ(0, selectCheck(select_fd));
+
+ // Verify that getSelectFd() returns is still good.
+ ASSERT_EQ(select_fd, watch->getSelectFd());
+}
+
+/// @brief Checks behavior when select_fd has been marked ready but then
+/// contents have been "corrupted" by a partial read.
+TEST(WatchSocketTest, badReadOnClear) {
+ WatchSocketPtr watch;
+
+ /// Verify that we can construct a WatchSocket.
+ ASSERT_NO_THROW(watch.reset(new WatchSocket()));
+ ASSERT_TRUE(watch);
+
+ /// Verify that post-construction the state the select-fd is valid.
+ int select_fd = watch->getSelectFd();
+ ASSERT_NE(select_fd, WatchSocket::INVALID_SOCKET);
+
+ /// Verify that the socket can be marked ready.
+ ASSERT_NO_THROW(watch->markReady());
+ EXPECT_EQ(1, selectCheck(select_fd));
+
+ // Interfere by reading the fd. This should empty the read pipe.
+ uint32_t buf = 0;
+ ASSERT_EQ((read (select_fd, &buf, 1)), 1);
+ ASSERT_NE(WatchSocket::MARKER, buf);
+
+ // Really nothing that can be done to protect against this, but let's
+ // make sure we aren't in a weird state.
+ /// @todo maybe clear should never throw, log only
+ ASSERT_THROW(watch->clearReady(), WatchSocketError);
+
+ // Verify the select_fd does not evalute to ready.
+ EXPECT_NE(1, selectCheck(select_fd));
+
+ // Verify that getSelectFd() returns INVALID.
+ ASSERT_EQ(WatchSocket::INVALID_SOCKET, watch->getSelectFd());
+
+ // Verify that subsequent attempt to mark it fails.
+ ASSERT_THROW(watch->markReady(), WatchSocketError);
+}
+
+} // end of anonymous namespace
diff --git a/src/lib/dhcp_ddns/watch_socket.cc b/src/lib/dhcp_ddns/watch_socket.cc
new file mode 100644
index 0000000..fd4027e
--- /dev/null
+++ b/src/lib/dhcp_ddns/watch_socket.cc
@@ -0,0 +1,152 @@
+// Copyright (C) 2014 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.
+
+/// @file watch_socket.cc
+
+#include <dhcp_ddns/dhcp_ddns_log.h>
+#include <dhcp_ddns/watch_socket.h>
+
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/select.h>
+
+namespace isc {
+namespace dhcp_ddns {
+
+
+const int WatchSocket::INVALID_SOCKET;
+const uint32_t WatchSocket::MARKER;
+
+WatchSocket::WatchSocket()
+ : source_(INVALID_SOCKET), sink_(INVALID_SOCKET) {
+ // Open the pipe.
+ int fds[2];
+ if (pipe(fds)) {
+ const char* errstr = strerror(errno);
+ isc_throw(WatchSocketError, "Cannot construct pipe: " << errstr);
+ }
+
+ source_ = fds[1];
+ sink_ = fds[0];
+
+ if (fcntl(sink_, F_SETFL, O_NONBLOCK)) {
+ const char* errstr = strerror(errno);
+ isc_throw(WatchSocketError, "Cannot set sink to non-blocking: "
+ << errstr);
+ }
+}
+
+WatchSocket::~WatchSocket() {
+ closeSocket();
+}
+
+void
+WatchSocket::markReady() {
+ // Make sure it hasn't been orphaned! Otherwise we may get SIGPIPE. We
+ // use fcntl to check as select() on some systems may show it as ready to
+ // read.
+ if (fcntl(sink_, F_GETFL) < 0) {
+ closeSocket();
+ isc_throw(WatchSocketError, "WatchSocket markReady failed:"
+ " select_fd was closed!");
+ }
+
+ if (!isReady()) {
+ int nbytes = write (source_, &MARKER, sizeof(MARKER));
+ if (nbytes != sizeof(MARKER)) {
+ // If there's an error get the error message than close
+ // the pipe. This should ensure any further use of the socket
+ // or testing the fd with select_fd will fail.
+ const char* errstr = strerror(errno);
+ closeSocket();
+ isc_throw(WatchSocketError, "WatchSocket markReady failed:"
+ << " bytes written: " << nbytes << " : " << errstr);
+ }
+ }
+}
+
+bool
+WatchSocket::isReady() {
+ // Report it as not ready rather than error here.
+ if (sink_ == INVALID_SOCKET) {
+ return (false);
+ }
+
+ fd_set read_fds;
+ FD_ZERO(&read_fds);
+
+ // Add select_fd socket to listening set
+ FD_SET(sink_, &read_fds);
+
+ // Set zero timeout (non-blocking).
+ struct timeval select_timeout;
+ select_timeout.tv_sec = 0;
+ select_timeout.tv_usec = 0;
+
+ // Return true only if read ready, treat error same as not ready.
+ return (select(sink_ + 1, &read_fds, NULL, NULL, &select_timeout) > 0);
+}
+
+void
+WatchSocket::clearReady() {
+ if (isReady()) {
+ uint32_t buf = 0;
+ int nbytes = read (sink_, &buf, sizeof(buf));
+ if ((nbytes != sizeof(MARKER) || (buf != MARKER))) {
+ // If there's an error get the error message than close
+ // the pipe. This should ensure any further use of the socket
+ // or testing the fd with select_fd will fail.
+ const char* errstr = strerror(errno);
+ closeSocket();
+ isc_throw(WatchSocketError, "WatchSocket clearReady failed:"
+ << " bytes read: " << nbytes << " : "
+ << " value read: " << buf << " error :" <<errstr);
+ }
+ }
+}
+
+void
+WatchSocket::closeSocket() {
+ // Close the pipe fds. Technically a close can fail (hugely unlikely)
+ // but there's no recovery for it either. If one does fail we log it
+ // and go on. Plus this is called by the destructor and no one likes
+ // destructors that throw.
+ if (source_ != INVALID_SOCKET) {
+ if (close(source_)) {
+ const char* errstr = strerror(errno);
+ LOG_ERROR(dhcp_ddns_logger, DHCP_DDNS_WATCH_SOURCE_CLOSE_ERROR)
+ .arg(errstr);
+ }
+
+ source_ = INVALID_SOCKET;
+ }
+
+ if (sink_ != INVALID_SOCKET) {
+ if (close(sink_)) {
+ const char* errstr = strerror(errno);
+ LOG_ERROR(dhcp_ddns_logger, DHCP_DDNS_WATCH_SINK_CLOSE_ERROR)
+ .arg(errstr);
+ }
+
+ sink_ = INVALID_SOCKET;
+ }
+}
+
+int
+WatchSocket::getSelectFd() {
+ return (sink_);
+}
+
+} // namespace isc::dhcp_ddns
+} // namespace isc
diff --git a/src/lib/dhcp_ddns/watch_socket.h b/src/lib/dhcp_ddns/watch_socket.h
new file mode 100644
index 0000000..f067ea6
--- /dev/null
+++ b/src/lib/dhcp_ddns/watch_socket.h
@@ -0,0 +1,138 @@
+// Copyright (C) 2014 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 WATCH_SOCKET_H
+#define WATCH_SOCKET_H
+
+/// @file watch_socket.h Defines the class, WatchSocket.
+
+#include <exceptions/exceptions.h>
+
+#include <boost/shared_ptr.hpp>
+
+#include <stdint.h>
+
+namespace isc {
+namespace dhcp_ddns {
+
+/// @brief Exception thrown if an error occurs during IO source open.
+class WatchSocketError : public isc::Exception {
+public:
+ WatchSocketError(const char* file, size_t line, const char* what) :
+ isc::Exception(file, line, what) { };
+};
+
+/// @brief Provides an IO "ready" semaphore for use with select() or poll()
+/// WatchSocket exposes a single open file descriptor, the "select-fd" which
+/// can be marked as being ready to read (i.e. !EWOULDBLOCK) and cleared
+/// (i.e. EWOULDBLOCK). The select-fd can be used with select(), poll(), or
+/// their variants alongside other file descriptors.
+///
+/// Internally, WatchSocket uses a pipe. The select-fd is the "read" end of
+/// pipe. To mark the socket as ready to read, an integer marker is written
+/// to the pipe. To clear the socket, the marker is read from the pipe. Note
+/// that WatchSocket will only write the marker if it is not already marked.
+/// This prevents the socket's pipe from filling endlessly.
+///
+/// @warning Because the read or "sink" side of the pipe is used as the select_fd,
+/// it is possible for that fd to be interfered with, albeit only from within the
+/// process space which owns it. Performing operations that may alter the fd's state
+/// such as close, read, or altering behavior flags with fcntl or ioctl can have
+/// unpredictable results. It is intended strictly use with functions such as select()
+/// poll() or their variants.
+class WatchSocket {
+public:
+ /// @brief Value used to signify an invalid descriptor.
+ static const int INVALID_SOCKET = -1;
+ /// @brief Value written to the source when marking the socket as ready.
+ /// The value itself is arbitrarily chosen as one that is unlikely to occur
+ /// otherwise and easy to debug.
+ static const uint32_t MARKER = 0xDEADBEEF;
+
+ /// @brief Constructor
+ ///
+ /// Constructs an instance of the WatchSocket in the cleared (EWOULDBLOCK)
+ /// state.
+ WatchSocket();
+
+ /// @brief Destructor
+ ///
+ /// Closes all internal resources, including the select-fd.
+ virtual ~WatchSocket();
+
+ /// @brief Marks the select-fd as ready to read.
+ ///
+ /// Marks the socket as ready to read, if is not already so marked.
+ /// If an error occurs, closeSocket is called. This will force any further
+ /// use of the select_fd to fail rather than show the fd as READY. Such
+ /// an error is almost surely a programmatic error which has corrupted the
+ /// select_fd.
+ ///
+ /// @throw WatchSocketError if an error occurs marking the socket.
+ void markReady();
+
+ /// @brief Returns true the if socket is marked as ready.
+ ///
+ /// This method uses a non-blocking call to select() to test read state of the
+ /// select_fd. Rather than track what the status "should be" it tests the status.
+ /// This should eliminate conditions where the select-fd appear to be perpetually
+ /// ready.
+ /// @return Returns true if select_fd is not INVALID_SOCKET and select() reports it
+ /// as !EWOULDBLOCK, otherwise it returns false.
+ /// This method is guaranteed NOT to throw.
+ bool isReady();
+
+ /// @brief Clears the socket's ready to read marker.
+ ///
+ /// Clears the socket if it is currently marked as ready to read.
+ /// If an error occurs, closeSocket is called. This will force any further
+ /// use of the select_fd to fail rather than show the fd as READY. Such
+ /// an error is almost surely a programmatic error which has corrupted the
+ /// select_fd.
+ ///
+ /// @throw WatchSocketError if an error occurs clearing the socket
+ /// marker.
+ void clearReady();
+
+ /// @brief Returns the file descriptor to use to monitor the socket.
+ ///
+ /// @note Using this file descriptor as anything other than an argument
+ /// to select() or similar methods can have unpredictable results.
+ ///
+ /// @return The file descriptor associated with read end of the socket's
+ /// pipe.
+ int getSelectFd();
+
+private:
+ /// @brief Closes the descriptors associated with the socket.
+ ///
+ /// Used internally in the destructor and if an error occurs marking or
+ /// clearing the socket.
+ void closeSocket();
+
+ /// @brief The end of the pipe to which the marker is written
+ int source_;
+
+ /// @brief The end of the pipe from which the marker is read.
+ /// This is the value returned as the select-fd.
+ int sink_;
+};
+
+/// @brief Defines a smart pointer to an instance of a WatchSocket.
+typedef boost::shared_ptr<WatchSocket> WatchSocketPtr;
+
+} // namespace isc::dhcp_ddns
+} // namespace isc
+
+#endif
diff --git a/src/lib/dhcpsrv/Makefile.am b/src/lib/dhcpsrv/Makefile.am
index 69b14b2..1c8a18a 100644
--- a/src/lib/dhcpsrv/Makefile.am
+++ b/src/lib/dhcpsrv/Makefile.am
@@ -39,7 +39,8 @@ libb10_dhcpsrv_la_SOURCES =
libb10_dhcpsrv_la_SOURCES += addr_utilities.cc addr_utilities.h
libb10_dhcpsrv_la_SOURCES += alloc_engine.cc alloc_engine.h
libb10_dhcpsrv_la_SOURCES += callout_handle_store.h
-libb10_dhcpsrv_la_SOURCES += d2_client.cc d2_client.h
+libb10_dhcpsrv_la_SOURCES += d2_client_cfg.cc d2_client_cfg.h
+libb10_dhcpsrv_la_SOURCES += d2_client_mgr.cc d2_client_mgr.h
libb10_dhcpsrv_la_SOURCES += dbaccess_parser.cc dbaccess_parser.h
libb10_dhcpsrv_la_SOURCES += dhcpsrv_log.cc dhcpsrv_log.h
libb10_dhcpsrv_la_SOURCES += cfgmgr.cc cfgmgr.h
diff --git a/src/lib/dhcpsrv/alloc_engine.cc b/src/lib/dhcpsrv/alloc_engine.cc
index 37f2fdf..11b0700 100644
--- a/src/lib/dhcpsrv/alloc_engine.cc
+++ b/src/lib/dhcpsrv/alloc_engine.cc
@@ -1,4 +1,4 @@
-// Copyright (C) 2012-2013 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2012-2014 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
@@ -294,11 +294,12 @@ AllocEngine::AllocEngine(AllocType engine_type, unsigned int attempts,
Lease6Collection
AllocEngine::allocateLeases6(const Subnet6Ptr& subnet, const DuidPtr& duid,
- uint32_t iaid, const IOAddress& hint,
+ const uint32_t iaid, const IOAddress& hint,
Lease::Type type, const bool fwd_dns_update,
const bool rev_dns_update,
const std::string& hostname, bool fake_allocation,
- const isc::hooks::CalloutHandlePtr& callout_handle) {
+ const isc::hooks::CalloutHandlePtr& callout_handle,
+ Lease6Collection& old_leases) {
try {
AllocatorPtr allocator = getAllocator(type);
@@ -316,37 +317,49 @@ AllocEngine::allocateLeases6(const Subnet6Ptr& subnet, const DuidPtr& duid,
isc_throw(InvalidOperation, "DUID is mandatory for allocation");
}
- // check if there's existing lease for that subnet/duid/iaid combination.
+ // Check if there's existing lease for that subnet/duid/iaid
+ // combination.
/// @todo: Make this generic (cover temp. addrs and prefixes)
Lease6Collection existing = LeaseMgrFactory::instance().getLeases6(type,
*duid, iaid, subnet->getID());
+ // There is at least one lease for this client. We will return these
+ // leases for the client, but we may need to update FQDN information.
if (!existing.empty()) {
- // we have at least one lease already. This is a returning client,
- // probably after his reboot.
- return (existing);
+ // Return old leases so the server can see what has changed.
+ old_leases = existing;
+ return (updateFqdnData(existing, fwd_dns_update, rev_dns_update,
+ hostname, fake_allocation));
}
// check if the hint is in pool and is available
// This is equivalent of subnet->inPool(hint), but returns the pool
- Pool6Ptr pool = boost::dynamic_pointer_cast<Pool6>(subnet->getPool(type, hint, false));
+ Pool6Ptr pool = boost::dynamic_pointer_cast<
+ Pool6>(subnet->getPool(type, hint, false));
if (pool) {
/// @todo: We support only one hint for now
Lease6Ptr lease = LeaseMgrFactory::instance().getLease6(type, hint);
if (!lease) {
- /// @todo: check if the hint is reserved once we have host support
- /// implemented
-
- // the hint is valid and not currently used, let's create a lease for it
- lease = createLease6(subnet, duid, iaid, hint, pool->getLength(),
- type, fwd_dns_update, rev_dns_update,
+ /// @todo: check if the hint is reserved once we have host
+ /// support implemented
+
+ // The hint is valid and not currently used, let's create a
+ // lease for it
+ lease = createLease6(subnet, duid, iaid, hint,
+ pool->getLength(), type,
+ fwd_dns_update, rev_dns_update,
hostname, callout_handle, fake_allocation);
- // It can happen that the lease allocation failed (we could have lost
- // the race condition. That means that the hint is lo longer usable and
- // we need to continue the regular allocation path.
+ // It can happen that the lease allocation failed (we could
+ // have lost the race condition. That means that the hint is
+ // lo longer usable and we need to continue the regular
+ // allocation path.
if (lease) {
+ // We are allocating a new lease (not renewing). So, the
+ // old lease should be NULL.
+ old_leases.push_back(Lease6Ptr());
+
/// @todo: We support only one lease per ia for now
Lease6Collection collection;
collection.push_back(lease);
@@ -354,6 +367,11 @@ AllocEngine::allocateLeases6(const Subnet6Ptr& subnet, const DuidPtr& duid,
}
} else {
if (lease->expired()) {
+ // Copy an existing, expired lease so as it can be returned
+ // to the caller.
+ Lease6Ptr old_lease(new Lease6(*lease));
+ old_leases.push_back(old_lease);
+
/// We found a lease and it is expired, so we can reuse it
lease = reuseExpiredLease(lease, subnet, duid, iaid,
pool->getLength(),
@@ -414,6 +432,10 @@ AllocEngine::allocateLeases6(const Subnet6Ptr& subnet, const DuidPtr& duid,
rev_dns_update, hostname,
callout_handle, fake_allocation);
if (lease) {
+ // We are allocating a new lease (not renewing). So, the
+ // old lease should be NULL.
+ old_leases.push_back(Lease6Ptr());
+
Lease6Collection collection;
collection.push_back(lease);
return (collection);
@@ -424,6 +446,11 @@ AllocEngine::allocateLeases6(const Subnet6Ptr& subnet, const DuidPtr& duid,
// allocation attempts.
} else {
if (existing->expired()) {
+ // Copy an existing, expired lease so as it can be returned
+ // to the caller.
+ Lease6Ptr old_lease(new Lease6(*existing));
+ old_leases.push_back(old_lease);
+
existing = reuseExpiredLease(existing, subnet, duid, iaid,
prefix_len, fwd_dns_update,
rev_dns_update, hostname,
@@ -1037,6 +1064,30 @@ Lease4Ptr AllocEngine::createLease4(const SubnetPtr& subnet,
}
}
+Lease6Collection
+AllocEngine::updateFqdnData(const Lease6Collection& leases,
+ const bool fwd_dns_update,
+ const bool rev_dns_update,
+ const std::string& hostname,
+ const bool fake_allocation) {
+ Lease6Collection updated_leases;
+ for (Lease6Collection::const_iterator lease_it = leases.begin();
+ lease_it != leases.end(); ++lease_it) {
+ Lease6Ptr lease(new Lease6(**lease_it));
+ lease->fqdn_fwd_ = fwd_dns_update;
+ lease->fqdn_rev_ = rev_dns_update;
+ lease->hostname_ = hostname;
+ if (!fake_allocation &&
+ ((lease->fqdn_fwd_ != (*lease_it)->fqdn_fwd_) ||
+ (lease->fqdn_rev_ != (*lease_it)->fqdn_rev_) ||
+ (lease->hostname_ != (*lease_it)->hostname_))) {
+ LeaseMgrFactory::instance().updateLease6(lease);
+ }
+ updated_leases.push_back(lease);
+ }
+ return (updated_leases);
+}
+
AllocEngine::AllocatorPtr AllocEngine::getAllocator(Lease::Type type) {
std::map<Lease::Type, AllocatorPtr>::const_iterator alloc = allocators_.find(type);
diff --git a/src/lib/dhcpsrv/alloc_engine.h b/src/lib/dhcpsrv/alloc_engine.h
index 8299bb8..ed2a767 100644
--- a/src/lib/dhcpsrv/alloc_engine.h
+++ b/src/lib/dhcpsrv/alloc_engine.h
@@ -1,4 +1,4 @@
-// Copyright (C) 2012-2013 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2012-2014 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
@@ -338,14 +338,21 @@ protected:
/// an address for SOLICIT that is not really allocated (true)
/// @param callout_handle a callout handle (used in hooks). A lease callouts
/// will be executed if this parameter is passed.
+ /// @param [out] old_leases Collection to which this function will append
+ /// old leases. Leases are stored in the same order as in the
+ /// collection of new leases, being returned. For newly allocated
+ /// leases (not renewed) the NULL pointers are stored in this
+ /// collection as old leases.
///
/// @return Allocated IPv6 leases (may be empty if allocation failed)
Lease6Collection
- allocateLeases6(const Subnet6Ptr& subnet, const DuidPtr& duid, uint32_t iaid,
+ allocateLeases6(const Subnet6Ptr& subnet, const DuidPtr& duid,
+ const uint32_t iaid,
const isc::asiolink::IOAddress& hint, Lease::Type type,
const bool fwd_dns_update, const bool rev_dns_update,
const std::string& hostname, bool fake_allocation,
- const isc::hooks::CalloutHandlePtr& callout_handle);
+ const isc::hooks::CalloutHandlePtr& callout_handle,
+ Lease6Collection& old_leases);
/// @brief returns allocator for a given pool type
/// @param type type of pool (V4, IA, TA or PD)
@@ -489,6 +496,28 @@ private:
const isc::hooks::CalloutHandlePtr& callout_handle,
bool fake_allocation = false);
+ /// @brief Updates FQDN data for a collection of leases.
+ ///
+ /// @param leases Collection of leases for which FQDN data should be
+ /// updated.
+ /// @param fwd_dns_update Boolean value which indicates whether forward FQDN
+ /// update was performed for each lease (true) or not (false).
+ /// @param rev_dns_update Boolean value which indicates whether reverse FQDN
+ /// update was performed for each lease (true) or not (false).
+ /// @param hostname Client hostname associated with a lease.
+ /// @param fake_allocation Boolean value which indicates that it is a real
+ /// lease allocation, e.g. Request message is processed (false), or address
+ /// is just being picked as a result of processing Solicit (true). In the
+ /// latter case, the FQDN data should not be updated in the lease database.
+ ///
+ /// @return Collection of leases with updated FQDN data. Note that returned
+ /// collection holds updated FQDN data even for fake allocation.
+ Lease6Collection updateFqdnData(const Lease6Collection& leases,
+ const bool fwd_dns_update,
+ const bool rev_dns_update,
+ const std::string& hostname,
+ const bool fake_allocation);
+
/// @brief a pointer to currently used allocator
///
/// For IPv4, there will be only one allocator: TYPE_V4
diff --git a/src/lib/dhcpsrv/cfgmgr.cc b/src/lib/dhcpsrv/cfgmgr.cc
index 6f008f1..960cfd2 100644
--- a/src/lib/dhcpsrv/cfgmgr.cc
+++ b/src/lib/dhcpsrv/cfgmgr.cc
@@ -1,4 +1,4 @@
-// Copyright (C) 2012-2013 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2012-2014 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,6 +13,7 @@
// PERFORMANCE OF THIS SOFTWARE.
#include <asiolink/io_address.h>
+#include <dhcp/iface_mgr.h>
#include <dhcp/libdhcp++.h>
#include <dhcpsrv/cfgmgr.h>
#include <dhcpsrv/dhcpsrv_log.h>
@@ -74,12 +75,15 @@ CfgMgr::addOptionDef(const OptionDefinitionPtr& def,
isc_throw(DuplicateOptionDefinition, "option definition already added"
<< " to option space " << option_space);
+ // We must not override standard (assigned) option for which there is a
+ // definition in libdhcp++. The standard options belong to dhcp4 or dhcp6
+ // option space.
} else if ((option_space == "dhcp4" &&
- LibDHCP::isStandardOption(Option::V4, def->getCode())) ||
+ LibDHCP::isStandardOption(Option::V4, def->getCode()) &&
+ LibDHCP::getOptionDef(Option::V4, def->getCode())) ||
(option_space == "dhcp6" &&
- LibDHCP::isStandardOption(Option::V6, def->getCode()))) {
- // We must not override standard (assigned) option. The standard options
- // belong to dhcp4 or dhcp6 option space.
+ LibDHCP::isStandardOption(Option::V6, def->getCode()) &&
+ LibDHCP::getOptionDef(Option::V6, def->getCode()))) {
isc_throw(BadValue, "unable to override definition of option '"
<< def->getCode() << "' in standard option space '"
<< option_space << "'.");
@@ -122,7 +126,8 @@ CfgMgr::getOptionDef(const std::string& option_space,
}
Subnet6Ptr
-CfgMgr::getSubnet6(const std::string& iface) {
+CfgMgr::getSubnet6(const std::string& iface,
+ const isc::dhcp::ClientClasses& classes) {
if (!iface.length()) {
return (Subnet6Ptr());
@@ -131,6 +136,12 @@ CfgMgr::getSubnet6(const std::string& iface) {
// If there is more than one, we need to choose the proper one
for (Subnet6Collection::iterator subnet = subnets6_.begin();
subnet != subnets6_.end(); ++subnet) {
+
+ // If client is rejected because of not meeting client class criteria...
+ if (!(*subnet)->clientSupported(classes)) {
+ continue;
+ }
+
if (iface == (*subnet)->getIface()) {
LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE,
DHCPSRV_CFGMGR_SUBNET6_IFACE)
@@ -142,7 +153,8 @@ CfgMgr::getSubnet6(const std::string& iface) {
}
Subnet6Ptr
-CfgMgr::getSubnet6(const isc::asiolink::IOAddress& hint) {
+CfgMgr::getSubnet6(const isc::asiolink::IOAddress& hint,
+ const isc::dhcp::ClientClasses& classes) {
// If there's only one subnet configured, let's just use it
// The idea is to keep small deployments easy. In a small network - one
@@ -152,7 +164,7 @@ CfgMgr::getSubnet6(const isc::asiolink::IOAddress& hint) {
// configuration. Such requirement makes sense in IPv4, but not in IPv6.
// The server does not need to have a global address (using just link-local
// is ok for DHCPv6 server) from the pool it serves.
- if ((subnets6_.size() == 1) && hint.getAddress().to_v6().is_link_local()) {
+ if ((subnets6_.size() == 1) && hint.isV6LinkLocal()) {
LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE,
DHCPSRV_CFGMGR_ONLY_SUBNET6)
.arg(subnets6_[0]->toText()).arg(hint.toText());
@@ -163,6 +175,11 @@ CfgMgr::getSubnet6(const isc::asiolink::IOAddress& hint) {
for (Subnet6Collection::iterator subnet = subnets6_.begin();
subnet != subnets6_.end(); ++subnet) {
+ // If client is rejected because of not meeting client class criteria...
+ if (!(*subnet)->clientSupported(classes)) {
+ continue;
+ }
+
if ((*subnet)->inRange(hint)) {
LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE,
DHCPSRV_CFGMGR_SUBNET6)
@@ -177,7 +194,8 @@ CfgMgr::getSubnet6(const isc::asiolink::IOAddress& hint) {
return (Subnet6Ptr());
}
-Subnet6Ptr CfgMgr::getSubnet6(OptionPtr iface_id_option) {
+Subnet6Ptr CfgMgr::getSubnet6(OptionPtr iface_id_option,
+ const isc::dhcp::ClientClasses& classes) {
if (!iface_id_option) {
return (Subnet6Ptr());
}
@@ -186,6 +204,12 @@ Subnet6Ptr CfgMgr::getSubnet6(OptionPtr iface_id_option) {
// defined, check if the interface-id is equal to what we are looking for
for (Subnet6Collection::iterator subnet = subnets6_.begin();
subnet != subnets6_.end(); ++subnet) {
+
+ // If client is rejected because of not meeting client class criteria...
+ if (!(*subnet)->clientSupported(classes)) {
+ continue;
+ }
+
if ( (*subnet)->getInterfaceId() &&
((*subnet)->getInterfaceId()->equal(iface_id_option))) {
LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE,
@@ -207,26 +231,19 @@ void CfgMgr::addSubnet6(const Subnet6Ptr& subnet) {
}
Subnet4Ptr
-CfgMgr::getSubnet4(const isc::asiolink::IOAddress& hint) {
+CfgMgr::getSubnet4(const isc::asiolink::IOAddress& hint,
+ const isc::dhcp::ClientClasses& classes) const {
+ // Iterate over existing subnets to find a suitable one for the
+ // given address.
+ for (Subnet4Collection::const_iterator subnet = subnets4_.begin();
+ subnet != subnets4_.end(); ++subnet) {
- // If there's only one subnet configured, let's just use it
- // The idea is to keep small deployments easy. In a small network - one
- // router that also runs DHCPv6 server. Users specifies a single pool and
- // expects it to just work. Without this, the server would complain that it
- // doesn't have IP address on its interfaces that matches that
- // configuration. Such requirement makes sense in IPv4, but not in IPv6.
- // The server does not need to have a global address (using just link-local
- // is ok for DHCPv6 server) from the pool it serves.
- if (subnets4_.size() == 1) {
- LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE,
- DHCPSRV_CFGMGR_ONLY_SUBNET4)
- .arg(subnets4_[0]->toText()).arg(hint.toText());
- return (subnets4_[0]);
- }
+ // If client is rejected because of not meeting client class criteria...
+ if (!(*subnet)->clientSupported(classes)) {
+ continue;
+ }
- // If there is more than one, we need to choose the proper one
- for (Subnet4Collection::iterator subnet = subnets4_.begin();
- subnet != subnets4_.end(); ++subnet) {
+ // Let's check if the client belongs to the given subnet
if ((*subnet)->inRange(hint)) {
LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE,
DHCPSRV_CFGMGR_SUBNET4)
@@ -241,6 +258,23 @@ CfgMgr::getSubnet4(const isc::asiolink::IOAddress& hint) {
return (Subnet4Ptr());
}
+Subnet4Ptr
+CfgMgr::getSubnet4(const std::string& iface_name,
+ const isc::dhcp::ClientClasses& classes) const {
+ Iface* iface = IfaceMgr::instance().getIface(iface_name);
+ // This should never happen in the real life. Hence we throw an exception.
+ if (iface == NULL) {
+ isc_throw(isc::BadValue, "interface " << iface_name <<
+ " doesn't exist and therefore it is impossible"
+ " to find a suitable subnet for its IPv4 address");
+ }
+ IOAddress addr("0.0.0.0");
+ // If IPv4 address assigned to the interface exists, find a suitable
+ // subnet for it, else return NULL pointer to indicate that no subnet
+ // could be found.
+ return (iface->getAddress4(addr) ? getSubnet4(addr, classes) : Subnet4Ptr());
+}
+
void CfgMgr::addSubnet4(const Subnet4Ptr& subnet) {
/// @todo: Check that this new subnet does not cross boundaries of any
/// other already defined subnet.
@@ -363,6 +397,10 @@ CfgMgr::getD2ClientConfig() const {
return (d2_client_mgr_.getD2ClientConfig());
}
+D2ClientMgr&
+CfgMgr::getD2ClientMgr() {
+ return (d2_client_mgr_);
+}
CfgMgr::CfgMgr()
: datadir_(DHCP_DATA_DIR),
diff --git a/src/lib/dhcpsrv/cfgmgr.h b/src/lib/dhcpsrv/cfgmgr.h
index fcec8bf..ca854d6 100644
--- a/src/lib/dhcpsrv/cfgmgr.h
+++ b/src/lib/dhcpsrv/cfgmgr.h
@@ -1,4 +1,4 @@
-// Copyright (C) 2012-2013 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2012-2014 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,7 +19,8 @@
#include <dhcp/option.h>
#include <dhcp/option_definition.h>
#include <dhcp/option_space.h>
-#include <dhcpsrv/d2_client.h>
+#include <dhcp/classify.h>
+#include <dhcpsrv/d2_client_mgr.h>
#include <dhcpsrv/option_space_container.h>
#include <dhcpsrv/pool.h>
#include <dhcpsrv/subnet.h>
@@ -165,28 +166,45 @@ public:
/// b) our global address on the interface the message was received on
/// (for directly connected clients)
///
+ /// If there are any classes specified in a subnet, that subnet
+ /// will be selected only if the client belongs to appropriate class.
+ ///
/// @param hint an address that belongs to a searched subnet
+ /// @param classes classes the client belongs to
///
/// @return a subnet object (or NULL if no suitable match was fount)
- Subnet6Ptr getSubnet6(const isc::asiolink::IOAddress& hint);
+ Subnet6Ptr getSubnet6(const isc::asiolink::IOAddress& hint,
+ const isc::dhcp::ClientClasses& classes);
/// @brief get IPv6 subnet by interface name
///
/// Finds a matching local subnet, based on interface name. This
/// is used for selecting subnets that were explicitly marked by the
/// user as reachable over specified network interface.
+ ///
+ /// If there are any classes specified in a subnet, that subnet
+ /// will be selected only if the client belongs to appropriate class.
+ ///
/// @param iface_name interface name
+ /// @param classes classes the client belongs to
+ ///
/// @return a subnet object (or NULL if no suitable match was fount)
- Subnet6Ptr getSubnet6(const std::string& iface_name);
+ Subnet6Ptr getSubnet6(const std::string& iface_name,
+ const isc::dhcp::ClientClasses& classes);
/// @brief get IPv6 subnet by interface-id
///
/// Another possibility to find a subnet is based on interface-id.
///
+ /// If there are any classes specified in a subnet, that subnet
+ /// will be selected only if the client belongs to appropriate class.
+ ///
/// @param interface_id content of interface-id option returned by a relay
+ /// @param classes classes the client belongs to
///
/// @return a subnet object
- Subnet6Ptr getSubnet6(OptionPtr interface_id);
+ Subnet6Ptr getSubnet6(OptionPtr interface_id,
+ const isc::dhcp::ClientClasses& classes);
/// @brief adds an IPv6 subnet
///
@@ -219,7 +237,7 @@ public:
/// to choose a different subnet. Server code has to offer a list
/// of possible choices (i.e. all subnets).
/// @return a pointer to const Subnet6 collection
- const Subnet4Collection* getSubnets4() {
+ const Subnet4Collection* getSubnets4() const {
return (&subnets4_);
}
@@ -241,10 +259,31 @@ public:
/// b) our global address on the interface the message was received on
/// (for directly connected clients)
///
+ /// If there are any classes specified in a subnet, that subnet
+ /// will be selected only if the client belongs to appropriate class.
+ ///
/// @param hint an address that belongs to a searched subnet
+ /// @param classes classes the client belongs to
///
/// @return a subnet object
- Subnet4Ptr getSubnet4(const isc::asiolink::IOAddress& hint);
+ Subnet4Ptr getSubnet4(const isc::asiolink::IOAddress& hint,
+ const isc::dhcp::ClientClasses& classes) const;
+
+ /// @brief Returns a subnet for the specified local interface.
+ ///
+ /// This function checks that the IP address assigned to the specified
+ /// interface belongs to any IPv4 subnet configured, and returns this
+ /// subnet.
+ ///
+ /// @todo Implement classes support here.
+ ///
+ /// @param iface Short name of the interface which is being checked.
+ /// @param classes classes the client belongs to
+ ///
+ /// @return Pointer to the subnet matching interface specified or NULL
+ /// pointer if IPv4 address on the interface doesn't match any subnet.
+ Subnet4Ptr getSubnet4(const std::string& iface,
+ const isc::dhcp::ClientClasses& classes) const;
/// @brief adds a subnet4
void addSubnet4(const Subnet4Ptr& subnet);
@@ -341,7 +380,7 @@ public:
/// pointer.
void setD2ClientConfig(D2ClientConfigPtr& new_config);
- /// @param Convenience method for checking if DHCP-DDNS updates are enabled.
+ /// @brief Convenience method for checking if DHCP-DDNS updates are enabled.
///
/// @return True if the D2 configuration is enabled.
bool ddnsEnabled();
@@ -351,6 +390,11 @@ public:
/// @return a reference to the current configuration pointer.
const D2ClientConfigPtr& getD2ClientConfig() const;
+ /// @brief Fetches the DHCP-DDNS manager.
+ ///
+ /// @return a reference to the DHCP-DDNS manager.
+ D2ClientMgr& getD2ClientMgr();
+
protected:
/// @brief Protected constructor.
diff --git a/src/lib/dhcpsrv/d2_client.cc b/src/lib/dhcpsrv/d2_client.cc
deleted file mode 100644
index d1b06ae..0000000
--- a/src/lib/dhcpsrv/d2_client.cc
+++ /dev/null
@@ -1,185 +0,0 @@
-// Copyright (C) 2013-2014 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 <dhcpsrv/d2_client.h>
-#include <dhcpsrv/dhcpsrv_log.h>
-
-#include <string>
-
-using namespace std;
-
-namespace isc {
-namespace dhcp {
-
-D2ClientConfig::D2ClientConfig(const bool enable_updates,
- const isc::asiolink::IOAddress& server_ip,
- const size_t server_port,
- const dhcp_ddns::
- NameChangeProtocol& ncr_protocol,
- const dhcp_ddns::
- NameChangeFormat& ncr_format,
- const bool remove_on_renew,
- const bool always_include_fqdn,
- const bool override_no_update,
- const bool override_client_update,
- const bool replace_client_name,
- const std::string& generated_prefix,
- const std::string& qualifying_suffix)
- : enable_updates_(enable_updates),
- server_ip_(server_ip.getAddress()),
- server_port_(server_port),
- ncr_protocol_(ncr_protocol),
- ncr_format_(ncr_format),
- remove_on_renew_(remove_on_renew),
- always_include_fqdn_(always_include_fqdn),
- override_no_update_(override_no_update),
- override_client_update_(override_client_update),
- replace_client_name_(replace_client_name),
- generated_prefix_(generated_prefix),
- qualifying_suffix_(qualifying_suffix) {
- validateContents();
-}
-
-D2ClientConfig::D2ClientConfig()
- : enable_updates_(false),
- server_ip_(isc::asiolink::IOAddress("0.0.0.0")),
- server_port_(0),
- ncr_protocol_(dhcp_ddns::NCR_UDP),
- ncr_format_(dhcp_ddns::FMT_JSON),
- remove_on_renew_(false),
- always_include_fqdn_(false),
- override_no_update_(false),
- override_client_update_(false),
- replace_client_name_(false),
- generated_prefix_(""),
- qualifying_suffix_("") {
- validateContents();
-}
-
-D2ClientConfig::~D2ClientConfig(){};
-
-void
-D2ClientConfig::validateContents() {
- if (ncr_format_ != dhcp_ddns::FMT_JSON) {
- isc_throw(D2ClientError, "D2ClientConfig: NCR Format:"
- << dhcp_ddns::ncrFormatToString(ncr_format_)
- << " is not yet supported");
- }
-
- if (ncr_protocol_ != dhcp_ddns::NCR_UDP) {
- isc_throw(D2ClientError, "D2ClientConfig: NCR Protocol:"
- << dhcp_ddns::ncrProtocolToString(ncr_protocol_)
- << " is not yet supported");
- }
-
- // @todo perhaps more validation we should do yet?
- // Are there any invalid combinations of options we need to test against?
- // Also do we care about validating contents if it's disabled?
-}
-
-bool
-D2ClientConfig::operator == (const D2ClientConfig& other) const {
- return ((enable_updates_ == other.enable_updates_) &&
- (server_ip_ == other.server_ip_) &&
- (server_port_ == other.server_port_) &&
- (ncr_protocol_ == other.ncr_protocol_) &&
- (ncr_format_ == other.ncr_format_) &&
- (remove_on_renew_ == other.remove_on_renew_) &&
- (always_include_fqdn_ == other.always_include_fqdn_) &&
- (override_no_update_ == other.override_no_update_) &&
- (override_client_update_ == other.override_client_update_) &&
- (replace_client_name_ == other.replace_client_name_) &&
- (generated_prefix_ == other.generated_prefix_) &&
- (qualifying_suffix_ == other.qualifying_suffix_));
-}
-
-bool
-D2ClientConfig::operator != (const D2ClientConfig& other) const {
- return (!(*this == other));
-}
-
-std::string
-D2ClientConfig::toText() const {
- std::ostringstream stream;
-
- stream << "enable_updates: " << (enable_updates_ ? "yes" : "no");
- if (enable_updates_) {
- stream << ", server_ip: " << server_ip_.toText()
- << ", server_port: " << server_port_
- << ", ncr_protocol: " << ncr_protocol_
- << ", ncr_format: " << ncr_format_
- << ", remove_on_renew: " << (remove_on_renew_ ? "yes" : "no")
- << ", always_include_fqdn: " << (always_include_fqdn_ ?
- "yes" : "no")
- << ", override_no_update: " << (override_no_update_ ?
- "yes" : "no")
- << ", override_client_update: " << (override_client_update_ ?
- "yes" : "no")
- << ", replace_client_name: " << (replace_client_name_ ?
- "yes" : "no")
- << ", generated_prefix: [" << generated_prefix_ << "]"
- << ", qualifying_suffix: [" << qualifying_suffix_ << "]";
- }
-
- return (stream.str());
-}
-
-std::ostream&
-operator<<(std::ostream& os, const D2ClientConfig& config) {
- os << config.toText();
- return (os);
-}
-
-D2ClientMgr::D2ClientMgr() : d2_client_config_(new D2ClientConfig()) {
- // Default contstructor initializes with a disabled config.
-}
-
-D2ClientMgr::~D2ClientMgr(){
-}
-
-void
-D2ClientMgr::setD2ClientConfig(D2ClientConfigPtr& new_config) {
- if (!new_config) {
- isc_throw(D2ClientError,
- "D2ClientMgr cannot set DHCP-DDNS configuration to NULL.");
- }
-
- // @todo When NameChangeSender is integrated, we will need to handle these
- // scenarios:
- // 1. D2 was enabled but now it is disabled
- // - destroy the sender, flush any queued
- // 2. D2 is still enabled but server params have changed
- // - preserve any queued, reconnect based on sender params
- // 3. D2 was was disabled now it is enabled.
- // - create sender
- //
- // For now we just update the configuration.
- d2_client_config_ = new_config;
- LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE, DHCPSRV_CFGMGR_CFG_DHCP_DDNS)
- .arg(!ddnsEnabled() ? "DHCP-DDNS updates disabled" :
- "DHCP_DDNS updates enabled");
-}
-
-bool
-D2ClientMgr::ddnsEnabled() {
- return (d2_client_config_->getEnableUpdates());
-}
-
-const D2ClientConfigPtr&
-D2ClientMgr::getD2ClientConfig() const {
- return (d2_client_config_);
-}
-
-}; // namespace dhcp
-}; // namespace isc
diff --git a/src/lib/dhcpsrv/d2_client.h b/src/lib/dhcpsrv/d2_client.h
deleted file mode 100644
index 2f84515..0000000
--- a/src/lib/dhcpsrv/d2_client.h
+++ /dev/null
@@ -1,278 +0,0 @@
-// Copyright (C) 2013-2014 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 D2_CLIENT_H
-#define D2_CLIENT_H
-
-/// @file d2_client.h Defines the D2ClientConfig and D2ClientMgr classes.
-/// This file defines the classes Kea uses to act as a client of the b10-
-/// dhcp-ddns module (aka D2).
-///
-#include <asiolink/io_address.h>
-#include <dhcp_ddns/ncr_io.h>
-#include <exceptions/exceptions.h>
-
-#include <boost/shared_ptr.hpp>
-
-#include <stdint.h>
-#include <string>
-#include <vector>
-
-namespace isc {
-namespace dhcp {
-
-
-/// An exception that is thrown if an error occurs while configuring
-/// the D2 DHCP DDNS client.
-class D2ClientError : public isc::Exception {
-public:
-
- /// @brief constructor
- ///
- /// @param file name of the file, where exception occurred
- /// @param line line of the file, where exception occurred
- /// @param what text description of the issue that caused exception
- D2ClientError(const char* file, size_t line, const char* what)
- : isc::Exception(file, line, what) {}
-};
-
-/// @brief Acts as a storage vault for D2 client configuration
-///
-/// A simple container class for storing and retrieving the configuration
-/// parameters associated with DHCP-DDNS and acting as a client of D2.
-/// Instances of this class may be constructed through configuration parsing.
-///
-class D2ClientConfig {
-public:
- /// @brief Constructor
- ///
- /// @param enable_updates Enables DHCP-DDNS updates
- /// @param server_ip IP address of the b10-dhcp-ddns server (IPv4 or IPv6)
- /// @param server_port IP port of the b10-dhcp-ddns server
- /// @param ncr_protocol Socket protocol to use with b10-dhcp-ddns
- /// Currently only UDP is supported.
- /// @param ncr_format Format of the b10-dhcp-ddns requests.
- /// Currently only JSON format is supported.
- /// @param remove_on_renew Enables DNS Removes when renewing a lease
- /// If true, Kea should request an explicit DNS remove prior to requesting
- /// a DNS update when renewing a lease.
- /// (Note: b10-dhcp-ddns is implemented per RFC 4703 and such a remove
- /// is unnecessary).
- /// @param always_include_fqdn Enables always including the FQDN option in
- /// DHCP responses.
- /// @param override_no_update Enables updates, even if clients request no
- /// updates.
- /// @param override_client_update Perform updates, even if client requested
- /// delegation.
- /// @param replace_client_name enables replacement of the domain-name
- /// supplied by the client with a generated name.
- /// @param generated_prefix Prefix to use when generating domain-names.
- /// @param qualifying_suffix Suffix to use to qualify partial domain-names.
- ///
- /// @throw D2ClientError if given an invalid protocol or format.
- D2ClientConfig(const bool enable_updates,
- const isc::asiolink::IOAddress& server_ip,
- const size_t server_port,
- const dhcp_ddns::NameChangeProtocol& ncr_protocol,
- const dhcp_ddns::NameChangeFormat& ncr_format,
- const bool remove_on_renew,
- const bool always_include_fqdn,
- const bool override_no_update,
- const bool override_client_update,
- const bool replace_client_name,
- const std::string& generated_prefix,
- const std::string& qualifying_suffix);
-
- /// @brief Default constructor
- /// The default constructor creates an instance that has updates disabled.
- D2ClientConfig();
-
- /// @brief Destructor
- virtual ~D2ClientConfig();
-
- /// @brief Return whether or not DHCP-DDNS updating is enabled.
- bool getEnableUpdates() const {
- return(enable_updates_);
- }
-
- /// @brief Return the IP address of b10-dhcp-ddns (IPv4 or IPv6).
- const isc::asiolink::IOAddress& getServerIp() const {
- return(server_ip_);
- }
-
- /// @brief Return the IP port of b10-dhcp-ddns.
- size_t getServerPort() const {
- return(server_port_);
- }
-
- /// @brief Return the socket protocol to use with b10-dhcp-ddns.
- const dhcp_ddns::NameChangeProtocol& getNcrProtocol() const {
- return(ncr_protocol_);
- }
-
- /// @brief Return the b10-dhcp-ddns request format.
- const dhcp_ddns::NameChangeFormat& getNcrFormat() const {
- return(ncr_format_);
- }
-
- /// @brief Return whether or not removes should be sent for lease renewals.
- bool getRemoveOnRenew() const {
- return(remove_on_renew_);
- }
-
- /// @brief Return whether or not FQDN is always included in DHCP responses.
- bool getAlwaysIncludeFqdn() const {
- return(always_include_fqdn_);
- }
-
- /// @brief Return if updates are done even if clients request no updates.
- bool getOverrideNoUpdate() const {
- return(override_no_update_);
- }
-
- /// @brief Return if updates are done even when clients request delegation.
- bool getOverrideClientUpdate() const {
- return(override_client_update_);
- }
-
- /// @brief Return whether or not client's domain-name is always replaced.
- bool getReplaceClientName() const {
- return(replace_client_name_);
- }
-
- /// @brief Return the prefix to use when generating domain-names.
- const std::string& getGeneratedPrefix() const {
- return(generated_prefix_);
- }
-
- /// @brief Return the suffix to use to qualify partial domain-names.
- const std::string& getQualifyingSuffix() const {
- return(qualifying_suffix_);
- }
-
- /// @brief Compares two D2ClientConfigs for equality
- bool operator == (const D2ClientConfig& other) const;
-
- /// @brief Compares two D2ClientConfigs for inequality
- bool operator != (const D2ClientConfig& other) const;
-
- /// @brief Generates a string representation of the class contents.
- std::string toText() const;
-
-protected:
- /// @brief Validates member values.
- ///
- /// Method is used by the constructor to validate member contents.
- ///
- /// @throw D2ClientError if given an invalid protocol or format.
- virtual void validateContents();
-
-private:
- /// @brief Indicates whether or not DHCP DDNS updating is enabled.
- bool enable_updates_;
-
- /// @brief IP address of the b10-dhcp-ddns server (IPv4 or IPv6).
- isc::asiolink::IOAddress server_ip_;
-
- /// @brief IP port of the b10-dhcp-ddns server.
- size_t server_port_;
-
- /// @brief The socket protocol to use with b10-dhcp-ddns.
- /// Currently only UPD is supported.
- dhcp_ddns::NameChangeProtocol ncr_protocol_;
-
- /// @brief Format of the b10-dhcp-ddns requests.
- /// Currently only JSON format is supported.
- dhcp_ddns::NameChangeFormat ncr_format_;
-
- /// @brief Should Kea request a DNS Remove when renewing a lease.
- /// If true, Kea should request an explicit DNS remove prior to requesting
- /// a DNS update when renewing a lease.
- /// (Note: b10-dhcp-ddns is implemented per RFC 4703 and such a remove
- /// is unnecessary).
- bool remove_on_renew_;
-
- /// @brief Should Kea always include the FQDN option in its response.
- bool always_include_fqdn_;
-
- /// @brief Should Kea perform updates, even if client requested no updates.
- /// Overrides the client request for no updates via the N flag.
- bool override_no_update_;
-
- /// @brief Should Kea perform updates, even if client requested delegation.
- bool override_client_update_;
-
- /// @brief Should Kea replace the domain-name supplied by the client.
- bool replace_client_name_;
-
- /// @brief Prefix Kea should use when generating domain-names.
- std::string generated_prefix_;
-
- /// @brief Suffix Kea should use when to qualify partial domain-names.
- std::string qualifying_suffix_;
-};
-
-std::ostream&
-operator<<(std::ostream& os, const D2ClientConfig& config);
-
-/// @brief Defines a pointer for D2ClientConfig instances.
-typedef boost::shared_ptr<D2ClientConfig> D2ClientConfigPtr;
-
-/// @brief D2ClientMgr isolates Kea from the details of being a D2 client.
-///
-/// Provides services for managing the current D2ClientConfig and managing
-/// communications with D2. (@todo The latter will be added once communication
-/// with D2 is implemented through the integration of
-/// dhcp_ddns::NameChangeSender interface(s)).
-///
-class D2ClientMgr {
-public:
- /// @brief Constructor
- ///
- /// Default constructor which constructs an instance which has DHCP-DDNS
- /// updates disabled.
- D2ClientMgr();
-
- /// @brief Destructor.
- ~D2ClientMgr();
-
- /// @brief Updates the DHCP-DDNS client configuration to the given value.
- ///
- /// @param new_config pointer to the new client configuration.
- /// @throw D2ClientError if passed an empty pointer.
- void setD2ClientConfig(D2ClientConfigPtr& new_config);
-
- /// @brief Convenience method for checking if DHCP-DDNS is enabled.
- ///
- /// @return True if the D2 configuration is enabled.
- bool ddnsEnabled();
-
- /// @brief Fetches the DHCP-DDNS configuration pointer.
- ///
- /// @return a reference to the current configuration pointer.
- const D2ClientConfigPtr& getD2ClientConfig() const;
-
-private:
- /// @brief Container class for DHCP-DDNS configuration parameters.
- D2ClientConfigPtr d2_client_config_;
-};
-
-/// @brief Defines a pointer for D2ClientMgr instances.
-typedef boost::shared_ptr<D2ClientMgr> D2ClientMgrPtr;
-
-
-} // namespace isc
-} // namespace dhcp
-
-#endif
diff --git a/src/lib/dhcpsrv/d2_client_cfg.cc b/src/lib/dhcpsrv/d2_client_cfg.cc
new file mode 100644
index 0000000..9ad0d2f
--- /dev/null
+++ b/src/lib/dhcpsrv/d2_client_cfg.cc
@@ -0,0 +1,146 @@
+// Copyright (C) 2013-2014 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 <dhcp_ddns/ncr_udp.h>
+#include <dhcpsrv/d2_client_cfg.h>
+#include <dhcpsrv/dhcpsrv_log.h>
+
+#include <string>
+
+using namespace std;
+
+namespace isc {
+namespace dhcp {
+
+D2ClientConfig::D2ClientConfig(const bool enable_updates,
+ const isc::asiolink::IOAddress& server_ip,
+ const size_t server_port,
+ const dhcp_ddns::
+ NameChangeProtocol& ncr_protocol,
+ const dhcp_ddns::
+ NameChangeFormat& ncr_format,
+ const bool always_include_fqdn,
+ const bool override_no_update,
+ const bool override_client_update,
+ const bool replace_client_name,
+ const std::string& generated_prefix,
+ const std::string& qualifying_suffix)
+ : enable_updates_(enable_updates),
+ server_ip_(server_ip),
+ server_port_(server_port),
+ ncr_protocol_(ncr_protocol),
+ ncr_format_(ncr_format),
+ always_include_fqdn_(always_include_fqdn),
+ override_no_update_(override_no_update),
+ override_client_update_(override_client_update),
+ replace_client_name_(replace_client_name),
+ generated_prefix_(generated_prefix),
+ qualifying_suffix_(qualifying_suffix) {
+ validateContents();
+}
+
+D2ClientConfig::D2ClientConfig()
+ : enable_updates_(false),
+ server_ip_(isc::asiolink::IOAddress("0.0.0.0")),
+ server_port_(0),
+ ncr_protocol_(dhcp_ddns::NCR_UDP),
+ ncr_format_(dhcp_ddns::FMT_JSON),
+ always_include_fqdn_(false),
+ override_no_update_(false),
+ override_client_update_(false),
+ replace_client_name_(false),
+ generated_prefix_("myhost"),
+ qualifying_suffix_("example.com") {
+ validateContents();
+}
+
+D2ClientConfig::~D2ClientConfig(){};
+
+void
+D2ClientConfig::enableUpdates(bool enable) {
+ enable_updates_ = enable;
+}
+
+void
+D2ClientConfig::validateContents() {
+ if (ncr_format_ != dhcp_ddns::FMT_JSON) {
+ isc_throw(D2ClientError, "D2ClientConfig: NCR Format:"
+ << dhcp_ddns::ncrFormatToString(ncr_format_)
+ << " is not yet supported");
+ }
+
+ if (ncr_protocol_ != dhcp_ddns::NCR_UDP) {
+ isc_throw(D2ClientError, "D2ClientConfig: NCR Protocol:"
+ << dhcp_ddns::ncrProtocolToString(ncr_protocol_)
+ << " is not yet supported");
+ }
+
+ /// @todo perhaps more validation we should do yet?
+ /// Are there any invalid combinations of options we need to test against?
+}
+
+bool
+D2ClientConfig::operator == (const D2ClientConfig& other) const {
+ return ((enable_updates_ == other.enable_updates_) &&
+ (server_ip_ == other.server_ip_) &&
+ (server_port_ == other.server_port_) &&
+ (ncr_protocol_ == other.ncr_protocol_) &&
+ (ncr_format_ == other.ncr_format_) &&
+ (always_include_fqdn_ == other.always_include_fqdn_) &&
+ (override_no_update_ == other.override_no_update_) &&
+ (override_client_update_ == other.override_client_update_) &&
+ (replace_client_name_ == other.replace_client_name_) &&
+ (generated_prefix_ == other.generated_prefix_) &&
+ (qualifying_suffix_ == other.qualifying_suffix_));
+}
+
+bool
+D2ClientConfig::operator != (const D2ClientConfig& other) const {
+ return (!(*this == other));
+}
+
+std::string
+D2ClientConfig::toText() const {
+ std::ostringstream stream;
+
+ stream << "enable_updates: " << (enable_updates_ ? "yes" : "no");
+ if (enable_updates_) {
+ stream << ", server_ip: " << server_ip_.toText()
+ << ", server_port: " << server_port_
+ << ", ncr_protocol: " << ncr_protocol_
+ << ", ncr_format: " << ncr_format_
+ << ", always_include_fqdn: " << (always_include_fqdn_ ?
+ "yes" : "no")
+ << ", override_no_update: " << (override_no_update_ ?
+ "yes" : "no")
+ << ", override_client_update: " << (override_client_update_ ?
+ "yes" : "no")
+ << ", replace_client_name: " << (replace_client_name_ ?
+ "yes" : "no")
+ << ", generated_prefix: [" << generated_prefix_ << "]"
+ << ", qualifying_suffix: [" << qualifying_suffix_ << "]";
+ }
+
+ return (stream.str());
+}
+
+std::ostream&
+operator<<(std::ostream& os, const D2ClientConfig& config) {
+ os << config.toText();
+ return (os);
+}
+
+}; // namespace dhcp
+
+}; // namespace isc
diff --git a/src/lib/dhcpsrv/d2_client_cfg.h b/src/lib/dhcpsrv/d2_client_cfg.h
new file mode 100644
index 0000000..37b682d
--- /dev/null
+++ b/src/lib/dhcpsrv/d2_client_cfg.h
@@ -0,0 +1,226 @@
+// Copyright (C) 2013-2014 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 D2_CLIENT_CFG_H
+#define D2_CLIENT_CFG_H
+
+/// @file d2_client_cfg.h Defines the D2ClientConfig class.
+/// This file defines the classes Kea uses to manage configuration needed to
+/// act as a client of the b10-dhcp-ddns module (aka D2).
+///
+#include <asiolink/io_address.h>
+#include <dhcp_ddns/ncr_io.h>
+#include <exceptions/exceptions.h>
+
+#include <boost/shared_ptr.hpp>
+
+#include <stdint.h>
+#include <string>
+#include <vector>
+
+namespace isc {
+namespace dhcp {
+
+
+/// An exception that is thrown if an error occurs while configuring
+/// the D2 DHCP DDNS client.
+class D2ClientError : public isc::Exception {
+public:
+
+ /// @brief constructor
+ ///
+ /// @param file name of the file, where exception occurred
+ /// @param line line of the file, where exception occurred
+ /// @param what text description of the issue that caused exception
+ D2ClientError(const char* file, size_t line, const char* what)
+ : isc::Exception(file, line, what) {}
+};
+
+/// @brief Acts as a storage vault for D2 client configuration
+///
+/// A simple container class for storing and retrieving the configuration
+/// parameters associated with DHCP-DDNS and acting as a client of D2.
+/// Instances of this class may be constructed through configuration parsing.
+///
+class D2ClientConfig {
+public:
+ /// @brief Constructor
+ ///
+ /// @param enable_updates Enables DHCP-DDNS updates
+ /// @param server_ip IP address of the b10-dhcp-ddns server (IPv4 or IPv6)
+ /// @param server_port IP port of the b10-dhcp-ddns server
+ /// @param ncr_protocol Socket protocol to use with b10-dhcp-ddns
+ /// Currently only UDP is supported.
+ /// @param ncr_format Format of the b10-dhcp-ddns requests.
+ /// Currently only JSON format is supported.
+ /// @param always_include_fqdn Enables always including the FQDN option in
+ /// DHCP responses.
+ /// @param override_no_update Enables updates, even if clients request no
+ /// updates.
+ /// @param override_client_update Perform updates, even if client requested
+ /// delegation.
+ /// @param replace_client_name enables replacement of the domain-name
+ /// supplied by the client with a generated name.
+ /// @param generated_prefix Prefix to use when generating domain-names.
+ /// @param qualifying_suffix Suffix to use to qualify partial domain-names.
+ ///
+ /// @throw D2ClientError if given an invalid protocol or format.
+ D2ClientConfig(const bool enable_updates,
+ const isc::asiolink::IOAddress& server_ip,
+ const size_t server_port,
+ const dhcp_ddns::NameChangeProtocol& ncr_protocol,
+ const dhcp_ddns::NameChangeFormat& ncr_format,
+ const bool always_include_fqdn,
+ const bool override_no_update,
+ const bool override_client_update,
+ const bool replace_client_name,
+ const std::string& generated_prefix,
+ const std::string& qualifying_suffix);
+
+ /// @brief Default constructor
+ /// The default constructor creates an instance that has updates disabled.
+ D2ClientConfig();
+
+ /// @brief Destructor
+ virtual ~D2ClientConfig();
+
+ /// @brief Return whether or not DHCP-DDNS updating is enabled.
+ bool getEnableUpdates() const {
+ return(enable_updates_);
+ }
+
+ /// @brief Return the IP address of b10-dhcp-ddns (IPv4 or IPv6).
+ const isc::asiolink::IOAddress& getServerIp() const {
+ return(server_ip_);
+ }
+
+ /// @brief Return the IP port of b10-dhcp-ddns.
+ size_t getServerPort() const {
+ return(server_port_);
+ }
+
+ /// @brief Return the socket protocol to use with b10-dhcp-ddns.
+ const dhcp_ddns::NameChangeProtocol& getNcrProtocol() const {
+ return(ncr_protocol_);
+ }
+
+ /// @brief Return the b10-dhcp-ddns request format.
+ const dhcp_ddns::NameChangeFormat& getNcrFormat() const {
+ return(ncr_format_);
+ }
+
+ /// @brief Return whether or not FQDN is always included in DHCP responses.
+ bool getAlwaysIncludeFqdn() const {
+ return(always_include_fqdn_);
+ }
+
+ /// @brief Return if updates are done even if clients request no updates.
+ bool getOverrideNoUpdate() const {
+ return(override_no_update_);
+ }
+
+ /// @brief Return if updates are done even when clients request delegation.
+ bool getOverrideClientUpdate() const {
+ return(override_client_update_);
+ }
+
+ /// @brief Return whether or not client's domain-name is always replaced.
+ bool getReplaceClientName() const {
+ return(replace_client_name_);
+ }
+
+ /// @brief Return the prefix to use when generating domain-names.
+ const std::string& getGeneratedPrefix() const {
+ return(generated_prefix_);
+ }
+
+ /// @brief Return the suffix to use to qualify partial domain-names.
+ const std::string& getQualifyingSuffix() const {
+ return(qualifying_suffix_);
+ }
+
+ /// @brief Compares two D2ClientConfigs for equality
+ bool operator == (const D2ClientConfig& other) const;
+
+ /// @brief Compares two D2ClientConfigs for inequality
+ bool operator != (const D2ClientConfig& other) const;
+
+ /// @brief Generates a string representation of the class contents.
+ std::string toText() const;
+
+ /// @brief Sets enable-updates flag to the given value.
+ ///
+ /// This is the only value that may be altered outside the constructor
+ /// as it may be desirable to toggle it off and on when dealing with
+ /// D2 IO errors.
+ ///
+ /// @param enable boolean value to assign to the enable-updates flag
+ void enableUpdates(bool enable);
+
+protected:
+ /// @brief Validates member values.
+ ///
+ /// Method is used by the constructor to validate member contents.
+ ///
+ /// @throw D2ClientError if given an invalid protocol or format.
+ virtual void validateContents();
+
+private:
+ /// @brief Indicates whether or not DHCP DDNS updating is enabled.
+ bool enable_updates_;
+
+ /// @brief IP address of the b10-dhcp-ddns server (IPv4 or IPv6).
+ isc::asiolink::IOAddress server_ip_;
+
+ /// @brief IP port of the b10-dhcp-ddns server.
+ size_t server_port_;
+
+ /// @brief The socket protocol to use with b10-dhcp-ddns.
+ /// Currently only UDP is supported.
+ dhcp_ddns::NameChangeProtocol ncr_protocol_;
+
+ /// @brief Format of the b10-dhcp-ddns requests.
+ /// Currently only JSON format is supported.
+ dhcp_ddns::NameChangeFormat ncr_format_;
+
+ /// @brief Should Kea always include the FQDN option in its response.
+ bool always_include_fqdn_;
+
+ /// @brief Should Kea perform updates, even if client requested no updates.
+ /// Overrides the client request for no updates via the N flag.
+ bool override_no_update_;
+
+ /// @brief Should Kea perform updates, even if client requested delegation.
+ bool override_client_update_;
+
+ /// @brief Should Kea replace the domain-name supplied by the client.
+ bool replace_client_name_;
+
+ /// @brief Prefix Kea should use when generating domain-names.
+ std::string generated_prefix_;
+
+ /// @brief Suffix Kea should use when to qualify partial domain-names.
+ std::string qualifying_suffix_;
+};
+
+std::ostream&
+operator<<(std::ostream& os, const D2ClientConfig& config);
+
+/// @brief Defines a pointer for D2ClientConfig instances.
+typedef boost::shared_ptr<D2ClientConfig> D2ClientConfigPtr;
+
+} // namespace isc
+} // namespace dhcp
+
+#endif
diff --git a/src/lib/dhcpsrv/d2_client_mgr.cc b/src/lib/dhcpsrv/d2_client_mgr.cc
new file mode 100644
index 0000000..c806bc4
--- /dev/null
+++ b/src/lib/dhcpsrv/d2_client_mgr.cc
@@ -0,0 +1,379 @@
+// Copyright (C) 2014 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 <dhcp/iface_mgr.h>
+#include <dhcp_ddns/ncr_udp.h>
+#include <dhcpsrv/d2_client_mgr.h>
+#include <dhcpsrv/dhcpsrv_log.h>
+
+#include <boost/bind.hpp>
+
+#include <string>
+
+using namespace std;
+
+namespace isc {
+namespace dhcp {
+
+D2ClientMgr::D2ClientMgr() : d2_client_config_(new D2ClientConfig()),
+ name_change_sender_(), private_io_service_(),
+ registered_select_fd_(dhcp_ddns::WatchSocket::INVALID_SOCKET) {
+ // Default constructor initializes with a disabled configuration.
+}
+
+D2ClientMgr::~D2ClientMgr(){
+ stopSender();
+}
+
+void
+D2ClientMgr::suspendUpdates() {
+ if (ddnsEnabled()) {
+ /// @todo For now we will disable updates and stop sending.
+ /// This at least provides a means to shut it off if there are errors.
+ LOG_WARN(dhcpsrv_logger, DHCPSRV_DHCP_DDNS_SUSPEND_UPDATES);
+ d2_client_config_->enableUpdates(false);
+ if (name_change_sender_) {
+ stopSender();
+ }
+ }
+}
+
+void
+D2ClientMgr::setD2ClientConfig(D2ClientConfigPtr& new_config) {
+ if (!new_config) {
+ isc_throw(D2ClientError,
+ "D2ClientMgr cannot set DHCP-DDNS configuration to NULL.");
+ }
+
+ // Don't do anything unless configuration values are actually different.
+ if (*d2_client_config_ != *new_config) {
+ // Make sure we stop sending first.
+ stopSender();
+ if (!new_config->getEnableUpdates()) {
+ // Updating has been turned off.
+ // Destroy current sender (any queued requests are tossed).
+ name_change_sender_.reset();
+ } else {
+ dhcp_ddns::NameChangeSenderPtr new_sender;
+ switch (new_config->getNcrProtocol()) {
+ case dhcp_ddns::NCR_UDP: {
+ /// @todo Should we be able to configure a sender's client
+ /// side ip and port? We should certainly be able to
+ /// configure a maximum queue size. These were overlooked
+ /// but are covered in Trac# 3328.
+ isc::asiolink::IOAddress any_addr("0.0.0.0");
+ uint32_t any_port = 0;
+ uint32_t queue_max = 1024;
+
+ // Instantiate a new sender.
+ new_sender.reset(new dhcp_ddns::NameChangeUDPSender(
+ any_addr, any_port,
+ new_config->getServerIp(),
+ new_config->getServerPort(),
+ new_config->getNcrFormat(),
+ *this, queue_max));
+ break;
+ }
+ default:
+ // In theory you can't get here.
+ isc_throw(D2ClientError, "Invalid sender Protocol: "
+ << new_config->getNcrProtocol());
+ break;
+ }
+
+ // Transfer queued requests from previous sender to the new one.
+ /// @todo - Should we consider anything queued to be wrong?
+ /// If only server values changed content might still be right but
+ /// if content values changed (e.g. suffix or an override flag)
+ /// then the queued contents might now be invalid. There is
+ /// no way to regenerate them if they are wrong.
+ if (name_change_sender_) {
+ new_sender->assumeQueue(*name_change_sender_);
+ }
+
+ // Replace the old sender with the new one.
+ name_change_sender_ = new_sender;
+ }
+ }
+
+ // Update the configuration.
+ d2_client_config_ = new_config;
+ LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE, DHCPSRV_CFGMGR_CFG_DHCP_DDNS)
+ .arg(!ddnsEnabled() ? "DHCP-DDNS updates disabled" :
+ "DHCP_DDNS updates enabled");
+}
+
+bool
+D2ClientMgr::ddnsEnabled() {
+ return (d2_client_config_->getEnableUpdates());
+}
+
+const D2ClientConfigPtr&
+D2ClientMgr::getD2ClientConfig() const {
+ return (d2_client_config_);
+}
+
+void
+D2ClientMgr::analyzeFqdn(const bool client_s, const bool client_n,
+ bool& server_s, bool& server_n) const {
+ // Per RFC 4702 & 4704, the client N and S flags allow the client to
+ // request one of three options:
+ //
+ // N flag S flag Option
+ // ------------------------------------------------------------------
+ // 0 0 client wants to do forward updates (section 3.2)
+ // 0 1 client wants server to do forward updates (section 3.3)
+ // 1 0 client wants no one to do updates (section 3.4)
+ // 1 1 invalid combination
+ // (Note section numbers cited are for 4702, for 4704 see 5.1, 5.2, and 5.3)
+ //
+ // Make a bit mask from the client's flags and use it to set the response
+ // flags accordingly.
+ const uint8_t mask = ((client_n ? 2 : 0) + (client_s ? 1 : 0));
+
+ switch (mask) {
+ case 0:
+ // If updates are enabled and we are overriding client delegation
+ // then S flag should be true.
+ server_s = (d2_client_config_->getEnableUpdates() &&
+ d2_client_config_->getOverrideClientUpdate());
+ break;
+
+ case 1:
+ server_s = d2_client_config_->getEnableUpdates();
+ break;
+
+ case 2:
+ // If updates are enabled and we are overriding "no updates" then
+ // S flag should be true.
+ server_s = (d2_client_config_->getEnableUpdates() &&
+ d2_client_config_->getOverrideNoUpdate());
+ break;
+
+ default:
+ // RFCs declare this an invalid combination.
+ isc_throw(isc::BadValue,
+ "Invalid client FQDN - N and S cannot both be 1");
+ break;
+ }
+
+ /// @todo Currently we are operating under the premise that N should be 1
+ /// if the server is not doing updates nor do we have configuration
+ /// controls to govern forward and reverse updates independently.
+ /// In addition, the client FQDN flags cannot explicitly suggest what to
+ /// do with reverse updates. They request either forward updates or no
+ /// updates. In other words, the client cannot request the server do or
+ /// not do reverse updates. For now, we are either going to do updates in
+ /// both directions or none at all. If and when additional configuration
+ /// parameters are added this logic will have to be reassessed.
+ server_n = !server_s;
+}
+
+std::string
+D2ClientMgr::generateFqdn(const asiolink::IOAddress& address) const {
+ std::string hostname = address.toText();
+ std::replace(hostname.begin(), hostname.end(),
+ (address.isV4() ? '.' : ':'), '-');
+
+ std::ostringstream gen_name;
+ gen_name << d2_client_config_->getGeneratedPrefix() << "-" << hostname;
+ return (qualifyName(gen_name.str()));
+}
+
+std::string
+D2ClientMgr::qualifyName(const std::string& partial_name) const {
+ std::ostringstream gen_name;
+ gen_name << partial_name << "." << d2_client_config_->getQualifyingSuffix();
+
+ // Tack on a trailing dot in case suffix doesn't have one.
+ std::string str = gen_name.str();
+ size_t len = str.length();
+ if ((len > 0) && (str[len - 1] != '.')) {
+ gen_name << ".";
+ }
+
+ return (gen_name.str());
+}
+
+void
+D2ClientMgr::startSender(D2ClientErrorHandler error_handler) {
+ if (amSending()) {
+ return;
+ }
+
+ // Create a our own service instance when we are not being multiplexed
+ // into an external service..
+ private_io_service_.reset(new asiolink::IOService());
+ startSender(error_handler, *private_io_service_);
+ LOG_INFO(dhcpsrv_logger, DHCPSRV_DHCP_DDNS_SENDER_STARTED)
+ .arg(d2_client_config_->toText());
+}
+
+void
+D2ClientMgr::startSender(D2ClientErrorHandler error_handler,
+ isc::asiolink::IOService& io_service) {
+ if (amSending()) {
+ return;
+ }
+
+ if (!name_change_sender_) {
+ isc_throw(D2ClientError, "D2ClientMgr::startSender sender is null");
+ }
+
+ if (!error_handler) {
+ isc_throw(D2ClientError, "D2ClientMgr::startSender handler is null");
+ }
+
+ // Set the error handler.
+ client_error_handler_ = error_handler;
+
+ // Start the sender on the given service.
+ name_change_sender_->startSending(io_service);
+
+ // Register sender's select-fd with IfaceMgr.
+ // We need to remember the fd that is registered so we can unregister later.
+ // IO error handling in the sender may alter its select-fd.
+ registered_select_fd_ = name_change_sender_->getSelectFd();
+ IfaceMgr::instance().addExternalSocket(registered_select_fd_,
+ boost::bind(&D2ClientMgr::runReadyIO,
+ this));
+}
+
+bool
+D2ClientMgr::amSending() const {
+ return (name_change_sender_ && name_change_sender_->amSending());
+}
+
+void
+D2ClientMgr::stopSender() {
+ /// Unregister sender's select-fd.
+ if (registered_select_fd_ != dhcp_ddns::WatchSocket::INVALID_SOCKET) {
+ IfaceMgr::instance().deleteExternalSocket(registered_select_fd_);
+ registered_select_fd_ = dhcp_ddns::WatchSocket::INVALID_SOCKET;
+ }
+
+ // If its not null, call stop.
+ if (amSending()) {
+ name_change_sender_->stopSending();
+ LOG_INFO(dhcpsrv_logger, DHCPSRV_DHCP_DDNS_SENDER_STOPPED);
+ }
+}
+
+void
+D2ClientMgr::sendRequest(dhcp_ddns::NameChangeRequestPtr& ncr) {
+ if (!amSending()) {
+ // This is programmatic error so bust them for it.
+ isc_throw(D2ClientError, "D2ClientMgr::sendRequest not in send mode");
+ }
+
+ try {
+ name_change_sender_->sendRequest(ncr);
+ } catch (const std::exception& ex) {
+ LOG_ERROR(dhcpsrv_logger, DHCPSRV_DHCP_DDNS_NCR_REJECTED)
+ .arg(ex.what()).arg((ncr ? ncr->toText() : " NULL "));
+ invokeClientErrorHandler(dhcp_ddns::NameChangeSender::ERROR, ncr);
+ }
+}
+
+void
+D2ClientMgr::invokeClientErrorHandler(const dhcp_ddns::NameChangeSender::
+ Result result,
+ dhcp_ddns::NameChangeRequestPtr& ncr) {
+ // Handler is mandatory to enter send mode but test it just to be safe.
+ if (!client_error_handler_) {
+ LOG_ERROR(dhcpsrv_logger, DHCPSRV_DHCP_DDNS_HANDLER_NULL);
+ } else {
+ // Handler is not supposed to throw, but catch just in case.
+ try {
+ (client_error_handler_)(result, ncr);
+ } catch (const std::exception& ex) {
+ LOG_ERROR(dhcpsrv_logger, DHCPSRV_DHCP_DDNS_ERROR_EXCEPTION)
+ .arg(ex.what());
+ }
+ }
+}
+
+size_t
+D2ClientMgr::getQueueSize() const {
+ if (!name_change_sender_) {
+ isc_throw(D2ClientError, "D2ClientMgr::getQueueSize sender is null");
+ }
+
+ return(name_change_sender_->getQueueSize());
+}
+
+size_t
+D2ClientMgr::getQueueMaxSize() const {
+ if (!name_change_sender_) {
+ isc_throw(D2ClientError, "D2ClientMgr::getQueueMaxSize sender is null");
+ }
+
+ return(name_change_sender_->getQueueMaxSize());
+}
+
+
+
+const dhcp_ddns::NameChangeRequestPtr&
+D2ClientMgr::peekAt(const size_t index) const {
+ if (!name_change_sender_) {
+ isc_throw(D2ClientError, "D2ClientMgr::peekAt sender is null");
+ }
+
+ return (name_change_sender_->peekAt(index));
+}
+
+void
+D2ClientMgr::clearQueue() {
+ if (!name_change_sender_) {
+ isc_throw(D2ClientError, "D2ClientMgr::clearQueue sender is null");
+ }
+
+ name_change_sender_->clearSendQueue();
+}
+
+void
+D2ClientMgr::operator()(const dhcp_ddns::NameChangeSender::Result result,
+ dhcp_ddns::NameChangeRequestPtr& ncr) {
+ if (result == dhcp_ddns::NameChangeSender::SUCCESS) {
+ LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
+ DHCPSRV_DHCP_DDNS_NCR_SENT).arg(ncr->toText());
+ } else {
+ invokeClientErrorHandler(result, ncr);
+ }
+}
+
+int
+D2ClientMgr::getSelectFd() {
+ if (!amSending()) {
+ isc_throw (D2ClientError, "D2ClientMgr::getSelectFd "
+ " not in send mode");
+ }
+
+ return (name_change_sender_->getSelectFd());
+}
+
+void
+D2ClientMgr::runReadyIO() {
+ if (!name_change_sender_) {
+ // This should never happen.
+ isc_throw(D2ClientError, "D2ClientMgr::runReadyIO"
+ " name_change_sender is null");
+ }
+
+ name_change_sender_->runReadyIO();
+}
+
+}; // namespace dhcp
+
+}; // namespace isc
diff --git a/src/lib/dhcpsrv/d2_client_mgr.h b/src/lib/dhcpsrv/d2_client_mgr.h
new file mode 100644
index 0000000..6222e6b
--- /dev/null
+++ b/src/lib/dhcpsrv/d2_client_mgr.h
@@ -0,0 +1,421 @@
+// Copyright (C) 2014 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 D2_CLIENT_MGR_H
+#define D2_CLIENT_MGR_H
+
+/// @file d2_client_mgr.h Defines the D2ClientMgr class.
+/// This file defines the class Kea uses to act as a client of the
+/// b10-dhcp-ddns module (aka D2).
+///
+#include <asiolink/io_address.h>
+#include <dhcp_ddns/ncr_io.h>
+#include <dhcpsrv/d2_client_cfg.h>
+#include <exceptions/exceptions.h>
+
+#include <boost/shared_ptr.hpp>
+#include <boost/noncopyable.hpp>
+
+#include <stdint.h>
+#include <string>
+#include <vector>
+
+namespace isc {
+namespace dhcp {
+
+/// @brief Defines the type for D2 IO error handler.
+/// This callback is invoked when a send to b10-dhcp-ddns completes with a
+/// failed status. This provides the application layer (Kea) with a means to
+/// handle the error appropriately.
+///
+/// @param result Result code of the send operation.
+/// @param ncr NameChangeRequest which failed to send.
+///
+/// @note Handlers are expected not to throw. In the event a handler does
+/// throw invoking code logs the exception and then swallows it.
+typedef
+boost::function<void(const dhcp_ddns::NameChangeSender::Result result,
+ dhcp_ddns::NameChangeRequestPtr& ncr)> D2ClientErrorHandler;
+
+/// @brief D2ClientMgr isolates Kea from the details of being a D2 client.
+///
+/// Provides services for managing the current dhcp-ddns configuration and
+/// as well as communications with b10-dhcp-ddns. Regarding configuration it
+/// provides services to store, update, and access the current dhcp-ddns
+/// configuration. As for b10-dhcp-ddns communications, D2ClientMgr creates
+/// maintains a NameChangeSender appropriate to the current configuration and
+/// provides services to start, stop, and post NCRs to the sender. Additionally
+/// there are methods to examine the queue of requests currently waiting for
+/// transmission.
+///
+/// The manager also provides the mechanics to integrate the ASIO-based IO
+/// used by the NCR IPC with the select-driven IO used by Kea. Senders expose
+/// a file descriptor, the "select-fd" that can monitored for read-readiness
+/// with the select() function (or variants). D2ClientMgr provides a method,
+/// runReadyIO(), that will instructs the sender to process the next ready
+/// ready IO handler on the sender's IOservice. Track# 3315 extended
+/// Kea's IfaceMgr to support the registration of multiple external sockets
+/// with callbacks that are then monitored with IO readiness via select().
+/// D2ClientMgr registers the sender's select-fd and runReadyIO() with
+/// IfaceMgr when entering the send mode and unregisters it when exiting send
+/// mode.
+///
+/// To place the manager in send mode, the calling layer must supply an error
+/// handler and optionally an IOService instance. The error handler is invoked
+/// if a send completes with a failed status. This provides the calling layer
+/// an opportunity act upon the error.
+///
+/// If the caller supplies an IOService, that service will be used to process
+/// the sender's IO. If not supplied, D2ClientMgr pass a private IOService
+/// into the sender. Using a private service isolates the sender's IO from
+/// any other services.
+///
+class D2ClientMgr : public dhcp_ddns::NameChangeSender::RequestSendHandler,
+ boost::noncopyable {
+public:
+ /// @brief Constructor
+ ///
+ /// Default constructor which constructs an instance which has DHCP-DDNS
+ /// updates disabled.
+ D2ClientMgr();
+
+ /// @brief Destructor.
+ ~D2ClientMgr();
+
+ /// @brief Updates the DHCP-DDNS client configuration to the given value.
+ ///
+ /// @param new_config pointer to the new client configuration.
+ /// @throw D2ClientError if passed an empty pointer.
+ void setD2ClientConfig(D2ClientConfigPtr& new_config);
+
+ /// @brief Convenience method for checking if DHCP-DDNS is enabled.
+ ///
+ /// @return True if the D2 configuration is enabled.
+ bool ddnsEnabled();
+
+ /// @brief Fetches the DHCP-DDNS configuration pointer.
+ ///
+ /// @return a reference to the current configuration pointer.
+ const D2ClientConfigPtr& getD2ClientConfig() const;
+
+ /// @brief Determines server flags based on configuration and client flags.
+ ///
+ /// This method uses input values for the client's FQDN S and N flags, in
+ /// conjunction with the configuration parameters updates-enabled, override-
+ /// no-updates, and override-client-updates to determine the values that
+ /// should be used for the server's FQDN S and N flags.
+ /// The logic in this method is based upon RFCs 4702 and 4704.
+ ///
+ /// @param client_s S Flag from the client's FQDN
+ /// @param client_n N Flag from the client's FQDN
+ /// @param server_s [out] S Flag for the server's FQDN
+ /// @param server_n [out] N Flag for the server's FQDN
+ ///
+ /// @throw isc::BadValue if client_s and client_n are both 1 as this is
+ /// an invalid combination per RFCs.
+ void analyzeFqdn(const bool client_s, const bool client_n, bool& server_s,
+ bool& server_n) const;
+
+ /// @brief Builds a FQDN based on the configuration and given IP address.
+ ///
+ /// Using the current values for generated-prefix, qualifying-suffix and
+ /// an IP address, this method constructs a fully qualified domain name.
+ /// It supports both IPv4 and IPv6 addresses. The format of the name
+ /// is as follows:
+ ///
+ /// <generated-prefix>-<ip address>.<qualifying-suffix>.
+ ///
+ /// <ip-address> is the result of IOAddress.toText() with the delimiters
+ /// ('.' for IPv4 or ':' for IPv6) replaced with a hyphen, '-'.
+ ///
+ /// @param address IP address from which to derive the name (IPv4 or IPv6)
+ ///
+ /// @return std::string containing the generated name.
+ std::string generateFqdn(const asiolink::IOAddress& address) const;
+
+ /// @brief Adds a qualifying suffix to a given domain name
+ ///
+ /// Constructs a FQDN based on the configured qualifying-suffix and
+ /// a partial domain name as follows:
+ ///
+ /// <partial_name>.<qualifying-suffix>.
+ /// Note it will add a trailing '.' should qualifying-suffix not end with
+ /// one.
+ ///
+ /// @param partial_name domain name to qualify
+ ///
+ /// @return std::string containing the qualified name.
+ std::string qualifyName(const std::string& partial_name) const;
+
+ /// @brief Set server FQDN flags based on configuration and a given FQDN
+ ///
+ /// Templated wrapper around the analyzeFqdn() allowing that method to
+ /// be used for either IPv4 or IPv6 processing. This methods resets all
+ /// of the flags in the response to zero and then sets the S,N, and O
+ /// flags. Any other flags are the responsibility of the invoking layer.
+ ///
+ /// @param fqdn FQDN option from which to read client (inbound) flags
+ /// @param fqdn_resp FQDN option to update with the server (outbound) flags
+ /// @tparam T FQDN Option class containing the FQDN data such as
+ /// dhcp::Option4ClientFqdn or dhcp::Option6ClientFqdn
+ template <class T>
+ void adjustFqdnFlags(const T& fqdn, T& fqdn_resp);
+
+ /// @brief Set server FQDN name based on configuration and a given FQDN
+ ///
+ /// Templated method which adjusts the domain name value and type in
+ /// a server FQDN from a client (inbound) FQDN and the current
+ /// configuration. The logic is as follows:
+ ///
+ /// If replace-client-name is true or the supplied name is empty, the
+ /// server FQDN is set to ""/PARTIAL.
+ ///
+ /// If replace-client-name is false and the supplied name is a partial
+ /// name the server FQDN is set to the supplied name qualified by
+ /// appending the qualifying-suffix.
+ ///
+ /// If replace-client-name is false and the supplied name is a fully
+ /// qualified name, set the server FQDN to the supplied name.
+ ///
+ /// @param fqdn FQDN option from which to get client (inbound) name
+ /// @param fqdn_resp FQDN option to update with the adjusted name
+ /// @tparam T FQDN Option class containing the FQDN data such as
+ /// dhcp::Option4ClientFqdn or dhcp::Option6ClientFqdn
+ template <class T>
+ void adjustDomainName(const T& fqdn, T& fqdn_resp);
+
+ /// @brief Enables sending NameChangeRequests to b10-dhcp-ddns
+ ///
+ /// Places the NameChangeSender into send mode. This instructs the
+ /// sender to begin dequeuing and transmitting requests and to accept
+ /// additional requests via the sendRequest() method.
+ ///
+ /// @param error_handler application level error handler to cope with
+ /// sends that complete with a failed status. A valid function must be
+ /// supplied as the manager cannot know how an application should deal
+ /// with send failures.
+ /// @param io_service IOService to be used for sender IO event processing
+ /// @warning It is up to the invoking layer to ensure the io_service
+ /// instance used outlives the D2ClientMgr send mode. When the send mode
+ /// is exited, either expliclity by callind stopSender() or implicitly
+ /// through D2CLientMgr destruction, any ASIO objects such as sockets or
+ /// timers will be closed and released. If the io_service goes out of scope
+ /// first this behavior could be unpredictable.
+ ///
+ /// @throw D2ClientError if sender instance is null. Underlying layer
+ /// may throw NCRSenderExceptions exceptions.
+ void startSender(D2ClientErrorHandler error_handler,
+ isc::asiolink::IOService& io_service);
+
+ /// @brief Enables sending NameChangeRequests to b10-dhcp-ddns
+ ///
+ /// Places the NameChangeSender into send mode. This instructs the
+ /// sender to begin dequeuing and transmitting requests and to accept
+ /// additional requests via the sendRequest() method. The manager
+ /// will create a new, private instance of an IOService for the sender
+ /// to use for IO event processing.
+ ///
+ /// @param error_handler application level error handler to cope with
+ /// sends that complete with a failed status. A valid function must be
+ /// supplied as the manager cannot know how an application should deal
+ /// with send failures.
+ ///
+ /// @throw D2ClientError if sender instance is null. Underlying layer
+ /// may throw NCRSenderExceptions exceptions.
+ void startSender(D2ClientErrorHandler error_handler);
+
+ /// @brief Returns true if the sender is in send mode, false otherwise.
+ ///
+ /// A true value indicates that the sender is present and in accepting
+ /// messages for transmission, false otherwise.
+ bool amSending() const;
+
+ /// @brief Disables sending NameChangeRequests to b10-dhcp-ddns
+ ///
+ /// Takes the NameChangeSender out of send mode. The sender will stop
+ /// transmitting requests, though any queued requests remain queued.
+ /// Attempts to queue additional requests via sendRequest will fail.
+ ///
+ /// @throw D2ClientError if sender instance is null. Underlying layer
+ /// may throw NCRSenderExceptions exceptions.
+ void stopSender();
+
+ /// @brief Send the given NameChangeRequests to b10-dhcp-ddns
+ ///
+ /// Passes NameChangeRequests to the NCR sender for transmission to
+ /// b10-dhcp-ddns. If the sender rejects the message, the client's error
+ /// handler will be invoked. The most likely cause for rejection is
+ /// the senders' queue has reached maximum capacity.
+ ///
+ /// @param ncr NameChangeRequest to send
+ ///
+ /// @throw D2ClientError if sender instance is null or not in send
+ /// mode. Either of these represents a programmatic error.
+ void sendRequest(dhcp_ddns::NameChangeRequestPtr& ncr);
+
+ /// @brief Calls the client's error handler.
+ ///
+ /// Calls the error handler method set by startSender() when an
+ /// error occurs attempting to send a method. If the error handler
+ /// throws an exception it will be caught and logged.
+ ///
+ /// @param result contains that send outcome status.
+ /// @param ncr is a pointer to the NameChangeRequest that was attempted.
+ ///
+ /// This method is exception safe.
+ void invokeClientErrorHandler(const dhcp_ddns::NameChangeSender::
+ Result result,
+ dhcp_ddns::NameChangeRequestPtr& ncr);
+
+ /// @brief Returns the number of NCRs queued for transmission.
+ size_t getQueueSize() const;
+
+ /// @brief Returns the maximum number of NCRs allowed in the queue.
+ size_t getQueueMaxSize() const;
+
+ /// @brief Returns the nth NCR queued for transmission.
+ ///
+ /// Note that the entry is not removed from the queue.
+ /// @param index the index of the entry in the queue to fetch.
+ /// Valid values are 0 (front of the queue) to (queue size - 1).
+ /// @note This method is for test purposes only.
+ ///
+ /// @return Pointer reference to the queue entry.
+ ///
+ /// @throw D2ClientError if sender instance is null. Underlying layer
+ /// may throw NCRSenderExceptions exceptions.
+ const dhcp_ddns::NameChangeRequestPtr& peekAt(const size_t index) const;
+
+ /// @brief Removes all NCRs queued for transmission.
+ ///
+ /// @throw D2ClientError if sender instance is null. Underlying layer
+ /// may throw NCRSenderExceptions exceptions.
+ void clearQueue();
+
+ /// @brief Processes sender IO events
+ ///
+ /// Serves as callback registered for the sender's select-fd with IfaceMgr.
+ /// It instructs the sender to execute the next ready IO handler.
+ /// It provides an instance method that can be bound via boost::bind, as
+ /// NameChangeSender is abstract.
+ void runReadyIO();
+
+ /// @brief Suspends sending requests.
+ ///
+ /// This method is intended to be used when IO errors occur. It toggles
+ /// the enable-updates configuration flag to off, and takes the sender
+ /// out of send mode. Messages in the sender's queue will remain in the
+ /// queue.
+ /// @todo This logic may change in NameChangeSender is altered allow
+ /// queuing while stopped. Currently when a sender is not in send mode
+ /// it will not accept additional messages.
+ void suspendUpdates();
+
+protected:
+ /// @brief Function operator implementing the NCR sender callback.
+ ///
+ /// This method is invoked each time the NameChangeSender completes
+ /// an asynchronous send.
+ ///
+ /// @param result contains that send outcome status.
+ /// @param ncr is a pointer to the NameChangeRequest that was
+ /// delivered (or attempted).
+ ///
+ /// @throw This method MUST NOT throw.
+ virtual void operator ()(const dhcp_ddns::NameChangeSender::Result result,
+ dhcp_ddns::NameChangeRequestPtr& ncr);
+
+ /// @brief Fetches the sender's select-fd.
+ ///
+ /// The select-fd may be used with select() or poll(). If the sender has
+ /// IO waiting to process, the fd will evaluate as !EWOULDBLOCK.
+ /// @note This is only exposed for testing purposes.
+ ///
+ /// @return The sender's select-fd
+ ///
+ /// @throw D2ClientError if the sender does not exist or is not in send
+ /// mode.
+ int getSelectFd();
+
+ /// @brief Fetches the select-fd that is currently registered.
+ ///
+ /// @return The currently registered select-fd or
+ /// dhcp_ddns::WatchSocket::INVALID_SOCKET.
+ ///
+ /// @note This is only exposed for testing purposes.
+ int getRegisteredSelectFd();
+
+private:
+ /// @brief Container class for DHCP-DDNS configuration parameters.
+ D2ClientConfigPtr d2_client_config_;
+
+ /// @brief Pointer to the current interface to DHCP-DDNS.
+ dhcp_ddns::NameChangeSenderPtr name_change_sender_;
+
+ /// @brief Private IOService to use if calling layer doesn't wish to
+ /// supply one.
+ boost::shared_ptr<asiolink::IOService> private_io_service_;
+
+ /// @brief Application supplied error handler invoked when a send
+ /// completes with a failed status.
+ D2ClientErrorHandler client_error_handler_;
+
+ /// @brief Remembers the select-fd registered with IfaceMgr.
+ int registered_select_fd_;
+};
+
+template <class T>
+void
+D2ClientMgr::adjustFqdnFlags(const T& fqdn, T& fqdn_resp) {
+ bool server_s = false;
+ bool server_n = false;
+ analyzeFqdn(fqdn.getFlag(T::FLAG_S), fqdn.getFlag(T::FLAG_N),
+ server_s, server_n);
+
+ // Reset the flags to zero to avoid triggering N and S both 1 check.
+ fqdn_resp.resetFlags();
+
+ // Set S and N flags.
+ fqdn_resp.setFlag(T::FLAG_S, server_s);
+ fqdn_resp.setFlag(T::FLAG_N, server_n);
+
+ // Set O flag true if server S overrides client S.
+ fqdn_resp.setFlag(T::FLAG_O, (fqdn.getFlag(T::FLAG_S) != server_s));
+}
+
+
+template <class T>
+void
+D2ClientMgr::adjustDomainName(const T& fqdn, T& fqdn_resp) {
+ // If we're configured to replace it or the supplied name is blank
+ // set the response name to blank.
+ if (d2_client_config_->getReplaceClientName() ||
+ fqdn.getDomainName().empty()) {
+ fqdn_resp.setDomainName("", T::PARTIAL);
+ } else {
+ // If the supplied name is partial, qualify it by adding the suffix.
+ if (fqdn.getDomainNameType() == T::PARTIAL) {
+ fqdn_resp.setDomainName(qualifyName(fqdn.getDomainName()), T::FULL);
+ }
+ }
+}
+
+/// @brief Defines a pointer for D2ClientMgr instances.
+typedef boost::shared_ptr<D2ClientMgr> D2ClientMgrPtr;
+
+
+} // namespace isc
+} // namespace dhcp
+
+#endif
diff --git a/src/lib/dhcpsrv/dbaccess_parser.h b/src/lib/dhcpsrv/dbaccess_parser.h
index 35d72ff..77c9845 100644
--- a/src/lib/dhcpsrv/dbaccess_parser.h
+++ b/src/lib/dhcpsrv/dbaccess_parser.h
@@ -94,7 +94,8 @@ public:
///
/// Creates an instance of this parser.
///
- /// @param param_name Name of the parameter used to access the configuration.
+ /// @param param_name Name of the parameter used to access the
+ /// configuration.
///
/// @return Pointer to a DbAccessParser. The caller is responsible for
/// destroying the parser after use.
diff --git a/src/lib/dhcpsrv/dhcp_parsers.cc b/src/lib/dhcpsrv/dhcp_parsers.cc
index c647954..13e1f0f 100644
--- a/src/lib/dhcpsrv/dhcp_parsers.cc
+++ b/src/lib/dhcpsrv/dhcp_parsers.cc
@@ -400,15 +400,27 @@ void
OptionDataParser::createOption() {
// Option code is held in the uint32_t storage but is supposed to
// be uint16_t value. We need to check that value in the configuration
- // does not exceed range of uint8_t and is not zero.
+ // does not exceed range of uint8_t for DHCPv4, uint16_t for DHCPv6 and
+ // is not zero.
uint32_t option_code = uint32_values_->getParam("code");
if (option_code == 0) {
isc_throw(DhcpConfigError, "option code must not be zero."
- << " Option code '0' is reserved in DHCPv4.");
- } else if (option_code > std::numeric_limits<uint8_t>::max()) {
+ << " Option code '0' is reserved.");
+
+ } else if (global_context_->universe_ == Option::V4 &&
+ option_code > std::numeric_limits<uint8_t>::max()) {
isc_throw(DhcpConfigError, "invalid option code '" << option_code
<< "', it must not exceed '"
- << std::numeric_limits<uint8_t>::max() << "'");
+ << static_cast<int>(std::numeric_limits<uint8_t>::max())
+ << "'");
+
+ } else if (global_context_->universe_ == Option::V6 &&
+ option_code > std::numeric_limits<uint16_t>::max()) {
+ isc_throw(DhcpConfigError, "invalid option code '" << option_code
+ << "', it must not exceed '"
+ << std::numeric_limits<uint16_t>::max()
+ << "'");
+
}
// Check that the option name has been specified, is non-empty and does not
@@ -464,7 +476,7 @@ OptionDataParser::createOption() {
}
// Get option data from the configuration database ('data' field).
- const std::string option_data = string_values_->getParam("data");
+ std::string option_data = string_values_->getParam("data");
// Transform string of hexadecimal digits into binary format.
std::vector<uint8_t> binary;
@@ -480,6 +492,12 @@ OptionDataParser::createOption() {
// Otherwise, the option data is specified as a string of
// hexadecimal digits that we have to turn into binary format.
try {
+ // The decodeHex function expects that the string contains an
+ // even number of digits. If we don't meet this requirement,
+ // we have to insert a leading 0.
+ if (!option_data.empty() && option_data.length() % 2) {
+ option_data = option_data.insert(0, "0");
+ }
util::encode::decodeHex(option_data, binary);
} catch (...) {
isc_throw(DhcpConfigError, "option data is not a valid"
@@ -793,6 +811,67 @@ OptionDefListParser::commit() {
}
}
+//****************************** RelayInfoParser ********************************
+RelayInfoParser::RelayInfoParser(const std::string&,
+ const isc::dhcp::Subnet::RelayInfoPtr& relay_info,
+ const Option::Universe& family)
+ :storage_(relay_info), local_(isc::asiolink::IOAddress(
+ family == Option::V4 ? "0.0.0.0" : "::")),
+ string_values_(new StringStorage()), family_(family) {
+ if (!relay_info) {
+ isc_throw(isc::dhcp::DhcpConfigError, "parser logic error:"
+ << "relay-info storage may not be NULL");
+ }
+
+};
+
+void
+RelayInfoParser::build(ConstElementPtr relay_info) {
+
+ BOOST_FOREACH(ConfigPair param, relay_info->mapValue()) {
+ ParserPtr parser(createConfigParser(param.first));
+ parser->build(param.second);
+ parser->commit();
+ }
+
+ // Get the IP address
+ boost::scoped_ptr<asiolink::IOAddress> ip;
+ try {
+ ip.reset(new asiolink::IOAddress(string_values_->getParam("ip-address")));
+ } catch (...) {
+ isc_throw(DhcpConfigError, "Failed to parse ip-address "
+ "value: " << string_values_->getParam("ip-address"));
+ }
+
+ if ( (ip->isV4() && family_ != Option::V4) ||
+ (ip->isV6() && family_ != Option::V6) ) {
+ isc_throw(DhcpConfigError, "ip-address field " << ip->toText()
+ << "does not have IP address of expected family type: "
+ << (family_ == Option::V4?"IPv4":"IPv6"));
+ }
+
+ local_.addr_ = *ip;
+}
+
+isc::dhcp::ParserPtr
+RelayInfoParser::createConfigParser(const std::string& parameter) {
+ DhcpConfigParser* parser = NULL;
+ if (parameter.compare("ip-address") == 0) {
+ parser = new StringParser(parameter, string_values_);
+ } else {
+ isc_throw(NotImplemented,
+ "parser error: RelayInfoParser parameter not supported: "
+ << parameter);
+ }
+
+ return (isc::dhcp::ParserPtr(parser));
+}
+
+void
+RelayInfoParser::commit() {
+ *storage_ = local_;
+}
+
//****************************** PoolParser ********************************
PoolParser::PoolParser(const std::string&, PoolStoragePtr pools)
:pools_(pools) {
@@ -876,10 +955,12 @@ PoolParser::commit() {
//****************************** SubnetConfigParser *************************
SubnetConfigParser::SubnetConfigParser(const std::string&,
- ParserContextPtr global_context)
+ ParserContextPtr global_context,
+ const isc::asiolink::IOAddress& default_addr)
: uint32_values_(new Uint32Storage()), string_values_(new StringStorage()),
pools_(new PoolStorage()), options_(new OptionStorage()),
- global_context_(global_context) {
+ global_context_(global_context),
+ relay_info_(new isc::dhcp::Subnet::RelayInfo(default_addr)) {
// The first parameter should always be "subnet", but we don't check
// against that here in case some wants to reuse this parser somewhere.
if (!global_context_) {
@@ -1182,10 +1263,11 @@ D2ClientConfigParser::build(isc::data::ConstElementPtr client_config) {
}
bool enable_updates = boolean_values_->getParam("enable-updates");
- if (!enable_updates) {
- // If it's not enabled, don't bother validating the rest. This
- // allows for an abbreviated config entry that only contains
- // the flag. The default constructor creates a disabled instance.
+ if (!enable_updates && (client_config->mapValue().size() == 1)) {
+ // If enable-updates is the only parameter and it is false then
+ // we're done. This allows for an abbreviated configuration entry
+ // that only contains that flag. Use the default D2ClientConfig
+ // constructor to a create a disabled instance.
local_client_config_.reset(new D2ClientConfig());
return;
}
@@ -1207,7 +1289,6 @@ D2ClientConfigParser::build(isc::data::ConstElementPtr client_config) {
std::string qualifying_suffix = string_values_->
getParam("qualifying-suffix");
- bool remove_on_renew = boolean_values_->getParam("remove-on-renew");
bool always_include_fqdn = boolean_values_->getParam("always-include-fqdn");
bool override_no_update = boolean_values_->getParam("override-no-update");
bool override_client_update = boolean_values_->
@@ -1217,7 +1298,7 @@ D2ClientConfigParser::build(isc::data::ConstElementPtr client_config) {
// Attempt to create the new client config.
local_client_config_.reset(new D2ClientConfig(enable_updates, server_ip,
server_port, ncr_protocol,
- ncr_format, remove_on_renew,
+ ncr_format,
always_include_fqdn,
override_no_update,
override_client_update,
@@ -1238,7 +1319,6 @@ D2ClientConfigParser::createConfigParser(const std::string& config_id) {
(config_id.compare("qualifying-suffix") == 0)) {
parser = new StringParser(config_id, string_values_);
} else if ((config_id.compare("enable-updates") == 0) ||
- (config_id.compare("remove-on-renew") == 0) ||
(config_id.compare("always-include-fqdn") == 0) ||
(config_id.compare("allow-client-update") == 0) ||
(config_id.compare("override-no-update") == 0) ||
diff --git a/src/lib/dhcpsrv/dhcp_parsers.h b/src/lib/dhcpsrv/dhcp_parsers.h
index 00a07a0..eefc76e 100644
--- a/src/lib/dhcpsrv/dhcp_parsers.h
+++ b/src/lib/dhcpsrv/dhcp_parsers.h
@@ -18,7 +18,7 @@
#include <asiolink/io_address.h>
#include <cc/data.h>
#include <dhcp/option_definition.h>
-#include <dhcpsrv/d2_client.h>
+#include <dhcpsrv/d2_client_cfg.h>
#include <dhcpsrv/dhcp_config_parser.h>
#include <dhcpsrv/option_space_container.h>
#include <dhcpsrv/subnet.h>
@@ -754,6 +754,59 @@ protected:
PoolStorage local_pools_;
};
+/// @brief parser for additional relay information
+///
+/// This concrete parser handles RelayInfo structure defintions.
+/// So far that structure holds only relay IP (v4 or v6) address, but it
+/// is expected that the number of parameters will increase over time.
+///
+/// It is useful for parsing Dhcp<4/6>/subnet<4/6>[x]/relay parameters.
+class RelayInfoParser : public DhcpConfigParser {
+public:
+
+ /// @brief constructor
+ /// @param unused first argument is ignored, all Parser constructors
+ /// accept string as first argument.
+ /// @param relay_info is the storage in which to store the parsed
+ /// @param family specifies protocol family (IPv4 or IPv6)
+ RelayInfoParser(const std::string& unused,
+ const isc::dhcp::Subnet::RelayInfoPtr& relay_info,
+ const isc::dhcp::Option::Universe& family);
+
+ /// @brief parses the actual relay parameters
+ /// @param relay_info JSON structure holding relay parameters to parse
+ virtual void build(isc::data::ConstElementPtr relay_info);
+
+ /// @brief stores parsed info in relay_info
+ virtual void commit();
+
+protected:
+
+ /// @brief Creates a parser for the given "relay" member element id.
+ ///
+ /// The elements currently supported are:
+ /// -# ip-address
+ ///
+ /// @param config_id is the "item_name" for a specific member element of
+ /// the "relay" specification.
+ ///
+ /// @return returns a pointer to newly created parser.
+ isc::dhcp::ParserPtr
+ createConfigParser(const std::string& parser);
+
+ /// Parsed data will be stored there on commit()
+ isc::dhcp::Subnet::RelayInfoPtr storage_;
+
+ /// Local storage information (for temporary values)
+ isc::dhcp::Subnet::RelayInfo local_;
+
+ /// Storage for subnet-specific string values.
+ StringStoragePtr string_values_;
+
+ /// Protocol family (IPv4 or IPv6)
+ Option::Universe family_;
+};
+
/// @brief this class parses a single subnet
///
/// This class parses the whole subnet definition. It creates parsers
@@ -762,7 +815,11 @@ class SubnetConfigParser : public DhcpConfigParser {
public:
/// @brief constructor
- SubnetConfigParser(const std::string&, ParserContextPtr global_context);
+ ///
+ /// @param global_context
+ /// @param default_addr default IP address (0.0.0.0 for IPv4, :: for IPv6)
+ SubnetConfigParser(const std::string&, ParserContextPtr global_context,
+ const isc::asiolink::IOAddress& default_addr);
/// @brief parses parameter value
///
@@ -875,6 +932,9 @@ protected:
/// Parsing context which contains global values, options and option
/// definitions.
ParserContextPtr global_context_;
+
+ /// Pointer to relay information
+ isc::dhcp::Subnet::RelayInfoPtr relay_info_;
};
/// @brief Parser for D2ClientConfig
diff --git a/src/lib/dhcpsrv/dhcpsrv_messages.mes b/src/lib/dhcpsrv/dhcpsrv_messages.mes
index 5c7854e..5151851 100644
--- a/src/lib/dhcpsrv/dhcpsrv_messages.mes
+++ b/src/lib/dhcpsrv/dhcpsrv_messages.mes
@@ -1,4 +1,4 @@
-# Copyright (C) 2012-2013 Internet Systems Consortium, Inc. ("ISC")
+# Copyright (C) 2012-2014 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
@@ -138,11 +138,29 @@ the database access parameters are changed: in the latter case, the
server closes the currently open database, and opens a database using
the new parameters.
-% DHCPSRV_HOOK_LEASE4_SELECT_SKIP Lease4 creation was skipped, because of callout skip flag.
-This debug message is printed when a callout installed on lease4_select
-hook point sets the skip flag. It means that the server was told that
-no lease4 should be assigned. The server will not put that lease in its
-database and the client will get a NAK packet.
+% DHCPSRV_DHCP_DDNS_ERROR_EXCEPTION error handler for DHCP_DDNS IO generated an expected exception: %1
+This is an error message that occurs when an attempt to send a request to
+b10-dhcp-ddns fails there registered error handler threw an uncaught exception.
+This is a programmatic error which should not occur. By convention, the error
+handler should not propagate exceptions. Please report this error.
+
+% DHCPSRV_DHCP_DDNS_HANDLER_NULL error handler for DHCP_DDNS IO is not set.
+This is an error message that occurs when an attempt to send a request to
+b10-dhcp-ddns fails and there is no registered error handler. This is a
+programmatic error which should never occur and should be reported.
+
+% DHCPSRV_DHCP_DDNS_NCR_SENT NameChangeRequest sent to b10-dhcp-ddns: %1
+A debug message issued when a NameChangeRequest has been successfully sent to
+b10-dhcp-ddns.
+
+% DHCPSRV_DHCP_DDNS_SENDER_STARTED NameChangeRequest sender has been started: %1
+A informational message issued when a communications with b10-dhcp-ddns has
+been successfully started.
+
+% DHCPSRV_DHCP_DDNS_SENDER_STOPPED NameChangeRequest sender has been stopped.
+A informational message issued when a communications with b10-dhcp-ddns has
+been stopped. This normally occurs during reconfiguration and as part of normal
+shutdown. It may occur if b10-dhcp-ddns communications breakdown.
% DHCPSRV_HOOK_LEASE4_RENEW_SKIP DHCPv4 lease was not renewed because a callout set the skip flag.
This debug message is printed when a callout installed on lease4_renew
@@ -150,6 +168,12 @@ hook point set the skip flag. For this particular hook point, the setting
of the flag by a callout instructs the server to not renew a lease. The
server will use existing lease as it is, without extending its lifetime.
+% DHCPSRV_HOOK_LEASE4_SELECT_SKIP Lease4 creation was skipped, because of callout skip flag.
+This debug message is printed when a callout installed on lease4_select
+hook point sets the skip flag. It means that the server was told that
+no lease4 should be assigned. The server will not put that lease in its
+database and the client will get a NAK packet.
+
% DHCPSRV_HOOK_LEASE6_SELECT_SKIP Lease6 (non-temporary) creation was skipped, because of callout skip flag.
This debug message is printed when a callout installed on lease6_select
hook point sets the skip flag. It means that the server was told that
@@ -198,6 +222,11 @@ A debug message issued when the server is attempting to obtain a set of
IPv4 leases from the memory file database for a client with the specified
client identification.
+% DHCPSRV_MEMFILE_GET_CLIENTID_HWADDR_SUBID obtaining IPv4 lease for client ID %1, hardware address %2 and subnet ID %3
+A debug message issued when the server is attempting to obtain an IPv4
+lease from the memory file database for a client with the specified
+client ID, hardware address and subnet ID.
+
% DHCPSRV_MEMFILE_GET_HWADDR obtaining IPv4 leases for hardware address %1
A debug message issued when the server is attempting to obtain a set of
IPv4 leases from the memory file database for a client with the specified
@@ -213,11 +242,6 @@ A debug message issued when the server is attempting to obtain an IPv6
lease from the memory file database for a client with the specified IAID
(Identity Association ID), Subnet ID and DUID (DHCP Unique Identifier).
-% DHCPSRV_MEMFILE_GET_CLIENTID_HWADDR_SUBID obtaining IPv4 lease for client ID %1, hardware address %2 and subnet ID %3
-A debug message issued when the server is attempting to obtain an IPv4
-lease from the memory file database for a client with the specified
-client ID, hardware address and subnet ID.
-
% DHCPSRV_MEMFILE_GET_SUBID_CLIENTID obtaining IPv4 lease for subnet ID %1 and client ID %2
A debug message issued when the server is attempting to obtain an IPv4
lease from the memory file database for a client with the specified
@@ -344,3 +368,16 @@ indicate an error in the source code, please submit a bug report.
% DHCPSRV_UNKNOWN_DB unknown database type: %1
The database access string specified a database type (given in the
message) that is unknown to the software. This is a configuration error.
+
+% DHCPSRV_DHCP_DDNS_SUSPEND_UPDATES DHCP_DDNS updates are being suspended.
+This is a warning message indicating the DHCP_DDNS updates have been turned
+off. This should only occur if IO errors communicating with b10-dhcp-ddns
+have been experienced. Any such errors should have preceding entries in the
+log with details. No further attempts to communicate with b10-dhcp-ddns will
+be made without intervention.
+
+% DHCPSRV_DHCP_DDNS_NCR_REJECTED NameChangeRequest rejected by the sender: %1, ncr: %2
+This is an error message indicating that NameChangeSender used to deliver DDNS
+update requests to b10-dhcp-ddns rejected the request. This most likely cause
+is the sender's queue has reached maximum capacity. This would imply that
+requests are being generated faster than they can be delivered.
diff --git a/src/lib/dhcpsrv/lease.cc b/src/lib/dhcpsrv/lease.cc
index a15d300..7bc71f9 100644
--- a/src/lib/dhcpsrv/lease.cc
+++ b/src/lib/dhcpsrv/lease.cc
@@ -1,4 +1,4 @@
-// Copyright (C) 2012-2013 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2012-2014 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
@@ -56,6 +56,13 @@ bool Lease::expired() const {
return (expire_time < time(NULL));
}
+bool
+Lease::hasIdenticalFqdn(const Lease& other) const {
+ return (hostname_ == other.hostname_ &&
+ fqdn_fwd_ == other.fqdn_fwd_ &&
+ fqdn_rev_ == other.fqdn_rev_);
+}
+
Lease4::Lease4(const Lease4& other)
: Lease(other.addr_, other.t1_, other.t2_, other.valid_lft_,
other.subnet_id_, other.cltt_, other.fqdn_fwd_,
diff --git a/src/lib/dhcpsrv/lease.h b/src/lib/dhcpsrv/lease.h
index 8626620..c767142 100644
--- a/src/lib/dhcpsrv/lease.h
+++ b/src/lib/dhcpsrv/lease.h
@@ -1,4 +1,4 @@
-// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2013-2014 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
@@ -141,6 +141,14 @@ struct Lease {
/// @return true if the lease is expired
bool expired() const;
+ /// @brief Returns true if the other lease has equal FQDN data.
+ ///
+ /// @param other Lease which FQDN data is to be compared with our lease.
+ ///
+ /// @return Boolean value which indicates whether FQDN data of the other
+ /// lease is equal to the FQDN data of our lease (true) or not (false).
+ bool hasIdenticalFqdn(const Lease& other) const;
+
};
/// @brief Structure that holds a lease for IPv4 address
diff --git a/src/lib/dhcpsrv/subnet.cc b/src/lib/dhcpsrv/subnet.cc
index 0134d8c..f181a3a 100644
--- a/src/lib/dhcpsrv/subnet.cc
+++ b/src/lib/dhcpsrv/subnet.cc
@@ -1,4 +1,4 @@
-// Copyright (C) 2012-2013 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2012-2014 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
@@ -30,12 +30,14 @@ SubnetID Subnet::static_id_ = 1;
Subnet::Subnet(const isc::asiolink::IOAddress& prefix, uint8_t len,
const Triplet<uint32_t>& t1,
const Triplet<uint32_t>& t2,
- const Triplet<uint32_t>& valid_lifetime)
- :id_(generateNextID()), prefix_(prefix), prefix_len_(len), t1_(t1),
- t2_(t2), valid_(valid_lifetime),
+ const Triplet<uint32_t>& valid_lifetime,
+ const isc::dhcp::Subnet::RelayInfo& relay)
+ :id_(generateNextID()), prefix_(prefix), prefix_len_(len),
+ t1_(t1), t2_(t2), valid_(valid_lifetime),
last_allocated_ia_(lastAddrInPrefix(prefix, len)),
last_allocated_ta_(lastAddrInPrefix(prefix, len)),
- last_allocated_pd_(lastAddrInPrefix(prefix, len)) {
+ last_allocated_pd_(lastAddrInPrefix(prefix, len)), relay_(relay)
+ {
if ((prefix.isV6() && len > 128) ||
(prefix.isV4() && len > 32)) {
isc_throw(BadValue,
@@ -43,6 +45,10 @@ Subnet::Subnet(const isc::asiolink::IOAddress& prefix, uint8_t len,
}
}
+Subnet::RelayInfo::RelayInfo(const isc::asiolink::IOAddress& addr)
+ :addr_(addr) {
+}
+
bool
Subnet::inRange(const isc::asiolink::IOAddress& addr) const {
IOAddress first = firstAddrInPrefix(prefix_, prefix_len_);
@@ -66,6 +72,33 @@ Subnet::addOption(const OptionPtr& option, bool persistent,
}
void
+Subnet::setRelayInfo(const isc::dhcp::Subnet::RelayInfo& relay) {
+ relay_ = relay;
+}
+
+bool
+Subnet::clientSupported(const isc::dhcp::ClientClasses& classes) const {
+ if (white_list_.empty()) {
+ return (true); // There is no class defined for this subnet, so we do
+ // support everyone.
+ }
+
+ for (ClientClasses::const_iterator it = white_list_.begin();
+ it != white_list_.end(); ++it) {
+ if (classes.contains(*it)) {
+ return (true);
+ }
+ }
+
+ return (false);
+}
+
+void
+Subnet::allowClientClass(const isc::dhcp::ClientClass& class_name) {
+ white_list_.insert(class_name);
+}
+
+void
Subnet::delOptions() {
option_spaces_.clearItems();
}
@@ -179,8 +212,8 @@ Subnet4::Subnet4(const isc::asiolink::IOAddress& prefix, uint8_t length,
const Triplet<uint32_t>& t1,
const Triplet<uint32_t>& t2,
const Triplet<uint32_t>& valid_lifetime)
- :Subnet(prefix, length, t1, t2, valid_lifetime),
- siaddr_(IOAddress("0.0.0.0")) {
+:Subnet(prefix, length, t1, t2, valid_lifetime,
+ RelayInfo(IOAddress("0.0.0.0"))), siaddr_(IOAddress("0.0.0.0")) {
if (!prefix.isV4()) {
isc_throw(BadValue, "Non IPv4 prefix " << prefix.toText()
<< " specified in subnet4");
@@ -331,7 +364,7 @@ Subnet6::Subnet6(const isc::asiolink::IOAddress& prefix, uint8_t length,
const Triplet<uint32_t>& t2,
const Triplet<uint32_t>& preferred_lifetime,
const Triplet<uint32_t>& valid_lifetime)
- :Subnet(prefix, length, t1, t2, valid_lifetime),
+:Subnet(prefix, length, t1, t2, valid_lifetime, RelayInfo(IOAddress("::"))),
preferred_(preferred_lifetime){
if (!prefix.isV6()) {
isc_throw(BadValue, "Non IPv6 prefix " << prefix
diff --git a/src/lib/dhcpsrv/subnet.h b/src/lib/dhcpsrv/subnet.h
index ecac6c3..ae90761 100644
--- a/src/lib/dhcpsrv/subnet.h
+++ b/src/lib/dhcpsrv/subnet.h
@@ -1,4 +1,4 @@
-// Copyright (C) 2012-2013 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2012-2014 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,6 +24,7 @@
#include <asiolink/io_address.h>
#include <dhcp/option.h>
+#include <dhcp/classify.h>
#include <dhcpsrv/key_from_key.h>
#include <dhcpsrv/option_space_container.h>
#include <dhcpsrv/pool.h>
@@ -154,7 +155,7 @@ public:
>
> OptionContainer;
- // Pointer to the OptionContainer object.
+ /// Pointer to the OptionContainer object.
typedef boost::shared_ptr<OptionContainer> OptionContainerPtr;
/// Type of the index #1 - option type.
typedef OptionContainer::nth_index<1>::type OptionContainerTypeIndex;
@@ -166,6 +167,26 @@ public:
/// Type of the index #2 - option persistency flag.
typedef OptionContainer::nth_index<2>::type OptionContainerPersistIndex;
+ /// @brief Holds optional information about relay.
+ ///
+ /// In some cases it is beneficial to have additional information about
+ /// a relay configured in the subnet. For now, the structure holds only
+ /// IP address, but there may potentially be additional parameters added
+ /// later, e.g. relay interface-id or relay-id.
+ struct RelayInfo {
+
+ /// @brief default and the only constructor
+ ///
+ /// @param addr an IP address of the relay (may be :: or 0.0.0.0)
+ RelayInfo(const isc::asiolink::IOAddress& addr);
+
+ /// @brief IP address of the relay
+ isc::asiolink::IOAddress addr_;
+ };
+
+ /// Pointer to the RelayInfo structure
+ typedef boost::shared_ptr<Subnet::RelayInfo> RelayInfoPtr;
+
/// @brief checks if specified address is in range
bool inRange(const isc::asiolink::IOAddress& addr) const;
@@ -375,6 +396,65 @@ public:
static_id_ = 1;
}
+ /// @brief Sets information about relay
+ ///
+ /// In some situations where there are shared subnets (i.e. two different
+ /// subnets are available on the same physical link), there is only one
+ /// relay that handles incoming requests from clients. In such a case,
+ /// the usual subnet selection criteria based on relay belonging to the
+ /// subnet being selected are no longer sufficient and we need to explicitly
+ /// specify a relay. One notable example of such uncommon, but valid
+ /// scenario is a cable network, where there is only one CMTS (one relay),
+ /// but there are 2 distinct subnets behind it: one for cable modems
+ /// and another one for CPEs and other user equipment behind modems.
+ /// From manageability perspective, it is essential that modems get addresses
+ /// from different subnet, so users won't tinker with their modems.
+ ///
+ /// Setting this parameter is not needed in most deployments.
+ /// This structure holds IP address only for now, but it is expected to
+ /// be extended in the future.
+ ///
+ /// @param relay structure that contains relay information
+ void setRelayInfo(const isc::dhcp::Subnet::RelayInfo& relay);
+
+
+ /// @brief Returns const reference to relay information
+ ///
+ /// @note The returned reference is only valid as long as the object
+ /// returned it is valid.
+ ///
+ /// @return const reference to the relay information
+ const isc::dhcp::Subnet::RelayInfo& getRelayInfo() {
+ return (relay_);
+ }
+
+ /// @brief checks whether this subnet supports client that belongs to
+ /// specified classes.
+ ///
+ /// This method checks whether a client that belongs to given classes can
+ /// use this subnet. For example, if this class is reserved for client
+ /// class "foo" and the client belongs to classes "foo", "bar" and "baz",
+ /// it is supported. On the other hand, client belonging to classes
+ /// "foobar" and "zyxxy" is not supported.
+ ///
+ /// @todo: Currently the logic is simple: client is supported if it belongs
+ /// to any class mentioned in white_list_. We will eventually need a
+ /// way to specify more fancy logic (e.g. to meet all classes, not just
+ /// any)
+ ///
+ /// @param client_classes list of all classes the client belongs to
+ /// @return true if client can be supported, false otherwise
+ bool
+ clientSupported(const isc::dhcp::ClientClasses& client_classes) const;
+
+ /// @brief adds class class_name to the list of supported classes
+ ///
+ /// Also see explanation note in @ref white_list_.
+ ///
+ /// @param class_name client class to be supported by this subnet
+ void
+ allowClientClass(const isc::dhcp::ClientClass& class_name);
+
protected:
/// @brief Returns all pools (non-const variant)
///
@@ -393,10 +473,18 @@ protected:
/// This subnet-id has unique value that is strictly monotonously increasing
/// for each subnet, until it is explicitly reset back to 1 during
/// reconfiguration process.
+ ///
+ /// @param prefix subnet prefix
+ /// @param len prefix length for the subnet
+ /// @param t1 T1 (renewal-time) timer, expressed in seconds
+ /// @param t2 T2 (rebind-time) timer, expressed in seconds
+ /// @param valid_lifetime valid lifetime of leases in this subnet (in seconds)
+ /// @param relay optional relay information (currently with address only)
Subnet(const isc::asiolink::IOAddress& prefix, uint8_t len,
const Triplet<uint32_t>& t1,
const Triplet<uint32_t>& t2,
- const Triplet<uint32_t>& valid_lifetime);
+ const Triplet<uint32_t>& valid_lifetime,
+ const isc::dhcp::Subnet::RelayInfo& relay);
/// @brief virtual destructor
///
@@ -406,8 +494,9 @@ protected:
/// @brief keeps the subnet-id value
///
- /// It is inreased every time a new Subnet object is created.
- /// It is reset (@ref resetSubnetId) every time reconfiguration occurs.
+ /// It is inreased every time a new Subnet object is created. It is reset
+ /// (@ref resetSubnetID) every time reconfiguration
+ /// occurs.
///
/// Static value initialized in subnet.cc.
static SubnetID static_id_;
@@ -493,6 +582,26 @@ protected:
/// @brief Name of the network interface (if connected directly)
std::string iface_;
+ /// @brief Relay information
+ ///
+ /// See @ref RelayInfo for detailed description. This structure is public,
+ /// so its fields are easily accessible. Making it protected would bring in
+ /// the issue of returning references that may become stale after its parent
+ /// subnet object disappears.
+ RelayInfo relay_;
+
+ /// @brief optional definition of a client class
+ ///
+ /// If defined, only clients belonging to that class will be allowed to use
+ /// this particular subnet. The default value for this is an empty list,
+ /// which means that any client is allowed, regardless of its class.
+ ///
+ /// @todo This is just a single list of allowed classes. We'll also need
+ /// to add a black-list (only classes on the list are rejected, the rest
+ /// are allowed). Implementing this will require more fancy parser logic,
+ /// so it may be a while until we support this.
+ ClientClasses white_list_;
+
private:
/// A collection of option spaces grouping option descriptors.
diff --git a/src/lib/dhcpsrv/tests/Makefile.am b/src/lib/dhcpsrv/tests/Makefile.am
index ff57254..037db46 100644
--- a/src/lib/dhcpsrv/tests/Makefile.am
+++ b/src/lib/dhcpsrv/tests/Makefile.am
@@ -33,7 +33,8 @@ if HAVE_GTEST
# to unexpected errors. For this reason, the --enable-static-link option is
# ignored for unit tests built here.
-lib_LTLIBRARIES = libco1.la libco2.la
+nodistdir=$(abs_top_builddir)/src/lib/dhcpsrv/tests
+nodist_LTLIBRARIES = libco1.la libco2.la
libco1_la_SOURCES = callout_library.cc
libco1_la_CXXFLAGS = $(AM_CXXFLAGS)
@@ -53,6 +54,7 @@ libdhcpsrv_unittests_SOURCES += alloc_engine_unittest.cc
libdhcpsrv_unittests_SOURCES += callout_handle_store_unittest.cc
libdhcpsrv_unittests_SOURCES += cfgmgr_unittest.cc
libdhcpsrv_unittests_SOURCES += d2_client_unittest.cc
+libdhcpsrv_unittests_SOURCES += d2_udp_unittest.cc
libdhcpsrv_unittests_SOURCES += dbaccess_parser_unittest.cc
libdhcpsrv_unittests_SOURCES += lease_unittest.cc
libdhcpsrv_unittests_SOURCES += lease_mgr_factory_unittest.cc
@@ -88,6 +90,7 @@ libdhcpsrv_unittests_CXXFLAGS += -Wno-unused-variable -Wno-unused-parameter
endif
libdhcpsrv_unittests_LDADD = $(top_builddir)/src/lib/dhcpsrv/libb10-dhcpsrv.la
+libdhcpsrv_unittests_LDADD += $(top_builddir)/src/lib/dhcp/tests/libdhcptest.la
libdhcpsrv_unittests_LDADD += $(top_builddir)/src/lib/dhcp/libb10-dhcp++.la
libdhcpsrv_unittests_LDADD += $(top_builddir)/src/lib/dhcp_ddns/libb10-dhcp_ddns.la
libdhcpsrv_unittests_LDADD += $(top_builddir)/src/lib/config/libb10-cfgclient.la
diff --git a/src/lib/dhcpsrv/tests/alloc_engine_unittest.cc b/src/lib/dhcpsrv/tests/alloc_engine_unittest.cc
index 8a37915..4330efd 100644
--- a/src/lib/dhcpsrv/tests/alloc_engine_unittest.cc
+++ b/src/lib/dhcpsrv/tests/alloc_engine_unittest.cc
@@ -94,22 +94,56 @@ public:
duid_ = DuidPtr(new DUID(vector<uint8_t>(8, 0x42)));
iaid_ = 42;
- // instantiate cfg_mgr
+ // Initialize a subnet and short address pool.
+ initSubnet(IOAddress("2001:db8:1::"),
+ IOAddress("2001:db8:1::10"),
+ IOAddress("2001:db8:1::20"));
+
+ initFqdn("", false, false);
+
+ factory_.create("type=memfile");
+ }
+
+ /// @brief Configures a subnet and adds one pool to it.
+ ///
+ /// This function removes existing v6 subnets before configuring
+ /// a new one.
+ ///
+ /// @param subnet Address of a subnet to be configured.
+ /// @param pool_start First address in the address pool.
+ /// @param pool_end Last address in the address pool.
+ void initSubnet(const IOAddress& subnet, const IOAddress& pool_start,
+ const IOAddress& pool_end) {
CfgMgr& cfg_mgr = CfgMgr::instance();
+ cfg_mgr.deleteSubnets6();
+
+ subnet_ = Subnet6Ptr(new Subnet6(subnet, 56, 1, 2, 3, 4));
+ pool_ = Pool6Ptr(new Pool6(Lease::TYPE_NA, pool_start, pool_end));
- // Configure normal address pool
- subnet_ = Subnet6Ptr(new Subnet6(IOAddress("2001:db8:1::"), 56, 1, 2, 3, 4));
- pool_ = Pool6Ptr(new Pool6(Lease::TYPE_NA, IOAddress("2001:db8:1::10"),
- IOAddress("2001:db8:1::20")));
subnet_->addPool(pool_);
- // Configure PD pool
- pd_pool_ = Pool6Ptr(new Pool6(Lease::TYPE_PD, IOAddress("2001:db8:1::"), 56, 64));
+ pd_pool_ = Pool6Ptr(new Pool6(Lease::TYPE_PD, subnet, 56, 64));
subnet_->addPool(pd_pool_);
cfg_mgr.addSubnet6(subnet_);
- factory_.create("type=memfile");
+ }
+
+ /// @brief Initializes FQDN data for a test.
+ ///
+ /// The initialized values are used by the test fixture class members to
+ /// verify the correctness of a lease.
+ ///
+ /// @param hostname Hostname to be assigned to a lease.
+ /// @param fqdn_fwd Indicates whether or not to perform forward DNS update
+ /// for a lease.
+ /// @param fqdn_fwd Indicates whether or not to perform reverse DNS update
+ /// for a lease.
+ void initFqdn(const std::string& hostname, const bool fqdn_fwd,
+ const bool fqdn_rev) {
+ hostname_ = hostname;
+ fqdn_fwd_ = fqdn_fwd;
+ fqdn_rev_ = fqdn_rev;
}
/// @brief attempts to convert leases collection to a single lease
@@ -151,8 +185,9 @@ public:
EXPECT_EQ(subnet_->getT1(), lease->t1_);
EXPECT_EQ(subnet_->getT2(), lease->t2_);
EXPECT_EQ(exp_pd_len, lease->prefixlen_);
- EXPECT_TRUE(false == lease->fqdn_fwd_);
- EXPECT_TRUE(false == lease->fqdn_rev_);
+ EXPECT_EQ(fqdn_fwd_, lease->fqdn_fwd_);
+ EXPECT_EQ(fqdn_rev_, lease->fqdn_rev_);
+ EXPECT_EQ(hostname_, lease->hostname_);
EXPECT_TRUE(*lease->duid_ == *duid_);
/// @todo: check cltt
}
@@ -211,7 +246,7 @@ public:
Lease6Ptr lease;
EXPECT_NO_THROW(lease = expectOneLease(engine->allocateLeases6(subnet_,
duid_, iaid_, hint, type, false, false,
- "", fake, CalloutHandlePtr())));
+ "", fake, CalloutHandlePtr(), old_leases_)));
// Check that we got a lease
EXPECT_TRUE(lease);
@@ -275,7 +310,7 @@ public:
Lease6Ptr lease;
EXPECT_NO_THROW(lease = expectOneLease(engine->allocateLeases6(subnet_,
duid_, iaid_, requested, type, false, false, "", false,
- CalloutHandlePtr())));
+ CalloutHandlePtr(), old_leases_)));
// Check that we got a lease
ASSERT_TRUE(lease);
@@ -319,7 +354,7 @@ public:
Lease6Ptr lease;
EXPECT_NO_THROW(lease = expectOneLease(engine->allocateLeases6(subnet_,
duid_, iaid_, hint, type, false,
- false, "", false, CalloutHandlePtr())));
+ false, "", false, CalloutHandlePtr(), old_leases_)));
// Check that we got a lease
ASSERT_TRUE(lease);
@@ -353,7 +388,14 @@ public:
Subnet6Ptr subnet_; ///< subnet6 (used in tests)
Pool6Ptr pool_; ///< NA pool belonging to subnet_
Pool6Ptr pd_pool_; ///< PD pool belonging to subnet_
+ std::string hostname_; ///< Hostname
+ bool fqdn_fwd_; ///< Perform forward update for a lease.
+ bool fqdn_rev_; ///< Perform reverse update for a lease.
LeaseMgrFactory factory_; ///< pointer to LeaseMgr factory
+
+ /// @brief Collection of leases being replaced by newly allocated or renewed
+ /// leases.
+ Lease6Collection old_leases_;
};
/// @brief Used in Allocation Engine tests for IPv4
@@ -521,13 +563,13 @@ TEST_F(AllocEngine6Test, allocateAddress6Nulls) {
Lease6Ptr lease;
EXPECT_NO_THROW(lease = expectOneLease(engine->allocateLeases6(
Subnet6Ptr(), duid_, iaid_, IOAddress("::"), Lease::TYPE_NA,
- false, false, "", false, CalloutHandlePtr())));
+ false, false, "", false, CalloutHandlePtr(), old_leases_)));
ASSERT_FALSE(lease);
// Allocations without DUID are not allowed either
EXPECT_NO_THROW(lease = expectOneLease(engine->allocateLeases6(subnet_,
DuidPtr(), iaid_, IOAddress("::"), Lease::TYPE_NA, false,
- false, "", false, CalloutHandlePtr())));
+ false, "", false, CalloutHandlePtr(), old_leases_)));
ASSERT_FALSE(lease);
}
@@ -765,19 +807,18 @@ TEST_F(AllocEngine6Test, smallPool6) {
ASSERT_TRUE(engine);
IOAddress addr("2001:db8:1::ad");
- CfgMgr& cfg_mgr = CfgMgr::instance();
- cfg_mgr.deleteSubnets6(); // Get rid of the default test configuration
- // Create configuration similar to other tests, but with a single address pool
- subnet_ = Subnet6Ptr(new Subnet6(IOAddress("2001:db8:1::"), 56, 1, 2, 3, 4));
- pool_ = Pool6Ptr(new Pool6(Lease::TYPE_NA, addr, addr)); // just a single address
- subnet_->addPool(pool_);
- cfg_mgr.addSubnet6(subnet_);
+ // Create a subnet with a pool that has one address.
+ initSubnet(IOAddress("2001:db8:1::"), addr, addr);
+
+ // Initialize FQDN for a lease.
+ initFqdn("myhost.example.com", true, true);
Lease6Ptr lease;
EXPECT_NO_THROW(lease = expectOneLease(engine->allocateLeases6(subnet_,
- duid_, iaid_, IOAddress("::"), Lease::TYPE_NA, false, false,
- "", false, CalloutHandlePtr())));
+ duid_, iaid_, IOAddress("::"), Lease::TYPE_NA, fqdn_fwd_,
+ fqdn_rev_, hostname_, false, CalloutHandlePtr(),
+ old_leases_)));
// Check that we got that single lease
ASSERT_TRUE(lease);
@@ -794,6 +835,11 @@ TEST_F(AllocEngine6Test, smallPool6) {
// Now check that the lease in LeaseMgr has the same parameters
detailCompareLease(lease, from_mgr);
+
+ // This is a new lease allocation. The old lease corresponding to a newly
+ // allocated lease should be NULL.
+ ASSERT_EQ(1, old_leases_.size());
+ EXPECT_FALSE(old_leases_[0]);
}
// This test checks if all addresses in a pool are currently used, the attempt
@@ -826,8 +872,9 @@ TEST_F(AllocEngine6Test, outOfAddresses6) {
Lease6Ptr lease2;
EXPECT_NO_THROW(lease2 = expectOneLease(engine->allocateLeases6(subnet_,
duid_, iaid_, IOAddress("::"), Lease::TYPE_NA, false, false,
- "", false, CalloutHandlePtr())));
+ "", false, CalloutHandlePtr(), old_leases_)));
EXPECT_FALSE(lease2);
+
}
// This test checks if an expired lease can be reused in SOLICIT (fake allocation)
@@ -837,14 +884,12 @@ TEST_F(AllocEngine6Test, solicitReuseExpiredLease6) {
ASSERT_TRUE(engine);
IOAddress addr("2001:db8:1::ad");
- CfgMgr& cfg_mgr = CfgMgr::instance();
- cfg_mgr.deleteSubnets6(); // Get rid of the default test configuration
- // Create configuration similar to other tests, but with a single address pool
- subnet_ = Subnet6Ptr(new Subnet6(IOAddress("2001:db8:1::"), 56, 1, 2, 3, 4));
- pool_ = Pool6Ptr(new Pool6(Lease::TYPE_NA, addr, addr)); // just a single address
- subnet_->addPool(pool_);
- cfg_mgr.addSubnet6(subnet_);
+ // Create one subnet with a pool holding one address.
+ initSubnet(IOAddress("2001:db8:1::"), addr, addr);
+
+ // Initialize FQDN data for the lease.
+ initFqdn("myhost.example.com", true, true);
// Just a different duid
DuidPtr other_duid = DuidPtr(new DUID(vector<uint8_t>(12, 0xff)));
@@ -860,8 +905,9 @@ TEST_F(AllocEngine6Test, solicitReuseExpiredLease6) {
// CASE 1: Asking for any address
EXPECT_NO_THROW(lease = expectOneLease(engine->allocateLeases6(subnet_,
- duid_, iaid_, IOAddress("::"), Lease::TYPE_NA, false, false, "", true,
- CalloutHandlePtr())));
+ duid_, iaid_, IOAddress("::"), Lease::TYPE_NA, fqdn_fwd_,
+ fqdn_rev_, hostname_, true, CalloutHandlePtr(),
+ old_leases_)));
// Check that we got that single lease
ASSERT_TRUE(lease);
EXPECT_EQ(addr, lease->addr_);
@@ -872,7 +918,7 @@ TEST_F(AllocEngine6Test, solicitReuseExpiredLease6) {
// CASE 2: Asking specifically for this address
EXPECT_NO_THROW(lease = expectOneLease(engine->allocateLeases6(subnet_,
duid_, iaid_, addr, Lease::TYPE_NA, false, false, "",
- true, CalloutHandlePtr())));
+ true, CalloutHandlePtr(), old_leases_)));
// Check that we got that single lease
ASSERT_TRUE(lease);
@@ -903,16 +949,32 @@ TEST_F(AllocEngine6Test, requestReuseExpiredLease6) {
501, 502, 503, 504, other_subnetid, 0));
lease->cltt_ = time(NULL) - 500; // Allocated 500 seconds ago
lease->valid_lft_ = 495; // Lease was valid for 495 seconds
+ lease->fqdn_fwd_ = true;
+ lease->fqdn_rev_ = true;
+ lease->hostname_ = "myhost.example.com.";
ASSERT_TRUE(LeaseMgrFactory::instance().addLease(lease));
// A client comes along, asking specifically for this address
EXPECT_NO_THROW(lease = expectOneLease(engine->allocateLeases6(subnet_,
duid_, iaid_, addr, Lease::TYPE_NA, false, false, "",
- false, CalloutHandlePtr())));
+ false, CalloutHandlePtr(), old_leases_)));
// Check that he got that single lease
ASSERT_TRUE(lease);
EXPECT_EQ(addr, lease->addr_);
+ // This reactivated lease should have updated FQDN data.
+ EXPECT_TRUE(lease->hostname_.empty());
+ EXPECT_FALSE(lease->fqdn_fwd_);
+ EXPECT_FALSE(lease->fqdn_rev_);
+
+ // Check that the old lease has been returned.
+ Lease6Ptr old_lease = expectOneLease(old_leases_);
+ // It should at least have the same IPv6 address.
+ EXPECT_EQ(lease->addr_, old_lease->addr_);
+ // Check that it carries not updated FQDN data.
+ EXPECT_EQ("myhost.example.com.", old_lease->hostname_);
+ EXPECT_TRUE(old_lease->fqdn_fwd_);
+ EXPECT_TRUE(old_lease->fqdn_rev_);
// Check that the lease is indeed updated in LeaseMgr
Lease6Ptr from_mgr = LeaseMgrFactory::instance().getLease6(Lease::TYPE_NA,
@@ -1623,7 +1685,7 @@ TEST_F(HookAllocEngine6Test, lease6_select) {
Lease6Ptr lease;
EXPECT_NO_THROW(lease = expectOneLease(engine->allocateLeases6(subnet_,
duid_, iaid_, IOAddress("::"), Lease::TYPE_NA, false, false,
- "", false, callout_handle)));
+ "", false, callout_handle, old_leases_)));
// Check that we got a lease
ASSERT_TRUE(lease);
@@ -1694,7 +1756,7 @@ TEST_F(HookAllocEngine6Test, change_lease6_select) {
Lease6Ptr lease;
EXPECT_NO_THROW(lease = expectOneLease(engine->allocateLeases6(subnet_,
duid_, iaid_, IOAddress("::"), Lease::TYPE_NA, false, false,
- "", false, callout_handle)));
+ "", false, callout_handle, old_leases_)));
// Check that we got a lease
ASSERT_TRUE(lease);
diff --git a/src/lib/dhcpsrv/tests/cfgmgr_unittest.cc b/src/lib/dhcpsrv/tests/cfgmgr_unittest.cc
index 8c3d2ea..ee54c3d 100644
--- a/src/lib/dhcpsrv/tests/cfgmgr_unittest.cc
+++ b/src/lib/dhcpsrv/tests/cfgmgr_unittest.cc
@@ -18,6 +18,7 @@
#include <dhcpsrv/dhcp_parsers.h>
#include <exceptions/exceptions.h>
#include <dhcp/dhcp6.h>
+#include <dhcp/tests/iface_mgr_test_config.h>
#include <gtest/gtest.h>
@@ -29,6 +30,7 @@
using namespace std;
using namespace isc::asiolink;
using namespace isc::dhcp;
+using namespace isc::dhcp::test;
using namespace isc::util;
using namespace isc;
@@ -184,6 +186,9 @@ public:
CfgMgr::instance().deleteSubnets6();
CfgMgr::instance().deleteOptionDefs();
}
+
+ /// used in client classification (or just empty container for other tests)
+ isc::dhcp::ClientClasses classify_;
};
// This test verifies that multiple option definitions can be added
@@ -325,18 +330,46 @@ TEST_F(CfgMgrTest, getOptionDef) {
}
+// This test verifies that it is not allowed to override a definition of the
+// standard option which has its definition defined in libdhcp++, but it is
+// allowed to create a definition for the standard option which doesn't have
+// its definition in libdhcp++.
+TEST_F(CfgMgrTest, overrideStdOptionDef) {
+ CfgMgr& cfg_mgr = CfgMgr::instance();
+
+ OptionDefinitionPtr def;
+ // There is a definition for routers option in libdhcp++, so an attempt
+ // to add (override) another definition for this option should fail.
+ def.reset(new OptionDefinition("routers", DHO_ROUTERS, "uint32"));
+ EXPECT_THROW(cfg_mgr.addOptionDef(def, "dhcp4"), isc::BadValue);
+
+ /// @todo There is no definition for the NIS Server Addr option in
+ /// libdhcp++. Once it is implemented it should be not allowed to
+ /// add a custom definition for it. At the moment, it should be ok
+ /// to add a definition for this option (using configuration mechanism)
+ /// because we haven't implemented the one in libdhcp++.
+ def.reset(new OptionDefinition("nis-server-addr", 65, "uint16"));
+ EXPECT_NO_THROW(cfg_mgr.addOptionDef(def, "dhcp4"));
+
+ // It is not allowed to override the definition of the option which
+ // has its definition in the libdhcp++.
+ def.reset(new OptionDefinition("sntp-servers", D6O_SNTP_SERVERS,
+ "ipv4-address"));
+ EXPECT_THROW(cfg_mgr.addOptionDef(def, "dhcp6"), isc::BadValue);
+ // There is no definition for option 59 in libdhcp++ yet, so it should
+ // be possible provide a custom definition.
+ def.reset(new OptionDefinition("bootfile-url", 59, "uint32"));
+ EXPECT_NO_THROW(cfg_mgr.addOptionDef(def, "dhcp6"));
+
+}
+
// This test verifies that the function that adds new option definition
// throws exceptions when arguments are invalid.
TEST_F(CfgMgrTest, addOptionDefNegative) {
CfgMgr& cfg_mgr = CfgMgr::instance();
- // The option code 65 is reserved for standard options either in
- // DHCPv4 or DHCPv6. Thus we expect that adding an option to this
- // option space fails.
- OptionDefinitionPtr def(new OptionDefinition("option-foo", 65, "uint16"));
-
- // Try reserved option space names.
- ASSERT_THROW(cfg_mgr.addOptionDef(def, "dhcp4"), isc::BadValue);
- ASSERT_THROW(cfg_mgr.addOptionDef(def, "dhcp6"), isc::BadValue);
+
+ OptionDefinitionPtr def(new OptionDefinition("option-foo", 1000, "uint16"));
+
// Try empty option space name.
ASSERT_THROW(cfg_mgr.addOptionDef(def, ""), isc::BadValue);
// Try NULL option definition.
@@ -349,7 +382,7 @@ TEST_F(CfgMgrTest, addOptionDefNegative) {
}
// This test verifies if the configuration manager is able to hold and return
-// valid leases
+// valid subnets.
TEST_F(CfgMgrTest, subnet4) {
CfgMgr& cfg_mgr = CfgMgr::instance();
@@ -358,30 +391,222 @@ TEST_F(CfgMgrTest, subnet4) {
Subnet4Ptr subnet3(new Subnet4(IOAddress("192.0.2.128"), 26, 1, 2, 3));
// There shouldn't be any subnet configured at this stage
- EXPECT_FALSE(cfg_mgr.getSubnet4(IOAddress("192.0.2.0")));
+ EXPECT_FALSE(cfg_mgr.getSubnet4(IOAddress("192.0.2.0"), classify_));
cfg_mgr.addSubnet4(subnet1);
// Now we have only one subnet, any request will be served from it
- EXPECT_EQ(subnet1, cfg_mgr.getSubnet4(IOAddress("192.0.2.63")));
+ EXPECT_EQ(subnet1, cfg_mgr.getSubnet4(IOAddress("192.0.2.63"),
+ classify_));
// Now we add more subnets and check that both old and new subnets
// are accessible.
cfg_mgr.addSubnet4(subnet2);
cfg_mgr.addSubnet4(subnet3);
- EXPECT_EQ(subnet3, cfg_mgr.getSubnet4(IOAddress("192.0.2.191")));
- EXPECT_EQ(subnet1, cfg_mgr.getSubnet4(IOAddress("192.0.2.15")));
- EXPECT_EQ(subnet2, cfg_mgr.getSubnet4(IOAddress("192.0.2.85")));
+ EXPECT_EQ(subnet3, cfg_mgr.getSubnet4(IOAddress("192.0.2.191"), classify_));
+ EXPECT_EQ(subnet1, cfg_mgr.getSubnet4(IOAddress("192.0.2.15"), classify_));
+ EXPECT_EQ(subnet2, cfg_mgr.getSubnet4(IOAddress("192.0.2.85"), classify_));
// Try to find an address that does not belong to any subnet
- EXPECT_FALSE(cfg_mgr.getSubnet4(IOAddress("192.0.2.192")));
+ EXPECT_FALSE(cfg_mgr.getSubnet4(IOAddress("192.0.2.192"), classify_));
// Check that deletion of the subnets works.
cfg_mgr.deleteSubnets4();
- EXPECT_FALSE(cfg_mgr.getSubnet4(IOAddress("192.0.2.191")));
- EXPECT_FALSE(cfg_mgr.getSubnet4(IOAddress("192.0.2.15")));
- EXPECT_FALSE(cfg_mgr.getSubnet4(IOAddress("192.0.2.85")));
+ EXPECT_FALSE(cfg_mgr.getSubnet4(IOAddress("192.0.2.191"), classify_));
+ EXPECT_FALSE(cfg_mgr.getSubnet4(IOAddress("192.0.2.15"), classify_));
+ EXPECT_FALSE(cfg_mgr.getSubnet4(IOAddress("192.0.2.85"), classify_));
+}
+
+// This test verifies if the configuration manager is able to hold subnets with
+// their classifier information and return proper subnets, based on those
+// classes.
+TEST_F(CfgMgrTest, classifySubnet4) {
+ CfgMgr& cfg_mgr = CfgMgr::instance();
+
+ // Let's configure 3 subnets
+ Subnet4Ptr subnet1(new Subnet4(IOAddress("192.0.2.0"), 26, 1, 2, 3));
+ Subnet4Ptr subnet2(new Subnet4(IOAddress("192.0.2.64"), 26, 1, 2, 3));
+ Subnet4Ptr subnet3(new Subnet4(IOAddress("192.0.2.128"), 26, 1, 2, 3));
+
+ cfg_mgr.addSubnet4(subnet1);
+ cfg_mgr.addSubnet4(subnet2);
+ cfg_mgr.addSubnet4(subnet3);
+
+ // Let's sanity check that we can use that configuration.
+ EXPECT_EQ(subnet1, cfg_mgr.getSubnet4(IOAddress("192.0.2.5"), classify_));
+ EXPECT_EQ(subnet2, cfg_mgr.getSubnet4(IOAddress("192.0.2.70"), classify_));
+ EXPECT_EQ(subnet3, cfg_mgr.getSubnet4(IOAddress("192.0.2.130"), classify_));
+
+ // Client now belongs to bar class.
+ classify_.insert("bar");
+
+ // There are no class restrictions defined, so everything should work
+ // as before
+ EXPECT_EQ(subnet1, cfg_mgr.getSubnet4(IOAddress("192.0.2.5"), classify_));
+ EXPECT_EQ(subnet2, cfg_mgr.getSubnet4(IOAddress("192.0.2.70"), classify_));
+ EXPECT_EQ(subnet3, cfg_mgr.getSubnet4(IOAddress("192.0.2.130"), classify_));
+
+ // Now let's add client class restrictions.
+ subnet1->allowClientClass("foo"); // Serve here only clients from foo class
+ subnet2->allowClientClass("bar"); // Serve here only clients from bar class
+ subnet3->allowClientClass("baz"); // Serve here only clients from baz class
+
+ // The same check as above should result in client being served only in
+ // bar class, i.e. subnet2
+ EXPECT_FALSE(cfg_mgr.getSubnet4(IOAddress("192.0.2.5"), classify_));
+ EXPECT_EQ(subnet2, cfg_mgr.getSubnet4(IOAddress("192.0.2.70"), classify_));
+ EXPECT_FALSE(cfg_mgr.getSubnet4(IOAddress("192.0.2.130"), classify_));
+
+ // Now let's check that client with wrong class is not supported
+ classify_.clear();
+ classify_.insert("some_other_class");
+ EXPECT_FALSE(cfg_mgr.getSubnet4(IOAddress("192.0.2.5"), classify_));
+ EXPECT_FALSE(cfg_mgr.getSubnet4(IOAddress("192.0.2.70"), classify_));
+ EXPECT_FALSE(cfg_mgr.getSubnet4(IOAddress("192.0.2.130"), classify_));
+
+ // Finally, let's check that client without any classes is not supported
+ classify_.clear();
+ EXPECT_FALSE(cfg_mgr.getSubnet4(IOAddress("192.0.2.5"), classify_));
+ EXPECT_FALSE(cfg_mgr.getSubnet4(IOAddress("192.0.2.70"), classify_));
+ EXPECT_FALSE(cfg_mgr.getSubnet4(IOAddress("192.0.2.130"), classify_));
+}
+
+// This test verifies if the configuration manager is able to hold and return
+// valid leases
+TEST_F(CfgMgrTest, classifySubnet6) {
+ CfgMgr& cfg_mgr = CfgMgr::instance();
+
+ // Let's configure 3 subnets
+ Subnet6Ptr subnet1(new Subnet6(IOAddress("2000::"), 48, 1, 2, 3, 4));
+ Subnet6Ptr subnet2(new Subnet6(IOAddress("3000::"), 48, 1, 2, 3, 4));
+ Subnet6Ptr subnet3(new Subnet6(IOAddress("4000::"), 48, 1, 2, 3, 4));
+
+ cfg_mgr.addSubnet6(subnet1);
+ cfg_mgr.addSubnet6(subnet2);
+ cfg_mgr.addSubnet6(subnet3);
+
+ // Let's sanity check that we can use that configuration.
+ EXPECT_EQ(subnet1, cfg_mgr.getSubnet6(IOAddress("2000::123"), classify_));
+ EXPECT_EQ(subnet2, cfg_mgr.getSubnet6(IOAddress("3000::345"), classify_));
+ EXPECT_EQ(subnet3, cfg_mgr.getSubnet6(IOAddress("4000::567"), classify_));
+
+ // Client now belongs to bar class.
+ classify_.insert("bar");
+
+ // There are no class restrictions defined, so everything should work
+ // as before
+ EXPECT_EQ(subnet1, cfg_mgr.getSubnet6(IOAddress("2000::123"), classify_));
+ EXPECT_EQ(subnet2, cfg_mgr.getSubnet6(IOAddress("3000::345"), classify_));
+ EXPECT_EQ(subnet3, cfg_mgr.getSubnet6(IOAddress("4000::567"), classify_));
+
+ // Now let's add client class restrictions.
+ subnet1->allowClientClass("foo"); // Serve here only clients from foo class
+ subnet2->allowClientClass("bar"); // Serve here only clients from bar class
+ subnet3->allowClientClass("baz"); // Serve here only clients from baz class
+
+ // The same check as above should result in client being served only in
+ // bar class, i.e. subnet2
+ EXPECT_FALSE(cfg_mgr.getSubnet6(IOAddress("2000::123"), classify_));
+ EXPECT_EQ(subnet2, cfg_mgr.getSubnet6(IOAddress("3000::345"), classify_));
+ EXPECT_FALSE(cfg_mgr.getSubnet6(IOAddress("4000::567"), classify_));
+
+ // Now let's check that client with wrong class is not supported
+ classify_.clear();
+ classify_.insert("some_other_class");
+ EXPECT_FALSE(cfg_mgr.getSubnet6(IOAddress("2000::123"), classify_));
+ EXPECT_FALSE(cfg_mgr.getSubnet6(IOAddress("3000::345"), classify_));
+ EXPECT_FALSE(cfg_mgr.getSubnet6(IOAddress("4000::567"), classify_));
+
+ // Finally, let's check that client without any classes is not supported
+ classify_.clear();
+ EXPECT_FALSE(cfg_mgr.getSubnet6(IOAddress("2000::123"), classify_));
+ EXPECT_FALSE(cfg_mgr.getSubnet6(IOAddress("3000::345"), classify_));
+ EXPECT_FALSE(cfg_mgr.getSubnet6(IOAddress("4000::567"), classify_));
+}
+
+// This test verifies if the configuration manager is able to hold, select
+// and return valid subnets, based on interface names along with client
+// classification.
+TEST_F(CfgMgrTest, classifySubnet6Interface) {
+ CfgMgr& cfg_mgr = CfgMgr::instance();
+
+ // Let's have an odd configuration: 3 shared subnets available on the
+ // same direct link.
+ Subnet6Ptr subnet1(new Subnet6(IOAddress("2000::"), 48, 1, 2, 3, 4));
+ Subnet6Ptr subnet2(new Subnet6(IOAddress("3000::"), 48, 1, 2, 3, 4));
+ Subnet6Ptr subnet3(new Subnet6(IOAddress("4000::"), 48, 1, 2, 3, 4));
+ subnet1->setIface("foo");
+ subnet2->setIface("foo");
+ subnet3->setIface("foo");
+ cfg_mgr.addSubnet6(subnet1);
+ cfg_mgr.addSubnet6(subnet2);
+ cfg_mgr.addSubnet6(subnet3);
+
+
+ // Regular client should get the first subnet, because it meets all
+ // criteria (matching interface name, no class restrictions.
+ EXPECT_EQ(subnet1, cfg_mgr.getSubnet6("foo", classify_));
+
+ // Now let's add class requirements for subnet1
+ subnet1->allowClientClass("alpha");
+
+ // Client should now get the subnet2, because he no longer meets
+ // requirements for subnet1 (belongs to wrong class)
+ EXPECT_EQ(subnet2, cfg_mgr.getSubnet6("foo", classify_));
+
+ // Now let's add (not matching) classes to the other two subnets
+ subnet2->allowClientClass("beta");
+ subnet3->allowClientClass("gamma");
+
+ // No subnets are suitable, so nothing will be selected
+ EXPECT_FALSE(cfg_mgr.getSubnet6("foo", classify_));
+
+ // Ok, let's add the client to gamme class, so he'll get a subnet
+ classify_.insert("gamma");
+ EXPECT_EQ(subnet3, cfg_mgr.getSubnet6("foo", classify_));
+}
+
+// This test verifies if the configuration manager is able to hold, select
+// and return valid subnets, based on interface-id option inserted by relay,
+// along with client classification.
+TEST_F(CfgMgrTest, classifySubnet6InterfaceId) {
+ CfgMgr& cfg_mgr = CfgMgr::instance();
+
+ // Let's have an odd configuration: 3 shared subnets available via the
+ // same remote relay with the same interface-id.
+ Subnet6Ptr subnet1(new Subnet6(IOAddress("2000::"), 48, 1, 2, 3, 4));
+ Subnet6Ptr subnet2(new Subnet6(IOAddress("3000::"), 48, 1, 2, 3, 4));
+ Subnet6Ptr subnet3(new Subnet6(IOAddress("4000::"), 48, 1, 2, 3, 4));
+ OptionPtr ifaceid = generateInterfaceId("relay1.eth0");
+ subnet1->setInterfaceId(ifaceid);
+ subnet2->setInterfaceId(ifaceid);
+ subnet3->setInterfaceId(ifaceid);
+ cfg_mgr.addSubnet6(subnet1);
+ cfg_mgr.addSubnet6(subnet2);
+ cfg_mgr.addSubnet6(subnet3);
+
+ // Regular client should get the first subnet, because it meets all
+ // criteria (matching interface name, no class restrictions.
+ EXPECT_EQ(subnet1, cfg_mgr.getSubnet6(ifaceid, classify_));
+
+ // Now let's add class requirements for subnet1
+ subnet1->allowClientClass("alpha");
+
+ // Client should now get the subnet2, because he no longer meets
+ // requirements for subnet1 (belongs to wrong class)
+ EXPECT_EQ(subnet2, cfg_mgr.getSubnet6(ifaceid, classify_));
+
+ // Now let's add (not matching) classes to the other two subnets
+ subnet2->allowClientClass("beta");
+ subnet3->allowClientClass("gamma");
+
+ // No subnets are suitable, so nothing will be selected
+ EXPECT_FALSE(cfg_mgr.getSubnet6(ifaceid, classify_));
+
+ // Ok, let's add the client to gamme class, so he'll get a subnet
+ classify_.insert("gamma");
+ EXPECT_EQ(subnet3, cfg_mgr.getSubnet6(ifaceid, classify_));
}
// This test verifies if the configuration manager is able to hold and return
@@ -394,29 +619,31 @@ TEST_F(CfgMgrTest, subnet6) {
Subnet6Ptr subnet3(new Subnet6(IOAddress("4000::"), 48, 1, 2, 3, 4));
// There shouldn't be any subnet configured at this stage
- EXPECT_FALSE(cfg_mgr.getSubnet6(IOAddress("2000::1")));
+ EXPECT_FALSE(cfg_mgr.getSubnet6(IOAddress("2000::1"), classify_));
cfg_mgr.addSubnet6(subnet1);
// Now we have only one subnet, any request will be served from it
- EXPECT_EQ(subnet1, cfg_mgr.getSubnet6(IOAddress("2000::1")));
+ EXPECT_EQ(subnet1, cfg_mgr.getSubnet6(IOAddress("2000::1"), classify_));
// If we have only a single subnet and the request came from a local
// address, let's use that subnet
- EXPECT_EQ(subnet1, cfg_mgr.getSubnet6(IOAddress("fe80::dead:beef")));
+ EXPECT_EQ(subnet1, cfg_mgr.getSubnet6(IOAddress("fe80::dead:beef"),
+ classify_));
cfg_mgr.addSubnet6(subnet2);
cfg_mgr.addSubnet6(subnet3);
- EXPECT_EQ(subnet3, cfg_mgr.getSubnet6(IOAddress("4000::123")));
- EXPECT_EQ(subnet2, cfg_mgr.getSubnet6(IOAddress("3000::dead:beef")));
- EXPECT_FALSE(cfg_mgr.getSubnet6(IOAddress("5000::1")));
+ EXPECT_EQ(subnet3, cfg_mgr.getSubnet6(IOAddress("4000::123"), classify_));
+ EXPECT_EQ(subnet2, cfg_mgr.getSubnet6(IOAddress("3000::dead:beef"),
+ classify_));
+ EXPECT_FALSE(cfg_mgr.getSubnet6(IOAddress("5000::1"), classify_));
// Check that deletion of the subnets works.
cfg_mgr.deleteSubnets6();
- EXPECT_FALSE(cfg_mgr.getSubnet6(IOAddress("200::123")));
- EXPECT_FALSE(cfg_mgr.getSubnet6(IOAddress("3000::123")));
- EXPECT_FALSE(cfg_mgr.getSubnet6(IOAddress("4000::123")));
+ EXPECT_FALSE(cfg_mgr.getSubnet6(IOAddress("2000::123"), classify_));
+ EXPECT_FALSE(cfg_mgr.getSubnet6(IOAddress("3000::123"), classify_));
+ EXPECT_FALSE(cfg_mgr.getSubnet6(IOAddress("4000::123"), classify_));
}
// This test verifies if the configuration manager is able to hold, select
@@ -432,33 +659,34 @@ TEST_F(CfgMgrTest, subnet6Interface) {
subnet3->setIface("foobar");
// There shouldn't be any subnet configured at this stage
- EXPECT_FALSE(cfg_mgr.getSubnet6("foo"));
+ EXPECT_FALSE(cfg_mgr.getSubnet6("foo", classify_));
cfg_mgr.addSubnet6(subnet1);
// Now we have only one subnet, any request will be served from it
- EXPECT_EQ(subnet1, cfg_mgr.getSubnet6("foo"));
+ EXPECT_EQ(subnet1, cfg_mgr.getSubnet6("foo", classify_));
// Check that the interface name is checked even when there is
// only one subnet defined.
- EXPECT_FALSE(cfg_mgr.getSubnet6("bar"));
+ EXPECT_FALSE(cfg_mgr.getSubnet6("bar", classify_));
// If we have only a single subnet and the request came from a local
// address, let's use that subnet
- EXPECT_EQ(subnet1, cfg_mgr.getSubnet6(IOAddress("fe80::dead:beef")));
+ EXPECT_EQ(subnet1, cfg_mgr.getSubnet6(IOAddress("fe80::dead:beef"),
+ classify_));
cfg_mgr.addSubnet6(subnet2);
cfg_mgr.addSubnet6(subnet3);
- EXPECT_EQ(subnet3, cfg_mgr.getSubnet6("foobar"));
- EXPECT_EQ(subnet2, cfg_mgr.getSubnet6("bar"));
- EXPECT_FALSE(cfg_mgr.getSubnet6("xyzzy")); // no such interface
+ EXPECT_EQ(subnet3, cfg_mgr.getSubnet6("foobar", classify_));
+ EXPECT_EQ(subnet2, cfg_mgr.getSubnet6("bar", classify_));
+ EXPECT_FALSE(cfg_mgr.getSubnet6("xyzzy", classify_)); // no such interface
// Check that deletion of the subnets works.
cfg_mgr.deleteSubnets6();
- EXPECT_FALSE(cfg_mgr.getSubnet6("foo"));
- EXPECT_FALSE(cfg_mgr.getSubnet6("bar"));
- EXPECT_FALSE(cfg_mgr.getSubnet6("foobar"));
+ EXPECT_FALSE(cfg_mgr.getSubnet6("foo", classify_));
+ EXPECT_FALSE(cfg_mgr.getSubnet6("bar", classify_));
+ EXPECT_FALSE(cfg_mgr.getSubnet6("foobar", classify_));
}
// This test verifies if the configuration manager is able to hold, select
@@ -484,27 +712,27 @@ TEST_F(CfgMgrTest, subnet6InterfaceId) {
subnet3->setInterfaceId(ifaceid3);
// There shouldn't be any subnet configured at this stage
- EXPECT_FALSE(cfg_mgr.getSubnet6(ifaceid1));
+ EXPECT_FALSE(cfg_mgr.getSubnet6(ifaceid1, classify_));
cfg_mgr.addSubnet6(subnet1);
// If we have only a single subnet and the request came from a local
// address, let's use that subnet
- EXPECT_EQ(subnet1, cfg_mgr.getSubnet6(ifaceid1));
- EXPECT_FALSE(cfg_mgr.getSubnet6(ifaceid2));
+ EXPECT_EQ(subnet1, cfg_mgr.getSubnet6(ifaceid1, classify_));
+ EXPECT_FALSE(cfg_mgr.getSubnet6(ifaceid2, classify_));
cfg_mgr.addSubnet6(subnet2);
cfg_mgr.addSubnet6(subnet3);
- EXPECT_EQ(subnet3, cfg_mgr.getSubnet6(ifaceid3));
- EXPECT_EQ(subnet2, cfg_mgr.getSubnet6(ifaceid2));
- EXPECT_FALSE(cfg_mgr.getSubnet6(ifaceid_bogus));
+ EXPECT_EQ(subnet3, cfg_mgr.getSubnet6(ifaceid3, classify_));
+ EXPECT_EQ(subnet2, cfg_mgr.getSubnet6(ifaceid2, classify_));
+ EXPECT_FALSE(cfg_mgr.getSubnet6(ifaceid_bogus, classify_));
// Check that deletion of the subnets works.
cfg_mgr.deleteSubnets6();
- EXPECT_FALSE(cfg_mgr.getSubnet6(ifaceid1));
- EXPECT_FALSE(cfg_mgr.getSubnet6(ifaceid2));
- EXPECT_FALSE(cfg_mgr.getSubnet6(ifaceid3));
+ EXPECT_FALSE(cfg_mgr.getSubnet6(ifaceid1, classify_));
+ EXPECT_FALSE(cfg_mgr.getSubnet6(ifaceid2, classify_));
+ EXPECT_FALSE(cfg_mgr.getSubnet6(ifaceid3, classify_));
}
@@ -672,15 +900,17 @@ TEST_F(CfgMgrTest, echoClientId) {
// This test checks the D2ClientMgr wrapper methods.
TEST_F(CfgMgrTest, d2ClientConfig) {
- // After CfgMgr construction, D2 configuration should be disabled.
- // Fetch it and verify this is the case.
+ // After CfgMgr construction, D2ClientMgr member should be initialized
+ // with a D2 configuration that is disabled.
+ // Verify we can Fetch the mgr.
+ D2ClientMgr& d2_mgr = CfgMgr::instance().getD2ClientMgr();
+ EXPECT_FALSE(d2_mgr.ddnsEnabled());
+
+ // Make sure the convenience method fetches the config correctly.
D2ClientConfigPtr original_config = CfgMgr::instance().getD2ClientConfig();
ASSERT_TRUE(original_config);
EXPECT_FALSE(original_config->getEnableUpdates());
- // Make sure convenience method agrees.
- EXPECT_FALSE(CfgMgr::instance().ddnsEnabled());
-
// Verify that we cannot set the configuration to an empty pointer.
D2ClientConfigPtr new_cfg;
ASSERT_THROW(CfgMgr::instance().setD2ClientConfig(new_cfg), D2ClientError);
@@ -689,7 +919,7 @@ TEST_F(CfgMgrTest, d2ClientConfig) {
ASSERT_NO_THROW(new_cfg.reset(new D2ClientConfig(true,
isc::asiolink::IOAddress("127.0.0.1"), 477,
dhcp_ddns::NCR_UDP, dhcp_ddns::FMT_JSON,
- true, true, true, true, true,
+ true, true, true, true,
"pre-fix", "suf-fix")));
// Verify that we can assign a new, non-empty configuration.
@@ -709,6 +939,47 @@ TEST_F(CfgMgrTest, d2ClientConfig) {
EXPECT_NE(*original_config, *updated_config);
}
+// This test verfies that CfgMgr correctly determines that the address of the
+// interface belongs to existing IPv4 subnet.
+TEST_F(CfgMgrTest, getSubnet4ForInterface) {
+ IfaceMgrTestConfig config(true);
+
+ // Initially, there are no subnets configured, so none of the IPv4
+ // addresses assigned to eth0 and eth1 can match with any subnet.
+ EXPECT_FALSE(CfgMgr::instance().getSubnet4("eth0", classify_));
+ EXPECT_FALSE(CfgMgr::instance().getSubnet4("eth1", classify_));
+
+ // Configure first subnet which address on eth0 corresponds to.
+ Subnet4Ptr subnet1(new Subnet4(IOAddress("10.0.0.1"), 24, 1, 2, 3));
+ CfgMgr::instance().addSubnet4(subnet1);
+
+ // The address on eth0 should match the existing subnet.
+ Subnet4Ptr subnet1_ret;
+ subnet1_ret = CfgMgr::instance().getSubnet4("eth0", classify_);
+ ASSERT_TRUE(subnet1_ret);
+ EXPECT_EQ(subnet1->get().first, subnet1_ret->get().first);
+ // There should still be no match for eth1.
+ EXPECT_FALSE(CfgMgr::instance().getSubnet4("eth1", classify_));
+
+ // Configure a second subnet.
+ Subnet4Ptr subnet2(new Subnet4(IOAddress("192.0.2.1"), 24, 1, 2, 3));
+ CfgMgr::instance().addSubnet4(subnet2);
+
+ // There should be match between eth0 and subnet1 and between eth1 and
+ // subnet 2.
+ subnet1_ret = CfgMgr::instance().getSubnet4("eth0", classify_);
+ ASSERT_TRUE(subnet1_ret);
+ EXPECT_EQ(subnet1->get().first, subnet1_ret->get().first);
+ Subnet4Ptr subnet2_ret = CfgMgr::instance().getSubnet4("eth1", classify_);
+ ASSERT_TRUE(subnet2_ret);
+ EXPECT_EQ(subnet2->get().first, subnet2_ret->get().first);
+
+ // This function throws an exception if the name of the interface is wrong.
+ EXPECT_THROW(CfgMgr::instance().getSubnet4("bogus-interface", classify_),
+ isc::BadValue);
+
+}
+
/// @todo Add unit-tests for testing:
/// - addActiveIface() with invalid interface name
diff --git a/src/lib/dhcpsrv/tests/d2_client_unittest.cc b/src/lib/dhcpsrv/tests/d2_client_unittest.cc
index bd8f061..b935a0b 100644
--- a/src/lib/dhcpsrv/tests/d2_client_unittest.cc
+++ b/src/lib/dhcpsrv/tests/d2_client_unittest.cc
@@ -13,7 +13,9 @@
// PERFORMANCE OF THIS SOFTWARE.
#include <config.h>
-#include <dhcpsrv/d2_client.h>
+#include <dhcp/option4_client_fqdn.h>
+#include <dhcp/option6_client_fqdn.h>
+#include <dhcpsrv/d2_client_mgr.h>
#include <exceptions/exceptions.h>
#include <gtest/gtest.h>
@@ -34,6 +36,12 @@ TEST(D2ClientConfigTest, constructorsAndAccessors) {
ASSERT_NO_THROW(d2_client_config.reset(new D2ClientConfig()));
EXPECT_FALSE(d2_client_config->getEnableUpdates());
+ // Verify the enable-updates can be toggled.
+ d2_client_config->enableUpdates(true);
+ EXPECT_TRUE(d2_client_config->getEnableUpdates());
+ d2_client_config->enableUpdates(false);
+ EXPECT_FALSE(d2_client_config->getEnableUpdates());
+
d2_client_config.reset();
bool enable_updates = true;
@@ -41,7 +49,6 @@ TEST(D2ClientConfigTest, constructorsAndAccessors) {
size_t server_port = 477;
dhcp_ddns::NameChangeProtocol ncr_protocol = dhcp_ddns::NCR_UDP;
dhcp_ddns::NameChangeFormat ncr_format = dhcp_ddns::FMT_JSON;
- bool remove_on_renew = true;
bool always_include_fqdn = true;
bool override_no_update = true;
bool override_client_update = true;
@@ -56,7 +63,6 @@ TEST(D2ClientConfigTest, constructorsAndAccessors) {
server_port,
ncr_protocol,
ncr_format,
- remove_on_renew,
always_include_fqdn,
override_no_update,
override_client_update,
@@ -73,7 +79,6 @@ TEST(D2ClientConfigTest, constructorsAndAccessors) {
EXPECT_EQ(d2_client_config->getServerPort(), server_port);
EXPECT_EQ(d2_client_config->getNcrProtocol(), ncr_protocol);
EXPECT_EQ(d2_client_config->getNcrFormat(), ncr_format);
- EXPECT_EQ(d2_client_config->getRemoveOnRenew(), remove_on_renew);
EXPECT_EQ(d2_client_config->getAlwaysIncludeFqdn(), always_include_fqdn);
EXPECT_EQ(d2_client_config->getOverrideNoUpdate(), override_no_update);
EXPECT_EQ(d2_client_config->getOverrideClientUpdate(),
@@ -94,7 +99,6 @@ TEST(D2ClientConfigTest, constructorsAndAccessors) {
server_port,
dhcp_ddns::NCR_TCP,
ncr_format,
- remove_on_renew,
always_include_fqdn,
override_no_update,
override_client_update,
@@ -119,7 +123,7 @@ TEST(D2ClientConfigTest, equalityOperator) {
ASSERT_NO_THROW(ref_config.reset(new D2ClientConfig(true,
ref_address, 477,
dhcp_ddns::NCR_UDP, dhcp_ddns::FMT_JSON,
- true, true, true, true, true,
+ true, true, true, true,
"pre-fix", "suf-fix")));
ASSERT_TRUE(ref_config);
@@ -127,7 +131,7 @@ TEST(D2ClientConfigTest, equalityOperator) {
ASSERT_NO_THROW(test_config.reset(new D2ClientConfig(true,
ref_address, 477,
dhcp_ddns::NCR_UDP, dhcp_ddns::FMT_JSON,
- true, true, true, true, true,
+ true, true, true, true,
"pre-fix", "suf-fix")));
ASSERT_TRUE(test_config);
EXPECT_TRUE(*ref_config == *test_config);
@@ -137,7 +141,7 @@ TEST(D2ClientConfigTest, equalityOperator) {
ASSERT_NO_THROW(test_config.reset(new D2ClientConfig(false,
ref_address, 477,
dhcp_ddns::NCR_UDP, dhcp_ddns::FMT_JSON,
- true, true, true, true, true,
+ true, true, true, true,
"pre-fix", "suf-fix")));
ASSERT_TRUE(test_config);
EXPECT_FALSE(*ref_config == *test_config);
@@ -147,7 +151,7 @@ TEST(D2ClientConfigTest, equalityOperator) {
ASSERT_NO_THROW(test_config.reset(new D2ClientConfig(true,
test_address, 477,
dhcp_ddns::NCR_UDP, dhcp_ddns::FMT_JSON,
- true, true, true, true, true,
+ true, true, true, true,
"pre-fix", "suf-fix")));
ASSERT_TRUE(test_config);
EXPECT_FALSE(*ref_config == *test_config);
@@ -157,17 +161,7 @@ TEST(D2ClientConfigTest, equalityOperator) {
ASSERT_NO_THROW(test_config.reset(new D2ClientConfig(true,
ref_address, 333,
dhcp_ddns::NCR_UDP, dhcp_ddns::FMT_JSON,
- true, true, true, true, true,
- "pre-fix", "suf-fix")));
- ASSERT_TRUE(test_config);
- EXPECT_FALSE(*ref_config == *test_config);
- EXPECT_TRUE(*ref_config != *test_config);
-
- // Check a configuration that differs only by remove_on_renew.
- ASSERT_NO_THROW(test_config.reset(new D2ClientConfig(true,
- ref_address, 477,
- dhcp_ddns::NCR_UDP, dhcp_ddns::FMT_JSON,
- false, true, true, true, true,
+ true, true, true, true,
"pre-fix", "suf-fix")));
ASSERT_TRUE(test_config);
EXPECT_FALSE(*ref_config == *test_config);
@@ -177,7 +171,7 @@ TEST(D2ClientConfigTest, equalityOperator) {
ASSERT_NO_THROW(test_config.reset(new D2ClientConfig(true,
ref_address, 477,
dhcp_ddns::NCR_UDP, dhcp_ddns::FMT_JSON,
- true, false, true, true, true,
+ false, true, true, true,
"pre-fix", "suf-fix")));
ASSERT_TRUE(test_config);
EXPECT_FALSE(*ref_config == *test_config);
@@ -187,7 +181,7 @@ TEST(D2ClientConfigTest, equalityOperator) {
ASSERT_NO_THROW(test_config.reset(new D2ClientConfig(true,
ref_address, 477,
dhcp_ddns::NCR_UDP, dhcp_ddns::FMT_JSON,
- true, true, false, true, true,
+ true, false, true, true,
"pre-fix", "suf-fix")));
ASSERT_TRUE(test_config);
EXPECT_FALSE(*ref_config == *test_config);
@@ -197,7 +191,7 @@ TEST(D2ClientConfigTest, equalityOperator) {
ASSERT_NO_THROW(test_config.reset(new D2ClientConfig(true,
ref_address, 477,
dhcp_ddns::NCR_UDP, dhcp_ddns::FMT_JSON,
- true, true, true, false, true,
+ true, true, false, true,
"pre-fix", "suf-fix")));
ASSERT_TRUE(test_config);
EXPECT_FALSE(*ref_config == *test_config);
@@ -207,7 +201,7 @@ TEST(D2ClientConfigTest, equalityOperator) {
ASSERT_NO_THROW(test_config.reset(new D2ClientConfig(true,
ref_address, 477,
dhcp_ddns::NCR_UDP, dhcp_ddns::FMT_JSON,
- true, true, true, true, false,
+ true, true, true, false,
"pre-fix", "suf-fix")));
ASSERT_TRUE(test_config);
EXPECT_FALSE(*ref_config == *test_config);
@@ -217,7 +211,7 @@ TEST(D2ClientConfigTest, equalityOperator) {
ASSERT_NO_THROW(test_config.reset(new D2ClientConfig(true,
ref_address, 477,
dhcp_ddns::NCR_UDP, dhcp_ddns::FMT_JSON,
- true, true, true, true, true,
+ true, true, true, true,
"bogus", "suf-fix")));
ASSERT_TRUE(test_config);
EXPECT_FALSE(*ref_config == *test_config);
@@ -227,7 +221,7 @@ TEST(D2ClientConfigTest, equalityOperator) {
ASSERT_NO_THROW(test_config.reset(new D2ClientConfig(true,
ref_address, 477,
dhcp_ddns::NCR_UDP, dhcp_ddns::FMT_JSON,
- true, true, true, true, true,
+ true, true, true, true,
"pre-fix", "bogus")));
ASSERT_TRUE(test_config);
EXPECT_FALSE(*ref_config == *test_config);
@@ -270,7 +264,7 @@ TEST(D2ClientMgr, validConfig) {
ASSERT_NO_THROW(new_cfg.reset(new D2ClientConfig(true,
isc::asiolink::IOAddress("127.0.0.1"), 477,
dhcp_ddns::NCR_UDP, dhcp_ddns::FMT_JSON,
- true, true, true, true, true,
+ true, true, true, true,
"pre-fix", "suf-fix")));
// Verify that we can assign a new, non-empty configuration.
@@ -291,4 +285,520 @@ TEST(D2ClientMgr, validConfig) {
}
+/// @brief Tests that analyzeFqdn detects invalid combination of both the
+/// client S and N flags set to true.
+TEST(D2ClientMgr, analyzeFqdnInvalidCombination) {
+ D2ClientMgr mgr;
+ bool server_s = false;
+ bool server_n = false;
+
+ // Create disabled configuration.
+ D2ClientConfigPtr cfg;
+ ASSERT_NO_THROW(cfg.reset(new D2ClientConfig()));
+ ASSERT_NO_THROW(mgr.setD2ClientConfig(cfg));
+ ASSERT_FALSE(mgr.ddnsEnabled());
+
+ // client S=1 N=1 is invalid. analyzeFqdn should throw.
+ ASSERT_THROW(mgr.analyzeFqdn(true, true, server_s, server_n),
+ isc::BadValue);
+
+ // Create enabled configuration with all controls off (no overrides).
+ ASSERT_NO_THROW(cfg.reset(new D2ClientConfig(true,
+ isc::asiolink::IOAddress("127.0.0.1"), 477,
+ dhcp_ddns::NCR_UDP, dhcp_ddns::FMT_JSON,
+ false, false, false, false,
+ "pre-fix", "suf-fix")));
+ ASSERT_NO_THROW(mgr.setD2ClientConfig(cfg));
+ ASSERT_TRUE(mgr.ddnsEnabled());
+
+ // client S=1 N=1 is invalid. analyzeFqdn should throw.
+ ASSERT_THROW(mgr.analyzeFqdn(true, true, server_s, server_n),
+ isc::BadValue);
+}
+
+/// @brief Tests that analyzeFqdn generates correct server S and N flags when
+/// updates are enabled and all overrides are off.
+TEST(D2ClientMgr, analyzeFqdnEnabledNoOverrides) {
+ D2ClientMgr mgr;
+ bool server_s = false;
+ bool server_n = false;
+
+ // Create enabled configuration with all controls off (no overrides).
+ D2ClientConfigPtr cfg;
+ ASSERT_NO_THROW(cfg.reset(new D2ClientConfig(true,
+ isc::asiolink::IOAddress("127.0.0.1"), 477,
+ dhcp_ddns::NCR_UDP, dhcp_ddns::FMT_JSON,
+ false, false, false, false,
+ "pre-fix", "suf-fix")));
+ ASSERT_NO_THROW(mgr.setD2ClientConfig(cfg));
+ ASSERT_TRUE(mgr.ddnsEnabled());
+ ASSERT_FALSE(cfg->getOverrideClientUpdate());
+ ASSERT_FALSE(cfg->getOverrideNoUpdate());
+
+ // client S=0 N=0 means client wants to do forward update.
+ // server S should be 0 (server is not doing forward updates)
+ // and server N should be 1 (server doing no updates)
+ mgr.analyzeFqdn(false, false, server_s, server_n);
+ EXPECT_FALSE(server_s);
+ EXPECT_TRUE(server_n);
+
+ // client S=1 N=0 means client wants server to do forward update.
+ // server S should be 1 (server is doing forward updates)
+ // and server N should be 0 (server doing updates)
+ mgr.analyzeFqdn(true, false, server_s, server_n);
+ EXPECT_TRUE(server_s);
+ EXPECT_FALSE(server_n);
+
+
+ // client S=0 N=1 means client wants no one to do forward updates.
+ // server S should be 0 (server is not forward updates)
+ // and server N should be 1 (server is not doing any updates)
+ mgr.analyzeFqdn(false, true, server_s, server_n);
+ EXPECT_FALSE(server_s);
+ EXPECT_TRUE(server_n);
+}
+
+/// @brief Tests that analyzeFqdn generates correct server S and N flags when
+/// updates are enabled and override-no-update is on.
+TEST(D2ClientMgr, analyzeFqdnEnabledOverrideNoUpdate) {
+ D2ClientMgr mgr;
+ bool server_s = false;
+ bool server_n = false;
+
+ // Create enabled configuration with OVERRIDE_NO_UPDATE on.
+ D2ClientConfigPtr cfg;
+ ASSERT_NO_THROW(cfg.reset(new D2ClientConfig(true,
+ isc::asiolink::IOAddress("127.0.0.1"), 477,
+ dhcp_ddns::NCR_UDP, dhcp_ddns::FMT_JSON,
+ false, true, false, false,
+ "pre-fix", "suf-fix")));
+ ASSERT_NO_THROW(mgr.setD2ClientConfig(cfg));
+ ASSERT_TRUE(mgr.ddnsEnabled());
+ ASSERT_TRUE(cfg->getOverrideNoUpdate());
+ ASSERT_FALSE(cfg->getOverrideClientUpdate());
+
+ // client S=0 N=0 means client wants to do forward update.
+ // server S should be 0 (server is not doing forward updates)
+ // and server N should be 1 (server is not doing any updates)
+ mgr.analyzeFqdn(false, false, server_s, server_n);
+ EXPECT_FALSE(server_s);
+ EXPECT_TRUE(server_n);
+
+ // client S=1 N=0 means client wants server to do forward update.
+ // server S should be 1 (server is doing forward updates)
+ // and server N should be 0 (server doing updates)
+ mgr.analyzeFqdn(true, false, server_s, server_n);
+ EXPECT_TRUE(server_s);
+ EXPECT_FALSE(server_n);
+
+ // client S=0 N=1 means client wants no one to do forward updates.
+ // server S should be 1 (server is doing forward updates)
+ // and server N should be 0 (server is doing updates)
+ mgr.analyzeFqdn(false, true, server_s, server_n);
+ EXPECT_TRUE(server_s);
+ EXPECT_FALSE(server_n);
+}
+
+/// @brief Tests that analyzeFqdn generates correct server S and N flags when
+/// updates are enabled and override-client-update is on.
+TEST(D2ClientMgr, analyzeFqdnEnabledOverrideClientUpdate) {
+ D2ClientMgr mgr;
+ bool server_s = false;
+ bool server_n = false;
+
+ // Create enabled configuration with OVERRIDE_CLIENT_UPDATE on.
+ D2ClientConfigPtr cfg;
+ ASSERT_NO_THROW(cfg.reset(new D2ClientConfig(true,
+ isc::asiolink::IOAddress("127.0.0.1"), 477,
+ dhcp_ddns::NCR_UDP, dhcp_ddns::FMT_JSON,
+ false, false, true, false,
+ "pre-fix", "suf-fix")));
+ ASSERT_NO_THROW(mgr.setD2ClientConfig(cfg));
+ ASSERT_TRUE(mgr.ddnsEnabled());
+ ASSERT_FALSE(cfg->getOverrideNoUpdate());
+ ASSERT_TRUE(cfg->getOverrideClientUpdate());
+
+ // client S=0 N=0 means client wants to do forward update.
+ // server S should be 1 (server is doing forward updates)
+ // and server N should be 0 (server doing updates)
+ mgr.analyzeFqdn(false, false, server_s, server_n);
+ EXPECT_TRUE(server_s);
+ EXPECT_FALSE(server_n);
+
+ // client S=1 N=0 means client wants server to do forward update.
+ // server S should be 1 (server is doing forward updates)
+ // and server N should be 0 (server doing updates)
+ mgr.analyzeFqdn(true, false, server_s, server_n);
+ EXPECT_TRUE(server_s);
+ EXPECT_FALSE(server_n);
+
+ // client S=0 N=1 means client wants no one to do forward updates.
+ // server S should be 0 (server is not forward updates)
+ // and server N should be 1 (server is not doing any updates)
+ mgr.analyzeFqdn(false, true, server_s, server_n);
+ EXPECT_FALSE(server_s);
+ EXPECT_TRUE(server_n);
+}
+
+/// @brief Verifies the adustFqdnFlags template with Option4ClientFqdn objects.
+/// Ensures that the method can set the N, S, and O flags properly.
+/// Other permutations are covered by analyzeFqdnFlag tests.
+TEST(D2ClientMgr, adjustFqdnFlagsV4) {
+ D2ClientMgr mgr;
+ Option4ClientFqdnPtr request;
+ Option4ClientFqdnPtr response;
+
+ // Create enabled configuration and override-no-update on.
+ D2ClientConfigPtr cfg;
+ ASSERT_NO_THROW(cfg.reset(new D2ClientConfig(true,
+ isc::asiolink::IOAddress("127.0.0.1"), 477,
+ dhcp_ddns::NCR_UDP, dhcp_ddns::FMT_JSON,
+ false, true, false, false,
+ "pre-fix", "suf-fix")));
+ ASSERT_NO_THROW(mgr.setD2ClientConfig(cfg));
+ ASSERT_TRUE(mgr.ddnsEnabled());
+ ASSERT_TRUE(cfg->getOverrideNoUpdate());
+ ASSERT_FALSE(cfg->getOverrideClientUpdate());
+
+ // client S=0 N=0 means client wants to do forward update.
+ // server S should be 0 (server is not doing forward updates)
+ // and server N should be 1 (server doing no updates)
+ // and server O should be 0
+ request.reset(new Option4ClientFqdn(0, Option4ClientFqdn::RCODE_CLIENT(),
+ "", Option4ClientFqdn::PARTIAL));
+ response.reset(new Option4ClientFqdn(*request));
+ response->resetFlags();
+
+ mgr.adjustFqdnFlags<Option4ClientFqdn>(*request, *response);
+ EXPECT_FALSE(response->getFlag(Option4ClientFqdn::FLAG_S));
+ EXPECT_TRUE(response->getFlag(Option4ClientFqdn::FLAG_N));
+ EXPECT_FALSE(response->getFlag(Option4ClientFqdn::FLAG_O));
+
+ // client S=1 N=0 means client wants server to do forward update.
+ // server S should be 1 (server is doing forward updates)
+ // and server N should be 0 (server doing updates)
+ // and server O should be 0
+ request.reset(new Option4ClientFqdn(Option4ClientFqdn::FLAG_S,
+ Option4ClientFqdn::RCODE_CLIENT(),
+ "", Option4ClientFqdn::PARTIAL));
+ response.reset(new Option4ClientFqdn(*request));
+ response->resetFlags();
+
+ mgr.adjustFqdnFlags<Option4ClientFqdn>(*request, *response);
+ EXPECT_TRUE(response->getFlag(Option4ClientFqdn::FLAG_S));
+ EXPECT_FALSE(response->getFlag(Option4ClientFqdn::FLAG_N));
+ EXPECT_FALSE(response->getFlag(Option4ClientFqdn::FLAG_O));
+
+ // client S=0 N=1 means client wants no one to do updates
+ // server S should be 1 (server is doing forward updates)
+ // and server N should be 0 (server doing updates)
+ // and O should be 1 (overriding client S)
+ request.reset(new Option4ClientFqdn(Option4ClientFqdn::FLAG_N,
+ Option4ClientFqdn::RCODE_CLIENT(),
+ "", Option4ClientFqdn::PARTIAL));
+ response.reset(new Option4ClientFqdn(*request));
+ response->resetFlags();
+
+ mgr.adjustFqdnFlags<Option4ClientFqdn>(*request, *response);
+ EXPECT_TRUE(response->getFlag(Option4ClientFqdn::FLAG_S));
+ EXPECT_FALSE(response->getFlag(Option4ClientFqdn::FLAG_N));
+ EXPECT_TRUE(response->getFlag(Option4ClientFqdn::FLAG_O));
+}
+
+/// @brief Tests the qualifyName method's ability to construct FQDNs
+TEST(D2ClientMgr, qualifyName) {
+ D2ClientMgr mgr;
+
+ // Create enabled configuration.
+ D2ClientConfigPtr cfg;
+ ASSERT_NO_THROW(cfg.reset(new D2ClientConfig(true,
+ isc::asiolink::IOAddress("127.0.0.1"), 477,
+ dhcp_ddns::NCR_UDP, dhcp_ddns::FMT_JSON,
+ false, false, true, false,
+ "prefix", "suffix.com")));
+ ASSERT_NO_THROW(mgr.setD2ClientConfig(cfg));
+
+ // Verify that the qualifying suffix gets appended with trailing dot added.
+ std::string partial_name = "somehost";
+ std::string qualified_name = mgr.qualifyName(partial_name);
+ EXPECT_EQ("somehost.suffix.com.", qualified_name);
+
+ ASSERT_NO_THROW(cfg.reset(new D2ClientConfig(true,
+ isc::asiolink::IOAddress("127.0.0.1"), 477,
+ dhcp_ddns::NCR_UDP, dhcp_ddns::FMT_JSON,
+ false, false, true, false,
+ "prefix", "hasdot.com.")));
+ ASSERT_NO_THROW(mgr.setD2ClientConfig(cfg));
+
+ // Verify that the qualifying suffix gets appended without dot added.
+ qualified_name = mgr.qualifyName(partial_name);
+ EXPECT_EQ("somehost.hasdot.com.", qualified_name);
+}
+
+
+/// @brief Tests the generateFdqn method's ability to construct FQDNs
+TEST(D2ClientMgr, generateFqdn) {
+ D2ClientMgr mgr;
+
+ // Create enabled configuration.
+ D2ClientConfigPtr cfg;
+ ASSERT_NO_THROW(cfg.reset(new D2ClientConfig(true,
+ isc::asiolink::IOAddress("127.0.0.1"), 477,
+ dhcp_ddns::NCR_UDP, dhcp_ddns::FMT_JSON,
+ false, false, true, false,
+ "prefix", "suffix.com")));
+ ASSERT_NO_THROW(mgr.setD2ClientConfig(cfg));
+
+ // Verify that it works with an IPv4 address.
+ asiolink::IOAddress v4address("192.0.2.75");
+ EXPECT_EQ("prefix-192-0-2-75.suffix.com.", mgr.generateFqdn(v4address));
+
+ // Verify that it works with an IPv6 address.
+ asiolink::IOAddress v6address("2001:db8::2");
+ EXPECT_EQ("prefix-2001-db8--2.suffix.com.", mgr.generateFqdn(v6address));
+
+ // Create a disabled config.
+ ASSERT_NO_THROW(cfg.reset(new D2ClientConfig()));
+ ASSERT_NO_THROW(mgr.setD2ClientConfig(cfg));
+
+ // Verify names generate properly with a disabled configuration.
+ EXPECT_EQ("myhost-192-0-2-75.example.com.", mgr.generateFqdn(v4address));
+ EXPECT_EQ("myhost-2001-db8--2.example.com.", mgr.generateFqdn(v6address));
+}
+
+/// @brief Tests adjustDomainName template method with Option4ClientFqdn
+TEST(D2ClientMgr, adjustDomainNameV4) {
+ D2ClientMgr mgr;
+ Option4ClientFqdnPtr request;
+ Option4ClientFqdnPtr response;
+
+ // Create enabled configuration.
+ D2ClientConfigPtr cfg;
+ ASSERT_NO_THROW(cfg.reset(new D2ClientConfig(true,
+ isc::asiolink::IOAddress("127.0.0.1"), 477,
+ dhcp_ddns::NCR_UDP, dhcp_ddns::FMT_JSON,
+ false, false, false, false,
+ "prefix", "suffix.com")));
+ ASSERT_NO_THROW(mgr.setD2ClientConfig(cfg));
+ ASSERT_FALSE(cfg->getReplaceClientName());
+
+ // replace-client-name is false, client passes in empty fqdn
+ // reponse domain should be empty/partial.
+ request.reset(new Option4ClientFqdn(0, Option4ClientFqdn::RCODE_CLIENT(),
+ "", Option4ClientFqdn::PARTIAL));
+ response.reset(new Option4ClientFqdn(*request));
+
+ mgr.adjustDomainName<Option4ClientFqdn>(*request, *response);
+ EXPECT_EQ("", response->getDomainName());
+ EXPECT_EQ(Option4ClientFqdn::PARTIAL, response->getDomainNameType());
+
+
+ // replace-client-name is false, client passes in a partial fqdn
+ // response should contain client's name plus the qualifying suffix.
+ request.reset(new Option4ClientFqdn(0, Option4ClientFqdn::RCODE_CLIENT(),
+ "myhost", Option4ClientFqdn::PARTIAL));
+ response.reset(new Option4ClientFqdn(*request));
+
+ mgr.adjustDomainName<Option4ClientFqdn>(*request, *response);
+ EXPECT_EQ("myhost.suffix.com.", response->getDomainName());
+ EXPECT_EQ(Option4ClientFqdn::FULL, response->getDomainNameType());
+
+
+ // replace-client-name is false, client passes in a full fqdn
+ // response domain should not be altered.
+ request.reset(new Option4ClientFqdn(0, Option4ClientFqdn::RCODE_CLIENT(),
+ "myhost.example.com.",
+ Option4ClientFqdn::FULL));
+ response.reset(new Option4ClientFqdn(*request));
+ mgr.adjustDomainName<Option4ClientFqdn>(*request, *response);
+ EXPECT_EQ("myhost.example.com.", response->getDomainName());
+ EXPECT_EQ(Option4ClientFqdn::FULL, response->getDomainNameType());
+
+ // Create enabled configuration.
+ ASSERT_NO_THROW(cfg.reset(new D2ClientConfig(true,
+ isc::asiolink::IOAddress("127.0.0.1"), 477,
+ dhcp_ddns::NCR_UDP, dhcp_ddns::FMT_JSON,
+ false, false, false, true,
+ "prefix", "suffix.com")));
+ ASSERT_NO_THROW(mgr.setD2ClientConfig(cfg));
+ ASSERT_TRUE(cfg->getReplaceClientName());
+
+ // replace-client-name is true, client passes in empty fqdn
+ // reponse domain should be empty/partial.
+ request.reset(new Option4ClientFqdn(0, Option4ClientFqdn::RCODE_CLIENT(),
+ "", Option4ClientFqdn::PARTIAL));
+ response.reset(new Option4ClientFqdn(*request));
+
+ mgr.adjustDomainName<Option4ClientFqdn>(*request, *response);
+ EXPECT_EQ("", response->getDomainName());
+ EXPECT_EQ(Option4ClientFqdn::PARTIAL, response->getDomainNameType());
+
+ // replace-client-name is true, client passes in a partial fqdn
+ // reponse domain should be empty/partial.
+ request.reset(new Option4ClientFqdn(0, Option4ClientFqdn::RCODE_CLIENT(),
+ "myhost", Option4ClientFqdn::PARTIAL));
+ response.reset(new Option4ClientFqdn(*request));
+
+ mgr.adjustDomainName<Option4ClientFqdn>(*request, *response);
+ EXPECT_EQ("", response->getDomainName());
+ EXPECT_EQ(Option4ClientFqdn::PARTIAL, response->getDomainNameType());
+
+
+ // replace-client-name is true, client passes in a full fqdn
+ // reponse domain should be empty/partial.
+ request.reset(new Option4ClientFqdn(0, Option4ClientFqdn::RCODE_CLIENT(),
+ "myhost.example.com.",
+ Option4ClientFqdn::FULL));
+ response.reset(new Option4ClientFqdn(*request));
+ mgr.adjustDomainName<Option4ClientFqdn>(*request, *response);
+ EXPECT_EQ("", response->getDomainName());
+ EXPECT_EQ(Option4ClientFqdn::PARTIAL, response->getDomainNameType());
+}
+
+/// @brief Tests adjustDomainName template method with Option6ClientFqdn
+TEST(D2ClientMgr, adjustDomainNameV6) {
+ D2ClientMgr mgr;
+ Option6ClientFqdnPtr request;
+ Option6ClientFqdnPtr response;
+
+ // Create enabled configuration.
+ D2ClientConfigPtr cfg;
+ ASSERT_NO_THROW(cfg.reset(new D2ClientConfig(true,
+ isc::asiolink::IOAddress("127.0.0.1"), 477,
+ dhcp_ddns::NCR_UDP, dhcp_ddns::FMT_JSON,
+ false, false, false, false,
+ "prefix", "suffix.com")));
+ ASSERT_NO_THROW(mgr.setD2ClientConfig(cfg));
+ ASSERT_FALSE(cfg->getReplaceClientName());
+
+ // replace-client-name is false, client passes in empty fqdn
+ // reponse domain should be empty/partial.
+ request.reset(new Option6ClientFqdn(0, "", Option6ClientFqdn::PARTIAL));
+ response.reset(new Option6ClientFqdn(*request));
+
+ mgr.adjustDomainName<Option6ClientFqdn>(*request, *response);
+ EXPECT_EQ("", response->getDomainName());
+ EXPECT_EQ(Option6ClientFqdn::PARTIAL, response->getDomainNameType());
+
+ // replace-client-name is false, client passes in a partial fqdn
+ // response should contain client's name plus the qualifying suffix.
+ request.reset(new Option6ClientFqdn(0, "myhost",
+ Option6ClientFqdn::PARTIAL));
+ response.reset(new Option6ClientFqdn(*request));
+
+ mgr.adjustDomainName<Option6ClientFqdn>(*request, *response);
+ EXPECT_EQ("myhost.suffix.com.", response->getDomainName());
+ EXPECT_EQ(Option6ClientFqdn::FULL, response->getDomainNameType());
+
+
+ // replace-client-name is false, client passes in a full fqdn
+ // response domain should not be altered.
+ request.reset(new Option6ClientFqdn(0, "myhost.example.com.",
+ Option6ClientFqdn::FULL));
+ response.reset(new Option6ClientFqdn(*request));
+ mgr.adjustDomainName<Option6ClientFqdn>(*request, *response);
+ EXPECT_EQ("myhost.example.com.", response->getDomainName());
+ EXPECT_EQ(Option6ClientFqdn::FULL, response->getDomainNameType());
+
+ // Create enabled configuration.
+ ASSERT_NO_THROW(cfg.reset(new D2ClientConfig(true,
+ isc::asiolink::IOAddress("127.0.0.1"), 477,
+ dhcp_ddns::NCR_UDP, dhcp_ddns::FMT_JSON,
+ false, false, false, true,
+ "prefix", "suffix.com")));
+ ASSERT_NO_THROW(mgr.setD2ClientConfig(cfg));
+ ASSERT_TRUE(cfg->getReplaceClientName());
+
+ // replace-client-name is true, client passes in empty fqdn
+ // reponse domain should be empty/partial.
+ request.reset(new Option6ClientFqdn(0, "", Option6ClientFqdn::PARTIAL));
+ response.reset(new Option6ClientFqdn(*request));
+
+ mgr.adjustDomainName<Option6ClientFqdn>(*request, *response);
+ EXPECT_EQ("", response->getDomainName());
+ EXPECT_EQ(Option6ClientFqdn::PARTIAL, response->getDomainNameType());
+
+ // replace-client-name is true, client passes in a partial fqdn
+ // reponse domain should be empty/partial.
+ request.reset(new Option6ClientFqdn(0, "myhost",
+ Option6ClientFqdn::PARTIAL));
+ response.reset(new Option6ClientFqdn(*request));
+
+ mgr.adjustDomainName<Option6ClientFqdn>(*request, *response);
+ EXPECT_EQ("", response->getDomainName());
+ EXPECT_EQ(Option6ClientFqdn::PARTIAL, response->getDomainNameType());
+
+
+ // replace-client-name is true, client passes in a full fqdn
+ // reponse domain should be empty/partial.
+ request.reset(new Option6ClientFqdn(0, "myhost.example.com.",
+ Option6ClientFqdn::FULL));
+ response.reset(new Option6ClientFqdn(*request));
+ mgr.adjustDomainName<Option6ClientFqdn>(*request, *response);
+ EXPECT_EQ("", response->getDomainName());
+ EXPECT_EQ(Option6ClientFqdn::PARTIAL, response->getDomainNameType());
+}
+
+/// @brief Verifies the adustFqdnFlags template with Option6ClientFqdn objects.
+/// Ensures that the method can set the N, S, and O flags properly.
+/// Other permutations are covered by analyzeFqdnFlags tests.
+TEST(D2ClientMgr, adjustFqdnFlagsV6) {
+ D2ClientMgr mgr;
+ Option6ClientFqdnPtr request;
+ Option6ClientFqdnPtr response;
+
+ // Create enabled configuration and override-no-update on.
+ D2ClientConfigPtr cfg;
+ ASSERT_NO_THROW(cfg.reset(new D2ClientConfig(true,
+ isc::asiolink::IOAddress("127.0.0.1"), 477,
+ dhcp_ddns::NCR_UDP, dhcp_ddns::FMT_JSON,
+ false, true, false, false,
+ "pre-fix", "suf-fix")));
+ ASSERT_NO_THROW(mgr.setD2ClientConfig(cfg));
+ ASSERT_TRUE(mgr.ddnsEnabled());
+ ASSERT_TRUE(cfg->getOverrideNoUpdate());
+ ASSERT_FALSE(cfg->getOverrideClientUpdate());
+
+ // client S=0 N=0 means client wants to do forward update.
+ // server S should be 0 (server is not doing forward updates)
+ // and server N should be 1 (server doing no updates)
+ // and server O should be 0
+ request.reset(new Option6ClientFqdn(0, "", Option6ClientFqdn::PARTIAL));
+ response.reset(new Option6ClientFqdn(*request));
+ response->resetFlags();
+
+ mgr.adjustFqdnFlags<Option6ClientFqdn>(*request, *response);
+ EXPECT_FALSE(response->getFlag(Option6ClientFqdn::FLAG_S));
+ EXPECT_TRUE(response->getFlag(Option6ClientFqdn::FLAG_N));
+ EXPECT_FALSE(response->getFlag(Option6ClientFqdn::FLAG_O));
+
+ // client S=1 N=0 means client wants server to do forward update.
+ // server S should be 1 (server is doing forward updates)
+ // and server N should be 0 (server doing updates)
+ // and server O should be 0
+ request.reset(new Option6ClientFqdn(Option6ClientFqdn::FLAG_S,
+ "", Option6ClientFqdn::PARTIAL));
+ response.reset(new Option6ClientFqdn(*request));
+ response->resetFlags();
+
+ mgr.adjustFqdnFlags<Option6ClientFqdn>(*request, *response);
+ EXPECT_TRUE(response->getFlag(Option6ClientFqdn::FLAG_S));
+ EXPECT_FALSE(response->getFlag(Option6ClientFqdn::FLAG_N));
+ EXPECT_FALSE(response->getFlag(Option6ClientFqdn::FLAG_O));
+
+ // client S=0 N=1 means client wants no one to do updates
+ // server S should be 1 (server is doing forward updates)
+ // and server N should be 0 (server doing updates)
+ // and O should be 1 (overriding client S)
+ request.reset(new Option6ClientFqdn(Option6ClientFqdn::FLAG_N,
+ "", Option6ClientFqdn::PARTIAL));
+ response.reset(new Option6ClientFqdn(*request));
+ response->resetFlags();
+
+ mgr.adjustFqdnFlags<Option6ClientFqdn>(*request, *response);
+ EXPECT_TRUE(response->getFlag(Option6ClientFqdn::FLAG_S));
+ EXPECT_FALSE(response->getFlag(Option6ClientFqdn::FLAG_N));
+ EXPECT_TRUE(response->getFlag(Option6ClientFqdn::FLAG_O));
+}
+
} // end of anonymous namespace
diff --git a/src/lib/dhcpsrv/tests/d2_udp_unittest.cc b/src/lib/dhcpsrv/tests/d2_udp_unittest.cc
new file mode 100644
index 0000000..cc17d19
--- /dev/null
+++ b/src/lib/dhcpsrv/tests/d2_udp_unittest.cc
@@ -0,0 +1,477 @@
+// Copyright (C) 2014 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.
+
+/// @file d2_upd_unittest.cc Unit tests for D2ClientMgr UDP communications.
+/// Note these tests are not intended to verify the actual send and receive
+/// across UDP sockets. This level of testing is done in libdhcp-ddns.
+
+#include <asio.hpp>
+#include <asiolink/io_service.h>
+#include <config.h>
+#include <dhcp/iface_mgr.h>
+#include <dhcpsrv/d2_client_mgr.h>
+#include <exceptions/exceptions.h>
+
+#include <boost/function.hpp>
+#include <boost/bind.hpp>
+
+#include <gtest/gtest.h>
+
+#include <sys/select.h>
+
+using namespace std;
+using namespace isc::dhcp;
+using namespace isc;
+
+namespace {
+
+/// @brief Test fixture for excerising D2ClientMgr send management
+/// services. It inherents from D2ClientMgr to allow overriding various
+/// methods and accessing otherwise restricted member. In particular it
+/// overrides the NameChangeSender completion completion callback, allowing
+/// the injection of send errors.
+class D2ClientMgrTest : public D2ClientMgr, public ::testing::Test {
+public:
+ /// @brief If true simulates a send which completed with a failed status.
+ bool simulate_send_failure_;
+ /// @brief If true causes an exception throw in the client error handler.
+ bool error_handler_throw_;
+ /// @brief Tracks the number times the completion handler is called.
+ int callback_count_;
+ /// @brief Tracks the number of times the client error handler was called.
+ int error_handler_count_;
+
+ /// @brief Constructor
+ D2ClientMgrTest() : simulate_send_failure_(false),
+ error_handler_throw_(false),
+ callback_count_(0), error_handler_count_(0) {
+ }
+
+ /// @brief virtual Destructor
+ virtual ~D2ClientMgrTest(){
+ }
+
+ /// @brief Updates the D2ClientMgr's configuration to DDNS disabled.
+ void disableDdns() {
+ D2ClientConfigPtr new_cfg;
+ ASSERT_NO_THROW(new_cfg.reset(new D2ClientConfig()));
+ ASSERT_NO_THROW(setD2ClientConfig(new_cfg));
+ ASSERT_FALSE(ddnsEnabled());
+ }
+
+ /// @brief Updates the D2ClientMgr's configuration to DDNS enabled.
+ ///
+ /// @param server_address IP address of b10-dhcp-ddns.
+ /// @param server_port IP port number of b10-dhcp-ddns.
+ /// @param protocol NCR protocol to use. (Currently only UDP is
+ /// supported).
+ void enableDdns(const std::string& server_address,
+ const size_t server_port,
+ const dhcp_ddns::NameChangeProtocol protocol) {
+ // Update the configuration with one that is enabled.
+ D2ClientConfigPtr new_cfg;
+ ASSERT_NO_THROW(new_cfg.reset(new D2ClientConfig(true,
+ isc::asiolink::IOAddress(server_address),
+ server_port,
+ protocol, dhcp_ddns::FMT_JSON,
+ true, true, true, true,
+ "myhost", ".example.com.")));
+ ASSERT_NO_THROW(setD2ClientConfig(new_cfg));
+ ASSERT_TRUE(ddnsEnabled());
+ }
+
+ /// @brief Checks sender's select-fd against an expected state of readiness.
+ ///
+ /// Uses select() to determine if the sender's select_fd is marked as
+ /// ready to read, and compares this against the expected state. The
+ /// select function is called with a timeout of 0.0 (non blocking).
+ ///
+ /// @param expect_ready Expected state of readiness (True if expecting
+ /// a ready to ready result, false if expecting otherwise).
+ void selectCheck(bool expect_ready) {
+ fd_set read_fds;
+ int maxfd = 0;
+
+ FD_ZERO(&read_fds);
+
+ int select_fd = -1;
+ ASSERT_NO_THROW(select_fd = getSelectFd());
+
+ FD_SET(select_fd, &read_fds);
+ maxfd = select_fd;
+
+ struct timeval select_timeout;
+ select_timeout.tv_sec = 0;
+ select_timeout.tv_usec = 0;
+
+ int result = (select(maxfd + 1, &read_fds, NULL, NULL,
+ &select_timeout));
+
+ if (result < 0) {
+ const char *errstr = strerror(errno);
+ FAIL() << "select failed :" << errstr;
+ }
+
+ if (expect_ready) {
+ ASSERT_TRUE(result > 0);
+ } else {
+ ASSERT_TRUE(result == 0);
+ }
+ }
+
+ /// @brief Overrides base class completion callback.
+ ///
+ /// This method will be invoked each time a send completes. It allows
+ /// intervention prior to calling the production implemenation in the
+ /// base. If simulate_send_failure_ is true, the base call impl will
+ /// be called with an error status, otherwise it will be called with
+ /// the result paramater given.
+ ///
+ /// @param result Result code of the send operation.
+ /// @param ncr NameChangeRequest which failed to send.
+ virtual void operator()(const dhcp_ddns::NameChangeSender::Result result,
+ dhcp_ddns::NameChangeRequestPtr& ncr) {
+ ++callback_count_;
+ if (simulate_send_failure_) {
+ simulate_send_failure_ = false;
+ D2ClientMgr::operator()(dhcp_ddns::NameChangeSender::ERROR, ncr);
+ } else {
+ D2ClientMgr::operator()(result, ncr);
+ }
+ }
+
+ /// @brief Serves as the "application level" client error handler.
+ ///
+ /// This method is passed into calls to startSender as the client error
+ /// handler. It should be invoked whenever the completion callback is
+ /// passed a result other than SUCCESS. If error_handler_throw_
+ /// is true it will throw an exception.
+ ///
+ /// @param result unused - Result code of the send operation.
+ /// @param ncr unused -NameChangeRequest which failed to send.
+ void error_handler(const dhcp_ddns::NameChangeSender::Result /*result*/,
+ dhcp_ddns::NameChangeRequestPtr& /*ncr*/) {
+ if (error_handler_throw_) {
+ error_handler_throw_ = false;
+ isc_throw(isc::InvalidOperation, "Simulated client handler throw");
+ }
+
+ ++error_handler_count_;
+ }
+
+ /// @brief Returns D2ClientErroHandler bound to this::error_handler_.
+ D2ClientErrorHandler getErrorHandler() {
+ return (boost::bind(&D2ClientMgrTest::error_handler, this, _1, _2));
+ }
+
+ /// @brief Contructs a NameChangeRequest message from a fixed JSON string.
+ dhcp_ddns::NameChangeRequestPtr buildTestNcr() {
+ // Build an NCR from json string.
+ const char* ncr_str =
+ "{"
+ " \"change_type\" : 0 , "
+ " \"forward_change\" : true , "
+ " \"reverse_change\" : false , "
+ " \"fqdn\" : \"myhost.example.com.\" , "
+ " \"ip_address\" : \"192.168.2.1\" , "
+ " \"dhcid\" : \"010203040A7F8E3D\" , "
+ " \"lease_expires_on\" : \"20140121132405\" , "
+ " \"lease_length\" : 1300 "
+ "}";
+
+ return (dhcp_ddns::NameChangeRequest::fromJSON(ncr_str));
+ }
+
+ /// Expose restricted members.
+ using D2ClientMgr::getSelectFd;
+};
+
+
+/// @brief Checks that D2ClientMgr disable and enable a UDP sender.
+TEST_F(D2ClientMgrTest, udpSenderEnableDisable) {
+ // Verify DDNS is disabled by default.
+ ASSERT_FALSE(ddnsEnabled());
+
+ // Verify we are not in send mode.
+ ASSERT_FALSE(amSending());
+
+ // Enable DDNS with server at 127.0.0.1/prot 53001 via UDP.
+ enableDdns("127.0.0.1", 530001, dhcp_ddns::NCR_UDP);
+ ASSERT_FALSE(amSending());
+
+ ASSERT_NO_THROW(startSender(getErrorHandler()));
+ ASSERT_TRUE(amSending());
+
+ // Verify that we take sender out of send mode.
+ ASSERT_NO_THROW(stopSender());
+ ASSERT_FALSE(amSending());
+}
+
+/// @brief Checks D2ClientMgr queuing methods with a UDP sender.
+TEST_F(D2ClientMgrTest, udpSenderQueing) {
+ // Enable DDNS with server at 127.0.0.1/prot 53001 via UDP.
+ enableDdns("127.0.0.1", 530001, dhcp_ddns::NCR_UDP);
+ ASSERT_FALSE(amSending());
+
+ // Queue should be empty.
+ EXPECT_EQ(0, getQueueSize());
+
+ // Trying to peek past the end of the queue should throw.
+ EXPECT_THROW(peekAt(1), dhcp_ddns::NcrSenderError);
+
+ // Trying to send a NCR when not in send mode should fail.
+ dhcp_ddns::NameChangeRequestPtr ncr = buildTestNcr();
+ EXPECT_THROW(sendRequest(ncr), D2ClientError);
+
+ // Place sender in send mode.
+ ASSERT_NO_THROW(startSender(getErrorHandler()));
+ ASSERT_TRUE(amSending());
+
+ // Send should succeed now.
+ ASSERT_NO_THROW(sendRequest(ncr));
+
+ // Queue should have 1 entry.
+ EXPECT_EQ(1, getQueueSize());
+
+ // Attempt to fetch the entry we just queued.
+ dhcp_ddns::NameChangeRequestPtr ncr2;
+ ASSERT_NO_THROW(ncr2 = peekAt(0));
+
+ // Verify what we queued matches what we fetched.
+ EXPECT_TRUE(*ncr == *ncr2);
+
+ // Clearing the queue while in send mode should fail.
+ ASSERT_THROW(clearQueue(), dhcp_ddns::NcrSenderError);
+
+ // We should still have 1 in the queue.
+ EXPECT_EQ(1, getQueueSize());
+
+ // Get out of send mode.
+ ASSERT_NO_THROW(stopSender());
+ ASSERT_FALSE(amSending());
+
+ // Clear queue should succeed now.
+ ASSERT_NO_THROW(clearQueue());
+ EXPECT_EQ(0, getQueueSize());
+}
+
+/// @brief Checks that D2ClientMgr can send with a UDP sender and
+/// a private IOService.
+TEST_F(D2ClientMgrTest, udpSend) {
+ // Enable DDNS with server at 127.0.0.1/prot 53001 via UDP.
+ enableDdns("127.0.0.1", 530001, dhcp_ddns::NCR_UDP);
+
+ // Trying to fetch the select-fd when not sending should fail.
+ ASSERT_THROW(getSelectFd(), D2ClientError);
+
+ // Place sender in send mode.
+ ASSERT_NO_THROW(startSender(getErrorHandler()));
+
+ // select_fd should evaluate to NOT ready to read.
+ selectCheck(false);
+
+ // Build a test request and send it.
+ dhcp_ddns::NameChangeRequestPtr ncr = buildTestNcr();
+ ASSERT_NO_THROW(sendRequest(ncr));
+
+ // select_fd should evaluate to ready to read.
+ selectCheck(true);
+
+ // Call service handler.
+ runReadyIO();
+
+ // select_fd should evaluate to not ready to read.
+ selectCheck(false);
+}
+
+/// @brief Checks that D2ClientMgr can send with a UDP sender and
+/// an external IOService.
+TEST_F(D2ClientMgrTest, udpSendExternalIOService) {
+ // Enable DDNS with server at 127.0.0.1/prot 53001 via UDP.
+ enableDdns("127.0.0.1", 530001, dhcp_ddns::NCR_UDP);
+
+ // Place sender in send mode using an external IO service.
+ asiolink::IOService io_service;
+ ASSERT_NO_THROW(startSender(getErrorHandler(), io_service));
+
+ // select_fd should evaluate to NOT ready to read.
+ selectCheck(false);
+
+ // Build a test request and send it.
+ dhcp_ddns::NameChangeRequestPtr ncr = buildTestNcr();
+ ASSERT_NO_THROW(sendRequest(ncr));
+
+ // select_fd should evaluate to ready to read.
+ selectCheck(true);
+
+ // Call service handler.
+ runReadyIO();
+
+ // select_fd should evaluate to not ready to read.
+ selectCheck(false);
+
+ // Explicitly stop the sender. This ensures the sender's
+ // ASIO socket is closed prior to the local io_service
+ // instance goes out of scope.
+ ASSERT_NO_THROW(stopSender());
+}
+
+/// @brief Checks that D2ClientMgr invokes the client error handler
+/// when send errors occur.
+TEST_F(D2ClientMgrTest, udpSendErrorHandler) {
+ // Enable DDNS with server at 127.0.0.1/prot 53001 via UDP.
+ // Place sender in send mode.
+ enableDdns("127.0.0.1", 530001, dhcp_ddns::NCR_UDP);
+ ASSERT_NO_THROW(startSender(getErrorHandler()));
+
+ // Simulate a failed response in the send call back. This should
+ // cause the error handler to get invoked.
+ simulate_send_failure_ = true;
+
+ // Verify error count is zero.
+ ASSERT_EQ(0, error_handler_count_);
+
+ // Send a test request.
+ dhcp_ddns::NameChangeRequestPtr ncr = buildTestNcr();
+ ASSERT_NO_THROW(sendRequest(ncr));
+
+ // Call the ready handler. This should complete the message with an error.
+ ASSERT_NO_THROW(runReadyIO());
+
+ // If we executed error handler properly, the error count should one.
+ ASSERT_EQ(1, error_handler_count_);
+}
+
+
+/// @brief Checks that client error handler exceptions are handled gracefully.
+TEST_F(D2ClientMgrTest, udpSendErrorHandlerThrow) {
+ // Enable DDNS with server at 127.0.0.1/prot 53001 via UDP.
+ // Place sender in send mode.
+ enableDdns("127.0.0.1", 530001, dhcp_ddns::NCR_UDP);
+ ASSERT_NO_THROW(startSender(getErrorHandler()));
+
+ // Simulate a failed response in the send call back and
+ // force a throw in the error handler.
+ simulate_send_failure_ = true;
+ error_handler_throw_ = true;
+
+ // Verify error count is zero.
+ ASSERT_EQ(0, error_handler_count_);
+
+ // Send a test request.
+ dhcp_ddns::NameChangeRequestPtr ncr = buildTestNcr();
+ ASSERT_NO_THROW(sendRequest(ncr));
+
+ // Call the ready handler. This should complete the message with an error.
+ // The handler should throw but the exception should not escape.
+ ASSERT_NO_THROW(runReadyIO());
+
+ // If throw flag is false, then we were in the error handler should
+ // have thrown.
+ ASSERT_FALSE(error_handler_throw_);
+
+ // If error count is still zero, then we did throw.
+ ASSERT_EQ(0, error_handler_count_);
+}
+
+/// @brief Tests that D2ClientMgr registers and unregisters with IfaceMgr.
+TEST_F(D2ClientMgrTest, ifaceRegister) {
+ // Enable DDNS with server at 127.0.0.1/prot 53001 via UDP.
+ enableDdns("127.0.0.1", 530001, dhcp_ddns::NCR_UDP);
+
+ // Place sender in send mode.
+ ASSERT_NO_THROW(startSender(getErrorHandler()));
+
+ // Queue three messages.
+ for (int i = 0; i < 3; ++i) {
+ dhcp_ddns::NameChangeRequestPtr ncr = buildTestNcr();
+ ASSERT_NO_THROW(sendRequest(ncr));
+ }
+
+ // Make sure queue count is correct.
+ EXPECT_EQ(3, getQueueSize());
+
+ // select_fd should evaluate to ready to read.
+ selectCheck(true);
+
+ // Calling receive should complete the first message and start the second.
+ IfaceMgr::instance().receive4(0, 0);
+
+ // Verify the callback hander was invoked, no errors counted.
+ EXPECT_EQ(2, getQueueSize());
+ ASSERT_EQ(1, callback_count_);
+ ASSERT_EQ(0, error_handler_count_);
+
+ // Stop the sender. This should complete the second message but leave
+ // the third in the queue.
+ ASSERT_NO_THROW(stopSender());
+ EXPECT_EQ(1, getQueueSize());
+ ASSERT_EQ(2, callback_count_);
+ ASSERT_EQ(0, error_handler_count_);
+
+ // Calling receive again should have no affect.
+ IfaceMgr::instance().receive4(0, 0);
+ EXPECT_EQ(1, getQueueSize());
+ ASSERT_EQ(2, callback_count_);
+ ASSERT_EQ(0, error_handler_count_);
+}
+
+/// @brief Checks that D2ClientMgr suspendUpdates works properly.
+TEST_F(D2ClientMgrTest, udpSuspendUpdates) {
+ // Enable DDNS with server at 127.0.0.1/prot 53001 via UDP.
+ // Place sender in send mode.
+ enableDdns("127.0.0.1", 530001, dhcp_ddns::NCR_UDP);
+ ASSERT_NO_THROW(startSender(getErrorHandler()));
+
+ // Send a test request.
+ for (int i = 0; i < 3; ++i) {
+ dhcp_ddns::NameChangeRequestPtr ncr = buildTestNcr();
+ ASSERT_NO_THROW(sendRequest(ncr));
+ }
+ ASSERT_EQ(3, getQueueSize());
+
+ // Call the ready handler. This should complete the first message
+ // and initiate sending the second message.
+ ASSERT_NO_THROW(runReadyIO());
+
+ // Queue count should have gone down by 1.
+ ASSERT_EQ(2, getQueueSize());
+
+ // Suspend updates. This should disable updates and stop the sender.
+ ASSERT_NO_THROW(suspendUpdates());
+
+ EXPECT_FALSE(ddnsEnabled());
+ EXPECT_FALSE(amSending());
+
+ // Stopping the sender should have completed the second message's
+ // in-progess send, so queue size should be 1.
+ ASSERT_EQ(1, getQueueSize());
+}
+
+/// @brief Tests that invokeErrorHandler does not fail if there is no handler.
+TEST_F(D2ClientMgrTest, missingErrorHandler) {
+ // Ensure we aren't in send mode.
+ ASSERT_FALSE(ddnsEnabled());
+ ASSERT_FALSE(amSending());
+
+ // There is no error handler at this point, so invoking should not throw.
+ dhcp_ddns::NameChangeRequestPtr ncr;
+ ASSERT_NO_THROW(invokeClientErrorHandler(dhcp_ddns::NameChangeSender::ERROR,
+ ncr));
+
+ // Verify we didn't invoke the error handler, error count is zero.
+ ASSERT_EQ(0, error_handler_count_);
+}
+
+} // end of anonymous namespace
diff --git a/src/lib/dhcpsrv/tests/dhcp_parsers_unittest.cc b/src/lib/dhcpsrv/tests/dhcp_parsers_unittest.cc
index 2e6adb9..71d63c5 100644
--- a/src/lib/dhcpsrv/tests/dhcp_parsers_unittest.cc
+++ b/src/lib/dhcpsrv/tests/dhcp_parsers_unittest.cc
@@ -720,7 +720,6 @@ TEST_F(ParseConfigTest, validD2Config) {
" \"server-port\" : 3432, "
" \"ncr-protocol\" : \"UDP\", "
" \"ncr-format\" : \"JSON\", "
- " \"remove-on-renew\" : true, "
" \"always-include-fqdn\" : true, "
" \"override-no-update\" : true, "
" \"override-client-update\" : true, "
@@ -746,7 +745,6 @@ TEST_F(ParseConfigTest, validD2Config) {
EXPECT_EQ(3432, d2_client_config->getServerPort());
EXPECT_EQ(dhcp_ddns::NCR_UDP, d2_client_config->getNcrProtocol());
EXPECT_EQ(dhcp_ddns::FMT_JSON, d2_client_config->getNcrFormat());
- EXPECT_TRUE(d2_client_config->getRemoveOnRenew());
EXPECT_TRUE(d2_client_config->getAlwaysIncludeFqdn());
EXPECT_TRUE(d2_client_config->getOverrideNoUpdate());
EXPECT_TRUE(d2_client_config->getOverrideClientUpdate());
@@ -755,17 +753,16 @@ TEST_F(ParseConfigTest, validD2Config) {
EXPECT_EQ("test.suffix.", d2_client_config->getQualifyingSuffix());
// Another valid Configuration string.
- // This one has IPV6 server ip, control flags false,
+ // This one is disabled, has IPV6 server ip, control flags false,
// empty prefix/suffix
std::string config_str2 =
"{ \"dhcp-ddns\" :"
" {"
- " \"enable-updates\" : true, "
+ " \"enable-updates\" : false, "
" \"server-ip\" : \"2001:db8::\", "
" \"server-port\" : 43567, "
" \"ncr-protocol\" : \"UDP\", "
" \"ncr-format\" : \"JSON\", "
- " \"remove-on-renew\" : false, "
" \"always-include-fqdn\" : false, "
" \"override-no-update\" : false, "
" \"override-client-update\" : false, "
@@ -779,18 +776,17 @@ TEST_F(ParseConfigTest, validD2Config) {
rcode = parseConfiguration(config_str2);
ASSERT_TRUE(rcode == 0) << error_text_;
- // Verify that DHCP-DDNS is enabled and we can fetch the configuration.
- EXPECT_TRUE(CfgMgr::instance().ddnsEnabled());
+ // Verify that DHCP-DDNS is disabled and we can fetch the configuration.
+ EXPECT_FALSE(CfgMgr::instance().ddnsEnabled());
ASSERT_NO_THROW(d2_client_config = CfgMgr::instance().getD2ClientConfig());
ASSERT_TRUE(d2_client_config);
// Verify that the configuration values are as expected.
- EXPECT_TRUE(d2_client_config->getEnableUpdates());
+ EXPECT_FALSE(d2_client_config->getEnableUpdates());
EXPECT_EQ("2001:db8::", d2_client_config->getServerIp().toText());
EXPECT_EQ(43567, d2_client_config->getServerPort());
EXPECT_EQ(dhcp_ddns::NCR_UDP, d2_client_config->getNcrProtocol());
EXPECT_EQ(dhcp_ddns::FMT_JSON, d2_client_config->getNcrFormat());
- EXPECT_FALSE(d2_client_config->getRemoveOnRenew());
EXPECT_FALSE(d2_client_config->getAlwaysIncludeFqdn());
EXPECT_FALSE(d2_client_config->getOverrideNoUpdate());
EXPECT_FALSE(d2_client_config->getOverrideClientUpdate());
@@ -842,7 +838,6 @@ TEST_F(ParseConfigTest, invalidD2Config) {
" \"server-port\" : 53001, "
" \"ncr-protocol\" : \"UDP\", "
" \"ncr-format\" : \"JSON\", "
- " \"remove-on-renew\" : true, "
" \"always-include-fqdn\" : true, "
" \"override-no-update\" : true, "
" \"override-client-update\" : true, "
@@ -859,7 +854,6 @@ TEST_F(ParseConfigTest, invalidD2Config) {
" \"server-port\" : 53001, "
" \"ncr-protocol\" : \"UDP\", "
" \"ncr-format\" : \"JSON\", "
- " \"remove-on-renew\" : true, "
" \"always-include-fqdn\" : true, "
" \"override-no-update\" : true, "
" \"override-client-update\" : true, "
@@ -876,7 +870,6 @@ TEST_F(ParseConfigTest, invalidD2Config) {
" \"server-port\" : 53001, "
" \"ncr-protocol\" : \"Bogus\", "
" \"ncr-format\" : \"JSON\", "
- " \"remove-on-renew\" : true, "
" \"always-include-fqdn\" : true, "
" \"override-no-update\" : true, "
" \"override-client-update\" : true, "
@@ -893,7 +886,6 @@ TEST_F(ParseConfigTest, invalidD2Config) {
" \"server-port\" : 53001, "
" \"ncr-protocol\" : \"TCP\", "
" \"ncr-format\" : \"JSON\", "
- " \"remove-on-renew\" : true, "
" \"always-include-fqdn\" : true, "
" \"override-no-update\" : true, "
" \"override-client-update\" : true, "
@@ -910,7 +902,6 @@ TEST_F(ParseConfigTest, invalidD2Config) {
" \"server-port\" : 53001, "
" \"ncr-protocol\" : \"UDP\", "
" \"ncr-format\" : \"Bogus\", "
- " \"remove-on-renew\" : true, "
" \"always-include-fqdn\" : true, "
" \"override-no-update\" : true, "
" \"override-client-update\" : true, "
@@ -927,7 +918,6 @@ TEST_F(ParseConfigTest, invalidD2Config) {
// " \"server-port\" : 53001, "
" \"ncr-protocol\" : \"UDP\", "
" \"ncr-format\" : \"JSON\", "
- " \"remove-on-renew\" : true, "
" \"always-include-fqdn\" : true, "
" \"override-no-update\" : true, "
" \"override-client-update\" : true, "
@@ -1267,3 +1257,90 @@ TEST_F(ParserContextTest, copyConstruct) {
TEST_F(ParserContextTest, copyConstructNull) {
testCopyAssignmentNull(true);
}
+
+/// @brief Checks that a valid relay info structure for IPv4 can be handled
+TEST_F(ParseConfigTest, validRelayInfo4) {
+
+ // Relay information structure. Very simple for now.
+ std::string config_str =
+ " {"
+ " \"ip-address\" : \"192.0.2.1\""
+ " }";
+ ElementPtr json = Element::fromJSON(config_str);
+
+ // Invalid config (wrong family type of the ip-address field)
+ std::string config_str_bogus1 =
+ " {"
+ " \"ip-address\" : \"2001:db8::1\""
+ " }";
+ ElementPtr json_bogus1 = Element::fromJSON(config_str_bogus1);
+
+ // Invalid config (that thing is not an IPv4 address)
+ std::string config_str_bogus2 =
+ " {"
+ " \"ip-address\" : \"256.345.123.456\""
+ " }";
+ ElementPtr json_bogus2 = Element::fromJSON(config_str_bogus2);
+
+ // We need to set the default ip-address to something.
+ Subnet::RelayInfoPtr result(new Subnet::RelayInfo(asiolink::IOAddress("0.0.0.0")));
+
+ boost::shared_ptr<RelayInfoParser> parser;
+
+ // Subnet4 parser will pass 0.0.0.0 to the RelayInfoParser
+ EXPECT_NO_THROW(parser.reset(new RelayInfoParser("ignored", result,
+ Option::V4)));
+ EXPECT_NO_THROW(parser->build(json));
+ EXPECT_NO_THROW(parser->commit());
+
+ EXPECT_EQ("192.0.2.1", result->addr_.toText());
+
+ // Let's check negative scenario (wrong family type)
+ EXPECT_THROW(parser->build(json_bogus1), DhcpConfigError);
+
+ // Let's check negative scenario (too large byte values in pseudo-IPv4 addr)
+ EXPECT_THROW(parser->build(json_bogus2), DhcpConfigError);
+}
+
+/// @brief Checks that a valid relay info structure for IPv6 can be handled
+TEST_F(ParseConfigTest, validRelayInfo6) {
+
+ // Relay information structure. Very simple for now.
+ std::string config_str =
+ " {"
+ " \"ip-address\" : \"2001:db8::1\""
+ " }";
+ ElementPtr json = Element::fromJSON(config_str);
+
+ // Invalid config (wrong family type of the ip-address field
+ std::string config_str_bogus1 =
+ " {"
+ " \"ip-address\" : \"192.0.2.1\""
+ " }";
+ ElementPtr json_bogus1 = Element::fromJSON(config_str_bogus1);
+
+ // That IPv6 address doesn't look right
+ std::string config_str_bogus2 =
+ " {"
+ " \"ip-address\" : \"2001:db8:::4\""
+ " }";
+ ElementPtr json_bogus2 = Element::fromJSON(config_str_bogus2);
+
+ // We need to set the default ip-address to something.
+ Subnet::RelayInfoPtr result(new Subnet::RelayInfo(asiolink::IOAddress("::")));
+
+ boost::shared_ptr<RelayInfoParser> parser;
+ // Subnet4 parser will pass :: to the RelayInfoParser
+ EXPECT_NO_THROW(parser.reset(new RelayInfoParser("ignored", result,
+ Option::V6)));
+ EXPECT_NO_THROW(parser->build(json));
+ EXPECT_NO_THROW(parser->commit());
+
+ EXPECT_EQ("2001:db8::1", result->addr_.toText());
+
+ // Let's check negative scenario (wrong family type)
+ EXPECT_THROW(parser->build(json_bogus1), DhcpConfigError);
+
+ // Unparseable text that looks like IPv6 address, but has too many colons
+ EXPECT_THROW(parser->build(json_bogus2), DhcpConfigError);
+}
diff --git a/src/lib/dhcpsrv/tests/lease_mgr_unittest.cc b/src/lib/dhcpsrv/tests/lease_mgr_unittest.cc
index ab5a8b2..f8a4aed 100644
--- a/src/lib/dhcpsrv/tests/lease_mgr_unittest.cc
+++ b/src/lib/dhcpsrv/tests/lease_mgr_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright (C) 2012-2013 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2012-2014 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
@@ -251,17 +251,6 @@ public:
namespace {
-/// Hardware address used by different tests.
-const uint8_t HWADDR[] = {0x08, 0x00, 0x2b, 0x02, 0x3f, 0x4e};
-/// Client id used by different tests.
-const uint8_t CLIENTID[] = {0x17, 0x34, 0xe2, 0xff, 0x09, 0x92, 0x54};
-/// Valid lifetime value used by different tests.
-const uint32_t VALID_LIFETIME = 500;
-/// Subnet ID used by different tests.
-const uint32_t SUBNET_ID = 42;
-/// IAID value used by different tests.
-const uint32_t IAID = 7;
-
/// @brief getParameter test
///
/// This test checks if the LeaseMgr can be instantiated and that it
@@ -320,616 +309,6 @@ TEST_F(LeaseMgrTest, getLease6) {
// are purely virtual, so we would only call ConcreteLeaseMgr methods.
// Those methods are just stubs that do not return anything.
-/// @brief Lease4 Constructor Test
-///
-/// Lease4 is also defined in lease_mgr.h, so is tested in this file as well.
-// This test checks if the Lease4 structure can be instantiated correctly
-TEST(Lease4, constructor) {
-
- // Random values for the tests
- const uint8_t HWADDR[] = {0x08, 0x00, 0x2b, 0x02, 0x3f, 0x4e};
- std::vector<uint8_t> hwaddr(HWADDR, HWADDR + sizeof(HWADDR));
-
- const uint8_t CLIENTID[] = {0x17, 0x34, 0xe2, 0xff, 0x09, 0x92, 0x54};
- std::vector<uint8_t> clientid_vec(CLIENTID, CLIENTID + sizeof(CLIENTID));
- ClientId clientid(clientid_vec);
-
- // ...and a time
- const time_t current_time = time(NULL);
-
- // Other random constants.
- const uint32_t SUBNET_ID = 42;
- const uint32_t VALID_LIFETIME = 500;
-
- // We want to check that various addresses work, so let's iterate over
- // these.
- const uint32_t ADDRESS[] = {
- 0x00000000, 0x01020304, 0x7fffffff, 0x80000000, 0x80000001, 0xffffffff
- };
-
- for (int i = 0; i < sizeof(ADDRESS) / sizeof(ADDRESS[0]); ++i) {
-
- // Create the lease
- Lease4 lease(ADDRESS[i], HWADDR, sizeof(HWADDR),
- CLIENTID, sizeof(CLIENTID), VALID_LIFETIME, 0, 0,
- current_time, SUBNET_ID, true, true,
- "hostname.example.com.");
-
- EXPECT_EQ(ADDRESS[i], static_cast<uint32_t>(lease.addr_));
- EXPECT_EQ(0, lease.ext_);
- EXPECT_TRUE(hwaddr == lease.hwaddr_);
- EXPECT_TRUE(clientid == *lease.client_id_);
- EXPECT_EQ(0, lease.t1_);
- EXPECT_EQ(0, lease.t2_);
- EXPECT_EQ(VALID_LIFETIME, lease.valid_lft_);
- EXPECT_EQ(current_time, lease.cltt_);
- EXPECT_EQ(SUBNET_ID, lease.subnet_id_);
- EXPECT_FALSE(lease.fixed_);
- EXPECT_EQ("hostname.example.com.", lease.hostname_);
- EXPECT_TRUE(lease.fqdn_fwd_);
- EXPECT_TRUE(lease.fqdn_rev_);
- EXPECT_TRUE(lease.comments_.empty());
- }
-}
-
-// This test verfies that copy constructor copies Lease4 fields correctly.
-TEST(Lease4, copyConstructor) {
-
- // Random values for the tests
- const uint8_t HWADDR[] = {0x08, 0x00, 0x2b, 0x02, 0x3f, 0x4e};
- std::vector<uint8_t> hwaddr(HWADDR, HWADDR + sizeof(HWADDR));
-
- const uint8_t CLIENTID[] = {0x17, 0x34, 0xe2, 0xff, 0x09, 0x92, 0x54};
- std::vector<uint8_t> clientid_vec(CLIENTID, CLIENTID + sizeof(CLIENTID));
- ClientId clientid(clientid_vec);
-
- // ...and a time
- const time_t current_time = time(NULL);
-
- // Other random constants.
- const uint32_t SUBNET_ID = 42;
- const uint32_t VALID_LIFETIME = 500;
-
- // Create the lease
- Lease4 lease(0xffffffff, HWADDR, sizeof(HWADDR),
- CLIENTID, sizeof(CLIENTID), VALID_LIFETIME, 0, 0, current_time,
- SUBNET_ID);
-
- // Use copy constructor to copy the lease.
- Lease4 copied_lease(lease);
-
- // Both leases should be now equal. When doing this check we assume that
- // the equality operator works correctly.
- EXPECT_TRUE(lease == copied_lease);
- // Client IDs are equal, but they should be in two distinct pointers.
- EXPECT_FALSE(lease.client_id_ == copied_lease.client_id_);
-}
-
-// This test verfies that the assignment operator copies all Lease4 fields
-// correctly.
-TEST(Lease4, operatorAssign) {
-
- // Random values for the tests
- const uint8_t HWADDR[] = {0x08, 0x00, 0x2b, 0x02, 0x3f, 0x4e};
- std::vector<uint8_t> hwaddr(HWADDR, HWADDR + sizeof(HWADDR));
-
- const uint8_t CLIENTID[] = {0x17, 0x34, 0xe2, 0xff, 0x09, 0x92, 0x54};
- std::vector<uint8_t> clientid_vec(CLIENTID, CLIENTID + sizeof(CLIENTID));
- ClientId clientid(clientid_vec);
-
- // ...and a time
- const time_t current_time = time(NULL);
-
- // Other random constants.
- const uint32_t SUBNET_ID = 42;
- const uint32_t VALID_LIFETIME = 500;
-
- // Create the lease
- Lease4 lease(0xffffffff, HWADDR, sizeof(HWADDR),
- CLIENTID, sizeof(CLIENTID), VALID_LIFETIME, 0, 0, current_time,
- SUBNET_ID);
-
- // Use assignment operator to assign the lease.
- Lease4 copied_lease = lease;
-
- // Both leases should be now equal. When doing this check we assume that
- // the equality operator works correctly.
- EXPECT_TRUE(lease == copied_lease);
- // Client IDs are equal, but they should be in two distinct pointers.
- EXPECT_FALSE(lease.client_id_ == copied_lease.client_id_);
-}
-
-// This test verifies that the matches() returns true if two leases differ
-// by values other than address, HW address, Client ID and ext_.
-TEST(Lease4, matches) {
- // Create two leases which share the same address, HW address, client id
- // and ext_ value.
- const time_t current_time = time(NULL);
- Lease4 lease1(IOAddress("192.0.2.3"), HWADDR, sizeof(HWADDR), CLIENTID,
- sizeof(CLIENTID), VALID_LIFETIME, current_time, 0, 0,
- SUBNET_ID);
- lease1.hostname_ = "lease1.example.com.";
- lease1.fqdn_fwd_ = true;
- lease1.fqdn_rev_ = true;
- Lease4 lease2(IOAddress("192.0.2.3"), HWADDR, sizeof(HWADDR), CLIENTID,
- sizeof(CLIENTID), VALID_LIFETIME + 10, current_time - 10,
- 100, 200, SUBNET_ID);
- lease2.hostname_ = "lease2.example.com.";
- lease2.fqdn_fwd_ = false;
- lease2.fqdn_rev_ = true;
-
- // Leases should match.
- EXPECT_TRUE(lease1.matches(lease2));
- EXPECT_TRUE(lease2.matches(lease1));
-
- // Change address, leases should not match anymore.
- lease1.addr_ = IOAddress("192.0.2.4");
- EXPECT_FALSE(lease1.matches(lease2));
- lease1.addr_ = lease2.addr_;
-
- // Change HW address, leases should not match.
- lease1.hwaddr_[1] += 1;
- EXPECT_FALSE(lease1.matches(lease2));
- lease1.hwaddr_ = lease2.hwaddr_;
-
- // Chanage client id, leases should not match.
- std::vector<uint8_t> client_id = lease1.client_id_->getClientId();
- client_id[1] += 1;
- lease1.client_id_.reset(new ClientId(client_id));
- EXPECT_FALSE(lease1.matches(lease2));
- lease1.client_id_ = lease2.client_id_;
-
- // Change ext_, leases should not match.
- lease1.ext_ += 1;
- EXPECT_FALSE(lease1.matches(lease2));
- lease1.ext_ = lease2.ext_;
-}
-
-/// @brief Lease4 Equality Test
-///
-/// Checks that the operator==() correctly compares two leases for equality.
-/// As operator!=() is also defined for this class, every check on operator==()
-/// is followed by the reverse check on operator!=().
-TEST(Lease4, operatorEquals) {
-
- // Random values for the tests
- const uint32_t ADDRESS = 0x01020304;
- const uint8_t HWADDR[] = {0x08, 0x00, 0x2b, 0x02, 0x3f, 0x4e};
- std::vector<uint8_t> hwaddr(HWADDR, HWADDR + sizeof(HWADDR));
- const uint8_t CLIENTID[] = {0x17, 0x34, 0xe2, 0xff, 0x09, 0x92, 0x54};
- std::vector<uint8_t> clientid_vec(CLIENTID, CLIENTID + sizeof(CLIENTID));
- ClientId clientid(clientid_vec);
- const time_t current_time = time(NULL);
- const uint32_t SUBNET_ID = 42;
- const uint32_t VALID_LIFETIME = 500;
-
- // Check when the leases are equal.
- Lease4 lease1(ADDRESS, HWADDR, sizeof(HWADDR),
- CLIENTID, sizeof(CLIENTID), VALID_LIFETIME, current_time, 0,
- 0, SUBNET_ID);
- Lease4 lease2(ADDRESS, HWADDR, sizeof(HWADDR),
- CLIENTID, sizeof(CLIENTID), VALID_LIFETIME, current_time, 0, 0,
- SUBNET_ID);
- EXPECT_TRUE(lease1 == lease2);
- EXPECT_FALSE(lease1 != lease2);
-
- // Now vary individual fields in a lease and check that the leases compare
- // not equal in every case.
- lease1.addr_ = IOAddress(ADDRESS + 1);
- EXPECT_FALSE(lease1 == lease2);
- EXPECT_TRUE(lease1 != lease2);
- lease1.addr_ = lease2.addr_;
- EXPECT_TRUE(lease1 == lease2); // Check that the reversion has made the
- EXPECT_FALSE(lease1 != lease2); // ... leases equal
-
- ++lease1.ext_;
- EXPECT_FALSE(lease1 == lease2);
- EXPECT_TRUE(lease1 != lease2);
- lease1.ext_ = lease2.ext_;
- EXPECT_TRUE(lease1 == lease2); // Check that the reversion has made the
- EXPECT_FALSE(lease1 != lease2); // ... leases equal
-
- ++lease1.hwaddr_[0];
- EXPECT_FALSE(lease1 == lease2);
- EXPECT_TRUE(lease1 != lease2);
- lease1.hwaddr_ = lease2.hwaddr_;
- EXPECT_TRUE(lease1 == lease2); // Check that the reversion has made the
- EXPECT_FALSE(lease1 != lease2); // ... leases equal
-
- ++clientid_vec[0];
- lease1.client_id_.reset(new ClientId(clientid_vec));
- EXPECT_FALSE(lease1 == lease2);
- EXPECT_TRUE(lease1 != lease2);
- --clientid_vec[0];
- lease1.client_id_.reset(new ClientId(clientid_vec));
- EXPECT_TRUE(lease1 == lease2); // Check that the reversion has made the
- EXPECT_FALSE(lease1 != lease2); // ... leases equal
-
- ++lease1.t1_;
- EXPECT_FALSE(lease1 == lease2);
- EXPECT_TRUE(lease1 != lease2);
- lease1.t1_ = lease2.t1_;
- EXPECT_TRUE(lease1 == lease2); // Check that the reversion has made the
- EXPECT_FALSE(lease1 != lease2); // ... leases equal
-
- ++lease1.t2_;
- EXPECT_FALSE(lease1 == lease2);
- EXPECT_TRUE(lease1 != lease2);
- lease1.t2_ = lease2.t2_;
- EXPECT_TRUE(lease1 == lease2); // Check that the reversion has made the
- EXPECT_FALSE(lease1 != lease2); // ... leases equal
-
- ++lease1.valid_lft_;
- EXPECT_FALSE(lease1 == lease2);
- EXPECT_TRUE(lease1 != lease2);
- lease1.valid_lft_ = lease2.valid_lft_;
- EXPECT_TRUE(lease1 == lease2); // Check that the reversion has made the
- EXPECT_FALSE(lease1 != lease2); // ... leases equal
-
- ++lease1.cltt_;
- EXPECT_FALSE(lease1 == lease2);
- EXPECT_TRUE(lease1 != lease2);
- lease1.cltt_ = lease2.cltt_;
- EXPECT_TRUE(lease1 == lease2); // Check that the reversion has made the
- EXPECT_FALSE(lease1 != lease2); // ... leases equal
-
- ++lease1.subnet_id_;
- EXPECT_FALSE(lease1 == lease2);
- EXPECT_TRUE(lease1 != lease2);
- lease1.subnet_id_ = lease2.subnet_id_;
- EXPECT_TRUE(lease1 == lease2); // Check that the reversion has made the
- EXPECT_FALSE(lease1 != lease2); // ... leases equal
-
- lease1.fixed_ = !lease1.fixed_;
- EXPECT_FALSE(lease1 == lease2);
- EXPECT_TRUE(lease1 != lease2);
- lease1.fixed_ = lease2.fixed_;
- EXPECT_TRUE(lease1 == lease2); // Check that the reversion has made the
- EXPECT_FALSE(lease1 != lease2); // ... leases equal
-
- lease1.hostname_ += string("Something random");
- EXPECT_FALSE(lease1 == lease2);
- EXPECT_TRUE(lease1 != lease2);
- lease1.hostname_ = lease2.hostname_;
- EXPECT_TRUE(lease1 == lease2); // Check that the reversion has made the
- EXPECT_FALSE(lease1 != lease2); // ... leases equal
-
- lease1.fqdn_fwd_ = !lease1.fqdn_fwd_;
- EXPECT_FALSE(lease1 == lease2);
- EXPECT_TRUE(lease1 != lease2);
- lease1.fqdn_fwd_ = lease2.fqdn_fwd_;
- EXPECT_TRUE(lease1 == lease2); // Check that the reversion has made the
- EXPECT_FALSE(lease1 != lease2); // ... leases equal
-
- lease1.fqdn_rev_ = !lease1.fqdn_rev_;
- EXPECT_FALSE(lease1 == lease2);
- EXPECT_TRUE(lease1 != lease2);
- lease1.fqdn_rev_ = lease2.fqdn_rev_;
- EXPECT_TRUE(lease1 == lease2); // Check that the reversion has made the
- EXPECT_FALSE(lease1 != lease2); // ... leases equal
-
- lease1.comments_ += string("Something random");
- EXPECT_FALSE(lease1 == lease2);
- EXPECT_TRUE(lease1 != lease2);
- lease1.comments_ = lease2.comments_;
- EXPECT_TRUE(lease1 == lease2); // Check that the reversion has made the
- EXPECT_FALSE(lease1 != lease2); // ... leases equal
-}
-
-
-
-// Lease6 is also defined in lease_mgr.h, so is tested in this file as well.
-// This test checks if the Lease6 structure can be instantiated correctly
-TEST(Lease6, Lease6ConstructorDefault) {
-
- // check a variety of addresses with different bits set.
- const char* ADDRESS[] = {
- "::", "::1", "2001:db8:1::456",
- "7fff:ffff:ffff:ffff:ffff:ffff:ffff:ffff",
- "8000::", "8000::1",
- "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"
- };
-
- // Other values
- uint8_t llt[] = {0, 1, 2, 3, 4, 5, 6, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf};
- DuidPtr duid(new DUID(llt, sizeof(llt)));
- uint32_t iaid = 7; // Just a number
- SubnetID subnet_id = 8; // Just another number
-
- for (int i = 0; i < sizeof(ADDRESS) / sizeof(ADDRESS[0]); ++i) {
- IOAddress addr(ADDRESS[i]);
- Lease6Ptr lease(new Lease6(Lease::TYPE_NA, addr,
- duid, iaid, 100, 200, 50, 80,
- subnet_id));
-
- EXPECT_TRUE(lease->addr_ == addr);
- EXPECT_TRUE(*lease->duid_ == *duid);
- EXPECT_TRUE(lease->iaid_ == iaid);
- EXPECT_TRUE(lease->subnet_id_ == subnet_id);
- EXPECT_TRUE(lease->type_ == Lease::TYPE_NA);
- EXPECT_TRUE(lease->preferred_lft_ == 100);
- EXPECT_TRUE(lease->valid_lft_ == 200);
- EXPECT_TRUE(lease->t1_ == 50);
- EXPECT_TRUE(lease->t2_ == 80);
- EXPECT_FALSE(lease->fqdn_fwd_);
- EXPECT_FALSE(lease->fqdn_rev_);
- EXPECT_TRUE(lease->hostname_.empty());
-
- }
-
- // Lease6 must be instantiated with a DUID, not with NULL pointer
- IOAddress addr(ADDRESS[0]);
- Lease6Ptr lease2;
- EXPECT_THROW(lease2.reset(new Lease6(Lease::TYPE_NA, addr,
- DuidPtr(), iaid, 100, 200, 50, 80,
- subnet_id)), InvalidOperation);
-}
-
-// This test verifies that the Lease6 constructor which accepts FQDN data,
-// sets the data correctly for the lease.
-TEST(Lease6, Lease6ConstructorWithFQDN) {
-
- // check a variety of addresses with different bits set.
- const char* ADDRESS[] = {
- "::", "::1", "2001:db8:1::456",
- "7fff:ffff:ffff:ffff:ffff:ffff:ffff:ffff",
- "8000::", "8000::1",
- "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"
- };
-
- // Other values
- uint8_t llt[] = {0, 1, 2, 3, 4, 5, 6, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf};
- DuidPtr duid(new DUID(llt, sizeof(llt)));
- uint32_t iaid = 7; // Just a number
- SubnetID subnet_id = 8; // Just another number
-
- for (int i = 0; i < sizeof(ADDRESS) / sizeof(ADDRESS[0]); ++i) {
- IOAddress addr(ADDRESS[i]);
- Lease6Ptr lease(new Lease6(Lease::TYPE_NA, addr,
- duid, iaid, 100, 200, 50, 80, subnet_id,
- true, true, "host.example.com."));
-
- EXPECT_TRUE(lease->addr_ == addr);
- EXPECT_TRUE(*lease->duid_ == *duid);
- EXPECT_TRUE(lease->iaid_ == iaid);
- EXPECT_TRUE(lease->subnet_id_ == subnet_id);
- EXPECT_TRUE(lease->type_ == Lease::TYPE_NA);
- EXPECT_TRUE(lease->preferred_lft_ == 100);
- EXPECT_TRUE(lease->valid_lft_ == 200);
- EXPECT_TRUE(lease->t1_ == 50);
- EXPECT_TRUE(lease->t2_ == 80);
- EXPECT_TRUE(lease->fqdn_fwd_);
- EXPECT_TRUE(lease->fqdn_rev_);
- EXPECT_EQ("host.example.com.", lease->hostname_);
- }
- // Lease6 must be instantiated with a DUID, not with NULL pointer
- IOAddress addr(ADDRESS[0]);
- Lease6Ptr lease2;
- EXPECT_THROW(lease2.reset(new Lease6(Lease::TYPE_NA, addr,
- DuidPtr(), iaid, 100, 200, 50, 80,
- subnet_id)), InvalidOperation);
-}
-
-// This test verifies that the matches() function returns true if two leases
-// differ by values other than address, type, prefix length, IAID and DUID.
-TEST(Lease6, matches) {
-
- // Create two matching leases.
- uint8_t llt[] = {0, 1, 2, 3, 4, 5, 6, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf};
- DuidPtr duid(new DUID(llt, sizeof(llt)));
-
- Lease6 lease1(Lease6::TYPE_NA, IOAddress("2001:db8:1::1"), duid,
- IAID, 100, 200, 50, 80,
- SUBNET_ID);
- lease1.hostname_ = "lease1.example.com.";
- lease1.fqdn_fwd_ = true;
- lease1.fqdn_rev_ = true;
- Lease6 lease2(Lease6::TYPE_NA, IOAddress("2001:db8:1::1"), duid,
- IAID, 200, 300, 90, 70,
- SUBNET_ID);
- lease2.hostname_ = "lease1.example.com.";
- lease2.fqdn_fwd_ = false;
- lease2.fqdn_rev_ = true;
-
- EXPECT_TRUE(lease1.matches(lease2));
-
- // Modify each value used to match both leases, and make sure that
- // leases don't match.
-
- // Modify address.
- lease1.addr_ = IOAddress("2001:db8:1::2");
- EXPECT_FALSE(lease1.matches(lease2));
- lease1.addr_ = lease2.addr_;
-
- // Modify lease type.
- lease1.type_ = Lease6::TYPE_TA;
- EXPECT_FALSE(lease1.matches(lease2));
- lease1.type_ = lease2.type_;
-
- // Modify prefix length.
- lease1.prefixlen_ += 1;
- EXPECT_FALSE(lease1.matches(lease2));
- lease1.prefixlen_ = lease2.prefixlen_;
-
- // Modify IAID.
- lease1.iaid_ += 1;
- EXPECT_FALSE(lease1.matches(lease2));
- lease1.iaid_ = lease2.iaid_;
-
- // Modify DUID.
- llt[1] += 1;
- duid.reset(new DUID(llt, sizeof(llt)));
- lease1.duid_ = duid;
- EXPECT_FALSE(lease1.matches(lease2));
- lease1.duid_ = lease2.duid_;
-}
-
-/// @brief Lease6 Equality Test
-///
-/// Checks that the operator==() correctly compares two leases for equality.
-/// As operator!=() is also defined for this class, every check on operator==()
-/// is followed by the reverse check on operator!=().
-TEST(Lease6, OperatorEquals) {
-
- // check a variety of addresses with different bits set.
- const IOAddress addr("2001:db8:1::456");
- uint8_t duid_array[] = {0, 1, 2, 3, 4, 5, 6, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf};
- DuidPtr duid(new DUID(duid_array, sizeof(duid_array)));
- uint32_t iaid = 7; // just a number
- SubnetID subnet_id = 8; // just another number
-
- // Check for equality.
- Lease6 lease1(Lease::TYPE_NA, addr, duid, iaid, 100, 200, 50, 80,
- subnet_id);
- Lease6 lease2(Lease::TYPE_NA, addr, duid, iaid, 100, 200, 50, 80,
- subnet_id);
-
- // cltt_ constructs with time(NULL), make sure they are always equal
- lease1.cltt_ = lease2.cltt_;
-
- EXPECT_TRUE(lease1 == lease2);
- EXPECT_FALSE(lease1 != lease2);
-
- // Go through and alter all the fields one by one
-
- lease1.addr_ = IOAddress("::1");
- EXPECT_FALSE(lease1 == lease2);
- EXPECT_TRUE(lease1 != lease2);
- lease1.addr_ = lease2.addr_;
- EXPECT_TRUE(lease1 == lease2); // Check that the reversion has made the
- EXPECT_FALSE(lease1 != lease2); // ... leases equal
-
- lease1.type_ = Lease::TYPE_PD;
- EXPECT_FALSE(lease1 == lease2);
- EXPECT_TRUE(lease1 != lease2);
- lease1.type_ = lease2.type_;
- EXPECT_TRUE(lease1 == lease2); // Check that the reversion has made the
- EXPECT_FALSE(lease1 != lease2); // ... leases equal
-
- ++lease1.prefixlen_;
- EXPECT_FALSE(lease1 == lease2);
- EXPECT_TRUE(lease1 != lease2);
- lease1.prefixlen_ = lease2.prefixlen_;
- EXPECT_TRUE(lease1 == lease2); // Check that the reversion has made the
- EXPECT_FALSE(lease1 != lease2); // ... leases equal
-
- ++lease1.iaid_;
- EXPECT_FALSE(lease1 == lease2);
- EXPECT_TRUE(lease1 != lease2);
- lease1.iaid_ = lease2.iaid_;
- EXPECT_TRUE(lease1 == lease2); // Check that the reversion has made the
- EXPECT_FALSE(lease1 != lease2); // ... leases equal
-
- ++duid_array[0];
- lease1.duid_.reset(new DUID(duid_array, sizeof(duid_array)));
- EXPECT_FALSE(lease1 == lease2);
- EXPECT_TRUE(lease1 != lease2);
- --duid_array[0];
- lease1.duid_.reset(new DUID(duid_array, sizeof(duid_array)));
- EXPECT_TRUE(lease1 == lease2); // Check that the reversion has made the
- EXPECT_FALSE(lease1 != lease2); // ... leases equal
-
- ++lease1.preferred_lft_;
- EXPECT_FALSE(lease1 == lease2);
- EXPECT_TRUE(lease1 != lease2);
- lease1.preferred_lft_ = lease2.preferred_lft_;
- EXPECT_TRUE(lease1 == lease2); // Check that the reversion has made the
- EXPECT_FALSE(lease1 != lease2); // ... leases equal
-
- ++lease1.valid_lft_;
- EXPECT_FALSE(lease1 == lease2);
- EXPECT_TRUE(lease1 != lease2);
- lease1.valid_lft_ = lease2.valid_lft_;
- EXPECT_TRUE(lease1 == lease2); // Check that the reversion has made the
- EXPECT_FALSE(lease1 != lease2); // ... leases equal
-
- ++lease1.t1_;
- EXPECT_FALSE(lease1 == lease2);
- EXPECT_TRUE(lease1 != lease2);
- lease1.t1_ = lease2.t1_;
- EXPECT_TRUE(lease1 == lease2); // Check that the reversion has made the
- EXPECT_FALSE(lease1 != lease2); // ... leases equal
-
- ++lease1.t2_;
- EXPECT_FALSE(lease1 == lease2);
- EXPECT_TRUE(lease1 != lease2);
- lease1.t2_ = lease2.t2_;
- EXPECT_TRUE(lease1 == lease2); // Check that the reversion has made the
- EXPECT_FALSE(lease1 != lease2); // ... leases equal
-
- ++lease1.cltt_;
- EXPECT_FALSE(lease1 == lease2);
- EXPECT_TRUE(lease1 != lease2);
- lease1.cltt_ = lease2.cltt_;
- EXPECT_TRUE(lease1 == lease2); // Check that the reversion has made the
- EXPECT_FALSE(lease1 != lease2); // ... leases equal
-
- ++lease1.subnet_id_;
- EXPECT_FALSE(lease1 == lease2);
- EXPECT_TRUE(lease1 != lease2);
- lease1.subnet_id_ = lease2.subnet_id_;
- EXPECT_TRUE(lease1 == lease2); // Check that the reversion has made the
- EXPECT_FALSE(lease1 != lease2); // ... leases equal
-
- lease1.fixed_ = !lease1.fixed_;
- EXPECT_FALSE(lease1 == lease2);
- EXPECT_TRUE(lease1 != lease2);
- lease1.fixed_ = lease2.fixed_;
- EXPECT_TRUE(lease1 == lease2); // Check that the reversion has made the
- EXPECT_FALSE(lease1 != lease2); // ... leases equal
-
- lease1.hostname_ += string("Something random");
- EXPECT_FALSE(lease1 == lease2);
- EXPECT_TRUE(lease1 != lease2);
- lease1.hostname_ = lease2.hostname_;
- EXPECT_TRUE(lease1 == lease2); // Check that the reversion has made the
- EXPECT_FALSE(lease1 != lease2); // ... leases equal
-
- lease1.fqdn_fwd_ = !lease1.fqdn_fwd_;
- EXPECT_FALSE(lease1 == lease2);
- EXPECT_TRUE(lease1 != lease2);
- lease1.fqdn_fwd_ = lease2.fqdn_fwd_;
- EXPECT_TRUE(lease1 == lease2); // Check that the reversion has made the
- EXPECT_FALSE(lease1 != lease2); // ... leases equal
-
- lease1.fqdn_rev_ = !lease1.fqdn_rev_;
- EXPECT_FALSE(lease1 == lease2);
- EXPECT_TRUE(lease1 != lease2);
- lease1.fqdn_rev_ = lease2.fqdn_rev_;
- EXPECT_TRUE(lease1 == lease2); // Check that the reversion has made the
- EXPECT_FALSE(lease1 != lease2); // ... leases equal
-
- lease1.comments_ += string("Something random");
- EXPECT_FALSE(lease1 == lease2);
- EXPECT_TRUE(lease1 != lease2);
- lease1.comments_ = lease2.comments_;
- EXPECT_TRUE(lease1 == lease2); // Check that the reversion has made the
- EXPECT_FALSE(lease1 != lease2); // ... leases equal
-}
-
-// Checks if lease expiration is calculated properly
-TEST(Lease6, Lease6Expired) {
- const IOAddress addr("2001:db8:1::456");
- const uint8_t duid_array[] = {0, 1, 2, 3, 4, 5, 6, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf};
- const DuidPtr duid(new DUID(duid_array, sizeof(duid_array)));
- const uint32_t iaid = 7; // Just a number
- const SubnetID subnet_id = 8; // Just another number
- Lease6 lease(Lease::TYPE_NA, addr, duid, iaid, 100, 200, 50, 80,
- subnet_id);
-
- // Case 1: a second before expiration
- lease.cltt_ = time(NULL) - 100;
- lease.valid_lft_ = 101;
- EXPECT_FALSE(lease.expired());
-
- // Case 2: the lease will expire after this second is concluded
- lease.cltt_ = time(NULL) - 101;
- EXPECT_FALSE(lease.expired());
-
- // Case 3: the lease is expired
- lease.cltt_ = time(NULL) - 102;
- EXPECT_TRUE(lease.expired());
-}
}; // end of anonymous namespace
diff --git a/src/lib/dhcpsrv/tests/lease_unittest.cc b/src/lib/dhcpsrv/tests/lease_unittest.cc
index 6f57f30..4e7451f 100644
--- a/src/lib/dhcpsrv/tests/lease_unittest.cc
+++ b/src/lib/dhcpsrv/tests/lease_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2013-2014 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,23 +13,342 @@
// PERFORMANCE OF THIS SOFTWARE.
#include <config.h>
+#include <asiolink/io_address.h>
#include <dhcp/duid.h>
#include <dhcpsrv/lease.h>
#include <gtest/gtest.h>
#include <vector>
using namespace isc;
+using namespace isc::asiolink;
using namespace isc::dhcp;
namespace {
-// @todo Currently this file contains tests for new functions which return DUID
-// or client identifier. Other tests for Lease objects must be implemented.
-// See http://bind10.isc.org/ticket/3240.
+/// Hardware address used by different tests.
+const uint8_t HWADDR[] = {0x08, 0x00, 0x2b, 0x02, 0x3f, 0x4e};
+/// Client id used by different tests.
+const uint8_t CLIENTID[] = {0x17, 0x34, 0xe2, 0xff, 0x09, 0x92, 0x54};
+/// Valid lifetime value used by different tests.
+const uint32_t VALID_LIFETIME = 500;
+/// Subnet ID used by different tests.
+const uint32_t SUBNET_ID = 42;
+/// IAID value used by different tests.
+const uint32_t IAID = 7;
+
+/// @brief Creates an instance of the lease with certain FQDN data.
+///
+/// @param hostname Hostname.
+/// @param fqdn_fwd Forward FQDN update setting for a created lease.
+/// @param fqdn_rev Reverse FQDN update setting for a created lease.
+///
+/// @return Instance of the created lease.
+Lease4 createLease4(const std::string& hostname, const bool fqdn_fwd,
+ const bool fqdn_rev) {
+ Lease4 lease;
+ lease.hostname_ = hostname;
+ lease.fqdn_fwd_ = fqdn_fwd;
+ lease.fqdn_rev_ = fqdn_rev;
+ return (lease);
+}
+
+/// Lease4 is also defined in lease_mgr.h, so is tested in this file as well.
+// This test checks if the Lease4 structure can be instantiated correctly
+TEST(Lease4, constructor) {
+
+ // Random values for the tests
+ const uint8_t HWADDR[] = {0x08, 0x00, 0x2b, 0x02, 0x3f, 0x4e};
+ std::vector<uint8_t> hwaddr(HWADDR, HWADDR + sizeof(HWADDR));
+
+ const uint8_t CLIENTID[] = {0x17, 0x34, 0xe2, 0xff, 0x09, 0x92, 0x54};
+ std::vector<uint8_t> clientid_vec(CLIENTID, CLIENTID + sizeof(CLIENTID));
+ ClientId clientid(clientid_vec);
+
+ // ...and a time
+ const time_t current_time = time(NULL);
+
+ // Other random constants.
+ const uint32_t SUBNET_ID = 42;
+ const uint32_t VALID_LIFETIME = 500;
+
+ // We want to check that various addresses work, so let's iterate over
+ // these.
+ const uint32_t ADDRESS[] = {
+ 0x00000000, 0x01020304, 0x7fffffff, 0x80000000, 0x80000001, 0xffffffff
+ };
+
+ for (int i = 0; i < sizeof(ADDRESS) / sizeof(ADDRESS[0]); ++i) {
+
+ // Create the lease
+ Lease4 lease(ADDRESS[i], HWADDR, sizeof(HWADDR),
+ CLIENTID, sizeof(CLIENTID), VALID_LIFETIME, 0, 0,
+ current_time, SUBNET_ID, true, true,
+ "hostname.example.com.");
+
+ EXPECT_EQ(ADDRESS[i], static_cast<uint32_t>(lease.addr_));
+ EXPECT_EQ(0, lease.ext_);
+ EXPECT_TRUE(hwaddr == lease.hwaddr_);
+ EXPECT_TRUE(clientid == *lease.client_id_);
+ EXPECT_EQ(0, lease.t1_);
+ EXPECT_EQ(0, lease.t2_);
+ EXPECT_EQ(VALID_LIFETIME, lease.valid_lft_);
+ EXPECT_EQ(current_time, lease.cltt_);
+ EXPECT_EQ(SUBNET_ID, lease.subnet_id_);
+ EXPECT_FALSE(lease.fixed_);
+ EXPECT_EQ("hostname.example.com.", lease.hostname_);
+ EXPECT_TRUE(lease.fqdn_fwd_);
+ EXPECT_TRUE(lease.fqdn_rev_);
+ EXPECT_TRUE(lease.comments_.empty());
+ }
+}
+
+// This test verfies that copy constructor copies Lease4 fields correctly.
+TEST(Lease4, copyConstructor) {
+
+ // Random values for the tests
+ const uint8_t HWADDR[] = {0x08, 0x00, 0x2b, 0x02, 0x3f, 0x4e};
+ std::vector<uint8_t> hwaddr(HWADDR, HWADDR + sizeof(HWADDR));
+
+ const uint8_t CLIENTID[] = {0x17, 0x34, 0xe2, 0xff, 0x09, 0x92, 0x54};
+ std::vector<uint8_t> clientid_vec(CLIENTID, CLIENTID + sizeof(CLIENTID));
+ ClientId clientid(clientid_vec);
+
+ // ...and a time
+ const time_t current_time = time(NULL);
+
+ // Other random constants.
+ const uint32_t SUBNET_ID = 42;
+ const uint32_t VALID_LIFETIME = 500;
+
+ // Create the lease
+ Lease4 lease(0xffffffff, HWADDR, sizeof(HWADDR),
+ CLIENTID, sizeof(CLIENTID), VALID_LIFETIME, 0, 0, current_time,
+ SUBNET_ID);
+
+ // Use copy constructor to copy the lease.
+ Lease4 copied_lease(lease);
+
+ // Both leases should be now equal. When doing this check we assume that
+ // the equality operator works correctly.
+ EXPECT_TRUE(lease == copied_lease);
+ // Client IDs are equal, but they should be in two distinct pointers.
+ EXPECT_FALSE(lease.client_id_ == copied_lease.client_id_);
+}
+
+// This test verfies that the assignment operator copies all Lease4 fields
+// correctly.
+TEST(Lease4, operatorAssign) {
+
+ // Random values for the tests
+ const uint8_t HWADDR[] = {0x08, 0x00, 0x2b, 0x02, 0x3f, 0x4e};
+ std::vector<uint8_t> hwaddr(HWADDR, HWADDR + sizeof(HWADDR));
+
+ const uint8_t CLIENTID[] = {0x17, 0x34, 0xe2, 0xff, 0x09, 0x92, 0x54};
+ std::vector<uint8_t> clientid_vec(CLIENTID, CLIENTID + sizeof(CLIENTID));
+ ClientId clientid(clientid_vec);
+
+ // ...and a time
+ const time_t current_time = time(NULL);
+
+ // Other random constants.
+ const uint32_t SUBNET_ID = 42;
+ const uint32_t VALID_LIFETIME = 500;
+
+ // Create the lease
+ Lease4 lease(0xffffffff, HWADDR, sizeof(HWADDR),
+ CLIENTID, sizeof(CLIENTID), VALID_LIFETIME, 0, 0, current_time,
+ SUBNET_ID);
+
+ // Use assignment operator to assign the lease.
+ Lease4 copied_lease = lease;
+
+ // Both leases should be now equal. When doing this check we assume that
+ // the equality operator works correctly.
+ EXPECT_TRUE(lease == copied_lease);
+ // Client IDs are equal, but they should be in two distinct pointers.
+ EXPECT_FALSE(lease.client_id_ == copied_lease.client_id_);
+}
+
+// This test verifies that the matches() returns true if two leases differ
+// by values other than address, HW address, Client ID and ext_.
+TEST(Lease4, matches) {
+ // Create two leases which share the same address, HW address, client id
+ // and ext_ value.
+ const time_t current_time = time(NULL);
+ Lease4 lease1(IOAddress("192.0.2.3"), HWADDR, sizeof(HWADDR), CLIENTID,
+ sizeof(CLIENTID), VALID_LIFETIME, current_time, 0, 0,
+ SUBNET_ID);
+ lease1.hostname_ = "lease1.example.com.";
+ lease1.fqdn_fwd_ = true;
+ lease1.fqdn_rev_ = true;
+ Lease4 lease2(IOAddress("192.0.2.3"), HWADDR, sizeof(HWADDR), CLIENTID,
+ sizeof(CLIENTID), VALID_LIFETIME + 10, current_time - 10,
+ 100, 200, SUBNET_ID);
+ lease2.hostname_ = "lease2.example.com.";
+ lease2.fqdn_fwd_ = false;
+ lease2.fqdn_rev_ = true;
+
+ // Leases should match.
+ EXPECT_TRUE(lease1.matches(lease2));
+ EXPECT_TRUE(lease2.matches(lease1));
+
+ // Change address, leases should not match anymore.
+ lease1.addr_ = IOAddress("192.0.2.4");
+ EXPECT_FALSE(lease1.matches(lease2));
+ lease1.addr_ = lease2.addr_;
+
+ // Change HW address, leases should not match.
+ lease1.hwaddr_[1] += 1;
+ EXPECT_FALSE(lease1.matches(lease2));
+ lease1.hwaddr_ = lease2.hwaddr_;
+
+ // Chanage client id, leases should not match.
+ std::vector<uint8_t> client_id = lease1.client_id_->getClientId();
+ client_id[1] += 1;
+ lease1.client_id_.reset(new ClientId(client_id));
+ EXPECT_FALSE(lease1.matches(lease2));
+ lease1.client_id_ = lease2.client_id_;
+
+ // Change ext_, leases should not match.
+ lease1.ext_ += 1;
+ EXPECT_FALSE(lease1.matches(lease2));
+ lease1.ext_ = lease2.ext_;
+}
+
+/// @brief Lease4 Equality Test
+///
+/// Checks that the operator==() correctly compares two leases for equality.
+/// As operator!=() is also defined for this class, every check on operator==()
+/// is followed by the reverse check on operator!=().
+TEST(Lease4, operatorEquals) {
+
+ // Random values for the tests
+ const uint32_t ADDRESS = 0x01020304;
+ const uint8_t HWADDR[] = {0x08, 0x00, 0x2b, 0x02, 0x3f, 0x4e};
+ std::vector<uint8_t> hwaddr(HWADDR, HWADDR + sizeof(HWADDR));
+ const uint8_t CLIENTID[] = {0x17, 0x34, 0xe2, 0xff, 0x09, 0x92, 0x54};
+ std::vector<uint8_t> clientid_vec(CLIENTID, CLIENTID + sizeof(CLIENTID));
+ ClientId clientid(clientid_vec);
+ const time_t current_time = time(NULL);
+ const uint32_t SUBNET_ID = 42;
+ const uint32_t VALID_LIFETIME = 500;
+
+ // Check when the leases are equal.
+ Lease4 lease1(ADDRESS, HWADDR, sizeof(HWADDR),
+ CLIENTID, sizeof(CLIENTID), VALID_LIFETIME, current_time, 0,
+ 0, SUBNET_ID);
+ Lease4 lease2(ADDRESS, HWADDR, sizeof(HWADDR),
+ CLIENTID, sizeof(CLIENTID), VALID_LIFETIME, current_time, 0, 0,
+ SUBNET_ID);
+ EXPECT_TRUE(lease1 == lease2);
+ EXPECT_FALSE(lease1 != lease2);
+
+ // Now vary individual fields in a lease and check that the leases compare
+ // not equal in every case.
+ lease1.addr_ = IOAddress(ADDRESS + 1);
+ EXPECT_FALSE(lease1 == lease2);
+ EXPECT_TRUE(lease1 != lease2);
+ lease1.addr_ = lease2.addr_;
+ EXPECT_TRUE(lease1 == lease2); // Check that the reversion has made the
+ EXPECT_FALSE(lease1 != lease2); // ... leases equal
+
+ ++lease1.ext_;
+ EXPECT_FALSE(lease1 == lease2);
+ EXPECT_TRUE(lease1 != lease2);
+ lease1.ext_ = lease2.ext_;
+ EXPECT_TRUE(lease1 == lease2); // Check that the reversion has made the
+ EXPECT_FALSE(lease1 != lease2); // ... leases equal
+
+ ++lease1.hwaddr_[0];
+ EXPECT_FALSE(lease1 == lease2);
+ EXPECT_TRUE(lease1 != lease2);
+ lease1.hwaddr_ = lease2.hwaddr_;
+ EXPECT_TRUE(lease1 == lease2); // Check that the reversion has made the
+ EXPECT_FALSE(lease1 != lease2); // ... leases equal
+
+ ++clientid_vec[0];
+ lease1.client_id_.reset(new ClientId(clientid_vec));
+ EXPECT_FALSE(lease1 == lease2);
+ EXPECT_TRUE(lease1 != lease2);
+ --clientid_vec[0];
+ lease1.client_id_.reset(new ClientId(clientid_vec));
+ EXPECT_TRUE(lease1 == lease2); // Check that the reversion has made the
+ EXPECT_FALSE(lease1 != lease2); // ... leases equal
+
+ ++lease1.t1_;
+ EXPECT_FALSE(lease1 == lease2);
+ EXPECT_TRUE(lease1 != lease2);
+ lease1.t1_ = lease2.t1_;
+ EXPECT_TRUE(lease1 == lease2); // Check that the reversion has made the
+ EXPECT_FALSE(lease1 != lease2); // ... leases equal
+
+ ++lease1.t2_;
+ EXPECT_FALSE(lease1 == lease2);
+ EXPECT_TRUE(lease1 != lease2);
+ lease1.t2_ = lease2.t2_;
+ EXPECT_TRUE(lease1 == lease2); // Check that the reversion has made the
+ EXPECT_FALSE(lease1 != lease2); // ... leases equal
+
+ ++lease1.valid_lft_;
+ EXPECT_FALSE(lease1 == lease2);
+ EXPECT_TRUE(lease1 != lease2);
+ lease1.valid_lft_ = lease2.valid_lft_;
+ EXPECT_TRUE(lease1 == lease2); // Check that the reversion has made the
+ EXPECT_FALSE(lease1 != lease2); // ... leases equal
+
+ ++lease1.cltt_;
+ EXPECT_FALSE(lease1 == lease2);
+ EXPECT_TRUE(lease1 != lease2);
+ lease1.cltt_ = lease2.cltt_;
+ EXPECT_TRUE(lease1 == lease2); // Check that the reversion has made the
+ EXPECT_FALSE(lease1 != lease2); // ... leases equal
+
+ ++lease1.subnet_id_;
+ EXPECT_FALSE(lease1 == lease2);
+ EXPECT_TRUE(lease1 != lease2);
+ lease1.subnet_id_ = lease2.subnet_id_;
+ EXPECT_TRUE(lease1 == lease2); // Check that the reversion has made the
+ EXPECT_FALSE(lease1 != lease2); // ... leases equal
+
+ lease1.fixed_ = !lease1.fixed_;
+ EXPECT_FALSE(lease1 == lease2);
+ EXPECT_TRUE(lease1 != lease2);
+ lease1.fixed_ = lease2.fixed_;
+ EXPECT_TRUE(lease1 == lease2); // Check that the reversion has made the
+ EXPECT_FALSE(lease1 != lease2); // ... leases equal
+
+ lease1.hostname_ += std::string("Something random");
+ EXPECT_FALSE(lease1 == lease2);
+ EXPECT_TRUE(lease1 != lease2);
+ lease1.hostname_ = lease2.hostname_;
+ EXPECT_TRUE(lease1 == lease2); // Check that the reversion has made the
+ EXPECT_FALSE(lease1 != lease2); // ... leases equal
+
+ lease1.fqdn_fwd_ = !lease1.fqdn_fwd_;
+ EXPECT_FALSE(lease1 == lease2);
+ EXPECT_TRUE(lease1 != lease2);
+ lease1.fqdn_fwd_ = lease2.fqdn_fwd_;
+ EXPECT_TRUE(lease1 == lease2); // Check that the reversion has made the
+ EXPECT_FALSE(lease1 != lease2); // ... leases equal
+
+ lease1.fqdn_rev_ = !lease1.fqdn_rev_;
+ EXPECT_FALSE(lease1 == lease2);
+ EXPECT_TRUE(lease1 != lease2);
+ lease1.fqdn_rev_ = lease2.fqdn_rev_;
+ EXPECT_TRUE(lease1 == lease2); // Check that the reversion has made the
+ EXPECT_FALSE(lease1 != lease2); // ... leases equal
+
+ lease1.comments_ += std::string("Something random");
+ EXPECT_FALSE(lease1 == lease2);
+ EXPECT_TRUE(lease1 != lease2);
+ lease1.comments_ = lease2.comments_;
+ EXPECT_TRUE(lease1 == lease2); // Check that the reversion has made the
+ EXPECT_FALSE(lease1 != lease2); // ... leases equal
+}
// Verify that the client id can be returned as a vector object and if client
// id is NULL the empty vector is returned.
-TEST(Lease4Test, getClientIdVector) {
+TEST(Lease4, getClientIdVector) {
// Create a lease.
Lease4 lease;
// By default, the lease should have client id set to NULL. If it doesn't,
@@ -47,9 +366,356 @@ TEST(Lease4Test, getClientIdVector) {
EXPECT_TRUE(returned_vec == client_id_vec);
}
+// Verify the behavior of the function which checks FQDN data for equality.
+TEST(Lease4, hasIdenticalFqdn) {
+ Lease4 lease = createLease4("myhost.example.com.", true, true);
+ EXPECT_TRUE(lease.hasIdenticalFqdn(createLease4("myhost.example.com.",
+ true, true)));
+ EXPECT_FALSE(lease.hasIdenticalFqdn(createLease4("other.example.com.",
+ true, true)));
+ EXPECT_FALSE(lease.hasIdenticalFqdn(createLease4("myhost.example.com.",
+ false, true)));
+ EXPECT_FALSE(lease.hasIdenticalFqdn(createLease4("myhost.example.com.",
+ true, false)));
+ EXPECT_FALSE(lease.hasIdenticalFqdn(createLease4("myhost.example.com.",
+ false, false)));
+ EXPECT_FALSE(lease.hasIdenticalFqdn(createLease4("other.example.com.",
+ false, false)));
+}
+
+/// @brief Creates an instance of the lease with certain FQDN data.
+///
+/// @param hostname Hostname.
+/// @param fqdn_fwd Forward FQDN update setting for a created lease.
+/// @param fqdn_rev Reverse FQDN update setting for a created lease.
+///
+/// @return Instance of the created lease.
+Lease6 createLease6(const std::string& hostname, const bool fqdn_fwd,
+ const bool fqdn_rev) {
+ Lease6 lease;
+ lease.hostname_ = hostname;
+ lease.fqdn_fwd_ = fqdn_fwd;
+ lease.fqdn_rev_ = fqdn_rev;
+ return (lease);
+}
+
+// Lease6 is also defined in lease_mgr.h, so is tested in this file as well.
+// This test checks if the Lease6 structure can be instantiated correctly
+TEST(Lease6, Lease6ConstructorDefault) {
+
+ // check a variety of addresses with different bits set.
+ const char* ADDRESS[] = {
+ "::", "::1", "2001:db8:1::456",
+ "7fff:ffff:ffff:ffff:ffff:ffff:ffff:ffff",
+ "8000::", "8000::1",
+ "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"
+ };
+
+ // Other values
+ uint8_t llt[] = {0, 1, 2, 3, 4, 5, 6, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf};
+ DuidPtr duid(new DUID(llt, sizeof(llt)));
+ uint32_t iaid = 7; // Just a number
+ SubnetID subnet_id = 8; // Just another number
+
+ for (int i = 0; i < sizeof(ADDRESS) / sizeof(ADDRESS[0]); ++i) {
+ IOAddress addr(ADDRESS[i]);
+ Lease6Ptr lease(new Lease6(Lease::TYPE_NA, addr,
+ duid, iaid, 100, 200, 50, 80,
+ subnet_id));
+
+ EXPECT_TRUE(lease->addr_ == addr);
+ EXPECT_TRUE(*lease->duid_ == *duid);
+ EXPECT_TRUE(lease->iaid_ == iaid);
+ EXPECT_TRUE(lease->subnet_id_ == subnet_id);
+ EXPECT_TRUE(lease->type_ == Lease::TYPE_NA);
+ EXPECT_TRUE(lease->preferred_lft_ == 100);
+ EXPECT_TRUE(lease->valid_lft_ == 200);
+ EXPECT_TRUE(lease->t1_ == 50);
+ EXPECT_TRUE(lease->t2_ == 80);
+ EXPECT_FALSE(lease->fqdn_fwd_);
+ EXPECT_FALSE(lease->fqdn_rev_);
+ EXPECT_TRUE(lease->hostname_.empty());
+
+ }
+
+ // Lease6 must be instantiated with a DUID, not with NULL pointer
+ IOAddress addr(ADDRESS[0]);
+ Lease6Ptr lease2;
+ EXPECT_THROW(lease2.reset(new Lease6(Lease::TYPE_NA, addr,
+ DuidPtr(), iaid, 100, 200, 50, 80,
+ subnet_id)), InvalidOperation);
+}
+
+// This test verifies that the Lease6 constructor which accepts FQDN data,
+// sets the data correctly for the lease.
+TEST(Lease6, Lease6ConstructorWithFQDN) {
+
+ // check a variety of addresses with different bits set.
+ const char* ADDRESS[] = {
+ "::", "::1", "2001:db8:1::456",
+ "7fff:ffff:ffff:ffff:ffff:ffff:ffff:ffff",
+ "8000::", "8000::1",
+ "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"
+ };
+
+ // Other values
+ uint8_t llt[] = {0, 1, 2, 3, 4, 5, 6, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf};
+ DuidPtr duid(new DUID(llt, sizeof(llt)));
+ uint32_t iaid = 7; // Just a number
+ SubnetID subnet_id = 8; // Just another number
+
+ for (int i = 0; i < sizeof(ADDRESS) / sizeof(ADDRESS[0]); ++i) {
+ IOAddress addr(ADDRESS[i]);
+ Lease6Ptr lease(new Lease6(Lease::TYPE_NA, addr,
+ duid, iaid, 100, 200, 50, 80, subnet_id,
+ true, true, "host.example.com."));
+
+ EXPECT_TRUE(lease->addr_ == addr);
+ EXPECT_TRUE(*lease->duid_ == *duid);
+ EXPECT_TRUE(lease->iaid_ == iaid);
+ EXPECT_TRUE(lease->subnet_id_ == subnet_id);
+ EXPECT_TRUE(lease->type_ == Lease::TYPE_NA);
+ EXPECT_TRUE(lease->preferred_lft_ == 100);
+ EXPECT_TRUE(lease->valid_lft_ == 200);
+ EXPECT_TRUE(lease->t1_ == 50);
+ EXPECT_TRUE(lease->t2_ == 80);
+ EXPECT_TRUE(lease->fqdn_fwd_);
+ EXPECT_TRUE(lease->fqdn_rev_);
+ EXPECT_EQ("host.example.com.", lease->hostname_);
+ }
+
+ // Lease6 must be instantiated with a DUID, not with NULL pointer
+ IOAddress addr(ADDRESS[0]);
+ Lease6Ptr lease2;
+ EXPECT_THROW(lease2.reset(new Lease6(Lease::TYPE_NA, addr,
+ DuidPtr(), iaid, 100, 200, 50, 80,
+ subnet_id)), InvalidOperation);
+}
+
+// This test verifies that the matches() function returns true if two leases
+// differ by values other than address, type, prefix length, IAID and DUID.
+TEST(Lease6, matches) {
+
+ // Create two matching leases.
+ uint8_t llt[] = {0, 1, 2, 3, 4, 5, 6, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf};
+ DuidPtr duid(new DUID(llt, sizeof(llt)));
+
+ Lease6 lease1(Lease6::TYPE_NA, IOAddress("2001:db8:1::1"), duid,
+ IAID, 100, 200, 50, 80,
+ SUBNET_ID);
+ lease1.hostname_ = "lease1.example.com.";
+ lease1.fqdn_fwd_ = true;
+ lease1.fqdn_rev_ = true;
+ Lease6 lease2(Lease6::TYPE_NA, IOAddress("2001:db8:1::1"), duid,
+ IAID, 200, 300, 90, 70,
+ SUBNET_ID);
+ lease2.hostname_ = "lease1.example.com.";
+ lease2.fqdn_fwd_ = false;
+ lease2.fqdn_rev_ = true;
+
+ EXPECT_TRUE(lease1.matches(lease2));
+
+ // Modify each value used to match both leases, and make sure that
+ // leases don't match.
+
+ // Modify address.
+ lease1.addr_ = IOAddress("2001:db8:1::2");
+ EXPECT_FALSE(lease1.matches(lease2));
+ lease1.addr_ = lease2.addr_;
+
+ // Modify lease type.
+ lease1.type_ = Lease6::TYPE_TA;
+ EXPECT_FALSE(lease1.matches(lease2));
+ lease1.type_ = lease2.type_;
+
+ // Modify prefix length.
+ lease1.prefixlen_ += 1;
+ EXPECT_FALSE(lease1.matches(lease2));
+ lease1.prefixlen_ = lease2.prefixlen_;
+
+ // Modify IAID.
+ lease1.iaid_ += 1;
+ EXPECT_FALSE(lease1.matches(lease2));
+ lease1.iaid_ = lease2.iaid_;
+
+ // Modify DUID.
+ llt[1] += 1;
+ duid.reset(new DUID(llt, sizeof(llt)));
+ lease1.duid_ = duid;
+ EXPECT_FALSE(lease1.matches(lease2));
+ lease1.duid_ = lease2.duid_;
+}
+
+/// @brief Lease6 Equality Test
+///
+/// Checks that the operator==() correctly compares two leases for equality.
+/// As operator!=() is also defined for this class, every check on operator==()
+/// is followed by the reverse check on operator!=().
+TEST(Lease6, OperatorEquals) {
+
+ // check a variety of addresses with different bits set.
+ const IOAddress addr("2001:db8:1::456");
+ uint8_t duid_array[] = {0, 1, 2, 3, 4, 5, 6, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf};
+ DuidPtr duid(new DUID(duid_array, sizeof(duid_array)));
+ uint32_t iaid = 7; // just a number
+ SubnetID subnet_id = 8; // just another number
+
+ // Check for equality.
+ Lease6 lease1(Lease::TYPE_NA, addr, duid, iaid, 100, 200, 50, 80,
+ subnet_id);
+ Lease6 lease2(Lease::TYPE_NA, addr, duid, iaid, 100, 200, 50, 80,
+ subnet_id);
+
+ // cltt_ constructs with time(NULL), make sure they are always equal
+ lease1.cltt_ = lease2.cltt_;
+
+ EXPECT_TRUE(lease1 == lease2);
+ EXPECT_FALSE(lease1 != lease2);
+
+ // Go through and alter all the fields one by one
+
+ lease1.addr_ = IOAddress("::1");
+ EXPECT_FALSE(lease1 == lease2);
+ EXPECT_TRUE(lease1 != lease2);
+ lease1.addr_ = lease2.addr_;
+ EXPECT_TRUE(lease1 == lease2); // Check that the reversion has made the
+ EXPECT_FALSE(lease1 != lease2); // ... leases equal
+
+ lease1.type_ = Lease::TYPE_PD;
+ EXPECT_FALSE(lease1 == lease2);
+ EXPECT_TRUE(lease1 != lease2);
+ lease1.type_ = lease2.type_;
+ EXPECT_TRUE(lease1 == lease2); // Check that the reversion has made the
+ EXPECT_FALSE(lease1 != lease2); // ... leases equal
+
+ ++lease1.prefixlen_;
+ EXPECT_FALSE(lease1 == lease2);
+ EXPECT_TRUE(lease1 != lease2);
+ lease1.prefixlen_ = lease2.prefixlen_;
+ EXPECT_TRUE(lease1 == lease2); // Check that the reversion has made the
+ EXPECT_FALSE(lease1 != lease2); // ... leases equal
+
+ ++lease1.iaid_;
+ EXPECT_FALSE(lease1 == lease2);
+ EXPECT_TRUE(lease1 != lease2);
+ lease1.iaid_ = lease2.iaid_;
+ EXPECT_TRUE(lease1 == lease2); // Check that the reversion has made the
+ EXPECT_FALSE(lease1 != lease2); // ... leases equal
+
+ ++duid_array[0];
+ lease1.duid_.reset(new DUID(duid_array, sizeof(duid_array)));
+ EXPECT_FALSE(lease1 == lease2);
+ EXPECT_TRUE(lease1 != lease2);
+ --duid_array[0];
+ lease1.duid_.reset(new DUID(duid_array, sizeof(duid_array)));
+ EXPECT_TRUE(lease1 == lease2); // Check that the reversion has made the
+ EXPECT_FALSE(lease1 != lease2); // ... leases equal
+
+ ++lease1.preferred_lft_;
+ EXPECT_FALSE(lease1 == lease2);
+ EXPECT_TRUE(lease1 != lease2);
+ lease1.preferred_lft_ = lease2.preferred_lft_;
+ EXPECT_TRUE(lease1 == lease2); // Check that the reversion has made the
+ EXPECT_FALSE(lease1 != lease2); // ... leases equal
+
+ ++lease1.valid_lft_;
+ EXPECT_FALSE(lease1 == lease2);
+ EXPECT_TRUE(lease1 != lease2);
+ lease1.valid_lft_ = lease2.valid_lft_;
+ EXPECT_TRUE(lease1 == lease2); // Check that the reversion has made the
+ EXPECT_FALSE(lease1 != lease2); // ... leases equal
+
+ ++lease1.t1_;
+ EXPECT_FALSE(lease1 == lease2);
+ EXPECT_TRUE(lease1 != lease2);
+ lease1.t1_ = lease2.t1_;
+ EXPECT_TRUE(lease1 == lease2); // Check that the reversion has made the
+ EXPECT_FALSE(lease1 != lease2); // ... leases equal
+
+ ++lease1.t2_;
+ EXPECT_FALSE(lease1 == lease2);
+ EXPECT_TRUE(lease1 != lease2);
+ lease1.t2_ = lease2.t2_;
+ EXPECT_TRUE(lease1 == lease2); // Check that the reversion has made the
+ EXPECT_FALSE(lease1 != lease2); // ... leases equal
+
+ ++lease1.cltt_;
+ EXPECT_FALSE(lease1 == lease2);
+ EXPECT_TRUE(lease1 != lease2);
+ lease1.cltt_ = lease2.cltt_;
+ EXPECT_TRUE(lease1 == lease2); // Check that the reversion has made the
+ EXPECT_FALSE(lease1 != lease2); // ... leases equal
+
+ ++lease1.subnet_id_;
+ EXPECT_FALSE(lease1 == lease2);
+ EXPECT_TRUE(lease1 != lease2);
+ lease1.subnet_id_ = lease2.subnet_id_;
+ EXPECT_TRUE(lease1 == lease2); // Check that the reversion has made the
+ EXPECT_FALSE(lease1 != lease2); // ... leases equal
+
+ lease1.fixed_ = !lease1.fixed_;
+ EXPECT_FALSE(lease1 == lease2);
+ EXPECT_TRUE(lease1 != lease2);
+ lease1.fixed_ = lease2.fixed_;
+ EXPECT_TRUE(lease1 == lease2); // Check that the reversion has made the
+ EXPECT_FALSE(lease1 != lease2); // ... leases equal
+
+ lease1.hostname_ += std::string("Something random");
+ EXPECT_FALSE(lease1 == lease2);
+ EXPECT_TRUE(lease1 != lease2);
+ lease1.hostname_ = lease2.hostname_;
+ EXPECT_TRUE(lease1 == lease2); // Check that the reversion has made the
+ EXPECT_FALSE(lease1 != lease2); // ... leases equal
+
+ lease1.fqdn_fwd_ = !lease1.fqdn_fwd_;
+ EXPECT_FALSE(lease1 == lease2);
+ EXPECT_TRUE(lease1 != lease2);
+ lease1.fqdn_fwd_ = lease2.fqdn_fwd_;
+ EXPECT_TRUE(lease1 == lease2); // Check that the reversion has made the
+ EXPECT_FALSE(lease1 != lease2); // ... leases equal
+
+ lease1.fqdn_rev_ = !lease1.fqdn_rev_;
+ EXPECT_FALSE(lease1 == lease2);
+ EXPECT_TRUE(lease1 != lease2);
+ lease1.fqdn_rev_ = lease2.fqdn_rev_;
+ EXPECT_TRUE(lease1 == lease2); // Check that the reversion has made the
+ EXPECT_FALSE(lease1 != lease2); // ... leases equal
+
+ lease1.comments_ += std::string("Something random");
+ EXPECT_FALSE(lease1 == lease2);
+ EXPECT_TRUE(lease1 != lease2);
+ lease1.comments_ = lease2.comments_;
+ EXPECT_TRUE(lease1 == lease2); // Check that the reversion has made the
+ EXPECT_FALSE(lease1 != lease2); // ... leases equal
+}
+
+// Checks if lease expiration is calculated properly
+TEST(Lease6, Lease6Expired) {
+ const IOAddress addr("2001:db8:1::456");
+ const uint8_t duid_array[] = {0, 1, 2, 3, 4, 5, 6, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf};
+ const DuidPtr duid(new DUID(duid_array, sizeof(duid_array)));
+ const uint32_t iaid = 7; // Just a number
+ const SubnetID subnet_id = 8; // Just another number
+ Lease6 lease(Lease::TYPE_NA, addr, duid, iaid, 100, 200, 50, 80,
+ subnet_id);
+
+ // Case 1: a second before expiration
+ lease.cltt_ = time(NULL) - 100;
+ lease.valid_lft_ = 101;
+ EXPECT_FALSE(lease.expired());
+
+ // Case 2: the lease will expire after this second is concluded
+ lease.cltt_ = time(NULL) - 101;
+ EXPECT_FALSE(lease.expired());
+
+ // Case 3: the lease is expired
+ lease.cltt_ = time(NULL) - 102;
+ EXPECT_TRUE(lease.expired());
+}
+
// Verify that the DUID can be returned as a vector object and if DUID is NULL
// the empty vector is returned.
-TEST(Lease6Test, getDuidVector) {
+TEST(Lease6, getDuidVector) {
// Create a lease.
Lease6 lease;
// By default, the lease should have client id set to NULL. If it doesn't,
@@ -67,5 +733,21 @@ TEST(Lease6Test, getDuidVector) {
EXPECT_TRUE(returned_vec == duid_vec);
}
+// Verify the behavior of the function which checks FQDN data for equality.
+TEST(Lease6, hasIdenticalFqdn) {
+ Lease6 lease = createLease6("myhost.example.com.", true, true);
+ EXPECT_TRUE(lease.hasIdenticalFqdn(createLease6("myhost.example.com.",
+ true, true)));
+ EXPECT_FALSE(lease.hasIdenticalFqdn(createLease6("other.example.com.",
+ true, true)));
+ EXPECT_FALSE(lease.hasIdenticalFqdn(createLease6("myhost.example.com.",
+ false, true)));
+ EXPECT_FALSE(lease.hasIdenticalFqdn(createLease6("myhost.example.com.",
+ true, false)));
+ EXPECT_FALSE(lease.hasIdenticalFqdn(createLease6("myhost.example.com.",
+ false, false)));
+ EXPECT_FALSE(lease.hasIdenticalFqdn(createLease6("other.example.com.",
+ false, false)));
+}
}; // end of anonymous namespace
diff --git a/src/lib/dhcpsrv/tests/subnet_unittest.cc b/src/lib/dhcpsrv/tests/subnet_unittest.cc
index d0dd57a..a16f1a0 100644
--- a/src/lib/dhcpsrv/tests/subnet_unittest.cc
+++ b/src/lib/dhcpsrv/tests/subnet_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright (C) 2012-2013 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2012-2014 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
@@ -49,6 +49,8 @@ TEST(Subnet4Test, in_range) {
EXPECT_EQ(2000, subnet.getT2());
EXPECT_EQ(3000, subnet.getValid());
+ EXPECT_EQ("0.0.0.0", subnet.getRelayInfo().addr_.toText());
+
EXPECT_FALSE(subnet.inRange(IOAddress("192.0.0.0")));
EXPECT_TRUE(subnet.inRange(IOAddress("192.0.2.0")));
EXPECT_TRUE(subnet.inRange(IOAddress("192.0.2.1")));
@@ -58,6 +60,17 @@ TEST(Subnet4Test, in_range) {
EXPECT_FALSE(subnet.inRange(IOAddress("255.255.255.255")));
}
+// Checks whether the relay field has sane default and if it can
+// be changed, stored and retrieved
+TEST(Subnet4Test, relay) {
+ Subnet4 subnet(IOAddress("192.0.2.1"), 24, 1000, 2000, 3000);
+
+ EXPECT_EQ("0.0.0.0", subnet.getRelayInfo().addr_.toText());
+
+ subnet.setRelayInfo(IOAddress("192.0.123.45"));
+ EXPECT_EQ("192.0.123.45", subnet.getRelayInfo().addr_.toText());
+}
+
// Checks whether siaddr field can be set and retrieved correctly.
TEST(Subnet4Test, siaddr) {
Subnet4 subnet(IOAddress("192.0.2.1"), 24, 1000, 2000, 3000);
@@ -126,6 +139,80 @@ TEST(Subnet4Test, Subnet4_Pool4_checks) {
EXPECT_THROW(subnet->addPool(pool3), BadValue);
}
+// Tests whether Subnet4 object is able to store and process properly
+// information about allowed client class (a single class).
+TEST(Subnet4Test, clientClasses) {
+ // Create the V4 subnet.
+ Subnet4Ptr subnet(new Subnet4(IOAddress("192.0.2.0"), 8, 1, 2, 3));
+
+ // This client does not belong to any class.
+ isc::dhcp::ClientClasses no_class;
+
+ // This client belongs to foo only.
+ isc::dhcp::ClientClasses foo_class;
+ foo_class.insert("foo");
+
+ // This client belongs to bar only. I like that client.
+ isc::dhcp::ClientClasses bar_class;
+ bar_class.insert("bar");
+
+ // This client belongs to foo, bar and baz classes.
+ isc::dhcp::ClientClasses three_classes;
+ three_classes.insert("foo");
+ three_classes.insert("bar");
+ three_classes.insert("baz");
+
+ // No class restrictions defined, any client should be supported
+ EXPECT_TRUE(subnet->clientSupported(no_class));
+ EXPECT_TRUE(subnet->clientSupported(foo_class));
+ EXPECT_TRUE(subnet->clientSupported(bar_class));
+ EXPECT_TRUE(subnet->clientSupported(three_classes));
+
+ // Let's allow only clients belongning to "bar" class.
+ subnet->allowClientClass("bar");
+
+ EXPECT_FALSE(subnet->clientSupported(no_class));
+ EXPECT_FALSE(subnet->clientSupported(foo_class));
+ EXPECT_TRUE(subnet->clientSupported(bar_class));
+ EXPECT_TRUE(subnet->clientSupported(three_classes));
+}
+
+// Tests whether Subnet4 object is able to store and process properly
+// information about allowed client classes (multiple classes allowed).
+TEST(Subnet4Test, clientClassesMultiple) {
+ // Create the V4 subnet.
+ Subnet4Ptr subnet(new Subnet4(IOAddress("192.0.2.0"), 8, 1, 2, 3));
+
+ // This client does not belong to any class.
+ isc::dhcp::ClientClasses no_class;
+
+ // This client belongs to foo only.
+ isc::dhcp::ClientClasses foo_class;
+ foo_class.insert("foo");
+
+ // This client belongs to bar only. I like that client.
+ isc::dhcp::ClientClasses bar_class;
+ bar_class.insert("bar");
+
+ // No class restrictions defined, any client should be supported
+ EXPECT_TRUE(subnet->clientSupported(no_class));
+ EXPECT_TRUE(subnet->clientSupported(foo_class));
+ EXPECT_TRUE(subnet->clientSupported(bar_class));
+
+ // Let's allow clients belongning to "bar" or "foo" class.
+ subnet->allowClientClass("bar");
+ subnet->allowClientClass("foo");
+
+ // Class-less clients are to be rejected.
+ EXPECT_FALSE(subnet->clientSupported(no_class));
+
+ // Clients in foo class should be accepted.
+ EXPECT_TRUE(subnet->clientSupported(foo_class));
+
+ // Clients in bar class should be accepted as well.
+ EXPECT_TRUE(subnet->clientSupported(bar_class));
+}
+
TEST(Subnet4Test, addInvalidOption) {
// Create the V4 subnet.
Subnet4Ptr subnet(new Subnet4(IOAddress("192.0.2.0"), 8, 1, 2, 3));
@@ -291,6 +378,18 @@ TEST(Subnet6Test, in_range) {
EXPECT_FALSE(subnet.inRange(IOAddress("::")));
}
+// Checks whether the relay field has sane default and if it can
+// be changed, stored and retrieved
+TEST(Subnet6Test, relay) {
+ Subnet6 subnet(IOAddress("2001:db8:1::"), 64, 1000, 2000, 3000, 4000);
+
+ EXPECT_EQ("::", subnet.getRelayInfo().addr_.toText());
+
+ subnet.setRelayInfo(IOAddress("2001:ffff::1"));
+
+ EXPECT_EQ("2001:ffff::1", subnet.getRelayInfo().addr_.toText());
+}
+
TEST(Subnet6Test, Pool6InSubnet6) {
Subnet6Ptr subnet(new Subnet6(IOAddress("2001:db8:1::"), 56, 1, 2, 3, 4));
@@ -391,6 +490,80 @@ TEST(Subnet6Test, PoolTypes) {
EXPECT_THROW(subnet->addPool(pool5), BadValue);
}
+// Tests whether Subnet6 object is able to store and process properly
+// information about allowed client class (a single class).
+TEST(Subnet6Test, clientClasses) {
+ // Create the V6 subnet.
+ Subnet6Ptr subnet(new Subnet6(IOAddress("2001:db8:1::"), 56, 1, 2, 3, 4));
+
+ // This client does not belong to any class.
+ isc::dhcp::ClientClasses no_class;
+
+ // This client belongs to foo only.
+ isc::dhcp::ClientClasses foo_class;
+ foo_class.insert("foo");
+
+ // This client belongs to bar only. I like that client.
+ isc::dhcp::ClientClasses bar_class;
+ bar_class.insert("bar");
+
+ // This client belongs to foo, bar and baz classes.
+ isc::dhcp::ClientClasses three_classes;
+ three_classes.insert("foo");
+ three_classes.insert("bar");
+ three_classes.insert("baz");
+
+ // No class restrictions defined, any client should be supported
+ EXPECT_TRUE(subnet->clientSupported(no_class));
+ EXPECT_TRUE(subnet->clientSupported(foo_class));
+ EXPECT_TRUE(subnet->clientSupported(bar_class));
+ EXPECT_TRUE(subnet->clientSupported(three_classes));
+
+ // Let's allow only clients belongning to "bar" class.
+ subnet->allowClientClass("bar");
+
+ EXPECT_FALSE(subnet->clientSupported(no_class));
+ EXPECT_FALSE(subnet->clientSupported(foo_class));
+ EXPECT_TRUE(subnet->clientSupported(bar_class));
+ EXPECT_TRUE(subnet->clientSupported(three_classes));
+}
+
+// Tests whether Subnet6 object is able to store and process properly
+// information about allowed client class (multiple classes allowed).
+TEST(Subnet6Test, clientClassesMultiple) {
+ // Create the V6 subnet.
+ Subnet6Ptr subnet(new Subnet6(IOAddress("2001:db8:1::"), 56, 1, 2, 3, 4));
+
+ // This client does not belong to any class.
+ isc::dhcp::ClientClasses no_class;
+
+ // This client belongs to foo only.
+ isc::dhcp::ClientClasses foo_class;
+ foo_class.insert("foo");
+
+ // This client belongs to bar only. I like that client.
+ isc::dhcp::ClientClasses bar_class;
+ bar_class.insert("bar");
+
+ // No class restrictions defined, any client should be supported
+ EXPECT_TRUE(subnet->clientSupported(no_class));
+ EXPECT_TRUE(subnet->clientSupported(foo_class));
+ EXPECT_TRUE(subnet->clientSupported(bar_class));
+
+ // Let's allow only clients belongning to "foo" or "bar" class.
+ subnet->allowClientClass("foo");
+ subnet->allowClientClass("bar");
+
+ // Class-less clients are to be rejected.
+ EXPECT_FALSE(subnet->clientSupported(no_class));
+
+ // Clients in foo class should be accepted.
+ EXPECT_TRUE(subnet->clientSupported(foo_class));
+
+ // Clients in bar class should be accepted as well.
+ EXPECT_TRUE(subnet->clientSupported(bar_class));
+}
+
TEST(Subnet6Test, Subnet6_Pool6_checks) {
Subnet6Ptr subnet(new Subnet6(IOAddress("2001:db8:1::"), 56, 1, 2, 3, 4));
diff --git a/src/lib/dns/Makefile.am b/src/lib/dns/Makefile.am
index bda4e85..ede699a 100644
--- a/src/lib/dns/Makefile.am
+++ b/src/lib/dns/Makefile.am
@@ -158,7 +158,7 @@ rrclass.h: rrclass-placeholder.h
rrtype.h: rrtype-placeholder.h
rrparamregistry.cc: rrparamregistry-placeholder.cc
-s-rdatacode: Makefile
+s-rdatacode: Makefile $(EXTRA_DIST)
$(PYTHON) ./gen-rdatacode.py
touch $@
diff --git a/src/lib/dns/exceptions.h b/src/lib/dns/exceptions.h
index 070b152..21030b4 100644
--- a/src/lib/dns/exceptions.h
+++ b/src/lib/dns/exceptions.h
@@ -30,10 +30,34 @@ namespace dns {
///
class Rcode; // forward declaration
-class DNSProtocolError : public isc::Exception {
+class Exception : public isc::Exception {
public:
- DNSProtocolError(const char* file, size_t line, const char* what) :
+ Exception(const char* file, size_t line, const char* what) :
isc::Exception(file, line, what) {}
+};
+
+///
+/// \brief Base class for all sorts of text parse errors.
+///
+class DNSTextError : public isc::dns::Exception {
+public:
+ DNSTextError(const char* file, size_t line, const char* what) :
+ isc::dns::Exception(file, line, what) {}
+};
+
+///
+/// \brief Base class for name parser exceptions.
+///
+class NameParserException : public DNSTextError {
+public:
+ NameParserException(const char* file, size_t line, const char* what) :
+ DNSTextError(file, line, what) {}
+};
+
+class DNSProtocolError : public isc::dns::Exception {
+public:
+ DNSProtocolError(const char* file, size_t line, const char* what) :
+ isc::dns::Exception(file, line, what) {}
virtual const Rcode& getRcode() const = 0;
};
@@ -50,6 +74,7 @@ public:
DNSProtocolError(file, line, what) {}
virtual const Rcode& getRcode() const;
};
+
}
}
#endif // DNS_EXCEPTIONS_H
diff --git a/src/lib/dns/labelsequence.cc b/src/lib/dns/labelsequence.cc
index f5f6a95..91af633 100644
--- a/src/lib/dns/labelsequence.cc
+++ b/src/lib/dns/labelsequence.cc
@@ -1,4 +1,4 @@
-// Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2012-2014 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 <config.h>
+
#include <dns/labelsequence.h>
#include <dns/name_internal.h>
#include <exceptions/exceptions.h>
@@ -24,24 +26,34 @@ namespace isc {
namespace dns {
LabelSequence::LabelSequence(const void* buf) {
+#ifdef ENABLE_DEBUG
+ // In non-debug mode, derefencing the NULL pointer further below
+ // will lead to a crash, so disabling this check is not
+ // unsafe. Except for a programming mistake, this case should not
+ // happen.
if (buf == NULL) {
isc_throw(BadValue,
"Null pointer passed to LabelSequence constructor");
}
+#endif
const uint8_t* bp = reinterpret_cast<const uint8_t*>(buf);
-
first_label_ = 0;
const uint8_t offsets_len = *bp++;
+
+#ifdef ENABLE_DEBUG
if (offsets_len == 0 || offsets_len > Name::MAX_LABELS) {
isc_throw(BadValue,
"Bad offsets len in serialized LabelSequence data: "
<< static_cast<unsigned int>(offsets_len));
}
+#endif
+
last_label_ = offsets_len - 1;
offsets_ = bp;
data_ = bp + offsets_len;
+#ifdef ENABLE_DEBUG
// Check the integrity on the offsets and the name data
const uint8_t* dp = data_;
for (size_t cur_offset = 0; cur_offset < offsets_len; ++cur_offset) {
@@ -52,6 +64,7 @@ LabelSequence::LabelSequence(const void* buf) {
}
dp += (1 + *dp);
}
+#endif
}
LabelSequence::LabelSequence(const LabelSequence& src,
diff --git a/src/lib/dns/master_lexer.h b/src/lib/dns/master_lexer.h
index e33f964..1184f04 100644
--- a/src/lib/dns/master_lexer.h
+++ b/src/lib/dns/master_lexer.h
@@ -15,7 +15,7 @@
#ifndef MASTER_LEXER_H
#define MASTER_LEXER_H 1
-#include <exceptions/exceptions.h>
+#include <dns/exceptions.h>
#include <istream>
#include <string>
@@ -324,10 +324,10 @@ public:
///
/// The \c token_ member variable (read-only) is set to a \c MasterToken
/// object of type ERROR indicating the reason for the error.
- class LexerError : public Exception {
+ class LexerError : public isc::dns::Exception {
public:
LexerError(const char* file, size_t line, MasterToken error_token) :
- Exception(file, line, error_token.getErrorText().c_str()),
+ isc::dns::Exception(file, line, error_token.getErrorText().c_str()),
token_(error_token)
{}
const MasterToken token_;
diff --git a/src/lib/dns/master_loader.cc b/src/lib/dns/master_loader.cc
index 6b6e091..80b1053 100644
--- a/src/lib/dns/master_loader.cc
+++ b/src/lib/dns/master_loader.cc
@@ -1,4 +1,4 @@
-// Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2012-2014 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,14 +21,16 @@
#include <dns/rrtype.h>
#include <dns/rdata.h>
-#include <boost/scoped_ptr.hpp>
+#include <boost/format.hpp>
#include <boost/algorithm/string/predicate.hpp> // for iequals
+#include <boost/scoped_ptr.hpp>
+#include <boost/shared_ptr.hpp>
#include <string>
#include <memory>
#include <vector>
-#include <boost/algorithm/string/predicate.hpp> // for iequals
-#include <boost/shared_ptr.hpp>
+
+#include <cstdio> // for sscanf()
using std::string;
using std::auto_ptr;
@@ -54,9 +56,34 @@ public:
} // end unnamed namespace
+/// \brief Private implementation class for the \c MasterLoader
+///
+/// This class is used internally by the \c MasterLoader and is not
+/// publicly visible. It is present to avoid polluting the public API
+/// with internal implementation details of the \c MasterLoader.
// cppcheck-suppress noConstructor
class MasterLoader::MasterLoaderImpl {
public:
+ /// \brief Constructor.
+ ///
+ /// \param master_file Path to the file to load.
+ /// \param zone_origin The origin of zone to be expected inside
+ /// the master file. Currently unused, but it is expected to
+ /// be used for some validation.
+ /// \param zone_class The class of zone to be expected inside the
+ /// master file.
+ /// \param callbacks The callbacks by which it should report problems.
+ /// Usually, the callback carries a filename and line number of the
+ /// input where the problem happens. There's a special case of empty
+ /// filename and zero line in case the opening of the top-level master
+ /// file fails.
+ /// \param add_callback The callback which would be called with each
+ /// loaded RR.
+ /// \param options Options for the parsing, which is bitwise-or of
+ /// the Options values or DEFAULT. If the MANY_ERRORS option is
+ /// included, the parser tries to continue past errors. If it
+ /// is not included, it stops at first encountered error.
+ /// \throw std::bad_alloc when there's not enough memory.
MasterLoaderImpl(const char* master_file,
const Name& zone_origin,
const RRClass& zone_class,
@@ -81,6 +108,16 @@ public:
rr_count_(0)
{}
+ /// \brief Wrapper around \c MasterLexer::pushSource() (file version)
+ ///
+ /// This method is used as a wrapper around the lexer's
+ /// \c pushSource() to also save the current origin and the last
+ /// seen name (to be restored upon \c popSource()). It also calls
+ /// \c pushSource(). See \c doInclude() implementation for more
+ /// details.
+ ///
+ /// \param filename Path to the file to push as a new source.
+ /// \param current_origin The current origin name to save.
void pushSource(const std::string& filename, const Name& current_origin) {
std::string error;
if (!lexer_.pushSource(filename.c_str(), &error)) {
@@ -98,17 +135,35 @@ public:
previous_name_ = false;
}
+ /// \brief Wrapper around \c MasterLexer::pushSource() (stream version)
+ ///
+ /// Similar to \c pushSource(). This method need not save the
+ /// current origin as it is not used with $INCLUDE processing.
+ ///
+ /// \param stream The input stream to use as a new source.
void pushStreamSource(std::istream& stream) {
lexer_.pushSource(stream);
initialized_ = true;
}
+ /// \brief Implementation of \c MasterLoader::loadIncremental()
+ ///
+ /// See \c MasterLoader::loadIncremental() for details.
bool loadIncremental(size_t count_limit);
+ /// \brief Return the total size of the input sources pushed so
+ /// far. See \c MasterLexer::getTotalSourceSize().
size_t getSize() const { return (lexer_.getTotalSourceSize()); }
+
+ /// \brief Return the line number being parsed in the pushed input
+ /// sources. See \c MasterLexer::getPosition().
size_t getPosition() const { return (lexer_.getPosition()); }
private:
+ /// \brief Report an error using the callbacks that were supplied
+ /// during \c MasterLoader construction. Note that this method also
+ /// throws \c MasterLoaderError exception if necessary, so the
+ /// caller need not throw it.
void reportError(const std::string& filename, size_t line,
const std::string& reason)
{
@@ -123,6 +178,12 @@ private:
}
}
+ /// \brief Wrapper around \c MasterLexer::popSource()
+ ///
+ /// This method is used as a wrapper around the lexer's
+ /// \c popSource() to also restore the current origin and the last
+ /// seen name (at time of push). It also calls \c popSource(). See
+ /// \c doInclude() implementation for more details.
bool popSource() {
if (lexer_.getSourceCount() == 1) {
return (false);
@@ -141,14 +202,43 @@ private:
return (true);
}
- // Get a string token. Handle it as error if it is not string.
+ /// \brief Get a string token. Handle it as error if it is not string.
const string getString() {
lexer_.getNextToken(MasterToken::STRING).getString(string_token_);
return (string_token_);
}
+ /// \brief Parse the initial token at the beginning of a line in a
+ /// master file (or stream).
+ ///
+ /// A helper method of \c loadIncremental(), parsing the first token
+ /// of a new line. If it looks like an RR, detect its owner name
+ /// and return a string token for the next field of the RR.
+ ///
+ /// Otherwise, return either \c END_OF_LINE or \c END_OF_FILE token
+ /// depending on whether the loader continues to the next line or
+ /// completes the load, respectively. Other corner cases including
+ /// $-directive handling is done here.
+ ///
+ /// For unexpected errors, it throws an exception, which will be
+ /// handled in \c loadIncremental().
MasterToken handleInitialToken();
+ /// \brief Helper method for \c doGenerate().
+ ///
+ /// This is a helper method for \c doGenerate() that processes the
+ /// LHS or RHS for a single iteration in the range that is requested
+ /// by the $GENERATE directive and returns a generated string (that
+ /// is used to build a name (LHS) or RDATA (RHS) for an RR). See the
+ /// commented implementation for details.
+ std::string generateForIter(const std::string& str, const int it);
+
+ /// \brief Process the $GENERATE directive.
+ ///
+ /// See the commented implementation for details.
+ void doGenerate();
+
+ /// \brief Process the $ORIGIN directive.
void doOrigin(bool is_optional) {
// Parse and create the new origin. It is relative to the previous
// one.
@@ -181,6 +271,7 @@ private:
}
}
+ /// \brief Process the $INCLUDE directive.
void doInclude() {
// First, get the filename to include
const string
@@ -201,11 +292,16 @@ private:
pushSource(filename, current_origin);
}
- // A helper method for loadIncremental(). It parses part of an RR
- // until it finds the RR type field. If TTL or RR class is
- // specified before the RR type, it also recognizes and validates
- // them. explicit_ttl will be set to true if this method finds a
- // valid TTL field.
+ /// \brief Parse RR fields (TTL, CLASS and TYPE).
+ ///
+ /// A helper method for \c loadIncremental(). It parses part of an
+ /// RR until it finds the RR type field. If TTL or RR class is
+ /// specified before the RR type, it also recognizes and validates
+ /// them.
+ ///
+ /// \param explicit_ttl will be set to true if this method finds a
+ /// valid TTL field.
+ /// \param rrparam_token Pass the current (parsed) token here.
RRType parseRRParams(bool& explicit_ttl, MasterToken rrparam_token) {
// Find TTL, class and type. Both TTL and class are
// optional and may occur in any order if they exist. TTL
@@ -245,20 +341,25 @@ private:
return (RRType(rrparam_token.getString()));
}
- // Upper limit check when recognizing a specific TTL value from the
- // zone file ($TTL, the RR's TTL field, or the SOA minimum). RFC2181
- // Section 8 limits the range of TTL values to 2^31-1 (0x7fffffff),
- // and prohibits transmitting a TTL field exceeding this range. We
- // guarantee that by limiting the value at the time of zone
- // parsing/loading, following what BIND 9 does. Resetting it to 0
- // at this point may not be exactly what the RFC states (depending on
- // the meaning of 'received'), but the end result would be the same (i.e.,
- // the guarantee on transmission). Again, we follow the BIND 9's behavior
- // here.
- //
- // post_parsing is true iff this method is called after parsing the entire
- // RR and the lexer is positioned at the next line. It's just for
- // calculating the accurate source line when callback is necessary.
+ /// \brief Check and limit TTL to maximum value.
+ ///
+ /// Upper limit check when recognizing a specific TTL value from the
+ /// zone file ($TTL, the RR's TTL field, or the SOA minimum). RFC2181
+ /// Section 8 limits the range of TTL values to 2^31-1 (0x7fffffff),
+ /// and prohibits transmitting a TTL field exceeding this range. We
+ /// guarantee that by limiting the value at the time of zone
+ /// parsing/loading, following what BIND 9 does. Resetting it to 0
+ /// at this point may not be exactly what the RFC states (depending on
+ /// the meaning of 'received'), but the end result would be the same (i.e.,
+ /// the guarantee on transmission). Again, we follow the BIND 9's behavior
+ /// here.
+ ///
+ /// \param ttl the TTL to check. If it is larger than the maximum
+ /// allowed, it is set to 0.
+ /// \param post_parsing should be true iff this method is called
+ /// after parsing the entire RR and the lexer is positioned at the
+ /// next line. It's just for calculating the accurate source line
+ /// when callback is necessary.
void limitTTL(RRTTL& ttl, bool post_parsing) {
if (ttl > RRTTL::MAX_TTL()) {
const size_t src_line = lexer_.getSourceLine() -
@@ -270,19 +371,25 @@ private:
}
}
- // Set/reset the default TTL. This should be from either $TTL or SOA
- // minimum TTL (it's the caller's responsibility; this method doesn't
- // care about where it comes from). see LimitTTL() for parameter
- // post_parsing.
+ /// \brief Set/reset the default TTL.
+ ///
+ /// This should be from either $TTL or SOA minimum TTL (it's the
+ /// caller's responsibility; this method doesn't care about where it
+ /// comes from). See \c limitTTL() for parameter post_parsing.
void setDefaultTTL(const RRTTL& ttl, bool post_parsing) {
assignTTL(default_ttl_, ttl);
limitTTL(*default_ttl_, post_parsing);
}
- // Try to set/reset the current TTL from candidate TTL text. It's possible
- // it does not actually represent a TTL (which is not immediately
- // considered an error). Return true iff it's recognized as a valid TTL
- // (and only in which case the current TTL is set).
+ /// \brief Try to set/reset the current TTL from candidate TTL text.
+ ///
+ /// It's possible it that the text does not actually represent a TTL
+ /// (which is not immediately considered an error). Returns \c true
+ /// iff it's recognized as a valid TTL (and only in which case the
+ /// current TTL is set).
+ ///
+ /// \param ttl_txt The text to parse as a TTL.
+ /// \return true if a TTL was parsed (and set as the current TTL).
bool setCurrentTTL(const string& ttl_txt) {
// We use the factory version instead of RRTTL constructor as we
// need to expect cases where ttl_txt does not actually represent a TTL
@@ -296,14 +403,15 @@ private:
return (false);
}
- // Determine the TTL of the current RR based on the given parsing context.
- //
- // explicit_ttl is true iff the TTL is explicitly specified for that RR
- // (in which case current_ttl_ is set to that TTL).
- // rrtype is the type of the current RR, and rdata is its RDATA. They
- // only matter if the type is SOA and no available TTL is known. In this
- // case the minimum TTL of the SOA will be used as the TTL of that SOA
- // and the default TTL for subsequent RRs.
+ /// \brief Determine the TTL of the current RR based on the given
+ /// parsing context.
+ ///
+ /// \c explicit_ttl is true iff the TTL is explicitly specified for that RR
+ /// (in which case current_ttl_ is set to that TTL).
+ /// \c rrtype is the type of the current RR, and \c rdata is its RDATA. They
+ /// only matter if the type is SOA and no available TTL is known. In this
+ /// case the minimum TTL of the SOA will be used as the TTL of that SOA
+ /// and the default TTL for subsequent RRs.
const RRTTL& getCurrentTTL(bool explicit_ttl, const RRType& rrtype,
const rdata::ConstRdataPtr& rdata) {
// We've completed parsing the full of RR, and the lexer is already
@@ -342,12 +450,19 @@ private:
return (*current_ttl_);
}
+ /// \brief Handle a $DIRECTIVE
+ ///
+ /// This method is called when a $DIRECTIVE is encountered in the
+ /// input stream.
void handleDirective(const char* directive, size_t length) {
if (iequals(directive, "INCLUDE")) {
doInclude();
} else if (iequals(directive, "ORIGIN")) {
doOrigin(false);
eatUntilEOL(true);
+ } else if (iequals(directive, "GENERATE")) {
+ doGenerate();
+ eatUntilEOL(true);
} else if (iequals(directive, "TTL")) {
setDefaultTTL(RRTTL(getString()), false);
eatUntilEOL(true);
@@ -357,6 +472,7 @@ private:
}
}
+ /// \brief Skip tokens until end-of-line.
void eatUntilEOL(bool reportExtra) {
// We want to continue. Try to read until the end of line
for (;;) {
@@ -437,15 +553,320 @@ public:
size_t rr_count_; // number of RRs successfully loaded
};
-// A helper method of loadIncremental, parsing the first token of a new line.
-// If it looks like an RR, detect its owner name and return a string token for
-// the next field of the RR.
-// Otherwise, return either END_OF_LINE or END_OF_FILE token depending on
-// whether the loader continues to the next line or completes the load,
-// respectively. Other corner cases including $-directive handling is done
-// here.
-// For unexpected errors, it throws an exception, which will be handled in
-// loadIncremental.
+namespace { // begin unnamed namespace
+
+/// \brief Generate a dotted nibble sequence.
+///
+/// This method generates a dotted nibble sequence and returns it as a
+/// string. The nibbles are appended from the least significant digit
+/// (in hex representation of \c num) to the most significant digit with
+/// dots ('.') to separate the digits. If \c width is non-zero and the
+/// dotted nibble sequence has not filled the requested width, the rest
+/// of the width is filled with a dotted nibble sequence of 0 nibbles.
+///
+/// Some sample representations:
+///
+/// num = 0x1234, width = 0
+/// "4.3.2.1"
+///
+/// num = 0x1234, width = 1
+/// "4.3.2.1"
+///
+/// num = 0x1234, width = 8
+/// "4.3.2.1"
+///
+/// num = 0x1234, width = 9
+/// "4.3.2.1."
+///
+/// num = 0x1234, width = 10
+/// "4.3.2.1.0"
+///
+/// num = 0x1234, width = 11
+/// "4.3.2.1.0."
+///
+/// num = 0xabcd, width = 0, uppercase = true
+/// "D.C.B.A"
+///
+/// num = 0, width = 0
+/// "0"
+///
+/// num = 0, width = 1
+/// "0"
+///
+/// num = 0, width = 2
+/// "0."
+///
+/// num = 0, width = 3
+/// "0.0"
+///
+/// \param num The number for which the dotted nibble sequence should be
+/// generated.
+/// \param width The width of the generated string. This is only
+/// meaningful when it is larger than the dotted nibble sequence
+/// representation of \c num.
+/// \param uppercase Whether to use uppercase characters in nibble
+/// sequence.
+/// \return A string containing the dotted nibble sequence.
+std::string
+genNibbles(int num, unsigned int width, bool uppercase) {
+ static const char *hex = "0123456789abcdef0123456789ABCDEF";
+ std::string rstr;
+
+ do {
+ char ch = hex[(num & 0x0f) + (uppercase ? 16 : 0)];
+ num >>= 4;
+ rstr.push_back(ch);
+
+ if (width > 0) {
+ --width;
+ }
+
+ // If width is non zero then we need to add a label separator.
+ // If value is non zero then we need to add another label and
+ // that requires a label separator.
+ if (width > 0 || num != 0) {
+ rstr.push_back('.');
+
+ if (width > 0) {
+ --width;
+ }
+ }
+ } while ((num != 0) || (width > 0));
+
+ return (rstr);
+}
+
+} // end unnamed namespace
+
+std::string
+MasterLoader::MasterLoaderImpl::generateForIter(const std::string& str,
+ const int num)
+{
+ std::string rstr;
+
+ for (std::string::const_iterator it = str.begin(); it != str.end();) {
+ switch (*it) {
+ case '$':
+ // This is the case when the '$' character is encountered in
+ // the LHS or RHS. A computed value is added in its place in
+ // the generated string.
+ ++it;
+ if ((it != str.end()) && (*it == '$')) {
+ rstr.push_back('$');
+ ++it;
+ continue;
+ }
+
+ // 'it' can be equal to str.end() here, but it is handled
+ // correctly.
+ if (*it != '{') {
+ // There is no modifier (between {}), so just copy the
+ // passed number into the generated string.
+ rstr += boost::str(boost::format("%d") % num);
+ } else {
+ // There is a modifier (between {}). Parse it and handle
+ // the various cases below.
+ const char* scan_str =
+ str.c_str() + std::distance(str.begin(), it);
+ int offset = 0;
+ unsigned int width;
+ char base[2] = {'d', 0}; // char plus null byte
+ // cppcheck-suppress invalidscanf
+ const int n = sscanf(scan_str, "{%d,%u,%1[doxXnN]}",
+ &offset, &width, base);
+ switch (n) {
+ case 1:
+ // Only 1 item was matched (the offset). Copy (num +
+ // offset) into the generated string.
+ rstr += boost::str(boost::format("%d") % (num + offset));
+ break;
+
+ case 2: {
+ // 2 items were matched (the offset and width). Copy
+ // (num + offset) and format it according to the width
+ // into the generated string.
+ const std::string fmt =
+ boost::str(boost::format("%%0%ud") % width);
+ rstr += boost::str(boost::format(fmt) % (num + offset));
+ break;
+ }
+
+ case 3:
+ // 3 items were matched (offset, width and base).
+ if ((base[0] == 'n') || (base[0] == 'N')) {
+ // The base is requesting nibbles. Format it
+ // specially (see genNibbles() documentation).
+ rstr += genNibbles(num + offset, width, (base[0] == 'N'));
+ } else {
+ // The base is not requesting nibbles. Copy (num +
+ // offset) and format it according to the width
+ // and base into the generated string.
+ const std::string fmt =
+ boost::str(boost::format("%%0%u%c") % width % base[0]);
+ rstr += boost::str(boost::format(fmt) % (num + offset));
+ }
+ break;
+
+ default:
+ // Any other case in the modifiers is an error.
+ reportError(lexer_.getSourceName(), lexer_.getSourceLine(),
+ "Invalid $GENERATE format modifiers");
+ return ("");
+ }
+
+ // Find the closing brace. Careful that 'it' can be equal
+ // to str.end() here.
+ while ((it != str.end()) && (*it != '}')) {
+ ++it;
+ }
+ // Skip past the closing brace (if there is one).
+ if (it != str.end()) {
+ ++it;
+ }
+ }
+ break;
+
+ case '\\':
+ // This is the case when the '\' character is encountered in
+ // the LHS or RHS. The '\' and the following character are
+ // copied as-is into the generated string. This is usually
+ // used for escaping the $ character.
+ rstr.push_back(*it);
+ ++it;
+ if (it == str.end()) {
+ continue;
+ }
+ rstr.push_back(*it);
+ ++it;
+ break;
+
+ default:
+ // This is the default case that handles all other
+ // characters. They are copied as-is into the generated
+ // string.
+ rstr.push_back(*it);
+ ++it;
+ break;
+ }
+ }
+
+ return (rstr);
+}
+
+void
+MasterLoader::MasterLoaderImpl::doGenerate() {
+ // Parse the range token
+ const MasterToken& range_token = lexer_.getNextToken(MasterToken::STRING);
+ if (range_token.getType() != MasterToken::STRING) {
+ reportError(lexer_.getSourceName(), lexer_.getSourceLine(),
+ "Invalid $GENERATE syntax");
+ return;
+ }
+ const std::string range = range_token.getString();
+
+ // Parse the LHS token
+ const MasterToken& lhs_token = lexer_.getNextToken(MasterToken::STRING);
+ if (lhs_token.getType() != MasterToken::STRING) {
+ reportError(lexer_.getSourceName(), lexer_.getSourceLine(),
+ "Invalid $GENERATE syntax");
+ return;
+ }
+ const std::string lhs = lhs_token.getString();
+
+ // Parse the TTL, RR class and RR type tokens. Note that TTL and RR
+ // class may come in any order, or may be missing (either or
+ // both). If TTL is missing, we expect that it was either specified
+ // explicitly using $TTL, or is implicitly known from a previous RR,
+ // or that this is the SOA RR from which the MINIMUM field is
+ // used. It's unlikely that $GENERATE will be used with an SOA RR,
+ // but it's possible. The parsing happens within the parseRRParams()
+ // helper method which is called below.
+ const MasterToken& param_token = lexer_.getNextToken(MasterToken::STRING);
+ if (param_token.getType() != MasterToken::STRING) {
+ reportError(lexer_.getSourceName(), lexer_.getSourceLine(),
+ "Invalid $GENERATE syntax");
+ return;
+ }
+
+ bool explicit_ttl = false;
+ const RRType rrtype = parseRRParams(explicit_ttl, param_token);
+
+ // Parse the RHS token. It can be a quoted string.
+ const MasterToken& rhs_token = lexer_.getNextToken(MasterToken::QSTRING);
+ if ((rhs_token.getType() != MasterToken::QSTRING) &&
+ (rhs_token.getType() != MasterToken::STRING))
+ {
+ reportError(lexer_.getSourceName(), lexer_.getSourceLine(),
+ "Invalid $GENERATE syntax");
+ return;
+ }
+ const std::string rhs = rhs_token.getString();
+
+ // Range can be one of two forms: start-stop or start-stop/step. If
+ // the first form is used, then step is set to 1. All of start, stop
+ // and step must be positive.
+ unsigned int start;
+ unsigned int stop;
+ unsigned int step;
+ // cppcheck-suppress invalidscanf
+ const int n = sscanf(range.c_str(), "%u-%u/%u", &start, &stop, &step);
+ if ((n < 2) || (stop < start)) {
+ reportError(lexer_.getSourceName(), lexer_.getSourceLine(),
+ "$GENERATE: invalid range: " + range);
+ return;
+ }
+
+ if (n == 2) {
+ step = 1;
+ }
+
+ // Generate and add the records.
+ for (int i = start; i <= stop; i += step) {
+ // Get generated strings for LHS and RHS. LHS goes to form the
+ // name, RHS goes to form the RDATA of the RR.
+ const std::string generated_name = generateForIter(lhs, i);
+ const std::string generated_rdata = generateForIter(rhs, i);
+ if (generated_name.empty() || generated_rdata.empty()) {
+ // The error should have been sent to the callbacks already
+ // by generateForIter().
+ reportError(lexer_.getSourceName(), lexer_.getSourceLine(),
+ "$GENERATE error");
+ return;
+ }
+
+ // generateForIter() can return a string with a trailing '.' in
+ // case of a nibble representation. So we cannot use the
+ // relative Name constructor. We use concatenate() which is
+ // expensive, but keeps the generated LHS-based Name within the
+ // active origin.
+ last_name_.reset
+ (new Name(Name(generated_name).concatenate(active_origin_)));
+ previous_name_ = true;
+
+ const rdata::RdataPtr rdata =
+ rdata::createRdata(rrtype, zone_class_, generated_rdata);
+ // In case we get NULL, it means there was error creating the
+ // Rdata. The errors should have been reported by callbacks_
+ // already. We need to decide if we want to continue or not.
+ if (rdata) {
+ add_callback_(*last_name_, zone_class_, rrtype,
+ getCurrentTTL(explicit_ttl, rrtype, rdata),
+ rdata);
+ // Good, we added another one
+ ++rr_count_;
+ } else {
+ seen_error_ = true;
+ if (!many_errors_) {
+ ok_ = false;
+ complete_ = true;
+ // We don't have the exact error here, but it was
+ // reported by the error callback.
+ isc_throw(MasterLoaderError, "Invalid RR data");
+ }
+ }
+ }
+}
+
MasterToken
MasterLoader::MasterLoaderImpl::handleInitialToken() {
const MasterToken& initial_token =
@@ -576,15 +997,19 @@ MasterLoader::MasterLoaderImpl::loadIncremental(size_t count_limit) {
isc_throw(MasterLoaderError, "Invalid RR data");
}
}
- } catch (const MasterLoaderError&) {
- // This is a hack. We exclude the MasterLoaderError from the
- // below case. Once we restrict the below to some smaller
- // exception, we should remove this.
- throw;
- } catch (const isc::Exception& e) {
- // TODO: Once we do #2518, catch only the DNSTextError here,
- // not isc::Exception. The rest should be just simply
- // propagated.
+ } catch (const isc::dns::DNSTextError& e) {
+ reportError(lexer_.getSourceName(), lexer_.getSourceLine(),
+ e.what());
+ eatUntilEOL(false);
+ } catch (const MasterLexer::ReadError& e) {
+ reportError(lexer_.getSourceName(), lexer_.getSourceLine(),
+ e.what());
+ eatUntilEOL(false);
+ } catch (const MasterLexer::LexerError& e) {
+ reportError(lexer_.getSourceName(), lexer_.getSourceLine(),
+ e.what());
+ eatUntilEOL(false);
+ } catch (const InternalException& e) {
reportError(lexer_.getSourceName(), lexer_.getSourceLine(),
e.what());
eatUntilEOL(false);
diff --git a/src/lib/dns/message.cc b/src/lib/dns/message.cc
index 2bc337a..33cd07b 100644
--- a/src/lib/dns/message.cc
+++ b/src/lib/dns/message.cc
@@ -604,13 +604,8 @@ Message::addQuestion(const Question& question) {
}
void
-Message::toWire(AbstractMessageRenderer& renderer) {
- impl_->toWire(renderer, NULL);
-}
-
-void
-Message::toWire(AbstractMessageRenderer& renderer, TSIGContext& tsig_ctx) {
- impl_->toWire(renderer, &tsig_ctx);
+Message::toWire(AbstractMessageRenderer& renderer, TSIGContext* tsig_ctx) {
+ impl_->toWire(renderer, tsig_ctx);
}
void
@@ -620,6 +615,10 @@ Message::parseHeader(InputBuffer& buffer) {
"Message parse attempted in non parse mode");
}
+ if (impl_->header_parsed_) {
+ return;
+ }
+
if ((buffer.getLength() - buffer.getPosition()) < HEADERLEN) {
isc_throw(MessageTooShort, "Malformed DNS message (short length): "
<< buffer.getLength() - buffer.getPosition());
@@ -645,9 +644,11 @@ Message::fromWire(InputBuffer& buffer, ParseOptions options) {
"Message parse attempted in non parse mode");
}
- if (!impl_->header_parsed_) {
- parseHeader(buffer);
- }
+ // Clear any old parsed data
+ clear(Message::PARSE);
+
+ buffer.setPosition(0);
+ parseHeader(buffer);
impl_->counts_[SECTION_QUESTION] = impl_->parseQuestion(buffer);
impl_->counts_[SECTION_ANSWER] =
diff --git a/src/lib/dns/message.h b/src/lib/dns/message.h
index aaa0d76..3afad1f 100644
--- a/src/lib/dns/message.h
+++ b/src/lib/dns/message.h
@@ -21,7 +21,7 @@
#include <string>
#include <ostream>
-#include <exceptions/exceptions.h>
+#include <dns/exceptions.h>
#include <dns/edns.h>
#include <dns/question.h>
@@ -41,10 +41,10 @@ class TSIGRecord;
/// message parser encounters a short length of data that don't even contain
/// the full header section.
///
-class MessageTooShort : public Exception {
+class MessageTooShort : public isc::dns::Exception {
public:
MessageTooShort(const char* file, size_t line, const char* what) :
- isc::Exception(file, line, what) {}
+ isc::dns::Exception(file, line, what) {}
};
///
@@ -52,10 +52,10 @@ public:
/// is being constructed for an incompatible section. Specifically, this
/// happens RRset iterator is being constructed for a Question section.
///
-class InvalidMessageSection : public Exception {
+class InvalidMessageSection : public isc::dns::Exception {
public:
InvalidMessageSection(const char* file, size_t line, const char* what) :
- isc::Exception(file, line, what) {}
+ isc::dns::Exception(file, line, what) {}
};
///
@@ -63,10 +63,10 @@ public:
/// class method is called that is prohibited for the current mode of
/// the message.
///
-class InvalidMessageOperation : public Exception {
+class InvalidMessageOperation : public isc::dns::Exception {
public:
InvalidMessageOperation(const char* file, size_t line, const char* what) :
- isc::Exception(file, line, what) {}
+ isc::dns::Exception(file, line, what) {}
};
///
@@ -74,10 +74,10 @@ public:
/// smaller than the standard default maximum (DEFAULT_MAX_UDPSIZE) is
/// being specified for the message.
///
-class InvalidMessageUDPSize : public Exception {
+class InvalidMessageUDPSize : public isc::dns::Exception {
public:
InvalidMessageUDPSize(const char* file, size_t line, const char* what) :
- isc::Exception(file, line, what) {}
+ isc::dns::Exception(file, line, what) {}
};
typedef uint16_t qid_t;
@@ -550,29 +550,18 @@ public:
std::string toText() const;
/// \brief Render the message in wire formant into a message renderer
- /// object.
+ /// object with (or without) TSIG.
///
/// This \c Message must be in the \c RENDER mode and both \c Opcode and
/// \c Rcode must have been set beforehand; otherwise, an exception of
/// class \c InvalidMessageOperation will be thrown.
///
- /// \note The renderer's internal buffers and data are automatically
- /// cleared, keeping the length limit and the compression mode intact.
- /// In case truncation is triggered, the renderer is cleared completely.
- ///
- /// \param renderer DNS message rendering context that encapsulates the
- /// output buffer and name compression information.
- void toWire(AbstractMessageRenderer& renderer);
-
- /// \brief Render the message in wire formant into a message renderer
- /// object with TSIG.
- ///
- /// This method is similar to the other version of \c toWire(), but
- /// it will also add a TSIG RR with (in many cases) the TSIG MAC for
- /// the message along with the given TSIG context (\c tsig_ctx).
- /// The TSIG RR will be placed at the end of \c renderer.
- /// \c tsig_ctx will be updated based on the fact it was used for signing
- /// and with the latest MAC.
+ /// If a non-NULL \c tsig_ctx is passed, it will also add a TSIG RR
+ /// with (in many cases) the TSIG MAC for the message along with the
+ /// given TSIG context (\c tsig_ctx). The TSIG RR will be placed at
+ /// the end of \c renderer. The \c TSIGContext at \c tsig_ctx will
+ /// be updated based on the fact it was used for signing and with
+ /// the latest MAC.
///
/// \exception InvalidMessageOperation The message is not in the Render
/// mode, or either Rcode or Opcode is not set.
@@ -589,10 +578,12 @@ public:
/// cleared, keeping the length limit and the compression mode intact.
/// In case truncation is triggered, the renderer is cleared completely.
///
- /// \param renderer See the other version
+ /// \param renderer DNS message rendering context that encapsulates the
+ /// output buffer and name compression information.
/// \param tsig_ctx A TSIG context that is to be used for signing the
/// message
- void toWire(AbstractMessageRenderer& renderer, TSIGContext& tsig_ctx);
+ void toWire(AbstractMessageRenderer& renderer,
+ TSIGContext* tsig_ctx = NULL);
/// Parse options.
///
@@ -607,6 +598,10 @@ public:
};
/// \brief Parse the header section of the \c Message.
+ ///
+ /// NOTE: If the header has already been parsed by a previous call
+ /// to this method, this method simply returns (i.e., it does not
+ /// read from the \c buffer).
void parseHeader(isc::util::InputBuffer& buffer);
/// \brief (Re)build a \c Message object from wire-format data.
@@ -642,7 +637,8 @@ public:
/// \exception std::bad_alloc Memory allocation failure
/// \exception Others \c Name, \c Rdata, and \c EDNS classes can also throw
///
- /// \param buffer A input buffer object that stores the wire data
+ /// \param buffer A input buffer object that stores the wire
+ /// data. This method reads from position 0 in the passed buffer.
/// \param options Parse options
void fromWire(isc::util::InputBuffer& buffer, ParseOptions options
= PARSE_DEFAULT);
diff --git a/src/lib/dns/name.h b/src/lib/dns/name.h
index 02c868f..7c750e3 100644
--- a/src/lib/dns/name.h
+++ b/src/lib/dns/name.h
@@ -20,7 +20,7 @@
#include <string>
#include <vector>
-#include <exceptions/exceptions.h>
+#include <dns/exceptions.h>
namespace isc {
namespace util {
@@ -32,15 +32,6 @@ namespace dns {
class AbstractMessageRenderer;
///
-/// \brief Base class for name parser exceptions.
-///
-class NameParserException : public Exception {
-public:
- NameParserException(const char* file, size_t line, const char* what) :
- isc::Exception(file, line, what) {}
-};
-
-///
/// \brief A standard DNS module exception that is thrown if the name parser
/// encounters an empty label in the middle of a name.
///
diff --git a/src/lib/dns/nsec3hash.cc b/src/lib/dns/nsec3hash.cc
index 0e03798..4e6fea1 100644
--- a/src/lib/dns/nsec3hash.cc
+++ b/src/lib/dns/nsec3hash.cc
@@ -29,8 +29,10 @@
#include <util/hash/sha1.h>
#include <dns/name.h>
+#include <dns/labelsequence.h>
#include <dns/nsec3hash.h>
#include <dns/rdataclass.h>
+#include <dns/name_internal.h>
using namespace std;
using namespace isc::util;
@@ -84,6 +86,7 @@ public:
}
virtual std::string calculate(const Name& name) const;
+ virtual std::string calculate(const LabelSequence& ls) const;
virtual bool match(const generic::NSEC3& nsec3) const;
virtual bool match(const generic::NSEC3PARAM& nsec3param) const;
@@ -91,6 +94,8 @@ public:
const vector<uint8_t>& salt) const;
private:
+ std::string calculateForWiredata(const uint8_t* data, size_t length) const;
+
const uint8_t algorithm_;
const uint16_t iterations_;
uint8_t* salt_data_;
@@ -116,19 +121,33 @@ iterateSHA1(SHA1Context* ctx, const uint8_t* input, size_t inlength,
}
string
-NSEC3HashRFC5155::calculate(const Name& name) const {
+NSEC3HashRFC5155::calculateForWiredata(const uint8_t* data,
+ size_t length) const
+{
// We first need to normalize the name by converting all upper case
// characters in the labels to lower ones.
- obuf_.clear();
- Name name_copy(name);
- name_copy.downcase();
- name_copy.toWire(obuf_);
+
+ uint8_t name_buf[256];
+ assert(length < sizeof (name_buf));
+
+ const uint8_t *p1 = data;
+ uint8_t *p2 = name_buf;
+ while (*p1 != 0) {
+ char len = *p1;
+
+ *p2++ = *p1++;
+ while (len--) {
+ *p2++ = isc::dns::name::internal::maptolower[*p1++];
+ }
+ }
+
+ *p2 = *p1;
uint8_t* const digest = &digest_[0];
assert(digest_.size() == SHA1_HASHSIZE);
- iterateSHA1(&sha1_ctx_, static_cast<const uint8_t*>(obuf_.getData()),
- obuf_.getLength(), salt_data_, salt_length_, digest);
+ iterateSHA1(&sha1_ctx_, name_buf, length,
+ salt_data_, salt_length_, digest);
for (unsigned int n = 0; n < iterations_; ++n) {
iterateSHA1(&sha1_ctx_, digest, SHA1_HASHSIZE,
salt_data_, salt_length_, digest);
@@ -137,6 +156,25 @@ NSEC3HashRFC5155::calculate(const Name& name) const {
return (encodeBase32Hex(digest_));
}
+string
+NSEC3HashRFC5155::calculate(const Name& name) const {
+ obuf_.clear();
+ name.toWire(obuf_);
+
+ return (calculateForWiredata(static_cast<const uint8_t*>(obuf_.getData()),
+ obuf_.getLength()));
+}
+
+string
+NSEC3HashRFC5155::calculate(const LabelSequence& ls) const {
+ assert(ls.isAbsolute());
+
+ size_t length;
+ const uint8_t* data = ls.getData(&length);
+
+ return (calculateForWiredata(data, length));
+}
+
bool
NSEC3HashRFC5155::match(uint8_t algorithm, uint16_t iterations,
const vector<uint8_t>& salt) const
diff --git a/src/lib/dns/nsec3hash.h b/src/lib/dns/nsec3hash.h
index f1ca1a3..b58f0c9 100644
--- a/src/lib/dns/nsec3hash.h
+++ b/src/lib/dns/nsec3hash.h
@@ -23,6 +23,7 @@
namespace isc {
namespace dns {
class Name;
+class LabelSequence;
namespace rdata {
namespace generic {
@@ -122,26 +123,40 @@ public:
/// (SHA-1) is supported
/// \param iterations the number of iterations
/// \param salt_data the salt data as a byte array
- /// \param salt_data_length the length of the salt data
+ /// \param salt_length the length of the salt data
static NSEC3Hash* create(uint8_t algorithm, uint16_t iterations,
const uint8_t* salt_data, size_t salt_length);
/// \brief The destructor.
virtual ~NSEC3Hash() {}
- /// \brief Calculate the NSEC3 hash.
+ /// \brief Calculate the NSEC3 hash (Name variant).
///
/// This method calculates the NSEC3 hash value for the given \c name
/// with the hash parameters (algorithm, iterations and salt) given at
/// construction, and returns the value as a base32hex-encoded string
/// (without containing any white spaces). All US-ASCII letters in the
- /// string will be upper cased.
+ /// string will be lower cased.
///
/// \param name The domain name for which the hash value is to be
/// calculated.
/// \return Base32hex-encoded string of the hash value.
virtual std::string calculate(const Name& name) const = 0;
+ /// \brief Calculate the NSEC3 hash (LabelSequence variant).
+ ///
+ /// This method calculates the NSEC3 hash value for the given
+ /// absolute LabelSequence \c ls with the hash parameters
+ /// (algorithm, iterations and salt) given at construction, and
+ /// returns the value as a base32hex-encoded string (without
+ /// containing any white spaces). All US-ASCII letters in the
+ /// string will be lower cased.
+ ///
+ /// \param ls The absolute label sequence for which the hash value
+ /// is to be calculated.
+ /// \return Base32hex-encoded string of the hash value.
+ virtual std::string calculate(const LabelSequence& ls) const = 0;
+
/// \brief Match given NSEC3 parameters with that of the hash.
///
/// This method compares NSEC3 parameters used for hash calculation
@@ -233,7 +248,7 @@ public:
/// (SHA-1) is supported
/// \param iterations the number of iterations
/// \param salt_data the salt data as a byte array
- /// \param salt_data_length the length of the salt data
+ /// \param salt_length the length of the salt data
virtual NSEC3Hash* create(uint8_t algorithm, uint16_t iterations,
const uint8_t* salt_data, size_t salt_length)
const = 0;
diff --git a/src/lib/dns/python/message_python.cc b/src/lib/dns/python/message_python.cc
index 4dfee24..d86754b 100644
--- a/src/lib/dns/python/message_python.cc
+++ b/src/lib/dns/python/message_python.cc
@@ -696,10 +696,10 @@ Message_toWire(s_Message* self, PyObject* args) {
PyObject* mr;
PyObject* tsig_ctx = NULL;
- if (PyArg_ParseTuple(args, "O!|O!", &messagerenderer_type, &mr,
- &tsigcontext_type, &tsig_ctx)) {
+ if (PyArg_ParseTuple(args, "O!|O", &messagerenderer_type, &mr,
+ &tsig_ctx)) {
try {
- if (tsig_ctx == NULL) {
+ if ((tsig_ctx == NULL) || (tsig_ctx == Py_None)) {
self->cppobj->toWire(PyMessageRenderer_ToMessageRenderer(mr));
} else {
self->cppobj->toWire(PyMessageRenderer_ToMessageRenderer(mr),
@@ -726,7 +726,7 @@ Message_toWire(s_Message* self, PyObject* args) {
}
PyErr_Clear();
PyErr_SetString(PyExc_TypeError,
- "toWire argument must be a MessageRenderer");
+ "Bad to_wire() arguments were passed");
return (NULL);
}
diff --git a/src/lib/dns/python/name_python.cc b/src/lib/dns/python/name_python.cc
index 2799db9..ecdae84 100644
--- a/src/lib/dns/python/name_python.cc
+++ b/src/lib/dns/python/name_python.cc
@@ -537,6 +537,7 @@ namespace python {
// Initialization and addition of these go in the module init at the
// end
//
+PyObject* po_NameParserException;
PyObject* po_EmptyLabel;
PyObject* po_TooLongName;
PyObject* po_TooLongLabel;
@@ -544,7 +545,6 @@ PyObject* po_BadLabelType;
PyObject* po_BadEscape;
PyObject* po_IncompleteName;
PyObject* po_InvalidBufferPosition;
-PyObject* po_DNSMessageFORMERR;
//
// Definition of enums
diff --git a/src/lib/dns/python/name_python.h b/src/lib/dns/python/name_python.h
index d18c0d9..2cce999 100644
--- a/src/lib/dns/python/name_python.h
+++ b/src/lib/dns/python/name_python.h
@@ -23,6 +23,7 @@ class Name;
namespace python {
+extern PyObject* po_NameParserException;
extern PyObject* po_EmptyLabel;
extern PyObject* po_TooLongName;
extern PyObject* po_TooLongLabel;
@@ -30,7 +31,6 @@ extern PyObject* po_BadLabelType;
extern PyObject* po_BadEscape;
extern PyObject* po_IncompleteName;
extern PyObject* po_InvalidBufferPosition;
-extern PyObject* po_DNSMessageFORMERR;
//
// Declaration of enums
diff --git a/src/lib/dns/python/pydnspp.cc b/src/lib/dns/python/pydnspp.cc
index 30dc090..b0bda22 100644
--- a/src/lib/dns/python/pydnspp.cc
+++ b/src/lib/dns/python/pydnspp.cc
@@ -28,6 +28,7 @@
#include <Python.h>
#include <structmember.h>
+#include <dns/exceptions.h>
#include <dns/message.h>
#include <dns/opcode.h>
#include <dns/tsig.h>
@@ -152,8 +153,14 @@ initModulePart_Message(PyObject* mod) {
PyErr_NewException("pydnspp.InvalidMessageUDPSize", NULL, NULL);
PyObjectContainer(po_InvalidMessageUDPSize).installToModule(
mod, "InvalidMessageUDPSize");
+ po_DNSMessageFORMERR =
+ PyErr_NewException("pydnspp.DNSMessageFORMERR",
+ po_DNSProtocolError, NULL);
+ PyObjectContainer(po_DNSMessageFORMERR).installToModule(
+ mod, "DNSMessageFORMERR");
po_DNSMessageBADVERS =
- PyErr_NewException("pydnspp.DNSMessageBADVERS", NULL, NULL);
+ PyErr_NewException("pydnspp.DNSMessageBADVERS",
+ po_DNSProtocolError, NULL);
PyObjectContainer(po_DNSMessageBADVERS).installToModule(
mod, "DNSMessageBADVERS");
po_UnknownNSEC3HashAlgorithm =
@@ -243,36 +250,40 @@ initModulePart_Name(PyObject* mod) {
// Add the exceptions to the module
try {
- po_EmptyLabel = PyErr_NewException("pydnspp.EmptyLabel", NULL, NULL);
+ po_NameParserException =
+ PyErr_NewException("pydnspp.NameParserException",
+ po_DNSTextError, NULL);
+ PyObjectContainer(po_NameParserException)
+ .installToModule(mod, "NameParserException");
+
+ po_EmptyLabel = PyErr_NewException("pydnspp.EmptyLabel",
+ po_NameParserException, NULL);
PyObjectContainer(po_EmptyLabel).installToModule(mod, "EmptyLabel");
- po_TooLongName = PyErr_NewException("pydnspp.TooLongName", NULL, NULL);
+ po_TooLongName = PyErr_NewException("pydnspp.TooLongName",
+ po_NameParserException, NULL);
PyObjectContainer(po_TooLongName).installToModule(mod, "TooLongName");
- po_TooLongLabel = PyErr_NewException("pydnspp.TooLongLabel", NULL, NULL);
+ po_TooLongLabel = PyErr_NewException("pydnspp.TooLongLabel",
+ po_NameParserException, NULL);
PyObjectContainer(po_TooLongLabel).installToModule(mod, "TooLongLabel");
- po_BadLabelType = PyErr_NewException("pydnspp.BadLabelType", NULL, NULL);
+ po_BadLabelType = PyErr_NewException("pydnspp.BadLabelType",
+ po_NameParserException, NULL);
PyObjectContainer(po_BadLabelType).installToModule(mod, "BadLabelType");
- po_BadEscape = PyErr_NewException("pydnspp.BadEscape", NULL, NULL);
+ po_BadEscape = PyErr_NewException("pydnspp.BadEscape",
+ po_NameParserException, NULL);
PyObjectContainer(po_BadEscape).installToModule(mod, "BadEscape");
- po_IncompleteName = PyErr_NewException("pydnspp.IncompleteName", NULL,
- NULL);
+ po_IncompleteName = PyErr_NewException("pydnspp.IncompleteName",
+ po_NameParserException, NULL);
PyObjectContainer(po_IncompleteName).installToModule(mod, "IncompleteName");
po_InvalidBufferPosition =
PyErr_NewException("pydnspp.InvalidBufferPosition", NULL, NULL);
PyObjectContainer(po_InvalidBufferPosition).installToModule(
mod, "InvalidBufferPosition");
-
- // This one could have gone into the message_python.cc file, but is
- // already needed here.
- po_DNSMessageFORMERR = PyErr_NewException("pydnspp.DNSMessageFORMERR",
- NULL, NULL);
- PyObjectContainer(po_DNSMessageFORMERR).installToModule(
- mod, "DNSMessageFORMERR");
} catch (const std::exception& ex) {
const std::string ex_what =
"Unexpected failure in Name initialization: " +
@@ -865,14 +876,31 @@ PyInit_pydnspp(void) {
PyObjectContainer(po_IscException).installToModule(mod, "IscException");
po_InvalidOperation = PyErr_NewException("pydnspp.InvalidOperation",
- NULL, NULL);
- PyObjectContainer(po_InvalidOperation).installToModule(
- mod, "InvalidOperation");
+ po_IscException, NULL);
+ PyObjectContainer(po_InvalidOperation)
+ .installToModule(mod, "InvalidOperation");
po_InvalidParameter = PyErr_NewException("pydnspp.InvalidParameter",
- NULL, NULL);
- PyObjectContainer(po_InvalidParameter).installToModule(
- mod, "InvalidParameter");
+ po_IscException, NULL);
+ PyObjectContainer(po_InvalidParameter)
+ .installToModule(mod, "InvalidParameter");
+
+ // Add DNS exceptions
+ po_DNSException = PyErr_NewException("pydnspp.DNSException",
+ po_IscException, NULL);
+ PyObjectContainer(po_DNSException)
+ .installToModule(mod, "DNSException");
+
+ po_DNSTextError = PyErr_NewException("pydnspp.DNSTextError",
+ po_DNSException, NULL);
+ PyObjectContainer(po_DNSTextError)
+ .installToModule(mod, "DNSTextError");
+
+ po_DNSProtocolError = PyErr_NewException("pydnspp.DNSProtocolError",
+ po_DNSException, NULL);
+ PyObjectContainer(po_DNSProtocolError)
+ .installToModule(mod, "DNSProtocolError");
+
} catch (const std::exception& ex) {
const std::string ex_what =
"Unexpected failure in pydnspp initialization: " +
diff --git a/src/lib/dns/python/pydnspp_common.cc b/src/lib/dns/python/pydnspp_common.cc
index 4250db7..c95f877 100644
--- a/src/lib/dns/python/pydnspp_common.cc
+++ b/src/lib/dns/python/pydnspp_common.cc
@@ -50,7 +50,11 @@ PyObject* po_IscException;
PyObject* po_InvalidOperation;
PyObject* po_InvalidParameter;
-// For our own isc::dns::Exception
+// For DNS exceptions
+PyObject* po_DNSException;
+PyObject* po_DNSTextError;
+PyObject* po_DNSProtocolError;
+PyObject* po_DNSMessageFORMERR;
PyObject* po_DNSMessageBADVERS;
diff --git a/src/lib/dns/python/pydnspp_common.h b/src/lib/dns/python/pydnspp_common.h
index 9c27cfd..77191dc 100644
--- a/src/lib/dns/python/pydnspp_common.h
+++ b/src/lib/dns/python/pydnspp_common.h
@@ -34,6 +34,10 @@ extern PyObject* po_InvalidOperation;
extern PyObject* po_InvalidParameter;
// For our own isc::dns::Exception
+extern PyObject* po_DNSException;
+extern PyObject* po_DNSTextError;
+extern PyObject* po_DNSProtocolError;
+extern PyObject* po_DNSMessageFORMERR;
extern PyObject* po_DNSMessageBADVERS;
// This function reads 'bytes' from a sequence
diff --git a/src/lib/dns/python/rdata_python.cc b/src/lib/dns/python/rdata_python.cc
index 20f67c8..4b52002 100644
--- a/src/lib/dns/python/rdata_python.cc
+++ b/src/lib/dns/python/rdata_python.cc
@@ -25,6 +25,7 @@
#include "rrclass_python.h"
#include "messagerenderer_python.h"
#include "name_python.h"
+#include "pydnspp_common.h"
using namespace isc::dns;
using namespace isc::dns::python;
@@ -104,9 +105,9 @@ Rdata_init(PyObject* self_p, PyObject* args, PyObject*) {
const char* s;
const char* data;
Py_ssize_t len;
- s_Rdata* self(static_cast<s_Rdata*>(self_p));
-
try {
+ s_Rdata* self = static_cast<s_Rdata*>(self_p);
+
// Create from string
if (PyArg_ParseTuple(args, "O!O!s", &rrtype_type, &rrtype,
&rrclass_type, &rrclass,
diff --git a/src/lib/dns/python/rrset_python.cc b/src/lib/dns/python/rrset_python.cc
index de5925a..4d864d1 100644
--- a/src/lib/dns/python/rrset_python.cc
+++ b/src/lib/dns/python/rrset_python.cc
@@ -53,6 +53,7 @@ int RRset_init(s_RRset* self, PyObject* args);
void RRset_destroy(s_RRset* self);
PyObject* RRset_getRdataCount(PyObject* self, PyObject* args);
+PyObject* RRset_getLength(PyObject* self, PyObject* args);
PyObject* RRset_getName(PyObject* self, PyObject* args);
PyObject* RRset_getClass(PyObject* self, PyObject* args);
PyObject* RRset_getType(PyObject* self, PyObject* args);
@@ -70,6 +71,8 @@ PyObject* RRset_removeRRsig(PyObject* self, PyObject* args);
PyMethodDef RRset_methods[] = {
{ "get_rdata_count", RRset_getRdataCount, METH_NOARGS,
"Returns the number of rdata fields." },
+ { "get_length", RRset_getLength, METH_NOARGS,
+ "Returns the wire format length of the RRset." },
{ "get_name", RRset_getName, METH_NOARGS,
"Returns the name of the RRset, as a Name object." },
{ "get_class", RRset_getClass, METH_NOARGS,
@@ -136,6 +139,18 @@ RRset_getRdataCount(PyObject* self, PyObject*) {
}
PyObject*
+RRset_getLength(PyObject* self, PyObject*) {
+ try {
+ return (Py_BuildValue("H", static_cast<const s_RRset*>(self)->cppobj->
+ getLength()));
+ } catch (const EmptyRRset& ers) {
+ PyErr_Clear();
+ PyErr_SetString(po_EmptyRRset, ers.what());
+ return (NULL);
+ }
+}
+
+PyObject*
RRset_getName(PyObject* self, PyObject*) {
try {
return (createNameObject(static_cast<const s_RRset*>(self)->cppobj->
@@ -236,9 +251,9 @@ PyObject*
RRset_toWire(PyObject* self_p, PyObject* args) {
PyObject* bytes;
PyObject* mr;
- const s_RRset* self(static_cast<const s_RRset*>(self_p));
try {
+ const s_RRset* self = static_cast<const s_RRset*>(self_p);
if (PyArg_ParseTuple(args, "O", &bytes) && PySequence_Check(bytes)) {
PyObject* bytes_o = bytes;
diff --git a/src/lib/dns/python/tests/Makefile.am b/src/lib/dns/python/tests/Makefile.am
index 4574303..8fe8bb7 100644
--- a/src/lib/dns/python/tests/Makefile.am
+++ b/src/lib/dns/python/tests/Makefile.am
@@ -6,6 +6,7 @@ PYTESTS += name_python_test.py
PYTESTS += nsec3hash_python_test.py
PYTESTS += question_python_test.py
PYTESTS += opcode_python_test.py
+PYTESTS += pydnspp_python_test.py
PYTESTS += rcode_python_test.py
PYTESTS += rdata_python_test.py
PYTESTS += rrclass_python_test.py
diff --git a/src/lib/dns/python/tests/message_python_test.py b/src/lib/dns/python/tests/message_python_test.py
index 996ef89..6f017df 100644
--- a/src/lib/dns/python/tests/message_python_test.py
+++ b/src/lib/dns/python/tests/message_python_test.py
@@ -371,6 +371,13 @@ class MessageTest(unittest.TestCase):
self.__common_tsigmessage_setup()
self.__common_tsig_checks("message_toWire2.wire")
+ def test_to_wire_with_tsig_none(self):
+ message_render = create_message()
+ renderer = MessageRenderer()
+ message_render.to_wire(renderer, None)
+ self.assertEqual(b'\x105\x85\x00\x00\x01\x00\x02\x00\x00\x00\x00\x04test\x07example\x03com\x00\x00\x01\x00\x01\xc0\x0c\x00\x01\x00\x01\x00\x00\x0e\x10\x00\x04\xc0\x00\x02\x01\xc0\x0c\x00\x01\x00\x01\x00\x00\x0e\x10\x00\x04\xc0\x00\x02\x02',
+ renderer.get_data())
+
def test_to_wire_with_edns_tsig(self):
fix_current_time(0x4db60d1f)
self.r.set_qid(0x6cd)
diff --git a/src/lib/dns/python/tests/name_python_test.py b/src/lib/dns/python/tests/name_python_test.py
index 8ea2e35..8160716 100644
--- a/src/lib/dns/python/tests/name_python_test.py
+++ b/src/lib/dns/python/tests/name_python_test.py
@@ -14,7 +14,7 @@
# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#
-# Tests for the messagerenderer part of the pydnspp module
+# Tests for the name part of the pydnspp module
#
import unittest
@@ -97,6 +97,16 @@ class NameTest(unittest.TestCase):
self.assertRaises(DNSMessageFORMERR, Name, b, 0)
self.assertRaises(IndexError, Name, b, -1)
+ def test_exception_hierarchy(self):
+ self.assertTrue(isinstance(EmptyLabel(), NameParserException))
+ self.assertTrue(isinstance(TooLongLabel(), NameParserException))
+ self.assertTrue(isinstance(BadLabelType(), NameParserException))
+ self.assertTrue(isinstance(BadEscape(), NameParserException))
+ self.assertTrue(isinstance(TooLongName(), NameParserException))
+ self.assertTrue(isinstance(IncompleteName(), NameParserException))
+
+ self.assertTrue(isinstance(NameParserException(), DNSTextError))
+
def test_at(self):
self.assertEqual(7, self.name1.at(0))
self.assertEqual(101, self.name1.at(1))
diff --git a/src/lib/dns/python/tests/pydnspp_python_test.py b/src/lib/dns/python/tests/pydnspp_python_test.py
new file mode 100644
index 0000000..574fc00
--- /dev/null
+++ b/src/lib/dns/python/tests/pydnspp_python_test.py
@@ -0,0 +1,34 @@
+# Copyright (C) 2014 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.
+
+#
+# Tests for the common part of the pydnspp module
+#
+
+import unittest
+import os
+from pydnspp import *
+
+class CommonTest(unittest.TestCase):
+ def test_exception_hierarchy(self):
+ self.assertTrue(isinstance(InvalidOperation(), IscException))
+ self.assertTrue(isinstance(InvalidParameter(), IscException))
+
+ self.assertTrue(isinstance(DNSException(), IscException))
+ self.assertTrue(isinstance(DNSTextError(), DNSException))
+ self.assertTrue(isinstance(DNSProtocolError(), DNSException))
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/src/lib/dns/python/tests/rrset_python_test.py b/src/lib/dns/python/tests/rrset_python_test.py
index 9592b42..d848d27 100644
--- a/src/lib/dns/python/tests/rrset_python_test.py
+++ b/src/lib/dns/python/tests/rrset_python_test.py
@@ -45,6 +45,24 @@ class TestModuleSpec(unittest.TestCase):
self.assertEqual(i, self.rrset_a_empty.get_rdata_count())
self.rrset_a_empty.add_rdata(Rdata(RRType("A"), RRClass("IN"), "192.0.2.1"))
+ def test_get_length(self):
+ # Empty RRset should throw
+ self.assertRaises(EmptyRRset, self.rrset_a_empty.get_length);
+
+ # Unless it is type ANY or NONE:
+ # test.example.com = 1 + 4 + 1 + 7 + 1 + 3 + 1 = 18 octets
+ # TYPE field = 2 octets
+ # CLASS field = 2 octets
+ # TTL field = 4 octets
+ # RDLENGTH field = 2 octets
+ # Total = 18 + 2 + 2 + 4 + 2 = 28 octets
+ self.assertEqual(28, self.rrset_any_a_empty.get_length())
+
+ # Single A RR:
+ # 28 octets (above) + 4 octets (A RDATA) = 32 octets
+ # With 2 A RRs:
+ self.assertEqual(32 + 32, self.rrset_a.get_length())
+
def test_get_name(self):
self.assertEqual(self.test_name, self.rrset_a.get_name())
self.assertEqual(self.test_domain, self.rrset_ns.get_name())
diff --git a/src/lib/dns/python/tsig_python.cc b/src/lib/dns/python/tsig_python.cc
index abb7733..c5b5421 100644
--- a/src/lib/dns/python/tsig_python.cc
+++ b/src/lib/dns/python/tsig_python.cc
@@ -334,14 +334,21 @@ PyTSIGContext_Check(PyObject* obj) {
return (PyObject_TypeCheck(obj, &tsigcontext_type));
}
-TSIGContext&
+TSIGContext*
PyTSIGContext_ToTSIGContext(PyObject* tsigcontext_obj) {
if (tsigcontext_obj == NULL) {
isc_throw(PyCPPWrapperException,
"obj argument NULL in TSIGContext PyObject conversion");
}
+
+ if (!PyTSIGContext_Check(tsigcontext_obj)) {
+ isc_throw(TSIGContextError,
+ "obj argument is of wrong type in TSIGContext "
+ "PyObject conversion");
+ }
+
s_TSIGContext* tsigcontext = static_cast<s_TSIGContext*>(tsigcontext_obj);
- return (*tsigcontext->cppobj);
+ return (tsigcontext->cppobj);
}
} // namespace python
diff --git a/src/lib/dns/python/tsig_python.h b/src/lib/dns/python/tsig_python.h
index 0bd57d7..bbef56b 100644
--- a/src/lib/dns/python/tsig_python.h
+++ b/src/lib/dns/python/tsig_python.h
@@ -36,8 +36,8 @@ extern PyObject* po_TSIGContextError;
/// \return true if the object is of type TSIGContext, false otherwise
bool PyTSIGContext_Check(PyObject* obj);
-/// \brief Returns a reference to the TSIGContext object contained within the given
-/// Python object.
+/// \brief Returns a pointer to the TSIGContext object contained within
+/// the given Python object.
///
/// \note The given object MUST be of type TSIGContext; this can be checked with
/// either the right call to ParseTuple("O!"), or with PyTSIGContext_Check()
@@ -46,7 +46,7 @@ bool PyTSIGContext_Check(PyObject* obj);
/// may be destroyed, the caller must copy it itself.
///
/// \param tsigcontext_obj The tsigcontext object to convert
-TSIGContext& PyTSIGContext_ToTSIGContext(PyObject* tsigcontext_obj);
+TSIGContext* PyTSIGContext_ToTSIGContext(PyObject* tsigcontext_obj);
} // namespace python
diff --git a/src/lib/dns/rdata.cc b/src/lib/dns/rdata.cc
index f42c349..a34f887 100644
--- a/src/lib/dns/rdata.cc
+++ b/src/lib/dns/rdata.cc
@@ -15,6 +15,7 @@
#include <exceptions/exceptions.h>
#include <util/buffer.h>
+#include <util/encode/hex.h>
#include <dns/name.h>
#include <dns/messagerenderer.h>
@@ -46,6 +47,15 @@ namespace isc {
namespace dns {
namespace rdata {
+uint16_t
+Rdata::getLength() const {
+ OutputBuffer obuffer(0);
+
+ toWire(obuffer);
+
+ return (obuffer.getLength());
+}
+
// XXX: we need to specify std:: for string to help doxygen match the
// function signature with that given in the header file.
RdataPtr
@@ -211,94 +221,100 @@ Generic::Generic(isc::util::InputBuffer& buffer, size_t rdata_len) {
impl_ = new GenericImpl(data);
}
-void
-Generic::constructHelper(const std::string& rdata_string) {
- istringstream iss(rdata_string);
- string unknown_mark;
- iss >> unknown_mark;
- if (unknown_mark != "\\#") {
+GenericImpl*
+Generic::constructFromLexer(MasterLexer& lexer) {
+ const MasterToken& token = lexer.getNextToken(MasterToken::STRING);
+ if (token.getString() != "\\#") {
isc_throw(InvalidRdataText,
- "Missing the special token (\\#) for generic RDATA encoding");
+ "Missing the special token (\\#) for "
+ "unknown RDATA encoding");
}
- // RDLENGTH: read into a string so that we can easily reject invalid tokens
- string rdlen_txt;
- iss >> rdlen_txt;
- istringstream iss_rdlen(rdlen_txt);
- int32_t rdlen;
- iss_rdlen >> rdlen;
- if (iss_rdlen.rdstate() != ios::eofbit) {
- isc_throw(InvalidRdataText,
- "Invalid representation for a generic RDLENGTH");
+ // Initialize with an absurd value.
+ uint32_t rdlen = 65536;
+
+ try {
+ rdlen = lexer.getNextToken(MasterToken::NUMBER).getNumber();
+ } catch (const MasterLexer::LexerError& ex) {
+ isc_throw(InvalidRdataLength,
+ "Unknown RDATA length is invalid");
}
- if (rdlen < 0 || rdlen > 0xffff) {
- isc_throw(InvalidRdataLength, "RDATA length is out of range");
+
+ if (rdlen > 65535) {
+ isc_throw(InvalidRdataLength,
+ "Unknown RDATA length is out of range: " << rdlen);
}
- iss >> ws; // skip any white spaces
- // Hexadecimal encoding of RDATA: each segment must consist of an even
- // number of hex digits.
vector<uint8_t> data;
- while (!iss.eof() && data.size() < rdlen) {
- // extract two characters, which should compose a single byte of data.
- char buf[2];
- iss.read(buf, sizeof(buf));
- if ((iss.rdstate() & (ios::badbit | ios::failbit)) != 0) {
- isc_throw(InvalidRdataText,
- "Invalid hex encoding of generic RDATA");
+
+ if (rdlen > 0) {
+ string hex_txt;
+ string hex_part;
+ // Whitespace is allowed within hex data, so read to the end of input.
+ while (true) {
+ const MasterToken& token =
+ lexer.getNextToken(MasterToken::STRING, true);
+ if ((token.getType() == MasterToken::END_OF_FILE) ||
+ (token.getType() == MasterToken::END_OF_LINE)) {
+ // Unget the last read token as createRdata() expects us
+ // to leave it at the end-of-line or end-of-file when we
+ // return.
+ lexer.ungetToken();
+ break;
+ }
+ token.getString(hex_part);
+ hex_txt.append(hex_part);
}
- // convert it to a single byte integer as a hex digit.
- istringstream iss_byte(string(buf, sizeof(buf)));
- unsigned int ch;
- iss_byte >> hex >> ch;
- if (iss_byte.rdstate() != ios::eofbit) {
+ try {
+ isc::util::encode::decodeHex(hex_txt, data);
+ } catch (const isc::BadValue& ex) {
isc_throw(InvalidRdataText,
- "Invalid hex encoding of generic RDATA");
+ "Invalid hex encoding of generic RDATA: " << ex.what());
}
- data.push_back(ch);
- iss >> ws; // skip spaces
- }
-
- if (!iss.eof()) {
- isc_throw(InvalidRdataLength,
- "RDLENGTH is too small for generic RDATA");
}
if (data.size() != rdlen) {
isc_throw(InvalidRdataLength,
- "Generic RDATA code doesn't match RDLENGTH");
+ "Size of unknown RDATA hex data doesn't match RDLENGTH: "
+ << data.size() << " vs. " << rdlen);
}
- impl_ = new GenericImpl(data);
-}
-
-Generic::Generic(const std::string& rdata_string) {
- constructHelper(rdata_string);
+ return (new GenericImpl(data));
}
-Generic::Generic(MasterLexer& lexer, const Name*,
- MasterLoader::Options,
- MasterLoaderCallbacks&)
+Generic::Generic(const std::string& rdata_string) :
+ impl_(NULL)
{
- std::string s;
+ // We use auto_ptr here because if there is an exception in this
+ // constructor, the destructor is not called and there could be a
+ // leak of the GenericImpl that constructFromLexer() returns.
+ std::auto_ptr<GenericImpl> impl_ptr(NULL);
- while (true) {
- const MasterToken& token = lexer.getNextToken();
- if ((token.getType() == MasterToken::END_OF_FILE) ||
- (token.getType() == MasterToken::END_OF_LINE)) {
- lexer.ungetToken(); // let the upper layer handle the end-of token
- break;
- }
+ try {
+ std::istringstream ss(rdata_string);
+ MasterLexer lexer;
+ lexer.pushSource(ss);
- if (!s.empty()) {
- s += " ";
- }
+ impl_ptr.reset(constructFromLexer(lexer));
- s += token.getString();
+ if (lexer.getNextToken().getType() != MasterToken::END_OF_FILE) {
+ isc_throw(InvalidRdataText, "extra input text for unknown RDATA: "
+ << rdata_string);
+ }
+ } catch (const MasterLexer::LexerError& ex) {
+ isc_throw(InvalidRdataText, "Failed to construct unknown RDATA "
+ "from '" << rdata_string << "': " << ex.what());
}
- constructHelper(s);
+ impl_ = impl_ptr.release();
+}
+
+Generic::Generic(MasterLexer& lexer, const Name*,
+ MasterLoader::Options,
+ MasterLoaderCallbacks&) :
+ impl_(constructFromLexer(lexer))
+{
}
Generic::~Generic() {
diff --git a/src/lib/dns/rdata.h b/src/lib/dns/rdata.h
index 3fe0c74..eb07937 100644
--- a/src/lib/dns/rdata.h
+++ b/src/lib/dns/rdata.h
@@ -19,7 +19,7 @@
#include <dns/master_loader.h>
#include <dns/master_loader_callbacks.h>
-#include <exceptions/exceptions.h>
+#include <dns/exceptions.h>
#include <boost/shared_ptr.hpp>
@@ -42,20 +42,20 @@ namespace rdata {
/// \brief A standard DNS module exception that is thrown if RDATA parser
/// encounters an invalid or inconsistent data length.
///
-class InvalidRdataLength : public Exception {
+class InvalidRdataLength : public DNSTextError {
public:
InvalidRdataLength(const char* file, size_t line, const char* what) :
- isc::Exception(file, line, what) {}
+ DNSTextError(file, line, what) {}
};
///
/// \brief A standard DNS module exception that is thrown if RDATA parser
/// fails to recognize a given textual representation.
///
-class InvalidRdataText : public Exception {
+class InvalidRdataText : public DNSTextError {
public:
InvalidRdataText(const char* file, size_t line, const char* what) :
- isc::Exception(file, line, what) {}
+ DNSTextError(file, line, what) {}
};
///
@@ -63,10 +63,10 @@ public:
/// encounters a character-string (as defined in RFC1035) exceeding
/// the maximum allowable length (\c MAX_CHARSTRING_LEN).
///
-class CharStringTooLong : public Exception {
+class CharStringTooLong : public DNSTextError {
public:
CharStringTooLong(const char* file, size_t line, const char* what) :
- isc::Exception(file, line, what) {}
+ DNSTextError(file, line, what) {}
};
// Forward declaration to define RdataPtr.
@@ -221,6 +221,21 @@ public:
/// \return > 0 if \c this would be sorted after \c other.
virtual int compare(const Rdata& other) const = 0;
//@}
+
+ /// \brief Get the wire format length of an Rdata.
+ ///
+ /// IMPLEMENTATION NOTE: Currently this base class implementation is
+ /// non-optimal as it renders the wire data to a buffer and returns
+ /// the buffer's length. What would perform better is to add
+ /// implementations of \c getLength() method to every RDATA
+ /// type. This is why this method is virtual. Once all Rdata types
+ /// have \c getLength() implementations, this base class
+ /// implementation must be removed and the method should become a
+ /// pure interface.
+ ///
+ /// \return The length of the wire format representation of the
+ /// RDATA.
+ virtual uint16_t getLength() const;
};
namespace generic {
@@ -378,7 +393,7 @@ public:
//@}
private:
- void constructHelper(const std::string& rdata_string);
+ GenericImpl* constructFromLexer(MasterLexer& lexer);
GenericImpl* impl_;
};
diff --git a/src/lib/dns/rdata/generic/sshfp_44.cc b/src/lib/dns/rdata/generic/sshfp_44.cc
index 2865ed1..4c1e832 100644
--- a/src/lib/dns/rdata/generic/sshfp_44.cc
+++ b/src/lib/dns/rdata/generic/sshfp_44.cc
@@ -14,8 +14,6 @@
#include <config.h>
-#include <string>
-
#include <boost/lexical_cast.hpp>
#include <exceptions/exceptions.h>
@@ -38,7 +36,7 @@ using namespace isc::util::encode;
struct SSHFPImpl {
// straightforward representation of SSHFP RDATA fields
SSHFPImpl(uint8_t algorithm, uint8_t fingerprint_type,
- vector<uint8_t>& fingerprint) :
+ const vector<uint8_t>& fingerprint) :
algorithm_(algorithm),
fingerprint_type_(fingerprint_type),
fingerprint_(fingerprint)
@@ -82,7 +80,11 @@ SSHFP::constructFromLexer(MasterLexer& lexer) {
// If fingerprint is missing, it's OK. See the API documentation of the
// constructor.
if (fingerprint_str.size() > 0) {
- decodeHex(fingerprint_str, fingerprint);
+ try {
+ decodeHex(fingerprint_str, fingerprint);
+ } catch (const isc::BadValue& e) {
+ isc_throw(InvalidRdataText, "Bad SSHFP fingerprint: " << e.what());
+ }
}
return (new SSHFPImpl(algorithm, fingerprint_type, fingerprint));
@@ -102,8 +104,9 @@ SSHFP::constructFromLexer(MasterLexer& lexer) {
/// valid hex encoding of the fingerprint. For compatibility with BIND 9,
/// whitespace is allowed in the hex text (RFC4255 is silent on the matter).
///
-/// \throw InvalidRdataText if any fields are missing, out of their valid
-/// ranges, or incorrect.
+/// \throw InvalidRdataText if any fields are missing, are out of their
+/// valid ranges or are incorrect, or if the fingerprint is not a valid
+/// hex string.
///
/// \param sshfp_str A string containing the RDATA to be created
SSHFP::SSHFP(const string& sshfp_str) :
@@ -128,9 +131,6 @@ SSHFP::SSHFP(const string& sshfp_str) :
} catch (const MasterLexer::LexerError& ex) {
isc_throw(InvalidRdataText, "Failed to construct SSHFP from '" <<
sshfp_str << "': " << ex.what());
- } catch (const isc::BadValue& e) {
- isc_throw(InvalidRdataText,
- "Bad SSHFP fingerprint: " << e.what());
}
impl_ = impl_ptr.release();
@@ -142,9 +142,8 @@ SSHFP::SSHFP(const string& sshfp_str) :
/// of an SSHFP RDATA.
///
/// \throw MasterLexer::LexerError General parsing error such as missing field.
-/// \throw InvalidRdataText Fields are out of their valid range, or are
-/// incorrect.
-/// \throw BadValue Fingerprint is not a valid hex string.
+/// \throw InvalidRdataText Fields are out of their valid range or are
+/// incorrect, or if the fingerprint is not a valid hex string.
///
/// \param lexer A \c MasterLexer object parsing a master file for the
/// RDATA to be created
@@ -293,8 +292,13 @@ SSHFP::getFingerprintType() const {
return (impl_->fingerprint_type_);
}
+const std::vector<uint8_t>&
+SSHFP::getFingerprint() const {
+ return (impl_->fingerprint_);
+}
+
size_t
-SSHFP::getFingerprintLen() const {
+SSHFP::getFingerprintLength() const {
return (impl_->fingerprint_.size());
}
diff --git a/src/lib/dns/rdata/generic/sshfp_44.h b/src/lib/dns/rdata/generic/sshfp_44.h
index 28ce0f3..85ae806 100644
--- a/src/lib/dns/rdata/generic/sshfp_44.h
+++ b/src/lib/dns/rdata/generic/sshfp_44.h
@@ -17,6 +17,7 @@
#include <stdint.h>
#include <string>
+#include <vector>
#include <dns/name.h>
#include <dns/rdata.h>
@@ -45,7 +46,8 @@ public:
///
uint8_t getAlgorithmNumber() const;
uint8_t getFingerprintType() const;
- size_t getFingerprintLen() const;
+ const std::vector<uint8_t>& getFingerprint() const;
+ size_t getFingerprintLength() const;
private:
SSHFPImpl* constructFromLexer(MasterLexer& lexer);
diff --git a/src/lib/dns/rrclass-placeholder.h b/src/lib/dns/rrclass-placeholder.h
index b4f1851..709057e 100644
--- a/src/lib/dns/rrclass-placeholder.h
+++ b/src/lib/dns/rrclass-placeholder.h
@@ -20,7 +20,7 @@
#include <string>
#include <ostream>
-#include <exceptions/exceptions.h>
+#include <dns/exceptions.h>
#include <boost/optional.hpp>
@@ -39,20 +39,20 @@ class AbstractMessageRenderer;
/// \brief A standard DNS module exception that is thrown if an RRClass object
/// is being constructed from an unrecognized string.
///
-class InvalidRRClass : public Exception {
+class InvalidRRClass : public DNSTextError {
public:
InvalidRRClass(const char* file, size_t line, const char* what) :
- isc::Exception(file, line, what) {}
+ DNSTextError(file, line, what) {}
};
///
/// \brief A standard DNS module exception that is thrown if an RRClass object
/// is being constructed from a incomplete (too short) wire-format data.
///
-class IncompleteRRClass : public Exception {
+class IncompleteRRClass : public isc::dns::Exception {
public:
IncompleteRRClass(const char* file, size_t line, const char* what) :
- isc::Exception(file, line, what) {}
+ isc::dns::Exception(file, line, what) {}
};
///
diff --git a/src/lib/dns/rrparamregistry.h b/src/lib/dns/rrparamregistry.h
index 1d59e01..3e30a36 100644
--- a/src/lib/dns/rrparamregistry.h
+++ b/src/lib/dns/rrparamregistry.h
@@ -21,7 +21,7 @@
#include <boost/shared_ptr.hpp>
-#include <exceptions/exceptions.h>
+#include <dns/exceptions.h>
#include <dns/rdata.h>
@@ -35,20 +35,20 @@ struct RRParamRegistryImpl;
/// \brief A standard DNS module exception that is thrown if a new RR type is
/// being registered with a different type string.
///
-class RRTypeExists : public Exception {
+class RRTypeExists : public isc::dns::Exception {
public:
RRTypeExists(const char* file, size_t line, const char* what) :
- isc::Exception(file, line, what) {}
+ isc::dns::Exception(file, line, what) {}
};
///
/// \brief A standard DNS module exception that is thrown if a new RR class is
/// being registered with a different type string.
///
-class RRClassExists : public Exception {
+class RRClassExists : public isc::dns::Exception {
public:
RRClassExists(const char* file, size_t line, const char* what) :
- isc::Exception(file, line, what) {}
+ isc::dns::Exception(file, line, what) {}
};
namespace rdata {
diff --git a/src/lib/dns/rrset.cc b/src/lib/dns/rrset.cc
index 8dfe884..b3da32c 100644
--- a/src/lib/dns/rrset.cc
+++ b/src/lib/dns/rrset.cc
@@ -254,6 +254,11 @@ BasicRRset::addRdata(const Rdata& rdata) {
AbstractRRset::addRdata(rdata);
}
+void
+BasicRRset::addRdata(const std::string& rdata_str) {
+ addRdata(createRdata(getType(), getClass(), rdata_str));
+}
+
unsigned int
BasicRRset::getRdataCount() const {
return (impl_->rdatalist_.size());
@@ -289,6 +294,50 @@ BasicRRset::toText() const {
return (AbstractRRset::toText());
}
+uint16_t
+BasicRRset::getLength() const {
+ uint16_t length = 0;
+ RdataIteratorPtr it = getRdataIterator();
+
+ if (it->isLast()) {
+ // empty rrsets are only allowed for classes ANY and NONE
+ if (getClass() != RRClass::ANY() &&
+ getClass() != RRClass::NONE()) {
+ isc_throw(EmptyRRset, "getLength() is attempted for an empty RRset");
+ }
+
+ // For an empty RRset, write the name, type, class and TTL once,
+ // followed by empty rdata.
+ length += getName().getLength();
+ length += 2; // TYPE field
+ length += 2; // CLASS field
+ length += 4; // TTL field
+ length += 2; // RDLENGTH field (=0 in wire format)
+
+ return (length);
+ }
+
+ do {
+ // This is a size_t as some of the following additions may
+ // overflow due to a programming mistake somewhere.
+ size_t rrlen = 0;
+
+ rrlen += getName().getLength();
+ rrlen += 2; // TYPE field
+ rrlen += 2; // CLASS field
+ rrlen += 4; // TTL field
+ rrlen += 2; // RDLENGTH field
+ rrlen += it->getCurrent().getLength();
+
+ assert(length + rrlen < 65536);
+ length += rrlen;
+
+ it->next();
+ } while (!it->isLast());
+
+ return (length);
+}
+
unsigned int
BasicRRset::toWire(OutputBuffer& buffer) const {
return (AbstractRRset::toWire(buffer));
@@ -322,6 +371,21 @@ RRset::getRRsigDataCount() const {
}
}
+uint16_t
+RRset::getLength() const {
+ uint16_t length = BasicRRset::getLength();
+
+ if (rrsig_) {
+ const uint16_t rrsigs_length = rrsig_->getLength();
+ // the uint16_ts are promoted to ints during addition below, so
+ // it won't overflow a 16-bit register.
+ assert(length + rrsigs_length < 65536);
+ length += rrsigs_length;
+ }
+
+ return (length);
+}
+
unsigned int
RRset::toWire(OutputBuffer& buffer) const {
unsigned int rrs_written = BasicRRset::toWire(buffer);
diff --git a/src/lib/dns/rrset.h b/src/lib/dns/rrset.h
index eb8fa6e..c474ac6 100644
--- a/src/lib/dns/rrset.h
+++ b/src/lib/dns/rrset.h
@@ -20,7 +20,7 @@
#include <boost/shared_ptr.hpp>
-#include <exceptions/exceptions.h>
+#include <dns/exceptions.h>
#include <dns/rdata.h>
#include <dns/rrtype.h>
@@ -36,10 +36,10 @@ namespace dns {
/// \brief A standard DNS module exception that is thrown if an RRset object
/// does not contain any RDATA where required.
///
-class EmptyRRset : public Exception {
+class EmptyRRset : public isc::dns::Exception {
public:
EmptyRRset(const char* file, size_t line, const char* what) :
- isc::Exception(file, line, what) {}
+ isc::dns::Exception(file, line, what) {}
};
// forward declarations
@@ -206,6 +206,20 @@ public:
/// \return The number of \c Rdata objects contained.
virtual unsigned int getRdataCount() const = 0;
+ /// \brief Get the wire format length of the \c AbstractRRset.
+ ///
+ /// This method returns the wire format length of the
+ /// \c AbstractRRset, which is calculated by summing the individual
+ /// lengths of the various fields that make up each RR.
+ ///
+ /// NOTE: When including name lengths, the allocation for
+ /// uncompressed name wire format representation is used.
+ ///
+ /// \return The length of the wire format representation of the
+ /// \c AbstractRRset.
+ /// \throw \c EmptyRRset if the \c AbstractRRset is empty.
+ virtual uint16_t getLength() const = 0;
+
/// \brief Returns the owner name of the \c RRset.
///
/// \return A reference to a \c Name class object corresponding to the
@@ -376,10 +390,27 @@ public:
/// Still, this version would offer a more intuitive interface and is
/// provided as such.
///
+ /// NOTE: Because a new Rdata object is constructed, this method can
+ /// throw a std::bad_cast exception if this RRset's class is NONE,
+ /// or if some other error occurs. If you want to be able to add
+ /// RDATA to an RRset whose class is NONE, please use the other
+ /// variant of \c addRdata() which accepts a \c ConstRdataPtr
+ /// argument.
+ ///
/// \param rdata A reference to a \c rdata::RdataPtr (derived) class
/// object, a copy of which is to be added to the \c RRset.
virtual void addRdata(const rdata::Rdata& rdata) = 0;
+ /// \brief Add an RDATA to the RRset (string version).
+ ///
+ /// This method constructs an Rdata object from the the given
+ /// \c rdata_str in presentation format and adds it to the \c RRset.
+ ///
+ /// \param rdata_str RDATA string in presentation format.
+ /// \throw InvalidRdataText if the \c rdata_str is invalid for this
+ /// \c RRset.
+ virtual void addRdata(const std::string& rdata_str) = 0;
+
/// \brief Return an iterator to go through all RDATA stored in the
/// \c RRset.
///
@@ -642,6 +673,13 @@ public:
/// \return The number of \c Rdata objects contained.
virtual unsigned int getRdataCount() const;
+ /// \brief Get the wire format length of the \c BasicRRset.
+ ///
+ /// \return The length of the wire format representation of the
+ /// \c BasicRRset.
+ /// \throw \c EmptyRRset if the \c BasicRRset is empty.
+ virtual uint16_t getLength() const;
+
/// \brief Returns the owner name of the \c RRset.
///
/// This method never throws an exception.
@@ -727,6 +765,13 @@ public:
/// See \c AbstractRRset::addRdata(const rdata::Rdata&).
virtual void addRdata(const rdata::Rdata& rdata);
+ /// \brief Add an RDATA to the RRset (string version).
+ ///
+ /// \param rdata_str RDATA string in presentation format.
+ /// \throw InvalidRdataText if the \c rdata_str is invalid for this
+ /// \c RRset.
+ virtual void addRdata(const std::string& rdata_str);
+
/// \brief Return an iterator to go through all RDATA stored in the
/// \c BasicRRset.
///
@@ -813,6 +858,13 @@ public:
virtual ~RRset();
+ /// \brief Get the wire format length of the \c RRset.
+ ///
+ /// \return The length of the wire format representation of the
+ /// \c RRset.
+ /// \throw \c EmptyRRset if the \c RRset is empty.
+ virtual uint16_t getLength() const;
+
/// \brief Render the RRset in the wire format with name compression and
/// truncation handling.
///
diff --git a/src/lib/dns/rrttl.cc b/src/lib/dns/rrttl.cc
index 73ac259..e6ce007 100644
--- a/src/lib/dns/rrttl.cc
+++ b/src/lib/dns/rrttl.cc
@@ -42,14 +42,15 @@ myIsalpha(char c) {
struct Unit {
char unit;
uint32_t multiply;
+ uint32_t max_allowed;
};
Unit units[] = {
- { 'S', 1 },
- { 'M', 60 },
- { 'H', 60 * 60 },
- { 'D', 24 * 60 * 60 },
- { 'W', 7 * 24 * 60 * 60 }
+ { 'S', 1, 0xffffffff / 1 },
+ { 'M', 60, 0xffffffff / 60 },
+ { 'H', 60 * 60, 0xffffffff / (60 * 60) },
+ { 'D', 24 * 60 * 60, 0xffffffff / (24 * 60 * 60) },
+ { 'W', 7 * 24 * 60 * 60, 0xffffffff / (7 * 24 * 60 * 60) }
};
}
@@ -66,11 +67,9 @@ parseTTLString(const string& ttlstr, uint32_t& ttlval, string* error_txt) {
}
return (false);
}
- // We use a larger data type during the computation. This is because
- // some compilers don't fail when out of range, so we check the range
- // ourselves later.
- int64_t val = 0;
+ // We use a larger data type to handle negative number cases.
+ uint64_t val = 0;
const string::const_iterator end = ttlstr.end();
string::const_iterator pos = ttlstr.begin();
@@ -92,7 +91,7 @@ parseTTLString(const string& ttlstr, uint32_t& ttlval, string* error_txt) {
} else {
// Case without any units at all. Just convert and store
// it.
- val = boost::lexical_cast<int64_t>(ttlstr);
+ val = boost::lexical_cast<uint64_t>(ttlstr);
break;
}
}
@@ -100,11 +99,13 @@ parseTTLString(const string& ttlstr, uint32_t& ttlval, string* error_txt) {
units_mode = true;
// Find the unit and get the size.
uint32_t multiply = 1; // initialize to silence compiler warnings
+ uint32_t max_allowed = 0xffffffff;
bool found = false;
for (size_t i = 0; i < sizeof(units) / sizeof(*units); ++i) {
if (toupper(*unit) == units[i].unit) {
found = true;
multiply = units[i].multiply;
+ max_allowed = units[i].max_allowed;
break;
}
}
@@ -122,15 +123,25 @@ parseTTLString(const string& ttlstr, uint32_t& ttlval, string* error_txt) {
}
return (false);
}
- const int64_t value = boost::lexical_cast<int64_t>(string(pos,
- unit));
+ const uint64_t value =
+ boost::lexical_cast<uint64_t>(string(pos, unit));
+ if (value > max_allowed) {
+ if (error_txt != NULL) {
+ *error_txt = "Part of TTL out of range: " + ttlstr;
+ }
+ return (false);
+ }
+
+ // seconds cannot be out of range at this point.
+ const uint64_t seconds = value * multiply;
+ assert(seconds <= 0xffffffff);
+
// Add what we found
- val += multiply * value;
+ val += seconds;
// Check the partial value is still in range (the value can only
// grow, so if we get out of range now, it won't get better, so
// there's no need to continue).
- if (value < 0 || value > 0xffffffff || val < 0 ||
- val > 0xffffffff) {
+ if (val < seconds || val > 0xffffffff) {
if (error_txt != NULL) {
*error_txt = "Part of TTL out of range: " + ttlstr;
}
@@ -146,9 +157,10 @@ parseTTLString(const string& ttlstr, uint32_t& ttlval, string* error_txt) {
return (false);
}
- if (val >= 0 && val <= 0xffffffff) {
+ if (val <= 0xffffffff) {
ttlval = val;
} else {
+ // This could be due to negative numbers in input, etc.
if (error_txt != NULL) {
*error_txt = "TTL out of range: " + ttlstr;
}
diff --git a/src/lib/dns/rrttl.h b/src/lib/dns/rrttl.h
index 35403b6..ffa3467 100644
--- a/src/lib/dns/rrttl.h
+++ b/src/lib/dns/rrttl.h
@@ -15,7 +15,7 @@
#ifndef RRTTL_H
#define RRTTL_H 1
-#include <exceptions/exceptions.h>
+#include <dns/exceptions.h>
#include <boost/optional.hpp>
@@ -36,20 +36,20 @@ class AbstractMessageRenderer;
/// \brief A standard DNS module exception that is thrown if an RRTTL object
/// is being constructed from an unrecognized string.
///
-class InvalidRRTTL : public Exception {
+class InvalidRRTTL : public DNSTextError {
public:
InvalidRRTTL(const char* file, size_t line, const char* what) :
- isc::Exception(file, line, what) {}
+ DNSTextError(file, line, what) {}
};
///
/// \brief A standard DNS module exception that is thrown if an RRTTL object
/// is being constructed from a incomplete (too short) wire-format data.
///
-class IncompleteRRTTL : public Exception {
+class IncompleteRRTTL : public isc::dns::Exception {
public:
IncompleteRRTTL(const char* file, size_t line, const char* what) :
- isc::Exception(file, line, what) {}
+ isc::dns::Exception(file, line, what) {}
};
///
diff --git a/src/lib/dns/rrtype-placeholder.h b/src/lib/dns/rrtype-placeholder.h
index 5541635..46167e4 100644
--- a/src/lib/dns/rrtype-placeholder.h
+++ b/src/lib/dns/rrtype-placeholder.h
@@ -20,7 +20,7 @@
#include <string>
#include <ostream>
-#include <exceptions/exceptions.h>
+#include <dns/exceptions.h>
// Solaris x86 defines DS in <sys/regset.h>, which gets pulled in by Boost
#if defined(__sun) && defined(DS)
@@ -42,20 +42,20 @@ class AbstractMessageRenderer;
/// \brief A standard DNS module exception that is thrown if an RRType object
/// is being constructed from an unrecognized string.
///
-class InvalidRRType : public Exception {
+class InvalidRRType : public DNSTextError {
public:
InvalidRRType(const char* file, size_t line, const char* what) :
- isc::Exception(file, line, what) {}
+ DNSTextError(file, line, what) {}
};
///
/// \brief A standard DNS module exception that is thrown if an RRType object
/// is being constructed from a incomplete (too short) wire-format data.
///
-class IncompleteRRType : public Exception {
+class IncompleteRRType : public isc::dns::Exception {
public:
IncompleteRRType(const char* file, size_t line, const char* what) :
- isc::Exception(file, line, what) {}
+ isc::dns::Exception(file, line, what) {}
};
///
diff --git a/src/lib/dns/tests/Makefile.am b/src/lib/dns/tests/Makefile.am
index 5029681..f982f88 100644
--- a/src/lib/dns/tests/Makefile.am
+++ b/src/lib/dns/tests/Makefile.am
@@ -21,6 +21,7 @@ TESTS =
if HAVE_GTEST
TESTS += run_unittests
run_unittests_SOURCES = unittest_util.h unittest_util.cc
+run_unittests_SOURCES += dns_exceptions_unittest.cc
run_unittests_SOURCES += edns_unittest.cc
run_unittests_SOURCES += master_lexer_inputsource_unittest.cc
run_unittests_SOURCES += labelsequence_unittest.cc
diff --git a/src/lib/dns/tests/dns_exceptions_unittest.cc b/src/lib/dns/tests/dns_exceptions_unittest.cc
new file mode 100644
index 0000000..905e2ca
--- /dev/null
+++ b/src/lib/dns/tests/dns_exceptions_unittest.cc
@@ -0,0 +1,69 @@
+// Copyright (C) 2014 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <dns/exceptions.h>
+
+#include <gtest/gtest.h>
+
+namespace { // begin unnamed namespace
+
+TEST(DNSExceptionsTest, checkExceptionsHierarchy) {
+ EXPECT_NO_THROW({
+ const isc::dns::Exception exception("", 0, "");
+ const isc::Exception& exception_cast =
+ dynamic_cast<const isc::Exception&>(exception);
+ // to avoid compiler warning
+ exception_cast.what();
+ });
+
+ EXPECT_NO_THROW({
+ const isc::dns::DNSTextError exception("", 0, "");
+ const isc::dns::Exception& exception_cast =
+ dynamic_cast<const isc::dns::Exception&>(exception);
+ // to avoid compiler warning
+ exception_cast.what();
+ });
+
+ EXPECT_NO_THROW({
+ const isc::dns::NameParserException exception("", 0, "");
+ const isc::dns::DNSTextError& exception_cast =
+ dynamic_cast<const isc::dns::DNSTextError&>(exception);
+ // to avoid compiler warning
+ exception_cast.what();
+ });
+
+ EXPECT_NO_THROW({
+ const isc::dns::DNSMessageFORMERR exception("", 0, "");
+ const isc::dns::DNSProtocolError& exception_cast =
+ dynamic_cast<const isc::dns::DNSProtocolError&>(exception);
+ const isc::dns::Exception& exception_cast2 =
+ dynamic_cast<const isc::dns::Exception&>(exception);
+ // to avoid compiler warning
+ exception_cast.what();
+ exception_cast2.what();
+ });
+
+ EXPECT_NO_THROW({
+ const isc::dns::DNSMessageBADVERS exception("", 0, "");
+ const isc::dns::DNSProtocolError& exception_cast =
+ dynamic_cast<const isc::dns::DNSProtocolError&>(exception);
+ const isc::dns::Exception& exception_cast2 =
+ dynamic_cast<const isc::dns::Exception&>(exception);
+ // to avoid compiler warning
+ exception_cast.what();
+ exception_cast2.what();
+ });
+}
+
+} // end unnamed namespace
diff --git a/src/lib/dns/tests/edns_unittest.cc b/src/lib/dns/tests/edns_unittest.cc
index de2d244..3fffc6f 100644
--- a/src/lib/dns/tests/edns_unittest.cc
+++ b/src/lib/dns/tests/edns_unittest.cc
@@ -31,12 +31,14 @@
#include <gtest/gtest.h>
#include <dns/tests/unittest_util.h>
+#include <util/unittests/wiredata.h>
-using isc::UnitTestUtil;
using namespace std;
using namespace isc::dns;
using namespace isc::util;
using namespace isc::dns::rdata;
+using isc::UnitTestUtil;
+using isc::util::unittests::matchWireData;
const uint8_t EDNS::SUPPORTED_VERSION;
@@ -159,8 +161,8 @@ TEST_F(EDNSTest, toWireRenderer) {
EXPECT_EQ(1, edns_base.toWire(renderer,
Rcode::NOERROR().getExtendedCode()));
UnitTestUtil::readWireData("edns_toWire1.wire", wiredata);
- EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, renderer.getData(),
- renderer.getLength(), &wiredata[0], wiredata.size());
+ matchWireData(&wiredata[0], wiredata.size(),
+ renderer.getData(), renderer.getLength());
// Typical case, enabling DNSSEC
renderer.clear();
@@ -169,8 +171,8 @@ TEST_F(EDNSTest, toWireRenderer) {
EXPECT_EQ(1, edns_base.toWire(renderer,
Rcode::NOERROR().getExtendedCode()));
UnitTestUtil::readWireData("edns_toWire2.wire", wiredata);
- EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, renderer.getData(),
- renderer.getLength(), &wiredata[0], wiredata.size());
+ matchWireData(&wiredata[0], wiredata.size(),
+ renderer.getData(), renderer.getLength());
// Non-0 extended Rcode
renderer.clear();
@@ -179,8 +181,8 @@ TEST_F(EDNSTest, toWireRenderer) {
EXPECT_EQ(1, edns_base.toWire(renderer,
Rcode::BADVERS().getExtendedCode()));
UnitTestUtil::readWireData("edns_toWire3.wire", wiredata);
- EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, renderer.getData(),
- renderer.getLength(), &wiredata[0], wiredata.size());
+ matchWireData(&wiredata[0], wiredata.size(),
+ renderer.getData(), renderer.getLength());
// Uncommon UDP buffer size
renderer.clear();
@@ -190,8 +192,8 @@ TEST_F(EDNSTest, toWireRenderer) {
EXPECT_EQ(1, edns_base.toWire(renderer,
Rcode::NOERROR().getExtendedCode()));
UnitTestUtil::readWireData("edns_toWire4.wire", wiredata);
- EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, renderer.getData(),
- renderer.getLength(), &wiredata[0], wiredata.size());
+ matchWireData(&wiredata[0], wiredata.size(),
+ renderer.getData(), renderer.getLength());
// From RR with unknown flag. If used for toWire(), the unknown flag
// should disappear.
@@ -201,8 +203,8 @@ TEST_F(EDNSTest, toWireRenderer) {
*opt_rdata).toWire(renderer,
Rcode::NOERROR().getExtendedCode()));
UnitTestUtil::readWireData("edns_toWire2.wire", wiredata);
- EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, renderer.getData(),
- renderer.getLength(), &wiredata[0], wiredata.size());
+ matchWireData(&wiredata[0], wiredata.size(),
+ renderer.getData(), renderer.getLength());
// If the available length in the renderer is not sufficient for the OPT
// RR, it shouldn't be inserted.
@@ -222,8 +224,8 @@ TEST_F(EDNSTest, toWireBuffer) {
EXPECT_EQ(1, edns_base.toWire(obuffer,
Rcode::NOERROR().getExtendedCode()));
UnitTestUtil::readWireData("edns_toWire1.wire", wiredata);
- EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, obuffer.getData(),
- obuffer.getLength(), &wiredata[0], wiredata.size());
+ matchWireData(&wiredata[0], wiredata.size(),
+ obuffer.getData(), obuffer.getLength());
}
TEST_F(EDNSTest, createFromRR) {
diff --git a/src/lib/dns/tests/labelsequence_unittest.cc b/src/lib/dns/tests/labelsequence_unittest.cc
index a3ac767..c211a4b 100644
--- a/src/lib/dns/tests/labelsequence_unittest.cc
+++ b/src/lib/dns/tests/labelsequence_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2012-2014 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 <config.h>
+
#include <util/buffer.h>
#include <dns/labelsequence.h>
@@ -853,6 +855,10 @@ TEST_F(LabelSequenceTest, serialize) {
isc::BadValue);
}
+#ifdef ENABLE_DEBUG
+
+// These checks are enabled only in debug mode in the LabelSequence
+// class.
TEST_F(LabelSequenceTest, badDeserialize) {
EXPECT_THROW(LabelSequence(NULL), isc::BadValue);
const uint8_t zero_offsets[] = { 0 };
@@ -879,6 +885,8 @@ TEST_F(LabelSequenceTest, badDeserialize) {
EXPECT_THROW(LabelSequence ls(offsets_noincrease), isc::BadValue);
}
+#endif
+
namespace {
// Helper function; repeatedly calls
diff --git a/src/lib/dns/tests/master_loader_unittest.cc b/src/lib/dns/tests/master_loader_unittest.cc
index ce9b8f7..1b6de74 100644
--- a/src/lib/dns/tests/master_loader_unittest.cc
+++ b/src/lib/dns/tests/master_loader_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2012-2014 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
@@ -123,7 +123,9 @@ public:
EXPECT_EQ(rrttl, current->getTTL());
ASSERT_EQ(1, current->getRdataCount());
EXPECT_EQ(0, isc::dns::rdata::createRdata(type, RRClass::IN(), data)->
- compare(current->getRdataIterator()->getCurrent()));
+ compare(current->getRdataIterator()->getCurrent()))
+ << data << " vs. "
+ << current->getRdataIterator()->getCurrent().toText();
}
void checkBasicRRs() {
@@ -307,6 +309,481 @@ TEST_F(MasterLoaderTest, origin) {
}
}
+TEST_F(MasterLoaderTest, generate) {
+ // Various forms of the directive
+ const char* generates[] = {
+ "$generate",
+ "$GENERATE",
+ "$Generate",
+ "$GeneratE",
+ "\"$GENERATE\"",
+ NULL
+ };
+ for (const char** generate = generates; *generate != NULL; ++generate) {
+ SCOPED_TRACE(*generate);
+
+ clear();
+ const string directive = *generate;
+ const string input =
+ "$ORIGIN example.org.\n"
+ "before.example.org. 3600 IN A 192.0.2.0\n" +
+ directive + " 3-5 host$ A 192.0.2.$\n" +
+ "after.example.org. 3600 IN A 192.0.2.255\n";
+ stringstream ss(input);
+ setLoader(ss, Name("example.org."), RRClass::IN(),
+ MasterLoader::MANY_ERRORS);
+
+ loader_->load();
+ EXPECT_TRUE(loader_->loadedSucessfully());
+ EXPECT_TRUE(errors_.empty());
+
+ // The "before" and "after" scaffolding below checks that no
+ // extra records are added by $GENERATE outside the requested
+ // range.
+ checkRR("before.example.org", RRType::A(), "192.0.2.0");
+ checkRR("host3.example.org", RRType::A(), "192.0.2.3");
+ checkRR("host4.example.org", RRType::A(), "192.0.2.4");
+ checkRR("host5.example.org", RRType::A(), "192.0.2.5");
+ checkRR("after.example.org", RRType::A(), "192.0.2.255");
+ }
+}
+
+TEST_F(MasterLoaderTest, generateRelativeLHS) {
+ const string input =
+ "$ORIGIN example.org.\n"
+ "$GENERATE 1-2 @ 3600 NS ns$.example.org.\n";
+ stringstream ss(input);
+ setLoader(ss, Name("example.org."), RRClass::IN(),
+ MasterLoader::MANY_ERRORS);
+
+ loader_->load();
+ EXPECT_TRUE(loader_->loadedSucessfully());
+ EXPECT_TRUE(errors_.empty());
+
+ checkRR("example.org", RRType::NS(), "ns1.example.org.");
+ checkRR("example.org", RRType::NS(), "ns2.example.org.");
+}
+
+TEST_F(MasterLoaderTest, generateInFront) {
+ // $ is in the front
+ const string input =
+ "$ORIGIN example.org.\n"
+ "$GENERATE 9-10 $host 3600 TXT \"$ pomegranate\"\n";
+ stringstream ss(input);
+ setLoader(ss, Name("example.org."), RRClass::IN(),
+ MasterLoader::MANY_ERRORS);
+
+ loader_->load();
+ EXPECT_TRUE(loader_->loadedSucessfully());
+ EXPECT_TRUE(errors_.empty());
+
+ checkRR("9host.example.org", RRType::TXT(), "9 pomegranate");
+ checkRR("10host.example.org", RRType::TXT(), "10 pomegranate");
+}
+
+TEST_F(MasterLoaderTest, generateInMiddle) {
+ // $ is in the middle
+ const string input =
+ "$ORIGIN example.org.\n"
+ "$GENERATE 9-10 num$-host 3600 TXT \"This is $ pomegranate\"\n";
+ stringstream ss(input);
+ setLoader(ss, Name("example.org."), RRClass::IN(),
+ MasterLoader::MANY_ERRORS);
+
+ loader_->load();
+ EXPECT_TRUE(loader_->loadedSucessfully());
+ EXPECT_TRUE(errors_.empty());
+
+ checkRR("num9-host.example.org", RRType::TXT(), "This is 9 pomegranate");
+ checkRR("num10-host.example.org", RRType::TXT(), "This is 10 pomegranate");
+}
+
+TEST_F(MasterLoaderTest, generateAtEnd) {
+ // $ is at the end
+ const string input =
+ "$ORIGIN example.org.\n"
+ "$GENERATE 9-10 num$-host 3600 TXT Pomegranate$\n";
+ stringstream ss(input);
+ setLoader(ss, Name("example.org."), RRClass::IN(),
+ MasterLoader::MANY_ERRORS);
+
+ loader_->load();
+ EXPECT_TRUE(loader_->loadedSucessfully());
+ EXPECT_TRUE(errors_.empty());
+
+ checkRR("num9-host.example.org", RRType::TXT(), "Pomegranate9");
+ checkRR("num10-host.example.org", RRType::TXT(), "Pomegranate10");
+}
+
+TEST_F(MasterLoaderTest, generateStripsQuotes) {
+ const string input =
+ "$ORIGIN example.org.\n"
+ "$GENERATE 1-2 @ 3600 MX \"$ mx$.example.org.\"\n";
+ stringstream ss(input);
+ setLoader(ss, Name("example.org."), RRClass::IN(),
+ MasterLoader::MANY_ERRORS);
+
+ loader_->load();
+ EXPECT_TRUE(loader_->loadedSucessfully());
+ EXPECT_TRUE(errors_.empty());
+
+ checkRR("example.org", RRType::MX(), "1 mx1.example.org.");
+ checkRR("example.org", RRType::MX(), "2 mx2.example.org.");
+}
+
+TEST_F(MasterLoaderTest, generateWithDoublePlaceholder) {
+ const string input =
+ "$ORIGIN example.org.\n"
+ "$GENERATE 9-10 host$ 3600 TXT \"This is $$ pomegranate\"\n";
+ stringstream ss(input);
+ setLoader(ss, Name("example.org."), RRClass::IN(),
+ MasterLoader::MANY_ERRORS);
+
+ loader_->load();
+ EXPECT_TRUE(loader_->loadedSucessfully());
+ EXPECT_TRUE(errors_.empty());
+
+ checkRR("host9.example.org", RRType::TXT(), "This is $ pomegranate");
+ checkRR("host10.example.org", RRType::TXT(), "This is $ pomegranate");
+}
+
+TEST_F(MasterLoaderTest, generateWithEscape) {
+ const string input =
+ "$ORIGIN example.org.\n"
+ "$GENERATE 9-10 host$ 3600 TXT \"This is \\$\\pomegranate\"\n";
+ stringstream ss(input);
+ setLoader(ss, Name("example.org."), RRClass::IN(),
+ MasterLoader::MANY_ERRORS);
+
+ loader_->load();
+ EXPECT_TRUE(loader_->loadedSucessfully());
+ EXPECT_TRUE(errors_.empty());
+
+ checkRR("host9.example.org", RRType::TXT(), "This is \\$\\pomegranate");
+ checkRR("host10.example.org", RRType::TXT(), "This is \\$\\pomegranate");
+}
+
+TEST_F(MasterLoaderTest, generateWithParams) {
+ const string input =
+ "$ORIGIN example.org.\n"
+ "$TTL 3600\n"
+ "$GENERATE 2-3 host$ A 192.0.2.$\n"
+ "$GENERATE 5-6 host$ 3600 A 192.0.2.$\n"
+ "$GENERATE 8-9 host$ IN A 192.0.2.$\n"
+ "$GENERATE 11-12 host$ IN 3600 A 192.0.2.$\n"
+ "$GENERATE 14-15 host$ 3600 IN A 192.0.2.$\n";
+ stringstream ss(input);
+ setLoader(ss, Name("example.org."), RRClass::IN(),
+ MasterLoader::MANY_ERRORS);
+
+ loader_->load();
+ EXPECT_TRUE(loader_->loadedSucessfully());
+ EXPECT_TRUE(errors_.empty());
+
+ checkRR("host2.example.org", RRType::A(), "192.0.2.2");
+ checkRR("host3.example.org", RRType::A(), "192.0.2.3");
+
+ checkRR("host5.example.org", RRType::A(), "192.0.2.5");
+ checkRR("host6.example.org", RRType::A(), "192.0.2.6");
+
+ checkRR("host8.example.org", RRType::A(), "192.0.2.8");
+ checkRR("host9.example.org", RRType::A(), "192.0.2.9");
+
+ checkRR("host11.example.org", RRType::A(), "192.0.2.11");
+ checkRR("host12.example.org", RRType::A(), "192.0.2.12");
+
+ checkRR("host14.example.org", RRType::A(), "192.0.2.14");
+ checkRR("host15.example.org", RRType::A(), "192.0.2.15");
+}
+
+TEST_F(MasterLoaderTest, generateWithStep) {
+ const string input =
+ "$ORIGIN example.org.\n"
+ "$GENERATE 2-9/2 host$ 3600 A 192.0.2.$\n"
+ "$GENERATE 12-21/3 host$ 3600 A 192.0.2.$\n"
+ "$GENERATE 30-31/1 host$ 3600 A 192.0.2.$\n";
+ stringstream ss(input);
+ setLoader(ss, Name("example.org."), RRClass::IN(),
+ MasterLoader::MANY_ERRORS);
+
+ loader_->load();
+ EXPECT_TRUE(loader_->loadedSucessfully());
+ EXPECT_TRUE(errors_.empty());
+
+ checkRR("host2.example.org", RRType::A(), "192.0.2.2");
+ checkRR("host4.example.org", RRType::A(), "192.0.2.4");
+ checkRR("host6.example.org", RRType::A(), "192.0.2.6");
+ checkRR("host8.example.org", RRType::A(), "192.0.2.8");
+
+ checkRR("host12.example.org", RRType::A(), "192.0.2.12");
+ checkRR("host15.example.org", RRType::A(), "192.0.2.15");
+ checkRR("host18.example.org", RRType::A(), "192.0.2.18");
+ checkRR("host21.example.org", RRType::A(), "192.0.2.21");
+
+ checkRR("host30.example.org", RRType::A(), "192.0.2.30");
+ checkRR("host31.example.org", RRType::A(), "192.0.2.31");
+}
+
+TEST_F(MasterLoaderTest, generateWithModifiers) {
+ const string input =
+ "$ORIGIN example.org.\n"
+ "$TTL 3600\n"
+
+ // Use a positive delta of 1 in the LHS and a negative delta of
+ // -1 in the RHS
+ "$GENERATE 2-9/2 host${1} A 192.0.2.${-1}\n"
+
+ "$GENERATE 10-12 host${0,4} A 192.0.2.$\n"
+ "$GENERATE 14-15 host${0,4,d} A 192.0.2.$\n"
+
+ // Names are case-insensitive, so we use TXT's RDATA to check
+ // case with hex representation.
+ "$GENERATE 30-31 host$ TXT \"Value ${0,4,x}\"\n"
+ "$GENERATE 42-43 host$ TXT \"Value ${0,4,X}\"\n"
+
+ // Octal does not use any alphabets
+ "$GENERATE 45-46 host${0,4,o} A 192.0.2.$\n"
+
+ // Here, the LHS has a trailing dot (which would result in an
+ // out-of-zone name), but that should be handled as a relative
+ // name.
+ "$GENERATE 90-92 ${0,8,n} A 192.0.2.$\n"
+
+ // Here, the LHS has no trailing dot, and results in the same
+ // number of labels as width=8 above.
+ "$GENERATE 94-96 ${0,7,n} A 192.0.2.$\n"
+
+ // Names are case-insensitive, so we use TXT's RDATA to check
+ // case with nibble representation.
+ "$GENERATE 106-107 host$ TXT \"Value ${0,9,n}\"\n"
+ "$GENERATE 109-110 host$ TXT \"Value ${0,9,N}\"\n"
+
+ // Junk type will not parse and 'd' is assumed. No error is
+ // generated (this is to match BIND 9 behavior).
+ "$GENERATE 200-201 host${0,4,j} A 192.0.2.$\n";
+ stringstream ss(input);
+ setLoader(ss, Name("example.org."), RRClass::IN(),
+ MasterLoader::MANY_ERRORS);
+
+ loader_->load();
+ EXPECT_TRUE(loader_->loadedSucessfully());
+ EXPECT_TRUE(errors_.empty());
+
+ checkRR("host3.example.org", RRType::A(), "192.0.2.1");
+ checkRR("host5.example.org", RRType::A(), "192.0.2.3");
+ checkRR("host7.example.org", RRType::A(), "192.0.2.5");
+ checkRR("host9.example.org", RRType::A(), "192.0.2.7");
+
+ checkRR("host0010.example.org", RRType::A(), "192.0.2.10");
+ checkRR("host0011.example.org", RRType::A(), "192.0.2.11");
+ checkRR("host0012.example.org", RRType::A(), "192.0.2.12");
+
+ checkRR("host0014.example.org", RRType::A(), "192.0.2.14");
+ checkRR("host0015.example.org", RRType::A(), "192.0.2.15");
+
+ checkRR("host30.example.org", RRType::TXT(), "Value 001e");
+ checkRR("host31.example.org", RRType::TXT(), "Value 001f");
+
+ checkRR("host42.example.org", RRType::TXT(), "Value 002A");
+ checkRR("host43.example.org", RRType::TXT(), "Value 002B");
+
+ checkRR("host0055.example.org", RRType::A(), "192.0.2.45");
+ checkRR("host0056.example.org", RRType::A(), "192.0.2.46");
+
+ checkRR("a.5.0.0.example.org", RRType::A(), "192.0.2.90");
+ checkRR("b.5.0.0.example.org", RRType::A(), "192.0.2.91");
+ checkRR("c.5.0.0.example.org", RRType::A(), "192.0.2.92");
+
+ checkRR("e.5.0.0.example.org", RRType::A(), "192.0.2.94");
+ checkRR("f.5.0.0.example.org", RRType::A(), "192.0.2.95");
+ checkRR("0.6.0.0.example.org", RRType::A(), "192.0.2.96");
+
+ checkRR("host106.example.org", RRType::TXT(), "Value a.6.0.0.0");
+ checkRR("host107.example.org", RRType::TXT(), "Value b.6.0.0.0");
+ checkRR("host109.example.org", RRType::TXT(), "Value D.6.0.0.0");
+ checkRR("host110.example.org", RRType::TXT(), "Value E.6.0.0.0");
+
+ checkRR("host0200.example.org", RRType::A(), "192.0.2.200");
+ checkRR("host0201.example.org", RRType::A(), "192.0.2.201");
+}
+
+TEST_F(MasterLoaderTest, generateWithNoModifiers) {
+ const string input =
+ "$ORIGIN example.org.\n"
+ "$TTL 3600\n"
+ "$GENERATE 10-12 host${} A 192.0.2.$\n";
+ stringstream ss(input);
+ setLoader(ss, Name("example.org."), RRClass::IN(),
+ MasterLoader::MANY_ERRORS);
+
+ loader_->load();
+ EXPECT_FALSE(loader_->loadedSucessfully());
+ ASSERT_EQ(2, errors_.size()); // For the broken GENERATE
+ EXPECT_TRUE(warnings_.empty());
+
+ checkCallbackMessage(errors_.at(0),
+ "Invalid $GENERATE format modifiers", 3);
+ checkCallbackMessage(errors_.at(1),
+ "$GENERATE error", 3);
+}
+
+TEST_F(MasterLoaderTest, generateWithBadModifiers) {
+ const string input =
+ "$ORIGIN example.org.\n"
+ "$TTL 3600\n"
+ "$GENERATE 10-12 host${GARBAGE} A 192.0.2.$\n";
+ stringstream ss(input);
+ setLoader(ss, Name("example.org."), RRClass::IN(),
+ MasterLoader::MANY_ERRORS);
+
+ loader_->load();
+ EXPECT_FALSE(loader_->loadedSucessfully());
+ ASSERT_EQ(2, errors_.size()); // For the broken GENERATE
+ EXPECT_TRUE(warnings_.empty());
+
+ checkCallbackMessage(errors_.at(0),
+ "Invalid $GENERATE format modifiers", 3);
+ checkCallbackMessage(errors_.at(1),
+ "$GENERATE error", 3);
+}
+
+TEST_F(MasterLoaderTest, generateMissingRange) {
+ const string input =
+ "$ORIGIN example.org.\n"
+ "$GENERATE\n";
+ stringstream ss(input);
+ setLoader(ss, Name("example.org."), RRClass::IN(),
+ MasterLoader::MANY_ERRORS);
+
+ loader_->load();
+ EXPECT_FALSE(loader_->loadedSucessfully());
+ EXPECT_EQ(1, errors_.size()); // For the broken GENERATE
+ EXPECT_TRUE(warnings_.empty());
+
+ checkCallbackMessage(errors_.at(0),
+ "unexpected end of input", 2);
+}
+
+TEST_F(MasterLoaderTest, generateMissingLHS) {
+ const string input =
+ "$ORIGIN example.org.\n"
+ "$GENERATE 2-4\n";
+ stringstream ss(input);
+ setLoader(ss, Name("example.org."), RRClass::IN(),
+ MasterLoader::MANY_ERRORS);
+
+ loader_->load();
+ EXPECT_FALSE(loader_->loadedSucessfully());
+ EXPECT_EQ(1, errors_.size()); // For the broken GENERATE
+ EXPECT_TRUE(warnings_.empty());
+
+ checkCallbackMessage(errors_.at(0),
+ "unexpected end of input", 2);
+}
+
+TEST_F(MasterLoaderTest, generateMissingType) {
+ const string input =
+ "$ORIGIN example.org.\n"
+ "$GENERATE 2-4 host$\n";
+ stringstream ss(input);
+ setLoader(ss, Name("example.org."), RRClass::IN(),
+ MasterLoader::MANY_ERRORS);
+
+ loader_->load();
+ EXPECT_FALSE(loader_->loadedSucessfully());
+ EXPECT_EQ(1, errors_.size()); // For the broken GENERATE
+ EXPECT_TRUE(warnings_.empty());
+
+ checkCallbackMessage(errors_.at(0),
+ "unexpected end of input", 2);
+}
+
+TEST_F(MasterLoaderTest, generateMissingRHS) {
+ const string input =
+ "$ORIGIN example.org.\n"
+ "$GENERATE 2-4 host$ A\n";
+ stringstream ss(input);
+ setLoader(ss, Name("example.org."), RRClass::IN(),
+ MasterLoader::MANY_ERRORS);
+
+ loader_->load();
+ EXPECT_FALSE(loader_->loadedSucessfully());
+ EXPECT_EQ(1, errors_.size()); // For the broken GENERATE
+ EXPECT_TRUE(warnings_.empty());
+
+ checkCallbackMessage(errors_.at(0),
+ "unexpected end of input", 2);
+}
+
+TEST_F(MasterLoaderTest, generateWithBadRangeSyntax) {
+ const string input =
+ "$ORIGIN example.org.\n"
+ "$GENERATE ABCD host$ 3600 A 192.0.2.$\n";
+ stringstream ss(input);
+ setLoader(ss, Name("example.org."), RRClass::IN(),
+ MasterLoader::MANY_ERRORS);
+
+ loader_->load();
+ EXPECT_FALSE(loader_->loadedSucessfully());
+ EXPECT_EQ(1, errors_.size()); // For the broken GENERATE
+ EXPECT_TRUE(warnings_.empty());
+
+ checkCallbackMessage(errors_.at(0),
+ "$GENERATE: invalid range: ABCD", 2);
+}
+
+TEST_F(MasterLoaderTest, generateWithInvalidRange) {
+ // start > stop
+ const string input =
+ "$ORIGIN example.org.\n"
+ "$GENERATE 2-1 host$ 3600 A 192.0.2.$\n";
+ stringstream ss(input);
+ setLoader(ss, Name("example.org."), RRClass::IN(),
+ MasterLoader::MANY_ERRORS);
+
+ loader_->load();
+ EXPECT_FALSE(loader_->loadedSucessfully());
+ EXPECT_EQ(1, errors_.size()); // For the broken GENERATE
+ EXPECT_TRUE(warnings_.empty());
+
+ checkCallbackMessage(errors_.at(0),
+ "$GENERATE: invalid range: 2-1", 2);
+}
+
+TEST_F(MasterLoaderTest, generateWithInvalidClass) {
+ const string input =
+ "$ORIGIN example.org.\n"
+ "$GENERATE 1-2 host$ 3600 CH A 192.0.2.$\n";
+ stringstream ss(input);
+ setLoader(ss, Name("example.org."), RRClass::IN(),
+ MasterLoader::MANY_ERRORS);
+
+ loader_->load();
+ EXPECT_FALSE(loader_->loadedSucessfully());
+ EXPECT_EQ(1, errors_.size()); // For the broken GENERATE
+ EXPECT_TRUE(warnings_.empty());
+
+ checkCallbackMessage(errors_.at(0),
+ "Class mismatch: CH vs. IN", 2);
+}
+
+TEST_F(MasterLoaderTest, generateWithNoAvailableTTL) {
+ const string input =
+ "$ORIGIN example.org.\n"
+ "$GENERATE 1-2 host$ A 192.0.2.$\n";
+ stringstream ss(input);
+ setLoader(ss, Name("example.org."), RRClass::IN(),
+ MasterLoader::MANY_ERRORS);
+
+ loader_->load();
+ EXPECT_FALSE(loader_->loadedSucessfully());
+ EXPECT_EQ(1, errors_.size()); // For the broken GENERATE
+ EXPECT_TRUE(warnings_.empty());
+
+ checkCallbackMessage(errors_.at(0),
+ "no TTL specified; load rejected", 2);
+}
+
// Test the source is correctly popped even after error
TEST_F(MasterLoaderTest, popAfterError) {
const string include_str = "$include " TEST_DATA_SRCDIR
@@ -952,4 +1429,19 @@ TEST_F(MasterLoaderTest, previousInInclude) {
checkARR("www.example.org");
}
+TEST_F(MasterLoaderTest, numericOwnerName) {
+ const string input("$ORIGIN example.org.\n"
+ "1 3600 IN A 192.0.2.1\n");
+ stringstream ss(input);
+ setLoader(ss, Name("example.org."), RRClass::IN(),
+ MasterLoader::MANY_ERRORS);
+
+ loader_->load();
+ EXPECT_TRUE(loader_->loadedSucessfully());
+ EXPECT_TRUE(errors_.empty());
+ EXPECT_TRUE(warnings_.empty());
+
+ checkRR("1.example.org", RRType::A(), "192.0.2.1");
+}
+
}
diff --git a/src/lib/dns/tests/message_unittest.cc b/src/lib/dns/tests/message_unittest.cc
index 8aaebaa..00b8a5b 100644
--- a/src/lib/dns/tests/message_unittest.cc
+++ b/src/lib/dns/tests/message_unittest.cc
@@ -41,13 +41,15 @@
#include <gtest/gtest.h>
#include <dns/tests/unittest_util.h>
+#include <util/unittests/wiredata.h>
-using isc::UnitTestUtil;
using namespace std;
using namespace isc;
using namespace isc::dns;
using namespace isc::util;
using namespace isc::dns::rdata;
+using isc::UnitTestUtil;
+using isc::util::unittests::matchWireData;
//
// Note: we need more tests, including:
@@ -217,10 +219,9 @@ TEST_F(MessageTest, fromWireWithTSIG) {
EXPECT_EQ(TSIGKey::HMACMD5_NAME(), tsig_rr->getRdata().getAlgorithm());
EXPECT_EQ(0x4da8877a, tsig_rr->getRdata().getTimeSigned());
EXPECT_EQ(TSIGContext::DEFAULT_FUDGE, tsig_rr->getRdata().getFudge());
- EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
- tsig_rr->getRdata().getMAC(),
- tsig_rr->getRdata().getMACSize(),
- expected_mac, sizeof(expected_mac));
+ matchWireData(expected_mac, sizeof(expected_mac),
+ tsig_rr->getRdata().getMAC(),
+ tsig_rr->getRdata().getMACSize());
EXPECT_EQ(0, tsig_rr->getRdata().getError());
EXPECT_EQ(0, tsig_rr->getRdata().getOtherLen());
EXPECT_EQ(static_cast<void*>(NULL), tsig_rr->getRdata().getOtherData());
@@ -572,12 +573,10 @@ TEST_F(MessageTest, parseHeader) {
message_parse.endSection(Message::SECTION_ADDITIONAL));
}
-TEST_F(MessageTest, fromWire) {
- // fromWire() isn't allowed in the render mode.
- EXPECT_THROW(factoryFromFile(message_render, "message_fromWire1"),
- InvalidMessageOperation);
-
- factoryFromFile(message_parse, "message_fromWire1");
+void
+checkMessageFromWire(const Message& message_parse,
+ const Name& test_name)
+{
EXPECT_EQ(0x1035, message_parse.getQid());
EXPECT_EQ(Opcode::QUERY(), message_parse.getOpcode());
EXPECT_EQ(Rcode::NOERROR(), message_parse.getRcode());
@@ -608,6 +607,37 @@ TEST_F(MessageTest, fromWire) {
EXPECT_TRUE(it->isLast());
}
+
+TEST_F(MessageTest, fromWire) {
+ // fromWire() isn't allowed in the render mode.
+ EXPECT_THROW(factoryFromFile(message_render, "message_fromWire1"),
+ InvalidMessageOperation);
+
+ factoryFromFile(message_parse, "message_fromWire1");
+ checkMessageFromWire(message_parse, test_name);
+}
+
+TEST_F(MessageTest, fromWireMultiple) {
+ // Parse from wire multiple times.
+ factoryFromFile(message_parse, "message_fromWire1");
+ factoryFromFile(message_parse, "message_fromWire1");
+ factoryFromFile(message_parse, "message_fromWire1");
+ factoryFromFile(message_parse, "message_fromWire1");
+ checkMessageFromWire(message_parse, test_name);
+
+ // Calling parseHeader() directly before fromWire() should not cause
+ // any problems.
+ received_data.clear();
+ UnitTestUtil::readWireData("message_fromWire1", received_data);
+
+ InputBuffer buffer(&received_data[0], received_data.size());
+ message_parse.parseHeader(buffer);
+ message_parse.fromWire(buffer);
+ message_parse.parseHeader(buffer);
+ message_parse.fromWire(buffer);
+ checkMessageFromWire(message_parse, test_name);
+}
+
TEST_F(MessageTest, fromWireShortBuffer) {
// We trim a valid message (ending with an SOA RR) for one byte.
// fromWire() should throw an exception while parsing the trimmed RR.
@@ -729,8 +759,8 @@ TEST_F(MessageTest, toWire) {
message_render.toWire(renderer);
vector<unsigned char> data;
UnitTestUtil::readWireData("message_toWire1", data);
- EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, renderer.getData(),
- renderer.getLength(), &data[0], data.size());
+ matchWireData(&data[0], data.size(),
+ renderer.getData(), renderer.getLength());
}
TEST_F(MessageTest, toWireSigned) {
@@ -766,8 +796,8 @@ TEST_F(MessageTest, toWireSigned) {
message_render.toWire(renderer);
vector<unsigned char> data;
UnitTestUtil::readWireData("message_toWire6", data);
- EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, renderer.getData(),
- renderer.getLength(), &data[0], data.size());
+ matchWireData(&data[0], data.size(),
+ renderer.getData(), renderer.getLength());
}
TEST_F(MessageTest, toWireSignedAndTruncated) {
@@ -810,8 +840,8 @@ TEST_F(MessageTest, toWireSignedAndTruncated) {
message_render.toWire(renderer);
vector<unsigned char> data;
UnitTestUtil::readWireData("message_toWire7", data);
- EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, renderer.getData(),
- renderer.getLength(), &data[0], data.size());
+ matchWireData(&data[0], data.size(),
+ renderer.getData(), renderer.getLength());
}
TEST_F(MessageTest, toWireInParseMode) {
@@ -864,12 +894,11 @@ commonTSIGToWireCheck(Message& message, MessageRenderer& renderer,
message.addRRset(Message::SECTION_ANSWER, ans_rrset);
}
- message.toWire(renderer, tsig_ctx);
+ message.toWire(renderer, &tsig_ctx);
vector<unsigned char> expected_data;
UnitTestUtil::readWireData(expected_file, expected_data);
- EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, renderer.getData(),
- renderer.getLength(),
- &expected_data[0], expected_data.size());
+ matchWireData(&expected_data[0], expected_data.size(),
+ renderer.getData(), renderer.getLength());
}
TEST_F(MessageTest, toWireWithTSIG) {
@@ -997,7 +1026,7 @@ TEST_F(MessageTest, toWireTSIGTruncation3) {
message_render.addQuestion(Question(Name("www.example.com"),
RRClass::IN(), RRType(i)));
}
- message_render.toWire(renderer, tsig_ctx);
+ message_render.toWire(renderer, &tsig_ctx);
// Check the rendered data by parsing it. We only check it has the
// TC bit on, has the correct number of questions, and has a TSIG RR.
diff --git a/src/lib/dns/tests/messagerenderer_unittest.cc b/src/lib/dns/tests/messagerenderer_unittest.cc
index 582c164..2b4da59 100644
--- a/src/lib/dns/tests/messagerenderer_unittest.cc
+++ b/src/lib/dns/tests/messagerenderer_unittest.cc
@@ -19,6 +19,7 @@
#include <dns/messagerenderer.h>
#include <dns/tests/unittest_util.h>
+#include <util/unittests/wiredata.h>
#include <gtest/gtest.h>
@@ -33,6 +34,7 @@ using isc::dns::LabelSequence;
using isc::dns::MessageRenderer;
using isc::util::OutputBuffer;
using boost::lexical_cast;
+using isc::util::unittests::matchWireData;
namespace {
class MessageRendererTest : public ::testing::Test {
@@ -56,8 +58,8 @@ TEST_F(MessageRendererTest, writeIntger) {
renderer.writeUint16(data16);
expected_size += sizeof(data16);
- EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, renderer.getData(),
- renderer.getLength(), &testdata[1], sizeof(data16));
+ matchWireData(&testdata[1], sizeof(data16),
+ renderer.getData(), renderer.getLength());
}
TEST_F(MessageRendererTest, writeName) {
@@ -65,8 +67,8 @@ TEST_F(MessageRendererTest, writeName) {
renderer.writeName(Name("a.example.com."));
renderer.writeName(Name("b.example.com."));
renderer.writeName(Name("a.example.org."));
- EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, renderer.getData(),
- renderer.getLength(), &data[0], data.size());
+ matchWireData(&data[0], data.size(),
+ renderer.getData(), renderer.getLength());
}
TEST_F(MessageRendererTest, writeNameInLargeBuffer) {
@@ -77,11 +79,9 @@ TEST_F(MessageRendererTest, writeNameInLargeBuffer) {
renderer.writeName(Name("a.example.com."));
renderer.writeName(Name("a.example.com."));
renderer.writeName(Name("b.example.com."));
- EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
- static_cast<const uint8_t*>(renderer.getData()) +
- offset,
- renderer.getLength() - offset,
- &data[0], data.size());
+ matchWireData(&data[0], data.size(),
+ static_cast<const uint8_t*>(renderer.getData()) + offset,
+ renderer.getLength() - offset);
}
TEST_F(MessageRendererTest, writeNameWithUncompressed) {
@@ -89,8 +89,8 @@ TEST_F(MessageRendererTest, writeNameWithUncompressed) {
renderer.writeName(Name("a.example.com."));
renderer.writeName(Name("b.example.com."), false);
renderer.writeName(Name("b.example.com."));
- EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, renderer.getData(),
- renderer.getLength(), &data[0], data.size());
+ matchWireData(&data[0], data.size(),
+ renderer.getData(), renderer.getLength());
}
TEST_F(MessageRendererTest, writeNamePointerChain) {
@@ -98,8 +98,8 @@ TEST_F(MessageRendererTest, writeNamePointerChain) {
renderer.writeName(Name("a.example.com."));
renderer.writeName(Name("b.example.com."));
renderer.writeName(Name("b.example.com."));
- EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, renderer.getData(),
- renderer.getLength(), &data[0], data.size());
+ matchWireData(&data[0], data.size(),
+ renderer.getData(), renderer.getLength());
}
TEST_F(MessageRendererTest, compressMode) {
@@ -126,8 +126,8 @@ TEST_F(MessageRendererTest, writeNameCaseCompress) {
// this should match the first name in terms of compression:
renderer.writeName(Name("b.exAmple.CoM."));
renderer.writeName(Name("a.example.org."));
- EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, renderer.getData(),
- renderer.getLength(), &data[0], data.size());
+ matchWireData(&data[0], data.size(),
+ renderer.getData(), renderer.getLength());
}
TEST_F(MessageRendererTest, writeNameCaseSensitiveCompress) {
@@ -138,8 +138,8 @@ TEST_F(MessageRendererTest, writeNameCaseSensitiveCompress) {
renderer.writeName(Name("a.example.com."));
renderer.writeName(Name("b.eXample.com."));
renderer.writeName(Name("c.eXample.com."));
- EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, renderer.getData(),
- renderer.getLength(), &data[0], data.size());
+ matchWireData(&data[0], data.size(),
+ renderer.getData(), renderer.getLength());
}
TEST_F(MessageRendererTest, writeNameMixedCaseCompress) {
@@ -171,11 +171,10 @@ TEST_F(MessageRendererTest, writeRootName) {
renderer.writeName(Name("."));
renderer.writeName(example_name);
- EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
- static_cast<const uint8_t*>(renderer.getData()),
- renderer.getLength(),
- static_cast<const uint8_t*>(expected.getData()),
- expected.getLength());
+ matchWireData(static_cast<const uint8_t*>(expected.getData()),
+ expected.getLength(),
+ static_cast<const uint8_t*>(renderer.getData()),
+ renderer.getLength());
}
TEST_F(MessageRendererTest, writeNameLabelSequence1) {
@@ -192,8 +191,8 @@ TEST_F(MessageRendererTest, writeNameLabelSequence1) {
// example.com.
renderer.writeName(ls1);
- EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, renderer.getData(),
- renderer.getLength(), &data[0], data.size());
+ matchWireData(&data[0], data.size(),
+ renderer.getData(), renderer.getLength());
}
TEST_F(MessageRendererTest, writeNameLabelSequence2) {
@@ -207,8 +206,8 @@ TEST_F(MessageRendererTest, writeNameLabelSequence2) {
// a.example.com (without root .)
renderer.writeName(ls1);
- EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, renderer.getData(),
- renderer.getLength(), &data[0], data.size());
+ matchWireData(&data[0], data.size(),
+ renderer.getData(), renderer.getLength());
}
TEST_F(MessageRendererTest, writeNameLabelSequence3) {
@@ -235,8 +234,8 @@ TEST_F(MessageRendererTest, writeNameLabelSequence3) {
// example
renderer.writeName(ls1);
- EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, renderer.getData(),
- renderer.getLength(), &data[0], data.size());
+ matchWireData(&data[0], data.size(),
+ renderer.getData(), renderer.getLength());
}
TEST_F(MessageRendererTest, setBuffer) {
diff --git a/src/lib/dns/tests/name_unittest.cc b/src/lib/dns/tests/name_unittest.cc
index 10d1e55..bb8320e 100644
--- a/src/lib/dns/tests/name_unittest.cc
+++ b/src/lib/dns/tests/name_unittest.cc
@@ -25,6 +25,7 @@
#include <dns/messagerenderer.h>
#include <dns/tests/unittest_util.h>
+#include <util/unittests/wiredata.h>
#include <gtest/gtest.h>
@@ -32,6 +33,7 @@ using namespace std;
using namespace isc;
using namespace isc::dns;
using namespace isc::util;
+using isc::util::unittests::matchWireData;
//
// XXX: these are defined as class static constants, but some compilers
@@ -137,9 +139,8 @@ NameTest::compareInWireFormat(const Name& name_actual,
name_actual.toWire(buffer_actual);
name_expected.toWire(buffer_expected);
- EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
- buffer_actual.getData(), buffer_actual.getLength(),
- buffer_expected.getData(), buffer_expected.getLength());
+ matchWireData(buffer_expected.getData(), buffer_expected.getLength(),
+ buffer_actual.getData(), buffer_actual.getLength());
}
TEST_F(NameTest, nonlocalObject) {
@@ -164,6 +165,64 @@ checkBadTextName(const string& txt) {
NameParserException);
}
+TEST_F(NameTest, checkExceptionsHierarchy) {
+ EXPECT_NO_THROW({
+ const isc::dns::EmptyLabel exception("", 0, "");
+ const isc::dns::NameParserException& exception_cast =
+ dynamic_cast<const isc::dns::NameParserException&>(exception);
+ // to avoid compiler warning
+ exception_cast.what();
+ });
+
+ EXPECT_NO_THROW({
+ const isc::dns::TooLongName exception("", 0, "");
+ const isc::dns::NameParserException& exception_cast =
+ dynamic_cast<const isc::dns::NameParserException&>(exception);
+ // to avoid compiler warning
+ exception_cast.what();
+ });
+
+ EXPECT_NO_THROW({
+ const isc::dns::TooLongLabel exception("", 0, "");
+ const isc::dns::NameParserException& exception_cast =
+ dynamic_cast<const isc::dns::NameParserException&>(exception);
+ // to avoid compiler warning
+ exception_cast.what();
+ });
+
+ EXPECT_NO_THROW({
+ const isc::dns::BadLabelType exception("", 0, "");
+ const isc::dns::NameParserException& exception_cast =
+ dynamic_cast<const isc::dns::NameParserException&>(exception);
+ // to avoid compiler warning
+ exception_cast.what();
+ });
+
+ EXPECT_NO_THROW({
+ const isc::dns::BadEscape exception("", 0, "");
+ const isc::dns::NameParserException& exception_cast =
+ dynamic_cast<const isc::dns::NameParserException&>(exception);
+ // to avoid compiler warning
+ exception_cast.what();
+ });
+
+ EXPECT_NO_THROW({
+ const isc::dns::IncompleteName exception("", 0, "");
+ const isc::dns::NameParserException& exception_cast =
+ dynamic_cast<const isc::dns::NameParserException&>(exception);
+ // to avoid compiler warning
+ exception_cast.what();
+ });
+
+ EXPECT_NO_THROW({
+ const isc::dns::MissingNameOrigin exception("", 0, "");
+ const isc::dns::NameParserException& exception_cast =
+ dynamic_cast<const isc::dns::NameParserException&>(exception);
+ // to avoid compiler warning
+ exception_cast.what();
+ });
+}
+
TEST_F(NameTest, fromText) {
vector<string> strnames;
strnames.push_back("www.example.com");
@@ -454,8 +513,8 @@ TEST_F(NameTest, toWireBuffer) {
UnitTestUtil::readWireData(string("01610376697803636f6d00"), data);
Name("a.vix.com.").toWire(buffer);
- EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, &data[0], data.size(),
- buffer.getData(), buffer.getLength());
+ matchWireData(&data[0], data.size(),
+ buffer.getData(), buffer.getLength());
}
//
@@ -468,8 +527,8 @@ TEST_F(NameTest, toWireRenderer) {
UnitTestUtil::readWireData(string("01610376697803636f6d00"), data);
Name("a.vix.com.").toWire(renderer);
- EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, &data[0], data.size(),
- renderer.getData(), renderer.getLength());
+ matchWireData(&data[0], data.size(),
+ renderer.getData(), renderer.getLength());
}
//
@@ -628,9 +687,8 @@ TEST_F(NameTest, at) {
}
example_name.toWire(buffer_expected);
- EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
- &data[0], data.size(), buffer_expected.getData(),
- buffer_expected.getLength());
+ matchWireData(&data[0], data.size(),
+ buffer_expected.getData(), buffer_expected.getLength());
// Out-of-range access: should trigger an exception.
EXPECT_THROW(example_name.at(example_name.getLength()), OutOfRange);
diff --git a/src/lib/dns/tests/nsec3hash_unittest.cc b/src/lib/dns/tests/nsec3hash_unittest.cc
index 4ef0c7b..44f8096 100644
--- a/src/lib/dns/tests/nsec3hash_unittest.cc
+++ b/src/lib/dns/tests/nsec3hash_unittest.cc
@@ -19,6 +19,7 @@
#include <boost/scoped_ptr.hpp>
#include <dns/nsec3hash.h>
+#include <dns/labelsequence.h>
#include <dns/rdataclass.h>
#include <util/encode/hex.h>
@@ -92,6 +93,18 @@ calculateCheck(NSEC3Hash& hash) {
// Check case-insensitiveness
EXPECT_EQ("0P9MHAVEQVM6T7VBL5LOP2U3T2RP3TOM",
hash.calculate(Name("EXAMPLE")));
+
+ // Repeat for the LabelSequence variant.
+
+ // A couple of normal cases from the RFC5155 example.
+ EXPECT_EQ("0P9MHAVEQVM6T7VBL5LOP2U3T2RP3TOM",
+ hash.calculate(LabelSequence(Name("example"))));
+ EXPECT_EQ("35MTHGPGCU1QG68FAB165KLNSNK3DPVL",
+ hash.calculate(LabelSequence(Name("a.example"))));
+
+ // Check case-insensitiveness
+ EXPECT_EQ("0P9MHAVEQVM6T7VBL5LOP2U3T2RP3TOM",
+ hash.calculate(LabelSequence(Name("EXAMPLE"))));
}
TEST_F(NSEC3HashTest, calculate) {
@@ -113,13 +126,16 @@ TEST_F(NSEC3HashTest, calculate) {
EXPECT_EQ("CK0POJMG874LJREF7EFN8430QVIT8BSM",
NSEC3HashPtr(NSEC3Hash::create(generic::NSEC3PARAM("1 0 0 -")))
->calculate(Name("com")));
+ EXPECT_EQ("CK0POJMG874LJREF7EFN8430QVIT8BSM",
+ NSEC3HashPtr(NSEC3Hash::create(generic::NSEC3PARAM("1 0 0 -")))
+ ->calculate(LabelSequence(Name("com"))));
// Using unusually large iterations, something larger than the 8-bit range.
// (expected hash value generated by BIND 9's dnssec-signzone)
EXPECT_EQ("COG6A52MJ96MNMV3QUCAGGCO0RHCC2Q3",
NSEC3HashPtr(NSEC3Hash::create(
generic::NSEC3PARAM("1 0 256 AABBCCDD")))
- ->calculate(Name("example.org")));
+ ->calculate(LabelSequence(Name("example.org"))));
}
// Common checks for match cases
@@ -169,6 +185,9 @@ class TestNSEC3Hash : public NSEC3Hash {
virtual string calculate(const Name&) const {
return ("00000000000000000000000000000000");
}
+ virtual string calculate(const LabelSequence&) const {
+ return ("00000000000000000000000000000000");
+ }
virtual bool match(const generic::NSEC3PARAM&) const {
return (true);
}
@@ -207,6 +226,8 @@ TEST_F(NSEC3HashTest, setCreator) {
// Re-check an existing case using the default creator/hash implementation
EXPECT_EQ("0P9MHAVEQVM6T7VBL5LOP2U3T2RP3TOM",
test_hash->calculate(Name("example")));
+ EXPECT_EQ("0P9MHAVEQVM6T7VBL5LOP2U3T2RP3TOM",
+ test_hash->calculate(LabelSequence(Name("example"))));
// Replace the creator, and confirm the hash values are faked
TestNSEC3HashCreator test_creator;
@@ -215,12 +236,16 @@ TEST_F(NSEC3HashTest, setCreator) {
test_hash.reset(NSEC3Hash::create(generic::NSEC3PARAM("1 0 12 aabbccdd")));
EXPECT_EQ("00000000000000000000000000000000",
test_hash->calculate(Name("example")));
+ EXPECT_EQ("00000000000000000000000000000000",
+ test_hash->calculate(LabelSequence(Name("example"))));
// Same for hash from NSEC3 RDATA
test_hash.reset(NSEC3Hash::create(generic::NSEC3
("1 0 12 aabbccdd " +
string(nsec3_common))));
EXPECT_EQ("00000000000000000000000000000000",
test_hash->calculate(Name("example")));
+ EXPECT_EQ("00000000000000000000000000000000",
+ test_hash->calculate(LabelSequence(Name("example"))));
// If we set a special flag big (0x80) on creation, it will act like the
// default creator.
@@ -228,17 +253,23 @@ TEST_F(NSEC3HashTest, setCreator) {
"1 128 12 aabbccdd")));
EXPECT_EQ("0P9MHAVEQVM6T7VBL5LOP2U3T2RP3TOM",
test_hash->calculate(Name("example")));
+ EXPECT_EQ("0P9MHAVEQVM6T7VBL5LOP2U3T2RP3TOM",
+ test_hash->calculate(LabelSequence(Name("example"))));
test_hash.reset(NSEC3Hash::create(generic::NSEC3
("1 128 12 aabbccdd " +
string(nsec3_common))));
EXPECT_EQ("0P9MHAVEQVM6T7VBL5LOP2U3T2RP3TOM",
test_hash->calculate(Name("example")));
+ EXPECT_EQ("0P9MHAVEQVM6T7VBL5LOP2U3T2RP3TOM",
+ test_hash->calculate(LabelSequence(Name("example"))));
// Reset the creator to default, and confirm that
setNSEC3HashCreator(NULL);
test_hash.reset(NSEC3Hash::create(generic::NSEC3PARAM("1 0 12 aabbccdd")));
EXPECT_EQ("0P9MHAVEQVM6T7VBL5LOP2U3T2RP3TOM",
test_hash->calculate(Name("example")));
+ EXPECT_EQ("0P9MHAVEQVM6T7VBL5LOP2U3T2RP3TOM",
+ test_hash->calculate(LabelSequence(Name("example"))));
}
} // end namespace
diff --git a/src/lib/dns/tests/question_unittest.cc b/src/lib/dns/tests/question_unittest.cc
index d1214a1..8e1667f 100644
--- a/src/lib/dns/tests/question_unittest.cc
+++ b/src/lib/dns/tests/question_unittest.cc
@@ -28,11 +28,13 @@
#include <gtest/gtest.h>
#include <dns/tests/unittest_util.h>
+#include <util/unittests/wiredata.h>
-using isc::UnitTestUtil;
using namespace std;
using namespace isc::dns;
using namespace isc::util;
+using isc::UnitTestUtil;
+using isc::util::unittests::matchWireData;
namespace {
class QuestionTest : public ::testing::Test {
@@ -100,16 +102,16 @@ TEST_F(QuestionTest, toWireBuffer) {
test_question1.toWire(obuffer);
test_question2.toWire(obuffer);
UnitTestUtil::readWireData("question_toWire1", wiredata);
- EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, obuffer.getData(),
- obuffer.getLength(), &wiredata[0], wiredata.size());
+ matchWireData(&wiredata[0], wiredata.size(),
+ obuffer.getData(), obuffer.getLength());
}
TEST_F(QuestionTest, toWireRenderer) {
test_question1.toWire(renderer);
test_question2.toWire(renderer);
UnitTestUtil::readWireData("question_toWire2", wiredata);
- EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, renderer.getData(),
- renderer.getLength(), &wiredata[0], wiredata.size());
+ matchWireData(&wiredata[0], wiredata.size(),
+ renderer.getData(), renderer.getLength());
}
TEST_F(QuestionTest, toWireTruncated) {
diff --git a/src/lib/dns/tests/rdata_afsdb_unittest.cc b/src/lib/dns/tests/rdata_afsdb_unittest.cc
index 9a628cd..6e933f5 100644
--- a/src/lib/dns/tests/rdata_afsdb_unittest.cc
+++ b/src/lib/dns/tests/rdata_afsdb_unittest.cc
@@ -24,12 +24,14 @@
#include <dns/tests/unittest_util.h>
#include <dns/tests/rdata_unittest.h>
+#include <util/unittests/wiredata.h>
-using isc::UnitTestUtil;
using namespace std;
using namespace isc::dns;
using namespace isc::util;
using namespace isc::dns::rdata;
+using isc::UnitTestUtil;
+using isc::util::unittests::matchWireData;
const char* const afsdb_text = "1 afsdb.example.com.";
const char* const afsdb_text2 = "0 root.example.com.";
@@ -155,9 +157,8 @@ TEST_F(Rdata_AFSDB_Test, toWireBuffer) {
UnitTestUtil::readWireData("rdata_afsdb_toWire1.wire", expected_wire);
// then compare them
- EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
- obuffer.getData(), obuffer.getLength(),
- &expected_wire[0], expected_wire.size());
+ matchWireData(&expected_wire[0], expected_wire.size(),
+ obuffer.getData(), obuffer.getLength());
// clear buffer for the next test
obuffer.clear();
@@ -170,9 +171,8 @@ TEST_F(Rdata_AFSDB_Test, toWireBuffer) {
UnitTestUtil::readWireData("rdata_afsdb_toWire2.wire", expected_wire);
// then compare them
- EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
- obuffer.getData(), obuffer.getLength(),
- &expected_wire[0], expected_wire.size());
+ matchWireData(&expected_wire[0], expected_wire.size(),
+ obuffer.getData(), obuffer.getLength());
}
TEST_F(Rdata_AFSDB_Test, toWireRenderer) {
@@ -187,9 +187,8 @@ TEST_F(Rdata_AFSDB_Test, toWireRenderer) {
UnitTestUtil::readWireData("rdata_afsdb_toWire1.wire", expected_wire);
// then compare them
- EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
- renderer.getData(), renderer.getLength(),
- &expected_wire[0], expected_wire.size());
+ matchWireData(&expected_wire[0], expected_wire.size(),
+ renderer.getData(), renderer.getLength());
// clear renderer for the next test
renderer.clear();
@@ -202,9 +201,8 @@ TEST_F(Rdata_AFSDB_Test, toWireRenderer) {
UnitTestUtil::readWireData("rdata_afsdb_toWire2.wire", expected_wire);
// then compare them
- EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
- renderer.getData(), renderer.getLength(),
- &expected_wire[0], expected_wire.size());
+ matchWireData(&expected_wire[0], expected_wire.size(),
+ renderer.getData(), renderer.getLength());
}
TEST_F(Rdata_AFSDB_Test, toText) {
diff --git a/src/lib/dns/tests/rdata_cname_unittest.cc b/src/lib/dns/tests/rdata_cname_unittest.cc
index 5f602f0..6e706e0 100644
--- a/src/lib/dns/tests/rdata_cname_unittest.cc
+++ b/src/lib/dns/tests/rdata_cname_unittest.cc
@@ -24,12 +24,14 @@
#include <dns/tests/unittest_util.h>
#include <dns/tests/rdata_unittest.h>
+#include <util/unittests/wiredata.h>
-using isc::UnitTestUtil;
using namespace std;
using namespace isc::dns;
using namespace isc::util;
using namespace isc::dns::rdata;
+using isc::UnitTestUtil;
+using isc::util::unittests::matchWireData;
namespace {
class Rdata_CNAME_Test : public RdataTest {
@@ -115,20 +117,18 @@ TEST_F(Rdata_CNAME_Test, createFromLexer) {
TEST_F(Rdata_CNAME_Test, toWireBuffer) {
rdata_cname.toWire(obuffer);
- EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
- obuffer.getData(), obuffer.getLength(),
- wiredata_cname, sizeof(wiredata_cname));
+ matchWireData(wiredata_cname, sizeof(wiredata_cname),
+ obuffer.getData(), obuffer.getLength());
}
TEST_F(Rdata_CNAME_Test, toWireRenderer) {
rdata_cname.toWire(renderer);
- EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
- renderer.getData(), renderer.getLength(),
- wiredata_cname, sizeof(wiredata_cname));
+ matchWireData(wiredata_cname, sizeof(wiredata_cname),
+ renderer.getData(), renderer.getLength());
+
rdata_cname2.toWire(renderer);
- EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
- renderer.getData(), renderer.getLength(),
- wiredata_cname2, sizeof(wiredata_cname2));
+ matchWireData(wiredata_cname2, sizeof(wiredata_cname2),
+ renderer.getData(), renderer.getLength());
}
TEST_F(Rdata_CNAME_Test, toText) {
diff --git a/src/lib/dns/tests/rdata_dhcid_unittest.cc b/src/lib/dns/tests/rdata_dhcid_unittest.cc
index 77baccd..5bc1d1e 100644
--- a/src/lib/dns/tests/rdata_dhcid_unittest.cc
+++ b/src/lib/dns/tests/rdata_dhcid_unittest.cc
@@ -22,14 +22,16 @@
#include <dns/tests/unittest_util.h>
#include <dns/tests/rdata_unittest.h>
+#include <util/unittests/wiredata.h>
-using isc::UnitTestUtil;
using namespace std;
using namespace isc;
using namespace isc::dns;
using namespace isc::util;
using namespace isc::util::encode;
using namespace isc::dns::rdata;
+using isc::UnitTestUtil;
+using isc::util::unittests::matchWireData;
namespace {
@@ -125,8 +127,8 @@ TEST_F(Rdata_DHCID_Test, toWireRenderer) {
vector<unsigned char> data;
UnitTestUtil::readWireData("rdata_dhcid_toWire", data);
- EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, renderer.getData(),
- renderer.getLength(), &data[0], data.size());
+ matchWireData(&data[0], data.size(),
+ renderer.getData(), renderer.getLength());
}
TEST_F(Rdata_DHCID_Test, toWireBuffer) {
@@ -134,8 +136,8 @@ TEST_F(Rdata_DHCID_Test, toWireBuffer) {
vector<unsigned char> data;
UnitTestUtil::readWireData("rdata_dhcid_toWire", data);
- EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, obuffer.getData(),
- obuffer.getLength(), &data[0], data.size());
+ matchWireData(&data[0], data.size(),
+ obuffer.getData(), obuffer.getLength());
}
TEST_F(Rdata_DHCID_Test, toText) {
diff --git a/src/lib/dns/tests/rdata_dname_unittest.cc b/src/lib/dns/tests/rdata_dname_unittest.cc
index 7209e36..9e13a16 100644
--- a/src/lib/dns/tests/rdata_dname_unittest.cc
+++ b/src/lib/dns/tests/rdata_dname_unittest.cc
@@ -24,12 +24,14 @@
#include <dns/tests/unittest_util.h>
#include <dns/tests/rdata_unittest.h>
+#include <util/unittests/wiredata.h>
-using isc::UnitTestUtil;
using namespace std;
using namespace isc::dns;
using namespace isc::util;
using namespace isc::dns::rdata;
+using isc::UnitTestUtil;
+using isc::util::unittests::matchWireData;
namespace {
class Rdata_DNAME_Test : public RdataTest {
@@ -117,20 +119,18 @@ TEST_F(Rdata_DNAME_Test, createFromLexer) {
TEST_F(Rdata_DNAME_Test, toWireBuffer) {
rdata_dname.toWire(obuffer);
- EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
- obuffer.getData(), obuffer.getLength(),
- wiredata_dname, sizeof(wiredata_dname));
+ matchWireData(wiredata_dname, sizeof(wiredata_dname),
+ obuffer.getData(), obuffer.getLength());
}
TEST_F(Rdata_DNAME_Test, toWireRenderer) {
rdata_dname.toWire(renderer);
- EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
- renderer.getData(), renderer.getLength(),
- wiredata_dname, sizeof(wiredata_dname));
+ matchWireData(wiredata_dname, sizeof(wiredata_dname),
+ renderer.getData(), renderer.getLength());
+
rdata_dname2.toWire(renderer);
- EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
- renderer.getData(), renderer.getLength(),
- wiredata_dname2, sizeof(wiredata_dname2));
+ matchWireData(wiredata_dname2, sizeof(wiredata_dname2),
+ renderer.getData(), renderer.getLength());
}
TEST_F(Rdata_DNAME_Test, toText) {
diff --git a/src/lib/dns/tests/rdata_dnskey_unittest.cc b/src/lib/dns/tests/rdata_dnskey_unittest.cc
index 872dc2a..0c77735 100644
--- a/src/lib/dns/tests/rdata_dnskey_unittest.cc
+++ b/src/lib/dns/tests/rdata_dnskey_unittest.cc
@@ -27,13 +27,15 @@
#include <dns/tests/unittest_util.h>
#include <dns/tests/rdata_unittest.h>
+#include <util/unittests/wiredata.h>
-using isc::UnitTestUtil;
using namespace std;
using namespace isc;
using namespace isc::dns;
using namespace isc::util;
using namespace isc::dns::rdata;
+using isc::UnitTestUtil;
+using isc::util::unittests::matchWireData;
namespace {
class Rdata_DNSKEY_Test : public RdataTest {
@@ -155,9 +157,9 @@ TEST_F(Rdata_DNSKEY_Test, toWireRenderer) {
vector<unsigned char> data;
UnitTestUtil::readWireData("rdata_dnskey_fromWire.wire", data);
- EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
- static_cast<const uint8_t *>(renderer.getData()) + 2,
- renderer.getLength() - 2, &data[2], data.size() - 2);
+ matchWireData(&data[2], data.size() - 2,
+ static_cast<const uint8_t *>(renderer.getData()) + 2,
+ renderer.getLength() - 2);
}
TEST_F(Rdata_DNSKEY_Test, toWireBuffer) {
@@ -165,9 +167,8 @@ TEST_F(Rdata_DNSKEY_Test, toWireBuffer) {
vector<unsigned char> data;
UnitTestUtil::readWireData("rdata_dnskey_fromWire.wire", data);
- EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
- obuffer.getData(), obuffer.getLength(),
- &data[2], data.size() - 2);
+ matchWireData(&data[2], data.size() - 2,
+ obuffer.getData(), obuffer.getLength());
}
TEST_F(Rdata_DNSKEY_Test, createFromWire) {
diff --git a/src/lib/dns/tests/rdata_ds_like_unittest.cc b/src/lib/dns/tests/rdata_ds_like_unittest.cc
index ae6a360..e278f1f 100644
--- a/src/lib/dns/tests/rdata_ds_like_unittest.cc
+++ b/src/lib/dns/tests/rdata_ds_like_unittest.cc
@@ -26,12 +26,14 @@
#include <dns/tests/unittest_util.h>
#include <dns/tests/rdata_unittest.h>
+#include <util/unittests/wiredata.h>
-using isc::UnitTestUtil;
using namespace std;
using namespace isc::dns;
using namespace isc::util;
using namespace isc::dns::rdata;
+using isc::UnitTestUtil;
+using isc::util::unittests::matchWireData;
namespace {
// hacks to make templates work
@@ -148,11 +150,9 @@ TYPED_TEST(Rdata_DS_LIKE_Test, toWireRenderer) {
vector<unsigned char> data;
UnitTestUtil::readWireData("rdata_ds_fromWire", data);
- EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
- static_cast<const uint8_t*>
- (this->renderer.getData()) + 2,
- this->renderer.getLength() - 2,
- &data[2], data.size() - 2);
+ matchWireData(&data[2], data.size() - 2,
+ static_cast<const uint8_t*>(this->renderer.getData()) + 2,
+ this->renderer.getLength() - 2);
}
TYPED_TEST(Rdata_DS_LIKE_Test, toWireBuffer) {
diff --git a/src/lib/dns/tests/rdata_hinfo_unittest.cc b/src/lib/dns/tests/rdata_hinfo_unittest.cc
index 7be2cb6..887848e 100644
--- a/src/lib/dns/tests/rdata_hinfo_unittest.cc
+++ b/src/lib/dns/tests/rdata_hinfo_unittest.cc
@@ -24,13 +24,15 @@
#include <dns/tests/unittest_util.h>
#include <dns/tests/rdata_unittest.h>
+#include <util/unittests/wiredata.h>
-using isc::UnitTestUtil;
using namespace std;
using namespace isc::dns;
using namespace isc::util;
using namespace isc::dns::rdata;
using namespace isc::dns::rdata::generic;
+using isc::UnitTestUtil;
+using isc::util::unittests::matchWireData;
namespace {
class Rdata_HINFO_Test : public RdataTest {
@@ -113,19 +115,18 @@ TEST_F(Rdata_HINFO_Test, toText) {
TEST_F(Rdata_HINFO_Test, toWire) {
HINFO hinfo(hinfo_str);
- hinfo.toWire(obuffer);
- EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, obuffer.getData(),
- obuffer.getLength(), hinfo_rdata, sizeof(hinfo_rdata));
+ hinfo.toWire(obuffer);
+ matchWireData(hinfo_rdata, sizeof (hinfo_rdata),
+ obuffer.getData(), obuffer.getLength());
}
TEST_F(Rdata_HINFO_Test, toWireRenderer) {
HINFO hinfo(hinfo_str);
hinfo.toWire(renderer);
- EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, renderer.getData(),
- renderer.getLength(), hinfo_rdata,
- sizeof(hinfo_rdata));
+ matchWireData(hinfo_rdata, sizeof (hinfo_rdata),
+ renderer.getData(), renderer.getLength());
}
TEST_F(Rdata_HINFO_Test, compare) {
diff --git a/src/lib/dns/tests/rdata_in_a_unittest.cc b/src/lib/dns/tests/rdata_in_a_unittest.cc
index 3f65641..c940075 100644
--- a/src/lib/dns/tests/rdata_in_a_unittest.cc
+++ b/src/lib/dns/tests/rdata_in_a_unittest.cc
@@ -27,17 +27,19 @@
#include <dns/tests/unittest_util.h>
#include <dns/tests/rdata_unittest.h>
+#include <util/unittests/wiredata.h>
#include <sstream>
#include <arpa/inet.h>
#include <sys/socket.h>
-using isc::UnitTestUtil;
using namespace std;
using namespace isc::dns;
using namespace isc::util;
using namespace isc::dns::rdata;
+using isc::UnitTestUtil;
+using isc::util::unittests::matchWireData;
namespace {
class Rdata_IN_A_Test : public RdataTest {
@@ -121,16 +123,14 @@ TEST_F(Rdata_IN_A_Test, createFromWire) {
TEST_F(Rdata_IN_A_Test, toWireBuffer) {
rdata_in_a.toWire(obuffer);
- EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
- obuffer.getData(), obuffer.getLength(),
- wiredata_in_a, sizeof(wiredata_in_a));
+ matchWireData(wiredata_in_a, sizeof (wiredata_in_a),
+ obuffer.getData(), obuffer.getLength());
}
TEST_F(Rdata_IN_A_Test, toWireRenderer) {
rdata_in_a.toWire(renderer);
- EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
- renderer.getData(), renderer.getLength(),
- wiredata_in_a, sizeof(wiredata_in_a));
+ matchWireData(wiredata_in_a, sizeof (wiredata_in_a),
+ renderer.getData(), renderer.getLength());
}
TEST_F(Rdata_IN_A_Test, toText) {
diff --git a/src/lib/dns/tests/rdata_in_aaaa_unittest.cc b/src/lib/dns/tests/rdata_in_aaaa_unittest.cc
index 82f75a8..ff92d07 100644
--- a/src/lib/dns/tests/rdata_in_aaaa_unittest.cc
+++ b/src/lib/dns/tests/rdata_in_aaaa_unittest.cc
@@ -24,12 +24,14 @@
#include <dns/tests/unittest_util.h>
#include <dns/tests/rdata_unittest.h>
+#include <util/unittests/wiredata.h>
-using isc::UnitTestUtil;
using namespace std;
using namespace isc::dns;
using namespace isc::util;
using namespace isc::dns::rdata;
+using isc::UnitTestUtil;
+using isc::util::unittests::matchWireData;
namespace {
class Rdata_IN_AAAA_Test : public RdataTest {
@@ -117,16 +119,14 @@ TEST_F(Rdata_IN_AAAA_Test, createFromLexer) {
TEST_F(Rdata_IN_AAAA_Test, toWireBuffer) {
rdata_in_aaaa.toWire(obuffer);
- EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
- obuffer.getData(), obuffer.getLength(),
- wiredata_in_aaaa, sizeof(wiredata_in_aaaa));
+ matchWireData(wiredata_in_aaaa, sizeof (wiredata_in_aaaa),
+ obuffer.getData(), obuffer.getLength());
}
TEST_F(Rdata_IN_AAAA_Test, toWireRenderer) {
rdata_in_aaaa.toWire(renderer);
- EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
- renderer.getData(), renderer.getLength(),
- wiredata_in_aaaa, sizeof(wiredata_in_aaaa));
+ matchWireData(wiredata_in_aaaa, sizeof (wiredata_in_aaaa),
+ renderer.getData(), renderer.getLength());
}
TEST_F(Rdata_IN_AAAA_Test, toText) {
diff --git a/src/lib/dns/tests/rdata_minfo_unittest.cc b/src/lib/dns/tests/rdata_minfo_unittest.cc
index 3ce6a6c..8addb29 100644
--- a/src/lib/dns/tests/rdata_minfo_unittest.cc
+++ b/src/lib/dns/tests/rdata_minfo_unittest.cc
@@ -26,12 +26,14 @@
#include <dns/tests/unittest_util.h>
#include <dns/tests/rdata_unittest.h>
+#include <util/unittests/wiredata.h>
-using isc::UnitTestUtil;
using namespace std;
using namespace isc::util;
using namespace isc::dns;
using namespace isc::dns::rdata;
+using isc::UnitTestUtil;
+using isc::util::unittests::matchWireData;
namespace {
class Rdata_MINFO_Test : public RdataTest {
@@ -177,33 +179,30 @@ TEST_F(Rdata_MINFO_Test, toWireBuffer) {
rdata_minfo.toWire(obuffer);
vector<unsigned char> data;
UnitTestUtil::readWireData("rdata_minfo_toWireUncompressed1.wire", data);
- EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
- static_cast<const uint8_t *>(obuffer.getData()),
- obuffer.getLength(), &data[0], data.size());
+ matchWireData(&data[0], data.size(),
+ obuffer.getData(), obuffer.getLength());
obuffer.clear();
rdata_minfo2.toWire(obuffer);
vector<unsigned char> data2;
UnitTestUtil::readWireData("rdata_minfo_toWireUncompressed2.wire", data2);
- EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
- static_cast<const uint8_t *>(obuffer.getData()),
- obuffer.getLength(), &data2[0], data2.size());
+ matchWireData(&data2[0], data2.size(),
+ obuffer.getData(), obuffer.getLength());
}
TEST_F(Rdata_MINFO_Test, toWireRenderer) {
rdata_minfo.toWire(renderer);
vector<unsigned char> data;
UnitTestUtil::readWireData("rdata_minfo_toWire1.wire", data);
- EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
- static_cast<const uint8_t *>(renderer.getData()),
- renderer.getLength(), &data[0], data.size());
+ matchWireData(&data[0], data.size(),
+ renderer.getData(), renderer.getLength());
+
renderer.clear();
rdata_minfo2.toWire(renderer);
vector<unsigned char> data2;
UnitTestUtil::readWireData("rdata_minfo_toWire2.wire", data2);
- EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
- static_cast<const uint8_t *>(renderer.getData()),
- renderer.getLength(), &data2[0], data2.size());
+ matchWireData(&data2[0], data2.size(),
+ renderer.getData(), renderer.getLength());
}
TEST_F(Rdata_MINFO_Test, toText) {
diff --git a/src/lib/dns/tests/rdata_mx_unittest.cc b/src/lib/dns/tests/rdata_mx_unittest.cc
index 6e4eaba..926374a 100644
--- a/src/lib/dns/tests/rdata_mx_unittest.cc
+++ b/src/lib/dns/tests/rdata_mx_unittest.cc
@@ -23,12 +23,14 @@
#include <dns/tests/unittest_util.h>
#include <dns/tests/rdata_unittest.h>
+#include <util/unittests/wiredata.h>
-using isc::UnitTestUtil;
using namespace std;
using namespace isc::dns;
using namespace isc::util;
using namespace isc::dns::rdata;
+using isc::UnitTestUtil;
+using isc::util::unittests::matchWireData;
namespace {
class Rdata_MX_Test : public RdataTest {
@@ -101,8 +103,8 @@ TEST_F(Rdata_MX_Test, toWireRenderer) {
vector<unsigned char> data;
UnitTestUtil::readWireData("rdata_mx_toWire1", data);
- EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, renderer.getData(),
- renderer.getLength(), &data[0], data.size());
+ matchWireData(&data[0], data.size(),
+ renderer.getData(), renderer.getLength());
}
TEST_F(Rdata_MX_Test, toWireBuffer) {
@@ -111,8 +113,8 @@ TEST_F(Rdata_MX_Test, toWireBuffer) {
vector<unsigned char> data;
UnitTestUtil::readWireData("rdata_mx_toWire2", data);
- EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, obuffer.getData(),
- obuffer.getLength(), &data[0], data.size());
+ matchWireData(&data[0], data.size(),
+ obuffer.getData(), obuffer.getLength());
}
TEST_F(Rdata_MX_Test, toText) {
diff --git a/src/lib/dns/tests/rdata_naptr_unittest.cc b/src/lib/dns/tests/rdata_naptr_unittest.cc
index d828e73..982bf76 100644
--- a/src/lib/dns/tests/rdata_naptr_unittest.cc
+++ b/src/lib/dns/tests/rdata_naptr_unittest.cc
@@ -24,13 +24,15 @@
#include <dns/tests/unittest_util.h>
#include <dns/tests/rdata_unittest.h>
+#include <util/unittests/wiredata.h>
-using isc::UnitTestUtil;
using namespace std;
using namespace isc::dns;
using namespace isc::util;
using namespace isc::dns::rdata;
using namespace isc::dns::rdata::generic;
+using isc::UnitTestUtil;
+using isc::util::unittests::matchWireData;
namespace {
class Rdata_NAPTR_Test : public RdataTest {
@@ -165,19 +167,18 @@ TEST_F(Rdata_NAPTR_Test, createFromLexer) {
TEST_F(Rdata_NAPTR_Test, toWire) {
NAPTR naptr(naptr_str);
- naptr.toWire(obuffer);
- EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, obuffer.getData(),
- obuffer.getLength(), naptr_rdata, sizeof(naptr_rdata));
+ naptr.toWire(obuffer);
+ matchWireData(naptr_rdata, sizeof(naptr_rdata),
+ obuffer.getData(), obuffer.getLength());
}
TEST_F(Rdata_NAPTR_Test, toWireRenderer) {
NAPTR naptr(naptr_str);
naptr.toWire(renderer);
- EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, renderer.getData(),
- renderer.getLength(), naptr_rdata,
- sizeof(naptr_rdata));
+ matchWireData(naptr_rdata, sizeof(naptr_rdata),
+ renderer.getData(), renderer.getLength());
}
TEST_F(Rdata_NAPTR_Test, toText) {
diff --git a/src/lib/dns/tests/rdata_ns_unittest.cc b/src/lib/dns/tests/rdata_ns_unittest.cc
index 53eb670..305149c 100644
--- a/src/lib/dns/tests/rdata_ns_unittest.cc
+++ b/src/lib/dns/tests/rdata_ns_unittest.cc
@@ -24,12 +24,14 @@
#include <dns/tests/unittest_util.h>
#include <dns/tests/rdata_unittest.h>
+#include <util/unittests/wiredata.h>
-using isc::UnitTestUtil;
using namespace std;
using namespace isc::dns;
using namespace isc::util;
using namespace isc::dns::rdata;
+using isc::UnitTestUtil;
+using isc::util::unittests::matchWireData;
namespace {
class Rdata_NS_Test : public RdataTest {
@@ -118,20 +120,18 @@ TEST_F(Rdata_NS_Test, createFromLexer) {
TEST_F(Rdata_NS_Test, toWireBuffer) {
rdata_ns.toWire(obuffer);
- EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
- obuffer.getData(), obuffer.getLength(),
- wiredata_ns, sizeof(wiredata_ns));
+ matchWireData(wiredata_ns, sizeof(wiredata_ns),
+ obuffer.getData(), obuffer.getLength());
}
TEST_F(Rdata_NS_Test, toWireRenderer) {
rdata_ns.toWire(renderer);
- EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
- renderer.getData(), renderer.getLength(),
- wiredata_ns, sizeof(wiredata_ns));
+ matchWireData(wiredata_ns, sizeof(wiredata_ns),
+ renderer.getData(), renderer.getLength());
+
rdata_ns2.toWire(renderer);
- EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
- renderer.getData(), renderer.getLength(),
- wiredata_ns2, sizeof(wiredata_ns2));
+ matchWireData(wiredata_ns2, sizeof(wiredata_ns2),
+ renderer.getData(), renderer.getLength());
}
TEST_F(Rdata_NS_Test, toText) {
diff --git a/src/lib/dns/tests/rdata_nsec3param_like_unittest.cc b/src/lib/dns/tests/rdata_nsec3param_like_unittest.cc
index 23d6d0e..ea38e7c 100644
--- a/src/lib/dns/tests/rdata_nsec3param_like_unittest.cc
+++ b/src/lib/dns/tests/rdata_nsec3param_like_unittest.cc
@@ -22,14 +22,17 @@
#include <dns/tests/unittest_util.h>
#include <dns/tests/rdata_unittest.h>
+#include <util/unittests/wiredata.h>
#include <string>
#include <vector>
using namespace std;
-using isc::UnitTestUtil;
using namespace isc::dns;
using namespace isc::dns::rdata;
+using namespace isc::util;
+using isc::UnitTestUtil;
+using isc::util::unittests::matchWireData;
namespace {
@@ -230,8 +233,8 @@ toWireCheck(RRType rrtype, OUTPUT_TYPE& output, const string& data_file) {
output.clear();
output.writeUint16(rdlen);
createRdata(rrtype, RRClass::IN(), buffer, rdlen)->toWire(output);
- EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, output.getData(),
- output.getLength(), &data[0], data.size());
+ matchWireData(&data[0], data.size(),
+ output.getData(), output.getLength());
}
TYPED_TEST(NSEC3PARAMLikeTest, toWire) {
diff --git a/src/lib/dns/tests/rdata_nsec3param_unittest.cc b/src/lib/dns/tests/rdata_nsec3param_unittest.cc
index 4fccbf3..9f121cc 100644
--- a/src/lib/dns/tests/rdata_nsec3param_unittest.cc
+++ b/src/lib/dns/tests/rdata_nsec3param_unittest.cc
@@ -27,13 +27,15 @@
#include <dns/tests/unittest_util.h>
#include <dns/tests/rdata_unittest.h>
+#include <util/unittests/wiredata.h>
-using isc::UnitTestUtil;
using namespace std;
using namespace isc;
using namespace isc::dns;
using namespace isc::util;
using namespace isc::dns::rdata;
+using isc::UnitTestUtil;
+using isc::util::unittests::matchWireData;
namespace {
class Rdata_NSEC3PARAM_Test : public RdataTest {
@@ -170,9 +172,9 @@ TEST_F(Rdata_NSEC3PARAM_Test, toWireRenderer) {
vector<unsigned char> data;
UnitTestUtil::readWireData("rdata_nsec3param_fromWire1", data);
- EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
- static_cast<const uint8_t *>(renderer.getData()) + 2,
- renderer.getLength() - 2, &data[2], data.size() - 2);
+ matchWireData(&data[2], data.size() - 2,
+ static_cast<const uint8_t *>(renderer.getData()) + 2,
+ renderer.getLength() - 2);
}
TEST_F(Rdata_NSEC3PARAM_Test, toWireBuffer) {
@@ -180,9 +182,8 @@ TEST_F(Rdata_NSEC3PARAM_Test, toWireBuffer) {
vector<unsigned char> data;
UnitTestUtil::readWireData("rdata_nsec3param_fromWire1", data);
- EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
- obuffer.getData(), obuffer.getLength(),
- &data[2], data.size() - 2);
+ matchWireData(&data[2], data.size() - 2,
+ obuffer.getData(), obuffer.getLength());
}
TEST_F(Rdata_NSEC3PARAM_Test, getHashAlg) {
diff --git a/src/lib/dns/tests/rdata_nsec_unittest.cc b/src/lib/dns/tests/rdata_nsec_unittest.cc
index 810e2cc..dcbc1f3 100644
--- a/src/lib/dns/tests/rdata_nsec_unittest.cc
+++ b/src/lib/dns/tests/rdata_nsec_unittest.cc
@@ -26,12 +26,14 @@
#include <dns/tests/unittest_util.h>
#include <dns/tests/rdata_unittest.h>
+#include <util/unittests/wiredata.h>
-using isc::UnitTestUtil;
using namespace std;
using namespace isc::dns;
using namespace isc::util;
using namespace isc::dns::rdata;
+using isc::UnitTestUtil;
+using isc::util::unittests::matchWireData;
namespace {
class Rdata_NSEC_Test : public RdataTest {
@@ -90,9 +92,9 @@ TEST_F(Rdata_NSEC_Test, toWireRenderer_NSEC) {
vector<unsigned char> data;
UnitTestUtil::readWireData("rdata_nsec_fromWire1", data);
- EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
- static_cast<const uint8_t *>(renderer.getData()) + 2,
- renderer.getLength() - 2, &data[2], data.size() - 2);
+ matchWireData(&data[2], data.size() - 2,
+ static_cast<const uint8_t *>(renderer.getData()) + 2,
+ renderer.getLength() - 2);
}
TEST_F(Rdata_NSEC_Test, toWireBuffer_NSEC) {
diff --git a/src/lib/dns/tests/rdata_nsecbitmap_unittest.cc b/src/lib/dns/tests/rdata_nsecbitmap_unittest.cc
index fbb256a..9c24200 100644
--- a/src/lib/dns/tests/rdata_nsecbitmap_unittest.cc
+++ b/src/lib/dns/tests/rdata_nsecbitmap_unittest.cc
@@ -23,6 +23,7 @@
#include <gtest/gtest.h>
#include <dns/tests/rdata_unittest.h>
+#include <util/unittests/wiredata.h>
#include <boost/lexical_cast.hpp>
@@ -30,10 +31,12 @@
#include <vector>
using namespace std;
-using boost::lexical_cast;
-using isc::UnitTestUtil;
using namespace isc::dns;
using namespace isc::dns::rdata;
+using namespace isc::util;
+using isc::UnitTestUtil;
+using isc::util::unittests::matchWireData;
+using boost::lexical_cast;
namespace {
@@ -253,16 +256,16 @@ TEST_F(NSEC3BitmapTest, emptyMap) {
OutputBuffer obuffer(0);
obuffer.writeUint16(rdlen);
empty_nsec3.toWire(obuffer);
- EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, obuffer.getData(),
- obuffer.getLength(), &data[0], data.size());
+ matchWireData(&data[0], data.size(),
+ obuffer.getData(), obuffer.getLength());
// Same for MessageRenderer.
obuffer.clear();
MessageRenderer renderer;
renderer.writeUint16(rdlen);
empty_nsec3.toWire(renderer);
- EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, renderer.getData(),
- renderer.getLength(), &data[0], data.size());
+ matchWireData(&data[0], data.size(),
+ renderer.getData(), renderer.getLength());
}
}
diff --git a/src/lib/dns/tests/rdata_opt_unittest.cc b/src/lib/dns/tests/rdata_opt_unittest.cc
index c5a10b9..9d09a60 100644
--- a/src/lib/dns/tests/rdata_opt_unittest.cc
+++ b/src/lib/dns/tests/rdata_opt_unittest.cc
@@ -23,12 +23,14 @@
#include <dns/tests/unittest_util.h>
#include <dns/tests/rdata_unittest.h>
+#include <util/unittests/wiredata.h>
-using isc::UnitTestUtil;
using namespace std;
using namespace isc::dns;
using namespace isc::util;
using namespace isc::dns::rdata;
+using isc::UnitTestUtil;
+using isc::util::unittests::matchWireData;
namespace {
class Rdata_OPT_Test : public RdataTest {
@@ -93,10 +95,8 @@ TEST_F(Rdata_OPT_Test, toWireBuffer) {
obuffer.clear();
rdata_opt.toWire(obuffer);
- EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
- obuffer.getData(),
- obuffer.getLength(),
- rdata_opt_wiredata, sizeof(rdata_opt_wiredata));
+ matchWireData(rdata_opt_wiredata, sizeof(rdata_opt_wiredata),
+ obuffer.getData(), obuffer.getLength());
}
TEST_F(Rdata_OPT_Test, toWireRenderer) {
@@ -108,10 +108,8 @@ TEST_F(Rdata_OPT_Test, toWireRenderer) {
renderer.clear();
rdata_opt.toWire(renderer);
- EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
- renderer.getData(),
- renderer.getLength(),
- rdata_opt_wiredata, sizeof(rdata_opt_wiredata));
+ matchWireData(rdata_opt_wiredata, sizeof(rdata_opt_wiredata),
+ renderer.getData(), renderer.getLength());
}
TEST_F(Rdata_OPT_Test, toText) {
@@ -182,10 +180,8 @@ TEST_F(Rdata_OPT_Test, appendPseudoRR) {
obuffer.clear();
rdata_opt.toWire(obuffer);
- EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
- obuffer.getData(),
- obuffer.getLength(),
- rdata_opt_wiredata2, sizeof(rdata_opt_wiredata2));
+ matchWireData(rdata_opt_wiredata2, sizeof(rdata_opt_wiredata2),
+ obuffer.getData(), obuffer.getLength());
}
TEST_F(Rdata_OPT_Test, getPseudoRRs) {
diff --git a/src/lib/dns/tests/rdata_ptr_unittest.cc b/src/lib/dns/tests/rdata_ptr_unittest.cc
index 5d6d37d..d6a99f7 100644
--- a/src/lib/dns/tests/rdata_ptr_unittest.cc
+++ b/src/lib/dns/tests/rdata_ptr_unittest.cc
@@ -24,12 +24,14 @@
#include <dns/tests/unittest_util.h>
#include <dns/tests/rdata_unittest.h>
+#include <util/unittests/wiredata.h>
-using isc::UnitTestUtil;
using namespace std;
using namespace isc::dns;
using namespace isc::util;
using namespace isc::dns::rdata;
+using isc::UnitTestUtil;
+using isc::util::unittests::matchWireData;
//
// This test currently simply copies the NS RDATA tests.
@@ -118,20 +120,18 @@ TEST_F(Rdata_PTR_Test, createFromLexer) {
TEST_F(Rdata_PTR_Test, toWireBuffer) {
rdata_ptr.toWire(obuffer);
- EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
- obuffer.getData(), obuffer.getLength(),
- wiredata_ptr, sizeof(wiredata_ptr));
+ matchWireData(wiredata_ptr, sizeof(wiredata_ptr),
+ obuffer.getData(), obuffer.getLength());
}
TEST_F(Rdata_PTR_Test, toWireRenderer) {
rdata_ptr.toWire(renderer);
- EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
- renderer.getData(), renderer.getLength(),
- wiredata_ptr, sizeof(wiredata_ptr));
+ matchWireData(wiredata_ptr, sizeof(wiredata_ptr),
+ renderer.getData(), renderer.getLength());
+
rdata_ptr2.toWire(renderer);
- EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
- renderer.getData(), renderer.getLength(),
- wiredata_ptr2, sizeof(wiredata_ptr2));
+ matchWireData(wiredata_ptr2, sizeof(wiredata_ptr2),
+ renderer.getData(), renderer.getLength());
}
TEST_F(Rdata_PTR_Test, toText) {
diff --git a/src/lib/dns/tests/rdata_rp_unittest.cc b/src/lib/dns/tests/rdata_rp_unittest.cc
index 38bec04..d8de028 100644
--- a/src/lib/dns/tests/rdata_rp_unittest.cc
+++ b/src/lib/dns/tests/rdata_rp_unittest.cc
@@ -22,12 +22,14 @@
#include <dns/tests/unittest_util.h>
#include <dns/tests/rdata_unittest.h>
+#include <util/unittests/wiredata.h>
-using isc::UnitTestUtil;
using namespace std;
using namespace isc::util;
using namespace isc::dns;
using namespace isc::dns::rdata;
+using isc::UnitTestUtil;
+using isc::util::unittests::matchWireData;
namespace {
class Rdata_RP_Test : public RdataTest {
@@ -157,9 +159,8 @@ TEST_F(Rdata_RP_Test, toWireBuffer) {
rdata_rp.toWire(obuffer);
// then compare them
- EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
- obuffer.getData(), obuffer.getLength(),
- &expected_wire[0], expected_wire.size());
+ matchWireData(&expected_wire[0], expected_wire.size(),
+ obuffer.getData(), obuffer.getLength());
}
TEST_F(Rdata_RP_Test, toWireRenderer) {
@@ -172,9 +173,8 @@ TEST_F(Rdata_RP_Test, toWireRenderer) {
renderer.writeName(Name("a.example.com"));
renderer.writeName(Name("b.example.net"));
generic::RP(mailbox_name, Name("rp-text.example.net")).toWire(renderer);
- EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
- renderer.getData(), renderer.getLength(),
- &expected_wire[0], expected_wire.size());
+ matchWireData(&expected_wire[0], expected_wire.size(),
+ renderer.getData(), renderer.getLength());
}
TEST_F(Rdata_RP_Test, toText) {
diff --git a/src/lib/dns/tests/rdata_rrsig_unittest.cc b/src/lib/dns/tests/rdata_rrsig_unittest.cc
index 2d075ec..67ead3c 100644
--- a/src/lib/dns/tests/rdata_rrsig_unittest.cc
+++ b/src/lib/dns/tests/rdata_rrsig_unittest.cc
@@ -25,16 +25,57 @@
#include <gtest/gtest.h>
#include <dns/tests/unittest_util.h>
+#include <util/unittests/wiredata.h>
#include <dns/tests/rdata_unittest.h>
-using isc::UnitTestUtil;
using namespace std;
using namespace isc;
using namespace isc::dns;
using namespace isc::util;
using namespace isc::dns::rdata;
+using isc::UnitTestUtil;
+using isc::util::unittests::matchWireData;
namespace {
+
+const uint8_t wiredata_rrsig[] = {
+ // type covered = A
+ 0x00, 0x01,
+ // algorithm = 5
+ 0x05,
+ // labels = 4
+ 0x04,
+ // original TTL = 43200 (0x0000a8c0)
+ 0x00, 0x00, 0xa8, 0xc0,
+ // signature expiration = 1266961577 (0x4b844ca9)
+ 0x4b, 0x84, 0x4c, 0xa9,
+ // signature inception = 1266875177 (0x4b82fb29)
+ 0x4b, 0x82, 0xfb, 0x29,
+ // key tag = 8496 (0x2130)
+ 0x21, 0x30,
+ // signer's name (isc.org.)
+ // 3 i s c 3 o r g 0
+ 0x03, 0x69, 0x73, 0x63, 0x03, 0x6f, 0x72, 0x67, 0x00,
+ // signature data follows
+ 0x7a, 0xfc, 0x61, 0x94, 0x6c,
+ 0x75, 0xde, 0x6a, 0x4a, 0x2d, 0x59, 0x0a, 0xb2,
+ 0x3a, 0x46, 0xcf, 0x27, 0x12, 0xe6, 0xdc, 0x2d,
+ 0x22, 0x8c, 0x4e, 0x9a, 0x53, 0x75, 0xe3, 0x0f,
+ 0x6d, 0xe4, 0x08, 0x33, 0x18, 0x19, 0xb3, 0x76,
+ 0x21, 0x9d, 0x2c, 0x8a, 0xc5, 0x69, 0xba, 0xab,
+ 0xef, 0x66, 0x9f, 0xda, 0xb5, 0x2a, 0xf9, 0x40,
+ 0xc1, 0x28, 0xc5, 0x97, 0xba, 0x3c, 0x19, 0x4d,
+ 0x95, 0x13, 0xc2, 0xcd, 0xf6, 0xb1, 0x59, 0x5d,
+ 0x0c, 0xf9, 0x3f, 0x35, 0xbb, 0x9a, 0x70, 0x93,
+ 0x36, 0xe5, 0xf4, 0x17, 0x7e, 0xfe, 0x66, 0x3b,
+ 0x70, 0x1f, 0xed, 0x33, 0xa8, 0xa3, 0x0d, 0xc0,
+ 0x8c, 0xc6, 0x95, 0x1b, 0xd8, 0x9c, 0x8c, 0x25,
+ 0xb4, 0x57, 0x9e, 0x56, 0x71, 0x64, 0x14, 0x7f,
+ 0x8f, 0x6d, 0xfa, 0xc5, 0xca, 0x3f, 0x36, 0xe2,
+ 0xa4, 0xdf, 0x60, 0xfa, 0xcd, 0x59, 0x3e, 0x22,
+ 0x32, 0xa1, 0xf7
+};
+
class Rdata_RRSIG_Test : public RdataTest {
protected:
Rdata_RRSIG_Test() :
@@ -299,13 +340,17 @@ TEST_F(Rdata_RRSIG_Test, createFromLexer) {
}
TEST_F(Rdata_RRSIG_Test, toWireRenderer) {
- // FIXME: This doesn't check the result.
rdata_rrsig.toWire(renderer);
+
+ matchWireData(wiredata_rrsig, sizeof(wiredata_rrsig),
+ renderer.getData(), renderer.getLength());
}
TEST_F(Rdata_RRSIG_Test, toWireBuffer) {
- // FIXME: This doesn't check the result.
rdata_rrsig.toWire(obuffer);
+
+ matchWireData(wiredata_rrsig, sizeof(wiredata_rrsig),
+ obuffer.getData(), obuffer.getLength());
}
TEST_F(Rdata_RRSIG_Test, createFromWire) {
diff --git a/src/lib/dns/tests/rdata_soa_unittest.cc b/src/lib/dns/tests/rdata_soa_unittest.cc
index d919329..808ff8f 100644
--- a/src/lib/dns/tests/rdata_soa_unittest.cc
+++ b/src/lib/dns/tests/rdata_soa_unittest.cc
@@ -23,12 +23,14 @@
#include <dns/tests/unittest_util.h>
#include <dns/tests/rdata_unittest.h>
+#include <util/unittests/wiredata.h>
-using isc::UnitTestUtil;
using namespace std;
using namespace isc::dns;
using namespace isc::util;
using namespace isc::dns::rdata;
+using isc::UnitTestUtil;
+using isc::util::unittests::matchWireData;
namespace {
class Rdata_SOA_Test : public RdataTest {
@@ -180,9 +182,9 @@ TEST_F(Rdata_SOA_Test, toWireRenderer) {
vector<unsigned char> data;
UnitTestUtil::readWireData("rdata_soa_fromWire", data);
- EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
- static_cast<const uint8_t *>(renderer.getData()) + 2,
- renderer.getLength() - 2, &data[2], data.size() - 2);
+ matchWireData(&data[2], data.size() - 2,
+ static_cast<const uint8_t *>(renderer.getData()) + 2,
+ renderer.getLength() - 2);
}
TEST_F(Rdata_SOA_Test, toWireBuffer) {
@@ -190,9 +192,9 @@ TEST_F(Rdata_SOA_Test, toWireBuffer) {
rdata_soa.toWire(obuffer);
vector<unsigned char> data;
UnitTestUtil::readWireData("rdata_soa_toWireUncompressed.wire", data);
- EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
- static_cast<const uint8_t *>(obuffer.getData()) + 2,
- obuffer.getLength() - 2, &data[2], data.size() - 2);
+ matchWireData(&data[2], data.size() - 2,
+ static_cast<const uint8_t *>(obuffer.getData()) + 2,
+ obuffer.getLength() - 2);
}
TEST_F(Rdata_SOA_Test, toText) {
diff --git a/src/lib/dns/tests/rdata_srv_unittest.cc b/src/lib/dns/tests/rdata_srv_unittest.cc
index 6ca0c7f..8608c48 100644
--- a/src/lib/dns/tests/rdata_srv_unittest.cc
+++ b/src/lib/dns/tests/rdata_srv_unittest.cc
@@ -24,12 +24,14 @@
#include <dns/tests/unittest_util.h>
#include <dns/tests/rdata_unittest.h>
+#include <util/unittests/wiredata.h>
-using isc::UnitTestUtil;
using namespace std;
using namespace isc::dns;
using namespace isc::util;
using namespace isc::dns::rdata;
+using isc::UnitTestUtil;
+using isc::util::unittests::matchWireData;
namespace {
class Rdata_SRV_Test : public RdataTest {
@@ -159,26 +161,24 @@ TEST_F(Rdata_SRV_Test, createFromLexer) {
TEST_F(Rdata_SRV_Test, toWireBuffer) {
rdata_srv.toWire(obuffer);
- EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
- obuffer.getData(), obuffer.getLength(),
- wiredata_srv, sizeof(wiredata_srv));
+ matchWireData(wiredata_srv, sizeof(wiredata_srv),
+ obuffer.getData(), obuffer.getLength());
+
obuffer.clear();
rdata_srv2.toWire(obuffer);
- EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
- obuffer.getData(), obuffer.getLength(),
- wiredata_srv2, sizeof(wiredata_srv2));
+ matchWireData(wiredata_srv2, sizeof(wiredata_srv2),
+ obuffer.getData(), obuffer.getLength());
}
TEST_F(Rdata_SRV_Test, toWireRenderer) {
rdata_srv.toWire(renderer);
- EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
- renderer.getData(), renderer.getLength(),
- wiredata_srv, sizeof(wiredata_srv));
+ matchWireData(wiredata_srv, sizeof(wiredata_srv),
+ renderer.getData(), renderer.getLength());
+
renderer.clear();
rdata_srv2.toWire(renderer);
- EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
- renderer.getData(), renderer.getLength(),
- wiredata_srv2, sizeof(wiredata_srv2));
+ matchWireData(wiredata_srv2, sizeof(wiredata_srv2),
+ renderer.getData(), renderer.getLength());
}
TEST_F(Rdata_SRV_Test, toText) {
diff --git a/src/lib/dns/tests/rdata_sshfp_unittest.cc b/src/lib/dns/tests/rdata_sshfp_unittest.cc
index b85dfa5..cb8640a 100644
--- a/src/lib/dns/tests/rdata_sshfp_unittest.cc
+++ b/src/lib/dns/tests/rdata_sshfp_unittest.cc
@@ -26,14 +26,17 @@
#include <dns/tests/unittest_util.h>
#include <dns/tests/rdata_unittest.h>
+#include <util/unittests/wiredata.h>
+
#include <boost/algorithm/string.hpp>
-using isc::UnitTestUtil;
using namespace std;
using namespace isc;
using namespace isc::dns;
using namespace isc::util;
using namespace isc::dns::rdata;
+using isc::UnitTestUtil;
+using isc::util::unittests::matchWireData;
namespace {
class Rdata_SSHFP_Test : public RdataTest {
@@ -59,11 +62,6 @@ protected:
rdata_str, rdata_sshfp, true, true);
}
- void checkFromText_BadValue(const string& rdata_str) {
- checkFromText<generic::SSHFP, InvalidRdataText, BadValue>(
- rdata_str, rdata_sshfp, true, true);
- }
-
void checkFromText_BadString(const string& rdata_str) {
checkFromText
<generic::SSHFP, InvalidRdataText, isc::Exception>(
@@ -138,8 +136,8 @@ TEST_F(Rdata_SSHFP_Test, badText) {
checkFromText_LexerError("1");
checkFromText_LexerError("ONE 2 123456789abcdef67890123456789abcdef67890");
checkFromText_LexerError("1 TWO 123456789abcdef67890123456789abcdef67890");
- checkFromText_BadValue("1 2 BUCKLEMYSHOE");
- checkFromText_BadValue(sshfp_txt + " extra text");
+ checkFromText_InvalidText("1 2 BUCKLEMYSHOE");
+ checkFromText_InvalidText(sshfp_txt + " extra text");
// yes, these are redundant to the last test cases in algorithmTypes
checkFromText_InvalidText(
@@ -232,12 +230,11 @@ TEST_F(Rdata_SSHFP_Test, toWire) {
this->obuffer.clear();
rdata_sshfp.toWire(this->obuffer);
- EXPECT_EQ(22, this->obuffer.getLength());
+ EXPECT_EQ(sizeof (rdata_sshfp_wiredata),
+ this->obuffer.getLength());
- EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
- this->obuffer.getData(),
- this->obuffer.getLength(),
- rdata_sshfp_wiredata, sizeof(rdata_sshfp_wiredata));
+ matchWireData(rdata_sshfp_wiredata, sizeof(rdata_sshfp_wiredata),
+ obuffer.getData(), obuffer.getLength());
}
TEST_F(Rdata_SSHFP_Test, compare) {
@@ -254,8 +251,20 @@ TEST_F(Rdata_SSHFP_Test, getFingerprintType) {
EXPECT_EQ(1, rdata_sshfp.getFingerprintType());
}
-TEST_F(Rdata_SSHFP_Test, getFingerprintLen) {
- EXPECT_EQ(20, rdata_sshfp.getFingerprintLen());
+TEST_F(Rdata_SSHFP_Test, getFingerprint) {
+ const std::vector<uint8_t>& fingerprint =
+ rdata_sshfp.getFingerprint();
+
+ EXPECT_EQ(rdata_sshfp.getFingerprintLength(),
+ fingerprint.size());
+ for (int i = 0; i < fingerprint.size(); ++i) {
+ EXPECT_EQ(rdata_sshfp_wiredata[i + 2],
+ fingerprint.at(i));
+ }
+}
+
+TEST_F(Rdata_SSHFP_Test, getFingerprintLength) {
+ EXPECT_EQ(20, rdata_sshfp.getFingerprintLength());
}
TEST_F(Rdata_SSHFP_Test, emptyFingerprintFromWire) {
@@ -273,17 +282,15 @@ TEST_F(Rdata_SSHFP_Test, emptyFingerprintFromWire) {
EXPECT_EQ(4, rdf.getAlgorithmNumber());
EXPECT_EQ(9, rdf.getFingerprintType());
- EXPECT_EQ(0, rdf.getFingerprintLen());
+ EXPECT_EQ(0, rdf.getFingerprintLength());
this->obuffer.clear();
rdf.toWire(this->obuffer);
EXPECT_EQ(2, this->obuffer.getLength());
- EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
- this->obuffer.getData(),
- this->obuffer.getLength(),
- rdf_wiredata, sizeof(rdf_wiredata));
+ matchWireData(rdf_wiredata, sizeof(rdf_wiredata),
+ obuffer.getData(), obuffer.getLength());
}
TEST_F(Rdata_SSHFP_Test, emptyFingerprintFromString) {
@@ -297,16 +304,14 @@ TEST_F(Rdata_SSHFP_Test, emptyFingerprintFromString) {
EXPECT_EQ(5, rdata_sshfp2.getAlgorithmNumber());
EXPECT_EQ(6, rdata_sshfp2.getFingerprintType());
- EXPECT_EQ(0, rdata_sshfp2.getFingerprintLen());
+ EXPECT_EQ(0, rdata_sshfp2.getFingerprintLength());
this->obuffer.clear();
rdata_sshfp2.toWire(this->obuffer);
EXPECT_EQ(2, this->obuffer.getLength());
- EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
- this->obuffer.getData(),
- this->obuffer.getLength(),
- rdata_sshfp2_wiredata, sizeof(rdata_sshfp2_wiredata));
+ matchWireData(rdata_sshfp2_wiredata, sizeof(rdata_sshfp2_wiredata),
+ obuffer.getData(), obuffer.getLength());
}
}
diff --git a/src/lib/dns/tests/rdata_tsig_unittest.cc b/src/lib/dns/tests/rdata_tsig_unittest.cc
index 270a1b2..ab8fa27 100644
--- a/src/lib/dns/tests/rdata_tsig_unittest.cc
+++ b/src/lib/dns/tests/rdata_tsig_unittest.cc
@@ -29,32 +29,34 @@
#include <dns/tests/unittest_util.h>
#include <dns/tests/rdata_unittest.h>
+#include <util/unittests/wiredata.h>
-using isc::UnitTestUtil;
using namespace std;
using namespace isc;
using namespace isc::dns;
using namespace isc::util;
using namespace isc::dns::rdata;
+using isc::UnitTestUtil;
+using isc::util::unittests::matchWireData;
namespace {
class Rdata_TSIG_Test : public RdataTest {
protected:
Rdata_TSIG_Test() :
- // no MAC or Other Data
+ // no MAC or Other Data
valid_text1("hmac-md5.sig-alg.reg.int. 1286779327 300 "
"0 16020 BADKEY 0"),
- // MAC but no Other Data
+ // MAC but no Other Data
valid_text2("hmac-sha256. 1286779327 300 12 "
"FAKEFAKEFAKEFAKE 16020 BADSIG 0"),
- // MAC and Other Data
+ // MAC and Other Data
valid_text3("hmac-sha1. 1286779327 300 12 "
"FAKEFAKEFAKEFAKE 16020 BADTIME 6 FAKEFAKE"),
- // MAC and Other Data (with Error that doesn't expect Other Data)
+ // MAC and Other Data (with Error that doesn't expect Other Data)
valid_text4("hmac-sha1. 1286779327 300 12 "
"FAKEFAKEFAKEFAKE 16020 BADSIG 6 FAKEFAKE"),
- // numeric error code
+ // numeric error code
valid_text5("hmac-sha256. 1286779327 300 12 "
"FAKEFAKEFAKEFAKE 16020 2845 0"),
rdata_tsig(valid_text1)
@@ -204,9 +206,8 @@ fromWireCommonChecks(const any::TSIG& tsig) {
EXPECT_EQ(300, tsig.getFudge());
vector<uint8_t> expect_mac(32, 'x');
- EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
- &expect_mac[0], expect_mac.size(),
- tsig.getMAC(), tsig.getMACSize());
+ matchWireData(&expect_mac[0], expect_mac.size(),
+ tsig.getMAC(), tsig.getMACSize());
EXPECT_EQ(2845, tsig.getOriginalID());
@@ -234,9 +235,8 @@ TEST_F(Rdata_TSIG_Test, createFromWireWithOtherData) {
expect_data[3] = ((otherdata >> 16) & 0xff);
expect_data[4] = ((otherdata >> 8) & 0xff);
expect_data[5] = (otherdata & 0xff);
- EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
- &expect_data[0], expect_data.size(),
- tsig.getOtherData(), tsig.getOtherLen());
+ matchWireData(&expect_data[0], expect_data.size(),
+ tsig.getOtherData(), tsig.getOtherLen());
}
TEST_F(Rdata_TSIG_Test, createFromWireWithoutMAC) {
@@ -351,27 +351,24 @@ Rdata_TSIG_Test::toWireCommonChecks(Output& output) const {
// read the expected wire format data and trim the RDLEN part.
UnitTestUtil::readWireData("rdata_tsig_toWire1.wire", expect_data);
expect_data.erase(expect_data.begin(), expect_data.begin() + 2);
- EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
- &expect_data[0], expect_data.size(),
- output.getData(), output.getLength());
+ matchWireData(&expect_data[0], expect_data.size(),
+ output.getData(), output.getLength());
expect_data.clear();
output.clear();
any::TSIG(valid_text2).toWire(output);
UnitTestUtil::readWireData("rdata_tsig_toWire2.wire", expect_data);
expect_data.erase(expect_data.begin(), expect_data.begin() + 2);
- EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
- &expect_data[0], expect_data.size(),
- output.getData(), output.getLength());
+ matchWireData(&expect_data[0], expect_data.size(),
+ output.getData(), output.getLength());
expect_data.clear();
output.clear();
any::TSIG(valid_text3).toWire(output);
UnitTestUtil::readWireData("rdata_tsig_toWire3.wire", expect_data);
expect_data.erase(expect_data.begin(), expect_data.begin() + 2);
- EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
- &expect_data[0], expect_data.size(),
- output.getData(), output.getLength());
+ matchWireData(&expect_data[0], expect_data.size(),
+ output.getData(), output.getLength());
}
TEST_F(Rdata_TSIG_Test, toWireBuffer) {
@@ -388,9 +385,8 @@ TEST_F(Rdata_TSIG_Test, toWireRenderer) {
renderer.writeUint16(42); // RDLEN
rdata_tsig.toWire(renderer);
UnitTestUtil::readWireData("rdata_tsig_toWire4.wire", expect_data);
- EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
- &expect_data[0], expect_data.size(),
- renderer.getData(), renderer.getLength());
+ matchWireData(&expect_data[0], expect_data.size(),
+ renderer.getData(), renderer.getLength());
// check algorithm can be used as a compression target.
expect_data.clear();
@@ -399,9 +395,8 @@ TEST_F(Rdata_TSIG_Test, toWireRenderer) {
rdata_tsig.toWire(renderer);
renderer.writeName(Name("hmac-md5.sig-alg.reg.int"));
UnitTestUtil::readWireData("rdata_tsig_toWire5.wire", expect_data);
- EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
- &expect_data[0], expect_data.size(),
- renderer.getData(), renderer.getLength());
+ matchWireData(&expect_data[0], expect_data.size(),
+ renderer.getData(), renderer.getLength());
}
TEST_F(Rdata_TSIG_Test, toText) {
diff --git a/src/lib/dns/tests/rdata_txt_like_unittest.cc b/src/lib/dns/tests/rdata_txt_like_unittest.cc
index cb3c44d..573a4a1 100644
--- a/src/lib/dns/tests/rdata_txt_like_unittest.cc
+++ b/src/lib/dns/tests/rdata_txt_like_unittest.cc
@@ -21,6 +21,8 @@
#include <dns/tests/unittest_util.h>
#include <dns/tests/rdata_unittest.h>
+#include <util/unittests/wiredata.h>
+
#include <gtest/gtest.h>
#include <boost/bind.hpp>
@@ -29,11 +31,12 @@
#include <sstream>
#include <vector>
-using isc::UnitTestUtil;
using namespace std;
using namespace isc::dns;
using namespace isc::util;
using namespace isc::dns::rdata;
+using isc::UnitTestUtil;
+using isc::util::unittests::matchWireData;
namespace {
@@ -133,31 +136,31 @@ TYPED_TEST(Rdata_TXT_LIKE_Test, createFromText) {
// Null character-string.
this->obuffer.clear();
TypeParam(string("\"\"")).toWire(this->obuffer);
- EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
- this->obuffer.getData(), this->obuffer.getLength(),
- wiredata_nulltxt, sizeof(wiredata_nulltxt));
+ matchWireData(wiredata_nulltxt, sizeof(wiredata_nulltxt),
+ this->obuffer.getData(), this->obuffer.getLength());
+
this->obuffer.clear();
TypeParam(this->lexer, NULL, MasterLoader::MANY_ERRORS, this->loader_cb).
toWire(this->obuffer);
- EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
- this->obuffer.getData(), this->obuffer.getLength(),
- wiredata_nulltxt, sizeof(wiredata_nulltxt));
+ matchWireData(wiredata_nulltxt, sizeof(wiredata_nulltxt),
+ this->obuffer.getData(), this->obuffer.getLength());
+
EXPECT_EQ(MasterToken::END_OF_LINE, this->lexer.getNextToken().getType());
// Longest possible character-string.
this->obuffer.clear();
TypeParam(string(255, 'a')).toWire(this->obuffer);
- EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
- this->obuffer.getData(), this->obuffer.getLength(),
- &this->wiredata_longesttxt[0],
- this->wiredata_longesttxt.size());
+ matchWireData(&this->wiredata_longesttxt[0],
+ this->wiredata_longesttxt.size(),
+ this->obuffer.getData(), this->obuffer.getLength());
+
this->obuffer.clear();
TypeParam(this->lexer, NULL, MasterLoader::MANY_ERRORS, this->loader_cb).
toWire(this->obuffer);
- EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
- this->obuffer.getData(), this->obuffer.getLength(),
- &this->wiredata_longesttxt[0],
- this->wiredata_longesttxt.size());
+ matchWireData(&this->wiredata_longesttxt[0],
+ this->wiredata_longesttxt.size(),
+ this->obuffer.getData(), this->obuffer.getLength());
+
EXPECT_EQ(MasterToken::END_OF_LINE, this->lexer.getNextToken().getType());
// Too long text for a valid character-string.
@@ -269,10 +272,8 @@ TYPED_TEST(Rdata_TXT_LIKE_Test, createFromWire) {
sizeof(wiredata_txt_like));
expected_data.insert(expected_data.end(), wiredata_txt_like,
wiredata_txt_like + sizeof(wiredata_txt_like));
- EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
- this->obuffer.getData(),
- this->obuffer.getLength(),
- &expected_data[0], expected_data.size());
+ matchWireData(&expected_data[0], expected_data.size(),
+ this->obuffer.getData(), this->obuffer.getLength());
// Largest length of data. There's nothing special, but should be
// constructed safely, and the content should be identical to the original
@@ -284,11 +285,8 @@ TYPED_TEST(Rdata_TXT_LIKE_Test, createFromWire) {
TypeParam largest_txt_like(ibuffer, largest_txt_like_data.size());
this->obuffer.clear();
largest_txt_like.toWire(this->obuffer);
- EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
- this->obuffer.getData(),
- this->obuffer.getLength(),
- &largest_txt_like_data[0],
- largest_txt_like_data.size());
+ matchWireData(&largest_txt_like_data[0], largest_txt_like_data.size(),
+ this->obuffer.getData(), this->obuffer.getLength());
// rdlen parameter is out of range. This is a rare event because we'd
// normally call the constructor via a polymorphic wrapper, where the
@@ -316,18 +314,14 @@ TYPED_TEST(Rdata_TXT_LIKE_Test, createFromLexer) {
TYPED_TEST(Rdata_TXT_LIKE_Test, toWireBuffer) {
this->rdata_txt_like.toWire(this->obuffer);
- EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
- this->obuffer.getData(),
- this->obuffer.getLength(),
- wiredata_txt_like, sizeof(wiredata_txt_like));
+ matchWireData(wiredata_txt_like, sizeof(wiredata_txt_like),
+ this->obuffer.getData(), this->obuffer.getLength());
}
TYPED_TEST(Rdata_TXT_LIKE_Test, toWireRenderer) {
this->rdata_txt_like.toWire(this->renderer);
- EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
- this->renderer.getData(),
- this->renderer.getLength(),
- wiredata_txt_like, sizeof(wiredata_txt_like));
+ matchWireData(wiredata_txt_like, sizeof(wiredata_txt_like),
+ this->renderer.getData(), this->renderer.getLength());
}
TYPED_TEST(Rdata_TXT_LIKE_Test, toText) {
diff --git a/src/lib/dns/tests/rdata_unittest.cc b/src/lib/dns/tests/rdata_unittest.cc
index 3839fbd..f29b61c 100644
--- a/src/lib/dns/tests/rdata_unittest.cc
+++ b/src/lib/dns/tests/rdata_unittest.cc
@@ -28,14 +28,17 @@
#include <dns/tests/unittest_util.h>
#include <dns/tests/rdata_unittest.h>
+#include <util/unittests/wiredata.h>
+
#include <boost/bind.hpp>
#include <boost/lexical_cast.hpp>
-using isc::UnitTestUtil;
using namespace std;
using namespace isc::dns;
using namespace isc::util;
using namespace isc::dns::rdata;
+using isc::UnitTestUtil;
+using isc::util::unittests::matchWireData;
namespace isc {
namespace dns {
@@ -211,6 +214,14 @@ TEST_F(RdataTest, createRdataWithLexer) {
"file does not end with newline");
}
+TEST_F(RdataTest, getLength) {
+ const in::AAAA aaaa_rdata("2001:db8::1");
+ EXPECT_EQ(16, aaaa_rdata.getLength());
+
+ const generic::TXT txt_rdata("Hello World");
+ EXPECT_EQ(12, txt_rdata.getLength());
+}
+
}
}
}
@@ -290,14 +301,14 @@ TEST_F(Rdata_Unknown_Test, createFromText) {
// the length should be 16-bit unsigned integer
EXPECT_THROW(generic::Generic("\\# 65536 a1b2c30d"), InvalidRdataLength);
EXPECT_THROW(generic::Generic("\\# -1 a1b2c30d"), InvalidRdataLength);
- EXPECT_THROW(generic::Generic("\\# 1.1 a1"), InvalidRdataText);
+ EXPECT_THROW(generic::Generic("\\# 1.1 a1"), InvalidRdataLength);
EXPECT_THROW(generic::Generic("\\# 0a 00010203040506070809"),
- InvalidRdataText);
+ InvalidRdataLength);
// should reject if the special token is missing.
EXPECT_THROW(generic::Generic("4 a1b2c30d"), InvalidRdataText);
// the special token, the RDLENGTH and the data must be space separated.
EXPECT_THROW(generic::Generic("\\#0"), InvalidRdataText);
- EXPECT_THROW(generic::Generic("\\# 1ff"), InvalidRdataText);
+ EXPECT_THROW(generic::Generic("\\# 1ff"), InvalidRdataLength);
}
TEST_F(Rdata_Unknown_Test, createFromWire) {
@@ -393,16 +404,14 @@ TEST_F(Rdata_Unknown_Test, toText) {
TEST_F(Rdata_Unknown_Test, toWireBuffer) {
rdata_unknown.toWire(obuffer);
- EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
- obuffer.getData(), obuffer.getLength(),
- wiredata_unknown, sizeof(wiredata_unknown));
+ matchWireData(wiredata_unknown, sizeof(wiredata_unknown),
+ obuffer.getData(), obuffer.getLength());
}
TEST_F(Rdata_Unknown_Test, toWireRenderer) {
rdata_unknown.toWire(renderer);
- EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
- renderer.getData(), renderer.getLength(),
- wiredata_unknown, sizeof(wiredata_unknown));
+ matchWireData(wiredata_unknown, sizeof(wiredata_unknown),
+ renderer.getData(), renderer.getLength());
}
TEST_F(Rdata_Unknown_Test, compare) {
diff --git a/src/lib/dns/tests/rdata_unittest.h b/src/lib/dns/tests/rdata_unittest.h
index 04af07c..8c7f954 100644
--- a/src/lib/dns/tests/rdata_unittest.h
+++ b/src/lib/dns/tests/rdata_unittest.h
@@ -27,8 +27,6 @@
#include <string>
#include <sstream>
-using namespace isc::util;
-
namespace isc {
namespace dns {
namespace rdata {
@@ -77,7 +75,7 @@ protected:
}
}
- OutputBuffer obuffer;
+ isc::util::OutputBuffer obuffer;
MessageRenderer renderer;
/// This is an RDATA object of some "unknown" RR type so that it can be
/// used to test the compare() method against a well-known RR type.
diff --git a/src/lib/dns/tests/rdatafields_unittest.cc b/src/lib/dns/tests/rdatafields_unittest.cc
index ef83ed4..a6bead7 100644
--- a/src/lib/dns/tests/rdatafields_unittest.cc
+++ b/src/lib/dns/tests/rdatafields_unittest.cc
@@ -24,14 +24,17 @@
#include <dns/rdatafields.h>
#include <dns/tests/unittest_util.h>
+#include <util/unittests/wiredata.h>
+
#include <gtest/gtest.h>
-using isc::UnitTestUtil;
using namespace std;
using namespace isc::dns;
using namespace isc::dns::rdata;
+using isc::UnitTestUtil;
using isc::util::OutputBuffer;
using isc::util::InputBuffer;
+using isc::util::unittests::matchWireData;
namespace {
class RdataFieldsTest : public ::testing::Test {
@@ -67,23 +70,21 @@ RdataFieldsTest::constructCommonTests(const RdataFields& fields,
const uint8_t* const expected_data,
const size_t expected_data_len)
{
- EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, expected_data,
- expected_data_len, fields.getData(),
- fields.getDataLength());
+ matchWireData(expected_data, expected_data_len,
+ fields.getData(), fields.getDataLength());
+
EXPECT_EQ(sizeof(RdataFields::FieldSpec), fields.getFieldSpecDataSize());
EXPECT_EQ(1, fields.getFieldCount());
EXPECT_EQ(RdataFields::DATA, fields.getFieldSpec(0).type);
EXPECT_EQ(4, fields.getFieldSpec(0).len);
fields.toWire(obuffer);
- EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, expected_data,
- expected_data_len, obuffer.getData(),
- obuffer.getLength());
+ matchWireData(expected_data, expected_data_len,
+ obuffer.getData(), obuffer.getLength());
fields.toWire(renderer);
- EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, expected_data,
- expected_data_len, renderer.getData(),
- renderer.getLength());
+ matchWireData(expected_data, expected_data_len,
+ renderer.getData(), renderer.getLength());
}
TEST_F(RdataFieldsTest, constructFromRdata) {
@@ -112,17 +113,15 @@ RdataFieldsTest::constructCommonTestsNS(const RdataFields& fields) {
UnitTestUtil::readWireData("rdatafields1.wire", expected_wire);
other_name.toWire(obuffer);
fields.toWire(obuffer);
- EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, &expected_wire[0],
- expected_wire.size(), obuffer.getData(),
- obuffer.getLength());
+ matchWireData(&expected_wire[0], expected_wire.size(),
+ obuffer.getData(), obuffer.getLength());
expected_wire.clear();
UnitTestUtil::readWireData("rdatafields2.wire", expected_wire);
other_name.toWire(renderer);
fields.toWire(renderer);
- EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, &expected_wire[0],
- expected_wire.size(), renderer.getData(),
- renderer.getLength());
+ matchWireData(&expected_wire[0], expected_wire.size(),
+ renderer.getData(), renderer.getLength());
}
TEST_F(RdataFieldsTest, constructFromRdataNS) {
@@ -150,14 +149,12 @@ RdataFieldsTest::constructCommonTestsTXT(const RdataFields& fields) {
EXPECT_EQ(expected_wire.size(), fields.getFieldSpec(0).len);
fields.toWire(obuffer);
- EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, &expected_wire[0],
- expected_wire.size(), obuffer.getData(),
- obuffer.getLength());
+ matchWireData(&expected_wire[0], expected_wire.size(),
+ obuffer.getData(), obuffer.getLength());
fields.toWire(renderer);
- EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, &expected_wire[0],
- expected_wire.size(), renderer.getData(),
- renderer.getLength());
+ matchWireData(&expected_wire[0], expected_wire.size(),
+ renderer.getData(), renderer.getLength());
}
TEST_F(RdataFieldsTest, constructFromRdataTXT) {
@@ -208,9 +205,8 @@ RdataFieldsTest::constructCommonTestsRRSIG(const RdataFields& fields) {
obuffer.writeUint16(fields.getDataLength());
fields.toWire(obuffer);
other_name.toWire(obuffer);
- EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, &expected_wire[0],
- expected_wire.size(), obuffer.getData(),
- obuffer.getLength());
+ matchWireData(&expected_wire[0], expected_wire.size(),
+ obuffer.getData(), obuffer.getLength());
expected_wire.clear();
UnitTestUtil::readWireData("rdatafields6.wire", expected_wire);
@@ -218,9 +214,8 @@ RdataFieldsTest::constructCommonTestsRRSIG(const RdataFields& fields) {
renderer.writeUint16(fields.getDataLength());
fields.toWire(renderer); // the signer field won't be compressed
other_name.toWire(renderer); // but will be used as a compression target
- EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, &expected_wire[0],
- expected_wire.size(), renderer.getData(),
- renderer.getLength());
+ matchWireData(&expected_wire[0], expected_wire.size(),
+ renderer.getData(), renderer.getLength());
}
TEST_F(RdataFieldsTest, constructFromRdataRRSIG) {
diff --git a/src/lib/dns/tests/rrclass_unittest.cc b/src/lib/dns/tests/rrclass_unittest.cc
index b79aedf..c20125e 100644
--- a/src/lib/dns/tests/rrclass_unittest.cc
+++ b/src/lib/dns/tests/rrclass_unittest.cc
@@ -19,6 +19,7 @@
#include <dns/rrclass.h>
#include <dns/tests/unittest_util.h>
+#include <util/unittests/wiredata.h>
#include <boost/scoped_ptr.hpp>
@@ -27,6 +28,7 @@ using namespace isc;
using namespace isc::dns;
using namespace isc::util;
using boost::scoped_ptr;
+using isc::util::unittests::matchWireData;
namespace {
class RRClassTest : public ::testing::Test {
@@ -115,9 +117,8 @@ TEST_F(RRClassTest, toWireBuffer) {
rrclass_0x8000.toWire(obuffer);
rrclass_max.toWire(obuffer);
- EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
- obuffer.getData(), obuffer.getLength(),
- wiredata, sizeof(wiredata));
+ matchWireData(wiredata, sizeof (wiredata),
+ obuffer.getData(), obuffer.getLength());
}
TEST_F(RRClassTest, toWireRenderer) {
@@ -127,9 +128,8 @@ TEST_F(RRClassTest, toWireRenderer) {
rrclass_0x8000.toWire(renderer);
rrclass_max.toWire(renderer);
- EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
- renderer.getData(), renderer.getLength(),
- wiredata, sizeof(wiredata));
+ matchWireData(wiredata, sizeof (wiredata),
+ renderer.getData(), renderer.getLength());
}
TEST_F(RRClassTest, wellKnownClasss) {
diff --git a/src/lib/dns/tests/rrset_unittest.cc b/src/lib/dns/tests/rrset_unittest.cc
index da3eb52..fc75b77 100644
--- a/src/lib/dns/tests/rrset_unittest.cc
+++ b/src/lib/dns/tests/rrset_unittest.cc
@@ -23,18 +23,19 @@
#include <dns/rrset.h>
#include <dns/tests/unittest_util.h>
+#include <util/unittests/wiredata.h>
#include <gtest/gtest.h>
#include <stdexcept>
#include <sstream>
-using isc::UnitTestUtil;
-
using namespace std;
using namespace isc::dns;
using namespace isc::util;
using namespace isc::dns::rdata;
+using isc::UnitTestUtil;
+using isc::util::unittests::matchWireData;
namespace {
class RRsetTest : public ::testing::Test {
@@ -130,7 +131,7 @@ TEST_F(RRsetTest, isSameKind) {
void
addRdataTestCommon(const RRset& rrset) {
- EXPECT_EQ(2, rrset.getRdataCount());
+ ASSERT_EQ(2, rrset.getRdataCount());
RdataIteratorPtr it = rrset.getRdataIterator(); // cursor is set to the 1st
EXPECT_FALSE(it->isLast());
@@ -157,14 +158,34 @@ TEST_F(RRsetTest, addRdataPtr) {
rrset_a_empty.addRdata(createRdata(rrset_a_empty.getType(),
rrset_a_empty.getClass(),
"192.0.2.2"));
+ addRdataTestCommon(rrset_a_empty);
+}
- addRdataTestCommon(rrset_a);
-
+TEST_F(RRsetTest, addRdataPtrMismatched) {
// Pointer version of addRdata() doesn't type check and does allow to
//add a different type of Rdata as a result.
+
+ // Type mismatch
rrset_a_empty.addRdata(createRdata(RRType::NS(), RRClass::IN(),
"ns.example.com."));
- EXPECT_EQ(3, rrset_a_empty.getRdataCount());
+ EXPECT_EQ(1, rrset_a_empty.getRdataCount());
+
+ // Class mismatch
+ rrset_ch_txt.addRdata(createRdata(RRType::TXT(), RRClass::IN(),
+ "Test String"));
+ EXPECT_EQ(1, rrset_ch_txt.getRdataCount());
+}
+
+TEST_F(RRsetTest, addRdataString) {
+ rrset_a_empty.addRdata("192.0.2.1");
+ rrset_a_empty.addRdata("192.0.2.2");
+
+ addRdataTestCommon(rrset_a_empty);
+
+ // String version of addRdata() will throw for bad RDATA for
+ // RRType::A().
+ EXPECT_THROW(rrset_a_empty.addRdata("ns.example.com."), InvalidRdataText);
+ addRdataTestCommon(rrset_a_empty);
}
TEST_F(RRsetTest, iterator) {
@@ -204,12 +225,36 @@ TEST_F(RRsetTest, toText) {
rrset_none_a_empty.toText());
}
+TEST_F(RRsetTest, getLength) {
+ // Empty RRset should throw
+ EXPECT_THROW(rrset_a_empty.getLength(), EmptyRRset);
+
+ // Unless it is type ANY or NONE:
+ // test.example.com = 1 + 4 + 1 + 7 + 1 + 3 + 1 = 18 octets
+ // TYPE field = 2 octets
+ // CLASS field = 2 octets
+ // TTL field = 4 octets
+ // RDLENGTH field = 2 octets
+ // Total = 18 + 2 + 2 + 4 + 2 = 28 octets
+ EXPECT_EQ(28, rrset_any_a_empty.getLength());
+ EXPECT_EQ(28, rrset_none_a_empty.getLength());
+
+ // RRset with single RDATA
+ // 28 (above) + 4 octets (A RDATA) = 32 octets
+ rrset_a_empty.addRdata(in::A("192.0.2.1"));
+ EXPECT_EQ(32, rrset_a_empty.getLength());
+
+ // 2 A RRs
+ rrset_a_empty.addRdata(in::A("192.0.2.2"));
+ EXPECT_EQ(32 + 32, rrset_a_empty.getLength());
+}
+
TEST_F(RRsetTest, toWireBuffer) {
rrset_a.toWire(buffer);
UnitTestUtil::readWireData("rrset_toWire1", wiredata);
- EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, buffer.getData(),
- buffer.getLength(), &wiredata[0], wiredata.size());
+ matchWireData(&wiredata[0], wiredata.size(),
+ buffer.getData(), buffer.getLength());
// toWire() cannot be performed for an empty RRset except when
// class=ANY or class=NONE.
@@ -222,14 +267,15 @@ TEST_F(RRsetTest, toWireBuffer) {
rrset_any_a_empty.toWire(buffer);
wiredata.clear();
UnitTestUtil::readWireData("rrset_toWire3", wiredata);
- EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, buffer.getData(),
- buffer.getLength(), &wiredata[0], wiredata.size());
+ matchWireData(&wiredata[0], wiredata.size(),
+ buffer.getData(), buffer.getLength());
+
buffer.clear();
rrset_none_a_empty.toWire(buffer);
wiredata.clear();
UnitTestUtil::readWireData("rrset_toWire4", wiredata);
- EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, buffer.getData(),
- buffer.getLength(), &wiredata[0], wiredata.size());
+ matchWireData(&wiredata[0], wiredata.size(),
+ buffer.getData(), buffer.getLength());
}
TEST_F(RRsetTest, toWireRenderer) {
@@ -239,8 +285,8 @@ TEST_F(RRsetTest, toWireRenderer) {
rrset_ns.toWire(renderer);
UnitTestUtil::readWireData("rrset_toWire2", wiredata);
- EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, renderer.getData(),
- renderer.getLength(), &wiredata[0], wiredata.size());
+ matchWireData(&wiredata[0], wiredata.size(),
+ renderer.getData(), renderer.getLength());
// toWire() cannot be performed for an empty RRset except when
// class=ANY or class=NONE.
@@ -253,15 +299,15 @@ TEST_F(RRsetTest, toWireRenderer) {
rrset_any_a_empty.toWire(renderer);
wiredata.clear();
UnitTestUtil::readWireData("rrset_toWire3", wiredata);
- EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, renderer.getData(),
- renderer.getLength(), &wiredata[0], wiredata.size());
+ matchWireData(&wiredata[0], wiredata.size(),
+ renderer.getData(), renderer.getLength());
renderer.clear();
rrset_none_a_empty.toWire(renderer);
wiredata.clear();
UnitTestUtil::readWireData("rrset_toWire4", wiredata);
- EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, renderer.getData(),
- renderer.getLength(), &wiredata[0], wiredata.size());
+ matchWireData(&wiredata[0], wiredata.size(),
+ renderer.getData(), renderer.getLength());
}
// test operator<<. We simply confirm it appends the result of toText().
@@ -365,4 +411,38 @@ TEST_F(RRsetRRSIGTest, toText) {
"20100322084538 20100220084538 1 example.com. FAKEFAKEFAKEFAKE\n",
rrset_aaaa->toText());
}
+
+TEST_F(RRsetRRSIGTest, getLength) {
+ // A RR
+ // test.example.com = 1 + 4 + 1 + 7 + 1 + 3 + 1 = 18 octets
+ // TYPE field = 2 octets
+ // CLASS field = 2 octets
+ // TTL field = 4 octets
+ // RDLENGTH field = 2 octets
+ // A RDATA = 4 octets
+ // Total = 18 + 2 + 2 + 4 + 2 + 4 = 32 octets
+
+ // 2 A RRs
+ EXPECT_EQ(32 + 32, rrset_a->getLength());
+
+ // RRSIG
+ // test.example.com = 1 + 4 + 1 + 7 + 1 + 3 + 1 = 18 octets
+ // TYPE field = 2 octets
+ // CLASS field = 2 octets
+ // TTL field = 4 octets
+ // RDLENGTH field = 2 octets
+ // RRSIG RDATA = 40 octets
+ // Total = 18 + 2 + 2 + 4 + 2 + 40 = 68 octets
+ RRsetPtr my_rrsig(new RRset(test_name, RRClass::IN(),
+ RRType::RRSIG(), RRTTL(3600)));
+ my_rrsig->addRdata(generic::RRSIG("A 4 3 3600 "
+ "20000101000000 20000201000000 "
+ "12345 example.com. FAKEFAKEFAKE"));
+ EXPECT_EQ(68, my_rrsig->getLength());
+
+ // RRset with attached RRSIG
+ rrset_a->addRRsig(my_rrsig);
+
+ EXPECT_EQ(32 + 32 + 68, rrset_a->getLength());
+}
}
diff --git a/src/lib/dns/tests/rrttl_unittest.cc b/src/lib/dns/tests/rrttl_unittest.cc
index c849c44..23b7c2d 100644
--- a/src/lib/dns/tests/rrttl_unittest.cc
+++ b/src/lib/dns/tests/rrttl_unittest.cc
@@ -19,6 +19,7 @@
#include <dns/rrttl.h>
#include <dns/tests/unittest_util.h>
+#include <util/unittests/wiredata.h>
#include <boost/scoped_ptr.hpp>
@@ -27,6 +28,7 @@ using namespace isc;
using namespace isc::dns;
using namespace isc::util;
using boost::scoped_ptr;
+using isc::util::unittests::matchWireData;
namespace {
class RRTTLTest : public ::testing::Test {
@@ -193,9 +195,8 @@ TEST_F(RRTTLTest, toWireBuffer) {
ttl_32bit.toWire(obuffer);
ttl_max.toWire(obuffer);
- EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
- obuffer.getData(), obuffer.getLength(),
- wiredata, sizeof(wiredata));
+ matchWireData(wiredata, sizeof(wiredata),
+ obuffer.getData(), obuffer.getLength());
}
TEST_F(RRTTLTest, toWireRenderer) {
@@ -205,9 +206,8 @@ TEST_F(RRTTLTest, toWireRenderer) {
ttl_32bit.toWire(renderer);
ttl_max.toWire(renderer);
- EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
- renderer.getData(), renderer.getLength(),
- wiredata, sizeof(wiredata));
+ matchWireData(wiredata, sizeof(wiredata),
+ renderer.getData(), renderer.getLength());
}
TEST_F(RRTTLTest, equal) {
diff --git a/src/lib/dns/tests/rrtype_unittest.cc b/src/lib/dns/tests/rrtype_unittest.cc
index ee302a1..84d52a9 100644
--- a/src/lib/dns/tests/rrtype_unittest.cc
+++ b/src/lib/dns/tests/rrtype_unittest.cc
@@ -19,11 +19,13 @@
#include <dns/rrtype.h>
#include <dns/tests/unittest_util.h>
+#include <util/unittests/wiredata.h>
using namespace std;
using namespace isc;
using namespace isc::dns;
using namespace isc::util;
+using isc::util::unittests::matchWireData;
namespace {
class RRTypeTest : public ::testing::Test {
@@ -107,9 +109,8 @@ TEST_F(RRTypeTest, toWireBuffer) {
rrtype_0x8000.toWire(obuffer);
rrtype_max.toWire(obuffer);
- EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
- obuffer.getData(), obuffer.getLength(),
- wiredata, sizeof(wiredata));
+ matchWireData(wiredata, sizeof(wiredata),
+ obuffer.getData(), obuffer.getLength());
}
TEST_F(RRTypeTest, toWireRenderer) {
@@ -119,9 +120,8 @@ TEST_F(RRTypeTest, toWireRenderer) {
rrtype_0x8000.toWire(renderer);
rrtype_max.toWire(renderer);
- EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
- renderer.getData(), renderer.getLength(),
- wiredata, sizeof(wiredata));
+ matchWireData(wiredata, sizeof(wiredata),
+ renderer.getData(), renderer.getLength());
}
TEST_F(RRTypeTest, wellKnownTypes) {
diff --git a/src/lib/dns/tests/tsig_unittest.cc b/src/lib/dns/tests/tsig_unittest.cc
index 458a6e0..6348e9c 100644
--- a/src/lib/dns/tests/tsig_unittest.cc
+++ b/src/lib/dns/tests/tsig_unittest.cc
@@ -40,6 +40,7 @@
#include <dns/tsigrecord.h>
#include <dns/tests/unittest_util.h>
+#include <util/unittests/wiredata.h>
using namespace std;
using namespace isc;
@@ -48,6 +49,7 @@ using namespace isc::util;
using namespace isc::util::encode;
using namespace isc::dns::rdata;
using isc::UnitTestUtil;
+using isc::util::unittests::matchWireData;
// See dnssectime.cc
namespace isc {
@@ -188,7 +190,7 @@ TSIGTest::createMessageAndSign(uint16_t id, const Name& qname,
(ctx->getState() == TSIGContext::INIT) ?
TSIGContext::SENT_REQUEST : TSIGContext::SENT_RESPONSE;
- message.toWire(renderer, *ctx);
+ message.toWire(renderer, ctx);
message.clear(Message::PARSE);
InputBuffer buffer(renderer.getData(), renderer.getLength());
@@ -224,15 +226,14 @@ commonSignChecks(ConstTSIGRecordPtr tsig, uint16_t expected_qid,
EXPECT_EQ(expected_timesigned, tsig_rdata.getTimeSigned());
EXPECT_EQ(300, tsig_rdata.getFudge());
EXPECT_EQ(expected_maclen, tsig_rdata.getMACSize());
- EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
- tsig_rdata.getMAC(), tsig_rdata.getMACSize(),
- expected_mac, expected_maclen);
+ matchWireData(expected_mac, expected_maclen,
+ tsig_rdata.getMAC(), tsig_rdata.getMACSize());
+
EXPECT_EQ(expected_qid, tsig_rdata.getOriginalID());
EXPECT_EQ(expected_error, tsig_rdata.getError());
EXPECT_EQ(expected_otherlen, tsig_rdata.getOtherLen());
- EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
- tsig_rdata.getOtherData(), tsig_rdata.getOtherLen(),
- expected_otherdata, expected_otherlen);
+ matchWireData(expected_otherdata, expected_otherlen,
+ tsig_rdata.getOtherData(), tsig_rdata.getOtherLen());
}
void
diff --git a/src/lib/dns/tests/tsigkey_unittest.cc b/src/lib/dns/tests/tsigkey_unittest.cc
index eaf4040..8a9d86f 100644
--- a/src/lib/dns/tests/tsigkey_unittest.cc
+++ b/src/lib/dns/tests/tsigkey_unittest.cc
@@ -23,10 +23,12 @@
#include <dns/tsigkey.h>
#include <dns/tests/unittest_util.h>
+#include <util/unittests/wiredata.h>
using namespace std;
using namespace isc::dns;
using isc::UnitTestUtil;
+using isc::util::unittests::matchWireData;
namespace {
class TSIGKeyTest : public ::testing::Test {
@@ -72,15 +74,16 @@ TEST_F(TSIGKeyTest, construct) {
secret.c_str(), secret.size());
EXPECT_EQ(key_name, key.getKeyName());
EXPECT_EQ(Name("hmac-md5.sig-alg.reg.int"), key.getAlgorithmName());
- EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, secret.c_str(),
- secret.size(), key.getSecret(), key.getSecretLength());
+ matchWireData(secret.c_str(), secret.size(),
+ key.getSecret(), key.getSecretLength());
TSIGKey key_short_md5(key_name, TSIGKey::HMACMD5_SHORT_NAME(),
secret.c_str(), secret.size());
- EXPECT_EQ(key_name, key.getKeyName());
- EXPECT_EQ(Name("hmac-md5.sig-alg.reg.int"), key.getAlgorithmName());
- EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, secret.c_str(),
- secret.size(), key.getSecret(), key.getSecretLength());
+ EXPECT_EQ(key_name, key_short_md5.getKeyName());
+ EXPECT_EQ(Name("hmac-md5.sig-alg.reg.int"),
+ key_short_md5.getAlgorithmName());
+ matchWireData(secret.c_str(), secret.size(),
+ key_short_md5.getSecret(), key_short_md5.getSecretLength());
// "unknown" algorithm is only accepted with empty secret.
EXPECT_THROW(TSIGKey(key_name, Name("unknown-alg"),
@@ -113,9 +116,8 @@ void
compareTSIGKeys(const TSIGKey& expect, const TSIGKey& actual) {
EXPECT_EQ(expect.getKeyName(), actual.getKeyName());
EXPECT_EQ(expect.getAlgorithmName(), actual.getAlgorithmName());
- EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
- expect.getSecret(), expect.getSecretLength(),
- actual.getSecret(), actual.getSecretLength());
+ matchWireData(expect.getSecret(), expect.getSecretLength(),
+ actual.getSecret(), actual.getSecretLength());
}
TEST_F(TSIGKeyTest, copyConstruct) {
@@ -249,9 +251,8 @@ TEST_F(TSIGKeyRingTest, find) {
EXPECT_EQ(TSIGKeyRing::SUCCESS, result1.code);
EXPECT_EQ(key_name, result1.key->getKeyName());
EXPECT_EQ(TSIGKey::HMACSHA256_NAME(), result1.key->getAlgorithmName());
- EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, secret, secret_len,
- result1.key->getSecret(),
- result1.key->getSecretLength());
+ matchWireData(secret, secret_len,
+ result1.key->getSecret(), result1.key->getSecretLength());
// If either key name or algorithm doesn't match, search should fail.
const TSIGKeyRing::FindResult result2 =
@@ -268,9 +269,8 @@ TEST_F(TSIGKeyRingTest, find) {
EXPECT_EQ(TSIGKeyRing::SUCCESS, result4.code);
EXPECT_EQ(key_name, result4.key->getKeyName());
EXPECT_EQ(TSIGKey::HMACSHA256_NAME(), result4.key->getAlgorithmName());
- EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, secret, secret_len,
- result4.key->getSecret(),
- result4.key->getSecretLength());
+ matchWireData(secret, secret_len,
+ result4.key->getSecret(), result4.key->getSecretLength());
}
TEST_F(TSIGKeyRingTest, findFromSome) {
diff --git a/src/lib/dns/tests/tsigrecord_unittest.cc b/src/lib/dns/tests/tsigrecord_unittest.cc
index 532681a..9c08fe0 100644
--- a/src/lib/dns/tests/tsigrecord_unittest.cc
+++ b/src/lib/dns/tests/tsigrecord_unittest.cc
@@ -29,12 +29,14 @@
#include <dns/tsigrecord.h>
#include <dns/tests/unittest_util.h>
+#include <util/unittests/wiredata.h>
using namespace std;
using namespace isc::util;
using namespace isc::dns;
using namespace isc::dns::rdata;
using isc::UnitTestUtil;
+using isc::util::unittests::matchWireData;
namespace {
class TSIGRecordTest : public ::testing::Test {
@@ -108,16 +110,14 @@ TEST_F(TSIGRecordTest, fromParams) {
TEST_F(TSIGRecordTest, recordToWire) {
UnitTestUtil::readWireData("tsigrecord_toWire1.wire", data);
EXPECT_EQ(1, test_record.toWire(renderer));
- EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
- renderer.getData(), renderer.getLength(),
- &data[0], data.size());
+ matchWireData(&data[0], data.size(),
+ renderer.getData(), renderer.getLength());
// Same test for a dumb buffer
buffer.clear();
EXPECT_EQ(1, test_record.toWire(buffer));
- EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
- buffer.getData(), buffer.getLength(),
- &data[0], data.size());
+ matchWireData(&data[0], data.size(),
+ buffer.getData(), buffer.getLength());
}
TEST_F(TSIGRecordTest, recordToOLongToWire) {
@@ -139,9 +139,8 @@ TEST_F(TSIGRecordTest, recordToWireAfterNames) {
renderer.writeName(TSIGKey::HMACMD5_NAME());
renderer.writeName(Name("foo.example.com"));
EXPECT_EQ(1, test_record.toWire(renderer));
- EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
- renderer.getData(), renderer.getLength(),
- &data[0], data.size());
+ matchWireData(&data[0], data.size(),
+ renderer.getData(), renderer.getLength());
}
TEST_F(TSIGRecordTest, toText) {
diff --git a/src/lib/dns/tests/unittest_util.cc b/src/lib/dns/tests/unittest_util.cc
index 66d49a8..bf4c8cb 100644
--- a/src/lib/dns/tests/unittest_util.cc
+++ b/src/lib/dns/tests/unittest_util.cc
@@ -132,33 +132,6 @@ UnitTestUtil::readWireData(const string& datastr,
}
::testing::AssertionResult
-UnitTestUtil::matchWireData(const char*, const char*, const char*, const char*,
- const void* data1, size_t len1,
- const void* data2, size_t len2)
-{
- ::testing::Message msg;
- size_t cmplen = min(len1, len2);
-
- for (size_t i = 0; i < cmplen; i++) {
- int ch1 = static_cast<const uint8_t*>(data1)[i];
- int ch2 = static_cast<const uint8_t*>(data2)[i];
- if (ch1 != ch2) {
- msg << "Wire data mismatch at " << i << "th byte\n"
- << " Actual: " << ch1 << "\n"
- << "Expected: " << ch2 << "\n";
- return (::testing::AssertionFailure(msg));
- }
- }
- if (len1 != len2) {
- msg << "Wire data mismatch in length:\n"
- << " Actual: " << len1 << "\n"
- << "Expected: " << len2 << "\n";
- return (::testing::AssertionFailure(msg));
- }
- return (::testing::AssertionSuccess());
-}
-
-::testing::AssertionResult
UnitTestUtil::matchName(const char*, const char*,
const isc::dns::Name& name1,
const isc::dns::Name& name2)
diff --git a/src/lib/dns/tests/unittest_util.h b/src/lib/dns/tests/unittest_util.h
index f50df14..1d65f8d 100644
--- a/src/lib/dns/tests/unittest_util.h
+++ b/src/lib/dns/tests/unittest_util.h
@@ -46,28 +46,6 @@ public:
std::vector<unsigned char>& data);
///
- /// Compare len1 bytes of data1 with len2 bytes of data2 as binary data.
- ///
- /// If they don't match report the point of mismatch in the google test
- /// format. This method is expected to be used from the EXPECT_PRED_FORMAT4
- /// macro of google test as follows:
- /// \code EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
- /// actual_data, actual_data_len,
- /// expected_data, expected_data_len); \endcode
- /// Parameters from dataexp1 to lenexp2 are passed via the macro but will
- /// be ignored by this method.
- /// Note: newer versions of google test supports the direct use of
- /// AssertionResult with the EXPECT_TRUE macro, which would be more
- /// intuitive, but to be as compatible as possible we use the more primitive
- /// macro, i.e., EXPECT_PRED_FORMAT4.
- ///
- static ::testing::AssertionResult
- matchWireData(const char* dataexp1, const char* lenexp1,
- const char* dataexp2, const char* lenexp2,
- const void* data1, size_t len1,
- const void* data2, size_t len2);
-
- ///
/// Compare two names.
///
/// This check method uses \c Name::compare() for comparison, which performs
diff --git a/src/lib/dns/tsig.cc b/src/lib/dns/tsig.cc
index d7ffcf8..b6e1716 100644
--- a/src/lib/dns/tsig.cc
+++ b/src/lib/dns/tsig.cc
@@ -79,7 +79,7 @@ struct TSIGContext::TSIGContextImpl {
key_.getSecret(), key_.getSecretLength(),
key_.getAlgorithm()),
deleteHMAC);
- } catch (const Exception&) {
+ } catch (const isc::Exception&) {
return;
}
digest_len_ = hmac_->getOutputLength();
@@ -289,20 +289,20 @@ TSIGContext::getTSIGLength() const {
//
// The space required for an TSIG record is:
//
- // n1 bytes for the (key) name
- // 2 bytes for the type
- // 2 bytes for the class
- // 4 bytes for the ttl
- // 2 bytes for the rdlength
- // n2 bytes for the algorithm name
- // 6 bytes for the time signed
- // 2 bytes for the fudge
- // 2 bytes for the MAC size
- // x bytes for the MAC
- // 2 bytes for the original id
- // 2 bytes for the error
- // 2 bytes for the other data length
- // y bytes for the other data (at most)
+ // n1 bytes for the (key) name
+ // 2 bytes for the type
+ // 2 bytes for the class
+ // 4 bytes for the ttl
+ // 2 bytes for the rdlength
+ // n2 bytes for the algorithm name
+ // 6 bytes for the time signed
+ // 2 bytes for the fudge
+ // 2 bytes for the MAC size
+ // x bytes for the MAC
+ // 2 bytes for the original id
+ // 2 bytes for the error
+ // 2 bytes for the other data length
+ // y bytes for the other data (at most)
// ---------------------------------
// 26 + n1 + n2 + x + y bytes
//
diff --git a/src/lib/dns/tsigkey.cc b/src/lib/dns/tsigkey.cc
index 24a6f57..862d300 100644
--- a/src/lib/dns/tsigkey.cc
+++ b/src/lib/dns/tsigkey.cc
@@ -141,7 +141,7 @@ TSIGKey::TSIGKey(const std::string& str) : impl_(NULL) {
impl_ = new TSIGKeyImpl(Name(keyname_str), algo_name, algorithm,
secret.empty() ? NULL : &secret[0],
secret.size());
- } catch (const Exception& e) {
+ } catch (const isc::Exception& e) {
// 'reduce' the several types of exceptions name parsing and
// Base64 decoding can throw to just the InvalidParameter
isc_throw(InvalidParameter, e.what());
diff --git a/src/lib/hooks/tests/Makefile.am b/src/lib/hooks/tests/Makefile.am
index 36b6287..65153e2 100644
--- a/src/lib/hooks/tests/Makefile.am
+++ b/src/lib/hooks/tests/Makefile.am
@@ -36,6 +36,7 @@ if HAVE_GTEST
# (see http://www.gnu.org/software/libtool/manual/html_node/Link-mode.html).
# Use of these switches will guarantee that the .so files are created in the
# .libs folder and they can be dlopened.
+#
# Note that the shared libraries with callouts should not be used together with
# the --enable-static-link option. With this option, the bind10 libraries are
# statically linked with the program and if the callout invokes the methods
@@ -44,8 +45,9 @@ if HAVE_GTEST
# to unexpected errors. For this reason, the --enable-static-link option is
# ignored for unit tests built here.
-lib_LTLIBRARIES = libnvl.la libivl.la libfxl.la libbcl.la liblcl.la liblecl.la \
- libucl.la libfcl.la
+nodistdir=$(abs_top_builddir)/src/lib/hooks/tests
+nodist_LTLIBRARIES = libnvl.la libivl.la libfxl.la libbcl.la liblcl.la \
+ liblecl.la libucl.la libfcl.la
# No version function
libnvl_la_SOURCES = no_version_library.cc
@@ -124,14 +126,16 @@ run_unittests_LDADD = $(AM_LDADD) $(GTEST_LDADD)
run_unittests_LDADD += $(ALL_LIBS)
run_unittests_LDADD += $(top_builddir)/src/lib/util/unittests/libutil_unittests.la
-# As noted in configure.ac, libtool doesn't work perfectly with Darwin: it embeds the
-# final install path in dynamic libraries and loadable modules refer to that path even
-# if its loaded within the source tree, so preventing tests from working - but only
-# when linking statically. The solution used in other Makefiles (setting the path
-# to the dynamic libraries via an environment variable) don't seem to work. What does
-# work is to run the unit test using libtool and specifying paths via -dlopen switches.
-# So... If running in an environment where we have to set the library path AND if
-# linking statically, override the "check" target and run the unit tests ourselves.
+# As noted in configure.ac, libtool doesn't work perfectly with Darwin: it
+# embeds the final install path in dynamic libraries and loadable modules refer
+# to that path even if its loaded within the source tree, so preventing tests
+# from working - but only when linking statically. The solution used in other
+# Makefiles (setting the path to the dynamic libraries via an environment
+# variable) don't seem to work. What does work is to run the unit test using
+# libtool and specifying paths via -dlopen switches. So... If running in an
+# environment where we have to set the library path AND if linking statically,
+# override the "check" target and run the unit tests ourselves.
+
if USE_STATIC_LINK
if SET_ENV_LIBRARY_PATH
check-TESTS:
diff --git a/src/lib/log/Makefile.am b/src/lib/log/Makefile.am
index 0bd1b05..04b90d1 100644
--- a/src/lib/log/Makefile.am
+++ b/src/lib/log/Makefile.am
@@ -32,7 +32,7 @@ libb10_log_la_SOURCES += message_types.h
libb10_log_la_SOURCES += output_option.cc output_option.h
libb10_log_la_SOURCES += buffer_appender_impl.cc buffer_appender_impl.h
-EXTRA_DIST = README
+EXTRA_DIST = logging.dox
EXTRA_DIST += logimpl_messages.mes
EXTRA_DIST += log_messages.mes
diff --git a/src/lib/log/README b/src/lib/log/README
deleted file mode 100644
index 2a7af28..0000000
--- a/src/lib/log/README
+++ /dev/null
@@ -1,529 +0,0 @@
-This directory holds the first release of the logging system.
-
-
-Basic Ideas
-===========
-The BIND-10 logging system merges two ideas:
-
-* A hierarchical logging system similar to that used in Java (i.e. log4j)
-* Separation of message use from message text
-
-
-Hierarchical Logging System
-===========================
-When a program writes a message to the logging system, it does so using an
-instance of the Logger class. As well as performing the write of the message,
-the logger identifies the source of the message: different sources can write
-to different destinations and can log different severities of messages.
-For example, the "cache" logger could write messages of DEBUG severity or
-above to a file while all other components write messages of "INFO" severity
-or above to the Syslog file.
-
-The loggers are hierarchical in that each logger is the child of another
-logger. The top of the hierarchy is the root logger, which does not have
-a parent. The point of the hierarchy is that unless a logger is explicitly
-assigned an attribute (such as severity of message being logger), it picks
-it up from the parent. (In BIND-10, there is the root logger (named after
-the program) and every other logger is a child of that.) So in the example
-above, the INFO/Syslog attributes could be associated with the root logger
-while the DEBUG/file attributes are associated with the "cache" logger.
-
-
-Separation of Messages Use from Message Text
-============================================
-By separating the use of the message from the text associated with this -
-in essence, defining message text in an external file - it is possible to
-replace the supplied text of the messages with a local language version.
-
-Each message is identified by an identifier e.g. "LOG_WRITE_ERROR".
-Within the program, this is the symbol passed to the logging system.
-The logger system uses the symbol as an index into a dictionary to
-retrieve the message associated with it (e.g. "unable to open %s for
-input"). It then substitutes any message parameters (in this example,
-the name of the file where the write operation failed) and logs it to
-the destination.
-
-In BIND-10, a the default text for each message is linked into the
-program. Each program is able to read a locally-defined message file
-when it starts, updating the stored definitions with site-specific text.
-When the message is logged, the updated text is output. However, the
-message identifier is always included in the output so that the origin
-of the message can be identified even if the text has been changed.
-
-
-Using The System
-================
-The steps in using the system are:
-
-1. Create a message file. This defines messages by an identification - a
- mnemonic for the message, the convention being that these are a few
- words separated by underscores - and text that explains the message in
- more detail. The file is described in more detail below.
-
- Ideally the file should have a file type of ".mes".
-
-2. Run it through the message compiler to produce the .h and .cc files. This
- step should be included in the build process. (For an example of how to
- do this, see src/lib/nsas/Makefile.am.) During development though, the
- message compiler (found in the "src/lib/log/compiler" directory) will need
- to be run manually. The only argument is the name of the message file: it
- will produce as output two files in the default directory, having the same
- name as the input file but with file types of ".h" and ".cc".
-
-3. Include the .h file in your source code to define message symbols, and
- make sure that the .cc file is compiled and linked into your program -
- static initialization will add the symbols to the global dictionary.
-
-4. Declare loggers in your code and use them to log messages. This is
- described in more detail below.
-
-5. To set the debug level and run-time message file, call initLogger (declared
- in logger_support.h) in the main program unit.
-
-
-Message Files
-=============
-
-File Contents and Format
-------------------------
-A message file is a file containing message definitions. Typically there
-will be one message file for each component that declares message symbols.
-An example file could be:
-
--- BEGIN --
-
-# Example message file
-
-$NAMESPACE isc::log
-
-% LOG_UNRECOGNISED_DIRECTIVE line %1: unrecognised directive '%2'
-A line starting with a dollar symbol was found, but the first word on the line
-(shown in the message) was not a recognised message compiler directive.
-
-% LOG_WRITE_ERROR error writing to %1: %2
-The specified error was encountered by the message compiler when writing to
-the named output file.
-
--- END --
-
-Points to note:
-* Leading and trailing space are trimmed from the line. Although the above
- example has every line starting at column 1, the lines could be indented
- if desired.
-
-* Blank lines are ignored.
-
-* Lines starting with "#" are comments are are ignored. Comments must be on
- a line by themselves - inline comments will be interpreted as part of the
- text of the line.
-
-* Lines starting $ are directives. At present, just one directive is
- recognised:
-
- * $NAMESPACE, which has one argument: the namespace in which the symbols are
- created. In the absence of a $NAMESPACE directive, symbols will be put in
- the anonymous namespace.
-
-* Message lines. These start with a "%" and are followed by the message
- identification and the message text, the latter including zero or more
- replacement tokens, e.g.
-
- % LOG_WRITE_ERROR error writing to %1: %2
-
- * There may be zero or more spaces between the leading "%" and the message
- identification (which, in the example above, is the string
- "LOG_WRITE_ERROR").
-
- * The message identification can be any string of letters, digits and
- underscores, but should not start with a digit. The convention adopted
- in BIND 10 is for the first component (before the first underscore) to be
- a string indicating the origin of the message, and the remainder to
- describe the message. So in the example above, the LOG_ indicates that
- the error originated from the logging library and the "WRITE_ERROR"
- indicates that there was a problem in a write operation.
-
- * The rest of the line - from the first non-space character to the
- last non- space character - is taken exactly for the text
- of the message. There are no restrictions on what characters may
- be in this text, other than they be printable. (This means that
- both single-quote (') and double-quote (") characters are allowed.)
- The message text may include replacement tokens (the strings "%1",
- "%2" etc.). When a message is logged, these are replaced with the
- arguments passed to the logging call: %1 refers to the first argument,
- %2 to the second etc. Within the message text, the placeholders
- can appear in any order and placeholders can be repeated. Otherwise,
- the message is printed unmodified.
-
-* Remaining lines indicate an explanation for the preceding message. These
- are intended to be processed by a separate program and used to generate
- an error messages manual. They are ignored by the message compiler.
-
-Message Compiler
-----------------
-The message compiler is a program built in the src/log/compiler directory.
-It is invoked by the command:
-
- message [-h] [-v] -p] <message-file>
-
-("-v" prints the version number and exits; "-h" prints brief help text.) The
-compiler produces source files for C++ and Python.
-
-C++ Files
----------
-Without the "-p" option, the message compiler processes the message file
-to produce two files:
-
-1) A C++ header file (called <message-file-name>.h) that holds lines of
-the form:
-
- namespace <namespace> {
- extern const isc::log::MessageID LOG_WRITE_ERROR;
- :
- }
-
-The symbols define the keys in the global message dictionary, with the
-namespace enclosing the symbols set by the $NAMESPACE directive.
-
-(This is the reason for the restriction on message identifiers - they
-have to be valid C++ symbol names.)
-
-2) A C++ source file (called <message-file-name>.cc) that holds the definitions
-of the global symbols and code to insert the symbols and messages into the map.
-
-Symbols are defined to be equal to strings holding the identifier, e.g.
-
- extern const isc::log::MessageID LOG_WRITE_ERROR = "LOG_WRITE_ERROR";
-
-(The implementation allows symbols to be compared. However, use of strings
-should not be assumed - a future implementation may change this.)
-
-In addition, the file declares an array of identifiers/messages and an object
-to add them to the global dictionary:
-
- namespace {
- const char* values[] = {
- identifier1, text1,
- identifier2, text2,
- :
- NULL
- };
-
- const isc::log::MessageInitializer initializer(values);
- }
-
-The constructor of the MessageInitializer object retrieves the singleton
-global Dictionary object (created using standard methods to avoid the
-"static initialization fiasco") and adds each identifier and text to it.
-A check is made as each is added; if the identifier already exists, it is
-added to "overflow" vector; the vector is printed to the main logging output
-when logging is finally enabled (to indicate a programming error).
-
-Python Files
-------------
-If the "-p" option is given, the compiler produces a Python module defining
-the messages. The format of this is:
-
-import isc.log
- :
-LOG_WRITE_ERROR = isc.log.create_message("LOG_WRITE_ERROR",
- "error writing to %1 : %2")
-
-(The definition is output on one line - it is split across two lines in this
-document for readability.)
-
-The module can be imported into other Python code, and messages logged
-in a similar way to C++ using the Python logging library.
-
-Using the Logging - C++
-=======================
-1. Build message header file and source file as describe above.
-
-2. The main program unit must include a call to isc::log::initLogger()
- (described in more detail below) to set the logging severity, debug log
- level, and external message file:
-
- a) The logging severity is one of the enum defined in logger.h, i.e.
-
- isc::log::DEBUG
- isc::log::INFO
- isc::log::WARN
- isc::log::ERROR
- isc::log::FATAL
- isc::log::NONE
-
- b) The debug log level is only interpreted when the severity is
- DEBUG and is an integer ranging from 0 to 99. 0 should be used
- for the highest-level debug messages and 99 for the lowest-level
- (and typically more verbose) messages.
-
- c) The external message file. If present, this is the same as a
- standard message file, although it should not include any
- directives. (A single directive of a particular type will be
- ignored; multiple directives will cause the read of the file to
- fail with an error.)
-
- The settings remain in effect until the logging configuration is read,
- and so provide the default logging during program initialization.
-
-3. Declare a logger through which the message will be logged.
-
- isc::log::Logger logger("name");
-
- The string passed to the constructor is the name of the logger (it
- can be any string) and is used when configuring it. Loggers with
- the same name share the same configuration.
-
-4. Issue logging calls using supplied macros in "log/macros.h", e.g.
-
- LOG_ERROR(logger, LOG_WRITE_ERROR).arg("output.txt");
-
- (The macros are more efficient that calls to the methods on the logger
- class: they avoid the overhead of evaluating the parameters to arg()
- if the settings are such that the message is not going to be output.)
-
-Using the Logging - Python
-==========================
-1. Build message module as describe above.
-
-2. The main program unit must include a call to isc.log.init()
- (described in more detail below) to set the to set the logging
- severity, debug log level, and external message file:
-
- a) The logging severity is one of the strings:
-
- DEBUG
- INFO
- WARN
- ERROR
- FATAL
- NONE
-
- b) The debug log level is only interpreted when the severity is
- DEBUG and is an integer ranging from 0 to 99. 0 should be used
- for the highest-level debug messages and 99 for the lowest-level
- (and typically more verbose) messages.
-
- c) The external message file. If present, this is the same as a
- standard message file, although it should not include any
- directives. (Any that are there will be ignored.)
-
- The settings remain in effect until the logging configuration is read,
- and so provide the default logging during program initialization.
-
-3. Declare a logger through which the message will be logged.
-
- isc.log.Logger logger("name")
-
- The string passed to the constructor is the name of the logger (it
- can be any string) and is used when configuring it. Loggers with
- the same name share the same configuration.
-
-4. Issue calls to the logging methods:
-
- logger.error(LOG_WRITE_ERROR, "output.txt");
-
-Logging Initialization
-======================
-In all cases, if an attempt is made to use a logging method before the logging
-has been initialized, the program will terminate with a LoggingNotInitialized
-exception.
-
-C++
----
-Logging Initialization is carried out by calling initLogger(). There are two
-variants to the call, one for use by production programs and one for use by
-unit tests.
-
-Variant #1, Used by Production Programs
----------------------------------------
-void isc::log::initLogger(const std::string& root,
- isc::log::Severity severity = isc::log::INFO,
- int dbglevel = 0, const char* file = NULL,
- bool buffer = false);
-
-This is the call that should be used by production programs:
-
-root
-Name of the program (e.g. "b10-auth"). This is also the name of the root
-logger and is used when configuring logging.
-
-severity
-Default severity that the program will start logging with. Although this may
-be overridden when the program obtains its configuration from the configuration
-database, this is the severity that it used until then. (This may be set by
-a command-line parameter.)
-
-dbglevel
-The debug level used if "severity" is set to isc::log::DEBUG.
-
-file
-The name of a local message file. This will be read and its definitions used
-to replace the compiled-in text of the messages.
-
-buffer
-If set to true, initial log messages will be internally buffered, until the
-first time a logger specification is processed. This way the program can
-use logging before even processing its logging configuration. As soon as any
-specification is processed (even an empty one), the buffered log messages will
-be flushed according to the specification. Note that if this option is used,
-the program SHOULD call one of the LoggerManager's process() calls (if you
-are using the built-in logging configuration handling in ModuleCCSession,
-this is automatically handled). If the program exits before this is done,
-all log messages are dumped in a raw format to stdout (so that no messages
-get lost).
-
-Variant #2, Used by Unit Tests
-------------------------------
- void isc::log::initLogger()
-
-This is the call that should be used by unit tests. In this variant, all the
-options are supplied by environment variables. (It should not be used for
-production programs to avoid the chance that the program operation is affected
-by inadvertently-defined environment variables.)
-
-The environment variables are:
-
-B10_LOGGER_ROOT
-Sets the "root" for the unit test. If not defined, the name "bind10" is used.
-
-B10_LOGGER_SEVERITY
-The severity to set for the root logger in the unit test. Valid values are
-"DEBUG", "INFO", "WARN", "ERROR", "FATAL" and "NONE". If not defined, "INFO"
-is used.
-
-B10_LOGGER_DBGLEVEL
-If B10_LOGGER_SEVERITY is set to "DEBUG", the debug level. This can be a
-number between 0 and 99, and defaults to 0.
-
-B10_LOGGER_LOCALMSG
-If defined, points to a local message file. The default is not to use a local
-message file.
-
-B10_LOGGER_DESTINATION
-The location to which log message are written. This can be one of:
-
- stdout Message are written to stdout
- stderr Messages are written to stderr
- syslog[:facility] Messages are written to syslog. If the optional
- "facility" is used, the messages are written using
- that facility. (This defaults to "local0" if not
- specified.)
- Anything else Interpreted as the name of a file to which output
- is appended. If the file does not exist, a new one
- is opened.
-
-In the case of "stdout", "stderr" and "syslog", they must be written exactly
-as is - no leading or trailing spaces, and in lower-case.
-
-Python
-------
-To be supplied
-
-
-Severity Guidelines
-===================
-When using logging, the question arises, what severity should a message
-be logged at? The following is a suggestion - as always, the decision
-must be made in the context of which the message is logged.
-
-One thing that should always be borne in mind is whether the logging
-could be used as a vector for a DOS attack. For example, if a warning
-message is logged every time an invalid packet is received, an attacker
-could simply send large numbers of invalid packets. (Of course, warnings
-could be disabled (or just warnings for that that particular logger),
-but nevertheless the message is an attack vector.)
-
-FATAL
------
-The program has encountered an error that is so severe that it cannot
-continue (or there is no point in continuing). When a fatal error
-has been logged, the program will usually exit immediately (or shortly
-afterwards) after dumping some diagnostic information.
-
-ERROR
------
-Something has happened such that the program can continue but the
-results for the current (or future) operations cannot be guaranteed to
-be correct, or the results will be correct but the service is impaired.
-For example, the program started but attempts to open one or more network
-interfaces failed.
-
-WARN
-----
-An unusual event happened. Although the program will continue working
-normally, the event was sufficiently out of the ordinary to warrant
-drawing attention to it. For example, at program start-up a zone was
-loaded that contained no resource records,
-
-INFO
-----
-A normal but significant event has occurred that should be recorded,
-e.g. the program has started or is just about to terminate, a new zone
-has been created, etc.
-
-DEBUG
------
-This severity is only enabled on for debugging purposes. A debug level is
-associated with debug messages, level 0 (the default) being for high-level
-messages and level 99 (the maximum) for the lowest level. How the
-messages are distributed between the levels is up to the developer.
-So if debugging the NSAS (for example), a level 0 message might record
-the creation of a new zone, a level 10 recording a timeout when trying
-to get a nameserver address, but a level 50 would record every query for
-an address. (And we might add level 70 to record every update of the RTT.)
-
-Note that like severities, levels are cumulative; so if level 25 is
-set as the debug level, all debug levels from 0 to 25 will be output.
-In fact, it is probably easier to visualise the debug levels as part of
-the severity system:
-
- FATAL High
- ERROR
- WARN
- INFO
- DEBUG level 0
- DEBUG level 1
- :
- DEBUG level 99 Low
-
-When a particular severity is set, it - and all severities and/or debug
-levels above it - will be logged.
-
-To try to ensure that the information from different modules is roughly
-comparable for the same debug level, a set of standard debug levels has
-been defined for common type of debug output. However, modules are free
-to set their own debug levels or define additional ones.
-
-Logging Sources v Logging Severities
-------------------------------------
-When logging events, make a distinction between events related to the
-server and events related to DNS messages received. Caution needs to
-be exercised with the latter as, if the logging is enabled in the normal
-course of events, such logging could be a denial of service vector. For
-example, suppose that the main authoritative service logger were to
-log both zone loading and unloading as INFO and a warning message if
-it received an invalid packet. An attacker could make the INFO messages
-unusable by flooding the server with malformed packets.
-
-There are two approaches to get round this:
-
-a) Make the logging of packet-dependent events a DEBUG-severity message.
-DEBUG is not enabled by default, so these events will not be recorded
-unless DEBUG is specifically chosen.
-
-b) Record system-related and packet-related messages via different loggers
-(e.g. in the example given, server events could be logged using the
-logger "auth" and packet-related events at that level logged using the
-logger "pkt-auth".) As the loggers are independent and the severity
-levels independent, fine-tuning of what and what is not recorded can
-be achieved.
-
-
-Notes
-=====
-The message compiler is written in C++ (instead of Python) because it
-contains a component that reads the message file. This component is used
-in both the message compiler and the server; in the server it is used
-when the server starts up (or when triggered by a command) to read in
-a message file to overwrite the internal dictionary. Writing it in C++
-means there is only one piece of code that does this functionality.
diff --git a/src/lib/log/buffer_appender_impl.cc b/src/lib/log/buffer_appender_impl.cc
index 8899c4f..4aa222f 100644
--- a/src/lib/log/buffer_appender_impl.cc
+++ b/src/lib/log/buffer_appender_impl.cc
@@ -30,6 +30,7 @@ BufferAppender::~BufferAppender() {
// So dump all that is left to stdout
try {
flushStdout();
+ destructorImpl();
} catch (...) {
// Ok if we can't even seem to dump to stdout, never mind.
}
diff --git a/src/lib/log/interprocess/interprocess_sync_file.h b/src/lib/log/interprocess/interprocess_sync_file.h
index cb07003..454cd8b 100644
--- a/src/lib/log/interprocess/interprocess_sync_file.h
+++ b/src/lib/log/interprocess/interprocess_sync_file.h
@@ -53,7 +53,7 @@ public:
///
/// Creates a file-based interprocess synchronization object
///
- /// \param name Name of the synchronization task. This has to be
+ /// \param task_name Name of the synchronization task. This has to be
/// identical among the various processes that need to be
/// synchronized for the same task.
InterprocessSyncFile(const std::string& task_name) :
diff --git a/src/lib/log/interprocess/interprocess_sync_null.h b/src/lib/log/interprocess/interprocess_sync_null.h
index 41dab50..f046f79 100644
--- a/src/lib/log/interprocess/interprocess_sync_null.h
+++ b/src/lib/log/interprocess/interprocess_sync_null.h
@@ -32,7 +32,7 @@ public:
///
/// Creates a null interprocess synchronization object
///
- /// \param name Name of the synchronization task. This has to be
+ /// \param task_name Name of the synchronization task. This has to be
/// identical among the various processes that need to be
/// synchronized for the same task.
InterprocessSyncNull(const std::string& task_name) :
diff --git a/src/lib/log/log_formatter.h b/src/lib/log/log_formatter.h
index 8cb34c7..d336b30 100644
--- a/src/lib/log/log_formatter.h
+++ b/src/lib/log/log_formatter.h
@@ -163,8 +163,12 @@ public:
/// This is the place where output happens if the formatter is active.
~ Formatter() {
if (logger_) {
- checkExcessPlaceholders(message_, ++nextPlaceholder_);
- logger_->output(severity_, *message_);
+ try {
+ checkExcessPlaceholders(message_, ++nextPlaceholder_);
+ logger_->output(severity_, *message_);
+ } catch (...) {
+ // Catch and ignore all exceptions here.
+ }
delete message_;
}
}
diff --git a/src/lib/log/logging.dox b/src/lib/log/logging.dox
new file mode 100644
index 0000000..f4d022c
--- /dev/null
+++ b/src/lib/log/logging.dox
@@ -0,0 +1,762 @@
+// Copyright (C) 2013-2014 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.
+
+// Note: the prefix "log" to all labels is an abbreviation for "Logging"
+// and is used to prevent a clash with symbols in any other Doxygen file.
+
+/**
+ at page logBind10Logging BIND 10 Logging
+
+ at section logBasicIdeas Basic Ideas
+
+The BIND 10 logging system is based on the log4J logging system
+common in Java development, and includes the following ideas:
+
+- A set of severity levels.
+- A hierarchy of logging sources.
+- Separation of message use from message text.
+
+ at subsection logSeverity Message Severity
+Each message logged by BIND 10 has a severity associated with it, ranging
+from FATAL - the most severe - to DEBUG - messages output as the code
+executes to facilitate debugging. In order of decreasing severity,
+the levels are:
+
+<dl>
+<dt>FATAL</dt>
+<dd>The program has encountered an error that is so severe that it
+cannot continue (or there is no point in continuing). For example, an
+unhandled exception generated deep within the code has been caught by the
+top-level program. When a fatal error has been logged, the program will
+exit immediately (or shortly afterwards) after dumping some diagnostic
+information.</dd>
+
+<dt>ERROR</dt>
+<dd>Something has happened such that the program can continue but the
+results for the current (or future) operations cannot be guaranteed to
+be correct, or the results will be correct but the service is impaired.
+For example, the program started but attempts to open one or more network
+interfaces failed.</dd>
+
+<dt>WARN</dt>
+<dd>(Warning) An unusual event happened. Although the program will
+continue working normally, the event was sufficiently out of the ordinary
+to warrant drawing attention to it. For example, the authoritative
+server loaded a zone that contained no resource records.</dd>
+
+<dt>INFO</dt>
+<dd>(Information) A normal but significant event has occurred that should
+be recorded, e.g. the program has started or is just about to terminate,
+a new zone has been created, etc.</dd>
+
+<dt>DEBUG</dt>
+<dd>Debug messages are output at this severity. Each message also
+has a debug level associated with it, ranging from 0 (the default)
+for high-level messages and level 99 (the maximum) for the the lowest
+level.</dd>
+</dl>
+
+When logging is enabled for a component, it is enabled for a particular
+severity level and all higher severities. So if logging is enabled
+for INFO messages, WARN, ERROR and FATAL messages will also be logged,
+but not DEBUG ones. If enabled for ERROR, only ERROR and FATAL messages
+will be logged.
+
+As noted above, DEBUG messages are also associated with a debug level.
+This allows the developer to control the amount of debugging information
+produced; the higher the debug level, the more information is output.
+For example, if debugging the NSAS (nameserver address store), debug
+levels might be assigned as follows: a level 0 debug message records
+the creation of a new zone, a level 10 logs every timeout when trying
+to get a nameserver address, a level of 50 records every query for an
+address and a level of 70 records every update of the round-trip time.
+
+Like severities, levels are cumulative; so if level 25 is set as the
+debug level, all debug messages associated levels 0 to 25 (inclusive)
+will be output. In fact, it is probably easier to visualise the debug
+levels as part of the severity system:
+ at code
+FATAL Most severe
+ERROR
+WARN
+INFO
+DEBUG level 0
+DEBUG level 1
+ :
+DEBUG level 99 Least severe
+ at endcode
+When a particular debug level is set, it - and all debug levels and
+severities above it - will be logged.
+
+To try to ensure that the information from different modules is roughly
+comparable for the same debug level, a set of standard debug levels has
+been defined for common types of debug output. (These can be found in
+ at ref log_dbglevels.h) However, modules are free to set their own debug
+levels or define additional ones.
+
+ at subsection logHierarchical Hierarchical Logging System
+
+When a program writes a message to the logging system, it does so using an
+instance of the @ref isc::log::Logger class. As well as performing the
+write of the message, the logger identifies the source of the message:
+different sources can write to different destinations and can log
+different severities of messages. For example, the logger associated
+with the resolver's cache code could write debugging and other messages
+to a file while all other components only write messages relating to
+errors to the syslog file.
+
+The loggers are hierarchical in that each logger is the child of another
+logger. The top of the hierarchy is the root logger; this does not
+have a parent. The reason for this hierarchy is that unless a logger
+explicitly assigns a value to an attribute (such as severity of messages
+it should log), it picks it up the value from the parent. In BIND 10,
+each component (b10-auth, b10-resolver etc.) has a root logger (named
+after the program) and every other logger in the component is a child
+of that. So in the example above, the error/syslog attributes could be
+associated with the b10-resolver logger while the logger associated with
+the cache sets its own values for the debug/file attributes.
+
+More information about the logging hierarchy can be found in the section
+on Logging configuration in the <a
+href="http://bind10.isc.org/docs/bind10-guide.html#logging">BIND 10
+Guide</a>.
+
+ at subsection logSeparationUseText Separation of Messages Use from Message Text
+
+Separating the use of the message from the text associated with it -
+in essence, defining message text in an external file - allows for the
+replacement the supplied text of the messages with a local language version.
+It also means that other attributes can be associated with the message,
+for example, an explanation of the meaning of the message and other
+information such as remedial action in the case of errors.
+
+Each message has an identifier such as "LOG_WRITE_ERROR".
+Within the program, this is the symbol passed to the logging system
+which uses the symbol as an index into a dictionary to retrieve the message
+associated with it (e.g. "unable to open %1 for input"), after which it
+substitutes any message parameters (in this example, the name of the file
+where the write operation failed) and logs the result to the destination.
+
+In BIND 10, a the default text for each message is linked into the
+program. Each program is able to read a locally-defined message file
+when it starts, updating the stored definitions with site-specific text.
+When the message is logged, the updated text is output. However, the
+message identifier is always included in the output so that the origin
+of the message can be identified even if the text has been changed.
+
+ at note Local message files have not yet been implemented in BIND 10.
+
+
+
+ at section logDeveloperUse Using Logging in a BIND 10 Component
+
+The steps in using the logging system in a BIND 10 component (such as
+an executable or library) are:
+
+<ol>
+<li>Create a message file. This defines messages by an identification
+string and and text that explains the message in more detail. Ideally the
+file should have a file type of ".mes".</li>
+
+<li>Run it through the message compiler to produce the files for your
+module. This step should be included in the build process. The message
+compiler is a BIND10 program and is one of the first programs built and
+linked in the build process. As a result, it should be available for
+compiling the message files of all BIND 10 components and libraries.
+
+For C++ development, the message compiler produces two files in the
+default directory, having the same name as the input file but with file
+types of ".h" and ".cc". For Python, the message compiler will produce
+a Python module containing the symbols.</li>
+
+<li>Include the resultant files in your source code to define message symbols,
+and (for C++) compile the code and link it into your program.</li>
+
+<li>Declare loggers in your code and use them to log messages.</li>
+
+<li>Call the logger initialization function in the program's main module.</li>
+
+</ol>
+
+The following sections describe these steps in more detail.
+
+
+ at subsection logMessageFiles Create a Message File
+
+A message file contains message definitions. Typically there
+will be one message file for each component that uses BIND 10 logging.
+An example file could be:
+
+ at code
+# Example message file
+
+$NAMESPACE isc::log
+
+% LOG_UNRECOGNISED_DIRECTIVE line %1: unrecognised directive '%2'
+A line starting with a dollar symbol was found, but the first word on the line
+(shown in the message) was not a recognised message compiler directive.
+
+% LOG_WRITE_ERROR error writing to %1: %2
+The specified error was encountered by the message compiler when writing to
+the named output file.
+ at endcode
+
+Points to note are:
+
+<ul>
+<li>Leading and trailing spaces are trimmed from each line before it
+is processed. Although the above example has every line starting at
+column 1, the lines could be indented if desired.</li>
+
+<li>Lines starting with "#" are comments are are ignored. Comments must
+be on a line by themselves; inline comments will be interpreted as part
+of the text of that line.</li>
+
+<li>Lines starting with "$" are directives. At present, just one
+directive is recognised:
+ <dl>
+ <dt>$NAMESPACE <namespace-name></dt>
+ <dd>The sole argument is the name of the namespace in which the
+ symbols are created. In the absence of a $NAMESPACE directive,
+ symbols will be put in the anonymous namespace.</dd>
+ </dl>
+</li>
+
+<li>Lines starting with "%" are message definitions and comprise the message
+identification and the message text. For example:
+ at code
+% LOG_WRITE_ERROR error writing to %1: %2
+ at endcode
+
+There may be zero or more spaces between the leading "%" and the
+message identification (which, in the example above, is the string
+"LOG_WRITE_ERROR").</li>
+
+<li>The message identification can be any string of letters, digits and
+underscores, but must not start with a digit.</li>
+
+<li>The rest of the line - from the first non-space character to the
+last non- space character - is the text of the message. There are no
+restrictions on what characters may be in this text, other than they be
+printable (so both single-quote (') and double-quote (") characters are
+allowed). The message text may include replacement tokens (the strings
+"%1", "%2" etc.). When a message is logged, these are replaced with the
+arguments passed to the logging call: %1 refers to the first argument,
+%2 to the second etc. Within the message text, the placeholders can
+appear in any order and placeholders can be repeated. Otherwise, the
+message is printed unmodified.</li>
+
+<li>Remaining lines indicate an explanation for the preceding message.
+The explanation can comprise multiple paragraphs, the paragraphs being
+separated by blank lines. These lines are intended to be processed by a
+separate program to generate an error messages manual; they are ignored
+by the message compiler.</li>
+
+<li>Except when used to separate paragraphs in the message explanation,
+blank lines are ignored.</li>
+</ul>
+
+Although there are few restriction on what can be in the message
+identifcation and text, there are a number of conventions used by BIND
+10, both in the contents of the message and in the usage. All code
+should adhere to these:
+
+<ul>
+<li>Message identifications should include at least one underscore.
+The component before the first underscore is a string indicating the
+origin of the message, and the remainder describes the condition.
+So in the example above, the LOG indicates that the error originated
+from the logging library and the "WRITE_ERROR" indicates that there was
+a problem in a write operation.</li>
+
+<li>The part of the message identification describing the error (e.g.
+"WRITE_ERROR" in the example above) should comprise no more than
+two or three words separated by underscores. An excessive number
+of words or overly long message identification should be avoided;
+such information should be put in the text of the message. For example,
+"RESOLVER_ERROR_FETCHING_NAME_FROM_UPSTREAM_SERVER" is excessively long,
+"RESOLVER_FETCH_ERROR" being better.</li>
+
+<li>Similarly, the text of the message should be reasonably concise. It should
+include enough information (possibly supplied at run-time in the form of
+parameters) to allow further investigations to be undertaken if required.
+
+Taking the above example, a suitable error message to indicate that the
+resolver has failed to read a name from an upstream authoritative server
+could be:
+
+ at code
+% RESOLVER_FETCH_ERROR fetch from %1 failed, error code %2 (%3)
+ at endcode
+
+... where %1 indicates the name or IP address of the server to which the
+fetch was sent, %2 the errno value returned and %3 the message associated
+with that error number (retrieved via a call to "strerror()").
+
+</li>
+
+<li>The message should not have a comma after the message identification.
+The message text should neither start with a capital letter (unless
+the first word is a proper noun or is normally written in capitals)
+nor end with a period. The message reporting system takes care of such
+punctuation.</li>
+
+<li>The parameters substituted into the message text should not include
+line breaks. Messages are normally output to the syslog file which
+has the inbuilt assumption of one line per message. Splitting a message
+across multiple lines makes it awkward to search the file for messages
+and associated information.</li>
+
+<li>The message identifier should be unique across the entire BIND 10
+system. (An error will be reported at system start-up if an identifier
+is repeated.)</li>
+
+<li>A particular message identifier should only be used at one place in
+the BIND 10 code. In this way, if the message indicates a problem, the
+code in question can be quickly identified.</li>
+
+<li>The explanation of the message - the free-form text following the
+message identification - appears in the BIND 10 message manual. It
+should:
+
+<ul>
+<li>Describe the serverity of the message (debug, informational etc.)</li>
+
+<li>Expand on the text of the message. In some cases, such as
+debug messages, the message text may provide more or less sufficient
+description. For warnings and errors, the explanation should provide
+sufficient background to the problem to allow a non-developer to
+understand the issue and to begin fault-finding. If possible, the
+explanation should also include suggested remedial action.</li>
+</ul>
+</ul>
+
+ at subsection logSourceFiles Produce Source Files
+The message file created in the previous step is then run through the
+message compiler to produce source files that are included in the BIND
+10 programs.
+
+ at subsubsection logMessageCompiler Message Compiler
+The message compiler is a program built in the src/log/compiler directory.
+It is invoked by the command:
+ at code
+message [-h] [-v] [-p] [-d dir] <message-file>
+ at endcode
+"-v" prints the version number and exits; "-h" prints brief help text.
+The compiler produces source files for C++ unless the "-p" switch is
+specified, in which case it produces Python code. Finally, the "-d"
+switch directs the compiler to produce the output files in the specified
+directory (the default being the current working directory).
+
+<b>C++ Files</b><br/>
+Without the "-p" option, the message compiler processes the message file
+to produce two files:
+
+<ol>
+<li>A C++ header file (called <message-file-name>.h) holding lines of
+the form:
+ at code
+namespace <namespace-name> {
+ extern const isc::log::MessageID LOG_BAD_DESTINATION;
+ extern const isc::log::MessageID LOG_BAD_SEVERITY;
+ :
+}
+ at endcode
+The symbols define keys in the global message dictionary, with
+the namespace enclosing the symbols set by the $NAMESPACE directive.
+(This is the reason for the restriction on message identifiers - they
+have to be valid C++ symbol names.)</li>
+
+<li>A C++ source file (called <message-file-name>.cc) that holds the definitions
+of the global symbols and code to insert the symbols and messages into
+an internal dictionary.
+
+Symbols are defined to be equal to strings equal to the identifier, e.g.
+ at code
+extern const isc::log::MessageID LOG_BAD_DESTINATION = "LOG_BAD_DESTINATION";
+extern const isc::log::MessageID LOG_BAD_SEVERITY = "LOG_BAD_SEVERITY";
+ :
+ at endcode
+(The current implementation allows symbols to be compared. However,
+use of strings should not be assumed - a future implementation may change
+this.) In addition, the file declares an array of identifiers/messages
+and an object to add them to the global dictionary, e.g.:
+ at code
+namespace {
+ const char* values[] = {
+ "LOG_BAD_DESTINATION", "unrecognized log destination: %1",
+ "LOG_BAD_SEVERITY", "unrecognized log severity: %1",
+ :
+ NULL
+ };
+ const isc::log::MessageInitializer initializer(values);
+}
+ at endcode
+
+The constructor of the @ref isc::log::MessageInitializer object retrieves
+the singleton global @ref isc::log::MessageDictionary object (created
+using standard methods to avoid the "static initialization fiasco") and
+adds each identifier and associated text to it. These constructors are run
+when the program starts; a check is made as each identifier is added and,
+if the identifier already exists in the dictionary, a warning message
+is printed to the main logging output when logging is finally enabled.
+The appearance of such a message indicates a programming error.
+</li>
+</ol>
+
+<b>Python Files</b><br/>
+If the "-p" option is given, the compiler produces a Python module defining
+the messages. The content of this is of the form:
+ at code
+import isc.log
+ :
+LOG_WRITE_ERROR = isc.log.create_message("LOG_WRITE_ERROR",
+ "error writing to %1 : %2")
+ at endcode
+(The definition is output on one line - it is split across two lines in this
+document for readability.)
+
+The module can be imported into other Python code, and messages logged
+in a similar way to C++ using the Python logging library.
+
+ at subsubsection logMakefile Include Message Compilation in Makefile
+The source file for the messages is the ".mes" file, but the files used
+by the code (which, in the case of C++, must be compiled and linked)
+are the output of the message compiler. (The compiler is produced very
+early on in the BIND 10 build sequence, so is available for use in the
+building of subsequent components.) To allow this, certain dependencies
+must be included in the Makefile.am for each component that uses logging.
+
+<b>Including Message files in C++ Component Builds</b><br/>
+The following segment from the "hooks" Makefile.am illustrates
+the entries needed.
+ at code
+# Define rule to build logging source files from message file
+hooks_messages.h hooks_messages.cc: s-messages
+
+s-messages: hooks_messages.mes
+ $(top_builddir)/src/lib/log/compiler/message $(top_srcdir)/src/lib/hooks/hooks_messages.mes
+ touch $@
+
+# Tell automake that the message files are built as part of the build process
+# (so that they are built before the main library is built).
+BUILT_SOURCES = hooks_messages.h hooks_messages.cc
+
+# Ensure that the message file is included in the distribution
+EXTRA_DIST = hooks_messages.mes
+
+# Get rid of generated message files on a clean
+CLEANFILES = *.gcno *.gcda hooks_messages.h hooks_messages.cc s-messages
+ at endcode
+The first two rules relate the output .h and .cc files produced by the
+message compiler to the input .mes file. The intermediate "s-messages"
+file is used to overcome synchronization issues with parallel builds
+(where "make" uses multiple processes running in parallel). Note that the
+reference to both the compiler and the input message file are via absolute
+paths defined in terms of Automake macros. In particular it is important
+that the message compiler - which is created during the build process - is
+referred to via the "top_builddir" macro, whereas the input message file -
+which is in the repository - is accessed through the "top_srcdir" macro.
+
+The BUILT_SOURCES line notifies the Automake that the .h and .cc files
+need to be created before they can be used in the compilation, so
+instructs it to organse things so that the message compiler is run first.
+
+As the .mes file is not directly included in any compilation, it will
+not be automatically copied into a distribution created through this
+Makefile.am. The EXTRA_DIST line informs Automake that this file does
+need to be included.
+
+Finally, the intermediate files - the .cc and .h file, as well as the
+intermediate s-messages file - need to be removed when "make clean" is run.
+These files are therefore included in the definition of the CLEANFILES macro.
+
+Not shown are the Makefile.am lines where the .h and .cc file are used. These
+are the same as other lines specifying .h and .cc source files.
+
+<b>Including Message files in Python Component Builds</b><br/>
+The following (modified) segments from the "xfrin" Makefile.am illustrates
+the entries needed.
+
+ at code
+CLEANFILES = $(PYTHON_LOGMSGPKG_DIR)/work/xfrin_messages.py
+CLEANFILES += $(PYTHON_LOGMSGPKG_DIR)/work/xfrin_messages.pyc
+ :
+EXTRA_DIST += xfrin_messages.mes
+ :
+# Define rule to build logging source files from message file
+$(PYTHON_LOGMSGPKG_DIR)/work/xfrin_messages.py : xfrin_messages.mes
+ $(top_builddir)/src/lib/log/compiler/message \
+ -d $(PYTHON_LOGMSGPKG_DIR)/work -p $(srcdir)/xfrin_messages.mes
+ at endcode
+The CLEANFILES lines remove the created Python (and compiled Python)
+code when "make clean" is run.
+
+The EXTRA_DIST line ensures that the .mes file is copied to the
+distribution tarball.
+
+The final dependency shows the use of the message compiler to
+create the Python message module in a working directory.
+
+
+ at subsection logUsage Using Logging Files in Program Development
+
+ at subsubsection logCppUsage Use in a C++ Program or Module
+To use logging in a C++ program or module:
+
+<ol>
+<li>Build the message header file and source file as described above.</li>
+
+<li>In each C++ file in which logging is to be used, declare a logger
+through which the message will be logged.
+
+ at code
+isc::log::Logger logger("name");
+ at endcode
+This declaration can be per-function, or it can be declared statically
+in file scope. The string passed to the constructor is the name of
+the logger (it can be any string) and is used when configuring it.
+(Remember though that the name of root logger for the program will be
+prepended to the name chosen. So if, for example, the name "cache"
+is chosen and the model is included in the "b10-resolver" program, the
+full name of the logger will be "b10-resolver.cache".) Loggers with
+the same name share the same configuration. For this reason if, as is
+usual, messages logged in different files in the same component (e.g.
+hooks module, nameserver address store, etc.) originate from loggers
+with the same name, the logger declaration can be placed into a header
+file.</li>
+
+<li>Issue logging calls using supplied macros in "log/macros.h", e.g.
+ at code
+LOG_ERROR(logger, LOG_WRITE_ERROR).arg("output.txt");
+LOG_DEBUG(nsas_logger, NSAS_DBG_TRACE, NSAS_LOOKUP_CANCEL).arg(zone);
+ at endcode
+All macros (with the exception of LOG_DEBUG) take two arguments:
+the C++ logger object that will be used to log the message, and the
+identification of the message to be logged. LOG_DEBUG takes three
+arguments, the additional one being the debug level associated with
+the message. The .arg() call appended to the end of the LOG_XXX()
+macro handles the arguments to the message. A chain of these is used
+in cases where a message takes multiple arguments, e.g.
+ at code
+LOG_DEBUG(nsas_logger, NSAS_DBG_RTT, NSAS_UPDATE_RTT)
+ .arg(addresses_[family][index].getAddress().toText())
+ .arg(old_rtt).arg(new_rtt);
+ at endcode
+Using the macros is more efficient than direct calls to the methods on
+the logger class: they avoid the overhead of evaluating the parameters
+to arg() if the logging settings are such that the message is not going
+to be output (e.g. it is a DEBUG message and the logging is set to output
+messages of INFO severity or above).</li>
+
+<li>The main program unit must include a call to isc::log::initLogger()
+(described in more detail below) to set the initial logging severity, debug log
+level, and external message file.
+</ol>
+
+ at subsubsection logPythonUsage Use in a Python Module
+To use logging in a Python module:
+<ol>
+<li>Build message module as described above.</li>
+
+<li>Declare a logger through which the message will be logged.
+ at code
+isc.log.Logger logger("name")
+ at endcode
+The string passed to the constructor is the name of the logger (it can
+be any string) and is used when configuring it. Loggers with the same
+name share the same configuration.</li>
+
+<li>Issue calls to the logging methods:
+ at code
+logger.error(LOG_WRITE_ERROR, "output.txt")
+ at endcode
+The message parameters are included as trailing arguments in the
+logger call.</li>
+
+<li>The main program unit must include a call to isc.log.init() (described
+in more detail below) to set the to set the logging severity, debug log
+level, and external message file. The settings remain in effect until
+the logging configuration is read, so are the ones used during program
+initialization.</li>
+</ol>
+
+
+ at subsection logInitialization Logging Initialization
+In all cases, if an attempt is made to use a logging method before
+the logging has been initialized, the program will terminate with a
+LoggingNotInitialized exception.
+
+ at subsection logInitializationCpp C++ Initialization
+Logging Initialization is carried out by calling @ref
+isc::log::initLogger(). There are two variants to the call, one for
+use by production programs and one for use by unit tests.
+
+ at subsubsection logInitializationCppVariant1 Variant #1, Used by Production Programs
+The call that should be used by all production programs is:
+ at code
+void isc::log::initLogger(const std::string& root,
+ isc::log::Severity severity = isc::log::INFO,
+ int dbglevel = 0, const char* file = NULL,
+ bool buffer = false);
+ at endcode
+Arguments are:
+<dl>
+<dt><code>root</code></dt>
+<dd>Name of the root logger. This should be the name of the program
+(e.g. "b10-auth") and is used when configuring logging.</dd>
+
+<dt><code>severity</code></dt>
+<dd>Default severity that the program will start logging with. Although
+this may be overridden when the program obtains its configuration from
+the configuration database, this is the severity that it used until then.
+The logging severity is one of the enum defined in @ref logger.h, i.e.
+
+ at code
+isc::log::DEBUG
+isc::log::INFO
+isc::log::WARN
+isc::log::ERROR
+isc::log::FATAL
+isc::log::NONE
+ at endcode
+
+(The level NONE may be used to disable logging.)
+</dd>
+
+<dt><code>dbglevel</code></dt>
+<dd>The debug log level is only interpreted when the severity is
+isc::log::DEBUG and is an integer ranging from 0 to 99. 0 should be
+used for the highest-level debug messages and 99 for the lowest-level
+(and typically more verbose) messages.</dd>
+
+<dt><code>file</code></dt>
+<dd>The name of a local message file. This will be read and its
+definitions used to replace the compiled-in text of the messages.
+The default value of NULL indicates that no local message file is
+supplied.</dd>
+
+<dt><code>buffer</code></dt>
+<dd>If set to true, initial log messages will be internally buffered,
+until the first time a logger specification is processed. This
+way the program can use logging before even processing its logging
+configuration. As soon as any specification is processed (even an
+empty one), the buffered log messages will be flushed according to
+the specification. Note that if this option is used, the program
+SHOULD call one of the @ref isc::log::LoggerManager::process() calls.
+(If you are using the built-in logging configuration handling in @ref
+isc::config::ModuleCCSession, this is automatically handled.) If the
+program exits before this is done, all log messages are dumped in a raw
+format to stdout (so that no messages get lost).</dd>
+</dl>
+
+ at subsubsection logInitializationCppVariant2 Variant #2, Used by Unit Tests
+ at code
+void isc::log::initLogger()
+ at endcode
+This is the call that should be used by unit tests. In this variant,
+all the options are supplied by environment variables: it should not
+be used for production programs to avoid the chance that the program
+operation is affected by inadvertently-defined environment variables. The
+environment variables are:
+
+<dl>
+<dt>B10_LOGGER_ROOT</dt>
+<dd>Sets the "root" for the unit test. If not defined, the name "bind10"
+is used.</dd>
+
+<dt>B10_LOGGER_SEVERITY</dt>
+<dd>The severity to set for the root logger in the unit test.
+Valid values are "DEBUG", "INFO", "WARN", "ERROR", "FATAL" and "NONE".
+If not defined, "INFO" is used.</dd>
+
+<dt>B10_LOGGER_DBGLEVEL</dt>
+<dd>If B10_LOGGER_SEVERITY is set to "DEBUG", the debug level. This can
+be a number between 0 and 99, and defaults to 0.</dd>
+
+<dt>B10_LOGGER_LOCALMSG</dt>
+<dd>If defined, points to a local message file. The default is not to
+use a local message file.</dd>
+
+<dt>B10_LOGGER_DESTINATION</dt>
+<dd>The location to which log message are written. This can be one of:
+<ul>
+<li><b>stdout</b> Message are written to stdout.</li>
+<li><b>stderr</b> Messages are written to stderr.</li>
+<li><b>syslog[:facility]</b> Messages are written to syslog. If the
+optional "facility" is used, the messages are written using that facility.
+(This defaults to "local0" if not specified.)</li>
+<li><b>Anything else</b> Interpreted as the name of a file to which
+output is appended. If the file does not exist, a new one is opened.</li>
+</ul>
+In the case of "stdout", "stderr" and "syslog", they must be written exactly
+as is - no leading or trailing spaces, and in lower-case.</dd>
+</dl>
+
+ at subsection logInitializationPython Python Initialization
+To initialize the logger in a Python program, the "init" method must be
+called:
+ at code
+isc.log.init(name, severity, debuglevel, file, buffer)
+ at endcode
+
+<dl>
+<dt><code>name</code></dt>
+<dd>String giving the name of the root logger. This is the only mandatory
+argument, the rest are optional.</dd>
+
+<dt><code>severity</code></dt>
+<dd>The severity, and is one of the strings "DEBUG", INFO" etc.
+The default is "INFO".</dd>
+
+<dt><code>debuglevel</code></dt>
+<dd>Debug level, an integer between 0 and 99. A default value of 0 will
+be used if this is not specified.</dd>
+
+<dt><code>file</code></dt>
+<dd>Name of the external message file (if present). By default, no
+external message file is used.</dd>
+
+<dt><code>buffer</code></dt>
+<dd>If set to true, initial log messages will be internally buffered,
+until the first time a logger specification is processed. This
+way the program can use logging before even processing its logging
+configuration. By default, no buffer is used.</dd>
+</dl>
+
+
+ at section logNotes Notes on the Use of Logging
+One thing that should always be kept in mind is whether the logging
+could be used as a means for a DOS attack. For example, if a warning
+message is logged every time an invalid packet is received, an attacker
+could simply send large numbers of invalid packets. Of course, warnings
+could be disabled (or just warnings for that that particular logger),
+but nevertheless the message is an attack vector. As a general rule,
+if the message can be triggered by a user action, it can be used as an
+attack vector.
+
+There are two approaches to get round this:
+<ol>
+<li>Log messages generated by such user actions as DEBUG messages. DEBUG
+is not enabled by default, so these events will not be recorded unless
+DEBUG is specifically enabled. Choosing a suitable debug level for
+such messages will select only those messages and not the more general
+debug messages.</li>
+
+<li>Record system-related and packet-related messages via different
+loggers. As the loggers are independent and the severity levels
+independent, fine-tuning of what and what is not recorded can be achieved.</li>
+
+</ol>
+*/
diff --git a/src/lib/log/message_dictionary.cc b/src/lib/log/message_dictionary.cc
index 3bfc56c..577042b 100644
--- a/src/lib/log/message_dictionary.cc
+++ b/src/lib/log/message_dictionary.cc
@@ -63,13 +63,13 @@ MessageDictionary::load(const char* messages[]) {
vector<std::string> duplicates;
int i = 0;
while (messages[i]) {
-
- // ID present, so note it and point to text.
- const MessageID ident(messages[i++]);
+ // ID present, so point to text.
+ ++i;
if (messages[i]) {
-
+ const MessageID ident(messages[i - 1]);
// Text not null, note it and point to next ident.
- string text(messages[i++]);
+ const std::string text(messages[i]);
+ ++i;
// Add ID and text to message dictionary, noting if the ID was
// already present.
diff --git a/src/lib/log/tests/logger_example.cc b/src/lib/log/tests/logger_example.cc
index daadb7c..729fdac 100644
--- a/src/lib/log/tests/logger_example.cc
+++ b/src/lib/log/tests/logger_example.cc
@@ -215,7 +215,7 @@ int main(int argc, char** argv) {
return (1);
}
try {
- cur_opt.maxsize = boost::lexical_cast<unsigned int>(optarg);
+ cur_opt.maxver = boost::lexical_cast<unsigned int>(optarg);
} catch (boost::bad_lexical_cast&) {
std::cerr << "Maximum version (-m) argument must be a positive "
"integer\n";
diff --git a/src/lib/nsas/tests/hash_unittest.cc b/src/lib/nsas/tests/hash_unittest.cc
index f71d4b3..fe43bfa 100644
--- a/src/lib/nsas/tests/hash_unittest.cc
+++ b/src/lib/nsas/tests/hash_unittest.cc
@@ -26,6 +26,7 @@
#include "nsas_test.h"
using namespace std;
+using namespace isc::dns;
namespace isc {
namespace nsas {
diff --git a/src/lib/nsas/tests/nameserver_address_store_unittest.cc b/src/lib/nsas/tests/nameserver_address_store_unittest.cc
index a606f26..3bde33b 100644
--- a/src/lib/nsas/tests/nameserver_address_store_unittest.cc
+++ b/src/lib/nsas/tests/nameserver_address_store_unittest.cc
@@ -41,8 +41,10 @@
#include "nsas_test.h"
using namespace isc::dns;
+using namespace isc::dns::rdata;
using namespace isc::util;
using namespace std;
+using isc::util::unittests::TestResolver;
namespace isc {
namespace nsas {
diff --git a/src/lib/nsas/tests/nameserver_address_unittest.cc b/src/lib/nsas/tests/nameserver_address_unittest.cc
index 1b211e9..f8512bf 100644
--- a/src/lib/nsas/tests/nameserver_address_unittest.cc
+++ b/src/lib/nsas/tests/nameserver_address_unittest.cc
@@ -26,12 +26,13 @@
#include "../nameserver_entry.h"
#include "nsas_test.h"
+using namespace isc::dns;
+using namespace isc::dns::rdata;
+using isc::util::unittests::TestResolver;
+
namespace isc {
namespace nsas {
-using namespace dns;
-using namespace rdata;
-
#define TEST_ADDRESS_INDEX 1
/// \brief NameserverEntry sample class for testing
diff --git a/src/lib/nsas/tests/nameserver_entry_unittest.cc b/src/lib/nsas/tests/nameserver_entry_unittest.cc
index 10f0c20..dd7f3b9 100644
--- a/src/lib/nsas/tests/nameserver_entry_unittest.cc
+++ b/src/lib/nsas/tests/nameserver_entry_unittest.cc
@@ -41,7 +41,7 @@ using namespace isc::nsas;
using namespace isc::asiolink;
using namespace std;
using namespace isc::dns;
-using namespace rdata;
+using isc::util::unittests::TestResolver;
namespace {
diff --git a/src/lib/nsas/tests/nsas_entry_compare_unittest.cc b/src/lib/nsas/tests/nsas_entry_compare_unittest.cc
index 33e41b8..9bc5b9c 100644
--- a/src/lib/nsas/tests/nsas_entry_compare_unittest.cc
+++ b/src/lib/nsas/tests/nsas_entry_compare_unittest.cc
@@ -25,6 +25,7 @@
#include "../nsas_entry_compare.h"
#include "nsas_test.h"
+using namespace isc::dns;
using namespace std;
namespace isc {
diff --git a/src/lib/nsas/tests/nsas_test.h b/src/lib/nsas/tests/nsas_test.h
index 9f92149..54e5d45 100644
--- a/src/lib/nsas/tests/nsas_test.h
+++ b/src/lib/nsas/tests/nsas_test.h
@@ -38,11 +38,6 @@
#include <dns/rdataclass.h>
#include "../nsas_entry.h"
-using namespace isc::dns::rdata;
-using namespace isc::dns;
-using namespace isc::util;
-using isc::util::unittests::TestResolver;
-
namespace isc {
namespace dns {
@@ -77,15 +72,15 @@ public:
/// For this reason, a single class definition
template <typename T>
-class RdataTest: public Rdata {
+class RdataTest: public rdata::Rdata {
public:
/// \brief Constructor
///
/// Set the data in the object.
///
- /// \param v4address IPV4 address to store. (The format of this address is
- /// not checked.)
+ /// \param v4address IPV4 address to store. (The format of this
+ /// address is not checked.)
RdataTest(const std::string& data) : data_(data)
{}
@@ -99,8 +94,9 @@ public:
/// \brief Return type of Rdata
///
- /// Returns the type of the data. May be useful in the tests, although this
- /// will not appear in the main code as this interface is not defined.
+ /// Returns the type of the data. May be useful in the tests,
+ /// although this will not appear in the main code as this interface
+ /// is not defined.
virtual uint16_t getType() const {
return (type_.getType());
}
@@ -111,13 +107,13 @@ public:
///
//@{
/// \brief Render the \c Rdata in the wire format to a buffer
- virtual void toWire(OutputBuffer& buffer) const;
+ virtual void toWire(isc::util::OutputBuffer& buffer) const;
/// \brief render the \Rdata in the wire format to a \c MessageRenderer
virtual void toWire(AbstractMessageRenderer& renderer) const;
/// \brief Comparison Method
- virtual int compare(const Rdata& other) const;
+ virtual int compare(const rdata::Rdata& other) const;
//@}
private:
@@ -126,7 +122,7 @@ private:
};
template <typename T>
-void RdataTest<T>::toWire(OutputBuffer&) const {
+void RdataTest<T>::toWire(isc::util::OutputBuffer&) const {
}
template <typename T>
@@ -134,7 +130,7 @@ void RdataTest<T>::toWire(AbstractMessageRenderer&) const {
}
template <typename T>
-int RdataTest<T>::compare(const Rdata&) const {
+int RdataTest<T>::compare(const rdata::Rdata&) const {
return 0;
}
@@ -210,7 +206,8 @@ private:
///
/// Some constants used in the various tests.
-static const uint32_t HASHTABLE_DEFAULT_SIZE = 1009; ///< First prime above 1000
+static const uint32_t HASHTABLE_DEFAULT_SIZE = 1009; ///< First prime
+ ///above 1000
// String constants. These should end in a dot.
static const std::string EXAMPLE_CO_UK("example.co.uk.");
@@ -219,63 +216,92 @@ static const std::string MIXED_EXAMPLE_CO_UK("EXAmple.co.uk.");
class TestWithRdata : public ::testing::Test {
protected:
- typedef boost::shared_ptr<RRset> RRsetPtr;
+ typedef boost::shared_ptr<isc::dns::RRset> RRsetPtr;
/// \brief Constructor
///
- /// Initializes the RRsets used in the tests. The RRsets themselves have to
- /// be initialized with the basic data on their construction. The Rdata for
- /// them is added in SetUp().
+ /// Initializes the RRsets used in the tests. The RRsets themselves
+ /// have to be initialized with the basic data on their
+ /// construction. The Rdata for them is added in SetUp().
TestWithRdata() :
- rrv4_(new RRset(Name(EXAMPLE_CO_UK), RRClass::IN(), RRType::A(),
- RRTTL(1200))),
- rrcase_(new RRset(Name(MIXED_EXAMPLE_CO_UK), RRClass::IN(),
- RRType::A(), RRTTL(1200))),
- rrch_(new RRset(Name(EXAMPLE_CO_UK), RRClass::CH(), RRType::A(),
- RRTTL(1200))),
- rrns_(new RRset(Name(EXAMPLE_CO_UK), RRClass::IN(), RRType::NS(),
- RRTTL(1200))),
- rr_single_(new RRset(Name(EXAMPLE_CO_UK), RRClass::IN(),
- RRType::NS(), RRTTL(600))),
- rr_empty_(new RRset(Name(EXAMPLE_CO_UK), RRClass::IN(),
- RRType::NS(), RRTTL(600))),
- rrv6_(new RRset(Name(EXAMPLE_CO_UK), RRClass::IN(),
- RRType::AAAA(), RRTTL(900))),
- rrnet_(new RRset(Name(EXAMPLE_NET), RRClass::IN(), RRType::A(),
- RRTTL(600))),
+ rrv4_(new isc::dns::RRset(isc::dns::Name(EXAMPLE_CO_UK),
+ isc::dns::RRClass::IN(),
+ isc::dns::RRType::A(),
+ isc::dns::RRTTL(1200))),
+ rrcase_(new isc::dns::RRset(isc::dns::Name(MIXED_EXAMPLE_CO_UK),
+ isc::dns::RRClass::IN(),
+ isc::dns::RRType::A(),
+ isc::dns::RRTTL(1200))),
+ rrch_(new isc::dns::RRset(isc::dns::Name(EXAMPLE_CO_UK),
+ isc::dns::RRClass::CH(),
+ isc::dns::RRType::A(),
+ isc::dns::RRTTL(1200))),
+ rrns_(new isc::dns::RRset(isc::dns::Name(EXAMPLE_CO_UK),
+ isc::dns::RRClass::IN(),
+ isc::dns::RRType::NS(),
+ isc::dns::RRTTL(1200))),
+ rr_single_(new isc::dns::RRset(isc::dns::Name(EXAMPLE_CO_UK),
+ isc::dns::RRClass::IN(),
+ isc::dns::RRType::NS(),
+ isc::dns::RRTTL(600))),
+ rr_empty_(new isc::dns::RRset(isc::dns::Name(EXAMPLE_CO_UK),
+ isc::dns::RRClass::IN(),
+ isc::dns::RRType::NS(),
+ isc::dns::RRTTL(600))),
+ rrv6_(new isc::dns::RRset(isc::dns::Name(EXAMPLE_CO_UK),
+ isc::dns::RRClass::IN(),
+ isc::dns::RRType::AAAA(),
+ isc::dns::RRTTL(900))),
+ rrnet_(new isc::dns::RRset(isc::dns::Name(EXAMPLE_NET),
+ isc::dns::RRClass::IN(),
+ isc::dns::RRType::A(),
+ isc::dns::RRTTL(600))),
ns_name_("ns.example.net.")
{}
/// \brief Add Rdata to RRsets
///
- /// The data are added as const pointers to avoid the stricter type checking
- /// applied by the Rdata code. There is no need for it in these tests.
+ /// The data are added as const pointers to avoid the stricter type
+ /// checking applied by the Rdata code. There is no need for it in
+ /// these tests.
virtual void SetUp() {
// A records
- rrv4_->addRdata(ConstRdataPtr(new RdataTest<A>("1.2.3.4")));
- rrv4_->addRdata(ConstRdataPtr(new RdataTest<A>("5.6.7.8")));
- rrv4_->addRdata(ConstRdataPtr(new RdataTest<A>("9.10.11.12")));
+ rrv4_->addRdata(isc::dns::rdata::ConstRdataPtr
+ (new isc::dns::RdataTest<isc::dns::A>("1.2.3.4")));
+ rrv4_->addRdata(isc::dns::rdata::ConstRdataPtr
+ (new isc::dns::RdataTest<isc::dns::A>("5.6.7.8")));
+ rrv4_->addRdata(isc::dns::rdata::ConstRdataPtr
+ (new isc::dns::RdataTest<isc::dns::A>("9.10.11.12")));
// A records
- rrcase_->addRdata(ConstRdataPtr(new RdataTest<A>("13.14.15.16")));
+ rrcase_->addRdata(isc::dns::rdata::ConstRdataPtr
+ (new isc::dns::RdataTest<isc::dns::A>
+ ("13.14.15.16")));
// No idea what Chaosnet address look like other than they are 16 bits
// The fact that they are type A is probably also incorrect.
- rrch_->addRdata(ConstRdataPtr(new RdataTest<A>("1324")));
+ rrch_->addRdata(isc::dns::rdata::ConstRdataPtr
+ (new isc::dns::RdataTest<isc::dns::A>("1324")));
// NS records take a single name
- rrns_->addRdata(rdata::generic::NS("example.fr."));
- rrns_->addRdata(rdata::generic::NS("example.de."));
+ rrns_->addRdata(isc::dns::rdata::generic::NS("example.fr."));
+ rrns_->addRdata(isc::dns::rdata::generic::NS("example.de."));
// Single NS record with 0 TTL
- rr_single_->addRdata(rdata::generic::NS(ns_name_));
+ rr_single_->addRdata(isc::dns::rdata::generic::NS(ns_name_));
// AAAA records
- rrv6_->addRdata(ConstRdataPtr(new RdataTest<AAAA>("2001::1002")));
- rrv6_->addRdata(ConstRdataPtr(new RdataTest<AAAA>("dead:beef:feed::")));
+ rrv6_->addRdata(isc::dns::rdata::ConstRdataPtr
+ (new isc::dns::RdataTest<isc::dns::AAAA>
+ ("2001::1002")));
+ rrv6_->addRdata(isc::dns::rdata::ConstRdataPtr
+ (new isc::dns::RdataTest<isc::dns::AAAA>
+ ("dead:beef:feed::")));
// A record for example.net
- rrnet_->addRdata(ConstRdataPtr(new RdataTest<A>("17.18.18.20")));
+ rrnet_->addRdata(isc::dns::rdata::ConstRdataPtr
+ (new isc::dns::RdataTest<isc::dns::A>
+ ("17.18.18.20")));
}
/// \brief Data for the tests
@@ -287,7 +313,7 @@ protected:
RRsetPtr rr_empty_; ///< NS RRset without any nameservers
RRsetPtr rrv6_; ///< Standard RRset, IN, AAAA, lowercase name
RRsetPtr rrnet_; ///< example.net A RRset
- Name ns_name_; ///< Nameserver name of ns.example.net
+ isc::dns::Name ns_name_; ///< Nameserver name of ns.example.net
};
} // namespace nsas
diff --git a/src/lib/nsas/tests/zone_entry_unittest.cc b/src/lib/nsas/tests/zone_entry_unittest.cc
index d685fbb..04a6d4d 100644
--- a/src/lib/nsas/tests/zone_entry_unittest.cc
+++ b/src/lib/nsas/tests/zone_entry_unittest.cc
@@ -33,9 +33,11 @@
using namespace isc::nsas;
using namespace isc::asiolink;
-using namespace std;
using namespace isc::dns;
+using namespace isc::dns::rdata;
using namespace isc::util;
+using namespace std;
+using isc::util::unittests::TestResolver;
namespace {
diff --git a/src/lib/python/isc/cc/tests/session_test.py b/src/lib/python/isc/cc/tests/session_test.py
index 65afb6c..7b4771c 100644
--- a/src/lib/python/isc/cc/tests/session_test.py
+++ b/src/lib/python/isc/cc/tests/session_test.py
@@ -334,10 +334,10 @@ class testSession(unittest.TestCase):
self.assertEqual(sess._sequence, 1)
self.assertEqual(sess._next_sequence(), 2)
self.assertEqual(sess._sequence, 2)
- sess._sequence = 47805
- self.assertEqual(sess._sequence, 47805)
- self.assertEqual(sess._next_sequence(), 47806)
- self.assertEqual(sess._sequence, 47806)
+ sess._sequence = 56175
+ self.assertEqual(sess._sequence, 56175)
+ self.assertEqual(sess._next_sequence(), 56176)
+ self.assertEqual(sess._sequence, 56176)
def test_group_subscribe(self):
sess = MySession()
diff --git a/src/lib/python/isc/config/config_data.py b/src/lib/python/isc/config/config_data.py
index e2b1357..3683f33 100644
--- a/src/lib/python/isc/config/config_data.py
+++ b/src/lib/python/isc/config/config_data.py
@@ -53,6 +53,22 @@ def spec_part_is_any(spec_part):
return (type(spec_part) == dict and 'item_type' in spec_part and
spec_part['item_type'] == "any")
+def _type_as_string(value):
+ if type(value) == int:
+ return 'integer'
+ elif type(value) == float:
+ return 'real'
+ elif type(value) == bool:
+ return 'boolean'
+ elif type(value) == str:
+ return 'string'
+ elif type(value) == list:
+ return 'list'
+ elif type(value) == dict:
+ return 'map'
+ else:
+ return '<unknown>'
+
def check_type(spec_part, value):
"""Does nothing if the value is of the correct type given the
specification part relevant for the value. Raises an
@@ -65,27 +81,35 @@ def check_type(spec_part, value):
if data_type == "integer":
if type(value) != int:
- raise isc.cc.data.DataTypeError(str(value) + " is not an integer")
+ raise isc.cc.data.DataTypeError('%s is not an integer (%s was passed)' % \
+ (str(value), _type_as_string(value)))
if value > sys.maxsize:
- raise isc.cc.data.DataTypeError(str(value) + " is too large an integer")
+ raise isc.cc.data.DataTypeError('%s is too large an integer' % \
+ (str(value)))
elif data_type == "real":
if type(value) != float:
- raise isc.cc.data.DataTypeError(str(value) + " is not a real")
+ raise isc.cc.data.DataTypeError('%s is not a real (%s was passed)' % \
+ (str(value), _type_as_string(value)))
if float(value) > sys.float_info.max:
- raise isc.cc.data.DataTypeError(str(value) + " is too large a float")
+ raise isc.cc.data.DataTypeError('%s is too large for a float' % \
+ (str(value)))
elif data_type == "boolean" and type(value) != bool:
- raise isc.cc.data.DataTypeError(str(value) + " is not a boolean")
+ raise isc.cc.data.DataTypeError('%s is not a boolean (%s was passed)' % \
+ (str(value), _type_as_string(value)))
elif data_type == "string" and type(value) != str:
- raise isc.cc.data.DataTypeError(str(value) + " is not a string")
+ raise isc.cc.data.DataTypeError('%s is not a string (%s was passed)' % \
+ (str(value), _type_as_string(value)))
elif data_type == "list":
if type(value) != list:
- raise isc.cc.data.DataTypeError(str(value) + " is not a list")
+ raise isc.cc.data.DataTypeError('%s is not a list (%s was passed)' % \
+ (str(value), _type_as_string(value)))
else:
for element in value:
check_type(spec_part['list_item_spec'], element)
elif data_type == "map" and type(value) != dict:
# todo: check types of map contents too
- raise isc.cc.data.DataTypeError(str(value) + " is not a map")
+ raise isc.cc.data.DataTypeError('%s is not a map (%s was passed)' % \
+ (str(value), _type_as_string(value)))
def convert_type(spec_part, value):
"""Convert the given value(type is string) according specification
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 83f22c9..74d13f2 100644
--- a/src/lib/python/isc/config/tests/config_data_test.py
+++ b/src/lib/python/isc/config/tests/config_data_test.py
@@ -667,7 +667,7 @@ class TestMultiConfigData(unittest.TestCase):
'modified': False,
'name': 'Spec22/value9/v92/v92b',
'type': 'integer',
- 'value': 47806
+ 'value': 56176
}
]
maps = self.mcd.get_value_maps("/Spec22/value9")
diff --git a/src/lib/resolve/recursive_query.cc b/src/lib/resolve/recursive_query.cc
index 3c54a78..e0c5a30 100644
--- a/src/lib/resolve/recursive_query.cc
+++ b/src/lib/resolve/recursive_query.cc
@@ -43,6 +43,7 @@
#include <resolve/recursive_query.h>
using namespace isc::dns;
+using namespace isc::nsas;
using namespace isc::util;
using namespace isc::asiolink;
using namespace isc::resolve;
diff --git a/src/lib/resolve/tests/recursive_query_unittest.cc b/src/lib/resolve/tests/recursive_query_unittest.cc
index 48e5a31..dddfab3 100644
--- a/src/lib/resolve/tests/recursive_query_unittest.cc
+++ b/src/lib/resolve/tests/recursive_query_unittest.cc
@@ -31,6 +31,7 @@
#include <exceptions/exceptions.h>
#include <dns/tests/unittest_util.h>
+#include <util/unittests/wiredata.h>
#include <dns/rcode.h>
#include <util/buffer.h>
@@ -57,12 +58,13 @@
#include <asiolink/io_message.h>
#include <asiolink/io_error.h>
-using isc::UnitTestUtil;
using namespace std;
using namespace isc::asiodns;
using namespace isc::asiolink;
using namespace isc::dns;
using namespace isc::util;
+using isc::UnitTestUtil;
+using isc::util::unittests::matchWireData;
using boost::scoped_ptr;
namespace isc {
@@ -361,9 +363,8 @@ protected:
const size_t expected_datasize =
protocol == IPPROTO_UDP ? sizeof(test_data) :
sizeof(test_data) - 2;
- EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, &callback_data_[0],
- callback_data_.size(),
- expected_data, expected_datasize);
+ matchWireData(expected_data, expected_datasize,
+ &callback_data_[0], callback_data_.size());
}
protected:
@@ -377,7 +378,6 @@ protected:
DNSLookup* lookup = NULL,
DNSAnswer* answer = NULL) :
io_(io_service),
- done_(false),
message_(new Message(Message::PARSE)),
answer_message_(new Message(Message::RENDER)),
respbuf_(new OutputBuffer(0)),
@@ -406,7 +406,6 @@ protected:
protected:
IOService& io_;
- bool done_;
private:
// Currently unused; these will be used for testing
@@ -1004,6 +1003,4 @@ TEST_F(RecursiveQueryTest, CachedNS) {
// TODO: add tests that check whether the cache is updated on succesfull
// responses, and not updated on failures.
-
-
}
diff --git a/src/lib/resolve/tests/recursive_query_unittest_2.cc b/src/lib/resolve/tests/recursive_query_unittest_2.cc
index d6019d0..8c2e9e3 100644
--- a/src/lib/resolve/tests/recursive_query_unittest_2.cc
+++ b/src/lib/resolve/tests/recursive_query_unittest_2.cc
@@ -462,7 +462,8 @@ public:
tcp_cumulative_ += length;
bool complete = false;
if (tcp_cumulative_ > 2) {
- uint16_t dns_length = readUint16(tcp_receive_buffer_);
+ uint16_t dns_length = readUint16(tcp_receive_buffer_,
+ sizeof(tcp_receive_buffer_));
complete = ((dns_length + 2) == tcp_cumulative_);
}
diff --git a/src/lib/resolve/tests/recursive_query_unittest_3.cc b/src/lib/resolve/tests/recursive_query_unittest_3.cc
index 7803b88..acf60d6 100644
--- a/src/lib/resolve/tests/recursive_query_unittest_3.cc
+++ b/src/lib/resolve/tests/recursive_query_unittest_3.cc
@@ -336,7 +336,8 @@ public:
tcp_cumulative_ += length;
bool complete = false;
if (tcp_cumulative_ > 2) {
- uint16_t dns_length = readUint16(tcp_receive_buffer_);
+ uint16_t dns_length = readUint16(tcp_receive_buffer_,
+ sizeof(tcp_receive_buffer_));
complete = ((dns_length + 2) == tcp_cumulative_);
}
diff --git a/src/lib/testutils/srv_test.cc b/src/lib/testutils/srv_test.cc
index 7b0b1bb..8b13371 100644
--- a/src/lib/testutils/srv_test.cc
+++ b/src/lib/testutils/srv_test.cc
@@ -72,12 +72,7 @@ SrvTestBase::createRequestPacket(Message& message,
const char* const remote_address,
uint16_t remote_port)
{
- if (context == NULL) {
- message.toWire(request_renderer);
- } else {
- message.toWire(request_renderer, *context);
- }
-
+ message.toWire(request_renderer, context);
endpoint.reset(IOEndpoint::create(protocol, IOAddress(remote_address),
remote_port));
io_sock = (protocol == IPPROTO_UDP) ? &IOSocket::getDummyUDPSocket() :
diff --git a/src/lib/util/io_utilities.h b/src/lib/util/io_utilities.h
index cd35364..4cfa999 100644
--- a/src/lib/util/io_utilities.h
+++ b/src/lib/util/io_utilities.h
@@ -15,6 +15,7 @@
#ifndef IO_UTILITIES_H
#define IO_UTILITIES_H
+#include <exceptions/exceptions.h>
#include <cstddef>
namespace isc {
@@ -28,10 +29,17 @@ namespace util {
/// \param buffer Data buffer at least two bytes long of which the first two
/// bytes are assumed to represent a 16-bit integer in network-byte
/// order.
+/// \param length Length of the data buffer.
///
/// \return Value of 16-bit integer
inline uint16_t
-readUint16(const void* buffer) {
+readUint16(const void* buffer, size_t length) {
+ if (length < sizeof(uint16_t)) {
+ isc_throw(isc::OutOfRange,
+ "Length (" << length << ") of buffer is insufficient " <<
+ "to read a uint16_t");
+ }
+
const uint8_t* byte_buffer = static_cast<const uint8_t*>(buffer);
uint16_t result = (static_cast<uint16_t>(byte_buffer[0])) << 8;
@@ -48,10 +56,17 @@ readUint16(const void* buffer) {
/// \param value 16-bit value to convert
/// \param buffer Data buffer at least two bytes long into which the 16-bit
/// value is written in network-byte order.
+/// \param length Length of the data buffer.
///
/// \return pointer to the next byte after stored value
inline uint8_t*
-writeUint16(uint16_t value, void* buffer) {
+writeUint16(uint16_t value, void* buffer, size_t length) {
+ if (length < sizeof(uint16_t)) {
+ isc_throw(isc::OutOfRange,
+ "Length (" << length << ") of buffer is insufficient " <<
+ "to write a uint16_t");
+ }
+
uint8_t* byte_buffer = static_cast<uint8_t*>(buffer);
byte_buffer[0] = static_cast<uint8_t>((value & 0xff00U) >> 8);
@@ -65,10 +80,17 @@ writeUint16(uint16_t value, void* buffer) {
/// \param buffer Data buffer at least four bytes long of which the first four
/// bytes are assumed to represent a 32-bit integer in network-byte
/// order.
+/// \param length Length of the data buffer.
///
/// \return Value of 32-bit unsigned integer
inline uint32_t
-readUint32(const uint8_t* buffer) {
+readUint32(const uint8_t* buffer, size_t length) {
+ if (length < sizeof(uint32_t)) {
+ isc_throw(isc::OutOfRange,
+ "Length (" << length << ") of buffer is insufficient " <<
+ "to read a uint32_t");
+ }
+
const uint8_t* byte_buffer = static_cast<const uint8_t*>(buffer);
uint32_t result = (static_cast<uint32_t>(byte_buffer[0])) << 24;
@@ -84,10 +106,17 @@ readUint32(const uint8_t* buffer) {
/// \param value 32-bit value to convert
/// \param buffer Data buffer at least four bytes long into which the 32-bit
/// value is written in network-byte order.
+/// \param length Length of the data buffer.
///
/// \return pointer to the next byte after stored value
inline uint8_t*
-writeUint32(uint32_t value, uint8_t* buffer) {
+writeUint32(uint32_t value, uint8_t* buffer, size_t length) {
+ if (length < sizeof(uint32_t)) {
+ isc_throw(isc::OutOfRange,
+ "Length (" << length << ") of buffer is insufficient " <<
+ "to write a uint32_t");
+ }
+
uint8_t* byte_buffer = static_cast<uint8_t*>(buffer);
byte_buffer[0] = static_cast<uint8_t>((value & 0xff000000U) >> 24);
diff --git a/src/lib/util/tests/io_utilities_unittest.cc b/src/lib/util/tests/io_utilities_unittest.cc
index 8e0c232..d7d3b0e 100644
--- a/src/lib/util/tests/io_utilities_unittest.cc
+++ b/src/lib/util/tests/io_utilities_unittest.cc
@@ -42,11 +42,15 @@ TEST(asioutil, readUint16) {
data[0] = i8;
data[1] = j8;
buffer.setPosition(0);
- EXPECT_EQ(buffer.readUint16(), readUint16(data));
+ EXPECT_EQ(buffer.readUint16(), readUint16(data, sizeof(data)));
}
}
}
+TEST(asioutil, readUint16OutOfRange) {
+ uint8_t data;
+ EXPECT_THROW(readUint16(&data, sizeof(data)), isc::OutOfRange);
+}
TEST(asioutil, writeUint16) {
@@ -64,7 +68,7 @@ TEST(asioutil, writeUint16) {
buffer.writeUint16(i16);
// ... and the test data
- writeUint16(i16, test);
+ writeUint16(i16, test, sizeof(test));
// ... and compare
const uint8_t* ref = static_cast<const uint8_t*>(buffer.getData());
@@ -73,6 +77,12 @@ TEST(asioutil, writeUint16) {
}
}
+TEST(asioutil, writeUint16OutOfRange) {
+ uint16_t i16 = 42;
+ uint8_t data;
+ EXPECT_THROW(writeUint16(i16, &data, sizeof(data)), isc::OutOfRange);
+}
+
// test data shared amount readUint32 and writeUint32 tests
const static uint32_t test32[] = {
0,
@@ -93,11 +103,15 @@ TEST(asioutil, readUint32) {
uint32_t tmp = htonl(test32[i]);
memcpy(&data[offset], &tmp, sizeof(uint32_t));
- EXPECT_EQ(test32[i], readUint32(&data[offset]));
+ EXPECT_EQ(test32[i], readUint32(&data[offset], sizeof(uint32_t)));
}
}
}
+TEST(asioutil, readUint32OutOfRange) {
+ uint8_t data[3];
+ EXPECT_THROW(readUint32(data, sizeof(data)), isc::OutOfRange);
+}
TEST(asioutil, writeUint32) {
uint8_t data[8];
@@ -107,7 +121,8 @@ TEST(asioutil, writeUint32) {
// it 4 times.
for (int offset=0; offset < 4; offset++) {
for (int i=0; i < sizeof(test32)/sizeof(uint32_t); i++) {
- uint8_t* ptr = writeUint32(test32[i], &data[offset]);
+ uint8_t* ptr = writeUint32(test32[i], &data[offset],
+ sizeof(uint32_t));
EXPECT_EQ(&data[offset]+sizeof(uint32_t), ptr);
@@ -117,3 +132,9 @@ TEST(asioutil, writeUint32) {
}
}
}
+
+TEST(asioutil, writeUint32OutOfRange) {
+ uint32_t i32 = 28;
+ uint8_t data[3];
+ EXPECT_THROW(writeUint32(i32, data, sizeof(data)), isc::OutOfRange);
+}
diff --git a/tests/lettuce/README b/tests/lettuce/README
index 48c4043..786fe02 100644
--- a/tests/lettuce/README
+++ b/tests/lettuce/README
@@ -21,9 +21,9 @@ it is called python-pip. On FreeBSD the port is devel/py-pip.
Running the tests
-----------------
-At this moment, we have a fixed port for local tests in our setups, port 47806.
+At this moment, we have a fixed port for local tests in our setups, port 56176.
This port must be free. (TODO: can we make this run-time discovered?).
-Port 47805 is used for cmdctl, and must also be available.
+Port 56175 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)
diff --git a/tests/lettuce/configurations/.gitignore b/tests/lettuce/configurations/.gitignore
index 69d136f..f534cf6 100644
--- a/tests/lettuce/configurations/.gitignore
+++ b/tests/lettuce/configurations/.gitignore
@@ -1,2 +1,5 @@
/bindctl_commands.config
/example.org.config
+/generate.config
+/root.config
+/static.config
diff --git a/tests/lettuce/configurations/DO_NOT_USE_127.0.0.1:47807 b/tests/lettuce/configurations/DO_NOT_USE_127.0.0.1:47807
deleted file mode 100644
index d8f0b5f..0000000
--- a/tests/lettuce/configurations/DO_NOT_USE_127.0.0.1:47807
+++ /dev/null
@@ -1,6 +0,0 @@
-Note well:
-
-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/auth/auth_badzone.config.orig b/tests/lettuce/configurations/auth/auth_badzone.config.orig
index f86882a..02e2186 100644
--- a/tests/lettuce/configurations/auth/auth_badzone.config.orig
+++ b/tests/lettuce/configurations/auth/auth_badzone.config.orig
@@ -9,7 +9,7 @@
},
"Auth": {
"listen_on": [{
- "port": 47806,
+ "port": 56176,
"address": "127.0.0.1"
}]
},
diff --git a/tests/lettuce/configurations/auth/auth_basic.config.orig b/tests/lettuce/configurations/auth/auth_basic.config.orig
index 24f615c..e94e73a 100644
--- a/tests/lettuce/configurations/auth/auth_basic.config.orig
+++ b/tests/lettuce/configurations/auth/auth_basic.config.orig
@@ -9,7 +9,7 @@
},
"Auth": {
"listen_on": [ {
- "port": 47806,
+ "port": 56176,
"address": "127.0.0.1"
} ]
},
diff --git a/tests/lettuce/configurations/bindctl/bindctl.config.orig b/tests/lettuce/configurations/bindctl/bindctl.config.orig
index ef0e8e2..51615cb 100644
--- a/tests/lettuce/configurations/bindctl/bindctl.config.orig
+++ b/tests/lettuce/configurations/bindctl/bindctl.config.orig
@@ -10,7 +10,7 @@
"Auth": {
"database_file": "data/example.org.sqlite3",
"listen_on": [ {
- "port": 47806,
+ "port": 56176,
"address": "127.0.0.1"
} ]
},
diff --git a/tests/lettuce/configurations/bindctl_commands.config.orig b/tests/lettuce/configurations/bindctl_commands.config.orig
index 980262b..1b560ad 100644
--- a/tests/lettuce/configurations/bindctl_commands.config.orig
+++ b/tests/lettuce/configurations/bindctl_commands.config.orig
@@ -10,7 +10,7 @@
"Auth": {
"database_file": "data/example.org.sqlite3",
"listen_on": [ {
- "port": 47806,
+ "port": 56176,
"address": "127.0.0.1"
} ]
},
diff --git a/tests/lettuce/configurations/ddns/ddns.config.orig b/tests/lettuce/configurations/ddns/ddns.config.orig
index 02978be..c3bf319 100644
--- a/tests/lettuce/configurations/ddns/ddns.config.orig
+++ b/tests/lettuce/configurations/ddns/ddns.config.orig
@@ -21,7 +21,7 @@
"database_file": "data/ddns/example.org.sqlite3",
"listen_on": [
{
- "port": 47806,
+ "port": 56176,
"address":
"127.0.0.1"
}
diff --git a/tests/lettuce/configurations/ddns/noddns.config.orig b/tests/lettuce/configurations/ddns/noddns.config.orig
index d075924..2b3f9ca 100644
--- a/tests/lettuce/configurations/ddns/noddns.config.orig
+++ b/tests/lettuce/configurations/ddns/noddns.config.orig
@@ -14,7 +14,7 @@
"database_file": "data/ddns/example.org.sqlite3",
"listen_on": [
{
- "port": 47806,
+ "port": 56176,
"address": "127.0.0.1"
}
]
diff --git a/tests/lettuce/configurations/default_user.csv b/tests/lettuce/configurations/default_user.csv
new file mode 100644
index 0000000..e13e194
--- /dev/null
+++ b/tests/lettuce/configurations/default_user.csv
@@ -0,0 +1 @@
+root,bind10
diff --git a/tests/lettuce/configurations/example.org.config.orig b/tests/lettuce/configurations/example.org.config.orig
index 7da6304..005096c 100644
--- a/tests/lettuce/configurations/example.org.config.orig
+++ b/tests/lettuce/configurations/example.org.config.orig
@@ -10,7 +10,7 @@
"Auth": {
"database_file": "data/example.org.sqlite3",
"listen_on": [ {
- "port": 47806,
+ "port": 56176,
"address": "127.0.0.1"
} ]
},
diff --git a/tests/lettuce/configurations/example.org.inmem.config b/tests/lettuce/configurations/example.org.inmem.config
index 2eadedb..a7c4d9f 100644
--- a/tests/lettuce/configurations/example.org.inmem.config
+++ b/tests/lettuce/configurations/example.org.inmem.config
@@ -9,7 +9,7 @@
},
"Auth": {
"listen_on": [{
- "port": 47806,
+ "port": 56176,
"address": "127.0.0.1"
}]
},
diff --git a/tests/lettuce/configurations/example2.org.config b/tests/lettuce/configurations/example2.org.config
index 3bb3330..0425860 100644
--- a/tests/lettuce/configurations/example2.org.config
+++ b/tests/lettuce/configurations/example2.org.config
@@ -11,7 +11,7 @@
"Auth": {
"database_file": "data/example.org.sqlite3",
"listen_on": [ {
- "port": 47807,
+ "port": 56177,
"address": "::1"
} ]
},
diff --git a/tests/lettuce/configurations/generate.config.orig b/tests/lettuce/configurations/generate.config.orig
new file mode 100644
index 0000000..a40d8c2
--- /dev/null
+++ b/tests/lettuce/configurations/generate.config.orig
@@ -0,0 +1,35 @@
+{
+ "version": 3,
+ "Logging": {
+ "loggers": [{
+ "severity": "DEBUG",
+ "name": "*",
+ "debuglevel": 99
+ }]
+ },
+ "Auth": {
+ "listen_on": [{
+ "port": 56176,
+ "address": "127.0.0.1"
+ }]
+ },
+ "data_sources": {
+ "classes": {
+ "IN": [
+ {
+ "type": "MasterFiles",
+ "cache-enable": true,
+ "params": {
+ "example.org": "data/generate.zone"
+ }
+ }
+ ]
+ }
+ },
+ "Init": {
+ "components": {
+ "b10-auth": { "kind": "needed", "special": "auth" },
+ "b10-cmdctl": { "special": "cmdctl", "kind": "needed" }
+ }
+ }
+}
diff --git a/tests/lettuce/configurations/glue.config b/tests/lettuce/configurations/glue.config
index fc301d3..13df1a4 100644
--- a/tests/lettuce/configurations/glue.config
+++ b/tests/lettuce/configurations/glue.config
@@ -9,7 +9,7 @@
},
"Auth": {
"listen_on": [{
- "port": 47806,
+ "port": 56176,
"address": "127.0.0.1"
}]
},
diff --git a/tests/lettuce/configurations/inmemory_over_sqlite3/secondary.conf b/tests/lettuce/configurations/inmemory_over_sqlite3/secondary.conf
index d93a8c6..f49498a 100644
--- a/tests/lettuce/configurations/inmemory_over_sqlite3/secondary.conf
+++ b/tests/lettuce/configurations/inmemory_over_sqlite3/secondary.conf
@@ -9,7 +9,7 @@
},
"Auth": {
"listen_on": [ {
- "port": 47806,
+ "port": 56176,
"address": "127.0.0.1"
} ]
},
diff --git a/tests/lettuce/configurations/ixfr-out/testset1-config.db b/tests/lettuce/configurations/ixfr-out/testset1-config.db
index 38d29a7..9623d8e 100644
--- a/tests/lettuce/configurations/ixfr-out/testset1-config.db
+++ b/tests/lettuce/configurations/ixfr-out/testset1-config.db
@@ -29,11 +29,11 @@
"database_file": "data/ixfr-out/zones.sqlite3",
"listen_on": [
{
- "port": 47806,
+ "port": 56176,
"address": "::"
},
{
- "port": 47806,
+ "port": 56176,
"address": "0.0.0.0"
}
]
diff --git a/tests/lettuce/configurations/multi_instance/multi_auth.config.orig b/tests/lettuce/configurations/multi_instance/multi_auth.config.orig
index 96e25d8..5d9ca68 100644
--- a/tests/lettuce/configurations/multi_instance/multi_auth.config.orig
+++ b/tests/lettuce/configurations/multi_instance/multi_auth.config.orig
@@ -10,7 +10,7 @@
"Auth": {
"database_file": "data/test_nonexistent_db.sqlite3",
"listen_on": [ {
- "port": 47806,
+ "port": 56176,
"address": "127.0.0.1"
} ]
},
diff --git a/tests/lettuce/configurations/no_db_file.config b/tests/lettuce/configurations/no_db_file.config
index 9e6c168..3229238 100644
--- a/tests/lettuce/configurations/no_db_file.config
+++ b/tests/lettuce/configurations/no_db_file.config
@@ -11,7 +11,7 @@
"Auth": {
"database_file": "data/test_nonexistent_db.sqlite3",
"listen_on": [ {
- "port": 47806,
+ "port": 56176,
"address": "127.0.0.1"
} ]
},
diff --git a/tests/lettuce/configurations/nsec3/nsec3_auth.config b/tests/lettuce/configurations/nsec3/nsec3_auth.config
index 5dfffa1..d2a0643 100644
--- a/tests/lettuce/configurations/nsec3/nsec3_auth.config
+++ b/tests/lettuce/configurations/nsec3/nsec3_auth.config
@@ -11,7 +11,7 @@
"Auth": {
"datasources": [
{"zones": [{"origin": "example.", "file": "configurations/nsec3/rfc5155-example.zone.signed"}], "type": "memory"}],
- "listen_on": [{"port": 47806, "address": "0.0.0.0"}
+ "listen_on": [{"port": 56176, "address": "0.0.0.0"}
]
},
"data_sources": {
diff --git a/tests/lettuce/configurations/resolver/resolver_basic.config.orig b/tests/lettuce/configurations/resolver/resolver_basic.config.orig
index fe5ddd0..7f7004f 100644
--- a/tests/lettuce/configurations/resolver/resolver_basic.config.orig
+++ b/tests/lettuce/configurations/resolver/resolver_basic.config.orig
@@ -13,7 +13,7 @@
"from": "127.0.0.1"
} ],
"listen_on": [ {
- "port": 47806,
+ "port": 56176,
"address": "127.0.0.1"
} ]
},
diff --git a/tests/lettuce/configurations/root.config.orig b/tests/lettuce/configurations/root.config.orig
new file mode 100644
index 0000000..282b7d1
--- /dev/null
+++ b/tests/lettuce/configurations/root.config.orig
@@ -0,0 +1,40 @@
+{
+ "version": 3,
+ "Logging": {
+ "loggers": [ {
+ "debuglevel": 99,
+ "severity": "DEBUG",
+ "name": "*"
+ } ]
+ },
+ "Auth": {
+ "database_file": "data/root.sqlite3",
+ "listen_on": [ {
+ "port": 56176,
+ "address": "127.0.0.1"
+ } ]
+ },
+ "data_sources": {
+ "classes": {
+ "IN": [
+ {
+ "type": "sqlite3",
+ "cache-enable": false,
+ "params": {
+ "database_file": "data/root.sqlite3"
+ }
+ }
+ ]
+ }
+ },
+ "Stats": {
+ "poll-interval": 60
+ },
+ "Init": {
+ "components": {
+ "b10-auth": { "kind": "needed", "special": "auth" },
+ "b10-stats": { "address": "Stats", "kind": "dispensable" },
+ "b10-cmdctl": { "special": "cmdctl", "kind": "needed" }
+ }
+ }
+}
diff --git a/tests/lettuce/configurations/static.config.orig b/tests/lettuce/configurations/static.config.orig
new file mode 100644
index 0000000..3f20490
--- /dev/null
+++ b/tests/lettuce/configurations/static.config.orig
@@ -0,0 +1,36 @@
+{
+ "version": 3,
+ "Logging": {
+ "loggers": [{
+ "severity": "DEBUG",
+ "name": "*",
+ "debuglevel": 99
+ }]
+ },
+ "Auth": {
+ "listen_on": [{
+ "port": 56176,
+ "address": "127.0.0.1"
+ }]
+ },
+ "data_sources": {
+ "classes": {
+ "IN": [
+ {
+ "type": "MasterFiles",
+ "cache-enable": true,
+ "params": {
+ "example.org": "data/example.org"
+ }
+ }
+ ]
+ }
+ },
+ "Init": {
+ "components": {
+ "b10-auth": { "kind": "needed", "special": "auth" },
+ "b10-stats": { "address": "Stats", "kind": "dispensable" },
+ "b10-cmdctl": { "special": "cmdctl", "kind": "needed" }
+ }
+ }
+}
diff --git a/tests/lettuce/configurations/xfrin/inmem_slave.conf b/tests/lettuce/configurations/xfrin/inmem_slave.conf
index fedf372..4f6ac17 100644
--- a/tests/lettuce/configurations/xfrin/inmem_slave.conf
+++ b/tests/lettuce/configurations/xfrin/inmem_slave.conf
@@ -11,7 +11,7 @@
"database_file": "data/inmem-xfrin.sqlite3",
"listen_on": [ {
"address": "::1",
- "port": 47806
+ "port": 56176
} ]
},
"data_sources": {
diff --git a/tests/lettuce/configurations/xfrin/retransfer_master.conf.orig b/tests/lettuce/configurations/xfrin/retransfer_master.conf.orig
index d7ea9a5..f3529fc 100644
--- a/tests/lettuce/configurations/xfrin/retransfer_master.conf.orig
+++ b/tests/lettuce/configurations/xfrin/retransfer_master.conf.orig
@@ -11,7 +11,7 @@
"database_file": "data/example.org.sqlite3",
"listen_on": [ {
"address": "::1",
- "port": 47807
+ "port": 56177
} ]
},
"data_sources": {
@@ -30,7 +30,7 @@
} ],
"also_notify": [ {
"address": "::1",
- "port": 47806
+ "port": 56176
} ]
},
"Init": {
diff --git a/tests/lettuce/configurations/xfrin/retransfer_master_diffs.conf b/tests/lettuce/configurations/xfrin/retransfer_master_diffs.conf
index 34d2d03..d25f69c 100644
--- a/tests/lettuce/configurations/xfrin/retransfer_master_diffs.conf
+++ b/tests/lettuce/configurations/xfrin/retransfer_master_diffs.conf
@@ -11,7 +11,7 @@
"database_file": "data/xfrin-diffs.sqlite3",
"listen_on": [ {
"address": "::1",
- "port": 47807
+ "port": 56177
} ]
},
"data_sources": {
@@ -30,7 +30,7 @@
} ],
"also_notify": [ {
"address": "::1",
- "port": 47806
+ "port": 56176
} ]
},
"Stats": {
diff --git a/tests/lettuce/configurations/xfrin/retransfer_master_nons.conf.orig b/tests/lettuce/configurations/xfrin/retransfer_master_nons.conf.orig
index bccadf7..ecb9e05 100644
--- a/tests/lettuce/configurations/xfrin/retransfer_master_nons.conf.orig
+++ b/tests/lettuce/configurations/xfrin/retransfer_master_nons.conf.orig
@@ -11,7 +11,7 @@
"database_file": "data/example.org-nons.sqlite3",
"listen_on": [ {
"address": "::1",
- "port": 47807
+ "port": 56177
} ]
},
"data_sources": {
@@ -30,7 +30,7 @@
} ],
"also_notify": [ {
"address": "::1",
- "port": 47806
+ "port": 56176
} ]
},
"Stats": {
diff --git a/tests/lettuce/configurations/xfrin/retransfer_master_v4.conf.orig b/tests/lettuce/configurations/xfrin/retransfer_master_v4.conf.orig
index 755c91b..5eca05f 100644
--- a/tests/lettuce/configurations/xfrin/retransfer_master_v4.conf.orig
+++ b/tests/lettuce/configurations/xfrin/retransfer_master_v4.conf.orig
@@ -11,7 +11,7 @@
"database_file": "data/example.org.sqlite3",
"listen_on": [ {
"address": "127.0.0.1",
- "port": 47809
+ "port": 56179
} ]
},
"data_sources": {
@@ -30,7 +30,7 @@
} ],
"also_notify": [ {
"address": "127.0.0.1",
- "port": 47806
+ "port": 56176
} ]
},
"Init": {
diff --git a/tests/lettuce/configurations/xfrin/retransfer_slave.conf.orig b/tests/lettuce/configurations/xfrin/retransfer_slave.conf.orig
index aff8218..6c32f48 100644
--- a/tests/lettuce/configurations/xfrin/retransfer_slave.conf.orig
+++ b/tests/lettuce/configurations/xfrin/retransfer_slave.conf.orig
@@ -10,7 +10,7 @@
"Auth": {
"listen_on": [ {
"address": "::1",
- "port": 47806
+ "port": 56176
} ]
},
"data_sources": {
diff --git a/tests/lettuce/configurations/xfrin/retransfer_slave_diffs.conf b/tests/lettuce/configurations/xfrin/retransfer_slave_diffs.conf
index 24bfa2a..acaf921 100644
--- a/tests/lettuce/configurations/xfrin/retransfer_slave_diffs.conf
+++ b/tests/lettuce/configurations/xfrin/retransfer_slave_diffs.conf
@@ -10,14 +10,14 @@
"Auth": {
"listen_on": [ {
"address": "::1",
- "port": 47806
+ "port": 56176
} ]
},
"Xfrin": {
"zones": [ {
"name": "example",
"master_addr": "::1",
- "master_port": 47807
+ "master_port": 56177
} ]
},
"data_sources": {
diff --git a/tests/lettuce/configurations/xfrin/retransfer_slave_notify.conf.orig b/tests/lettuce/configurations/xfrin/retransfer_slave_notify.conf.orig
index c4ba1ef..4076ea5 100644
--- a/tests/lettuce/configurations/xfrin/retransfer_slave_notify.conf.orig
+++ b/tests/lettuce/configurations/xfrin/retransfer_slave_notify.conf.orig
@@ -10,7 +10,7 @@
"Auth": {
"listen_on": [ {
"address": "::1",
- "port": 47806
+ "port": 56176
} ]
},
"data_sources": {
@@ -27,7 +27,7 @@
"zones": [ {
"name": "example.org",
"master_addr": "::1",
- "master_port": 47807,
+ "master_port": 56177,
"request_ixfr": "no"
} ]
},
diff --git a/tests/lettuce/configurations/xfrin/retransfer_slave_notify_v4.conf b/tests/lettuce/configurations/xfrin/retransfer_slave_notify_v4.conf
index b99f3f7..5d44a00 100644
--- a/tests/lettuce/configurations/xfrin/retransfer_slave_notify_v4.conf
+++ b/tests/lettuce/configurations/xfrin/retransfer_slave_notify_v4.conf
@@ -10,7 +10,7 @@
"Auth": {
"listen_on": [ {
"address": "127.0.0.1",
- "port": 47806
+ "port": 56176
} ]
},
"data_sources": {
@@ -27,7 +27,7 @@
"zones": [ {
"name": "example.org",
"master_addr": "127.0.0.1",
- "master_port": 47809,
+ "master_port": 56179,
"request_ixfr": "no"
} ]
},
diff --git a/tests/lettuce/configurations/xfrout_master.conf b/tests/lettuce/configurations/xfrout_master.conf
index 755e698..da5381c 100644
--- a/tests/lettuce/configurations/xfrout_master.conf
+++ b/tests/lettuce/configurations/xfrout_master.conf
@@ -11,7 +11,7 @@
"database_file": "data/xfrout.sqlite3",
"listen_on": [ {
"address": "::1",
- "port": 47806
+ "port": 56176
} ]
},
"data_sources": {
diff --git a/tests/lettuce/data/.gitignore b/tests/lettuce/data/.gitignore
index 946228c..da78834 100644
--- a/tests/lettuce/data/.gitignore
+++ b/tests/lettuce/data/.gitignore
@@ -1,4 +1,5 @@
/inmem-xfrin.sqlite3
+/root.sqlite3
/test_nonexistent_db.sqlite3
/xfrin-before-diffs.sqlite3
/xfrin-notify.sqlite3
diff --git a/tests/lettuce/data/generate.zone b/tests/lettuce/data/generate.zone
new file mode 100644
index 0000000..f558372
--- /dev/null
+++ b/tests/lettuce/data/generate.zone
@@ -0,0 +1,4 @@
+$ORIGIN example.org.
+example.org. 3600 IN SOA ns1.example.org. admin.example.org. 12341 3600 1800 2419200 7200
+$GENERATE 1-2 @ NS ns$.example.org.
+$GENERATE 1-4 host$ A 192.0.2.$
diff --git a/tests/lettuce/data/root.sqlite3.orig b/tests/lettuce/data/root.sqlite3.orig
new file mode 100644
index 0000000..1bef761
Binary files /dev/null and b/tests/lettuce/data/root.sqlite3.orig differ
diff --git a/tests/lettuce/data/static.zone b/tests/lettuce/data/static.zone
new file mode 100644
index 0000000..51525db
--- /dev/null
+++ b/tests/lettuce/data/static.zone
@@ -0,0 +1,3 @@
+BIND. 3600 CH SOA BIND. BIND. 3 3600 300 36000 3600
+BIND. 3600 CH NS BIND.
+VERSION.BIND. 3600 CH TXT "10"
diff --git a/tests/lettuce/features/ddns_system.feature b/tests/lettuce/features/ddns_system.feature
index 6747b53..9b3f116 100644
--- a/tests/lettuce/features/ddns_system.feature
+++ b/tests/lettuce/features/ddns_system.feature
@@ -164,16 +164,16 @@ Feature: DDNS System
# And wait for primary stderr message XFROUT_STARTED
# And wait for primary stderr message DDNS_STARTED
- # And I start bind10 with configuration example2.org.config with cmdctl port 47804 as secondary
+ # And I start bind10 with configuration example2.org.config with cmdctl port 56174 as secondary
# And wait for secondary stderr message AUTH_SERVER_STARTED
# And wait for secondary stderr message XFRIN_STARTED
# # Sanity check
# The SOA serial for example.org should be 1234
- # The SOA serial for example.org at 127.0.0.1:47807 should be 1234
+ # The SOA serial for example.org at 127.0.0.1:56177 should be 1234
# When I use DDNS to set the SOA serial to 1235
# The DDNS response should be SUCCESS
# The SOA serial for example.org should be 1235
- # The SOA serial for example.org at 127.0.0.1:47807 should be 1235
+ # The SOA serial for example.org at 127.0.0.1:56177 should be 1235
diff --git a/tests/lettuce/features/example.feature b/tests/lettuce/features/example.feature
index 48cef20..0a11a16 100644
--- a/tests/lettuce/features/example.feature
+++ b/tests/lettuce/features/example.feature
@@ -129,8 +129,8 @@ Feature: Example feature
# where
A query for www.example.org class CH should have rcode REFUSED
A query for www.example.org to 127.0.0.1 should have rcode NOERROR
- A query for www.example.org to 127.0.0.1:47806 should have rcode NOERROR
- A query for www.example.org type A class IN to 127.0.0.1:47806 should have rcode NOERROR
+ A query for www.example.org to 127.0.0.1:56176 should have rcode NOERROR
+ A query for www.example.org type A class IN to 127.0.0.1:56176 should have rcode NOERROR
Scenario: example.org mixed-case query
# This scenario performs a mixed-case query and checks that the
@@ -207,19 +207,19 @@ Feature: Example feature
And wait for bind10_one stderr message CMDCTL_STARTED
And wait for bind10_one stderr message AUTH_SERVER_STARTED
- And I start bind10 with configuration example2.org.config with cmdctl port 47804 as bind10_two
+ And I start bind10 with configuration example2.org.config with cmdctl port 56174 as bind10_two
And wait for bind10_two stderr message BIND10_STARTED_CC
And wait for bind10_two stderr message CMDCTL_STARTED
And wait for bind10_two stderr message AUTH_SERVER_STARTED
- A query for www.example.org to 127.0.0.1:47806 should have rcode NOERROR
- A query for www.example.org to [::1]:47807 should have rcode NOERROR
+ A query for www.example.org to 127.0.0.1:56176 should have rcode NOERROR
+ A query for www.example.org to [::1]:56177 should have rcode NOERROR
The SOA serial for example.org should be 1234
- The SOA serial for example.org at 127.0.0.1:47806 should be 1234
- The SOA serial for example.org at ::1:47807 should be 1234
+ The SOA serial for example.org at 127.0.0.1:56176 should be 1234
+ The SOA serial for example.org at ::1:56177 should be 1234
Then set bind10 configuration data_sources/classes/IN[0]/params to {"database_file": "data/empty_db.sqlite3"}
And wait for bind10_one stderr message DATASRC_SQLITE_CONNOPEN
- A query for www.example.org to 127.0.0.1:47806 should have rcode REFUSED
- A query for www.example.org to [::1]:47807 should have rcode NOERROR
+ A query for www.example.org to 127.0.0.1:56176 should have rcode REFUSED
+ A query for www.example.org to [::1]:56177 should have rcode NOERROR
diff --git a/tests/lettuce/features/inmemory_over_sqlite3.feature b/tests/lettuce/features/inmemory_over_sqlite3.feature
index 7d8ad6d..2f230b9 100644
--- a/tests/lettuce/features/inmemory_over_sqlite3.feature
+++ b/tests/lettuce/features/inmemory_over_sqlite3.feature
@@ -12,7 +12,7 @@ Feature: In-memory zone using SQLite3 backend
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
+ Given I have bind10 running with configuration xfrin/retransfer_master.conf with cmdctl port 56174 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
@@ -26,18 +26,18 @@ Feature: In-memory zone using SQLite3 backend
And wait for bind10 stderr message XFRIN_STARTED
And wait for bind10 stderr message ZONEMGR_STARTED
- A query for www.example.org to [::1]:47806 should have rcode NOERROR
+ A query for www.example.org to [::1]:56176 should have rcode NOERROR
"""
www.example.org. 3600 IN A 192.0.2.63
"""
- A query for mail.example.org to [::1]:47806 should have rcode NXDOMAIN
- When I send bind10 the command Xfrin retransfer example.org IN ::1 47807
+ A query for mail.example.org to [::1]:56176 should have rcode NXDOMAIN
+ When I send bind10 the command Xfrin retransfer example.org IN ::1 56177
Then wait for new bind10 stderr message XFRIN_TRANSFER_SUCCESS not XFRIN_XFR_PROCESS_FAILURE
Then wait for new bind10 stderr message AUTH_DATASRC_CLIENTS_BUILDER_LOAD_ZONE
- A query for www.example.org to [::1]:47807 should have rcode NOERROR
+ A query for www.example.org to [::1]:56177 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 to [::1]:47806 should have rcode NOERROR
+ A query for mail.example.org to [::1]:56176 should have rcode NOERROR
diff --git a/tests/lettuce/features/master_loader.feature b/tests/lettuce/features/master_loader.feature
new file mode 100644
index 0000000..4706b32
--- /dev/null
+++ b/tests/lettuce/features/master_loader.feature
@@ -0,0 +1,50 @@
+Feature: Master loader feature
+ This feature is a collection of tests for the zone file loader in
+ BIND 10.
+
+ Scenario: $GENERATE support
+ Given I have bind10 running with configuration generate.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
+
+ bind10 module Auth should be running
+ And bind10 module Resolver should not be running
+ And bind10 module Xfrout should not be running
+ And bind10 module Zonemgr should not be running
+ And bind10 module Xfrin should not be running
+ And bind10 module Stats should not be running
+ And bind10 module StatsHttpd should not be running
+
+ A query for www.example.org should have rcode NXDOMAIN
+ The SOA serial for example.org should be 12341
+
+ A query for host0.example.org should have rcode NXDOMAIN
+ A query for host1.example.org should have rcode NOERROR
+ The answer section of the last query response should be
+ """
+ host1.example.org. 3600 IN A 192.0.2.1
+ """
+ A query for host2.example.org should have rcode NOERROR
+ The answer section of the last query response should be
+ """
+ host2.example.org. 3600 IN A 192.0.2.2
+ """
+ A query for host3.example.org should have rcode NOERROR
+ The answer section of the last query response should be
+ """
+ host3.example.org. 3600 IN A 192.0.2.3
+ """
+ A query for host4.example.org should have rcode NOERROR
+ The answer section of the last query response should be
+ """
+ host4.example.org. 3600 IN A 192.0.2.4
+ """
+ A query for host5.example.org should have rcode NXDOMAIN
+
+ A query for example.org type NS should have rcode NOERROR
+ The answer section of the last query response should be
+ """
+ example.org. 3600 IN NS ns1.example.org.
+ example.org. 3600 IN NS ns2.example.org.
+ """
diff --git a/tests/lettuce/features/queries.feature b/tests/lettuce/features/queries.feature
index 2db6c3e..2404256 100644
--- a/tests/lettuce/features/queries.feature
+++ b/tests/lettuce/features/queries.feature
@@ -331,6 +331,28 @@ Feature: Querying feature
| qryreferral | 1 |
| rcode.noerror | 1 |
+ Scenario: RRSIG query
+ # Directly querying for RRSIGs should result in rcode=REFUSED.
+ Given I have bind10 running with configuration nsec3/nsec3_auth.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
+
+ bind10 module Auth should be running
+ And bind10 module Resolver should not be running
+ And bind10 module Xfrout should not be running
+ And bind10 module Zonemgr should not be running
+ And bind10 module Xfrin should not be running
+ And bind10 module Stats should not be running
+ And bind10 module StatsHttpd should not be running
+
+ A dnssec query for example. type RRSIG should have rcode REFUSED
+ The last query response should have flags qr aa
+ The last query response should have edns_flags do
+ The last query response should have ancount 0
+ The last query response should have nscount 0
+ The last query response should have adcount 1
+
Scenario: SSHFP query
# We are testing one more RR type for a normal successful case
Given I have bind10 running with configuration example.org.inmem.config
@@ -412,3 +434,61 @@ Feature: Querying feature
| qryauthans | 2 |
| qrynxrrset | 1 |
| rcode.noerror | 2 |
+
+ Scenario: Querying non-existing name in root zone from sqlite3 should work
+ Given I have bind10 running with configuration root.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
+
+ bind10 module Auth should be running
+ And bind10 module Stats should be running
+ And bind10 module Resolver should not be running
+ And bind10 module Xfrout should not be running
+ And bind10 module Zonemgr should not be running
+ And bind10 module Xfrin should not be running
+ And bind10 module StatsHttpd should not be running
+
+ A query for . type SOA should have rcode NOERROR
+ A query for nonexistent. type A should have rcode NXDOMAIN
+ Then wait for bind10 stderr message AUTH_SEND_NORMAL_RESPONSE not AUTH_PROCESS_FAIL
+
+ Scenario: CH class static zone query
+ # We are testing one more RR type for a normal successful case
+ Given I have bind10 running with configuration static.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
+
+ bind10 module Auth should be running
+ And bind10 module Stats should be running
+ And bind10 module Resolver should not be running
+ And bind10 module Xfrout should not be running
+ And bind10 module Zonemgr should not be running
+ And bind10 module Xfrin should not be running
+ And bind10 module StatsHttpd should not be running
+
+ A query for version.bind. type TXT class CH should have rcode REFUSED
+
+ When I send bind10 the following commands
+ """
+ config add data_sources/classes/CH
+ config set data_sources/classes/CH[0]/type MasterFiles
+ config set data_sources/classes/CH[0]/cache-enable true
+ config set data_sources/classes/CH[0]/params {"BIND": "data/static.zone"}
+ config commit
+ """
+
+ And wait for new bind10 stderr message AUTH_DATASRC_CLIENTS_BUILDER_RECONFIGURE_SUCCESS
+
+ A query for version.bind. type TXT class CH should have rcode NOERROR
+ The last query response should have ancount 1
+
+ # NOTE: The double double-quote characters trailing 10 in the
+ # response below are required due to a lettuce bug in reading
+ # multi-line strings with embedded double-quotes.
+
+ The answer section of the last query response should be
+ """
+ version.bind. 3600 CH TXT "10""
+ """
diff --git a/tests/lettuce/features/terrain/bind10_control.py b/tests/lettuce/features/terrain/bind10_control.py
index e288aa9..97f582c 100644
--- a/tests/lettuce/features/terrain/bind10_control.py
+++ b/tests/lettuce/features/terrain/bind10_control.py
@@ -40,7 +40,7 @@ def start_bind10(step, config_file, cmdctl_port, msgq_sockfile, process_name):
will be used. The path is relative to the base lettuce
directory.
cmdctl_port ('with cmdctl port <portnr>', optional): The port on which
- b10-cmdctl listens for bindctl commands. Defaults to 47805.
+ b10-cmdctl listens for bindctl commands. Defaults to 56175.
msgq_sockfile ('with msgq socket file', optional): The msgq socket file
that will be used for internal communication
process_name ('as <name>', optional). This is the name that can be used
@@ -59,7 +59,7 @@ def start_bind10(step, config_file, cmdctl_port, msgq_sockfile, process_name):
args.append('-c')
args.append(config_file)
if cmdctl_port is None:
- args.append('--cmdctl-port=47805')
+ args.append('--cmdctl-port=56175')
else:
args.append('--cmdctl-port=' + cmdctl_port)
if process_name is None:
@@ -126,7 +126,7 @@ def run_bindctl(commands, cmdctl_port=None, ignore_failure=False):
commands: a sequence of strings which will be sent.
cmdctl_port: a port number on which cmdctl is listening, is converted
to string if necessary. If not provided, or None, defaults
- to 47805
+ to 56175
ignore_failure(bool): if set to True, don't examin the result code
of bindctl and assert it succeeds.
@@ -135,8 +135,9 @@ def run_bindctl(commands, cmdctl_port=None, ignore_failure=False):
Fails if the return code is not 0
"""
if cmdctl_port is None:
- cmdctl_port = 47805
- args = ['bindctl', '-p', str(cmdctl_port)]
+ cmdctl_port = 56175
+
+ args = ['bindctl', '-p', str(cmdctl_port), '--csv-file-dir=configurations/' ]
bindctl = subprocess.Popen(args, 1, None, subprocess.PIPE,
subprocess.PIPE, None)
for line in commands:
@@ -268,7 +269,7 @@ def config_set_command(step, name, value, cmdctl_port):
name ('configuration <name>'): Identifier of the configuration to set
value ('to <value>'): value to set it to.
cmdctl_port ('with cmdctl port <portnr>', optional): cmdctl port to send
- the command to. Defaults to 47805.
+ the command to. Defaults to 56175.
Fails if cmdctl does not exit with status code 0.
"""
commands = ["config set " + name + " " + value,
@@ -282,7 +283,7 @@ def send_multiple_commands(step, cmdctl_port):
Run bindctl, and send it the given multiline set of commands.
A quit command is always appended.
cmdctl_port ('with cmdctl port <portnr>', optional): cmdctl port to send
- the command to. Defaults to 47805.
+ the command to. Defaults to 56175.
Fails if cmdctl does not exit with status code 0.
"""
commands = step.multiline.split("\n")
@@ -299,7 +300,7 @@ def config_remove_command(step, name, value, cmdctl_port):
value ('value <value>'): if name is a named set, use value to identify
item to remove
cmdctl_port ('with cmdctl port <portnr>', optional): cmdctl port to send
- the command to. Defaults to 47805.
+ the command to. Defaults to 56175.
Fails if cmdctl does not exit with status code 0.
"""
cmd = "config remove " + name
@@ -317,7 +318,7 @@ def send_command(step, cmdctl_port, ignore_failure, command):
Parameters:
command ('the command <command>'): The command to send.
cmdctl_port ('with cmdctl port <portnr>', optional): cmdctl port to send
- the command to. Defaults to 47805.
+ the command to. Defaults to 56175.
ignore_failure ('ignoring failure', optional): set to None if bindctl
is expected to succeed (normal case, which is the default); if it is
not None, it means bindctl is expected to fail (and it's acceptable).
diff --git a/tests/lettuce/features/terrain/nsupdate.py b/tests/lettuce/features/terrain/nsupdate.py
index 26964f5..78e89ee 100644
--- a/tests/lettuce/features/terrain/nsupdate.py
+++ b/tests/lettuce/features/terrain/nsupdate.py
@@ -106,7 +106,7 @@ def prepare_update(step, zone, server, port):
if port is not None:
commands[0] = commands[0] + " " + port
else:
- commands[0] = commands[0] + " 47806"
+ commands[0] = commands[0] + " 56176"
if zone is not None:
commands.append("zone " + zone)
world.nsupdate_commands = commands
diff --git a/tests/lettuce/features/terrain/querying.py b/tests/lettuce/features/terrain/querying.py
index d585e5e..1ba0b93 100644
--- a/tests/lettuce/features/terrain/querying.py
+++ b/tests/lettuce/features/terrain/querying.py
@@ -18,7 +18,7 @@
#
# query for <name> [type X] [class X] [to <addr>[:port]] should have rcode <rc>
#
-# By default, it will send queries to 127.0.0.1:47806 unless specified
+# By default, it will send queries to 127.0.0.1:56176 unless specified
# otherwise. The rcode is always checked. If the result is not NO_ANSWER,
# the result will be stored in last_query_result, which can then be inspected
# more closely, for instance with the step
@@ -221,7 +221,7 @@ def query(step, dnssec, recursive, query_name, qtype, qclass, addr, port,
addr ('to <address>', optional): The IP address of the nameserver to query.
Defaults to 127.0.0.1.
port (':<port>', optional): The port number of the nameserver to query.
- Defaults to 47806.
+ Defaults to 56176.
rcode ('should have rcode <rcode>'): The expected rcode of the answer.
"""
if qtype is None:
@@ -232,7 +232,7 @@ def query(step, dnssec, recursive, query_name, qtype, qclass, addr, port,
addr = "127.0.0.1"
addr = re.sub(r"\[(.+)\]", r"\1", addr) # convert [IPv6_addr] to IPv6_addr
if port is None:
- port = 47806
+ port = 56176
additional_arguments = []
if dnssec is not None:
additional_arguments.append("+dnssec")
@@ -254,7 +254,7 @@ def query(step, dnssec, recursive, query_name, qtype, qclass, addr, port,
def query_soa(step, query_name, address, port, serial=None):
"""
Convenience function to check the SOA SERIAL value of the given zone at
- the nameserver at the default address (127.0.0.1:47806).
+ the nameserver at the default address (127.0.0.1:56176).
Parameters:
query_name ('for <name>'): The zone to find the SOA record for.
serial ('should be <number>'): The expected value of the SOA SERIAL.
@@ -264,7 +264,7 @@ def query_soa(step, query_name, address, port, serial=None):
if address is None:
address = "127.0.0.1"
if port is None:
- port = "47806"
+ port = "56176"
query_result = QueryResult(query_name, "SOA", "IN", address, port)
assert "NOERROR" == query_result.rcode,\
"Got " + query_result.rcode + ", expected NOERROR"
diff --git a/tests/lettuce/features/terrain/terrain.py b/tests/lettuce/features/terrain/terrain.py
index d0ba4fe..c9afb17 100644
--- a/tests/lettuce/features/terrain/terrain.py
+++ b/tests/lettuce/features/terrain/terrain.py
@@ -1,5 +1,4 @@
-
-# Copyright (C) 2011 Internet Systems Consortium.
+# Copyright (C) 2011-2014 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
@@ -52,6 +51,8 @@ copylist = [
"configurations/bindctl_commands.config"],
["configurations/example.org.config.orig",
"configurations/example.org.config"],
+ ["configurations/generate.config.orig",
+ "configurations/generate.config"],
["configurations/bindctl/bindctl.config.orig",
"configurations/bindctl/bindctl.config"],
["configurations/auth/auth_basic.config.orig",
@@ -76,8 +77,14 @@ copylist = [
"configurations/xfrin/retransfer_slave.conf"],
["configurations/xfrin/retransfer_slave_notify.conf.orig",
"configurations/xfrin/retransfer_slave_notify.conf"],
+ ["configurations/root.config.orig",
+ "configurations/root.config"],
+ ["configurations/static.config.orig",
+ "configurations/static.config"],
["data/inmem-xfrin.sqlite3.orig",
"data/inmem-xfrin.sqlite3"],
+ ["data/root.sqlite3.orig",
+ "data/root.sqlite3"],
["data/xfrin-before-diffs.sqlite3.orig",
"data/xfrin-before-diffs.sqlite3"],
["data/xfrin-notify.sqlite3.orig",
diff --git a/tests/lettuce/features/terrain/transfer.py b/tests/lettuce/features/terrain/transfer.py
index 0983de5..5f21991 100644
--- a/tests/lettuce/features/terrain/transfer.py
+++ b/tests/lettuce/features/terrain/transfer.py
@@ -61,11 +61,11 @@ class TransferResult(object):
def parse_addr_port(address, port):
if address is None:
- address = "::1" # default address
+ address = "::1" # default address
# convert [IPv6_addr] to IPv6_addr:
address = re.sub(r"\[(.+)\]", r"\1", address)
if port is None:
- port = 47806 # default port
+ port = 56176 # default port
return (address, port)
@step('An AXFR transfer of ([\w.]+)(?: from ([\d.]+|\[[0-9a-fA-F:]+\])(?::([0-9]+))?)?')
@@ -78,19 +78,19 @@ def perform_axfr(step, zone_name, address, port):
An AXFR transfer of <zone_name> [from <address>:<port>]
Address defaults to ::1
- Port defaults to 47806
+ Port defaults to 56176
"""
(address, port) = parse_addr_port(address, port)
args = [ 'dig', 'AXFR', '@' + str(address), '-p', str(port), zone_name ]
world.transfer_result = TransferResult(args)
- at step('A customized AXFR transfer of ([\w.]+)(?: from ([\d.]+|\[[0-9a-fA-F:]+\])(?::([0-9]+))?)?(?: with pose of (\d+) seconds?)?')
+ at step('A customized AXFR transfer of ([\w.]+)(?: from ([\d.]+|\[[0-9a-fA-F:]+\])(?::([0-9]+))?)?(?: with pause of (\d+) seconds?)?')
def perform_custom_axfr(step, zone_name, address, port, delay):
"""Checks AXFR transfer, and store the result in the form of internal
CustomTransferResult class, which is compatible with TransferResult.
Step definition:
- A customized AXFR transfer of <zone_name> [from <address>:<port>] [with pose of <delay> second]
+ A customized AXFR transfer of <zone_name> [from <address>:<port>] [with pause of <delay> second]
If optional delay is specified (not None), it waits for the specified
seconds after sending the AXFR query before starting receiving
@@ -144,13 +144,13 @@ def perform_ixfr(step, zone_name, serial, address, port, protocol):
An IXFR transfer of <zone_name> <serial> [from <address>:port] [over <tcp|udp>]
Address defaults to 127.0.0.1
- Port defaults to 47806
+ Port defaults to 56176
If either tcp or udp is specified, only this protocol will be used.
"""
if address is None:
address = "127.0.0.1"
if port is None:
- port = 47806
+ port = 56176
args = [ 'dig', 'IXFR=' + str(serial), '@' + str(address), '-p', str(port), zone_name ]
if protocol is not None:
assert protocol == 'tcp' or protocol == 'udp', "Unknown protocol: " + protocol
diff --git a/tests/lettuce/features/xfrin_bind10.feature b/tests/lettuce/features/xfrin_bind10.feature
index db4bddb..c3b5bdb 100644
--- a/tests/lettuce/features/xfrin_bind10.feature
+++ b/tests/lettuce/features/xfrin_bind10.feature
@@ -6,7 +6,7 @@ Feature: Xfrin
# 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
+ Given I have bind10 running with configuration xfrin/retransfer_master.conf with cmdctl port 56174 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
@@ -24,10 +24,10 @@ Feature: Xfrin
# The DB currently doesn't know anything about the zone, so we install
# an empty zone for xfrin.
The file data/test_nonexistent_db.sqlite3 should exist
- A query for www.example.org to [::1]:47806 should have rcode REFUSED
+ A query for www.example.org to [::1]:56176 should have rcode REFUSED
Then make empty zone example.org in DB file data/test_nonexistent_db.sqlite3
- When I send bind10 the command Xfrin retransfer example.org IN ::1 47807
+ When I send bind10 the command Xfrin retransfer example.org IN ::1 56177
# The data we receive contain a NS RRset that refers to three names in the
# example.org. zone. All these three are nonexistent in the data, producing
# 3 separate warning messages in the log.
@@ -40,7 +40,7 @@ Feature: Xfrin
# we can't reliably use 'wait for new'. In this case this should be the
# only occurrence of this message, so this should be okay.
Then wait for bind10 stderr message ZONEMGR_RECEIVE_XFRIN_SUCCESS
- A query for www.example.org to [::1]:47806 should have rcode NOERROR
+ A query for www.example.org to [::1]:56176 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
@@ -53,18 +53,18 @@ Feature: Xfrin
# Now try to offer another update. However, the validation of
# data should fail. The old version shoud still be available.
- When I send bind10 the following commands with cmdctl port 47804:
+ When I send bind10 the following commands with cmdctl port 56174:
"""
config set data_sources/classes/IN[0]/params/database_file data/example.org-nons.sqlite3
config set Auth/database_file data/example.org-nons.sqlite3
config commit
"""
- Then I send bind10 the command Xfrin retransfer example.org IN ::1 47807
+ Then I send bind10 the command Xfrin retransfer example.org IN ::1 56177
And wait for new bind10 stderr message XFRIN_ZONE_INVALID
And wait for new bind10 stderr message XFRIN_INVALID_ZONE_DATA
# We can't use 'wait for new' here; see above.
Then wait for bind10 stderr message ZONEMGR_RECEIVE_XFRIN_FAILED
- A query for example.org type NS to [::1]:47806 should have rcode NOERROR
+ A query for example.org type NS to [::1]:56176 should have rcode NOERROR
And transfer result should have 13 rrs
Scenario: Transfer with TSIG
@@ -75,7 +75,7 @@ Feature: Xfrin
# non-TSIG config, add TSIG on the master side, see it fail, add TSIG
# on the slave side, then check again.
- Given I have bind10 running with configuration xfrin/retransfer_master.conf with cmdctl port 47804 as master
+ Given I have bind10 running with configuration xfrin/retransfer_master.conf with cmdctl port 56174 as master
And wait for master stderr message AUTH_SERVER_STARTED
And wait for master stderr message XFROUT_STARTED
@@ -87,7 +87,7 @@ Feature: Xfrin
Then make empty zone example.org in DB file data/test_nonexistent_db.sqlite3
# Set slave config for 'automatic' xfrin
- When I set bind10 configuration Xfrin/zones to [{"master_port": 47806, "name": "example.org", "master_addr": "::1"}]
+ When I set bind10 configuration Xfrin/zones to [{"master_port": 56176, "name": "example.org", "master_addr": "::1"}]
# Make sure it is fully open
When I send bind10 the command Xfrin retransfer example.org
@@ -96,7 +96,7 @@ Feature: Xfrin
And wait for bind10 stderr message ZONEMGR_RECEIVE_XFRIN_SUCCESS
# First to master, a transfer should then fail
- When I send bind10 the following commands with cmdctl port 47804:
+ When I send bind10 the following commands with cmdctl port 56174:
"""
config add tsig_keys/keys "example.key.:c2VjcmV0"
config set Xfrout/zone_config[0]/transfer_acl [{"action": "ACCEPT", "from": "::1", "key": "example.key."}]
@@ -127,7 +127,7 @@ Feature: Xfrin
# do here).
The file data/test_nonexistent_db.sqlite3 should not exist
- Given I have bind10 running with configuration xfrin/retransfer_master_nons.conf with cmdctl port 47804 as master
+ Given I have bind10 running with configuration xfrin/retransfer_master_nons.conf with cmdctl port 56174 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
@@ -144,10 +144,10 @@ Feature: Xfrin
# Now we use the first step again to see if the file has been created,
# then install empty zone data
The file data/test_nonexistent_db.sqlite3 should exist
- A query for www.example.org to [::1]:47806 should have rcode REFUSED
+ A query for www.example.org to [::1]:56176 should have rcode REFUSED
Then make empty zone example.org in DB file data/test_nonexistent_db.sqlite3
- When I send bind10 the command Xfrin retransfer example.org IN ::1 47807
+ When I send bind10 the command Xfrin retransfer example.org IN ::1 56177
# It should complain once about invalid data, then again that the whole
# zone is invalid and then reject it.
And wait for new bind10 stderr message XFRIN_ZONE_INVALID
@@ -157,7 +157,7 @@ Feature: Xfrin
# The zone still doesn't exist as it is rejected.
# FIXME: This step fails. Probably an empty zone is created in the data
# source :-|. This should be REFUSED, not SERVFAIL.
- A query for www.example.org to [::1]:47806 should have rcode SERVFAIL
+ A query for www.example.org to [::1]:56176 should have rcode SERVFAIL
# TODO:
# * IXFR - generate an sqlite db that contains the journal. Check it was
@@ -170,7 +170,7 @@ Feature: Xfrin
# scenario. Just this time, the master contains the differences table
# and the slave has a previous version of the zone, so we use the IXFR.
- Given I have bind10 running with configuration xfrin/retransfer_master_diffs.conf with cmdctl port 47804 as master
+ Given I have bind10 running with configuration xfrin/retransfer_master_diffs.conf with cmdctl port 56174 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
@@ -184,20 +184,20 @@ Feature: Xfrin
And wait for bind10 stderr message XFRIN_STARTED
And wait for bind10 stderr message ZONEMGR_STARTED
- A query for example. type SOA to [::1]:47806 should have rcode NOERROR
+ A query for example. type SOA to [::1]:56176 should have rcode NOERROR
The answer section of the last query response should be
"""
example. 3600 IN SOA ns1.example. hostmaster.example. 94 3600 900 7200 300
"""
# To invoke IXFR we need to use refresh command
- When I send bind10 the command Xfrin refresh example. IN ::1 47807
+ When I send bind10 the command Xfrin refresh example. IN ::1 56177
Then wait for new bind10 stderr message XFRIN_GOT_INCREMENTAL_RESP
Then wait for new bind10 stderr message XFRIN_IXFR_TRANSFER_SUCCESS not XFRIN_XFR_PROCESS_FAILURE
# This can't be 'wait for new'
Then wait for bind10 stderr message ZONEMGR_RECEIVE_XFRIN_SUCCESS
- A query for example. type SOA to [::1]:47806 should have rcode NOERROR
+ A query for example. type SOA to [::1]:56176 should have rcode NOERROR
The answer section of the last query response should be
"""
example. 3600 IN SOA ns1.example. hostmaster.example. 100 3600 900 7200 300
diff --git a/tests/lettuce/features/xfrin_notify_handling.feature b/tests/lettuce/features/xfrin_notify_handling.feature
index 2f43dfd..99bfbb4 100644
--- a/tests/lettuce/features/xfrin_notify_handling.feature
+++ b/tests/lettuce/features/xfrin_notify_handling.feature
@@ -7,7 +7,7 @@ Feature: Xfrin incoming notify handling
server or slave server is unreachable.
Scenario: Handle incoming notify
- Given I have bind10 running with configuration xfrin/retransfer_master.conf with cmdctl port 47804 as master
+ Given I have bind10 running with configuration xfrin/retransfer_master.conf with cmdctl port 56174 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
@@ -22,12 +22,12 @@ Feature: Xfrin incoming notify handling
And wait for bind10 stderr message XFRIN_STARTED
And wait for bind10 stderr message ZONEMGR_STARTED
- A query for www.example.org to [::1]:47806 should have rcode NXDOMAIN
+ A query for www.example.org to [::1]:56176 should have rcode NXDOMAIN
#
# Test1 for Xfrout statistics
#
- check initial statistics not containing example.org for Xfrout with cmdctl port 47804 except for the following items
+ check initial statistics not containing example.org for Xfrout with cmdctl port 56174 except for the following items
| item_name | min_value | max_value |
| socket.unixdomain.open | 0 | 1 |
# Note: .Xfrout.socket.unixdomain.open can be either expected to
@@ -49,19 +49,19 @@ Feature: Xfrin incoming notify handling
# reason is same as the case of .Xfrout.socket.unixdomain.open. as described
# above.
- When I send bind10 with cmdctl port 47804 the command Xfrout notify example.org IN
+ When I send bind10 with cmdctl port 56174 the command Xfrout notify example.org IN
Then wait for new master stderr message XFROUT_NOTIFY_COMMAND
Then wait for new bind10 stderr message AUTH_RECEIVED_NOTIFY
- # From this point we can't reliably 'wait for new' because the ordering
- # of logs from different processes is unpredictable. But these
- # should be okay in this case.
- Then wait for bind10 stderr message ZONEMGR_RECEIVE_NOTIFY
- Then wait for bind10 stderr message XFRIN_XFR_TRANSFER_STARTED
- Then wait for bind10 stderr message XFRIN_TRANSFER_SUCCESS not XFRIN_XFR_PROCESS_FAILURE
- Then wait for bind10 stderr message ZONEMGR_RECEIVE_XFRIN_SUCCESS
- Then wait for master stderr message NOTIFY_OUT_REPLY_RECEIVED
-
- A query for www.example.org to [::1]:47806 should have rcode NOERROR
+ Then wait for new bind10 stderr message ZONEMGR_RECEIVE_NOTIFY
+ # Note: The following waits should be for *new* log messages, or
+ # they will match older log messages that were generated by AXFR
+ # during startup.
+ Then wait for new bind10 stderr message XFRIN_XFR_TRANSFER_STARTED
+ Then wait for new bind10 stderr message XFRIN_TRANSFER_SUCCESS not XFRIN_XFR_PROCESS_FAILURE
+ Then wait for new bind10 stderr message ZONEMGR_RECEIVE_XFRIN_SUCCESS
+ Then wait for new master stderr message NOTIFY_OUT_REPLY_RECEIVED
+
+ A query for www.example.org to [::1]:56176 should have rcode NOERROR
# Make sure handling statistics command handling checked below is
# after this query
And wait for bind10 stderr message AUTH_SEND_NORMAL_RESPONSE
@@ -73,14 +73,14 @@ Feature: Xfrin incoming notify handling
#
# wait until the last stats requesting is finished
- When I query statistics zones of bind10 module Xfrout with cmdctl port 47804
+ When I query statistics zones of bind10 module Xfrout with cmdctl port 56174
# note that this does not 100% guarantee the stats updated Xfrout
# statistics. But there doesn't seem to be a better log message that
# suggests this event.
wait for new master stderr message XFROUT_RECEIVED_GETSTATS_COMMAND
last bindctl output should not contain "error"
- When I query statistics zones of bind10 module Xfrout with cmdctl port 47804
+ When I query statistics zones of bind10 module Xfrout with cmdctl port 56174
The statistics counters are 0 in category .Xfrout.zones.IN except for the following items
| item_name | item_value |
| _SERVER_.notifyoutv6 | 1 |
@@ -88,7 +88,7 @@ Feature: Xfrin incoming notify handling
| example.org..notifyoutv6 | 1 |
| example.org..xfrreqdone | 1 |
- When I query statistics socket of bind10 module Xfrout with cmdctl port 47804
+ When I query statistics socket of bind10 module Xfrout with cmdctl port 56174
The statistics counters are 0 in category .Xfrout.socket.unixdomain except for the following items
| item_name | item_value |
| open | 1 |
@@ -127,7 +127,7 @@ Feature: Xfrin incoming notify handling
# Test for handling incoming notify only in IPv4
#
Scenario: Handle incoming notify (IPv4)
- Given I have bind10 running with configuration xfrin/retransfer_master_v4.conf with cmdctl port 47804 as master
+ Given I have bind10 running with configuration xfrin/retransfer_master_v4.conf with cmdctl port 56174 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
@@ -142,12 +142,12 @@ Feature: Xfrin incoming notify handling
And wait for bind10 stderr message XFRIN_STARTED
And wait for bind10 stderr message ZONEMGR_STARTED
- A query for www.example.org to 127.0.0.1:47806 should have rcode NXDOMAIN
+ A query for www.example.org to 127.0.0.1:56176 should have rcode NXDOMAIN
#
# Test1 for Xfrout statistics
#
- check initial statistics not containing example.org for Xfrout with cmdctl port 47804 except for the following items
+ check initial statistics not containing example.org for Xfrout with cmdctl port 56174 except for the following items
| item_name | min_value | max_value |
| socket.unixdomain.open | 0 | 1 |
# Note: See above about .Xfrout.socket.unixdomain.open.
@@ -161,19 +161,19 @@ Feature: Xfrin incoming notify handling
| axfr_running | 0 | 1 |
# Note: See above about soa_in_progress and axfr_running of Xfrin
- When I send bind10 with cmdctl port 47804 the command Xfrout notify example.org IN
+ When I send bind10 with cmdctl port 56174 the command Xfrout notify example.org IN
Then wait for new master stderr message XFROUT_NOTIFY_COMMAND
Then wait for new bind10 stderr message AUTH_RECEIVED_NOTIFY
- # From this point we can't reliably 'wait for new' because the ordering
- # of logs from different processes is unpredictable. But these
- # should be okay in this case.
- Then wait for bind10 stderr message ZONEMGR_RECEIVE_NOTIFY
- Then wait for bind10 stderr message XFRIN_XFR_TRANSFER_STARTED
- Then wait for bind10 stderr message XFRIN_TRANSFER_SUCCESS not XFRIN_XFR_PROCESS_FAILURE
- Then wait for bind10 stderr message ZONEMGR_RECEIVE_XFRIN_SUCCESS
- Then wait for master stderr message NOTIFY_OUT_REPLY_RECEIVED
-
- A query for www.example.org to 127.0.0.1:47806 should have rcode NOERROR
+ Then wait for new bind10 stderr message ZONEMGR_RECEIVE_NOTIFY
+ # Note: The following waits should be for *new* log messages, or
+ # they will match older log messages that were generated by AXFR
+ # during startup.
+ Then wait for new bind10 stderr message XFRIN_XFR_TRANSFER_STARTED
+ Then wait for new bind10 stderr message XFRIN_TRANSFER_SUCCESS not XFRIN_XFR_PROCESS_FAILURE
+ Then wait for new bind10 stderr message ZONEMGR_RECEIVE_XFRIN_SUCCESS
+ Then wait for new master stderr message NOTIFY_OUT_REPLY_RECEIVED
+
+ A query for www.example.org to 127.0.0.1:56176 should have rcode NOERROR
# Make sure handling statistics command handling checked below is
# after this query
And wait for bind10 stderr message AUTH_SEND_NORMAL_RESPONSE
@@ -185,14 +185,14 @@ Feature: Xfrin incoming notify handling
#
# wait until the last stats requesting is finished
- When I query statistics zones of bind10 module Xfrout with cmdctl port 47804
+ When I query statistics zones of bind10 module Xfrout with cmdctl port 56174
# note that this does not 100% guarantee the stats updated Xfrout
# statistics. But there doesn't seem to be a better log message that
# suggests this event.
wait for new master stderr message XFROUT_RECEIVED_GETSTATS_COMMAND
last bindctl output should not contain "error"
- When I query statistics zones of bind10 module Xfrout with cmdctl port 47804
+ When I query statistics zones of bind10 module Xfrout with cmdctl port 56174
The statistics counters are 0 in category .Xfrout.zones.IN except for the following items
| item_name | item_value |
| _SERVER_.notifyoutv4 | 1 |
@@ -200,7 +200,7 @@ Feature: Xfrin incoming notify handling
| example.org..notifyoutv4 | 1 |
| example.org..xfrreqdone | 1 |
- When I query statistics socket of bind10 module Xfrout with cmdctl port 47804
+ When I query statistics socket of bind10 module Xfrout with cmdctl port 56174
The statistics counters are 0 in category .Xfrout.socket.unixdomain except for the following items
| item_name | item_value |
| open | 1 |
@@ -239,7 +239,7 @@ Feature: Xfrin incoming notify handling
# Test for Xfr request rejected
#
Scenario: Handle incoming notify (XFR request rejected)
- Given I have bind10 running with configuration xfrin/retransfer_master.conf with cmdctl port 47804 as master
+ Given I have bind10 running with configuration xfrin/retransfer_master.conf with cmdctl port 56174 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
@@ -254,12 +254,12 @@ Feature: Xfrin incoming notify handling
And wait for bind10 stderr message XFRIN_STARTED
And wait for bind10 stderr message ZONEMGR_STARTED
- A query for www.example.org to [::1]:47806 should have rcode NXDOMAIN
+ A query for www.example.org to [::1]:56176 should have rcode NXDOMAIN
#
# Test1 for Xfrout statistics
#
- check initial statistics not containing example.org for Xfrout with cmdctl port 47804 except for the following items
+ check initial statistics not containing example.org for Xfrout with cmdctl port 56174 except for the following items
| item_name | min_value | max_value |
| socket.unixdomain.open | 0 | 1 |
# Note: See above about .Xfrout.socket.unixdomain.open.
@@ -277,24 +277,27 @@ Feature: Xfrin incoming notify handling
# set transfer_acl rejection
# Local xfr requests from Xfrin module would be rejected here.
#
- When I send bind10 the following commands with cmdctl port 47804
+ When I send bind10 the following commands with cmdctl port 56174
"""
config set Xfrout/zone_config[0]/transfer_acl [{"action": "REJECT", "from": "::1"}]
config commit
"""
last bindctl output should not contain Error
+ Then wait for new master stderr message XFROUT_NEW_CONFIG_DONE
- When I send bind10 with cmdctl port 47804 the command Xfrout notify example.org IN
+ When I send bind10 with cmdctl port 56174 the command Xfrout notify example.org IN
Then wait for new master stderr message XFROUT_NOTIFY_COMMAND
Then wait for new bind10 stderr message AUTH_RECEIVED_NOTIFY
- # can't use 'wait for new' below.
- Then wait for bind10 stderr message ZONEMGR_RECEIVE_NOTIFY
- Then wait for bind10 stderr message XFRIN_XFR_TRANSFER_STARTED
- Then wait for bind10 stderr message XFRIN_XFR_TRANSFER_PROTOCOL_VIOLATION not XFRIN_TRANSFER_SUCCESS
- Then wait for bind10 stderr message ZONEMGR_RECEIVE_XFRIN_FAILED not ZONEMGR_RECEIVE_XFRIN_SUCCESS
- Then wait for master stderr message NOTIFY_OUT_REPLY_RECEIVED
+ Then wait for new bind10 stderr message ZONEMGR_RECEIVE_NOTIFY
+ # Note: The following waits should be for *new* log messages, or
+ # they will match older log messages that were generated by AXFR
+ # during startup.
+ Then wait for new bind10 stderr message XFRIN_XFR_TRANSFER_STARTED
+ Then wait for new bind10 stderr message XFRIN_XFR_TRANSFER_PROTOCOL_VIOLATION not XFRIN_TRANSFER_SUCCESS
+ Then wait for new bind10 stderr message ZONEMGR_RECEIVE_XFRIN_FAILED not ZONEMGR_RECEIVE_XFRIN_SUCCESS
+ Then wait for new master stderr message NOTIFY_OUT_REPLY_RECEIVED
- A query for www.example.org to [::1]:47806 should have rcode NXDOMAIN
+ A query for www.example.org to [::1]:56176 should have rcode NXDOMAIN
#
# Test3 for Xfrout statistics
@@ -303,11 +306,11 @@ Feature: Xfrin incoming notify handling
#
# wait until the last stats requesting is finished
- When I query statistics zones of bind10 module Xfrout with cmdctl port 47804
+ When I query statistics zones of bind10 module Xfrout with cmdctl port 56174
wait for new master stderr message XFROUT_RECEIVED_GETSTATS_COMMAND
last bindctl output should not contain "error"
- When I query statistics zones of bind10 module Xfrout with cmdctl port 47804
+ When I query statistics zones of bind10 module Xfrout with cmdctl port 56174
The statistics counters are 0 in category .Xfrout.zones.IN except for the following items
| item_name | item_value | min_value | max_value |
| _SERVER_.notifyoutv6 | 1 | | |
@@ -318,7 +321,7 @@ Feature: Xfrin incoming notify handling
# up to 3. See this for details
# http://git.bind10.isc.org/~tester/builder/BIND10-lettuce/20120918210000-MacOS/logs/lettuce.out
- When I query statistics socket of bind10 module Xfrout with cmdctl port 47804
+ When I query statistics socket of bind10 module Xfrout with cmdctl port 56174
The statistics counters are 0 in category .Xfrout.socket.unixdomain except for the following items
| item_name | item_value |
| open | 1 |
@@ -355,7 +358,7 @@ Feature: Xfrin incoming notify handling
# Test for Xfr request rejected in IPv4
#
Scenario: Handle incoming notify (XFR request rejected in IPv4)
- Given I have bind10 running with configuration xfrin/retransfer_master_v4.conf with cmdctl port 47804 as master
+ Given I have bind10 running with configuration xfrin/retransfer_master_v4.conf with cmdctl port 56174 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
@@ -370,12 +373,12 @@ Feature: Xfrin incoming notify handling
And wait for bind10 stderr message XFRIN_STARTED
And wait for bind10 stderr message ZONEMGR_STARTED
- A query for www.example.org to 127.0.0.1:47806 should have rcode NXDOMAIN
+ A query for www.example.org to 127.0.0.1:56176 should have rcode NXDOMAIN
#
# Test1 for Xfrout statistics
#
- check initial statistics not containing example.org for Xfrout with cmdctl port 47804 except for the following items
+ check initial statistics not containing example.org for Xfrout with cmdctl port 56174 except for the following items
| item_name | min_value | max_value |
| socket.unixdomain.open | 0 | 1 |
# Note: See above about .Xfrout.socket.unixdomain.open.
@@ -393,24 +396,27 @@ Feature: Xfrin incoming notify handling
# set transfer_acl rejection
# Local xfr requests from Xfrin module would be rejected here.
#
- When I send bind10 the following commands with cmdctl port 47804
+ When I send bind10 the following commands with cmdctl port 56174
"""
config set Xfrout/zone_config[0]/transfer_acl [{"action": "REJECT", "from": "127.0.0.1"}]
config commit
"""
last bindctl output should not contain Error
+ Then wait for new master stderr message XFROUT_NEW_CONFIG_DONE
- When I send bind10 with cmdctl port 47804 the command Xfrout notify example.org IN
+ When I send bind10 with cmdctl port 56174 the command Xfrout notify example.org IN
Then wait for new master stderr message XFROUT_NOTIFY_COMMAND
Then wait for new bind10 stderr message AUTH_RECEIVED_NOTIFY
- # can't use 'wait for new' below.
- Then wait for bind10 stderr message ZONEMGR_RECEIVE_NOTIFY
- Then wait for bind10 stderr message XFRIN_XFR_TRANSFER_STARTED
- Then wait for bind10 stderr message XFRIN_XFR_TRANSFER_PROTOCOL_VIOLATION not XFRIN_TRANSFER_SUCCESS
- Then wait for bind10 stderr message ZONEMGR_RECEIVE_XFRIN_FAILED not ZONEMGR_RECEIVE_XFRIN_SUCCESS
- Then wait for master stderr message NOTIFY_OUT_REPLY_RECEIVED
+ Then wait for new bind10 stderr message ZONEMGR_RECEIVE_NOTIFY
+ # Note: The following waits should be for *new* log messages, or
+ # they will match older log messages that were generated by AXFR
+ # during startup.
+ Then wait for new bind10 stderr message XFRIN_XFR_TRANSFER_STARTED
+ Then wait for new bind10 stderr message XFRIN_XFR_TRANSFER_PROTOCOL_VIOLATION not XFRIN_TRANSFER_SUCCESS
+ Then wait for new bind10 stderr message ZONEMGR_RECEIVE_XFRIN_FAILED not ZONEMGR_RECEIVE_XFRIN_SUCCESS
+ Then wait for new master stderr message NOTIFY_OUT_REPLY_RECEIVED
- A query for www.example.org to 127.0.0.1:47806 should have rcode NXDOMAIN
+ A query for www.example.org to 127.0.0.1:56176 should have rcode NXDOMAIN
#
# Test3 for Xfrout statistics
@@ -418,12 +424,12 @@ Feature: Xfrin incoming notify handling
# check statistics change
#
- When I query statistics zones of bind10 module Xfrout with cmdctl port 47804
+ When I query statistics zones of bind10 module Xfrout with cmdctl port 56174
# wait until stats request at least after NOTIFY_OUT_REPLY_RECEIVED
wait for new master stderr message XFROUT_RECEIVED_GETSTATS_COMMAND
last bindctl output should not contain "error"
- When I query statistics zones of bind10 module Xfrout with cmdctl port 47804
+ When I query statistics zones of bind10 module Xfrout with cmdctl port 56174
The statistics counters are 0 in category .Xfrout.zones.IN except for the following items
| item_name | item_value | min_value | max_value |
| _SERVER_.notifyoutv4 | 1 | | |
@@ -434,7 +440,7 @@ Feature: Xfrin incoming notify handling
# up to 3. See this for details
# http://git.bind10.isc.org/~tester/builder/BIND10-lettuce/20120918210000-MacOS/logs/lettuce.out
- When I query statistics socket of bind10 module Xfrout with cmdctl port 47804
+ When I query statistics socket of bind10 module Xfrout with cmdctl port 56174
The statistics counters are 0 in category .Xfrout.socket.unixdomain except for the following items
| item_name | item_value |
| open | 1 |
@@ -471,7 +477,7 @@ Feature: Xfrin incoming notify handling
# Test for unreachable slave
#
Scenario: Handle incoming notify (unreachable slave)
- Given I have bind10 running with configuration xfrin/retransfer_master.conf with cmdctl port 47804 as master
+ Given I have bind10 running with configuration xfrin/retransfer_master.conf with cmdctl port 56174 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
@@ -479,7 +485,7 @@ Feature: Xfrin incoming notify handling
And wait for master stderr message ZONEMGR_STARTED
And wait for master stderr message STATS_STARTING
- When I send bind10 with cmdctl port 47804 the command Xfrout notify example.org IN
+ When I send bind10 with cmdctl port 56174 the command Xfrout notify example.org IN
Then wait for new master stderr message XFROUT_NOTIFY_COMMAND
Then wait for new master stderr message NOTIFY_OUT_SENDING_NOTIFY
Then wait for new master stderr message NOTIFY_OUT_TIMEOUT
@@ -490,18 +496,18 @@ Feature: Xfrin incoming notify handling
# check statistics change
#
- When I query statistics zones of bind10 module Xfrout with cmdctl port 47804
+ When I query statistics zones of bind10 module Xfrout with cmdctl port 56174
# wait until stats request at least after NOTIFY_OUT_TIMEOUT
wait for new master stderr message XFROUT_RECEIVED_GETSTATS_COMMAND
last bindctl output should not contain "error"
- When I query statistics zones of bind10 module Xfrout with cmdctl port 47804
+ When I query statistics zones of bind10 module Xfrout with cmdctl port 56174
The statistics counters are 0 in category .Xfrout.zones.IN except for the following items
| item_name | min_value | max_value |
| _SERVER_.notifyoutv6 | 1 | 5 |
| example.org..notifyoutv6 | 1 | 5 |
- When I query statistics socket of bind10 module Xfrout with cmdctl port 47804
+ When I query statistics socket of bind10 module Xfrout with cmdctl port 56174
The statistics counters are 0 in category .Xfrout.socket.unixdomain except for the following items
| item_name | item_value |
| open | 1 |
@@ -510,7 +516,7 @@ Feature: Xfrin incoming notify handling
# Test for NOTIFY that would result in NOTAUTH
#
Scenario: Handle incoming notify that does match authoritative zones
- Given I have bind10 running with configuration xfrin/retransfer_master.conf with cmdctl port 47804 as master
+ Given I have bind10 running with configuration xfrin/retransfer_master.conf with cmdctl port 56174 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
@@ -529,7 +535,7 @@ Feature: Xfrin incoming notify handling
# replace master's data source with unmatched zone for slave's zone.
# we restart Xfrout to make it sure.
#
- When I send bind10 the following commands with cmdctl port 47804
+ When I send bind10 the following commands with cmdctl port 56174
"""
config set data_sources/classes/IN[0]/params/database_file data/ixfr-out/zones.sqlite3
config set Auth/database_file data/ixfr-out/zones.sqlite3
@@ -540,20 +546,20 @@ Feature: Xfrin incoming notify handling
last bindctl output should not contain "error"
And wait for new master stderr message XFROUT_STARTED
- A query for www.example.com to [::1]:47806 should have rcode REFUSED
+ A query for www.example.com to [::1]:56176 should have rcode REFUSED
- When I send bind10 with cmdctl port 47804 the command Xfrout notify example.com IN
+ When I send bind10 with cmdctl port 56174 the command Xfrout notify example.com IN
Then wait for new master stderr message XFROUT_NOTIFY_COMMAND
Then wait for new bind10 stderr message AUTH_RECEIVED_NOTIFY_NOTAUTH
Then wait for new master stderr message NOTIFY_OUT_REPLY_RECEIVED
- A query for www.example.com to [::1]:47806 should have rcode REFUSED
+ A query for www.example.com to [::1]:56176 should have rcode REFUSED
#
# Test for NOTIFY that's not in the secondaries list
#
Scenario: Handle incoming notify that is not in the secondaries list
- Given I have bind10 running with configuration xfrin/retransfer_master.conf with cmdctl port 47804 as master
+ Given I have bind10 running with configuration xfrin/retransfer_master.conf with cmdctl port 56174 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
@@ -580,22 +586,22 @@ Feature: Xfrin incoming notify handling
last bindctl output should not contain "error"
And wait for new bind10 stderr message ZONEMGR_STARTED
- A query for www.example.org to [::1]:47806 should have rcode NXDOMAIN
+ A query for www.example.org to [::1]:56176 should have rcode NXDOMAIN
- When I send bind10 with cmdctl port 47804 the command Xfrout notify example.org IN
+ When I send bind10 with cmdctl port 56174 the command Xfrout notify example.org IN
Then wait for new master stderr message XFROUT_NOTIFY_COMMAND
Then wait for new bind10 stderr message AUTH_RECEIVED_NOTIFY
Then wait for new bind10 stderr message ZONEMGR_RECEIVE_NOTIFY
Then wait for new bind10 stderr message ZONEMGR_ZONE_NOTIFY_NOT_SECONDARY
Then wait for new master stderr message NOTIFY_OUT_REPLY_RECEIVED
- A query for www.example.org to [::1]:47806 should have rcode NXDOMAIN
+ A query for www.example.org to [::1]:56176 should have rcode NXDOMAIN
#
# Test for NOTIFY when zonemgr is not running
#
Scenario: Handle incoming notify while zonemgr is not running
- Given I have bind10 running with configuration xfrin/retransfer_master.conf with cmdctl port 47804 as master
+ Given I have bind10 running with configuration xfrin/retransfer_master.conf with cmdctl port 56174 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
@@ -620,15 +626,15 @@ Feature: Xfrin incoming notify handling
last bindctl output should not contain "error"
And wait for new bind10 stderr message BIND10_PROCESS_ENDED
- A query for www.example.org to [::1]:47806 should have rcode NXDOMAIN
+ A query for www.example.org to [::1]:56176 should have rcode NXDOMAIN
- When I send bind10 with cmdctl port 47804 the command Xfrout notify example.org IN
+ When I send bind10 with cmdctl port 56174 the command Xfrout notify example.org IN
Then wait for master stderr message XFROUT_NOTIFY_COMMAND
Then wait for new bind10 stderr message AUTH_RECEIVED_NOTIFY
Then wait for new bind10 stderr message AUTH_ZONEMGR_NOTEXIST not AUTH_ZONEMGR_ERROR
Then wait for master stderr message NOTIFY_OUT_TIMEOUT not NOTIFY_OUT_REPLY_RECEIVED
- A query for www.example.org to [::1]:47806 should have rcode NXDOMAIN
+ A query for www.example.org to [::1]:56176 should have rcode NXDOMAIN
#
# Test for unreachable master
@@ -642,7 +648,7 @@ Feature: Xfrin incoming notify handling
And wait for bind10 stderr message XFRIN_STARTED
And wait for bind10 stderr message ZONEMGR_STARTED
- A query for www.example.org to [::1]:47806 should have rcode NXDOMAIN
+ A query for www.example.org to [::1]:56176 should have rcode NXDOMAIN
#
# Test1 for Xfrin statistics
diff --git a/tests/lettuce/features/xfrout_bind10.feature b/tests/lettuce/features/xfrout_bind10.feature
index 7f4e4de..e2ca7be 100644
--- a/tests/lettuce/features/xfrout_bind10.feature
+++ b/tests/lettuce/features/xfrout_bind10.feature
@@ -35,5 +35,5 @@ Feature: Xfrout
And wait for bind10 stderr message XFROUT_STARTED
And wait for bind10 stderr message ZONEMGR_STARTED
- When I do a customized AXFR transfer of example.org from [::1]:47806 with pose of 5 seconds
+ When I do a customized AXFR transfer of example.org from [::1]:56176 with pause of 5 seconds
Then transfer result should have 50001 rrs
diff --git a/tests/tools/perfdhcp/test_control.cc b/tests/tools/perfdhcp/test_control.cc
index 3751e2f..b317eff 100644
--- a/tests/tools/perfdhcp/test_control.cc
+++ b/tests/tools/perfdhcp/test_control.cc
@@ -1,4 +1,4 @@
-// Copyright (C) 2012-2013 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2012-2014 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
@@ -564,11 +564,8 @@ TestControl::getElapsedTime(const T& pkt1, const T& pkt2) {
isc_throw(InvalidOperation, "packet timestamp not set");;
}
time_period elapsed_period(pkt1_time, pkt2_time);
- if (elapsed_period.is_null()) {
- isc_throw(InvalidOperation, "unable to calculate time elapsed"
- " between packets");
- }
- return(elapsed_period.length().total_milliseconds());
+ return (elapsed_period.is_null() ? 0 :
+ elapsed_period.length().total_milliseconds());
}
int
@@ -780,8 +777,7 @@ TestControl::openSocket() const {
} else if (options.getIpVersion() == 6) {
// If remote address is multicast we need to enable it on
// the socket that has been created.
- asio::ip::address_v6 remote_v6 = remoteaddr.getAddress().to_v6();
- if (remote_v6.is_multicast()) {
+ if (remoteaddr.isV6Multicast()) {
int hops = 1;
int ret = setsockopt(sock, IPPROTO_IPV6, IPV6_MULTICAST_HOPS,
&hops, sizeof(hops));
@@ -1643,7 +1639,7 @@ TestControl::sendRequest4(const TestControlSocket& socket,
/// Set client address.
asiolink::IOAddress yiaddr = offer_pkt4->getYiaddr();
- if (!yiaddr.getAddress().is_v4()) {
+ if (!yiaddr.isV4()) {
isc_throw(BadValue, "the YIADDR returned in OFFER packet is not "
" IPv4 address");
}
@@ -1751,7 +1747,7 @@ TestControl::sendRequest4(const TestControlSocket& socket,
/// Set client address.
asiolink::IOAddress yiaddr = offer_pkt4->getYiaddr();
- if (!yiaddr.getAddress().is_v4()) {
+ if (!yiaddr.isV4()) {
isc_throw(BadValue, "the YIADDR returned in OFFER packet is not "
" IPv4 address");
}
diff --git a/tests/tools/perfdhcp/test_control.h b/tests/tools/perfdhcp/test_control.h
index 5a7ef48..a0746a0 100644
--- a/tests/tools/perfdhcp/test_control.h
+++ b/tests/tools/perfdhcp/test_control.h
@@ -1,4 +1,4 @@
-// Copyright (C) 2012-2013 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2012-2014 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
@@ -943,6 +943,11 @@ protected:
/// \brief Calculate elapsed time between two packets.
///
+ /// This function calculates the time elapsed between two packets. If
+ /// the timestamp of the pkt2 is greater than timestamp of the pkt1,
+ /// the positive value is returned. If the pkt2 timestamp is equal or
+ /// less than pkt1 timestamp, 0 is returned.
+ ///
/// \tparam T Pkt4Ptr or Pkt6Ptr class.
/// \param pkt1 first packet.
/// \param pkt2 second packet.
More information about the bind10-changes
mailing list